winmd-inspect: disambiguate colliding names with fabricated namespaces - #351
winmd-inspect: disambiguate colliding names with fabricated namespaces#351compnerd wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ca46124ecd
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
ca46124 to
20aed49
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 20aed49ef5
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
20aed49 to
5e22166
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5e22166e40
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
5e22166 to
6ad027f
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6ad027fe48
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
6ad027f to
480b0e3
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 480b0e3bec
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
480b0e3 to
c6e410c
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c6e410c7d3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
1f99012 to
285009e
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 285009ea57
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
285009e to
4ca5e73
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4ca5e73c94
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
4ca5e73 to
c535488
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c535488547
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
c535488 to
9dd1364
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9dd1364b40
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
9dd1364 to
561a427
Compare
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 561a427072
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
561a427 to
fe9943e
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fe9943e45a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d7b3b7e6d6
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
d7b3b7e to
9c163ef
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9c163ef328
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
9c163ef to
768b2f5
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 768b2f51cb
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| try emitted.contains(emission.id) | ||
| && emission.name == language.escape(language.root) | ||
| && session.storage.nesting(of: emission.id).isEmpty |
There was a problem hiding this comment.
Require the fallback-root match to be an interface
When a rootless interface reaches a top-level value type named IUnknown through one of its signatures, this predicate sets rooted even though the emission is a struct or enum. The synthesized protocol IRoot: IUnknown is then excluded from the frontier set, so the value type remains bare and the inheritance resolves to a non-protocol type, producing invalid Swift. Restrict this exemption to an emitted interface that can actually serve as the fallback protocol.
Useful? React with 👍 / 👎.
| let folded = fold(kids.sorted { earliest($0) < earliest($1) }) | ||
| let container = Shell.inject(folded.block, | ||
| into: "public enum \(label(node)) {\n}", | ||
| container: label(node)) | ||
| return (folded.header, container, folded.footer) |
There was a problem hiding this comment.
Wrap every conditional branch declaring the primary type
When a custom template declares the same ambiguous value type in multiple conditional-compilation clauses, such as #if A; struct Point {}; #else; struct Point {}; #endif, partition selects only the first declaration and leaves the other clause in folded.footer. This namespace branch consequently wraps only the first Point; when the alternate clause is active, signatures still spell A.Point but the output declares only a bare Point. Preserve the conditional structure while applying the fabricated namespace to every branch that declares the primary type.
Useful? React with 👍 / 👎.
768b2f5 to
f93536b
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f93536bf72
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| for reference in spelled | ||
| where emitted.contains(reference.owner) | ||
| && (reference.id == nil || !emitted.contains(reference.id!)) | ||
| && !(reference.fallback && rooted) { |
There was a problem hiding this comment.
Reserve fixed template type names before fabricating namespaces
When an ambiguous value type is under a namespace whose leading segment matches a type name emitted directly by the bundled template, this loop cannot detect the shadow because spelled contains only metadata references and bases. For example, two reached Point types can fabricate public enum OptionSet, after which the bundled flags projection nested inside it declares struct Point: OptionSet; Swift resolves OptionSet to the enclosing enum rather than the standard-library protocol, producing invalid source. Primitive spellings such as CInt have the same problem and can silently bind fields to the fabricated enum, so fixed dialect/template type references must also be reserved against namespace containers.
Useful? React with 👍 / 👎.
f93536b to
141c8cb
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 141c8cba72
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| for modifier in mods { | ||
| try record(modifier.type, into: &table, with: storage) | ||
| try record(modifier.type, into: &table, spelling: &spellings, | ||
| with: storage, qualifying: qualifying) |
There was a problem hiding this comment.
Revisit references first cached as modifiers
When the same TypeDefOrRef token first appears as a custom modifier and later as an indirect reference to a value type beneath a generic encloser, this call records it with the default embedded: true. The later pointer/byref occurrence then returns early from record because the token is already cached, so decoding emits an invalid spelling such as UnsafeMutablePointer<Outer.Inner> instead of the intended opaque pointer. Fresh evidence beyond the earlier indirect-reference report is this modifier-first cache path; pass the occurrence context here or allow the later ordinary occurrence to revise the cached resolution.
Useful? React with 👍 / 👎.
141c8cb to
61afc02
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 61afc024d7
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| let label = String((bridge ?? reference.identity.name) | ||
| .prefix { $0 != "." && $0 != "`" }) |
There was a problem hiding this comment.
Track the projected Guid spelling
When a signature references System.Guid, decoding emits IID or CLSID rather than Guid (with the parameter name selecting CLSID). This fallback nevertheless records Guid as the frontier label, so a reached value type such as A.CLSID is not treated as contended and remains a bare struct CLSID; the generated parameter then binds to that struct instead of the imported CLSID. Record the same IID/CLSID spelling used by decoding for each occurrence.
Useful? React with 👍 / 👎.
61afc02 to
5485bf0
Compare
5485bf0 to
35e3968
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 35e3968cb5
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| let leaf = reference.identity.name.split(separator: ".").last | ||
| .map(String.init) ?? reference.identity.name | ||
| let identity = Identity(namespace: reference.identity.namespace, | ||
| name: leaf) | ||
| let bridge = dialect.known[identity] |
There was a problem hiding this comment.
Reserve the bridged encloser name for nested references
When a custom language maps an enclosing type (for example, wellknown Outer OuterBridge) and a signature names Outer.Inner, TypeDefOrRef.spelling emits OuterBridge.Inner via its encloser-bridge lookup. This frontier scan only tests the leaf Inner and then falls back to Outer, so a reached value type such as A.OuterBridge remains bare and captures the generated reference instead of being wrapped; mirror the decoder's encloser-bridge lookup when deriving the frontier label.
Useful? React with 👍 / 👎.
35e3968 to
cf2c451
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cf2c451bb2
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| } else { | ||
| generic = try storage.enclosedByGeneric(tuple) | ||
| value = try storage.kind(tuple).value |
There was a problem hiding this comment.
Preserve external generic-nested value kinds
When a by-value signature names an external value type nested under a generic encloser (for example, External.Outer1.Innerencoded asVALUETYPE), there is no local definition, so this branch classifies the TypeRefrow itself; since aTypeRefhas neitherFlagsnorExtends, kind(tuple)always reports.class. The subsequent generic guard therefore discards the reference, and decoding silently substitutes the dialect's opaque pointer instead of dropping the containing declaration, changing its ABI layout. Preserve the NamedKind.valuecarried bySignatureType.named` for unresolved external references rather than inferring their kind from the reference row.
Useful? React with 👍 / 👎.
cf2c451 to
ef315ea
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ef315ea1e0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if emission.kind == "struct" || emission.kind == "enum" { | ||
| container.insert(emission.id) |
There was a problem hiding this comment.
Validate the rendered declaration before making it a container
When a custom template renders an enclosing metadata struct/enum as another accepted type declaration, such as typealias Outer = CInt or protocol Outer, declared remains true and this metadata-kind check still marks it as a nestable container. If the closure also reaches Outer.Inner, rendered tries to inject Inner into that alias/protocol; the alias fallback leaves Inner at file scope, while a protocol cannot contain the synthesized stored type, so signatures spelling Outer.Inner do not resolve. Fresh evidence beyond the earlier emittedness reports is that container eligibility is derived from emission.kind rather than the actual declaration kind found in the rendered body; require a rendered member-bearing struct/enum before nesting children.
Useful? React with 👍 / 👎.
Disambiguate the names two reached declarations would otherwise share, so every signature spelling resolves to the declaration it names and the generated source compiles. Builds on the nesting slice; the frontier reservation the shadow check and contention need is the following slice. The projection is one flat top-level Swift scope, so a name is ambiguous when two or more of the emitted top-level declarations bear it, of any kind. Only a value type can be wrapped, so an ambiguous value type is emitted fully namespace-qualified under fabricated namespace `enum` containers (`A.Point`/`B.Point`) while the same-named `protocol` stays bare. A closure integration test pins this cross-kind case end to end — a reached struct `A.Point` beside an interface, then a delegate, `B.Point` — so the value type wraps under `enum A` while the emitted non-value declaration stays a bare top-level `Point`, no redeclaration. Ambiguity is counted over the projected (arity-stripped) name and only over the reached declarations, so an unreachable same-named type never forces a wrap; the qualification keys off the value type's raw `namespace.name` identity and confirms the reference resolves locally. One `Storage.spelling(resolved:qualifying:)` rule drives the decode spelling, the emit nesting, and — through a `spelling(of id:)` overload — the inheritance clause, so a nested base under a wrapped encloser reads `A.Outer.IChild` exactly as a signature naming it decodes, and the three seams cannot drift. Nesting folds only a type's *primary* declaration into its container: a custom template may frame the type with a file-scope `import` header or an `extension` footer, which Swift permits only at file scope, so `Surface.partition` — locating the primary declaration off the syntax tree — splits them off the rendered body and they bubble up, through every enclosing container, to the roots. This is symmetric across a real `.type` container and a fabricated `.space` namespace: `nest` renders a node as a (header, declaration, footer) triple, folds each member's declaration in, and accumulates its members' hoisted headers and footers to pass further up. A hoisted auxiliary lands in the root file scope, so the per-scope collision check attributes it there rather than its container — and every nested type's auxiliaries hoist, however deep it sits, so two descendants of different wrapped containers each hoisting a same-named `struct Helper` are a file-scope redeclaration a per-container check would miss. Emittedness — which reached types the closure actually declares — is read from the rendered body off the syntax tree (`Surface.declarations`): the top-level `struct`/`class`/`enum`/`protocol`/`actor` and `typealias` names, so per-type boilerplate, a documented-but-omitted ABI helper, a value declaration of the type's name, a nested member, or a declaration inside a comment does not count. Every *other* top-level name a body declares — not just the primary and the specially recognised generic `<name>ABI` helper, but any auxiliary a custom template emits (a `struct A` beside a `protocol IRoot`) — is recorded as a scope occupant, so the per-scope collision check faults a fabricated namespace `enum A` or another declaration that redeclares it. The collision tally and the emit both key off this emitted set, computed once after a reachability prune, so a body is spelled with the same set that folds it into the tree. The emitted set is re-pruned against the *bodied* declarations before it drives the tally: the first prune propagates a node's references on its metadata kind, but a custom template may omit an intermediate declaration (render nothing for a reached `struct Point`), so a type reached only through that omitted node — a `Widget` a `Point` field names — is an orphan the emitted source never references, dropped rather than left to render unattached and fault an unrelated collision. A by-value type nested beneath a generic encloser — named directly or through a `TypeRef`, its kind read off the resolved definition — is dropped rather than misrendered as an ABI-changing opaque pointer; but only a genuinely *by-value* occurrence poisons its owner — a reference solely behind a pointer, byref, or array embeds no layout and renders as an opaque pointer, so it drops nothing — and its referent is left unrecorded, so the decode spells that opaque pointer rather than an uncompilable `UnsafeMutablePointer<Outer.Inner>` naming a type the closure never emits. The collision fixtures assemble their metadata through `WinMDFixture`, an in-target `#~` assembler that interns the heaps and computes the table ranges, with a fluent `WinMDBuilder` facade that assigns rids, links member lists, encodes signatures, and resolves references — so a scenario reads in a dozen declarative lines instead of a hundred of hand-encoded bytes. This slice also reserves the frontier names a signature spells but the closure does not emit, so a reached value type sharing a name with one contends — wrapping and spelling namespace-qualified rather than bare and capturing the frontier's reference. The frontier set is derived once from the emitted-set complement: the walk records every spelled reference — a signature-named type or an interface's named base — as its emitted top-level spelling paired with the `TypeDef` Id it resolves to (its outermost encloser's, so a nested `A.Inner` turns on its root `A`), and a reference is a frontier exactly when that Id is absent from the emitted set. `Storage.collisions(contended:)` bumps a reached value type whose projected name a frontier bears to ambiguous — so an emitted signature naming both a local `A.Point` and an external `Point` wraps `A.Point` rather than leaving both positions bound to the generated bare `Point`. Whether a fabricated namespace would then *shadow* one of those bare references is the following slice; this slice reserves and contends. The reservation reads the metadata resolution, not the rendered text. The decode records every signature reference of every emitted owner in `spelled` — its owner, the local `Id` it resolves to (or nil for an external), and its category — so a reference is a frontier exactly when that referent is unemitted. The template spells names; it does not choose which references the surface bears, so the closure keys renderedness off that graph rather than re-scanning the rendered body for each label. A reached value type still contends with a frontier of its projected name, and a literal identifier a template writes that no signature references is absent from `spelled` and reserves nothing. The synthesized fallback root (`IUnknown`) is not a frontier only when a *top-level* local interface of that name is emitted: the bare `protocol IRoot: IUnknown` resolves at file scope, so a metadata-nested `Outer.IUnknown` does not satisfy the exemption, and with several local `IUnknown`s the reached one is chosen. The exemption is keyed on a fallback flag, not the spelling, so an explicit external `IUnknown` parameter or base stays a frontier. A `known`-bridged reference reserves the frontier under its bridge leaf name, keyed on the identity's leaf component as the decode spells it: a nested `Outer.Inner` bridged `wellknown Inner …` reserves `InnerBridge`, not the enclosing root `Outer`. Keying the bridge on the qualified `Outer.Inner` missed it and reserved `Outer`, so a reached `A.InnerBridge` of the bridge name stayed uncontended and captured the frontier bare rather than wrapping under `enum A`. The base render tolerates a `Queries/bases.sql` override predating the `ref`/`def` provenance columns. The bundled render reads those columns to resolve a local base's Id, so a copied override carrying the former `(base, spec)` view shape faults on the missing column; `heritage` catches that `SQLError.column` and re-runs a legacy shape that resolves each base to a local interface by name — the best-effort resolution the provenance replaced — so an emitted local base keeps its Id (not mistaken for a frontier) and the older override renders rather than failing, the extension-point tolerance `fields`/`requires` get.
ef315ea to
d85afcf
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d85afcff00
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| let root: Int? = if let id = rows.first?[0].integer { | ||
| try session.storage.nesting(of: id).first?.id ?? id |
There was a problem hiding this comment.
Keep bridged nested references on the frontier
When a wellknown Inner InnerBridge reference names Outer.Inner and Outer is independently emitted, pairing the bridged label with the outermost encloser's ID makes the frontier pass treat InnerBridge as emitted. The nested Inner declaration is actually suppressed by the bridge, so a reached value type such as A.InnerBridge remains bare and captures the signature intended for the imported bridge. Even with the new leaf-based bridge lookup, bridged references should not use the encloser's emittedness when constructing Spelled.
Useful? React with 👍 / 👎.
Disambiguate the names two reached declarations would otherwise share, so every signature spelling resolves to the declaration it names and the generated source compiles. Builds on the nesting slice, adding the collision tally, the fabricated namespace containers, and the frontier logic.
The projection is one flat top-level Swift scope, so a name is ambiguous when two or more of the top-level declarations the closure emits bear it of any kind — a second value type, an interface or delegate, or a runtime class. Because only a value type can be wrapped, an ambiguous value type is spelled and emitted fully namespace-qualified under fabricated namespace
enumcontainers (A.Point/B.Point), while the same-namedprotocolstays bare. Ambiguity is counted over the projected (arity-stripped) name the emission carries and only over the reached declarations, so an unreachable same-named type never forces a wrap; the qualification keys off the value type's rawnamespace.nameidentity (so a same-named generic protocol does not borrow it) and confirms the reference resolves locally (so an external same-named type spells bare). The decode spelling, the emit nesting, and the collision tally derive from one source onStorage, so they cannot drift.The per-scope validation reconciles every declaration scope — the top-level roots and each container's direct children — so members bear distinct labels: a residual clash faults rather than emitting uncompilable output. Two same-named top-level protocols fault
ambiguous; a fabricated namespace container shadowing an emitted type, or a generic interface/delegate's synthesizedinternal protocol <name>ABIcolliding with a co-emitted type, or a fabricated container shadowing a signature-referenced frontier type the closure spells but does not emit, faultscollision. A frontier — a name the closure spells but does not emit — is derived once from the emitted-set complement rather than enumerated per reason: the walk records every spelled reference (a signature-named type or an interface's named base) as its emitted top-level spelling paired with theTypeDefIdit resolves to, and a reference is a frontier exactly when thatId(its outermost encloser's, so a nestedA.Innerturns on its rootA, not onInner) is absent from the emitted set. One rule then covers every way a name goes unemitted — a runtime class, a GUID-less shape, aknown-bridged or layout-rejected value type, a metadata-nested protocol, a value type under a non-container, and an external reference (including anexternal base the
basesview names though the local-onlyrequireswalk does not). The one frontier set serves both seams: the fabricated-namespace shadow check faults on a label it bears, and the reached-only ambiguity tally treats it as a distinct top-level bearer — a reachedA.Pointbeside a frontierB.Point(external, layout-rejected,known-bridged to the same target, or an inherited base) wraps and qualifies toA.Pointrather than spelling a barePointthat captures the frontier's reference. Deriving from the complement, not a name subtraction, keeps a top-level frontierAreserved even when an unrelated nestedFoo.Ais emitted, and reading theknownbridge name as the spelling lets a value type of that target name contend. A post-walk reachability prune keeps only the declarations reachable from the seeds through emittable nodes, dropping the orphans a discarded frontier would otherwise leave behind, and the emit renders each kept body once the reached-only qualification sets are fixed, so a body is spelled with the same set that folds it into the tree.Integration tests cover two same-named value types each wrapped in its namespace
enum, a value type colliding with a same-named protocol, arity and same-namespace-arity collisions, a generic type's ABI name colliding with a metadata type, a fabricated container shadowing an emitted type (at the root and a level down) and a frontier type (top-level, nested — whose enclosing root it shadows —, an external interface base, and an external base a fabricatedenum Amust not un-frontier merely because an unrelated nestedFoo.Ais emitted), a reached value type wrapping against a frontier of the same name (an externalreferent, a layout-rejected value type, an external base, and a
knownbridge target each), and an unreachable same-named type leaving a reached one bare.