Why a `stackalloc` expression cannot be assigned to a `Span<T>` parameter?
Asked Answered
F

1

5

Consider the following methods (fiddle):

void test(Span<int> param)
{
    //Fail, the stackalloc'ed buffer could be exposed.
    param = stackalloc int[10];
}

void test2(Span<int> param)
{
    //OK
    Span<int> local = stackalloc int[10];
}

I don't understand why param = stackalloc int[10]; produces the error:

A result of a stackalloc expression of type 'Span' cannot be used in this context because it may be exposed outside of the containing method

Span is a ref struct but (despite its name) it is still a value-type so any modification to param won't be reflected on the caller object.

I think of param as a local variable with an initial value and I don't see why test2 compiles while test doesn't.

How can the returned value of stackalloc int[10] in test escape the scope of the method?

Friarbird answered 28/4, 2021 at 11:59 Comment(4)
I don't pretend to understand the intricacies of this but this article appears to cover the background of why this is prohibited, starting from As a result, Span<T> instances can only live on the stack, not on the heap. - I lack the knowledge to make it coherent as an answer.Brookbrooke
Thinking of parameters as "local variables with an initial value" that you are free to modify is not exactly good practice to begin with, even if it's sort-of correct. If this is a shortcoming in the compiler's analysis due to overly conservative rules (and it might well be, I don't know if it is), it's not exactly an important one. If anything if they did remove it as an error I'd want to get a separate warning especially for when this is done with Span parameters, where it more than likely is an actual mistake.Munday
Asked here, it's by design, try stackalloc[0] (whatever that would means)?Bailable
@Bailable Though that link is not mainly about parameters, I think it got me in the right direction: param is something that could potentially be returned by the method and thus we cannot assign stackalloc'ed expression to it. Just link a local initialized to any safe-to-escape value. Thank you!Friarbird
F
7

Sinatr posted a link (here the relevant part) in the comments that helped me put the whole code in the context of Span safety.

In the method test the parameter param is safe to return because we can pretend it was like a local variable initialized with a value given by the caller (and thus, being external to the method, safe to return).

When a local is marked safe to return, the compiler will prevent any assignment to it with values that are not safe to return (stackalloc expressions are safe to escape to the top scope of the method but are not safe to return, obviously).

The fact that the method is void doesn't matter (neither to me as an asker, neither to the compiler) as this rule is general (I don't see much benefit from handling these corner cases).

For completeness, this code doesn't compile:

void test2(Span<int> param)
{
    //Fail, because local is marked safe to return
    Span<int> local = param;
    local = stackalloc int[10];
}

This was the missing piece I was looking for.

Friarbird answered 28/4, 2021 at 13:43 Comment(1)
Correct analysis. This seems like an unfortunate design with the tail wagging the dog. The intuitive design would have been for the local (or parameter) to simply lose its "safe to return" mark through the rest of the function.Boatbill

© 2022 - 2024 — McMap. All rights reserved.