提交 698b9e0e 编写于 作者: V Vladimir Sadov 提交者: GitHub

Merge pull request #22203 from VSadov/relaxedEscape

Relaxed escape rules for spans
......@@ -949,21 +949,21 @@ private bool CheckPropertyValueKind(SyntaxNode node, BoundExpression expr, BindV
ImmutableArray<BoundExpression> args,
ImmutableArray<RefKind> argRefKinds,
ImmutableArray<int> argsToParamsOpt,
uint scopeOfTheContainingExpression)
uint scopeOfTheContainingExpression,
bool isRefEscape
)
{
// SPEC: (also applies to the CheckInvocationEscape counterpart)
//
// An lvalue resulting from a ref-returning method invocation e1.M(e2, ...) is ref-safe - to - escape the smallest of the following scopes:
//• The entire enclosing method
//• the ref-safe-to-escape of all ref/out/in argument expressions(excluding the receiver and arguments that have ref-like type)
//• the ref-safe-to-escape of all ref/out/in argument expressions(excluding the receiver)
//• the safe-to - escape of all argument expressions(including the receiver)
//
// An rvalue resulting from a method invocation e1.M(e2, ...) is safe - to - escape from the smallest of the following scopes:
//• The entire enclosing method
//• the ref-safe-to-escape of all ref/out/in argument expressions(excluding the receiver and arguments that have ref-like type)
//• the safe-to-escape of all argument expressions(including the receiver)
//
// Note that these rules are identical to the above rules for ref-safe - to - escape, but apply only when the return type is a ref struct type.
//by default it is safe to escape
uint escapeScope = Binder.ExternalScope;
......@@ -976,10 +976,15 @@ private bool CheckPropertyValueKind(SyntaxNode node, BoundExpression expr, BindV
{
RefKind effectiveRefKind = GetEffectiveRefKind(argIndex, argRefKinds, parameters, argsToParamsOpt, ref inParametersMatchedWithArgs);
// if ref is passed and it is not a ref-like, check ref escape,
// since it is the same or narrower than val escape and callee may wrap the ref into a val
// ref escape scope is the narrowest of
// - ref escape of all byref arguments
// - val escape of all byval arguments (ref-like values can be unwrapped into refs, so treat val escape of values as possible ref escape of the result)
//
// val escape scope is the narrowest of
// - val escape of all byval arguments (refs cannot be wrapped into values, so their ref escape is irrelevant, only use val escapes)
var argument = args[argIndex];
var argEscape = effectiveRefKind != RefKind.None && argument.Type?.IsByRefLikeType == false ?
var argEscape = effectiveRefKind != RefKind.None && isRefEscape ?
GetRefEscape(argument, scopeOfTheContainingExpression) :
GetValEscape(argument, scopeOfTheContainingExpression);
......@@ -999,9 +1004,9 @@ private bool CheckPropertyValueKind(SyntaxNode node, BoundExpression expr, BindV
// handle omitted optional "in" parameters if there are any
ParameterSymbol unmatchedInParameter = TryGetUnmatchedInParameterAndFreeMatchedArgs(parameters, ref inParametersMatchedWithArgs);
// unmatched "in" parameter is same as a literal, its ref escape is scopeOfTheContainingExpression (can't get any worse)
// unmatched "in" parameter is the same as a literal, its ref escape is scopeOfTheContainingExpression (can't get any worse)
// its val escape is ExternalScope (does not affect overal result)
if (unmatchedInParameter != null)
if (unmatchedInParameter != null && isRefEscape)
{
return scopeOfTheContainingExpression;
}
......@@ -1020,9 +1025,6 @@ private bool CheckPropertyValueKind(SyntaxNode node, BoundExpression expr, BindV
/// The result indicates whether the escape is possible.
/// Additionally, the method emits diagnostics (possibly more than one, recursively) that would help identify the cause for the failure.
///
/// NOTE: the escape scope for ref and val escapes is the same for invocations except for trivial cases (ordinary type returned by val)
/// where escape is known otherwise. Therefore we do not vave two ref/val variants of this.
///
/// NOTE: we need scopeOfTheContainingExpression as some expressions such as optional `in` parameters or `ref dynamic` behave as
/// local variables declared at the scope of the invocation.
/// </summary>
......@@ -1037,9 +1039,10 @@ private bool CheckPropertyValueKind(SyntaxNode node, BoundExpression expr, BindV
bool checkingReceiver,
uint escapeFrom,
uint escapeTo,
DiagnosticBag diagnostics)
DiagnosticBag diagnostics,
bool isRefEscape
)
{
// SPEC:
// In a method invocation, the following constraints apply:
//• If there is a ref or out argument to a ref struct type (including the receiver), with safe-to-escape E1, then
......@@ -1053,10 +1056,15 @@ private bool CheckPropertyValueKind(SyntaxNode node, BoundExpression expr, BindV
{
RefKind effectiveRefKind = GetEffectiveRefKind(argIndex, argRefKinds, parameters, argsToParamsOpt, ref inParametersMatchedWithArgs);
// if ref is passed and it is not a ref-like, check ref escape,
// since it is the same or narrower than val escape and callee may wrap the ref into a val
// ref escape scope is the narrowest of
// - ref escape of all byref arguments
// - val escape of all byval arguments (ref-like values can be unwrapped into refs, so treat val escape of values as possible ref escape of the result)
//
// val escape scope is the narrowest of
// - val escape of all byval arguments (refs cannot be wrapped into values, so their ref escape is irrelevant, only use val escapes)
var argument = args[argIndex];
var valid = effectiveRefKind != RefKind.None && argument.Type.IsByRefLikeType == false ?
var valid = effectiveRefKind != RefKind.None && isRefEscape ?
CheckRefEscape(argument.Syntax, argument, escapeFrom, escapeTo, false, diagnostics) :
CheckValEscape(argument.Syntax, argument, escapeFrom, escapeTo, false, diagnostics);
......@@ -1077,9 +1085,9 @@ private bool CheckPropertyValueKind(SyntaxNode node, BoundExpression expr, BindV
// handle omitted optional "in" parameters if there are any
ParameterSymbol unmatchedInParameter = TryGetUnmatchedInParameterAndFreeMatchedArgs(parameters, ref inParametersMatchedWithArgs);
// unmatched "in" parameter is same as a literal, its ref escape is scopeOfTheContainingExpression (can't get any worse)
// unmatched "in" parameter is the same as a literal, its ref escape is scopeOfTheContainingExpression (can't get any worse)
// its val escape is ExternalScope (does not affect overal result)
if (unmatchedInParameter != null)
if (unmatchedInParameter != null && isRefEscape)
{
Error(diagnostics, GetStandardCallEscapeError(checkingReceiver), syntax, symbol, unmatchedInParameter.Name);
return false;
......@@ -1139,27 +1147,16 @@ private bool CheckPropertyValueKind(SyntaxNode node, BoundExpression expr, BindV
return true;
}
// check that no ref/out/in or ref-like can be captured from a narrower scope
ArrayBuilder<bool> inParametersMatchedWithArgs = null;
if (!args.IsDefault)
{
for (var argIndex = 0; argIndex < args.Length; argIndex++)
{
RefKind effectiveRefKind = GetEffectiveRefKind(argIndex, argRefKinds, parameters, argsToParamsOpt, ref inParametersMatchedWithArgs);
// if ref is passed and it is not a ref-like, check ref escape,
// since it is the same or narrower than val escape and callee may wrap the ref into a val
// check val escape of all arguments
var argument = args[argIndex];
var valid = effectiveRefKind != RefKind.None && argument.Type?.IsByRefLikeType == false ?
CheckRefEscape(argument.Syntax, argument, scopeOfTheContainingExpression, escapeTo, false, diagnostics) :
CheckValEscape(argument.Syntax, argument, scopeOfTheContainingExpression, escapeTo, false, diagnostics);
var valid = CheckValEscape(argument.Syntax, argument, scopeOfTheContainingExpression, escapeTo, false, diagnostics);
if (!valid)
{
// no longer needed
inParametersMatchedWithArgs?.Free();
var paramIndex = argsToParamsOpt.IsDefault ? argIndex : argsToParamsOpt[argIndex];
var parameterName = parameters[paramIndex].Name;
Error(diagnostics, ErrorCode.ERR_CallArgMixing, syntax, symbol, parameterName);
......@@ -1168,18 +1165,10 @@ private bool CheckPropertyValueKind(SyntaxNode node, BoundExpression expr, BindV
}
}
// handle omitted optional "in" parameters if there are any
ParameterSymbol unmatchedInParameter = TryGetUnmatchedInParameterAndFreeMatchedArgs(parameters, ref inParametersMatchedWithArgs);
//NB: we do not care about unmatched "ref readonly" parameters here.
// They have "outer" val escape, so cannot be worse than escapeTo.
// unmatched "in" parameter is same as a literal, its ref escape is scopeOfTheContainingExpression (can't get any worse)
// its val escape is ExternalScope (does not affect overal result)
if (unmatchedInParameter != null)
{
Error(diagnostics, ErrorCode.ERR_CallArgMixing, syntax, symbol, unmatchedInParameter.Name);
return false;
}
// check receiver if ref-like
// check val escape of receiver if ref-like
if (receiverOpt?.Type?.IsByRefLikeType == true)
{
return CheckValEscape(receiverOpt.Syntax, receiverOpt, scopeOfTheContainingExpression, escapeTo, false, diagnostics);
......@@ -1577,10 +1566,11 @@ internal static uint GetRefEscape(BoundExpression expr, uint scopeOfTheContainin
case BoundKind.ConditionalOperator:
var conditional = (BoundConditionalOperator)expr;
// byref conditional defers to one operand. Since are here without errors, both operands must match.
if (conditional.IsByRef)
{
return GetRefEscape(conditional.Consequence, scopeOfTheContainingExpression);
// ref conditional defers to its operands
return Math.Max(GetRefEscape(conditional.Consequence, scopeOfTheContainingExpression),
GetRefEscape(conditional.Alternative, scopeOfTheContainingExpression));
}
// otherwise it is an RValue
......@@ -1633,7 +1623,8 @@ internal static uint GetRefEscape(BoundExpression expr, uint scopeOfTheContainin
call.Arguments,
call.ArgumentRefKindsOpt,
call.ArgsToParamsOpt,
scopeOfTheContainingExpression);
scopeOfTheContainingExpression,
isRefEscape: true);
case BoundKind.IndexerAccess:
var indexerAccess = (BoundIndexerAccess)expr;
......@@ -1645,7 +1636,8 @@ internal static uint GetRefEscape(BoundExpression expr, uint scopeOfTheContainin
indexerAccess.Arguments,
indexerAccess.ArgumentRefKindsOpt,
indexerAccess.ArgsToParamsOpt,
scopeOfTheContainingExpression);
scopeOfTheContainingExpression,
isRefEscape: true);
case BoundKind.PropertyAccess:
var propertyAccess = (BoundPropertyAccess)expr;
......@@ -1657,7 +1649,8 @@ internal static uint GetRefEscape(BoundExpression expr, uint scopeOfTheContainin
default,
default,
default,
scopeOfTheContainingExpression);
scopeOfTheContainingExpression,
isRefEscape: true);
}
// At this point we should have covered all the possible cases for anything that is not a strict RValue.
......@@ -1748,11 +1741,10 @@ internal static bool CheckRefEscape(SyntaxNode node, BoundExpression expr, uint
case BoundKind.ConditionalOperator:
var conditional = (BoundConditionalOperator)expr;
// byref conditional defers to one operand. Since are here without errors, both operands must match.
if (conditional.IsByRef &&
CheckRefEscape(conditional.Consequence.Syntax, conditional.Consequence, escapeFrom, escapeTo, checkingReceiver: false, diagnostics: diagnostics))
if (conditional.IsByRef)
{
return true;
return CheckRefEscape(conditional.Consequence.Syntax, conditional.Consequence, escapeFrom, escapeTo, checkingReceiver: false, diagnostics: diagnostics) &&
CheckRefEscape(conditional.Alternative.Syntax, conditional.Alternative, escapeFrom, escapeTo, checkingReceiver: false, diagnostics: diagnostics);
}
// report standard lvalue error
......@@ -1792,7 +1784,8 @@ internal static bool CheckRefEscape(SyntaxNode node, BoundExpression expr, uint
checkingReceiver,
escapeFrom,
escapeTo,
diagnostics);
diagnostics,
isRefEscape: true);
case BoundKind.IndexerAccess:
var indexerAccess = (BoundIndexerAccess)expr;
......@@ -1814,7 +1807,8 @@ internal static bool CheckRefEscape(SyntaxNode node, BoundExpression expr, uint
checkingReceiver,
escapeFrom,
escapeTo,
diagnostics);
diagnostics,
isRefEscape: true);
case BoundKind.PropertyAccess:
var propertyAccess = (BoundPropertyAccess)expr;
......@@ -1837,7 +1831,8 @@ internal static bool CheckRefEscape(SyntaxNode node, BoundExpression expr, uint
checkingReceiver,
escapeFrom,
escapeTo,
diagnostics);
diagnostics,
isRefEscape: true);
}
// At this point we should have covered all the possible cases for anything that is not a strict RValue.
......@@ -1888,8 +1883,17 @@ internal static uint GetValEscape(BoundExpression expr, uint scopeOfTheContainin
case BoundKind.ConditionalOperator:
var conditional = (BoundConditionalOperator)expr;
// conditional defers to its operands
return Math.Max(GetValEscape(conditional.Consequence, scopeOfTheContainingExpression),
var consEscape = GetValEscape(conditional.Consequence, scopeOfTheContainingExpression);
if (conditional.IsByRef)
{
// ref conditional defers to one operand.
// the other one is the same or we will be reporting errors anyways.
return consEscape;
}
// val conditional gets narrowest of its operands
return Math.Max(consEscape,
GetValEscape(conditional.Alternative, scopeOfTheContainingExpression));
case BoundKind.FieldAccess:
......@@ -1910,7 +1914,8 @@ internal static uint GetValEscape(BoundExpression expr, uint scopeOfTheContainin
call.Arguments,
call.ArgumentRefKindsOpt,
call.ArgsToParamsOpt,
scopeOfTheContainingExpression);
scopeOfTheContainingExpression,
isRefEscape: false);
case BoundKind.IndexerAccess:
var indexerAccess = (BoundIndexerAccess)expr;
......@@ -1922,7 +1927,8 @@ internal static uint GetValEscape(BoundExpression expr, uint scopeOfTheContainin
indexerAccess.Arguments,
indexerAccess.ArgumentRefKindsOpt,
indexerAccess.ArgsToParamsOpt,
scopeOfTheContainingExpression);
scopeOfTheContainingExpression,
isRefEscape: false);
case BoundKind.PropertyAccess:
var propertyAccess = (BoundPropertyAccess)expr;
......@@ -1934,7 +1940,8 @@ internal static uint GetValEscape(BoundExpression expr, uint scopeOfTheContainin
default,
default,
default,
scopeOfTheContainingExpression);
scopeOfTheContainingExpression,
isRefEscape: false);
case BoundKind.ObjectCreationExpression:
var objectCreation = (BoundObjectCreationExpression)expr;
......@@ -1946,7 +1953,8 @@ internal static uint GetValEscape(BoundExpression expr, uint scopeOfTheContainin
objectCreation.Arguments,
objectCreation.ArgumentRefKindsOpt,
objectCreation.ArgsToParamsOpt,
scopeOfTheContainingExpression);
scopeOfTheContainingExpression,
isRefEscape: false);
return escape;
......@@ -2044,8 +2052,16 @@ internal static bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint
case BoundKind.ConditionalOperator:
var conditional = (BoundConditionalOperator)expr;
return CheckValEscape(conditional.Consequence.Syntax, conditional.Consequence, escapeFrom, escapeTo, checkingReceiver: false, diagnostics: diagnostics) &&
CheckValEscape(conditional.Alternative.Syntax, conditional.Alternative, escapeFrom, escapeTo, checkingReceiver: false, diagnostics: diagnostics);
var consValid = CheckValEscape(conditional.Consequence.Syntax, conditional.Consequence, escapeFrom, escapeTo, checkingReceiver: false, diagnostics: diagnostics);
if (!consValid || conditional.IsByRef)
{
// ref conditional defers to one operand.
// the other one is the same or we will be reporting errors anyways.
return consValid;
}
return CheckValEscape(conditional.Alternative.Syntax, conditional.Alternative, escapeFrom, escapeTo, checkingReceiver: false, diagnostics: diagnostics);
case BoundKind.FieldAccess:
var fieldAccess = (BoundFieldAccess)expr;
......@@ -2071,7 +2087,8 @@ internal static bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint
checkingReceiver,
escapeFrom,
escapeTo,
diagnostics);
diagnostics,
isRefEscape: false);
case BoundKind.IndexerAccess:
var indexerAccess = (BoundIndexerAccess)expr;
......@@ -2088,7 +2105,8 @@ internal static bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint
checkingReceiver,
escapeFrom,
escapeTo,
diagnostics);
diagnostics,
isRefEscape: false);
case BoundKind.PropertyAccess:
var propertyAccess = (BoundPropertyAccess)expr;
......@@ -2105,7 +2123,8 @@ internal static bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint
checkingReceiver,
escapeFrom,
escapeTo,
diagnostics);
diagnostics,
isRefEscape: false);
case BoundKind.ObjectCreationExpression:
var objectCreation = (BoundObjectCreationExpression)expr;
......@@ -2122,7 +2141,8 @@ internal static bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint
checkingReceiver,
escapeFrom,
escapeTo,
diagnostics);
diagnostics,
isRefEscape: false);
return escape;
......
......@@ -3629,20 +3629,20 @@ private BoundExpression BindConditionalOperator(ConditionalExpressionSyntax node
{
var currentScope = this.LocalScopeDepth;
// ref-escape must agree on both branches.
uint whenTrueEscape = GetRefEscape(trueExpr, currentScope);
uint whenFalseEscape = GetRefEscape(falseExpr, currentScope);
// val-escape must agree on both branches.
uint whenTrueEscape = GetValEscape(trueExpr, currentScope);
uint whenFalseEscape = GetValEscape(falseExpr, currentScope);
if (whenTrueEscape != whenFalseEscape)
{
// ask the one with narrower escape, for the wider - hopefully the errors will make the violation easier to fix.
if (whenTrueEscape < whenFalseEscape)
{
CheckRefEscape(falseExpr.Syntax, falseExpr, currentScope, whenTrueEscape, checkingReceiver: false, diagnostics: diagnostics);
CheckValEscape(falseExpr.Syntax, falseExpr, currentScope, whenTrueEscape, checkingReceiver: false, diagnostics: diagnostics);
}
else
{
CheckRefEscape(trueExpr.Syntax, trueExpr, currentScope, whenFalseEscape, checkingReceiver: false, diagnostics: diagnostics);
CheckValEscape(trueExpr.Syntax, trueExpr, currentScope, whenFalseEscape, checkingReceiver: false, diagnostics: diagnostics);
}
diagnostics.Add(ErrorCode.ERR_MismatchedRefEscapeInTernary, node.Location);
......
......@@ -49,11 +49,21 @@ public override BoundNode VisitConditionalOperator(BoundConditionalOperator node
ConstantValue conditionConstantValue = rewrittenCondition.ConstantValue;
if (conditionConstantValue == ConstantValue.True)
{
return EnsureNotAssignableIfUsedAsMethodReceiver(rewrittenConsequence);
if (!isRef)
{
rewrittenConsequence = EnsureNotAssignableIfUsedAsMethodReceiver(rewrittenConsequence);
}
return rewrittenConsequence;
}
else if (conditionConstantValue == ConstantValue.False)
{
return EnsureNotAssignableIfUsedAsMethodReceiver(rewrittenAlternative);
if (!isRef)
{
rewrittenAlternative = EnsureNotAssignableIfUsedAsMethodReceiver(rewrittenAlternative);
}
return rewrittenAlternative;
}
else
{
......
......@@ -662,10 +662,7 @@ static void Main()
comp.VerifyEmitDiagnostics(
// (15,27): error CS8168: Cannot return local 'local1' by reference because it is not a ref local
// return ref b? ref local1: ref local2;
Diagnostic(ErrorCode.ERR_RefReturnLocal, "local1").WithArguments("local1").WithLocation(15, 27),
// (15,20): error CS8156: An expression cannot be used in this context because it may not be returned by reference
// return ref b? ref local1: ref local2;
Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "b? ref local1: ref local2").WithLocation(15, 20)
Diagnostic(ErrorCode.ERR_RefReturnLocal, "local1").WithArguments("local1").WithLocation(15, 27)
);
}
......@@ -696,10 +693,7 @@ static void Main()
comp.VerifyEmitDiagnostics(
// (14,37): error CS8168: Cannot return local 'local2' by reference because it is not a ref local
// return ref b? ref val1: ref local2;
Diagnostic(ErrorCode.ERR_RefReturnLocal, "local2").WithArguments("local2").WithLocation(14, 37),
// (14,20): error CS8351: Branches of a ref ternary operator cannot refer to variables with incompatible declaration scopes.
// return ref b? ref val1: ref local2;
Diagnostic(ErrorCode.ERR_MismatchedRefEscapeInTernary, "b? ref val1: ref local2").WithLocation(14, 20)
Diagnostic(ErrorCode.ERR_RefReturnLocal, "local2").WithArguments("local2").WithLocation(14, 37)
);
}
......@@ -735,10 +729,7 @@ struct S1
comp.VerifyEmitDiagnostics(
// (14,38): error CS8168: Cannot return local 'local2' by reference because it is not a ref local
// return ref (b? ref val1: ref local2).x;
Diagnostic(ErrorCode.ERR_RefReturnLocal, "local2").WithArguments("local2").WithLocation(14, 38),
// (14,21): error CS8351: Branches of a ref ternary operator cannot refer to variables with incompatible declaration scopes.
// return ref (b? ref val1: ref local2).x;
Diagnostic(ErrorCode.ERR_MismatchedRefEscapeInTernary, "b? ref val1: ref local2").WithLocation(14, 21)
Diagnostic(ErrorCode.ERR_RefReturnLocal, "local2").WithArguments("local2").WithLocation(14, 38)
);
}
......@@ -773,12 +764,9 @@ struct S1
var comp = CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe);
comp.VerifyEmitDiagnostics(
// (14,46): error CS8168: Cannot return local 'local2' by reference because it is not a ref local
// ref var temp = ref (b? ref val1: ref local2).x;
Diagnostic(ErrorCode.ERR_RefReturnLocal, "local2").WithArguments("local2").WithLocation(14, 46),
// (14,29): error CS8351: Branches of a ref ternary operator cannot refer to variables with incompatible declaration scopes.
// ref var temp = ref (b? ref val1: ref local2).x;
Diagnostic(ErrorCode.ERR_MismatchedRefEscapeInTernary, "b? ref val1: ref local2").WithLocation(14, 29)
// (15,20): error CS8157: Cannot return 'temp' by reference because it was initialized to a value that cannot be returned by reference
// return ref temp;
Diagnostic(ErrorCode.ERR_RefReturnNonreturnableLocal, "temp").WithArguments("temp").WithLocation(15, 20)
);
}
......@@ -828,6 +816,81 @@ .maxstack 1
");
}
[Fact]
public void TestRefConditionalSafeToReturn2()
{
var source = @"
class C
{
static void Main()
{
Test() ++;
System.Console.WriteLine(val1.x);
}
static ref int Test()
{
return ref (true? ref val1: ref val2).x;
}
static S1 val1;
static S1 val2;
struct S1
{
public int x;
}
}
";
var comp = CompileAndVerify(source, expectedOutput: "1", verify: false);
comp.VerifyDiagnostics();
comp.VerifyIL("C.Test", @"
{
// Code size 11 (0xb)
.maxstack 1
IL_0000: ldsflda ""C.S1 C.val1""
IL_0005: ldflda ""int C.S1.x""
IL_000a: ret
}
");
}
[Fact]
public void TestRefConditionalSafeToReturn3()
{
var source = @"
class C
{
static void Main()
{
(false? ref val1: ref val2) = (true? 1: val2);
(true? ref val1: ref val2) = (false? ref val1: ref val2);
System.Console.WriteLine(val1);
}
static int val1;
static int val2;
}
";
var comp = CompileAndVerify(source, expectedOutput: "1", verify: false);
comp.VerifyDiagnostics();
comp.VerifyIL("C.Main()", @"
{
// Code size 27 (0x1b)
.maxstack 1
IL_0000: ldc.i4.1
IL_0001: stsfld ""int C.val2""
IL_0006: ldsfld ""int C.val2""
IL_000b: stsfld ""int C.val1""
IL_0010: ldsfld ""int C.val1""
IL_0015: call ""void System.Console.WriteLine(int)""
IL_001a: ret
}
");
}
[Fact]
public void TestRefConditionalDifferentTypes1()
{
......
......@@ -18,6 +18,8 @@ public class RefEscapingTests : CompilingTestBase
public void RefLikeReturnEscape()
{
var text = @"
using System;
class Program
{
static void Main()
......@@ -29,14 +31,14 @@ static void Main()
return ref (new int[1])[0];
}
static S1 MayWrap(ref int arg)
static S1 MayWrap(ref Span<int> arg)
{
return default;
}
static ref int Test3()
{
int local = 42;
Span<int> local = stackalloc int[1];
return ref Test1(MayWrap(ref local));
}
......@@ -45,16 +47,16 @@ static S1 MayWrap(ref int arg)
}
}
";
CreateStandardCompilation(text).VerifyDiagnostics(
// (21,42): error CS8168: Cannot return local 'local' by reference because it is not a ref local
CreateCompilationWithMscorlibAndSpan(text).VerifyDiagnostics(
// (23,42): error CS8526: Cannot use local 'local' in this context because it may expose referenced variables outside of their declaration scope
// return ref Test1(MayWrap(ref local));
Diagnostic(ErrorCode.ERR_RefReturnLocal, "local").WithArguments("local").WithLocation(21, 42),
// (21,30): error CS8347: Cannot use a result of 'Program.MayWrap(ref int)' in this context because it may expose variables referenced by parameter 'arg' outside of their declaration scope
Diagnostic(ErrorCode.ERR_EscapeLocal, "local").WithArguments("local").WithLocation(23, 42),
// (23,30): error CS8521: Cannot use a result of 'Program.MayWrap(ref Span<int>)' in this context because it may expose variables referenced by parameter 'arg' outside of their declaration scope
// return ref Test1(MayWrap(ref local));
Diagnostic(ErrorCode.ERR_EscapeCall, "MayWrap(ref local)").WithArguments("Program.MayWrap(ref int)", "arg").WithLocation(21, 30),
// (21,24): error CS8347: Cannot use a result of 'Program.Test1(Program.S1)' in this context because it may expose variables referenced by parameter 'arg' outside of their declaration scope
Diagnostic(ErrorCode.ERR_EscapeCall, "MayWrap(ref local)").WithArguments("Program.MayWrap(ref System.Span<int>)", "arg").WithLocation(23, 30),
// (23,24): error CS8521: Cannot use a result of 'Program.Test1(Program.S1)' in this context because it may expose variables referenced by parameter 'arg' outside of their declaration scope
// return ref Test1(MayWrap(ref local));
Diagnostic(ErrorCode.ERR_EscapeCall, "Test1(MayWrap(ref local))").WithArguments("Program.Test1(Program.S1)", "arg").WithLocation(21, 24)
Diagnostic(ErrorCode.ERR_EscapeCall, "Test1(MayWrap(ref local))").WithArguments("Program.Test1(Program.S1)", "arg").WithLocation(23, 24)
);
}
......@@ -62,6 +64,8 @@ static S1 MayWrap(ref int arg)
public void RefLikeReturnEscape1()
{
var text = @"
using System;
class Program
{
static void Main()
......@@ -73,15 +77,15 @@ static void Main()
return ref (new int[1])[0];
}
static S1 MayWrap(ref int arg)
static S1 MayWrap(Span<int> arg)
{
return default;
}
static ref int Test3()
{
int local = 42;
var sp = MayWrap(ref local);
Span<int> local = stackalloc int[1];
var sp = MayWrap(local);
return ref Test1(sp);
}
......@@ -90,13 +94,13 @@ static S1 MayWrap(ref int arg)
}
}
";
CreateStandardCompilation(text).VerifyDiagnostics(
// (22,30): error CS8346: Cannot use local 'sp' in this context because it may expose referenced variables outside of their declaration scope
CreateCompilationWithMscorlibAndSpan(text).VerifyDiagnostics(
// (24,30): error CS8526: Cannot use local 'sp' in this context because it may expose referenced variables outside of their declaration scope
// return ref Test1(sp);
Diagnostic(ErrorCode.ERR_EscapeLocal, "sp").WithArguments("sp").WithLocation(22, 30),
// (22,24): error CS8347: Cannot use a result of 'Program.Test1(Program.S1)' in this context because it may expose variables referenced by parameter 'arg' outside of their declaration scope
Diagnostic(ErrorCode.ERR_EscapeLocal, "sp").WithArguments("sp").WithLocation(24, 30),
// (24,24): error CS8521: Cannot use a result of 'Program.Test1(Program.S1)' in this context because it may expose variables referenced by parameter 'arg' outside of their declaration scope
// return ref Test1(sp);
Diagnostic(ErrorCode.ERR_EscapeCall, "Test1(sp)").WithArguments("Program.Test1(Program.S1)", "arg").WithLocation(22, 24)
Diagnostic(ErrorCode.ERR_EscapeCall, "Test1(sp)").WithArguments("Program.Test1(Program.S1)", "arg").WithLocation(24, 24)
);
}
......@@ -104,6 +108,7 @@ static S1 MayWrap(ref int arg)
public void RefLikeReturnEscapeWithRefLikes()
{
var text = @"
using System;
class Program
{
static void Main()
......@@ -115,25 +120,22 @@ static void Main()
return ref (new int[1])[0];
}
static S1 MayWrap(ref int arg)
static S1 MayWrap(Span<int> arg)
{
return default;
}
static ref int Test3()
{
int local = 42;
var sp = MayWrap(ref local);
return ref Test1(ref sp); // error
Span<int> local = stackalloc int[1];
var sp = MayWrap(local);
return ref Test1(ref sp); // error1
}
static ref int Test4()
{
var sp = MayWrap(ref (new int[1])[0]);
// valid.
// Even though sp is itself not ref-returnable, Test1 cannot ref-return it
return ref Test1(ref sp); // OK
var sp = MayWrap(default);
return ref Test1(ref sp); // error2
}
ref struct S1
......@@ -141,13 +143,19 @@ static S1 MayWrap(ref int arg)
}
}
";
CreateStandardCompilation(text).VerifyDiagnostics(
// (22,34): error CS8346: Cannot use local 'sp' in this context because it may expose referenced variables outside of their declaration scope
// return ref Test1(ref sp); // error
Diagnostic(ErrorCode.ERR_EscapeLocal, "sp").WithArguments("sp").WithLocation(22, 34),
// (22,24): error CS8347: Cannot use a result of 'Program.Test1(ref Program.S1)' in this context because it may expose variables referenced by parameter 'arg' outside of their declaration scope
// return ref Test1(ref sp); // error
Diagnostic(ErrorCode.ERR_EscapeCall, "Test1(ref sp)").WithArguments("Program.Test1(ref Program.S1)", "arg").WithLocation(22, 24)
CreateCompilationWithMscorlibAndSpan(text).VerifyDiagnostics(
// (23,34): error CS8168: Cannot return local 'sp' by reference because it is not a ref local
// return ref Test1(ref sp); // error1
Diagnostic(ErrorCode.ERR_RefReturnLocal, "sp").WithArguments("sp").WithLocation(23, 34),
// (23,24): error CS8521: Cannot use a result of 'Program.Test1(ref Program.S1)' in this context because it may expose variables referenced by parameter 'arg' outside of their declaration scope
// return ref Test1(ref sp); // error1
Diagnostic(ErrorCode.ERR_EscapeCall, "Test1(ref sp)").WithArguments("Program.Test1(ref Program.S1)", "arg").WithLocation(23, 24),
// (29,34): error CS8168: Cannot return local 'sp' by reference because it is not a ref local
// return ref Test1(ref sp); // error2
Diagnostic(ErrorCode.ERR_RefReturnLocal, "sp").WithArguments("sp").WithLocation(29, 34),
// (29,24): error CS8521: Cannot use a result of 'Program.Test1(ref Program.S1)' in this context because it may expose variables referenced by parameter 'arg' outside of their declaration scope
// return ref Test1(ref sp); // error2
Diagnostic(ErrorCode.ERR_EscapeCall, "Test1(ref sp)").WithArguments("Program.Test1(ref Program.S1)", "arg").WithLocation(29, 24)
);
}
......@@ -155,41 +163,41 @@ static S1 MayWrap(ref int arg)
public void RefLikeReturnEscapeWithRefLikes1()
{
var text = @"
using System;
class Program
{
static void Main()
{
}
static ref int Test1(ref S1 arg)
static ref Span<int> Test1(ref S1 arg)
{
return ref (new int[1])[0];
throw null;
}
static S1 MayWrap(ref int arg)
static S1 MayWrap(Span<int> arg)
{
return default;
}
static void Test5()
{
var sp = MayWrap(ref (new int[1])[0]);
var sp = MayWrap(default);
// returnable.
// Even though sp is itself not ref-returnable, Test1 cannot ref-return it, so spR is returnable by value.
var spR = MayWrap(ref Test1(ref sp));
var spR = MayWrap(Test1(ref sp));
int local = 42;
var sp1 = MayWrap(ref local);
Span<int> local = stackalloc int[1];
var sp1 = MayWrap(local);
// not returnable by value. (since it refers to a local)
var spNr = MayWrap(ref Test1(ref sp1));
// not returnable by value. (since it refers to a local data)
var spNr = MayWrap(Test1(ref sp1));
// error
spR = spNr;
// OK, since both are not ref returnable
ref var ternary = ref true? ref spR: ref spNr;
// ok, picks the narrowest val-escape
var ternary = true? spR: spNr;
// error
spR = ternary;
......@@ -200,11 +208,11 @@ static void Test5()
}
}
";
CreateStandardCompilation(text).VerifyDiagnostics(
// (33,19): error CS8346: Cannot use local 'spNr' in this context because it may expose referenced variables outside of their declaration scope
CreateCompilationWithMscorlibAndSpan(text).VerifyDiagnostics(
// (33,19): error CS8526: Cannot use local 'spNr' in this context because it may expose referenced variables outside of their declaration scope
// spR = spNr;
Diagnostic(ErrorCode.ERR_EscapeLocal, "spNr").WithArguments("spNr").WithLocation(33, 19),
// (39,19): error CS8346: Cannot use local 'ternary' in this context because it may expose referenced variables outside of their declaration scope
// (39,19): error CS8526: Cannot use local 'ternary' in this context because it may expose referenced variables outside of their declaration scope
// spR = ternary;
Diagnostic(ErrorCode.ERR_EscapeLocal, "ternary").WithArguments("ternary").WithLocation(39, 19)
);
......@@ -214,6 +222,7 @@ static void Test5()
public void RefLikeReturnEscapeInParam()
{
var text = @"
using System;
class Program
{
static void Main()
......@@ -225,14 +234,14 @@ static void Main()
return ref (new int[1])[0];
}
static S1 MayWrap(ref readonly int arg)
static S1 MayWrap(ref readonly Span<int> arg)
{
return default;
}
static ref int Test3()
{
int local = 42;
Span<int> local = stackalloc int[1];
var sp = MayWrap(local);
// not an error
......@@ -250,16 +259,16 @@ static S1 MayWrap(ref readonly int arg)
}
}
";
CreateStandardCompilation(text).VerifyDiagnostics(
// (30,30): error CS8346: Cannot use local 'sp' in this context because it may expose referenced variables outside of their declaration scope
CreateCompilationWithMscorlibAndSpan(text).VerifyDiagnostics(
// (31,30): error CS8526: Cannot use local 'sp' in this context because it may expose referenced variables outside of their declaration scope
// return ref Test1(sp);
Diagnostic(ErrorCode.ERR_EscapeLocal, "sp").WithArguments("sp").WithLocation(30, 30),
// (30,24): error CS8347: Cannot use a result of 'Program.Test1(Program.S1)' in this context because it may expose variables referenced by parameter 'arg' outside of their declaration scope
Diagnostic(ErrorCode.ERR_EscapeLocal, "sp").WithArguments("sp").WithLocation(31, 30),
// (31,24): error CS8521: Cannot use a result of 'Program.Test1(Program.S1)' in this context because it may expose variables referenced by parameter 'arg' outside of their declaration scope
// return ref Test1(sp);
Diagnostic(ErrorCode.ERR_EscapeCall, "Test1(sp)").WithArguments("Program.Test1(Program.S1)", "arg").WithLocation(30, 24),
// (27,13): warning CS1717: Assignment made to same variable; did you mean to assign something else?
Diagnostic(ErrorCode.ERR_EscapeCall, "Test1(sp)").WithArguments("Program.Test1(Program.S1)", "arg").WithLocation(31, 24),
// (28,13): warning CS1717: Assignment made to same variable; did you mean to assign something else?
// sp = sp;
Diagnostic(ErrorCode.WRN_AssignmentToSelf, "sp = sp").WithLocation(27, 13)
Diagnostic(ErrorCode.WRN_AssignmentToSelf, "sp = sp").WithLocation(28, 13)
);
}
......@@ -278,15 +287,15 @@ static void Main()
return ref (new int[1])[0];
}
static S1 MayWrap(ref readonly int arg = 123)
static S1 MayNotWrap(ref readonly int arg = 123)
{
return default;
}
static ref int Test3()
{
// error here
return ref Test1(MayWrap());
// ok
return ref Test1(MayNotWrap());
}
ref struct S1
......@@ -294,40 +303,34 @@ static S1 MayWrap(ref readonly int arg = 123)
}
}
";
CreateStandardCompilation(text).VerifyDiagnostics(
// (21,30): error CS8521: Cannot use a result of 'Program.MayWrap(ref readonly int)' in this context because it may expose variables referenced by parameter 'arg' outside of their declaration scope
// return ref Test1(MayWrap());
Diagnostic(ErrorCode.ERR_EscapeCall, "MayWrap()").WithArguments("Program.MayWrap(ref readonly int)", "arg").WithLocation(21, 30),
// (21,24): error CS8521: Cannot use a result of 'Program.Test1(Program.S1)' in this context because it may expose variables referenced by parameter 'arg' outside of their declaration scope
// return ref Test1(MayWrap());
Diagnostic(ErrorCode.ERR_EscapeCall, "Test1(MayWrap())").WithArguments("Program.Test1(Program.S1)", "arg").WithLocation(21, 24)
);
CreateCompilationWithMscorlibAndSpan(text).VerifyDiagnostics();
}
[Fact()]
public void RefLikeScopeEscape()
{
var text = @"
using System;
class Program
{
static void Main()
{
int outer = 1;
Span<int> outer = default;
S1 x = MayWrap(ref outer);
S1 x = MayWrap(outer);
{
int inner = 1;
Span<int> inner = stackalloc int[1];
// valid
x = MayWrap(ref outer);
x = MayWrap(outer);
// error
x = MayWrap(ref inner);
x = MayWrap(inner);
}
}
static S1 MayWrap(ref int arg)
static S1 MayWrap(Span<int> arg)
{
return default;
}
......@@ -337,13 +340,13 @@ static S1 MayWrap(ref int arg)
}
}
";
CreateStandardCompilation(text).VerifyDiagnostics(
// (17,33): error CS8346: Cannot use local 'inner' in this context because it may expose referenced variables outside of their declaration scope
// x = MayWrap(ref inner);
Diagnostic(ErrorCode.ERR_EscapeLocal, "inner").WithArguments("inner").WithLocation(17, 33),
// (17,21): error CS8347: Cannot use a result of 'Program.MayWrap(ref int)' in this context because it may expose variables referenced by parameter 'arg' outside of their declaration scope
// x = MayWrap(ref inner);
Diagnostic(ErrorCode.ERR_EscapeCall, "MayWrap(ref inner)").WithArguments("Program.MayWrap(ref int)", "arg").WithLocation(17, 21)
CreateCompilationWithMscorlibAndSpan(text).VerifyDiagnostics(
// (18,29): error CS8526: Cannot use local 'inner' in this context because it may expose referenced variables outside of their declaration scope
// x = MayWrap(inner);
Diagnostic(ErrorCode.ERR_EscapeLocal, "inner").WithArguments("inner").WithLocation(18, 29),
// (18,21): error CS8521: Cannot use a result of 'Program.MayWrap(Span<int>)' in this context because it may expose variables referenced by parameter 'arg' outside of their declaration scope
// x = MayWrap(inner);
Diagnostic(ErrorCode.ERR_EscapeCall, "MayWrap(inner)").WithArguments("Program.MayWrap(System.Span<int>)", "arg").WithLocation(18, 21)
);
}
......@@ -351,17 +354,18 @@ static S1 MayWrap(ref int arg)
public void RefLikeScopeEscapeReturnable()
{
var text = @"
using System;
class Program
{
static void Main()
{
int outer = 1;
Span<int> outer = default;
// make x returnable
S1 x = default;
{
int inner = 1;
Span<int> inner = stackalloc int[0];
// valid
x = MayWrap(ref outer);
......@@ -371,7 +375,7 @@ static void Main()
}
}
static S1 MayWrap(ref int arg)
static S1 MayWrap(ref Span<int> arg)
{
return default;
}
......@@ -381,19 +385,13 @@ static S1 MayWrap(ref int arg)
}
}
";
CreateStandardCompilation(text).VerifyDiagnostics(
// (15,33): error CS8168: Cannot return local 'outer' by reference because it is not a ref local
// x = MayWrap(ref outer);
Diagnostic(ErrorCode.ERR_RefReturnLocal, "outer").WithArguments("outer").WithLocation(15, 33),
// (15,21): error CS8347: Cannot use a result of 'Program.MayWrap(ref int)' in this context because it may expose variables referenced by parameter 'arg' outside of their declaration scope
// x = MayWrap(ref outer);
Diagnostic(ErrorCode.ERR_EscapeCall, "MayWrap(ref outer)").WithArguments("Program.MayWrap(ref int)", "arg").WithLocation(15, 21),
// (18,33): error CS8168: Cannot return local 'inner' by reference because it is not a ref local
CreateCompilationWithMscorlibAndSpan(text).VerifyDiagnostics(
// (19,33): error CS8526: Cannot use local 'inner' in this context because it may expose referenced variables outside of their declaration scope
// x = MayWrap(ref inner);
Diagnostic(ErrorCode.ERR_RefReturnLocal, "inner").WithArguments("inner").WithLocation(18, 33),
// (18,21): error CS8347: Cannot use a result of 'Program.MayWrap(ref int)' in this context because it may expose variables referenced by parameter 'arg' outside of their declaration scope
Diagnostic(ErrorCode.ERR_EscapeLocal, "inner").WithArguments("inner").WithLocation(19, 33),
// (19,21): error CS8521: Cannot use a result of 'Program.MayWrap(ref Span<int>)' in this context because it may expose variables referenced by parameter 'arg' outside of their declaration scope
// x = MayWrap(ref inner);
Diagnostic(ErrorCode.ERR_EscapeCall, "MayWrap(ref inner)").WithArguments("Program.MayWrap(ref int)", "arg").WithLocation(18, 21)
Diagnostic(ErrorCode.ERR_EscapeCall, "MayWrap(ref inner)").WithArguments("Program.MayWrap(ref System.Span<int>)", "arg").WithLocation(19, 21)
);
}
......@@ -401,16 +399,17 @@ static S1 MayWrap(ref int arg)
public void RefLikeScopeEscapeThis()
{
var text = @"
using System;
class Program
{
static void Main()
{
int outer = 1;
Span<int> outer = default;
S1 x = MayWrap(ref outer);
{
int inner = 1;
Span<int> inner = stackalloc int[1];
// valid
x = S1.NotSlice(1);
......@@ -423,7 +422,7 @@ static void Main()
}
}
static S1 MayWrap(ref int arg)
static S1 MayWrap(ref Span<int> arg)
{
return default;
}
......@@ -436,13 +435,13 @@ static S1 MayWrap(ref int arg)
}
}
";
CreateStandardCompilation(text).VerifyDiagnostics(
// (20,33): error CS8346: Cannot use local 'inner' in this context because it may expose referenced variables outside of their declaration scope
CreateCompilationWithMscorlibAndSpan(text).VerifyDiagnostics(
// (21,33): error CS8526: Cannot use local 'inner' in this context because it may expose referenced variables outside of their declaration scope
// x = MayWrap(ref inner).Slice(1);
Diagnostic(ErrorCode.ERR_EscapeLocal, "inner").WithArguments("inner").WithLocation(20, 33),
// (20,21): error CS8347: Cannot use a result of 'Program.MayWrap(ref int)' in this context because it may expose variables referenced by parameter 'arg' outside of their declaration scope
Diagnostic(ErrorCode.ERR_EscapeLocal, "inner").WithArguments("inner").WithLocation(21, 33),
// (21,21): error CS8521: Cannot use a result of 'Program.MayWrap(ref Span<int>)' in this context because it may expose variables referenced by parameter 'arg' outside of their declaration scope
// x = MayWrap(ref inner).Slice(1);
Diagnostic(ErrorCode.ERR_EscapeCall, "MayWrap(ref inner)").WithArguments("Program.MayWrap(ref int)", "arg").WithLocation(20, 21)
Diagnostic(ErrorCode.ERR_EscapeCall, "MayWrap(ref inner)").WithArguments("Program.MayWrap(ref System.Span<int>)", "arg").WithLocation(21, 21)
);
}
......@@ -450,16 +449,17 @@ static S1 MayWrap(ref int arg)
public void RefLikeScopeEscapeThisRef()
{
var text = @"
using System;
class Program
{
static void Main()
{
int outer = 1;
Span<int> outer = default;
ref S1 x = ref MayWrap(ref outer)[0];
{
int inner = 1;
Span<int> inner = stackalloc int[1];
// valid
x[0] = MayWrap(ref outer).Slice(1)[0];
......@@ -479,7 +479,7 @@ static void Main()
}
}
static S1 MayWrap(ref int arg)
static S1 MayWrap(ref Span<int> arg)
{
return default;
}
......@@ -496,28 +496,28 @@ static S1 MayWrap(ref int arg)
}
}
";
CreateStandardCompilation(text).VerifyDiagnostics(
// (19,32): error CS8346: Cannot use local 'inner' in this context because it may expose referenced variables outside of their declaration scope
CreateCompilationWithMscorlibAndSpan(text).VerifyDiagnostics(
// (20,32): error CS8526: Cannot use local 'inner' in this context because it may expose referenced variables outside of their declaration scope
// x[0] = MayWrap(ref inner).Slice(1)[0];
Diagnostic(ErrorCode.ERR_EscapeLocal, "inner").WithArguments("inner").WithLocation(19, 32),
// (19,20): error CS8347: Cannot use a result of 'Program.MayWrap(ref int)' in this context because it may expose variables referenced by parameter 'arg' outside of their declaration scope
Diagnostic(ErrorCode.ERR_EscapeLocal, "inner").WithArguments("inner").WithLocation(20, 32),
// (20,20): error CS8521: Cannot use a result of 'Program.MayWrap(ref Span<int>)' in this context because it may expose variables referenced by parameter 'arg' outside of their declaration scope
// x[0] = MayWrap(ref inner).Slice(1)[0];
Diagnostic(ErrorCode.ERR_EscapeCall, "MayWrap(ref inner)").WithArguments("Program.MayWrap(ref int)", "arg").WithLocation(19, 20),
// (24,32): error CS8346: Cannot use local 'inner' in this context because it may expose referenced variables outside of their declaration scope
Diagnostic(ErrorCode.ERR_EscapeCall, "MayWrap(ref inner)").WithArguments("Program.MayWrap(ref System.Span<int>)", "arg").WithLocation(20, 20),
// (25,32): error CS8526: Cannot use local 'inner' in this context because it may expose referenced variables outside of their declaration scope
// x[x] = MayWrap(ref inner).Slice(1)[0];
Diagnostic(ErrorCode.ERR_EscapeLocal, "inner").WithArguments("inner").WithLocation(24, 32),
// (24,20): error CS8347: Cannot use a result of 'Program.MayWrap(ref int)' in this context because it may expose variables referenced by parameter 'arg' outside of their declaration scope
Diagnostic(ErrorCode.ERR_EscapeLocal, "inner").WithArguments("inner").WithLocation(25, 32),
// (25,20): error CS8521: Cannot use a result of 'Program.MayWrap(ref Span<int>)' in this context because it may expose variables referenced by parameter 'arg' outside of their declaration scope
// x[x] = MayWrap(ref inner).Slice(1)[0];
Diagnostic(ErrorCode.ERR_EscapeCall, "MayWrap(ref inner)").WithArguments("Program.MayWrap(ref int)", "arg").WithLocation(24, 20),
// (27,50): error CS8346: Cannot use local 'inner' in this context because it may expose referenced variables outside of their declaration scope
Diagnostic(ErrorCode.ERR_EscapeCall, "MayWrap(ref inner)").WithArguments("Program.MayWrap(ref System.Span<int>)", "arg").WithLocation(25, 20),
// (28,50): error CS8526: Cannot use local 'inner' in this context because it may expose referenced variables outside of their declaration scope
// x.ReturnsRefArg(ref x) = MayWrap(ref inner).Slice(1)[0];
Diagnostic(ErrorCode.ERR_EscapeLocal, "inner").WithArguments("inner").WithLocation(27, 50),
// (27,38): error CS8347: Cannot use a result of 'Program.MayWrap(ref int)' in this context because it may expose variables referenced by parameter 'arg' outside of their declaration scope
Diagnostic(ErrorCode.ERR_EscapeLocal, "inner").WithArguments("inner").WithLocation(28, 50),
// (28,38): error CS8521: Cannot use a result of 'Program.MayWrap(ref Span<int>)' in this context because it may expose variables referenced by parameter 'arg' outside of their declaration scope
// x.ReturnsRefArg(ref x) = MayWrap(ref inner).Slice(1)[0];
Diagnostic(ErrorCode.ERR_EscapeCall, "MayWrap(ref inner)").WithArguments("Program.MayWrap(ref int)", "arg").WithLocation(27, 38),
// (42,56): error CS8166: Cannot return a parameter by reference 'arg' because it is not a ref or out parameter
Diagnostic(ErrorCode.ERR_EscapeCall, "MayWrap(ref inner)").WithArguments("Program.MayWrap(ref System.Span<int>)", "arg").WithLocation(28, 38),
// (43,56): error CS8166: Cannot return a parameter by reference 'arg' because it is not a ref or out parameter
// public ref S1 ReturnsRefArg(ref S1 arg) => ref arg;
Diagnostic(ErrorCode.ERR_RefReturnParameter, "arg").WithArguments("arg").WithLocation(42, 56)
Diagnostic(ErrorCode.ERR_RefReturnParameter, "arg").WithArguments("arg").WithLocation(43, 56)
);
}
......@@ -525,26 +525,27 @@ static S1 MayWrap(ref int arg)
public void RefLikeScopeEscapeField()
{
var text = @"
using System;
class Program
{
static void Main()
{
int outer = 1;
Span<int> outer = default;
S1 x = MayWrap(ref outer);
S1 x = MayWrap(outer);
{
int inner = 1;
Span<int> inner = stackalloc int[1];
// valid
x.field = MayWrap(ref outer).Slice(1).field;
x.field = MayWrap(outer).Slice(1).field;
// error
x.field = MayWrap(ref inner).Slice(1).field;
x.field = MayWrap(inner).Slice(1).field;
}
}
static S1 MayWrap(ref int arg)
static S1 MayWrap(Span<int> arg)
{
return default;
}
......@@ -561,13 +562,13 @@ static S1 MayWrap(ref int arg)
}
}
";
CreateStandardCompilation(text).VerifyDiagnostics(
// (17,35): error CS8346: Cannot use local 'inner' in this context because it may expose referenced variables outside of their declaration scope
// x.field = MayWrap(ref inner).Slice(1).field;
Diagnostic(ErrorCode.ERR_EscapeLocal, "inner").WithArguments("inner").WithLocation(17, 35),
// (17,23): error CS8347: Cannot use a result of 'Program.MayWrap(ref int)' in this context because it may expose variables referenced by parameter 'arg' outside of their declaration scope
// x.field = MayWrap(ref inner).Slice(1).field;
Diagnostic(ErrorCode.ERR_EscapeCall, "MayWrap(ref inner)").WithArguments("Program.MayWrap(ref int)", "arg").WithLocation(17, 23)
CreateCompilationWithMscorlibAndSpan(text).VerifyDiagnostics(
// (18,31): error CS8526: Cannot use local 'inner' in this context because it may expose referenced variables outside of their declaration scope
// x.field = MayWrap(inner).Slice(1).field;
Diagnostic(ErrorCode.ERR_EscapeLocal, "inner").WithArguments("inner").WithLocation(18, 31),
// (18,23): error CS8521: Cannot use a result of 'Program.MayWrap(Span<int>)' in this context because it may expose variables referenced by parameter 'arg' outside of their declaration scope
// x.field = MayWrap(inner).Slice(1).field;
Diagnostic(ErrorCode.ERR_EscapeCall, "MayWrap(inner)").WithArguments("Program.MayWrap(System.Span<int>)", "arg").WithLocation(18, 23)
);
}
......@@ -605,7 +606,7 @@ static S1 MayWrap(ref int arg)
}
}
";
CreateStandardCompilation(text).VerifyDiagnostics(
CreateCompilationWithMscorlibAndSpan(text).VerifyDiagnostics(
// no diagnostics expected
);
}
......@@ -614,7 +615,8 @@ static S1 MayWrap(ref int arg)
public void RefLikeEscapeMixingCallSameArgValue()
{
var text = @"
class Program
using System;
public class Program
{
static void Main()
{
......@@ -624,8 +626,8 @@ void Test1()
{
S1 rOuter = default;
int inner = 1;
S1 rInner = MayWrap(ref inner);
Span<int> inner = stackalloc int[1];
S1 rInner = MayWrap(inner);
// valid
MayAssign(ref rOuter);
......@@ -636,35 +638,29 @@ void Test1()
static void MayAssign(ref S1 arg1)
{
// should be an error
arg1 = MayWrap(ref arg1.field);
// valid
arg1 = MayWrap(arg1.field);
}
static S1 MayWrap(ref int arg)
static S1 MayWrap(Span<int> arg)
{
return default;
}
ref struct S1
public ref struct S1
{
public int field;
public Span<int> field;
}
}
";
CreateStandardCompilation(text).VerifyDiagnostics(
// (25,32): error CS8167: Cannot return by reference a member of parameter 'arg1' because it is not a ref or out parameter
// arg1 = MayWrap(ref arg1.field);
Diagnostic(ErrorCode.ERR_RefReturnParameter2, "arg1").WithArguments("arg1").WithLocation(25, 32),
// (25,20): error CS8347: Cannot use a result of 'Program.MayWrap(ref int)' in this context because it may expose variables referenced by parameter 'arg' outside of their declaration scope
// arg1 = MayWrap(ref arg1.field);
Diagnostic(ErrorCode.ERR_EscapeCall, "MayWrap(ref arg1.field)").WithArguments("Program.MayWrap(ref int)", "arg").WithLocation(25, 20)
);
CreateCompilationWithMscorlibAndSpan(text).VerifyDiagnostics();
}
[Fact()]
public void RefLikeEscapeMixingCall()
{
var text = @"
using System;
class Program
{
static void Main()
......@@ -675,7 +671,7 @@ void Test1()
{
S1 rOuter = default;
int inner = 1;
Span<int> inner = stackalloc int[1];
S1 rInner = MayWrap(ref inner);
// valid
......@@ -688,7 +684,7 @@ void Test1()
MayAssign(ref inner, ref rOuter);
}
static void MayAssign(ref int arg1, ref S1 arg2)
static void MayAssign(ref Span<int> arg1, ref S1 arg2)
{
arg2 = MayWrap(ref arg1);
}
......@@ -698,7 +694,7 @@ static void MayAssign(ref S1 arg1, ref S1 arg2)
arg1 = arg2;
}
static S1 MayWrap(ref int arg)
static S1 MayWrap(ref Span<int> arg)
{
return default;
}
......@@ -708,19 +704,19 @@ static S1 MayWrap(ref int arg)
}
}
";
CreateStandardCompilation(text).VerifyDiagnostics(
// (19,39): error CS8346: Cannot use local 'rInner' in this context because it may expose referenced variables outside of their declaration scope
CreateCompilationWithMscorlibAndSpan(text).VerifyDiagnostics(
// (20,39): error CS8526: Cannot use local 'rInner' in this context because it may expose referenced variables outside of their declaration scope
// MayAssign(ref rOuter, ref rInner);
Diagnostic(ErrorCode.ERR_EscapeLocal, "rInner").WithArguments("rInner").WithLocation(19, 39),
// (19,13): error CS8350: This combination of arguments to 'Program.MayAssign(ref Program.S1, ref Program.S1)' is disallowed because it may expose variables referenced by parameter 'arg2' outside of their declaration scope
Diagnostic(ErrorCode.ERR_EscapeLocal, "rInner").WithArguments("rInner").WithLocation(20, 39),
// (20,13): error CS8524: This combination of arguments to 'Program.MayAssign(ref Program.S1, ref Program.S1)' is disallowed because it may expose variables referenced by parameter 'arg2' outside of their declaration scope
// MayAssign(ref rOuter, ref rInner);
Diagnostic(ErrorCode.ERR_CallArgMixing, "MayAssign(ref rOuter, ref rInner)").WithArguments("Program.MayAssign(ref Program.S1, ref Program.S1)", "arg2").WithLocation(19, 13),
// (22,27): error CS8168: Cannot return local 'inner' by reference because it is not a ref local
Diagnostic(ErrorCode.ERR_CallArgMixing, "MayAssign(ref rOuter, ref rInner)").WithArguments("Program.MayAssign(ref Program.S1, ref Program.S1)", "arg2").WithLocation(20, 13),
// (23,27): error CS8526: Cannot use local 'inner' in this context because it may expose referenced variables outside of their declaration scope
// MayAssign(ref inner, ref rOuter);
Diagnostic(ErrorCode.ERR_RefReturnLocal, "inner").WithArguments("inner").WithLocation(22, 27),
// (22,13): error CS8350: This combination of arguments to 'Program.MayAssign(ref int, ref Program.S1)' is disallowed because it may expose variables referenced by parameter 'arg1' outside of their declaration scope
Diagnostic(ErrorCode.ERR_EscapeLocal, "inner").WithArguments("inner").WithLocation(23, 27),
// (23,13): error CS8524: This combination of arguments to 'Program.MayAssign(ref Span<int>, ref Program.S1)' is disallowed because it may expose variables referenced by parameter 'arg1' outside of their declaration scope
// MayAssign(ref inner, ref rOuter);
Diagnostic(ErrorCode.ERR_CallArgMixing, "MayAssign(ref inner, ref rOuter)").WithArguments("Program.MayAssign(ref int, ref Program.S1)", "arg1").WithLocation(22, 13)
Diagnostic(ErrorCode.ERR_CallArgMixing, "MayAssign(ref inner, ref rOuter)").WithArguments("Program.MayAssign(ref System.Span<int>, ref Program.S1)", "arg1").WithLocation(23, 13)
);
}
......@@ -781,7 +777,7 @@ static S1 MayWrap(ref int arg)
}
}
";
CreateStandardCompilation(text).VerifyDiagnostics(
CreateCompilationWithMscorlibAndSpan(text).VerifyDiagnostics(
// no diagnostics
);
}
......@@ -790,6 +786,7 @@ static S1 MayWrap(ref int arg)
public void RefLikeEscapeMixingIndexOnRefLike()
{
var text = @"
using System;
class Program
{
static void Main()
......@@ -799,9 +796,9 @@ static void Main()
void Test1()
{
S1 rOuter = default;
rOuter.field = 1;
rOuter.field = default;
int inner = 1;
Span<int> inner = stackalloc int[1];
S1 rInner = MayWrap(inner);
// valid
......@@ -817,16 +814,16 @@ void Test1()
int dummy4 = rOuter[inner];
}
static S1 MayWrap(ref readonly int arg)
static S1 MayWrap(ref readonly Span<int> arg)
{
return default;
}
ref struct S1
{
public int field;
public Span<int> field;
public int this[ref readonly int arg1]
public int this[ref readonly Span<int> arg1]
{
get
{
......@@ -840,7 +837,7 @@ static S1 MayWrap(ref readonly int arg)
{
get
{
// should be an error, arg1 is not ref-returnable, 'this' is val-returnable
// ok
this = MayWrap(arg1.field);
// this is actually OK and thus the errors in corresponding Test1 scenarios.
......@@ -852,25 +849,19 @@ static S1 MayWrap(ref readonly int arg)
}
}
";
CreateStandardCompilation(text).VerifyDiagnostics(
// (23,29): error CS8346: Cannot use local 'rInner' in this context because it may expose referenced variables outside of their declaration scope
CreateCompilationWithMscorlibAndSpan(text).VerifyDiagnostics(
// (24,29): error CS8526: Cannot use local 'rInner' in this context because it may expose referenced variables outside of their declaration scope
// int dummy3 = rOuter[rInner];
Diagnostic(ErrorCode.ERR_EscapeLocal, "rInner").WithArguments("rInner").WithLocation(23, 29),
// (23,22): error CS8524: This combination of arguments to 'Program.S1.this[ref readonly Program.S1]' is disallowed because it may expose variables referenced by parameter 'arg1' outside of their declaration scope
Diagnostic(ErrorCode.ERR_EscapeLocal, "rInner").WithArguments("rInner").WithLocation(24, 29),
// (24,22): error CS8524: This combination of arguments to 'Program.S1.this[ref readonly Program.S1]' is disallowed because it may expose variables referenced by parameter 'arg1' outside of their declaration scope
// int dummy3 = rOuter[rInner];
Diagnostic(ErrorCode.ERR_CallArgMixing, "rOuter[rInner]").WithArguments("Program.S1.this[ref readonly Program.S1]", "arg1").WithLocation(23, 22),
// (26,29): error CS8168: Cannot return local 'inner' by reference because it is not a ref local
Diagnostic(ErrorCode.ERR_CallArgMixing, "rOuter[rInner]").WithArguments("Program.S1.this[ref readonly Program.S1]", "arg1").WithLocation(24, 22),
// (27,29): error CS8526: Cannot use local 'inner' in this context because it may expose referenced variables outside of their declaration scope
// int dummy4 = rOuter[inner];
Diagnostic(ErrorCode.ERR_RefReturnLocal, "inner").WithArguments("inner").WithLocation(26, 29),
// (26,22): error CS8524: This combination of arguments to 'Program.S1.this[ref readonly int]' is disallowed because it may expose variables referenced by parameter 'arg1' outside of their declaration scope
Diagnostic(ErrorCode.ERR_EscapeLocal, "inner").WithArguments("inner").WithLocation(27, 29),
// (27,22): error CS8524: This combination of arguments to 'Program.S1.this[ref readonly Span<int>]' is disallowed because it may expose variables referenced by parameter 'arg1' outside of their declaration scope
// int dummy4 = rOuter[inner];
Diagnostic(ErrorCode.ERR_CallArgMixing, "rOuter[inner]").WithArguments("Program.S1.this[ref readonly int]", "arg1").WithLocation(26, 22),
// (53,32): error CS8167: Cannot return by reference a member of parameter 'arg1' because it is not a ref or out parameter
// this = MayWrap(arg1.field);
Diagnostic(ErrorCode.ERR_RefReturnParameter2, "arg1").WithArguments("arg1").WithLocation(53, 32),
// (53,24): error CS8521: Cannot use a result of 'Program.MayWrap(ref readonly int)' in this context because it may expose variables referenced by parameter 'arg' outside of their declaration scope
// this = MayWrap(arg1.field);
Diagnostic(ErrorCode.ERR_EscapeCall, "MayWrap(arg1.field)").WithArguments("Program.MayWrap(ref readonly int)", "arg").WithLocation(53, 24)
Diagnostic(ErrorCode.ERR_CallArgMixing, "rOuter[inner]").WithArguments("Program.S1.this[ref readonly System.Span<int>]", "arg1").WithLocation(27, 22)
);
}
......@@ -878,6 +869,7 @@ static S1 MayWrap(ref readonly int arg)
public void RefLikeEscapeMixingCtor()
{
var text = @"
using System;
class Program
{
static void Main()
......@@ -885,13 +877,13 @@ static void Main()
}
delegate void D1(ref S1 arg1, ref S1 arg2);
delegate void D2(ref int arg1, ref S1 arg2);
delegate void D2(ref Span<int> arg1, ref S1 arg2);
void Test1()
{
S1 rOuter = default;
int inner = 1;
Span<int> inner = stackalloc int[1];
S1 rInner = MayWrap(ref inner);
D1 MayAssignDel1 = MayAssign;
......@@ -907,7 +899,7 @@ void Test1()
MayAssignDel2(ref inner, ref rOuter);
}
static void MayAssign(ref int arg1, ref S1 arg2)
static void MayAssign(ref Span<int> arg1, ref S1 arg2)
{
arg2 = MayWrap(ref arg1);
}
......@@ -917,7 +909,7 @@ static void MayAssign(ref S1 arg1, ref S1 arg2)
arg1 = arg2;
}
static S1 MayWrap(ref int arg)
static S1 MayWrap(ref Span<int> arg)
{
return default;
}
......@@ -927,19 +919,19 @@ static S1 MayWrap(ref int arg)
}
}
";
CreateStandardCompilation(text).VerifyDiagnostics(
// (25,43): error CS8346: Cannot use local 'rInner' in this context because it may expose referenced variables outside of their declaration scope
CreateCompilationWithMscorlibAndSpan(text).VerifyDiagnostics(
// (26,43): error CS8526: Cannot use local 'rInner' in this context because it may expose referenced variables outside of their declaration scope
// MayAssignDel1(ref rOuter, ref rInner);
Diagnostic(ErrorCode.ERR_EscapeLocal, "rInner").WithArguments("rInner").WithLocation(25, 43),
// (25,13): error CS8350: This combination of arguments to 'Program.D1.Invoke(ref Program.S1, ref Program.S1)' is disallowed because it may expose variables referenced by parameter 'arg2' outside of their declaration scope
Diagnostic(ErrorCode.ERR_EscapeLocal, "rInner").WithArguments("rInner").WithLocation(26, 43),
// (26,13): error CS8524: This combination of arguments to 'Program.D1.Invoke(ref Program.S1, ref Program.S1)' is disallowed because it may expose variables referenced by parameter 'arg2' outside of their declaration scope
// MayAssignDel1(ref rOuter, ref rInner);
Diagnostic(ErrorCode.ERR_CallArgMixing, "MayAssignDel1(ref rOuter, ref rInner)").WithArguments("Program.D1.Invoke(ref Program.S1, ref Program.S1)", "arg2").WithLocation(25, 13),
// (28,31): error CS8168: Cannot return local 'inner' by reference because it is not a ref local
Diagnostic(ErrorCode.ERR_CallArgMixing, "MayAssignDel1(ref rOuter, ref rInner)").WithArguments("Program.D1.Invoke(ref Program.S1, ref Program.S1)", "arg2").WithLocation(26, 13),
// (29,31): error CS8526: Cannot use local 'inner' in this context because it may expose referenced variables outside of their declaration scope
// MayAssignDel2(ref inner, ref rOuter);
Diagnostic(ErrorCode.ERR_RefReturnLocal, "inner").WithArguments("inner").WithLocation(28, 31),
// (28,13): error CS8350: This combination of arguments to 'Program.D2.Invoke(ref int, ref Program.S1)' is disallowed because it may expose variables referenced by parameter 'arg1' outside of their declaration scope
Diagnostic(ErrorCode.ERR_EscapeLocal, "inner").WithArguments("inner").WithLocation(29, 31),
// (29,13): error CS8524: This combination of arguments to 'Program.D2.Invoke(ref Span<int>, ref Program.S1)' is disallowed because it may expose variables referenced by parameter 'arg1' outside of their declaration scope
// MayAssignDel2(ref inner, ref rOuter);
Diagnostic(ErrorCode.ERR_CallArgMixing, "MayAssignDel2(ref inner, ref rOuter)").WithArguments("Program.D2.Invoke(ref int, ref Program.S1)", "arg1").WithLocation(28, 13)
Diagnostic(ErrorCode.ERR_CallArgMixing, "MayAssignDel2(ref inner, ref rOuter)").WithArguments("Program.D2.Invoke(ref System.Span<int>, ref Program.S1)", "arg1").WithLocation(29, 13)
);
}
......@@ -947,6 +939,7 @@ static S1 MayWrap(ref int arg)
public void RefLikeEscapeMixingDelegate()
{
var text = @"
using System;
class Program
{
static void Main()
......@@ -957,7 +950,7 @@ void Test1()
{
S1 rOuter = default;
int inner = 1;
Span<int> inner = stackalloc int[2];
S1 rInner = MayWrap(ref inner);
// valid
......@@ -970,7 +963,7 @@ void Test1()
var dummy3 = new Program(ref inner, ref rOuter);
}
Program(ref int arg1, ref S1 arg2)
Program(ref Span<int> arg1, ref S1 arg2)
{
arg2 = MayWrap(ref arg1);
}
......@@ -980,7 +973,7 @@ void Test1()
arg1 = arg2;
}
static S1 MayWrap(ref int arg)
static S1 MayWrap(ref Span<int> arg)
{
return default;
}
......@@ -990,19 +983,19 @@ static S1 MayWrap(ref int arg)
}
}
";
CreateStandardCompilation(text).VerifyDiagnostics(
// (19,54): error CS8346: Cannot use local 'rInner' in this context because it may expose referenced variables outside of their declaration scope
CreateCompilationWithMscorlibAndSpan(text).VerifyDiagnostics(
// (20,54): error CS8526: Cannot use local 'rInner' in this context because it may expose referenced variables outside of their declaration scope
// var dummy2 = new Program(ref rOuter, ref rInner);
Diagnostic(ErrorCode.ERR_EscapeLocal, "rInner").WithArguments("rInner").WithLocation(19, 54),
// (19,26): error CS8350: This combination of arguments to 'Program.Program(ref Program.S1, ref Program.S1)' is disallowed because it may expose variables referenced by parameter 'arg2' outside of their declaration scope
Diagnostic(ErrorCode.ERR_EscapeLocal, "rInner").WithArguments("rInner").WithLocation(20, 54),
// (20,26): error CS8524: This combination of arguments to 'Program.Program(ref Program.S1, ref Program.S1)' is disallowed because it may expose variables referenced by parameter 'arg2' outside of their declaration scope
// var dummy2 = new Program(ref rOuter, ref rInner);
Diagnostic(ErrorCode.ERR_CallArgMixing, "new Program(ref rOuter, ref rInner)").WithArguments("Program.Program(ref Program.S1, ref Program.S1)", "arg2").WithLocation(19, 26),
// (22,42): error CS8168: Cannot return local 'inner' by reference because it is not a ref local
Diagnostic(ErrorCode.ERR_CallArgMixing, "new Program(ref rOuter, ref rInner)").WithArguments("Program.Program(ref Program.S1, ref Program.S1)", "arg2").WithLocation(20, 26),
// (23,42): error CS8526: Cannot use local 'inner' in this context because it may expose referenced variables outside of their declaration scope
// var dummy3 = new Program(ref inner, ref rOuter);
Diagnostic(ErrorCode.ERR_RefReturnLocal, "inner").WithArguments("inner").WithLocation(22, 42),
// (22,26): error CS8350: This combination of arguments to 'Program.Program(ref int, ref Program.S1)' is disallowed because it may expose variables referenced by parameter 'arg1' outside of their declaration scope
Diagnostic(ErrorCode.ERR_EscapeLocal, "inner").WithArguments("inner").WithLocation(23, 42),
// (23,26): error CS8524: This combination of arguments to 'Program.Program(ref Span<int>, ref Program.S1)' is disallowed because it may expose variables referenced by parameter 'arg1' outside of their declaration scope
// var dummy3 = new Program(ref inner, ref rOuter);
Diagnostic(ErrorCode.ERR_CallArgMixing, "new Program(ref inner, ref rOuter)").WithArguments("Program.Program(ref int, ref Program.S1)", "arg1").WithLocation(22, 26)
Diagnostic(ErrorCode.ERR_CallArgMixing, "new Program(ref inner, ref rOuter)").WithArguments("Program.Program(ref System.Span<int>, ref Program.S1)", "arg1").WithLocation(23, 26)
);
}
......@@ -1010,6 +1003,7 @@ static S1 MayWrap(ref int arg)
public void RefLikeEscapeMixingCallOptionalIn()
{
var text = @"
using System;
class Program
{
static void Main()
......@@ -1020,22 +1014,22 @@ void Test1()
{
S1 rOuter = default;
int inner = 1;
Span<int> inner = stackalloc int[1];
S1 rInner = MayWrap(inner);
// error
// valid, optional arg is of the same escape level
MayAssign(ref rOuter);
// valid, optional arg is of the same escape level
// valid, optional arg is of wider escape level
MayAssign(ref rInner);
}
static void MayAssign(ref S1 arg1, ref readonly int arg2 = 42)
static void MayAssign(ref S1 arg1, ref readonly Span<int> arg2 = default)
{
arg1 = MayWrap(arg2);
}
static S1 MayWrap(ref readonly int arg)
static S1 MayWrap(ref readonly Span<int> arg)
{
return default;
}
......@@ -1045,11 +1039,7 @@ static S1 MayWrap(ref readonly int arg)
}
}
";
CreateStandardCompilation(text).VerifyDiagnostics(
// (16,13): error CS8524: This combination of arguments to 'Program.MayAssign(ref Program.S1, ref readonly int)' is disallowed because it may expose variables referenced by parameter 'arg2' outside of their declaration scope
// MayAssign(ref rOuter);
Diagnostic(ErrorCode.ERR_CallArgMixing, "MayAssign(ref rOuter)").WithArguments("Program.MayAssign(ref Program.S1, ref readonly int)", "arg2").WithLocation(16, 13)
);
CreateCompilationWithMscorlibAndSpan(text).VerifyDiagnostics();
}
[Fact()]
......@@ -1064,31 +1054,35 @@ static void Main()
public static int field = 1;
void Test1()
bool flag = true;
ref int Test1()
{
var local = 42;
ref var ternary1 = ref true ? ref field : ref local;
if (flag)
return ref true ? ref field : ref local;
ref var lr = ref local;
ref var fr = ref field;
ref var ternary2 = ref true ? ref lr : ref fr;
ref var ternary1 = ref true ? ref lr : ref fr;
if (flag)
return ref ternary1;
}
}
";
CreateStandardCompilation(text).VerifyDiagnostics(
// (13,55): error CS8168: Cannot return local 'local' by reference because it is not a ref local
// ref var ternary1 = ref true ? ref field : ref local;
Diagnostic(ErrorCode.ERR_RefReturnLocal, "local").WithArguments("local").WithLocation(13, 55),
// (13,32): error CS8351: Branches of a ref ternary operator cannot refer to variables with incompatible declaration scopes
// ref var ternary1 = ref true ? ref field : ref local;
Diagnostic(ErrorCode.ERR_MismatchedRefEscapeInTernary, "true ? ref field : ref local").WithLocation(13, 32),
// (18,43): error CS8157: Cannot return 'lr' by reference because it was initialized to a value that cannot be returned by reference
// ref var ternary2 = ref true ? ref lr : ref fr;
Diagnostic(ErrorCode.ERR_RefReturnNonreturnableLocal, "lr").WithArguments("lr").WithLocation(18, 43),
// (18,32): error CS8351: Branches of a ref ternary operator cannot refer to variables with incompatible declaration scopes
// ref var ternary2 = ref true ? ref lr : ref fr;
Diagnostic(ErrorCode.ERR_MismatchedRefEscapeInTernary, "true ? ref lr : ref fr").WithLocation(18, 32)
CreateCompilationWithMscorlibAndSpan(text).VerifyDiagnostics(
// (17,47): error CS8168: Cannot return local 'local' by reference because it is not a ref local
// return ref true ? ref field : ref local;
Diagnostic(ErrorCode.ERR_RefReturnLocal, "local").WithArguments("local").WithLocation(17, 47),
// (25,24): error CS8157: Cannot return 'ternary1' by reference because it was initialized to a value that cannot be returned by reference
// return ref ternary1;
Diagnostic(ErrorCode.ERR_RefReturnNonreturnableLocal, "ternary1").WithArguments("ternary1").WithLocation(25, 24),
// (12,13): error CS0161: 'Program.Test1()': not all code paths return a value
// ref int Test1()
Diagnostic(ErrorCode.ERR_ReturnExpected, "Test1").WithArguments("Program.Test1()").WithLocation(12, 13)
);
}
......@@ -1096,6 +1090,7 @@ void Test1()
public void MismatchedRefTernaryEscapeBlock()
{
var text = @"
using System;
class Program
{
static void Main()
......@@ -1106,23 +1101,43 @@ static void Main()
void Test1()
{
var outer = 42;
Span<int> outer = default;
var sOuter = MayWrap(ref outer);
{
var inner = 42;
Span<int> inner = stackalloc int[1];
var sInner = MayWrap(ref inner);
ref var ternary1 = ref true ? ref sOuter[1] : ref sInner[1];
ref var ternarySame1 = ref true ? ref sInner : ref sInner;
ref var ternarySame2 = ref true ? ref sOuter : ref sOuter;
// ok
ternarySame2 = true ? sOuter : sOuter;
// error
ternarySame2 = true ? sOuter : sInner;
// error
ternarySame2 = true ? sInner : sOuter;
// error, mixing val escapes
ref var ternary1 = ref true ? ref sOuter : ref sInner;
// error, mixing val escapes
ref var ternary2 = ref true ? ref sInner : ref sOuter;
// error, mixing val escapes
ref var ternary3 = ref true ? ref ternarySame1 : ref ternarySame2;
ref var ir = ref sInner[1];
ref var or = ref sOuter[1];
ref var ternary2 = ref true ? ref ir : ref or;
// no error, indexer cannot ref-return the instance, so ir and or are both safe to return
ref var ternary4 = ref true ? ref ir : ref or;
}
}
static S1 MayWrap(ref int arg)
static S1 MayWrap(ref Span<int> arg)
{
return default;
}
......@@ -1133,19 +1148,31 @@ static S1 MayWrap(ref int arg)
}
}
";
CreateStandardCompilation(text).VerifyDiagnostics(
// (19,63): error CS8346: Cannot use local 'sInner' in this context because it may expose referenced variables outside of their declaration scope
// ref var ternary1 = ref true ? ref sOuter[1] : ref sInner[1];
Diagnostic(ErrorCode.ERR_EscapeLocal, "sInner").WithArguments("sInner").WithLocation(19, 63),
// (19,36): error CS8351: Branches of a ref ternary operator cannot refer to variables with incompatible declaration scopes.
// ref var ternary1 = ref true ? ref sOuter[1] : ref sInner[1];
Diagnostic(ErrorCode.ERR_MismatchedRefEscapeInTernary, "true ? ref sOuter[1] : ref sInner[1]").WithLocation(19, 36),
// (24,47): error CS8346: Cannot use local 'ir' in this context because it may expose referenced variables outside of their declaration scope
// ref var ternary2 = ref true ? ref ir : ref or;
Diagnostic(ErrorCode.ERR_EscapeLocal, "ir").WithArguments("ir").WithLocation(24, 47),
// (24,36): error CS8351: Branches of a ref ternary operator cannot refer to variables with incompatible declaration scopes.
// ref var ternary2 = ref true ? ref ir : ref or;
Diagnostic(ErrorCode.ERR_MismatchedRefEscapeInTernary, "true ? ref ir : ref or").WithLocation(24, 36)
CreateCompilationWithMscorlibAndSpan(text).VerifyDiagnostics(
// (27,44): error CS8526: Cannot use local 'sInner' in this context because it may expose referenced variables outside of their declaration scope
// ternarySame2 = true ? sOuter : sInner;
Diagnostic(ErrorCode.ERR_EscapeLocal, "sInner").WithArguments("sInner").WithLocation(27, 44),
// (30,35): error CS8526: Cannot use local 'sInner' in this context because it may expose referenced variables outside of their declaration scope
// ternarySame2 = true ? sInner : sOuter;
Diagnostic(ErrorCode.ERR_EscapeLocal, "sInner").WithArguments("sInner").WithLocation(30, 35),
// (33,60): error CS8526: Cannot use local 'sInner' in this context because it may expose referenced variables outside of their declaration scope
// ref var ternary1 = ref true ? ref sOuter : ref sInner;
Diagnostic(ErrorCode.ERR_EscapeLocal, "sInner").WithArguments("sInner").WithLocation(33, 60),
// (33,36): error CS8525: Branches of a ref ternary operator cannot refer to variables with incompatible declaration scopes
// ref var ternary1 = ref true ? ref sOuter : ref sInner;
Diagnostic(ErrorCode.ERR_MismatchedRefEscapeInTernary, "true ? ref sOuter : ref sInner").WithLocation(33, 36),
// (36,47): error CS8526: Cannot use local 'sInner' in this context because it may expose referenced variables outside of their declaration scope
// ref var ternary2 = ref true ? ref sInner : ref sOuter;
Diagnostic(ErrorCode.ERR_EscapeLocal, "sInner").WithArguments("sInner").WithLocation(36, 47),
// (36,36): error CS8525: Branches of a ref ternary operator cannot refer to variables with incompatible declaration scopes
// ref var ternary2 = ref true ? ref sInner : ref sOuter;
Diagnostic(ErrorCode.ERR_MismatchedRefEscapeInTernary, "true ? ref sInner : ref sOuter").WithLocation(36, 36),
// (39,47): error CS8526: Cannot use local 'ternarySame1' in this context because it may expose referenced variables outside of their declaration scope
// ref var ternary3 = ref true ? ref ternarySame1 : ref ternarySame2;
Diagnostic(ErrorCode.ERR_EscapeLocal, "ternarySame1").WithArguments("ternarySame1").WithLocation(39, 47),
// (39,36): error CS8525: Branches of a ref ternary operator cannot refer to variables with incompatible declaration scopes
// ref var ternary3 = ref true ? ref ternarySame1 : ref ternarySame2;
Diagnostic(ErrorCode.ERR_MismatchedRefEscapeInTernary, "true ? ref ternarySame1 : ref ternarySame2").WithLocation(39, 36)
);
}
......@@ -1205,6 +1232,7 @@ Span<int> Test6()
public void LocalWithNoInitializerEscape()
{
var text = @"
using System;
class Program
{
static void Main()
......@@ -1215,7 +1243,7 @@ static void Main()
// ok
sp = default;
int local = 42;
Span<int> local = stackalloc int[1];
// error
sp = MayWrap(ref local);
......@@ -1227,7 +1255,7 @@ static S1 SefReferringTest()
return sp1;
}
static S1 MayWrap(ref int arg)
static S1 MayWrap(ref Span<int> arg)
{
return default;
}
......@@ -1239,15 +1267,15 @@ static S1 MayWrap(ref int arg)
}
";
CreateCompilationWithMscorlibAndSpan(text).VerifyDiagnostics(
// (15,30): error CS8168: Cannot return local 'local' by reference because it is not a ref local
// (16,30): error CS8526: Cannot use local 'local' in this context because it may expose referenced variables outside of their declaration scope
// sp = MayWrap(ref local);
Diagnostic(ErrorCode.ERR_RefReturnLocal, "local").WithArguments("local").WithLocation(15, 30),
// (15,18): error CS8347: Cannot use a result of 'Program.MayWrap(ref int)' in this context because it may expose variables referenced by parameter 'arg' outside of their declaration scope
Diagnostic(ErrorCode.ERR_EscapeLocal, "local").WithArguments("local").WithLocation(16, 30),
// (16,18): error CS8521: Cannot use a result of 'Program.MayWrap(ref Span<int>)' in this context because it may expose variables referenced by parameter 'arg' outside of their declaration scope
// sp = MayWrap(ref local);
Diagnostic(ErrorCode.ERR_EscapeCall, "MayWrap(ref local)").WithArguments("Program.MayWrap(ref int)", "arg").WithLocation(15, 18),
// (21,20): error CS8352: Cannot use local 'sp1' in this context because it may expose referenced variables outside of their declaration scope
Diagnostic(ErrorCode.ERR_EscapeCall, "MayWrap(ref local)").WithArguments("Program.MayWrap(ref System.Span<int>)", "arg").WithLocation(16, 18),
// (22,20): error CS8526: Cannot use local 'sp1' in this context because it may expose referenced variables outside of their declaration scope
// return sp1;
Diagnostic(ErrorCode.ERR_EscapeLocal, "sp1").WithArguments("sp1").WithLocation(21, 20)
Diagnostic(ErrorCode.ERR_EscapeLocal, "sp1").WithArguments("sp1").WithLocation(22, 20)
);
}
......@@ -1305,41 +1333,43 @@ public S2 M2()
public void MemberOfReadonlyRefLikeEscape()
{
var text = @"
using System;
public static class Program
{
public static void Main()
{
// OK, SR is readonly
new SR().TryGet(out int value1);
Span<int> value1 = stackalloc int[1];
new SR().TryGet(out value1);
// not OK, TryGet can write into the instance
new SW().TryGet(out int value2);
// error, TryGet can write into the instance
new SW().TryGet(out value1);
}
}
public readonly ref struct SR
{
public void TryGet(out int result)
public void TryGet(out Span<int> result)
{
result = 1;
result = default;
}
}
public ref struct SW
{
public void TryGet(out int result)
public void TryGet(out Span<int> result)
{
result = 1;
result = default;
}
}
";
CreateCompilationWithMscorlibAndSpan(text).VerifyDiagnostics(
// (10,33): error CS8168: Cannot return local 'value2' by reference because it is not a ref local
// new SW().TryGet(out int value2);
Diagnostic(ErrorCode.ERR_RefReturnLocal, "int value2").WithArguments("value2").WithLocation(10, 33),
// (10,13): error CS8350: This combination of arguments to 'SW.TryGet(out int)' is disallowed because it may expose variables referenced by parameter 'result' outside of their declaration scope
// new SW().TryGet(out int value2);
Diagnostic(ErrorCode.ERR_CallArgMixing, "new SW().TryGet(out int value2)").WithArguments("SW.TryGet(out int)", "result").WithLocation(10, 13)
// (12,33): error CS8526: Cannot use local 'value1' in this context because it may expose referenced variables outside of their declaration scope
// new SW().TryGet(out value1);
Diagnostic(ErrorCode.ERR_EscapeLocal, "value1").WithArguments("value1").WithLocation(12, 33),
// (12,13): error CS8524: This combination of arguments to 'SW.TryGet(out Span<int>)' is disallowed because it may expose variables referenced by parameter 'result' outside of their declaration scope
// new SW().TryGet(out value1);
Diagnostic(ErrorCode.ERR_CallArgMixing, "new SW().TryGet(out value1)").WithArguments("SW.TryGet(out System.Span<int>)", "result").WithLocation(12, 13)
);
}
......@@ -1386,5 +1416,92 @@ public void CopyTo(Span<T> other)
Diagnostic(ErrorCode.ERR_CallArgMixing, "new NotReadOnly<int>().CopyTo(stackAllocated)").WithArguments("NotReadOnly<int>.CopyTo(System.Span<int>)", "other").WithLocation(17, 13)
);
}
[WorkItem(22197, "https://github.com/dotnet/roslyn/issues/22197")]
[Fact()]
public void RefTernaryMustMatchValEscapes()
{
var text = @"
using System;
public class C
{
bool flag1 = true;
bool flag2 = false;
public void M(ref Span<int> global)
{
Span<int> local = stackalloc int[10];
ref var r1 = ref (flag1 ? ref global : ref local);
ref var r2 = ref (flag2 ? ref global : ref local);
// same as global = local; which would be an error.
// but we can’t fail here, since r1 and r2 are basically the same,
// so should fail when making r1 and r2 above.
r1 = r2;
}
public static void Main()
{
}
}
";
CreateCompilationWithMscorlibAndSpan(text).VerifyDiagnostics(
// (13,56): error CS8526: Cannot use local 'local' in this context because it may expose referenced variables outside of their declaration scope
// ref var r1 = ref (flag1 ? ref global : ref local);
Diagnostic(ErrorCode.ERR_EscapeLocal, "local").WithArguments("local").WithLocation(13, 56),
// (13,31): error CS8525: Branches of a ref ternary operator cannot refer to variables with incompatible declaration scopes
// ref var r1 = ref (flag1 ? ref global : ref local);
Diagnostic(ErrorCode.ERR_MismatchedRefEscapeInTernary, "flag1 ? ref global : ref local").WithLocation(13, 31),
// (14,56): error CS8526: Cannot use local 'local' in this context because it may expose referenced variables outside of their declaration scope
// ref var r2 = ref (flag2 ? ref global : ref local);
Diagnostic(ErrorCode.ERR_EscapeLocal, "local").WithArguments("local").WithLocation(14, 56),
// (14,31): error CS8525: Branches of a ref ternary operator cannot refer to variables with incompatible declaration scopes
// ref var r2 = ref (flag2 ? ref global : ref local);
Diagnostic(ErrorCode.ERR_MismatchedRefEscapeInTernary, "flag2 ? ref global : ref local").WithLocation(14, 31)
);
}
[WorkItem(22197, "https://github.com/dotnet/roslyn/issues/22197")]
[Fact()]
public void RefTernaryMustMatchValEscapes1()
{
var text = @"
using System;
public class C
{
public void M(ref Span<int> global)
{
Span<int> local = stackalloc int[0];
// ok
(true ? ref local : ref local) = (false ? ref global : ref global);
// also OK
(true ? ref local : ref local) = (false ? global : local);
}
public static void Main()
{
}
}
";
var comp = CreateCompilationWithMscorlibAndSpan(text);
comp.VerifyDiagnostics();
var compiled = CompileAndVerify(comp,expectedOutput: "", verify: false);
compiled.VerifyIL("C.M(ref System.Span<int>)", @"
{
// Code size 8 (0x8)
.maxstack 1
IL_0000: ldarg.1
IL_0001: ldobj ""System.Span<int>""
IL_0006: pop
IL_0007: ret
}
");
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册