예제 #1
0
        // Input must be used no more than once in the result. If it is needed repeatedly store its value in a temp and use the temp.
        BoundExpression MakeIsPattern(BoundPattern loweredPattern, BoundExpression loweredInput)
        {
            var syntax = _factory.Syntax = loweredPattern.Syntax;

            switch (loweredPattern.Kind)
            {
            case BoundKind.DeclarationPattern:
            {
                var declPattern = (BoundDeclarationPattern)loweredPattern;
                return(MakeIsDeclarationPattern(declPattern, loweredInput));
            }

            case BoundKind.WildcardPattern:
                return(_factory.Literal(true));

            case BoundKind.ConstantPattern:
            {
                var constantPattern = (BoundConstantPattern)loweredPattern;
                return(MakeIsConstantPattern(constantPattern, loweredInput));
            }

            default:
                throw ExceptionUtilities.UnexpectedValue(loweredPattern.Kind);
            }
        }
 /// <summary>
 /// Learn something about the input from a test of a given expression against a given pattern.  The given
 /// state is updated to note that any slots that are tested against `null` may be null.
 /// </summary>
 /// <returns>true if there is a top-level explicit null check</returns>
 private void LearnFromAnyNullPatterns(
     BoundExpression expression,
     BoundPattern pattern)
 {
     int slot = MakeSlot(expression);
     LearnFromAnyNullPatterns(slot, expression.Type, pattern);
 }
예제 #3
0
        /// <summary>
        /// Record declared variables in the pattern.
        /// </summary>
        private void NoteDeclaredPatternVariables(BoundPattern pattern)
        {
            if (IsInside)
            {
                switch (pattern)
                {
                case BoundDeclarationPattern decl:
                {
                    // The variable may be null if it is a discard designation `_`.
                    if (decl.Variable?.Kind == SymbolKind.Local)
                    {
                        // Because this API only returns local symbols and parameters,
                        // we exclude pattern variables that have become fields in scripts.
                        _variablesDeclared.Add(decl.Variable);
                    }
                }
                break;

                case BoundRecursivePattern recur:
                {
                    if (recur.Variable?.Kind == SymbolKind.Local)
                    {
                        _variablesDeclared.Add(recur.Variable);
                    }
                }
                break;
                }
            }
        }
예제 #4
0
        // Input must be used no more than once in the result. If it is needed repeatedly store its value in a temp and use the temp.
        BoundExpression LowerPattern(BoundPattern pattern, BoundExpression input)
        {
            var syntax = _factory.Syntax = pattern.Syntax;

            switch (pattern.Kind)
            {
            case BoundKind.DeclarationPattern:
            {
                var declPattern = (BoundDeclarationPattern)pattern;
                return(LowerDeclarationPattern(declPattern, input));
            }

            case BoundKind.WildcardPattern:
                return(_factory.Literal(true));

            case BoundKind.ConstantPattern:
            {
                var constantPattern = (BoundConstantPattern)pattern;
                return(LowerConstantPattern(constantPattern, input));
            }

            default:
                throw ExceptionUtilities.UnexpectedValue(pattern.Kind);
            }
        }
