Skip to content

Commit c3d5428

Browse files
committed
Sema: properly handle noreturn fields in unions
1 parent b0a55e1 commit c3d5428

File tree

5 files changed

+270
-34
lines changed

5 files changed

+270
-34
lines changed

src/Sema.zig

Lines changed: 170 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3772,6 +3772,7 @@ fn validateStructInit(
37723772
}
37733773

37743774
var root_msg: ?*Module.ErrorMsg = null;
3775+
errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
37753776

37763777
const struct_ptr = try sema.resolveInst(struct_ptr_zir_ref);
37773778
if ((is_comptime or block.is_comptime) and
@@ -3947,6 +3948,7 @@ fn validateStructInit(
39473948
}
39483949

39493950
if (root_msg) |msg| {
3951+
root_msg = null;
39503952
if (struct_ty.castTag(.@"struct")) |struct_obj| {
39513953
const fqn = try struct_obj.data.getFullyQualifiedName(sema.mod);
39523954
defer gpa.free(fqn);
@@ -4005,6 +4007,8 @@ fn zirValidateArrayInit(
40054007
if (instrs.len != array_len and array_ty.isTuple()) {
40064008
const struct_obj = array_ty.castTag(.tuple).?.data;
40074009
var root_msg: ?*Module.ErrorMsg = null;
4010+
errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
4011+
40084012
for (struct_obj.values) |default_val, i| {
40094013
if (i < instrs.len) continue;
40104014

@@ -4019,6 +4023,7 @@ fn zirValidateArrayInit(
40194023
}
40204024

40214025
if (root_msg) |msg| {
4026+
root_msg = null;
40224027
return sema.failWithOwnedErrorMsg(msg);
40234028
}
40244029
}
@@ -8964,12 +8969,15 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
89648969
},
89658970
};
89668971

8967-
const union_originally = blk: {
8972+
const maybe_union_ty = blk: {
89688973
const zir_data = sema.code.instructions.items(.data);
89698974
const cond_index = Zir.refToIndex(extra.data.operand).?;
89708975
const raw_operand = sema.resolveInst(zir_data[cond_index].un_node.operand) catch unreachable;
8971-
break :blk sema.typeOf(raw_operand).zigTypeTag() == .Union;
8976+
break :blk sema.typeOf(raw_operand);
89728977
};
8978+
const union_originally = maybe_union_ty.zigTypeTag() == .Union;
8979+
var seen_union_fields: []?Module.SwitchProngSrc = &.{};
8980+
defer gpa.free(seen_union_fields);
89738981

89748982
const operand_ty = sema.typeOf(operand);
89758983

@@ -9004,7 +9012,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
90049012
.Union => unreachable, // handled in zirSwitchCond
90059013
.Enum => {
90069014
var seen_fields = try gpa.alloc(?Module.SwitchProngSrc, operand_ty.enumFieldCount());
9007-
defer gpa.free(seen_fields);
9015+
defer if (!union_originally) gpa.free(seen_fields);
9016+
if (union_originally) seen_union_fields = seen_fields;
90089017
mem.set(?Module.SwitchProngSrc, seen_fields, null);
90099018

90109019
// This is used for non-exhaustive enum values that do not correspond to any tags.
@@ -9637,18 +9646,28 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
96379646
const item = try sema.resolveInst(item_ref);
96389647
// `item` is already guaranteed to be constant known.
96399648

9640-
_ = sema.analyzeBodyInner(&case_block, body) catch |err| switch (err) {
9641-
error.ComptimeBreak => {
9642-
const zir_datas = sema.code.instructions.items(.data);
9643-
const break_data = zir_datas[sema.comptime_break_inst].@"break";
9644-
try sema.addRuntimeBreak(&case_block, .{
9645-
.block_inst = break_data.block_inst,
9646-
.operand = break_data.operand,
9647-
.inst = sema.comptime_break_inst,
9648-
});
9649-
},
9650-
else => |e| return e,
9651-
};
9649+
const analyze_body = if (union_originally) blk: {
9650+
const item_val = sema.resolveConstValue(block, .unneeded, item, undefined) catch unreachable;
9651+
const field_ty = maybe_union_ty.unionFieldType(item_val, sema.mod);
9652+
break :blk field_ty.zigTypeTag() != .NoReturn;
9653+
} else true;
9654+
9655+
if (analyze_body) {
9656+
_ = sema.analyzeBodyInner(&case_block, body) catch |err| switch (err) {
9657+
error.ComptimeBreak => {
9658+
const zir_datas = sema.code.instructions.items(.data);
9659+
const break_data = zir_datas[sema.comptime_break_inst].@"break";
9660+
try sema.addRuntimeBreak(&case_block, .{
9661+
.block_inst = break_data.block_inst,
9662+
.operand = break_data.operand,
9663+
.inst = sema.comptime_break_inst,
9664+
});
9665+
},
9666+
else => |e| return e,
9667+
};
9668+
} else {
9669+
_ = try case_block.addNoOp(.unreach);
9670+
}
96529671

96539672
try wip_captures.finalize();
96549673

@@ -9689,20 +9708,34 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
96899708
if (ranges_len == 0) {
96909709
cases_len += 1;
96919710

9711+
const analyze_body = if (union_originally)
9712+
for (items) |item_ref| {
9713+
const item = try sema.resolveInst(item_ref);
9714+
const item_val = sema.resolveConstValue(block, .unneeded, item, undefined) catch unreachable;
9715+
const field_ty = maybe_union_ty.unionFieldType(item_val, sema.mod);
9716+
if (field_ty.zigTypeTag() != .NoReturn) break true;
9717+
} else false
9718+
else
9719+
true;
9720+
96929721
const body = sema.code.extra[extra_index..][0..body_len];
96939722
extra_index += body_len;
9694-
_ = sema.analyzeBodyInner(&case_block, body) catch |err| switch (err) {
9695-
error.ComptimeBreak => {
9696-
const zir_datas = sema.code.instructions.items(.data);
9697-
const break_data = zir_datas[sema.comptime_break_inst].@"break";
9698-
try sema.addRuntimeBreak(&case_block, .{
9699-
.block_inst = break_data.block_inst,
9700-
.operand = break_data.operand,
9701-
.inst = sema.comptime_break_inst,
9702-
});
9703-
},
9704-
else => |e| return e,
9705-
};
9723+
if (analyze_body) {
9724+
_ = sema.analyzeBodyInner(&case_block, body) catch |err| switch (err) {
9725+
error.ComptimeBreak => {
9726+
const zir_datas = sema.code.instructions.items(.data);
9727+
const break_data = zir_datas[sema.comptime_break_inst].@"break";
9728+
try sema.addRuntimeBreak(&case_block, .{
9729+
.block_inst = break_data.block_inst,
9730+
.operand = break_data.operand,
9731+
.inst = sema.comptime_break_inst,
9732+
});
9733+
},
9734+
else => |e| return e,
9735+
};
9736+
} else {
9737+
_ = try case_block.addNoOp(.unreach);
9738+
}
97069739

97079740
try cases_extra.ensureUnusedCapacity(gpa, 2 + items.len +
97089741
case_block.instructions.items.len);
@@ -9824,7 +9857,17 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
98249857
case_block.instructions.shrinkRetainingCapacity(0);
98259858
case_block.wip_capture_scope = wip_captures.scope;
98269859

9827-
if (special.body.len != 0) {
9860+
const analyze_body = if (union_originally)
9861+
for (seen_union_fields) |seen_field, index| {
9862+
if (seen_field != null) continue;
9863+
const union_obj = maybe_union_ty.cast(Type.Payload.Union).?.data;
9864+
const field_ty = union_obj.fields.values()[index].ty;
9865+
if (field_ty.zigTypeTag() != .NoReturn) break true;
9866+
} else false
9867+
else
9868+
true;
9869+
9870+
if (special.body.len != 0 and analyze_body) {
98289871
_ = sema.analyzeBodyInner(&case_block, special.body) catch |err| switch (err) {
98299872
error.ComptimeBreak => {
98309873
const zir_datas = sema.code.instructions.items(.data);
@@ -13225,6 +13268,14 @@ fn analyzeCmpUnionTag(
1322513268
const coerced_tag = try sema.coerce(block, union_tag_ty, tag, tag_src);
1322613269
const coerced_union = try sema.coerce(block, union_tag_ty, un, un_src);
1322713270

13271+
if (try sema.resolveMaybeUndefVal(block, tag_src, coerced_tag)) |enum_val| {
13272+
if (enum_val.isUndef()) return sema.addConstUndef(Type.bool);
13273+
const field_ty = union_ty.unionFieldType(enum_val, sema.mod);
13274+
if (field_ty.zigTypeTag() == .NoReturn) {
13275+
return Air.Inst.Ref.bool_false;
13276+
}
13277+
}
13278+
1322813279
return sema.cmpSelf(block, src, coerced_union, coerced_tag, op, un_src, tag_src);
1322913280
}
1323013281

@@ -15579,6 +15630,8 @@ fn finishStructInit(
1557915630
const gpa = sema.gpa;
1558015631

1558115632
var root_msg: ?*Module.ErrorMsg = null;
15633+
errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
15634+
1558215635
if (struct_ty.isAnonStruct()) {
1558315636
const struct_obj = struct_ty.castTag(.anon_struct).?.data;
1558415637
for (struct_obj.values) |default_val, i| {
@@ -15634,6 +15687,7 @@ fn finishStructInit(
1563415687
}
1563515688

1563615689
if (root_msg) |msg| {
15690+
root_msg = null;
1563715691
if (struct_ty.castTag(.@"struct")) |struct_obj| {
1563815692
const fqn = try struct_obj.data.getFullyQualifiedName(sema.mod);
1563915693
defer gpa.free(fqn);
@@ -21682,6 +21736,18 @@ fn unionFieldPtr(
2168221736
.@"addrspace" = union_ptr_ty.ptrAddressSpace(),
2168321737
});
2168421738

21739+
if (initializing and field.ty.zigTypeTag() == .NoReturn) {
21740+
const msg = msg: {
21741+
const msg = try sema.errMsg(block, src, "cannot initialize 'noreturn' field of union", .{});
21742+
errdefer msg.destroy(sema.gpa);
21743+
21744+
try sema.addFieldErrNote(block, union_ty, field_index, msg, "field '{s}' declared here", .{field_name});
21745+
try sema.addDeclaredHereNote(msg, union_ty);
21746+
break :msg msg;
21747+
};
21748+
return sema.failWithOwnedErrorMsg(msg);
21749+
}
21750+
2168521751
if (try sema.resolveDefinedValue(block, src, union_ptr)) |union_ptr_val| ct: {
2168621752
switch (union_obj.layout) {
2168721753
.Auto => if (!initializing) {
@@ -21734,6 +21800,10 @@ fn unionFieldPtr(
2173421800
const ok = try block.addBinOp(.cmp_eq, active_tag, wanted_tag);
2173521801
try sema.addSafetyCheck(block, ok, .inactive_union_field);
2173621802
}
21803+
if (field.ty.zigTypeTag() == .NoReturn) {
21804+
_ = try block.addNoOp(.unreach);
21805+
return Air.Inst.Ref.unreachable_value;
21806+
}
2173721807
return block.addStructFieldPtr(union_ptr, field_index, ptr_field_ty);
2173821808
}
2173921809

@@ -21802,6 +21872,10 @@ fn unionFieldVal(
2180221872
const ok = try block.addBinOp(.cmp_eq, active_tag, wanted_tag);
2180321873
try sema.addSafetyCheck(block, ok, .inactive_union_field);
2180421874
}
21875+
if (field.ty.zigTypeTag() == .NoReturn) {
21876+
_ = try block.addNoOp(.unreach);
21877+
return Air.Inst.Ref.unreachable_value;
21878+
}
2180521879
return block.addStructFieldVal(union_byval, field_index, field.ty);
2180621880
}
2180721881

@@ -25002,6 +25076,18 @@ fn coerceEnumToUnion(
2500225076
};
2500325077
const field = union_obj.fields.values()[field_index];
2500425078
const field_ty = try sema.resolveTypeFields(block, inst_src, field.ty);
25079+
if (field_ty.zigTypeTag() == .NoReturn) {
25080+
const msg = msg: {
25081+
const msg = try sema.errMsg(block, inst_src, "cannot initialize 'noreturn' field of union", .{});
25082+
errdefer msg.destroy(sema.gpa);
25083+
25084+
const field_name = union_obj.fields.keys()[field_index];
25085+
try sema.addFieldErrNote(block, union_ty, field_index, msg, "field '{s}' declared here", .{field_name});
25086+
try sema.addDeclaredHereNote(msg, union_ty);
25087+
break :msg msg;
25088+
};
25089+
return sema.failWithOwnedErrorMsg(msg);
25090+
}
2500525091
const opv = (try sema.typeHasOnePossibleValue(block, inst_src, field_ty)) orelse {
2500625092
const msg = msg: {
2500725093
const field_name = union_obj.fields.keys()[field_index];
@@ -25037,13 +25123,37 @@ fn coerceEnumToUnion(
2503725123
return sema.failWithOwnedErrorMsg(msg);
2503825124
}
2503925125

25126+
const union_obj = union_ty.cast(Type.Payload.Union).?.data;
25127+
{
25128+
var msg: ?*Module.ErrorMsg = null;
25129+
errdefer if (msg) |some| some.destroy(sema.gpa);
25130+
25131+
for (union_obj.fields.values()) |field, i| {
25132+
if (field.ty.zigTypeTag() == .NoReturn) {
25133+
const err_msg = msg orelse try sema.errMsg(
25134+
block,
25135+
inst_src,
25136+
"runtime coercion from enum '{}' to union '{}' which has a 'noreturn' field",
25137+
.{ tag_ty.fmt(sema.mod), union_ty.fmt(sema.mod) },
25138+
);
25139+
msg = err_msg;
25140+
25141+
try sema.addFieldErrNote(block, union_ty, i, err_msg, "'noreturn' field here", .{});
25142+
}
25143+
}
25144+
if (msg) |some| {
25145+
msg = null;
25146+
try sema.addDeclaredHereNote(some, union_ty);
25147+
return sema.failWithOwnedErrorMsg(some);
25148+
}
25149+
}
25150+
2504025151
// If the union has all fields 0 bits, the union value is just the enum value.
2504125152
if (union_ty.unionHasAllZeroBitFieldTypes()) {
2504225153
return block.addBitCast(union_ty, enum_tag);
2504325154
}
2504425155

2504525156
const msg = msg: {
25046-
const union_obj = union_ty.cast(Type.Payload.Union).?.data;
2504725157
const msg = try sema.errMsg(
2504825158
block,
2504925159
inst_src,
@@ -25054,11 +25164,11 @@ fn coerceEnumToUnion(
2505425164

2505525165
var it = union_obj.fields.iterator();
2505625166
var field_index: usize = 0;
25057-
while (it.next()) |field| {
25167+
while (it.next()) |field| : (field_index += 1) {
2505825168
const field_name = field.key_ptr.*;
2505925169
const field_ty = field.value_ptr.ty;
25170+
if (!field_ty.hasRuntimeBits()) continue;
2506025171
try sema.addFieldErrNote(block, union_ty, field_index, msg, "field '{s}' has type '{}'", .{ field_name, field_ty.fmt(sema.mod) });
25061-
field_index += 1;
2506225172
}
2506325173
try sema.addDeclaredHereNote(msg, union_ty);
2506425174
break :msg msg;
@@ -25361,6 +25471,7 @@ fn coerceTupleToStruct(
2536125471

2536225472
// Populate default field values and report errors for missing fields.
2536325473
var root_msg: ?*Module.ErrorMsg = null;
25474+
errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
2536425475

2536525476
for (field_refs) |*field_ref, i| {
2536625477
if (field_ref.* != .none) continue;
@@ -25386,6 +25497,7 @@ fn coerceTupleToStruct(
2538625497
}
2538725498

2538825499
if (root_msg) |msg| {
25500+
root_msg = null;
2538925501
try sema.addDeclaredHereNote(msg, struct_ty);
2539025502
return sema.failWithOwnedErrorMsg(msg);
2539125503
}
@@ -25455,6 +25567,7 @@ fn coerceTupleToTuple(
2545525567

2545625568
// Populate default field values and report errors for missing fields.
2545725569
var root_msg: ?*Module.ErrorMsg = null;
25570+
errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
2545825571

2545925572
for (field_refs) |*field_ref, i| {
2546025573
if (field_ref.* != .none) continue;
@@ -25490,6 +25603,7 @@ fn coerceTupleToTuple(
2549025603
}
2549125604

2549225605
if (root_msg) |msg| {
25606+
root_msg = null;
2549325607
try sema.addDeclaredHereNote(msg, tuple_ty);
2549425608
return sema.failWithOwnedErrorMsg(msg);
2549525609
}
@@ -27914,6 +28028,18 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void
2791428028
};
2791528029
return sema.failWithOwnedErrorMsg(msg);
2791628030
}
28031+
if (field_ty.zigTypeTag() == .NoReturn) {
28032+
const msg = msg: {
28033+
const tree = try sema.getAstTree(&block_scope);
28034+
const field_src = enumFieldSrcLoc(decl, tree.*, 0, i);
28035+
const msg = try sema.errMsg(&block_scope, field_src, "struct fields cannot be 'noreturn'", .{});
28036+
errdefer msg.destroy(sema.gpa);
28037+
28038+
try sema.addDeclaredHereNote(msg, field_ty);
28039+
break :msg msg;
28040+
};
28041+
return sema.failWithOwnedErrorMsg(msg);
28042+
}
2791728043
if (struct_obj.layout == .Extern and !sema.validateExternType(field.ty, .other)) {
2791828044
const msg = msg: {
2791928045
const tree = try sema.getAstTree(&block_scope);
@@ -28725,6 +28851,16 @@ fn enumFieldSrcLoc(
2872528851
.container_decl_arg_trailing,
2872628852
=> tree.containerDeclArg(enum_node),
2872728853

28854+
.tagged_union,
28855+
.tagged_union_trailing,
28856+
=> tree.taggedUnion(enum_node),
28857+
.tagged_union_two,
28858+
.tagged_union_two_trailing,
28859+
=> tree.taggedUnionTwo(&buffer, enum_node),
28860+
.tagged_union_enum_tag,
28861+
.tagged_union_enum_tag_trailing,
28862+
=> tree.taggedUnionEnumTag(enum_node),
28863+
2872828864
// Container was constructed with `@Type`.
2872928865
else => return LazySrcLoc.nodeOffset(0),
2873028866
};
@@ -29375,7 +29511,9 @@ fn unionFieldAlignment(
2937529511
src: LazySrcLoc,
2937629512
field: Module.Union.Field,
2937729513
) !u32 {
29378-
if (field.abi_align == 0) {
29514+
if (field.ty.zigTypeTag() == .NoReturn) {
29515+
return @as(u32, 0);
29516+
} else if (field.abi_align == 0) {
2937929517
return sema.typeAbiAlignment(block, src, field.ty);
2938029518
} else {
2938129519
return field.abi_align;

0 commit comments

Comments
 (0)