Paul King created GROOVY-12760:
----------------------------------

             Summary: STC: call to a property accessor that also exists as an 
interface default method compiles to different bytecode depending on whether 
the class is compiled in the same unit
                 Key: GROOVY-12760
                 URL: https://issues.apache.org/jira/browse/GROOVY-12760
             Project: Groovy
          Issue Type: Bug
          Components: Static Type Checker
    Affects Versions: 6.0.0, 5.1.3, 4.0.33
            Reporter: Paul King


When an interface declares *default* accessor methods for a property that an 
implementing class declares, {{@CompileStatic}} calls on a subclass receiver 
are compiled differently depending on whether the class comes from source in 
the same compilation unit or from a .class file:

{code:groovy}
package p
interface Named {
    default String getName() { 'named' }
    default void setName(String n) { }
}
abstract class Base implements Named { String name }
class Impl extends Base { }

@groovy.transform.CompileStatic
class Svc {
    static String rename(Impl base) {
        base.name = 'x'
        base.setName('y')
        base.getName()
    }
}
{code}

|| ||compiled together||{{Svc}} compiled against .class files||
|4.0.33|{{invokevirtual Impl.setName}}, {{invokeinterface Named.setName}}, 
{{invokeinterface Named.getName}}|{{invokevirtual Impl.setName}} ×2, 
{{invokevirtual Impl.getName}}|
|5.1.3, 6.0.0|{{invokeinterface Named.setName}} ×2, {{invokeinterface 
Named.getName}}|{{invokevirtual Impl.setName}} ×2, {{invokevirtual 
Impl.getName}}|

Runtime behaviour is the same, because dispatch reaches {{Base}}'s generated 
accessors either way. But the class files differ, which breaks incremental 
compilation reproducing a full build (see 
[gradle/gradle#39622|https://github.com/gradle/gradle/pull/39622]).

The cause is that while the class is compiled from source, the type checker 
sees no accessor in {{Base}}: property accessors are only generated by the 
Verifier, after type checking. For abstract interface accessors, {{findMethod}} 
adds a synthetic accessor node (GROOVY-10922, GROOVY-12552). But that doesn't 
happen when the interface method is a default method: the getter's synthetic is 
suppressed because {{getGetterMethod}} finds the default getter (GROOVY-11381), 
and the setter's is gated on all candidates being abstract. So the interface 
method is chosen. From a .class file, {{Base.getName}}/{{setName}} exist and 
are chosen.

Related: GROOVY-12552 (the same disparity for abstract interface accessors), 
GROOVY-11381, GROOVY-10922.




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

Reply via email to