[
https://issues.apache.org/jira/browse/GROOVY-12476?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18130131#comment-18130131
]
ASF GitHub Bot commented on GROOVY-12476:
-----------------------------------------
testlens-app[bot] commented on PR #3005:
URL: https://github.com/apache/groovy/pull/3005#issuecomment-6333274274
## 🚨 TestLens detected 1 failed test 🚨
### Test Summary
> [!TIP]
> Inspect all details with the [TestLens
CLI](https://testlens.app/docs/features/cli): `testlens pr 3005`
#### [Build and test / lts \(17,
macos-latest\)](https://github.com/apache/groovy/actions/runs/39726116301/job/119650277518?pr=3005)
> :groovy-ant:test
| Test | Runs | Flakiness |
|---|---|--:|
| AntTest > testDefineTarget\_groovy2900\(\) | ❌ | 0% 🟢 |
🏷️ Commit: 7079b3711fd6cfc62fb9ec1c59e880b46aaf221f
▶️ Tests: 66 executed
🟡 Checks: 1/31 completed
### Test Failures
<details><summary><strong>AntTest > testDefineTarget_groovy2900()</strong>
(:groovy-ant:test in <a
href="https://github.com/apache/groovy/actions/runs/39726116301/job/119650277518?pr=3005">Build
and test / lts (17, macos-latest)</a>)</summary>
```
Assertion failed:
assert customListener.spoof.toString() == '''\ started:
echo[message:myTestTarget] finished: echo[message:myTestTarget] started:
echo[message:myTestTarget3] finished: echo[message:myTestTarget3] started:
echo[message:myTestTarget] finished: echo[message:myTestTarget] started:
echo[message:myTestTarget2] finished: echo[message:myTestTarget2] started:
echo[message:myTestTarget4] finished: echo[message:myTestTarget4] '''
| | | |
| | | false
| | 'started: echo{message=myTestTarget}\nfinished:
echo{message=myTestTarget}\nstarted: echo{message=myTestTarget3}\nfinished:
echo{message=myTestTarget3}\nstarted: echo{message=myTestTarget}\nfinished:
echo{message=myTestTarget}\nstarted: echo{message=myTestTarget2}\nfinished:
echo{message=myTestTarget2}\nstarted: echo{message=myTestTarget4}\nfinished:
echo{message=myTestTarget4}\n'
| "started: echo{message=myTestTarget}\nfinished:
echo{message=myTestTarget}\nstarted: echo{message=myTestTarget3}\nfinished:
echo{message=myTestTarget3}\nstarted: echo{message=myTestTarget}\nfinished:
echo{message=myTestTarget}\nstarted: echo{message=myTestTarget2}\nfinished:
echo{message=myTestTarget2}\nstarted: echo{message=myTestTarget4}\nfinished:
echo{message=myTestTarget4}\n"
groovy.ant.SimpleListener@3728a578
at
org.codehaus.groovy.runtime.InvokerHelper.createAssertError(InvokerHelper.java:409)
at groovy.ant.AntTest.testDefineTarget_groovy2900(AntTest.groovy:297)
```
</details>
### Rerun Controls
> [!NOTE]
> Checks are currently running using the configuration below.
Select tests to mute in this pull request:
🔲 AntTest > testDefineTarget\_groovy2900\(\) <!
> STC: toString() on an interface-typed receiver selects DGM toString(Object)
> over the runtime override (regression from 4.x)
> ---------------------------------------------------------------------------------------------------------------------------
>
> Key: GROOVY-12476
> URL: https://issues.apache.org/jira/browse/GROOVY-12476
> Project: Groovy
> Issue Type: Bug
> Components: Static compilation, Static Type Checker
> Affects Versions: 5.0.3, 5.0.8, 5.1.3, 6.0.0
> Environment: OpenJDK 25.0.4 (25.0.4+9-LTS), macOS. Groovy 4.0.33 is
> unaffected; 5.0.3, 5.0.8, 5.1.3, 6.0.0-RC-3 and 6.0.0 reproduce.
> Reporter: James Daugherty
> Priority: Major
> Attachments: InterfaceToString.groovy
>
>
> h3. Summary
> Under {{@CompileStatic}} (and {{@TypeChecked}}), a call to {{toString()}} on
> a receiver whose *static type is an interface* ({{List}}, {{Map}}, or any
> user-defined interface) now resolves to the extension method
> {{DefaultGroovyMethods#toString(Object)}} instead of
> {{java.lang.Object#toString()}}. The extension renders through
> {{InvokerHelper.format}}, which formats any {{Collection}} or {{Map}} itself
> and never calls the runtime class's {{toString()}} override.
> Groovy 4.0.33 resolved the same call to {{Object#toString()}} and emitted a
> virtual call, which reached the override. Dynamic dispatch selects the
> override in every version, so adding {{@CompileStatic}} now changes a
> program's output. Nothing fails at compile time.
> h3. Reproducer
> {code:groovy}
> import groovy.transform.CompileStatic
> interface Element {}
> class JsonList extends ArrayList<Object> implements Element {
> @Override
> String toString() { '[{"name":"name","value":"x"}]' }
> }
> @CompileStatic
> class Check {
> static String viaInterface(Element e) { e.toString() }
> static String viaList(List e) { e.toString() }
> static String viaClass(JsonList e) { e.toString() }
> static String viaObject(Object e) { e.toString() }
> }
> JsonList list = new JsonList()
> list << [name: 'name', value: 'x']
> println "Groovy ${GroovySystem.version}"
> println "static, receiver typed Element : ${Check.viaInterface(list)}"
> println "static, receiver typed List : ${Check.viaList(list)}"
> println "static, receiver typed JsonList: ${Check.viaClass(list)}"
> println "static, receiver typed Object : ${Check.viaObject(list)}"
> println "dynamic : ${list.toString()}"
> {code}
> Groovy 4.0.33:
> {noformat}
> static, receiver typed Element : [{"name":"name","value":"x"}]
> static, receiver typed List : [{"name":"name","value":"x"}]
> static, receiver typed JsonList: [{"name":"name","value":"x"}]
> static, receiver typed Object : [[name:name, value:x]]
> dynamic : [{"name":"name","value":"x"}]
> {noformat}
> Groovy 5.0.3, 5.0.8, 5.1.3, 6.0.0-RC-3 and 6.0.0 (identical output):
> {noformat}
> static, receiver typed Element : [[name:name, value:x]]
> static, receiver typed List : [[name:name, value:x]]
> static, receiver typed JsonList: [{"name":"name","value":"x"}]
> static, receiver typed Object : [[name:name, value:x]]
> dynamic : [{"name":"name","value":"x"}]
> {noformat}
> Run with {{java -cp groovy-<version>.jar groovy.ui.GroovyMain
> InterfaceToString.groovy}} on OpenJDK 25.0.4.
> h3. Analysis
> Both versions collect the same two candidates for the call:
> {{java.lang.Object#toString()}}, which
> {{StaticTypeCheckingVisitor#findMethodsWithGenerated}} adds for interface
> receivers, and the extension {{DefaultGroovyMethods#toString(Object)}}. They
> rank them differently:
> * 4.0.33 {{StaticTypeCheckingSupport#chooseBestMethod}} computes one summed
> distance per candidate: the parameter/argument distance (for an extension
> method the receiver is prepended as the self argument), plus the
> declaring-class distance, plus the extension bonus. The self-argument
> distance from the interface to {{Object}} outweighs the bonus, so
> {{Object#toString()}} wins.
> * 5.x and 6.x {{StaticTypeCheckingSupport#chooseBestMethods}} (introduced by
> the GROOVY-8788 fix, commit 14946ff27a60d74b5d07c1b9a63f02624283c863 in
> 5.0.0-alpha-1, which also removed {{getExtensionDistance}}) scores in phases.
> Phase 1 compares declared parameters only, so the extension's self parameter
> no longer counts and both candidates score 0. Phase 2 compares declaring
> class to receiver; both declare in {{Object}}, so they tie again. Phase 3
> prefers the extension method on a tie (the GROOVY-6849 rule), so the
> extension wins for every interface-typed receiver.
> A receiver typed as a class ({{JsonList}}, {{ArrayList}}) is unaffected
> because the class's own {{toString()}} has declaring-class distance 0 and
> wins phase 2. An {{Object}}-typed receiver already resolved to the extension
> in 4.x. The regression is that interface types, which per JLS 9.2 implicitly
> declare the public methods of {{Object}}, are now scored like {{Object}}
> instead of like a class, so {{toString()}} through an interface-typed
> variable, parameter or return type no longer reaches the override.
> h3. Where it bit
> Grails 8 (Groovy 5.1.3): a statically compiled service normalized JSON with
> {{grails.converters.JSON.parse(text).toString()}}. {{JSON.parse}} returns the
> interface {{JSONElement}}; the runtime {{JSONArray}} overrides {{toString()}}
> to render JSON. Under Groovy 5 the stored text silently became Groovy list
> notation ({{[[name:name, value:x]]}}) and failed to parse later. The same
> expression is correct in 4.0.33 and in dynamic Groovy.
> h3. Expected
> {{toString()}} on an interface-typed receiver should compile to a virtual
> {{Object#toString()}} call, as 4.0.33 did and as dynamic Groovy does, or at
> least agree with dynamic dispatch for a class that declares its own
> {{toString()}}.
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)