jamesfredley opened a new issue, #16157:
URL: https://github.com/apache/grails-core/issues/16157

   Tracking issue for the changes Apache Grails needs in order to build and 
test against Apache Groovy 6. It documents what the 
[`grails8-groovy6-canary`](https://github.com/apache/grails-core/pull/15558) 
branch carries, why each item exists, and the condition under which it can be 
deleted.
   
   Current canary state: Groovy `6.0.0-beta-2`, Grails 9 (`9.0.x` base), JDK 
21, Spring Boot 4.1, Spock `2.4-groovy-5.0`.
   
   Every row below was established empirically: a workaround was only kept when 
removing it produced an observed failure, and only removed when the build and 
tests stayed green without it.
   
   ## 1. Upstream Groovy 6 regression (not yet reported)
   
   Static type checking merges the flow state of a `||` inside a closure to 
`void`, so a variable guarded by `x == null || ...` becomes unusable in the 
branch body.
   
   ```groovy
   @groovy.transform.CompileStatic
   class Repro {
       void bind(Collection val, Class componentType) {
           List boundItems = []
           ((Collection) val).each { item ->
               if (item == null || 
componentType.isAssignableFrom(item.getClass())) {
                   boundItems << item   // FAILS on 6.0.0-beta-2
               }
           }
       }
   }
   ```
   
   ```
   [Static type checking] - Cannot find matching method 
java.util.ArrayList#leftShift(void)
   ```
   
   The Map form fails equivalently with `Cannot find matching method 
java.util.LinkedHashMap#putAt(java.lang.Object, void)`.
   
   - Compiles cleanly on Groovy `5.0.8`; fails on `6.0.0-beta-2`. It is a 
regression, not intended behaviour.
   - `&&` in the same position compiles. Only `||` triggers it.
   - Adding type arguments to the cast, declaring the closure parameter type, 
converting to `for`-in with an explicit `Object` declaration, and renaming 
shadowed variables all still fail. The trigger is the `||` flow merge itself.
   - Workaround: hoist the disjunction into a `boolean` local. Short-circuiting 
and null handling are unchanged.
   
   Applied in `grails-web-databinding/.../GrailsWebDataBinder.groovy` (both the 
Collection and Map branches).
   
   ## 2. Workarounds currently required on 6.0.0-beta-2
   
   | # | File | Groovy 6 behaviour | Observed failure without it | Removable 
when |
   |---|---|---|---|---|
   | 1 | `grails-web-databinding/.../GrailsWebDataBinder.groovy` | `\|\|` flow 
state inside a closure infers as `void` | `Cannot find matching method 
java.util.ArrayList#leftShift(void)` | The regression in section 1 is fixed 
upstream |
   | 2 | `grails-core/.../config/external/WriterFilteringMap.groovy` | 
`@Delegate` now generates mutator methods that were previously excluded | 
`WriteFilteringMapSpec`: `getWrittenValues().size() == 0` | Groovy restores the 
previous `@Delegate` mutator exclusion behaviour, or the exclusion is made 
explicit upstream |
   | 3 | `grails-datamapping-core/.../GormEntity.groovy` | Generic trait method 
signatures are specialized differently, so the trait-injected `merge(Object)` 
is not found | `NoSuchMethodException: Book.merge(Object)` | Groovy restores 
the prior generic trait-signature resolution |
   | 4 | `grails-datastore-core/.../reflect/ClassPropertyFetcher.java` | 
Interface methods surface differently during property introspection | 
`MissingMethodException ... __transients$get` | Unknown - needs upstream 
confirmation of intended behaviour |
   | 5 | `grails-testing-support-http-client/.../utils/XmlUtils.groovy` | The 
SAX/JAXP feature set recognised by the default parser configuration changed | 
`DOCTYPE is disallowed...` | Unknown - needs upstream confirmation. Note this 
code is XXE-hardening; it must not be dropped for convenience |
   | 6 | `grails-validation/.../ValidateableTraitSpec.groovy` | Static trait 
methods are emitted with a modifier combination the verifier rejects | `illegal 
combination of modifiers: abstract and static` | Groovy fixes static 
trait-method emission |
   | 7 | `grails-data-hibernate7/.../HibernateGormInstanceApi.groovy` | Negated 
`instanceof` requires explicit parenthesization | Compile failure | Groovy 
restores the prior parsing precedence |
   | 8 | `grails-fields/.../BeanPropertyAccessorImpl.groovy` | `@Canonical` no 
longer implies `@MapConstructor` | Compile failure | Groovy reinstates the 
implied `@MapConstructor`, or Grails adopts the explicit annotation permanently 
|
   | 9 | `grails-views-gson/.../GrailsJsonViewHelper.groovy`, 
`.../internal/TemplateRenderer.groovy` | Closure/generic inference changes 
under `@CompileStatic` | Compile failure | Unknown - needs upstream 
confirmation |
   | 10 | `grails-testing-support-http-client/.../HttpClientSupport.groovy` | A 
`static final` constant in a Spec collides with a trait's instance getter | 
`cannot have both a static and an instance method` | Groovy restores the prior 
static/instance resolution |
   | 11 | `gradle/*-test-config.gradle`, `gradle.properties` | Spock 2.4 is 
built against Groovy 5 and refuses to run on Groovy 6 | `Executing Spock 
2.4.0-groovy-5.0 with NOT compatible Groovy version 6.0.0-beta-2` | Spock ships 
a `groovy-6.0` build. This is the single largest category and is not a Grails 
or Groovy defect |
   | 12 | `grails-common/build.gradle`, `dependencies.gradle` | 
`groovy-callsite` is a separate module in Groovy 6 | Missing class at runtime | 
Never - this is a permanent Groovy 6 packaging change |
   | 13 | `build-logic/.../SbomPlugin.groovy` | Groovy 6 pulls JLine 4, whose 
POM omits the licence the SBOM validation expects | SBOM licence validation 
failure | JLine publishes complete licence metadata |
   | 14 | `settings.gradle` | The Micronaut island is pinned to Groovy 5 and 
must not join a Groovy 6 build | Version conflict on JDK 25 CI | The Micronaut 
island supports Groovy 6 |
   | 15 | `.github/workflows/groovy-joint-workflow.yml` | Groovy 6 development 
is still on `master`; `GROOVY_6_0_X` does not exist | Joint validation checks 
out a non-existent branch | Apache Groovy cuts a `GROOVY_6_0_X` branch |
   
   ## 3. Workarounds retired at 6.0.0-beta-2
   
   | Workaround | Why it is gone |
   |---|---|
   | Gradle `9.6.1` bump (wrappers, `.sdkmanrc`, generated-project template, 
`gradleToolingApiVersion`) | Never a Groovy 6 requirement. The build is green 
on `9.6.0` |
   | `CoreGrailsPlugin` `BeanConfiguration.addProperty` workaround | Obsolete: 
the `beanRegistrar` rewrite on `9.0.x` removed the BeanBuilder closure entirely 
|
   | `GrailsApplicationLifeCycle` rewritten as a Java interface | Groovy 6 
handles the interface default method again. Verified by booting a Grails 
application under `-PgrailsIndy=false`, the classic-callsite mode the original 
bug required. Its regression test is retained as coverage |
   | ~30 further snapshot-era compile workarounds | No longer reproduce on 
`6.0.0-beta-2` |
   
   ## 4. Not Groovy 6 issues
   
   - `ConfigurationBuilder` nested-map handling is **Spring 7** compatibility 
and is independent of the Groovy version. It is currently carried by the canary 
and should be extracted into its own `9.0.x` pull request; trunk has the 
underlying gap today.
   - Embedded MongoDB and Testcontainers failures seen locally are 
environmental (no Docker, or no network to `fastdl.mongodb.org`).
   
   ## Verification
   
   Root build compiles on `6.0.0-beta-2`. Tests re-run with `--rerun-tasks`, 
zero failures: `grails-core` 507, `grails-datastore-core` 108, 
`grails-datamapping-core` 293, `grails-web-url-mappings` 211, 
`grails-validation` 91, `grails-testing-support-http-client` 105, 
`grails-test-suite-uber` 566. A Grails application boots and passes integration 
tests under `-PgrailsIndy=false`.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to