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 b526730  2026/04/12 06:36:48: Generated dev website from 
groovy-website@e0828fc
b526730 is described below

commit b5267304e6d3347995c70eccbdb5a5d25f30b544
Author: jenkins <[email protected]>
AuthorDate: Sun Apr 12 06:36:48 2026 +0000

    2026/04/12 06:36:48: Generated dev website from groovy-website@e0828fc
---
 download.html                   |   2 +-
 search/search-index.json        |  11 ++-
 wiki/{geps.html => GEP-15.html} | 201 +++++++++++++++++++++++++++++++++++++++-
 wiki/geps.html                  |   2 +-
 4 files changed, 209 insertions(+), 7 deletions(-)

diff --git a/download.html b/download.html
index a42595f..7b2851e 100644
--- a/download.html
+++ b/download.html
@@ -64,7 +64,7 @@
                                                 <i class='fa-classic 
fa-regular fa-pen-to-square'></i> Improve this doc
                                             </button>
                                         </div><h1><i class='fa-classic 
fa-solid fa-cloud-arrow-down'></i> Download Groovy&trade;</h1><button 
id='big-download-button' type='button' class='btn btn-default' title='Download 
Apache Groovy&trade; 5.0.5 binary zip
-See below for verification information' 
onclick='window.location.href="https://groovy.jfrog.io/artifactory/dist-release-local/groovy-zips/apache-groovy-sdk-5.0.5.zip";'><i
 class='fa-classic fa-solid fa-download'></i> Download 
5.0.5</button><article><p>Ways to get Apache Groovy&trade;:</p><ul><li>Download 
a source or binary <a href='#distro'>distribution</a>.</li><li>Use a package 
manager or bundle for your <a href='#osinstall'>operating 
system</a>.</li><li>Refer to the appropriate Apache  [...]
+See below for verification information' 
onclick='window.location.href="https://groovy.jfrog.io/artifactory/dist-release-local/groovy-zips/apache-groovy-sdk-5.0.5.zip";'><i
 class='fa-classic fa-solid fa-download'></i> Download 
5.0.5</button><article><p>Ways to get Apache Groovy&trade;:</p><ul><li>Download 
a source or binary <a href='#distro'>distribution</a>.</li><li>Use a package 
manager or bundle for your <a href='#osinstall'>operating 
system</a>.</li><li>Refer to the appropriate Apache  [...]
                             <div class='row'>
                                 <div class='colset-3-footer'>
                                     <div class='col-1'>
diff --git a/search/search-index.json b/search/search-index.json
index f9722e7..18151d2 100644
--- a/search/search-index.json
+++ b/search/search-index.json
@@ -730,7 +730,7 @@
     {
         "id": "download.html",
         "title": "The Apache Groovy&trade; programming language - Download",
-        "content": "The Apache Groovy&trade; programming language - Download 
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&trade; Learn Documentation Download Support Contribute Ecosystem 
Blog posts Socialize Download G [...]
+        "content": "The Apache Groovy&trade; programming language - Download 
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&trade; Learn Documentation Download Support Contribute Ecosystem 
Blog posts Socialize Download G [...]
         "url": "download.html",
         "site": "dev"
     },
@@ -797,6 +797,13 @@
         "url": "wiki/GEP-14.html",
         "site": "dev"
     },
