You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@groovy.apache.org by Saravanan Palanichamy <ch...@gmail.com> on 2024/09/08 15:08:41 UTC
Modifying the AST during compile static type checking
Hello folks
I have this following use case for my DSL (I use Groovy 3.0.16)
- I turn on compile static on everything
- This will hit some errors in type checking that I want to gracefully
redirect to the right methods
- Specifically it will hit the method not found function in the type
checking extension
- When it does, I want to take the method call and update its arguments
(by wrapping them around another function call that will transform it to
the right parameter type expected by the function)
To do this
- I updated the AST in the type checking AST by looking at each
parameter and wrapping it in a transform method
- I set the inferred types and direct method call metadata for this
transform method
I first hit the error that arg types did not match which is when I realized
that the arg types needed to be updated if I change them in the extension.
So I did this
- I updated the passed in arg types array with the replaced types
- I then returned the original method
Type checking passed, but class generation failed with an wierd stack index
-1 error *(ASM reporting processing error Index -1 out of bounds for length
0)*. So my question is this, what is the right way to update the AST inside
the type checking extension? How do I get the type checker to absorb my
updated arguments? Thank you for taking the time to read this. I have
attached the code snippet (removed details not needed)
override fun handleMissingMethod(
receiver: ClassNode,
name: String,
argumentList: ArgumentListExpression,
argumentTypes: Array<ClassNode>,
call: MethodCall
): List<MethodNode> {
// TODO: Add check for types as well and not just name and number
of params
val matchingMethod = receiver.methods.filter {
it.name == name && argumentTypes.size == it.parameters.size
}.singleOrNull() ?: return emptyList()
return listOf(matchingMethod).also {
val paramTypes = matchingMethod.parameters.map { it.type }
// Update the argument list to match the method signature
ArgumentListVisitor(typeCheckingVisitor.typeCheckingContext.source,
paramTypes).also {
call.arguments.visit(it)
}
* // Update argument types ( This feels weird to update types
here ...)*
argumentTypes.forEachIndexed { index, _ ->
paramTypes[index]?.also { argumentTypes[index] = it }
}
}
}
class ArgumentListVisitor(
private val sourceUnit: SourceUnit,
private val parameterTypes: List<ClassNode>
) : ClassCodeVisitorSupport() {
override fun getSourceUnit() = sourceUnit
override fun visitTupleExpression(expression: TupleExpression) {
require(expression.expressions.size ==
argumentSerializationTypeReferences.size) {
"Dont have enough type references to fix arguments"
}
val modifiedArguments = expression.expressions.zip(parameterTypes)
{ arg, paramType ->
GeneralUtils.callX(
GeneralUtils.classX(REDEFINED_TYPES_NODE),
"transform",
GeneralUtils.args(arg ... + other details needed to
make the transform)
).also {
it.methodTarget = TRANSFORM_NODE
it.putNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET,
TRANSFORM_NODE)
it.inferred_type = paramType
}
}
*// No way for me to update the expression (I cannot do a transform
because it will create a new expression that I have no way of setting back*
expression.expressions.also {
it.clear()
it.addAll(modifiedArguments)
}
}
}
regards
Saravanan
Re: Modifying the AST during compile static type checking
Posted by Saravanan Palanichamy <ch...@gmail.com>.
As always, it is a silly programmer error. I forgot to resolve the other
extra things I added (in addition to the transform method) in the AST
modification. I have updated the code snippet to show that I am also
resolving the constructor call I added. This works with the class
generation now ... YAAAY!!!!!
That said, the original concern still exists. It feels like the type
checking extension is good when
- You have to redirect to a different method with the signature in the
caller
- You have to handle invalid assignments and other type checking errors
But when you are trying to handle a missing method, and want to change the
call arguments to match it (instead of routing to a different method that
matches the args), it feels like we have to mess with the details of the
argtypes assumed in the level above the type checking. If I had to add a
few extra arguments to make it work, it feels like this is impossible
because the argTypes is an array
regards
Saravanan
On Mon, Sep 9, 2024 at 5:41 AM Saravanan Palanichamy <ch...@gmail.com>
wrote:
> Thank you jochen
>
> I have tried to comment on the code in the attachment. The problem is that
> I am modifying the AST in the type checker and I am trying to make it
> compatible with what comes after. I am sure I am doing something wrong
> because the class generation fails with a stack traversal issue. I can deal
> with the expression transformation and the like but my doubt is what is the
> right way to work with the type checker when adding new elements to the AST
> during type checking
>
> My specific problems are with these lines of code and that the overall
> solution passed the type checking but did not pass class generation
>
> ===
> // Update argument types (this is what feels off to me, as if
> I am hacking this. argument types is an array
> // passed in from the type checker. It has the original arg
> types and I am replacing it with the parameter
> // types from the function I matched. Without this change, the
> type checker complains that it is not able
> // to verify the matching function)
> argumentTypes.forEachIndexed { index, _ ->
> paramTypes[index]?.also { argumentTypes[index] = it }
> }
> ===
>
>
> On Sun, Sep 8, 2024 at 11:54 PM Jochen Theodorou <bl...@gmx.org>
> wrote:
>
>> On 08.09.24 17:08, Saravanan Palanichamy wrote:
>> [...]
>> > override fun handleMissingMethod(
>> > receiver: ClassNode,
>> > name: String,
>> > argumentList: ArgumentListExpression,
>> > argumentTypes: Array<ClassNode>,
>> > call: MethodCall
>> > ): List<MethodNode> {
>> >
>> > // TODO: Add check for types as well and not just name and
>> > number of params
>> > val matchingMethod = receiver.methods.filter {
>> > it.name <http://it.name> == name && argumentTypes.size ==
>> it.parameters.size
>> > }.singleOrNull() ?: return emptyList()
>>
>> is this return here really intended? Won't it be a return for the
>> method? But then again this code looks like Kotlin and I did not learn
>> such details of that language...
>>
>> [...]
>> > */// No way for me to update the expression (I cannot do a transform
>> > because it will create a new expression that I have no way of setting
>> back/*
>>
>> Maybe you need two steps, maybe an Expression transformer and maybe the
>> type checking extension
>>
>>
>> bye Jochen
>>
>
Re: Modifying the AST during compile static type checking
Posted by Saravanan Palanichamy <ch...@gmail.com>.
Thank you jochen
I have tried to comment on the code in the attachment. The problem is that
I am modifying the AST in the type checker and I am trying to make it
compatible with what comes after. I am sure I am doing something wrong
because the class generation fails with a stack traversal issue. I can deal
with the expression transformation and the like but my doubt is what is the
right way to work with the type checker when adding new elements to the AST
during type checking
My specific problems are with these lines of code and that the overall
solution passed the type checking but did not pass class generation
===
// Update argument types (this is what feels off to me, as if I
am hacking this. argument types is an array
// passed in from the type checker. It has the original arg
types and I am replacing it with the parameter
// types from the function I matched. Without this change, the
type checker complains that it is not able
// to verify the matching function)
argumentTypes.forEachIndexed { index, _ ->
paramTypes[index]?.also { argumentTypes[index] = it }
}
===
On Sun, Sep 8, 2024 at 11:54 PM Jochen Theodorou <bl...@gmx.org> wrote:
> On 08.09.24 17:08, Saravanan Palanichamy wrote:
> [...]
> > override fun handleMissingMethod(
> > receiver: ClassNode,
> > name: String,
> > argumentList: ArgumentListExpression,
> > argumentTypes: Array<ClassNode>,
> > call: MethodCall
> > ): List<MethodNode> {
> >
> > // TODO: Add check for types as well and not just name and
> > number of params
> > val matchingMethod = receiver.methods.filter {
> > it.name <http://it.name> == name && argumentTypes.size ==
> it.parameters.size
> > }.singleOrNull() ?: return emptyList()
>
> is this return here really intended? Won't it be a return for the
> method? But then again this code looks like Kotlin and I did not learn
> such details of that language...
>
> [...]
> > */// No way for me to update the expression (I cannot do a transform
> > because it will create a new expression that I have no way of setting
> back/*
>
> Maybe you need two steps, maybe an Expression transformer and maybe the
> type checking extension
>
>
> bye Jochen
>
Re: Modifying the AST during compile static type checking
Posted by Jochen Theodorou <bl...@gmx.org>.
On 08.09.24 17:08, Saravanan Palanichamy wrote:
[...]
> override fun handleMissingMethod(
> receiver: ClassNode,
> name: String,
> argumentList: ArgumentListExpression,
> argumentTypes: Array<ClassNode>,
> call: MethodCall
> ): List<MethodNode> {
>
> // TODO: Add check for types as well and not just name and
> number of params
> val matchingMethod = receiver.methods.filter {
> it.name <http://it.name> == name && argumentTypes.size == it.parameters.size
> }.singleOrNull() ?: return emptyList()
is this return here really intended? Won't it be a return for the
method? But then again this code looks like Kotlin and I did not learn
such details of that language...
[...]
> */// No way for me to update the expression (I cannot do a transform
> because it will create a new expression that I have no way of setting back/*
Maybe you need two steps, maybe an Expression transformer and maybe the
type checking extension
bye Jochen