diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml new file mode 100644 index 0000000000..db1ddb337a --- /dev/null +++ b/.builds/freebsd.yml @@ -0,0 +1,22 @@ +arch: x86_64 +image: freebsd +packages: + - cmake + - ninja + - llvm70 +sources: + - https://github.com/ziglang/zig.git +tasks: + - build: | + cd zig && mkdir build && cd build + cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release + ninja install + - test: | + cd zig/build + bin/zig test ../test/behavior.zig + # TODO enable all tests + #bin/zig build --build-file ../build.zig test + # TODO integrate with the download page updater and make a + # static build available to download for FreeBSD. + # This will require setting up a cache of LLVM/Clang built + # statically. diff --git a/doc/langref.html.in b/doc/langref.html.in index f41eb2dec1..2a2b4003fb 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -165,7 +165,7 @@ const std = @import("std"); pub fn main() !void { // If this program is run without stdout attached, exit with an error. - var stdout_file = try std.io.getStdOut(); + const stdout_file = try std.io.getStdOut(); // If this program encounters pipe failure when printing to stdout, exit // with an error. try stdout_file.write("Hello, world!\n"); diff --git a/example/hello_world/hello.zig b/example/hello_world/hello.zig index 8e65e06a96..cb7d5ef157 100644 --- a/example/hello_world/hello.zig +++ b/example/hello_world/hello.zig @@ -2,7 +2,7 @@ const std = @import("std"); pub fn main() !void { // If this program is run without stdout attached, exit with an error. - var stdout_file = try std.io.getStdOut(); + const stdout_file = try std.io.getStdOut(); // If this program encounters pipe failure when printing to stdout, exit // with an error. try stdout_file.write("Hello, world!\n"); diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index 0594cbd749..c00c7c1d41 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -300,6 +300,7 @@ pub const Compilation = struct { UserResourceLimitReached, InvalidUtf8, BadPathName, + DeviceBusy, }; pub const Event = union(enum) { diff --git a/src/analyze.cpp b/src/analyze.cpp index 064cf11cc1..63608e825b 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -2681,39 +2681,50 @@ static Error resolve_struct_alignment(CodeGen *g, ZigType *struct_type) { assert(decl_node->type == NodeTypeContainerDecl); assert(struct_type->di_type); + size_t field_count = struct_type->data.structure.src_field_count; if (struct_type->data.structure.layout == ContainerLayoutPacked) { struct_type->data.structure.abi_alignment = 1; - } - - size_t field_count = struct_type->data.structure.src_field_count; - for (size_t i = 0; i < field_count; i += 1) { - TypeStructField *field = &struct_type->data.structure.fields[i]; - - // If this assertion trips, look up the call stack. Probably something is - // calling type_resolve with ResolveStatusAlignmentKnown when it should only - // be resolving ResolveStatusZeroBitsKnown - assert(field->type_entry != nullptr); - - if (type_is_invalid(field->type_entry)) { - struct_type->data.structure.resolve_status = ResolveStatusInvalid; - break; + for (size_t i = 0; i < field_count; i += 1) { + TypeStructField *field = &struct_type->data.structure.fields[i]; + if (field->type_entry != nullptr && type_is_invalid(field->type_entry)) { + struct_type->data.structure.resolve_status = ResolveStatusInvalid; + break; + } } + } else for (size_t i = 0; i < field_count; i += 1) { + TypeStructField *field = &struct_type->data.structure.fields[i]; + uint32_t this_field_align; - if (!type_has_bits(field->type_entry)) - continue; + // TODO If we have no type_entry for the field, we've already failed to + // compile the program correctly. This stage1 compiler needs a deeper + // reworking to make this correct, or we can ignore the problem + // and make sure it is fixed in stage2. This workaround is for when + // there is a false positive of a dependency loop, of alignment depending + // on itself. When this false positive happens we assume a pointer-aligned + // field, which is usually fine but could be incorrectly over-aligned or + // even under-aligned. See https://github.com/ziglang/zig/issues/1512 + if (field->type_entry == nullptr) { + this_field_align = LLVMABIAlignmentOfType(g->target_data_ref, LLVMPointerType(LLVMInt8Type(), 0)); + } else { + if (type_is_invalid(field->type_entry)) { + struct_type->data.structure.resolve_status = ResolveStatusInvalid; + break; + } + + if (!type_has_bits(field->type_entry)) + continue; - // alignment of structs is the alignment of the most-aligned field - if (struct_type->data.structure.layout != ContainerLayoutPacked) { if ((err = type_resolve(g, field->type_entry, ResolveStatusAlignmentKnown))) { struct_type->data.structure.resolve_status = ResolveStatusInvalid; break; } - uint32_t this_field_align = get_abi_alignment(g, field->type_entry); + this_field_align = get_abi_alignment(g, field->type_entry); assert(this_field_align != 0); - if (this_field_align > struct_type->data.structure.abi_alignment) { - struct_type->data.structure.abi_alignment = this_field_align; - } + } + // alignment of structs is the alignment of the most-aligned field + if (this_field_align > struct_type->data.structure.abi_alignment) { + struct_type->data.structure.abi_alignment = this_field_align; } } diff --git a/src/ir.cpp b/src/ir.cpp index 76dd6c6391..83960f2eee 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -159,7 +159,7 @@ static ZigType *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *value, LVal lval); static ZigType *adjust_ptr_align(CodeGen *g, ZigType *ptr_type, uint32_t new_align); static ZigType *adjust_slice_align(CodeGen *g, ZigType *slice_type, uint32_t new_align); -static void buf_read_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue *val); +static Error buf_read_value_bytes(IrAnalyze *ira, AstNode *source_node, uint8_t *buf, ConstExprValue *val); static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue *val); static Error ir_read_const_ptr(IrAnalyze *ira, AstNode *source_node, ConstExprValue *out_val, ConstExprValue *ptr_val); @@ -12495,6 +12495,7 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruct case ReqCompTimeNo: if (casted_init_value->value.special == ConstValSpecialStatic && casted_init_value->value.type->id == ZigTypeIdFn && + casted_init_value->value.data.x_ptr.special != ConstPtrSpecialHardCodedAddr && casted_init_value->value.data.x_ptr.data.fn.fn_entry->fn_inline == FnInlineAlways) { var_class_requires_const = true; @@ -13724,7 +13725,8 @@ static Error ir_read_const_ptr(IrAnalyze *ira, AstNode *source_node, Buf buf = BUF_INIT; buf_resize(&buf, src_size); buf_write_value_bytes(ira->codegen, (uint8_t*)buf_ptr(&buf), pointee); - buf_read_value_bytes(ira->codegen, (uint8_t*)buf_ptr(&buf), out_val); + if ((err = buf_read_value_bytes(ira, source_node, (uint8_t*)buf_ptr(&buf), out_val))) + return err; return ErrorNone; } @@ -13758,7 +13760,8 @@ static Error ir_read_const_ptr(IrAnalyze *ira, AstNode *source_node, ConstExprValue *elem_val = &array_val->data.x_array.data.s_none.elements[elem_index + i]; buf_write_value_bytes(ira->codegen, (uint8_t*)buf_ptr(&buf) + (i * elem_size), elem_val); } - buf_read_value_bytes(ira->codegen, (uint8_t*)buf_ptr(&buf), out_val); + if ((err = buf_read_value_bytes(ira, source_node, (uint8_t*)buf_ptr(&buf), out_val))) + return err; return ErrorNone; } case ConstPtrSpecialBaseStruct: @@ -20076,7 +20079,8 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue zig_unreachable(); } -static void buf_read_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue *val) { +static Error buf_read_value_bytes(IrAnalyze *ira, AstNode *source_node, uint8_t *buf, ConstExprValue *val) { + Error err; assert(val->special == ConstValSpecialStatic); switch (val->type->id) { case ZigTypeIdInvalid: @@ -20093,30 +20097,60 @@ static void buf_read_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue case ZigTypeIdPromise: zig_unreachable(); case ZigTypeIdVoid: - return; + return ErrorNone; case ZigTypeIdBool: val->data.x_bool = (buf[0] != 0); - return; + return ErrorNone; case ZigTypeIdInt: bigint_read_twos_complement(&val->data.x_bigint, buf, val->type->data.integral.bit_count, - codegen->is_big_endian, val->type->data.integral.is_signed); - return; + ira->codegen->is_big_endian, val->type->data.integral.is_signed); + return ErrorNone; case ZigTypeIdFloat: - float_read_ieee597(val, buf, codegen->is_big_endian); - return; + float_read_ieee597(val, buf, ira->codegen->is_big_endian); + return ErrorNone; case ZigTypeIdPointer: { val->data.x_ptr.special = ConstPtrSpecialHardCodedAddr; BigInt bn; - bigint_read_twos_complement(&bn, buf, codegen->builtin_types.entry_usize->data.integral.bit_count, - codegen->is_big_endian, false); + bigint_read_twos_complement(&bn, buf, ira->codegen->builtin_types.entry_usize->data.integral.bit_count, + ira->codegen->is_big_endian, false); val->data.x_ptr.data.hard_coded_addr.addr = bigint_as_unsigned(&bn); - return; + return ErrorNone; } case ZigTypeIdArray: zig_panic("TODO buf_read_value_bytes array type"); case ZigTypeIdStruct: - zig_panic("TODO buf_read_value_bytes struct type"); + switch (val->type->data.structure.layout) { + case ContainerLayoutAuto: { + ErrorMsg *msg = ir_add_error_node(ira, source_node, + buf_sprintf("non-extern, non-packed struct '%s' cannot have its bytes reinterpreted", + buf_ptr(&val->type->name))); + add_error_note(ira->codegen, msg, val->type->data.structure.decl_node, + buf_sprintf("declared here")); + return ErrorSemanticAnalyzeFail; + } + case ContainerLayoutExtern: { + size_t src_field_count = val->type->data.structure.src_field_count; + val->data.x_struct.fields = create_const_vals(src_field_count); + for (size_t field_i = 0; field_i < src_field_count; field_i += 1) { + ConstExprValue *field_val = &val->data.x_struct.fields[field_i]; + field_val->special = ConstValSpecialStatic; + TypeStructField *type_field = &val->type->data.structure.fields[field_i]; + field_val->type = type_field->type_entry; + if (type_field->gen_index == SIZE_MAX) + continue; + size_t offset = LLVMOffsetOfElement(ira->codegen->target_data_ref, val->type->type_ref, + type_field->gen_index); + uint8_t *new_buf = buf + offset; + if ((err = buf_read_value_bytes(ira, source_node, new_buf, field_val))) + return err; + } + return ErrorNone; + } + case ContainerLayoutPacked: + zig_panic("TODO buf_read_value_bytes packed struct"); + } + zig_unreachable(); case ZigTypeIdOptional: zig_panic("TODO buf_read_value_bytes maybe type"); case ZigTypeIdErrorUnion: @@ -20219,7 +20253,8 @@ static IrInstruction *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruct IrInstruction *result = ir_const(ira, &instruction->base, dest_type); uint8_t *buf = allocate_nonzero(src_size_bytes); buf_write_value_bytes(ira->codegen, buf, val); - buf_read_value_bytes(ira->codegen, buf, &result->value); + if ((err = buf_read_value_bytes(ira, instruction->base.source_node, buf, &result->value))) + return ira->codegen->invalid_instruction; return result; } diff --git a/src/translate_c.cpp b/src/translate_c.cpp index 10f2124eb6..f6bc9cd683 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -4776,6 +4776,14 @@ Error parse_h_file(ImportTableEntry *import, ZigList *errors, const clang_argv.append(target_file); + if (codegen->verbose_cimport) { + fprintf(stderr, "clang"); + for (size_t i = 0; i < clang_argv.length; i += 1) { + fprintf(stderr, " %s", clang_argv.at(i)); + } + fprintf(stderr, "\n"); + } + // to make the [start...end] argument work clang_argv.append(nullptr); diff --git a/std/array_list.zig b/std/array_list.zig index 3ee425fe14..ddad9c989c 100644 --- a/std/array_list.zig +++ b/std/array_list.zig @@ -398,3 +398,14 @@ test "std.ArrayList.insertSlice" { assert(list.len == 6); assert(list.items[0] == 1); } + +const Item = struct { + integer: i32, + sub_items: ArrayList(Item), +}; + +test "std.ArrayList: ArrayList(T) of struct T" { + var root = Item{ .integer = 1, .sub_items = ArrayList(Item).init(debug.global_allocator) }; + try root.sub_items.append( Item{ .integer = 42, .sub_items = ArrayList(Item).init(debug.global_allocator) } ); + assert(root.sub_items.items[0].integer == 42); +} diff --git a/std/dynamic_library.zig b/std/dynamic_library.zig index 49f217bc8e..4d19951318 100644 --- a/std/dynamic_library.zig +++ b/std/dynamic_library.zig @@ -19,7 +19,6 @@ pub const DynLib = switch (builtin.os) { }; pub const LinuxDynLib = struct { - allocator: *mem.Allocator, elf_lib: ElfLib, fd: i32, map_addr: usize, @@ -27,7 +26,7 @@ pub const LinuxDynLib = struct { /// Trusts the file pub fn open(allocator: *mem.Allocator, path: []const u8) !DynLib { - const fd = try std.os.posixOpen(allocator, path, 0, linux.O_RDONLY | linux.O_CLOEXEC); + const fd = try std.os.posixOpen(path, 0, linux.O_RDONLY | linux.O_CLOEXEC); errdefer std.os.close(fd); const size = @intCast(usize, (try std.os.posixFStat(fd)).size); @@ -45,7 +44,6 @@ pub const LinuxDynLib = struct { const bytes = @intToPtr([*]align(std.os.page_size) u8, addr)[0..size]; return DynLib{ - .allocator = allocator, .elf_lib = try ElfLib.init(bytes), .fd = fd, .map_addr = addr, diff --git a/std/fmt/index.zig b/std/fmt/index.zig index adf2882f35..b010072273 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -243,6 +243,9 @@ pub fn formatType( } return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(&value)); }, + builtin.TypeId.Fn => { + return format(context, Errors, output, "{}@{x}", @typeName(T), @ptrToInt(value)); + }, else => @compileError("Unable to format type '" ++ @typeName(T) ++ "'"), } } @@ -1013,6 +1016,10 @@ test "fmt.format" { const value = @intToPtr(fn () void, 0xdeadbeef); try testFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", value); } + { + const value = @intToPtr(fn () void, 0xdeadbeef); + try testFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", value); + } try testFmt("buf: Test \n", "buf: {s5}\n", "Test"); try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", "Test"); try testFmt("cstr: Test C\n", "cstr: {s}\n", c"Test C"); diff --git a/std/index.zig b/std/index.zig index 55ad016bb1..33eec14b0e 100644 --- a/std/index.zig +++ b/std/index.zig @@ -57,7 +57,8 @@ test "std" { _ = @import("mutex.zig"); _ = @import("segmented_list.zig"); _ = @import("spinlock.zig"); - + + _ = @import("dynamic_library.zig"); _ = @import("base64.zig"); _ = @import("build.zig"); _ = @import("c/index.zig"); diff --git a/std/io.zig b/std/io.zig index c40ededc00..428d95725d 100644 --- a/std/io.zig +++ b/std/io.zig @@ -155,32 +155,32 @@ pub fn InStream(comptime ReadError: type) type { pub fn readIntNative(self: *Self, comptime T: type) !T { var bytes: [@sizeOf(T)]u8 = undefined; try self.readNoEof(bytes[0..]); - return mem.readIntSliceNative(T, bytes); + return mem.readIntNative(T, &bytes); } /// Reads a foreign-endian integer pub fn readIntForeign(self: *Self, comptime T: type) !T { var bytes: [@sizeOf(T)]u8 = undefined; try self.readNoEof(bytes[0..]); - return mem.readIntSliceForeign(T, bytes); + return mem.readIntForeign(T, &bytes); } pub fn readIntLittle(self: *Self, comptime T: type) !T { var bytes: [@sizeOf(T)]u8 = undefined; try self.readNoEof(bytes[0..]); - return mem.readIntSliceLittle(T, bytes); + return mem.readIntLittle(T, &bytes); } pub fn readIntBig(self: *Self, comptime T: type) !T { var bytes: [@sizeOf(T)]u8 = undefined; try self.readNoEof(bytes[0..]); - return mem.readIntSliceBig(T, bytes); + return mem.readIntBig(T, &bytes); } pub fn readInt(self: *Self, comptime T: type, endian: builtin.Endian) !T { var bytes: [@sizeOf(T)]u8 = undefined; try self.readNoEof(bytes[0..]); - return mem.readIntSlice(T, bytes, endian); + return mem.readInt(T, &bytes, endian); } pub fn readVarInt(self: *Self, comptime ReturnType: type, endian: builtin.Endian, size: usize) !ReturnType { diff --git a/std/os/index.zig b/std/os/index.zig index 817f11493c..b82a777450 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -459,6 +459,7 @@ pub const PosixOpenError = error{ NoSpaceLeft, NotDir, PathAlreadyExists, + DeviceBusy, /// See https://github.com/ziglang/zig/issues/1396 Unexpected, @@ -497,6 +498,7 @@ pub fn posixOpenC(file_path: [*]const u8, flags: u32, perm: usize) !i32 { posix.ENOTDIR => return PosixOpenError.NotDir, posix.EPERM => return PosixOpenError.AccessDenied, posix.EEXIST => return PosixOpenError.PathAlreadyExists, + posix.EBUSY => return PosixOpenError.DeviceBusy, else => return unexpectedErrorPosix(err), } } @@ -1402,6 +1404,7 @@ const DeleteTreeError = error{ FileSystem, FileBusy, DirNotEmpty, + DeviceBusy, /// On Windows, file paths must be valid Unicode. InvalidUtf8, @@ -1463,6 +1466,7 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError! error.Unexpected, error.InvalidUtf8, error.BadPathName, + error.DeviceBusy, => return err, }; defer dir.close(); @@ -1545,6 +1549,7 @@ pub const Dir = struct { OutOfMemory, InvalidUtf8, BadPathName, + DeviceBusy, /// See https://github.com/ziglang/zig/issues/1396 Unexpected, diff --git a/std/os/path.zig b/std/os/path.zig index ff70bc039e..31f949f0d6 100644 --- a/std/os/path.zig +++ b/std/os/path.zig @@ -1093,6 +1093,7 @@ pub const RealError = error{ NoSpaceLeft, FileSystem, BadPathName, + DeviceBusy, /// On Windows, file paths must be valid Unicode. InvalidUtf8, diff --git a/test/behavior.zig b/test/behavior.zig index e32063dec8..8090359772 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -42,6 +42,7 @@ comptime { _ = @import("cases/if.zig"); _ = @import("cases/import.zig"); _ = @import("cases/incomplete_struct_param_tld.zig"); + _ = @import("cases/inttoptr.zig"); _ = @import("cases/ir_block_deps.zig"); _ = @import("cases/math.zig"); _ = @import("cases/merge_error_sets.zig"); diff --git a/test/cases/inttoptr.zig b/test/cases/inttoptr.zig new file mode 100644 index 0000000000..6695352383 --- /dev/null +++ b/test/cases/inttoptr.zig @@ -0,0 +1,13 @@ +const builtin = @import("builtin"); +const std = @import("std"); +const assertOrPanic = std.debug.assertOrPanic; + +test "casting random address to function pointer" { + randomAddressToFunction(); + comptime randomAddressToFunction(); +} + +fn randomAddressToFunction() void { + var addr: usize = 0xdeadbeef; + var ptr = @intToPtr(fn () void, addr); +} diff --git a/test/cases/ptrcast.zig b/test/cases/ptrcast.zig index 071087c5c4..6f0e6e5946 100644 --- a/test/cases/ptrcast.zig +++ b/test/cases/ptrcast.zig @@ -15,3 +15,22 @@ fn testReinterpretBytesAsInteger() void { }; assertOrPanic(@ptrCast(*align(1) const u32, bytes[1..5].ptr).* == expected); } + +test "reinterpret bytes of an array into an extern struct" { + testReinterpretBytesAsExternStruct(); + comptime testReinterpretBytesAsExternStruct(); +} + +fn testReinterpretBytesAsExternStruct() void { + var bytes align(2) = []u8{ 1, 2, 3, 4, 5, 6 }; + + const S = extern struct { + a: u8, + b: u16, + c: u8, + }; + + var ptr = @ptrCast(*const S, &bytes); + var val = ptr.c; + assertOrPanic(val == 5); +} diff --git a/test/cases/struct_contains_slice_of_itself.zig b/test/cases/struct_contains_slice_of_itself.zig index 07987ae32b..aa3075312c 100644 --- a/test/cases/struct_contains_slice_of_itself.zig +++ b/test/cases/struct_contains_slice_of_itself.zig @@ -5,6 +5,11 @@ const Node = struct { children: []Node, }; +const NodeAligned = struct { + payload: i32, + children: []align(@alignOf(NodeAligned)) NodeAligned, +}; + test "struct contains slice of itself" { var other_nodes = []Node{ Node{ @@ -41,3 +46,40 @@ test "struct contains slice of itself" { assert(root.children[2].children[0].payload == 31); assert(root.children[2].children[1].payload == 32); } + +test "struct contains aligned slice of itself" { + var other_nodes = []NodeAligned{ + NodeAligned{ + .payload = 31, + .children = []NodeAligned{}, + }, + NodeAligned{ + .payload = 32, + .children = []NodeAligned{}, + }, + }; + var nodes = []NodeAligned{ + NodeAligned{ + .payload = 1, + .children = []NodeAligned{}, + }, + NodeAligned{ + .payload = 2, + .children = []NodeAligned{}, + }, + NodeAligned{ + .payload = 3, + .children = other_nodes[0..], + }, + }; + const root = NodeAligned{ + .payload = 1234, + .children = nodes[0..], + }; + assert(root.payload == 1234); + assert(root.children[0].payload == 1); + assert(root.children[1].payload == 2); + assert(root.children[2].payload == 3); + assert(root.children[2].children[0].payload == 31); + assert(root.children[2].children[1].payload == 32); +} diff --git a/test/cases/type_info.zig b/test/cases/type_info.zig index 6f99268c08..cec532d5d3 100644 --- a/test/cases/type_info.zig +++ b/test/cases/type_info.zig @@ -144,15 +144,20 @@ test "type info: enum info" { } fn testEnum() void { - const Os = @import("builtin").Os; + const Os = enum { + Windows, + Macos, + Linux, + FreeBSD, + }; const os_info = @typeInfo(Os); assert(TypeId(os_info) == TypeId.Enum); assert(os_info.Enum.layout == TypeInfo.ContainerLayout.Auto); - assert(os_info.Enum.fields.len == 32); - assert(mem.eql(u8, os_info.Enum.fields[1].name, "ananas")); - assert(os_info.Enum.fields[10].value == 10); - assert(os_info.Enum.tag_type == u5); + assert(os_info.Enum.fields.len == 4); + assert(mem.eql(u8, os_info.Enum.fields[1].name, "Macos")); + assert(os_info.Enum.fields[3].value == 3); + assert(os_info.Enum.tag_type == u2); assert(os_info.Enum.defs.len == 0); }