-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Share pattern-matching temps when they are the same type. #19378
Changes from 3 commits
4eb82b1
11f360d
e088442
300a47e
c63b016
716d6df
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,17 +26,63 @@ internal abstract class DecisionTreeBuilder | |
protected readonly Symbol _enclosingSymbol; | ||
protected readonly Conversions _conversions; | ||
protected HashSet<DiagnosticInfo> _useSiteDiagnostics = new HashSet<DiagnosticInfo>(); | ||
private Dictionary<TypeSymbol, LocalSymbol> localByType = new Dictionary<TypeSymbol, LocalSymbol>(); | ||
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.
Should this dictionary be sensitive to dynamic/object, tuple names and custom modifier differences? 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. No, it definitely should not. 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.
Please add a test targeting this scenario. 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. Added 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. My "No" above should be "Yes". |
||
|
||
protected DecisionTreeBuilder( | ||
Symbol enclosingSymbol, | ||
SyntaxNode syntax, | ||
Conversions conversions) | ||
{ | ||
this._enclosingSymbol = enclosingSymbol; | ||
this.Syntax = syntax; | ||
this._conversions = conversions; | ||
} | ||
|
||
protected SyntaxNode Syntax { private get; set; } | ||
|
||
private LocalSymbol PatternMatchingTemp(TypeSymbol type) | ||
{ | ||
LocalSymbol temp; | ||
if (!localByType.TryGetValue(type, out temp)) | ||
{ | ||
temp = new SynthesizedLocal(_enclosingSymbol as MethodSymbol, type, SynthesizedLocalKind.PatternMatching, Syntax); | ||
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.
Can this be null? If not use explicit cast. 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. Yes, the parameter is "optional" in the constructor. While it would be possible to change this to a cast now, that would be an error when we introduce a match expression, because in that case the enclosing symbol could be a field (for a field initializer). I'd rather leave this as is so it is future-proof. |
||
localByType.Add(type, temp); | ||
} | ||
|
||
return temp; | ||
} | ||
|
||
/// <summary> | ||
/// Create a fresh decision tree for the given input expression of the given type. | ||
/// </summary> | ||
protected DecisionTree CreateEmptyDecisionTree(BoundExpression expression) | ||
{ | ||
var type = expression.Type; | ||
|
||
LocalSymbol temp = null; | ||
if (expression.ConstantValue == null) | ||
{ | ||
// Unless it is a constant, the decision tree acts on a copy of the input expression. | ||
// We create a temp to represent that copy. Lowering will assign into this temp. | ||
temp = PatternMatchingTemp(type); | ||
expression = new BoundLocal(expression.Syntax, temp, null, type); | ||
} | ||
|
||
if (type.CanContainNull() || type.SpecialType == SpecialType.None) | ||
{ | ||
// We need the ByType decision tree to separate null from non-null values. | ||
// Note that, for the purpose of the decision tree (and subsumption), we | ||
// ignore the fact that the input may be a constant, and therefore always | ||
// or never null. | ||
return new DecisionTree.ByType(expression, type, temp); | ||
} | ||
else | ||
{ | ||
// If it is a (e.g. builtin) value type, we can switch on its (constant) values. | ||
return new DecisionTree.ByValue(expression, type, temp); | ||
} | ||
} | ||
|
||
protected DecisionTree AddToDecisionTree(DecisionTree decisionTree, SyntaxNode sectionSyntax, BoundPatternSwitchLabel label) | ||
{ | ||
var pattern = label.Pattern; | ||
|
@@ -237,7 +283,7 @@ private DecisionTree AddByValue(DecisionTree.ByType byType, BoundConstantPattern | |
if (forType == null) | ||
{ | ||
var type = value.Value.Type; | ||
var localSymbol = new SynthesizedLocal(_enclosingSymbol as MethodSymbol, type, SynthesizedLocalKind.PatternMatching, Syntax, false, RefKind.None); | ||
var localSymbol = PatternMatchingTemp(type); | ||
var narrowedExpression = new BoundLocal(Syntax, localSymbol, null, type); | ||
forType = new DecisionTree.ByValue(narrowedExpression, value.Value.Type.TupleUnderlyingTypeOrSelf(), localSymbol); | ||
byType.TypeAndDecision.Add(new KeyValuePair<TypeSymbol, DecisionTree>(value.Value.Type, forType)); | ||
|
@@ -338,7 +384,7 @@ private DecisionTree AddByType(DecisionTree.ByType byType, TypeSymbol type, Deci | |
|
||
if (result == null) | ||
{ | ||
var localSymbol = new SynthesizedLocal(_enclosingSymbol as MethodSymbol, type, SynthesizedLocalKind.PatternMatching, Syntax, false, RefKind.None); | ||
var localSymbol = PatternMatchingTemp(type); | ||
var expression = new BoundLocal(Syntax, localSymbol, null, type); | ||
result = makeDecision(expression, type); | ||
Debug.Assert(result.Temp == null); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,7 +38,7 @@ private class PatternSwitchLocalRewriter : DecisionTreeBuilder | |
private ArrayBuilder<BoundStatement> _loweredDecisionTree = ArrayBuilder<BoundStatement>.GetInstance(); | ||
|
||
private PatternSwitchLocalRewriter(LocalRewriter localRewriter, BoundPatternSwitchStatement node) | ||
: base(localRewriter._factory.CurrentMethod, localRewriter._factory.Compilation.Conversions) | ||
: base(localRewriter._factory.CurrentMethod, node.Syntax, localRewriter._factory.Compilation.Conversions) | ||
{ | ||
this._localRewriter = localRewriter; | ||
this._factory = localRewriter._factory; | ||
|
@@ -59,15 +59,9 @@ private BoundStatement MakeLoweredForm(BoundPatternSwitchStatement node) | |
var expression = _localRewriter.VisitExpression(node.Expression); | ||
var result = ArrayBuilder<BoundStatement>.GetInstance(); | ||
|
||
// if the expression is "too complex", we copy it to a temp. | ||
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.
Could you please explain why this is no longer needed? 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. Lowering starts by lowering to a decision tree, which starts by calling /// <summary>
/// Create a fresh decision tree for the given input expression of the given type.
/// </summary>
protected DecisionTree CreateEmptyDecisionTree(BoundExpression expression)
{
var type = expression.Type;
LocalSymbol temp = null;
if (expression.ConstantValue == null)
{
// Unless it is a constant, the decision tree acts on a copy of the input expression.
// We create a temp to represent that copy. Lowering will assign into this temp.
temp = PatternMatchingTemp(type);
expression = new BoundLocal(expression.Syntax, temp, null, type);
} 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. Thanks for the explanation. |
||
LocalSymbol initialTemp = null; | ||
if (expression.ConstantValue == null) | ||
{ | ||
initialTemp = _factory.SynthesizedLocal(expression.Type, expression.Syntax); | ||
result.Add(_factory.Assignment(_factory.Local(initialTemp), expression)); | ||
expression = _factory.Local(initialTemp); | ||
|
||
// EnC: We need to insert a hidden sequence point to handle function remapping in case | ||
// EnC: We need to insert a hidden sequence point to handle function remapping in case | ||
// the containing method is edited while methods invoked in the expression are being executed. | ||
if (!node.WasCompilerGenerated && _localRewriter.Instrument) | ||
{ | ||
|
@@ -85,7 +79,6 @@ private BoundStatement MakeLoweredForm(BoundPatternSwitchStatement node) | |
} | ||
// at this point the end of result is unreachable. | ||
|
||
_declaredTemps.AddOptional(initialTemp); | ||
_declaredTemps.AddRange(node.InnerLocals); | ||
|
||
// output the sections of code | ||
|
@@ -148,7 +141,7 @@ private DecisionTree LowerToDecisionTree( | |
BoundExpression loweredExpression, | ||
BoundPatternSwitchStatement node) | ||
{ | ||
var loweredDecisionTree = DecisionTree.Create(loweredExpression, loweredExpression.Type, _enclosingSymbol); | ||
var loweredDecisionTree = CreateEmptyDecisionTree(loweredExpression); | ||
BoundPatternSwitchLabel defaultLabel = null; | ||
SyntaxNode defaultSection = null; | ||
foreach (var section in node.SwitchSections) | ||
|
@@ -223,15 +216,11 @@ private void LowerDecisionTree(BoundExpression expression, DecisionTree decision | |
_loweredDecisionTree.Add(_factory.Assignment(decisionTree.Expression, convertedExpression)); | ||
} | ||
|
||
// If the temp is not yet in the declared temp set, add it now | ||
if (_declaredTempSet.Add(decisionTree.Temp)) | ||
{ | ||
_declaredTemps.Add(decisionTree.Temp); | ||
} | ||
else | ||
{ | ||
// we should only attempt to declare each temp once. | ||
throw ExceptionUtilities.Unreachable; | ||
} | ||
} | ||
|
||
switch (decisionTree.Kind) | ||
|
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.
It looks like there is only one call-site. Consider making this a SwitchStatementSyntax and removing the two next parameters (boundSwitchExpression is coming from a property, sections are available from SwitchStatementSyntax). #Closed
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.
Yes; even this
syntax
parameter is unneeded (it is available in the propertySwitchSyntax
).