Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better leverage Projection.toType(Reference|Expression) #444

Merged
merged 5 commits into from
Dec 25, 2024
Merged
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
11 changes: 11 additions & 0 deletions Generator/Sources/ProjectionModel/Projection+conversion.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,17 @@ extension Projection {
return result
}

public func toBindingType(_ type: TypeDefinition) throws -> SwiftType {
.named(try toBindingTypeName(type))
}

public func toBindingType(_ type: BoundType) throws -> SwiftType {
let definitionBindingType = try toBindingType(type.definition)
return type.genericArgs.isEmpty
? definitionBindingType
: definitionBindingType.member(try Projection.toBindingInstantiationTypeName(genericArgs: type.genericArgs))
}

public static func getSwiftAttributes(_ member: any Attributable) throws -> [SwiftAttribute] {
// We recognize any attribute called SwiftAttribute and expect it has a field called Literal,
// ideally that would be a positional argument, but IDL doesn't seem to have a syntax for that.
Expand Down
2 changes: 1 addition & 1 deletion Generator/Sources/ProjectionModel/Projection+params.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ extension Projection {

public func toParameter(label: String = "_", _ param: Param, genericTypeArgs: [TypeNode] = []) throws -> SwiftParam {
SwiftParam(label: label, name: toParamName(param), `inout`: param.isByRef,
type: try genericTypeArgs.isEmpty ? toType(param.type) : toType(param.type.bindGenericParams(typeArgs: genericTypeArgs)))
type: try toTypeExpression(genericTypeArgs.isEmpty ? param.type : param.type.bindGenericParams(typeArgs: genericTypeArgs)))
}

public func getParamBinding(_ param: ParamBase, genericTypeArgs: [TypeNode] = []) throws -> ParamProjection {
Expand Down
48 changes: 23 additions & 25 deletions Generator/Sources/ProjectionModel/Projection+types.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,38 @@ import WindowsMetadata
import CodeWriters

extension Projection {
public func toType(_ type: TypeNode) throws -> SwiftType {
public func toTypeExpression(_ type: TypeNode, outerNullable: Bool = true) throws -> SwiftType {
switch type {
case let .bound(type):
return try toType(type)
case let .bound(boundType):
if let specialTypeBinding = try getSpecialTypeBinding(boundType) {
if boundType.definition.namespace == "System", boundType.definition.name == "Object", !outerNullable {
return specialTypeBinding.swiftType.unwrapOptional()
}
return specialTypeBinding.swiftType
}

let swiftType = try SwiftType.named(
toTypeName(boundType.definition),
genericArgs: boundType.genericArgs.map { try toTypeExpression($0) })
return boundType.definition.isReferenceType && outerNullable ? swiftType.optional() : swiftType
case let .genericParam(param):
return .named(param.name)
case let .array(of: element):
return .array(element: try toType(element))
return .array(element: try toTypeExpression(element))
default:
fatalError("Not implemented: Swift representation of values of type \(type)")
}
}

public func toType(_ boundType: BoundType, nullable: Bool = true) throws -> SwiftType {
public func toTypeReference(_ boundType: BoundType) throws -> SwiftType {
// getSpecialTypeBinding returns a type expression, which includes the optional wrapping.
if let specialTypeBinding = try getSpecialTypeBinding(boundType) {
return specialTypeBinding.swiftType
return specialTypeBinding.swiftType.unwrapOptional()
}

let swiftObjectType: SwiftType = .named(
return .named(
try toTypeName(boundType.definition),
genericArgs: try boundType.genericArgs.map { try toType($0) })
return boundType.definition.isReferenceType && nullable ? swiftObjectType.optional() : swiftObjectType
genericArgs: try boundType.genericArgs.map { try toTypeExpression($0) })
}

public func isPODBinding(_ typeDefinition: TypeDefinition) throws -> Bool {
Expand Down Expand Up @@ -60,9 +70,8 @@ extension Projection {
try enumDefinition.attributes.contains(where: { try $0.type.name == "SwiftEnumAttribute" }) && !enumDefinition.isFlags
}

public func toReturnType(_ type: TypeNode, typeGenericArgs: [TypeNode]? = nil) throws -> SwiftType {
let swiftType = try toType(type.bindGenericParams(typeArgs: typeGenericArgs))
return isNullAsErrorEligible(type) ? swiftType.unwrapOptional() : swiftType
public func toReturnType(_ type: TypeNode) throws -> SwiftType {
try toTypeExpression(type, outerNullable: !isNullAsErrorEligible(type))
}

public func getTypeBinding(_ type: TypeNode) throws -> TypeProjection {
Expand Down Expand Up @@ -108,23 +117,12 @@ extension Projection {
abiType = .unsafeMutablePointer(pointee: abiType).optional()
}

let bindingType: SwiftType = try {
let bindingTypeName = try toBindingTypeName(type.definition)
if type.genericArgs.isEmpty {
return .named(bindingTypeName)
}
else {
return .named(bindingTypeName)
.member(try Projection.toBindingInstantiationTypeName(genericArgs: type.genericArgs))
}
}()

return TypeProjection(
abiType: abiType,
abiDefaultValue: type.definition.isReferenceType ? "nil" : .defaultInitializer,
swiftType: try toType(type.asNode),
swiftType: try toTypeExpression(type.asNode),
swiftDefaultValue: type.definition.isReferenceType ? "nil" : .defaultInitializer,
bindingType: bindingType,
bindingType: try toBindingType(type),
kind: try isPODBinding(type.definition) ? .pod : .allocating)
}

Expand Down
5 changes: 5 additions & 0 deletions Generator/Sources/ProjectionModel/SupportModules.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ extension SupportModules.COM {
public static var comBinding: SwiftType { moduleType.member("COMBinding") }
public static var comTwoWayBinding: SwiftType { moduleType.member("COMTwoWayBinding") }

public static var comImport: SwiftType { moduleType.member("COMImport") }
public static func comImport(of type: SwiftType) -> SwiftType {
moduleType.member("COMImport", genericArgs: [type])
}

public static var comEmbedding: SwiftType { moduleType.member("COMEmbedding") }

public static var comReference: SwiftType { moduleType.member("COMReference") }
Expand Down
49 changes: 23 additions & 26 deletions Generator/Sources/SwiftWinRT/Writing/ABIBinding.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ import struct Foundation.UUID
/// Writes a type or extension providing the ABIBinding conformance for a given projected WinRT type.
internal func writeABIBindingConformance(_ typeDefinition: TypeDefinition, genericArgs: [TypeNode]?, projection: Projection, to writer: SwiftSourceFileWriter) throws {
if SupportModules.WinRT.getBuiltInTypeKind(typeDefinition) == .definitionAndBinding {
// The support module already defines a projection, just import and reexport it.
// The support module already defines a projection, just reexport it.
if typeDefinition.isReferenceType {
let bindingTypeName = try projection.toBindingTypeName(typeDefinition)
writer.writeImport(exported: true, kind: .enum, module: SupportModules.WinRT.moduleName, symbolName: bindingTypeName)
writer.writeImport(exported: true, kind: .enum,
module: SupportModules.WinRT.moduleName,
symbolName: try projection.toBindingTypeName(typeDefinition))
}
else {
// The struct conforms to ABIBinding itself, and we already imported it.
Expand All @@ -30,7 +31,7 @@ internal func writeABIBindingConformance(_ typeDefinition: TypeDefinition, gener
let enumBindingProtocol = try projection.isSwiftEnumEligible(enumDefinition)
? SupportModules.WinRT.closedEnumBinding : SupportModules.WinRT.openEnumBinding
try writer.writeExtension(
type: .named(projection.toTypeName(enumDefinition)),
type: projection.toTypeReference(enumDefinition.bindType()),
protocolConformances: [ enumBindingProtocol ]) { writer in
// public static var typeName: String { "..." }
try writeTypeNameProperty(type: enumDefinition.bindType(), to: writer)
Expand Down Expand Up @@ -59,20 +60,20 @@ internal func writeABIBindingConformance(_ typeDefinition: TypeDefinition, gener
// Non-generic type, create a standard projection type.
// enum IVectorBinding: WinRTBinding... {}
try writeInterfaceOrDelegateBindingType(typeDefinition.bindType(),
projectionName: try projection.toBindingTypeName(typeDefinition), projection: projection, to: writer)
name: try projection.toBindingTypeName(typeDefinition),
projection: projection, to: writer)
}
else if let genericArgs {
// Generic type specialization. Create a projection for the specialization.
// extension IVectorBinding {
// internal final class Boolean: WinRTBinding... {}
// }
try writer.writeExtension(
type: .named(projection.toBindingTypeName(typeDefinition))) { writer in
type: projection.toBindingType(typeDefinition)) { writer in
try writeInterfaceOrDelegateBindingType(
typeDefinition.bindType(genericArgs: genericArgs),
projectionName: try Projection.toBindingInstantiationTypeName(genericArgs: genericArgs),
projection: projection,
to: writer)
name: try Projection.toBindingInstantiationTypeName(genericArgs: genericArgs),
projection: projection, to: writer)
}
}
else {
Expand Down Expand Up @@ -256,21 +257,19 @@ fileprivate func writeClassBindingType(
to writer: SwiftSourceFileWriter) throws {
assert(!classDefinition.isStatic)

let projectionProtocol = try classDefinition.hasAttribute(ComposableAttribute.self)
let bindingProtocol = try classDefinition.hasAttribute(ComposableAttribute.self)
? SupportModules.WinRT.composableClassBinding
: SupportModules.WinRT.runtimeClassBinding

let bindingTypeName = try projection.toBindingTypeName(classDefinition)
try writer.writeClass(
visibility: Projection.toVisibility(classDefinition.visibility),
name: bindingTypeName, protocolConformances: [ projectionProtocol ]) { writer throws in
let typeName = try projection.toTypeName(classDefinition)

name: projection.toBindingTypeName(classDefinition),
protocolConformances: [ bindingProtocol ]) { writer throws in
try writeReferenceTypeBindingConformance(
apiType: classDefinition.bindType(),
abiType: defaultInterface.asBoundType,
wrapImpl: { writer, paramName in
writer.writeStatement("\(typeName)(_wrapping: consume \(paramName))")
writer.writeStatement(".init(_wrapping: consume \(paramName))")
},
projection: projection,
to: writer)
Expand All @@ -289,8 +288,7 @@ fileprivate func writeComposableClassOuterObject(

let baseOuterObjectClass: SwiftType
if let base = try classDefinition.base, try base.definition.base != nil {
baseOuterObjectClass = .named(try projection.toBindingTypeName(base.definition))
.member(outerObjectClassName)
baseOuterObjectClass = try projection.toBindingType(base.definition).member(outerObjectClassName)
} else {
baseOuterObjectClass = SupportModules.WinRT.composableClass_outerObject
}
Expand All @@ -306,8 +304,6 @@ fileprivate func writeComposableClassOuterObject(
return
}

let classSwiftType: SwiftType = .named(try projection.toTypeName(classDefinition))

try writer.writeClass(
visibility: .open,
name: outerObjectClassName,
Expand All @@ -326,7 +322,7 @@ fileprivate func writeComposableClassOuterObject(

// _ifoo.initOwner(owner as! MyClass)
// return _ifoo.toCOM()
writer.writeStatement("\(propertyName).initOwner(owner as! \(classSwiftType))")
writer.writeStatement("\(propertyName).initOwner(owner as! SwiftObject)")
writer.writeReturnStatement(value: "\(propertyName).toCOM()")
}
}
Expand Down Expand Up @@ -356,19 +352,19 @@ fileprivate func writeComposableClassOuterObject(

fileprivate func writeInterfaceOrDelegateBindingType(
_ type: BoundType,
projectionName: String,
name: String,
projection: Projection,
to writer: some SwiftDeclarationWriter) throws {
precondition(type.definition is InterfaceDefinition || type.definition is DelegateDefinition)
let projectionProtocol = type.definition is InterfaceDefinition
let bindingProtocol = type.definition is InterfaceDefinition
? SupportModules.WinRT.interfaceBinding : SupportModules.WinRT.delegateBinding

// Projections of generic instantiations are not owned by any specific module.
// Making them internal avoids clashes between redundant definitions across modules.
try writer.writeEnum(
visibility: type.genericArgs.isEmpty ? Projection.toVisibility(type.definition.visibility) : .internal,
name: projectionName,
protocolConformances: [ projectionProtocol ]) { writer throws in
name: name,
protocolConformances: [ bindingProtocol ]) { writer throws in

let importClassName = "Import"

Expand Down Expand Up @@ -397,7 +393,8 @@ fileprivate func writeInterfaceOrDelegateBindingType(
}

try writeCOMImportClass(
type, visibility: .private, name: importClassName, projectionName: projectionName,
type, visibility: .private, name: importClassName,
bindingType: .named(name),
projection: projection, to: writer)

// public static var virtualTablePointer: UnsafeRawPointer { .init(withUnsafePointer(to: &virtualTable) { $0 }) }
Expand Down Expand Up @@ -426,7 +423,7 @@ internal func writeReferenceTypeBindingConformance(
projection: Projection,
to writer: SwiftTypeDefinitionWriter) throws {
writer.writeTypeAlias(visibility: .public, name: "SwiftObject",
target: try projection.toType(apiType.asNode).unwrapOptional())
target: try projection.toTypeReference(apiType))
writer.writeTypeAlias(visibility: .public, name: "ABIStruct",
target: try projection.toABIType(abiType))

Expand Down
12 changes: 6 additions & 6 deletions Generator/Sources/SwiftWinRT/Writing/COMImportClass.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,25 @@ internal func writeCOMImportClass(
_ type: BoundType,
visibility: SwiftVisibility,
name: String,
projectionName: String,
bindingType: SwiftType,
projection: Projection,
to writer: SwiftTypeDefinitionWriter) throws {
let importBaseTypeName: String
let importBaseType: SwiftType
let protocolConformances: [SwiftType]
switch type.definition {
case let interfaceDefinition as InterfaceDefinition:
importBaseTypeName = "WinRTImport"
importBaseType = SupportModules.WinRT.winRTImport(of: bindingType)
protocolConformances = [.named(try projection.toProtocolName(interfaceDefinition)) ]
case is DelegateDefinition:
importBaseTypeName = "COMImport"
importBaseType = SupportModules.COM.comImport(of: bindingType)
protocolConformances = []
default: fatalError()
}

// private final class Import: WinRTImport<IFooBinding>, IFooProtocol {}
try writer.writeClass(
visibility: visibility, final: true, name: name,
base: .named(importBaseTypeName, genericArgs: [ .named(projectionName) ]),
base: importBaseType,
protocolConformances: protocolConformances) { writer throws in

let interfaces = try type.definition.baseInterfaces.map {
Expand Down Expand Up @@ -92,7 +92,7 @@ internal func writeGenericTypeAliases(interfaces: [BoundInterface], projection:
for (index, genericArg) in interface.genericArgs.enumerated() {
let genericParamName = interface.definition.genericParams[index].name
if typeAliases[genericParamName] == nil {
typeAliases[genericParamName] = try projection.toType(genericArg)
typeAliases[genericParamName] = try projection.toTypeExpression(genericArg)
}
}
}
Expand Down
14 changes: 6 additions & 8 deletions Generator/Sources/SwiftWinRT/Writing/ClassDefinition.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,13 @@ internal func writeClassDefinition(_ classDefinition: ClassDefinition, projectio
return
}

let bindingTypeName = try projection.toBindingTypeName(classDefinition)

// Both composable and activatable classes can have a base class
let base: SwiftType
if let baseClassDefinition = try getRuntimeClassBase(classDefinition) {
base = try projection.toType(baseClassDefinition.bindType(), nullable: false)
base = try projection.toTypeReference(baseClassDefinition.bindType())
} else {
base = classDefinition.isSealed
? SupportModules.WinRT.winRTImport(of: .named(bindingTypeName))
base = try classDefinition.isSealed
? SupportModules.WinRT.winRTImport(of: projection.toBindingType(classDefinition))
: SupportModules.WinRT.composableClass
}

Expand Down Expand Up @@ -259,7 +257,7 @@ fileprivate func writeComposableInitializers(
let propertyName = SecondaryInterfaces.getPropertyName(factoryInterface.bind())

let baseClassDefinition = try getRuntimeClassBase(classDefinition)
let outerObjectType: SwiftType = .named(try projection.toBindingTypeName(classDefinition)).member("OuterObject")
let outerObjectType = try projection.toBindingType(classDefinition).member("OuterObject")

for method in factoryInterface.methods {
// Swift requires "override" on initializers iff the same initializer is defined in the direct base class
Expand Down Expand Up @@ -325,9 +323,9 @@ fileprivate func writeDefaultActivatableInitializer(
override: isOverriding,
throws: true) { writer in
let propertyName = SecondaryInterfaces.getPropertyName(interfaceName: "IActivationFactory")
let projectionClassName = try projection.toBindingTypeName(classDefinition)
let bindingType = try projection.toBindingType(classDefinition)
writer.writeStatement("let _instance = \(SupportModules.COM.comReference)(transferringRef: try Self.\(propertyName)"
+ ".activateInstance(binding: \(projectionClassName).self))")
+ ".activateInstance(binding: \(bindingType).self))")
if try hasComposableBase(classDefinition) {
writer.writeStatement("super.init(_wrapping: _instance.cast()) // Transitively casts down to IInspectable")
}
Expand Down
4 changes: 2 additions & 2 deletions Generator/Sources/SwiftWinRT/Writing/EnumDefinition.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ fileprivate func writeOpenEnumDefinition(_ enumDefinition: EnumDefinition, proje
name: structName,
protocolConformances: [.named("CStyleEnum") ]) { writer throws in

let rawValueType = try projection.toType(enumDefinition.underlyingType.bindNode())
let rawValueType = try projection.toTypeExpression(enumDefinition.underlyingType.bindNode())
writer.writeStoredProperty(visibility: .public, declarator: .var, name: "rawValue", type: rawValueType)
writer.writeInit(visibility: .public,
params: [ .init(name: "rawValue", type: rawValueType, defaultValue: "0") ]) {
Expand Down Expand Up @@ -90,7 +90,7 @@ fileprivate func writeClosedEnumDefinition(_ enumDefinition: EnumDefinition, pro
documentation: projection.getDocumentationComment(enumDefinition),
visibility: Projection.toVisibility(enumDefinition.visibility),
name: try projection.toTypeName(enumDefinition),
rawValueType: try projection.toType(enumDefinition.underlyingType.bindNode()),
rawValueType: try projection.toTypeExpression(enumDefinition.underlyingType.bindNode()),
protocolConformances: [.named("ClosedEnum") ]) { writer throws in
for field in enumDefinition.fields.filter({ $0.visibility == .public && $0.isStatic }) {
try writer.writeEnumCase(
Expand Down
Loading
Loading