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

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

jdaugherty commented on PR #3081:
URL: https://github.com/apache/groovy/pull/3081#issuecomment-6511435399

   Good question. The gate asymmetry predates this PR: 282ba5cd6d 
(GROOVY-10922) made the getter always synthesize when the declaring class has 
no getter, and the setter only when every candidate is abstract. This PR 
changes position only.
   
   The getter is gated differently rather than not at all. 
`getGetterMethod(name)` on the declaring class searches that class, its 
superclasses and (GROOVY-11381) interface default getters, so a concrete getter 
in any of those suppresses the synthetic. The concrete candidates that can 
still share the list are a subclass override, which 
`removeCovariantsAndInterfaceEquivalents` drops through `isDerivedFrom` in 
either pair order, and an enclosing-class method, which the receiver-distance 
phase decides regardless of order. Position only affects the class-vs-interface 
rule, which drops the interface method solely when it comes second. When 
neither is dropped, the receiver distance ranks an interface implemented by the 
superclass at 1 and the superclass itself at 4, so the interface won whenever 
the receiver was narrowed to a subclass (`Impl` here); with the receiver 
exactly `Base`, master already chose the accessor.
   
   I checked with this ordering and with master's: a concrete superclass 
accessor and a concrete covariant subclass override (`Object` property, 
`String` getter) select the concrete method in both orderings, and the subclass 
case keeps the `()Ljava/lang/String;` descriptor. Both are now tests in 
`Groovy12552`, and the comment at the getter `add(0, …)` says why no 
abstract-only gate is needed there.
   
   Interface default accessors are a separate, pre-existing gap: neither 
synthetic is created, so the together-compile uses `invokeinterface` and the 
incremental one `invokevirtual`. I can file that separately.
   




> STC: property access calls an interface's accessor instead of the property's 
> own when the property's class is in the same compilation unit
> ------------------------------------------------------------------------------------------------------------------------------------------
>
>                 Key: GROOVY-12552
>                 URL: https://issues.apache.org/jira/browse/GROOVY-12552
>             Project: Groovy
>          Issue Type: Bug
>          Components: Static Type Checker
>    Affects Versions: 5.1.3
>            Reporter: James Daugherty
>            Priority: Major
>
> When an interface declares the setter and getter of a property that a class 
> implements as a Groovy property, static compilation calls the interface's 
> methods with {{invokeinterface}} when the class is compiled together with the 
> caller, and the class's own accessors with {{invokevirtual}} when the class 
> is already compiled. The behaviour at run time is the same, but the same 
> source compiles to a different class file depending on what else is compiled. 
> Groovy 4.0.33 calls the class's accessors in both cases.
> {code:groovy}
> interface Named {
>     void setName(String name)
>     String getName()
> }
> abstract class Base implements Named {
>     String name
> }
> class Impl extends Base {}
> @groovy.transform.CompileStatic
> class Svc {
>     String rename() {
>         Base base = new Impl()
>         base.name = 'x'     // invokeinterface Named.setName when Base is in 
> the unit, invokevirtual Impl.setName when it is compiled
>         base.setName('y')
>         base.getName()
>     }
> }
> {code}
> Reproduced with 5.1.3 and master. A reproducer with a groovyc script and an 
> incremental Gradle build: 
> https://github.com/jdaugherty/groovy-property-accessor-interface-method
> *Cause*
> A property of a class in the compilation unit does not have its accessors yet 
> when static type checking runs. {{StaticTypeCheckingVisitor.findMethod}} adds 
> a synthetic accessor for the property when every method found is abstract 
> (GROOVY-10922), but after the interface's method. 
> {{StaticTypeCheckingSupport.removeCovariantsAndInterfaceEquivalents}} only 
> drops an interface method that comes after the method of a class implementing 
> it, so the interface method stays and is chosen. A compiled class lists its 
> real accessor first, and the interface method is dropped.
> *Proposed fix*
> Add the synthetic accessor first, where a compiled class has its accessor, so 
> that it is preferred over the interface method the same way.
> Found while making incremental builds of a Grails application produce the 
> same classes as a full build; see also GROOVY-12550 and GROOVY-12544.



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

Reply via email to