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?
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to