Commit 0caee421e3
src/ir.cpp
@@ -1225,7 +1225,7 @@ static IrInstruction *ir_build_size_of(IrBuilder *irb, Scope *scope, AstNode *so
return &instruction->base;
}
-static IrInstruction *ir_build_test_null(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
+static IrInstruction *ir_build_test_nonnull(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
IrInstructionTestNonNull *instruction = ir_build_instruction<IrInstructionTestNonNull>(irb, scope, source_node);
instruction->value = value;
@@ -1234,10 +1234,10 @@ static IrInstruction *ir_build_test_null(IrBuilder *irb, Scope *scope, AstNode *
return &instruction->base;
}
-static IrInstruction *ir_build_test_null_from(IrBuilder *irb, IrInstruction *old_instruction,
+static IrInstruction *ir_build_test_nonnull_from(IrBuilder *irb, IrInstruction *old_instruction,
IrInstruction *value)
{
- IrInstruction *new_instruction = ir_build_test_null(irb, old_instruction->scope,
+ IrInstruction *new_instruction = ir_build_test_nonnull(irb, old_instruction->scope,
old_instruction->source_node, value);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
@@ -2923,7 +2923,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
IrBasicBlock *null_block = ir_build_basic_block(irb, scope, "MaybeRetNull");
IrBasicBlock *ok_block = ir_build_basic_block(irb, scope, "MaybeRetOk");
- IrInstruction *is_non_null = ir_build_test_null(irb, scope, node, return_value);
+ IrInstruction *is_non_null = ir_build_test_nonnull(irb, scope, node, return_value);
IrInstruction *is_comptime;
if (ir_should_inline(irb)) {
@@ -2980,7 +2980,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
if (maybe_val_ptr == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node, maybe_val_ptr);
- IrInstruction *is_non_null = ir_build_test_null(irb, scope, node, maybe_val);
+ IrInstruction *is_non_null = ir_build_test_nonnull(irb, scope, node, maybe_val);
IrBasicBlock *return_block = ir_build_basic_block(irb, scope, "MaybeRetReturn");
IrBasicBlock *continue_block = ir_build_basic_block(irb, scope, "MaybeRetContinue");
@@ -3293,7 +3293,7 @@ static IrInstruction *ir_gen_maybe_ok_or(IrBuilder *irb, Scope *parent_scope, As
return irb->codegen->invalid_instruction;
IrInstruction *maybe_val = ir_build_load_ptr(irb, parent_scope, node, maybe_ptr);
- IrInstruction *is_non_null = ir_build_test_null(irb, parent_scope, node, maybe_val);
+ IrInstruction *is_non_null = ir_build_test_nonnull(irb, parent_scope, node, maybe_val);
IrInstruction *is_comptime;
if (ir_should_inline(irb)) {
@@ -4595,7 +4595,7 @@ static IrInstruction *ir_gen_if_var_expr(IrBuilder *irb, Scope *scope, AstNode *
return maybe_val_ptr;
IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node, maybe_val_ptr);
- IrInstruction *is_non_null = ir_build_test_null(irb, scope, node, maybe_val);
+ IrInstruction *is_non_null = ir_build_test_nonnull(irb, scope, node, maybe_val);
IrBasicBlock *then_block = ir_build_basic_block(irb, scope, "MaybeThen");
IrBasicBlock *else_block = ir_build_basic_block(irb, scope, "MaybeElse");
@@ -6839,13 +6839,55 @@ static TypeTableEntry *ir_analyze_bin_op_bool(IrAnalyze *ira, IrInstructionBinOp
static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) {
IrInstruction *op1 = bin_op_instruction->op1->other;
IrInstruction *op2 = bin_op_instruction->op2->other;
+
+ IrBinOp op_id = bin_op_instruction->op_id;
+ bool is_equality_cmp = (op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq);
+ if (is_equality_cmp &&
+ ((op1->value.type->id == TypeTableEntryIdNullLit && op2->value.type->id == TypeTableEntryIdMaybe) ||
+ (op2->value.type->id == TypeTableEntryIdNullLit && op1->value.type->id == TypeTableEntryIdMaybe) ||
+ (op1->value.type->id == TypeTableEntryIdNullLit && op2->value.type->id == TypeTableEntryIdNullLit)))
+ {
+ bool depends_on_compile_var = op1->value.depends_on_compile_var || op2->value.depends_on_compile_var;
+ if (op1->value.type->id == TypeTableEntryIdNullLit && op2->value.type->id == TypeTableEntryIdNullLit) {
+ ConstExprValue *out_val = ir_build_const_from(ira, &bin_op_instruction->base, depends_on_compile_var);
+ out_val->data.x_bool = (op_id == IrBinOpCmpEq);
+ return ira->codegen->builtin_types.entry_bool;
+ }
+ IrInstruction *maybe_op;
+ if (op1->value.type->id == TypeTableEntryIdNullLit) {
+ maybe_op = op2;
+ } else if (op2->value.type->id == TypeTableEntryIdNullLit) {
+ maybe_op = op1;
+ } else {
+ zig_unreachable();
+ }
+ if (instr_is_comptime(maybe_op)) {
+ ConstExprValue *maybe_val = ir_resolve_const(ira, maybe_op, UndefBad);
+ if (!maybe_val)
+ return ira->codegen->builtin_types.entry_invalid;
+ bool is_null = (maybe_val->data.x_maybe == nullptr);
+ ConstExprValue *out_val = ir_build_const_from(ira, &bin_op_instruction->base, depends_on_compile_var);
+ out_val->data.x_bool = (op_id == IrBinOpCmpEq) ? is_null : !is_null;
+ return ira->codegen->builtin_types.entry_bool;
+ }
+
+ IrInstruction *is_non_null = ir_build_test_nonnull(&ira->new_irb, bin_op_instruction->base.scope,
+ bin_op_instruction->base.source_node, maybe_op);
+ is_non_null->value.type = ira->codegen->builtin_types.entry_bool;
+
+ if (op_id == IrBinOpCmpEq) {
+ ir_build_bool_not_from(&ira->new_irb, &bin_op_instruction->base, is_non_null);
+ } else {
+ ir_link_new_instruction(is_non_null, &bin_op_instruction->base);
+ }
+ return ira->codegen->builtin_types.entry_bool;
+ }
+
IrInstruction *instructions[] = {op1, op2};
TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, bin_op_instruction->base.source_node, instructions, 2);
if (resolved_type->id == TypeTableEntryIdInvalid)
return resolved_type;
- IrBinOp op_id = bin_op_instruction->op_id;
- bool is_equality_cmp = (op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq);
AstNode *source_node = bin_op_instruction->base.source_node;
switch (resolved_type->id) {
case TypeTableEntryIdInvalid:
@@ -9240,7 +9282,7 @@ static TypeTableEntry *ir_analyze_instruction_test_non_null(IrAnalyze *ira, IrIn
return ira->codegen->builtin_types.entry_bool;
}
- ir_build_test_null_from(&ira->new_irb, &instruction->base, value);
+ ir_build_test_nonnull_from(&ira->new_irb, &instruction->base, value);
return ira->codegen->builtin_types.entry_bool;
} else if (type_entry->id == TypeTableEntryIdNullLit) {
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, false);
test/cases/null.zig
@@ -83,8 +83,11 @@ const Particle = struct {
fn nullLiteralOutsideFunction() {
@setFnTest(this);
- const is_null = if (const _ ?= here_is_a_null_literal.context) false else true;
+ const is_null = here_is_a_null_literal.context == null;
assert(is_null);
+
+ const is_non_null = here_is_a_null_literal.context != null;
+ assert(!is_non_null);
}
const SillyStruct = struct {
context: ?i32,
@@ -99,3 +102,13 @@ fn foo(x: ?i32) -> ?bool {
const value = ?return x;
return value > 1234;
}
+
+fn testNullRuntime() {
+ @setFnTest(this);
+
+ testTestNullRuntime(null);
+}
+fn testTestNullRuntime(x: ?i32) {
+ assert(x == null);
+ assert(!(x != null));
+}