Skip to content

Commit 9c613be

Browse files
committed
[JExtract] Lower generic parameters conforming to some protocols
1 parent d8e0106 commit 9c613be

File tree

4 files changed

+170
-45
lines changed

4 files changed

+170
-45
lines changed

Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift

Lines changed: 64 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -98,18 +98,22 @@ struct CdeclLowering {
9898
try lowerParameter(
9999
selfParameter.type,
100100
convention: selfParameter.convention,
101-
parameterName: selfParameter.parameterName ?? "self"
101+
parameterName: selfParameter.parameterName ?? "self",
102+
genericParameters: signature.genericParameters,
103+
genericRequirements: signature.genericRequirements
102104
)
103105
case nil, .initializer(_), .staticMethod(_):
104106
nil
105107
}
106108

107109
// Lower all of the parameters.
108110
let loweredParameters = try signature.parameters.enumerated().map { (index, param) in
109-
try lowerParameter(
111+
return try lowerParameter(
110112
param.type,
111113
convention: param.convention,
112-
parameterName: param.parameterName ?? "_\(index)"
114+
parameterName: param.parameterName ?? "_\(index)",
115+
genericParameters: signature.genericParameters,
116+
genericRequirements: signature.genericRequirements
113117
)
114118
}
115119

@@ -142,7 +146,9 @@ struct CdeclLowering {
142146
func lowerParameter(
143147
_ type: SwiftType,
144148
convention: SwiftParameterConvention,
145-
parameterName: String
149+
parameterName: String,
150+
genericParameters: [SwiftGenericParameterDeclaration],
151+
genericRequirements: [SwiftGenericRequirement]
146152
) throws -> LoweredParameter {
147153
// If there is a 1:1 mapping between this Swift type and a C type, we just
148154
// return it.
@@ -257,7 +263,7 @@ struct CdeclLowering {
257263
guard let genericArgs = nominal.genericArguments, genericArgs.count == 1 else {
258264
throw LoweringError.unhandledType(type)
259265
}
260-
return try lowerOptionalParameter(genericArgs[0], convention: convention, parameterName: parameterName)
266+
return try lowerOptionalParameter(genericArgs[0], convention: convention, parameterName: parameterName, genericParameters: genericParameters, genericRequirements: genericRequirements)
261267

262268
case .string:
263269
// 'String' is passed in by C string. i.e. 'UnsafePointer<Int8>' ('const uint8_t *')
@@ -300,7 +306,7 @@ struct CdeclLowering {
300306

301307
case .tuple(let tuple):
302308
if tuple.count == 1 {
303-
return try lowerParameter(tuple[0], convention: convention, parameterName: parameterName)
309+
return try lowerParameter(tuple[0], convention: convention, parameterName: parameterName, genericParameters: genericParameters, genericRequirements: genericRequirements)
304310
}
305311
if convention == .inout {
306312
throw LoweringError.inoutNotSupported(type)
@@ -310,7 +316,7 @@ struct CdeclLowering {
310316
for (idx, element) in tuple.enumerated() {
311317
// FIXME: Use tuple element label.
312318
let cdeclName = "\(parameterName)_\(idx)"
313-
let lowered = try lowerParameter(element, convention: convention, parameterName: cdeclName)
319+
let lowered = try lowerParameter(element, convention: convention, parameterName: cdeclName, genericParameters: genericParameters, genericRequirements: genericRequirements)
314320

315321
parameters.append(contentsOf: lowered.cdeclParameters)
316322
conversions.append(lowered.conversion)
@@ -330,23 +336,14 @@ struct CdeclLowering {
330336
conversion: conversion
331337
)
332338

333-
case .opaque(let proto), .existential(let proto):
334-
// If the protocol has a known representative implementation, e.g. `String` for `StringProtocol`
335-
// Translate it as the concrete type.
336-
// NOTE: This is a temporary workaround until we add support for generics.
337-
if
338-
let knownProtocol = proto.asNominalTypeDeclaration?.knownTypeKind,
339-
let concreteTy = knownTypes.representativeType(of: knownProtocol)
340-
{
341-
return try lowerParameter(concreteTy, convention: convention, parameterName: parameterName)
339+
case .opaque, .existential, .genericParameter:
340+
if let concreteTy = representativeConcreteType(type, genericParameters: genericParameters, genericRequirements: genericRequirements) {
341+
return try lowerParameter(concreteTy, convention: convention, parameterName: parameterName, genericParameters: genericParameters, genericRequirements: genericRequirements)
342342
}
343343
throw LoweringError.unhandledType(type)
344344

345345
case .optional(let wrapped):
346-
return try lowerOptionalParameter(wrapped, convention: convention, parameterName: parameterName)
347-
348-
case .genericParameter:
349-
throw LoweringError.unhandledType(type)
346+
return try lowerOptionalParameter(wrapped, convention: convention, parameterName: parameterName, genericParameters: genericParameters, genericRequirements: genericRequirements)
350347
}
351348
}
352349

@@ -357,7 +354,9 @@ struct CdeclLowering {
357354
func lowerOptionalParameter(
358355
_ wrappedType: SwiftType,
359356
convention: SwiftParameterConvention,
360-
parameterName: String
357+
parameterName: String,
358+
genericParameters: [SwiftGenericParameterDeclaration],
359+
genericRequirements: [SwiftGenericRequirement]
361360
) throws -> LoweredParameter {
362361
// If there is a 1:1 mapping between this Swift type and a C type, lower it to 'UnsafePointer<T>?'
363362
if let _ = try? CType(cdeclType: wrappedType) {
@@ -401,26 +400,59 @@ struct CdeclLowering {
401400
conversion: .pointee(.typedPointer(.optionalChain(.placeholder), swiftType: wrappedType))
402401
)
403402

404-
case .existential(let proto), .opaque(let proto):
405-
if
406-
let knownProtocol = proto.asNominalTypeDeclaration?.knownTypeKind,
407-
let concreteTy = knownTypes.representativeType(of: knownProtocol)
408-
{
409-
return try lowerOptionalParameter(concreteTy, convention: convention, parameterName: parameterName)
403+
case .existential, .opaque, .genericParameter:
404+
if let concreteTy = representativeConcreteType(wrappedType, genericParameters: genericParameters, genericRequirements: genericRequirements) {
405+
return try lowerOptionalParameter(concreteTy, convention: convention, parameterName: parameterName, genericParameters: genericParameters, genericRequirements: genericRequirements)
410406
}
411407
throw LoweringError.unhandledType(.optional(wrappedType))
412408

413409
case .tuple(let tuple):
414410
if tuple.count == 1 {
415-
return try lowerOptionalParameter(tuple[0], convention: convention, parameterName: parameterName)
411+
return try lowerOptionalParameter(tuple[0], convention: convention, parameterName: parameterName, genericParameters: genericParameters, genericRequirements: genericRequirements)
416412
}
417413
throw LoweringError.unhandledType(.optional(wrappedType))
418414

419-
case .function, .metatype, .optional, .genericParameter:
415+
case .function, .metatype, .optional:
420416
throw LoweringError.unhandledType(.optional(wrappedType))
421417
}
422418
}
423419

420+
/// Returns a concrete type if the specified type is a generic parameter that
421+
/// conforms to a protocol with representative concrete type.
422+
func representativeConcreteType(
423+
_ type: SwiftType,
424+
genericParameters: [SwiftGenericParameterDeclaration],
425+
genericRequirements: [SwiftGenericRequirement]
426+
) -> SwiftType? {
427+
var maybeProto: SwiftType? = nil
428+
switch type {
429+
case .existential(let proto), .opaque(let proto):
430+
maybeProto = proto
431+
case .genericParameter(let genericParam):
432+
// If the type is a generic parameter declared in this function and
433+
// conforms to a protocol with representative concrete type, use it.
434+
if genericParameters.contains(genericParam) {
435+
for requirement in genericRequirements {
436+
if case .inherits(let left, let right) = requirement, left == type {
437+
guard maybeProto == nil else {
438+
// multiple requirements on the generic parameter.
439+
return nil
440+
}
441+
maybeProto = right
442+
break
443+
}
444+
}
445+
}
446+
default:
447+
return nil
448+
}
449+
450+
if let knownProtocol = maybeProto?.asNominalTypeDeclaration?.knownTypeKind {
451+
return knownTypes.representativeType(of: knownProtocol)
452+
}
453+
return nil
454+
}
455+
424456
/// Lower a Swift function type (i.e. closure) to cdecl function type.
425457
///
426458
/// - Parameters:
@@ -757,7 +789,9 @@ public struct LoweredFunctionSignature: Equatable {
757789
selfParameter: nil,
758790
parameters: allLoweredParameters,
759791
result: SwiftResult(convention: .direct, type: result.cdeclResultType),
760-
effectSpecifiers: []
792+
effectSpecifiers: [],
793+
genericParameters: [],
794+
genericRequirements: []
761795
)
762796
}
763797
}

Sources/JExtractSwiftLib/SwiftTypes/SwiftFunctionSignature.swift

Lines changed: 84 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,24 +24,26 @@ enum SwiftGenericRequirement: Equatable {
2424
/// parameters and return type.
2525
public struct SwiftFunctionSignature: Equatable {
2626
var selfParameter: SwiftSelfParameter?
27-
var genericParameters: [SwiftTypeDeclaration]
2827
var parameters: [SwiftParameter]
2928
var result: SwiftResult
3029
var effectSpecifiers: [SwiftEffectSpecifier]
30+
var genericParameters: [SwiftGenericParameterDeclaration]
3131
var genericRequirements: [SwiftGenericRequirement]
3232

3333
init(
3434
selfParameter: SwiftSelfParameter? = nil,
3535
parameters: [SwiftParameter],
3636
result: SwiftResult,
37-
effectSpecifiers: [SwiftEffectSpecifier]
37+
effectSpecifiers: [SwiftEffectSpecifier],
38+
genericParameters: [SwiftGenericParameterDeclaration],
39+
genericRequirements: [SwiftGenericRequirement]
3840
) {
3941
self.selfParameter = selfParameter
4042
self.parameters = parameters
4143
self.result = result
4244
self.effectSpecifiers = effectSpecifiers
43-
self.genericParameters = []
44-
self.genericRequirements = []
45+
self.genericParameters = genericParameters
46+
self.genericRequirements = genericRequirements
4547
}
4648
}
4749

@@ -79,11 +81,11 @@ extension SwiftFunctionSignature {
7981
throw SwiftFunctionTranslationError.failableInitializer(node)
8082
}
8183

82-
// Prohibit generics for now.
83-
if let generics = node.genericParameterClause {
84-
throw SwiftFunctionTranslationError.generic(generics)
85-
}
86-
84+
let (genericParams, genericRequirements) = try Self.translateGenericParameters(
85+
parameterClause: node.genericParameterClause,
86+
whereClause: node.genericWhereClause,
87+
lookupContext: lookupContext
88+
)
8789
let (parameters, effectSpecifiers) = try Self.translateFunctionSignature(
8890
node.signature,
8991
lookupContext: lookupContext
@@ -93,7 +95,9 @@ extension SwiftFunctionSignature {
9395
selfParameter: .initializer(enclosingType),
9496
parameters: parameters,
9597
result: SwiftResult(convention: .direct, type: enclosingType),
96-
effectSpecifiers: effectSpecifiers
98+
effectSpecifiers: effectSpecifiers,
99+
genericParameters: genericParams,
100+
genericRequirements: genericRequirements
97101
)
98102
}
99103

@@ -103,9 +107,11 @@ extension SwiftFunctionSignature {
103107
lookupContext: SwiftTypeLookupContext
104108
) throws {
105109
// Prohibit generics for now.
106-
if let generics = node.genericParameterClause {
107-
throw SwiftFunctionTranslationError.generic(generics)
108-
}
110+
let (genericParams, genericRequirements) = try Self.translateGenericParameters(
111+
parameterClause: node.genericParameterClause,
112+
whereClause: node.genericWhereClause,
113+
lookupContext: lookupContext,
114+
)
109115

110116
// If this is a member of a type, so we will have a self parameter. Figure out the
111117
// type and convention for the self parameter.
@@ -155,7 +161,68 @@ extension SwiftFunctionSignature {
155161
result = .void
156162
}
157163

158-
self.init(selfParameter: selfParameter, parameters: parameters, result: result, effectSpecifiers: effectSpecifiers)
164+
self.init(
165+
selfParameter: selfParameter,
166+
parameters: parameters,
167+
result: result,
168+
effectSpecifiers: effectSpecifiers,
169+
genericParameters: genericParams,
170+
genericRequirements: genericRequirements
171+
)
172+
}
173+
174+
static func translateGenericParameters(
175+
parameterClause: GenericParameterClauseSyntax?,
176+
whereClause: GenericWhereClauseSyntax?,
177+
lookupContext: SwiftTypeLookupContext,
178+
) throws -> (parameters: [SwiftGenericParameterDeclaration], requirements: [SwiftGenericRequirement]) {
179+
var params: [SwiftGenericParameterDeclaration] = []
180+
var requirements: [SwiftGenericRequirement] = []
181+
182+
// Parameter clause
183+
if let parameterClause {
184+
for parameterNode in parameterClause.parameters {
185+
guard parameterNode.specifier == nil else {
186+
throw SwiftFunctionTranslationError.genericParameterSpecifier(parameterNode)
187+
}
188+
let param = try lookupContext.typeDeclaration(for: parameterNode) as! SwiftGenericParameterDeclaration
189+
params.append(param)
190+
if let inheritedNode = parameterNode.inheritedType {
191+
let inherited = try SwiftType(inheritedNode, lookupContext: lookupContext)
192+
requirements.append(.inherits(.genericParameter(param), inherited))
193+
}
194+
}
195+
}
196+
197+
// Where clause
198+
if let whereClause {
199+
for requirementNode in whereClause.requirements {
200+
let requirement: SwiftGenericRequirement
201+
switch requirementNode.requirement {
202+
case .conformanceRequirement(let conformance):
203+
requirement = .inherits(
204+
try SwiftType(conformance.leftType, lookupContext: lookupContext),
205+
try SwiftType(conformance.rightType, lookupContext: lookupContext)
206+
)
207+
case .sameTypeRequirement(let sameType):
208+
guard let leftType = sameType.leftType.as(TypeSyntax.self) else {
209+
throw SwiftFunctionTranslationError.expressionInGenericRequirement(requirementNode)
210+
}
211+
guard let rightType = sameType.rightType.as(TypeSyntax.self) else {
212+
throw SwiftFunctionTranslationError.expressionInGenericRequirement(requirementNode)
213+
}
214+
requirement = .equals(
215+
try SwiftType(leftType, lookupContext: lookupContext),
216+
try SwiftType(rightType, lookupContext: lookupContext)
217+
)
218+
case .layoutRequirement:
219+
throw SwiftFunctionTranslationError.layoutRequirement(requirementNode)
220+
}
221+
requirements.append(requirement)
222+
}
223+
}
224+
225+
return (params, requirements)
159226
}
160227

161228
/// Translate the function signature, returning the list of translated
@@ -310,4 +377,7 @@ enum SwiftFunctionTranslationError: Error {
310377
case multipleBindings(VariableDeclSyntax)
311378
case missingTypeAnnotation(VariableDeclSyntax)
312379
case unsupportedAccessor(AccessorDeclSyntax)
380+
case genericParameterSpecifier(GenericParameterSyntax)
381+
case expressionInGenericRequirement(GenericRequirementSyntax)
382+
case layoutRequirement(GenericRequirementSyntax)
313383
}

Sources/JExtractSwiftLib/SwiftTypes/SwiftType.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ extension SwiftType {
333333
self = .nominal(
334334
SwiftNominalType(
335335
parent: parent?.asNominalType,
336-
nominalTypeDecl: nominalTypeDecl as! SwiftNominalTypeDeclaration,
336+
nominalTypeDecl: nominalTypeDecl,
337337
genericArguments: nil
338338
)
339339
)

Tests/JExtractSwiftTests/FunctionLoweringTests.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,27 @@ final class FunctionLoweringTests {
404404
""")
405405
}
406406

407+
@Test("Lowering generic parameters")
408+
func genericParam() throws {
409+
try assertLoweredFunction(
410+
"""
411+
func fn<T, U: DataProtocol>(x: T, y: U?) where T: DataProtocol
412+
""",
413+
sourceFile: """
414+
import Foundation
415+
""",
416+
expectedCDecl: """
417+
@_cdecl("c_fn")
418+
public func c_fn(_ x: UnsafeRawPointer, _ y: UnsafeRawPointer?) {
419+
fn(x: x.assumingMemoryBound(to: Data.self).pointee, y: y?.assumingMemoryBound(to: Data.self).pointee)
420+
}
421+
""",
422+
expectedCFunction: """
423+
void c_fn(const void *x, const void *y)
424+
"""
425+
)
426+
}
427+
407428
@Test("Lowering read accessor")
408429
func lowerGlobalReadAccessor() throws {
409430
try assertLoweredVariableAccessor(

0 commit comments

Comments
 (0)