authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-05-18 20:16:09+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-05-19 19:26:12+01:00
logf2077f57ae03ef21f1bfee6e4e041c5f466fc3be
tree9401d586e5fd757e1ac1805f0af7bd8fcaed77bf
parentac8720f399d4ad4ac287d6f78dae2a262cc3b1d6

Sema: allow `@ptrCast` single-item pointer to slice

Also, rework this logic a little to make it simpler. The length of the result slice is now computed in one place.

4 files changed, 204 insertions(+), 138 deletions(-)

src/Sema.zig+148-134
...@@ -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);
2289522895
22896 const src_slice_like = src_info.flags.size == .slice or22896 const DestSliceLen = union(enum) {
22897 (src_info.flags.size == .one and Type.fromInterned(src_info.child).zigTypeTag(zcu) == .array);22897 undef,
2289822898 constant: u64,
22899 const dest_slice_like = dest_info.flags.size == .slice or22899 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 {
2290122901 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 };
2290522905 // 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 array22909 .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) {
2291322913 .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't22923 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 };
2293222976
22933 // The checking logic in this function must stay in sync with Sema.coerceInMemoryAllowedPtrs22977 // The checking logic in this function must stay in sync with Sema.coerceInMemoryAllowedPtrs
2293422978
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 -> []T23050 // [*]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 }
2317523228
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 };
2318323233
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 }
2319923249
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-slice23264 // 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 }
2324223269
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);
2324823275
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 it23282 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 pointer23283 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);
2334923381
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 },
2335523389 .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.
2336823401 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);
2337023403 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 below23410 } else |err| switch (err) {
23378 }23411 error.DivisionByZero => unreachable,
2337923412 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 };
2341223426
23413 const operand_ptr_ty = sema.typeOf(operand_ptr);23427 const operand_ptr_ty = sema.typeOf(operand_ptr);
test/behavior/ptrcast.zig+44
...@@ -527,3 +527,47 @@ test "@ptrCast slice of zero-bit type to different slice" {...@@ -527,3 +527,47 @@ test "@ptrCast slice of zero-bit type to different slice" {
527 try comptime S.doTheTest(u0, &.{ 0, 0, 0, 0 });527 try comptime S.doTheTest(u0, &.{ 0, 0, 0, 0 });
528 try comptime S.doTheTest(packed struct(u0) {}, &.{ .{}, .{} });528 try comptime S.doTheTest(packed struct(u0) {}, &.{ .{}, .{} });
529}529}
530
531test "@ptrCast single-item pointer to slice with length 1" {
532 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
533 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
534 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
535 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
536 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
537
538 const S = struct {
539 fn doTheTest(comptime T: type, ptr: *const T) !void {
540 const slice: []const T = @ptrCast(ptr);
541 try expect(slice.len == 1);
542 try expect(&slice[0] == ptr);
543 }
544 };
545 try S.doTheTest(u8, &123);
546 try S.doTheTest(void, &{});
547 try S.doTheTest(struct { x: u32 }, &.{ .x = 123 });
548 try comptime S.doTheTest(u8, &123);
549 try comptime S.doTheTest(void, &{});
550 try comptime S.doTheTest(struct { x: u32 }, &.{ .x = 123 });
551}
552
553test "@ptrCast single-item pointer to slice of bytes" {
554 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
555 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
556 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
557 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
558 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
559
560 const S = struct {
561 fn doTheTest(comptime T: type, ptr: *const T) !void {
562 const slice: []const u8 = @ptrCast(ptr);
563 try expect(slice.len == @sizeOf(T));
564 try expect(slice.ptr == @as([*]const u8, @ptrCast(ptr)));
565 }
566 };
567 try S.doTheTest(u16, &123);
568 try S.doTheTest(void, &{});
569 try S.doTheTest(struct { x: u32 }, &.{ .x = 123 });
570 try comptime S.doTheTest(u16, &123);
571 try comptime S.doTheTest(void, &{});
572 try comptime S.doTheTest(struct { x: u32 }, &.{ .x = 123 });
573}
test/cases/compile_errors/cast_ptr_to_slice_of_larger.zig created+8
...@@ -0,0 +1,8 @@
1export fn foo(ptr: *u8) void {
2 const slice: []align(1) u16 = @ptrCast(ptr);
3 _ = slice;
4}
5
6// error
7//
8// :2:35: error: type 'u8' does not divide exactly into destination elements
test/cases/compile_errors/slice_cast_change_len.zig+4-4
...@@ -48,10 +48,10 @@ comptime {...@@ -48,10 +48,10 @@ comptime {
4848
49// error49// error
50//50//
51// :3:31: error: cannot infer length of slice of 'type' from slice of 'comptime_int'51// :3:31: error: cannot infer length of comptime-only '[]const type' from incompatible '[]const comptime_int'
52// :16:30: error: slice length '3' does not divide exactly into destination elements52// :16:30: error: slice length '3' does not divide exactly into destination elements
53// :21:30: error: slice length '3' does not divide exactly into destination elements53// :21:30: error: type '[3]u8' does not divide exactly into destination elements
54// :28:31: error: slice length '1' does not divide exactly into destination elements54// :28:31: error: slice length '1' does not divide exactly into destination elements
55// :33:31: error: slice length '1' does not divide exactly into destination elements55// :33:31: error: type '[1][4]u8' does not divide exactly into destination elements
56// :40:30: error: slice length '1' does not divide exactly into destination elements56// :40:30: error: slice length '1' does not divide exactly into destination elements
57// :45:30: error: slice length '1' does not divide exactly into destination elements57// :45:30: error: type '[1][3]u8' does not divide exactly into destination elements