Skip to content

Commit 820632d

Browse files
committed
[JExtract] Translate generic parameters
1 parent 9c613be commit 820632d

File tree

5 files changed

+132
-85
lines changed

5 files changed

+132
-85
lines changed

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

Lines changed: 2 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ struct CdeclLowering {
337337
)
338338

339339
case .opaque, .existential, .genericParameter:
340-
if let concreteTy = representativeConcreteType(type, genericParameters: genericParameters, genericRequirements: genericRequirements) {
340+
if let concreteTy = type.representativeConcreteTypeIn(knownTypes: knownTypes, genericParameters: genericParameters, genericRequirements: genericRequirements) {
341341
return try lowerParameter(concreteTy, convention: convention, parameterName: parameterName, genericParameters: genericParameters, genericRequirements: genericRequirements)
342342
}
343343
throw LoweringError.unhandledType(type)
@@ -401,7 +401,7 @@ struct CdeclLowering {
401401
)
402402

403403
case .existential, .opaque, .genericParameter:
404-
if let concreteTy = representativeConcreteType(wrappedType, genericParameters: genericParameters, genericRequirements: genericRequirements) {
404+
if let concreteTy = wrappedType.representativeConcreteTypeIn(knownTypes: knownTypes, genericParameters: genericParameters, genericRequirements: genericRequirements) {
405405
return try lowerOptionalParameter(concreteTy, convention: convention, parameterName: parameterName, genericParameters: genericParameters, genericRequirements: genericRequirements)
406406
}
407407
throw LoweringError.unhandledType(.optional(wrappedType))
@@ -417,42 +417,6 @@ struct CdeclLowering {
417417
}
418418
}
419419

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-
456420
/// Lower a Swift function type (i.e. closure) to cdecl function type.
457421
///
458422
/// - Parameters:

Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift

Lines changed: 50 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,9 @@ extension FFMSwift2JavaGenerator {
256256
convention: swiftSelf.convention,
257257
parameterName: swiftSelf.parameterName ?? "self",
258258
loweredParam: loweredFunctionSignature.selfParameter!,
259-
methodName: methodName
259+
methodName: methodName,
260+
genericParameters: swiftSignature.genericParameters,
261+
genericRequirements: swiftSignature.genericRequirements
260262
)
261263
} else {
262264
selfParameter = nil
@@ -272,7 +274,9 @@ extension FFMSwift2JavaGenerator {
272274
convention: swiftParam.convention,
273275
parameterName: parameterName,
274276
loweredParam: loweredParam,
275-
methodName: methodName
277+
methodName: methodName,
278+
genericParameters: swiftSignature.genericParameters,
279+
genericRequirements: swiftSignature.genericRequirements
276280
)
277281
}
278282

