winmd-inspect: render WinRT generic interfaces ABI-erased - #219
Conversation
There was a problem hiding this comment.
💡 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".
Codecov Report❌ Patch coverage is 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. 🚀 New features to boost your workflow:
|
2e4fca9 to
0dbb2c2
Compare
There was a problem hiding this comment.
💡 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".
0dbb2c2 to
71fe481
Compare
There was a problem hiding this comment.
💡 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".
5e273a2 to
83ba1f3
Compare
There was a problem hiding this comment.
💡 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".
| let argument = if projects[index] { | ||
| "\(local).toABI()" |
There was a problem hiding this comment.
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 👍 / 👎.
| generics != nil && Shell.arity(name) | ||
| ? language.escape(String(name.prefix { $0 != "`" }) + "ABI") | ||
| + arguments |
There was a problem hiding this comment.
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`.
83ba1f3 to
353ac35
Compare
There was a problem hiding this comment.
💡 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)" |
There was a problem hiding this comment.
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}}> { |
There was a problem hiding this comment.
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 👍 / 👎.
The generic-interface rendering previously projected a generic ABI protocol (
protocol Name<T…>withassociatedtypes) 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 mergedSignatureType.abi(…)/classificationkeystone: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
structwrapperName<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 throughbase—unsafeBitCast, the text analogue of windows-rs'stransmute_copyacross 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, nowhere-constraints), a non-generic base (IInspectable) is inherited unchanged. The non-generic-closed-base wall cannot occur.The
Database+SQLdecode layer gains erased-ABI (abi(return:)/abi(parameter:)) and classification (reference(return:)/reference(parameter:)) accessors beside the existing typeddecode(…), all sharing the extracted signature-navigation.Shellcomposes the per-slot erased/typed spellings and the forwarding cast into the render context; thecom.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.