Version Used:
Visual Studio Community 2017 Preview
15.6.0 Preview 6.0
Steps to Reproduce:
class Program
{
static void Main(string[] args)
{
System.Span<byte> bytes;
bytes = stackalloc byte[1024];
}
}
<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.
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.
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.