detect duplicate switch value even when else prong present

closes #43
master
Andrew Kelley 2017-05-07 13:40:35 -04:00
parent dc2df15528
commit 7261cd19b7
4 changed files with 47 additions and 18 deletions

View File

@ -2471,6 +2471,7 @@ struct IrInstructionCheckSwitchProngs {
IrInstruction *target_value; IrInstruction *target_value;
IrInstructionCheckSwitchProngsRange *ranges; IrInstructionCheckSwitchProngsRange *ranges;
size_t range_count; size_t range_count;
bool have_else_prong;
}; };
struct IrInstructionCheckStatementIsVoid { struct IrInstructionCheckStatementIsVoid {

View File

@ -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, 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>( IrInstructionCheckSwitchProngs *instruction = ir_build_instruction<IrInstructionCheckSwitchProngs>(
irb, scope, source_node); irb, scope, source_node);
instruction->target_value = target_value; instruction->target_value = target_value;
instruction->ranges = ranges; instruction->ranges = ranges;
instruction->range_count = range_count; instruction->range_count = range_count;
instruction->have_else_prong = have_else_prong;
ir_ref_instruction(target_value, irb->current_basic_block); ir_ref_instruction(target_value, irb->current_basic_block);
for (size_t i = 0; i < range_count; i += 1) { 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) { if (cases.length == 0) {
ir_build_br(irb, scope, node, else_block, is_comptime); 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; 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 (!instruction->have_else_prong) {
if (field_prev_uses[i] == nullptr) { for (uint32_t i = 0; i < enum_type->data.enumeration.src_field_count; i += 1) {
ir_add_error(ira, &instruction->base, if (field_prev_uses[i] == nullptr) {
buf_sprintf("enumeration value '%s.%s' not handled in switch", buf_ptr(&enum_type->name), ir_add_error(ira, &instruction->base,
buf_ptr(enum_type->data.enumeration.fields[i].name))); 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) { } 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; return ira->codegen->builtin_types.entry_invalid;
} }
} }
BigNum min_val; if (!instruction->have_else_prong) {
eval_min_max_value_int(ira->codegen, switch_type, &min_val, false); BigNum min_val;
BigNum max_val; eval_min_max_value_int(ira->codegen, switch_type, &min_val, false);
eval_min_max_value_int(ira->codegen, switch_type, &max_val, true); BigNum max_val;
if (!rangeset_spans(&rs, &min_val, &max_val)) { eval_min_max_value_int(ira->codegen, switch_type, &max_val, true);
ir_add_error(ira, &instruction->base, buf_sprintf("switch must handle all possibilities")); if (!rangeset_spans(&rs, &min_val, &max_val)) {
return ira->codegen->builtin_types.entry_invalid; 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, ir_add_error(ira, &instruction->base,
buf_sprintf("else prong required when switching on type '%s'", buf_ptr(&switch_type->name))); buf_sprintf("else prong required when switching on type '%s'", buf_ptr(&switch_type->name)));
return ira->codegen->builtin_types.entry_invalid; return ira->codegen->builtin_types.entry_invalid;

View File

@ -807,7 +807,8 @@ static void ir_print_check_switch_prongs(IrPrint *irp, IrInstructionCheckSwitchP
fprintf(irp->f, "..."); fprintf(irp->f, "...");
ir_print_other_instruction(irp, instruction->ranges[i].end); 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) { static void ir_print_check_statement_is_void(IrPrint *irp, IrInstructionCheckStatementIsVoid *instruction) {

View File

@ -581,6 +581,28 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
, ".tmp_source.zig:13:15: error: duplicate switch value", , ".tmp_source.zig:13:15: error: duplicate switch value",
".tmp_source.zig:10:15: note: other value is here"); ".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", cases.add("switch expression - multiple else prongs",
\\fn f(x: u32) { \\fn f(x: u32) {
\\ const value: bool = switch (x) { \\ const value: bool = switch (x) {