Why does the following code not compile in C# 11?
// Example 1 - fails
class C {
public Span<int> M(ref int arg) {
Span<int> span;
span = new Span<int>(ref arg);
return span;
}
}
It produces two compile errors:
error CS9077: Cannot return a parameter by reference 'arg' through a ref parameter; it can only be returned in a return statement.
error CS8347: Cannot use a result of 'Span.Span(ref int)' in this context because it may expose variables referenced by parameter 'reference' outside of their declaration scope.
Neither of them makes sense to me: my code doesn't try to return arg by a ref parameter, and it can't expose variables referenced by arg outside of their declaration scope.
By comparison, the following two pieces of code compile successfully:
// Example 2 - succeeds
class C {
public Span<int> M(ref int arg) {
Span<int> span = new Span<int>(ref arg);
return span;
}
}
// Example 3 - succeeds
class C {
public Span<int> M(Span<int> arg) {
Span<int> span;
span = new Span<int>(ref arg[0]);
return span;
}
}
My intuition is that Span<int> internally holds a ref field of type int, so the escape rules should work the same for Examples 1 and 3 above (which, apparently, they do not).
I made an analogous experiment with a ref struct explicitly holding a ref field:
ref struct S {
public ref int X;
}
Now, the following code fails to compile:
// Example 4 - fails
class C {
public S M(ref int arg) {
S instance;
instance.X = ref arg;
return instance;
}
}
It produces the following error, which at least makes slightly more sense to me:
error CS9079: Cannot ref-assign 'arg' to 'X' because 'arg' can only escape the current method through a return statement.
By comparison, the following two pieces of code compile successfully (with the definition of S above):
// Example 5 - succeeds
class C {
public S M(ref int arg) {
S instance = new S() { X = ref arg };
return instance;
}
}
// Example 6 - succeeds
class C {
public S M(S arg) {
S instance;
instance.X = ref arg.X;
return instance;
}
}
In particular, if arg can only escape the current method through a return statement, as in the error message for Example 4 above, while doesn't the same hold for arg.X in Example 6?
I tried to find the answer in the documentation for low level struct improvements, but I failed. Moreover, that documentation page seems to contradict itself in several places.
are you sure you are using C# 11? Using linqpad with .Net 7 your "fails to compile" example worked fine for me:
Compiles Fine
Update: doesn't compile if using the daily build of the Rosyln compiler
My new hypothesis is that the spec actually got tighter... and ex1 and ex2 should both fail, but they have not accounted for the ex2 syntax where its not triggering when it should (for the reason Marc G pointed out) so might be worth filing a bug report on this :-)