[jira] [Commented] (GROOVY-8660) Unexpected MethodSelectionException with implicit null argument
[ https://issues.apache.org/jira/browse/GROOVY-8660?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel=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)
[jira] [Commented] (GROOVY-8660) Unexpected MethodSelectionException with implicit null argument
[ https://issues.apache.org/jira/browse/GROOVY-8660?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel=17364329#comment-17364329 ] Paul King commented on GROOVY-8660: --- [~blackdrag], you don't recall any more specifics do you? Otherwise I can try to refresh my memory by trawling through the mailing lists or commit history. > 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 > > {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.3.4#803005)
[jira] [Commented] (GROOVY-8660) Unexpected MethodSelectionException with implicit null argument
[ https://issues.apache.org/jira/browse/GROOVY-8660?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel=17364325#comment-17364325 ] Eric Milles commented on GROOVY-8660: - I only found out about this a couple weeks ago. As best as I can gather, the method selection (in MetaClassImpl) will translate "foo()" to "foo(null)" if there is only one method "foo" for the receiver type and the method has one non-primitive parameter. This extends to category methods, so a static category method will apply to "foo()" if is has one self parameter and one additional non-primitive parameter. In your "Both" case, the one method criterion is not met. I'm not sure why this exists and I think I have seen some discussion of removing it in MOP 2.0. But I think that effort is largely stalled. > 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 > > {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.3.4#803005)
[jira] [Commented] (GROOVY-8660) Unexpected MethodSelectionException with implicit null argument
[ https://issues.apache.org/jira/browse/GROOVY-8660?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel=17364269#comment-17364269 ] Daniil Ovchinnikov commented on GROOVY-8660: This should be documented somewhere. Last time I've checked, there was something like: "if a method can be called with a single null argument, then the argument can be omitted", there was no mention that the method must not have overloads or varargs. If {{new OnlyVararg().foo()}} fails => {{Both#foo(Object, [LObject;)}} must be inapplicable => method selection in {{new Both().foo()}} should try only {{Both#foo(Object)}} against {{null}} argument => {{Both#foo(Object)}} should be selected. Now let's reverse the above. If {{Both#foo(Object)}} is not selected => method selection in {{new Both().foo()}} actually sees both methods as applicable before trying them with null argument => {{Both#foo(Object, [LObject;)}} was applicable => {{new OnlyVararg().foo()}} must not fail. I'm having trouble understanding `org.codehaus.groovy.*` code. Can you describe me the algorithm in human words, please? > 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 > > {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.3.4#803005)
[jira] [Commented] (GROOVY-8660) Unexpected MethodSelectionException with implicit null argument
[ https://issues.apache.org/jira/browse/GROOVY-8660?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel=17363714#comment-17363714 ] Eric Milles commented on GROOVY-8660: - That said, I think the method selection is working as intended. Are you looking for a change in one of the cases? > 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 > > {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.3.4#803005)
[jira] [Commented] (GROOVY-8660) Unexpected MethodSelectionException with implicit null argument
[ https://issues.apache.org/jira/browse/GROOVY-8660?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel=17363712#comment-17363712 ] Eric Milles commented on GROOVY-8660: - The implicit null selection only works when there is a single method with one non-primitive parameter. It is the last case in {{org.codehaus.groovy.reflection.ParameterTypes#isValidMethod}}: {code:java} public boolean isValidMethod(Class[] arguments) { if (arguments == null) return true; final int size = arguments.length; CachedClass[] pt = getParameterTypes(); final int paramMinus1 = pt.length - 1; if (isVargsMethod && size >= paramMinus1) return isValidVarargsMethod(arguments, size, pt, paramMinus1); else if (pt.length == size) return isValidExactMethod(arguments, pt); else if (pt.length == 1 && size == 0 && !pt[0].isPrimitive) // here return true; return false; } {code} When there are multiple methods available and no arguments, selection goes through {{MetaClassHelper.chooseEmptyMethodParams}} in {{groovy.lang.MetaClassImpl#chooseMethodInternal}}, which just tries to execute a no-arg method instead of a varargs method if both are declared. > 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 > > {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.3.4#803005)