Skip to content

Commit

Permalink
Ref-like type parameters can return safely to external scope by ref (#…
Browse files Browse the repository at this point in the history
…27882)

* Ref-like type parameters can return safely to external scope by ref

* Update failing test

* Document breaking change

* PR Comments
  • Loading branch information
OmarTawfik authored Jun 28, 2018
1 parent 11e5b43 commit 70c4eaf
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ Example:
- Visutal Studio 2017 version 15.7: https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/tuple-equality.md#compatibility The "tuple equality" features (in C# 7.3) introduces built-in operators `==` and `!=` on tuple types. Those built-in operators take precedence over user-defined comparison operators on a custom `ValueTuple` type.
- Visual Studio 2017 version 15.8: https://github.com/dotnet/roslyn/issues/22455 C# compiler will now produce errors if there was an "in" or an "out" argument to an "__arglist" call. "out" was always allowed, and "in" was introduced in 15.5.
- Visual Studio 2017 version 15.8: https://github.com/dotnet/roslyn/pull/27882 C# compiler will now produce errors on ref assigning a local to a parameter with a wider escape scope if it was a ref-like type.
- Visual Studio 2017 version 15.8: https://github.com/dotnet/roslyn/issues/26418 C# compiler will now produce errors on out variable declarations that have "ref" or "ref readonly" ref kinds. Example: M(out ref int x);
- Visual Studio 2017 version 15.8: https://github.com/dotnet/roslyn/issues/27047 The C# compiler will now produce diagnostics for operators marked as obsolete when they are used as part of a tuple comparison.
- Visual Studio 2017 version 15.8: https://github.com/dotnet/roslyn/pull/27461 The method `LanguaguageVersionFacts.TryParse` is no longer an extension method.
Expand Down
13 changes: 5 additions & 8 deletions src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -596,8 +596,8 @@ private static bool CheckParameterRefEscape(SyntaxNode node, BoundParameter para
{
ParameterSymbol parameterSymbol = parameter.ParameterSymbol;

// byval parameters can escape to method's top level.
// others can be escape further, unless they are ref-like.
// byval parameters can escape to method's top level. Others can escape further.
// NOTE: "method" here means nearest containing method, lambda or local function.
if (escapeTo == Binder.ExternalScope && parameterSymbol.RefKind == RefKind.None)
{
if (checkingReceiver)
Expand Down Expand Up @@ -1743,12 +1743,9 @@ internal static uint GetRefEscape(BoundExpression expr, uint scopeOfTheContainin
case BoundKind.Parameter:
var parameter = ((BoundParameter)expr).ParameterSymbol;

// byval parameters can escape to method's top level.
// others can be escape further, unless they are ref-like.
// NOTE: "method" here means nearest containing method, lambda or nested method
return parameter.RefKind == RefKind.None || parameter.Type?.IsByRefLikeType == true ?
Binder.TopLevelScope :
Binder.ExternalScope;
// byval parameters can escape to method's top level. Others can escape further.
// NOTE: "method" here means nearest containing method, lambda or local function.
return parameter.RefKind == RefKind.None ? Binder.TopLevelScope : Binder.ExternalScope;

case BoundKind.Local:
return ((BoundLocal)expr).LocalSymbol.RefEscapeScope;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -527,21 +527,24 @@ class C
void M(ref Span<int> s)
{
Span<int> s2 = new Span<int>(new int[10]);
s = ref s2; // OK
s = ref s2; // Illegal, narrower escape scope
s2 = stackalloc int[10]; // Illegal, narrower lifetime
Span<int> s3 = stackalloc int[10];
s = ref s3; // Illegal, narrower lifetime
s = ref s3; // Illegal, narrower escape scope
}
}");
comp.VerifyDiagnostics(
// (8,9): error CS8374: Cannot ref-assign 's2' to 's' because 's2' has a narrower escape scope than 's'.
// s = ref s2; // Illegal, narrower escape scope
Diagnostic(ErrorCode.ERR_RefAssignNarrower, "s = ref s2").WithArguments("s", "s2").WithLocation(8, 9),
// (10,14): error CS8353: A result of a stackalloc expression of type 'Span<int>' cannot be used in this context because it may be exposed outside of the containing method
// s2 = stackalloc int[10]; // Illegal, narrower lifetime
Diagnostic(ErrorCode.ERR_EscapeStackAlloc, "stackalloc int[10]").WithArguments("System.Span<int>").WithLocation(10, 14),
// (13,17): error CS8352: Cannot use local 's3' in this context because it may expose referenced variables outside of their declaration scope
// s = ref s3; // Illegal, narrower lifetime
Diagnostic(ErrorCode.ERR_EscapeLocal, "s3").WithArguments("s3").WithLocation(13, 17));
// (13,9): error CS8374: Cannot ref-assign 's3' to 's' because 's3' has a narrower escape scope than 's'.
// s = ref s3; // Illegal, narrower escape scope
Diagnostic(ErrorCode.ERR_RefAssignNarrower, "s = ref s3").WithArguments("s", "s3").WithLocation(13, 9));
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1440,5 +1440,38 @@ static void Main()
Diagnostic(ErrorCode.ERR_BadSKunknown, "S1").WithArguments("Program.S1", "type").WithLocation(16, 24)
);
}

[Fact]
[WorkItem(27874, "https://github.com/dotnet/roslyn/issues/27874")]
public void PassingSpansToLocals_EscapeScope()
{
CompileAndVerify(
CreateCompilationWithMscorlibAndSpan(@"
using System;
class C
{
static void Main()
{
Span<int> x = stackalloc int [10];
Console.WriteLine(M1(ref x).Length);
Console.WriteLine(M2(ref x).Length);
}
static ref Span<int> M1(ref Span<int> x)
{
ref Span<int> q = ref x;
return ref q;
}
static ref Span<int> M2(ref Span<int> x)
{
return ref x;
}
}",
options: TestOptions.ReleaseExe), verify: Verification.Fails, expectedOutput: @"
10
10");
}
}
}

0 comments on commit 70c4eaf

Please sign in to comment.