Skip to content

Commit 7e34363

Browse files
committed
winmd-inspect: nest metadata-nested value types into Swift nesting
Fold the flat closure emission into real Swift nesting, so a metadata-nested value type emits as a nested type under its container chain and every signature that names it resolves to that declaration. Disambiguating the names two reached declarations would otherwise share is the next slice. A metadata-nested value type emits as a real nested Swift type under its enclosing-`TypeDef` container chain (`Outer.Inner`), and every signature that names it spells the same dot-path, so the declaration and its uses agree and two same-named nested types under different enclosers stay distinct. The walk emits the enclosing value type too — even when only `Outer.Inner` is named and nothing references `Outer` directly — so the container `Inner` nests inside actually exists; a value type nests only when every enclosing level is an emitted value-type container (`struct`/`enum`), else it is a dropped frontier the consumer defines (an enclosing `protocol` cannot nest a value type, an excluded runtime `class` is not emitted). An interface or delegate is a Swift `protocol`, which cannot nest, so a metadata-nested one is a dropped frontier and only a top-level one is a legal root. The flat emission folds into a containment forest whose roots render in the walk's post-order, so a dependency precedes the type naming it and a top-level type keeps its position. A custom struct/enum template may open with a header comment and append a brace-delimited footer around its main declaration; the child block is spliced before the *main declaration's* closing brace, located off the syntax tree (`Surface.inject`) — the primary `struct`/`enum` named for the container, its member block's closing brace — so a `{` inside a comment or a string literal, a leading helper before the declaration, and a trailing `extension` after it are excluded by the grammar rather than a hand-tracked brace count. The child nests inside the container, not above the declaration or into the footer. A nested type's enclosing dot-path strips the CLR arity suffix from every component, not only the leaf, and the strip lives at the single escape seam, before each component is keyword-escaped, so a storage spelling and a directly-resolved one escape identically and a generic instantiation's base (`` `protocol`<…> ``) composes without a second strip that would cut the escape backtick. But a *generic* encloser has no valid unqualified spelling — a member of `Outer`1` is `Outer<T>.Inner`, needing the enclosing specialization the projection does not yet emit, which the arity-stripped `Outer.Inner` misbinds if the consumer supplies the generic `Outer` — so a reference into a type nested under a generic encloser is dropped as an unsupported frontier rather than spelled uncompilable, resolved (like a `TypeSpec` or a null index) to nothing. An interface whose base is a metadata-nested interface names that base through the same enclosing dot-path (`Outer.IChild`) its own nested declaration and a signature naming it both wear, so the refinement resolves rather than reading the bare `TypeName`, which binds no visible declaration. The selected base is resolved to its local definition through the same `requires` scope-chain walk the closure uses, so a base named directly through an `Interface_TypeDef` and one named through an `Interface_TypeRef` — whose nested references the walk follows to the local nested definition — alike carry the enclosing path; the `.render *` batch resolves a `TypeRef` base by the same recursion. Each path component is keyword-escaped separately. A top-level or external base resolves to no enclosing path and spells bare, as does a base a `-I` override names that matches no local definition. Only a type's *primary* declaration nests. A custom template may frame a value type with a file-scope `import` header or an `extension` footer, which Swift permits only at file scope, so `nest` renders each node as a (header, declaration, footer) triple: `partition` splits the header and footer off the rendered body, only the declaration folds into the container, and the header and footer bubble up — through every enclosing container — to the roots. A metadata- nested child's `import`/`extension` therefore stays at file scope rather than being indented into its parent's body, which would be invalid Swift. A well-known spelling is keyed on a type's *own* raw identity, not the enclosing dot-path the resolver qualifies a nested type to. A nested `Outer.Inner` mapped `wellknown Inner InnerBridge` resolves on `("", "Inner")`, so the decode's well-known lookup keys on the identity's leaf component — the raw name the walk suppresses the declaration on — so the return spells the bridge `InnerBridge` rather than the `Outer.Inner` the qualified identity would miss, leaving an unresolved type whose declaration the walk already frontiered. Integration tests cover two same-named nested value types each nested under its encloser, an enclosing value type reached only through a nested member, a child nested inside its container despite a header comment's braces, a trailing footer, and a multiline string literal's braces, a reference into a type nested under a generic encloser dropped as an unsupported frontier, a generic instantiation whose already-stripped keyword base keeps its escape, and a top-level interface whose base is a nested interface — named through an `Interface_TypeDef`, and (resolved by the scope-chain walk) through an `Interface_TypeRef` — spelled through its enclosing path. A `known` bridge resolves on the reference's *raw* identity, which the resolver now preserves for exactly this lookup. `Storage.identifier` carries the leaf's CLR arity suffix (`` Box`1 ``) while arity-stripping the enclosing path, so the decode keys the well-known lookup on the raw leaf (backtick removed, `Box1`) and a generic type spells its configured bridge (`BoxBridge<…>`) rather than the projected `Box<…>` the shared `qualified` still produces for the base-label path. And a nested reference under a `known`-bridged *encloser* spells through the bridge — `wellknown Outer OuterBridge` makes `Outer.Inner` spell `OuterBridge.Inner` — so a signature into a type whose encloser the closure suppressed as imported resolves against the consumer's type, not an unemitted `Outer`. Only a nesting render qualifies a nested inheritance base. The closure emits a nested interface's enclosing container, so `IRoot`'s clause names its base through the enclosing path (`Outer.IChild`) that resolves against it. A flat render (`.render`, `.render *`) emits every interface at the top level and nests nothing, so the same base must spell its bare leaf (`IChild`) — the top-level declaration the flat render emits it as — rather than an enclosing path with no container to resolve against. A `Mode` flag the two render entry points set lets `refine` tell the paths apart, so the flat inheritance stays consistent with the flat declarations it names. The closure's own frontier check normalizes a generic's name like the decode does. The walk frontiers a reached dependency whose identity a `known` bridge names; it now drops the arity backtick from the raw `TypeName` (`` Box`1 `` → `Box1`) before the lookup, matching the decode's normalization, so a bridged generic is suppressed here rather than emitted as a local `Box<T>` whose declaration would redeclare the imported `BoxBridge<T>` the decode spells.
1 parent 9b38385 commit 7e34363

8 files changed

Lines changed: 3403 additions & 207 deletions

File tree

Sources/WinMD/Storage.swift

Lines changed: 329 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,4 +224,333 @@ public struct Storage: ~Escapable {
224224
}
225225
return nil
226226
}
227+
228+
// MARK: - Nesting
229+
230+
/// The lexically enclosing type of the type `tuple` names — its immediate
231+
/// encloser — or `nil` when the type is top-level.
232+
///
233+
/// A `TypeRef` nests through its `ResolutionScope`: a scope that is itself a
234+
/// `TypeRef` is the enclosing reference, and any other scope (a `Module`, a
235+
/// `ModuleRef`, or an `AssemblyRef`) makes the reference top-level. A
236+
/// `TypeDef` nests through the `NestedClass` table (§II.22.32) — the
237+
/// `EnclosingClass` of the row whose `NestedClass` names this definition,
238+
/// found by binary search when the table is stored sorted on its key and by a
239+
/// linear scan otherwise. Any other table has no enclosing and yields `nil`.
240+
/// It is `package` so the synthesis decode and the render adapter compose one
241+
/// nested type's dot-path from the same walk.
242+
@_lifetime(copy self)
243+
package func enclosing(_ tuple: borrowing Tuple)
244+
throws(WinMDError) -> Tuple? {
245+
// The enclosing tuple is opened off `self` (not off the borrowed `tuple`)
246+
// so its lifetime tracks the storage rather than the shorter-lived
247+
// argument, which is what a recursive walk returns through.
248+
switch tuple.table.number {
249+
case Metadata.Tables.TypeRef.number:
250+
guard let scope = tuple.ordinal(for: "ResolutionScope") else {
251+
return nil
252+
}
253+
// Tag 3 of `ResolutionScope` selects `TypeRef`: a scope that is itself a
254+
// `TypeRef` is the enclosing reference; a `Module`/`ModuleRef`/
255+
// `AssemblyRef` scope makes the reference top-level.
256+
let coded = ResolutionScope(rawValue: tuple[scope])
257+
guard coded.tag == 3, coded.row != 0 else { return nil }
258+
return try self.tuple(coded.row - 1,
259+
of: Metadata.Tables.TypeRef.self)
260+
case Metadata.Tables.TypeDef.number:
261+
guard let table = opened(Metadata.Tables.NestedClass.number) else {
262+
return nil
263+
}
264+
// The `NestedClass` (ordinal 0) column holds the 1-based `TypeDef` Id of
265+
// the nested type; `EnclosingClass` (ordinal 1) its encloser.
266+
let child = tuple.row + 1
267+
let count = Int(table.rows)
268+
if sorted & (1 << Metadata.Tables.NestedClass.number) != 0 {
269+
let lower = bound(table, 0, child, count, strict: false)
270+
guard lower < count, Tuple(lower, table, self)[0] == child else {
271+
return nil
272+
}
273+
return try self.tuple(Tuple(lower, table, self)[1] - 1,
274+
of: Metadata.Tables.TypeDef.self)
275+
}
276+
for index in 0 ..< count where Tuple(index, table, self)[0] == child {
277+
return try self.tuple(Tuple(index, table, self)[1] - 1,
278+
of: Metadata.Tables.TypeDef.self)
279+
}
280+
return nil
281+
default:
282+
return nil
283+
}
284+
}
285+
286+
/// The dot-path name of the type `tuple` names, from its outermost encloser —
287+
/// `Foo.Bar` for a `Bar` nested under `Foo`, `Foo.Bar.Baz` for a deeper
288+
/// nesting — or the bare `TypeName` for a top-level type.
289+
///
290+
/// The components are joined raw (unescaped); a caller that spells the path
291+
/// as target source escapes each component separately, so a keyword component
292+
/// is delimited within the path rather than treated as one identifier.
293+
package func qualified(_ tuple: borrowing Tuple)
294+
throws(WinMDError) -> String {
295+
// Each component is a declaration name spelled arity-stripped: a generic
296+
// encloser `Outer``1` nests as `Outer` and the generic decode strips the
297+
// leaf, so strip every component of the enclosing path here — a signature
298+
// spelling `Outer.Inner`, not the unresolved `Outer``1.Inner`. The suffix
299+
// marks only a generic definition, which is never a `known` bridge or
300+
// `System.Guid` (both non-generic), so the identity this feeds still
301+
// matches those lookups, and a top-level component is already projected at
302+
// the seam that qualifies it.
303+
let name = try projected(bare(tuple))
304+
guard let outer = try enclosing(tuple) else { return name }
305+
return try qualified(outer) + "." + name
306+
}
307+
308+
/// As `qualified`, but the *leaf* component keeps its CLR generic-arity
309+
/// suffix (`` Box`1 ``) while the enclosing path stays arity-stripped. The
310+
/// decode's well-known lookup keys on this raw leaf — the spec maps a
311+
/// generic type by its raw name (`Box1`) — while the projected spelling still
312+
/// strips the suffix, so the two agree on a non-generic type and diverge only
313+
/// where a generic's arity is the difference between hitting and missing the
314+
/// configured bridge.
315+
package func identifier(_ tuple: borrowing Tuple)
316+
throws(WinMDError) -> String {
317+
let leaf = try bare(tuple)
318+
guard let outer = try enclosing(tuple) else { return leaf }
319+
return try qualified(outer) + "." + leaf
320+
}
321+
322+
/// Whether any *enclosing* type of `tuple` is generic — its raw `TypeName`
323+
/// carries the CLR arity suffix (a backtick). Such a nesting has no valid
324+
/// unqualified spelling: a member of `Outer``1` is `Outer<T>.Inner`, which
325+
/// needs the enclosing specialization, while both `qualified`'s arity-stripped
326+
/// `Outer.Inner` and appending the generic arguments to the leaf misname it.
327+
/// Projecting the WinRT generic-nesting specialization is a deferred redesign,
328+
/// so a reference into such a type is an unsupported frontier the caller drops
329+
/// rather than spelling. Only an *encloser*'s arity matters: the leaf's own
330+
/// generic arity is stripped and supplied by the decode's own clause.
331+
package func enclosedByGeneric(_ tuple: borrowing Tuple)
332+
throws(WinMDError) -> Bool {
333+
guard let outer = try enclosing(tuple) else { return false }
334+
if try bare(outer).contains("`") { return true }
335+
return try enclosedByGeneric(outer)
336+
}
337+
338+
/// The dot-path name of the `TypeDef` at 1-based `id` — the `qualified`
339+
/// spelling addressed by `Id` rather than by an already-fetched `Tuple`, for
340+
/// a caller (the render's inheritance clause) that holds only the resolved
341+
/// base `Id`. A missing row spells empty, the same absent-name the caller's
342+
/// bare fallback would.
343+
package func qualified(of id: Int) throws(WinMDError) -> String {
344+
guard let tuple = try self.tuple(id - 1, of: Metadata.Tables.TypeDef.self)
345+
else {
346+
return ""
347+
}
348+
return try qualified(tuple)
349+
}
350+
351+
/// The enclosing `TypeDef` chain of the `TypeDef` at 1-based `id`, outermost
352+
/// first — each an `(id, name)` pair — for the render to group a nested type
353+
/// under a container per level. An empty array for a top-level type.
354+
package func nesting(of id: Int)
355+
throws(WinMDError) -> Array<(id: Int, name: String)> {
356+
guard let tuple = try self.tuple(id - 1, of: Metadata.Tables.TypeDef.self),
357+
let outer = try enclosing(tuple) else {
358+
return []
359+
}
360+
return try nesting(of: outer.row + 1) + [(outer.row + 1, bare(outer))]
361+
}
362+
363+
// MARK: - Kind and qualification
364+
365+
/// The projection kind of a named type — how the render spells and nests it.
366+
///
367+
/// The partition is the one the SQL `types` view draws: an `interface` (the
368+
/// `tdInterface` flag), a `delegate`/`structure`/`enumeration` (told apart by
369+
/// the base its `Extends` names — `System.MulticastDelegate`/`System.ValueType`
370+
/// /`System.Enum`), or a runtime `class` (anything else). Only a `structure`
371+
/// or `enumeration` is a value type, spelled fully namespace-qualified and
372+
/// nested; the rest spell by their bare name.
373+
package enum Kind: Sendable, Equatable {
374+
case interface
375+
case delegate
376+
case structure
377+
case enumeration
378+
case `class`
379+
380+
/// Whether the kind is a value type — a `structure` or an `enumeration` —
381+
/// the render namespace-qualifies and nests, as opposed to a `protocol`
382+
/// (`interface`/`delegate`) or a runtime `class` it spells bare.
383+
package var value: Bool {
384+
self == .structure || self == .enumeration
385+
}
386+
}
387+
388+
/// The projection kind of the `TypeDef` the `tuple` names, classified exactly
389+
/// as the SQL `types` view does so the decode spelling and the emit nesting
390+
/// agree from one source.
391+
///
392+
/// The `tdInterface` flag (`0x20`) marks an interface regardless of its base;
393+
/// otherwise the base type the `Extends` coded index names classifies the row
394+
/// — `System.Enum` an enumeration, `System.MulticastDelegate` a delegate,
395+
/// `System.ValueType` a structure, anything else (or no base) a runtime class.
396+
package func kind(_ tuple: borrowing Tuple) throws(WinMDError) -> Kind {
397+
if let flags = tuple.ordinal(for: "Flags"), tuple[flags] & 0x20 == 0x20 {
398+
return .interface
399+
}
400+
guard let extends = tuple.ordinal(for: "Extends") else { return .class }
401+
let base = TypeDefOrRef(rawValue: tuple[extends])
402+
guard let parent = try resolve(base) else { return .class }
403+
switch try names(parent) {
404+
case ("System", "Enum"): return .enumeration
405+
case ("System", "MulticastDelegate"): return .delegate
406+
case ("System", "ValueType"): return .structure
407+
default: return .class
408+
}
409+
}
410+
411+
/// The outermost encloser of the type `tuple` names — the top of its nesting
412+
/// chain, itself when top-level — reached by climbing `enclosing`. The result
413+
/// is opened off `self`, so its lifetime tracks the storage.
414+
@_lifetime(copy self)
415+
private func outermost(_ tuple: borrowing Tuple)
416+
throws(WinMDError) -> Tuple {
417+
guard let up = try enclosing(tuple) else {
418+
return Tuple(tuple.row, tuple.table, self)
419+
}
420+
return try outermost(up)
421+
}
422+
423+
/// The local `TypeDef` the named type `reference` resolves to, or `nil` when
424+
/// it names no local definition.
425+
///
426+
/// A `TypeDef` reference already names a local definition. A `TypeRef` resolves
427+
/// through its `ResolutionScope` chain — a module-scoped reference to the
428+
/// non-nested `TypeDef` of the same (namespace, name), a `TypeRef`-scoped
429+
/// (nested) reference to the nested `TypeDef` under the local definition its
430+
/// enclosing reference resolves to — exactly the walk the render's `references`
431+
/// CTE performs. A reference whose chain terminates at a `ModuleRef` or
432+
/// `AssemblyRef` (an external assembly) resolves to nothing; a `TypeSpec`
433+
/// names no definition.
434+
@_lifetime(copy self)
435+
package func definition(of reference: TypeDefOrRef)
436+
throws(WinMDError) -> Tuple? {
437+
guard let tuple = try resolve(reference) else { return nil }
438+
switch tuple.table.number {
439+
case Metadata.Tables.TypeDef.number:
440+
return tuple
441+
case Metadata.Tables.TypeRef.number:
442+
return try definition(reference: tuple)
443+
default:
444+
return nil
445+
}
446+
}
447+
448+
/// The local `TypeDef` the `TypeRef` `tuple` resolves to through its
449+
/// `ResolutionScope` chain, or `nil` when the reference is external.
450+
@_lifetime(copy self)
451+
private func definition(reference tuple: borrowing Tuple)
452+
throws(WinMDError) -> Tuple? {
453+
guard let ordinal = tuple.ordinal(for: "ResolutionScope") else {
454+
return nil
455+
}
456+
let scope = ResolutionScope(rawValue: tuple[ordinal])
457+
let target = try names(tuple)
458+
// A `TypeRef`-scoped (tag 3) reference is nested: resolve its enclosing
459+
// reference to a local `TypeDef`, then match the nested `TypeDef` directly
460+
// under it by `TypeName` — a nested type's namespace is empty, so the match
461+
// is by name under the encloser, never by namespace.
462+
if scope.tag == 3, scope.row != 0 {
463+
guard let enclosing = try self.tuple(scope.row - 1,
464+
of: Metadata.Tables.TypeRef.self),
465+
let encloser = try definition(reference: enclosing) else {
466+
return nil
467+
}
468+
return try nested(target.name, in: encloser.row + 1)
469+
}
470+
// A `Module`-scoped (tag 0) reference is local and top-level; any other
471+
// scope — a `ModuleRef`/`AssemblyRef`, or a null scope — is external.
472+
guard scope.tag == 0, scope.row != 0 else { return nil }
473+
return try toplevel(target.namespace, target.name)
474+
}
475+
476+
/// The non-nested local `TypeDef` named (`namespace`, `name`), or `nil` — the
477+
/// anchor a module-scoped reference resolves to.
478+
@_lifetime(copy self)
479+
private func toplevel(_ namespace: String, _ name: String)
480+
throws(WinMDError) -> Tuple? {
481+
guard let table = opened(Metadata.Tables.TypeDef.number) else { return nil }
482+
for row in 0 ..< Int(table.rows) {
483+
let tuple = Tuple(row, table, self)
484+
let (space, simple) = try names(tuple)
485+
guard space == namespace, simple == name else { continue }
486+
// A nested type shares a bare (namespace, name) with a top-level one only
487+
// by coincidence, and the empty namespace collapses every nested type's
488+
// pair — so the module-scoped reference names the non-nested definition.
489+
switch try enclosing(tuple) {
490+
case .none: return tuple
491+
case .some: continue
492+
}
493+
}
494+
return nil
495+
}
496+
497+
/// The local nested `TypeDef` named `name` directly under the `TypeDef` at
498+
/// 1-based `encloser` `Id`, or `nil` — the nested reference's resolution step.
499+
@_lifetime(copy self)
500+
private func nested(_ name: String, in encloser: Int)
501+
throws(WinMDError) -> Tuple? {
502+
guard let table = opened(Metadata.Tables.NestedClass.number) else {
503+
return nil
504+
}
505+
// The `NestedClass` column (ordinal 0) is the nested `TypeDef` Id, the
506+
// `EnclosingClass` column (ordinal 1) its encloser.
507+
for index in 0 ..< Int(table.rows) {
508+
let link = Tuple(index, table, self)
509+
guard link[1] == encloser else { continue }
510+
guard let child = try self.tuple(link[0] - 1,
511+
of: Metadata.Tables.TypeDef.self) else {
512+
continue
513+
}
514+
if try bare(child) == name { return child }
515+
}
516+
return nil
517+
}
518+
519+
/// The bare `TypeName` of the type `tuple` names — the empty string when it
520+
/// carries no such column.
521+
private func bare(_ tuple: borrowing Tuple) throws(WinMDError) -> String {
522+
guard let name = tuple.ordinal(for: "TypeName") else { return "" }
523+
return try tuple.string(name)
524+
}
525+
526+
/// A `TypeName` with its CLR generic-arity suffix removed — the projected
527+
/// declaration name the render actually emits and the decode spells. The
528+
/// suffix is a backtick and an arity count (`Foo` backtick `1`), which the
529+
/// projection strips, so a generic `Foo` and a non-generic `Foo` project to
530+
/// the one Swift name and collide. The collision tally and the qualification
531+
/// identity key off this projected name so they match the emission; a name
532+
/// without the suffix is returned unchanged.
533+
private func projected(_ name: String) -> String {
534+
String(name.prefix { $0 != "`" })
535+
}
536+
537+
/// The (namespace, name) the `TypeDef`/`TypeRef` `tuple` names — the empty
538+
/// string for either column it lacks.
539+
private func names(_ tuple: borrowing Tuple)
540+
throws(WinMDError) -> (namespace: String, name: String) {
541+
let name = try bare(tuple)
542+
guard let space = tuple.ordinal(for: "TypeNamespace") else {
543+
return ("", name)
544+
}
545+
return (try tuple.string(space), name)
546+
}
547+
548+
/// The open table numbered `number`, or `nil` when the database omits it —
549+
/// the population-count slot lookup `rows(of:)`/`tuple(_:of:)` share, reduced
550+
/// to the `Table` for a direct row read.
551+
private func opened(_ number: Int) -> Table? {
552+
guard valid & (1 << number) != 0 else { return nil }
553+
let slot = (valid & ((1 << number) - 1)).nonzeroBitCount
554+
return tables[slot]
555+
}
227556
}

0 commit comments

Comments
 (0)