This is an automated email from the ASF dual-hosted git repository.
paulk-asert pushed a commit to branch asf-site
in repository https://gitbox.apache.org/repos/asf/groovy-website.git
The following commit(s) were added to refs/heads/asf-site by this push:
new 7b3bd4b updated with recent changes for GROOVY-12151
7b3bd4b is described below
commit 7b3bd4b1e5388621fad311760fa3ad353cb75120
Author: Paul King <[email protected]>
AuthorDate: Thu Jul 16 19:10:17 2026 +1000
updated with recent changes for GROOVY-12151
---
site/src/site/wiki/GEP-27.adoc | 34 ++++++++++++++++++++++++++++++++++
1 file changed, 34 insertions(+)
diff --git a/site/src/site/wiki/GEP-27.adoc b/site/src/site/wiki/GEP-27.adoc
index 94a4bed..d321181 100644
--- a/site/src/site/wiki/GEP-27.adoc
+++ b/site/src/site/wiki/GEP-27.adoc
@@ -412,6 +412,39 @@ owner-correct retargeting (Groovy 7) — but the trivial
subset need not wait fo
`{ x -> x + 1 }` cannot misbehave — but exploiting that for nested closures
needs owner-correct
retargeting, see _Reference implementation_.)
+=== The packability decision procedure
+
+The complete boundary, as implemented (`ClosureWriter#chooseStrategy`; any
decline keeps the
+closure a generated class exactly as today, and is what `WARN`/`STRICT` — or
+`groovy.target.closure.pack.report=true` on the flag path — reports with a
reason):
+
+. *Real-`Closure` semantics?* References
`owner`/`delegate`/`thisObject`/`resolveStrategy`/
+ `metaClass`/`super`, has default parameter values, or contains an anonymous
inner class → decline.
+. *Scope opted out?* The most-specific `@PackedClosures` mode wins (method
over class);
+ `DISABLED` beats an enclosing opt-in and the flag → decline.
+. *Triggered and sound?* One of: the annotation (dynamic trust + runtime
guard); the flag plus the
+ syntactic no-free-name proof (dynamic — any implicit-this call or property,
field/property-bound
+ or dynamic bare name, or explicit `this`-property declines); the flag or
annotation plus the type
+ checker's delegate-independence proof (`@CompileStatic` — which also
declines names a
+ type-checking extension left to runtime resolution, and `this`-properties of
`Map`-implementing
+ owners). Otherwise → decline.
+. *Structural position:* written directly in a method of the owner class;
nested closures await
+ owner-correct retargeting (Groovy 7) → decline.
+. *Visible escape:* stored to a field/property/index, returned, appended with
`<<`, or placed in a
+ collection literal → decline.
+. *Visibly serialization-bound:* cast/coerced to a `Serializable` type, or
passed to `writeObject`
+ directly or via a literal-holding local → decline.
+. *Field initialiser:* a class-level `def x = { … }` (a field store outside
any method's visible
+ code) → decline.
+. *Intersection cast:* the generated class would need per-literal marker
interfaces → decline.
+. *Trait body:* trait methods live in `$Trait$Helper` with a synthetic
receiver → decline.
+. *Constructor special call:* an argument to `this(...)`/`super(...)`, where
`this` is not yet
+ initialised → decline.
+
+Everything that passes packs. The matrix is pinned as executable documentation
by
+`PackedClosureBoundariesTest` (18 shapes × both compilation modes, plus the
special-context
+shapes), and each gate has focused behavioural tests besides.
+
== Groovy 6.0 deliverables
Groovy 6 lands in two parts: the *merged* sound-by-construction SAM-lambda
slice (pieces 1–2), and
@@ -616,6 +649,7 @@ means byte-for-byte today's behaviour.
|Closure packing under `@CompileStatic` (Groovy 6 stretch, GROOVY-12151)
|`groovy.target.closure.pack` (+ per-scope `@PackedClosures`) |off (opt-in);
default-on is Groovy 7
|Closure packing under dynamic compilation, free-name closures
|`@PackedClosures` opt-in only |off unless annotated
|Closure packing under dynamic compilation, no-free-name subset
|`groovy.target.closure.pack` (syntactic proof) |off (opt-in)
+|Decline reporting on the flag path |`groovy.target.closure.pack.report` —
every non-packed closure gets a compiler warning naming the gate that declined
it |off
|===
`@PackedClosures` carries a `mode` element (`LENIENT` | `WARN` | `STRICT` |
`DISABLED`, default