Repository navigation
GROOVY-12602: SC: pass $self as the receiver of a lambda in a trait method - #3129
codeconsole wants to merge 3 commits into
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #3129 +/- ##
==================================================
+ Coverage 74.4085% 74.4168% +0.0084%
- Complexity 44367 44389 +22
==================================================
Files 1699 1699
Lines 151050 151076 +26
Branches 28783 28794 +11
==================================================
+ Hits 112394 112426 +32
- Misses 28376 28381 +5
+ Partials 10280 10269 -11
🚀 New features to boost your workflow:
|
|
Note: #3129 (GROOVY-12602) and #3139 (GROOVY-12612) fix different shapes of a call from a
With both applied, every shape I probed passes under dynamic, |
daniellansun
left a comment
There was a problem hiding this comment.
Looks correct for GROOVY-12602.
After GROOVY-11265 dropped rehydrate($self,$self,$self) for native lambdas under SC, StaticTypesLambdaWriter treated the enclosing static helper as owner/this and implicit-this calls cast getThisObject() to the trait — hence ClassCastException: Class cannot be cast to U. Loading $self when the enclosing method is a trait helper and the lambda accesses instance members restores the closure-equivalent receiver.
Tests cover getters/properties, nested lambda/closure mixes, method refs, field-initializer lambdas, private/static helpers, and three compile modes. Solid.
CI: lts (17, windows-latest, 1) is red; other LTS cells green. Happy to APPROVE once that cell is re-run green.
|
@daniellansun I can't re-run jobs on apache/groovy, so I merged current master (492ae79) to get a fresh run, which also brings in the Windows Gradle cache fix (#3110). The failed cell was the |
…static trait method A lambda in a static trait method still received the trait helper class as its receiver, so an implicit-this call such as getSimpleName() named the helper, while a closure in the same method names the implementing class. The lambda now receives $static$self, as it receives $self in an instance method.
✅ All tests passed ✅🏷️ Commit: 411f2e5 Learn more about TestLens at testlens.app/docs. |
Under
@CompileStatic, a lambda in a trait method that calls an instance method of the trait by implicitthisfails at run time:The same happens for a super trait's accessor (
unameorgetUname()), a lambda in a lambda, a closure in a lambda and a lambda in a trait field initializer, and in 4.0.33, 5.0.8 and 6.0.0. Dynamic Groovy,@TypeCheckedand closures work.A trait method body becomes a static method of
T$Trait$Helperthat takes the instance as$self. A closure there is rewritten toclosure.rehydrate($self,$self,$self). Since GROOVY-11265 static compilation drops that rewrite for a native lambda, andStaticTypesLambdaWriterthen passes the helper class as the lambda's owner and this object, because the enclosing method is static. The implicit-this call readsgetThisObject()and casts it to the trait.StaticTypesLambdaWriter.loadLambdaReceivernow passes the enclosing helper method's$selfas the owner and this object of a lambda that accesses instance members, asrehydratedoes for a closure. Closures nested in such a lambda take its this object, so they work too. Static trait methods are unchanged. Doing this inTraitReceiverTransformerinstead would break a lambda coerced toClosurethat resolves names through a delegate (for exampleobj.with((b) -> prop)): that transform runs before type checking and cannot tell the two kinds of lambda apart.A lambda in a static trait method gets the same treatment. Its helper method takes the implementing class as
$static$self, and the lambda now receives that, so an implicit-this call of aClassmethod there, such asgetSimpleName(), sees the implementing class (C, notT$Trait$Helper), as in a closure.TraitWithClosureOrLambda.testInvokeClassMethodFromTraitLambdaInStaticMethodcovers this. #3092 (GROOVY-12563) makes the same change toStaticTypesLambdaWriter; whichever lands second keeps one copy.Tests
TraitWithClosureOrLambdagets five tests.testInvokeTraitMethodFromTraitLambda,testInvokeSuperTraitMethodFromTraitLambda,testInvokeTraitMethodFromTraitLambdaInFieldInitializerandtestInvokeTraitMethodFromTraitLambdaOrClosurefail without the change.testInvokeTraitStaticOrPrivateMethodFromTraitLambdacovers neighbouring shapes.groovy.transform.stc.*,org.codehaus.groovy.classgen.asm.sc.*,org.codehaus.groovy.transform.traitx.*andgroovy.ThisAndSuperTestpass. The Checkstyle and CodeNarc gates pass.