예제 #5
0
        /// <summary>
        /// Learn from any constant null patterns appearing in the pattern.
        /// </summary>
        /// <param name="inputType">Type type of the input expression (before nullable analysis).
        /// Used to determine which types can contain null.</param>
        /// <returns>true if there is a top-level explicit null check</returns>
        private void LearnFromAnyNullPatterns(
            int inputSlot,
            TypeSymbol inputType,
            BoundPattern pattern)
        {
            if (inputSlot <= 0)
            {
                return;
            }

            switch (pattern)
            {
            case BoundConstantPattern cp:
                bool isExplicitNullCheck = cp.Value.ConstantValue == ConstantValue.Null;
                if (isExplicitNullCheck)
                {
                    LearnFromNullTest(inputSlot, inputType, ref this.State);
                }
                break;

            case BoundDeclarationPattern _:
            case BoundDiscardPattern _:
            case BoundITuplePattern _:
                break;     // nothing to learn

            case BoundRecursivePattern rp:
            {
                // for positional part: we only learn from tuples (not Deconstruct)
                if (rp.DeconstructMethod is null && !rp.Deconstruction.IsDefault)
                {
                    var elements = inputType.TupleElements;
                    for (int i = 0, n = Math.Min(rp.Deconstruction.Length, elements.IsDefault ? 0 : elements.Length); i < n; i++)
                    {
                        BoundSubpattern item    = rp.Deconstruction[i];
                        FieldSymbol     element = elements[i];
                        LearnFromAnyNullPatterns(GetOrCreateSlot(element, inputSlot), element.Type, item.Pattern);
                    }
                }

                // for property part
                if (!rp.Properties.IsDefault)
                {
                    for (int i = 0, n = rp.Properties.Length; i < n; i++)
                    {
                        BoundSubpattern item   = rp.Properties[i];
                        Symbol          symbol = item.Symbol;
                        if (symbol?.ContainingType.Equals(inputType, TypeCompareKind.AllIgnoreOptions) == true)
                        {
                            LearnFromAnyNullPatterns(GetOrCreateSlot(symbol, inputSlot), symbol.GetTypeOrReturnType().Type, item.Pattern);
                        }
                    }
                }
            }
            break;

            default:
                throw ExceptionUtilities.UnexpectedValue(pattern);
            }
        }
예제 #6
0
 /// <summary>
 /// Record declared variables in the pattern.
 /// </summary>
 private void NoteDeclaredPatternVariables(BoundPattern pattern)
 {
     if (IsInside && pattern.Kind == BoundKind.DeclarationPattern)
     {
         var decl = (BoundDeclarationPattern)pattern;
         _variablesDeclared.Add(decl.LocalSymbol);
     }
 }
 private void VisitPatternForRewriting(BoundPattern pattern)
 {
     // Don't let anything under the pattern actually affect current state,
     // as we're only visiting for nullable information.
     Debug.Assert(!IsConditionalState);
     var currentState = State;
     VisitWithoutDiagnostics(pattern);
     SetState(currentState);
 }
예제 #8
0
 /// <summary>
 /// Record declared variables in the pattern.
 /// </summary>
 private void NoteDeclaredPatternVariables(BoundPattern pattern)
 {
     if (IsInside && pattern.Kind == BoundKind.DeclarationPattern)
     {
         var decl = (BoundDeclarationPattern)pattern;
         if (decl.Variable.Kind == SymbolKind.Local)
         {
             // Because this API only returns local symbols and parameters,
             // we exclude pattern variables that have become fields in scripts.
             _variablesDeclared.Add(decl.Variable);
         }
     }
 }
예제 #9
0
        internal override BoundSwitchExpressionArm BindSwitchExpressionArm(SwitchExpressionArmSyntax node, BindingDiagnosticBag diagnostics)
        {
            Debug.Assert(node == _arm);
            Binder armBinder = this.GetBinder(node);
            bool   hasErrors = _switchExpressionBinder.SwitchGoverningType.IsErrorType();
            ImmutableArray <LocalSymbol> locals = _armScopeBinder.Locals;
            BoundPattern    pattern             = armBinder.BindPattern(node.Pattern, _switchExpressionBinder.SwitchGoverningType, _switchExpressionBinder.SwitchGoverningValEscape, permitDesignations: true, hasErrors, diagnostics);
            BoundExpression whenClause          = node.WhenClause != null
                ? armBinder.BindBooleanExpression(node.WhenClause.Condition, diagnostics)
                : null;

            BoundExpression armResult = armBinder.BindValue(node.Expression, diagnostics, BindValueKind.RValue);
            var             label     = new GeneratedLabelSymbol("arm");

            return(new BoundSwitchExpressionArm(node, locals, pattern, whenClause, armResult, label, hasErrors | pattern.HasErrors));
        }
