Skip to content

Commit

Permalink
Add collection literal support for immutable collections (#88470)
Browse files Browse the repository at this point in the history
* Add collection literal support for immutable collections

Add the CollectionBuilderAttribute and use it on the supported immutable collections

* Address feedback
  • Loading branch information
stephentoub committed Jul 17, 2023
1 parent 58bca2c commit ec025ad
Show file tree
Hide file tree
Showing 13 changed files with 77 additions and 0 deletions.
Expand Up @@ -210,6 +210,7 @@ public static partial class ImmutableArray
public static System.Collections.Immutable.ImmutableArray<T> ToImmutableArray<T>(this System.ReadOnlySpan<T> items) { throw null; }
public static System.Collections.Immutable.ImmutableArray<T> ToImmutableArray<T>(this System.Span<T> items) { throw null; }
}
[System.Runtime.CompilerServices.CollectionBuilderAttribute(typeof(System.Collections.Immutable.ImmutableArray), "Create")]
public readonly partial struct ImmutableArray<T> : System.Collections.Generic.ICollection<T>, System.Collections.Generic.IEnumerable<T>, System.Collections.Generic.IList<T>, System.Collections.Generic.IReadOnlyCollection<T>, System.Collections.Generic.IReadOnlyList<T>, System.Collections.ICollection, System.Collections.IEnumerable, System.Collections.IList, System.Collections.Immutable.IImmutableList<T>, System.Collections.IStructuralComparable, System.Collections.IStructuralEquatable, System.IEquatable<System.Collections.Immutable.ImmutableArray<T>>
{
private readonly T[] array;
Expand Down Expand Up @@ -566,6 +567,7 @@ public static partial class ImmutableHashSet
public static System.Collections.Immutable.ImmutableHashSet<TSource> ToImmutableHashSet<TSource>(this System.Collections.Immutable.ImmutableHashSet<TSource>.Builder builder) { throw null; }
}

[System.Runtime.CompilerServices.CollectionBuilderAttribute(typeof(System.Collections.Immutable.ImmutableHashSet), "Create")]
#if NETCOREAPP
public sealed partial class ImmutableHashSet<T> : System.Collections.Generic.ICollection<T>, System.Collections.Generic.IEnumerable<T>, System.Collections.Generic.IReadOnlyCollection<T>, System.Collections.Generic.ISet<T>, System.Collections.Generic.IReadOnlySet<T>, System.Collections.ICollection, System.Collections.IEnumerable, System.Collections.Immutable.IImmutableSet<T>
#else
Expand Down Expand Up @@ -700,6 +702,7 @@ public static partial class ImmutableList
public static System.Collections.Immutable.ImmutableList<TSource> ToImmutableList<TSource>(this System.Collections.Generic.IEnumerable<TSource> source) { throw null; }
public static System.Collections.Immutable.ImmutableList<TSource> ToImmutableList<TSource>(this System.Collections.Immutable.ImmutableList<TSource>.Builder builder) { throw null; }
}
[System.Runtime.CompilerServices.CollectionBuilderAttribute(typeof(System.Collections.Immutable.ImmutableList), "Create")]
public sealed partial class ImmutableList<T> : System.Collections.Generic.ICollection<T>, System.Collections.Generic.IEnumerable<T>, System.Collections.Generic.IList<T>, System.Collections.Generic.IReadOnlyCollection<T>, System.Collections.Generic.IReadOnlyList<T>, System.Collections.ICollection, System.Collections.IEnumerable, System.Collections.IList, System.Collections.Immutable.IImmutableList<T>
{
internal ImmutableList() { }
Expand Down Expand Up @@ -883,6 +886,7 @@ public static partial class ImmutableQueue
public static System.Collections.Immutable.ImmutableQueue<T> Create<T>(System.ReadOnlySpan<T> items) { throw null; }
public static System.Collections.Immutable.IImmutableQueue<T> Dequeue<T>(this System.Collections.Immutable.IImmutableQueue<T> queue, out T value) { throw null; }
}
[System.Runtime.CompilerServices.CollectionBuilderAttribute(typeof(System.Collections.Immutable.ImmutableQueue), "Create")]
public sealed partial class ImmutableQueue<T> : System.Collections.Generic.IEnumerable<T>, System.Collections.IEnumerable, System.Collections.Immutable.IImmutableQueue<T>
{
internal ImmutableQueue() { }
Expand Down Expand Up @@ -1064,6 +1068,7 @@ public static partial class ImmutableSortedSet
public static System.Collections.Immutable.ImmutableSortedSet<TSource> ToImmutableSortedSet<TSource>(this System.Collections.Immutable.ImmutableSortedSet<TSource>.Builder builder) { throw null; }
}

