Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 12 additions & 9 deletions src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3894,7 +3894,8 @@ internal SafeContext GetRefEscape(BoundExpression expr, SafeContext localScopeDe
break;
}

return GetRefEscape(assignment.Left, localScopeDepth);
// The result of a ref assignment is its right-hand side.
return GetRefEscape(assignment.Right, localScopeDepth);

case BoundKind.Conversion:
Debug.Assert(expr is BoundConversion conversion &&
Expand Down Expand Up @@ -4228,9 +4229,10 @@ internal bool CheckRefEscape(SyntaxNode node, BoundExpression expr, SafeContext
break;
}

// The result of a ref assignment is its right-hand side.
return CheckRefEscape(
node,
assignment.Left,
assignment.Right,
escapeFrom,
escapeTo,
checkingReceiver: false,
Expand Down Expand Up @@ -4615,14 +4617,13 @@ internal SafeContext GetValEscape(BoundExpression expr, SafeContext localScopeDe
return GetValEscape(conversion.Operand, localScopeDepth);

case BoundKind.AssignmentOperator:
// https://github.com/dotnet/roslyn/issues/73549:
// We do not have a test that demonstrates that the statement below makes a difference.
// If 'localScopeDepth' is always returned, not a single test fails.
// Same for the 'case BoundKind.NullCoalescingAssignmentOperator:' below.
// The result of an assignment is its right-hand side.
return GetValEscape(((BoundAssignmentOperator)expr).Right, localScopeDepth);

case BoundKind.NullCoalescingAssignmentOperator:
return GetValEscape(((BoundNullCoalescingAssignmentOperator)expr).RightOperand, localScopeDepth);
var nullCoalescingAssignment = (BoundNullCoalescingAssignmentOperator)expr;
return GetValEscape(nullCoalescingAssignment.LeftOperand, localScopeDepth)
.Intersect(GetValEscape(nullCoalescingAssignment.RightOperand, localScopeDepth));

case BoundKind.IncrementOperator:
var increment = (BoundIncrementOperator)expr;
Expand Down Expand Up @@ -5376,11 +5377,13 @@ internal bool CheckValEscape(SyntaxNode node, BoundExpression expr, SafeContext

case BoundKind.AssignmentOperator:
var assignment = (BoundAssignmentOperator)expr;
return CheckValEscape(node, assignment.Left, escapeFrom, escapeTo, checkingReceiver: false, diagnostics: diagnostics);
// The result of an assignment is its right-hand side.
return CheckValEscape(node, assignment.Right, escapeFrom, escapeTo, checkingReceiver: false, diagnostics: diagnostics);

case BoundKind.NullCoalescingAssignmentOperator:
var nullCoalescingAssignment = (BoundNullCoalescingAssignmentOperator)expr;
return CheckValEscape(node, nullCoalescingAssignment.LeftOperand, escapeFrom, escapeTo, checkingReceiver: false, diagnostics: diagnostics);
return CheckValEscape(node, nullCoalescingAssignment.LeftOperand, escapeFrom, escapeTo, checkingReceiver: false, diagnostics: diagnostics) &&
CheckValEscape(node, nullCoalescingAssignment.RightOperand, escapeFrom, escapeTo, checkingReceiver: false, diagnostics: diagnostics);

case BoundKind.IncrementOperator:
var increment = (BoundIncrementOperator)expr;
Expand Down
88 changes: 88 additions & 0 deletions src/Compilers/CSharp/Test/Emit3/RefStructInterfacesTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28614,6 +28614,94 @@ static T F<T>() where T : allows ref struct
comp.VerifyEmitDiagnostics();
}

[Fact]
public void NullCoalescingAssignment_06()
{
var comp = CreateCompilation("""
static class C
{
static T M1<T>(T c1, scoped T c2) where T : allows ref struct
{
return X(c1 ??= c2);
}
static T M2<T>(T c1, scoped T c2) where T : allows ref struct
{
return X(c1 ??= c1);
}
static T M3<T>(T c1, scoped T c2) where T : allows ref struct
{
return X(c2 ??= c2);
}
static T M4<T>(T c1, scoped T c2) where T : allows ref struct
{
return X(c2 ??= c1);
}
static T X<T>(T c) where T : allows ref struct => c;
}
""", targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);

comp.VerifyDiagnostics(
// (5,18): error CS8352: Cannot use variable 'scoped T c2' in this context because it may expose referenced variables outside of their declaration scope
// return X(c1 ??= c2);
Diagnostic(ErrorCode.ERR_EscapeVariable, "c1 ??= c2").WithArguments("scoped T c2").WithLocation(5, 18),
// (5,16): error CS8347: Cannot use a result of 'C.X<T>(T)' in this context because it may expose variables referenced by parameter 'c' outside of their declaration scope
// return X(c1 ??= c2);
Diagnostic(ErrorCode.ERR_EscapeCall, "X(c1 ??= c2)").WithArguments("C.X<T>(T)", "c").WithLocation(5, 16),
// (13,18): error CS8352: Cannot use variable 'scoped T c2' in this context because it may expose referenced variables outside of their declaration scope
// return X(c2 ??= c2);
Diagnostic(ErrorCode.ERR_EscapeVariable, "c2 ??= c2").WithArguments("scoped T c2").WithLocation(13, 18),
// (13,16): error CS8347: Cannot use a result of 'C.X<T>(T)' in this context because it may expose variables referenced by parameter 'c' outside of their declaration scope
// return X(c2 ??= c2);
Diagnostic(ErrorCode.ERR_EscapeCall, "X(c2 ??= c2)").WithArguments("C.X<T>(T)", "c").WithLocation(13, 16),
// (17,18): error CS8352: Cannot use variable 'scoped T c2' in this context because it may expose referenced variables outside of their declaration scope
// return X(c2 ??= c1);
Diagnostic(ErrorCode.ERR_EscapeVariable, "c2 ??= c1").WithArguments("scoped T c2").WithLocation(17, 18),
// (17,16): error CS8347: Cannot use a result of 'C.X<T>(T)' in this context because it may expose variables referenced by parameter 'c' outside of their declaration scope
// return X(c2 ??= c1);
Diagnostic(ErrorCode.ERR_EscapeCall, "X(c2 ??= c1)").WithArguments("C.X<T>(T)", "c").WithLocation(17, 16));
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79054")]
public void NullCoalescingAssignment_07()
{
var comp = CreateCompilation("""
static class C
{
static T M1<T>(T c1, scoped T c2) where T : allows ref struct
{
var c3 = (c1 ??= c2);
return c3; // 1
}
static T M2<T>(T c1, scoped T c2) where T : allows ref struct
{
var c3 = (c1 ??= c1);
return c3; // 2
}
static T M3<T>(T c1, scoped T c2) where T : allows ref struct
{
var c3 = (c2 ??= c2);
return c3; // 3
}
static T M4<T>(T c1, scoped T c2) where T : allows ref struct
{
var c3 = (c2 ??= c1);
return c3; // 4
}
}
""", targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);

comp.VerifyDiagnostics(
// (6,16): error CS8352: Cannot use variable 'c3' in this context because it may expose referenced variables outside of their declaration scope
// return c3; // 1
Diagnostic(ErrorCode.ERR_EscapeVariable, "c3").WithArguments("c3").WithLocation(6, 16),
// (16,16): error CS8352: Cannot use variable 'c3' in this context because it may expose referenced variables outside of their declaration scope
// return c3; // 3
Diagnostic(ErrorCode.ERR_EscapeVariable, "c3").WithArguments("c3").WithLocation(16, 16),
// (21,16): error CS8352: Cannot use variable 'c3' in this context because it may expose referenced variables outside of their declaration scope
// return c3; // 4
Diagnostic(ErrorCode.ERR_EscapeVariable, "c3").WithArguments("c3").WithLocation(21, 16));
}

[Theory]
[CombinatorialData]
public void ObjectCreation_01(bool addStructConstraint)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24616,9 +24616,18 @@ static class Extensions
// (8,23): error CS8352: Cannot use variable 'scoped C c1' in this context because it may expose referenced variables outside of their declaration scope
// return Y(c += c1);
Diagnostic(ErrorCode.ERR_EscapeVariable, "c1").WithArguments("scoped C c1").WithLocation(8, 23),
// (12,16): error CS8347: Cannot use a result of 'C.Y(C)' in this context because it may expose variables referenced by parameter 'left' outside of their declaration scope
// return Y(c = X(c, c1));
Diagnostic(ErrorCode.ERR_EscapeCall, "Y(c = X(c, c1))").WithArguments("C.Y(C)", "left").WithLocation(12, 16),
// (12,22): error CS8347: Cannot use a result of 'C.X(C, C)' in this context because it may expose variables referenced by parameter 'right' outside of their declaration scope
// return Y(c = X(c, c1));
Diagnostic(ErrorCode.ERR_EscapeCall, "X(c, c1)").WithArguments("C.X(C, C)", "right").WithLocation(12, 22),
// (12,22): error CS8347: Cannot use a result of 'C.X(C, C)' in this context because it may expose variables referenced by parameter 'right' outside of their declaration scope
// return Y(c = X(c, c1));
Diagnostic(ErrorCode.ERR_EscapeCall, "X(c, c1)").WithArguments("C.X(C, C)", "right").WithLocation(12, 22),
// (12,27): error CS8352: Cannot use variable 'scoped C c1' in this context because it may expose referenced variables outside of their declaration scope
// return Y(c = X(c, c1));
Diagnostic(ErrorCode.ERR_EscapeVariable, "c1").WithArguments("scoped C c1").WithLocation(12, 27),
// (12,27): error CS8352: Cannot use variable 'scoped C c1' in this context because it may expose referenced variables outside of their declaration scope
// return Y(c = X(c, c1));
Diagnostic(ErrorCode.ERR_EscapeVariable, "c1").WithArguments("scoped C c1").WithLocation(12, 27)
Expand Down Expand Up @@ -24817,9 +24826,12 @@ static class Extensions
// (12,16): error CS8347: Cannot use a result of 'C.Y(C)' in this context because it may expose variables referenced by parameter 'left' outside of their declaration scope
// return Y(c = X(c, c1));
Diagnostic(ErrorCode.ERR_EscapeCall, "Y(c = X(c, c1))").WithArguments("C.Y(C)", "left").WithLocation(12, 16),
// (12,18): error CS8352: Cannot use variable 'scoped C c' in this context because it may expose referenced variables outside of their declaration scope
// (12,22): error CS8347: Cannot use a result of 'C.X(C, C)' in this context because it may expose variables referenced by parameter 'left' outside of their declaration scope
// return Y(c = X(c, c1));
Diagnostic(ErrorCode.ERR_EscapeCall, "X(c, c1)").WithArguments("C.X(C, C)", "left").WithLocation(12, 22),
// (12,24): error CS8352: Cannot use variable 'scoped C c' in this context because it may expose referenced variables outside of their declaration scope
// return Y(c = X(c, c1));
Diagnostic(ErrorCode.ERR_EscapeVariable, "c = X(c, c1)").WithArguments("scoped C c").WithLocation(12, 18)
Diagnostic(ErrorCode.ERR_EscapeVariable, "c").WithArguments("scoped C c").WithLocation(12, 24)
);
}

Expand Down Expand Up @@ -24866,16 +24878,19 @@ static class Extensions
// (12,16): error CS8347: Cannot use a result of 'C.Y(C)' in this context because it may expose variables referenced by parameter 'left' outside of their declaration scope
// return Y(c = X(c, c1));
Diagnostic(ErrorCode.ERR_EscapeCall, "Y(c = X(c, c1))").WithArguments("C.Y(C)", "left").WithLocation(12, 16),
// (12,18): error CS8352: Cannot use variable 'scoped C c' in this context because it may expose referenced variables outside of their declaration scope
// (12,22): error CS8347: Cannot use a result of 'C.X(C, scoped C)' in this context because it may expose variables referenced by parameter 'left' outside of their declaration scope
// return Y(c = X(c, c1));
Diagnostic(ErrorCode.ERR_EscapeVariable, "c = X(c, c1)").WithArguments("scoped C c").WithLocation(12, 18)
Diagnostic(ErrorCode.ERR_EscapeCall, "X(c, c1)").WithArguments("C.X(C, scoped C)", "left").WithLocation(12, 22),
// (12,24): error CS8352: Cannot use variable 'scoped C c' in this context because it may expose referenced variables outside of their declaration scope
// return Y(c = X(c, c1));
Diagnostic(ErrorCode.ERR_EscapeVariable, "c").WithArguments("scoped C c").WithLocation(12, 24)
);
}

/// <summary>
/// This is a clone of Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics.RefEscapingTests.UserDefinedBinaryOperator_RefStruct_Compound_ScopedTarget_04
/// </summary>
[Fact]
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79054")]
public void CompoundAssignment_089_RefSafety()
{
var source = """
Expand All @@ -24902,14 +24917,7 @@ static class Extensions
}
}
""";
CreateCompilation(source).VerifyDiagnostics(
// (12,16): error CS8347: Cannot use a result of 'C.Y(C)' in this context because it may expose variables referenced by parameter 'left' outside of their declaration scope
// return Y(c = X(c, c1));
Diagnostic(ErrorCode.ERR_EscapeCall, "Y(c = X(c, c1))").WithArguments("C.Y(C)", "left").WithLocation(12, 16),
// (12,18): error CS8352: Cannot use variable 'scoped C c' in this context because it may expose referenced variables outside of their declaration scope
// return Y(c = X(c, c1));
Diagnostic(ErrorCode.ERR_EscapeVariable, "c = X(c, c1)").WithArguments("scoped C c").WithLocation(12, 18)
);
CreateCompilation(source).VerifyDiagnostics();
}

/// <summary>
Expand Down
Loading
Loading