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

Change typeswitch lowering per code review comments.

Moved a PROTOTYPE comment to issue #12431, and report an unimplemented
error message rather than crashing the compiler when switching on float, double,
or decimal.
上级 1ea21c70
......@@ -14,16 +14,16 @@ namespace Microsoft.CodeAnalysis.CSharp
{
internal partial class SwitchBinder : LocalScopeBinder
{
protected readonly SwitchStatementSyntax _switchSyntax;
protected readonly GeneratedLabelSymbol _breakLabel;
protected readonly SwitchStatementSyntax SwitchSyntax;
private readonly GeneratedLabelSymbol _breakLabel;
private BoundExpression _switchGoverningExpression;
private DiagnosticBag _switchGoverningDiagnostics;
protected SwitchBinder(Binder next, SwitchStatementSyntax switchSyntax)
: base(next)
{
_switchSyntax = switchSyntax;
SwitchSyntax = switchSyntax;
_breakLabel = new GeneratedLabelSymbol("break");
}
......@@ -31,9 +31,11 @@ internal static SwitchBinder Create(Binder next, SwitchStatementSyntax switchSyn
{
var parseOptions = switchSyntax?.SyntaxTree?.Options as CSharpParseOptions;
return
// in C# 6 and earlier, we use the old binder. In C# 7 and later, we use the new binder which
// In C# 6 and earlier, we use the old binder. In C# 7 and later, we use the new binder which
// is capable of binding both the old and new syntax. However, the new binder does not yet
// lead to a translation that fully supports edit-and-continue.
// lead to a translation that fully supports edit-and-continue, so it delegates to the C# 6
// binder when it can. The "typeswitch" feature flag forces the use of the C# 7 switch binder
// for all operations; we use it to enhance test coverage.
(parseOptions?.IsFeatureEnabled(MessageID.IDS_FeaturePatternMatching) != false || parseOptions?.Features.ContainsKey("typeswitch") != false)
? new PatternSwitchBinder(next, switchSyntax)
: new SwitchBinder(next, switchSyntax);
......@@ -41,7 +43,7 @@ internal static SwitchBinder Create(Binder next, SwitchStatementSyntax switchSyn
protected bool PatternsEnabled =>
((CSharpParseOptions)this._switchSyntax.SyntaxTree.Options)?.IsFeatureEnabled(MessageID.IDS_FeaturePatternMatching) != false;
((CSharpParseOptions)SwitchSyntax.SyntaxTree.Options)?.IsFeatureEnabled(MessageID.IDS_FeaturePatternMatching) != false;
protected BoundExpression SwitchGoverningExpression
{
......@@ -49,12 +51,10 @@ protected BoundExpression SwitchGoverningExpression
{
if (_switchGoverningExpression == null)
{
var switchGoverningDiagnostics = new DiagnosticBag();
var boundSwitchExpression = BindSwitchExpression(_switchSyntax.Expression, switchGoverningDiagnostics);
_switchGoverningDiagnostics = switchGoverningDiagnostics;
Interlocked.CompareExchange(ref _switchGoverningExpression, boundSwitchExpression, null);
EnsureSwitchGoverningExpressionAndDiagnosticsBound();
}
Debug.Assert(_switchGoverningExpression != null);
return _switchGoverningExpression;
}
}
......@@ -65,11 +65,19 @@ protected DiagnosticBag SwitchGoverningDiagnostics
{
get
{
var discarded = SwitchGoverningExpression;
EnsureSwitchGoverningExpressionAndDiagnosticsBound();
return _switchGoverningDiagnostics;
}
}
private void EnsureSwitchGoverningExpressionAndDiagnosticsBound()
{
var switchGoverningDiagnostics = new DiagnosticBag();
var boundSwitchExpression = BindSwitchExpression(SwitchSyntax.Expression, switchGoverningDiagnostics);
_switchGoverningDiagnostics = switchGoverningDiagnostics;
Interlocked.CompareExchange(ref _switchGoverningExpression, boundSwitchExpression, null);
}
// Dictionary for the switch case/default labels.
// Case labels with a non-null constant value are indexed on their ConstantValue.
// Default label(s) are indexed on a special DefaultKey object.
......@@ -102,7 +110,7 @@ protected DiagnosticBag SwitchGoverningDiagnostics
object key;
var constantValue = label.SwitchCaseLabelConstant;
if (constantValue != (object)null && !constantValue.IsBad)
if ((object)constantValue != null && !constantValue.IsBad)
{
// Case labels with a non-null constant value are indexed on their ConstantValue.
key = KeyForConstant(constantValue);
......@@ -118,14 +126,11 @@ protected DiagnosticBag SwitchGoverningDiagnostics
key = label.IdentifierNodeOrToken.AsNode();
}
// If there is a duplicate label, ignore it. It will be reported when binding the switch label.
if (!map.ContainsKey(key))
{
map.Add(key, label);
}
else
{
// If there is a duplicate label, ignore it. It will be reported when binding the switch label.
}
}
return map;
......@@ -135,7 +140,7 @@ override protected ImmutableArray<LocalSymbol> BuildLocals()
{
var builder = ArrayBuilder<LocalSymbol>.GetInstance();
foreach (var section in _switchSyntax.Sections)
foreach (var section in SwitchSyntax.Sections)
{
builder.AddRange(BuildLocals(section.Statements));
}
......@@ -147,7 +152,7 @@ protected override ImmutableArray<LocalFunctionSymbol> BuildLocalFunctions()
{
var builder = ArrayBuilder<LocalFunctionSymbol>.GetInstance();
foreach (var section in _switchSyntax.Sections)
foreach (var section in SwitchSyntax.Sections)
{
builder.AddRange(BuildLocalFunctions(section.Statements));
}
......@@ -178,11 +183,10 @@ protected override ImmutableArray<LabelSymbol> BuildLabels()
ArrayBuilder<LabelSymbol> labels = ArrayBuilder<LabelSymbol>.GetInstance();
DiagnosticBag tempDiagnosticBag = DiagnosticBag.GetInstance();
TypeSymbol switchGoverningType = this.SwitchGoverningType;
foreach (var section in _switchSyntax.Sections)
foreach (var section in SwitchSyntax.Sections)
{
// add switch case/default labels
BuildSwitchLabels(switchGoverningType, section.Labels, GetBinder(section), labels, tempDiagnosticBag);
BuildSwitchLabels(section.Labels, GetBinder(section), labels, tempDiagnosticBag);
// add regular labels from the statements in the switch section
BuildLabels(section.Statements, ref labels);
......@@ -200,7 +204,7 @@ internal override bool IsLabelsScopeBinder
}
}
private void BuildSwitchLabels(TypeSymbol switchGoverningType, SyntaxList<SwitchLabelSyntax> labelsSyntax, Binder sectionBinder, ArrayBuilder<LabelSymbol> labels, DiagnosticBag tempDiagnosticBag)
private void BuildSwitchLabels(SyntaxList<SwitchLabelSyntax> labelsSyntax, Binder sectionBinder, ArrayBuilder<LabelSymbol> labels, DiagnosticBag tempDiagnosticBag)
{
// add switch case/default labels
foreach (var labelSyntax in labelsSyntax)
......@@ -213,7 +217,7 @@ private void BuildSwitchLabels(TypeSymbol switchGoverningType, SyntaxList<Switch
var caseLabel = (CaseSwitchLabelSyntax)labelSyntax;
Debug.Assert(caseLabel.Value != null);
var boundLabelExpression = sectionBinder.BindValue(caseLabel.Value, tempDiagnosticBag, BindValueKind.RValue);
boundLabelExpression = ConvertCaseExpression(switchGoverningType, labelSyntax, boundLabelExpression, sectionBinder, ref boundLabelConstantOpt, tempDiagnosticBag);
boundLabelExpression = ConvertCaseExpression(labelSyntax, boundLabelExpression, sectionBinder, ref boundLabelConstantOpt, tempDiagnosticBag);
break;
default:
......@@ -226,7 +230,7 @@ private void BuildSwitchLabels(TypeSymbol switchGoverningType, SyntaxList<Switch
}
}
protected BoundExpression ConvertCaseExpression(TypeSymbol switchGoverningType, CSharpSyntaxNode node, BoundExpression caseExpression, Binder sectionBinder, ref ConstantValue constantValueOpt, DiagnosticBag diagnostics, bool isGotoCaseExpr = false)
protected BoundExpression ConvertCaseExpression(CSharpSyntaxNode node, BoundExpression caseExpression, Binder sectionBinder, ref ConstantValue constantValueOpt, DiagnosticBag diagnostics, bool isGotoCaseExpr = false)
{
if (isGotoCaseExpr)
{
......@@ -242,27 +246,27 @@ protected BoundExpression ConvertCaseExpression(TypeSymbol switchGoverningType,
// instead of an error. See test "CS0469_NoImplicitConversionWarning".
HashSet<DiagnosticInfo> useSiteDiagnostics = null;
Conversion conversion = Conversions.ClassifyConversionFromExpression(caseExpression, switchGoverningType, ref useSiteDiagnostics);
Conversion conversion = Conversions.ClassifyConversionFromExpression(caseExpression, SwitchGoverningType, ref useSiteDiagnostics);
diagnostics.Add(node, useSiteDiagnostics);
if (!conversion.IsValid)
{
GenerateImplicitConversionError(diagnostics, node, conversion, caseExpression, switchGoverningType);
GenerateImplicitConversionError(diagnostics, node, conversion, caseExpression, SwitchGoverningType);
}
else if (!conversion.IsImplicit)
{
diagnostics.Add(ErrorCode.WRN_GotoCaseShouldConvert, node.Location, switchGoverningType);
diagnostics.Add(ErrorCode.WRN_GotoCaseShouldConvert, node.Location, SwitchGoverningType);
}
caseExpression = CreateConversion(caseExpression, conversion, switchGoverningType, diagnostics);
caseExpression = CreateConversion(caseExpression, conversion, SwitchGoverningType, diagnostics);
}
return ConvertPatternExpression(switchGoverningType, node, caseExpression, ref constantValueOpt, diagnostics);
return ConvertPatternExpression(SwitchGoverningType, node, caseExpression, ref constantValueOpt, diagnostics);
}
private static readonly object s_nullKey = new object();
protected static object KeyForConstant(ConstantValue constantValue)
{
Debug.Assert(constantValue != (object)null);
Debug.Assert((object)constantValue != null);
return constantValue.IsNull ? s_nullKey : constantValue.Value;
}
......@@ -273,7 +277,7 @@ protected SourceLabelSymbol FindMatchingSwitchCaseLabel(ConstantValue constantVa
// Invalid case labels (with null constant value) are indexed on the label syntax.
object key;
if (constantValue != (object)null && !constantValue.IsBad)
if ((object)constantValue != null && !constantValue.IsBad)
{
key = KeyForConstant(constantValue);
}
......@@ -303,7 +307,7 @@ private SourceLabelSymbol FindMatchingSwitchLabel(object key)
SourceLabelSymbol label;
if (labelsMap.TryGetValue(key, out label))
{
Debug.Assert(label != (object)null);
Debug.Assert((object)label != null);
return label;
}
}
......@@ -313,7 +317,7 @@ private SourceLabelSymbol FindMatchingSwitchLabel(object key)
internal override ImmutableArray<LocalSymbol> GetDeclaredLocalsForScope(CSharpSyntaxNode scopeDesignator)
{
if (_switchSyntax == scopeDesignator)
if (SwitchSyntax == scopeDesignator)
{
return this.Locals;
}
......@@ -323,7 +327,7 @@ internal override ImmutableArray<LocalSymbol> GetDeclaredLocalsForScope(CSharpSy
internal override ImmutableArray<LocalFunctionSymbol> GetDeclaredLocalFunctionsForScope(CSharpSyntaxNode scopeDesignator)
{
if (_switchSyntax == scopeDesignator)
if (SwitchSyntax == scopeDesignator)
{
return this.LocalFunctions;
}
......@@ -335,7 +339,7 @@ internal override SyntaxNode ScopeDesignator
{
get
{
return _switchSyntax;
return SwitchSyntax;
}
}
......@@ -343,7 +347,7 @@ internal override SyntaxNode ScopeDesignator
internal override BoundStatement BindSwitchExpressionAndSections(SwitchStatementSyntax node, Binder originalBinder, DiagnosticBag diagnostics)
{
Debug.Assert(_switchSyntax.Equals(node));
Debug.Assert(SwitchSyntax.Equals(node));
// Bind switch expression and set the switch governing type.
var boundSwitchExpression = this.SwitchGoverningExpression;
......@@ -388,7 +392,7 @@ private BoundExpression BindSwitchExpression(ExpressionSyntax node, DiagnosticBa
//
// The "x" in "switch(x)" refers to this.x, not the local x that is in scope inside the switch block.
Debug.Assert(node == _switchSyntax.Expression);
Debug.Assert(node == SwitchSyntax.Expression);
var binder = this.GetBinder(node);
Debug.Assert(binder != null);
......@@ -475,7 +479,7 @@ private LabelSymbol BindConstantJumpTarget(ConstantValue constantValue, CSharpSy
// value, that label is set as the target.
SourceLabelSymbol labelSymbol = FindMatchingSwitchCaseLabel(constantValue, syntax);
if (labelSymbol != (object)null)
if ((object)labelSymbol != null)
{
boundLabel = labelSymbol;
}
......@@ -485,7 +489,7 @@ private LabelSymbol BindConstantJumpTarget(ConstantValue constantValue, CSharpSy
// switch statement and set that as the target label.
labelSymbol = GetDefaultLabel();
if (labelSymbol != (object)null)
if ((object)labelSymbol != null)
{
boundLabel = labelSymbol;
}
......@@ -572,7 +576,7 @@ private BoundSwitchLabel BindSwitchSectionLabel(SwitchLabelSyntax node, Binder s
var caseLabelSyntax = (CaseSwitchLabelSyntax)node;
// Bind the label case expression
boundLabelExpressionOpt = sectionBinder.BindValue(caseLabelSyntax.Value, diagnostics, BindValueKind.RValue);
boundLabelExpressionOpt = ConvertCaseExpression(switchGoverningType, caseLabelSyntax, boundLabelExpressionOpt, sectionBinder, ref labelExpressionConstant, diagnostics);
boundLabelExpressionOpt = ConvertCaseExpression(caseLabelSyntax, boundLabelExpressionOpt, sectionBinder, ref labelExpressionConstant, diagnostics);
// Check for bind errors
hasErrors = hasErrors || boundLabelExpressionOpt.HasAnyErrors;
......@@ -585,7 +589,7 @@ private BoundSwitchLabel BindSwitchSectionLabel(SwitchLabelSyntax node, Binder s
hasErrors = true;
}
if (!hasErrors && labelExpressionConstant != (object)null && FindMatchingSwitchCaseLabel(labelExpressionConstant, caseLabelSyntax) != label)
if (!hasErrors && (object)labelExpressionConstant != null && FindMatchingSwitchCaseLabel(labelExpressionConstant, caseLabelSyntax) != label)
{
diagnostics.Add(ErrorCode.ERR_DuplicateCaseLabel, node.Location, labelExpressionConstant?.GetValueToDisplay() ?? label.Name);
hasErrors = true;
......@@ -629,7 +633,6 @@ internal BoundStatement BindGotoCaseOrDefault(GotoStatementSyntax node, Binder g
if (!node.HasErrors)
{
ConstantValue gotoCaseExpressionConstant = null;
TypeSymbol switchGoverningType = SwitchGoverningType;
bool hasErrors = false;
SourceLabelSymbol matchedLabelSymbol;
......@@ -648,7 +651,7 @@ internal BoundStatement BindGotoCaseOrDefault(GotoStatementSyntax node, Binder g
// Bind the goto case expression
gotoCaseExpressionOpt = gotoBinder.BindValue(node.Expression, diagnostics, BindValueKind.RValue);
gotoCaseExpressionOpt = ConvertCaseExpression(switchGoverningType, node, gotoCaseExpressionOpt, gotoBinder,
gotoCaseExpressionOpt = ConvertCaseExpression(node, gotoCaseExpressionOpt, gotoBinder,
ref gotoCaseExpressionConstant, diagnostics, isGotoCaseExpr: true);
// Check for bind errors
......@@ -670,7 +673,7 @@ internal BoundStatement BindGotoCaseOrDefault(GotoStatementSyntax node, Binder g
matchedLabelSymbol = GetDefaultLabel();
}
if (matchedLabelSymbol == (object)null)
if ((object)matchedLabelSymbol == null)
{
if (!hasErrors)
{
......
......@@ -33,11 +33,11 @@ private bool IsPatternSwitch
{
if (_isPatternSwitch == null)
{
var parseOptions = _switchSyntax?.SyntaxTree?.Options as CSharpParseOptions;
var parseOptions = SwitchSyntax?.SyntaxTree?.Options as CSharpParseOptions;
_isPatternSwitch =
(parseOptions?.IsFeatureEnabled(MessageID.IDS_FeaturePatternMatching) != false &&
(parseOptions?.Features.ContainsKey("typeswitch") == true ||
IsPatternSwitchSyntax(_switchSyntax) ||
IsPatternSwitchSyntax(SwitchSyntax) ||
!SwitchGoverningType.IsValidV6SwitchGoverningType()));
}
......@@ -63,7 +63,7 @@ internal override BoundStatement BindSwitchExpressionAndSections(SwitchStatement
// If it is a valid C# 6 switch statement, we use the old binder to bind it.
if (!IsPatternSwitch) return base.BindSwitchExpressionAndSections(node, originalBinder, diagnostics);
Debug.Assert(_switchSyntax.Equals(node));
Debug.Assert(SwitchSyntax.Equals(node));
// Bind switch expression and set the switch governing type.
var boundSwitchExpression = SwitchGoverningExpression;
......@@ -135,7 +135,7 @@ private ImmutableArray<BoundPatternSwitchSection> BindPatternSwitchSections(Boun
node, boundSwitchExpression, boundSwitchExpression.Type, caseLabelSyntax.Value, node.HasErrors, diagnostics, out wasExpression, wasSwitchCase: true);
bool hasErrors = pattern.HasErrors;
var constantValue = pattern.ConstantValue;
if (!hasErrors && constantValue != (object)null && this.FindMatchingSwitchCaseLabel(constantValue, caseLabelSyntax) != label)
if (!hasErrors && (object)constantValue != null && this.FindMatchingSwitchCaseLabel(constantValue, caseLabelSyntax) != label)
{
diagnostics.Add(ErrorCode.ERR_DuplicateCaseLabel, node.Location, pattern.ConstantValue.GetValueToDisplay() ?? label.Name);
hasErrors = true;
......
......@@ -9,6 +9,7 @@
using System.Collections.Generic;
using System;
using Roslyn.Utilities;
using Microsoft.CodeAnalysis.CSharp;
namespace Microsoft.CodeAnalysis.CSharp
{
......@@ -19,17 +20,16 @@ public override BoundNode VisitPatternSwitchStatement(BoundPatternSwitchStatemen
_factory.Syntax = node.Syntax;
var pslr = new PatternSwitchLocalRewriter(this, node);
var expression = VisitExpression(node.Expression);
var result = ArrayBuilder<BoundStatement>.GetInstance();
// output the decision tree part
pslr.LowerDecisionTree(expression, node.DecisionTree);
var result = ArrayBuilder<BoundStatement>.GetInstance();
result.AddRange(pslr.LoweredDecisionTree);
pslr.LowerDecisionTree(expression, node.DecisionTree, result);
// if the endpoint is reachable, we exit the switch
if (!node.DecisionTree.MatchIsComplete) result.Add(_factory.Goto(node.BreakLabel));
// at this point the end of result is unreachable.
// output the sections of code (that were reachable)
// output the sections of code
foreach (var section in node.SwitchSections)
{
// Start with the part of the decision tree that is in scope of the section variables.
......@@ -60,7 +60,8 @@ private class PatternSwitchLocalRewriter
public readonly HashSet<LocalSymbol> DeclaredTempSet = new HashSet<LocalSymbol>();
public readonly ArrayBuilder<LocalSymbol> DeclaredTemps = ArrayBuilder<LocalSymbol>.GetInstance();
public readonly Dictionary<BoundPatternSwitchSection, ArrayBuilder<BoundStatement>> SwitchSections = new Dictionary<BoundPatternSwitchSection, ArrayBuilder<BoundStatement>>();
public ArrayBuilder<BoundStatement> LoweredDecisionTree = ArrayBuilder<BoundStatement>.GetInstance();
private ArrayBuilder<BoundStatement> _loweredDecisionTree = ArrayBuilder<BoundStatement>.GetInstance();
private readonly SyntheticBoundNodeFactory _factory;
public PatternSwitchLocalRewriter(LocalRewriter localRewriter, BoundPatternSwitchStatement node)
......@@ -76,15 +77,15 @@ public PatternSwitchLocalRewriter(LocalRewriter localRewriter, BoundPatternSwitc
/// <summary>
/// Lower the given decision tree into the given statement builder.
/// </summary>
private void LowerDecisionTree(BoundExpression expression, DecisionTree decisionTree, ArrayBuilder<BoundStatement> loweredDecisionTree)
public void LowerDecisionTree(BoundExpression expression, DecisionTree decisionTree, ArrayBuilder<BoundStatement> loweredDecisionTree)
{
var oldLoweredDecisionTree = this.LoweredDecisionTree;
this.LoweredDecisionTree = loweredDecisionTree;
var oldLoweredDecisionTree = this._loweredDecisionTree;
this._loweredDecisionTree = loweredDecisionTree;
LowerDecisionTree(expression, decisionTree);
this.LoweredDecisionTree = oldLoweredDecisionTree;
this._loweredDecisionTree = oldLoweredDecisionTree;
}
public void LowerDecisionTree(BoundExpression expression, DecisionTree decisionTree)
private void LowerDecisionTree(BoundExpression expression, DecisionTree decisionTree)
{
if (decisionTree == null) return;
......@@ -96,7 +97,7 @@ public void LowerDecisionTree(BoundExpression expression, DecisionTree decisionT
// Store the input expression into a temp
if (decisionTree.Expression != expression)
{
LoweredDecisionTree.Add(_factory.Assignment(decisionTree.Expression, expression));
_loweredDecisionTree.Add(_factory.Assignment(decisionTree.Expression, expression));
}
if (DeclaredTempSet.Add(decisionTree.Temp))
......@@ -171,10 +172,10 @@ private void LowerDecisionTree(DecisionTree.ByType byType)
BoundExpression notNull = byType.Type.IsNullableType()
? LocalRewriter.RewriteNullableNullEquality(_factory.Syntax, BinaryOperatorKind.NullableNullNotEqual, byType.Expression, nullValue, _factory.SpecialType(SpecialType.System_Boolean))
: _factory.ObjectNotEqual(byType.Expression, nullValue);
LoweredDecisionTree.Add(_factory.ConditionalGoto(notNull, notNullLabel, true));
_loweredDecisionTree.Add(_factory.ConditionalGoto(notNull, notNullLabel, true));
LowerDecisionTree(byType.Expression, byType.WhenNull);
if (byType.WhenNull?.MatchIsComplete != true) LoweredDecisionTree.Add(_factory.Goto(defaultLabel));
LoweredDecisionTree.Add(_factory.Label(notNullLabel));
if (byType.WhenNull?.MatchIsComplete != true) _loweredDecisionTree.Add(_factory.Goto(defaultLabel));
_loweredDecisionTree.Add(_factory.Label(notNullLabel));
}
else
{
......@@ -188,13 +189,13 @@ private void LowerDecisionTree(DecisionTree.ByType byType)
var decision = td.Value;
var failLabel = _factory.GenerateLabel("failedDecision");
var testAndCopy = TypeTestAndCopyToTemp(byType.Expression, decision.Expression);
LoweredDecisionTree.Add(_factory.ConditionalGoto(testAndCopy, failLabel, false));
_loweredDecisionTree.Add(_factory.ConditionalGoto(testAndCopy, failLabel, false));
LowerDecisionTree(decision.Expression, decision);
LoweredDecisionTree.Add(_factory.Label(failLabel));
_loweredDecisionTree.Add(_factory.Label(failLabel));
}
// finally, the default for when no type matches
LoweredDecisionTree.Add(_factory.Label(defaultLabel));
_loweredDecisionTree.Add(_factory.Label(defaultLabel));
LowerDecisionTree(byType.Expression, byType.Default);
}
}
......@@ -219,6 +220,12 @@ private void LowerDecisionTree(DecisionTree.ByValue byValue)
return;
}
if (byValue.ValueAndDecision.Count == 0)
{
LowerDecisionTree(byValue.Expression, byValue.Default);
return;
}
switch (byValue.Type.SpecialType)
{
case SpecialType.System_Byte:
......@@ -268,12 +275,10 @@ private void LowerConstantValueDecision(DecisionTree.ByValue byValue)
if (byValue.ValueAndDecision.TryGetValue(value, out onValue))
{
LowerDecisionTree(byValue.Expression, onValue);
if (!onValue.MatchIsComplete) LowerDecisionTree(byValue.Expression, byValue.Default);
}
else
{
LowerDecisionTree(byValue.Expression, byValue.Default);
if (onValue.MatchIsComplete) return;
}
LowerDecisionTree(byValue.Expression, byValue.Default);
}
private void LowerDecisionTree(DecisionTree.Guarded guarded)
......@@ -286,13 +291,13 @@ private void LowerDecisionTree(DecisionTree.Guarded guarded)
// unconditional
if (guarded.Bindings.IsDefaultOrEmpty)
{
LoweredDecisionTree.Add(_factory.Goto(targetLabel));
_loweredDecisionTree.Add(_factory.Goto(targetLabel));
}
else
{
// with bindings
var matched = _factory.GenerateLabel("matched");
LoweredDecisionTree.Add(_factory.Goto(matched));
_loweredDecisionTree.Add(_factory.Goto(matched));
sectionBuilder.Add(_factory.Label(matched));
AddBindings(sectionBuilder, guarded.Bindings);
sectionBuilder.Add(_factory.Goto(targetLabel));
......@@ -301,13 +306,13 @@ private void LowerDecisionTree(DecisionTree.Guarded guarded)
else
{
var checkGuard = _factory.GenerateLabel("checkGuard");
LoweredDecisionTree.Add(_factory.Goto(checkGuard));
_loweredDecisionTree.Add(_factory.Goto(checkGuard));
sectionBuilder.Add(_factory.Label(checkGuard));
AddBindings(sectionBuilder, guarded.Bindings);
sectionBuilder.Add(_factory.ConditionalGoto(LocalRewriter.VisitExpression(guarded.Guard), targetLabel, true));
var guardFailed = _factory.GenerateLabel("guardFailed");
sectionBuilder.Add(_factory.Goto(guardFailed));
LoweredDecisionTree.Add(_factory.Label(guardFailed));
_loweredDecisionTree.Add(_factory.Label(guardFailed));
}
}
......@@ -382,7 +387,7 @@ private void LowerBasicSwitch(DecisionTree.ByValue byValue)
constantTarget,
ImmutableArray<LocalSymbol>.Empty, ImmutableArray<LocalFunctionSymbol>.Empty,
rewrittenSections, noValueMatches, stringEquality);
LoweredDecisionTree.Add(switchStatement);
_loweredDecisionTree.Add(switchStatement);
// The bound switch statement implicitly defines the label noValueMatches at the end, so we do not add it explicitly.
LowerDecisionTree(byValue.Expression, byValue.Default);
}
......@@ -393,8 +398,8 @@ private void LowerBooleanSwitch(DecisionTree.ByValue byValue)
{
case 0:
{
LowerDecisionTree(byValue.Expression, byValue.Default);
break;
// this should have been handled in the caller.
throw ExceptionUtilities.Unreachable;
}
case 1:
{
......@@ -403,10 +408,10 @@ private void LowerBooleanSwitch(DecisionTree.ByValue byValue)
if (!onBoolean) byValue.ValueAndDecision.TryGetValue(false, out decision);
Debug.Assert(decision != null);
var onOther = _factory.GenerateLabel("on" + !onBoolean);
LoweredDecisionTree.Add(_factory.ConditionalGoto(byValue.Expression, onOther, !onBoolean));
_loweredDecisionTree.Add(_factory.ConditionalGoto(byValue.Expression, onOther, !onBoolean));
LowerDecisionTree(byValue.Expression, decision);
// if we fall through here, that means the match was not complete and we invoke the default part
LoweredDecisionTree.Add(_factory.Label(onOther));
_loweredDecisionTree.Add(_factory.Label(onOther));
LowerDecisionTree(byValue.Expression, byValue.Default);
break;
}
......@@ -418,12 +423,12 @@ private void LowerBooleanSwitch(DecisionTree.ByValue byValue)
Debug.Assert(hasTrue && hasFalse);
var tryAnother = _factory.GenerateLabel("tryAnother");
var onFalse = _factory.GenerateLabel("onFalse");
LoweredDecisionTree.Add(_factory.ConditionalGoto(byValue.Expression, onFalse, false));
_loweredDecisionTree.Add(_factory.ConditionalGoto(byValue.Expression, onFalse, false));
LowerDecisionTree(byValue.Expression, trueDecision);
LoweredDecisionTree.Add(_factory.Goto(tryAnother));
LoweredDecisionTree.Add(_factory.Label(onFalse));
_loweredDecisionTree.Add(_factory.Goto(tryAnother));
_loweredDecisionTree.Add(_factory.Label(onFalse));
LowerDecisionTree(byValue.Expression, falseDecision);
LoweredDecisionTree.Add(_factory.Label(tryAnother));
_loweredDecisionTree.Add(_factory.Label(tryAnother));
// if both true and false (i.e. all values) are fully handled, there should be no default.
Debug.Assert(!trueDecision.MatchIsComplete || !falseDecision.MatchIsComplete || byValue.Default == null);
LowerDecisionTree(byValue.Expression, byValue.Default);
......@@ -440,7 +445,7 @@ private void LowerBooleanSwitch(DecisionTree.ByValue byValue)
/// </summary>
private void LowerOtherSwitch(DecisionTree.ByValue byValue)
{
// PROTOTYPE(typeswitch): we do not yet support switch on float, double, or decimal.
this.LocalRewriter._diagnostics.Add(ErrorCode.ERR_FeatureIsUnimplemented, _factory.Syntax.GetLocation(), "switch on float, double, or decimal");
throw new NotImplementedException();
}
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册