Skip to content

Latest commit

 

History

History
94 lines (70 loc) · 3.63 KB

File metadata and controls

94 lines (70 loc) · 3.63 KB

Collection literals working group meeting for April 4, 2023

Agenda

  • Allow efficient construction of collections that are typically represented as arrays, particularly ImmutableArray<T>.

Discussion

We discussed possible construction method patterns that would allow the compiler to write directly to the underlying storage for collections such as ImmutableArray<T> and List<T>, and avoid extra allocations or copying in cases where the collection has a known length.

There were two promising approaches that provide equivalent capability, with the difference of whether the underlying storage is allocated by the caller or by the collection.

Pass ownership of array

One possible approach is a construction method that takes an array as an argument and uses that array directly as the underlying storage of the collection, with the caller passing ownership of the array to the collection instance.

For instance, given the following method for creating an ImmutableArray<T>:

// Create ImmutableArray<T> using the array as the underlying storage.
public static ImmutableArray<T> AsImmutableArray<T>(T[] array)
{
    // ...
}

The compiler could translate ImmutableArray<T> result = [x, ..e, y]; to:

T[] __array = new T[e.Count + 2];

int __i = 0;
__array[__i++] = x;
foreach (T __t in e)
    __array[__i++] = __t;
__array[__i++] = y;

ImmutableArray<T> result = AsImmutableArray(__array);

Write directly to storage span

An alternative is a construction method that creates a collection of a given size and returns the collection and a span that refers to the underlying storage, and the caller populates the collection using the span.

For instance, given the following method for creating a ImmutableArray<T>:

// Create ImmutableArray<T> and return a span to the underlying storage.
public static ImmutableArray<T> CreateImmutableArray<T>(int size, out Span<T> span)
{
    // ...
}

The compiler could translate ImmutableArray<T> result = [x, ..e, y]; to:

Span<T> __span;
ImmutableArray<T> result = CreateImmutableArray(e.Count + 2, out __span);

int __i = 0;
__span[__i++] = x;
foreach (T __t in e)
    __span[__i++] = __t;
__span[__i++] = y;

Runtime libraries

The BCL could provide construction methods for ImmutableArray<T> and List<T>, and perhaps a few additional collection types where the underlying storage can be represented as a span.

The methods could be implemented in System.Runtime.InteropServices or System.Runtime.CompilerServices to indicate the intended use is from compilers or interop.

Compiler use

The compiler will use a construction method for the corresponding collection type when creating a collection of known length.

When creating collections of unknown length, using a construction method may not provide an advantage over existing alternatives since either approach may require resizing and reallocating during construction.

What if the collection literal has a known length but the resulting collection instance is mutable? Is there an advantage to providing an initial length?

Construction methods could be registered with attributes on the collection type.

For instance, an attribute could identify the construction method by containing type and method name.

[CollectionLiteralConstruction(typeof(ImmutableCollections), "AsImmutableArray")]
public struct ImmutableArray<T>
{
    // ...
}

public static class ImmutableCollections
{
    public static ImmutableArray<T> AsImmutableArray<T>(T[] array)
    {
        // ...
    }
}

A collection type should have at most one construction method. Most collection types will have none.