parent
be7cacfbbe
commit
50bbb34594
|
@ -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)
|
||||||
|
|
99
src/ir.cpp
99
src/ir.cpp
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue