Skip to content

Commit b7a6ba3

Browse files
committed
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: ziglang#131
1 parent 72f0aaa commit b7a6ba3

22 files changed

+1022
-410
lines changed

src/Compilation.zig

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2906,6 +2906,7 @@ const Header = extern struct {
29062906
file_deps_len: u32,
29072907
src_hash_deps_len: u32,
29082908
nav_val_deps_len: u32,
2909+
nav_ty_deps_len: u32,
29092910
namespace_deps_len: u32,
29102911
namespace_name_deps_len: u32,
29112912
first_dependency_len: u32,
@@ -2949,6 +2950,7 @@ pub fn saveState(comp: *Compilation) !void {
29492950
.file_deps_len = @intCast(ip.file_deps.count()),
29502951
.src_hash_deps_len = @intCast(ip.src_hash_deps.count()),
29512952
.nav_val_deps_len = @intCast(ip.nav_val_deps.count()),
2953+
.nav_ty_deps_len = @intCast(ip.nav_ty_deps.count()),
29522954
.namespace_deps_len = @intCast(ip.namespace_deps.count()),
29532955
.namespace_name_deps_len = @intCast(ip.namespace_name_deps.count()),
29542956
.first_dependency_len = @intCast(ip.first_dependency.count()),
@@ -2979,6 +2981,8 @@ pub fn saveState(comp: *Compilation) !void {
29792981
addBuf(&bufs, mem.sliceAsBytes(ip.src_hash_deps.values()));
29802982
addBuf(&bufs, mem.sliceAsBytes(ip.nav_val_deps.keys()));
29812983
addBuf(&bufs, mem.sliceAsBytes(ip.nav_val_deps.values()));
2984+
addBuf(&bufs, mem.sliceAsBytes(ip.nav_ty_deps.keys()));
2985+
addBuf(&bufs, mem.sliceAsBytes(ip.nav_ty_deps.values()));
29822986
addBuf(&bufs, mem.sliceAsBytes(ip.namespace_deps.keys()));
29832987
addBuf(&bufs, mem.sliceAsBytes(ip.namespace_deps.values()));
29842988
addBuf(&bufs, mem.sliceAsBytes(ip.namespace_name_deps.keys()));
@@ -3145,7 +3149,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
31453149

31463150
const file_index = switch (anal_unit.unwrap()) {
31473151
.@"comptime" => |cu| ip.getComptimeUnit(cu).zir_index.resolveFile(ip),
3148-
.nav_val => |nav| ip.getNav(nav).analysis.?.zir_index.resolveFile(ip),
3152+
.nav_val, .nav_ty => |nav| ip.getNav(nav).analysis.?.zir_index.resolveFile(ip),
31493153
.type => |ty| Type.fromInterned(ty).typeDeclInst(zcu).?.resolveFile(ip),
31503154
.func => |ip_index| zcu.funcInfo(ip_index).zir_body_inst.resolveFile(ip),
31513155
};
@@ -3380,7 +3384,7 @@ pub fn addModuleErrorMsg(
33803384
defer gpa.free(rt_file_path);
33813385
const name = switch (ref.referencer.unwrap()) {
33823386
.@"comptime" => "comptime",
3383-
.nav_val => |nav| ip.getNav(nav).name.toSlice(ip),
3387+
.nav_val, .nav_ty => |nav| ip.getNav(nav).name.toSlice(ip),
33843388
.type => |ty| Type.fromInterned(ty).containerTypeName(ip).toSlice(ip),
33853389
.func => |f| ip.getNav(zcu.funcInfo(f).owner_nav).name.toSlice(ip),
33863390
};
@@ -3647,6 +3651,7 @@ fn performAllTheWorkInner(
36473651
try comp.queueJob(switch (outdated.unwrap()) {
36483652
.func => |f| .{ .analyze_func = f },
36493653
.@"comptime",
3654+
.nav_ty,
36503655
.nav_val,
36513656
.type,
36523657
=> .{ .analyze_comptime_unit = outdated },
@@ -3679,7 +3684,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre
36793684
return;
36803685
}
36813686
}
3682-
assert(nav.status == .resolved);
3687+
assert(nav.status == .fully_resolved);
36833688
comp.dispatchCodegenTask(tid, .{ .codegen_nav = nav_index });
36843689
},
36853690
.codegen_func => |func| {
@@ -3709,6 +3714,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre
37093714

37103715
const maybe_err: Zcu.SemaError!void = switch (unit.unwrap()) {
37113716
.@"comptime" => |cu| pt.ensureComptimeUnitUpToDate(cu),
3717+
.nav_ty => |nav| pt.ensureNavTypeUpToDate(nav),
37123718
.nav_val => |nav| pt.ensureNavValUpToDate(nav),
37133719
.type => |ty| if (pt.ensureTypeUpToDate(ty)) |_| {} else |err| err,
37143720
.func => unreachable,
@@ -3734,7 +3740,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre
37343740
// Tests are always emitted in test binaries. The decl_refs are created by
37353741
// Zcu.populateTestFunctions, but this will not queue body analysis, so do
37363742
// that now.
3737-
try pt.zcu.ensureFuncBodyAnalysisQueued(ip.getNav(nav).status.resolved.val);
3743+
try pt.zcu.ensureFuncBodyAnalysisQueued(ip.getNav(nav).status.fully_resolved.val);
37383744
}
37393745
},
37403746
.resolve_type_fully => |ty| {

0 commit comments

Comments
 (0)