Roslyn: CS8353 when trying to initialize Span<T> variable using stackalloc outside of its declaration

Created on 28 Feb 2018  路  4Comments  路  Source: dotnet/roslyn

Version Used:
Visual Studio Community 2017 Preview
15.6.0 Preview 6.0

Steps to Reproduce:

Program.cs

class Program
{
    static void Main(string[] args)
    {
        System.Span<byte> bytes;
        bytes = stackalloc byte[1024];
    }
}

App.csproj

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.1</TargetFramework>
    <LangVersion>latest</LangVersion>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="System.Memory" Version="4.5.0-preview1-26216-02" />
  </ItemGroup>

</Project>

Expected Behavior:
This feels like it should be considered safe. The Span<T> never escapes this method.

Actual Behavior:
CS8353: A result of a stackalloc expression of type 'Span<byte>' cannot be used in this context because it may be exposed outside of the containing method.

Workaround:
Joining the declaration and assignment will make the error go away, but that doesn't look amazing in this situation (and this situation doesn't really seem all that esoteric)

Edit: a better workaround for a more "full" version of this repro, from the comments below:

using System;

class Program
{
    static void Main(string[] args)
    {
        int len = ComputeLength();

        Span<byte> bytes = stackalloc byte[0];
        if (len <= 1024)
        {
            bytes = stackalloc byte[len];
        }
        else
        {
            bytes = new byte[len];
        }

        // do stuff with bytes
    }

    static int ComputeLength() => 2048;
}

My Own Guesses:
I'm guessing the analyzer is a bit paranoid here, because it's a lot easier to reject cases like this than to prove that there are no possible ways for this Span<T> to escape whenever it's stack-allocated.

Area-Compilers Resolution-By Design

Most helpful comment

Uninitialized span local is classified as ordinary "returnable" span. Then you cannot assign stackallocated spans to it.

If you initialize with stackalloc SomeStruct[0] , your code will work. As long as you do not try returning the span.

All 4 comments

This would also be useful if TryForSufficientStack is implemented (https://github.com/dotnet/corefx/issues/26954), so that you could write code like the following (from https://github.com/dotnet/corefx/issues/26954#issuecomment-364211315):

c# Span<SomeStruct> span; if (RuntimeHelpers.TryEnsureSufficientExecutionStack(list.Count * PUT_HERE_YOUR_STRUCT_SIZE)) { span = stackalloc SomeStruct[size]; } else { span = ArrayPool<SomeStruct>.Shared.Rent(size); }

Tagging @VSadov @gafter for comment/triage.

Uninitialized span local is classified as ordinary "returnable" span. Then you cannot assign stackallocated spans to it.

If you initialize with stackalloc SomeStruct[0] , your code will work. As long as you do not try returning the span.

The usability issues that come from this behavior are discussed in https://github.com/dotnet/csharplang/issues/1130

For the time being this is ByDesign.

We may need to introduce a feature to make such declarations easier to do.
It looks like stackalloc[0] worked for this case though.

Was this page helpful?
0 / 5 - 0 ratings