+    {
+        "id": "wiki/GEP-15.html",
+        "title": "The Apache Groovy programming language - Developer docs - 
GEP-15",
+        "content": "The Apache Groovy programming language - Developer docs - 
GEP-15 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&trade; Learn Documentation Download Support 
Contribute Ecosystem Blog posts Socialize =  [...]
+        "url": "wiki/GEP-15.html",
+        "site": "dev"
+    },
     {
         "id": "wiki/grails-proposal.html",
         "title": "The Apache Groovy programming language - Developer docs - 
Grails Project Proposal",
@@ -835,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&trade; 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&trade; 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/geps.html b/wiki/GEP-15.html
similarity index 53%
copy from wiki/geps.html
copy to wiki/GEP-15.html
index 12950d8..80b1a9e 100644
--- a/wiki/geps.html
+++ b/wiki/GEP-15.html
@@ -3,7 +3,7 @@
 <!--[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 - 
GEPs</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='stylesheet' 
type='text/css' [...]
+    <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-15</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'>
@@ -59,7 +59,202 @@
                                     </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><asciidocText 
attributes='{DOCS_BASEURL=https://docs.groovy-lang.org/latest}'>= GEP-15: 
Compound assignment operator overloading
+
+:icons: font
+
+.Metadata
+****
+[horizontal,options="compact"]
+*Number*:: GEP-15
+*Title*:: Compound assignment operator overloading
+*Version*:: 1
+*Type*:: Feature
+*Status*:: Draft
+*Leader*:: Paul King
+*Created*:: 2026-04-12
+*Last modification*&#160;:: 2026-04-12
+****
+
+== Abstract
+
+Groovy supports operator overloading: `\+` maps to `plus()`, `-` maps to 
`minus()`,
+`<<` maps to `leftShift()`, and so on. However, compound assignment operators
+(`pass:[+=]`, `-=`, `pass:[<<=]`, etc.) are always desugared to `x = x.op(y)` 
-- there is
+no way to override `+=` independently of `pass:[+]`. This GEP proposes adding
+support for dedicated compound assignment methods such as `plusAssign`,
+`minusAssign`, `leftShiftAssign`, etc.
+
+=== Motivation
+
+The current desugaring of `x += y` to `x = x.plus(y)` creates a new object
+and reassigns the variable. This has several drawbacks:
+
+* **Mutable data structures** are forced into a create-and-reassign pattern
+  when in-place mutation is the intended semantics. For example, a mutable
+  list's `+=` creates a new list rather than appending in place.
+* **Final fields and variables** cannot use compound assignment at all,
+  even when the underlying object is mutable and supports in-place mutation.
+* **Intent** is unclear: the class author cannot distinguish between
+  `x + y` (produce a new value) and `x += y` (mutate in place).
+
+Languages like Kotlin and Scala already support this distinction.
+Kotlin maps `\+=` to `plusAssign()` when available, falling back to
+`plus()` with reassignment. Scala allows mutable collections to define
+`+=` directly.
+
+=== Requirements
+
+* Support dedicated compound assignment methods (`plusAssign`, `minusAssign`, 
etc.)
+  that are called in preference to the current `plus` + reassign pattern.
+* Maintain full backward compatibility: existing code that uses `+=` with
+  `plus()` must continue to work identically when no `plusAssign` method 
exists.
+* Support `+=` on `final` fields/variables when `plusAssign` is available.
+* Work correctly in both `@CompileStatic` and dynamic Groovy.
+
+==== Non-goals
+
+* Changing the behavior of `++` and `--` operators (these use 
`next()`/`previous()`
+  and are conceptually different).
+* Changing subscript compound assignment (`a[i] += b`), which uses the existing
+  `getAt`/`putAt` pattern.
+
+=== Operator method name mapping
+
+[cols="1,2,2",options="header"]
+|===
+| Operator | Assign method | Fallback method
+
+| `+=` | `plusAssign` | `plus`
+| `-=` | `minusAssign` | `minus`
+| `*=` | `multiplyAssign` | `multiply`
+| `/=` | `divAssign` | `div`
+| `%=` | `modAssign` | `mod`
+| `%%=` | `remainderAssign` | `remainder`
+| `**=` | `powerAssign` | `power`
+| `<\<=` | `leftShiftAssign` | `leftShift`
+| `>>=` | `rightShiftAssign` | `rightShift`
+| `>>>=` | `rightShiftUnsignedAssign` | `rightShiftUnsigned`
+| `&=` | `andAssign` | `and`
+| `\|=` | `orAssign` | `or`
+| `^=` | `xorAssign` | `xor`
+| `//=` | `intdivAssign` | `intdiv`
+|===
+
+=== Resolution algorithm
+
+When the compiler encounters `x += y`:
+
+1. Look for a method `plusAssign(y)` on the type of `x`.
+2. If found, call `x.plusAssign(y)` directly. No reassignment occurs.
+   This works even when `x` is `final`.
+3. If not found, fall back to the current behavior: `x = x.plus(y)`.
+   This requires `x` to be reassignable.
+4. If `x` is `final` and no `plusAssign` exists, report a compile error
+   (in `@CompileStatic` mode).
+
+When both `plusAssign` and `plus` exist on the same type, `plusAssign`
+takes precedence. This is the pragmatic choice for Groovy since it lacks
+Kotlin's `val`/`var` distinction. If a class author defined `plusAssign`,
+they intended it to be used for `+=`.
+
+=== Design considerations
+
+* **Primitives are unaffected.** `int x = 0; x += 1` continues to use
+  the existing fast-path for primitive arithmetic. The `plusAssign` lookup
+  only applies when the base operator would resolve to a method call.
+* **Expression value.** When `plusAssign` is called, the expression value
+  of `x += y` is `x` (the mutated object), not the return value of
+  `plusAssign`. This differs from the current behavior where the expression
+  value is the result of `x.plus(y)`.
+* **Extension methods.** `plusAssign` is discoverable as an extension method
+  (DGM or category), not just as an instance method.
+* **`@OperatorRename` support.** The existing `@OperatorRename` annotation
+  will be extended to support renaming the assign variants, e.g.
+  `@OperatorRename(plusAssign="addInPlace")`.
+
+=== Examples
+
+A mutable accumulator:
+
+[source,groovy]
+----
+class Accumulator {
+    int total = 0
+    void plusAssign(int n) { total += n }
+    Accumulator plus(int n) { new Accumulator(total: total + n) }
+}
+
+def acc = new Accumulator()
+acc += 5          // calls acc.plusAssign(5), mutates in place
+assert acc.total == 5
+
+def acc2 = acc + 3 // calls acc.plus(3), returns new Accumulator
+assert acc2.total == 8
+assert acc.total == 5
+----
+
+A final field with in-place mutation:
+
+[source,groovy]
+----
+@CompileStatic
+class EventBus {
+    final List<String> listeners = []
+    // List has leftShiftAssign via extension method or subclass
+}
+
+def bus = new EventBus()
+bus.listeners <<= "listener1"  // calls listeners.leftShiftAssign("listener1")
+----
+
+=== Static compilation (`@CompileStatic`)
+
+In `@CompileStatic` mode, the type checker resolves `plusAssign` at
+compile time using `findMethod()`. If found, it records the target method
+on the expression via node metadata and the bytecode generator emits a
+direct method call -- no dup, no store-back.
+
+If not found, the existing desugaring to `x = x.plus(y)` applies.
+
+=== Dynamic Groovy
+
+In dynamic Groovy, the runtime checks for `plusAssign` via the Meta-Object
+Protocol (MOP). If `respondsTo(target, "plusAssign", arg)` succeeds, it is
+called. Otherwise, the fallback to `plus` + reassignment applies.
+
+This requires a new runtime helper method (e.g., in `ScriptBytecodeAdapter`)
+that encapsulates the try-assign-then-fallback logic.
+
+=== Breaking behavior
+
+1. **Existing `plusAssign` methods.** If a class already defines a method
+   literally named `plusAssign`, `+=` will now call it instead of desugaring
+   to `plus` + reassign. This is unlikely in practice but must be documented
+   in release notes.
+2. **Expression value change.** `val = (x += y)` -- with `plusAssign`, the
+   captured value is `x` (the mutated object), not the result of `plus`.
+   This only affects code that uses compound assignment as an expression.
+3. **Final fields.** In `@CompileStatic`, `final` fields with a type that
+   has `plusAssign` can now use `+=`. Previously this was always an error.
+
+== References and useful links
+
+* 
https://kotlinlang.org/docs/operator-overloading.html#augmented-assignments[Kotlin
 augmented assignments]
+* https://docs.scala-lang.org/overviews/collections-2.13/overview.html[Scala 
mutable collection operators]
+
+=== Reference implementation
+
+_TBD_
+
+=== JIRA issues
+
+_TBD_
+
+== Update history
+
+1 (2026-04-12) Initial draft
+</asciidocText><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-15</a></li></ul></div><div class='col-lg-8 
col-lg-pull-0'><a 
name='doc'></a><h1>GEP-15</h1><hr/></div></div></div></div><footer id='footer'>
                             <div class='row'>
                                 <div class='colset-3-footer'>
                                     <div class='col-1'>
@@ -87,5 +282,5 @@
                 </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>
+    </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 12950d8..af74788 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'>

Reply via email to