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