Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Sources/SQLEngineWinMD/Database+SQL.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ extension WinMD.Storage: SQLEngine.Catalog {
/// physically-present tables plus the referenced optionals.
private static var optionals: Array<(name: String, schema: TableSchema.Type)> {
[(name: "TypeSpec", schema: Metadata.Tables.TypeSpec.self),
(name: "NestedClass", schema: Metadata.Tables.NestedClass.self)]
(name: "NestedClass", schema: Metadata.Tables.NestedClass.self),
(name: "ClassLayout", schema: Metadata.Tables.ClassLayout.self)]
}

/// The relation named `name`, resolved case-insensitively against the
Expand Down
3 changes: 3 additions & 0 deletions Sources/winmd-inspect/Resources/Languages/swift.lang
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ guid-clsid CLSID
# The well-known projection of resolved CLR types to `COM` module symbols.
wellknown Windows.Win32.Foundation.HRESULT HRESULT
wellknown Windows.Win32.Foundation.BOOL BOOL
# `_` is the wildcard pattern, not a name: a metadata identifier spelled `_`
# must be backtick-escaped (`` `_` ``) to project as a usable declaration.
keyword _
keyword Any
keyword as
keyword associatedtype
Expand Down
21 changes: 15 additions & 6 deletions Sources/winmd-inspect/Resources/Render/fields.sql
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
-- A struct's fields, in declaration order, through the `fields` view bound by
-- the struct's `Id` — each field's `Id` (for the render to decode its type from
-- its `FieldDef` signature) and its keyword-escaped `Name`. The render spells
-- each field's type at render time from the target `Dialect`, so the query
-- projects only the identity and name, the way `params` does for a method.
-- its `FieldDef` signature), its keyword-escaped `Name`, its `Flags`, and its
-- raw (unsanitized) metadata name. The render spells each field's type
-- at render time from the target `Dialect`, so the query projects the identity
-- and name the way `params` does for a method; the `Flags` let the struct
-- render drop a static or literal field (the `fdStatic` bit), which is not
-- instance storage, while the enum render (which reads the same view for its
-- members) keeps them. The raw name lets the enum render find its `value__`
-- storage field by its metadata name, unaffected by a `SANITIZE` override that
-- would change the escaped spelling the way a member's does.
SELECT
Id,
SANITIZE(Name) AS Name
f.Id,
SANITIZE(f.Name) AS Name,
d.Flags AS Flags,
f.Name AS Raw
FROM
fields
fields f
JOIN FieldDef d ON d.Id = f.Id
53 changes: 53 additions & 0 deletions Sources/winmd-inspect/Resources/Render/flags.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
-- Whether the enum `TypeDef` at `:parent` bears a `System.FlagsAttribute`
-- custom attribute — the `[flags]` marking that makes the enum a bitmask. It
-- returns the enum's `Id` when the attribute is present and no row otherwise,
-- so the render projects a `[flags]` enum as an `OptionSet` (its members
-- combine) and a plain enum as a native fixed-size Swift `enum`.
--
-- The attribute is matched by its (namespace, name) the way `guid.sql` and the
-- `interfaces` view match `GuidAttribute` — through the three encodings a
-- `CustomAttribute` names its constructor by: a `MemberRef` to a `TypeRef` (an
-- external reference, the shape real metadata uses for `System.FlagsAttribute`),
-- a `MethodDef` on the attribute `TypeDef` (a local definition), and a
-- `MemberRef` to a `TypeDef` (a `MemberRef` naming a local definition). Only the
-- namespace and name differ from `guid.sql`: `System.FlagsAttribute`, not the
-- Win32 metadata `GuidAttribute`.
--
-- `CustomAttribute`/`MemberRef` are present in real metadata; a fixture that
-- omits the matching rows simply yields no row, so the enum reads as not a
-- `[flags]` enum — the same graceful non-match the `guid` query relies on.
SELECT
t.Id AS flags
FROM
TypeDef t
JOIN CustomAttribute c ON c.Parent_TypeDef = t.Id
JOIN MemberRef r ON c.Type_MemberRef = r.Id
JOIN TypeRef g ON r.Class_TypeRef = g.Id
WHERE
g.TypeNamespace = 'System'
AND g.TypeName = 'FlagsAttribute'
AND t.Id = :parent
UNION
SELECT
t.Id AS flags
FROM
TypeDef t
JOIN CustomAttribute c ON c.Parent_TypeDef = t.Id
JOIN MethodDef m ON c.Type_MethodDef = m.Id
JOIN TypeDef g ON m.TypeDef = g.Id
WHERE
g.TypeNamespace = 'System'
AND g.TypeName = 'FlagsAttribute'
AND t.Id = :parent
UNION
SELECT
t.Id AS flags
FROM
TypeDef t
JOIN CustomAttribute c ON c.Parent_TypeDef = t.Id
JOIN MemberRef r ON c.Type_MemberRef = r.Id
JOIN TypeDef g ON r.Class_TypeDef = g.Id
WHERE
g.TypeNamespace = 'System'
AND g.TypeName = 'FlagsAttribute'
AND t.Id = :parent
43 changes: 43 additions & 0 deletions Sources/winmd-inspect/Resources/Render/layout.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
-- Whether the value type at `:parent` has an ABI layout the `@frozen` struct
-- projection cannot faithfully reproduce: it returns the type's `Id` when the
-- type must be rejected, no row when it renders safely.
--
-- Only a sequential layout is safe. The `0x18` LayoutMask (§II.23.1.15) marks a
-- value type auto (`0x00`), sequential (`0x08` tdSequentialLayout), or explicit
-- (`0x10` tdExplicitLayout). An auto layout lets the runtime lay the fields out
-- in whatever order it chooses, so their declaration (FieldDef) order need not
-- be their memory order — a Swift `struct`, which lays its stored properties out
-- in source order, would then mismatch. An explicit layout places fields at
-- declared offsets — an overlapping union or a hand-laid record — which a Swift
-- `struct` cannot express. Only a sequential layout guarantees the fields lie in
-- declaration order at their natural offsets, the one arrangement a `struct`
-- reproduces, so the check admits `0x08` alone and rejects auto, explicit, and
-- the reserved `0x18`.
--
-- A non-default packing (a `ClassLayout` row whose `PackingSize` is not zero)
-- tightens the fields below their natural alignment, shifting every offset past
-- the first. A declared size (a `ClassLayout` row whose `ClassSize` is not zero,
-- §II.22.8) fixes the total in-memory extent — padding the record out to a
-- larger size, or forcing tail padding a naturally-laid struct would not carry —
-- which no `@frozen` `struct` reproduces either. `@frozen` only freezes the
-- layout Swift itself chooses; it applies neither an explicit offset, a packing,
-- nor a declared size, so such a type would decode to the wrong size and field
-- offsets when passed to a native API. The closure walk rejects a type this
-- query returns a row for, leaving it a frontier the consumer defines rather
-- than an ABI-incompatible declaration. A `ClassSize` of zero is unspecified
-- (the natural size stands), so it is safe; any non-zero declared size is
-- rejected conservatively.
--
-- `ClassLayout` is an optional table (`table(named:)` resolves it to an empty
-- relation when absent), so a database with no laid-out type reads no packing or
-- size row and the LEFT JOIN yields NULL, which `COALESCE` treats as the default.
SELECT
t.Id
FROM
TypeDef t
LEFT JOIN ClassLayout c ON c.Parent = t.Id
WHERE
t.Id = :parent
AND (BITAND(t.Flags, 24) <> 8
OR COALESCE(c.PackingSize, 0) <> 0
OR COALESCE(c.ClassSize, 0) <> 0)
6 changes: 4 additions & 2 deletions Sources/winmd-inspect/Resources/Render/requires.sql
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ SELECT
n.Id,
n.TypeNamespace,
n.TypeName,
n.iid
n.iid,
'interface' AS kind
FROM
InterfaceImpl i
JOIN resolved rv ON rv.ref = i.Interface_TypeRef
Expand All @@ -66,7 +67,8 @@ SELECT
n.Id,
n.TypeNamespace,
n.TypeName,
n.iid
n.iid,
'interface' AS kind
FROM
InterfaceImpl i
JOIN interfaces n ON n.Id = i.Interface_TypeDef
Expand Down
66 changes: 66 additions & 0 deletions Sources/winmd-inspect/Resources/Templates/com.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,69 @@ public protocol {{{name}}}{{#base}}: {{{.}}}{{/base}} {
{{/methods}}
}
{{/interface}}
{{#struct}}
// @frozen fixes the ABI layout, so the projected struct is the C/C++ record it
// mirrors field for field — the transparent layout interop passes by value.
@frozen public struct {{{name}}} {
{{#fields}}
public var {{{name}}}: {{{type}}}
{{/fields}}

// The synthesized memberwise initializer is internal even for a public
// struct, so an explicit public one lets a caller outside the generated
// module construct the value type to pass to a native API.
public init({{#fields}}{{{name}}}{{#aliased}} {{{local}}}{{/aliased}}: {{{type}}}{{^last}}, {{/last}}{{/fields}}) {
{{#fields}}
self.{{{name}}} = {{{local}}}
{{/fields}}
}
}
{{/struct}}
{{#enum}}
{{#flags}}
{{! A `[flags]` enum projects to an OptionSet the way ClangImporter imports an
NS_OPTIONS C enum: its members combine as a bitmask, which a native `enum`
case — a single value — cannot name. `rawValue` is typed as the enum's
`value__` underlying type directly — the OptionSet `RawValue` associated
type is inferred from it rather than spelled as an explicit `typealias`, so
a member legitimately named `RawValue` (not a keyword `SANITIZE` escapes)
stays a `static var` and does not collide with a support typealias. The set
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 {

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 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 👍 / 👎.

public let rawValue: {{{underlying}}}
@inlinable public init(rawValue: {{{underlying}}}) { self.rawValue = rawValue }
{{#members}}
@_transparent public static var {{{name}}}: {{{owner}}} { {{{owner}}}(rawValue: {{{value}}}) }
{{/members}}
}
{{/flags}}
{{^flags}}
// A regular enum projects to an explicitly-stored raw-value struct newtype, not
// a native Swift enum: a native `enum E: {{{underlying}}}` does not carry the ABI
// layout of its raw type — `@frozen` freezes the compact representation Swift
// chooses, and the raw type is only a `RawRepresentable` conversion, so a
// two-case `enum E: UInt32` is one byte, not four, and the signature would pass
// the wrong width to native code. The newtype stores the `value__` underlying
// type directly, so it is the ABI-exact width of the C enum; its members are
// named constants and it holds any ABI value (an undeclared or aliased one)
// where a native enum would be the wrong size and trap.
@frozen public struct {{{name}}}: Hashable, Sendable {
public var rawValue: {{{underlying}}}
@inlinable public init(rawValue: {{{underlying}}}) { self.rawValue = rawValue }
{{#members}}
@_transparent public static var {{{name}}}: {{{owner}}} { {{{owner}}}(rawValue: {{{value}}}) }
{{/members}}
}
{{/flags}}
{{/enum}}
{{#delegate}}
// A delegate is a COM interface — IUnknown plus a single `Invoke` — so `@com`
// 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}}} {

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 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 👍 / 👎.

func Invoke({{#params}}_ {{{name}}}: {{{type}}}{{^last}}, {{/last}}{{/params}}){{#returns}} -> {{{.}}}{{/returns}}
}
{{/delegate}}
Loading