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(); }
protected DecisionTree AddToDecisionTree(DecisionTree decisionTree, SyntaxNode sectionSyntax, BoundPatternSwitchLabel label) { var pattern = label.Pattern; var guard = label.Guard; if (guard?.ConstantValue == ConstantValue.False) { return null; } switch (pattern.Kind) { case BoundKind.ConstantPattern: { var constantPattern = (BoundConstantPattern)pattern; DecisionMaker makeDecision = (e, t) => new DecisionTree.Guarded(e, t, default(ImmutableArray<KeyValuePair<BoundExpression, BoundExpression>>), sectionSyntax, guard, label); if (constantPattern.ConstantValue == ConstantValue.Null) { return AddByNull(decisionTree, makeDecision); } else { return AddByValue(decisionTree, constantPattern, makeDecision); } } 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)), sectionSyntax, guard, label); if (declarationPattern.IsVar) { return Add(decisionTree, maker); } else { return AddByType(decisionTree, declarationPattern.DeclaredType.Type, maker); } } 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 ImmutableArray<BoundPatternSwitchSection> BindPatternSwitchSections( BoundExpression boundSwitchExpression, SyntaxList<SwitchSectionSyntax> sections, 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, this.Conversions, boundSwitchExpression); foreach (var sectionSyntax in sections) { boundPatternSwitchSectionsBuilder.Add(BindPatternSwitchSection( boundSwitchExpression, 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; } // 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); } }
/// <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()); }
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()); }
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 && 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, "default"); 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); } }
private BoundPatternSwitchLabel LowerSwitchLabel(BoundPatternSwitchLabel label) { return label.Update(label.Label, _localRewriter.LowerPattern(label.Pattern), _localRewriter.VisitExpression(label.Guard), label.IsReachable); }
/// <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(); } }
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, LocalSymbol>>), _section, guard, label), label.HasErrors); break; } case BoundKind.DeclarationPattern: { var declarationPattern = (BoundDeclarationPattern)pattern; DecisionMaker maker = (e, t) => new DecisionTree.Guarded(e, t, ImmutableArray.Create(new KeyValuePair<BoundExpression, LocalSymbol>(e, declarationPattern.LocalSymbol)), _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); } }
public Guarded( BoundExpression expression, TypeSymbol type, ImmutableArray<KeyValuePair<BoundExpression, LocalSymbol>> bindings, BoundPatternSwitchSection section, BoundExpression guard, BoundPatternSwitchLabel label) : base(expression, type, null) { this.Guard = guard; this.Label = label; this.Bindings = bindings; this.Section = section; Debug.Assert(guard?.ConstantValue != ConstantValue.False); base.MatchIsComplete = (guard == null) || (guard.ConstantValue == ConstantValue.True); }