fix abi alignment of union-enums not counting tag type

add more tests for unions

See #618
master
Andrew Kelley 2017-12-04 00:32:12 -05:00
parent 5a8367e892
commit fce435db26
4 changed files with 291 additions and 194 deletions

View File

@ -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<BigInt, AstNode *, bigint_hash, bigint_eql> 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<ZigLLVMDIEnumerator*>(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<TypeEnumField>(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<bool>(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<TypeEnumField>(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<BigInt, AstNode *, bigint_hash, bigint_eql> 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<ZigLLVMDIEnumerator*>(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<TypeEnumField>(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<bool>(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<TypeEnumField>(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) {

View File

@ -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;
}

View File

@ -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,
});
}

View File

@ -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");
}