| author | |
| committer | |
| log | f330eebe4bc6a036846cf05706f72855627c705a |
| tree | 7deea0f2c87091083fe5b59ab13d3924b645ea82 |
| parent | 7843c96df87007561c107d11afa2fa74b46667fd |
closes #18177 files changed, 104 insertions(+), 17 deletions(-)
src/all_types.hpp+7| ... | ... | @@ -2239,6 +2239,7 @@ enum IrInstructionId { |
| 2239 | 2239 | IrInstructionIdCheckRuntimeScope, |
| 2240 | 2240 | IrInstructionIdVectorToArray, |
| 2241 | 2241 | IrInstructionIdArrayToVector, |
| 2242 | IrInstructionIdAssertZero, | |
| 2242 | 2243 | }; |
| 2243 | 2244 | |
| 2244 | 2245 | struct IrInstruction { |
| ... | ... | @@ -3381,6 +3382,12 @@ struct IrInstructionVectorToArray { |
| 3381 | 3382 | LLVMValueRef tmp_ptr; |
| 3382 | 3383 | }; |
| 3383 | 3384 | |
| 3385 | struct IrInstructionAssertZero { | |
| 3386 | IrInstruction base; | |
| 3387 | ||
| 3388 | IrInstruction *target; | |
| 3389 | }; | |
| 3390 | ||
| 3384 | 3391 | static const size_t slice_ptr_index = 0; |
| 3385 | 3392 | static const size_t slice_len_index = 1; |
| 3386 | 3393 |
src/codegen.cpp+29-11| ... | ... | @@ -1651,10 +1651,25 @@ static void add_bounds_check(CodeGen *g, LLVMValueRef target_val, |
| 1651 | 1651 | LLVMPositionBuilderAtEnd(g->builder, ok_block); |
| 1652 | 1652 | } |
| 1653 | 1653 | |
| 1654 | static LLVMValueRef gen_assert_zero(CodeGen *g, LLVMValueRef expr_val, ZigType *int_type) { | |
| 1655 | LLVMValueRef zero = LLVMConstNull(int_type->type_ref); | |
| 1656 | LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, expr_val, zero, ""); | |
| 1657 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "CastShortenOk"); | |
| 1658 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "CastShortenFail"); | |
| 1659 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); | |
| 1660 | ||
| 1661 | LLVMPositionBuilderAtEnd(g->builder, fail_block); | |
| 1662 | gen_safety_crash(g, PanicMsgIdCastTruncatedData); | |
| 1663 | ||
| 1664 | LLVMPositionBuilderAtEnd(g->builder, ok_block); | |
| 1665 | return nullptr; | |
| 1666 | } | |
| 1667 | ||
| 1654 | 1668 | static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, ZigType *actual_type, |
| 1655 | 1669 | ZigType *wanted_type, LLVMValueRef expr_val) |
| 1656 | 1670 | { |
| 1657 | 1671 | assert(actual_type->id == wanted_type->id); |
| 1672 | assert(expr_val != nullptr); | |
| 1658 | 1673 | |
| 1659 | 1674 | uint64_t actual_bits; |
| 1660 | 1675 | uint64_t wanted_bits; |
| ... | ... | @@ -1707,17 +1722,7 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, Z |
| 1707 | 1722 | if (!want_runtime_safety) |
| 1708 | 1723 | return nullptr; |
| 1709 | 1724 | |
| 1710 | LLVMValueRef zero = LLVMConstNull(actual_type->type_ref); | |
| 1711 | LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, expr_val, zero, ""); | |
| 1712 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "CastShortenOk"); | |
| 1713 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "CastShortenFail"); | |
| 1714 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); | |
| 1715 | ||
| 1716 | LLVMPositionBuilderAtEnd(g->builder, fail_block); | |
| 1717 | gen_safety_crash(g, PanicMsgIdCastTruncatedData); | |
| 1718 | ||
| 1719 | LLVMPositionBuilderAtEnd(g->builder, ok_block); | |
| 1720 | return nullptr; | |
| 1725 | return gen_assert_zero(g, expr_val, actual_type); | |
| 1721 | 1726 | } |
| 1722 | 1727 | LLVMValueRef trunc_val = LLVMBuildTrunc(g->builder, expr_val, wanted_type->type_ref, ""); |
| 1723 | 1728 | if (!want_runtime_safety) { |
| ... | ... | @@ -5209,6 +5214,17 @@ static LLVMValueRef ir_render_array_to_vector(CodeGen *g, IrExecutable *executab |
| 5209 | 5214 | return gen_load_untyped(g, casted_ptr, 0, false, ""); |
| 5210 | 5215 | } |
| 5211 | 5216 | |
| 5217 | static LLVMValueRef ir_render_assert_zero(CodeGen *g, IrExecutable *executable, | |
| 5218 | IrInstructionAssertZero *instruction) | |
| 5219 | { | |
| 5220 | LLVMValueRef target = ir_llvm_value(g, instruction->target); | |
| 5221 | ZigType *int_type = instruction->target->value.type; | |
| 5222 | if (ir_want_runtime_safety(g, &instruction->base)) { | |
| 5223 | return gen_assert_zero(g, target, int_type); | |
| 5224 | } | |
| 5225 | return nullptr; | |
| 5226 | } | |
| 5227 | ||
| 5212 | 5228 | static void set_debug_location(CodeGen *g, IrInstruction *instruction) { |
| 5213 | 5229 | AstNode *source_node = instruction->source_node; |
| 5214 | 5230 | Scope *scope = instruction->scope; |
| ... | ... | @@ -5458,6 +5474,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, |
| 5458 | 5474 | return ir_render_array_to_vector(g, executable, (IrInstructionArrayToVector *)instruction); |
| 5459 | 5475 | case IrInstructionIdVectorToArray: |
| 5460 | 5476 | return ir_render_vector_to_array(g, executable, (IrInstructionVectorToArray *)instruction); |
| 5477 | case IrInstructionIdAssertZero: | |
| 5478 | return ir_render_assert_zero(g, executable, (IrInstructionAssertZero *)instruction); | |
| 5461 | 5479 | } |
| 5462 | 5480 | zig_unreachable(); |
| 5463 | 5481 | } |
src/ir.cpp+31| ... | ... | @@ -908,6 +908,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionArrayToVector *) |
| 908 | 908 | return IrInstructionIdArrayToVector; |
| 909 | 909 | } |
| 910 | 910 | |
| 911 | static constexpr IrInstructionId ir_instruction_id(IrInstructionAssertZero *) { | |
| 912 | return IrInstructionIdAssertZero; | |
| 913 | } | |
| 914 | ||
| 911 | 915 | template<typename T> |
| 912 | 916 | static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { |
| 913 | 917 | T *special_instruction = allocate<T>(1); |
| ... | ... | @@ -2858,6 +2862,19 @@ static IrInstruction *ir_build_array_to_vector(IrAnalyze *ira, IrInstruction *so |
| 2858 | 2862 | return &instruction->base; |
| 2859 | 2863 | } |
| 2860 | 2864 | |
| 2865 | static IrInstruction *ir_build_assert_zero(IrAnalyze *ira, IrInstruction *source_instruction, | |
| 2866 | IrInstruction *target) | |
| 2867 | { | |
| 2868 | IrInstructionAssertZero *instruction = ir_build_instruction<IrInstructionAssertZero>(&ira->new_irb, | |
| 2869 | source_instruction->scope, source_instruction->source_node); | |
| 2870 | instruction->base.value.type = ira->codegen->builtin_types.entry_void; | |
| 2871 | instruction->target = target; | |
| 2872 | ||
| 2873 | ir_ref_instruction(target, ira->new_irb.current_basic_block); | |
| 2874 | ||
| 2875 | return &instruction->base; | |
| 2876 | } | |
| 2877 | ||
| 2861 | 2878 | static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) { |
| 2862 | 2879 | results[ReturnKindUnconditional] = 0; |
| 2863 | 2880 | results[ReturnKindError] = 0; |
| ... | ... | @@ -10395,6 +10412,18 @@ static IrInstruction *ir_analyze_widen_or_shorten(IrAnalyze *ira, IrInstruction |
| 10395 | 10412 | return result; |
| 10396 | 10413 | } |
| 10397 | 10414 | |
| 10415 | // If the destination integer type has no bits, then we can emit a comptime | |
| 10416 | // zero. However, we still want to emit a runtime safety check to make sure | |
| 10417 | // the target is zero. | |
| 10418 | if (!type_has_bits(wanted_type)) { | |
| 10419 | assert(wanted_type->id == ZigTypeIdInt); | |
| 10420 | assert(type_has_bits(target->value.type)); | |
| 10421 | ir_build_assert_zero(ira, source_instr, target); | |
| 10422 | IrInstruction *result = ir_const_unsigned(ira, source_instr, 0); | |
| 10423 | result->value.type = wanted_type; | |
| 10424 | return result; | |
| 10425 | } | |
| 10426 | ||
| 10398 | 10427 | IrInstruction *result = ir_build_widen_or_shorten(&ira->new_irb, source_instr->scope, |
| 10399 | 10428 | source_instr->source_node, target); |
| 10400 | 10429 | result->value.type = wanted_type; |
| ... | ... | @@ -21705,6 +21734,7 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio |
| 21705 | 21734 | case IrInstructionIdCmpxchgGen: |
| 21706 | 21735 | case IrInstructionIdArrayToVector: |
| 21707 | 21736 | case IrInstructionIdVectorToArray: |
| 21737 | case IrInstructionIdAssertZero: | |
| 21708 | 21738 | zig_unreachable(); |
| 21709 | 21739 | |
| 21710 | 21740 | case IrInstructionIdReturn: |
| ... | ... | @@ -22103,6 +22133,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { |
| 22103 | 22133 | case IrInstructionIdAtomicRmw: |
| 22104 | 22134 | case IrInstructionIdCmpxchgGen: |
| 22105 | 22135 | case IrInstructionIdCmpxchgSrc: |
| 22136 | case IrInstructionIdAssertZero: | |
| 22106 | 22137 | return true; |
| 22107 | 22138 | |
| 22108 | 22139 | case IrInstructionIdPhi: |
src/ir_print.cpp+9| ... | ... | @@ -984,6 +984,12 @@ static void ir_print_vector_to_array(IrPrint *irp, IrInstructionVectorToArray *i |
| 984 | 984 | fprintf(irp->f, ")"); |
| 985 | 985 | } |
| 986 | 986 | |
| 987 | static void ir_print_assert_zero(IrPrint *irp, IrInstructionAssertZero *instruction) { | |
| 988 | fprintf(irp->f, "AssertZero("); | |
| 989 | ir_print_other_instruction(irp, instruction->target); | |
| 990 | fprintf(irp->f, ")"); | |
| 991 | } | |
| 992 | ||
| 987 | 993 | static void ir_print_int_to_err(IrPrint *irp, IrInstructionIntToErr *instruction) { |
| 988 | 994 | fprintf(irp->f, "inttoerr "); |
| 989 | 995 | ir_print_other_instruction(irp, instruction->target); |
| ... | ... | @@ -1843,6 +1849,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 1843 | 1849 | case IrInstructionIdVectorToArray: |
| 1844 | 1850 | ir_print_vector_to_array(irp, (IrInstructionVectorToArray *)instruction); |
| 1845 | 1851 | break; |
| 1852 | case IrInstructionIdAssertZero: | |
| 1853 | ir_print_assert_zero(irp, (IrInstructionAssertZero *)instruction); | |
| 1854 | break; | |
| 1846 | 1855 | } |
| 1847 | 1856 | fprintf(irp->f, "\n"); |
| 1848 | 1857 | } |
test/runtime_safety.zig+17| ... | ... | @@ -362,6 +362,23 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { |
| 362 | 362 | \\} |
| 363 | 363 | ); |
| 364 | 364 | |
| 365 | // @intCast a runtime integer to u0 actually results in a comptime-known value, | |
| 366 | // but we still emit a safety check to ensure the integer was 0 and thus | |
| 367 | // did not truncate information. | |
| 368 | cases.addRuntimeSafety("@intCast to u0", | |
| 369 | \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { | |
| 370 | \\ @import("std").os.exit(126); | |
| 371 | \\} | |
| 372 | \\ | |
| 373 | \\pub fn main() void { | |
| 374 | \\ bar(1, 1); | |
| 375 | \\} | |
| 376 | \\ | |
| 377 | \\fn bar(one: u1, not_zero: i32) void { | |
| 378 | \\ var x = one << @intCast(u0, not_zero); | |
| 379 | \\} | |
| 380 | ); | |
| 381 | ||
| 365 | 382 | // This case makes sure that the code compiles and runs. There is not actually a special |
| 366 | 383 | // runtime safety check having to do specifically with error return traces across suspend points. |
| 367 | 384 | cases.addRuntimeSafety("error return trace across suspend points", |
test/stage1/behavior/cast.zig+11| ... | ... | @@ -471,3 +471,14 @@ test "@intToEnum passed a comptime_int to an enum with one item" { |
| 471 | 471 | const x = @intToEnum(E, 0); |
| 472 | 472 | assertOrPanic(x == E.A); |
| 473 | 473 | } |
| 474 | ||
| 475 | test "@intCast to u0 and use the result" { | |
| 476 | const S = struct { | |
| 477 | fn doTheTest(zero: u1, one: u1, bigzero: i32) void { | |
| 478 | assertOrPanic((one << @intCast(u0, bigzero)) == 1); | |
| 479 | assertOrPanic((zero << @intCast(u0, bigzero)) == 0); | |
| 480 | } | |
| 481 | }; | |
| 482 | S.doTheTest(0, 1, 0); | |
| 483 | comptime S.doTheTest(0, 1, 0); | |
| 484 | } |
test/stage1/behavior/eval.zig-6| ... | ... | @@ -697,12 +697,6 @@ test "bit shift a u1" { |
| 697 | 697 | assertOrPanic(y == 1); |
| 698 | 698 | } |
| 699 | 699 | |
| 700 | test "@intCast to a u0" { | |
| 701 | var x: u8 = 0; | |
| 702 | var y: u0 = @intCast(u0, x); | |
| 703 | assertOrPanic(y == 0); | |
| 704 | } | |
| 705 | ||
| 706 | 700 | test "@bytesToslice on a packed struct" { |
| 707 | 701 | const F = packed struct { |
| 708 | 702 | a: u8, |