From 2715f6fdb8bca1c88c58dac047889711359e193b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 5 Dec 2017 21:10:47 -0500 Subject: [PATCH 1/5] allow implicit cast from union to its enum tag type closes #642 --- src/ir.cpp | 11 +++++++++++ test/cases/union.zig | 8 ++++++++ 2 files changed, 19 insertions(+) diff --git a/src/ir.cpp b/src/ir.cpp index 86fa8c3566..0ab63a02fe 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7468,6 +7468,17 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, } } + // implicit union to its enum tag type + if (expected_type->id == TypeTableEntryIdEnum && actual_type->id == TypeTableEntryIdUnion && + (actual_type->data.unionation.decl_node->data.container_decl.auto_enum || + actual_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr)) + { + type_ensure_zero_bits_known(ira->codegen, actual_type); + if (actual_type->data.unionation.tag_type == expected_type) { + return ImplicitCastMatchResultYes; + } + } + // implicit enum to union which has the enum as the tag type if (expected_type->id == TypeTableEntryIdUnion && actual_type->id == TypeTableEntryIdEnum && (expected_type->data.unionation.decl_node->data.container_decl.auto_enum || diff --git a/test/cases/union.zig b/test/cases/union.zig index cd7fad4c88..5657767dce 100644 --- a/test/cases/union.zig +++ b/test/cases/union.zig @@ -197,3 +197,11 @@ test "cast tag type of union to union" { } const Letter2 = enum { A, B, C }; const Value2 = union(Letter2) { A: i32, B, C, }; + +test "implicit cast union to its tag type" { + var x: Value2 = Letter2.B; + giveMeLetterB(x); +} +fn giveMeLetterB(x: Letter2) { + assert(x == Value2.B); +} From c49ee9f632dd5ee7f341e9093234f39c19a32115 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 5 Dec 2017 21:33:24 -0500 Subject: [PATCH 2/5] allow union and its tag type to peer resolve to the tag type --- src/ir.cpp | 132 ++++++++++++++++++++++++++++++++----------- test/cases/union.zig | 1 + 2 files changed, 101 insertions(+), 32 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 0ab63a02fe..d9d70346bd 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7519,33 +7519,52 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod IrInstruction *cur_inst = instructions[i]; TypeTableEntry *cur_type = cur_inst->value.type; TypeTableEntry *prev_type = prev_inst->value.type; + if (type_is_invalid(cur_type)) { return cur_type; - } else if (prev_type->id == TypeTableEntryIdUnreachable) { + } + + if (prev_type->id == TypeTableEntryIdUnreachable) { prev_inst = cur_inst; - } else if (cur_type->id == TypeTableEntryIdUnreachable) { + } + + if (cur_type->id == TypeTableEntryIdUnreachable) { continue; - } else if (prev_type->id == TypeTableEntryIdPureError) { + } + + if (prev_type->id == TypeTableEntryIdPureError) { prev_inst = cur_inst; continue; - } else if (prev_type->id == TypeTableEntryIdNullLit) { + } + + if (prev_type->id == TypeTableEntryIdNullLit) { prev_inst = cur_inst; continue; - } else if (cur_type->id == TypeTableEntryIdPureError) { + } + + if (cur_type->id == TypeTableEntryIdPureError) { if (prev_type->id == TypeTableEntryIdArray) { convert_to_const_slice = true; } any_are_pure_error = true; continue; - } else if (cur_type->id == TypeTableEntryIdNullLit) { + } + + if (cur_type->id == TypeTableEntryIdNullLit) { any_are_null = true; continue; - } else if (types_match_const_cast_only(prev_type, cur_type)) { + } + + if (types_match_const_cast_only(prev_type, cur_type)) { continue; - } else if (types_match_const_cast_only(cur_type, prev_type)) { + } + + if (types_match_const_cast_only(cur_type, prev_type)) { prev_inst = cur_inst; continue; - } else if (prev_type->id == TypeTableEntryIdInt && + } + + if (prev_type->id == TypeTableEntryIdInt && cur_type->id == TypeTableEntryIdInt && prev_type->data.integral.is_signed == cur_type->data.integral.is_signed) { @@ -7553,36 +7572,52 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod prev_inst = cur_inst; } continue; - } else if (prev_type->id == TypeTableEntryIdFloat && + } + + if (prev_type->id == TypeTableEntryIdFloat && cur_type->id == TypeTableEntryIdFloat) { if (cur_type->data.floating.bit_count > prev_type->data.floating.bit_count) { prev_inst = cur_inst; } - } else if (prev_type->id == TypeTableEntryIdErrorUnion && + } + + if (prev_type->id == TypeTableEntryIdErrorUnion && types_match_const_cast_only(prev_type->data.error.child_type, cur_type)) { continue; - } else if (cur_type->id == TypeTableEntryIdErrorUnion && + } + + if (cur_type->id == TypeTableEntryIdErrorUnion && types_match_const_cast_only(cur_type->data.error.child_type, prev_type)) { prev_inst = cur_inst; continue; - } else if (prev_type->id == TypeTableEntryIdMaybe && + } + + if (prev_type->id == TypeTableEntryIdMaybe && types_match_const_cast_only(prev_type->data.maybe.child_type, cur_type)) { continue; - } else if (cur_type->id == TypeTableEntryIdMaybe && + } + + if (cur_type->id == TypeTableEntryIdMaybe && types_match_const_cast_only(cur_type->data.maybe.child_type, prev_type)) { prev_inst = cur_inst; continue; - } else if (cur_type->id == TypeTableEntryIdUndefLit) { + } + + if (cur_type->id == TypeTableEntryIdUndefLit) { continue; - } else if (prev_type->id == TypeTableEntryIdUndefLit) { + } + + if (prev_type->id == TypeTableEntryIdUndefLit) { prev_inst = cur_inst; continue; - } else if (prev_type->id == TypeTableEntryIdNumLitInt || + } + + if (prev_type->id == TypeTableEntryIdNumLitInt || prev_type->id == TypeTableEntryIdNumLitFloat) { if (ir_num_lit_fits_in_other_type(ira, prev_inst, cur_type, false)) { @@ -7591,7 +7626,9 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod } else { return ira->codegen->builtin_types.entry_invalid; } - } else if (cur_type->id == TypeTableEntryIdNumLitInt || + } + + if (cur_type->id == TypeTableEntryIdNumLitInt || cur_type->id == TypeTableEntryIdNumLitFloat) { if (ir_num_lit_fits_in_other_type(ira, cur_inst, prev_type, false)) { @@ -7599,20 +7636,26 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod } else { return ira->codegen->builtin_types.entry_invalid; } - } else if (cur_type->id == TypeTableEntryIdArray && prev_type->id == TypeTableEntryIdArray && + } + + if (cur_type->id == TypeTableEntryIdArray && prev_type->id == TypeTableEntryIdArray && cur_type->data.array.len != prev_type->data.array.len && types_match_const_cast_only(cur_type->data.array.child_type, prev_type->data.array.child_type)) { convert_to_const_slice = true; prev_inst = cur_inst; continue; - } else if (cur_type->id == TypeTableEntryIdArray && prev_type->id == TypeTableEntryIdArray && + } + + if (cur_type->id == TypeTableEntryIdArray && prev_type->id == TypeTableEntryIdArray && cur_type->data.array.len != prev_type->data.array.len && types_match_const_cast_only(prev_type->data.array.child_type, cur_type->data.array.child_type)) { convert_to_const_slice = true; continue; - } else if (cur_type->id == TypeTableEntryIdArray && is_slice(prev_type) && + } + + if (cur_type->id == TypeTableEntryIdArray && is_slice(prev_type) && (prev_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const || cur_type->data.array.len == 0) && types_match_const_cast_only(prev_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type, @@ -7620,7 +7663,9 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod { convert_to_const_slice = false; continue; - } else if (prev_type->id == TypeTableEntryIdArray && is_slice(cur_type) && + } + + if (prev_type->id == TypeTableEntryIdArray && is_slice(cur_type) && (cur_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const || prev_type->data.array.len == 0) && types_match_const_cast_only(cur_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type, @@ -7629,17 +7674,40 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod prev_inst = cur_inst; convert_to_const_slice = false; continue; - } else { - ErrorMsg *msg = ir_add_error_node(ira, source_node, - buf_sprintf("incompatible types: '%s' and '%s'", - buf_ptr(&prev_type->name), buf_ptr(&cur_type->name))); - add_error_note(ira->codegen, msg, prev_inst->source_node, - buf_sprintf("type '%s' here", buf_ptr(&prev_type->name))); - add_error_note(ira->codegen, msg, cur_inst->source_node, - buf_sprintf("type '%s' here", buf_ptr(&cur_type->name))); - - return ira->codegen->builtin_types.entry_invalid; } + + if (prev_type->id == TypeTableEntryIdEnum && cur_type->id == TypeTableEntryIdUnion && + (cur_type->data.unionation.decl_node->data.container_decl.auto_enum || cur_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr)) + { + type_ensure_zero_bits_known(ira->codegen, cur_type); + if (type_is_invalid(cur_type)) + return ira->codegen->builtin_types.entry_invalid; + if (cur_type->data.unionation.tag_type == prev_type) { + continue; + } + } + + if (cur_type->id == TypeTableEntryIdEnum && prev_type->id == TypeTableEntryIdUnion && + (prev_type->data.unionation.decl_node->data.container_decl.auto_enum || prev_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr)) + { + type_ensure_zero_bits_known(ira->codegen, prev_type); + if (type_is_invalid(prev_type)) + return ira->codegen->builtin_types.entry_invalid; + if (prev_type->data.unionation.tag_type == cur_type) { + prev_inst = cur_inst; + continue; + } + } + + ErrorMsg *msg = ir_add_error_node(ira, source_node, + buf_sprintf("incompatible types: '%s' and '%s'", + buf_ptr(&prev_type->name), buf_ptr(&cur_type->name))); + add_error_note(ira->codegen, msg, prev_inst->source_node, + buf_sprintf("type '%s' here", buf_ptr(&prev_type->name))); + add_error_note(ira->codegen, msg, cur_inst->source_node, + buf_sprintf("type '%s' here", buf_ptr(&cur_type->name))); + + return ira->codegen->builtin_types.entry_invalid; } if (convert_to_const_slice) { assert(prev_inst->value.type->id == TypeTableEntryIdArray); diff --git a/test/cases/union.zig b/test/cases/union.zig index 5657767dce..7c1c04c711 100644 --- a/test/cases/union.zig +++ b/test/cases/union.zig @@ -200,6 +200,7 @@ const Value2 = union(Letter2) { A: i32, B, C, }; test "implicit cast union to its tag type" { var x: Value2 = Letter2.B; + assert(x == Letter2.B); giveMeLetterB(x); } fn giveMeLetterB(x: Letter2) { From bb6b4f8db2bf793f4118ee68a05ff0b4df52c318 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 5 Dec 2017 22:15:33 -0500 Subject: [PATCH 3/5] fix enum with 1 member causing segfault closes #647 --- src/ir.cpp | 9 +++++++-- test/cases/enum.zig | 17 +++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index d9d70346bd..0eaf6bdf01 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -9504,6 +9504,10 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, bin_op_instruction->base.source_node, instructions, 2); if (type_is_invalid(resolved_type)) return resolved_type; + type_ensure_zero_bits_known(ira->codegen, resolved_type); + if (type_is_invalid(resolved_type)) + return resolved_type; + AstNode *source_node = bin_op_instruction->base.source_node; switch (resolved_type->id) { @@ -9568,7 +9572,8 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp ConstExprValue *op1_val = &casted_op1->value; ConstExprValue *op2_val = &casted_op2->value; - if ((value_is_comptime(op1_val) && value_is_comptime(op2_val)) || resolved_type->id == TypeTableEntryIdVoid) { + bool one_possible_value = !type_requires_comptime(resolved_type) && !type_has_bits(resolved_type); + if (one_possible_value || (value_is_comptime(op1_val) && value_is_comptime(op2_val))) { bool answer; if (resolved_type->id == TypeTableEntryIdNumLitFloat || resolved_type->id == TypeTableEntryIdFloat) { Cmp cmp_result = float_cmp(op1_val, op2_val); @@ -9577,7 +9582,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp Cmp cmp_result = bigint_cmp(&op1_val->data.x_bigint, &op2_val->data.x_bigint); answer = resolve_cmp_op_id(op_id, cmp_result); } else { - bool are_equal = resolved_type->id == TypeTableEntryIdVoid || const_values_equal(op1_val, op2_val); + bool are_equal = one_possible_value || const_values_equal(op1_val, op2_val); if (op_id == IrBinOpCmpEq) { answer = are_equal; } else if (op_id == IrBinOpCmpNotEq) { diff --git a/test/cases/enum.zig b/test/cases/enum.zig index d15614f9fd..cad3289c39 100644 --- a/test/cases/enum.zig +++ b/test/cases/enum.zig @@ -349,3 +349,20 @@ test "cast integer literal to enum" { assert(MultipleChoice2(0) == MultipleChoice2.Unspecified1); assert(MultipleChoice2(40) == MultipleChoice2.B); } + +const EnumWithOneMember = enum { + Eof, +}; + +fn doALoopThing(id: EnumWithOneMember) { + while (true) { + if (id == EnumWithOneMember.Eof) { + break; + } + @compileError("above if condition should be comptime"); + } +} + +test "comparison operator on enum with one member is comptime known" { + doALoopThing(EnumWithOneMember.Eof); +} From f464fe14f4ece387935dbe2bb6b73ecf466c3f83 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 5 Dec 2017 22:26:17 -0500 Subject: [PATCH 4/5] switch on enum which only has 1 field is comptime known closes #593 --- src/ir.cpp | 10 ++++++++++ test/cases/enum.zig | 11 +++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/ir.cpp b/src/ir.cpp index 0eaf6bdf01..6cd8b678c9 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -13174,6 +13174,16 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira, return tag_type; } case TypeTableEntryIdEnum: { + type_ensure_zero_bits_known(ira->codegen, target_type); + if (type_is_invalid(target_type)) + return ira->codegen->builtin_types.entry_invalid; + if (target_type->data.enumeration.src_field_count < 2) { + TypeEnumField *only_field = &target_type->data.enumeration.fields[0]; + ConstExprValue *out_val = ir_build_const_from(ira, &switch_target_instruction->base); + bigint_init_bigint(&out_val->data.x_enum_tag, &only_field->value); + return target_type; + } + if (pointee_val) { ConstExprValue *out_val = ir_build_const_from(ira, &switch_target_instruction->base); bigint_init_bigint(&out_val->data.x_enum_tag, &pointee_val->data.x_enum_tag); diff --git a/test/cases/enum.zig b/test/cases/enum.zig index cad3289c39..6352a23afa 100644 --- a/test/cases/enum.zig +++ b/test/cases/enum.zig @@ -366,3 +366,14 @@ fn doALoopThing(id: EnumWithOneMember) { test "comparison operator on enum with one member is comptime known" { doALoopThing(EnumWithOneMember.Eof); } + +const State = enum { + Start, +}; +test "switch on enum with one member is comptime known" { + var state = State.Start; + switch (state) { + State.Start => return, + } + @compileError("analysis should not reach here"); +} From 249cb2aa30bbdd0c30f24ef18097e3b1cd3e0da5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 5 Dec 2017 22:39:36 -0500 Subject: [PATCH 5/5] fix regressions from previous commit c49ee9f632dd5ee7f341e9093234f39c19a32115 broke the tests and this fixes them --- src/ir.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 6cd8b678c9..43ea16c976 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7526,6 +7526,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod if (prev_type->id == TypeTableEntryIdUnreachable) { prev_inst = cur_inst; + continue; } if (cur_type->id == TypeTableEntryIdUnreachable) { @@ -7574,12 +7575,11 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod continue; } - if (prev_type->id == TypeTableEntryIdFloat && - cur_type->id == TypeTableEntryIdFloat) - { + if (prev_type->id == TypeTableEntryIdFloat && cur_type->id == TypeTableEntryIdFloat) { if (cur_type->data.floating.bit_count > prev_type->data.floating.bit_count) { prev_inst = cur_inst; } + continue; } if (prev_type->id == TypeTableEntryIdErrorUnion &&