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

Refactor SwiftType to support arbitrary chaining #440

Merged
merged 1 commit into from
Dec 17, 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
72 changes: 41 additions & 31 deletions Generator/Sources/CodeWriters/Swift/SwiftType+write.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,52 @@ extension SwiftType: CustomStringConvertible, TextOutputStreamable {
return output
}

private func writeNamed(_ identifier: SwiftIdentifier, genericArgs: [SwiftType], to output: inout some TextOutputStream) {
identifier.write(to: &output)
if !genericArgs.isEmpty {
output.write("<")
for (index, arg) in genericArgs.enumerated() {
if index > 0 { output.write(", ") }
arg.write(to: &output)
}
output.write(">")
}
}

private func writeParenthesizingProtocolModifiers(_ type: SwiftType, to output: inout some TextOutputStream) {
switch type {
case let .existential(`protocol`), let .opaque(`protocol`):
output.write("(")
`protocol`.write(to: &output)
output.write(")")
default:
type.write(to: &output)
}
}

public func write(to output: inout some TextOutputStream) {
switch self {
case let .chain(chain):
if chain.protocolModifier == .any {
output.write("any ")
}
else if chain.protocolModifier == .some {
output.write("some ")
}
case .self: output.write("Self")
case .any: output.write("Any")

for (index, component) in chain.components.enumerated() {
if index > 0 { output.write(".") }
component.identifier.write(to: &output)
guard !component.genericArgs.isEmpty else { continue }
output.write("<")
for (index, arg) in component.genericArgs.enumerated() {
if index > 0 { output.write(", ") }
arg.write(to: &output)
}
output.write(">")
}
case let .named(identifier, genericArgs):
writeNamed(identifier, genericArgs: genericArgs, to: &output)

case let .member(of, identifier, genericArgs):
writeParenthesizingProtocolModifiers(of, to: &output)
output.write(".")
writeNamed(identifier, genericArgs: genericArgs, to: &output)

case let .opaque(`protocol`):
output.write("some ")
`protocol`.write(to: &output)

case let .existential(`protocol`):
output.write("any ")
`protocol`.write(to: &output)

case let .`optional`(wrapped, forceUnwrap):
let parenthesized: Bool
if case let .chain(chain) = wrapped, chain.protocolModifier != nil {
output.write("(")
parenthesized = true
}
else {
parenthesized = false
}
wrapped.write(to: &output)
if parenthesized { output.write(")") }
writeParenthesizingProtocolModifiers(wrapped, to: &output)
output.write(forceUnwrap ? "!" : "?")

case let .tuple(elements):
Expand Down Expand Up @@ -70,9 +83,6 @@ extension SwiftType: CustomStringConvertible, TextOutputStreamable {
if `throws` { output.write(" throws") }
output.write(" -> ")
returnType.write(to: &output)

case .self: output.write("Self")
case .any: output.write("Any")
}
}
}
167 changes: 74 additions & 93 deletions Generator/Sources/CodeWriters/Swift/SwiftType.swift
Original file line number Diff line number Diff line change
@@ -1,125 +1,106 @@
// See https://docs.swift.org/swift-book/documentation/the-swift-programming-language/types
// Loosely based on https://docs.swift.org/swift-book/documentation/the-swift-programming-language/types
// However that grammar has a bug: it cannot represent "[Int].Element"
public enum SwiftType {
/// An identifier chain, such as "Foo.Bar" or "any Foo.Bar<Woof.Meow>".
case chain(Chain)
indirect case `optional`(wrapped: SwiftType, implicitUnwrap: Bool = false)
indirect case tuple(elements: [SwiftType])
indirect case array(element: SwiftType)
indirect case dictionary(key: SwiftType, value: SwiftType)
indirect case function(params: [SwiftType], throws: Bool = false, returnType: SwiftType)
// Self
case `self`
// Any
case any

public enum ProtocolModifier {
case any // existential
case some // opaque
}
// Foo<X, Y>
case named(SwiftIdentifier, genericArgs: [SwiftType] = [])

public struct Chain {
public let protocolModifier: ProtocolModifier?
public let components: [ChainComponent]
// X.Foo<Y, Z>
indirect case member(of: SwiftType, SwiftIdentifier, genericArgs: [SwiftType] = [])

public init(protocolModifier: ProtocolModifier? = nil, _ components: [ChainComponent]) {
precondition(!components.isEmpty)
self.protocolModifier = protocolModifier
self.components = components
}

public func appending(_ component: ChainComponent) -> Chain {
Chain(protocolModifier: protocolModifier, components + [component])
}
// some X
indirect case opaque(protocol: SwiftType)
// any X
indirect case existential(protocol: SwiftType)

public func appending(_ identifier: String, genericArgs: [SwiftType] = []) -> Chain {
appending(ChainComponent(identifier, genericArgs: genericArgs))
}
}

public struct ChainComponent {
public let identifier: SwiftIdentifier
public let genericArgs: [SwiftType]

public init(_ identifier: String, genericArgs: [SwiftType] = []) {
self.identifier = SwiftIdentifier(identifier)
self.genericArgs = genericArgs
}
}
// X?
indirect case `optional`(wrapped: SwiftType, implicitUnwrap: Bool = false)
// (X, Y)
indirect case tuple(elements: [SwiftType])
// [X]
indirect case array(element: SwiftType)
// [X: Y]
indirect case dictionary(key: SwiftType, value: SwiftType)
// (X, Y) throws -> Z
indirect case function(params: [SwiftType], throws: Bool = false, returnType: SwiftType)
}

