-
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
Clarify Span<T> rules #1593
Clarify Span<T> rules #1593
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -236,6 +236,20 @@ A `new` expression that invokes a constructor obeys the same rules as a method i | |
|
||
In addition *safe-to-escape* is no wider than the smallest of the *safe-to-escape* of all arguments/operands of the object initializer expressions, recursively, if initializer is present. | ||
|
||
## Span constructor | ||
The language relies on `Span<T>` not having a constructor of the following form: | ||
|
||
``` csharp | ||
void Example(ref int x) | ||
{ | ||
// Create a span of length one | ||
var span = new Span<int>(ref x); | ||
} | ||
``` | ||
|
||
Such a constructor makes `Span<T>` which are used as fields indistinguishable from a `ref` field. The safety rules described in this document | ||
depend on `ref` fields not being a valid construct in C#, or .NET. | ||
|
||
## `default` expressions | ||
|
||
A `default` expression is *safe-to-escape* from the entire enclosing method. | ||
|
@@ -280,3 +294,37 @@ We wish to ensure that no `ref` local variable, and no variable of `ref struct` | |
> ``` c# | ||
> Foo(new Span<int>(...), await e2); | ||
> ``` | ||
|
||
# Future Considerations | ||
|
||
## Length one Span<T> over ref values | ||
Though legal today there are cases where creating a length one `Span<T>` instance over a value would be beneficial: | ||
|
||
``` csharp | ||
void RefExample() | ||
{ | ||
int x = ...; | ||
|
||
// Today creating a length one Span<int> requires a stackalloc and a new | ||
// local | ||
Span<int> span1 = stackalloc [] { x }; | ||
Use(span1); | ||
x = span1[0]; | ||
|
||
// Simpler to just allow length one span | ||
var span2 = new Span<int>(ref x); | ||
Use(span2); | ||
} | ||
``` | ||
|
||
This feature gets more compelling if we lift the restrictions on (fixed sized buffers)[https://github.com/dotnet/csharplang/blob/master/proposals/fixed-sized-buffers.md] as it would | ||
allow for `Span<T>` instances of even greater length. | ||
|
||
If there is ever a need to go down this path then the language could accommodate this by ensuring such `Span<T>` instances | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure this would work. By the time we would want to make that change, there might already be a user-declared type internal ref struct MySpan<T>
{
public MySpan(ref T data) ...;
} This API is perfectly safe today because it doesn't (has no safe way to) capture the data. MySpan<int> M(int x)
{
return new MySpan<int>(ref x);
} Although this is safe today, but would become illegal under the rule changes we would need to permit a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That constructor though couldn't be implemented in a way that captured There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, that would work. Because:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would also add that in a case when the ref argument is "definitely referring to a heap object" - like a ref to an array element, for example, the resulting instance could be allowed to escape. It feels like it might be another common case. |
||
were downward facing only. That is they were only ever *safe-to-escape* to the scope in which they were created. This ensure | ||
the language never had to consider a `ref` value escaping a method via a `ref struct` return or field of `ref struct`. This | ||
would likely also require further changes to recognize such constructors as capturing a `ref` parameter in this way though. | ||
|
||
|
||
|
||
|
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.
typo? not legal.
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.
Oops :(