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]