예제 #10
0
 BoundPattern LowerPattern(BoundPattern pattern)
 {
     switch (pattern.Kind)
     {
         case BoundKind.DeclarationPattern:
             {
                 var declPattern = (BoundDeclarationPattern)pattern;
                 return declPattern.Update(declPattern.Variable, VisitExpression(declPattern.VariableAccess), declPattern.DeclaredType, declPattern.IsVar);
             }
         case BoundKind.ConstantPattern:
             {
                 var constantPattern = (BoundConstantPattern)pattern;
                 return constantPattern.Update(VisitExpression(constantPattern.Value), constantPattern.ConstantValue);
             }
         default:
             return pattern;
     }
 }
        /// <summary>
        /// Check if the given expression is known to *always* match, or *always* fail against the given pattern.
        /// Return true for known match, false for known fail, and null otherwise.
        /// </summary>
        private bool?CheckRefutations(BoundExpression expression, BoundPattern pattern)
        {
            switch (pattern.Kind)
            {
            case BoundKind.DeclarationPattern:
            {
                var declPattern = (BoundDeclarationPattern)pattern;
                if (declPattern.IsVar ||                                                                                             // var pattern always matches
                    declPattern.DeclaredType?.Type?.IsValueType == true && declPattern.DeclaredType.Type == (object)expression.Type) // exact match
                {
                    return(true);
                }
                // there are probably other cases to check. Note that reference types can, in general, fail because of null
            }
            break;
            }

            return(null);
        }
        public virtual void VisitPattern(BoundExpression expression, BoundPattern pattern)
        {
            Split();
            bool?knownMatch = CheckRefutations(expression, pattern);

            switch (knownMatch)
            {
            case true:
                SetState(StateWhenTrue);
                SetConditionalState(this.State, UnreachableState());
                break;

            case false:
                SetState(StateWhenFalse);
                SetConditionalState(UnreachableState(), this.State);
                break;

            case null:
                break;
            }
        }
