Repository navigation
JNI: align inherited protocol requirement names across bindings - #3
Merged
Merged
Conversation
Disambiguate protocol method names using actual requirements, and preserve the declaring interface's names for inherited box methods and native thunks. Avoid caching provisional names while probing translation support. Add regression and Java runtime tests covering default implementations, callbacks, and overloads introduced by a refined protocol.
teamhimeh
changed the base branch from
codex/fix-jni-protocol-extension-requirements
to
qoncept-fork
October 23, 2026 06:43
Collaborator
Author
|
related: swiftlang#934 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Java interfaces, returned existential boxes, and native JNI thunks can disagree about protocol method names. Extension implementations can incorrectly influence overload detection, and an inherited requirement can be renamed on a child box even though the parent Java interface still declares its original name.
For example, if
Parentdeclaresvalue(number:)andChildaddsvalue(text:), the child box must implement the parent's existingvalue(long)method and its ownvalueText(long)method. EmittingvalueNumber(long)for the inherited method neither implements the parent interface nor matches its API.Example 1: a Swift default implementation must not rename a requirement
Consider this Swift API, matching the first runtime regression fixture:
There is one requirement,
message(). The extension supplies a default implementation of that same requirement. It does not introduce another method that needs a different Java name.Before this change, the extension implementation incorrectly participated in name disambiguation. The generated Java interface declared
String message_(), while the generatedNamingCallbackBoximplementedString message(). The box therefore failed to implement the interface's abstract method, causing Java compilation to fail.After this change, the interface, box, and corresponding JNI thunk consistently use the name
message. Both a Java implementation and a Swift value using the default implementation work through the same Java method:The Swift default implementation remains the implementation for
DefaultNamingCallback; the Java lambda supplies its own protocol witness. Generating Java interfacedefaultmethods that forward to Swift implementations remains separate future work.Example 2: child overloads must preserve the parent interface's API
Swift argument labels distinguish these two requirements even though both accept an
Int64:Java does not have Swift argument labels. Two
value(long)methods cannot represent the two distinct operations on the child interface. The parent interface already exposesvalue(number:)asvalue(long), so that inherited API must keep its existing name. The child's additional requirement is namedvalueText(long).Before this change, the child box recalculated names using the child's overload set and emitted
valueNumber(long)for the inherited requirement. That did not implement the parent'svalue(long)method. The child interface, box, and native thunks could also use inconsistent names because translation support was checked and cached before overload disambiguation was complete.After this change, each inherited requirement keeps the name chosen by its declaring interface, and all generated layers agree:
NamingParent.value(number:)value(long)$valueNamingChild.value(text:)valueText(long)$valueTextJava callers can use both operations, including through a parent reference:
makeNamingChild()returnsany NamingChildin Swift. Its generated Java box holds that Swift value and forwards each method through a native JNI thunk to the correct Swift protocol witness. Consistent names are required for the generated Java code to compile and for the Java native declarations to resolve to the matching Swift JNI entry points.The runtime regressions verify the Java callback override, the Swift default implementation, and both inherited overload results (
13and23). Generator regressions also check the matching native entry-point names.Changes
Validation
swift test --disable-experimental-prebuilts --disable-sandbox../gradlew :Samples:SwiftJavaExtractJNISampleApp:test --no-daemon(three new runtime tests).swift-format lint --strictandgit diff --check.Tested with Apple Swift 6.4 and JetBrains Runtime 25.0.2 on macOS.
Dependency
This change builds on the distinction between protocol requirements and extension implementations. That prerequisite is already present in the base branch; this diff contains only the naming changes.