/// <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()); }
/// <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 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 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(); }
/// <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())); }