From 84930fec279a8bf0e7ce79c79a7ccd98d1ef4d0d Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Mon, 13 Jan 2020 21:45:16 +0100 Subject: [PATCH 1/2] Validate switch range endpoints --- src/ir.cpp | 12 ++++++++++++ test/compile_errors.zig | 14 ++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/ir.cpp b/src/ir.cpp index a5561e9bfd..bbf56fd08d 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -26394,6 +26394,7 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira, if (type_is_invalid(end_value->value->type)) return ira->codegen->invalid_instruction; + assert(start_value->value->type->id == ZigTypeIdEnum); BigInt start_index; bigint_init_bigint(&start_index, &start_value->value->data.x_enum_tag); @@ -26401,6 +26402,11 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira, BigInt end_index; bigint_init_bigint(&end_index, &end_value->value->data.x_enum_tag); + if (bigint_cmp(&start_index, &end_index) == CmpGT) { + ir_add_error(ira, start_value, + buf_sprintf("range start value is greater than the end value")); + } + BigInt field_index; bigint_init_bigint(&field_index, &start_index); for (;;) { @@ -26530,6 +26536,12 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira, assert(start_val->type->id == ZigTypeIdInt || start_val->type->id == ZigTypeIdComptimeInt); assert(end_val->type->id == ZigTypeIdInt || end_val->type->id == ZigTypeIdComptimeInt); + + if (bigint_cmp(&start_val->data.x_bigint, &end_val->data.x_bigint) == CmpGT) { + ir_add_error(ira, start_value, + buf_sprintf("range start value is greater than the end value")); + } + AstNode *prev_node = rangeset_add_range(&rs, &start_val->data.x_bigint, &end_val->data.x_bigint, start_value->source_node); if (prev_node != nullptr) { diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 5ec93c3b6a..6552ad2413 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2,6 +2,20 @@ const tests = @import("tests.zig"); const builtin = @import("builtin"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.addTest("switch ranges endpoints are validated", + \\pub export fn entry() void { + \\ var x: i32 = 0; + \\ switch (x) { + \\ 6...1 => {}, + \\ -1...-5 => {}, + \\ else => unreachable, + \\ } + \\} + , &[_][]const u8{ + "tmp.zig:4:9: error: range start value is greater than the end value", + "tmp.zig:5:9: error: range start value is greater than the end value", + }); + cases.addTest("errors in for loop bodies are propagated", \\pub export fn entry() void { \\ var arr: [100]u8 = undefined; From cae93c860bc2c599618482a4190daf619a0c69e2 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Mon, 13 Jan 2020 22:18:49 +0100 Subject: [PATCH 2/2] Allow switching on pointer types Closes #4074 --- src/codegen.cpp | 22 +++++++++++++++++++--- test/stage1/behavior/switch.zig | 27 +++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index fd36e15f82..d64c191c93 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4876,14 +4876,30 @@ static LLVMValueRef ir_render_pop_count(CodeGen *g, IrExecutable *executable, Ir } static LLVMValueRef ir_render_switch_br(CodeGen *g, IrExecutable *executable, IrInstructionSwitchBr *instruction) { - LLVMValueRef target_value = ir_llvm_value(g, instruction->target_value); + ZigType *target_type = instruction->target_value->value->type; LLVMBasicBlockRef else_block = instruction->else_block->llvm_block; + + LLVMValueRef target_value = ir_llvm_value(g, instruction->target_value); + if (target_type->id == ZigTypeIdPointer) { + const ZigType *usize = g->builtin_types.entry_usize; + target_value = LLVMBuildPtrToInt(g->builder, target_value, usize->llvm_type, ""); + } + LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, target_value, else_block, - (unsigned)instruction->case_count); + (unsigned)instruction->case_count); + for (size_t i = 0; i < instruction->case_count; i += 1) { IrInstructionSwitchBrCase *this_case = &instruction->cases[i]; - LLVMAddCase(switch_instr, ir_llvm_value(g, this_case->value), this_case->block->llvm_block); + + LLVMValueRef case_value = ir_llvm_value(g, this_case->value); + if (target_type->id == ZigTypeIdPointer) { + const ZigType *usize = g->builtin_types.entry_usize; + case_value = LLVMBuildPtrToInt(g->builder, case_value, usize->llvm_type, ""); + } + + LLVMAddCase(switch_instr, case_value, this_case->block->llvm_block); } + return nullptr; } diff --git a/test/stage1/behavior/switch.zig b/test/stage1/behavior/switch.zig index 625585cd9e..3cfab63f86 100644 --- a/test/stage1/behavior/switch.zig +++ b/test/stage1/behavior/switch.zig @@ -452,3 +452,30 @@ test "switch on global mutable var isn't constant-folded" { poll(); } } + +test "switch on pointer type" { + const S = struct { + const X = struct { + field: u32, + }; + + const P1 = @intToPtr(*X, 0x400); + const P2 = @intToPtr(*X, 0x800); + const P3 = @intToPtr(*X, 0xC00); + + fn doTheTest(arg: *X) i32 { + switch (arg) { + P1 => return 1, + P2 => return 2, + else => return 3, + } + } + }; + + expect(1 == S.doTheTest(S.P1)); + expect(2 == S.doTheTest(S.P2)); + expect(3 == S.doTheTest(S.P3)); + comptime expect(1 == S.doTheTest(S.P1)); + comptime expect(2 == S.doTheTest(S.P2)); + comptime expect(3 == S.doTheTest(S.P3)); +}