From 7261cd19b78367d78f758f7ade370efbc3e25237 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 7 May 2017 13:40:35 -0400 Subject: [PATCH] detect duplicate switch value even when else prong present closes #43 --- src/all_types.hpp | 1 + src/ir.cpp | 39 ++++++++++++++++++++++----------------- src/ir_print.cpp | 3 ++- test/compile_errors.zig | 22 ++++++++++++++++++++++ 4 files changed, 47 insertions(+), 18 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 2dd53e2e00..152b29faf1 100644 --- a/src/all_types.hpp +++ b/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 { diff --git a/src/ir.cpp b/src/ir.cpp index f5ced359ba..53f2a48d74 100644 --- a/src/ir.cpp +++ b/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( 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; diff --git a/src/ir_print.cpp b/src/ir_print.cpp index e676ad73a9..88eff409cf 100644 --- a/src/ir_print.cpp +++ b/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) { diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 54ab8444b8..6cce0c89bd 100644 --- a/test/compile_errors.zig +++ b/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) {