[ 
https://issues.apache.org/jira/browse/GROOVY-12606?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18135606#comment-18135606
 ] 

ASF GitHub Bot commented on GROOVY-12606:
-----------------------------------------

codeconsole opened a new pull request, #3133:
URL: https://github.com/apache/groovy/pull/3133

   The static type checker rejects a closure or lambda that returns a 
`Closure<R>` value where the expected return type is a functional interface:
   
   ```groovy
   import java.util.function.Supplier
   
   @groovy.transform.CompileStatic
   class C {
       static String take(Supplier<Supplier<String>> s) { s.get().get() }
       static void main(args) {
           Closure<String> c = { 'x' }
           assert take { c } == 'x'   // Cannot return value of type 
groovy.lang.Closure<java.lang.String> for closure expecting 
java.util.function.Supplier<java.lang.String>
           assert take(() -> c) == 'x' // same, "for lambda expecting"
       }
   }
   ```
   
   4.0.33, 5.0.8 and 6.0.0 reject it the same way. The closure branch of 
`StaticTypeCheckingVisitor.checkReturnType` only allowed covariant returns. It 
now also accepts a Closure value when the expected type is a SAM type whose 
method returns void or a supertype of the Closure's type argument (a raw 
Closure is accepted). In that case it records the SAM type as the closure's 
return type. For closures this is enough: the closure method already declares 
the inferred return type, and the returned value gets a Groovy cast, which 
converts the Closure to the interface (as for GROOVY-9971).
   
   A statically compiled lambda's method declared the SAM method's erased 
return type, so the Closure was returned unconverted and the caller failed with 
a ClassCastException. `StaticTypesLambdaWriter` now declares the lambda's 
inferred return type when the SAM method returns a type variable and that 
inferred type is a SAM type, so the same Groovy cast is written at the return.
   
   A `Closure<Integer>` returned for `Supplier<String>` is still an error. This 
PR, #3113 (GROOVY-12588) and #3126 (GROOVY-12599) each add an 
`isClosureCoercibleToSAM` helper; whichever lands later should fold its copy 
into the one already on master.
   
   ## Tests
   
   `CoercionSTCTest.testCoerceClosureReturnToFunctionalInterface` fails without 
the change, under both `CoercionSTCTest` and `CoercionStaticCompileTests`. Its 
lambda cases fail under static compilation without the 
`StaticTypesLambdaWriter` change. 
`testCoerceClosureReturnToFunctionalInterface2` checks that mismatched return 
types are still rejected. The `groovy.transform.stc`, 
`org.codehaus.groovy.classgen.asm.sc`, `groovy.bugs` and `gls` tests pass. The 
Checkstyle and CodeNarc gates pass.
   




> STC rejects a closure or lambda returning a Closure<R> value where a 
> functional interface return type is expected
> -----------------------------------------------------------------------------------------------------------------
>
>                 Key: GROOVY-12606
>                 URL: https://issues.apache.org/jira/browse/GROOVY-12606
>             Project: Groovy
>          Issue Type: Bug
>    Affects Versions: 4.0.33, 5.0.8, 6.0.0
>            Reporter: Scott Murphy Heiberg
>            Priority: Major
>
> {code:groovy}
> import java.util.function.Supplier
> @groovy.transform.CompileStatic // or @TypeChecked
> class C {
>     static String take(Supplier<Supplier<String>> s) { s.get().get() }
>     static void main(args) {
>         Closure<String> c = { 'x' }
>         assert take { c } == 'x'
>         assert take(() -> c) == 'x'
>     }
> }
> {code}
> Actual:
> {noformat}
> [Static type checking] - Cannot return value of type 
> groovy.lang.Closure<java.lang.String> for closure expecting 
> java.util.function.Supplier<java.lang.String>
> [Static type checking] - Cannot return value of type 
> groovy.lang.Closure<java.lang.String> for lambda expecting 
> java.util.function.Supplier<java.lang.String>
> {noformat}
> Expected: both calls compile and the returned closure is converted to the 
> functional interface, as it is when a Closure value is passed as a Supplier 
> argument. A Closure<Integer> should still be rejected.
> The return check for closures and lambdas only accepts subtypes of the 
> expected return type and does not consider closure-to-SAM coercion. A 
> returned closure literal and a raw Closure are rejected the same way. Under 
> static compilation, a lambda also needs its generated method to declare the 
> SAM type, so that the returned closure is converted instead of being returned 
> as a Closure.
> Seen in 4.0.33, 5.0.8 and 6.0.0.



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

Reply via email to