Merge pull request #5693 from antlilja/switch-unreachable-else

Add error message for unreachable else prong in switch
This commit is contained in:
Andrew Kelley 2020-07-26 05:46:18 +00:00 committed by GitHub
commit a36772ee64
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 122 additions and 15 deletions

View File

@ -104,7 +104,6 @@ pub fn parse(
return error.InvalidCharacter;
},
},
else => unreachable,
}
}
unreachable;

View File

@ -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);

View File

@ -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;
};

View File

@ -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;

View File

@ -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);
}

View File

@ -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 {

View File

@ -7,6 +7,5 @@ test "issue 1111 fixed" {
switch (v) {
Foo.Bar => return,
else => return,
}
}