From 418b0967fc703dbad484b58bc09390e101219581 Mon Sep 17 00:00:00 2001 From: Josh Wolfe Date: Wed, 29 Nov 2017 17:52:58 -0700 Subject: [PATCH 01/32] fix os.Dir compile errors --- std/os/index.zig | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/std/os/index.zig b/std/os/index.zig index e6a5fc4d15..361750aedc 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -939,6 +939,7 @@ start_over: } pub const Dir = struct { + // See man getdents fd: i32, allocator: &Allocator, buf: []u8, @@ -981,7 +982,7 @@ pub const Dir = struct { pub fn close(self: &Dir) { self.allocator.free(self.buf); - close(self.fd); + os.close(self.fd); } /// Memory such as file names referenced in this returned entry becomes invalid @@ -1013,7 +1014,7 @@ pub const Dir = struct { break; } } - const linux_entry = @ptrCast(&LinuxEntry, &self.buf[self.index]); + const linux_entry = @ptrCast(& align(1) LinuxEntry, &self.buf[self.index]); const next_index = self.index + linux_entry.d_reclen; self.index = next_index; From 88a7f203f9de9a78f908dc63082173e10f9bf30e Mon Sep 17 00:00:00 2001 From: Josh Wolfe Date: Wed, 29 Nov 2017 19:31:09 -0700 Subject: [PATCH 02/32] add Buffer.appendFormat() --- std/buffer.zig | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/std/buffer.zig b/std/buffer.zig index a1aa8faf9d..96abaeb762 100644 --- a/std/buffer.zig +++ b/std/buffer.zig @@ -4,6 +4,8 @@ const Allocator = mem.Allocator; const assert = debug.assert; const ArrayList = @import("array_list.zig").ArrayList; +const fmt = @import("fmt/index.zig"); + /// A buffer that allocates memory and maintains a null byte at the end. pub const Buffer = struct { list: ArrayList(u8), @@ -96,6 +98,10 @@ pub const Buffer = struct { mem.copy(u8, self.list.toSlice()[old_len..], m); } + pub fn appendFormat(self: &Buffer, comptime format: []const u8, args: ...) -> %void { + return fmt.format(self, append, format, args); + } + pub fn appendByte(self: &Buffer, byte: u8) -> %void { return self.appendByteNTimes(byte, 1); } From ccea8dcbf61cc4483bc73ba45751e545a8f3541e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 29 Nov 2017 20:32:59 -0500 Subject: [PATCH 03/32] better error code for File.getEndPos failure --- std/io.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/io.zig b/std/io.zig index d570927488..6c6c171997 100644 --- a/std/io.zig +++ b/std/io.zig @@ -259,7 +259,7 @@ pub const File = struct { if (err > 0) { return switch (err) { system.EBADF => error.BadFd, - system.ENOMEM => error.OutOfMemory, + system.ENOMEM => error.SystemResources, else => os.unexpectedErrorPosix(err), } } From 716b0b8655610a3a64818c51a91254c6e4715e49 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 29 Nov 2017 21:33:58 -0500 Subject: [PATCH 04/32] fix capturing value of switch with all unreachable prongs closes #635 --- src/analyze.cpp | 2 +- src/ir.cpp | 4 ++-- test/cases/switch.zig | 11 +++++++++++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 1c223c63f7..a12934f2b2 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -4664,7 +4664,7 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { buf_appendf(buf, "%s", buf_ptr(&const_val->data.x_type->name)); return; case TypeTableEntryIdUnreachable: - buf_appendf(buf, "@unreachable()"); + buf_appendf(buf, "unreachable"); return; case TypeTableEntryIdBool: { diff --git a/src/ir.cpp b/src/ir.cpp index 7c15b48bee..f1dc1e1b4d 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -11154,8 +11154,8 @@ static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionP } if (new_incoming_blocks.length == 0) { - ir_build_const_from(ira, &phi_instruction->base); - return ira->codegen->builtin_types.entry_void; + ir_build_unreachable_from(&ira->new_irb, &phi_instruction->base); + return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable); } if (new_incoming_blocks.length == 1) { diff --git a/test/cases/switch.zig b/test/cases/switch.zig index 154e251a04..9ec50c7e25 100644 --- a/test/cases/switch.zig +++ b/test/cases/switch.zig @@ -224,3 +224,14 @@ fn switchWithUnreachable(x: i32) -> i32 { } return 10; } + +fn return_a_number() -> %i32 { + return 1; +} + +test "capture value of switch with all unreachable prongs" { + const x = return_a_number() %% |err| switch (err) { + else => unreachable, + }; + assert(x == 1); +} From 7729f6cf4edd7c8eda935b1c7c8b4276d73b6e69 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 29 Nov 2017 21:50:38 -0500 Subject: [PATCH 05/32] translate-c: support static incomplete array inside function --- src/translate_c.cpp | 16 ++++++++++++++-- test/translate_c.zig | 10 ++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/translate_c.cpp b/src/translate_c.cpp index f5c1ef5810..f89444a5ee 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -976,11 +976,23 @@ static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &sou const AttributedType *attributed_ty = static_cast(ty); return trans_qual_type(c, attributed_ty->getEquivalentType(), source_loc); } + case Type::IncompleteArray: + { + const IncompleteArrayType *incomplete_array_ty = static_cast(ty); + QualType child_qt = incomplete_array_ty->getElementType(); + AstNode *child_type_node = trans_qual_type(c, child_qt, source_loc); + if (child_type_node == nullptr) { + emit_warning(c, source_loc, "unresolved array element type"); + return nullptr; + } + AstNode *pointer_node = trans_create_node_addr_of(c, child_qt.isConstQualified(), + child_qt.isVolatileQualified(), child_type_node); + return pointer_node; + } case Type::BlockPointer: case Type::LValueReference: case Type::RValueReference: case Type::MemberPointer: - case Type::IncompleteArray: case Type::VariableArray: case Type::DependentSizedArray: case Type::DependentSizedExtVector: @@ -4301,7 +4313,7 @@ int parse_h_file(ImportTableEntry *import, ZigList *errors, const ch } } - return 0; + return ErrorUnexpected; } c->ctx = &ast_unit->getASTContext(); diff --git a/test/translate_c.zig b/test/translate_c.zig index 90b99b5faf..d4974109da 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1178,4 +1178,14 @@ pub fn addCases(cases: &tests.TranslateCContext) { , \\pub var v0: ?&const u8 = c"0.0.0"; ); + + cases.add("static incomplete array inside function", + \\void foo(void) { + \\ static const char v2[] = "2.2.2"; + \\} + , + \\pub fn foo() { + \\ const v2: &const u8 = c"2.2.2"; + \\} + ); } From 210d0017c40ee24215ce705fcee342fe34b9dccb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 29 Nov 2017 23:09:35 -0500 Subject: [PATCH 06/32] fix build broken by previous commit now we report a compile error for unusual failures from translate-c --- src/error.cpp | 1 + src/error.hpp | 1 + src/ir.cpp | 5 ++++- src/translate_c.cpp | 2 +- 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/error.cpp b/src/error.cpp index a6953d4ab3..8303a80a1d 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -26,6 +26,7 @@ const char *err_str(int err) { case ErrorExactDivRemainder: return "exact division had a remainder"; case ErrorNegativeDenominator: return "negative denominator"; case ErrorShiftedOutOneBits: return "exact shift shifted out one bits"; + case ErrorCCompileErrors: return "C compile errors"; } return "(invalid error)"; } diff --git a/src/error.hpp b/src/error.hpp index d7d9c45baf..e3b87fc6b8 100644 --- a/src/error.hpp +++ b/src/error.hpp @@ -26,6 +26,7 @@ enum Error { ErrorExactDivRemainder, ErrorNegativeDenominator, ErrorShiftedOutOneBits, + ErrorCCompileErrors, }; const char *err_str(int err); diff --git a/src/ir.cpp b/src/ir.cpp index f1dc1e1b4d..e3f223a595 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -13870,7 +13870,10 @@ static TypeTableEntry *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstruc int err; if ((err = parse_h_buf(child_import, &errors, &cimport_scope->buf, ira->codegen, node))) { - zig_panic("unable to parse C file: %s\n", err_str(err)); + if (err != ErrorCCompileErrors) { + ir_add_error_node(ira, node, buf_sprintf("C import failed: %s", err_str(err))); + return ira->codegen->builtin_types.entry_invalid; + } } if (errors.length > 0) { diff --git a/src/translate_c.cpp b/src/translate_c.cpp index f89444a5ee..3dafa757d9 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -4313,7 +4313,7 @@ int parse_h_file(ImportTableEntry *import, ZigList *errors, const ch } } - return ErrorUnexpected; + return ErrorCCompileErrors; } c->ctx = &ast_unit->getASTContext(); From 5786df933d37d52d57fef9c28acb9c2c23128d31 Mon Sep 17 00:00:00 2001 From: Josh Wolfe Date: Thu, 30 Nov 2017 11:20:39 -0700 Subject: [PATCH 07/32] add mem.readIntLE and readIntBE --- std/mem.zig | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/std/mem.zig b/std/mem.zig index 3cfdb25b35..9d78b1513b 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -180,6 +180,7 @@ test "mem.indexOf" { /// Reads an integer from memory with size equal to bytes.len. /// T specifies the return type, which must be large enough to store /// the result. +/// See also ::readIntBE or ::readIntLE. pub fn readInt(bytes: []const u8, comptime T: type, big_endian: bool) -> T { if (T.bit_count == 8) { return bytes[0]; @@ -198,6 +199,34 @@ pub fn readInt(bytes: []const u8, comptime T: type, big_endian: bool) -> T { return result; } +/// Reads a big-endian int of type T from bytes. +/// bytes.len must be exactly @sizeOf(T). +pub fn readIntBE(comptime T: type, bytes: []const u8) -> T { + if (T.is_signed) { + return @bitCast(T, readIntBE(@IntType(false, T.bit_count), bytes)); + } + assert(bytes.len == @sizeOf(T)); + var result: T = 0; + {comptime var i = 0; inline while (i < @sizeOf(T)) : (i += 1) { + result = (result << 8) | T(bytes[i]); + }} + return result; +} + +/// Reads a little-endian int of type T from bytes. +/// bytes.len must be exactly @sizeOf(T). +pub fn readIntLE(comptime T: type, bytes: []const u8) -> T { + if (T.is_signed) { + return @bitCast(T, readIntLE(@IntType(false, T.bit_count), bytes)); + } + assert(bytes.len == @sizeOf(T)); + var result: T = 0; + {comptime var i = 0; inline while (i < @sizeOf(T)) : (i += 1) { + result |= T(bytes[i]) << i * 8; + }} + return result; +} + /// Writes an integer to memory with size equal to bytes.len. Pads with zeroes /// to fill the entire buffer provided. /// value must be an integer. @@ -348,8 +377,12 @@ test "testReadInt" { fn testReadIntImpl() { { const bytes = []u8{ 0x12, 0x34, 0x56, 0x78 }; - assert(readInt(bytes, u32, true) == 0x12345678); + assert(readInt(bytes, u32, true) == 0x12345678); + assert(readIntBE(u32, bytes) == 0x12345678); + assert(readIntBE(i32, bytes) == 0x12345678); assert(readInt(bytes, u32, false) == 0x78563412); + assert(readIntLE(u32, bytes) == 0x78563412); + assert(readIntLE(i32, bytes) == 0x78563412); } { const buf = []u8{0x00, 0x00, 0x12, 0x34}; @@ -361,6 +394,13 @@ fn testReadIntImpl() { const answer = readInt(buf, u64, false); assert(answer == 0x00003412); } + { + const bytes = []u8{0xff, 0xfe}; + assert(readIntBE(u16, bytes) == 0xfffe); + assert(readIntBE(i16, bytes) == -0x0002); + assert(readIntLE(u16, bytes) == 0xfeff); + assert(readIntLE(i16, bytes) == -0x0101); + } } test "testWriteInt" { From b62e2fd8703129fcf0dc80675800f005e84ee724 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 30 Nov 2017 21:46:02 -0500 Subject: [PATCH 08/32] ability to specify tag type of enums see #305 --- doc/langref.html.in | 8 +++++- src/all_types.hpp | 8 ++++++ src/analyze.cpp | 21 ++++++++++++++- src/ast_render.cpp | 8 +++++- src/codegen.cpp | 4 ++- src/ir.cpp | 57 +++++++++++++++++++++++++++++++++++++++++ src/ir_print.cpp | 9 +++++++ src/parser.cpp | 7 ++++- src/translate_c.cpp | 12 ++++++++- test/cases/enum.zig | 24 +++++++++++++++++ test/compile_errors.zig | 28 ++++++++++++++++++++ 11 files changed, 180 insertions(+), 6 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index e8f76e230b..32c099f547 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -137,6 +137,7 @@
  • @divTrunc
  • @embedFile
  • @enumTagName
  • +
  • @EnumTagType
  • @errorName
  • @fence
  • @fieldParentPtr
  • @@ -4256,6 +4257,11 @@ test.zig:6:2: error: found compile log statement

    Converts an enum tag name to a slice of bytes.

    +

    @EnumTagType

    +
    @EnumTagType(T: type) -> type
    +

    + Returns the integer type that is used to store the enumeration value. +

    @errorName

    @errorName(err: error) -> []u8

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

    Zen

    • Communicate intent precisely.
    • diff --git a/src/all_types.hpp b/src/all_types.hpp index 2fccf08e88..40d246c43c 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1286,6 +1286,7 @@ enum BuiltinFnId { BuiltinFnIdIntToPtr, BuiltinFnIdPtrToInt, BuiltinFnIdEnumTagName, + BuiltinFnIdEnumTagType, BuiltinFnIdFieldParentPtr, BuiltinFnIdOffsetOf, BuiltinFnIdInlineCall, @@ -1911,6 +1912,7 @@ enum IrInstructionId { IrInstructionIdDeclRef, IrInstructionIdPanic, IrInstructionIdEnumTagName, + IrInstructionIdEnumTagType, IrInstructionIdFieldParentPtr, IrInstructionIdOffsetOf, IrInstructionIdTypeId, @@ -2695,6 +2697,12 @@ struct IrInstructionEnumTagName { IrInstruction *target; }; +struct IrInstructionEnumTagType { + IrInstruction base; + + IrInstruction *target; +}; + struct IrInstructionFieldParentPtr { IrInstruction base; diff --git a/src/analyze.cpp b/src/analyze.cpp index a12934f2b2..33d6ccdc39 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1267,7 +1267,7 @@ TypeTableEntry *create_enum_tag_type(CodeGen *g, TypeTableEntry *enum_type, Type TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnumTag); buf_resize(&entry->name, 0); - buf_appendf(&entry->name, "@enumTagType(%s)", buf_ptr(&enum_type->name)); + buf_appendf(&entry->name, "@EnumTagType(%s)", buf_ptr(&enum_type->name)); entry->is_copyable = true; entry->data.enum_tag.enum_type = enum_type; @@ -1391,6 +1391,25 @@ static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) { } 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.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_type_entry = create_enum_tag_type(g, enum_type, tag_int_type); enum_type->data.enumeration.tag_type = tag_type_entry; diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 5ffec92030..ce7bcd9e36 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -660,7 +660,13 @@ 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 {\n", layout_str, container_str); + fprintf(ar->f, "%s%s", layout_str, container_str); + 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, ")"); + } + 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) { AstNode *field_node = node->data.container_decl.fields.at(field_i); diff --git a/src/codegen.cpp b/src/codegen.cpp index 24d24a91e5..da94dd4bcd 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3537,6 +3537,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdOpaqueType: case IrInstructionIdSetAlignStack: case IrInstructionIdArgType: + case IrInstructionIdEnumTagType: zig_unreachable(); case IrInstructionIdReturn: return ir_render_return(g, executable, (IrInstructionReturn *)instruction); @@ -5049,7 +5050,8 @@ 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); + create_builtin_fn(g, BuiltinFnIdEnumTagName, "enumTagName", 1); // TODO rename to memberName + create_builtin_fn(g, BuiltinFnIdEnumTagType, "EnumTagType", 1); create_builtin_fn(g, BuiltinFnIdFieldParentPtr, "fieldParentPtr", 3); create_builtin_fn(g, BuiltinFnIdOffsetOf, "offsetOf", 2); create_builtin_fn(g, BuiltinFnIdDivExact, "divExact", 2); diff --git a/src/ir.cpp b/src/ir.cpp index e3f223a595..c8617e3a8a 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -551,6 +551,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionEnumTagName *) { return IrInstructionIdEnumTagName; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionEnumTagType *) { + return IrInstructionIdEnumTagType; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionFieldParentPtr *) { return IrInstructionIdFieldParentPtr; } @@ -2270,6 +2274,17 @@ static IrInstruction *ir_build_enum_tag_name(IrBuilder *irb, Scope *scope, AstNo return &instruction->base; } +static IrInstruction *ir_build_enum_tag_type(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *target) +{ + IrInstructionEnumTagType *instruction = ir_build_instruction(irb, scope, source_node); + instruction->target = target; + + ir_ref_instruction(target, irb->current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_field_parent_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_value, IrInstruction *field_name, IrInstruction *field_ptr, TypeStructField *field) { @@ -3066,6 +3081,13 @@ static IrInstruction *ir_instruction_enumtagname_get_dep(IrInstructionEnumTagNam } } +static IrInstruction *ir_instruction_enumtagtype_get_dep(IrInstructionEnumTagType *instruction, size_t index) { + switch (index) { + case 0: return instruction->target; + default: return nullptr; + } +} + static IrInstruction *ir_instruction_fieldparentptr_get_dep(IrInstructionFieldParentPtr *instruction, size_t index) { switch (index) { case 0: return instruction->type_value; @@ -3326,6 +3348,8 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t return ir_instruction_panic_get_dep((IrInstructionPanic *) instruction, index); case IrInstructionIdEnumTagName: return ir_instruction_enumtagname_get_dep((IrInstructionEnumTagName *) instruction, index); + case IrInstructionIdEnumTagType: + return ir_instruction_enumtagtype_get_dep((IrInstructionEnumTagType *) instruction, index); case IrInstructionIdFieldParentPtr: return ir_instruction_fieldparentptr_get_dep((IrInstructionFieldParentPtr *) instruction, index); case IrInstructionIdOffsetOf: @@ -4681,6 +4705,15 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo IrInstruction *actual_tag = ir_build_enum_tag(irb, scope, node, arg0_value); return ir_build_enum_tag_name(irb, scope, node, actual_tag); } + case BuiltinFnIdEnumTagType: + { + 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; + + return ir_build_enum_tag_type(irb, scope, node, arg0_value); + } case BuiltinFnIdFieldParentPtr: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); @@ -15831,6 +15864,27 @@ static TypeTableEntry *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstruc return ira->codegen->builtin_types.entry_type; } +static TypeTableEntry *ir_analyze_instruction_enum_tag_type(IrAnalyze *ira, IrInstructionEnumTagType *instruction) { + IrInstruction *target_inst = instruction->target->other; + TypeTableEntry *enum_type = ir_resolve_type(ira, target_inst); + if (type_is_invalid(enum_type)) + return ira->codegen->builtin_types.entry_invalid; + if (enum_type->id != TypeTableEntryIdEnum) { + ir_add_error(ira, target_inst, buf_sprintf("expected enum, found '%s'", buf_ptr(&enum_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + ensure_complete_type(ira->codegen, enum_type); + 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; + return ira->codegen->builtin_types.entry_type; +} + static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { switch (instruction->id) { case IrInstructionIdInvalid: @@ -16029,6 +16083,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_set_align_stack(ira, (IrInstructionSetAlignStack *)instruction); case IrInstructionIdArgType: return ir_analyze_instruction_arg_type(ira, (IrInstructionArgType *)instruction); + case IrInstructionIdEnumTagType: + return ir_analyze_instruction_enum_tag_type(ira, (IrInstructionEnumTagType *)instruction); } zig_unreachable(); } @@ -16214,6 +16270,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdAlignCast: case IrInstructionIdOpaqueType: case IrInstructionIdArgType: + case IrInstructionIdEnumTagType: return false; case IrInstructionIdAsm: { diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 55ad3ceb6c..0e06d1b563 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -994,6 +994,12 @@ static void ir_print_arg_type(IrPrint *irp, IrInstructionArgType *instruction) { fprintf(irp->f, ")"); } +static void ir_print_enum_tag_type(IrPrint *irp, IrInstructionEnumTagType *instruction) { + fprintf(irp->f, "@EnumTagType("); + ir_print_other_instruction(irp, instruction->target); + fprintf(irp->f, ")"); +} + static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { ir_print_prefix(irp, instruction); @@ -1312,6 +1318,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdArgType: ir_print_arg_type(irp, (IrInstructionArgType *)instruction); break; + case IrInstructionIdEnumTagType: + ir_print_enum_tag_type(irp, (IrInstructionEnumTagType *)instruction); + break; } fprintf(irp->f, "\n"); } 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); diff --git a/src/translate_c.cpp b/src/translate_c.cpp index 3dafa757d9..2bba498720 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -651,6 +651,14 @@ static bool c_is_unsigned_integer(Context *c, QualType qt) { } } +static bool c_is_builtin_type(Context *c, QualType qt, BuiltinType::Kind kind) { + const Type *c_type = qual_type_canon(qt); + if (c_type->getTypeClass() != Type::Builtin) + return false; + const BuiltinType *builtin_ty = static_cast(c_type); + return builtin_ty->getKind() == kind; +} + static bool c_is_float(Context *c, QualType qt) { const Type *c_type = qt.getTypePtr(); if (c_type->getTypeClass() != Type::Builtin) @@ -3426,7 +3434,9 @@ static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) { AstNode *enum_node = trans_create_node(c, NodeTypeContainerDecl); enum_node->data.container_decl.kind = ContainerKindEnum; enum_node->data.container_decl.layout = ContainerLayoutExtern; - enum_node->data.container_decl.init_arg_expr = tag_int_type; + if (!c_is_builtin_type(c, enum_decl->getIntegerType(), BuiltinType::UInt)) { + enum_node->data.container_decl.init_arg_expr = tag_int_type; + } enum_node->data.container_decl.fields.resize(field_count); uint32_t i = 0; diff --git a/test/cases/enum.zig b/test/cases/enum.zig index f2d8ded0d6..a56ed398b8 100644 --- a/test/cases/enum.zig +++ b/test/cases/enum.zig @@ -190,3 +190,27 @@ test "enum sizes" { assert(@sizeOf(ValueCount257) == 2); } } + +const Small2 = enum (u2) { + One, + Two, +}; +const Small = enum (u2) { + One, + Two, + Three, + Four, +}; + +test "set enum tag type" { + { + var x = Small.One; + x = Small.Two; + comptime assert(@EnumTagType(Small) == u2); + } + { + var x = Small2.One; + x = Small2.Two; + comptime assert(@EnumTagType(Small2) == u2); + } +} diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 3ef4a63e5f..9b5ec6a6d6 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2362,4 +2362,32 @@ pub fn addCases(cases: &tests.CompileErrorContext) { ".tmp_source.zig:4:25: error: aoeu", ".tmp_source.zig:1:36: note: called from here", ".tmp_source.zig:12:20: note: referenced here"); + + cases.add("specify enum tag type that is too small", + \\const Small = enum (u2) { + \\ One, + \\ Two, + \\ Three, + \\ Four, + \\ Five, + \\}; + \\ + \\export fn entry() { + \\ var x = Small.One; + \\} + , + ".tmp_source.zig:1:20: error: 'u2' too small to hold all bits; must be at least 'u3'"); + + cases.add("specify non-integer enum tag type", + \\const Small = enum (f32) { + \\ One, + \\ Two, + \\ Three, + \\}; + \\ + \\export fn entry() { + \\ var x = Small.One; + \\} + , + ".tmp_source.zig:1:20: error: expected integer, found 'f32'"); } From 264c86853b714482d006baa38482a6f7d55e8d94 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 1 Dec 2017 00:34:29 -0500 Subject: [PATCH 09/32] packed structs can have enums with explicit tag types See #305 --- src/analyze.cpp | 4 ++- src/codegen.cpp | 9 +++++- test/cases/enum.zig | 70 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 2 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 33d6ccdc39..235aeea682 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1536,7 +1536,6 @@ static bool type_allowed_in_packed_struct(TypeTableEntry *type_entry) { case TypeTableEntryIdNullLit: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdPureError: - case TypeTableEntryIdEnum: case TypeTableEntryIdEnumTag: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: @@ -1560,6 +1559,9 @@ static bool type_allowed_in_packed_struct(TypeTableEntry *type_entry) { TypeTableEntry *child_type = type_entry->data.maybe.child_type; 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; } zig_unreachable(); } diff --git a/src/codegen.cpp b/src/codegen.cpp index da94dd4bcd..a5f8b85e22 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3762,7 +3762,6 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con case TypeTableEntryIdNullLit: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdPureError: - case TypeTableEntryIdEnum: case TypeTableEntryIdEnumTag: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: @@ -3773,6 +3772,13 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con zig_unreachable(); case TypeTableEntryIdBool: 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); + } case TypeTableEntryIdInt: { LLVMValueRef int_val = gen_const_val(g, const_val); @@ -3814,6 +3820,7 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con } return val; } + } zig_unreachable(); } diff --git a/test/cases/enum.zig b/test/cases/enum.zig index a56ed398b8..1b2ff10a9b 100644 --- a/test/cases/enum.zig +++ b/test/cases/enum.zig @@ -214,3 +214,73 @@ test "set enum tag type" { comptime assert(@EnumTagType(Small2) == u2); } } + + +const A = enum (u3) { + One, + Two, + Three, + Four, + One2, + Two2, + Three2, + Four2, +}; + +const B = enum (u3) { + One3, + Two3, + Three3, + Four3, + One23, + Two23, + Three23, + Four23, +}; + +const C = enum (u2) { + One4, + Two4, + Three4, + Four4, +}; + +const BitFieldOfEnums = packed struct { + a: A, + b: B, + c: C, +}; + +const bit_field_1 = BitFieldOfEnums { + .a = A.Two, + .b = B.Three3, + .c = C.Four4, +}; + +test "bit field access with enum fields" { + var data = bit_field_1; + assert(getA(&data) == A.Two); + assert(getB(&data) == B.Three3); + assert(getC(&data) == C.Four4); + comptime assert(@sizeOf(BitFieldOfEnums) == 1); + + data.b = B.Four3; + assert(data.b == B.Four3); + + data.a = A.Three; + assert(data.a == A.Three); + assert(data.b == B.Four3); +} + +fn getA(data: &const BitFieldOfEnums) -> A { + return data.a; +} + +fn getB(data: &const BitFieldOfEnums) -> B { + return data.b; +} + +fn getC(data: &const BitFieldOfEnums) -> C { + return data.c; +} + From b4120423a5f0785e950739ab8c1324691971d680 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 1 Dec 2017 00:37:15 -0500 Subject: [PATCH 10/32] translate-c: only emit enum tag type if not c_int or c_uint --- src/translate_c.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/translate_c.cpp b/src/translate_c.cpp index 2bba498720..5082c37a61 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -3434,7 +3434,12 @@ static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) { AstNode *enum_node = trans_create_node(c, NodeTypeContainerDecl); enum_node->data.container_decl.kind = ContainerKindEnum; enum_node->data.container_decl.layout = ContainerLayoutExtern; - if (!c_is_builtin_type(c, enum_decl->getIntegerType(), BuiltinType::UInt)) { + // TODO only emit this tag type if the enum tag type is not the default. + // I don't know what the default is, need to figure out how clang is deciding. + // it appears to at least be different across gcc/msvc + if (!c_is_builtin_type(c, enum_decl->getIntegerType(), BuiltinType::UInt) && + !c_is_builtin_type(c, enum_decl->getIntegerType(), BuiltinType::Int)) + { enum_node->data.container_decl.init_arg_expr = tag_int_type; } From 77b530b50aedd1cf9943e1d4fdd97a364fe9a921 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 1 Dec 2017 11:59:14 -0500 Subject: [PATCH 11/32] updated embedded LLD to 5.0.1rc2 --- deps/lld-prebuilt/lld/Config/Version.inc | 4 +- deps/lld/COFF/Driver.cpp | 6 +- deps/lld/ELF/LinkerScript.cpp | 2 +- deps/lld/ELF/SyntheticSections.cpp | 12 +++- deps/lld/ELF/SyntheticSections.h | 3 +- deps/lld/lib/ReaderWriter/MachO/ArchHandler.h | 4 -- .../ReaderWriter/MachO/ArchHandler_arm.cpp | 4 -- .../ReaderWriter/MachO/ArchHandler_arm64.cpp | 4 -- .../ReaderWriter/MachO/ArchHandler_x86.cpp | 4 -- .../ReaderWriter/MachO/ArchHandler_x86_64.cpp | 6 +- .../MachO/MachONormalizedFileFromAtoms.cpp | 57 ----------------- .../test/ELF/eh-frame-padding-no-rosegment.s | 64 +++++++++++++++++++ deps/lld/test/mach-o/lazy-bind-x86_64.yaml | 4 +- 13 files changed, 85 insertions(+), 89 deletions(-) create mode 100644 deps/lld/test/ELF/eh-frame-padding-no-rosegment.s diff --git a/deps/lld-prebuilt/lld/Config/Version.inc b/deps/lld-prebuilt/lld/Config/Version.inc index 2fb8a16222..a3f6f44588 100644 --- a/deps/lld-prebuilt/lld/Config/Version.inc +++ b/deps/lld-prebuilt/lld/Config/Version.inc @@ -1,5 +1,5 @@ -#define LLD_VERSION 5.0.0 -#define LLD_VERSION_STRING "5.0.0" +#define LLD_VERSION 5.0.1 +#define LLD_VERSION_STRING "5.0.1" #define LLD_VERSION_MAJOR 5 #define LLD_VERSION_MINOR 0 #define LLD_REVISION_STRING "" diff --git a/deps/lld/COFF/Driver.cpp b/deps/lld/COFF/Driver.cpp index 0dabca6e37..854c3e6909 100644 --- a/deps/lld/COFF/Driver.cpp +++ b/deps/lld/COFF/Driver.cpp @@ -61,7 +61,6 @@ bool link(ArrayRef Args, raw_ostream &Diag) { (ErrorOS == &llvm::errs() && Process::StandardErrHasColors()); Driver = make(); Driver->link(Args); - freeArena(); return !ErrorCount; } @@ -1031,7 +1030,7 @@ void LinkerDriver::link(ArrayRef ArgsArr) { if (!Args.hasArgNoClaim(OPT_INPUT)) { fixupExports(); createImportLibrary(/*AsLib=*/true); - return; + exit(0); } // Handle /delayload @@ -1173,6 +1172,9 @@ void LinkerDriver::link(ArrayRef ArgsArr) { // Write the result. writeResult(&Symtab); + + // Call exit to avoid calling destructors. + exit(0); } } // namespace coff diff --git a/deps/lld/ELF/LinkerScript.cpp b/deps/lld/ELF/LinkerScript.cpp index 614f5e2c8b..8bdbd8db20 100644 --- a/deps/lld/ELF/LinkerScript.cpp +++ b/deps/lld/ELF/LinkerScript.cpp @@ -751,7 +751,7 @@ void LinkerScript::adjustSectionsAfterSorting() { if (auto *Cmd = dyn_cast(Base)) { Cmd->MemRegion = findMemoryRegion(Cmd); // Handle align (e.g. ".foo : ALIGN(16) { ... }"). - if (Cmd->AlignExpr && Cmd->Sec) + if (Cmd->AlignExpr) Cmd->Sec->updateAlignment(Cmd->AlignExpr().getValue()); } } diff --git a/deps/lld/ELF/SyntheticSections.cpp b/deps/lld/ELF/SyntheticSections.cpp index 4bbec4ab34..a67b039ddf 100644 --- a/deps/lld/ELF/SyntheticSections.cpp +++ b/deps/lld/ELF/SyntheticSections.cpp @@ -427,10 +427,11 @@ CieRecord *EhFrameSection::addCie(EhSectionPiece &Piece, &Sec->template getFile()->getRelocTargetSym(Rels[FirstRelI]); // Search for an existing CIE by CIE contents/relocation target pair. - CieRecord *Cie = &CieMap[{Piece.data(), Personality}]; + CieRecord *&Cie = CieMap[{Piece.data(), Personality}]; // If not found, create a new one. - if (Cie->Piece == nullptr) { + if (!Cie) { + Cie = make(); Cie->Piece = &Piece; Cies.push_back(Cie); } @@ -522,9 +523,14 @@ template static void writeCieFde(uint8_t *Buf, ArrayRef D) { memcpy(Buf, D.data(), D.size()); + size_t Aligned = alignTo(D.size(), sizeof(typename ELFT::uint)); + + // Zero-clear trailing padding if it exists. + memset(Buf + D.size(), 0, Aligned - D.size()); + // Fix the size field. -4 since size does not include the size field itself. const endianness E = ELFT::TargetEndianness; - write32(Buf, alignTo(D.size(), sizeof(typename ELFT::uint)) - 4); + write32(Buf, Aligned - 4); } template void EhFrameSection::finalizeContents() { diff --git a/deps/lld/ELF/SyntheticSections.h b/deps/lld/ELF/SyntheticSections.h index ddd8ca99a6..ccf021ec95 100644 --- a/deps/lld/ELF/SyntheticSections.h +++ b/deps/lld/ELF/SyntheticSections.h @@ -103,7 +103,8 @@ private: std::vector Cies; // CIE records are uniquified by their contents and personality functions. - llvm::DenseMap, SymbolBody *>, CieRecord> CieMap; + llvm::DenseMap, SymbolBody *>, CieRecord *> + CieMap; }; class GotSection : public SyntheticSection { diff --git a/deps/lld/lib/ReaderWriter/MachO/ArchHandler.h b/deps/lld/lib/ReaderWriter/MachO/ArchHandler.h index 6028006ca9..70a63bd100 100644 --- a/deps/lld/lib/ReaderWriter/MachO/ArchHandler.h +++ b/deps/lld/lib/ReaderWriter/MachO/ArchHandler.h @@ -112,10 +112,6 @@ public: /// info in final executables. virtual bool isLazyPointer(const Reference &); - /// Reference from an __stub_helper entry to the required offset of the - /// lazy bind commands. - virtual Reference::KindValue lazyImmediateLocationKind() = 0; - /// Returns true if the specified relocation is paired to the next relocation. virtual bool isPairedReloc(const normalized::Relocation &) = 0; diff --git a/deps/lld/lib/ReaderWriter/MachO/ArchHandler_arm.cpp b/deps/lld/lib/ReaderWriter/MachO/ArchHandler_arm.cpp index 2f663c660f..7d1544854c 100644 --- a/deps/lld/lib/ReaderWriter/MachO/ArchHandler_arm.cpp +++ b/deps/lld/lib/ReaderWriter/MachO/ArchHandler_arm.cpp @@ -67,10 +67,6 @@ public: return invalid; } - Reference::KindValue lazyImmediateLocationKind() override { - return lazyImmediateLocation; - } - Reference::KindValue pointerKind() override { return invalid; } diff --git a/deps/lld/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp b/deps/lld/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp index b9c815c5a3..10360b5c6d 100644 --- a/deps/lld/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp +++ b/deps/lld/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp @@ -127,10 +127,6 @@ public: return pointer64; } - Reference::KindValue lazyImmediateLocationKind() override { - return lazyImmediateLocation; - } - uint32_t dwarfCompactUnwindType() override { return 0x03000000; } diff --git a/deps/lld/lib/ReaderWriter/MachO/ArchHandler_x86.cpp b/deps/lld/lib/ReaderWriter/MachO/ArchHandler_x86.cpp index a2c6809272..2272bff65c 100644 --- a/deps/lld/lib/ReaderWriter/MachO/ArchHandler_x86.cpp +++ b/deps/lld/lib/ReaderWriter/MachO/ArchHandler_x86.cpp @@ -70,10 +70,6 @@ public: return delta32; } - Reference::KindValue lazyImmediateLocationKind() override { - return lazyImmediateLocation; - } - Reference::KindValue unwindRefToEhFrameKind() override { return invalid; } diff --git a/deps/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp b/deps/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp index efe23abb91..d687ca5de5 100644 --- a/deps/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp +++ b/deps/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp @@ -116,10 +116,6 @@ public: return unwindFDEToFunction; } - Reference::KindValue lazyImmediateLocationKind() override { - return lazyImmediateLocation; - } - Reference::KindValue unwindRefToEhFrameKind() override { return unwindInfoToEhFrame; } @@ -621,7 +617,7 @@ void ArchHandler_x86_64::applyFixupFinal( // Fall into llvm_unreachable(). break; } - return; + llvm_unreachable("invalid x86_64 Reference Kind"); } void ArchHandler_x86_64::applyFixupRelocatable(const Reference &ref, diff --git a/deps/lld/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp b/deps/lld/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp index f2e5ed7816..e58e3d2e7a 100644 --- a/deps/lld/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp +++ b/deps/lld/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp @@ -172,8 +172,6 @@ private: SymbolScope &symbolScope); void appendSection(SectionInfo *si, NormalizedFile &file); uint32_t sectionIndexForAtom(const Atom *atom); - void fixLazyReferenceImm(const DefinedAtom *atom, uint32_t offset, - NormalizedFile &file); typedef llvm::DenseMap AtomToIndex; struct AtomAndIndex { const Atom *atom; uint32_t index; SymbolScope scope; }; @@ -1425,8 +1423,6 @@ void Util::addRebaseAndBindingInfo(const lld::File &atomFile, uint8_t segmentIndex; uint64_t segmentStartAddr; - uint32_t offsetInBindInfo = 0; - for (SectionInfo *sect : _sectionInfos) { segIndexForSection(sect, segmentIndex, segmentStartAddr); for (const AtomInfo &info : sect->atomsAndOffsets) { @@ -1471,59 +1467,6 @@ void Util::addRebaseAndBindingInfo(const lld::File &atomFile, bind.symbolName = targ->name(); bind.addend = ref->addend(); nFile.lazyBindingInfo.push_back(bind); - - // Now that we know the segmentOffset and the ordinal attribute, - // we can fix the helper's code - - fixLazyReferenceImm(atom, offsetInBindInfo, nFile); - - // 5 bytes for opcodes + variable sizes (target name + \0 and offset - // encode's size) - offsetInBindInfo += - 6 + targ->name().size() + llvm::getULEB128Size(bind.segOffset); - if (bind.ordinal > BIND_IMMEDIATE_MASK) - offsetInBindInfo += llvm::getULEB128Size(bind.ordinal); - } - } - } - } -} - -void Util::fixLazyReferenceImm(const DefinedAtom *atom, uint32_t offset, - NormalizedFile &file) { - for (const auto &ref : *atom) { - const DefinedAtom *da = dyn_cast(ref->target()); - if (da == nullptr) - return; - - const Reference *helperRef = nullptr; - for (const Reference *hr : *da) { - if (hr->kindValue() == _archHandler.lazyImmediateLocationKind()) { - helperRef = hr; - break; - } - } - if (helperRef == nullptr) - continue; - - // TODO: maybe get the fixed atom content from _archHandler ? - for (SectionInfo *sectInfo : _sectionInfos) { - for (const AtomInfo &atomInfo : sectInfo->atomsAndOffsets) { - if (atomInfo.atom == helperRef->target()) { - auto sectionContent = - file.sections[sectInfo->normalizedSectionIndex].content; - uint8_t *rawb = - file.ownedAllocations.Allocate(sectionContent.size()); - llvm::MutableArrayRef newContent{rawb, - sectionContent.size()}; - std::copy(sectionContent.begin(), sectionContent.end(), - newContent.begin()); - llvm::support::ulittle32_t *loc = - reinterpret_cast( - &newContent[atomInfo.offsetInSection + - helperRef->offsetInAtom()]); - *loc = offset; - file.sections[sectInfo->normalizedSectionIndex].content = newContent; } } } diff --git a/deps/lld/test/ELF/eh-frame-padding-no-rosegment.s b/deps/lld/test/ELF/eh-frame-padding-no-rosegment.s new file mode 100644 index 0000000000..951fed0a56 --- /dev/null +++ b/deps/lld/test/ELF/eh-frame-padding-no-rosegment.s @@ -0,0 +1,64 @@ +// REQUIRES: x86 + +.cfi_startproc +.cfi_personality 0x1b, bar +.cfi_endproc + +.global bar +.hidden bar +bar: + +// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o + +// Check the size of the CIE (0x18 + 4) and FDE (0x10 + 4) +// RUN: llvm-readobj -s -section-data %t.o | FileCheck --check-prefix=OBJ %s + +// OBJ: Name: .eh_frame +// OBJ-NEXT: Type: +// OBJ-NEXT: Flags [ +// OBJ-NEXT: SHF_ALLOC +// OBJ-NEXT: ] +// OBJ-NEXT: Address: +// OBJ-NEXT: Offset: +// OBJ-NEXT: Size: +// OBJ-NEXT: Link: +// OBJ-NEXT: Info: +// OBJ-NEXT: AddressAlignment: +// OBJ-NEXT: EntrySize: +// OBJ-NEXT: SectionData ( +// OBJ-NEXT: 0000: 18000000 00000000 017A5052 00017810 +// OBJ-NEXT: 0010: 061B0000 00001B0C 07089001 10000000 +// OBJ-NEXT: 0020: 20000000 00000000 00000000 00000000 +// OBJ-NEXT: ) + +// RUN: ld.lld %t.o -no-rosegment -o %t -shared + +// Check that .eh_frame is in the same segment as .text +// RUN: llvm-readobj -l --elf-output-style=GNU %t | FileCheck --check-prefix=PHDR %s + +// PHDR: Segment Sections +// PHDR: .text +// PHDR-SAME: .eh_frame + +// Check that the CIE and FDE are padded with 0x00 and not 0xCC when the +// .eh_frame section is placed in the executable segment +// RUN: llvm-readobj -s -section-data %t | FileCheck %s + +// CHECK: Name: .eh_frame +// CHECK-NEXT: Type: +// CHECK-NEXT: Flags +// CHECK-NEXT: SHF_ALLOC +// CHECK-NEXT: ] +// CHECK-NEXT: Address: +// CHECK-NEXT: Offset: +// CHECK-NEXT: Size: +// CHECK-NEXT: Link: +// CHECK-NEXT: Info: +// CHECK-NEXT: AddressAlignment: +// CHECK-NEXT: EntrySize: +// CHECK-NEXT: SectionData ( +// CHECK-NEXT: 0000: 1C000000 00000000 017A5052 00017810 +// CHECK-NEXT: 0010: 061BBEFF FFFF1B0C 07089001 00000000 +// CHECK-NEXT: 0020: 14000000 24000000 A8FFFFFF 00000000 +// CHECK-NEXT: 0030: 00000000 00000000 +// CHECK-NEXT: ) diff --git a/deps/lld/test/mach-o/lazy-bind-x86_64.yaml b/deps/lld/test/mach-o/lazy-bind-x86_64.yaml index 1322719e5f..5c588c5719 100644 --- a/deps/lld/test/mach-o/lazy-bind-x86_64.yaml +++ b/deps/lld/test/mach-o/lazy-bind-x86_64.yaml @@ -80,8 +80,8 @@ undefined-symbols: # CHECK-HELPERS:Disassembly of section __TEXT,__stub_helper: # CHECK-HELPERS: 68 00 00 00 00 pushq $0 -# CHECK-HELPERS: 68 0b 00 00 00 pushq $11 -# CHECK-HELPERS: 68 16 00 00 00 pushq $22 +# CHECK-HELPERS: 68 10 00 00 00 pushq $16 +# CHECK-HELPERS: 68 20 00 00 00 pushq $32 # Make sure the stub helper is correctly aligned # CHECK-DYLIBS: sectname __stub_helper From 9ea23272fac7f4580d29f7ee557108883f127a5d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 1 Dec 2017 12:06:33 -0500 Subject: [PATCH 12/32] LLD patch: COFF: better behavior when using as a library This applies de776439b61fb71c1256ad86238799c758c66048 from the LLVM git monorepo to the embedded LLD. --- deps/lld/COFF/Config.h | 1 + deps/lld/COFF/Driver.cpp | 16 ++++++++++------ deps/lld/COFF/Error.cpp | 5 +++-- deps/lld/COFF/Error.h | 2 ++ deps/lld/include/lld/Driver/Driver.h | 2 +- deps/lld/tools/lld/lld.cpp | 2 +- 6 files changed, 18 insertions(+), 10 deletions(-) diff --git a/deps/lld/COFF/Config.h b/deps/lld/COFF/Config.h index 7f8259d016..ffbd0715cf 100644 --- a/deps/lld/COFF/Config.h +++ b/deps/lld/COFF/Config.h @@ -157,6 +157,7 @@ struct Configuration { uint32_t MinorImageVersion = 0; uint32_t MajorOSVersion = 6; uint32_t MinorOSVersion = 0; + bool CanExitEarly = false; bool DynamicBase = true; bool NxCompat = true; bool AllowIsolation = true; diff --git a/deps/lld/COFF/Driver.cpp b/deps/lld/COFF/Driver.cpp index 854c3e6909..868ff0aa1e 100644 --- a/deps/lld/COFF/Driver.cpp +++ b/deps/lld/COFF/Driver.cpp @@ -52,15 +52,22 @@ BumpPtrAllocator BAlloc; StringSaver Saver{BAlloc}; std::vector SpecificAllocBase::Instances; -bool link(ArrayRef Args, raw_ostream &Diag) { +bool link(ArrayRef Args, bool CanExitEarly, raw_ostream &Diag) { ErrorCount = 0; ErrorOS = &Diag; Config = make(); Config->Argv = {Args.begin(), Args.end()}; Config->ColorDiagnostics = (ErrorOS == &llvm::errs() && Process::StandardErrHasColors()); + Config->CanExitEarly = CanExitEarly; Driver = make(); Driver->link(Args); + + // Call exit() if we can to avoid calling destructors. + if (CanExitEarly) + exitLld(ErrorCount ? 1 : 0); + + freeArena(); return !ErrorCount; } @@ -1030,7 +1037,7 @@ void LinkerDriver::link(ArrayRef ArgsArr) { if (!Args.hasArgNoClaim(OPT_INPUT)) { fixupExports(); createImportLibrary(/*AsLib=*/true); - exit(0); + return; } // Handle /delayload @@ -1122,7 +1129,7 @@ void LinkerDriver::link(ArrayRef ArgsArr) { // This is useful because MSVC link.exe can generate complete PDBs. if (Args.hasArg(OPT_msvclto)) { invokeMSVC(Args); - exit(0); + return; } // Do LTO by compiling bitcode input files to a set of native COFF files then @@ -1172,9 +1179,6 @@ void LinkerDriver::link(ArrayRef ArgsArr) { // Write the result. writeResult(&Symtab); - - // Call exit to avoid calling destructors. - exit(0); } } // namespace coff diff --git a/deps/lld/COFF/Error.cpp b/deps/lld/COFF/Error.cpp index 34abc280f6..550d9b9696 100644 --- a/deps/lld/COFF/Error.cpp +++ b/deps/lld/COFF/Error.cpp @@ -32,7 +32,7 @@ namespace coff { uint64_t ErrorCount; raw_ostream *ErrorOS; -static LLVM_ATTRIBUTE_NORETURN void exitLld(int Val) { +LLVM_ATTRIBUTE_NORETURN void exitLld(int Val) { // Dealloc/destroy ManagedStatic variables before calling // _exit(). In a non-LTO build, this is a nop. In an LTO // build allows us to get the output of -time-passes. @@ -78,7 +78,8 @@ void error(const Twine &Msg) { print("error: ", raw_ostream::RED); *ErrorOS << "too many errors emitted, stopping now" << " (use /ERRORLIMIT:0 to see all errors)\n"; - exitLld(1); + if (Config->CanExitEarly) + exitLld(1); } ++ErrorCount; diff --git a/deps/lld/COFF/Error.h b/deps/lld/COFF/Error.h index e1e4c1e521..1c1e2beab5 100644 --- a/deps/lld/COFF/Error.h +++ b/deps/lld/COFF/Error.h @@ -27,6 +27,8 @@ LLVM_ATTRIBUTE_NORETURN void fatal(const Twine &Msg); LLVM_ATTRIBUTE_NORETURN void fatal(std::error_code EC, const Twine &Prefix); LLVM_ATTRIBUTE_NORETURN void fatal(llvm::Error &Err, const Twine &Prefix); +LLVM_ATTRIBUTE_NORETURN void exitLld(int Val); + template T check(ErrorOr V, const Twine &Prefix) { if (auto EC = V.getError()) fatal(EC, Prefix); diff --git a/deps/lld/include/lld/Driver/Driver.h b/deps/lld/include/lld/Driver/Driver.h index 4ba0994e88..02c3c059d3 100644 --- a/deps/lld/include/lld/Driver/Driver.h +++ b/deps/lld/include/lld/Driver/Driver.h @@ -15,7 +15,7 @@ namespace lld { namespace coff { -bool link(llvm::ArrayRef Args, +bool link(llvm::ArrayRef Args, bool CanExitEarly, llvm::raw_ostream &Diag = llvm::errs()); } diff --git a/deps/lld/tools/lld/lld.cpp b/deps/lld/tools/lld/lld.cpp index 09f8079010..aa81aa9712 100644 --- a/deps/lld/tools/lld/lld.cpp +++ b/deps/lld/tools/lld/lld.cpp @@ -103,7 +103,7 @@ int main(int Argc, const char **Argv) { case Gnu: return !elf::link(Args, true); case WinLink: - return !coff::link(Args); + return !coff::link(Args, true); case Darwin: return !mach_o::link(Args); default: From fa45407e78c7a20281bf063f659d74f86c127ea1 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 1 Dec 2017 12:08:16 -0500 Subject: [PATCH 13/32] LLD patch: Fix for LLD on linker scripts with empty sections This reapplies 569cf286ff79a10126b9f20f39fa8c64df9b8b25 to the embedded LLD. --- deps/lld/ELF/LinkerScript.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/lld/ELF/LinkerScript.cpp b/deps/lld/ELF/LinkerScript.cpp index 8bdbd8db20..614f5e2c8b 100644 --- a/deps/lld/ELF/LinkerScript.cpp +++ b/deps/lld/ELF/LinkerScript.cpp @@ -751,7 +751,7 @@ void LinkerScript::adjustSectionsAfterSorting() { if (auto *Cmd = dyn_cast(Base)) { Cmd->MemRegion = findMemoryRegion(Cmd); // Handle align (e.g. ".foo : ALIGN(16) { ... }"). - if (Cmd->AlignExpr) + if (Cmd->AlignExpr && Cmd->Sec) Cmd->Sec->updateAlignment(Cmd->AlignExpr().getValue()); } } From ddca67a2b94f68985789fc8254fd1326e26269f6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 1 Dec 2017 12:09:55 -0500 Subject: [PATCH 14/32] LLD patch: workaround for buggy MACH-O code This reapplies 1a1414fc42c7beb25b6de4134d99884ea6544b57 to the embedded LLD. --- deps/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/deps/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp b/deps/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp index d687ca5de5..07958da4b9 100644 --- a/deps/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp +++ b/deps/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp @@ -617,7 +617,6 @@ void ArchHandler_x86_64::applyFixupFinal( // Fall into llvm_unreachable(). break; } - llvm_unreachable("invalid x86_64 Reference Kind"); } void ArchHandler_x86_64::applyFixupRelocatable(const Reference &ref, From a206ef34bbbc46017e471063a4a1832c1ddafb0a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 1 Dec 2017 12:11:55 -0500 Subject: [PATCH 15/32] LLD patch: Fix the ASM code generated for __stub_helpers section This applies 93ca847862af07632197dcf2d8a68b9b27a26d7a from the llvm-project git monorepo to the embedded LLD. --- deps/lld/lib/ReaderWriter/MachO/ArchHandler.h | 4 ++ .../ReaderWriter/MachO/ArchHandler_arm.cpp | 4 ++ .../ReaderWriter/MachO/ArchHandler_arm64.cpp | 4 ++ .../ReaderWriter/MachO/ArchHandler_x86.cpp | 4 ++ .../ReaderWriter/MachO/ArchHandler_x86_64.cpp | 4 ++ .../MachO/MachONormalizedFileFromAtoms.cpp | 57 +++++++++++++++++++ deps/lld/test/mach-o/lazy-bind-x86_64.yaml | 4 +- 7 files changed, 79 insertions(+), 2 deletions(-) diff --git a/deps/lld/lib/ReaderWriter/MachO/ArchHandler.h b/deps/lld/lib/ReaderWriter/MachO/ArchHandler.h index 70a63bd100..6028006ca9 100644 --- a/deps/lld/lib/ReaderWriter/MachO/ArchHandler.h +++ b/deps/lld/lib/ReaderWriter/MachO/ArchHandler.h @@ -112,6 +112,10 @@ public: /// info in final executables. virtual bool isLazyPointer(const Reference &); + /// Reference from an __stub_helper entry to the required offset of the + /// lazy bind commands. + virtual Reference::KindValue lazyImmediateLocationKind() = 0; + /// Returns true if the specified relocation is paired to the next relocation. virtual bool isPairedReloc(const normalized::Relocation &) = 0; diff --git a/deps/lld/lib/ReaderWriter/MachO/ArchHandler_arm.cpp b/deps/lld/lib/ReaderWriter/MachO/ArchHandler_arm.cpp index 7d1544854c..2f663c660f 100644 --- a/deps/lld/lib/ReaderWriter/MachO/ArchHandler_arm.cpp +++ b/deps/lld/lib/ReaderWriter/MachO/ArchHandler_arm.cpp @@ -67,6 +67,10 @@ public: return invalid; } + Reference::KindValue lazyImmediateLocationKind() override { + return lazyImmediateLocation; + } + Reference::KindValue pointerKind() override { return invalid; } diff --git a/deps/lld/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp b/deps/lld/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp index 10360b5c6d..b9c815c5a3 100644 --- a/deps/lld/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp +++ b/deps/lld/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp @@ -127,6 +127,10 @@ public: return pointer64; } + Reference::KindValue lazyImmediateLocationKind() override { + return lazyImmediateLocation; + } + uint32_t dwarfCompactUnwindType() override { return 0x03000000; } diff --git a/deps/lld/lib/ReaderWriter/MachO/ArchHandler_x86.cpp b/deps/lld/lib/ReaderWriter/MachO/ArchHandler_x86.cpp index 2272bff65c..a2c6809272 100644 --- a/deps/lld/lib/ReaderWriter/MachO/ArchHandler_x86.cpp +++ b/deps/lld/lib/ReaderWriter/MachO/ArchHandler_x86.cpp @@ -70,6 +70,10 @@ public: return delta32; } + Reference::KindValue lazyImmediateLocationKind() override { + return lazyImmediateLocation; + } + Reference::KindValue unwindRefToEhFrameKind() override { return invalid; } diff --git a/deps/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp b/deps/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp index 07958da4b9..b207c85523 100644 --- a/deps/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp +++ b/deps/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp @@ -116,6 +116,10 @@ public: return unwindFDEToFunction; } + Reference::KindValue lazyImmediateLocationKind() override { + return lazyImmediateLocation; + } + Reference::KindValue unwindRefToEhFrameKind() override { return unwindInfoToEhFrame; } diff --git a/deps/lld/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp b/deps/lld/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp index e58e3d2e7a..f2e5ed7816 100644 --- a/deps/lld/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp +++ b/deps/lld/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp @@ -172,6 +172,8 @@ private: SymbolScope &symbolScope); void appendSection(SectionInfo *si, NormalizedFile &file); uint32_t sectionIndexForAtom(const Atom *atom); + void fixLazyReferenceImm(const DefinedAtom *atom, uint32_t offset, + NormalizedFile &file); typedef llvm::DenseMap AtomToIndex; struct AtomAndIndex { const Atom *atom; uint32_t index; SymbolScope scope; }; @@ -1423,6 +1425,8 @@ void Util::addRebaseAndBindingInfo(const lld::File &atomFile, uint8_t segmentIndex; uint64_t segmentStartAddr; + uint32_t offsetInBindInfo = 0; + for (SectionInfo *sect : _sectionInfos) { segIndexForSection(sect, segmentIndex, segmentStartAddr); for (const AtomInfo &info : sect->atomsAndOffsets) { @@ -1467,6 +1471,59 @@ void Util::addRebaseAndBindingInfo(const lld::File &atomFile, bind.symbolName = targ->name(); bind.addend = ref->addend(); nFile.lazyBindingInfo.push_back(bind); + + // Now that we know the segmentOffset and the ordinal attribute, + // we can fix the helper's code + + fixLazyReferenceImm(atom, offsetInBindInfo, nFile); + + // 5 bytes for opcodes + variable sizes (target name + \0 and offset + // encode's size) + offsetInBindInfo += + 6 + targ->name().size() + llvm::getULEB128Size(bind.segOffset); + if (bind.ordinal > BIND_IMMEDIATE_MASK) + offsetInBindInfo += llvm::getULEB128Size(bind.ordinal); + } + } + } + } +} + +void Util::fixLazyReferenceImm(const DefinedAtom *atom, uint32_t offset, + NormalizedFile &file) { + for (const auto &ref : *atom) { + const DefinedAtom *da = dyn_cast(ref->target()); + if (da == nullptr) + return; + + const Reference *helperRef = nullptr; + for (const Reference *hr : *da) { + if (hr->kindValue() == _archHandler.lazyImmediateLocationKind()) { + helperRef = hr; + break; + } + } + if (helperRef == nullptr) + continue; + + // TODO: maybe get the fixed atom content from _archHandler ? + for (SectionInfo *sectInfo : _sectionInfos) { + for (const AtomInfo &atomInfo : sectInfo->atomsAndOffsets) { + if (atomInfo.atom == helperRef->target()) { + auto sectionContent = + file.sections[sectInfo->normalizedSectionIndex].content; + uint8_t *rawb = + file.ownedAllocations.Allocate(sectionContent.size()); + llvm::MutableArrayRef newContent{rawb, + sectionContent.size()}; + std::copy(sectionContent.begin(), sectionContent.end(), + newContent.begin()); + llvm::support::ulittle32_t *loc = + reinterpret_cast( + &newContent[atomInfo.offsetInSection + + helperRef->offsetInAtom()]); + *loc = offset; + file.sections[sectInfo->normalizedSectionIndex].content = newContent; } } } diff --git a/deps/lld/test/mach-o/lazy-bind-x86_64.yaml b/deps/lld/test/mach-o/lazy-bind-x86_64.yaml index 5c588c5719..1322719e5f 100644 --- a/deps/lld/test/mach-o/lazy-bind-x86_64.yaml +++ b/deps/lld/test/mach-o/lazy-bind-x86_64.yaml @@ -80,8 +80,8 @@ undefined-symbols: # CHECK-HELPERS:Disassembly of section __TEXT,__stub_helper: # CHECK-HELPERS: 68 00 00 00 00 pushq $0 -# CHECK-HELPERS: 68 10 00 00 00 pushq $16 -# CHECK-HELPERS: 68 20 00 00 00 pushq $32 +# CHECK-HELPERS: 68 0b 00 00 00 pushq $11 +# CHECK-HELPERS: 68 16 00 00 00 pushq $22 # Make sure the stub helper is correctly aligned # CHECK-DYLIBS: sectname __stub_helper From bdd5241615bf41b69d3e12f3b6796bd796fb90f2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 1 Dec 2017 12:15:19 -0500 Subject: [PATCH 16/32] update c_headers to llvm 5.0.1rc2 --- c_headers/avx512fintrin.h | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/c_headers/avx512fintrin.h b/c_headers/avx512fintrin.h index 4ce6945311..4b66acc02f 100644 --- a/c_headers/avx512fintrin.h +++ b/c_headers/avx512fintrin.h @@ -267,21 +267,16 @@ _mm512_maskz_set1_epi32(__mmask16 __M, int __A) __M); } +#ifdef __x86_64__ static __inline __m512i __DEFAULT_FN_ATTRS _mm512_maskz_set1_epi64(__mmask8 __M, long long __A) { -#ifdef __x86_64__ return (__m512i) __builtin_ia32_pbroadcastq512_gpr_mask (__A, (__v8di) _mm512_setzero_si512 (), __M); -#else - return (__m512i) __builtin_ia32_pbroadcastq512_mem_mask (__A, - (__v8di) - _mm512_setzero_si512 (), - __M); -#endif } +#endif static __inline __m512 __DEFAULT_FN_ATTRS _mm512_setzero_ps(void) From cf96b6f87b5feaa699f0d15b1525d53a374b6227 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 1 Dec 2017 13:44:28 -0500 Subject: [PATCH 17/32] update to LLVM 5.0.1rc2 --- CMakeLists.txt | 11 ++++++++--- src/zig_llvm.cpp | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 72c480cd40..d8e2d9ff7a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,15 +47,20 @@ option(ZIG_TEST_COVERAGE "Build Zig with test coverage instrumentation" OFF) option(ZIG_FORCE_EXTERNAL_LLD "If your system has the LLD patches use it instead of the embedded LLD" OFF) find_package(llvm) -include_directories(${LLVM_INCLUDE_DIRS}) - find_package(clang) -include_directories(${CLANG_INCLUDE_DIRS}) if(ZIG_FORCE_EXTERNAL_LLD) find_package(lld) + include_directories(${LLVM_INCLUDE_DIRS}) include_directories(${LLD_INCLUDE_DIRS}) + include_directories(${CLANG_INCLUDE_DIRS}) else() + # This goes first so that we find embedded LLD instead + # of system LLD. + include_directories("${CMAKE_SOURCE_DIR}/deps/lld/include") + + include_directories(${LLVM_INCLUDE_DIRS}) + include_directories(${CLANG_INCLUDE_DIRS}) set(EMBEDDED_LLD_LIB_SOURCES "${CMAKE_SOURCE_DIR}/deps/lld/lib/Driver/DarwinLdDriver.cpp" "${CMAKE_SOURCE_DIR}/deps/lld/lib/Config/Version.cpp" diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index 658de77b31..fa352147cc 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -789,7 +789,7 @@ bool ZigLLDLink(ZigLLVM_ObjectFormatType oformat, const char **args, size_t arg_ zig_unreachable(); case ZigLLVM_COFF: - return lld::coff::link(array_ref_args); + return lld::coff::link(array_ref_args, false, diag); case ZigLLVM_ELF: return lld::elf::link(array_ref_args, false, diag); From 67b8b00c446634a6db5818bcb84f4207aad3948a Mon Sep 17 00:00:00 2001 From: Josh Wolfe Date: Fri, 1 Dec 2017 16:11:39 -0700 Subject: [PATCH 18/32] implement insertion sort. something's broken --- std/sort.zig | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/std/sort.zig b/std/sort.zig index 1d7ca507ee..a1932c40e6 100644 --- a/std/sort.zig +++ b/std/sort.zig @@ -4,6 +4,19 @@ const math = @import("math/index.zig"); pub const Cmp = math.Cmp; +/// Stable sort using O(1) space. Currently implemented as insertion sort. +pub fn sort_stable(comptime T: type, array: []T, comptime cmp: fn(a: &const T, b: &const T)->Cmp) { + {var i: usize = 1; while (i < array.len) : (i += 1) { + const x = array[i]; + var j: usize = i; + while (j > 0 and cmp(array[j - 1], x) == Cmp.Greater) : (j -= 1) { + array[j] = array[j - 1]; + } + array[j] = x; + }} +} + +/// Unstable sort using O(n) stack space. Currentl 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); @@ -58,6 +71,62 @@ fn reverse(was: Cmp) -> Cmp { // --------------------------------------- // tests +test "stable sort" { + testStableSort(); + comptime testStableSort(); +} +fn testStableSort() { + var expected = []IdAndValue { + IdAndValue{.id = 0, .value = 0}, + IdAndValue{.id = 1, .value = 0}, + IdAndValue{.id = 2, .value = 0}, + IdAndValue{.id = 0, .value = 1}, + IdAndValue{.id = 1, .value = 1}, + IdAndValue{.id = 2, .value = 1}, + IdAndValue{.id = 0, .value = 2}, + IdAndValue{.id = 1, .value = 2}, + IdAndValue{.id = 2, .value = 2}, + }; + var cases = [][9]IdAndValue { + []IdAndValue { + IdAndValue{.id = 0, .value = 0}, + IdAndValue{.id = 0, .value = 1}, + IdAndValue{.id = 0, .value = 2}, + IdAndValue{.id = 1, .value = 0}, + IdAndValue{.id = 1, .value = 1}, + IdAndValue{.id = 1, .value = 2}, + IdAndValue{.id = 2, .value = 0}, + IdAndValue{.id = 2, .value = 1}, + IdAndValue{.id = 2, .value = 2}, + }, + []IdAndValue { + IdAndValue{.id = 0, .value = 2}, + IdAndValue{.id = 0, .value = 1}, + IdAndValue{.id = 0, .value = 0}, + IdAndValue{.id = 1, .value = 2}, + IdAndValue{.id = 1, .value = 1}, + IdAndValue{.id = 1, .value = 0}, + IdAndValue{.id = 2, .value = 2}, + IdAndValue{.id = 2, .value = 1}, + IdAndValue{.id = 2, .value = 0}, + }, + }; + for (cases) |*case| { + sort_stable(IdAndValue, (*case)[0..], cmpByValue); + for (*case) |item, i| { + assert(item.id == expected[i].id); + assert(item.value == expected[i].value); + } + } +} +const IdAndValue = struct { + id: i32, + value: i32, +}; +fn cmpByValue(a: &const IdAndValue, b: &const IdAndValue) -> Cmp { + return i32asc(a.value, b.value); +} + test "testSort" { const u8cases = [][]const []const u8 { [][]const u8{"", ""}, From 54a0db0daf8fd5ef307f275275e10f32ebd7d27a Mon Sep 17 00:00:00 2001 From: Josh Wolfe Date: Fri, 1 Dec 2017 19:54:01 -0700 Subject: [PATCH 19/32] todo: fix #639 --- std/sort.zig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/std/sort.zig b/std/sort.zig index a1932c40e6..57fb0ab4c0 100644 --- a/std/sort.zig +++ b/std/sort.zig @@ -73,7 +73,8 @@ fn reverse(was: Cmp) -> Cmp { test "stable sort" { testStableSort(); - comptime testStableSort(); + // TODO: uncomment this after https://github.com/zig-lang/zig/issues/639 + //comptime testStableSort(); } fn testStableSort() { var expected = []IdAndValue { From 98237f7c0ba62099e85a8caf8fc09039845b224e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 2 Dec 2017 17:12:37 -0500 Subject: [PATCH 20/32] casting between integer and enum only works via tag type See #305 --- src/analyze.cpp | 4 +++ src/ir.cpp | 24 +++++++++++++++-- test/cases/enum.zig | 14 +++++++--- test/compile_errors.zig | 57 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 94 insertions(+), 5 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 235aeea682..7d51e83677 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1399,6 +1399,10 @@ static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) { 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, diff --git a/src/ir.cpp b/src/ir.cpp index c8617e3a8a..c6003fbf32 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -8911,7 +8911,17 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst wanted_type->id == TypeTableEntryIdEnum && wanted_type->data.enumeration.gen_field_count == 0) { - return ir_analyze_int_to_enum(ira, source_instr, value, wanted_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_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; } // explicit cast from enum type with no payload to integer @@ -8919,7 +8929,17 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst actual_type->id == TypeTableEntryIdEnum && actual_type->data.enumeration.gen_field_count == 0) { - return ir_analyze_enum_to_int(ira, source_instr, value, wanted_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_type->data.enum_tag.int_type) { + return ir_analyze_enum_to_int(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 diff --git a/test/cases/enum.zig b/test/cases/enum.zig index 1b2ff10a9b..6df858a48f 100644 --- a/test/cases/enum.zig +++ b/test/cases/enum.zig @@ -89,8 +89,8 @@ test "enum to int" { shouldEqual(Number.Four, 4); } -fn shouldEqual(n: Number, expected: usize) { - assert(usize(n) == expected); +fn shouldEqual(n: Number, expected: u3) { + assert(u3(n) == expected); } @@ -98,7 +98,7 @@ test "int to enum" { testIntToEnumEval(3); } fn testIntToEnumEval(x: i32) { - assert(IntToEnumNumber(x) == IntToEnumNumber.Three); + assert(IntToEnumNumber(u3(x)) == IntToEnumNumber.Three); } const IntToEnumNumber = enum { Zero, @@ -284,3 +284,11 @@ fn getC(data: &const BitFieldOfEnums) -> C { return data.c; } +test "casting enum to its tag type" { + testCastEnumToTagType(Small2.Two); + comptime testCastEnumToTagType(Small2.Two); +} + +fn testCastEnumToTagType(value: Small2) { + assert(u2(value) == 1); +} diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 9b5ec6a6d6..367dec08b3 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2390,4 +2390,61 @@ pub fn addCases(cases: &tests.CompileErrorContext) { \\} , ".tmp_source.zig:1:20: error: expected integer, found 'f32'"); + + cases.add("implicitly casting enum to tag type", + \\const Small = enum(u2) { + \\ One, + \\ Two, + \\ Three, + \\ Four, + \\}; + \\ + \\export fn entry() { + \\ var x: u2 = Small.Two; + \\} + , + ".tmp_source.zig:9:22: error: expected type 'u2', found 'Small'"); + + cases.add("explicitly casting enum to non tag type", + \\const Small = enum(u2) { + \\ One, + \\ Two, + \\ Three, + \\ Four, + \\}; + \\ + \\export fn entry() { + \\ var x = u3(Small.Two); + \\} + , + ".tmp_source.zig:9:15: error: enum to integer cast to 'u3' instead of its tag type, 'u2'"); + + cases.add("explicitly casting non tag type to enum", + \\const Small = enum(u2) { + \\ One, + \\ Two, + \\ Three, + \\ Four, + \\}; + \\ + \\export fn entry() { + \\ var y = u3(3); + \\ var x = Small(y); + \\} + , + ".tmp_source.zig:10:18: error: integer to enum cast from 'u3' instead of its tag type, 'u2'"); + + cases.add("non unsigned integer enum tag type", + \\const Small = enum(i2) { + \\ One, + \\ Two, + \\ Three, + \\ Four, + \\}; + \\ + \\export fn entry() { + \\ var y = Small.Two; + \\} + , + ".tmp_source.zig:1:19: error: expected unsigned integer, found 'i2'"); } From 137c8f5e8a6023db24f90555e968b592a4b843e4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 2 Dec 2017 22:31:42 -0500 Subject: [PATCH 21/32] 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 | 50 ++++----- std/sort.zig | 2 +- test/cases/enum.zig | 56 +++++++++- test/compile_errors.zig | 55 ++++++++-- 13 files changed, 452 insertions(+), 147 deletions(-) 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); - - Token *token = &pc->tokens->at(*token_index); - if (token->id == TokenIdRBrace) { - *token_index += 1; - break; - } else { - ast_eat_token(pc, token_index, TokenIdComma); - } + 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 *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"); } From 0ad1239522c70418990dc7b9da4e128da7cdd1d5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 3 Dec 2017 20:43:56 -0500 Subject: [PATCH 22/32] 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 | 245 +++------ 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, 803 insertions(+), 953 deletions(-) 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; + TypeEnumField *enum_field = &enum_type->data.enumeration.fields[i]; - 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); - - 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); + // 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, ""); - 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, ""); - - 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; + TypeUnionField *union_field = &union_type->data.unionation.fields[i]; + union_field->name = field_node->data.struct_field.name; + + 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; + } + TypeTableEntry *field_type = analyze_type_expr(g, scope, field_node->data.struct_field.type); - type_union_field->type_entry = field_type; + union_field->type_entry = field_type; - // 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")); + 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); - } - - 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; + if (type_entry->data.unionation.gen_field_count == 0) { + if (type_entry->data.unionation.gen_tag_index == SIZE_MAX) { + return nullptr; } 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); - } else { - union_value_ref = LLVMConstNamedStruct(union_type_ref, fields, 2); - } + return bigint_to_llvm_const(type_entry->data.unionation.tag_type->type_ref, + &const_val->data.x_union.tag); } } - if (type_entry->data.unionation.gen_tag_index == SIZE_MAX) { - return union_value_ref; + LLVMValueRef union_value_ref; + 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 { + 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; + } } 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); - } - } - } + 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; From 5a8367e8924e809b1390796eb656633ea5e34a86 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 3 Dec 2017 22:36:01 -0500 Subject: [PATCH 23/32] rename @EnumTagType to @TagType. add tests for union-enums See #618 --- src/all_types.hpp | 10 ++--- src/codegen.cpp | 10 ++--- src/ir.cpp | 91 +++++++++++++++++++++++++++----------------- src/ir_print.cpp | 16 ++++---- test/cases/enum.zig | 4 +- test/cases/union.zig | 33 ++++++++++++++++ 6 files changed, 109 insertions(+), 55 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 6061f63da6..086ad7db49 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1266,7 +1266,7 @@ enum BuiltinFnId { BuiltinFnIdIntToPtr, BuiltinFnIdPtrToInt, BuiltinFnIdTagName, - BuiltinFnIdEnumTagType, + BuiltinFnIdTagType, BuiltinFnIdFieldParentPtr, BuiltinFnIdOffsetOf, BuiltinFnIdInlineCall, @@ -1889,8 +1889,8 @@ enum IrInstructionId { IrInstructionIdSetGlobalLinkage, IrInstructionIdDeclRef, IrInstructionIdPanic, - IrInstructionIdEnumTagName, - IrInstructionIdEnumTagType, + IrInstructionIdTagName, + IrInstructionIdTagType, IrInstructionIdFieldParentPtr, IrInstructionIdOffsetOf, IrInstructionIdTypeId, @@ -2652,13 +2652,13 @@ struct IrInstructionPanic { IrInstruction *msg; }; -struct IrInstructionEnumTagName { +struct IrInstructionTagName { IrInstruction base; IrInstruction *target; }; -struct IrInstructionEnumTagType { +struct IrInstructionTagType { IrInstruction base; IrInstruction *target; diff --git a/src/codegen.cpp b/src/codegen.cpp index dbf4f8522b..bd84310ff1 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2717,7 +2717,7 @@ static LLVMValueRef ir_render_err_name(CodeGen *g, IrExecutable *executable, IrI } static LLVMValueRef ir_render_enum_tag_name(CodeGen *g, IrExecutable *executable, - IrInstructionEnumTagName *instruction) + IrInstructionTagName *instruction) { TypeTableEntry *enum_type = instruction->target->value.type; assert(enum_type->id == TypeTableEntryIdEnum); @@ -3484,7 +3484,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdOpaqueType: case IrInstructionIdSetAlignStack: case IrInstructionIdArgType: - case IrInstructionIdEnumTagType: + case IrInstructionIdTagType: zig_unreachable(); case IrInstructionIdReturn: return ir_render_return(g, executable, (IrInstructionReturn *)instruction); @@ -3594,8 +3594,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_container_init_list(g, executable, (IrInstructionContainerInitList *)instruction); case IrInstructionIdPanic: return ir_render_panic(g, executable, (IrInstructionPanic *)instruction); - case IrInstructionIdEnumTagName: - return ir_render_enum_tag_name(g, executable, (IrInstructionEnumTagName *)instruction); + case IrInstructionIdTagName: + return ir_render_enum_tag_name(g, executable, (IrInstructionTagName *)instruction); case IrInstructionIdFieldParentPtr: return ir_render_field_parent_ptr(g, executable, (IrInstructionFieldParentPtr *)instruction); case IrInstructionIdAlignCast: @@ -4963,7 +4963,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdIntToPtr, "intToPtr", 2); create_builtin_fn(g, BuiltinFnIdPtrToInt, "ptrToInt", 1); create_builtin_fn(g, BuiltinFnIdTagName, "tagName", 1); - create_builtin_fn(g, BuiltinFnIdEnumTagType, "EnumTagType", 1); + create_builtin_fn(g, BuiltinFnIdTagType, "TagType", 1); create_builtin_fn(g, BuiltinFnIdFieldParentPtr, "fieldParentPtr", 3); create_builtin_fn(g, BuiltinFnIdOffsetOf, "offsetOf", 2); create_builtin_fn(g, BuiltinFnIdDivExact, "divExact", 2); diff --git a/src/ir.cpp b/src/ir.cpp index 5da59fedb7..e70340b300 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -539,12 +539,12 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionPanic *) { return IrInstructionIdPanic; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionEnumTagName *) { - return IrInstructionIdEnumTagName; +static constexpr IrInstructionId ir_instruction_id(IrInstructionTagName *) { + return IrInstructionIdTagName; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionEnumTagType *) { - return IrInstructionIdEnumTagType; +static constexpr IrInstructionId ir_instruction_id(IrInstructionTagType *) { + return IrInstructionIdTagType; } static constexpr IrInstructionId ir_instruction_id(IrInstructionFieldParentPtr *) { @@ -2205,10 +2205,10 @@ static IrInstruction *ir_build_panic(IrBuilder *irb, Scope *scope, AstNode *sour return &instruction->base; } -static IrInstruction *ir_build_enum_tag_name(IrBuilder *irb, Scope *scope, AstNode *source_node, +static IrInstruction *ir_build_tag_name(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *target) { - IrInstructionEnumTagName *instruction = ir_build_instruction(irb, scope, source_node); + IrInstructionTagName *instruction = ir_build_instruction(irb, scope, source_node); instruction->target = target; ir_ref_instruction(target, irb->current_basic_block); @@ -2216,10 +2216,10 @@ static IrInstruction *ir_build_enum_tag_name(IrBuilder *irb, Scope *scope, AstNo return &instruction->base; } -static IrInstruction *ir_build_enum_tag_type(IrBuilder *irb, Scope *scope, AstNode *source_node, +static IrInstruction *ir_build_tag_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *target) { - IrInstructionEnumTagType *instruction = ir_build_instruction(irb, scope, source_node); + IrInstructionTagType *instruction = ir_build_instruction(irb, scope, source_node); instruction->target = target; ir_ref_instruction(target, irb->current_basic_block); @@ -3002,14 +3002,14 @@ static IrInstruction *ir_instruction_panic_get_dep(IrInstructionPanic *instructi } } -static IrInstruction *ir_instruction_enumtagname_get_dep(IrInstructionEnumTagName *instruction, size_t index) { +static IrInstruction *ir_instruction_enumtagname_get_dep(IrInstructionTagName *instruction, size_t index) { switch (index) { case 0: return instruction->target; default: return nullptr; } } -static IrInstruction *ir_instruction_enumtagtype_get_dep(IrInstructionEnumTagType *instruction, size_t index) { +static IrInstruction *ir_instruction_enumtagtype_get_dep(IrInstructionTagType *instruction, size_t index) { switch (index) { case 0: return instruction->target; default: return nullptr; @@ -3270,10 +3270,10 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t return ir_instruction_declref_get_dep((IrInstructionDeclRef *) instruction, index); case IrInstructionIdPanic: return ir_instruction_panic_get_dep((IrInstructionPanic *) instruction, index); - case IrInstructionIdEnumTagName: - return ir_instruction_enumtagname_get_dep((IrInstructionEnumTagName *) instruction, index); - case IrInstructionIdEnumTagType: - return ir_instruction_enumtagtype_get_dep((IrInstructionEnumTagType *) instruction, index); + case IrInstructionIdTagName: + return ir_instruction_enumtagname_get_dep((IrInstructionTagName *) instruction, index); + case IrInstructionIdTagType: + return ir_instruction_enumtagtype_get_dep((IrInstructionTagType *) instruction, index); case IrInstructionIdFieldParentPtr: return ir_instruction_fieldparentptr_get_dep((IrInstructionFieldParentPtr *) instruction, index); case IrInstructionIdOffsetOf: @@ -4627,16 +4627,16 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return 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); + return ir_build_tag_name(irb, scope, node, actual_tag); } - case BuiltinFnIdEnumTagType: + case BuiltinFnIdTagType: { 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; - return ir_build_enum_tag_type(irb, scope, node, arg0_value); + return ir_build_tag_type(irb, scope, node, arg0_value); } case BuiltinFnIdFieldParentPtr: { @@ -13638,7 +13638,7 @@ static TypeTableEntry *ir_analyze_instruction_err_name(IrAnalyze *ira, IrInstruc return str_type; } -static TypeTableEntry *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrInstructionEnumTagName *instruction) { +static TypeTableEntry *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrInstructionTagName *instruction) { IrInstruction *target = instruction->target->other; if (type_is_invalid(target->value.type)) return ira->codegen->builtin_types.entry_invalid; @@ -13658,7 +13658,7 @@ static TypeTableEntry *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrIn ira->codegen->name_table_enums.append(target->value.type); } - IrInstruction *result = ir_build_enum_tag_name(&ira->new_irb, instruction->base.scope, + IrInstruction *result = ir_build_tag_name(&ira->new_irb, instruction->base.scope, instruction->base.source_node, target); ir_link_new_instruction(result, &instruction->base); TypeTableEntry *u8_ptr_type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, true); @@ -15838,22 +15838,43 @@ static TypeTableEntry *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstruc return ira->codegen->builtin_types.entry_type; } -static TypeTableEntry *ir_analyze_instruction_enum_tag_type(IrAnalyze *ira, IrInstructionEnumTagType *instruction) { +static TypeTableEntry *ir_analyze_instruction_tag_type(IrAnalyze *ira, IrInstructionTagType *instruction) { IrInstruction *target_inst = instruction->target->other; TypeTableEntry *enum_type = ir_resolve_type(ira, target_inst); if (type_is_invalid(enum_type)) return ira->codegen->builtin_types.entry_invalid; - if (enum_type->id != TypeTableEntryIdEnum) { - ir_add_error(ira, target_inst, buf_sprintf("expected enum, found '%s'", buf_ptr(&enum_type->name))); + + if (enum_type->id == TypeTableEntryIdEnum) { + ensure_complete_type(ira->codegen, enum_type); + if (type_is_invalid(enum_type)) + return ira->codegen->builtin_types.entry_invalid; + + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + out_val->data.x_type = enum_type->data.enumeration.tag_int_type; + return ira->codegen->builtin_types.entry_type; + } else if (enum_type->id == TypeTableEntryIdUnion) { + ensure_complete_type(ira->codegen, enum_type); + if (type_is_invalid(enum_type)) + return ira->codegen->builtin_types.entry_invalid; + + AstNode *decl_node = enum_type->data.unionation.decl_node; + if (decl_node->data.container_decl.auto_enum || decl_node->data.container_decl.init_arg_expr != nullptr) { + assert(enum_type->data.unionation.tag_type != nullptr); + + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + out_val->data.x_type = enum_type->data.unionation.tag_type; + return ira->codegen->builtin_types.entry_type; + } else { + ErrorMsg *msg = ir_add_error(ira, target_inst, buf_sprintf("union '%s' has no tag", + buf_ptr(&enum_type->name))); + add_error_note(ira->codegen, msg, decl_node, buf_sprintf("consider 'union(enum)' here")); + return ira->codegen->builtin_types.entry_invalid; + } + } else { + ir_add_error(ira, target_inst, buf_sprintf("expected enum or union, found '%s'", + buf_ptr(&enum_type->name))); return ira->codegen->builtin_types.entry_invalid; } - ensure_complete_type(ira->codegen, enum_type); - if (type_is_invalid(enum_type)) - return ira->codegen->builtin_types.entry_invalid; - - ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); - out_val->data.x_type = enum_type->data.enumeration.tag_int_type; - return ira->codegen->builtin_types.entry_type; } static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { @@ -16032,8 +16053,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_int_to_ptr(ira, (IrInstructionIntToPtr *)instruction); case IrInstructionIdPtrToInt: return ir_analyze_instruction_ptr_to_int(ira, (IrInstructionPtrToInt *)instruction); - case IrInstructionIdEnumTagName: - return ir_analyze_instruction_enum_tag_name(ira, (IrInstructionEnumTagName *)instruction); + case IrInstructionIdTagName: + return ir_analyze_instruction_enum_tag_name(ira, (IrInstructionTagName *)instruction); case IrInstructionIdFieldParentPtr: return ir_analyze_instruction_field_parent_ptr(ira, (IrInstructionFieldParentPtr *)instruction); case IrInstructionIdOffsetOf: @@ -16052,8 +16073,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_set_align_stack(ira, (IrInstructionSetAlignStack *)instruction); case IrInstructionIdArgType: return ir_analyze_instruction_arg_type(ira, (IrInstructionArgType *)instruction); - case IrInstructionIdEnumTagType: - return ir_analyze_instruction_enum_tag_type(ira, (IrInstructionEnumTagType *)instruction); + case IrInstructionIdTagType: + return ir_analyze_instruction_tag_type(ira, (IrInstructionTagType *)instruction); } zig_unreachable(); } @@ -16230,14 +16251,14 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdDeclRef: case IrInstructionIdErrName: case IrInstructionIdTypeName: - case IrInstructionIdEnumTagName: + case IrInstructionIdTagName: case IrInstructionIdFieldParentPtr: case IrInstructionIdOffsetOf: case IrInstructionIdTypeId: case IrInstructionIdAlignCast: case IrInstructionIdOpaqueType: case IrInstructionIdArgType: - case IrInstructionIdEnumTagType: + case IrInstructionIdTagType: return false; case IrInstructionIdAsm: { diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 99617056de..223a012456 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -872,8 +872,8 @@ static void ir_print_type_name(IrPrint *irp, IrInstructionTypeName *instruction) ir_print_other_instruction(irp, instruction->type_value); } -static void ir_print_enum_tag_name(IrPrint *irp, IrInstructionEnumTagName *instruction) { - fprintf(irp->f, "enumtagname "); +static void ir_print_tag_name(IrPrint *irp, IrInstructionTagName *instruction) { + fprintf(irp->f, "tagname "); ir_print_other_instruction(irp, instruction->target); } @@ -981,8 +981,8 @@ static void ir_print_arg_type(IrPrint *irp, IrInstructionArgType *instruction) { fprintf(irp->f, ")"); } -static void ir_print_enum_tag_type(IrPrint *irp, IrInstructionEnumTagType *instruction) { - fprintf(irp->f, "@EnumTagType("); +static void ir_print_enum_tag_type(IrPrint *irp, IrInstructionTagType *instruction) { + fprintf(irp->f, "@TagType("); ir_print_other_instruction(irp, instruction->target); fprintf(irp->f, ")"); } @@ -1254,8 +1254,8 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdTypeName: ir_print_type_name(irp, (IrInstructionTypeName *)instruction); break; - case IrInstructionIdEnumTagName: - ir_print_enum_tag_name(irp, (IrInstructionEnumTagName *)instruction); + case IrInstructionIdTagName: + ir_print_tag_name(irp, (IrInstructionTagName *)instruction); break; case IrInstructionIdCanImplicitCast: ir_print_can_implicit_cast(irp, (IrInstructionCanImplicitCast *)instruction); @@ -1299,8 +1299,8 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdArgType: ir_print_arg_type(irp, (IrInstructionArgType *)instruction); break; - case IrInstructionIdEnumTagType: - ir_print_enum_tag_type(irp, (IrInstructionEnumTagType *)instruction); + case IrInstructionIdTagType: + ir_print_enum_tag_type(irp, (IrInstructionTagType *)instruction); break; } fprintf(irp->f, "\n"); diff --git a/test/cases/enum.zig b/test/cases/enum.zig index f3240045df..ec900511eb 100644 --- a/test/cases/enum.zig +++ b/test/cases/enum.zig @@ -204,12 +204,12 @@ test "set enum tag type" { { var x = Small.One; x = Small.Two; - comptime assert(@EnumTagType(Small) == u2); + comptime assert(@TagType(Small) == u2); } { var x = Small2.One; x = Small2.Two; - comptime assert(@EnumTagType(Small2) == u2); + comptime assert(@TagType(Small2) == u2); } } diff --git a/test/cases/union.zig b/test/cases/union.zig index ec050a20d5..c4767fd649 100644 --- a/test/cases/union.zig +++ b/test/cases/union.zig @@ -104,3 +104,36 @@ fn bar(value: &const Payload) -> i32 { Payload.C => |x| if (x) i32(30) else 31, }; } + +const MultipleChoice2 = union(enum(u32)) { + Unspecified1: i32, + A: f32 = 20, + Unspecified2: void, + B: bool = 40, + Unspecified3: i32, + C: i8 = 60, + Unspecified4: void, + D: void = 1000, + Unspecified5: i32, +}; + +test "union(enum(u32)) with specified and unspecified tag values" { + comptime assert(@TagType(@TagType(MultipleChoice2)) == u32); + testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2 {.C = 123}); + comptime testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2 { .C = 123} ); +} + +fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: &const MultipleChoice2) { + assert(u32(@TagType(MultipleChoice2)(*x)) == 60); + assert(1123 == switch (*x) { + MultipleChoice2.A => 1, + MultipleChoice2.B => 2, + MultipleChoice2.C => |v| i32(1000) + v, + MultipleChoice2.D => 4, + MultipleChoice2.Unspecified1 => 5, + MultipleChoice2.Unspecified2 => 6, + MultipleChoice2.Unspecified3 => 7, + MultipleChoice2.Unspecified4 => 8, + MultipleChoice2.Unspecified5 => 9, + }); +} From fce435db269162774106ce7e6ddf70871d5eeb49 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 4 Dec 2017 00:32:12 -0500 Subject: [PATCH 24/32] fix abi alignment of union-enums not counting tag type add more tests for unions See #618 --- src/analyze.cpp | 418 ++++++++++++++++++++++------------------ src/ir.cpp | 7 +- test/cases/union.zig | 13 ++ test/compile_errors.zig | 47 +++++ 4 files changed, 291 insertions(+), 194 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 9d2d4f1a8f..96f0eb44db 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1713,84 +1713,16 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) { uint64_t biggest_align_in_bits = 0; uint64_t biggest_size_in_bits = 0; - ZigLLVMDIEnumerator **di_enumerators; - Scope *scope = &union_type->data.unionation.decls_scope->base; ImportTableEntry *import = get_scope_import(scope); // set temporary flag union_type->data.unionation.embedded_in_current = true; - HashMap occupied_tag_values = {}; - - AstNode *enum_type_node = decl_node->data.container_decl.init_arg_expr; - bool auto_layout = (union_type->data.unionation.layout == ContainerLayoutAuto); - bool want_safety = (field_count >= 2) && (auto_layout || enum_type_node != nullptr); - TypeTableEntry *tag_type; - bool create_enum_type = decl_node->data.container_decl.auto_enum || (enum_type_node == nullptr && want_safety); - bool *covered_enum_fields; - if (create_enum_type) { - occupied_tag_values.init(field_count); - - di_enumerators = allocate(field_count); - - TypeTableEntry *tag_int_type; - if (enum_type_node != nullptr) { - tag_int_type = analyze_type_expr(g, scope, enum_type_node); - if (type_is_invalid(tag_int_type)) { - union_type->data.unionation.is_invalid = true; - return; - } - if (tag_int_type->id != TypeTableEntryIdInt) { - add_node_error(g, enum_type_node, - buf_sprintf("expected integer tag type, found '%s'", buf_ptr(&tag_int_type->name))); - union_type->data.unionation.is_invalid = true; - return; - } - } else { - tag_int_type = get_smallest_unsigned_int_type(g, field_count - 1); - } - - tag_type = new_type_table_entry(TypeTableEntryIdEnum); - buf_resize(&tag_type->name, 0); - buf_appendf(&tag_type->name, "@EnumTagType(%s)", buf_ptr(&union_type->name)); - tag_type->is_copyable = true; - tag_type->type_ref = tag_int_type->type_ref; - tag_type->zero_bits = tag_int_type->zero_bits; - - tag_type->data.enumeration.tag_int_type = tag_int_type; - tag_type->data.enumeration.zero_bits_known = true; - tag_type->data.enumeration.decl_node = decl_node; - tag_type->data.enumeration.layout = ContainerLayoutAuto; - tag_type->data.enumeration.src_field_count = field_count; - tag_type->data.enumeration.fields = allocate(field_count); - tag_type->data.enumeration.decls_scope = union_type->data.unionation.decls_scope; - tag_type->data.enumeration.complete = true; - } else if (enum_type_node != nullptr) { - TypeTableEntry *enum_type = analyze_type_expr(g, scope, enum_type_node); - if (type_is_invalid(enum_type)) { - union_type->data.unionation.is_invalid = true; - union_type->data.unionation.embedded_in_current = false; - return; - } - if (enum_type->id != TypeTableEntryIdEnum) { - union_type->data.unionation.is_invalid = true; - union_type->data.unionation.embedded_in_current = false; - add_node_error(g, enum_type_node, - buf_sprintf("expected enum tag type, found '%s'", buf_ptr(&enum_type->name))); - return; - } - tag_type = enum_type; - covered_enum_fields = allocate(enum_type->data.enumeration.src_field_count); - } else { - tag_type = nullptr; - } - union_type->data.unionation.tag_type = tag_type; for (uint32_t i = 0; i < field_count; i += 1) { AstNode *field_node = decl_node->data.container_decl.fields.at(i); TypeUnionField *union_field = &union_type->data.unionation.fields[i]; - Buf *field_name = field_node->data.struct_field.name; TypeTableEntry *field_type = union_field->type_entry; ensure_complete_type(g, field_type); @@ -1799,57 +1731,6 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) { continue; } - if (create_enum_type) { - di_enumerators[i] = ZigLLVMCreateDebugEnumerator(g->dbuilder, buf_ptr(field_name), i); - union_field->enum_field = &tag_type->data.enumeration.fields[i]; - union_field->enum_field->name = field_name; - union_field->enum_field->decl_index = i; - - AstNode *tag_value = field_node->data.struct_field.value; - // In this first pass we resolve explicit tag values. - // In a second pass we will fill in the unspecified ones. - if (tag_value != nullptr) { - TypeTableEntry *tag_int_type = tag_type->data.enumeration.tag_int_type; - IrInstruction *result_inst = analyze_const_value(g, scope, tag_value, tag_int_type, nullptr); - if (result_inst->value.type->id == TypeTableEntryIdInvalid) { - union_type->data.unionation.is_invalid = true; - continue; - } - assert(result_inst->value.special != ConstValSpecialRuntime); - assert(result_inst->value.type->id == TypeTableEntryIdInt); - auto entry = occupied_tag_values.put_unique(result_inst->value.data.x_bigint, tag_value); - if (entry == nullptr) { - bigint_init_bigint(&union_field->enum_field->value, &result_inst->value.data.x_bigint); - } else { - Buf *val_buf = buf_alloc(); - bigint_append_buf(val_buf, &result_inst->value.data.x_bigint, 10); - - ErrorMsg *msg = add_node_error(g, tag_value, - buf_sprintf("enum tag value %s already taken", buf_ptr(val_buf))); - add_error_note(g, msg, entry->value, - buf_sprintf("other occurrence here")); - union_type->data.unionation.is_invalid = true; - continue; - } - } - } else if (enum_type_node != nullptr) { - union_field->enum_field = find_enum_type_field(tag_type, field_name); - if (union_field->enum_field == nullptr) { - ErrorMsg *msg = add_node_error(g, field_node, - buf_sprintf("enum field not found: '%s'", buf_ptr(field_name))); - add_error_note(g, msg, tag_type->data.enumeration.decl_node, - buf_sprintf("enum declared here")); - union_type->data.unionation.is_invalid = true; - continue; - } - covered_enum_fields[union_field->enum_field->decl_index] = true; - } else { - union_field->enum_field = allocate(1); - union_field->enum_field->name = field_name; - union_field->enum_field->decl_index = i; - bigint_init_unsigned(&union_field->enum_field->value, i); - } - if (!type_has_bits(field_type)) continue; @@ -1876,48 +1757,6 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) { } } - if (create_enum_type) { - // Now iterate again and populate the unspecified tag values - uint32_t next_maybe_unoccupied_index = 0; - - for (uint32_t field_i = 0; field_i < field_count; field_i += 1) { - AstNode *field_node = decl_node->data.container_decl.fields.at(field_i); - TypeUnionField *union_field = &union_type->data.unionation.fields[field_i]; - AstNode *tag_value = field_node->data.struct_field.value; - - if (tag_value == nullptr) { - if (occupied_tag_values.size() == 0) { - bigint_init_unsigned(&union_field->enum_field->value, next_maybe_unoccupied_index); - next_maybe_unoccupied_index += 1; - } else { - BigInt proposed_value; - for (;;) { - bigint_init_unsigned(&proposed_value, next_maybe_unoccupied_index); - next_maybe_unoccupied_index += 1; - auto entry = occupied_tag_values.put_unique(proposed_value, field_node); - if (entry != nullptr) { - continue; - } - break; - } - bigint_init_bigint(&union_field->enum_field->value, &proposed_value); - } - } - } - } else if (enum_type_node != nullptr) { - for (uint32_t i = 0; i < tag_type->data.enumeration.src_field_count; i += 1) { - TypeEnumField *enum_field = &tag_type->data.enumeration.fields[i]; - if (!covered_enum_fields[i]) { - AstNode *enum_decl_node = tag_type->data.enumeration.decl_node; - AstNode *field_node = enum_decl_node->data.container_decl.fields.at(i); - ErrorMsg *msg = add_node_error(g, decl_node, - buf_sprintf("enum field missing: '%s'", buf_ptr(enum_field->name))); - add_error_note(g, msg, field_node, - buf_sprintf("declared here")); - union_type->data.unionation.is_invalid = true; - } - } - } // unset temporary flag union_type->data.unionation.embedded_in_current = false; @@ -1948,11 +1787,12 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) { return; } - assert(most_aligned_union_member != nullptr); - uint64_t padding_in_bits = biggest_size_in_bits - size_of_most_aligned_member_in_bits; + TypeTableEntry *tag_type = union_type->data.unionation.tag_type; if (tag_type == nullptr) { + assert(most_aligned_union_member != nullptr); + if (padding_in_bits > 0) { TypeTableEntry *u8_type = get_int_type(g, false, 8); TypeTableEntry *padding_array = get_array_type(g, u8_type, padding_in_bits / 8); @@ -1993,7 +1833,13 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) { }; union_type_ref = LLVMStructType(union_element_types, 2, false); } else if (most_aligned_union_member == nullptr) { - zig_panic("TODO zero bit payload"); + union_type->data.unionation.gen_tag_index = SIZE_MAX; + union_type->data.unionation.gen_union_index = SIZE_MAX; + union_type->type_ref = tag_type->type_ref; + + ZigLLVMReplaceTemporary(g->dbuilder, union_type->di_type, tag_type->di_type); + union_type->di_type = tag_type->di_type; + return; } else { union_type_ref = most_aligned_union_member->type_ref; } @@ -2020,20 +1866,6 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) { LLVMStructSetBody(union_type->type_ref, root_struct_element_types, 2, false); - // create debug type for root struct - - uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, tag_type->type_ref); - uint64_t tag_debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_type->type_ref); - if (create_enum_type) { - // create debug type for tag - ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder, - ZigLLVMTypeToScope(union_type->di_type), "AnonEnum", - import->di_file, (unsigned)(decl_node->line + 1), - tag_debug_size_in_bits, tag_debug_align_in_bits, di_enumerators, field_count, - tag_type->di_type, ""); - tag_type->di_type = tag_di_type; - } - // create debug type for union ZigLLVMDIType *union_di_type = ZigLLVMCreateDebugUnionType(g->dbuilder, ZigLLVMTypeToScope(union_type->di_type), "AnonUnion", @@ -2053,6 +1885,10 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) { biggest_align_in_bits, union_offset_in_bits, 0, union_di_type); + + uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, tag_type->type_ref); + uint64_t tag_debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_type->type_ref); + ZigLLVMDIType *tag_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(union_type->di_type), "tag", import->di_file, (unsigned)(decl_node->line + 1), @@ -2312,8 +2148,23 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) { if (union_type->data.unionation.zero_bits_known) return; + if (type_is_invalid(union_type)) + return; + if (union_type->data.unionation.zero_bits_loop_flag) { + // If we get here it's due to recursion. From this we conclude that the struct is + // not zero bits, and if abi_alignment == 0 we further conclude that the first field + // is a pointer to this very struct, or a function pointer with parameters that + // reference such a type. union_type->data.unionation.zero_bits_known = true; + if (union_type->data.unionation.abi_alignment == 0) { + if (union_type->data.unionation.layout == ContainerLayoutPacked) { + union_type->data.unionation.abi_alignment = 1; + } else { + union_type->data.unionation.abi_alignment = LLVMABIAlignmentOfType(g->target_data_ref, + LLVMPointerType(LLVMInt8Type(), 0)); + } + } return; } @@ -2342,19 +2193,99 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) { Scope *scope = &union_type->data.unionation.decls_scope->base; + HashMap occupied_tag_values = {}; + + AstNode *enum_type_node = decl_node->data.container_decl.init_arg_expr; + bool auto_layout = (union_type->data.unionation.layout == ContainerLayoutAuto); + bool want_safety = (field_count >= 2) && (auto_layout || enum_type_node != nullptr); + TypeTableEntry *tag_type; + bool create_enum_type = decl_node->data.container_decl.auto_enum || (enum_type_node == nullptr && want_safety); + bool *covered_enum_fields; + ZigLLVMDIEnumerator **di_enumerators; + if (create_enum_type) { + occupied_tag_values.init(field_count); + + di_enumerators = allocate(field_count); + + TypeTableEntry *tag_int_type; + if (enum_type_node != nullptr) { + tag_int_type = analyze_type_expr(g, scope, enum_type_node); + if (type_is_invalid(tag_int_type)) { + union_type->data.unionation.is_invalid = true; + return; + } + if (tag_int_type->id != TypeTableEntryIdInt) { + add_node_error(g, enum_type_node, + buf_sprintf("expected integer tag type, found '%s'", buf_ptr(&tag_int_type->name))); + union_type->data.unionation.is_invalid = true; + return; + } + } else { + tag_int_type = get_smallest_unsigned_int_type(g, field_count - 1); + } + union_type->data.unionation.abi_alignment = get_abi_alignment(g, tag_int_type); + + tag_type = new_type_table_entry(TypeTableEntryIdEnum); + buf_resize(&tag_type->name, 0); + buf_appendf(&tag_type->name, "@EnumTagType(%s)", buf_ptr(&union_type->name)); + tag_type->is_copyable = true; + tag_type->type_ref = tag_int_type->type_ref; + tag_type->zero_bits = tag_int_type->zero_bits; + + tag_type->data.enumeration.tag_int_type = tag_int_type; + tag_type->data.enumeration.zero_bits_known = true; + tag_type->data.enumeration.decl_node = decl_node; + tag_type->data.enumeration.layout = ContainerLayoutAuto; + tag_type->data.enumeration.src_field_count = field_count; + tag_type->data.enumeration.fields = allocate(field_count); + tag_type->data.enumeration.decls_scope = union_type->data.unionation.decls_scope; + tag_type->data.enumeration.complete = true; + } else if (enum_type_node != nullptr) { + TypeTableEntry *enum_type = analyze_type_expr(g, scope, enum_type_node); + if (type_is_invalid(enum_type)) { + union_type->data.unionation.is_invalid = true; + union_type->data.unionation.embedded_in_current = false; + return; + } + if (enum_type->id != TypeTableEntryIdEnum) { + union_type->data.unionation.is_invalid = true; + union_type->data.unionation.embedded_in_current = false; + add_node_error(g, enum_type_node, + buf_sprintf("expected enum tag type, found '%s'", buf_ptr(&enum_type->name))); + return; + } + tag_type = enum_type; + covered_enum_fields = allocate(enum_type->data.enumeration.src_field_count); + union_type->data.unionation.abi_alignment = get_abi_alignment(g, enum_type); + } else { + tag_type = nullptr; + } + union_type->data.unionation.tag_type = tag_type; + uint32_t gen_field_index = 0; for (uint32_t i = 0; i < field_count; i += 1) { AstNode *field_node = decl_node->data.container_decl.fields.at(i); + Buf *field_name = field_node->data.struct_field.name; TypeUnionField *union_field = &union_type->data.unionation.fields[i]; union_field->name = field_node->data.struct_field.name; + TypeTableEntry *field_type; if (field_node->data.struct_field.type == nullptr) { - add_node_error(g, field_node, buf_sprintf("union field missing type")); - union_type->data.unionation.is_invalid = true; - continue; + if (decl_node->data.container_decl.auto_enum || decl_node->data.container_decl.init_arg_expr != nullptr) { + field_type = g->builtin_types.entry_void; + } else { + add_node_error(g, field_node, buf_sprintf("union field missing type")); + union_type->data.unionation.is_invalid = true; + continue; + } + } else { + field_type = analyze_type_expr(g, scope, field_node->data.struct_field.type); + type_ensure_zero_bits_known(g, field_type); + if (type_is_invalid(field_type)) { + union_type->data.unionation.is_invalid = true; + continue; + } } - - TypeTableEntry *field_type = analyze_type_expr(g, scope, field_node->data.struct_field.type); union_field->type_entry = field_type; if (field_node->data.struct_field.value != nullptr && !decl_node->data.container_decl.auto_enum) { @@ -2364,11 +2295,57 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) { buf_sprintf("consider 'union(enum)' here")); } - type_ensure_zero_bits_known(g, field_type); - if (type_is_invalid(field_type)) { - union_type->data.unionation.is_invalid = true; - continue; + if (create_enum_type) { + di_enumerators[i] = ZigLLVMCreateDebugEnumerator(g->dbuilder, buf_ptr(field_name), i); + union_field->enum_field = &tag_type->data.enumeration.fields[i]; + union_field->enum_field->name = field_name; + union_field->enum_field->decl_index = i; + + AstNode *tag_value = field_node->data.struct_field.value; + // In this first pass we resolve explicit tag values. + // In a second pass we will fill in the unspecified ones. + if (tag_value != nullptr) { + TypeTableEntry *tag_int_type = tag_type->data.enumeration.tag_int_type; + IrInstruction *result_inst = analyze_const_value(g, scope, tag_value, tag_int_type, nullptr); + if (result_inst->value.type->id == TypeTableEntryIdInvalid) { + union_type->data.unionation.is_invalid = true; + continue; + } + assert(result_inst->value.special != ConstValSpecialRuntime); + assert(result_inst->value.type->id == TypeTableEntryIdInt); + auto entry = occupied_tag_values.put_unique(result_inst->value.data.x_bigint, tag_value); + if (entry == nullptr) { + bigint_init_bigint(&union_field->enum_field->value, &result_inst->value.data.x_bigint); + } else { + Buf *val_buf = buf_alloc(); + bigint_append_buf(val_buf, &result_inst->value.data.x_bigint, 10); + + ErrorMsg *msg = add_node_error(g, tag_value, + buf_sprintf("enum tag value %s already taken", buf_ptr(val_buf))); + add_error_note(g, msg, entry->value, + buf_sprintf("other occurrence here")); + union_type->data.unionation.is_invalid = true; + continue; + } + } + } else if (enum_type_node != nullptr) { + union_field->enum_field = find_enum_type_field(tag_type, field_name); + if (union_field->enum_field == nullptr) { + ErrorMsg *msg = add_node_error(g, field_node, + buf_sprintf("enum field not found: '%s'", buf_ptr(field_name))); + add_error_note(g, msg, tag_type->data.enumeration.decl_node, + buf_sprintf("enum declared here")); + union_type->data.unionation.is_invalid = true; + continue; + } + covered_enum_fields[union_field->enum_field->decl_index] = true; + } else { + union_field->enum_field = allocate(1); + union_field->enum_field->name = field_name; + union_field->enum_field->decl_index = i; + bigint_init_unsigned(&union_field->enum_field->value, i); } + assert(union_field->enum_field != nullptr); if (!type_has_bits(field_type)) continue; @@ -2379,9 +2356,15 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) { uint32_t field_align_bytes = get_abi_alignment(g, field_type); if (field_align_bytes > biggest_align_bytes) { biggest_align_bytes = field_align_bytes; + if (biggest_align_bytes > union_type->data.unionation.abi_alignment) { + union_type->data.unionation.abi_alignment = biggest_align_bytes; + } } } + if (union_type->data.unionation.is_invalid) + return; + bool src_have_tag = decl_node->data.container_decl.auto_enum || decl_node->data.container_decl.init_arg_expr != nullptr; @@ -2405,15 +2388,66 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) { return; } + if (create_enum_type) { + // Now iterate again and populate the unspecified tag values + uint32_t next_maybe_unoccupied_index = 0; + + for (uint32_t field_i = 0; field_i < field_count; field_i += 1) { + AstNode *field_node = decl_node->data.container_decl.fields.at(field_i); + TypeUnionField *union_field = &union_type->data.unionation.fields[field_i]; + AstNode *tag_value = field_node->data.struct_field.value; + + if (tag_value == nullptr) { + if (occupied_tag_values.size() == 0) { + bigint_init_unsigned(&union_field->enum_field->value, next_maybe_unoccupied_index); + next_maybe_unoccupied_index += 1; + } else { + BigInt proposed_value; + for (;;) { + bigint_init_unsigned(&proposed_value, next_maybe_unoccupied_index); + next_maybe_unoccupied_index += 1; + auto entry = occupied_tag_values.put_unique(proposed_value, field_node); + if (entry != nullptr) { + continue; + } + break; + } + bigint_init_bigint(&union_field->enum_field->value, &proposed_value); + } + } + } + } else if (enum_type_node != nullptr) { + for (uint32_t i = 0; i < tag_type->data.enumeration.src_field_count; i += 1) { + TypeEnumField *enum_field = &tag_type->data.enumeration.fields[i]; + if (!covered_enum_fields[i]) { + AstNode *enum_decl_node = tag_type->data.enumeration.decl_node; + AstNode *field_node = enum_decl_node->data.container_decl.fields.at(i); + ErrorMsg *msg = add_node_error(g, decl_node, + buf_sprintf("enum field missing: '%s'", buf_ptr(enum_field->name))); + add_error_note(g, msg, field_node, + buf_sprintf("declared here")); + union_type->data.unionation.is_invalid = true; + } + } + } + + if (create_enum_type) { + ImportTableEntry *import = get_scope_import(scope); + uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, tag_type->type_ref); + uint64_t tag_debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_type->type_ref); + // TODO get a more accurate debug scope + ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder, + ZigLLVMFileToScope(import->di_file), buf_ptr(&tag_type->name), + import->di_file, (unsigned)(decl_node->line + 1), + tag_debug_size_in_bits, tag_debug_align_in_bits, di_enumerators, field_count, + tag_type->di_type, ""); + tag_type->di_type = tag_di_type; + } + union_type->data.unionation.zero_bits_loop_flag = false; union_type->data.unionation.gen_field_count = gen_field_index; union_type->zero_bits = (gen_field_index == 0 && (field_count < 2 || !src_have_tag)); union_type->data.unionation.zero_bits_known = true; - - // also compute abi_alignment - if (!union_type->zero_bits) { - union_type->data.unionation.abi_alignment = biggest_align_bytes; - } } static void get_fully_qualified_decl_name_internal(Buf *buf, Scope *scope, uint8_t sep) { diff --git a/src/ir.cpp b/src/ir.cpp index e70340b300..d784974ae8 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -12970,10 +12970,13 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira, ir_build_load_ptr_from(&ira->new_irb, &switch_target_instruction->base, target_value_ptr); return target_type; case TypeTableEntryIdUnion: { - if (target_type->data.unionation.gen_tag_index == SIZE_MAX) { + AstNode *decl_node = target_type->data.unionation.decl_node; + if (!decl_node->data.container_decl.auto_enum && + decl_node->data.container_decl.init_arg_expr == nullptr) + { ErrorMsg *msg = ir_add_error(ira, target_value_ptr, buf_sprintf("switch on union which has no attached enum")); - add_error_note(ira->codegen, msg, target_type->data.unionation.decl_node, + add_error_note(ira->codegen, msg, decl_node, buf_sprintf("union declared here")); return ira->codegen->builtin_types.entry_invalid; } diff --git a/test/cases/union.zig b/test/cases/union.zig index c4767fd649..291384eaeb 100644 --- a/test/cases/union.zig +++ b/test/cases/union.zig @@ -105,6 +105,18 @@ fn bar(value: &const Payload) -> i32 { }; } +const MultipleChoice = union(enum(u32)) { + A = 20, + B = 40, + C = 60, + D = 1000, +}; +test "simple union(enum(u32))" { + var x = MultipleChoice.C; + assert(x == MultipleChoice.C); + assert(u32(@TagType(MultipleChoice)(x)) == 60); +} + const MultipleChoice2 = union(enum(u32)) { Unspecified1: i32, A: f32 = 20, @@ -137,3 +149,4 @@ fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: &const MultipleChoice2) { MultipleChoice2.Unspecified5 => 9, }); } + diff --git a/test/compile_errors.zig b/test/compile_errors.zig index ac06b5aa2a..e8a517b1ca 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2524,4 +2524,51 @@ pub fn addCases(cases: &tests.CompileErrorContext) { , ".tmp_source.zig:6:17: error: enum field missing: 'C'", ".tmp_source.zig:4:5: note: declared here"); + + cases.add("@TagType when union has no attached enum", + \\const Foo = union { + \\ A: i32, + \\}; + \\export fn entry() { + \\ const x = @TagType(Foo); + \\} + , + ".tmp_source.zig:5:24: error: union 'Foo' has no tag", + ".tmp_source.zig:1:13: note: consider 'union(enum)' here"); + + cases.add("non-integer tag type to automatic union enum", + \\const Foo = union(enum(f32)) { + \\ A: i32, + \\}; + \\export fn entry() { + \\ const x = @TagType(Foo); + \\} + , + ".tmp_source.zig:1:23: error: expected integer tag type, found 'f32'"); + + cases.add("non-enum tag type passed to union", + \\const Foo = union(u32) { + \\ A: i32, + \\}; + \\export fn entry() { + \\ const x = @TagType(Foo); + \\} + , + ".tmp_source.zig:1:18: error: expected enum tag type, found 'u32'"); + + cases.add("union auto-enum value already taken", + \\const MultipleChoice = union(enum(u32)) { + \\ A = 20, + \\ B = 40, + \\ C = 60, + \\ D = 1000, + \\ E = 60, + \\}; + \\export fn entry() { + \\ var x = MultipleChoice { .C = {} }; + \\} + , + ".tmp_source.zig:6:9: error: enum tag value 60 already taken", + ".tmp_source.zig:4:9: note: other occurrence here"); + } From 05d9f07541c8c5b788687046ad313c6cff211476 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 4 Dec 2017 00:56:27 -0500 Subject: [PATCH 25/32] more tests for unions See #618 --- src/ir.cpp | 2 +- test/compile_errors.zig | 115 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 115 insertions(+), 2 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index d784974ae8..facd7087f0 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -12977,7 +12977,7 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira, 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, decl_node, - buf_sprintf("union declared here")); + buf_sprintf("consider 'union(enum)' here")); return ira->codegen->builtin_types.entry_invalid; } TypeTableEntry *tag_type = target_type->data.unionation.tag_type; diff --git a/test/compile_errors.zig b/test/compile_errors.zig index e8a517b1ca..5d13ed8d48 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2479,7 +2479,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) { \\ A: i32 = 20, \\}; \\export fn entry() { - \\ var x: MultipleChoice = undefined; + \\ var x: MultipleChoice = undefined; \\} , ".tmp_source.zig:2:14: error: non-enum union field assignment", @@ -2493,6 +2493,14 @@ pub fn addCases(cases: &tests.CompileErrorContext) { , ".tmp_source.zig:1:13: error: enums must have 1 or more fields"); + cases.add("union with 0 fields", + \\const Foo = union {}; + \\export fn entry() -> usize { + \\ return @sizeOf(Foo); + \\} + , + ".tmp_source.zig:1:13: error: unions must have 1 or more fields"); + cases.add("enum value already taken", \\const MultipleChoice = enum(u32) { \\ A = 20, @@ -2571,4 +2579,109 @@ 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 enum field does not match enum", + \\const Letter = enum { + \\ A, + \\ B, + \\ C, + \\}; + \\const Payload = union(Letter) { + \\ A: i32, + \\ B: f64, + \\ C: bool, + \\ D: bool, + \\}; + \\export fn entry() { + \\ var a = Payload {.A = 1234}; + \\} + , + ".tmp_source.zig:10:5: error: enum field not found: 'D'", + ".tmp_source.zig:1:16: note: enum declared here"); + + cases.add("field type supplied in an enum", + \\const Letter = enum { + \\ A: void, + \\ B, + \\ C, + \\}; + \\export fn entry() { + \\ var b = Letter.B; + \\} + , + ".tmp_source.zig:2:8: error: structs and unions, not enums, support field types", + ".tmp_source.zig:1:16: note: consider 'union(enum)' here"); + + cases.add("struct field missing type", + \\const Letter = struct { + \\ A, + \\}; + \\export fn entry() { + \\ var a = Letter { .A = {} }; + \\} + , + ".tmp_source.zig:2:5: error: struct field missing type"); + + cases.add("extern union field missing type", + \\const Letter = extern union { + \\ A, + \\}; + \\export fn entry() { + \\ var a = Letter { .A = {} }; + \\} + , + ".tmp_source.zig:2:5: error: union field missing type"); + + cases.add("extern union given enum tag type", + \\const Letter = enum { + \\ A, + \\ B, + \\ C, + \\}; + \\const Payload = extern union(Letter) { + \\ A: i32, + \\ B: f64, + \\ C: bool, + \\}; + \\export fn entry() { + \\ var a = Payload { .A = { 1234 } }; + \\} + , + ".tmp_source.zig:6:29: error: extern union does not support enum tag type"); + + cases.add("packed union given enum tag type", + \\const Letter = enum { + \\ A, + \\ B, + \\ C, + \\}; + \\const Payload = packed union(Letter) { + \\ A: i32, + \\ B: f64, + \\ C: bool, + \\}; + \\export fn entry() { + \\ var a = Payload { .A = { 1234 } }; + \\} + , + ".tmp_source.zig:6:29: error: packed union does not support enum tag type"); + + cases.add("switch on union with no attached enum", + \\const Payload = union { + \\ A: i32, + \\ B: f64, + \\ C: bool, + \\}; + \\export fn entry() { + \\ const a = Payload { .A = { 1234 } }; + \\ foo(a); + \\} + \\fn foo(a: &const Payload) { + \\ switch (*a) { + \\ Payload.A => {}, + \\ else => unreachable, + \\ } + \\} + , + ".tmp_source.zig:11:13: error: switch on union which has no attached enum", + ".tmp_source.zig:1:17: note: consider 'union(enum)' here"); } From 942b250895581f01adf52b3d5addd99b7a4bc0d3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 4 Dec 2017 01:42:02 -0500 Subject: [PATCH 26/32] update docs regarding enums and unions --- doc/langref.html.in | 211 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 163 insertions(+), 48 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 76bf0ce237..e5b896410d 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -2096,30 +2096,38 @@ const Type = enum { NotOk, }; -// Enums are sum types, and can hold more complex data of different types. -const ComplexType = enum { - Ok: u8, - NotOk: void, -}; - // Declare a specific instance of the enum variant. -const c = ComplexType.Ok { 0 }; +const c = Type.Ok; -// The ordinal value of a simple enum with no data members can be -// retrieved by a simple cast. -// The value starts from 0, counting up for each member. -const Value = enum { +// If you want access to the ordinal value of an enum, you +// can specify the tag type. +const Value = enum(u2) { Zero, One, Two, }; + +// Now you can cast between u2 and Value. +// The ordinal value starts from 0, counting up for each member. test "enum ordinal value" { - assert(usize(Value.Zero) == 0); - assert(usize(Value.One) == 1); - assert(usize(Value.Two) == 2); + assert(u2(Value.Zero) == 0); + assert(u2(Value.One) == 1); + assert(u2(Value.Two) == 2); } -// Enums can have methods, the same as structs. +// You can override the ordinal value for an enum. +const Value2 = enum(u32) { + Hundred = 100, + Thousand = 1000, + Million = 1000000, +}; +test "set enum ordinal value" { + assert(u32(Value2.Hundred) == 100); + assert(u32(Value2.Thousand) == 1000); + assert(u32(Value2.Million) == 1000000); +} + +// Enums can have methods, the same as structs and unions. // Enum methods are not special, they are only namespaced // functions that you can call with dot syntax. const Suit = enum { @@ -2128,26 +2136,120 @@ const Suit = enum { Diamonds, Hearts, - pub fn ordinal(self: &const Suit) -> u8 { - u8(*self) + pub fn isClubs(self: Suit) -> bool { + return self == Suit.Clubs; } }; test "enum method" { const p = Suit.Spades; - assert(p.ordinal() == 1); + assert(!p.isClubs()); } // An enum variant of different types can be switched upon. -// The associated data can be retrieved using `|...|` syntax. -// -// A void type is not required on a tag-only member. const Foo = enum { - String: []const u8, - Number: u64, + String, + Number, None, }; test "enum variant switch" { - const p = Foo.Number { 54 }; + const p = Foo.Number; + const what_is_it = switch (p) { + Foo.String => "this is a string", + Foo.Number => "this is a number", + Foo.None => "this is a none", + }; + assert(mem.eql(u8, what_is_it, "this is a number")); +} + +// @TagType can be used to access the integer tag type of an enum. +const Small = enum { + One, + Two, + Three, + Four, +}; +test "@TagType" { + assert(@TagType(Small) == u2); +} + +// @memberCount tells how many fields an enum has: +test "@memberCount" { + assert(@memberCount(Small) == 4); +} + +// @memberName tells the name of a field in an enum: +test "@memberName" { + assert(mem.eql(u8, @memberName(Small, 1), "Two")); +} + +// @tagName gives a []const u8 representation of an enum value: +test "@tagName" { + assert(mem.eql(u8, @tagName(Small.Three), "Three")); +} +

        TODO extern enum

        +

        TODO packed enum

        +
        $ zig test enum.zig
        +Test 1/8 enum ordinal value...OK
        +Test 2/8 set enum ordinal value...OK
        +Test 3/8 enum method...OK
        +Test 4/8 enum variant switch...OK
        +Test 5/8 @TagType...OK
        +Test 6/8 @memberCount...OK
        +Test 7/8 @memberName...OK
        +Test 8/8 @tagName...OK
        +

        See also:

        + +

        union

        +
        const assert = @import("std").debug.assert;
        +const mem = @import("std").mem;
        +
        +// A union has only 1 active field at a time.
        +const Payload = union {
        +    Int: i64,
        +    Float: f64,
        +    Bool: bool,
        +};
        +test "simple union" {
        +    var payload = Payload {.Int = 1234};
        +    // payload.Float = 12.34; // ERROR! field not active
        +    assert(payload.Int == 1234);
        +    // You can activate another field by assigning the entire union.
        +    payload = Payload {.Float = 12.34};
        +    assert(payload.Float == 12.34);
        +}
        +
        +// Unions can be given an enum tag type:
        +const ComplexTypeTag = enum { Ok, NotOk }; 
        +const ComplexType = union(ComplexTypeTag) {
        +    Ok: u8,
        +    NotOk: void,
        +};
        +
        +// Declare a specific instance of the union variant.
        +test "declare union value" {
        +    const c = ComplexType { .Ok = 0 };
        +    assert(ComplexTypeTag(c) == ComplexTypeTag.Ok);
        +}
        +
        +// @TagType can be used to access the enum tag type of a union.
        +test "@TagType" {
        +    assert(@TagType(ComplexType) == ComplexTypeTag);
        +}
        +
        +// Unions can be made to infer the enum tag type.
        +const Foo = union(enum) {
        +    String: []const u8,
        +    Number: u64,
        +
        +    // void can be omitted when inferring enum tag type.
        +    None,
        +};
        +test "union variant switch" {
        +    const p = Foo { .Number = 54 };
             const what_is_it = switch (p) {
                 // Capture by reference
                 Foo.String => |*x| {
        @@ -2156,6 +2258,7 @@ test "enum variant switch" {
         
                 // Capture by value
                 Foo.Number => |x| {
        +            assert(x == 54);
                     "this is a number"
                 },
         
        @@ -2163,38 +2266,50 @@ test "enum variant switch" {
                     "this is a none"
                 }
             };
        +    assert(mem.eql(u8, what_is_it, "this is a number"));
         }
         
        -// The @memberName and @memberCount builtin functions can be used to
        -// the string representation and number of members respectively.
        -const BuiltinType = enum {
        -    A: f32,
        -    B: u32,
        -    C,
        +// TODO union methods
        +
        +
        +const Small = union {
        +    A: i32,
        +    B: bool,
        +    C: u8,
         };
         
        -test "enum builtins" {
        -    assert(mem.eql(u8, @memberName(BuiltinType.A { 0 }), "A"));
        -    assert(mem.eql(u8, @memberName(BuiltinType.C), "C"));
        -    assert(@memberCount(BuiltinType) == 3);
        +// @memberCount tells how many fields a union has:
        +test "@memberCount" {
        +    assert(@memberCount(Small) == 3);
        +}
        +
        +// @memberName tells the name of a field in an enum:
        +test "@memberName" {
        +    assert(mem.eql(u8, @memberName(Small, 1), "B"));
        +}
        +
        +// @tagName gives a []const u8 representation of an enum value,
        +// but only if the union has an enum tag type.
        +const Small2 = union(enum) {
        +    A: i32,
        +    B: bool,
        +    C: u8,
        +};
        +test "@tagName" {
        +    assert(mem.eql(u8, @tagName(Small2.C), "C"));
         }
        -
        $ zig test enum.zig
        -Test 1/4 enum ordinal value...OK
        -Test 2/4 enum method...OK
        -Test 3/4 enum variant switch...OK
        -Test 4/4 enum builtins...OK
        +
        $ zig test union.zig
        +Test 1/7 simple union...OK
        +Test 2/7 declare union value...OK
        +Test 3/7 @TagType...OK
        +Test 4/7 union variant switch...OK
        +Test 5/7 @memberCount...OK
        +Test 6/7 @memberName...OK
        +Test 7/7 @tagName...OK

        - Enums are generated as a struct with a tag field and union field. Zig + Unions with an enum tag are generated as a struct with a tag field and union field. Zig sorts the order of the tag and union field by the largest alignment.

        -

        See also:

        - -

        union

        -

        TODO union documentation

        switch

        const assert = @import("std").debug.assert;
         const builtin = @import("builtin");
        
        From 084911d9b376fa9bea2567f53854511adc6d07ec Mon Sep 17 00:00:00 2001
        From: Andrew Kelley 
        Date: Mon, 4 Dec 2017 02:04:08 -0500
        Subject: [PATCH 27/32] add test for @sizeOf on extern and packed unions
        
        ---
         test/cases/union.zig | 16 ++++++++++++++++
         1 file changed, 16 insertions(+)
        
        diff --git a/test/cases/union.zig b/test/cases/union.zig
        index 291384eaeb..fb0ca1238d 100644
        --- a/test/cases/union.zig
        +++ b/test/cases/union.zig
        @@ -150,3 +150,19 @@ fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: &const MultipleChoice2) {
             });
         }
         
        +
        +const ExternPtrOrInt = extern union {
        +    ptr: &u8,
        +    int: u64
        +};
        +test "extern union size" {
        +    comptime assert(@sizeOf(ExternPtrOrInt) == 8);
        +}
        +
        +const PackedPtrOrInt = packed union {
        +    ptr: &u8,
        +    int: u64
        +};
        +test "extern union size" {
        +    comptime assert(@sizeOf(PackedPtrOrInt) == 8);
        +}
        
        From 54138d9e82d545094a85435ef862a6d4894e859e Mon Sep 17 00:00:00 2001
        From: Andrew Kelley 
        Date: Mon, 4 Dec 2017 02:05:33 -0500
        Subject: [PATCH 28/32] add test for union with 1 void field being 0 bits
        
        ---
         test/cases/union.zig | 7 +++++++
         1 file changed, 7 insertions(+)
        
        diff --git a/test/cases/union.zig b/test/cases/union.zig
        index fb0ca1238d..13bc084551 100644
        --- a/test/cases/union.zig
        +++ b/test/cases/union.zig
        @@ -166,3 +166,10 @@ const PackedPtrOrInt = packed union {
         test "extern union size" {
             comptime assert(@sizeOf(PackedPtrOrInt) == 8);
         }
        +
        +const ZeroBits = union {
        +    OnlyField: void,
        +};
        +test "union with only 1 field which is void should be zero bits" {
        +    comptime assert(@sizeOf(ZeroBits) == 0);
        +}
        
        From dd3437d5ba30c2101719fa38a95914fae5d965dd Mon Sep 17 00:00:00 2001
        From: Andrew Kelley 
        Date: Mon, 4 Dec 2017 02:08:26 -0500
        Subject: [PATCH 29/32] fix build on windows
        
        ---
         std/os/child_process.zig | 4 ++--
         1 file changed, 2 insertions(+), 2 deletions(-)
        
        diff --git a/std/os/child_process.zig b/std/os/child_process.zig
        index 3a4c9410c5..0548592f56 100644
        --- a/std/os/child_process.zig
        +++ b/std/os/child_process.zig
        @@ -213,9 +213,9 @@ pub const ChildProcess = struct {
                 self.term = (%Term)({
                     var exit_code: windows.DWORD = undefined;
                     if (windows.GetExitCodeProcess(self.handle, &exit_code) == 0) {
        -                Term.Unknown{0}
        +                Term { .Unknown = 0 }
                     } else {
        -                Term.Exited {@bitCast(i32, exit_code)}
        +                Term { .Exited = @bitCast(i32, exit_code)}
                     }
                 });
         
        
        From 76f3bdfff8224144e7b57748eb8eda2001d0308d Mon Sep 17 00:00:00 2001
        From: Andrew Kelley 
        Date: Mon, 4 Dec 2017 02:12:13 -0500
        Subject: [PATCH 30/32] add test for casting union to tag type of union
        
        ---
         test/cases/union.zig | 17 +++++++++++++++++
         1 file changed, 17 insertions(+)
        
        diff --git a/test/cases/union.zig b/test/cases/union.zig
        index 13bc084551..1db9a1bef1 100644
        --- a/test/cases/union.zig
        +++ b/test/cases/union.zig
        @@ -173,3 +173,20 @@ const ZeroBits = union {
         test "union with only 1 field which is void should be zero bits" {
             comptime assert(@sizeOf(ZeroBits) == 0);
         }
        +
        +const TheTag = enum {A, B, C};
        +const TheUnion = union(TheTag) { A: i32, B: i32, C: i32 };
        +test "union field access gives the enum values" {
        +    assert(TheUnion.A == TheTag.A);
        +    assert(TheUnion.B == TheTag.B);
        +    assert(TheUnion.C == TheTag.C);
        +}
        +
        +test "cast union to tag type of union" {
        +    testCastUnionToTagType(TheUnion {.B = 1234});
        +    comptime testCastUnionToTagType(TheUnion {.B = 1234});
        +}
        +
        +fn testCastUnionToTagType(x: &const TheUnion) {
        +    assert(TheTag(*x) == TheTag.B);
        +}
        
        From fea016afc0c1102a4847f4e5676260b14d81096e Mon Sep 17 00:00:00 2001
        From: MIURA Masahiro 
        Date: Mon, 4 Dec 2017 19:22:34 +0900
        Subject: [PATCH 31/32] Fix the color of compiler messages for light-themed
         terminal.
        
        ---
         src/errmsg.cpp | 8 ++++----
         src/os.cpp     | 5 +++++
         src/os.hpp     | 1 +
         3 files changed, 10 insertions(+), 4 deletions(-)
        
        diff --git a/src/errmsg.cpp b/src/errmsg.cpp
        index 01c3ee8429..edbfd858a8 100644
        --- a/src/errmsg.cpp
        +++ b/src/errmsg.cpp
        @@ -24,20 +24,20 @@ static void print_err_msg_type(ErrorMsg *err, ErrColor color, ErrType err_type)
             bool is_tty = os_stderr_tty();
             if (color == ErrColorOn || (color == ErrColorAuto && is_tty)) {
                 if (err_type == ErrTypeError) {
        -            os_stderr_set_color(TermColorWhite);
        +            os_stderr_set_color(TermColorBold);
                     fprintf(stderr, "%s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize ": ", path, line, col);
                     os_stderr_set_color(TermColorRed);
                     fprintf(stderr, "error:");
        -            os_stderr_set_color(TermColorWhite);
        +            os_stderr_set_color(TermColorBold);
                     fprintf(stderr, " %s", text);
                     os_stderr_set_color(TermColorReset);
                     fprintf(stderr, "\n");
                 } else if (err_type == ErrTypeNote) {
        -            os_stderr_set_color(TermColorWhite);
        +            os_stderr_set_color(TermColorBold);
                     fprintf(stderr, "%s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize ": ", path, line, col);
                     os_stderr_set_color(TermColorCyan);
                     fprintf(stderr, "note:");
        -            os_stderr_set_color(TermColorWhite);
        +            os_stderr_set_color(TermColorBold);
                     fprintf(stderr, " %s", text);
                     os_stderr_set_color(TermColorReset);
                     fprintf(stderr, "\n");
        diff --git a/src/os.cpp b/src/os.cpp
        index 6c242bc350..a59fa12657 100644
        --- a/src/os.cpp
        +++ b/src/os.cpp
        @@ -931,6 +931,7 @@ int os_self_exe_path(Buf *out_path) {
         #define VT_GREEN "\x1b[32;1m"
         #define VT_CYAN "\x1b[36;1m"
         #define VT_WHITE "\x1b[37;1m"
        +#define VT_BOLD "\x1b[0;1m"
         #define VT_RESET "\x1b[0m"
         
         static void set_color_posix(TermColor color) {
        @@ -947,6 +948,9 @@ static void set_color_posix(TermColor color) {
                 case TermColorWhite:
                     fprintf(stderr, VT_WHITE);
                     break;
        +        case TermColorBold:
        +            fprintf(stderr, VT_BOLD);
        +            break;
                 case TermColorReset:
                     fprintf(stderr, VT_RESET);
                     break;
        @@ -989,6 +993,7 @@ void os_stderr_set_color(TermColor color) {
                     SetConsoleTextAttribute(stderr_handle, FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_INTENSITY);
                     break;
                 case TermColorWhite:
        +        case TermColorBold:
                     SetConsoleTextAttribute(stderr_handle,
                         FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_INTENSITY);
                     break;
        diff --git a/src/os.hpp b/src/os.hpp
        index 611779f7c8..d4d1676df6 100644
        --- a/src/os.hpp
        +++ b/src/os.hpp
        @@ -21,6 +21,7 @@ enum TermColor {
             TermColorGreen,
             TermColorCyan,
             TermColorWhite,
        +    TermColorBold,
             TermColorReset,
         };
         
        
        From a966275e509670e750ef54a37f7202078aa4cf07 Mon Sep 17 00:00:00 2001
        From: Andrew Kelley 
        Date: Mon, 4 Dec 2017 10:35:55 -0500
        Subject: [PATCH 32/32] rename builtin.is_big_endian to builtin.endian
        
        See #307
        ---
         example/guess_number/main.zig       |  3 +-
         src/codegen.cpp                     | 14 ++++-
         std/debug.zig                       | 18 +++----
         std/elf.zig                         | 79 +++++++++++++++--------------
         std/endian.zig                      |  4 +-
         std/io.zig                          | 12 ++---
         std/mem.zig                         | 69 ++++++++++++++-----------
         std/os/child_process.zig            |  4 +-
         std/rand.zig                        |  6 +--
         std/special/compiler_rt/udivmod.zig |  2 +-
         10 files changed, 116 insertions(+), 95 deletions(-)
        
        diff --git a/example/guess_number/main.zig b/example/guess_number/main.zig
        index 20a2fefc9f..db7e38ada3 100644
        --- a/example/guess_number/main.zig
        +++ b/example/guess_number/main.zig
        @@ -1,3 +1,4 @@
        +const builtin = @import("builtin");
         const std = @import("std");
         const io = std.io;
         const fmt = std.fmt;
        @@ -15,7 +16,7 @@ pub fn main() -> %void {
         
             var seed_bytes: [@sizeOf(usize)]u8 = undefined;
             %%os.getRandomBytes(seed_bytes[0..]);
        -    const seed = std.mem.readInt(seed_bytes, usize, true);
        +    const seed = std.mem.readInt(seed_bytes, usize, builtin.Endian.Big);
             var rand = Rand.init(seed);
         
             const answer = rand.range(u8, 0, 100) + 1;
        diff --git a/src/codegen.cpp b/src/codegen.cpp
        index bd84310ff1..e10d260e6e 100644
        --- a/src/codegen.cpp
        +++ b/src/codegen.cpp
        @@ -5129,7 +5129,19 @@ static void define_builtin_compile_vars(CodeGen *g) {
                 assert(FloatModeOptimized == 0);
                 assert(FloatModeStrict == 1);
             }
        -    buf_appendf(contents, "pub const is_big_endian = %s;\n", bool_to_str(g->is_big_endian));
        +    {
        +        buf_appendf(contents,
        +            "pub const Endian = enum {\n"
        +            "    Big,\n"
        +            "    Little,\n"
        +            "};\n\n");
        +        assert(FloatModeOptimized == 0);
        +        assert(FloatModeStrict == 1);
        +    }
        +    {
        +        const char *endian_str = g->is_big_endian ? "Endian.Big" : "Endian.Little";
        +        buf_appendf(contents, "pub const endian = %s;\n", endian_str);
        +    }
             buf_appendf(contents, "pub const is_test = %s;\n", bool_to_str(g->is_test_build));
             buf_appendf(contents, "pub const os = Os.%s;\n", cur_os);
             buf_appendf(contents, "pub const arch = Arch.%s;\n", cur_arch);
        diff --git a/std/debug.zig b/std/debug.zig
        index 5bfa436614..a2bea9eddd 100644
        --- a/std/debug.zig
        +++ b/std/debug.zig
        @@ -303,7 +303,7 @@ const Constant = struct {
                     return error.InvalidDebugInfo;
                 if (self.signed)
                     return error.InvalidDebugInfo;
        -        return mem.readInt(self.payload, u64, false);
        +        return mem.readInt(self.payload, u64, builtin.Endian.Little);
             }
         };
         
        @@ -479,7 +479,7 @@ fn parseFormValueBlockLen(allocator: &mem.Allocator, in_stream: &io.InStream, si
         }
         
         fn parseFormValueBlock(allocator: &mem.Allocator, in_stream: &io.InStream, size: usize) -> %FormValue {
        -    const block_len = %return in_stream.readVarInt(false, usize, size);
        +    const block_len = %return in_stream.readVarInt(builtin.Endian.Little, usize, size);
             return parseFormValueBlockLen(allocator, in_stream, block_len);
         }
         
        @@ -669,10 +669,10 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe
                     continue;
                 }
         
        -        const version = %return in_stream.readInt(st.elf.is_big_endian, u16);
        +        const version = %return in_stream.readInt(st.elf.endian, u16);
                 if (version != 2) return error.InvalidDebugInfo;
         
        -        const prologue_length = %return in_stream.readInt(st.elf.is_big_endian, u32);
        +        const prologue_length = %return in_stream.readInt(st.elf.endian, u32);
                 const prog_start_offset = (%return in_file.getPos()) + prologue_length;
         
                 const minimum_instruction_length = %return in_stream.readByte();
        @@ -739,7 +739,7 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe
                                 return error.MissingDebugInfo;
                             },
                             DW.LNE_set_address => {
        -                        const addr = %return in_stream.readInt(st.elf.is_big_endian, usize);
        +                        const addr = %return in_stream.readInt(st.elf.endian, usize);
                                 prog.address = addr;
                             },
                             DW.LNE_define_file => {
        @@ -801,7 +801,7 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe
                                 prog.address += inc_addr;
                             },
                             DW.LNS_fixed_advance_pc => {
        -                        const arg = %return in_stream.readInt(st.elf.is_big_endian, u16);
        +                        const arg = %return in_stream.readInt(st.elf.endian, u16);
                                 prog.address += arg;
                             },
                             DW.LNS_set_prologue_end => {
        @@ -839,13 +839,13 @@ fn scanAllCompileUnits(st: &ElfStackTrace) -> %void {
                     return;
                 const next_offset = unit_length + (if (is_64) usize(12) else usize(4));
         
        -        const version = %return in_stream.readInt(st.elf.is_big_endian, u16);
        +        const version = %return in_stream.readInt(st.elf.endian, u16);
                 if (version < 2 or version > 5) return error.InvalidDebugInfo;
         
                 const debug_abbrev_offset = if (is_64) {
        -            %return in_stream.readInt(st.elf.is_big_endian, u64)
        +            %return in_stream.readInt(st.elf.endian, u64)
                 } else {
        -            %return in_stream.readInt(st.elf.is_big_endian, u32)
        +            %return in_stream.readInt(st.elf.endian, u32)
                 };
         
                 const address_size = %return in_stream.readByte();
        diff --git a/std/elf.zig b/std/elf.zig
        index f3f7de512f..f7be236d15 100644
        --- a/std/elf.zig
        +++ b/std/elf.zig
        @@ -1,3 +1,4 @@
        +const builtin = @import("builtin");
         const std = @import("index.zig");
         const io = std.io;
         const math = std.math;
        @@ -67,7 +68,7 @@ pub const Elf = struct {
             in_file: &io.File,
             auto_close_stream: bool,
             is_64: bool,
        -    is_big_endian: bool,
        +    endian: builtin.Endian,
             file_type: FileType,
             arch: Arch,
             entry_addr: u64,
        @@ -105,9 +106,9 @@ pub const Elf = struct {
                     else => return error.InvalidFormat,
                 };
         
        -        elf.is_big_endian = switch (%return in.readByte()) {
        -            1 => false,
        -            2 => true,
        +        elf.endian = switch (%return in.readByte()) {
        +            1 => builtin.Endian.Little,
        +            2 => builtin.Endian.Big,
                     else => return error.InvalidFormat,
                 };
         
        @@ -117,7 +118,7 @@ pub const Elf = struct {
                 // skip over padding
                 %return elf.in_file.seekForward(9);
         
        -        elf.file_type = switch (%return in.readInt(elf.is_big_endian, u16)) {
        +        elf.file_type = switch (%return in.readInt(elf.endian, u16)) {
                     1 => FileType.Relocatable,
                     2 => FileType.Executable,
                     3 => FileType.Shared,
        @@ -125,7 +126,7 @@ pub const Elf = struct {
                     else => return error.InvalidFormat,
                 };
         
        -        elf.arch = switch (%return in.readInt(elf.is_big_endian, u16)) {
        +        elf.arch = switch (%return in.readInt(elf.endian, u16)) {
                     0x02 => Arch.Sparc,
                     0x03 => Arch.x86,
                     0x08 => Arch.Mips,
        @@ -138,34 +139,34 @@ pub const Elf = struct {
                     else => return error.InvalidFormat,
                 };
         
        -        const elf_version = %return in.readInt(elf.is_big_endian, u32);
        +        const elf_version = %return in.readInt(elf.endian, u32);
                 if (elf_version != 1) return error.InvalidFormat;
         
                 if (elf.is_64) {
        -            elf.entry_addr = %return in.readInt(elf.is_big_endian, u64);
        -            elf.program_header_offset = %return in.readInt(elf.is_big_endian, u64);
        -            elf.section_header_offset = %return in.readInt(elf.is_big_endian, u64);
        +            elf.entry_addr = %return in.readInt(elf.endian, u64);
        +            elf.program_header_offset = %return in.readInt(elf.endian, u64);
        +            elf.section_header_offset = %return in.readInt(elf.endian, u64);
                 } else {
        -            elf.entry_addr = u64(%return in.readInt(elf.is_big_endian, u32));
        -            elf.program_header_offset = u64(%return in.readInt(elf.is_big_endian, u32));
        -            elf.section_header_offset = u64(%return in.readInt(elf.is_big_endian, u32));
        +            elf.entry_addr = u64(%return in.readInt(elf.endian, u32));
        +            elf.program_header_offset = u64(%return in.readInt(elf.endian, u32));
        +            elf.section_header_offset = u64(%return in.readInt(elf.endian, u32));
                 }
         
                 // skip over flags
                 %return elf.in_file.seekForward(4);
         
        -        const header_size = %return in.readInt(elf.is_big_endian, u16);
        +        const header_size = %return in.readInt(elf.endian, u16);
                 if ((elf.is_64 and header_size != 64) or
                     (!elf.is_64 and header_size != 52))
                 {
                     return error.InvalidFormat;
                 }
         
        -        const ph_entry_size = %return in.readInt(elf.is_big_endian, u16);
        -        const ph_entry_count = %return in.readInt(elf.is_big_endian, u16);
        -        const sh_entry_size = %return in.readInt(elf.is_big_endian, u16);
        -        const sh_entry_count = %return in.readInt(elf.is_big_endian, u16);
        -        elf.string_section_index = u64(%return in.readInt(elf.is_big_endian, u16));
        +        const ph_entry_size = %return in.readInt(elf.endian, u16);
        +        const ph_entry_count = %return in.readInt(elf.endian, u16);
        +        const sh_entry_size = %return in.readInt(elf.endian, u16);
        +        const sh_entry_count = %return in.readInt(elf.endian, u16);
        +        elf.string_section_index = u64(%return in.readInt(elf.endian, u16));
         
                 if (elf.string_section_index >= sh_entry_count) return error.InvalidFormat;
         
        @@ -188,32 +189,32 @@ pub const Elf = struct {
                     if (sh_entry_size != 64) return error.InvalidFormat;
         
                     for (elf.section_headers) |*section| {
        -                section.name         = %return in.readInt(elf.is_big_endian, u32);
        -                section.sh_type      = %return in.readInt(elf.is_big_endian, u32);
        -                section.flags        = %return in.readInt(elf.is_big_endian, u64);
        -                section.addr         = %return in.readInt(elf.is_big_endian, u64);
        -                section.offset       = %return in.readInt(elf.is_big_endian, u64);
        -                section.size         = %return in.readInt(elf.is_big_endian, u64);
        -                section.link         = %return in.readInt(elf.is_big_endian, u32);
        -                section.info         = %return in.readInt(elf.is_big_endian, u32);
        -                section.addr_align   = %return in.readInt(elf.is_big_endian, u64);
        -                section.ent_size     = %return in.readInt(elf.is_big_endian, u64);
        +                section.name         = %return in.readInt(elf.endian, u32);
        +                section.sh_type      = %return in.readInt(elf.endian, u32);
        +                section.flags        = %return in.readInt(elf.endian, u64);
        +                section.addr         = %return in.readInt(elf.endian, u64);
        +                section.offset       = %return in.readInt(elf.endian, u64);
        +                section.size         = %return in.readInt(elf.endian, u64);
        +                section.link         = %return in.readInt(elf.endian, u32);
        +                section.info         = %return in.readInt(elf.endian, u32);
        +                section.addr_align   = %return in.readInt(elf.endian, u64);
        +                section.ent_size     = %return in.readInt(elf.endian, u64);
                     }
                 } else {
                     if (sh_entry_size != 40) return error.InvalidFormat;
         
                     for (elf.section_headers) |*section| {
                         // TODO (multiple occurences) allow implicit cast from %u32 -> %u64 ?
        -                section.name = %return in.readInt(elf.is_big_endian, u32);
        -                section.sh_type = %return in.readInt(elf.is_big_endian, u32);
        -                section.flags = u64(%return in.readInt(elf.is_big_endian, u32));
        -                section.addr = u64(%return in.readInt(elf.is_big_endian, u32));
        -                section.offset = u64(%return in.readInt(elf.is_big_endian, u32));
        -                section.size = u64(%return in.readInt(elf.is_big_endian, u32));
        -                section.link = %return in.readInt(elf.is_big_endian, u32);
        -                section.info = %return in.readInt(elf.is_big_endian, u32);
        -                section.addr_align = u64(%return in.readInt(elf.is_big_endian, u32));
        -                section.ent_size = u64(%return in.readInt(elf.is_big_endian, u32));
        +                section.name = %return in.readInt(elf.endian, u32);
        +                section.sh_type = %return in.readInt(elf.endian, u32);
        +                section.flags = u64(%return in.readInt(elf.endian, u32));
        +                section.addr = u64(%return in.readInt(elf.endian, u32));
        +                section.offset = u64(%return in.readInt(elf.endian, u32));
        +                section.size = u64(%return in.readInt(elf.endian, u32));
        +                section.link = %return in.readInt(elf.endian, u32);
        +                section.info = %return in.readInt(elf.endian, u32);
        +                section.addr_align = u64(%return in.readInt(elf.endian, u32));
        +                section.ent_size = u64(%return in.readInt(elf.endian, u32));
                     }
                 }
         
        diff --git a/std/endian.zig b/std/endian.zig
        index 8ae8ae22f8..2dc6b8d34e 100644
        --- a/std/endian.zig
        +++ b/std/endian.zig
        @@ -9,8 +9,8 @@ pub fn swapIfBe(comptime T: type, x: T) -> T {
             swapIf(true, T, x)
         }
         
        -pub fn swapIf(is_be: bool, comptime T: type, x: T) -> T {
        -    if (builtin.is_big_endian == is_be) swap(T, x) else x
        +pub fn swapIf(endian: builtin.Endian, comptime T: type, x: T) -> T {
        +    if (builtin.endian == endian) swap(T, x) else x
         }
         
         pub fn swap(comptime T: type, x: T) -> T {
        diff --git a/std/io.zig b/std/io.zig
        index 6c6c171997..c86ebed326 100644
        --- a/std/io.zig
        +++ b/std/io.zig
        @@ -441,26 +441,26 @@ pub const InStream = struct {
             }
         
             pub fn readIntLe(self: &InStream, comptime T: type) -> %T {
        -        return self.readInt(false, T);
        +        return self.readInt(builtin.Endian.Little, T);
             }
         
             pub fn readIntBe(self: &InStream, comptime T: type) -> %T {
        -        return self.readInt(true, T);
        +        return self.readInt(builtin.Endian.Big, T);
             }
         
        -    pub fn readInt(self: &InStream, is_be: bool, comptime T: type) -> %T {
        +    pub fn readInt(self: &InStream, endian: builtin.Endian, comptime T: type) -> %T {
                 var bytes: [@sizeOf(T)]u8 = undefined;
                 %return self.readNoEof(bytes[0..]);
        -        return mem.readInt(bytes, T, is_be);
        +        return mem.readInt(bytes, T, endian);
             }
         
        -    pub fn readVarInt(self: &InStream, is_be: bool, comptime T: type, size: usize) -> %T {
        +    pub fn readVarInt(self: &InStream, endian: builtin.Endian, comptime T: type, size: usize) -> %T {
                 assert(size <= @sizeOf(T));
                 assert(size <= 8);
                 var input_buf: [8]u8 = undefined;
                 const input_slice = input_buf[0..size];
                 %return self.readNoEof(input_slice);
        -        return mem.readInt(input_slice, T, is_be);
        +        return mem.readInt(input_slice, T, endian);
             }
         
         
        diff --git a/std/mem.zig b/std/mem.zig
        index 9d78b1513b..815e122812 100644
        --- a/std/mem.zig
        +++ b/std/mem.zig
        @@ -1,6 +1,7 @@
         const debug = @import("debug.zig");
         const assert = debug.assert;
         const math = @import("math/index.zig");
        +const builtin = @import("builtin");
         
         pub const Cmp = math.Cmp;
         
        @@ -181,20 +182,23 @@ test "mem.indexOf" {
         /// T specifies the return type, which must be large enough to store
         /// the result.
         /// See also ::readIntBE or ::readIntLE.
        -pub fn readInt(bytes: []const u8, comptime T: type, big_endian: bool) -> T {
        +pub fn readInt(bytes: []const u8, comptime T: type, endian: builtin.Endian) -> T {
             if (T.bit_count == 8) {
                 return bytes[0];
             }
             var result: T = 0;
        -    if (big_endian) {
        -        for (bytes) |b| {
        -            result = (result << 8) | b;
        -        }
        -    } else {
        -        const ShiftType = math.Log2Int(T);
        -        for (bytes) |b, index| {
        -            result = result | (T(b) << ShiftType(index * 8));
        -        }
        +    switch (endian) {
        +        builtin.Endian.Big => {
        +            for (bytes) |b| {
        +                result = (result << 8) | b;
        +            }
        +        },
        +        builtin.Endian.Little => {
        +            const ShiftType = math.Log2Int(T);
        +            for (bytes) |b, index| {
        +                result = result | (T(b) << ShiftType(index * 8));
        +            }
        +        },
             }
             return result;
         }
        @@ -230,22 +234,25 @@ pub fn readIntLE(comptime T: type, bytes: []const u8) -> T {
         /// Writes an integer to memory with size equal to bytes.len. Pads with zeroes
         /// to fill the entire buffer provided.
         /// value must be an integer.
        -pub fn writeInt(buf: []u8, value: var, big_endian: bool) {
        +pub fn writeInt(buf: []u8, value: var, endian: builtin.Endian) {
             const uint = @IntType(false, @typeOf(value).bit_count);
             var bits = @truncate(uint, value);
        -    if (big_endian) {
        -        var index: usize = buf.len;
        -        while (index != 0) {
        -            index -= 1;
        +    switch (endian) {
        +        builtin.Endian.Big => {
        +            var index: usize = buf.len;
        +            while (index != 0) {
        +                index -= 1;
         
        -            buf[index] = @truncate(u8, bits);
        -            bits >>= 8;
        -        }
        -    } else {
        -        for (buf) |*b| {
        -            *b = @truncate(u8, bits);
        -            bits >>= 8;
        -        }
        +                buf[index] = @truncate(u8, bits);
        +                bits >>= 8;
        +            }
        +        },
        +        builtin.Endian.Little => {
        +            for (buf) |*b| {
        +                *b = @truncate(u8, bits);
        +                bits >>= 8;
        +            }
        +        },
             }
             assert(bits == 0);
         }
        @@ -377,21 +384,21 @@ test "testReadInt" {
         fn testReadIntImpl() {
             {
                 const bytes = []u8{ 0x12, 0x34, 0x56, 0x78 };
        -        assert(readInt(bytes, u32, true)  == 0x12345678);
        +        assert(readInt(bytes, u32, builtin.Endian.Big)  == 0x12345678);
                 assert(readIntBE(u32, bytes)      == 0x12345678);
                 assert(readIntBE(i32, bytes)      == 0x12345678);
        -        assert(readInt(bytes, u32, false) == 0x78563412);
        +        assert(readInt(bytes, u32, builtin.Endian.Little) == 0x78563412);
                 assert(readIntLE(u32, bytes)      == 0x78563412);
                 assert(readIntLE(i32, bytes)      == 0x78563412);
             }
             {
                 const buf = []u8{0x00, 0x00, 0x12, 0x34};
        -        const answer = readInt(buf, u64, true);
        +        const answer = readInt(buf, u64, builtin.Endian.Big);
                 assert(answer == 0x00001234);
             }
             {
                 const buf = []u8{0x12, 0x34, 0x00, 0x00};
        -        const answer = readInt(buf, u64, false);
        +        const answer = readInt(buf, u64, builtin.Endian.Little);
                 assert(answer == 0x00003412);
             }
             {
        @@ -410,16 +417,16 @@ test "testWriteInt" {
         fn testWriteIntImpl() {
             var bytes: [4]u8 = undefined;
         
        -    writeInt(bytes[0..], u32(0x12345678), true);
        +    writeInt(bytes[0..], u32(0x12345678), builtin.Endian.Big);
             assert(eql(u8, bytes, []u8{ 0x12, 0x34, 0x56, 0x78 }));
         
        -    writeInt(bytes[0..], u32(0x78563412), false);
        +    writeInt(bytes[0..], u32(0x78563412), builtin.Endian.Little);
             assert(eql(u8, bytes, []u8{ 0x12, 0x34, 0x56, 0x78 }));
         
        -    writeInt(bytes[0..], u16(0x1234), true);
        +    writeInt(bytes[0..], u16(0x1234), builtin.Endian.Big);
             assert(eql(u8, bytes, []u8{ 0x00, 0x00, 0x12, 0x34 }));
         
        -    writeInt(bytes[0..], u16(0x1234), false);
        +    writeInt(bytes[0..], u16(0x1234), builtin.Endian.Little);
             assert(eql(u8, bytes, []u8{ 0x34, 0x12, 0x00, 0x00 }));
         }
         
        diff --git a/std/os/child_process.zig b/std/os/child_process.zig
        index 0548592f56..75a2dcf24d 100644
        --- a/std/os/child_process.zig
        +++ b/std/os/child_process.zig
        @@ -722,14 +722,14 @@ const ErrInt = @IntType(false, @sizeOf(error) * 8);
         
         fn writeIntFd(fd: i32, value: ErrInt) -> %void {
             var bytes: [@sizeOf(ErrInt)]u8 = undefined;
        -    mem.writeInt(bytes[0..], value, true);
        +    mem.writeInt(bytes[0..], value, builtin.endian);
             os.posixWrite(fd, bytes[0..]) %% return error.SystemResources;
         }
         
         fn readIntFd(fd: i32) -> %ErrInt {
             var bytes: [@sizeOf(ErrInt)]u8 = undefined;
             os.posixRead(fd, bytes[0..]) %% return error.SystemResources;
        -    return mem.readInt(bytes[0..], ErrInt, true);
        +    return mem.readInt(bytes[0..], ErrInt, builtin.endian);
         }
         
         extern fn sigchld_handler(_: i32) {
        diff --git a/std/rand.zig b/std/rand.zig
        index d7798df3da..09e0c8ac78 100644
        --- a/std/rand.zig
        +++ b/std/rand.zig
        @@ -50,12 +50,12 @@ pub const Rand = struct {
             pub fn fillBytes(r: &Rand, buf: []u8) {
                 var bytes_left = buf.len;
                 while (bytes_left >= @sizeOf(usize)) {
        -            mem.writeInt(buf[buf.len - bytes_left..], r.rng.get(), false);
        +            mem.writeInt(buf[buf.len - bytes_left..], r.rng.get(), builtin.Endian.Little);
                     bytes_left -= @sizeOf(usize);
                 }
                 if (bytes_left > 0) {
                     var rand_val_array: [@sizeOf(usize)]u8 = undefined;
        -            mem.writeInt(rand_val_array[0..], r.rng.get(), false);
        +            mem.writeInt(rand_val_array[0..], r.rng.get(), builtin.Endian.Little);
                     while (bytes_left > 0) {
                         buf[buf.len - bytes_left] = rand_val_array[@sizeOf(usize) - bytes_left];
                         bytes_left -= 1;
        @@ -98,7 +98,7 @@ pub const Rand = struct {
         
                     while (true) {
                         r.fillBytes(rand_val_array[0..]);
        -                const rand_val = mem.readInt(rand_val_array, T, false);
        +                const rand_val = mem.readInt(rand_val_array, T, builtin.Endian.Little);
                         if (rand_val < upper_bound) {
                             return start + (rand_val % total_range);
                         }
        diff --git a/std/special/compiler_rt/udivmod.zig b/std/special/compiler_rt/udivmod.zig
        index c6a2babbf6..7e09c3d4d7 100644
        --- a/std/special/compiler_rt/udivmod.zig
        +++ b/std/special/compiler_rt/udivmod.zig
        @@ -1,7 +1,7 @@
         const builtin = @import("builtin");
         const is_test = builtin.is_test;
         
        -const low = if (builtin.is_big_endian) 1 else 0;
        +const low = switch (builtin.endian) { builtin.Endian.Big => 1, builtin.Endian.Little => 0 };
         const high = 1 - low;
         
         pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: ?&DoubleInt) -> DoubleInt {