This is an automated email from the ASF dual-hosted git repository.
asf-gitbox-commits pushed a commit to branch asf-site
in repository https://gitbox.apache.org/repos/asf/groovy-dev-site.git
The following commit(s) were added to refs/heads/asf-site by this push:
new 88360f5 2026/05/14 10:08:12: Generated dev website from
groovy-website@9bd7be5
88360f5 is described below
commit 88360f56ce9194d98d4347681c4fee90e67ae128
Author: jenkins <[email protected]>
AuthorDate: Thu May 14 10:08:12 2026 +0000
2026/05/14 10:08:12: Generated dev website from groovy-website@9bd7be5
---
search/search-index.json | 4 +-
wiki/GEP-13.html | 613 +++++++++++++++++++++++++++++++++++++++++------
wiki/geps.html | 2 +-
3 files changed, 545 insertions(+), 74 deletions(-)
diff --git a/search/search-index.json b/search/search-index.json
index 9b38cb2..5af3119 100644
--- a/search/search-index.json
+++ b/search/search-index.json
@@ -758,7 +758,7 @@
{
"id": "wiki/GEP-13.html",
"title": "The Apache Groovy programming language - Developer docs -
GEP-13",
- "content": "The Apache Groovy programming language - Developer docs -
GEP-13 Socialize Discuss on the mailing list Groovy on X Groovy on Bluesky
Groovy on Mastodon Groovy on LinkedIn Events and conferences Source code on
GitHub Report issues in Jira Stack Overflow questions Slack Community You are
using an outdated browser. Please upgrade your browser to improve your
experience. Apache Groovy™ Learn Documentation Download Support
Contribute Ecosystem Blog posts Socialize GE [...]
+ "content": "The Apache Groovy programming language - Developer docs -
GEP-13 Socialize Discuss on the mailing list Groovy on X Groovy on Bluesky
Groovy on Mastodon Groovy on LinkedIn Events and conferences Source code on
GitHub Report issues in Jira Stack Overflow questions Slack Community You are
using an outdated browser. Please upgrade your browser to improve your
experience. Apache Groovy™ Learn Documentation Download Support
Contribute Ecosystem Blog posts Socialize GE [...]
"url": "wiki/GEP-13.html",
"site": "dev"
},
@@ -884,7 +884,7 @@
{
"id": "wiki/geps.html",
"title": "The Apache Groovy programming language - GEPs",
- "content": "The Apache Groovy programming language - GEPs Socialize
Discuss on the mailing list Groovy on X Groovy on Bluesky Groovy on Mastodon
Groovy on LinkedIn Events and conferences Source code on GitHub Report issues
in Jira Stack Overflow questions Slack Community You are using an outdated
browser. Please upgrade your browser to improve your experience. Apache
Groovy™ Learn Documentation Download Support Contribute Ecosystem Blog
posts Socialize GEPs GEP-1 GEP-2 GEP- [...]
+ "content": "The Apache Groovy programming language - GEPs Socialize
Discuss on the mailing list Groovy on X Groovy on Bluesky Groovy on Mastodon
Groovy on LinkedIn Events and conferences Source code on GitHub Report issues
in Jira Stack Overflow questions Slack Community You are using an outdated
browser. Please upgrade your browser to improve your experience. Apache
Groovy™ Learn Documentation Download Support Contribute Ecosystem Blog
posts Socialize GEPs GEP-1 GEP-2 GEP- [...]
"url": "wiki/geps.html",
"site": "dev"
},
diff --git a/wiki/GEP-13.html b/wiki/GEP-13.html
index 739e8ba..15462fe 100644
--- a/wiki/GEP-13.html
+++ b/wiki/GEP-13.html
@@ -63,7 +63,7 @@
</ul>
</div>
</div>
- </div><div id='content' class='page-1'><div
class='row'><div class='row-fluid'><div class='col-lg-3'><ul
class='nav-sidebar'><li><a href='geps.html'>GEP index</a></li><li
class='active'><a href='#doc'>GEP-13</a></li><li><a
href='#_abstract_sealed_classes' class='anchor-link'>Abstract: Sealed
classes</a></li><li><a href='#_references_and_useful_links'
class='anchor-link'>References and useful links</a></li><li><a
href='#_update_history' class='anchor-link'>Update h [...]
+ </div><div id='content' class='page-1'><div
class='row'><div class='row-fluid'><div class='col-lg-3'><ul
class='nav-sidebar'><li><a href='geps.html'>GEP index</a></li><li
class='active'><a href='#doc'>GEP-13</a></li><li><a href='#_abstract'
class='anchor-link'>Abstract</a></li><li><a href='#_motivation'
class='anchor-link'>Motivation</a></li><li><a href='#_specification'
class='anchor-link'>Specification</a></li><li><a href='#_differences_from_java'
class='anchor- [...]
<div class="sectionbody">
<div class="sidebarblock">
<div class="content">
@@ -83,7 +83,7 @@
<strong>Title</strong>
</td>
<td class="hdlist2">
-<p>Sealed classes</p>
+<p>Sealed Types</p>
</td>
</tr>
<tr>
@@ -91,7 +91,7 @@
<strong>Version</strong>
</td>
<td class="hdlist2">
-<p>1</p>
+<p>2</p>
</td>
</tr>
<tr>
@@ -107,7 +107,7 @@
<strong>Status</strong>
</td>
<td class="hdlist2">
-<p>Draft</p>
+<p>Final</p>
</td>
</tr>
<tr>
@@ -131,7 +131,7 @@
<strong>Last modification</strong>
</td>
<td class="hdlist2">
-<p>2021-07-22</p>
+<p>2026-05-05</p>
</td>
</tr>
</table>
@@ -141,129 +141,551 @@
</div>
</div>
<div class="sect1">
-<h2 id="_abstract_sealed_classes">Abstract: Sealed classes</h2>
+<h2 id="_abstract">Abstract</h2>
<div class="sectionbody">
<div class="paragraph">
-<p>Sealed classes and interfaces restrict which other classes or interfaces
may extend or implement them.
-By supporting sealed classes and interfaces, the Groovy programming language
-can offer an additional mechanism for controlling class hierarchy
construction.</p>
+<p>Sealed types — classes, interfaces, and traits — restrict the set of
+permitted direct subtypes. They sit between the unconstrained
+extensibility of public types and the absolute closure of <code>final</code>,
+enabling enumerable hierarchies suitable for algebraic data type
+modelling, compiler-checked exhaustiveness analysis, and stable API
+design where the implementation set is intentionally bounded.</p>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_motivation">Motivation</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>Inheritance is a powerful mechanism but is binary as expressed by the
+existing modifiers: a public, non-final class invites unbounded
+extension; <code>final</code> prevents all of it. Visibility modifiers
+(<code>protected</code>, package-private) constrain extension at the cost of
+losing the parent type as a public abstraction.</p>
+</div>
+<div class="paragraph">
+<p>Sealed types fill the gap. A sealed type is publicly accessible — it
+can be a method parameter type, a switch selector, a return type — but
+the set of types that may extend or implement it is explicitly
+enumerated. Code receiving a value of a sealed type knows the
+exhaustive list of its possible runtime shapes.</p>
</div>
-<div class="sect2">
-<h3 id="_motivation">Motivation</h3>
<div class="paragraph">
-<p>Inheritance is a powerful mechanism for creating hierarchies of related
class and interfaces.
-Sometimes, it is desirable to restrict the definition of children in such
hierarchies.
-Modifiers already provide some mechanisms:</p>
+<p>This makes sealed types the type-system foundation for several
+adjacent features:</p>
</div>
<div class="ulist">
<ul>
<li>
-<p>If all of our classes and interfaces are public, this indicates that we want
-maximum reuse.</p>
+<p>algebraic data type modelling, particularly when combined with records;</p>
</li>
<li>
-<p>The <code>final</code> modifier offers one mechanism for restricting
further inheritance at the method or class level.
-It effectively limits all further extension and indicates no further code
reuse is desired.</p>
+<p>compiler-checked exhaustiveness in structural switch (a potential topic for
GEP-19);</p>
</li>
<li>
-<p>By making a base class package-private we can limit extension to only
classes within
-the same package. If an abstract <code>Shape</code> class is package-private,
we could have
-public classes <code>Square</code> and <code>Circle</code> in the same
package. This indicates that we want
-code reuse to occur only within the package. While it does limit creation of
-new shapes outside the original package, it offers no abstraction for a shape
which
-could be either a square or circle since <code>Shape</code> is not public.</p>
-</li>
-<li>
-<p>We can use <code>protected</code> visibility to limit access of members
strictly to children
-but that doesn’t help us solve the aforementioned problems like lack of
a visible
-abstraction for <code>Shape</code> in the discussed example.</p>
+<p>stable APIs whose implementation set is intentionally closed.</p>
</li>
</ul>
</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_specification">Specification</h2>
+<div class="sectionbody">
+<div class="sect2">
+<h3 id="_sealed_type_declarations">Sealed type declarations</h3>
<div class="paragraph">
-<p>Sealed classes or interfaces can be public but have an associated list of
allowed children.
-Classes or interfaces which are not in that list cannot inherit from those
sealed types.
-This indicates that we want code reuse within the hierarchy but not beyond.
-Parent classes in the hierarchy can be made <em>accessible</em>, without also
making them <em>extensible</em>.
-This allows hierarchies to be created with maximum reuse within but without
having
-to defensively code for arbitrary extensions added at a later time.</p>
+<p>A class, interface, or trait is sealed by declaring it with the
+<code>sealed</code> modifier and an optional <code>permits</code> clause:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">sealed interface
Shape permits Circle, Square, Triangle {}
+
+sealed class Vehicle permits Car, Truck, Motorcycle {}
+
+sealed trait Auditable permits LedgerEntry, JournalEntry {}</code></pre>
+</div>
</div>
<div class="paragraph">
-<p>Such classes are useful in defining Algebraic Data Types (ADTs) and in
scenarios where
-we might want to reason about whether we have accounted for all possible
types, e.g. the
-static compiler may wish to give a warning if a switch block doesn’t
exhaustively
-cover all possible types by respective case branches.</p>
+<p>Enums and annotation definitions cannot be sealed.</p>
+</div>
+<div class="paragraph">
+<p>The annotation form <code>@Sealed</code> is provided as an equivalent
surface for
+environments where keyword grammar is inconvenient:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code
data-lang="groovy">@Sealed(permittedSubclasses = [Circle, Square, Triangle])
+interface Shape {}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>The keyword and annotation forms are interchangeable.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_the_permits_clause">The <code>permits</code> clause</h3>
+<div class="paragraph">
+<p>The <code>permits</code> clause enumerates the direct subtypes permitted to
+extend or implement the sealed type. A permitted subtype must:</p>
</div>
-<div class="sect3">
-<h4 id="_initial_implementation">Initial implementation</h4>
<div class="ulist">
<ul>
<li>
-<p>Provide a <code>@Sealed</code> marker annotation or AST transform which
allows a list of
-permitted children to be defined. Use of this annotation will be an incubating
-feature subject to change. Explicit use may eventually be discouraged and
instead
-a keyword, e.g. <code>sealed</code> would be encouraged instead. However, the
annotation
-could be retained to offer support for this feature on earlier JVMs or versions
-of Groovy prior to any grammar changes.</p>
+<p>declare the sealed type as a direct supertype (via <code>extends</code> or
<code>implements</code>);</p>
</li>
<li>
-<p>Prohibit extension of JDK17+ sealed classes or annotated
<code>@Sealed</code> classes.
-This also applies for interfaces, anonymous inner classes and traits.</p>
+<p>be accessible to the compiler when the sealed type is loaded;</p>
</li>
<li>
-<p>Provide checks in other places where such extension might occur implicitly,
e.g.: with <code>@Delegate</code>,
-when using type coercion, etc.</p>
+<p>not be the sealed type itself (self-references are rejected at compile
time).</p>
</li>
+</ul>
+</div>
+<div class="sect3">
+<h4 id="_inference">Inference</h4>
+<div class="paragraph">
+<p>If the <code>permits</code> clause is omitted (and
<code>permittedSubclasses</code> is not
+set on <code>@Sealed</code>), the compiler infers permitted subtypes by
collecting
+all direct subtypes declared in the same compilation unit. If no such
+subtypes exist, the declaration is treated as having an empty
<code>permits</code>
+set, equivalent to <code>final</code>.</p>
+</div>
+<div class="paragraph">
+<p>Inference does not consult other compilation units.</p>
+</div>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_permitted_subtype_obligations">Permitted-subtype obligations</h3>
+<div class="paragraph">
+<p>Every direct permitted subtype of a sealed type adopts one of three
+stances:</p>
+</div>
+<table class="tableblock frame-all grid-all stretch">
+<colgroup>
+<col style="width: 16.6666%;">
+<col style="width: 33.3333%;">
+<col style="width: 50.0001%;">
+</colgroup>
+<thead>
+<tr>
+<th class="tableblock halign-left valign-top">Stance</th>
+<th class="tableblock halign-left valign-top">Declaration</th>
+<th class="tableblock halign-left valign-top">Effect</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>final</code></p></td>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>final class C extends S</code></p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">C is
closed; no further subtypes.</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>sealed</code></p></td>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>sealed class C extends S permits
…​</code></p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">C
continues the closed hierarchy with its own enumerated subtypes.</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Non-sealed
(explicit)</p></td>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>non-sealed class C extends S</code> or
<code>@NonSealed</code></p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">C is open;
descendants of C are unconstrained and require no marker.</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Non-sealed
(implicit)</p></td>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>class C extends S</code> (no other sealed-related
modifier)</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Same
effect as explicit <code>non-sealed</code>.</p></td>
+</tr>
+</tbody>
+</table>
+<div class="sect3">
+<h4 id="_implicit_non_sealed_default">Implicit non-sealed default</h4>
+<div class="paragraph">
+<p>Where Java requires one of <code>final</code>, <code>sealed</code>, or
<code>non-sealed</code> to be
+stated, Groovy infers <code>non-sealed</code> when none is given. This is a
+deliberate divergence motivated by Groovy’s preference for terse
+declarations.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_propagation_past_a_non_sealed_boundary">Propagation past a non-sealed
boundary</h4>
+<div class="paragraph">
+<p>The <code>permits</code> constraint applies to <strong>direct</strong>
subtypes only. Once a
+non-sealed type appears in a hierarchy descending from a sealed root,
+that branch is unconstrained: descendants of a non-sealed type require
+no sealed-related modifier and are not <code>permits</code>-restricted by
virtue
+of the sealed ancestor.</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">sealed interface
Shape permits Polygon, Circle {}
+final class Circle implements Shape {}
+class Polygon implements Shape {} // implicit non-sealed
+class RegularPolygon extends Polygon {} // unrestricted
+class Hexagon extends RegularPolygon {} // unrestricted</code></pre>
+</div>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_constraint_applicability">Constraint applicability</h4>
+<div class="paragraph">
+<p>A class declared <code>non-sealed</code> (whether by keyword or
<code>@NonSealed</code>)
+must have a sealed direct parent. A class without a sealed parent
+cannot be marked <code>non-sealed</code>.</p>
+</div>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_restricted_identifiers">Restricted identifiers</h3>
+<div class="paragraph">
+<p><code>sealed</code>, <code>non-sealed</code>, and <code>permits</code> are
<strong>restricted identifiers</strong>,
+not reserved keywords. They retain their identifier meaning in
+expression and identifier positions, taking on grammatical meaning
+only in type-declaration contexts. Existing code using these names as
+identifiers continues to compile.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_anonymous_classes_traits_and_proxy_paths">Anonymous classes, traits,
and proxy paths</h3>
+<div class="paragraph">
+<p>The set of types extending or implementing a sealed type is enumerated
+in <code>permits</code>; no other type may do so. Specifically prohibited:</p>
+</div>
+<div class="ulist">
+<ul>
<li>
-<p>Support <code>non-sealed</code> or <code>unsealed</code> sub-hierarchies.
(See JEP-409)</p>
+<p>anonymous inner classes targeting a sealed type that does not permit
+the enclosing type;</p>
</li>
<li>
-<p>Allow the permitted subclasses to be inferred automatically just for the
case
-where the base and all permitted subclasses are in the same file. (See
JEP-409)</p>
+<p>traits not in the <code>permits</code> set;</p>
</li>
<li>
-<p>Introduce the <code>sealed</code> modifier and <code>permits</code> clause
in the grammar.</p>
+<p>coercion-generated proxies (<code>x as Bar</code> where <code>Bar</code> is
sealed and the
+source type is not permitted);</p>
</li>
<li>
-<p>By default, when running on JDK17+, sealed class information is added into
the bytecode.
-We refer to such classes as <em>native</em> sealed classes.</p>
+<p>delegate-generated subclasses (<code>@Delegate</code> targeting a sealed
type
+from a non-permitted enclosing class).</p>
</li>
+</ul>
+</div>
+<div class="paragraph">
+<p>These extension paths are checked at compile time; runtime proxy
+generation paths perform the same check at proxy-construction time.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_bytecode_representation">Bytecode representation</h3>
+<div class="paragraph">
+<p>Sealed types are represented in bytecode using one of two mechanisms,
+selected by <code>@SealedOptions(mode = …​)</code>:</p>
+</div>
+<table class="tableblock frame-all grid-all stretch">
+<colgroup>
+<col style="width: 25%;">
+<col style="width: 75%;">
+</colgroup>
+<thead>
+<tr>
+<th class="tableblock halign-left valign-top">Mode</th>
+<th class="tableblock halign-left valign-top">Behaviour</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>NATIVE</code></p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Emit
JVM-level sealed metadata (<code>ACC_SEALED</code> access flag and
+ <code>PermittedSubclasses</code> attribute). Requires <code>--target
17</code> or later.</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>EMULATE</code></p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Emit
<code>@Sealed</code> annotation only, recognised by the Groovy compiler.
+ Compatible with all target bytecode versions supported by the Groovy
+ compiler, but invisible to the Java compiler and to JVM-level sealed
+ checks.</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>AUTO</code> (default)</p></td>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>NATIVE</code> when target bytecode is 17 or later,
otherwise <code>EMULATE</code>.</p></td>
+</tr>
+</tbody>
+</table>
+<div class="paragraph">
+<p>The <code>alwaysAnnotate</code> attribute of <code>@SealedOptions</code>
(default <code>true</code>)
+controls whether the <code>@Sealed</code> annotation is also emitted alongside
+native metadata. Setting it to <code>false</code> suppresses the annotation for
+hierarchies that are confirmed JDK-17-only and want to avoid duplicate
+metadata.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_annotation_forms">Annotation forms</h3>
+<div class="paragraph">
+<p>The annotation forms parallel the keyword forms and exist for
+environments where keyword grammar is inconvenient.</p>
+</div>
+<table class="tableblock frame-all grid-all stretch">
+<colgroup>
+<col style="width: 25%;">
+<col style="width: 75%;">
+</colgroup>
+<thead>
+<tr>
+<th class="tableblock halign-left valign-top">Annotation</th>
+<th class="tableblock halign-left valign-top">Equivalent</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>@Sealed(permittedSubclasses =
[…​])</code></p></td>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>sealed …​ permits …​</code>.
<code>permittedSubclasses</code> defaults to the
+ inferred set when omitted.</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>@NonSealed</code></p></td>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>non-sealed</code>.</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>@SealedOptions(mode = …​, alwaysAnnotate =
…​)</code></p></td>
+<td class="tableblock halign-left valign-top"><p
class="tableblock">Fine-grained control over bytecode representation; no keyword
+ equivalent.</p></td>
+</tr>
+</tbody>
+</table>
+<div class="paragraph">
+<p><code>@Sealed</code> has <code>RUNTIME</code> retention;
<code>@NonSealed</code> and <code>@SealedOptions</code>
+have <code>SOURCE</code> retention.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_joint_compilation_and_decompiled_types">Joint compilation and
decompiled types</h3>
+<div class="paragraph">
+<p>Sealed type information flows correctly across joint Groovy/Java
+compilation:</p>
+</div>
+<div class="ulist">
+<ul>
<li>
-<p>By default, when running on earlier JDKs, an annotation is added to a class
to indicate that
-a class is sealed. Such classes will be recognized by Groovy 4+ compilers but
not by Java.</p>
+<p>a Groovy class extending a Java sealed class is checked against the
+Java type’s <code>permits</code> set;</p>
</li>
<li>
-<p>The <code>@SealedOptions</code> annotation has a <code>mode</code>
annotation attribute which can override the default behavior.</p>
+<p>a Java class extending a Groovy sealed class likewise honours the
+Groovy <code>permits</code> set;</p>
+</li>
+<li>
+<p>the implicit non-sealed rule applies on the Groovy side; Java’s
+explicit-modifier-required rule applies on the Java side; descendants
+on either side compute non-sealed status from the immediate parent’s
+sealed flag.</p>
</li>
</ul>
</div>
+<div class="paragraph">
+<p>For a class loaded from bytecode (without source available), non-sealed
+status is computed as: <strong>the parent is sealed</strong> AND <strong>this
type is neither
+final nor sealed</strong>. The JVM has no <code>non-sealed</code> flag, so
this derivation
+is the canonical algorithm.</p>
</div>
-<div class="sect3">
-<h4 id="_potential_extensions">Potential extensions</h4>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_differences_from_java">Differences from Java</h2>
+<div class="sectionbody">
+<table class="tableblock frame-all grid-all stretch">
+<colgroup>
+<col style="width: 33.3333%;">
+<col style="width: 33.3333%;">
+<col style="width: 33.3334%;">
+</colgroup>
+<thead>
+<tr>
+<th class="tableblock halign-left valign-top">Aspect</th>
+<th class="tableblock halign-left valign-top">Java</th>
+<th class="tableblock halign-left valign-top">Groovy</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Subtype
modifier</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">One of
<code>final</code> / <code>sealed</code> / <code>non-sealed</code> is
mandatory</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Defaults
to implicit <code>non-sealed</code> when none is given</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock">Compile-together enforcement</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Required:
all permitted subclasses must be available and compiled
+ together with the sealed parent</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Not
enforced; permitted-subclass references are resolved when
+ available</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Module
membership</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Permitted
subclasses must be in the same module (or in the unnamed
+ module within the same package)</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Not
enforced</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Annotation
form</p></td>
+<td class="tableblock halign-left valign-top"><p
class="tableblock">None</p></td>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>@Sealed</code>, <code>@NonSealed</code>,
<code>@SealedOptions</code> are equivalent surface
+ forms</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock">Earlier-JDK targets</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Not
supported; sealed metadata requires JDK 17+ bytecode</p></td>
+<td class="tableblock halign-left valign-top"><p
class="tableblock">Annotation-based emulation works on earlier target bytecode
and is
+ recognised by the Groovy compiler (invisible to the Java compiler
+ and to JVM-level sealed checks)</p></td>
+</tr>
+</tbody>
+</table>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_pattern_matching_integration">Pattern matching integration</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>Sealed types provide the closed-set semantics that compiler-checked
+exhaustiveness analysis requires. A switch over a value of a sealed
+type whose arms cover every permitted subtype is, in principle,
+exhaustive without a <code>default</code> arm. Recursion into
sealed-or-<code>final</code>
+permitted subtypes contributes to exhaustiveness; a non-sealed
+permitted subtype requires a fallback arm.</p>
+</div>
+<div class="paragraph">
+<p>The switch surface that consumes sealed types — type patterns and
+record patterns over a sealed-typed selector — already works in
+Groovy. The remaining piece is the exhaustiveness analysis itself,
+which is a potential topic for <a href="GEP-19.html">GEP-19</a>. GEP-13
+contributes the type-system primitives; any consumer that performs
+exhaustiveness analysis operates as a compile-time check on the
+consumer side and does not change sealed-type semantics, on-disk
+representation, or public API. The severity of such consumer-side
+checking — warning, opt-in error, or default error — is a consumer
+decision and is not constrained by this GEP.</p>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_excluded_and_deferred_features">Excluded and deferred features</h2>
+<div class="sectionbody">
+<table class="tableblock frame-all grid-all stretch">
+<colgroup>
+<col style="width: 33.3333%;">
+<col style="width: 16.6666%;">
+<col style="width: 50.0001%;">
+</colgroup>
+<thead>
+<tr>
+<th class="tableblock halign-left valign-top">Feature</th>
+<th class="tableblock halign-left valign-top">Status</th>
+<th class="tableblock halign-left valign-top">Rationale</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Mandatory
compile-together enforcement</p></td>
+<td class="tableblock halign-left valign-top"><p
class="tableblock">Deferred</p></td>
+<td class="tableblock halign-left valign-top"><p
class="tableblock">Java’s hard requirement raises the cost of using
sealed types in
+ multi-module builds. May be surfaced as an opt-in compiler check.</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Mandatory
module-membership enforcement</p></td>
+<td class="tableblock halign-left valign-top"><p
class="tableblock">Deferred</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Same
reasoning; cross-module sealed hierarchies are occasionally
+ useful and a hard JPMS check would block them.</p></td>
+</tr>
+</tbody>
+</table>
+<div class="paragraph">
+<p>Any future tightening of these constraints is opt-in. See
+<em>Compatibility</em> for the stability commitment. Severity of consumer-side
+checks (such as exhaustiveness analysis on a structural switch) is out
+of scope for this GEP — see <em>Pattern matching integration</em>.</p>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_compatibility">Compatibility</h2>
+<div class="sectionbody">
+<div class="sect2">
+<h3 id="_public_api_surface">Public API surface</h3>
<div class="paragraph">
-<p>The following potential extensions are possibly all desirable but
-are non-goals for the first implementation:</p>
+<p>The following are part of Groovy’s public API and stable:</p>
</div>
<div class="ulist">
<ul>
<li>
-<p>Require that all classes within a sealed hierarchy be compiled at the same
time.</p>
+<p>the <code>sealed</code> / <code>non-sealed</code> / <code>permits</code>
grammar;</p>
+</li>
+<li>
+<p>the <code>@Sealed</code>, <code>@NonSealed</code>, and
<code>@SealedOptions</code> annotations,
+including their attributes and retention;</p>
</li>
<li>
-<p>Require that all classes within a sealed hierarchy belong to the same JPMS
module.</p>
+<p><code>ClassNode.isSealed()</code> and
<code>ClassNode.getPermittedSubclasses()</code>;</p>
</li>
<li>
-<p>Add warnings to the static compiler if a switch is used for a sealed
hierarchy
-and not all types are exhaustively covered.</p>
+<p>the public helpers in <code>SealedASTTransformation</code>.</p>
</li>
</ul>
</div>
+<div class="paragraph">
+<p>The annotations and public AST API shipped under <code>@Incubating</code> in
+Groovy 4.0 and remained incubating through the Groovy 5 line; they
+were promoted out of incubation in Groovy 6.0.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_stability_commitment">Stability commitment</h3>
+<div class="paragraph">
+<p>Sealed-type semantics — declaration syntax, the <code>permits</code>
contract,
+bytecode representation (native and emulated), public API surface,
+and joint-compilation interoperability — are stable. Programs that
+declare or extend sealed types under this specification continue to
+compile and produce equivalent bytecode across subsequent revisions.</p>
+</div>
+<div class="paragraph">
+<p>The deferred items in <em>Excluded and deferred features</em> — mandatory
+compile-together enforcement and mandatory module-membership
+enforcement — are subject to this commitment: if introduced, they
+are opt-in checks (compiler flag or <code>@SealedOptions</code> attribute)
+rather than default behaviour.</p>
+</div>
+<div class="paragraph">
+<p>Compile-time checks performed by <strong>consumers</strong> of sealed types
— most
+notably exhaustiveness analysis on a structural switch (see
+<a href="GEP-19.html">GEP-19</a>) — fall outside this commitment. Their
+severity may evolve independently and may, in a future major
+revision, default to error. Such evolution is a consumer-side
+language change, not a change to sealed types: it does not alter
+sealed-type semantics or the on-disk representation, and existing
+sealed declarations are unaffected.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_bytecode_interoperability">Bytecode interoperability</h3>
+<div class="paragraph">
+<p>Native sealed bytecode (<code>mode = NATIVE</code>, or <code>AUTO</code> on
target 17 or
+later) is JVM-level sealed metadata. Java and Groovy compilers consume
+each other’s sealed types under the standard JVM rules.</p>
+</div>
+<div class="paragraph">
+<p>The <code>EMULATE</code> mode produces an annotation that is recognised by
the
+Groovy compiler but not by the Java compiler or the JVM. Mixing
+emulated sealed Groovy types with Java consumers is therefore not
+sealed-checked from the Java side; this is a known consequence of
+emulation and is the reason <code>AUTO</code> defaults to <code>NATIVE</code>
whenever the
+target permits.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
-<h2 id="_references_and_useful_links">References and useful links</h2>
+<h2 id="_references">References</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
@@ -277,11 +699,29 @@ and not all types are exhaustively covered.</p>
<p><a href="https://openjdk.org/jeps/409">JEP 409: Sealed Classes</a></p>
</li>
<li>
+<p><a href="https://openjdk.org/jeps/394">JEP 394: Pattern Matching for
instanceof</a></p>
+</li>
+<li>
+<p><a href="https://openjdk.org/jeps/440">JEP 440: Record Patterns</a></p>
+</li>
+<li>
+<p><a href="https://openjdk.org/jeps/441">JEP 441: Pattern Matching for
switch</a></p>
+</li>
+<li>
+<p><a href="https://openjdk.org/jeps/456">JEP 456: Unnamed Variables and
Patterns</a></p>
+</li>
+<li>
+<p><a href="https://openjdk.org/jeps/507">JEP 507: Primitive Types in
Patterns</a></p>
+</li>
+<li>
<p><a href="https://kotlinlang.org/docs/sealed-classes.html">Sealed
Classes</a> in Kotlin</p>
</li>
<li>
<p><a href="https://github.com/scala/improvement-proposals/pull/43">Sealed
Types</a> in Scala (withdrawn)</p>
</li>
+<li>
+<p><a href="GEP-19.html">GEP-19: Structural Pattern Matching in switch</a></p>
+</li>
</ul>
</div>
<div class="sect2">
@@ -295,7 +735,37 @@ and not all types are exhaustively covered.</p>
<div class="ulist">
<ul>
<li>
-<p><a href="https://issues.apache.org/jira/browse/GROOVY-10148">GROOVY-10148:
Groovy should not allow classes to extend sealed Java classes</a></p>
+<p><a
href="https://issues.apache.org/jira/browse/GROOVY-10148">GROOVY-10148</a> —
Initial implementation</p>
+</li>
+<li>
+<p><a
href="https://issues.apache.org/jira/browse/GROOVY-10193">GROOVY-10193</a> —
<code>sealed</code> / <code>permits</code> / <code>non-sealed</code> grammar</p>
+</li>
+<li>
+<p><a
href="https://issues.apache.org/jira/browse/GROOVY-10201">GROOVY-10201</a> —
Proxy generation against JDK 17 sealed interfaces</p>
+</li>
+<li>
+<p><a
href="https://issues.apache.org/jira/browse/GROOVY-10233">GROOVY-10233</a> —
Native sealed bytecode for JDK 17+</p>
+</li>
+<li>
+<p><a
href="https://issues.apache.org/jira/browse/GROOVY-10240">GROOVY-10240</a> —
Record/sealed grammar consistency</p>
+</li>
+<li>
+<p><a
href="https://issues.apache.org/jira/browse/GROOVY-10340">GROOVY-10340</a> —
Drop system-property gating</p>
+</li>
+<li>
+<p><a
href="https://issues.apache.org/jira/browse/GROOVY-10433">GROOVY-10433</a> —
Restricted identifiers</p>
+</li>
+<li>
+<p><a
href="https://issues.apache.org/jira/browse/GROOVY-10434">GROOVY-10434</a> —
Public AST API for sealed status</p>
+</li>
+<li>
+<p><a
href="https://issues.apache.org/jira/browse/GROOVY-10451">GROOVY-10451</a> —
Self-reference guard in <code>permittedSubclasses</code></p>
+</li>
+<li>
+<p><a
href="https://issues.apache.org/jira/browse/GROOVY-10565">GROOVY-10565</a> —
Packaged sealed type <code>ClassFormatError</code> fix</p>
+</li>
+<li>
+<p><a
href="https://issues.apache.org/jira/browse/GROOVY-11292">GROOVY-11292</a>, <a
href="https://issues.apache.org/jira/browse/GROOVY-11750">-11750</a>, <a
href="https://issues.apache.org/jira/browse/GROOVY-11768">-11768</a> — Implicit
non-sealed propagation across hierarchies and joint-compilation chains</p>
</li>
</ul>
</div>
@@ -307,7 +777,8 @@ and not all types are exhaustively covered.</p>
<div class="sectionbody">
<div class="paragraph">
<p>1 (2021-07-22) Initial draft<br>
-2 (2021-11-06) Update to align with 4.0.0-beta-2</p>
+2 (2021-11-06) Update to align with 4.0.0-beta-2<br>
+3 (2026-05-05) Specification rewrite: implicit non-sealed propagation,
joint-compilation rules, restricted identifiers, public API stability
commitment, pattern-matching integration</p>
</div>
</div>
</div></div></div></div></div><footer id='footer'>
diff --git a/wiki/geps.html b/wiki/geps.html
index 32582c8..cda3e1a 100644
--- a/wiki/geps.html
+++ b/wiki/geps.html
@@ -63,7 +63,7 @@
</ul>
</div>
</div>
- </div><div id='content' class='page-1'><div
class='row'><div class='row-fluid'><div class='col-lg-3'><ul
class='nav-sidebar'><li class='active'><a href='#gep'>GEPs</a></li><li><a
href='#GEP-1' class='anchor-link'>GEP-1</a></li><li><a href='#GEP-2'
class='anchor-link'>GEP-2</a></li><li><a href='#GEP-3'
class='anchor-link'>GEP-3</a></li><li><a href='#GEP-4'
class='anchor-link'>GEP-4</a></li><li><a href='#GEP-5'
class='anchor-link'>GEP-5</a></li><li><a href='#GEP-6' [...]
+ </div><div id='content' class='page-1'><div
class='row'><div class='row-fluid'><div class='col-lg-3'><ul
class='nav-sidebar'><li class='active'><a href='#gep'>GEPs</a></li><li><a
href='#GEP-1' class='anchor-link'>GEP-1</a></li><li><a href='#GEP-2'
class='anchor-link'>GEP-2</a></li><li><a href='#GEP-3'
class='anchor-link'>GEP-3</a></li><li><a href='#GEP-4'
class='anchor-link'>GEP-4</a></li><li><a href='#GEP-5'
class='anchor-link'>GEP-5</a></li><li><a href='#GEP-6' [...]
<div class='row'>
<div class='colset-3-footer'>
<div class='col-1'>