authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-01-10 16:28:49-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-01-10 16:28:49-05:00
logfde276a3bf3d20da5346bd302a736101ce440362
treede395f7e8e8624c99a4af3eea160437b89d4b3ee
parent430e33b869b004ca24faee2dfa9e51aa4e94093f

IR: implement error for missing or extra switch prongs


5 files changed, 155 insertions(+), 12 deletions(-)

src/all_types.hpp+14
...@@ -1484,6 +1484,7 @@ enum IrInstructionId {...@@ -1484,6 +1484,7 @@ enum IrInstructionId {
1484 IrInstructionIdIntToPtr,1484 IrInstructionIdIntToPtr,
1485 IrInstructionIdPtrToInt,1485 IrInstructionIdPtrToInt,
1486 IrInstructionIdIntToEnum,1486 IrInstructionIdIntToEnum,
1487 IrInstructionIdCheckSwitchProngs,
1487};1488};
14881489
1489struct IrInstruction {1490struct IrInstruction {
...@@ -2160,6 +2161,19 @@ struct IrInstructionIntToEnum {...@@ -2160,6 +2161,19 @@ struct IrInstructionIntToEnum {
2160 IrInstruction *target;2161 IrInstruction *target;
2161};2162};
21622163
2164struct IrInstructionCheckSwitchProngsRange {
2165 IrInstruction *start;
2166 IrInstruction *end;
2167};
2168
2169struct IrInstructionCheckSwitchProngs {
2170 IrInstruction base;
2171
2172 IrInstruction *target_value;
2173 IrInstructionCheckSwitchProngsRange *ranges;
2174 size_t range_count;
2175};
2176
2163enum LValPurpose {2177enum LValPurpose {
2164 LValPurposeNone,2178 LValPurposeNone,
2165 LValPurposeAssign,2179 LValPurposeAssign,
src/codegen.cpp+1
...@@ -2274,6 +2274,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,...@@ -2274,6 +2274,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
2274 case IrInstructionIdFnProto:2274 case IrInstructionIdFnProto:
2275 case IrInstructionIdTestComptime:2275 case IrInstructionIdTestComptime:
2276 case IrInstructionIdGeneratedCode:2276 case IrInstructionIdGeneratedCode:
2277 case IrInstructionIdCheckSwitchProngs:
2277 zig_unreachable();2278 zig_unreachable();
2278 case IrInstructionIdReturn:2279 case IrInstructionIdReturn:
2279 return ir_render_return(g, executable, (IrInstructionReturn *)instruction);2280 return ir_render_return(g, executable, (IrInstructionReturn *)instruction);
src/ir.cpp+119
...@@ -483,6 +483,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionIntToEnum *) {...@@ -483,6 +483,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionIntToEnum *) {
483 return IrInstructionIdIntToEnum;483 return IrInstructionIdIntToEnum;
484}484}
485485
486static constexpr IrInstructionId ir_instruction_id(IrInstructionCheckSwitchProngs *) {
487 return IrInstructionIdCheckSwitchProngs;
488}
489
486template<typename T>490template<typename T>
487static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {491static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
488 T *special_instruction = allocate<T>(1);492 T *special_instruction = allocate<T>(1);
...@@ -1981,6 +1985,24 @@ static IrInstruction *ir_build_int_to_enum(IrBuilder *irb, Scope *scope, AstNode...@@ -1981,6 +1985,24 @@ static IrInstruction *ir_build_int_to_enum(IrBuilder *irb, Scope *scope, AstNode
1981 return &instruction->base;1985 return &instruction->base;
1982}1986}
19831987
1988static IrInstruction *ir_build_check_switch_prongs(IrBuilder *irb, Scope *scope, AstNode *source_node,
1989 IrInstruction *target_value, IrInstructionCheckSwitchProngsRange *ranges, size_t range_count)
1990{
1991 IrInstructionCheckSwitchProngs *instruction = ir_build_instruction<IrInstructionCheckSwitchProngs>(
1992 irb, scope, source_node);
1993 instruction->target_value = target_value;
1994 instruction->ranges = ranges;
1995 instruction->range_count = range_count;
1996
1997 ir_ref_instruction(target_value, irb->current_basic_block);
1998 for (size_t i = 0; i < range_count; i += 1) {
1999 ir_ref_instruction(ranges[i].start, irb->current_basic_block);
2000 ir_ref_instruction(ranges[i].end, irb->current_basic_block);
2001 }
2002
2003 return &instruction->base;
2004}
2005
1984static IrInstruction *ir_instruction_br_get_dep(IrInstructionBr *instruction, size_t index) {2006static IrInstruction *ir_instruction_br_get_dep(IrInstructionBr *instruction, size_t index) {
1985 return nullptr;2007 return nullptr;
1986}2008}
...@@ -2580,6 +2602,18 @@ static IrInstruction *ir_instruction_inttoenum_get_dep(IrInstructionIntToEnum *i...@@ -2580,6 +2602,18 @@ static IrInstruction *ir_instruction_inttoenum_get_dep(IrInstructionIntToEnum *i
2580 }2602 }
2581}2603}
25822604
2605static IrInstruction *ir_instruction_checkswitchprongs_get_dep(IrInstructionCheckSwitchProngs *instruction,
2606 size_t index)
2607{
2608 if (index == 0) return instruction->target_value;
2609 size_t range_index = index - 1;
2610 if (range_index < instruction->range_count * 2) {
2611 IrInstructionCheckSwitchProngsRange *range = &instruction->ranges[range_index / 2];
2612 return (range_index % 2 == 0) ? range->start : range->end;
2613 }
2614 return nullptr;
2615}
2616
2583static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t index) {2617static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t index) {
2584 switch (instruction->id) {2618 switch (instruction->id) {
2585 case IrInstructionIdInvalid:2619 case IrInstructionIdInvalid:
...@@ -2752,6 +2786,8 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t...@@ -2752,6 +2786,8 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t
2752 return ir_instruction_ptrtoint_get_dep((IrInstructionPtrToInt *) instruction, index);2786 return ir_instruction_ptrtoint_get_dep((IrInstructionPtrToInt *) instruction, index);
2753 case IrInstructionIdIntToEnum:2787 case IrInstructionIdIntToEnum:
2754 return ir_instruction_inttoenum_get_dep((IrInstructionIntToEnum *) instruction, index);2788 return ir_instruction_inttoenum_get_dep((IrInstructionIntToEnum *) instruction, index);
2789 case IrInstructionIdCheckSwitchProngs:
2790 return ir_instruction_checkswitchprongs_get_dep((IrInstructionCheckSwitchProngs *) instruction, index);
2755 }2791 }
2756 zig_unreachable();2792 zig_unreachable();
2757}2793}
...@@ -4677,6 +4713,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *...@@ -4677,6 +4713,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *
46774713
4678 ZigList<IrInstruction *> incoming_values = {0};4714 ZigList<IrInstruction *> incoming_values = {0};
4679 ZigList<IrBasicBlock *> incoming_blocks = {0};4715 ZigList<IrBasicBlock *> incoming_blocks = {0};
4716 ZigList<IrInstructionCheckSwitchProngsRange> check_ranges = {0};
46804717
4681 AstNode *else_prong = nullptr;4718 AstNode *else_prong = nullptr;
4682 for (size_t prong_i = 0; prong_i < prong_count; prong_i += 1) {4719 for (size_t prong_i = 0; prong_i < prong_count; prong_i += 1) {
...@@ -4719,6 +4756,10 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *...@@ -4719,6 +4756,10 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *
4719 if (end_value == irb->codegen->invalid_instruction)4756 if (end_value == irb->codegen->invalid_instruction)
4720 return irb->codegen->invalid_instruction;4757 return irb->codegen->invalid_instruction;
47214758
4759 IrInstructionCheckSwitchProngsRange *check_range = check_ranges.add_one();
4760 check_range->start = start_value;
4761 check_range->end = end_value;
4762
4722 IrInstruction *start_value_const = ir_build_static_eval(irb, scope, start_node, start_value);4763 IrInstruction *start_value_const = ir_build_static_eval(irb, scope, start_node, start_value);
4723 IrInstruction *end_value_const = ir_build_static_eval(irb, scope, start_node, end_value);4764 IrInstruction *end_value_const = ir_build_static_eval(irb, scope, start_node, end_value);
47244765
...@@ -4738,6 +4779,10 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *...@@ -4738,6 +4779,10 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *
4738 if (item_value == irb->codegen->invalid_instruction)4779 if (item_value == irb->codegen->invalid_instruction)
4739 return irb->codegen->invalid_instruction;4780 return irb->codegen->invalid_instruction;
47404781
4782 IrInstructionCheckSwitchProngsRange *check_range = check_ranges.add_one();
4783 check_range->start = item_value;
4784 check_range->end = item_value;
4785
4741 IrInstruction *cmp_ok = ir_build_bin_op(irb, scope, item_node, IrBinOpCmpEq,4786 IrInstruction *cmp_ok = ir_build_bin_op(irb, scope, item_node, IrBinOpCmpEq,
4742 item_value, target_value, false);4787 item_value, target_value, false);
4743 if (ok_bit) {4788 if (ok_bit) {
...@@ -4776,6 +4821,10 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *...@@ -4776,6 +4821,10 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *
4776 if (item_value == irb->codegen->invalid_instruction)4821 if (item_value == irb->codegen->invalid_instruction)
4777 return irb->codegen->invalid_instruction;4822 return irb->codegen->invalid_instruction;
47784823
4824 IrInstructionCheckSwitchProngsRange *check_range = check_ranges.add_one();
4825 check_range->start = item_value;
4826 check_range->end = item_value;
4827
4779 IrInstructionSwitchBrCase *this_case = cases.add_one();4828 IrInstructionSwitchBrCase *this_case = cases.add_one();
4780 this_case->value = item_value;4829 this_case->value = item_value;
4781 this_case->block = prong_block;4830 this_case->block = prong_block;
...@@ -4798,6 +4847,10 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *...@@ -4798,6 +4847,10 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *
4798 }4847 }
4799 }4848 }
48004849
4850 if (!else_prong) {
4851 ir_build_check_switch_prongs(irb, scope, node, target_value, check_ranges.items, check_ranges.length);
4852 }
4853
4801 if (cases.length == 0) {4854 if (cases.length == 0) {
4802 ir_build_br(irb, scope, node, else_block, is_comptime);4855 ir_build_br(irb, scope, node, else_block, is_comptime);
4803 } else {4856 } else {
...@@ -11091,6 +11144,69 @@ static TypeTableEntry *ir_analyze_instruction_test_comptime(IrAnalyze *ira, IrIn...@@ -11091,6 +11144,69 @@ static TypeTableEntry *ir_analyze_instruction_test_comptime(IrAnalyze *ira, IrIn
11091 return ira->codegen->builtin_types.entry_bool;11144 return ira->codegen->builtin_types.entry_bool;
11092}11145}
1109311146
11147static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
11148 IrInstructionCheckSwitchProngs *instruction)
11149{
11150 IrInstruction *target_value = instruction->target_value->other;
11151 TypeTableEntry *switch_type = target_value->value.type;
11152 if (switch_type->id == TypeTableEntryIdInvalid)
11153 return ira->codegen->builtin_types.entry_invalid;
11154
11155 if (switch_type->id == TypeTableEntryIdEnumTag) {
11156 TypeTableEntry *enum_type = switch_type->data.enum_tag.enum_type;
11157 size_t *field_use_counts = allocate<size_t>(enum_type->data.enumeration.src_field_count);
11158 for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) {
11159 IrInstructionCheckSwitchProngsRange *range = &instruction->ranges[range_i];
11160
11161 IrInstruction *start_value = range->start->other;
11162 if (start_value->value.type->id == TypeTableEntryIdInvalid)
11163 return ira->codegen->builtin_types.entry_invalid;
11164
11165 IrInstruction *end_value = range->end->other;
11166 if (end_value->value.type->id == TypeTableEntryIdInvalid)
11167 return ira->codegen->builtin_types.entry_invalid;
11168
11169 size_t start_index;
11170 size_t end_index;
11171 if (start_value->value.type->id == TypeTableEntryIdEnumTag) {
11172 start_index = start_value->value.data.x_bignum.data.x_uint;
11173 } else if (start_value->value.type->id == TypeTableEntryIdEnum) {
11174 start_index = start_value->value.data.x_enum.tag;
11175 } else {
11176 zig_unreachable();
11177 }
11178 if (end_value->value.type->id == TypeTableEntryIdEnumTag) {
11179 end_index = end_value->value.data.x_bignum.data.x_uint;
11180 } else if (end_value->value.type->id == TypeTableEntryIdEnum) {
11181 end_index = end_value->value.data.x_enum.tag;
11182 } else {
11183 zig_unreachable();
11184 }
11185
11186 for (size_t field_index = start_index; field_index <= end_index; field_index += 1) {
11187 field_use_counts[field_index] += 1;
11188 if (field_use_counts[field_index] > 1) {
11189 TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_index];
11190 ir_add_error(ira, start_value,
11191 buf_sprintf("duplicate switch value: '%s.%s'", buf_ptr(&enum_type->name),
11192 buf_ptr(type_enum_field->name)));
11193 }
11194 }
11195 }
11196 for (uint32_t i = 0; i < enum_type->data.enumeration.src_field_count; i += 1) {
11197 if (field_use_counts[i] == 0) {
11198 ir_add_error(ira, &instruction->base,
11199 buf_sprintf("enumeration value '%s.%s' not handled in switch", buf_ptr(&enum_type->name),
11200 buf_ptr(enum_type->data.enumeration.fields[i].name)));
11201 }
11202 }
11203 } else {
11204 // TODO check prongs of types other than enumtag
11205 }
11206 ir_build_const_from(ira, &instruction->base, false);
11207 return ira->codegen->builtin_types.entry_void;
11208}
11209
11094static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {11210static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
11095 switch (instruction->id) {11211 switch (instruction->id) {
11096 case IrInstructionIdInvalid:11212 case IrInstructionIdInvalid:
...@@ -11246,6 +11362,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi...@@ -11246,6 +11362,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
11246 return ir_analyze_instruction_fn_proto(ira, (IrInstructionFnProto *)instruction);11362 return ir_analyze_instruction_fn_proto(ira, (IrInstructionFnProto *)instruction);
11247 case IrInstructionIdTestComptime:11363 case IrInstructionIdTestComptime:
11248 return ir_analyze_instruction_test_comptime(ira, (IrInstructionTestComptime *)instruction);11364 return ir_analyze_instruction_test_comptime(ira, (IrInstructionTestComptime *)instruction);
11365 case IrInstructionIdCheckSwitchProngs:
11366 return ir_analyze_instruction_check_switch_prongs(ira, (IrInstructionCheckSwitchProngs *)instruction);
11249 case IrInstructionIdMaybeWrap:11367 case IrInstructionIdMaybeWrap:
11250 case IrInstructionIdErrWrapCode:11368 case IrInstructionIdErrWrapCode:
11251 case IrInstructionIdErrWrapPayload:11369 case IrInstructionIdErrWrapPayload:
...@@ -11351,6 +11469,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {...@@ -11351,6 +11469,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
11351 case IrInstructionIdMemcpy:11469 case IrInstructionIdMemcpy:
11352 case IrInstructionIdBreakpoint:11470 case IrInstructionIdBreakpoint:
11353 case IrInstructionIdOverflowOp: // TODO when we support multiple returns this can be side effect free11471 case IrInstructionIdOverflowOp: // TODO when we support multiple returns this can be side effect free
11472 case IrInstructionIdCheckSwitchProngs:
11354 return true;11473 return true;
11355 case IrInstructionIdPhi:11474 case IrInstructionIdPhi:
11356 case IrInstructionIdUnOp:11475 case IrInstructionIdUnOp:
src/ir_print.cpp+17
...@@ -799,6 +799,20 @@ static void ir_print_int_to_enum(IrPrint *irp, IrInstructionIntToEnum *instructi...@@ -799,6 +799,20 @@ static void ir_print_int_to_enum(IrPrint *irp, IrInstructionIntToEnum *instructi
799 fprintf(irp->f, ")");799 fprintf(irp->f, ")");
800}800}
801801
802static void ir_print_check_switch_prongs(IrPrint *irp, IrInstructionCheckSwitchProngs *instruction) {
803 fprintf(irp->f, "@checkSwitchProngs(");
804 ir_print_other_instruction(irp, instruction->target_value);
805 fprintf(irp->f, ",");
806 for (size_t i = 0; i < instruction->range_count; i += 1) {
807 if (i != 0)
808 fprintf(irp->f, ",");
809 ir_print_other_instruction(irp, instruction->ranges[i].start);
810 fprintf(irp->f, "...");
811 ir_print_other_instruction(irp, instruction->ranges[i].end);
812 }
813 fprintf(irp->f, ")");
814}
815
802static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {816static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
803 ir_print_prefix(irp, instruction);817 ir_print_prefix(irp, instruction);
804 switch (instruction->id) {818 switch (instruction->id) {
...@@ -1056,6 +1070,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {...@@ -1056,6 +1070,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
1056 case IrInstructionIdIntToEnum:1070 case IrInstructionIdIntToEnum:
1057 ir_print_int_to_enum(irp, (IrInstructionIntToEnum *)instruction);1071 ir_print_int_to_enum(irp, (IrInstructionIntToEnum *)instruction);
1058 break;1072 break;
1073 case IrInstructionIdCheckSwitchProngs:
1074 ir_print_check_switch_prongs(irp, (IrInstructionCheckSwitchProngs *)instruction);
1075 break;
1059 }1076 }
1060 fprintf(irp->f, "\n");1077 fprintf(irp->f, "\n");
1061}1078}
test/run_tests.cpp+4-12
...@@ -1167,12 +1167,12 @@ const Number = enum {...@@ -1167,12 +1167,12 @@ const Number = enum {
1167};1167};
1168fn f(n: Number) -> i32 {1168fn f(n: Number) -> i32 {
1169 switch (n) {1169 switch (n) {
1170 One => 1,1170 Number.One => 1,
1171 Two => 2,1171 Number.Two => 2,
1172 Three => 3,1172 Number.Three => i32(3),
1173 }1173 }
1174}1174}
1175 )SOURCE", 1, ".tmp_source.zig:9:5: error: enumeration value 'Four' not handled in switch");1175 )SOURCE", 1, ".tmp_source.zig:9:5: error: enumeration value 'Number.Four' not handled in switch");
11761176
1177 add_compile_fail_case("import inside function body", R"SOURCE(1177 add_compile_fail_case("import inside function body", R"SOURCE(
1178fn f() {1178fn f() {
...@@ -1430,14 +1430,6 @@ fn f() -> i32 {...@@ -1430,14 +1430,6 @@ fn f() -> i32 {
1430}1430}
1431 )SOURCE", 1, ".tmp_source.zig:2:15: error: inline parameter not allowed in extern function");1431 )SOURCE", 1, ".tmp_source.zig:2:15: error: inline parameter not allowed in extern function");
14321432
1433 /* TODO
1434 add_compile_fail_case("inline export function", R"SOURCE(
1435export inline fn foo(x: i32, y: i32) -> i32{
1436 x + y
1437}
1438 )SOURCE", 1, ".tmp_source.zig:2:1: error: extern functions cannot be inline");
1439 */
1440
1441 add_compile_fail_case("convert fixed size array to slice with invalid size", R"SOURCE(1433 add_compile_fail_case("convert fixed size array to slice with invalid size", R"SOURCE(
1442fn f() {1434fn f() {
1443 var array: [5]u8 = undefined;1435 var array: [5]u8 = undefined;