[ 
https://issues.apache.org/jira/browse/GROOVY-12611?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18135623#comment-18135623
 ] 

ASF GitHub Bot commented on GROOVY-12611:
-----------------------------------------

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.
   




> A private or static trait method called from a closure ignores the closure's 
> delegate
> -------------------------------------------------------------------------------------
>
>                 Key: GROOVY-12611
>                 URL: https://issues.apache.org/jira/browse/GROOVY-12611
>             Project: Groovy
>          Issue Type: Bug
>    Affects Versions: 4.0.33, 5.0.8, 6.0.0
>            Reporter: Scott Murphy Heiberg
>            Priority: Major
>
> {code: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
> {code}
> Actual: 1, in dynamic, @TypeChecked and @CompileStatic code. The same happens 
> for a static trait method called from a static trait method.
> Expected: 2, as for the same code in a class. The delegate's method is used 
> when the resolve strategy tries the delegate first and the delegate has the 
> method. Otherwise the trait method is called. A public instance trait method 
> already behaves this way.
> Cause: TraitReceiverTransformer rewrites an implicit-this call to a private 
> or static trait method inside a closure into a direct call on the trait 
> helper class, so the closure's delegate is never consulted.
> Seen in 4.0.33, 5.0.8 and 6.0.0 (4.0.33 is right for public static methods).



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to