| 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,12 +2244,10 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) { |
| 2244 | TypeTableEntry *enum_type = analyze_type_expr(g, scope, enum_type_node); | 2244 | TypeTableEntry *enum_type = analyze_type_expr(g, scope, enum_type_node); |
| 2245 | if (type_is_invalid(enum_type)) { | 2245 | if (type_is_invalid(enum_type)) { |
| 2246 | union_type->data.unionation.is_invalid = true; | 2246 | union_type->data.unionation.is_invalid = true; |
| 2247 | union_type->data.unionation.embedded_in_current = false; | ||
| 2248 | return; | 2247 | return; |
| 2249 | } | 2248 | } |
| 2250 | if (enum_type->id != TypeTableEntryIdEnum) { | 2249 | if (enum_type->id != TypeTableEntryIdEnum) { |
| 2251 | union_type->data.unionation.is_invalid = true; | 2250 | union_type->data.unionation.is_invalid = true; |
| 2252 | union_type->data.unionation.embedded_in_current = false; | ||
| 2253 | add_node_error(g, enum_type_node, | 2251 | add_node_error(g, enum_type_node, |
| 2254 | buf_sprintf("expected enum tag type, found '%s'", buf_ptr(&enum_type->name))); | 2252 | buf_sprintf("expected enum tag type, found '%s'", buf_ptr(&enum_type->name))); |
| 2255 | return; | 2253 | return; |
| ... | @@ -3319,7 +3317,7 @@ TypeStructField *find_struct_type_field(TypeTableEntry *type_entry, Buf *name) { | ... | @@ -3319,7 +3317,7 @@ TypeStructField *find_struct_type_field(TypeTableEntry *type_entry, Buf *name) { |
| 3319 | 3317 | ||
| 3320 | TypeUnionField *find_union_type_field(TypeTableEntry *type_entry, Buf *name) { | 3318 | TypeUnionField *find_union_type_field(TypeTableEntry *type_entry, Buf *name) { |
| 3321 | assert(type_entry->id == TypeTableEntryIdUnion); | 3319 | assert(type_entry->id == TypeTableEntryIdUnion); |
| 3322 | assert(type_entry->data.unionation.complete); | 3320 | assert(type_entry->data.unionation.zero_bits_known); |
| 3323 | for (uint32_t i = 0; i < type_entry->data.unionation.src_field_count; i += 1) { | 3321 | for (uint32_t i = 0; i < type_entry->data.unionation.src_field_count; i += 1) { |
| 3324 | TypeUnionField *field = &type_entry->data.unionation.fields[i]; | 3322 | TypeUnionField *field = &type_entry->data.unionation.fields[i]; |
| 3325 | if (buf_eql_buf(field->enum_field->name, name)) { | 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,7 +3329,7 @@ TypeUnionField *find_union_type_field(TypeTableEntry *type_entry, Buf *name) { |
| 3331 | 3329 | ||
| 3332 | TypeUnionField *find_union_field_by_tag(TypeTableEntry *type_entry, const BigInt *tag) { | 3330 | TypeUnionField *find_union_field_by_tag(TypeTableEntry *type_entry, const BigInt *tag) { |
| 3333 | assert(type_entry->id == TypeTableEntryIdUnion); | 3331 | assert(type_entry->id == TypeTableEntryIdUnion); |
| 3334 | assert(type_entry->data.unionation.complete); | 3332 | assert(type_entry->data.unionation.zero_bits_known); |
| 3335 | assert(type_entry->data.unionation.gen_tag_index != SIZE_MAX); | 3333 | assert(type_entry->data.unionation.gen_tag_index != SIZE_MAX); |
| 3336 | for (uint32_t i = 0; i < type_entry->data.unionation.src_field_count; i += 1) { | 3334 | for (uint32_t i = 0; i < type_entry->data.unionation.src_field_count; i += 1) { |
| 3337 | TypeUnionField *field = &type_entry->data.unionation.fields[i]; | 3335 | TypeUnionField *field = &type_entry->data.unionation.fields[i]; |
| ... | @@ -3888,7 +3886,6 @@ bool handle_is_ptr(TypeTableEntry *type_entry) { | ... | @@ -3888,7 +3886,6 @@ bool handle_is_ptr(TypeTableEntry *type_entry) { |
| 3888 | return false; | 3886 | return false; |
| 3889 | case TypeTableEntryIdArray: | 3887 | case TypeTableEntryIdArray: |
| 3890 | case TypeTableEntryIdStruct: | 3888 | case TypeTableEntryIdStruct: |
| 3891 | case TypeTableEntryIdUnion: | ||
| 3892 | return type_has_bits(type_entry); | 3889 | return type_has_bits(type_entry); |
| 3893 | case TypeTableEntryIdErrorUnion: | 3890 | case TypeTableEntryIdErrorUnion: |
| 3894 | return type_has_bits(type_entry->data.error.child_type); | 3891 | return type_has_bits(type_entry->data.error.child_type); |
| ... | @@ -3896,6 +3893,14 @@ bool handle_is_ptr(TypeTableEntry *type_entry) { | ... | @@ -3896,6 +3893,14 @@ bool handle_is_ptr(TypeTableEntry *type_entry) { |
| 3896 | return type_has_bits(type_entry->data.maybe.child_type) && | 3893 | return type_has_bits(type_entry->data.maybe.child_type) && |
| 3897 | type_entry->data.maybe.child_type->id != TypeTableEntryIdPointer && | 3894 | type_entry->data.maybe.child_type->id != TypeTableEntryIdPointer && |
| 3898 | type_entry->data.maybe.child_type->id != TypeTableEntryIdFn; | 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 | zig_unreachable(); | 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,8 +3946,6 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { |
| 3946 | case TypeTableEntryIdUnion: | 3946 | case TypeTableEntryIdUnion: |
| 3947 | { | 3947 | { |
| 3948 | LLVMTypeRef union_type_ref = type_entry->data.unionation.union_type_ref; | 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 | if (type_entry->data.unionation.gen_field_count == 0) { | 3950 | if (type_entry->data.unionation.gen_field_count == 0) { |
| 3953 | if (type_entry->data.unionation.gen_tag_index == SIZE_MAX) { | 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,7 +3958,8 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { |
| 3960 | 3958 | ||
| 3961 | LLVMValueRef union_value_ref; | 3959 | LLVMValueRef union_value_ref; |
| 3962 | bool make_unnamed_struct; | 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 | if (type_entry->data.unionation.gen_tag_index == SIZE_MAX) | 3963 | if (type_entry->data.unionation.gen_tag_index == SIZE_MAX) |
| 3965 | return LLVMGetUndef(type_entry->type_ref); | 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,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 | // implicit undefined literal to anything | 7482 | // implicit undefined literal to anything |
| 7472 | if (actual_type->id == TypeTableEntryIdUndefLit) { | 7483 | if (actual_type->id == TypeTableEntryIdUndefLit) { |
| 7473 | return ImplicitCastMatchResultYes; | 7484 | return ImplicitCastMatchResultYes; |
| ... | @@ -8370,6 +8381,63 @@ static IrInstruction *ir_analyze_undefined_to_anything(IrAnalyze *ira, IrInstruc | ... | @@ -8370,6 +8381,63 @@ static IrInstruction *ir_analyze_undefined_to_anything(IrAnalyze *ira, IrInstruc |
| 8370 | return result; | 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 | static IrInstruction *ir_analyze_widen_or_shorten(IrAnalyze *ira, IrInstruction *source_instr, | 8441 | static IrInstruction *ir_analyze_widen_or_shorten(IrAnalyze *ira, IrInstruction *source_instr, |
| 8374 | IrInstruction *target, TypeTableEntry *wanted_type) | 8442 | IrInstruction *target, TypeTableEntry *wanted_type) |
| 8375 | { | 8443 | { |
| ... | @@ -8919,6 +8987,17 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst | ... | @@ -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 | // explicit cast from undefined to anything | 9001 | // explicit cast from undefined to anything |
| 8923 | if (actual_type->id == TypeTableEntryIdUndefLit) { | 9002 | if (actual_type->id == TypeTableEntryIdUndefLit) { |
| 8924 | return ir_analyze_undefined_to_anything(ira, source_instr, value, wanted_type); | 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,3 +190,10 @@ test "cast union to tag type of union" { |
| 190 | fn testCastUnionToTagType(x: &const TheUnion) { | 190 | fn testCastUnionToTagType(x: &const TheUnion) { |
| 191 | assert(TheTag(*x) == TheTag.B); | 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,4 +2696,35 @@ pub fn addCases(cases: &tests.CompileErrorContext) { |
| 2696 | , | 2696 | , |
| 2697 | ".tmp_source.zig:6:16: error: enum 'Foo' has no tag matching integer value 0", | 2697 | ".tmp_source.zig:6:16: error: enum 'Foo' has no tag matching integer value 0", |
| 2698 | ".tmp_source.zig:1:13: note: 'Foo' declared here"); | 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 | } |