[System.Runtime.CompilerServices.CollectionBuilderAttribute(typeof(System.Collections.Immutable.ImmutableSortedSet), "Create")]
#if NETCOREAPP
public sealed partial class ImmutableSortedSet<T> : System.Collections.Generic.ICollection<T>, System.Collections.Generic.IEnumerable<T>, System.Collections.Generic.IList<T>, System.Collections.Generic.IReadOnlyCollection<T>, System.Collections.Generic.IReadOnlyList<T>, System.Collections.Generic.ISet<T>, System.Collections.Generic.IReadOnlySet<T>, System.Collections.ICollection, System.Collections.IEnumerable, System.Collections.IList, System.Collections.Immutable.IImmutableSet<T>
#else
Expand Down Expand Up @@ -1192,6 +1197,7 @@ public static partial class ImmutableStack
public static System.Collections.Immutable.ImmutableStack<T> Create<T>(System.ReadOnlySpan<T> items) { throw null; }
public static System.Collections.Immutable.IImmutableStack<T> Pop<T>(this System.Collections.Immutable.IImmutableStack<T> stack, out T value) { throw null; }
}
[System.Runtime.CompilerServices.CollectionBuilderAttribute(typeof(System.Collections.Immutable.ImmutableStack), "Create")]
public sealed partial class ImmutableStack<T> : System.Collections.Generic.IEnumerable<T>, System.Collections.IEnumerable, System.Collections.Immutable.IImmutableStack<T>
{
internal ImmutableStack() { }
Expand Down
Expand Up @@ -13,6 +13,10 @@
<ProjectReference Include="$(LibrariesProjectRoot)System.Runtime\ref\System.Runtime.csproj" />
</ItemGroup>

<ItemGroup Condition="!$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net8.0'))">
<Compile Include="$(CoreLibSharedDir)System\Runtime\CompilerServices\CollectionBuilderAttribute.cs" Link="System\Runtime\CompilerServices\CollectionBuilderAttribute.cs" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFrameworkIdentifier)' != '.NETCoreApp'">
<PackageReference Include="System.Memory" Version="$(SystemMemoryVersion)" />
</ItemGroup>
Expand Down
Expand Up @@ -147,6 +147,10 @@ The System.Collections.Immutable library is built-in as part of the shared frame
<None Include="Interfaces.cd" />
</ItemGroup>

<ItemGroup Condition="!$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net8.0'))">
<Compile Include="$(CoreLibSharedDir)System\Runtime\CompilerServices\CollectionBuilderAttribute.cs" Link="System\Runtime\CompilerServices\CollectionBuilderAttribute.cs" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == '$(NetCoreAppCurrent)'">
<Reference Include="System.Collections" />
<Reference Include="System.Linq" />
Expand Down
Expand Up @@ -6,10 +6,12 @@
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;

