authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-03-31 09:11:18+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-03-31 19:03:08-04:00
logd53cc5e5b2ac51793ea19a847d8cee409af1dee3
treee60cee6a989fd62a64d93d9acdc19729f4c44ed7
parent0bdc0bb534ed6d3cc787b5bca44173d2b49480da

Sema: allow `@ptrCast` slice of zero-bit type to slice of non-zero-bit type

This is actually completely well-defined. The resulting slice always has 0 elements. The only disallowed case is casting *to* a slice of a zero-bit type, because in that case, you cna't figure out how many destination elements to use (and there's *no* valid destination length if the source slice corresponds to more than 0 bits).

2 files changed, 25 insertions(+), 2 deletions(-)

src/Sema.zig+5-2
...@@ -22837,11 +22837,14 @@ fn ptrCastFull(...@@ -22837,11 +22837,14 @@ fn ptrCastFull(
22837 if (src_slice_like_elem.comptimeOnly(zcu) or dest_elem.comptimeOnly(zcu)) {22837 if (src_slice_like_elem.comptimeOnly(zcu) or dest_elem.comptimeOnly(zcu)) {
22838 return sema.fail(block, src, "cannot infer length of slice of '{}' from slice of '{}'", .{ dest_elem.fmt(pt), src_slice_like_elem.fmt(pt) });22838 return sema.fail(block, src, "cannot infer length of slice of '{}' from slice of '{}'", .{ dest_elem.fmt(pt), src_slice_like_elem.fmt(pt) });
22839 }22839 }
22840 const src_elem_size = src_slice_like_elem.abiSize(zcu);22840 // It's okay for `src_slice_like_elem` to be 0-bit; the resulting slice will just always have 0 elements.
22841 // However, `dest_elem` can't be 0-bit. If it were, then either the source slice has 0 bits and we don't
22842 // know how what `result.len` should be, or the source has >0 bits and there is no valid `result.len`.
22841 const dest_elem_size = dest_elem.abiSize(zcu);22843 const dest_elem_size = dest_elem.abiSize(zcu);
22842 if (src_elem_size == 0 or dest_elem_size == 0) {22844 if (dest_elem_size == 0) {
22843 return sema.fail(block, src, "cannot infer length of slice of '{}' from slice of '{}'", .{ dest_elem.fmt(pt), src_slice_like_elem.fmt(pt) });22845 return sema.fail(block, src, "cannot infer length of slice of '{}' from slice of '{}'", .{ dest_elem.fmt(pt), src_slice_like_elem.fmt(pt) });
22844 }22846 }
22847 const src_elem_size = src_slice_like_elem.abiSize(zcu);
22845 break :need_len_change src_elem_size != dest_elem_size;22848 break :need_len_change src_elem_size != dest_elem_size;
22846 } else false;22849 } else false;
2284722850
test/behavior/ptrcast.zig+20
...@@ -507,3 +507,23 @@ test "@ptrCast array pointer to slice with complex length decrease" {...@@ -507,3 +507,23 @@ test "@ptrCast array pointer to slice with complex length decrease" {
507 try S.doTheTest(@splat(0));507 try S.doTheTest(@splat(0));
508 try comptime S.doTheTest(@splat(0));508 try comptime S.doTheTest(@splat(0));
509}509}
510
511test "@ptrCast slice of zero-bit type to different slice" {
512 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
513 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
514 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
515 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
516
517 const S = struct {
518 fn doTheTest(comptime T: type, zero_bits: []const T) !void {
519 const out: []const u8 = @ptrCast(zero_bits);
520 try expect(out.len == 0);
521 }
522 };
523 try S.doTheTest(void, &.{ {}, {}, {} });
524 try S.doTheTest(u0, &.{ 0, 0, 0, 0 });
525 try S.doTheTest(packed struct(u0) {}, &.{ .{}, .{} });
526 try comptime S.doTheTest(void, &.{ {}, {}, {} });
527 try comptime S.doTheTest(u0, &.{ 0, 0, 0, 0 });
528 try comptime S.doTheTest(packed struct(u0) {}, &.{ .{}, .{} });
529}