| author | |
| committer | |
| log | d3e67d99216d3dd6c18259c17652fcad54aebc21 |
| tree | c61934554aeab9798a0bc8ae29ab95c80b54eefe |
| parent | b9f37ffe19e30da1fd79cd01dbb6191ec2733b5c |
| parent | cae93c860bc2c599618482a4190daf619a0c69e2 |
| signature |
Two switch-related patches4 files changed, 72 insertions(+), 3 deletions(-)
src/codegen.cpp+19-3| ... | @@ -4876,14 +4876,30 @@ static LLVMValueRef ir_render_pop_count(CodeGen *g, IrExecutable *executable, Ir | ... | @@ -4876,14 +4876,30 @@ static LLVMValueRef ir_render_pop_count(CodeGen *g, IrExecutable *executable, Ir |
| 4876 | } | 4876 | } |
| 4877 | 4877 | ||
| 4878 | static LLVMValueRef ir_render_switch_br(CodeGen *g, IrExecutable *executable, IrInstructionSwitchBr *instruction) { | 4878 | static LLVMValueRef ir_render_switch_br(CodeGen *g, IrExecutable *executable, IrInstructionSwitchBr *instruction) { |
| 4879 | LLVMValueRef target_value = ir_llvm_value(g, instruction->target_value); | 4879 | ZigType *target_type = instruction->target_value->value->type; |
| 4880 | LLVMBasicBlockRef else_block = instruction->else_block->llvm_block; | 4880 | LLVMBasicBlockRef else_block = instruction->else_block->llvm_block; |
| 4881 | |||
| 4882 | LLVMValueRef target_value = ir_llvm_value(g, instruction->target_value); | ||
| 4883 | if (target_type->id == ZigTypeIdPointer) { | ||
| 4884 | const ZigType *usize = g->builtin_types.entry_usize; | ||
| 4885 | target_value = LLVMBuildPtrToInt(g->builder, target_value, usize->llvm_type, ""); | ||
| 4886 | } | ||
| 4887 | |||
| 4881 | LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, target_value, else_block, | 4888 | LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, target_value, else_block, |
| 4882 | (unsigned)instruction->case_count); | 4889 | (unsigned)instruction->case_count); |
| 4890 | |||
| 4883 | for (size_t i = 0; i < instruction->case_count; i += 1) { | 4891 | for (size_t i = 0; i < instruction->case_count; i += 1) { |
| 4884 | IrInstructionSwitchBrCase *this_case = &instruction->cases[i]; | 4892 | IrInstructionSwitchBrCase *this_case = &instruction->cases[i]; |
| 4885 | LLVMAddCase(switch_instr, ir_llvm_value(g, this_case->value), this_case->block->llvm_block); | 4893 | |
| 4894 | LLVMValueRef case_value = ir_llvm_value(g, this_case->value); | ||
| 4895 | if (target_type->id == ZigTypeIdPointer) { | ||
| 4896 | const ZigType *usize = g->builtin_types.entry_usize; | ||
| 4897 | case_value = LLVMBuildPtrToInt(g->builder, case_value, usize->llvm_type, ""); | ||
| 4898 | } | ||
| 4899 | |||
| 4900 | LLVMAddCase(switch_instr, case_value, this_case->block->llvm_block); | ||
| 4886 | } | 4901 | } |
| 4902 | |||
| 4887 | return nullptr; | 4903 | return nullptr; |
| 4888 | } | 4904 | } |
| 4889 | 4905 |
src/ir.cpp+12| ... | @@ -26394,6 +26394,7 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira, | ... | @@ -26394,6 +26394,7 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira, |
| 26394 | if (type_is_invalid(end_value->value->type)) | 26394 | if (type_is_invalid(end_value->value->type)) |
| 26395 | return ira->codegen->invalid_instruction; | 26395 | return ira->codegen->invalid_instruction; |
| 26396 | 26396 | ||
| 26397 | assert(start_value->value->type->id == ZigTypeIdEnum); | ||
| 26397 | BigInt start_index; | 26398 | BigInt start_index; |
| 26398 | bigint_init_bigint(&start_index, &start_value->value->data.x_enum_tag); | 26399 | bigint_init_bigint(&start_index, &start_value->value->data.x_enum_tag); |
| 26399 | 26400 | ||
| ... | @@ -26401,6 +26402,11 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira, | ... | @@ -26401,6 +26402,11 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira, |
| 26401 | BigInt end_index; | 26402 | BigInt end_index; |
| 26402 | bigint_init_bigint(&end_index, &end_value->value->data.x_enum_tag); | 26403 | bigint_init_bigint(&end_index, &end_value->value->data.x_enum_tag); |
| 26403 | 26404 | ||
| 26405 | if (bigint_cmp(&start_index, &end_index) == CmpGT) { | ||
| 26406 | ir_add_error(ira, start_value, | ||
| 26407 | buf_sprintf("range start value is greater than the end value")); | ||
| 26408 | } | ||
| 26409 | |||
| 26404 | BigInt field_index; | 26410 | BigInt field_index; |
| 26405 | bigint_init_bigint(&field_index, &start_index); | 26411 | bigint_init_bigint(&field_index, &start_index); |
| 26406 | for (;;) { | 26412 | for (;;) { |
| ... | @@ -26530,6 +26536,12 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira, | ... | @@ -26530,6 +26536,12 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira, |
| 26530 | 26536 | ||
| 26531 | assert(start_val->type->id == ZigTypeIdInt || start_val->type->id == ZigTypeIdComptimeInt); | 26537 | assert(start_val->type->id == ZigTypeIdInt || start_val->type->id == ZigTypeIdComptimeInt); |
| 26532 | assert(end_val->type->id == ZigTypeIdInt || end_val->type->id == ZigTypeIdComptimeInt); | 26538 | assert(end_val->type->id == ZigTypeIdInt || end_val->type->id == ZigTypeIdComptimeInt); |
| 26539 | |||
| 26540 | if (bigint_cmp(&start_val->data.x_bigint, &end_val->data.x_bigint) == CmpGT) { | ||
| 26541 | ir_add_error(ira, start_value, | ||
| 26542 | buf_sprintf("range start value is greater than the end value")); | ||
| 26543 | } | ||
| 26544 | |||
| 26533 | AstNode *prev_node = rangeset_add_range(&rs, &start_val->data.x_bigint, &end_val->data.x_bigint, | 26545 | AstNode *prev_node = rangeset_add_range(&rs, &start_val->data.x_bigint, &end_val->data.x_bigint, |
| 26534 | start_value->source_node); | 26546 | start_value->source_node); |
| 26535 | if (prev_node != nullptr) { | 26547 | if (prev_node != nullptr) { |
test/compile_errors.zig+14| ... | @@ -2,6 +2,20 @@ const tests = @import("tests.zig"); | ... | @@ -2,6 +2,20 @@ const tests = @import("tests.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | 3 | ||
| 4 | pub fn addCases(cases: *tests.CompileErrorContext) void { | 4 | pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5 | cases.addTest("switch ranges endpoints are validated", | ||
| 6 | \\pub export fn entry() void { | ||
| 7 | \\ var x: i32 = 0; | ||
| 8 | \\ switch (x) { | ||
| 9 | \\ 6...1 => {}, | ||
| 10 | \\ -1...-5 => {}, | ||
| 11 | \\ else => unreachable, | ||
| 12 | \\ } | ||
| 13 | \\} | ||
| 14 | , &[_][]const u8{ | ||
| 15 | "tmp.zig:4:9: error: range start value is greater than the end value", | ||
| 16 | "tmp.zig:5:9: error: range start value is greater than the end value", | ||
| 17 | }); | ||
| 18 | |||
| 5 | cases.addTest("errors in for loop bodies are propagated", | 19 | cases.addTest("errors in for loop bodies are propagated", |
| 6 | \\pub export fn entry() void { | 20 | \\pub export fn entry() void { |
| 7 | \\ var arr: [100]u8 = undefined; | 21 | \\ var arr: [100]u8 = undefined; |
test/stage1/behavior/switch.zig+27| ... | @@ -452,3 +452,30 @@ test "switch on global mutable var isn't constant-folded" { | ... | @@ -452,3 +452,30 @@ test "switch on global mutable var isn't constant-folded" { |
| 452 | poll(); | 452 | poll(); |
| 453 | } | 453 | } |
| 454 | } | 454 | } |
| 455 | |||
| 456 | test "switch on pointer type" { | ||
| 457 | const S = struct { | ||
| 458 | const X = struct { | ||
| 459 | field: u32, | ||
| 460 | }; | ||
| 461 | |||
| 462 | const P1 = @intToPtr(*X, 0x400); | ||
| 463 | const P2 = @intToPtr(*X, 0x800); | ||
| 464 | const P3 = @intToPtr(*X, 0xC00); | ||
| 465 | |||
| 466 | fn doTheTest(arg: *X) i32 { | ||
| 467 | switch (arg) { | ||
| 468 | P1 => return 1, | ||
| 469 | P2 => return 2, | ||
| 470 | else => return 3, | ||
| 471 | } | ||
| 472 | } | ||
| 473 | }; | ||
| 474 | |||
| 475 | expect(1 == S.doTheTest(S.P1)); | ||
| 476 | expect(2 == S.doTheTest(S.P2)); | ||
| 477 | expect(3 == S.doTheTest(S.P3)); | ||
| 478 | comptime expect(1 == S.doTheTest(S.P1)); | ||
| 479 | comptime expect(2 == S.doTheTest(S.P2)); | ||
| 480 | comptime expect(3 == S.doTheTest(S.P3)); | ||
| 481 | } |