未验证 提交 962c84b3 编写于 作者: N Neal Gafter 提交者: GitHub

Extract nullable state operations to extension methods. (#31188)

Add tests to demonstrate that they are associative and commutative.
Add (skipped) tests to demonstrate that we have inconsistent meet and join functions.
    We will have to decide how to handle the differences.
I have not yet extracted the NullableState operations from ConversionsBase
    (e.g. testing if one NullableState is higher or lower than another in the lattice)
Also removed some inoperative code in MethodTypeInference
上级 891fc9b3
......@@ -1415,11 +1415,6 @@ private void ExactInference(TypeSymbolWithAnnotations source, TypeSymbolWithAnno
return;
}
if (ExactTupleInference(source, target, ref useSiteDiagnostics))
{
return;
}
// SPEC: * Otherwise, if V is a constructed type C<V1...Vk> and U is a constructed
// SPEC: type C<U1...Uk> then an exact inference is made
// SPEC: from each Ui to the corresponding Vi.
......@@ -1527,7 +1522,7 @@ private bool ExactNullableInference(TypeSymbolWithAnnotations source, TypeSymbol
return ExactOrBoundsNullableInference(ExactOrBoundsKind.Exact, source, target, ref useSiteDiagnostics);
}
private bool ExactOrBoundsTupleInference(ExactOrBoundsKind kind, TypeSymbolWithAnnotations source, TypeSymbolWithAnnotations target, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
private bool LowerBoundTupleInference(TypeSymbolWithAnnotations source, TypeSymbolWithAnnotations target, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{
Debug.Assert(!source.IsNull);
Debug.Assert(!target.IsNull);
......@@ -1548,17 +1543,12 @@ private bool ExactOrBoundsTupleInference(ExactOrBoundsKind kind, TypeSymbolWithA
for (int i = 0; i < sourceTypes.Length; i++)
{
ExactOrBoundsInference(kind, sourceTypes[i], targetTypes[i], ref useSiteDiagnostics);
LowerBoundInference(sourceTypes[i], targetTypes[i], ref useSiteDiagnostics);
}
return true;
}
private bool ExactTupleInference(TypeSymbolWithAnnotations source, TypeSymbolWithAnnotations target, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{
return ExactOrBoundsTupleInference(ExactOrBoundsKind.Exact, source, target, ref useSiteDiagnostics);
}
private bool ExactConstructedInference(TypeSymbolWithAnnotations source, TypeSymbolWithAnnotations target, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{
Debug.Assert(!source.IsNull);
......@@ -1802,11 +1792,6 @@ private bool LowerBoundNullableInference(TypeSymbolWithAnnotations source, TypeS
return ExactOrBoundsNullableInference(ExactOrBoundsKind.LowerBound, source, target, ref useSiteDiagnostics);
}
private bool LowerBoundTupleInference(TypeSymbolWithAnnotations source, TypeSymbolWithAnnotations target, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{
return ExactOrBoundsTupleInference(ExactOrBoundsKind.LowerBound, source, target, ref useSiteDiagnostics);
}
private bool LowerBoundConstructedInference(TypeSymbol source, TypeSymbol target, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{
Debug.Assert((object)source != null);
......
......@@ -4648,22 +4648,7 @@ protected override void UnionWith(ref LocalState self, ref LocalState other)
{
NullableAnnotation selfAnnotation = self[slot];
NullableAnnotation otherAnnotation = other[slot];
NullableAnnotation union;
if (selfAnnotation.IsAnyNotNullable() || otherAnnotation.IsAnyNotNullable())
{
union = selfAnnotation == NullableAnnotation.NotNullableBasedOnAnalysis || otherAnnotation == NullableAnnotation.NotNullableBasedOnAnalysis ?
NullableAnnotation.NotNullableBasedOnAnalysis : NullableAnnotation.NotNullable;
}
else if (selfAnnotation == NullableAnnotation.Unknown || otherAnnotation == NullableAnnotation.Unknown)
{
union = NullableAnnotation.Unknown;
}
else
{
union = selfAnnotation == NullableAnnotation.NullableBasedOnAnalysis || otherAnnotation == NullableAnnotation.NullableBasedOnAnalysis ?
NullableAnnotation.NullableBasedOnAnalysis : NullableAnnotation.Nullable;
}
NullableAnnotation union = selfAnnotation.MeetForFlowAnalysisFinally(otherAnnotation);
if (selfAnnotation != union)
{
......@@ -4688,57 +4673,7 @@ protected override bool IntersectWith(ref LocalState self, ref LocalState other)
{
NullableAnnotation selfAnnotation = self[slot];
NullableAnnotation otherAnnotation = other[slot];
NullableAnnotation intersection;
if (selfAnnotation.IsAnyNullable() || otherAnnotation.IsAnyNullable())
{
intersection = selfAnnotation == NullableAnnotation.Nullable || otherAnnotation == NullableAnnotation.Nullable ?
NullableAnnotation.Nullable : NullableAnnotation.NullableBasedOnAnalysis;
}
else if (selfAnnotation == NullableAnnotation.Unknown)
{
if (otherAnnotation == NullableAnnotation.Unknown || otherAnnotation == NullableAnnotation.NotNullableBasedOnAnalysis)
{
intersection = NullableAnnotation.Unknown;
}
else
{
Debug.Assert(otherAnnotation == NullableAnnotation.NotNullable);
if (isPossiblyNullableReferenceTypeTypeParameter(slot))
{
intersection = otherAnnotation;
}
else
{
intersection = NullableAnnotation.Unknown;
}
}
}
else if (otherAnnotation == NullableAnnotation.Unknown)
{
if (selfAnnotation == NullableAnnotation.NotNullableBasedOnAnalysis)
{
intersection = NullableAnnotation.Unknown;
}
else
{
Debug.Assert(selfAnnotation == NullableAnnotation.NotNullable);
if (isPossiblyNullableReferenceTypeTypeParameter(slot))
{
intersection = selfAnnotation;
}
else
{
intersection = NullableAnnotation.Unknown;
}
}
}
else
{
intersection = selfAnnotation == NullableAnnotation.NotNullable || otherAnnotation == NullableAnnotation.NotNullable ?
NullableAnnotation.NotNullable : NullableAnnotation.NotNullableBasedOnAnalysis;
}
NullableAnnotation intersection = selfAnnotation.JoinForFlowAnalysisBranches(otherAnnotation, (slot, this), isPossiblyNullableReferenceTypeTypeParameterDelegate);
if (selfAnnotation != intersection)
{
self[slot] = intersection;
......@@ -4758,14 +4693,14 @@ protected override bool IntersectWith(ref LocalState self, ref LocalState other)
Debug.Assert(!other.Reachable);
return false;
}
bool isPossiblyNullableReferenceTypeTypeParameter(int slot)
{
Symbol symbol = variableBySlot[slot].Symbol;
return (object)symbol != null && VariableType(symbol).TypeSymbol?.IsPossiblyNullableReferenceTypeTypeParameter() == true;
}
}
private readonly static Func<(int slot, NullableWalker self), bool> isPossiblyNullableReferenceTypeTypeParameterDelegate = args =>
{
Symbol symbol = args.self.variableBySlot[args.slot].Symbol;
return (object)symbol != null && VariableType(symbol).TypeSymbol?.IsPossiblyNullableReferenceTypeTypeParameter() == true;
};
[DebuggerDisplay("{GetDebuggerDisplay(), nq}")]
#if REFERENCE_STATE
internal class LocalState : AbstractLocalState
......
......@@ -31,6 +31,222 @@ public static bool IsAnyNotNullable(this NullableAnnotation annotation)
{
return annotation == NullableAnnotation.NotNullable || annotation == NullableAnnotation.NotNullableBasedOnAnalysis;
}
/// <summary>
/// Join nullable annotations from the set of lower bounds for fixing a type parameter.
/// </summary>
public static NullableAnnotation JoinForFixingLowerBounds(this NullableAnnotation a, NullableAnnotation b)
{
if (a == b)
{
return a;
}
if (a.IsAnyNullable() && b.IsAnyNullable())
{
return NullableAnnotation.Nullable;
}
// If nullability on both sides matches - result is that nullability (trivial cases like these are handled before the switch)
// If either candidate is nullable - result is nullable
// Otherwise - result is "oblivious".
if (a.IsAnyNullable())
{
Debug.Assert(!b.IsAnyNullable());
return a;
}
if (b.IsAnyNullable())
{
return b;
}
if (a == NullableAnnotation.Unknown || b == NullableAnnotation.Unknown)
{
return NullableAnnotation.Unknown;
}
Debug.Assert((a == NullableAnnotation.NotNullable && b == NullableAnnotation.NotNullableBasedOnAnalysis) ||
(b == NullableAnnotation.NotNullable && a == NullableAnnotation.NotNullableBasedOnAnalysis));
return NullableAnnotation.NotNullable; // It is reasonable to settle on this value because the difference in annotations is either
// not significant for the type, or candidate corresponding to this value is possibly a
// nullable reference type type parameter and nullable should win.
}
/// <summary>
/// Join nullable annotations from distinct branches during flow analysis.
/// </summary>
public static NullableAnnotation JoinForFlowAnalysisBranches<T>(this NullableAnnotation selfAnnotation, NullableAnnotation otherAnnotation, T type, Func<T, bool> isPossiblyNullableReferenceTypeTypeParameter)
{
if (selfAnnotation == otherAnnotation)
{
return selfAnnotation;
}
if (selfAnnotation.IsAnyNullable() || otherAnnotation.IsAnyNullable())
{
return selfAnnotation == NullableAnnotation.Nullable || otherAnnotation == NullableAnnotation.Nullable ?
NullableAnnotation.Nullable : NullableAnnotation.NullableBasedOnAnalysis;
}
else if (selfAnnotation == NullableAnnotation.Unknown)
{
if (otherAnnotation == NullableAnnotation.Unknown || otherAnnotation == NullableAnnotation.NotNullableBasedOnAnalysis)
{
return NullableAnnotation.Unknown;
}
else
{
Debug.Assert(otherAnnotation == NullableAnnotation.NotNullable);
if (isPossiblyNullableReferenceTypeTypeParameter(type))
{
return otherAnnotation;
}
else
{
return NullableAnnotation.Unknown;
}
}
}
else if (otherAnnotation == NullableAnnotation.Unknown)
{
if (selfAnnotation == NullableAnnotation.NotNullableBasedOnAnalysis)
{
return NullableAnnotation.Unknown;
}
else
{
Debug.Assert(selfAnnotation == NullableAnnotation.NotNullable);
if (isPossiblyNullableReferenceTypeTypeParameter(type))
{
return selfAnnotation;
}
else
{
return NullableAnnotation.Unknown;
}
}
}
else
{
return selfAnnotation == NullableAnnotation.NotNullable || otherAnnotation == NullableAnnotation.NotNullable ?
NullableAnnotation.NotNullable : NullableAnnotation.NotNullableBasedOnAnalysis;
}
}
/// <summary>
/// Meet two nullable annotations for computing the nullable annotation of a type parameter from upper bounds.
/// </summary>
public static NullableAnnotation MeetForFixingUpperBounds(this NullableAnnotation a, NullableAnnotation b)
{
if (a == b)
{
return a;
}
if (a.IsAnyNullable() && b.IsAnyNullable())
{
return NullableAnnotation.Nullable;
}
// If nullability on both sides matches - result is that nullability (trivial cases like these are handled before the switch)
// If either candidate is not nullable - result is not nullable
// Otherwise - result is "oblivious".
if (a == NullableAnnotation.NotNullableBasedOnAnalysis || b == NullableAnnotation.NotNullableBasedOnAnalysis)
{
return NullableAnnotation.NotNullableBasedOnAnalysis;
}
if (a == NullableAnnotation.NotNullable || b == NullableAnnotation.NotNullable)
{
return NullableAnnotation.NotNullable;
}
Debug.Assert(a == NullableAnnotation.Unknown || b == NullableAnnotation.Unknown);
return NullableAnnotation.Unknown;
}
/// <summary>
/// Meet two nullable annotations from distinct states for the meet (union) operation in flow analysis.
/// </summary>
public static NullableAnnotation MeetForFlowAnalysisFinally(this NullableAnnotation selfAnnotation, NullableAnnotation otherAnnotation)
{
if (selfAnnotation == otherAnnotation)
{
return selfAnnotation;
}
if (selfAnnotation.IsAnyNotNullable() || otherAnnotation.IsAnyNotNullable())
{
return selfAnnotation == NullableAnnotation.NotNullableBasedOnAnalysis || otherAnnotation == NullableAnnotation.NotNullableBasedOnAnalysis ?
NullableAnnotation.NotNullableBasedOnAnalysis : NullableAnnotation.NotNullable;
}
else if (selfAnnotation == NullableAnnotation.Unknown || otherAnnotation == NullableAnnotation.Unknown)
{
return NullableAnnotation.Unknown;
}
else
{
return selfAnnotation == NullableAnnotation.NullableBasedOnAnalysis || otherAnnotation == NullableAnnotation.NullableBasedOnAnalysis ?
NullableAnnotation.NullableBasedOnAnalysis : NullableAnnotation.Nullable;
}
}
/// <summary>
/// Check that two nullable annotations are "compatible", which means they could be the same. Return the
/// nullable annotation to be used as a result. Also returns through <paramref name="hadNullabilityMismatch"/>
/// whether the caller should report a warning because there was an actual mismatch (e.g. nullable vs non-nullable).
/// </summary>
public static NullableAnnotation EnsureCompatible<T>(this NullableAnnotation a, NullableAnnotation b, T type, Func<T, bool> isPossiblyNullableReferenceTypeTypeParameter, out bool hadNullabilityMismatch)
{
hadNullabilityMismatch = false;
if (a == b)
{
return a;
}
if (a.IsAnyNullable() && b.IsAnyNullable())
{
return NullableAnnotation.Nullable;
}
// If nullability on both sides matches - result is that nullability (trivial cases like these are handled before the switch)
// If either candidate is "oblivious" - result is the nullability of the other candidate
// Otherwise - we declare a mismatch and result is not nullable.
if (a == NullableAnnotation.Unknown)
{
return b;
}
if (b == NullableAnnotation.Unknown)
{
return a;
}
// At this point we know that either nullability of both sides is significantly different NotNullable vs. Nullable,
// or we are dealing with different flavors of not nullable for both candidates
if ((a == NullableAnnotation.NotNullable && b == NullableAnnotation.NotNullableBasedOnAnalysis) ||
(b == NullableAnnotation.NotNullable && a == NullableAnnotation.NotNullableBasedOnAnalysis))
{
if (!isPossiblyNullableReferenceTypeTypeParameter(type))
{
// For this type both not nullable annotations are equivalent and therefore match.
return NullableAnnotation.NotNullable;
}
// We are dealing with different flavors of not nullable for a possibly nullable reference type parameter,
// we don't have a reliable way to merge them since one of them can actually represent a nullable type.
}
else
{
Debug.Assert(a.IsAnyNullable() != b.IsAnyNullable());
}
hadNullabilityMismatch = true;
return NullableAnnotation.NotNullable;
}
}
/// <summary>
......@@ -309,104 +525,21 @@ internal TypeSymbolWithAnnotations MergeNullability(TypeSymbolWithAnnotations ot
private static NullableAnnotation MergeNullableAnnotation(TypeSymbol type, NullableAnnotation a, NullableAnnotation b, VarianceKind variance, out bool hadNullabilityMismatch)
{
hadNullabilityMismatch = false;
if (a == b)
{
return a;
}
if (a.IsAnyNullable() && b.IsAnyNullable())
{
return NullableAnnotation.Nullable;
}
switch (variance)
{
case VarianceKind.In:
// If nullability on both sides matches - result is that nullability (trivial cases like these are handled before the switch)
// If either candidate is not nullable - result is not nullable
// Otherwise - result is "oblivious".
if (a == NullableAnnotation.NotNullableBasedOnAnalysis || b == NullableAnnotation.NotNullableBasedOnAnalysis)
{
return NullableAnnotation.NotNullableBasedOnAnalysis;
}
if (a == NullableAnnotation.NotNullable || b == NullableAnnotation.NotNullable)
{
return NullableAnnotation.NotNullable;
}
Debug.Assert(a == NullableAnnotation.Unknown || b == NullableAnnotation.Unknown);
return NullableAnnotation.Unknown;
return a.MeetForFixingUpperBounds(b);
case VarianceKind.Out:
// If nullability on both sides matches - result is that nullability (trivial cases like these are handled before the switch)
// If either candidate is nullable - result is nullable
// Otherwise - result is "oblivious".
if (a.IsAnyNullable())
{
Debug.Assert(!b.IsAnyNullable());
return a;
}
if (b.IsAnyNullable())
{
return b;
}
if (a == NullableAnnotation.Unknown || b == NullableAnnotation.Unknown)
{
return NullableAnnotation.Unknown;
}
Debug.Assert((a == NullableAnnotation.NotNullable && b == NullableAnnotation.NotNullableBasedOnAnalysis) ||
(b == NullableAnnotation.NotNullable && a == NullableAnnotation.NotNullableBasedOnAnalysis));
return NullableAnnotation.NotNullable; // It is reasonable to settle on this value because the difference in annotations is either
// not significant for the type, or candidate corresponding to this value is possibly a
// nullable reference type type parameter and nullable should win.
return a.JoinForFixingLowerBounds(b);
case VarianceKind.None:
return a.EnsureCompatible(b, type, _IsPossiblyNullableReferenceTypeTypeParameterDelegate, out hadNullabilityMismatch);
default:
// If nullability on both sides matches - result is that nullability (trivial cases like these are handled before the switch)
// If either candidate is "oblivious" - result is the nullability of the other candidate
// Otherwise - we declare a mismatch and result is not nullable.
if (a == NullableAnnotation.Unknown)
{
return b;
}
if (b == NullableAnnotation.Unknown)
{
return a;
}
// At this point we know that either nullability of both sides is significantly different NotNullable vs. Nullable,
// or we are dealing with different flavors of not nullable for both candidates
if ((a == NullableAnnotation.NotNullable && b == NullableAnnotation.NotNullableBasedOnAnalysis) ||
(b == NullableAnnotation.NotNullable && a == NullableAnnotation.NotNullableBasedOnAnalysis))
{
if (!type.IsPossiblyNullableReferenceTypeTypeParameter())
{
// For this type both not nullable annotations are equivalent and therefore match.
return NullableAnnotation.NotNullable;
}
// We are dealing with different flavors of not nullable for a possibly nullable reference type parameter,
// we don't have a reliable way to merge them since one of them can actually represent a nullable type.
}
else
{
Debug.Assert(a.IsAnyNullable() != b.IsAnyNullable());
}
hadNullabilityMismatch = true;
return NullableAnnotation.NotNullable;
throw ExceptionUtilities.UnexpectedValue(variance);
}
}
private readonly static Func<TypeSymbol, bool> _IsPossiblyNullableReferenceTypeTypeParameterDelegate = type => type.IsPossiblyNullableReferenceTypeTypeParameter();
public TypeSymbolWithAnnotations WithModifiers(ImmutableArray<CustomModifier> customModifiers) =>
_extensions.WithModifiers(this, customModifiers);
......
......@@ -57132,5 +57132,215 @@ static void F(object? x)
// y = (object)x;
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(object)x").WithLocation(8, 13));
}
private readonly static NullableAnnotation[] s_AllNullableAnnotations = (NullableAnnotation[])Enum.GetValues(typeof(NullableAnnotation));
[Fact]
public void TestJoinForFixingLowerBoundsIsAssociative()
{
foreach (var a in s_AllNullableAnnotations)
{
foreach (var b in s_AllNullableAnnotations)
{
foreach (var c in s_AllNullableAnnotations)
{
var leftFirst = a.JoinForFixingLowerBounds(b).JoinForFixingLowerBounds(c);
var rightFirst = a.JoinForFixingLowerBounds(b.JoinForFixingLowerBounds(c));
Assert.Equal(leftFirst, rightFirst);
}
}
}
}
[Fact]
public void TestJoinForFlowAnalysisBranchesIsAssociative()
{
Func<bool, bool> identity = x => x;
foreach (var a in s_AllNullableAnnotations)
{
foreach (var b in s_AllNullableAnnotations)
{
foreach (var c in s_AllNullableAnnotations)
{
foreach (bool isPossiblyNullableReferenceTypeTypeParameter in new[] { true, false })
{
var leftFirst = a.JoinForFlowAnalysisBranches(b, isPossiblyNullableReferenceTypeTypeParameter, identity).JoinForFlowAnalysisBranches(c, isPossiblyNullableReferenceTypeTypeParameter, identity);
var rightFirst = a.JoinForFlowAnalysisBranches(b.JoinForFlowAnalysisBranches(c, isPossiblyNullableReferenceTypeTypeParameter, identity), isPossiblyNullableReferenceTypeTypeParameter, identity);
Assert.Equal(leftFirst, rightFirst);
}
}
}
}
}
[Fact]
public void TestMeetForFixingUpperBoundsIsAssociative()
{
foreach (var a in s_AllNullableAnnotations)
{
foreach (var b in s_AllNullableAnnotations)
{
foreach (var c in s_AllNullableAnnotations)
{
var leftFirst = a.MeetForFixingUpperBounds(b).MeetForFixingUpperBounds(c);
var rightFirst = a.MeetForFixingUpperBounds(b.MeetForFixingUpperBounds(c));
Assert.Equal(leftFirst, rightFirst);
}
}
}
}
[Fact]
public void TestMeetForFlowAnalysisFinallyIsAssociative()
{
foreach (var a in s_AllNullableAnnotations)
{
foreach (var b in s_AllNullableAnnotations)
{
foreach (var c in s_AllNullableAnnotations)
{
var leftFirst = a.MeetForFlowAnalysisFinally(b).MeetForFlowAnalysisFinally(c);
var rightFirst = a.MeetForFlowAnalysisFinally(b.MeetForFlowAnalysisFinally(c));
Assert.Equal(leftFirst, rightFirst);
}
}
}
}
[Fact]
public void TestEnsureCompatibleIsAssociative()
{
Func<bool, bool> identity = x => x;
foreach (var a in s_AllNullableAnnotations)
{
foreach (var b in s_AllNullableAnnotations)
{
foreach (var c in s_AllNullableAnnotations)
{
foreach (bool isPossiblyNullableReferenceTypeTypeParameter in new[] { true, false })
{
var leftFirst = a.EnsureCompatible(b, isPossiblyNullableReferenceTypeTypeParameter, identity, out var w1a).EnsureCompatible(c, isPossiblyNullableReferenceTypeTypeParameter, identity, out var w1b);
var rightFirst = a.EnsureCompatible(b.EnsureCompatible(c, isPossiblyNullableReferenceTypeTypeParameter, identity, out var w2a), isPossiblyNullableReferenceTypeTypeParameter, identity, out var w2b);
Assert.Equal(leftFirst, rightFirst);
Assert.Equal(w1a | w1b, w2a | w2b);
}
}
}
}
}
[Fact]
public void TestJoinForFixingLowerBoundsIsCommutative()
{
foreach (var a in s_AllNullableAnnotations)
{
foreach (var b in s_AllNullableAnnotations)
{
var leftFirst = a.JoinForFixingLowerBounds(b);
var rightFirst = b.JoinForFixingLowerBounds(a);
Assert.Equal(leftFirst, rightFirst);
}
}
}
[Fact]
public void TestJoinForFlowAnalysisBranchesIsCommutative()
{
Func<bool, bool> identity = x => x;
foreach (var a in s_AllNullableAnnotations)
{
foreach (var b in s_AllNullableAnnotations)
{
foreach (bool isPossiblyNullableReferenceTypeTypeParameter in new[] { true, false })
{
var leftFirst = a.JoinForFlowAnalysisBranches(b, isPossiblyNullableReferenceTypeTypeParameter, identity);
var rightFirst = b.JoinForFlowAnalysisBranches(a, isPossiblyNullableReferenceTypeTypeParameter, identity);
Assert.Equal(leftFirst, rightFirst);
}
}
}
}
[Fact]
public void TestMeetForFixingUpperBoundsIsCommutative()
{
foreach (var a in s_AllNullableAnnotations)
{
foreach (var b in s_AllNullableAnnotations)
{
var leftFirst = a.MeetForFixingUpperBounds(b);
var rightFirst = b.MeetForFixingUpperBounds(a);
Assert.Equal(leftFirst, rightFirst);
}
}
}
[Fact]
public void TestMeetForFlowAnalysisFinallyIsCommutative()
{
foreach (var a in s_AllNullableAnnotations)
{
foreach (var b in s_AllNullableAnnotations)
{
foreach (var c in s_AllNullableAnnotations)
{
var leftFirst = a.MeetForFlowAnalysisFinally(b);
var rightFirst = b.MeetForFlowAnalysisFinally(a);
Assert.Equal(leftFirst, rightFirst);
}
}
}
}
[Fact]
public void TestEnsureCompatibleIsCommutative()
{
Func<bool, bool> identity = x => x;
foreach (var a in s_AllNullableAnnotations)
{
foreach (var b in s_AllNullableAnnotations)
{
foreach (bool isPossiblyNullableReferenceTypeTypeParameter in new[] { true, false })
{
var leftFirst = a.EnsureCompatible(b, isPossiblyNullableReferenceTypeTypeParameter, identity, out var w1);
var rightFirst = b.EnsureCompatible(a, isPossiblyNullableReferenceTypeTypeParameter, identity, out var w2);
Assert.Equal(leftFirst, rightFirst);
Assert.Equal(w1, w2);
}
}
}
}
[Fact(Skip ="Two different implementations of NullableAnnotation Join do not agree")]
public void TestJoinsAgree()
{
Func<bool, bool> identity = x => x;
foreach (var a in s_AllNullableAnnotations)
{
foreach (var b in s_AllNullableAnnotations)
{
foreach (bool isPossiblyNullableReferenceTypeTypeParameter in new[] { true, false })
{
var result1 = a.JoinForFixingLowerBounds(b);
var result2 = a.JoinForFlowAnalysisBranches(b, isPossiblyNullableReferenceTypeTypeParameter, identity);
Assert.Equal(result1, result2);
}
}
}
}
[Fact(Skip = "Two different implementations of NullableAnnotation Meet do not agree")]
public void TestMeetsAgree()
{
foreach (var a in s_AllNullableAnnotations)
{
foreach (var b in s_AllNullableAnnotations)
{
var result1 = a.MeetForFixingUpperBounds(b);
var result2 = a.MeetForFlowAnalysisFinally(b);
Assert.Equal(result1, result2);
}
}
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册