codeconsole opened a new pull request, #3138:
URL: https://github.com/apache/groovy/pull/3138

   In trait code, an implicit-this call from a closure to a private or static 
trait method always called the trait method, even when the closure's delegate 
had a matching method:
   
   ```groovy
   trait T {
       private int size() { 1 }   // or: static int size() { 1 }
       def m() { [5, 6].with { size() } }
   }
   class C implements T {}
   assert new C().m() == 2 // was 1
   ```
   
   The same code in a class returns 2 in dynamic, `@TypeChecked` and 
`@CompileStatic` code. 4.0.33, 5.0.8, 6.0.0 and master return 1 for private 
methods, and 5.0.8 onwards for static ones too. `TraitReceiverTransformer` 
rewrote the call to `T$Trait$Helper.size($self)` before the closure's resolve 
strategy could apply. The implementing class has no private trait methods, so 
the call cannot simply be left for the closure to resolve against its owner.
   
   `TraitReceiverTransformer` still produces the helper call, and now attaches 
the plain implicit-this call to it as a post-type-checking replacement. Then:
   
   - **Type checking.** `TraitTypeCheckingExtension.afterMethodCall` walks the 
receivers that a class's implicit-this call would get. If a delegate that comes 
before the owner has the method, it type-checks the plain call against that 
delegate and stores its result type.
   - **Static compilation.** Statically compiled code compiles that delegate 
call. Otherwise it keeps the direct helper call, so the common case still 
compiles to a static call.
   - **Dynamic and `@TypeChecked` code.** These keep the plain call. 
`PostTypeCheckingExpressionReplacer` now visits closure bodies to apply the 
replacement.
   - **Runtime fallback.** `ClosureMetaClass` recognises a closure whose 
enclosing class is a trait helper. Where a class would call the owner's method, 
it calls the helper method for a private or static trait method, passing the 
owner or the owner's class. The extra lookup only runs for closures nested in a 
`$Trait$Helper`.
   
   Explicit `this.size()`, owner-first closures, lambdas, and static methods 
inherited from a super trait keep calling the trait method. As before, the 
trait method wins over a same-named method of the implementing class.
   
   #3125 (GROOVY-12598) adds an `inLambda` flag to `TraitReceiverTransformer` 
with slightly different semantics (it stays true for closures nested in a 
lambda); whichever lands second needs a small merge.
   
   ## Tests
   
   `TraitASTTransformationTest.testPrivateTraitMethodInDelegateFirstClosure`, 
`testStaticTraitMethodInDelegateFirstClosure` and 
`testPrivateTraitMethodInClosureWithDelegateSetAtRuntime` each run in dynamic, 
`@TypeChecked` and `@CompileStatic` mode. All fail without the change except 
the `@CompileStatic` mode of the last, which checks that statically compiled 
code still decides at compile time. `org.codehaus.groovy.transform.traitx.*`, 
`groovy.transform.stc.*`, `org.codehaus.groovy.classgen.asm.sc.*`, `gls.*`, 
`groovy.ThisAndSuperTest`, `groovy.Closure*`, `groovy.lang.Closure*` and 
`org.codehaus.groovy.runtime.metaclass.*` pass. The Checkstyle and CodeNarc 
gates pass.
   


-- 
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