提交 cec28aa3 编写于 作者: N Neal Gafter

Refactoring in prep for tuple literal switch optimization

上级 ab7a2446
......@@ -6,6 +6,11 @@ namespace Microsoft.CodeAnalysis.CSharp
{
partial class BoundDagTemp
{
/// <summary>
/// Does this dag temp represent the original input of the pattern-matching operation?
/// </summary>
public bool IsOriginalInput => this.Source == null;
public override bool Equals(object obj) => obj is BoundDagTemp other && this.Equals(other);
public bool Equals(BoundDagTemp other)
{
......
......@@ -156,7 +156,7 @@ BoundDecisionDagNode makeReplacement(BoundDecisionDagNode dag)
// Is the decision's result known because the input is a constant?
bool? knownResult(BoundDagTest choice)
{
if (choice.Input.Source != null)
if (!choice.Input.IsOriginalInput)
{
// This is a test of something other than the main input; result unknown
return null;
......
......@@ -24,57 +24,44 @@ private class BasePatternSwitchLocalRewriter : PatternLocalRewriter
/// includes the code to assign to the pattern variables and evaluate the when clause. Since a
/// when clause can yield a false value, it can jump back to a label in the lowered decision dag.
/// </summary>
protected readonly Dictionary<SyntaxNode, ArrayBuilder<BoundStatement>> _switchArms = new Dictionary<SyntaxNode, ArrayBuilder<BoundStatement>>();
/// <summary>
/// In a switch expression, some labels may first reached by a backward branch, and
/// it may occur when something (from the enclosing expression) is on the stack.
/// To satisfy the verifier, the caller must arrange forward jumps to these labels. The set of
/// states whose labels will need such forward jumps (if something can be on the stack) is stored in
/// _backwardLabels. In practice, this is exclusively the set of states that are reached
/// when a when-clause evaluates to false.
/// PROTOTYPE(patterns2): This is a placeholder. It is not used yet for lowering the
/// switch expression, where it will be needed.
/// </summary>
private readonly ArrayBuilder<BoundDecisionDagNode> _backwardLabels = ArrayBuilder<BoundDecisionDagNode>.GetInstance();
private readonly PooledDictionary<SyntaxNode, ArrayBuilder<BoundStatement>> _switchArms = PooledDictionary<SyntaxNode, ArrayBuilder<BoundStatement>>.GetInstance();
/// <summary>
/// The lowered decision dag. This includes all of the code to decide which pattern
/// is matched, but not the code to assign to pattern variables and evaluate when clauses.
/// </summary>
protected readonly ArrayBuilder<BoundStatement> _loweredDecisionDag = ArrayBuilder<BoundStatement>.GetInstance();
private readonly ArrayBuilder<BoundStatement> _loweredDecisionDag = ArrayBuilder<BoundStatement>.GetInstance();
/// <summary>
/// The label in the code for the beginning of code for each node of the dag.
/// </summary>
private readonly Dictionary<BoundDecisionDagNode, LabelSymbol> _dagNodeLabels = new Dictionary<BoundDecisionDagNode, LabelSymbol>();
private readonly PooledDictionary<BoundDecisionDagNode, LabelSymbol> _dagNodeLabels = PooledDictionary<BoundDecisionDagNode, LabelSymbol>.GetInstance();
protected BasePatternSwitchLocalRewriter(LocalRewriter localRewriter, BoundExpression loweredInput, ImmutableArray<SyntaxNode> arms, BoundDecisionDag decisionDag)
: base(localRewriter, loweredInput)
/// <summary>
/// True if we are translating a switch statement. This affects sequence points (a when clause gets
/// a sequence point in a switch statement, but not in a switch expression).
/// </summary>
private readonly bool _isSwitchStatement;
protected BasePatternSwitchLocalRewriter(
SyntaxNode node,
LocalRewriter localRewriter,
ImmutableArray<SyntaxNode> arms,
bool isSwitchStatement)
: base(node, localRewriter)
{
this._isSwitchStatement = isSwitchStatement;
foreach (var arm in arms)
{
_switchArms.Add(arm, new ArrayBuilder<BoundStatement>());
_switchArms.Add(arm, ArrayBuilder<BoundStatement>.GetInstance());
}
ImmutableArray<BoundDecisionDagNode> sortedNodes = decisionDag.TopologicallySortedNodes;
ComputeLabelSet(sortedNodes);
LowerDecisionDag(sortedNodes);
}
private void ComputeLabelSet(ImmutableArray<BoundDecisionDagNode> sortedNodes)
private void ComputeLabelSet(BoundDecisionDag decisionDag)
{
// Nodes with more than one predecessor are assigned a label
var hasPredecessor = PooledHashSet<BoundDecisionDagNode>.GetInstance();
void notePredecessor(BoundDecisionDagNode successor)
{
if (successor != null && !hasPredecessor.Add(successor))
{
GetDagNodeLabel(successor);
}
}
foreach (BoundDecisionDagNode node in sortedNodes)
foreach (BoundDecisionDagNode node in decisionDag.TopologicallySortedNodes)
{
switch (node)
{
......@@ -83,7 +70,6 @@ void notePredecessor(BoundDecisionDagNode successor)
if (w.WhenFalse != null)
{
GetDagNodeLabel(w.WhenFalse);
_backwardLabels.Add(w.WhenFalse);
}
break;
case BoundLeafDecisionDagNode d:
......@@ -103,13 +89,22 @@ void notePredecessor(BoundDecisionDagNode successor)
}
hasPredecessor.Free();
return;
void notePredecessor(BoundDecisionDagNode successor)
{
if (successor != null && !hasPredecessor.Add(successor))
{
GetDagNodeLabel(successor);
}
}
}
protected new void Free()
{
_dagNodeLabels.Free();
_switchArms.Free();
base.Free();
_loweredDecisionDag.Free();
_backwardLabels.Free();
}
protected virtual LabelSymbol GetDagNodeLabel(BoundDecisionDagNode dag)
......@@ -122,11 +117,115 @@ protected virtual LabelSymbol GetDagNodeLabel(BoundDecisionDagNode dag)
return label;
}
/// <summary>
/// A utility class that is used to scan a when clause to determine if it might assign a variable,
/// directly or indirectly. Used to determine if we can skip the allocation of pattern-matching
/// temporary variables and use user-declared variables instead, because we can conclude that they
/// are not mutated while the pattern-matching automaton is running.
/// </summary>
protected class WhenClauseMightAssignWalker : BoundTreeWalkerWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator
{
private readonly bool _isSwitchStatement;
private bool _mightAssignSomething;
internal WhenClauseMightAssignWalker(bool isSwitchStatement)
{
this._isSwitchStatement = isSwitchStatement;
}
public bool MightAssignSomething(BoundExpression expr)
{
if (expr == null || expr.ConstantValue != null)
{
return false;
}
this._mightAssignSomething = false;
this.Visit(expr);
return this._mightAssignSomething;
}
public override BoundNode VisitCall(BoundCall node)
{
_mightAssignSomething =
// might be a call to a local function that assigns a pattern temp
_isSwitchStatement && node.Method.MethodKind == MethodKind.LocalFunction ||
// or perhaps we are passing a variable by ref and mutating it that way
!node.ArgumentRefKindsOpt.IsDefault;
return base.VisitCall(node);
}
public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node)
{
_mightAssignSomething = true;
return null;
}
public override BoundNode VisitCompoundAssignmentOperator(BoundCompoundAssignmentOperator node)
{
_mightAssignSomething = true;
return null;
}
public override BoundNode VisitDeconstructionAssignmentOperator(BoundDeconstructionAssignmentOperator node)
{
_mightAssignSomething = true;
return null;
}
public override BoundNode VisitObjectCreationExpression(BoundObjectCreationExpression node)
{
// perhaps we are passing a variable by ref and mutating it that way
_mightAssignSomething = !node.ArgumentRefKindsOpt.IsDefault;
return base.VisitObjectCreationExpression(node);
}
}
protected BoundDecisionDag ShareTempsIfPossibleAndEvaluateInput(
BoundDecisionDag decisionDag,
BoundExpression loweredSwitchGoverningExpression,
ArrayBuilder<BoundStatement> result)
{
// Note that a when-clause can contain an assignment to a
// pattern variable declared in a different when-clause (e.g. in the same section, or
// in a different section via the use of a local function), so we need to analyze all
// of the when clauses to see if they are all simple enough to conclude that they do
// not mutate pattern variables.
var mightAssignWalker = new WhenClauseMightAssignWalker(isSwitchStatement: this._isSwitchStatement);
bool canShareTemps =
!decisionDag.TopologicallySortedNodes
.OfType<BoundWhenDecisionDagNode>()
.Any(w => mightAssignWalker.MightAssignSomething(w.WhenExpression));
if (canShareTemps)
{
return ShareTempsAndEvaluateInput(loweredSwitchGoverningExpression, decisionDag, expr => result.Add(_factory.ExpressionStatement(expr)));
}
else
{
// assign the input expression to its temp.
BoundExpression inputTemp = _tempAllocator.GetTemp(InputTemp(loweredSwitchGoverningExpression));
Debug.Assert(inputTemp != loweredSwitchGoverningExpression);
result.Add(_factory.Assignment(inputTemp, loweredSwitchGoverningExpression));
return decisionDag;
}
}
/// <summary>
/// Lower the given nodes into _loweredDecisionDag.
/// </summary>
private void LowerDecisionDag(ImmutableArray<BoundDecisionDagNode> sortedNodes)
protected (ImmutableArray<BoundStatement> loweredDag, ImmutableDictionary<SyntaxNode, ImmutableArray<BoundStatement>> switchSections) LowerDecisionDag(BoundDecisionDag decisionDag)
{
Debug.Assert(this._loweredDecisionDag.IsEmpty());
ComputeLabelSet(decisionDag);
LowerDecisionDagCore(decisionDag);
ImmutableArray<BoundStatement> loweredDag = this._loweredDecisionDag.ToImmutableAndFree();
ImmutableDictionary<SyntaxNode, ImmutableArray<BoundStatement>> switchSections = this._switchArms.ToImmutableDictionary(kv => kv.Key, kv => kv.Value.ToImmutableAndFree());
return (loweredDag, switchSections);
}
private void LowerDecisionDagCore(BoundDecisionDag decisionDag)
{
ImmutableArray<BoundDecisionDagNode> sortedNodes = decisionDag.TopologicallySortedNodes;
var firstNode = sortedNodes[0];
switch (firstNode)
{
......@@ -138,16 +237,6 @@ private void LowerDecisionDag(ImmutableArray<BoundDecisionDagNode> sortedNodes)
break;
}
// Note that a when-clause can contain an assignment to a
// pattern variable declared in a different when-clause (e.g. in the same section, or
// in a different section via the use of a local function), so we only apply the optimization
// of using user-declared pattern variables as pattern-matching automaton temps
// when there are no nontrivial when-clauses at all.
if (!sortedNodes.Any(n => n is BoundWhenDecisionDagNode w && w.WhenExpression != null && w.WhenExpression.ConstantValue != ConstantValue.True))
{
ShareTemps(sortedNodes);
}
// Code for each when clause goes in the separate code section for its switch section.
foreach (BoundDecisionDagNode node in sortedNodes)
{
......@@ -164,6 +253,7 @@ private void LowerDecisionDag(ImmutableArray<BoundDecisionDagNode> sortedNodes)
BoundDecisionDagNode node = nodesToLower[i];
if (loweredNodes.Contains(node))
{
Debug.Assert(!_dagNodeLabels.TryGetValue(node, out _));
continue;
}
......@@ -239,6 +329,7 @@ private void LowerDecisionDag(ImmutableArray<BoundDecisionDagNode> sortedNodes)
private void GenerateTest(BoundExpression test, BoundDecisionDagNode whenTrue, BoundDecisionDagNode whenFalse, BoundDecisionDagNode nextNode)
{
// Because we have already "optimized" away tests for a constant switch expression, the test should be nontrivial.
_factory.Syntax = test.Syntax;
Debug.Assert(test != null);
if (nextNode == whenFalse)
......@@ -422,10 +513,9 @@ private void LowerWhenClause(BoundWhenDecisionDagNode whenClause)
if (whenClause.WhenExpression != null && whenClause.WhenExpression.ConstantValue != ConstantValue.True)
{
// PROTOTYPE(patterns2): there should perhaps be a sequence point (for e.g. a breakpoint) on the when clause.
// However, it is not clear that is wanted for the switch expression as that would be a breakpoint where the stack is nonempty.
_factory.Syntax = whenClause.Syntax;
sectionBuilder.Add(_factory.ConditionalGoto(_localRewriter.VisitExpression(whenClause.WhenExpression), trueLabel, jumpIfTrue: true));
Debug.Assert(whenFalse != null);
Debug.Assert(_backwardLabels.Contains(whenFalse));
sectionBuilder.Add(_factory.Goto(GetDagNodeLabel(whenFalse)));
}
else
......@@ -440,6 +530,7 @@ private void LowerWhenClause(BoundWhenDecisionDagNode whenClause)
/// </summary>
private void LowerDecisionDagNode(BoundDecisionDagNode node, BoundDecisionDagNode nextNode)
{
_factory.Syntax = node.Syntax;
switch (node)
{
case BoundEvaluationDecisionDagNode evaluationNode:
......
......@@ -4,6 +4,7 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
......@@ -28,13 +29,6 @@ public static BoundStatement Rewrite(LocalRewriter localRewriter, BoundPatternSw
return result;
}
/// <summary>
/// Map from switch section's syntax to the lowered code for the section. The code for a section
/// includes the code to assign to the pattern variables and evaluate the when clause. Since a
/// when clause can yield a false value, it can jump back to a label in the lowered decision dag.
/// </summary>
private Dictionary<SyntaxNode, ArrayBuilder<BoundStatement>> _switchSections => base._switchArms;
/// <summary>
/// A map from section syntax to the first label in that section.
/// </summary>
......@@ -69,30 +63,24 @@ protected override LabelSymbol GetDagNodeLabel(BoundDecisionDagNode dag)
}
private PatternSwitchLocalRewriter(BoundPatternSwitchStatement node, LocalRewriter localRewriter)
: base(localRewriter, localRewriter.VisitExpression(node.Expression), node.SwitchSections.SelectAsArray(section => section.Syntax), node.DecisionDag)
: base(node.Syntax, localRewriter, node.SwitchSections.SelectAsArray(section => section.Syntax), isSwitchStatement: true)
{
}
private BoundStatement LowerPatternSwitchStatement(BoundPatternSwitchStatement node)
{
var reachableLabels = node.DecisionDag.ReachableLabels;
_factory.Syntax = node.Syntax;
var result = ArrayBuilder<BoundStatement>.GetInstance();
// The set of variables attached to the outer block
var outerVariables = ArrayBuilder<LocalSymbol>.GetInstance();
outerVariables.AddRange(node.InnerLocals);
// 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.
var expression = _loweredInput;
var loweredSwitchGoverningExpression = _localRewriter.VisitExpression(node.Expression);
if (!node.WasCompilerGenerated && _localRewriter.Instrument)
{
var instrumentedExpression = _localRewriter._instrumenter.InstrumentSwitchStatementExpression(node, expression, _factory);
if (expression.ConstantValue == null)
// 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.
var instrumentedExpression = _localRewriter._instrumenter.InstrumentSwitchStatementExpression(node, loweredSwitchGoverningExpression, _factory);
if (loweredSwitchGoverningExpression.ConstantValue == null)
{
expression = instrumentedExpression;
loweredSwitchGoverningExpression = instrumentedExpression;
}
else
{
......@@ -102,20 +90,18 @@ private BoundStatement LowerPatternSwitchStatement(BoundPatternSwitchStatement n
}
}
// Assign the input to a temp
BoundExpression inputTemp = _tempAllocator.GetTemp(base._inputTemp);
if (inputTemp == expression)
{
// In this case we would just be assigning the variable to itself, so we need generate no code.
// This arises due to an optimization by which we use pattern variables as pattern-matching temps.
}
else
{
result.Add(_factory.Assignment(inputTemp, expression));
}
// The set of variables attached to the outer block
outerVariables.AddRange(node.InnerLocals);
// Evaluate the input and set up sharing for dag temps with user variables
BoundDecisionDag decisionDag = ShareTempsIfPossibleAndEvaluateInput(node.DecisionDag, loweredSwitchGoverningExpression, result);
// lower the decision dag.
(ImmutableArray<BoundStatement> loweredDag, ImmutableDictionary<SyntaxNode, ImmutableArray<BoundStatement>> switchSections) =
LowerDecisionDag(decisionDag);
// then add the rest of the lowered dag that references that input
result.Add(_factory.Block(this._loweredDecisionDag.ToImmutable()));
result.Add(_factory.Block(loweredDag));
// A branch to the default label when no switch case matches is included in the
// decision dag, so the code in `result` is unreachable at this point.
......@@ -124,7 +110,8 @@ private BoundStatement LowerPatternSwitchStatement(BoundPatternSwitchStatement n
foreach (BoundPatternSwitchSection section in node.SwitchSections)
{
_factory.Syntax = section.Syntax;
ArrayBuilder<BoundStatement> sectionBuilder = _switchSections[section.Syntax];
var sectionBuilder = ArrayBuilder<BoundStatement>.GetInstance();
sectionBuilder.AddRange(switchSections[section.Syntax]);
foreach (BoundPatternSwitchLabel switchLabel in section.SwitchLabels)
{
sectionBuilder.Add(_factory.Label(switchLabel.Label));
......@@ -147,8 +134,8 @@ private BoundStatement LowerPatternSwitchStatement(BoundPatternSwitchStatement n
}
else
{
// Note the scope of the locals, even though they are included for the purposes of
// closure analysis in the enclosing scope.
// Note the language scope of the locals, even though they are included for the purposes of
// lifetime analysis in the enclosing scope.
result.Add(new BoundScope(section.Syntax, section.Locals, statements));
}
}
......
......@@ -4,6 +4,7 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
......@@ -18,19 +19,14 @@ internal sealed partial class LocalRewriter
private class PatternLocalRewriter
{
protected readonly LocalRewriter _localRewriter;
protected readonly BoundExpression _loweredInput;
protected readonly SyntheticBoundNodeFactory _factory;
protected readonly DagTempAllocator _tempAllocator;
protected readonly BoundDagTemp _inputTemp;
public PatternLocalRewriter(LocalRewriter localRewriter, BoundExpression loweredInput)
public PatternLocalRewriter(SyntaxNode node, LocalRewriter localRewriter)
{
this._loweredInput = loweredInput;
this._localRewriter = localRewriter;
this._factory = localRewriter._factory;
this._factory.Syntax = _loweredInput.Syntax;
this._tempAllocator = new DagTempAllocator(_factory);
this._inputTemp = new BoundDagTemp(loweredInput.Syntax, loweredInput.Type, source: null, index: 0);
this._tempAllocator = new DagTempAllocator(_factory, node);
}
public void Free()
......@@ -38,15 +34,19 @@ public void Free()
_tempAllocator.Free();
}
protected BoundDagTemp InputTemp(BoundExpression expr) => new BoundDagTemp(expr.Syntax, expr.Type, null, 0);
public class DagTempAllocator
{
private readonly SyntheticBoundNodeFactory _factory;
private readonly PooledDictionary<BoundDagTemp, BoundExpression> _map = PooledDictionary<BoundDagTemp, BoundExpression>.GetInstance();
private readonly ArrayBuilder<LocalSymbol> _temps = ArrayBuilder<LocalSymbol>.GetInstance();
private readonly SyntaxNode _node;
public DagTempAllocator(SyntheticBoundNodeFactory factory)
public DagTempAllocator(SyntheticBoundNodeFactory factory, SyntaxNode node)
{
this._factory = factory;
this._node = node;
}
public void Free()
......@@ -59,8 +59,7 @@ public BoundExpression GetTemp(BoundDagTemp dagTemp)
{
if (!_map.TryGetValue(dagTemp, out BoundExpression result))
{
// PROTOTYPE(patterns2): Not sure what temp kind should be used for `is pattern`.
LocalSymbol temp = _factory.SynthesizedLocal(dagTemp.Type, syntax: _factory.Syntax, kind: SynthesizedLocalKind.SwitchCasePatternMatching);
LocalSymbol temp = _factory.SynthesizedLocal(dagTemp.Type, syntax: _node, kind: SynthesizedLocalKind.SwitchCasePatternMatching);
result = _factory.Local(temp);
_map.Add(dagTemp, result);
_temps.Add(temp);
......@@ -204,12 +203,11 @@ void addArg(RefKind refKind, BoundExpression expression)
/// </summary>
protected BoundExpression LowerTest(BoundDagTest test)
{
_factory.Syntax = test.Syntax;
BoundExpression input = _tempAllocator.GetTemp(test.Input);
switch (test)
{
case BoundDagNonNullTest d:
// If the actual input is a constant, the test should have been removed from the decision dag
Debug.Assert(!(d.Input == _inputTemp && _loweredInput.ConstantValue != null));
return _localRewriter.MakeNullCheck(d.Syntax, input, input.Type.IsNullableType() ? BinaryOperatorKind.NullableNullNotEqual : BinaryOperatorKind.NotEqual);
case BoundDagTypeTest d:
......@@ -217,13 +215,9 @@ protected BoundExpression LowerTest(BoundDagTest test)
return _factory.Is(input, d.Type);
case BoundDagNullTest d:
// If the actual input is a constant, the test should have been removed from the decision dag
Debug.Assert(!(d.Input == _inputTemp && _loweredInput.ConstantValue != null));
return _localRewriter.MakeNullCheck(d.Syntax, input, input.Type.IsNullableType() ? BinaryOperatorKind.NullableNullEqual : BinaryOperatorKind.Equal);
case BoundDagValueTest d:
// If the actual input is a constant, the test should have been removed from the decision dag
Debug.Assert(!(d.Input == _inputTemp && _loweredInput.ConstantValue != null));
Debug.Assert(!input.Type.IsNullableType());
return _localRewriter.MakeEqual(_localRewriter.MakeLiteral(d.Syntax, d.Value, input.Type), input);
......@@ -268,26 +262,33 @@ protected BoundExpression LowerTest(BoundDagTest test)
return false;
}
protected void ShareTemps(ImmutableArray<BoundDecisionDagNode> sortedNodes)
/// <summary>
/// Produce assignment of the input expression. This method is also responsible for assigning
/// variables for some pattern-matching temps that can be shared with user variables.
/// </summary>
protected BoundDecisionDag ShareTempsAndEvaluateInput(
BoundExpression loweredInput,
BoundDecisionDag decisionDag,
Action<BoundExpression> addCode)
{
if (_loweredInput.Kind == BoundKind.Local || _loweredInput.Kind == BoundKind.Parameter)
var inputDagTemp = InputTemp(loweredInput);
if (loweredInput.Kind == BoundKind.Local || loweredInput.Kind == BoundKind.Parameter)
{
// If we're switching on a local variable and there is no when clause (checked by the caller),
// we assume the value of the local variable does not change during the execution of the
// decision automaton and we just reuse the local variable when we need the input expression.
// It is possible for this assumption to be violated by a side-effecting Deconstruct that
// modifies the local variable which has been captured in a lambda. Since the language assumes
// that functions called during pattern-matching are idempotent and not side-effecting, we feel
// that functions called by pattern-matching are idempotent and not side-effecting, we feel
// justified in taking this assumption in the compiler too.
_ = _tempAllocator.TrySetTemp(_inputTemp, _loweredInput);
bool tempAssigned = _tempAllocator.TrySetTemp(inputDagTemp, loweredInput);
Debug.Assert(tempAssigned);
}
foreach (BoundDecisionDagNode node in sortedNodes)
foreach (BoundDecisionDagNode node in decisionDag.TopologicallySortedNodes)
{
if (node is BoundWhenDecisionDagNode w)
{
Debug.Assert(w.WhenExpression == null || w.WhenExpression.ConstantValue == ConstantValue.True);
// We share a slot for a user-declared pattern-matching variable with a pattern temp if there
// is no user-written when-clause that could modify the variable before the matching
// automaton is done with it (checked by the caller).
......@@ -301,6 +302,65 @@ protected void ShareTemps(ImmutableArray<BoundDecisionDagNode> sortedNodes)
}
}
}
if (loweredInput.Kind == BoundKind.TupleLiteral &&
!decisionDag.TopologicallySortedNodes.Any(n => !usesOriginalInput(n)) &&
false)
{
// If the switch governing expression is a tuple literal that is not used anywhere,
// (though perhaps its component parts are used), then we can save the component parts
// and assign them into temps (or perhaps user variables) to avoid the creation of
// the tuple altogether.
decisionDag = RewriteTupleSwitch(decisionDag, (BoundTupleLiteral)loweredInput, addCode);
}
else
{
// Otherwise we emit an assignment of the input expression to a temporary variable.
BoundExpression inputTemp = _tempAllocator.GetTemp(inputDagTemp);
if (inputTemp != loweredInput)
{
addCode(_factory.AssignmentExpression(inputTemp, loweredInput));
}
}
return decisionDag;
bool usesOriginalInput(BoundDecisionDagNode node)
{
switch (node)
{
case BoundWhenDecisionDagNode n:
return (n.Bindings.Any(b => b.dagTemp.IsOriginalInput));
case BoundTestDecisionDagNode t:
return t.Test.Input.IsOriginalInput;
case BoundEvaluationDecisionDagNode e:
switch (e.Evaluation)
{
case BoundDagFieldEvaluation f:
// Uses only a tuple element, not the whole input
return f.Input.IsOriginalInput && !f.Field.IsTupleElement();
default:
return e.Evaluation.Input.IsOriginalInput;
}
default:
return false;
}
}
}
/// <summary>
/// We have a decision dag whose input is a tuple literal, and the decision dag does not need the tuple itself.
/// We rewrite the decision dag into one which doesn't touch the tuple, but instead works directly with the
/// values that have been stored in temps. This permits the caller to avoid creation of the tuple object
/// itself. We also emit assignments of the tuple values into their corresponding temps.
/// </summary>
/// <returns>A new decision dag that does not reference the input directly</returns>
private BoundDecisionDag RewriteTupleSwitch(
BoundDecisionDag decisionDag,
BoundTupleLiteral loweredInput,
Action<BoundExpression> addCode)
{
throw new NotImplementedException();
}
}
......@@ -318,8 +378,8 @@ private class IsPatternExpressionLocalRewriter : PatternLocalRewriter
/// </summary>
private readonly ArrayBuilder<BoundExpression> _conjunctBuilder;
public IsPatternExpressionLocalRewriter(LocalRewriter localRewriter, BoundExpression loweredInput)
: base(localRewriter, loweredInput)
public IsPatternExpressionLocalRewriter(SyntaxNode node, LocalRewriter localRewriter)
: base(node, localRewriter)
{
this._conjunctBuilder = ArrayBuilder<BoundExpression>.GetInstance();
this._sideEffectBuilder = ArrayBuilder<BoundExpression>.GetInstance();
......@@ -370,25 +430,20 @@ private void LowerOneTest(BoundDagTest test)
}
}
public BoundExpression LowerIsPattern(BoundPattern pattern, CSharpCompilation compilation, DiagnosticBag diagnostics)
public BoundExpression LowerIsPattern(BoundExpression loweredInput, BoundPattern pattern, CSharpCompilation compilation, DiagnosticBag diagnostics)
{
LabelSymbol failureLabel = new GeneratedLabelSymbol("failure");
BoundDecisionDag dag = DecisionDagBuilder.CreateDecisionDagForIsPattern(compilation, pattern.Syntax, this._loweredInput, pattern, failureLabel, diagnostics, out LabelSymbol successLabel);
if (_loweredInput.ConstantValue != null)
BoundDecisionDag decisionDag = DecisionDagBuilder.CreateDecisionDagForIsPattern(compilation, pattern.Syntax, loweredInput, pattern, failureLabel, diagnostics, out LabelSymbol successLabel);
if (loweredInput.ConstantValue != null)
{
dag = dag.SimplifyDecisionDagForConstantInput(_loweredInput, _localRewriter._compilation.Conversions, diagnostics);
decisionDag = decisionDag.SimplifyDecisionDagForConstantInput(loweredInput, _localRewriter._compilation.Conversions, diagnostics);
}
// The optimization of sharing pattern-matching temps with user variables can always apply to
// an is-pattern expression because there is no when clause.
ShareTemps(dag.TopologicallySortedNodes);
var inputTemp = _tempAllocator.GetTemp(_inputTemp);
if (inputTemp != _loweredInput)
{
_sideEffectBuilder.Add(_factory.AssignmentExpression(inputTemp, _loweredInput));
}
var node = dag.RootNode;
// an is-pattern expression because there is no when clause that could possibly intervene during
// the execution of the pattern-matching automaton and change one of those variables.
decisionDag = ShareTempsAndEvaluateInput(loweredInput, decisionDag, expr => _sideEffectBuilder.Add(expr));
var node = decisionDag.RootNode;
// We follow the "good" path in the decision dag. We depend on it being nicely linear in structure.
// If we add "or" patterns that assumption breaks down.
......@@ -457,7 +512,7 @@ public BoundExpression LowerIsPattern(BoundPattern pattern, CSharpCompilation co
break;
default:
throw ExceptionUtilities.UnexpectedValue(dag.Kind);
throw ExceptionUtilities.UnexpectedValue(decisionDag.Kind);
}
if (_sideEffectBuilder.Count > 0 || _conjunctBuilder.Count == 0)
......@@ -551,8 +606,8 @@ public override BoundNode VisitIsPatternExpression(BoundIsPatternExpression node
{
BoundExpression loweredExpression = VisitExpression(node.Expression);
BoundPattern loweredPattern = LowerPattern(node.Pattern);
var isPatternRewriter = new IsPatternExpressionLocalRewriter(this, loweredExpression);
BoundExpression result = isPatternRewriter.LowerIsPattern(loweredPattern, this._compilation, this._diagnostics);
var isPatternRewriter = new IsPatternExpressionLocalRewriter(node.Syntax, this);
BoundExpression result = isPatternRewriter.LowerIsPattern(loweredExpression, loweredPattern, this._compilation, this._diagnostics);
isPatternRewriter.Free();
return result;
}
......
......@@ -2,6 +2,9 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.PooledObjects;
......@@ -21,14 +24,14 @@ public override BoundNode VisitSwitchExpression(BoundSwitchExpression node)
private class SwitchExpressionLocalRewriter : BasePatternSwitchLocalRewriter
{
private SwitchExpressionLocalRewriter(LocalRewriter localRewriter, BoundSwitchExpression node)
: base(localRewriter, localRewriter.VisitExpression(node.Expression), node.SwitchArms.SelectAsArray(arm => arm.Syntax), node.DecisionDag)
private SwitchExpressionLocalRewriter(BoundSwitchExpression node, LocalRewriter localRewriter)
: base(node.Syntax, localRewriter, node.SwitchArms.SelectAsArray(arm => arm.Syntax), isSwitchStatement: false)
{
}
public static BoundExpression Rewrite(LocalRewriter localRewriter, BoundSwitchExpression node)
{
var rewriter = new SwitchExpressionLocalRewriter(localRewriter, node);
var rewriter = new SwitchExpressionLocalRewriter(node, localRewriter);
BoundExpression result = rewriter.LowerSwitchExpression(node);
rewriter.Free();
return result;
......@@ -36,41 +39,41 @@ public static BoundExpression Rewrite(LocalRewriter localRewriter, BoundSwitchEx
private BoundExpression LowerSwitchExpression(BoundSwitchExpression node)
{
_factory.Syntax = node.Syntax;
var result = ArrayBuilder<BoundStatement>.GetInstance();
var locals = ArrayBuilder<LocalSymbol>.GetInstance();
LocalSymbol resultTemp = _factory.SynthesizedLocal(node.Type, node.Syntax, kind: SynthesizedLocalKind.SwitchCasePatternMatching);
LabelSymbol afterSwitchExpression = _factory.GenerateLabel("afterSwitchExpression");
var loweredSwitchGoverningExpression = _localRewriter.VisitExpression(node.Expression);
// Note that a when-clause can contain an assignment to a
// pattern variable declared in a different when-clause (e.g. in the same section, or
// in a different section via the use of a local function), so we need to analyze all
// of the when clauses to see if they are all simple enough to conclude that they do
// not mutate pattern variables.
BoundDecisionDag decisionDag = ShareTempsIfPossibleAndEvaluateInput(node.DecisionDag, loweredSwitchGoverningExpression, result);
// Assign the input to a temp
result.Add(_factory.Assignment(_tempAllocator.GetTemp(base._inputTemp), base._loweredInput));
// lower the decision dag.
(ImmutableArray<BoundStatement> loweredDag, ImmutableDictionary<SyntaxNode, ImmutableArray<BoundStatement>> switchSections) =
LowerDecisionDag(decisionDag);
// then lower the rest of the dag that references that input
result.AddRange(_loweredDecisionDag.ToImmutable());
// then add the rest of the lowered dag that references that input
result.Add(_factory.Block(loweredDag));
// A branch to the default label when no switch case matches is included in the
// decision tree, so the code in result is unreachable at this point.
// Lower each switch arm
// Lower each switch expression arm
LocalSymbol resultTemp = _factory.SynthesizedLocal(node.Type, node.Syntax, kind: SynthesizedLocalKind.SwitchCasePatternMatching);
LabelSymbol afterSwitchExpression = _factory.GenerateLabel("afterSwitchExpression");
foreach (BoundSwitchExpressionArm arm in node.SwitchArms)
{
ArrayBuilder<BoundStatement> sectionStatementBuilder = _switchArms[arm.Syntax];
result.Add(_factory.Label(arm.Label));
sectionStatementBuilder.Add(_factory.Assignment(_factory.Local(resultTemp), _localRewriter.VisitExpression(arm.Value)));
var statements = sectionStatementBuilder.ToImmutableAndFree();
if (arm.Locals.IsEmpty)
{
result.Add(_factory.StatementList(statements));
}
else
{
// even though the whole switch expression will be lifted to the statement level, we
// want the locals of each section to see a section-specific scope.
result.Add(new BoundScope(arm.Syntax, arm.Locals, statements));
locals.AddRange(arm.Locals);
}
result.Add(_factory.Goto(afterSwitchExpression));
_factory.Syntax = arm.Syntax;
var sectionBuilder = ArrayBuilder<BoundStatement>.GetInstance();
sectionBuilder.AddRange(switchSections[arm.Syntax]);
sectionBuilder.Add(_factory.Label(arm.Label));
sectionBuilder.Add(_factory.Assignment(_factory.Local(resultTemp), _localRewriter.VisitExpression(arm.Value)));
sectionBuilder.Add(_factory.Goto(afterSwitchExpression));
result.Add(new BoundBlock(arm.Syntax, arm.Locals, sectionBuilder.ToImmutableAndFree()));
}
_factory.Syntax = node.Syntax;
if (node.DefaultLabel != null)
{
result.Add(_factory.Label(node.DefaultLabel));
......@@ -79,9 +82,7 @@ private BoundExpression LowerSwitchExpression(BoundSwitchExpression node)
}
result.Add(_factory.Label(afterSwitchExpression));
locals.AddRange(_tempAllocator.AllTemps());
locals.Add(resultTemp);
return _factory.SpillSequence(locals.ToImmutableAndFree(), result.ToImmutableAndFree(), _factory.Local(resultTemp));
return _factory.SpillSequence(_tempAllocator.AllTemps().Add(resultTemp), result.ToImmutableAndFree(), _factory.Local(resultTemp));
}
}
}
......
......@@ -91,9 +91,9 @@ static void M1(int? x)
// (14,18): error CS0656: Missing compiler required member 'System.Nullable`1.GetValueOrDefault'
// case int i: break;
Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "int i").WithArguments("System.Nullable`1", "GetValueOrDefault").WithLocation(14, 18),
// (12,17): error CS0656: Missing compiler required member 'System.Nullable`1.get_Value'
// switch (x)
Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "x").WithArguments("System.Nullable`1", "get_Value").WithLocation(12, 17),
// (14,18): error CS0656: Missing compiler required member 'System.Nullable`1.get_Value'
// case int i: break;
Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "int i").WithArguments("System.Nullable`1", "get_Value").WithLocation(14, 18),
// (17,36): error CS0656: Missing compiler required member 'System.Nullable`1.get_HasValue'
// static bool M2(int? x) => x is int i;
Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "int i").WithArguments("System.Nullable`1", "get_HasValue").WithLocation(17, 36),
......@@ -339,12 +339,13 @@ static void Main(string[] args)
var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput);
compVerifier.VerifyIL("Program.Main",
@"{
// Code size 82 (0x52)
// Code size 84 (0x54)
.maxstack 2
.locals init (Base<object> V_0, //x
Derived V_1, //y
Derived V_2, //z
Base<object> V_3)
Base<object> V_3,
Base<object> V_4)
IL_0000: nop
IL_0001: newobj ""Derived..ctor()""
IL_0006: stloc.0
......@@ -363,27 +364,27 @@ .maxstack 2
IL_0021: call ""void System.Console.WriteLine(bool)""
IL_0026: nop
IL_0027: ldloc.0
IL_0028: stloc.3
IL_0029: ldloc.3
IL_002a: stloc.0
IL_002b: ldloc.0
IL_002c: isinst ""Derived""
IL_0031: stloc.2
IL_0032: ldloc.2
IL_0033: brtrue.s IL_0037
IL_0035: br.s IL_0042
IL_0037: br.s IL_0039
IL_0039: ldc.i4.1
IL_003a: call ""void System.Console.WriteLine(bool)""
IL_003f: nop
IL_0040: br.s IL_0042
IL_0042: ldloc.0
IL_0043: isinst ""Derived""
IL_0048: ldnull
IL_0049: cgt.un
IL_004b: call ""void System.Console.WriteLine(bool)""
IL_0050: nop
IL_0051: ret
IL_0028: stloc.s V_4
IL_002a: ldloc.s V_4
IL_002c: stloc.3
IL_002d: ldloc.3
IL_002e: isinst ""Derived""
IL_0033: stloc.2
IL_0034: ldloc.2
IL_0035: brtrue.s IL_0039
IL_0037: br.s IL_0044
IL_0039: br.s IL_003b
IL_003b: ldc.i4.1
IL_003c: call ""void System.Console.WriteLine(bool)""
IL_0041: nop
IL_0042: br.s IL_0044
IL_0044: ldloc.0
IL_0045: isinst ""Derived""
IL_004a: ldnull
IL_004b: cgt.un
IL_004d: call ""void System.Console.WriteLine(bool)""
IL_0052: nop
IL_0053: ret
}");
}
......@@ -469,60 +470,61 @@ .maxstack 2
}");
compVerifier.VerifyIL("Program.P5",
@"{
// Code size 98 (0x62)
// Code size 103 (0x67)
.maxstack 2
.locals init (double V_0,
float V_1,
object V_2,
bool V_3)
.locals init (object V_0,
double V_1,
float V_2,
object V_3,
bool V_4)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: stloc.2
IL_0003: ldloc.2
IL_0004: starg.s V_0
IL_0006: ldarg.0
IL_0007: isinst ""double""
IL_000c: brfalse.s IL_002b
IL_000e: ldarg.0
IL_000f: unbox.any ""double""
IL_0014: stloc.0
IL_0015: ldloc.0
IL_0016: call ""bool double.IsNaN(double)""
IL_001b: brtrue.s IL_004c
IL_001d: ldc.r8 3.14
IL_0026: ldloc.0
IL_0027: beq.s IL_0054
IL_0029: br.s IL_005c
IL_002b: ldarg.0
IL_002c: isinst ""float""
IL_0031: brfalse.s IL_005c
IL_0033: ldarg.0
IL_0034: unbox.any ""float""
IL_0039: stloc.1
IL_003a: ldloc.1
IL_003b: call ""bool float.IsNaN(float)""
IL_0040: brtrue.s IL_0050
IL_0042: ldc.r4 3.14
IL_0047: ldloc.1
IL_0048: beq.s IL_0058
IL_004a: br.s IL_005c
IL_004c: ldc.i4.1
IL_004d: stloc.3
IL_004e: br.s IL_0060
IL_0002: stloc.3
IL_0003: ldloc.3
IL_0004: stloc.0
IL_0005: ldloc.0
IL_0006: isinst ""double""
IL_000b: brfalse.s IL_002a
IL_000d: ldloc.0
IL_000e: unbox.any ""double""
IL_0013: stloc.1
IL_0014: ldloc.1
IL_0015: call ""bool double.IsNaN(double)""
IL_001a: brtrue.s IL_004b
IL_001c: ldc.r8 3.14
IL_0025: ldloc.1
IL_0026: beq.s IL_0055
IL_0028: br.s IL_005f
IL_002a: ldloc.0
IL_002b: isinst ""float""
IL_0030: brfalse.s IL_005f
IL_0032: ldloc.0
IL_0033: unbox.any ""float""
IL_0038: stloc.2
IL_0039: ldloc.2
IL_003a: call ""bool float.IsNaN(float)""
IL_003f: brtrue.s IL_0050
IL_0041: ldc.r4 3.14
IL_0046: ldloc.2
IL_0047: beq.s IL_005a
IL_0049: br.s IL_005f
IL_004b: ldc.i4.1
IL_004c: stloc.s V_4
IL_004e: br.s IL_0064
IL_0050: ldc.i4.1
IL_0051: stloc.3
IL_0052: br.s IL_0060
IL_0054: ldc.i4.1
IL_0055: stloc.3
IL_0056: br.s IL_0060
IL_0058: ldc.i4.1
IL_0059: stloc.3
IL_005a: br.s IL_0060
IL_005c: ldc.i4.0
IL_005d: stloc.3
IL_005e: br.s IL_0060
IL_0060: ldloc.3
IL_0061: ret
IL_0051: stloc.s V_4
IL_0053: br.s IL_0064
IL_0055: ldc.i4.1
IL_0056: stloc.s V_4
IL_0058: br.s IL_0064
IL_005a: ldc.i4.1
IL_005b: stloc.s V_4
IL_005d: br.s IL_0064
IL_005f: ldc.i4.0
IL_0060: stloc.s V_4
IL_0062: br.s IL_0064
IL_0064: ldloc.s V_4
IL_0066: ret
}");
}
......@@ -708,35 +710,32 @@ static int M2(object o, bool b)
var compVerifier = CompileAndVerify(compilation);
compVerifier.VerifyIL("C.M1",
@"{
// Code size 35 (0x23)
// Code size 33 (0x21)
.maxstack 1
.locals init (bool? V_0)
IL_0000: ldarg.0
IL_0001: stloc.0
IL_0002: ldloca.s V_0
IL_0004: call ""bool bool?.HasValue.get""
IL_0009: brfalse.s IL_0018
IL_000b: br.s IL_001a
IL_000d: ldloca.s V_0
IL_000f: call ""bool bool?.GetValueOrDefault()""
IL_0014: brtrue.s IL_001f
IL_0016: br.s IL_0021
IL_0018: ldc.i4.1
IL_0019: ret
IL_001a: ldarg.1
IL_001b: brfalse.s IL_000d
IL_001d: ldc.i4.2
IL_0000: ldarga.s V_0
IL_0002: call ""bool bool?.HasValue.get""
IL_0007: brfalse.s IL_0016
IL_0009: br.s IL_0018
IL_000b: ldarga.s V_0
IL_000d: call ""bool bool?.GetValueOrDefault()""
IL_0012: brtrue.s IL_001d
IL_0014: br.s IL_001f
IL_0016: ldc.i4.1
IL_0017: ret
IL_0018: ldarg.1
IL_0019: brfalse.s IL_000b
IL_001b: ldc.i4.2
IL_001c: ret
IL_001d: ldc.i4.3
IL_001e: ret
IL_001f: ldc.i4.3
IL_001f: ldc.i4.4
IL_0020: ret
IL_0021: ldc.i4.4
IL_0022: ret
}");
compVerifier.VerifyIL("C.M2",
@"{
// Code size 19 (0x13)
.maxstack 1
.locals init (string V_0)
.locals init (string V_0) //a
IL_0000: ldarg.0
IL_0001: isinst ""string""
IL_0006: stloc.0
......@@ -803,41 +802,35 @@ public void SwitchBasedPatternMatching(object o)
var compVerifier = CompileAndVerify(compilation);
compVerifier.VerifyIL("C.SwitchBasedPatternMatching",
@"{
// Code size 71 (0x47)
// Code size 67 (0x43)
.maxstack 2
.locals init (int V_0,
string V_1,
object V_2)
.locals init (int V_0) //n
IL_0000: ldarg.1
IL_0001: stloc.2
IL_0002: ldloc.2
IL_0003: isinst ""int""
IL_0008: brfalse.s IL_0013
IL_000a: ldloc.2
IL_000b: unbox.any ""int""
IL_0010: stloc.0
IL_0011: br.s IL_001e
IL_0013: ldloc.2
IL_0014: isinst ""string""
IL_0019: stloc.1
IL_001a: ldloc.1
IL_001b: brtrue.s IL_002d
IL_001d: ret
IL_001e: ldloc.0
IL_001f: ldc.i4.1
IL_0020: bne.un.s IL_0038
IL_0022: ldstr ""1""
IL_0027: call ""void System.Console.WriteLine(string)""
IL_002c: ret
IL_002d: ldstr ""s""
IL_0032: call ""void System.Console.WriteLine(string)""
IL_0037: ret
IL_0038: ldloc.0
IL_0039: ldc.i4.2
IL_003a: bne.un.s IL_0046
IL_003c: ldstr ""2""
IL_0041: call ""void System.Console.WriteLine(string)""
IL_0046: ret
IL_0001: isinst ""int""
IL_0006: brfalse.s IL_0011
IL_0008: ldarg.1
IL_0009: unbox.any ""int""
IL_000e: stloc.0
IL_000f: br.s IL_001a
IL_0011: ldarg.1
IL_0012: isinst ""string""
IL_0017: brtrue.s IL_0029
IL_0019: ret
IL_001a: ldloc.0
IL_001b: ldc.i4.1
IL_001c: bne.un.s IL_0034
IL_001e: ldstr ""1""
IL_0023: call ""void System.Console.WriteLine(string)""
IL_0028: ret
IL_0029: ldstr ""s""
IL_002e: call ""void System.Console.WriteLine(string)""
IL_0033: ret
IL_0034: ldloc.0
IL_0035: ldc.i4.2
IL_0036: bne.un.s IL_0042
IL_0038: ldstr ""2""
IL_003d: call ""void System.Console.WriteLine(string)""
IL_0042: ret
}");
}
......@@ -1204,7 +1197,7 @@ static void Main(string[] args)
@"{
// Code size 94 (0x5e)
.maxstack 3
.locals init (Door.DoorState V_0,
.locals init (Door.DoorState V_0, //oldState
System.ValueTuple<Door.DoorState, Door.Action> V_1,
Door.Action V_2)
IL_0000: ldloca.s V_1
......@@ -1260,7 +1253,7 @@ .maxstack 3
}");
compVerifier.VerifyIL("Door.ChangeState1",
@"{
// Code size 98 (0x62)
// Code size 104 (0x68)
.maxstack 3
.locals init (System.ValueTuple<Door.DoorState, Door.Action> V_0,
Door.DoorState V_1,
......@@ -1278,14 +1271,14 @@ .maxstack 3
IL_0024,
IL_0031,
IL_0041)
IL_0022: br.s IL_005e
IL_0022: br.s IL_0064
IL_0024: ldloc.0
IL_0025: ldfld ""Door.Action System.ValueTuple<Door.DoorState, Door.Action>.Item2""
IL_002a: stloc.2
IL_002b: ldc.i4.1
IL_002c: ldloc.2
IL_002d: beq.s IL_004e
IL_002f: br.s IL_005e
IL_002f: br.s IL_0064
IL_0031: ldloc.0
IL_0032: ldfld ""Door.Action System.ValueTuple<Door.DoorState, Door.Action>.Item2""
IL_0037: stloc.2
......@@ -1294,29 +1287,34 @@ .maxstack 3
IL_003b: ldloc.2
IL_003c: ldc.i4.2
IL_003d: beq.s IL_0056
IL_003f: br.s IL_005e
IL_003f: br.s IL_0064
IL_0041: ldloc.0
IL_0042: ldfld ""Door.Action System.ValueTuple<Door.DoorState, Door.Action>.Item2""
IL_0047: stloc.2
IL_0048: ldc.i4.3
IL_0049: ldloc.2
IL_004a: beq.s IL_005b
IL_004c: br.s IL_005e
IL_004a: beq.s IL_005d
IL_004c: br.s IL_0064
IL_004e: ldc.i4.1
IL_004f: stloc.3
IL_0050: br.s IL_0060
IL_0050: br.s IL_0066
IL_0052: ldc.i4.0
IL_0053: stloc.3
IL_0054: br.s IL_0060
IL_0054: br.s IL_0066
IL_0056: ldarg.2
IL_0057: brtrue.s IL_0056
IL_0059: br.s IL_005e
IL_005b: ldarg.2
IL_005c: brtrue.s IL_005b
IL_005e: ldarg.0
IL_005f: stloc.3
IL_0060: ldloc.3
IL_0061: ret
IL_0057: brfalse.s IL_0064
IL_0059: ldc.i4.2
IL_005a: stloc.3
IL_005b: br.s IL_0066
IL_005d: ldarg.2
IL_005e: brfalse.s IL_0064
IL_0060: ldc.i4.1
IL_0061: stloc.3
IL_0062: br.s IL_0066
IL_0064: ldarg.0
IL_0065: stloc.3
IL_0066: ldloc.3
IL_0067: ret
}");
}
}
......
......@@ -2216,21 +2216,20 @@ .maxstack 2
{
// Code size 61 (0x3d)
.maxstack 2
.locals init ([string] V_0,
string V_1,
string V_2)
.locals init (string V_0,
string V_1)
-IL_0000: nop
-IL_0001: call ""string C.F()""
IL_0006: stloc.1
~IL_0007: ldloc.1
IL_0008: stloc.2
IL_0009: ldloc.2
IL_0008: stloc.0
IL_0009: ldloc.0
IL_000a: brfalse.s IL_003c
IL_000c: ldloc.2
IL_000c: ldloc.0
IL_000d: ldstr ""a""
IL_0012: call ""bool string.op_Equality(string, string)""
IL_0017: brtrue.s IL_0028
IL_0019: ldloc.2
IL_0019: ldloc.0
IL_001a: ldstr ""b""
IL_001f: call ""bool string.op_Equality(string, string)""
IL_0024: brtrue.s IL_0032
......@@ -2323,7 +2322,7 @@ .maxstack 2
<customDebugInfo>
<forward declaringType=""C"" methodName=""F"" />
<encLocalSlotMap>
<slot kind=""35"" offset=""19"" />
<slot kind=""35"" offset=""11"" />
<slot kind=""1"" offset=""11"" />
</encLocalSlotMap>
</customDebugInfo>
......@@ -2354,19 +2353,18 @@ .maxstack 2
{
// Code size 42 (0x2a)
.maxstack 2
.locals init ([int] V_0,
int V_1,
int V_2)
.locals init (int V_0,
int V_1)
IL_0000: nop
IL_0001: call ""int C.F()""
IL_0006: stloc.1
IL_0007: ldloc.1
IL_0008: stloc.2
IL_0009: ldloc.2
IL_0008: stloc.0
IL_0009: ldloc.0
IL_000a: ldc.i4.1
IL_000b: beq.s IL_0015
IL_000d: br.s IL_000f
IL_000f: ldloc.2
IL_000f: ldloc.0
IL_0010: ldc.i4.2
IL_0011: beq.s IL_001f
IL_0013: br.s IL_0029
......@@ -2424,9 +2422,6 @@ static void M()
<slot kind=""0"" offset=""162"" />
<slot kind=""0"" offset=""273"" />
<slot kind=""0"" offset=""323"" />
<slot kind=""35"" offset=""19"" />
<slot kind=""35"" offset=""19"" />
<slot kind=""35"" offset=""19"" />
<slot kind=""1"" offset=""11"" />
</encLocalSlotMap>
</customDebugInfo>
......@@ -2436,86 +2431,77 @@ static void M()
v0.VerifyIL("C.M", @"
{
// Code size 167 (0xa7)
// Code size 147 (0x93)
.maxstack 2
.locals init (byte V_0, //b
int V_1, //i
int V_2, //j
object V_3, //o
int V_4,
byte V_5,
object V_6,
object V_7)
object V_4)
IL_0000: nop
IL_0001: call ""object C.F()""
IL_0006: stloc.s V_7
IL_0008: ldloc.s V_7
IL_000a: stloc.s V_6
IL_000c: ldloc.s V_6
IL_000e: isinst ""int""
IL_0013: brfalse.s IL_0025
IL_0015: ldloc.s V_6
IL_0017: unbox.any ""int""
IL_001c: stloc.s V_4
IL_001e: ldc.i4.1
IL_001f: ldloc.s V_4
IL_0021: beq.s IL_0046
IL_0023: br.s IL_0068
IL_0025: ldloc.s V_6
IL_0027: isinst ""byte""
IL_002c: brfalse.s IL_0040
IL_002e: ldloc.s V_6
IL_0030: unbox.any ""byte""
IL_0035: stloc.s V_5
IL_0037: br.s IL_0053
IL_0039: ldc.i4.1
IL_003a: ldloc.s V_5
IL_003c: beq.s IL_007d
IL_003e: br.s IL_0098
IL_0040: ldloc.s V_6
IL_0042: brtrue.s IL_0098
IL_0044: br.s IL_00a6
IL_0046: ldstr ""int 1""
IL_004b: call ""void System.Console.WriteLine(string)""
IL_0050: nop
IL_0051: br.s IL_00a6
IL_0053: ldloc.s V_5
IL_0055: stloc.0
IL_0056: call ""bool C.P()""
IL_005b: brtrue.s IL_005f
IL_005d: br.s IL_0039
IL_005f: ldloc.0
IL_0060: call ""void System.Console.WriteLine(int)""
IL_0065: nop
IL_0066: br.s IL_00a6
IL_0068: ldloc.s V_4
IL_006a: stloc.1
IL_006b: call ""bool C.P()""
IL_0070: brtrue.s IL_0074
IL_0072: br.s IL_008a
IL_0074: ldloc.1
IL_0075: call ""void System.Console.WriteLine(int)""
IL_007a: nop
IL_007b: br.s IL_00a6
IL_007d: ldstr ""byte 1""
IL_0082: call ""void System.Console.WriteLine(string)""
IL_0087: nop
IL_0088: br.s IL_00a6
IL_008a: ldloc.s V_4
IL_008c: stloc.2
IL_008d: br.s IL_008f
IL_008f: ldloc.2
IL_0090: call ""void System.Console.WriteLine(int)""
IL_0095: nop
IL_0096: br.s IL_00a6
IL_0098: ldloc.s V_6
IL_009a: stloc.3
IL_009b: br.s IL_009d
IL_009d: ldloc.3
IL_009e: call ""void System.Console.WriteLine(object)""
IL_00a3: nop
IL_00a4: br.s IL_00a6
IL_00a6: ret
IL_0006: stloc.s V_4
IL_0008: ldloc.s V_4
IL_000a: stloc.3
IL_000b: ldloc.3
IL_000c: isinst ""int""
IL_0011: brfalse.s IL_0020
IL_0013: ldloc.3
IL_0014: unbox.any ""int""
IL_0019: stloc.1
IL_001a: ldc.i4.1
IL_001b: ldloc.1
IL_001c: beq.s IL_003c
IL_001e: br.s IL_005b
IL_0020: ldloc.3
IL_0021: isinst ""byte""
IL_0026: brfalse.s IL_0037
IL_0028: ldloc.3
IL_0029: unbox.any ""byte""
IL_002e: stloc.0
IL_002f: br.s IL_0049
IL_0031: ldc.i4.1
IL_0032: ldloc.0
IL_0033: beq.s IL_006d
IL_0035: br.s IL_0087
IL_0037: ldloc.3
IL_0038: brtrue.s IL_0087
IL_003a: br.s IL_0092
IL_003c: ldstr ""int 1""
IL_0041: call ""void System.Console.WriteLine(string)""
IL_0046: nop
IL_0047: br.s IL_0092
IL_0049: call ""bool C.P()""
IL_004e: brtrue.s IL_0052
IL_0050: br.s IL_0031
IL_0052: ldloc.0
IL_0053: call ""void System.Console.WriteLine(int)""
IL_0058: nop
IL_0059: br.s IL_0092
IL_005b: call ""bool C.P()""
IL_0060: brtrue.s IL_0064
IL_0062: br.s IL_007a
IL_0064: ldloc.1
IL_0065: call ""void System.Console.WriteLine(int)""
IL_006a: nop
IL_006b: br.s IL_0092
IL_006d: ldstr ""byte 1""
IL_0072: call ""void System.Console.WriteLine(string)""
IL_0077: nop
IL_0078: br.s IL_0092
IL_007a: ldloc.1
IL_007b: stloc.2
IL_007c: br.s IL_007e
IL_007e: ldloc.2
IL_007f: call ""void System.Console.WriteLine(int)""
IL_0084: nop
IL_0085: br.s IL_0092
IL_0087: br.s IL_0089
IL_0089: ldloc.3
IL_008a: call ""void System.Console.WriteLine(object)""
IL_008f: nop
IL_0090: br.s IL_0092
IL_0092: ret
}");
var methodData0 = v0.TestData.GetMethodData("C.M");
var method0 = compilation0.GetMember<MethodSymbol>("C.M");
......@@ -2528,89 +2514,77 @@ .maxstack 2
diff1.VerifyIL("C.M", @"
{
// Code size 167 (0xa7)
// Code size 147 (0x93)
.maxstack 2
.locals init (byte V_0, //b
int V_1, //i
int V_2, //j
object V_3, //o
[int] V_4,
[unchanged] V_5,
[object] V_6,
object V_7,
int V_8,
byte V_9,
object V_10)
object V_4)
IL_0000: nop
IL_0001: call ""object C.F()""
IL_0006: stloc.s V_7
IL_0008: ldloc.s V_7
IL_000a: stloc.s V_10
IL_000c: ldloc.s V_10
IL_000e: isinst ""int""
IL_0013: brfalse.s IL_0025
IL_0015: ldloc.s V_10
IL_0017: unbox.any ""int""
IL_001c: stloc.s V_8
IL_001e: ldc.i4.1
IL_001f: ldloc.s V_8
IL_0021: beq.s IL_0046
IL_0023: br.s IL_0068
IL_0025: ldloc.s V_10
IL_0027: isinst ""byte""
IL_002c: brfalse.s IL_0040
IL_002e: ldloc.s V_10
IL_0030: unbox.any ""byte""
IL_0035: stloc.s V_9
IL_0037: br.s IL_0053
IL_0039: ldc.i4.1
IL_003a: ldloc.s V_9
IL_003c: beq.s IL_007d
IL_003e: br.s IL_0098
IL_0040: ldloc.s V_10
IL_0042: brtrue.s IL_0098
IL_0044: br.s IL_00a6
IL_0046: ldstr ""int 1""
IL_004b: call ""void System.Console.WriteLine(string)""
IL_0050: nop
IL_0051: br.s IL_00a6
IL_0053: ldloc.s V_9
IL_0055: stloc.0
IL_0056: call ""bool C.P()""
IL_005b: brtrue.s IL_005f
IL_005d: br.s IL_0039
IL_005f: ldloc.0
IL_0060: call ""void System.Console.WriteLine(int)""
IL_0065: nop
IL_0066: br.s IL_00a6
IL_0068: ldloc.s V_8
IL_006a: stloc.1
IL_006b: call ""bool C.P()""
IL_0070: brtrue.s IL_0074
IL_0072: br.s IL_008a
IL_0074: ldloc.1
IL_0075: call ""void System.Console.WriteLine(int)""
IL_007a: nop
IL_007b: br.s IL_00a6
IL_007d: ldstr ""byte 1""
IL_0082: call ""void System.Console.WriteLine(string)""
IL_0087: nop
IL_0088: br.s IL_00a6
IL_008a: ldloc.s V_8
IL_008c: stloc.2
IL_008d: br.s IL_008f
IL_008f: ldloc.2
IL_0090: call ""void System.Console.WriteLine(int)""
IL_0095: nop
IL_0096: br.s IL_00a6
IL_0098: ldloc.s V_10
IL_009a: stloc.3
IL_009b: br.s IL_009d
IL_009d: ldloc.3
IL_009e: call ""void System.Console.WriteLine(object)""
IL_00a3: nop
IL_00a4: br.s IL_00a6
IL_00a6: ret
IL_0006: stloc.s V_4
IL_0008: ldloc.s V_4
IL_000a: stloc.3
IL_000b: ldloc.3
IL_000c: isinst ""int""
IL_0011: brfalse.s IL_0020
IL_0013: ldloc.3
IL_0014: unbox.any ""int""
IL_0019: stloc.1
IL_001a: ldc.i4.1
IL_001b: ldloc.1
IL_001c: beq.s IL_003c
IL_001e: br.s IL_005b
IL_0020: ldloc.3
IL_0021: isinst ""byte""
IL_0026: brfalse.s IL_0037
IL_0028: ldloc.3
IL_0029: unbox.any ""byte""
IL_002e: stloc.0
IL_002f: br.s IL_0049
IL_0031: ldc.i4.1
IL_0032: ldloc.0
IL_0033: beq.s IL_006d
IL_0035: br.s IL_0087
IL_0037: ldloc.3
IL_0038: brtrue.s IL_0087
IL_003a: br.s IL_0092
IL_003c: ldstr ""int 1""
IL_0041: call ""void System.Console.WriteLine(string)""
IL_0046: nop
IL_0047: br.s IL_0092
IL_0049: call ""bool C.P()""
IL_004e: brtrue.s IL_0052
IL_0050: br.s IL_0031
IL_0052: ldloc.0
IL_0053: call ""void System.Console.WriteLine(int)""
IL_0058: nop
IL_0059: br.s IL_0092
IL_005b: call ""bool C.P()""
IL_0060: brtrue.s IL_0064
IL_0062: br.s IL_007a
IL_0064: ldloc.1
IL_0065: call ""void System.Console.WriteLine(int)""
IL_006a: nop
IL_006b: br.s IL_0092
IL_006d: ldstr ""byte 1""
IL_0072: call ""void System.Console.WriteLine(string)""
IL_0077: nop
IL_0078: br.s IL_0092
IL_007a: ldloc.1
IL_007b: stloc.2
IL_007c: br.s IL_007e
IL_007e: ldloc.2
IL_007f: call ""void System.Console.WriteLine(int)""
IL_0084: nop
IL_0085: br.s IL_0092
IL_0087: br.s IL_0089
IL_0089: ldloc.3
IL_008a: call ""void System.Console.WriteLine(object)""
IL_008f: nop
IL_0090: br.s IL_0092
IL_0092: ret
}");
}
......
......@@ -1250,7 +1250,7 @@ void F()
<encLocalSlotMap>
<slot kind=""30"" offset=""0"" />
<slot kind=""30"" offset=""86"" />
<slot kind=""35"" offset=""94"" />
<slot kind=""35"" offset=""86"" />
<slot kind=""1"" offset=""86"" />
</encLocalSlotMap>
<encLambdaMap>
......@@ -1285,8 +1285,7 @@ void F()
</scope>
</method>
</methods>
</symbols>
");
</symbols>");
}
[Fact]
......
......@@ -331,7 +331,7 @@ void F()
<encLocalSlotMap>
<slot kind=""30"" offset=""0"" />
<slot kind=""30"" offset=""75"" />
<slot kind=""35"" offset=""83"" />
<slot kind=""35"" offset=""75"" />
<slot kind=""1"" offset=""75"" />
</encLocalSlotMap>
<encLambdaMap>
......
......@@ -2790,7 +2790,7 @@ static void Main(string[] args)
<encLocalSlotMap>
<slot kind=""5"" offset=""15"" />
<slot kind=""0"" offset=""15"" />
<slot kind=""35"" offset=""91"" />
<slot kind=""35"" offset=""83"" />
<slot kind=""1"" offset=""83"" />
<slot kind=""1"" offset=""237"" />
</encLocalSlotMap>
......@@ -3022,9 +3022,7 @@ class Student : Person { public double GPA; }
<slot kind=""0"" offset=""59"" />
<slot kind=""0"" offset=""163"" />
<slot kind=""0"" offset=""250"" />
<slot kind=""35"" offset=""19"" />
<slot kind=""35"" offset=""19"" />
<slot kind=""35"" offset=""19"" />
<slot kind=""35"" offset=""11"" />
<slot kind=""1"" offset=""11"" />
<slot kind=""21"" offset=""0"" />
</encLocalSlotMap>
......@@ -3033,21 +3031,21 @@ class Student : Person { public double GPA; }
<entry offset=""0x0"" startLine=""19"" startColumn=""5"" endLine=""19"" endColumn=""6"" document=""1"" />
<entry offset=""0x1"" startLine=""20"" startColumn=""9"" endLine=""20"" endColumn=""19"" document=""1"" />
<entry offset=""0x4"" hidden=""true"" document=""1"" />
<entry offset=""0x37"" startLine=""23"" startColumn=""17"" endLine=""23"" endColumn=""57"" document=""1"" />
<entry offset=""0x5a"" startLine=""25"" startColumn=""17"" endLine=""25"" endColumn=""57"" document=""1"" />
<entry offset=""0x7e"" startLine=""27"" startColumn=""17"" endLine=""27"" endColumn=""59"" document=""1"" />
<entry offset=""0x98"" startLine=""29"" startColumn=""17"" endLine=""29"" endColumn=""43"" document=""1"" />
<entry offset=""0xac"" startLine=""31"" startColumn=""5"" endLine=""31"" endColumn=""6"" document=""1"" />
</sequencePoints>
<scope startOffset=""0x0"" endOffset=""0xaf"">
<scope startOffset=""0x22"" endOffset=""0x56"">
<local name=""s"" il_index=""0"" il_start=""0x22"" il_end=""0x56"" attributes=""0"" />
<entry offset=""0x30"" startLine=""23"" startColumn=""17"" endLine=""23"" endColumn=""57"" document=""1"" />
<entry offset=""0x53"" startLine=""25"" startColumn=""17"" endLine=""25"" endColumn=""57"" document=""1"" />
<entry offset=""0x74"" startLine=""27"" startColumn=""17"" endLine=""27"" endColumn=""59"" document=""1"" />
<entry offset=""0x8e"" startLine=""29"" startColumn=""17"" endLine=""29"" endColumn=""43"" document=""1"" />
<entry offset=""0xa2"" startLine=""31"" startColumn=""5"" endLine=""31"" endColumn=""6"" document=""1"" />
</sequencePoints>
<scope startOffset=""0x0"" endOffset=""0xa5"">
<scope startOffset=""0x1d"" endOffset=""0x4f"">
<local name=""s"" il_index=""0"" il_start=""0x1d"" il_end=""0x4f"" attributes=""0"" />
</scope>
<scope startOffset=""0x56"" endOffset=""0x79"">
<local name=""s"" il_index=""1"" il_start=""0x56"" il_end=""0x79"" attributes=""0"" />
<scope startOffset=""0x4f"" endOffset=""0x72"">
<local name=""s"" il_index=""1"" il_start=""0x4f"" il_end=""0x72"" attributes=""0"" />
</scope>
<scope startOffset=""0x79"" endOffset=""0x98"">
<local name=""t"" il_index=""2"" il_start=""0x79"" il_end=""0x98"" attributes=""0"" />
<scope startOffset=""0x72"" endOffset=""0x8e"">
<local name=""t"" il_index=""2"" il_start=""0x72"" il_end=""0x8e"" attributes=""0"" />
</scope>
</scope>
</method>
......@@ -3109,9 +3107,7 @@ class Student : Person { public double GPA; }
<encLocalSlotMap>
<slot kind=""30"" offset=""0"" />
<slot kind=""30"" offset=""11"" />
<slot kind=""35"" offset=""19"" />
<slot kind=""35"" offset=""19"" />
<slot kind=""35"" offset=""19"" />
<slot kind=""35"" offset=""11"" />
<slot kind=""1"" offset=""11"" />
<slot kind=""21"" offset=""0"" />
</encLocalSlotMap>
......@@ -3129,17 +3125,17 @@ class Student : Person { public double GPA; }
<entry offset=""0x0"" hidden=""true"" document=""1"" />
<entry offset=""0xd"" startLine=""19"" startColumn=""5"" endLine=""19"" endColumn=""6"" document=""1"" />
<entry offset=""0xe"" hidden=""true"" document=""1"" />
<entry offset=""0x1c"" hidden=""true"" document=""1"" />
<entry offset=""0x57"" startLine=""23"" startColumn=""17"" endLine=""23"" endColumn=""63"" document=""1"" />
<entry offset=""0x70"" startLine=""25"" startColumn=""17"" endLine=""25"" endColumn=""63"" document=""1"" />
<entry offset=""0x89"" startLine=""27"" startColumn=""17"" endLine=""27"" endColumn=""65"" document=""1"" />
<entry offset=""0x99"" startLine=""29"" startColumn=""17"" endLine=""29"" endColumn=""49"" document=""1"" />
<entry offset=""0xa9"" startLine=""31"" startColumn=""5"" endLine=""31"" endColumn=""6"" document=""1"" />
</sequencePoints>
<scope startOffset=""0x0"" endOffset=""0xac"">
<local name=""CS$&lt;&gt;8__locals0"" il_index=""0"" il_start=""0x0"" il_end=""0xac"" attributes=""0"" />
<scope startOffset=""0xe"" endOffset=""0xa9"">
<local name=""CS$&lt;&gt;8__locals1"" il_index=""1"" il_start=""0xe"" il_end=""0xa9"" attributes=""0"" />
<entry offset=""0x1b"" hidden=""true"" document=""1"" />
<entry offset=""0x5f"" startLine=""23"" startColumn=""17"" endLine=""23"" endColumn=""63"" document=""1"" />
<entry offset=""0x7d"" startLine=""25"" startColumn=""17"" endLine=""25"" endColumn=""63"" document=""1"" />
<entry offset=""0x8f"" startLine=""27"" startColumn=""17"" endLine=""27"" endColumn=""65"" document=""1"" />
<entry offset=""0x9f"" startLine=""29"" startColumn=""17"" endLine=""29"" endColumn=""49"" document=""1"" />
<entry offset=""0xaf"" startLine=""31"" startColumn=""5"" endLine=""31"" endColumn=""6"" document=""1"" />
</sequencePoints>
<scope startOffset=""0x0"" endOffset=""0xb2"">
<local name=""CS$&lt;&gt;8__locals0"" il_index=""0"" il_start=""0x0"" il_end=""0xb2"" attributes=""0"" />
<scope startOffset=""0xe"" endOffset=""0xaf"">
<local name=""CS$&lt;&gt;8__locals1"" il_index=""1"" il_start=""0xe"" il_end=""0xaf"" attributes=""0"" />
</scope>
</scope>
</method>
......@@ -3205,9 +3201,7 @@ class Student : Person { public double GPA; }
<encLocalSlotMap>
<slot kind=""30"" offset=""0"" />
<slot kind=""30"" offset=""11"" />
<slot kind=""35"" offset=""19"" />
<slot kind=""35"" offset=""19"" />
<slot kind=""35"" offset=""19"" />
<slot kind=""35"" offset=""11"" />
<slot kind=""1"" offset=""11"" />
<slot kind=""21"" offset=""0"" />
</encLocalSlotMap>
......@@ -3225,23 +3219,22 @@ class Student : Person { public double GPA; }
<entry offset=""0x0"" hidden=""true"" document=""1"" />
<entry offset=""0xd"" startLine=""19"" startColumn=""5"" endLine=""19"" endColumn=""6"" document=""1"" />
<entry offset=""0xe"" hidden=""true"" document=""1"" />
<entry offset=""0x1c"" hidden=""true"" document=""1"" />
<entry offset=""0x58"" startLine=""24"" startColumn=""17"" endLine=""24"" endColumn=""27"" document=""1"" />
<entry offset=""0x72"" startLine=""27"" startColumn=""17"" endLine=""27"" endColumn=""27"" document=""1"" />
<entry offset=""0x8c"" startLine=""30"" startColumn=""17"" endLine=""30"" endColumn=""27"" document=""1"" />
<entry offset=""0x9d"" startLine=""33"" startColumn=""17"" endLine=""33"" endColumn=""27"" document=""1"" />
<entry offset=""0xad"" startLine=""35"" startColumn=""5"" endLine=""35"" endColumn=""6"" document=""1"" />
</sequencePoints>
<scope startOffset=""0x0"" endOffset=""0xb0"">
<local name=""CS$&lt;&gt;8__locals0"" il_index=""0"" il_start=""0x0"" il_end=""0xb0"" attributes=""0"" />
<scope startOffset=""0xe"" endOffset=""0xad"">
<local name=""CS$&lt;&gt;8__locals1"" il_index=""1"" il_start=""0xe"" il_end=""0xad"" attributes=""0"" />
<entry offset=""0x1b"" hidden=""true"" document=""1"" />
<entry offset=""0x60"" startLine=""24"" startColumn=""17"" endLine=""24"" endColumn=""27"" document=""1"" />
<entry offset=""0x7f"" startLine=""27"" startColumn=""17"" endLine=""27"" endColumn=""27"" document=""1"" />
<entry offset=""0x92"" startLine=""30"" startColumn=""17"" endLine=""30"" endColumn=""27"" document=""1"" />
<entry offset=""0xa3"" startLine=""33"" startColumn=""17"" endLine=""33"" endColumn=""27"" document=""1"" />
<entry offset=""0xb3"" startLine=""35"" startColumn=""5"" endLine=""35"" endColumn=""6"" document=""1"" />
</sequencePoints>
<scope startOffset=""0x0"" endOffset=""0xb6"">
<local name=""CS$&lt;&gt;8__locals0"" il_index=""0"" il_start=""0x0"" il_end=""0xb6"" attributes=""0"" />
<scope startOffset=""0xe"" endOffset=""0xb3"">
<local name=""CS$&lt;&gt;8__locals1"" il_index=""1"" il_start=""0xe"" il_end=""0xb3"" attributes=""0"" />
</scope>
</scope>
</method>
</methods>
</symbols>
");
</symbols>");
}
[Fact, WorkItem(17090, "https://github.com/dotnet/roslyn/issues/17090"), WorkItem(19731, "https://github.com/dotnet/roslyn/issues/19731")]
......@@ -7142,7 +7135,7 @@ public void SyntaxOffset_Pattern()
</using>
<encLocalSlotMap>
<slot kind=""0"" offset=""12"" />
<slot kind=""35"" offset=""22"" />
<slot kind=""35"" offset=""17"" />
</encLocalSlotMap>
</customDebugInfo>
<sequencePoints>
......@@ -8310,14 +8303,16 @@ static void M(object o)
var c = CreateCompilationWithMscorlib40AndSystemCore(source, options: TestOptions.DebugDll);
CompileAndVerify(c).VerifyIL("Program.M",
@"{
// Code size 122 (0x7a)
// Code size 123 (0x7b)
.maxstack 2
.locals init (object V_0,
int V_1,
object V_2,
int V_3,
object V_4,
object V_5)
object V_3,
int V_4,
object V_5,
object V_6,
object V_7)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: stloc.2
......@@ -8356,28 +8351,28 @@ .maxstack 2
IL_004a: br.s IL_004e
IL_004c: br.s IL_004e
IL_004e: ldarg.0
IL_004f: stloc.s V_4
IL_0051: ldloc.s V_4
IL_0053: starg.s V_0
IL_0055: ldarg.0
IL_0056: isinst ""int""
IL_005b: brfalse.s IL_006c
IL_005d: ldarg.0
IL_005e: unbox.any ""int""
IL_0063: stloc.3
IL_004f: stloc.s V_5
IL_0051: ldloc.s V_5
IL_0053: stloc.3
IL_0054: ldloc.3
IL_0055: isinst ""int""
IL_005a: brfalse.s IL_006d
IL_005c: ldloc.3
IL_005d: unbox.any ""int""
IL_0062: stloc.s V_4
IL_0064: ldc.i4.1
IL_0065: ldloc.3
IL_0066: beq.s IL_006a
IL_0068: br.s IL_006c
IL_006a: br.s IL_006e
IL_006c: br.s IL_006e
IL_006e: ldarg.0
IL_006f: stloc.s V_5
IL_0071: ldloc.s V_5
IL_0073: starg.s V_0
IL_0075: br.s IL_0077
IL_0077: br.s IL_0079
IL_0079: ret
IL_0065: ldloc.s V_4
IL_0067: beq.s IL_006b
IL_0069: br.s IL_006d
IL_006b: br.s IL_006f
IL_006d: br.s IL_006f
IL_006f: ldarg.0
IL_0070: stloc.s V_7
IL_0072: ldloc.s V_7
IL_0074: stloc.s V_6
IL_0076: br.s IL_0078
IL_0078: br.s IL_007a
IL_007a: ret
}");
c.VerifyPdb(
@"<symbols>
......@@ -8391,11 +8386,13 @@ .maxstack 2
<namespace usingCount=""0"" />
</using>
<encLocalSlotMap>
<slot kind=""35"" offset=""19"" />
<slot kind=""35"" offset=""19"" />
<slot kind=""35"" offset=""11"" />
<slot kind=""35"" offset=""11"" />
<slot kind=""1"" offset=""11"" />
<slot kind=""35"" offset=""386"" />
<slot kind=""35"" offset=""378"" />
<slot kind=""35"" offset=""378"" />
<slot kind=""1"" offset=""378"" />
<slot kind=""35"" offset=""511"" />
<slot kind=""1"" offset=""511"" />
</encLocalSlotMap>
</customDebugInfo>
......@@ -8409,12 +8406,12 @@ .maxstack 2
<entry offset=""0x4c"" startLine=""18"" startColumn=""17"" endLine=""18"" endColumn=""23"" document=""1"" />
<entry offset=""0x4e"" startLine=""20"" startColumn=""9"" endLine=""20"" endColumn=""19"" document=""1"" />
<entry offset=""0x51"" hidden=""true"" document=""1"" />
<entry offset=""0x6a"" startLine=""23"" startColumn=""17"" endLine=""23"" endColumn=""23"" document=""1"" />
<entry offset=""0x6c"" startLine=""25"" startColumn=""17"" endLine=""25"" endColumn=""23"" document=""1"" />
<entry offset=""0x6e"" startLine=""27"" startColumn=""9"" endLine=""27"" endColumn=""19"" document=""1"" />
<entry offset=""0x71"" hidden=""true"" document=""1"" />
<entry offset=""0x77"" startLine=""30"" startColumn=""17"" endLine=""30"" endColumn=""23"" document=""1"" />
<entry offset=""0x79"" startLine=""32"" startColumn=""5"" endLine=""32"" endColumn=""6"" document=""1"" />
<entry offset=""0x6b"" startLine=""23"" startColumn=""17"" endLine=""23"" endColumn=""23"" document=""1"" />
<entry offset=""0x6d"" startLine=""25"" startColumn=""17"" endLine=""25"" endColumn=""23"" document=""1"" />
<entry offset=""0x6f"" startLine=""27"" startColumn=""9"" endLine=""27"" endColumn=""19"" document=""1"" />
<entry offset=""0x72"" hidden=""true"" document=""1"" />
<entry offset=""0x78"" startLine=""30"" startColumn=""17"" endLine=""30"" endColumn=""23"" document=""1"" />
<entry offset=""0x7a"" startLine=""32"" startColumn=""5"" endLine=""32"" endColumn=""6"" document=""1"" />
</sequencePoints>
</method>
</methods>
......@@ -8541,10 +8538,10 @@ .maxstack 2
IL_0016: stloc.1
// sequence point: <hidden>
IL_0017: ldloc.1
IL_0018: stfld ""int Program.<Test>d__0.<i>5__1""
IL_0018: stfld ""int Program.<Test>d__0.<>s__2""
IL_001d: ldc.i4.1
IL_001e: ldarg.0
IL_001f: ldfld ""int Program.<Test>d__0.<i>5__1""
IL_001f: ldfld ""int Program.<Test>d__0.<>s__2""
IL_0024: beq.s IL_0028
IL_0026: br.s IL_002a
// sequence point: break;
......
......@@ -5513,9 +5513,9 @@ public struct Single
compilation.VerifyDiagnostics(
);
compilation.GetEmitDiagnostics().Where(d => d.Severity != DiagnosticSeverity.Warning).Verify(
// (5,17): error CS0656: Missing compiler required member 'System.Single.IsNaN'
// switch (o)
Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "o").WithArguments("System.Single", "IsNaN").WithLocation(5, 17)
// (7,13): error CS0656: Missing compiler required member 'System.Single.IsNaN'
// case 0f/0f: break;
Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "case 0f/0f:").WithArguments("System.Single", "IsNaN").WithLocation(7, 13)
);
}
......
......@@ -2080,9 +2080,9 @@ public static void M(object o)
// (13,13): error CS0656: Missing compiler required member 'System.String.op_Equality'
// case "hmm":
Diagnostic(ErrorCode.ERR_MissingPredefinedMember, @"case ""hmm"":").WithArguments("System.String", "op_Equality").WithLocation(13, 13),
// (11,17): error CS0656: Missing compiler required member 'System.String.op_Equality'
// switch (o)
Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "o").WithArguments("System.String", "op_Equality").WithLocation(11, 17)
// (33,13): error CS0656: Missing compiler required member 'System.String.op_Equality'
// case "baz":
Diagnostic(ErrorCode.ERR_MissingPredefinedMember, @"case ""baz"":").WithArguments("System.String", "op_Equality").WithLocation(33, 13)
);
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册