Paul King created GROOVY-12748:
----------------------------------

             Summary: STC: an implicit-parameter closure is reported ambiguous 
between functional interfaces of different arity when its call is type checked 
a second time
                 Key: GROOVY-12748
                 URL: https://issues.apache.org/jira/browse/GROOVY-12748
             Project: Groovy
          Issue Type: Bug
            Reporter: Paul King


Under {{@TypeChecked}} / {{@CompileStatic}}, a method overloaded on functional 
interfaces that take different numbers of parameters accepts a closure with an 
implicit parameter: GROOVY-10905 prefers the interface whose abstract method 
takes none or one parameter. When the same call is type checked a second time, 
the preference is lost and the call is reported as ambiguous, although nothing 
about it has changed.

{code:groovy}
import java.util.function.*

class C {
    static String m(Function<Integer, Integer> f) { 'function' }
    static String m(BinaryOperator<Integer> f) { 'binary' }
}

@groovy.transform.CompileStatic
def test() {
    assert C.m { it } == 'function'   // fine

    def x = 1
    for (i in 0..1) {
        assert C.m { it } == 'function'   // [Static type checking] - Reference 
to method is ambiguous. Cannot choose between [java.lang.String 
C#m(java.util.function.Function<java.lang.Integer, java.lang.Integer>), 
java.lang.String C#m(java.util.function.BinaryOperator<java.lang.Integer>)]
        x = 'a'
    }
}
test()
{code}

Dynamic Groovy chooses {{Function}} in every case.

h3. When a call is checked again

* the body of a {{for}} or {{while}} loop, or of a closure, in which a variable 
changes type, is checked a second time;
* a method that is not found on the result of the call, e.g. {{C.m \{ it 
\}.length()}}, makes the type checker check the call again while looking for a 
property of that name. The expected error (_Cannot find matching method 
java.lang.Object#length()_) is then accompanied by the misleading ambiguity 
error.

h3. Cause

The first time the call is checked, the closure has not been visited yet, and 
{{StaticTypeCheckingVisitor.getType}} builds its type with 
{{CLOSURE_ARGUMENTS}} metadata giving its parameter count (-1 for an implicit 
parameter), which {{StaticTypeCheckingSupport.getDistance}} uses to prefer the 
matching interface. Visiting the closure then stores {{Closure<R>}} as its 
inferred type, without that metadata, and {{getType}} returns the stored type 
from then on. Without the parameter count, both overloads are at the same 
distance.

Closures with declared parameters and method references are not affected on 
master, as the arity check added by GROOVY-10714 / GROOVY-11010 is recomputed 
every time; before that, a closure with declared parameters failed in the same 
way inside a loop.

h3. Versions

Seen in 5.1.3 and 6.0.0. 4.0.33 does not have the GROOVY-10905 preference and 
reports such calls as ambiguous even the first time.

h3. Fix

Store the parameter count on the closure's inferred type too, so that it is 
used however often the call is checked.



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to