-
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
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
5943daf
Escape rules for deconstruction and foreach variables
462c0e2
Tweaks
jcouv 952792a
Tweaks
jcouv a135d99
Report constraint errors on deconstruction of typeless tuple
5ec929b
Pre-emptively implement val escape logic for tuples
jcouv 68d0b49
Always make the merged tuple type for tuple deconstructions
jcouv File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,7 +5,6 @@ | |
using System.Collections.Generic; | ||
using System.Diagnostics; | ||
using System.Linq; | ||
using Microsoft.CodeAnalysis.Collections; | ||
using Microsoft.CodeAnalysis.CSharp.Symbols; | ||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
using Microsoft.CodeAnalysis.PooledObjects; | ||
|
@@ -95,13 +94,15 @@ internal BoundExpression BindDeconstruction(AssignmentExpressionSyntax node, Dia | |
DeconstructionVariable locals = BindDeconstructionVariables(left, diagnostics, ref declaration, ref expression); | ||
Debug.Assert(locals.HasNestedVariables); | ||
|
||
BoundExpression boundRight = rightPlaceholder ?? BindValue(right, diagnostics, BindValueKind.RValue); | ||
boundRight = FixTupleLiteral(locals.NestedVariables, boundRight, deconstruction, diagnostics); | ||
var deconstructionDiagnostics = new DiagnosticBag(); | ||
BoundExpression boundRight = rightPlaceholder ?? BindValue(right, deconstructionDiagnostics, BindValueKind.RValue); | ||
boundRight = FixTupleLiteral(locals.NestedVariables, boundRight, deconstruction, deconstructionDiagnostics); | ||
|
||
bool resultIsUsed = resultIsUsedOverride || IsDeconstructionResultUsed(left); | ||
var assignment = BindDeconstructionAssignment(deconstruction, left, boundRight, locals.NestedVariables, resultIsUsed, diagnostics); | ||
var assignment = BindDeconstructionAssignment(deconstruction, left, boundRight, locals.NestedVariables, resultIsUsed, deconstructionDiagnostics); | ||
DeconstructionVariable.FreeDeconstructionVariables(locals.NestedVariables); | ||
|
||
diagnostics.AddRange(deconstructionDiagnostics); | ||
return assignment; | ||
} | ||
|
||
|
@@ -113,10 +114,12 @@ internal BoundExpression BindDeconstruction(AssignmentExpressionSyntax node, Dia | |
bool resultIsUsed, | ||
DiagnosticBag diagnostics) | ||
{ | ||
uint rightEscape = GetValEscape(boundRHS, this.LocalScopeDepth); | ||
|
||
if ((object)boundRHS.Type == null || boundRHS.Type.IsErrorType()) | ||
{ | ||
// we could still not infer a type for the RHS | ||
FailRemainingInferences(checkedVariables, diagnostics); | ||
FailRemainingInferencesAndSetValEscape(checkedVariables, diagnostics, rightEscape); | ||
var voidType = GetSpecialType(SpecialType.System_Void, diagnostics, node); | ||
|
||
var type = boundRHS.Type ?? voidType; | ||
|
@@ -139,11 +142,14 @@ internal BoundExpression BindDeconstruction(AssignmentExpressionSyntax node, Dia | |
checkedVariables, | ||
out conversion); | ||
|
||
FailRemainingInferences(checkedVariables, diagnostics); | ||
FailRemainingInferencesAndSetValEscape(checkedVariables, diagnostics, rightEscape); | ||
|
||
var lhsTuple = DeconstructionVariablesAsTuple(left, checkedVariables, diagnostics, ignoreDiagnosticsFromTuple: hasErrors || !resultIsUsed); | ||
var lhsTuple = DeconstructionVariablesAsTuple(left, checkedVariables, diagnostics, ignoreDiagnosticsFromTuple: diagnostics.HasAnyErrors() || !resultIsUsed); | ||
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 commentThe 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. |
||
|
||
var boundConversion = new BoundConversion( | ||
boundRHS.Syntax, | ||
boundRHS, | ||
|
@@ -197,7 +203,10 @@ private BoundExpression FixTupleLiteral(ArrayBuilder<DeconstructionVariable> che | |
// Let's fix the literal up by figuring out its type | ||
// For declarations, that means merging type information from the LHS and RHS | ||
// For assignments, only the LHS side matters since it is necessarily typed | ||
TypeSymbol mergedTupleType = MakeMergedTupleType(checkedVariables, (BoundTupleLiteral)boundRHS, syntax, Compilation, diagnostics); | ||
|
||
// If we already have diagnostics at this point, it is not worth collecting likely duplicate diagnostics from making the merged type | ||
bool hadErrors = diagnostics.HasAnyErrors(); | ||
TypeSymbol mergedTupleType = MakeMergedTupleType(checkedVariables, (BoundTupleLiteral)boundRHS, syntax, Compilation, hadErrors ? null : diagnostics); | ||
if ((object)mergedTupleType != null) | ||
{ | ||
boundRHS = GenerateConversionForAssignment(mergedTupleType, boundRHS, diagnostics); | ||
|
@@ -248,7 +257,7 @@ private BoundExpression FixTupleLiteral(ArrayBuilder<DeconstructionVariable> che | |
else | ||
{ | ||
ImmutableArray<BoundDeconstructValuePlaceholder> outPlaceholders; | ||
var inputPlaceholder = new BoundDeconstructValuePlaceholder(syntax, type); | ||
var inputPlaceholder = new BoundDeconstructValuePlaceholder(syntax, this.LocalScopeDepth, type); | ||
var deconstructInvocation = MakeDeconstructInvocationExpression(variables.Count, | ||
inputPlaceholder, rightSyntax, diagnostics, out outPlaceholders); | ||
|
||
|
@@ -345,24 +354,33 @@ private BoundExpression SetInferredType(BoundExpression expression, TypeSymbol t | |
|
||
/// <summary> | ||
/// Find any deconstruction locals that are still pending inference and fail their inference. | ||
/// Set the safe-to-escape scope for all deconstruction locals. | ||
/// </summary> | ||
private void FailRemainingInferences(ArrayBuilder<DeconstructionVariable> variables, DiagnosticBag diagnostics) | ||
private void FailRemainingInferencesAndSetValEscape(ArrayBuilder<DeconstructionVariable> variables, DiagnosticBag diagnostics, | ||
uint rhsValEscape) | ||
{ | ||
int count = variables.Count; | ||
for (int i = 0; i < count; i++) | ||
{ | ||
var variable = variables[i]; | ||
if (variable.HasNestedVariables) | ||
{ | ||
FailRemainingInferences(variable.NestedVariables, diagnostics); | ||
FailRemainingInferencesAndSetValEscape(variable.NestedVariables, diagnostics, rhsValEscape); | ||
} | ||
else | ||
{ | ||
switch (variable.Single.Kind) | ||
{ | ||
case BoundKind.Local: | ||
var local = (BoundLocal)variable.Single; | ||
if (local.IsDeclaration) | ||
{ | ||
((SourceLocalSymbol)local.LocalSymbol).SetValEscape(rhsValEscape); | ||
} | ||
break; | ||
case BoundKind.DeconstructionVariablePendingInference: | ||
BoundExpression local = ((DeconstructionVariablePendingInference)variable.Single).FailInference(this, diagnostics); | ||
variables[i] = new DeconstructionVariable(local, local.Syntax); | ||
BoundExpression errorLocal = ((DeconstructionVariablePendingInference)variable.Single).FailInference(this, diagnostics); | ||
variables[i] = new DeconstructionVariable(errorLocal, errorLocal.Syntax); | ||
break; | ||
case BoundKind.DiscardExpression: | ||
var pending = (BoundDiscardExpression)variable.Single; | ||
|
@@ -439,6 +457,7 @@ private static TypeSymbol MakeMergedTupleType(ArrayBuilder<DeconstructionVariabl | |
int rightLength = rhsLiteral.Arguments.Length; | ||
|
||
var typesBuilder = ArrayBuilder<TypeSymbol>.GetInstance(leftLength); | ||
var locationsBuilder = ArrayBuilder<Location>.GetInstance(leftLength); | ||
for (int i = 0; i < rightLength; i++) | ||
{ | ||
BoundExpression element = rhsLiteral.Arguments[i]; | ||
|
@@ -479,29 +498,29 @@ private static TypeSymbol MakeMergedTupleType(ArrayBuilder<DeconstructionVariabl | |
} | ||
|
||
typesBuilder.Add(mergedType); | ||
locationsBuilder.Add(element.Syntax.Location); | ||
} | ||
|
||
if (typesBuilder.Any(t => t == null)) | ||
{ | ||
typesBuilder.Free(); | ||
locationsBuilder.Free(); | ||
return null; | ||
} | ||
|
||
// The tuple created here is not identical to the one created by | ||
// DeconstructionVariablesAsTuple. It represents a smaller | ||
// tree of types used for figuring out natural types in tuple literal. | ||
// Therefore, we do not check constraints here as it would report errors | ||
// that are already reported later. DeconstructionVariablesAsTuple | ||
// constructs the final tuple type and checks constraints. | ||
return TupleTypeSymbol.Create( | ||
locationOpt: null, | ||
elementTypes: typesBuilder.ToImmutableAndFree(), | ||
elementLocations: default(ImmutableArray<Location>), | ||
elementLocations: locationsBuilder.ToImmutableAndFree(), | ||
elementNames: default(ImmutableArray<string>), | ||
compilation: compilation, | ||
diagnostics: diagnostics, | ||
shouldCheckConstraints: false, | ||
errorPositions: default(ImmutableArray<bool>)); | ||
shouldCheckConstraints: true, | ||
errorPositions: default(ImmutableArray<bool>), | ||
syntax: syntax); | ||
} | ||
|
||
private BoundTupleLiteral DeconstructionVariablesAsTuple(CSharpSyntaxNode syntax, ArrayBuilder<DeconstructionVariable> variables, | ||
|
@@ -672,7 +691,7 @@ private static string ExtractDeconstructResultElementName(BoundExpression expres | |
out ImmutableArray<BoundDeconstructValuePlaceholder> outPlaceholders, BoundExpression childNode) | ||
{ | ||
Error(diagnostics, ErrorCode.ERR_MissingDeconstruct, rightSyntax, receiver.Type, numParameters); | ||
outPlaceholders = default(ImmutableArray<BoundDeconstructValuePlaceholder>); | ||
outPlaceholders = default; | ||
|
||
return BadExpression(rightSyntax, childNode); | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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 would expect this test to be part of the switch in
GetValEscape
. It seems out of place here. #ResolvedThere 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)