authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-03-12 03:00:45+00:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-03-16 03:05:33+00:00
logaa3db7cc15520228c0b44328198c78a31e194209
tree8df74ef528c3bfdefe00d95f98ad9e0acd9b8afe
parentea57fb55ea3e433ffb2801f818084fdc9a3c5618

Sema: correctly handle empty by-ref initializers

Resolves: #23210

2 files changed, 72 insertions(+), 1 deletions(-)

src/Sema.zig+31-1
...@@ -20310,11 +20310,41 @@ fn zirStructInitEmptyResult(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is...@@ -20310,11 +20310,41 @@ fn zirStructInitEmptyResult(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is
20310 const zcu = pt.zcu;20310 const zcu = pt.zcu;
20311 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;20311 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
20312 const src = block.nodeOffset(inst_data.src_node);20312 const src = block.nodeOffset(inst_data.src_node);
20313
20313 // Generic poison means this is an untyped anonymous empty struct/array init20314 // Generic poison means this is an untyped anonymous empty struct/array init
20314 const ty_operand = try sema.resolveTypeOrPoison(block, src, inst_data.operand) orelse return .empty_tuple;20315 const ty_operand = try sema.resolveTypeOrPoison(block, src, inst_data.operand) orelse {
20316 if (is_byref) {
20317 return sema.uavRef(.empty_tuple);
20318 } else {
20319 return .empty_tuple;
20320 }
20321 };
20322
20315 const init_ty = if (is_byref) ty: {20323 const init_ty = if (is_byref) ty: {
20316 const ptr_ty = ty_operand.optEuBaseType(zcu);20324 const ptr_ty = ty_operand.optEuBaseType(zcu);
20317 assert(ptr_ty.zigTypeTag(zcu) == .pointer); // validated by a previous instruction20325 assert(ptr_ty.zigTypeTag(zcu) == .pointer); // validated by a previous instruction
20326 switch (ptr_ty.ptrSize(zcu)) {
20327 // Use a zero-length array for a slice or many-ptr result
20328 .slice, .many => break :ty try pt.arrayType(.{
20329 .len = 0,
20330 .child = ptr_ty.childType(zcu).toIntern(),
20331 .sentinel = if (ptr_ty.sentinel(zcu)) |s| s.toIntern() else .none,
20332 }),
20333 // Just use the child type for a single-pointer or C-pointer result
20334 .one, .c => {
20335 const child = ptr_ty.childType(zcu);
20336 if (child.toIntern() == .anyopaque_type) {
20337 // ...unless that child is anyopaque, in which case this is equivalent to an untyped init.
20338 // `.{}` is an empty tuple.
20339 if (is_byref) {
20340 return sema.uavRef(.empty_tuple);
20341 } else {
20342 return .empty_tuple;
20343 }
20344 }
20345 break :ty child;
20346 },
20347 }
20318 if (!ptr_ty.isSlice(zcu)) {20348 if (!ptr_ty.isSlice(zcu)) {
20319 break :ty ptr_ty.childType(zcu);20349 break :ty ptr_ty.childType(zcu);
20320 }20350 }
test/behavior/array.zig+41
...@@ -1094,3 +1094,44 @@ test "@splat zero-length array" {...@@ -1094,3 +1094,44 @@ test "@splat zero-length array" {
1094 try S.doTheTest(?*anyopaque, null);1094 try S.doTheTest(?*anyopaque, null);
1095 try comptime S.doTheTest(?*anyopaque, null);1095 try comptime S.doTheTest(?*anyopaque, null);
1096}1096}
1097
1098test "initialize slice with reference to empty array initializer" {
1099 const a: []const u8 = &.{};
1100 comptime assert(a.len == 0);
1101}
1102
1103test "initialize many-pointer with reference to empty array initializer" {
1104 const a: [*]const u8 = &.{};
1105 _ = a; // nothing meaningful to test; points to zero bits
1106}
1107
1108test "initialize sentinel-terminated slice with reference to empty array initializer" {
1109 const a: [:0]const u8 = &.{};
1110 comptime assert(a.len == 0);
1111 comptime assert(a[0] == 0);
1112}
1113
1114test "initialize sentinel-terminated many-pointer with reference to empty array initializer" {
1115 const a: [*:0]const u8 = &.{};
1116 comptime assert(a[0] == 0);
1117}
1118
1119test "pass pointer to empty array initializer to anytype parameter" {
1120 const S = struct {
1121 fn TypeOf(x: anytype) type {
1122 return @TypeOf(x);
1123 }
1124 };
1125 comptime assert(S.TypeOf(&.{}) == @TypeOf(&.{}));
1126}
1127
1128test "initialize pointer to anyopaque with reference to empty array initializer" {
1129 const ptr: *const anyopaque = &.{};
1130 // The above acts like an untyped initializer, since the `.{}` has no result type.
1131 // So, `ptr` points in memory to an empty tuple (`@TypeOf(.{})`).
1132 const casted: *const @TypeOf(.{}) = @alignCast(@ptrCast(ptr));
1133 const loaded = casted.*;
1134 // `val` should be a `@TypeOf(.{})`, as expected.
1135 // We can't check the value, but it's zero-bit, so the type matching is good enough.
1136 comptime assert(@TypeOf(loaded) == @TypeOf(.{}));
1137}