mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 05:44:20 +00:00
compiler: analyze type and value of global declaration separately
This commit separates semantic analysis of the annotated type vs value
of a global declaration, therefore allowing recursive and mutually
recursive values to be declared.
Every `Nav` which undergoes analysis now has *two* corresponding
`AnalUnit`s: `.{ .nav_val = n }` and `.{ .nav_ty = n }`. The `nav_val`
unit is responsible for *fully resolving* the `Nav`: determining its
value, linksection, addrspace, etc. The `nav_ty` unit, on the other
hand, resolves only the information necessary to construct a *pointer*
to the `Nav`: its type, addrspace, etc. (It does also analyze its
linksection, but that could be moved to `nav_val` I think; it doesn't
make any difference).
Analyzing a `nav_ty` for a declaration with no type annotation will just
mark a dependency on the `nav_val`, analyze it, and finish. Conversely,
analyzing a `nav_val` for a declaration *with* a type annotation will
first mark a dependency on the `nav_ty` and analyze it, using this as
the result type when evaluating the value body.
The `nav_val` and `nav_ty` units always have references to one another:
so, if a `Nav`'s type is referenced, its value implicitly is too, and
vice versa. However, these dependencies are trivial, so, to save memory,
are only known implicitly by logic in `resolveReferences`.
In general, analyzing ZIR `decl_val` will only analyze `nav_ty` of the
corresponding `Nav`. There are two exceptions to this. If the
declaration is an `extern` declaration, then we immediately ensure the
`Nav` value is resolved (which doesn't actually require any more
analysis, since such a declaration has no value body anyway).
Additionally, if the resolved type has type tag `.@"fn"`, we again
immediately resolve the `Nav` value. The latter restriction is in place
for two reasons:
* Functions are special, in that their externs are allowed to trivially
alias; i.e. with a declaration `extern fn foo(...)`, you can write
`const bar = foo;`. This is not allowed for non-function externs, and
it means that function types are the only place where it is possible
for a declaration `Nav` to have a `.@"extern"` value without actually
being declared `extern`. We need to identify this situation
immediately so that the `decl_ref` can create a pointer to the *real*
extern `Nav`, not this alias.
* In certain situations, such as taking a pointer to a `Nav`, Sema needs
to queue analysis of a runtime function if the value is a function. To
do this, the function value needs to be known, so we need to resolve
the value immediately upon `&foo` where `foo` is a function.
This restriction is simple to codify into the eventual language
specification, and doesn't limit the utility of this feature in
practice.
A consequence of this commit is that codegen and linking logic needs to
be more careful when looking at `Nav`s. In general:
* When `updateNav` or `updateFunc` is called, it is safe to assume that
the `Nav` being updated (the owner `Nav` for `updateFunc`) is fully
resolved.
* Any `Nav` whose value is/will be an `@"extern"` or a function is fully
resolved; see `Nav.getExtern` for a helper for a common case here.
* Any other `Nav` may only have its type resolved.
This didn't seem to be too tricky to satisfy in any of the existing
codegen/linker backends.
Resolves: #131
This commit is contained in:
parent
40aafcd6a8
commit
3afda4322c
22 changed files with 1039 additions and 416 deletions
|
|
@ -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| {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
.resolved => |r| .{
|
||||
.status = .resolved,
|
||||
.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,
|
||||
},
|
||||
.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,17 +2194,6 @@ pub const Key = union(enum) {
|
|||
return @atomicLoad(FuncAnalysis, func.analysisPtr(ip), .unordered);
|
||||
}
|
||||
|
||||
pub fn setAnalysisState(func: Func, ip: *InternPool, state: FuncAnalysis.State) 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;
|
||||
@atomicStore(FuncAnalysis, analysis_ptr, analysis, .release);
|
||||
}
|
||||
|
||||
pub fn setCallsOrAwaitsErrorableFn(func: Func, ip: *InternPool, value: bool) void {
|
||||
const extra_mutex = &ip.getLocal(func.tid).mutate.extra.mutex;
|
||||
extra_mutex.lock();
|
||||
|
|
@ -2112,6 +2216,17 @@ pub const Key = union(enum) {
|
|||
@atomicStore(FuncAnalysis, analysis_ptr, analysis, .release);
|
||||
}
|
||||
|
||||
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.is_analyzed = true;
|
||||
@atomicStore(FuncAnalysis, analysis_ptr, analysis, .release);
|
||||
}
|
||||
|
||||
/// Returns a pointer that becomes invalid after any additions to the `InternPool`.
|
||||
fn zirBodyInstPtr(func: Func, ip: *const InternPool) *TrackedInst.Index {
|
||||
const extra = ip.getLocalShared(func.tid).extra.acquire();
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
203
src/Sema.zig
203
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",
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
81
src/Zcu.zig
81
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,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,23 +1095,248 @@ 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,
|
||||
},
|
||||
.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",
|
||||
};
|
||||
},
|
||||
.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 };
|
||||
},
|
||||
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 {
|
||||
dev.check(.sema);
|
||||
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
const nav_ty: Type = .fromInterned(nav.typeOf(ip));
|
||||
|
||||
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);
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
11
test/cases/compile_errors/self_reference_missing_const.zig
Normal file
11
test/cases/compile_errors/self_reference_missing_const.zig
Normal file
|
|
@ -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
|
||||
Loading…
Add table
Reference in a new issue