Quantum64
12/09/2022, 11:51 AMFirExpressionResolutionExtension
?dmitriy.novozhilov
12/09/2022, 11:54 AMYoussef Shoaib [MOD]
12/09/2022, 1:19 PMaddNewImplicitReceivers
also cause issues in IDE? Or is arbitrary transformation more dangerous I guess?dmitriy.novozhilov
12/09/2022, 1:21 PMaddNewImplicitReceivers
don't actually change FIR tree, so it looks quite safer
In IDE we need to guarantee the strict mapping between FIR and PSI nodes, and arbitrary transformations from plugins may easily break itdmitriy.novozhilov
12/09/2022, 1:22 PMFirExpressionResolutionExtension
will exists in future at all
Originally it was added as an experiment, but we delayed actual design and prototyping any plugins with it because of focusing on K2 releaseQuantum64
12/09/2022, 2:04 PMdmitriy.novozhilov
12/09/2022, 2:07 PMfoo(a, b)
// ---->
foo(
run {
someCallback()
bar(a)
},
b
)
And there is a problem with someCallback()
call
Where it should be reported in IDE?
Or even if there are no problems, how to tell the user what exactly is happening here?Quantum64
12/09/2022, 2:09 PMYoussef Shoaib [MOD]
12/09/2022, 2:10 PMQuantum64
12/09/2022, 2:10 PMdmitriy.novozhilov
12/09/2022, 2:10 PMQuantum64
12/09/2022, 2:12 PMdmitriy.novozhilov
12/09/2022, 2:12 PMI mean, do you not have this issue already with the variable assignment transformer?Assignment transformer is extremely restricted. Well right now it's not that restricted, but we will change it to disallow changing arguments in any way
dmitriy.novozhilov
12/09/2022, 2:13 PMSo overload selection happens at some point after the arguments are resolved right? Can we not run transformers before that? (But after the arguments are resolved)Not always. Regular arguments are resolved before resolution of call itself. But lambdas and callable references are resolved after candidate for containing call is already chosen
Youssef Shoaib [MOD]
12/09/2022, 2:15 PMQuantum64
12/09/2022, 2:16 PMQuantum64
12/09/2022, 2:19 PMdmitriy.novozhilov
12/09/2022, 2:20 PMWhat about theThis one is very restricted as well (and part of my colleagues thinks that we shouldn't implement this feature at all, because it goes against all our resolution rules)resolverOverloadResolutuonByLambdaReturnType
dmitriy.novozhilov
12/09/2022, 2:29 PMin this way could enable auto-boxing of union types implemented with sealed interfacesI think this specific usecase will be solved when we introduce real union-types in the language itself. This is one of most important features which will we work on after K2 release
Quantum64
12/09/2022, 2:31 PMdmitriy.novozhilov
12/09/2022, 2:31 PMdmitriy.novozhilov
12/09/2022, 2:37 PMI think users installing compilers plugins should know what they are signing up forIt is correct for small projects where all code is owned by 1-3 people But there might be problems in big projects, which may live more than one specific person work on it. So it is discusable topic too I understand your concerns, but during language design we should consider all users, problems and benefits for each (at least each popular) user (and project) type
Quantum64
12/09/2022, 2:49 PMdmitriy.novozhilov
12/09/2022, 3:08 PMYoussef Shoaib [MOD]
12/09/2022, 3:23 PMraulraja
12/09/2022, 6:59 PMFirFile
.
https://github.com/arrow-kt/arrow-reflection/blob/main/arrow-reflect-compiler-plug[…]piler/plugin/fir/checkers/FirMetaAdditionalCheckersExtension.kt
Seems to work for all cases excepts resolution members added to new declarations.
for that we have to do it through the DeclarationGenerationExtension https://github.com/arrow-kt/arrow-reflection/blob/main/arrow-reflect-compiler-plug[…]/reflect/compiler/plugin/fir/codegen/FirMetaCodegenExtension.kt
We could also use a proper transformation extension point instead of the current declaration checker hack.
Would be ideal to have the checker context and diagnostic reporter in scope too. They are useful to add the current declarations in scope and to emit diagnostics as you perform the transformation over problematic nodes that can't be transformed.Quantum64
12/10/2022, 3:12 AMraulraja
12/10/2022, 12:45 PMFirElement.transform
and FirElement.transformChildren
which can be invoked as members of the class everywhere.
The trick is that there is only a couple of places in the entire frontend pipeline where using these methods make sense for compiler plugins that transform trees:
FirStatusTransformerExtension
: too early for most elements
`FirDeclarationGenerationExtension`: probably right place but only gets called when you give it names to generate members
FirAdditionalCheckersExtension
: too late.Youssef Shoaib [MOD]
12/10/2022, 1:10 PMFirExpressionResolutionExtension.addNewImplicitReceivers
you get access to every `FirFunctionCall`after it is resolved, so theoretically you can transform it and its children (and in fact I have done so initially).
Another place that isn't quite exposed, but just works, is `FirSessionComponent`s. You can replace any pre-existing one by calling FirSession.register()
IIRC, and from that you get access to goodies like changing how conflict resolution works.
I think making these things explicitly allowed but also explicitly opt-in would be great, perhaps through having "unsafe" compiler plugins as a distinction.raulraja
12/10/2022, 3:56 PMFirExpressionResolutionExtension
you need to fix whatever you add in backend IR.
This very example is something that is very frustrating. Most compiler plugin authors would want to perform a transformation and be done with it. The reality is that we have to go over multiple phases interfaces and implementations with different unrelated models to get it done FirCall, IrCall, bunch of utility classes etc. All these types have apis that are daunting and spread in different compiler utility classes. In general is a bad experience for the maintainer and library authors trying to keep up with the compiler.
I wish for a future where compiler plugins can be implemented with one just method or interface and it does not require you understand all the compiler internals and the implications of every single phase. At least for plugins that just want to transform the tree, type check the transformation and be done with it.dmitriy.novozhilov
12/10/2022, 4:25 PMraulraja
12/10/2022, 5:12 PMquotes
abstraction covers a couple of important use cases when working on macros or compiler plugins.
https://docs.scala-lang.org/scala3/reference/metaprogramming/macros.html
User already know how to write Kotlin, and that should be the default to generate and capture kotlin expressions. Similarly in Scala we do:
inline def assert(inline expr: Boolean): Unit =
${ assertImpl('expr) } // the ' is a quote that captures the expr arg at compile time
def assertImpl(expr: Expr[Boolean])(using Quotes) = '{
if !$expr then
throw AssertionError(s"failed assertion: ${${ showExpr(expr) }}")
}
There any args typed to Boolean
are captured and turned into Expr[Boolean]
. This allows the compiler to invoke assertImpl
with reflection and pass in the Expr[Boolean]
tree at compile time for the macro to expand. This expression tree is already typed but the compiler will allow a transformation in scope. Same you have something like Expr[_]
there is also Type[_]
What we are trying to implement in Meta is inspired by this model where compiler plugin authors write just a function/annotation with a single function that can do what we have to do today in FIR in different phases:
Generate, Transform, Report diagnostics.
Currently looks like https://github.com/arrow-kt/arrow-reflection/blob/23a03a79ef620c41ad193dba5b7e398d[…]ect-annotations/src/main/kotlin/arrow/meta/samples/Increment.kt but it could be further simplified if we had quotes and were not dealing directly with complexity of FIR and Diagnostic API's. In this example I'm trying to get rid of the boilerplate required to encode Diagnostics in the compiler by hiding all that in the Diagnostics
parent class and working around the amount of boilerplate required to create kotlin expressions with the FIR builder with "${+expression} + 1".call
The biggest problem IMO in meta-programming in Kotlin right now is that people need to understand the compiler internal API's and trees and they have the entire compiler in the classpath when they work with it. Would be easier to work with a representation and it's own api even if it's programmatic and not based in quotes or templates.
From the Arrow side we are grateful for FIR because many of the features we have today based on KSP are suboptimal and require users to perform full compilations in the IDE before they get proper error highlighting. All these compiler plugins working in the IDE as you type is the killer feature for FIR and the user experience.