C pointers support `null`

See #1967
master
Andrew Kelley 2019-05-08 16:06:34 -04:00
parent be7cacfbbe
commit 50bbb34594
No known key found for this signature in database
GPG Key ID: 7C5F548F728501A9
4 changed files with 121 additions and 28 deletions

View File

@ -4023,20 +4023,20 @@ static LLVMValueRef ir_render_asm(CodeGen *g, IrExecutable *executable, IrInstru
} }
static LLVMValueRef gen_non_null_bit(CodeGen *g, ZigType *maybe_type, LLVMValueRef maybe_handle) { static LLVMValueRef gen_non_null_bit(CodeGen *g, ZigType *maybe_type, LLVMValueRef maybe_handle) {
assert(maybe_type->id == ZigTypeIdOptional); assert(maybe_type->id == ZigTypeIdOptional ||
(maybe_type->id == ZigTypeIdPointer && maybe_type->data.pointer.allow_zero));
ZigType *child_type = maybe_type->data.maybe.child_type; ZigType *child_type = maybe_type->data.maybe.child_type;
if (!type_has_bits(child_type)) { if (!type_has_bits(child_type))
return maybe_handle; return maybe_handle;
} else {
bool is_scalar = !handle_is_ptr(maybe_type); bool is_scalar = !handle_is_ptr(maybe_type);
if (is_scalar) { if (is_scalar)
return LLVMBuildICmp(g->builder, LLVMIntNE, maybe_handle, LLVMConstNull(get_llvm_type(g, maybe_type)), ""); return LLVMBuildICmp(g->builder, LLVMIntNE, maybe_handle, LLVMConstNull(get_llvm_type(g, maybe_type)), "");
} else {
LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_handle, maybe_null_index, ""); LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_handle, maybe_null_index, "");
return gen_load_untyped(g, maybe_field_ptr, 0, false, ""); return gen_load_untyped(g, maybe_field_ptr, 0, false, "");
} }
}
}
static LLVMValueRef ir_render_test_non_null(CodeGen *g, IrExecutable *executable, static LLVMValueRef ir_render_test_non_null(CodeGen *g, IrExecutable *executable,
IrInstructionTestNonNull *instruction) IrInstructionTestNonNull *instruction)

View File

