提交 3deab5fe 编写于 作者: A AlekseyTs

Initial support for nullable flow analysis in lambdas.

At the moment we just follow the definite assignment logic. The majority of the work is to make sure proper flow of nullable annotations from target delegate types to lambdas and to make lambda binding caches aware of the annotations.
上级 7179442d
......@@ -2370,7 +2370,7 @@ internal BoundExpression GenerateConversionForAssignment(TypeSymbol targetType,
var delegateParameterType = delegateParameters[i].Type.TypeSymbol;
var delegateRefKind = delegateParameters[i].RefKind;
if (!lambdaParameterType.Equals(delegateParameterType, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: true))
if (!lambdaParameterType.Equals(delegateParameterType, TypeSymbolEqualityOptions.SameType))
{
SymbolDistinguisher distinguisher = new SymbolDistinguisher(this.Compilation, lambdaParameterType, delegateParameterType);
......
......@@ -433,7 +433,7 @@ private static LambdaConversionResult IsAnonymousFunctionCompatibleWithDelegate(
for (int p = 0; p < delegateParameters.Length; ++p)
{
if (delegateParameters[p].RefKind != anonymousFunction.RefKind(p) ||
!delegateParameters[p].Type.TypeSymbol.Equals(anonymousFunction.ParameterType(p).TypeSymbol, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: true))
!delegateParameters[p].Type.TypeSymbol.Equals(anonymousFunction.ParameterType(p).TypeSymbol, TypeSymbolEqualityOptions.SameType))
{
return LambdaConversionResult.MismatchedParameterType;
}
......
......@@ -527,7 +527,7 @@ public static bool HasIdentityConversion(TypeSymbol type1, TypeSymbol type2)
Debug.Assert((object)type1 != null);
Debug.Assert((object)type2 != null);
return type1.Equals(type2, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: true);
return type1.Equals(type2, TypeSymbolEqualityOptions.SameType);
}
public static bool HasIdentityConversionToAny<T>(T type, ArrayBuilder<T> targetTypes)
......
......@@ -621,10 +621,10 @@ private static void AddDistinctOperators(ArrayBuilder<BinaryOperatorAnalysisResu
// Return types must match exactly, parameters might match modulo identity conversion.
if (op.Signature.Kind == existingSignature.Kind && // Easy out
op.Signature.ReturnType.Equals(existingSignature.ReturnType, ignoreDynamic: false) &&
op.Signature.LeftType.Equals(existingSignature.LeftType, ignoreDynamic: true) &&
op.Signature.RightType.Equals(existingSignature.RightType, ignoreDynamic: true) &&
op.Signature.Method.ContainingType.Equals(existingSignature.Method.ContainingType, ignoreDynamic: true))
op.Signature.ReturnType.Equals(existingSignature.ReturnType) &&
op.Signature.LeftType.Equals(existingSignature.LeftType, TypeSymbolEqualityOptions.IgnoreDynamic) &&
op.Signature.RightType.Equals(existingSignature.RightType, TypeSymbolEqualityOptions.IgnoreDynamic) &&
op.Signature.Method.ContainingType.Equals(existingSignature.Method.ContainingType, TypeSymbolEqualityOptions.IgnoreDynamic))
{
equivalentToExisting = true;
break;
......
......@@ -246,7 +246,7 @@ internal abstract class UnboundLambdaState
{
private UnboundLambda _unboundLambda; // we would prefer this readonly, but we have an initialization cycle.
protected readonly Binder binder;
private readonly ConcurrentDictionary<object, BoundLambda> _bindingCache = new ConcurrentDictionary<object, BoundLambda>();
private readonly ConcurrentDictionary<TypeSymbol, BoundLambda> _bindingCache = new ConcurrentDictionary<TypeSymbol, BoundLambda>(TypeSymbol.EqualsIncludingNullableComparer);
private readonly ConcurrentDictionary<MethodSymbol, BoundLambda> _returnInferenceCache =
new ConcurrentDictionary<MethodSymbol, BoundLambda>(MemberSignatureComparer.LambdaReturnInferenceCacheComparer);
......@@ -331,10 +331,10 @@ private static ImmutableArray<ParameterSymbol> DelegateParameters(NamedTypeSymbo
return ((object)d == null || (object)d.DelegateInvokeMethod == null) ? ImmutableArray<ParameterSymbol>.Empty : d.DelegateInvokeMethod.Parameters;
}
private static TypeSymbol DelegateReturnType(NamedTypeSymbol delegateType)
private static TypeSymbolWithAnnotations DelegateReturnType(NamedTypeSymbol delegateType)
{
NamedTypeSymbol d = delegateType.GetDelegateType();
return ((object)d == null || (object)d.DelegateInvokeMethod == null) ? null : d.DelegateInvokeMethod.ReturnType.TypeSymbol;
return ((object)d == null || (object)d.DelegateInvokeMethod == null) ? null : d.DelegateInvokeMethod.ReturnType;
}
private bool DelegateNeedsReturn(NamedTypeSymbol delegateType)
......@@ -372,8 +372,8 @@ private BoundLambda ReallyBind(NamedTypeSymbol delegateType)
if (_returnInferenceCache.TryGetValue(cacheKey, out returnInferenceLambda) && returnInferenceLambda.InferredFromSingleType)
{
var lambdaSym = returnInferenceLambda.Symbol;
var lambdaRetType = lambdaSym.ReturnType.TypeSymbol;
if (lambdaRetType == returnType)
var lambdaRetType = lambdaSym.ReturnType;
if (lambdaRetType.Equals(returnType, TypeSymbolEqualityOptions.CompareNullableModifiersForReferenceTypes))
{
lambdaSymbol = lambdaSym;
lambdaBodyBinder = returnInferenceLambda.Binder;
......@@ -410,8 +410,8 @@ private BoundLambda ReallyBind(NamedTypeSymbol delegateType)
{
if ((object)returnType != null && // Can be null if "delegateType" is not actually a delegate type.
returnType.SpecialType != SpecialType.System_Void &&
returnType != binder.Compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_Task) &&
returnType.OriginalDefinition != binder.Compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_Task_T))
returnType.TypeSymbol != binder.Compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_Task) &&
returnType.TypeSymbol.OriginalDefinition != binder.Compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_Task_T))
{
// Cannot convert async {0} to delegate type '{1}'. An async {0} may return void, Task or Task&lt;T&gt;, none of which are convertible to '{1}'.
diagnostics.Add(ErrorCode.ERR_CantConvAsyncAnonFuncReturns, lambdaSymbol.Locations[0], lambdaSymbol.MessageID.Localize(), delegateType);
......
......@@ -727,6 +727,7 @@
<Compile Include="Symbols\TypeParameterSymbolExtensions.cs" />
<Compile Include="Symbols\TypeSymbol.cs" />
<Compile Include="Symbols\TypeSymbol.SymbolAndDiagnostics.cs" />
<Compile Include="Symbols\TypeSymbolEqualityOptions.cs" />
<Compile Include="Symbols\TypeSymbolExtensions.cs" />
<Compile Include="Symbols\TypeUnification.cs" />
<Compile Include="Symbols\UnboundGenericType.cs" />
......@@ -905,4 +906,4 @@
<ImportGroup Label="Targets">
<Import Project="..\..\..\..\build\Targets\VSL.Imports.targets" />
</ImportGroup>
</Project>
</Project>
\ No newline at end of file
......@@ -896,7 +896,7 @@ public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node)
// - i.e assigns int value to a short local.
// in that case we should force lhs to be a real local.
Debug.Assert(
node.Left.Type.Equals(node.Right.Type, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: true),
node.Left.Type.Equals(node.Right.Type, TypeSymbolEqualityOptions.SameType),
@"type of the assignment value is not the same as the type of assignment target.
This is not expected by the optimizer and is typically a result of a bug somewhere else.");
......
......@@ -234,7 +234,7 @@ private Binder GetEnclosingBinder(CSharpSyntaxNode node, int position)
binder = new ExecutableCodeBinder(unexpectedAnonymousFunction,
new LambdaSymbol(binder.ContainingMemberOrLambda,
ImmutableArray<ParameterSymbol>.Empty,
ErrorTypeSymbol.UnknownResultType,
TypeSymbolWithAnnotations.Create(ErrorTypeSymbol.UnknownResultType),
unexpectedAnonymousFunction.Kind() == SyntaxKind.AnonymousMethodExpression ? MessageID.IDS_AnonMethod : MessageID.IDS_Lambda,
unexpectedAnonymousFunction,
isSynthesized: false,
......
......@@ -797,7 +797,7 @@ public bool Equals(TypeSymbol source, TypeSymbol other)
var visitedSource = (TypeSymbol)_matcher.Visit(source);
var visitedOther = (_deepTranslatorOpt != null) ? (TypeSymbol)_deepTranslatorOpt.Visit(other) : other;
return visitedSource?.Equals(visitedOther, ignoreDynamic: true) == true;
return visitedSource?.Equals(visitedOther, TypeSymbolEqualityOptions.IgnoreDynamic) == true;
}
}
}
......
......@@ -788,7 +788,7 @@ protected int MakeSlot(BoundExpression node)
}
else if (IsTrackableAnonymousTypeProperty(propSymbol))
{
Debug.Assert(!propSymbol.Type.IsReferenceType || propSymbol.Type.IsNullable);
Debug.Assert(!propSymbol.Type.IsReferenceType || propSymbol.Type.IsNullable == true);
var receiverOpt = propAccess.ReceiverOpt;
if (receiverOpt == null || receiverOpt.Kind == BoundKind.TypeExpression) return -1;
int containingSlot = MakeSlot(receiverOpt);
......@@ -1271,7 +1271,7 @@ private void TrackNullableStateForAssignment(BoundNode node, Symbol assignmentTa
bool isByRefTarget = IsByRefTarget(slot);
// TODO: For now always respect annotations for array elements. Specially handle array types as assignment targets.
if ((assignmentTarget.Kind == SymbolKind.ArrayType || RespectNullableAnnotations(assignmentTarget)) && !targetType.IsNullable)
if ((assignmentTarget.Kind == SymbolKind.ArrayType || RespectNullableAnnotations(assignmentTarget)) && targetType.IsNullable == false)
{
if (valueIsNotNull == false)
{
......@@ -1286,7 +1286,7 @@ private void TrackNullableStateForAssignment(BoundNode node, Symbol assignmentTa
{
// Since reference can point to the heap, we cannot assume the value is not null after this assignment,
// regardless of what value is being assigned.
this.State.KnownNullState[slot] = true;
this.State.KnownNullState[slot] = (targetType.IsNullable == true);
this.State.NotNull[slot] = false;
}
else if (valueIsNotNull.HasValue)
......@@ -1369,7 +1369,7 @@ private void InheritNullableStateOfFieldOrProperty(int targetContainerSlot, int
// If statically declared as not-nullable, no need to adjust the tracking info.
// Declaration information takes priority.
if (!(respectNullableAnnotations && !fieldOrPropertyType.IsNullable))
if (!(respectNullableAnnotations && fieldOrPropertyType.IsNullable == false))
{
int targetMemberSlot = GetOrCreateSlot(fieldOrProperty, targetContainerSlot);
if (targetMemberSlot >= this.State.KnownNullState.Capacity) NormalizeNullable(ref this.State);
......@@ -1379,7 +1379,7 @@ private void InheritNullableStateOfFieldOrProperty(int targetContainerSlot, int
// This is a property/field acesses through a by ref entity and it isn't considered declared as not-nullable.
// Since reference can point to the heap, we cannot assume the property/field doesn't have null value after this assignment,
// regardless of what value is being assigned.
this.State.KnownNullState[targetMemberSlot] = true;
this.State.KnownNullState[targetMemberSlot] = (fieldOrPropertyType.IsNullable == true);
this.State.NotNull[targetMemberSlot] = false;
}
else if (valueContainerSlot > 0)
......@@ -1394,8 +1394,8 @@ private void InheritNullableStateOfFieldOrProperty(int targetContainerSlot, int
// with information inferred from the declaration.
if (respectNullableAnnotations)
{
Debug.Assert(fieldOrPropertyType.IsNullable);
this.State.KnownNullState[targetMemberSlot] = true;
Debug.Assert(fieldOrPropertyType.IsNullable != false);
this.State.KnownNullState[targetMemberSlot] = (fieldOrPropertyType.IsNullable == true);
this.State.NotNull[targetMemberSlot] = false;
}
else
......@@ -1626,11 +1626,11 @@ protected virtual void EnterParameter(ParameterSymbol parameter)
if (paramType.IsReferenceType)
{
if (paramType.IsNullable)
if (paramType.IsNullable != false)
{
if (slot >= this.State.KnownNullState.Capacity) NormalizeNullable(ref this.State);
this.State.KnownNullState[slot] = true;
this.State.KnownNullState[slot] = (paramType.IsNullable == true);
this.State.NotNull[slot] = false;
}
......@@ -1702,7 +1702,7 @@ public override BoundNode VisitReturnStatement(BoundReturnStatement node)
{
TypeSymbolWithAnnotations returnType = this.currentMethodOrLambda?.ReturnType;
if ((object)returnType != null && returnType.IsReferenceType && !returnType.IsNullable)
if ((object)returnType != null && returnType.IsReferenceType && returnType.IsNullable == false)
{
ReportStaticNullCheckingDiagnostics(ErrorCode.WRN_NullReferenceReturn, node.ExpressionOpt.Syntax);
}
......@@ -2334,7 +2334,7 @@ public override BoundNode VisitCall(BoundCall node)
{
bool respectNullableAnnotations = RespectNullableAnnotations(resultSymbol);
if (respectNullableAnnotations && !resultType.IsNullable)
if (respectNullableAnnotations && resultType.IsNullable == false)
{
// Statically declared as not-nullable. This takes priority.
return true;
......@@ -2359,8 +2359,12 @@ public override BoundNode VisitCall(BoundCall node)
// The node is not trackable, use information from the declaration.
if (respectNullableAnnotations)
{
Debug.Assert(resultType.IsNullable);
return false;
Debug.Assert(resultType.IsNullable != false);
if (resultType.IsNullable.HasValue)
{
return false;
}
}
// The declaration doesn't contain any annotation we can/should rely on.
......@@ -2402,7 +2406,32 @@ public override BoundNode VisitConversion(BoundConversion node)
{
CheckAssigned(node.SymbolOpt.OriginalDefinition, node.Syntax);
}
return base.VisitConversion(node);
var result = base.VisitConversion(node);
if (_performStaticNullChecks && this.State.Reachable)
{
if (node.Type.IsReferenceType)
{
switch (node.ConversionKind)
{
case ConversionKind.MethodGroup:
case ConversionKind.AnonymousFunction:
this.State.ResultIsNotNull = true;
break;
default:
// Inherit state from the operand
break;
}
}
else
{
this.State.ResultIsNotNull = null;
}
}
return result;
}
public override BoundNode VisitDelegateCreationExpression(BoundDelegateCreationExpression node)
......@@ -2573,7 +2602,7 @@ protected override void VisitArgumentAsRvalue(BoundExpression argument, Paramete
{
TypeSymbolWithAnnotations paramType = expanded ? ((ArrayTypeSymbol)parameter.Type.TypeSymbol).ElementType : parameter.Type;
if (paramType.IsReferenceType && !paramType.IsNullable)
if (paramType.IsReferenceType && paramType.IsNullable == false)
{
ReportStaticNullCheckingDiagnostics(ErrorCode.WRN_NullReferenceArgument, argument.Syntax);
}
......
......@@ -1324,6 +1324,7 @@ public override BoundNode VisitConversion(BoundConversion node)
{
BoundExpression receiver = ((BoundMethodGroup)node.Operand).ReceiverOpt;
// A method group's "implicit this" is only used for instance methods.
// TODO: StaticNullChecking - should we use VisitReceiverBeforeCall if we are dealing with an instance method?
if (_trackRegions)
{
if (node.Operand == this.firstInRegion && this.regionPlace == RegionPlace.Before) EnterRegion();
......
......@@ -192,7 +192,7 @@ private BoundExpression VisitExpressionImpl(BoundExpression node)
// like compound assignment does (extra flag only passed when it is an expression
// statement means that this constraint is not violated).
// Dynamic type will be erased in emit phase. It is considered equivalent to Object in lowered bound trees.
Debug.Assert(visited == null || visited.HasErrors || ReferenceEquals(visited.Type, node.Type) || visited.Type.Equals(node.Type, ignoreDynamic: true));
Debug.Assert(visited == null || visited.HasErrors || ReferenceEquals(visited.Type, node.Type) || visited.Type.Equals(node.Type, TypeSymbolEqualityOptions.IgnoreDynamic));
return visited;
}
......
......@@ -131,7 +131,7 @@ private static bool IsFloatPointExpressionOfUnknownPrecision(BoundExpression rew
// but we need to change the Type property on the resulting BoundExpression to match the rewrittenType.
// This is necessary so that subsequent lowering transformations see that the expression is dynamic.
if (_inExpressionLambda || !rewrittenOperand.Type.Equals(rewrittenType, ignoreCustomModifiersAndArraySizesAndLowerBounds: false, ignoreDynamic: false))
if (_inExpressionLambda || !rewrittenOperand.Type.Equals(rewrittenType, TypeSymbolEqualityOptions.None))
{
break;
}
......
......@@ -30,7 +30,7 @@ public override BoundNode VisitNullCoalescingOperator(BoundNullCoalescingOperato
Debug.Assert(rewrittenRight != null);
Debug.Assert(leftConversion.IsValid);
Debug.Assert((object)rewrittenResultType != null);
Debug.Assert(rewrittenRight.Type.Equals(rewrittenResultType, ignoreDynamic: true));
Debug.Assert(rewrittenRight.Type.Equals(rewrittenResultType, TypeSymbolEqualityOptions.IgnoreDynamic));
if (_inExpressionLambda)
{
......@@ -116,7 +116,7 @@ public override BoundNode VisitNullCoalescingOperator(BoundNullCoalescingOperato
// MakeConversion(temp, rewrittenResultType)
BoundExpression convertedLeft = GetConvertedLeftForNullCoalescingOperator(boundTemp, leftConversion, rewrittenResultType);
Debug.Assert(convertedLeft.Type.Equals(rewrittenResultType, ignoreDynamic: true));
Debug.Assert(convertedLeft.Type.Equals(rewrittenResultType, TypeSymbolEqualityOptions.IgnoreDynamic));
// (temp != null) ? MakeConversion(temp, LeftConversion) : RightOperand
BoundExpression conditionalExpression = RewriteConditionalOperator(
......@@ -128,7 +128,7 @@ public override BoundNode VisitNullCoalescingOperator(BoundNullCoalescingOperato
rewrittenType: rewrittenResultType);
Debug.Assert(conditionalExpression.ConstantValue == null); // we shouldn't have hit this else case otherwise
Debug.Assert(conditionalExpression.Type.Equals(rewrittenResultType, ignoreDynamic: true));
Debug.Assert(conditionalExpression.Type.Equals(rewrittenResultType, TypeSymbolEqualityOptions.IgnoreDynamic));
return new BoundSequence(
syntax: syntax,
......
......@@ -387,7 +387,7 @@ public BoundExpressionStatement ExpressionStatement(BoundExpression expr)
public BoundAssignmentOperator AssignmentExpression(BoundExpression left, BoundExpression right, RefKind refKind = RefKind.None)
{
Debug.Assert(left.Type.Equals(right.Type, ignoreDynamic: true) || right.Type.IsErrorType() || left.Type.IsErrorType());
Debug.Assert(left.Type.Equals(right.Type, TypeSymbolEqualityOptions.IgnoreDynamic) || right.Type.IsErrorType() || left.Type.IsErrorType());
return new BoundAssignmentOperator(Syntax, left, right, left.Type, refKind: refKind) { WasCompilerGenerated = true };
}
......@@ -607,7 +607,7 @@ public BoundExpression ComplexConditionalReceiver(BoundExpression valueTypeRecei
public BoundExpression Coalesce(BoundExpression left, BoundExpression right)
{
Debug.Assert(left.Type.Equals(right.Type, ignoreCustomModifiersAndArraySizesAndLowerBounds: true));
Debug.Assert(left.Type.Equals(right.Type, TypeSymbolEqualityOptions.IgnoreCustomModifiersAndArraySizesAndLowerBounds));
Debug.Assert(left.Type.IsReferenceType);
return new BoundNullCoalescingOperator(Syntax, left, right, Conversion.Identity, left.Type) { WasCompilerGenerated = true };
......
......@@ -59,13 +59,13 @@ internal void AssertIsGood()
public bool Equals(AnonymousTypeDescriptor desc)
{
return this.Equals(desc, ignoreCustomModifiersAndArraySizesAndLowerBounds: false, ignoreDynamic: false);
return this.Equals(desc, TypeSymbolEqualityOptions.None);
}
/// <summary>
/// Compares two anonymous type descriptors, takes into account fields names and types, not locations.
/// </summary>
internal bool Equals(AnonymousTypeDescriptor other, bool ignoreCustomModifiersAndArraySizesAndLowerBounds, bool ignoreDynamic)
internal bool Equals(AnonymousTypeDescriptor other, TypeSymbolEqualityOptions options)
{
// Comparing keys ensures field count and field names are the same
if (this.Key != other.Key)
......@@ -79,7 +79,7 @@ internal bool Equals(AnonymousTypeDescriptor other, bool ignoreCustomModifiersAn
ImmutableArray<AnonymousTypeField> otherFields = other.Fields;
for (int i = 0; i < count; i++)
{
if (!myFields[i].Type.TypeSymbol.Equals(otherFields[i].Type.TypeSymbol, ignoreCustomModifiersAndArraySizesAndLowerBounds, ignoreDynamic))
if (!myFields[i].Type.TypeSymbol.Equals(otherFields[i].Type.TypeSymbol, options))
{
return false;
}
......@@ -93,7 +93,7 @@ internal bool Equals(AnonymousTypeDescriptor other, bool ignoreCustomModifiersAn
/// </summary>
public override bool Equals(object obj)
{
return obj is AnonymousTypeDescriptor && this.Equals((AnonymousTypeDescriptor)obj, ignoreCustomModifiersAndArraySizesAndLowerBounds: false, ignoreDynamic: false);
return obj is AnonymousTypeDescriptor && this.Equals((AnonymousTypeDescriptor)obj, TypeSymbolEqualityOptions.None);
}
public override int GetHashCode()
......
......@@ -72,41 +72,5 @@ public static NamedTypeSymbol ConstructAnonymousTypeSymbol(NamedTypeSymbol type,
var anonymous = (AnonymousTypePublicSymbol)type;
return anonymous.Manager.ConstructAnonymousTypeSymbol(anonymous.TypeDescriptor.WithNewFieldsTypes(newFieldTypes));
}
/// <summary>
/// Logical equality on anonymous types that ignores custom modifiers and/or the object/dynamic distinction.
/// Differs from IsSameType for arrays, pointers, and generic instantiations.
/// </summary>
internal static bool IsSameType(TypeSymbol type1, TypeSymbol type2, bool ignoreCustomModifiersAndArraySizesAndLowerBounds, bool ignoreDynamic)
{
Debug.Assert(type1.IsAnonymousType);
Debug.Assert(type2.IsAnonymousType);
if (ignoreCustomModifiersAndArraySizesAndLowerBounds || ignoreDynamic)
{
AnonymousTypeDescriptor left = ((AnonymousTypePublicSymbol)type1).TypeDescriptor;
AnonymousTypeDescriptor right = ((AnonymousTypePublicSymbol)type2).TypeDescriptor;
if (left.Key != right.Key)
{
return false;
}
int count = left.Fields.Length;
Debug.Assert(right.Fields.Length == count);
for (int i = 0; i < count; i++)
{
if (!left.Fields[i].Type.TypeSymbol.Equals(right.Fields[i].Type.TypeSymbol, ignoreCustomModifiersAndArraySizesAndLowerBounds, ignoreDynamic))
{
return false;
}
}
return true;
}
else
{
return type1 == type2;
}
}
}
}
......@@ -323,7 +323,7 @@ internal override ImmutableArray<NamedTypeSymbol> GetDeclaredInterfaces(ConsList
return ImmutableArray<NamedTypeSymbol>.Empty;
}
internal override bool Equals(TypeSymbol t2, bool ignoreCustomModifiersAndArraySizesAndLowerBounds, bool ignoreDynamic)
internal override bool Equals(TypeSymbol t2, TypeSymbolEqualityOptions options)
{
if (ReferenceEquals(this, t2))
{
......@@ -331,7 +331,7 @@ internal override bool Equals(TypeSymbol t2, bool ignoreCustomModifiersAndArrayS
}
var other = t2 as AnonymousTypePublicSymbol;
return (object)other != null && this.TypeDescriptor.Equals(other.TypeDescriptor, ignoreCustomModifiersAndArraySizesAndLowerBounds, ignoreDynamic);
return (object)other != null && this.TypeDescriptor.Equals(other.TypeDescriptor, options);
}
public override int GetHashCode()
......
......@@ -308,17 +308,17 @@ public override TResult Accept<TResult>(CSharpSymbolVisitor<TResult> visitor)
return visitor.VisitArrayType(this);
}
internal override bool Equals(TypeSymbol t2, bool ignoreCustomModifiersAndArraySizesAndLowerBounds, bool ignoreDynamic)
internal override bool Equals(TypeSymbol t2, TypeSymbolEqualityOptions options)
{
return this.Equals(t2 as ArrayTypeSymbol, ignoreCustomModifiersAndArraySizesAndLowerBounds, ignoreDynamic);
return this.Equals(t2 as ArrayTypeSymbol, options);
}
internal bool Equals(ArrayTypeSymbol other)
{
return Equals(other, false, false);
return Equals(other, TypeSymbolEqualityOptions.None);
}
private bool Equals(ArrayTypeSymbol other, bool ignoreCustomModifiersAndArraySizesAndLowerBounds, bool ignoreDynamic)
private bool Equals(ArrayTypeSymbol other, TypeSymbolEqualityOptions options)
{
if (ReferenceEquals(this, other))
{
......@@ -326,35 +326,15 @@ private bool Equals(ArrayTypeSymbol other, bool ignoreCustomModifiersAndArraySiz
}
if ((object)other == null || !other.HasSameShapeAs(this) ||
!other.ElementType.TypeSymbol.Equals(ElementType.TypeSymbol, ignoreCustomModifiersAndArraySizesAndLowerBounds, ignoreDynamic))
!other.ElementType.Equals(ElementType, options))
{
return false;
}
// Make sure custom modifiers and bounds are the same.
if (!ignoreCustomModifiersAndArraySizesAndLowerBounds)
// Make sure bounds are the same.
if ((options & TypeSymbolEqualityOptions.IgnoreArraySizesAndLowerBounds) == 0 && !this.HasSameSizesAndLowerBoundsAs(other))
{
var mod = this.ElementType.CustomModifiers;
var otherMod = other.ElementType.CustomModifiers;
var count = mod.Length;
if (count != otherMod.Length)
{
return false;
}
for (int i = 0; i < count; i++)
{
if (!mod[i].Equals(otherMod[i]))
{
return false;
}
}
if (!this.HasSameSizesAndLowerBoundsAs(other))
{
return false;
}
return false;
}
return true;
......
......@@ -44,7 +44,7 @@ internal override TypeSymbolWithAnnotations Substitute(AbstractTypeMap typeMap)
substitutedReferencedType.CustomModifiers);
}
internal override bool Equals(TypeSymbol t2, bool ignoreCustomModifiersAndArraySizesAndLowerBounds, bool ignoreDynamic)
internal override bool Equals(TypeSymbol t2, TypeSymbolEqualityOptions options)
{
if ((object)this == (object)t2)
{
......@@ -52,13 +52,13 @@ internal override bool Equals(TypeSymbol t2, bool ignoreCustomModifiersAndArrayS
}
ByRefReturnErrorTypeSymbol other = t2 as ByRefReturnErrorTypeSymbol;
return (object)other != null && _referencedType.Equals(other._referencedType, ignoreCustomModifiersAndArraySizesAndLowerBounds, ignoreDynamic) &&
(ignoreCustomModifiersAndArraySizesAndLowerBounds || _countOfCustomModifiersPrecedingByRef == other._countOfCustomModifiersPrecedingByRef);
return (object)other != null && _referencedType.Equals(other._referencedType, options) &&
((options & TypeSymbolEqualityOptions.IgnoreCustomModifiers) != 0 || _countOfCustomModifiersPrecedingByRef == other._countOfCustomModifiersPrecedingByRef);
}
public override int GetHashCode()
{
return Hash.Combine(Hash.Combine(_referencedType.GetHashCode(), _countOfCustomModifiersPrecedingByRef), 13); // Reduce collisions with referencedType.
return Hash.Combine(_referencedType.GetHashCode(), 13); // Reduce collisions with referencedType.
}
#endregion Defining characteristics of this type
......
......@@ -152,7 +152,7 @@ internal bool IsEqualOrDerivedFromWellKnownClass(TypeSymbol type, WellKnownType
}
var wkType = GetWellKnownType(wellKnownType);
return type.Equals(wkType, ignoreDynamic: false) || type.IsDerivedFrom(wkType, ignoreDynamic: false, useSiteDiagnostics: ref useSiteDiagnostics);
return type.Equals(wkType) || type.IsDerivedFrom(wkType, ignoreDynamic: false, useSiteDiagnostics: ref useSiteDiagnostics);
}
internal override bool IsSystemTypeReference(ITypeSymbol type)
......
......@@ -47,8 +47,8 @@ public bool Equals(SourceUserDefinedConversionSymbol member1, SourceUserDefinedC
return false;
}
return member1.ReturnType.TypeSymbol.Equals(member2.ReturnType.TypeSymbol, ignoreDynamic: true)
&& member1.ParameterTypes[0].Equals(member2.ParameterTypes[0], ignoreDynamic: true);
return member1.ReturnType.TypeSymbol.Equals(member2.ReturnType.TypeSymbol, TypeSymbolEqualityOptions.IgnoreDynamic)
&& member1.ParameterTypes[0].Equals(member2.ParameterTypes[0], TypeSymbolEqualityOptions.IgnoreDynamic);
}
public int GetHashCode(SourceUserDefinedConversionSymbol member)
......
......@@ -183,7 +183,7 @@ public override int GetHashCode()
return (int)Microsoft.CodeAnalysis.SpecialType.System_Object;
}
internal override bool Equals(TypeSymbol t2, bool ignoreCustomModifiersAndArraySizesAndLowerBounds, bool ignoreDynamic)
internal override bool Equals(TypeSymbol t2, TypeSymbolEqualityOptions options)
{
if ((object)t2 == null)
{
......@@ -195,7 +195,7 @@ internal override bool Equals(TypeSymbol t2, bool ignoreCustomModifiersAndArrayS
return true;
}
if (ignoreDynamic)
if ((options & TypeSymbolEqualityOptions.IgnoreDynamic) != 0)
{
var other = t2 as NamedTypeSymbol;
return (object)other != null && other.SpecialType == Microsoft.CodeAnalysis.SpecialType.System_Object;
......
......@@ -140,7 +140,7 @@ public override int GetHashCode()
return Hash.Combine(_container.GetHashCode(), _ordinal);
}
internal override bool Equals(TypeSymbol t2, bool ignoreCustomModifiersAndArraySizesAndLowerBounds, bool ignoreDynamic)
internal override bool Equals(TypeSymbol t2, TypeSymbolEqualityOptions options)
{
if (ReferenceEquals(this, t2))
{
......@@ -150,7 +150,7 @@ internal override bool Equals(TypeSymbol t2, bool ignoreCustomModifiersAndArrayS
var other = t2 as ErrorTypeParameterSymbol;
return (object)other != null &&
other._ordinal == _ordinal &&
other.ContainingType.Equals(this.ContainingType, ignoreCustomModifiersAndArraySizesAndLowerBounds, ignoreDynamic);
other.ContainingType.Equals(this.ContainingType, options);
}
}
}
......
......@@ -284,7 +284,7 @@ internal static TypeKind ExtractNonErrorTypeKind(TypeSymbol oldSymbol)
return commonTypeKind;
}
internal override bool Equals(TypeSymbol t2, bool ignoreCustomModifiersAndArraySizesAndLowerBounds, bool ignoreDynamic)
internal override bool Equals(TypeSymbol t2, TypeSymbolEqualityOptions options)
{
if (ReferenceEquals(this, t2))
{
......@@ -298,7 +298,7 @@ internal override bool Equals(TypeSymbol t2, bool ignoreCustomModifiersAndArrayS
}
return
((object)this.ContainingType != null ? this.ContainingType.Equals(other.ContainingType, ignoreCustomModifiersAndArraySizesAndLowerBounds, ignoreDynamic) :
((object)this.ContainingType != null ? this.ContainingType.Equals(other.ContainingType, options) :
(object)this.ContainingSymbol == null ? (object)other.ContainingSymbol == null : this.ContainingSymbol.Equals(other.ContainingSymbol)) &&
this.Name == other.Name && this.Arity == other.Arity;
}
......
......@@ -43,7 +43,7 @@ internal class MemberSignatureComparer : IEqualityComparer<Symbol>
considerTypeConstraints: false,
considerRefOutDifference: true,
considerCallingConvention: true,
considerCustomModifiers: false);
typeSymbolEqualityOptions: TypeSymbolEqualityOptions.IgnoreCustomModifiersAndArraySizesAndLowerBounds | TypeSymbolEqualityOptions.IgnoreDynamic);
/// <summary>
/// This instance is used when trying to determine if one member implicitly implements another,
......@@ -65,7 +65,7 @@ internal class MemberSignatureComparer : IEqualityComparer<Symbol>
considerTypeConstraints: false, // constraints are checked by caller instead
considerCallingConvention: true,
considerRefOutDifference: true,
considerCustomModifiers: false);
typeSymbolEqualityOptions: TypeSymbolEqualityOptions.IgnoreCustomModifiersAndArraySizesAndLowerBounds | TypeSymbolEqualityOptions.IgnoreDynamic);
/// <summary>
/// This instance is used as a fallback when it is determined that one member does not implicitly implement
......@@ -79,7 +79,7 @@ internal class MemberSignatureComparer : IEqualityComparer<Symbol>
considerTypeConstraints: false,
considerCallingConvention: false,
considerRefOutDifference: true,
considerCustomModifiers: false); //shouldn't actually matter for source members
typeSymbolEqualityOptions: TypeSymbolEqualityOptions.IgnoreCustomModifiersAndArraySizesAndLowerBounds | TypeSymbolEqualityOptions.IgnoreDynamic); //shouldn't actually matter for source members
/// <summary>
/// This instance is used to determine if two C# member declarations in source conflict with each other.
......@@ -96,7 +96,7 @@ internal class MemberSignatureComparer : IEqualityComparer<Symbol>
considerTypeConstraints: false,
considerCallingConvention: false,
considerRefOutDifference: false,
considerCustomModifiers: false); //shouldn't actually matter for source members
typeSymbolEqualityOptions: TypeSymbolEqualityOptions.IgnoreCustomModifiersAndArraySizesAndLowerBounds | TypeSymbolEqualityOptions.IgnoreDynamic); //shouldn't actually matter for source members
/// <summary>
/// This instance is used to check whether one member overrides another, according to the C# definition.
......@@ -108,7 +108,7 @@ internal class MemberSignatureComparer : IEqualityComparer<Symbol>
considerTypeConstraints: false,
considerCallingConvention: false, //ignore static-ness
considerRefOutDifference: true,
considerCustomModifiers: false);
typeSymbolEqualityOptions: TypeSymbolEqualityOptions.IgnoreCustomModifiersAndArraySizesAndLowerBounds | TypeSymbolEqualityOptions.IgnoreDynamic);
/// <summary>
/// This instance is used to check whether one property or event overrides another, according to the C# definition.
......@@ -122,7 +122,7 @@ internal class MemberSignatureComparer : IEqualityComparer<Symbol>
considerTypeConstraints: false,
considerCallingConvention: false, //ignore static-ness
considerRefOutDifference: true,
considerCustomModifiers: false);
typeSymbolEqualityOptions: TypeSymbolEqualityOptions.IgnoreCustomModifiersAndArraySizesAndLowerBounds | TypeSymbolEqualityOptions.IgnoreDynamic);
/// <summary>
/// Same as <see cref="CSharpOverrideComparer"/> except that it pays attention to custom modifiers and return type.
......@@ -136,7 +136,7 @@ internal class MemberSignatureComparer : IEqualityComparer<Symbol>
considerTypeConstraints: false,
considerCallingConvention: false, //ignore static-ness
considerRefOutDifference: true,
considerCustomModifiers: true);
typeSymbolEqualityOptions: TypeSymbolEqualityOptions.IgnoreDynamic);
/// <summary>
/// If this returns false, then the real override comparer (whichever one is appropriate for the scenario)
......@@ -149,7 +149,7 @@ internal class MemberSignatureComparer : IEqualityComparer<Symbol>
considerTypeConstraints: false,
considerCallingConvention: false, //ignore static-ness
considerRefOutDifference: false,
considerCustomModifiers: false);
typeSymbolEqualityOptions: TypeSymbolEqualityOptions.IgnoreCustomModifiersAndArraySizesAndLowerBounds | TypeSymbolEqualityOptions.IgnoreDynamic);
/// <summary>
/// This instance is intended to reflect the definition of signature equality used by the runtime
......@@ -164,7 +164,7 @@ internal class MemberSignatureComparer : IEqualityComparer<Symbol>
considerTypeConstraints: false,
considerCallingConvention: true,
considerRefOutDifference: false,
considerCustomModifiers: true);
typeSymbolEqualityOptions: TypeSymbolEqualityOptions.IgnoreDynamic);
/// <summary>
/// Same as <see cref="RuntimeSignatureComparer"/>, but distinguishes between <c>ref</c> and <c>out</c>. During override resolution,
......@@ -178,7 +178,7 @@ internal class MemberSignatureComparer : IEqualityComparer<Symbol>
considerTypeConstraints: false,
considerCallingConvention: true,
considerRefOutDifference: true,
considerCustomModifiers: true);
typeSymbolEqualityOptions: TypeSymbolEqualityOptions.IgnoreDynamic);
/// <summary>
/// This instance is the same as RuntimeSignatureComparer.
......@@ -191,7 +191,7 @@ internal class MemberSignatureComparer : IEqualityComparer<Symbol>
considerTypeConstraints: false, // constraints are checked by caller instead
considerCallingConvention: true,
considerRefOutDifference: false,
considerCustomModifiers: true);
typeSymbolEqualityOptions: TypeSymbolEqualityOptions.IgnoreDynamic);
// NOTE: Not used anywhere. Do we still need to keep it?
/// <summary>
......@@ -205,7 +205,7 @@ internal class MemberSignatureComparer : IEqualityComparer<Symbol>
considerTypeConstraints: true,
considerCallingConvention: true,
considerRefOutDifference: true,
considerCustomModifiers: false); //intended for source types
typeSymbolEqualityOptions: TypeSymbolEqualityOptions.IgnoreCustomModifiersAndArraySizesAndLowerBounds | TypeSymbolEqualityOptions.IgnoreDynamic); //intended for source types
/// <summary>
/// This instance is used to search for members that have identical signatures in every regard.
......@@ -217,7 +217,7 @@ internal class MemberSignatureComparer : IEqualityComparer<Symbol>
considerTypeConstraints: false,
considerCallingConvention: true,
considerRefOutDifference: true,
considerCustomModifiers: true); //if it was a true explicit impl, we expect it to remain so after retargeting
typeSymbolEqualityOptions: TypeSymbolEqualityOptions.IgnoreDynamic); //if it was a true explicit impl, we expect it to remain so after retargeting
/// <summary>
/// This instance is used for performing approximate overload resolution of documentation
......@@ -230,7 +230,7 @@ internal class MemberSignatureComparer : IEqualityComparer<Symbol>
considerTypeConstraints: false,
considerCallingConvention: false, //ignore static-ness
considerRefOutDifference: true,
considerCustomModifiers: false);
typeSymbolEqualityOptions: TypeSymbolEqualityOptions.IgnoreCustomModifiersAndArraySizesAndLowerBounds | TypeSymbolEqualityOptions.IgnoreDynamic);
/// <summary>
/// This instance is used as a key in the lambda return type inference.
......@@ -243,8 +243,7 @@ internal class MemberSignatureComparer : IEqualityComparer<Symbol>
considerTypeConstraints: false, // valid invoke is never generic
considerCallingConvention: false, // valid invoke is never static
considerRefOutDifference: true,
considerCustomModifiers: true,
ignoreDynamic: false);
typeSymbolEqualityOptions: TypeSymbolEqualityOptions.CompareNullableModifiersForReferenceTypes);
// Compare the "unqualified" part of the member name (no explicit part)
private readonly bool _considerName;
......@@ -264,11 +263,8 @@ internal class MemberSignatureComparer : IEqualityComparer<Symbol>
// True to consider RefKind.Ref and RefKind.Out different, false to consider them the same.
private readonly bool _considerRefOutDifference;
// Consider custom modifiers on/in parameters and return types (if return is considered).
private readonly bool _considerCustomModifiers;
// Ignore Object vs. Dynamic difference
private readonly bool _ignoreDynamic;
// Equality options for parameter types and return types (if return is considered).
private readonly TypeSymbolEqualityOptions _typeSymbolEqualityOptions;
private MemberSignatureComparer(
bool considerName,
......@@ -277,8 +273,7 @@ internal class MemberSignatureComparer : IEqualityComparer<Symbol>
bool considerTypeConstraints,
bool considerCallingConvention,
bool considerRefOutDifference,
bool considerCustomModifiers,
bool ignoreDynamic = true)
TypeSymbolEqualityOptions typeSymbolEqualityOptions = TypeSymbolEqualityOptions.IgnoreDynamic)
{
Debug.Assert(!considerExplicitlyImplementedInterfaces || considerName, "Doesn't make sense to consider interfaces separately from name.");
......@@ -288,8 +283,7 @@ internal class MemberSignatureComparer : IEqualityComparer<Symbol>
_considerTypeConstraints = considerTypeConstraints;
_considerCallingConvention = considerCallingConvention;
_considerRefOutDifference = considerRefOutDifference;
_considerCustomModifiers = considerCustomModifiers;
_ignoreDynamic = ignoreDynamic;
_typeSymbolEqualityOptions = typeSymbolEqualityOptions;
}
#region IEqualityComparer<Symbol> Members
......@@ -334,13 +328,13 @@ public bool Equals(Symbol member1, Symbol member2)
var typeMap1 = GetTypeMap(member1);
var typeMap2 = GetTypeMap(member2);
if (_considerReturnType && !HaveSameReturnTypes(member1, typeMap1, member2, typeMap2, _considerCustomModifiers, _ignoreDynamic))
if (_considerReturnType && !HaveSameReturnTypes(member1, typeMap1, member2, typeMap2, _typeSymbolEqualityOptions))
{
return false;
}
if (member1.GetParameterCount() > 0 && !HaveSameParameterTypes(member1.GetParameters(), typeMap1, member2.GetParameters(), typeMap2,
_considerRefOutDifference, _considerCustomModifiers, _ignoreDynamic))
_considerRefOutDifference, _typeSymbolEqualityOptions))
{
return false;
}
......@@ -416,7 +410,8 @@ public int GetHashCode(Symbol member)
// CONSIDER: could use interface type, but that might be quite expensive
}
if (_considerReturnType && member.GetMemberArity() == 0 && !_considerCustomModifiers) // If it is generic, then type argument might be in return type.
if (_considerReturnType && member.GetMemberArity() == 0 &&
(_typeSymbolEqualityOptions & TypeSymbolEqualityOptions.IgnoreCustomModifiers) != 0) // If it is generic, then type argument might be in return type.
{
hash = Hash.Combine(member.GetTypeOrReturnType(), hash);
}
......@@ -433,10 +428,14 @@ public int GetHashCode(Symbol member)
public static bool HaveSameReturnTypes(MethodSymbol member1, MethodSymbol member2, bool considerCustomModifiers)
{
return HaveSameReturnTypes(member1, GetTypeMap(member1), member2, GetTypeMap(member2), considerCustomModifiers, ignoreDynamic: true);
return HaveSameReturnTypes(member1, GetTypeMap(member1), member2, GetTypeMap(member2),
(considerCustomModifiers ?
TypeSymbolEqualityOptions.None :
TypeSymbolEqualityOptions.IgnoreCustomModifiersAndArraySizesAndLowerBounds) |
TypeSymbolEqualityOptions.IgnoreDynamic);
}
private static bool HaveSameReturnTypes(Symbol member1, TypeMap typeMap1, Symbol member2, TypeMap typeMap2, bool considerCustomModifiers, bool ignoreDynamic)
private static bool HaveSameReturnTypes(Symbol member1, TypeMap typeMap1, Symbol member2, TypeMap typeMap2, TypeSymbolEqualityOptions typeSymbolEqualityOptions)
{
TypeSymbolWithAnnotations unsubstitutedReturnType1 = member1.GetTypeOrReturnType();
TypeSymbolWithAnnotations unsubstitutedReturnType2 = member2.GetTypeOrReturnType();
......@@ -452,7 +451,7 @@ private static bool HaveSameReturnTypes(Symbol member1, TypeMap typeMap1, Symbol
if (isVoid1)
{
if (!considerCustomModifiers ||
if ((typeSymbolEqualityOptions & TypeSymbolEqualityOptions.IgnoreCustomModifiers) != 0 ||
(unsubstitutedReturnType1.CustomModifiers.IsEmpty && unsubstitutedReturnType2.CustomModifiers.IsEmpty))
{
return true;
......@@ -461,11 +460,7 @@ private static bool HaveSameReturnTypes(Symbol member1, TypeMap typeMap1, Symbol
var returnType1 = SubstituteType(typeMap1, unsubstitutedReturnType1);
var returnType2 = SubstituteType(typeMap2, unsubstitutedReturnType2);
// the runtime compares custom modifiers using (effectively) SequenceEqual
return considerCustomModifiers ?
returnType1.TypeSymbol.Equals(returnType2.TypeSymbol, ignoreDynamic: ignoreDynamic) && returnType1.CustomModifiers.SequenceEqual(returnType2.CustomModifiers) :
returnType1.TypeSymbol.Equals(returnType2.TypeSymbol, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: ignoreDynamic);
return returnType1.Equals(returnType2, typeSymbolEqualityOptions);
}
private static TypeMap GetTypeMap(Symbol member)
......@@ -590,7 +585,7 @@ private static void SubstituteConstraintTypes(ImmutableArray<TypeSymbolWithAnnot
}
private static bool HaveSameParameterTypes(ImmutableArray<ParameterSymbol> params1, TypeMap typeMap1, ImmutableArray<ParameterSymbol> params2, TypeMap typeMap2,
bool considerRefOutDifference, bool considerCustomModifiers, bool ignoreDynamic)
bool considerRefOutDifference, TypeSymbolEqualityOptions typeSymbolEqualityOptions)
{
Debug.Assert(params1.Length == params2.Length);
......@@ -604,17 +599,13 @@ private static void SubstituteConstraintTypes(ImmutableArray<TypeSymbolWithAnnot
var type1 = SubstituteType(typeMap1, param1.Type);
var type2 = SubstituteType(typeMap2, param2.Type);
// the runtime compares custom modifiers using (effectively) SequenceEqual
if (considerCustomModifiers)
if (!type1.Equals(type2, typeSymbolEqualityOptions))
{
if (!type1.TypeSymbol.Equals(type2.TypeSymbol, ignoreDynamic: ignoreDynamic) ||
!type1.CustomModifiers.SequenceEqual(type2.CustomModifiers) ||
(param1.CountOfCustomModifiersPrecedingByRef != param2.CountOfCustomModifiersPrecedingByRef))
{
return false;
}
return false;
}
else if (!type1.TypeSymbol.Equals(type2.TypeSymbol, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: ignoreDynamic))
if ((typeSymbolEqualityOptions & TypeSymbolEqualityOptions.IgnoreCustomModifiers) == 0 &&
param1.CountOfCustomModifiersPrecedingByRef != param2.CountOfCustomModifiersPrecedingByRef)
{
return false;
}
......
......@@ -301,7 +301,7 @@ public override int GetHashCode()
return Hash.Combine(MetadataName, Hash.Combine(_containingModule, Hash.Combine(_namespaceName, arity)));
}
internal override bool Equals(TypeSymbol t2, bool ignoreCustomModifiersAndArraySizesAndLowerBounds, bool ignoreDynamic)
internal override bool Equals(TypeSymbol t2, TypeSymbolEqualityOptions options)
{
if (ReferenceEquals(this, t2))
{
......@@ -309,7 +309,7 @@ internal override bool Equals(TypeSymbol t2, bool ignoreCustomModifiersAndArrayS
}
// if ignoring dynamic, then treat dynamic the same as the type 'object'
if (ignoreDynamic &&
if ((options & TypeSymbolEqualityOptions.IgnoreDynamic) != 0 &&
(object)t2 != null &&
t2.TypeKind == TypeKind.Dynamic &&
this.SpecialType == Microsoft.CodeAnalysis.SpecialType.System_Object)
......@@ -404,7 +404,7 @@ public override int GetHashCode()
return Hash.Combine(_containingType, Hash.Combine(MetadataName, arity));
}
internal override bool Equals(TypeSymbol t2, bool ignoreCustomModifiersAndArraySizesAndLowerBounds, bool ignoreDynamic)
internal override bool Equals(TypeSymbol t2, TypeSymbolEqualityOptions options)
{
if (ReferenceEquals(this, t2))
{
......@@ -414,7 +414,7 @@ internal override bool Equals(TypeSymbol t2, bool ignoreCustomModifiersAndArrayS
var other = t2 as Nested;
return (object)other != null && string.Equals(MetadataName, other.MetadataName, StringComparison.Ordinal) &&
arity == other.arity &&
_containingType.Equals(other._containingType, ignoreCustomModifiersAndArraySizesAndLowerBounds, ignoreDynamic);
_containingType.Equals(other._containingType, options);
}
}
}
......
......@@ -605,13 +605,13 @@ public override int GetHashCode()
/// <summary>
/// Compares this type to another type.
/// </summary>
internal override bool Equals(TypeSymbol t2, bool ignoreCustomModifiersAndArraySizesAndLowerBounds = false, bool ignoreDynamic = false)
internal override bool Equals(TypeSymbol t2, TypeSymbolEqualityOptions options)
{
if (ReferenceEquals(this, t2)) return true;
if ((object)t2 == null) return false;
// if ignoring dynamic, then treat dynamic the same as the type 'object'
if (ignoreDynamic &&
if ((options & TypeSymbolEqualityOptions.IgnoreDynamic) != 0 &&
t2.TypeKind == TypeKind.Dynamic &&
this.SpecialType == SpecialType.System_Object)
{
......@@ -638,15 +638,15 @@ internal override bool Equals(TypeSymbol t2, bool ignoreCustomModifiersAndArrayS
// The checks above are supposed to handle the vast majority of cases.
// More complicated cases are handled in a special helper to make the common case scenario simple/fast
return EqualsComplicatedCases(other, ignoreCustomModifiersAndArraySizesAndLowerBounds, ignoreDynamic);
return EqualsComplicatedCases(other, options);
}
/// <summary>
/// Helper for more complicated cases of Equals like when we have generic instantiations or types nested within them.
/// </summary>
private bool EqualsComplicatedCases(NamedTypeSymbol other, bool ignoreCustomModifiersAndArraySizesAndLowerBounds, bool ignoreDynamic)
private bool EqualsComplicatedCases(NamedTypeSymbol other, TypeSymbolEqualityOptions options)
{
if ((object)this.ContainingType != null && !this.ContainingType.Equals(other.ContainingType, ignoreCustomModifiersAndArraySizesAndLowerBounds, ignoreDynamic))
if ((object)this.ContainingType != null && !this.ContainingType.Equals(other.ContainingType, options))
{
return false;
}
......@@ -680,12 +680,7 @@ private bool EqualsComplicatedCases(NamedTypeSymbol other, bool ignoreCustomModi
{
var typeArgument = typeArguments[i];
var otherTypeArgument = otherTypeArguments[i];
if (!typeArgument.TypeSymbol.Equals(otherTypeArgument.TypeSymbol, ignoreCustomModifiersAndArraySizesAndLowerBounds, ignoreDynamic))
{
return false;
}
if (!ignoreCustomModifiersAndArraySizesAndLowerBounds && !typeArgument.CustomModifiers.SequenceEqual(otherTypeArgument.CustomModifiers))
if (!typeArgument.Equals(otherTypeArgument, options))
{
return false;
}
......
......@@ -75,7 +75,7 @@ public override int GetHashCode()
return RuntimeHelpers.GetHashCode(this);
}
internal override bool Equals(TypeSymbol t2, bool ignoreCustomModifiersAndArraySizesAndLowerBounds, bool ignoreDynamic)
internal override bool Equals(TypeSymbol t2, TypeSymbolEqualityOptions options)
{
return ReferenceEquals(this, t2);
}
......
......@@ -63,7 +63,7 @@ public override int GetHashCode()
return RuntimeHelpers.GetHashCode(this);
}
internal override bool Equals(TypeSymbol t2, bool ignoreCustomModifiersAndArraySizesAndLowerBounds, bool ignoreDynamic)
internal override bool Equals(TypeSymbol t2, TypeSymbolEqualityOptions options)
{
return ReferenceEquals(this, t2);
}
......
......@@ -99,7 +99,7 @@ public override int GetHashCode()
return RuntimeHelpers.GetHashCode(this);
}
internal override bool Equals(TypeSymbol t2, bool ignoreCustomModifiersAndArraySizesAndLowerBounds, bool ignoreDynamic)
internal override bool Equals(TypeSymbol t2, TypeSymbolEqualityOptions options)
{
return ReferenceEquals(this, t2);
}
......
......@@ -206,50 +206,28 @@ public override int GetHashCode()
return Hash.Combine(current, indirections);
}
internal override bool Equals(TypeSymbol t2, bool ignoreCustomModifiersAndArraySizesAndLowerBounds, bool ignoreDynamic)
internal override bool Equals(TypeSymbol t2, TypeSymbolEqualityOptions options)
{
return this.Equals(t2 as PointerTypeSymbol, ignoreCustomModifiersAndArraySizesAndLowerBounds, ignoreDynamic);
return this.Equals(t2 as PointerTypeSymbol, options);
}
internal bool Equals(PointerTypeSymbol other)
{
return this.Equals(other, false, false);
return this.Equals(other, TypeSymbolEqualityOptions.None);
}
private bool Equals(PointerTypeSymbol other, bool ignoreCustomModifiersAndArraySizesAndLowerBounds, bool ignoreDynamic)
private bool Equals(PointerTypeSymbol other, TypeSymbolEqualityOptions options)
{
if (ReferenceEquals(this, other))
{
return true;
}
if ((object)other == null || !other._pointedAtType.TypeSymbol.Equals(_pointedAtType.TypeSymbol, ignoreCustomModifiersAndArraySizesAndLowerBounds, ignoreDynamic))
if ((object)other == null || !other._pointedAtType.Equals(_pointedAtType, options))
{
return false;
}
if (!ignoreCustomModifiersAndArraySizesAndLowerBounds)
{
// Make sure custom modifiers are the same.
var mod = this.PointedAtType.CustomModifiers;
var otherMod = other.PointedAtType.CustomModifiers;
int count = mod.Length;
if (count != otherMod.Length)
{
return false;
}
for (int i = 0; i < count; i++)
{
if (!mod[i].Equals(otherMod[i]))
{
return false;
}
}
}
return true;
}
......
......@@ -87,7 +87,7 @@ public override int Ordinal
get { return _ordinal; }
}
internal override bool Equals(TypeSymbol t2, bool ignoreCustomModifiersAndArraySizesAndLowerBounds, bool ignoreDynamic)
internal override bool Equals(TypeSymbol t2, TypeSymbolEqualityOptions options)
{
if (ReferenceEquals(this, t2))
{
......
......@@ -46,7 +46,7 @@ internal static class CustomModifierUtils
// we want to retain the original (incorrect) return type to avoid hiding the return type
// given in source.
TypeSymbol returnTypeWithCustomModifiers = sourceMethodReturnType.TypeSymbol;
if (returnTypeSymbol.Equals(returnTypeWithCustomModifiers, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: true))
if (returnTypeSymbol.Equals(returnTypeWithCustomModifiers, TypeSymbolEqualityOptions.SameType))
{
returnType = returnType.Update(CopyTypeCustomModifiers(returnTypeWithCustomModifiers, returnTypeSymbol, RefKind.None, destinationMethod.ContainingAssembly),
sourceMethodReturnType.CustomModifiers);
......@@ -60,7 +60,7 @@ internal static class CustomModifierUtils
/// <returns><paramref name="destinationType"/> with custom modifiers copied from <paramref name="sourceType"/>.</returns>
internal static TypeSymbol CopyTypeCustomModifiers(TypeSymbol sourceType, TypeSymbol destinationType, RefKind refKind, AssemblySymbol containingAssembly)
{
Debug.Assert(sourceType.Equals(destinationType, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: true));
Debug.Assert(sourceType.Equals(destinationType, TypeSymbolEqualityOptions.SameType));
// NOTE: overrides can differ by object/dynamic. If they do, we'll need to tweak newType before
// we can use it in place of this.Type. We do so by computing the dynamic transform flags that
......@@ -70,8 +70,8 @@ internal static TypeSymbol CopyTypeCustomModifiers(TypeSymbol sourceType, TypeSy
ImmutableArray<bool> flags = CSharpCompilation.DynamicTransformsEncoder.Encode(destinationType, customModifierCount, refKind);
TypeSymbol resultType = DynamicTypeDecoder.TransformTypeWithoutCustomModifierFlags(sourceType, containingAssembly, refKind, flags);
Debug.Assert(resultType.Equals(sourceType, ignoreCustomModifiersAndArraySizesAndLowerBounds: false, ignoreDynamic: true)); // Same custom modifiers as source type.
Debug.Assert(resultType.Equals(destinationType, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: false)); // Same object/dynamic as destination type.
Debug.Assert(resultType.Equals(sourceType, TypeSymbolEqualityOptions.IgnoreDynamic)); // Same custom modifiers as source type.
Debug.Assert(resultType.Equals(destinationType, TypeSymbolEqualityOptions.IgnoreCustomModifiersAndArraySizesAndLowerBounds)); // Same object/dynamic as destination type.
return resultType;
}
......
......@@ -99,7 +99,7 @@ public override int Ordinal
}
// These object are unique (per index).
internal override bool Equals(TypeSymbol t2, bool ignoreCustomModifiersAndArraySizesAndLowerBounds, bool ignoreDynamic)
internal override bool Equals(TypeSymbol t2, TypeSymbolEqualityOptions options)
{
return ReferenceEquals(this, t2);
}
......
......@@ -29,36 +29,18 @@ internal sealed class LambdaSymbol : MethodSymbol
Symbol containingSymbol,
UnboundLambda unboundLambda,
ImmutableArray<ParameterSymbol> delegateParameters,
TypeSymbol returnType)
TypeSymbolWithAnnotations returnType)
{
_containingSymbol = containingSymbol;
_messageID = unboundLambda.Data.MessageID;
_syntax = unboundLambda.Syntax;
_returnType = (object)returnType == null ? ReturnTypeIsBeingInferred : TypeSymbolWithAnnotations.Create(returnType);
_returnType = returnType ?? ReturnTypeIsBeingInferred;
_isSynthesized = unboundLambda.WasCompilerGenerated;
_isAsync = unboundLambda.IsAsync;
// No point in making this lazy. We are always going to need these soon after creation of the symbol.
_parameters = MakeParameters(compilation, unboundLambda, delegateParameters);
}
public LambdaSymbol(
Symbol containingSymbol,
ImmutableArray<ParameterSymbol> parameters,
TypeSymbol returnType,
MessageID messageID,
CSharpSyntaxNode syntax,
bool isSynthesized,
bool isAsync)
{
_containingSymbol = containingSymbol;
_messageID = messageID;
_syntax = syntax;
_returnType = (object)returnType == null ? ReturnTypeIsBeingInferred : TypeSymbolWithAnnotations.Create(returnType);
_isSynthesized = isSynthesized;
_isAsync = isAsync;
_parameters = parameters.SelectAsArray(CopyParameter, this);
}
public LambdaSymbol(
Symbol containingSymbol,
ImmutableArray<ParameterSymbol> parameters,
......@@ -71,7 +53,7 @@ internal sealed class LambdaSymbol : MethodSymbol
_containingSymbol = containingSymbol;
_messageID = messageID;
_syntax = syntax;
_returnType = returnType;
_returnType = returnType ?? ReturnTypeIsBeingInferred;
_isSynthesized = isSynthesized;
_isAsync = isAsync;
_parameters = parameters.SelectAsArray(CopyParameter, this);
......
......@@ -500,7 +500,7 @@ protected static void CopyEventCustomModifiers(EventSymbol eventWithCustomModifi
// We do an extra check before copying the type to handle the case where the overriding
// event (incorrectly) has a different type than the overridden event. In such cases,
// we want to retain the original (incorrect) type to avoid hiding the type given in source.
if (type.TypeSymbol.Equals(overriddenEventType, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: false))
if (type.TypeSymbol.Equals(overriddenEventType, TypeSymbolEqualityOptions.IgnoreCustomModifiersAndArraySizesAndLowerBounds))
{
type = type.Update(overriddenEventType, ImmutableArray<CustomModifier>.Empty);
}
......
......@@ -1907,13 +1907,13 @@ private static bool DoOperatorsPair(MethodSymbol op1, MethodSymbol op2)
for (int p = 0; p < op1.ParameterCount; ++p)
{
if (!op1.ParameterTypes[p].Equals(op2.ParameterTypes[p], ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: true))
if (!op1.ParameterTypes[p].Equals(op2.ParameterTypes[p], TypeSymbolEqualityOptions.SameType))
{
return false;
}
}
if (!op1.ReturnType.TypeSymbol.Equals(op2.ReturnType.TypeSymbol, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: true))
if (!op1.ReturnType.TypeSymbol.Equals(op2.ReturnType.TypeSymbol, TypeSymbolEqualityOptions.SameType))
{
return false;
}
......@@ -2576,7 +2576,7 @@ private static bool ParametersMatchPropertyAccessor(PropertySymbol propertySymbo
}
var propertyParamType = (((i == numParams - 1) && !getNotSet) ? propertySymbol.Type : propertyParams[i].Type).TypeSymbol;
if (!propertyParamType.Equals(methodParam.Type.TypeSymbol, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: true))
if (!propertyParamType.Equals(methodParam.Type.TypeSymbol, TypeSymbolEqualityOptions.SameType))
{
return false;
}
......@@ -2594,7 +2594,7 @@ private static bool ParametersMatchEventAccessor(EventSymbol eventSymbol, Immuta
return
methodParams.Length == 1 &&
methodParams[0].RefKind == RefKind.None &&
eventSymbol.Type.TypeSymbol.Equals(methodParams[0].Type.TypeSymbol, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: true);
eventSymbol.Type.TypeSymbol.Equals(methodParams[0].Type.TypeSymbol, TypeSymbolEqualityOptions.SameType);
}
private void AddEnumMembers(MembersAndInitializersBuilder result, EnumDeclarationSyntax syntax, DiagnosticBag diagnostics)
......
......@@ -699,7 +699,7 @@ private void CheckNewModifier(Symbol symbol, bool isNew, DiagnosticBag diagnosti
TypeSymbol overriddenMemberType = overriddenProperty.Type.TypeSymbol;
// Ignore custom modifiers because this diagnostic is based on the C# semantics.
if (!overridingMemberType.Equals(overriddenMemberType, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: true))
if (!overridingMemberType.Equals(overriddenMemberType, TypeSymbolEqualityOptions.SameType))
{
diagnostics.Add(ErrorCode.ERR_CantChangeTypeOnOverride, overridingMemberLocation, overridingMember, overriddenMember, overriddenMemberType);
suppressAccessors = true; //we get really unhelpful errors from the accessor if the type is mismatched
......@@ -736,7 +736,7 @@ private void CheckNewModifier(Symbol symbol, bool isNew, DiagnosticBag diagnosti
TypeSymbol overriddenMemberType = overriddenEvent.Type.TypeSymbol;
// Ignore custom modifiers because this diagnostic is based on the C# semantics.
if (!overridingMemberType.Equals(overriddenMemberType, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: true))
if (!overridingMemberType.Equals(overriddenMemberType, TypeSymbolEqualityOptions.SameType))
{
diagnostics.Add(ErrorCode.ERR_CantChangeTypeOnOverride, overridingMemberLocation, overridingMember, overriddenMember, overriddenMemberType);
suppressAccessors = true; //we get really unhelpful errors from the accessor if the type is mismatched
......
......@@ -231,7 +231,7 @@ internal sealed class SourcePropertySymbol : PropertySymbol, IAttributeTargetSym
// We do an extra check before copying the type to handle the case where the overriding
// property (incorrectly) has a different type than the overridden property. In such cases,
// we want to retain the original (incorrect) type to avoid hiding the type given in source.
if (_lazyType.TypeSymbol.Equals(overriddenPropertyType.TypeSymbol, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: false))
if (_lazyType.TypeSymbol.Equals(overriddenPropertyType.TypeSymbol, TypeSymbolEqualityOptions.IgnoreCustomModifiersAndArraySizesAndLowerBounds))
{
_lazyType = overriddenPropertyType;
}
......
......@@ -6,6 +6,7 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Threading;
namespace Microsoft.CodeAnalysis.CSharp.Symbols
......@@ -221,7 +222,7 @@ public TypeSymbolWithAnnotations AsNullableReferenceOrValueType(CSharpCompilatio
/// If it is a nullable reference type, <see cref="TypeSymbol"/>
/// simply returns a symbol for the reference type.
/// </summary>
public abstract bool IsNullable { get; }
public abstract bool? IsNullable { get; }
/// <summary>
/// Is this System.Nullable`1 type, or its substitution.
......@@ -249,6 +250,32 @@ public TypeSymbolWithAnnotations AsNullableReferenceOrValueType(CSharpCompilatio
public virtual bool IsVoid => TypeSymbol.SpecialType == SpecialType.System_Void;
public virtual bool IsSZArray() => TypeSymbol.IsSZArray();
public bool Equals(TypeSymbolWithAnnotations other, TypeSymbolEqualityOptions options)
{
if (ReferenceEquals(this, other))
{
return true;
}
if ((object)other == null ||
!other.TypeSymbol.Equals(this.TypeSymbol, options))
{
return false;
}
// Make sure custom modifiers are the same.
if ((options & TypeSymbolEqualityOptions.IgnoreCustomModifiers) == 0 && !this.CustomModifiers.SequenceEqual(other.CustomModifiers))
{
return false;
}
if ((options & TypeSymbolEqualityOptions.CompareNullableModifiersForReferenceTypes) != 0 && other.IsNullable != this.IsNullable)
{
return false;
}
return true;
}
public bool GetUnificationUseSiteDiagnosticRecursive(ref DiagnosticInfo result, Symbol owner, ref HashSet<TypeSymbol> checkedTypes)
{
......@@ -270,14 +297,82 @@ public virtual TypeSymbolWithAnnotations SubstituteType(AbstractTypeMap typeMap)
{
var newCustomModifiers = typeMap.SubstituteCustomModifiers(this.CustomModifiers);
var newTypeWithModifiers = typeMap.SubstituteType(this.TypeSymbol);
if (!newTypeWithModifiers.Is(this.TypeSymbol) || newCustomModifiers != this.CustomModifiers)
bool? newIsNullable = this.IsNullable;
if (newIsNullable == false)
{
return DoUpdate(newTypeWithModifiers.TypeSymbol, newCustomModifiers.Concat(newTypeWithModifiers.CustomModifiers));
newIsNullable = newTypeWithModifiers.IsNullable;
}
else if (newIsNullable == null)
{
if (newTypeWithModifiers.IsNullable == true)
{
newIsNullable = true;
}
}
else
{
return this; // substitution had no effect on the type or modifiers
Debug.Assert(newIsNullable == true);
}
if (!this.TypeSymbol.Equals(newTypeWithModifiers.TypeSymbol, TypeSymbolEqualityOptions.CompareNullableModifiersForReferenceTypes) ||
!newTypeWithModifiers.CustomModifiers.IsEmpty ||
newIsNullable != this.IsNullable ||
newCustomModifiers != this.CustomModifiers)
{
if (newTypeWithModifiers.TypeSymbol.IsNullableType())
{
Debug.Assert(newIsNullable == true);
if (newCustomModifiers.IsEmpty)
{
return newTypeWithModifiers;
}
return TypeSymbolWithAnnotations.Create(newTypeWithModifiers.TypeSymbol, newCustomModifiers.Concat(newTypeWithModifiers.CustomModifiers));
}
if (newIsNullable == false)
{
Debug.Assert(newTypeWithModifiers.IsNullable == false);
if (newCustomModifiers.IsEmpty)
{
return newTypeWithModifiers;
}
return TypeSymbolWithAnnotations.Create(newTypeWithModifiers.TypeSymbol, newCustomModifiers.Concat(newTypeWithModifiers.CustomModifiers));
}
Debug.Assert(newIsNullable != false);
if (newCustomModifiers.IsEmpty && newTypeWithModifiers.IsNullable == newIsNullable)
{
return newTypeWithModifiers;
}
newCustomModifiers = newCustomModifiers.Concat(newTypeWithModifiers.CustomModifiers);
if (newIsNullable == true)
{
if (newCustomModifiers.IsEmpty)
{
return new NullableReferenceTypeWithoutCustomModifiers(newTypeWithModifiers.TypeSymbol);
}
return new NullableReferenceTypeWithCustomModifiers(newTypeWithModifiers.TypeSymbol, newCustomModifiers);
}
Debug.Assert(newIsNullable == null);
if (newCustomModifiers.IsEmpty)
{
return new ReferenceTypeUnknownNullabilityWithoutCustomModifiers(newTypeWithModifiers.TypeSymbol);
}
return new ReferenceTypeUnknownNullabilityWithCustomModifiers(newTypeWithModifiers.TypeSymbol, newCustomModifiers);
}
return this; // substitution had no effect on the type or modifiers
}
public virtual void ReportDiagnosticsIfObsolete(Binder binder, SyntaxNode syntax, DiagnosticBag diagnostics)
......@@ -319,7 +414,7 @@ public WithoutCustomModifiers(TypeSymbol typeSymbol)
}
public sealed override TypeSymbol TypeSymbol => _typeSymbol;
public sealed override bool IsNullable => _typeSymbol.IsNullableType();
public sealed override bool? IsNullable => _typeSymbol.IsNullableType();
public override ImmutableArray<CustomModifier> CustomModifiers => ImmutableArray<CustomModifier>.Empty;
public override TypeSymbolWithAnnotations WithModifiers(ImmutableArray<CustomModifier> customModifiers)
......@@ -333,7 +428,7 @@ public override TypeSymbolWithAnnotations WithModifiers(ImmutableArray<CustomMod
}
public override TypeSymbol AsTypeSymbolOnly() => _typeSymbol;
public override bool Is(TypeSymbol other) => _typeSymbol == other;
public override bool Is(TypeSymbol other) => _typeSymbol.Equals(other, TypeSymbolEqualityOptions.CompareNullableModifiersForReferenceTypes);
protected sealed override TypeSymbolWithAnnotations DoUpdate(TypeSymbol typeSymbol, ImmutableArray<CustomModifier> customModifiers)
{
......@@ -363,10 +458,15 @@ public NullableReferenceTypeWithoutCustomModifiers(TypeSymbol typeSymbol)
}
public sealed override TypeSymbol TypeSymbol => _typeSymbol;
public sealed override bool IsNullable => true;
public sealed override bool? IsNullable => true;
public override ImmutableArray<CustomModifier> CustomModifiers => ImmutableArray<CustomModifier>.Empty;
public override TypeSymbol AsTypeSymbolOnly() => _typeSymbol;
public override TypeSymbol AsTypeSymbolOnly()
{
Debug.Assert(this.IsNullable == false);
return _typeSymbol;
}
public sealed override bool Is(TypeSymbol other) => false; // It has nullable annotation.
public override TypeSymbolWithAnnotations WithModifiers(ImmutableArray<CustomModifier> customModifiers)
......@@ -395,6 +495,55 @@ public sealed override TypeSymbolWithAnnotations AsNullableReferenceType()
}
}
private class ReferenceTypeUnknownNullabilityWithoutCustomModifiers : TypeSymbolWithAnnotations
{
protected readonly TypeSymbol _typeSymbol;
public ReferenceTypeUnknownNullabilityWithoutCustomModifiers(TypeSymbol typeSymbol)
{
Debug.Assert((object)typeSymbol != null);
Debug.Assert(!typeSymbol.IsNullableType());
_typeSymbol = typeSymbol;
}
public sealed override TypeSymbol TypeSymbol => _typeSymbol;
public sealed override bool? IsNullable => null;
public override ImmutableArray<CustomModifier> CustomModifiers => ImmutableArray<CustomModifier>.Empty;
public override TypeSymbol AsTypeSymbolOnly()
{
Debug.Assert(this.IsNullable == false);
return _typeSymbol;
}
public sealed override bool Is(TypeSymbol other) => false; // It has nullable annotation, unknown is an annotation.
public override TypeSymbolWithAnnotations WithModifiers(ImmutableArray<CustomModifier> customModifiers)
{
if (customModifiers.IsDefaultOrEmpty)
{
return this;
}
return new ReferenceTypeUnknownNullabilityWithCustomModifiers(_typeSymbol, customModifiers);
}
protected sealed override TypeSymbolWithAnnotations DoUpdate(TypeSymbol typeSymbol, ImmutableArray<CustomModifier> customModifiers)
{
if (customModifiers.IsDefaultOrEmpty)
{
return new ReferenceTypeUnknownNullabilityWithoutCustomModifiers(typeSymbol);
}
return new ReferenceTypeUnknownNullabilityWithCustomModifiers(typeSymbol, customModifiers);
}
public override TypeSymbolWithAnnotations AsNullableReferenceType()
{
return new NullableReferenceTypeWithoutCustomModifiers(_typeSymbol);
}
}
private class LazyNullableType : TypeSymbolWithAnnotations
{
private readonly CSharpCompilation _compilation;
......@@ -404,13 +553,13 @@ private class LazyNullableType : TypeSymbolWithAnnotations
public LazyNullableType(CSharpCompilation compilation, SyntaxReference nullableTypeSyntax, TypeSymbolWithAnnotations underlying)
{
Debug.Assert(!underlying.IsNullable);
Debug.Assert(underlying.IsNullable == false);
_compilation = compilation;
_nullableTypeSyntax = nullableTypeSyntax;
_underlying = underlying;
}
public override bool IsNullable => true;
public override bool? IsNullable => true;
public override bool IsVoid
{
......@@ -492,12 +641,13 @@ public override TypeSymbol TypeSymbol
public override TypeSymbol AsTypeSymbolOnly()
{
Debug.Assert(TypeSymbol.IsNullableType() && CustomModifiers.IsEmpty);
return TypeSymbol;
}
public override bool Is(TypeSymbol other)
{
return TypeSymbol == other;
return TypeSymbol.Equals(other, TypeSymbolEqualityOptions.CompareNullableModifiersForReferenceTypes) && TypeSymbol.IsNullableType();
}
public override ImmutableArray<CustomModifier> CustomModifiers => _underlying.CustomModifiers;
......@@ -600,7 +750,7 @@ public override TypeSymbolWithAnnotations WithModifiers(ImmutableArray<CustomMod
public override TypeSymbol AsTypeSymbolOnly()
{
Debug.Assert(this.CustomModifiers.IsEmpty);
return _typeSymbol;
return base.AsTypeSymbolOnly();
}
public override bool Is(TypeSymbol other)
......@@ -645,7 +795,42 @@ public override TypeSymbolWithAnnotations WithModifiers(ImmutableArray<CustomMod
public override TypeSymbol AsTypeSymbolOnly()
{
Debug.Assert(this.CustomModifiers.IsEmpty);
return _typeSymbol;
return base.AsTypeSymbolOnly();
}
}
private class ReferenceTypeUnknownNullabilityWithCustomModifiers : ReferenceTypeUnknownNullabilityWithoutCustomModifiers
{
private readonly ImmutableArray<CustomModifier> _customModifiers;
public ReferenceTypeUnknownNullabilityWithCustomModifiers(TypeSymbol typeSymbol, ImmutableArray<CustomModifier> customModifiers)
: base(typeSymbol)
{
Debug.Assert(!customModifiers.IsDefaultOrEmpty);
_customModifiers = customModifiers;
}
public override TypeSymbolWithAnnotations WithModifiers(ImmutableArray<CustomModifier> customModifiers)
{
if (customModifiers.IsDefaultOrEmpty)
{
return this;
}
return new ReferenceTypeUnknownNullabilityWithCustomModifiers(_typeSymbol, _customModifiers.Concat(customModifiers));
}
public override ImmutableArray<CustomModifier> CustomModifiers => _customModifiers;
public override TypeSymbol AsTypeSymbolOnly()
{
Debug.Assert(this.CustomModifiers.IsEmpty);
return base.AsTypeSymbolOnly();
}
public override TypeSymbolWithAnnotations AsNullableReferenceType()
{
return new NullableReferenceTypeWithCustomModifiers(_typeSymbol, _customModifiers);
}
}
}
......
......@@ -21,17 +21,17 @@ internal sealed class SynthesizedIntrinsicOperatorSymbol : MethodSymbol
public SynthesizedIntrinsicOperatorSymbol(TypeSymbol leftType, string name, TypeSymbol rightType, TypeSymbol returnType, bool isCheckedBuiltin)
{
if (leftType.Equals(rightType, ignoreCustomModifiersAndArraySizesAndLowerBounds: true))
if (leftType.Equals(rightType, TypeSymbolEqualityOptions.IgnoreCustomModifiersAndArraySizesAndLowerBounds))
{
_containingType = leftType;
}
else if (rightType.Equals(returnType, ignoreCustomModifiersAndArraySizesAndLowerBounds: true))
else if (rightType.Equals(returnType, TypeSymbolEqualityOptions.IgnoreCustomModifiersAndArraySizesAndLowerBounds))
{
_containingType = rightType;
}
else
{
Debug.Assert(leftType.Equals(returnType, ignoreCustomModifiersAndArraySizesAndLowerBounds: true));
Debug.Assert(leftType.Equals(returnType, TypeSymbolEqualityOptions.IgnoreCustomModifiersAndArraySizesAndLowerBounds));
_containingType = leftType;
}
......
......@@ -493,17 +493,17 @@ internal sealed override bool GetUnificationUseSiteDiagnosticRecursive(ref Diagn
return false;
}
internal override bool Equals(TypeSymbol t2, bool ignoreCustomModifiersAndArraySizesAndLowerBounds, bool ignoreDynamic)
internal override bool Equals(TypeSymbol t2, TypeSymbolEqualityOptions options)
{
return this.Equals(t2 as TypeParameterSymbol, ignoreCustomModifiersAndArraySizesAndLowerBounds, ignoreDynamic);
return this.Equals(t2 as TypeParameterSymbol, options);
}
internal bool Equals(TypeParameterSymbol other)
{
return Equals(other, false, false);
return Equals(other, TypeSymbolEqualityOptions.None);
}
private bool Equals(TypeParameterSymbol other, bool ignoreCustomModifiersAndArraySizesAndLowerBounds, bool ignoreDynamic)
private bool Equals(TypeParameterSymbol other, TypeSymbolEqualityOptions options)
{
if (ReferenceEquals(this, other))
{
......@@ -516,7 +516,7 @@ private bool Equals(TypeParameterSymbol other, bool ignoreCustomModifiersAndArra
}
// Type parameters may be equal but not reference equal due to independent alpha renamings.
return other.ContainingSymbol.ContainingType.Equals(this.ContainingSymbol.ContainingType, ignoreCustomModifiersAndArraySizesAndLowerBounds, ignoreDynamic);
return other.ContainingSymbol.ContainingType.Equals(this.ContainingSymbol.ContainingType, options);
}
public override int GetHashCode()
......
......@@ -107,7 +107,12 @@ private InterfaceInfo GetInterfaceInfo()
/// <summary>
/// A comparator that treats dynamic and object as "the same" types.
/// </summary>
internal static readonly EqualityComparer<TypeSymbol> EqualsIgnoringDynamicComparer = new EqualsIgnoringComparer(ignoreDynamic: true, ignoreCustomModifiersAndArraySizesAndLowerBounds: false);
internal static readonly EqualityComparer<TypeSymbol> EqualsIgnoringDynamicComparer = new EqualityComparer(TypeSymbolEqualityOptions.IgnoreDynamic);
/// <summary>
/// A comparator that pays attention to nullable modifiers in addition to default behavior.
/// </summary>
internal static readonly EqualityComparer<TypeSymbol> EqualsIncludingNullableComparer = new EqualityComparer(TypeSymbolEqualityOptions.CompareNullableModifiersForReferenceTypes);
/// <summary>
/// The original definition of this symbol. If this symbol is constructed from another
......@@ -270,9 +275,10 @@ internal bool IsDerivedFrom(TypeSymbol type, bool ignoreDynamic, ref HashSet<Dia
}
var t = this.BaseTypeWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics);
var options = ignoreDynamic ? TypeSymbolEqualityOptions.IgnoreDynamic : TypeSymbolEqualityOptions.None;
while ((object)t != null)
{
if (type.Equals(t, ignoreDynamic: ignoreDynamic))
if (type.Equals(t, options))
{
return true;
}
......@@ -288,7 +294,8 @@ internal bool IsDerivedFrom(TypeSymbol type, bool ignoreDynamic, ref HashSet<Dia
/// </summary>
internal bool IsEqualToOrDerivedFrom(TypeSymbol type, bool ignoreDynamic, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{
return this.Equals(type, ignoreDynamic: ignoreDynamic) || this.IsDerivedFrom(type, ignoreDynamic, ref useSiteDiagnostics);
return this.Equals(type, ignoreDynamic ? TypeSymbolEqualityOptions.IgnoreDynamic : TypeSymbolEqualityOptions.None) ||
this.IsDerivedFrom(type, ignoreDynamic, ref useSiteDiagnostics);
}
/// <summary>
......@@ -296,10 +303,9 @@ internal bool IsEqualToOrDerivedFrom(TypeSymbol type, bool ignoreDynamic, ref Ha
/// semantics.
/// </summary>
/// <param name="t2">The other type.</param>
/// <param name="ignoreCustomModifiersAndArraySizesAndLowerBounds">True to compare without regard to custom modifiers, false by default.</param>
/// <param name="ignoreDynamic">True to ignore the distinction between object and dynamic, false by default.</param>
/// <param name="options"></param>
/// <returns>True if the types are equivalent.</returns>
internal virtual bool Equals(TypeSymbol t2, bool ignoreCustomModifiersAndArraySizesAndLowerBounds = false, bool ignoreDynamic = false)
internal virtual bool Equals(TypeSymbol t2, TypeSymbolEqualityOptions options = TypeSymbolEqualityOptions.None)
{
return ReferenceEquals(this, t2);
}
......@@ -308,7 +314,7 @@ public sealed override bool Equals(object obj)
{
var t2 = obj as TypeSymbol;
if ((object)t2 == null) return false;
return this.Equals(t2, false, false);
return this.Equals(t2, TypeSymbolEqualityOptions.None);
}
/// <summary>
......@@ -320,14 +326,13 @@ public override int GetHashCode()
return RuntimeHelpers.GetHashCode(this);
}
private sealed class EqualsIgnoringComparer : EqualityComparer<TypeSymbol>
private sealed class EqualityComparer : EqualityComparer<TypeSymbol>
{
private readonly bool _ignoreDynamic, _ignoreCustomModifiersAndArraySizesAndLowerBounds;
private readonly TypeSymbolEqualityOptions _options;
public EqualsIgnoringComparer(bool ignoreDynamic, bool ignoreCustomModifiersAndArraySizesAndLowerBounds)
public EqualityComparer(TypeSymbolEqualityOptions options)
{
_ignoreDynamic = ignoreDynamic;
_ignoreCustomModifiersAndArraySizesAndLowerBounds = ignoreCustomModifiersAndArraySizesAndLowerBounds;
_options = options;
}
public override int GetHashCode(TypeSymbol obj)
......@@ -339,7 +344,7 @@ public override bool Equals(TypeSymbol x, TypeSymbol y)
{
return
(object)x == null ? (object)y == null :
x.Equals(y, ignoreCustomModifiersAndArraySizesAndLowerBounds: _ignoreCustomModifiersAndArraySizesAndLowerBounds, ignoreDynamic: _ignoreDynamic);
x.Equals(y, _options);
}
}
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
[Flags]
internal enum TypeSymbolEqualityOptions : byte
{
None = 0,
IgnoreCustomModifiers = 1 << 0,
IgnoreArraySizesAndLowerBounds = 1 << 1,
IgnoreDynamic = 1 << 2,
CompareNullableModifiersForReferenceTypes = 1 << 3,
IgnoreCustomModifiersAndArraySizesAndLowerBounds = IgnoreCustomModifiers | IgnoreArraySizesAndLowerBounds,
SameType = IgnoreDynamic | IgnoreCustomModifiers | IgnoreArraySizesAndLowerBounds
}
}
\ No newline at end of file
......@@ -89,7 +89,7 @@ internal override DiagnosticInfo ErrorInfo
}
}
internal override bool Equals(TypeSymbol t2, bool ignoreCustomModifiersAndArraySizesAndLowerBounds, bool ignoreDynamic)
internal override bool Equals(TypeSymbol t2, TypeSymbolEqualityOptions options)
{
if ((object)t2 == (object)this)
{
......
......@@ -3129,6 +3129,754 @@ class CL1
);
}
[Fact]
public void Lambda_01()
{
CSharpCompilation c = CreateCompilationWithMscorlib(@"
class C
{
static void Main()
{
}
CL1? M1() { return null; }
void Test1()
{
System.Func<CL1?> x1 = () => M1();
}
void Test2()
{
System.Func<CL1?> x2 = delegate { return M1(); };
}
delegate CL1? D1();
void Test3()
{
D1 x3 = () => M1();
}
void Test4()
{
D1 x4 = delegate { return M1(); };
}
}
class CL1
{}
", parseOptions: TestOptions.Regular.WithFeature("staticNullChecking", "true"));
c.VerifyDiagnostics(
);
}
[Fact]
public void Lambda_02()
{
CSharpCompilation c = CreateCompilationWithMscorlib(@"
class C
{
static void Main()
{
}
CL1? M1() { return null; }
void Test1()
{
System.Action<CL1?> x1 = (p1) => p1 = M1();
}
delegate void D1(CL1? p);
void Test3()
{
D1 x3 = (p3) => p3 = M1();
}
}
class CL1
{}
", parseOptions: TestOptions.Regular.WithFeature("staticNullChecking", "true"));
c.VerifyDiagnostics(
);
}
[Fact]
public void Lambda_03()
{
CSharpCompilation c = CreateCompilationWithMscorlib(@"
class C
{
static void Main()
{
}
CL1? M1() { return null; }
void Test1()
{
System.Func<CL1> x1 = () => M1();
}
void Test2()
{
System.Func<CL1> x2 = delegate { return M1(); };
}
delegate CL1 D1();
void Test3()
{
D1 x3 = () => M1();
}
void Test4()
{
D1 x4 = delegate { return M1(); };
}
}
class CL1
{}
", parseOptions: TestOptions.Regular.WithFeature("staticNullChecking", "true"));
c.VerifyDiagnostics(
// (12,37): warning CS8203: Possible null reference return.
// System.Func<CL1> x1 = () => M1();
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "M1()").WithLocation(12, 37),
// (17,49): warning CS8203: Possible null reference return.
// System.Func<CL1> x2 = delegate { return M1(); };
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "M1()").WithLocation(17, 49),
// (24,23): warning CS8203: Possible null reference return.
// D1 x3 = () => M1();
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "M1()").WithLocation(24, 23),
// (29,35): warning CS8203: Possible null reference return.
// D1 x4 = delegate { return M1(); };
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "M1()").WithLocation(29, 35)
);
}
[Fact]
public void Lambda_04()
{
CSharpCompilation c = CreateCompilationWithMscorlib(@"
class C
{
static void Main()
{
}
CL1? M1() { return null; }
void Test1()
{
System.Action<CL1> x1 = (p1) => p1 = M1();
}
delegate void D1(CL1 p);
void Test3()
{
D1 x3 = (p3) => p3 = M1();
}
}
class CL1
{}
", parseOptions: TestOptions.Regular.WithFeature("staticNullChecking", "true"));
c.VerifyDiagnostics(
// (12,46): warning CS8201: Possible null reference assignment.
// System.Action<CL1> x1 = (p1) => p1 = M1();
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "M1()").WithLocation(12, 46),
// (19,30): warning CS8201: Possible null reference assignment.
// D1 x3 = (p3) => p3 = M1();
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "M1()").WithLocation(19, 30)
);
}
[Fact]
public void Lambda_05()
{
CSharpCompilation c = CreateCompilationWithMscorlib(@"
class C
{
static void Main()
{
}
CL1? M1() { return null; }
delegate CL1 D1();
delegate CL1? D2();
void M2(int x, D1 y) {}
void M2(long x, D2 y) {}
void M3(long x, D2 y) {}
void M3(int x, D1 y) {}
void Test1(int x1)
{
M2(x1, () => M1());
}
void Test2(int x2)
{
M3(x2, () => M1());
}
void Test3(int x3)
{
M2(x3, delegate { return M1(); });
}
void Test4(int x4)
{
M3(x4, delegate { return M1(); });
}
}
class CL1
{}
", parseOptions: TestOptions.Regular.WithFeature("staticNullChecking", "true"));
c.VerifyDiagnostics(
// (20,22): warning CS8203: Possible null reference return.
// M2(x1, () => M1());
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "M1()").WithLocation(20, 22),
// (25,22): warning CS8203: Possible null reference return.
// M3(x2, () => M1());
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "M1()").WithLocation(25, 22),
// (30,34): warning CS8203: Possible null reference return.
// M2(x3, delegate { return M1(); });
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "M1()").WithLocation(30, 34),
// (35,34): warning CS8203: Possible null reference return.
// M3(x4, delegate { return M1(); });
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "M1()").WithLocation(35, 34)
);
}
[Fact]
public void Lambda_06()
{
CSharpCompilation c = CreateCompilationWithMscorlib(@"
class C
{
static void Main()
{
}
CL1? M1() { return null; }
delegate CL1 D1();
delegate CL1? D2();
void M2(int x, D2 y) {}
void M2(long x, D1 y) {}
void M3(long x, D1 y) {}
void M3(int x, D2 y) {}
void Test1(int x1)
{
M2(x1, () => M1());
}
void Test2(int x2)
{
M3(x2, () => M1());
}
void Test3(int x3)
{
M2(x3, delegate { return M1(); });
}
void Test4(int x4)
{
M3(x4, delegate { return M1(); });
}
}
class CL1
{}
", parseOptions: TestOptions.Regular.WithFeature("staticNullChecking", "true"));
c.VerifyDiagnostics(
);
}
[Fact]
public void Lambda_07()
{
CSharpCompilation c = CreateCompilationWithMscorlib(@"
class C
{
static void Main()
{
}
CL1? M1() { return null; }
delegate T D<T>();
void M2(int x, D<CL1> y) {}
void M2<T>(int x, D<T> y) {}
void M3<T>(int x, D<T> y) {}
void M3(int x, D<CL1> y) {}
void Test1(int x1)
{
M2(x1, () => M1());
}
void Test2(int x2)
{
M3(x2, () => M1());
}
void Test3(int x3)
{
M2(x3, delegate { return M1(); });
}
void Test4(int x4)
{
M3(x4, delegate { return M1(); });
}
}
class CL1
{}
", parseOptions: TestOptions.Regular.WithFeature("staticNullChecking", "true"));
c.VerifyDiagnostics(
// (19,22): warning CS8203: Possible null reference return.
// M2(x1, () => M1());
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "M1()").WithLocation(19, 22),
// (24,22): warning CS8203: Possible null reference return.
// M3(x2, () => M1());
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "M1()").WithLocation(24, 22),
// (29,34): warning CS8203: Possible null reference return.
// M2(x3, delegate { return M1(); });
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "M1()").WithLocation(29, 34),
// (34,34): warning CS8203: Possible null reference return.
// M3(x4, delegate { return M1(); });
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "M1()").WithLocation(34, 34)
);
}
[Fact]
public void Lambda_08()
{
CSharpCompilation c = CreateCompilationWithMscorlib(@"
class C
{
static void Main()
{
}
CL1? M1() { return null; }
delegate T D<T>();
void M2(int x, D<CL1?> y) {}
void M2<T>(int x, D<T> y) {}
void M3<T>(int x, D<T> y) {}
void M3(int x, D<CL1?> y) {}
void Test1(int x1)
{
M2(x1, () => M1());
}
void Test2(int x2)
{
M3(x2, () => M1());
}
void Test3(int x3)
{
M2(x3, delegate { return M1(); });
}
void Test4(int x4)
{
M3(x4, delegate { return M1(); });
}
}
class CL1
{}
", parseOptions: TestOptions.Regular.WithFeature("staticNullChecking", "true"));
c.VerifyDiagnostics(
);
}
[Fact]
public void Lambda_09()
{
CSharpCompilation c = CreateCompilationWithMscorlib(@"
class C
{
static void Main()
{
}
CL1? M1() { return null; }
delegate T1 D<T1, T2>(T2 y);
void M2(int x, D<CL1, CL1> y) {}
void M2<T>(int x, D<T, CL1> y) {}
void M3<T>(int x, D<T, CL1> y) {}
void M3(int x, D<CL1, CL1> y) {}
void Test1(int x1)
{
M2(x1, (y1) =>
{
y1 = M1();
return y1;
});
}
void Test2(int x2)
{
M3(x2, (y2) =>
{
y2 = M1();
return y2;
});
}
}
class CL1
{}
", parseOptions: TestOptions.Regular.WithFeature("staticNullChecking", "true"));
c.VerifyDiagnostics(
// (21,26): warning CS8201: Possible null reference assignment.
// y1 = M1();
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "M1()").WithLocation(21, 26),
// (30,26): warning CS8201: Possible null reference assignment.
// y2 = M1();
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "M1()").WithLocation(30, 26)
);
}
[Fact]
public void Lambda_10()
{
CSharpCompilation c = CreateCompilationWithMscorlib(@"
class C
{
static void Main()
{
}
CL1? M1() { return null; }
delegate T1 D<T1, T2>(T2 y);
void M2(int x, D<CL1, CL1?> y) {}
void M2<T>(int x, D<T, CL1> y) {}
void M3<T>(int x, D<T, CL1> y) {}
void M3(int x, D<CL1, CL1?> y) {}
void Test1(int x1)
{
M2(x1, (y1) =>
{
y1 = M1();
return y1;
});
}
void Test2(int x2)
{
M3(x2, (y2) =>
{
y2 = M1();
return y2;
});
}
}
class CL1
{}
", parseOptions: TestOptions.Regular.WithFeature("staticNullChecking", "true"));
c.VerifyDiagnostics(
// (22,28): warning CS8203: Possible null reference return.
// return y1;
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "y1").WithLocation(22, 28),
// (31,28): warning CS8203: Possible null reference return.
// return y2;
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "y2").WithLocation(31, 28)
);
}
[Fact]
public void Lambda_11()
{
CSharpCompilation c = CreateCompilationWithMscorlib(@"
class C
{
static void Main()
{
}
CL1? M1() { return null; }
void Test1()
{
System.Action<CL1> x1 = (CL1 p1) => p1 = M1();
}
void Test2()
{
System.Action<CL1> x2 = delegate (CL1 p2) { p2 = M1(); };
}
delegate void D1(CL1 p);
void Test3()
{
D1 x3 = (CL1 p3) => p3 = M1();
}
void Test4()
{
D1 x4 = delegate (CL1 p4) { p4 = M1(); };
}
}
class CL1
{}
", parseOptions: TestOptions.Regular.WithFeature("staticNullChecking", "true"));
c.VerifyDiagnostics(
// (12,50): warning CS8201: Possible null reference assignment.
// System.Action<CL1> x1 = (CL1 p1) => p1 = M1();
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "M1()").WithLocation(12, 50),
// (17,58): warning CS8201: Possible null reference assignment.
// System.Action<CL1> x2 = delegate (CL1 p2) { p2 = M1(); };
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "M1()").WithLocation(17, 58),
// (24,34): warning CS8201: Possible null reference assignment.
// D1 x3 = (CL1 p3) => p3 = M1();
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "M1()").WithLocation(24, 34),
// (29,42): warning CS8201: Possible null reference assignment.
// D1 x4 = delegate (CL1 p4) { p4 = M1(); };
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "M1()").WithLocation(29, 42)
);
}
[Fact]
public void Lambda_12()
{
CSharpCompilation c = CreateCompilationWithMscorlib(@"
class C
{
static void Main()
{
}
CL1? M1() { return null; }
void Test1()
{
System.Action<CL1?> x1 = (CL1 p1) => p1 = M1();
}
void Test2()
{
System.Action<CL1?> x2 = delegate (CL1 p2) { p2 = M1(); };
}
delegate void D1(CL1? p);
void Test3()
{
D1 x3 = (CL1 p3) => p3 = M1();
}
void Test4()
{
D1 x4 = delegate (CL1 p4) { p4 = M1(); };
}
}
class CL1
{}
", parseOptions: TestOptions.Regular.WithFeature("staticNullChecking", "true"));
c.VerifyDiagnostics(
// (12,51): warning CS8201: Possible null reference assignment.
// System.Action<CL1?> x1 = (CL1 p1) => p1 = M1();
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "M1()").WithLocation(12, 51),
// (17,59): warning CS8201: Possible null reference assignment.
// System.Action<CL1?> x2 = delegate (CL1 p2) { p2 = M1(); };
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "M1()").WithLocation(17, 59),
// (24,34): warning CS8201: Possible null reference assignment.
// D1 x3 = (CL1 p3) => p3 = M1();
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "M1()").WithLocation(24, 34),
// (29,42): warning CS8201: Possible null reference assignment.
// D1 x4 = delegate (CL1 p4) { p4 = M1(); };
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "M1()").WithLocation(29, 42)
);
}
[Fact]
public void Lambda_13()
{
CSharpCompilation c = CreateCompilationWithMscorlib(@"
class C
{
static void Main()
{
}
CL1? M1() { return null; }
void Test1()
{
System.Action<CL1> x1 = (CL1? p1) => p1 = M1();
}
void Test2()
{
System.Action<CL1> x2 = delegate (CL1? p2) { p2 = M1(); };
}
delegate void D1(CL1 p);
void Test3()
{
D1 x3 = (CL1? p3) => p3 = M1();
}
void Test4()
{
D1 x4 = delegate (CL1? p4) { p4 = M1(); };
}
}
class CL1
{}
", parseOptions: TestOptions.Regular.WithFeature("staticNullChecking", "true"));
c.VerifyDiagnostics(
);
}
[Fact]
public void Lambda_14()
{
CSharpCompilation c = CreateCompilationWithMscorlib(@"
class C
{
static void Main()
{
}
CL1? M1() { return null; }
void Test1()
{
System.Action<CL1?> x1 = (CL1? p1) => p1 = M1();
}
void Test2()
{
System.Action<CL1?> x2 = delegate (CL1? p2) { p2 = M1(); };
}
delegate void D1(CL1? p);
void Test3()
{
D1 x3 = (CL1? p3) => p3 = M1();
}
void Test4()
{
D1 x4 = delegate (CL1? p4) { p4 = M1(); };
}
}
class CL1
{}
", parseOptions: TestOptions.Regular.WithFeature("staticNullChecking", "true"));
c.VerifyDiagnostics(
);
}
[Fact(Skip = "TODO")]
public void Lambda_15()
{
CSharpCompilation notAnnotated = CreateCompilationWithMscorlib45(@"
public class CL0
{
public static void M1(System.Func<CL1<CL0>, CL0> x) {}
}
public class CL1<T>
{
public T F1;
public CL1()
{
F1 = default(T);
}
}
", options: TestOptions.DebugDll);
CSharpCompilation c = CreateCompilationWithMscorlib45(@"
class C
{
static void Main() {}
static void Test1()
{
CL0.M1( p1 =>
{
p1.F1 = null;
p1 = null;
return null; // 1
});
}
static void Test2()
{
System.Func<CL1<CL0>, CL0> l2 = p2 =>
{
p2.F1 = null;
p2 = null;
return null; // 2
};
}
}
", parseOptions: TestOptions.Regular.WithFeature("staticNullChecking", "true"), references: new[] { notAnnotated.EmitToImageReference() });
c.VerifyDiagnostics(
// (20,29): warning CS8201: Possible null reference assignment.
// p2.F1 = null;
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "null").WithLocation(20, 29),
// (21,26): warning CS8201: Possible null reference assignment.
// p2 = null;
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "null").WithLocation(21, 26),
// (22,28): warning CS8203: Possible null reference return.
// return null; // 2
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "null").WithLocation(22, 28)
);
}
[Fact(Skip = "TODO")]
public void Test2()
{
......
......@@ -240,41 +240,41 @@ class X {
string s = f7Type.ToTestDisplayString();
Assert.False(f1Type.Equals(f2Type));
Assert.True(f1Type.Equals(f2Type, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: true));
Assert.True(f2Type.Equals(f1Type, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: true));
Assert.True(f1Type.Equals(f1Type, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: true));
Assert.True(f2Type.Equals(f2Type, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: true));
Assert.True(f1Type.Equals(f2Type, TypeSymbolEqualityOptions.SameType));
Assert.True(f2Type.Equals(f1Type, TypeSymbolEqualityOptions.SameType));
Assert.True(f1Type.Equals(f1Type, TypeSymbolEqualityOptions.SameType));
Assert.True(f2Type.Equals(f2Type, TypeSymbolEqualityOptions.SameType));
Assert.False(f3Type.Equals(f4Type));
Assert.True(f3Type.Equals(f4Type, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: true));
Assert.True(f4Type.Equals(f3Type, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: true));
Assert.False(f4Type.Equals(f5Type, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: true));
Assert.False(f5Type.Equals(f4Type, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: true));
Assert.True(f3Type.Equals(f4Type, TypeSymbolEqualityOptions.SameType));
Assert.True(f4Type.Equals(f3Type, TypeSymbolEqualityOptions.SameType));
Assert.False(f4Type.Equals(f5Type, TypeSymbolEqualityOptions.SameType));
Assert.False(f5Type.Equals(f4Type, TypeSymbolEqualityOptions.SameType));
Assert.False(f6Type.Equals(f7Type));
Assert.False(f6Type.Equals(f8Type));
Assert.False(f7Type.Equals(f8Type));
Assert.True(f6Type.Equals(f7Type, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: true));
Assert.True(f7Type.Equals(f6Type, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: true));
Assert.True(f6Type.Equals(f6Type, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: true));
Assert.True(f7Type.Equals(f7Type, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: true));
Assert.True(f8Type.Equals(f7Type, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: true));
Assert.True(f7Type.Equals(f8Type, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: true));
Assert.True(f8Type.Equals(f8Type, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: true));
Assert.True(f7Type.Equals(f7Type, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: true));
Assert.True(f8Type.Equals(f6Type, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: true));
Assert.True(f6Type.Equals(f8Type, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: true));
Assert.True(f8Type.Equals(f8Type, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: true));
Assert.True(f6Type.Equals(f6Type, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: true));
Assert.False(f9Type.Equals(f10Type, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: true));
Assert.False(f10Type.Equals(f9Type, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: true));
Assert.True(f6Type.Equals(f7Type, TypeSymbolEqualityOptions.SameType));
Assert.True(f7Type.Equals(f6Type, TypeSymbolEqualityOptions.SameType));
Assert.True(f6Type.Equals(f6Type, TypeSymbolEqualityOptions.SameType));
Assert.True(f7Type.Equals(f7Type, TypeSymbolEqualityOptions.SameType));
Assert.True(f8Type.Equals(f7Type, TypeSymbolEqualityOptions.SameType));
Assert.True(f7Type.Equals(f8Type, TypeSymbolEqualityOptions.SameType));
Assert.True(f8Type.Equals(f8Type, TypeSymbolEqualityOptions.SameType));
Assert.True(f7Type.Equals(f7Type, TypeSymbolEqualityOptions.SameType));
Assert.True(f8Type.Equals(f6Type, TypeSymbolEqualityOptions.SameType));
Assert.True(f6Type.Equals(f8Type, TypeSymbolEqualityOptions.SameType));
Assert.True(f8Type.Equals(f8Type, TypeSymbolEqualityOptions.SameType));
Assert.True(f6Type.Equals(f6Type, TypeSymbolEqualityOptions.SameType));
Assert.False(f9Type.Equals(f10Type, TypeSymbolEqualityOptions.SameType));
Assert.False(f10Type.Equals(f9Type, TypeSymbolEqualityOptions.SameType));
Assert.False(g1Type.Equals(g2Type));
Assert.True(g1Type.Equals(g2Type, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: true));
Assert.True(g2Type.Equals(g1Type, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: true));
Assert.True(g1Type.Equals(g1Type, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: true));
Assert.True(g2Type.Equals(g2Type, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: true));
Assert.True(g1Type.Equals(g2Type, TypeSymbolEqualityOptions.SameType));
Assert.True(g2Type.Equals(g1Type, TypeSymbolEqualityOptions.SameType));
Assert.True(g1Type.Equals(g1Type, TypeSymbolEqualityOptions.SameType));
Assert.True(g2Type.Equals(g2Type, TypeSymbolEqualityOptions.SameType));
}
/// <summary>
......
......@@ -188,7 +188,7 @@ public override void Test(int x)
var withoutModifiers = withModifiers.OriginalDefinition.Construct(withModifiers.TypeArguments.SelectAsArray(TypeMap.AsTypeSymbol));
Assert.True(HasTypeArgumentsCustomModifiers(withModifiers));
Assert.False(HasTypeArgumentsCustomModifiers(withoutModifiers));
Assert.True(withoutModifiers.Equals(withModifiers, ignoreCustomModifiersAndArraySizesAndLowerBounds:true));
Assert.True(withoutModifiers.Equals(withModifiers, TypeSymbolEqualityOptions.IgnoreCustomModifiersAndArraySizesAndLowerBounds));
Assert.NotEqual(withoutModifiers, withModifiers);
CompileAndVerify(compilation, expectedOutput: "Overriden");
......@@ -805,11 +805,11 @@ static void Main()
Assert.True(HasTypeArgumentsCustomModifiers(base1));
Assert.True(HasTypeArgumentsCustomModifiers(base2));
Assert.True(base1.Equals(base2, ignoreCustomModifiersAndArraySizesAndLowerBounds:true));
Assert.True(base1.Equals(base2, TypeSymbolEqualityOptions.IgnoreCustomModifiersAndArraySizesAndLowerBounds));
Assert.NotEqual(base1, base2);
Assert.True(HasTypeArgumentsCustomModifiers(base3));
Assert.True(base1.Equals(base3, ignoreCustomModifiersAndArraySizesAndLowerBounds: true));
Assert.True(base1.Equals(base3, TypeSymbolEqualityOptions.IgnoreCustomModifiersAndArraySizesAndLowerBounds));
Assert.Equal(base1, base3);
Assert.NotSame(base1, base3);
}
......
......@@ -159,8 +159,8 @@ private BoundExpression RewriteParameter(CSharpSyntaxNode syntax, ParameterSymbo
var result = variable.ToBoundExpression(syntax);
Debug.Assert(node.Kind == BoundKind.BaseReference
? result.Type.BaseType.Equals(node.Type, ignoreDynamic: true)
: result.Type.Equals(node.Type, ignoreDynamic: true));
? result.Type.BaseType.Equals(node.Type, TypeSymbolEqualityOptions.IgnoreDynamic)
: result.Type.Equals(node.Type, TypeSymbolEqualityOptions.IgnoreDynamic));
return result;
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册