namespace System.Collections.Immutable
{
[CollectionBuilder(typeof(ImmutableArray), nameof(ImmutableArray.Create))]
public readonly partial struct ImmutableArray<T> : IReadOnlyList<T>, IList<T>, IEquatable<ImmutableArray<T>>, IList, IImmutableArray, IStructuralComparable, IStructuralEquatable, IImmutableList<T>
{
/// <summary>
Expand Down
Expand Up @@ -4,13 +4,15 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;

namespace System.Collections.Immutable
{
/// <summary>
/// An immutable unordered hash set implementation.
/// </summary>
/// <typeparam name="T">The type of elements in the set.</typeparam>
[CollectionBuilder(typeof(ImmutableHashSet), nameof(ImmutableHashSet.Create))]
[DebuggerDisplay("Count = {Count}")]
[DebuggerTypeProxy(typeof(ImmutableEnumerableDebuggerProxy<>))]
#if NETCOREAPP
Expand Down
Expand Up @@ -5,13 +5,15 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.CompilerServices;

namespace System.Collections.Immutable
{
/// <summary>
/// An immutable list implementation.
/// </summary>
/// <typeparam name="T">The type of elements in the set.</typeparam>
[CollectionBuilder(typeof(ImmutableList), nameof(ImmutableList.Create))]
[DebuggerDisplay("Count = {Count}")]
[DebuggerTypeProxy(typeof(ImmutableEnumerableDebuggerProxy<>))]
public sealed partial class ImmutableList<T> : IImmutableList<T>, IList<T>, IList, IOrderedCollection<T>, IImmutableListQueries<T>, IStrongEnumerable<T, ImmutableList<T>.Enumerator>
Expand Down
Expand Up @@ -5,13 +5,15 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.CompilerServices;

namespace System.Collections.Immutable
{
/// <summary>
/// An immutable queue.
/// </summary>
/// <typeparam name="T">The type of elements stored in the queue.</typeparam>
[CollectionBuilder(typeof(ImmutableQueue), nameof(ImmutableQueue.Create))]
[DebuggerDisplay("IsEmpty = {IsEmpty}")]
[DebuggerTypeProxy(typeof(ImmutableEnumerableDebuggerProxy<>))]
public sealed partial class ImmutableQueue<T> : IImmutableQueue<T>
Expand Down
Expand Up @@ -5,6 +5,7 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.CompilerServices;

namespace System.Collections.Immutable
{
Expand All @@ -16,6 +17,7 @@ namespace System.Collections.Immutable
/// We implement <see cref="IReadOnlyList{T}"/> because it adds an ordinal indexer.
/// We implement <see cref="IList{T}"/> because it gives us <see cref="IList{T}.IndexOf"/>, which is important for some folks.
/// </devremarks>
[CollectionBuilder(typeof(ImmutableSortedSet), nameof(ImmutableSortedSet.Create))]
[DebuggerDisplay("Count = {Count}")]
[DebuggerTypeProxy(typeof(ImmutableEnumerableDebuggerProxy<>))]
#if NETCOREAPP
Expand Down
Expand Up @@ -5,13 +5,15 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.CompilerServices;

namespace System.Collections.Immutable
{
/// <summary>
/// An immutable stack.
/// </summary>
/// <typeparam name="T">The type of element stored by the stack.</typeparam>
[CollectionBuilder(typeof(ImmutableStack), nameof(ImmutableStack.Create))]
[DebuggerDisplay("IsEmpty = {IsEmpty}; Top = {_head}")]
[DebuggerTypeProxy(typeof(ImmutableEnumerableDebuggerProxy<>))]
public sealed partial class ImmutableStack<T> : IImmutableStack<T>
Expand Down
Expand Up @@ -780,6 +780,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\CallerLineNumberAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\CallerMemberNameAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\CallingConventions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\CollectionBuilderAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\CompExactlyDependsOnAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\CompilationRelaxations.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\CompilationRelaxationsAttribute.cs" />
Expand Down
@@ -0,0 +1,35 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface, Inherited = false)]
#if SYSTEM_PRIVATE_CORELIB
public
#else
internal
#endif
sealed class CollectionBuilderAttribute : Attribute
{
/// <summary>Initialize the attribute to refer to the <paramref name="methodName"/> method on the <paramref name="builderType"/> type.</summary>
/// <param name="builderType">The type of the builder to use to construct the collection.</param>
/// <param name="methodName">The name of the method on the builder to use to construct the collection.</param>
/// <remarks>
/// <paramref name="methodName"/> must refer to a static method that accepts a single parameter of
/// type <see cref="ReadOnlySpan{T}"/> and returns an instance of the collection being built containing
/// a copy of the data from that span. In future releases of .NET, additional patterns may be supported.
/// </remarks>
public CollectionBuilderAttribute(Type builderType, string methodName)
{
BuilderType = builderType;
MethodName = methodName;
}

/// <summary>Gets the type of the builder to use to construct the collection.</summary>
public Type BuilderType { get; }

/// <summary>Gets the name of the method on the builder to use to construct the collection.</summary>
/// <remarks>This should match the metadata name of the target method. For example, this might be ".ctor" if targeting the type's constructor.</remarks>
public string MethodName { get; }
}
}
7 changes: 7 additions & 0 deletions src/libraries/System.Runtime/ref/System.Runtime.cs
Expand Up @@ -12615,6 +12615,13 @@ public sealed partial class CallerMemberNameAttribute : System.Attribute
{
public CallerMemberNameAttribute() { }
}
[System.AttributeUsage(System.AttributeTargets.Class | System.AttributeTargets.Interface | System.AttributeTargets.Struct, Inherited = false)]
public sealed class CollectionBuilderAttribute : Attribute
{
public CollectionBuilderAttribute(System.Type builderType, string methodName) { }
public System.Type BuilderType { get { throw null; } }
public string MethodName { get { throw null; } }
}
[System.FlagsAttribute]
public enum CompilationRelaxations
{
Expand Down
Expand Up @@ -47,6 +47,14 @@ public static void CallerMemberNameAttributeTests()
new CallerMemberNameAttribute();
}

[Fact]
public static void CollectionBuilderAttributeTests()
{
var attr = new CollectionBuilderAttribute(typeof(AttributesTests), "Create");
Assert.Same(typeof(AttributesTests), attr.BuilderType);
Assert.Equal("Create", attr.MethodName);
}

[Fact]
public static void CompilationRelaxationsAttributeTests()
{
Expand Down

0 comments on commit ec025ad

Please sign in to comment.