示例#1
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)
                {
                    // 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 (_conversions.ExpressionOfTypeMatchesPatternType(constantPattern.Value.Type, type) == 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 (_conversions.ExpressionOfTypeMatchesPatternType(declarationPattern.DeclaredType.Type.TupleUnderlyingTypeOrSelf(), type) == 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);
                }
            }

            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 (_conversions.ExpressionOfTypeMatchesPatternType(decisionTree.Type, type) == 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);
            }
        }