Paul King created GROOVY-12586:
----------------------------------
Summary: A list literal assigned to a collection class is spread
into its constructor: "Vector v = [10]" is empty, and "@CompileStatic
LinkedList l = [3, 4, 5]" does not compile
Key: GROOVY-12586
URL: https://issues.apache.org/jira/browse/GROOVY-12586
Project: Groovy
Issue Type: Bug
Components: Static Type Checker
Reporter: Paul King
Fix For: 7.x
h3. Problem
When a list literal is assigned to a type that is not a supertype of
{{ArrayList}} or {{LinkedHashSet}}, Groovy treats the literal as constructor
arguments ("Groovy-style constructor", {{Dimension d = [100, 200]}}). For
collection classes that is rarely what is meant, and the result depends on
which constructors the class happens to have:
||Declaration||dynamic||{{@CompileStatic}}||
|{{Vector v = [10]}}|{{Vector []}} (capacity 10)|{{Vector []}} (capacity 10)|
|{{PriorityQueue q = [10]}}|{{PriorityQueue []}}|{{PriorityQueue []}}|
|{{ArrayDeque d = [10]}}|{{ArrayDeque []}}|{{ArrayDeque []}}|
|{{LinkedList l = [10]}}|{{LinkedList [10]}}|error: Cannot find matching
constructor java.util.LinkedList(int)|
|{{TreeSet t = [10]}}|{{TreeSet [10]}}|error: Cannot find matching constructor
java.util.TreeSet(int)|
|{{LinkedList l = [3, 4, 5]}}|{{LinkedList [3, 4, 5]}}|error: Cannot find
matching constructor java.util.LinkedList(int, int, int)|
|{{Vector v = [3, 1, 2]}}|{{Vector [3, 1, 2]}}|error|
|{{ArrayDeque d = [3, 1, 2]}}, {{PriorityQueue}},
{{ConcurrentLinkedQueue}}|works|error|
|{{Stack s = [3, 1, 2]}}|{{GroovyCastException}}|error|
|{{CopyOnWriteArrayList c = [3, 1, 2]}}|works|works, only because its {{(E[])}}
constructor matches as varargs|
* A single-element literal whose element fits an {{int}} capacity constructor
silently gives an empty collection, in dynamic and static code alike.
* Under {{@CompileStatic}}, a multi-element literal fails to compile for most
collection classes, including the syntax guide's own example ({{LinkedList
otherLinked = [3, 4, 5]}}, {{SyntaxTest}} tag {{coercion_of_list}}), which
works in dynamic code.
Map literals do not have this problem.
{{StaticTypeCheckingVisitor.addMapAssignmentConstructorErrors}} passes the
whole map to the constructor, so {{TreeMap m = [b: 2, a: 1]}} and
{{ConcurrentHashMap}} use the copy constructor and work in both modes.
{{addListAssignmentConstructorErrors}} instead spreads the elements
({{checkGroovyStyleConstructor(leftRedirect, types, ...)}}). Dynamically,
{{DefaultTypeTransformation.continueCastOnSAM}} likewise tries the
element-spread constructor before the copy constructor.
h3. Proposal
For a target type that implements {{java.util.Collection}}, a list literal (or,
dynamically, any collection value) is copied, never spread into a constructor:
# a constructor taking a single {{Collection}}, if there is one;
# otherwise the no-arg constructor followed by {{addAll}} (this also fixes
{{Stack}});
# for an interface or abstract target, the default implementation from the
default-implementations table in [the improvement issue for groups 1, 2 and 4].
The Groovy-style constructor (element spreading) stays as it is for types that
do not implement {{Collection}}, so {{Dimension d = [100, 200]}} and similar
are unaffected. The type checker should accept the same declarations,
generating the copy, so that dynamic and static code agree.
The "List and map constructors" section of the type-checking docs
({{core-semantics.adoc}}) needs a sentence saying that the list rule does not
apply to {{Collection}} types.
h3. Behaviour change
{{Vector v = [10]}}, {{PriorityQueue q = [10]}} and {{ArrayDeque d = [10]}}
would hold the element {{10}} instead of being empty. Code that uses a
one-element literal to mean "initial capacity" would change. That seems
unlikely to be intended, and {{new Vector(10)}} says it directly. Hence 7.0.0
rather than a patch release.
h3. Tests
* Each row of the table above, in dynamic and {{@CompileStatic}} code,
including an empty literal (which already works).
* {{Dimension}}/{{Point}}-style constructor literals, unchanged.
* The {{SyntaxTest}} {{coercion_of_list}} example run under {{@CompileStatic}}.
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)