Commit 7261cd19b7

Andrew Kelley <superjoe30@gmail.com>
2017-05-07 19:40:35
detect duplicate switch value even when else prong present
closes #43
1 parent dc2df15
src/all_types.hpp
@@ -2471,6 +2471,7 @@ struct IrInstructionCheckSwitchProngs {
     IrInstruction *target_value;
     IrInstructionCheckSwitchProngsRange *ranges;
     size_t range_count;
+    bool have_else_prong;
 };
 
 struct IrInstructionCheckStatementIsVoid {
src/ir.cpp
@@ -2015,13 +2015,15 @@ static IrInstruction *ir_build_err_to_int(IrBuilder *irb, Scope *scope, AstNode
 }
 
 static IrInstruction *ir_build_check_switch_prongs(IrBuilder *irb, Scope *scope, AstNode *source_node,
-        IrInstruction *target_value, IrInstructionCheckSwitchProngsRange *ranges, size_t range_count)
+        IrInstruction *target_value, IrInstructionCheckSwitchProngsRange *ranges, size_t range_count,
+        bool have_else_prong)
 {
     IrInstructionCheckSwitchProngs *instruction = ir_build_instruction<IrInstructionCheckSwitchProngs>(
             irb, scope, source_node);
     instruction->target_value = target_value;
     instruction->ranges = ranges;
     instruction->range_count = range_count;
+    instruction->have_else_prong = have_else_prong;
 
     ir_ref_instruction(target_value, irb->current_basic_block);
     for (size_t i = 0; i < range_count; i += 1) {
@@ -5542,9 +5544,8 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *
         }
     }
 
-    if (!else_prong) {
-        ir_build_check_switch_prongs(irb, scope, node, target_value, check_ranges.items, check_ranges.length);
-    }
+    ir_build_check_switch_prongs(irb, scope, node, target_value, check_ranges.items, check_ranges.length,
+            else_prong != nullptr);
 
     if (cases.length == 0) {
         ir_build_br(irb, scope, node, else_block, is_comptime);
@@ -13019,11 +13020,13 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira
                 field_prev_uses[field_index] = start_value->source_node;
             }
         }
-        for (uint32_t i = 0; i < enum_type->data.enumeration.src_field_count; i += 1) {
-            if (field_prev_uses[i] == nullptr) {
-                ir_add_error(ira, &instruction->base,
-                    buf_sprintf("enumeration value '%s.%s' not handled in switch", buf_ptr(&enum_type->name),
-                        buf_ptr(enum_type->data.enumeration.fields[i].name)));
+        if (!instruction->have_else_prong) {
+            for (uint32_t i = 0; i < enum_type->data.enumeration.src_field_count; i += 1) {
+                if (field_prev_uses[i] == nullptr) {
+                    ir_add_error(ira, &instruction->base,
+                        buf_sprintf("enumeration value '%s.%s' not handled in switch", buf_ptr(&enum_type->name),
+                            buf_ptr(enum_type->data.enumeration.fields[i].name)));
+                }
             }
         }
     } else if (switch_type->id == TypeTableEntryIdInt) {
@@ -13055,15 +13058,17 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira
                 return ira->codegen->builtin_types.entry_invalid;
             }
         }
-        BigNum min_val;
-        eval_min_max_value_int(ira->codegen, switch_type, &min_val, false);
-        BigNum max_val;
-        eval_min_max_value_int(ira->codegen, switch_type, &max_val, true);
-        if (!rangeset_spans(&rs, &min_val, &max_val)) {
-            ir_add_error(ira, &instruction->base, buf_sprintf("switch must handle all possibilities"));
-            return ira->codegen->builtin_types.entry_invalid;
+        if (!instruction->have_else_prong) {
+            BigNum min_val;
+            eval_min_max_value_int(ira->codegen, switch_type, &min_val, false);
+            BigNum max_val;
+            eval_min_max_value_int(ira->codegen, switch_type, &max_val, true);
+            if (!rangeset_spans(&rs, &min_val, &max_val)) {
+                ir_add_error(ira, &instruction->base, buf_sprintf("switch must handle all possibilities"));
+                return ira->codegen->builtin_types.entry_invalid;
+            }
         }
-    } else {
+    } else if (!instruction->have_else_prong) {
         ir_add_error(ira, &instruction->base,
             buf_sprintf("else prong required when switching on type '%s'", buf_ptr(&switch_type->name)));
         return ira->codegen->builtin_types.entry_invalid;
src/ir_print.cpp
@@ -807,7 +807,8 @@ static void ir_print_check_switch_prongs(IrPrint *irp, IrInstructionCheckSwitchP
         fprintf(irp->f, "...");
         ir_print_other_instruction(irp, instruction->ranges[i].end);
     }
-    fprintf(irp->f, ")");
+    const char *have_else_str = instruction->have_else_prong ? "yes" : "no";
+    fprintf(irp->f, ")else:%s", have_else_str);
 }
 
 static void ir_print_check_statement_is_void(IrPrint *irp, IrInstructionCheckStatementIsVoid *instruction) {
test/compile_errors.zig
@@ -581,6 +581,28 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
     , ".tmp_source.zig:13:15: error: duplicate switch value",
       ".tmp_source.zig:10:15: note: other value is here");
 
+    cases.add("switch expression - duplicate enumeration prong when else present",
+        \\const Number = enum {
+        \\    One,
+        \\    Two,
+        \\    Three,
+        \\    Four,
+        \\};
+        \\fn f(n: Number) -> i32 {
+        \\    switch (n) {
+        \\        Number.One => 1,
+        \\        Number.Two => 2,
+        \\        Number.Three => i32(3),
+        \\        Number.Four => 4,
+        \\        Number.Two => 2,
+        \\        else => 10,
+        \\    }
+        \\}
+        \\
+        \\export fn entry() -> usize { @sizeOf(@typeOf(f)) }
+    , ".tmp_source.zig:13:15: error: duplicate switch value",
+      ".tmp_source.zig:10:15: note: other value is here");
+
     cases.add("switch expression - multiple else prongs",
         \\fn f(x: u32) {
         \\    const value: bool = switch (x) {