/// <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);
     }
 }
        /// <summary>
        /// Sets <paramref name="innerPattern"/> to the inner pattern after stripping off outer
        /// <see cref="BoundNegatedPattern"/>s, and returns true if the original pattern is a
        /// negated form of the inner pattern.
        /// </summary>
        internal bool IsNegated(out BoundPattern innerPattern)
        {
            innerPattern = this;
            bool negated = false;

            while (innerPattern is BoundNegatedPattern negatedPattern)
            {
                negated      = !negated;
                innerPattern = negatedPattern.Negated;
            }
            return(negated);
        }
 /// <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);
         }
     }
 }
Example #4
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;
     }
 }
Example #5
0
        private BoundExpression ConvertPatternToExpression(BoundPattern pattern, BoundExpression matchExpr)
        {
            var syntax = pattern.Syntax;
            var expr   = default(BoundExpression);

            switch (pattern)
            {
            case BoundMatchAnyPattern b:
            {
                expr = Literal(syntax, true);
            }
            break;

            case BoundConstantPattern constantPattern:
            {
                expr = Equal(syntax, matchExpr, constantPattern.Expression);
            }
            break;

            // TODO: when infix function parsing (binary and unary!) is done
            // case BoundInfixPattern infixPattern:
            // {
            //     var comparison = BoundBinaryOperator.Bind( TokenType.Eq , matchExpr.Type , infixPattern.Expression.Type )!;
            //
            //     var args = ImmutableArray.Create( matchExpr , infixPattern.Expression );
            //     var expr = new BoundCallExpression( infixFuncSymbol , args );
            //
            //     return expr;
            // }
            // break;

            default:
            {
                throw new Exception("unreachable");
            }
            }

            return(this.RewriteExpression(expr !));
        }
Example #6
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);
            }
        }
Example #7
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);
            }
        }
 public override void VisitPattern(BoundExpression expression, BoundPattern pattern)
 {
     base.VisitPattern(expression, pattern);
     NoteDeclaredPatternVariables(pattern);
 }
        /// <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 (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 (ExpressionOfTypeMatchesPatternType(
                                                declarationPattern.DeclaredType.Type.TupleUnderlyingTypeOrSelf(), 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);
                        }
                    }
                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);
            }
        }