Skip to content

Kotlin: explicitly set visibility to internal for elements nested in other internal types#1801

Open
pwrobeldev wants to merge 23 commits intomasterfrom
pwrobeldev/KOTLIN_NESTED_INTERNALS_FINAL
Open

Kotlin: explicitly set visibility to internal for elements nested in other internal types#1801
pwrobeldev wants to merge 23 commits intomasterfrom
pwrobeldev/KOTLIN_NESTED_INTERNALS_FINAL

Conversation

@pwrobeldev
Copy link
Contributor

---------- Motivation ----------
When Kotlin code is accessed from Java, then 'internal' elements become public.
In our mustache templates we annotate internal functions, properties and fields via
internal and JvmSynthetic. The latter removes such element altogether for Java
compiler.

However, if we have elements not annotated as @Internal in some internal type e.g.:

@Internal
class Foo {
    fun bar(): Int
}

Then in Java the bar() function was accessible. The problem did not exist for Kotlin.

---------- Solution ----------
Explicitly treat each element nested in another internal type like it is internal.

This change introduces the following:

  1. The new class called KotlinInternalVisibilityPredicate which provides isConceptuallyInternal() function that checks if element is internal or is nested in some internal types.
  2. Introduces the new logic in KotlinVisibilityResolver to resolve visibility to 'internal' for conceptually internal elements that are not direct elements of interfaces.
  3. Introduces KotlinSyntheticResolver class that is used to resolve string when element needs JvmSynthetic annotation and is conceptually internal --> direct children of interface cannot be internal in Kotlin, but can use JvmSynthetic to remove them from Java.
  4. Introduces new smoke and functional tests.
  5. Removes the interoperability test cases from Java tests that cannot be compiled with the new approach. This showcases how the situation was improved.

Hsilgos
Hsilgos previously approved these changes Feb 19, 2026
This is not a functional change. The logic is preserved.
All tests work in the identical way.

This change introduces a new function, which will be used
later for all types.

Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com>
The logic was simplified to use 'return when' statement.
This improves readability and prepares for further changes.

This is not a functional change. The code works as previously.
All tests pass.

Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com>
…internal

This change ensures that all elements nested in internal
types hierarchy are internal and annotated with JvmSyntetic.

Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com>
All elements nested in internal types hierarchy are
internal.

This does not apply to direct members of interfaces.
According to Kotlin rules such types cannot be internal.

However, such direct nested elements like functions and
properties should use 'JvmSynthetic'. This will be fixed
in the next commit.

Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com>
…pile

The previous changes ensure that more elements are not accessible in Java.
The following errors were visible -- the test cases were removed, because
with the new handling of nested internal the compilation error was expected.

```
> Task :functional:compileReleaseUnitTestJavaWithJavac FAILED
/functional/android/src/test/java/com/here/android/test/VisibilityAttributeTest.java:57: error: cannot find symbol
    SomeInternalClassWithMembers someObject = SomeInternalClassWithMembers.create();
                                                                          ^
  symbol:   method create()
  location: class SomeInternalClassWithMembers
/functional/android/src/test/java/com/here/android/test/VisibilityAttributeTest.java:58: error: cannot find symbol
    assertEquals(987, someObject.someFunction());
                                ^
  symbol:   method someFunction()
  location: variable someObject of type SomeInternalClassWithMembers
/functional/android/src/test/java/com/here/android/test/VisibilityAttributeTest.java:63: error: cannot find symbol
    assertEquals(765, SomeInternalClassWithMembers.someStaticFunction());
                                                  ^
  symbol:   method someStaticFunction()
  location: class SomeInternalClassWithMembers
/functional/android/src/test/java/com/here/android/test/VisibilityAttributeTest.java:68: error: cannot find symbol
    SomeInternalStructWithMembers someObject = SomeInternalStructWithMembers.create();
                                                                            ^
  symbol:   method create()
  location: class SomeInternalStructWithMembers
/functional/android/src/test/java/com/here/android/test/VisibilityAttributeTest.java:69: error: cannot find symbol
    assertEquals(123, someObject.someInteger);
                                ^
  symbol:   variable someInteger
  location: variable someObject of type SomeInternalStructWithMembers
/functional/android/src/test/java/com/here/android/test/VisibilityAttributeTest.java:70: error: cannot find symbol
    assertEquals(456, someObject.someLong);
                                ^
  symbol:   variable someLong
  location: variable someObject of type SomeInternalStructWithMembers
/functional/android/src/test/java/com/here/android/test/VisibilityAttributeTest.java:75: error: cannot find symbol
    SomeInternalStructWithMembers someObject = SomeInternalStructWithMembers.create();
                                                                            ^
  symbol:   method create()
  location: class SomeInternalStructWithMembers
/functional/android/src/test/java/com/here/android/test/VisibilityAttributeTest.java:76: error: cannot find symbol
    assertEquals(32, someObject.someFunction());
                               ^
  symbol:   method someFunction()
  location: variable someObject of type SomeInternalStructWithMembers
```

Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com>
The logic will be handled in dedicated classes
for resolving synthetic annotation and visibility.

Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com>
Some elements cannot use internal keyword even though
conceptually they are internal.

