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)

Reply via email to