Skip to content

Ref safety spec gap for collection expression with ref struct element type #80750

@RikkiGibson

Description

@RikkiGibson

The following program reports an error on return spans, but nothing in the spec indicates that should be the case. Strictly, the code seems valid to me. I think there is a spec gap due to the fact that ref structs were not anticipated to be valid element types under any conditions.

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/77827")]
public void RefStructElementType_SafeContext_06()
{
    var source = """
        #nullable enable
        using System;

        partial class Program
        {
            public static SpanCollection<Span<int>> Test(Span<int> span1, Span<int> span2)
            {
                SpanCollection<Span<int>> spans = [span1, span2];
                return spans;
            }
        }
        """;

    var comp = CreateCompilation([source, s_collectionOfRefStructsSource], targetFramework: TargetFramework.Net90);
    comp.VerifyDiagnostics(
        // (9,16): error CS8352: Cannot use variable 'spans' in this context because it may expose referenced variables outside of their declaration scope
        //         return spans;
        Diagnostic(ErrorCode.ERR_EscapeVariable, "spans").WithArguments("spans").WithLocation(9, 16));
}

private static readonly string s_collectionOfRefStructsSource = """
        #nullable enable
        using System;
        using System.Collections;
        using System.Collections.Generic;

        ref struct SpanCollection<T>() : IEnumerable<T> where T : allows ref struct
        {
            int _count = 0;
            T? _item0;
            T? _item1;

            public SpanEnumerator GetEnumerator()
                => new SpanEnumerator(this);

            public void Add(T element)
            {
                switch (_count)
                {
                    case 0: _item0 = element; break;
                    case 1: _item1 = element; break;
                    default: throw new InvalidOperationException();
                }
                _count++;
            }

            IEnumerator<T> IEnumerable<T>.GetEnumerator()
                => throw new NotImplementedException();

            IEnumerator IEnumerable.GetEnumerator()
                => throw new NotImplementedException();

            public ref struct SpanEnumerator(SpanCollection<T> spans)
            {
                private SpanCollection<T> spans = spans;
                private int _index = -1;
                public T Current
                {
                    get
                    {
                        switch (_index)
                        {
                            case 0: return spans._item0!;
                            case 1: return spans._item1!;
                            default: throw new InvalidOperationException();
                        }
                    }
                }
                public bool MoveNext() => ++_index < spans._count;
            }
        }
        """;

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions