Skip to content

Commit aa3db7c

Browse files
committed
Sema: correctly handle empty by-ref initializers
Resolves: #23210
1 parent ea57fb5 commit aa3db7c

File tree

2 files changed

+72
-1
lines changed

2 files changed

+72
-1
lines changed

src/Sema.zig

+31-1
Original file line numberDiff line numberDiff line change
@@ -20310,11 +20310,41 @@ fn zirStructInitEmptyResult(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is
2031020310
const zcu = pt.zcu;
2031120311
const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
2031220312
const src = block.nodeOffset(inst_data.src_node);
20313+
2031320314
// 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+
2031520323
const init_ty = if (is_byref) ty: {
2031620324
const ptr_ty = ty_operand.optEuBaseType(zcu);
2031720325
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+
}
2031820348
if (!ptr_ty.isSlice(zcu)) {
2031920349
break :ty ptr_ty.childType(zcu);
2032020350
}

test/behavior/array.zig

+41
Original file line numberDiff line numberDiff line change
@@ -1094,3 +1094,44 @@ test "@splat zero-length array" {
10941094
try S.doTheTest(?*anyopaque, null);
10951095
try comptime S.doTheTest(?*anyopaque, null);
10961096
}
1097+
1098+
test "initialize slice with reference to empty array initializer" {
1099+
const a: []const u8 = &.{};
1100+
comptime assert(a.len == 0);
1101+
}
1102+
1103+
test "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+
1108+
test "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+
1114+
test "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+
1119+
test "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+
1128+
test "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+
}

0 commit comments

Comments
 (0)