| ... | @@ -22893,49 +22893,102 @@ fn ptrCastFull( | ... | @@ -22893,49 +22893,102 @@ fn ptrCastFull( |
| 22893 | try Type.fromInterned(src_info.child).resolveLayout(pt); | 22893 | try Type.fromInterned(src_info.child).resolveLayout(pt); |
| 22894 | try Type.fromInterned(dest_info.child).resolveLayout(pt); | 22894 | try Type.fromInterned(dest_info.child).resolveLayout(pt); |
| 22895 | | 22895 | |
| 22896 | const src_slice_like = src_info.flags.size == .slice or | 22896 | const DestSliceLen = union(enum) { |
| 22897 | (src_info.flags.size == .one and Type.fromInterned(src_info.child).zigTypeTag(zcu) == .array); | 22897 | undef, |
| 22898 | | 22898 | constant: u64, |
| 22899 | const dest_slice_like = dest_info.flags.size == .slice or | 22899 | equal_runtime_src_slice, |
| 22900 | (dest_info.flags.size == .one and Type.fromInterned(dest_info.child).zigTypeTag(zcu) == .array); | 22900 | change_runtime_src_slice: struct { |
| 22901 | | 22901 | bytes_per_src: u64, |
| 22902 | if (dest_info.flags.size == .slice and !src_slice_like) { | 22902 | bytes_per_dest: u64, |
| 22903 | return sema.fail(block, src, "illegal pointer cast to slice", .{}); | 22903 | }, |
| 22904 | } | 22904 | }; |
| 22905 | | 22905 | // Populated iff the destination type is a slice. |
| 22906 | // Only defined if `src_slice_like` | 22906 | const dest_slice_len: ?DestSliceLen = len: { |
| 22907 | const src_slice_like_elem: Type = if (src_slice_like) switch (src_info.flags.size) { | 22907 | switch (dest_info.flags.size) { |
| 22908 | .slice => .fromInterned(src_info.child), | 22908 | .slice => {}, |
| 22909 | // pointer to array | 22909 | .many, .c, .one => break :len null, |
| 22910 | .one => Type.fromInterned(src_info.child).childType(zcu), | 22910 | } |
| 22911 | else => unreachable, | 22911 | // `null` means the operand is a runtime-known slice (so the length is runtime-known). |
| 22912 | } else undefined; | 22912 | const opt_src_len: ?u64 = switch (src_info.flags.size) { |
| 22913 | | 22913 | .one => 1, |
| 22914 | const slice_needs_len_change: bool = if (dest_info.flags.size == .slice) need_len_change: { | 22914 | .slice => src_len: { |
| 22915 | const dest_elem: Type = .fromInterned(dest_info.child); | 22915 | const operand_val = try sema.resolveValue(operand) orelse break :src_len null; |
| 22916 | if (src_slice_like_elem.toIntern() == dest_elem.toIntern()) { | 22916 | if (operand_val.isUndef(zcu)) break :len .undef; |
| 22917 | break :need_len_change false; | 22917 | const slice_val = switch (operand_ty.zigTypeTag(zcu)) { |
| 22918 | } | 22918 | .optional => operand_val.optionalValue(zcu) orelse break :len .undef, |
| 22919 | if (src_slice_like_elem.comptimeOnly(zcu) or dest_elem.comptimeOnly(zcu)) { | 22919 | .pointer => operand_val, |
| 22920 | return sema.fail(block, src, "cannot infer length of slice of '{}' from slice of '{}'", .{ dest_elem.fmt(pt), src_slice_like_elem.fmt(pt) }); | 22920 | else => unreachable, |
| 22921 | } | 22921 | }; |
| 22922 | // It's okay for `src_slice_like_elem` to be 0-bit; the resulting slice will just always have 0 elements. | 22922 | const slice_len_resolved = try sema.resolveLazyValue(.fromInterned(zcu.intern_pool.sliceLen(slice_val.toIntern()))); |
| 22923 | // However, `dest_elem` can't be 0-bit. If it were, then either the source slice has 0 bits and we don't | 22923 | if (slice_len_resolved.isUndef(zcu)) break :len .undef; |
| 22924 | // know how what `result.len` should be, or the source has >0 bits and there is no valid `result.len`. | 22924 | break :src_len slice_len_resolved.toUnsignedInt(zcu); |
| 22925 | const dest_elem_size = dest_elem.abiSize(zcu); | 22925 | }, |
| 22926 | if (dest_elem_size == 0) { | 22926 | .many, .c => { |
| 22927 | return sema.fail(block, src, "cannot infer length of slice of '{}' from slice of '{}'", .{ dest_elem.fmt(pt), src_slice_like_elem.fmt(pt) }); | 22927 | return sema.fail(block, src, "cannot infer length of slice from {s}", .{pointerSizeString(src_info.flags.size)}); |
| | 22928 | }, |
| | 22929 | }; |
| | 22930 | const dest_elem_ty: Type = .fromInterned(dest_info.child); |
| | 22931 | const src_elem_ty: Type = .fromInterned(src_info.child); |
| | 22932 | if (dest_elem_ty.toIntern() == src_elem_ty.toIntern()) { |
| | 22933 | break :len if (opt_src_len) |l| .{ .constant = l } else .equal_runtime_src_slice; |
| | 22934 | } |
| | 22935 | if (!src_elem_ty.comptimeOnly(zcu) and !dest_elem_ty.comptimeOnly(zcu)) { |
| | 22936 | const src_elem_size = src_elem_ty.abiSize(zcu); |
| | 22937 | const dest_elem_size = dest_elem_ty.abiSize(zcu); |
| | 22938 | if (dest_elem_size == 0) { |
| | 22939 | return sema.fail(block, src, "cannot infer length of slice of zero-bit '{}' from '{}'", .{ dest_elem_ty.fmt(pt), operand_ty.fmt(pt) }); |
| | 22940 | } |
| | 22941 | if (opt_src_len) |src_len| { |
| | 22942 | const bytes = src_len * src_elem_size; |
| | 22943 | const dest_len = std.math.divExact(u64, bytes, dest_elem_size) catch switch (src_info.flags.size) { |
| | 22944 | .slice => return sema.fail(block, src, "slice length '{d}' does not divide exactly into destination elements", .{src_len}), |
| | 22945 | .one => return sema.fail(block, src, "type '{}' does not divide exactly into destination elements", .{src_elem_ty.fmt(pt)}), |
| | 22946 | else => unreachable, |
| | 22947 | }; |
| | 22948 | break :len .{ .constant = dest_len }; |
| | 22949 | } |
| | 22950 | assert(src_info.flags.size == .slice); |
| | 22951 | break :len .{ .change_runtime_src_slice = .{ |
| | 22952 | .bytes_per_src = src_elem_size, |
| | 22953 | .bytes_per_dest = dest_elem_size, |
| | 22954 | } }; |
| 22928 | } | 22955 | } |
| 22929 | const src_elem_size = src_slice_like_elem.abiSize(zcu); | 22956 | // We apply rules for comptime memory consistent with comptime loads/stores, where arrays of |
| 22930 | break :need_len_change src_elem_size != dest_elem_size; | 22957 | // comptime-only types can be "restructured". |
| 22931 | } else false; | 22958 | const dest_base_ty: Type, const dest_base_per_elem: u64 = dest_elem_ty.arrayBase(zcu); |
| | 22959 | const src_base_ty: Type, const src_base_per_elem: u64 = src_elem_ty.arrayBase(zcu); |
| | 22960 | // The source value has `src_len * src_base_per_elem` values of type `src_base_ty`. |
| | 22961 | // The result value will have `dest_len * dest_base_per_elem` values of type `dest_base_ty`. |
| | 22962 | if (dest_base_ty.toIntern() != src_base_ty.toIntern()) { |
| | 22963 | return sema.fail(block, src, "cannot infer length of comptime-only '{}' from incompatible '{}'", .{ dest_ty.fmt(pt), operand_ty.fmt(pt) }); |
| | 22964 | } |
| | 22965 | // `src_base_ty` is comptime-only, so `src_elem_ty` is comptime-only, so `operand_ty` is |
| | 22966 | // comptime-only, so `operand` is comptime-known, so `opt_src_len` is non-`null`. |
| | 22967 | const src_len = opt_src_len.?; |
| | 22968 | const base_len = src_len * src_base_per_elem; |
| | 22969 | const dest_len = std.math.divExact(u64, base_len, dest_base_per_elem) catch switch (src_info.flags.size) { |
| | 22970 | .slice => return sema.fail(block, src, "slice length '{d}' does not divide exactly into destination elements", .{src_len}), |
| | 22971 | .one => return sema.fail(block, src, "type '{}' does not divide exactly into destination elements", .{src_elem_ty.fmt(pt)}), |
| | 22972 | else => unreachable, |
| | 22973 | }; |
| | 22974 | break :len .{ .constant = dest_len }; |
| | 22975 | }; |
| 22932 | | 22976 | |
| 22933 | // The checking logic in this function must stay in sync with Sema.coerceInMemoryAllowedPtrs | 22977 | // The checking logic in this function must stay in sync with Sema.coerceInMemoryAllowedPtrs |
| 22934 | | 22978 | |
| 22935 | if (!flags.ptr_cast) { | 22979 | if (!flags.ptr_cast) { |
| | 22980 | const is_array_ptr_to_slice = b: { |
| | 22981 | if (dest_info.flags.size != .slice) break :b false; |
| | 22982 | if (src_info.flags.size != .one) break :b false; |
| | 22983 | const src_pointer_child: Type = .fromInterned(src_info.child); |
| | 22984 | if (src_pointer_child.zigTypeTag(zcu) != .array) break :b false; |
| | 22985 | const src_elem = src_pointer_child.childType(zcu); |
| | 22986 | break :b src_elem.toIntern() == dest_info.child; |
| | 22987 | }; |
| | 22988 | |
| 22936 | check_size: { | 22989 | check_size: { |
| 22937 | if (src_info.flags.size == dest_info.flags.size) break :check_size; | 22990 | if (src_info.flags.size == dest_info.flags.size) break :check_size; |
| 22938 | if (src_slice_like and dest_slice_like) break :check_size; | 22991 | if (is_array_ptr_to_slice) break :check_size; |
| 22939 | if (src_info.flags.size == .c) break :check_size; | 22992 | if (src_info.flags.size == .c) break :check_size; |
| 22940 | if (dest_info.flags.size == .c) break :check_size; | 22993 | if (dest_info.flags.size == .c) break :check_size; |
| 22941 | return sema.failWithOwnedErrorMsg(block, msg: { | 22994 | return sema.failWithOwnedErrorMsg(block, msg: { |
| ... | @@ -22993,7 +23046,7 @@ fn ptrCastFull( | ... | @@ -22993,7 +23046,7 @@ fn ptrCastFull( |
| 22993 | const coerced_sent = try zcu.intern_pool.getCoerced(sema.gpa, pt.tid, src_info.sentinel, dest_info.child); | 23046 | const coerced_sent = try zcu.intern_pool.getCoerced(sema.gpa, pt.tid, src_info.sentinel, dest_info.child); |
| 22994 | if (dest_info.sentinel == coerced_sent) break :check_sent; | 23047 | if (dest_info.sentinel == coerced_sent) break :check_sent; |
| 22995 | } | 23048 | } |
| 22996 | if (src_slice_like and src_info.flags.size == .one and dest_info.flags.size == .slice) { | 23049 | if (is_array_ptr_to_slice) { |
| 22997 | // [*]nT -> []T | 23050 | // [*]nT -> []T |
| 22998 | const arr_ty = Type.fromInterned(src_info.child); | 23051 | const arr_ty = Type.fromInterned(src_info.child); |
| 22999 | if (arr_ty.sentinel(zcu)) |src_sentinel| { | 23052 | if (arr_ty.sentinel(zcu)) |src_sentinel| { |
| ... | @@ -23173,12 +23226,9 @@ fn ptrCastFull( | ... | @@ -23173,12 +23226,9 @@ fn ptrCastFull( |
| 23173 | } | 23226 | } |
| 23174 | } | 23227 | } |
| 23175 | | 23228 | |
| 23176 | const ptr_val: Value, const maybe_len_val: ?Value = switch (src_info.flags.size) { | 23229 | const ptr_val: Value = switch (src_info.flags.size) { |
| 23177 | .slice => switch (zcu.intern_pool.indexToKey(operand_val.toIntern())) { | 23230 | .slice => .fromInterned(zcu.intern_pool.indexToKey(operand_val.toIntern()).slice.ptr), |
| 23178 | .slice => |slice| .{ .fromInterned(slice.ptr), .fromInterned(slice.len) }, | 23231 | .one, .many, .c => operand_val, |
| 23179 | else => unreachable, | | |
| 23180 | }, | | |
| 23181 | .one, .many, .c => .{ operand_val, null }, | | |
| 23182 | }; | 23232 | }; |
| 23183 | | 23233 | |
| 23184 | if (dest_align.compare(.gt, src_align)) { | 23234 | if (dest_align.compare(.gt, src_align)) { |
| ... | @@ -23197,47 +23247,24 @@ fn ptrCastFull( | ... | @@ -23197,47 +23247,24 @@ fn ptrCastFull( |
| 23197 | } | 23247 | } |
| 23198 | } | 23248 | } |
| 23199 | | 23249 | |
| 23200 | if (dest_info.flags.size != .slice) { | 23250 | if (dest_info.flags.size == .slice) { |
| | 23251 | // Because the operand is comptime-known and not `null`, the slice length has already been computed: |
| | 23252 | const len: Value = switch (dest_slice_len.?) { |
| | 23253 | .undef => try pt.undefValue(.usize), |
| | 23254 | .constant => |n| try pt.intValue(.usize, n), |
| | 23255 | .equal_runtime_src_slice => unreachable, |
| | 23256 | .change_runtime_src_slice => unreachable, |
| | 23257 | }; |
| | 23258 | return Air.internedToRef(try pt.intern(.{ .slice = .{ |
| | 23259 | .ty = dest_ty.toIntern(), |
| | 23260 | .ptr = (try pt.getCoerced(ptr_val, dest_ty.slicePtrFieldType(zcu))).toIntern(), |
| | 23261 | .len = len.toIntern(), |
| | 23262 | } })); |
| | 23263 | } else { |
| 23201 | // Any to non-slice | 23264 | // Any to non-slice |
| 23202 | const new_ptr_val = try pt.getCoerced(ptr_val, dest_ty); | 23265 | const new_ptr_val = try pt.getCoerced(ptr_val, dest_ty); |
| 23203 | return Air.internedToRef(new_ptr_val.toIntern()); | 23266 | return Air.internedToRef(new_ptr_val.toIntern()); |
| 23204 | } | 23267 | } |
| 23205 | | | |
| 23206 | // Slice-like to slice, compatible element type | | |
| 23207 | // Here, we can preserve a lazy length. | | |
| 23208 | if (!slice_needs_len_change) { | | |
| 23209 | if (maybe_len_val) |len_val| { | | |
| 23210 | return Air.internedToRef(try pt.intern(.{ .slice = .{ | | |
| 23211 | .ty = dest_ty.toIntern(), | | |
| 23212 | .ptr = (try pt.getCoerced(ptr_val, dest_ty.slicePtrFieldType(zcu))).toIntern(), | | |
| 23213 | .len = len_val.toIntern(), | | |
| 23214 | } })); | | |
| 23215 | } | | |
| 23216 | } | | |
| 23217 | | | |
| 23218 | // Slice-like to slice, fallback | | |
| 23219 | | | |
| 23220 | const src_len: u64 = if (maybe_len_val) |val| | | |
| 23221 | try val.toUnsignedIntSema(pt) | | |
| 23222 | else | | |
| 23223 | Type.fromInterned(src_info.child).arrayLen(zcu); | | |
| 23224 | | | |
| 23225 | const dest_len: u64 = if (slice_needs_len_change) len: { | | |
| 23226 | const src_elem_size = src_slice_like_elem.abiSize(zcu); | | |
| 23227 | const dest_elem_size = Type.fromInterned(dest_info.child).abiSize(zcu); | | |
| 23228 | const bytes = src_len * src_elem_size; | | |
| 23229 | // Check: element count divides neatly | | |
| 23230 | break :len std.math.divExact(u64, bytes, dest_elem_size) catch |err| switch (err) { | | |
| 23231 | error.DivisionByZero => unreachable, | | |
| 23232 | error.UnexpectedRemainder => return sema.fail(block, src, "slice length '{d}' does not divide exactly into destination elements", .{src_len}), | | |
| 23233 | }; | | |
| 23234 | } else src_len; | | |
| 23235 | | | |
| 23236 | return Air.internedToRef(try pt.intern(.{ .slice = .{ | | |
| 23237 | .ty = dest_ty.toIntern(), | | |
| 23238 | .ptr = (try pt.getCoerced(ptr_val, dest_ty.slicePtrFieldType(zcu))).toIntern(), | | |
| 23239 | .len = (try pt.intValue(.usize, dest_len)).toIntern(), | | |
| 23240 | } })); | | |
| 23241 | } | 23268 | } |
| 23242 | | 23269 | |
| 23243 | try sema.validateRuntimeValue(block, operand_src, operand); | 23270 | try sema.validateRuntimeValue(block, operand_src, operand); |
| ... | @@ -23246,6 +23273,11 @@ fn ptrCastFull( | ... | @@ -23246,6 +23273,11 @@ fn ptrCastFull( |
| 23246 | const need_null_check = can_cast_to_int and block.wantSafety() and operand_ty.ptrAllowsZero(zcu) and !dest_ty.ptrAllowsZero(zcu); | 23273 | const need_null_check = can_cast_to_int and block.wantSafety() and operand_ty.ptrAllowsZero(zcu) and !dest_ty.ptrAllowsZero(zcu); |
| 23247 | const need_align_check = can_cast_to_int and block.wantSafety() and dest_align.compare(.gt, src_align); | 23274 | const need_align_check = can_cast_to_int and block.wantSafety() and dest_align.compare(.gt, src_align); |
| 23248 | | 23275 | |
| | 23276 | const slice_needs_len_change = if (dest_slice_len) |l| switch (l) { |
| | 23277 | .undef, .equal_runtime_src_slice => false, |
| | 23278 | .constant, .change_runtime_src_slice => true, |
| | 23279 | } else false; |
| | 23280 | |
| 23249 | // `operand` might be a slice. If `need_operand_ptr`, we'll populate `operand_ptr` with the raw pointer. | 23281 | // `operand` might be a slice. If `need_operand_ptr`, we'll populate `operand_ptr` with the raw pointer. |
| 23250 | const need_operand_ptr = src_info.flags.size != .slice or // we already have it | 23282 | const need_operand_ptr = src_info.flags.size != .slice or // we already have it |
| 23251 | dest_info.flags.size != .slice or // the result is a raw pointer | 23283 | dest_info.flags.size != .slice or // the result is a raw pointer |
| ... | @@ -23347,67 +23379,49 @@ fn ptrCastFull( | ... | @@ -23347,67 +23379,49 @@ fn ptrCastFull( |
| 23347 | // We need to deconstruct the slice (if applicable) and reconstruct it. | 23379 | // We need to deconstruct the slice (if applicable) and reconstruct it. |
| 23348 | assert(need_operand_ptr); | 23380 | assert(need_operand_ptr); |
| 23349 | | 23381 | |
| 23350 | const result_len: Air.Inst.Ref = len: { | 23382 | const result_len: Air.Inst.Ref = switch (dest_slice_len.?) { |
| 23351 | if (src_info.flags.size == .slice and !slice_needs_len_change) { | 23383 | .undef => try pt.undefRef(.usize), |
| | 23384 | .constant => |n| try pt.intRef(.usize, n), |
| | 23385 | .equal_runtime_src_slice => len: { |
| 23352 | assert(need_operand_len); | 23386 | assert(need_operand_len); |
| 23353 | break :len operand_len; | 23387 | break :len operand_len; |
| 23354 | } | 23388 | }, |
| 23355 | | 23389 | .change_runtime_src_slice => |change| len: { |
| 23356 | const src_elem_size = src_slice_like_elem.abiSize(zcu); | 23390 | assert(need_operand_len); |
| 23357 | const dest_elem_size = Type.fromInterned(dest_info.child).abiSize(zcu); | 23391 | // If `mul / div` is a whole number, then just multiply the length by it. |
| 23358 | if (src_info.flags.size != .slice) { | 23392 | if (std.math.divExact(u64, change.bytes_per_src, change.bytes_per_dest)) |dest_per_src| { |
| 23359 | assert(src_slice_like); | 23393 | const multiplier = try pt.intRef(.usize, dest_per_src); |
| 23360 | const src_len = Type.fromInterned(src_info.child).arrayLen(zcu); | 23394 | break :len try block.addBinOp(.mul, operand_len, multiplier); |
| 23361 | const bytes = src_len * src_elem_size; | 23395 | } else |err| switch (err) { |
| 23362 | const dest_len = std.math.divExact(u64, bytes, dest_elem_size) catch |err| switch (err) { | | |
| 23363 | error.DivisionByZero => unreachable, | 23396 | error.DivisionByZero => unreachable, |
| 23364 | error.UnexpectedRemainder => return sema.fail(block, src, "slice length '{d}' does not divide exactly into destination elements", .{src_len}), | 23397 | error.UnexpectedRemainder => {}, // fall through to code below |
| 23365 | }; | 23398 | } |
| 23366 | break :len try pt.intRef(.usize, dest_len); | 23399 | // If `div / mul` is a whole number, then just divide the length by it. |
| 23367 | } | 23400 | // This incurs a safety check. |
| 23368 | | 23401 | if (std.math.divExact(u64, change.bytes_per_dest, change.bytes_per_src)) |src_per_dest| { |
| 23369 | assert(need_operand_len); | 23402 | const divisor = try pt.intRef(.usize, src_per_dest); |
| 23370 | | 23403 | if (block.wantSafety()) { |
| 23371 | // If `src_elem_size * n == dest_elem_size`, then just multiply the length by `n`. | 23404 | // Check that the element count divides neatly. |
| 23372 | if (std.math.divExact(u64, src_elem_size, dest_elem_size)) |dest_per_src| { | 23405 | const remainder = try block.addBinOp(.rem, operand_len, divisor); |
| 23373 | const multiplier = try pt.intRef(.usize, dest_per_src); | 23406 | const ok = try block.addBinOp(.cmp_eq, remainder, .zero_usize); |
| 23374 | break :len try block.addBinOp(.mul, operand_len, multiplier); | 23407 | try sema.addSafetyCheckCall(block, src, ok, .@"panic.sliceCastLenRemainder", &.{operand_len}); |
| 23375 | } else |err| switch (err) { | 23408 | } |
| 23376 | error.DivisionByZero => unreachable, | 23409 | break :len try block.addBinOp(.div_exact, operand_len, divisor); |
| 23377 | error.UnexpectedRemainder => {}, // fall through to code below | 23410 | } else |err| switch (err) { |
| 23378 | } | 23411 | error.DivisionByZero => unreachable, |
| 23379 | | 23412 | error.UnexpectedRemainder => {}, // fall through to code below |
| 23380 | // If `src_elem_size == dest_elem_size * n`, then divide the length by `n`. | 23413 | } |
| 23381 | // This incurs a safety check. | 23414 | // Fallback: the elements don't divide easily. We'll multiply *and* divide. This incurs a safety check. |
| 23382 | if (std.math.divExact(u64, dest_elem_size, src_elem_size)) |src_per_dest| { | 23415 | const total_bytes_ref = try block.addBinOp(.mul, operand_len, try pt.intRef(.usize, change.bytes_per_src)); |
| 23383 | const divisor = try pt.intRef(.usize, src_per_dest); | 23416 | const bytes_per_dest_ref = try pt.intRef(.usize, change.bytes_per_dest); |
| 23384 | if (block.wantSafety()) { | 23417 | if (block.wantSafety()) { |
| 23385 | // Check that the element count divides neatly. | 23418 | // Check that `total_bytes_ref` divides neatly into `bytes_per_dest_ref`. |
| 23386 | const remainder = try block.addBinOp(.rem, operand_len, divisor); | 23419 | const remainder = try block.addBinOp(.rem, total_bytes_ref, bytes_per_dest_ref); |
| 23387 | const ok = try block.addBinOp(.cmp_eq, remainder, .zero_usize); | 23420 | const ok = try block.addBinOp(.cmp_eq, remainder, .zero_usize); |
| 23388 | try sema.addSafetyCheckCall(block, src, ok, .@"panic.sliceCastLenRemainder", &.{operand_len}); | 23421 | try sema.addSafetyCheckCall(block, src, ok, .@"panic.sliceCastLenRemainder", &.{operand_len}); |
| 23389 | } | 23422 | } |
| 23390 | break :len try block.addBinOp(.div_exact, operand_len, divisor); | 23423 | break :len try block.addBinOp(.div_exact, total_bytes_ref, bytes_per_dest_ref); |
| 23391 | } else |err| switch (err) { | 23424 | }, |
| 23392 | error.DivisionByZero => unreachable, | | |
| 23393 | error.UnexpectedRemainder => {}, // fall through to code below | | |
| 23394 | } | | |
| 23395 | | | |
| 23396 | // Fallback: the elements don't divide easily. | | |
| 23397 | // We'll multiply up to a byte count, then divide down to a new element count. | | |
| 23398 | // This incurs a safety check. | | |
| 23399 | | | |
| 23400 | const src_elem_size_ref = try pt.intRef(.usize, src_elem_size); | | |
| 23401 | const dest_elem_size_ref = try pt.intRef(.usize, dest_elem_size); | | |
| 23402 | | | |
| 23403 | const byte_count = try block.addBinOp(.mul, operand_len, src_elem_size_ref); | | |
| 23404 | if (block.wantSafety()) { | | |
| 23405 | // Check that `byte_count` divides neatly into `dest_elem_size`. | | |
| 23406 | const remainder = try block.addBinOp(.rem, byte_count, dest_elem_size_ref); | | |
| 23407 | const ok = try block.addBinOp(.cmp_eq, remainder, .zero_usize); | | |
| 23408 | try sema.addSafetyCheckCall(block, src, ok, .@"panic.sliceCastLenRemainder", &.{operand_len}); | | |
| 23409 | } | | |
| 23410 | break :len try block.addBinOp(.div_exact, byte_count, dest_elem_size_ref); | | |
| 23411 | }; | 23425 | }; |
| 23412 | | 23426 | |
| 23413 | const operand_ptr_ty = sema.typeOf(operand_ptr); | 23427 | const operand_ptr_ty = sema.typeOf(operand_ptr); |