private DecisionTree AddByValue(DecisionTree.ByValue byValue, BoundConstantPattern value, DecisionMaker makeDecision) { Debug.Assert(value.Value.Type.Equals(byValue.Type, TypeCompareKind.IgnoreDynamicAndTupleNames | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes)); if (byValue.Default != null) { return(AddByValue(byValue.Default, value, makeDecision)); } Debug.Assert(value.ConstantValue != null); object valueKey = value.ConstantValue.Value; DecisionTree valueDecision; if (byValue.ValueAndDecision.TryGetValue(valueKey, out valueDecision)) { valueDecision = Add(valueDecision, makeDecision); } else { valueDecision = makeDecision(byValue.Expression, byValue.Type); byValue.ValueAndDecision.Add(valueKey, valueDecision); } if (byValue.Type.SpecialType == SpecialType.System_Boolean && byValue.ValueAndDecision.Count == 2 && byValue.ValueAndDecision.Values.All(d => d.MatchIsComplete)) { byValue.MatchIsComplete = true; } return(valueDecision); }
private DecisionTree AddByValue(DecisionTree.ByValue byValue, BoundConstantPattern value, DecisionMaker makeDecision) { Debug.Assert(value.Value.Type.Equals(byValue.Type, TypeCompareKind.IgnoreDynamicAndTupleNames)); if (byValue.Default != null) { return(AddByValue(byValue.Default, value, makeDecision)); } // For error recovery, to avoid "unreachable code" diagnostics when there is a bad case // label, we use the case label itself as the value key. object valueKey = value.ConstantValue?.Value ?? value; DecisionTree valueDecision; if (byValue.ValueAndDecision.TryGetValue(valueKey, out valueDecision)) { valueDecision = Add(valueDecision, makeDecision); } else { valueDecision = makeDecision(byValue.Expression, byValue.Type); byValue.ValueAndDecision.Add(valueKey, valueDecision); } if (byValue.Type.SpecialType == SpecialType.System_Boolean && byValue.ValueAndDecision.Count == 2 && byValue.ValueAndDecision.Values.All(d => d.MatchIsComplete)) { byValue.MatchIsComplete = true; } return(valueDecision); }
private DecisionTree AddByValue(DecisionTree decision, BoundConstantPattern value, DecisionMaker makeDecision) { Debug.Assert(!decision.MatchIsComplete); // otherwise we would have given a subsumption error if (value.ConstantValue == null) { // If value.ConstantValue == null, we have a bad expression in a case label. // The case label is considered unreachable. return(null); } switch (decision.Kind) { case DecisionTree.DecisionKind.ByType: return(AddByValue((DecisionTree.ByType)decision, value, makeDecision)); case DecisionTree.DecisionKind.ByValue: return(AddByValue((DecisionTree.ByValue)decision, value, makeDecision)); case DecisionTree.DecisionKind.Guarded: return(AddByValue((DecisionTree.Guarded)decision, value, makeDecision)); default: throw ExceptionUtilities.UnexpectedValue(decision.Kind); } }
private DecisionTree AddByValue(DecisionTree.Guarded guarded, BoundConstantPattern value, DecisionMaker makeDecision) { if (guarded.Default != null) { Debug.Assert(!guarded.Default.MatchIsComplete); // otherwise we would have given a subsumption error } else { // There is no default at this branch of the decision tree, so we create one. // Before the decision tree can match by value, it needs to test if the input is of the required type. // So we create a ByType node to represent that test. guarded.Default = new DecisionTree.ByType(guarded.Expression, guarded.Type, null); } return(AddByValue(guarded.Default, value, makeDecision)); }
private DecisionTree AddByValue(DecisionTree.Guarded guarded, BoundConstantPattern value, DecisionMaker makeDecision) { if (guarded.Default != null) { if (guarded.Default.MatchIsComplete) { return(null); } } else { guarded.Default = new DecisionTree.ByValue(guarded.Expression, guarded.Type, null); } return(AddByValue(guarded.Default, value, makeDecision)); }
private DecisionTree AddByValue(DecisionTree decision, BoundConstantPattern value, DecisionMaker makeDecision) { Debug.Assert(!decision.MatchIsComplete); // otherwise we would have given a subsumption error // Even if value.ConstantValue == null, we proceed here for error recovery, so that the case label isn't // dropped on the floor. That is useful, for example to suppress unreachable code warnings on bad case labels. switch (decision.Kind) { case DecisionTree.DecisionKind.ByType: return(AddByValue((DecisionTree.ByType)decision, value, makeDecision)); case DecisionTree.DecisionKind.ByValue: return(AddByValue((DecisionTree.ByValue)decision, value, makeDecision)); case DecisionTree.DecisionKind.Guarded: return(AddByValue((DecisionTree.Guarded)decision, value, makeDecision)); default: throw ExceptionUtilities.UnexpectedValue(decision.Kind); } }
public override BoundNode VisitConstantPattern(BoundConstantPattern node) { Visit(node.Value); return(null); }
private BoundExpression MakeIsConstantPattern(BoundConstantPattern loweredPattern, BoundExpression loweredInput) { return(CompareWithConstant(loweredInput, loweredPattern.Value)); }
private BoundExpression LowerConstantPattern(BoundConstantPattern pattern, BoundExpression input) { return(CompareWithConstant(input, VisitExpression(pattern.Value))); }
private DecisionTree AddByValue(DecisionTree.ByType byType, BoundConstantPattern value, DecisionMaker makeDecision) { if (byType.Default != null) { try { return(AddByValue(byType.Default, value, makeDecision)); } finally { if (byType.Default.MatchIsComplete) { // This code may be unreachable due to https://github.com/dotnet/roslyn/issues/16878 byType.MatchIsComplete = true; } } } if (value.ConstantValue == ConstantValue.Null) { // This should not occur, as the caller will have invoked AddByNull instead. throw ExceptionUtilities.Unreachable; } if ((object)value.Value.Type == null) { return(null); } foreach (var kvp in byType.TypeAndDecision) { var matchedType = kvp.Key; var decision = kvp.Value; // See if the test is already subsumed switch (ExpressionOfTypeMatchesPatternType(value.Value.Type, matchedType, ref _useSiteDiagnostics)) { case true: if (decision.MatchIsComplete) { return(null); } continue; case false: case null: continue; } } DecisionTree forType = null; // This new type test should logically be last. However it might be the same type as the one that is already // last. In that case we can produce better code by piggy-backing our new case on to the last decision. // Also, the last one might be a non-overlapping type, in which case we can piggy-back onto the second-last // type test. for (int i = byType.TypeAndDecision.Count - 1; i >= 0; i--) { var kvp = byType.TypeAndDecision[i]; var matchedType = kvp.Key; var decision = kvp.Value; if (matchedType.Equals(value.Value.Type, TypeCompareKind.IgnoreDynamicAndTupleNames)) { forType = decision; break; } else if (ExpressionOfTypeMatchesPatternType(value.Value.Type, matchedType, ref _useSiteDiagnostics) != false) { // because there is overlap, we cannot reuse some earlier entry break; } } // if we did not piggy-back, then create a new decision tree node for the type. if (forType == null) { var type = value.Value.Type; var localSymbol = new SynthesizedLocal(_enclosingSymbol as MethodSymbol, type, SynthesizedLocalKind.PatternMatching, Syntax, false, RefKind.None); var narrowedExpression = new BoundLocal(Syntax, localSymbol, null, type); forType = new DecisionTree.ByValue(narrowedExpression, value.Value.Type.TupleUnderlyingTypeOrSelf(), localSymbol); byType.TypeAndDecision.Add(new KeyValuePair <TypeSymbol, DecisionTree>(value.Value.Type, forType)); } return(AddByValue(forType, value, makeDecision)); }
private DecisionTree AddByValue(DecisionTree.ByType byType, BoundConstantPattern value, DecisionMaker makeDecision) { if (byType.Default != null) { try { return(AddByValue(byType.Default, value, makeDecision)); } finally { if (byType.Default.MatchIsComplete) { byType.MatchIsComplete = true; } } } if (value.ConstantValue == ConstantValue.Null) { return(byType.Expression.ConstantValue?.IsNull == false ? null : AddByNull((DecisionTree)byType, makeDecision)); } foreach (var kvp in byType.TypeAndDecision) { var matchedType = kvp.Key; var decision = kvp.Value; // See if the test is already subsumed switch (ExpressionOfTypeMatchesPatternType(value.Value.Type, matchedType, ref _useSiteDiagnostics)) { case true: if (decision.MatchIsComplete) { return(null); } continue; case false: case null: continue; } } DecisionTree forType = null; // Find an existing decision tree for the expression's type. Since this new test // should logically be last, we look for the last one we can piggy-back it onto. for (int i = byType.TypeAndDecision.Count - 1; i >= 0 && forType == null; i--) { var kvp = byType.TypeAndDecision[i]; var matchedType = kvp.Key; var decision = kvp.Value; if (matchedType.TupleUnderlyingTypeOrSelf() == value.Value.Type.TupleUnderlyingTypeOrSelf()) { forType = decision; break; } else if (ExpressionOfTypeMatchesPatternType(value.Value.Type, matchedType, ref _useSiteDiagnostics) != false) { break; } } if (forType == null) { var type = value.Value.Type; var localSymbol = new SynthesizedLocal(_enclosingSymbol as MethodSymbol, type, SynthesizedLocalKind.PatternMatchingTemp, Syntax, false, RefKind.None); var narrowedExpression = new BoundLocal(Syntax, localSymbol, null, type); forType = new DecisionTree.ByValue(narrowedExpression, value.Value.Type.TupleUnderlyingTypeOrSelf(), localSymbol); byType.TypeAndDecision.Add(new KeyValuePair <TypeSymbol, DecisionTree>(value.Value.Type, forType)); } return(AddByValue(forType, value, makeDecision)); }
private DecisionTree AddByValue(DecisionTree.ByType byType, BoundConstantPattern value, DecisionMaker makeDecision) { if (byType.Default != null) { try { return(AddByValue(byType.Default, value, makeDecision)); } finally { if (byType.Default.MatchIsComplete) { // This code may be unreachable due to https://github.com/dotnet/roslyn/issues/16878 byType.MatchIsComplete = true; } } } if (value.ConstantValue == ConstantValue.Null) { // This should not occur, as the caller will have invoked AddByNull instead. throw ExceptionUtilities.Unreachable; } if ((object)value.Value.Type == null || value.ConstantValue == null) { return(null); } foreach (var kvp in byType.TypeAndDecision) { var matchedType = kvp.Key; var decision = kvp.Value; // See if the test is already subsumed switch (ExpressionOfTypeMatchesPatternType(value.Value.Type, matchedType, ref _useSiteDiagnostics)) { case true: if (decision.MatchIsComplete) { // Subsumed case have been eliminated by semantic analysis. Debug.Assert(false); return(null); } continue; case false: case null: continue; } } DecisionTree forType = null; // This new type test should logically be last. However it might be the same type as the one that is already // last. In that case we can produce better code by piggy-backing our new case on to the last decision. // Also, the last one might be a non-overlapping type, in which case we can piggy-back onto the second-last // type test. for (int i = byType.TypeAndDecision.Count - 1; i >= 0; i--) { var kvp = byType.TypeAndDecision[i]; var matchedType = kvp.Key; var decision = kvp.Value; if (matchedType.Equals(value.Value.Type, TypeCompareKind.IgnoreDynamicAndTupleNames | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes)) { forType = decision; break; } switch (ExpressionOfTypeMatchesPatternType(value.Value.Type, matchedType, ref _useSiteDiagnostics)) { case true: if (decision.MatchIsComplete) { // we should have reported this case as subsumed already. Debug.Assert(false); return(null); } else { goto case null; } case false: continue; case null: // because there is overlap, we cannot reuse some earlier entry goto noReuse; } } noReuse :; // if we did not piggy-back, then create a new decision tree node for the type. if (forType == null) { var type = value.Value.Type; if (byType.Type.Equals(type, TypeCompareKind.AllIgnoreOptions)) { // reuse the input expression when we have an equivalent type to reduce the number of generated temps forType = new DecisionTree.ByValue(byType.Expression, type.TupleUnderlyingTypeOrSelf(), null); } else { var narrowedExpression = GetBoundPatternMatchingLocal(type); forType = new DecisionTree.ByValue(narrowedExpression, type.TupleUnderlyingTypeOrSelf(), narrowedExpression.LocalSymbol); } byType.TypeAndDecision.Add(new KeyValuePair <TypeSymbol, DecisionTree>(type, forType)); } return(AddByValue(forType, value, makeDecision)); }