A good example are direct children elements of interfaces.
Kotlin interface disallows internal elements as the direct
children elements.

Even though such children elements are not visible when
accessed from Kotlin (because interface is internal), in
Java they are accessible.

To remove them from Java we will need to annotate them
as synthetic via annotation. We need to have a way to
nicely check whether element should be annotated as synthetic
instead of having logic in mustache template.

This change introduces new class, which checks for the conceptual
internal visibility. The new class is applied in KotlinVisibilityResolver.

No changes in smoke tests are present -- the logic is preserved.

In next commits the new class will be used to create synthetic resolver.

Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com>
In the past we had special logic for properties getters/setters
when resolving visibility for extensions. The extensions have
been removed and the special logic is a dead code.

Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com>
This allows to minimize the logic in mustache files.

Moreover, the JvmSynthetic is used for elements defined in
internal interfaces and internal lambda.

Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com>
Still, the synthetic annotation is missing for properties.
This will be adjusted in the next commit.

Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com>
…pile

Due to usage of jvm synthetic for functions of
internal interfaces the interoperability cases
stopped to compile -- functions in such interfaces
are not visible for Java now.

```
> Task :functional:compileReleaseUnitTestJavaWithJavac FAILED
/functional/android/src/test/java/com/here/android/test/VisibilityAttributeTest.java:34: error: method does not override or implement a method from a supertype
  @OverRide
  ^
/functional/android/src/test/java/com/here/android/test/VisibilityAttributeTest.java:39: error: method does not override or implement a method from a supertype
  @OverRide
```

Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com>
The properties should also be synthetic to hide them from Java.

Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com>
This hides them from Java.

Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com>
…aces

Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com>
In Kotlin the internal interface can be used and fields
can be overridden. Similar example was implemented in Java,
but it did not compile -- we could not override the required
methods.

Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com>
Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com>
The test case called 'internalFieldOfStructCanBeAccessed' initially
used internal fields, but the usage was removed when it stopped to
compile.

The fields 'someInteger' and 'someLong' are public in public type:
```
struct SomeStructWithInternalMembers {
    someInteger: Int
    someLong: Long
```

Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com>
This commit brings new smoke tests related to elements nested
in the internal class in Kotlin.

The expected result is: "ALL NESTED ELEMENTS ARE TREATED AS INTERNAL".

One finding, that will be fixed in upcoming commits:
 - the 'error' field of internal exception is not
   annotated as synthetic -> this must be adjusted

Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com>
This commit brings functional tests used to validate
if nested elements (including multi-level nesting)
in internal class can be accessed and there are no
runtime problems with symbols.

This commit is implementation of tests similar to
the smoke tests introduced in the previous commit.

Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com>
This commit adjusts the mustache template to generate
synthetic annotation for exceptions when they are internal.

Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com>
When exceptions are internal the synthetic annotation
is used to hide the field.

Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com>
…erop test

Because we generate 'JvmSynthetic' for exception fields, the compilation error
was trigerred.

```
/functional/android/src/test/java/com/here/android/test/VisibilityAttributeTest.java:76: error: cannot find symbol
    assertEquals(exception.error, SomeInternalEnum.ONE);
                          ^
  symbol:   variable error
  location: variable exception of type SomethingBadHappenedException
```

Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com>
Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com>
@pwrobeldev pwrobeldev force-pushed the pwrobeldev/KOTLIN_NESTED_INTERNALS_FINAL branch from 1c41b27 to 1384afe Compare February 22, 2026 19:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants