jamesfredley commented on PR #15558: URL: https://github.com/apache/grails-core/pull/15558#issuecomment-4577971891
## Blocker #6 resolved - Groovy 6 Verifier abstract-method regression worked around (`8b349bcdb8`) After re-baselining the canary on the updated `grails8-groovy5-sb4` (merge `3538600648`, which brings in the GROOVY-12040 `@Builder` removal), `:grails-views-gson:compileGroovy` was the **sole** remaining failure - the entire rest of the dependency chain compiles. It is now fixed. ### Root cause (confirmed) The diagnostic comes from `org.codehaus.groovy.classgen.ClassCompletionVerifier.checkNoAbstractMethodsNonAbstractClass`, which iterates `ClassNode.getAbstractMethods()` built from `getDeclaredMethodsMap()`, keyed by `MethodNode.getTypeDescriptor()` - and the descriptor **includes the return type**. On Groovy 6 the concrete leaf `render(...)` overrides in `DefaultGrailsJsonViewHelper` resolve a *different* descriptor for the inner-class return type `grails.plugin.json.builder.JsonOutput.JsonWritable` than the abstract `render(...)` entries inherited from the `GrailsJsonViewHelper` interface, so they never displace the abstract entries, which then survive and are reported "unimplemented" - for all 5 overloads, including the two declared explicitly. A key catalyst: **`groovy.json.JsonOutput.JsonWritable` was removed in Groovy 6** (the class now declares only `JsonUnescaped`). The Grails `JsonOutput.JsonWritable` shadowed it on Groovy 5; on Groovy 6 there is nothing to shadow, which changes how the inner-class return type resolves. This is a Verifier-layer defect, not the static type checker - it reproduces with `@CompileStatic` removed. ### The fix (workaround #12, the first that works) Declare the 5 `GrailsJsonViewHelper#render(...)` methods as **`default`** (concrete, throwing `UnsupportedOperationException`). Because the bug is specifically in the *abstract*-method check, making the methods non-abstract removes them from `getAbstractMethods()` entirely - the verifier has nothing to flag. `DefaultGrailsJsonViewHelper` is the **sole implementor** and overrides all 5, so the throwing default bodies are never reached. The twelve earlier attempts that did **not** work (this PR's [workaround-attempts comment](https://github.com/apache/grails-core/pull/15558#issuecomment-4523762826) lists the first ten): explicit forwarders, fully-qualified return types, inner-class rename, removing `@CompileStatic` from class and interface, explicit constructor, abstract-parent + concrete-subclass, `@CompileDynamic`, diamond removal, diamond + covariant-`getG` removal, and - new this round - concrete `render` stubs on the intermediate superclass `DefaultJsonViewHelper` (Oracle's first suggestion; it failed because the stub gets the same mismatched descriptor). ### Verification (Groovy 6.0.0-SNAPSHOT build #716 / Gradle 9.5.1 / Spring Boot 4.0.6, JDK 21) | Task | Result | |---|---| | `:grails-views-gson:compileGroovy` | green | | `:grails-views-gson:test` | all pass (render / HAL / JSON-API / template-inheritance incl. `g.render(..)`); 1 pre-existing `@IgnoreIf` skip | | `:grails-views-gson:codeStyle` | green | The render-path tests exercise the real implementations, confirming the `default` bodies are never hit at runtime. ### Standalone reproducer status I built a faithful structural mirror (`repro6b`): a Java outer class extending `groovy.json.JsonOutput` with a shadowing inner `JsonWritable`, the `@CompileStatic` interface chain `JsonViewHelper extends ViewHelper extends LinkGen`, the diamond (`DefaultJsonViewHelperBase extends DefaultViewHelper implements ViewHelper`; `Impl extends ... implements JsonViewHelper`), default-argument `render`, mixed `void inline(...)`, joint Java+Groovy compilation, precompiled-jar split, and anonymous `JsonWritable` subclass instances. **It compiles cleanly** on build #716 - i.e. none of those ingredients in isolation trigger the defect (matching the earlier finding). The real `grails-views-gson` module remains the working in-tree reproducer; isolating the last differentiating element into a dependency-free case is still open and will accompany the upstream Apache Groovy ticket. ### Net The canary now builds, tests, and style-checks end-to-end on build #716; the only remaining non-production crutch is the Spock `disableGroovyVersionCheck` bridge (still DRAFT/DO-NOT-MERGE until a Spock `*-groovy-6.0` artifact ships). Inherited workarounds are unchanged (#1 Validateable / GROOVY-11985, #2 VariableScopeVisitor canonicalization, #3 boot4-disabled-integration-test-config, #4 AbstractConstraint static-init). Assisted-by: claude-code:claude-4.8-opus -- 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]
