Skip to content

Commit a36772e

Browse files
authored
Merge pull request #5693 from antlilja/switch-unreachable-else
Add error message for unreachable else prong in switch
2 parents f050150 + dcc406d commit a36772e

File tree

7 files changed

+122
-15
lines changed

7 files changed

+122
-15
lines changed

lib/std/zig/string_literal.zig

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,6 @@ pub fn parse(
104104
return error.InvalidCharacter;
105105
},
106106
},
107-
else => unreachable,
108107
}
109108
}
110109
unreachable;

src-self-hosted/translate_c.zig

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2864,7 +2864,6 @@ fn transCharLiteral(
28642864
"TODO: support character literal kind {}",
28652865
.{kind},
28662866
),
2867-
else => unreachable,
28682867
};
28692868
if (suppress_as == .no_as) {
28702869
return maybeSuppressResult(rp, scope, result_used, int_lit_node);

src/all_types.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4110,7 +4110,7 @@ struct IrInstSrcCheckSwitchProngs {
41104110
IrInstSrc *target_value;
41114111
IrInstSrcCheckSwitchProngsRange *ranges;
41124112
size_t range_count;
4113-
bool have_else_prong;
4113+
AstNode* else_prong;
41144114
bool have_underscore_prong;
41154115
};
41164116

src/ir.cpp

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4299,14 +4299,14 @@ static IrInstGen *ir_build_err_to_int_gen(IrAnalyze *ira, Scope *scope, AstNode
42994299

43004300
static IrInstSrc *ir_build_check_switch_prongs(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
43014301
IrInstSrc *target_value, IrInstSrcCheckSwitchProngsRange *ranges, size_t range_count,
4302-
bool have_else_prong, bool have_underscore_prong)
4302+
AstNode* else_prong, bool have_underscore_prong)
43034303
{
43044304
IrInstSrcCheckSwitchProngs *instruction = ir_build_instruction<IrInstSrcCheckSwitchProngs>(
43054305
irb, scope, source_node);
43064306
instruction->target_value = target_value;
43074307
instruction->ranges = ranges;
43084308
instruction->range_count = range_count;
4309-
instruction->have_else_prong = have_else_prong;
4309+
instruction->else_prong = else_prong;
43104310
instruction->have_underscore_prong = have_underscore_prong;
43114311

43124312
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
93469346
}
93479347

93489348
IrInstSrc *switch_prongs_void = ir_build_check_switch_prongs(irb, scope, node, target_value,
9349-
check_ranges.items, check_ranges.length, else_prong != nullptr, underscore_prong != nullptr);
9349+
check_ranges.items, check_ranges.length, else_prong, underscore_prong != nullptr);
93509350

93519351
IrInstSrc *br_instruction;
93529352
if (cases.length == 0) {
@@ -28827,7 +28827,7 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
2882728827
buf_ptr(enum_field->name)));
2882828828
}
2882928829
}
28830-
} else if (!instruction->have_else_prong) {
28830+
} else if (instruction->else_prong == nullptr) {
2883128831
if (switch_type->data.enumeration.non_exhaustive) {
2883228832
ir_add_error(ira, &instruction->base.base,
2883328833
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,
2884228842
buf_ptr(enum_field->name)));
2884328843
}
2884428844
}
28845+
} else if(!switch_type->data.enumeration.non_exhaustive && switch_type->data.enumeration.src_field_count == instruction->range_count) {
28846+
ir_add_error_node(ira, instruction->else_prong,
28847+
buf_sprintf("unreachable else prong, all cases already handled"));
28848+
return ira->codegen->invalid_inst_gen;
2884528849
}
2884628850
} else if (switch_type->id == ZigTypeIdErrorSet) {
2884728851
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,
2888828892
}
2888928893
field_prev_uses[start_index] = start_value->base.source_node;
2889028894
}
28891-
if (!instruction->have_else_prong) {
28895+
if (instruction->else_prong == nullptr) {
2889228896
if (type_is_global_error_set(switch_type)) {
2889328897
ir_add_error(ira, &instruction->base.base,
2889428898
buf_sprintf("else prong required when switching on type 'anyerror'"));
@@ -28950,16 +28954,20 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
2895028954
return ira->codegen->invalid_inst_gen;
2895128955
}
2895228956
}
28953-
if (!instruction->have_else_prong) {
28957+
2895428958
BigInt min_val;
2895528959
eval_min_max_value_int(ira->codegen, switch_type, &min_val, false);
2895628960
BigInt max_val;
2895728961
eval_min_max_value_int(ira->codegen, switch_type, &max_val, true);
28958-
if (!rangeset_spans(&rs, &min_val, &max_val)) {
28962+
bool handles_all_cases = rangeset_spans(&rs, &min_val, &max_val);
28963+
if (!handles_all_cases && instruction->else_prong == nullptr) {
2895928964
ir_add_error(ira, &instruction->base.base, buf_sprintf("switch must handle all possibilities"));
2896028965
return ira->codegen->invalid_inst_gen;
28966+
} else if(handles_all_cases && instruction->else_prong != nullptr) {
28967+
ir_add_error_node(ira, instruction->else_prong,
28968+
buf_sprintf("unreachable else prong, all cases already handled"));
28969+
return ira->codegen->invalid_inst_gen;
2896128970
}
28962-
}
2896328971
} else if (switch_type->id == ZigTypeIdBool) {
2896428972
int seenTrue = 0;
2896528973
int seenFalse = 0;
@@ -28989,11 +28997,17 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
2898928997
return ira->codegen->invalid_inst_gen;
2899028998
}
2899128999
}
28992-
if (((seenTrue < 1) || (seenFalse < 1)) && !instruction->have_else_prong) {
29000+
if (((seenTrue < 1) || (seenFalse < 1)) && instruction->else_prong == nullptr) {
2899329001
ir_add_error(ira, &instruction->base.base, buf_sprintf("switch must handle all possibilities"));
2899429002
return ira->codegen->invalid_inst_gen;
2899529003
}
28996-
} else if (!instruction->have_else_prong) {
29004+
29005+
if(seenTrue == 1 && seenFalse == 1 && instruction->else_prong != nullptr) {
29006+
ir_add_error_node(ira, instruction->else_prong,
29007+
buf_sprintf("unreachable else prong, all cases already handled"));
29008+
return ira->codegen->invalid_inst_gen;
29009+
}
29010+
} else if (instruction->else_prong == nullptr) {
2899729011
ir_add_error(ira, &instruction->base.base,
2899829012
buf_sprintf("else prong required when switching on type '%s'", buf_ptr(&switch_type->name)));
2899929013
return ira->codegen->invalid_inst_gen;

src/ir_print.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2175,7 +2175,7 @@ static void ir_print_check_switch_prongs(IrPrintSrc *irp, IrInstSrcCheckSwitchPr
21752175
fprintf(irp->f, "...");
21762176
ir_print_other_inst_src(irp, instruction->ranges[i].end);
21772177
}
2178-
const char *have_else_str = instruction->have_else_prong ? "yes" : "no";
2178+
const char *have_else_str = instruction->else_prong != nullptr ? "yes" : "no";
21792179
fprintf(irp->f, ")else:%s", have_else_str);
21802180
}
21812181

