-
Notifications
You must be signed in to change notification settings - Fork 4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Escape rules for deconstruction and foreach variables #22354
Conversation
@gafter for review. Thanks |
foreach (var element in expr.Arguments) | ||
{ | ||
uint valEscape; | ||
if (element.Kind == BoundKind.TupleLiteral) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if [](start = 16, length = 2)
I would expect this test to be part of the switch in GetValEscape
. It seems out of place here. #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I see, in GetValEscape we would be using Math.Max.
In reply to: 141214199 [](ancestors = 141214199)
@@ -2237,6 +2263,8 @@ internal static bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint | |||
case BoundKind.DefaultExpression: | |||
case BoundKind.Parameter: | |||
case BoundKind.ThisReference: | |||
case BoundKind.TupleLiteral: | |||
case BoundKind.ConvertedTupleLiteral: | |||
// always returnable | |||
return true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
true [](start = 27, length = 4)
I don't think this is correct for a tuple literal. For example, I could write
(spanA, someString) = (spanB, null);
This is all valid in the type system even if spanA
and spanB
are ref structs (because the right-hand-side has no type), but the safe-to-escape value computed from the right-hand-side is from the safe-to-escape of the variable tupleB
, and the whole thing is only valid if the assignment spanA = spanB
would be valid.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I corrected the comment just above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the scenario.
I don't agree with your expectation though. The tuple on the right does receive a type, by inferring a merged type from both sides. In this case, the RHS will be made into a (Span, string)
which is illegal.
So (spanA, someString) = (spanB, null);
should produce an constraint error (Span cannot be used as a type argument).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In current circumstances a tuple literal cannot not contain ref-like elements (That could be considered a bug, but it is what it is). Considering that, returning val-escape being External seems to be valid.
Once we allow tuples to contain ref-likes, we will need to revisit this place. For now it seems valid assumption,
In reply to: 141237397 [](ancestors = 141237397)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there is a remaining issue for code such as (spanA, someString) = (spanB, null);
, which it appears this code would permit even if spanA = spanB
would be rejected.
22686fb
to
b55f46e
Compare
Filed feature request to allow deconstruction without requiring |
@VSadov @dotnet/roslyn-compiler for review. Thanks |
var tree = compilation.SyntaxTrees[0]; | ||
var decl = (ArgumentSyntax)tree.GetCompilationUnitRoot().DescendantNodes().Last(n => n.IsKind(SyntaxKind.Argument)); | ||
var model = compilation.GetSemanticModel(tree); | ||
Assert.Null(model.GetDeclaredSymbol(decl)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📝 There is a slight regression of the semantic model here. But (1) I don't think it is very significant, and (2) I think it is worthwhile because the root cause is a change to avoid many duplicate diagnostics.
TypeSymbol returnType = hasErrors ? CreateErrorType() : lhsTuple.Type; | ||
|
||
uint leftEscape = GetBroadestValEscape(lhsTuple, this.LocalScopeDepth); | ||
boundRHS = ValidateEscape(boundRHS, leftEscape, isByRef: false, diagnostics: diagnostics); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the future we may want to do this pair-wise each value to each assignment target.
For now, since in all reachable cases the assignment values will have the same escape which is equal to escape of RHS, this is sufficient.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gafter Please review when you have the chance. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
test windows_debug_unit32_prtest please |
1 similar comment
test windows_debug_unit32_prtest please |
* dotnet/master: Escape rules for deconstruction and foreach variables (dotnet#22354) Address review feedback Address PR feedback Add IOperation unit tests for try/catch statements and make the APIs public again
Customer scenario
Use ref-like types (such as
Span
) in a deconstruction or foreach loop. The assignments implicit in those operations need to be tracked to avoid holding on to references past their scope.Bugs this fixes:
Fixes #22338
Details of the fix
Workarounds, if any
No workaround. This is a safety rule the compiler should enforce.
Risk
Performance impact
Low. This is mostly adding a check to binding deconstructions and foreach.
Is this a regression from a previous update?
No. This is part of a C# 7.2 feature (part of 15.5 release).