| ... | ... | @@ -22893,49 +22893,102 @@ fn ptrCastFull( |
| 22893 | 22893 | try Type.fromInterned(src_info.child).resolveLayout(pt); |
| 22894 | 22894 | try Type.fromInterned(dest_info.child).resolveLayout(pt); |
| 22895 | 22895 | |
| 22896 | | const src_slice_like = src_info.flags.size == .slice or |
| 22897 | | (src_info.flags.size == .one and Type.fromInterned(src_info.child).zigTypeTag(zcu) == .array); |
| 22898 | | |
| 22899 | | const dest_slice_like = dest_info.flags.size == .slice or |
| 22900 | | (dest_info.flags.size == .one and Type.fromInterned(dest_info.child).zigTypeTag(zcu) == .array); |
| 22901 | | |
| 22902 | | if (dest_info.flags.size == .slice and !src_slice_like) { |
| 22903 | | return sema.fail(block, src, "illegal pointer cast to slice", .{}); |
| 22904 | | } |
| 22905 | | |
| 22906 | | // Only defined if `src_slice_like` |
| 22907 | | const src_slice_like_elem: Type = if (src_slice_like) switch (src_info.flags.size) { |
| 22908 | | .slice => .fromInterned(src_info.child), |
| 22909 | | // pointer to array |
| 22910 | | .one => Type.fromInterned(src_info.child).childType(zcu), |
| 22911 | | else => unreachable, |
| 22912 | | } else undefined; |
| 22913 | | |
| 22914 | | const slice_needs_len_change: bool = if (dest_info.flags.size == .slice) need_len_change: { |
| 22915 | | const dest_elem: Type = .fromInterned(dest_info.child); |
| 22916 | | if (src_slice_like_elem.toIntern() == dest_elem.toIntern()) { |
| 22917 | | break :need_len_change false; |
| 22918 | | } |
| 22919 | | if (src_slice_like_elem.comptimeOnly(zcu) or dest_elem.comptimeOnly(zcu)) { |
| 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) }); |
| 22921 | | } |
| 22922 | | // It's okay for `src_slice_like_elem` to be 0-bit; the resulting slice will just always have 0 elements. |
| 22923 | | // However, `dest_elem` can't be 0-bit. If it were, then either the source slice has 0 bits and we don't |
| 22924 | | // know how what `result.len` should be, or the source has >0 bits and there is no valid `result.len`. |
| 22925 | | const dest_elem_size = dest_elem.abiSize(zcu); |
| 22926 | | if (dest_elem_size == 0) { |
| 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) }); |
| 22896 | const DestSliceLen = union(enum) { |
| 22897 | undef, |
| 22898 | constant: u64, |
| 22899 | equal_runtime_src_slice, |
| 22900 | change_runtime_src_slice: struct { |
| 22901 | bytes_per_src: u64, |
| 22902 | bytes_per_dest: u64, |
| 22903 | }, |
| 22904 | }; |
| 22905 | // Populated iff the destination type is a slice. |
| 22906 | const dest_slice_len: ?DestSliceLen = len: { |
| 22907 | switch (dest_info.flags.size) { |
| 22908 | .slice => {}, |
| 22909 | .many, .c, .one => break :len null, |
| 22910 | } |
| 22911 | // `null` means the operand is a runtime-known slice (so the length is runtime-known). |
| 22912 | const opt_src_len: ?u64 = switch (src_info.flags.size) { |
| 22913 | .one => 1, |
| 22914 | .slice => src_len: { |
| 22915 | const operand_val = try sema.resolveValue(operand) orelse break :src_len null; |
| 22916 | if (operand_val.isUndef(zcu)) break :len .undef; |
| 22917 | const slice_val = switch (operand_ty.zigTypeTag(zcu)) { |
| 22918 | .optional => operand_val.optionalValue(zcu) orelse break :len .undef, |
| 22919 | .pointer => operand_val, |
| 22920 | else => unreachable, |
| 22921 | }; |
| 22922 | const slice_len_resolved = try sema.resolveLazyValue(.fromInterned(zcu.intern_pool.sliceLen(slice_val.toIntern()))); |
| 22923 | if (slice_len_resolved.isUndef(zcu)) break :len .undef; |
| 22924 | break :src_len slice_len_resolved.toUnsignedInt(zcu); |
| 22925 | }, |
| 22926 | .many, .c => { |
| 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); |
| 22930 | | break :need_len_change src_elem_size != dest_elem_size; |
| 22931 | | } else false; |
| 22956 | // We apply rules for comptime memory consistent with comptime loads/stores, where arrays of |
| 22957 | // comptime-only types can be "restructured". |
| 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 | 22977 | // The checking logic in this function must stay in sync with Sema.coerceInMemoryAllowedPtrs |
| 22934 | 22978 | |
| 22935 | 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 | 22989 | check_size: { |
| 22937 | 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 | 22992 | if (src_info.flags.size == .c) break :check_size; |
| 22940 | 22993 | if (dest_info.flags.size == .c) break :check_size; |
| 22941 | 22994 | return sema.failWithOwnedErrorMsg(block, msg: { |
| ... | ... | @@ -22993,7 +23046,7 @@ fn ptrCastFull( |
| 22993 | 23046 | const coerced_sent = try zcu.intern_pool.getCoerced(sema.gpa, pt.tid, src_info.sentinel, dest_info.child); |
| 22994 | 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 | 23050 | // [*]nT -> []T |
| 22998 | 23051 | const arr_ty = Type.fromInterned(src_info.child); |
| 22999 | 23052 | if (arr_ty.sentinel(zcu)) |src_sentinel| { |
| ... | ... | @@ -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) { |
| 23177 | | .slice => switch (zcu.intern_pool.indexToKey(operand_val.toIntern())) { |
| 23178 | | .slice => |slice| .{ .fromInterned(slice.ptr), .fromInterned(slice.len) }, |
| 23179 | | else => unreachable, |
| 23180 | | }, |
| 23181 | | .one, .many, .c => .{ operand_val, null }, |
| 23229 | const ptr_val: Value = switch (src_info.flags.size) { |
| 23230 | .slice => .fromInterned(zcu.intern_pool.indexToKey(operand_val.toIntern()).slice.ptr), |
| 23231 | .one, .many, .c => operand_val, |
| 23182 | 23232 | }; |
| 23183 | 23233 | |
| 23184 | 23234 | if (dest_align.compare(.gt, src_align)) { |
| ... | ... | @@ -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 | 23264 | // Any to non-slice |
| 23202 | 23265 | const new_ptr_val = try pt.getCoerced(ptr_val, dest_ty); |
| 23203 | 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 | 23270 | try sema.validateRuntimeValue(block, operand_src, operand); |
| ... | ... | @@ -23246,6 +23273,11 @@ fn ptrCastFull( |
| 23246 | 23273 | const need_null_check = can_cast_to_int and block.wantSafety() and operand_ty.ptrAllowsZero(zcu) and !dest_ty.ptrAllowsZero(zcu); |
| 23247 | 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 | 23281 | // `operand` might be a slice. If `need_operand_ptr`, we'll populate `operand_ptr` with the raw pointer. |
| 23250 | 23282 | const need_operand_ptr = src_info.flags.size != .slice or // we already have it |
| 23251 | 23283 | dest_info.flags.size != .slice or // the result is a raw pointer |
| ... | ... | @@ -23347,67 +23379,49 @@ fn ptrCastFull( |
| 23347 | 23379 | // We need to deconstruct the slice (if applicable) and reconstruct it. |
| 23348 | 23380 | assert(need_operand_ptr); |
| 23349 | 23381 | |
| 23350 | | const result_len: Air.Inst.Ref = len: { |
| 23351 | | if (src_info.flags.size == .slice and !slice_needs_len_change) { |
| 23382 | const result_len: Air.Inst.Ref = switch (dest_slice_len.?) { |
| 23383 | .undef => try pt.undefRef(.usize), |
| 23384 | .constant => |n| try pt.intRef(.usize, n), |
| 23385 | .equal_runtime_src_slice => len: { |
| 23352 | 23386 | assert(need_operand_len); |
| 23353 | 23387 | break :len operand_len; |
| 23354 | | } |
| 23355 | | |
| 23356 | | const src_elem_size = src_slice_like_elem.abiSize(zcu); |
| 23357 | | const dest_elem_size = Type.fromInterned(dest_info.child).abiSize(zcu); |
| 23358 | | if (src_info.flags.size != .slice) { |
| 23359 | | assert(src_slice_like); |
| 23360 | | const src_len = Type.fromInterned(src_info.child).arrayLen(zcu); |
| 23361 | | const bytes = src_len * src_elem_size; |
| 23362 | | const dest_len = std.math.divExact(u64, bytes, dest_elem_size) catch |err| switch (err) { |
| 23388 | }, |
| 23389 | .change_runtime_src_slice => |change| len: { |
| 23390 | assert(need_operand_len); |
| 23391 | // If `mul / div` is a whole number, then just multiply the length by it. |
| 23392 | if (std.math.divExact(u64, change.bytes_per_src, change.bytes_per_dest)) |dest_per_src| { |
| 23393 | const multiplier = try pt.intRef(.usize, dest_per_src); |
| 23394 | break :len try block.addBinOp(.mul, operand_len, multiplier); |
| 23395 | } else |err| switch (err) { |
| 23363 | 23396 | error.DivisionByZero => unreachable, |
| 23364 | | error.UnexpectedRemainder => return sema.fail(block, src, "slice length '{d}' does not divide exactly into destination elements", .{src_len}), |
| 23365 | | }; |
| 23366 | | break :len try pt.intRef(.usize, dest_len); |
| 23367 | | } |
| 23368 | | |
| 23369 | | assert(need_operand_len); |
| 23370 | | |
| 23371 | | // If `src_elem_size * n == dest_elem_size`, then just multiply the length by `n`. |
| 23372 | | if (std.math.divExact(u64, src_elem_size, dest_elem_size)) |dest_per_src| { |
| 23373 | | const multiplier = try pt.intRef(.usize, dest_per_src); |
| 23374 | | break :len try block.addBinOp(.mul, operand_len, multiplier); |
| 23375 | | } else |err| switch (err) { |
| 23376 | | error.DivisionByZero => unreachable, |
| 23377 | | error.UnexpectedRemainder => {}, // fall through to code below |
| 23378 | | } |
| 23379 | | |
| 23380 | | // If `src_elem_size == dest_elem_size * n`, then divide the length by `n`. |
| 23381 | | // This incurs a safety check. |
| 23382 | | if (std.math.divExact(u64, dest_elem_size, src_elem_size)) |src_per_dest| { |
| 23383 | | const divisor = try pt.intRef(.usize, src_per_dest); |
| 23397 | error.UnexpectedRemainder => {}, // fall through to code below |
| 23398 | } |
| 23399 | // If `div / mul` is a whole number, then just divide the length by it. |
| 23400 | // This incurs a safety check. |
| 23401 | if (std.math.divExact(u64, change.bytes_per_dest, change.bytes_per_src)) |src_per_dest| { |
| 23402 | const divisor = try pt.intRef(.usize, src_per_dest); |
| 23403 | if (block.wantSafety()) { |
| 23404 | // Check that the element count divides neatly. |
| 23405 | const remainder = try block.addBinOp(.rem, operand_len, divisor); |
| 23406 | const ok = try block.addBinOp(.cmp_eq, remainder, .zero_usize); |
| 23407 | try sema.addSafetyCheckCall(block, src, ok, .@"panic.sliceCastLenRemainder", &.{operand_len}); |
| 23408 | } |
| 23409 | break :len try block.addBinOp(.div_exact, operand_len, divisor); |
| 23410 | } else |err| switch (err) { |
| 23411 | error.DivisionByZero => unreachable, |
| 23412 | error.UnexpectedRemainder => {}, // fall through to code below |
| 23413 | } |
| 23414 | // Fallback: the elements don't divide easily. We'll multiply *and* divide. This incurs a safety check. |
| 23415 | const total_bytes_ref = try block.addBinOp(.mul, operand_len, try pt.intRef(.usize, change.bytes_per_src)); |
| 23416 | const bytes_per_dest_ref = try pt.intRef(.usize, change.bytes_per_dest); |
| 23384 | 23417 | if (block.wantSafety()) { |
| 23385 | | // Check that the element count divides neatly. |
| 23386 | | const remainder = try block.addBinOp(.rem, operand_len, divisor); |
| 23418 | // Check that `total_bytes_ref` divides neatly into `bytes_per_dest_ref`. |
| 23419 | const remainder = try block.addBinOp(.rem, total_bytes_ref, bytes_per_dest_ref); |
| 23387 | 23420 | const ok = try block.addBinOp(.cmp_eq, remainder, .zero_usize); |
| 23388 | 23421 | try sema.addSafetyCheckCall(block, src, ok, .@"panic.sliceCastLenRemainder", &.{operand_len}); |
| 23389 | 23422 | } |
| 23390 | | break :len try block.addBinOp(.div_exact, operand_len, divisor); |
| 23391 | | } else |err| switch (err) { |
| 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); |
| 23423 | break :len try block.addBinOp(.div_exact, total_bytes_ref, bytes_per_dest_ref); |
| 23424 | }, |
| 23411 | 23425 | }; |
| 23412 | 23426 | |
| 23413 | 23427 | const operand_ptr_ty = sema.typeOf(operand_ptr); |