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

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

paulk-asert commented on PR #3089:
URL: https://github.com/apache/groovy/pull/3089#issuecomment-6578808467

   Following on from my earlier comment: there's some overlap with the GEP-27 
work (GROOVY-12143 lambda hoisting, GROOVY-12151 closure packing, and the 
Groovy 7 follow-up in #2714) that may help shape these two PRs.
   
   **1. Only change a closure's representation where nobody can tell the 
difference.**
   Closure packing changes what a closure literal compiles to only when the 
compiler can prove the change can't be seen. It also declines closures that 
visibly escape their method (`ClosureWriter.escapesEnclosingMethod`): stores to 
an index/property/field, `<<`, `return`, and list/map literals. Those are 
exactly the sites #3090 converts, plus `<<` in this PR. So by that rule, these 
PRs change the representation exactly where the difference is visible. That's 
why dynamic callers break (`h.shout('hi')` and `.call()` on a map filled by 
`@CompileStatic` code) and closure variables lose identity (`contains`, 
`remove`, `indexOf`, one-way `equals`).
   
   Applying the same rule: convert closure **literals** only (nothing else 
holds a reference, so identity isn't an issue), and leave values already typed 
`Closure` unconverted, or at least report them.
   
   **2. Gate changes that callers can observe.**
   Lambda hoisting and closure packing both ship opt-in 
(`groovy.target.lambda.hoist`, `groovy.target.closure.pack`). Packing also has 
`@PackedClosures` with `LENIENT`/`WARN`/`STRICT`/`DISABLED` modes, a `.report` 
flag giving the reason for each decline, and the remaining differences written 
up in its javadoc. These PRs change runtime behaviour unconditionally. I'd 
suggest either limiting them to cases where nothing observable changes (point 
1), or gating and documenting them the same way.
   
   **3. Record the resolved interface as its own piece of metadata.**
   `PackStrategy` already anticipates a LambdaMetafactory-based strategy for 
closures with a known functional-interface target. The type-checker work here 
(resolving `E` of `add(E)` to `Supplier<String>`) is exactly what that strategy 
needs as input. But the conversion used now, a runtime `castToType` producing a 
new `ConvertedClosure` proxy each time, is the slowest representation available.
   
   Lambdas already show what's possible. With 
`-Dgroovy.target.lambda.hoist=true`, `l.add(() -> 'y')` on a 
`List<Supplier<String>>`:
   - on master compiles to a Closure class and fails with ClassCastException;
   - with this PR (and #3090) merged, it is hoisted and generates no class at 
all.
   
   So rather than reusing `PARAMETER_TYPE` (which lambdas, method references 
and already-functional parameters also use, hence the second 
`getFunctionalTypeArgument` in `StaticInvocationWriter`), it would help to 
record "this literal's target is interface T" as a separate fact. A transformer 
step could insert the cast (as #3090 and `optimizeArrayCollectionAssignment` 
do) and a future metafactory strategy could use the same fact.
   
   **4. Interaction with packing goes both ways.**
   With `groovy.target.closure.pack=true`, `list.add { 'x' }` is packed (method 
arguments aren't in the escape list) and this PR then wraps it in a proxy. It 
works, but it isn't tested. Going the other way: once a stored closure is 
converted to an interface, packing's reason for declining escaping closures (a 
delegate could be set later) no longer applies, so a shared "target interface" 
fact could let #3090's sites pack too. Neither PR is currently exercised with 
the GEP-27 flags. `org.apache.groovy-tested.gradle` already forwards both, so 
`-Pgroovy.target.lambda.hoist=true -Pgroovy.target.closure.pack=true` would 
cover that cheaply.
   
   **5. Possible fix for dynamic callers.**
   #2714's `OpenClosure.AsClosure` keeps the full Closure contract as a wrapper 
over a lighter core. Going the other way, a converted literal could be a value 
that is a Closure *and* implements the interface: the generated closure class 
(or a packed adapter variant) implements `Supplier` with a bridge `get()` 
calling `doCall()`. Then `h.shout('hi')`, `.call()` and `.apply()` all keep 
working and identity is preserved. Closure variables would still need a proxy, 
which is another reason to restrict conversion to literals.
   
   None of this needs to block fixing the ClassCastException. But it would be 
good to pick the semantics (literals only? gated?) and the metadata shape now, 
so this lines up with the GEP-27 direction rather than having to be undone for 
Groovy 7.




> 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