// 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); }
/// <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; } } }
// 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); } }
/// <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); } }
/// <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); }
/// <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); } } }
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)); }
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; } }
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); }
/// <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); } }
/// <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); } }
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)); } }
/// <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); } }
public override void VisitPattern(BoundExpression expression, BoundPattern pattern) { base.VisitPattern(expression, pattern); NoteDeclaredPatternVariables(pattern); }