| author | |
| committer | |
| log | 51c6bb92b1c0c02b214ae21986dce3f2e9960099 |
| tree | 58d7865a9829233fa789fc1a9a4946c66d38d89e |
| parent | 83f6f730cdd5bb9c2a12b30c0aac33d858a1eaa8 |
| parent | 2f1052a313cb09f87f04cef56805c33be62eb169 |
| signature |
Stricter shift left/right safety checks8 files changed, 172 insertions(+), 32 deletions(-)
lib/std/io.zig+8-2| ... | @@ -350,12 +350,18 @@ pub fn BitInStream(endian: builtin.Endian, comptime Error: type) type { | ... | @@ -350,12 +350,18 @@ pub fn BitInStream(endian: builtin.Endian, comptime Error: type) type { |
| 350 | switch (endian) { | 350 | switch (endian) { |
| 351 | .Big => { | 351 | .Big => { |
| 352 | out_buffer = @as(Buf, self.bit_buffer >> shift); | 352 | out_buffer = @as(Buf, self.bit_buffer >> shift); |
| 353 | self.bit_buffer <<= n; | 353 | if (n >= u7_bit_count) |
| 354 | self.bit_buffer = 0 | ||
| 355 | else | ||
| 356 | self.bit_buffer <<= n; | ||
| 354 | }, | 357 | }, |
| 355 | .Little => { | 358 | .Little => { |
| 356 | const value = (self.bit_buffer << shift) >> shift; | 359 | const value = (self.bit_buffer << shift) >> shift; |
| 357 | out_buffer = @as(Buf, value); | 360 | out_buffer = @as(Buf, value); |
| 358 | self.bit_buffer >>= n; | 361 | if (n >= u7_bit_count) |
| 362 | self.bit_buffer = 0 | ||
| 363 | else | ||
| 364 | self.bit_buffer >>= n; | ||
| 359 | }, | 365 | }, |
| 360 | } | 366 | } |
| 361 | self.bit_count -= n; | 367 | self.bit_count -= n; |
lib/std/mem.zig+7-1| ... | @@ -935,6 +935,9 @@ pub fn writeInt(comptime T: type, buffer: *[@divExact(T.bit_count, 8)]u8, value: | ... | @@ -935,6 +935,9 @@ pub fn writeInt(comptime T: type, buffer: *[@divExact(T.bit_count, 8)]u8, value: |
| 935 | pub fn writeIntSliceLittle(comptime T: type, buffer: []u8, value: T) void { | 935 | pub fn writeIntSliceLittle(comptime T: type, buffer: []u8, value: T) void { |
| 936 | assert(buffer.len >= @divExact(T.bit_count, 8)); | 936 | assert(buffer.len >= @divExact(T.bit_count, 8)); |
| 937 | 937 | ||
| 938 | if (T.bit_count == 0) | ||
| 939 | return set(u8, buffer, 0); | ||
| 940 | |||
| 938 | // TODO I want to call writeIntLittle here but comptime eval facilities aren't good enough | 941 | // TODO I want to call writeIntLittle here but comptime eval facilities aren't good enough |
| 939 | const uint = std.meta.IntType(false, T.bit_count); | 942 | const uint = std.meta.IntType(false, T.bit_count); |
| 940 | var bits = @truncate(uint, value); | 943 | var bits = @truncate(uint, value); |
| ... | @@ -952,6 +955,9 @@ pub fn writeIntSliceLittle(comptime T: type, buffer: []u8, value: T) void { | ... | @@ -952,6 +955,9 @@ pub fn writeIntSliceLittle(comptime T: type, buffer: []u8, value: T) void { |
| 952 | pub fn writeIntSliceBig(comptime T: type, buffer: []u8, value: T) void { | 955 | pub fn writeIntSliceBig(comptime T: type, buffer: []u8, value: T) void { |
| 953 | assert(buffer.len >= @divExact(T.bit_count, 8)); | 956 | assert(buffer.len >= @divExact(T.bit_count, 8)); |
| 954 | 957 | ||
| 958 | if (T.bit_count == 0) | ||
| 959 | return set(u8, buffer, 0); | ||
| 960 | |||
| 955 | // TODO I want to call writeIntBig here but comptime eval facilities aren't good enough | 961 | // TODO I want to call writeIntBig here but comptime eval facilities aren't good enough |
| 956 | const uint = std.meta.IntType(false, T.bit_count); | 962 | const uint = std.meta.IntType(false, T.bit_count); |
| 957 | var bits = @truncate(uint, value); | 963 | var bits = @truncate(uint, value); |
| ... | @@ -1821,7 +1827,7 @@ test "sliceAsBytes" { | ... | @@ -1821,7 +1827,7 @@ test "sliceAsBytes" { |
| 1821 | } | 1827 | } |
| 1822 | 1828 | ||
| 1823 | test "sliceAsBytes with sentinel slice" { | 1829 | test "sliceAsBytes with sentinel slice" { |
| 1824 | const empty_string:[:0]const u8 = ""; | 1830 | const empty_string: [:0]const u8 = ""; |
| 1825 | const bytes = sliceAsBytes(empty_string); | 1831 | const bytes = sliceAsBytes(empty_string); |
| 1826 | testing.expect(bytes.len == 0); | 1832 | testing.expect(bytes.len == 0); |
| 1827 | } | 1833 | } |
src/all_types.hpp+1| ... | @@ -1834,6 +1834,7 @@ enum PanicMsgId { | ... | @@ -1834,6 +1834,7 @@ enum PanicMsgId { |
| 1834 | PanicMsgIdBadNoAsyncCall, | 1834 | PanicMsgIdBadNoAsyncCall, |
| 1835 | PanicMsgIdResumeNotSuspendedFn, | 1835 | PanicMsgIdResumeNotSuspendedFn, |
| 1836 | PanicMsgIdBadSentinel, | 1836 | PanicMsgIdBadSentinel, |
| 1837 | PanicMsgIdShxTooBigRhs, | ||
| 1837 | 1838 | ||
| 1838 | PanicMsgIdCount, | 1839 | PanicMsgIdCount, |
| 1839 | }; | 1840 | }; |
src/codegen.cpp+32| ... | @@ -974,6 +974,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) { | ... | @@ -974,6 +974,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) { |
| 974 | return buf_create_from_str("resumed a non-suspended function"); | 974 | return buf_create_from_str("resumed a non-suspended function"); |
| 975 | case PanicMsgIdBadSentinel: | 975 | case PanicMsgIdBadSentinel: |
| 976 | return buf_create_from_str("sentinel mismatch"); | 976 | return buf_create_from_str("sentinel mismatch"); |
| 977 | case PanicMsgIdShxTooBigRhs: | ||
| 978 | return buf_create_from_str("shift amount is greater than the type size"); | ||
| 977 | } | 979 | } |
| 978 | zig_unreachable(); | 980 | zig_unreachable(); |
| 979 | } | 981 | } |
| ... | @@ -2841,6 +2843,26 @@ static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast | ... | @@ -2841,6 +2843,26 @@ static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2841 | 2843 | ||
| 2842 | } | 2844 | } |
| 2843 | 2845 | ||
| 2846 | static void gen_shift_rhs_check(CodeGen *g, ZigType *lhs_type, ZigType *rhs_type, LLVMValueRef value) { | ||
| 2847 | // We only check if the rhs value of the shift expression is greater or | ||
| 2848 | // equal to the number of bits of the lhs if it's not a power of two, | ||
| 2849 | // otherwise the check is useful as the allowed values are limited by the | ||
| 2850 | // operand type itself | ||
| 2851 | if (!is_power_of_2(lhs_type->data.integral.bit_count)) { | ||
| 2852 | LLVMValueRef bit_count_value = LLVMConstInt(get_llvm_type(g, rhs_type), | ||
| 2853 | lhs_type->data.integral.bit_count, false); | ||
| 2854 | LLVMValueRef less_than_bit = LLVMBuildICmp(g->builder, LLVMIntULT, value, bit_count_value, ""); | ||
| 2855 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "CheckFail"); | ||
| 2856 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "CheckOk"); | ||
| 2857 | LLVMBuildCondBr(g->builder, less_than_bit, ok_block, fail_block); | ||
| 2858 | |||
| 2859 | LLVMPositionBuilderAtEnd(g->builder, fail_block); | ||
| 2860 | gen_safety_crash(g, PanicMsgIdShxTooBigRhs); | ||
| 2861 | |||
| 2862 | LLVMPositionBuilderAtEnd(g->builder, ok_block); | ||
| 2863 | } | ||
| 2864 | } | ||
| 2865 | |||
| 2844 | static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutableGen *executable, | 2866 | static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutableGen *executable, |
| 2845 | IrInstGenBinOp *bin_op_instruction) | 2867 | IrInstGenBinOp *bin_op_instruction) |
| 2846 | { | 2868 | { |
| ... | @@ -2949,6 +2971,11 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutableGen *executable, | ... | @@ -2949,6 +2971,11 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutableGen *executable, |
| 2949 | { | 2971 | { |
| 2950 | assert(scalar_type->id == ZigTypeIdInt); | 2972 | assert(scalar_type->id == ZigTypeIdInt); |
| 2951 | LLVMValueRef op2_casted = gen_widen_or_shorten(g, false, op2->value->type, scalar_type, op2_value); | 2973 | LLVMValueRef op2_casted = gen_widen_or_shorten(g, false, op2->value->type, scalar_type, op2_value); |
| 2974 | |||
| 2975 | if (want_runtime_safety) { | ||
| 2976 | gen_shift_rhs_check(g, scalar_type, op2->value->type, op2_value); | ||
| 2977 | } | ||
| 2978 | |||
| 2952 | bool is_sloppy = (op_id == IrBinOpBitShiftLeftLossy); | 2979 | bool is_sloppy = (op_id == IrBinOpBitShiftLeftLossy); |
| 2953 | if (is_sloppy) { | 2980 | if (is_sloppy) { |
| 2954 | return LLVMBuildShl(g->builder, op1_value, op2_casted, ""); | 2981 | return LLVMBuildShl(g->builder, op1_value, op2_casted, ""); |
| ... | @@ -2965,6 +2992,11 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutableGen *executable, | ... | @@ -2965,6 +2992,11 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutableGen *executable, |
| 2965 | { | 2992 | { |
| 2966 | assert(scalar_type->id == ZigTypeIdInt); | 2993 | assert(scalar_type->id == ZigTypeIdInt); |
| 2967 | LLVMValueRef op2_casted = gen_widen_or_shorten(g, false, op2->value->type, scalar_type, op2_value); | 2994 | LLVMValueRef op2_casted = gen_widen_or_shorten(g, false, op2->value->type, scalar_type, op2_value); |
| 2995 | |||
| 2996 | if (want_runtime_safety) { | ||
| 2997 | gen_shift_rhs_check(g, scalar_type, op2->value->type, op2_value); | ||
| 2998 | } | ||
| 2999 | |||
| 2968 | bool is_sloppy = (op_id == IrBinOpBitShiftRightLossy); | 3000 | bool is_sloppy = (op_id == IrBinOpBitShiftRightLossy); |
| 2969 | if (is_sloppy) { | 3001 | if (is_sloppy) { |
| 2970 | if (scalar_type->data.integral.is_signed) { | 3002 | if (scalar_type->data.integral.is_signed) { |
src/ir.cpp+41-27| ... | @@ -16635,49 +16635,69 @@ static IrInstGen *ir_analyze_bit_shift(IrAnalyze *ira, IrInstSrcBinOp *bin_op_in | ... | @@ -16635,49 +16635,69 @@ static IrInstGen *ir_analyze_bit_shift(IrAnalyze *ira, IrInstSrcBinOp *bin_op_in |
| 16635 | IrInstGen *casted_op2; | 16635 | IrInstGen *casted_op2; |
| 16636 | IrBinOp op_id = bin_op_instruction->op_id; | 16636 | IrBinOp op_id = bin_op_instruction->op_id; |
| 16637 | if (op1->value->type->id == ZigTypeIdComptimeInt) { | 16637 | if (op1->value->type->id == ZigTypeIdComptimeInt) { |
| 16638 | // comptime_int has no finite bit width | ||
| 16638 | casted_op2 = op2; | 16639 | casted_op2 = op2; |
| 16639 | 16640 | ||
| 16640 | if (op_id == IrBinOpBitShiftLeftLossy) { | 16641 | if (op_id == IrBinOpBitShiftLeftLossy) { |
| 16641 | op_id = IrBinOpBitShiftLeftExact; | 16642 | op_id = IrBinOpBitShiftLeftExact; |
| 16642 | } | 16643 | } |
| 16643 | 16644 | ||
| 16644 | if (casted_op2->value->data.x_bigint.is_negative) { | 16645 | if (!instr_is_comptime(op2)) { |
| 16646 | ir_add_error(ira, &bin_op_instruction->base.base, | ||
| 16647 | buf_sprintf("LHS of shift must be an integer type, or RHS must be compile-time known")); | ||
| 16648 | return ira->codegen->invalid_inst_gen; | ||
| 16649 | } | ||
| 16650 | |||
| 16651 | ZigValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); | ||
| 16652 | if (op2_val == nullptr) | ||
| 16653 | return ira->codegen->invalid_inst_gen; | ||
| 16654 | |||
| 16655 | if (op2_val->data.x_bigint.is_negative) { | ||
| 16645 | Buf *val_buf = buf_alloc(); | 16656 | Buf *val_buf = buf_alloc(); |
| 16646 | bigint_append_buf(val_buf, &casted_op2->value->data.x_bigint, 10); | 16657 | bigint_append_buf(val_buf, &op2_val->data.x_bigint, 10); |
| 16647 | ir_add_error(ira, &casted_op2->base, buf_sprintf("shift by negative value %s", buf_ptr(val_buf))); | 16658 | ir_add_error(ira, &casted_op2->base, |
| 16659 | buf_sprintf("shift by negative value %s", buf_ptr(val_buf))); | ||
| 16648 | return ira->codegen->invalid_inst_gen; | 16660 | return ira->codegen->invalid_inst_gen; |
| 16649 | } | 16661 | } |
| 16650 | } else { | 16662 | } else { |
| 16663 | const unsigned bit_count = op1->value->type->data.integral.bit_count; | ||
| 16651 | ZigType *shift_amt_type = get_smallest_unsigned_int_type(ira->codegen, | 16664 | ZigType *shift_amt_type = get_smallest_unsigned_int_type(ira->codegen, |
| 16652 | op1->value->type->data.integral.bit_count - 1); | 16665 | bit_count > 0 ? bit_count - 1 : 0); |
| 16653 | if (bin_op_instruction->op_id == IrBinOpBitShiftLeftLossy && | ||
| 16654 | op2->value->type->id == ZigTypeIdComptimeInt) { | ||
| 16655 | 16666 | ||
| 16656 | ZigValue *op2_val = ir_resolve_const(ira, op2, UndefBad); | 16667 | casted_op2 = ir_implicit_cast(ira, op2, shift_amt_type); |
| 16668 | if (type_is_invalid(casted_op2->value->type)) | ||
| 16669 | return ira->codegen->invalid_inst_gen; | ||
| 16670 | |||
| 16671 | // This check is only valid iff op1 has at least one bit | ||
| 16672 | if (bit_count > 0 && instr_is_comptime(casted_op2)) { | ||
| 16673 | ZigValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); | ||
| 16657 | if (op2_val == nullptr) | 16674 | if (op2_val == nullptr) |
| 16658 | return ira->codegen->invalid_inst_gen; | 16675 | return ira->codegen->invalid_inst_gen; |
| 16659 | if (!bigint_fits_in_bits(&op2_val->data.x_bigint, | 16676 | |
| 16660 | shift_amt_type->data.integral.bit_count, | 16677 | BigInt bit_count_value = {0}; |
| 16661 | op2_val->data.x_bigint.is_negative)) { | 16678 | bigint_init_unsigned(&bit_count_value, bit_count); |
| 16662 | Buf *val_buf = buf_alloc(); | 16679 | |
| 16663 | bigint_append_buf(val_buf, &op2_val->data.x_bigint, 10); | 16680 | if (bigint_cmp(&op2_val->data.x_bigint, &bit_count_value) != CmpLT) { |
| 16664 | ErrorMsg* msg = ir_add_error(ira, | 16681 | ErrorMsg* msg = ir_add_error(ira, |
| 16665 | &bin_op_instruction->base.base, | 16682 | &bin_op_instruction->base.base, |
| 16666 | buf_sprintf("RHS of shift is too large for LHS type")); | 16683 | buf_sprintf("RHS of shift is too large for LHS type")); |
| 16667 | add_error_note( | 16684 | add_error_note(ira->codegen, msg, op1->base.source_node, |
| 16668 | ira->codegen, | 16685 | buf_sprintf("type %s has only %u bits", |
| 16669 | msg, | 16686 | buf_ptr(&op1->value->type->name), bit_count)); |
| 16670 | op2->base.source_node, | 16687 | |
| 16671 | buf_sprintf("value %s cannot fit into type %s", | ||
| 16672 | buf_ptr(val_buf), | ||
| 16673 | buf_ptr(&shift_amt_type->name))); | ||
| 16674 | return ira->codegen->invalid_inst_gen; | 16688 | return ira->codegen->invalid_inst_gen; |
| 16675 | } | 16689 | } |
| 16676 | } | 16690 | } |
| 16691 | } | ||
| 16677 | 16692 | ||
| 16678 | casted_op2 = ir_implicit_cast(ira, op2, shift_amt_type); | 16693 | // Fast path for zero RHS |
| 16679 | if (type_is_invalid(casted_op2->value->type)) | 16694 | if (instr_is_comptime(casted_op2)) { |
| 16695 | ZigValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); | ||
| 16696 | if (op2_val == nullptr) | ||
| 16680 | return ira->codegen->invalid_inst_gen; | 16697 | return ira->codegen->invalid_inst_gen; |
| 16698 | |||
| 16699 | if (bigint_cmp_zero(&op2_val->data.x_bigint) == CmpEQ) | ||
| 16700 | return ir_analyze_cast(ira, &bin_op_instruction->base.base, op1->value->type, op1); | ||
| 16681 | } | 16701 | } |
| 16682 | 16702 | ||
| 16683 | if (instr_is_comptime(op1) && instr_is_comptime(casted_op2)) { | 16703 | if (instr_is_comptime(op1) && instr_is_comptime(casted_op2)) { |
| ... | @@ -16690,12 +16710,6 @@ static IrInstGen *ir_analyze_bit_shift(IrAnalyze *ira, IrInstSrcBinOp *bin_op_in | ... | @@ -16690,12 +16710,6 @@ static IrInstGen *ir_analyze_bit_shift(IrAnalyze *ira, IrInstSrcBinOp *bin_op_in |
| 16690 | return ira->codegen->invalid_inst_gen; | 16710 | return ira->codegen->invalid_inst_gen; |
| 16691 | 16711 | ||
| 16692 | return ir_analyze_math_op(ira, &bin_op_instruction->base.base, op1->value->type, op1_val, op_id, op2_val); | 16712 | return ir_analyze_math_op(ira, &bin_op_instruction->base.base, op1->value->type, op1_val, op_id, op2_val); |
| 16693 | } else if (op1->value->type->id == ZigTypeIdComptimeInt) { | ||
| 16694 | ir_add_error(ira, &bin_op_instruction->base.base, | ||
| 16695 | buf_sprintf("LHS of shift must be an integer type, or RHS must be compile-time known")); | ||
| 16696 | return ira->codegen->invalid_inst_gen; | ||
| 16697 | } else if (instr_is_comptime(casted_op2) && bigint_cmp_zero(&casted_op2->value->data.x_bigint) == CmpEQ) { | ||
| 16698 | return ir_build_cast(ira, &bin_op_instruction->base.base, op1->value->type, op1, CastOpNoop); | ||
| 16699 | } | 16713 | } |
| 16700 | 16714 | ||
| 16701 | return ir_build_bin_op_gen(ira, &bin_op_instruction->base.base, op1->value->type, | 16715 | return ir_build_bin_op_gen(ira, &bin_op_instruction->base.base, op1->value->type, |
test/compile_errors.zig+33-2| ... | @@ -2,6 +2,38 @@ const tests = @import("tests.zig"); | ... | @@ -2,6 +2,38 @@ const tests = @import("tests.zig"); |
| 2 | const std = @import("std"); | 2 | const std = @import("std"); |
| 3 | 3 | ||
| 4 | pub fn addCases(cases: *tests.CompileErrorContext) void { | 4 | pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5 | cases.addTest("shift on type with non-power-of-two size", | ||
| 6 | \\export fn entry() void { | ||
| 7 | \\ const S = struct { | ||
| 8 | \\ fn a() void { | ||
| 9 | \\ var x: u24 = 42; | ||
| 10 | \\ _ = x >> 24; | ||
| 11 | \\ } | ||
| 12 | \\ fn b() void { | ||
| 13 | \\ var x: u24 = 42; | ||
| 14 | \\ _ = x << 24; | ||
| 15 | \\ } | ||
| 16 | \\ fn c() void { | ||
| 17 | \\ var x: u24 = 42; | ||
| 18 | \\ _ = @shlExact(x, 24); | ||
| 19 | \\ } | ||
| 20 | \\ fn d() void { | ||
| 21 | \\ var x: u24 = 42; | ||
| 22 | \\ _ = @shrExact(x, 24); | ||
| 23 | \\ } | ||
| 24 | \\ }; | ||
| 25 | \\ S.a(); | ||
| 26 | \\ S.b(); | ||
| 27 | \\ S.c(); | ||
| 28 | \\ S.d(); | ||
| 29 | \\} | ||
| 30 | , &[_][]const u8{ | ||
| 31 | "tmp.zig:5:19: error: RHS of shift is too large for LHS type", | ||
| 32 | "tmp.zig:9:19: error: RHS of shift is too large for LHS type", | ||
| 33 | "tmp.zig:13:17: error: RHS of shift is too large for LHS type", | ||
| 34 | "tmp.zig:17:17: error: RHS of shift is too large for LHS type", | ||
| 35 | }); | ||
| 36 | |||
| 5 | cases.addTest("combination of noasync and async", | 37 | cases.addTest("combination of noasync and async", |
| 6 | \\export fn entry() void { | 38 | \\export fn entry() void { |
| 7 | \\ noasync { | 39 | \\ noasync { |
| ... | @@ -4029,8 +4061,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { | ... | @@ -4029,8 +4061,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4029 | \\} | 4061 | \\} |
| 4030 | \\export fn entry() u16 { return f(); } | 4062 | \\export fn entry() u16 { return f(); } |
| 4031 | , &[_][]const u8{ | 4063 | , &[_][]const u8{ |
| 4032 | "tmp.zig:3:14: error: RHS of shift is too large for LHS type", | 4064 | "tmp.zig:3:17: error: integer value 8 cannot be coerced to type 'u3'", |
| 4033 | "tmp.zig:3:17: note: value 8 cannot fit into type u3", | ||
| 4034 | }); | 4065 | }); |
| 4035 | 4066 | ||
| 4036 | cases.add("missing function call param", | 4067 | cases.add("missing function call param", |
test/runtime_safety.zig+31| ... | @@ -1,6 +1,37 @@ | ... | @@ -1,6 +1,37 @@ |
| 1 | const tests = @import("tests.zig"); | 1 | const tests = @import("tests.zig"); |
| 2 | 2 | ||
| 3 | pub fn addCases(cases: *tests.CompareOutputContext) void { | 3 | pub fn addCases(cases: *tests.CompareOutputContext) void { |
| 4 | cases.addRuntimeSafety("shift left by huge amount", | ||
| 5 | \\const std = @import("std"); | ||
| 6 | \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { | ||
| 7 | \\ std.debug.warn("{}\n", .{message}); | ||
| 8 | \\ if (std.mem.eql(u8, message, "shift amount is greater than the type size")) { | ||
| 9 | \\ std.process.exit(126); // good | ||
| 10 | \\ } | ||
| 11 | \\ std.process.exit(0); // test failed | ||
| 12 | \\} | ||
| 13 | \\pub fn main() void { | ||
| 14 | \\ var x: u24 = 42; | ||
| 15 | \\ var y: u5 = 24; | ||
| 16 | \\ var z = x >> y; | ||
| 17 | \\} | ||
| 18 | ); | ||
| 19 | |||
| 20 | cases.addRuntimeSafety("shift right by huge amount", | ||
| 21 | \\const std = @import("std"); | ||
| 22 | \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { | ||
| 23 | \\ if (std.mem.eql(u8, message, "shift amount is greater than the type size")) { | ||
| 24 | \\ std.process.exit(126); // good | ||
| 25 | \\ } | ||
| 26 | \\ std.process.exit(0); // test failed | ||
| 27 | \\} | ||
| 28 | \\pub fn main() void { | ||
| 29 | \\ var x: u24 = 42; | ||
| 30 | \\ var y: u5 = 24; | ||
| 31 | \\ var z = x << y; | ||
| 32 | \\} | ||
| 33 | ); | ||
| 34 | |||
| 4 | cases.addRuntimeSafety("slice sentinel mismatch - optional pointers", | 35 | cases.addRuntimeSafety("slice sentinel mismatch - optional pointers", |
| 5 | \\const std = @import("std"); | 36 | \\const std = @import("std"); |
| 6 | \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { | 37 | \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { |
test/stage1/behavior/math.zig+19| ... | @@ -453,6 +453,25 @@ fn testShrExact(x: u8) void { | ... | @@ -453,6 +453,25 @@ fn testShrExact(x: u8) void { |
| 453 | expect(shifted == 0b00101101); | 453 | expect(shifted == 0b00101101); |
| 454 | } | 454 | } |
| 455 | 455 | ||
| 456 | test "shift left/right on u0 operand" { | ||
| 457 | const S = struct { | ||
| 458 | fn doTheTest() void { | ||
| 459 | var x: u0 = 0; | ||
| 460 | var y: u0 = 0; | ||
| 461 | expectEqual(@as(u0, 0), x << 0); | ||
| 462 | expectEqual(@as(u0, 0), x >> 0); | ||
| 463 | expectEqual(@as(u0, 0), x << y); | ||
| 464 | expectEqual(@as(u0, 0), x >> y); | ||
| 465 | expectEqual(@as(u0, 0), @shlExact(x, 0)); | ||
| 466 | expectEqual(@as(u0, 0), @shrExact(x, 0)); | ||
| 467 | expectEqual(@as(u0, 0), @shlExact(x, y)); | ||
| 468 | expectEqual(@as(u0, 0), @shrExact(x, y)); | ||
| 469 | } | ||
| 470 | }; | ||
| 471 | S.doTheTest(); | ||
| 472 | comptime S.doTheTest(); | ||
| 473 | } | ||
| 474 | |||
| 456 | test "comptime_int addition" { | 475 | test "comptime_int addition" { |
| 457 | comptime { | 476 | comptime { |
| 458 | expect(35361831660712422535336160538497375248 + 101752735581729509668353361206450473702 == 137114567242441932203689521744947848950); | 477 | expect(35361831660712422535336160538497375248 + 101752735581729509668353361206450473702 == 137114567242441932203689521744947848950); |