From d53cc5e5b2ac51793ea19a847d8cee409af1dee3 Mon Sep 17 00:00:00 2001 From: mlugg Date: Mon, 31 Mar 2025 09:11:18 +0100 Subject: [PATCH] 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). --- src/Sema.zig | 7 +++++-- test/behavior/ptrcast.zig | 20 ++++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 3f231410a8..012bfb3a5c 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -22837,11 +22837,14 @@ fn ptrCastFull( if (src_slice_like_elem.comptimeOnly(zcu) or dest_elem.comptimeOnly(zcu)) { return sema.fail(block, src, "cannot infer length of slice of '{}' from slice of '{}'", .{ dest_elem.fmt(pt), src_slice_like_elem.fmt(pt) }); } - const src_elem_size = src_slice_like_elem.abiSize(zcu); + // It's okay for `src_slice_like_elem` to be 0-bit; the resulting slice will just always have 0 elements. + // However, `dest_elem` can't be 0-bit. If it were, then either the source slice has 0 bits and we don't + // know how what `result.len` should be, or the source has >0 bits and there is no valid `result.len`. const dest_elem_size = dest_elem.abiSize(zcu); - if (src_elem_size == 0 or dest_elem_size == 0) { + if (dest_elem_size == 0) { return sema.fail(block, src, "cannot infer length of slice of '{}' from slice of '{}'", .{ dest_elem.fmt(pt), src_slice_like_elem.fmt(pt) }); } + const src_elem_size = src_slice_like_elem.abiSize(zcu); break :need_len_change src_elem_size != dest_elem_size; } else false; diff --git a/test/behavior/ptrcast.zig b/test/behavior/ptrcast.zig index 91955e3e94..c8e247abfb 100644 --- a/test/behavior/ptrcast.zig +++ b/test/behavior/ptrcast.zig @@ -507,3 +507,23 @@ test "@ptrCast array pointer to slice with complex length decrease" { try S.doTheTest(@splat(0)); try comptime S.doTheTest(@splat(0)); } + +test "@ptrCast slice of zero-bit type to different slice" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + + const S = struct { + fn doTheTest(comptime T: type, zero_bits: []const T) !void { + const out: []const u8 = @ptrCast(zero_bits); + try expect(out.len == 0); + } + }; + try S.doTheTest(void, &.{ {}, {}, {} }); + try S.doTheTest(u0, &.{ 0, 0, 0, 0 }); + try S.doTheTest(packed struct(u0) {}, &.{ .{}, .{} }); + try comptime S.doTheTest(void, &.{ {}, {}, {} }); + try comptime S.doTheTest(u0, &.{ 0, 0, 0, 0 }); + try comptime S.doTheTest(packed struct(u0) {}, &.{ .{}, .{} }); +}