From 137c8f5e8a6023db24f90555e968b592a4b843e4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 2 Dec 2017 22:31:42 -0500 Subject: [PATCH] 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(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 32c099f547c4..20b8ae1eeea3 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 40d246c43cbf..8d4ffc5b8464 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 7d51e83677d2..b7d12443a094 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 b2464af9a006..50eb2a800ff4 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 ce7bcd9e36ee..cdc18c72527e 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 d12c8d075951..f01436d23224 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 a1facb5c7895..9f044c87229d 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 a5f8b85e2251..0ac8ffb7e167 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 c6003fbf3282..e51f52adae57 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 7f25e3ef218a..c36434b52130 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 57fb0ab4c0b7..d02d685e0779 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 6df858a48f2c..eda3cf63769e 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 367dec08b300..e1de167ac555 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"); }