From d54122c9614a92008bc10516b48e19d51c855005 Mon Sep 17 00:00:00 2001 From: Iceman Date: Fri, 15 May 2026 16:06:41 +0900 Subject: [PATCH 1/3] Avoid compile error for generic enum with its generic associated value --- .../Sources/MySwiftLibrary/GenericType.swift | 5 ++ ...t2JavaGenerator+JavaBindingsPrinting.swift | 53 ++++++++++++------- .../JNI/JNIGenericTypeTests.swift | 37 +++++++++++++ 3 files changed, 76 insertions(+), 19 deletions(-) diff --git a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift index 420afda5..d3a0ff8b 100644 --- a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift +++ b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift @@ -88,6 +88,11 @@ public func makeIntGenericEnum() -> GenericEnum { if Bool.random() { return .foo } else { return .bar } } +public enum GenericEnumWithValue { + case some(T) + case none +} + extension MyID where T: BinaryInteger { // Conditional extension functions are not exported public func computeSomeValue() -> Int { diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift index e9f13626..d46b748d 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift @@ -504,25 +504,40 @@ extension JNISwift2JavaGenerator { } printer.println() - let requiresSwiftArena = decl.cases.compactMap { - self.translatedEnumCase(for: $0) - }.contains(where: \.requiresSwiftArena) - - printer.printBraceBlock("public Case getCase(\(requiresSwiftArena ? "SwiftArena swiftArena" : ""))") { printer in - printer.printBraceBlock("return switch (this.getDiscriminator())", .semicolonNewLine) { printer in - for enumCase in decl.cases { - guard let translatedCase = self.translatedEnumCase(for: enumCase) else { - continue - } - if enumCase.parameters.isEmpty { - printer.print( - "case \(enumCase.name.uppercased()) -> new Case.\(translatedCase.name)();" - ) - } else { - let arenaArgument = translatedCase.requiresSwiftArena ? "swiftArena" : "" - printer.print( - "case \(enumCase.name.uppercased()) -> this.getAs\(translatedCase.name)(\(arenaArgument)).orElseThrow();" - ) + // Ensure all cases can be generated to avoid inconsistency with the discriminator. + let allCasesCanBeTranslated = decl.cases.allSatisfy({ + translatedEnumCase(for: $0) != nil + }) + if !allCasesCanBeTranslated { + printer.print( + """ + // This is unavailable because it contains unsupported cases. + private Case getCase() { + return null; + } + """ + ) + } else { + let requiresSwiftArena = decl.cases.compactMap { + self.translatedEnumCase(for: $0) + }.contains(where: \.requiresSwiftArena) + + printer.printBraceBlock("public Case getCase(\(requiresSwiftArena ? "SwiftArena swiftArena" : ""))") { printer in + printer.printBraceBlock("return switch (this.getDiscriminator())", .semicolonNewLine) { printer in + for enumCase in decl.cases { + guard let translatedCase = self.translatedEnumCase(for: enumCase) else { + continue + } + if enumCase.parameters.isEmpty { + printer.print( + "case \(enumCase.name.uppercased()) -> new Case.\(translatedCase.name)();" + ) + } else { + let arenaArgument = translatedCase.requiresSwiftArena ? "swiftArena" : "" + printer.print( + "case \(enumCase.name.uppercased()) -> this.getAs\(translatedCase.name)(\(arenaArgument)).orElseThrow();" + ) + } } } } diff --git a/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift b/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift index 5f82b3bd..5ccc68b4 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift @@ -252,6 +252,43 @@ struct JNIGenericTypeTests { ) } + @Test + func genericEnumWithAssociatedValue() throws { + let input = + #""" + public enum MyOptional { + case some(Wrapped) + case none + } + """# + + try assertOutput( + input: input, + .jni, + .java, + detectChunkByInitialLines: 2, + expectedChunks: [ + """ + public enum Discriminator { + SOME, + NONE + } + """, + """ + public sealed interface Case { + record None() implements Case {} + } + """, + """ + // This is unavailable because it contains unsupported cases. + private Case getCase() { + return null; + } + """, + ] + ) + } + @Test func nestedGenericType() throws { let input = From d9c1d143f6a1adb3839676a629e73b8fba3340f1 Mon Sep 17 00:00:00 2001 From: Iceman Date: Mon, 18 May 2026 16:30:47 +0900 Subject: [PATCH 2/3] Fix to throw an error instead of hiding whole getCase method. --- ...t2JavaGenerator+JavaBindingsPrinting.swift | 36 +++++++------------ .../JNI/JNIGenericTypeTests.swift | 8 +++-- 2 files changed, 17 insertions(+), 27 deletions(-) diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift index d46b748d..5a13fa9f 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift @@ -504,30 +504,14 @@ extension JNISwift2JavaGenerator { } printer.println() - // Ensure all cases can be generated to avoid inconsistency with the discriminator. - let allCasesCanBeTranslated = decl.cases.allSatisfy({ - translatedEnumCase(for: $0) != nil - }) - if !allCasesCanBeTranslated { - printer.print( - """ - // This is unavailable because it contains unsupported cases. - private Case getCase() { - return null; - } - """ - ) - } else { - let requiresSwiftArena = decl.cases.compactMap { - self.translatedEnumCase(for: $0) - }.contains(where: \.requiresSwiftArena) - - printer.printBraceBlock("public Case getCase(\(requiresSwiftArena ? "SwiftArena swiftArena" : ""))") { printer in - printer.printBraceBlock("return switch (this.getDiscriminator())", .semicolonNewLine) { printer in - for enumCase in decl.cases { - guard let translatedCase = self.translatedEnumCase(for: enumCase) else { - continue - } + let requiresSwiftArena = decl.cases.compactMap { + self.translatedEnumCase(for: $0) + }.contains(where: \.requiresSwiftArena) + + printer.printBraceBlock("public Case getCase(\(requiresSwiftArena ? "SwiftArena swiftArena" : ""))") { printer in + printer.printBraceBlock("return switch (this.getDiscriminator())", .semicolonNewLine) { printer in + for enumCase in decl.cases { + if let translatedCase = self.translatedEnumCase(for: enumCase) { if enumCase.parameters.isEmpty { printer.print( "case \(enumCase.name.uppercased()) -> new Case.\(translatedCase.name)();" @@ -538,6 +522,10 @@ extension JNISwift2JavaGenerator { "case \(enumCase.name.uppercased()) -> this.getAs\(translatedCase.name)(\(arenaArgument)).orElseThrow();" ) } + } else { + printer.print( + "case \(enumCase.name.uppercased()) -> throw new UnsupportedOperationException(\"\(decl.effectiveJavaName).\(enumCase.name) contains unsupported associated values.\");" + ) } } } diff --git a/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift b/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift index 5ccc68b4..a9e04c57 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift @@ -280,9 +280,11 @@ struct JNIGenericTypeTests { } """, """ - // This is unavailable because it contains unsupported cases. - private Case getCase() { - return null; + public Case getCase() { + return switch (this.getDiscriminator()) { + case SOME -> throw new UnsupportedOperationException("MyOptional.some contains unsupported associated values."); + case NONE -> new Case.None(); + }; } """, ] From abf3db9bfd65980054e7853d9c11d825861d2bcf Mon Sep 17 00:00:00 2001 From: Iceman Date: Mon, 18 May 2026 16:39:17 +0900 Subject: [PATCH 3/3] Print warning on generate --- .../JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift | 3 ++- Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift index 5a13fa9f..05a22ed6 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift @@ -523,8 +523,9 @@ extension JNISwift2JavaGenerator { ) } } else { + logger.warning("\(decl.effectiveJavaName).\(enumCase.name) contains unsupported values so its getCase() method call throws an error.") printer.print( - "case \(enumCase.name.uppercased()) -> throw new UnsupportedOperationException(\"\(decl.effectiveJavaName).\(enumCase.name) contains unsupported associated values.\");" + "case \(enumCase.name.uppercased()) -> throw new UnsupportedOperationException(\"\(decl.effectiveJavaName).\(enumCase.name) contains unsupported values.\");" ) } } diff --git a/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift b/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift index a9e04c57..5f419600 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift @@ -282,7 +282,7 @@ struct JNIGenericTypeTests { """ public Case getCase() { return switch (this.getDiscriminator()) { - case SOME -> throw new UnsupportedOperationException("MyOptional.some contains unsupported associated values."); + case SOME -> throw new UnsupportedOperationException("MyOptional.some contains unsupported values."); case NONE -> new Case.None(); }; }