提交 b26fc0dc 编写于 作者: V vsadov

Ternary ref branches must have matching ref-escapes

上级 3c7e7ac9
......@@ -1518,15 +1518,14 @@ internal static bool CheckRefEscape(SyntaxNode node, BoundExpression expr, uint
case BoundKind.ConditionalOperator:
var conditional = (BoundConditionalOperator)expr;
// byref conditional defers to its operands
// 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) &&
CheckRefEscape(conditional.Alternative.Syntax, conditional.Alternative, escapeFrom, escapeTo, checkingReceiver: false, diagnostics: diagnostics)))
CheckRefEscape(conditional.Consequence.Syntax, conditional.Consequence, escapeFrom, escapeTo, checkingReceiver: false, diagnostics: diagnostics))
{
return true;
}
// reprot standard lvalue error
// report standard lvalue error
break;
case BoundKind.FieldAccess:
......@@ -1691,14 +1690,13 @@ internal static uint GetRefEscape(BoundExpression expr, uint scopeOfTheContainin
case BoundKind.ConditionalOperator:
var conditional = (BoundConditionalOperator)expr;
// byref conditional defers to its operands
// byref conditional defers to one operand. Since are here without errors, both operands must match.
if (conditional.IsByRef)
{
return Math.Max(GetRefEscape(conditional.Consequence, scopeOfTheContainingExpression),
GetRefEscape(conditional.Alternative, scopeOfTheContainingExpression));
return GetRefEscape(conditional.Consequence, scopeOfTheContainingExpression);
}
// report standard lvalue error
// otherwise it is an RValue
break;
case BoundKind.FieldAccess:
......
......@@ -3524,7 +3524,7 @@ private BoundExpression BindConditionalOperator(ConditionalExpressionSyntax node
{
diagnostics.Add(ErrorCode.ERR_RefConditionalNeedsTwoRefs, whenTrue.GetFirstToken().GetLocation());
}
}
}
BoundExpression condition = BindBooleanExpression(node.Condition, diagnostics);
......@@ -3636,6 +3636,32 @@ private BoundExpression BindConditionalOperator(ConditionalExpressionSyntax node
}
}
if (!hasErrors && isRef)
{
var currentScope = this.LocalScopeDepth;
// ref-escape must agree on both branches.
var whenTrueEscape = GetRefEscape(trueExpr, currentScope);
var whenFalseEscape = GetRefEscape(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);
}
else
{
CheckRefEscape(trueExpr.Syntax, trueExpr, currentScope, whenFalseEscape, checkingReceiver: false, diagnostics: diagnostics);
}
diagnostics.Add(ErrorCode.ERR_MismatchedRefEscapeInTernary, node.Location);
hasErrors = true;
}
}
ConstantValue constantValue = null;
if (!hasErrors)
......
......@@ -6307,6 +6307,15 @@ internal class CSharpResources {
}
}
/// <summary>
/// Looks up a localized string similar to Branches of a ref ternary operator cannot refer to variables with incompatible declaration scopes..
/// </summary>
internal static string ERR_MismatchedRefEscapeInTernary {
get {
return ResourceManager.GetString("ERR_MismatchedRefEscapeInTernary", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Argument missing.
/// </summary>
......
......@@ -4867,7 +4867,10 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
</data>
<data name="ERR_CallArgMixing" xml:space="preserve">
<value>This combination of arguments to '{0}' is disallowed because it may expose variables referenced by parameter '{1}' outside of their declaration scope</value>
</data>
</data>
<data name="ERR_MismatchedRefEscapeInTernary" xml:space="preserve">
<value>Branches of a ref ternary operator cannot refer to variables with incompatible declaration scopes.</value>
</data>
<data name="ERR_InitializeByValueVariableWithReference" xml:space="preserve">
<value>Cannot initialize a by-value variable with a reference</value>
</data>
......
......@@ -1531,6 +1531,7 @@ internal enum ErrorCode
ERR_EscapeCall2 = 8522,
ERR_EscapeOther = 8523,
ERR_CallArgMixing = 8524,
ERR_EscapeLocal = 8525,
ERR_MismatchedRefEscapeInTernary = 8525,
ERR_EscapeLocal = 8526,
}
}
......@@ -941,5 +941,101 @@ static S1 MayWrap(in int arg)
Diagnostic(ErrorCode.ERR_CallArgMixing, "MayAssign(ref rOuter)").WithArguments("Program.MayAssign(ref Program.S1, in int)", "arg2").WithLocation(16, 13)
);
}
[Fact()]
public void MismatchedRefTernaryEscape()
{
var text = @"
class Program
{
static void Main()
{
}
public static int field = 1;
void Test1()
{
var local = 42;
ref var ternary1 = ref true ? ref field : ref local;
ref var lr = ref local;
ref var fr = ref field;
ref var ternary2 = ref true ? ref field : ref local;
}
}
";
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 CS8525: 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,55): error CS8168: Cannot return local 'local' by reference because it is not a ref local
// ref var ternary2 = ref true ? ref field : ref local;
Diagnostic(ErrorCode.ERR_RefReturnLocal, "local").WithArguments("local").WithLocation(18, 55),
// (18,32): error CS8525: Branches of a ref ternary operator cannot refer to variables with incompatible declaration scopes.
// ref var ternary2 = ref true ? ref field : ref local;
Diagnostic(ErrorCode.ERR_MismatchedRefEscapeInTernary, "true ? ref field : ref local").WithLocation(18, 32));
}
[Fact()]
public void MismatchedRefTernaryEscapeBlock()
{
var text = @"
class Program
{
static void Main()
{
}
public static int field = 1;
void Test1()
{
var outer = 42;
var sOuter = MayWrap(ref outer);
{
var inner = 42;
var sInner = MayWrap(ref inner);
ref var ternary1 = ref true ? ref sOuter[1] : ref sInner[1];
ref var ir = ref sInner[1];
ref var or = ref sOuter[1];
ref var ternary2 = ref true ? ref ir : ref or;
}
}
static S1 MayWrap(ref int arg)
{
return default;
}
ref struct S1
{
public ref int this[int i] => throw null;
}
}
";
CreateStandardCompilation(text).VerifyDiagnostics(
// (19,63): error CS8520: 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 CS8525: 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 CS8520: 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 CS8525: 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)
);
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册