diff options
| author | Andrew Kelley <superjoe30@gmail.com> | 2017-01-16 16:39:31 -0500 |
|---|---|---|
| committer | Andrew Kelley <superjoe30@gmail.com> | 2017-01-16 16:39:31 -0500 |
| commit | 0caee421e38672869dd596e6af732567cfaf2037 (patch) | |
| tree | 44ccd0e37f9f4b6d3ed984ed5bd20f7326dab79a | |
| parent | 867686af420bab965f63359499a526f35e875c9a (diff) | |
| download | zig-0caee421e38672869dd596e6af732567cfaf2037.tar.gz zig-0caee421e38672869dd596e6af732567cfaf2037.zip | |
ability to equality compare with null
closes #106
| -rw-r--r-- | src/ir.cpp | 62 | ||||
| -rw-r--r-- | test/cases/null.zig | 15 |
2 files changed, 66 insertions, 11 deletions
diff --git a/src/ir.cpp b/src/ir.cpp index 5e7f00c306..1103aa697a 100644 --- a/src/ir.cpp +++ b/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); diff --git a/test/cases/null.zig b/test/cases/null.zig index 280023a93d..5359f0309f 100644 --- a/test/cases/null.zig +++ b/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)); +} |
