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)