Hello everyone, I have a question about compiler p...
# compiler
c
Hello everyone, I have a question about compiler plugin, can I delay
SyntheticResolveExtension
execution? I need to synthesize a new property, and then the type of this property depends on the initializer expression of another property, so I must use
BindingContext.getType
to parse the actual type of another property’s initializer, but unfortunately
SyntheticResolveExtension
is executed a bit too early, at this time the value returned by the
BindingContext.getType
is
null
. Or is there another way for me to infer the type of another expression during synthesis?
d
You can force resolution of property initializer by asking return type of property descriptor
c
The problem is that the type declared by another property is a generic:
X<*>
, but what I want to know is the actual type (
X<String>
) of the initializer.
d
Well, actually you can't Assume you want to generate function
A.foo(): ???
where type of
???
depends of type of initializer of property
B.bar
, but this initializer looks like this:
val b: Any = a.foo()
API of
SyntheticResolveExtension
allows to access type of intializer sometimes, but in general case it's not safe. In plugins for K2 compiler with new API you will be forced to generate all members you want to create long before any body of any declaration will be analyzed
c
Well, that means I can’t solve the problem, right? Maybe I should generate an extension function source code at sometime so that it has IDE support, but I don’t know if compiler plugin has such capabilities.
d
In example above there is no difference between
A.foo
as member or extension You can check doc of invariants of future FIR API here: https://github.com/JetBrains/kotlin/tree/master/docs/fir API described in those docs most likely will be changed, but principles will stay same
BTW what's your usecase for such plugin?
c
I have an interface that has an abstract property
val box: Wrapper<*>
, so I need to find all classes that inherit this interface, then parse the initializer of the overridden
box
property (
override val box = StringWrapper()
), and finally implicitly create a new property from the actual type of the initializer (
val real: String
)
In example above there is no difference between A.foo as member or extension
But in
Checker
I can resolve the actual type of the expression, and then I create a kt file and write an extension, okay? But I don't think it's right...
BTW, will FIR be available in 1.6.20? When will 1.6.20 be released?
d
then parse the initializer of the overridden box property (
override val box = StringWrapper()
)
But in this case
box
will be inferred to
StringWrapper
and you can use type of property
Copy code
interface Wrapper<T> {
    val value: T
}
class StringWrapper(override val value: String) : Wrapper<String>

abstract class A {
    abstract val wrapper: Wrapper<*>
}

class B : A() {
    // val wrapper has type StringWrapper
    override val wrapper = StringWrapper("hello")
}

fun test(b: B) {
    b.wrapper.value
}
If user directly specifies type of
box
as
Wrapper<*>
then he can have some reasoning behind that and it can be not safe to use type of initializer. Moreover, there can be no intializer at all:
Copy code
class B(override val wrapper: Wrapper<*>) : A()
IMO your case may be solved without any plugins at all:
Copy code
abstract class A<T> {
    abstract val wrapper: Wrapper<T>
    
    val value: T
        get() = wrapper.value
}

class B(override val wrapper: StringWrapper) : A<String>()
But in 
Checker
 I can resolve the actual type of the expression,
Can you elaborate what "Checker" do you mean? It is very ambiguous word for compiler
c
Can you elaborate what “Checker” do you mean
DeclarationChecker or CallChecker
d
BTW, will FIR be available in 1.6.20? When will 1.6.20 be released?
We plan release FIR preview in 1.7.0, but you can try it with any actual release of compiler (including developer builds) by adding
-Xuse-fir
flag to CLI compiler arguments. Note that this flag will enable FIR during compilation, but IDE plugin will continue to use FE 1.0, so warnings/errors in IDE in build may be different
DeclarationChecker or CallChecker
Well, those checkers are called right after resolution of some specific expression or declaration. But generated declarations may be accessed during resolution
When will 1.6.20 be released?
We want to release
1.6.20-M1
on this week
c
But in this case 
box
 will be inferred to 
StringWrapper
 and you can use type of property
But in fact, initializer expression may be
Wrapper<Object>
instead of
ObjectWrapper
Copy code
val stringWrapper: Wrapper<String> = Wrapper("")

class AImpl : A {
  override val wrapper: Wrapper<*> = stringWrapper
}
Therefore, if I get the type of wrapper through
PropertyDescriptor
, I will get
Wrapper<*>
instead of
Wrapper<String>
IMO your case may be solved without any plugins at all:
But in fact, my actual use case is much more complex than this, it is actually a stub of a reflected class. What I propose is only a similar use case, so the compiler plugin is necessary.
In fact, if Kotlin supports automatic inference of class type argument from property, I really don’t need a compiler plugin:
Copy code
interface A<T> {
  val value: T
}

// A will be implicitly inferred to be A<Int>
class AImpl : A {
  override val value = 100
}
d
But in fact, initializer expression may be Wrapper<Object> instead of ObjectWrapper
Solution with type parameter in
A
still works
What I propose is only a similar use case, so the compiler plugin is necessary.
I understand, but it's impossible to implement such abilities without rounds of compilation which can be infinite with some plugins (and for compiler and IDE performance purposes we don't want to introduce even second round) Imagine something like that:
Copy code
class A {
    val x = this.y
    
    // plugin want to generate member
    //   val y: Int
    // if type of initializer of `x` is String
}

val A.y: String
    get() = "hello"
First round:
this.y
resolves to extension and have type
String
• plugin generates member
y: Int
Second round: •
this.y
resovles to generated
y: Int
this.y
type is
Int
, not
String
, so member
y
is not generated Third round: same as first. We are in infinte cycle
In fact, if Kotlin supports automatic inference of class type argument from property, I really don’t need a compiler plugin
Such feature won't be implemented because of number of architectural and design reasons
c
Well, so I can only add type parameter to the interface to solve this problem. Thank you for your detailed answer!
👌 1