Example #1
0
        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()));
        }
Example #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());
        }
Example #3
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());
        }
Example #4
0
        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);
            }
        }
Example #5
0
        /// <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();
            }
        }
Example #6
0
 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);
 }
Example #7
0
        /// <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);
        }
Example #8
0
            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);
            }
Example #9
0
        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,
Example #11
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()));
        }
Example #12
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()));
        }
Example #13
0
        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);
            }
        }
Example #14
0
 private BoundPatternSwitchLabel LowerSwitchLabel(BoundPatternSwitchLabel label)
 {
     return(label.Update(label.Label, _localRewriter.LowerPattern(label.Pattern), _localRewriter.VisitExpression(label.Guard), label.IsReachable));
 }
Example #15
0
        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());
        }
Example #16
0
        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);
        }
Example #17
0
        /// <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();
            }
        }