Commit dcc406deff

antlilja <liljaanton2001@gmail.com>
2020-06-24 19:12:42
Add new error message for unreachable else prongs * Adds error message for types: enum, int and bool * Adds compile error tests
1 parent e60be30
Changed files (2)
src/ir.cpp
@@ -28700,6 +28700,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)) {
@@ -28808,16 +28812,20 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
                 return ira->codegen->invalid_inst_gen;
             }
         }
-        if (instruction->else_prong == nullptr) {
+
             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;
@@ -28851,6 +28859,12 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
             ir_add_error(ira, &instruction->base.base, buf_sprintf("switch must handle all possibilities"));
             return ira->codegen->invalid_inst_gen;
         }
+
+        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)));
test/compile_errors.zig
@@ -468,6 +468,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 {