diff --git a/src/analyze.cpp b/src/analyze.cpp index 9d2d4f1a8..96f0eb44d 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1713,84 +1713,16 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) { uint64_t biggest_align_in_bits = 0; uint64_t biggest_size_in_bits = 0; - ZigLLVMDIEnumerator **di_enumerators; - Scope *scope = &union_type->data.unionation.decls_scope->base; ImportTableEntry *import = get_scope_import(scope); // set temporary flag union_type->data.unionation.embedded_in_current = true; - HashMap occupied_tag_values = {}; - - AstNode *enum_type_node = decl_node->data.container_decl.init_arg_expr; - bool auto_layout = (union_type->data.unionation.layout == ContainerLayoutAuto); - bool want_safety = (field_count >= 2) && (auto_layout || enum_type_node != nullptr); - TypeTableEntry *tag_type; - bool create_enum_type = decl_node->data.container_decl.auto_enum || (enum_type_node == nullptr && want_safety); - bool *covered_enum_fields; - if (create_enum_type) { - occupied_tag_values.init(field_count); - - di_enumerators = allocate(field_count); - - TypeTableEntry *tag_int_type; - if (enum_type_node != nullptr) { - tag_int_type = analyze_type_expr(g, scope, enum_type_node); - if (type_is_invalid(tag_int_type)) { - union_type->data.unionation.is_invalid = true; - return; - } - if (tag_int_type->id != TypeTableEntryIdInt) { - add_node_error(g, enum_type_node, - buf_sprintf("expected integer tag type, found '%s'", buf_ptr(&tag_int_type->name))); - union_type->data.unionation.is_invalid = true; - return; - } - } else { - tag_int_type = get_smallest_unsigned_int_type(g, field_count - 1); - } - - tag_type = new_type_table_entry(TypeTableEntryIdEnum); - buf_resize(&tag_type->name, 0); - buf_appendf(&tag_type->name, "@EnumTagType(%s)", buf_ptr(&union_type->name)); - tag_type->is_copyable = true; - tag_type->type_ref = tag_int_type->type_ref; - tag_type->zero_bits = tag_int_type->zero_bits; - - tag_type->data.enumeration.tag_int_type = tag_int_type; - tag_type->data.enumeration.zero_bits_known = true; - tag_type->data.enumeration.decl_node = decl_node; - tag_type->data.enumeration.layout = ContainerLayoutAuto; - tag_type->data.enumeration.src_field_count = field_count; - tag_type->data.enumeration.fields = allocate(field_count); - tag_type->data.enumeration.decls_scope = union_type->data.unionation.decls_scope; - tag_type->data.enumeration.complete = true; - } else if (enum_type_node != nullptr) { - TypeTableEntry *enum_type = analyze_type_expr(g, scope, enum_type_node); - if (type_is_invalid(enum_type)) { - union_type->data.unionation.is_invalid = true; - union_type->data.unionation.embedded_in_current = false; - return; - } - if (enum_type->id != TypeTableEntryIdEnum) { - union_type->data.unionation.is_invalid = true; - union_type->data.unionation.embedded_in_current = false; - add_node_error(g, enum_type_node, - buf_sprintf("expected enum tag type, found '%s'", buf_ptr(&enum_type->name))); - return; - } - tag_type = enum_type; - covered_enum_fields = allocate(enum_type->data.enumeration.src_field_count); - } else { - tag_type = nullptr; - } - union_type->data.unionation.tag_type = tag_type; for (uint32_t i = 0; i < field_count; i += 1) { AstNode *field_node = decl_node->data.container_decl.fields.at(i); TypeUnionField *union_field = &union_type->data.unionation.fields[i]; - Buf *field_name = field_node->data.struct_field.name; TypeTableEntry *field_type = union_field->type_entry; ensure_complete_type(g, field_type); @@ -1799,57 +1731,6 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) { continue; } - if (create_enum_type) { - di_enumerators[i] = ZigLLVMCreateDebugEnumerator(g->dbuilder, buf_ptr(field_name), i); - union_field->enum_field = &tag_type->data.enumeration.fields[i]; - union_field->enum_field->name = field_name; - union_field->enum_field->decl_index = i; - - AstNode *tag_value = field_node->data.struct_field.value; - // In this first pass we resolve explicit tag values. - // In a second pass we will fill in the unspecified ones. - if (tag_value != nullptr) { - TypeTableEntry *tag_int_type = tag_type->data.enumeration.tag_int_type; - IrInstruction *result_inst = analyze_const_value(g, scope, tag_value, tag_int_type, nullptr); - if (result_inst->value.type->id == TypeTableEntryIdInvalid) { - union_type->data.unionation.is_invalid = true; - continue; - } - assert(result_inst->value.special != ConstValSpecialRuntime); - assert(result_inst->value.type->id == TypeTableEntryIdInt); - auto entry = occupied_tag_values.put_unique(result_inst->value.data.x_bigint, tag_value); - if (entry == nullptr) { - bigint_init_bigint(&union_field->enum_field->value, &result_inst->value.data.x_bigint); - } else { - Buf *val_buf = buf_alloc(); - bigint_append_buf(val_buf, &result_inst->value.data.x_bigint, 10); - - ErrorMsg *msg = add_node_error(g, tag_value, - buf_sprintf("enum tag value %s already taken", buf_ptr(val_buf))); - add_error_note(g, msg, entry->value, - buf_sprintf("other occurrence here")); - union_type->data.unionation.is_invalid = true; - continue; - } - } - } else if (enum_type_node != nullptr) { - union_field->enum_field = find_enum_type_field(tag_type, field_name); - if (union_field->enum_field == nullptr) { - ErrorMsg *msg = add_node_error(g, field_node, - buf_sprintf("enum field not found: '%s'", buf_ptr(field_name))); - add_error_note(g, msg, tag_type->data.enumeration.decl_node, - buf_sprintf("enum declared here")); - union_type->data.unionation.is_invalid = true; - continue; - } - covered_enum_fields[union_field->enum_field->decl_index] = true; - } else { - union_field->enum_field = allocate(1); - union_field->enum_field->name = field_name; - union_field->enum_field->decl_index = i; - bigint_init_unsigned(&union_field->enum_field->value, i); - } - if (!type_has_bits(field_type)) continue; @@ -1876,48 +1757,6 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) { } } - if (create_enum_type) { - // Now iterate again and populate the unspecified tag values - uint32_t next_maybe_unoccupied_index = 0; - - for (uint32_t field_i = 0; field_i < field_count; field_i += 1) { - AstNode *field_node = decl_node->data.container_decl.fields.at(field_i); - TypeUnionField *union_field = &union_type->data.unionation.fields[field_i]; - AstNode *tag_value = field_node->data.struct_field.value; - - if (tag_value == nullptr) { - if (occupied_tag_values.size() == 0) { - bigint_init_unsigned(&union_field->enum_field->value, next_maybe_unoccupied_index); - next_maybe_unoccupied_index += 1; - } else { - BigInt proposed_value; - for (;;) { - bigint_init_unsigned(&proposed_value, next_maybe_unoccupied_index); - next_maybe_unoccupied_index += 1; - auto entry = occupied_tag_values.put_unique(proposed_value, field_node); - if (entry != nullptr) { - continue; - } - break; - } - bigint_init_bigint(&union_field->enum_field->value, &proposed_value); - } - } - } - } else if (enum_type_node != nullptr) { - for (uint32_t i = 0; i < tag_type->data.enumeration.src_field_count; i += 1) { - TypeEnumField *enum_field = &tag_type->data.enumeration.fields[i]; - if (!covered_enum_fields[i]) { - AstNode *enum_decl_node = tag_type->data.enumeration.decl_node; - AstNode *field_node = enum_decl_node->data.container_decl.fields.at(i); - ErrorMsg *msg = add_node_error(g, decl_node, - buf_sprintf("enum field missing: '%s'", buf_ptr(enum_field->name))); - add_error_note(g, msg, field_node, - buf_sprintf("declared here")); - union_type->data.unionation.is_invalid = true; - } - } - } // unset temporary flag union_type->data.unionation.embedded_in_current = false; @@ -1948,11 +1787,12 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) { return; } - assert(most_aligned_union_member != nullptr); - uint64_t padding_in_bits = biggest_size_in_bits - size_of_most_aligned_member_in_bits; + TypeTableEntry *tag_type = union_type->data.unionation.tag_type; if (tag_type == nullptr) { + assert(most_aligned_union_member != nullptr); + if (padding_in_bits > 0) { TypeTableEntry *u8_type = get_int_type(g, false, 8); TypeTableEntry *padding_array = get_array_type(g, u8_type, padding_in_bits / 8); @@ -1993,7 +1833,13 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) { }; union_type_ref = LLVMStructType(union_element_types, 2, false); } else if (most_aligned_union_member == nullptr) { - zig_panic("TODO zero bit payload"); + union_type->data.unionation.gen_tag_index = SIZE_MAX; + union_type->data.unionation.gen_union_index = SIZE_MAX; + union_type->type_ref = tag_type->type_ref; + + ZigLLVMReplaceTemporary(g->dbuilder, union_type->di_type, tag_type->di_type); + union_type->di_type = tag_type->di_type; + return; } else { union_type_ref = most_aligned_union_member->type_ref; } @@ -2020,20 +1866,6 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) { LLVMStructSetBody(union_type->type_ref, root_struct_element_types, 2, false); - // create debug type for root struct - - uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, tag_type->type_ref); - uint64_t tag_debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_type->type_ref); - if (create_enum_type) { - // create debug type for tag - ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder, - ZigLLVMTypeToScope(union_type->di_type), "AnonEnum", - import->di_file, (unsigned)(decl_node->line + 1), - tag_debug_size_in_bits, tag_debug_align_in_bits, di_enumerators, field_count, - tag_type->di_type, ""); - tag_type->di_type = tag_di_type; - } - // create debug type for union ZigLLVMDIType *union_di_type = ZigLLVMCreateDebugUnionType(g->dbuilder, ZigLLVMTypeToScope(union_type->di_type), "AnonUnion", @@ -2053,6 +1885,10 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) { biggest_align_in_bits, union_offset_in_bits, 0, union_di_type); + + uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, tag_type->type_ref); + uint64_t tag_debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_type->type_ref); + ZigLLVMDIType *tag_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(union_type->di_type), "tag", import->di_file, (unsigned)(decl_node->line + 1), @@ -2312,8 +2148,23 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) { if (union_type->data.unionation.zero_bits_known) return; + if (type_is_invalid(union_type)) + return; + if (union_type->data.unionation.zero_bits_loop_flag) { + // If we get here it's due to recursion. From this we conclude that the struct is + // not zero bits, and if abi_alignment == 0 we further conclude that the first field + // is a pointer to this very struct, or a function pointer with parameters that + // reference such a type. union_type->data.unionation.zero_bits_known = true; + if (union_type->data.unionation.abi_alignment == 0) { + if (union_type->data.unionation.layout == ContainerLayoutPacked) { + union_type->data.unionation.abi_alignment = 1; + } else { + union_type->data.unionation.abi_alignment = LLVMABIAlignmentOfType(g->target_data_ref, + LLVMPointerType(LLVMInt8Type(), 0)); + } + } return; } @@ -2342,19 +2193,99 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) { Scope *scope = &union_type->data.unionation.decls_scope->base; + HashMap occupied_tag_values = {}; + + AstNode *enum_type_node = decl_node->data.container_decl.init_arg_expr; + bool auto_layout = (union_type->data.unionation.layout == ContainerLayoutAuto); + bool want_safety = (field_count >= 2) && (auto_layout || enum_type_node != nullptr); + TypeTableEntry *tag_type; + bool create_enum_type = decl_node->data.container_decl.auto_enum || (enum_type_node == nullptr && want_safety); + bool *covered_enum_fields; + ZigLLVMDIEnumerator **di_enumerators; + if (create_enum_type) { + occupied_tag_values.init(field_count); + + di_enumerators = allocate(field_count); + + TypeTableEntry *tag_int_type; + if (enum_type_node != nullptr) { + tag_int_type = analyze_type_expr(g, scope, enum_type_node); + if (type_is_invalid(tag_int_type)) { + union_type->data.unionation.is_invalid = true; + return; + } + if (tag_int_type->id != TypeTableEntryIdInt) { + add_node_error(g, enum_type_node, + buf_sprintf("expected integer tag type, found '%s'", buf_ptr(&tag_int_type->name))); + union_type->data.unionation.is_invalid = true; + return; + } + } else { + tag_int_type = get_smallest_unsigned_int_type(g, field_count - 1); + } + union_type->data.unionation.abi_alignment = get_abi_alignment(g, tag_int_type); + + tag_type = new_type_table_entry(TypeTableEntryIdEnum); + buf_resize(&tag_type->name, 0); + buf_appendf(&tag_type->name, "@EnumTagType(%s)", buf_ptr(&union_type->name)); + tag_type->is_copyable = true; + tag_type->type_ref = tag_int_type->type_ref; + tag_type->zero_bits = tag_int_type->zero_bits; + + tag_type->data.enumeration.tag_int_type = tag_int_type; + tag_type->data.enumeration.zero_bits_known = true; + tag_type->data.enumeration.decl_node = decl_node; + tag_type->data.enumeration.layout = ContainerLayoutAuto; + tag_type->data.enumeration.src_field_count = field_count; + tag_type->data.enumeration.fields = allocate(field_count); + tag_type->data.enumeration.decls_scope = union_type->data.unionation.decls_scope; + tag_type->data.enumeration.complete = true; + } else if (enum_type_node != nullptr) { + TypeTableEntry *enum_type = analyze_type_expr(g, scope, enum_type_node); + if (type_is_invalid(enum_type)) { + union_type->data.unionation.is_invalid = true; + union_type->data.unionation.embedded_in_current = false; + return; + } + if (enum_type->id != TypeTableEntryIdEnum) { + union_type->data.unionation.is_invalid = true; + union_type->data.unionation.embedded_in_current = false; + add_node_error(g, enum_type_node, + buf_sprintf("expected enum tag type, found '%s'", buf_ptr(&enum_type->name))); + return; + } + tag_type = enum_type; + covered_enum_fields = allocate(enum_type->data.enumeration.src_field_count); + union_type->data.unionation.abi_alignment = get_abi_alignment(g, enum_type); + } else { + tag_type = nullptr; + } + union_type->data.unionation.tag_type = tag_type; + uint32_t gen_field_index = 0; for (uint32_t i = 0; i < field_count; i += 1) { AstNode *field_node = decl_node->data.container_decl.fields.at(i); + Buf *field_name = field_node->data.struct_field.name; TypeUnionField *union_field = &union_type->data.unionation.fields[i]; union_field->name = field_node->data.struct_field.name; + TypeTableEntry *field_type; if (field_node->data.struct_field.type == nullptr) { - add_node_error(g, field_node, buf_sprintf("union field missing type")); - union_type->data.unionation.is_invalid = true; - continue; + if (decl_node->data.container_decl.auto_enum || decl_node->data.container_decl.init_arg_expr != nullptr) { + field_type = g->builtin_types.entry_void; + } else { + add_node_error(g, field_node, buf_sprintf("union field missing type")); + union_type->data.unionation.is_invalid = true; + continue; + } + } else { + field_type = analyze_type_expr(g, scope, field_node->data.struct_field.type); + type_ensure_zero_bits_known(g, field_type); + if (type_is_invalid(field_type)) { + union_type->data.unionation.is_invalid = true; + continue; + } } - - TypeTableEntry *field_type = analyze_type_expr(g, scope, field_node->data.struct_field.type); union_field->type_entry = field_type; if (field_node->data.struct_field.value != nullptr && !decl_node->data.container_decl.auto_enum) { @@ -2364,11 +2295,57 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) { buf_sprintf("consider 'union(enum)' here")); } - type_ensure_zero_bits_known(g, field_type); - if (type_is_invalid(field_type)) { - union_type->data.unionation.is_invalid = true; - continue; + if (create_enum_type) { + di_enumerators[i] = ZigLLVMCreateDebugEnumerator(g->dbuilder, buf_ptr(field_name), i); + union_field->enum_field = &tag_type->data.enumeration.fields[i]; + union_field->enum_field->name = field_name; + union_field->enum_field->decl_index = i; + + AstNode *tag_value = field_node->data.struct_field.value; + // In this first pass we resolve explicit tag values. + // In a second pass we will fill in the unspecified ones. + if (tag_value != nullptr) { + TypeTableEntry *tag_int_type = tag_type->data.enumeration.tag_int_type; + IrInstruction *result_inst = analyze_const_value(g, scope, tag_value, tag_int_type, nullptr); + if (result_inst->value.type->id == TypeTableEntryIdInvalid) { + union_type->data.unionation.is_invalid = true; + continue; + } + assert(result_inst->value.special != ConstValSpecialRuntime); + assert(result_inst->value.type->id == TypeTableEntryIdInt); + auto entry = occupied_tag_values.put_unique(result_inst->value.data.x_bigint, tag_value); + if (entry == nullptr) { + bigint_init_bigint(&union_field->enum_field->value, &result_inst->value.data.x_bigint); + } else { + Buf *val_buf = buf_alloc(); + bigint_append_buf(val_buf, &result_inst->value.data.x_bigint, 10); + + ErrorMsg *msg = add_node_error(g, tag_value, + buf_sprintf("enum tag value %s already taken", buf_ptr(val_buf))); + add_error_note(g, msg, entry->value, + buf_sprintf("other occurrence here")); + union_type->data.unionation.is_invalid = true; + continue; + } + } + } else if (enum_type_node != nullptr) { + union_field->enum_field = find_enum_type_field(tag_type, field_name); + if (union_field->enum_field == nullptr) { + ErrorMsg *msg = add_node_error(g, field_node, + buf_sprintf("enum field not found: '%s'", buf_ptr(field_name))); + add_error_note(g, msg, tag_type->data.enumeration.decl_node, + buf_sprintf("enum declared here")); + union_type->data.unionation.is_invalid = true; + continue; + } + covered_enum_fields[union_field->enum_field->decl_index] = true; + } else { + union_field->enum_field = allocate(1); + union_field->enum_field->name = field_name; + union_field->enum_field->decl_index = i; + bigint_init_unsigned(&union_field->enum_field->value, i); } + assert(union_field->enum_field != nullptr); if (!type_has_bits(field_type)) continue; @@ -2379,9 +2356,15 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) { uint32_t field_align_bytes = get_abi_alignment(g, field_type); if (field_align_bytes > biggest_align_bytes) { biggest_align_bytes = field_align_bytes; + if (biggest_align_bytes > union_type->data.unionation.abi_alignment) { + union_type->data.unionation.abi_alignment = biggest_align_bytes; + } } } + if (union_type->data.unionation.is_invalid) + return; + bool src_have_tag = decl_node->data.container_decl.auto_enum || decl_node->data.container_decl.init_arg_expr != nullptr; @@ -2405,15 +2388,66 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) { return; } + if (create_enum_type) { + // Now iterate again and populate the unspecified tag values + uint32_t next_maybe_unoccupied_index = 0; + + for (uint32_t field_i = 0; field_i < field_count; field_i += 1) { + AstNode *field_node = decl_node->data.container_decl.fields.at(field_i); + TypeUnionField *union_field = &union_type->data.unionation.fields[field_i]; + AstNode *tag_value = field_node->data.struct_field.value; + + if (tag_value == nullptr) { + if (occupied_tag_values.size() == 0) { + bigint_init_unsigned(&union_field->enum_field->value, next_maybe_unoccupied_index); + next_maybe_unoccupied_index += 1; + } else { + BigInt proposed_value; + for (;;) { + bigint_init_unsigned(&proposed_value, next_maybe_unoccupied_index); + next_maybe_unoccupied_index += 1; + auto entry = occupied_tag_values.put_unique(proposed_value, field_node); + if (entry != nullptr) { + continue; + } + break; + } + bigint_init_bigint(&union_field->enum_field->value, &proposed_value); + } + } + } + } else if (enum_type_node != nullptr) { + for (uint32_t i = 0; i < tag_type->data.enumeration.src_field_count; i += 1) { + TypeEnumField *enum_field = &tag_type->data.enumeration.fields[i]; + if (!covered_enum_fields[i]) { + AstNode *enum_decl_node = tag_type->data.enumeration.decl_node; + AstNode *field_node = enum_decl_node->data.container_decl.fields.at(i); + ErrorMsg *msg = add_node_error(g, decl_node, + buf_sprintf("enum field missing: '%s'", buf_ptr(enum_field->name))); + add_error_note(g, msg, field_node, + buf_sprintf("declared here")); + union_type->data.unionation.is_invalid = true; + } + } + } + + if (create_enum_type) { + ImportTableEntry *import = get_scope_import(scope); + uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, tag_type->type_ref); + uint64_t tag_debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_type->type_ref); + // TODO get a more accurate debug scope + ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder, + ZigLLVMFileToScope(import->di_file), buf_ptr(&tag_type->name), + import->di_file, (unsigned)(decl_node->line + 1), + tag_debug_size_in_bits, tag_debug_align_in_bits, di_enumerators, field_count, + tag_type->di_type, ""); + tag_type->di_type = tag_di_type; + } + union_type->data.unionation.zero_bits_loop_flag = false; union_type->data.unionation.gen_field_count = gen_field_index; union_type->zero_bits = (gen_field_index == 0 && (field_count < 2 || !src_have_tag)); union_type->data.unionation.zero_bits_known = true; - - // also compute abi_alignment - if (!union_type->zero_bits) { - union_type->data.unionation.abi_alignment = biggest_align_bytes; - } } static void get_fully_qualified_decl_name_internal(Buf *buf, Scope *scope, uint8_t sep) { diff --git a/src/ir.cpp b/src/ir.cpp index e70340b30..d784974ae 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -12970,10 +12970,13 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira, ir_build_load_ptr_from(&ira->new_irb, &switch_target_instruction->base, target_value_ptr); return target_type; case TypeTableEntryIdUnion: { - if (target_type->data.unionation.gen_tag_index == SIZE_MAX) { + AstNode *decl_node = target_type->data.unionation.decl_node; + if (!decl_node->data.container_decl.auto_enum && + decl_node->data.container_decl.init_arg_expr == nullptr) + { ErrorMsg *msg = ir_add_error(ira, target_value_ptr, buf_sprintf("switch on union which has no attached enum")); - add_error_note(ira->codegen, msg, target_type->data.unionation.decl_node, + add_error_note(ira->codegen, msg, decl_node, buf_sprintf("union declared here")); return ira->codegen->builtin_types.entry_invalid; } diff --git a/test/cases/union.zig b/test/cases/union.zig index c4767fd64..291384eae 100644 --- a/test/cases/union.zig +++ b/test/cases/union.zig @@ -105,6 +105,18 @@ fn bar(value: &const Payload) -> i32 { }; } +const MultipleChoice = union(enum(u32)) { + A = 20, + B = 40, + C = 60, + D = 1000, +}; +test "simple union(enum(u32))" { + var x = MultipleChoice.C; + assert(x == MultipleChoice.C); + assert(u32(@TagType(MultipleChoice)(x)) == 60); +} + const MultipleChoice2 = union(enum(u32)) { Unspecified1: i32, A: f32 = 20, @@ -137,3 +149,4 @@ fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: &const MultipleChoice2) { MultipleChoice2.Unspecified5 => 9, }); } + diff --git a/test/compile_errors.zig b/test/compile_errors.zig index ac06b5aa2..e8a517b1c 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2524,4 +2524,51 @@ pub fn addCases(cases: &tests.CompileErrorContext) { , ".tmp_source.zig:6:17: error: enum field missing: 'C'", ".tmp_source.zig:4:5: note: declared here"); + + cases.add("@TagType when union has no attached enum", + \\const Foo = union { + \\ A: i32, + \\}; + \\export fn entry() { + \\ const x = @TagType(Foo); + \\} + , + ".tmp_source.zig:5:24: error: union 'Foo' has no tag", + ".tmp_source.zig:1:13: note: consider 'union(enum)' here"); + + cases.add("non-integer tag type to automatic union enum", + \\const Foo = union(enum(f32)) { + \\ A: i32, + \\}; + \\export fn entry() { + \\ const x = @TagType(Foo); + \\} + , + ".tmp_source.zig:1:23: error: expected integer tag type, found 'f32'"); + + cases.add("non-enum tag type passed to union", + \\const Foo = union(u32) { + \\ A: i32, + \\}; + \\export fn entry() { + \\ const x = @TagType(Foo); + \\} + , + ".tmp_source.zig:1:18: error: expected enum tag type, found 'u32'"); + + cases.add("union auto-enum value already taken", + \\const MultipleChoice = union(enum(u32)) { + \\ A = 20, + \\ B = 40, + \\ C = 60, + \\ D = 1000, + \\ E = 60, + \\}; + \\export fn entry() { + \\ var x = MultipleChoice { .C = {} }; + \\} + , + ".tmp_source.zig:6:9: error: enum tag value 60 already taken", + ".tmp_source.zig:4:9: note: other occurrence here"); + }