Skip to content

[API Proposal]: Apply [OverloadResolutionPriority] to Span-based overloads #109549

@stephentoub

Description

@stephentoub

Background and motivation

System.MemoryExtensions provides extension methods on things related to span and memory. There are a bunch of extensions on spans, e.g. IndexOf, and for a long time we were doubling up these methods, with one overload where the receiver was a ReadOnlySpan<T> and one overload where it was a Span. That's because C# 12 and earlier didn't recognize spans as a first-class feature, and as a result extensions on ReadOnlySpan<T> wouldn't be applicable for Span<T>. Starting in C# 13 under LangVersion preview, and in C# 14 on always, spans are now first-class citizens, and extensions on ReadOnlySpan<T> will now apply to Span<T> (and T[], and string if T==char, and so on). As a result, most of the Span<T>-based overloads are now defunct. Those overloads mostly just delegate to the ReadOnlySpan<T>-based implementation. We can't remove them, but as of C# 13 with [OverloadResolutionPriority] we can now deprioritize them, so that the compiler will prefer to bind to the ReadOnlySpan<T>-based overloads. Doing this will avoid the extra call / pressure on the JIT to inline, and will avoid those extra methods being kept around when trimming.

API Proposal

namespace System
{
    public static partial class MemoryExtensions
    {
+       [OverloadResolutionPriority(-1)]
        public static int BinarySearch<T>(this System.Span<T> span, System.IComparable<T> comparable) { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static int BinarySearch<T, TComparer>(this System.Span<T> span, T value, TComparer comparer) where TComparer : System.Collections.Generic.IComparer<T>, allows ref struct { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static int BinarySearch<T, TComparable>(this System.Span<T> span, TComparable comparable) where TComparable : System.IComparable<T>, allows ref struct { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static int CommonPrefixLength<T>(this System.Span<T> span, System.ReadOnlySpan<T> other) { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static int CommonPrefixLength<T>(this System.Span<T> span, System.ReadOnlySpan<T> other, System.Collections.Generic.IEqualityComparer<T>? comparer) { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static bool Contains<T>(this System.Span<T> span, T value) where T : System.IEquatable<T>? { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static bool ContainsAny(this System.Span<char> span, System.Buffers.SearchValues<string> values) { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static bool ContainsAny<T>(this System.Span<T> span, System.Buffers.SearchValues<T> values) where T : System.IEquatable<T>? { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static bool ContainsAny<T>(this System.Span<T> span, System.ReadOnlySpan<T> values) where T : System.IEquatable<T>? { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static bool ContainsAny<T>(this System.Span<T> span, T value0, T value1) where T : System.IEquatable<T>? { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static bool ContainsAny<T>(this System.Span<T> span, T value0, T value1, T value2) where T : System.IEquatable<T>? { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static bool ContainsAnyExcept<T>(this System.Span<T> span, System.Buffers.SearchValues<T> values) where T : System.IEquatable<T>? { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static bool ContainsAnyExcept<T>(this System.Span<T> span, System.ReadOnlySpan<T> values) where T : System.IEquatable<T>? { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static bool ContainsAnyExcept<T>(this System.Span<T> span, T value) where T : System.IEquatable<T>? { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static bool ContainsAnyExcept<T>(this System.Span<T> span, T value0, T value1) where T : System.IEquatable<T>? { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static bool ContainsAnyExcept<T>(this System.Span<T> span, T value0, T value1, T value2) where T : System.IEquatable<T>? { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static bool ContainsAnyExceptInRange<T>(this System.Span<T> span, T lowInclusive, T highInclusive) where T : System.IComparable<T> { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static bool ContainsAnyInRange<T>(this System.Span<T> span, T lowInclusive, T highInclusive) where T : System.IComparable<T> { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static int Count<T>(this System.Span<T> span, T value) where T : System.IEquatable<T>? { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static int Count<T>(this System.Span<T> span, System.ReadOnlySpan<T> value) where T : System.IEquatable<T>? { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static bool EndsWith<T>(this System.Span<T> span, System.ReadOnlySpan<T> value) where T : System.IEquatable<T>? { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static System.Text.SpanLineEnumerator EnumerateLines(this System.Span<char> span) { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static System.Text.SpanRuneEnumerator EnumerateRunes(this System.Span<char> span) { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static int IndexOfAny(this System.Span<char> span, System.Buffers.SearchValues<string> values) { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static int IndexOfAny<T>(this System.Span<T> span, System.Buffers.SearchValues<T> values) where T : System.IEquatable<T>? { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static int IndexOfAny<T>(this System.Span<T> span, System.ReadOnlySpan<T> values) where T : System.IEquatable<T>? { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static int IndexOfAny<T>(this System.Span<T> span, T value0, T value1) where T : System.IEquatable<T>? { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static int IndexOfAny<T>(this System.Span<T> span, T value0, T value1, T value2) where T : System.IEquatable<T>? { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static int IndexOfAnyExcept<T>(this System.Span<T> span, T value) where T : System.IEquatable<T>? { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static int IndexOfAnyExcept<T>(this System.Span<T> span, T value0, T value1) where T : System.IEquatable<T>? { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static int IndexOfAnyExcept<T>(this System.Span<T> span, T value0, T value1, T value2) where T : System.IEquatable<T>? { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static int IndexOfAnyExcept<T>(this System.Span<T> span, System.Buffers.SearchValues<T> values) where T : System.IEquatable<T>? { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static int IndexOfAnyExcept<T>(this System.Span<T> span, System.ReadOnlySpan<T> values) where T : System.IEquatable<T>? { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static int IndexOfAnyExceptInRange<T>(this System.Span<T> span, T lowInclusive, T highInclusive) where T : System.IComparable<T> { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static int IndexOf<T>(this System.Span<T> span, System.ReadOnlySpan<T> value) where T : System.IEquatable<T>? { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static int IndexOf<T>(this System.Span<T> span, T value) where T : System.IEquatable<T>? { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static int IndexOfAnyInRange<T>(this System.Span<T> span, T lowInclusive, T highInclusive) where T : System.IComparable<T> { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static int LastIndexOfAny<T>(this System.Span<T> span, System.Buffers.SearchValues<T> values) where T : System.IEquatable<T>? { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static int LastIndexOfAny<T>(this System.Span<T> span, System.ReadOnlySpan<T> values) where T : System.IEquatable<T>? { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static int LastIndexOfAny<T>(this System.Span<T> span, T value0, T value1) where T : System.IEquatable<T>? { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static int LastIndexOfAny<T>(this System.Span<T> span, T value0, T value1, T value2) where T : System.IEquatable<T>? { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static int LastIndexOfAnyExcept<T>(this System.Span<T> span, T value) where T : System.IEquatable<T>? { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static int LastIndexOfAnyExcept<T>(this System.Span<T> span, T value0, T value1) where T : System.IEquatable<T>? { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static int LastIndexOfAnyExcept<T>(this System.Span<T> span, T value0, T value1, T value2) where T : System.IEquatable<T>? { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static int LastIndexOfAnyExcept<T>(this System.Span<T> span, System.Buffers.SearchValues<T> values) where T : System.IEquatable<T>? { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static int LastIndexOfAnyExcept<T>(this System.Span<T> span, System.ReadOnlySpan<T> values) where T : System.IEquatable<T>? { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static int LastIndexOfAnyExceptInRange<T>(this System.Span<T> span, T lowInclusive, T highInclusive) where T : System.IComparable<T> { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static int LastIndexOf<T>(this System.Span<T> span, System.ReadOnlySpan<T> value) where T : System.IEquatable<T>? { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static int LastIndexOf<T>(this System.Span<T> span, T value) where T : System.IEquatable<T>? { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static int LastIndexOfAnyInRange<T>(this System.Span<T> span, T lowInclusive, T highInclusive) where T : System.IComparable<T> { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static bool Overlaps<T>(this System.Span<T> span, System.ReadOnlySpan<T> other) { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static bool Overlaps<T>(this System.Span<T> span, System.ReadOnlySpan<T> other, out int elementOffset) { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static int SequenceCompareTo<T>(this System.Span<T> span, System.ReadOnlySpan<T> other) where T : System.IComparable<T>? { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static bool SequenceEqual<T>(this System.Span<T> span, System.ReadOnlySpan<T> other) where T : System.IEquatable<T>? { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static bool SequenceEqual<T>(this System.Span<T> span, System.ReadOnlySpan<T> other, System.Collections.Generic.IEqualityComparer<T>? comparer = null) { throw null; }
+       [OverloadResolutionPriority(-1)]
        public static bool StartsWith<T>(this System.Span<T> span, System.ReadOnlySpan<T> value) where T : System.IEquatable<T>? { throw null; }
    }
}

namespace System.Collections.Immutable
{
    public static partial class ImmutableArray
    {
+       [OverloadResolutionPriority(-1)]
        public static ImmutableArray<T> Create<T>(Span<T> items);
+       [OverloadResolutionPriority(-1)]
        public static ImmutableArray<T> ToImmutableArray<T>(this Span<T> items)
    }
}

namespace System.Numerics.Vectors
{
    public readonly struct Vector<T>
    {
+       [OverloadResolutionPriority(-1)]
        public Vector(Span<T> values);
    }
}

API Usage

n/a

Alternative Designs

n/a

Risks

n/a

Metadata

Metadata

Assignees

No one assigned

    Labels

    api-approvedAPI was approved in API review, it can be implementedarea-System.Memoryin-prThere is an active PR which will close this issue when it is merged

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions