Skip to content

Spread element on initialized inline array throws AccessViolationException #107018

@monkey0506

Description

@monkey0506

Description

When creating an inline array via a constructor that assigns the elements of the array, accessing said inline array via a spread element throws a System.AccessViolationException.

Reproduction Steps

For an inline array defined as:

[InlineArray(8)]
public struct InlineIntArray8
{
    private int element0;

    public InlineIntArray8()
    {
        for (int i = 0; i < 8; ++i)
        {
            this[i] = i;
        }
    }
}

Then accessing the inline array such as:

InlineIntArray8 inlineArray = new();
ReadOnlySpan<int> readOnlySpan = [ 5, ..inlineArray ]; // AccessViolationException

Note that the AccessViolationException occurs only when using the spread element on the inline array which has at least one element assigned (this[i] = ...) in the constructor. This does not occur if the array elements are unassigned, nor when performing an implicit/explicit cast from the inline array to ReadOnlySpan<T>/Span<T>.

Expected behavior

The spread element should not throw an exception in this use-case.

Actual behavior

An exception of System.AccessViolationException is thrown when the inline array is accessed via the spread element (in a collection expression). Other uses of the inline array do not throw this exception.

Regression?

Unknown.

Known Workarounds

The inline array can be implicitly/explicitly cast to a span first, and then used by the spread element normally.

ReadOnlySpan<int> intermediateSpan = inlineArray;
ReadOnlySpan<int> targetSpan = [ 5, ..intermediateSpan ];
ReadOnlySpan<int> targetSpan2 = [ 5, ..((ReadOnlySpan<int>)inlineArray) ];

targetSpan and targetSpan2 do not throw an exception.

Configuration

.NET 8
Visual Studio Community 2022 (64-bit) Version 17.8.1
Windows 11

VS version and OS do not seem to affect this issue.

Other information

Via StackOverflow, user Ivan Petrov reports that the spread element is compiled into the following:

Span<int>.Enumerator enumerator = ((Span<int>*)(&inlineArray))->GetEnumerator();

That is, rather than constructing a new span from the inline array, an unsafe cast is performed, incorrectly mapping the inline array onto the memory layout of a Span<T>:

internal readonly ref T _reference;
private readonly int _length;

Because the inline array does not match this layout in memory, the AccessViolationException is thrown.

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