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

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

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

   Under `@CompileStatic`, a lambda in a trait method that calls an instance 
method of the trait by implicit `this` fails at run time:
   
   ```groovy
   import java.util.function.Supplier
   @groovy.transform.CompileStatic
   trait U {
       String getUname() { 'u' }
       String a() { Supplier<String> s = () -> getUname(); s.get() }
   }
   class C implements U {}
   assert new C().a() == 'u' // ClassCastException: class java.lang.Class 
cannot be cast to class U
   ```
   
   The same happens for a super trait's accessor (`uname` or `getUname()`), 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, `@TypeChecked` and 
closures work.
   
   A trait method body becomes a static method of `T$Trait$Helper` that takes 
the instance as `$self`. A closure there is rewritten to 
`closure.rehydrate($self,$self,$self)`. Since GROOVY-11265 static compilation 
drops that rewrite for a native lambda, and `StaticTypesLambdaWriter` then 
passes the helper class as the lambda's owner and this object, because the 
enclosing method is static. The implicit-this call reads `getThisObject()` and 
casts it to the trait.
   
   `StaticTypesLambdaWriter.loadLambdaReceiver` now passes the enclosing helper 
method's `$self` as the owner and this object of a lambda that accesses 
instance members, as `rehydrate` does for a closure. Closures nested in such a 
lambda take its this object, so they work too. Static trait methods are 
unchanged. Doing this in `TraitReceiverTransformer` instead would break a 
lambda coerced to `Closure` that resolves names through a delegate (for example 
`obj.with((b) -> prop)`): that transform runs before type checking and cannot 
tell the two kinds of lambda apart.
   
   ## Tests
   
   `TraitWithClosureOrLambda` gets five tests. 
`testInvokeTraitMethodFromTraitLambda`, 
`testInvokeSuperTraitMethodFromTraitLambda`, 
`testInvokeTraitMethodFromTraitLambdaInFieldInitializer` and 
`testInvokeTraitMethodFromTraitLambdaOrClosure` fail without the change. 
`testInvokeTraitStaticOrPrivateMethodFromTraitLambda` covers neighbouring 
shapes. `groovy.transform.stc.*`, `org.codehaus.groovy.classgen.asm.sc.*`, 
`org.codehaus.groovy.transform.traitx.*` and `groovy.ThisAndSuperTest` pass. 
The Checkstyle and CodeNarc gates pass.
   




> SC: a lambda in a trait method calling a trait instance method by implicit 
> this throws ClassCastException: Class cannot be cast to the trait
> --------------------------------------------------------------------------------------------------------------------------------------------
>
>                 Key: GROOVY-12602
>                 URL: https://issues.apache.org/jira/browse/GROOVY-12602
>             Project: Groovy
>          Issue Type: Bug
>    Affects Versions: 4.0.33, 5.0.8, 6.0.0
>            Reporter: Scott Murphy Heiberg
>            Priority: Major
>
> {code:groovy}
> import java.util.function.Supplier
> @groovy.transform.CompileStatic
> trait U {
>     String getUname() { 'u' }
>     String a() { Supplier<String> s = () -> getUname(); s.get() }
> }
> class C implements U {}
> assert new C().a() == 'u'
> {code}
> Actual:
> {noformat}
> java.lang.ClassCastException: class java.lang.Class cannot be cast to class U
>       at U$Trait$Helper$_a_lambda1.doCall(...)
> {noformat}
> The same happens for a super trait's accessor read as {{uname}} or called as 
> {{getUname()}} in a lambda, for a lambda in a lambda, for a closure in a 
> lambda and for a lambda in a trait field initializer.
> Expected: the assertion passes, as it does without {{@CompileStatic}}, with 
> {{@TypeChecked}}, and with a closure instead of the lambda.
> A closure in a trait method is rehydrated with the trait instance as its this 
> object. A statically compiled lambda is not, and the lambda writer passes the 
> trait helper class as the lambda's this object because the helper method is 
> static. The implicit-this call then casts that class to the trait.
> Seen in 4.0.33, 5.0.8 and 6.0.0.



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

Reply via email to