Chachako
01/31/2022, 4:34 PMSyntheticResolveExtension
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?dmitriy.novozhilov
01/31/2022, 5:08 PMChachako
01/31/2022, 5:11 PMX<*>
, but what I want to know is the actual type (X<String>
) of the initializer.dmitriy.novozhilov
01/31/2022, 5:17 PMA.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 analyzedChachako
01/31/2022, 5:23 PMdmitriy.novozhilov
01/31/2022, 5:26 PMA.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 samedmitriy.novozhilov
01/31/2022, 5:26 PMChachako
01/31/2022, 5:51 PMval 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
)Chachako
01/31/2022, 5:56 PMIn 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...Chachako
01/31/2022, 5:58 PMdmitriy.novozhilov
02/01/2022, 8:16 AMthen parse the initializer of the overridden box property (But in this case)override val box = StringWrapper()
box
will be inferred to StringWrapper
and you can use type of property
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:
class B(override val wrapper: Wrapper<*>) : A()
IMO your case may be solved without any plugins at all:
abstract class A<T> {
abstract val wrapper: Wrapper<T>
val value: T
get() = wrapper.value
}
class B(override val wrapper: StringWrapper) : A<String>()
dmitriy.novozhilov
02/01/2022, 8:17 AMBut inCan you elaborate what "Checker" do you mean? It is very ambiguous word for compilerI can resolve the actual type of the expression,Checker
Chachako
02/01/2022, 8:19 AMCan you elaborate what “Checker” do you meanDeclarationChecker or CallChecker
dmitriy.novozhilov
02/01/2022, 8:19 AMBTW, 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 differentdmitriy.novozhilov
02/01/2022, 8:20 AMDeclarationChecker or CallCheckerWell, those checkers are called right after resolution of some specific expression or declaration. But generated declarations may be accessed during resolution
dmitriy.novozhilov
02/01/2022, 8:23 AMWhen will 1.6.20 be released?We want to release
1.6.20-M1
on this weekChachako
02/01/2022, 8:26 AMBut in this caseBut in fact, initializer expression may bewill be inferred tobox
and you can use type of propertyStringWrapper
Wrapper<Object>
instead of ObjectWrapper
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.
Chachako
02/01/2022, 8:33 AMinterface A<T> {
val value: T
}
// A will be implicitly inferred to be A<Int>
class AImpl : A {
override val value = 100
}
dmitriy.novozhilov
02/01/2022, 8:34 AMBut in fact, initializer expression may be Wrapper<Object> instead of ObjectWrapperSolution 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:
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 cycledmitriy.novozhilov
02/01/2022, 8:36 AMIn fact, if Kotlin supports automatic inference of class type argument from property, I really don’t need a compiler pluginSuch feature won't be implemented because of number of architectural and design reasons
Chachako
02/01/2022, 8:51 AM