[ 
https://issues.apache.org/jira/browse/GROOVY-8660?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17446767#comment-17446767
 ] 

Jochen Theodorou commented on GROOVY-8660:
------------------------------------------

I just happen to find this by chance. The mechanism for foo() calling foo(x) is 
an eyesore to me for a long time. Yes I wanted to remove it and most people 
agreed with it, though it is difficult to know if these know the implications 
to existing code. Anyway...

RULE1:  For varargs and calling foo(SomeClass...) we basically have the 
following rules from Java:

# *foo( )* -> calls foo with empty array of type SomeClass[]
# *foo( null )* -> calls foo with null
# *foo( x )* -> calls foo using the reference provided by x is x is an array 
and its element type is a subclass of SomeClass. Otherwise the method is not 
applicable
# *foo( y_1, y_2, ..., y_n )* -> calls foo wrapping all y_i in an array if all 
the y_i are a subclass of SomeClass. The Array will have the class SomeClass[].

RULE2: For the... let me call it null-expansion we have the following rule:
If normal method selection fails and the call is without arguments try method 
selection again with null as argument. In the method distance algorithm any 
expansion, that has to be made gives a distance penalty. That means a 
foo(someClass...) has a bigger distance for a call foo() than if I would call a 
method declared as foo() directly.  foo(null) or foo(x) do not have that 
penalty, because no additional action has to be taken.

Now let's look at the cases in the issue:
{code:Java}
class OnlySingle {
    def foo(a) { "single param: $a" }
}

println new OnlySingle().foo()
// as expected: 'single param: null'
{code}
This means the initial method selection fails and we start a second method 
selection with foo(null), which then successfully calls the method. RULE1
{code:Java}
class Both {
    def foo(a) { "single param: $a" }
    def foo(a, ... b) { "vararg param: $a, $b" }
}

println new Both().foo()
// unexpected:
// MethodSelectionException: Could not find which method foo() to invoke from 
this list:
//  public java.lang.Object Both#foo(java.lang.Object)
//  public transient java.lang.Object Both#foo(java.lang.Object, 
[Ljava.lang.Object;)
{code}
Here actually the first method selection fails, since there is no applicable 
method without argument. Because of RULE2 we then try foo(null). And unlike the 
optimization done, this was supposed to be a full method selection. That means 
we are looking at foo(Object) and foo(Object,Object...) to select from. Because 
of RULE1 foo(Object,Object...) has a penalty compared to foo(Object), as we 
need to do an expansion to fit the varargs call.

This means a correct implementation should not throw an MSE, it should have 
selected foo(Object) with null as argument.



> Unexpected MethodSelectionException with implicit null argument
> ---------------------------------------------------------------
>
>                 Key: GROOVY-8660
>                 URL: https://issues.apache.org/jira/browse/GROOVY-8660
>             Project: Groovy
>          Issue Type: Bug
>    Affects Versions: 3.0.0-alpha-2, 2.4.15, 2.5.0
>            Reporter: Daniil Ovchinnikov
>            Priority: Major
>              Labels: varargs
>
> {code:groovy}
> class OnlySingle {
>     def foo(a) { "single param: $a" }
> }
> println new OnlySingle().foo()
> // as expected: 'single param: null'
> class OnlyVararg {
>     def foo(a, ... b) { "vararg param: $a, $b" }
> }
> println new OnlyVararg().foo()
> // as expected: 'MME: No signature of method: OnlyVararg.foo() is applicable 
> for argument types: () values: []'
> class Both {
>     def foo(a) { "single param: $a" }
>     def foo(a, ... b) { "vararg param: $a, $b" }
> }
> println new Both().foo()
> // unexpected:
> // MethodSelectionException: Could not find which method foo() to invoke from 
> this list:
> //  public java.lang.Object Both#foo(java.lang.Object)
> //  public transient java.lang.Object Both#foo(java.lang.Object, 
> [Ljava.lang.Object;)
> {code}
> If the exception is expected then {{OnlyVararg}} case should work too.
> If the exception is unexpected then {{Both#foo(Object)}} should be selected.



--
This message was sent by Atlassian Jira
(v8.20.1#820001)

Reply via email to