Commit 141c8cb
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; 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.1 parent 6cbd5ba commit 141c8cb
12 files changed
Lines changed: 3642 additions & 260 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 | | |
| |||
1160 | 1168 | | |
1161 | 1169 | | |
1162 | 1170 | | |
| 1171 | + | |
| 1172 | + | |
| 1173 | + | |
| 1174 | + | |
| 1175 | + | |
| 1176 | + | |
| 1177 | + | |
| 1178 | + | |
| 1179 | + | |
| 1180 | + | |
| 1181 | + | |
| 1182 | + | |
| 1183 | + | |
| 1184 | + | |
| 1185 | + | |
| 1186 | + | |
| 1187 | + | |
| 1188 | + | |
| 1189 | + | |
| 1190 | + | |
| 1191 | + | |
| 1192 | + | |
| 1193 | + | |
| 1194 | + | |
| 1195 | + | |
| 1196 | + | |
| 1197 | + | |
| 1198 | + | |
| 1199 | + | |
| 1200 | + | |
| 1201 | + | |
| 1202 | + | |
| 1203 | + | |
| 1204 | + | |
1163 | 1205 | | |
1164 | 1206 | | |
1165 | 1207 | | |
| |||
| 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