test/compile_errors.zig

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,102 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
509509
"tmp.zig:12:5: error: switch on non-exhaustive enum must include `else` or `_` prong",
510510
});
511511

512+
cases.add("switch expression - unreachable else prong (bool)",
513+
\\fn foo(x: bool) void {
514+
\\ switch (x) {
515+
\\ true => {},
516+
\\ false => {},
517+
\\ else => {},
518+
\\ }
519+
\\}
520+
\\export fn entry() usize { return @sizeOf(@TypeOf(foo)); }
521+
, &[_][]const u8{
522+
"tmp.zig:5:9: error: unreachable else prong, all cases already handled",
523+
});
524+
525+
cases.add("switch expression - unreachable else prong (u1)",
526+
\\fn foo(x: u1) void {
527+
\\ switch (x) {
528+
\\ 0 => {},
529+
\\ 1 => {},
530+
\\ else => {},
531+
\\ }
532+
\\}
533+
\\export fn entry() usize { return @sizeOf(@TypeOf(foo)); }
534+
, &[_][]const u8{
535+
"tmp.zig:5:9: error: unreachable else prong, all cases already handled",
536+
});
537+
538+
cases.add("switch expression - unreachable else prong (u2)",
539+
\\fn foo(x: u2) void {
540+
\\ switch (x) {
541+
\\ 0 => {},
542+
\\ 1 => {},
543+
\\ 2 => {},
544+
\\ 3 => {},
545+
\\ else => {},
546+
\\ }
547+
\\}
548+
\\export fn entry() usize { return @sizeOf(@TypeOf(foo)); }
549+
, &[_][]const u8{
550+
"tmp.zig:7:9: error: unreachable else prong, all cases already handled",
551+
});
552+
553+
cases.add("switch expression - unreachable else prong (range u8)",
554+
\\fn foo(x: u8) void {
555+
\\ switch (x) {
556+
\\ 0 => {},
557+
\\ 1 => {},
558+
\\ 2 => {},
559+
\\ 3 => {},
560+
\\ 4...255 => {},
561+
\\ else => {},
562+
\\ }
563+
\\}
564+
\\export fn entry() usize { return @sizeOf(@TypeOf(foo)); }
565+
, &[_][]const u8{
566+
"tmp.zig:8:9: error: unreachable else prong, all cases already handled",
567+
});
568+
569+
cases.add("switch expression - unreachable else prong (range i8)",
570+
\\fn foo(x: i8) void {
571+
\\ switch (x) {
572+
\\ -128...0 => {},
573+
\\ 1 => {},
574+
\\ 2 => {},
575+
\\ 3 => {},
576+
\\ 4...127 => {},
577+
\\ else => {},
578+
\\ }
579+
\\}
580+
\\export fn entry() usize { return @sizeOf(@TypeOf(foo)); }
581+
, &[_][]const u8{
582+
"tmp.zig:8:9: error: unreachable else prong, all cases already handled",
583+
});
584+
585+
cases.add("switch expression - unreachable else prong (enum)",
586+
\\const TestEnum = enum{ T1, T2 };
587+
\\
588+
\\fn err(x: u8) TestEnum {
589+
\\ switch (x) {
590+
\\ 0 => return TestEnum.T1,
591+
\\ else => return TestEnum.T2,
592+
\\ }
593+
\\}
594+
\\
595+
\\fn foo(x: u8) void {
596+
\\ switch (err(x)) {
597+
\\ TestEnum.T1 => {},
598+
\\ TestEnum.T2 => {},
599+
\\ else => {},
600+
\\ }
601+
\\}
602+
\\
603+
\\export fn entry() usize { return @sizeOf(@TypeOf(foo)); }
604+
, &[_][]const u8{
605+
"tmp.zig:14:9: error: unreachable else prong, all cases already handled",
606+
});
607+
512608
cases.addTest("@export with empty name string",
513609
\\pub export fn entry() void { }
514610
\\comptime {

test/stage1/behavior/bugs/1111.zig

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,5 @@ test "issue 1111 fixed" {
77

88
switch (v) {
99
Foo.Bar => return,
10-
else => return,
1110
}
1211
}

0 commit comments

Comments
 (0)