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 <[email protected]>
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 <[email protected]>
> 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
>>
>
override fun handleMissingMethod(
receiver: ClassNode,
name: String,
argumentList: ArgumentListExpression,
argumentTypes: Array<ClassNode>,
call: MethodCall
): List<MethodNode> {
// I am matching name and number of args here, if the find is null,
I'll return an empty list
val matchingMethod = receiver.methods.filter {
it.name == name && argumentTypes.size == it.parameters.size
}.singleOrNull() ?: return emptyList()
// If I found something, I will return that method
return listOf(matchingMethod).also {
// But I also need to update the call arguments to match the
parameters of the method
// So I get the parameter types
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 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 th 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 }
}
}
}
private fun ClassNode.defaultConstructorExpression(): Expression =
GeneralUtils.ctorX(this).apply {
val defaultConstructor = declaredConstructors.single {
it.parameters.isNullOrEmpty() }
setNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET,
defaultConstructor)
}
class ArgumentListVisitor(
private val sourceUnit: SourceUnit,
private val parameterTypes: List<ClassNode>
) : ClassCodeVisitorSupport() {
override fun getSourceUnit() = sourceUnit
override fun visitTupleExpression(expression: TupleExpression) {
// parsing the tuple expression to wrap each argument with the
transform function
val modifiedArguments = expression.expressions.zip(parameterTypes) {
arg, paramType ->
GeneralUtils.callX(
GeneralUtils.classX(REDEFINED_TYPES_NODE),
"transform",
GeneralUtils.args(arg ... +
paramTypeReference.defaultConstructorExpression())
).also {
it.methodTarget = TRANSFORM_NODE
it.putNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET, TRANSFORM_NODE)
it.inferred_type = paramType
}
}
// Replacing all sub expressions inside the expression. we now have the
transform method around each arg
expression.expressions.also {
it.clear()
it.addAll(modifiedArguments)
}
}
}