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

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

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

   In a statically compiled trait method, closures and lambdas got the static 
helper class where the trait instance, or the implementing class in a static 
method, is expected:
   
   ```groovy
   @CompileStatic
   trait T {
       String helper() { 'helper' }
       Object own() { def c = { owner }; c() }                                  
 // GroovyCastException to T$Trait$Helper
       String viaThis() { def c = { this.helper() }; c() }                      
 // ClassCastException: Class to T$Trait$Helper
       String viaLambda() { Supplier<String> s = () -> helper(); s.get() }      
 // ClassCastException: Class to T
       static Closure<String> f = { 'f' }
       static String callF() { f() }                                            
 // MissingMethodException: T__f$get
   }
   ```
   
   Five commits:
   
   - The type checker types the this object of a closure in a trait instance 
method or instance field initializer, and its owner and delegate when it is not 
nested in another closure, as the trait (with its `@SelfType` types), since the 
trait transformation rehydrates the closure to `$self`. `owner`, `thisObject`, 
`delegate` without `@DelegatesTo`, `getOwner()` and `getThisObject()` failed 
with a GroovyCastException, under `@TypeChecked` too.
   - A closure in a lambda of a trait method gets the trait instance: a lambda 
that uses instance state and contains a closure is given `$self` (or 
`$static$self`) as its owner and this object instead of the helper class. In 
any class, a lambda whose closure reads only `owner` now captures, so the 
closure's owner is the lambda and not the lambda's class.
   - `this.m()` in a closure or lambda of a trait method calls `m` on the trait 
instance, as `this` alone already resolves. It called the helper class in all 
three modes: a ClassCastException under `@CompileStatic`, a 
MissingMethodException for a method with arguments, a `@SelfType` method or an 
abstract method in dynamic and `@TypeChecked` code, and the helper's 
`toString()` and `getClass()`.
   - Any lambda in a trait helper method that uses instance state gets `$self` 
as its owner and this object, so `() -> helper()` works.
   - `f()` on a static closure field of a trait reads the field of the 
implementing class, as `f.call()` and dynamic code do, instead of going through 
the trait's static accessor.
   
   ## Tests
   
   `TraitWithClosureOrLambda.testOwnerAndThisObjectOfTraitClosure`, 
`testClosureInTraitLambda`, `testExplicitThisMethodCallInTraitClosure` and 
`testStaticClosureFieldCalledAsMethod`, and 
`LambdaTest.testOwnerOfClosureInLambda`, fail without the change. 12,903 tests 
in the `groovy`, `gls`, `bugs`, STC/SC and `org.codehaus.groovy.transform` 
packages pass, and the Checkstyle and CodeNarc gates pass.
   
   Seen in 4.0.33, 5.0.8 and 6.0.0 (the static closure field is a type-checking 
error there before 6.0.0).
   




> SC: closures and lambdas in a trait method see the trait helper class instead 
> of the trait instance
> ---------------------------------------------------------------------------------------------------
>
>                 Key: GROOVY-12563
>                 URL: https://issues.apache.org/jira/browse/GROOVY-12563
>             Project: Groovy
>          Issue Type: Bug
>          Components: Static compilation, Static Type Checker
>    Affects Versions: 4.0.33, 5.0.8, 6.0.0
>            Reporter: Scott Murphy Heiberg
>            Priority: Major
>
> In a statically compiled trait method, closures and lambdas get the static 
> helper class where the trait instance (or, in a static method, the 
> implementing class) is expected:
> {code:groovy}
> @groovy.transform.CompileStatic
> trait T {
>     String helper() { 'helper' }
>     Object own() { def c = { owner }; c() }
>     String viaThis() { def c = { this.helper() }; c() }
>     String viaLambda() { java.util.function.Supplier<String> s = () -> 
> helper(); s.get() }
> }
> @groovy.transform.CompileStatic
> class C implements T { }
> new C().own()       // GroovyCastException: Cannot cast object 'C@...' with 
> class 'C' to class 'T$Trait$Helper'
> new C().viaThis()   // ClassCastException: class java.lang.Class cannot be 
> cast to class T$Trait$Helper
> new C().viaLambda() // ClassCastException: class java.lang.Class cannot be 
> cast to class T
> {code}
> Also: {{thisObject}}, {{delegate}} without {{@DelegatesTo}}, {{getOwner()}} 
> and {{getThisObject()}} in such a closure (also under {{@TypeChecked}}); a 
> closure written in a lambda; {{this.m()}} in a closure of a dynamic or 
> {{@TypeChecked}} trait (MissingMethodException for a method with arguments, 
> the helper's {{toString()}} and {{getClass()}}); and calling a static closure 
> field of the trait as a method:
> {code:groovy}
> @groovy.transform.CompileStatic
> trait T {
>     static Closure<String> f = { 'f' }
>     static String callF() { f() }
> }
> @groovy.transform.CompileStatic
> class C implements T { }
> C.callF() // 6.0.0: MissingMethodException: No signature of static method: 
> T__f$get for class: T
>           // 4.0.33, 5.0.8: [Static type checking] - Cannot find matching 
> method java.lang.Class#f()
> {code}
> Expected: these give the values and calls dynamic Groovy gives: the trait 
> instance as this object, owner and delegate of a closure written directly in 
> a trait method, the lambda as the owner of a closure written in a lambda, and 
> the implementing class's static field.



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

Reply via email to