From 7ac44fbd4fd84734b828f5f9542b17caa59b4ada Mon Sep 17 00:00:00 2001 From: foobles Date: Sat, 9 May 2020 15:32:12 -0500 Subject: [PATCH 01/15] extracted function ir_try_evaluate_bin_op_const --- src/ir.cpp | 90 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 51 insertions(+), 39 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 4a0fc55d748d..7034f816398b 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15829,12 +15829,12 @@ static void set_optional_payload(ZigValue *opt_val, ZigValue *payload) { } static IrInstGen *ir_evaluate_bin_op_cmp(IrAnalyze *ira, ZigType *resolved_type, - ZigValue *op1_val, ZigValue *op2_val, IrInstSrcBinOp *bin_op_instruction, IrBinOp op_id, + ZigValue *op1_val, ZigValue *op2_val, IrInst *source_instr, IrBinOp op_id, bool one_possible_value) { if (op1_val->special == ConstValSpecialUndef || op2_val->special == ConstValSpecialUndef) - return ir_const_undef(ira, &bin_op_instruction->base.base, resolved_type); + return ir_const_undef(ira, source_instr, resolved_type); if (resolved_type->id == ZigTypeIdPointer && op_id != IrBinOpCmpEq && op_id != IrBinOpCmpNotEq) { if ((op1_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr || op1_val->data.x_ptr.special == ConstPtrSpecialNull) && @@ -15854,7 +15854,7 @@ static IrInstGen *ir_evaluate_bin_op_cmp(IrAnalyze *ira, ZigType *resolved_type, cmp_result = CmpEQ; } bool answer = resolve_cmp_op_id(op_id, cmp_result); - return ir_const_bool(ira, &bin_op_instruction->base.base, answer); + return ir_const_bool(ira, source_instr, answer); } } else { bool are_equal = one_possible_value || const_values_equal(ira->codegen, op1_val, op2_val); @@ -15866,11 +15866,55 @@ static IrInstGen *ir_evaluate_bin_op_cmp(IrAnalyze *ira, ZigType *resolved_type, } else { zig_unreachable(); } - return ir_const_bool(ira, &bin_op_instruction->base.base, answer); + return ir_const_bool(ira, source_instr, answer); } zig_unreachable(); } +static IrInstGen *ir_try_evaluate_bin_op_const(IrAnalyze *ira, IrInst *source_instr, IrInstGen *op1, IrInstGen *op2, + ZigType *resolved_type, IrBinOp op_id) +{ + assert(op1->value->type == resolved_type && op2->value->type == resolved_type); + bool one_possible_value; + switch (type_has_one_possible_value(ira->codegen, resolved_type)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_inst_gen; + case OnePossibleValueYes: + one_possible_value = true; + break; + case OnePossibleValueNo: + one_possible_value = false; + break; + } + + if (one_possible_value || (instr_is_comptime(op1) && instr_is_comptime(op2))) { + ZigValue *op1_val = one_possible_value ? op1->value : ir_resolve_const(ira, op1, UndefBad); + if (op1_val == nullptr) + return ira->codegen->invalid_inst_gen; + ZigValue *op2_val = one_possible_value ? op2->value : ir_resolve_const(ira, op2, UndefBad); + if (op2_val == nullptr) + return ira->codegen->invalid_inst_gen; + if (resolved_type->id != ZigTypeIdVector) + return ir_evaluate_bin_op_cmp(ira, resolved_type, op1_val, op2_val, source_instr, op_id, one_possible_value); + IrInstGen *result = ir_const(ira, source_instr, + get_vector_type(ira->codegen, resolved_type->data.vector.len, ira->codegen->builtin_types.entry_bool)); + result->value->data.x_array.data.s_none.elements = + ira->codegen->pass1_arena->allocate(resolved_type->data.vector.len); + + expand_undef_array(ira->codegen, result->value); + for (size_t i = 0;i < resolved_type->data.vector.len;i++) { + IrInstGen *cur_res = ir_evaluate_bin_op_cmp(ira, resolved_type->data.vector.elem_type, + &op1_val->data.x_array.data.s_none.elements[i], + &op2_val->data.x_array.data.s_none.elements[i], + source_instr, op_id, one_possible_value); + copy_const_val(ira->codegen, &result->value->data.x_array.data.s_none.elements[i], cur_res->value); + } + return result; + } else { + return nullptr; + } +} + // Returns ErrorNotLazy when the value cannot be determined static Error lazy_cmp_zero(CodeGen *codegen, AstNode *source_node, ZigValue *val, Cmp *result) { Error err; @@ -16681,41 +16725,9 @@ static IrInstGen *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstSrcBinOp *bin_op_i if (type_is_invalid(casted_op2->value->type)) return ira->codegen->invalid_inst_gen; - bool one_possible_value; - switch (type_has_one_possible_value(ira->codegen, resolved_type)) { - case OnePossibleValueInvalid: - return ira->codegen->invalid_inst_gen; - case OnePossibleValueYes: - one_possible_value = true; - break; - case OnePossibleValueNo: - one_possible_value = false; - break; - } - - if (one_possible_value || (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2))) { - ZigValue *op1_val = one_possible_value ? casted_op1->value : ir_resolve_const(ira, casted_op1, UndefBad); - if (op1_val == nullptr) - return ira->codegen->invalid_inst_gen; - ZigValue *op2_val = one_possible_value ? casted_op2->value : ir_resolve_const(ira, casted_op2, UndefBad); - if (op2_val == nullptr) - return ira->codegen->invalid_inst_gen; - if (resolved_type->id != ZigTypeIdVector) - return ir_evaluate_bin_op_cmp(ira, resolved_type, op1_val, op2_val, bin_op_instruction, op_id, one_possible_value); - IrInstGen *result = ir_const(ira, &bin_op_instruction->base.base, - get_vector_type(ira->codegen, resolved_type->data.vector.len, ira->codegen->builtin_types.entry_bool)); - result->value->data.x_array.data.s_none.elements = - ira->codegen->pass1_arena->allocate(resolved_type->data.vector.len); - - expand_undef_array(ira->codegen, result->value); - for (size_t i = 0;i < resolved_type->data.vector.len;i++) { - IrInstGen *cur_res = ir_evaluate_bin_op_cmp(ira, resolved_type->data.vector.elem_type, - &op1_val->data.x_array.data.s_none.elements[i], - &op2_val->data.x_array.data.s_none.elements[i], - bin_op_instruction, op_id, one_possible_value); - copy_const_val(ira->codegen, &result->value->data.x_array.data.s_none.elements[i], cur_res->value); - } - return result; + IrInstGen *resolve_const_result = ir_try_evaluate_bin_op_const(ira, &bin_op_instruction->base.base, casted_op1, casted_op2, resolved_type, op_id); + if (resolve_const_result != nullptr) { + return resolve_const_result; } ZigType *res_type = (resolved_type->id == ZigTypeIdVector) ? From efe0749e9b8f0000702521858b5d759618d0b78c Mon Sep 17 00:00:00 2001 From: foobles Date: Sat, 9 May 2020 15:47:28 -0500 Subject: [PATCH 02/15] extracted type_is_self_comparable function --- src/ir.cpp | 93 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 49 insertions(+), 44 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 7034f816398b..d3da31d71628 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -16449,6 +16449,54 @@ static IrInstGen *ir_analyze_bin_op_cmp_numeric(IrAnalyze *ira, IrInst *source_i return ir_build_bin_op_gen(ira, source_instr, result_type, op_id, casted_op1, casted_op2, true); } +static bool type_is_self_comparable(ZigType *ty, bool is_equality_cmp) { + if (type_is_numeric(ty)) { + return true; + } + switch (ty->id) { + case ZigTypeIdInvalid: + zig_unreachable(); + + case ZigTypeIdComptimeFloat: + case ZigTypeIdComptimeInt: + case ZigTypeIdInt: + case ZigTypeIdFloat: + zig_unreachable(); // handled with the type_is_numeric check above + + case ZigTypeIdVector: + // Not every case is handled by the type_is_numeric check above, + // vectors of bool trigger this code path + case ZigTypeIdBool: + case ZigTypeIdMetaType: + case ZigTypeIdVoid: + case ZigTypeIdErrorSet: + case ZigTypeIdFn: + case ZigTypeIdOpaque: + case ZigTypeIdBoundFn: + case ZigTypeIdEnum: + case ZigTypeIdEnumLiteral: + case ZigTypeIdAnyFrame: + return is_equality_cmp; + + case ZigTypeIdPointer: + return is_equality_cmp || (ty->data.pointer.ptr_len == PtrLenC); + + case ZigTypeIdUnreachable: + case ZigTypeIdArray: + case ZigTypeIdStruct: + case ZigTypeIdUndefined: + case ZigTypeIdNull: + case ZigTypeIdErrorUnion: + case ZigTypeIdUnion: + case ZigTypeIdFnFrame: + return false; + + case ZigTypeIdOptional: + return is_equality_cmp && get_src_ptr_type(ty) != nullptr; + } + zig_unreachable(); +} + static IrInstGen *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstSrcBinOp *bin_op_instruction) { IrInstGen *op1 = bin_op_instruction->op1->child; if (type_is_invalid(op1->value->type)) @@ -16666,51 +16714,8 @@ static IrInstGen *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstSrcBinOp *bin_op_i if (type_is_invalid(resolved_type)) return ira->codegen->invalid_inst_gen; - bool operator_allowed; - switch (resolved_type->id) { - case ZigTypeIdInvalid: - zig_unreachable(); // handled above - - case ZigTypeIdComptimeFloat: - case ZigTypeIdComptimeInt: - case ZigTypeIdInt: - case ZigTypeIdFloat: - zig_unreachable(); // handled with the type_is_numeric checks above + bool operator_allowed = type_is_self_comparable(resolved_type, is_equality_cmp); - case ZigTypeIdVector: - // Not every case is handled by the type_is_numeric checks above, - // vectors of bool trigger this code path - case ZigTypeIdBool: - case ZigTypeIdMetaType: - case ZigTypeIdVoid: - case ZigTypeIdErrorSet: - case ZigTypeIdFn: - case ZigTypeIdOpaque: - case ZigTypeIdBoundFn: - case ZigTypeIdEnum: - case ZigTypeIdEnumLiteral: - case ZigTypeIdAnyFrame: - operator_allowed = is_equality_cmp; - break; - - case ZigTypeIdPointer: - operator_allowed = is_equality_cmp || (resolved_type->data.pointer.ptr_len == PtrLenC); - break; - - case ZigTypeIdUnreachable: - case ZigTypeIdArray: - case ZigTypeIdStruct: - case ZigTypeIdUndefined: - case ZigTypeIdNull: - case ZigTypeIdErrorUnion: - case ZigTypeIdUnion: - case ZigTypeIdFnFrame: - operator_allowed = false; - break; - case ZigTypeIdOptional: - operator_allowed = is_equality_cmp && get_src_ptr_type(resolved_type) != nullptr; - break; - } if (!operator_allowed) { ir_add_error_node(ira, source_node, buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name))); From 4fa609c4fb1b933b271a90c90fa0b423918f2cc4 Mon Sep 17 00:00:00 2001 From: foobles Date: Sun, 10 May 2020 22:15:11 -0500 Subject: [PATCH 03/15] renamed ir_try_evaluate_bin_op_const to ir_try_evaluate_bin_op_cmp_const --- src/ir.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index d3da31d71628..2ac043f1b4cb 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15871,7 +15871,7 @@ static IrInstGen *ir_evaluate_bin_op_cmp(IrAnalyze *ira, ZigType *resolved_type, zig_unreachable(); } -static IrInstGen *ir_try_evaluate_bin_op_const(IrAnalyze *ira, IrInst *source_instr, IrInstGen *op1, IrInstGen *op2, +static IrInstGen *ir_try_evaluate_bin_op_cmp_const(IrAnalyze *ira, IrInst *source_instr, IrInstGen *op1, IrInstGen *op2, ZigType *resolved_type, IrBinOp op_id) { assert(op1->value->type == resolved_type && op2->value->type == resolved_type); @@ -16730,7 +16730,8 @@ static IrInstGen *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstSrcBinOp *bin_op_i if (type_is_invalid(casted_op2->value->type)) return ira->codegen->invalid_inst_gen; - IrInstGen *resolve_const_result = ir_try_evaluate_bin_op_const(ira, &bin_op_instruction->base.base, casted_op1, casted_op2, resolved_type, op_id); + IrInstGen *resolve_const_result = ir_try_evaluate_bin_op_cmp_const(ira, &bin_op_instruction->base.base, casted_op1, + casted_op2, resolved_type, op_id); if (resolve_const_result != nullptr) { return resolve_const_result; } From 08fe20ec84631f3621cb65762cdd5a8d280f8e92 Mon Sep 17 00:00:00 2001 From: foobles Date: Sun, 10 May 2020 23:11:52 -0500 Subject: [PATCH 04/15] implemented analysis of ?T == T --- src/ir.cpp | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/src/ir.cpp b/src/ir.cpp index 2ac043f1b4cb..2cddf963f328 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15,6 +15,7 @@ #include "softfloat.hpp" #include "util.hpp" #include "mem_list.hpp" +#include "all_types.hpp" #include @@ -16497,6 +16498,97 @@ static bool type_is_self_comparable(ZigType *ty, bool is_equality_cmp) { zig_unreachable(); } +static IrInstGen *ir_analyze_cmp_optional_non_optional(IrAnalyze *ira, IrInst *source_instr, + IrInstGen *op1, IrInstGen *op2, IrInstGen *optional, IrBinOp op_id) +{ + assert(op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq); + assert(optional->value->type->id == ZigTypeIdOptional); + + IrInstGen *non_optional; + if (op1 == optional) { + non_optional = op2; + } else if (op2 == optional) { + non_optional = op1; + } else { + zig_unreachable(); + } + + ZigType *child_type = optional->value->type->data.maybe.child_type; + bool child_type_does_not_match = (child_type != non_optional->value->type); + if (child_type_does_not_match || !type_is_self_comparable(child_type, true)) { + ErrorMsg *msg = ir_add_error_node(ira, source_instr->source_node, buf_sprintf("cannot compare types '%s' and '%s'", + buf_ptr(&op1->value->type->name), + buf_ptr(&op2->value->type->name))); + + if (child_type_does_not_match && non_optional->value->type->id == ZigTypeIdOptional) { + add_error_note(ira->codegen, msg, source_instr->source_node, buf_sprintf("only optional to non-optional comparison is allowed for non-pointer types")); + } else if (child_type_does_not_match) { + add_error_note(ira->codegen, msg, source_instr->source_node, buf_sprintf("optional child type '%s' must equal non-optional type '%s'", + buf_ptr(&child_type->name), + buf_ptr(&non_optional->value->type->name))); + } else { + add_error_note(ira->codegen, msg, source_instr->source_node, buf_sprintf("operator not supported for type '%s'", + buf_ptr(&child_type->name))); + } + + return ira->codegen->invalid_inst_gen; + } + + if (child_type->id == ZigTypeIdVector) { + ir_add_error_node(ira, source_instr->source_node, buf_sprintf("TODO add comparison of optional vector")); + return ira->codegen->invalid_inst_gen; + } + + if (instr_is_comptime(optional) && instr_is_comptime(non_optional)) { + ZigValue *optional_val = ir_resolve_const(ira, optional, UndefBad); + if (!optional_val) { + return ira->codegen->invalid_inst_gen; + } + + ZigValue *non_optional_val = ir_resolve_const(ira, non_optional, UndefBad); + if (!non_optional_val) { + return ira->codegen->invalid_inst_gen; + } + + if (!optional_value_is_null(optional_val)) { + IrInstGen *optional_unwrapped = ir_analyze_optional_value_payload_value(ira, source_instr, optional, false); + if (type_is_invalid(optional_unwrapped->value->type)) { + return ira->codegen->invalid_inst_gen; + } + + IrInstGen *ret = ir_try_evaluate_bin_op_cmp_const(ira, source_instr, optional_unwrapped, non_optional, child_type, op_id); + assert(ret != nullptr); + return ret; + } + return ir_const_bool(ira, source_instr, (op_id != IrBinOpCmpEq)); + } + + ZigType *result_type = ira->codegen->builtin_types.entry_bool; + + IrBasicBlockGen *is_null_block = ir_create_basic_block_gen(ira, source_instr->scope, "CmpOptionalNonOptional optional is null"); + IrBasicBlockGen *is_non_null_block = ir_create_basic_block_gen(ira, source_instr->scope, "CmpOptionalNonOptional optional is not null"); + IrBasicBlockGen *end_block = ir_create_basic_block_gen(ira, source_instr->scope, "CmpOptionalNonOptional end"); + + IrInstGen *non_null_bit = ir_build_test_non_null_gen(ira, source_instr, optional); + ir_build_cond_br_gen(ira, source_instr, non_null_bit, is_non_null_block, is_null_block); + ir_finish_bb(ira); + + ir_set_cursor_at_end_gen(&ira->new_irb, is_non_null_block); + IrInstGen *optional_unwrapped = ir_analyze_optional_value_payload_value(ira, source_instr, optional, false); + IrInstGen *non_null_cmp_result = ir_build_bin_op_gen(ira, source_instr, result_type, op_id, + optional_unwrapped, non_optional, false); + ir_finish_bb(ira); + + ir_set_cursor_at_end_gen(&ira->new_irb, is_null_block); + IrInstGen *is_null_result = ir_const_bool(ira, source_instr, (op_id != IrBinOpCmpEq)); + ir_finish_bb(ira); + + ir_set_cursor_at_end_gen(&ira->new_irb, end_block); + IrBasicBlockGen *incoming_blocks[] = {is_null_block, is_non_null_block}; + IrInstGen *incoming_values[] = {is_null_result, non_null_cmp_result}; + return ir_build_phi_gen(ira, source_instr, 2, incoming_blocks, incoming_values, result_type); +} + static IrInstGen *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstSrcBinOp *bin_op_instruction) { IrInstGen *op1 = bin_op_instruction->op1->child; if (type_is_invalid(op1->value->type)) @@ -16573,6 +16665,14 @@ static IrInstGen *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstSrcBinOp *bin_op_i } else { return is_non_null; } + } else if (is_equality_cmp && + (op1->value->type->id == ZigTypeIdOptional && get_src_ptr_type(op1->value->type) == nullptr)) + { + return ir_analyze_cmp_optional_non_optional(ira, &bin_op_instruction->base.base, op1, op2, op1, op_id); + } else if(is_equality_cmp && + (op2->value->type->id == ZigTypeIdOptional && get_src_ptr_type(op2->value->type) == nullptr)) + { + return ir_analyze_cmp_optional_non_optional(ira, &bin_op_instruction->base.base, op1, op2, op2, op_id); } else if (op1->value->type->id == ZigTypeIdNull || op2->value->type->id == ZigTypeIdNull) { ZigType *non_null_type = (op1->value->type->id == ZigTypeIdNull) ? op2->value->type : op1->value->type; ir_add_error_node(ira, source_node, buf_sprintf("comparison of '%s' with null", From 3f730458addbb1d90e100b1acda26df1cb8b80ce Mon Sep 17 00:00:00 2001 From: foobles Date: Mon, 11 May 2020 10:53:24 -0500 Subject: [PATCH 05/15] added ir_set_cursor_at_end_and_append_basic_block_gen --- src/ir.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/ir.cpp b/src/ir.cpp index 2cddf963f328..b6528ccd4636 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -5097,6 +5097,13 @@ static void ir_set_cursor_at_end(IrBuilderSrc *irb, IrBasicBlockSrc *basic_block irb->current_basic_block = basic_block; } +static void ir_set_cursor_at_end_and_append_block_gen(IrBuilderGen *irb, IrBasicBlockGen *basic_block) { + assert(!basic_block->already_appended); + basic_block->already_appended = true; + irb->exec->basic_block_list.append(basic_block); + ir_set_cursor_at_end_gen(irb, basic_block); +} + static void ir_set_cursor_at_end_and_append_block(IrBuilderSrc *irb, IrBasicBlockSrc *basic_block) { basic_block->index = irb->exec->basic_block_list.length; irb->exec->basic_block_list.append(basic_block); From fe377357f014bfecfce4c3663f08eb25812cd181 Mon Sep 17 00:00:00 2001 From: foobles Date: Wed, 13 May 2020 22:23:00 -0500 Subject: [PATCH 06/15] use build_br_gen and ir_set_cursor_at_end_and_append_block_gen --- src/ir.cpp | 10 ++++----- test/stage1/behavior/optional.zig | 34 +++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index b6528ccd4636..9848dd032c35 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -16578,17 +16578,17 @@ static IrInstGen *ir_analyze_cmp_optional_non_optional(IrAnalyze *ira, IrInst *s IrInstGen *non_null_bit = ir_build_test_non_null_gen(ira, source_instr, optional); ir_build_cond_br_gen(ira, source_instr, non_null_bit, is_non_null_block, is_null_block); - ir_finish_bb(ira); - ir_set_cursor_at_end_gen(&ira->new_irb, is_non_null_block); + ir_set_cursor_at_end_and_append_block_gen(&ira->new_irb, is_non_null_block); IrInstGen *optional_unwrapped = ir_analyze_optional_value_payload_value(ira, source_instr, optional, false); IrInstGen *non_null_cmp_result = ir_build_bin_op_gen(ira, source_instr, result_type, op_id, optional_unwrapped, non_optional, false); - ir_finish_bb(ira); + ir_build_br_gen(ira, source_instr, end_block); - ir_set_cursor_at_end_gen(&ira->new_irb, is_null_block); + + ir_set_cursor_at_end_and_append_block_gen(&ira->new_irb, is_null_block); IrInstGen *is_null_result = ir_const_bool(ira, source_instr, (op_id != IrBinOpCmpEq)); - ir_finish_bb(ira); + ir_build_br_gen(ira, source_instr, end_block); ir_set_cursor_at_end_gen(&ira->new_irb, end_block); IrBasicBlockGen *incoming_blocks[] = {is_null_block, is_non_null_block}; diff --git a/test/stage1/behavior/optional.zig b/test/stage1/behavior/optional.zig index ffd37e59f5e6..f1645fd7d99a 100644 --- a/test/stage1/behavior/optional.zig +++ b/test/stage1/behavior/optional.zig @@ -49,6 +49,40 @@ test "address of unwrap optional" { expect(foo.a == 1234); } +test "equality compare optional with non-optional" { + //test_cmp_optional_non_optional(); CAUSES ASSERTION ERROR I CANNOT TRACK AT RUNTIME. + comptime test_cmp_optional_non_optional(); +} + +fn test_cmp_optional_non_optional() void { + comptime{ + var ten: i32 = 10; + var opt_ten: ?i32 = 10; + var five: i32 = 5; + var int_n: ?i32 = null; + + expect(int_n != ten); + expect(opt_ten == ten); + expect(opt_ten != five); + + // test where handle_is_ptr() is false + var err_test_a: anyerror = error.TestErrorA; + var err_opt_test_a: ?anyerror = error.TestErrorA; + var err_test_b: anyerror = error.TestErrorB; + var err_n: ?anyerror = null; + + expect(err_n != err_test_a); + expect(err_opt_test_a == err_test_a); + expect(err_opt_test_a != err_test_b); + + // test evaluation is always lexical + // ensure that the optional isn't always computed before the non-optional + var mutable_state: i32 = 0; + _ = blk1: { mutable_state += 1; break :blk1 @as(?f64, 10.0); } != blk2: { expect(mutable_state == 1); break :blk2 @as(f64, 5.0); }; + _ = blk1: { mutable_state += 1; break :blk1 @as(f64, 10.0); } != blk2: { expect(mutable_state == 2); break :blk2 @as(?f64, 5.0); }; + } + +} test "passing an optional integer as a parameter" { const S = struct { fn entry() bool { From a1797772f77288cdde54c87714e5e979045fd9db Mon Sep 17 00:00:00 2001 From: foobles Date: Tue, 19 May 2020 20:50:25 -0500 Subject: [PATCH 07/15] fixed use of pointer to local variable; basic blocks not being appended --- src/ir.cpp | 16 +++++++++++----- test/stage1/behavior/optional.zig | 5 +---- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index c660a15f62dd..514560e20190 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -16573,10 +16573,11 @@ static IrInstGen *ir_analyze_cmp_optional_non_optional(IrAnalyze *ira, IrInst *s } ZigType *result_type = ira->codegen->builtin_types.entry_bool; + ira->new_irb.exec->basic_block_list.append(ira->new_irb.current_basic_block); - IrBasicBlockGen *is_null_block = ir_create_basic_block_gen(ira, source_instr->scope, "CmpOptionalNonOptional optional is null"); - IrBasicBlockGen *is_non_null_block = ir_create_basic_block_gen(ira, source_instr->scope, "CmpOptionalNonOptional optional is not null"); - IrBasicBlockGen *end_block = ir_create_basic_block_gen(ira, source_instr->scope, "CmpOptionalNonOptional end"); + IrBasicBlockGen *is_null_block = ir_create_basic_block_gen(ira, source_instr->scope, "CmpOptionalNonOptionalOptionalNull"); + IrBasicBlockGen *is_non_null_block = ir_create_basic_block_gen(ira, source_instr->scope, "CmpOptionalNonOptionalOptionalNotNull"); + IrBasicBlockGen *end_block = ir_create_basic_block_gen(ira, source_instr->scope, "CmpOptionalNonOptionalEnd"); IrInstGen *non_null_bit = ir_build_test_non_null_gen(ira, source_instr, optional); ir_build_cond_br_gen(ira, source_instr, non_null_bit, is_non_null_block, is_null_block); @@ -16593,8 +16594,13 @@ static IrInstGen *ir_analyze_cmp_optional_non_optional(IrAnalyze *ira, IrInst *s ir_build_br_gen(ira, source_instr, end_block); ir_set_cursor_at_end_gen(&ira->new_irb, end_block); - IrBasicBlockGen *incoming_blocks[] = {is_null_block, is_non_null_block}; - IrInstGen *incoming_values[] = {is_null_result, non_null_cmp_result}; + IrBasicBlockGen **incoming_blocks = heap::c_allocator.allocate_nonzero(2); + incoming_blocks[0] = is_null_block; + incoming_blocks[1] = is_non_null_block; + IrInstGen **incoming_values = heap::c_allocator.allocate_nonzero(2); + incoming_values[0] = is_null_result; + incoming_values[1] = non_null_cmp_result; + return ir_build_phi_gen(ira, source_instr, 2, incoming_blocks, incoming_values, result_type); } diff --git a/test/stage1/behavior/optional.zig b/test/stage1/behavior/optional.zig index f1645fd7d99a..9931510969f1 100644 --- a/test/stage1/behavior/optional.zig +++ b/test/stage1/behavior/optional.zig @@ -50,12 +50,11 @@ test "address of unwrap optional" { } test "equality compare optional with non-optional" { - //test_cmp_optional_non_optional(); CAUSES ASSERTION ERROR I CANNOT TRACK AT RUNTIME. + test_cmp_optional_non_optional(); comptime test_cmp_optional_non_optional(); } fn test_cmp_optional_non_optional() void { - comptime{ var ten: i32 = 10; var opt_ten: ?i32 = 10; var five: i32 = 5; @@ -80,8 +79,6 @@ fn test_cmp_optional_non_optional() void { var mutable_state: i32 = 0; _ = blk1: { mutable_state += 1; break :blk1 @as(?f64, 10.0); } != blk2: { expect(mutable_state == 1); break :blk2 @as(f64, 5.0); }; _ = blk1: { mutable_state += 1; break :blk1 @as(f64, 10.0); } != blk2: { expect(mutable_state == 2); break :blk2 @as(?f64, 5.0); }; - } - } test "passing an optional integer as a parameter" { const S = struct { From 5529f8d9e5814ff57faff7e70da071de0e237c4e Mon Sep 17 00:00:00 2001 From: foobles Date: Tue, 19 May 2020 21:35:50 -0500 Subject: [PATCH 08/15] added ir_append_basic_block_gen --- src/ir.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 514560e20190..02712e8a121b 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -5097,10 +5097,14 @@ static void ir_set_cursor_at_end(IrBuilderSrc *irb, IrBasicBlockSrc *basic_block irb->current_basic_block = basic_block; } +static void ir_append_basic_block_gen(IrBuilderGen *irb, IrBasicBlockGen *bb) { + assert(!bb->already_appended); + bb->already_appended = true; + irb->exec->basic_block_list.append(bb); +} + static void ir_set_cursor_at_end_and_append_block_gen(IrBuilderGen *irb, IrBasicBlockGen *basic_block) { - assert(!basic_block->already_appended); - basic_block->already_appended = true; - irb->exec->basic_block_list.append(basic_block); + ir_append_basic_block_gen(irb, basic_block); ir_set_cursor_at_end_gen(irb, basic_block); } @@ -13133,12 +13137,11 @@ static void ir_start_next_bb(IrAnalyze *ira) { static void ir_finish_bb(IrAnalyze *ira) { if (!ira->new_irb.current_basic_block->already_appended) { - ira->new_irb.current_basic_block->already_appended = true; + ir_append_basic_block_gen(&ira->new_irb, ira->new_irb.current_basic_block); if (ira->codegen->verbose_ir) { fprintf(stderr, "append new bb %s_%" PRIu32 "\n", ira->new_irb.current_basic_block->name_hint, ira->new_irb.current_basic_block->debug_id); } - ira->new_irb.exec->basic_block_list.append(ira->new_irb.current_basic_block); } ira->instruction_index += 1; while (ira->instruction_index < ira->old_irb.current_basic_block->instruction_list.length) { @@ -16573,7 +16576,7 @@ static IrInstGen *ir_analyze_cmp_optional_non_optional(IrAnalyze *ira, IrInst *s } ZigType *result_type = ira->codegen->builtin_types.entry_bool; - ira->new_irb.exec->basic_block_list.append(ira->new_irb.current_basic_block); + ir_append_basic_block_gen(&ira->new_irb, ira->new_irb.current_basic_block); IrBasicBlockGen *is_null_block = ir_create_basic_block_gen(ira, source_instr->scope, "CmpOptionalNonOptionalOptionalNull"); IrBasicBlockGen *is_non_null_block = ir_create_basic_block_gen(ira, source_instr->scope, "CmpOptionalNonOptionalOptionalNotNull"); From 219d0921e7dffd406745a69d05cb5129f59644dd Mon Sep 17 00:00:00 2001 From: foobles Date: Tue, 19 May 2020 21:50:07 -0500 Subject: [PATCH 09/15] improved test cases --- test/compile_errors.zig | 34 +++++++++++++++++++++++++++++++ test/stage1/behavior/optional.zig | 11 +--------- 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/test/compile_errors.zig b/test/compile_errors.zig index a6e2d0b98f26..db15d22580de 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -7419,4 +7419,38 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { , &[_][]const u8{ ":2:75: error: operation caused overflow", }); + + cases.add("compare optional to non-optional with invalid types", + \\export fn inconsistentChildType() void { + \\ var x: ?i32 = undefined; + \\ const y: comptime_int = 10; + \\ _ = (x == y); + \\} + \\ + \\export fn optionalToOptional() void { + \\ var x: ?i32 = undefined; + \\ var y: ?i32 = undefined; + \\ _ = (x == y); + \\} + \\ + \\export fn optionalVector() void { + \\ var x: ?@Vector(10, i32) = undefined; + \\ var y: @Vector(10, i32) = undefined; + \\ _ = (x == y); + \\} + \\ + \\export fn invalidChildType() void { + \\ var x: ?[3]i32 = undefined; + \\ var y: [3]i32 = undefined; + \\ _ = (x == y); + \\} + , &[_][]const u8{ + ":4:12: error: cannot compare types '?i32' and 'comptime_int'", + ":4:12: note: optional child type 'i32' must equal non-optional type 'comptime_int'", + ":10:12: error: cannot compare types '?i32' and '?i32'", + ":10:12: note: only optional to non-optional comparison is allowed", + ":16:12: error: TODO add comparison of optional vector", + ":22:12: error: cannot compare types '?[3]i32' and '[3]i32'", + ":22:12: note: operator not supported for type '[3]i32'", + }); } diff --git a/test/stage1/behavior/optional.zig b/test/stage1/behavior/optional.zig index 9931510969f1..0003bb86e176 100644 --- a/test/stage1/behavior/optional.zig +++ b/test/stage1/behavior/optional.zig @@ -64,22 +64,13 @@ fn test_cmp_optional_non_optional() void { expect(opt_ten == ten); expect(opt_ten != five); - // test where handle_is_ptr() is false - var err_test_a: anyerror = error.TestErrorA; - var err_opt_test_a: ?anyerror = error.TestErrorA; - var err_test_b: anyerror = error.TestErrorB; - var err_n: ?anyerror = null; - - expect(err_n != err_test_a); - expect(err_opt_test_a == err_test_a); - expect(err_opt_test_a != err_test_b); - // test evaluation is always lexical // ensure that the optional isn't always computed before the non-optional var mutable_state: i32 = 0; _ = blk1: { mutable_state += 1; break :blk1 @as(?f64, 10.0); } != blk2: { expect(mutable_state == 1); break :blk2 @as(f64, 5.0); }; _ = blk1: { mutable_state += 1; break :blk1 @as(f64, 10.0); } != blk2: { expect(mutable_state == 2); break :blk2 @as(?f64, 5.0); }; } + test "passing an optional integer as a parameter" { const S = struct { fn entry() bool { From 6b2181229aa8b4427e75ab60327d4fa3d3f59b74 Mon Sep 17 00:00:00 2001 From: foobles Date: Tue, 19 May 2020 22:02:16 -0500 Subject: [PATCH 10/15] removed include of all_types in ir.cpp --- src/ir.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ir.cpp b/src/ir.cpp index d8317b704036..8710eb7aa988 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15,7 +15,6 @@ #include "softfloat.hpp" #include "util.hpp" #include "mem_list.hpp" -#include "all_types.hpp" #include From 1bbb7375ed7cabd7b79e50e4254dfafac7a7ae33 Mon Sep 17 00:00:00 2001 From: foobles Date: Wed, 20 May 2020 13:17:17 -0500 Subject: [PATCH 11/15] improved error messages --- src/ir.cpp | 26 +++++++++++++++----------- test/compile_errors.zig | 4 ++-- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 8710eb7aa988..b1ff8235a62f 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -16525,23 +16525,27 @@ static IrInstGen *ir_analyze_cmp_optional_non_optional(IrAnalyze *ira, IrInst *s } ZigType *child_type = optional->value->type->data.maybe.child_type; - bool child_type_does_not_match = (child_type != non_optional->value->type); - if (child_type_does_not_match || !type_is_self_comparable(child_type, true)) { + bool child_type_matches = (child_type == non_optional->value->type); + if (!child_type_matches || !type_is_self_comparable(child_type, true)) { ErrorMsg *msg = ir_add_error_node(ira, source_instr->source_node, buf_sprintf("cannot compare types '%s' and '%s'", buf_ptr(&op1->value->type->name), buf_ptr(&op2->value->type->name))); - if (child_type_does_not_match && non_optional->value->type->id == ZigTypeIdOptional) { - add_error_note(ira->codegen, msg, source_instr->source_node, buf_sprintf("only optional to non-optional comparison is allowed for non-pointer types")); - } else if (child_type_does_not_match) { - add_error_note(ira->codegen, msg, source_instr->source_node, buf_sprintf("optional child type '%s' must equal non-optional type '%s'", - buf_ptr(&child_type->name), - buf_ptr(&non_optional->value->type->name))); + if (!child_type_matches) { + if (non_optional->value->type->id == ZigTypeIdOptional) { + add_error_note(ira->codegen, msg, source_instr->source_node, buf_sprintf( + "optional to optional comparison is only supported for optional pointer types")); + } else { + add_error_note(ira->codegen, msg, source_instr->source_node, + buf_sprintf("optional child type '%s' must be the same as non-optional type '%s'", + buf_ptr(&child_type->name), + buf_ptr(&non_optional->value->type->name))); + } } else { - add_error_note(ira->codegen, msg, source_instr->source_node, buf_sprintf("operator not supported for type '%s'", - buf_ptr(&child_type->name))); + add_error_note(ira->codegen, msg, source_instr->source_node, + buf_sprintf("operator not supported for type '%s'", + buf_ptr(&child_type->name))); } - return ira->codegen->invalid_inst_gen; } diff --git a/test/compile_errors.zig b/test/compile_errors.zig index d6eba167144b..ac0a2405cfe7 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -7472,9 +7472,9 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\} , &[_][]const u8{ ":4:12: error: cannot compare types '?i32' and 'comptime_int'", - ":4:12: note: optional child type 'i32' must equal non-optional type 'comptime_int'", + ":4:12: note: optional child type 'i32' must be the same as non-optional type 'comptime_int'", ":10:12: error: cannot compare types '?i32' and '?i32'", - ":10:12: note: only optional to non-optional comparison is allowed", + ":10:12: note: optional to optional comparison is only supported for optional pointer types", ":16:12: error: TODO add comparison of optional vector", ":22:12: error: cannot compare types '?[3]i32' and '[3]i32'", ":22:12: note: operator not supported for type '[3]i32'", From cb47b68ac5de78092ef4f280222bdc0a23a9bb0f Mon Sep 17 00:00:00 2001 From: foobles Date: Wed, 20 May 2020 13:40:57 -0500 Subject: [PATCH 12/15] clean up variable names; add error handling --- src/ir.cpp | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index b1ff8235a62f..6da73cd2094c 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -16581,30 +16581,33 @@ static IrInstGen *ir_analyze_cmp_optional_non_optional(IrAnalyze *ira, IrInst *s ZigType *result_type = ira->codegen->builtin_types.entry_bool; ir_append_basic_block_gen(&ira->new_irb, ira->new_irb.current_basic_block); - IrBasicBlockGen *is_null_block = ir_create_basic_block_gen(ira, source_instr->scope, "CmpOptionalNonOptionalOptionalNull"); - IrBasicBlockGen *is_non_null_block = ir_create_basic_block_gen(ira, source_instr->scope, "CmpOptionalNonOptionalOptionalNotNull"); + IrBasicBlockGen *null_block = ir_create_basic_block_gen(ira, source_instr->scope, "CmpOptionalNonOptionalOptionalNull"); + IrBasicBlockGen *non_null_block = ir_create_basic_block_gen(ira, source_instr->scope, "CmpOptionalNonOptionalOptionalNotNull"); IrBasicBlockGen *end_block = ir_create_basic_block_gen(ira, source_instr->scope, "CmpOptionalNonOptionalEnd"); - IrInstGen *non_null_bit = ir_build_test_non_null_gen(ira, source_instr, optional); - ir_build_cond_br_gen(ira, source_instr, non_null_bit, is_non_null_block, is_null_block); + IrInstGen *is_non_null = ir_build_test_non_null_gen(ira, source_instr, optional); + ir_build_cond_br_gen(ira, source_instr, is_non_null, non_null_block, null_block); - ir_set_cursor_at_end_and_append_block_gen(&ira->new_irb, is_non_null_block); + ir_set_cursor_at_end_and_append_block_gen(&ira->new_irb, non_null_block); IrInstGen *optional_unwrapped = ir_analyze_optional_value_payload_value(ira, source_instr, optional, false); + if (type_is_invalid(optional_unwrapped->value->type)) { + return ira->codegen->invalid_inst_gen; + } IrInstGen *non_null_cmp_result = ir_build_bin_op_gen(ira, source_instr, result_type, op_id, optional_unwrapped, non_optional, false); ir_build_br_gen(ira, source_instr, end_block); - ir_set_cursor_at_end_and_append_block_gen(&ira->new_irb, is_null_block); - IrInstGen *is_null_result = ir_const_bool(ira, source_instr, (op_id != IrBinOpCmpEq)); + ir_set_cursor_at_end_and_append_block_gen(&ira->new_irb, null_block); + IrInstGen *null_result = ir_const_bool(ira, source_instr, (op_id != IrBinOpCmpEq)); ir_build_br_gen(ira, source_instr, end_block); ir_set_cursor_at_end_gen(&ira->new_irb, end_block); IrBasicBlockGen **incoming_blocks = heap::c_allocator.allocate_nonzero(2); - incoming_blocks[0] = is_null_block; - incoming_blocks[1] = is_non_null_block; + incoming_blocks[0] = null_block; + incoming_blocks[1] = non_null_block; IrInstGen **incoming_values = heap::c_allocator.allocate_nonzero(2); - incoming_values[0] = is_null_result; + incoming_values[0] = null_result; incoming_values[1] = non_null_cmp_result; return ir_build_phi_gen(ira, source_instr, 2, incoming_blocks, incoming_values, result_type); From befc09d68e5eddf9f5a2bffc5d6ed747ffd95a0e Mon Sep 17 00:00:00 2001 From: foobles Date: Wed, 20 May 2020 13:57:01 -0500 Subject: [PATCH 13/15] extracted compile-time and runtime evaluation of cmp_optional_non_optional to seperate functions --- src/ir.cpp | 108 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 63 insertions(+), 45 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 6da73cd2094c..85089e5036bb 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -16509,51 +16509,9 @@ static bool type_is_self_comparable(ZigType *ty, bool is_equality_cmp) { zig_unreachable(); } -static IrInstGen *ir_analyze_cmp_optional_non_optional(IrAnalyze *ira, IrInst *source_instr, - IrInstGen *op1, IrInstGen *op2, IrInstGen *optional, IrBinOp op_id) +static IrInstGen *ir_try_evaluate_cmp_optional_non_optional_const(IrAnalyze *ira, IrInst *source_instr, ZigType *child_type, + IrInstGen *optional, IrInstGen *non_optional, IrBinOp op_id) { - assert(op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq); - assert(optional->value->type->id == ZigTypeIdOptional); - - IrInstGen *non_optional; - if (op1 == optional) { - non_optional = op2; - } else if (op2 == optional) { - non_optional = op1; - } else { - zig_unreachable(); - } - - ZigType *child_type = optional->value->type->data.maybe.child_type; - bool child_type_matches = (child_type == non_optional->value->type); - if (!child_type_matches || !type_is_self_comparable(child_type, true)) { - ErrorMsg *msg = ir_add_error_node(ira, source_instr->source_node, buf_sprintf("cannot compare types '%s' and '%s'", - buf_ptr(&op1->value->type->name), - buf_ptr(&op2->value->type->name))); - - if (!child_type_matches) { - if (non_optional->value->type->id == ZigTypeIdOptional) { - add_error_note(ira->codegen, msg, source_instr->source_node, buf_sprintf( - "optional to optional comparison is only supported for optional pointer types")); - } else { - add_error_note(ira->codegen, msg, source_instr->source_node, - buf_sprintf("optional child type '%s' must be the same as non-optional type '%s'", - buf_ptr(&child_type->name), - buf_ptr(&non_optional->value->type->name))); - } - } else { - add_error_note(ira->codegen, msg, source_instr->source_node, - buf_sprintf("operator not supported for type '%s'", - buf_ptr(&child_type->name))); - } - return ira->codegen->invalid_inst_gen; - } - - if (child_type->id == ZigTypeIdVector) { - ir_add_error_node(ira, source_instr->source_node, buf_sprintf("TODO add comparison of optional vector")); - return ira->codegen->invalid_inst_gen; - } - if (instr_is_comptime(optional) && instr_is_comptime(non_optional)) { ZigValue *optional_val = ir_resolve_const(ira, optional, UndefBad); if (!optional_val) { @@ -16576,8 +16534,14 @@ static IrInstGen *ir_analyze_cmp_optional_non_optional(IrAnalyze *ira, IrInst *s return ret; } return ir_const_bool(ira, source_instr, (op_id != IrBinOpCmpEq)); + } else { + return nullptr; } +} +static IrInstGen *ir_evaluate_cmp_optional_non_optional(IrAnalyze *ira, IrInst *source_instr, ZigType *child_type, + IrInstGen *optional, IrInstGen *non_optional, IrBinOp op_id) +{ ZigType *result_type = ira->codegen->builtin_types.entry_bool; ir_append_basic_block_gen(&ira->new_irb, ira->new_irb.current_basic_block); @@ -16594,7 +16558,7 @@ static IrInstGen *ir_analyze_cmp_optional_non_optional(IrAnalyze *ira, IrInst *s return ira->codegen->invalid_inst_gen; } IrInstGen *non_null_cmp_result = ir_build_bin_op_gen(ira, source_instr, result_type, op_id, - optional_unwrapped, non_optional, false); + optional_unwrapped, non_optional, false); ir_build_br_gen(ira, source_instr, end_block); @@ -16613,6 +16577,60 @@ static IrInstGen *ir_analyze_cmp_optional_non_optional(IrAnalyze *ira, IrInst *s return ir_build_phi_gen(ira, source_instr, 2, incoming_blocks, incoming_values, result_type); } +static IrInstGen *ir_analyze_cmp_optional_non_optional(IrAnalyze *ira, IrInst *source_instr, + IrInstGen *op1, IrInstGen *op2, IrInstGen *optional, IrBinOp op_id) +{ + assert(op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq); + assert(optional->value->type->id == ZigTypeIdOptional); + + IrInstGen *non_optional; + if (op1 == optional) { + non_optional = op2; + } else if (op2 == optional) { + non_optional = op1; + } else { + zig_unreachable(); + } + + ZigType *child_type = optional->value->type->data.maybe.child_type; + bool child_type_matches = (child_type == non_optional->value->type); + if (!child_type_matches || !type_is_self_comparable(child_type, true)) { + ErrorMsg *msg = ir_add_error_node(ira, source_instr->source_node, buf_sprintf("cannot compare types '%s' and '%s'", + buf_ptr(&op1->value->type->name), + buf_ptr(&op2->value->type->name))); + + if (!child_type_matches) { + if (non_optional->value->type->id == ZigTypeIdOptional) { + add_error_note(ira->codegen, msg, source_instr->source_node, buf_sprintf( + "optional to optional comparison is only supported for optional pointer types")); + } else { + add_error_note(ira->codegen, msg, source_instr->source_node, + buf_sprintf("optional child type '%s' must be the same as non-optional type '%s'", + buf_ptr(&child_type->name), + buf_ptr(&non_optional->value->type->name))); + } + } else { + add_error_note(ira->codegen, msg, source_instr->source_node, + buf_sprintf("operator not supported for type '%s'", + buf_ptr(&child_type->name))); + } + return ira->codegen->invalid_inst_gen; + } + + if (child_type->id == ZigTypeIdVector) { + ir_add_error_node(ira, source_instr->source_node, buf_sprintf("TODO add comparison of optional vector")); + return ira->codegen->invalid_inst_gen; + } + + if (IrInstGen *const_result = ir_try_evaluate_cmp_optional_non_optional_const(ira, source_instr, child_type, + optional, non_optional, op_id)) + { + return const_result; + } + + return ir_evaluate_cmp_optional_non_optional(ira, source_instr, child_type, optional, non_optional, op_id); +} + static IrInstGen *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstSrcBinOp *bin_op_instruction) { IrInstGen *op1 = bin_op_instruction->op1->child; if (type_is_invalid(op1->value->type)) From eaf6510a327fb05951b7bf745073f425d9cc0496 Mon Sep 17 00:00:00 2001 From: foobles Date: Wed, 20 May 2020 14:17:59 -0500 Subject: [PATCH 14/15] gave magic number a name --- src/ir.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 85089e5036bb..a3d637a8aaab 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -16567,14 +16567,15 @@ static IrInstGen *ir_evaluate_cmp_optional_non_optional(IrAnalyze *ira, IrInst * ir_build_br_gen(ira, source_instr, end_block); ir_set_cursor_at_end_gen(&ira->new_irb, end_block); - IrBasicBlockGen **incoming_blocks = heap::c_allocator.allocate_nonzero(2); + int incoming_count = 2; + IrBasicBlockGen **incoming_blocks = heap::c_allocator.allocate_nonzero(incoming_count); incoming_blocks[0] = null_block; incoming_blocks[1] = non_null_block; - IrInstGen **incoming_values = heap::c_allocator.allocate_nonzero(2); + IrInstGen **incoming_values = heap::c_allocator.allocate_nonzero(incoming_count); incoming_values[0] = null_result; incoming_values[1] = non_null_cmp_result; - return ir_build_phi_gen(ira, source_instr, 2, incoming_blocks, incoming_values, result_type); + return ir_build_phi_gen(ira, source_instr, incoming_count, incoming_blocks, incoming_values, result_type); } static IrInstGen *ir_analyze_cmp_optional_non_optional(IrAnalyze *ira, IrInst *source_instr, From 13c1f01bb38612ae4f9d0415226fcde60a5c8188 Mon Sep 17 00:00:00 2001 From: foobles Date: Wed, 20 May 2020 15:09:27 -0500 Subject: [PATCH 15/15] improved assertions --- src/ir.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/ir.cpp b/src/ir.cpp index a3d637a8aaab..e1ad88900757 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15,6 +15,7 @@ #include "softfloat.hpp" #include "util.hpp" #include "mem_list.hpp" +#include "all_types.hpp" #include @@ -16512,6 +16513,11 @@ static bool type_is_self_comparable(ZigType *ty, bool is_equality_cmp) { static IrInstGen *ir_try_evaluate_cmp_optional_non_optional_const(IrAnalyze *ira, IrInst *source_instr, ZigType *child_type, IrInstGen *optional, IrInstGen *non_optional, IrBinOp op_id) { + assert(optional->value->type->id == ZigTypeIdOptional); + assert(optional->value->type->data.maybe.child_type == non_optional->value->type); + assert(non_optional->value->type == child_type); + assert(op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq); + if (instr_is_comptime(optional) && instr_is_comptime(non_optional)) { ZigValue *optional_val = ir_resolve_const(ira, optional, UndefBad); if (!optional_val) { @@ -16542,6 +16548,11 @@ static IrInstGen *ir_try_evaluate_cmp_optional_non_optional_const(IrAnalyze *ira static IrInstGen *ir_evaluate_cmp_optional_non_optional(IrAnalyze *ira, IrInst *source_instr, ZigType *child_type, IrInstGen *optional, IrInstGen *non_optional, IrBinOp op_id) { + assert(optional->value->type->id == ZigTypeIdOptional); + assert(optional->value->type->data.maybe.child_type == non_optional->value->type); + assert(non_optional->value->type == child_type); + assert(op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq); + ZigType *result_type = ira->codegen->builtin_types.entry_bool; ir_append_basic_block_gen(&ira->new_irb, ira->new_irb.current_basic_block); @@ -16558,7 +16569,7 @@ static IrInstGen *ir_evaluate_cmp_optional_non_optional(IrAnalyze *ira, IrInst * return ira->codegen->invalid_inst_gen; } IrInstGen *non_null_cmp_result = ir_build_bin_op_gen(ira, source_instr, result_type, op_id, - optional_unwrapped, non_optional, false); + optional_unwrapped, non_optional, false); // safety check unnecessary for comparison operators ir_build_br_gen(ira, source_instr, end_block); @@ -16583,6 +16594,7 @@ static IrInstGen *ir_analyze_cmp_optional_non_optional(IrAnalyze *ira, IrInst *s { assert(op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq); assert(optional->value->type->id == ZigTypeIdOptional); + assert(get_src_ptr_type(optional->value->type) == nullptr); IrInstGen *non_optional; if (op1 == optional) {