extension SwiftType {
public static let void: SwiftType = .chain("Swift", "Void")
public static let anyObject: SwiftType = .chain("Swift", "AnyObject")
public static let never: SwiftType = .chain("Swift", "Never")
public static let bool: SwiftType = .chain("Swift", "Bool")
public static let float: SwiftType = .chain("Swift", "Float")
public static let double: SwiftType = .chain("Swift", "Double")
public static let string: SwiftType = .chain("Swift", "String")
public static let int: SwiftType = .chain("Swift", "Int")
public static let uint: SwiftType = .chain("Swift", "UInt")

public static func int(bits: Int, signed: Bool = true) -> SwiftType {
switch (bits, signed) {
case (8, true): return .chain("Swift", "Int8")
case (8, false): return .chain("Swift", "UInt8")
case (16, true): return .chain("Swift", "Int16")
case (16, false): return .chain("Swift", "UInt16")
case (32, true): return .chain("Swift", "Int32")
case (32, false): return .chain("Swift", "UInt32")
case (64, true): return .chain("Swift", "Int64")
case (64, false): return .chain("Swift", "UInt64")
default: preconditionFailure("bits should be one of 8, 16, 32 or 64")
}
public static func named(_ name: String, genericArgs: [SwiftType] = []) -> Self {
.named(SwiftIdentifier(name), genericArgs: genericArgs)
}

public static func uint(bits: Int) -> SwiftType { int(bits: bits, signed: false) }

public static func identifier(
protocolModifier: SwiftType.ProtocolModifier? = nil,
name: String,
genericArgs: [SwiftType] = []) -> SwiftType {
public func metatype() -> Self { .member(of: self, "Type") }

.chain(Chain(protocolModifier: protocolModifier, [ ChainComponent(name, genericArgs: genericArgs) ]))
public func member(_ name: String, genericArgs: [SwiftType] = []) -> Self {
member(SwiftIdentifier(name), genericArgs: genericArgs)
}

public static func identifier(_ name: String, genericArgs: [SwiftType] = []) -> SwiftType {
identifier(name: name, genericArgs: genericArgs)
public func member(_ name: SwiftIdentifier, genericArgs: [SwiftType] = []) -> Self {
.member(of: self, name, genericArgs: genericArgs)
}

public static func chain(
protocolModifier: SwiftType.ProtocolModifier? = nil,
_ components: [ChainComponent]) -> SwiftType {
public func opaque() -> Self { .opaque(protocol: self) }
public func existential() -> Self { .existential(protocol: self) }

.chain(Chain(protocolModifier: protocolModifier, components))
public func optional(implicitUnwrap: Bool = false) -> Self {
.optional(wrapped: self, implicitUnwrap: implicitUnwrap)
}

public static func chain(
protocolModifier: SwiftType.ProtocolModifier? = nil,
_ components: ChainComponent...) -> SwiftType {

.chain(protocolModifier: protocolModifier, components)
public func unwrapOptional() -> SwiftType {
switch self {
case let .optional(wrapped, _): return wrapped
default: return self
}
}
}

public static func chain(
protocolModifier: SwiftType.ProtocolModifier? = nil,
_ identifiers: [String]) -> SwiftType {
extension SwiftType {
private static let swiftModule: Self = .named("Swift")

.chain(Chain(protocolModifier: protocolModifier, identifiers.map { ChainComponent($0) }))
public static func swift(_ identifier: SwiftIdentifier, genericArgs: [SwiftType] = []) -> Self {
swiftModule.member(identifier, genericArgs: genericArgs)
}

public static func chain(
protocolModifier: SwiftType.ProtocolModifier? = nil,
_ identifiers: String...) -> SwiftType {

.chain(protocolModifier: protocolModifier, identifiers)
public static func swift(_ identifier: String, genericArgs: [SwiftType] = []) -> Self {
swiftModule.member(SwiftIdentifier(identifier), genericArgs: genericArgs)
}

public func unwrapOptional() -> SwiftType {
switch self {
case let .optional(wrapped, _): return wrapped
default: return self
public static let void: Self = swift("Void")
public static let anyObject: Self = swift("AnyObject")
public static let never: Self = swift("Never")
public static let bool: Self = swift("Bool")
public static let float: Self = swift("Float")
public static let double: Self = swift("Double")
public static let string: Self = swift("String")
public static let int: Self = swift("Int")
public static let uint: Self = swift("UInt")

public static func int(bits: Int, signed: Bool = true) -> Self {
switch (bits, signed) {
case (8, true): return swift("Int8")
case (8, false): return swift("UInt8")
case (16, true): return swift("Int16")
case (16, false): return swift("UInt16")
case (32, true): return swift("Int32")
case (32, false): return swift("UInt32")
case (64, true): return swift("Int64")
case (64, false): return swift("UInt64")
default: preconditionFailure("bits should be one of 8, 16, 32 or 64")
}
}

public static func unsafeMutablePointer(to pointee: SwiftType) -> SwiftType {
.chain(.init("Swift"), .init("UnsafeMutablePointer", genericArgs: [pointee]))
public static func uint(bits: Int) -> Self { int(bits: bits, signed: false) }

public static func unsafePointer(pointee: SwiftType) -> SwiftType {
swift("UnsafePointer", genericArgs: [pointee])
}

public static func unsafeMutablePointer(pointee: SwiftType) -> SwiftType {
swift("UnsafeMutablePointer", genericArgs: [pointee])
}
}
4 changes: 2 additions & 2 deletions Generator/Sources/ProjectionModel/Projection+abi.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ extension Projection {
return try toABIType(defaultInterface.asBoundType)
}

return .identifier(try CAbi.mangleName(type: type))
return .named(try CAbi.mangleName(type: type))
}

public func toABIVirtualTableType(_ type: BoundType) throws -> SwiftType {
precondition(type.definition is InterfaceDefinition || type.definition is DelegateDefinition)
return .identifier(try CAbi.mangleName(type: type) + CAbi.virtualTableSuffix)
return .named(try CAbi.mangleName(type: type) + CAbi.virtualTableSuffix)
}
}
4 changes: 2 additions & 2 deletions Generator/Sources/ProjectionModel/Projection+conversion.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ extension Projection {

public func toBaseProtocol(_ interface: InterfaceDefinition) throws -> SwiftType {
// Protocols have no generic arguments in base type lists
.identifier(name: try toProtocolName(interface))
.named(try toProtocolName(interface))
}

public func toBaseType(_ type: BoundType?) throws -> SwiftType? {
Expand All @@ -34,7 +34,7 @@ extension Projection {
return try toBaseProtocol(interfaceDefinition)
}
else {
return .identifier(name: try toTypeName(type.definition))
return .named(try toTypeName(type.definition))
}
}

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 @@ -67,7 +67,7 @@ extension Projection {
returnBinding = ParamProjection(
name: "_result",
typeProjection: TypeProjection(
abiType: .optional(wrapped: .unsafeMutablePointer(to: abiType)),
abiType: .unsafeMutablePointer(pointee: abiType).optional(),
abiDefaultValue: .`nil`,
swiftType: SupportModules.COM.comReference(to: abiType),
swiftDefaultValue: .`nil`, // No projection needed
Expand Down
Loading
Loading