-
Notifications
You must be signed in to change notification settings - Fork 1k
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
Updated draft for the safety of ref-like types such as Span<T>
#472
Conversation
- it is ok to return ref-like values from methods/lambdas, including by reference. | ||
- it is ok to pass ref-like values to methods/lambdas, including by reference. | ||
- it is ok for a ref-like type be a field of another struct as long as the struct itself is ref-like. | ||
- it is ok for an intermediate span values to be used in lambdas/async methods. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs explaining - you've not explained "intermediate span values" previously.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Basicaly M1(M2())
is ok in an async method, even if M2()
returns a span. Will add an example.
In particular: _Slice of a stack-referring value is a stack-referring value._ | ||
|
||
- "No Mixing with refs to ordinary" rule: | ||
Stack-referring spans can be passed as an argument as long as no other argument is an ordinary span variable (not a stack-referring local), and: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the rationale for this? It's not clear to me (not being involved in this, why this rule exists)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we classified one variable as safe to return and another variable as stack-referring, we will need to prevent assignments from second to the first, since that would invalidate the classification (and we do not want to do global data flow analysis - it is hardly tractable).
The goal here is to prevent such assignments while not causing too much inconvenience. The call boundary is an issue though.
If I make a call like M1(p1: ref safeToReturn)
, I am risking that M1
will do p1 = stackalloc int[10];
In a more complex case I am making a call like M2(p1: ref safeToReturn, p2: unsafeToReturn)
and M2
does p1 = p2;
.
We need to make both scenarios illegal.
The original proposal simply disallowed passing spans by reference. It was found too restricting so we need something else.
In this proposal assigning stack-referring values to parameters (or to anything other than stack-referring locals) is disallowed. That handles the p1 = stackalloc int[10];
case.
The other case is harder. Inside M2
we cannot tell whether assigning the second parameter to the first is unsafe, so we disallow at the call side passing an ordinary span variable by reference and any kind of stack-referring spans in the same call, so that M2
could assign one parameter to another without worrying.
Thanks for commenting. I will add these details to the document.
We are going to need attribute to mark ref-like types - to avoid spooky action at a distance when Span is embedded in other structs. |
@jkotas - yes, we will use [Obsolete] with a well-known message in metadata. That will also allow us to error on compilers that are not aware of span requirements. And in the source we will use We had some follow up discussions on that. I will add that to the document. |
|
It is possible to add another attribute, but it would always have to be combined with the Obsolete. |
Note that the Obsolete message must be precise or we cannot distinguish it from an ordinary obsolete attribute. It does feel that having two attributes having distinct meanings is more direct way of doing this, but there is also the part that they will have to be always emitted in pairs makes it seem a bit redundant. We should think about this. |
|
||
``` | ||
|
||
Designating a struct as ref-like will allow the struct to have ref-like instance fields and will also make all the requirements of ref-like types applicable to the struct. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will it somehow be possible to create a ref-like struct that contains ref
fields?
|
||
```cs | ||
//This usage of stackalloc does not require unsafe | ||
Span<int> sp = stackalloc int[100]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Firstly: I love this very much; this would remove the unsafe
from much of my stack span code.
Probably simply "no" (and that's fine), but: is there any utility to exposing something akin to array initializer syntax here?
Span<int> sp = stackalloc { 1, 42, 13 };
(Note preserving the stackalloc
keyword to retain the intent in the code)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is interesting. It doesn't require unsafe but the resulting code is unverifiable. It would seem that we're moving towards a system where the managed compiler(s) are responsible for ensuring type safety and not the runtime.
Can perhaps Other improper uses of ref-like types will be blocked by the runtime. Sure, it would be it would be nice to be blocked at compile time but using |
@mikedn It's an interesting idea, and definitely falls in the spirit of what they were designed for (think of some the C++ modifiers; IsUdtReturn, IsByValue, etc). |
Will such scenario be allowed? private void M()
{
var myStruct = new MyStruct(stackalloc int[10]);
Foo(myStruct);
Bar(myStruct.ReadOnlyView);
}
private void Foo(MyStruct myStruct)
{
}
private void Bar(ReadOnlySpan<int> roSpan)
{
}
reflike struct MyStruct
{
private Span<int> span;
MyStruct(Span<int> span) => this.span = span;
public ReadOnlySpan<int> ReadOnlyView => span.AsReadOnly();
}
|
@omariom - not exactly like that, stackalloc can be used only when initializing a local, so you would need one more span-typed variable, but yes, conceptually the above example will work. At least according to the rules in the proposal. |
to me, though, it does also prompt the question of whether there should
also be a syntax for initializing a stack-allocated span into a local
*without* requiring "unsafe" (for "stackalloc"). Probably ties more into
#535 than this though - is specific to span.
On 4 Jun 2017 3:40 a.m., "Vladimir Sadov" <notifications@github.com> wrote:
@omariom <https://github.com/omariom> - not exactly like that, stackalloc
can be used only when initializing a local, so you would need one more
span-typed variable, but yes, conceptually the above example will work. At
least according to the rules in the proposal.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#472 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AABDsPgvdW74zF9ET-qTs9zwdWO4VIZnks5sAhkmgaJpZM4NDv60>
.
|
It should be relaxed for safe (Spans) case. |
No description provided.