Commit 3afda4322c
Changed files (22)
src
test
behavior
cases
compile_errors
src/arch/wasm/CodeGen.zig
@@ -3218,15 +3218,7 @@ fn lowerNavRef(func: *CodeGen, nav_index: InternPool.Nav.Index, offset: u32) Inn
const zcu = pt.zcu;
const ip = &zcu.intern_pool;
- // check if decl is an alias to a function, in which case we
- // want to lower the actual decl, rather than the alias itself.
- const owner_nav = switch (ip.indexToKey(zcu.navValue(nav_index).toIntern())) {
- .func => |function| function.owner_nav,
- .variable => |variable| variable.owner_nav,
- .@"extern" => |@"extern"| @"extern".owner_nav,
- else => nav_index,
- };
- const nav_ty = ip.getNav(owner_nav).typeOf(ip);
+ const nav_ty = ip.getNav(nav_index).typeOf(ip);
if (!ip.isFunctionType(nav_ty) and !Type.fromInterned(nav_ty).hasRuntimeBitsIgnoreComptime(zcu)) {
return .{ .imm32 = 0xaaaaaaaa };
}
src/codegen/c.zig
@@ -770,11 +770,14 @@ pub const DeclGen = struct {
const ctype_pool = &dg.ctype_pool;
// Chase function values in order to be able to reference the original function.
- const owner_nav = switch (ip.indexToKey(zcu.navValue(nav_index).toIntern())) {
- .variable => |variable| variable.owner_nav,
- .func => |func| func.owner_nav,
- .@"extern" => |@"extern"| @"extern".owner_nav,
- else => nav_index,
+ const owner_nav = switch (ip.getNav(nav_index).status) {
+ .unresolved => unreachable,
+ .type_resolved => nav_index, // this can't be an extern or a function
+ .fully_resolved => |r| switch (ip.indexToKey(r.val)) {
+ .func => |f| f.owner_nav,
+ .@"extern" => |e| e.owner_nav,
+ else => nav_index,
+ },
};
// Render an undefined pointer if we have a pointer to a zero-bit or comptime type.
@@ -2237,7 +2240,7 @@ pub const DeclGen = struct {
Type.fromInterned(nav.typeOf(ip)),
.{ .nav = nav_index },
CQualifiers.init(.{ .@"const" = flags.is_const }),
- nav.status.resolved.alignment,
+ nav.getAlignment(),
.complete,
);
try fwd.writeAll(";\n");
@@ -2246,19 +2249,19 @@ pub const DeclGen = struct {
fn renderNavName(dg: *DeclGen, writer: anytype, nav_index: InternPool.Nav.Index) !void {
const zcu = dg.pt.zcu;
const ip = &zcu.intern_pool;
- switch (ip.indexToKey(zcu.navValue(nav_index).toIntern())) {
- .@"extern" => |@"extern"| try writer.print("{ }", .{
+ const nav = ip.getNav(nav_index);
+ if (nav.getExtern(ip)) |@"extern"| {
+ try writer.print("{ }", .{
fmtIdent(ip.getNav(@"extern".owner_nav).name.toSlice(ip)),
- }),
- else => {
- // MSVC has a limit of 4095 character token length limit, and fmtIdent can (worst case),
- // expand to 3x the length of its input, but let's cut it off at a much shorter limit.
- const fqn_slice = ip.getNav(nav_index).fqn.toSlice(ip);
- try writer.print("{}__{d}", .{
- fmtIdent(fqn_slice[0..@min(fqn_slice.len, 100)]),
- @intFromEnum(nav_index),
- });
- },
+ });
+ } else {
+ // MSVC has a limit of 4095 character token length limit, and fmtIdent can (worst case),
+ // expand to 3x the length of its input, but let's cut it off at a much shorter limit.
+ const fqn_slice = ip.getNav(nav_index).fqn.toSlice(ip);
+ try writer.print("{}__{d}", .{
+ fmtIdent(fqn_slice[0..@min(fqn_slice.len, 100)]),
+ @intFromEnum(nav_index),
+ });
}
}
@@ -2826,7 +2829,7 @@ pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFn
const fwd = o.dg.fwdDeclWriter();
try fwd.print("static zig_{s} ", .{@tagName(key)});
- try o.dg.renderFunctionSignature(fwd, fn_val, ip.getNav(fn_nav_index).status.resolved.alignment, .forward, .{
+ try o.dg.renderFunctionSignature(fwd, fn_val, ip.getNav(fn_nav_index).getAlignment(), .forward, .{
.fmt_ctype_pool_string = fn_name,
});
try fwd.writeAll(";\n");
@@ -2867,13 +2870,13 @@ pub fn genFunc(f: *Function) !void {
try o.dg.renderFunctionSignature(
fwd,
nav_val,
- nav.status.resolved.alignment,
+ nav.status.fully_resolved.alignment,
.forward,
.{ .nav = nav_index },
);
try fwd.writeAll(";\n");
- if (nav.status.resolved.@"linksection".toSlice(ip)) |s|
+ if (nav.status.fully_resolved.@"linksection".toSlice(ip)) |s|
try o.writer().print("zig_linksection_fn({s}) ", .{fmtStringLiteral(s, null)});
try o.dg.renderFunctionSignature(
o.writer(),
@@ -2952,7 +2955,7 @@ pub fn genDecl(o: *Object) !void {
const nav_ty = Type.fromInterned(nav.typeOf(ip));
if (!nav_ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) return;
- switch (ip.indexToKey(nav.status.resolved.val)) {
+ switch (ip.indexToKey(nav.status.fully_resolved.val)) {
.@"extern" => |@"extern"| {
if (!ip.isFunctionType(nav_ty.toIntern())) return o.dg.renderFwdDecl(o.dg.pass.nav, .{
.is_extern = true,
@@ -2965,8 +2968,8 @@ pub fn genDecl(o: *Object) !void {
try fwd.writeAll("zig_extern ");
try o.dg.renderFunctionSignature(
fwd,
- Value.fromInterned(nav.status.resolved.val),
- nav.status.resolved.alignment,
+ Value.fromInterned(nav.status.fully_resolved.val),
+ nav.status.fully_resolved.alignment,
.forward,
.{ .@"export" = .{
.main_name = nav.name,
@@ -2985,14 +2988,14 @@ pub fn genDecl(o: *Object) !void {
const w = o.writer();
if (variable.is_weak_linkage) try w.writeAll("zig_weak_linkage ");
if (variable.is_threadlocal and !o.dg.mod.single_threaded) try w.writeAll("zig_threadlocal ");
- if (nav.status.resolved.@"linksection".toSlice(&zcu.intern_pool)) |s|
+ if (nav.status.fully_resolved.@"linksection".toSlice(&zcu.intern_pool)) |s|
try w.print("zig_linksection({s}) ", .{fmtStringLiteral(s, null)});
try o.dg.renderTypeAndName(
w,
nav_ty,
.{ .nav = o.dg.pass.nav },
.{},
- nav.status.resolved.alignment,
+ nav.status.fully_resolved.alignment,
.complete,
);
try w.writeAll(" = ");
@@ -3002,10 +3005,10 @@ pub fn genDecl(o: *Object) !void {
},
else => try genDeclValue(
o,
- Value.fromInterned(nav.status.resolved.val),
+ Value.fromInterned(nav.status.fully_resolved.val),
.{ .nav = o.dg.pass.nav },
- nav.status.resolved.alignment,
- nav.status.resolved.@"linksection",
+ nav.status.fully_resolved.alignment,
+ nav.status.fully_resolved.@"linksection",
),
}
}
src/codegen/llvm.zig
@@ -1476,7 +1476,7 @@ pub const Object = struct {
} }, &o.builder);
}
- if (nav.status.resolved.@"linksection".toSlice(ip)) |section|
+ if (nav.status.fully_resolved.@"linksection".toSlice(ip)) |section|
function_index.setSection(try o.builder.string(section), &o.builder);
var deinit_wip = true;
@@ -1684,7 +1684,7 @@ pub const Object = struct {
const file = try o.getDebugFile(file_scope);
const line_number = zcu.navSrcLine(func.owner_nav) + 1;
- const is_internal_linkage = ip.indexToKey(nav.status.resolved.val) != .@"extern";
+ const is_internal_linkage = ip.indexToKey(nav.status.fully_resolved.val) != .@"extern";
const debug_decl_type = try o.lowerDebugType(fn_ty);
const subprogram = try o.builder.debugSubprogram(
@@ -2928,9 +2928,7 @@ pub const Object = struct {
const gpa = o.gpa;
const nav = ip.getNav(nav_index);
const owner_mod = zcu.navFileScope(nav_index).mod;
- const resolved = nav.status.resolved;
- const val = Value.fromInterned(resolved.val);
- const ty = val.typeOf(zcu);
+ const ty: Type = .fromInterned(nav.typeOf(ip));
const gop = try o.nav_map.getOrPut(gpa, nav_index);
if (gop.found_existing) return gop.value_ptr.ptr(&o.builder).kind.function;
@@ -2938,14 +2936,14 @@ pub const Object = struct {
const target = owner_mod.resolved_target.result;
const sret = firstParamSRet(fn_info, zcu, target);
- const is_extern, const lib_name = switch (ip.indexToKey(val.toIntern())) {
- .@"extern" => |@"extern"| .{ true, @"extern".lib_name },
- else => .{ false, .none },
- };
+ const is_extern, const lib_name = if (nav.getExtern(ip)) |@"extern"|
+ .{ true, @"extern".lib_name }
+ else
+ .{ false, .none };
const function_index = try o.builder.addFunction(
try o.lowerType(ty),
try o.builder.strtabString((if (is_extern) nav.name else nav.fqn).toSlice(ip)),
- toLlvmAddressSpace(resolved.@"addrspace", target),
+ toLlvmAddressSpace(nav.getAddrspace(), target),
);
gop.value_ptr.* = function_index.ptrConst(&o.builder).global;
@@ -3063,8 +3061,8 @@ pub const Object = struct {
}
}
- if (resolved.alignment != .none)
- function_index.setAlignment(resolved.alignment.toLlvm(), &o.builder);
+ if (nav.getAlignment() != .none)
+ function_index.setAlignment(nav.getAlignment().toLlvm(), &o.builder);
// Function attributes that are independent of analysis results of the function body.
try o.addCommonFnAttributes(
@@ -3249,17 +3247,21 @@ pub const Object = struct {
const zcu = pt.zcu;
const ip = &zcu.intern_pool;
const nav = ip.getNav(nav_index);
- const resolved = nav.status.resolved;
- const is_extern, const is_threadlocal, const is_weak_linkage, const is_dll_import = switch (ip.indexToKey(resolved.val)) {
- .variable => |variable| .{ false, variable.is_threadlocal, variable.is_weak_linkage, false },
- .@"extern" => |@"extern"| .{ true, @"extern".is_threadlocal, @"extern".is_weak_linkage, @"extern".is_dll_import },
- else => .{ false, false, false, false },
+ const is_extern, const is_threadlocal, const is_weak_linkage, const is_dll_import = switch (nav.status) {
+ .unresolved => unreachable,
+ .fully_resolved => |r| switch (ip.indexToKey(r.val)) {
+ .variable => |variable| .{ false, variable.is_threadlocal, variable.is_weak_linkage, false },
+ .@"extern" => |@"extern"| .{ true, @"extern".is_threadlocal, @"extern".is_weak_linkage, @"extern".is_dll_import },
+ else => .{ false, false, false, false },
+ },
+ // This means it's a source declaration which is not `extern`!
+ .type_resolved => |r| .{ false, r.is_threadlocal, false, false },
};
const variable_index = try o.builder.addVariable(
try o.builder.strtabString((if (is_extern) nav.name else nav.fqn).toSlice(ip)),
try o.lowerType(Type.fromInterned(nav.typeOf(ip))),
- toLlvmGlobalAddressSpace(resolved.@"addrspace", zcu.getTarget()),
+ toLlvmGlobalAddressSpace(nav.getAddrspace(), zcu.getTarget()),
);
gop.value_ptr.* = variable_index.ptrConst(&o.builder).global;
@@ -4528,20 +4530,10 @@ pub const Object = struct {
const zcu = pt.zcu;
const ip = &zcu.intern_pool;
- // In the case of something like:
- // fn foo() void {}
- // const bar = foo;
- // ... &bar;
- // `bar` is just an alias and we actually want to lower a reference to `foo`.
- const owner_nav_index = switch (ip.indexToKey(zcu.navValue(nav_index).toIntern())) {
- .func => |func| func.owner_nav,
- .@"extern" => |@"extern"| @"extern".owner_nav,
- else => nav_index,
- };
- const owner_nav = ip.getNav(owner_nav_index);
+ const nav = ip.getNav(nav_index);
- const nav_ty = Type.fromInterned(owner_nav.typeOf(ip));
- const ptr_ty = try pt.navPtrType(owner_nav_index);
+ const nav_ty = Type.fromInterned(nav.typeOf(ip));
+ const ptr_ty = try pt.navPtrType(nav_index);
const is_fn_body = nav_ty.zigTypeTag(zcu) == .@"fn";
if ((!is_fn_body and !nav_ty.hasRuntimeBits(zcu)) or
@@ -4551,13 +4543,13 @@ pub const Object = struct {
}
const llvm_global = if (is_fn_body)
- (try o.resolveLlvmFunction(owner_nav_index)).ptrConst(&o.builder).global
+ (try o.resolveLlvmFunction(nav_index)).ptrConst(&o.builder).global
else
- (try o.resolveGlobalNav(owner_nav_index)).ptrConst(&o.builder).global;
+ (try o.resolveGlobalNav(nav_index)).ptrConst(&o.builder).global;
const llvm_val = try o.builder.convConst(
llvm_global.toConst(),
- try o.builder.ptrType(toLlvmAddressSpace(owner_nav.status.resolved.@"addrspace", zcu.getTarget())),
+ try o.builder.ptrType(toLlvmAddressSpace(nav.getAddrspace(), zcu.getTarget())),
);
return o.builder.convConst(llvm_val, try o.lowerType(ptr_ty));
@@ -4799,7 +4791,7 @@ pub const NavGen = struct {
const ip = &zcu.intern_pool;
const nav_index = ng.nav_index;
const nav = ip.getNav(nav_index);
- const resolved = nav.status.resolved;
+ const resolved = nav.status.fully_resolved;
const is_extern, const lib_name, const is_threadlocal, const is_weak_linkage, const is_dll_import, const is_const, const init_val, const owner_nav = switch (ip.indexToKey(resolved.val)) {
.variable => |variable| .{ false, .none, variable.is_threadlocal, variable.is_weak_linkage, false, false, variable.init, variable.owner_nav },
@@ -5765,7 +5757,7 @@ pub const FuncGen = struct {
const msg_nav_index = zcu.panic_messages[@intFromEnum(panic_id)].unwrap().?;
const msg_nav = ip.getNav(msg_nav_index);
const msg_len = Type.fromInterned(msg_nav.typeOf(ip)).childType(zcu).arrayLen(zcu);
- const msg_ptr = try o.lowerValue(msg_nav.status.resolved.val);
+ const msg_ptr = try o.lowerValue(msg_nav.status.fully_resolved.val);
const null_opt_addr_global = try fg.resolveNullOptUsize();
const target = zcu.getTarget();
const llvm_usize = try o.lowerType(Type.usize);
src/codegen/spirv.zig
@@ -268,7 +268,7 @@ pub const Object = struct {
// TODO: Extern fn?
const kind: SpvModule.Decl.Kind = if (ip.isFunctionType(nav.typeOf(ip)))
.func
- else switch (nav.status.resolved.@"addrspace") {
+ else switch (nav.getAddrspace()) {
.generic => .invocation_global,
else => .global,
};
@@ -1279,17 +1279,20 @@ const NavGen = struct {
const ip = &zcu.intern_pool;
const ty_id = try self.resolveType(ty, .direct);
const nav = ip.getNav(nav_index);
- const nav_val = zcu.navValue(nav_index);
- const nav_ty = nav_val.typeOf(zcu);
-
- switch (ip.indexToKey(nav_val.toIntern())) {
- .func => {
- // TODO: Properly lower function pointers. For now we are going to hack around it and
- // just generate an empty pointer. Function pointers are represented by a pointer to usize.
- return try self.spv.constUndef(ty_id);
+ const nav_ty: Type = .fromInterned(nav.typeOf(ip));
+
+ switch (nav.status) {
+ .unresolved => unreachable,
+ .type_resolved => {}, // this is not a function or extern
+ .fully_resolved => |r| switch (ip.indexToKey(r.val)) {
+ .func => {
+ // TODO: Properly lower function pointers. For now we are going to hack around it and
+ // just generate an empty pointer. Function pointers are represented by a pointer to usize.
+ return try self.spv.constUndef(ty_id);
+ },
+ .@"extern" => if (ip.isFunctionType(nav_ty.toIntern())) @panic("TODO"),
+ else => {},
},
- .@"extern" => assert(!ip.isFunctionType(nav_ty.toIntern())), // TODO
- else => {},
}
if (!nav_ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) {
@@ -1305,7 +1308,7 @@ const NavGen = struct {
.global, .invocation_global => spv_decl.result_id,
};
- const storage_class = self.spvStorageClass(nav.status.resolved.@"addrspace");
+ const storage_class = self.spvStorageClass(nav.getAddrspace());
try self.addFunctionDep(spv_decl_index, storage_class);
const decl_ptr_ty_id = try self.ptrType(nav_ty, storage_class);
@@ -3182,7 +3185,7 @@ const NavGen = struct {
};
assert(maybe_init_val == null); // TODO
- const storage_class = self.spvStorageClass(nav.status.resolved.@"addrspace");
+ const storage_class = self.spvStorageClass(nav.getAddrspace());
assert(storage_class != .Generic); // These should be instance globals
const ptr_ty_id = try self.ptrType(ty, storage_class);
src/link/Elf/ZigObject.zig
@@ -925,14 +925,11 @@ pub fn getNavVAddr(
const ip = &zcu.intern_pool;
const nav = ip.getNav(nav_index);
log.debug("getNavVAddr {}({d})", .{ nav.fqn.fmt(ip), nav_index });
- const this_sym_index = switch (ip.indexToKey(nav.status.resolved.val)) {
- .@"extern" => |@"extern"| try self.getGlobalSymbol(
- elf_file,
- nav.name.toSlice(ip),
- @"extern".lib_name.toSlice(ip),
- ),
- else => try self.getOrCreateMetadataForNav(zcu, nav_index),
- };
+ const this_sym_index = if (nav.getExtern(ip)) |@"extern"| try self.getGlobalSymbol(
+ elf_file,
+ nav.name.toSlice(ip),
+ @"extern".lib_name.toSlice(ip),
+ ) else try self.getOrCreateMetadataForNav(zcu, nav_index);
const this_sym = self.symbol(this_sym_index);
const vaddr = this_sym.address(.{}, elf_file);
switch (reloc_info.parent) {
@@ -1107,15 +1104,13 @@ pub fn freeNav(self: *ZigObject, elf_file: *Elf, nav_index: InternPool.Nav.Index
pub fn getOrCreateMetadataForNav(self: *ZigObject, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Symbol.Index {
const gpa = zcu.gpa;
+ const ip = &zcu.intern_pool;
const gop = try self.navs.getOrPut(gpa, nav_index);
if (!gop.found_existing) {
const symbol_index = try self.newSymbolWithAtom(gpa, 0);
- const nav_val = Value.fromInterned(zcu.intern_pool.getNav(nav_index).status.resolved.val);
const sym = self.symbol(symbol_index);
- if (nav_val.getVariable(zcu)) |variable| {
- if (variable.is_threadlocal and zcu.comp.config.any_non_single_threaded) {
- sym.flags.is_tls = true;
- }
+ if (ip.getNav(nav_index).isThreadlocal(ip) and zcu.comp.config.any_non_single_threaded) {
+ sym.flags.is_tls = true;
}
gop.value_ptr.* = .{ .symbol_index = symbol_index };
}
@@ -1547,7 +1542,7 @@ pub fn updateNav(
log.debug("updateNav {}({d})", .{ nav.fqn.fmt(ip), nav_index });
- const nav_init = switch (ip.indexToKey(nav.status.resolved.val)) {
+ const nav_init = switch (ip.indexToKey(nav.status.fully_resolved.val)) {
.func => .none,
.variable => |variable| variable.init,
.@"extern" => |@"extern"| {
@@ -1560,7 +1555,7 @@ pub fn updateNav(
self.symbol(sym_index).flags.is_extern_ptr = true;
return;
},
- else => nav.status.resolved.val,
+ else => nav.status.fully_resolved.val,
};
if (nav_init != .none and Value.fromInterned(nav_init).typeOf(zcu).hasRuntimeBits(zcu)) {
src/link/MachO/ZigObject.zig
@@ -608,14 +608,11 @@ pub fn getNavVAddr(
const ip = &zcu.intern_pool;
const nav = ip.getNav(nav_index);
log.debug("getNavVAddr {}({d})", .{ nav.fqn.fmt(ip), nav_index });
- const sym_index = switch (ip.indexToKey(nav.status.resolved.val)) {
- .@"extern" => |@"extern"| try self.getGlobalSymbol(
- macho_file,
- nav.name.toSlice(ip),
- @"extern".lib_name.toSlice(ip),
- ),
- else => try self.getOrCreateMetadataForNav(macho_file, nav_index),
- };
+ const sym_index = if (nav.getExtern(ip)) |@"extern"| try self.getGlobalSymbol(
+ macho_file,
+ nav.name.toSlice(ip),
+ @"extern".lib_name.toSlice(ip),
+ ) else try self.getOrCreateMetadataForNav(macho_file, nav_index);
const sym = self.symbols.items[sym_index];
const vaddr = sym.getAddress(.{}, macho_file);
switch (reloc_info.parent) {
@@ -882,7 +879,7 @@ pub fn updateNav(
const ip = &zcu.intern_pool;
const nav = ip.getNav(nav_index);
- const nav_init = switch (ip.indexToKey(nav.status.resolved.val)) {
+ const nav_init = switch (ip.indexToKey(nav.status.fully_resolved.val)) {
.func => .none,
.variable => |variable| variable.init,
.@"extern" => |@"extern"| {
@@ -895,7 +892,7 @@ pub fn updateNav(
sym.flags.is_extern_ptr = true;
return;
},
- else => nav.status.resolved.val,
+ else => nav.status.fully_resolved.val,
};
if (nav_init != .none and Value.fromInterned(nav_init).typeOf(zcu).hasRuntimeBits(zcu)) {
@@ -1561,11 +1558,7 @@ fn isThreadlocal(macho_file: *MachO, nav_index: InternPool.Nav.Index) bool {
if (!macho_file.base.comp.config.any_non_single_threaded)
return false;
const ip = &macho_file.base.comp.zcu.?.intern_pool;
- return switch (ip.indexToKey(ip.getNav(nav_index).status.resolved.val)) {
- .variable => |variable| variable.is_threadlocal,
- .@"extern" => |@"extern"| @"extern".is_threadlocal,
- else => false,
- };
+ return ip.getNav(nav_index).isThreadlocal(ip);
}
fn addAtom(self: *ZigObject, allocator: Allocator) !Atom.Index {
src/link/Wasm/ZigObject.zig
@@ -734,15 +734,14 @@ pub fn getNavVAddr(
const target_atom_index = try zig_object.getOrCreateAtomForNav(wasm, pt, nav_index);
const target_atom = wasm.getAtom(target_atom_index);
const target_symbol_index = @intFromEnum(target_atom.sym_index);
- switch (ip.indexToKey(nav.status.resolved.val)) {
- .@"extern" => |@"extern"| try zig_object.addOrUpdateImport(
+ if (nav.getExtern(ip)) |@"extern"| {
+ try zig_object.addOrUpdateImport(
wasm,
nav.name.toSlice(ip),
target_atom.sym_index,
@"extern".lib_name.toSlice(ip),
null,
- ),
- else => {},
+ );
}
std.debug.assert(reloc_info.parent.atom_index != 0);
@@ -945,8 +944,8 @@ pub fn freeNav(zig_object: *ZigObject, wasm: *Wasm, nav_index: InternPool.Nav.In
segment.name = &.{}; // Ensure no accidental double free
}
- const nav_val = zcu.navValue(nav_index).toIntern();
- if (ip.indexToKey(nav_val) == .@"extern") {
+ const nav = ip.getNav(nav_index);
+ if (nav.getExtern(ip) != null) {
std.debug.assert(zig_object.imports.remove(atom.sym_index));
}
std.debug.assert(wasm.symbol_atom.remove(atom.symbolLoc()));
@@ -960,7 +959,7 @@ pub fn freeNav(zig_object: *ZigObject, wasm: *Wasm, nav_index: InternPool.Nav.In
if (sym.isGlobal()) {
std.debug.assert(zig_object.global_syms.remove(atom.sym_index));
}
- if (ip.isFunctionType(ip.typeOf(nav_val))) {
+ if (ip.isFunctionType(nav.typeOf(ip))) {
zig_object.functions_free_list.append(gpa, sym.index) catch {};
std.debug.assert(zig_object.atom_types.remove(atom_index));
} else {
src/link/C.zig
@@ -217,7 +217,7 @@ pub fn updateFunc(
.mod = zcu.navFileScope(func.owner_nav).mod,
.error_msg = null,
.pass = .{ .nav = func.owner_nav },
- .is_naked_fn = zcu.navValue(func.owner_nav).typeOf(zcu).fnCallingConvention(zcu) == .naked,
+ .is_naked_fn = Type.fromInterned(func.ty).fnCallingConvention(zcu) == .naked,
.fwd_decl = fwd_decl.toManaged(gpa),
.ctype_pool = ctype_pool.*,
.scratch = .{},
@@ -320,11 +320,11 @@ pub fn updateNav(self: *C, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !
const ip = &zcu.intern_pool;
const nav = ip.getNav(nav_index);
- const nav_init = switch (ip.indexToKey(nav.status.resolved.val)) {
+ const nav_init = switch (ip.indexToKey(nav.status.fully_resolved.val)) {
.func => return,
.@"extern" => .none,
.variable => |variable| variable.init,
- else => nav.status.resolved.val,
+ else => nav.status.fully_resolved.val,
};
if (nav_init != .none and !Value.fromInterned(nav_init).typeOf(zcu).hasRuntimeBits(zcu)) return;
@@ -499,7 +499,7 @@ pub fn flushModule(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
av_block,
self.exported_navs.getPtr(nav),
export_names,
- if (ip.indexToKey(zcu.navValue(nav).toIntern()) == .@"extern")
+ if (ip.getNav(nav).getExtern(ip) != null)
ip.getNav(nav).name.toOptional()
else
.none,
@@ -544,13 +544,11 @@ pub fn flushModule(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
},
self.getString(av_block.code),
);
- for (self.navs.keys(), self.navs.values()) |nav, av_block| f.appendCodeAssumeCapacity(
- if (self.exported_navs.contains(nav)) .default else switch (ip.indexToKey(zcu.navValue(nav).toIntern())) {
- .@"extern" => .zig_extern,
- else => .static,
- },
- self.getString(av_block.code),
- );
+ for (self.navs.keys(), self.navs.values()) |nav, av_block| f.appendCodeAssumeCapacity(storage: {
+ if (self.exported_navs.contains(nav)) break :storage .default;
+ if (ip.getNav(nav).getExtern(ip) != null) break :storage .zig_extern;
+ break :storage .static;
+ }, self.getString(av_block.code));
const file = self.base.file.?;
try file.setEndPos(f.file_size);
src/link/Coff.zig
@@ -1110,6 +1110,8 @@ pub fn updateFunc(coff: *Coff, pt: Zcu.PerThread, func_index: InternPool.Index,
const atom_index = try coff.getOrCreateAtomForNav(func.owner_nav);
coff.freeRelocations(atom_index);
+ coff.navs.getPtr(func.owner_nav).?.section = coff.text_section_index.?;
+
var code_buffer = std.ArrayList(u8).init(gpa);
defer code_buffer.deinit();
@@ -1223,6 +1225,8 @@ pub fn updateNav(
coff.freeRelocations(atom_index);
const atom = coff.getAtom(atom_index);
+ coff.navs.getPtr(nav_index).?.section = coff.getNavOutputSection(nav_index);
+
var code_buffer = std.ArrayList(u8).init(gpa);
defer code_buffer.deinit();
@@ -1342,7 +1346,8 @@ pub fn getOrCreateAtomForNav(coff: *Coff, nav_index: InternPool.Nav.Index) !Atom
if (!gop.found_existing) {
gop.value_ptr.* = .{
.atom = try coff.createAtom(),
- .section = coff.getNavOutputSection(nav_index),
+ // If necessary, this will be modified by `updateNav` or `updateFunc`.
+ .section = coff.rdata_section_index.?,
.exports = .{},
};
}
@@ -1355,7 +1360,7 @@ fn getNavOutputSection(coff: *Coff, nav_index: InternPool.Nav.Index) u16 {
const nav = ip.getNav(nav_index);
const ty = Type.fromInterned(nav.typeOf(ip));
const zig_ty = ty.zigTypeTag(zcu);
- const val = Value.fromInterned(nav.status.resolved.val);
+ const val = Value.fromInterned(nav.status.fully_resolved.val);
const index: u16 = blk: {
if (val.isUndefDeep(zcu)) {
// TODO in release-fast and release-small, we should put undef in .bss
@@ -2348,10 +2353,10 @@ pub fn getNavVAddr(
const ip = &zcu.intern_pool;
const nav = ip.getNav(nav_index);
log.debug("getNavVAddr {}({d})", .{ nav.fqn.fmt(ip), nav_index });
- const sym_index = switch (ip.indexToKey(nav.status.resolved.val)) {
- .@"extern" => |@"extern"| try coff.getGlobalSymbol(nav.name.toSlice(ip), @"extern".lib_name.toSlice(ip)),
- else => coff.getAtom(try coff.getOrCreateAtomForNav(nav_index)).getSymbolIndex().?,
- };
+ const sym_index = if (nav.getExtern(ip)) |e|
+ try coff.getGlobalSymbol(nav.name.toSlice(ip), e.lib_name.toSlice(ip))
+ else
+ coff.getAtom(try coff.getOrCreateAtomForNav(nav_index)).getSymbolIndex().?;
const atom_index = coff.getAtomIndexForSymbol(.{
.sym_index = reloc_info.parent.atom_index,
.file = null,
src/link/Dwarf.zig
@@ -2281,7 +2281,7 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In
const nav_ty = nav_val.typeOf(zcu);
const nav_ty_reloc_index = try wip_nav.refForward();
try wip_nav.infoExprloc(.{ .addr = .{ .sym = sym_index } });
- try uleb128(diw, nav.status.resolved.alignment.toByteUnits() orelse
+ try uleb128(diw, nav.status.fully_resolved.alignment.toByteUnits() orelse
nav_ty.abiAlignment(zcu).toByteUnits().?);
try diw.writeByte(@intFromBool(false));
wip_nav.finishForward(nav_ty_reloc_index);
@@ -2313,7 +2313,7 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In
try wip_nav.refType(ty);
const addr: Loc = .{ .addr = .{ .sym = sym_index } };
try wip_nav.infoExprloc(if (variable.is_threadlocal) .{ .form_tls_address = &addr } else addr);
- try uleb128(diw, nav.status.resolved.alignment.toByteUnits() orelse
+ try uleb128(diw, nav.status.fully_resolved.alignment.toByteUnits() orelse
ty.abiAlignment(zcu).toByteUnits().?);
try diw.writeByte(@intFromBool(false));
},
@@ -2388,7 +2388,7 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In
wip_nav.func_high_pc = @intCast(wip_nav.debug_info.items.len);
try diw.writeInt(u32, 0, dwarf.endian);
const target = file.mod.resolved_target.result;
- try uleb128(diw, switch (nav.status.resolved.alignment) {
+ try uleb128(diw, switch (nav.status.fully_resolved.alignment) {
.none => target_info.defaultFunctionAlignment(target),
else => |a| a.maxStrict(target_info.minFunctionAlignment(target)),
}.toByteUnits().?);
@@ -2952,7 +2952,7 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool
const nav_ty = nav_val.typeOf(zcu);
try wip_nav.refType(nav_ty);
try wip_nav.blockValue(nav_src_loc, nav_val);
- try uleb128(diw, nav.status.resolved.alignment.toByteUnits() orelse
+ try uleb128(diw, nav.status.fully_resolved.alignment.toByteUnits() orelse
nav_ty.abiAlignment(zcu).toByteUnits().?);
try diw.writeByte(@intFromBool(false));
},
@@ -2977,7 +2977,7 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool
try wip_nav.strp(nav.name.toSlice(ip));
try wip_nav.strp(nav.fqn.toSlice(ip));
const nav_ty_reloc_index = try wip_nav.refForward();
- try uleb128(diw, nav.status.resolved.alignment.toByteUnits() orelse
+ try uleb128(diw, nav.status.fully_resolved.alignment.toByteUnits() orelse
nav_ty.abiAlignment(zcu).toByteUnits().?);
try diw.writeByte(@intFromBool(false));
if (has_runtime_bits) try wip_nav.blockValue(nav_src_loc, nav_val);
src/link/Plan9.zig
@@ -1021,7 +1021,7 @@ pub fn seeNav(self: *Plan9, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index)
const atom_idx = gop.value_ptr.index;
// handle externs here because they might not get updateDecl called on them
const nav = ip.getNav(nav_index);
- if (ip.indexToKey(nav.status.resolved.val) == .@"extern") {
+ if (nav.getExtern(ip) != null) {
// this is a "phantom atom" - it is never actually written to disk, just convenient for us to store stuff about externs
if (nav.name.eqlSlice("etext", ip)) {
self.etext_edata_end_atom_indices[0] = atom_idx;
@@ -1370,7 +1370,7 @@ pub fn getNavVAddr(
const ip = &pt.zcu.intern_pool;
const nav = ip.getNav(nav_index);
log.debug("getDeclVAddr for {}", .{nav.name.fmt(ip)});
- if (ip.indexToKey(nav.status.resolved.val) == .@"extern") {
+ if (nav.getExtern(ip) != null) {
if (nav.name.eqlSlice("etext", ip)) {
try self.addReloc(reloc_info.parent.atom_index, .{
.target = undefined,
src/Sema/comptime_ptr_access.zig
@@ -219,9 +219,8 @@ fn loadComptimePtrInner(
const base_val: MutableValue = switch (ptr.base_addr) {
.nav => |nav| val: {
- try sema.declareDependency(.{ .nav_val = nav });
- try sema.ensureNavResolved(src, nav);
- const val = ip.getNav(nav).status.resolved.val;
+ try sema.ensureNavResolved(src, nav, .fully);
+ const val = ip.getNav(nav).status.fully_resolved.val;
switch (ip.indexToKey(val)) {
.variable => return .runtime_load,
// We let `.@"extern"` through here if it's a function.
src/Zcu/PerThread.zig
@@ -731,10 +731,12 @@ pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu
const gpa = zcu.gpa;
const ip = &zcu.intern_pool;
+ _ = zcu.nav_val_analysis_queued.swapRemove(nav_id);
+
const anal_unit: AnalUnit = .wrap(.{ .nav_val = nav_id });
const nav = ip.getNav(nav_id);
- log.debug("ensureNavUpToDate {}", .{zcu.fmtAnalUnit(anal_unit)});
+ log.debug("ensureNavValUpToDate {}", .{zcu.fmtAnalUnit(anal_unit)});
// Determine whether or not this `Nav`'s value is outdated. This also includes checking if the
// status is `.unresolved`, which indicates that the value is outdated because it has *never*
@@ -763,19 +765,19 @@ pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu
} else {
// We can trust the current information about this unit.
if (prev_failed) return error.AnalysisFail;
- if (nav.status == .resolved) return;
+ switch (nav.status) {
+ .unresolved, .type_resolved => {},
+ .fully_resolved => return,
+ }
}
const unit_prog_node = zcu.sema_prog_node.start(nav.fqn.toSlice(ip), 0);
defer unit_prog_node.end();
- const sema_result: SemaNavResult, const new_failed: bool = if (pt.analyzeNavVal(nav_id)) |result| res: {
+ const invalidate_value: bool, const new_failed: bool = if (pt.analyzeNavVal(nav_id)) |result| res: {
break :res .{
- .{
- // If the unit has gone from failed to success, we still need to invalidate the dependencies.
- .invalidate_nav_val = result.invalidate_nav_val or prev_failed,
- .invalidate_nav_ref = result.invalidate_nav_ref or prev_failed,
- },
+ // If the unit has gone from failed to success, we still need to invalidate the dependencies.
+ result.val_changed or prev_failed,
false,
};
} else |err| switch (err) {
@@ -786,10 +788,7 @@ pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu
try zcu.transitive_failed_analysis.put(gpa, anal_unit, {});
log.debug("mark transitive analysis failure for {}", .{zcu.fmtAnalUnit(anal_unit)});
}
- break :res .{ .{
- .invalidate_nav_val = !prev_failed,
- .invalidate_nav_ref = !prev_failed,
- }, true };
+ break :res .{ !prev_failed, true };
},
error.OutOfMemory => {
// TODO: it's unclear how to gracefully handle this.
@@ -806,10 +805,8 @@ pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu
};
if (was_outdated) {
- // TODO: we do not yet have separate dependencies for Nav values vs types.
- const invalidate = sema_result.invalidate_nav_val or sema_result.invalidate_nav_ref;
const dependee: InternPool.Dependee = .{ .nav_val = nav_id };
- if (invalidate) {
+ if (invalidate_value) {
// This dependency was marked as PO, meaning dependees were waiting
// on its analysis result, and it has turned out to be outdated.
// Update dependees accordingly.
@@ -824,14 +821,7 @@ pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu
if (new_failed) return error.AnalysisFail;
}
-const SemaNavResult = packed struct {
- /// Whether the value of a `decl_val` of the corresponding Nav changed.
- invalidate_nav_val: bool,
- /// Whether the type of a `decl_ref` of the corresponding Nav changed.
- invalidate_nav_ref: bool,
-};
-
-fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileError!SemaNavResult {
+fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileError!struct { val_changed: bool } {
const zcu = pt.zcu;
const gpa = zcu.gpa;
const ip = &zcu.intern_pool;
@@ -875,9 +865,13 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr
};
defer sema.deinit();
- // The comptime unit declares on the source of the corresponding declaration.
+ // Every `Nav` declares a dependency on the source of the corresponding declaration.
try sema.declareDependency(.{ .src_hash = old_nav.analysis.?.zir_index });
+ // In theory, we would also add a reference to the corresponding `nav_val` unit here: there are
+ // always references in both directions between a `nav_val` and `nav_ty`. However, to save memory,
+ // these references are known implicitly. See logic in `Zcu.resolveReferences`.
+
var block: Sema.Block = .{
.parent = null,
.sema = &sema,
@@ -891,31 +885,44 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr
defer block.instructions.deinit(gpa);
const zir_decl = zir.getDeclaration(inst_resolved.inst);
-
assert(old_nav.is_usingnamespace == (zir_decl.kind == .@"usingnamespace"));
+ const ty_src = block.src(.{ .node_offset_var_decl_ty = 0 });
+ const init_src = block.src(.{ .node_offset_var_decl_init = 0 });
const align_src = block.src(.{ .node_offset_var_decl_align = 0 });
const section_src = block.src(.{ .node_offset_var_decl_section = 0 });
const addrspace_src = block.src(.{ .node_offset_var_decl_addrspace = 0 });
- const ty_src = block.src(.{ .node_offset_var_decl_ty = 0 });
- const init_src = block.src(.{ .node_offset_var_decl_init = 0 });
+
+ const maybe_ty: ?Type = if (zir_decl.type_body != null) ty: {
+ // Since we have a type body, the type is resolved separately!
+ // Of course, we need to make sure we depend on it properly.
+ try sema.declareDependency(.{ .nav_ty = nav_id });
+ try pt.ensureNavTypeUpToDate(nav_id);
+ break :ty .fromInterned(ip.getNav(nav_id).status.type_resolved.type);
+ } else null;
+
+ const final_val: ?Value = if (zir_decl.value_body) |value_body| val: {
+ if (maybe_ty) |ty| {
+ // Put the resolved type into `inst_map` to be used as the result type of the init.
+ try sema.inst_map.ensureSpaceForInstructions(gpa, &.{inst_resolved.inst});
+ sema.inst_map.putAssumeCapacity(inst_resolved.inst, Air.internedToRef(ty.toIntern()));
+ const uncoerced_result_ref = try sema.resolveInlineBody(&block, value_body, inst_resolved.inst);
+ assert(sema.inst_map.remove(inst_resolved.inst));
+
+ const result_ref = try sema.coerce(&block, ty, uncoerced_result_ref, init_src);
+ break :val try sema.resolveFinalDeclValue(&block, init_src, result_ref);
+ } else {
+ // Just analyze the value; we have no type to offer.
+ const result_ref = try sema.resolveInlineBody(&block, value_body, inst_resolved.inst);
+ break :val try sema.resolveFinalDeclValue(&block, init_src, result_ref);
+ }
+ } else null;
+
+ const nav_ty: Type = maybe_ty orelse final_val.?.typeOf(zcu);
// First, we must resolve the declaration's type. To do this, we analyze the type body if available,
// or otherwise, we analyze the value body, populating `early_val` in the process.
- const nav_ty: Type, const early_val: ?Value = if (zir_decl.type_body) |type_body| ty: {
- // We evaluate only the type now; no need for the value yet.
- const uncoerced_type_ref = try sema.resolveInlineBody(&block, type_body, inst_resolved.inst);
- const type_ref = try sema.coerce(&block, .type, uncoerced_type_ref, ty_src);
- break :ty .{ .fromInterned(type_ref.toInterned().?), null };
- } else ty: {
- // We don't have a type body, so we need to evaluate the value immediately.
- const value_body = zir_decl.value_body.?;
- const result_ref = try sema.resolveInlineBody(&block, value_body, inst_resolved.inst);
- const val = try sema.resolveFinalDeclValue(&block, init_src, result_ref);
- break :ty .{ val.typeOf(zcu), val };
- };
-
switch (zir_decl.kind) {
.@"comptime" => unreachable, // this is not a Nav
.unnamed_test, .@"test", .decltest => assert(nav_ty.zigTypeTag(zcu) == .@"fn"),
@@ -932,58 +939,24 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr
// Now that we know the type, we can evaluate the alignment, linksection, and addrspace, to determine
// the full pointer type of this declaration.
- const alignment: InternPool.Alignment = a: {
- const align_body = zir_decl.align_body orelse break :a .none;
- const align_ref = try sema.resolveInlineBody(&block, align_body, inst_resolved.inst);
- break :a try sema.analyzeAsAlign(&block, align_src, align_ref);
- };
-
- const @"linksection": InternPool.OptionalNullTerminatedString = ls: {
- const linksection_body = zir_decl.linksection_body orelse break :ls .none;
- const linksection_ref = try sema.resolveInlineBody(&block, linksection_body, inst_resolved.inst);
- const bytes = try sema.toConstString(&block, section_src, linksection_ref, .{
- .needed_comptime_reason = "linksection must be comptime-known",
- });
- if (std.mem.indexOfScalar(u8, bytes, 0) != null) {
- return sema.fail(&block, section_src, "linksection cannot contain null bytes", .{});
- } else if (bytes.len == 0) {
- return sema.fail(&block, section_src, "linksection cannot be empty", .{});
- }
- break :ls try ip.getOrPutStringOpt(gpa, pt.tid, bytes, .no_embedded_nulls);
- };
-
- const @"addrspace": std.builtin.AddressSpace = as: {
- const addrspace_ctx: Sema.AddressSpaceContext = switch (zir_decl.kind) {
- .@"var" => .variable,
- else => switch (nav_ty.zigTypeTag(zcu)) {
- .@"fn" => .function,
- else => .constant,
+ const modifiers: Sema.NavPtrModifiers = if (zir_decl.type_body != null) m: {
+ // `analyzeNavType` (from the `ensureNavTypeUpToDate` call above) has already populated this data into
+ // the `Nav`. Load the new one, and pull the modifiers out.
+ switch (ip.getNav(nav_id).status) {
+ .unresolved => unreachable, // `analyzeNavType` will never leave us in this state
+ inline .type_resolved, .fully_resolved => |r| break :m .{
+ .alignment = r.alignment,
+ .@"linksection" = r.@"linksection",
+ .@"addrspace" = r.@"addrspace",
},
- };
- const target = zcu.getTarget();
- const addrspace_body = zir_decl.addrspace_body orelse break :as switch (addrspace_ctx) {
- .function => target_util.defaultAddressSpace(target, .function),
- .variable => target_util.defaultAddressSpace(target, .global_mutable),
- .constant => target_util.defaultAddressSpace(target, .global_constant),
- else => unreachable,
- };
- const addrspace_ref = try sema.resolveInlineBody(&block, addrspace_body, inst_resolved.inst);
- break :as try sema.analyzeAsAddressSpace(&block, addrspace_src, addrspace_ref, addrspace_ctx);
+ }
+ } else m: {
+ // `analyzeNavType` is essentially a stub which calls us. We are responsible for resolving this data.
+ break :m try sema.resolveNavPtrModifiers(&block, zir_decl, inst_resolved.inst, nav_ty);
};
- // Lastly, we must evaluate the value if we have not already done so. Note, however, that extern declarations
- // don't have an associated value body.
-
- const final_val: ?Value = early_val orelse if (zir_decl.value_body) |value_body| val: {
- // Put the resolved type into `inst_map` to be used as the result type of the init.
- try sema.inst_map.ensureSpaceForInstructions(gpa, &.{inst_resolved.inst});
- sema.inst_map.putAssumeCapacity(inst_resolved.inst, Air.internedToRef(nav_ty.toIntern()));
- const uncoerced_result_ref = try sema.resolveInlineBody(&block, value_body, inst_resolved.inst);
- assert(sema.inst_map.remove(inst_resolved.inst));
-
- const result_ref = try sema.coerce(&block, nav_ty, uncoerced_result_ref, init_src);
- break :val try sema.resolveFinalDeclValue(&block, init_src, result_ref);
- } else null;
+ // Lastly, we must figure out the actual interned value to store to the `Nav`.
+ // This isn't necessarily the same as `final_val`!
const nav_val: Value = switch (zir_decl.linkage) {
.normal, .@"export" => switch (zir_decl.kind) {
@@ -1013,8 +986,8 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr
.is_threadlocal = zir_decl.is_threadlocal,
.is_weak_linkage = false,
.is_dll_import = false,
- .alignment = alignment,
- .@"addrspace" = @"addrspace",
+ .alignment = modifiers.alignment,
+ .@"addrspace" = modifiers.@"addrspace",
.zir_index = old_nav.analysis.?.zir_index, // `declaration` instruction
.owner_nav = undefined, // ignored by `getExtern`
}));
@@ -1047,10 +1020,7 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr
});
// TODO: usingnamespace cannot participate in incremental compilation
assert(zcu.analysis_in_progress.swapRemove(anal_unit));
- return .{
- .invalidate_nav_val = true,
- .invalidate_nav_ref = true,
- };
+ return .{ .val_changed = true };
}
const queue_linker_work, const is_owned_fn = switch (ip.indexToKey(nav_val.toIntern())) {
@@ -1087,14 +1057,22 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr
ip.resolveNavValue(nav_id, .{
.val = nav_val.toIntern(),
- .alignment = alignment,
- .@"linksection" = @"linksection",
- .@"addrspace" = @"addrspace",
+ .alignment = modifiers.alignment,
+ .@"linksection" = modifiers.@"linksection",
+ .@"addrspace" = modifiers.@"addrspace",
});
// Mark the unit as completed before evaluating the export!
assert(zcu.analysis_in_progress.swapRemove(anal_unit));
+ if (zir_decl.type_body == null) {
+ // In this situation, it's possible that we were triggered by `analyzeNavType` up the stack. In that
+ // case, we must also signal that the *type* is now populated to make this export behave correctly.
+ // An alternative strategy would be to just put something on the job queue to perform the export, but
+ // this is a little more straightforward, if perhaps less elegant.
+ _ = zcu.analysis_in_progress.swapRemove(.wrap(.{ .nav_ty = nav_id }));
+ }
+
if (zir_decl.linkage == .@"export") {
const export_src = block.src(.{ .token_offset = @intFromBool(zir_decl.is_pub) });
const name_slice = zir.nullTerminatedString(zir_decl.name);
@@ -1117,21 +1095,246 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr
}
switch (old_nav.status) {
- .unresolved => return .{
- .invalidate_nav_val = true,
- .invalidate_nav_ref = true,
+ .unresolved, .type_resolved => return .{ .val_changed = true },
+ .fully_resolved => |old| return .{ .val_changed = old.val != nav_val.toIntern() },
+ }
+}
+
+pub fn ensureNavTypeUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.SemaError!void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const zcu = pt.zcu;
+ const gpa = zcu.gpa;
+ const ip = &zcu.intern_pool;
+
+ const anal_unit: AnalUnit = .wrap(.{ .nav_ty = nav_id });
+ const nav = ip.getNav(nav_id);
+
+ log.debug("ensureNavTypeUpToDate {}", .{zcu.fmtAnalUnit(anal_unit)});
+
+ // Determine whether or not this `Nav`'s type is outdated. This also includes checking if the
+ // status is `.unresolved`, which indicates that the value is outdated because it has *never*
+ // been analyzed so far.
+ //
+ // Note that if the unit is PO, we pessimistically assume that it *does* require re-analysis, to
+ // ensure that the unit is definitely up-to-date when this function returns. This mechanism could
+ // result in over-analysis if analysis occurs in a poor order; we do our best to avoid this by
+ // carefully choosing which units to re-analyze. See `Zcu.findOutdatedToAnalyze`.
+
+ const was_outdated = zcu.outdated.swapRemove(anal_unit) or
+ zcu.potentially_outdated.swapRemove(anal_unit);
+
+ const prev_failed = zcu.failed_analysis.contains(anal_unit) or
+ zcu.transitive_failed_analysis.contains(anal_unit);
+
+ if (was_outdated) {
+ dev.check(.incremental);
+ _ = zcu.outdated_ready.swapRemove(anal_unit);
+ zcu.deleteUnitExports(anal_unit);
+ zcu.deleteUnitReferences(anal_unit);
+ if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| {
+ kv.value.destroy(gpa);
+ }
+ _ = zcu.transitive_failed_analysis.swapRemove(anal_unit);
+ } else {
+ // We can trust the current information about this unit.
+ if (prev_failed) return error.AnalysisFail;
+ switch (nav.status) {
+ .unresolved => {},
+ .type_resolved, .fully_resolved => return,
+ }
+ }
+
+ const unit_prog_node = zcu.sema_prog_node.start(nav.fqn.toSlice(ip), 0);
+ defer unit_prog_node.end();
+
+ const invalidate_type: bool, const new_failed: bool = if (pt.analyzeNavType(nav_id)) |result| res: {
+ break :res .{
+ // If the unit has gone from failed to success, we still need to invalidate the dependencies.
+ result.type_changed or prev_failed,
+ false,
+ };
+ } else |err| switch (err) {
+ error.AnalysisFail => res: {
+ if (!zcu.failed_analysis.contains(anal_unit)) {
+ // If this unit caused the error, it would have an entry in `failed_analysis`.
+ // Since it does not, this must be a transitive failure.
+ try zcu.transitive_failed_analysis.put(gpa, anal_unit, {});
+ log.debug("mark transitive analysis failure for {}", .{zcu.fmtAnalUnit(anal_unit)});
+ }
+ break :res .{ !prev_failed, true };
},
- .resolved => |old| {
- const new = ip.getNav(nav_id).status.resolved;
- return .{
- .invalidate_nav_val = new.val != old.val,
- .invalidate_nav_ref = ip.typeOf(new.val) != ip.typeOf(old.val) or
- new.alignment != old.alignment or
- new.@"linksection" != old.@"linksection" or
- new.@"addrspace" != old.@"addrspace",
- };
+ error.OutOfMemory => {
+ // TODO: it's unclear how to gracefully handle this.
+ // To report the error cleanly, we need to add a message to `failed_analysis` and a
+ // corresponding entry to `retryable_failures`; but either of these things is quite
+ // likely to OOM at this point.
+ // If that happens, what do we do? Perhaps we could have a special field on `Zcu`
+ // for reporting OOM errors without allocating.
+ return error.OutOfMemory;
},
+ error.GenericPoison => unreachable,
+ error.ComptimeReturn => unreachable,
+ error.ComptimeBreak => unreachable,
+ };
+
+ if (was_outdated) {
+ const dependee: InternPool.Dependee = .{ .nav_ty = nav_id };
+ if (invalidate_type) {
+ // This dependency was marked as PO, meaning dependees were waiting
+ // on its analysis result, and it has turned out to be outdated.
+ // Update dependees accordingly.
+ try zcu.markDependeeOutdated(.marked_po, dependee);
+ } else {
+ // This dependency was previously PO, but turned out to be up-to-date.
+ // We do not need to queue successive analysis.
+ try zcu.markPoDependeeUpToDate(dependee);
+ }
}
+
+ if (new_failed) return error.AnalysisFail;
+}
+
+fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileError!struct { type_changed: bool } {
+ const zcu = pt.zcu;
+ const gpa = zcu.gpa;
+ const ip = &zcu.intern_pool;
+
+ const anal_unit: AnalUnit = .wrap(.{ .nav_ty = nav_id });
+ const old_nav = ip.getNav(nav_id);
+
+ log.debug("analyzeNavType {}", .{zcu.fmtAnalUnit(anal_unit)});
+
+ const inst_resolved = old_nav.analysis.?.zir_index.resolveFull(ip) orelse return error.AnalysisFail;
+ const file = zcu.fileByIndex(inst_resolved.file);
+ // TODO: stop the compiler ever reaching Sema if there are failed files. That way, this check is
+ // unnecessary, and we can move the below `removeDependenciesForDepender` call up with its friends
+ // in `ensureComptimeUnitUpToDate`.
+ if (file.status != .success_zir) return error.AnalysisFail;
+ const zir = file.zir;
+
+ // We are about to re-analyze this unit; drop its depenndencies.
+ zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit);
+
+ try zcu.analysis_in_progress.put(gpa, anal_unit, {});
+ defer _ = zcu.analysis_in_progress.swapRemove(anal_unit);
+
+ var analysis_arena: std.heap.ArenaAllocator = .init(gpa);
+ defer analysis_arena.deinit();
+
+ var comptime_err_ret_trace: std.ArrayList(Zcu.LazySrcLoc) = .init(gpa);
+ defer comptime_err_ret_trace.deinit();
+
+ var sema: Sema = .{
+ .pt = pt,
+ .gpa = gpa,
+ .arena = analysis_arena.allocator(),
+ .code = zir,
+ .owner = anal_unit,
+ .func_index = .none,
+ .func_is_naked = false,
+ .fn_ret_ty = .void,
+ .fn_ret_ty_ies = null,
+ .comptime_err_ret_trace = &comptime_err_ret_trace,
+ };
+ defer sema.deinit();
+
+ // Every `Nav` declares a dependency on the source of the corresponding declaration.
+ try sema.declareDependency(.{ .src_hash = old_nav.analysis.?.zir_index });
+
+ // In theory, we would also add a reference to the corresponding `nav_val` unit here: there are
+ // always references in both directions between a `nav_val` and `nav_ty`. However, to save memory,
+ // these references are known implicitly. See logic in `Zcu.resolveReferences`.
+
+ var block: Sema.Block = .{
+ .parent = null,
+ .sema = &sema,
+ .namespace = old_nav.analysis.?.namespace,
+ .instructions = .{},
+ .inlining = null,
+ .is_comptime = true,
+ .src_base_inst = old_nav.analysis.?.zir_index,
+ .type_name_ctx = old_nav.fqn,
+ };
+ defer block.instructions.deinit(gpa);
+
+ const zir_decl = zir.getDeclaration(inst_resolved.inst);
+ assert(old_nav.is_usingnamespace == (zir_decl.kind == .@"usingnamespace"));
+
+ const type_body = zir_decl.type_body orelse {
+ // The type of this `Nav` is inferred from the value.
+ // In other words, this `nav_ty` depends on the corresponding `nav_val`.
+ try sema.declareDependency(.{ .nav_val = nav_id });
+ try pt.ensureNavValUpToDate(nav_id);
+ // Note that the above call, if it did any work, has removed our `analysis_in_progress` entry for us.
+ // (Our `defer` will run anyway, but it does nothing in this case.)
+
+ // There's not a great way for us to know whether the type actually changed.
+ // For instance, perhaps the `nav_val` was already up-to-date, but this `nav_ty` is being
+ // analyzed because this declaration had a type annotation on the *previous* update.
+ // However, such cases are rare, and it's not unreasonable to re-analyze in them; and in
+ // other cases where we get here, it's because the `nav_val` was already re-analyzed and
+ // is outdated.
+ return .{ .type_changed = true };
+ };
+
+ const ty_src = block.src(.{ .node_offset_var_decl_ty = 0 });
+
+ const resolved_ty: Type = ty: {
+ const uncoerced_type_ref = try sema.resolveInlineBody(&block, type_body, inst_resolved.inst);
+ const type_ref = try sema.coerce(&block, .type, uncoerced_type_ref, ty_src);
+ break :ty .fromInterned(type_ref.toInterned().?);
+ };
+
+ // In the case where the type is specified, this function is also responsible for resolving
+ // the pointer modifiers, i.e. alignment, linksection, addrspace.
+ const modifiers = try sema.resolveNavPtrModifiers(&block, zir_decl, inst_resolved.inst, resolved_ty);
+
+ // Usually, we can infer this information from the resolved `Nav` value; see `Zcu.navValIsConst`.
+ // However, since we don't have one, we need to quickly check the ZIR to figure this out.
+ const is_const = switch (zir_decl.kind) {
+ .@"comptime" => unreachable,
+ .unnamed_test, .@"test", .decltest, .@"usingnamespace", .@"const" => true,
+ .@"var" => false,
+ };
+
+ const is_extern_decl = zir_decl.linkage == .@"extern";
+
+ // Now for the question of the day: are the type and modifiers the same as before?
+ // If they are, then we should actually keep the `Nav` as `fully_resolved` if it currently is.
+ // That's because `analyzeNavVal` will later want to look at the resolved value to figure out
+ // whether it's changed: if we threw that data away now, it would have to assume that the value
+ // had changed, potentially spinning off loads of unnecessary re-analysis!
+ const changed = switch (old_nav.status) {
+ .unresolved => true,
+ .type_resolved => |r| r.type != resolved_ty.toIntern() or
+ r.alignment != modifiers.alignment or
+ r.@"linksection" != modifiers.@"linksection" or
+ r.@"addrspace" != modifiers.@"addrspace" or
+ r.is_const != is_const or
+ r.is_extern_decl != is_extern_decl,
+ .fully_resolved => |r| ip.typeOf(r.val) != resolved_ty.toIntern() or
+ r.alignment != modifiers.alignment or
+ r.@"linksection" != modifiers.@"linksection" or
+ r.@"addrspace" != modifiers.@"addrspace" or
+ zcu.navValIsConst(r.val) != is_const or
+ (old_nav.getExtern(ip) != null) != is_extern_decl,
+ };
+
+ if (!changed) return .{ .type_changed = false };
+
+ ip.resolveNavType(nav_id, .{
+ .type = resolved_ty.toIntern(),
+ .alignment = modifiers.alignment,
+ .@"linksection" = modifiers.@"linksection",
+ .@"addrspace" = modifiers.@"addrspace",
+ .is_const = is_const,
+ .is_threadlocal = zir_decl.is_threadlocal,
+ .is_extern_decl = is_extern_decl,
+ });
+
+ return .{ .type_changed = true };
}
pub fn ensureFuncBodyUpToDate(pt: Zcu.PerThread, maybe_coerced_func_index: InternPool.Index) Zcu.SemaError!void {
@@ -1144,6 +1347,8 @@ pub fn ensureFuncBodyUpToDate(pt: Zcu.PerThread, maybe_coerced_func_index: Inter
const gpa = zcu.gpa;
const ip = &zcu.intern_pool;
+ _ = zcu.func_body_analysis_queued.swapRemove(maybe_coerced_func_index);
+
// We only care about the uncoerced function.
const func_index = ip.unwrapCoercedFunc(maybe_coerced_func_index);
const anal_unit: AnalUnit = .wrap(.{ .func = func_index });
@@ -1171,11 +1376,7 @@ pub fn ensureFuncBodyUpToDate(pt: Zcu.PerThread, maybe_coerced_func_index: Inter
if (prev_failed) {
return error.AnalysisFail;
}
- switch (func.analysisUnordered(ip).state) {
- .unreferenced => {}, // this is the first reference
- .queued => {}, // we're waiting on first-time analysis
- .analyzed => return, // up-to-date
- }
+ if (func.analysisUnordered(ip).is_analyzed) return;
}
const func_prog_node = zcu.sema_prog_node.start(ip.getNav(func.owner_nav).fqn.toSlice(ip), 0);
@@ -1236,7 +1437,7 @@ fn analyzeFuncBody(
if (func.generic_owner == .none) {
// Among another things, this ensures that the function's `zir_body_inst` is correct.
try pt.ensureNavValUpToDate(func.owner_nav);
- if (ip.getNav(func.owner_nav).status.resolved.val != func_index) {
+ if (ip.getNav(func.owner_nav).status.fully_resolved.val != func_index) {
// This function is no longer referenced! There's no point in re-analyzing it.
// Just mark a transitive failure and move on.
return error.AnalysisFail;
@@ -1245,7 +1446,7 @@ fn analyzeFuncBody(
const go_nav = zcu.funcInfo(func.generic_owner).owner_nav;
// Among another things, this ensures that the function's `zir_body_inst` is correct.
try pt.ensureNavValUpToDate(go_nav);
- if (ip.getNav(go_nav).status.resolved.val != func.generic_owner) {
+ if (ip.getNav(go_nav).status.fully_resolved.val != func.generic_owner) {
// The generic owner is no longer referenced, so this function is also unreferenced.
// There's no point in re-analyzing it. Just mark a transitive failure and move on.
return error.AnalysisFail;
@@ -2172,7 +2373,7 @@ fn analyzeFnBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaE
try zcu.analysis_in_progress.put(gpa, anal_unit, {});
errdefer _ = zcu.analysis_in_progress.swapRemove(anal_unit);
- func.setAnalysisState(ip, .analyzed);
+ func.setAnalyzed(ip);
if (func.analysisUnordered(ip).inferred_error_set) {
func.setResolvedErrorSet(ip, .none);
}
@@ -2550,8 +2751,8 @@ fn processExportsInner(
if (zcu.transitive_failed_analysis.contains(unit)) break :failed true;
}
const val = switch (nav.status) {
- .unresolved => break :failed true,
- .resolved => |r| Value.fromInterned(r.val),
+ .unresolved, .type_resolved => break :failed true,
+ .fully_resolved => |r| Value.fromInterned(r.val),
};
// If the value is a function, we also need to check if that function succeeded analysis.
if (val.typeOf(zcu).zigTypeTag(zcu) == .@"fn") {
@@ -3256,30 +3457,29 @@ pub fn getBuiltinNav(pt: Zcu.PerThread, name: []const u8) Allocator.Error!Intern
const builtin_nav = std_namespace.pub_decls.getKeyAdapted(builtin_str, Zcu.Namespace.NameAdapter{ .zcu = zcu }) orelse
@panic("lib/std.zig is corrupt and missing 'builtin'");
pt.ensureNavValUpToDate(builtin_nav) catch @panic("std.builtin is corrupt");
- const builtin_type = Type.fromInterned(ip.getNav(builtin_nav).status.resolved.val);
+ const builtin_type = Type.fromInterned(ip.getNav(builtin_nav).status.fully_resolved.val);
const builtin_namespace = zcu.namespacePtr(builtin_type.getNamespace(zcu).unwrap() orelse @panic("std.builtin is corrupt"));
const name_str = try ip.getOrPutString(gpa, pt.tid, name, .no_embedded_nulls);
return builtin_namespace.pub_decls.getKeyAdapted(name_str, Zcu.Namespace.NameAdapter{ .zcu = zcu }) orelse @panic("lib/std/builtin.zig is corrupt");
}
-pub fn navPtrType(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) Allocator.Error!Type {
+pub fn navPtrType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Allocator.Error!Type {
const zcu = pt.zcu;
const ip = &zcu.intern_pool;
- const r = ip.getNav(nav_index).status.resolved;
- const ty = Value.fromInterned(r.val).typeOf(zcu);
+ const ty, const alignment, const @"addrspace", const is_const = switch (ip.getNav(nav_id).status) {
+ .unresolved => unreachable,
+ .type_resolved => |r| .{ r.type, r.alignment, r.@"addrspace", r.is_const },
+ .fully_resolved => |r| .{ ip.typeOf(r.val), r.alignment, r.@"addrspace", zcu.navValIsConst(r.val) },
+ };
return pt.ptrType(.{
- .child = ty.toIntern(),
+ .child = ty,
.flags = .{
- .alignment = if (r.alignment == ty.abiAlignment(zcu))
+ .alignment = if (alignment == Type.fromInterned(ty).abiAlignment(zcu))
.none
else
- r.alignment,
- .address_space = r.@"addrspace",
- .is_const = switch (ip.indexToKey(r.val)) {
- .variable => false,
- .@"extern" => |e| e.is_const,
- else => true,
- },
+ alignment,
+ .address_space = @"addrspace",
+ .is_const = is_const,
},
});
}
@@ -3299,9 +3499,13 @@ pub fn getExtern(pt: Zcu.PerThread, key: InternPool.Key.Extern) Allocator.Error!
// TODO: this shouldn't need a `PerThread`! Fix the signature of `Type.abiAlignment`.
pub fn navAlignment(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) InternPool.Alignment {
const zcu = pt.zcu;
- const r = zcu.intern_pool.getNav(nav_index).status.resolved;
- if (r.alignment != .none) return r.alignment;
- return Value.fromInterned(r.val).typeOf(zcu).abiAlignment(zcu);
+ const ty: Type, const alignment = switch (zcu.intern_pool.getNav(nav_index).status) {
+ .unresolved => unreachable,
+ .type_resolved => |r| .{ .fromInterned(r.type), r.alignment },
+ .fully_resolved => |r| .{ Value.fromInterned(r.val).typeOf(zcu), r.alignment },
+ };
+ if (alignment != .none) return alignment;
+ return ty.abiAlignment(zcu);
}
/// Given a container type requiring resolution, ensures that it is up-to-date.
src/codegen.zig
@@ -817,7 +817,7 @@ fn genNavRef(
pt: Zcu.PerThread,
src_loc: Zcu.LazySrcLoc,
val: Value,
- ref_nav_index: InternPool.Nav.Index,
+ nav_index: InternPool.Nav.Index,
target: std.Target,
) CodeGenError!GenResult {
const zcu = pt.zcu;
@@ -851,14 +851,15 @@ fn genNavRef(
}
}
- const nav_index, const is_extern, const lib_name, const is_threadlocal = switch (ip.indexToKey(zcu.navValue(ref_nav_index).toIntern())) {
- .func => |func| .{ func.owner_nav, false, .none, false },
- .variable => |variable| .{ variable.owner_nav, false, .none, variable.is_threadlocal },
- .@"extern" => |@"extern"| .{ @"extern".owner_nav, true, @"extern".lib_name, @"extern".is_threadlocal },
- else => .{ ref_nav_index, false, .none, false },
- };
+ const nav = ip.getNav(nav_index);
+
+ const is_extern, const lib_name, const is_threadlocal = if (nav.getExtern(ip)) |e|
+ .{ true, e.lib_name, e.is_threadlocal }
+ else
+ .{ false, .none, nav.isThreadlocal(ip) };
+
const single_threaded = zcu.navFileScope(nav_index).mod.single_threaded;
- const name = ip.getNav(nav_index).name;
+ const name = nav.name;
if (lf.cast(.elf)) |elf_file| {
const zo = elf_file.zigObjectPtr().?;
if (is_extern) {
src/Compilation.zig
@@ -2906,6 +2906,7 @@ const Header = extern struct {
file_deps_len: u32,
src_hash_deps_len: u32,
nav_val_deps_len: u32,
+ nav_ty_deps_len: u32,
namespace_deps_len: u32,
namespace_name_deps_len: u32,
first_dependency_len: u32,
@@ -2949,6 +2950,7 @@ pub fn saveState(comp: *Compilation) !void {
.file_deps_len = @intCast(ip.file_deps.count()),
.src_hash_deps_len = @intCast(ip.src_hash_deps.count()),
.nav_val_deps_len = @intCast(ip.nav_val_deps.count()),
+ .nav_ty_deps_len = @intCast(ip.nav_ty_deps.count()),
.namespace_deps_len = @intCast(ip.namespace_deps.count()),
.namespace_name_deps_len = @intCast(ip.namespace_name_deps.count()),
.first_dependency_len = @intCast(ip.first_dependency.count()),
@@ -2979,6 +2981,8 @@ pub fn saveState(comp: *Compilation) !void {
addBuf(&bufs, mem.sliceAsBytes(ip.src_hash_deps.values()));
addBuf(&bufs, mem.sliceAsBytes(ip.nav_val_deps.keys()));
addBuf(&bufs, mem.sliceAsBytes(ip.nav_val_deps.values()));
+ addBuf(&bufs, mem.sliceAsBytes(ip.nav_ty_deps.keys()));
+ addBuf(&bufs, mem.sliceAsBytes(ip.nav_ty_deps.values()));
addBuf(&bufs, mem.sliceAsBytes(ip.namespace_deps.keys()));
addBuf(&bufs, mem.sliceAsBytes(ip.namespace_deps.values()));
addBuf(&bufs, mem.sliceAsBytes(ip.namespace_name_deps.keys()));
@@ -3145,7 +3149,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
const file_index = switch (anal_unit.unwrap()) {
.@"comptime" => |cu| ip.getComptimeUnit(cu).zir_index.resolveFile(ip),
- .nav_val => |nav| ip.getNav(nav).analysis.?.zir_index.resolveFile(ip),
+ .nav_val, .nav_ty => |nav| ip.getNav(nav).analysis.?.zir_index.resolveFile(ip),
.type => |ty| Type.fromInterned(ty).typeDeclInst(zcu).?.resolveFile(ip),
.func => |ip_index| zcu.funcInfo(ip_index).zir_body_inst.resolveFile(ip),
};
@@ -3380,7 +3384,7 @@ pub fn addModuleErrorMsg(
defer gpa.free(rt_file_path);
const name = switch (ref.referencer.unwrap()) {
.@"comptime" => "comptime",
- .nav_val => |nav| ip.getNav(nav).name.toSlice(ip),
+ .nav_val, .nav_ty => |nav| ip.getNav(nav).name.toSlice(ip),
.type => |ty| Type.fromInterned(ty).containerTypeName(ip).toSlice(ip),
.func => |f| ip.getNav(zcu.funcInfo(f).owner_nav).name.toSlice(ip),
};
@@ -3647,6 +3651,7 @@ fn performAllTheWorkInner(
try comp.queueJob(switch (outdated.unwrap()) {
.func => |f| .{ .analyze_func = f },
.@"comptime",
+ .nav_ty,
.nav_val,
.type,
=> .{ .analyze_comptime_unit = outdated },
@@ -3679,7 +3684,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre
return;
}
}
- assert(nav.status == .resolved);
+ assert(nav.status == .fully_resolved);
comp.dispatchCodegenTask(tid, .{ .codegen_nav = nav_index });
},
.codegen_func => |func| {
@@ -3709,6 +3714,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre
const maybe_err: Zcu.SemaError!void = switch (unit.unwrap()) {
.@"comptime" => |cu| pt.ensureComptimeUnitUpToDate(cu),
+ .nav_ty => |nav| pt.ensureNavTypeUpToDate(nav),
.nav_val => |nav| pt.ensureNavValUpToDate(nav),
.type => |ty| if (pt.ensureTypeUpToDate(ty)) |_| {} else |err| err,
.func => unreachable,
@@ -3734,7 +3740,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre
// Tests are always emitted in test binaries. The decl_refs are created by
// Zcu.populateTestFunctions, but this will not queue body analysis, so do
// that now.
- try pt.zcu.ensureFuncBodyAnalysisQueued(ip.getNav(nav).status.resolved.val);
+ try pt.zcu.ensureFuncBodyAnalysisQueued(ip.getNav(nav).status.fully_resolved.val);
}
},
.resolve_type_fully => |ty| {
src/InternPool.zig
@@ -34,6 +34,9 @@ src_hash_deps: std.AutoArrayHashMapUnmanaged(TrackedInst.Index, DepEntry.Index),
/// Dependencies on the value of a Nav.
/// Value is index into `dep_entries` of the first dependency on this Nav value.
nav_val_deps: std.AutoArrayHashMapUnmanaged(Nav.Index, DepEntry.Index),
+/// Dependencies on the type of a Nav.
+/// Value is index into `dep_entries` of the first dependency on this Nav value.
+nav_ty_deps: std.AutoArrayHashMapUnmanaged(Nav.Index, DepEntry.Index),
/// Dependencies on an interned value, either:
/// * a runtime function (invalidated when its IES changes)
/// * a container type requiring resolution (invalidated when the type must be recreated at a new index)
@@ -80,6 +83,7 @@ pub const empty: InternPool = .{
.file_deps = .empty,
.src_hash_deps = .empty,
.nav_val_deps = .empty,
+ .nav_ty_deps = .empty,
.interned_deps = .empty,
.namespace_deps = .empty,
.namespace_name_deps = .empty,
@@ -371,6 +375,7 @@ pub const AnalUnit = packed struct(u64) {
pub const Kind = enum(u32) {
@"comptime",
nav_val,
+ nav_ty,
type,
func,
};
@@ -380,6 +385,8 @@ pub const AnalUnit = packed struct(u64) {
@"comptime": ComptimeUnit.Id,
/// This `AnalUnit` resolves the value of the given `Nav`.
nav_val: Nav.Index,
+ /// This `AnalUnit` resolves the type of the given `Nav`.
+ nav_ty: Nav.Index,
/// This `AnalUnit` resolves the given `struct`/`union`/`enum` type.
/// Generated tag enums are never used here (they do not undergo type resolution).
type: InternPool.Index,
@@ -483,8 +490,20 @@ pub const Nav = struct {
status: union(enum) {
/// This `Nav` is pending semantic analysis.
unresolved,
+ /// The type of this `Nav` is resolved; the value is queued for resolution.
+ type_resolved: struct {
+ type: InternPool.Index,
+ alignment: Alignment,
+ @"linksection": OptionalNullTerminatedString,
+ @"addrspace": std.builtin.AddressSpace,
+ is_const: bool,
+ is_threadlocal: bool,
+ /// This field is whether this `Nav` is a literal `extern` definition.
+ /// It does *not* tell you whether this might alias an extern fn (see #21027).
+ is_extern_decl: bool,
+ },
/// The value of this `Nav` is resolved.
- resolved: struct {
+ fully_resolved: struct {
val: InternPool.Index,
alignment: Alignment,
@"linksection": OptionalNullTerminatedString,
@@ -492,14 +511,81 @@ pub const Nav = struct {
},
},
- /// Asserts that `status == .resolved`.
+ /// Asserts that `status != .unresolved`.
pub fn typeOf(nav: Nav, ip: *const InternPool) InternPool.Index {
- return ip.typeOf(nav.status.resolved.val);
+ return switch (nav.status) {
+ .unresolved => unreachable,
+ .type_resolved => |r| r.type,
+ .fully_resolved => |r| ip.typeOf(r.val),
+ };
}
- /// Asserts that `status == .resolved`.
- pub fn isExtern(nav: Nav, ip: *const InternPool) bool {
- return ip.indexToKey(nav.status.resolved.val) == .@"extern";
+ /// Always returns `null` for `status == .type_resolved`. This function is inteded
+ /// to be used by code generation, since semantic analysis will ensure that any `Nav`
+ /// which is potentially `extern` is fully resolved.
+ /// Asserts that `status != .unresolved`.
+ pub fn getExtern(nav: Nav, ip: *const InternPool) ?Key.Extern {
+ return switch (nav.status) {
+ .unresolved => unreachable,
+ .type_resolved => null,
+ .fully_resolved => |r| switch (ip.indexToKey(r.val)) {
+ .@"extern" => |e| e,
+ else => null,
+ },
+ };
+ }
+
+ /// Asserts that `status != .unresolved`.
+ pub fn getAddrspace(nav: Nav) std.builtin.AddressSpace {
+ return switch (nav.status) {
+ .unresolved => unreachable,
+ .type_resolved => |r| r.@"addrspace",
+ .fully_resolved => |r| r.@"addrspace",
+ };
+ }
+
+ /// Asserts that `status != .unresolved`.
+ pub fn getAlignment(nav: Nav) Alignment {
+ return switch (nav.status) {
+ .unresolved => unreachable,
+ .type_resolved => |r| r.alignment,
+ .fully_resolved => |r| r.alignment,
+ };
+ }
+
+ /// Asserts that `status != .unresolved`.
+ pub fn isThreadlocal(nav: Nav, ip: *const InternPool) bool {
+ return switch (nav.status) {
+ .unresolved => unreachable,
+ .type_resolved => |r| r.is_threadlocal,
+ .fully_resolved => |r| switch (ip.indexToKey(r.val)) {
+ .@"extern" => |e| e.is_threadlocal,
+ .variable => |v| v.is_threadlocal,
+ else => false,
+ },
+ };
+ }
+
+ /// If this returns `true`, then a pointer to this `Nav` might actually be encoded as a pointer
+ /// to some other `Nav` due to an extern definition or extern alias (see #21027).
+ /// This query is valid on `Nav`s for whom only the type is resolved.
+ /// Asserts that `status != .unresolved`.
+ pub fn isExternOrFn(nav: Nav, ip: *const InternPool) bool {
+ return switch (nav.status) {
+ .unresolved => unreachable,
+ .type_resolved => |r| {
+ if (r.is_extern_decl) return true;
+ const tag = ip.zigTypeTagOrPoison(r.type) catch unreachable;
+ if (tag == .@"fn") return true;
+ return false;
+ },
+ .fully_resolved => |r| {
+ if (ip.indexToKey(r.val) == .@"extern") return true;
+ const tag = ip.zigTypeTagOrPoison(ip.typeOf(r.val)) catch unreachable;
+ if (tag == .@"fn") return true;
+ return false;
+ },
+ };
}
/// Get the ZIR instruction corresponding to this `Nav`, used to resolve source locations.
@@ -509,7 +595,7 @@ pub const Nav = struct {
return a.zir_index;
}
// A `Nav` which does not undergo analysis always has a resolved value.
- return switch (ip.indexToKey(nav.status.resolved.val)) {
+ return switch (ip.indexToKey(nav.status.fully_resolved.val)) {
.func => |func| {
// Since `analysis` was not populated, this must be an instantiation.
// Go up to the generic owner and consult *its* `analysis` field.
@@ -567,19 +653,22 @@ pub const Nav = struct {
// The following 1 fields are either both populated, or both `.none`.
analysis_namespace: OptionalNamespaceIndex,
analysis_zir_index: TrackedInst.Index.Optional,
- /// Populated only if `bits.status == .resolved`.
- val: InternPool.Index,
- /// Populated only if `bits.status == .resolved`.
+ /// Populated only if `bits.status != .unresolved`.
+ type_or_val: InternPool.Index,
+ /// Populated only if `bits.status != .unresolved`.
@"linksection": OptionalNullTerminatedString,
bits: Bits,
const Bits = packed struct(u16) {
- status: enum(u1) { unresolved, resolved },
- /// Populated only if `bits.status == .resolved`.
+ status: enum(u2) { unresolved, type_resolved, fully_resolved, type_resolved_extern_decl },
+ /// Populated only if `bits.status != .unresolved`.
alignment: Alignment,
- /// Populated only if `bits.status == .resolved`.
+ /// Populated only if `bits.status != .unresolved`.
@"addrspace": std.builtin.AddressSpace,
- _: u3 = 0,
+ /// Populated only if `bits.status == .type_resolved`.
+ is_const: bool,
+ /// Populated only if `bits.status == .type_resolved`.
+ is_threadlocal: bool,
is_usingnamespace: bool,
};
@@ -597,8 +686,17 @@ pub const Nav = struct {
.is_usingnamespace = repr.bits.is_usingnamespace,
.status = switch (repr.bits.status) {
.unresolved => .unresolved,
- .resolved => .{ .resolved = .{
- .val = repr.val,
+ .type_resolved, .type_resolved_extern_decl => .{ .type_resolved = .{
+ .type = repr.type_or_val,
+ .alignment = repr.bits.alignment,
+ .@"linksection" = repr.@"linksection",
+ .@"addrspace" = repr.bits.@"addrspace",
+ .is_const = repr.bits.is_const,
+ .is_threadlocal = repr.bits.is_threadlocal,
+ .is_extern_decl = repr.bits.status == .type_resolved_extern_decl,
+ } },
+ .fully_resolved => .{ .fully_resolved = .{
+ .val = repr.type_or_val,
.alignment = repr.bits.alignment,
.@"linksection" = repr.@"linksection",
.@"addrspace" = repr.bits.@"addrspace",
@@ -616,13 +714,15 @@ pub const Nav = struct {
.fqn = nav.fqn,
.analysis_namespace = if (nav.analysis) |a| a.namespace.toOptional() else .none,
.analysis_zir_index = if (nav.analysis) |a| a.zir_index.toOptional() else .none,
- .val = switch (nav.status) {
+ .type_or_val = switch (nav.status) {
.unresolved => .none,
- .resolved => |r| r.val,
+ .type_resolved => |r| r.type,
+ .fully_resolved => |r| r.val,
},
.@"linksection" = switch (nav.status) {
.unresolved => .none,
- .resolved => |r| r.@"linksection",
+ .type_resolved => |r| r.@"linksection",
+ .fully_resolved => |r| r.@"linksection",
},
.bits = switch (nav.status) {
.unresolved => .{
@@ -630,12 +730,24 @@ pub const Nav = struct {
.alignment = .none,
.@"addrspace" = .generic,
.is_usingnamespace = nav.is_usingnamespace,
+ .is_const = false,
+ .is_threadlocal = false,
+ },
+ .type_resolved => |r| .{
+ .status = if (r.is_extern_decl) .type_resolved_extern_decl else .type_resolved,
+ .alignment = r.alignment,
+ .@"addrspace" = r.@"addrspace",
+ .is_usingnamespace = nav.is_usingnamespace,
+ .is_const = r.is_const,
+ .is_threadlocal = r.is_threadlocal,
},
- .resolved => |r| .{
- .status = .resolved,
+ .fully_resolved => |r| .{
+ .status = .fully_resolved,
.alignment = r.alignment,
.@"addrspace" = r.@"addrspace",
.is_usingnamespace = nav.is_usingnamespace,
+ .is_const = false,
+ .is_threadlocal = false,
},
},
};
@@ -646,6 +758,7 @@ pub const Dependee = union(enum) {
file: FileIndex,
src_hash: TrackedInst.Index,
nav_val: Nav.Index,
+ nav_ty: Nav.Index,
interned: Index,
namespace: TrackedInst.Index,
namespace_name: NamespaceNameKey,
@@ -695,6 +808,7 @@ pub fn dependencyIterator(ip: *const InternPool, dependee: Dependee) DependencyI
.file => |x| ip.file_deps.get(x),
.src_hash => |x| ip.src_hash_deps.get(x),
.nav_val => |x| ip.nav_val_deps.get(x),
+ .nav_ty => |x| ip.nav_ty_deps.get(x),
.interned => |x| ip.interned_deps.get(x),
.namespace => |x| ip.namespace_deps.get(x),
.namespace_name => |x| ip.namespace_name_deps.get(x),
@@ -732,6 +846,7 @@ pub fn addDependency(ip: *InternPool, gpa: Allocator, depender: AnalUnit, depend
.file => ip.file_deps,
.src_hash => ip.src_hash_deps,
.nav_val => ip.nav_val_deps,
+ .nav_ty => ip.nav_ty_deps,
.interned => ip.interned_deps,
.namespace => ip.namespace_deps,
.namespace_name => ip.namespace_name_deps,
@@ -2079,36 +2194,36 @@ pub const Key = union(enum) {
return @atomicLoad(FuncAnalysis, func.analysisPtr(ip), .unordered);
}
- pub fn setAnalysisState(func: Func, ip: *InternPool, state: FuncAnalysis.State) void {
+ pub fn setCallsOrAwaitsErrorableFn(func: Func, ip: *InternPool, value: bool) void {
const extra_mutex = &ip.getLocal(func.tid).mutate.extra.mutex;
extra_mutex.lock();
defer extra_mutex.unlock();
const analysis_ptr = func.analysisPtr(ip);
var analysis = analysis_ptr.*;
- analysis.state = state;
+ analysis.calls_or_awaits_errorable_fn = value;
@atomicStore(FuncAnalysis, analysis_ptr, analysis, .release);
}
- pub fn setCallsOrAwaitsErrorableFn(func: Func, ip: *InternPool, value: bool) void {
+ pub fn setBranchHint(func: Func, ip: *InternPool, hint: std.builtin.BranchHint) void {
const extra_mutex = &ip.getLocal(func.tid).mutate.extra.mutex;
extra_mutex.lock();
defer extra_mutex.unlock();
const analysis_ptr = func.analysisPtr(ip);
var analysis = analysis_ptr.*;
- analysis.calls_or_awaits_errorable_fn = value;
+ analysis.branch_hint = hint;
@atomicStore(FuncAnalysis, analysis_ptr, analysis, .release);
}
- pub fn setBranchHint(func: Func, ip: *InternPool, hint: std.builtin.BranchHint) void {
+ pub fn setAnalyzed(func: Func, ip: *InternPool) void {
const extra_mutex = &ip.getLocal(func.tid).mutate.extra.mutex;
extra_mutex.lock();
defer extra_mutex.unlock();
const analysis_ptr = func.analysisPtr(ip);
var analysis = analysis_ptr.*;
- analysis.branch_hint = hint;
+ analysis.is_analyzed = true;
@atomicStore(FuncAnalysis, analysis_ptr, analysis, .release);
}
@@ -5755,7 +5870,7 @@ pub const Tag = enum(u8) {
/// equality or hashing, except for `inferred_error_set` which is considered
/// to be part of the type of the function.
pub const FuncAnalysis = packed struct(u32) {
- state: State,
+ is_analyzed: bool,
branch_hint: std.builtin.BranchHint,
is_noinline: bool,
calls_or_awaits_errorable_fn: bool,
@@ -5763,20 +5878,7 @@ pub const FuncAnalysis = packed struct(u32) {
inferred_error_set: bool,
disable_instrumentation: bool,
- _: u23 = 0,
-
- pub const State = enum(u2) {
- /// The runtime function has never been referenced.
- /// As such, it has never been analyzed, nor is it queued for analysis.
- unreferenced,
- /// The runtime function has been referenced, but has not yet been analyzed.
- /// Its semantic analysis is queued.
- queued,
- /// The runtime function has been (or is currently being) semantically analyzed.
- /// To know if analysis succeeded, consult `zcu.[transitive_]failed_analysis`.
- /// To know if analysis is up-to-date, consult `zcu.[potentially_]outdated`.
- analyzed,
- };
+ _: u24 = 0,
};
pub const Bytes = struct {
@@ -6419,6 +6521,7 @@ pub fn deinit(ip: *InternPool, gpa: Allocator) void {
ip.file_deps.deinit(gpa);
ip.src_hash_deps.deinit(gpa);
ip.nav_val_deps.deinit(gpa);
+ ip.nav_ty_deps.deinit(gpa);
ip.interned_deps.deinit(gpa);
ip.namespace_deps.deinit(gpa);
ip.namespace_name_deps.deinit(gpa);
@@ -6875,8 +6978,8 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
.is_threadlocal = extra.flags.is_threadlocal,
.is_weak_linkage = extra.flags.is_weak_linkage,
.is_dll_import = extra.flags.is_dll_import,
- .alignment = nav.status.resolved.alignment,
- .@"addrspace" = nav.status.resolved.@"addrspace",
+ .alignment = nav.status.fully_resolved.alignment,
+ .@"addrspace" = nav.status.fully_resolved.@"addrspace",
.zir_index = extra.zir_index,
.owner_nav = extra.owner_nav,
} };
@@ -8794,7 +8897,7 @@ pub fn getFuncDecl(
const func_decl_extra_index = addExtraAssumeCapacity(extra, Tag.FuncDecl{
.analysis = .{
- .state = .unreferenced,
+ .is_analyzed = false,
.branch_hint = .none,
.is_noinline = key.is_noinline,
.calls_or_awaits_errorable_fn = false,
@@ -8903,7 +9006,7 @@ pub fn getFuncDeclIes(
const func_decl_extra_index = addExtraAssumeCapacity(extra, Tag.FuncDecl{
.analysis = .{
- .state = .unreferenced,
+ .is_analyzed = false,
.branch_hint = .none,
.is_noinline = key.is_noinline,
.calls_or_awaits_errorable_fn = false,
@@ -9099,7 +9202,7 @@ pub fn getFuncInstance(
const func_extra_index = addExtraAssumeCapacity(extra, Tag.FuncInstance{
.analysis = .{
- .state = .unreferenced,
+ .is_analyzed = false,
.branch_hint = .none,
.is_noinline = arg.is_noinline,
.calls_or_awaits_errorable_fn = false,
@@ -9197,7 +9300,7 @@ pub fn getFuncInstanceIes(
const func_extra_index = addExtraAssumeCapacity(extra, Tag.FuncInstance{
.analysis = .{
- .state = .unreferenced,
+ .is_analyzed = false,
.branch_hint = .none,
.is_noinline = arg.is_noinline,
.calls_or_awaits_errorable_fn = false,
@@ -9316,9 +9419,9 @@ fn finishFuncInstance(
.name = nav_name,
.fqn = try ip.namespacePtr(fn_namespace).internFullyQualifiedName(ip, gpa, tid, nav_name),
.val = func_index,
- .alignment = fn_owner_nav.status.resolved.alignment,
- .@"linksection" = fn_owner_nav.status.resolved.@"linksection",
- .@"addrspace" = fn_owner_nav.status.resolved.@"addrspace",
+ .alignment = fn_owner_nav.status.fully_resolved.alignment,
+ .@"linksection" = fn_owner_nav.status.fully_resolved.@"linksection",
+ .@"addrspace" = fn_owner_nav.status.fully_resolved.@"addrspace",
});
// Populate the owner_nav field which was left undefined until now.
@@ -11030,7 +11133,7 @@ pub fn createNav(
.name = opts.name,
.fqn = opts.fqn,
.analysis = null,
- .status = .{ .resolved = .{
+ .status = .{ .fully_resolved = .{
.val = opts.val,
.alignment = opts.alignment,
.@"linksection" = opts.@"linksection",
@@ -11077,6 +11180,50 @@ pub fn createDeclNav(
return nav;
}
+/// Resolve the type of a `Nav` with an analysis owner.
+/// If its status is already `resolved`, the old value is discarded.
+pub fn resolveNavType(
+ ip: *InternPool,
+ nav: Nav.Index,
+ resolved: struct {
+ type: InternPool.Index,
+ alignment: Alignment,
+ @"linksection": OptionalNullTerminatedString,
+ @"addrspace": std.builtin.AddressSpace,
+ is_const: bool,
+ is_threadlocal: bool,
+ is_extern_decl: bool,
+ },
+) void {
+ const unwrapped = nav.unwrap(ip);
+
+ const local = ip.getLocal(unwrapped.tid);
+ local.mutate.extra.mutex.lock();
+ defer local.mutate.extra.mutex.unlock();
+
+ const navs = local.shared.navs.view();
+
+ const nav_analysis_namespace = navs.items(.analysis_namespace);
+ const nav_analysis_zir_index = navs.items(.analysis_zir_index);
+ const nav_types = navs.items(.type_or_val);
+ const nav_linksections = navs.items(.@"linksection");
+ const nav_bits = navs.items(.bits);
+
+ assert(nav_analysis_namespace[unwrapped.index] != .none);
+ assert(nav_analysis_zir_index[unwrapped.index] != .none);
+
+ @atomicStore(InternPool.Index, &nav_types[unwrapped.index], resolved.type, .release);
+ @atomicStore(OptionalNullTerminatedString, &nav_linksections[unwrapped.index], resolved.@"linksection", .release);
+
+ var bits = nav_bits[unwrapped.index];
+ bits.status = if (resolved.is_extern_decl) .type_resolved_extern_decl else .type_resolved;
+ bits.alignment = resolved.alignment;
+ bits.@"addrspace" = resolved.@"addrspace";
+ bits.is_const = resolved.is_const;
+ bits.is_threadlocal = resolved.is_threadlocal;
+ @atomicStore(Nav.Repr.Bits, &nav_bits[unwrapped.index], bits, .release);
+}
+
/// Resolve the value of a `Nav` with an analysis owner.
/// If its status is already `resolved`, the old value is discarded.
pub fn resolveNavValue(
@@ -11099,7 +11246,7 @@ pub fn resolveNavValue(
const nav_analysis_namespace = navs.items(.analysis_namespace);
const nav_analysis_zir_index = navs.items(.analysis_zir_index);
- const nav_vals = navs.items(.val);
+ const nav_vals = navs.items(.type_or_val);
const nav_linksections = navs.items(.@"linksection");
const nav_bits = navs.items(.bits);
@@ -11110,7 +11257,7 @@ pub fn resolveNavValue(
@atomicStore(OptionalNullTerminatedString, &nav_linksections[unwrapped.index], resolved.@"linksection", .release);
var bits = nav_bits[unwrapped.index];
- bits.status = .resolved;
+ bits.status = .fully_resolved;
bits.alignment = resolved.alignment;
bits.@"addrspace" = resolved.@"addrspace";
@atomicStore(Nav.Repr.Bits, &nav_bits[unwrapped.index], bits, .release);
src/link.zig
@@ -692,7 +692,7 @@ pub const File = struct {
/// May be called before or after updateExports for any given Nav.
pub fn updateNav(base: *File, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) UpdateNavError!void {
const nav = pt.zcu.intern_pool.getNav(nav_index);
- assert(nav.status == .resolved);
+ assert(nav.status == .fully_resolved);
switch (base.tag) {
inline else => |tag| {
dev.check(tag.devFeature());
src/Sema.zig
@@ -6495,9 +6495,9 @@ pub fn analyzeExport(
if (options.linkage == .internal)
return;
- try sema.ensureNavResolved(src, orig_nav_index);
+ try sema.ensureNavResolved(src, orig_nav_index, .fully);
- const exported_nav_index = switch (ip.indexToKey(ip.getNav(orig_nav_index).status.resolved.val)) {
+ const exported_nav_index = switch (ip.indexToKey(ip.getNav(orig_nav_index).status.fully_resolved.val)) {
.variable => |v| v.owner_nav,
.@"extern" => |e| e.owner_nav,
.func => |f| f.owner_nav,
@@ -6520,7 +6520,7 @@ pub fn analyzeExport(
}
// TODO: some backends might support re-exporting extern decls
- if (exported_nav.isExtern(ip)) {
+ if (exported_nav.getExtern(ip) != null) {
return sema.fail(block, src, "export target cannot be extern", .{});
}
@@ -6542,6 +6542,7 @@ fn zirDisableInstrumentation(sema: *Sema) CompileError!void {
.func => |func| func,
.@"comptime",
.nav_val,
+ .nav_ty,
.type,
=> return, // does nothing outside a function
};
@@ -6854,8 +6855,8 @@ fn lookupInNamespace(
}
for (usingnamespaces.items) |sub_ns_nav| {
- try sema.ensureNavResolved(src, sub_ns_nav);
- const sub_ns_ty = Type.fromInterned(ip.getNav(sub_ns_nav).status.resolved.val);
+ try sema.ensureNavResolved(src, sub_ns_nav, .fully);
+ const sub_ns_ty = Type.fromInterned(ip.getNav(sub_ns_nav).status.fully_resolved.val);
const sub_ns = zcu.namespacePtr(sub_ns_ty.getNamespaceIndex(zcu));
try checked_namespaces.put(gpa, sub_ns, {});
}
@@ -6865,7 +6866,7 @@ fn lookupInNamespace(
ignore_self: {
const skip_nav = switch (sema.owner.unwrap()) {
.@"comptime", .type, .func => break :ignore_self,
- .nav_val => |nav| nav,
+ .nav_ty, .nav_val => |nav| nav,
};
var i: usize = 0;
while (i < candidates.items.len) {
@@ -7125,7 +7126,7 @@ fn zirCall(
const call_inst = try sema.analyzeCall(block, func, func_ty, callee_src, call_src, modifier, ensure_result_used, args_info, call_dbg_node, .call);
switch (sema.owner.unwrap()) {
- .@"comptime", .type, .nav_val => input_is_error = false,
+ .@"comptime", .type, .nav_ty, .nav_val => input_is_error = false,
.func => |owner_func| if (!zcu.intern_pool.funcAnalysisUnordered(owner_func).calls_or_awaits_errorable_fn) {
// No errorable fn actually called; we have no error return trace
input_is_error = false;
@@ -7686,12 +7687,13 @@ fn analyzeCall(
.ptr => |ptr| blk: {
switch (ptr.base_addr) {
.nav => |nav_index| if (ptr.byte_offset == 0) {
+ try sema.ensureNavResolved(call_src, nav_index, .fully);
const nav = ip.getNav(nav_index);
- if (nav.isExtern(ip))
+ if (nav.getExtern(ip) != null)
return sema.fail(block, call_src, "{s} call of extern function pointer", .{
if (is_comptime_call) "comptime" else "inline",
});
- break :blk nav.status.resolved.val;
+ break :blk nav.status.fully_resolved.val;
},
else => {},
}
@@ -8007,7 +8009,7 @@ fn analyzeCall(
if (call_dbg_node) |some| try sema.zirDbgStmt(block, some);
switch (sema.owner.unwrap()) {
- .@"comptime", .nav_val, .type => {},
+ .@"comptime", .nav_ty, .nav_val, .type => {},
.func => |owner_func| if (Type.fromInterned(func_ty_info.return_type).isError(zcu)) {
ip.funcSetCallsOrAwaitsErrorableFn(owner_func);
},
@@ -8046,7 +8048,10 @@ fn analyzeCall(
switch (zcu.intern_pool.indexToKey(func_val.toIntern())) {
.func => break :skip_safety,
.ptr => |ptr| if (ptr.byte_offset == 0) switch (ptr.base_addr) {
- .nav => |nav| if (!ip.getNav(nav).isExtern(ip)) break :skip_safety,
+ .nav => |nav| {
+ try sema.ensureNavResolved(call_src, nav, .fully);
+ if (ip.getNav(nav).getExtern(ip) == null) break :skip_safety;
+ },
else => {},
},
else => {},
@@ -8243,7 +8248,7 @@ fn instantiateGenericCall(
});
const generic_owner = switch (zcu.intern_pool.indexToKey(func_val.toIntern())) {
.func => func_val.toIntern(),
- .ptr => |ptr| ip.getNav(ptr.base_addr.nav).status.resolved.val,
+ .ptr => |ptr| ip.getNav(ptr.base_addr.nav).status.fully_resolved.val,
else => unreachable,
};
const generic_owner_func = zcu.intern_pool.indexToKey(generic_owner).func;
@@ -8471,7 +8476,7 @@ fn instantiateGenericCall(
if (call_dbg_node) |some| try sema.zirDbgStmt(block, some);
switch (sema.owner.unwrap()) {
- .@"comptime", .nav_val, .type => {},
+ .@"comptime", .nav_ty, .nav_val, .type => {},
.func => |owner_func| if (Type.fromInterned(func_ty_info.return_type).isError(zcu)) {
ip.funcSetCallsOrAwaitsErrorableFn(owner_func);
},
@@ -19311,8 +19316,8 @@ fn typeInfoNamespaceDecls(
if (zcu.analysis_in_progress.contains(.wrap(.{ .nav_val = nav }))) {
continue;
}
- try sema.ensureNavResolved(src, nav);
- const namespace_ty = Type.fromInterned(ip.getNav(nav).status.resolved.val);
+ try sema.ensureNavResolved(src, nav, .fully);
+ const namespace_ty = Type.fromInterned(ip.getNav(nav).status.fully_resolved.val);
try sema.typeInfoNamespaceDecls(block, src, namespace_ty.getNamespaceIndex(zcu).toOptional(), declaration_ty, decl_vals, seen_namespaces);
}
}
@@ -21602,7 +21607,7 @@ fn getErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref {
.func => |func| if (ip.funcAnalysisUnordered(func).calls_or_awaits_errorable_fn and block.ownerModule().error_tracing) {
return block.addTy(.err_return_trace, opt_ptr_stack_trace_ty);
},
- .@"comptime", .nav_val, .type => {},
+ .@"comptime", .nav_ty, .nav_val, .type => {},
}
return Air.internedToRef(try pt.intern(.{ .opt = .{
.ty = opt_ptr_stack_trace_ty.toIntern(),
@@ -27086,7 +27091,7 @@ fn zirBuiltinExtern(
.zir_index = switch (sema.owner.unwrap()) {
.@"comptime" => |cu| ip.getComptimeUnit(cu).zir_index,
.type => |owner_ty| Type.fromInterned(owner_ty).typeDeclInst(zcu).?,
- .nav_val => |nav| ip.getNav(nav).analysis.?.zir_index,
+ .nav_ty, .nav_val => |nav| ip.getNav(nav).analysis.?.zir_index,
.func => |func| zir_index: {
const func_info = zcu.funcInfo(func);
const owner_func_info = if (func_info.generic_owner != .none) owner: {
@@ -27741,7 +27746,7 @@ fn preparePanicId(sema: *Sema, block: *Block, src: LazySrcLoc, panic_id: Zcu.Pan
error.GenericPoison, error.ComptimeReturn, error.ComptimeBreak => unreachable,
error.OutOfMemory => |e| return e,
}).?;
- try sema.ensureNavResolved(src, msg_nav_index);
+ try sema.ensureNavResolved(src, msg_nav_index, .fully);
zcu.panic_messages[@intFromEnum(panic_id)] = msg_nav_index.toOptional();
return msg_nav_index;
}
@@ -32648,21 +32653,29 @@ fn addTypeReferenceEntry(
try zcu.addTypeReference(sema.owner, referenced_type, src);
}
-pub fn ensureNavResolved(sema: *Sema, src: LazySrcLoc, nav_index: InternPool.Nav.Index) CompileError!void {
+pub fn ensureNavResolved(sema: *Sema, src: LazySrcLoc, nav_index: InternPool.Nav.Index, kind: enum { type, fully }) CompileError!void {
const pt = sema.pt;
const zcu = pt.zcu;
const ip = &zcu.intern_pool;
const nav = ip.getNav(nav_index);
if (nav.analysis == null) {
- assert(nav.status == .resolved);
+ assert(nav.status == .fully_resolved);
return;
}
+ try sema.declareDependency(switch (kind) {
+ .type => .{ .nav_ty = nav_index },
+ .fully => .{ .nav_val = nav_index },
+ });
+
// Note that even if `nav.status == .resolved`, we must still trigger `ensureNavValUpToDate`
// to make sure the value is up-to-date on incremental updates.
- const anal_unit: AnalUnit = .wrap(.{ .nav_val = nav_index });
+ const anal_unit: AnalUnit = .wrap(switch (kind) {
+ .type => .{ .nav_ty = nav_index },
+ .fully => .{ .nav_val = nav_index },
+ });
try sema.addReferenceEntry(src, anal_unit);
if (zcu.analysis_in_progress.contains(anal_unit)) {
@@ -32672,7 +32685,13 @@ pub fn ensureNavResolved(sema: *Sema, src: LazySrcLoc, nav_index: InternPool.Nav
}, "dependency loop detected", .{}));
}
- return pt.ensureNavValUpToDate(nav_index);
+ switch (kind) {
+ .type => {
+ try zcu.ensureNavValAnalysisQueued(nav_index);
+ return pt.ensureNavTypeUpToDate(nav_index);
+ },
+ .fully => return pt.ensureNavValUpToDate(nav_index),
+ }
}
fn optRefValue(sema: *Sema, opt_val: ?Value) !Value {
@@ -32691,36 +32710,44 @@ fn analyzeNavRef(sema: *Sema, src: LazySrcLoc, nav_index: InternPool.Nav.Index)
return sema.analyzeNavRefInner(src, nav_index, true);
}
-/// Analyze a reference to the `Nav` at the given index. Ensures the underlying `Nav` is analyzed, but
-/// only triggers analysis for function bodies if `analyze_fn_body` is true. If it's possible for a
-/// decl_ref to end up in runtime code, the function body must be analyzed: `analyzeNavRef` wraps
-/// this function with `analyze_fn_body` set to true.
-fn analyzeNavRefInner(sema: *Sema, src: LazySrcLoc, orig_nav_index: InternPool.Nav.Index, analyze_fn_body: bool) CompileError!Air.Inst.Ref {
+/// Analyze a reference to the `Nav` at the given index. Ensures the underlying `Nav` is analyzed.
+/// If this pointer will be used directly, `is_ref` must be `true`.
+/// If this pointer will be immediately loaded (i.e. a `decl_val` instruction), `is_ref` must be `false`.
+fn analyzeNavRefInner(sema: *Sema, src: LazySrcLoc, orig_nav_index: InternPool.Nav.Index, is_ref: bool) CompileError!Air.Inst.Ref {
const pt = sema.pt;
const zcu = pt.zcu;
const ip = &zcu.intern_pool;
- // TODO: if this is a `decl_ref` of a non-variable Nav, only depend on Nav type
- try sema.declareDependency(.{ .nav_val = orig_nav_index });
- try sema.ensureNavResolved(src, orig_nav_index);
+ try sema.ensureNavResolved(src, orig_nav_index, if (is_ref) .type else .fully);
- const nav_val = zcu.navValue(orig_nav_index);
- const nav_index, const is_const = switch (ip.indexToKey(nav_val.toIntern())) {
- .variable => |v| .{ v.owner_nav, false },
- .func => |f| .{ f.owner_nav, true },
- .@"extern" => |e| .{ e.owner_nav, e.is_const },
- else => .{ orig_nav_index, true },
+ const nav_index = nav: {
+ if (ip.getNav(orig_nav_index).isExternOrFn(ip)) {
+ // Getting a pointer to this `Nav` might mean we actually get a pointer to something else!
+ // We need to resolve the value to know for sure.
+ if (is_ref) try sema.ensureNavResolved(src, orig_nav_index, .fully);
+ switch (ip.indexToKey(ip.getNav(orig_nav_index).status.fully_resolved.val)) {
+ .func => |f| break :nav f.owner_nav,
+ .@"extern" => |e| break :nav e.owner_nav,
+ else => {},
+ }
+ }
+ break :nav orig_nav_index;
+ };
+
+ const ty, const alignment, const @"addrspace", const is_const = switch (ip.getNav(nav_index).status) {
+ .unresolved => unreachable,
+ .type_resolved => |r| .{ r.type, r.alignment, r.@"addrspace", r.is_const },
+ .fully_resolved => |r| .{ ip.typeOf(r.val), r.alignment, r.@"addrspace", zcu.navValIsConst(r.val) },
};
- const nav_info = ip.getNav(nav_index).status.resolved;
const ptr_ty = try pt.ptrTypeSema(.{
- .child = nav_val.typeOf(zcu).toIntern(),
+ .child = ty,
.flags = .{
- .alignment = nav_info.alignment,
+ .alignment = alignment,
.is_const = is_const,
- .address_space = nav_info.@"addrspace",
+ .address_space = @"addrspace",
},
});
- if (analyze_fn_body) {
+ if (is_ref) {
try sema.maybeQueueFuncBodyAnalysis(src, nav_index);
}
return Air.internedToRef((try pt.intern(.{ .ptr = .{
@@ -32731,11 +32758,22 @@ fn analyzeNavRefInner(sema: *Sema, src: LazySrcLoc, orig_nav_index: InternPool.N
}
fn maybeQueueFuncBodyAnalysis(sema: *Sema, src: LazySrcLoc, nav_index: InternPool.Nav.Index) !void {
- const zcu = sema.pt.zcu;
+ const pt = sema.pt;
+ const zcu = pt.zcu;
const ip = &zcu.intern_pool;
+
+ // To avoid forcing too much resolution, let's first resolve the type, and check if it's a function.
+ // If it is, we can resolve the *value*, and queue analysis as needed.
+
+ try sema.ensureNavResolved(src, nav_index, .type);
+ const nav_ty: Type = .fromInterned(ip.getNav(nav_index).typeOf(ip));
+ if (nav_ty.zigTypeTag(zcu) != .@"fn") return;
+ if (!try nav_ty.fnHasRuntimeBitsSema(pt)) return;
+
+ try sema.ensureNavResolved(src, nav_index, .fully);
const nav_val = zcu.navValue(nav_index);
if (!ip.isFuncBody(nav_val.toIntern())) return;
- if (!try nav_val.typeOf(zcu).fnHasRuntimeBitsSema(sema.pt)) return;
+
try sema.addReferenceEntry(src, AnalUnit.wrap(.{ .func = nav_val.toIntern() }));
try zcu.ensureFuncBodyAnalysisQueued(nav_val.toIntern());
}
@@ -38450,11 +38488,16 @@ pub fn declareDependency(sema: *Sema, dependee: InternPool.Dependee) !void {
// of a type and they use `@This()`. This dependency would be unnecessary, and in fact would
// just result in over-analysis since `Zcu.findOutdatedToAnalyze` would never be able to resolve
// the loop.
+ // Note that this also disallows a `nav_val`
switch (sema.owner.unwrap()) {
.nav_val => |this_nav| switch (dependee) {
.nav_val => |other_nav| if (this_nav == other_nav) return,
else => {},
},
+ .nav_ty => |this_nav| switch (dependee) {
+ .nav_ty => |other_nav| if (this_nav == other_nav) return,
+ else => {},
+ },
else => {},
}
@@ -38873,8 +38916,8 @@ fn getBuiltinInnerType(
const nav = opt_nav orelse return sema.fail(block, src, "std.builtin.{s} missing {s}", .{
compile_error_parent_name, inner_name,
});
- try sema.ensureNavResolved(src, nav);
- const val = Value.fromInterned(ip.getNav(nav).status.resolved.val);
+ try sema.ensureNavResolved(src, nav, .fully);
+ const val = Value.fromInterned(ip.getNav(nav).status.fully_resolved.val);
const ty = val.toType();
try ty.resolveFully(pt);
return ty;
@@ -38886,5 +38929,73 @@ fn getBuiltin(sema: *Sema, name: []const u8) SemaError!Air.Inst.Ref {
const ip = &zcu.intern_pool;
const nav = try pt.getBuiltinNav(name);
try pt.ensureNavValUpToDate(nav);
- return Air.internedToRef(ip.getNav(nav).status.resolved.val);
+ return Air.internedToRef(ip.getNav(nav).status.fully_resolved.val);
+}
+
+pub const NavPtrModifiers = struct {
+ alignment: Alignment,
+ @"linksection": InternPool.OptionalNullTerminatedString,
+ @"addrspace": std.builtin.AddressSpace,
+};
+
+pub fn resolveNavPtrModifiers(
+ sema: *Sema,
+ block: *Block,
+ zir_decl: Zir.Inst.Declaration.Unwrapped,
+ decl_inst: Zir.Inst.Index,
+ nav_ty: Type,
+) CompileError!NavPtrModifiers {
+ const pt = sema.pt;
+ const zcu = pt.zcu;
+ const gpa = zcu.gpa;
+ const ip = &zcu.intern_pool;
+
+ const align_src = block.src(.{ .node_offset_var_decl_align = 0 });
+ const section_src = block.src(.{ .node_offset_var_decl_section = 0 });
+ const addrspace_src = block.src(.{ .node_offset_var_decl_addrspace = 0 });
+
+ const alignment: InternPool.Alignment = a: {
+ const align_body = zir_decl.align_body orelse break :a .none;
+ const align_ref = try sema.resolveInlineBody(block, align_body, decl_inst);
+ break :a try sema.analyzeAsAlign(block, align_src, align_ref);
+ };
+
+ const @"linksection": InternPool.OptionalNullTerminatedString = ls: {
+ const linksection_body = zir_decl.linksection_body orelse break :ls .none;
+ const linksection_ref = try sema.resolveInlineBody(block, linksection_body, decl_inst);
+ const bytes = try sema.toConstString(block, section_src, linksection_ref, .{
+ .needed_comptime_reason = "linksection must be comptime-known",
+ });
+ if (std.mem.indexOfScalar(u8, bytes, 0) != null) {
+ return sema.fail(block, section_src, "linksection cannot contain null bytes", .{});
+ } else if (bytes.len == 0) {
+ return sema.fail(block, section_src, "linksection cannot be empty", .{});
+ }
+ break :ls try ip.getOrPutStringOpt(gpa, pt.tid, bytes, .no_embedded_nulls);
+ };
+
+ const @"addrspace": std.builtin.AddressSpace = as: {
+ const addrspace_ctx: Sema.AddressSpaceContext = switch (zir_decl.kind) {
+ .@"var" => .variable,
+ else => switch (nav_ty.zigTypeTag(zcu)) {
+ .@"fn" => .function,
+ else => .constant,
+ },
+ };
+ const target = zcu.getTarget();
+ const addrspace_body = zir_decl.addrspace_body orelse break :as switch (addrspace_ctx) {
+ .function => target_util.defaultAddressSpace(target, .function),
+ .variable => target_util.defaultAddressSpace(target, .global_mutable),
+ .constant => target_util.defaultAddressSpace(target, .global_constant),
+ else => unreachable,
+ };
+ const addrspace_ref = try sema.resolveInlineBody(block, addrspace_body, decl_inst);
+ break :as try sema.analyzeAsAddressSpace(block, addrspace_src, addrspace_ref, addrspace_ctx);
+ };
+
+ return .{
+ .alignment = alignment,
+ .@"linksection" = @"linksection",
+ .@"addrspace" = @"addrspace",
+ };
}
src/Value.zig
@@ -1343,7 +1343,12 @@ pub fn isLazySize(val: Value, zcu: *Zcu) bool {
pub fn isPtrRuntimeValue(val: Value, zcu: *Zcu) bool {
const ip = &zcu.intern_pool;
const nav = ip.getBackingNav(val.toIntern()).unwrap() orelse return false;
- return switch (ip.indexToKey(ip.getNav(nav).status.resolved.val)) {
+ const nav_val = switch (ip.getNav(nav).status) {
+ .unresolved => unreachable,
+ .type_resolved => |r| return r.is_threadlocal,
+ .fully_resolved => |r| r.val,
+ };
+ return switch (ip.indexToKey(nav_val)) {
.@"extern" => |e| e.is_threadlocal or e.is_dll_import,
.variable => |v| v.is_threadlocal,
else => false,
src/Zcu.zig
@@ -170,6 +170,9 @@ outdated_ready: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .empty,
/// it as outdated.
retryable_failures: std.ArrayListUnmanaged(AnalUnit) = .empty,
+func_body_analysis_queued: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .empty,
+nav_val_analysis_queued: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void) = .empty,
+
/// These are the modules which we initially queue for analysis in `Compilation.update`.
/// `resolveReferences` will use these as the root of its reachability traversal.
analysis_roots: std.BoundedArray(*Package.Module, 3) = .{},
@@ -282,7 +285,11 @@ pub const Exported = union(enum) {
pub fn getAlign(exported: Exported, zcu: *Zcu) Alignment {
return switch (exported) {
- .nav => |nav| zcu.intern_pool.getNav(nav).status.resolved.alignment,
+ .nav => |nav| switch (zcu.intern_pool.getNav(nav).status) {
+ .unresolved => unreachable,
+ .type_resolved => |r| r.alignment,
+ .fully_resolved => |r| r.alignment,
+ },
.uav => .none,
};
}
@@ -2241,6 +2248,9 @@ pub fn deinit(zcu: *Zcu) void {
zcu.outdated_ready.deinit(gpa);
zcu.retryable_failures.deinit(gpa);
+ zcu.func_body_analysis_queued.deinit(gpa);
+ zcu.nav_val_analysis_queued.deinit(gpa);
+
zcu.test_functions.deinit(gpa);
for (zcu.global_assembly.values()) |s| {
@@ -2441,6 +2451,7 @@ pub fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void {
switch (depender.unwrap()) {
.@"comptime" => {},
.nav_val => |nav| try zcu.markPoDependeeUpToDate(.{ .nav_val = nav }),
+ .nav_ty => |nav| try zcu.markPoDependeeUpToDate(.{ .nav_ty = nav }),
.type => |ty| try zcu.markPoDependeeUpToDate(.{ .interned = ty }),
.func => |func| try zcu.markPoDependeeUpToDate(.{ .interned = func }),
}
@@ -2453,7 +2464,8 @@ fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUni
const ip = &zcu.intern_pool;
const dependee: InternPool.Dependee = switch (maybe_outdated.unwrap()) {
.@"comptime" => return, // analysis of a comptime decl can't outdate any dependencies
- .nav_val => |nav| .{ .nav_val = nav }, // TODO: also `nav_ref` deps when introduced
+ .nav_val => |nav| .{ .nav_val = nav },
+ .nav_ty => |nav| .{ .nav_ty = nav },
.type => |ty| .{ .interned = ty },
.func => |func_index| .{ .interned = func_index }, // IES
};
@@ -2540,6 +2552,7 @@ pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit {
.@"comptime" => continue, // a `comptime` block can't even be depended on so it is a terrible choice
.type => |ty| .{ .interned = ty },
.nav_val => |nav| .{ .nav_val = nav },
+ .nav_ty => |nav| .{ .nav_ty = nav },
});
while (it.next()) |_| n += 1;
@@ -2780,14 +2793,39 @@ pub fn ensureFuncBodyAnalysisQueued(zcu: *Zcu, func_index: InternPool.Index) !vo
const ip = &zcu.intern_pool;
const func = zcu.funcInfo(func_index);
- switch (func.analysisUnordered(ip).state) {
- .unreferenced => {}, // We're the first reference!
- .queued => return, // Analysis is already queued.
- .analyzed => return, // Analysis is complete; if it's out-of-date, it'll be re-analyzed later this update.
+ if (zcu.func_body_analysis_queued.contains(func_index)) return;
+
+ if (func.analysisUnordered(ip).is_analyzed) {
+ if (!zcu.outdated.contains(.wrap(.{ .func = func_index })) and
+ !zcu.potentially_outdated.contains(.wrap(.{ .func = func_index })))
+ {
+ // This function has been analyzed before and is definitely up-to-date.
+ return;
+ }
}
+ try zcu.func_body_analysis_queued.ensureUnusedCapacity(zcu.gpa, 1);
try zcu.comp.queueJob(.{ .analyze_func = func_index });
- func.setAnalysisState(ip, .queued);
+ zcu.func_body_analysis_queued.putAssumeCapacityNoClobber(func_index, {});
+}
+
+pub fn ensureNavValAnalysisQueued(zcu: *Zcu, nav_id: InternPool.Nav.Index) !void {
+ const ip = &zcu.intern_pool;
+
+ if (zcu.nav_val_analysis_queued.contains(nav_id)) return;
+
+ if (ip.getNav(nav_id).status == .fully_resolved) {
+ if (!zcu.outdated.contains(.wrap(.{ .nav_val = nav_id })) and
+ !zcu.potentially_outdated.contains(.wrap(.{ .nav_val = nav_id })))
+ {
+ // This `Nav` has been analyzed before and is definitely up-to-date.
+ return;
+ }
+ }
+
+ try zcu.nav_val_analysis_queued.ensureUnusedCapacity(zcu.gpa, 1);
+ try zcu.comp.queueJob(.{ .analyze_comptime_unit = .wrap(.{ .nav_val = nav_id }) });
+ zcu.nav_val_analysis_queued.putAssumeCapacityNoClobber(nav_id, {});
}
pub const ImportFileResult = struct {
@@ -3424,6 +3462,17 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv
const unit = kv.key;
try result.putNoClobber(gpa, unit, kv.value);
+ // `nav_val` and `nav_ty` reference each other *implicitly* to save memory.
+ queue_paired: {
+ const other: AnalUnit = .wrap(switch (unit.unwrap()) {
+ .nav_val => |n| .{ .nav_ty = n },
+ .nav_ty => |n| .{ .nav_val = n },
+ .@"comptime", .type, .func => break :queue_paired,
+ });
+ if (result.contains(other)) break :queue_paired;
+ try unit_queue.put(gpa, other, kv.value); // same reference location
+ }
+
log.debug("handle unit '{}'", .{zcu.fmtAnalUnit(unit)});
if (zcu.reference_table.get(unit)) |first_ref_idx| {
@@ -3513,7 +3562,7 @@ pub fn navSrcLine(zcu: *Zcu, nav_index: InternPool.Nav.Index) u32 {
}
pub fn navValue(zcu: *const Zcu, nav_index: InternPool.Nav.Index) Value {
- return Value.fromInterned(zcu.intern_pool.getNav(nav_index).status.resolved.val);
+ return Value.fromInterned(zcu.intern_pool.getNav(nav_index).status.fully_resolved.val);
}
pub fn navFileScopeIndex(zcu: *Zcu, nav: InternPool.Nav.Index) File.Index {
@@ -3547,6 +3596,7 @@ fn formatAnalUnit(data: struct { unit: AnalUnit, zcu: *Zcu }, comptime fmt: []co
}
},
.nav_val => |nav| return writer.print("nav_val('{}')", .{ip.getNav(nav).fqn.fmt(ip)}),
+ .nav_ty => |nav| return writer.print("nav_ty('{}')", .{ip.getNav(nav).fqn.fmt(ip)}),
.type => |ty| return writer.print("ty('{}')", .{Type.fromInterned(ty).containerTypeName(ip).fmt(ip)}),
.func => |func| {
const nav = zcu.funcInfo(func).owner_nav;
@@ -3572,7 +3622,11 @@ fn formatDependee(data: struct { dependee: InternPool.Dependee, zcu: *Zcu }, com
},
.nav_val => |nav| {
const fqn = ip.getNav(nav).fqn;
- return writer.print("nav('{}')", .{fqn.fmt(ip)});
+ return writer.print("nav_val('{}')", .{fqn.fmt(ip)});
+ },
+ .nav_ty => |nav| {
+ const fqn = ip.getNav(nav).fqn;
+ return writer.print("nav_ty('{}')", .{fqn.fmt(ip)});
},
.interned => |ip_index| switch (ip.indexToKey(ip_index)) {
.struct_type, .union_type, .enum_type => return writer.print("type('{}')", .{Type.fromInterned(ip_index).containerTypeName(ip).fmt(ip)}),
@@ -3749,3 +3803,12 @@ pub fn callconvSupported(zcu: *Zcu, cc: std.builtin.CallingConvention) union(enu
if (!backend_ok) return .{ .bad_backend = backend };
return .ok;
}
+
+/// Given that a `Nav` has value `val`, determine if a ref of that `Nav` gives a `const` pointer.
+pub fn navValIsConst(zcu: *const Zcu, val: InternPool.Index) bool {
+ return switch (zcu.intern_pool.indexToKey(val)) {
+ .variable => false,
+ .@"extern" => |e| e.is_const,
+ else => true,
+ };
+}
test/behavior/globals.zig
@@ -66,3 +66,99 @@ test "global loads can affect liveness" {
S.f();
try std.testing.expect(y.a == 1);
}
+
+test "global const can be self-referential" {
+ const S = struct {
+ self: *const @This(),
+ x: u32,
+
+ const foo: @This() = .{ .self = &foo, .x = 123 };
+ };
+
+ try std.testing.expect(S.foo.x == 123);
+ try std.testing.expect(S.foo.self.x == 123);
+ try std.testing.expect(S.foo.self.self.x == 123);
+ try std.testing.expect(S.foo.self == &S.foo);
+ try std.testing.expect(S.foo.self.self == &S.foo);
+}
+
+test "global var can be self-referential" {
+ const S = struct {
+ self: *@This(),
+ x: u32,
+
+ var foo: @This() = .{ .self = &foo, .x = undefined };
+ };
+
+ S.foo.x = 123;
+
+ try std.testing.expect(S.foo.x == 123);
+ try std.testing.expect(S.foo.self.x == 123);
+ try std.testing.expect(S.foo.self == &S.foo);
+
+ S.foo.self.x = 456;
+
+ try std.testing.expect(S.foo.x == 456);
+ try std.testing.expect(S.foo.self.x == 456);
+ try std.testing.expect(S.foo.self == &S.foo);
+
+ S.foo.self.self.x = 789;
+
+ try std.testing.expect(S.foo.x == 789);
+ try std.testing.expect(S.foo.self.x == 789);
+ try std.testing.expect(S.foo.self == &S.foo);
+}
+
+test "global const can be indirectly self-referential" {
+ const S = struct {
+ other: *const @This(),
+ x: u32,
+
+ const foo: @This() = .{ .other = &bar, .x = 123 };
+ const bar: @This() = .{ .other = &foo, .x = 456 };
+ };
+
+ try std.testing.expect(S.foo.x == 123);
+ try std.testing.expect(S.foo.other.x == 456);
+ try std.testing.expect(S.foo.other.other.x == 123);
+ try std.testing.expect(S.foo.other.other.other.x == 456);
+ try std.testing.expect(S.foo.other == &S.bar);
+ try std.testing.expect(S.foo.other.other == &S.foo);
+
+ try std.testing.expect(S.bar.x == 456);
+ try std.testing.expect(S.bar.other.x == 123);
+ try std.testing.expect(S.bar.other.other.x == 456);
+ try std.testing.expect(S.bar.other.other.other.x == 123);
+ try std.testing.expect(S.bar.other == &S.foo);
+ try std.testing.expect(S.bar.other.other == &S.bar);
+}
+
+test "global var can be indirectly self-referential" {
+ const S = struct {
+ other: *@This(),
+ x: u32,
+
+ var foo: @This() = .{ .other = &bar, .x = undefined };
+ var bar: @This() = .{ .other = &foo, .x = undefined };
+ };
+
+ S.foo.other.x = 123; // bar.x
+ S.foo.other.other.x = 456; // foo.x
+
+ try std.testing.expect(S.foo.x == 456);
+ try std.testing.expect(S.foo.other.x == 123);
+ try std.testing.expect(S.foo.other.other.x == 456);
+ try std.testing.expect(S.foo.other.other.other.x == 123);
+ try std.testing.expect(S.foo.other == &S.bar);
+ try std.testing.expect(S.foo.other.other == &S.foo);
+
+ S.bar.other.x = 111; // foo.x
+ S.bar.other.other.x = 222; // bar.x
+
+ try std.testing.expect(S.bar.x == 222);
+ try std.testing.expect(S.bar.other.x == 111);
+ try std.testing.expect(S.bar.other.other.x == 222);
+ try std.testing.expect(S.bar.other.other.other.x == 111);
+ try std.testing.expect(S.bar.other == &S.foo);
+ try std.testing.expect(S.bar.other.other == &S.bar);
+}
test/cases/compile_errors/self_reference_missing_const.zig
@@ -0,0 +1,11 @@
+const S = struct { self: *S, x: u32 };
+const s: S = .{ .self = &s, .x = 123 };
+
+comptime {
+ _ = s;
+}
+
+// error
+//
+// :2:18: error: expected type '*tmp.S', found '*const tmp.S'
+// :2:18: note: cast discards const qualifier