authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-05-08 16:06:34-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-05-08 16:06:34-04:00
log50bbb34594eedf7a978c00edb525bcea472b554b
tree4936a28958fea277de9702b582bd13bc0ddeda83
parentbe7cacfbbec8aed234a0316d11a9ae0e8cda0286
signaturelock-open Commit is signed but in an unrecognized format.

C pointers support `null`

See #1967

4 files changed, 121 insertions(+), 28 deletions(-)

src/codegen.cpp+11-11
...@@ -4023,19 +4023,19 @@ static LLVMValueRef ir_render_asm(CodeGen *g, IrExecutable *executable, IrInstru...@@ -4023,19 +4023,19 @@ static LLVMValueRef ir_render_asm(CodeGen *g, IrExecutable *executable, IrInstru
4023}4023}
40244024
4025static LLVMValueRef gen_non_null_bit(CodeGen *g, ZigType *maybe_type, LLVMValueRef maybe_handle) {4025static LLVMValueRef gen_non_null_bit(CodeGen *g, ZigType *maybe_type, LLVMValueRef maybe_handle) {
4026 assert(maybe_type->id == ZigTypeIdOptional);4026 assert(maybe_type->id == ZigTypeIdOptional ||
4027 (maybe_type->id == ZigTypeIdPointer && maybe_type->data.pointer.allow_zero));
4028
4027 ZigType *child_type = maybe_type->data.maybe.child_type;4029 ZigType *child_type = maybe_type->data.maybe.child_type;
4028 if (!type_has_bits(child_type)) {4030 if (!type_has_bits(child_type))
4029 return maybe_handle;4031 return maybe_handle;
4030 } else {4032
4031 bool is_scalar = !handle_is_ptr(maybe_type);4033 bool is_scalar = !handle_is_ptr(maybe_type);
4032 if (is_scalar) {4034 if (is_scalar)
4033 return LLVMBuildICmp(g->builder, LLVMIntNE, maybe_handle, LLVMConstNull(get_llvm_type(g, maybe_type)), "");4035 return LLVMBuildICmp(g->builder, LLVMIntNE, maybe_handle, LLVMConstNull(get_llvm_type(g, maybe_type)), "");
4034 } else {4036
4035 LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_handle, maybe_null_index, "");4037 LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_handle, maybe_null_index, "");
4036 return gen_load_untyped(g, maybe_field_ptr, 0, false, "");4038 return gen_load_untyped(g, maybe_field_ptr, 0, false, "");
4037 }
4038 }
4039}4039}
40404040
4041static LLVMValueRef ir_render_test_non_null(CodeGen *g, IrExecutable *executable,4041static LLVMValueRef ir_render_test_non_null(CodeGen *g, IrExecutable *executable,
src/ir.cpp+81-16
...@@ -10283,6 +10283,12 @@ static IrInstruction *ir_const_bool(IrAnalyze *ira, IrInstruction *source_instru...@@ -10283,6 +10283,12 @@ static IrInstruction *ir_const_bool(IrAnalyze *ira, IrInstruction *source_instru
10283 return result;10283 return result;
10284}10284}
1028510285
10286static IrInstruction *ir_const_undef(IrAnalyze *ira, IrInstruction *source_instruction, ZigType *ty) {
10287 IrInstruction *result = ir_const(ira, source_instruction, ty);
10288 result->value.special = ConstValSpecialUndef;
10289 return result;
10290}
10291
10286static IrInstruction *ir_const_void(IrAnalyze *ira, IrInstruction *source_instruction) {10292static IrInstruction *ir_const_void(IrAnalyze *ira, IrInstruction *source_instruction) {
10287 return ir_const(ira, source_instruction, ira->codegen->builtin_types.entry_void);10293 return ir_const(ira, source_instruction, ira->codegen->builtin_types.entry_void);
10288}10294}
...@@ -10596,19 +10602,34 @@ static IrInstruction *ir_analyze_null_to_maybe(IrAnalyze *ira, IrInstruction *so...@@ -10596,19 +10602,34 @@ static IrInstruction *ir_analyze_null_to_maybe(IrAnalyze *ira, IrInstruction *so
10596 assert(instr_is_comptime(value));10602 assert(instr_is_comptime(value));
1059710603
10598 ConstExprValue *val = ir_resolve_const(ira, value, UndefBad);10604 ConstExprValue *val = ir_resolve_const(ira, value, UndefBad);
10599 assert(val);10605 assert(val != nullptr);
1060010606
10601 IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(&ira->new_irb, source_instr->scope, source_instr->source_node);10607 IrInstruction *result = ir_const(ira, source_instr, wanted_type);
10602 const_instruction->base.value.special = ConstValSpecialStatic;10608 result->value.special = ConstValSpecialStatic;
10603 if (get_codegen_ptr_type(wanted_type) != nullptr) {10609 if (get_codegen_ptr_type(wanted_type) != nullptr) {
10604 const_instruction->base.value.data.x_ptr.special = ConstPtrSpecialNull;10610 result->value.data.x_ptr.special = ConstPtrSpecialNull;
10605 } else if (is_opt_err_set(wanted_type)) {10611 } else if (is_opt_err_set(wanted_type)) {
10606 const_instruction->base.value.data.x_err_set = nullptr;10612 result->value.data.x_err_set = nullptr;
10607 } else {10613 } else {
10608 const_instruction->base.value.data.x_optional = nullptr;10614 result->value.data.x_optional = nullptr;
10609 }10615 }
10610 const_instruction->base.value.type = wanted_type;10616 return result;
10611 return &const_instruction->base;10617}
10618
10619static IrInstruction *ir_analyze_null_to_c_pointer(IrAnalyze *ira, IrInstruction *source_instr,
10620 IrInstruction *value, ZigType *wanted_type)
10621{
10622 assert(wanted_type->id == ZigTypeIdPointer);
10623 assert(wanted_type->data.pointer.ptr_len == PtrLenC);
10624 assert(instr_is_comptime(value));
10625
10626 ConstExprValue *val = ir_resolve_const(ira, value, UndefBad);
10627 assert(val != nullptr);
10628
10629 IrInstruction *result = ir_const(ira, source_instr, wanted_type);
10630 result->value.data.x_ptr.special = ConstPtrSpecialNull;
10631 result->value.data.x_ptr.mut = ConstPtrMutComptimeConst;
10632 return result;
10612}10633}
1061310634
10614static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *value,10635static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *value,
...@@ -11610,6 +11631,13 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst...@@ -11610,6 +11631,13 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
11610 return ir_analyze_null_to_maybe(ira, source_instr, value, wanted_type);11631 return ir_analyze_null_to_maybe(ira, source_instr, value, wanted_type);
11611 }11632 }
1161211633
11634 // cast from null literal to C pointer
11635 if (wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.ptr_len == PtrLenC &&
11636 actual_type->id == ZigTypeIdNull)
11637 {
11638 return ir_analyze_null_to_c_pointer(ira, source_instr, value, wanted_type);
11639 }
11640
11613 // cast from [N]T to E![]const T11641 // cast from [N]T to E![]const T
11614 if (wanted_type->id == ZigTypeIdErrorUnion &&11642 if (wanted_type->id == ZigTypeIdErrorUnion &&
11615 is_slice(wanted_type->data.error_union.payload_type) &&11643 is_slice(wanted_type->data.error_union.payload_type) &&
...@@ -12227,14 +12255,12 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *...@@ -12227,14 +12255,12 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *
1222712255
12228 IrBinOp op_id = bin_op_instruction->op_id;12256 IrBinOp op_id = bin_op_instruction->op_id;
12229 bool is_equality_cmp = (op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq);12257 bool is_equality_cmp = (op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq);
12230 if (is_equality_cmp &&12258 if (is_equality_cmp && op1->value.type->id == ZigTypeIdNull && op2->value.type->id == ZigTypeIdNull) {
12259 return ir_const_bool(ira, &bin_op_instruction->base, (op_id == IrBinOpCmpEq));
12260 } else if (is_equality_cmp &&
12231 ((op1->value.type->id == ZigTypeIdNull && op2->value.type->id == ZigTypeIdOptional) ||12261 ((op1->value.type->id == ZigTypeIdNull && op2->value.type->id == ZigTypeIdOptional) ||
12232 (op2->value.type->id == ZigTypeIdNull && op1->value.type->id == ZigTypeIdOptional) ||12262 (op2->value.type->id == ZigTypeIdNull && op1->value.type->id == ZigTypeIdOptional)))
12233 (op1->value.type->id == ZigTypeIdNull && op2->value.type->id == ZigTypeIdNull)))
12234 {12263 {
12235 if (op1->value.type->id == ZigTypeIdNull && op2->value.type->id == ZigTypeIdNull) {
12236 return ir_const_bool(ira, &bin_op_instruction->base, (op_id == IrBinOpCmpEq));
12237 }
12238 IrInstruction *maybe_op;12264 IrInstruction *maybe_op;
12239 if (op1->value.type->id == ZigTypeIdNull) {12265 if (op1->value.type->id == ZigTypeIdNull) {
12240 maybe_op = op2;12266 maybe_op = op2;
...@@ -12256,6 +12282,44 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *...@@ -12256,6 +12282,44 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *
12256 source_node, maybe_op);12282 source_node, maybe_op);
12257 is_non_null->value.type = ira->codegen->builtin_types.entry_bool;12283 is_non_null->value.type = ira->codegen->builtin_types.entry_bool;
1225812284
12285 if (op_id == IrBinOpCmpEq) {
12286 IrInstruction *result = ir_build_bool_not(&ira->new_irb, bin_op_instruction->base.scope,
12287 bin_op_instruction->base.source_node, is_non_null);
12288 result->value.type = ira->codegen->builtin_types.entry_bool;
12289 return result;
12290 } else {
12291 return is_non_null;
12292 }
12293 } else if (is_equality_cmp &&
12294 ((op1->value.type->id == ZigTypeIdNull && op2->value.type->id == ZigTypeIdPointer &&
12295 op2->value.type->data.pointer.ptr_len == PtrLenC) ||
12296 (op2->value.type->id == ZigTypeIdNull && op1->value.type->id == ZigTypeIdPointer &&
12297 op1->value.type->data.pointer.ptr_len == PtrLenC)))
12298 {
12299 IrInstruction *c_ptr_op;
12300 if (op1->value.type->id == ZigTypeIdNull) {
12301 c_ptr_op = op2;
12302 } else if (op2->value.type->id == ZigTypeIdNull) {
12303 c_ptr_op = op1;
12304 } else {
12305 zig_unreachable();
12306 }
12307 if (instr_is_comptime(c_ptr_op)) {
12308 ConstExprValue *c_ptr_val = ir_resolve_const(ira, c_ptr_op, UndefOk);
12309 if (!c_ptr_val)
12310 return ira->codegen->invalid_instruction;
12311 if (c_ptr_val->special == ConstValSpecialUndef)
12312 return ir_const_undef(ira, &bin_op_instruction->base, ira->codegen->builtin_types.entry_bool);
12313 bool is_null = c_ptr_val->data.x_ptr.special == ConstPtrSpecialNull ||
12314 (c_ptr_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr &&
12315 c_ptr_val->data.x_ptr.data.hard_coded_addr.addr == 0);
12316 bool bool_result = (op_id == IrBinOpCmpEq) ? is_null : !is_null;
12317 return ir_const_bool(ira, &bin_op_instruction->base, bool_result);
12318 }
12319 IrInstruction *is_non_null = ir_build_test_nonnull(&ira->new_irb, bin_op_instruction->base.scope,
12320 source_node, c_ptr_op);
12321 is_non_null->value.type = ira->codegen->builtin_types.entry_bool;
12322
12259 if (op_id == IrBinOpCmpEq) {12323 if (op_id == IrBinOpCmpEq) {
12260 IrInstruction *result = ir_build_bool_not(&ira->new_irb, bin_op_instruction->base.scope,12324 IrInstruction *result = ir_build_bool_not(&ira->new_irb, bin_op_instruction->base.scope,
12261 bin_op_instruction->base.source_node, is_non_null);12325 bin_op_instruction->base.source_node, is_non_null);
...@@ -12265,8 +12329,9 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *...@@ -12265,8 +12329,9 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *
12265 return is_non_null;12329 return is_non_null;
12266 }12330 }
12267 } else if (op1->value.type->id == ZigTypeIdNull || op2->value.type->id == ZigTypeIdNull) {12331 } else if (op1->value.type->id == ZigTypeIdNull || op2->value.type->id == ZigTypeIdNull) {
12268 ir_add_error_node(ira, source_node, buf_sprintf("only optionals (not '%s') can compare to null",12332 ZigType *non_null_type = (op1->value.type->id == ZigTypeIdNull) ? op2->value.type : op1->value.type;
12269 buf_ptr(&(op1->value.type->id == ZigTypeIdNull ? op2->value.type->name : op1->value.type->name))));12333 ir_add_error_node(ira, source_node, buf_sprintf("comparison of '%s' with null",
12334 buf_ptr(&non_null_type->name)));
12270 return ira->codegen->invalid_instruction;12335 return ira->codegen->invalid_instruction;
12271 }12336 }
1227212337
test/compile_errors.zig+1-1
...@@ -863,7 +863,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -863,7 +863,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
863 \\ _ = &x == null;863 \\ _ = &x == null;
864 \\}864 \\}
865 ,865 ,
866 "tmp.zig:3:12: error: only optionals (not '*i32') can compare to null",866 "tmp.zig:3:12: error: comparison of '*i32' with null",
867 );867 );
868868
869 cases.add(869 cases.add(
test/stage1/behavior/pointers.zig+28
...@@ -150,3 +150,31 @@ test "allowzero pointer and slice" {...@@ -150,3 +150,31 @@ test "allowzero pointer and slice" {
150 expect(@typeInfo(@typeOf(ptr)).Pointer.is_allowzero);150 expect(@typeInfo(@typeOf(ptr)).Pointer.is_allowzero);
151 expect(@typeInfo(@typeOf(slice)).Pointer.is_allowzero);151 expect(@typeInfo(@typeOf(slice)).Pointer.is_allowzero);
152}152}
153
154test "assign null directly to C pointer and test null equality" {
155 var x: [*c]i32 = null;
156 expect(x == null);
157 expect(null == x);
158 expect(!(x != null));
159 expect(!(null != x));
160
161 const y: [*c]i32 = null;
162 expect(y == null);
163 expect(null == y);
164 expect(!(y != null));
165 expect(!(null != y));
166
167 var n: i32 = 1234;
168 var x1: [*c]i32 = &n;
169 expect(!(x1 == null));
170 expect(!(null == x1));
171 expect(x1 != null);
172 expect(null != x1);
173
174 const nc: i32 = 1234;
175 const y1: [*c]const i32 = &nc;
176 expect(!(y1 == null));
177 expect(!(null == y1));
178 expect(y1 != null);
179 expect(null != y1);
180}