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

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

codeconsole commented on PR #3089:
URL: https://github.com/apache/groovy/pull/3089#issuecomment-6608485133

   @paulk-asert Thanks. One set of rules for both PRs:
   
   1. **Literals only.** A closure literal, lambda or method reference written 
directly in a type-parameter slot that the call binds to a functional interface 
is converted, and so is a literal branch of a conditional (`flag ? { 'a' } : { 
'b' }`). A value typed `Closure` (a variable or a call result) is stored as on 
master: the same object, so `contains`, `remove`, `indexOf` and `equals` keep 
working, and reading it through the interface fails as on master. The type 
checker warns there, at `LIKELY_ERRORS` so groovyc, Gradle and IDEs show it, 
and suggests `as Supplier<String>`.
   
   2. **The closure class implements the interface**, as you suggest in 5. Its 
SAM method, declared with the parameterized signature, calls `doCall` the way 
the direct `call` methods of `StaticTypesClosureWriter` do, and the `Verifier` 
adds the erasure bridge (`Object apply(Object)` for `Function<String, 
String>`). `castToType` returns an instance of the target unchanged, so the 
stored value is the closure itself: `h.shout('hi')`, `.call()` and `.apply()` 
work. One gap: `ClosureMetaClass.invokeMethod` sees only `doCall` and 
`Closure`'s own methods, so `invokeMethod('apply', ...)`, a method pointer or a 
call inside `with` would reach the owner instead. I'll make it find the methods 
the closure class declares.
      Not converted, as on master: interfaces `Closure` already implements 
(`Runnable`, `Callable`, `GroovyCallable`), a SAM whose method name `Closure` 
has (`call`, `run`, `isCase`, `getProperty`, `invokeMethod`, ...), and 
abstract-class SAMs. `Supplier<String> s = { 'x' }` keeps its proxy; changing 
that is a separate issue.
   
   3. **Gating.** For closures, what works on master keeps working, apart from 
`STRICT` packing (6). The closure is now also an instance of the interface, 
which `instanceof` and dynamic overload choice (`m(Callable)` vs `m(Supplier)`) 
can see. I would not gate that. Lambdas and method references compile for the 
interface, as `Supplier<String> s = () -> 'y'` does. That is visible: on master 
a lambda in such a slot compiles to a closure class, so dynamic `l[0]()` and 
`l[0].call()` work; with the change they throw MissingMethodException, as they 
already do for the variable. I'd document that rather than gate it, unless you 
prefer a flag.
   
   4. **Metadata.** The type checker records the interface on the closure 
literal as its own `StaticTypesMarker`, only where the slot's erasure is not 
the interface. `StaticTypesClosureWriter.createClosureClass` and 
`ClosureWriter.chooseStrategy` read it. No cast is needed, so the writer cast, 
the varargs `castX`, the `BinaryExpressionTransformer` step, the named-argument 
metadata and the second `getFunctionalTypeArgument` all go. Lambdas and method 
references keep `PARAMETER_TYPE`, which their writers already read.
   
   5. **`@TypeChecked`.** There an argument for a type-parameter slot is passed 
as it is, so slot resolution runs only under `@CompileStatic`, in 
`StaticCompilationVisitor` as for GROOVY-12526. `@TypeChecked` keeps master's 
behaviour: `l.add { it.toUpperCase() }` and `l.add(String::toUpperCase)` stay 
compile errors, and a closure with declared parameter types or a lambda still 
compiles and fails at run time.
   
   6. **GEP-27.** Packing declines a literal carrying the marker, with its own 
reason in `declineReason`, as it does for intersection casts; under 
`@PackedClosures(mode = STRICT)` that is a new error where master packed the 
closure. Packing these sites needs an adapter that implements the interface, 
and the marker is that strategy's input. Tests use `@PackedClosures`, and I'll 
run the packages with `-Pgroovy.target.lambda.hoist=true 
-Pgroovy.target.closure.pack=true`.
      Separately, GROOVY-12650 (#3171): under `@CompileStatic` with 
`groovy.target.closure.pack=true`, master packs `list << { 1 }` and a 
setter-backed `holder.value = { 1 }`, because `BinaryExpressionTransformer` has 
turned them into calls before `collectEscapingClosures` walks the method. 
Dynamic code declines both.
   
   **Scope.** GROOVY-10979 is the method-reference error. #3089 keeps that: 
slot resolution under `@CompileStatic`, lambdas and method references. Closure 
literals, as arguments and in assignments, move to #3090, stacked on #3089, and 
I'll retitle GROOVY-12561 to cover both: one rule, one test class.
   
   Tests will pin: dynamic callers of a list and map filled by `@CompileStatic` 
code, including `invokeMethod`; closure variables unchanged, plus the warning; 
lambdas read by dynamic code; the `@TypeChecked` behaviour; the cases left 
unconverted; and packing and hoisting on and off. The change is documented 
under "Closure to type coercion" in `core-semantics.adoc`.
   
   Does that work for you?
   




> Cannot provide method reference to a type parameter instantiated with a SAM 
> type
> --------------------------------------------------------------------------------
>
>                 Key: GROOVY-10979
>                 URL: https://issues.apache.org/jira/browse/GROOVY-10979
>             Project: Groovy
>          Issue Type: Bug
>          Components: Static Type Checker
>            Reporter: Thodoris Sotiropoulos
>            Priority: Minor
>
> I have the following program
> {code}
> import java.util.function.*;
> import java.util.stream.*;
> public class Test {
>     public static void test() {
>       Stream<Number> x = null;
>       BiFunction<Function<String, Integer>, Number, Function<String, 
> Integer>> y = null;
>       BinaryOperator<Function<String, Integer>> z = null;
>       x.<Function<String, Integer>>reduce(Test::m, y, z);  // fails
>       x.reduce(Test::m, y, z);   // fails
>       x.reduce((d) -> 1, y, z);   // works
>     }
>     public static Integer m(String x) { return 1; }
> }
> {code}
> h3. Actual behavior
> {code}
> org.codehaus.groovy.control.MultipleCompilationErrorsException: startup 
> failed:
> test.groovy: 12: The argument is a method reference, but the parameter type 
> is not a functional interface
>  @ line 12, column 43.
>    ction<String, Integer>>reduce(Test::m, y
>                                  ^
> test.groovy: 13: The argument is a method reference, but the parameter type 
> is not a functional interface
>  @ line 13, column 16.
>          x.reduce(Test::m, y, z);   // fails
>                   ^
> 2 errors
> {code}
> h3. Expected behavior
> Compile successfully
> Tested against master (commit: 55c6edfd81a64ce759357a3976070c0811bff637)



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

Reply via email to