From b62e2fd8703129fcf0dc80675800f005e84ee724 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 30 Nov 2017 21:46:02 -0500 Subject: ability to specify tag type of enums see #305 --- src/parser.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src/parser.cpp') diff --git a/src/parser.cpp b/src/parser.cpp index ba1fd99e57..7f25e3ef21 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2377,7 +2377,7 @@ static AstNode *ast_parse_use(ParseContext *pc, size_t *token_index, VisibMod vi } /* -ContainerDecl = option("extern" | "packed") ("struct" | "enum" | "union") "{" many(ContainerMember) "}" +ContainerDecl = option("extern" | "packed") ("struct" | "union" | ("enum" option(GroupedExpression))) "{" many(ContainerMember) "}" ContainerMember = (ContainerField | FnDef | GlobalVarDecl) ContainerField = Symbol option(":" Expression) "," */ @@ -2415,6 +2415,10 @@ static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index, node->data.container_decl.layout = layout; node->data.container_decl.kind = kind; + if (kind == ContainerKindEnum || kind == ContainerKindStruct) { + node->data.container_decl.init_arg_expr = ast_parse_grouped_expr(pc, token_index, false); + } + ast_eat_token(pc, token_index, TokenIdLBrace); for (;;) { @@ -2804,6 +2808,7 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont case NodeTypeContainerDecl: visit_node_list(&node->data.container_decl.fields, visit, context); visit_node_list(&node->data.container_decl.decls, visit, context); + visit_field(&node->data.container_decl.init_arg_expr, visit, context); break; case NodeTypeStructField: visit_field(&node->data.struct_field.type, visit, context); -- cgit v1.2.3 From 137c8f5e8a6023db24f90555e968b592a4b843e4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 2 Dec 2017 22:31:42 -0500 Subject: ability to set tag values of enums also remove support for enums with 0 values closes #305 --- doc/langref.html.in | 2 +- src/all_types.hpp | 10 ++- src/analyze.cpp | 234 +++++++++++++++++++++++++++++++++++++----------- src/analyze.hpp | 9 +- src/ast_render.cpp | 4 + src/bigint.cpp | 32 +++++++ src/bigint.hpp | 5 ++ src/codegen.cpp | 31 ++++--- src/ir.cpp | 109 +++++++++++++--------- src/parser.cpp | 48 +++++----- std/sort.zig | 2 +- test/cases/enum.zig | 56 +++++++++++- test/compile_errors.zig | 55 ++++++++++-- 13 files changed, 451 insertions(+), 146 deletions(-) (limited to 'src/parser.cpp') diff --git a/doc/langref.html.in b/doc/langref.html.in index 32c099f547..20b8ae1eee 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -5709,7 +5709,7 @@ VariableDeclaration = option("comptime") ("var" | "const") Symbol option(":" Typ ContainerMember = (ContainerField | FnDef | GlobalVarDecl) -ContainerField = Symbol option(":" Expression) "," +ContainerField = Symbol option(":" PrefixOpExpression option("=" PrefixOpExpression "," UseDecl = "use" Expression ";" diff --git a/src/all_types.hpp b/src/all_types.hpp index 40d246c43c..8d4ffc5b84 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -98,7 +98,7 @@ struct ConstParent { }; struct ConstEnumValue { - uint64_t tag; + BigInt tag; ConstExprValue *payload; }; @@ -108,7 +108,7 @@ struct ConstStructValue { }; struct ConstUnionValue { - uint64_t tag; + BigInt tag; ConstExprValue *payload; ConstParent parent; }; @@ -346,14 +346,14 @@ struct TldCompTime { struct TypeEnumField { Buf *name; TypeTableEntry *type_entry; - uint32_t value; + BigInt value; uint32_t gen_index; }; struct TypeUnionField { Buf *name; TypeTableEntry *type_entry; - uint32_t value; + BigInt value; uint32_t gen_index; }; @@ -780,6 +780,7 @@ struct AstNodeStructField { VisibMod visib_mod; Buf *name; AstNode *type; + AstNode *value; }; struct AstNodeStringLiteral { @@ -1014,6 +1015,7 @@ struct TypeTableEntryEnum { TypeEnumField *fields; bool is_invalid; // true if any fields are invalid TypeTableEntry *tag_type; + TypeTableEntry *tag_int_type; LLVMTypeRef union_type_ref; ScopeDecls *decls_scope; diff --git a/src/analyze.cpp b/src/analyze.cpp index 7d51e83677..b7d12443a0 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1390,30 +1390,7 @@ static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) { return; } - TypeTableEntry *tag_int_type = get_smallest_unsigned_int_type(g, field_count - 1); - if (decl_node->data.container_decl.init_arg_expr != nullptr) { - TypeTableEntry *wanted_tag_int_type = analyze_type_expr(g, scope, decl_node->data.container_decl.init_arg_expr); - if (type_is_invalid(wanted_tag_int_type)) { - enum_type->data.enumeration.is_invalid = true; - } else if (wanted_tag_int_type->id != TypeTableEntryIdInt) { - enum_type->data.enumeration.is_invalid = true; - add_node_error(g, decl_node->data.container_decl.init_arg_expr, - buf_sprintf("expected integer, found '%s'", buf_ptr(&wanted_tag_int_type->name))); - } else if (wanted_tag_int_type->data.integral.is_signed) { - enum_type->data.enumeration.is_invalid = true; - add_node_error(g, decl_node->data.container_decl.init_arg_expr, - buf_sprintf("expected unsigned integer, found '%s'", buf_ptr(&wanted_tag_int_type->name))); - } else if (wanted_tag_int_type->data.integral.bit_count < tag_int_type->data.integral.bit_count) { - enum_type->data.enumeration.is_invalid = true; - add_node_error(g, decl_node->data.container_decl.init_arg_expr, - buf_sprintf("'%s' too small to hold all bits; must be at least '%s'", - buf_ptr(&wanted_tag_int_type->name), buf_ptr(&tag_int_type->name))); - } else { - tag_int_type = wanted_tag_int_type; - } - } - - + TypeTableEntry *tag_int_type = enum_type->data.enumeration.tag_int_type; TypeTableEntry *tag_type_entry = create_enum_tag_type(g, enum_type, tag_int_type); enum_type->data.enumeration.tag_type = tag_type_entry; @@ -1683,7 +1660,6 @@ static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type) { TypeTableEntry *field_type = type_struct_field->type_entry; ensure_complete_type(g, field_type); - if (type_is_invalid(field_type)) { struct_type->data.structure.is_invalid = true; break; @@ -2121,6 +2097,18 @@ static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) { assert(!enum_type->data.enumeration.fields); uint32_t field_count = (uint32_t)decl_node->data.container_decl.fields.length; + if (field_count == 0) { + add_node_error(g, decl_node, buf_sprintf("enums must have 1 or more fields")); + + enum_type->data.enumeration.src_field_count = field_count; + enum_type->data.enumeration.fields = nullptr; + enum_type->data.enumeration.is_invalid = true; + enum_type->data.enumeration.zero_bits_loop_flag = false; + enum_type->data.enumeration.gen_field_count = 0; + enum_type->data.enumeration.zero_bits_known = true; + return; + } + enum_type->data.enumeration.src_field_count = field_count; enum_type->data.enumeration.fields = allocate(field_count); @@ -2128,14 +2116,69 @@ static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) { Scope *scope = &enum_type->data.enumeration.decls_scope->base; + HashMap occupied_tag_values = {}; + occupied_tag_values.init(field_count); + + TypeTableEntry *tag_int_type = get_smallest_unsigned_int_type(g, field_count - 1); + + if (decl_node->data.container_decl.init_arg_expr != nullptr) { + TypeTableEntry *wanted_tag_int_type = analyze_type_expr(g, scope, decl_node->data.container_decl.init_arg_expr); + if (type_is_invalid(wanted_tag_int_type)) { + enum_type->data.enumeration.is_invalid = true; + } else if (wanted_tag_int_type->id != TypeTableEntryIdInt) { + enum_type->data.enumeration.is_invalid = true; + add_node_error(g, decl_node->data.container_decl.init_arg_expr, + buf_sprintf("expected integer, found '%s'", buf_ptr(&wanted_tag_int_type->name))); + } else if (wanted_tag_int_type->data.integral.is_signed) { + enum_type->data.enumeration.is_invalid = true; + add_node_error(g, decl_node->data.container_decl.init_arg_expr, + buf_sprintf("expected unsigned integer, found '%s'", buf_ptr(&wanted_tag_int_type->name))); + } else if (wanted_tag_int_type->data.integral.bit_count < tag_int_type->data.integral.bit_count) { + enum_type->data.enumeration.is_invalid = true; + add_node_error(g, decl_node->data.container_decl.init_arg_expr, + buf_sprintf("'%s' too small to hold all bits; must be at least '%s'", + buf_ptr(&wanted_tag_int_type->name), buf_ptr(&tag_int_type->name))); + } else { + tag_int_type = wanted_tag_int_type; + } + } + enum_type->data.enumeration.tag_int_type = tag_int_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); - TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[i]; + 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); + TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_i]; type_enum_field->name = field_node->data.struct_field.name; TypeTableEntry *field_type = analyze_type_expr(g, scope, field_node->data.struct_field.type); type_enum_field->type_entry = field_type; - type_enum_field->value = 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) { + IrInstruction *result_inst = analyze_const_value(g, scope, tag_value, tag_int_type, nullptr); + if (result_inst->value.type->id == TypeTableEntryIdInvalid) { + enum_type->data.enumeration.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(&type_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")); + enum_type->data.enumeration.is_invalid = true; + continue; + } + } type_ensure_zero_bits_known(g, field_type); if (type_is_invalid(field_type)) { @@ -2155,6 +2198,34 @@ static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *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); + TypeEnumField *type_enum_field = &enum_type->data.enumeration.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(&type_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(&type_enum_field->value, &proposed_value); + } + } + } + enum_type->data.enumeration.zero_bits_loop_flag = false; enum_type->data.enumeration.gen_field_count = gen_field_index; enum_type->zero_bits = (gen_field_index == 0 && field_count < 2); @@ -2162,7 +2233,6 @@ static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) { // also compute abi_alignment if (!enum_type->zero_bits) { - TypeTableEntry *tag_int_type = get_smallest_unsigned_int_type(g, field_count); uint32_t align_of_tag_in_bytes = LLVMABIAlignmentOfType(g->target_data_ref, tag_int_type->type_ref); enum_type->data.enumeration.abi_alignment = max(align_of_tag_in_bytes, biggest_align_bytes); } @@ -2214,6 +2284,11 @@ static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) { type_struct_field->src_index = i; type_struct_field->gen_index = SIZE_MAX; + if (field_node->data.struct_field.value != nullptr) { + add_node_error(g, field_node->data.struct_field.value, + buf_sprintf("enums, not structs, support field assignment")); + } + type_ensure_zero_bits_known(g, field_type); if (type_is_invalid(field_type)) { struct_type->data.structure.is_invalid = true; @@ -2277,7 +2352,14 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) { type_union_field->name = field_node->data.struct_field.name; TypeTableEntry *field_type = analyze_type_expr(g, scope, field_node->data.struct_field.type); type_union_field->type_entry = field_type; - type_union_field->value = i; + + // TODO look for enum arg to union + bigint_init_unsigned(&type_union_field->value, i); + + if (field_node->data.struct_field.value != nullptr) { + add_node_error(g, field_node->data.struct_field.value, + buf_sprintf("enums, not unions, support field assignment")); + } type_ensure_zero_bits_known(g, field_type); if (type_is_invalid(field_type)) { @@ -3190,6 +3272,29 @@ TypeUnionField *find_union_type_field(TypeTableEntry *type_entry, Buf *name) { return nullptr; } +static TypeUnionField *find_union_field_by_tag(TypeTableEntry *type_entry, const BigInt *tag) { + assert(type_entry->id == TypeTableEntryIdUnion); + assert(type_entry->data.unionation.complete); + for (uint32_t i = 0; i < type_entry->data.unionation.src_field_count; i += 1) { + TypeUnionField *field = &type_entry->data.unionation.fields[i]; + if (bigint_cmp(&field->value, tag) == CmpEQ) { + return field; + } + } + return nullptr; +} + +TypeEnumField *find_enum_field_by_tag(TypeTableEntry *enum_type, const BigInt *tag) { + for (uint32_t i = 0; i < enum_type->data.enumeration.src_field_count; i += 1) { + TypeEnumField *field = &enum_type->data.enumeration.fields[i]; + if (bigint_cmp(&field->value, tag) == CmpEQ) { + return field; + } + } + return nullptr; +} + + static bool is_container(TypeTableEntry *type_entry) { switch (type_entry->id) { case TypeTableEntryIdInvalid: @@ -4178,6 +4283,18 @@ ConstExprValue *create_const_c_str_lit(CodeGen *g, Buf *str) { return const_val; } +void init_const_bigint(ConstExprValue *const_val, TypeTableEntry *type, const BigInt *bigint) { + const_val->special = ConstValSpecialStatic; + const_val->type = type; + bigint_init_bigint(&const_val->data.x_bigint, bigint); +} + +ConstExprValue *create_const_bigint(TypeTableEntry *type, const BigInt *bigint) { + ConstExprValue *const_val = create_const_vals(1); + init_const_bigint(const_val, type, bigint); + return const_val; +} + void init_const_unsigned_negative(ConstExprValue *const_val, TypeTableEntry *type, uint64_t x, bool negative) { const_val->special = ConstValSpecialStatic; const_val->type = type; @@ -4241,13 +4358,13 @@ ConstExprValue *create_const_float(TypeTableEntry *type, double value) { return const_val; } -void init_const_enum_tag(ConstExprValue *const_val, TypeTableEntry *type, uint64_t tag) { +void init_const_enum_tag(ConstExprValue *const_val, TypeTableEntry *type, const BigInt *tag) { const_val->special = ConstValSpecialStatic; const_val->type = type; - const_val->data.x_enum.tag = tag; + bigint_init_bigint(&const_val->data.x_enum.tag, tag); } -ConstExprValue *create_const_enum_tag(TypeTableEntry *type, uint64_t tag) { +ConstExprValue *create_const_enum_tag(TypeTableEntry *type, const BigInt *tag) { ConstExprValue *const_val = create_const_vals(1); init_const_enum_tag(const_val, type, tag); return const_val; @@ -4450,20 +4567,35 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) { switch (a->type->id) { case TypeTableEntryIdOpaque: zig_unreachable(); - case TypeTableEntryIdEnum: - { - ConstEnumValue *enum1 = &a->data.x_enum; - ConstEnumValue *enum2 = &b->data.x_enum; - if (enum1->tag == enum2->tag) { - TypeEnumField *enum_field = &a->type->data.enumeration.fields[enum1->tag]; - if (type_has_bits(enum_field->type_entry)) { - zig_panic("TODO const expr analyze enum special value for equality"); - } else { - return true; - } + case TypeTableEntryIdEnum: { + ConstEnumValue *enum1 = &a->data.x_enum; + ConstEnumValue *enum2 = &b->data.x_enum; + if (bigint_cmp(&enum1->tag, &enum2->tag) == CmpEQ) { + TypeEnumField *field = find_enum_field_by_tag(a->type, &enum1->tag); + assert(field != nullptr); + if (type_has_bits(field->type_entry)) { + zig_panic("TODO const expr analyze enum field value for equality"); + } else { + return true; } - return false; } + return false; + } + case TypeTableEntryIdUnion: { + ConstUnionValue *union1 = &a->data.x_union; + ConstUnionValue *union2 = &b->data.x_union; + + if (bigint_cmp(&union1->tag, &union2->tag) == CmpEQ) { + TypeUnionField *field = find_union_field_by_tag(a->type, &union1->tag); + assert(field != nullptr); + if (type_has_bits(field->type_entry)) { + zig_panic("TODO const expr analyze union field value for equality"); + } else { + return true; + } + } + return false; + } case TypeTableEntryIdMetaType: return a->data.x_type == b->data.x_type; case TypeTableEntryIdVoid: @@ -4544,8 +4676,6 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) { return false; } return true; - case TypeTableEntryIdUnion: - zig_panic("TODO"); case TypeTableEntryIdUndefLit: zig_panic("TODO"); case TypeTableEntryIdNullLit: @@ -4855,11 +4985,10 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { } TypeTableEntry *make_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits) { - assert(size_in_bits > 0); - TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt); entry->is_copyable = true; - entry->type_ref = LLVMIntType(size_in_bits); + entry->type_ref = (size_in_bits == 0) ? LLVMVoidType() : LLVMIntType(size_in_bits); + entry->zero_bits = (size_in_bits == 0); const char u_or_i = is_signed ? 'i' : 'u'; buf_resize(&entry->name, 0); @@ -4880,7 +5009,8 @@ TypeTableEntry *make_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits) } } - uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref); + uint64_t debug_size_in_bits = (size_in_bits == 0) ? + 0 : (8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref)); entry->di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), debug_size_in_bits, dwarf_tag); entry->data.integral.is_signed = is_signed; entry->data.integral.bit_count = size_in_bits; diff --git a/src/analyze.hpp b/src/analyze.hpp index b2464af9a0..50eb2a800f 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -64,6 +64,8 @@ TypeStructField *find_struct_type_field(TypeTableEntry *type_entry, Buf *name); ScopeDecls *get_container_scope(TypeTableEntry *type_entry); TypeEnumField *find_enum_type_field(TypeTableEntry *enum_type, Buf *name); TypeUnionField *find_union_type_field(TypeTableEntry *type_entry, Buf *name); +TypeEnumField *find_enum_field_by_tag(TypeTableEntry *enum_type, const BigInt *tag); + bool is_container_ref(TypeTableEntry *type_entry); void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node); void scan_import(CodeGen *g, ImportTableEntry *import); @@ -109,6 +111,9 @@ ConstExprValue *create_const_str_lit(CodeGen *g, Buf *str); void init_const_c_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *c_str); ConstExprValue *create_const_c_str_lit(CodeGen *g, Buf *c_str); +void init_const_bigint(ConstExprValue *const_val, TypeTableEntry *type, const BigInt *bigint); +ConstExprValue *create_const_bigint(TypeTableEntry *type, const BigInt *bigint); + void init_const_unsigned_negative(ConstExprValue *const_val, TypeTableEntry *type, uint64_t x, bool negative); ConstExprValue *create_const_unsigned_negative(TypeTableEntry *type, uint64_t x, bool negative); @@ -121,8 +126,8 @@ ConstExprValue *create_const_usize(CodeGen *g, uint64_t x); void init_const_float(ConstExprValue *const_val, TypeTableEntry *type, double value); ConstExprValue *create_const_float(TypeTableEntry *type, double value); -void init_const_enum_tag(ConstExprValue *const_val, TypeTableEntry *type, uint64_t tag); -ConstExprValue *create_const_enum_tag(TypeTableEntry *type, uint64_t tag); +void init_const_enum_tag(ConstExprValue *const_val, TypeTableEntry *type, const BigInt *tag); +ConstExprValue *create_const_enum_tag(TypeTableEntry *type, const BigInt *tag); void init_const_bool(CodeGen *g, ConstExprValue *const_val, bool value); ConstExprValue *create_const_bool(CodeGen *g, bool value); diff --git a/src/ast_render.cpp b/src/ast_render.cpp index ce7bcd9e36..cdc18c7252 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -677,6 +677,10 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { fprintf(ar->f, ": "); render_node_grouped(ar, field_node->data.struct_field.type); } + if (field_node->data.struct_field.value != nullptr) { + fprintf(ar->f, "= "); + render_node_grouped(ar, field_node->data.struct_field.value); + } fprintf(ar->f, ",\n"); } diff --git a/src/bigint.cpp b/src/bigint.cpp index d12c8d0759..f01436d232 100644 --- a/src/bigint.cpp +++ b/src/bigint.cpp @@ -1224,3 +1224,35 @@ Cmp bigint_cmp_zero(const BigInt *op) { } return op->is_negative ? CmpLT : CmpGT; } + +uint32_t bigint_hash(BigInt x) { + if (x.digit_count == 0) { + return 0; + } else { + return bigint_ptr(&x)[0]; + } +} + +bool bigint_eql(BigInt a, BigInt b) { + return bigint_cmp(&a, &b) == CmpEQ; +} + +void bigint_incr(BigInt *x) { + if (x->digit_count == 0) { + bigint_init_unsigned(x, 1); + return; + } + + if (x->digit_count == 1 && x->data.digit != UINT64_MAX) { + x->data.digit += 1; + return; + } + + BigInt copy; + bigint_init_bigint(©, x); + + BigInt one; + bigint_init_unsigned(&one, 1); + + bigint_add(x, ©, &one); +} diff --git a/src/bigint.hpp b/src/bigint.hpp index a1facb5c78..9f044c8722 100644 --- a/src/bigint.hpp +++ b/src/bigint.hpp @@ -88,6 +88,11 @@ size_t bigint_bits_needed(const BigInt *op); // convenience functions Cmp bigint_cmp_zero(const BigInt *op); +void bigint_incr(BigInt *value); + bool mul_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result); +uint32_t bigint_hash(BigInt x); +bool bigint_eql(BigInt a, BigInt b); + #endif diff --git a/src/codegen.cpp b/src/codegen.cpp index a5f8b85e22..0ac8ffb7e1 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1362,8 +1362,12 @@ static LLVMValueRef bigint_to_llvm_const(LLVMTypeRef type_ref, BigInt *bigint) { if (bigint->digit_count == 0) { return LLVMConstNull(type_ref); } - LLVMValueRef unsigned_val = LLVMConstIntOfArbitraryPrecision(type_ref, - bigint->digit_count, bigint_ptr(bigint)); + LLVMValueRef unsigned_val; + if (bigint->digit_count == 1) { + unsigned_val = LLVMConstInt(type_ref, bigint_ptr(bigint)[0], false); + } else { + unsigned_val = LLVMConstIntOfArbitraryPrecision(type_ref, bigint->digit_count, bigint_ptr(bigint)); + } if (bigint->is_negative) { return LLVMConstNeg(unsigned_val); } else { @@ -2420,9 +2424,10 @@ static LLVMValueRef ir_render_union_field_ptr(CodeGen *g, IrExecutable *executab if (ir_want_debug_safety(g, &instruction->base)) { LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, union_type->data.unionation.gen_tag_index, ""); LLVMValueRef tag_value = gen_load_untyped(g, tag_field_ptr, 0, false, ""); - LLVMValueRef expected_tag_value = LLVMConstInt(union_type->data.unionation.tag_type->type_ref, - field->value, false); + + LLVMValueRef expected_tag_value = bigint_to_llvm_const(union_type->data.unionation.tag_type->type_ref, + &field->value); LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnionCheckOk"); LLVMBasicBlockRef bad_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnionCheckFail"); LLVMValueRef ok_val = LLVMBuildICmp(g->builder, LLVMIntEQ, tag_value, expected_tag_value, ""); @@ -3364,9 +3369,9 @@ static LLVMValueRef ir_render_enum_tag(CodeGen *g, IrExecutable *executable, IrI static LLVMValueRef ir_render_init_enum(CodeGen *g, IrExecutable *executable, IrInstructionInitEnum *instruction) { TypeTableEntry *enum_type = instruction->enum_type; - uint32_t value = instruction->field->value; LLVMTypeRef tag_type_ref = enum_type->data.enumeration.tag_type->type_ref; - LLVMValueRef tag_value = LLVMConstInt(tag_type_ref, value, false); + + LLVMValueRef tag_value = bigint_to_llvm_const(tag_type_ref, &instruction->field->value); if (enum_type->data.enumeration.gen_field_count == 0) return tag_value; @@ -3429,8 +3434,9 @@ static LLVMValueRef ir_render_union_init(CodeGen *g, IrExecutable *executable, I if (union_type->data.unionation.gen_tag_index != SIZE_MAX) { LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, union_type->data.unionation.gen_tag_index, ""); - LLVMValueRef tag_value = LLVMConstInt(union_type->data.unionation.tag_type->type_ref, - type_union_field->value, false); + + LLVMValueRef tag_value = bigint_to_llvm_const(union_type->data.unionation.tag_type->type_ref, + &type_union_field->value); gen_store_untyped(g, tag_value, tag_field_ptr, 0, false); uncasted_union_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, @@ -4039,7 +4045,8 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { return union_value_ref; } - LLVMValueRef tag_value = LLVMConstInt(type_entry->data.unionation.tag_type->type_ref, const_val->data.x_union.tag, false); + LLVMValueRef tag_value = bigint_to_llvm_const(type_entry->data.unionation.tag_type->type_ref, + &const_val->data.x_union.tag); LLVMValueRef fields[2]; fields[type_entry->data.unionation.gen_union_index] = union_value_ref; @@ -4055,13 +4062,13 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { case TypeTableEntryIdEnum: { LLVMTypeRef tag_type_ref = type_entry->data.enumeration.tag_type->type_ref; - LLVMValueRef tag_value = LLVMConstInt(tag_type_ref, const_val->data.x_enum.tag, false); + LLVMValueRef tag_value = bigint_to_llvm_const(tag_type_ref, &const_val->data.x_enum.tag); if (type_entry->data.enumeration.gen_field_count == 0) { return tag_value; } else { LLVMTypeRef union_type_ref = type_entry->data.enumeration.union_type_ref; - TypeEnumField *enum_field = &type_entry->data.enumeration.fields[const_val->data.x_enum.tag]; - assert(enum_field->value == const_val->data.x_enum.tag); + TypeEnumField *enum_field = find_enum_field_by_tag(type_entry, &const_val->data.x_enum.tag); + assert(bigint_cmp(&enum_field->value, &const_val->data.x_enum.tag) == CmpEQ); LLVMValueRef union_value; bool make_unnamed_struct; diff --git a/src/ir.cpp b/src/ir.cpp index c6003fbf32..e51f52adae 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -8387,7 +8387,7 @@ static IrInstruction *ir_analyze_enum_to_int(IrAnalyze *ira, IrInstruction *sour return ira->codegen->invalid_instruction; IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type); - init_const_unsigned_negative(&result->value, wanted_type, val->data.x_enum.tag, false); + init_const_bigint(&result->value, wanted_type, &val->data.x_enum.tag); return result; } @@ -8469,7 +8469,7 @@ static IrInstruction *ir_analyze_int_to_enum(IrAnalyze *ira, IrInstruction *sour IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type); - result->value.data.x_enum.tag = bigint_as_unsigned(&val->data.x_bigint); + bigint_init_bigint(&result->value.data.x_enum.tag, &val->data.x_bigint); return result; } @@ -9148,7 +9148,7 @@ static bool ir_resolve_atomic_order(IrAnalyze *ira, IrInstruction *value, Atomic if (!const_val) return false; - *out = (AtomicOrder)const_val->data.x_enum.tag; + *out = (AtomicOrder)bigint_as_unsigned(&const_val->data.x_enum.tag); return true; } @@ -9168,7 +9168,27 @@ static bool ir_resolve_global_linkage(IrAnalyze *ira, IrInstruction *value, Glob if (!const_val) return false; - *out = (GlobalLinkageId)const_val->data.x_enum.tag; + *out = (GlobalLinkageId)bigint_as_unsigned(&const_val->data.x_enum.tag); + return true; +} + +static bool ir_resolve_float_mode(IrAnalyze *ira, IrInstruction *value, FloatMode *out) { + if (type_is_invalid(value->value.type)) + return false; + + ConstExprValue *float_mode_val = get_builtin_value(ira->codegen, "FloatMode"); + assert(float_mode_val->type->id == TypeTableEntryIdMetaType); + TypeTableEntry *float_mode_type = float_mode_val->data.x_type; + + IrInstruction *casted_value = ir_implicit_cast(ira, value, float_mode_type); + if (type_is_invalid(casted_value->value.type)) + return false; + + ConstExprValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); + if (!const_val) + return false; + + *out = (FloatMode)bigint_as_unsigned(&const_val->data.x_enum.tag); return true; } @@ -11825,13 +11845,13 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, - create_const_enum_tag(child_type, field->value), child_type, + create_const_enum_tag(child_type, &field->value), child_type, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); } else { bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, - create_const_unsigned_negative(child_type->data.enumeration.tag_type, field->value, false), + create_const_bigint(child_type->data.enumeration.tag_type, &field->value), child_type->data.enumeration.tag_type, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); } @@ -12420,21 +12440,11 @@ static TypeTableEntry *ir_analyze_instruction_set_float_mode(IrAnalyze *ira, return ira->codegen->builtin_types.entry_invalid; } - ConstExprValue *float_mode_val = get_builtin_value(ira->codegen, "FloatMode"); - assert(float_mode_val->type->id == TypeTableEntryIdMetaType); - TypeTableEntry *float_mode_enum_type = float_mode_val->data.x_type; - IrInstruction *float_mode_value = instruction->mode_value->other; - if (type_is_invalid(float_mode_value->value.type)) - return ira->codegen->builtin_types.entry_invalid; - IrInstruction *casted_value = ir_implicit_cast(ira, float_mode_value, float_mode_enum_type); - if (type_is_invalid(casted_value->value.type)) - return ira->codegen->builtin_types.entry_invalid; - ConstExprValue *mode_val = ir_resolve_const(ira, casted_value, UndefBad); - if (!mode_val) - return ira->codegen->builtin_types.entry_invalid; - bool want_fast_math = (mode_val->data.x_enum.tag == FloatModeOptimized); + FloatMode float_mode_scalar; + if (!ir_resolve_float_mode(ira, float_mode_value, &float_mode_scalar)) + return ira->codegen->builtin_types.entry_invalid; AstNode *source_node = instruction->base.source_node; if (*fast_math_set_node_ptr) { @@ -12444,7 +12454,7 @@ static TypeTableEntry *ir_analyze_instruction_set_float_mode(IrAnalyze *ira, return ira->codegen->builtin_types.entry_invalid; } *fast_math_set_node_ptr = source_node; - *fast_math_off_ptr = !want_fast_math; + *fast_math_off_ptr = (float_mode_scalar == FloatModeStrict); ir_build_const_from(ira, &instruction->base); return ira->codegen->builtin_types.entry_void; @@ -12835,7 +12845,7 @@ static IrInstruction *ir_analyze_enum_tag(IrAnalyze *ira, IrInstruction *source_ source_instr->scope, source_instr->source_node); const_instruction->base.value.type = tag_type; const_instruction->base.value.special = ConstValSpecialStatic; - bigint_init_unsigned(&const_instruction->base.value.data.x_bigint, val->data.x_enum.tag); + bigint_init_bigint(&const_instruction->base.value.data.x_bigint, &val->data.x_enum.tag); return &const_instruction->base; } @@ -13005,7 +13015,7 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira, assert(tag_type != nullptr); if (pointee_val) { ConstExprValue *out_val = ir_build_const_from(ira, &switch_target_instruction->base); - bigint_init_unsigned(&out_val->data.x_bigint, pointee_val->data.x_enum.tag); + bigint_init_bigint(&out_val->data.x_bigint, &pointee_val->data.x_enum.tag); return tag_type; } @@ -13056,9 +13066,9 @@ static TypeTableEntry *ir_analyze_instruction_switch_var(IrAnalyze *ira, IrInstr TypeEnumField *field; if (prong_value->value.type->id == TypeTableEntryIdEnumTag) { - field = &target_type->data.enumeration.fields[bigint_as_unsigned(&prong_val->data.x_bigint)]; + field = find_enum_field_by_tag(target_type, &prong_val->data.x_bigint); } else if (prong_value->value.type->id == TypeTableEntryIdEnum) { - field = &target_type->data.enumeration.fields[prong_val->data.x_enum.tag]; + field = find_enum_field_by_tag(target_type, &prong_val->data.x_enum.tag); } else { zig_unreachable(); } @@ -13503,8 +13513,8 @@ static TypeTableEntry *ir_analyze_instruction_container_init_list(IrAnalyze *ira TypeTableEntry *enum_type = container_type_value->value.type->data.enum_tag.enum_type; - uint64_t tag_uint = bigint_as_unsigned(&tag_value->data.x_bigint); - TypeEnumField *field = &enum_type->data.enumeration.fields[tag_uint]; + TypeEnumField *field = find_enum_field_by_tag(enum_type, &tag_value->data.x_bigint); + assert(field != nullptr); TypeTableEntry *this_field_type = field->type_entry; IrInstruction *init_value = instruction->items[0]->other; @@ -13520,7 +13530,7 @@ static TypeTableEntry *ir_analyze_instruction_container_init_list(IrAnalyze *ira if (!init_val) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); - out_val->data.x_enum.tag = tag_uint; + bigint_init_bigint(&out_val->data.x_enum.tag, &tag_value->data.x_bigint); out_val->data.x_enum.payload = init_val; return enum_type; } @@ -13859,7 +13869,7 @@ static TypeTableEntry *ir_analyze_instruction_type_id(IrAnalyze *ira, TypeTableEntry *result_type = var_value->data.x_type; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); - out_val->data.x_enum.tag = type_id_index(type_entry->id); + bigint_init_unsigned(&out_val->data.x_enum.tag, type_id_index(type_entry->id)); return result_type; } @@ -15117,7 +15127,9 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira if (switch_type->id == TypeTableEntryIdEnumTag) { TypeTableEntry *enum_type = switch_type->data.enum_tag.enum_type; - AstNode **field_prev_uses = allocate(enum_type->data.enumeration.src_field_count); + HashMap field_prev_uses = {}; + field_prev_uses.init(enum_type->data.enumeration.src_field_count); + for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) { IrInstructionCheckSwitchProngsRange *range = &instruction->ranges[range_i]; @@ -15129,41 +15141,52 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira if (type_is_invalid(end_value->value.type)) return ira->codegen->builtin_types.entry_invalid; - size_t start_index; - size_t end_index; + BigInt start_index; + BigInt end_index; if (start_value->value.type->id == TypeTableEntryIdEnumTag) { - start_index = bigint_as_unsigned(&start_value->value.data.x_bigint); + bigint_init_bigint(&start_index, &start_value->value.data.x_bigint); } else if (start_value->value.type->id == TypeTableEntryIdEnum) { - start_index = start_value->value.data.x_enum.tag; + bigint_init_bigint(&start_index, &start_value->value.data.x_enum.tag); } else { zig_unreachable(); } if (end_value->value.type->id == TypeTableEntryIdEnumTag) { - end_index = bigint_as_unsigned(&end_value->value.data.x_bigint); + bigint_init_bigint(&end_index, &end_value->value.data.x_bigint); } else if (end_value->value.type->id == TypeTableEntryIdEnum) { - end_index = end_value->value.data.x_enum.tag; + bigint_init_bigint(&end_index, &end_value->value.data.x_enum.tag); } else { zig_unreachable(); } - for (size_t field_index = start_index; field_index <= end_index; field_index += 1) { - AstNode *prev_node = field_prev_uses[field_index]; - if (prev_node != nullptr) { - TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_index]; + BigInt field_index; + bigint_init_bigint(&field_index, &start_index); + for (;;) { + Cmp cmp = bigint_cmp(&field_index, &end_index); + if (cmp == CmpGT) { + break; + } + auto entry = field_prev_uses.put_unique(field_index, start_value->source_node); + if (entry) { + AstNode *prev_node = entry->value; + TypeEnumField *enum_field = find_enum_field_by_tag(enum_type, &field_index); + assert(enum_field != nullptr); ErrorMsg *msg = ir_add_error(ira, start_value, buf_sprintf("duplicate switch value: '%s.%s'", buf_ptr(&enum_type->name), - buf_ptr(type_enum_field->name))); + buf_ptr(enum_field->name))); add_error_note(ira->codegen, msg, prev_node, buf_sprintf("other value is here")); } - field_prev_uses[field_index] = start_value->source_node; + bigint_incr(&field_index); } } if (!instruction->have_else_prong) { for (uint32_t i = 0; i < enum_type->data.enumeration.src_field_count; i += 1) { - if (field_prev_uses[i] == nullptr) { + TypeEnumField *enum_field = &enum_type->data.enumeration.fields[i]; + + auto entry = field_prev_uses.maybe_get(enum_field->value); + if (!entry) { ir_add_error(ira, &instruction->base, buf_sprintf("enumeration value '%s.%s' not handled in switch", buf_ptr(&enum_type->name), - buf_ptr(enum_type->data.enumeration.fields[i].name))); + buf_ptr(enum_field->name))); } } } diff --git a/src/parser.cpp b/src/parser.cpp index 7f25e3ef21..c36434b521 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2379,7 +2379,7 @@ static AstNode *ast_parse_use(ParseContext *pc, size_t *token_index, VisibMod vi /* ContainerDecl = option("extern" | "packed") ("struct" | "union" | ("enum" option(GroupedExpression))) "{" many(ContainerMember) "}" ContainerMember = (ContainerField | FnDef | GlobalVarDecl) -ContainerField = Symbol option(":" Expression) "," +ContainerField = Symbol option(":" PrefixOpExpression option("=" PrefixOpExpression "," */ static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index, bool mandatory) { Token *first_token = &pc->tokens->at(*token_index); @@ -2414,10 +2414,7 @@ static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index, AstNode *node = ast_create_node(pc, NodeTypeContainerDecl, first_token); node->data.container_decl.layout = layout; node->data.container_decl.kind = kind; - - if (kind == ContainerKindEnum || kind == ContainerKindStruct) { - node->data.container_decl.init_arg_expr = ast_parse_grouped_expr(pc, token_index, false); - } + node->data.container_decl.init_arg_expr = ast_parse_grouped_expr(pc, token_index, false); ast_eat_token(pc, token_index, TokenIdLBrace); @@ -2456,31 +2453,35 @@ static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index, AstNode *field_node = ast_create_node(pc, NodeTypeStructField, token); *token_index += 1; + node->data.container_decl.fields.append(field_node); field_node->data.struct_field.visib_mod = visib_mod; field_node->data.struct_field.name = token_buf(token); - Token *token = &pc->tokens->at(*token_index); - if (token->id == TokenIdComma || token->id == TokenIdRBrace) { - field_node->data.struct_field.type = ast_create_void_type_node(pc, token); + Token *colon_token = &pc->tokens->at(*token_index); + if (colon_token->id == TokenIdColon) { *token_index += 1; - node->data.container_decl.fields.append(field_node); - - if (token->id == TokenIdRBrace) { - break; - } + field_node->data.struct_field.type = ast_parse_prefix_op_expr(pc, token_index, true); } else { - ast_eat_token(pc, token_index, TokenIdColon); - field_node->data.struct_field.type = ast_parse_expression(pc, token_index, true); - node->data.container_decl.fields.append(field_node); + field_node->data.struct_field.type = ast_create_void_type_node(pc, colon_token); + } + Token *eq_token = &pc->tokens->at(*token_index); + if (eq_token->id == TokenIdEq) { + *token_index += 1; + field_node->data.struct_field.value = ast_parse_prefix_op_expr(pc, token_index, true); + } - Token *token = &pc->tokens->at(*token_index); - if (token->id == TokenIdRBrace) { - *token_index += 1; - break; - } else { - ast_eat_token(pc, token_index, TokenIdComma); - } + Token *next_token = &pc->tokens->at(*token_index); + if (next_token->id == TokenIdComma) { + *token_index += 1; + continue; } + + if (next_token->id == TokenIdRBrace) { + *token_index += 1; + break; + } + + ast_invalid_token_error(pc, next_token); } else { ast_invalid_token_error(pc, token); } @@ -2812,6 +2813,7 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont break; case NodeTypeStructField: visit_field(&node->data.struct_field.type, visit, context); + visit_field(&node->data.struct_field.value, visit, context); break; case NodeTypeContainerInitExpr: visit_field(&node->data.container_init_expr.type, visit, context); diff --git a/std/sort.zig b/std/sort.zig index 57fb0ab4c0..d02d685e07 100644 --- a/std/sort.zig +++ b/std/sort.zig @@ -16,7 +16,7 @@ pub fn sort_stable(comptime T: type, array: []T, comptime cmp: fn(a: &const T, b }} } -/// Unstable sort using O(n) stack space. Currentl implemented as quicksort. +/// Unstable sort using O(n) stack space. Currently implemented as quicksort. pub fn sort(comptime T: type, array: []T, comptime cmp: fn(a: &const T, b: &const T)->Cmp) { if (array.len > 0) { quicksort(T, array, 0, array.len - 1, cmp); diff --git a/test/cases/enum.zig b/test/cases/enum.zig index 6df858a48f..eda3cf6376 100644 --- a/test/cases/enum.zig +++ b/test/cases/enum.zig @@ -137,7 +137,6 @@ const AlignTestEnum = enum { B: u64, }; -const ValueCount0 = enum {}; const ValueCount1 = enum { I0 }; const ValueCount2 = enum { I0, I1 }; const ValueCount256 = enum { @@ -183,7 +182,6 @@ const ValueCount257 = enum { test "enum sizes" { comptime { - assert(@sizeOf(ValueCount0) == 0); assert(@sizeOf(ValueCount1) == 0); assert(@sizeOf(ValueCount2) == 1); assert(@sizeOf(ValueCount256) == 1); @@ -292,3 +290,57 @@ test "casting enum to its tag type" { fn testCastEnumToTagType(value: Small2) { assert(u2(value) == 1); } + +const MultipleChoice = enum(u32) { + A = 20, + B = 40, + C = 60, + D = 1000, +}; + +test "enum with specified tag values" { + testEnumWithSpecifiedTagValues(MultipleChoice.C); + comptime testEnumWithSpecifiedTagValues(MultipleChoice.C); +} + +fn testEnumWithSpecifiedTagValues(x: MultipleChoice) { + assert(u32(x) == 60); + assert(1234 == switch (x) { + MultipleChoice.A => 1, + MultipleChoice.B => 2, + MultipleChoice.C => u32(1234), + MultipleChoice.D => 4, + }); +} + +const MultipleChoice2 = enum(u32) { + Unspecified1, + A = 20, + Unspecified2, + B = 40, + Unspecified3, + C = 60, + Unspecified4, + D = 1000, + Unspecified5, +}; + +test "enum with specified and unspecified tag values" { + testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2.D); + comptime testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2.D); +} + +fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) { + assert(u32(x) == 1000); + assert(1234 == switch (x) { + MultipleChoice2.A => 1, + MultipleChoice2.B => 2, + MultipleChoice2.C => 3, + MultipleChoice2.D => u32(1234), + MultipleChoice2.Unspecified1 => 5, + MultipleChoice2.Unspecified2 => 6, + MultipleChoice2.Unspecified3 => 7, + MultipleChoice2.Unspecified4 => 8, + MultipleChoice2.Unspecified5 => 9, + }); +} diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 367dec08b3..e1de167ac5 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2307,11 +2307,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) { cases.add("@memberType enum out of bounds", \\comptime { - \\ _ = @memberType(Foo, 0); + \\ _ = @memberType(Foo, 1); \\} - \\const Foo = enum {}; + \\const Foo = enum {A,}; , - ".tmp_source.zig:2:26: error: member index 0 out of bounds; 'Foo' has 0 members"); + ".tmp_source.zig:2:26: error: member index 1 out of bounds; 'Foo' has 1 members"); cases.add("@memberName on unsupported type", \\comptime { @@ -2330,11 +2330,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) { cases.add("@memberName enum out of bounds", \\comptime { - \\ _ = @memberName(Foo, 0); + \\ _ = @memberName(Foo, 1); \\} - \\const Foo = enum {}; + \\const Foo = enum {A,}; , - ".tmp_source.zig:2:26: error: member index 0 out of bounds; 'Foo' has 0 members"); + ".tmp_source.zig:2:26: error: member index 1 out of bounds; 'Foo' has 1 members"); cases.add("calling var args extern function, passing array instead of pointer", \\export fn entry() { @@ -2447,4 +2447,47 @@ pub fn addCases(cases: &tests.CompileErrorContext) { \\} , ".tmp_source.zig:1:19: error: expected unsigned integer, found 'i2'"); + + cases.add("struct fields with value assignments", + \\const MultipleChoice = struct { + \\ A: i32 = 20, + \\}; + \\export fn entry() { + \\ var x: MultipleChoice = undefined; + \\} + , + ".tmp_source.zig:2:14: error: enums, not structs, support field assignment"); + + cases.add("union fields with value assignments", + \\const MultipleChoice = union { + \\ A: i32 = 20, + \\}; + \\export fn entry() { + \\ var x: MultipleChoice = undefined; + \\} + , + ".tmp_source.zig:2:14: error: enums, not unions, support field assignment"); + + cases.add("enum with 0 fields", + \\const Foo = enum {}; + \\export fn entry() -> usize { + \\ return @sizeOf(Foo); + \\} + , + ".tmp_source.zig:1:13: error: enums must have 1 or more fields"); + + cases.add("enum value already taken", + \\const MultipleChoice = 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"); } -- cgit v1.2.3 From 0ad1239522c70418990dc7b9da4e128da7cdd1d5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 3 Dec 2017 20:43:56 -0500 Subject: rework enums and unions and their relationship to each other * @enumTagName renamed to @tagName and it works on enums and union-enums * Remove the EnumTag type. Now there is only enum and union, and the tag type of a union is always an enum. * unions support specifying the tag enum type, and they support inferring an enum tag type. * Enums no longer support field types but they do support setting the tag values. Likewise union-enums when inferring an enum tag type support setting the tag values. * It is now an error for enums and unions to have 0 fields. * switch statements support union-enums closes #618 --- doc/langref.html.in | 21 +- src/all_types.hpp | 59 +-- src/analyze.cpp | 607 +++++++++++++++--------------- src/analyze.hpp | 6 +- src/ast_render.cpp | 7 + src/codegen.cpp | 239 ++++-------- src/ir.cpp | 488 ++++++++++-------------- src/ir_print.cpp | 31 +- src/parser.cpp | 29 +- std/build.zig | 42 +-- std/debug.zig | 28 +- std/os/child_process.zig | 10 +- std/os/path.zig | 2 +- test/cases/bugs/394.zig | 4 +- test/cases/enum.zig | 24 +- test/cases/enum_with_members.zig | 6 +- test/cases/misc.zig | 16 +- test/cases/reflection.zig | 4 +- test/cases/switch.zig | 16 +- test/cases/switch_prong_err_enum.zig | 4 +- test/cases/switch_prong_implicit_cast.zig | 8 +- test/cases/union.zig | 35 +- test/compile_errors.zig | 54 ++- test/tests.zig | 10 +- 24 files changed, 800 insertions(+), 950 deletions(-) (limited to 'src/parser.cpp') diff --git a/doc/langref.html.in b/doc/langref.html.in index 20b8ae1eee..76bf0ce237 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -136,7 +136,7 @@
  • @divFloor
  • @divTrunc
  • @embedFile
  • -
  • @enumTagName
  • +
  • @tagName
  • @EnumTagType
  • @errorName
  • @fence
  • @@ -2165,7 +2165,7 @@ test "enum variant switch" { }; } -// The @enumTagName and @memberCount builtin functions can be used to +// The @memberName and @memberCount builtin functions can be used to // the string representation and number of members respectively. const BuiltinType = enum { A: f32, @@ -2174,8 +2174,8 @@ const BuiltinType = enum { }; test "enum builtins" { - assert(mem.eql(u8, @enumTagName(BuiltinType.A { 0 }), "A")); - assert(mem.eql(u8, @enumTagName(BuiltinType.C), "C")); + assert(mem.eql(u8, @memberName(BuiltinType.A { 0 }), "A")); + assert(mem.eql(u8, @memberName(BuiltinType.C), "C")); assert(@memberCount(BuiltinType) == 3); }
    $ zig test enum.zig
    @@ -2189,8 +2189,9 @@ Test 4/4 enum builtins...OK

    See also:

    union

    TODO union documentation

    @@ -4252,10 +4253,10 @@ test.zig:6:2: error: found compile log statement -

    @enumTagName

    -
    @enumTagName(value: var) -> []const u8
    +

    @tagName

    +
    @tagName(value: var) -> []const u8

    - Converts an enum tag name to a slice of bytes. + Converts an enum value or union value to a slice of bytes representing the name.

    @EnumTagType

    @EnumTagType(T: type) -> type
    @@ -5843,7 +5844,9 @@ GroupedExpression = "(" Expression ")" KeywordLiteral = "true" | "false" | "null" | "continue" | "undefined" | "error" | "this" | "unreachable" -ContainerDecl = option("extern" | "packed") ("struct" | "union" | ("enum" option(GroupedExpression))) "{" many(ContainerMember) "}" +ContainerDecl = option("extern" | "packed") + ("struct" option(GroupedExpression) | "union" option("enum" option(GroupedExpression) | GroupedExpression) | ("enum" option(GroupedExpression))) + "{" many(ContainerMember) "}"

    Zen

    • Communicate intent precisely.
    • diff --git a/src/all_types.hpp b/src/all_types.hpp index 8d4ffc5b84..6061f63da6 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -97,11 +97,6 @@ struct ConstParent { } data; }; -struct ConstEnumValue { - BigInt tag; - ConstExprValue *payload; -}; - struct ConstStructValue { ConstExprValue *fields; ConstParent parent; @@ -249,7 +244,7 @@ struct ConstExprValue { ConstExprValue *x_maybe; ConstErrValue x_err_union; ErrorTableEntry *x_pure_err; - ConstEnumValue x_enum; + BigInt x_enum_tag; ConstStructValue x_struct; ConstUnionValue x_union; ConstArrayValue x_array; @@ -345,15 +340,14 @@ struct TldCompTime { struct TypeEnumField { Buf *name; - TypeTableEntry *type_entry; BigInt value; - uint32_t gen_index; + uint32_t decl_index; }; struct TypeUnionField { Buf *name; + TypeEnumField *enum_field; TypeTableEntry *type_entry; - BigInt value; uint32_t gen_index; }; @@ -773,7 +767,8 @@ struct AstNodeContainerDecl { ZigList fields; ZigList decls; ContainerLayout layout; - AstNode *init_arg_expr; // enum(T) or struct(endianness) + AstNode *init_arg_expr; // enum(T), struct(endianness), or union(T), or union(enum(T)) + bool auto_enum; // union(enum) }; struct AstNodeStructField { @@ -1010,13 +1005,9 @@ struct TypeTableEntryEnum { AstNode *decl_node; ContainerLayout layout; uint32_t src_field_count; - // number of fields in the union. 0 if enum with no payload - uint32_t gen_field_count; TypeEnumField *fields; bool is_invalid; // true if any fields are invalid - TypeTableEntry *tag_type; TypeTableEntry *tag_int_type; - LLVMTypeRef union_type_ref; ScopeDecls *decls_scope; @@ -1028,18 +1019,7 @@ struct TypeTableEntryEnum { bool zero_bits_loop_flag; bool zero_bits_known; - uint32_t abi_alignment; // also figured out with zero_bits pass - - size_t gen_union_index; - size_t gen_tag_index; - - uint32_t union_size_bytes; - TypeTableEntry *most_aligned_union_member; -}; -struct TypeTableEntryEnumTag { - TypeTableEntry *enum_type; - TypeTableEntry *int_type; bool generate_name_table; LLVMValueRef name_table; }; @@ -1054,7 +1034,7 @@ struct TypeTableEntryUnion { uint32_t gen_field_count; TypeUnionField *fields; bool is_invalid; // true if any fields are invalid - TypeTableEntry *tag_type; + TypeTableEntry *tag_type; // always an enum or null LLVMTypeRef union_type_ref; ScopeDecls *decls_scope; @@ -1119,7 +1099,6 @@ enum TypeTableEntryId { TypeTableEntryIdErrorUnion, TypeTableEntryIdPureError, TypeTableEntryIdEnum, - TypeTableEntryIdEnumTag, TypeTableEntryIdUnion, TypeTableEntryIdFn, TypeTableEntryIdNamespace, @@ -1148,7 +1127,6 @@ struct TypeTableEntry { TypeTableEntryMaybe maybe; TypeTableEntryError error; TypeTableEntryEnum enumeration; - TypeTableEntryEnumTag enum_tag; TypeTableEntryUnion unionation; TypeTableEntryFn fn; TypeTableEntryBoundFn bound_fn; @@ -1287,7 +1265,7 @@ enum BuiltinFnId { BuiltinFnIdBitCast, BuiltinFnIdIntToPtr, BuiltinFnIdPtrToInt, - BuiltinFnIdEnumTagName, + BuiltinFnIdTagName, BuiltinFnIdEnumTagType, BuiltinFnIdFieldParentPtr, BuiltinFnIdOffsetOf, @@ -1832,7 +1810,6 @@ enum IrInstructionId { IrInstructionIdStorePtr, IrInstructionIdFieldPtr, IrInstructionIdStructFieldPtr, - IrInstructionIdEnumFieldPtr, IrInstructionIdUnionFieldPtr, IrInstructionIdElemPtr, IrInstructionIdVarPtr, @@ -1857,7 +1834,7 @@ enum IrInstructionId { IrInstructionIdTestNonNull, IrInstructionIdUnwrapMaybe, IrInstructionIdMaybeWrap, - IrInstructionIdEnumTag, + IrInstructionIdUnionTag, IrInstructionIdClz, IrInstructionIdCtz, IrInstructionIdImport, @@ -1896,7 +1873,6 @@ enum IrInstructionId { IrInstructionIdErrWrapPayload, IrInstructionIdFnProto, IrInstructionIdTestComptime, - IrInstructionIdInitEnum, IrInstructionIdPtrCast, IrInstructionIdBitCast, IrInstructionIdWidenOrShorten, @@ -2092,14 +2068,6 @@ struct IrInstructionStructFieldPtr { bool is_const; }; -struct IrInstructionEnumFieldPtr { - IrInstruction base; - - IrInstruction *enum_ptr; - TypeEnumField *field; - bool is_const; -}; - struct IrInstructionUnionFieldPtr { IrInstruction base; @@ -2303,7 +2271,7 @@ struct IrInstructionClz { IrInstruction *value; }; -struct IrInstructionEnumTag { +struct IrInstructionUnionTag { IrInstruction base; IrInstruction *value; @@ -2573,15 +2541,6 @@ struct IrInstructionTestComptime { IrInstruction *value; }; -struct IrInstructionInitEnum { - IrInstruction base; - - TypeTableEntry *enum_type; - TypeEnumField *field; - IrInstruction *init_value; - LLVMValueRef tmp_ptr; -}; - struct IrInstructionPtrCast { IrInstruction base; diff --git a/src/analyze.cpp b/src/analyze.cpp index b7d12443a0..9d2d4f1a8f 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -223,7 +223,6 @@ bool type_is_complete(TypeTableEntry *type_entry) { case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: - case TypeTableEntryIdEnumTag: case TypeTableEntryIdArgTuple: return true; } @@ -260,7 +259,6 @@ bool type_has_zero_bits_known(TypeTableEntry *type_entry) { case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: - case TypeTableEntryIdEnumTag: case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: return true; @@ -1175,7 +1173,6 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: - case TypeTableEntryIdEnumTag: ensure_complete_type(g, type_entry); if (fn_type_id.cc == CallingConventionUnspecified && !type_is_copyable(g, type_entry)) { add_node_error(g, param_node->data.param_decl.type, @@ -1239,7 +1236,6 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: - case TypeTableEntryIdEnumTag: break; } @@ -1263,22 +1259,6 @@ bool type_is_invalid(TypeTableEntry *type_entry) { } -TypeTableEntry *create_enum_tag_type(CodeGen *g, TypeTableEntry *enum_type, TypeTableEntry *int_type) { - TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnumTag); - - buf_resize(&entry->name, 0); - buf_appendf(&entry->name, "@EnumTagType(%s)", buf_ptr(&enum_type->name)); - - entry->is_copyable = true; - entry->data.enum_tag.enum_type = enum_type; - entry->data.enum_tag.int_type = int_type; - entry->type_ref = int_type->type_ref; - entry->di_type = int_type->di_type; - entry->zero_bits = int_type->zero_bits; - - return entry; -} - static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) { assert(enum_type->id == TypeTableEntryIdEnum); @@ -1308,14 +1288,6 @@ static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) { assert(enum_type->data.enumeration.fields); ZigLLVMDIEnumerator **di_enumerators = allocate(field_count); - uint32_t gen_field_count = enum_type->data.enumeration.gen_field_count; - ZigLLVMDIType **union_inner_di_types = allocate(gen_field_count); - - TypeTableEntry *most_aligned_union_member = nullptr; - uint64_t size_of_most_aligned_member_in_bits = 0; - uint64_t biggest_align_in_bits = 0; - uint64_t biggest_size_in_bits = 0; - Scope *scope = &enum_type->data.enumeration.decls_scope->base; ImportTableEntry *import = get_scope_import(scope); @@ -1323,49 +1295,17 @@ static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) { enum_type->data.enumeration.embedded_in_current = true; for (uint32_t i = 0; i < field_count; i += 1) { - AstNode *field_node = decl_node->data.container_decl.fields.at(i); - TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[i]; - TypeTableEntry *field_type = type_enum_field->type_entry; - - di_enumerators[i] = ZigLLVMCreateDebugEnumerator(g->dbuilder, buf_ptr(type_enum_field->name), i); - - ensure_complete_type(g, field_type); - if (type_is_invalid(field_type)) { - enum_type->data.enumeration.is_invalid = true; - continue; - } - - if (!type_has_bits(field_type)) - continue; - - uint64_t store_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, field_type->type_ref); - uint64_t abi_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, field_type->type_ref); + TypeEnumField *enum_field = &enum_type->data.enumeration.fields[i]; - assert(store_size_in_bits > 0); - assert(abi_align_in_bits > 0); - - union_inner_di_types[type_enum_field->gen_index] = ZigLLVMCreateDebugMemberType(g->dbuilder, - ZigLLVMTypeToScope(enum_type->di_type), buf_ptr(type_enum_field->name), - import->di_file, (unsigned)(field_node->line + 1), - store_size_in_bits, - abi_align_in_bits, - 0, - 0, field_type->di_type); - - biggest_size_in_bits = max(biggest_size_in_bits, store_size_in_bits); - - if (!most_aligned_union_member || abi_align_in_bits > biggest_align_in_bits) { - most_aligned_union_member = field_type; - biggest_align_in_bits = abi_align_in_bits; - size_of_most_aligned_member_in_bits = store_size_in_bits; - } + // TODO send patch to LLVM to support APInt in createEnumerator instead of int64_t + // http://lists.llvm.org/pipermail/llvm-dev/2017-December/119456.html + di_enumerators[i] = ZigLLVMCreateDebugEnumerator(g->dbuilder, buf_ptr(enum_field->name), + bigint_as_signed(&enum_field->value)); } // unset temporary flag enum_type->data.enumeration.embedded_in_current = false; enum_type->data.enumeration.complete = true; - enum_type->data.enumeration.union_size_bytes = biggest_size_in_bits / 8; - enum_type->data.enumeration.most_aligned_union_member = most_aligned_union_member; if (enum_type->data.enumeration.is_invalid) return; @@ -1391,117 +1331,20 @@ static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) { } TypeTableEntry *tag_int_type = enum_type->data.enumeration.tag_int_type; - TypeTableEntry *tag_type_entry = create_enum_tag_type(g, enum_type, tag_int_type); - enum_type->data.enumeration.tag_type = tag_type_entry; - - uint64_t align_of_tag_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_int_type->type_ref); - - if (most_aligned_union_member) { - // create llvm type for union - uint64_t padding_in_bits = biggest_size_in_bits - size_of_most_aligned_member_in_bits; - LLVMTypeRef union_type_ref; - 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); - LLVMTypeRef union_element_types[] = { - most_aligned_union_member->type_ref, - padding_array->type_ref, - }; - union_type_ref = LLVMStructType(union_element_types, 2, false); - } else { - union_type_ref = most_aligned_union_member->type_ref; - } - enum_type->data.enumeration.union_type_ref = union_type_ref; - - assert(8*LLVMABIAlignmentOfType(g->target_data_ref, union_type_ref) >= biggest_align_in_bits); - assert(8*LLVMStoreSizeOfType(g->target_data_ref, union_type_ref) >= biggest_size_in_bits); - - if (align_of_tag_in_bits >= biggest_align_in_bits) { - enum_type->data.enumeration.gen_tag_index = 0; - enum_type->data.enumeration.gen_union_index = 1; - } else { - enum_type->data.enumeration.gen_union_index = 0; - enum_type->data.enumeration.gen_tag_index = 1; - } - - // create llvm type for root struct - LLVMTypeRef root_struct_element_types[2]; - root_struct_element_types[enum_type->data.enumeration.gen_tag_index] = tag_type_entry->type_ref; - root_struct_element_types[enum_type->data.enumeration.gen_union_index] = union_type_ref; - LLVMStructSetBody(enum_type->type_ref, root_struct_element_types, 2, false); - - // create debug type for tag - uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, tag_type_entry->type_ref); - uint64_t tag_debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_type_entry->type_ref); - ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder, - ZigLLVMTypeToScope(enum_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_entry->di_type, ""); - - // create debug type for union - ZigLLVMDIType *union_di_type = ZigLLVMCreateDebugUnionType(g->dbuilder, - ZigLLVMTypeToScope(enum_type->di_type), "AnonUnion", - import->di_file, (unsigned)(decl_node->line + 1), - biggest_size_in_bits, biggest_align_in_bits, 0, union_inner_di_types, - gen_field_count, 0, ""); - - // create debug types for members of root struct - uint64_t tag_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, enum_type->type_ref, - enum_type->data.enumeration.gen_tag_index); - ZigLLVMDIType *tag_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder, - ZigLLVMTypeToScope(enum_type->di_type), "tag_field", - import->di_file, (unsigned)(decl_node->line + 1), - tag_debug_size_in_bits, - tag_debug_align_in_bits, - tag_offset_in_bits, - 0, tag_di_type); - - uint64_t union_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, enum_type->type_ref, - enum_type->data.enumeration.gen_union_index); - ZigLLVMDIType *union_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder, - ZigLLVMTypeToScope(enum_type->di_type), "union_field", - import->di_file, (unsigned)(decl_node->line + 1), - biggest_size_in_bits, - biggest_align_in_bits, - union_offset_in_bits, - 0, union_di_type); - - // create debug type for root struct - ZigLLVMDIType *di_root_members[2]; - di_root_members[enum_type->data.enumeration.gen_tag_index] = tag_member_di_type; - di_root_members[enum_type->data.enumeration.gen_union_index] = union_member_di_type; - - uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, enum_type->type_ref); - uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, enum_type->type_ref); - ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder, - ZigLLVMFileToScope(import->di_file), - buf_ptr(&enum_type->name), - import->di_file, (unsigned)(decl_node->line + 1), - debug_size_in_bits, - debug_align_in_bits, - 0, nullptr, di_root_members, 2, 0, nullptr, ""); - - ZigLLVMReplaceTemporary(g->dbuilder, enum_type->di_type, replacement_di_type); - enum_type->di_type = replacement_di_type; - } else { - // create llvm type for root struct - enum_type->type_ref = tag_type_entry->type_ref; - // create debug type for tag - uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, tag_type_entry->type_ref); - uint64_t tag_debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_type_entry->type_ref); - ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder, - ZigLLVMFileToScope(import->di_file), buf_ptr(&enum_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_entry->di_type, ""); + // create debug type for tag + uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, tag_int_type->type_ref); + uint64_t tag_debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_int_type->type_ref); + ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder, + ZigLLVMFileToScope(import->di_file), buf_ptr(&enum_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_int_type->di_type, ""); - ZigLLVMReplaceTemporary(g->dbuilder, enum_type->di_type, tag_di_type); - enum_type->di_type = tag_di_type; - } + ZigLLVMReplaceTemporary(g->dbuilder, enum_type->di_type, tag_di_type); + enum_type->di_type = tag_di_type; } static bool type_allowed_in_packed_struct(TypeTableEntry *type_entry) { @@ -1517,7 +1360,6 @@ static bool type_allowed_in_packed_struct(TypeTableEntry *type_entry) { case TypeTableEntryIdNullLit: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdPureError: - case TypeTableEntryIdEnumTag: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: @@ -1541,8 +1383,7 @@ static bool type_allowed_in_packed_struct(TypeTableEntry *type_entry) { return child_type->id == TypeTableEntryIdPointer || child_type->id == TypeTableEntryIdFn; } case TypeTableEntryIdEnum: - return type_entry->data.enumeration.gen_field_count == 0 && - type_entry->data.enumeration.decl_node->data.container_decl.init_arg_expr != nullptr; + return type_entry->data.enumeration.decl_node->data.container_decl.init_arg_expr != nullptr; } zig_unreachable(); } @@ -1850,6 +1691,7 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) { if (union_type->data.unionation.embedded_in_current) { if (!union_type->data.unionation.reported_infinite_err) { union_type->data.unionation.reported_infinite_err = true; + union_type->data.unionation.is_invalid = true; add_node_error(g, decl_node, buf_sprintf("union '%s' contains itself", buf_ptr(&union_type->name))); } return; @@ -1871,8 +1713,7 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) { uint64_t biggest_align_in_bits = 0; uint64_t biggest_size_in_bits = 0; - bool auto_layout = (union_type->data.unionation.layout == ContainerLayoutAuto); - ZigLLVMDIEnumerator **di_enumerators = allocate(field_count); + ZigLLVMDIEnumerator **di_enumerators; Scope *scope = &union_type->data.unionation.decls_scope->base; ImportTableEntry *import = get_scope_import(scope); @@ -1880,10 +1721,77 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) { // 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 *type_union_field = &union_type->data.unionation.fields[i]; - TypeTableEntry *field_type = type_union_field->type_entry; + 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); if (type_is_invalid(field_type)) { @@ -1891,19 +1799,68 @@ 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; - di_enumerators[i] = ZigLLVMCreateDebugEnumerator(g->dbuilder, buf_ptr(type_union_field->name), i); - uint64_t store_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, field_type->type_ref); uint64_t abi_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, field_type->type_ref); assert(store_size_in_bits > 0); assert(abi_align_in_bits > 0); - union_inner_di_types[type_union_field->gen_index] = ZigLLVMCreateDebugMemberType(g->dbuilder, - ZigLLVMTypeToScope(union_type->di_type), buf_ptr(type_union_field->name), + union_inner_di_types[union_field->gen_index] = ZigLLVMCreateDebugMemberType(g->dbuilder, + ZigLLVMTypeToScope(union_type->di_type), buf_ptr(union_field->enum_field->name), import->di_file, (unsigned)(field_node->line + 1), store_size_in_bits, abi_align_in_bits, @@ -1919,6 +1876,49 @@ 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; union_type->data.unionation.complete = true; @@ -1950,11 +1950,9 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) { assert(most_aligned_union_member != nullptr); - bool want_safety = auto_layout && (field_count >= 2); uint64_t padding_in_bits = biggest_size_in_bits - size_of_most_aligned_member_in_bits; - - if (!want_safety) { + if (tag_type == 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); @@ -1994,6 +1992,8 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) { padding_array->type_ref, }; union_type_ref = LLVMStructType(union_element_types, 2, false); + } else if (most_aligned_union_member == nullptr) { + zig_panic("TODO zero bit payload"); } else { union_type_ref = most_aligned_union_member->type_ref; } @@ -2003,9 +2003,7 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) { assert(8*LLVMStoreSizeOfType(g->target_data_ref, union_type_ref) >= biggest_size_in_bits); // create llvm type for root struct - TypeTableEntry *tag_int_type = get_smallest_unsigned_int_type(g, field_count - 1); - TypeTableEntry *tag_type_entry = tag_int_type; - union_type->data.unionation.tag_type = tag_type_entry; + TypeTableEntry *tag_int_type = tag_type->data.enumeration.tag_int_type; uint64_t align_of_tag_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_int_type->type_ref); if (align_of_tag_in_bits >= biggest_align_in_bits) { @@ -2017,21 +2015,24 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) { } LLVMTypeRef root_struct_element_types[2]; - root_struct_element_types[union_type->data.unionation.gen_tag_index] = tag_type_entry->type_ref; + root_struct_element_types[union_type->data.unionation.gen_tag_index] = tag_type->type_ref; root_struct_element_types[union_type->data.unionation.gen_union_index] = union_type_ref; LLVMStructSetBody(union_type->type_ref, root_struct_element_types, 2, false); // create debug type for root struct - // create debug type for tag - uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, tag_type_entry->type_ref); - uint64_t tag_debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_type_entry->type_ref); - 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_entry->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); + 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, @@ -2046,19 +2047,19 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) { union_type->data.unionation.gen_tag_index); ZigLLVMDIType *union_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder, - ZigLLVMTypeToScope(union_type->di_type), "union_field", + ZigLLVMTypeToScope(union_type->di_type), "payload", import->di_file, (unsigned)(decl_node->line + 1), biggest_size_in_bits, biggest_align_in_bits, union_offset_in_bits, 0, union_di_type); ZigLLVMDIType *tag_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder, - ZigLLVMTypeToScope(union_type->di_type), "tag_field", + ZigLLVMTypeToScope(union_type->di_type), "tag", import->di_file, (unsigned)(decl_node->line + 1), tag_debug_size_in_bits, tag_debug_align_in_bits, tag_offset_in_bits, - 0, tag_di_type); + 0, tag_type->di_type); ZigLLVMDIType *di_root_members[2]; di_root_members[union_type->data.unionation.gen_tag_index] = tag_member_di_type; @@ -2104,7 +2105,6 @@ static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) { enum_type->data.enumeration.fields = nullptr; enum_type->data.enumeration.is_invalid = true; enum_type->data.enumeration.zero_bits_loop_flag = false; - enum_type->data.enumeration.gen_field_count = 0; enum_type->data.enumeration.zero_bits_known = true; return; } @@ -2112,8 +2112,6 @@ static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) { enum_type->data.enumeration.src_field_count = field_count; enum_type->data.enumeration.fields = allocate(field_count); - uint32_t biggest_align_bytes = 0; - Scope *scope = &enum_type->data.enumeration.decls_scope->base; HashMap occupied_tag_values = {}; @@ -2143,14 +2141,20 @@ static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) { } } enum_type->data.enumeration.tag_int_type = tag_int_type; + enum_type->type_ref = tag_int_type->type_ref; - uint32_t gen_field_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); TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_i]; type_enum_field->name = field_node->data.struct_field.name; - TypeTableEntry *field_type = analyze_type_expr(g, scope, field_node->data.struct_field.type); - type_enum_field->type_entry = field_type; + type_enum_field->decl_index = field_i; + + if (field_node->data.struct_field.type != nullptr) { + ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.type, + buf_sprintf("structs and unions, not enums, support field types")); + add_error_note(g, msg, decl_node, + buf_sprintf("consider 'union(enum)' here")); + } AstNode *tag_value = field_node->data.struct_field.value; @@ -2179,23 +2183,6 @@ static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) { continue; } } - - type_ensure_zero_bits_known(g, field_type); - if (type_is_invalid(field_type)) { - enum_type->data.enumeration.is_invalid = true; - continue; - } - - if (!type_has_bits(field_type)) - continue; - - type_enum_field->gen_index = gen_field_index; - gen_field_index += 1; - - uint32_t field_align_bytes = get_abi_alignment(g, field_type); - if (field_align_bytes > biggest_align_bytes) { - biggest_align_bytes = field_align_bytes; - } } // Now iterate again and populate the unspecified tag values @@ -2227,15 +2214,8 @@ static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) { } enum_type->data.enumeration.zero_bits_loop_flag = false; - enum_type->data.enumeration.gen_field_count = gen_field_index; - enum_type->zero_bits = (gen_field_index == 0 && field_count < 2); + enum_type->zero_bits = (field_count < 2); enum_type->data.enumeration.zero_bits_known = true; - - // also compute abi_alignment - if (!enum_type->zero_bits) { - uint32_t align_of_tag_in_bytes = LLVMABIAlignmentOfType(g->target_data_ref, tag_int_type->type_ref); - enum_type->data.enumeration.abi_alignment = max(align_of_tag_in_bytes, biggest_align_bytes); - } } static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) { @@ -2279,6 +2259,13 @@ static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) { AstNode *field_node = decl_node->data.container_decl.fields.at(i); TypeStructField *type_struct_field = &struct_type->data.structure.fields[i]; type_struct_field->name = field_node->data.struct_field.name; + + if (field_node->data.struct_field.type == nullptr) { + add_node_error(g, field_node, buf_sprintf("struct field missing type")); + struct_type->data.structure.is_invalid = true; + continue; + } + TypeTableEntry *field_type = analyze_type_expr(g, scope, field_node->data.struct_field.type); type_struct_field->type_entry = field_type; type_struct_field->src_index = i; @@ -2338,6 +2325,16 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) { assert(!union_type->data.unionation.fields); uint32_t field_count = (uint32_t)decl_node->data.container_decl.fields.length; + if (field_count == 0) { + add_node_error(g, decl_node, buf_sprintf("unions must have 1 or more fields")); + + union_type->data.unionation.src_field_count = field_count; + union_type->data.unionation.fields = nullptr; + union_type->data.unionation.is_invalid = true; + union_type->data.unionation.zero_bits_loop_flag = false; + union_type->data.unionation.zero_bits_known = true; + return; + } union_type->data.unionation.src_field_count = field_count; union_type->data.unionation.fields = allocate(field_count); @@ -2348,17 +2345,23 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_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); - TypeUnionField *type_union_field = &union_type->data.unionation.fields[i]; - type_union_field->name = field_node->data.struct_field.name; - TypeTableEntry *field_type = analyze_type_expr(g, scope, field_node->data.struct_field.type); - type_union_field->type_entry = field_type; + TypeUnionField *union_field = &union_type->data.unionation.fields[i]; + union_field->name = field_node->data.struct_field.name; - // TODO look for enum arg to union - bigint_init_unsigned(&type_union_field->value, i); + 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 (field_node->data.struct_field.value != nullptr) { - add_node_error(g, field_node->data.struct_field.value, - buf_sprintf("enums, not unions, support field assignment")); + 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) { + ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.value, + buf_sprintf("non-enum union field assignment")); + add_error_note(g, msg, decl_node, + buf_sprintf("consider 'union(enum)' here")); } type_ensure_zero_bits_known(g, field_type); @@ -2370,7 +2373,7 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) { if (!type_has_bits(field_type)) continue; - type_union_field->gen_index = gen_field_index; + union_field->gen_index = gen_field_index; gen_field_index += 1; uint32_t field_align_bytes = get_abi_alignment(g, field_type); @@ -2379,11 +2382,32 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) { } } - bool auto_layout = (union_type->data.unionation.layout == ContainerLayoutAuto); + bool src_have_tag = decl_node->data.container_decl.auto_enum || + decl_node->data.container_decl.init_arg_expr != nullptr; + + if (src_have_tag && union_type->data.unionation.layout != ContainerLayoutAuto) { + const char *qual_str; + switch (union_type->data.unionation.layout) { + case ContainerLayoutAuto: + zig_unreachable(); + case ContainerLayoutPacked: + qual_str = "packed"; + break; + case ContainerLayoutExtern: + qual_str = "extern"; + break; + } + AstNode *source_node = (decl_node->data.container_decl.init_arg_expr != nullptr) ? + decl_node->data.container_decl.init_arg_expr : decl_node; + add_node_error(g, source_node, + buf_sprintf("%s union does not support enum tag type", qual_str)); + union_type->data.unionation.is_invalid = true; + return; + } 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 || !auto_layout)); + 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 @@ -2848,7 +2872,6 @@ TypeTableEntry *validate_var_type(CodeGen *g, AstNode *source_node, TypeTableEnt case TypeTableEntryIdUnion: case TypeTableEntryIdFn: case TypeTableEntryIdBoundFn: - case TypeTableEntryIdEnumTag: return type_entry; } zig_unreachable(); @@ -3265,19 +3288,20 @@ TypeUnionField *find_union_type_field(TypeTableEntry *type_entry, Buf *name) { assert(type_entry->data.unionation.complete); for (uint32_t i = 0; i < type_entry->data.unionation.src_field_count; i += 1) { TypeUnionField *field = &type_entry->data.unionation.fields[i]; - if (buf_eql_buf(field->name, name)) { + if (buf_eql_buf(field->enum_field->name, name)) { return field; } } return nullptr; } -static TypeUnionField *find_union_field_by_tag(TypeTableEntry *type_entry, const BigInt *tag) { +TypeUnionField *find_union_field_by_tag(TypeTableEntry *type_entry, const BigInt *tag) { assert(type_entry->id == TypeTableEntryIdUnion); assert(type_entry->data.unionation.complete); + assert(type_entry->data.unionation.gen_tag_index != SIZE_MAX); for (uint32_t i = 0; i < type_entry->data.unionation.src_field_count; i += 1) { TypeUnionField *field = &type_entry->data.unionation.fields[i]; - if (bigint_cmp(&field->value, tag) == CmpEQ) { + if (bigint_cmp(&field->enum_field->value, tag) == CmpEQ) { return field; } } @@ -3323,7 +3347,6 @@ static bool is_container(TypeTableEntry *type_entry) { case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: - case TypeTableEntryIdEnumTag: case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: return false; @@ -3374,7 +3397,6 @@ void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry) { case TypeTableEntryIdBoundFn: case TypeTableEntryIdInvalid: case TypeTableEntryIdVar: - case TypeTableEntryIdEnumTag: case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: zig_unreachable(); @@ -3828,7 +3850,7 @@ bool handle_is_ptr(TypeTableEntry *type_entry) { case TypeTableEntryIdPointer: case TypeTableEntryIdPureError: case TypeTableEntryIdFn: - case TypeTableEntryIdEnumTag: + case TypeTableEntryIdEnum: return false; case TypeTableEntryIdArray: case TypeTableEntryIdStruct: @@ -3836,9 +3858,6 @@ bool handle_is_ptr(TypeTableEntry *type_entry) { return type_has_bits(type_entry); case TypeTableEntryIdErrorUnion: return type_has_bits(type_entry->data.error.child_type); - case TypeTableEntryIdEnum: - assert(type_entry->data.enumeration.complete); - return type_entry->data.enumeration.gen_field_count != 0; case TypeTableEntryIdMaybe: return type_has_bits(type_entry->data.maybe.child_type) && type_entry->data.maybe.child_type->id != TypeTableEntryIdPointer && @@ -3980,7 +3999,6 @@ static uint32_t hash_const_val(ConstExprValue *const_val) { return (uint32_t)4149439618; case TypeTableEntryIdInt: case TypeTableEntryIdNumLitInt: - case TypeTableEntryIdEnumTag: { uint32_t result = 1331471175; for (size_t i = 0; i < const_val->data.x_bigint.digit_count; i += 1) { @@ -3989,6 +4007,15 @@ static uint32_t hash_const_val(ConstExprValue *const_val) { } return result; } + case TypeTableEntryIdEnum: + { + uint32_t result = 31643936; + for (size_t i = 0; i < const_val->data.x_enum_tag.digit_count; i += 1) { + uint64_t digit = bigint_ptr(&const_val->data.x_enum_tag)[i]; + result ^= ((uint32_t)(digit >> 32)) ^ (uint32_t)(result); + } + return result; + } case TypeTableEntryIdFloat: switch (const_val->type->data.floating.bit_count) { case 32: @@ -4089,9 +4116,6 @@ static uint32_t hash_const_val(ConstExprValue *const_val) { case TypeTableEntryIdPureError: // TODO better hashing algorithm return 2630160122; - case TypeTableEntryIdEnum: - // TODO better hashing algorithm - return 31643936; case TypeTableEntryIdFn: return 4133894920 ^ hash_ptr(const_val->data.x_fn.fn_entry); case TypeTableEntryIdNamespace: @@ -4224,7 +4248,6 @@ bool type_requires_comptime(TypeTableEntry *type_entry) { case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdPointer: - case TypeTableEntryIdEnumTag: case TypeTableEntryIdVoid: case TypeTableEntryIdUnreachable: return false; @@ -4295,6 +4318,7 @@ ConstExprValue *create_const_bigint(TypeTableEntry *type, const BigInt *bigint) return const_val; } + void init_const_unsigned_negative(ConstExprValue *const_val, TypeTableEntry *type, uint64_t x, bool negative) { const_val->special = ConstValSpecialStatic; const_val->type = type; @@ -4358,18 +4382,19 @@ ConstExprValue *create_const_float(TypeTableEntry *type, double value) { return const_val; } -void init_const_enum_tag(ConstExprValue *const_val, TypeTableEntry *type, const BigInt *tag) { +void init_const_enum(ConstExprValue *const_val, TypeTableEntry *type, const BigInt *tag) { const_val->special = ConstValSpecialStatic; const_val->type = type; - bigint_init_bigint(&const_val->data.x_enum.tag, tag); + bigint_init_bigint(&const_val->data.x_enum_tag, tag); } -ConstExprValue *create_const_enum_tag(TypeTableEntry *type, const BigInt *tag) { +ConstExprValue *create_const_enum(TypeTableEntry *type, const BigInt *tag) { ConstExprValue *const_val = create_const_vals(1); - init_const_enum_tag(const_val, type, tag); + init_const_enum(const_val, type, tag); return const_val; } + void init_const_bool(CodeGen *g, ConstExprValue *const_val, bool value) { const_val->special = ConstValSpecialStatic; const_val->type = g->builtin_types.entry_bool; @@ -4567,20 +4592,8 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) { switch (a->type->id) { case TypeTableEntryIdOpaque: zig_unreachable(); - case TypeTableEntryIdEnum: { - ConstEnumValue *enum1 = &a->data.x_enum; - ConstEnumValue *enum2 = &b->data.x_enum; - if (bigint_cmp(&enum1->tag, &enum2->tag) == CmpEQ) { - TypeEnumField *field = find_enum_field_by_tag(a->type, &enum1->tag); - assert(field != nullptr); - if (type_has_bits(field->type_entry)) { - zig_panic("TODO const expr analyze enum field value for equality"); - } else { - return true; - } - } - return false; - } + case TypeTableEntryIdEnum: + return bigint_cmp(&a->data.x_enum_tag, &b->data.x_enum_tag) == CmpEQ; case TypeTableEntryIdUnion: { ConstUnionValue *union1 = &a->data.x_union; ConstUnionValue *union2 = &b->data.x_union; @@ -4622,7 +4635,6 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) { return bigfloat_cmp(&a->data.x_bigfloat, &b->data.x_bigfloat) == CmpEQ; case TypeTableEntryIdInt: case TypeTableEntryIdNumLitInt: - case TypeTableEntryIdEnumTag: return bigint_cmp(&a->data.x_bigint, &b->data.x_bigint) == CmpEQ; case TypeTableEntryIdPointer: if (a->data.x_ptr.special != b->data.x_ptr.special) @@ -4949,7 +4961,8 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { } case TypeTableEntryIdEnum: { - buf_appendf(buf, "(enum %s constant)", buf_ptr(&type_entry->name)); + TypeEnumField *field = find_enum_field_by_tag(type_entry, &const_val->data.x_enum_tag); + buf_appendf(buf, "%s.%s", buf_ptr(&type_entry->name), buf_ptr(field->name)); return; } case TypeTableEntryIdErrorUnion: @@ -4967,14 +4980,6 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { buf_appendf(buf, "(pure error constant)"); return; } - case TypeTableEntryIdEnumTag: - { - TypeTableEntry *enum_type = type_entry->data.enum_tag.enum_type; - size_t field_index = bigint_as_unsigned(&const_val->data.x_bigint); - TypeEnumField *field = &enum_type->data.enumeration.fields[field_index]; - buf_appendf(buf, "%s.%s", buf_ptr(&enum_type->name), buf_ptr(field->name)); - return; - } case TypeTableEntryIdArgTuple: { buf_appendf(buf, "(args value)"); @@ -5036,7 +5041,6 @@ uint32_t type_id_hash(TypeId x) { case TypeTableEntryIdErrorUnion: case TypeTableEntryIdPureError: case TypeTableEntryIdEnum: - case TypeTableEntryIdEnumTag: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: case TypeTableEntryIdNamespace: @@ -5081,7 +5085,6 @@ bool type_id_eql(TypeId a, TypeId b) { case TypeTableEntryIdErrorUnion: case TypeTableEntryIdPureError: case TypeTableEntryIdEnum: - case TypeTableEntryIdEnumTag: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: case TypeTableEntryIdNamespace: @@ -5196,7 +5199,6 @@ static const TypeTableEntryId all_type_ids[] = { TypeTableEntryIdErrorUnion, TypeTableEntryIdPureError, TypeTableEntryIdEnum, - TypeTableEntryIdEnumTag, TypeTableEntryIdUnion, TypeTableEntryIdFn, TypeTableEntryIdNamespace, @@ -5254,22 +5256,20 @@ size_t type_id_index(TypeTableEntryId id) { return 15; case TypeTableEntryIdEnum: return 16; - case TypeTableEntryIdEnumTag: - return 17; case TypeTableEntryIdUnion: - return 18; + return 17; case TypeTableEntryIdFn: - return 19; + return 18; case TypeTableEntryIdNamespace: - return 20; + return 19; case TypeTableEntryIdBlock: - return 21; + return 20; case TypeTableEntryIdBoundFn: - return 22; + return 21; case TypeTableEntryIdArgTuple: - return 23; + return 22; case TypeTableEntryIdOpaque: - return 24; + return 23; } zig_unreachable(); } @@ -5313,8 +5313,6 @@ const char *type_id_name(TypeTableEntryId id) { return "Error"; case TypeTableEntryIdEnum: return "Enum"; - case TypeTableEntryIdEnumTag: - return "EnumTag"; case TypeTableEntryIdUnion: return "Union"; case TypeTableEntryIdFn: @@ -5381,9 +5379,6 @@ uint32_t get_abi_alignment(CodeGen *g, TypeTableEntry *type_entry) { if (type_entry->id == TypeTableEntryIdStruct) { assert(type_entry->data.structure.abi_alignment != 0); return type_entry->data.structure.abi_alignment; - } else if (type_entry->id == TypeTableEntryIdEnum) { - assert(type_entry->data.enumeration.abi_alignment != 0); - return type_entry->data.enumeration.abi_alignment; } else if (type_entry->id == TypeTableEntryIdUnion) { assert(type_entry->data.unionation.abi_alignment != 0); return type_entry->data.unionation.abi_alignment; diff --git a/src/analyze.hpp b/src/analyze.hpp index 50eb2a800f..e6100c692c 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -65,6 +65,7 @@ ScopeDecls *get_container_scope(TypeTableEntry *type_entry); TypeEnumField *find_enum_type_field(TypeTableEntry *enum_type, Buf *name); TypeUnionField *find_union_type_field(TypeTableEntry *type_entry, Buf *name); TypeEnumField *find_enum_field_by_tag(TypeTableEntry *enum_type, const BigInt *tag); +TypeUnionField *find_union_field_by_tag(TypeTableEntry *type_entry, const BigInt *tag); bool is_container_ref(TypeTableEntry *type_entry); void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node); @@ -126,8 +127,8 @@ ConstExprValue *create_const_usize(CodeGen *g, uint64_t x); void init_const_float(ConstExprValue *const_val, TypeTableEntry *type, double value); ConstExprValue *create_const_float(TypeTableEntry *type, double value); -void init_const_enum_tag(ConstExprValue *const_val, TypeTableEntry *type, const BigInt *tag); -ConstExprValue *create_const_enum_tag(TypeTableEntry *type, const BigInt *tag); +void init_const_enum(ConstExprValue *const_val, TypeTableEntry *type, const BigInt *tag); +ConstExprValue *create_const_enum(TypeTableEntry *type, const BigInt *tag); void init_const_bool(CodeGen *g, ConstExprValue *const_val, bool value); ConstExprValue *create_const_bool(CodeGen *g, bool value); @@ -163,7 +164,6 @@ ConstExprValue *create_const_vals(size_t count); TypeTableEntry *make_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits); ConstParent *get_const_val_parent(CodeGen *g, ConstExprValue *value); -TypeTableEntry *create_enum_tag_type(CodeGen *g, TypeTableEntry *enum_type, TypeTableEntry *int_type); void expand_undef_array(CodeGen *g, ConstExprValue *const_val); void update_compile_var(CodeGen *g, Buf *name, ConstExprValue *value); diff --git a/src/ast_render.cpp b/src/ast_render.cpp index cdc18c7252..4f4dc1decd 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -661,11 +661,18 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { const char *layout_str = layout_string(node->data.container_decl.layout); const char *container_str = container_string(node->data.container_decl.kind); fprintf(ar->f, "%s%s", layout_str, container_str); + if (node->data.container_decl.auto_enum) { + fprintf(ar->f, "(enum"); + } if (node->data.container_decl.init_arg_expr != nullptr) { fprintf(ar->f, "("); render_node_grouped(ar, node->data.container_decl.init_arg_expr); fprintf(ar->f, ")"); } + if (node->data.container_decl.auto_enum) { + fprintf(ar->f, ")"); + } + fprintf(ar->f, " {\n"); ar->indent += ar->indent_size; for (size_t field_i = 0; field_i < node->data.container_decl.fields.length; field_i += 1) { diff --git a/src/codegen.cpp b/src/codegen.cpp index 0ac8ffb7e1..dbf4f8522b 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1631,12 +1631,8 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, type_entry->data.integral.is_signed); return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, ""); } else if (type_entry->id == TypeTableEntryIdEnum) { - if (type_entry->data.enumeration.gen_field_count == 0) { - LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, false); - return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, ""); - } else { - zig_unreachable(); - } + LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, false); + return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, ""); } else if (type_entry->id == TypeTableEntryIdPureError || type_entry->id == TypeTableEntryIdPointer || type_entry->id == TypeTableEntryIdBool) @@ -1920,9 +1916,7 @@ static LLVMValueRef ir_render_widen_or_shorten(CodeGen *g, IrExecutable *executa // enum_tag to the underlying int type TypeTableEntry *int_type; if (actual_type->id == TypeTableEntryIdEnum) { - TypeTableEntry *tag_type = actual_type->data.enumeration.tag_type; - assert(tag_type->id == TypeTableEntryIdEnumTag); - int_type = tag_type->data.enum_tag.int_type; + int_type = actual_type->data.enumeration.tag_int_type; } else { int_type = actual_type; } @@ -1946,19 +1940,11 @@ static LLVMValueRef ir_render_ptr_to_int(CodeGen *g, IrExecutable *executable, I static LLVMValueRef ir_render_int_to_enum(CodeGen *g, IrExecutable *executable, IrInstructionIntToEnum *instruction) { TypeTableEntry *wanted_type = instruction->base.value.type; assert(wanted_type->id == TypeTableEntryIdEnum); - TypeTableEntry *tag_type = wanted_type->data.enumeration.tag_type; - TypeTableEntry *wanted_int_type; - if (tag_type->id == TypeTableEntryIdEnumTag) { - wanted_int_type = tag_type->data.enum_tag.int_type; - } else if (tag_type->id == TypeTableEntryIdInt) { - wanted_int_type = tag_type; - } else { - zig_unreachable(); - } + TypeTableEntry *tag_int_type = wanted_type->data.enumeration.tag_int_type; LLVMValueRef target_val = ir_llvm_value(g, instruction->target); return gen_widen_or_shorten(g, ir_want_debug_safety(g, &instruction->base), - instruction->target->value.type, wanted_int_type, target_val); + instruction->target->value.type, tag_int_type, target_val); } static LLVMValueRef ir_render_int_to_err(CodeGen *g, IrExecutable *executable, IrInstructionIntToErr *instruction) { @@ -2378,27 +2364,6 @@ static LLVMValueRef ir_render_struct_field_ptr(CodeGen *g, IrExecutable *executa return LLVMBuildStructGEP(g->builder, struct_ptr, (unsigned)field->gen_index, ""); } -static LLVMValueRef ir_render_enum_field_ptr(CodeGen *g, IrExecutable *executable, - IrInstructionEnumFieldPtr *instruction) -{ - TypeTableEntry *enum_ptr_type = instruction->enum_ptr->value.type; - assert(enum_ptr_type->id == TypeTableEntryIdPointer); - TypeTableEntry *enum_type = enum_ptr_type->data.pointer.child_type; - assert(enum_type->id == TypeTableEntryIdEnum); - - TypeEnumField *field = instruction->field; - - if (!type_has_bits(field->type_entry)) - return nullptr; - - LLVMValueRef enum_ptr = ir_llvm_value(g, instruction->enum_ptr); - LLVMTypeRef field_type_ref = LLVMPointerType(field->type_entry->type_ref, 0); - LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, enum_ptr, enum_type->data.enumeration.gen_union_index, ""); - LLVMValueRef bitcasted_union_field_ptr = LLVMBuildBitCast(g->builder, union_field_ptr, field_type_ref, ""); - - return bitcasted_union_field_ptr; -} - static LLVMValueRef ir_render_union_field_ptr(CodeGen *g, IrExecutable *executable, IrInstructionUnionFieldPtr *instruction) { @@ -2427,7 +2392,7 @@ static LLVMValueRef ir_render_union_field_ptr(CodeGen *g, IrExecutable *executab LLVMValueRef expected_tag_value = bigint_to_llvm_const(union_type->data.unionation.tag_type->type_ref, - &field->value); + &field->enum_field->value); LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnionCheckOk"); LLVMBasicBlockRef bad_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnionCheckFail"); LLVMValueRef ok_val = LLVMBuildICmp(g->builder, LLVMIntEQ, tag_value, expected_tag_value, ""); @@ -2754,19 +2719,19 @@ static LLVMValueRef ir_render_err_name(CodeGen *g, IrExecutable *executable, IrI static LLVMValueRef ir_render_enum_tag_name(CodeGen *g, IrExecutable *executable, IrInstructionEnumTagName *instruction) { - TypeTableEntry *enum_tag_type = instruction->target->value.type; - assert(enum_tag_type->data.enum_tag.generate_name_table); + TypeTableEntry *enum_type = instruction->target->value.type; + assert(enum_type->id == TypeTableEntryIdEnum); + assert(enum_type->data.enumeration.generate_name_table); + TypeTableEntry *tag_int_type = enum_type->data.enumeration.tag_int_type; LLVMValueRef enum_tag_value = ir_llvm_value(g, instruction->target); if (ir_want_debug_safety(g, &instruction->base)) { - TypeTableEntry *enum_type = enum_tag_type->data.enum_tag.enum_type; size_t field_count = enum_type->data.enumeration.src_field_count; - // if the field_count can't fit in the bits of the enum_tag_type, then it can't possibly + // if the field_count can't fit in the bits of the enum_type, then it can't possibly // be the wrong value BigInt field_bi; bigint_init_unsigned(&field_bi, field_count); - TypeTableEntry *tag_int_type = enum_tag_type->data.enum_tag.int_type; if (bigint_fits_in_bits(&field_bi, tag_int_type->data.integral.bit_count, false)) { LLVMValueRef end_val = LLVMConstInt(LLVMTypeOf(enum_tag_value), field_count, false); add_bounds_check(g, enum_tag_value, LLVMIntEQ, nullptr, LLVMIntULT, end_val); @@ -2775,10 +2740,10 @@ static LLVMValueRef ir_render_enum_tag_name(CodeGen *g, IrExecutable *executable LLVMValueRef indices[] = { LLVMConstNull(g->builtin_types.entry_usize->type_ref), - gen_widen_or_shorten(g, false, enum_tag_type->data.enum_tag.int_type, + gen_widen_or_shorten(g, false, tag_int_type, g->builtin_types.entry_usize, enum_tag_value), }; - return LLVMBuildInBoundsGEP(g->builder, enum_tag_type->data.enum_tag.name_table, indices, 2, ""); + return LLVMBuildInBoundsGEP(g->builder, enum_type->data.enumeration.name_table, indices, 2, ""); } static LLVMValueRef ir_render_field_parent_ptr(CodeGen *g, IrExecutable *executable, @@ -3352,48 +3317,24 @@ static LLVMValueRef ir_render_err_wrap_payload(CodeGen *g, IrExecutable *executa return instruction->tmp_ptr; } -static LLVMValueRef ir_render_enum_tag(CodeGen *g, IrExecutable *executable, IrInstructionEnumTag *instruction) { - TypeTableEntry *enum_type = instruction->value->value.type; - TypeTableEntry *tag_type = enum_type->data.enumeration.tag_type; +static LLVMValueRef ir_render_union_tag(CodeGen *g, IrExecutable *executable, IrInstructionUnionTag *instruction) { + TypeTableEntry *union_type = instruction->value->value.type; + assert(union_type->data.unionation.gen_tag_index != SIZE_MAX); + + TypeTableEntry *tag_type = union_type->data.unionation.tag_type; if (!type_has_bits(tag_type)) return nullptr; - LLVMValueRef enum_val = ir_llvm_value(g, instruction->value); - if (enum_type->data.enumeration.gen_field_count == 0) - return enum_val; + LLVMValueRef union_val = ir_llvm_value(g, instruction->value); + if (union_type->data.unionation.gen_field_count == 0) + return union_val; - LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, enum_val, enum_type->data.enumeration.gen_tag_index, ""); + LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, union_val, + union_type->data.unionation.gen_tag_index, ""); TypeTableEntry *ptr_type = get_pointer_to_type(g, tag_type, false); return get_handle_value(g, tag_field_ptr, tag_type, ptr_type); } -static LLVMValueRef ir_render_init_enum(CodeGen *g, IrExecutable *executable, IrInstructionInitEnum *instruction) { - TypeTableEntry *enum_type = instruction->enum_type; - LLVMTypeRef tag_type_ref = enum_type->data.enumeration.tag_type->type_ref; - - LLVMValueRef tag_value = bigint_to_llvm_const(tag_type_ref, &instruction->field->value); - - if (enum_type->data.enumeration.gen_field_count == 0) - return tag_value; - - LLVMValueRef tmp_struct_ptr = instruction->tmp_ptr; - - LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, enum_type->data.enumeration.gen_tag_index, ""); - gen_store_untyped(g, tag_value, tag_field_ptr, 0, false); - - TypeTableEntry *union_val_type = instruction->field->type_entry; - if (type_has_bits(union_val_type)) { - LLVMValueRef new_union_val = ir_llvm_value(g, instruction->init_value); - LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, enum_type->data.enumeration.gen_union_index, ""); - LLVMValueRef bitcasted_union_field_ptr = LLVMBuildBitCast(g->builder, union_field_ptr, - LLVMPointerType(union_val_type->type_ref, 0), ""); - - gen_assign_raw(g, bitcasted_union_field_ptr, get_pointer_to_type(g, union_val_type, false), new_union_val); - } - - return tmp_struct_ptr; -} - static LLVMValueRef ir_render_struct_init(CodeGen *g, IrExecutable *executable, IrInstructionStructInit *instruction) { for (size_t i = 0; i < instruction->field_count; i += 1) { IrInstructionStructInitField *field = &instruction->fields[i]; @@ -3436,7 +3377,7 @@ static LLVMValueRef ir_render_union_init(CodeGen *g, IrExecutable *executable, I union_type->data.unionation.gen_tag_index, ""); LLVMValueRef tag_value = bigint_to_llvm_const(union_type->data.unionation.tag_type->type_ref, - &type_union_field->value); + &type_union_field->enum_field->value); gen_store_untyped(g, tag_value, tag_field_ptr, 0, false); uncasted_union_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, @@ -3573,8 +3514,6 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_call(g, executable, (IrInstructionCall *)instruction); case IrInstructionIdStructFieldPtr: return ir_render_struct_field_ptr(g, executable, (IrInstructionStructFieldPtr *)instruction); - case IrInstructionIdEnumFieldPtr: - return ir_render_enum_field_ptr(g, executable, (IrInstructionEnumFieldPtr *)instruction); case IrInstructionIdUnionFieldPtr: return ir_render_union_field_ptr(g, executable, (IrInstructionUnionFieldPtr *)instruction); case IrInstructionIdAsm: @@ -3629,10 +3568,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_err_wrap_code(g, executable, (IrInstructionErrWrapCode *)instruction); case IrInstructionIdErrWrapPayload: return ir_render_err_wrap_payload(g, executable, (IrInstructionErrWrapPayload *)instruction); - case IrInstructionIdEnumTag: - return ir_render_enum_tag(g, executable, (IrInstructionEnumTag *)instruction); - case IrInstructionIdInitEnum: - return ir_render_init_enum(g, executable, (IrInstructionInitEnum *)instruction); + case IrInstructionIdUnionTag: + return ir_render_union_tag(g, executable, (IrInstructionUnionTag *)instruction); case IrInstructionIdStructInit: return ir_render_struct_init(g, executable, (IrInstructionStructInit *)instruction); case IrInstructionIdUnionInit: @@ -3768,7 +3705,6 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con case TypeTableEntryIdNullLit: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdPureError: - case TypeTableEntryIdEnumTag: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: @@ -3780,7 +3716,6 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con return LLVMConstInt(big_int_type_ref, const_val->data.x_bool ? 1 : 0, false); case TypeTableEntryIdEnum: { - assert(type_entry->data.enumeration.gen_field_count == 0); assert(type_entry->data.enumeration.decl_node->data.container_decl.init_arg_expr != nullptr); LLVMValueRef int_val = gen_const_val(g, const_val); return LLVMConstZExt(int_val, big_int_type_ref); @@ -3852,7 +3787,6 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { switch (type_entry->id) { case TypeTableEntryIdInt: - case TypeTableEntryIdEnumTag: return bigint_to_llvm_const(type_entry->type_ref, &const_val->data.x_bigint); case TypeTableEntryIdPureError: assert(const_val->data.x_pure_err); @@ -4015,34 +3949,48 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { ConstExprValue *payload_value = const_val->data.x_union.payload; assert(payload_value != nullptr); - if (!type_has_bits(payload_value->type)) { - return LLVMGetUndef(union_type_ref); + if (type_entry->data.unionation.gen_field_count == 0) { + if (type_entry->data.unionation.gen_tag_index == SIZE_MAX) { + return nullptr; + } else { + return bigint_to_llvm_const(type_entry->data.unionation.tag_type->type_ref, + &const_val->data.x_union.tag); + } } - uint64_t field_type_bytes = LLVMStoreSizeOfType(g->target_data_ref, payload_value->type->type_ref); - uint64_t pad_bytes = type_entry->data.unionation.union_size_bytes - field_type_bytes; - LLVMValueRef correctly_typed_value = gen_const_val(g, payload_value); - bool make_unnamed_struct = is_llvm_value_unnamed_type(payload_value->type, correctly_typed_value) || - payload_value->type != type_entry->data.unionation.most_aligned_union_member; - LLVMValueRef union_value_ref; - { - if (pad_bytes == 0) { - union_value_ref = correctly_typed_value; - } else { - LLVMValueRef fields[2]; - fields[0] = correctly_typed_value; - fields[1] = LLVMGetUndef(LLVMArrayType(LLVMInt8Type(), (unsigned)pad_bytes)); - if (make_unnamed_struct || type_entry->data.unionation.gen_tag_index != SIZE_MAX) { - union_value_ref = LLVMConstStruct(fields, 2, false); + bool make_unnamed_struct; + if (!type_has_bits(payload_value->type)) { + if (type_entry->data.unionation.gen_tag_index == SIZE_MAX) + return LLVMGetUndef(type_entry->type_ref); + + union_value_ref = LLVMGetUndef(type_entry->data.unionation.most_aligned_union_member->type_ref); + make_unnamed_struct = false; + } else { + uint64_t field_type_bytes = LLVMStoreSizeOfType(g->target_data_ref, payload_value->type->type_ref); + uint64_t pad_bytes = type_entry->data.unionation.union_size_bytes - field_type_bytes; + LLVMValueRef correctly_typed_value = gen_const_val(g, payload_value); + make_unnamed_struct = is_llvm_value_unnamed_type(payload_value->type, correctly_typed_value) || + payload_value->type != type_entry->data.unionation.most_aligned_union_member; + + { + if (pad_bytes == 0) { + union_value_ref = correctly_typed_value; } else { - union_value_ref = LLVMConstNamedStruct(union_type_ref, fields, 2); + LLVMValueRef fields[2]; + fields[0] = correctly_typed_value; + fields[1] = LLVMGetUndef(LLVMArrayType(LLVMInt8Type(), (unsigned)pad_bytes)); + if (make_unnamed_struct || type_entry->data.unionation.gen_tag_index != SIZE_MAX) { + union_value_ref = LLVMConstStruct(fields, 2, false); + } else { + union_value_ref = LLVMConstNamedStruct(union_type_ref, fields, 2); + } } } - } - if (type_entry->data.unionation.gen_tag_index == SIZE_MAX) { - return union_value_ref; + if (type_entry->data.unionation.gen_tag_index == SIZE_MAX) { + return union_value_ref; + } } LLVMValueRef tag_value = bigint_to_llvm_const(type_entry->data.unionation.tag_type->type_ref, @@ -4059,55 +4007,9 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { } } - case TypeTableEntryIdEnum: - { - LLVMTypeRef tag_type_ref = type_entry->data.enumeration.tag_type->type_ref; - LLVMValueRef tag_value = bigint_to_llvm_const(tag_type_ref, &const_val->data.x_enum.tag); - if (type_entry->data.enumeration.gen_field_count == 0) { - return tag_value; - } else { - LLVMTypeRef union_type_ref = type_entry->data.enumeration.union_type_ref; - TypeEnumField *enum_field = find_enum_field_by_tag(type_entry, &const_val->data.x_enum.tag); - assert(bigint_cmp(&enum_field->value, &const_val->data.x_enum.tag) == CmpEQ); - LLVMValueRef union_value; - - bool make_unnamed_struct; - if (type_has_bits(enum_field->type_entry)) { - uint64_t field_type_bytes = LLVMStoreSizeOfType(g->target_data_ref, - enum_field->type_entry->type_ref); - uint64_t pad_bytes = type_entry->data.enumeration.union_size_bytes - field_type_bytes; - - ConstExprValue *payload_value = const_val->data.x_enum.payload; - LLVMValueRef correctly_typed_value = gen_const_val(g, payload_value); - - make_unnamed_struct = is_llvm_value_unnamed_type(payload_value->type, correctly_typed_value) || - payload_value->type != type_entry->data.enumeration.most_aligned_union_member; - - if (pad_bytes == 0) { - union_value = correctly_typed_value; - } else { - LLVMValueRef fields[] = { - correctly_typed_value, - LLVMGetUndef(LLVMArrayType(LLVMInt8Type(), (unsigned)pad_bytes)), - }; - union_value = LLVMConstStruct(fields, 2, false); - } - } else { - make_unnamed_struct = false; - union_value = LLVMGetUndef(union_type_ref); - } - LLVMValueRef fields[2]; - fields[type_entry->data.enumeration.gen_tag_index] = tag_value; - fields[type_entry->data.enumeration.gen_union_index] = union_value; - - if (make_unnamed_struct) { - return LLVMConstStruct(fields, 2, false); - } else { - return LLVMConstNamedStruct(type_entry->type_ref, fields, 2); - } - } - } + case TypeTableEntryIdEnum: + return bigint_to_llvm_const(type_entry->type_ref, &const_val->data.x_enum_tag); case TypeTableEntryIdFn: return fn_llvm_value(g, const_val->data.x_fn.fn_entry); case TypeTableEntryIdPointer: @@ -4318,9 +4220,8 @@ static void generate_enum_name_tables(CodeGen *g) { for (size_t enum_i = 0; enum_i < g->name_table_enums.length; enum_i += 1) { - TypeTableEntry *enum_tag_type = g->name_table_enums.at(enum_i); - assert(enum_tag_type->id == TypeTableEntryIdEnumTag); - TypeTableEntry *enum_type = enum_tag_type->data.enum_tag.enum_type; + TypeTableEntry *enum_type = g->name_table_enums.at(enum_i); + assert(enum_type->id == TypeTableEntryIdEnum); size_t field_count = enum_type->data.enumeration.src_field_count; LLVMValueRef *values = allocate(field_count); @@ -4351,7 +4252,7 @@ static void generate_enum_name_tables(CodeGen *g) { LLVMSetGlobalConstant(name_table, true); LLVMSetUnnamedAddr(name_table, true); LLVMSetAlignment(name_table, LLVMABIAlignmentOfType(g->target_data_ref, LLVMTypeOf(name_table_init))); - enum_tag_type->data.enum_tag.name_table = name_table; + enum_type->data.enumeration.name_table = name_table; } } @@ -4555,9 +4456,6 @@ static void do_code_gen(CodeGen *g) { } else if (instruction->id == IrInstructionIdErrWrapCode) { IrInstructionErrWrapCode *err_wrap_code_instruction = (IrInstructionErrWrapCode *)instruction; slot = &err_wrap_code_instruction->tmp_ptr; - } else if (instruction->id == IrInstructionIdInitEnum) { - IrInstructionInitEnum *init_enum_instruction = (IrInstructionInitEnum *)instruction; - slot = &init_enum_instruction->tmp_ptr; } else { zig_unreachable(); } @@ -5054,7 +4952,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdTruncate, "truncate", 2); create_builtin_fn(g, BuiltinFnIdCompileErr, "compileError", 1); create_builtin_fn(g, BuiltinFnIdCompileLog, "compileLog", SIZE_MAX); - create_builtin_fn(g, BuiltinFnIdIntType, "IntType", 2); + create_builtin_fn(g, BuiltinFnIdIntType, "IntType", 2); // TODO rename to Int create_builtin_fn(g, BuiltinFnIdSetDebugSafety, "setDebugSafety", 2); create_builtin_fn(g, BuiltinFnIdSetFloatMode, "setFloatMode", 2); create_builtin_fn(g, BuiltinFnIdSetGlobalSection, "setGlobalSection", 2); @@ -5064,7 +4962,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdBitCast, "bitCast", 2); create_builtin_fn(g, BuiltinFnIdIntToPtr, "intToPtr", 2); create_builtin_fn(g, BuiltinFnIdPtrToInt, "ptrToInt", 1); - create_builtin_fn(g, BuiltinFnIdEnumTagName, "enumTagName", 1); // TODO rename to memberName + create_builtin_fn(g, BuiltinFnIdTagName, "tagName", 1); create_builtin_fn(g, BuiltinFnIdEnumTagType, "EnumTagType", 1); create_builtin_fn(g, BuiltinFnIdFieldParentPtr, "fieldParentPtr", 3); create_builtin_fn(g, BuiltinFnIdOffsetOf, "offsetOf", 2); @@ -5681,7 +5579,6 @@ static void get_c_type(CodeGen *g, TypeTableEntry *type_entry, Buf *out_buf) { case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: - case TypeTableEntryIdEnumTag: zig_panic("TODO implement get_c_type for more types"); case TypeTableEntryIdInvalid: case TypeTableEntryIdMetaType: diff --git a/src/ir.cpp b/src/ir.cpp index e51f52adae..5da59fedb7 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -223,10 +223,6 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionStructFieldPtr * return IrInstructionIdStructFieldPtr; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionEnumFieldPtr *) { - return IrInstructionIdEnumFieldPtr; -} - static constexpr IrInstructionId ir_instruction_id(IrInstructionUnionFieldPtr *) { return IrInstructionIdUnionFieldPtr; } @@ -319,8 +315,8 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionCtz *) { return IrInstructionIdCtz; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionEnumTag *) { - return IrInstructionIdEnumTag; +static constexpr IrInstructionId ir_instruction_id(IrInstructionUnionTag *) { + return IrInstructionIdUnionTag; } static constexpr IrInstructionId ir_instruction_id(IrInstructionImport *) { @@ -479,10 +475,6 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionTestComptime *) return IrInstructionIdTestComptime; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionInitEnum *) { - return IrInstructionIdInitEnum; -} - static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrCast *) { return IrInstructionIdPtrCast; } @@ -913,27 +905,6 @@ static IrInstruction *ir_build_struct_field_ptr_from(IrBuilder *irb, IrInstructi return new_instruction; } -static IrInstruction *ir_build_enum_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *enum_ptr, TypeEnumField *field) -{ - IrInstructionEnumFieldPtr *instruction = ir_build_instruction(irb, scope, source_node); - instruction->enum_ptr = enum_ptr; - instruction->field = field; - - ir_ref_instruction(enum_ptr, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstruction *ir_build_enum_field_ptr_from(IrBuilder *irb, IrInstruction *old_instruction, - IrInstruction *enum_ptr, TypeEnumField *type_enum_field) -{ - IrInstruction *new_instruction = ir_build_enum_field_ptr(irb, old_instruction->scope, - old_instruction->source_node, enum_ptr, type_enum_field); - ir_link_new_instruction(new_instruction, old_instruction); - return new_instruction; -} - static IrInstruction *ir_build_union_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *union_ptr, TypeUnionField *field) { @@ -1528,8 +1499,8 @@ static IrInstruction *ir_build_switch_var(IrBuilder *irb, Scope *scope, AstNode return &instruction->base; } -static IrInstruction *ir_build_enum_tag(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { - IrInstructionEnumTag *instruction = ir_build_instruction(irb, scope, source_node); +static IrInstruction *ir_build_union_tag(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { + IrInstructionUnionTag *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; ir_ref_instruction(value, irb->current_basic_block); @@ -1537,13 +1508,6 @@ static IrInstruction *ir_build_enum_tag(IrBuilder *irb, Scope *scope, AstNode *s return &instruction->base; } -static IrInstruction *ir_build_enum_tag_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *value) { - IrInstruction *new_instruction = ir_build_enum_tag(irb, old_instruction->scope, - old_instruction->source_node, value); - ir_link_new_instruction(new_instruction, old_instruction); - return new_instruction; -} - static IrInstruction *ir_build_import(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *name) { IrInstructionImport *instruction = ir_build_instruction(irb, scope, source_node); instruction->name = name; @@ -2033,28 +1997,6 @@ static IrInstruction *ir_build_test_comptime(IrBuilder *irb, Scope *scope, AstNo return &instruction->base; } -static IrInstruction *ir_build_init_enum(IrBuilder *irb, Scope *scope, AstNode *source_node, - TypeTableEntry *enum_type, TypeEnumField *field, IrInstruction *init_value) -{ - IrInstructionInitEnum *instruction = ir_build_instruction(irb, scope, source_node); - instruction->enum_type = enum_type; - instruction->field = field; - instruction->init_value = init_value; - - ir_ref_instruction(init_value, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstruction *ir_build_init_enum_from(IrBuilder *irb, IrInstruction *old_instruction, - TypeTableEntry *enum_type, TypeEnumField *field, IrInstruction *init_value) -{ - IrInstruction *new_instruction = ir_build_init_enum(irb, old_instruction->scope, old_instruction->source_node, - enum_type, field, init_value); - ir_link_new_instruction(new_instruction, old_instruction); - return new_instruction; -} - static IrInstruction *ir_build_ptr_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *ptr) { @@ -2481,13 +2423,6 @@ static IrInstruction *ir_instruction_structfieldptr_get_dep(IrInstructionStructF } } -static IrInstruction *ir_instruction_enumfieldptr_get_dep(IrInstructionEnumFieldPtr *instruction, size_t index) { - switch (index) { - case 0: return instruction->enum_ptr; - default: return nullptr; - } -} - static IrInstruction *ir_instruction_unionfieldptr_get_dep(IrInstructionUnionFieldPtr *instruction, size_t index) { switch (index) { case 0: return instruction->union_ptr; @@ -2657,7 +2592,7 @@ static IrInstruction *ir_instruction_maybewrap_get_dep(IrInstructionMaybeWrap *i } } -static IrInstruction *ir_instruction_enumtag_get_dep(IrInstructionEnumTag *instruction, size_t index) { +static IrInstruction *ir_instruction_uniontag_get_dep(IrInstructionUnionTag *instruction, size_t index) { switch (index) { case 0: return instruction->value; default: return nullptr; @@ -2943,13 +2878,6 @@ static IrInstruction *ir_instruction_testcomptime_get_dep(IrInstructionTestCompt } } -static IrInstruction *ir_instruction_initenum_get_dep(IrInstructionInitEnum *instruction, size_t index) { - switch (index) { - case 0: return instruction->init_value; - default: return nullptr; - } -} - static IrInstruction *ir_instruction_ptrcast_get_dep(IrInstructionPtrCast *instruction, size_t index) { @@ -3184,8 +3112,6 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t return ir_instruction_fieldptr_get_dep((IrInstructionFieldPtr *) instruction, index); case IrInstructionIdStructFieldPtr: return ir_instruction_structfieldptr_get_dep((IrInstructionStructFieldPtr *) instruction, index); - case IrInstructionIdEnumFieldPtr: - return ir_instruction_enumfieldptr_get_dep((IrInstructionEnumFieldPtr *) instruction, index); case IrInstructionIdUnionFieldPtr: return ir_instruction_unionfieldptr_get_dep((IrInstructionUnionFieldPtr *) instruction, index); case IrInstructionIdElemPtr: @@ -3234,8 +3160,8 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t return ir_instruction_unwrapmaybe_get_dep((IrInstructionUnwrapMaybe *) instruction, index); case IrInstructionIdMaybeWrap: return ir_instruction_maybewrap_get_dep((IrInstructionMaybeWrap *) instruction, index); - case IrInstructionIdEnumTag: - return ir_instruction_enumtag_get_dep((IrInstructionEnumTag *) instruction, index); + case IrInstructionIdUnionTag: + return ir_instruction_uniontag_get_dep((IrInstructionUnionTag *) instruction, index); case IrInstructionIdClz: return ir_instruction_clz_get_dep((IrInstructionClz *) instruction, index); case IrInstructionIdCtz: @@ -3312,8 +3238,6 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t return ir_instruction_fnproto_get_dep((IrInstructionFnProto *) instruction, index); case IrInstructionIdTestComptime: return ir_instruction_testcomptime_get_dep((IrInstructionTestComptime *) instruction, index); - case IrInstructionIdInitEnum: - return ir_instruction_initenum_get_dep((IrInstructionInitEnum *) instruction, index); case IrInstructionIdPtrCast: return ir_instruction_ptrcast_get_dep((IrInstructionPtrCast *) instruction, index); case IrInstructionIdBitCast: @@ -4695,14 +4619,14 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return ir_build_ptr_to_int(irb, scope, node, arg0_value); } - case BuiltinFnIdEnumTagName: + case BuiltinFnIdTagName: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - IrInstruction *actual_tag = ir_build_enum_tag(irb, scope, node, arg0_value); + IrInstruction *actual_tag = ir_build_union_tag(irb, scope, node, arg0_value); return ir_build_enum_tag_name(irb, scope, node, actual_tag); } case BuiltinFnIdEnumTagType: @@ -8381,13 +8305,28 @@ static IrInstruction *ir_analyze_enum_to_int(IrAnalyze *ira, IrInstruction *sour { assert(wanted_type->id == TypeTableEntryIdInt); + TypeTableEntry *actual_type = target->value.type; + ensure_complete_type(ira->codegen, actual_type); + if (type_is_invalid(actual_type)) + return ira->codegen->invalid_instruction; + + if (wanted_type != actual_type->data.enumeration.tag_int_type) { + ir_add_error(ira, source_instr, + buf_sprintf("enum to integer cast to '%s' instead of its tag type, '%s'", + buf_ptr(&wanted_type->name), + buf_ptr(&actual_type->data.enumeration.tag_int_type->name))); + return ira->codegen->invalid_instruction; + } + + assert(actual_type->id == TypeTableEntryIdEnum); + if (instr_is_comptime(target)) { ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_instruction; IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type); - init_const_bigint(&result->value, wanted_type, &val->data.x_enum.tag); + init_const_bigint(&result->value, wanted_type, &val->data.x_enum_tag); return result; } @@ -8397,6 +8336,31 @@ static IrInstruction *ir_analyze_enum_to_int(IrAnalyze *ira, IrInstruction *sour return result; } +static IrInstruction *ir_analyze_union_to_tag(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *target, TypeTableEntry *wanted_type) +{ + assert(target->value.type->id == TypeTableEntryIdUnion); + assert(wanted_type->id == TypeTableEntryIdEnum); + assert(wanted_type == target->value.type->data.unionation.tag_type); + + if (instr_is_comptime(target)) { + ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); + if (!val) + return ira->codegen->invalid_instruction; + IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, + source_instr->source_node, wanted_type); + result->value.special = ConstValSpecialStatic; + result->value.type = wanted_type; + bigint_init_bigint(&result->value.data.x_enum_tag, &val->data.x_union.tag); + return result; + } + + IrInstruction *result = ir_build_union_tag(&ira->new_irb, source_instr->scope, + source_instr->source_node, target); + result->value.type = wanted_type; + return result; +} + static IrInstruction *ir_analyze_undefined_to_anything(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target, TypeTableEntry *wanted_type) { @@ -8452,6 +8416,22 @@ static IrInstruction *ir_analyze_int_to_enum(IrAnalyze *ira, IrInstruction *sour { assert(wanted_type->id == TypeTableEntryIdEnum); + TypeTableEntry *actual_type = target->value.type; + + ensure_complete_type(ira->codegen, wanted_type); + if (type_is_invalid(wanted_type)) + return ira->codegen->invalid_instruction; + + if (actual_type != wanted_type->data.enumeration.tag_int_type) { + ir_add_error(ira, source_instr, + buf_sprintf("integer to enum cast from '%s' instead of its tag type, '%s'", + buf_ptr(&actual_type->name), + buf_ptr(&wanted_type->data.enumeration.tag_int_type->name))); + return ira->codegen->invalid_instruction; + } + + assert(actual_type->id == TypeTableEntryIdInt); + if (instr_is_comptime(target)) { ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) @@ -8469,7 +8449,7 @@ static IrInstruction *ir_analyze_int_to_enum(IrAnalyze *ira, IrInstruction *sour IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type); - bigint_init_bigint(&result->value.data.x_enum.tag, &val->data.x_bigint); + bigint_init_bigint(&result->value.data.x_enum_tag, &val->data.x_bigint); return result; } @@ -8907,39 +8887,24 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } // explicit cast from integer to enum type with no payload - if (actual_type->id == TypeTableEntryIdInt && - wanted_type->id == TypeTableEntryIdEnum && - wanted_type->data.enumeration.gen_field_count == 0) - { - ensure_complete_type(ira->codegen, wanted_type); - if (type_is_invalid(wanted_type)) - return ira->codegen->invalid_instruction; - if (actual_type == wanted_type->data.enumeration.tag_type->data.enum_tag.int_type) { - return ir_analyze_int_to_enum(ira, source_instr, value, wanted_type); - } - ir_add_error(ira, source_instr, - buf_sprintf("integer to enum cast from '%s' instead of its tag type, '%s'", - buf_ptr(&actual_type->name), - buf_ptr(&wanted_type->data.enumeration.tag_type->data.enum_tag.int_type->name))); - return ira->codegen->invalid_instruction; + if (actual_type->id == TypeTableEntryIdInt && wanted_type->id == TypeTableEntryIdEnum) { + return ir_analyze_int_to_enum(ira, source_instr, value, wanted_type); } // explicit cast from enum type with no payload to integer - if (wanted_type->id == TypeTableEntryIdInt && - actual_type->id == TypeTableEntryIdEnum && - actual_type->data.enumeration.gen_field_count == 0) - { - ensure_complete_type(ira->codegen, actual_type); + if (wanted_type->id == TypeTableEntryIdInt && actual_type->id == TypeTableEntryIdEnum) { + return ir_analyze_enum_to_int(ira, source_instr, value, wanted_type); + } + + // explicit cast from union to the enum type of the union + if (actual_type->id == TypeTableEntryIdUnion && wanted_type->id == TypeTableEntryIdEnum) { + type_ensure_zero_bits_known(ira->codegen, actual_type); if (type_is_invalid(actual_type)) return ira->codegen->invalid_instruction; - if (wanted_type == actual_type->data.enumeration.tag_type->data.enum_tag.int_type) { - return ir_analyze_enum_to_int(ira, source_instr, value, wanted_type); + + if (actual_type->data.unionation.tag_type == wanted_type) { + return ir_analyze_union_to_tag(ira, source_instr, value, wanted_type); } - ir_add_error(ira, source_instr, - buf_sprintf("enum to integer cast to '%s' instead of its tag type, '%s'", - buf_ptr(&wanted_type->name), - buf_ptr(&actual_type->data.enumeration.tag_type->data.enum_tag.int_type->name))); - return ira->codegen->invalid_instruction; } // explicit cast from undefined to anything @@ -9148,7 +9113,7 @@ static bool ir_resolve_atomic_order(IrAnalyze *ira, IrInstruction *value, Atomic if (!const_val) return false; - *out = (AtomicOrder)bigint_as_unsigned(&const_val->data.x_enum.tag); + *out = (AtomicOrder)bigint_as_unsigned(&const_val->data.x_enum_tag); return true; } @@ -9168,7 +9133,7 @@ static bool ir_resolve_global_linkage(IrAnalyze *ira, IrInstruction *value, Glob if (!const_val) return false; - *out = (GlobalLinkageId)bigint_as_unsigned(&const_val->data.x_enum.tag); + *out = (GlobalLinkageId)bigint_as_unsigned(&const_val->data.x_enum_tag); return true; } @@ -9188,7 +9153,7 @@ static bool ir_resolve_float_mode(IrAnalyze *ira, IrInstruction *value, FloatMod if (!const_val) return false; - *out = (FloatMode)bigint_as_unsigned(&const_val->data.x_enum.tag); + *out = (FloatMode)bigint_as_unsigned(&const_val->data.x_enum_tag); return true; } @@ -9400,7 +9365,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp break; case TypeTableEntryIdEnum: - if (!is_equality_cmp || resolved_type->data.enumeration.gen_field_count != 0) { + if (!is_equality_cmp) { ir_add_error_node(ira, source_node, buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name))); return ira->codegen->builtin_types.entry_invalid; @@ -9419,9 +9384,6 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name))); return ira->codegen->builtin_types.entry_invalid; - case TypeTableEntryIdEnumTag: - zig_panic("TODO implement comparison for enum tag type"); - case TypeTableEntryIdVar: zig_unreachable(); } @@ -10170,7 +10132,6 @@ static VarClassRequired get_var_class_required(TypeTableEntry *type_entry) { case TypeTableEntryIdVoid: case TypeTableEntryIdPureError: case TypeTableEntryIdFn: - case TypeTableEntryIdEnumTag: return VarClassRequiredAny; case TypeTableEntryIdNumLitFloat: case TypeTableEntryIdNumLitInt: @@ -10913,7 +10874,6 @@ static TypeTableEntry *ir_analyze_unary_prefix_op_err(IrAnalyze *ira, IrInstruct case TypeTableEntryIdUnion: case TypeTableEntryIdFn: case TypeTableEntryIdBoundFn: - case TypeTableEntryIdEnumTag: { ConstExprValue *out_val = ir_build_const_from(ira, &un_op_instruction->base); TypeTableEntry *result_type = get_error_type(ira->codegen, meta_type); @@ -11001,7 +10961,6 @@ static TypeTableEntry *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: - case TypeTableEntryIdEnumTag: case TypeTableEntryIdArgTuple: { ConstExprValue *out_val = ir_build_const_from(ira, &un_op_instruction->base); @@ -11662,15 +11621,8 @@ static TypeTableEntry *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field field_ptr_instruction, container_ptr, container_type); } } else if (bare_type->id == TypeTableEntryIdEnum) { - TypeEnumField *field = find_enum_type_field(bare_type, field_name); - if (field) { - ir_build_enum_field_ptr_from(&ira->new_irb, &field_ptr_instruction->base, container_ptr, field); - return get_pointer_to_type_extra(ira->codegen, field->type_entry, is_const, is_volatile, - get_abi_alignment(ira->codegen, field->type_entry), 0, 0); - } else { - return ir_analyze_container_member_access_inner(ira, bare_type, field_name, - field_ptr_instruction, container_ptr, container_type); - } + return ir_analyze_container_member_access_inner(ira, bare_type, field_name, + field_ptr_instruction, container_ptr, container_type); } else if (bare_type->id == TypeTableEntryIdUnion) { TypeUnionField *field = find_union_type_field(bare_type, field_name); if (field) { @@ -11841,20 +11793,27 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru TypeEnumField *field = find_enum_type_field(child_type, field_name); if (field) { - if (field->type_entry->id == TypeTableEntryIdVoid) { - bool ptr_is_const = true; - bool ptr_is_volatile = false; - return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, - create_const_enum_tag(child_type, &field->value), child_type, - ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); - } else { - bool ptr_is_const = true; - bool ptr_is_volatile = false; - return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, - create_const_bigint(child_type->data.enumeration.tag_type, &field->value), - child_type->data.enumeration.tag_type, + bool ptr_is_const = true; + bool ptr_is_volatile = false; + return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, + create_const_enum(child_type, &field->value), child_type, + ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); + } + } else if (child_type->id == TypeTableEntryIdUnion && + (child_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr || + child_type->data.unionation.decl_node->data.container_decl.auto_enum)) + { + ensure_complete_type(ira->codegen, child_type); + if (type_is_invalid(child_type)) + return ira->codegen->builtin_types.entry_invalid; + TypeUnionField *field = find_union_type_field(child_type, field_name); + if (field) { + TypeTableEntry *enum_type = child_type->data.unionation.tag_type; + bool ptr_is_const = true; + bool ptr_is_volatile = false; + return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, + create_const_enum(enum_type, &field->enum_field->value), enum_type, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); - } } } ScopeDecls *container_scope = get_container_scope(child_type); @@ -12163,7 +12122,6 @@ static TypeTableEntry *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructi case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: - case TypeTableEntryIdEnumTag: case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: { @@ -12511,7 +12469,6 @@ static TypeTableEntry *ir_analyze_instruction_slice_type(IrAnalyze *ira, case TypeTableEntryIdFn: case TypeTableEntryIdNamespace: case TypeTableEntryIdBoundFn: - case TypeTableEntryIdEnumTag: { type_ensure_zero_bits_known(ira->codegen, child_type); TypeTableEntry *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, child_type, @@ -12620,7 +12577,6 @@ static TypeTableEntry *ir_analyze_instruction_array_type(IrAnalyze *ira, case TypeTableEntryIdFn: case TypeTableEntryIdNamespace: case TypeTableEntryIdBoundFn: - case TypeTableEntryIdEnumTag: { TypeTableEntry *result_type = get_array_type(ira->codegen, child_type, size); ConstExprValue *out_val = ir_build_const_from(ira, &array_type_instruction->base); @@ -12671,7 +12627,6 @@ static TypeTableEntry *ir_analyze_instruction_size_of(IrAnalyze *ira, case TypeTableEntryIdPureError: case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: - case TypeTableEntryIdEnumTag: case TypeTableEntryIdFn: { uint64_t size_in_bytes = type_size(ira->codegen, type_entry); @@ -12824,17 +12779,22 @@ static TypeTableEntry *ir_analyze_instruction_clz(IrAnalyze *ira, IrInstructionC } } -static IrInstruction *ir_analyze_enum_tag(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value) { +static IrInstruction *ir_analyze_union_tag(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value) { if (type_is_invalid(value->value.type)) return ira->codegen->invalid_instruction; - if (value->value.type->id != TypeTableEntryIdEnum) { + if (value->value.type->id == TypeTableEntryIdEnum) { + return value; + } + + if (value->value.type->id != TypeTableEntryIdUnion) { ir_add_error(ira, source_instr, - buf_sprintf("expected enum type, found '%s'", buf_ptr(&value->value.type->name))); + buf_sprintf("expected enum or union type, found '%s'", buf_ptr(&value->value.type->name))); return ira->codegen->invalid_instruction; } - TypeTableEntry *tag_type = value->value.type->data.enumeration.tag_type; + TypeTableEntry *tag_type = value->value.type->data.unionation.tag_type; + assert(tag_type->id == TypeTableEntryIdEnum); if (instr_is_comptime(value)) { ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); @@ -12845,11 +12805,11 @@ static IrInstruction *ir_analyze_enum_tag(IrAnalyze *ira, IrInstruction *source_ source_instr->scope, source_instr->source_node); const_instruction->base.value.type = tag_type; const_instruction->base.value.special = ConstValSpecialStatic; - bigint_init_bigint(&const_instruction->base.value.data.x_bigint, &val->data.x_enum.tag); + bigint_init_bigint(&const_instruction->base.value.data.x_enum_tag, &val->data.x_union.tag); return &const_instruction->base; } - IrInstruction *result = ir_build_enum_tag(&ira->new_irb, source_instr->scope, source_instr->source_node, value); + IrInstruction *result = ir_build_union_tag(&ira->new_irb, source_instr->scope, source_instr->source_node, value); result->value.type = tag_type; return result; } @@ -12880,7 +12840,7 @@ static TypeTableEntry *ir_analyze_instruction_switch_br(IrAnalyze *ira, return ir_unreach_error(ira); if (case_value->value.type->id == TypeTableEntryIdEnum) { - case_value = ir_analyze_enum_tag(ira, &switch_br_instruction->base, case_value); + case_value = ir_analyze_union_tag(ira, &switch_br_instruction->base, case_value); if (type_is_invalid(case_value->value.type)) return ir_unreach_error(ira); } @@ -12927,7 +12887,7 @@ static TypeTableEntry *ir_analyze_instruction_switch_br(IrAnalyze *ira, continue; if (new_value->value.type->id == TypeTableEntryIdEnum) { - new_value = ir_analyze_enum_tag(ira, &switch_br_instruction->base, new_value); + new_value = ir_analyze_union_tag(ira, &switch_br_instruction->base, new_value); if (type_is_invalid(new_value->value.type)) continue; } @@ -13009,34 +12969,54 @@ 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 TypeTableEntryIdEnum: - { - TypeTableEntry *tag_type = target_type->data.enumeration.tag_type; - assert(tag_type != nullptr); - if (pointee_val) { - ConstExprValue *out_val = ir_build_const_from(ira, &switch_target_instruction->base); - bigint_init_bigint(&out_val->data.x_bigint, &pointee_val->data.x_enum.tag); - return tag_type; - } - - IrInstruction *enum_value = ir_build_load_ptr(&ira->new_irb, switch_target_instruction->base.scope, - switch_target_instruction->base.source_node, target_value_ptr); - enum_value->value.type = target_type; - ir_build_enum_tag_from(&ira->new_irb, &switch_target_instruction->base, enum_value); + case TypeTableEntryIdUnion: { + if (target_type->data.unionation.gen_tag_index == SIZE_MAX) { + 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, + buf_sprintf("union declared here")); + return ira->codegen->builtin_types.entry_invalid; + } + TypeTableEntry *tag_type = target_type->data.unionation.tag_type; + assert(tag_type != nullptr); + 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_union.tag); return tag_type; } + + IrInstruction *union_value = ir_build_load_ptr(&ira->new_irb, switch_target_instruction->base.scope, + switch_target_instruction->base.source_node, target_value_ptr); + union_value->value.type = target_type; + + IrInstruction *union_tag_inst = ir_build_union_tag(&ira->new_irb, switch_target_instruction->base.scope, + switch_target_instruction->base.source_node, union_value); + union_tag_inst->value.type = tag_type; + ir_link_new_instruction(union_tag_inst, &switch_target_instruction->base); + return tag_type; + } + case TypeTableEntryIdEnum: { + 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); + return target_type; + } + + IrInstruction *enum_value = ir_build_load_ptr(&ira->new_irb, switch_target_instruction->base.scope, + switch_target_instruction->base.source_node, target_value_ptr); + enum_value->value.type = target_type; + ir_link_new_instruction(enum_value, &switch_target_instruction->base); + return target_type; + } case TypeTableEntryIdErrorUnion: - // see https://github.com/andrewrk/zig/issues/83 + // see https://github.com/andrewrk/zig/issues/632 zig_panic("TODO switch on error union"); - case TypeTableEntryIdEnumTag: - zig_panic("TODO switch on enum tag type"); case TypeTableEntryIdUnreachable: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdMaybe: - case TypeTableEntryIdUnion: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: case TypeTableEntryIdArgTuple: @@ -13059,19 +13039,13 @@ static TypeTableEntry *ir_analyze_instruction_switch_var(IrAnalyze *ira, IrInstr assert(target_value_ptr->value.type->id == TypeTableEntryIdPointer); TypeTableEntry *target_type = target_value_ptr->value.type->data.pointer.child_type; - if (target_type->id == TypeTableEntryIdEnum) { + if (target_type->id == TypeTableEntryIdUnion) { ConstExprValue *prong_val = ir_resolve_const(ira, prong_value, UndefBad); if (!prong_val) return ira->codegen->builtin_types.entry_invalid; - TypeEnumField *field; - if (prong_value->value.type->id == TypeTableEntryIdEnumTag) { - field = find_enum_field_by_tag(target_type, &prong_val->data.x_bigint); - } else if (prong_value->value.type->id == TypeTableEntryIdEnum) { - field = find_enum_field_by_tag(target_type, &prong_val->data.x_enum.tag); - } else { - zig_unreachable(); - } + assert(prong_value->value.type->id == TypeTableEntryIdEnum); + TypeUnionField *field = find_union_field_by_tag(target_type, &prong_val->data.x_enum_tag); if (instr_is_comptime(target_value_ptr)) { ConstExprValue *target_val_ptr = ir_resolve_const(ira, target_value_ptr, UndefBad); @@ -13082,11 +13056,11 @@ static TypeTableEntry *ir_analyze_instruction_switch_var(IrAnalyze *ira, IrInstr ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_ptr.special = ConstPtrSpecialRef; out_val->data.x_ptr.mut = target_val_ptr->data.x_ptr.mut; - out_val->data.x_ptr.data.ref.pointee = pointee_val->data.x_enum.payload; + out_val->data.x_ptr.data.ref.pointee = pointee_val->data.x_union.payload; return get_pointer_to_type(ira->codegen, field->type_entry, target_val_ptr->type->data.pointer.is_const); } - ir_build_enum_field_ptr_from(&ira->new_irb, &instruction->base, target_value_ptr, field); + ir_build_union_field_ptr_from(&ira->new_irb, &instruction->base, target_value_ptr, field); return get_pointer_to_type(ira->codegen, field->type_entry, target_value_ptr->value.type->data.pointer.is_const); } else { @@ -13096,10 +13070,10 @@ static TypeTableEntry *ir_analyze_instruction_switch_var(IrAnalyze *ira, IrInstr } } -static TypeTableEntry *ir_analyze_instruction_enum_tag(IrAnalyze *ira, IrInstructionEnumTag *enum_tag_instruction) { - IrInstruction *value = enum_tag_instruction->value->other; - IrInstruction *new_instruction = ir_analyze_enum_tag(ira, &enum_tag_instruction->base, value); - ir_link_new_instruction(new_instruction, &enum_tag_instruction->base); +static TypeTableEntry *ir_analyze_instruction_union_tag(IrAnalyze *ira, IrInstructionUnionTag *instruction) { + IrInstruction *value = instruction->value->other; + IrInstruction *new_instruction = ir_analyze_union_tag(ira, &instruction->base, value); + ir_link_new_instruction(new_instruction, &instruction->base); return new_instruction->value.type; } @@ -13255,7 +13229,7 @@ static TypeTableEntry *ir_analyze_container_init_fields_union(IrAnalyze *ira, Ir ConstExprValue *out_val = ir_build_const_from(ira, instruction); out_val->data.x_union.payload = field_val; - out_val->data.x_union.tag = type_field->value; + out_val->data.x_union.tag = type_field->enum_field->value; ConstParent *parent = get_const_val_parent(ira->codegen, field_val); if (parent != nullptr) { @@ -13502,46 +13476,9 @@ static TypeTableEntry *ir_analyze_instruction_container_init_list(IrAnalyze *ira buf_ptr(&container_type->name))); return ira->codegen->builtin_types.entry_invalid; } - } else if (container_type_value->value.type->id == TypeTableEntryIdEnumTag) { - if (elem_count != 1) { - ir_add_error(ira, &instruction->base, buf_sprintf("enum initialization requires exactly one element")); - return ira->codegen->builtin_types.entry_invalid; - } - ConstExprValue *tag_value = ir_resolve_const(ira, container_type_value, UndefBad); - if (!tag_value) - return ira->codegen->builtin_types.entry_invalid; - - TypeTableEntry *enum_type = container_type_value->value.type->data.enum_tag.enum_type; - - TypeEnumField *field = find_enum_field_by_tag(enum_type, &tag_value->data.x_bigint); - assert(field != nullptr); - TypeTableEntry *this_field_type = field->type_entry; - - IrInstruction *init_value = instruction->items[0]->other; - if (type_is_invalid(init_value->value.type)) - return ira->codegen->builtin_types.entry_invalid; - - IrInstruction *casted_init_value = ir_implicit_cast(ira, init_value, this_field_type); - if (casted_init_value == ira->codegen->invalid_instruction) - return ira->codegen->builtin_types.entry_invalid; - - if (instr_is_comptime(casted_init_value)) { - ConstExprValue *init_val = ir_resolve_const(ira, casted_init_value, UndefOk); - if (!init_val) - return ira->codegen->builtin_types.entry_invalid; - ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); - bigint_init_bigint(&out_val->data.x_enum.tag, &tag_value->data.x_bigint); - out_val->data.x_enum.payload = init_val; - return enum_type; - } - - IrInstruction *new_instruction = ir_build_init_enum_from(&ira->new_irb, &instruction->base, - enum_type, field, casted_init_value); - ir_add_alloca(ira, new_instruction, enum_type); - return enum_type; } else { ir_add_error(ira, container_type_value, - buf_sprintf("expected type, found '%s'", buf_ptr(&container_type_value->value.type->name))); + buf_sprintf("expected type, found '%s' value", buf_ptr(&container_type_value->value.type->name))); return ira->codegen->builtin_types.entry_invalid; } } @@ -13584,8 +13521,8 @@ static TypeTableEntry *ir_analyze_min_max(IrAnalyze *ira, IrInstruction *source_ eval_min_max_value(ira->codegen, target_type, out_val, is_max); return target_type; } - case TypeTableEntryIdEnumTag: - zig_panic("TODO min/max value for enum tag type"); + case TypeTableEntryIdEnum: + zig_panic("TODO min/max value for enum type"); case TypeTableEntryIdVar: case TypeTableEntryIdMetaType: case TypeTableEntryIdUnreachable: @@ -13599,7 +13536,6 @@ static TypeTableEntry *ir_analyze_min_max(IrAnalyze *ira, IrInstruction *source_ case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdPureError: - case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: case TypeTableEntryIdNamespace: @@ -13707,20 +13643,18 @@ static TypeTableEntry *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrIn if (type_is_invalid(target->value.type)) return ira->codegen->builtin_types.entry_invalid; - assert(target->value.type->id == TypeTableEntryIdEnumTag); + assert(target->value.type->id == TypeTableEntryIdEnum); if (instr_is_comptime(target)) { - TypeTableEntry *enum_type = target->value.type->data.enum_tag.enum_type; - uint64_t tag_value = bigint_as_unsigned(&target->value.data.x_bigint); - TypeEnumField *field = &enum_type->data.enumeration.fields[tag_value]; + TypeEnumField *field = find_enum_field_by_tag(target->value.type, &target->value.data.x_bigint); ConstExprValue *array_val = create_const_str_lit(ira->codegen, field->name); ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); init_const_slice(ira->codegen, out_val, array_val, 0, buf_len(field->name), true); return out_val->type; } - if (!target->value.type->data.enum_tag.generate_name_table) { - target->value.type->data.enum_tag.generate_name_table = true; + if (!target->value.type->data.enumeration.generate_name_table) { + target->value.type->data.enumeration.generate_name_table = true; ira->codegen->name_table_enums.append(target->value.type); } @@ -13869,7 +13803,7 @@ static TypeTableEntry *ir_analyze_instruction_type_id(IrAnalyze *ira, TypeTableEntry *result_type = var_value->data.x_type; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); - bigint_init_unsigned(&out_val->data.x_enum.tag, type_id_index(type_entry->id)); + bigint_init_unsigned(&out_val->data.x_enum_tag, type_id_index(type_entry->id)); return result_type; } @@ -14698,14 +14632,14 @@ static TypeTableEntry *ir_analyze_instruction_member_type(IrAnalyze *ira, IrInst ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_type = field->type_entry; return ira->codegen->builtin_types.entry_type; - } else if (container_type->id == TypeTableEntryIdEnum) { - if (member_index >= container_type->data.enumeration.src_field_count) { + } else if (container_type->id == TypeTableEntryIdUnion) { + if (member_index >= container_type->data.unionation.src_field_count) { ir_add_error(ira, index_value, buf_sprintf("member index %" ZIG_PRI_u64 " out of bounds; '%s' has %" PRIu32 " members", - member_index, buf_ptr(&container_type->name), container_type->data.enumeration.src_field_count)); + member_index, buf_ptr(&container_type->name), container_type->data.unionation.src_field_count)); return ira->codegen->builtin_types.entry_invalid; } - TypeEnumField *field = &container_type->data.enumeration.fields[member_index]; + TypeUnionField *field = &container_type->data.unionation.fields[member_index]; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_type = field->type_entry; @@ -14749,6 +14683,18 @@ static TypeTableEntry *ir_analyze_instruction_member_name(IrAnalyze *ira, IrInst } TypeEnumField *field = &container_type->data.enumeration.fields[member_index]; + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + init_const_str_lit(ira->codegen, out_val, field->name); + return out_val->type; + } else if (container_type->id == TypeTableEntryIdUnion) { + if (member_index >= container_type->data.unionation.src_field_count) { + ir_add_error(ira, index_value, + buf_sprintf("member index %" ZIG_PRI_u64 " out of bounds; '%s' has %" PRIu32 " members", + member_index, buf_ptr(&container_type->name), container_type->data.unionation.src_field_count)); + return ira->codegen->builtin_types.entry_invalid; + } + TypeUnionField *field = &container_type->data.unionation.fields[member_index]; + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); init_const_str_lit(ira->codegen, out_val, field->name); return out_val->type; @@ -14819,7 +14765,6 @@ static TypeTableEntry *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstruc case TypeTableEntryIdErrorUnion: case TypeTableEntryIdPureError: case TypeTableEntryIdEnum: - case TypeTableEntryIdEnumTag: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: { @@ -15125,10 +15070,9 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira if (type_is_invalid(switch_type)) return ira->codegen->builtin_types.entry_invalid; - if (switch_type->id == TypeTableEntryIdEnumTag) { - TypeTableEntry *enum_type = switch_type->data.enum_tag.enum_type; + if (switch_type->id == TypeTableEntryIdEnum) { HashMap field_prev_uses = {}; - field_prev_uses.init(enum_type->data.enumeration.src_field_count); + field_prev_uses.init(switch_type->data.enumeration.src_field_count); for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) { IrInstructionCheckSwitchProngsRange *range = &instruction->ranges[range_i]; @@ -15141,22 +15085,13 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira if (type_is_invalid(end_value->value.type)) return ira->codegen->builtin_types.entry_invalid; + assert(start_value->value.type->id == TypeTableEntryIdEnum); BigInt start_index; + bigint_init_bigint(&start_index, &start_value->value.data.x_enum_tag); + + assert(end_value->value.type->id == TypeTableEntryIdEnum); BigInt end_index; - if (start_value->value.type->id == TypeTableEntryIdEnumTag) { - bigint_init_bigint(&start_index, &start_value->value.data.x_bigint); - } else if (start_value->value.type->id == TypeTableEntryIdEnum) { - bigint_init_bigint(&start_index, &start_value->value.data.x_enum.tag); - } else { - zig_unreachable(); - } - if (end_value->value.type->id == TypeTableEntryIdEnumTag) { - bigint_init_bigint(&end_index, &end_value->value.data.x_bigint); - } else if (end_value->value.type->id == TypeTableEntryIdEnum) { - bigint_init_bigint(&end_index, &end_value->value.data.x_enum.tag); - } else { - zig_unreachable(); - } + bigint_init_bigint(&end_index, &end_value->value.data.x_enum_tag); BigInt field_index; bigint_init_bigint(&field_index, &start_index); @@ -15168,10 +15103,10 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira auto entry = field_prev_uses.put_unique(field_index, start_value->source_node); if (entry) { AstNode *prev_node = entry->value; - TypeEnumField *enum_field = find_enum_field_by_tag(enum_type, &field_index); + TypeEnumField *enum_field = find_enum_field_by_tag(switch_type, &field_index); assert(enum_field != nullptr); ErrorMsg *msg = ir_add_error(ira, start_value, - buf_sprintf("duplicate switch value: '%s.%s'", buf_ptr(&enum_type->name), + buf_sprintf("duplicate switch value: '%s.%s'", buf_ptr(&switch_type->name), buf_ptr(enum_field->name))); add_error_note(ira->codegen, msg, prev_node, buf_sprintf("other value is here")); } @@ -15179,13 +15114,13 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira } } if (!instruction->have_else_prong) { - for (uint32_t i = 0; i < enum_type->data.enumeration.src_field_count; i += 1) { - TypeEnumField *enum_field = &enum_type->data.enumeration.fields[i]; + for (uint32_t i = 0; i < switch_type->data.enumeration.src_field_count; i += 1) { + TypeEnumField *enum_field = &switch_type->data.enumeration.fields[i]; auto entry = field_prev_uses.maybe_get(enum_field->value); if (!entry) { ir_add_error(ira, &instruction->base, - buf_sprintf("enumeration value '%s.%s' not handled in switch", buf_ptr(&enum_type->name), + buf_sprintf("enumeration value '%s.%s' not handled in switch", buf_ptr(&switch_type->name), buf_ptr(enum_field->name))); } } @@ -15481,8 +15416,6 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue zig_panic("TODO buf_write_value_bytes pure error type"); case TypeTableEntryIdEnum: zig_panic("TODO buf_write_value_bytes enum type"); - case TypeTableEntryIdEnumTag: - zig_panic("TODO buf_write_value_bytes enum tag type"); case TypeTableEntryIdFn: zig_panic("TODO buf_write_value_bytes fn type"); case TypeTableEntryIdUnion: @@ -15541,8 +15474,6 @@ static void buf_read_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue zig_panic("TODO buf_read_value_bytes pure error type"); case TypeTableEntryIdEnum: zig_panic("TODO buf_read_value_bytes enum type"); - case TypeTableEntryIdEnumTag: - zig_panic("TODO buf_read_value_bytes enum tag type"); case TypeTableEntryIdFn: zig_panic("TODO buf_read_value_bytes fn type"); case TypeTableEntryIdUnion: @@ -15920,11 +15851,8 @@ static TypeTableEntry *ir_analyze_instruction_enum_tag_type(IrAnalyze *ira, IrIn if (type_is_invalid(enum_type)) return ira->codegen->builtin_types.entry_invalid; - TypeTableEntry *non_int_tag_type = enum_type->data.enumeration.tag_type; - assert(non_int_tag_type->id == TypeTableEntryIdEnumTag); - ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); - out_val->data.x_type = non_int_tag_type->data.enum_tag.int_type; + out_val->data.x_type = enum_type->data.enumeration.tag_int_type; return ira->codegen->builtin_types.entry_type; } @@ -15938,9 +15866,7 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi case IrInstructionIdStructInit: case IrInstructionIdUnionInit: case IrInstructionIdStructFieldPtr: - case IrInstructionIdEnumFieldPtr: case IrInstructionIdUnionFieldPtr: - case IrInstructionIdInitEnum: case IrInstructionIdMaybeWrap: case IrInstructionIdErrWrapCode: case IrInstructionIdErrWrapPayload: @@ -16012,8 +15938,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_switch_target(ira, (IrInstructionSwitchTarget *)instruction); case IrInstructionIdSwitchVar: return ir_analyze_instruction_switch_var(ira, (IrInstructionSwitchVar *)instruction); - case IrInstructionIdEnumTag: - return ir_analyze_instruction_enum_tag(ira, (IrInstructionEnumTag *)instruction); + case IrInstructionIdUnionTag: + return ir_analyze_instruction_union_tag(ira, (IrInstructionUnionTag *)instruction); case IrInstructionIdImport: return ir_analyze_instruction_import(ira, (IrInstructionImport *)instruction); case IrInstructionIdArrayLen: @@ -16260,7 +16186,6 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdPtrTypeChild: case IrInstructionIdArrayLen: case IrInstructionIdStructFieldPtr: - case IrInstructionIdEnumFieldPtr: case IrInstructionIdUnionFieldPtr: case IrInstructionIdArrayType: case IrInstructionIdSliceType: @@ -16271,7 +16196,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdCtz: case IrInstructionIdSwitchVar: case IrInstructionIdSwitchTarget: - case IrInstructionIdEnumTag: + case IrInstructionIdUnionTag: case IrInstructionIdRef: case IrInstructionIdMinValue: case IrInstructionIdMaxValue: @@ -16293,7 +16218,6 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdErrWrapPayload: case IrInstructionIdFnProto: case IrInstructionIdTestComptime: - case IrInstructionIdInitEnum: case IrInstructionIdPtrCast: case IrInstructionIdBitCast: case IrInstructionIdWidenOrShorten: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 0e06d1b563..99617056de 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -291,7 +291,7 @@ static void ir_print_struct_init(IrPrint *irp, IrInstructionStructInit *instruct } static void ir_print_union_init(IrPrint *irp, IrInstructionUnionInit *instruction) { - Buf *field_name = instruction->field->name; + Buf *field_name = instruction->field->enum_field->name; fprintf(irp->f, "%s {", buf_ptr(&instruction->union_type->name)); fprintf(irp->f, ".%s = ", buf_ptr(field_name)); @@ -361,17 +361,10 @@ static void ir_print_struct_field_ptr(IrPrint *irp, IrInstructionStructFieldPtr fprintf(irp->f, ")"); } -static void ir_print_enum_field_ptr(IrPrint *irp, IrInstructionEnumFieldPtr *instruction) { - fprintf(irp->f, "@EnumFieldPtr(&"); - ir_print_other_instruction(irp, instruction->enum_ptr); - fprintf(irp->f, ".%s", buf_ptr(instruction->field->name)); - fprintf(irp->f, ")"); -} - static void ir_print_union_field_ptr(IrPrint *irp, IrInstructionUnionFieldPtr *instruction) { fprintf(irp->f, "@UnionFieldPtr(&"); ir_print_other_instruction(irp, instruction->union_ptr); - fprintf(irp->f, ".%s", buf_ptr(instruction->field->name)); + fprintf(irp->f, ".%s", buf_ptr(instruction->field->enum_field->name)); fprintf(irp->f, ")"); } @@ -509,8 +502,8 @@ static void ir_print_switch_target(IrPrint *irp, IrInstructionSwitchTarget *inst ir_print_other_instruction(irp, instruction->target_value_ptr); } -static void ir_print_enum_tag(IrPrint *irp, IrInstructionEnumTag *instruction) { - fprintf(irp->f, "enumtag "); +static void ir_print_union_tag(IrPrint *irp, IrInstructionUnionTag *instruction) { + fprintf(irp->f, "uniontag "); ir_print_other_instruction(irp, instruction->value); } @@ -799,12 +792,6 @@ static void ir_print_test_comptime(IrPrint *irp, IrInstructionTestComptime *inst fprintf(irp->f, ")"); } -static void ir_print_init_enum(IrPrint *irp, IrInstructionInitEnum *instruction) { - fprintf(irp->f, "%s.%s {", buf_ptr(&instruction->enum_type->name), buf_ptr(instruction->field->name)); - ir_print_other_instruction(irp, instruction->init_value); - fprintf(irp->f, "}"); -} - static void ir_print_ptr_cast(IrPrint *irp, IrInstructionPtrCast *instruction) { fprintf(irp->f, "@ptrCast("); if (instruction->dest_type) { @@ -1078,9 +1065,6 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdStructFieldPtr: ir_print_struct_field_ptr(irp, (IrInstructionStructFieldPtr *)instruction); break; - case IrInstructionIdEnumFieldPtr: - ir_print_enum_field_ptr(irp, (IrInstructionEnumFieldPtr *)instruction); - break; case IrInstructionIdUnionFieldPtr: ir_print_union_field_ptr(irp, (IrInstructionUnionFieldPtr *)instruction); break; @@ -1123,8 +1107,8 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdSwitchTarget: ir_print_switch_target(irp, (IrInstructionSwitchTarget *)instruction); break; - case IrInstructionIdEnumTag: - ir_print_enum_tag(irp, (IrInstructionEnumTag *)instruction); + case IrInstructionIdUnionTag: + ir_print_union_tag(irp, (IrInstructionUnionTag *)instruction); break; case IrInstructionIdImport: ir_print_import(irp, (IrInstructionImport *)instruction); @@ -1237,9 +1221,6 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdTestComptime: ir_print_test_comptime(irp, (IrInstructionTestComptime *)instruction); break; - case IrInstructionIdInitEnum: - ir_print_init_enum(irp, (IrInstructionInitEnum *)instruction); - break; case IrInstructionIdPtrCast: ir_print_ptr_cast(irp, (IrInstructionPtrCast *)instruction); break; diff --git a/src/parser.cpp b/src/parser.cpp index c36434b521..26ca7da31a 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2377,7 +2377,9 @@ static AstNode *ast_parse_use(ParseContext *pc, size_t *token_index, VisibMod vi } /* -ContainerDecl = option("extern" | "packed") ("struct" | "union" | ("enum" option(GroupedExpression))) "{" many(ContainerMember) "}" +ContainerDecl = option("extern" | "packed") + ("struct" option(GroupedExpression) | "union" option("enum" option(GroupedExpression) | GroupedExpression) | ("enum" option(GroupedExpression))) + "{" many(ContainerMember) "}" ContainerMember = (ContainerField | FnDef | GlobalVarDecl) ContainerField = Symbol option(":" PrefixOpExpression option("=" PrefixOpExpression "," */ @@ -2414,7 +2416,28 @@ static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index, AstNode *node = ast_create_node(pc, NodeTypeContainerDecl, first_token); node->data.container_decl.layout = layout; node->data.container_decl.kind = kind; - node->data.container_decl.init_arg_expr = ast_parse_grouped_expr(pc, token_index, false); + + if (kind == ContainerKindUnion) { + Token *lparen_token = &pc->tokens->at(*token_index); + if (lparen_token->id == TokenIdLParen) { + Token *enum_token = &pc->tokens->at(*token_index + 1); + if (enum_token->id == TokenIdKeywordEnum) { + Token *paren_token = &pc->tokens->at(*token_index + 2); + if (paren_token->id == TokenIdLParen) { + node->data.container_decl.auto_enum = true; + *token_index += 2; + node->data.container_decl.init_arg_expr = ast_parse_grouped_expr(pc, token_index, true); + ast_eat_token(pc, token_index, TokenIdRParen); + } else if (paren_token->id == TokenIdRParen) { + node->data.container_decl.auto_enum = true; + *token_index += 3; + } + } + } + } + if (!node->data.container_decl.auto_enum) { + node->data.container_decl.init_arg_expr = ast_parse_grouped_expr(pc, token_index, false); + } ast_eat_token(pc, token_index, TokenIdLBrace); @@ -2461,8 +2484,6 @@ static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index, if (colon_token->id == TokenIdColon) { *token_index += 1; field_node->data.struct_field.type = ast_parse_prefix_op_expr(pc, token_index, true); - } else { - field_node->data.struct_field.type = ast_create_void_type_node(pc, colon_token); } Token *eq_token = &pc->tokens->at(*token_index); if (eq_token->id == TokenIdEq) { diff --git a/std/build.zig b/std/build.zig index 009295c6ad..9bdc4b3076 100644 --- a/std/build.zig +++ b/std/build.zig @@ -69,8 +69,8 @@ pub const Builder = struct { used: bool, }; - const UserValue = enum { - Flag, + const UserValue = union(enum) { + Flag: void, Scalar: []const u8, List: ArrayList([]const u8), }; @@ -450,7 +450,7 @@ pub const Builder = struct { pub fn addUserInputOption(self: &Builder, name: []const u8, value: []const u8) -> bool { if (%%self.user_input_options.put(name, UserInputOption { .name = name, - .value = UserValue.Scalar { value }, + .value = UserValue { .Scalar = value }, .used = false, })) |*prev_value| { // option already exists @@ -462,7 +462,7 @@ pub const Builder = struct { %%list.append(value); _ = %%self.user_input_options.put(name, UserInputOption { .name = name, - .value = UserValue.List { list }, + .value = UserValue { .List = list }, .used = false, }); }, @@ -471,7 +471,7 @@ pub const Builder = struct { %%list.append(value); _ = %%self.user_input_options.put(name, UserInputOption { .name = name, - .value = UserValue.List { *list }, + .value = UserValue { .List = *list }, .used = false, }); }, @@ -487,7 +487,7 @@ pub const Builder = struct { pub fn addUserInputFlag(self: &Builder, name: []const u8) -> bool { if (%%self.user_input_options.put(name, UserInputOption { .name = name, - .value = UserValue.Flag, + .value = UserValue {.Flag = {} }, .used = false, })) |*prev_value| { switch (prev_value.value) { @@ -685,8 +685,8 @@ const CrossTarget = struct { environ: builtin.Environ, }; -const Target = enum { - Native, +const Target = union(enum) { + Native: void, Cross: CrossTarget, pub fn oFileExt(self: &const Target) -> []const u8 { @@ -844,7 +844,7 @@ pub const LibExeObjStep = struct { .kind = kind, .root_src = root_src, .name = name, - .target = Target.Native, + .target = Target { .Native = {} }, .linker_script = null, .link_libs = BufSet.init(builder.allocator), .frameworks = BufSet.init(builder.allocator), @@ -879,7 +879,7 @@ pub const LibExeObjStep = struct { .kind = kind, .version = *version, .static = static, - .target = Target.Native, + .target = Target { .Native = {} }, .cflags = ArrayList([]const u8).init(builder.allocator), .source_files = ArrayList([]const u8).init(builder.allocator), .object_files = ArrayList([]const u8).init(builder.allocator), @@ -948,8 +948,8 @@ pub const LibExeObjStep = struct { pub fn setTarget(self: &LibExeObjStep, target_arch: builtin.Arch, target_os: builtin.Os, target_environ: builtin.Environ) { - self.target = Target.Cross { - CrossTarget { + self.target = Target { + .Cross = CrossTarget { .arch = target_arch, .os = target_os, .environ = target_environ, @@ -1186,13 +1186,13 @@ pub const LibExeObjStep = struct { Target.Native => {}, Target.Cross => |cross_target| { %%zig_args.append("--target-arch"); - %%zig_args.append(@enumTagName(cross_target.arch)); + %%zig_args.append(@tagName(cross_target.arch)); %%zig_args.append("--target-os"); - %%zig_args.append(@enumTagName(cross_target.os)); + %%zig_args.append(@tagName(cross_target.os)); %%zig_args.append("--target-environ"); - %%zig_args.append(@enumTagName(cross_target.environ)); + %%zig_args.append(@tagName(cross_target.environ)); }, } @@ -1553,7 +1553,7 @@ pub const TestStep = struct { .name_prefix = "", .filter = null, .link_libs = BufSet.init(builder.allocator), - .target = Target.Native, + .target = Target { .Native = {} }, .exec_cmd_args = null, } } @@ -1581,8 +1581,8 @@ pub const TestStep = struct { pub fn setTarget(self: &TestStep, target_arch: builtin.Arch, target_os: builtin.Os, target_environ: builtin.Environ) { - self.target = Target.Cross { - CrossTarget { + self.target = Target { + .Cross = CrossTarget { .arch = target_arch, .os = target_os, .environ = target_environ, @@ -1620,13 +1620,13 @@ pub const TestStep = struct { Target.Native => {}, Target.Cross => |cross_target| { %%zig_args.append("--target-arch"); - %%zig_args.append(@enumTagName(cross_target.arch)); + %%zig_args.append(@tagName(cross_target.arch)); %%zig_args.append("--target-os"); - %%zig_args.append(@enumTagName(cross_target.os)); + %%zig_args.append(@tagName(cross_target.os)); %%zig_args.append("--target-environ"); - %%zig_args.append(@enumTagName(cross_target.environ)); + %%zig_args.append(@tagName(cross_target.environ)); }, } diff --git a/std/debug.zig b/std/debug.zig index 50322024c3..5bfa436614 100644 --- a/std/debug.zig +++ b/std/debug.zig @@ -280,7 +280,7 @@ const AbbrevAttr = struct { form_id: u64, }; -const FormValue = enum { +const FormValue = union(enum) { Address: u64, Block: []u8, Const: Constant, @@ -475,7 +475,7 @@ fn readAllocBytes(allocator: &mem.Allocator, in_stream: &io.InStream, size: usiz fn parseFormValueBlockLen(allocator: &mem.Allocator, in_stream: &io.InStream, size: usize) -> %FormValue { const buf = %return readAllocBytes(allocator, in_stream, size); - return FormValue.Block { buf }; + return FormValue { .Block = buf }; } fn parseFormValueBlock(allocator: &mem.Allocator, in_stream: &io.InStream, size: usize) -> %FormValue { @@ -484,7 +484,7 @@ fn parseFormValueBlock(allocator: &mem.Allocator, in_stream: &io.InStream, size: } fn parseFormValueConstant(allocator: &mem.Allocator, in_stream: &io.InStream, signed: bool, size: usize) -> %FormValue { - FormValue.Const { Constant { + FormValue { .Const = Constant { .signed = signed, .payload = %return readAllocBytes(allocator, in_stream, size), }} @@ -510,7 +510,7 @@ fn parseFormValueTargetAddrSize(in_stream: &io.InStream) -> %u64 { fn parseFormValueRefLen(allocator: &mem.Allocator, in_stream: &io.InStream, size: usize) -> %FormValue { const buf = %return readAllocBytes(allocator, in_stream, size); - return FormValue.Ref { buf }; + return FormValue { .Ref = buf }; } fn parseFormValueRef(allocator: &mem.Allocator, in_stream: &io.InStream, comptime T: type) -> %FormValue { @@ -520,7 +520,7 @@ fn parseFormValueRef(allocator: &mem.Allocator, in_stream: &io.InStream, comptim fn parseFormValue(allocator: &mem.Allocator, in_stream: &io.InStream, form_id: u64, is_64: bool) -> %FormValue { return switch (form_id) { - DW.FORM_addr => FormValue.Address { %return parseFormValueTargetAddrSize(in_stream) }, + DW.FORM_addr => FormValue { .Address = %return parseFormValueTargetAddrSize(in_stream) }, DW.FORM_block1 => parseFormValueBlock(allocator, in_stream, 1), DW.FORM_block2 => parseFormValueBlock(allocator, in_stream, 2), DW.FORM_block4 => parseFormValueBlock(allocator, in_stream, 4), @@ -540,13 +540,11 @@ fn parseFormValue(allocator: &mem.Allocator, in_stream: &io.InStream, form_id: u DW.FORM_exprloc => { const size = %return readULeb128(in_stream); const buf = %return readAllocBytes(allocator, in_stream, size); - return FormValue.ExprLoc { buf }; - }, - DW.FORM_flag => FormValue.Flag { (%return in_stream.readByte()) != 0 }, - DW.FORM_flag_present => FormValue.Flag { true }, - DW.FORM_sec_offset => FormValue.SecOffset { - %return parseFormValueDwarfOffsetSize(in_stream, is_64) + return FormValue { .ExprLoc = buf }; }, + DW.FORM_flag => FormValue { .Flag = (%return in_stream.readByte()) != 0 }, + DW.FORM_flag_present => FormValue { .Flag = true }, + DW.FORM_sec_offset => FormValue { .SecOffset = %return parseFormValueDwarfOffsetSize(in_stream, is_64) }, DW.FORM_ref1 => parseFormValueRef(allocator, in_stream, u8), DW.FORM_ref2 => parseFormValueRef(allocator, in_stream, u16), @@ -557,11 +555,11 @@ fn parseFormValue(allocator: &mem.Allocator, in_stream: &io.InStream, form_id: u parseFormValueRefLen(allocator, in_stream, ref_len) }, - DW.FORM_ref_addr => FormValue.RefAddr { %return parseFormValueDwarfOffsetSize(in_stream, is_64) }, - DW.FORM_ref_sig8 => FormValue.RefSig8 { %return in_stream.readIntLe(u64) }, + DW.FORM_ref_addr => FormValue { .RefAddr = %return parseFormValueDwarfOffsetSize(in_stream, is_64) }, + DW.FORM_ref_sig8 => FormValue { .RefSig8 = %return in_stream.readIntLe(u64) }, - DW.FORM_string => FormValue.String { %return readStringRaw(allocator, in_stream) }, - DW.FORM_strp => FormValue.StrPtr { %return parseFormValueDwarfOffsetSize(in_stream, is_64) }, + DW.FORM_string => FormValue { .String = %return readStringRaw(allocator, in_stream) }, + DW.FORM_strp => FormValue { .StrPtr = %return parseFormValueDwarfOffsetSize(in_stream, is_64) }, DW.FORM_indirect => { const child_form_id = %return readULeb128(in_stream); parseFormValue(allocator, in_stream, child_form_id, is_64) diff --git a/std/os/child_process.zig b/std/os/child_process.zig index 005d9772e4..3a4c9410c5 100644 --- a/std/os/child_process.zig +++ b/std/os/child_process.zig @@ -58,7 +58,7 @@ pub const ChildProcess = struct { err_pipe: if (is_windows) void else [2]i32, llnode: if (is_windows) void else LinkedList(&ChildProcess).Node, - pub const Term = enum { + pub const Term = union(enum) { Exited: i32, Signal: i32, Stopped: i32, @@ -281,13 +281,13 @@ pub const ChildProcess = struct { fn statusToTerm(status: i32) -> Term { return if (posix.WIFEXITED(status)) { - Term.Exited { posix.WEXITSTATUS(status) } + Term { .Exited = posix.WEXITSTATUS(status) } } else if (posix.WIFSIGNALED(status)) { - Term.Signal { posix.WTERMSIG(status) } + Term { .Signal = posix.WTERMSIG(status) } } else if (posix.WIFSTOPPED(status)) { - Term.Stopped { posix.WSTOPSIG(status) } + Term { .Stopped = posix.WSTOPSIG(status) } } else { - Term.Unknown { status } + Term { .Unknown = status } }; } diff --git a/std/os/path.zig b/std/os/path.zig index a372b5b077..3fd7b5e2db 100644 --- a/std/os/path.zig +++ b/std/os/path.zig @@ -1016,7 +1016,7 @@ pub fn real(allocator: &Allocator, pathname: []const u8) -> %[]u8 { return os.readLink(allocator, proc_path); }, - else => @compileError("TODO implement os.path.real for " ++ @enumTagName(builtin.os)), + else => @compileError("TODO implement os.path.real for " ++ @tagName(builtin.os)), } } diff --git a/test/cases/bugs/394.zig b/test/cases/bugs/394.zig index aaa7de6f88..071619d59c 100644 --- a/test/cases/bugs/394.zig +++ b/test/cases/bugs/394.zig @@ -1,9 +1,9 @@ -const E = enum { A: [9]u8, B: u64, }; +const E = union(enum) { A: [9]u8, B: u64, }; const S = struct { x: u8, y: E, }; const assert = @import("std").debug.assert; test "bug 394 fixed" { - const x = S { .x = 3, .y = E.B {1} }; + const x = S { .x = 3, .y = E {.B = 1 } }; assert(x.x == 3); } diff --git a/test/cases/enum.zig b/test/cases/enum.zig index eda3cf6376..f3240045df 100644 --- a/test/cases/enum.zig +++ b/test/cases/enum.zig @@ -2,8 +2,8 @@ const assert = @import("std").debug.assert; const mem = @import("std").mem; test "enum type" { - const foo1 = Foo.One {13}; - const foo2 = Foo.Two { Point { .x = 1234, .y = 5678, }}; + const foo1 = Foo{ .One = 13}; + const foo2 = Foo{. Two = Point { .x = 1234, .y = 5678, }}; const bar = Bar.B; assert(bar == Bar.B); @@ -24,12 +24,12 @@ const Point = struct { x: u64, y: u64, }; -const Foo = enum { +const Foo = union(enum) { One: i32, Two: Point, Three: void, }; -const FooNoVoid = enum { +const FooNoVoid = union(enum) { One: i32, Two: Point, }; @@ -41,13 +41,13 @@ const Bar = enum { }; fn returnAnInt(x: i32) -> Foo { - Foo.One { x } + Foo { .One = x } } test "constant enum with payload" { - var empty = AnEnumWithPayload.Empty; - var full = AnEnumWithPayload.Full {13}; + var empty = AnEnumWithPayload {.Empty = {}}; + var full = AnEnumWithPayload {.Full = 13}; shouldBeEmpty(empty); shouldBeNotEmpty(full); } @@ -66,8 +66,8 @@ fn shouldBeNotEmpty(x: &const AnEnumWithPayload) { } } -const AnEnumWithPayload = enum { - Empty, +const AnEnumWithPayload = union(enum) { + Empty: void, Full: i32, }; @@ -109,13 +109,13 @@ const IntToEnumNumber = enum { }; -test "@enumTagName" { +test "@tagName" { assert(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); comptime assert(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); } fn testEnumTagNameBare(n: BareNumber) -> []const u8 { - return @enumTagName(n); + return @tagName(n); } const BareNumber = enum { @@ -132,7 +132,7 @@ test "enum alignment" { } } -const AlignTestEnum = enum { +const AlignTestEnum = union(enum) { A: [9]u8, B: u64, }; diff --git a/test/cases/enum_with_members.zig b/test/cases/enum_with_members.zig index 67ed0410bc..ae48a266d0 100644 --- a/test/cases/enum_with_members.zig +++ b/test/cases/enum_with_members.zig @@ -2,7 +2,7 @@ const assert = @import("std").debug.assert; const mem = @import("std").mem; const fmt = @import("std").fmt; -const ET = enum { +const ET = union(enum) { SINT: i32, UINT: u32, @@ -15,8 +15,8 @@ const ET = enum { }; test "enum with members" { - const a = ET.SINT { -42 }; - const b = ET.UINT { 42 }; + const a = ET { .SINT = -42 }; + const b = ET { .UINT = 42 }; var buf: [20]u8 = undefined; assert(%%a.print(buf[0..]) == 3); diff --git a/test/cases/misc.zig b/test/cases/misc.zig index 76e1b829ee..9f4f064f6b 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -324,8 +324,8 @@ test "constant enum initialization with differing sizes" { test3_1(test3_foo); test3_2(test3_bar); } -const Test3Foo = enum { - One, +const Test3Foo = union(enum) { + One: void, Two: f32, Three: Test3Point, }; @@ -333,8 +333,8 @@ const Test3Point = struct { x: i32, y: i32, }; -const test3_foo = Test3Foo.Three{Test3Point {.x = 3, .y = 4}}; -const test3_bar = Test3Foo.Two{13}; +const test3_foo = Test3Foo { .Three = Test3Point {.x = 3, .y = 4}}; +const test3_bar = Test3Foo { .Two = 13}; fn test3_1(f: &const Test3Foo) { switch (*f) { Test3Foo.Three => |pt| { @@ -449,7 +449,8 @@ fn testArray2DConstDoublePtr(ptr: &const f32) { const Tid = builtin.TypeId; const AStruct = struct { x: i32, }; const AnEnum = enum { One, Two, }; -const AnEnumWithPayload = enum { One: i32, Two, }; +const AUnionEnum = union(enum) { One: i32, Two: void, }; +const AUnion = union { One: void, Two: void }; test "@typeId" { comptime { @@ -474,8 +475,9 @@ test "@typeId" { assert(@typeId(%i32) == Tid.ErrorUnion); assert(@typeId(error) == Tid.Error); assert(@typeId(AnEnum) == Tid.Enum); - assert(@typeId(@typeOf(AnEnumWithPayload.One)) == Tid.EnumTag); - // TODO union + assert(@typeId(@typeOf(AUnionEnum.One)) == Tid.Enum); + assert(@typeId(AUnionEnum) == Tid.Union); + assert(@typeId(AUnion) == Tid.Union); assert(@typeId(fn()) == Tid.Fn); assert(@typeId(@typeOf(builtin)) == Tid.Namespace); assert(@typeId(@typeOf({this})) == Tid.Block); diff --git a/test/cases/reflection.zig b/test/cases/reflection.zig index 4227f79a04..cbd98d212f 100644 --- a/test/cases/reflection.zig +++ b/test/cases/reflection.zig @@ -62,8 +62,8 @@ const Foo = struct { three: void, }; -const Bar = enum { - One, +const Bar = union(enum) { + One: void, Two: i32, Three: bool, Four: f64, diff --git a/test/cases/switch.zig b/test/cases/switch.zig index 9ec50c7e25..11c2178427 100644 --- a/test/cases/switch.zig +++ b/test/cases/switch.zig @@ -83,14 +83,14 @@ const SwitchStatmentFoo = enum { test "switch prong with variable" { - switchProngWithVarFn(SwitchProngWithVarEnum.One {13}); - switchProngWithVarFn(SwitchProngWithVarEnum.Two {13.0}); - switchProngWithVarFn(SwitchProngWithVarEnum.Meh); + switchProngWithVarFn(SwitchProngWithVarEnum { .One = 13}); + switchProngWithVarFn(SwitchProngWithVarEnum { .Two = 13.0}); + switchProngWithVarFn(SwitchProngWithVarEnum { .Meh = {}}); } -const SwitchProngWithVarEnum = enum { +const SwitchProngWithVarEnum = union(enum) { One: i32, Two: f32, - Meh, + Meh: void, }; fn switchProngWithVarFn(a: &const SwitchProngWithVarEnum) { switch(*a) { @@ -112,7 +112,7 @@ test "switch on enum using pointer capture" { } fn testSwitchEnumPtrCapture() { - var value = SwitchProngWithVarEnum.One { 1234 }; + var value = SwitchProngWithVarEnum { .One = 1234 }; switch (value) { SwitchProngWithVarEnum.One => |*x| *x += 1, else => unreachable, @@ -136,13 +136,13 @@ fn returnsFive() -> i32 { } -const Number = enum { +const Number = union(enum) { One: u64, Two: u8, Three: f32, }; -const number = Number.Three { 1.23 }; +const number = Number { .Three = 1.23 }; fn returnsFalse() -> bool { switch (number) { diff --git a/test/cases/switch_prong_err_enum.zig b/test/cases/switch_prong_err_enum.zig index d565de8c71..21f6b04037 100644 --- a/test/cases/switch_prong_err_enum.zig +++ b/test/cases/switch_prong_err_enum.zig @@ -9,14 +9,14 @@ fn readOnce() -> %u64 { error InvalidDebugInfo; -const FormValue = enum { +const FormValue = union(enum) { Address: u64, Other: bool, }; fn doThing(form_id: u64) -> %FormValue { return switch (form_id) { - 17 => FormValue.Address { %return readOnce() }, + 17 => FormValue { .Address = %return readOnce() }, else => error.InvalidDebugInfo, } } diff --git a/test/cases/switch_prong_implicit_cast.zig b/test/cases/switch_prong_implicit_cast.zig index 131c5e1c80..e7fe53b841 100644 --- a/test/cases/switch_prong_implicit_cast.zig +++ b/test/cases/switch_prong_implicit_cast.zig @@ -1,7 +1,7 @@ const assert = @import("std").debug.assert; -const FormValue = enum { - One, +const FormValue = union(enum) { + One: void, Two: bool, }; @@ -9,8 +9,8 @@ error Whatever; fn foo(id: u64) -> %FormValue { switch (id) { - 2 => FormValue.Two { true }, - 1 => FormValue.One, + 2 => FormValue { .Two = true }, + 1 => FormValue { .One = {} }, else => return error.Whatever, } } diff --git a/test/cases/union.zig b/test/cases/union.zig index 4044721582..ec050a20d5 100644 --- a/test/cases/union.zig +++ b/test/cases/union.zig @@ -1,6 +1,6 @@ const assert = @import("std").debug.assert; -const Value = enum { +const Value = union(enum) { Int: u64, Array: [9]u8, }; @@ -10,8 +10,8 @@ const Agg = struct { val2: Value, }; -const v1 = Value.Int { 1234 }; -const v2 = Value.Array { []u8{3} ** 9 }; +const v1 = Value { .Int = 1234 }; +const v2 = Value { .Array = []u8{3} ** 9 }; const err = (%Agg)(Agg { .val1 = v1, @@ -75,3 +75,32 @@ test "basic extern unions" { assert(foo.float == 12.34); } + +const Letter = enum { + A, + B, + C, +}; +const Payload = union(Letter) { + A: i32, + B: f64, + C: bool, +}; + +test "union with specified enum tag" { + doTest(); + comptime doTest(); +} + +fn doTest() { + assert(bar(Payload {.A = 1234}) == -10); +} + +fn bar(value: &const Payload) -> i32 { + assert(Letter(*value) == Letter.A); + return switch (*value) { + Payload.A => |x| return x - 1244, + Payload.B => |x| if (x == 12.34) i32(20) else 21, + Payload.C => |x| if (x) i32(30) else 31, + }; +} diff --git a/test/compile_errors.zig b/test/compile_errors.zig index e1de167ac5..ac06b5aa2a 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -930,8 +930,8 @@ pub fn addCases(cases: &tests.CompileErrorContext) { \\fn bad_eql_1(a: []u8, b: []u8) -> bool { \\ a == b \\} - \\const EnumWithData = enum { - \\ One, + \\const EnumWithData = union(enum) { + \\ One: void, \\ Two: i32, \\}; \\fn bad_eql_2(a: &const EnumWithData, b: &const EnumWithData) -> bool { @@ -1145,19 +1145,19 @@ pub fn addCases(cases: &tests.CompileErrorContext) { \\const JasonHM = u8; \\const JasonList = &JsonNode; \\ - \\const JsonOA = enum { + \\const JsonOA = union(enum) { \\ JSONArray: JsonList, \\ JSONObject: JasonHM, \\}; \\ - \\const JsonType = enum { + \\const JsonType = union(enum) { \\ JSONNull: void, \\ JSONInteger: isize, \\ JSONDouble: f64, \\ JSONBool: bool, \\ JSONString: []u8, - \\ JSONArray, - \\ JSONObject, + \\ JSONArray: void, + \\ JSONObject: void, \\}; \\ \\pub const JsonNode = struct { @@ -2138,7 +2138,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) { \\ \\const MdText = ArrayList(u8); \\ - \\const MdNode = enum { + \\const MdNode = union(enum) { \\ Header: struct { \\ text: MdText, \\ weight: HeaderValue, @@ -2297,6 +2297,14 @@ pub fn addCases(cases: &tests.CompileErrorContext) { , ".tmp_source.zig:2:21: error: type 'i32' does not support @memberType"); + cases.add("@memberType on enum", + \\comptime { + \\ _ = @memberType(Foo, 0); + \\} + \\const Foo = enum {A,}; + , + ".tmp_source.zig:2:21: error: type 'Foo' does not support @memberType"); + cases.add("@memberType struct out of bounds", \\comptime { \\ _ = @memberType(Foo, 0); @@ -2305,11 +2313,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) { , ".tmp_source.zig:2:26: error: member index 0 out of bounds; 'Foo' has 0 members"); - cases.add("@memberType enum out of bounds", + cases.add("@memberType union out of bounds", \\comptime { \\ _ = @memberType(Foo, 1); \\} - \\const Foo = enum {A,}; + \\const Foo = union {A: void,}; , ".tmp_source.zig:2:26: error: member index 1 out of bounds; 'Foo' has 1 members"); @@ -2336,6 +2344,14 @@ pub fn addCases(cases: &tests.CompileErrorContext) { , ".tmp_source.zig:2:26: error: member index 1 out of bounds; 'Foo' has 1 members"); + cases.add("@memberName union out of bounds", + \\comptime { + \\ _ = @memberName(Foo, 1); + \\} + \\const Foo = union {A:i32,}; + , + ".tmp_source.zig:2:26: error: member index 1 out of bounds; 'Foo' has 1 members"); + cases.add("calling var args extern function, passing array instead of pointer", \\export fn entry() { \\ foo("hello"); @@ -2466,7 +2482,8 @@ pub fn addCases(cases: &tests.CompileErrorContext) { \\ var x: MultipleChoice = undefined; \\} , - ".tmp_source.zig:2:14: error: enums, not unions, support field assignment"); + ".tmp_source.zig:2:14: error: non-enum union field assignment", + ".tmp_source.zig:1:24: note: consider 'union(enum)' here"); cases.add("enum with 0 fields", \\const Foo = enum {}; @@ -2490,4 +2507,21 @@ pub fn addCases(cases: &tests.CompileErrorContext) { , ".tmp_source.zig:6:9: error: enum tag value 60 already taken", ".tmp_source.zig:4:9: note: other occurrence here"); + + cases.add("union with specified enum omits field", + \\const Letter = enum { + \\ A, + \\ B, + \\ C, + \\}; + \\const Payload = union(Letter) { + \\ A: i32, + \\ B: f64, + \\}; + \\export fn entry() -> usize { + \\ return @sizeOf(Payload); + \\} + , + ".tmp_source.zig:6:17: error: enum field missing: 'C'", + ".tmp_source.zig:4:5: note: declared here"); } diff --git a/test/tests.zig b/test/tests.zig index 73d9646552..e74afa1755 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -150,8 +150,8 @@ pub fn addPkgTests(b: &build.Builder, test_filter: ?[]const u8, root_src: []cons continue; } const these_tests = b.addTest(root_src); - these_tests.setNamePrefix(b.fmt("{}-{}-{}-{}-{} ", name, @enumTagName(test_target.os), - @enumTagName(test_target.arch), @enumTagName(mode), if (link_libc) "c" else "bare")); + these_tests.setNamePrefix(b.fmt("{}-{}-{}-{}-{} ", name, @tagName(test_target.os), + @tagName(test_target.arch), @tagName(mode), if (link_libc) "c" else "bare")); these_tests.setFilter(test_filter); these_tests.setBuildMode(mode); if (!is_native) { @@ -428,7 +428,7 @@ pub const CompareOutputContext = struct { Special.None => { for ([]Mode{Mode.Debug, Mode.ReleaseSafe, Mode.ReleaseFast}) |mode| { const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "{} {} ({})", - "compare-output", case.name, @enumTagName(mode)); + "compare-output", case.name, @tagName(mode)); if (self.test_filter) |filter| { if (mem.indexOf(u8, annotated_case_name, filter) == null) continue; @@ -682,7 +682,7 @@ pub const CompileErrorContext = struct { for ([]Mode{Mode.Debug, Mode.ReleaseSafe, Mode.ReleaseFast}) |mode| { const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "compile-error {} ({})", - case.name, @enumTagName(mode)); + case.name, @tagName(mode)); if (self.test_filter) |filter| { if (mem.indexOf(u8, annotated_case_name, filter) == null) continue; @@ -750,7 +750,7 @@ pub const BuildExamplesContext = struct { for ([]Mode{Mode.Debug, Mode.ReleaseSafe, Mode.ReleaseFast}) |mode| { const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "build {} ({})", - root_src, @enumTagName(mode)); + root_src, @tagName(mode)); if (self.test_filter) |filter| { if (mem.indexOf(u8, annotated_case_name, filter) == null) continue; -- cgit v1.2.3