mirror of
https://github.com/ziglang/zig.git
synced 2024-11-27 07:32:44 +00:00
Merge pull request #5693 from antlilja/switch-unreachable-else
Add error message for unreachable else prong in switch
This commit is contained in:
commit
a36772ee64
@ -104,7 +104,6 @@ pub fn parse(
|
||||
return error.InvalidCharacter;
|
||||
},
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
unreachable;
|
||||
|
@ -2864,7 +2864,6 @@ fn transCharLiteral(
|
||||
"TODO: support character literal kind {}",
|
||||
.{kind},
|
||||
),
|
||||
else => unreachable,
|
||||
};
|
||||
if (suppress_as == .no_as) {
|
||||
return maybeSuppressResult(rp, scope, result_used, int_lit_node);
|
||||
|
@ -4110,7 +4110,7 @@ struct IrInstSrcCheckSwitchProngs {
|
||||
IrInstSrc *target_value;
|
||||
IrInstSrcCheckSwitchProngsRange *ranges;
|
||||
size_t range_count;
|
||||
bool have_else_prong;
|
||||
AstNode* else_prong;
|
||||
bool have_underscore_prong;
|
||||
};
|
||||
|
||||
|
34
src/ir.cpp
34
src/ir.cpp
@ -4299,14 +4299,14 @@ static IrInstGen *ir_build_err_to_int_gen(IrAnalyze *ira, Scope *scope, AstNode
|
||||
|
||||
static IrInstSrc *ir_build_check_switch_prongs(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
|
||||
IrInstSrc *target_value, IrInstSrcCheckSwitchProngsRange *ranges, size_t range_count,
|
||||
bool have_else_prong, bool have_underscore_prong)
|
||||
AstNode* else_prong, bool have_underscore_prong)
|
||||
{
|
||||
IrInstSrcCheckSwitchProngs *instruction = ir_build_instruction<IrInstSrcCheckSwitchProngs>(
|
||||
irb, scope, source_node);
|
||||
instruction->target_value = target_value;
|
||||
instruction->ranges = ranges;
|
||||
instruction->range_count = range_count;
|
||||
instruction->have_else_prong = have_else_prong;
|
||||
instruction->else_prong = else_prong;
|
||||
instruction->have_underscore_prong = have_underscore_prong;
|
||||
|
||||
ir_ref_instruction(target_value, irb->current_basic_block);
|
||||
@ -9346,7 +9346,7 @@ static IrInstSrc *ir_gen_switch_expr(IrBuilderSrc *irb, Scope *scope, AstNode *n
|
||||
}
|
||||
|
||||
IrInstSrc *switch_prongs_void = ir_build_check_switch_prongs(irb, scope, node, target_value,
|
||||
check_ranges.items, check_ranges.length, else_prong != nullptr, underscore_prong != nullptr);
|
||||
check_ranges.items, check_ranges.length, else_prong, underscore_prong != nullptr);
|
||||
|
||||
IrInstSrc *br_instruction;
|
||||
if (cases.length == 0) {
|
||||
@ -28827,7 +28827,7 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
|
||||
buf_ptr(enum_field->name)));
|
||||
}
|
||||
}
|
||||
} else if (!instruction->have_else_prong) {
|
||||
} else if (instruction->else_prong == nullptr) {
|
||||
if (switch_type->data.enumeration.non_exhaustive) {
|
||||
ir_add_error(ira, &instruction->base.base,
|
||||
buf_sprintf("switch on non-exhaustive enum must include `else` or `_` prong"));
|
||||
@ -28842,6 +28842,10 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
|
||||
buf_ptr(enum_field->name)));
|
||||
}
|
||||
}
|
||||
} else if(!switch_type->data.enumeration.non_exhaustive && switch_type->data.enumeration.src_field_count == instruction->range_count) {
|
||||
ir_add_error_node(ira, instruction->else_prong,
|
||||
buf_sprintf("unreachable else prong, all cases already handled"));
|
||||
return ira->codegen->invalid_inst_gen;
|
||||
}
|
||||
} else if (switch_type->id == ZigTypeIdErrorSet) {
|
||||
if (!resolve_inferred_error_set(ira->codegen, switch_type, target_value->base.source_node)) {
|
||||
@ -28888,7 +28892,7 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
|
||||
}
|
||||
field_prev_uses[start_index] = start_value->base.source_node;
|
||||
}
|
||||
if (!instruction->have_else_prong) {
|
||||
if (instruction->else_prong == nullptr) {
|
||||
if (type_is_global_error_set(switch_type)) {
|
||||
ir_add_error(ira, &instruction->base.base,
|
||||
buf_sprintf("else prong required when switching on type 'anyerror'"));
|
||||
@ -28950,16 +28954,20 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
|
||||
return ira->codegen->invalid_inst_gen;
|
||||
}
|
||||
}
|
||||
if (!instruction->have_else_prong) {
|
||||
|
||||
BigInt min_val;
|
||||
eval_min_max_value_int(ira->codegen, switch_type, &min_val, false);
|
||||
BigInt max_val;
|
||||
eval_min_max_value_int(ira->codegen, switch_type, &max_val, true);
|
||||
if (!rangeset_spans(&rs, &min_val, &max_val)) {
|
||||
bool handles_all_cases = rangeset_spans(&rs, &min_val, &max_val);
|
||||
if (!handles_all_cases && instruction->else_prong == nullptr) {
|
||||
ir_add_error(ira, &instruction->base.base, buf_sprintf("switch must handle all possibilities"));
|
||||
return ira->codegen->invalid_inst_gen;
|
||||
} else if(handles_all_cases && instruction->else_prong != nullptr) {
|
||||
ir_add_error_node(ira, instruction->else_prong,
|
||||
buf_sprintf("unreachable else prong, all cases already handled"));
|
||||
return ira->codegen->invalid_inst_gen;
|
||||
}
|
||||
}
|
||||
} else if (switch_type->id == ZigTypeIdBool) {
|
||||
int seenTrue = 0;
|
||||
int seenFalse = 0;
|
||||
@ -28989,11 +28997,17 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
|
||||
return ira->codegen->invalid_inst_gen;
|
||||
}
|
||||
}
|
||||
if (((seenTrue < 1) || (seenFalse < 1)) && !instruction->have_else_prong) {
|
||||
if (((seenTrue < 1) || (seenFalse < 1)) && instruction->else_prong == nullptr) {
|
||||
ir_add_error(ira, &instruction->base.base, buf_sprintf("switch must handle all possibilities"));
|
||||
return ira->codegen->invalid_inst_gen;
|
||||
}
|
||||
} else if (!instruction->have_else_prong) {
|
||||
|
||||
if(seenTrue == 1 && seenFalse == 1 && instruction->else_prong != nullptr) {
|
||||
ir_add_error_node(ira, instruction->else_prong,
|
||||
buf_sprintf("unreachable else prong, all cases already handled"));
|
||||
return ira->codegen->invalid_inst_gen;
|
||||
}
|
||||
} else if (instruction->else_prong == nullptr) {
|
||||
ir_add_error(ira, &instruction->base.base,
|
||||
buf_sprintf("else prong required when switching on type '%s'", buf_ptr(&switch_type->name)));
|
||||
return ira->codegen->invalid_inst_gen;
|
||||
|
@ -2175,7 +2175,7 @@ static void ir_print_check_switch_prongs(IrPrintSrc *irp, IrInstSrcCheckSwitchPr
|
||||
fprintf(irp->f, "...");
|
||||
ir_print_other_inst_src(irp, instruction->ranges[i].end);
|
||||
}
|
||||
const char *have_else_str = instruction->have_else_prong ? "yes" : "no";
|
||||
const char *have_else_str = instruction->else_prong != nullptr ? "yes" : "no";
|
||||
fprintf(irp->f, ")else:%s", have_else_str);
|
||||
}
|
||||
|
||||
|
@ -509,6 +509,102 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
|
||||
"tmp.zig:12:5: error: switch on non-exhaustive enum must include `else` or `_` prong",
|
||||
});
|
||||
|
||||
cases.add("switch expression - unreachable else prong (bool)",
|
||||
\\fn foo(x: bool) void {
|
||||
\\ switch (x) {
|
||||
\\ true => {},
|
||||
\\ false => {},
|
||||
\\ else => {},
|
||||
\\ }
|
||||
\\}
|
||||
\\export fn entry() usize { return @sizeOf(@TypeOf(foo)); }
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:5:9: error: unreachable else prong, all cases already handled",
|
||||
});
|
||||
|
||||
cases.add("switch expression - unreachable else prong (u1)",
|
||||
\\fn foo(x: u1) void {
|
||||
\\ switch (x) {
|
||||
\\ 0 => {},
|
||||
\\ 1 => {},
|
||||
\\ else => {},
|
||||
\\ }
|
||||
\\}
|
||||
\\export fn entry() usize { return @sizeOf(@TypeOf(foo)); }
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:5:9: error: unreachable else prong, all cases already handled",
|
||||
});
|
||||
|
||||
cases.add("switch expression - unreachable else prong (u2)",
|
||||
\\fn foo(x: u2) void {
|
||||
\\ switch (x) {
|
||||
\\ 0 => {},
|
||||
\\ 1 => {},
|
||||
\\ 2 => {},
|
||||
\\ 3 => {},
|
||||
\\ else => {},
|
||||
\\ }
|
||||
\\}
|
||||
\\export fn entry() usize { return @sizeOf(@TypeOf(foo)); }
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:7:9: error: unreachable else prong, all cases already handled",
|
||||
});
|
||||
|
||||
cases.add("switch expression - unreachable else prong (range u8)",
|
||||
\\fn foo(x: u8) void {
|
||||
\\ switch (x) {
|
||||
\\ 0 => {},
|
||||
\\ 1 => {},
|
||||
\\ 2 => {},
|
||||
\\ 3 => {},
|
||||
\\ 4...255 => {},
|
||||
\\ else => {},
|
||||
\\ }
|
||||
\\}
|
||||
\\export fn entry() usize { return @sizeOf(@TypeOf(foo)); }
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:8:9: error: unreachable else prong, all cases already handled",
|
||||
});
|
||||
|
||||
cases.add("switch expression - unreachable else prong (range i8)",
|
||||
\\fn foo(x: i8) void {
|
||||
\\ switch (x) {
|
||||
\\ -128...0 => {},
|
||||
\\ 1 => {},
|
||||
\\ 2 => {},
|
||||
\\ 3 => {},
|
||||
\\ 4...127 => {},
|
||||
\\ else => {},
|
||||
\\ }
|
||||
\\}
|
||||
\\export fn entry() usize { return @sizeOf(@TypeOf(foo)); }
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:8:9: error: unreachable else prong, all cases already handled",
|
||||
});
|
||||
|
||||
cases.add("switch expression - unreachable else prong (enum)",
|
||||
\\const TestEnum = enum{ T1, T2 };
|
||||
\\
|
||||
\\fn err(x: u8) TestEnum {
|
||||
\\ switch (x) {
|
||||
\\ 0 => return TestEnum.T1,
|
||||
\\ else => return TestEnum.T2,
|
||||
\\ }
|
||||
\\}
|
||||
\\
|
||||
\\fn foo(x: u8) void {
|
||||
\\ switch (err(x)) {
|
||||
\\ TestEnum.T1 => {},
|
||||
\\ TestEnum.T2 => {},
|
||||
\\ else => {},
|
||||
\\ }
|
||||
\\}
|
||||
\\
|
||||
\\export fn entry() usize { return @sizeOf(@TypeOf(foo)); }
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:14:9: error: unreachable else prong, all cases already handled",
|
||||
});
|
||||
|
||||
cases.addTest("@export with empty name string",
|
||||
\\pub export fn entry() void { }
|
||||
\\comptime {
|
||||
|
@ -7,6 +7,5 @@ test "issue 1111 fixed" {
|
||||
|
||||
switch (v) {
|
||||
Foo.Bar => return,
|
||||
else => return,
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user