Ejemplo n.º 1
0
        /// <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());
        }
Ejemplo n.º 2
0
        /// <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());
        }
Ejemplo n.º 3
0
        /// <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()));
        }
Ejemplo n.º 4
0
        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();
        }
Ejemplo n.º 5
0
        /// <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());
        }
Ejemplo n.º 6
0
        /// <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()));
        }