| author | |
| committer | |
| log | 0099583bd3eccbecbb827edbde46a40cf821fecf |
| tree | cb52a8242e85f3afd3343687efd79d2d1182bb97 |
| parent | 50bbb34594eedf7a978c00edb525bcea472b554b |
| signature |
see #19676 files changed, 132 insertions(+), 13 deletions(-)
src/all_types.hpp+7| ... | @@ -2293,6 +2293,7 @@ enum IrInstructionId { | ... | @@ -2293,6 +2293,7 @@ enum IrInstructionId { |
| 2293 | IrInstructionIdVectorToArray, | 2293 | IrInstructionIdVectorToArray, |
| 2294 | IrInstructionIdArrayToVector, | 2294 | IrInstructionIdArrayToVector, |
| 2295 | IrInstructionIdAssertZero, | 2295 | IrInstructionIdAssertZero, |
| 2296 | IrInstructionIdAssertNonNull, | ||
| 2296 | }; | 2297 | }; |
| 2297 | 2298 | ||
| 2298 | struct IrInstruction { | 2299 | struct IrInstruction { |
| ... | @@ -3482,6 +3483,12 @@ struct IrInstructionAssertZero { | ... | @@ -3482,6 +3483,12 @@ struct IrInstructionAssertZero { |
| 3482 | IrInstruction *target; | 3483 | IrInstruction *target; |
| 3483 | }; | 3484 | }; |
| 3484 | 3485 | ||
| 3486 | struct IrInstructionAssertNonNull { | ||
| 3487 | IrInstruction base; | ||
| 3488 | |||
| 3489 | IrInstruction *target; | ||
| 3490 | }; | ||
| 3491 | |||
| 3485 | static const size_t slice_ptr_index = 0; | 3492 | static const size_t slice_ptr_index = 0; |
| 3486 | static const size_t slice_len_index = 1; | 3493 | static const size_t slice_len_index = 1; |
| 3487 | 3494 |
src/codegen.cpp+37-1| ... | @@ -1008,10 +1008,19 @@ static void gen_panic(CodeGen *g, LLVMValueRef msg_arg, LLVMValueRef stack_trace | ... | @@ -1008,10 +1008,19 @@ static void gen_panic(CodeGen *g, LLVMValueRef msg_arg, LLVMValueRef stack_trace |
| 1008 | LLVMBuildUnreachable(g->builder); | 1008 | LLVMBuildUnreachable(g->builder); |
| 1009 | } | 1009 | } |
| 1010 | 1010 | ||
| 1011 | // TODO update most callsites to call gen_assertion instead of this | ||
| 1011 | static void gen_safety_crash(CodeGen *g, PanicMsgId msg_id) { | 1012 | static void gen_safety_crash(CodeGen *g, PanicMsgId msg_id) { |
| 1012 | gen_panic(g, get_panic_msg_ptr_val(g, msg_id), nullptr); | 1013 | gen_panic(g, get_panic_msg_ptr_val(g, msg_id), nullptr); |
| 1013 | } | 1014 | } |
| 1014 | 1015 | ||
| 1016 | static void gen_assertion(CodeGen *g, PanicMsgId msg_id, IrInstruction *source_instruction) { | ||
| 1017 | if (ir_want_runtime_safety(g, source_instruction)) { | ||
| 1018 | gen_safety_crash(g, msg_id); | ||
| 1019 | } else { | ||
| 1020 | LLVMBuildUnreachable(g->builder); | ||
| 1021 | } | ||
| 1022 | } | ||
| 1023 | |||
| 1015 | static LLVMValueRef get_stacksave_fn_val(CodeGen *g) { | 1024 | static LLVMValueRef get_stacksave_fn_val(CodeGen *g) { |
| 1016 | if (g->stacksave_fn_val) | 1025 | if (g->stacksave_fn_val) |
| 1017 | return g->stacksave_fn_val; | 1026 | return g->stacksave_fn_val; |
| ... | @@ -4056,8 +4065,8 @@ static LLVMValueRef ir_render_optional_unwrap_ptr(CodeGen *g, IrExecutable *exec | ... | @@ -4056,8 +4065,8 @@ static LLVMValueRef ir_render_optional_unwrap_ptr(CodeGen *g, IrExecutable *exec |
| 4056 | if (ir_want_runtime_safety(g, &instruction->base) && instruction->safety_check_on) { | 4065 | if (ir_want_runtime_safety(g, &instruction->base) && instruction->safety_check_on) { |
| 4057 | LLVMValueRef maybe_handle = get_handle_value(g, maybe_ptr, maybe_type, ptr_type); | 4066 | LLVMValueRef maybe_handle = get_handle_value(g, maybe_ptr, maybe_type, ptr_type); |
| 4058 | LLVMValueRef non_null_bit = gen_non_null_bit(g, maybe_type, maybe_handle); | 4067 | LLVMValueRef non_null_bit = gen_non_null_bit(g, maybe_type, maybe_handle); |
| 4059 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapOptionalOk"); | ||
| 4060 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapOptionalFail"); | 4068 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapOptionalFail"); |
| 4069 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapOptionalOk"); | ||
| 4061 | LLVMBuildCondBr(g->builder, non_null_bit, ok_block, fail_block); | 4070 | LLVMBuildCondBr(g->builder, non_null_bit, ok_block, fail_block); |
| 4062 | 4071 | ||
| 4063 | LLVMPositionBuilderAtEnd(g->builder, fail_block); | 4072 | LLVMPositionBuilderAtEnd(g->builder, fail_block); |
| ... | @@ -5487,6 +5496,31 @@ static LLVMValueRef ir_render_assert_zero(CodeGen *g, IrExecutable *executable, | ... | @@ -5487,6 +5496,31 @@ static LLVMValueRef ir_render_assert_zero(CodeGen *g, IrExecutable *executable, |
| 5487 | return nullptr; | 5496 | return nullptr; |
| 5488 | } | 5497 | } |
| 5489 | 5498 | ||
| 5499 | static LLVMValueRef ir_render_assert_non_null(CodeGen *g, IrExecutable *executable, | ||
| 5500 | IrInstructionAssertNonNull *instruction) | ||
| 5501 | { | ||
| 5502 | LLVMValueRef target = ir_llvm_value(g, instruction->target); | ||
| 5503 | ZigType *target_type = instruction->target->value.type; | ||
| 5504 | |||
| 5505 | if (target_type->id == ZigTypeIdPointer) { | ||
| 5506 | assert(target_type->data.pointer.ptr_len == PtrLenC); | ||
| 5507 | LLVMValueRef non_null_bit = LLVMBuildICmp(g->builder, LLVMIntNE, target, | ||
| 5508 | LLVMConstNull(get_llvm_type(g, target_type)), ""); | ||
| 5509 | |||
| 5510 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "AssertNonNullFail"); | ||
| 5511 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "AssertNonNullOk"); | ||
| 5512 | LLVMBuildCondBr(g->builder, non_null_bit, ok_block, fail_block); | ||
| 5513 | |||
| 5514 | LLVMPositionBuilderAtEnd(g->builder, fail_block); | ||
| 5515 | gen_assertion(g, PanicMsgIdUnwrapOptionalFail, &instruction->base); | ||
| 5516 | |||
| 5517 | LLVMPositionBuilderAtEnd(g->builder, ok_block); | ||
| 5518 | } else { | ||
| 5519 | zig_unreachable(); | ||
| 5520 | } | ||
| 5521 | return nullptr; | ||
| 5522 | } | ||
| 5523 | |||
| 5490 | static void set_debug_location(CodeGen *g, IrInstruction *instruction) { | 5524 | static void set_debug_location(CodeGen *g, IrInstruction *instruction) { |
| 5491 | AstNode *source_node = instruction->source_node; | 5525 | AstNode *source_node = instruction->source_node; |
| 5492 | Scope *scope = instruction->scope; | 5526 | Scope *scope = instruction->scope; |
| ... | @@ -5741,6 +5775,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, | ... | @@ -5741,6 +5775,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, |
| 5741 | return ir_render_vector_to_array(g, executable, (IrInstructionVectorToArray *)instruction); | 5775 | return ir_render_vector_to_array(g, executable, (IrInstructionVectorToArray *)instruction); |
| 5742 | case IrInstructionIdAssertZero: | 5776 | case IrInstructionIdAssertZero: |
| 5743 | return ir_render_assert_zero(g, executable, (IrInstructionAssertZero *)instruction); | 5777 | return ir_render_assert_zero(g, executable, (IrInstructionAssertZero *)instruction); |
| 5778 | case IrInstructionIdAssertNonNull: | ||
| 5779 | return ir_render_assert_non_null(g, executable, (IrInstructionAssertNonNull *)instruction); | ||
| 5744 | case IrInstructionIdResizeSlice: | 5780 | case IrInstructionIdResizeSlice: |
| 5745 | return ir_render_resize_slice(g, executable, (IrInstructionResizeSlice *)instruction); | 5781 | return ir_render_resize_slice(g, executable, (IrInstructionResizeSlice *)instruction); |
| 5746 | } | 5782 | } |
src/ir.cpp+49-4| ... | @@ -1003,6 +1003,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionAssertZero *) { | ... | @@ -1003,6 +1003,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionAssertZero *) { |
| 1003 | return IrInstructionIdAssertZero; | 1003 | return IrInstructionIdAssertZero; |
| 1004 | } | 1004 | } |
| 1005 | 1005 | ||
| 1006 | static constexpr IrInstructionId ir_instruction_id(IrInstructionAssertNonNull *) { | ||
| 1007 | return IrInstructionIdAssertNonNull; | ||
| 1008 | } | ||
| 1009 | |||
| 1006 | template<typename T> | 1010 | template<typename T> |
| 1007 | static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { | 1011 | static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { |
| 1008 | T *special_instruction = allocate<T>(1); | 1012 | T *special_instruction = allocate<T>(1); |
| ... | @@ -3037,6 +3041,19 @@ static IrInstruction *ir_build_assert_zero(IrAnalyze *ira, IrInstruction *source | ... | @@ -3037,6 +3041,19 @@ static IrInstruction *ir_build_assert_zero(IrAnalyze *ira, IrInstruction *source |
| 3037 | return &instruction->base; | 3041 | return &instruction->base; |
| 3038 | } | 3042 | } |
| 3039 | 3043 | ||
| 3044 | static IrInstruction *ir_build_assert_non_null(IrAnalyze *ira, IrInstruction *source_instruction, | ||
| 3045 | IrInstruction *target) | ||
| 3046 | { | ||
| 3047 | IrInstructionAssertNonNull *instruction = ir_build_instruction<IrInstructionAssertNonNull>(&ira->new_irb, | ||
| 3048 | source_instruction->scope, source_instruction->source_node); | ||
| 3049 | instruction->base.value.type = ira->codegen->builtin_types.entry_void; | ||
| 3050 | instruction->target = target; | ||
| 3051 | |||
| 3052 | ir_ref_instruction(target, ira->new_irb.current_basic_block); | ||
| 3053 | |||
| 3054 | return &instruction->base; | ||
| 3055 | } | ||
| 3056 | |||
| 3040 | static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) { | 3057 | static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) { |
| 3041 | results[ReturnKindUnconditional] = 0; | 3058 | results[ReturnKindUnconditional] = 0; |
| 3042 | results[ReturnKindError] = 0; | 3059 | results[ReturnKindError] = 0; |
| ... | @@ -16869,6 +16886,32 @@ static IrInstruction *ir_analyze_unwrap_optional_payload(IrAnalyze *ira, IrInstr | ... | @@ -16869,6 +16886,32 @@ static IrInstruction *ir_analyze_unwrap_optional_payload(IrAnalyze *ira, IrInstr |
| 16869 | if (type_is_invalid(type_entry)) | 16886 | if (type_is_invalid(type_entry)) |
| 16870 | return ira->codegen->invalid_instruction; | 16887 | return ira->codegen->invalid_instruction; |
| 16871 | 16888 | ||
| 16889 | if (type_entry->id == ZigTypeIdPointer && type_entry->data.pointer.ptr_len == PtrLenC) { | ||
| 16890 | if (instr_is_comptime(base_ptr)) { | ||
| 16891 | ConstExprValue *val = ir_resolve_const(ira, base_ptr, UndefBad); | ||
| 16892 | if (!val) | ||
| 16893 | return ira->codegen->invalid_instruction; | ||
| 16894 | if (val->data.x_ptr.mut != ConstPtrMutRuntimeVar) { | ||
| 16895 | ConstExprValue *c_ptr_val = const_ptr_pointee(ira, ira->codegen, val, source_instr->source_node); | ||
| 16896 | if (c_ptr_val == nullptr) | ||
| 16897 | return ira->codegen->invalid_instruction; | ||
| 16898 | bool is_null = c_ptr_val->data.x_ptr.special == ConstPtrSpecialNull || | ||
| 16899 | (c_ptr_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr && | ||
| 16900 | c_ptr_val->data.x_ptr.data.hard_coded_addr.addr == 0); | ||
| 16901 | if (is_null) { | ||
| 16902 | ir_add_error(ira, source_instr, buf_sprintf("unable to unwrap null")); | ||
| 16903 | return ira->codegen->invalid_instruction; | ||
| 16904 | } | ||
| 16905 | return base_ptr; | ||
| 16906 | } | ||
| 16907 | } | ||
| 16908 | if (!safety_check_on) | ||
| 16909 | return base_ptr; | ||
| 16910 | IrInstruction *c_ptr_val = ir_get_deref(ira, source_instr, base_ptr); | ||
| 16911 | ir_build_assert_non_null(ira, source_instr, c_ptr_val); | ||
| 16912 | return base_ptr; | ||
| 16913 | } | ||
| 16914 | |||
| 16872 | if (type_entry->id != ZigTypeIdOptional) { | 16915 | if (type_entry->id != ZigTypeIdOptional) { |
| 16873 | ir_add_error_node(ira, base_ptr->source_node, | 16916 | ir_add_error_node(ira, base_ptr->source_node, |
| 16874 | buf_sprintf("expected optional type, found '%s'", buf_ptr(&type_entry->name))); | 16917 | buf_sprintf("expected optional type, found '%s'", buf_ptr(&type_entry->name))); |
| ... | @@ -16883,11 +16926,11 @@ static IrInstruction *ir_analyze_unwrap_optional_payload(IrAnalyze *ira, IrInstr | ... | @@ -16883,11 +16926,11 @@ static IrInstruction *ir_analyze_unwrap_optional_payload(IrAnalyze *ira, IrInstr |
| 16883 | ConstExprValue *val = ir_resolve_const(ira, base_ptr, UndefBad); | 16926 | ConstExprValue *val = ir_resolve_const(ira, base_ptr, UndefBad); |
| 16884 | if (!val) | 16927 | if (!val) |
| 16885 | return ira->codegen->invalid_instruction; | 16928 | return ira->codegen->invalid_instruction; |
| 16886 | ConstExprValue *maybe_val = const_ptr_pointee(ira, ira->codegen, val, source_instr->source_node); | ||
| 16887 | if (maybe_val == nullptr) | ||
| 16888 | return ira->codegen->invalid_instruction; | ||
| 16889 | |||
| 16890 | if (val->data.x_ptr.mut != ConstPtrMutRuntimeVar) { | 16929 | if (val->data.x_ptr.mut != ConstPtrMutRuntimeVar) { |
| 16930 | ConstExprValue *maybe_val = const_ptr_pointee(ira, ira->codegen, val, source_instr->source_node); | ||
| 16931 | if (maybe_val == nullptr) | ||
| 16932 | return ira->codegen->invalid_instruction; | ||
| 16933 | |||
| 16891 | if (optional_value_is_null(maybe_val)) { | 16934 | if (optional_value_is_null(maybe_val)) { |
| 16892 | ir_add_error(ira, source_instr, buf_sprintf("unable to unwrap null")); | 16935 | ir_add_error(ira, source_instr, buf_sprintf("unable to unwrap null")); |
| 16893 | return ira->codegen->invalid_instruction; | 16936 | return ira->codegen->invalid_instruction; |
| ... | @@ -22942,6 +22985,7 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio | ... | @@ -22942,6 +22985,7 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio |
| 22942 | case IrInstructionIdArrayToVector: | 22985 | case IrInstructionIdArrayToVector: |
| 22943 | case IrInstructionIdVectorToArray: | 22986 | case IrInstructionIdVectorToArray: |
| 22944 | case IrInstructionIdAssertZero: | 22987 | case IrInstructionIdAssertZero: |
| 22988 | case IrInstructionIdAssertNonNull: | ||
| 22945 | case IrInstructionIdResizeSlice: | 22989 | case IrInstructionIdResizeSlice: |
| 22946 | case IrInstructionIdLoadPtrGen: | 22990 | case IrInstructionIdLoadPtrGen: |
| 22947 | case IrInstructionIdBitCastGen: | 22991 | case IrInstructionIdBitCastGen: |
| ... | @@ -23346,6 +23390,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { | ... | @@ -23346,6 +23390,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { |
| 23346 | case IrInstructionIdCmpxchgGen: | 23390 | case IrInstructionIdCmpxchgGen: |
| 23347 | case IrInstructionIdCmpxchgSrc: | 23391 | case IrInstructionIdCmpxchgSrc: |
| 23348 | case IrInstructionIdAssertZero: | 23392 | case IrInstructionIdAssertZero: |
| 23393 | case IrInstructionIdAssertNonNull: | ||
| 23349 | case IrInstructionIdResizeSlice: | 23394 | case IrInstructionIdResizeSlice: |
| 23350 | case IrInstructionIdGlobalAsm: | 23395 | case IrInstructionIdGlobalAsm: |
| 23351 | return true; | 23396 | return true; |
src/ir_print.cpp+9| ... | @@ -1003,6 +1003,12 @@ static void ir_print_assert_zero(IrPrint *irp, IrInstructionAssertZero *instruct | ... | @@ -1003,6 +1003,12 @@ static void ir_print_assert_zero(IrPrint *irp, IrInstructionAssertZero *instruct |
| 1003 | fprintf(irp->f, ")"); | 1003 | fprintf(irp->f, ")"); |
| 1004 | } | 1004 | } |
| 1005 | 1005 | ||
| 1006 | static void ir_print_assert_non_null(IrPrint *irp, IrInstructionAssertNonNull *instruction) { | ||
| 1007 | fprintf(irp->f, "AssertNonNull("); | ||
| 1008 | ir_print_other_instruction(irp, instruction->target); | ||
| 1009 | fprintf(irp->f, ")"); | ||
| 1010 | } | ||
| 1011 | |||
| 1006 | static void ir_print_resize_slice(IrPrint *irp, IrInstructionResizeSlice *instruction) { | 1012 | static void ir_print_resize_slice(IrPrint *irp, IrInstructionResizeSlice *instruction) { |
| 1007 | fprintf(irp->f, "@resizeSlice("); | 1013 | fprintf(irp->f, "@resizeSlice("); |
| 1008 | ir_print_other_instruction(irp, instruction->operand); | 1014 | ir_print_other_instruction(irp, instruction->operand); |
| ... | @@ -1880,6 +1886,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { | ... | @@ -1880,6 +1886,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 1880 | case IrInstructionIdAssertZero: | 1886 | case IrInstructionIdAssertZero: |
| 1881 | ir_print_assert_zero(irp, (IrInstructionAssertZero *)instruction); | 1887 | ir_print_assert_zero(irp, (IrInstructionAssertZero *)instruction); |
| 1882 | break; | 1888 | break; |
| 1889 | case IrInstructionIdAssertNonNull: | ||
| 1890 | ir_print_assert_non_null(irp, (IrInstructionAssertNonNull *)instruction); | ||
| 1891 | break; | ||
| 1883 | case IrInstructionIdResizeSlice: | 1892 | case IrInstructionIdResizeSlice: |
| 1884 | ir_print_resize_slice(irp, (IrInstructionResizeSlice *)instruction); | 1893 | ir_print_resize_slice(irp, (IrInstructionResizeSlice *)instruction); |
| 1885 | break; | 1894 | break; |
test/runtime_safety.zig+20| ... | @@ -1,6 +1,26 @@ | ... | @@ -1,6 +1,26 @@ |
| 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(".? operator on null pointer", | ||
| 5 | \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { | ||
| 6 | \\ @import("std").os.exit(126); | ||
| 7 | \\} | ||
| 8 | \\pub fn main() void { | ||
| 9 | \\ var ptr: ?*i32 = null; | ||
| 10 | \\ var b = ptr.?; | ||
| 11 | \\} | ||
| 12 | ); | ||
| 13 | |||
| 14 | cases.addRuntimeSafety(".? operator on C pointer", | ||
| 15 | \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { | ||
| 16 | \\ @import("std").os.exit(126); | ||
| 17 | \\} | ||
| 18 | \\pub fn main() void { | ||
| 19 | \\ var ptr: [*c]i32 = null; | ||
| 20 | \\ var b = ptr.?; | ||
| 21 | \\} | ||
| 22 | ); | ||
| 23 | |||
| 4 | cases.addRuntimeSafety("@ptrToInt address zero to non-optional pointer", | 24 | cases.addRuntimeSafety("@ptrToInt address zero to non-optional pointer", |
| 5 | \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { | 25 | \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { |
| 6 | \\ @import("std").os.exit(126); | 26 | \\ @import("std").os.exit(126); |
test/stage1/behavior/pointers.zig+10-8| ... | @@ -159,10 +159,10 @@ test "assign null directly to C pointer and test null equality" { | ... | @@ -159,10 +159,10 @@ test "assign null directly to C pointer and test null equality" { |
| 159 | expect(!(null != x)); | 159 | expect(!(null != x)); |
| 160 | 160 | ||
| 161 | const y: [*c]i32 = null; | 161 | const y: [*c]i32 = null; |
| 162 | expect(y == null); | 162 | comptime expect(y == null); |
| 163 | expect(null == y); | 163 | comptime expect(null == y); |
| 164 | expect(!(y != null)); | 164 | comptime expect(!(y != null)); |
| 165 | expect(!(null != y)); | 165 | comptime expect(!(null != y)); |
| 166 | 166 | ||
| 167 | var n: i32 = 1234; | 167 | var n: i32 = 1234; |
| 168 | var x1: [*c]i32 = &n; | 168 | var x1: [*c]i32 = &n; |
| ... | @@ -170,11 +170,13 @@ test "assign null directly to C pointer and test null equality" { | ... | @@ -170,11 +170,13 @@ test "assign null directly to C pointer and test null equality" { |
| 170 | expect(!(null == x1)); | 170 | expect(!(null == x1)); |
| 171 | expect(x1 != null); | 171 | expect(x1 != null); |
| 172 | expect(null != x1); | 172 | expect(null != x1); |
| 173 | expect(x1.?.* == 1234); | ||
| 173 | 174 | ||
| 174 | const nc: i32 = 1234; | 175 | const nc: i32 = 1234; |
| 175 | const y1: [*c]const i32 = &nc; | 176 | const y1: [*c]const i32 = &nc; |
| 176 | expect(!(y1 == null)); | 177 | comptime expect(!(y1 == null)); |
| 177 | expect(!(null == y1)); | 178 | comptime expect(!(null == y1)); |
| 178 | expect(y1 != null); | 179 | comptime expect(y1 != null); |
| 179 | expect(null != y1); | 180 | comptime expect(null != y1); |
| 181 | comptime expect(y1.?.* == 1234); | ||
| 180 | } | 182 | } |