mirror of
https://github.com/ziglang/zig.git
synced 2026-02-13 12:59:04 +00:00
disallow '_' prong when switching on non-exhaustive tagged union
A tagged union cannot legally be initiated to an invalid enumeration
This commit is contained in:
parent
65185016f1
commit
1e835e0fcc
16
src/ir.cpp
16
src/ir.cpp
@ -14138,7 +14138,7 @@ static IrInstGen *ir_analyze_union_to_tag(IrAnalyze *ira, IrInst* source_instr,
|
||||
// If there is only 1 possible tag, then we know at comptime what it is.
|
||||
if (wanted_type->data.enumeration.layout == ContainerLayoutAuto &&
|
||||
wanted_type->data.enumeration.src_field_count == 1 &&
|
||||
!wanted_type->data.enumeration.non_exhaustive) // TODO are non-exhaustive union tag types supposed to be allowed?
|
||||
!wanted_type->data.enumeration.non_exhaustive)
|
||||
{
|
||||
IrInstGen *result = ir_const(ira, source_instr, wanted_type);
|
||||
result->value->special = ConstValSpecialStatic;
|
||||
@ -23816,7 +23816,6 @@ static IrInstGen *ir_analyze_instruction_switch_target(IrAnalyze *ira,
|
||||
bigint_init_bigint(&result->value->data.x_enum_tag, &pointee_val->data.x_union.tag);
|
||||
return result;
|
||||
}
|
||||
// TODO are non-exhaustive union tag types supposed to be allowed?
|
||||
if (tag_type->data.enumeration.src_field_count == 1 && !tag_type->data.enumeration.non_exhaustive) {
|
||||
IrInstGen *result = ir_const(ira, &switch_target_instruction->base.base, tag_type);
|
||||
TypeEnumField *only_field = &tag_type->data.enumeration.fields[0];
|
||||
@ -28839,6 +28838,10 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
|
||||
if (type_is_invalid(switch_type))
|
||||
return ira->codegen->invalid_inst_gen;
|
||||
|
||||
ZigValue *original_value = ((IrInstSrcSwitchTarget *)(instruction->target_value))->target_value_ptr->child->value;
|
||||
bool target_is_originally_union = original_value->type->id == ZigTypeIdPointer &&
|
||||
original_value->type->data.pointer.child_type->id == ZigTypeIdUnion;
|
||||
|
||||
if (switch_type->id == ZigTypeIdEnum) {
|
||||
HashMap<BigInt, AstNode *, bigint_hash, bigint_eql> field_prev_uses = {};
|
||||
field_prev_uses.init(switch_type->data.enumeration.src_field_count);
|
||||
@ -28894,9 +28897,12 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
|
||||
}
|
||||
}
|
||||
if (instruction->have_underscore_prong) {
|
||||
if (!switch_type->data.enumeration.non_exhaustive){
|
||||
if (!switch_type->data.enumeration.non_exhaustive) {
|
||||
ir_add_error(ira, &instruction->base.base,
|
||||
buf_sprintf("switch on non-exhaustive enum has `_` prong"));
|
||||
buf_sprintf("switch on exhaustive enum has `_` prong"));
|
||||
} else if (target_is_originally_union) {
|
||||
ir_add_error(ira, &instruction->base.base,
|
||||
buf_sprintf("`_` prong not allowed when switching on tagged union"));
|
||||
}
|
||||
for (uint32_t i = 0; i < switch_type->data.enumeration.src_field_count; i += 1) {
|
||||
TypeEnumField *enum_field = &switch_type->data.enumeration.fields[i];
|
||||
@ -28911,7 +28917,7 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
|
||||
}
|
||||
}
|
||||
} else if (instruction->else_prong == nullptr) {
|
||||
if (switch_type->data.enumeration.non_exhaustive) {
|
||||
if (switch_type->data.enumeration.non_exhaustive && !target_is_originally_union) {
|
||||
ir_add_error(ira, &instruction->base.base,
|
||||
buf_sprintf("switch on non-exhaustive enum must include `else` or `_` prong"));
|
||||
}
|
||||
|
||||
@ -51,6 +51,23 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
|
||||
"tmp.zig:17:23: error: cannot adjust alignment of zero sized type 'fn(u32) anytype'",
|
||||
});
|
||||
|
||||
cases.addTest("switching with exhaustive enum has '_' prong ",
|
||||
\\const E = enum{
|
||||
\\ a,
|
||||
\\ b,
|
||||
\\};
|
||||
\\pub export fn entry() void {
|
||||
\\ var e: E = .b;
|
||||
\\ switch (e) {
|
||||
\\ .a => {},
|
||||
\\ .b => {},
|
||||
\\ _ => {},
|
||||
\\ }
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:7:5: error: switch on exhaustive enum has `_` prong",
|
||||
});
|
||||
|
||||
cases.addTest("invalid pointer with @Type",
|
||||
\\export fn entry() void {
|
||||
\\ _ = @Type(.{ .Pointer = .{
|
||||
@ -564,6 +581,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
|
||||
\\ b,
|
||||
\\ _,
|
||||
\\};
|
||||
\\const U = union(E) {
|
||||
\\ a: i32,
|
||||
\\ b: u32,
|
||||
\\};
|
||||
\\pub export fn entry() void {
|
||||
\\ var e: E = .b;
|
||||
\\ switch (e) { // error: switch not handling the tag `b`
|
||||
@ -574,10 +595,17 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
|
||||
\\ .a => {},
|
||||
\\ .b => {},
|
||||
\\ }
|
||||
\\ var u = U{.a = 2};
|
||||
\\ switch (u) { // error: `_` prong not allowed when switching on tagged union
|
||||
\\ .a => {},
|
||||
\\ .b => {},
|
||||
\\ _ => {},
|
||||
\\ }
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:8:5: error: enumeration value 'E.b' not handled in switch",
|
||||
"tmp.zig:12:5: error: switch on non-exhaustive enum must include `else` or `_` prong",
|
||||
"tmp.zig:12:5: error: enumeration value 'E.b' not handled in switch",
|
||||
"tmp.zig:16:5: error: switch on non-exhaustive enum must include `else` or `_` prong",
|
||||
"tmp.zig:21:5: error: `_` prong not allowed when switching on tagged union",
|
||||
});
|
||||
|
||||
cases.add("switch expression - unreachable else prong (bool)",
|
||||
|
||||
@ -690,3 +690,26 @@ test "method call on an empty union" {
|
||||
S.doTheTest();
|
||||
comptime S.doTheTest();
|
||||
}
|
||||
|
||||
test "switching on non exhaustive union" {
|
||||
const S = struct {
|
||||
const E = enum(u8) {
|
||||
a,
|
||||
b,
|
||||
_,
|
||||
};
|
||||
const U = union(E) {
|
||||
a: i32,
|
||||
b: u32,
|
||||
};
|
||||
fn doTheTest() void {
|
||||
var a = U{ .a = 2 };
|
||||
switch (a) {
|
||||
.a => |val| expect(val == 2),
|
||||
.b => unreachable,
|
||||
}
|
||||
}
|
||||
};
|
||||
S.doTheTest();
|
||||
comptime S.doTheTest();
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user