Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

can stackalloc be allowed in ref struct constructors? #35658

Closed
xtofs opened this issue May 11, 2019 · 5 comments
Closed

can stackalloc be allowed in ref struct constructors? #35658

xtofs opened this issue May 11, 2019 · 5 comments
Labels

Comments

@xtofs
Copy link

xtofs commented May 11, 2019

Version Used:

c# 7.3

Steps to Reproduce:

  public ref struct Matrix<T> where T: unmanaged
   {
       private readonly int w;
       private readonly int h;
       private readonly Span<T> span;

       public Matrix(int w, int h)
       {
           this.w = w;
           this.h = h;
           this.span = stackalloc T[w*h];
       }

results in error
CS8353: A result of a stackalloc expression of type 'Span<T>' cannot be used in this context because it may be exposed outside of the containing method

I think I understand the error message and why it is necessary to prevent it.
But in this specific case the this.span is a field on a ref struct itself, so the stack will be maintained and the matrix itself cannot "be exposed outside the context".

@mikedn
Copy link

mikedn commented May 11, 2019

But in this specific case the this.span is a field on a ref struct itself, so the stack will be maintained and the matrix itself cannot "be exposed outside the context".

Presumably you're trying to say that since a ref struct lives on the stack that means that it can contain a pointer to a stack allocated buffer. It can but there's no way for that pointer to be valid once the Matrix constructor returns. That's why it is a stack after all, you cannot add or remove things at random.

@jcouv
Copy link
Member

jcouv commented May 12, 2019

A constructor is just a regular method. If you invoke it and it returns, the stack frame of that method goes away (along with any stackalloc storage it may hold.
This diagnostic prevents leaking a reference to such storage outside the method body, and in this case the method is a constructor body.

@jcouv jcouv added Area-Compilers Question Resolution-Answered The question has been answered labels May 12, 2019
@xtofs
Copy link
Author

xtofs commented May 12, 2019

Thanks for clarifying. I think I understand. The constructor is a regular method and the parameters need to be cleaned from the stack, and with that the stackalloc'ed memory is gone.

Don't want to be difficult but we could ask ourselves if (in the case of a ref struct constructor) it has to be a regular method or if there is another way.
This would essentially allow variable size stack allocated structures and that sounds powerful.

@jcouv
Copy link
Member

jcouv commented May 12, 2019

"variable size structures" sound like trouble for the runtime, whether they are stack-allocated or not. I don't know if that's supported.

that sounds powerful

It would be good to identify some motivating examples and start a discussion on csharplang repo for a language design idea.
In the meantime, I'll close this roslyn issue since the compiler behaves correctly per current language design. Thanks

@jcouv jcouv closed this as completed May 12, 2019
@mikedn
Copy link

mikedn commented May 12, 2019

This would essentially allow variable size stack allocated structures and that sounds powerful.

It's powerful until someone passes large w and h and you get a stack overflow :)

The only way to do this today is to allocate stack memory in the caller but that's going to be cumbersome, at least because you can no longer rely on the Matrix constructor to initialize the struct.

C++, which usually can do a lot of crazy things, is too rather limited in this regard - you can have a variable sized struct but the language doesn't offer much support for that, practically none. Its only advantage would be "placement new" that allows you to construct an object in previously allocated memory. Not sure if that's something that would make sense in C#, most likely not.

Ultimately you might be better served by something like:

public ref struct Matrix<T> where T: unmanaged
{
       private readonly int w;
       private readonly int h;
       private readonly Span<T> span;

       public Matrix(int w, int h, Span<T> span)
       {
           this.w = w;
           this.h = h;
           if (span.Length != w * h) throw ArgumentException();

which allows the caller to decide how the memory is allocated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants