private BoundPatternSwitchSection BindPatternSwitchSection( BoundExpression boundSwitchExpression, SwitchSectionSyntax node, Binder originalBinder, ref BoundPatternSwitchLabel defaultLabel, DiagnosticBag diagnostics) { // Bind match section labels var boundLabelsBuilder = ArrayBuilder <BoundPatternSwitchLabel> .GetInstance(); var sectionBinder = originalBinder.GetBinder(node); // this binder can bind pattern variables from the section. Debug.Assert(sectionBinder != null); var labelsByNode = LabelsByNode; foreach (var labelSyntax in node.Labels) { LabelSymbol label = labelsByNode[labelSyntax]; BoundPatternSwitchLabel boundLabel = BindPatternSwitchSectionLabel(sectionBinder, boundSwitchExpression, labelSyntax, label, ref defaultLabel, diagnostics); boundLabelsBuilder.Add(boundLabel); } // Bind switch section statements var boundStatementsBuilder = ArrayBuilder <BoundStatement> .GetInstance(); foreach (var statement in node.Statements) { boundStatementsBuilder.Add(sectionBinder.BindStatement(statement, diagnostics)); } return(new BoundPatternSwitchSection(node, sectionBinder.GetDeclaredLocalsForScope(node), boundLabelsBuilder.ToImmutableAndFree(), boundStatementsBuilder.ToImmutableAndFree())); }
/// <summary> /// Bind the pattern switch labels, reporting in the process which cases are subsumed. The strategy /// implemented with the help of <see cref="SubsumptionDiagnosticBuilder"/>, is to start with an empty /// decision tree, and for each case we visit the decision tree to see if the case is subsumed. If it /// is, we report an error. If it is not subsumed and there is no guard expression, we then add it to /// the decision tree. /// </summary> private ImmutableArray <BoundPatternSwitchSection> BindPatternSwitchSections( Binder originalBinder, out BoundPatternSwitchLabel defaultLabel, out bool isComplete, out bool someCaseMatches, DiagnosticBag diagnostics) { defaultLabel = null; // someCaseMatches will be set to true if some single case label would handle all inputs someCaseMatches = false; // Bind match sections var boundPatternSwitchSectionsBuilder = ArrayBuilder <BoundPatternSwitchSection> .GetInstance(); SubsumptionDiagnosticBuilder subsumption = new SubsumptionDiagnosticBuilder(ContainingMemberOrLambda, SwitchSyntax, this.Conversions, SwitchGoverningType); foreach (var sectionSyntax in SwitchSyntax.Sections) { var section = BindPatternSwitchSection(sectionSyntax, originalBinder, ref defaultLabel, ref someCaseMatches, subsumption, diagnostics); boundPatternSwitchSectionsBuilder.Add(section); } isComplete = defaultLabel != null || subsumption.IsComplete || someCaseMatches; return(boundPatternSwitchSectionsBuilder.ToImmutableAndFree()); }
/// <summary> /// Bind the pattern switch labels, reporting in the process which cases are subsumed. The strategy, /// implemented with the help of <see cref="SubsumptionDiagnosticBuilder"/>, is to start with an empty /// decision tree, and for each case we visit the decision tree to see if the case is subsumed. If it /// is, we report an error. If it is not subsumed and there is no guard expression, we then add it to /// the decision tree. /// </summary> private ImmutableArray <BoundPatternSwitchSection> BindPatternSwitchSections( Binder originalBinder, out BoundPatternSwitchLabel defaultLabel, out bool isComplete, DiagnosticBag diagnostics) { defaultLabel = null; // true if we found a case label whose value is the same as the input expression's constant value bool someValueMatched = false; // Bind match sections var boundPatternSwitchSectionsBuilder = ArrayBuilder <BoundPatternSwitchSection> .GetInstance(); SubsumptionDiagnosticBuilder subsumption = new SubsumptionDiagnosticBuilder(ContainingMemberOrLambda, SwitchSyntax, this.Conversions, SwitchGoverningExpression); foreach (var sectionSyntax in SwitchSyntax.Sections) { boundPatternSwitchSectionsBuilder.Add(BindPatternSwitchSection( SwitchGoverningExpression, sectionSyntax, originalBinder, ref defaultLabel, ref someValueMatched, subsumption, diagnostics)); } isComplete = defaultLabel != null || subsumption.IsComplete || someValueMatched; return(boundPatternSwitchSectionsBuilder.ToImmutableAndFree()); }
private BoundPatternSwitchLabel BindPatternSwitchSectionLabel( Binder sectionBinder, BoundExpression boundSwitchExpression, SwitchLabelSyntax node, LabelSymbol label, ref BoundPatternSwitchLabel defaultLabel, DiagnosticBag diagnostics) { switch (node.Kind()) { case SyntaxKind.CaseSwitchLabel: { var caseLabelSyntax = (CaseSwitchLabelSyntax)node; bool wasExpression; var pattern = sectionBinder.BindConstantPattern( node, boundSwitchExpression, boundSwitchExpression.Type, caseLabelSyntax.Value, node.HasErrors, diagnostics, out wasExpression, wasSwitchCase: true); bool hasErrors = pattern.HasErrors; var constantValue = pattern.ConstantValue; if (!hasErrors && (object)constantValue != null && pattern.Value.Type == SwitchGoverningType && this.FindMatchingSwitchCaseLabel(constantValue, caseLabelSyntax) != label) { diagnostics.Add(ErrorCode.ERR_DuplicateCaseLabel, node.Location, pattern.ConstantValue.GetValueToDisplay() ?? label.Name); hasErrors = true; } return(new BoundPatternSwitchLabel(node, label, pattern, null, hasErrors)); } case SyntaxKind.DefaultSwitchLabel: { var defaultLabelSyntax = (DefaultSwitchLabelSyntax)node; var pattern = new BoundWildcardPattern(node); bool hasErrors = pattern.HasErrors; if (defaultLabel != null) { diagnostics.Add(ErrorCode.ERR_DuplicateCaseLabel, node.Location, label.Name); hasErrors = true; } // Note that this is semantically last! The caller will place it in the decision tree // in the final position. defaultLabel = new BoundPatternSwitchLabel(node, label, pattern, null, hasErrors); return(defaultLabel); } case SyntaxKind.CasePatternSwitchLabel: { var matchLabelSyntax = (CasePatternSwitchLabelSyntax)node; var pattern = sectionBinder.BindPattern( matchLabelSyntax.Pattern, boundSwitchExpression, boundSwitchExpression.Type, node.HasErrors, diagnostics, wasSwitchCase: true); return(new BoundPatternSwitchLabel(node, label, pattern, matchLabelSyntax.WhenClause != null ? sectionBinder.BindBooleanExpression(matchLabelSyntax.WhenClause.Condition, diagnostics) : null, node.HasErrors)); } default: throw ExceptionUtilities.UnexpectedValue(node); } }
/// <summary> /// Add the case label to the subsumption tree. Return true if the label is reachable /// (not subsumed) given the governing expression's type and previously added labels. /// </summary> internal bool AddLabel(BoundPatternSwitchLabel label, DiagnosticBag diagnostics) { // Use site diagnostics are reported (and cleared) by this method. // So they should be empty when we start. Debug.Assert(_useSiteDiagnostics.Count == 0); if (label.Syntax.Kind() == SyntaxKind.DefaultSwitchLabel) { // the default case label is always considered reachable. return(true); } try { // For purposes of subsumption, we do not take into consideration the value // of the input expression. Therefore we consider null possible if the type permits. var inputCouldBeNull = _subsumptionTree.Type.CanContainNull(); var subsumedErrorCode = CheckSubsumed(label.Pattern, _subsumptionTree, inputCouldBeNull: inputCouldBeNull); if (subsumedErrorCode != 0) { if (!label.HasErrors && subsumedErrorCode != ErrorCode.ERR_NoImplicitConvCast) { diagnostics.Add(subsumedErrorCode, label.Pattern.Syntax.Location); } return(false); } var guardAlwaysSatisfied = label.Guard == null || label.Guard.ConstantValue == ConstantValue.True; if (guardAlwaysSatisfied) { // Only unconditional switch labels contribute to subsumption if (AddToDecisionTree(_subsumptionTree, null, label) == null && !label.Pattern.HasErrors) { // Since the pattern was not subsumed, we should be able to add it to the decision tree throw ExceptionUtilities.Unreachable; } } return(true); } finally { // report the use-site diagnostics diagnostics.Add(label.Syntax.Location, _useSiteDiagnostics); _useSiteDiagnostics.Clear(); } }
public Guarded( BoundExpression expression, TypeSymbol type, ImmutableArray <KeyValuePair <BoundExpression, BoundExpression> > bindings, SyntaxNode sectionSyntax, BoundExpression guard, BoundPatternSwitchLabel label) : base(expression, type, null) { this.Guard = guard; this.Label = label; this.Bindings = bindings; this.SectionSyntax = sectionSyntax; Debug.Assert(guard?.ConstantValue != ConstantValue.False); base.MatchIsComplete = (guard == null) || (guard.ConstantValue == ConstantValue.True); }
/// <summary> /// Bind a pattern switch label in order to force inference of the type of pattern variables. /// </summary> internal override void BindPatternSwitchLabelForInference(CasePatternSwitchLabelSyntax node, DiagnosticBag diagnostics) { // node should be a label of this switch statement. Debug.Assert(this.SwitchSyntax == node.Parent.Parent); // This simulates enough of the normal binding path of a switch statement to cause // the label's pattern variables to have their types inferred, if necessary. // It also binds the when clause, and therefore any pattern and out variables there. BoundPatternSwitchLabel defaultLabel = null; BindPatternSwitchSectionLabel( sectionBinder: GetBinder(node.Parent), node: node, label: LabelsByNode[node], defaultLabel: ref defaultLabel, diagnostics: diagnostics); }
private DecisionTree LowerToDecisionTree( BoundExpression loweredExpression, BoundPatternSwitchStatement node) { var loweredDecisionTree = DecisionTree.Create(loweredExpression, loweredExpression.Type, _enclosingSymbol); BoundPatternSwitchLabel defaultLabel = null; SyntaxNode defaultSection = null; foreach (var section in node.SwitchSections) { var sectionSyntax = (SyntaxNode)section.Syntax; foreach (var label in section.SwitchLabels) { var loweredLabel = LowerSwitchLabel(label); if (loweredLabel.Syntax.Kind() == SyntaxKind.DefaultSwitchLabel) { if (defaultLabel != null) { // duplicate switch label will have been reported during initial binding. } else { defaultLabel = loweredLabel; defaultSection = sectionSyntax; } } else { Syntax = label.Syntax; AddToDecisionTree(loweredDecisionTree, sectionSyntax, loweredLabel); } } } if (defaultLabel != null) { Add(loweredDecisionTree, (e, t) => new DecisionTree.Guarded(loweredExpression, loweredExpression.Type, default(ImmutableArray <KeyValuePair <BoundExpression, BoundExpression> >), defaultSection, null, defaultLabel)); } // We discard use-site diagnostics, as they have been reported during initial binding. _useSiteDiagnostics.Clear(); return(loweredDecisionTree); }
private void AddToDecisionTree(DecisionTree decisionTree, BoundPatternSwitchLabel label) { var pattern = label.Pattern; var guard = label.Guard; if (guard?.ConstantValue == ConstantValue.False) { return; } switch (pattern.Kind) { case BoundKind.ConstantPattern: { var constantPattern = (BoundConstantPattern)pattern; AddByValue(decisionTree, constantPattern.Value, (e, t) => new DecisionTree.Guarded(e, t, default(ImmutableArray <KeyValuePair <BoundExpression, BoundExpression> >), _section, guard, label)); break; } case BoundKind.DeclarationPattern: { var declarationPattern = (BoundDeclarationPattern)pattern; DecisionMaker maker = (e, t) => new DecisionTree.Guarded(e, t, ImmutableArray.Create(new KeyValuePair <BoundExpression, BoundExpression>(e, declarationPattern.VariableAccess)), _section, guard, label); if (declarationPattern.IsVar) { Add(decisionTree, maker); } else { AddByType(decisionTree, declarationPattern.DeclaredType.Type, maker); } break; } case BoundKind.WildcardPattern: // We do not yet support a wildcard pattern syntax. It is used exclusively // to model the "default:" case, which is handled specially in the caller. default: throw ExceptionUtilities.UnexpectedValue(pattern.Kind); } }
private DecisionTree LowerToDecisionTree( BoundExpression loweredExpression, BoundPatternSwitchStatement node) { var loweredDecisionTree = CreateEmptyDecisionTree(loweredExpression); BoundPatternSwitchLabel defaultLabel = null; SyntaxNode defaultSection = null; foreach (var section in node.SwitchSections) { var sectionSyntax = section.Syntax; foreach (var label in section.SwitchLabels) { var loweredLabel = LowerSwitchLabel(label); if (loweredLabel.Syntax.Kind() == SyntaxKind.DefaultSwitchLabel) { if (defaultLabel != null) { // duplicate switch label will have been reported during initial binding. } else { defaultLabel = loweredLabel; defaultSection = sectionSyntax; } } else { AddToDecisionTree(loweredDecisionTree, sectionSyntax, loweredLabel); } } } if (defaultLabel != null && !loweredDecisionTree.MatchIsComplete) { Add(loweredDecisionTree, (e, t) => new DecisionTree.Guarded( expression: loweredExpression, type: loweredExpression.Type, bindings: default,
/// <summary> /// Bind the pattern switch section, producing subsumption diagnostics. /// </summary> /// <param name="boundSwitchExpression"/> /// <param name="node"/> /// <param name="originalBinder"/> /// <param name="defaultLabel">If a default label is found in this section, assigned that label</param> /// <param name="someValueMatched">If a constant label is found that matches the constant input, assigned that label</param> /// <param name="subsumption">A helper class that uses a decision tree to produce subsumption diagnostics.</param> /// <param name="diagnostics"></param> /// <returns></returns> private BoundPatternSwitchSection BindPatternSwitchSection( BoundExpression boundSwitchExpression, SwitchSectionSyntax node, Binder originalBinder, ref BoundPatternSwitchLabel defaultLabel, ref bool someValueMatched, SubsumptionDiagnosticBuilder subsumption, DiagnosticBag diagnostics) { // Bind match section labels var boundLabelsBuilder = ArrayBuilder <BoundPatternSwitchLabel> .GetInstance(); var sectionBinder = originalBinder.GetBinder(node); // this binder can bind pattern variables from the section. Debug.Assert(sectionBinder != null); var labelsByNode = LabelsByNode; foreach (var labelSyntax in node.Labels) { LabelSymbol label = labelsByNode[labelSyntax]; BoundPatternSwitchLabel boundLabel = BindPatternSwitchSectionLabel(sectionBinder, boundSwitchExpression, labelSyntax, label, ref defaultLabel, diagnostics); bool valueMatched; // true if we find an unconditional constant label that matches the input constant's value bool isReachable = subsumption.AddLabel(boundLabel, diagnostics, out valueMatched); boundLabel = boundLabel.Update(boundLabel.Label, boundLabel.Pattern, boundLabel.Guard, isReachable && !someValueMatched); someValueMatched |= valueMatched; boundLabelsBuilder.Add(boundLabel); } // Bind switch section statements var boundStatementsBuilder = ArrayBuilder <BoundStatement> .GetInstance(); foreach (var statement in node.Statements) { boundStatementsBuilder.Add(sectionBinder.BindStatement(statement, diagnostics)); } return(new BoundPatternSwitchSection(node, sectionBinder.GetDeclaredLocalsForScope(node), boundLabelsBuilder.ToImmutableAndFree(), boundStatementsBuilder.ToImmutableAndFree())); }
/// <summary> /// Bind the pattern switch section, producing subsumption diagnostics. /// </summary> /// <param name="node"/> /// <param name="originalBinder"/> /// <param name="defaultLabel">If a default label is found in this section, assigned that label</param> /// <param name="someCaseMatches">If a case is found that would always match the input, set to true</param> /// <param name="subsumption">A helper class that uses a decision tree to produce subsumption diagnostics.</param> /// <param name="diagnostics"></param> /// <returns></returns> private BoundPatternSwitchSection BindPatternSwitchSection( SwitchSectionSyntax node, Binder originalBinder, ref BoundPatternSwitchLabel defaultLabel, ref bool someCaseMatches, SubsumptionDiagnosticBuilder subsumption, DiagnosticBag diagnostics) { // Bind match section labels var boundLabelsBuilder = ArrayBuilder <BoundPatternSwitchLabel> .GetInstance(); var sectionBinder = originalBinder.GetBinder(node); // this binder can bind pattern variables from the section. Debug.Assert(sectionBinder != null); var labelsByNode = LabelsByNode; bool?inputMatchesType(TypeSymbol patternType) { // use-site diagnostics will have been reported previously. HashSet <DiagnosticInfo> useSiteDiagnostics = null; return(ExpressionOfTypeMatchesPatternType(Conversions, SwitchGoverningType, patternType, ref useSiteDiagnostics, out _, SwitchGoverningExpression.ConstantValue, true)); } foreach (var labelSyntax in node.Labels) { LabelSymbol label = labelsByNode[labelSyntax]; BoundPatternSwitchLabel boundLabel = BindPatternSwitchSectionLabel(sectionBinder, labelSyntax, label, ref defaultLabel, diagnostics); bool isNotSubsumed = subsumption.AddLabel(boundLabel, diagnostics); bool guardAlwaysSatisfied = boundLabel.Guard == null || boundLabel.Guard.ConstantValue == ConstantValue.True; // patternMatches is true if the input expression is unconditionally matched by the pattern, false if it never matches, null otherwise. // While subsumption would produce an error for an unreachable pattern based on the input's type, this is used for reachability (warnings), // and takes the input value into account. bool?patternMatches; if (labelSyntax.Kind() == SyntaxKind.DefaultSwitchLabel) { patternMatches = null; } else if (boundLabel.Pattern.Kind == BoundKind.WildcardPattern) { // wildcard pattern matches anything patternMatches = true; } else if (boundLabel.Pattern is BoundDeclarationPattern d) { // `var x` matches anything // `Type x` matches anything of a subtype of `Type` except null patternMatches = d.IsVar ? true : inputMatchesType(d.DeclaredType.Type); } else if (boundLabel.Pattern is BoundConstantPattern p) { // `case 2` matches the input `2` patternMatches = SwitchGoverningExpression.ConstantValue?.Equals(p.ConstantValue); } else { patternMatches = null; } bool labelIsReachable = isNotSubsumed && !someCaseMatches && patternMatches != false; boundLabel = boundLabel.Update(boundLabel.Label, boundLabel.Pattern, boundLabel.Guard, labelIsReachable); boundLabelsBuilder.Add(boundLabel); // labelWouldMatch is true if we find an unconditional (no `when` clause restriction) label that matches the input expression bool labelWouldMatch = guardAlwaysSatisfied && patternMatches == true; someCaseMatches |= labelWouldMatch; } // Bind switch section statements var boundStatementsBuilder = ArrayBuilder <BoundStatement> .GetInstance(); foreach (var statement in node.Statements) { boundStatementsBuilder.Add(sectionBinder.BindStatement(statement, diagnostics)); } return(new BoundPatternSwitchSection(node, sectionBinder.GetDeclaredLocalsForScope(node), boundLabelsBuilder.ToImmutableAndFree(), boundStatementsBuilder.ToImmutableAndFree())); }
private BoundPatternSwitchLabel BindPatternSwitchSectionLabel( Binder sectionBinder, BoundExpression boundSwitchExpression, SwitchLabelSyntax node, LabelSymbol label, ref BoundPatternSwitchLabel defaultLabel, DiagnosticBag diagnostics) { switch (node.Kind()) { case SyntaxKind.CaseSwitchLabel: { var caseLabelSyntax = (CaseSwitchLabelSyntax)node; bool wasExpression; var pattern = sectionBinder.BindConstantPattern( node, boundSwitchExpression, boundSwitchExpression.Type, caseLabelSyntax.Value, node.HasErrors, diagnostics, out wasExpression, wasSwitchCase: true); bool hasErrors = pattern.HasErrors; var constantValue = pattern.ConstantValue; if (!hasErrors && (object)constantValue != null && pattern.Value.Type == SwitchGoverningType && this.FindMatchingSwitchCaseLabel(constantValue, caseLabelSyntax) != label) { diagnostics.Add(ErrorCode.ERR_DuplicateCaseLabel, node.Location, pattern.ConstantValue.GetValueToDisplay() ?? label.Name); hasErrors = true; } if (caseLabelSyntax.Value.Kind() == SyntaxKind.DefaultLiteralExpression) { diagnostics.Add(ErrorCode.WRN_DefaultInSwitch, caseLabelSyntax.Value.Location); } // Until we've determined whether or not the switch label is reachable, we assume it // is. The caller updates isReachable after determining if the label is subsumed. const bool isReachable = true; return(new BoundPatternSwitchLabel(node, label, pattern, null, isReachable, hasErrors)); } case SyntaxKind.DefaultSwitchLabel: { var defaultLabelSyntax = (DefaultSwitchLabelSyntax)node; var pattern = new BoundWildcardPattern(node); bool hasErrors = pattern.HasErrors; if (defaultLabel != null) { diagnostics.Add(ErrorCode.ERR_DuplicateCaseLabel, node.Location, label.Name); hasErrors = true; } // We always treat the default label as reachable, even if the switch is complete. const bool isReachable = true; // Note that this is semantically last! The caller will place it in the decision tree // in the final position. defaultLabel = new BoundPatternSwitchLabel(node, label, pattern, null, isReachable, hasErrors); return(defaultLabel); } case SyntaxKind.CasePatternSwitchLabel: { var matchLabelSyntax = (CasePatternSwitchLabelSyntax)node; var pattern = sectionBinder.BindPattern( matchLabelSyntax.Pattern, boundSwitchExpression, boundSwitchExpression.Type, node.HasErrors, diagnostics, wasSwitchCase: true); return(new BoundPatternSwitchLabel(node, label, pattern, matchLabelSyntax.WhenClause != null ? sectionBinder.BindBooleanExpression(matchLabelSyntax.WhenClause.Condition, diagnostics) : null, true, node.HasErrors)); } default: throw ExceptionUtilities.UnexpectedValue(node); } }
private BoundPatternSwitchLabel LowerSwitchLabel(BoundPatternSwitchLabel label) { return(label.Update(label.Label, _localRewriter.LowerPattern(label.Pattern), _localRewriter.VisitExpression(label.Guard), label.IsReachable)); }
private ImmutableArray <BoundPatternSwitchSection> BindPatternSwitchSections(BoundExpression boundSwitchExpression, SyntaxList <SwitchSectionSyntax> sections, Binder originalBinder, out BoundPatternSwitchLabel defaultLabel, DiagnosticBag diagnostics) { defaultLabel = null; // Bind match sections var boundPatternSwitchSectionsBuilder = ArrayBuilder <BoundPatternSwitchSection> .GetInstance(); foreach (var sectionSyntax in sections) { boundPatternSwitchSectionsBuilder.Add(BindPatternSwitchSection(boundSwitchExpression, sectionSyntax, originalBinder, ref defaultLabel, diagnostics)); } return(boundPatternSwitchSectionsBuilder.ToImmutableAndFree()); }
internal DecisionTree ComputeDecisionTree() { Debug.Assert(_section == null); var expression = _switchStatement.Expression; if (expression.ConstantValue == null && expression.Kind != BoundKind.Local) { // unless the expression is simple enough, copy it into a local var localSymbol = new SynthesizedLocal(_enclosingSymbol as MethodSymbol, expression.Type, SynthesizedLocalKind.PatternMatchingTemp, _switchStatement.Syntax, false, RefKind.None); expression = new BoundLocal(expression.Syntax, localSymbol, null, expression.Type); } var result = DecisionTree.Create(_switchStatement.Expression, _switchStatement.Expression.Type, _enclosingSymbol); var subsumptionTree = DecisionTree.Create(_switchStatement.Expression, _switchStatement.Expression.Type, _enclosingSymbol); BoundPatternSwitchLabel defaultLabel = null; BoundPatternSwitchSection defaultSection = null; foreach (var section in _switchStatement.SwitchSections) { this._section = section; foreach (var label in section.SwitchLabels) { if (label.Syntax.Kind() == SyntaxKind.DefaultSwitchLabel) { if (defaultLabel != null) { // duplicate switch label will have been reported during initial binding. } else { defaultLabel = label; defaultSection = section; } } else { this._syntax = label.Syntax; // For purposes of subsumption, we do not take into consideration the value // of the input expression. Therefore we consider null possible if the type permits. var subsumedErrorCode = CheckSubsumed(label.Pattern, subsumptionTree, inputCouldBeNull: true); if (subsumedErrorCode != 0 && subsumedErrorCode != ErrorCode.ERR_NoImplicitConvCast) { if (!label.HasErrors) { _diagnostics.Add(subsumedErrorCode, label.Pattern.Syntax.Location); } } else { AddToDecisionTree(result, label); if (label.Guard == null || label.Guard.ConstantValue == ConstantValue.True) { // Only unconditional switch labels contribute to subsumption AddToDecisionTree(subsumptionTree, label); } } } } } if (defaultLabel != null) { Add(result, (e, t) => new DecisionTree.Guarded(_switchStatement.Expression, _switchStatement.Expression.Type, default(ImmutableArray <KeyValuePair <BoundExpression, BoundExpression> >), defaultSection, null, defaultLabel)); } return(result); }
/// <summary> /// Add the case label to the subsumption tree. Return true if the label is reachable /// given the expression and previously added labels. `valueMatched` is set to true /// if and only if the label is a reachable unconditional (no when clause) constant pattern /// whose value is the same as the input expression's constant value, and false otherwise. /// </summary> internal bool AddLabel(BoundPatternSwitchLabel label, DiagnosticBag diagnostics, out bool valueMatched) { // Use site diagnostics are reported (and cleared) by this method. // So they should be empty when we start. Debug.Assert(_useSiteDiagnostics.Count == 0); valueMatched = false; if (label.Syntax.Kind() == SyntaxKind.DefaultSwitchLabel) { // the default case label is always considered reachable. return(true); } try { // For purposes of subsumption, we do not take into consideration the value // of the input expression. Therefore we consider null possible if the type permits. Syntax = label.Syntax; var subsumedErrorCode = CheckSubsumed(label.Pattern, _subsumptionTree, inputCouldBeNull: true); if (subsumedErrorCode != 0 && subsumedErrorCode != ErrorCode.ERR_NoImplicitConvCast) { if (!label.HasErrors) { diagnostics.Add(subsumedErrorCode, label.Pattern.Syntax.Location); } return(false); } var guardAlwaysSatisfied = label.Guard == null || label.Guard.ConstantValue == ConstantValue.True; if (guardAlwaysSatisfied) { // Only unconditional switch labels contribute to subsumption if (AddToDecisionTree(_subsumptionTree, null, label) == null) { return(false); } } // For a constant switch, a constant label is only reachable if the value is equal. var patternConstant = (label.Pattern as BoundConstantPattern)?.ConstantValue; if (this._subsumptionTree.Expression.ConstantValue == null || patternConstant == null) { // either the input or the pattern wasn't a constant, so they might match. return(true); } // If not subsumed, the label is considered reachable unless its constant value is // distinct from the constant value of the input expression. if (this._subsumptionTree.Expression.ConstantValue.Equals(patternConstant)) { valueMatched = guardAlwaysSatisfied; return(true); } return(false); } finally { // report the use-site diagnostics diagnostics.Add(label.Syntax.Location, _useSiteDiagnostics); _useSiteDiagnostics.Clear(); } }