Skip to content

winmd-inspect: render WinRT generic interfaces ABI-erased - #219

Open
compnerd wants to merge 1 commit into
mainfrom
compnerd/erasure-render
Open

winmd-inspect: render WinRT generic interfaces ABI-erased#219
compnerd wants to merge 1 commit into
mainfrom
compnerd/erasure-render

Conversation

@compnerd

@compnerd compnerd commented Jul 7, 2026

Copy link
Copy Markdown
Owner

The generic-interface rendering previously projected a generic ABI protocol (protocol Name<T…> with associatedtypes) plus a wrapper forwarding own methods — the shape-1 foundation, which the non-generic-closed-base wall made a dead end for base inheritance. Pivot the rendering onto the ABI-erased shape windows-rs draws, reusing the merged SignatureType.abi(…)/classification keystone:

  • The ABI protocol is now NON-generic. Each method requirement reads every parameter/return through its ABI-erased spelling: a reference-typed slot (an interface, runtime class, delegate, generic-interface instantiation, or System.Object) is the opaque interface pointer, a value-typed slot keeps its own ABI. No generic parameters, no associated types.

  • The public generic struct wrapper Name<T…> keeps the typed Swift surface: each method takes/returns the decoded typed parameters and casts the typed value to the erased pointer (and the erased result back) as it forwards through baseunsafeBitCast, the text analogue of windows-rs's transmute_copy across the vtable. A value slot needs no cast (its typed and erased spellings coincide), so it forwards unchanged.

  • Base inheritance becomes trivial and is INCLUDED: a non-generic ABI protocol inherits its base's non-generic ABI protocol by plain protocol inheritance — a generic base inherits <stripped>ABI (no arguments, no where-constraints), a non-generic base (IInspectable) is inherited unchanged. The non-generic-closed-base wall cannot occur.

