| author | |
| committer | |
| log | 960914a073c367883c9fdf54e900890a6aefc05f |
| tree | 4dca5efa7967f0ecca20bd0d852005008a1ec925 |
| parent | 63a2f9a8b2251ffdc37d5f28dfbd3f6be1bd7908 |
when the enum is the tag type of the union and is comptime known
to be of a void field of the union
See #6425 files changed, 129 insertions(+), 8 deletions(-)
src/analyze.cpp+10-5| ... | ... | @@ -2244,12 +2244,10 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) { |
| 2244 | 2244 | TypeTableEntry *enum_type = analyze_type_expr(g, scope, enum_type_node); |
| 2245 | 2245 | if (type_is_invalid(enum_type)) { |
| 2246 | 2246 | union_type->data.unionation.is_invalid = true; |
| 2247 | union_type->data.unionation.embedded_in_current = false; | |
| 2248 | 2247 | return; |
| 2249 | 2248 | } |
| 2250 | 2249 | if (enum_type->id != TypeTableEntryIdEnum) { |
| 2251 | 2250 | union_type->data.unionation.is_invalid = true; |
| 2252 | union_type->data.unionation.embedded_in_current = false; | |
| 2253 | 2251 | add_node_error(g, enum_type_node, |
| 2254 | 2252 | buf_sprintf("expected enum tag type, found '%s'", buf_ptr(&enum_type->name))); |
| 2255 | 2253 | return; |
| ... | ... | @@ -3319,7 +3317,7 @@ TypeStructField *find_struct_type_field(TypeTableEntry *type_entry, Buf *name) { |
| 3319 | 3317 | |
| 3320 | 3318 | TypeUnionField *find_union_type_field(TypeTableEntry *type_entry, Buf *name) { |
| 3321 | 3319 | assert(type_entry->id == TypeTableEntryIdUnion); |
| 3322 | assert(type_entry->data.unionation.complete); | |
| 3320 | assert(type_entry->data.unionation.zero_bits_known); | |
| 3323 | 3321 | for (uint32_t i = 0; i < type_entry->data.unionation.src_field_count; i += 1) { |
| 3324 | 3322 | TypeUnionField *field = &type_entry->data.unionation.fields[i]; |
| 3325 | 3323 | if (buf_eql_buf(field->enum_field->name, name)) { |
| ... | ... | @@ -3331,7 +3329,7 @@ TypeUnionField *find_union_type_field(TypeTableEntry *type_entry, Buf *name) { |
| 3331 | 3329 | |
| 3332 | 3330 | TypeUnionField *find_union_field_by_tag(TypeTableEntry *type_entry, const BigInt *tag) { |
| 3333 | 3331 | assert(type_entry->id == TypeTableEntryIdUnion); |
| 3334 | assert(type_entry->data.unionation.complete); | |
| 3332 | assert(type_entry->data.unionation.zero_bits_known); | |
| 3335 | 3333 | assert(type_entry->data.unionation.gen_tag_index != SIZE_MAX); |
| 3336 | 3334 | for (uint32_t i = 0; i < type_entry->data.unionation.src_field_count; i += 1) { |
| 3337 | 3335 | TypeUnionField *field = &type_entry->data.unionation.fields[i]; |
| ... | ... | @@ -3888,7 +3886,6 @@ bool handle_is_ptr(TypeTableEntry *type_entry) { |
| 3888 | 3886 | return false; |
| 3889 | 3887 | case TypeTableEntryIdArray: |
| 3890 | 3888 | case TypeTableEntryIdStruct: |
| 3891 | case TypeTableEntryIdUnion: | |
| 3892 | 3889 | return type_has_bits(type_entry); |
| 3893 | 3890 | case TypeTableEntryIdErrorUnion: |
| 3894 | 3891 | return type_has_bits(type_entry->data.error.child_type); |
| ... | ... | @@ -3896,6 +3893,14 @@ bool handle_is_ptr(TypeTableEntry *type_entry) { |
| 3896 | 3893 | return type_has_bits(type_entry->data.maybe.child_type) && |
| 3897 | 3894 | type_entry->data.maybe.child_type->id != TypeTableEntryIdPointer && |
| 3898 | 3895 | type_entry->data.maybe.child_type->id != TypeTableEntryIdFn; |
| 3896 | case TypeTableEntryIdUnion: | |
| 3897 | assert(type_entry->data.unionation.complete); | |
| 3898 | if (type_entry->data.unionation.gen_field_count == 0) | |
| 3899 | return false; | |
| 3900 | if (!type_has_bits(type_entry)) | |
| 3901 | return false; | |
| 3902 | return true; | |
| 3903 | ||
| 3899 | 3904 | } |
| 3900 | 3905 | zig_unreachable(); |
| 3901 | 3906 | } |
src/codegen.cpp+2-3| ... | ... | @@ -3946,8 +3946,6 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { |
| 3946 | 3946 | case TypeTableEntryIdUnion: |
| 3947 | 3947 | { |
| 3948 | 3948 | LLVMTypeRef union_type_ref = type_entry->data.unionation.union_type_ref; |
| 3949 | ConstExprValue *payload_value = const_val->data.x_union.payload; | |
| 3950 | assert(payload_value != nullptr); | |
| 3951 | 3949 | |
| 3952 | 3950 | if (type_entry->data.unionation.gen_field_count == 0) { |
| 3953 | 3951 | if (type_entry->data.unionation.gen_tag_index == SIZE_MAX) { |
| ... | ... | @@ -3960,7 +3958,8 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { |
| 3960 | 3958 | |
| 3961 | 3959 | LLVMValueRef union_value_ref; |
| 3962 | 3960 | bool make_unnamed_struct; |
| 3963 | if (!type_has_bits(payload_value->type)) { | |
| 3961 | ConstExprValue *payload_value = const_val->data.x_union.payload; | |
| 3962 | if (payload_value == nullptr || !type_has_bits(payload_value->type)) { | |
| 3964 | 3963 | if (type_entry->data.unionation.gen_tag_index == SIZE_MAX) |
| 3965 | 3964 | return LLVMGetUndef(type_entry->type_ref); |
| 3966 | 3965 |
src/ir.cpp+79| ... | ... | @@ -7468,6 +7468,17 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, |
| 7468 | 7468 | } |
| 7469 | 7469 | } |
| 7470 | 7470 | |
| 7471 | // implicit enum to union which has the enum as the tag type | |
| 7472 | if (expected_type->id == TypeTableEntryIdUnion && actual_type->id == TypeTableEntryIdEnum && | |
| 7473 | (expected_type->data.unionation.decl_node->data.container_decl.auto_enum || | |
| 7474 | expected_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr)) | |
| 7475 | { | |
| 7476 | type_ensure_zero_bits_known(ira->codegen, expected_type); | |
| 7477 | if (expected_type->data.unionation.tag_type == actual_type) { | |
| 7478 | return ImplicitCastMatchResultYes; | |
| 7479 | } | |
| 7480 | } | |
| 7481 | ||
| 7471 | 7482 | // implicit undefined literal to anything |
| 7472 | 7483 | if (actual_type->id == TypeTableEntryIdUndefLit) { |
| 7473 | 7484 | return ImplicitCastMatchResultYes; |
| ... | ... | @@ -8370,6 +8381,63 @@ static IrInstruction *ir_analyze_undefined_to_anything(IrAnalyze *ira, IrInstruc |
| 8370 | 8381 | return result; |
| 8371 | 8382 | } |
| 8372 | 8383 | |
| 8384 | static IrInstruction *ir_analyze_enum_to_union(IrAnalyze *ira, IrInstruction *source_instr, | |
| 8385 | IrInstruction *target, TypeTableEntry *wanted_type) | |
| 8386 | { | |
| 8387 | assert(wanted_type->id == TypeTableEntryIdUnion); | |
| 8388 | assert(target->value.type->id == TypeTableEntryIdEnum); | |
| 8389 | ||
| 8390 | if (instr_is_comptime(target)) { | |
| 8391 | ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); | |
| 8392 | if (!val) | |
| 8393 | return ira->codegen->invalid_instruction; | |
| 8394 | TypeUnionField *union_field = find_union_field_by_tag(wanted_type, &val->data.x_enum_tag); | |
| 8395 | assert(union_field != nullptr); | |
| 8396 | type_ensure_zero_bits_known(ira->codegen, union_field->type_entry); | |
| 8397 | if (!union_field->type_entry->zero_bits) { | |
| 8398 | AstNode *field_node = wanted_type->data.unionation.decl_node->data.container_decl.fields.at( | |
| 8399 | union_field->enum_field->decl_index); | |
| 8400 | ErrorMsg *msg = ir_add_error(ira, source_instr, | |
| 8401 | buf_sprintf("cast to union '%s' must initialize '%s' field '%s'", | |
| 8402 | buf_ptr(&wanted_type->name), | |
| 8403 | buf_ptr(&union_field->type_entry->name), | |
| 8404 | buf_ptr(union_field->name))); | |
| 8405 | add_error_note(ira->codegen, msg, field_node, | |
| 8406 | buf_sprintf("field '%s' declared here", buf_ptr(union_field->name))); | |
| 8407 | return ira->codegen->invalid_instruction; | |
| 8408 | } | |
| 8409 | IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, | |
| 8410 | source_instr->source_node, wanted_type); | |
| 8411 | result->value.special = ConstValSpecialStatic; | |
| 8412 | result->value.type = wanted_type; | |
| 8413 | bigint_init_bigint(&result->value.data.x_union.tag, &val->data.x_enum_tag); | |
| 8414 | return result; | |
| 8415 | } | |
| 8416 | ||
| 8417 | // if the union has all fields 0 bits, we can do it | |
| 8418 | // and in fact it's a noop cast because the union value is just the enum value | |
| 8419 | if (wanted_type->data.unionation.gen_field_count == 0) { | |
| 8420 | IrInstruction *result = ir_build_cast(&ira->new_irb, target->scope, target->source_node, wanted_type, target, CastOpNoop); | |
| 8421 | result->value.type = wanted_type; | |
| 8422 | return result; | |
| 8423 | } | |
| 8424 | ||
| 8425 | ErrorMsg *msg = ir_add_error(ira, source_instr, | |
| 8426 | buf_sprintf("runtime cast to union '%s' which has non-void fields", | |
| 8427 | buf_ptr(&wanted_type->name))); | |
| 8428 | for (uint32_t i = 0; i < wanted_type->data.unionation.src_field_count; i += 1) { | |
| 8429 | TypeUnionField *union_field = &wanted_type->data.unionation.fields[i]; | |
| 8430 | if (type_has_bits(union_field->type_entry)) { | |
| 8431 | AstNode *field_node = wanted_type->data.unionation.decl_node->data.container_decl.fields.at(i); | |
| 8432 | add_error_note(ira->codegen, msg, field_node, | |
| 8433 | buf_sprintf("field '%s' has type '%s'", | |
| 8434 | buf_ptr(union_field->name), | |
| 8435 | buf_ptr(&union_field->type_entry->name))); | |
| 8436 | } | |
| 8437 | } | |
| 8438 | return ira->codegen->invalid_instruction; | |
| 8439 | } | |
| 8440 | ||
| 8373 | 8441 | static IrInstruction *ir_analyze_widen_or_shorten(IrAnalyze *ira, IrInstruction *source_instr, |
| 8374 | 8442 | IrInstruction *target, TypeTableEntry *wanted_type) |
| 8375 | 8443 | { |
| ... | ... | @@ -8919,6 +8987,17 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst |
| 8919 | 8987 | } |
| 8920 | 8988 | } |
| 8921 | 8989 | |
| 8990 | // explicit enum to union which has the enum as the tag type | |
| 8991 | if (wanted_type->id == TypeTableEntryIdUnion && actual_type->id == TypeTableEntryIdEnum && | |
| 8992 | (wanted_type->data.unionation.decl_node->data.container_decl.auto_enum || | |
| 8993 | wanted_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr)) | |
| 8994 | { | |
| 8995 | type_ensure_zero_bits_known(ira->codegen, wanted_type); | |
| 8996 | if (wanted_type->data.unionation.tag_type == actual_type) { | |
| 8997 | return ir_analyze_enum_to_union(ira, source_instr, value, wanted_type); | |
| 8998 | } | |
| 8999 | } | |
| 9000 | ||
| 8922 | 9001 | // explicit cast from undefined to anything |
| 8923 | 9002 | if (actual_type->id == TypeTableEntryIdUndefLit) { |
| 8924 | 9003 | return ir_analyze_undefined_to_anything(ira, source_instr, value, wanted_type); |
test/cases/union.zig+7| ... | ... | @@ -190,3 +190,10 @@ test "cast union to tag type of union" { |
| 190 | 190 | fn testCastUnionToTagType(x: &const TheUnion) { |
| 191 | 191 | assert(TheTag(*x) == TheTag.B); |
| 192 | 192 | } |
| 193 | ||
| 194 | test "cast tag type of union to union" { | |
| 195 | var x: Value2 = Letter2.B; | |
| 196 | assert(Letter2(x) == Letter2.B); | |
| 197 | } | |
| 198 | const Letter2 = enum { A, B, C }; | |
| 199 | const Value2 = union(Letter2) { A: i32, B, C, }; |
test/compile_errors.zig+31| ... | ... | @@ -2696,4 +2696,35 @@ pub fn addCases(cases: &tests.CompileErrorContext) { |
| 2696 | 2696 | , |
| 2697 | 2697 | ".tmp_source.zig:6:16: error: enum 'Foo' has no tag matching integer value 0", |
| 2698 | 2698 | ".tmp_source.zig:1:13: note: 'Foo' declared here"); |
| 2699 | ||
| 2700 | cases.add("comptime cast enum to union but field has payload", | |
| 2701 | \\const Letter = enum { A, B, C }; | |
| 2702 | \\const Value = union(Letter) { | |
| 2703 | \\ A: i32, | |
| 2704 | \\ B, | |
| 2705 | \\ C, | |
| 2706 | \\}; | |
| 2707 | \\export fn entry() { | |
| 2708 | \\ var x: Value = Letter.A; | |
| 2709 | \\} | |
| 2710 | , | |
| 2711 | ".tmp_source.zig:8:26: error: cast to union 'Value' must initialize 'i32' field 'A'", | |
| 2712 | ".tmp_source.zig:3:5: note: field 'A' declared here"); | |
| 2713 | ||
| 2714 | cases.add("runtime cast to union which has non-void fields", | |
| 2715 | \\const Letter = enum { A, B, C }; | |
| 2716 | \\const Value = union(Letter) { | |
| 2717 | \\ A: i32, | |
| 2718 | \\ B, | |
| 2719 | \\ C, | |
| 2720 | \\}; | |
| 2721 | \\export fn entry() { | |
| 2722 | \\ foo(Letter.A); | |
| 2723 | \\} | |
| 2724 | \\fn foo(l: Letter) { | |
| 2725 | \\ var x: Value = l; | |
| 2726 | \\} | |
| 2727 | , | |
| 2728 | ".tmp_source.zig:11:20: error: runtime cast to union 'Value' which has non-void fields", | |
| 2729 | ".tmp_source.zig:3:5: note: field 'A' has type 'i32'"); | |
| 2699 | 2730 | } |