/// <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); } } }
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; } }
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 !)); }
// 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); } }
// 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); } }