-
Notifications
You must be signed in to change notification settings - Fork 68
fix: .interoperabilityMode(.Cxx) build issue #463
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
base: main
Are you sure you want to change the base?
fix: .interoperabilityMode(.Cxx) build issue #463
Conversation
When Swift modules have C++ interoperability enabled, jni.h is parsed in C++ mode where JNIEnv is defined as a struct (JNIEnv_). However, SwiftJava modules are compiled without C++ interop, causing JNIEnv to be a pointer type. This mismatch causes type errors. This fix temporarily undefines __cplusplus before including jni.h, forcing C-mode type definitions regardless of the caller's interoperability settings. Fixes: swiftlang#391
Use module.modulemap's requires !cplusplus directive instead of preprocessor tricks to ensure JNI types are always parsed in C mode. This fixes type mismatches when Swift modules with C++ interoperability enabled import CSwiftJavaJNI, as jni.h defines JNIEnv differently in C mode (pointer) vs C++ mode (struct). Fixes: swiftlang#391 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit introduces C-compatible type aliases (CJNIEnv, Cjobject, Cjstring, etc.) to resolve type mismatches when Swift modules with C++ interoperability enabled import SwiftJava. The root cause is that jni.h defines JNI types differently in C vs C++: - C mode: JNIEnv = const struct JNINativeInterface_ * (pointer) - C++ mode: JNIEnv = JNIEnv_ (struct) When a module like HelloWorld has C++ interop enabled but imports SwiftJava (which is compiled without C++ interop), the JNI type definitions conflict, causing compilation errors. Changes: - CSwiftJavaJNI.h: Add C-compatible type aliases that resolve to the same underlying type in both C and C++ modes - JavaType+JNI.swift: Use C-compatible types (Cjstring, Cjobject, etc.) in generated JNI method signatures - JNISwift2JavaGenerator: Add unsafeBitCast for return values to convert between SwiftJava's internal JNI types and C-compatible types - JavaValue.swift, JavaEnvironment.swift: Use CJNIEnv in JNIEnvironment typealias for consistent type definitions Fixes: swiftlang#391 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
ktoso
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting, thank you! I guess we should start testing with C++ interoperability as well huh? 🤔
Update test expectations to match the new code generation output: - Use CJNIEnv, Cjstring, Cjobject, CjbyteArray, CjlongArray types - Add unsafeBitCast wrapper for return values - Remove explicit return statement from async function expectations 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
|
@ktoso
Wrapping existing C++ code in Swift and exposing it to Java is a very useful use case, so I think it's worth adding tests to ensure this works correctly. I've fixed the test failures - the test expectations needed to be updated to match the new code generation output (C-compatible types and unsafeBitCast wrapper). Could you please review again? |
|
I'll want to add a CI mode to check this before merging, thanks for the PR! |
|
Would you mind having a look at the verification jobs Seems this caused some real issues? Since you're familiar with the C++ interop it'd be helpful if you could have a look, thanks |
… package The previous approach tried to build swift-java itself with C++ interop mode enabled, which fails because dependencies like swift-system don't support being built in C++ mode. The actual goal is to verify that projects depending on swift-java can build with C++ interop enabled. This change: - Replaces test-swift-cxx-interop jobs with verify-samples-cxx-interop - Adds CXX_INTEROP environment variable support to all sample Package.swift - Uses .interoperabilityMode(.Cxx) conditionally when CXX_INTEROP=1 - Tests all 7 sample apps with C++ interop on Linux (6.1.3, 6.2, nightly) and macOS (6.2) Fixes: swiftlang#391 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
|
Thanks for flagging this! So I've reworked the approach in 11ae2af
This properly validates that swift-java's public API is compatible with C++ interop mode without requiring swift-java's dependencies to support it. Could you review it again ?? |
.github/workflows/pull_request.yml
Outdated
| 'SwiftJavaExtractJNISampleApp', | ||
| ] | ||
| env: | ||
| SWIFT_JAVA_VERBOSE: true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We lost the verbose here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Update ImplementsJavaMacro to generate CJNIEnv instead of JNIEnv to ensure compatibility with C++ interoperability mode - Update JNIAsyncTests LegacyFuture test expectations to use C-compatible types (CJNIEnv, Cjobject) See: swiftlang#391 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
In C++ interop mode, jobject is UnsafeMutablePointer<_jobject>, but the swift-java library (compiled without C++ interop) expects jobject as OpaquePointer. Use unsafeBitCast to ensure compatibility in both modes since both have the same memory representation (a pointer). This fixes the "cannot convert value of type 'jobject' to expected argument type 'OpaquePointer'" error when building sample apps with C++ interoperability enabled. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This is likely too many CI jobs, they are pretty expensive to run so we should just pick the stable 6.2 for C++ validation please. |
- Reduce C++ interop CI jobs to Swift 6.2 only (Linux) - Add JNI header path to JavaProbablyPrime sample - Fix CSwiftJavaJNI.h to use struct JNINativeInterface_ for Android NDK compatibility 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
ktoso
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a little bit concerned about the last few commits, they seem to be touching a bunch of things and are generated by Claude Code.
Would you mind verifying the changes some more locally?
Summary
modes
Problem
When a Swift module enables .interoperabilityMode(.Cxx), jni.h is interpreted in C++ mode where JNIEnv is a struct type. However, the generated JNI bridge code assumes C mode where JNIEnv is a pointer type, causing type mismatch errors:
cannot convert value of type 'UnsafeMutablePointer<JNIEnv_>' to expected argument type 'UnsafeMutablePointer<UnsafePointer<JNINativeInterface_>?>?'
Solution
Introduce C-compatible type aliases (CJNIEnv, Cjstring, etc.) that always resolve to pointer types regardless of C/C++ mode: