jamesfredley commented on PR #15557:
URL: https://github.com/apache/grails-core/pull/15557#issuecomment-4833554145

   ### Correction + real root cause: GROOVY-12106 is in the snapshot, but its 
fix is order-dependent and misses this case
   
   My previous comment was wrong on one point and I want to correct the record 
clearly.
   
   **What I got wrong:** I claimed the consumed `5.0.7-SNAPSHOT` (build 18, 
`5.0.7-20260628.011302-18`) *predated* the GROOVY-12106 fix. It does not. The 
CI run that produced build 18 (`Generate SNAPSHOT distributions`, run 
28307143921) was triggered by the fix commit `b46ebb25e7` itself (run window 
`01:05:34Z → 01:14:08Z`; snapshot timestamp `01:13:02Z` sits inside it). **The 
GROOVY-12106 fix is present in build 18.** Thanks for pushing on the timeline.
   
   **The real reason the `Arguable` / `ComplexTyped` inline must stay:** the 
GROOVY-12106 fix does not cover the Grails shape, because it is 
**order-dependent**.
   
   - The fix (`b7796c3f9b`, PR #2635) is entirely in 
`TraitTypeCheckingExtension` - the STC layer. It does subtype-aware lookup over 
the super-trait helper's declared statics, but only *after* the call has 
already been rewritten into `…$Trait$Helper.withDelegate($static$self, closure, 
arg)` form.
   - That rewrite happens earlier, in 
`TraitReceiverTransformer.findConcreteMethod`. If the super-trait's 
`$Trait$Helper` has not been generated yet when the sub-trait body is 
transformed, `findConcreteMethod` returns null and the call is left as a plain 
`Arguable#withDelegate(Closure, Object)` - so the fixed STC branch (which keys 
off `isClassType(argumentTypes[0])`, i.e. the synthetic `$static$self` Class 
arg) is never entered.
   - In this module the files sort `Arguable` / `ComplexTyped` **before** 
`ExecutesClosures` alphabetically, so the sub-traits are transformed first and 
hit exactly this gap. Groovy's own passing test 
`Groovy12106.testGrailsHelperShapeWithDelegatesTo` declares the parent trait 
first in a single script, so it never triggers the ordering.
   
   **Proof** - minimal self-contained reproducer compiled against build 18 (and 
against a local Groovy build from `b46ebb25e7`):
   
   ```groovy
   import groovy.transform.CompileStatic
   final class FieldA {}
   
   @CompileStatic
   trait ArguableA<T> extends ExecutesClosuresA {          // sub-trait 
declared FIRST
       String describe(FieldA f) { withDelegate({ -> }, (Object) f); 'ok' }
   }
   @CompileStatic
   trait ExecutesClosuresA {
       static void withDelegate(@DelegatesTo(strategy = Closure.DELEGATE_ONLY) 
Closure c, Object d) { if (c != null) c.call() }
   }
   class CA implements ArguableA<String> {}
   ```
   
   - Sub-trait-first (above) → **fails**: `Cannot find matching method 
ArguableA#withDelegate(groovy.lang.Closure, java.lang.Object)` - the exact 
Grails error.
   - Reverse the two trait declarations (super-trait first) → **compiles**.
   
   This is essentially `Groovy12106.testGrailsHelperShapeWithDelegatesTo` with 
only the declaration order reversed. It looks like a distinct upstream 
transform-order defect, earlier than the 12106 STC fix - reminiscent of the 
older [GROOVY-11743](https://issues.apache.org/jira/browse/GROOVY-11743) "super 
trait may not be transformed when creating helper". Worth a follow-up ticket 
for Paul with the reproducer above (the real fix would be in 
`TraitReceiverTransformer.findConcreteMethod`: fall back to the super-trait's 
declared statics when its helper is not yet generated, or order helper 
generation before sub-trait body rewriting).
   
   **Grails-side escape hatches tried and rejected:** plain 
`withDelegate(closure, argument)`, `(Object)`-cast, `this.withDelegate(...)`, 
and making `withDelegate` an instance method all fail - the instance + 
`@DelegatesTo` form fails to bind (`Not enough arguments found for a 
@DelegatesTo method call`), and `withDelegate` is used across ~8 GraphQL files, 
so a signature change is invasive. So the inline stays.
   
   The in-code comments and the PR description have been corrected to this root 
cause in `12292b860b`. The inline can drop to the byte-identical `8.0.x` 
`withDelegate(closure, (Object)…)` call once the upstream transform-order gap 
is fixed.
   
   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]

Reply via email to