codeconsole opened a new pull request, #3126:
URL: https://github.com/apache/groovy/pull/3126
Static type checking accepted a value whose static type is `Closure<R>` when
it was passed as a method argument or returned from a method, and the target
was a parameterized functional interface whose method does not return `R`:
```groovy
@CompileStatic
class C {
static String take(Supplier<String> s) { s.get().toLowerCase() }
static Supplier<String> make() { Closure<Integer> c = { 1 }; c }
static void main(args) {
Closure<Integer> c = { 1 }
take(c) // ClassCastException: Integer cannot be
cast to String
make().get().toLowerCase() // ClassCastException
}
}
```
4.0.33, 5.0.8 and 6.0.0 behave the same. A closure literal in the same place
is rejected ("Cannot return value of type int for closure expecting
java.lang.String").
For an argument,
`StaticTypeCheckingSupport.typeCheckMethodArgumentWithGenerics` accepted any
`Closure` for a SAM-type parameter and assumed the return type had been checked
earlier. That holds only for closure literals. For a return,
`StaticTypeCheckingVisitor.checkReturnType` checked generics only when the
value's type implements the return type, which `Closure` does not for most SAM
types.
The new `StaticTypeCheckingSupport.isClosureCoercibleToSAM` accepts the
value when:
- the SAM method returns void;
- it returns a type that accepts `R`;
- it returns `String` and `R` is `GString`, matching what a closure literal
is allowed to do.
Both paths now use it. This includes a non-generic subclass of `Closure<R>`
passed as an argument (`class D extends Closure<Integer>`; `take(new D())`),
whose `R` is found through the superclass. A raw `Closure`, `Closure<Object>`
or unresolved `R` is accepted as before. Closure and lambda literals keep their
own return check. Method pointers and references are not checked here, because
the type of a pointer to an overloaded method such as `Math.&abs` names only
one of the overloads.
Mismatches are now reported as `Cannot call
C#take(java.util.function.Supplier<java.lang.String>) with arguments
[groovy.lang.Closure<java.lang.Integer>]` and `Cannot return value of type
groovy.lang.Closure<java.lang.Integer> for method returning
java.util.function.Supplier<java.lang.String>`.
A local variable initialised from a closure literal is flow-typed from it,
so `def c = { 1 }; take(c)` for `take(Supplier<String>)` is now rejected as
`Closure<Integer>`, as `Supplier<String> s = c` already is. `take((Closure) c)`
or `c as Supplier<String>` still compile.
This PR and #3113 (GROOVY-12588, assignment) each add an
`isClosureCoercibleToSAM` helper; whichever lands second should fold its helper
into the other.
## Tests
`CoercionSTCTest.testCoerceClosureArgumentToFunctionalInterface2` and
`testCoerceClosureReturnToFunctionalInterface2` fail without the change, under
both `@TypeChecked` and `@CompileStatic` (`CoercionStaticCompileTests`).
`testCoerceClosureArgumentToFunctionalInterface` and
`testCoerceClosureReturnToFunctionalInterface` cover the cases that stay
accepted. `groovy.transform.stc.*`, `org.codehaus.groovy.classgen.asm.sc.*`,
`groovy.bugs.*`, `gls.*`, `org.codehaus.groovy.transform.traitx.*`,
`groovy.lang.*` and `groovy.ClosureTest` pass. The Checkstyle and CodeNarc
gates pass.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]