diff options
| -rw-r--r-- | doc/langref.html.in | 16 | ||||
| -rw-r--r-- | lib/std/Build.zig | 7 | ||||
| -rw-r--r-- | lib/std/coff.zig | 35 | ||||
| -rw-r--r-- | lib/std/debug.zig | 87 | ||||
| -rw-r--r-- | lib/std/json/dynamic.zig | 14 | ||||
| -rw-r--r-- | lib/std/json/dynamic_test.zig | 40 | ||||
| -rw-r--r-- | src/AstGen.zig | 30 | ||||
| -rw-r--r-- | src/Autodoc.zig | 3 | ||||
| -rw-r--r-- | src/Compilation.zig | 7 | ||||
| -rw-r--r-- | src/InternPool.zig | 30 | ||||
| -rw-r--r-- | src/Module.zig | 55 | ||||
| -rw-r--r-- | src/Sema.zig | 377 | ||||
| -rw-r--r-- | src/codegen/llvm.zig | 11 | ||||
| -rw-r--r-- | src/link/MachO/Object.zig | 65 | ||||
| -rw-r--r-- | src/translate_c.zig | 29 | ||||
| -rw-r--r-- | test/cases/compile_errors/wrong_types_given_to_export.zig | 2 | ||||
| -rw-r--r-- | test/link.zig | 4 | ||||
| -rw-r--r-- | test/link/macho/reexports/a.zig | 7 | ||||
| -rw-r--r-- | test/link/macho/reexports/build.zig | 38 | ||||
| -rw-r--r-- | test/link/macho/reexports/main.c | 5 | ||||
| -rw-r--r-- | test/run_translated_c.zig | 18 |
21 files changed, 582 insertions, 298 deletions
diff --git a/doc/langref.html.in b/doc/langref.html.in index 5c5e7f53f3..193621cfe9 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -10819,11 +10819,11 @@ pub fn build(b: *std.Build) void { {#header_open|Compiling C Source Code#} <pre>{#syntax#} -lib.addCSourceFile("src/lib.c", &[_][]const u8{ - "-Wall", - "-Wextra", - "-Werror", -}); +lib.addCSourceFile(.{ .file = .{ .path = "src/lib.c" }, .flags = &.{ + "-Wall", + "-Wextra", + "-Werror", + } }); {#endsyntax#}</pre> {#header_close#} @@ -11180,7 +11180,7 @@ pub fn build(b: *std.Build) void { const exe = b.addExecutable(.{ .name = "test", }); - exe.addCSourceFile("test.c", &[_][]const u8{"-std=c99"}); + exe.addCSourceFile(.{ .file = .{ .path = "test.c" }, .flags = &.{"-std=c99"} }); exe.linkLibrary(lib); exe.linkSystemLibrary("c"); @@ -11247,10 +11247,10 @@ pub fn build(b: *std.Build) void { const exe = b.addExecutable(.{ .name = "test", }); - exe.addCSourceFile("test.c", &[_][]const u8{"-std=c99"}); + exe.addCSourceFile(.{ .file = .{ .path = "test.c" }, .flags = &.{"-std=c99",} }); exe.addObject(obj); exe.linkSystemLibrary("c"); - exe.install(); + b.installArtifact(exe); } {#code_end#} {#shell_samp#}$ zig build diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 046f9af796..472ef7d740 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -414,7 +414,12 @@ fn userInputOptionsFromArgs(allocator: Allocator, args: anytype) UserInputOption }) catch @panic("OOM"); user_input_options.put("cpu", .{ .name = "cpu", - .value = .{ .scalar = serializeCpu(allocator, v.getCpu()) catch unreachable }, + .value = .{ + .scalar = if (v.isNativeCpu()) + "native" + else + serializeCpu(allocator, v.getCpu()) catch unreachable, + }, .used = false, }) catch @panic("OOM"); }, diff --git a/lib/std/coff.zig b/lib/std/coff.zig index c937baa18d..9db9f4d988 100644 --- a/lib/std/coff.zig +++ b/lib/std/coff.zig @@ -1059,6 +1059,8 @@ pub const CoffError = error{ // Official documentation of the format: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format pub const Coff = struct { data: []const u8, + // Set if `data` is backed by the image as loaded by the loader + is_loaded: bool, is_image: bool, coff_header_offset: usize, @@ -1066,7 +1068,7 @@ pub const Coff = struct { age: u32 = undefined, // The lifetime of `data` must be longer than the lifetime of the returned Coff - pub fn init(data: []const u8) !Coff { + pub fn init(data: []const u8, is_loaded: bool) !Coff { const pe_pointer_offset = 0x3C; const pe_magic = "PE\x00\x00"; @@ -1082,6 +1084,7 @@ pub const Coff = struct { var coff = @This(){ .data = data, .is_image = is_image, + .is_loaded = is_loaded, .coff_header_offset = coff_header_offset, }; @@ -1098,27 +1101,40 @@ pub const Coff = struct { return coff; } - pub fn getPdbPath(self: *Coff, buffer: []u8) !usize { + pub fn getPdbPath(self: *Coff, buffer: []u8) !?usize { assert(self.is_image); const data_dirs = self.getDataDirectories(); - const debug_dir = data_dirs[@intFromEnum(DirectoryEntry.DEBUG)]; + if (@intFromEnum(DirectoryEntry.DEBUG) >= data_dirs.len) return null; + const debug_dir = data_dirs[@intFromEnum(DirectoryEntry.DEBUG)]; var stream = std.io.fixedBufferStream(self.data); const reader = stream.reader(); - try stream.seekTo(debug_dir.virtual_address); + + if (self.is_loaded) { + try stream.seekTo(debug_dir.virtual_address); + } else { + // Find what section the debug_dir is in, in order to convert the RVA to a file offset + for (self.getSectionHeaders()) |*sect| { + if (debug_dir.virtual_address >= sect.virtual_address and debug_dir.virtual_address < sect.virtual_address + sect.virtual_size) { + try stream.seekTo(sect.pointer_to_raw_data + (debug_dir.virtual_address - sect.virtual_address)); + break; + } + } else return error.InvalidDebugDirectory; + } // Find the correct DebugDirectoryEntry, and where its data is stored. // It can be in any section. const debug_dir_entry_count = debug_dir.size / @sizeOf(DebugDirectoryEntry); var i: u32 = 0; - blk: while (i < debug_dir_entry_count) : (i += 1) { + while (i < debug_dir_entry_count) : (i += 1) { const debug_dir_entry = try reader.readStruct(DebugDirectoryEntry); if (debug_dir_entry.type == .CODEVIEW) { - try stream.seekTo(debug_dir_entry.address_of_raw_data); - break :blk; + const dir_offset = if (self.is_loaded) debug_dir_entry.address_of_raw_data else debug_dir_entry.pointer_to_raw_data; + try stream.seekTo(dir_offset); + break; } - } + } else return null; var cv_signature: [4]u8 = undefined; // CodeView signature try reader.readNoEof(cv_signature[0..]); @@ -1256,7 +1272,8 @@ pub const Coff = struct { } pub fn getSectionData(self: *const Coff, sec: *align(1) const SectionHeader) []const u8 { - return self.data[sec.pointer_to_raw_data..][0..sec.virtual_size]; + const offset = if (self.is_loaded) sec.virtual_address else sec.pointer_to_raw_data; + return self.data[offset..][0..sec.virtual_size]; } pub fn getSectionDataAlloc(self: *const Coff, sec: *align(1) const SectionHeader, allocator: mem.Allocator) ![]u8 { diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 7a32271953..6de21ddd1b 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -998,7 +998,6 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_obj: *coff.Coff) !ModuleDebu .base_address = undefined, .coff_image_base = coff_obj.getImageBase(), .coff_section_headers = undefined, - .debug_data = undefined, }; if (coff_obj.getSectionByName(".debug_info")) |_| { @@ -1023,32 +1022,33 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_obj: *coff.Coff) !ModuleDebu }; try DW.openDwarfDebugInfo(&dwarf, allocator); - di.debug_data = PdbOrDwarf{ .dwarf = dwarf }; - return di; + di.dwarf = dwarf; } - // Only used by pdb path - di.coff_section_headers = try coff_obj.getSectionHeadersAlloc(allocator); - errdefer allocator.free(di.coff_section_headers); - var path_buf: [windows.MAX_PATH]u8 = undefined; - const len = try coff_obj.getPdbPath(path_buf[0..]); + const len = try coff_obj.getPdbPath(path_buf[0..]) orelse return di; const raw_path = path_buf[0..len]; const path = try fs.path.resolve(allocator, &[_][]const u8{raw_path}); defer allocator.free(path); - di.debug_data = PdbOrDwarf{ .pdb = undefined }; - di.debug_data.pdb = pdb.Pdb.init(allocator, path) catch |err| switch (err) { - error.FileNotFound, error.IsDir => return error.MissingDebugInfo, + di.pdb = pdb.Pdb.init(allocator, path) catch |err| switch (err) { + error.FileNotFound, error.IsDir => { + if (di.dwarf == null) return error.MissingDebugInfo; + return di; + }, else => return err, }; - try di.debug_data.pdb.parseInfoStream(); - try di.debug_data.pdb.parseDbiStream(); + try di.pdb.?.parseInfoStream(); + try di.pdb.?.parseDbiStream(); - if (!mem.eql(u8, &coff_obj.guid, &di.debug_data.pdb.guid) or coff_obj.age != di.debug_data.pdb.age) + if (!mem.eql(u8, &coff_obj.guid, &di.pdb.?.guid) or coff_obj.age != di.pdb.?.age) return error.InvalidDebugInfo; + // Only used by the pdb path + di.coff_section_headers = try coff_obj.getSectionHeadersAlloc(allocator); + errdefer allocator.free(di.coff_section_headers); + return di; } } @@ -1696,7 +1696,7 @@ pub const DebugInfo = struct { errdefer self.allocator.destroy(obj_di); const mapped_module = @as([*]const u8, @ptrFromInt(module.base_address))[0..module.size]; - var coff_obj = try coff.Coff.init(mapped_module); + var coff_obj = try coff.Coff.init(mapped_module, true); // The string table is not mapped into memory by the loader, so if a section name is in the // string table then we have to map the full image file from disk. This can happen when @@ -1754,7 +1754,7 @@ pub const DebugInfo = struct { errdefer assert(windows.ntdll.NtUnmapViewOfSection(process_handle, @ptrFromInt(base_ptr)) == .SUCCESS); const section_view = @as([*]const u8, @ptrFromInt(base_ptr))[0..coff_len]; - coff_obj = try coff.Coff.init(section_view); + coff_obj = try coff.Coff.init(section_view, false); module.mapped_file = .{ .file = coff_file, @@ -2142,34 +2142,27 @@ pub const ModuleDebugInfo = switch (native_os) { }, .uefi, .windows => struct { base_address: usize, - debug_data: PdbOrDwarf, + pdb: ?pdb.Pdb = null, + dwarf: ?DW.DwarfInfo = null, coff_image_base: u64, - /// Only used if debug_data is .pdb + + /// Only used if pdb is non-null coff_section_headers: []coff.SectionHeader, pub fn deinit(self: *@This(), allocator: mem.Allocator) void { - self.debug_data.deinit(allocator); - if (self.debug_data == .pdb) { - allocator.free(self.coff_section_headers); + if (self.dwarf) |*dwarf| { + dwarf.deinit(allocator); } - } - pub fn getSymbolAtAddress(self: *@This(), allocator: mem.Allocator, address: usize) !SymbolInfo { - // Translate the VA into an address into this object - const relocated_address = address - self.base_address; - - switch (self.debug_data) { - .dwarf => |*dwarf| { - const dwarf_address = relocated_address + self.coff_image_base; - return getSymbolFromDwarf(allocator, dwarf_address, dwarf); - }, - .pdb => { - // fallthrough to pdb handling - }, + if (self.pdb) |*p| { + p.deinit(); + allocator.free(self.coff_section_headers); } + } + fn getSymbolFromPdb(self: *@This(), relocated_address: usize) !?SymbolInfo { var coff_section: *align(1) const coff.SectionHeader = undefined; - const mod_index = for (self.debug_data.pdb.sect_contribs) |sect_contrib| { + const mod_index = for (self.pdb.?.sect_contribs) |sect_contrib| { if (sect_contrib.Section > self.coff_section_headers.len) continue; // Remember that SectionContribEntry.Section is 1-based. coff_section = &self.coff_section_headers[sect_contrib.Section - 1]; @@ -2181,18 +2174,18 @@ pub const ModuleDebugInfo = switch (native_os) { } } else { // we have no information to add to the address - return SymbolInfo{}; + return null; }; - const module = (try self.debug_data.pdb.getModule(mod_index)) orelse + const module = (try self.pdb.?.getModule(mod_index)) orelse return error.InvalidDebugInfo; const obj_basename = fs.path.basename(module.obj_file_name); - const symbol_name = self.debug_data.pdb.getSymbolName( + const symbol_name = self.pdb.?.getSymbolName( module, relocated_address - coff_section.virtual_address, ) orelse "???"; - const opt_line_info = try self.debug_data.pdb.getLineNumberInfo( + const opt_line_info = try self.pdb.?.getLineNumberInfo( module, relocated_address - coff_section.virtual_address, ); @@ -2204,6 +2197,22 @@ pub const ModuleDebugInfo = switch (native_os) { }; } + pub fn getSymbolAtAddress(self: *@This(), allocator: mem.Allocator, address: usize) !SymbolInfo { + // Translate the VA into an address into this object + const relocated_address = address - self.base_address; + + if (self.pdb != null) { + if (try self.getSymbolFromPdb(relocated_address)) |symbol| return symbol; + } + + if (self.dwarf) |*dwarf| { + const dwarf_address = relocated_address + self.coff_image_base; + return getSymbolFromDwarf(allocator, dwarf_address, dwarf); + } + + return SymbolInfo{}; + } + pub fn getDwarfInfoForAddress(self: *@This(), allocator: mem.Allocator, address: usize) !?*const DW.DwarfInfo { _ = allocator; _ = address; diff --git a/lib/std/json/dynamic.zig b/lib/std/json/dynamic.zig index 5e99b52d44..81ca1db2da 100644 --- a/lib/std/json/dynamic.zig +++ b/lib/std/json/dynamic.zig @@ -93,11 +93,11 @@ pub const Value = union(enum) { stack.items[stack.items.len - 1] == .array or (stack.items[stack.items.len - 2] == .object and stack.items[stack.items.len - 1] == .string)); - switch (try source.nextAlloc(allocator, .alloc_if_needed)) { - inline .string, .allocated_string => |s| { + switch (try source.nextAlloc(allocator, .alloc_always)) { + .allocated_string => |s| { return try handleCompleteValue(&stack, allocator, source, Value{ .string = s }) orelse continue; }, - inline .number, .allocated_number => |slice| { + .allocated_number => |slice| { return try handleCompleteValue(&stack, allocator, source, Value.parseFromNumberSlice(slice)) orelse continue; }, @@ -106,9 +106,9 @@ pub const Value = union(enum) { .false => return try handleCompleteValue(&stack, allocator, source, Value{ .bool = false }) orelse continue, .object_begin => { - switch (try source.nextAlloc(allocator, .alloc_if_needed)) { + switch (try source.nextAlloc(allocator, .alloc_always)) { .object_end => return try handleCompleteValue(&stack, allocator, source, Value{ .object = ObjectMap.init(allocator) }) orelse continue, - inline .string, .allocated_string => |key| { + .allocated_string => |key| { try stack.appendSlice(&[_]Value{ Value{ .object = ObjectMap.init(allocator) }, Value{ .string = key }, @@ -152,7 +152,7 @@ fn handleCompleteValue(stack: *Array, allocator: Allocator, source: anytype, val // This is an invalid state to leave the stack in, // so we have to process the next token before we return. - switch (try source.nextAlloc(allocator, .alloc_if_needed)) { + switch (try source.nextAlloc(allocator, .alloc_always)) { .object_end => { // This object is complete. value = stack.pop(); @@ -160,7 +160,7 @@ fn handleCompleteValue(stack: *Array, allocator: Allocator, source: anytype, val if (stack.items.len == 0) return value; continue; }, - inline .string, .allocated_string => |next_key| { + .allocated_string => |next_key| { // We've got another key. try stack.append(Value{ .string = next_key }); // stack: [..., .object, .string] diff --git a/lib/std/json/dynamic_test.zig b/lib/std/json/dynamic_test.zig index 220c94edcf..144f108cb6 100644 --- a/lib/std/json/dynamic_test.zig +++ b/lib/std/json/dynamic_test.zig @@ -15,6 +15,7 @@ const parseFromValueLeaky = @import("static.zig").parseFromValueLeaky; const ParseOptions = @import("static.zig").ParseOptions; const jsonReader = @import("scanner.zig").reader; +const JsonReader = @import("scanner.zig").Reader; test "json.parser.dynamic" { const s = @@ -288,3 +289,42 @@ test "polymorphic parsing" { try testing.expect(tree.div.color == .blue); try testing.expectEqualStrings("Cancel", tree.div.children[1].button.caption); } + +test "long object value" { + const value = "01234567890123456789"; + const doc = "{\"key\":\"" ++ value ++ "\"}"; + var fbs = std.io.fixedBufferStream(doc); + var reader = smallBufferJsonReader(testing.allocator, fbs.reader()); + defer reader.deinit(); + var parsed = try parseFromTokenSource(Value, testing.allocator, &reader, .{}); + defer parsed.deinit(); + + try testing.expectEqualStrings(value, parsed.value.object.get("key").?.string); +} + +test "many object keys" { + const doc = + \\{ + \\ "k1": "v1", + \\ "k2": "v2", + \\ "k3": "v3", + \\ "k4": "v4", + \\ "k5": "v5" + \\} + ; + var fbs = std.io.fixedBufferStream(doc); + var reader = smallBufferJsonReader(testing.allocator, fbs.reader()); + defer reader.deinit(); + var parsed = try parseFromTokenSource(Value, testing.allocator, &reader, .{}); + defer parsed.deinit(); + + try testing.expectEqualStrings("v1", parsed.value.object.get("k1").?.string); + try testing.expectEqualStrings("v2", parsed.value.object.get("k2").?.string); + try testing.expectEqualStrings("v3", parsed.value.object.get("k3").?.string); + try testing.expectEqualStrings("v4", parsed.value.object.get("k4").?.string); + try testing.expectEqualStrings("v5", parsed.value.object.get("k5").?.string); +} + +fn smallBufferJsonReader(allocator: Allocator, io_reader: anytype) JsonReader(16, @TypeOf(io_reader)) { + return JsonReader(16, @TypeOf(io_reader)).init(allocator, io_reader); +} diff --git a/src/AstGen.zig b/src/AstGen.zig index 0cf9c75c55..bff56614fe 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -8384,10 +8384,7 @@ fn builtinCall( local_val.used = ident_token; _ = try gz.addPlNode(.export_value, node, Zir.Inst.ExportValue{ .operand = local_val.inst, - // TODO: the result location here should be `.{ .coerced_ty = .export_options_type }`, but - // that currently hits assertions in Sema due to type resolution issues. - // See #16603 - .options = try comptimeExpr(gz, scope, .{ .rl = .none }, params[1]), + .options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .export_options_type } }, params[1]), }); return rvalue(gz, ri, .void_value, node); } @@ -8402,10 +8399,7 @@ fn builtinCall( const loaded = try gz.addUnNode(.load, local_ptr.ptr, node); _ = try gz.addPlNode(.export_value, node, Zir.Inst.ExportValue{ .operand = loaded, - // TODO: the result location here should be `.{ .coerced_ty = .export_options_type }`, but - // that currently hits assertions in Sema due to type resolution issues. - // See #16603 - .options = try comptimeExpr(gz, scope, .{ .rl = .none }, params[1]), + .options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .export_options_type } }, params[1]), }); return rvalue(gz, ri, .void_value, node); } @@ -8439,10 +8433,7 @@ fn builtinCall( }, else => return astgen.failNode(params[0], "symbol to export must identify a declaration", .{}), } - // TODO: the result location here should be `.{ .coerced_ty = .export_options_type }`, but - // that currently hits assertions in Sema due to type resolution issues. - // See #16603 - const options = try comptimeExpr(gz, scope, .{ .rl = .none }, params[1]); + const options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .export_options_type } }, params[1]); _ = try gz.addPlNode(.@"export", node, Zir.Inst.Export{ .namespace = namespace, .decl_name = decl_name, @@ -8452,10 +8443,7 @@ fn builtinCall( }, .@"extern" => { const type_inst = try typeExpr(gz, scope, params[0]); - // TODO: the result location here should be `.{ .coerced_ty = .extern_options_type }`, but - // that currently hits assertions in Sema due to type resolution issues. - // See #16603 - const options = try comptimeExpr(gz, scope, .{ .rl = .none }, params[1]); + const options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .extern_options_type } }, params[1]); const result = try gz.addExtendedPayload(.builtin_extern, Zir.Inst.BinNode{ .node = gz.nodeIndexToRelative(node), .lhs = type_inst, @@ -8559,10 +8547,7 @@ fn builtinCall( // zig fmt: on .Type => { - // TODO: the result location here should be `.{ .coerced_ty = .type_info_type }`, but - // that currently hits assertions in Sema due to type resolution issues. - // See #16603 - const operand = try expr(gz, scope, .{ .rl = .none }, params[0]); + const operand = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .type_info_type } }, params[0]); const gpa = gz.astgen.gpa; @@ -8834,10 +8819,7 @@ fn builtinCall( }, .prefetch => { const ptr = try expr(gz, scope, .{ .rl = .none }, params[0]); - // TODO: the result location here should be `.{ .coerced_ty = .preftech_options_type }`, but - // that currently hits assertions in Sema due to type resolution issues. - // See #16603 - const options = try comptimeExpr(gz, scope, .{ .rl = .none }, params[1]); + const options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .prefetch_options_type } }, params[1]); _ = try gz.addExtendedPayload(.prefetch, Zir.Inst.BinNode{ .node = gz.nodeIndexToRelative(node), .lhs = ptr, diff --git a/src/Autodoc.zig b/src/Autodoc.zig index d865a43225..92cfac6b51 100644 --- a/src/Autodoc.zig +++ b/src/Autodoc.zig @@ -5537,9 +5537,8 @@ fn findGuidePaths(self: *Autodoc, file: *File, str: []const u8) ![]const u8 { fn addGuide(self: *Autodoc, file: *File, guide_path: []const u8, section: *Section) !void { if (guide_path.len == 0) return error.MissingAutodocGuideName; - const cur_mod_dir_path = file.pkg.root_src_directory.path orelse "."; const resolved_path = try std.fs.path.resolve(self.arena, &[_][]const u8{ - cur_mod_dir_path, file.sub_file_path, "..", guide_path, + file.sub_file_path, "..", guide_path, }); var guide_file = try file.pkg.root_src_directory.handle.openFile(resolved_path, .{}); diff --git a/src/Compilation.zig b/src/Compilation.zig index 47e13ba85c..726862f231 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -5175,10 +5175,13 @@ pub fn lockAndParseLldStderr(comp: *Compilation, comptime prefix: []const u8, st } pub fn dump_argv(argv: []const []const u8) void { + std.debug.getStderrMutex().lock(); + defer std.debug.getStderrMutex().unlock(); + const stderr = std.io.getStdErr().writer(); for (argv[0 .. argv.len - 1]) |arg| { - std.debug.print("{s} ", .{arg}); + nosuspend stderr.print("{s} ", .{arg}) catch return; } - std.debug.print("{s}\n", .{argv[argv.len - 1]}); + nosuspend stderr.print("{s}\n", .{argv[argv.len - 1]}) catch {}; } pub fn getZigBackend(comp: Compilation) std.builtin.CompilerBackend { diff --git a/src/InternPool.zig b/src/InternPool.zig index 178bc6870c..bde6c11256 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -7176,3 +7176,33 @@ fn unwrapCoercedFunc(ip: *const InternPool, i: Index) Index { else => unreachable, }; } + +/// Having resolved a builtin type to a real struct/union/enum (which is now at `resolverd_index`), +/// make `want_index` refer to this type instead. This invalidates `resolved_index`, so must be +/// called only when it is guaranteed that no reference to `resolved_index` exists. +pub fn resolveBuiltinType(ip: *InternPool, want_index: Index, resolved_index: Index) void { + assert(@intFromEnum(want_index) >= @intFromEnum(Index.first_type)); + assert(@intFromEnum(want_index) <= @intFromEnum(Index.last_type)); + + // Make sure the type isn't already resolved! + assert(ip.indexToKey(want_index) == .simple_type); + + // Make sure it's the same kind of type + assert((ip.zigTypeTagOrPoison(want_index) catch unreachable) == + (ip.zigTypeTagOrPoison(resolved_index) catch unreachable)); + + // Copy the data + const item = ip.items.get(@intFromEnum(resolved_index)); + ip.items.set(@intFromEnum(want_index), item); + + if (std.debug.runtime_safety) { + // Make the value unreachable - this is a weird value which will make (incorrect) existing + // references easier to spot + ip.items.set(@intFromEnum(resolved_index), .{ + .tag = .simple_value, + .data = @intFromEnum(SimpleValue.@"unreachable"), + }); + } else { + // TODO: add the index to a free-list for reuse + } +} diff --git a/src/Module.zig b/src/Module.zig index 06976ae49c..c19b742b54 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -770,9 +770,8 @@ pub const Decl = struct { /// Gets the namespace that this Decl creates by being a struct, union, /// enum, or opaque. - /// Only returns it if the Decl is the owner. - pub fn getOwnedInnerNamespaceIndex(decl: Decl, mod: *Module) Namespace.OptionalIndex { - if (!decl.owns_tv) return .none; + pub fn getInnerNamespaceIndex(decl: Decl, mod: *Module) Namespace.OptionalIndex { + if (!decl.has_tv) return .none; return switch (decl.val.ip_index) { .empty_struct_type => .none, .none => .none, @@ -786,11 +785,22 @@ pub const Decl = struct { }; } - /// Same as `getInnerNamespaceIndex` but additionally obtains the pointer. + /// Like `getInnerNamespaceIndex`, but only returns it if the Decl is the owner. + pub fn getOwnedInnerNamespaceIndex(decl: Decl, mod: *Module) Namespace.OptionalIndex { + if (!decl.owns_tv) return .none; + return decl.getInnerNamespaceIndex(mod); + } + + /// Same as `getOwnedInnerNamespaceIndex` but additionally obtains the pointer. pub fn getOwnedInnerNamespace(decl: Decl, mod: *Module) ?*Namespace { return mod.namespacePtrUnwrap(decl.getOwnedInnerNamespaceIndex(mod)); } + /// Same as `getInnerNamespaceIndex` but additionally obtains the pointer. + pub fn getInnerNamespace(decl: Decl, mod: *Module) ?*Namespace { + return mod.namespacePtrUnwrap(decl.getInnerNamespaceIndex(mod)); + } + pub fn dump(decl: *Decl) void { const loc = std.zig.findLineColumn(decl.scope.source.bytes, decl.src); std.debug.print("{s}:{d}:{d} name={d} status={s}", .{ @@ -4283,6 +4293,40 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { const zir = decl.getFileScope(mod).zir; const zir_datas = zir.instructions.items(.data); + // TODO: figure out how this works under incremental changes to builtin.zig! + const builtin_type_target_index: InternPool.Index = blk: { + const std_mod = mod.main_pkg.table.get("std").?; + if (decl.getFileScope(mod).pkg != std_mod) break :blk .none; + // We're in the std module. + const std_file = (try mod.importPkg(std_mod)).file; + const std_decl = mod.declPtr(std_file.root_decl.unwrap().?); + const std_namespace = std_decl.getInnerNamespace(mod).?; + const builtin_str = try mod.intern_pool.getOrPutString(gpa, "builtin"); + const builtin_decl = mod.declPtr(std_namespace.decls.getKeyAdapted(builtin_str, DeclAdapter{ .mod = mod }) orelse break :blk .none); + const builtin_namespace = builtin_decl.getInnerNamespaceIndex(mod).unwrap() orelse break :blk .none; + if (decl.src_namespace != builtin_namespace) break :blk .none; + // We're in builtin.zig. This could be a builtin we need to add to a specific InternPool index. + const decl_name = mod.intern_pool.stringToSlice(decl.name); + for ([_]struct { []const u8, InternPool.Index }{ + .{ "AtomicOrder", .atomic_order_type }, + .{ "AtomicRmwOp", .atomic_rmw_op_type }, + .{ "CallingConvention", .calling_convention_type }, + .{ "AddressSpace", .address_space_type }, + .{ "FloatMode", .float_mode_type }, + .{ "ReduceOp", .reduce_op_type }, + .{ "CallModifier", .call_modifier_type }, + .{ "PrefetchOptions", .prefetch_options_type }, + .{ "ExportOptions", .export_options_type }, + .{ "ExternOptions", .extern_options_type }, + .{ "Type", .type_info_type }, + }) |pair| { + if (std.mem.eql(u8, decl_name, pair[0])) { + break :blk pair[1]; + } + } + break :blk .none; + }; + decl.analysis = .in_progress; var analysis_arena = std.heap.ArenaAllocator.init(gpa); @@ -4304,6 +4348,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { .fn_ret_ty_ies = null, .owner_func_index = .none, .comptime_mutable_decls = &comptime_mutable_decls, + .builtin_type_target_index = builtin_type_target_index, }; defer sema.deinit(); @@ -4340,6 +4385,8 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { const extra = zir.extraData(Zir.Inst.Block, inst_data.payload_index); const body = zir.extra[extra.end..][0..extra.data.body_len]; const result_ref = (try sema.analyzeBodyBreak(&block_scope, body)).?.operand; + // We'll do some other bits with the Sema. Clear the type target index just in case they analyze any type. + sema.builtin_type_target_index = .none; try wip_captures.finalize(); for (comptime_mutable_decls.items) |ct_decl_index| { const ct_decl = mod.declPtr(ct_decl_index); diff --git a/src/Sema.zig b/src/Sema.zig index e7d1df0ebb..396ef9a786 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -107,6 +107,10 @@ comptime_mutable_decls: *std.ArrayList(Decl.Index), /// one encountered, the conflicting source location can be shown. prev_stack_alignment_src: ?LazySrcLoc = null, +/// While analyzing a type which has a special InternPool index, this is set to the index at which +/// the struct/enum/union type created should be placed. Otherwise, it is `.none`. +builtin_type_target_index: InternPool.Index = .none, + const std = @import("std"); const math = std.math; const mem = std.mem; @@ -1956,8 +1960,8 @@ pub fn setupErrorReturnTrace(sema: *Sema, block: *Block, last_arg_index: usize) const addrs_ptr = try err_trace_block.addTy(.alloc, try mod.singleMutPtrType(addr_arr_ty)); // var st: StackTrace = undefined; - const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace"); - const stack_trace_ty = try sema.resolveTypeFields(unresolved_stack_trace_ty); + const stack_trace_ty = try sema.getBuiltinType("StackTrace"); + try sema.resolveTypeFields(stack_trace_ty); const st_ptr = try err_trace_block.addTy(.alloc, try mod.singleMutPtrType(stack_trace_ty)); // st.instruction_addresses = &addrs; @@ -2890,10 +2894,17 @@ fn zirStructDecl( }); errdefer mod.destroyStruct(struct_index); - const struct_ty = try mod.intern_pool.get(gpa, .{ .struct_type = .{ - .index = struct_index.toOptional(), - .namespace = new_namespace_index.toOptional(), - } }); + const struct_ty = ty: { + const ty = try mod.intern_pool.get(gpa, .{ .struct_type = .{ + .index = struct_index.toOptional(), + .namespace = new_namespace_index.toOptional(), + } }); + if (sema.builtin_type_target_index != .none) { + mod.intern_pool.resolveBuiltinType(sema.builtin_type_target_index, ty); + break :ty sema.builtin_type_target_index; + } + break :ty ty; + }; // TODO: figure out InternPool removals for incremental compilation //errdefer mod.intern_pool.remove(struct_ty); @@ -3084,18 +3095,25 @@ fn zirEnumDecl( if (bag != 0) break true; } else false; - const incomplete_enum = try mod.intern_pool.getIncompleteEnum(gpa, .{ - .decl = new_decl_index, - .namespace = new_namespace_index.toOptional(), - .fields_len = fields_len, - .has_values = any_values, - .tag_mode = if (small.nonexhaustive) - .nonexhaustive - else if (tag_type_ref == .none) - .auto - else - .explicit, - }); + const incomplete_enum = incomplete_enum: { + var incomplete_enum = try mod.intern_pool.getIncompleteEnum(gpa, .{ + .decl = new_decl_index, + .namespace = new_namespace_index.toOptional(), + .fields_len = fields_len, + .has_values = any_values, + .tag_mode = if (small.nonexhaustive) + .nonexhaustive + else if (tag_type_ref == .none) + .auto + else + .explicit, + }); + if (sema.builtin_type_target_index != .none) { + mod.intern_pool.resolveBuiltinType(sema.builtin_type_target_index, incomplete_enum.index); + incomplete_enum.index = sema.builtin_type_target_index; + } + break :incomplete_enum incomplete_enum; + }; // TODO: figure out InternPool removals for incremental compilation //errdefer if (!done) mod.intern_pool.remove(incomplete_enum.index); @@ -3336,17 +3354,24 @@ fn zirUnionDecl( }); errdefer mod.destroyUnion(union_index); - const union_ty = try mod.intern_pool.get(gpa, .{ .union_type = .{ - .index = union_index, - .runtime_tag = if (small.has_tag_type or small.auto_enum_tag) - .tagged - else if (small.layout != .Auto) - .none - else switch (block.sema.mod.optimizeMode()) { - .Debug, .ReleaseSafe => .safety, - .ReleaseFast, .ReleaseSmall => .none, - }, - } }); + const union_ty = ty: { + const ty = try mod.intern_pool.get(gpa, .{ .union_type = .{ + .index = union_index, + .runtime_tag = if (small.has_tag_type or small.auto_enum_tag) + .tagged + else if (small.layout != .Auto) + .none + else switch (block.sema.mod.optimizeMode()) { + .Debug, .ReleaseSafe => .safety, + .ReleaseFast, .ReleaseSmall => .none, + }, + } }); + if (sema.builtin_type_target_index != .none) { + mod.intern_pool.resolveBuiltinType(sema.builtin_type_target_index, ty); + break :ty sema.builtin_type_target_index; + } + break :ty ty; + }; // TODO: figure out InternPool removals for incremental compilation //errdefer mod.intern_pool.remove(union_ty); @@ -3473,8 +3498,8 @@ fn zirRetPtr(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref { defer tracy.end(); if (block.is_comptime or try sema.typeRequiresComptime(sema.fn_ret_ty)) { - const fn_ret_ty = try sema.resolveTypeFields(sema.fn_ret_ty); - return sema.analyzeComptimeAlloc(block, fn_ret_ty, .none); + try sema.resolveTypeFields(sema.fn_ret_ty); + return sema.analyzeComptimeAlloc(block, sema.fn_ret_ty, .none); } const target = sema.mod.getTarget(); @@ -4371,7 +4396,7 @@ fn zirValidateArrayInitTy( return; }, .Struct => if (ty.isTuple(mod)) { - _ = try sema.resolveTypeFields(ty); + try sema.resolveTypeFields(ty); const array_len = ty.arrayLen(mod); if (extra.init_count > array_len) { return sema.fail(block, src, "expected at most {d} tuple fields; found {d}", .{ @@ -6476,11 +6501,11 @@ pub fn analyzeSaveErrRetIndex(sema: *Sema, block: *Block) SemaError!Air.Inst.Ref if (block.is_comptime) return .none; - const unresolved_stack_trace_ty = sema.getBuiltinType("StackTrace") catch |err| switch (err) { + const stack_trace_ty = sema.getBuiltinType("StackTrace") catch |err| switch (err) { error.NeededSourceLocation, error.GenericPoison, error.ComptimeReturn, error.ComptimeBreak => unreachable, else => |e| return e, }; - const stack_trace_ty = sema.resolveTypeFields(unresolved_stack_trace_ty) catch |err| switch (err) { + sema.resolveTypeFields(stack_trace_ty) catch |err| switch (err) { error.NeededSourceLocation, error.GenericPoison, error.ComptimeReturn, error.ComptimeBreak => unreachable, else => |e| return e, }; @@ -6522,8 +6547,8 @@ fn popErrorReturnTrace( // AstGen determined this result does not go to an error-handling expr (try/catch/return etc.), or // the result is comptime-known to be a non-error. Either way, pop unconditionally. - const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace"); - const stack_trace_ty = try sema.resolveTypeFields(unresolved_stack_trace_ty); + const stack_trace_ty = try sema.getBuiltinType("StackTrace"); + try sema.resolveTypeFields(stack_trace_ty); const ptr_stack_trace_ty = try mod.singleMutPtrType(stack_trace_ty); const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty); const field_name = try mod.intern_pool.getOrPutString(gpa, "index"); @@ -6548,8 +6573,8 @@ fn popErrorReturnTrace( defer then_block.instructions.deinit(gpa); // If non-error, then pop the error return trace by restoring the index. - const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace"); - const stack_trace_ty = try sema.resolveTypeFields(unresolved_stack_trace_ty); + const stack_trace_ty = try sema.getBuiltinType("StackTrace"); + try sema.resolveTypeFields(stack_trace_ty); const ptr_stack_trace_ty = try mod.singleMutPtrType(stack_trace_ty); const err_return_trace = try then_block.addTy(.err_return_trace, ptr_stack_trace_ty); const field_name = try mod.intern_pool.getOrPutString(gpa, "index"); @@ -6671,8 +6696,8 @@ fn zirCall( // If any input is an error-type, we might need to pop any trace it generated. Otherwise, we only // need to clean-up our own trace if we were passed to a non-error-handling expression. if (input_is_error or (pop_error_return_trace and return_ty.isError(mod))) { - const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace"); - const stack_trace_ty = try sema.resolveTypeFields(unresolved_stack_trace_ty); + const stack_trace_ty = try sema.getBuiltinType("StackTrace"); + try sema.resolveTypeFields(stack_trace_ty); const field_name = try mod.intern_pool.getOrPutString(sema.gpa, "index"); const field_index = try sema.structFieldIndex(block, stack_trace_ty, field_name, call_src); @@ -8084,7 +8109,7 @@ fn zirOptionalType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro fn zirElemTypeIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const mod = sema.mod; const bin = sema.code.instructions.items(.data)[inst].bin; - const operand = sema.resolveType(block, .unneeded, bin.lhs) catch |err| switch (err) { + const indexable_ty = sema.resolveType(block, .unneeded, bin.lhs) catch |err| switch (err) { // Since this is a ZIR instruction that returns a type, encountering // generic poison should not result in a failed compilation, but the // generic poison type. This prevents unnecessary failures when @@ -8092,7 +8117,7 @@ fn zirElemTypeIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr error.GenericPoison => return .generic_poison_type, else => |e| return e, }; - const indexable_ty = try sema.resolveTypeFields(operand); + try sema.resolveTypeFields(indexable_ty); assert(indexable_ty.isIndexable(mod)); // validated by a previous instruction if (indexable_ty.zigTypeTag(mod) == .Struct) { const elem_type = indexable_ty.structFieldType(@intFromEnum(bin.rhs), mod); @@ -8420,8 +8445,8 @@ fn zirIntFromEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const enum_tag: Air.Inst.Ref = switch (operand_ty.zigTypeTag(mod)) { .Enum => operand, .Union => blk: { - const union_ty = try sema.resolveTypeFields(operand_ty); - const tag_ty = union_ty.unionTagType(mod) orelse { + try sema.resolveTypeFields(operand_ty); + const tag_ty = operand_ty.unionTagType(mod) orelse { return sema.fail( block, operand_src, @@ -9573,7 +9598,7 @@ fn finishFunc( // Make sure that StackTrace's fields are resolved so that the backend can // lower this fn type. const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace"); - _ = try sema.resolveTypeFields(unresolved_stack_trace_ty); + try sema.resolveTypeFields(unresolved_stack_trace_ty); } return Air.internedToRef(if (opt_func_index != .none) opt_func_index else func_ty); @@ -10890,12 +10915,12 @@ fn switchCond( }, .Union => { - const union_ty = try sema.resolveTypeFields(operand_ty); - const enum_ty = union_ty.unionTagType(mod) orelse { + try sema.resolveTypeFields(operand_ty); + const enum_ty = operand_ty.unionTagType(mod) orelse { const msg = msg: { const msg = try sema.errMsg(block, src, "switch on union with no attached enum", .{}); errdefer msg.destroy(sema.gpa); - if (union_ty.declSrcLocOrNull(mod)) |union_src| { + if (operand_ty.declSrcLocOrNull(mod)) |union_src| { try mod.errNoteNonLazy(union_src, msg, "consider 'union(enum)' here", .{}); } break :msg msg; @@ -12780,9 +12805,9 @@ fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; - const unresolved_ty = try sema.resolveType(block, ty_src, extra.lhs); + const ty = try sema.resolveType(block, ty_src, extra.lhs); const field_name = try sema.resolveConstStringIntern(block, name_src, extra.rhs, "field name must be comptime-known"); - const ty = try sema.resolveTypeFields(unresolved_ty); + try sema.resolveTypeFields(ty); const ip = &mod.intern_pool; const has_field = hf: { @@ -16141,7 +16166,8 @@ fn analyzeCmpUnionTag( op: std.math.CompareOperator, ) CompileError!Air.Inst.Ref { const mod = sema.mod; - const union_ty = try sema.resolveTypeFields(sema.typeOf(un)); + const union_ty = sema.typeOf(un); + try sema.resolveTypeFields(union_ty); const union_tag_ty = union_ty.unionTagType(mod) orelse { const msg = msg: { const msg = try sema.errMsg(block, un_src, "comparison of union and enum literal is only valid for tagged union types", .{}); @@ -17278,11 +17304,10 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai break :t union_field_ty_decl.val.toType(); }; - const union_ty = try sema.resolveTypeFields(ty); try sema.resolveTypeLayout(ty); // Getting alignment requires type layout - const layout = union_ty.containerLayout(mod); + const layout = ty.containerLayout(mod); - const union_fields = union_ty.unionFields(mod); + const union_fields = ty.unionFields(mod); const union_field_vals = try gpa.alloc(InternPool.Index, union_fields.count()); defer gpa.free(union_field_vals); @@ -17357,11 +17382,11 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai } }); }; - const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, union_ty.getNamespaceIndex(mod)); + const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, ty.getNamespaceIndex(mod)); const enum_tag_ty_val = try mod.intern(.{ .opt = .{ .ty = (try mod.optionalType(.type_type)).toIntern(), - .val = if (union_ty.unionTagType(mod)) |tag_ty| tag_ty.toIntern() else .none, + .val = if (ty.unionTagType(mod)) |tag_ty| tag_ty.toIntern() else .none, } }); const container_layout_ty = t: { @@ -17429,18 +17454,17 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai break :t struct_field_ty_decl.val.toType(); }; - const struct_ty = try sema.resolveTypeFields(ty); try sema.resolveTypeLayout(ty); // Getting alignment requires type layout - const layout = struct_ty.containerLayout(mod); + const layout = ty.containerLayout(mod); var struct_field_vals: []InternPool.Index = &.{}; defer gpa.free(struct_field_vals); fv: { - const struct_type = switch (ip.indexToKey(struct_ty.toIntern())) { + const struct_type = switch (ip.indexToKey(ty.toIntern())) { .anon_struct_type => |tuple| { struct_field_vals = try gpa.alloc(InternPool.Index, tuple.types.len); for (struct_field_vals, 0..) |*struct_field_val, i| { - const anon_struct_type = ip.indexToKey(struct_ty.toIntern()).anon_struct_type; + const anon_struct_type = ip.indexToKey(ty.toIntern()).anon_struct_type; const field_ty = anon_struct_type.types[i]; const field_val = anon_struct_type.values[i]; const name_val = v: { @@ -17449,7 +17473,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai // TODO: write something like getCoercedInts to avoid needing to dupe const bytes = if (tuple.names.len != 0) // https://github.com/ziglang/zig/issues/15709 - try sema.arena.dupe(u8, ip.stringToSlice(ip.indexToKey(struct_ty.toIntern()).anon_struct_type.names[i])) + try sema.arena.dupe(u8, ip.stringToSlice(ip.indexToKey(ty.toIntern()).anon_struct_type.names[i])) else try std.fmt.allocPrint(sema.arena, "{d}", .{i}); const new_decl_ty = try mod.arrayType(.{ @@ -17582,12 +17606,12 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai } }); }; - const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, struct_ty.getNamespaceIndex(mod)); + const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, ty.getNamespaceIndex(mod)); const backing_integer_val = try mod.intern(.{ .opt = .{ .ty = (try mod.optionalType(.type_type)).toIntern(), .val = if (layout == .Packed) val: { - const struct_obj = mod.typeToStruct(struct_ty).?; + const struct_obj = mod.typeToStruct(ty).?; assert(struct_obj.haveLayout()); assert(struct_obj.backing_int_ty.isInt(mod)); break :val struct_obj.backing_int_ty.toIntern(); @@ -17617,7 +17641,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai // decls: []const Declaration, decls_val, // is_tuple: bool, - Value.makeBool(struct_ty.isTuple(mod)).toIntern(), + Value.makeBool(ty.isTuple(mod)).toIntern(), }; return Air.internedToRef((try mod.intern(.{ .un = .{ .ty = type_info_ty.toIntern(), @@ -17644,8 +17668,8 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai break :t type_opaque_ty_decl.val.toType(); }; - const opaque_ty = try sema.resolveTypeFields(ty); - const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, opaque_ty.getNamespaceIndex(mod)); + try sema.resolveTypeFields(ty); + const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, ty.getNamespaceIndex(mod)); const field_values = .{ // decls: []const Declaration, @@ -18511,8 +18535,8 @@ fn retWithErrTracing( else => true, }; const gpa = sema.gpa; - const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace"); - const stack_trace_ty = try sema.resolveTypeFields(unresolved_stack_trace_ty); + const stack_trace_ty = try sema.getBuiltinType("StackTrace"); + try sema.resolveTypeFields(stack_trace_ty); const ptr_stack_trace_ty = try mod.singleMutPtrType(stack_trace_ty); const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty); const return_err_fn = try sema.getBuiltin("returnError"); @@ -18874,14 +18898,14 @@ fn zirStructInitEmpty(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE fn structInitEmpty( sema: *Sema, block: *Block, - obj_ty: Type, + struct_ty: Type, dest_src: LazySrcLoc, init_src: LazySrcLoc, ) CompileError!Air.Inst.Ref { const mod = sema.mod; const gpa = sema.gpa; // This logic must be synchronized with that in `zirStructInit`. - const struct_ty = try sema.resolveTypeFields(obj_ty); + try sema.resolveTypeFields(struct_ty); // The init values to use for the struct instance. const field_inits = try gpa.alloc(Air.Inst.Ref, struct_ty.structFieldCount(mod)); @@ -19674,8 +19698,7 @@ fn fieldType( const mod = sema.mod; var cur_ty = aggregate_ty; while (true) { - const resolved_ty = try sema.resolveTypeFields(cur_ty); - cur_ty = resolved_ty; + try sema.resolveTypeFields(cur_ty); switch (cur_ty.zigTypeTag(mod)) { .Struct => switch (mod.intern_pool.indexToKey(cur_ty.toIntern())) { .anon_struct_type => |anon_struct| { @@ -19709,7 +19732,7 @@ fn fieldType( else => {}, } return sema.fail(block, ty_src, "expected struct or union; found '{}'", .{ - resolved_ty.fmt(sema.mod), + cur_ty.fmt(sema.mod), }); } } @@ -19721,8 +19744,8 @@ fn zirErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref { fn getErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref { const mod = sema.mod; const ip = &mod.intern_pool; - const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace"); - const stack_trace_ty = try sema.resolveTypeFields(unresolved_stack_trace_ty); + const stack_trace_ty = try sema.getBuiltinType("StackTrace"); + try sema.resolveTypeFields(stack_trace_ty); const ptr_stack_trace_ty = try mod.singleMutPtrType(stack_trace_ty); const opt_ptr_stack_trace_ty = try mod.optionalType(ptr_stack_trace_ty.toIntern()); @@ -20048,14 +20071,10 @@ fn zirReify( (try alignment_val.getUnsignedIntAdvanced(mod, sema)).?, ); - const unresolved_elem_ty = child_val.toType(); - const elem_ty = if (abi_align == .none) - unresolved_elem_ty - else t: { - const elem_ty = try sema.resolveTypeFields(unresolved_elem_ty); + const elem_ty = child_val.toType(); + if (abi_align != .none) { try sema.resolveTypeLayout(elem_ty); - break :t elem_ty; - }; + } const ptr_size = mod.toEnum(std.builtin.Type.Pointer.Size, size_val); @@ -25070,8 +25089,8 @@ fn prepareSimplePanic(sema: *Sema, block: *Block) !void { } if (mod.null_stack_trace == .none) { - const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace"); - const stack_trace_ty = try sema.resolveTypeFields(unresolved_stack_trace_ty); + const stack_trace_ty = try sema.getBuiltinType("StackTrace"); + try sema.resolveTypeFields(stack_trace_ty); const target = mod.getTarget(); const ptr_stack_trace_ty = try mod.ptrType(.{ .child = stack_trace_ty.toIntern(), @@ -25523,14 +25542,14 @@ fn fieldVal( return inst; } } - const union_ty = try sema.resolveTypeFields(child_type); - if (union_ty.unionTagType(mod)) |enum_ty| { + try sema.resolveTypeFields(child_type); + if (child_type.unionTagType(mod)) |enum_ty| { if (enum_ty.enumFieldIndex(field_name, mod)) |field_index_usize| { const field_index = @as(u32, @intCast(field_index_usize)); return Air.internedToRef((try mod.enumValueFieldIndex(enum_ty, field_index)).toIntern()); } } - return sema.failWithBadMemberAccess(block, union_ty, field_name_src, field_name); + return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name); }, .Enum => { if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| { @@ -25749,8 +25768,8 @@ fn fieldPtr( return inst; } } - const union_ty = try sema.resolveTypeFields(child_type); - if (union_ty.unionTagType(mod)) |enum_ty| { + try sema.resolveTypeFields(child_type); + if (child_type.unionTagType(mod)) |enum_ty| { if (enum_ty.enumFieldIndex(field_name, mod)) |field_index| { const field_index_u32 = @as(u32, @intCast(field_index)); var anon_decl = try block.startAnonDecl(); @@ -25854,35 +25873,35 @@ fn fieldCallBind( find_field: { switch (concrete_ty.zigTypeTag(mod)) { .Struct => { - const struct_ty = try sema.resolveTypeFields(concrete_ty); - if (mod.typeToStruct(struct_ty)) |struct_obj| { + try sema.resolveTypeFields(concrete_ty); + if (mod.typeToStruct(concrete_ty)) |struct_obj| { const field_index_usize = struct_obj.fields.getIndex(field_name) orelse break :find_field; const field_index = @as(u32, @intCast(field_index_usize)); const field = struct_obj.fields.values()[field_index]; return sema.finishFieldCallBind(block, src, ptr_ty, field.ty, field_index, object_ptr); - } else if (struct_ty.isTuple(mod)) { + } else if (concrete_ty.isTuple(mod)) { if (ip.stringEqlSlice(field_name, "len")) { - return .{ .direct = try mod.intRef(Type.usize, struct_ty.structFieldCount(mod)) }; + return .{ .direct = try mod.intRef(Type.usize, concrete_ty.structFieldCount(mod)) }; } if (field_name.toUnsigned(ip)) |field_index| { - if (field_index >= struct_ty.structFieldCount(mod)) break :find_field; - return sema.finishFieldCallBind(block, src, ptr_ty, struct_ty.structFieldType(field_index, mod), field_index, object_ptr); + if (field_index >= concrete_ty.structFieldCount(mod)) break :find_field; + return sema.finishFieldCallBind(block, src, ptr_ty, concrete_ty.structFieldType(field_index, mod), field_index, object_ptr); } } else { - const max = struct_ty.structFieldCount(mod); + const max = concrete_ty.structFieldCount(mod); for (0..max) |i_usize| { const i = @as(u32, @intCast(i_usize)); - if (field_name == struct_ty.structFieldName(i, mod)) { - return sema.finishFieldCallBind(block, src, ptr_ty, struct_ty.structFieldType(i, mod), i, object_ptr); + if (field_name == concrete_ty.structFieldName(i, mod)) { + return sema.finishFieldCallBind(block, src, ptr_ty, concrete_ty.structFieldType(i, mod), i, object_ptr); } } } }, .Union => { - const union_ty = try sema.resolveTypeFields(concrete_ty); - const fields = union_ty.unionFields(mod); + try sema.resolveTypeFields(concrete_ty); + const fields = concrete_ty.unionFields(mod); const field_index_usize = fields.getIndex(field_name) orelse break :find_field; const field_index = @as(u32, @intCast(field_index_usize)); const field = fields.values()[field_index]; @@ -26081,13 +26100,13 @@ fn structFieldPtr( struct_ptr: Air.Inst.Ref, field_name: InternPool.NullTerminatedString, field_name_src: LazySrcLoc, - unresolved_struct_ty: Type, + struct_ty: Type, initializing: bool, ) CompileError!Air.Inst.Ref { const mod = sema.mod; - assert(unresolved_struct_ty.zigTypeTag(mod) == .Struct); + assert(struct_ty.zigTypeTag(mod) == .Struct); - const struct_ty = try sema.resolveTypeFields(unresolved_struct_ty); + try sema.resolveTypeFields(struct_ty); try sema.resolveStructLayout(struct_ty); if (struct_ty.isTuple(mod)) { @@ -26234,12 +26253,12 @@ fn structFieldVal( struct_byval: Air.Inst.Ref, field_name: InternPool.NullTerminatedString, field_name_src: LazySrcLoc, - unresolved_struct_ty: Type, + struct_ty: Type, ) CompileError!Air.Inst.Ref { const mod = sema.mod; - assert(unresolved_struct_ty.zigTypeTag(mod) == .Struct); + assert(struct_ty.zigTypeTag(mod) == .Struct); - const struct_ty = try sema.resolveTypeFields(unresolved_struct_ty); + try sema.resolveTypeFields(struct_ty); switch (mod.intern_pool.indexToKey(struct_ty.toIntern())) { .struct_type => |struct_type| { const struct_obj = mod.structPtrUnwrap(struct_type.index).?; @@ -26361,17 +26380,17 @@ fn unionFieldPtr( union_ptr: Air.Inst.Ref, field_name: InternPool.NullTerminatedString, field_name_src: LazySrcLoc, - unresolved_union_ty: Type, + union_ty: Type, initializing: bool, ) CompileError!Air.Inst.Ref { const mod = sema.mod; const ip = &mod.intern_pool; - assert(unresolved_union_ty.zigTypeTag(mod) == .Union); + assert(union_ty.zigTypeTag(mod) == .Union); const union_ptr_ty = sema.typeOf(union_ptr); const union_ptr_info = union_ptr_ty.ptrInfo(mod); - const union_ty = try sema.resolveTypeFields(unresolved_union_ty); + try sema.resolveTypeFields(union_ty); const union_obj = mod.typeToUnion(union_ty).?; const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src); const field = union_obj.fields.values()[field_index]; @@ -26467,13 +26486,13 @@ fn unionFieldVal( union_byval: Air.Inst.Ref, field_name: InternPool.NullTerminatedString, field_name_src: LazySrcLoc, - unresolved_union_ty: Type, + union_ty: Type, ) CompileError!Air.Inst.Ref { const mod = sema.mod; const ip = &mod.intern_pool; - assert(unresolved_union_ty.zigTypeTag(mod) == .Union); + assert(union_ty.zigTypeTag(mod) == .Union); - const union_ty = try sema.resolveTypeFields(unresolved_union_ty); + try sema.resolveTypeFields(union_ty); const union_obj = mod.typeToUnion(union_ty).?; const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src); const field = union_obj.fields.values()[field_index]; @@ -26733,7 +26752,7 @@ fn tupleFieldPtr( const mod = sema.mod; const tuple_ptr_ty = sema.typeOf(tuple_ptr); const tuple_ty = tuple_ptr_ty.childType(mod); - _ = try sema.resolveTypeFields(tuple_ty); + try sema.resolveTypeFields(tuple_ty); const field_count = tuple_ty.structFieldCount(mod); if (field_count == 0) { @@ -26790,7 +26809,8 @@ fn tupleField( field_index: u32, ) CompileError!Air.Inst.Ref { const mod = sema.mod; - const tuple_ty = try sema.resolveTypeFields(sema.typeOf(tuple)); + const tuple_ty = sema.typeOf(tuple); + try sema.resolveTypeFields(tuple_ty); const field_count = tuple_ty.structFieldCount(mod); if (field_count == 0) { @@ -26872,6 +26892,7 @@ fn elemValArray( const runtime_src = if (maybe_undef_array_val != null) elem_index_src else array_src; try sema.requireRuntimeBlock(block, src, runtime_src); + try sema.queueFullTypeResolution(array_ty); if (oob_safety and block.wantSafety()) { // Runtime check is only needed if unable to comptime check if (maybe_index_val == null) { @@ -27113,16 +27134,17 @@ const CoerceOpts = struct { fn coerceExtra( sema: *Sema, block: *Block, - dest_ty_unresolved: Type, + dest_ty: Type, inst: Air.Inst.Ref, inst_src: LazySrcLoc, opts: CoerceOpts, ) CoersionError!Air.Inst.Ref { - if (dest_ty_unresolved.isGenericPoison()) return inst; + if (dest_ty.isGenericPoison()) return inst; const mod = sema.mod; const dest_ty_src = inst_src; // TODO better source location - const dest_ty = try sema.resolveTypeFields(dest_ty_unresolved); - const inst_ty = try sema.resolveTypeFields(sema.typeOf(inst)); + try sema.resolveTypeFields(dest_ty); + const inst_ty = sema.typeOf(inst); + try sema.resolveTypeFields(inst_ty); const target = mod.getTarget(); // If the types are the same, we can return the operand. if (dest_ty.eql(inst_ty, mod)) @@ -29830,16 +29852,15 @@ fn beginComptimePtrLoad( fn bitCast( sema: *Sema, block: *Block, - dest_ty_unresolved: Type, + dest_ty: Type, inst: Air.Inst.Ref, inst_src: LazySrcLoc, operand_src: ?LazySrcLoc, ) CompileError!Air.Inst.Ref { const mod = sema.mod; - const dest_ty = try sema.resolveTypeFields(dest_ty_unresolved); try sema.resolveTypeLayout(dest_ty); - const old_ty = try sema.resolveTypeFields(sema.typeOf(inst)); + const old_ty = sema.typeOf(inst); try sema.resolveTypeLayout(old_ty); const dest_bits = dest_ty.bitSize(mod); @@ -30042,8 +30063,8 @@ fn coerceEnumToUnion( const union_obj = mod.typeToUnion(union_ty).?; const field = union_obj.fields.values()[field_index]; - const field_ty = try sema.resolveTypeFields(field.ty); - if (field_ty.zigTypeTag(mod) == .NoReturn) { + try sema.resolveTypeFields(field.ty); + if (field.ty.zigTypeTag(mod) == .NoReturn) { const msg = msg: { const msg = try sema.errMsg(block, inst_src, "cannot initialize 'noreturn' field of union", .{}); errdefer msg.destroy(sema.gpa); @@ -30057,12 +30078,12 @@ fn coerceEnumToUnion( }; return sema.failWithOwnedErrorMsg(msg); } - const opv = (try sema.typeHasOnePossibleValue(field_ty)) orelse { + const opv = (try sema.typeHasOnePossibleValue(field.ty)) orelse { const msg = msg: { const field_name = union_obj.fields.keys()[field_index]; const msg = try sema.errMsg(block, inst_src, "coercion from enum '{}' to union '{}' must initialize '{}' field '{}'", .{ inst_ty.fmt(sema.mod), union_ty.fmt(sema.mod), - field_ty.fmt(sema.mod), field_name.fmt(ip), + field.ty.fmt(sema.mod), field_name.fmt(ip), }); errdefer msg.destroy(sema.gpa); @@ -30426,13 +30447,13 @@ fn coerceTupleToArrayPtrs( fn coerceTupleToStruct( sema: *Sema, block: *Block, - dest_ty: Type, + struct_ty: Type, inst: Air.Inst.Ref, inst_src: LazySrcLoc, ) !Air.Inst.Ref { const mod = sema.mod; const ip = &mod.intern_pool; - const struct_ty = try sema.resolveTypeFields(dest_ty); + try sema.resolveTypeFields(struct_ty); if (struct_ty.isTupleOrAnonStruct(mod)) { return sema.coerceTupleToTuple(block, struct_ty, inst, inst_src); @@ -33728,6 +33749,10 @@ fn resolveLazyValue(sema: *Sema, val: Value) CompileError!Value { pub fn resolveTypeLayout(sema: *Sema, ty: Type) CompileError!void { const mod = sema.mod; + switch (mod.intern_pool.indexToKey(ty.toIntern())) { + .simple_type => |simple_type| return sema.resolveSimpleType(simple_type), + else => {}, + } switch (ty.zigTypeTag(mod)) { .Struct => return sema.resolveStructLayout(ty), .Union => return sema.resolveUnionLayout(ty), @@ -33768,8 +33793,8 @@ pub fn resolveTypeLayout(sema: *Sema, ty: Type) CompileError!void { fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void { const mod = sema.mod; - const resolved_ty = try sema.resolveTypeFields(ty); - if (mod.typeToStruct(resolved_ty)) |struct_obj| { + try sema.resolveTypeFields(ty); + if (mod.typeToStruct(ty)) |struct_obj| { switch (struct_obj.status) { .none, .have_field_types => {}, .field_types_wip, .layout_wip => { @@ -33805,9 +33830,9 @@ fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void { } struct_obj.status = .have_layout; - _ = try sema.resolveTypeRequiresComptime(resolved_ty); + _ = try sema.resolveTypeRequiresComptime(ty); - if (struct_obj.assumed_runtime_bits and !(try sema.typeHasRuntimeBits(resolved_ty))) { + if (struct_obj.assumed_runtime_bits and !(try sema.typeHasRuntimeBits(ty))) { const msg = try Module.ErrorMsg.create( sema.gpa, struct_obj.srcLoc(mod), @@ -34019,8 +34044,8 @@ fn checkMemOperand(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void fn resolveUnionLayout(sema: *Sema, ty: Type) CompileError!void { const mod = sema.mod; - const resolved_ty = try sema.resolveTypeFields(ty); - const union_obj = mod.typeToUnion(resolved_ty).?; + try sema.resolveTypeFields(ty); + const union_obj = mod.typeToUnion(ty).?; switch (union_obj.status) { .none, .have_field_types => {}, .field_types_wip, .layout_wip => { @@ -34051,9 +34076,9 @@ fn resolveUnionLayout(sema: *Sema, ty: Type) CompileError!void { }; } union_obj.status = .have_layout; - _ = try sema.resolveTypeRequiresComptime(resolved_ty); + _ = try sema.resolveTypeRequiresComptime(ty); - if (union_obj.assumed_runtime_bits and !(try sema.typeHasRuntimeBits(resolved_ty))) { + if (union_obj.assumed_runtime_bits and !(try sema.typeHasRuntimeBits(ty))) { const msg = try Module.ErrorMsg.create( sema.gpa, union_obj.srcLoc(sema.mod), @@ -34227,8 +34252,7 @@ pub fn resolveTypeFully(sema: *Sema, ty: Type) CompileError!void { const mod = sema.mod; switch (ty.zigTypeTag(mod)) { .Pointer => { - const child_ty = try sema.resolveTypeFields(ty.childType(mod)); - return sema.resolveTypeFully(child_ty); + return sema.resolveTypeFully(ty.childType(mod)); }, .Struct => switch (mod.intern_pool.indexToKey(ty.toIntern())) { .struct_type => return sema.resolveStructFully(ty), @@ -34237,6 +34261,7 @@ pub fn resolveTypeFully(sema: *Sema, ty: Type) CompileError!void { try sema.resolveTypeFully(field_ty.toType()); } }, + .simple_type => |simple_type| try sema.resolveSimpleType(simple_type), else => {}, }, .Union => return sema.resolveUnionFully(ty), @@ -34267,8 +34292,8 @@ fn resolveStructFully(sema: *Sema, ty: Type) CompileError!void { try sema.resolveStructLayout(ty); const mod = sema.mod; - const resolved_ty = try sema.resolveTypeFields(ty); - const struct_obj = mod.typeToStruct(resolved_ty).?; + try sema.resolveTypeFields(ty); + const struct_obj = mod.typeToStruct(ty).?; switch (struct_obj.status) { .none, .have_field_types, .field_types_wip, .layout_wip, .have_layout => {}, @@ -34297,8 +34322,8 @@ fn resolveUnionFully(sema: *Sema, ty: Type) CompileError!void { try sema.resolveUnionLayout(ty); const mod = sema.mod; - const resolved_ty = try sema.resolveTypeFields(ty); - const union_obj = mod.typeToUnion(resolved_ty).?; + try sema.resolveTypeFields(ty); + const union_obj = mod.typeToUnion(ty).?; switch (union_obj.status) { .none, .have_field_types, .field_types_wip, .layout_wip, .have_layout => {}, .fully_resolved_wip, .fully_resolved => return, @@ -34322,7 +34347,7 @@ fn resolveUnionFully(sema: *Sema, ty: Type) CompileError!void { _ = try sema.typeRequiresComptime(ty); } -pub fn resolveTypeFields(sema: *Sema, ty: Type) CompileError!Type { +pub fn resolveTypeFields(sema: *Sema, ty: Type) CompileError!void { const mod = sema.mod; switch (ty.toIntern()) { @@ -34385,7 +34410,7 @@ pub fn resolveTypeFields(sema: *Sema, ty: Type) CompileError!Type { .anyerror_void_error_union_type, .generic_poison_type, .empty_struct_type, - => return ty, + => {}, .undef => unreachable, .zero => unreachable, @@ -34406,42 +34431,52 @@ pub fn resolveTypeFields(sema: *Sema, ty: Type) CompileError!Type { .empty_struct => unreachable, .generic_poison => unreachable, - .type_info_type => return sema.getBuiltinType("Type"), - .extern_options_type => return sema.getBuiltinType("ExternOptions"), - .export_options_type => return sema.getBuiltinType("ExportOptions"), - .atomic_order_type => return sema.getBuiltinType("AtomicOrder"), - .atomic_rmw_op_type => return sema.getBuiltinType("AtomicRmwOp"), - .calling_convention_type => return sema.getBuiltinType("CallingConvention"), - .address_space_type => return sema.getBuiltinType("AddressSpace"), - .float_mode_type => return sema.getBuiltinType("FloatMode"), - .reduce_op_type => return sema.getBuiltinType("ReduceOp"), - .call_modifier_type => return sema.getBuiltinType("CallModifier"), - .prefetch_options_type => return sema.getBuiltinType("PrefetchOptions"), - - _ => switch (mod.intern_pool.items.items(.tag)[@intFromEnum(ty.toIntern())]) { + else => switch (mod.intern_pool.items.items(.tag)[@intFromEnum(ty.toIntern())]) { .type_struct, .type_struct_ns, .type_union_tagged, .type_union_untagged, .type_union_safety, + .simple_type, => switch (mod.intern_pool.indexToKey(ty.toIntern())) { .struct_type => |struct_type| { - const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse return ty; + const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse return; try sema.resolveTypeFieldsStruct(ty, struct_obj); - return ty; }, .union_type => |union_type| { const union_obj = mod.unionPtr(union_type.index); try sema.resolveTypeFieldsUnion(ty, union_obj); - return ty; }, + .simple_type => |simple_type| try sema.resolveSimpleType(simple_type), else => unreachable, }, - else => return ty, + else => {}, }, } } +/// Fully resolves a simple type. This is usually a nop, but for builtin types with +/// special InternPool indices (such as std.builtin.Type) it will analyze and fully +/// resolve the container type. +fn resolveSimpleType(sema: *Sema, simple_type: InternPool.SimpleType) CompileError!void { + const builtin_type_name: []const u8 = switch (simple_type) { + .atomic_order => "AtomicOrder", + .atomic_rmw_op => "AtomicRmwOp", + .calling_convention => "CallingConvention", + .address_space => "AddressSpace", + .float_mode => "FloatMode", + .reduce_op => "ReduceOp", + .call_modifier => "CallModifer", + .prefetch_options => "PrefetchOptions", + .export_options => "ExportOptions", + .extern_options => "ExternOptions", + .type_info => "Type", + else => return, + }; + // This will fully resolve the type. + _ = try sema.getBuiltinType(builtin_type_name); +} + fn resolveTypeFieldsStruct( sema: *Sema, ty: Type, @@ -35784,7 +35819,7 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { }, .struct_type => |struct_type| { - const resolved_ty = try sema.resolveTypeFields(ty); + try sema.resolveTypeFields(ty); if (mod.structPtrUnwrap(struct_type.index)) |s| { const field_vals = try sema.arena.alloc(InternPool.Index, s.fields.count()); for (field_vals, s.fields.values(), 0..) |*field_val, field, i| { @@ -35792,14 +35827,14 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { field_val.* = field.default_val; continue; } - if (field.ty.eql(resolved_ty, sema.mod)) { + if (field.ty.eql(ty, sema.mod)) { const msg = try Module.ErrorMsg.create( sema.gpa, s.srcLoc(sema.mod), "struct '{}' depends on itself", .{ty.fmt(sema.mod)}, ); - try sema.addFieldErrNote(resolved_ty, i, msg, "while checking this field", .{}); + try sema.addFieldErrNote(ty, i, msg, "while checking this field", .{}); return sema.failWithOwnedErrorMsg(msg); } if (try sema.typeHasOnePossibleValue(field.ty)) |field_opv| { @@ -35837,7 +35872,7 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { }, .union_type => |union_type| { - const resolved_ty = try sema.resolveTypeFields(ty); + try sema.resolveTypeFields(ty); const union_obj = mod.unionPtr(union_type.index); const tag_val = (try sema.typeHasOnePossibleValue(union_obj.tag_ty)) orelse return null; @@ -35847,20 +35882,20 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { return only.toValue(); } const only_field = fields[0]; - if (only_field.ty.eql(resolved_ty, sema.mod)) { + if (only_field.ty.eql(ty, sema.mod)) { const msg = try Module.ErrorMsg.create( sema.gpa, union_obj.srcLoc(sema.mod), "union '{}' depends on itself", .{ty.fmt(sema.mod)}, ); - try sema.addFieldErrNote(resolved_ty, 0, msg, "while checking this field", .{}); + try sema.addFieldErrNote(ty, 0, msg, "while checking this field", .{}); return sema.failWithOwnedErrorMsg(msg); } const val_val = (try sema.typeHasOnePossibleValue(only_field.ty)) orelse return null; const only = try mod.intern(.{ .un = .{ - .ty = resolved_ty.toIntern(), + .ty = ty.toIntern(), .tag = tag_val.toIntern(), .val = val_val.toIntern(), } }); @@ -36430,12 +36465,12 @@ pub fn fnHasRuntimeBits(sema: *Sema, ty: Type) CompileError!bool { fn unionFieldIndex( sema: *Sema, block: *Block, - unresolved_union_ty: Type, + union_ty: Type, field_name: InternPool.NullTerminatedString, field_src: LazySrcLoc, ) !u32 { const mod = sema.mod; - const union_ty = try sema.resolveTypeFields(unresolved_union_ty); + try sema.resolveTypeFields(union_ty); const union_obj = mod.typeToUnion(union_ty).?; const field_index_usize = union_obj.fields.getIndex(field_name) orelse return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name); @@ -36445,12 +36480,12 @@ fn unionFieldIndex( fn structFieldIndex( sema: *Sema, block: *Block, - unresolved_struct_ty: Type, + struct_ty: Type, field_name: InternPool.NullTerminatedString, field_src: LazySrcLoc, ) !u32 { const mod = sema.mod; - const struct_ty = try sema.resolveTypeFields(unresolved_struct_ty); + try sema.resolveTypeFields(struct_ty); if (struct_ty.isAnonStruct(mod)) { return sema.anonStructFieldIndex(block, struct_ty, field_name, field_src); } else { diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 1ddf211bda..3db6321119 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -3134,17 +3134,6 @@ pub const Object = struct { .null_type, .undefined_type, .enum_literal_type, - .atomic_order_type, - .atomic_rmw_op_type, - .calling_convention_type, - .address_space_type, - .float_mode_type, - .reduce_op_type, - .call_modifier_type, - .prefetch_options_type, - .export_options_type, - .extern_options_type, - .type_info_type, => unreachable, .manyptr_u8_type, .manyptr_const_u8_type, diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 06458ea3cd..bbcfbb7047 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -718,7 +718,7 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void { } try self.eh_frame_relocs_lookup.ensureTotalCapacity(gpa, record_count); - try self.eh_frame_records_lookup.ensureTotalCapacity(gpa, record_count); + try self.eh_frame_records_lookup.ensureUnusedCapacity(gpa, record_count); it.reset(); @@ -768,11 +768,28 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void { else => unreachable, } }; - log.debug("FDE at offset {x} tracks {s}", .{ offset, zld.getSymbolName(target) }); if (target.getFile() != object_id) { + log.debug("FDE at offset {x} marked DEAD", .{offset}); self.eh_frame_relocs_lookup.getPtr(offset).?.dead = true; } else { - self.eh_frame_records_lookup.putAssumeCapacityNoClobber(target, offset); + // You would think that we are done but turns out that the compilers may use + // whichever symbol alias they want for a target symbol. This in particular + // very problematic when using Zig's @export feature to re-export symbols under + // additional names. For that reason, we need to ensure we record aliases here + // too so that we can tie them with their matching unwind records and vice versa. + const aliases = self.getSymbolAliases(target.sym_index); + var i: u32 = 0; + while (i < aliases.len) : (i += 1) { + const actual_target = SymbolWithLoc{ + .sym_index = i + aliases.start, + .file = target.file, + }; + log.debug("FDE at offset {x} tracks {s}", .{ + offset, + zld.getSymbolName(actual_target), + }); + try self.eh_frame_records_lookup.putNoClobber(gpa, actual_target, offset); + } } } } @@ -803,7 +820,7 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void { const unwind_records = self.getUnwindRecords(); - try self.unwind_records_lookup.ensureTotalCapacity(gpa, @as(u32, @intCast(unwind_records.len))); + try self.unwind_records_lookup.ensureUnusedCapacity(gpa, @as(u32, @intCast(unwind_records.len))); const needs_eh_frame = for (unwind_records) |record| { if (UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) break true; @@ -839,11 +856,28 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void { .code = mem.asBytes(&record), .base_offset = @as(i32, @intCast(offset)), }); - log.debug("unwind record {d} tracks {s}", .{ record_id, zld.getSymbolName(target) }); if (target.getFile() != object_id) { + log.debug("unwind record {d} marked DEAD", .{record_id}); self.unwind_relocs_lookup[record_id].dead = true; } else { - self.unwind_records_lookup.putAssumeCapacityNoClobber(target, @as(u32, @intCast(record_id))); + // You would think that we are done but turns out that the compilers may use + // whichever symbol alias they want for a target symbol. This in particular + // very problematic when using Zig's @export feature to re-export symbols under + // additional names. For that reason, we need to ensure we record aliases here + // too so that we can tie them with their matching unwind records and vice versa. + const aliases = self.getSymbolAliases(target.sym_index); + var i: u32 = 0; + while (i < aliases.len) : (i += 1) { + const actual_target = SymbolWithLoc{ + .sym_index = i + aliases.start, + .file = target.file, + }; + log.debug("unwind record {d} tracks {s}", .{ + record_id, + zld.getSymbolName(actual_target), + }); + try self.unwind_records_lookup.putNoClobber(gpa, actual_target, @intCast(record_id)); + } } } } @@ -991,6 +1025,18 @@ pub fn getSymbolName(self: Object, index: u32) []const u8 { return strtab[start..][0 .. len - 1 :0]; } +fn getSymbolAliases(self: Object, index: u32) Entry { + const addr = self.source_address_lookup[index]; + var start = index; + while (start > 0 and + self.source_address_lookup[start - 1] == addr) : (start -= 1) + {} + const end: u32 = for (self.source_address_lookup[start..], start..) |saddr, i| { + if (saddr != addr) break @as(u32, @intCast(i)); + } else @as(u32, @intCast(self.source_address_lookup.len)); + return .{ .start = start, .len = end - start }; +} + pub fn getSymbolByAddress(self: Object, addr: u64, sect_hint: ?u8) u32 { // Find containing atom const Predicate = struct { @@ -1012,11 +1058,8 @@ pub fn getSymbolByAddress(self: Object, addr: u64, sect_hint: ?u8) u32 { if (target_sym_index > 0) { // Hone in on the most senior alias of the target symbol. // See SymbolAtIndex.lessThan for more context. - var start = target_sym_index - 1; - while (start > 0 and - self.source_address_lookup[lookup.start..][start - 1] == addr) : (start -= 1) - {} - return @as(u32, @intCast(lookup.start + start)); + const aliases = self.getSymbolAliases(@intCast(lookup.start + target_sym_index - 1)); + return aliases.start; } } return self.getSectionAliasSymbolIndex(sect_id); diff --git a/src/translate_c.zig b/src/translate_c.zig index 5886947b0e..93c85ab8bd 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -965,6 +965,7 @@ fn buildFlexibleArrayFn( field_decl: *const clang.FieldDecl, ) TypeError!Node { const field_qt = field_decl.getType(); + const field_qt_canon = qualTypeCanon(field_qt); const u8_type = try Tag.type.create(c.arena, "u8"); const self_param_name = "self"; @@ -979,7 +980,7 @@ fn buildFlexibleArrayFn( .is_noalias = false, }; - const array_type = @as(*const clang.ArrayType, @ptrCast(field_qt.getTypePtr())); + const array_type = @as(*const clang.ArrayType, @ptrCast(field_qt_canon)); const element_qt = array_type.getElementType(); const element_type = try transQualType(c, scope, element_qt, field_decl.getLocation()); @@ -1049,21 +1050,33 @@ fn buildFlexibleArrayFn( return Node.initPayload(&payload.base); } +/// Return true if `field_decl` is the flexible array field for its parent record fn isFlexibleArrayFieldDecl(c: *Context, field_decl: *const clang.FieldDecl) bool { - return qualTypeCanon(field_decl.getType()).isIncompleteOrZeroLengthArrayType(c.clang_context); + const record_decl = field_decl.getParent() orelse return false; + const record_flexible_field = flexibleArrayField(c, record_decl) orelse return false; + return field_decl == record_flexible_field; } +/// Find the flexible array field for a record if any. A flexible array field is an +/// incomplete or zero-length array that occurs as the last field of a record. /// clang's RecordDecl::hasFlexibleArrayMember is not suitable for determining /// this because it returns false for a record that ends with a zero-length /// array, but we consider those to be flexible arrays -fn hasFlexibleArrayField(c: *Context, record_def: *const clang.RecordDecl) bool { +fn flexibleArrayField(c: *Context, record_def: *const clang.RecordDecl) ?*const clang.FieldDecl { var it = record_def.field_begin(); const end_it = record_def.field_end(); + var flexible_field: ?*const clang.FieldDecl = null; while (it.neq(end_it)) : (it = it.next()) { const field_decl = it.deref(); - if (isFlexibleArrayFieldDecl(c, field_decl)) return true; + const ty = qualTypeCanon(field_decl.getType()); + const incomplete_or_zero_size = ty.isIncompleteOrZeroLengthArrayType(c.clang_context); + if (incomplete_or_zero_size) { + flexible_field = field_decl; + } else { + flexible_field = null; + } } - return false; + return flexible_field; } fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordDecl) Error!void { @@ -1117,7 +1130,7 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD var functions = std.ArrayList(Node).init(c.gpa); defer functions.deinit(); - const has_flexible_array = hasFlexibleArrayField(c, record_def); + const flexible_field = flexibleArrayField(c, record_def); var unnamed_field_count: u32 = 0; var it = record_def.field_begin(); const end_it = record_def.field_end(); @@ -1143,7 +1156,7 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD unnamed_field_count += 1; is_anon = true; } - if (isFlexibleArrayFieldDecl(c, field_decl)) { + if (flexible_field == field_decl) { const flexible_array_fn = buildFlexibleArrayFn(c, scope, layout, field_name, field_decl) catch |err| switch (err) { error.UnsupportedType => { try c.opaque_demotes.put(c.gpa, @intFromPtr(record_decl.getCanonicalDecl()), {}); @@ -1164,7 +1177,7 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD else => |e| return e, }; - const alignment = if (has_flexible_array and field_decl.getFieldIndex() == 0) + const alignment = if (flexible_field != null and field_decl.getFieldIndex() == 0) @as(c_uint, @intCast(record_alignment)) else ClangAlignment.forField(c, field_decl, record_def).zigAlignment(); diff --git a/test/cases/compile_errors/wrong_types_given_to_export.zig b/test/cases/compile_errors/wrong_types_given_to_export.zig index 8278deac15..6e688d33d6 100644 --- a/test/cases/compile_errors/wrong_types_given_to_export.zig +++ b/test/cases/compile_errors/wrong_types_given_to_export.zig @@ -7,5 +7,5 @@ comptime { // backend=stage2 // target=native // -// :3:21: error: expected type 'builtin.GlobalLinkage', found 'u32' +// :3:51: error: expected type 'builtin.GlobalLinkage', found 'u32' // :?:?: note: enum declared here diff --git a/test/link.zig b/test/link.zig index 56b1cf415d..53caefd64f 100644 --- a/test/link.zig +++ b/test/link.zig @@ -157,6 +157,10 @@ pub const cases = [_]Case{ .import = @import("link/macho/pagezero/build.zig"), }, .{ + .build_root = "test/link/macho/reexports", + .import = @import("link/macho/reexports/build.zig"), + }, + .{ .build_root = "test/link/macho/search_strategy", .import = @import("link/macho/search_strategy/build.zig"), }, diff --git a/test/link/macho/reexports/a.zig b/test/link/macho/reexports/a.zig new file mode 100644 index 0000000000..cfa7e8b3ac --- /dev/null +++ b/test/link/macho/reexports/a.zig @@ -0,0 +1,7 @@ +const x: i32 = 42; +export fn foo() i32 { + return x; +} +comptime { + @export(foo, .{ .name = "bar", .linkage = .Strong }); +} diff --git a/test/link/macho/reexports/build.zig b/test/link/macho/reexports/build.zig new file mode 100644 index 0000000000..d9a9481fa0 --- /dev/null +++ b/test/link/macho/reexports/build.zig @@ -0,0 +1,38 @@ +const std = @import("std"); + +pub const requires_symlinks = true; + +pub fn build(b: *std.Build) void { + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + add(b, test_step, .Debug); + add(b, test_step, .ReleaseFast); + add(b, test_step, .ReleaseSmall); + add(b, test_step, .ReleaseSafe); +} + +fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { + const target: std.zig.CrossTarget = .{ .os_tag = .macos }; + + const lib = b.addStaticLibrary(.{ + .name = "a", + .root_source_file = .{ .path = "a.zig" }, + .optimize = optimize, + .target = target, + }); + + const exe = b.addExecutable(.{ + .name = "test", + .optimize = optimize, + .target = target, + }); + exe.addCSourceFile(.{ .file = .{ .path = "main.c" }, .flags = &.{} }); + exe.linkLibrary(lib); + exe.linkLibC(); + + const run = b.addRunArtifact(exe); + run.skip_foreign_checks = true; + run.expectExitCode(0); + test_step.dependOn(&run.step); +} diff --git a/test/link/macho/reexports/main.c b/test/link/macho/reexports/main.c new file mode 100644 index 0000000000..2beb701f1f --- /dev/null +++ b/test/link/macho/reexports/main.c @@ -0,0 +1,5 @@ +extern int foo(); +extern int bar(); +int main() { + return bar() - foo(); +} diff --git a/test/run_translated_c.zig b/test/run_translated_c.zig index fe32d376d2..0e3000a113 100644 --- a/test/run_translated_c.zig +++ b/test/run_translated_c.zig @@ -1554,6 +1554,24 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { \\} , ""); + cases.add("Flexible array with typedefed flexible item, issue #16838", + \\#include <stdlib.h> + \\#include <assert.h> + \\typedef int MARKER[0]; + \\typedef struct { int x; MARKER y; } Flexible; + \\#define SIZE 10 + \\int main(void) { + \\ Flexible *flex = malloc(sizeof(Flexible) + SIZE * sizeof(int)); + \\ for (int i = 0; i < SIZE; i++) { + \\ flex->y[i] = i; + \\ } + \\ for (int i = 0; i < SIZE; i++) { + \\ assert(flex->y[i] == i); + \\ } + \\ return 0; + \\} + , ""); + cases.add("enum with value that fits in c_uint but not c_int, issue #8003", \\#include <stdlib.h> \\enum my_enum { |
