aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/langref.html.in16
-rw-r--r--lib/std/Build.zig7
-rw-r--r--lib/std/coff.zig35
-rw-r--r--lib/std/debug.zig87
-rw-r--r--lib/std/json/dynamic.zig14
-rw-r--r--lib/std/json/dynamic_test.zig40
-rw-r--r--src/AstGen.zig30
-rw-r--r--src/Autodoc.zig3
-rw-r--r--src/Compilation.zig7
-rw-r--r--src/InternPool.zig30
-rw-r--r--src/Module.zig55
-rw-r--r--src/Sema.zig377
-rw-r--r--src/codegen/llvm.zig11
-rw-r--r--src/link/MachO/Object.zig65
-rw-r--r--src/translate_c.zig29
-rw-r--r--test/cases/compile_errors/wrong_types_given_to_export.zig2
-rw-r--r--test/link.zig4
-rw-r--r--test/link/macho/reexports/a.zig7
-rw-r--r--test/link/macho/reexports/build.zig38
-rw-r--r--test/link/macho/reexports/main.c5
-rw-r--r--test/run_translated_c.zig18
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 {