authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-03-12 03:00:45+00:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2025-03-18 04:57:57+01:00
log6c690a966ad21bfa7fabf1b1f9a25bb2d3f826cb
treef78550fda9996663be07419edb7f1d5dff9d8a49
parentf9549504851c9445ea474e78cedc5d52a5b21f36
signaturebadge-check Signed by SSH key SHA256:7B/LJ7bpR1eX8aCXSr4mtd5M45VMPKcx9zY8e95b5QM

Sema: correctly handle empty by-ref initializers

Resolves: #23210

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

src/Sema.zig+31-1
...@@ -20290,11 +20290,41 @@ fn zirStructInitEmptyResult(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is...@@ -20290,11 +20290,41 @@ fn zirStructInitEmptyResult(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is
20290 const zcu = pt.zcu;20290 const zcu = pt.zcu;
20291 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;20291 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
20292 const src = block.nodeOffset(inst_data.src_node);20292 const src = block.nodeOffset(inst_data.src_node);
20293
20293 // Generic poison means this is an untyped anonymous empty struct/array init20294 // Generic poison means this is an untyped anonymous empty struct/array init
20294 const ty_operand = try sema.resolveTypeOrPoison(block, src, inst_data.operand) orelse return .empty_tuple;20295 const ty_operand = try sema.resolveTypeOrPoison(block, src, inst_data.operand) orelse {
20296 if (is_byref) {
20297 return sema.uavRef(.empty_tuple);
20298 } else {
20299 return .empty_tuple;
20300 }
20301 };
20302
20295 const init_ty = if (is_byref) ty: {20303 const init_ty = if (is_byref) ty: {
20296 const ptr_ty = ty_operand.optEuBaseType(zcu);20304 const ptr_ty = ty_operand.optEuBaseType(zcu);
20297 assert(ptr_ty.zigTypeTag(zcu) == .pointer); // validated by a previous instruction20305 assert(ptr_ty.zigTypeTag(zcu) == .pointer); // validated by a previous instruction
20306 switch (ptr_ty.ptrSize(zcu)) {
20307 // Use a zero-length array for a slice or many-ptr result
20308 .slice, .many => break :ty try pt.arrayType(.{
20309 .len = 0,
20310 .child = ptr_ty.childType(zcu).toIntern(),
20311 .sentinel = if (ptr_ty.sentinel(zcu)) |s| s.toIntern() else .none,
20312 }),
20313 // Just use the child type for a single-pointer or C-pointer result
20314 .one, .c => {
20315 const child = ptr_ty.childType(zcu);
20316 if (child.toIntern() == .anyopaque_type) {
20317 // ...unless that child is anyopaque, in which case this is equivalent to an untyped init.
20318 // `.{}` is an empty tuple.
20319 if (is_byref) {
20320 return sema.uavRef(.empty_tuple);
20321 } else {
20322 return .empty_tuple;
20323 }
20324 }
20325 break :ty child;
20326 },
20327 }
20298 if (!ptr_ty.isSlice(zcu)) {20328 if (!ptr_ty.isSlice(zcu)) {
20299 break :ty ptr_ty.childType(zcu);20329 break :ty ptr_ty.childType(zcu);
20300 }20330 }
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}