Skip to content

Test plan for "collection literals" #66418

@jcouv

Description

@jcouv

The feature was renamed to "collection expressions".
Championed issue: dotnet/csharplang#5354
Speclet: https://github.com/dotnet/csharplang/blob/main/proposals/csharp-12.0/collection-expressions.md

Compiler

General Concerns

Parsing

  • Ambiguity scenarios
    • a?[b] (CollectionExpressionParsingTests.ConditionalAmbiguityX)
    • Attribute/collection literal confusion (CollectionExpressionParsingTests.AttributeOnStartOfLambda, CollectionExpressionParsingTests.TopLevelDotAccess_AttributeAmbiguityX)
    • Cast vs parenthesized expression (CollectionExpressionParsingTests.CastVsIndexAmbiguityX)
      • (a)[b]
      • (a?)[b]
      • (a<b>)[c]
    • Breaking change: _ = x ? y?[0] : z; // syntax error
  • As RValue
    • Indexing expression (CollectionExpressionParsingTests.AttemptToImmediatelyIndexInTopLevelStatement)
    • Direct invocation
    • Input to switch expression
    • Empty collection
    • Operators

Semantics

  • Definite assignment
  • Test in catch filter
    • Particularly with something that might require multiple spill levels, such as interpolated string handler element type
  • Usage in async methods
    • When builder type is a ref struct and there are no awaits in the elements
    • When builder type is a ref struct and there are awaits in the elements
      • With fallback ability (List)
      • Without fallback ability
  • Best common element type (BestCommonType_03, _04)
  • Type Inference:
    • Usage in generic arguments makes inferences from collection literal elements (TypeInference_03, _04)
    • Usage in lambda returns makes inferences from collection literal elements
    • Tuples of collection literals
    • Collection literals of tuples (TypeInference_37)
  • Target types:
    • var, object, dynamic
    • T[] (Array_*)
    • T[,] (Array_04)
    • Span<T>, ReadOnlySpan<T> (Span_*)
    • IEnumerable, ICollection, IList (InterfaceType_01)
    • IEnumerable<T>, ICollection<T>, IList<T>, IReadOnlyCollection<T>, IReadOnlyList<T> (InterfaceType_02)
    • collection initializer types: reference type, value type, type parameter (CollectionInitializerType_01, _02, TypeParameter_*)
    • custom construct methods
    • inline arrays
    • Element type has conversion from expression, like interpolated string handlers
  • Collection initializer types:
    • 0, 1, >1 Add() method (CollectionInitializerType_03, CollectionInitializerType_12)
    • inaccessible .ctor (CollectionInitializerType_05)
    • .ctor with/without int capacity
    • .ctor with all optional parameters (CollectionInitializerType_ConstructorOptionalParameters)
    • .ctor with single params parameter (CollectionInitializerType_ConstructorParamsArray)
    • .ctor with ref parameters
    • ref struct element type (RefStruct_03)
    • Obsolete methods
    • UnmanagedCallersOnly methods
    • CompilerFeatureRequired methods
    • Nullability mismatches
    • Collection builder type is a PIA
  • Spread collection type:
    • IEnumerable only (SpreadElement_10)
    • pattern-based enumerator
      • Without implementing IDisposable
      • With implementing IDisposable
    • ref struct iterator type
    • ref struct type
      • Without Dispose
      • With Dispose
    • with Length or Count property
    • Above marked Obsolete (SpreadElement_LengthObsolete)
    • Above marked UnmanagedCallersOnly
    • Above marked CompilerFeatureRequired
    • Embeds appropriate interop types when enumerator is an pia
  • Extension method invocation
    • type inference
      methods)
  • cannot be used as constants
  • Expression trees (ExpressionTrees)
  • Optimizations:
    • emit Array.Empty<T>() for IEnumerable<T> e = []; (ArrayEmpty_01)
    • avoid heap allocation when not observable: foreach (var b in [true, false]) { ... }
  • __arglist and ArgIterator usage
  • ref safety in elements
    • Element should not escape by value
    • Element should not escape by ref
    • stackalloc element
    • collection expressions targeting ref structs cannot be returned (RefSafety_Return_01)
  • Use as LValue
    • Directly assigning
    • Assigning into an indexer
    • Pass by ref (readonly)
    • Pass by in
      • Implicit (TypeInference_20)
      • Explicit (TypeInference_21)
    • ref (readonly) return
    • ref reassignment
  • Anonymous type usage
    • Assignment to property
    • Top-level usage
  • Direct slicing (1..2)
  • Direct indexing (^1)
  • Execution order of elements vs collection ctor
  • Direct method execution (MemberAccess_01, _02)
  • fixed statement immediately taking a pointer
  • Direct argument of a using block
  • Collection element subjected to a checked user-defined implicit conversion during creation
  • ReadOnlySpan<byte[]> x = ["abc"u8]; should behave like byte[] x = "abc"u8;
  • Suppression with ! (should produce a diagnostic)
  • Nullability analysis

Productivity

  • Completion
    • Verify that completion for [return: MyAttribute] and [return: expr] are both possible
  • Formatting: List<int> list = [1, 2, 3]; (no extra spaces)
  • Incomplete code typing experience
  • Find all on builder types/methods
  • F1 on the brackets
  • Immediate window compilation
  • DebuggerDisplay
  • Assigning values in the watch window
  • EE
  • EnC
  • Stepping through debugger with [Method1(), Method2()] should behave like array creation and other similar existing constructs

Open Questions

  • Syntax ambiguities:
    • (X)[i] cast collection literal or indexing?
    • [a ? [b] : c] conditional [a ? ([b]) : c] or key-value pair [(a ? [b]) : c]?
    • a ? b ? [c] : d nested indexer a ? (b?[c]) : d or nested collection literal a ? (b ? ([c]) : d)?
    • Range[] ranges = [range1, ..e, range2]; spread or range ..e?
  • Collection literals at the beginning of an expression statement?
    [1].ToString(); // error CS7014: Attributes are not valid in this context.
  • What are the requirements for a spread element type? Is a span spreadable for instance?
  • Is evaluation of elements interleaved with Add() calls?
  • Any type that implements IEnumerable and has an accessible parameterless constructor is a valid target type for []. Should the requirements for a collection initializer type be stronger than this?
  • Should multi-dimensional arrays be supported with nested collection literal syntax?
    int[,] a = [[1, 2], [3, 4]];
  • Should better function member prefer some constructible collection types over others?
    F([1, 2, 3]); // ambiguous?
    
    static void F(IEnumerable<int> arg) { }
    static void F(int[] arg) { }
  • When generating an intermediate List, should we use the well-known member List<T>.Add(T) rather than relying on lookup?
  • Should spread elements support target type?
    string[] a = [..b ? [null] : []];
  • Infer from spread element iteration type? See LDM-2023-09-20
    IEnumerable<int> e = [1, 2, 3];
    F([..e]);     // ok?
    F([..[4, 5]); // ok?
    
    static void F<T>(T[] arg) { }
  • Infer from lambda return expression? From 11.6.3.7, a lower-bound inference is made from the return type of the lambda expression. (TypeInference_40)
    F(() => [1]);
    F(() => { if (b) return []; return [2]; });
    
    static void F<T>(Func<T[]> arg) { }
  • Should IAsyncEnumerable<int> e be supported for [1, 2, await ..e]?

Metadata

Metadata

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions