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]