[
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)