提交 630e8fd5 编写于 作者: V vsadov

Updated CheckInvocationEscape/GetInvocationEscape for the "tweaked" rules

Fixed some ternary tests that violated mixing and that is now detected.
上级 b26fc0dc
......@@ -23,7 +23,7 @@ internal partial class Binder
// Some value kinds are semantically the same and the only distinction is how errors are reported
// for those purposes we reserve lowest 3 bits
private const int ValueKindInsignificantBits = 3;
private const BindValueKind ValueKindSignificantBitsMask = unchecked((BindValueKind)~((1 << ValueKindInsignificantBits)-1));
private const BindValueKind ValueKindSignificantBitsMask = unchecked((BindValueKind)~((1 << ValueKindInsignificantBits) - 1));
/// <summary>
/// Expression capabilities and requirements.
......@@ -83,7 +83,7 @@ internal enum BindValueKind : byte
/// Same as CompoundAssignment, the distinction is really just for error reporting.
/// </summary>
IncrementDecrement = CompoundAssignment + 1,
/// <summary>
/// Expression is a r/o reference.
/// </summary>
......@@ -403,7 +403,7 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin
var conditional = (BoundConditionalOperator)expr;
// byref conditional defers to its operands
if (conditional.IsByRef &&
if (conditional.IsByRef &&
(CheckValueKind(conditional.Consequence.Syntax, conditional.Consequence, valueKind, checkingReceiver: false, diagnostics: diagnostics) &
CheckValueKind(conditional.Alternative.Syntax, conditional.Alternative, valueKind, checkingReceiver: false, diagnostics: diagnostics)))
{
......@@ -412,7 +412,7 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin
// reprot standard lvalue error
break;
case BoundKind.FieldAccess:
var fieldAccess = (BoundFieldAccess)expr;
return CheckFieldValueKind(node, fieldAccess, valueKind, checkingReceiver, diagnostics);
......@@ -453,7 +453,7 @@ private bool CheckLocalValueKind(SyntaxNode node, BoundLocal local, BindValueKin
}
if (!localSymbol.IsWritable)
{
{
ReportReadonlyLocalError(node, localSymbol, valueKind, checkingReceiver, diagnostics);
return false;
}
......@@ -641,7 +641,7 @@ private static bool CheckFieldRefEscape(SyntaxNode node, BoundFieldAccess fieldA
}
// for other fields defer to the receiver.
return CheckRefEscape(node, fieldAccess.ReceiverOpt, escapeFrom, escapeTo, checkingReceiver:true, diagnostics: diagnostics);
return CheckRefEscape(node, fieldAccess.ReceiverOpt, escapeFrom, escapeTo, checkingReceiver: true, diagnostics: diagnostics);
}
private static bool CheckFieldLikeEventRefEscape(SyntaxNode node, BoundEventAccess eventAccess, uint escapeFrom, uint escapeTo, bool checkingReceiver, DiagnosticBag diagnostics)
......@@ -959,9 +959,10 @@ private bool CheckPropertyValueKind(SyntaxNode node, BoundExpression expr, BindV
}
}
// if ref is passed, check ref escape, since it is the same or narrower than val escape
// 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
var argument = args[argIndex];
var valid = effectiveRefKind != RefKind.None ?
var valid = effectiveRefKind != RefKind.None && argument.Type.IsByRefLikeType == false ?
CheckRefEscape(argument.Syntax, argument, escapeFrom, escapeTo, false, diagnostics) :
CheckValEscape(argument.Syntax, argument, escapeFrom, escapeTo, false, diagnostics);
......@@ -1040,8 +1041,10 @@ private bool CheckPropertyValueKind(SyntaxNode node, BoundExpression expr, BindV
}
}
// if ref is passed, check ref escape, since it is the same or narrower than val escape
var argEscape = effectiveRefKind != RefKind.None ?
// 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
var argument = args[argIndex];
var argEscape = effectiveRefKind != RefKind.None && argument.Type.IsByRefLikeType == false ?
GetRefEscape(args[argIndex], scopeOfTheContainingExpression) :
GetValEscape(args[argIndex], scopeOfTheContainingExpression);
......@@ -1059,7 +1062,7 @@ private bool CheckPropertyValueKind(SyntaxNode node, BoundExpression expr, BindV
// handle omitted optional "in" parameters if there are any
if (!parameters.IsDefault)
{
for(int i = 0; i < parameters.Length; i++)
for (int i = 0; i < parameters.Length; i++)
{
var parameter = parameters[i];
if (parameter.IsParams)
......@@ -1069,7 +1072,7 @@ private bool CheckPropertyValueKind(SyntaxNode node, BoundExpression expr, BindV
if (parameter.RefKind == RefKind.RefReadOnly && inParametersMatchedWithArgs?[i] != true)
{
// unmatched "in" parameter is an RValue
// unmatched "in" parameter is an RValue, its val-escape is scopeOfTheContainingExpression
inParametersMatchedWithArgs?.Free();
return scopeOfTheContainingExpression;
}
......@@ -1148,7 +1151,7 @@ private bool CheckPropertyValueKind(SyntaxNode node, BoundExpression expr, BindV
// 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
var argument = args[argIndex];
var valid = effectiveRefKind != RefKind.None && argument.Type.IsByRefLikeType == false?
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);
......@@ -1336,20 +1339,20 @@ static private ErrorCode GetStandardLvalueError(BindValueKind kind)
{
return ErrorCode.ERR_RefLvalueExpected;
}
throw ExceptionUtilities.UnexpectedValue(kind);
}
static private ErrorCode GetStandardRValueRefEscapeError(uint escapeTo)
{
if (escapeTo == Binder.ExternalScope)
{
{
return ErrorCode.ERR_RefReturnLvalueExpected;
}
return ErrorCode.ERR_EscapeOther;
}
private static void ReportReadOnlyFieldError(FieldSymbol field, SyntaxNode node, BindValueKind kind, bool checkingReceiver, DiagnosticBag diagnostics)
{
Debug.Assert((object)field != null);
......@@ -1503,7 +1506,7 @@ internal static bool CheckRefEscape(SyntaxNode node, BoundExpression expr, uint
if (!thisref.Type.IsValueType)
{
break;
}
}
//"this" is not returnable by reference in a struct.
if (escapeTo == Binder.ExternalScope)
......@@ -1546,7 +1549,7 @@ internal static bool CheckRefEscape(SyntaxNode node, BoundExpression expr, uint
var call = (BoundCall)expr;
var methodSymbol = call.Method;
if( methodSymbol.RefKind == RefKind.None)
if (methodSymbol.RefKind == RefKind.None)
{
break;
}
......
......@@ -695,9 +695,9 @@ static void Main()
// (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 CS8156: An expression cannot be used in this context because it may not be returned by reference
// (14,20): error CS8525: Branches of a ref ternary operator cannot refer to variables with incompatible declaration scopes.
// return ref b? ref val1: ref local2;
Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "b? ref val1: ref local2").WithLocation(14, 20)
Diagnostic(ErrorCode.ERR_MismatchedRefEscapeInTernary, "b? ref val1: ref local2").WithLocation(14, 20)
);
}
......@@ -734,9 +734,9 @@ struct S1
// (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,20): error CS8156: An expression cannot be used in this context because it may not be returned by reference
// (14,21): error CS8525: 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_RefReturnLvalueExpected, "(b? ref val1: ref local2).x").WithLocation(14, 20)
Diagnostic(ErrorCode.ERR_MismatchedRefEscapeInTernary, "b? ref val1: ref local2").WithLocation(14, 21)
);
}
......@@ -771,9 +771,12 @@ struct S1
var comp = CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe);
comp.VerifyEmitDiagnostics(
// (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)
// (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 CS8525: 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)
);
}
......
......@@ -100,6 +100,116 @@ static S1 MayWrap(ref int arg)
);
}
[Fact()]
public void RefLikeReturnEscapeWithRefLikes()
{
var text = @"
class Program
{
static void Main()
{
}
static ref int Test1(ref S1 arg)
{
return ref (new int[1])[0];
}
static S1 MayWrap(ref int arg)
{
return default;
}
static ref int Test3()
{
int local = 42;
var sp = MayWrap(ref local);
return ref Test1(ref sp); // error
}
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
}
ref struct S1
{
}
}
";
CreateStandardCompilation(text).VerifyDiagnostics(
// (22,34): error CS8520: 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 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); // error
Diagnostic(ErrorCode.ERR_EscapeCall, "Test1(ref sp)").WithArguments("Program.Test1(ref Program.S1)", "arg").WithLocation(22, 24)
);
}
[Fact()]
public void RefLikeReturnEscapeWithRefLikes1()
{
var text = @"
class Program
{
static void Main()
{
}
static ref int Test1(ref S1 arg)
{
return ref (new int[1])[0];
}
static S1 MayWrap(ref int arg)
{
return default;
}
static void Test5()
{
var sp = MayWrap(ref (new int[1])[0]);
// 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));
int local = 42;
var sp1 = MayWrap(ref local);
// not returnable by value. (since it refers to a local)
var spNr = MayWrap(ref Test1(ref sp1));
// error
spR = spNr;
// OK, since both are not ref returnable
ref var ternary = ref true? ref spR: ref spNr;
// error
spR = ternary;
}
ref struct S1
{
}
}
";
CreateStandardCompilation(text).VerifyDiagnostics(
// (33,19): error CS8520: 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 CS8520: 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)
);
}
[Fact()]
public void RefLikeReturnEscapeInParam()
{
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册