@@ -295,7 +299,9 @@ extension FFMSwift2JavaGenerator {
295299
convention: SwiftParameterConvention,
296300
parameterName: String,
297301
loweredParam: LoweredParameter,
298-
methodName: String
302+
methodName: String,
303+
genericParameters: [SwiftGenericParameterDeclaration],
304+
genericRequirements: [SwiftGenericRequirement]
299305
) throws -> TranslatedParameter {
300306

301307
// If there is a 1:1 mapping between this Swift type and a C type, that can
@@ -352,7 +358,15 @@ extension FFMSwift2JavaGenerator {
352358
guard let genericArgs = swiftNominalType.genericArguments, genericArgs.count == 1 else {
353359
throw JavaTranslationError.unhandledType(swiftType)
354360
}
355-
return try translateOptionalParameter(wrappedType: genericArgs[0], convention: convention, parameterName: parameterName, loweredParam: loweredParam, methodName: methodName)
361+
return try translateOptionalParameter(
362+
wrappedType: genericArgs[0],
363+
convention: convention,
364+
parameterName: parameterName,
365+
loweredParam: loweredParam,
366+
methodName: methodName,
367+
genericParameters: genericParameters,
368+
genericRequirements: genericRequirements
369+
)
356370

357371
case .string:
358372
return TranslatedParameter(
@@ -399,33 +413,32 @@ extension FFMSwift2JavaGenerator {
399413
conversion: .call(.placeholder, function: "\(methodName).$toUpcallStub", withArena: true)
400414
)
401415

402-
case .existential(let proto), .opaque(let proto):
403-
// If the protocol has a known representative implementation, e.g. `String` for `StringProtocol`
404-
// Translate it as the concrete type.
405-
// NOTE: This is a temporary workaround until we add support for generics.
406-
if
407-
let knownProtocol = proto.asNominalTypeDeclaration?.knownTypeKind,
408-
let concreteTy = knownTypes.representativeType(of: knownProtocol)
409-
{
416+
case .existential, .opaque, .genericParameter:
417+
if let concreteTy = swiftType.representativeConcreteTypeIn(knownTypes: knownTypes, genericParameters: genericParameters, genericRequirements: genericRequirements) {
410418
return try translateParameter(
411419
type: concreteTy,
412420
convention: convention,
413421
parameterName: parameterName,
414422
loweredParam: loweredParam,
415-
methodName: methodName
423+
methodName: methodName,
424+
genericParameters: genericParameters,
425+
genericRequirements: genericRequirements
416426
)
417427
}
418428

419429
// Otherwise, not supported yet.
420430
throw JavaTranslationError.unhandledType(swiftType)
421431

422432
case .optional(let wrapped):
423-
return try translateOptionalParameter(wrappedType: wrapped, convention: convention, parameterName: parameterName, loweredParam: loweredParam, methodName: methodName)
424-
425-
case .genericParameter:
426-
// Otherwise, not supported yet.
427-
throw JavaTranslationError.unhandledType(swiftType)
428-
433+
return try translateOptionalParameter(
434+
wrappedType: wrapped,
435+
convention: convention,
436+
parameterName: parameterName,
437+
loweredParam: loweredParam,
438+
methodName: methodName,
439+
genericParameters: genericParameters,
440+
genericRequirements: genericRequirements
441+
)
429442
}
430443
}
431444

@@ -435,12 +448,14 @@ extension FFMSwift2JavaGenerator {
435448
convention: SwiftParameterConvention,
436449
parameterName: String,
437450
loweredParam: LoweredParameter,
438-
methodName: String
451+
methodName: String,
452+
genericParameters: [SwiftGenericParameterDeclaration],
453+
genericRequirements: [SwiftGenericRequirement]
439454
) throws -> TranslatedParameter {
440455
// If there is a 1:1 mapping between this Swift type and a C type, that can
441456
// be expressed as a Java primitive type.
442457
if let cType = try? CType(cdeclType: swiftType) {
443-
var (translatedClass, lowerFunc) = switch cType.javaType {
458+
let (translatedClass, lowerFunc) = switch cType.javaType {
444459
case .int: ("OptionalInt", "toOptionalSegmentInt")
445460
case .long: ("OptionalLong", "toOptionalSegmentLong")
446461
case .double: ("OptionalDouble", "toOptionalSegmentDouble")
@@ -478,23 +493,30 @@ extension FFMSwift2JavaGenerator {
478493
],
479494
conversion: .call(.placeholder, function: "SwiftRuntime.toOptionalSegmentInstance", withArena: false)
480495
)
481-
case .existential(let proto), .opaque(let proto):
482-
if
483-
let knownProtocol = proto.asNominalTypeDeclaration?.knownTypeKind,
484-
let concreteTy = knownTypes.representativeType(of: knownProtocol)
485-
{
496+
case .existential, .opaque, .genericParameter:
497+
if let concreteTy = swiftType.representativeConcreteTypeIn(knownTypes: knownTypes, genericParameters: genericParameters, genericRequirements: genericRequirements) {
486498
return try translateOptionalParameter(
487499
wrappedType: concreteTy,
488500
convention: convention,
489501
parameterName: parameterName,
490502
loweredParam: loweredParam,
491-
methodName: methodName
503+
methodName: methodName,
504+
genericParameters: genericParameters,
505+
genericRequirements: genericRequirements
492506
)
493507
}
494508
throw JavaTranslationError.unhandledType(.optional(swiftType))
495509
case .tuple(let tuple):
496510
if tuple.count == 1 {
497-
return try translateOptionalParameter(wrappedType: tuple[0], convention: convention, parameterName: parameterName, loweredParam: loweredParam, methodName: methodName)
511+
return try translateOptionalParameter(
512+
wrappedType: tuple[0],
513+
convention: convention,
514+
parameterName: parameterName,
515+
loweredParam: loweredParam,
516+
methodName: methodName,
517+
genericParameters: genericParameters,
518+
genericRequirements: genericRequirements
519+
)
498520
}
499521
throw JavaTranslationError.unhandledType(.optional(swiftType))
500522
default:
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2025 Apple Inc. and the Swift.org project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of Swift.org project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
extension SwiftType {
16+
/// Returns a concrete type if this is a generic parameter in the list and it
17+
/// conforms to a protocol with representative concrete type.
18+
func representativeConcreteTypeIn(
19+
knownTypes: SwiftKnownTypes,
20+
genericParameters: [SwiftGenericParameterDeclaration],
21+
genericRequirements: [SwiftGenericRequirement]
22+
) -> SwiftType? {
23+
return representativeConcreteType(
24+
self,
25+
knownTypes: knownTypes,
26+
genericParameters: genericParameters,
27+
genericRequirements: genericRequirements
28+
)
29+
}
30+
}
31+
32+
private func representativeConcreteType(
33+
_ type: SwiftType,
34+
knownTypes: SwiftKnownTypes,
35+
genericParameters: [SwiftGenericParameterDeclaration],
36+
genericRequirements: [SwiftGenericRequirement]
37+
) -> SwiftType? {
38+
var maybeProto: SwiftType? = nil
39+
switch type {
40+
case .existential(let proto), .opaque(let proto):
41+
maybeProto = proto
42+
case .genericParameter(let genericParam):
43+
// If the type is a generic parameter declared in this function and
44+
// conforms to a protocol with representative concrete type, use it.
45+
if genericParameters.contains(genericParam) {
46+
for requirement in genericRequirements {
47+
if case .inherits(let left, let right) = requirement, left == type {
48+
guard maybeProto == nil else {
49+
// multiple requirements on the generic parameter.
50+
return nil
51+
}
52+
maybeProto = right
53+
break
54+
}
55+
}
56+
}
57+
default:
58+
return nil
59+
}
60+
61+
if let knownProtocol = maybeProto?.asNominalTypeDeclaration?.knownTypeKind {
62+
return knownTypes.representativeType(of: knownProtocol)
63+
}
64+
return nil
65+
}

Sources/JExtractSwiftLib/SwiftTypes/SwiftType.swift

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -360,8 +360,3 @@ enum TypeTranslationError: Error {
360360
/// Unknown nominal type.
361361
case unknown(TypeSyntax, file: StaticString = #file, line: Int = #line)
362362
}
363-
364-
365-
func foo(x: Syntax) {
366-
x.lookup(Identifier(x.firstToken(viewMode: .fixedUp)!))
367-
}

Tests/JExtractSwiftTests/DataImportTests.swift

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ final class DataImportTests {
2828
"""
2929
import Foundation
3030
31-
public func receiveDataProtocol(dat: some DataProtocol)
31+
public func receiveDataProtocol<T: DataProtocol>(dat: some DataProtocol, dat2: T?)
3232
"""
3333

3434

@@ -342,9 +342,9 @@ final class DataImportTests {
342342
import Foundation
343343
""",
344344
"""
345-
@_cdecl("swiftjava_SwiftModule_receiveDataProtocol_dat")
346-
public func swiftjava_SwiftModule_receiveDataProtocol_dat(_ dat: UnsafeRawPointer) {
347-
receiveDataProtocol(dat: dat.assumingMemoryBound(to: Data.self).pointee)
345+
@_cdecl("swiftjava_SwiftModule_receiveDataProtocol_dat_dat2")
346+
public func swiftjava_SwiftModule_receiveDataProtocol_dat_dat2(_ dat: UnsafeRawPointer, _ dat2: UnsafeRawPointer?) {
347+
receiveDataProtocol(dat: dat.assumingMemoryBound(to: Data.self).pointee, dat2: dat2?.assumingMemoryBound(to: Data.self).pointee)
348348
}
349349
""",
350350

@@ -365,22 +365,23 @@ final class DataImportTests {
365365
"""
366366
/**
367367
* {@snippet lang=c :
368-
* void swiftjava_SwiftModule_receiveDataProtocol_dat(const void *dat)
368+
* void swiftjava_SwiftModule_receiveDataProtocol_dat_dat2(const void *dat, const void *dat2)
369369
* }
370370
*/
371-
private static class swiftjava_SwiftModule_receiveDataProtocol_dat {
371+
private static class swiftjava_SwiftModule_receiveDataProtocol_dat_dat2 {
372372
private static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid(
373-
/* dat: */SwiftValueLayout.SWIFT_POINTER
373+
/* dat: */SwiftValueLayout.SWIFT_POINTER,
374+
/* dat2: */SwiftValueLayout.SWIFT_POINTER
374375
);
375376
private static final MemorySegment ADDR =
376-
SwiftModule.findOrThrow("swiftjava_SwiftModule_receiveDataProtocol_dat");
377+
SwiftModule.findOrThrow("swiftjava_SwiftModule_receiveDataProtocol_dat_dat2");
377378
private static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC);
378-
public static void call(java.lang.foreign.MemorySegment dat) {
379+
public static void call(java.lang.foreign.MemorySegment dat, java.lang.foreign.MemorySegment dat2) {
379380
try {
380381
if (CallTraces.TRACE_DOWNCALLS) {
381-
CallTraces.traceDowncall(dat);
382+
CallTraces.traceDowncall(dat, dat2);
382383
}
383-
HANDLE.invokeExact(dat);
384+
HANDLE.invokeExact(dat, dat2);
384385
} catch (Throwable ex$) {
385386
throw new AssertionError("should not reach here", ex$);
386387
}
@@ -392,11 +393,11 @@ final class DataImportTests {
392393
/**
393394
* Downcall to Swift:
394395
* {@snippet lang=swift :
395-
* public func receiveDataProtocol(dat: some DataProtocol)
396+
* public func receiveDataProtocol<T: DataProtocol>(dat: some DataProtocol, dat2: T?)
396397
* }
397398
*/
398-
public static void receiveDataProtocol(Data dat) {
399-
swiftjava_SwiftModule_receiveDataProtocol_dat.call(dat.$memorySegment());
399+
public static void receiveDataProtocol(Data dat, Optional<Data> dat2) {
400+
swiftjava_SwiftModule_receiveDataProtocol_dat_dat2.call(dat.$memorySegment(), SwiftRuntime.toOptionalSegmentInstance(dat2));
400401
}
401402
""",
402403

0 commit comments

Comments
 (0)