@ -10283,6 +10283,12 @@ static IrInstruction *ir_const_bool(IrAnalyze *ira, IrInstruction *source_instru
return result; return result;
} }
static IrInstruction *ir_const_undef(IrAnalyze *ira, IrInstruction *source_instruction, ZigType *ty) {
IrInstruction *result = ir_const(ira, source_instruction, ty);
result->value.special = ConstValSpecialUndef;
return result;
}
static IrInstruction *ir_const_void(IrAnalyze *ira, IrInstruction *source_instruction) { static IrInstruction *ir_const_void(IrAnalyze *ira, IrInstruction *source_instruction) {
return ir_const(ira, source_instruction, ira->codegen->builtin_types.entry_void); return ir_const(ira, source_instruction, ira->codegen->builtin_types.entry_void);
} }
@ -10596,19 +10602,34 @@ static IrInstruction *ir_analyze_null_to_maybe(IrAnalyze *ira, IrInstruction *so
assert(instr_is_comptime(value)); assert(instr_is_comptime(value));
ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); ConstExprValue *val = ir_resolve_const(ira, value, UndefBad);
assert(val); assert(val != nullptr);
IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(&ira->new_irb, source_instr->scope, source_instr->source_node); IrInstruction *result = ir_const(ira, source_instr, wanted_type);
const_instruction->base.value.special = ConstValSpecialStatic; result->value.special = ConstValSpecialStatic;
if (get_codegen_ptr_type(wanted_type) != nullptr) { if (get_codegen_ptr_type(wanted_type) != nullptr) {
const_instruction->base.value.data.x_ptr.special = ConstPtrSpecialNull; result->value.data.x_ptr.special = ConstPtrSpecialNull;
} else if (is_opt_err_set(wanted_type)) { } else if (is_opt_err_set(wanted_type)) {
const_instruction->base.value.data.x_err_set = nullptr; result->value.data.x_err_set = nullptr;
} else { } else {
const_instruction->base.value.data.x_optional = nullptr; result->value.data.x_optional = nullptr;
} }
const_instruction->base.value.type = wanted_type; return result;
return &const_instruction->base; }
static IrInstruction *ir_analyze_null_to_c_pointer(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *value, ZigType *wanted_type)
{
assert(wanted_type->id == ZigTypeIdPointer);
assert(wanted_type->data.pointer.ptr_len == PtrLenC);
assert(instr_is_comptime(value));
ConstExprValue *val = ir_resolve_const(ira, value, UndefBad);
assert(val != nullptr);
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
result->value.data.x_ptr.special = ConstPtrSpecialNull;
result->value.data.x_ptr.mut = ConstPtrMutComptimeConst;
return result;
} }
static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *value, static 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
return ir_analyze_null_to_maybe(ira, source_instr, value, wanted_type); return ir_analyze_null_to_maybe(ira, source_instr, value, wanted_type);
} }
// cast from null literal to C pointer
if (wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.ptr_len == PtrLenC &&
actual_type->id == ZigTypeIdNull)
{
return ir_analyze_null_to_c_pointer(ira, source_instr, value, wanted_type);
}
// cast from [N]T to E![]const T // cast from [N]T to E![]const T
if (wanted_type->id == ZigTypeIdErrorUnion && if (wanted_type->id == ZigTypeIdErrorUnion &&
is_slice(wanted_type->data.error_union.payload_type) && is_slice(wanted_type->data.error_union.payload_type) &&
@ -12227,14 +12255,12 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *
IrBinOp op_id = bin_op_instruction->op_id; IrBinOp op_id = bin_op_instruction->op_id;
bool is_equality_cmp = (op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq); bool is_equality_cmp = (op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq);
if (is_equality_cmp && if (is_equality_cmp && op1->value.type->id == ZigTypeIdNull && op2->value.type->id == ZigTypeIdNull) {
((op1->value.type->id == ZigTypeIdNull && op2->value.type->id == ZigTypeIdOptional) ||
(op2->value.type->id == ZigTypeIdNull && op1->value.type->id == ZigTypeIdOptional) ||
(op1->value.type->id == ZigTypeIdNull && op2->value.type->id == ZigTypeIdNull)))
{
if (op1->value.type->id == ZigTypeIdNull && op2->value.type->id == ZigTypeIdNull) {
return ir_const_bool(ira, &bin_op_instruction->base, (op_id == IrBinOpCmpEq)); return ir_const_bool(ira, &bin_op_instruction->base, (op_id == IrBinOpCmpEq));
} } else if (is_equality_cmp &&
((op1->value.type->id == ZigTypeIdNull && op2->value.type->id == ZigTypeIdOptional) ||
(op2->value.type->id == ZigTypeIdNull && op1->value.type->id == ZigTypeIdOptional)))
{
IrInstruction *maybe_op; IrInstruction *maybe_op;
if (op1->value.type->id == ZigTypeIdNull) { if (op1->value.type->id == ZigTypeIdNull) {
maybe_op = op2; maybe_op = op2;
@ -12256,6 +12282,44 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *
source_node, maybe_op); source_node, maybe_op);
is_non_null->value.type = ira->codegen->builtin_types.entry_bool; is_non_null->value.type = ira->codegen->builtin_types.entry_bool;
if (op_id == IrBinOpCmpEq) {
IrInstruction *result = ir_build_bool_not(&ira->new_irb, bin_op_instruction->base.scope,
bin_op_instruction->base.source_node, is_non_null);
result->value.type = ira->codegen->builtin_types.entry_bool;
return result;
} else {
return is_non_null;
}
} else if (is_equality_cmp &&
((op1->value.type->id == ZigTypeIdNull && op2->value.type->id == ZigTypeIdPointer &&
op2->value.type->data.pointer.ptr_len == PtrLenC) ||
(op2->value.type->id == ZigTypeIdNull && op1->value.type->id == ZigTypeIdPointer &&
op1->value.type->data.pointer.ptr_len == PtrLenC)))
{
IrInstruction *c_ptr_op;
if (op1->value.type->id == ZigTypeIdNull) {
c_ptr_op = op2;
} else if (op2->value.type->id == ZigTypeIdNull) {
c_ptr_op = op1;
} else {
zig_unreachable();
}
if (instr_is_comptime(c_ptr_op)) {
ConstExprValue *c_ptr_val = ir_resolve_const(ira, c_ptr_op, UndefOk);
if (!c_ptr_val)
return ira->codegen->invalid_instruction;
if (c_ptr_val->special == ConstValSpecialUndef)
return ir_const_undef(ira, &bin_op_instruction->base, ira->codegen->builtin_types.entry_bool);
bool is_null = c_ptr_val->data.x_ptr.special == ConstPtrSpecialNull ||
(c_ptr_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr &&
c_ptr_val->data.x_ptr.data.hard_coded_addr.addr == 0);
bool bool_result = (op_id == IrBinOpCmpEq) ? is_null : !is_null;
return ir_const_bool(ira, &bin_op_instruction->base, bool_result);
}
IrInstruction *is_non_null = ir_build_test_nonnull(&ira->new_irb, bin_op_instruction->base.scope,
source_node, c_ptr_op);
is_non_null->value.type = ira->codegen->builtin_types.entry_bool;
if (op_id == IrBinOpCmpEq) { if (op_id == IrBinOpCmpEq) {
IrInstruction *result = ir_build_bool_not(&ira->new_irb, bin_op_instruction->base.scope, IrInstruction *result = ir_build_bool_not(&ira->new_irb, bin_op_instruction->base.scope,
bin_op_instruction->base.source_node, is_non_null); bin_op_instruction->base.source_node, is_non_null);
@ -12265,8 +12329,9 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *
return is_non_null; return is_non_null;
} }
} else if (op1->value.type->id == ZigTypeIdNull || op2->value.type->id == ZigTypeIdNull) { } else if (op1->value.type->id == ZigTypeIdNull || op2->value.type->id == ZigTypeIdNull) {
ir_add_error_node(ira, source_node, buf_sprintf("only optionals (not '%s') can compare to null", ZigType *non_null_type = (op1->value.type->id == ZigTypeIdNull) ? op2->value.type : op1->value.type;
buf_ptr(&(op1->value.type->id == ZigTypeIdNull ? op2->value.type->name : op1->value.type->name)))); ir_add_error_node(ira, source_node, buf_sprintf("comparison of '%s' with null",
buf_ptr(&non_null_type->name)));
return ira->codegen->invalid_instruction; return ira->codegen->invalid_instruction;
} }

View File

@ -863,7 +863,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = &x == null; \\ _ = &x == null;
\\} \\}
, ,
"tmp.zig:3:12: error: only optionals (not '*i32') can compare to null", "tmp.zig:3:12: error: comparison of '*i32' with null",
); );
cases.add( cases.add(

View File

@ -150,3 +150,31 @@ test "allowzero pointer and slice" {
expect(@typeInfo(@typeOf(ptr)).Pointer.is_allowzero); expect(@typeInfo(@typeOf(ptr)).Pointer.is_allowzero);
expect(@typeInfo(@typeOf(slice)).Pointer.is_allowzero); expect(@typeInfo(@typeOf(slice)).Pointer.is_allowzero);
} }
test "assign null directly to C pointer and test null equality" {
var x: [*c]i32 = null;
expect(x == null);
expect(null == x);
expect(!(x != null));
expect(!(null != x));
const y: [*c]i32 = null;
expect(y == null);
expect(null == y);
expect(!(y != null));
expect(!(null != y));
var n: i32 = 1234;
var x1: [*c]i32 = &n;
expect(!(x1 == null));
expect(!(null == x1));
expect(x1 != null);
expect(null != x1);
const nc: i32 = 1234;
const y1: [*c]const i32 = &nc;
expect(!(y1 == null));
expect(!(null == y1));
expect(y1 != null);
expect(null != y1);
}