private DecisionTree AddByType(DecisionTree.ByType byType, TypeSymbol type, DecisionMaker makeDecision) { if (byType.Default != null) { try { return(AddByType(byType.Default, type, makeDecision)); } finally { if (byType.Default.MatchIsComplete) { byType.MatchIsComplete = true; } } } foreach (var kvp in byType.TypeAndDecision) { var MatchedType = kvp.Key; var Decision = kvp.Value; // See if matching Type matches this value switch (ExpressionOfTypeMatchesPatternType(type, MatchedType, ref _useSiteDiagnostics)) { case true: if (Decision.MatchIsComplete) { return(null); } continue; case false: continue; case null: continue; } } var localSymbol = new SynthesizedLocal(_enclosingSymbol as MethodSymbol, type, SynthesizedLocalKind.PatternMatchingTemp, Syntax, false, RefKind.None); var expression = new BoundLocal(Syntax, localSymbol, null, type); var result = makeDecision(expression, type); Debug.Assert(result.Temp == null); result.Temp = localSymbol; byType.TypeAndDecision.Add(new KeyValuePair <TypeSymbol, DecisionTree>(type, result)); if (ExpressionOfTypeMatchesPatternType(byType.Type, type, ref _useSiteDiagnostics) == true && result.MatchIsComplete && byType.WhenNull?.MatchIsComplete == true) { byType.MatchIsComplete = true; } return(result); }
private DecisionTree AddByType(DecisionTree.ByType byType, TypeSymbol type, DecisionMaker makeDecision) { if (byType.Default != null) { try { return(AddByType(byType.Default, type, makeDecision)); } finally { if (byType.Default.MatchIsComplete) { byType.MatchIsComplete = true; } } } // if the last type is the type we need, add to it DecisionTree result = null; if (byType.TypeAndDecision.Count != 0) { var lastTypeAndDecision = byType.TypeAndDecision.Last(); if (lastTypeAndDecision.Key.Equals(type, TypeCompareKind.IgnoreDynamicAndTupleNames)) { result = Add(lastTypeAndDecision.Value, makeDecision); } } if (result == null) { var localSymbol = new SynthesizedLocal(_enclosingSymbol as MethodSymbol, type, SynthesizedLocalKind.PatternMatching, Syntax, false, RefKind.None); var expression = new BoundLocal(Syntax, localSymbol, null, type); result = makeDecision(expression, type); Debug.Assert(result.Temp == null); result.Temp = localSymbol; byType.TypeAndDecision.Add(new KeyValuePair <TypeSymbol, DecisionTree>(type, result)); } if (ExpressionOfTypeMatchesPatternType(byType.Type, type, ref _useSiteDiagnostics) == true && result.MatchIsComplete && byType.WhenNull?.MatchIsComplete == true) { byType.MatchIsComplete = true; } return(result); }
private DecisionTree AddByType(DecisionTree.ByType byType, TypeSymbol type, DecisionMaker makeDecision) { if (byType.Default != null) { try { return(AddByType(byType.Default, type, makeDecision)); } finally { if (byType.Default.MatchIsComplete) { byType.MatchIsComplete = true; } } } // if the last type is the type we need, add to it DecisionTree result = null; if (byType.TypeAndDecision.Count != 0) { var lastTypeAndDecision = byType.TypeAndDecision.Last(); if (lastTypeAndDecision.Key.Equals(type, TypeCompareKind.IgnoreDynamicAndTupleNames | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes)) { result = Add(lastTypeAndDecision.Value, makeDecision); } } if (result == null) { var expression = GetBoundPatternMatchingLocal(type); result = makeDecision(expression, type); Debug.Assert(result.Temp == null); result.Temp = expression.LocalSymbol; byType.TypeAndDecision.Add(new KeyValuePair <TypeSymbol, DecisionTree>(type, result)); } if (ExpressionOfTypeMatchesPatternType(byType.Type, type, ref _useSiteDiagnostics) == true && result.MatchIsComplete && byType.WhenNull?.MatchIsComplete == true) { byType.MatchIsComplete = true; } return(result); }
private bool NonNullHandled(DecisionTree.ByType byType) { var inputType = byType.Type.StrippedType().TupleUnderlyingTypeOrSelf(); foreach (var td in byType.TypeAndDecision) { var type = td.Key; var decision = td.Value; if (ExpressionOfTypeMatchesPatternType(inputType, type, ref _useSiteDiagnostics) == true && decision.MatchIsComplete) { return(true); } } return(false); }
private DecisionTree Add(DecisionTree.ByType byType, DecisionMaker makeDecision) { try { if (byType.Default == null) { byType.Default = makeDecision(byType.Expression, byType.Type); return(byType.Default); } else { return(Add(byType.Default, makeDecision)); } } finally { if (byType.Default.MatchIsComplete) { byType.MatchIsComplete = true; } } }
private DecisionTree AddByNull(DecisionTree.ByType byType, DecisionMaker makeDecision) { if (byType.WhenNull?.MatchIsComplete == true || byType.Default?.MatchIsComplete == true) { return(null); } if (byType.Default != null) { try { return(AddByNull(byType.Default, makeDecision)); } finally { if (byType.Default.MatchIsComplete) { byType.MatchIsComplete = true; } } } DecisionTree result; if (byType.WhenNull == null) { result = byType.WhenNull = makeDecision(byType.Expression, byType.Type); } else { result = Add(byType.WhenNull, makeDecision); } if (byType.WhenNull.MatchIsComplete && NonNullHandled(byType)) { byType.MatchIsComplete = true; } return(result); }
private DecisionTree AddByNull(DecisionTree.ByType byType, DecisionMaker makeDecision) { // these tree cannot be complete, as if that were so we would have considered this decision subsumed. Debug.Assert(byType.WhenNull?.MatchIsComplete != true); Debug.Assert(byType.Default?.MatchIsComplete != true); if (byType.Default != null) { try { return(AddByNull(byType.Default, makeDecision)); } finally { if (byType.Default.MatchIsComplete) { byType.MatchIsComplete = true; } } } DecisionTree result; if (byType.WhenNull == null) { result = byType.WhenNull = makeDecision(byType.Expression, byType.Type); } else { result = Add(byType.WhenNull, makeDecision); } if (byType.WhenNull.MatchIsComplete && NonNullHandled(byType)) { byType.MatchIsComplete = true; } return(result); }
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 void LowerDecisionTree(DecisionTree.ByType byType) { var inputConstant = byType.Expression.ConstantValue; if (inputConstant != null) { if (inputConstant.IsNull) { // input is the constant null LowerDecisionTree(byType.Expression, byType.WhenNull); if (byType.WhenNull?.MatchIsComplete != true) { LowerDecisionTree(byType.Expression, byType.Default); } } else { // input is a non-null constant foreach (var kvp in byType.TypeAndDecision) { LowerDecisionTree(byType.Expression, kvp.Value); if (kvp.Value.MatchIsComplete) { return; } } LowerDecisionTree(byType.Expression, byType.Default); } } else { var defaultLabel = _factory.GenerateLabel("byTypeDefault"); // input is not a constant if (byType.Type.CanContainNull()) { // first test for null var notNullLabel = _factory.GenerateLabel("notNull"); var inputExpression = byType.Expression; var objectType = _factory.SpecialType(SpecialType.System_Object); var nullValue = _factory.Null(objectType); BoundExpression notNull = byType.Type.IsNullableType() ? _localRewriter.RewriteNullableNullEquality(_factory.Syntax, BinaryOperatorKind.NullableNullNotEqual, byType.Expression, nullValue, _factory.SpecialType(SpecialType.System_Boolean)) : _factory.ObjectNotEqual(nullValue, _factory.Convert(objectType, byType.Expression)); _loweredDecisionTree.Add(_factory.ConditionalGoto(notNull, notNullLabel, true)); LowerDecisionTree(byType.Expression, byType.WhenNull); if (byType.WhenNull?.MatchIsComplete != true) { _loweredDecisionTree.Add(_factory.Goto(defaultLabel)); } _loweredDecisionTree.Add(_factory.Label(notNullLabel)); } else { Debug.Assert(byType.WhenNull == null); } foreach (var td in byType.TypeAndDecision) { // then test for each type, sequentially var type = td.Key; var decision = td.Value; var failLabel = _factory.GenerateLabel("failedDecision"); var testAndCopy = TypeTestAndCopyToTemp(byType.Expression, decision.Expression); _loweredDecisionTree.Add(_factory.ConditionalGoto(testAndCopy, failLabel, false)); LowerDecisionTree(decision.Expression, decision); _loweredDecisionTree.Add(_factory.Label(failLabel)); } // finally, the default for when no type matches _loweredDecisionTree.Add(_factory.Label(defaultLabel)); LowerDecisionTree(byType.Expression, byType.Default); } }
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)); }