Skip to content

Commit

Permalink
Associate synthesized pattern matching switch case variables wiht swi…
Browse files Browse the repository at this point in the history
…tch stmt
  • Loading branch information
tmat committed May 19, 2017
1 parent 41cdec1 commit 2bdd344
Show file tree
Hide file tree
Showing 8 changed files with 339 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

using System.Diagnostics;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.CSharp
{

/// <summary>
/// Helper class for binding the pattern switch statement. It helps compute which labels
/// are subsumed and/or reachable. The strategy, implemented in <see cref="PatternSwitchBinder"/>,
Expand All @@ -15,12 +15,12 @@ namespace Microsoft.CodeAnalysis.CSharp
/// If it is not subsumed and there is no guard expression, we then add it to the decision
/// tree.
/// </summary>
internal class SubsumptionDiagnosticBuilder : DecisionTreeBuilder
internal sealed class SubsumptionDiagnosticBuilder : DecisionTreeBuilder
{
private readonly DecisionTree _subsumptionTree;

internal SubsumptionDiagnosticBuilder(Symbol enclosingSymbol,
SyntaxNode syntax,
SwitchStatementSyntax syntax,
Conversions conversions,
BoundExpression expression)
: base(enclosingSymbol, syntax, conversions)
Expand Down Expand Up @@ -52,7 +52,6 @@ internal bool AddLabel(BoundPatternSwitchLabel label, DiagnosticBag diagnostics,
{
// For purposes of subsumption, we do not take into consideration the value
// of the input expression. Therefore we consider null possible if the type permits.
Syntax = label.Syntax;
var inputCouldBeNull = _subsumptionTree.Type.CanContainNull();
var subsumedErrorCode = CheckSubsumed(label.Pattern, _subsumptionTree, inputCouldBeNull: inputCouldBeNull);
if (subsumedErrorCode != 0 && subsumedErrorCode != ErrorCode.ERR_NoImplicitConvCast)
Expand Down
50 changes: 26 additions & 24 deletions src/Compilers/CSharp/Portable/BoundTree/DecisionTreeBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Roslyn.Utilities;

Expand All @@ -26,30 +27,32 @@ internal abstract class DecisionTreeBuilder
protected readonly Symbol _enclosingSymbol;
protected readonly Conversions _conversions;
protected HashSet<DiagnosticInfo> _useSiteDiagnostics = new HashSet<DiagnosticInfo>();
protected readonly SwitchStatementSyntax _switchSyntax;
private Dictionary<TypeSymbol, LocalSymbol> localByType = new Dictionary<TypeSymbol, LocalSymbol>();

protected DecisionTreeBuilder(
Symbol enclosingSymbol,
SyntaxNode syntax,
SwitchStatementSyntax switchSyntax,
Conversions conversions)
{
this._enclosingSymbol = enclosingSymbol;
this.Syntax = syntax;
this._conversions = conversions;
_enclosingSymbol = enclosingSymbol;
_switchSyntax = switchSyntax;
_conversions = conversions;
}

protected SyntaxNode Syntax { private get; set; }

private LocalSymbol PatternMatchingTemp(TypeSymbol type)
private BoundLocal GetBoundPatternMatchingLocal(TypeSymbol type)
{
LocalSymbol temp;
if (!localByType.TryGetValue(type, out temp))
// All synthesized pattern matching locals are associated with the Switch statement syntax node.
// Their ordinals are zero.
// EnC local slot variable matching logic find the right slot based on the type of the local.

if (!localByType.TryGetValue(type, out var localSymbol))
{
temp = new SynthesizedLocal(_enclosingSymbol as MethodSymbol, type, SynthesizedLocalKind.PatternMatching, Syntax);
localByType.Add(type, temp);
localSymbol = new SynthesizedLocal(_enclosingSymbol as MethodSymbol, type, SynthesizedLocalKind.SwitchCasePatternMatching, _switchSyntax);
localByType.Add(type, localSymbol);
}

return temp;
return new BoundLocal(_switchSyntax, localSymbol, null, type);
}

/// <summary>
Expand All @@ -59,13 +62,14 @@ protected DecisionTree CreateEmptyDecisionTree(BoundExpression expression)
{
var type = expression.Type;

LocalSymbol temp = null;
LocalSymbol localSymbol = 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);
var local = GetBoundPatternMatchingLocal(type);
expression = local;
localSymbol = local.LocalSymbol;
}

if (type.CanContainNull() || type.SpecialType == SpecialType.None)
Expand All @@ -74,12 +78,12 @@ protected DecisionTree CreateEmptyDecisionTree(BoundExpression expression)
// 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);
return new DecisionTree.ByType(expression, type, localSymbol);
}
else
{
// If it is a (e.g. builtin) value type, we can switch on its (constant) values.
return new DecisionTree.ByValue(expression, type, temp);
return new DecisionTree.ByValue(expression, type, localSymbol);
}
}

Expand Down Expand Up @@ -283,10 +287,9 @@ private DecisionTree AddByValue(DecisionTree.ByType byType, BoundConstantPattern
if (forType == null)
{
var type = value.Value.Type;
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));
var narrowedExpression = GetBoundPatternMatchingLocal(type);
forType = new DecisionTree.ByValue(narrowedExpression, type.TupleUnderlyingTypeOrSelf(), narrowedExpression.LocalSymbol);
byType.TypeAndDecision.Add(new KeyValuePair<TypeSymbol, DecisionTree>(type, forType));
}

