[
https://issues.apache.org/jira/browse/GROOVY-12757?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Scott Murphy Heiberg updated GROOVY-12757:
------------------------------------------
Description:
When a generic method of a generic receiver is called with a lambda whose
result names a type parameter of the calling method, and the receiver's class
has a type parameter of the same name, static type checking replaces the
caller's type parameter with the receiver's type argument:
{code:groovy}
import java.util.concurrent.CompletableFuture
import java.util.function.Function
@groovy.transform.CompileStatic
class C {
static <T> Optional<List<T>> wrap(Optional<String> value, T item) {
Optional<List<T>> result = value.map((String s) -> [item] as List<T>)
result
}
static <T> CompletableFuture<List<T>> each(CompletableFuture<List<Object>>
future, Function<Object, T> fn) {
CompletableFuture<List<T>> result = future.thenApply((List<Object>
values) -> values.collect { fn.apply(it) } as List<T>)
result
}
}
{code}
{noformat}
[Static type checking] - Incompatible generic argument types. Cannot assign
java.util.Optional<java.util.List<java.lang.String>> to:
java.util.Optional<java.util.List<T>>
[Static type checking] - Incompatible generic argument types. Cannot assign
java.util.concurrent.CompletableFuture<java.util.List<java.util.List<java.lang.Object>>>
to: java.util.concurrent.CompletableFuture<java.util.List<T>>
{noformat}
{{map}}'s {{U}} is inferred from the lambda as {{List<T>}}, with the calling
method's {{T}}, and then {{Optional}}'s own {{T}}, here {{String}}, is applied
to the result. Groovy before GROOVY-12714 inferred the same wrong type, but its
assignment check accepted it. Renaming the calling method's type parameter
avoids the problem, and javac accepts the equivalent Java.
{{StaticTypeCheckingVisitor#inferReturnTypeGenerics}} resolves the method's
type parameters from the arguments first, and then applies the receiver's type
arguments to the result. Applying the receiver's type arguments to the declared
return type first keeps them away from the types the arguments supply.
{{extractPlaceHoldersVisibleToDeclaration}} already leaves out the method's own
type parameters (GROOVY-10322), so a method type parameter that shadows the
class's is unaffected.
Grails' {{AwaitablePromiseFactory}}, which calls
{{groovy.concurrent.Awaitable#then}} that way, no longer compiles against
GROOVY_6_0_X because of this.
Pull request: https://github.com/apache/groovy/pull/3290
was:
When a generic method of a generic receiver is called with a lambda whose
result names a type parameter of the calling method, and the receiver's class
has a type parameter of the same name, static type checking replaces the
caller's type parameter with the receiver's type argument:
{code:groovy}
import java.util.concurrent.CompletableFuture
import java.util.function.Function
@groovy.transform.CompileStatic
class C {
static <T> Optional<List<T>> wrap(Optional<String> value, T item) {
Optional<List<T>> result = value.map((String s) -> [item] as List<T>)
result
}
static <T> CompletableFuture<List<T>> each(CompletableFuture<List<Object>>
future, Function<Object, T> fn) {
CompletableFuture<List<T>> result = future.thenApply((List<Object>
values) -> values.collect { fn.apply(it) } as List<T>)
result
}
}
{code}
{noformat}
[Static type checking] - Incompatible generic argument types. Cannot assign
java.util.Optional<java.util.List<java.lang.String>> to:
java.util.Optional<java.util.List<T>>
[Static type checking] - Incompatible generic argument types. Cannot assign
java.util.concurrent.CompletableFuture<java.util.List<java.util.List<java.lang.Object>>>
to: java.util.concurrent.CompletableFuture<java.util.List<T>>
{noformat}
{{map}}'s {{U}} is inferred from the lambda as {{List<T>}}, with the calling
method's {{T}}, and then {{Optional}}'s own {{T}}, here {{String}}, is applied
to the result. Groovy before GROOVY-12714 inferred the same wrong type, but its
assignment check accepted it. Renaming the calling method's type parameter
avoids the problem, and javac accepts the equivalent Java.
{{StaticTypeCheckingVisitor#inferReturnTypeGenerics}} resolves the method's
type parameters from the arguments first, and then applies the receiver's type
arguments to the result. Applying the receiver's type arguments to the declared
return type first keeps them away from the types the arguments supply.
{{extractPlaceHoldersVisibleToDeclaration}} already leaves out the method's own
type parameters (GROOVY-10322), so a method type parameter that shadows the
class's is unaffected.
Grails' {{AwaitablePromiseFactory}}, which calls
{{groovy.concurrent.Awaitable#then}} that way, no longer compiles against
GROOVY_6_0_X because of this.
> STC: a lambda result naming a type parameter of the calling method takes the
> receiver's type argument for that name
> -------------------------------------------------------------------------------------------------------------------
>
> Key: GROOVY-12757
> URL: https://issues.apache.org/jira/browse/GROOVY-12757
> Project: Groovy
> Issue Type: Bug
> Components: Static Type Checker
> Affects Versions: 6.0.0
> Reporter: Scott Murphy Heiberg
> Priority: Major
>
> When a generic method of a generic receiver is called with a lambda whose
> result names a type parameter of the calling method, and the receiver's class
> has a type parameter of the same name, static type checking replaces the
> caller's type parameter with the receiver's type argument:
> {code:groovy}
> import java.util.concurrent.CompletableFuture
> import java.util.function.Function
> @groovy.transform.CompileStatic
> class C {
> static <T> Optional<List<T>> wrap(Optional<String> value, T item) {
> Optional<List<T>> result = value.map((String s) -> [item] as List<T>)
> result
> }
> static <T> CompletableFuture<List<T>>
> each(CompletableFuture<List<Object>> future, Function<Object, T> fn) {
> CompletableFuture<List<T>> result = future.thenApply((List<Object>
> values) -> values.collect { fn.apply(it) } as List<T>)
> result
> }
> }
> {code}
> {noformat}
> [Static type checking] - Incompatible generic argument types. Cannot assign
> java.util.Optional<java.util.List<java.lang.String>> to:
> java.util.Optional<java.util.List<T>>
> [Static type checking] - Incompatible generic argument types. Cannot assign
> java.util.concurrent.CompletableFuture<java.util.List<java.util.List<java.lang.Object>>>
> to: java.util.concurrent.CompletableFuture<java.util.List<T>>
> {noformat}
> {{map}}'s {{U}} is inferred from the lambda as {{List<T>}}, with the calling
> method's {{T}}, and then {{Optional}}'s own {{T}}, here {{String}}, is
> applied to the result. Groovy before GROOVY-12714 inferred the same wrong
> type, but its assignment check accepted it. Renaming the calling method's
> type parameter avoids the problem, and javac accepts the equivalent Java.
> {{StaticTypeCheckingVisitor#inferReturnTypeGenerics}} resolves the method's
> type parameters from the arguments first, and then applies the receiver's
> type arguments to the result. Applying the receiver's type arguments to the
> declared return type first keeps them away from the types the arguments
> supply. {{extractPlaceHoldersVisibleToDeclaration}} already leaves out the
> method's own type parameters (GROOVY-10322), so a method type parameter that
> shadows the class's is unaffected.
> Grails' {{AwaitablePromiseFactory}}, which calls
> {{groovy.concurrent.Awaitable#then}} that way, no longer compiles against
> GROOVY_6_0_X because of this.
> Pull request: https://github.com/apache/groovy/pull/3290
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)