This is an automated email from the ASF dual-hosted git repository.
git-site-role 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 8c8fc40 2026/04/12 11:25:22: Generated dev website from
groovy-website@98e317d
8c8fc40 is described below
commit 8c8fc409abf444f3accb3c66ab3debe0742e409b
Author: jenkins <[email protected]>
AuthorDate: Sun Apr 12 11:25:22 2026 +0000
2026/04/12 11:25:22: Generated dev website from groovy-website@98e317d
---
search/search-index.json | 9 +-
wiki/GEP-16.html | 755 +++++++++++++++++++++++++++++++++++++++++++++++
wiki/geps.html | 2 +-
3 files changed, 764 insertions(+), 2 deletions(-)
diff --git a/search/search-index.json b/search/search-index.json
index 2c9b7af..b71444d 100644
--- a/search/search-index.json
+++ b/search/search-index.json
@@ -842,7 +842,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"
},
@@ -853,6 +853,13 @@
"url": "wiki/GEP-6.html",
"site": "dev"
},
+ {
+ "id": "wiki/GEP-16.html",
+ "title": "The Apache Groovy programming language - Developer docs -
GEP-16",
+ "content": "The Apache Groovy programming language - Developer docs -
GEP-16 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-16.html",
+ "site": "dev"
+ },
{
"id": "wiki/GEP-7.html",
"title": "The Apache Groovy programming language - Developer docs -
GEP-7",
diff --git a/wiki/GEP-16.html b/wiki/GEP-16.html
new file mode 100644
index 0000000..2093bca
--- /dev/null
+++ b/wiki/GEP-16.html
@@ -0,0 +1,755 @@
+<!DOCTYPE html>
+<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
+<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
+<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
+<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]--><head>
+ <meta charset='utf-8'/><meta http-equiv='X-UA-Compatible'
content='IE=edge'/><meta name='viewport' content='width=device-width,
initial-scale=1'/><title>The Apache Groovy programming language - Developer
docs - GEP-16</title><link href='../img/favicon.ico' type='image/x-ico'
rel='icon'/><script src='../js/matomo.js'></script><link rel='stylesheet'
type='text/css' href='../css/bootstrap.css'/><link rel='stylesheet'
type='text/css' href='../css/fontawesome.min.css'/><link rel='styleshe [...]
+</head><body>
+ <div id='fork-me'>
+ <a href='https://github.com/apache/groovy'>
+ <img style='position: fixed; top: 20px; right: -58px; border: 0;
z-index: 100; transform: rotate(45deg);'
src='../img/horizontal-github-ribbon.png'/>
+ </a>
+ </div><div id='st-container' class='st-container st-effect-9'>
+ <nav class='st-menu st-effect-9' id='menu-12'>
+ <h2 class='icon icon-lab'>Socialize</h2><ul>
+ <li>
+ <a href='https://groovy-lang.org/mailing-lists.html'
class='icon'><span class='fa fa-classic fa-regular fa-envelope'></span> Discuss
on the mailing list</a>
+ </li><li>
+ <a href='https://x.com/ApacheGroovy' class='icon'><span
class='fa fa-brands fa-x-twitter'></span> Groovy on X</a>
+ </li><li>
+ <a href='https://bsky.app/profile/groovy.apache.org'
class='icon'><span class='fa fa-brands fa-bluesky'></span> Groovy on Bluesky</a>
+ </li><li>
+ <a href='https://fosstodon.org/@ApacheGroovy'
class='icon'><span class='fa fa-brands fa-mastodon'></span> Groovy on
Mastodon</a>
+ </li><li>
+ <a
href='https://www.linkedin.com/company/106402668/admin/dashboard/'
class='icon'><span class='fa fa-brands fa-linkedin'></span> Groovy on
LinkedIn</a>
+ </li><li>
+ <a href='https://groovy-lang.org/events.html'
class='icon'><span class='fa fa-classic fa-solid fa-calendar-days'></span>
Events and conferences</a>
+ </li><li>
+ <a href='https://github.com/apache/groovy'
class='icon'><span class='fa fa-brands fa-github'></span> Source code on
GitHub</a>
+ </li><li>
+ <a href='https://groovy-lang.org/reporting-issues.html'
class='icon'><span class='fa fa-classic fa-solid fa-bug'></span> Report issues
in Jira</a>
+ </li><li>
+ <a href='http://stackoverflow.com/questions/tagged/groovy'
class='icon'><span class='fa fa-brands fa-stack-overflow'></span> Stack
Overflow questions</a>
+ </li><li>
+ <a href='http://www.groovycommunity.com/'
class='icon'><span class='fa fa-brands fa-slack'></span> Slack Community</a>
+ </li>
+ </ul>
+ </nav><div class='st-pusher'>
+ <div class='st-content'>
+ <div class='st-content-inner'>
+ <!--[if lt IE 7]>
+ <p class="browsehappy">You are using an
<strong>outdated</strong> browser. Please <a
href="http://browsehappy.com/">upgrade your browser</a> to improve your
experience.</p>
+ <![endif]--><div><div class='navbar navbar-default
navbar-static-top' role='navigation'>
+ <div class='container'>
+ <div class='navbar-header'>
+ <button type='button'
class='navbar-toggle' data-toggle='collapse' data-target='.navbar-collapse'>
+ <span class='sr-only'></span><span
class='icon-bar'></span><span class='icon-bar'></span><span
class='icon-bar'></span>
+ </button><a class='navbar-brand'
href='../index.html'>
+ <i class='fa-classic fa-solid
fa-star'></i> Apache Groovy™
+ </a>
+ </div><div class='navbar-collapse collapse'>
+ <ul class='nav navbar-nav navbar-right'>
+ <li class=''><a
href='https://groovy-lang.org/learn.html'>Learn</a></li><li class=''><a
href='https://groovy-lang.org/documentation.html'>Documentation</a></li><li
class=''><a href='/download.html'>Download</a></li><li class=''><a
href='https://groovy-lang.org/support.html'>Support</a></li><li class=''><a
href='/'>Contribute</a></li><li class=''><a
href='https://groovy-lang.org/ecosystem.html'>Ecosystem</a></li><li class=''><a
href='/blog'>Blog pos [...]
+ <a data-effect='st-effect-9'
class='st-trigger' href='#'>Socialize</a>
+ </li><li class=''>
+ <a href='../search.html'>
+ <i class='fa-classic fa-solid
fa-magnifying-glass'></i>
+ </a>
+ </li>
+ </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='#doc'>GEP-16</a></li><li><a
href='#_abstract' class='anchor-link'>Abstract</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
history</a></li></ul></div><div class='col-lg-8 col-lg-pull-0'><a name='do [...]
+<div class="sectionbody">
+<div class="sidebarblock">
+<div class="content">
+<div class="title">Metadata</div>
+<div class="hdlist">
+<table>
+<tr>
+<td class="hdlist1">
+<strong>Number</strong>
+</td>
+<td class="hdlist2">
+<p>GEP-16</p>
+</td>
+</tr>
+<tr>
+<td class="hdlist1">
+<strong>Title</strong>
+</td>
+<td class="hdlist2">
+<p><code>val</code> keyword for final declarations</p>
+</td>
+</tr>
+<tr>
+<td class="hdlist1">
+<strong>Version</strong>
+</td>
+<td class="hdlist2">
+<p>2</p>
+</td>
+</tr>
+<tr>
+<td class="hdlist1">
+<strong>Type</strong>
+</td>
+<td class="hdlist2">
+<p>Feature</p>
+</td>
+</tr>
+<tr>
+<td class="hdlist1">
+<strong>Status</strong>
+</td>
+<td class="hdlist2">
+<p>Draft</p>
+</td>
+</tr>
+<tr>
+<td class="hdlist1">
+<strong>Leader</strong>
+</td>
+<td class="hdlist2">
+<p>Paul King</p>
+</td>
+</tr>
+<tr>
+<td class="hdlist1">
+<strong>Created</strong>
+</td>
+<td class="hdlist2">
+<p>2026-04-12</p>
+</td>
+</tr>
+<tr>
+<td class="hdlist1">
+<strong>Last modification</strong> 
+</td>
+<td class="hdlist2">
+<p>2026-04-12</p>
+</td>
+</tr>
+</table>
+</div>
+</div>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_abstract">Abstract</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>Groovy already supports <code>var</code> as a declaration keyword (alias
for <code>def</code>,
+introduced for Java 10 compatibility). This GEP proposes adding
<code>val</code>
+as a declaration keyword that combines <code>final</code> with type
inference — equivalent to <code>final def</code>, <code>final
var</code>, or just <code>final</code>.</p>
+</div>
+<div class="sect2">
+<h3 id="_motivation">Motivation</h3>
+<div class="paragraph">
+<p>Declaring immutable local variables in Groovy currently requires:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">final name =
'Groovy'
+final def name = 'Groovy'
+final var name = 'Groovy'
+final String name = 'Groovy'</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>All of these work, but none are as concise or intention-revealing as:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">val name =
'Groovy'</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>The <code>val</code> keyword is familiar from Kotlin and Scala, where it is
the
+default and idiomatic way to declare immutable bindings. Adding
<code>val</code>
+to Groovy provides:</p>
+</div>
+<div class="ulist">
+<ul>
+<li>
+<p>A concise, readable way to declare final variables with inferred types</p>
+</li>
+<li>
+<p>Familiar syntax for developers coming from Kotlin or Scala</p>
+</li>
+<li>
+<p>A natural complement to the existing <code>var</code> keyword</p>
+</li>
+<li>
+<p>Encouragement of immutability as a default practice</p>
+</li>
+<li>
+<p>Improved parity for Gradle build script authors: Kotlin Gradle build scripts
+(<code>build.gradle.kts</code>) already use <code>val</code> for local
variables. Supporting <code>val</code>
+in Groovy Gradle build scripts (<code>build.gradle</code>) makes it easier for
teams
+that mix both languages, and reduces friction when translating between
them.</p>
+</li>
+</ul>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_requirements">Requirements</h3>
+<div class="ulist">
+<ul>
+<li>
+<p><code>val</code> declares a <code>final</code> variable with an inferred
type (like <code>final def</code>)</p>
+</li>
+<li>
+<p><code>val</code> must still be usable as a variable name, method name, map
key,
+and property name — mirroring <code>var</code> behavior</p>
+</li>
+<li>
+<p><code>val</code> must NOT be usable for method return types or type
declarations — mirroring <code>var</code> restrictions</p>
+</li>
+<li>
+<p><code>final val</code> should be accepted (redundant but harmless)</p>
+</li>
+</ul>
+</div>
+<div class="sect3">
+<h4 id="_non_goals">Non-goals</h4>
+<div class="ulist">
+<ul>
+<li>
+<p>Changing the behavior of <code>def</code> or <code>var</code></p>
+</li>
+<li>
+<p>Making <code>val</code> the default declaration style</p>
+</li>
+<li>
+<p>Adding immutability enforcement beyond the <code>final</code> modifier
(deep immutability
+is the domain of <code>@Immutable</code> and related transforms)</p>
+</li>
+</ul>
+</div>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_design">Design</h3>
+<div class="sect3">
+<h4 id="_keyword_classification">Keyword classification</h4>
+<div class="paragraph">
+<p><code>val</code> is a <em>contextual keyword</em> in the same category as
<code>var</code>.
+It is a reserved token at the lexer level but is included in the
+parser’s <code>identifier</code> rule, allowing it to be used as a name
in
+non-declaration contexts.</p>
+</div>
+<div class="paragraph">
+<p>Groovy’s keywords fall into two categories with respect to identifier
usage:</p>
+</div>
+<table class="tableblock frame-all grid-all stretch">
+<colgroup>
+<col style="width: 25%;">
+<col style="width: 37.5%;">
+<col style="width: 37.5%;">
+</colgroup>
+<thead>
+<tr>
+<th class="tableblock halign-left valign-top">Category</th>
+<th class="tableblock halign-left valign-top">Examples</th>
+<th class="tableblock halign-left valign-top">In <code>identifier</code>
rule?</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock">Declaration + method keywords</p></td>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>def</code></p></td>
+<td class="tableblock halign-left valign-top"><p
class="tableblock">No — <code>def</code> is used for method
return types, so allowing it as an identifier
+ would create unresolvable ambiguity</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock">Declaration-only keywords</p></td>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>var</code>, <code>val</code></p></td>
+<td class="tableblock halign-left valign-top"><p
class="tableblock">Yes — these are forbidden on method return
types, so the contexts
+ don’t overlap and identifier use is unambiguous</p></td>
+</tr>
+</tbody>
+</table>
+<div class="paragraph">
+<p>This is why <code>var var = 4</code> works but <code>def def = 2</code>
does not.
+<code>val</code> follows the <code>var</code> pattern: <code>val val =
5</code> will work.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_semantics">Semantics</h4>
+<div class="paragraph">
+<p><code>val</code> is equivalent to <code>final def</code>, <code>final
var</code>, or just <code>final</code>:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">val x = 42
// final, type inferred as int
+val s = 'hello' // final, type inferred as String
+val list = [1,2,3] // final, type inferred as ArrayList</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>The variable cannot be reassigned:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">val x = 1
+x = 2 // compile error: cannot assign to final
variable</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>But the object itself can be mutated (shallow finality):</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">val list = [1, 2,
3]
+list << 4 // OK: mutates the list, doesn't
reassign</code></pre>
+</div>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_where_val_is_allowed">Where <code>val</code> is allowed</h4>
+<div class="ulist">
+<ul>
+<li>
+<p>Local variable declarations: <code>val x = 1</code></p>
+</li>
+<li>
+<p>Field declarations: <code>class C { val x = 1 }</code></p>
+</li>
+<li>
+<p>For-loop index variables: <code>for (val i in 0..10) {}</code></p>
+</li>
+<li>
+<p>Closure/lambda parameters: <code>{ val x → x * 2 }</code></p>
+</li>
+<li>
+<p>Package names: <code>package foo.val.bar</code></p>
+</li>
+</ul>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_where_val_is_not_allowed">Where <code>val</code> is NOT allowed</h4>
+<div class="ulist">
+<ul>
+<li>
+<p>Method return types: <code>val someMethod()
{}</code> — parse error</p>
+</li>
+<li>
+<p>Type declarations: <code>class val {}</code>, <code>interface val
{}</code>, <code>@interface val {}</code> — parse error</p>
+</li>
+<li>
+<p>Compact constructor declarations (records)</p>
+</li>
+</ul>
+</div>
+<div class="paragraph">
+<p>These restrictions mirror <code>var</code> exactly. Note that
<code>val</code> can still be used
+as a method <em>name</em> (<code>def val() {}</code>).</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_identifier_contexts_that_still_work">Identifier contexts that still
work</h4>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">// As a variable
name (just like var):
+val val = 5
+assert val == 5
+
+// As a map key:
+def m = [val: 42]
+
+// As a method name:
+def val() { 'hello' }
+
+// In GString interpolation:
+assert "$val" == '5'
+
+// Combined with other contextual keywords:
+var var = 4
+val val = 5
+assert "$var$val" == '45'</code></pre>
+</div>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_interaction_with_final">Interaction with <code>final</code></h4>
+<div class="paragraph">
+<p><code>final val</code> is redundant but accepted, just as <code>final
def</code> and <code>final var</code> are:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">final val x = 1
// OK, same as val x = 1</code></pre>
+</div>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_interaction_with_java_classes_named_val">Interaction with Java
classes named <code>val</code></h4>
+<div class="paragraph">
+<p>Unlike <code>var</code> (which Java prohibits as a class name since JDK
10), Java
+allows classes named <code>val</code>. This creates a potential conflict when
such a
+class is used in Groovy code. The following table summarises the behavior:</p>
+</div>
+<table class="tableblock frame-all grid-all stretch">
+<colgroup>
+<col style="width: 42.8571%;">
+<col style="width: 14.2857%;">
+<col style="width: 42.8572%;">
+</colgroup>
+<thead>
+<tr>
+<th class="tableblock halign-left valign-top">Scenario</th>
+<th class="tableblock halign-left valign-top">Works?</th>
+<th class="tableblock halign-left valign-top">Notes</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td class="tableblock halign-left valign-top"><p class="tableblock"><code>new
val()</code></p></td>
+<td class="tableblock halign-left valign-top"><p
class="tableblock">Yes</p></td>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>val</code> parsed as class name in <code>new</code>
expression</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>import val as MyVal</code> then <code>MyVal x = new
MyVal()</code></p></td>
+<td class="tableblock halign-left valign-top"><p
class="tableblock">Yes</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Import
alias fully resolves the ambiguity</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p class="tableblock"><code>val
x = new val()</code></p></td>
+<td class="tableblock halign-left valign-top"><p
class="tableblock">Yes*</p></td>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>val</code> on LHS is the <em>keyword</em> (final +
inferred type), not the class.
+ Type is inferred from RHS. Reassignment is forbidden.</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p class="tableblock"><code>val
bar()</code> as method return type</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Keyword
takes precedence — use FQN or import alias</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p class="tableblock"><code>val
x</code> as explicit type in declaration</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">No</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Keyword
takes precedence — use FQN or import alias</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock">Java-defined <code>@val</code> annotation</p></td>
+<td class="tableblock halign-left valign-top"><p
class="tableblock">Yes</p></td>
+<td class="tableblock halign-left valign-top"><p
class="tableblock">Annotations are resolved by class name, no conflict</p></td>
+</tr>
+</tbody>
+</table>
+<div class="paragraph">
+<p><strong>Workaround:</strong> If you need to use a Java class named
<code>val</code> as an explicit
+type or method return type, use its fully-qualified name or an import
alias:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">import val as Val
+
+Val x = new Val() // explicit type via alias
+
+class Foo {
+ Val bar() { new Val() } // return type via alias
+}</code></pre>
+</div>
+</div>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_implementation">Implementation</h3>
+<div class="admonitionblock note">
+<table>
+<tr>
+<td class="icon">
+<div class="title">Note</div>
+</td>
+<td class="content">
+Early in parsing, <code>val</code> is simply converted to <code>final</code>
with a dynamic
+type. From that point on, the compiler never sees <code>val</code>
again — it follows
+the same code paths as any <code>final</code> declaration, including type
inference
+in <code>@CompileStatic</code> mode.
+</td>
+</tr>
+</table>
+</div>
+<div class="sect3">
+<h4 id="_spike_implementation">Spike implementation</h4>
+<div class="paragraph">
+<p>A spike of the proposed functionality was implemented to validate the
+design and explore edge cases.</p>
+</div>
+<div class="paragraph">
+<p><strong>Production code</strong> (~15 lines across 5 files):</p>
+</div>
+<table class="tableblock frame-all grid-all stretch">
+<colgroup>
+<col style="width: 28.5714%;">
+<col style="width: 71.4286%;">
+</colgroup>
+<thead>
+<tr>
+<th class="tableblock halign-left valign-top">File</th>
+<th class="tableblock halign-left valign-top">Change</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>GroovyLexer.g4</code></p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Add
<code>VAL : 'val';</code> token (1 line)</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>GroovyParser.g4</code></p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Add
<code>VAL</code> to <code>modifier</code>, <code>variableModifier</code>,
<code>indexVariable</code>,
+ <code>identifier</code>, and <code>keywords</code>
rules — alongside <code>VAR</code> in each (5 lines)</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>ModifierNode.java</code></p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Add
<code>VAL</code> to <code>MODIFIER_OPCODE_MAP</code> mapping to
<code>ACC_FINAL</code>,
+ add <code>isVal()</code> method, include <code>VAL</code> in
<code>isDef()</code> (3 lines + import)</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>AstBuilder.java</code></p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Add
<code>VAL</code> to type name, method return type, and compact constructor
+ restrictions — alongside <code>VAR</code> in each (3
lines)</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>SmartDocumentFilter.java</code></p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Add
<code>VAL</code> to syntax highlighting keyword list (1 line + import)</p></td>
+</tr>
+</tbody>
+</table>
+<div class="paragraph">
+<p>No changes were needed in
<code>ModifierManager.java</code> — <code>val</code> is
handled
+automatically via the opcode map (<code>ACC_FINAL</code>), and
<code>isDef()</code> returning
+<code>true</code> ensures the dynamic type is applied.</p>
+</div>
+<div class="paragraph">
+<p><strong>Test code</strong> (4 new files + 2 existing test fixes):</p>
+</div>
+<table class="tableblock frame-all grid-all stretch">
+<colgroup>
+<col style="width: 28.5714%;">
+<col style="width: 71.4286%;">
+</colgroup>
+<thead>
+<tr>
+<th class="tableblock halign-left valign-top">File</th>
+<th class="tableblock halign-left valign-top">Purpose</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>src/test-resources/core/Val_01x.groovy</code></p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Valid
usage: basic <code>val</code>, closure param, <code>val val</code>, map key,
+ type inference, shallow finality, <code>final val</code>, for loop,
GString</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>src/test-resources/fail/Val_01x.groovy</code></p></td>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>class val {}</code> — type
declaration forbidden</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>src/test-resources/fail/Val_02x.groovy</code></p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock"><code>val
someMethod() {}</code> — method return type forbidden</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>src/test-resources/fail/Val_03x.groovy</code></p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock"><code>val
x = 1; x = 2</code> — reassignment forbidden</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>GroovyParserTest.groovy</code></p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Wire up
<code>Val_01x</code> core test</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p
class="tableblock"><code>SyntaxErrorTest.groovy</code></p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Wire up
<code>Val_01x</code>/<code>02x</code>/<code>03x</code> fail tests</p></td>
+</tr>
+</tbody>
+</table>
+<div class="paragraph">
+<p>The spike confirmed that all edge cases (identifier usage, map keys,
+package names, Java class interop, import aliases, cast expressions)
+work correctly with no additional code beyond the changes listed above.</p>
+</div>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_breaking_behavior">Breaking behavior</h3>
+<div class="paragraph">
+<p>The main concern is existing code that uses <code>val</code> as a variable
name.
+Since <code>val</code> will be in the <code>identifier</code> rule (like
<code>var</code>), most usage
+continues to work:</p>
+</div>
+<div class="ulist">
+<ul>
+<li>
+<p><code>def val = 1</code> — still works (declaration with
<code>def</code>, identifier <code>val</code>)</p>
+</li>
+<li>
+<p><code>val = something</code> — still works (assignment to
variable named <code>val</code>)</p>
+</li>
+<li>
+<p><code>[val: 42]</code> — still works (map key)</p>
+</li>
+<li>
+<p><code>obj.val</code> — still works (property access)</p>
+</li>
+</ul>
+</div>
+<div class="paragraph">
+<p>The disambiguation works the same way as <code>var</code>: the parser uses
+<code>SemanticPredicates.isInvalidLocalVariableDeclaration()</code> to check
+what follows. <code>val = 42</code> is an assignment; <code>val x = 42</code>
is a declaration.</p>
+</div>
+<div class="sect3">
+<h4 id="_field_named_val_or_var_before_a_method_declaration">Field named
<code>val</code> (or <code>var</code>) before a method declaration</h4>
+<div class="paragraph">
+<p>A field declared as <code>def val</code> (or <code>def var</code>)
immediately before a method
+declaration is misinterpreted by the parser as modifiers on the method.
+This is a pre-existing issue with <code>var</code> that equally applies to
<code>val</code>:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">class Foo {
+ def val // intended as field, but parsed as
+ void doSomething() {} // modifiers for this method -> error
+}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p><strong>Workarounds:</strong></p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">class Foo {
+ def val = null // add initializer
+ def val; // add semicolon
+ String val // use explicit type instead of def
+ void doSomething() {}
+}</code></pre>
+</div>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_val_as_a_cast_expression"><code>val</code> as a cast expression</h4>
+<div class="paragraph">
+<p>A variable or field named <code>val</code> used with the <code>as</code>
cast operator is
+misinterpreted. The parser sees <code>val</code> as the keyword followed by
<code>as</code>:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">def val = 42
+val as String // error: unable to resolve class as</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p><strong>Workarounds:</strong></p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">this.val as String
// qualify with this
+(val) as String // parenthesise
+"$val" as String // alternative approach</code></pre>
+</div>
+</div>
+<div class="admonitionblock note">
+<table>
+<tr>
+<td class="icon">
+<div class="title">Note</div>
+</td>
+<td class="content">
+This also applies to <code>var</code>. It is a pre-existing issue.
+</td>
+</tr>
+</table>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_java_class_named_val">Java class named <code>val</code></h4>
+<div class="paragraph">
+<p>A Java class named <code>val</code> cannot be used directly as a declared
type or
+method return type in Groovy — the keyword takes precedence
in those
+positions. The workaround is to use a fully-qualified name or import alias.
+This matches the behavior <code>var</code> would have if Java permitted
<code>class var</code>.</p>
+</div>
+</div>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_references_and_useful_links">References and useful links</h2>
+<div class="sectionbody">
+<div class="ulist">
+<ul>
+<li>
+<p><a href="https://kotlinlang.org/docs/basic-syntax.html#variables">Kotlin
val/var</a></p>
+</li>
+<li>
+<p><a href="https://docs.scala-lang.org/tour/basics.html">Scala val/var</a></p>
+</li>
+<li>
+<p>Groovy <code>var</code> implementation: commit <code>f4d96d8872</code>
(2018)</p>
+</li>
+</ul>
+</div>
+<div class="sect2">
+<h3 id="_reference_implementation">Reference implementation</h3>
+<div class="paragraph">
+<p><a href="https://github.com/apache/groovy/tree/valSpike"
class="bare">https://github.com/apache/groovy/tree/valSpike</a></p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_jira_issues">JIRA issues</h3>
+<div class="ulist">
+<ul>
+<li>
+<p><a href="https://issues.apache.org/jira/browse/GROOVY-9308">GROOVY-9308:
Support val for final declarations</a> — originally raised in
2019. At that time, the <code>val</code>/<code>var</code> distinction
+was less widely understood. Since then, Kotlin adoption has grown
+significantly and <code>val</code>/<code>var</code> semantics are now familiar
to a broad
+audience, making the case for this feature stronger.</p>
+</li>
+</ul>
+</div>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_update_history">Update history</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>1 (2026-04-12) Initial draft
+2 (2026-04-12) Added Java interop edge cases, spike implementation summary
+3 (2026-04-12) Added field naming edge case, Gradle motivation
+4 (2026-04-12) Added cast expression edge case, split code stats, linked spike
branch and JIRA</p>
+</div>
+</div>
+</div></div></div></div></div><footer id='footer'>
+ <div class='row'>
+ <div class='colset-3-footer'>
+ <div class='col-1'>
+ <h1>Groovy</h1><ul>
+ <li><a
href='https://groovy-lang.org/learn.html'>Learn</a></li><li><a
href='https://groovy-lang.org/documentation.html'>Documentation</a></li><li><a
href='/download.html'>Download</a></li><li><a
href='https://groovy-lang.org/support.html'>Support</a></li><li><a
href='/'>Contribute</a></li><li><a
href='https://groovy-lang.org/ecosystem.html'>Ecosystem</a></li><li><a
href='/blog'>Blog posts</a></li><li><a
href='https://groovy.apache.org/events.ht [...]
+ </ul>
+ </div><div class='col-2'>
+ <h1>About</h1><ul>
+ <li><a
href='https://github.com/apache/groovy'>Source code</a></li><li><a
href='https://groovy-lang.org/security.html'>Security</a></li><li><a
href='https://groovy-lang.org/learn.html#books'>Books</a></li><li><a
href='https://groovy-lang.org/thanks.html'>Thanks</a></li><li><a
href='http://www.apache.org/foundation/sponsorship.html'>Sponsorship</a></li><li><a
href='https://groovy-lang.org/faq.html'>FAQ</a></li><li><a
href='https://groovy-lang.or [...]
+ </ul>
+ </div><div class='col-3'>
+ <h1>Socialize</h1><ul>
+ <li><a
href='https://groovy-lang.org/mailing-lists.html'>Discuss on the mailing
list</a></li><li><a href='https://x.com/ApacheGroovy'>Groovy on
X</a></li><li><a href='https://bsky.app/profile/groovy.apache.org'>Groovy on
Bluesky</a></li><li><a href='https://fosstodon.org/@ApacheGroovy'>Groovy on
Mastodon</a></li><li><a
href='https://www.linkedin.com/company/106402668/admin/dashboard/'>Groovy on
LinkedIn</a></li><li><a href='https://groovy-lang. [...]
+ </ul>
+ </div><div class='col-right'>
+ <p>
+ The Groovy programming language is
supported by the <a href='https://www.apache.org'>Apache Software
Foundation</a> and the Groovy community.
+ </p><div text-align='right'>
+ <img
src='https://www.apache.org/img/asf_logo.png' title='The Apache Software
Foundation' alt='The Apache Software Foundation' style='width:60%'/>
+ </div><p>Apache, Apache Groovy,
Groovy, and the ASF logo are either registered trademarks or trademarks of The
Apache Software Foundation.</p>
+ </div>
+ </div><div class='clearfix'>© 2003-2026
the Apache Groovy project — Groovy is Open Source: <a
href='https://www.apache.org/licenses/LICENSE-2.0.html' alt='Apache 2
License'>license</a>, <a
href='https://privacy.apache.org/policies/privacy-policy-public.html'>privacy
policy</a>.</div>
+ </div>
+ </footer></div>
+ </div>
+ </div>
+ </div>
+ </div><script src='../js/vendor/jquery-1.10.2.min.js'
defer></script><script src='../js/vendor/classie.js' defer></script><script
src='../js/vendor/bootstrap.js' defer></script><script
src='../js/vendor/sidebarEffects.js' defer></script><script
src='../js/vendor/modernizr-2.6.2.min.js' defer></script><script
src='../js/plugins.js' defer></script><script
src='../js/vendor/prettify.min.js'></script><script>document.addEventListener('DOMContentLoaded',prettyPrint)</script>
+</body></html>
\ No newline at end of file
diff --git a/wiki/geps.html b/wiki/geps.html
index af74788..c34bed5 100644
--- a/wiki/geps.html
+++ b/wiki/geps.html
@@ -59,7 +59,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'>