| author | |
| committer | |
| log | 3afda4322c34dedc2319701fdfac3505c8d311e9 |
| tree | 467873c408750cb4223f3ccf31775e42ec9fbd5c |
| parent | 40aafcd6a85d3c517f445f17149c17523c832420 |
| signature |
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: #13122 files changed, 1033 insertions(+), 410 deletions(-)
src/Compilation.zig+10-4| ... | ... | @@ -2906,6 +2906,7 @@ const Header = extern struct { |
| 2906 | 2906 | file_deps_len: u32, |
| 2907 | 2907 | src_hash_deps_len: u32, |
| 2908 | 2908 | nav_val_deps_len: u32, |
| 2909 | nav_ty_deps_len: u32, | |
| 2909 | 2910 | namespace_deps_len: u32, |
| 2910 | 2911 | namespace_name_deps_len: u32, |
| 2911 | 2912 | first_dependency_len: u32, |
| ... | ... | @@ -2949,6 +2950,7 @@ pub fn saveState(comp: *Compilation) !void { |
| 2949 | 2950 | .file_deps_len = @intCast(ip.file_deps.count()), |
| 2950 | 2951 | .src_hash_deps_len = @intCast(ip.src_hash_deps.count()), |
| 2951 | 2952 | .nav_val_deps_len = @intCast(ip.nav_val_deps.count()), |
| 2953 | .nav_ty_deps_len = @intCast(ip.nav_ty_deps.count()), | |
| 2952 | 2954 | .namespace_deps_len = @intCast(ip.namespace_deps.count()), |
| 2953 | 2955 | .namespace_name_deps_len = @intCast(ip.namespace_name_deps.count()), |
| 2954 | 2956 | .first_dependency_len = @intCast(ip.first_dependency.count()), |
| ... | ... | @@ -2979,6 +2981,8 @@ pub fn saveState(comp: *Compilation) !void { |
| 2979 | 2981 | addBuf(&bufs, mem.sliceAsBytes(ip.src_hash_deps.values())); |
| 2980 | 2982 | addBuf(&bufs, mem.sliceAsBytes(ip.nav_val_deps.keys())); |
| 2981 | 2983 | 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())); | |
| 2982 | 2986 | addBuf(&bufs, mem.sliceAsBytes(ip.namespace_deps.keys())); |
| 2983 | 2987 | addBuf(&bufs, mem.sliceAsBytes(ip.namespace_deps.values())); |
| 2984 | 2988 | addBuf(&bufs, mem.sliceAsBytes(ip.namespace_name_deps.keys())); |
| ... | ... | @@ -3145,7 +3149,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { |
| 3145 | 3149 | |
| 3146 | 3150 | const file_index = switch (anal_unit.unwrap()) { |
| 3147 | 3151 | .@"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), | |
| 3149 | 3153 | .type => |ty| Type.fromInterned(ty).typeDeclInst(zcu).?.resolveFile(ip), |
| 3150 | 3154 | .func => |ip_index| zcu.funcInfo(ip_index).zir_body_inst.resolveFile(ip), |
| 3151 | 3155 | }; |
| ... | ... | @@ -3380,7 +3384,7 @@ pub fn addModuleErrorMsg( |
| 3380 | 3384 | defer gpa.free(rt_file_path); |
| 3381 | 3385 | const name = switch (ref.referencer.unwrap()) { |
| 3382 | 3386 | .@"comptime" => "comptime", |
| 3383 | .nav_val => |nav| ip.getNav(nav).name.toSlice(ip), | |
| 3387 | .nav_val, .nav_ty => |nav| ip.getNav(nav).name.toSlice(ip), | |
| 3384 | 3388 | .type => |ty| Type.fromInterned(ty).containerTypeName(ip).toSlice(ip), |
| 3385 | 3389 | .func => |f| ip.getNav(zcu.funcInfo(f).owner_nav).name.toSlice(ip), |
| 3386 | 3390 | }; |
| ... | ... | @@ -3647,6 +3651,7 @@ fn performAllTheWorkInner( |
| 3647 | 3651 | try comp.queueJob(switch (outdated.unwrap()) { |
| 3648 | 3652 | .func => |f| .{ .analyze_func = f }, |
| 3649 | 3653 | .@"comptime", |
| 3654 | .nav_ty, | |
| 3650 | 3655 | .nav_val, |
| 3651 | 3656 | .type, |
| 3652 | 3657 | => .{ .analyze_comptime_unit = outdated }, |
| ... | ... | @@ -3679,7 +3684,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre |
| 3679 | 3684 | return; |
| 3680 | 3685 | } |
| 3681 | 3686 | } |
| 3682 | assert(nav.status == .resolved); | |
| 3687 | assert(nav.status == .fully_resolved); | |
| 3683 | 3688 | comp.dispatchCodegenTask(tid, .{ .codegen_nav = nav_index }); |
| 3684 | 3689 | }, |
| 3685 | 3690 | .codegen_func => |func| { |
| ... | ... | @@ -3709,6 +3714,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre |
| 3709 | 3714 | |
| 3710 | 3715 | const maybe_err: Zcu.SemaError!void = switch (unit.unwrap()) { |
| 3711 | 3716 | .@"comptime" => |cu| pt.ensureComptimeUnitUpToDate(cu), |
| 3717 | .nav_ty => |nav| pt.ensureNavTypeUpToDate(nav), | |
| 3712 | 3718 | .nav_val => |nav| pt.ensureNavValUpToDate(nav), |
| 3713 | 3719 | .type => |ty| if (pt.ensureTypeUpToDate(ty)) |_| {} else |err| err, |
| 3714 | 3720 | .func => unreachable, |
| ... | ... | @@ -3734,7 +3740,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre |
| 3734 | 3740 | // Tests are always emitted in test binaries. The decl_refs are created by |
| 3735 | 3741 | // Zcu.populateTestFunctions, but this will not queue body analysis, so do |
| 3736 | 3742 | // 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); | |
| 3738 | 3744 | } |
| 3739 | 3745 | }, |
| 3740 | 3746 | .resolve_type_fully => |ty| { |
src/InternPool.zig+201-54| ... | ... | @@ -34,6 +34,9 @@ src_hash_deps: std.AutoArrayHashMapUnmanaged(TrackedInst.Index, DepEntry.Index), |
| 34 | 34 | /// Dependencies on the value of a Nav. |
| 35 | 35 | /// Value is index into `dep_entries` of the first dependency on this Nav value. |
| 36 | 36 | nav_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. | |
| 39 | nav_ty_deps: std.AutoArrayHashMapUnmanaged(Nav.Index, DepEntry.Index), | |
| 37 | 40 | /// Dependencies on an interned value, either: |
| 38 | 41 | /// * a runtime function (invalidated when its IES changes) |
| 39 | 42 | /// * a container type requiring resolution (invalidated when the type must be recreated at a new index) |
| ... | ... | @@ -80,6 +83,7 @@ pub const empty: InternPool = .{ |
| 80 | 83 | .file_deps = .empty, |
| 81 | 84 | .src_hash_deps = .empty, |
| 82 | 85 | .nav_val_deps = .empty, |
| 86 | .nav_ty_deps = .empty, | |
| 83 | 87 | .interned_deps = .empty, |
| 84 | 88 | .namespace_deps = .empty, |
| 85 | 89 | .namespace_name_deps = .empty, |
| ... | ... | @@ -371,6 +375,7 @@ pub const AnalUnit = packed struct(u64) { |
| 371 | 375 | pub const Kind = enum(u32) { |
| 372 | 376 | @"comptime", |
| 373 | 377 | nav_val, |
| 378 | nav_ty, | |
| 374 | 379 | type, |
| 375 | 380 | func, |
| 376 | 381 | }; |
| ... | ... | @@ -380,6 +385,8 @@ pub const AnalUnit = packed struct(u64) { |
| 380 | 385 | @"comptime": ComptimeUnit.Id, |
| 381 | 386 | /// This `AnalUnit` resolves the value of the given `Nav`. |
| 382 | 387 | nav_val: Nav.Index, |
| 388 | /// This `AnalUnit` resolves the type of the given `Nav`. | |
| 389 | nav_ty: Nav.Index, | |
| 383 | 390 | /// This `AnalUnit` resolves the given `struct`/`union`/`enum` type. |
| 384 | 391 | /// Generated tag enums are never used here (they do not undergo type resolution). |
| 385 | 392 | type: InternPool.Index, |
| ... | ... | @@ -483,8 +490,20 @@ pub const Nav = struct { |
| 483 | 490 | status: union(enum) { |
| 484 | 491 | /// This `Nav` is pending semantic analysis. |
| 485 | 492 | 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 | }, | |
| 486 | 505 | /// The value of this `Nav` is resolved. |
| 487 | resolved: struct { | |
| 506 | fully_resolved: struct { | |
| 488 | 507 | val: InternPool.Index, |
| 489 | 508 | alignment: Alignment, |
| 490 | 509 | @"linksection": OptionalNullTerminatedString, |
| ... | ... | @@ -492,14 +511,81 @@ pub const Nav = struct { |
| 492 | 511 | }, |
| 493 | 512 | }, |
| 494 | 513 | |
| 495 | /// Asserts that `status == .resolved`. | |
| 514 | /// Asserts that `status != .unresolved`. | |
| 496 | 515 | 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 | }; | |
| 498 | 521 | } |
| 499 | 522 | |
| 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 | }; | |
| 503 | 589 | } |
| 504 | 590 | |
| 505 | 591 | /// Get the ZIR instruction corresponding to this `Nav`, used to resolve source locations. |
| ... | ... | @@ -509,7 +595,7 @@ pub const Nav = struct { |
| 509 | 595 | return a.zir_index; |
| 510 | 596 | } |
| 511 | 597 | // 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)) { | |
| 513 | 599 | .func => |func| { |
| 514 | 600 | // Since `analysis` was not populated, this must be an instantiation. |
| 515 | 601 | // Go up to the generic owner and consult *its* `analysis` field. |
| ... | ... | @@ -567,19 +653,22 @@ pub const Nav = struct { |
| 567 | 653 | // The following 1 fields are either both populated, or both `.none`. |
| 568 | 654 | analysis_namespace: OptionalNamespaceIndex, |
| 569 | 655 | 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`. | |
| 573 | 659 | @"linksection": OptionalNullTerminatedString, |
| 574 | 660 | bits: Bits, |
| 575 | 661 | |
| 576 | 662 | 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`. | |
| 579 | 665 | alignment: Alignment, |
| 580 | /// Populated only if `bits.status == .resolved`. | |
| 666 | /// Populated only if `bits.status != .unresolved`. | |
| 581 | 667 | @"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, | |
| 583 | 672 | is_usingnamespace: bool, |
| 584 | 673 | }; |
| 585 | 674 | |
| ... | ... | @@ -597,8 +686,17 @@ pub const Nav = struct { |
| 597 | 686 | .is_usingnamespace = repr.bits.is_usingnamespace, |
| 598 | 687 | .status = switch (repr.bits.status) { |
| 599 | 688 | .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, | |
| 602 | 700 | .alignment = repr.bits.alignment, |
| 603 | 701 | .@"linksection" = repr.@"linksection", |
| 604 | 702 | .@"addrspace" = repr.bits.@"addrspace", |
| ... | ... | @@ -616,13 +714,15 @@ pub const Nav = struct { |
| 616 | 714 | .fqn = nav.fqn, |
| 617 | 715 | .analysis_namespace = if (nav.analysis) |a| a.namespace.toOptional() else .none, |
| 618 | 716 | .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) { | |
| 620 | 718 | .unresolved => .none, |
| 621 | .resolved => |r| r.val, | |
| 719 | .type_resolved => |r| r.type, | |
| 720 | .fully_resolved => |r| r.val, | |
| 622 | 721 | }, |
| 623 | 722 | .@"linksection" = switch (nav.status) { |
| 624 | 723 | .unresolved => .none, |
| 625 | .resolved => |r| r.@"linksection", | |
| 724 | .type_resolved => |r| r.@"linksection", | |
| 725 | .fully_resolved => |r| r.@"linksection", | |
| 626 | 726 | }, |
| 627 | 727 | .bits = switch (nav.status) { |
| 628 | 728 | .unresolved => .{ |
| ... | ... | @@ -630,12 +730,24 @@ pub const Nav = struct { |
| 630 | 730 | .alignment = .none, |
| 631 | 731 | .@"addrspace" = .generic, |
| 632 | 732 | .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, | |
| 633 | 743 | }, |
| 634 | .resolved => |r| .{ | |
| 635 | .status = .resolved, | |
| 744 | .fully_resolved => |r| .{ | |
| 745 | .status = .fully_resolved, | |
| 636 | 746 | .alignment = r.alignment, |
| 637 | 747 | .@"addrspace" = r.@"addrspace", |
| 638 | 748 | .is_usingnamespace = nav.is_usingnamespace, |
| 749 | .is_const = false, | |
| 750 | .is_threadlocal = false, | |
| 639 | 751 | }, |
| 640 | 752 | }, |
| 641 | 753 | }; |
| ... | ... | @@ -646,6 +758,7 @@ pub const Dependee = union(enum) { |
| 646 | 758 | file: FileIndex, |
| 647 | 759 | src_hash: TrackedInst.Index, |
| 648 | 760 | nav_val: Nav.Index, |
| 761 | nav_ty: Nav.Index, | |
| 649 | 762 | interned: Index, |
| 650 | 763 | namespace: TrackedInst.Index, |
| 651 | 764 | namespace_name: NamespaceNameKey, |
| ... | ... | @@ -695,6 +808,7 @@ pub fn dependencyIterator(ip: *const InternPool, dependee: Dependee) DependencyI |
| 695 | 808 | .file => |x| ip.file_deps.get(x), |
| 696 | 809 | .src_hash => |x| ip.src_hash_deps.get(x), |
| 697 | 810 | .nav_val => |x| ip.nav_val_deps.get(x), |
| 811 | .nav_ty => |x| ip.nav_ty_deps.get(x), | |
| 698 | 812 | .interned => |x| ip.interned_deps.get(x), |
| 699 | 813 | .namespace => |x| ip.namespace_deps.get(x), |
| 700 | 814 | .namespace_name => |x| ip.namespace_name_deps.get(x), |
| ... | ... | @@ -732,6 +846,7 @@ pub fn addDependency(ip: *InternPool, gpa: Allocator, depender: AnalUnit, depend |
| 732 | 846 | .file => ip.file_deps, |
| 733 | 847 | .src_hash => ip.src_hash_deps, |
| 734 | 848 | .nav_val => ip.nav_val_deps, |
| 849 | .nav_ty => ip.nav_ty_deps, | |
| 735 | 850 | .interned => ip.interned_deps, |
| 736 | 851 | .namespace => ip.namespace_deps, |
| 737 | 852 | .namespace_name => ip.namespace_name_deps, |
| ... | ... | @@ -2079,36 +2194,36 @@ pub const Key = union(enum) { |
| 2079 | 2194 | return @atomicLoad(FuncAnalysis, func.analysisPtr(ip), .unordered); |
| 2080 | 2195 | } |
| 2081 | 2196 | |
| 2082 | pub fn setAnalysisState(func: Func, ip: *InternPool, state: FuncAnalysis.State) void { | |
| 2197 | pub fn setCallsOrAwaitsErrorableFn(func: Func, ip: *InternPool, value: bool) void { | |
| 2083 | 2198 | const extra_mutex = &ip.getLocal(func.tid).mutate.extra.mutex; |
| 2084 | 2199 | extra_mutex.lock(); |
| 2085 | 2200 | defer extra_mutex.unlock(); |
| 2086 | 2201 | |
| 2087 | 2202 | const analysis_ptr = func.analysisPtr(ip); |
| 2088 | 2203 | var analysis = analysis_ptr.*; |
| 2089 | analysis.state = state; | |
| 2204 | analysis.calls_or_awaits_errorable_fn = value; | |
| 2090 | 2205 | @atomicStore(FuncAnalysis, analysis_ptr, analysis, .release); |
| 2091 | 2206 | } |
| 2092 | 2207 | |
| 2093 | pub fn setCallsOrAwaitsErrorableFn(func: Func, ip: *InternPool, value: bool) void { | |
| 2208 | pub fn setBranchHint(func: Func, ip: *InternPool, hint: std.builtin.BranchHint) void { | |
| 2094 | 2209 | const extra_mutex = &ip.getLocal(func.tid).mutate.extra.mutex; |
| 2095 | 2210 | extra_mutex.lock(); |
| 2096 | 2211 | defer extra_mutex.unlock(); |
| 2097 | 2212 | |
| 2098 | 2213 | const analysis_ptr = func.analysisPtr(ip); |
| 2099 | 2214 | var analysis = analysis_ptr.*; |
| 2100 | analysis.calls_or_awaits_errorable_fn = value; | |
| 2215 | analysis.branch_hint = hint; | |
| 2101 | 2216 | @atomicStore(FuncAnalysis, analysis_ptr, analysis, .release); |
| 2102 | 2217 | } |
| 2103 | 2218 | |
| 2104 | pub fn setBranchHint(func: Func, ip: *InternPool, hint: std.builtin.BranchHint) void { | |
| 2219 | pub fn setAnalyzed(func: Func, ip: *InternPool) void { | |
| 2105 | 2220 | const extra_mutex = &ip.getLocal(func.tid).mutate.extra.mutex; |
| 2106 | 2221 | extra_mutex.lock(); |
| 2107 | 2222 | defer extra_mutex.unlock(); |
| 2108 | 2223 | |
| 2109 | 2224 | const analysis_ptr = func.analysisPtr(ip); |
| 2110 | 2225 | var analysis = analysis_ptr.*; |
| 2111 | analysis.branch_hint = hint; | |
| 2226 | analysis.is_analyzed = true; | |
| 2112 | 2227 | @atomicStore(FuncAnalysis, analysis_ptr, analysis, .release); |
| 2113 | 2228 | } |
| 2114 | 2229 | |
| ... | ... | @@ -5755,7 +5870,7 @@ pub const Tag = enum(u8) { |
| 5755 | 5870 | /// equality or hashing, except for `inferred_error_set` which is considered |
| 5756 | 5871 | /// to be part of the type of the function. |
| 5757 | 5872 | pub const FuncAnalysis = packed struct(u32) { |
| 5758 | state: State, | |
| 5873 | is_analyzed: bool, | |
| 5759 | 5874 | branch_hint: std.builtin.BranchHint, |
| 5760 | 5875 | is_noinline: bool, |
| 5761 | 5876 | calls_or_awaits_errorable_fn: bool, |
| ... | ... | @@ -5763,20 +5878,7 @@ pub const FuncAnalysis = packed struct(u32) { |
| 5763 | 5878 | inferred_error_set: bool, |
| 5764 | 5879 | disable_instrumentation: bool, |
| 5765 | 5880 | |
| 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, | |
| 5780 | 5882 | }; |
| 5781 | 5883 | |
| 5782 | 5884 | pub const Bytes = struct { |
| ... | ... | @@ -6419,6 +6521,7 @@ pub fn deinit(ip: *InternPool, gpa: Allocator) void { |
| 6419 | 6521 | ip.file_deps.deinit(gpa); |
| 6420 | 6522 | ip.src_hash_deps.deinit(gpa); |
| 6421 | 6523 | ip.nav_val_deps.deinit(gpa); |
| 6524 | ip.nav_ty_deps.deinit(gpa); | |
| 6422 | 6525 | ip.interned_deps.deinit(gpa); |
| 6423 | 6526 | ip.namespace_deps.deinit(gpa); |
| 6424 | 6527 | ip.namespace_name_deps.deinit(gpa); |
| ... | ... | @@ -6875,8 +6978,8 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key { |
| 6875 | 6978 | .is_threadlocal = extra.flags.is_threadlocal, |
| 6876 | 6979 | .is_weak_linkage = extra.flags.is_weak_linkage, |
| 6877 | 6980 | .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", | |
| 6880 | 6983 | .zir_index = extra.zir_index, |
| 6881 | 6984 | .owner_nav = extra.owner_nav, |
| 6882 | 6985 | } }; |
| ... | ... | @@ -8794,7 +8897,7 @@ pub fn getFuncDecl( |
| 8794 | 8897 | |
| 8795 | 8898 | const func_decl_extra_index = addExtraAssumeCapacity(extra, Tag.FuncDecl{ |
| 8796 | 8899 | .analysis = .{ |
| 8797 | .state = .unreferenced, | |
| 8900 | .is_analyzed = false, | |
| 8798 | 8901 | .branch_hint = .none, |
| 8799 | 8902 | .is_noinline = key.is_noinline, |
| 8800 | 8903 | .calls_or_awaits_errorable_fn = false, |
| ... | ... | @@ -8903,7 +9006,7 @@ pub fn getFuncDeclIes( |
| 8903 | 9006 | |
| 8904 | 9007 | const func_decl_extra_index = addExtraAssumeCapacity(extra, Tag.FuncDecl{ |
| 8905 | 9008 | .analysis = .{ |
| 8906 | .state = .unreferenced, | |
| 9009 | .is_analyzed = false, | |
| 8907 | 9010 | .branch_hint = .none, |
| 8908 | 9011 | .is_noinline = key.is_noinline, |
| 8909 | 9012 | .calls_or_awaits_errorable_fn = false, |
| ... | ... | @@ -9099,7 +9202,7 @@ pub fn getFuncInstance( |
| 9099 | 9202 | |
| 9100 | 9203 | const func_extra_index = addExtraAssumeCapacity(extra, Tag.FuncInstance{ |
| 9101 | 9204 | .analysis = .{ |
| 9102 | .state = .unreferenced, | |
| 9205 | .is_analyzed = false, | |
| 9103 | 9206 | .branch_hint = .none, |
| 9104 | 9207 | .is_noinline = arg.is_noinline, |
| 9105 | 9208 | .calls_or_awaits_errorable_fn = false, |
| ... | ... | @@ -9197,7 +9300,7 @@ pub fn getFuncInstanceIes( |
| 9197 | 9300 | |
| 9198 | 9301 | const func_extra_index = addExtraAssumeCapacity(extra, Tag.FuncInstance{ |
| 9199 | 9302 | .analysis = .{ |
| 9200 | .state = .unreferenced, | |
| 9303 | .is_analyzed = false, | |
| 9201 | 9304 | .branch_hint = .none, |
| 9202 | 9305 | .is_noinline = arg.is_noinline, |
| 9203 | 9306 | .calls_or_awaits_errorable_fn = false, |
| ... | ... | @@ -9316,9 +9419,9 @@ fn finishFuncInstance( |
| 9316 | 9419 | .name = nav_name, |
| 9317 | 9420 | .fqn = try ip.namespacePtr(fn_namespace).internFullyQualifiedName(ip, gpa, tid, nav_name), |
| 9318 | 9421 | .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", | |
| 9322 | 9425 | }); |
| 9323 | 9426 | |
| 9324 | 9427 | // Populate the owner_nav field which was left undefined until now. |
| ... | ... | @@ -11030,7 +11133,7 @@ pub fn createNav( |
| 11030 | 11133 | .name = opts.name, |
| 11031 | 11134 | .fqn = opts.fqn, |
| 11032 | 11135 | .analysis = null, |
| 11033 | .status = .{ .resolved = .{ | |
| 11136 | .status = .{ .fully_resolved = .{ | |
| 11034 | 11137 | .val = opts.val, |
| 11035 | 11138 | .alignment = opts.alignment, |
| 11036 | 11139 | .@"linksection" = opts.@"linksection", |
| ... | ... | @@ -11077,6 +11180,50 @@ pub fn createDeclNav( |
| 11077 | 11180 | return nav; |
| 11078 | 11181 | } |
| 11079 | 11182 | |
| 11183 | /// Resolve the type of a `Nav` with an analysis owner. | |
| 11184 | /// If its status is already `resolved`, the old value is discarded. | |
| 11185 | pub 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 | ||
| 11080 | 11227 | /// Resolve the value of a `Nav` with an analysis owner. |
| 11081 | 11228 | /// If its status is already `resolved`, the old value is discarded. |
| 11082 | 11229 | pub fn resolveNavValue( |
| ... | ... | @@ -11099,7 +11246,7 @@ pub fn resolveNavValue( |
| 11099 | 11246 | |
| 11100 | 11247 | const nav_analysis_namespace = navs.items(.analysis_namespace); |
| 11101 | 11248 | 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); | |
| 11103 | 11250 | const nav_linksections = navs.items(.@"linksection"); |
| 11104 | 11251 | const nav_bits = navs.items(.bits); |
| 11105 | 11252 | |
| ... | ... | @@ -11110,7 +11257,7 @@ pub fn resolveNavValue( |
| 11110 | 11257 | @atomicStore(OptionalNullTerminatedString, &nav_linksections[unwrapped.index], resolved.@"linksection", .release); |
| 11111 | 11258 | |
| 11112 | 11259 | var bits = nav_bits[unwrapped.index]; |
| 11113 | bits.status = .resolved; | |
| 11260 | bits.status = .fully_resolved; | |
| 11114 | 11261 | bits.alignment = resolved.alignment; |
| 11115 | 11262 | bits.@"addrspace" = resolved.@"addrspace"; |
| 11116 | 11263 | @atomicStore(Nav.Repr.Bits, &nav_bits[unwrapped.index], bits, .release); |
src/Sema.zig+157-46| ... | ... | @@ -6495,9 +6495,9 @@ pub fn analyzeExport( |
| 6495 | 6495 | if (options.linkage == .internal) |
| 6496 | 6496 | return; |
| 6497 | 6497 | |
| 6498 | try sema.ensureNavResolved(src, orig_nav_index); | |
| 6498 | try sema.ensureNavResolved(src, orig_nav_index, .fully); | |
| 6499 | 6499 | |
| 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)) { | |
| 6501 | 6501 | .variable => |v| v.owner_nav, |
| 6502 | 6502 | .@"extern" => |e| e.owner_nav, |
| 6503 | 6503 | .func => |f| f.owner_nav, |
| ... | ... | @@ -6520,7 +6520,7 @@ pub fn analyzeExport( |
| 6520 | 6520 | } |
| 6521 | 6521 | |
| 6522 | 6522 | // TODO: some backends might support re-exporting extern decls |
| 6523 | if (exported_nav.isExtern(ip)) { | |
| 6523 | if (exported_nav.getExtern(ip) != null) { | |
| 6524 | 6524 | return sema.fail(block, src, "export target cannot be extern", .{}); |
| 6525 | 6525 | } |
| 6526 | 6526 | |
| ... | ... | @@ -6542,6 +6542,7 @@ fn zirDisableInstrumentation(sema: *Sema) CompileError!void { |
| 6542 | 6542 | .func => |func| func, |
| 6543 | 6543 | .@"comptime", |
| 6544 | 6544 | .nav_val, |
| 6545 | .nav_ty, | |
| 6545 | 6546 | .type, |
| 6546 | 6547 | => return, // does nothing outside a function |
| 6547 | 6548 | }; |
| ... | ... | @@ -6854,8 +6855,8 @@ fn lookupInNamespace( |
| 6854 | 6855 | } |
| 6855 | 6856 | |
| 6856 | 6857 | 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); | |
| 6859 | 6860 | const sub_ns = zcu.namespacePtr(sub_ns_ty.getNamespaceIndex(zcu)); |
| 6860 | 6861 | try checked_namespaces.put(gpa, sub_ns, {}); |
| 6861 | 6862 | } |
| ... | ... | @@ -6865,7 +6866,7 @@ fn lookupInNamespace( |
| 6865 | 6866 | ignore_self: { |
| 6866 | 6867 | const skip_nav = switch (sema.owner.unwrap()) { |
| 6867 | 6868 | .@"comptime", .type, .func => break :ignore_self, |
| 6868 | .nav_val => |nav| nav, | |
| 6869 | .nav_ty, .nav_val => |nav| nav, | |
| 6869 | 6870 | }; |
| 6870 | 6871 | var i: usize = 0; |
| 6871 | 6872 | while (i < candidates.items.len) { |
| ... | ... | @@ -7125,7 +7126,7 @@ fn zirCall( |
| 7125 | 7126 | const call_inst = try sema.analyzeCall(block, func, func_ty, callee_src, call_src, modifier, ensure_result_used, args_info, call_dbg_node, .call); |
| 7126 | 7127 | |
| 7127 | 7128 | switch (sema.owner.unwrap()) { |
| 7128 | .@"comptime", .type, .nav_val => input_is_error = false, | |
| 7129 | .@"comptime", .type, .nav_ty, .nav_val => input_is_error = false, | |
| 7129 | 7130 | .func => |owner_func| if (!zcu.intern_pool.funcAnalysisUnordered(owner_func).calls_or_awaits_errorable_fn) { |
| 7130 | 7131 | // No errorable fn actually called; we have no error return trace |
| 7131 | 7132 | input_is_error = false; |
| ... | ... | @@ -7686,12 +7687,13 @@ fn analyzeCall( |
| 7686 | 7687 | .ptr => |ptr| blk: { |
| 7687 | 7688 | switch (ptr.base_addr) { |
| 7688 | 7689 | .nav => |nav_index| if (ptr.byte_offset == 0) { |
| 7690 | try sema.ensureNavResolved(call_src, nav_index, .fully); | |
| 7689 | 7691 | const nav = ip.getNav(nav_index); |
| 7690 | if (nav.isExtern(ip)) | |
| 7692 | if (nav.getExtern(ip) != null) | |
| 7691 | 7693 | return sema.fail(block, call_src, "{s} call of extern function pointer", .{ |
| 7692 | 7694 | if (is_comptime_call) "comptime" else "inline", |
| 7693 | 7695 | }); |
| 7694 | break :blk nav.status.resolved.val; | |
| 7696 | break :blk nav.status.fully_resolved.val; | |
| 7695 | 7697 | }, |
| 7696 | 7698 | else => {}, |
| 7697 | 7699 | } |
| ... | ... | @@ -8007,7 +8009,7 @@ fn analyzeCall( |
| 8007 | 8009 | if (call_dbg_node) |some| try sema.zirDbgStmt(block, some); |
| 8008 | 8010 | |
| 8009 | 8011 | switch (sema.owner.unwrap()) { |
| 8010 | .@"comptime", .nav_val, .type => {}, | |
| 8012 | .@"comptime", .nav_ty, .nav_val, .type => {}, | |
| 8011 | 8013 | .func => |owner_func| if (Type.fromInterned(func_ty_info.return_type).isError(zcu)) { |
| 8012 | 8014 | ip.funcSetCallsOrAwaitsErrorableFn(owner_func); |
| 8013 | 8015 | }, |
| ... | ... | @@ -8046,7 +8048,10 @@ fn analyzeCall( |
| 8046 | 8048 | switch (zcu.intern_pool.indexToKey(func_val.toIntern())) { |
| 8047 | 8049 | .func => break :skip_safety, |
| 8048 | 8050 | .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 | }, | |
| 8050 | 8055 | else => {}, |
| 8051 | 8056 | }, |
| 8052 | 8057 | else => {}, |
| ... | ... | @@ -8243,7 +8248,7 @@ fn instantiateGenericCall( |
| 8243 | 8248 | }); |
| 8244 | 8249 | const generic_owner = switch (zcu.intern_pool.indexToKey(func_val.toIntern())) { |
| 8245 | 8250 | .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, | |
| 8247 | 8252 | else => unreachable, |
| 8248 | 8253 | }; |
| 8249 | 8254 | const generic_owner_func = zcu.intern_pool.indexToKey(generic_owner).func; |
| ... | ... | @@ -8471,7 +8476,7 @@ fn instantiateGenericCall( |
| 8471 | 8476 | if (call_dbg_node) |some| try sema.zirDbgStmt(block, some); |
| 8472 | 8477 | |
| 8473 | 8478 | switch (sema.owner.unwrap()) { |
| 8474 | .@"comptime", .nav_val, .type => {}, | |
| 8479 | .@"comptime", .nav_ty, .nav_val, .type => {}, | |
| 8475 | 8480 | .func => |owner_func| if (Type.fromInterned(func_ty_info.return_type).isError(zcu)) { |
| 8476 | 8481 | ip.funcSetCallsOrAwaitsErrorableFn(owner_func); |
| 8477 | 8482 | }, |
| ... | ... | @@ -19311,8 +19316,8 @@ fn typeInfoNamespaceDecls( |
| 19311 | 19316 | if (zcu.analysis_in_progress.contains(.wrap(.{ .nav_val = nav }))) { |
| 19312 | 19317 | continue; |
| 19313 | 19318 | } |
| 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); | |
| 19316 | 19321 | try sema.typeInfoNamespaceDecls(block, src, namespace_ty.getNamespaceIndex(zcu).toOptional(), declaration_ty, decl_vals, seen_namespaces); |
| 19317 | 19322 | } |
| 19318 | 19323 | } |
| ... | ... | @@ -21602,7 +21607,7 @@ fn getErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref { |
| 21602 | 21607 | .func => |func| if (ip.funcAnalysisUnordered(func).calls_or_awaits_errorable_fn and block.ownerModule().error_tracing) { |
| 21603 | 21608 | return block.addTy(.err_return_trace, opt_ptr_stack_trace_ty); |
| 21604 | 21609 | }, |
| 21605 | .@"comptime", .nav_val, .type => {}, | |
| 21610 | .@"comptime", .nav_ty, .nav_val, .type => {}, | |
| 21606 | 21611 | } |
| 21607 | 21612 | return Air.internedToRef(try pt.intern(.{ .opt = .{ |
| 21608 | 21613 | .ty = opt_ptr_stack_trace_ty.toIntern(), |
| ... | ... | @@ -27086,7 +27091,7 @@ fn zirBuiltinExtern( |
| 27086 | 27091 | .zir_index = switch (sema.owner.unwrap()) { |
| 27087 | 27092 | .@"comptime" => |cu| ip.getComptimeUnit(cu).zir_index, |
| 27088 | 27093 | .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, | |
| 27090 | 27095 | .func => |func| zir_index: { |
| 27091 | 27096 | const func_info = zcu.funcInfo(func); |
| 27092 | 27097 | 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 |
| 27741 | 27746 | error.GenericPoison, error.ComptimeReturn, error.ComptimeBreak => unreachable, |
| 27742 | 27747 | error.OutOfMemory => |e| return e, |
| 27743 | 27748 | }).?; |
| 27744 | try sema.ensureNavResolved(src, msg_nav_index); | |
| 27749 | try sema.ensureNavResolved(src, msg_nav_index, .fully); | |
| 27745 | 27750 | zcu.panic_messages[@intFromEnum(panic_id)] = msg_nav_index.toOptional(); |
| 27746 | 27751 | return msg_nav_index; |
| 27747 | 27752 | } |
| ... | ... | @@ -32648,21 +32653,29 @@ fn addTypeReferenceEntry( |
| 32648 | 32653 | try zcu.addTypeReference(sema.owner, referenced_type, src); |
| 32649 | 32654 | } |
| 32650 | 32655 | |
| 32651 | pub fn ensureNavResolved(sema: *Sema, src: LazySrcLoc, nav_index: InternPool.Nav.Index) CompileError!void { | |
| 32656 | pub fn ensureNavResolved(sema: *Sema, src: LazySrcLoc, nav_index: InternPool.Nav.Index, kind: enum { type, fully }) CompileError!void { | |
| 32652 | 32657 | const pt = sema.pt; |
| 32653 | 32658 | const zcu = pt.zcu; |
| 32654 | 32659 | const ip = &zcu.intern_pool; |
| 32655 | 32660 | |
| 32656 | 32661 | const nav = ip.getNav(nav_index); |
| 32657 | 32662 | if (nav.analysis == null) { |
| 32658 | assert(nav.status == .resolved); | |
| 32663 | assert(nav.status == .fully_resolved); | |
| 32659 | 32664 | return; |
| 32660 | 32665 | } |
| 32661 | 32666 | |
| 32667 | try sema.declareDependency(switch (kind) { | |
| 32668 | .type => .{ .nav_ty = nav_index }, | |
| 32669 | .fully => .{ .nav_val = nav_index }, | |
| 32670 | }); | |
| 32671 | ||
| 32662 | 32672 | // Note that even if `nav.status == .resolved`, we must still trigger `ensureNavValUpToDate` |
| 32663 | 32673 | // to make sure the value is up-to-date on incremental updates. |
| 32664 | 32674 | |
| 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 | }); | |
| 32666 | 32679 | try sema.addReferenceEntry(src, anal_unit); |
| 32667 | 32680 | |
| 32668 | 32681 | if (zcu.analysis_in_progress.contains(anal_unit)) { |
| ... | ... | @@ -32672,7 +32685,13 @@ pub fn ensureNavResolved(sema: *Sema, src: LazySrcLoc, nav_index: InternPool.Nav |
| 32672 | 32685 | }, "dependency loop detected", .{})); |
| 32673 | 32686 | } |
| 32674 | 32687 | |
| 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 | } | |
| 32676 | 32695 | } |
| 32677 | 32696 | |
| 32678 | 32697 | fn optRefValue(sema: *Sema, opt_val: ?Value) !Value { |
| ... | ... | @@ -32691,36 +32710,44 @@ fn analyzeNavRef(sema: *Sema, src: LazySrcLoc, nav_index: InternPool.Nav.Index) |
| 32691 | 32710 | return sema.analyzeNavRefInner(src, nav_index, true); |
| 32692 | 32711 | } |
| 32693 | 32712 | |
| 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. | |
| 32698 | fn 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`. | |
| 32716 | fn analyzeNavRefInner(sema: *Sema, src: LazySrcLoc, orig_nav_index: InternPool.Nav.Index, is_ref: bool) CompileError!Air.Inst.Ref { | |
| 32699 | 32717 | const pt = sema.pt; |
| 32700 | 32718 | const zcu = pt.zcu; |
| 32701 | 32719 | const ip = &zcu.intern_pool; |
| 32702 | 32720 | |
| 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); | |
| 32706 | 32722 | |
| 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) }, | |
| 32713 | 32741 | }; |
| 32714 | const nav_info = ip.getNav(nav_index).status.resolved; | |
| 32715 | 32742 | const ptr_ty = try pt.ptrTypeSema(.{ |
| 32716 | .child = nav_val.typeOf(zcu).toIntern(), | |
| 32743 | .child = ty, | |
| 32717 | 32744 | .flags = .{ |
| 32718 | .alignment = nav_info.alignment, | |
| 32745 | .alignment = alignment, | |
| 32719 | 32746 | .is_const = is_const, |
| 32720 | .address_space = nav_info.@"addrspace", | |
| 32747 | .address_space = @"addrspace", | |
| 32721 | 32748 | }, |
| 32722 | 32749 | }); |
| 32723 | if (analyze_fn_body) { | |
| 32750 | if (is_ref) { | |
| 32724 | 32751 | try sema.maybeQueueFuncBodyAnalysis(src, nav_index); |
| 32725 | 32752 | } |
| 32726 | 32753 | return Air.internedToRef((try pt.intern(.{ .ptr = .{ |
| ... | ... | @@ -32731,11 +32758,22 @@ fn analyzeNavRefInner(sema: *Sema, src: LazySrcLoc, orig_nav_index: InternPool.N |
| 32731 | 32758 | } |
| 32732 | 32759 | |
| 32733 | 32760 | fn 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; | |
| 32735 | 32763 | 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); | |
| 32736 | 32774 | const nav_val = zcu.navValue(nav_index); |
| 32737 | 32775 | if (!ip.isFuncBody(nav_val.toIntern())) return; |
| 32738 | if (!try nav_val.typeOf(zcu).fnHasRuntimeBitsSema(sema.pt)) return; | |
| 32776 | ||
| 32739 | 32777 | try sema.addReferenceEntry(src, AnalUnit.wrap(.{ .func = nav_val.toIntern() })); |
| 32740 | 32778 | try zcu.ensureFuncBodyAnalysisQueued(nav_val.toIntern()); |
| 32741 | 32779 | } |
| ... | ... | @@ -38450,11 +38488,16 @@ pub fn declareDependency(sema: *Sema, dependee: InternPool.Dependee) !void { |
| 38450 | 38488 | // of a type and they use `@This()`. This dependency would be unnecessary, and in fact would |
| 38451 | 38489 | // just result in over-analysis since `Zcu.findOutdatedToAnalyze` would never be able to resolve |
| 38452 | 38490 | // the loop. |
| 38491 | // Note that this also disallows a `nav_val` | |
| 38453 | 38492 | switch (sema.owner.unwrap()) { |
| 38454 | 38493 | .nav_val => |this_nav| switch (dependee) { |
| 38455 | 38494 | .nav_val => |other_nav| if (this_nav == other_nav) return, |
| 38456 | 38495 | else => {}, |
| 38457 | 38496 | }, |
| 38497 | .nav_ty => |this_nav| switch (dependee) { | |
| 38498 | .nav_ty => |other_nav| if (this_nav == other_nav) return, | |
| 38499 | else => {}, | |
| 38500 | }, | |
| 38458 | 38501 | else => {}, |
| 38459 | 38502 | } |
| 38460 | 38503 | |
| ... | ... | @@ -38873,8 +38916,8 @@ fn getBuiltinInnerType( |
| 38873 | 38916 | const nav = opt_nav orelse return sema.fail(block, src, "std.builtin.{s} missing {s}", .{ |
| 38874 | 38917 | compile_error_parent_name, inner_name, |
| 38875 | 38918 | }); |
| 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); | |
| 38878 | 38921 | const ty = val.toType(); |
| 38879 | 38922 | try ty.resolveFully(pt); |
| 38880 | 38923 | return ty; |
| ... | ... | @@ -38886,5 +38929,73 @@ fn getBuiltin(sema: *Sema, name: []const u8) SemaError!Air.Inst.Ref { |
| 38886 | 38929 | const ip = &zcu.intern_pool; |
| 38887 | 38930 | const nav = try pt.getBuiltinNav(name); |
| 38888 | 38931 | 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 | ||
| 38935 | pub const NavPtrModifiers = struct { | |
| 38936 | alignment: Alignment, | |
| 38937 | @"linksection": InternPool.OptionalNullTerminatedString, | |
| 38938 | @"addrspace": std.builtin.AddressSpace, | |
| 38939 | }; | |
| 38940 | ||
| 38941 | pub 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 | }; | |
| 38890 | 39001 | } |
src/Sema/comptime_ptr_access.zig+2-3| ... | ... | @@ -219,9 +219,8 @@ fn loadComptimePtrInner( |
| 219 | 219 | |
| 220 | 220 | const base_val: MutableValue = switch (ptr.base_addr) { |
| 221 | 221 | .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; | |
| 225 | 224 | switch (ip.indexToKey(val)) { |
| 226 | 225 | .variable => return .runtime_load, |
| 227 | 226 | // 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 { |
| 1343 | 1343 | pub fn isPtrRuntimeValue(val: Value, zcu: *Zcu) bool { |
| 1344 | 1344 | const ip = &zcu.intern_pool; |
| 1345 | 1345 | 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)) { | |
| 1347 | 1352 | .@"extern" => |e| e.is_threadlocal or e.is_dll_import, |
| 1348 | 1353 | .variable => |v| v.is_threadlocal, |
| 1349 | 1354 | else => false, |
src/Zcu.zig+72-9| ... | ... | @@ -170,6 +170,9 @@ outdated_ready: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .empty, |
| 170 | 170 | /// it as outdated. |
| 171 | 171 | retryable_failures: std.ArrayListUnmanaged(AnalUnit) = .empty, |
| 172 | 172 | |
| 173 | func_body_analysis_queued: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .empty, | |
| 174 | nav_val_analysis_queued: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void) = .empty, | |
| 175 | ||
| 173 | 176 | /// These are the modules which we initially queue for analysis in `Compilation.update`. |
| 174 | 177 | /// `resolveReferences` will use these as the root of its reachability traversal. |
| 175 | 178 | analysis_roots: std.BoundedArray(*Package.Module, 3) = .{}, |
| ... | ... | @@ -282,7 +285,11 @@ pub const Exported = union(enum) { |
| 282 | 285 | |
| 283 | 286 | pub fn getAlign(exported: Exported, zcu: *Zcu) Alignment { |
| 284 | 287 | 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 | }, | |
| 286 | 293 | .uav => .none, |
| 287 | 294 | }; |
| 288 | 295 | } |
| ... | ... | @@ -2241,6 +2248,9 @@ pub fn deinit(zcu: *Zcu) void { |
| 2241 | 2248 | zcu.outdated_ready.deinit(gpa); |
| 2242 | 2249 | zcu.retryable_failures.deinit(gpa); |
| 2243 | 2250 | |
| 2251 | zcu.func_body_analysis_queued.deinit(gpa); | |
| 2252 | zcu.nav_val_analysis_queued.deinit(gpa); | |
| 2253 | ||
| 2244 | 2254 | zcu.test_functions.deinit(gpa); |
| 2245 | 2255 | |
| 2246 | 2256 | for (zcu.global_assembly.values()) |s| { |
| ... | ... | @@ -2441,6 +2451,7 @@ pub fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void { |
| 2441 | 2451 | switch (depender.unwrap()) { |
| 2442 | 2452 | .@"comptime" => {}, |
| 2443 | 2453 | .nav_val => |nav| try zcu.markPoDependeeUpToDate(.{ .nav_val = nav }), |
| 2454 | .nav_ty => |nav| try zcu.markPoDependeeUpToDate(.{ .nav_ty = nav }), | |
| 2444 | 2455 | .type => |ty| try zcu.markPoDependeeUpToDate(.{ .interned = ty }), |
| 2445 | 2456 | .func => |func| try zcu.markPoDependeeUpToDate(.{ .interned = func }), |
| 2446 | 2457 | } |
| ... | ... | @@ -2453,7 +2464,8 @@ fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUni |
| 2453 | 2464 | const ip = &zcu.intern_pool; |
| 2454 | 2465 | const dependee: InternPool.Dependee = switch (maybe_outdated.unwrap()) { |
| 2455 | 2466 | .@"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 }, | |
| 2457 | 2469 | .type => |ty| .{ .interned = ty }, |
| 2458 | 2470 | .func => |func_index| .{ .interned = func_index }, // IES |
| 2459 | 2471 | }; |
| ... | ... | @@ -2540,6 +2552,7 @@ pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit { |
| 2540 | 2552 | .@"comptime" => continue, // a `comptime` block can't even be depended on so it is a terrible choice |
| 2541 | 2553 | .type => |ty| .{ .interned = ty }, |
| 2542 | 2554 | .nav_val => |nav| .{ .nav_val = nav }, |
| 2555 | .nav_ty => |nav| .{ .nav_ty = nav }, | |
| 2543 | 2556 | }); |
| 2544 | 2557 | while (it.next()) |_| n += 1; |
| 2545 | 2558 | |
| ... | ... | @@ -2780,14 +2793,39 @@ pub fn ensureFuncBodyAnalysisQueued(zcu: *Zcu, func_index: InternPool.Index) !vo |
| 2780 | 2793 | const ip = &zcu.intern_pool; |
| 2781 | 2794 | const func = zcu.funcInfo(func_index); |
| 2782 | 2795 | |
| 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 | } | |
| 2787 | 2805 | } |
| 2788 | 2806 | |
| 2807 | try zcu.func_body_analysis_queued.ensureUnusedCapacity(zcu.gpa, 1); | |
| 2789 | 2808 | try zcu.comp.queueJob(.{ .analyze_func = func_index }); |
| 2790 | func.setAnalysisState(ip, .queued); | |
| 2809 | zcu.func_body_analysis_queued.putAssumeCapacityNoClobber(func_index, {}); | |
| 2810 | } | |
| 2811 | ||
| 2812 | pub 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, {}); | |
| 2791 | 2829 | } |
| 2792 | 2830 | |
| 2793 | 2831 | pub const ImportFileResult = struct { |
| ... | ... | @@ -3424,6 +3462,17 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv |
| 3424 | 3462 | const unit = kv.key; |
| 3425 | 3463 | try result.putNoClobber(gpa, unit, kv.value); |
| 3426 | 3464 | |
| 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 | ||
| 3427 | 3476 | log.debug("handle unit '{}'", .{zcu.fmtAnalUnit(unit)}); |
| 3428 | 3477 | |
| 3429 | 3478 | if (zcu.reference_table.get(unit)) |first_ref_idx| { |
| ... | ... | @@ -3513,7 +3562,7 @@ pub fn navSrcLine(zcu: *Zcu, nav_index: InternPool.Nav.Index) u32 { |
| 3513 | 3562 | } |
| 3514 | 3563 | |
| 3515 | 3564 | pub 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); | |
| 3517 | 3566 | } |
| 3518 | 3567 | |
| 3519 | 3568 | pub fn navFileScopeIndex(zcu: *Zcu, nav: InternPool.Nav.Index) File.Index { |
| ... | ... | @@ -3547,6 +3596,7 @@ fn formatAnalUnit(data: struct { unit: AnalUnit, zcu: *Zcu }, comptime fmt: []co |
| 3547 | 3596 | } |
| 3548 | 3597 | }, |
| 3549 | 3598 | .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)}), | |
| 3550 | 3600 | .type => |ty| return writer.print("ty('{}')", .{Type.fromInterned(ty).containerTypeName(ip).fmt(ip)}), |
| 3551 | 3601 | .func => |func| { |
| 3552 | 3602 | const nav = zcu.funcInfo(func).owner_nav; |
| ... | ... | @@ -3572,7 +3622,11 @@ fn formatDependee(data: struct { dependee: InternPool.Dependee, zcu: *Zcu }, com |
| 3572 | 3622 | }, |
| 3573 | 3623 | .nav_val => |nav| { |
| 3574 | 3624 | 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)}); | |
| 3576 | 3630 | }, |
| 3577 | 3631 | .interned => |ip_index| switch (ip.indexToKey(ip_index)) { |
| 3578 | 3632 | .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 |
| 3749 | 3803 | if (!backend_ok) return .{ .bad_backend = backend }; |
| 3750 | 3804 | return .ok; |
| 3751 | 3805 | } |
| 3806 | ||
| 3807 | /// Given that a `Nav` has value `val`, determine if a ref of that `Nav` gives a `const` pointer. | |
| 3808 | pub 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 |
| 731 | 731 | const gpa = zcu.gpa; |
| 732 | 732 | const ip = &zcu.intern_pool; |
| 733 | 733 | |
| 734 | _ = zcu.nav_val_analysis_queued.swapRemove(nav_id); | |
| 735 | ||
| 734 | 736 | const anal_unit: AnalUnit = .wrap(.{ .nav_val = nav_id }); |
| 735 | 737 | const nav = ip.getNav(nav_id); |
| 736 | 738 | |
| 737 | log.debug("ensureNavUpToDate {}", .{zcu.fmtAnalUnit(anal_unit)}); | |
| 739 | log.debug("ensureNavValUpToDate {}", .{zcu.fmtAnalUnit(anal_unit)}); | |
| 738 | 740 | |
| 739 | 741 | // Determine whether or not this `Nav`'s value is outdated. This also includes checking if the |
| 740 | 742 | // 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 |
| 763 | 765 | } else { |
| 764 | 766 | // We can trust the current information about this unit. |
| 765 | 767 | 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 | } | |
| 767 | 772 | } |
| 768 | 773 | |
| 769 | 774 | const unit_prog_node = zcu.sema_prog_node.start(nav.fqn.toSlice(ip), 0); |
| 770 | 775 | defer unit_prog_node.end(); |
| 771 | 776 | |
| 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: { | |
| 773 | 778 | 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, | |
| 779 | 781 | false, |
| 780 | 782 | }; |
| 781 | 783 | } else |err| switch (err) { |
| ... | ... | @@ -786,10 +788,7 @@ pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu |
| 786 | 788 | try zcu.transitive_failed_analysis.put(gpa, anal_unit, {}); |
| 787 | 789 | log.debug("mark transitive analysis failure for {}", .{zcu.fmtAnalUnit(anal_unit)}); |
| 788 | 790 | } |
| 789 | break :res .{ .{ | |
| 790 | .invalidate_nav_val = !prev_failed, | |
| 791 | .invalidate_nav_ref = !prev_failed, | |
| 792 | }, true }; | |
| 791 | break :res .{ !prev_failed, true }; | |
| 793 | 792 | }, |
| 794 | 793 | error.OutOfMemory => { |
| 795 | 794 | // 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 |
| 806 | 805 | }; |
| 807 | 806 | |
| 808 | 807 | 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; | |
| 811 | 808 | const dependee: InternPool.Dependee = .{ .nav_val = nav_id }; |
| 812 | if (invalidate) { | |
| 809 | if (invalidate_value) { | |
| 813 | 810 | // This dependency was marked as PO, meaning dependees were waiting |
| 814 | 811 | // on its analysis result, and it has turned out to be outdated. |
| 815 | 812 | // Update dependees accordingly. |
| ... | ... | @@ -824,14 +821,7 @@ pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu |
| 824 | 821 | if (new_failed) return error.AnalysisFail; |
| 825 | 822 | } |
| 826 | 823 | |
| 827 | const 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 | ||
| 834 | fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileError!SemaNavResult { | |
| 824 | fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileError!struct { val_changed: bool } { | |
| 835 | 825 | const zcu = pt.zcu; |
| 836 | 826 | const gpa = zcu.gpa; |
| 837 | 827 | const ip = &zcu.intern_pool; |
| ... | ... | @@ -875,9 +865,13 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr |
| 875 | 865 | }; |
| 876 | 866 | defer sema.deinit(); |
| 877 | 867 | |
| 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. | |
| 879 | 869 | try sema.declareDependency(.{ .src_hash = old_nav.analysis.?.zir_index }); |
| 880 | 870 | |
| 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 | ||
| 881 | 875 | var block: Sema.Block = .{ |
| 882 | 876 | .parent = null, |
| 883 | 877 | .sema = &sema, |
| ... | ... | @@ -891,31 +885,44 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr |
| 891 | 885 | defer block.instructions.deinit(gpa); |
| 892 | 886 | |
| 893 | 887 | const zir_decl = zir.getDeclaration(inst_resolved.inst); |
| 894 | ||
| 895 | 888 | assert(old_nav.is_usingnamespace == (zir_decl.kind == .@"usingnamespace")); |
| 896 | 889 | |
| 890 | const ty_src = block.src(.{ .node_offset_var_decl_ty = 0 }); | |
| 891 | const init_src = block.src(.{ .node_offset_var_decl_init = 0 }); | |
| 897 | 892 | const align_src = block.src(.{ .node_offset_var_decl_align = 0 }); |
| 898 | 893 | const section_src = block.src(.{ .node_offset_var_decl_section = 0 }); |
| 899 | 894 | 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); | |
| 902 | 922 | |
| 903 | 923 | // First, we must resolve the declaration's type. To do this, we analyze the type body if available, |
| 904 | 924 | // or otherwise, we analyze the value body, populating `early_val` in the process. |
| 905 | 925 | |
| 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 | ||
| 919 | 926 | switch (zir_decl.kind) { |
| 920 | 927 | .@"comptime" => unreachable, // this is not a Nav |
| 921 | 928 | .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 |
| 932 | 939 | // Now that we know the type, we can evaluate the alignment, linksection, and addrspace, to determine |
| 933 | 940 | // the full pointer type of this declaration. |
| 934 | 941 | |
| 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", | |
| 961 | 951 | }, |
| 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); | |
| 972 | 956 | }; |
| 973 | 957 | |
| 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`! | |
| 987 | 960 | |
| 988 | 961 | const nav_val: Value = switch (zir_decl.linkage) { |
| 989 | 962 | .normal, .@"export" => switch (zir_decl.kind) { |
| ... | ... | @@ -1013,8 +986,8 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr |
| 1013 | 986 | .is_threadlocal = zir_decl.is_threadlocal, |
| 1014 | 987 | .is_weak_linkage = false, |
| 1015 | 988 | .is_dll_import = false, |
| 1016 | .alignment = alignment, | |
| 1017 | .@"addrspace" = @"addrspace", | |
| 989 | .alignment = modifiers.alignment, | |
| 990 | .@"addrspace" = modifiers.@"addrspace", | |
| 1018 | 991 | .zir_index = old_nav.analysis.?.zir_index, // `declaration` instruction |
| 1019 | 992 | .owner_nav = undefined, // ignored by `getExtern` |
| 1020 | 993 | })); |
| ... | ... | @@ -1047,10 +1020,7 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr |
| 1047 | 1020 | }); |
| 1048 | 1021 | // TODO: usingnamespace cannot participate in incremental compilation |
| 1049 | 1022 | 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 }; | |
| 1054 | 1024 | } |
| 1055 | 1025 | |
| 1056 | 1026 | 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 |
| 1087 | 1057 | |
| 1088 | 1058 | ip.resolveNavValue(nav_id, .{ |
| 1089 | 1059 | .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", | |
| 1093 | 1063 | }); |
| 1094 | 1064 | |
| 1095 | 1065 | // Mark the unit as completed before evaluating the export! |
| 1096 | 1066 | assert(zcu.analysis_in_progress.swapRemove(anal_unit)); |
| 1097 | 1067 | |
| 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 | ||
| 1098 | 1076 | if (zir_decl.linkage == .@"export") { |
| 1099 | 1077 | const export_src = block.src(.{ .token_offset = @intFromBool(zir_decl.is_pub) }); |
| 1100 | 1078 | const name_slice = zir.nullTerminatedString(zir_decl.name); |
| ... | ... | @@ -1117,21 +1095,246 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr |
| 1117 | 1095 | } |
| 1118 | 1096 | |
| 1119 | 1097 | 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 | ||
| 1103 | pub 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 }; | |
| 1123 | 1167 | }, |
| 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; | |
| 1133 | 1176 | }, |
| 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 | } | |
| 1134 | 1194 | } |
| 1195 | ||
| 1196 | if (new_failed) return error.AnalysisFail; | |
| 1197 | } | |
| 1198 | ||
| 1199 | fn 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 }; | |
| 1135 | 1338 | } |
| 1136 | 1339 | |
| 1137 | 1340 | pub fn ensureFuncBodyUpToDate(pt: Zcu.PerThread, maybe_coerced_func_index: InternPool.Index) Zcu.SemaError!void { |
| ... | ... | @@ -1144,6 +1347,8 @@ pub fn ensureFuncBodyUpToDate(pt: Zcu.PerThread, maybe_coerced_func_index: Inter |
| 1144 | 1347 | const gpa = zcu.gpa; |
| 1145 | 1348 | const ip = &zcu.intern_pool; |
| 1146 | 1349 | |
| 1350 | _ = zcu.func_body_analysis_queued.swapRemove(maybe_coerced_func_index); | |
| 1351 | ||
| 1147 | 1352 | // We only care about the uncoerced function. |
| 1148 | 1353 | const func_index = ip.unwrapCoercedFunc(maybe_coerced_func_index); |
| 1149 | 1354 | const anal_unit: AnalUnit = .wrap(.{ .func = func_index }); |
| ... | ... | @@ -1171,11 +1376,7 @@ pub fn ensureFuncBodyUpToDate(pt: Zcu.PerThread, maybe_coerced_func_index: Inter |
| 1171 | 1376 | if (prev_failed) { |
| 1172 | 1377 | return error.AnalysisFail; |
| 1173 | 1378 | } |
| 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; | |
| 1179 | 1380 | } |
| 1180 | 1381 | |
| 1181 | 1382 | const func_prog_node = zcu.sema_prog_node.start(ip.getNav(func.owner_nav).fqn.toSlice(ip), 0); |
| ... | ... | @@ -1236,7 +1437,7 @@ fn analyzeFuncBody( |
| 1236 | 1437 | if (func.generic_owner == .none) { |
| 1237 | 1438 | // Among another things, this ensures that the function's `zir_body_inst` is correct. |
| 1238 | 1439 | 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) { | |
| 1240 | 1441 | // This function is no longer referenced! There's no point in re-analyzing it. |
| 1241 | 1442 | // Just mark a transitive failure and move on. |
| 1242 | 1443 | return error.AnalysisFail; |
| ... | ... | @@ -1245,7 +1446,7 @@ fn analyzeFuncBody( |
| 1245 | 1446 | const go_nav = zcu.funcInfo(func.generic_owner).owner_nav; |
| 1246 | 1447 | // Among another things, this ensures that the function's `zir_body_inst` is correct. |
| 1247 | 1448 | 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) { | |
| 1249 | 1450 | // The generic owner is no longer referenced, so this function is also unreferenced. |
| 1250 | 1451 | // There's no point in re-analyzing it. Just mark a transitive failure and move on. |
| 1251 | 1452 | return error.AnalysisFail; |
| ... | ... | @@ -2172,7 +2373,7 @@ fn analyzeFnBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaE |
| 2172 | 2373 | try zcu.analysis_in_progress.put(gpa, anal_unit, {}); |
| 2173 | 2374 | errdefer _ = zcu.analysis_in_progress.swapRemove(anal_unit); |
| 2174 | 2375 | |
| 2175 | func.setAnalysisState(ip, .analyzed); | |
| 2376 | func.setAnalyzed(ip); | |
| 2176 | 2377 | if (func.analysisUnordered(ip).inferred_error_set) { |
| 2177 | 2378 | func.setResolvedErrorSet(ip, .none); |
| 2178 | 2379 | } |
| ... | ... | @@ -2550,8 +2751,8 @@ fn processExportsInner( |
| 2550 | 2751 | if (zcu.transitive_failed_analysis.contains(unit)) break :failed true; |
| 2551 | 2752 | } |
| 2552 | 2753 | 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), | |
| 2555 | 2756 | }; |
| 2556 | 2757 | // If the value is a function, we also need to check if that function succeeded analysis. |
| 2557 | 2758 | if (val.typeOf(zcu).zigTypeTag(zcu) == .@"fn") { |
| ... | ... | @@ -3256,30 +3457,29 @@ pub fn getBuiltinNav(pt: Zcu.PerThread, name: []const u8) Allocator.Error!Intern |
| 3256 | 3457 | const builtin_nav = std_namespace.pub_decls.getKeyAdapted(builtin_str, Zcu.Namespace.NameAdapter{ .zcu = zcu }) orelse |
| 3257 | 3458 | @panic("lib/std.zig is corrupt and missing 'builtin'"); |
| 3258 | 3459 | 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); | |
| 3260 | 3461 | const builtin_namespace = zcu.namespacePtr(builtin_type.getNamespace(zcu).unwrap() orelse @panic("std.builtin is corrupt")); |
| 3261 | 3462 | const name_str = try ip.getOrPutString(gpa, pt.tid, name, .no_embedded_nulls); |
| 3262 | 3463 | return builtin_namespace.pub_decls.getKeyAdapted(name_str, Zcu.Namespace.NameAdapter{ .zcu = zcu }) orelse @panic("lib/std/builtin.zig is corrupt"); |
| 3263 | 3464 | } |
| 3264 | 3465 | |
| 3265 | pub fn navPtrType(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) Allocator.Error!Type { | |
| 3466 | pub fn navPtrType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Allocator.Error!Type { | |
| 3266 | 3467 | const zcu = pt.zcu; |
| 3267 | 3468 | 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 | }; | |
| 3270 | 3474 | return pt.ptrType(.{ |
| 3271 | .child = ty.toIntern(), | |
| 3475 | .child = ty, | |
| 3272 | 3476 | .flags = .{ |
| 3273 | .alignment = if (r.alignment == ty.abiAlignment(zcu)) | |
| 3477 | .alignment = if (alignment == Type.fromInterned(ty).abiAlignment(zcu)) | |
| 3274 | 3478 | .none |
| 3275 | 3479 | 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, | |
| 3283 | 3483 | }, |
| 3284 | 3484 | }); |
| 3285 | 3485 | } |
| ... | ... | @@ -3299,9 +3499,13 @@ pub fn getExtern(pt: Zcu.PerThread, key: InternPool.Key.Extern) Allocator.Error! |
| 3299 | 3499 | // TODO: this shouldn't need a `PerThread`! Fix the signature of `Type.abiAlignment`. |
| 3300 | 3500 | pub fn navAlignment(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) InternPool.Alignment { |
| 3301 | 3501 | 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); | |
| 3305 | 3509 | } |
| 3306 | 3510 | |
| 3307 | 3511 | /// 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 |
| 3218 | 3218 | const zcu = pt.zcu; |
| 3219 | 3219 | const ip = &zcu.intern_pool; |
| 3220 | 3220 | |
| 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); | |
| 3230 | 3222 | if (!ip.isFunctionType(nav_ty) and !Type.fromInterned(nav_ty).hasRuntimeBitsIgnoreComptime(zcu)) { |
| 3231 | 3223 | return .{ .imm32 = 0xaaaaaaaa }; |
| 3232 | 3224 | } |
src/codegen.zig+9-8| ... | ... | @@ -817,7 +817,7 @@ fn genNavRef( |
| 817 | 817 | pt: Zcu.PerThread, |
| 818 | 818 | src_loc: Zcu.LazySrcLoc, |
| 819 | 819 | val: Value, |
| 820 | ref_nav_index: InternPool.Nav.Index, | |
| 820 | nav_index: InternPool.Nav.Index, | |
| 821 | 821 | target: std.Target, |
| 822 | 822 | ) CodeGenError!GenResult { |
| 823 | 823 | const zcu = pt.zcu; |
| ... | ... | @@ -851,14 +851,15 @@ fn genNavRef( |
| 851 | 851 | } |
| 852 | 852 | } |
| 853 | 853 | |
| 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 | ||
| 860 | 861 | const single_threaded = zcu.navFileScope(nav_index).mod.single_threaded; |
| 861 | const name = ip.getNav(nav_index).name; | |
| 862 | const name = nav.name; | |
| 862 | 863 | if (lf.cast(.elf)) |elf_file| { |
| 863 | 864 | const zo = elf_file.zigObjectPtr().?; |
| 864 | 865 | if (is_extern) { |
src/codegen/c.zig+32-29| ... | ... | @@ -770,11 +770,14 @@ pub const DeclGen = struct { |
| 770 | 770 | const ctype_pool = &dg.ctype_pool; |
| 771 | 771 | |
| 772 | 772 | // 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 | }, | |
| 778 | 781 | }; |
| 779 | 782 | |
| 780 | 783 | // Render an undefined pointer if we have a pointer to a zero-bit or comptime type. |
| ... | ... | @@ -2237,7 +2240,7 @@ pub const DeclGen = struct { |
| 2237 | 2240 | Type.fromInterned(nav.typeOf(ip)), |
| 2238 | 2241 | .{ .nav = nav_index }, |
| 2239 | 2242 | CQualifiers.init(.{ .@"const" = flags.is_const }), |
| 2240 | nav.status.resolved.alignment, | |
| 2243 | nav.getAlignment(), | |
| 2241 | 2244 | .complete, |
| 2242 | 2245 | ); |
| 2243 | 2246 | try fwd.writeAll(";\n"); |
| ... | ... | @@ -2246,19 +2249,19 @@ pub const DeclGen = struct { |
| 2246 | 2249 | fn renderNavName(dg: *DeclGen, writer: anytype, nav_index: InternPool.Nav.Index) !void { |
| 2247 | 2250 | const zcu = dg.pt.zcu; |
| 2248 | 2251 | 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("{ }", .{ | |
| 2251 | 2255 | 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 | }); | |
| 2262 | 2265 | } |
| 2263 | 2266 | } |
| 2264 | 2267 | |
| ... | ... | @@ -2826,7 +2829,7 @@ pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFn |
| 2826 | 2829 | |
| 2827 | 2830 | const fwd = o.dg.fwdDeclWriter(); |
| 2828 | 2831 | 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, .{ | |
| 2830 | 2833 | .fmt_ctype_pool_string = fn_name, |
| 2831 | 2834 | }); |
| 2832 | 2835 | try fwd.writeAll(";\n"); |
| ... | ... | @@ -2867,13 +2870,13 @@ pub fn genFunc(f: *Function) !void { |
| 2867 | 2870 | try o.dg.renderFunctionSignature( |
| 2868 | 2871 | fwd, |
| 2869 | 2872 | nav_val, |
| 2870 | nav.status.resolved.alignment, | |
| 2873 | nav.status.fully_resolved.alignment, | |
| 2871 | 2874 | .forward, |
| 2872 | 2875 | .{ .nav = nav_index }, |
| 2873 | 2876 | ); |
| 2874 | 2877 | try fwd.writeAll(";\n"); |
| 2875 | 2878 | |
| 2876 | if (nav.status.resolved.@"linksection".toSlice(ip)) |s| | |
| 2879 | if (nav.status.fully_resolved.@"linksection".toSlice(ip)) |s| | |
| 2877 | 2880 | try o.writer().print("zig_linksection_fn({s}) ", .{fmtStringLiteral(s, null)}); |
| 2878 | 2881 | try o.dg.renderFunctionSignature( |
| 2879 | 2882 | o.writer(), |
| ... | ... | @@ -2952,7 +2955,7 @@ pub fn genDecl(o: *Object) !void { |
| 2952 | 2955 | const nav_ty = Type.fromInterned(nav.typeOf(ip)); |
| 2953 | 2956 | |
| 2954 | 2957 | if (!nav_ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) return; |
| 2955 | switch (ip.indexToKey(nav.status.resolved.val)) { | |
| 2958 | switch (ip.indexToKey(nav.status.fully_resolved.val)) { | |
| 2956 | 2959 | .@"extern" => |@"extern"| { |
| 2957 | 2960 | if (!ip.isFunctionType(nav_ty.toIntern())) return o.dg.renderFwdDecl(o.dg.pass.nav, .{ |
| 2958 | 2961 | .is_extern = true, |
| ... | ... | @@ -2965,8 +2968,8 @@ pub fn genDecl(o: *Object) !void { |
| 2965 | 2968 | try fwd.writeAll("zig_extern "); |
| 2966 | 2969 | try o.dg.renderFunctionSignature( |
| 2967 | 2970 | 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, | |
| 2970 | 2973 | .forward, |
| 2971 | 2974 | .{ .@"export" = .{ |
| 2972 | 2975 | .main_name = nav.name, |
| ... | ... | @@ -2985,14 +2988,14 @@ pub fn genDecl(o: *Object) !void { |
| 2985 | 2988 | const w = o.writer(); |
| 2986 | 2989 | if (variable.is_weak_linkage) try w.writeAll("zig_weak_linkage "); |
| 2987 | 2990 | 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| | |
| 2989 | 2992 | try w.print("zig_linksection({s}) ", .{fmtStringLiteral(s, null)}); |
| 2990 | 2993 | try o.dg.renderTypeAndName( |
| 2991 | 2994 | w, |
| 2992 | 2995 | nav_ty, |
| 2993 | 2996 | .{ .nav = o.dg.pass.nav }, |
| 2994 | 2997 | .{}, |
| 2995 | nav.status.resolved.alignment, | |
| 2998 | nav.status.fully_resolved.alignment, | |
| 2996 | 2999 | .complete, |
| 2997 | 3000 | ); |
| 2998 | 3001 | try w.writeAll(" = "); |
| ... | ... | @@ -3002,10 +3005,10 @@ pub fn genDecl(o: *Object) !void { |
| 3002 | 3005 | }, |
| 3003 | 3006 | else => try genDeclValue( |
| 3004 | 3007 | o, |
| 3005 | Value.fromInterned(nav.status.resolved.val), | |
| 3008 | Value.fromInterned(nav.status.fully_resolved.val), | |
| 3006 | 3009 | .{ .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", | |
| 3009 | 3012 | ), |
| 3010 | 3013 | } |
| 3011 | 3014 | } |
src/codegen/llvm.zig+28-36| ... | ... | @@ -1476,7 +1476,7 @@ pub const Object = struct { |
| 1476 | 1476 | } }, &o.builder); |
| 1477 | 1477 | } |
| 1478 | 1478 | |
| 1479 | if (nav.status.resolved.@"linksection".toSlice(ip)) |section| | |
| 1479 | if (nav.status.fully_resolved.@"linksection".toSlice(ip)) |section| | |
| 1480 | 1480 | function_index.setSection(try o.builder.string(section), &o.builder); |
| 1481 | 1481 | |
| 1482 | 1482 | var deinit_wip = true; |
| ... | ... | @@ -1684,7 +1684,7 @@ pub const Object = struct { |
| 1684 | 1684 | const file = try o.getDebugFile(file_scope); |
| 1685 | 1685 | |
| 1686 | 1686 | 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"; | |
| 1688 | 1688 | const debug_decl_type = try o.lowerDebugType(fn_ty); |
| 1689 | 1689 | |
| 1690 | 1690 | const subprogram = try o.builder.debugSubprogram( |
| ... | ... | @@ -2928,9 +2928,7 @@ pub const Object = struct { |
| 2928 | 2928 | const gpa = o.gpa; |
| 2929 | 2929 | const nav = ip.getNav(nav_index); |
| 2930 | 2930 | 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)); | |
| 2934 | 2932 | const gop = try o.nav_map.getOrPut(gpa, nav_index); |
| 2935 | 2933 | if (gop.found_existing) return gop.value_ptr.ptr(&o.builder).kind.function; |
| 2936 | 2934 | |
| ... | ... | @@ -2938,14 +2936,14 @@ pub const Object = struct { |
| 2938 | 2936 | const target = owner_mod.resolved_target.result; |
| 2939 | 2937 | const sret = firstParamSRet(fn_info, zcu, target); |
| 2940 | 2938 | |
| 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 }; | |
| 2945 | 2943 | const function_index = try o.builder.addFunction( |
| 2946 | 2944 | try o.lowerType(ty), |
| 2947 | 2945 | try o.builder.strtabString((if (is_extern) nav.name else nav.fqn).toSlice(ip)), |
| 2948 | toLlvmAddressSpace(resolved.@"addrspace", target), | |
| 2946 | toLlvmAddressSpace(nav.getAddrspace(), target), | |
| 2949 | 2947 | ); |
| 2950 | 2948 | gop.value_ptr.* = function_index.ptrConst(&o.builder).global; |
| 2951 | 2949 | |
| ... | ... | @@ -3063,8 +3061,8 @@ pub const Object = struct { |
| 3063 | 3061 | } |
| 3064 | 3062 | } |
| 3065 | 3063 | |
| 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); | |
| 3068 | 3066 | |
| 3069 | 3067 | // Function attributes that are independent of analysis results of the function body. |
| 3070 | 3068 | try o.addCommonFnAttributes( |
| ... | ... | @@ -3249,17 +3247,21 @@ pub const Object = struct { |
| 3249 | 3247 | const zcu = pt.zcu; |
| 3250 | 3248 | const ip = &zcu.intern_pool; |
| 3251 | 3249 | 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 }, | |
| 3257 | 3259 | }; |
| 3258 | 3260 | |
| 3259 | 3261 | const variable_index = try o.builder.addVariable( |
| 3260 | 3262 | try o.builder.strtabString((if (is_extern) nav.name else nav.fqn).toSlice(ip)), |
| 3261 | 3263 | try o.lowerType(Type.fromInterned(nav.typeOf(ip))), |
| 3262 | toLlvmGlobalAddressSpace(resolved.@"addrspace", zcu.getTarget()), | |
| 3264 | toLlvmGlobalAddressSpace(nav.getAddrspace(), zcu.getTarget()), | |
| 3263 | 3265 | ); |
| 3264 | 3266 | gop.value_ptr.* = variable_index.ptrConst(&o.builder).global; |
| 3265 | 3267 | |
| ... | ... | @@ -4528,20 +4530,10 @@ pub const Object = struct { |
| 4528 | 4530 | const zcu = pt.zcu; |
| 4529 | 4531 | const ip = &zcu.intern_pool; |
| 4530 | 4532 | |
| 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); | |
| 4542 | 4534 | |
| 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); | |
| 4545 | 4537 | |
| 4546 | 4538 | const is_fn_body = nav_ty.zigTypeTag(zcu) == .@"fn"; |
| 4547 | 4539 | if ((!is_fn_body and !nav_ty.hasRuntimeBits(zcu)) or |
| ... | ... | @@ -4551,13 +4543,13 @@ pub const Object = struct { |
| 4551 | 4543 | } |
| 4552 | 4544 | |
| 4553 | 4545 | 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 | |
| 4555 | 4547 | else |
| 4556 | (try o.resolveGlobalNav(owner_nav_index)).ptrConst(&o.builder).global; | |
| 4548 | (try o.resolveGlobalNav(nav_index)).ptrConst(&o.builder).global; | |
| 4557 | 4549 | |
| 4558 | 4550 | const llvm_val = try o.builder.convConst( |
| 4559 | 4551 | 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())), | |
| 4561 | 4553 | ); |
| 4562 | 4554 | |
| 4563 | 4555 | return o.builder.convConst(llvm_val, try o.lowerType(ptr_ty)); |
| ... | ... | @@ -4799,7 +4791,7 @@ pub const NavGen = struct { |
| 4799 | 4791 | const ip = &zcu.intern_pool; |
| 4800 | 4792 | const nav_index = ng.nav_index; |
| 4801 | 4793 | const nav = ip.getNav(nav_index); |
| 4802 | const resolved = nav.status.resolved; | |
| 4794 | const resolved = nav.status.fully_resolved; | |
| 4803 | 4795 | |
| 4804 | 4796 | 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)) { |
| 4805 | 4797 | .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 { |
| 5765 | 5757 | const msg_nav_index = zcu.panic_messages[@intFromEnum(panic_id)].unwrap().?; |
| 5766 | 5758 | const msg_nav = ip.getNav(msg_nav_index); |
| 5767 | 5759 | 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); | |
| 5769 | 5761 | const null_opt_addr_global = try fg.resolveNullOptUsize(); |
| 5770 | 5762 | const target = zcu.getTarget(); |
| 5771 | 5763 | const llvm_usize = try o.lowerType(Type.usize); |
src/codegen/spirv.zig+16-13| ... | ... | @@ -268,7 +268,7 @@ pub const Object = struct { |
| 268 | 268 | // TODO: Extern fn? |
| 269 | 269 | const kind: SpvModule.Decl.Kind = if (ip.isFunctionType(nav.typeOf(ip))) |
| 270 | 270 | .func |
| 271 | else switch (nav.status.resolved.@"addrspace") { | |
| 271 | else switch (nav.getAddrspace()) { | |
| 272 | 272 | .generic => .invocation_global, |
| 273 | 273 | else => .global, |
| 274 | 274 | }; |
| ... | ... | @@ -1279,17 +1279,20 @@ const NavGen = struct { |
| 1279 | 1279 | const ip = &zcu.intern_pool; |
| 1280 | 1280 | const ty_id = try self.resolveType(ty, .direct); |
| 1281 | 1281 | 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 => {}, | |
| 1290 | 1295 | }, |
| 1291 | .@"extern" => assert(!ip.isFunctionType(nav_ty.toIntern())), // TODO | |
| 1292 | else => {}, | |
| 1293 | 1296 | } |
| 1294 | 1297 | |
| 1295 | 1298 | if (!nav_ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) { |
| ... | ... | @@ -1305,7 +1308,7 @@ const NavGen = struct { |
| 1305 | 1308 | .global, .invocation_global => spv_decl.result_id, |
| 1306 | 1309 | }; |
| 1307 | 1310 | |
| 1308 | const storage_class = self.spvStorageClass(nav.status.resolved.@"addrspace"); | |
| 1311 | const storage_class = self.spvStorageClass(nav.getAddrspace()); | |
| 1309 | 1312 | try self.addFunctionDep(spv_decl_index, storage_class); |
| 1310 | 1313 | |
| 1311 | 1314 | const decl_ptr_ty_id = try self.ptrType(nav_ty, storage_class); |
| ... | ... | @@ -3182,7 +3185,7 @@ const NavGen = struct { |
| 3182 | 3185 | }; |
| 3183 | 3186 | assert(maybe_init_val == null); // TODO |
| 3184 | 3187 | |
| 3185 | const storage_class = self.spvStorageClass(nav.status.resolved.@"addrspace"); | |
| 3188 | const storage_class = self.spvStorageClass(nav.getAddrspace()); | |
| 3186 | 3189 | assert(storage_class != .Generic); // These should be instance globals |
| 3187 | 3190 | |
| 3188 | 3191 | const ptr_ty_id = try self.ptrType(ty, storage_class); |
src/link.zig+1-1| ... | ... | @@ -692,7 +692,7 @@ pub const File = struct { |
| 692 | 692 | /// May be called before or after updateExports for any given Nav. |
| 693 | 693 | pub fn updateNav(base: *File, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) UpdateNavError!void { |
| 694 | 694 | const nav = pt.zcu.intern_pool.getNav(nav_index); |
| 695 | assert(nav.status == .resolved); | |
| 695 | assert(nav.status == .fully_resolved); | |
| 696 | 696 | switch (base.tag) { |
| 697 | 697 | inline else => |tag| { |
| 698 | 698 | dev.check(tag.devFeature()); |
src/link/C.zig+9-11| ... | ... | @@ -217,7 +217,7 @@ pub fn updateFunc( |
| 217 | 217 | .mod = zcu.navFileScope(func.owner_nav).mod, |
| 218 | 218 | .error_msg = null, |
| 219 | 219 | .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, | |
| 221 | 221 | .fwd_decl = fwd_decl.toManaged(gpa), |
| 222 | 222 | .ctype_pool = ctype_pool.*, |
| 223 | 223 | .scratch = .{}, |
| ... | ... | @@ -320,11 +320,11 @@ pub fn updateNav(self: *C, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) ! |
| 320 | 320 | const ip = &zcu.intern_pool; |
| 321 | 321 | |
| 322 | 322 | 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)) { | |
| 324 | 324 | .func => return, |
| 325 | 325 | .@"extern" => .none, |
| 326 | 326 | .variable => |variable| variable.init, |
| 327 | else => nav.status.resolved.val, | |
| 327 | else => nav.status.fully_resolved.val, | |
| 328 | 328 | }; |
| 329 | 329 | if (nav_init != .none and !Value.fromInterned(nav_init).typeOf(zcu).hasRuntimeBits(zcu)) return; |
| 330 | 330 | |
| ... | ... | @@ -499,7 +499,7 @@ pub fn flushModule(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: |
| 499 | 499 | av_block, |
| 500 | 500 | self.exported_navs.getPtr(nav), |
| 501 | 501 | export_names, |
| 502 | if (ip.indexToKey(zcu.navValue(nav).toIntern()) == .@"extern") | |
| 502 | if (ip.getNav(nav).getExtern(ip) != null) | |
| 503 | 503 | ip.getNav(nav).name.toOptional() |
| 504 | 504 | else |
| 505 | 505 | .none, |
| ... | ... | @@ -544,13 +544,11 @@ pub fn flushModule(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: |
| 544 | 544 | }, |
| 545 | 545 | self.getString(av_block.code), |
| 546 | 546 | ); |
| 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)); | |
| 554 | 552 | |
| 555 | 553 | const file = self.base.file.?; |
| 556 | 554 | 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, |
| 1110 | 1110 | const atom_index = try coff.getOrCreateAtomForNav(func.owner_nav); |
| 1111 | 1111 | coff.freeRelocations(atom_index); |
| 1112 | 1112 | |
| 1113 | coff.navs.getPtr(func.owner_nav).?.section = coff.text_section_index.?; | |
| 1114 | ||
| 1113 | 1115 | var code_buffer = std.ArrayList(u8).init(gpa); |
| 1114 | 1116 | defer code_buffer.deinit(); |
| 1115 | 1117 | |
| ... | ... | @@ -1223,6 +1225,8 @@ pub fn updateNav( |
| 1223 | 1225 | coff.freeRelocations(atom_index); |
| 1224 | 1226 | const atom = coff.getAtom(atom_index); |
| 1225 | 1227 | |
| 1228 | coff.navs.getPtr(nav_index).?.section = coff.getNavOutputSection(nav_index); | |
| 1229 | ||
| 1226 | 1230 | var code_buffer = std.ArrayList(u8).init(gpa); |
| 1227 | 1231 | defer code_buffer.deinit(); |
| 1228 | 1232 | |
| ... | ... | @@ -1342,7 +1346,8 @@ pub fn getOrCreateAtomForNav(coff: *Coff, nav_index: InternPool.Nav.Index) !Atom |
| 1342 | 1346 | if (!gop.found_existing) { |
| 1343 | 1347 | gop.value_ptr.* = .{ |
| 1344 | 1348 | .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.?, | |
| 1346 | 1351 | .exports = .{}, |
| 1347 | 1352 | }; |
| 1348 | 1353 | } |
| ... | ... | @@ -1355,7 +1360,7 @@ fn getNavOutputSection(coff: *Coff, nav_index: InternPool.Nav.Index) u16 { |
| 1355 | 1360 | const nav = ip.getNav(nav_index); |
| 1356 | 1361 | const ty = Type.fromInterned(nav.typeOf(ip)); |
| 1357 | 1362 | 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); | |
| 1359 | 1364 | const index: u16 = blk: { |
| 1360 | 1365 | if (val.isUndefDeep(zcu)) { |
| 1361 | 1366 | // TODO in release-fast and release-small, we should put undef in .bss |
| ... | ... | @@ -2348,10 +2353,10 @@ pub fn getNavVAddr( |
| 2348 | 2353 | const ip = &zcu.intern_pool; |
| 2349 | 2354 | const nav = ip.getNav(nav_index); |
| 2350 | 2355 | 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().?; | |
| 2355 | 2360 | const atom_index = coff.getAtomIndexForSymbol(.{ |
| 2356 | 2361 | .sym_index = reloc_info.parent.atom_index, |
| 2357 | 2362 | .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 |
| 2281 | 2281 | const nav_ty = nav_val.typeOf(zcu); |
| 2282 | 2282 | const nav_ty_reloc_index = try wip_nav.refForward(); |
| 2283 | 2283 | 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 | |
| 2285 | 2285 | nav_ty.abiAlignment(zcu).toByteUnits().?); |
| 2286 | 2286 | try diw.writeByte(@intFromBool(false)); |
| 2287 | 2287 | wip_nav.finishForward(nav_ty_reloc_index); |
| ... | ... | @@ -2313,7 +2313,7 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In |
| 2313 | 2313 | try wip_nav.refType(ty); |
| 2314 | 2314 | const addr: Loc = .{ .addr = .{ .sym = sym_index } }; |
| 2315 | 2315 | 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 | |
| 2317 | 2317 | ty.abiAlignment(zcu).toByteUnits().?); |
| 2318 | 2318 | try diw.writeByte(@intFromBool(false)); |
| 2319 | 2319 | }, |
| ... | ... | @@ -2388,7 +2388,7 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In |
| 2388 | 2388 | wip_nav.func_high_pc = @intCast(wip_nav.debug_info.items.len); |
| 2389 | 2389 | try diw.writeInt(u32, 0, dwarf.endian); |
| 2390 | 2390 | 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) { | |
| 2392 | 2392 | .none => target_info.defaultFunctionAlignment(target), |
| 2393 | 2393 | else => |a| a.maxStrict(target_info.minFunctionAlignment(target)), |
| 2394 | 2394 | }.toByteUnits().?); |
| ... | ... | @@ -2952,7 +2952,7 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool |
| 2952 | 2952 | const nav_ty = nav_val.typeOf(zcu); |
| 2953 | 2953 | try wip_nav.refType(nav_ty); |
| 2954 | 2954 | 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 | |
| 2956 | 2956 | nav_ty.abiAlignment(zcu).toByteUnits().?); |
| 2957 | 2957 | try diw.writeByte(@intFromBool(false)); |
| 2958 | 2958 | }, |
| ... | ... | @@ -2977,7 +2977,7 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool |
| 2977 | 2977 | try wip_nav.strp(nav.name.toSlice(ip)); |
| 2978 | 2978 | try wip_nav.strp(nav.fqn.toSlice(ip)); |
| 2979 | 2979 | 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 | |
| 2981 | 2981 | nav_ty.abiAlignment(zcu).toByteUnits().?); |
| 2982 | 2982 | try diw.writeByte(@intFromBool(false)); |
| 2983 | 2983 | 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( |
| 925 | 925 | const ip = &zcu.intern_pool; |
| 926 | 926 | const nav = ip.getNav(nav_index); |
| 927 | 927 | 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); | |
| 936 | 933 | const this_sym = self.symbol(this_sym_index); |
| 937 | 934 | const vaddr = this_sym.address(.{}, elf_file); |
| 938 | 935 | switch (reloc_info.parent) { |
| ... | ... | @@ -1107,15 +1104,13 @@ pub fn freeNav(self: *ZigObject, elf_file: *Elf, nav_index: InternPool.Nav.Index |
| 1107 | 1104 | |
| 1108 | 1105 | pub fn getOrCreateMetadataForNav(self: *ZigObject, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Symbol.Index { |
| 1109 | 1106 | const gpa = zcu.gpa; |
| 1107 | const ip = &zcu.intern_pool; | |
| 1110 | 1108 | const gop = try self.navs.getOrPut(gpa, nav_index); |
| 1111 | 1109 | if (!gop.found_existing) { |
| 1112 | 1110 | const symbol_index = try self.newSymbolWithAtom(gpa, 0); |
| 1113 | const nav_val = Value.fromInterned(zcu.intern_pool.getNav(nav_index).status.resolved.val); | |
| 1114 | 1111 | 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; | |
| 1119 | 1114 | } |
| 1120 | 1115 | gop.value_ptr.* = .{ .symbol_index = symbol_index }; |
| 1121 | 1116 | } |
| ... | ... | @@ -1547,7 +1542,7 @@ pub fn updateNav( |
| 1547 | 1542 | |
| 1548 | 1543 | log.debug("updateNav {}({d})", .{ nav.fqn.fmt(ip), nav_index }); |
| 1549 | 1544 | |
| 1550 | const nav_init = switch (ip.indexToKey(nav.status.resolved.val)) { | |
| 1545 | const nav_init = switch (ip.indexToKey(nav.status.fully_resolved.val)) { | |
| 1551 | 1546 | .func => .none, |
| 1552 | 1547 | .variable => |variable| variable.init, |
| 1553 | 1548 | .@"extern" => |@"extern"| { |
| ... | ... | @@ -1560,7 +1555,7 @@ pub fn updateNav( |
| 1560 | 1555 | self.symbol(sym_index).flags.is_extern_ptr = true; |
| 1561 | 1556 | return; |
| 1562 | 1557 | }, |
| 1563 | else => nav.status.resolved.val, | |
| 1558 | else => nav.status.fully_resolved.val, | |
| 1564 | 1559 | }; |
| 1565 | 1560 | |
| 1566 | 1561 | 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( |
| 608 | 608 | const ip = &zcu.intern_pool; |
| 609 | 609 | const nav = ip.getNav(nav_index); |
| 610 | 610 | 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); | |
| 619 | 616 | const sym = self.symbols.items[sym_index]; |
| 620 | 617 | const vaddr = sym.getAddress(.{}, macho_file); |
| 621 | 618 | switch (reloc_info.parent) { |
| ... | ... | @@ -882,7 +879,7 @@ pub fn updateNav( |
| 882 | 879 | const ip = &zcu.intern_pool; |
| 883 | 880 | const nav = ip.getNav(nav_index); |
| 884 | 881 | |
| 885 | const nav_init = switch (ip.indexToKey(nav.status.resolved.val)) { | |
| 882 | const nav_init = switch (ip.indexToKey(nav.status.fully_resolved.val)) { | |
| 886 | 883 | .func => .none, |
| 887 | 884 | .variable => |variable| variable.init, |
| 888 | 885 | .@"extern" => |@"extern"| { |
| ... | ... | @@ -895,7 +892,7 @@ pub fn updateNav( |
| 895 | 892 | sym.flags.is_extern_ptr = true; |
| 896 | 893 | return; |
| 897 | 894 | }, |
| 898 | else => nav.status.resolved.val, | |
| 895 | else => nav.status.fully_resolved.val, | |
| 899 | 896 | }; |
| 900 | 897 | |
| 901 | 898 | 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 { |
| 1561 | 1558 | if (!macho_file.base.comp.config.any_non_single_threaded) |
| 1562 | 1559 | return false; |
| 1563 | 1560 | 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); | |
| 1569 | 1562 | } |
| 1570 | 1563 | |
| 1571 | 1564 | fn 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) |
| 1021 | 1021 | const atom_idx = gop.value_ptr.index; |
| 1022 | 1022 | // handle externs here because they might not get updateDecl called on them |
| 1023 | 1023 | const nav = ip.getNav(nav_index); |
| 1024 | if (ip.indexToKey(nav.status.resolved.val) == .@"extern") { | |
| 1024 | if (nav.getExtern(ip) != null) { | |
| 1025 | 1025 | // this is a "phantom atom" - it is never actually written to disk, just convenient for us to store stuff about externs |
| 1026 | 1026 | if (nav.name.eqlSlice("etext", ip)) { |
| 1027 | 1027 | self.etext_edata_end_atom_indices[0] = atom_idx; |
| ... | ... | @@ -1370,7 +1370,7 @@ pub fn getNavVAddr( |
| 1370 | 1370 | const ip = &pt.zcu.intern_pool; |
| 1371 | 1371 | const nav = ip.getNav(nav_index); |
| 1372 | 1372 | log.debug("getDeclVAddr for {}", .{nav.name.fmt(ip)}); |
| 1373 | if (ip.indexToKey(nav.status.resolved.val) == .@"extern") { | |
| 1373 | if (nav.getExtern(ip) != null) { | |
| 1374 | 1374 | if (nav.name.eqlSlice("etext", ip)) { |
| 1375 | 1375 | try self.addReloc(reloc_info.parent.atom_index, .{ |
| 1376 | 1376 | .target = undefined, |
src/link/Wasm/ZigObject.zig+6-7| ... | ... | @@ -734,15 +734,14 @@ pub fn getNavVAddr( |
| 734 | 734 | const target_atom_index = try zig_object.getOrCreateAtomForNav(wasm, pt, nav_index); |
| 735 | 735 | const target_atom = wasm.getAtom(target_atom_index); |
| 736 | 736 | 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( | |
| 739 | 739 | wasm, |
| 740 | 740 | nav.name.toSlice(ip), |
| 741 | 741 | target_atom.sym_index, |
| 742 | 742 | @"extern".lib_name.toSlice(ip), |
| 743 | 743 | null, |
| 744 | ), | |
| 745 | else => {}, | |
| 744 | ); | |
| 746 | 745 | } |
| 747 | 746 | |
| 748 | 747 | 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 |
| 945 | 944 | segment.name = &.{}; // Ensure no accidental double free |
| 946 | 945 | } |
| 947 | 946 | |
| 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) { | |
| 950 | 949 | std.debug.assert(zig_object.imports.remove(atom.sym_index)); |
| 951 | 950 | } |
| 952 | 951 | 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 |
| 960 | 959 | if (sym.isGlobal()) { |
| 961 | 960 | std.debug.assert(zig_object.global_syms.remove(atom.sym_index)); |
| 962 | 961 | } |
| 963 | if (ip.isFunctionType(ip.typeOf(nav_val))) { | |
| 962 | if (ip.isFunctionType(nav.typeOf(ip))) { | |
| 964 | 963 | zig_object.functions_free_list.append(gpa, sym.index) catch {}; |
| 965 | 964 | std.debug.assert(zig_object.atom_types.remove(atom_index)); |
| 966 | 965 | } else { |
test/behavior/globals.zig+96| ... | ... | @@ -66,3 +66,99 @@ test "global loads can affect liveness" { |
| 66 | 66 | S.f(); |
| 67 | 67 | try std.testing.expect(y.a == 1); |
| 68 | 68 | } |
| 69 | ||
| 70 | test "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 | ||
| 85 | test "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 | ||
| 112 | test "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 | ||
| 136 | test "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 @@ |
| 1 | const S = struct { self: *S, x: u32 }; | |
| 2 | const s: S = .{ .self = &s, .x = 123 }; | |
| 3 | ||
| 4 | comptime { | |
| 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 |