Commit 4ca5e73
committed
winmd-inspect: disambiguate colliding names with fabricated namespaces
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 `enum` containers
(`A.Point`/`B.Point`), while the same-named `protocol` stays 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 raw `namespace.name` identity
(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 on
`Storage`, 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 synthesized
`internal protocol <name>ABI` colliding with a co-emitted type, or a fabricated
container shadowing a signature-referenced frontier type the closure spells but
does not emit, faults `collision`. The `<name>ABI` occupant is counted only when
the *selected* template actually declares it — read off the rendered body, not
assumed from the metadata arity — so a `-I` template that omits the helper does
not fault a spurious collision against a real same-named type. 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 the `TypeDef` `Id` it resolves to, and a reference is a
frontier exactly when that `Id` (its outermost encloser's, so a nested `A.Inner`
turns on its root `A`, not on `Inner`) is absent from the emitted set. One rule
then covers every way a name goes unemitted — a runtime class, a GUID-less
shape, a `known`-bridged or layout-rejected value type, a metadata-nested
protocol, a value type under a non-container, and an external reference
(including an
external base the `bases` view names though the local-only `requires` walk does
not). The one frontier set serves both seams: the fabricated-namespace shadow
check faults a fabricated `enum` whose name a frontier reference visible in its
scope bears, keyed per reference to its owner — a root segment is a top-level
`enum` visible as a bare name from any scope, so it shadows a frontier of its
name wherever owned; an inner segment (the `B` of `enum A.enum B` wrapping
`A.B.Point`) is visible only from within its own subtree, so it shadows only a
reference owned by a type whose fabricated enclosing path includes it, where a
bare `B` binds outward to the enclosing `A.B`, not the external type — and
the reached-only ambiguity tally treats it
as a distinct top-level bearer — a reached `A.Point` beside a frontier `B.Point`
(external, layout-rejected, `known`-bridged to the same target, or an inherited
base) wraps and qualifies to `A.Point` rather than spelling a bare `Point` that
captures the frontier's reference. Deriving from the complement, not a name
subtraction, keeps a top-level frontier `A` reserved even when an unrelated
nested `Foo.A` is emitted, and reading the `known` bridge 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 fabricated
`enum A` must not un-frontier merely because an unrelated nested `Foo.A` is
emitted), a reached value type wrapping against a frontier of the same name (an
external
referent, a layout-rejected value type, an external base, and a `known` bridge
target each), an unreachable same-named type leaving a reached one bare, an
inner fabricated segment shadowing a bare frontier reference owned by a type in
its subtree (faulting where a root-only check would let uncompilable source
through), and a `-I` template that omits the ABI helper leaving its generic type
uncontended.
The collision fixtures assemble their metadata through `WinMDFixture`, an
in-target `#~` assembler that interns the string and blob heaps and computes the
table ranges, so a fixture declares its rows rather than hand-maintaining the
byte offsets every edit would otherwise re-derive, and a fluent `WinMDBuilder`
facade over it lets a fixture state its interfaces, value types, and members and
assigns the rids, links the member lists, encodes the signatures, and resolves
the references — so a scenario reads in a dozen declarative lines instead of a
hundred of hand-encoded bytes. Several collision fixtures are stated through it.
Six review findings across the identity/visibility seam are resolved here. A
by-value type nested beneath a generic encloser has no valid unqualified
spelling, so rather than render it as an opaque pointer — which would silently
change a by-value struct's ABI layout — the whole containing declaration is
dropped (a by-reference type stays the harmless opaque pointer). The inheritance
edge is resolved by identity, not by name: the `bases` view carries the
InterfaceImpl provenance and the render query resolves the local Id, so an
external base sharing a `TypeName` with a local nested interface spells bare
rather than adopting the local one. A nested base reserves the root of its
qualified spelling (`Outer` of `Outer.IChild`) as its frontier label. The
fabricated-namespace shadow check is rewritten to mirror Swift's own name
lookup — a bare frontier reference resolves outward through its owner's
enclosing scopes to the top level, so a top-level protocol, the fabricated
segment above it, or a sibling container visible through a shared ancestor all
capture it and fault — one rule subsuming the top-level, inner, and sibling
cases. Emittedness is read from whether the selected kind section actually
declared the type (its body differs from the template's type-free skeleton), so
a template carrying only shared boilerplate outside its sections declares
nothing and its type stays an unemitted frontier.1 parent b6b7638 commit 4ca5e73
13 files changed
Lines changed: 3453 additions & 209 deletions
File tree
- Sources
- SQLEngineWinMD
- Resources/Queries
- WinMDSynthesis
- WinMD
- winmd-inspect
- Resources/Render
- Tests/winmd-inspectTests
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
56 | 56 | | |
57 | 57 | | |
58 | 58 | | |
59 | | - | |
60 | | - | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
61 | 63 | | |
62 | 64 | | |
63 | 65 | | |
| |||
1058 | 1060 | | |
1059 | 1061 | | |
1060 | 1062 | | |
1061 | | - | |
| 1063 | + | |
| 1064 | + | |
1062 | 1065 | | |
1063 | 1066 | | |
1064 | 1067 | | |
1065 | 1068 | | |
1066 | 1069 | | |
1067 | | - | |
| 1070 | + | |
| 1071 | + | |
1068 | 1072 | | |
1069 | 1073 | | |
1070 | 1074 | | |
| |||
1087 | 1091 | | |
1088 | 1092 | | |
1089 | 1093 | | |
1090 | | - | |
| 1094 | + | |
| 1095 | + | |
1091 | 1096 | | |
1092 | 1097 | | |
1093 | 1098 | | |
| |||
1107 | 1112 | | |
1108 | 1113 | | |
1109 | 1114 | | |
1110 | | - | |
| 1115 | + | |
| 1116 | + | |
1111 | 1117 | | |
1112 | 1118 | | |
1113 | 1119 | | |
| |||
1126 | 1132 | | |
1127 | 1133 | | |
1128 | 1134 | | |
1129 | | - | |
| 1135 | + | |
| 1136 | + | |
1130 | 1137 | | |
1131 | 1138 | | |
1132 | 1139 | | |
1133 | 1140 | | |
1134 | 1141 | | |
1135 | | - | |
| 1142 | + | |
| 1143 | + | |
1136 | 1144 | | |
1137 | 1145 | | |
1138 | 1146 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
2 | 9 | | |
3 | 10 | | |
| 11 | + | |
| 12 | + | |
4 | 13 | | |
5 | 14 | | |
6 | 15 | | |
| |||
10 | 19 | | |
11 | 20 | | |
12 | 21 | | |
| 22 | + | |
| 23 | + | |
13 | 24 | | |
14 | 25 | | |
15 | 26 | | |
| |||
18 | 29 | | |
19 | 30 | | |
20 | 31 | | |
21 | | - | |
22 | | - | |
23 | | - | |
24 | | - | |
25 | | - | |
26 | | - | |
27 | | - | |
28 | | - | |
29 | | - | |
30 | | - | |
31 | | - | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
32 | 37 | | |
33 | 38 | | |
| 39 | + | |
| 40 | + | |
34 | 41 | | |
35 | 42 | | |
36 | 43 | | |
| |||
41 | 48 | | |
42 | 49 | | |
43 | 50 | | |
| 51 | + | |
| 52 | + | |
44 | 53 | | |
45 | 54 | | |
46 | 55 | | |
| |||
0 commit comments