authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-05-08 17:39:00-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-05-08 17:39:00-04:00
log0099583bd3eccbecbb827edbde46a40cf821fecf
treecb52a8242e85f3afd3343687efd79d2d1182bb97
parent50bbb34594eedf7a978c00edb525bcea472b554b
signature Commit is signed but in an unrecognized format.

C pointers support .? operator

see #1967

6 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};
22972298
2298struct IrInstruction {2299struct IrInstruction {
...@@ -3482,6 +3483,12 @@ struct IrInstructionAssertZero {...@@ -3482,6 +3483,12 @@ struct IrInstructionAssertZero {
3482 IrInstruction *target;3483 IrInstruction *target;
3483};3484};
34843485
3486struct IrInstructionAssertNonNull {
3487 IrInstruction base;
3488
3489 IrInstruction *target;
3490};
3491
3485static const size_t slice_ptr_index = 0;3492static const size_t slice_ptr_index = 0;
3486static const size_t slice_len_index = 1;3493static const size_t slice_len_index = 1;
34873494
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}
10101010
1011// TODO update most callsites to call gen_assertion instead of this
1011static void gen_safety_crash(CodeGen *g, PanicMsgId msg_id) {1012static 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}
10141015
1016static 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
1015static LLVMValueRef get_stacksave_fn_val(CodeGen *g) {1024static 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);
40624071
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}
54895498
5499static 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
5490static void set_debug_location(CodeGen *g, IrInstruction *instruction) {5524static 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}
10051005
1006static constexpr IrInstructionId ir_instruction_id(IrInstructionAssertNonNull *) {
1007 return IrInstructionIdAssertNonNull;
1008}
1009
1006template<typename T>1010template<typename T>
1007static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {1011static 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}
30393043
3044static 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
3040static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {3057static 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;
1687116888
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}
10051005
1006static 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
1006static void ir_print_resize_slice(IrPrint *irp, IrInstructionResizeSlice *instruction) {1012static 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 @@
1const tests = @import("tests.zig");1const tests = @import("tests.zig");
22
3pub fn addCases(cases: *tests.CompareOutputContext) void {3pub 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));
160160
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));
166166
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);
173174
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}