예제 #13
0
            public BoundExpression LowerIsPattern(
                BoundIsPatternExpression isPatternExpression, BoundPattern pattern, CSharpCompilation compilation, DiagnosticBag diagnostics)
            {
                BoundDecisionDag decisionDag    = isPatternExpression.DecisionDag;
                LabelSymbol      whenTrueLabel  = isPatternExpression.WhenTrueLabel;
                LabelSymbol      whenFalseLabel = isPatternExpression.WhenFalseLabel;
                BoundExpression  loweredInput   = _localRewriter.VisitExpression(isPatternExpression.Expression);

                // The optimization of sharing pattern-matching temps with user variables can always apply to
                // an is-pattern expression because there is no when clause that could possibly intervene during
                // the execution of the pattern-matching automaton and change one of those variables.
                decisionDag = ShareTempsAndEvaluateInput(loweredInput, decisionDag, expr => _sideEffectBuilder.Add(expr), out _);
                var node = decisionDag.RootNode;

                // We follow the "good" path in the decision dag. We depend on it being nicely linear in structure.
                // If we add "or" patterns that assumption breaks down.
                while (node.Kind != BoundKind.LeafDecisionDagNode && node.Kind != BoundKind.WhenDecisionDagNode)
                {
                    switch (node)
                    {
                    case BoundEvaluationDecisionDagNode evalNode:
                    {
                        LowerOneTest(evalNode.Evaluation);
                        node = evalNode.Next;
                    }
                    break;

                    case BoundTestDecisionDagNode testNode:
                    {
                        Debug.Assert(testNode.WhenFalse is BoundLeafDecisionDagNode x && x.Label == whenFalseLabel);
                        if (testNode.WhenTrue is BoundEvaluationDecisionDagNode e &&
                            TryLowerTypeTestAndCast(testNode.Test, e.Evaluation, out BoundExpression sideEffect, out BoundExpression testExpression))
                        {
                            _sideEffectBuilder.Add(sideEffect);
                            AddConjunct(testExpression);
                            node = e.Next;
                        }
 public override void VisitPattern(BoundPattern pattern)
 {
     base.VisitPattern(pattern);
     NoteDeclaredPatternVariables(pattern);
 }
예제 #15
0
        /// <summary>
        /// Check if the pattern is subsumed by the decisions in the decision tree, given that the input could
        /// (or could not) be null based on the parameter <paramref name="inputCouldBeNull"/>. If it is subsumed,
        /// returns an error code suitable for reporting the issue. If it is not subsumed, returns 0.
        /// </summary>
        private ErrorCode CheckSubsumed(BoundPattern pattern, DecisionTree decisionTree, bool inputCouldBeNull)
        {
            if (decisionTree.MatchIsComplete)
            {
                return(ErrorCode.ERR_PatternIsSubsumed);
            }

            switch (pattern.Kind)
            {
            case BoundKind.ConstantPattern:
            {
                var constantPattern = (BoundConstantPattern)pattern;
                if (constantPattern.Value.HasErrors || constantPattern.Value.ConstantValue == null || constantPattern.Value.ConstantValue.IsBad)
                {
                    // since this will have been reported earlier, we use ErrorCode.ERR_NoImplicitConvCast
                    // as a flag to suppress errors in subsumption analysis.
                    return(ErrorCode.ERR_NoImplicitConvCast);
                }

                bool isNull = constantPattern.Value.ConstantValue.IsNull;

                // If null inputs have been handled by previous patterns, then
                // the input can no longer be null. In that case a null pattern is subsumed.
                if (isNull && !inputCouldBeNull)
                {
                    // Note: we do not have any test covering this. Is it reachable?
                    // Possibly not given the simple patterns types we support today.
                    return(ErrorCode.ERR_PatternIsSubsumed);
                }

                switch (decisionTree.Kind)
                {
                case DecisionTree.DecisionKind.ByValue:
                {
                    var byValue = (DecisionTree.ByValue)decisionTree;
                    if (isNull)
                    {
                        // This should not occur, as the decision tree should contain a handler for
                        // null earlier, for example in a type test.
                        throw ExceptionUtilities.Unreachable;
                    }

                    DecisionTree decision;
                    if (byValue.ValueAndDecision.TryGetValue(constantPattern.Value.ConstantValue.Value, out decision))
                    {
                        var error = CheckSubsumed(pattern, decision, inputCouldBeNull);
                        if (error != 0)
                        {
                            return(error);
                        }
                    }

                    if (byValue.Default != null)
                    {
                        // Note: we do not have any test covering this. Is it reachable?
                        // Possibly not given the simple patterns types we support today.
                        return(CheckSubsumed(pattern, byValue.Default, inputCouldBeNull));
                    }

                    return(0);
                }

                case DecisionTree.DecisionKind.ByType:
                {
                    var byType = (DecisionTree.ByType)decisionTree;
                    if (isNull)
                    {
                        if (byType.WhenNull != null)
                        {
                            var result = CheckSubsumed(pattern, byType.WhenNull, inputCouldBeNull);
                            if (result != 0)
                            {
                                return(result);
                            }
                        }
                    }
                    else
                    {
                        foreach (var td in byType.TypeAndDecision)
                        {
                            var type     = td.Key;
                            var decision = td.Value;
                            if (ExpressionOfTypeMatchesPatternType(constantPattern.Value.Type, type, ref _useSiteDiagnostics) == true)
                            {
                                var error = CheckSubsumed(pattern, decision, false);
                                if (error != 0)
                                {
                                    return(error);
                                }
                            }
                        }
                    }
                    return((byType.Default != null) ? CheckSubsumed(pattern, byType.Default, inputCouldBeNull) : 0);
                }

                case DecisionTree.DecisionKind.Guarded:
                {
                    // This is unreachable because the subsumption version of the decision tree
                    // never contains guarded decision trees that are not complete, or that have
                    // any guard other than `true`.
                    throw ExceptionUtilities.Unreachable;
                }

                default:
                    throw ExceptionUtilities.UnexpectedValue(decisionTree.Kind);
                }
            }

            case BoundKind.DeclarationPattern:
            {
                var declarationPattern = (BoundDeclarationPattern)pattern;
                switch (decisionTree.Kind)
                {
                case DecisionTree.DecisionKind.ByValue:
                {
                    // A declaration pattern is only subsumed by a value pattern if all of the values are accounted for.
                    // For example, when switching on a bool, do we handle both true and false?
                    // For now, we do not handle this case. Also, this provides compatibility with previous compilers.
                    if (inputCouldBeNull)
                    {
                        return(0);                // null could never be handled by a value decision
                    }

                    var byValue = (DecisionTree.ByValue)decisionTree;
                    if (byValue.Default != null)
                    {
                        // Note: we do not have any test covering this. Is it reachable?
                        // Possibly not given the simple patterns types we support today.
                        return(CheckSubsumed(pattern, byValue.Default, false));
                    }

                    return(0);
                }

                case DecisionTree.DecisionKind.ByType:
                {
                    var byType = (DecisionTree.ByType)decisionTree;
                    if (declarationPattern.IsVar &&
                        inputCouldBeNull &&
                        (byType.WhenNull == null || CheckSubsumed(pattern, byType.WhenNull, inputCouldBeNull) == 0) &&
                        (byType.Default == null || CheckSubsumed(pattern, byType.Default, inputCouldBeNull) == 0))
                    {
                        return(0);                // new pattern catches null if not caught by existing WhenNull or Default
                    }

                    inputCouldBeNull = false;
                    foreach (var td in byType.TypeAndDecision)
                    {
                        var type     = td.Key;
                        var decision = td.Value;
                        // if the pattern's type is already handled by the previous pattern
                        // or the previous pattern handles all of the (non-null) input data...
                        if (ExpressionOfTypeMatchesPatternType(
                                declarationPattern.DeclaredType.Type.TupleUnderlyingTypeOrSelf(), type, ref _useSiteDiagnostics) == true ||
                            ExpressionOfTypeMatchesPatternType(byType.Type, type, ref _useSiteDiagnostics) == true)
                        {
                            // then we check if the pattern is subsumed by the previous decision
                            var error = CheckSubsumed(pattern, decision, inputCouldBeNull);
                            if (error != 0)
                            {
                                return(error);
                            }
                        }
                    }

                    if (byType.Default != null)
                    {
                        // Note: we do not have any test covering this. Is it reachable?
                        // Possibly not given the simple patterns types we support today.
                        return(CheckSubsumed(pattern, byType.Default, inputCouldBeNull));
                    }

                    return(0);
                }

                case DecisionTree.DecisionKind.Guarded:
                {
                    // Because all guarded decision trees in the subsumption tree are
                    // complete, we should never get here.
                    throw ExceptionUtilities.Unreachable;
                }

                default:
                    throw ExceptionUtilities.UnexpectedValue(decisionTree.Kind);
                }
            }

            case BoundKind.WildcardPattern:
                // because we always handle `default:` last, and that is the only way to get a wildcard pattern,
                // we should never need to see if it subsumes something else.
                throw ExceptionUtilities.UnexpectedValue(decisionTree.Kind);

            default:
                throw ExceptionUtilities.UnexpectedValue(pattern.Kind);
            }
        }
예제 #16
0
        /// <summary>
        /// Learn from any constant null patterns appearing in the pattern.
        /// </summary>
        /// <param name="inputType">Type type of the input expression (before nullable analysis).
        /// Used to determine which types can contain null.</param>
        private void LearnFromAnyNullPatterns(
            int inputSlot,
            TypeSymbol inputType,
            BoundPattern pattern)
        {
            if (inputSlot <= 0)
            {
                return;
            }

            // https://github.com/dotnet/roslyn/issues/35041 We only need to do this when we're rewriting, so we
            // can get information for any nodes in the pattern.
            VisitPatternForRewriting(pattern);

            switch (pattern)
            {
            case BoundConstantPattern cp:
                bool isExplicitNullCheck = cp.Value.ConstantValue == ConstantValue.Null;
                if (isExplicitNullCheck)
                {
                    // Since we're not branching on this null test here, we just infer the top level
                    // nullability.  We'll branch on it later.
                    LearnFromNullTest(inputSlot, inputType, ref this.State, markDependentSlotsNotNull: false);
                }
                break;

            case BoundDeclarationPattern _:
            case BoundDiscardPattern _:
            case BoundITuplePattern _:
                break;     // nothing to learn

            case BoundRecursivePattern rp:
            {
                if (rp.IsExplicitNotNullTest)
                {
                    LearnFromNullTest(inputSlot, inputType, ref this.State, markDependentSlotsNotNull: false);
                }

                // for positional part: we only learn from tuples (not Deconstruct)
                if (rp.DeconstructMethod is null && !rp.Deconstruction.IsDefault)
                {
                    var elements = inputType.TupleElements;
                    for (int i = 0, n = Math.Min(rp.Deconstruction.Length, elements.IsDefault ? 0 : elements.Length); i < n; i++)
                    {
                        BoundSubpattern item    = rp.Deconstruction[i];
                        FieldSymbol     element = elements[i];
                        LearnFromAnyNullPatterns(GetOrCreateSlot(element, inputSlot), element.Type, item.Pattern);
                    }
                }

                // for property part
                if (!rp.Properties.IsDefault)
                {
                    for (int i = 0, n = rp.Properties.Length; i < n; i++)
                    {
                        BoundSubpattern item   = rp.Properties[i];
                        Symbol          symbol = item.Symbol;
                        if (symbol?.ContainingType.Equals(inputType, TypeCompareKind.AllIgnoreOptions) == true)
                        {
                            LearnFromAnyNullPatterns(GetOrCreateSlot(symbol, inputSlot), symbol.GetTypeOrReturnType().Type, item.Pattern);
                        }
                    }
                }
            }
            break;

            default:
                throw ExceptionUtilities.UnexpectedValue(pattern);
            }
        }
예제 #17
0
        private void LearnFromAnyNullPatterns(
            int inputSlot,
            TypeSymbol inputType,
            BoundPattern pattern)
        {
            if (inputSlot <= 0)
            {
                return;
            }

            // https://github.com/dotnet/roslyn/issues/35041 We only need to do this when we're rewriting, so we
            // can get information for any nodes in the pattern.
            VisitPatternForRewriting(pattern);

            switch (pattern)
            {
            case BoundConstantPattern cp:
                bool isExplicitNullCheck = cp.Value.ConstantValue == ConstantValue.Null;
                if (isExplicitNullCheck)
                {
                    // Since we're not branching on this null test here, we just infer the top level
                    // nullability.  We'll branch on it later.
                    LearnFromNullTest(inputSlot, inputType, ref this.State, markDependentSlotsNotNull: false);
                }
                break;

            case BoundDeclarationPattern _:
            case BoundDiscardPattern _:
            case BoundITuplePattern _:
            case BoundRelationalPattern _:
                break;     // nothing to learn

            case BoundTypePattern tp:
                if (tp.IsExplicitNotNullTest)
                {
                    LearnFromNullTest(inputSlot, inputType, ref this.State, markDependentSlotsNotNull: false);
                }
                break;

            case BoundRecursivePattern rp:
            {
                if (rp.IsExplicitNotNullTest)
                {
                    LearnFromNullTest(inputSlot, inputType, ref this.State, markDependentSlotsNotNull: false);
                }

                // for positional part: we only learn from tuples (not Deconstruct)
                if (rp.DeconstructMethod is null && !rp.Deconstruction.IsDefault)
                {
                    var elements = inputType.TupleElements;
                    for (int i = 0, n = Math.Min(rp.Deconstruction.Length, elements.IsDefault ? 0 : elements.Length); i < n; i++)
                    {
                        BoundSubpattern item    = rp.Deconstruction[i];
                        FieldSymbol     element = elements[i];
                        LearnFromAnyNullPatterns(GetOrCreateSlot(element, inputSlot), element.Type, item.Pattern);
                    }
                }

                // for property part
                if (!rp.Properties.IsDefault)
                {
                    foreach (BoundPropertySubpattern subpattern in rp.Properties)
                    {
                        if (subpattern.Member is BoundPropertySubpatternMember member)
                        {
                            LearnFromAnyNullPatterns(getExtendedPropertySlot(member, inputSlot), member.Type, subpattern.Pattern);
                        }
                    }
                }
            }
            break;

            case BoundNegatedPattern p:
                LearnFromAnyNullPatterns(inputSlot, inputType, p.Negated);
                break;

            case BoundBinaryPattern p:
                LearnFromAnyNullPatterns(inputSlot, inputType, p.Left);
                LearnFromAnyNullPatterns(inputSlot, inputType, p.Right);
                break;

            default:
                throw ExceptionUtilities.UnexpectedValue(pattern);
            }

            int getExtendedPropertySlot(BoundPropertySubpatternMember member, int inputSlot)
            {
                if (member.Symbol is null)
                {
                    return(-1);
                }

                if (member.Receiver is not null)
                {
                    inputSlot = getExtendedPropertySlot(member.Receiver, inputSlot);
                }

                if (inputSlot < 0)
                {
                    return(inputSlot);
                }

                return(GetOrCreateSlot(member.Symbol, inputSlot));
            }
        }
예제 #18
0
        /// <summary>
        /// Check if the pattern is subsumed by the decisions in the decision tree, given that the input could
        /// (or could not) be null based on the parameter <paramref name="inputCouldBeNull"/>. If it is subsumed,
        /// returns an error code suitable for reporting the issue. If it is not subsumed, returns 0.
        /// </summary>
        private ErrorCode CheckSubsumed(BoundPattern pattern, DecisionTree decisionTree, bool inputCouldBeNull)
        {
            if (decisionTree.MatchIsComplete)
            {
                return(ErrorCode.ERR_PatternIsSubsumed);
            }

            switch (pattern.Kind)
            {
            case BoundKind.ConstantPattern:
            {
                var constantPattern = (BoundConstantPattern)pattern;
                if (constantPattern.Value.HasErrors || constantPattern.Value.ConstantValue == null || constantPattern.Value.ConstantValue.IsBad)
                {
                    // since this will have been reported earlier, we use ErrorCode.ERR_NoImplicitConvCast
                    // as a flag to suppress errors in subsumption analysis.
                    return(ErrorCode.ERR_NoImplicitConvCast);
                }

                bool isNull = constantPattern.Value.ConstantValue.IsNull;

                // If null inputs have been handled by previous patterns, then
                // the input can no longer be null. In that case a null pattern is subsumed.
                if (isNull && !inputCouldBeNull)
                {
                    return(ErrorCode.ERR_PatternIsSubsumed);
                }

                switch (decisionTree.Kind)
                {
                case DecisionTree.DecisionKind.ByValue:
                {
                    var byValue = (DecisionTree.ByValue)decisionTree;
                    if (isNull)
                    {
                        return(0);                // null must be handled at a type test
                    }

                    DecisionTree decision;
                    if (byValue.ValueAndDecision.TryGetValue(constantPattern.Value.ConstantValue.Value, out decision))
                    {
                        var error = CheckSubsumed(pattern, decision, inputCouldBeNull);
                        if (error != 0)
                        {
                            return(error);
                        }
                    }

                    if (byValue.Default != null)
                    {
                        return(CheckSubsumed(pattern, byValue.Default, inputCouldBeNull));
                    }

                    return(0);
                }

                case DecisionTree.DecisionKind.ByType:
                {
                    var byType = (DecisionTree.ByType)decisionTree;
                    if (isNull)
                    {
                        if (byType.WhenNull != null)
                        {
                            var result = CheckSubsumed(pattern, byType.WhenNull, inputCouldBeNull);
                            if (result != 0)
                            {
                                return(result);
                            }
                        }
                    }
                    else
                    {
                        foreach (var td in byType.TypeAndDecision)
                        {
                            var type     = td.Key;
                            var decision = td.Value;
                            if (ExpressionOfTypeMatchesPatternType(constantPattern.Value.Type, type, ref _useSiteDiagnostics) == true)
                            {
                                var error = CheckSubsumed(pattern, decision, false);
                                if (error != 0)
                                {
                                    return(error);
                                }
                            }
                        }
                    }
                    return((byType.Default != null) ? CheckSubsumed(pattern, byType.Default, inputCouldBeNull) : 0);
                }

                case DecisionTree.DecisionKind.Guarded:
                {
                    var guarded = (DecisionTree.Guarded)decisionTree;
                    return
                        ((guarded.Guard == null || guarded.Guard.ConstantValue == ConstantValue.True) ? ErrorCode.ERR_PatternIsSubsumed :
                         guarded.Default == null ? 0 : CheckSubsumed(pattern, guarded.Default, inputCouldBeNull));
                }

                default:
                    throw ExceptionUtilities.UnexpectedValue(decisionTree.Kind);
                }
            }

            case BoundKind.DeclarationPattern:
            {
                var declarationPattern = (BoundDeclarationPattern)pattern;
                switch (decisionTree.Kind)
                {
                case DecisionTree.DecisionKind.ByValue:
                {
                    // A declaration pattern is only subsumed by a value pattern if all of the values are accounted for.
                    // For example, when switching on a bool, do we handle both true and false?
                    // For now, we do not handle this case. Also, this provides compatibility with previous compilers.
                    if (inputCouldBeNull)
                    {
                        return(0);                // null could never be handled by a value decision
                    }

                    var byValue = (DecisionTree.ByValue)decisionTree;
                    if (byValue.Default != null)
                    {
                        return(CheckSubsumed(pattern, byValue.Default, false));
                    }

                    return(0);
                }

                case DecisionTree.DecisionKind.ByType:
                {
                    var byType = (DecisionTree.ByType)decisionTree;
                    if (declarationPattern.IsVar &&
                        inputCouldBeNull &&
                        (byType.WhenNull == null || CheckSubsumed(pattern, byType.WhenNull, inputCouldBeNull) == 0) &&
                        (byType.Default == null || CheckSubsumed(pattern, byType.Default, inputCouldBeNull) == 0))
                    {
                        return(0);                // new pattern catches null if not caught by existing WhenNull or Default
                    }

                    inputCouldBeNull = false;
                    foreach (var td in byType.TypeAndDecision)
                    {
                        var type     = td.Key;
                        var decision = td.Value;
                        // if the pattern's type is already handled by the previous pattern
                        // or the previous pattern handles all of the (non-null) input data...
                        if (ExpressionOfTypeMatchesPatternType(
                                declarationPattern.DeclaredType.Type.TupleUnderlyingTypeOrSelf(), type, ref _useSiteDiagnostics) == true ||
                            ExpressionOfTypeMatchesPatternType(byType.Type, type, ref _useSiteDiagnostics) == true)
                        {
                            // then we check if the pattern is subsumed by the previous decision
                            var error = CheckSubsumed(pattern, decision, inputCouldBeNull);
                            if (error != 0)
                            {
                                return(error);
                            }
                        }
                    }

                    if (byType.Default != null)
                    {
                        return(CheckSubsumed(pattern, byType.Default, inputCouldBeNull));
                    }

                    return(0);
                }

                case DecisionTree.DecisionKind.Guarded:
                {
                    var guarded = (DecisionTree.Guarded)decisionTree;
                    return((guarded.Guard == null || guarded.Guard.ConstantValue == ConstantValue.True) ? ErrorCode.ERR_PatternIsSubsumed :
                           guarded.Default != null?CheckSubsumed(pattern, guarded.Default, inputCouldBeNull) : 0);
                }

                default:
                    throw ExceptionUtilities.UnexpectedValue(decisionTree.Kind);
                }
            }

            case BoundKind.WildcardPattern:
            {
                switch (decisionTree.Kind)
                {
                case DecisionTree.DecisionKind.ByValue:
                    return(0);            // a value pattern is always considered incomplete (even bool true and false)

                case DecisionTree.DecisionKind.ByType:
                {
                    var byType = (DecisionTree.ByType)decisionTree;
                    if (inputCouldBeNull &&
                        (byType.WhenNull == null || CheckSubsumed(pattern, byType.WhenNull, inputCouldBeNull) == 0) &&
                        (byType.Default == null || CheckSubsumed(pattern, byType.Default, inputCouldBeNull) == 0))
                    {
                        return(0);                // new pattern catches null if not caught by existing WhenNull or Default
                    }

                    inputCouldBeNull = false;
                    foreach (var td in byType.TypeAndDecision)
                    {
                        var type     = td.Key;
                        var decision = td.Value;
                        if (ExpressionOfTypeMatchesPatternType(decisionTree.Type, type, ref _useSiteDiagnostics) == true)
                        {
                            var error = CheckSubsumed(pattern, decision, inputCouldBeNull);
                            if (error != 0)
                            {
                                return(error);
                            }
                        }
                    }

                    if (byType.Default != null)
                    {
                        return(CheckSubsumed(pattern, byType.Default, inputCouldBeNull));
                    }

                    return(0);
                }

                case DecisionTree.DecisionKind.Guarded:
                {
                    var guarded = (DecisionTree.Guarded)decisionTree;
                    return((guarded.Guard == null || guarded.Guard.ConstantValue == ConstantValue.True) ? ErrorCode.ERR_PatternIsSubsumed :
                           guarded.Default != null?CheckSubsumed(pattern, guarded.Default, inputCouldBeNull) : 0);
                }

                default:
                    throw ExceptionUtilities.UnexpectedValue(decisionTree.Kind);
                }
            }

            default:
                throw ExceptionUtilities.UnexpectedValue(pattern.Kind);
            }
        }
예제 #19
0
 public override void VisitPattern(BoundExpression expression, BoundPattern pattern)
 {
     base.VisitPattern(expression, pattern);
     NoteDeclaredPatternVariables(pattern);
 }