The Database+SQL decode layer gains erased-ABI (abi(return:)/ abi(parameter:)) and classification (reference(return:)/ reference(parameter:)) accessors beside the existing typed decode(…), all sharing the extracted signature-navigation. Shell composes the per-slot erased/typed spellings and the forwarding cast into the render context; the com.mustache {{#generic}} arm emits the erased protocol + casting wrapper. The non-generic {{^generic}} path is byte-identical.

The generated Swift is swiftc-typecheck-verified for a simple generic interface (erased/cast element return), a generic interface with a generic base (plain non-generic ABI inheritance, wrapper casts), an out/interface-array method (erased element under the pointer), and a keyword-named generic parameter.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2e4fca9b51

ℹ️ 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".

Comment thread Sources/winmd-inspect/Resources/Templates/com.mustache Outdated
Comment thread Sources/winmd-inspect/Shell.swift
@codecov-commenter

codecov-commenter commented Jul 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.31081% with 10 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (main@204c219). Learn more about missing BASE report.

Files with missing lines Patch % Lines
Sources/winmd-inspect/Shell.swift 95.33% 7 Missing ⚠️
Sources/winmd-inspect/Database+SQL.swift 95.74% 2 Missing ⚠️
Tests/WinMDTests/SignatureTests.swift 93.75% 1 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main     #219   +/-   ##
=======================================
  Coverage        ?   92.72%           
=======================================
  Files           ?      156           
  Lines           ?    31666           
  Branches        ?        0           
=======================================
  Hits            ?    29362           
  Misses          ?     2304           
  Partials        ?        0           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@compnerd
compnerd force-pushed the compnerd/erasure-render branch from 2e4fca9 to 0dbb2c2 Compare July 8, 2026 03:45

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0dbb2c2178

ℹ️ 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".

Comment thread Sources/winmd-inspect/Shell.swift
@compnerd
compnerd force-pushed the compnerd/erasure-render branch from 0dbb2c2 to 71fe481 Compare July 8, 2026 05:52

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 71fe4819f0

ℹ️ 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".

Comment thread Sources/WinMDSynthesis/Decode.swift Outdated
Comment thread Sources/winmd-inspect/Resources/Queries/bases.sql
@compnerd
compnerd force-pushed the compnerd/erasure-render branch 2 times, most recently from 5e273a2 to 83ba1f3 Compare July 8, 2026 21:55

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 83ba1f325c

ℹ️ 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".

Comment on lines +792 to +793
let argument = if projects[index] {
"\(local).toABI()"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Convert projected pointer parameters explicitly

When a projected generic slot appears under indirection (for example WinRT collection methods with out T/T[] parameters), projects(parameter:) is true and the ABI type is UnsafeMutablePointer<Element.ABI>, while the wrapper local is UnsafeMutablePointer<Element>. This branch emits local.toABI(), but only Element: ABIProjectable has toABI(), not UnsafeMutablePointer<Element>, so common generic interface methods render source that cannot type-check and still need a pointer/element projection path.

Useful? React with 👍 / 👎.

Comment on lines +568 to +570
generics != nil && Shell.arity(name)
? language.escape(String(name.prefix { $0 != "`" }) + "ABI")
+ arguments

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve the TypeSpec base arguments

For a TypeSpec-recorded generic base whose arguments are not exactly the enclosing parameters, such as WinRT IMap<K,V> inheriting IIterable<IKeyValuePair<K,V>>, this rewrite ignores the TypeSpec argument list and always appends the current interface's generics (<K, V>) to the base name. The generated inheritance is therefore the wrong specialization/arity for these interfaces; the TypeSpec arm needs to carry and decode the actual base arguments instead of synthesizing them here.

Useful? React with 👍 / 👎.

… ABI type

The generic-interface rendering projected each type-variable slot onto a
fixed-size raw pointer: the ABI protocol was NON-generic, a `VAR` slot erased
to the opaque interface pointer, and the wrapper `unsafeBitCast` the typed
value to (and the result back from) that pointer. That is size-correct only for
a REFERENCE instantiation (`IVector<IFoo>`, a pointer either way); for a VALUE
instantiation (`IReference<Int32>`, `IVector<Int32>`) the slot's `Element` is a
4-byte value, and a fixed-size bitcast to an 8-byte pointer TRAPS — reads
garbage past the value. Replace the raw-pointer erasure of a generic slot with
the windows-rs `Type::Abi` mechanism: an associated-ABI-type projection that is
size-correct for both kinds.

  - A new bundled runtime-support source, `Resources/Support/ABIProjectable.swift`,
    defines the projection protocol: `ABIProjectable` with an
    `associatedtype ABI` and `toABI()`/`fromABI(_:)` conversions — the Swift
    analogue of windows-rs's `Type` trait. A value type conforms with
    `ABI == Self` (identity, the `CopyType`/`CloneType` case) and every
    WinRT-blittable primitive is made to conform; a reference type conforms via
    `ABIReference` with `ABI == UnsafeMutableRawPointer` (the opaque COM
    pointer, the `InterfaceType` case). It is emitted alongside the projected
    interfaces, not compiled into the tool.

  - The generic wrapper is now `struct IVector<Element: ABIProjectable>` and the
    ABI protocol is generic over the same parameters — `protocol
    IVectorABI<Element>` with an `associatedtype Element: ABIProjectable` — its
    requirements spelling each projected slot as `Element.ABI`. The wrapper
    forwards through `Element.toABI()` / `Element.fromABI(_:)` rather than a
    fixed-size `unsafeBitCast`, so a value instantiation crosses the vtable AS
    the value (`Element.ABI == Element`) and a reference instantiation as the
    opaque pointer — size-correct for both, exactly as windows-rs writes a
    generic vtable slot as `AbiType<T>` rather than a fixed `*mut c_void`.

  - `SignatureType.abi(…)` projects an UNBOUND type-level `VAR` through its
    declared name's `.ABI` member (the new `projection` dialect string) instead
    of collapsing it to the opaque pointer; a new `SignatureType.projects`
    reports such a slot. A CONCRETE reference (a `CLASS` named type) and a
    method-level `MVAR` still erase to the opaque pointer — a pointer either
    way, so their cast is size-safe. The argument-dependent specialisation
    (`substituting:`) is unchanged: a bound `VAR` resolves to its concrete
    argument.

  - `Database+SQL` gains `projects(return:)`/`projects(parameter:)` beside the
    existing `reference(…)`; `Shell` composes the projected slot's ABI spelling
    (`Element.ABI`) and the `toABI()`/`fromABI(_:)` forwarding into the render
    context; the `com.mustache` `{{#generic}}` arm emits the parameterised ABI
    protocol (with its `associatedtype` declarations) and the projecting
    wrapper. A generic base is inherited parameterised by the interface's own
    arguments (`IVectorABI<Element>: IIterableABI<Element>`). The non-generic
    `{{^generic}}` path is byte-identical.

The projected shape is swiftc-typecheck- and run-verified end to end for BOTH a
VALUE instantiation (`IVector<Int32>`: the ABI slot IS `CInt`, the wrapper does
NO pointer bitcast, size-correct) and a REFERENCE instantiation
(`IVector<IFoo>`: the slot is the opaque pointer, projected through
`ABIReference`). The original PR219 concern-1 bug is fixed: no value slot
carries a size-unsafe `unsafeBitCast`.
@compnerd
compnerd force-pushed the compnerd/erasure-render branch from 83ba1f3 to 353ac35 Compare July 13, 2026 23:38

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 353ac35d6b

ℹ️ 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".

guard let typed else { return call }
if projects { return "\(typed).fromABI(\(call))" }
guard reference else { return call }
return "unsafeBitCast(\(call), to: \(typed).self)"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Wrap concrete reference results instead of bitcasting

When a generic interface method returns another interface wrapper, for example IIterable<T>.First -> IIterator<T>, this emits unsafeBitCast(base.First(), to: IIterator<Element>.self). The generated IIterator<Element> is a struct containing base: any IIteratorABI<Element>, not a raw pointer-sized value, so executing the method traps with a size-mismatched bitcast instead of wrapping the ABI pointer.

Useful? React with 👍 / 👎.

// (`toABI()`/`fromABI(_:)`) as it forwards through `base` to the ABI protocol
// — the size-correct analogue of windows-rs's `transmute_copy` across the
// vtable. A value slot projects by identity, so it forwards unchanged.
public struct {{{name}}}<{{#generics}}{{{name}}}: ABIProjectable{{^last}}, {{/last}}{{/generics}}> {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Make generated reference types valid generic arguments

When a valid WinRT generic is instantiated with a generated interface type, e.g. IVector<IStorageFile>, this constraint requires the element to conform to ABIProjectable. The same template still emits non-generic interfaces as plain @com protocols and generic wrappers without ABIProjectable/ABIReference conformance, so generated reference types cannot be used as type arguments and those collection projections fail to type-check.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants