winmd-inspect: close over signature-named value types and delegates - #349
winmd-inspect: close over signature-named value types and delegates#349compnerd wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d77699eaa1
ℹ️ 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".
|
@codex review |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d77699eaa1
ℹ️ 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".
d77699e to
0f3673e
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0f3673e47f
ℹ️ 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".
0f3673e to
3fe1bff
Compare
3fe1bff to
87e3fd4
Compare
87e3fd4 to
a2fdeba
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a2fdebaffa
ℹ️ 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".
a2fdeba to
7dce41c
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7dce41c3e4
ℹ️ 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".
Extend the `--closure` walk to pull in the value-type and delegate types a
rendered interface names through its method signatures — the E3 value arm, the
E6 struct-field types, and the E7 delegate signatures — so the closure emits
everything a consumer needs to compile the projection, not the interface surface
alone. Each projected type is an ABI-faithful shape, the way the interface
projects an `@com` protocol that lowers to the COM vtable.
`emit` now dispatches on the row's kind (`found[4]`): a struct through
`structure`, an enum through `enumeration`, a delegate through `delegation`, and
an interface through `interface` (the former inline body, now its own half),
each rendered under exactly one of the template's `{{#struct}}`/`{{#enum}}`/
`{{#delegate}}`/`{{#interface}}` sections. The `walk` seeds and the `requires`
bases carry the kind at index 4 so the walk and emit dispatch on the same column
a `references` row already has.
The projected shapes match how the target language's import models them. A
struct is an `@frozen` struct, its guaranteed layout the C/C++ record it mirrors
field for field, with a public memberwise initialiser (the synthesised one is
internal, so a cross-module caller could not otherwise construct it). A field
literally named `self` — escaped `` `self` `` for its stored property — takes a
distinct collision-free local as the initialiser parameter's internal name and
the assignment right-hand side (`` init(`self` arg0:) { self.`self` = arg0 } ``);
reusing the field spelling would bind the assignment's leading `self` to the
parameter, not the struct instance, and fail to compile. A field named `_` — the
wildcard, no name — is added to the language spec's reserved words so `SANITIZE`
escapes it to `` `_` `` for a declarable property; unlike `self` it does not
shadow the instance, so it needs no distinct local. Only instance fields are
storage: a static or literal field (the `fdStatic` bit) is dropped, and the
dependency walk applies the same filter so a type named only by a dropped field
is not pulled in. A value type whose ABI layout `@frozen` cannot reproduce — an
explicit layout, a non-default packing, or a declared class size — is rejected
as a frontier rather than misprojected; the `layout` query returns a row only
for such a type (`ClassLayout` is an optional table the adapter now synthesises
empty when absent). An enum projects to an explicitly-stored raw-value struct
newtype, because a native `enum E: <underlying>` does not carry its raw type's
ABI width; a `[flags]` enum (a `System.FlagsAttribute`, detected through the
overridable `flags` query) is the same newtype refined to `OptionSet`, typing
`rawValue` as the underlying type directly so the OptionSet `RawValue` associated
type is inferred rather than spelled as an explicit `typealias` — a member
legitimately named `RawValue` then stays a `static var` and does not collide with
a support typealias. The `value__` storage field is found by its raw metadata
name, so a `SANITIZE` override respelling it does not drop the enum to the `i4`
fallback width, and each member's value is the constant `value(field:)` already
formats per signedness. A delegate is an `@com(interface:)` protocol carrying a
single `Invoke`, decoding its runtime IID through the `guid` query.
Signature adjacency (E3/E6/E7) runs per kind — an interface over its methods, a
struct over its instance fields, a delegate over its `Invoke` — resolving each
referenced type through `references` by the exact row it was named through,
never by (namespace, name), so two same-named nested types are not conflated. A
resolved runtime `class` stays a frontier; an external `TypeRef`-only reference
resolves to nothing and drops; an interface or delegate bearing no
`GuidAttribute` — whose `@com` shape would spell a GUID-less `""` — is dropped. A
reached type the language import already provides — a `Dialect.known` identity
such as `HRESULT` — is a frontier the closure emits no wrapper for, since the
consumer has it from the import.
A signature-named value type or delegate emits at top level by its bare name.
Metadata nesting (`Outer.Inner`) and the namespace disambiguation of same-named
value types are follow-ups; this change lands the per-kind projections and the
signature closure on the unique-name path.
Integration tests over hand-built fixtures cover a struct-returning method (the
`@frozen` struct with its public initialiser, emitted before the interface, a
field named `self` given a distinct initialiser local, and a field named `_`
escaped to `` `_` ``), an enum parameter (a
stored newtype, a duplicate raw value as two constants, a `[flags]` `OptionSet`,
and a flags member named `RawValue` kept distinct from the inferred support
type), a delegate parameter (its `@com` protocol and decoded IID), and a
signature-named interface enqueued and emitted; further tests pin an
auto/explicit/packed/sized struct and a static struct field each dropped, a
GUID-less interface and a language-provided type each frontiered, and a
unique-named value type emitted bare with no namespace.
The `fields` render query gains `Flags` and raw-name columns so the walk and the
struct render drop a static field and the enum render finds its `value__`
storage. Render-query shadowing is a supported extension point, though, so a `-I`
override carrying the former two-column shape (`Id`, `Name`) is tolerated rather
than trapping on the absent columns: a field with no `Flags` counts as instance
storage, as it did before the columns existed, and the raw-name read falls back
to the escaped `Name`.
The `requires` walk tolerates a copied override of the same kind. The bundled
query returns a trailing `kind` column ('interface') the walk and emit read from
`found[4]`; an override copied from the earlier four-column shape (`Id`,
namespace, name, `iid`) omits it. A base row shorter than five columns is padded
to the interface shape before recursion — an `InterfaceImpl` base is always an
interface — so the closure resolves and emits it rather than trapping on the
absent column.
The closure is scoped to pure COM: the walk frontiers a parameterised (generic)
type — its `TypeName` bears an arity suffix — so a WinRT parameterised interface
or delegate is never emitted, only referenced as a consumer-supplied type. A
delegate projects one `@com` protocol (no generic wrapper/ABI-protocol arm), and
`delegation` builds no generic-parameter clause.
7dce41c to
bdcdb81
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bdcdb81b2c
ℹ️ 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".
| is thus the ABI-exact width of the C enum, and each `@_transparent` member | ||
| folds to its raw constant at the use site with no static storage; a repeated | ||
| raw value across members is fine here. }} | ||
| @frozen public struct {{{name}}}: OptionSet { |
There was a problem hiding this comment.
Qualify support protocols to avoid enum-name shadowing
When metadata defines a flags enum named OptionSet, the generated declaration becomes struct OptionSet: OptionSet, and Swift resolves the conformance target to the struct being declared, producing “inheritance from non-protocol type.” The regular-enum branch has the same problem for types named Hashable or Sendable; qualify these support protocols (for example, Swift.OptionSet) or otherwise disambiguate them from the projected type name.
Useful? React with 👍 / 👎.
| // generates the IUnknown-based vtable from this Invoke-only protocol, the same | ||
| // ABI-faithful shape an interface projects, not a Swift closure. | ||
| @com(interface: "{{{iid}}}") | ||
| public protocol {{{name}}} { |
There was a problem hiding this comment.
Make projected delegates inherit the COM root
When --closure reaches a delegate, this emits an Invoke-only protocol with no IUnknown refinement, even though the delegate is described here as an IUnknown-based COM interface and the ordinary interface path explicitly adds the language's COM root. Consequently the projected delegate does not model the inherited COM surface and can produce the wrong vtable inheritance for @com; emit the root base for delegates as well.
Useful? React with 👍 / 👎.
Extend the
--closurewalk to pull in the value-type and delegate types a rendered interface names through its method signatures — the E3 value arm, the E6 struct-field types, and the E7 delegate signatures — so the closure emits everything a consumer needs to compile the projection, not the interface surface alone. Each projected type is an ABI-faithful shape, the way the interface projects an@comprotocol that lowers to the COM vtable.emitnow dispatches on the row's kind (found[4]): a struct throughstructure, an enum throughenumeration, a delegate throughdelegation, and an interface throughinterface(the former inline body, now its own half), each rendered under exactly one of the template's{{#struct}}/{{#enum}}/{{#delegate}}/{{#interface}}sections. Thewalkseeds and therequiresbases carry the kind at index 4 so the walk and emit dispatch on the same column areferencesrow already has.The projected shapes match how the target language's import models them. A struct is an
@frozenstruct, its guaranteed layout the C/C++ record it mirrors field for field, with a public memberwise initialiser (the synthesised one is internal, so a cross-module caller could not otherwise construct it). Only instance fields are storage: a static or literal field (thefdStaticbit) is dropped, and the dependency walk applies the same filter so a type named only by a dropped field is not pulled in. A value type whose ABI layout@frozencannot reproduce — an explicit layout, a non-default packing, or a declared class size — is rejected as a frontier rather than misprojected; thelayoutquery returns a row only for such a type (ClassLayoutis an optional table the adapter now synthesises empty when absent). An enum projects to an explicitly-stored raw-value struct newtype, because a nativeenum E: <underlying>does not carry its raw type's ABI width; a[flags]enum (aSystem.FlagsAttribute, detected through the overridableflagsquery) is the same newtype refined toOptionSet. Thevalue__storage field is found by its raw metadata name, so aSANITIZEoverride respelling it does not drop the enum to thei4fallback width, and each member's value is the constantvalue(field:)already formats per signedness. A delegate is an@com(interface:)protocol carrying a singleInvoke, decoding its runtime IID through theguidquery.Signature adjacency (E3/E6/E7) runs per kind — an interface over its methods, a struct over its instance fields, a delegate over its
Invoke— resolving each referenced type throughreferencesby the exact row it was named through, never by (namespace, name), so two same-named nested types are not conflated. A resolved runtimeclassstays a frontier; an externalTypeRef-only reference resolves to nothing and drops; a nongeneric interface or delegate bearing noGuidAttribute— whose@comshape would spell a GUID-less""— is dropped, while a generic one (no static IID by design) is kept and renders through the generic arm. A reached type the language import already provides — aDialect.knownidentity such asHRESULT— is a frontier the closure emits no wrapper for, since the consumer has it from the import.A signature-named value type or delegate emits at top level by its bare name. Metadata nesting (
Outer.Inner) and the namespace disambiguation of same-named value types are follow-ups; this change lands the per-kind projections and the signature closure on the unique-name path.Integration tests over hand-built fixtures cover a struct-returning method (the
@frozenstruct with its public initialiser, emitted before the interface), an enum parameter (a stored newtype, a duplicate raw value as two constants, and a[flags]OptionSet), a delegate parameter (its@comprotocol and decoded IID, plus a parameterised delegate kept through its generic arm and a keyword-named generic parameter escaped), and a signature-named interface enqueued and emitted; further tests pin an auto/explicit/packed/sized struct and a static struct field each dropped, a GUID-less nongeneric interface and a language-provided type each frontiered, and a unique-named value type emitted bare with no namespace.