authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-03-31 09:11:18+01:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2025-04-02 08:43:13+02:00
logf5e7850686a0630015f26cc7f60e9d860a7dd05f
tree5f1c2aeffdcf487ed4661e3bc7282386042155dd
parent6ecd14321208e5f4562c2d4e4883aa46423ac725
signaturebadge-check Signed by SSH key SHA256:7B/LJ7bpR1eX8aCXSr4mtd5M45VMPKcx9zY8e95b5QM

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
...@@ -23488,11 +23488,14 @@ fn ptrCastFull(...@@ -23488,11 +23488,14 @@ fn ptrCastFull(
23488 if (src_slice_like_elem.comptimeOnly(zcu) or dest_elem.comptimeOnly(zcu)) {23488 if (src_slice_like_elem.comptimeOnly(zcu) or dest_elem.comptimeOnly(zcu)) {
23489 return sema.fail(block, src, "cannot infer length of slice of '{}' from slice of '{}'", .{ dest_elem.fmt(pt), src_slice_like_elem.fmt(pt) });23489 return sema.fail(block, src, "cannot infer length of slice of '{}' from slice of '{}'", .{ dest_elem.fmt(pt), src_slice_like_elem.fmt(pt) });
23490 }23490 }
23491 const src_elem_size = src_slice_like_elem.abiSize(zcu);23491 // It's okay for `src_slice_like_elem` to be 0-bit; the resulting slice will just always have 0 elements.
23492 // However, `dest_elem` can't be 0-bit. If it were, then either the source slice has 0 bits and we don't
23493 // know how what `result.len` should be, or the source has >0 bits and there is no valid `result.len`.
23492 const dest_elem_size = dest_elem.abiSize(zcu);23494 const dest_elem_size = dest_elem.abiSize(zcu);
23493 if (src_elem_size == 0 or dest_elem_size == 0) {23495 if (dest_elem_size == 0) {
23494 return sema.fail(block, src, "cannot infer length of slice of '{}' from slice of '{}'", .{ dest_elem.fmt(pt), src_slice_like_elem.fmt(pt) });23496 return sema.fail(block, src, "cannot infer length of slice of '{}' from slice of '{}'", .{ dest_elem.fmt(pt), src_slice_like_elem.fmt(pt) });
23495 }23497 }
23498 const src_elem_size = src_slice_like_elem.abiSize(zcu);
23496 break :need_len_change src_elem_size != dest_elem_size;23499 break :need_len_change src_elem_size != dest_elem_size;
23497 } else false;23500 } else false;
2349823501
test/behavior/ptrcast.zig+20
...@@ -509,3 +509,23 @@ test "@ptrCast array pointer to slice with complex length decrease" {...@@ -509,3 +509,23 @@ test "@ptrCast array pointer to slice with complex length decrease" {
509 try S.doTheTest(@splat(0));509 try S.doTheTest(@splat(0));
510 try comptime S.doTheTest(@splat(0));510 try comptime S.doTheTest(@splat(0));
511}511}
512
513test "@ptrCast slice of zero-bit type to different slice" {
514 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
515 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
516 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
517 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
518
519 const S = struct {
520 fn doTheTest(comptime T: type, zero_bits: []const T) !void {
521 const out: []const u8 = @ptrCast(zero_bits);
522 try expect(out.len == 0);
523 }
524 };
525 try S.doTheTest(void, &.{ {}, {}, {} });
526 try S.doTheTest(u0, &.{ 0, 0, 0, 0 });
527 try S.doTheTest(packed struct(u0) {}, &.{ .{}, .{} });
528 try comptime S.doTheTest(void, &.{ {}, {}, {} });
529 try comptime S.doTheTest(u0, &.{ 0, 0, 0, 0 });
530 try comptime S.doTheTest(packed struct(u0) {}, &.{ .{}, .{} });
531}