return AddByValue(forType, value, makeDecision);
Expand Down Expand Up @@ -384,11 +387,10 @@ private DecisionTree AddByType(DecisionTree.ByType byType, TypeSymbol type, Deci

if (result == null)
{
var localSymbol = PatternMatchingTemp(type);
var expression = new BoundLocal(Syntax, localSymbol, null, type);
var expression = GetBoundPatternMatchingLocal(type);
result = makeDecision(expression, type);
Debug.Assert(result.Temp == null);
result.Temp = localSymbol;
result.Temp = expression.LocalSymbol;
byType.TypeAndDecision.Add(new KeyValuePair<TypeSymbol, DecisionTree>(type, result));
}

Expand Down
6 changes: 5 additions & 1 deletion src/Compilers/CSharp/Portable/CodeGen/EmitStatement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1535,7 +1535,11 @@ private string GetLocalDebugName(ILocalSymbolInternal local, out LocalDebugId lo
var syntax = local.GetDeclaratorSyntax();
int syntaxOffset = _method.CalculateLocalSyntaxOffset(syntax.SpanStart, syntax.SyntaxTree);

int ordinal = _synthesizedLocalOrdinals.AssignLocalOrdinal(localKind, syntaxOffset);
// Synthesized locals emitted for switch case patterns are all associated with the switch statement
// and have distinct types. We use theier types to match them, not the ordinal as the ordinal might
// change if switch cases are reordered.
int ordinal = (localKind != SynthesizedLocalKind.SwitchCasePatternMatching) ?
_synthesizedLocalOrdinals.AssignLocalOrdinal(localKind, syntaxOffset) : 0;

// user-defined locals should have 0 ordinal:
Debug.Assert(ordinal == 0 || localKind != SynthesizedLocalKind.UserDefined);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.CSharp
Expand Down Expand Up @@ -38,14 +39,14 @@ private class PatternSwitchLocalRewriter : DecisionTreeBuilder
private ArrayBuilder<BoundStatement> _loweredDecisionTree = ArrayBuilder<BoundStatement>.GetInstance();

private PatternSwitchLocalRewriter(LocalRewriter localRewriter, BoundPatternSwitchStatement node)
: base(localRewriter._factory.CurrentMethod, node.Syntax, localRewriter._factory.Compilation.Conversions)
: base(localRewriter._factory.CurrentMethod, (SwitchStatementSyntax)node.Syntax, localRewriter._factory.Compilation.Conversions)
{
this._localRewriter = localRewriter;
this._factory = localRewriter._factory;
this._factory.Syntax = node.Syntax;
foreach (var section in node.SwitchSections)
{
_switchSections.Add((SyntaxNode)section.Syntax, ArrayBuilder<BoundStatement>.GetInstance());
_switchSections.Add(section.Syntax, ArrayBuilder<BoundStatement>.GetInstance());
}
}

Expand Down Expand Up @@ -146,7 +147,7 @@ private DecisionTree LowerToDecisionTree(
SyntaxNode defaultSection = null;
foreach (var section in node.SwitchSections)
{
var sectionSyntax = (SyntaxNode)section.Syntax;
var sectionSyntax = section.Syntax;
foreach (var label in section.SwitchLabels)
{
var loweredLabel = LowerSwitchLabel(label);
Expand All @@ -164,7 +165,6 @@ private DecisionTree LowerToDecisionTree(
}
else
{
Syntax = label.Syntax;
AddToDecisionTree(loweredDecisionTree, sectionSyntax, loweredLabel);
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Test/Emit/CodeGen/SwitchTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9433,7 +9433,7 @@ .locals init (object V_0,
<forward declaringType=""Program"" methodName=""Main"" />
<encLocalSlotMap>
<slot kind=""35"" offset=""11"" />
<slot kind=""35"" offset=""46"" />
<slot kind=""35"" offset=""11"" />
<slot kind=""0"" offset=""55"" />
<slot kind=""0"" offset=""133"" />
<slot kind=""0"" offset=""211"" />
Expand Down
Loading

0 comments on commit 2bdd344

Please sign in to comment.