| author | |
| committer | |
| log | c464ecf3bf6d764ad06a20f10efc386e26fb7fa2 |
| tree | 158d169774098e271771c9462b86fb68a10c6990 |
| parent | aaef6259c32ff43be912c31f70e005170ee86efd |
| parent | 64061cc1bfb8e925d20f5824aa178b61eb2e5f11 |
| signature | Signed by PGP key 4AEE18F83AFDEB23 |
Switching on bools with duplicate and missing value detection: Issue …3 files changed, 108 insertions(+), 0 deletions(-)
src/ir.cpp+33| ... | @@ -19787,6 +19787,39 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira, | ... | @@ -19787,6 +19787,39 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira, |
| 19787 | return ira->codegen->invalid_instruction; | 19787 | return ira->codegen->invalid_instruction; |
| 19788 | } | 19788 | } |
| 19789 | } | 19789 | } |
| 19790 | } else if (switch_type->id == ZigTypeIdBool) { | ||
| 19791 | int seenTrue = 0; | ||
| 19792 | int seenFalse = 0; | ||
| 19793 | for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) { | ||
| 19794 | IrInstructionCheckSwitchProngsRange *range = &instruction->ranges[range_i]; | ||
| 19795 | |||
| 19796 | IrInstruction *value = range->start->child; | ||
| 19797 | |||
| 19798 | IrInstruction *casted_value = ir_implicit_cast(ira, value, switch_type); | ||
| 19799 | if (type_is_invalid(casted_value->value.type)) | ||
| 19800 | return ira->codegen->invalid_instruction; | ||
| 19801 | |||
| 19802 | ConstExprValue *const_expr_val = ir_resolve_const(ira, casted_value, UndefBad); | ||
| 19803 | if (!const_expr_val) | ||
| 19804 | return ira->codegen->invalid_instruction; | ||
| 19805 | |||
| 19806 | assert(const_expr_val->type->id == ZigTypeIdBool); | ||
| 19807 | |||
| 19808 | if (const_expr_val->data.x_bool == true) { | ||
| 19809 | seenTrue += 1; | ||
| 19810 | } else { | ||
| 19811 | seenFalse += 1; | ||
| 19812 | } | ||
| 19813 | |||
| 19814 | if ((seenTrue > 1) || (seenFalse > 1)) { | ||
| 19815 | ir_add_error(ira, value, buf_sprintf("duplicate switch value")); | ||
| 19816 | return ira->codegen->invalid_instruction; | ||
| 19817 | } | ||
| 19818 | } | ||
| 19819 | if (((seenTrue < 1) || (seenFalse < 1)) && !instruction->have_else_prong) { | ||
| 19820 | ir_add_error(ira, &instruction->base, buf_sprintf("switch must handle all possibilities")); | ||
| 19821 | return ira->codegen->invalid_instruction; | ||
| 19822 | } | ||
| 19790 | } else if (!instruction->have_else_prong) { | 19823 | } else if (!instruction->have_else_prong) { |
| 19791 | ir_add_error(ira, &instruction->base, | 19824 | ir_add_error(ira, &instruction->base, |
| 19792 | buf_sprintf("else prong required when switching on type '%s'", buf_ptr(&switch_type->name))); | 19825 | buf_sprintf("else prong required when switching on type '%s'", buf_ptr(&switch_type->name))); |
test/cases/switch.zig+37| ... | @@ -232,3 +232,40 @@ test "capture value of switch with all unreachable prongs" { | ... | @@ -232,3 +232,40 @@ test "capture value of switch with all unreachable prongs" { |
| 232 | }; | 232 | }; |
| 233 | assert(x == 1); | 233 | assert(x == 1); |
| 234 | } | 234 | } |
| 235 | |||
| 236 | test "switching on booleans" { | ||
| 237 | testSwitchOnBools(); | ||
| 238 | comptime testSwitchOnBools(); | ||
| 239 | } | ||
| 240 | |||
| 241 | fn testSwitchOnBools() void { | ||
| 242 | assert(testSwitchOnBoolsTrueAndFalse(true) == false); | ||
| 243 | assert(testSwitchOnBoolsTrueAndFalse(false) == true); | ||
| 244 | |||
| 245 | assert(testSwitchOnBoolsTrueWithElse(true) == false); | ||
| 246 | assert(testSwitchOnBoolsTrueWithElse(false) == true); | ||
| 247 | |||
| 248 | assert(testSwitchOnBoolsFalseWithElse(true) == false); | ||
| 249 | assert(testSwitchOnBoolsFalseWithElse(false) == true); | ||
| 250 | } | ||
| 251 | |||
| 252 | fn testSwitchOnBoolsTrueAndFalse(x: bool) bool { | ||
| 253 | return switch (x) { | ||
| 254 | true => false, | ||
| 255 | false => true, | ||
| 256 | }; | ||
| 257 | } | ||
| 258 | |||
| 259 | fn testSwitchOnBoolsTrueWithElse(x: bool) bool { | ||
| 260 | return switch (x) { | ||
| 261 | true => false, | ||
| 262 | else => true, | ||
| 263 | }; | ||
| 264 | } | ||
| 265 | |||
| 266 | fn testSwitchOnBoolsFalseWithElse(x: bool) bool { | ||
| 267 | return switch (x) { | ||
| 268 | false => true, | ||
| 269 | else => false, | ||
| 270 | }; | ||
| 271 | } |
test/compile_errors.zig+38| ... | @@ -1,6 +1,44 @@ | ... | @@ -1,6 +1,44 @@ |
| 1 | const tests = @import("tests.zig"); | 1 | const tests = @import("tests.zig"); |
| 2 | 2 | ||
| 3 | pub fn addCases(cases: *tests.CompileErrorContext) void { | 3 | pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4 | cases.add( | ||
| 5 | "duplicate boolean switch value", | ||
| 6 | \\comptime { | ||
| 7 | \\ const x = switch (true) { | ||
| 8 | \\ true => false, | ||
| 9 | \\ false => true, | ||
| 10 | \\ true => false, | ||
| 11 | \\ }; | ||
| 12 | \\} | ||
| 13 | \\comptime { | ||
| 14 | \\ const x = switch (true) { | ||
| 15 | \\ false => true, | ||
| 16 | \\ true => false, | ||
| 17 | \\ false => true, | ||
| 18 | \\ }; | ||
| 19 | \\} | ||
| 20 | , | ||
| 21 | ".tmp_source.zig:5:9: error: duplicate switch value", | ||
| 22 | ".tmp_source.zig:12:9: error: duplicate switch value", | ||
| 23 | ); | ||
| 24 | |||
| 25 | cases.add( | ||
| 26 | "missing boolean switch value", | ||
| 27 | \\comptime { | ||
| 28 | \\ const x = switch (true) { | ||
| 29 | \\ true => false, | ||
| 30 | \\ }; | ||
| 31 | \\} | ||
| 32 | \\comptime { | ||
| 33 | \\ const x = switch (true) { | ||
| 34 | \\ false => true, | ||
| 35 | \\ }; | ||
| 36 | \\} | ||
| 37 | , | ||
| 38 | ".tmp_source.zig:2:15: error: switch must handle all possibilities", | ||
| 39 | ".tmp_source.zig:7:15: error: switch must handle all possibilities", | ||
| 40 | ); | ||
| 41 | |||
| 4 | cases.add( | 42 | cases.add( |
| 5 | "reading past end of pointer casted array", | 43 | "reading past end of pointer casted array", |
| 6 | \\comptime { | 44 | \\comptime { |