Hi there, is this supposed to compile? ```private ...
# compiler
u
Hi there, is this supposed to compile?
Copy code
private val greeting = "Hello World"

private fun greetingFactory() = greeting

internal inline fun greet() {
    println(greetingFactory())
}

fun main() {
    greet()
}
https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMi4wLjIxIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIj[…]luKCkge1xuICAgIGdyZWV0KClcbn0iLCJwbGF0Zm9ybSI6ImphdmEifQ==
v
What makes you think it should not?
u
If I make `greet`public, the compiler complains: Public-API inline function cannot access non-public-API function Why would an Internal-API inline function be allowed to access a less-then-internal-API function
If
greet
is inlined into another file, it would produce code accessing the file private method
greetingFactory
from another file.
v
Ah, I see 😕
Just guessing here. But with public the problem is, that the consumer then references the private method and would fail if the private method changes. With
internal
the inline function is only usable within the same compilation unit, so such problems cannot arise and it is probably acceptable to inline that call unless it fails then at runtime due to private. 🤷‍♂️
u
With internal the same problems arise…. This is a reproducer, that does not fully map what is actually going on. • If you put
main
in a different file, it will probably throw at runtime. At least that’s what happens in my project. • This code compiles, even if I access
greeting
directly. In my original project the compiler complains when I access
greeting
, but not when I access
greetingFactory
. I’ll try to reproduce that.
The problem is not “binary incompatibility”. The problem is JVM access right verification. In my Android project, I get java.lang.NoSuchMethodError for the private method.
v
Maybe a bug then, either that it compiles or that it not runs. The docs also say that such restrictions are only for public or protected inline functions, or if you annotate an internal inline function with `@PublishedApi`: https://kotlinlang.org/docs/inline-functions.html#restrictions-for-public-api-inline-functions
So as it is clearly defined, I guess it is a bug that it fails at runtime
The compiler should probably add some bridge method or similar that is then used. 🤷‍♂️
u
Interesting, the compiler does create those bridge methods:
Copy code
private static final String greeting = "Hello World";

   private static final String greetingFactory() {
      return greeting;
   }

   public static final void greet() {
      int $i$f$greet = false;
      String var1 = access$greetingFactory();
      System.out.println(var1);
   }

   // $FF: synthetic method
   public static final String access$greetingFactory() {
      return greetingFactory();
   }
So remaining issues: • The code crashes for my real world example. • Android studio disagreeing in certain, yet to be analyzed situations I’ll do more research
One reason I thought it should not compile is Android studio complaining:
Copy code
Non-private inline function cannot access members of private classes: 'public data object O1 defined in com.test.test.Objects'
for below code. But it compiles and runs even when main is in a different file.
Copy code
private sealed interface Objects {
    data object O1
}

private fun objectFactory() = Objects.O1

internal inline fun greet() {
    println(Objects.O1)
}
v
Maybe a bug in the Kotlin IntelliJ Plugin that you should report to KTIJ project on youtrack.jetbrains.com
u
In general I’d say behaviour is at best inconsistent. Here is one more… Below code fails to compile with: Non-private inline function ‘fun getDefaultRoute(): Any’ cannot access members of private class ‘val default: Objects.O1’. This time it is the kotlin compiler complaining. Not the plugin. If I replace
Objects.default
with
Objects.O1
it compiles and runs. If I replace
Objects.default
with indirect access through
private fun defaultObject
, it compiles and runs.
Copy code
private sealed interface Objects {
    companion object {
        val default = O1
    }

    data object O1 : Objects
}

private fun defaultObject(): Objects = Objects.default

internal inline fun getDefaultRoute(): Any {
    return Objects.default
}
Actually the AS plugin agrees with the compiler. I just thought it would not, because I saw the compiler happily accepting
Objects.O1
and did not trust AS that it would not accept
Objects.default
v
Definitely sounds to me like you should report one or multiple bugs to KT and / or KTIJ 🙂