Paul King created GROOVY-12649:
----------------------------------
Summary: SC: explicit spread over a Set or other non-List Iterable
that declares the property casts each element to the receiver type
Key: GROOVY-12649
URL: https://issues.apache.org/jira/browse/GROOVY-12649
Project: Groovy
Issue Type: Bug
Components: Static compilation
Affects Versions: 6.0.0, 5.1.3, 4.0.33
Reporter: Paul King
Description:
Under {{@CompileStatic}}, an explicit spread {{coll*.prop}} over a {{Set}}
subclass, or over any other non-List {{Iterable}}, fails at runtime if the
receiver's own class declares {{prop}} (as a field or a getter). The compiled
code reads the receiver's property from each element instead, casting the
element to the receiver type:
{code:groovy}
class Person { String name; Person(String n) { name = n } }
class PeopleSet extends LinkedHashSet<Person> { String name = 'the set' }
@groovy.transform.CompileStatic
def test() {
def s = new PeopleSet()
s << new Person('Alice') << new Person('Bob')
assert s.name == 'the set' // ok
assert s*.name == ['Alice', 'Bob'] // GroovyCastException: Cannot cast
object 'Person@...' with class 'Person' to class 'PeopleSet'
}
test()
{code}
Dynamic Groovy and {{@TypeChecked}} return {{[Alice, Bob]}}: an explicit spread
always reads the elements.
||Receiver ({{x*.name}})||dynamic||CS ≤ 4.0.5||CS 4.0.24 → master||
|{{Set}} subclass declaring field {{name}}|[Alice, Bob]|[Alice,
Bob]|GroovyCastException|
|{{Set}} subclass declaring {{getName()}}|[Alice, Bob]|[Alice,
Bob]|GroovyCastException|
|{{Iterable}} (not a Collection) declaring field {{name}}|[Alice, Bob]|[Alice,
Bob]|GroovyCastException|
|plain {{Set<Person>}}|[Alice, Bob]|[Alice, Bob]|[Alice, Bob]|
Seen on JDK 17 with 4.0.24, 4.0.33, 5.0.8, 5.1.3, 6.0.0 and master. It works on
3.0.25 and 4.0.5, so this is a regression within the 4.0.x line. I haven't
bisected it; 9e2ed767e3 (GROOVY-5568, GROOVY-10815, "SC: groovy method as
property", first in 4.0.7) changed how SC compiles property access and is a
likely candidate.
The {{List}} form of this ({{people*.name}} where a List subclass declares
{{name}}, or gets it from an extension method) is addressed by GROOVY-12534 (PR
#3058). Its spread skip in {{StaticTypeCheckingVisitor.existsProperty}} is
limited to receivers implementing {{List}}, so the cases above still fail with
that change applied. Widening that check to any receiver of an explicit spread
may be enough, but I haven't tested it.
Related: GROOVY-6311 (resolved), where CS rejected spread over a {{Set}} at
compile time.
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)