authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-12-23 20:39:19+00:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-12-24 02:18:41+00:00
log3afda4322c34dedc2319701fdfac3505c8d311e9
tree467873c408750cb4223f3ccf31775e42ec9fbd5c
parent40aafcd6a85d3c517f445f17149c17523c832420
signaturelock-open Commit is signed but in an unrecognized format.

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

22 files changed, 1033 insertions(+), 410 deletions(-)

src/Compilation.zig+10-4
......@@ -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| {
src/InternPool.zig+201-54
......@@ -34,6 +34,9 @@ src_hash_deps: std.AutoArrayHashMapUnmanaged(TrackedInst.Index, DepEntry.Index),
3434/// Dependencies on the value of a Nav.
3535/// Value is index into `dep_entries` of the first dependency on this Nav value.
3636nav_val_deps: std.AutoArrayHashMapUnmanaged(Nav.Index, DepEntry.Index),
37/// Dependencies on the type of a Nav.
38/// Value is index into `dep_entries` of the first dependency on this Nav value.
39nav_ty_deps: std.AutoArrayHashMapUnmanaged(Nav.Index, DepEntry.Index),
3740/// Dependencies on an interned value, either:
3841/// * a runtime function (invalidated when its IES changes)
3942/// * a container type requiring resolution (invalidated when the type must be recreated at a new index)
......@@ -80,6 +83,7 @@ pub const empty: InternPool = .{
8083 .file_deps = .empty,
8184 .src_hash_deps = .empty,
8285 .nav_val_deps = .empty,
86 .nav_ty_deps = .empty,
8387 .interned_deps = .empty,
8488 .namespace_deps = .empty,
8589 .namespace_name_deps = .empty,
......@@ -371,6 +375,7 @@ pub const AnalUnit = packed struct(u64) {
371375 pub const Kind = enum(u32) {
372376 @"comptime",
373377 nav_val,
378 nav_ty,
374379 type,
375380 func,
376381 };
......@@ -380,6 +385,8 @@ pub const AnalUnit = packed struct(u64) {
380385 @"comptime": ComptimeUnit.Id,
381386 /// This `AnalUnit` resolves the value of the given `Nav`.
382387 nav_val: Nav.Index,
388 /// This `AnalUnit` resolves the type of the given `Nav`.
389 nav_ty: Nav.Index,
383390 /// This `AnalUnit` resolves the given `struct`/`union`/`enum` type.
384391 /// Generated tag enums are never used here (they do not undergo type resolution).
385392 type: InternPool.Index,
......@@ -483,8 +490,20 @@ pub const Nav = struct {
483490 status: union(enum) {
484491 /// This `Nav` is pending semantic analysis.
485492 unresolved,
493 /// The type of this `Nav` is resolved; the value is queued for resolution.
494 type_resolved: struct {
495 type: InternPool.Index,
496 alignment: Alignment,
497 @"linksection": OptionalNullTerminatedString,
498 @"addrspace": std.builtin.AddressSpace,
499 is_const: bool,
500 is_threadlocal: bool,
501 /// This field is whether this `Nav` is a literal `extern` definition.
502 /// It does *not* tell you whether this might alias an extern fn (see #21027).
503 is_extern_decl: bool,
504 },
486505 /// The value of this `Nav` is resolved.
487 resolved: struct {
506 fully_resolved: struct {
488507 val: InternPool.Index,
489508 alignment: Alignment,
490509 @"linksection": OptionalNullTerminatedString,
......@@ -492,14 +511,81 @@ pub const Nav = struct {
492511 },
493512 },
494513
495 /// Asserts that `status == .resolved`.
514 /// Asserts that `status != .unresolved`.
496515 pub fn typeOf(nav: Nav, ip: *const InternPool) InternPool.Index {
497 return ip.typeOf(nav.status.resolved.val);
516 return switch (nav.status) {
517 .unresolved => unreachable,
518 .type_resolved => |r| r.type,
519 .fully_resolved => |r| ip.typeOf(r.val),
520 };
498521 }
499522
500 /// Asserts that `status == .resolved`.
501 pub fn isExtern(nav: Nav, ip: *const InternPool) bool {
502 return ip.indexToKey(nav.status.resolved.val) == .@"extern";
523 /// Always returns `null` for `status == .type_resolved`. This function is inteded
524 /// to be used by code generation, since semantic analysis will ensure that any `Nav`
525 /// which is potentially `extern` is fully resolved.
526 /// Asserts that `status != .unresolved`.
527 pub fn getExtern(nav: Nav, ip: *const InternPool) ?Key.Extern {
528 return switch (nav.status) {
529 .unresolved => unreachable,
530 .type_resolved => null,
531 .fully_resolved => |r| switch (ip.indexToKey(r.val)) {
532 .@"extern" => |e| e,
533 else => null,
534 },
535 };
536 }
537
538 /// Asserts that `status != .unresolved`.
539 pub fn getAddrspace(nav: Nav) std.builtin.AddressSpace {
540 return switch (nav.status) {
541 .unresolved => unreachable,
542 .type_resolved => |r| r.@"addrspace",
543 .fully_resolved => |r| r.@"addrspace",
544 };
545 }
546
547 /// Asserts that `status != .unresolved`.
548 pub fn getAlignment(nav: Nav) Alignment {
549 return switch (nav.status) {
550 .unresolved => unreachable,
551 .type_resolved => |r| r.alignment,
552 .fully_resolved => |r| r.alignment,
553 };
554 }
555
556 /// Asserts that `status != .unresolved`.
557 pub fn isThreadlocal(nav: Nav, ip: *const InternPool) bool {
558 return switch (nav.status) {
559 .unresolved => unreachable,
560 .type_resolved => |r| r.is_threadlocal,
561 .fully_resolved => |r| switch (ip.indexToKey(r.val)) {
562 .@"extern" => |e| e.is_threadlocal,
563 .variable => |v| v.is_threadlocal,
564 else => false,
565 },
566 };
567 }
568
569 /// If this returns `true`, then a pointer to this `Nav` might actually be encoded as a pointer
570 /// to some other `Nav` due to an extern definition or extern alias (see #21027).
571 /// This query is valid on `Nav`s for whom only the type is resolved.
572 /// Asserts that `status != .unresolved`.
573 pub fn isExternOrFn(nav: Nav, ip: *const InternPool) bool {
574 return switch (nav.status) {
575 .unresolved => unreachable,
576 .type_resolved => |r| {
577 if (r.is_extern_decl) return true;
578 const tag = ip.zigTypeTagOrPoison(r.type) catch unreachable;
579 if (tag == .@"fn") return true;
580 return false;
581 },
582 .fully_resolved => |r| {
583 if (ip.indexToKey(r.val) == .@"extern") return true;
584 const tag = ip.zigTypeTagOrPoison(ip.typeOf(r.val)) catch unreachable;
585 if (tag == .@"fn") return true;
586 return false;
587 },
588 };
503589 }
504590
505591 /// Get the ZIR instruction corresponding to this `Nav`, used to resolve source locations.
......@@ -509,7 +595,7 @@ pub const Nav = struct {
509595 return a.zir_index;
510596 }
511597 // A `Nav` which does not undergo analysis always has a resolved value.
512 return switch (ip.indexToKey(nav.status.resolved.val)) {
598 return switch (ip.indexToKey(nav.status.fully_resolved.val)) {
513599 .func => |func| {
514600 // Since `analysis` was not populated, this must be an instantiation.
515601 // Go up to the generic owner and consult *its* `analysis` field.
......@@ -567,19 +653,22 @@ pub const Nav = struct {
567653 // The following 1 fields are either both populated, or both `.none`.
568654 analysis_namespace: OptionalNamespaceIndex,
569655 analysis_zir_index: TrackedInst.Index.Optional,
570 /// Populated only if `bits.status == .resolved`.
571 val: InternPool.Index,
572 /// Populated only if `bits.status == .resolved`.
656 /// Populated only if `bits.status != .unresolved`.
657 type_or_val: InternPool.Index,
658 /// Populated only if `bits.status != .unresolved`.
573659 @"linksection": OptionalNullTerminatedString,
574660 bits: Bits,
575661
576662 const Bits = packed struct(u16) {
577 status: enum(u1) { unresolved, resolved },
578 /// Populated only if `bits.status == .resolved`.
663 status: enum(u2) { unresolved, type_resolved, fully_resolved, type_resolved_extern_decl },
664 /// Populated only if `bits.status != .unresolved`.
579665 alignment: Alignment,
580 /// Populated only if `bits.status == .resolved`.
666 /// Populated only if `bits.status != .unresolved`.
581667 @"addrspace": std.builtin.AddressSpace,
582 _: u3 = 0,
668 /// Populated only if `bits.status == .type_resolved`.
669 is_const: bool,
670 /// Populated only if `bits.status == .type_resolved`.
671 is_threadlocal: bool,
583672 is_usingnamespace: bool,
584673 };
585674
......@@ -597,8 +686,17 @@ pub const Nav = struct {
597686 .is_usingnamespace = repr.bits.is_usingnamespace,
598687 .status = switch (repr.bits.status) {
599688 .unresolved => .unresolved,
600 .resolved => .{ .resolved = .{
601 .val = repr.val,
689 .type_resolved, .type_resolved_extern_decl => .{ .type_resolved = .{
690 .type = repr.type_or_val,
691 .alignment = repr.bits.alignment,
692 .@"linksection" = repr.@"linksection",
693 .@"addrspace" = repr.bits.@"addrspace",
694 .is_const = repr.bits.is_const,
695 .is_threadlocal = repr.bits.is_threadlocal,
696 .is_extern_decl = repr.bits.status == .type_resolved_extern_decl,
697 } },
698 .fully_resolved => .{ .fully_resolved = .{
699 .val = repr.type_or_val,
602700 .alignment = repr.bits.alignment,
603701 .@"linksection" = repr.@"linksection",
604702 .@"addrspace" = repr.bits.@"addrspace",
......@@ -616,13 +714,15 @@ pub const Nav = struct {
616714 .fqn = nav.fqn,
617715 .analysis_namespace = if (nav.analysis) |a| a.namespace.toOptional() else .none,
618716 .analysis_zir_index = if (nav.analysis) |a| a.zir_index.toOptional() else .none,
619 .val = switch (nav.status) {
717 .type_or_val = switch (nav.status) {
620718 .unresolved => .none,
621 .resolved => |r| r.val,
719 .type_resolved => |r| r.type,
720 .fully_resolved => |r| r.val,
622721 },
623722 .@"linksection" = switch (nav.status) {
624723 .unresolved => .none,
625 .resolved => |r| r.@"linksection",
724 .type_resolved => |r| r.@"linksection",
725 .fully_resolved => |r| r.@"linksection",
626726 },
627727 .bits = switch (nav.status) {
628728 .unresolved => .{
......@@ -630,12 +730,24 @@ pub const Nav = struct {
630730 .alignment = .none,
631731 .@"addrspace" = .generic,
632732 .is_usingnamespace = nav.is_usingnamespace,
733 .is_const = false,
734 .is_threadlocal = false,
735 },
736 .type_resolved => |r| .{
737 .status = if (r.is_extern_decl) .type_resolved_extern_decl else .type_resolved,
738 .alignment = r.alignment,
739 .@"addrspace" = r.@"addrspace",
740 .is_usingnamespace = nav.is_usingnamespace,
741 .is_const = r.is_const,
742 .is_threadlocal = r.is_threadlocal,
633743 },
634 .resolved => |r| .{
635 .status = .resolved,
744 .fully_resolved => |r| .{
745 .status = .fully_resolved,
636746 .alignment = r.alignment,
637747 .@"addrspace" = r.@"addrspace",
638748 .is_usingnamespace = nav.is_usingnamespace,
749 .is_const = false,
750 .is_threadlocal = false,
639751 },
640752 },
641753 };
......@@ -646,6 +758,7 @@ pub const Dependee = union(enum) {
646758 file: FileIndex,
647759 src_hash: TrackedInst.Index,
648760 nav_val: Nav.Index,
761 nav_ty: Nav.Index,
649762 interned: Index,
650763 namespace: TrackedInst.Index,
651764 namespace_name: NamespaceNameKey,
......@@ -695,6 +808,7 @@ pub fn dependencyIterator(ip: *const InternPool, dependee: Dependee) DependencyI
695808 .file => |x| ip.file_deps.get(x),
696809 .src_hash => |x| ip.src_hash_deps.get(x),
697810 .nav_val => |x| ip.nav_val_deps.get(x),
811 .nav_ty => |x| ip.nav_ty_deps.get(x),
698812 .interned => |x| ip.interned_deps.get(x),
699813 .namespace => |x| ip.namespace_deps.get(x),
700814 .namespace_name => |x| ip.namespace_name_deps.get(x),
......@@ -732,6 +846,7 @@ pub fn addDependency(ip: *InternPool, gpa: Allocator, depender: AnalUnit, depend
732846 .file => ip.file_deps,
733847 .src_hash => ip.src_hash_deps,
734848 .nav_val => ip.nav_val_deps,
849 .nav_ty => ip.nav_ty_deps,
735850 .interned => ip.interned_deps,
736851 .namespace => ip.namespace_deps,
737852 .namespace_name => ip.namespace_name_deps,
......@@ -2079,36 +2194,36 @@ pub const Key = union(enum) {
20792194 return @atomicLoad(FuncAnalysis, func.analysisPtr(ip), .unordered);
20802195 }
20812196
2082 pub fn setAnalysisState(func: Func, ip: *InternPool, state: FuncAnalysis.State) void {
2197 pub fn setCallsOrAwaitsErrorableFn(func: Func, ip: *InternPool, value: bool) void {
20832198 const extra_mutex = &ip.getLocal(func.tid).mutate.extra.mutex;
20842199 extra_mutex.lock();
20852200 defer extra_mutex.unlock();
20862201
20872202 const analysis_ptr = func.analysisPtr(ip);
20882203 var analysis = analysis_ptr.*;
2089 analysis.state = state;
2204 analysis.calls_or_awaits_errorable_fn = value;
20902205 @atomicStore(FuncAnalysis, analysis_ptr, analysis, .release);
20912206 }
20922207
2093 pub fn setCallsOrAwaitsErrorableFn(func: Func, ip: *InternPool, value: bool) void {
2208 pub fn setBranchHint(func: Func, ip: *InternPool, hint: std.builtin.BranchHint) void {
20942209 const extra_mutex = &ip.getLocal(func.tid).mutate.extra.mutex;
20952210 extra_mutex.lock();
20962211 defer extra_mutex.unlock();
20972212
20982213 const analysis_ptr = func.analysisPtr(ip);
20992214 var analysis = analysis_ptr.*;
2100 analysis.calls_or_awaits_errorable_fn = value;
2215 analysis.branch_hint = hint;
21012216 @atomicStore(FuncAnalysis, analysis_ptr, analysis, .release);
21022217 }
21032218
2104 pub fn setBranchHint(func: Func, ip: *InternPool, hint: std.builtin.BranchHint) void {
2219 pub fn setAnalyzed(func: Func, ip: *InternPool) void {
21052220 const extra_mutex = &ip.getLocal(func.tid).mutate.extra.mutex;
21062221 extra_mutex.lock();
21072222 defer extra_mutex.unlock();
21082223
21092224 const analysis_ptr = func.analysisPtr(ip);
21102225 var analysis = analysis_ptr.*;
2111 analysis.branch_hint = hint;
2226 analysis.is_analyzed = true;
21122227 @atomicStore(FuncAnalysis, analysis_ptr, analysis, .release);
21132228 }
21142229
......@@ -5755,7 +5870,7 @@ pub const Tag = enum(u8) {
57555870/// equality or hashing, except for `inferred_error_set` which is considered
57565871/// to be part of the type of the function.
57575872pub const FuncAnalysis = packed struct(u32) {
5758 state: State,
5873 is_analyzed: bool,
57595874 branch_hint: std.builtin.BranchHint,
57605875 is_noinline: bool,
57615876 calls_or_awaits_errorable_fn: bool,
......@@ -5763,20 +5878,7 @@ pub const FuncAnalysis = packed struct(u32) {
57635878 inferred_error_set: bool,
57645879 disable_instrumentation: bool,
57655880
5766 _: u23 = 0,
5767
5768 pub const State = enum(u2) {
5769 /// The runtime function has never been referenced.
5770 /// As such, it has never been analyzed, nor is it queued for analysis.
5771 unreferenced,
5772 /// The runtime function has been referenced, but has not yet been analyzed.
5773 /// Its semantic analysis is queued.
5774 queued,
5775 /// The runtime function has been (or is currently being) semantically analyzed.
5776 /// To know if analysis succeeded, consult `zcu.[transitive_]failed_analysis`.
5777 /// To know if analysis is up-to-date, consult `zcu.[potentially_]outdated`.
5778 analyzed,
5779 };
5881 _: u24 = 0,
57805882};
57815883
57825884pub const Bytes = struct {
......@@ -6419,6 +6521,7 @@ pub fn deinit(ip: *InternPool, gpa: Allocator) void {
64196521 ip.file_deps.deinit(gpa);
64206522 ip.src_hash_deps.deinit(gpa);
64216523 ip.nav_val_deps.deinit(gpa);
6524 ip.nav_ty_deps.deinit(gpa);
64226525 ip.interned_deps.deinit(gpa);
64236526 ip.namespace_deps.deinit(gpa);
64246527 ip.namespace_name_deps.deinit(gpa);
......@@ -6875,8 +6978,8 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
68756978 .is_threadlocal = extra.flags.is_threadlocal,
68766979 .is_weak_linkage = extra.flags.is_weak_linkage,
68776980 .is_dll_import = extra.flags.is_dll_import,
6878 .alignment = nav.status.resolved.alignment,
6879 .@"addrspace" = nav.status.resolved.@"addrspace",
6981 .alignment = nav.status.fully_resolved.alignment,
6982 .@"addrspace" = nav.status.fully_resolved.@"addrspace",
68806983 .zir_index = extra.zir_index,
68816984 .owner_nav = extra.owner_nav,
68826985 } };
......@@ -8794,7 +8897,7 @@ pub fn getFuncDecl(
87948897
87958898 const func_decl_extra_index = addExtraAssumeCapacity(extra, Tag.FuncDecl{
87968899 .analysis = .{
8797 .state = .unreferenced,
8900 .is_analyzed = false,
87988901 .branch_hint = .none,
87998902 .is_noinline = key.is_noinline,
88008903 .calls_or_awaits_errorable_fn = false,
......@@ -8903,7 +9006,7 @@ pub fn getFuncDeclIes(
89039006
89049007 const func_decl_extra_index = addExtraAssumeCapacity(extra, Tag.FuncDecl{
89059008 .analysis = .{
8906 .state = .unreferenced,
9009 .is_analyzed = false,
89079010 .branch_hint = .none,
89089011 .is_noinline = key.is_noinline,
89099012 .calls_or_awaits_errorable_fn = false,
......@@ -9099,7 +9202,7 @@ pub fn getFuncInstance(
90999202
91009203 const func_extra_index = addExtraAssumeCapacity(extra, Tag.FuncInstance{
91019204 .analysis = .{
9102 .state = .unreferenced,
9205 .is_analyzed = false,
91039206 .branch_hint = .none,
91049207 .is_noinline = arg.is_noinline,
91059208 .calls_or_awaits_errorable_fn = false,
......@@ -9197,7 +9300,7 @@ pub fn getFuncInstanceIes(
91979300
91989301 const func_extra_index = addExtraAssumeCapacity(extra, Tag.FuncInstance{
91999302 .analysis = .{
9200 .state = .unreferenced,
9303 .is_analyzed = false,
92019304 .branch_hint = .none,
92029305 .is_noinline = arg.is_noinline,
92039306 .calls_or_awaits_errorable_fn = false,
......@@ -9316,9 +9419,9 @@ fn finishFuncInstance(
93169419 .name = nav_name,
93179420 .fqn = try ip.namespacePtr(fn_namespace).internFullyQualifiedName(ip, gpa, tid, nav_name),
93189421 .val = func_index,
9319 .alignment = fn_owner_nav.status.resolved.alignment,
9320 .@"linksection" = fn_owner_nav.status.resolved.@"linksection",
9321 .@"addrspace" = fn_owner_nav.status.resolved.@"addrspace",
9422 .alignment = fn_owner_nav.status.fully_resolved.alignment,
9423 .@"linksection" = fn_owner_nav.status.fully_resolved.@"linksection",
9424 .@"addrspace" = fn_owner_nav.status.fully_resolved.@"addrspace",
93229425 });
93239426
93249427 // Populate the owner_nav field which was left undefined until now.
......@@ -11030,7 +11133,7 @@ pub fn createNav(
1103011133 .name = opts.name,
1103111134 .fqn = opts.fqn,
1103211135 .analysis = null,
11033 .status = .{ .resolved = .{
11136 .status = .{ .fully_resolved = .{
1103411137 .val = opts.val,
1103511138 .alignment = opts.alignment,
1103611139 .@"linksection" = opts.@"linksection",
......@@ -11077,6 +11180,50 @@ pub fn createDeclNav(
1107711180 return nav;
1107811181}
1107911182
11183/// Resolve the type of a `Nav` with an analysis owner.
11184/// If its status is already `resolved`, the old value is discarded.
11185pub fn resolveNavType(
11186 ip: *InternPool,
11187 nav: Nav.Index,
11188 resolved: struct {
11189 type: InternPool.Index,
11190 alignment: Alignment,
11191 @"linksection": OptionalNullTerminatedString,
11192 @"addrspace": std.builtin.AddressSpace,
11193 is_const: bool,
11194 is_threadlocal: bool,
11195 is_extern_decl: bool,
11196 },
11197) void {
11198 const unwrapped = nav.unwrap(ip);
11199
11200 const local = ip.getLocal(unwrapped.tid);
11201 local.mutate.extra.mutex.lock();
11202 defer local.mutate.extra.mutex.unlock();
11203
11204 const navs = local.shared.navs.view();
11205
11206 const nav_analysis_namespace = navs.items(.analysis_namespace);
11207 const nav_analysis_zir_index = navs.items(.analysis_zir_index);
11208 const nav_types = navs.items(.type_or_val);
11209 const nav_linksections = navs.items(.@"linksection");
11210 const nav_bits = navs.items(.bits);
11211
11212 assert(nav_analysis_namespace[unwrapped.index] != .none);
11213 assert(nav_analysis_zir_index[unwrapped.index] != .none);
11214
11215 @atomicStore(InternPool.Index, &nav_types[unwrapped.index], resolved.type, .release);
11216 @atomicStore(OptionalNullTerminatedString, &nav_linksections[unwrapped.index], resolved.@"linksection", .release);
11217
11218 var bits = nav_bits[unwrapped.index];
11219 bits.status = if (resolved.is_extern_decl) .type_resolved_extern_decl else .type_resolved;
11220 bits.alignment = resolved.alignment;
11221 bits.@"addrspace" = resolved.@"addrspace";
11222 bits.is_const = resolved.is_const;
11223 bits.is_threadlocal = resolved.is_threadlocal;
11224 @atomicStore(Nav.Repr.Bits, &nav_bits[unwrapped.index], bits, .release);
11225}
11226
1108011227/// Resolve the value of a `Nav` with an analysis owner.
1108111228/// If its status is already `resolved`, the old value is discarded.
1108211229pub fn resolveNavValue(
......@@ -11099,7 +11246,7 @@ pub fn resolveNavValue(
1109911246
1110011247 const nav_analysis_namespace = navs.items(.analysis_namespace);
1110111248 const nav_analysis_zir_index = navs.items(.analysis_zir_index);
11102 const nav_vals = navs.items(.val);
11249 const nav_vals = navs.items(.type_or_val);
1110311250 const nav_linksections = navs.items(.@"linksection");
1110411251 const nav_bits = navs.items(.bits);
1110511252
......@@ -11110,7 +11257,7 @@ pub fn resolveNavValue(
1111011257 @atomicStore(OptionalNullTerminatedString, &nav_linksections[unwrapped.index], resolved.@"linksection", .release);
1111111258
1111211259 var bits = nav_bits[unwrapped.index];
11113 bits.status = .resolved;
11260 bits.status = .fully_resolved;
1111411261 bits.alignment = resolved.alignment;
1111511262 bits.@"addrspace" = resolved.@"addrspace";
1111611263 @atomicStore(Nav.Repr.Bits, &nav_bits[unwrapped.index], bits, .release);
src/Sema.zig+157-46
......@@ -6495,9 +6495,9 @@ pub fn analyzeExport(
64956495 if (options.linkage == .internal)
64966496 return;
64976497
6498 try sema.ensureNavResolved(src, orig_nav_index);
6498 try sema.ensureNavResolved(src, orig_nav_index, .fully);
64996499
6500 const exported_nav_index = switch (ip.indexToKey(ip.getNav(orig_nav_index).status.resolved.val)) {
6500 const exported_nav_index = switch (ip.indexToKey(ip.getNav(orig_nav_index).status.fully_resolved.val)) {
65016501 .variable => |v| v.owner_nav,
65026502 .@"extern" => |e| e.owner_nav,
65036503 .func => |f| f.owner_nav,
......@@ -6520,7 +6520,7 @@ pub fn analyzeExport(
65206520 }
65216521
65226522 // TODO: some backends might support re-exporting extern decls
6523 if (exported_nav.isExtern(ip)) {
6523 if (exported_nav.getExtern(ip) != null) {
65246524 return sema.fail(block, src, "export target cannot be extern", .{});
65256525 }
65266526
......@@ -6542,6 +6542,7 @@ fn zirDisableInstrumentation(sema: *Sema) CompileError!void {
65426542 .func => |func| func,
65436543 .@"comptime",
65446544 .nav_val,
6545 .nav_ty,
65456546 .type,
65466547 => return, // does nothing outside a function
65476548 };
......@@ -6854,8 +6855,8 @@ fn lookupInNamespace(
68546855 }
68556856
68566857 for (usingnamespaces.items) |sub_ns_nav| {
6857 try sema.ensureNavResolved(src, sub_ns_nav);
6858 const sub_ns_ty = Type.fromInterned(ip.getNav(sub_ns_nav).status.resolved.val);
6858 try sema.ensureNavResolved(src, sub_ns_nav, .fully);
6859 const sub_ns_ty = Type.fromInterned(ip.getNav(sub_ns_nav).status.fully_resolved.val);
68596860 const sub_ns = zcu.namespacePtr(sub_ns_ty.getNamespaceIndex(zcu));
68606861 try checked_namespaces.put(gpa, sub_ns, {});
68616862 }
......@@ -6865,7 +6866,7 @@ fn lookupInNamespace(
68656866 ignore_self: {
68666867 const skip_nav = switch (sema.owner.unwrap()) {
68676868 .@"comptime", .type, .func => break :ignore_self,
6868 .nav_val => |nav| nav,
6869 .nav_ty, .nav_val => |nav| nav,
68696870 };
68706871 var i: usize = 0;
68716872 while (i < candidates.items.len) {
......@@ -7125,7 +7126,7 @@ fn zirCall(
71257126 const call_inst = try sema.analyzeCall(block, func, func_ty, callee_src, call_src, modifier, ensure_result_used, args_info, call_dbg_node, .call);
71267127
71277128 switch (sema.owner.unwrap()) {
7128 .@"comptime", .type, .nav_val => input_is_error = false,
7129 .@"comptime", .type, .nav_ty, .nav_val => input_is_error = false,
71297130 .func => |owner_func| if (!zcu.intern_pool.funcAnalysisUnordered(owner_func).calls_or_awaits_errorable_fn) {
71307131 // No errorable fn actually called; we have no error return trace
71317132 input_is_error = false;
......@@ -7686,12 +7687,13 @@ fn analyzeCall(
76867687 .ptr => |ptr| blk: {
76877688 switch (ptr.base_addr) {
76887689 .nav => |nav_index| if (ptr.byte_offset == 0) {
7690 try sema.ensureNavResolved(call_src, nav_index, .fully);
76897691 const nav = ip.getNav(nav_index);
7690 if (nav.isExtern(ip))
7692 if (nav.getExtern(ip) != null)
76917693 return sema.fail(block, call_src, "{s} call of extern function pointer", .{
76927694 if (is_comptime_call) "comptime" else "inline",
76937695 });
7694 break :blk nav.status.resolved.val;
7696 break :blk nav.status.fully_resolved.val;
76957697 },
76967698 else => {},
76977699 }
......@@ -8007,7 +8009,7 @@ fn analyzeCall(
80078009 if (call_dbg_node) |some| try sema.zirDbgStmt(block, some);
80088010
80098011 switch (sema.owner.unwrap()) {
8010 .@"comptime", .nav_val, .type => {},
8012 .@"comptime", .nav_ty, .nav_val, .type => {},
80118013 .func => |owner_func| if (Type.fromInterned(func_ty_info.return_type).isError(zcu)) {
80128014 ip.funcSetCallsOrAwaitsErrorableFn(owner_func);
80138015 },
......@@ -8046,7 +8048,10 @@ fn analyzeCall(
80468048 switch (zcu.intern_pool.indexToKey(func_val.toIntern())) {
80478049 .func => break :skip_safety,
80488050 .ptr => |ptr| if (ptr.byte_offset == 0) switch (ptr.base_addr) {
8049 .nav => |nav| if (!ip.getNav(nav).isExtern(ip)) break :skip_safety,
8051 .nav => |nav| {
8052 try sema.ensureNavResolved(call_src, nav, .fully);
8053 if (ip.getNav(nav).getExtern(ip) == null) break :skip_safety;
8054 },
80508055 else => {},
80518056 },
80528057 else => {},
......@@ -8243,7 +8248,7 @@ fn instantiateGenericCall(
82438248 });
82448249 const generic_owner = switch (zcu.intern_pool.indexToKey(func_val.toIntern())) {
82458250 .func => func_val.toIntern(),
8246 .ptr => |ptr| ip.getNav(ptr.base_addr.nav).status.resolved.val,
8251 .ptr => |ptr| ip.getNav(ptr.base_addr.nav).status.fully_resolved.val,
82478252 else => unreachable,
82488253 };
82498254 const generic_owner_func = zcu.intern_pool.indexToKey(generic_owner).func;
......@@ -8471,7 +8476,7 @@ fn instantiateGenericCall(
84718476 if (call_dbg_node) |some| try sema.zirDbgStmt(block, some);
84728477
84738478 switch (sema.owner.unwrap()) {
8474 .@"comptime", .nav_val, .type => {},
8479 .@"comptime", .nav_ty, .nav_val, .type => {},
84758480 .func => |owner_func| if (Type.fromInterned(func_ty_info.return_type).isError(zcu)) {
84768481 ip.funcSetCallsOrAwaitsErrorableFn(owner_func);
84778482 },
......@@ -19311,8 +19316,8 @@ fn typeInfoNamespaceDecls(
1931119316 if (zcu.analysis_in_progress.contains(.wrap(.{ .nav_val = nav }))) {
1931219317 continue;
1931319318 }
19314 try sema.ensureNavResolved(src, nav);
19315 const namespace_ty = Type.fromInterned(ip.getNav(nav).status.resolved.val);
19319 try sema.ensureNavResolved(src, nav, .fully);
19320 const namespace_ty = Type.fromInterned(ip.getNav(nav).status.fully_resolved.val);
1931619321 try sema.typeInfoNamespaceDecls(block, src, namespace_ty.getNamespaceIndex(zcu).toOptional(), declaration_ty, decl_vals, seen_namespaces);
1931719322 }
1931819323}
......@@ -21602,7 +21607,7 @@ fn getErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref {
2160221607 .func => |func| if (ip.funcAnalysisUnordered(func).calls_or_awaits_errorable_fn and block.ownerModule().error_tracing) {
2160321608 return block.addTy(.err_return_trace, opt_ptr_stack_trace_ty);
2160421609 },
21605 .@"comptime", .nav_val, .type => {},
21610 .@"comptime", .nav_ty, .nav_val, .type => {},
2160621611 }
2160721612 return Air.internedToRef(try pt.intern(.{ .opt = .{
2160821613 .ty = opt_ptr_stack_trace_ty.toIntern(),
......@@ -27086,7 +27091,7 @@ fn zirBuiltinExtern(
2708627091 .zir_index = switch (sema.owner.unwrap()) {
2708727092 .@"comptime" => |cu| ip.getComptimeUnit(cu).zir_index,
2708827093 .type => |owner_ty| Type.fromInterned(owner_ty).typeDeclInst(zcu).?,
27089 .nav_val => |nav| ip.getNav(nav).analysis.?.zir_index,
27094 .nav_ty, .nav_val => |nav| ip.getNav(nav).analysis.?.zir_index,
2709027095 .func => |func| zir_index: {
2709127096 const func_info = zcu.funcInfo(func);
2709227097 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
2774127746 error.GenericPoison, error.ComptimeReturn, error.ComptimeBreak => unreachable,
2774227747 error.OutOfMemory => |e| return e,
2774327748 }).?;
27744 try sema.ensureNavResolved(src, msg_nav_index);
27749 try sema.ensureNavResolved(src, msg_nav_index, .fully);
2774527750 zcu.panic_messages[@intFromEnum(panic_id)] = msg_nav_index.toOptional();
2774627751 return msg_nav_index;
2774727752}
......@@ -32648,21 +32653,29 @@ fn addTypeReferenceEntry(
3264832653 try zcu.addTypeReference(sema.owner, referenced_type, src);
3264932654}
3265032655
32651pub fn ensureNavResolved(sema: *Sema, src: LazySrcLoc, nav_index: InternPool.Nav.Index) CompileError!void {
32656pub fn ensureNavResolved(sema: *Sema, src: LazySrcLoc, nav_index: InternPool.Nav.Index, kind: enum { type, fully }) CompileError!void {
3265232657 const pt = sema.pt;
3265332658 const zcu = pt.zcu;
3265432659 const ip = &zcu.intern_pool;
3265532660
3265632661 const nav = ip.getNav(nav_index);
3265732662 if (nav.analysis == null) {
32658 assert(nav.status == .resolved);
32663 assert(nav.status == .fully_resolved);
3265932664 return;
3266032665 }
3266132666
32667 try sema.declareDependency(switch (kind) {
32668 .type => .{ .nav_ty = nav_index },
32669 .fully => .{ .nav_val = nav_index },
32670 });
32671
3266232672 // Note that even if `nav.status == .resolved`, we must still trigger `ensureNavValUpToDate`
3266332673 // to make sure the value is up-to-date on incremental updates.
3266432674
32665 const anal_unit: AnalUnit = .wrap(.{ .nav_val = nav_index });
32675 const anal_unit: AnalUnit = .wrap(switch (kind) {
32676 .type => .{ .nav_ty = nav_index },
32677 .fully => .{ .nav_val = nav_index },
32678 });
3266632679 try sema.addReferenceEntry(src, anal_unit);
3266732680
3266832681 if (zcu.analysis_in_progress.contains(anal_unit)) {
......@@ -32672,7 +32685,13 @@ pub fn ensureNavResolved(sema: *Sema, src: LazySrcLoc, nav_index: InternPool.Nav
3267232685 }, "dependency loop detected", .{}));
3267332686 }
3267432687
32675 return pt.ensureNavValUpToDate(nav_index);
32688 switch (kind) {
32689 .type => {
32690 try zcu.ensureNavValAnalysisQueued(nav_index);
32691 return pt.ensureNavTypeUpToDate(nav_index);
32692 },
32693 .fully => return pt.ensureNavValUpToDate(nav_index),
32694 }
3267632695}
3267732696
3267832697fn optRefValue(sema: *Sema, opt_val: ?Value) !Value {
......@@ -32691,36 +32710,44 @@ fn analyzeNavRef(sema: *Sema, src: LazySrcLoc, nav_index: InternPool.Nav.Index)
3269132710 return sema.analyzeNavRefInner(src, nav_index, true);
3269232711}
3269332712
32694/// Analyze a reference to the `Nav` at the given index. Ensures the underlying `Nav` is analyzed, but
32695/// only triggers analysis for function bodies if `analyze_fn_body` is true. If it's possible for a
32696/// decl_ref to end up in runtime code, the function body must be analyzed: `analyzeNavRef` wraps
32697/// this function with `analyze_fn_body` set to true.
32698fn analyzeNavRefInner(sema: *Sema, src: LazySrcLoc, orig_nav_index: InternPool.Nav.Index, analyze_fn_body: bool) CompileError!Air.Inst.Ref {
32713/// Analyze a reference to the `Nav` at the given index. Ensures the underlying `Nav` is analyzed.
32714/// If this pointer will be used directly, `is_ref` must be `true`.
32715/// If this pointer will be immediately loaded (i.e. a `decl_val` instruction), `is_ref` must be `false`.
32716fn analyzeNavRefInner(sema: *Sema, src: LazySrcLoc, orig_nav_index: InternPool.Nav.Index, is_ref: bool) CompileError!Air.Inst.Ref {
3269932717 const pt = sema.pt;
3270032718 const zcu = pt.zcu;
3270132719 const ip = &zcu.intern_pool;
3270232720
32703 // TODO: if this is a `decl_ref` of a non-variable Nav, only depend on Nav type
32704 try sema.declareDependency(.{ .nav_val = orig_nav_index });
32705 try sema.ensureNavResolved(src, orig_nav_index);
32721 try sema.ensureNavResolved(src, orig_nav_index, if (is_ref) .type else .fully);
3270632722
32707 const nav_val = zcu.navValue(orig_nav_index);
32708 const nav_index, const is_const = switch (ip.indexToKey(nav_val.toIntern())) {
32709 .variable => |v| .{ v.owner_nav, false },
32710 .func => |f| .{ f.owner_nav, true },
32711 .@"extern" => |e| .{ e.owner_nav, e.is_const },
32712 else => .{ orig_nav_index, true },
32723 const nav_index = nav: {
32724 if (ip.getNav(orig_nav_index).isExternOrFn(ip)) {
32725 // Getting a pointer to this `Nav` might mean we actually get a pointer to something else!
32726 // We need to resolve the value to know for sure.
32727 if (is_ref) try sema.ensureNavResolved(src, orig_nav_index, .fully);
32728 switch (ip.indexToKey(ip.getNav(orig_nav_index).status.fully_resolved.val)) {
32729 .func => |f| break :nav f.owner_nav,
32730 .@"extern" => |e| break :nav e.owner_nav,
32731 else => {},
32732 }
32733 }
32734 break :nav orig_nav_index;
32735 };
32736
32737 const ty, const alignment, const @"addrspace", const is_const = switch (ip.getNav(nav_index).status) {
32738 .unresolved => unreachable,
32739 .type_resolved => |r| .{ r.type, r.alignment, r.@"addrspace", r.is_const },
32740 .fully_resolved => |r| .{ ip.typeOf(r.val), r.alignment, r.@"addrspace", zcu.navValIsConst(r.val) },
3271332741 };
32714 const nav_info = ip.getNav(nav_index).status.resolved;
3271532742 const ptr_ty = try pt.ptrTypeSema(.{
32716 .child = nav_val.typeOf(zcu).toIntern(),
32743 .child = ty,
3271732744 .flags = .{
32718 .alignment = nav_info.alignment,
32745 .alignment = alignment,
3271932746 .is_const = is_const,
32720 .address_space = nav_info.@"addrspace",
32747 .address_space = @"addrspace",
3272132748 },
3272232749 });
32723 if (analyze_fn_body) {
32750 if (is_ref) {
3272432751 try sema.maybeQueueFuncBodyAnalysis(src, nav_index);
3272532752 }
3272632753 return Air.internedToRef((try pt.intern(.{ .ptr = .{
......@@ -32731,11 +32758,22 @@ fn analyzeNavRefInner(sema: *Sema, src: LazySrcLoc, orig_nav_index: InternPool.N
3273132758}
3273232759
3273332760fn maybeQueueFuncBodyAnalysis(sema: *Sema, src: LazySrcLoc, nav_index: InternPool.Nav.Index) !void {
32734 const zcu = sema.pt.zcu;
32761 const pt = sema.pt;
32762 const zcu = pt.zcu;
3273532763 const ip = &zcu.intern_pool;
32764
32765 // To avoid forcing too much resolution, let's first resolve the type, and check if it's a function.
32766 // If it is, we can resolve the *value*, and queue analysis as needed.
32767
32768 try sema.ensureNavResolved(src, nav_index, .type);
32769 const nav_ty: Type = .fromInterned(ip.getNav(nav_index).typeOf(ip));
32770 if (nav_ty.zigTypeTag(zcu) != .@"fn") return;
32771 if (!try nav_ty.fnHasRuntimeBitsSema(pt)) return;
32772
32773 try sema.ensureNavResolved(src, nav_index, .fully);
3273632774 const nav_val = zcu.navValue(nav_index);
3273732775 if (!ip.isFuncBody(nav_val.toIntern())) return;
32738 if (!try nav_val.typeOf(zcu).fnHasRuntimeBitsSema(sema.pt)) return;
32776
3273932777 try sema.addReferenceEntry(src, AnalUnit.wrap(.{ .func = nav_val.toIntern() }));
3274032778 try zcu.ensureFuncBodyAnalysisQueued(nav_val.toIntern());
3274132779}
......@@ -38450,11 +38488,16 @@ pub fn declareDependency(sema: *Sema, dependee: InternPool.Dependee) !void {
3845038488 // of a type and they use `@This()`. This dependency would be unnecessary, and in fact would
3845138489 // just result in over-analysis since `Zcu.findOutdatedToAnalyze` would never be able to resolve
3845238490 // the loop.
38491 // Note that this also disallows a `nav_val`
3845338492 switch (sema.owner.unwrap()) {
3845438493 .nav_val => |this_nav| switch (dependee) {
3845538494 .nav_val => |other_nav| if (this_nav == other_nav) return,
3845638495 else => {},
3845738496 },
38497 .nav_ty => |this_nav| switch (dependee) {
38498 .nav_ty => |other_nav| if (this_nav == other_nav) return,
38499 else => {},
38500 },
3845838501 else => {},
3845938502 }
3846038503
......@@ -38873,8 +38916,8 @@ fn getBuiltinInnerType(
3887338916 const nav = opt_nav orelse return sema.fail(block, src, "std.builtin.{s} missing {s}", .{
3887438917 compile_error_parent_name, inner_name,
3887538918 });
38876 try sema.ensureNavResolved(src, nav);
38877 const val = Value.fromInterned(ip.getNav(nav).status.resolved.val);
38919 try sema.ensureNavResolved(src, nav, .fully);
38920 const val = Value.fromInterned(ip.getNav(nav).status.fully_resolved.val);
3887838921 const ty = val.toType();
3887938922 try ty.resolveFully(pt);
3888038923 return ty;
......@@ -38886,5 +38929,73 @@ fn getBuiltin(sema: *Sema, name: []const u8) SemaError!Air.Inst.Ref {
3888638929 const ip = &zcu.intern_pool;
3888738930 const nav = try pt.getBuiltinNav(name);
3888838931 try pt.ensureNavValUpToDate(nav);
38889 return Air.internedToRef(ip.getNav(nav).status.resolved.val);
38932 return Air.internedToRef(ip.getNav(nav).status.fully_resolved.val);
38933}
38934
38935pub const NavPtrModifiers = struct {
38936 alignment: Alignment,
38937 @"linksection": InternPool.OptionalNullTerminatedString,
38938 @"addrspace": std.builtin.AddressSpace,
38939};
38940
38941pub fn resolveNavPtrModifiers(
38942 sema: *Sema,
38943 block: *Block,
38944 zir_decl: Zir.Inst.Declaration.Unwrapped,
38945 decl_inst: Zir.Inst.Index,
38946 nav_ty: Type,
38947) CompileError!NavPtrModifiers {
38948 const pt = sema.pt;
38949 const zcu = pt.zcu;
38950 const gpa = zcu.gpa;
38951 const ip = &zcu.intern_pool;
38952
38953 const align_src = block.src(.{ .node_offset_var_decl_align = 0 });
38954 const section_src = block.src(.{ .node_offset_var_decl_section = 0 });
38955 const addrspace_src = block.src(.{ .node_offset_var_decl_addrspace = 0 });
38956
38957 const alignment: InternPool.Alignment = a: {
38958 const align_body = zir_decl.align_body orelse break :a .none;
38959 const align_ref = try sema.resolveInlineBody(block, align_body, decl_inst);
38960 break :a try sema.analyzeAsAlign(block, align_src, align_ref);
38961 };
38962
38963 const @"linksection": InternPool.OptionalNullTerminatedString = ls: {
38964 const linksection_body = zir_decl.linksection_body orelse break :ls .none;
38965 const linksection_ref = try sema.resolveInlineBody(block, linksection_body, decl_inst);
38966 const bytes = try sema.toConstString(block, section_src, linksection_ref, .{
38967 .needed_comptime_reason = "linksection must be comptime-known",
38968 });
38969 if (std.mem.indexOfScalar(u8, bytes, 0) != null) {
38970 return sema.fail(block, section_src, "linksection cannot contain null bytes", .{});
38971 } else if (bytes.len == 0) {
38972 return sema.fail(block, section_src, "linksection cannot be empty", .{});
38973 }
38974 break :ls try ip.getOrPutStringOpt(gpa, pt.tid, bytes, .no_embedded_nulls);
38975 };
38976
38977 const @"addrspace": std.builtin.AddressSpace = as: {
38978 const addrspace_ctx: Sema.AddressSpaceContext = switch (zir_decl.kind) {
38979 .@"var" => .variable,
38980 else => switch (nav_ty.zigTypeTag(zcu)) {
38981 .@"fn" => .function,
38982 else => .constant,
38983 },
38984 };
38985 const target = zcu.getTarget();
38986 const addrspace_body = zir_decl.addrspace_body orelse break :as switch (addrspace_ctx) {
38987 .function => target_util.defaultAddressSpace(target, .function),
38988 .variable => target_util.defaultAddressSpace(target, .global_mutable),
38989 .constant => target_util.defaultAddressSpace(target, .global_constant),
38990 else => unreachable,
38991 };
38992 const addrspace_ref = try sema.resolveInlineBody(block, addrspace_body, decl_inst);
38993 break :as try sema.analyzeAsAddressSpace(block, addrspace_src, addrspace_ref, addrspace_ctx);
38994 };
38995
38996 return .{
38997 .alignment = alignment,
38998 .@"linksection" = @"linksection",
38999 .@"addrspace" = @"addrspace",
39000 };
3889039001}
src/Sema/comptime_ptr_access.zig+2-3
......@@ -219,9 +219,8 @@ fn loadComptimePtrInner(
219219
220220 const base_val: MutableValue = switch (ptr.base_addr) {
221221 .nav => |nav| val: {
222 try sema.declareDependency(.{ .nav_val = nav });
223 try sema.ensureNavResolved(src, nav);
224 const val = ip.getNav(nav).status.resolved.val;
222 try sema.ensureNavResolved(src, nav, .fully);
223 const val = ip.getNav(nav).status.fully_resolved.val;
225224 switch (ip.indexToKey(val)) {
226225 .variable => return .runtime_load,
227226 // We let `.@"extern"` through here if it's a function.
src/Value.zig+6-1
......@@ -1343,7 +1343,12 @@ pub fn isLazySize(val: Value, zcu: *Zcu) bool {
13431343pub fn isPtrRuntimeValue(val: Value, zcu: *Zcu) bool {
13441344 const ip = &zcu.intern_pool;
13451345 const nav = ip.getBackingNav(val.toIntern()).unwrap() orelse return false;
1346 return switch (ip.indexToKey(ip.getNav(nav).status.resolved.val)) {
1346 const nav_val = switch (ip.getNav(nav).status) {
1347 .unresolved => unreachable,
1348 .type_resolved => |r| return r.is_threadlocal,
1349 .fully_resolved => |r| r.val,
1350 };
1351 return switch (ip.indexToKey(nav_val)) {
13471352 .@"extern" => |e| e.is_threadlocal or e.is_dll_import,
13481353 .variable => |v| v.is_threadlocal,
13491354 else => false,
src/Zcu.zig+72-9
......@@ -170,6 +170,9 @@ outdated_ready: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .empty,
170170/// it as outdated.
171171retryable_failures: std.ArrayListUnmanaged(AnalUnit) = .empty,
172172
173func_body_analysis_queued: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .empty,
174nav_val_analysis_queued: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void) = .empty,
175
173176/// These are the modules which we initially queue for analysis in `Compilation.update`.
174177/// `resolveReferences` will use these as the root of its reachability traversal.
175178analysis_roots: std.BoundedArray(*Package.Module, 3) = .{},
......@@ -282,7 +285,11 @@ pub const Exported = union(enum) {
282285
283286 pub fn getAlign(exported: Exported, zcu: *Zcu) Alignment {
284287 return switch (exported) {
285 .nav => |nav| zcu.intern_pool.getNav(nav).status.resolved.alignment,
288 .nav => |nav| switch (zcu.intern_pool.getNav(nav).status) {
289 .unresolved => unreachable,
290 .type_resolved => |r| r.alignment,
291 .fully_resolved => |r| r.alignment,
292 },
286293 .uav => .none,
287294 };
288295 }
......@@ -2241,6 +2248,9 @@ pub fn deinit(zcu: *Zcu) void {
22412248 zcu.outdated_ready.deinit(gpa);
22422249 zcu.retryable_failures.deinit(gpa);
22432250
2251 zcu.func_body_analysis_queued.deinit(gpa);
2252 zcu.nav_val_analysis_queued.deinit(gpa);
2253
22442254 zcu.test_functions.deinit(gpa);
22452255
22462256 for (zcu.global_assembly.values()) |s| {
......@@ -2441,6 +2451,7 @@ pub fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void {
24412451 switch (depender.unwrap()) {
24422452 .@"comptime" => {},
24432453 .nav_val => |nav| try zcu.markPoDependeeUpToDate(.{ .nav_val = nav }),
2454 .nav_ty => |nav| try zcu.markPoDependeeUpToDate(.{ .nav_ty = nav }),
24442455 .type => |ty| try zcu.markPoDependeeUpToDate(.{ .interned = ty }),
24452456 .func => |func| try zcu.markPoDependeeUpToDate(.{ .interned = func }),
24462457 }
......@@ -2453,7 +2464,8 @@ fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUni
24532464 const ip = &zcu.intern_pool;
24542465 const dependee: InternPool.Dependee = switch (maybe_outdated.unwrap()) {
24552466 .@"comptime" => return, // analysis of a comptime decl can't outdate any dependencies
2456 .nav_val => |nav| .{ .nav_val = nav }, // TODO: also `nav_ref` deps when introduced
2467 .nav_val => |nav| .{ .nav_val = nav },
2468 .nav_ty => |nav| .{ .nav_ty = nav },
24572469 .type => |ty| .{ .interned = ty },
24582470 .func => |func_index| .{ .interned = func_index }, // IES
24592471 };
......@@ -2540,6 +2552,7 @@ pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit {
25402552 .@"comptime" => continue, // a `comptime` block can't even be depended on so it is a terrible choice
25412553 .type => |ty| .{ .interned = ty },
25422554 .nav_val => |nav| .{ .nav_val = nav },
2555 .nav_ty => |nav| .{ .nav_ty = nav },
25432556 });
25442557 while (it.next()) |_| n += 1;
25452558
......@@ -2780,14 +2793,39 @@ pub fn ensureFuncBodyAnalysisQueued(zcu: *Zcu, func_index: InternPool.Index) !vo
27802793 const ip = &zcu.intern_pool;
27812794 const func = zcu.funcInfo(func_index);
27822795
2783 switch (func.analysisUnordered(ip).state) {
2784 .unreferenced => {}, // We're the first reference!
2785 .queued => return, // Analysis is already queued.
2786 .analyzed => return, // Analysis is complete; if it's out-of-date, it'll be re-analyzed later this update.
2796 if (zcu.func_body_analysis_queued.contains(func_index)) return;
2797
2798 if (func.analysisUnordered(ip).is_analyzed) {
2799 if (!zcu.outdated.contains(.wrap(.{ .func = func_index })) and
2800 !zcu.potentially_outdated.contains(.wrap(.{ .func = func_index })))
2801 {
2802 // This function has been analyzed before and is definitely up-to-date.
2803 return;
2804 }
27872805 }
27882806
2807 try zcu.func_body_analysis_queued.ensureUnusedCapacity(zcu.gpa, 1);
27892808 try zcu.comp.queueJob(.{ .analyze_func = func_index });
2790 func.setAnalysisState(ip, .queued);
2809 zcu.func_body_analysis_queued.putAssumeCapacityNoClobber(func_index, {});
2810}
2811
2812pub fn ensureNavValAnalysisQueued(zcu: *Zcu, nav_id: InternPool.Nav.Index) !void {
2813 const ip = &zcu.intern_pool;
2814
2815 if (zcu.nav_val_analysis_queued.contains(nav_id)) return;
2816
2817 if (ip.getNav(nav_id).status == .fully_resolved) {
2818 if (!zcu.outdated.contains(.wrap(.{ .nav_val = nav_id })) and
2819 !zcu.potentially_outdated.contains(.wrap(.{ .nav_val = nav_id })))
2820 {
2821 // This `Nav` has been analyzed before and is definitely up-to-date.
2822 return;
2823 }
2824 }
2825
2826 try zcu.nav_val_analysis_queued.ensureUnusedCapacity(zcu.gpa, 1);
2827 try zcu.comp.queueJob(.{ .analyze_comptime_unit = .wrap(.{ .nav_val = nav_id }) });
2828 zcu.nav_val_analysis_queued.putAssumeCapacityNoClobber(nav_id, {});
27912829}
27922830
27932831pub const ImportFileResult = struct {
......@@ -3424,6 +3462,17 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv
34243462 const unit = kv.key;
34253463 try result.putNoClobber(gpa, unit, kv.value);
34263464
3465 // `nav_val` and `nav_ty` reference each other *implicitly* to save memory.
3466 queue_paired: {
3467 const other: AnalUnit = .wrap(switch (unit.unwrap()) {
3468 .nav_val => |n| .{ .nav_ty = n },
3469 .nav_ty => |n| .{ .nav_val = n },
3470 .@"comptime", .type, .func => break :queue_paired,
3471 });
3472 if (result.contains(other)) break :queue_paired;
3473 try unit_queue.put(gpa, other, kv.value); // same reference location
3474 }
3475
34273476 log.debug("handle unit '{}'", .{zcu.fmtAnalUnit(unit)});
34283477
34293478 if (zcu.reference_table.get(unit)) |first_ref_idx| {
......@@ -3513,7 +3562,7 @@ pub fn navSrcLine(zcu: *Zcu, nav_index: InternPool.Nav.Index) u32 {
35133562}
35143563
35153564pub fn navValue(zcu: *const Zcu, nav_index: InternPool.Nav.Index) Value {
3516 return Value.fromInterned(zcu.intern_pool.getNav(nav_index).status.resolved.val);
3565 return Value.fromInterned(zcu.intern_pool.getNav(nav_index).status.fully_resolved.val);
35173566}
35183567
35193568pub 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
35473596 }
35483597 },
35493598 .nav_val => |nav| return writer.print("nav_val('{}')", .{ip.getNav(nav).fqn.fmt(ip)}),
3599 .nav_ty => |nav| return writer.print("nav_ty('{}')", .{ip.getNav(nav).fqn.fmt(ip)}),
35503600 .type => |ty| return writer.print("ty('{}')", .{Type.fromInterned(ty).containerTypeName(ip).fmt(ip)}),
35513601 .func => |func| {
35523602 const nav = zcu.funcInfo(func).owner_nav;
......@@ -3572,7 +3622,11 @@ fn formatDependee(data: struct { dependee: InternPool.Dependee, zcu: *Zcu }, com
35723622 },
35733623 .nav_val => |nav| {
35743624 const fqn = ip.getNav(nav).fqn;
3575 return writer.print("nav('{}')", .{fqn.fmt(ip)});
3625 return writer.print("nav_val('{}')", .{fqn.fmt(ip)});
3626 },
3627 .nav_ty => |nav| {
3628 const fqn = ip.getNav(nav).fqn;
3629 return writer.print("nav_ty('{}')", .{fqn.fmt(ip)});
35763630 },
35773631 .interned => |ip_index| switch (ip.indexToKey(ip_index)) {
35783632 .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
37493803 if (!backend_ok) return .{ .bad_backend = backend };
37503804 return .ok;
37513805}
3806
3807/// Given that a `Nav` has value `val`, determine if a ref of that `Nav` gives a `const` pointer.
3808pub fn navValIsConst(zcu: *const Zcu, val: InternPool.Index) bool {
3809 return switch (zcu.intern_pool.indexToKey(val)) {
3810 .variable => false,
3811 .@"extern" => |e| e.is_const,
3812 else => true,
3813 };
3814}
src/Zcu/PerThread.zig+340-136
......@@ -731,10 +731,12 @@ pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu
731731 const gpa = zcu.gpa;
732732 const ip = &zcu.intern_pool;
733733
734 _ = zcu.nav_val_analysis_queued.swapRemove(nav_id);
735
734736 const anal_unit: AnalUnit = .wrap(.{ .nav_val = nav_id });
735737 const nav = ip.getNav(nav_id);
736738
737 log.debug("ensureNavUpToDate {}", .{zcu.fmtAnalUnit(anal_unit)});
739 log.debug("ensureNavValUpToDate {}", .{zcu.fmtAnalUnit(anal_unit)});
738740
739741 // Determine whether or not this `Nav`'s value is outdated. This also includes checking if the
740742 // 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
763765 } else {
764766 // We can trust the current information about this unit.
765767 if (prev_failed) return error.AnalysisFail;
766 if (nav.status == .resolved) return;
768 switch (nav.status) {
769 .unresolved, .type_resolved => {},
770 .fully_resolved => return,
771 }
767772 }
768773
769774 const unit_prog_node = zcu.sema_prog_node.start(nav.fqn.toSlice(ip), 0);
770775 defer unit_prog_node.end();
771776
772 const sema_result: SemaNavResult, const new_failed: bool = if (pt.analyzeNavVal(nav_id)) |result| res: {
777 const invalidate_value: bool, const new_failed: bool = if (pt.analyzeNavVal(nav_id)) |result| res: {
773778 break :res .{
774 .{
775 // If the unit has gone from failed to success, we still need to invalidate the dependencies.
776 .invalidate_nav_val = result.invalidate_nav_val or prev_failed,
777 .invalidate_nav_ref = result.invalidate_nav_ref or prev_failed,
778 },
779 // If the unit has gone from failed to success, we still need to invalidate the dependencies.
780 result.val_changed or prev_failed,
779781 false,
780782 };
781783 } else |err| switch (err) {
......@@ -786,10 +788,7 @@ pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu
786788 try zcu.transitive_failed_analysis.put(gpa, anal_unit, {});
787789 log.debug("mark transitive analysis failure for {}", .{zcu.fmtAnalUnit(anal_unit)});
788790 }
789 break :res .{ .{
790 .invalidate_nav_val = !prev_failed,
791 .invalidate_nav_ref = !prev_failed,
792 }, true };
791 break :res .{ !prev_failed, true };
793792 },
794793 error.OutOfMemory => {
795794 // 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
806805 };
807806
808807 if (was_outdated) {
809 // TODO: we do not yet have separate dependencies for Nav values vs types.
810 const invalidate = sema_result.invalidate_nav_val or sema_result.invalidate_nav_ref;
811808 const dependee: InternPool.Dependee = .{ .nav_val = nav_id };
812 if (invalidate) {
809 if (invalidate_value) {
813810 // This dependency was marked as PO, meaning dependees were waiting
814811 // on its analysis result, and it has turned out to be outdated.
815812 // Update dependees accordingly.
......@@ -824,14 +821,7 @@ pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu
824821 if (new_failed) return error.AnalysisFail;
825822}
826823
827const SemaNavResult = packed struct {
828 /// Whether the value of a `decl_val` of the corresponding Nav changed.
829 invalidate_nav_val: bool,
830 /// Whether the type of a `decl_ref` of the corresponding Nav changed.
831 invalidate_nav_ref: bool,
832};
833
834fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileError!SemaNavResult {
824fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileError!struct { val_changed: bool } {
835825 const zcu = pt.zcu;
836826 const gpa = zcu.gpa;
837827 const ip = &zcu.intern_pool;
......@@ -875,9 +865,13 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr
875865 };
876866 defer sema.deinit();
877867
878 // The comptime unit declares on the source of the corresponding declaration.
868 // Every `Nav` declares a dependency on the source of the corresponding declaration.
879869 try sema.declareDependency(.{ .src_hash = old_nav.analysis.?.zir_index });
880870
871 // In theory, we would also add a reference to the corresponding `nav_val` unit here: there are
872 // always references in both directions between a `nav_val` and `nav_ty`. However, to save memory,
873 // these references are known implicitly. See logic in `Zcu.resolveReferences`.
874
881875 var block: Sema.Block = .{
882876 .parent = null,
883877 .sema = &sema,
......@@ -891,31 +885,44 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr
891885 defer block.instructions.deinit(gpa);
892886
893887 const zir_decl = zir.getDeclaration(inst_resolved.inst);
894
895888 assert(old_nav.is_usingnamespace == (zir_decl.kind == .@"usingnamespace"));
896889
890 const ty_src = block.src(.{ .node_offset_var_decl_ty = 0 });
891 const init_src = block.src(.{ .node_offset_var_decl_init = 0 });
897892 const align_src = block.src(.{ .node_offset_var_decl_align = 0 });
898893 const section_src = block.src(.{ .node_offset_var_decl_section = 0 });
899894 const addrspace_src = block.src(.{ .node_offset_var_decl_addrspace = 0 });
900 const ty_src = block.src(.{ .node_offset_var_decl_ty = 0 });
901 const init_src = block.src(.{ .node_offset_var_decl_init = 0 });
895
896 const maybe_ty: ?Type = if (zir_decl.type_body != null) ty: {
897 // Since we have a type body, the type is resolved separately!
898 // Of course, we need to make sure we depend on it properly.
899 try sema.declareDependency(.{ .nav_ty = nav_id });
900 try pt.ensureNavTypeUpToDate(nav_id);
901 break :ty .fromInterned(ip.getNav(nav_id).status.type_resolved.type);
902 } else null;
903
904 const final_val: ?Value = if (zir_decl.value_body) |value_body| val: {
905 if (maybe_ty) |ty| {
906 // Put the resolved type into `inst_map` to be used as the result type of the init.
907 try sema.inst_map.ensureSpaceForInstructions(gpa, &.{inst_resolved.inst});
908 sema.inst_map.putAssumeCapacity(inst_resolved.inst, Air.internedToRef(ty.toIntern()));
909 const uncoerced_result_ref = try sema.resolveInlineBody(&block, value_body, inst_resolved.inst);
910 assert(sema.inst_map.remove(inst_resolved.inst));
911
912 const result_ref = try sema.coerce(&block, ty, uncoerced_result_ref, init_src);
913 break :val try sema.resolveFinalDeclValue(&block, init_src, result_ref);
914 } else {
915 // Just analyze the value; we have no type to offer.
916 const result_ref = try sema.resolveInlineBody(&block, value_body, inst_resolved.inst);
917 break :val try sema.resolveFinalDeclValue(&block, init_src, result_ref);
918 }
919 } else null;
920
921 const nav_ty: Type = maybe_ty orelse final_val.?.typeOf(zcu);
902922
903923 // First, we must resolve the declaration's type. To do this, we analyze the type body if available,
904924 // or otherwise, we analyze the value body, populating `early_val` in the process.
905925
906 const nav_ty: Type, const early_val: ?Value = if (zir_decl.type_body) |type_body| ty: {
907 // We evaluate only the type now; no need for the value yet.
908 const uncoerced_type_ref = try sema.resolveInlineBody(&block, type_body, inst_resolved.inst);
909 const type_ref = try sema.coerce(&block, .type, uncoerced_type_ref, ty_src);
910 break :ty .{ .fromInterned(type_ref.toInterned().?), null };
911 } else ty: {
912 // We don't have a type body, so we need to evaluate the value immediately.
913 const value_body = zir_decl.value_body.?;
914 const result_ref = try sema.resolveInlineBody(&block, value_body, inst_resolved.inst);
915 const val = try sema.resolveFinalDeclValue(&block, init_src, result_ref);
916 break :ty .{ val.typeOf(zcu), val };
917 };
918
919926 switch (zir_decl.kind) {
920927 .@"comptime" => unreachable, // this is not a Nav
921928 .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
932939 // Now that we know the type, we can evaluate the alignment, linksection, and addrspace, to determine
933940 // the full pointer type of this declaration.
934941
935 const alignment: InternPool.Alignment = a: {
936 const align_body = zir_decl.align_body orelse break :a .none;
937 const align_ref = try sema.resolveInlineBody(&block, align_body, inst_resolved.inst);
938 break :a try sema.analyzeAsAlign(&block, align_src, align_ref);
939 };
940
941 const @"linksection": InternPool.OptionalNullTerminatedString = ls: {
942 const linksection_body = zir_decl.linksection_body orelse break :ls .none;
943 const linksection_ref = try sema.resolveInlineBody(&block, linksection_body, inst_resolved.inst);
944 const bytes = try sema.toConstString(&block, section_src, linksection_ref, .{
945 .needed_comptime_reason = "linksection must be comptime-known",
946 });
947 if (std.mem.indexOfScalar(u8, bytes, 0) != null) {
948 return sema.fail(&block, section_src, "linksection cannot contain null bytes", .{});
949 } else if (bytes.len == 0) {
950 return sema.fail(&block, section_src, "linksection cannot be empty", .{});
951 }
952 break :ls try ip.getOrPutStringOpt(gpa, pt.tid, bytes, .no_embedded_nulls);
953 };
954
955 const @"addrspace": std.builtin.AddressSpace = as: {
956 const addrspace_ctx: Sema.AddressSpaceContext = switch (zir_decl.kind) {
957 .@"var" => .variable,
958 else => switch (nav_ty.zigTypeTag(zcu)) {
959 .@"fn" => .function,
960 else => .constant,
942 const modifiers: Sema.NavPtrModifiers = if (zir_decl.type_body != null) m: {
943 // `analyzeNavType` (from the `ensureNavTypeUpToDate` call above) has already populated this data into
944 // the `Nav`. Load the new one, and pull the modifiers out.
945 switch (ip.getNav(nav_id).status) {
946 .unresolved => unreachable, // `analyzeNavType` will never leave us in this state
947 inline .type_resolved, .fully_resolved => |r| break :m .{
948 .alignment = r.alignment,
949 .@"linksection" = r.@"linksection",
950 .@"addrspace" = r.@"addrspace",
961951 },
962 };
963 const target = zcu.getTarget();
964 const addrspace_body = zir_decl.addrspace_body orelse break :as switch (addrspace_ctx) {
965 .function => target_util.defaultAddressSpace(target, .function),
966 .variable => target_util.defaultAddressSpace(target, .global_mutable),
967 .constant => target_util.defaultAddressSpace(target, .global_constant),
968 else => unreachable,
969 };
970 const addrspace_ref = try sema.resolveInlineBody(&block, addrspace_body, inst_resolved.inst);
971 break :as try sema.analyzeAsAddressSpace(&block, addrspace_src, addrspace_ref, addrspace_ctx);
952 }
953 } else m: {
954 // `analyzeNavType` is essentially a stub which calls us. We are responsible for resolving this data.
955 break :m try sema.resolveNavPtrModifiers(&block, zir_decl, inst_resolved.inst, nav_ty);
972956 };
973957
974 // Lastly, we must evaluate the value if we have not already done so. Note, however, that extern declarations
975 // don't have an associated value body.
976
977 const final_val: ?Value = early_val orelse if (zir_decl.value_body) |value_body| val: {
978 // Put the resolved type into `inst_map` to be used as the result type of the init.
979 try sema.inst_map.ensureSpaceForInstructions(gpa, &.{inst_resolved.inst});
980 sema.inst_map.putAssumeCapacity(inst_resolved.inst, Air.internedToRef(nav_ty.toIntern()));
981 const uncoerced_result_ref = try sema.resolveInlineBody(&block, value_body, inst_resolved.inst);
982 assert(sema.inst_map.remove(inst_resolved.inst));
983
984 const result_ref = try sema.coerce(&block, nav_ty, uncoerced_result_ref, init_src);
985 break :val try sema.resolveFinalDeclValue(&block, init_src, result_ref);
986 } else null;
958 // Lastly, we must figure out the actual interned value to store to the `Nav`.
959 // This isn't necessarily the same as `final_val`!
987960
988961 const nav_val: Value = switch (zir_decl.linkage) {
989962 .normal, .@"export" => switch (zir_decl.kind) {
......@@ -1013,8 +986,8 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr
1013986 .is_threadlocal = zir_decl.is_threadlocal,
1014987 .is_weak_linkage = false,
1015988 .is_dll_import = false,
1016 .alignment = alignment,
1017 .@"addrspace" = @"addrspace",
989 .alignment = modifiers.alignment,
990 .@"addrspace" = modifiers.@"addrspace",
1018991 .zir_index = old_nav.analysis.?.zir_index, // `declaration` instruction
1019992 .owner_nav = undefined, // ignored by `getExtern`
1020993 }));
......@@ -1047,10 +1020,7 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr
10471020 });
10481021 // TODO: usingnamespace cannot participate in incremental compilation
10491022 assert(zcu.analysis_in_progress.swapRemove(anal_unit));
1050 return .{
1051 .invalidate_nav_val = true,
1052 .invalidate_nav_ref = true,
1053 };
1023 return .{ .val_changed = true };
10541024 }
10551025
10561026 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
10871057
10881058 ip.resolveNavValue(nav_id, .{
10891059 .val = nav_val.toIntern(),
1090 .alignment = alignment,
1091 .@"linksection" = @"linksection",
1092 .@"addrspace" = @"addrspace",
1060 .alignment = modifiers.alignment,
1061 .@"linksection" = modifiers.@"linksection",
1062 .@"addrspace" = modifiers.@"addrspace",
10931063 });
10941064
10951065 // Mark the unit as completed before evaluating the export!
10961066 assert(zcu.analysis_in_progress.swapRemove(anal_unit));
10971067
1068 if (zir_decl.type_body == null) {
1069 // In this situation, it's possible that we were triggered by `analyzeNavType` up the stack. In that
1070 // case, we must also signal that the *type* is now populated to make this export behave correctly.
1071 // An alternative strategy would be to just put something on the job queue to perform the export, but
1072 // this is a little more straightforward, if perhaps less elegant.
1073 _ = zcu.analysis_in_progress.swapRemove(.wrap(.{ .nav_ty = nav_id }));
1074 }
1075
10981076 if (zir_decl.linkage == .@"export") {
10991077 const export_src = block.src(.{ .token_offset = @intFromBool(zir_decl.is_pub) });
11001078 const name_slice = zir.nullTerminatedString(zir_decl.name);
......@@ -1117,21 +1095,246 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr
11171095 }
11181096
11191097 switch (old_nav.status) {
1120 .unresolved => return .{
1121 .invalidate_nav_val = true,
1122 .invalidate_nav_ref = true,
1098 .unresolved, .type_resolved => return .{ .val_changed = true },
1099 .fully_resolved => |old| return .{ .val_changed = old.val != nav_val.toIntern() },
1100 }
1101}
1102
1103pub fn ensureNavTypeUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.SemaError!void {
1104 const tracy = trace(@src());
1105 defer tracy.end();
1106
1107 const zcu = pt.zcu;
1108 const gpa = zcu.gpa;
1109 const ip = &zcu.intern_pool;
1110
1111 const anal_unit: AnalUnit = .wrap(.{ .nav_ty = nav_id });
1112 const nav = ip.getNav(nav_id);
1113
1114 log.debug("ensureNavTypeUpToDate {}", .{zcu.fmtAnalUnit(anal_unit)});
1115
1116 // Determine whether or not this `Nav`'s type is outdated. This also includes checking if the
1117 // status is `.unresolved`, which indicates that the value is outdated because it has *never*
1118 // been analyzed so far.
1119 //
1120 // Note that if the unit is PO, we pessimistically assume that it *does* require re-analysis, to
1121 // ensure that the unit is definitely up-to-date when this function returns. This mechanism could
1122 // result in over-analysis if analysis occurs in a poor order; we do our best to avoid this by
1123 // carefully choosing which units to re-analyze. See `Zcu.findOutdatedToAnalyze`.
1124
1125 const was_outdated = zcu.outdated.swapRemove(anal_unit) or
1126 zcu.potentially_outdated.swapRemove(anal_unit);
1127
1128 const prev_failed = zcu.failed_analysis.contains(anal_unit) or
1129 zcu.transitive_failed_analysis.contains(anal_unit);
1130
1131 if (was_outdated) {
1132 dev.check(.incremental);
1133 _ = zcu.outdated_ready.swapRemove(anal_unit);
1134 zcu.deleteUnitExports(anal_unit);
1135 zcu.deleteUnitReferences(anal_unit);
1136 if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| {
1137 kv.value.destroy(gpa);
1138 }
1139 _ = zcu.transitive_failed_analysis.swapRemove(anal_unit);
1140 } else {
1141 // We can trust the current information about this unit.
1142 if (prev_failed) return error.AnalysisFail;
1143 switch (nav.status) {
1144 .unresolved => {},
1145 .type_resolved, .fully_resolved => return,
1146 }
1147 }
1148
1149 const unit_prog_node = zcu.sema_prog_node.start(nav.fqn.toSlice(ip), 0);
1150 defer unit_prog_node.end();
1151
1152 const invalidate_type: bool, const new_failed: bool = if (pt.analyzeNavType(nav_id)) |result| res: {
1153 break :res .{
1154 // If the unit has gone from failed to success, we still need to invalidate the dependencies.
1155 result.type_changed or prev_failed,
1156 false,
1157 };
1158 } else |err| switch (err) {
1159 error.AnalysisFail => res: {
1160 if (!zcu.failed_analysis.contains(anal_unit)) {
1161 // If this unit caused the error, it would have an entry in `failed_analysis`.
1162 // Since it does not, this must be a transitive failure.
1163 try zcu.transitive_failed_analysis.put(gpa, anal_unit, {});
1164 log.debug("mark transitive analysis failure for {}", .{zcu.fmtAnalUnit(anal_unit)});
1165 }
1166 break :res .{ !prev_failed, true };
11231167 },
1124 .resolved => |old| {
1125 const new = ip.getNav(nav_id).status.resolved;
1126 return .{
1127 .invalidate_nav_val = new.val != old.val,
1128 .invalidate_nav_ref = ip.typeOf(new.val) != ip.typeOf(old.val) or
1129 new.alignment != old.alignment or
1130 new.@"linksection" != old.@"linksection" or
1131 new.@"addrspace" != old.@"addrspace",
1132 };
1168 error.OutOfMemory => {
1169 // TODO: it's unclear how to gracefully handle this.
1170 // To report the error cleanly, we need to add a message to `failed_analysis` and a
1171 // corresponding entry to `retryable_failures`; but either of these things is quite
1172 // likely to OOM at this point.
1173 // If that happens, what do we do? Perhaps we could have a special field on `Zcu`
1174 // for reporting OOM errors without allocating.
1175 return error.OutOfMemory;
11331176 },
1177 error.GenericPoison => unreachable,
1178 error.ComptimeReturn => unreachable,
1179 error.ComptimeBreak => unreachable,
1180 };
1181
1182 if (was_outdated) {
1183 const dependee: InternPool.Dependee = .{ .nav_ty = nav_id };
1184 if (invalidate_type) {
1185 // This dependency was marked as PO, meaning dependees were waiting
1186 // on its analysis result, and it has turned out to be outdated.
1187 // Update dependees accordingly.
1188 try zcu.markDependeeOutdated(.marked_po, dependee);
1189 } else {
1190 // This dependency was previously PO, but turned out to be up-to-date.
1191 // We do not need to queue successive analysis.
1192 try zcu.markPoDependeeUpToDate(dependee);
1193 }
11341194 }
1195
1196 if (new_failed) return error.AnalysisFail;
1197}
1198
1199fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileError!struct { type_changed: bool } {
1200 const zcu = pt.zcu;
1201 const gpa = zcu.gpa;
1202 const ip = &zcu.intern_pool;
1203
1204 const anal_unit: AnalUnit = .wrap(.{ .nav_ty = nav_id });
1205 const old_nav = ip.getNav(nav_id);
1206
1207 log.debug("analyzeNavType {}", .{zcu.fmtAnalUnit(anal_unit)});
1208
1209 const inst_resolved = old_nav.analysis.?.zir_index.resolveFull(ip) orelse return error.AnalysisFail;
1210 const file = zcu.fileByIndex(inst_resolved.file);
1211 // TODO: stop the compiler ever reaching Sema if there are failed files. That way, this check is
1212 // unnecessary, and we can move the below `removeDependenciesForDepender` call up with its friends
1213 // in `ensureComptimeUnitUpToDate`.
1214 if (file.status != .success_zir) return error.AnalysisFail;
1215 const zir = file.zir;
1216
1217 // We are about to re-analyze this unit; drop its depenndencies.
1218 zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit);
1219
1220 try zcu.analysis_in_progress.put(gpa, anal_unit, {});
1221 defer _ = zcu.analysis_in_progress.swapRemove(anal_unit);
1222
1223 var analysis_arena: std.heap.ArenaAllocator = .init(gpa);
1224 defer analysis_arena.deinit();
1225
1226 var comptime_err_ret_trace: std.ArrayList(Zcu.LazySrcLoc) = .init(gpa);
1227 defer comptime_err_ret_trace.deinit();
1228
1229 var sema: Sema = .{
1230 .pt = pt,
1231 .gpa = gpa,
1232 .arena = analysis_arena.allocator(),
1233 .code = zir,
1234 .owner = anal_unit,
1235 .func_index = .none,
1236 .func_is_naked = false,
1237 .fn_ret_ty = .void,
1238 .fn_ret_ty_ies = null,
1239 .comptime_err_ret_trace = &comptime_err_ret_trace,
1240 };
1241 defer sema.deinit();
1242
1243 // Every `Nav` declares a dependency on the source of the corresponding declaration.
1244 try sema.declareDependency(.{ .src_hash = old_nav.analysis.?.zir_index });
1245
1246 // In theory, we would also add a reference to the corresponding `nav_val` unit here: there are
1247 // always references in both directions between a `nav_val` and `nav_ty`. However, to save memory,
1248 // these references are known implicitly. See logic in `Zcu.resolveReferences`.
1249
1250 var block: Sema.Block = .{
1251 .parent = null,
1252 .sema = &sema,
1253 .namespace = old_nav.analysis.?.namespace,
1254 .instructions = .{},
1255 .inlining = null,
1256 .is_comptime = true,
1257 .src_base_inst = old_nav.analysis.?.zir_index,
1258 .type_name_ctx = old_nav.fqn,
1259 };
1260 defer block.instructions.deinit(gpa);
1261
1262 const zir_decl = zir.getDeclaration(inst_resolved.inst);
1263 assert(old_nav.is_usingnamespace == (zir_decl.kind == .@"usingnamespace"));
1264
1265 const type_body = zir_decl.type_body orelse {
1266 // The type of this `Nav` is inferred from the value.
1267 // In other words, this `nav_ty` depends on the corresponding `nav_val`.
1268 try sema.declareDependency(.{ .nav_val = nav_id });
1269 try pt.ensureNavValUpToDate(nav_id);
1270 // Note that the above call, if it did any work, has removed our `analysis_in_progress` entry for us.
1271 // (Our `defer` will run anyway, but it does nothing in this case.)
1272
1273 // There's not a great way for us to know whether the type actually changed.
1274 // For instance, perhaps the `nav_val` was already up-to-date, but this `nav_ty` is being
1275 // analyzed because this declaration had a type annotation on the *previous* update.
1276 // However, such cases are rare, and it's not unreasonable to re-analyze in them; and in
1277 // other cases where we get here, it's because the `nav_val` was already re-analyzed and
1278 // is outdated.
1279 return .{ .type_changed = true };
1280 };
1281
1282 const ty_src = block.src(.{ .node_offset_var_decl_ty = 0 });
1283
1284 const resolved_ty: Type = ty: {
1285 const uncoerced_type_ref = try sema.resolveInlineBody(&block, type_body, inst_resolved.inst);
1286 const type_ref = try sema.coerce(&block, .type, uncoerced_type_ref, ty_src);
1287 break :ty .fromInterned(type_ref.toInterned().?);
1288 };
1289
1290 // In the case where the type is specified, this function is also responsible for resolving
1291 // the pointer modifiers, i.e. alignment, linksection, addrspace.
1292 const modifiers = try sema.resolveNavPtrModifiers(&block, zir_decl, inst_resolved.inst, resolved_ty);
1293
1294 // Usually, we can infer this information from the resolved `Nav` value; see `Zcu.navValIsConst`.
1295 // However, since we don't have one, we need to quickly check the ZIR to figure this out.
1296 const is_const = switch (zir_decl.kind) {
1297 .@"comptime" => unreachable,
1298 .unnamed_test, .@"test", .decltest, .@"usingnamespace", .@"const" => true,
1299 .@"var" => false,
1300 };
1301
1302 const is_extern_decl = zir_decl.linkage == .@"extern";
1303
1304 // Now for the question of the day: are the type and modifiers the same as before?
1305 // If they are, then we should actually keep the `Nav` as `fully_resolved` if it currently is.
1306 // That's because `analyzeNavVal` will later want to look at the resolved value to figure out
1307 // whether it's changed: if we threw that data away now, it would have to assume that the value
1308 // had changed, potentially spinning off loads of unnecessary re-analysis!
1309 const changed = switch (old_nav.status) {
1310 .unresolved => true,
1311 .type_resolved => |r| r.type != resolved_ty.toIntern() or
1312 r.alignment != modifiers.alignment or
1313 r.@"linksection" != modifiers.@"linksection" or
1314 r.@"addrspace" != modifiers.@"addrspace" or
1315 r.is_const != is_const or
1316 r.is_extern_decl != is_extern_decl,
1317 .fully_resolved => |r| ip.typeOf(r.val) != resolved_ty.toIntern() or
1318 r.alignment != modifiers.alignment or
1319 r.@"linksection" != modifiers.@"linksection" or
1320 r.@"addrspace" != modifiers.@"addrspace" or
1321 zcu.navValIsConst(r.val) != is_const or
1322 (old_nav.getExtern(ip) != null) != is_extern_decl,
1323 };
1324
1325 if (!changed) return .{ .type_changed = false };
1326
1327 ip.resolveNavType(nav_id, .{
1328 .type = resolved_ty.toIntern(),
1329 .alignment = modifiers.alignment,
1330 .@"linksection" = modifiers.@"linksection",
1331 .@"addrspace" = modifiers.@"addrspace",
1332 .is_const = is_const,
1333 .is_threadlocal = zir_decl.is_threadlocal,
1334 .is_extern_decl = is_extern_decl,
1335 });
1336
1337 return .{ .type_changed = true };
11351338}
11361339
11371340pub fn ensureFuncBodyUpToDate(pt: Zcu.PerThread, maybe_coerced_func_index: InternPool.Index) Zcu.SemaError!void {
......@@ -1144,6 +1347,8 @@ pub fn ensureFuncBodyUpToDate(pt: Zcu.PerThread, maybe_coerced_func_index: Inter
11441347 const gpa = zcu.gpa;
11451348 const ip = &zcu.intern_pool;
11461349
1350 _ = zcu.func_body_analysis_queued.swapRemove(maybe_coerced_func_index);
1351
11471352 // We only care about the uncoerced function.
11481353 const func_index = ip.unwrapCoercedFunc(maybe_coerced_func_index);
11491354 const anal_unit: AnalUnit = .wrap(.{ .func = func_index });
......@@ -1171,11 +1376,7 @@ pub fn ensureFuncBodyUpToDate(pt: Zcu.PerThread, maybe_coerced_func_index: Inter
11711376 if (prev_failed) {
11721377 return error.AnalysisFail;
11731378 }
1174 switch (func.analysisUnordered(ip).state) {
1175 .unreferenced => {}, // this is the first reference
1176 .queued => {}, // we're waiting on first-time analysis
1177 .analyzed => return, // up-to-date
1178 }
1379 if (func.analysisUnordered(ip).is_analyzed) return;
11791380 }
11801381
11811382 const func_prog_node = zcu.sema_prog_node.start(ip.getNav(func.owner_nav).fqn.toSlice(ip), 0);
......@@ -1236,7 +1437,7 @@ fn analyzeFuncBody(
12361437 if (func.generic_owner == .none) {
12371438 // Among another things, this ensures that the function's `zir_body_inst` is correct.
12381439 try pt.ensureNavValUpToDate(func.owner_nav);
1239 if (ip.getNav(func.owner_nav).status.resolved.val != func_index) {
1440 if (ip.getNav(func.owner_nav).status.fully_resolved.val != func_index) {
12401441 // This function is no longer referenced! There's no point in re-analyzing it.
12411442 // Just mark a transitive failure and move on.
12421443 return error.AnalysisFail;
......@@ -1245,7 +1446,7 @@ fn analyzeFuncBody(
12451446 const go_nav = zcu.funcInfo(func.generic_owner).owner_nav;
12461447 // Among another things, this ensures that the function's `zir_body_inst` is correct.
12471448 try pt.ensureNavValUpToDate(go_nav);
1248 if (ip.getNav(go_nav).status.resolved.val != func.generic_owner) {
1449 if (ip.getNav(go_nav).status.fully_resolved.val != func.generic_owner) {
12491450 // The generic owner is no longer referenced, so this function is also unreferenced.
12501451 // There's no point in re-analyzing it. Just mark a transitive failure and move on.
12511452 return error.AnalysisFail;
......@@ -2172,7 +2373,7 @@ fn analyzeFnBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaE
21722373 try zcu.analysis_in_progress.put(gpa, anal_unit, {});
21732374 errdefer _ = zcu.analysis_in_progress.swapRemove(anal_unit);
21742375
2175 func.setAnalysisState(ip, .analyzed);
2376 func.setAnalyzed(ip);
21762377 if (func.analysisUnordered(ip).inferred_error_set) {
21772378 func.setResolvedErrorSet(ip, .none);
21782379 }
......@@ -2550,8 +2751,8 @@ fn processExportsInner(
25502751 if (zcu.transitive_failed_analysis.contains(unit)) break :failed true;
25512752 }
25522753 const val = switch (nav.status) {
2553 .unresolved => break :failed true,
2554 .resolved => |r| Value.fromInterned(r.val),
2754 .unresolved, .type_resolved => break :failed true,
2755 .fully_resolved => |r| Value.fromInterned(r.val),
25552756 };
25562757 // If the value is a function, we also need to check if that function succeeded analysis.
25572758 if (val.typeOf(zcu).zigTypeTag(zcu) == .@"fn") {
......@@ -3256,30 +3457,29 @@ pub fn getBuiltinNav(pt: Zcu.PerThread, name: []const u8) Allocator.Error!Intern
32563457 const builtin_nav = std_namespace.pub_decls.getKeyAdapted(builtin_str, Zcu.Namespace.NameAdapter{ .zcu = zcu }) orelse
32573458 @panic("lib/std.zig is corrupt and missing 'builtin'");
32583459 pt.ensureNavValUpToDate(builtin_nav) catch @panic("std.builtin is corrupt");
3259 const builtin_type = Type.fromInterned(ip.getNav(builtin_nav).status.resolved.val);
3460 const builtin_type = Type.fromInterned(ip.getNav(builtin_nav).status.fully_resolved.val);
32603461 const builtin_namespace = zcu.namespacePtr(builtin_type.getNamespace(zcu).unwrap() orelse @panic("std.builtin is corrupt"));
32613462 const name_str = try ip.getOrPutString(gpa, pt.tid, name, .no_embedded_nulls);
32623463 return builtin_namespace.pub_decls.getKeyAdapted(name_str, Zcu.Namespace.NameAdapter{ .zcu = zcu }) orelse @panic("lib/std/builtin.zig is corrupt");
32633464}
32643465
3265pub fn navPtrType(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) Allocator.Error!Type {
3466pub fn navPtrType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Allocator.Error!Type {
32663467 const zcu = pt.zcu;
32673468 const ip = &zcu.intern_pool;
3268 const r = ip.getNav(nav_index).status.resolved;
3269 const ty = Value.fromInterned(r.val).typeOf(zcu);
3469 const ty, const alignment, const @"addrspace", const is_const = switch (ip.getNav(nav_id).status) {
3470 .unresolved => unreachable,
3471 .type_resolved => |r| .{ r.type, r.alignment, r.@"addrspace", r.is_const },
3472 .fully_resolved => |r| .{ ip.typeOf(r.val), r.alignment, r.@"addrspace", zcu.navValIsConst(r.val) },
3473 };
32703474 return pt.ptrType(.{
3271 .child = ty.toIntern(),
3475 .child = ty,
32723476 .flags = .{
3273 .alignment = if (r.alignment == ty.abiAlignment(zcu))
3477 .alignment = if (alignment == Type.fromInterned(ty).abiAlignment(zcu))
32743478 .none
32753479 else
3276 r.alignment,
3277 .address_space = r.@"addrspace",
3278 .is_const = switch (ip.indexToKey(r.val)) {
3279 .variable => false,
3280 .@"extern" => |e| e.is_const,
3281 else => true,
3282 },
3480 alignment,
3481 .address_space = @"addrspace",
3482 .is_const = is_const,
32833483 },
32843484 });
32853485}
......@@ -3299,9 +3499,13 @@ pub fn getExtern(pt: Zcu.PerThread, key: InternPool.Key.Extern) Allocator.Error!
32993499// TODO: this shouldn't need a `PerThread`! Fix the signature of `Type.abiAlignment`.
33003500pub fn navAlignment(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) InternPool.Alignment {
33013501 const zcu = pt.zcu;
3302 const r = zcu.intern_pool.getNav(nav_index).status.resolved;
3303 if (r.alignment != .none) return r.alignment;
3304 return Value.fromInterned(r.val).typeOf(zcu).abiAlignment(zcu);
3502 const ty: Type, const alignment = switch (zcu.intern_pool.getNav(nav_index).status) {
3503 .unresolved => unreachable,
3504 .type_resolved => |r| .{ .fromInterned(r.type), r.alignment },
3505 .fully_resolved => |r| .{ Value.fromInterned(r.val).typeOf(zcu), r.alignment },
3506 };
3507 if (alignment != .none) return alignment;
3508 return ty.abiAlignment(zcu);
33053509}
33063510
33073511/// Given a container type requiring resolution, ensures that it is up-to-date.
src/arch/wasm/CodeGen.zig+1-9
......@@ -3218,15 +3218,7 @@ fn lowerNavRef(func: *CodeGen, nav_index: InternPool.Nav.Index, offset: u32) Inn
32183218 const zcu = pt.zcu;
32193219 const ip = &zcu.intern_pool;
32203220
3221 // check if decl is an alias to a function, in which case we
3222 // want to lower the actual decl, rather than the alias itself.
3223 const owner_nav = switch (ip.indexToKey(zcu.navValue(nav_index).toIntern())) {
3224 .func => |function| function.owner_nav,
3225 .variable => |variable| variable.owner_nav,
3226 .@"extern" => |@"extern"| @"extern".owner_nav,
3227 else => nav_index,
3228 };
3229 const nav_ty = ip.getNav(owner_nav).typeOf(ip);
3221 const nav_ty = ip.getNav(nav_index).typeOf(ip);
32303222 if (!ip.isFunctionType(nav_ty) and !Type.fromInterned(nav_ty).hasRuntimeBitsIgnoreComptime(zcu)) {
32313223 return .{ .imm32 = 0xaaaaaaaa };
32323224 }
src/codegen.zig+9-8
......@@ -817,7 +817,7 @@ fn genNavRef(
817817 pt: Zcu.PerThread,
818818 src_loc: Zcu.LazySrcLoc,
819819 val: Value,
820 ref_nav_index: InternPool.Nav.Index,
820 nav_index: InternPool.Nav.Index,
821821 target: std.Target,
822822) CodeGenError!GenResult {
823823 const zcu = pt.zcu;
......@@ -851,14 +851,15 @@ fn genNavRef(
851851 }
852852 }
853853
854 const nav_index, const is_extern, const lib_name, const is_threadlocal = switch (ip.indexToKey(zcu.navValue(ref_nav_index).toIntern())) {
855 .func => |func| .{ func.owner_nav, false, .none, false },
856 .variable => |variable| .{ variable.owner_nav, false, .none, variable.is_threadlocal },
857 .@"extern" => |@"extern"| .{ @"extern".owner_nav, true, @"extern".lib_name, @"extern".is_threadlocal },
858 else => .{ ref_nav_index, false, .none, false },
859 };
854 const nav = ip.getNav(nav_index);
855
856 const is_extern, const lib_name, const is_threadlocal = if (nav.getExtern(ip)) |e|
857 .{ true, e.lib_name, e.is_threadlocal }
858 else
859 .{ false, .none, nav.isThreadlocal(ip) };
860
860861 const single_threaded = zcu.navFileScope(nav_index).mod.single_threaded;
861 const name = ip.getNav(nav_index).name;
862 const name = nav.name;
862863 if (lf.cast(.elf)) |elf_file| {
863864 const zo = elf_file.zigObjectPtr().?;
864865 if (is_extern) {
src/codegen/c.zig+32-29
......@@ -770,11 +770,14 @@ pub const DeclGen = struct {
770770 const ctype_pool = &dg.ctype_pool;
771771
772772 // Chase function values in order to be able to reference the original function.
773 const owner_nav = switch (ip.indexToKey(zcu.navValue(nav_index).toIntern())) {
774 .variable => |variable| variable.owner_nav,
775 .func => |func| func.owner_nav,
776 .@"extern" => |@"extern"| @"extern".owner_nav,
777 else => nav_index,
773 const owner_nav = switch (ip.getNav(nav_index).status) {
774 .unresolved => unreachable,
775 .type_resolved => nav_index, // this can't be an extern or a function
776 .fully_resolved => |r| switch (ip.indexToKey(r.val)) {
777 .func => |f| f.owner_nav,
778 .@"extern" => |e| e.owner_nav,
779 else => nav_index,
780 },
778781 };
779782
780783 // Render an undefined pointer if we have a pointer to a zero-bit or comptime type.
......@@ -2237,7 +2240,7 @@ pub const DeclGen = struct {
22372240 Type.fromInterned(nav.typeOf(ip)),
22382241 .{ .nav = nav_index },
22392242 CQualifiers.init(.{ .@"const" = flags.is_const }),
2240 nav.status.resolved.alignment,
2243 nav.getAlignment(),
22412244 .complete,
22422245 );
22432246 try fwd.writeAll(";\n");
......@@ -2246,19 +2249,19 @@ pub const DeclGen = struct {
22462249 fn renderNavName(dg: *DeclGen, writer: anytype, nav_index: InternPool.Nav.Index) !void {
22472250 const zcu = dg.pt.zcu;
22482251 const ip = &zcu.intern_pool;
2249 switch (ip.indexToKey(zcu.navValue(nav_index).toIntern())) {
2250 .@"extern" => |@"extern"| try writer.print("{ }", .{
2252 const nav = ip.getNav(nav_index);
2253 if (nav.getExtern(ip)) |@"extern"| {
2254 try writer.print("{ }", .{
22512255 fmtIdent(ip.getNav(@"extern".owner_nav).name.toSlice(ip)),
2252 }),
2253 else => {
2254 // MSVC has a limit of 4095 character token length limit, and fmtIdent can (worst case),
2255 // expand to 3x the length of its input, but let's cut it off at a much shorter limit.
2256 const fqn_slice = ip.getNav(nav_index).fqn.toSlice(ip);
2257 try writer.print("{}__{d}", .{
2258 fmtIdent(fqn_slice[0..@min(fqn_slice.len, 100)]),
2259 @intFromEnum(nav_index),
2260 });
2261 },
2256 });
2257 } else {
2258 // MSVC has a limit of 4095 character token length limit, and fmtIdent can (worst case),
2259 // expand to 3x the length of its input, but let's cut it off at a much shorter limit.
2260 const fqn_slice = ip.getNav(nav_index).fqn.toSlice(ip);
2261 try writer.print("{}__{d}", .{
2262 fmtIdent(fqn_slice[0..@min(fqn_slice.len, 100)]),
2263 @intFromEnum(nav_index),
2264 });
22622265 }
22632266 }
22642267
......@@ -2826,7 +2829,7 @@ pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFn
28262829
28272830 const fwd = o.dg.fwdDeclWriter();
28282831 try fwd.print("static zig_{s} ", .{@tagName(key)});
2829 try o.dg.renderFunctionSignature(fwd, fn_val, ip.getNav(fn_nav_index).status.resolved.alignment, .forward, .{
2832 try o.dg.renderFunctionSignature(fwd, fn_val, ip.getNav(fn_nav_index).getAlignment(), .forward, .{
28302833 .fmt_ctype_pool_string = fn_name,
28312834 });
28322835 try fwd.writeAll(";\n");
......@@ -2867,13 +2870,13 @@ pub fn genFunc(f: *Function) !void {
28672870 try o.dg.renderFunctionSignature(
28682871 fwd,
28692872 nav_val,
2870 nav.status.resolved.alignment,
2873 nav.status.fully_resolved.alignment,
28712874 .forward,
28722875 .{ .nav = nav_index },
28732876 );
28742877 try fwd.writeAll(";\n");
28752878
2876 if (nav.status.resolved.@"linksection".toSlice(ip)) |s|
2879 if (nav.status.fully_resolved.@"linksection".toSlice(ip)) |s|
28772880 try o.writer().print("zig_linksection_fn({s}) ", .{fmtStringLiteral(s, null)});
28782881 try o.dg.renderFunctionSignature(
28792882 o.writer(),
......@@ -2952,7 +2955,7 @@ pub fn genDecl(o: *Object) !void {
29522955 const nav_ty = Type.fromInterned(nav.typeOf(ip));
29532956
29542957 if (!nav_ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) return;
2955 switch (ip.indexToKey(nav.status.resolved.val)) {
2958 switch (ip.indexToKey(nav.status.fully_resolved.val)) {
29562959 .@"extern" => |@"extern"| {
29572960 if (!ip.isFunctionType(nav_ty.toIntern())) return o.dg.renderFwdDecl(o.dg.pass.nav, .{
29582961 .is_extern = true,
......@@ -2965,8 +2968,8 @@ pub fn genDecl(o: *Object) !void {
29652968 try fwd.writeAll("zig_extern ");
29662969 try o.dg.renderFunctionSignature(
29672970 fwd,
2968 Value.fromInterned(nav.status.resolved.val),
2969 nav.status.resolved.alignment,
2971 Value.fromInterned(nav.status.fully_resolved.val),
2972 nav.status.fully_resolved.alignment,
29702973 .forward,
29712974 .{ .@"export" = .{
29722975 .main_name = nav.name,
......@@ -2985,14 +2988,14 @@ pub fn genDecl(o: *Object) !void {
29852988 const w = o.writer();
29862989 if (variable.is_weak_linkage) try w.writeAll("zig_weak_linkage ");
29872990 if (variable.is_threadlocal and !o.dg.mod.single_threaded) try w.writeAll("zig_threadlocal ");
2988 if (nav.status.resolved.@"linksection".toSlice(&zcu.intern_pool)) |s|
2991 if (nav.status.fully_resolved.@"linksection".toSlice(&zcu.intern_pool)) |s|
29892992 try w.print("zig_linksection({s}) ", .{fmtStringLiteral(s, null)});
29902993 try o.dg.renderTypeAndName(
29912994 w,
29922995 nav_ty,
29932996 .{ .nav = o.dg.pass.nav },
29942997 .{},
2995 nav.status.resolved.alignment,
2998 nav.status.fully_resolved.alignment,
29962999 .complete,
29973000 );
29983001 try w.writeAll(" = ");
......@@ -3002,10 +3005,10 @@ pub fn genDecl(o: *Object) !void {
30023005 },
30033006 else => try genDeclValue(
30043007 o,
3005 Value.fromInterned(nav.status.resolved.val),
3008 Value.fromInterned(nav.status.fully_resolved.val),
30063009 .{ .nav = o.dg.pass.nav },
3007 nav.status.resolved.alignment,
3008 nav.status.resolved.@"linksection",
3010 nav.status.fully_resolved.alignment,
3011 nav.status.fully_resolved.@"linksection",
30093012 ),
30103013 }
30113014}
src/codegen/llvm.zig+28-36
......@@ -1476,7 +1476,7 @@ pub const Object = struct {
14761476 } }, &o.builder);
14771477 }
14781478
1479 if (nav.status.resolved.@"linksection".toSlice(ip)) |section|
1479 if (nav.status.fully_resolved.@"linksection".toSlice(ip)) |section|
14801480 function_index.setSection(try o.builder.string(section), &o.builder);
14811481
14821482 var deinit_wip = true;
......@@ -1684,7 +1684,7 @@ pub const Object = struct {
16841684 const file = try o.getDebugFile(file_scope);
16851685
16861686 const line_number = zcu.navSrcLine(func.owner_nav) + 1;
1687 const is_internal_linkage = ip.indexToKey(nav.status.resolved.val) != .@"extern";
1687 const is_internal_linkage = ip.indexToKey(nav.status.fully_resolved.val) != .@"extern";
16881688 const debug_decl_type = try o.lowerDebugType(fn_ty);
16891689
16901690 const subprogram = try o.builder.debugSubprogram(
......@@ -2928,9 +2928,7 @@ pub const Object = struct {
29282928 const gpa = o.gpa;
29292929 const nav = ip.getNav(nav_index);
29302930 const owner_mod = zcu.navFileScope(nav_index).mod;
2931 const resolved = nav.status.resolved;
2932 const val = Value.fromInterned(resolved.val);
2933 const ty = val.typeOf(zcu);
2931 const ty: Type = .fromInterned(nav.typeOf(ip));
29342932 const gop = try o.nav_map.getOrPut(gpa, nav_index);
29352933 if (gop.found_existing) return gop.value_ptr.ptr(&o.builder).kind.function;
29362934
......@@ -2938,14 +2936,14 @@ pub const Object = struct {
29382936 const target = owner_mod.resolved_target.result;
29392937 const sret = firstParamSRet(fn_info, zcu, target);
29402938
2941 const is_extern, const lib_name = switch (ip.indexToKey(val.toIntern())) {
2942 .@"extern" => |@"extern"| .{ true, @"extern".lib_name },
2943 else => .{ false, .none },
2944 };
2939 const is_extern, const lib_name = if (nav.getExtern(ip)) |@"extern"|
2940 .{ true, @"extern".lib_name }
2941 else
2942 .{ false, .none };
29452943 const function_index = try o.builder.addFunction(
29462944 try o.lowerType(ty),
29472945 try o.builder.strtabString((if (is_extern) nav.name else nav.fqn).toSlice(ip)),
2948 toLlvmAddressSpace(resolved.@"addrspace", target),
2946 toLlvmAddressSpace(nav.getAddrspace(), target),
29492947 );
29502948 gop.value_ptr.* = function_index.ptrConst(&o.builder).global;
29512949
......@@ -3063,8 +3061,8 @@ pub const Object = struct {
30633061 }
30643062 }
30653063
3066 if (resolved.alignment != .none)
3067 function_index.setAlignment(resolved.alignment.toLlvm(), &o.builder);
3064 if (nav.getAlignment() != .none)
3065 function_index.setAlignment(nav.getAlignment().toLlvm(), &o.builder);
30683066
30693067 // Function attributes that are independent of analysis results of the function body.
30703068 try o.addCommonFnAttributes(
......@@ -3249,17 +3247,21 @@ pub const Object = struct {
32493247 const zcu = pt.zcu;
32503248 const ip = &zcu.intern_pool;
32513249 const nav = ip.getNav(nav_index);
3252 const resolved = nav.status.resolved;
3253 const is_extern, const is_threadlocal, const is_weak_linkage, const is_dll_import = switch (ip.indexToKey(resolved.val)) {
3254 .variable => |variable| .{ false, variable.is_threadlocal, variable.is_weak_linkage, false },
3255 .@"extern" => |@"extern"| .{ true, @"extern".is_threadlocal, @"extern".is_weak_linkage, @"extern".is_dll_import },
3256 else => .{ false, false, false, false },
3250 const is_extern, const is_threadlocal, const is_weak_linkage, const is_dll_import = switch (nav.status) {
3251 .unresolved => unreachable,
3252 .fully_resolved => |r| switch (ip.indexToKey(r.val)) {
3253 .variable => |variable| .{ false, variable.is_threadlocal, variable.is_weak_linkage, false },
3254 .@"extern" => |@"extern"| .{ true, @"extern".is_threadlocal, @"extern".is_weak_linkage, @"extern".is_dll_import },
3255 else => .{ false, false, false, false },
3256 },
3257 // This means it's a source declaration which is not `extern`!
3258 .type_resolved => |r| .{ false, r.is_threadlocal, false, false },
32573259 };
32583260
32593261 const variable_index = try o.builder.addVariable(
32603262 try o.builder.strtabString((if (is_extern) nav.name else nav.fqn).toSlice(ip)),
32613263 try o.lowerType(Type.fromInterned(nav.typeOf(ip))),
3262 toLlvmGlobalAddressSpace(resolved.@"addrspace", zcu.getTarget()),
3264 toLlvmGlobalAddressSpace(nav.getAddrspace(), zcu.getTarget()),
32633265 );
32643266 gop.value_ptr.* = variable_index.ptrConst(&o.builder).global;
32653267
......@@ -4528,20 +4530,10 @@ pub const Object = struct {
45284530 const zcu = pt.zcu;
45294531 const ip = &zcu.intern_pool;
45304532
4531 // In the case of something like:
4532 // fn foo() void {}
4533 // const bar = foo;
4534 // ... &bar;
4535 // `bar` is just an alias and we actually want to lower a reference to `foo`.
4536 const owner_nav_index = switch (ip.indexToKey(zcu.navValue(nav_index).toIntern())) {
4537 .func => |func| func.owner_nav,
4538 .@"extern" => |@"extern"| @"extern".owner_nav,
4539 else => nav_index,
4540 };
4541 const owner_nav = ip.getNav(owner_nav_index);
4533 const nav = ip.getNav(nav_index);
45424534
4543 const nav_ty = Type.fromInterned(owner_nav.typeOf(ip));
4544 const ptr_ty = try pt.navPtrType(owner_nav_index);
4535 const nav_ty = Type.fromInterned(nav.typeOf(ip));
4536 const ptr_ty = try pt.navPtrType(nav_index);
45454537
45464538 const is_fn_body = nav_ty.zigTypeTag(zcu) == .@"fn";
45474539 if ((!is_fn_body and !nav_ty.hasRuntimeBits(zcu)) or
......@@ -4551,13 +4543,13 @@ pub const Object = struct {
45514543 }
45524544
45534545 const llvm_global = if (is_fn_body)
4554 (try o.resolveLlvmFunction(owner_nav_index)).ptrConst(&o.builder).global
4546 (try o.resolveLlvmFunction(nav_index)).ptrConst(&o.builder).global
45554547 else
4556 (try o.resolveGlobalNav(owner_nav_index)).ptrConst(&o.builder).global;
4548 (try o.resolveGlobalNav(nav_index)).ptrConst(&o.builder).global;
45574549
45584550 const llvm_val = try o.builder.convConst(
45594551 llvm_global.toConst(),
4560 try o.builder.ptrType(toLlvmAddressSpace(owner_nav.status.resolved.@"addrspace", zcu.getTarget())),
4552 try o.builder.ptrType(toLlvmAddressSpace(nav.getAddrspace(), zcu.getTarget())),
45614553 );
45624554
45634555 return o.builder.convConst(llvm_val, try o.lowerType(ptr_ty));
......@@ -4799,7 +4791,7 @@ pub const NavGen = struct {
47994791 const ip = &zcu.intern_pool;
48004792 const nav_index = ng.nav_index;
48014793 const nav = ip.getNav(nav_index);
4802 const resolved = nav.status.resolved;
4794 const resolved = nav.status.fully_resolved;
48034795
48044796 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)) {
48054797 .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 {
57655757 const msg_nav_index = zcu.panic_messages[@intFromEnum(panic_id)].unwrap().?;
57665758 const msg_nav = ip.getNav(msg_nav_index);
57675759 const msg_len = Type.fromInterned(msg_nav.typeOf(ip)).childType(zcu).arrayLen(zcu);
5768 const msg_ptr = try o.lowerValue(msg_nav.status.resolved.val);
5760 const msg_ptr = try o.lowerValue(msg_nav.status.fully_resolved.val);
57695761 const null_opt_addr_global = try fg.resolveNullOptUsize();
57705762 const target = zcu.getTarget();
57715763 const llvm_usize = try o.lowerType(Type.usize);
src/codegen/spirv.zig+16-13
......@@ -268,7 +268,7 @@ pub const Object = struct {
268268 // TODO: Extern fn?
269269 const kind: SpvModule.Decl.Kind = if (ip.isFunctionType(nav.typeOf(ip)))
270270 .func
271 else switch (nav.status.resolved.@"addrspace") {
271 else switch (nav.getAddrspace()) {
272272 .generic => .invocation_global,
273273 else => .global,
274274 };
......@@ -1279,17 +1279,20 @@ const NavGen = struct {
12791279 const ip = &zcu.intern_pool;
12801280 const ty_id = try self.resolveType(ty, .direct);
12811281 const nav = ip.getNav(nav_index);
1282 const nav_val = zcu.navValue(nav_index);
1283 const nav_ty = nav_val.typeOf(zcu);
1284
1285 switch (ip.indexToKey(nav_val.toIntern())) {
1286 .func => {
1287 // TODO: Properly lower function pointers. For now we are going to hack around it and
1288 // just generate an empty pointer. Function pointers are represented by a pointer to usize.
1289 return try self.spv.constUndef(ty_id);
1282 const nav_ty: Type = .fromInterned(nav.typeOf(ip));
1283
1284 switch (nav.status) {
1285 .unresolved => unreachable,
1286 .type_resolved => {}, // this is not a function or extern
1287 .fully_resolved => |r| switch (ip.indexToKey(r.val)) {
1288 .func => {
1289 // TODO: Properly lower function pointers. For now we are going to hack around it and
1290 // just generate an empty pointer. Function pointers are represented by a pointer to usize.
1291 return try self.spv.constUndef(ty_id);
1292 },
1293 .@"extern" => if (ip.isFunctionType(nav_ty.toIntern())) @panic("TODO"),
1294 else => {},
12901295 },
1291 .@"extern" => assert(!ip.isFunctionType(nav_ty.toIntern())), // TODO
1292 else => {},
12931296 }
12941297
12951298 if (!nav_ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) {
......@@ -1305,7 +1308,7 @@ const NavGen = struct {
13051308 .global, .invocation_global => spv_decl.result_id,
13061309 };
13071310
1308 const storage_class = self.spvStorageClass(nav.status.resolved.@"addrspace");
1311 const storage_class = self.spvStorageClass(nav.getAddrspace());
13091312 try self.addFunctionDep(spv_decl_index, storage_class);
13101313
13111314 const decl_ptr_ty_id = try self.ptrType(nav_ty, storage_class);
......@@ -3182,7 +3185,7 @@ const NavGen = struct {
31823185 };
31833186 assert(maybe_init_val == null); // TODO
31843187
3185 const storage_class = self.spvStorageClass(nav.status.resolved.@"addrspace");
3188 const storage_class = self.spvStorageClass(nav.getAddrspace());
31863189 assert(storage_class != .Generic); // These should be instance globals
31873190
31883191 const ptr_ty_id = try self.ptrType(ty, storage_class);
src/link.zig+1-1
......@@ -692,7 +692,7 @@ pub const File = struct {
692692 /// May be called before or after updateExports for any given Nav.
693693 pub fn updateNav(base: *File, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) UpdateNavError!void {
694694 const nav = pt.zcu.intern_pool.getNav(nav_index);
695 assert(nav.status == .resolved);
695 assert(nav.status == .fully_resolved);
696696 switch (base.tag) {
697697 inline else => |tag| {
698698 dev.check(tag.devFeature());
src/link/C.zig+9-11
......@@ -217,7 +217,7 @@ pub fn updateFunc(
217217 .mod = zcu.navFileScope(func.owner_nav).mod,
218218 .error_msg = null,
219219 .pass = .{ .nav = func.owner_nav },
220 .is_naked_fn = zcu.navValue(func.owner_nav).typeOf(zcu).fnCallingConvention(zcu) == .naked,
220 .is_naked_fn = Type.fromInterned(func.ty).fnCallingConvention(zcu) == .naked,
221221 .fwd_decl = fwd_decl.toManaged(gpa),
222222 .ctype_pool = ctype_pool.*,
223223 .scratch = .{},
......@@ -320,11 +320,11 @@ pub fn updateNav(self: *C, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !
320320 const ip = &zcu.intern_pool;
321321
322322 const nav = ip.getNav(nav_index);
323 const nav_init = switch (ip.indexToKey(nav.status.resolved.val)) {
323 const nav_init = switch (ip.indexToKey(nav.status.fully_resolved.val)) {
324324 .func => return,
325325 .@"extern" => .none,
326326 .variable => |variable| variable.init,
327 else => nav.status.resolved.val,
327 else => nav.status.fully_resolved.val,
328328 };
329329 if (nav_init != .none and !Value.fromInterned(nav_init).typeOf(zcu).hasRuntimeBits(zcu)) return;
330330
......@@ -499,7 +499,7 @@ pub fn flushModule(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
499499 av_block,
500500 self.exported_navs.getPtr(nav),
501501 export_names,
502 if (ip.indexToKey(zcu.navValue(nav).toIntern()) == .@"extern")
502 if (ip.getNav(nav).getExtern(ip) != null)
503503 ip.getNav(nav).name.toOptional()
504504 else
505505 .none,
......@@ -544,13 +544,11 @@ pub fn flushModule(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
544544 },
545545 self.getString(av_block.code),
546546 );
547 for (self.navs.keys(), self.navs.values()) |nav, av_block| f.appendCodeAssumeCapacity(
548 if (self.exported_navs.contains(nav)) .default else switch (ip.indexToKey(zcu.navValue(nav).toIntern())) {
549 .@"extern" => .zig_extern,
550 else => .static,
551 },
552 self.getString(av_block.code),
553 );
547 for (self.navs.keys(), self.navs.values()) |nav, av_block| f.appendCodeAssumeCapacity(storage: {
548 if (self.exported_navs.contains(nav)) break :storage .default;
549 if (ip.getNav(nav).getExtern(ip) != null) break :storage .zig_extern;
550 break :storage .static;
551 }, self.getString(av_block.code));
554552
555553 const file = self.base.file.?;
556554 try file.setEndPos(f.file_size);
src/link/Coff.zig+11-6
......@@ -1110,6 +1110,8 @@ pub fn updateFunc(coff: *Coff, pt: Zcu.PerThread, func_index: InternPool.Index,
11101110 const atom_index = try coff.getOrCreateAtomForNav(func.owner_nav);
11111111 coff.freeRelocations(atom_index);
11121112
1113 coff.navs.getPtr(func.owner_nav).?.section = coff.text_section_index.?;
1114
11131115 var code_buffer = std.ArrayList(u8).init(gpa);
11141116 defer code_buffer.deinit();
11151117
......@@ -1223,6 +1225,8 @@ pub fn updateNav(
12231225 coff.freeRelocations(atom_index);
12241226 const atom = coff.getAtom(atom_index);
12251227
1228 coff.navs.getPtr(nav_index).?.section = coff.getNavOutputSection(nav_index);
1229
12261230 var code_buffer = std.ArrayList(u8).init(gpa);
12271231 defer code_buffer.deinit();
12281232
......@@ -1342,7 +1346,8 @@ pub fn getOrCreateAtomForNav(coff: *Coff, nav_index: InternPool.Nav.Index) !Atom
13421346 if (!gop.found_existing) {
13431347 gop.value_ptr.* = .{
13441348 .atom = try coff.createAtom(),
1345 .section = coff.getNavOutputSection(nav_index),
1349 // If necessary, this will be modified by `updateNav` or `updateFunc`.
1350 .section = coff.rdata_section_index.?,
13461351 .exports = .{},
13471352 };
13481353 }
......@@ -1355,7 +1360,7 @@ fn getNavOutputSection(coff: *Coff, nav_index: InternPool.Nav.Index) u16 {
13551360 const nav = ip.getNav(nav_index);
13561361 const ty = Type.fromInterned(nav.typeOf(ip));
13571362 const zig_ty = ty.zigTypeTag(zcu);
1358 const val = Value.fromInterned(nav.status.resolved.val);
1363 const val = Value.fromInterned(nav.status.fully_resolved.val);
13591364 const index: u16 = blk: {
13601365 if (val.isUndefDeep(zcu)) {
13611366 // TODO in release-fast and release-small, we should put undef in .bss
......@@ -2348,10 +2353,10 @@ pub fn getNavVAddr(
23482353 const ip = &zcu.intern_pool;
23492354 const nav = ip.getNav(nav_index);
23502355 log.debug("getNavVAddr {}({d})", .{ nav.fqn.fmt(ip), nav_index });
2351 const sym_index = switch (ip.indexToKey(nav.status.resolved.val)) {
2352 .@"extern" => |@"extern"| try coff.getGlobalSymbol(nav.name.toSlice(ip), @"extern".lib_name.toSlice(ip)),
2353 else => coff.getAtom(try coff.getOrCreateAtomForNav(nav_index)).getSymbolIndex().?,
2354 };
2356 const sym_index = if (nav.getExtern(ip)) |e|
2357 try coff.getGlobalSymbol(nav.name.toSlice(ip), e.lib_name.toSlice(ip))
2358 else
2359 coff.getAtom(try coff.getOrCreateAtomForNav(nav_index)).getSymbolIndex().?;
23552360 const atom_index = coff.getAtomIndexForSymbol(.{
23562361 .sym_index = reloc_info.parent.atom_index,
23572362 .file = null,
src/link/Dwarf.zig+5-5
......@@ -2281,7 +2281,7 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In
22812281 const nav_ty = nav_val.typeOf(zcu);
22822282 const nav_ty_reloc_index = try wip_nav.refForward();
22832283 try wip_nav.infoExprloc(.{ .addr = .{ .sym = sym_index } });
2284 try uleb128(diw, nav.status.resolved.alignment.toByteUnits() orelse
2284 try uleb128(diw, nav.status.fully_resolved.alignment.toByteUnits() orelse
22852285 nav_ty.abiAlignment(zcu).toByteUnits().?);
22862286 try diw.writeByte(@intFromBool(false));
22872287 wip_nav.finishForward(nav_ty_reloc_index);
......@@ -2313,7 +2313,7 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In
23132313 try wip_nav.refType(ty);
23142314 const addr: Loc = .{ .addr = .{ .sym = sym_index } };
23152315 try wip_nav.infoExprloc(if (variable.is_threadlocal) .{ .form_tls_address = &addr } else addr);
2316 try uleb128(diw, nav.status.resolved.alignment.toByteUnits() orelse
2316 try uleb128(diw, nav.status.fully_resolved.alignment.toByteUnits() orelse
23172317 ty.abiAlignment(zcu).toByteUnits().?);
23182318 try diw.writeByte(@intFromBool(false));
23192319 },
......@@ -2388,7 +2388,7 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In
23882388 wip_nav.func_high_pc = @intCast(wip_nav.debug_info.items.len);
23892389 try diw.writeInt(u32, 0, dwarf.endian);
23902390 const target = file.mod.resolved_target.result;
2391 try uleb128(diw, switch (nav.status.resolved.alignment) {
2391 try uleb128(diw, switch (nav.status.fully_resolved.alignment) {
23922392 .none => target_info.defaultFunctionAlignment(target),
23932393 else => |a| a.maxStrict(target_info.minFunctionAlignment(target)),
23942394 }.toByteUnits().?);
......@@ -2952,7 +2952,7 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool
29522952 const nav_ty = nav_val.typeOf(zcu);
29532953 try wip_nav.refType(nav_ty);
29542954 try wip_nav.blockValue(nav_src_loc, nav_val);
2955 try uleb128(diw, nav.status.resolved.alignment.toByteUnits() orelse
2955 try uleb128(diw, nav.status.fully_resolved.alignment.toByteUnits() orelse
29562956 nav_ty.abiAlignment(zcu).toByteUnits().?);
29572957 try diw.writeByte(@intFromBool(false));
29582958 },
......@@ -2977,7 +2977,7 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool
29772977 try wip_nav.strp(nav.name.toSlice(ip));
29782978 try wip_nav.strp(nav.fqn.toSlice(ip));
29792979 const nav_ty_reloc_index = try wip_nav.refForward();
2980 try uleb128(diw, nav.status.resolved.alignment.toByteUnits() orelse
2980 try uleb128(diw, nav.status.fully_resolved.alignment.toByteUnits() orelse
29812981 nav_ty.abiAlignment(zcu).toByteUnits().?);
29822982 try diw.writeByte(@intFromBool(false));
29832983 if (has_runtime_bits) try wip_nav.blockValue(nav_src_loc, nav_val);
src/link/Elf/ZigObject.zig+10-15
......@@ -925,14 +925,11 @@ pub fn getNavVAddr(
925925 const ip = &zcu.intern_pool;
926926 const nav = ip.getNav(nav_index);
927927 log.debug("getNavVAddr {}({d})", .{ nav.fqn.fmt(ip), nav_index });
928 const this_sym_index = switch (ip.indexToKey(nav.status.resolved.val)) {
929 .@"extern" => |@"extern"| try self.getGlobalSymbol(
930 elf_file,
931 nav.name.toSlice(ip),
932 @"extern".lib_name.toSlice(ip),
933 ),
934 else => try self.getOrCreateMetadataForNav(zcu, nav_index),
935 };
928 const this_sym_index = if (nav.getExtern(ip)) |@"extern"| try self.getGlobalSymbol(
929 elf_file,
930 nav.name.toSlice(ip),
931 @"extern".lib_name.toSlice(ip),
932 ) else try self.getOrCreateMetadataForNav(zcu, nav_index);
936933 const this_sym = self.symbol(this_sym_index);
937934 const vaddr = this_sym.address(.{}, elf_file);
938935 switch (reloc_info.parent) {
......@@ -1107,15 +1104,13 @@ pub fn freeNav(self: *ZigObject, elf_file: *Elf, nav_index: InternPool.Nav.Index
11071104
11081105pub fn getOrCreateMetadataForNav(self: *ZigObject, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Symbol.Index {
11091106 const gpa = zcu.gpa;
1107 const ip = &zcu.intern_pool;
11101108 const gop = try self.navs.getOrPut(gpa, nav_index);
11111109 if (!gop.found_existing) {
11121110 const symbol_index = try self.newSymbolWithAtom(gpa, 0);
1113 const nav_val = Value.fromInterned(zcu.intern_pool.getNav(nav_index).status.resolved.val);
11141111 const sym = self.symbol(symbol_index);
1115 if (nav_val.getVariable(zcu)) |variable| {
1116 if (variable.is_threadlocal and zcu.comp.config.any_non_single_threaded) {
1117 sym.flags.is_tls = true;
1118 }
1112 if (ip.getNav(nav_index).isThreadlocal(ip) and zcu.comp.config.any_non_single_threaded) {
1113 sym.flags.is_tls = true;
11191114 }
11201115 gop.value_ptr.* = .{ .symbol_index = symbol_index };
11211116 }
......@@ -1547,7 +1542,7 @@ pub fn updateNav(
15471542
15481543 log.debug("updateNav {}({d})", .{ nav.fqn.fmt(ip), nav_index });
15491544
1550 const nav_init = switch (ip.indexToKey(nav.status.resolved.val)) {
1545 const nav_init = switch (ip.indexToKey(nav.status.fully_resolved.val)) {
15511546 .func => .none,
15521547 .variable => |variable| variable.init,
15531548 .@"extern" => |@"extern"| {
......@@ -1560,7 +1555,7 @@ pub fn updateNav(
15601555 self.symbol(sym_index).flags.is_extern_ptr = true;
15611556 return;
15621557 },
1563 else => nav.status.resolved.val,
1558 else => nav.status.fully_resolved.val,
15641559 };
15651560
15661561 if (nav_init != .none and Value.fromInterned(nav_init).typeOf(zcu).hasRuntimeBits(zcu)) {
src/link/MachO/ZigObject.zig+8-15
......@@ -608,14 +608,11 @@ pub fn getNavVAddr(
608608 const ip = &zcu.intern_pool;
609609 const nav = ip.getNav(nav_index);
610610 log.debug("getNavVAddr {}({d})", .{ nav.fqn.fmt(ip), nav_index });
611 const sym_index = switch (ip.indexToKey(nav.status.resolved.val)) {
612 .@"extern" => |@"extern"| try self.getGlobalSymbol(
613 macho_file,
614 nav.name.toSlice(ip),
615 @"extern".lib_name.toSlice(ip),
616 ),
617 else => try self.getOrCreateMetadataForNav(macho_file, nav_index),
618 };
611 const sym_index = if (nav.getExtern(ip)) |@"extern"| try self.getGlobalSymbol(
612 macho_file,
613 nav.name.toSlice(ip),
614 @"extern".lib_name.toSlice(ip),
615 ) else try self.getOrCreateMetadataForNav(macho_file, nav_index);
619616 const sym = self.symbols.items[sym_index];
620617 const vaddr = sym.getAddress(.{}, macho_file);
621618 switch (reloc_info.parent) {
......@@ -882,7 +879,7 @@ pub fn updateNav(
882879 const ip = &zcu.intern_pool;
883880 const nav = ip.getNav(nav_index);
884881
885 const nav_init = switch (ip.indexToKey(nav.status.resolved.val)) {
882 const nav_init = switch (ip.indexToKey(nav.status.fully_resolved.val)) {
886883 .func => .none,
887884 .variable => |variable| variable.init,
888885 .@"extern" => |@"extern"| {
......@@ -895,7 +892,7 @@ pub fn updateNav(
895892 sym.flags.is_extern_ptr = true;
896893 return;
897894 },
898 else => nav.status.resolved.val,
895 else => nav.status.fully_resolved.val,
899896 };
900897
901898 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 {
15611558 if (!macho_file.base.comp.config.any_non_single_threaded)
15621559 return false;
15631560 const ip = &macho_file.base.comp.zcu.?.intern_pool;
1564 return switch (ip.indexToKey(ip.getNav(nav_index).status.resolved.val)) {
1565 .variable => |variable| variable.is_threadlocal,
1566 .@"extern" => |@"extern"| @"extern".is_threadlocal,
1567 else => false,
1568 };
1561 return ip.getNav(nav_index).isThreadlocal(ip);
15691562}
15701563
15711564fn addAtom(self: *ZigObject, allocator: Allocator) !Atom.Index {
src/link/Plan9.zig+2-2
......@@ -1021,7 +1021,7 @@ pub fn seeNav(self: *Plan9, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index)
10211021 const atom_idx = gop.value_ptr.index;
10221022 // handle externs here because they might not get updateDecl called on them
10231023 const nav = ip.getNav(nav_index);
1024 if (ip.indexToKey(nav.status.resolved.val) == .@"extern") {
1024 if (nav.getExtern(ip) != null) {
10251025 // this is a "phantom atom" - it is never actually written to disk, just convenient for us to store stuff about externs
10261026 if (nav.name.eqlSlice("etext", ip)) {
10271027 self.etext_edata_end_atom_indices[0] = atom_idx;
......@@ -1370,7 +1370,7 @@ pub fn getNavVAddr(
13701370 const ip = &pt.zcu.intern_pool;
13711371 const nav = ip.getNav(nav_index);
13721372 log.debug("getDeclVAddr for {}", .{nav.name.fmt(ip)});
1373 if (ip.indexToKey(nav.status.resolved.val) == .@"extern") {
1373 if (nav.getExtern(ip) != null) {
13741374 if (nav.name.eqlSlice("etext", ip)) {
13751375 try self.addReloc(reloc_info.parent.atom_index, .{
13761376 .target = undefined,
src/link/Wasm/ZigObject.zig+6-7
......@@ -734,15 +734,14 @@ pub fn getNavVAddr(
734734 const target_atom_index = try zig_object.getOrCreateAtomForNav(wasm, pt, nav_index);
735735 const target_atom = wasm.getAtom(target_atom_index);
736736 const target_symbol_index = @intFromEnum(target_atom.sym_index);
737 switch (ip.indexToKey(nav.status.resolved.val)) {
738 .@"extern" => |@"extern"| try zig_object.addOrUpdateImport(
737 if (nav.getExtern(ip)) |@"extern"| {
738 try zig_object.addOrUpdateImport(
739739 wasm,
740740 nav.name.toSlice(ip),
741741 target_atom.sym_index,
742742 @"extern".lib_name.toSlice(ip),
743743 null,
744 ),
745 else => {},
744 );
746745 }
747746
748747 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
945944 segment.name = &.{}; // Ensure no accidental double free
946945 }
947946
948 const nav_val = zcu.navValue(nav_index).toIntern();
949 if (ip.indexToKey(nav_val) == .@"extern") {
947 const nav = ip.getNav(nav_index);
948 if (nav.getExtern(ip) != null) {
950949 std.debug.assert(zig_object.imports.remove(atom.sym_index));
951950 }
952951 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
960959 if (sym.isGlobal()) {
961960 std.debug.assert(zig_object.global_syms.remove(atom.sym_index));
962961 }
963 if (ip.isFunctionType(ip.typeOf(nav_val))) {
962 if (ip.isFunctionType(nav.typeOf(ip))) {
964963 zig_object.functions_free_list.append(gpa, sym.index) catch {};
965964 std.debug.assert(zig_object.atom_types.remove(atom_index));
966965 } else {
test/behavior/globals.zig+96
......@@ -66,3 +66,99 @@ test "global loads can affect liveness" {
6666 S.f();
6767 try std.testing.expect(y.a == 1);
6868}
69
70test "global const can be self-referential" {
71 const S = struct {
72 self: *const @This(),
73 x: u32,
74
75 const foo: @This() = .{ .self = &foo, .x = 123 };
76 };
77
78 try std.testing.expect(S.foo.x == 123);
79 try std.testing.expect(S.foo.self.x == 123);
80 try std.testing.expect(S.foo.self.self.x == 123);
81 try std.testing.expect(S.foo.self == &S.foo);
82 try std.testing.expect(S.foo.self.self == &S.foo);
83}
84
85test "global var can be self-referential" {
86 const S = struct {
87 self: *@This(),
88 x: u32,
89
90 var foo: @This() = .{ .self = &foo, .x = undefined };
91 };
92
93 S.foo.x = 123;
94
95 try std.testing.expect(S.foo.x == 123);
96 try std.testing.expect(S.foo.self.x == 123);
97 try std.testing.expect(S.foo.self == &S.foo);
98
99 S.foo.self.x = 456;
100
101 try std.testing.expect(S.foo.x == 456);
102 try std.testing.expect(S.foo.self.x == 456);
103 try std.testing.expect(S.foo.self == &S.foo);
104
105 S.foo.self.self.x = 789;
106
107 try std.testing.expect(S.foo.x == 789);
108 try std.testing.expect(S.foo.self.x == 789);
109 try std.testing.expect(S.foo.self == &S.foo);
110}
111
112test "global const can be indirectly self-referential" {
113 const S = struct {
114 other: *const @This(),
115 x: u32,
116
117 const foo: @This() = .{ .other = &bar, .x = 123 };
118 const bar: @This() = .{ .other = &foo, .x = 456 };
119 };
120
121 try std.testing.expect(S.foo.x == 123);
122 try std.testing.expect(S.foo.other.x == 456);
123 try std.testing.expect(S.foo.other.other.x == 123);
124 try std.testing.expect(S.foo.other.other.other.x == 456);
125 try std.testing.expect(S.foo.other == &S.bar);
126 try std.testing.expect(S.foo.other.other == &S.foo);
127
128 try std.testing.expect(S.bar.x == 456);
129 try std.testing.expect(S.bar.other.x == 123);
130 try std.testing.expect(S.bar.other.other.x == 456);
131 try std.testing.expect(S.bar.other.other.other.x == 123);
132 try std.testing.expect(S.bar.other == &S.foo);
133 try std.testing.expect(S.bar.other.other == &S.bar);
134}
135
136test "global var can be indirectly self-referential" {
137 const S = struct {
138 other: *@This(),
139 x: u32,
140
141 var foo: @This() = .{ .other = &bar, .x = undefined };
142 var bar: @This() = .{ .other = &foo, .x = undefined };
143 };
144
145 S.foo.other.x = 123; // bar.x
146 S.foo.other.other.x = 456; // foo.x
147
148 try std.testing.expect(S.foo.x == 456);
149 try std.testing.expect(S.foo.other.x == 123);
150 try std.testing.expect(S.foo.other.other.x == 456);
151 try std.testing.expect(S.foo.other.other.other.x == 123);
152 try std.testing.expect(S.foo.other == &S.bar);
153 try std.testing.expect(S.foo.other.other == &S.foo);
154
155 S.bar.other.x = 111; // foo.x
156 S.bar.other.other.x = 222; // bar.x
157
158 try std.testing.expect(S.bar.x == 222);
159 try std.testing.expect(S.bar.other.x == 111);
160 try std.testing.expect(S.bar.other.other.x == 222);
161 try std.testing.expect(S.bar.other.other.other.x == 111);
162 try std.testing.expect(S.bar.other == &S.foo);
163 try std.testing.expect(S.bar.other.other == &S.bar);
164}
test/cases/compile_errors/self_reference_missing_const.zig created+11
......@@ -0,0 +1,11 @@
1const S = struct { self: *S, x: u32 };
2const s: S = .{ .self = &s, .x = 123 };
3
4comptime {
5 _ = s;
6}
7
8// error
9//
10// :2:18: error: expected type '*tmp.S', found '*const tmp.S'
11// :2:18: note: cast discards const qualifier