提交 8a6b522a 编写于 作者: Y Yair Halberstadt 提交者: AlekseyTs

Properly treat ambiguous explicit interface implementations (#34584)

* Properly treat ambiguous explicit interface implementations involving nullable reference types, including maintaining backwards compatability with pre -NRT code.

This covers ["step 1"] of https://github.com/dotnet/csharplang/issues/2378#issuecomment-479634969. 

Fixes #29846.
Fixes #34508.
Fixes #34583.
上级 e922628a
......@@ -46,19 +46,6 @@ internal class MemberSignatureComparer : IEqualityComparer<Symbol>
considerCallingConvention: true,
typeComparison: TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes | TypeCompareKind.IgnoreDynamicAndTupleNames);
/// <summary>
/// Same as <see cref="ExplicitImplementationComparer"/> except that it specially treats nullable types.
/// </summary>
public static readonly MemberSignatureComparer ExplicitImplementationLookupComparer = new MemberSignatureComparer(
considerName: false,
considerExplicitlyImplementedInterfaces: false,
considerReturnType: true,
considerTypeConstraints: false,
considerRefKindDifferences: true,
considerCallingConvention: true,
typeComparison: TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes | TypeCompareKind.IgnoreDynamicAndTupleNames,
useSpecialHandlingForNullableTypes: true);
/// <summary>
/// This instance is used when trying to determine if one member implicitly implements another,
/// according to the C# definition.
......@@ -137,19 +124,6 @@ internal class MemberSignatureComparer : IEqualityComparer<Symbol>
considerRefKindDifferences: true,
typeComparison: TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes | TypeCompareKind.IgnoreDynamicAndTupleNames);
/// <summary>
/// Same as <see cref="CSharpOverrideComparer"/> except that it specially treats nullable types.
/// </summary>
public static readonly MemberSignatureComparer CSharpNullableOverrideComparer = new MemberSignatureComparer(
considerName: true,
considerExplicitlyImplementedInterfaces: false,
considerReturnType: false,
considerTypeConstraints: false,
considerCallingConvention: false, //ignore static-ness
considerRefKindDifferences: true,
typeComparison: TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes | TypeCompareKind.IgnoreDynamicAndTupleNames,
useSpecialHandlingForNullableTypes: true);
/// <summary>
/// This instance checks whether two signatures match including tuples names, in both return type and parameters.
/// It is used to detect tuple-name-only differences.
......@@ -204,19 +178,6 @@ internal class MemberSignatureComparer : IEqualityComparer<Symbol>
considerRefKindDifferences: true,
typeComparison: TypeCompareKind.IgnoreDynamicAndTupleNames | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes);
/// <summary>
/// Same as <see cref="CSharpCustomModifierOverrideComparer"/> except that it specially treats nullable types.
/// </summary>
public static readonly MemberSignatureComparer CSharpCustomModifierNullableOverrideComparer = new MemberSignatureComparer(
considerName: true,
considerExplicitlyImplementedInterfaces: false,
considerReturnType: true,
considerTypeConstraints: false,
considerCallingConvention: false, //ignore static-ness
considerRefKindDifferences: true,
typeComparison: TypeCompareKind.IgnoreDynamicAndTupleNames | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes,
useSpecialHandlingForNullableTypes: true);
/// <summary>
/// If this returns false, then the real override comparer (whichever one is appropriate for the scenario)
/// will also return false.
......@@ -332,8 +293,6 @@ internal class MemberSignatureComparer : IEqualityComparer<Symbol>
// Equality options for parameter types and return types (if return is considered).
private readonly TypeCompareKind _typeComparison;
private readonly bool _useSpecialHandlingForNullableTypes;
private MemberSignatureComparer(
bool considerName,
bool considerExplicitlyImplementedInterfaces,
......@@ -341,8 +300,7 @@ internal class MemberSignatureComparer : IEqualityComparer<Symbol>
bool considerTypeConstraints,
bool considerCallingConvention,
bool considerRefKindDifferences,
TypeCompareKind typeComparison = TypeCompareKind.IgnoreDynamic,
bool useSpecialHandlingForNullableTypes = false)
TypeCompareKind typeComparison = TypeCompareKind.IgnoreDynamic)
{
Debug.Assert(!considerExplicitlyImplementedInterfaces || considerName, "Doesn't make sense to consider interfaces separately from name.");
......@@ -353,7 +311,6 @@ internal class MemberSignatureComparer : IEqualityComparer<Symbol>
_considerCallingConvention = considerCallingConvention;
_considerRefKindDifferences = considerRefKindDifferences;
_typeComparison = typeComparison;
_useSpecialHandlingForNullableTypes = useSpecialHandlingForNullableTypes;
}
#region IEqualityComparer<Symbol> Members
......@@ -396,38 +353,8 @@ public bool Equals(Symbol member1, Symbol member2)
return false;
}
TypeMap typeMap1;
TypeMap typeMap2;
if (arity > 0 && _useSpecialHandlingForNullableTypes)
{
// We need this special handling in order to avoid forcing resolution of nullable types
// in signature of an overriding member while we are looking for a matching overridden member.
// Doing the resolution in the original signature can send us into an infinite cycle because
// constraints must be inherited from the member we are looking for.
// It is important to ensure that the fact whether an indexed type parameter we are about to use
// is a reference type is inherited from the corresponding type parameter of the possibly overridden
// member (which is member2 when _useSpecialHandlingForNullableTypes is true). This will ensure
// proper resolution for nullable types in substituted signature of member1, ensuring proper
// comparison of types across both members.
ArrayBuilder<TypeParameterSymbol> builder = ArrayBuilder<TypeParameterSymbol>.GetInstance(arity);
var typeParameters2 = member2.GetMemberTypeParameters();
for (int i = arity - 1; i >= 0; i--)
{
builder.Add(IndexedTypeParameterSymbolForOverriding.GetTypeParameter(i, typeParameters2[i].IsValueType));
}
var indexed = builder.ToImmutableAndFree();
typeMap1 = new TypeMap(member1.GetMemberTypeParameters(), indexed, true);
typeMap2 = new TypeMap(typeParameters2, indexed, true);
}
else
{
typeMap1 = GetTypeMap(member1);
typeMap2 = GetTypeMap(member2);
}
TypeMap typeMap1 = GetTypeMap(member1);
TypeMap typeMap2 = GetTypeMap(member2);
if (_considerReturnType && !HaveSameReturnTypes(member1, typeMap1, member2, typeMap2, _typeComparison))
{
......
......@@ -490,10 +490,11 @@ private static OverriddenOrHiddenMembersResult MakeInterfaceOverriddenOrHiddenMe
int minCustomModifierCount = int.MaxValue;
IEqualityComparer<Symbol> exactMatchComparer = memberIsFromSomeCompilation
? (member.IsOverride ? MemberSignatureComparer.CSharpCustomModifierNullableOverrideComparer : MemberSignatureComparer.CSharpCustomModifierOverrideComparer)
? MemberSignatureComparer.CSharpCustomModifierOverrideComparer
: MemberSignatureComparer.RuntimePlusRefOutSignatureComparer;
IEqualityComparer<Symbol> fallbackComparer = memberIsFromSomeCompilation
? (member.IsOverride ? MemberSignatureComparer.CSharpNullableOverrideComparer : MemberSignatureComparer.CSharpOverrideComparer)
? MemberSignatureComparer.CSharpOverrideComparer
: MemberSignatureComparer.RuntimeSignatureComparer;
SymbolKind memberKind = member.Kind;
......
......@@ -242,7 +242,7 @@ public static string GetMemberNameWithoutInterfaceName(string fullName)
continue;
}
if (MemberSignatureComparer.ExplicitImplementationLookupComparer.Equals(implementingMember, interfaceMember))
if (MemberSignatureComparer.ExplicitImplementationComparer.Equals(implementingMember, interfaceMember))
{
foundMatchingMember = true;
// Cannot implement accessor directly unless
......
// 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;
using System.Collections.Immutable;
using System.Threading;
using Roslyn.Utilities;
using System.Diagnostics;
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
/// <summary>
/// Indexed type parameters are used in place of type parameters for method signatures.
///
/// They don't have a containing symbol or locations.
///
/// They do not have constraints (except reference type constraint), variance, or attributes.
/// </summary>
internal sealed class IndexedTypeParameterSymbolForOverriding : TypeParameterSymbol
{
private static TypeParameterSymbol[] s_parameterPoolHasValueTypeConstraint = Array.Empty<TypeParameterSymbol>();
private static TypeParameterSymbol[] s_parameterPoolHasNoValueTypeConstraint = Array.Empty<TypeParameterSymbol>();
private readonly int _index;
private readonly bool _hasValueTypeConstraint;
private IndexedTypeParameterSymbolForOverriding(int index, bool hasValueTypeConstraint)
{
_index = index;
_hasValueTypeConstraint = hasValueTypeConstraint;
}
public override TypeParameterKind TypeParameterKind
{
get
{
return TypeParameterKind.Method;
}
}
internal static TypeParameterSymbol GetTypeParameter(int index, bool hasValueTypeConstraint)
{
TypeParameterSymbol result;
if (hasValueTypeConstraint)
{
result = GetTypeParameter(ref s_parameterPoolHasValueTypeConstraint, index, hasValueTypeConstraint);
}
else
{
result = GetTypeParameter(ref s_parameterPoolHasNoValueTypeConstraint, index, hasValueTypeConstraint);
}
Debug.Assert(result.HasValueTypeConstraint == hasValueTypeConstraint);
return result;
}
private static TypeParameterSymbol GetTypeParameter(ref TypeParameterSymbol[] parameterPool, int index, bool hasValueTypeConstraint)
{
if (index >= parameterPool.Length)
{
GrowPool(ref parameterPool, index + 1, hasValueTypeConstraint);
}
return parameterPool[index];
}
private static void GrowPool(ref TypeParameterSymbol[] parameterPool, int count, bool hasValueTypeConstraint)
{
var initialPool = parameterPool;
while (count > initialPool.Length)
{
var newPoolSize = ((count + 0x0F) & ~0xF); // grow in increments of 16
var newPool = new TypeParameterSymbol[newPoolSize];
Array.Copy(initialPool, newPool, initialPool.Length);
for (int i = initialPool.Length; i < newPool.Length; i++)
{
newPool[i] = new IndexedTypeParameterSymbolForOverriding(i, hasValueTypeConstraint);
}
Interlocked.CompareExchange(ref parameterPool, newPool, initialPool);
// repeat if race condition occurred and someone else resized the pool before us
// and the new pool is still too small
initialPool = parameterPool;
}
}
public override int Ordinal
{
get { return _index; }
}
// These object are unique (per index).
internal override bool Equals(TypeSymbol t2, TypeCompareKind comparison)
{
return ReferenceEquals(this, t2);
}
public override int GetHashCode()
{
return _index;
}
public override VarianceKind Variance
{
get { return VarianceKind.None; }
}
public override bool HasValueTypeConstraint
{
get { return _hasValueTypeConstraint; }
}
public override bool HasReferenceTypeConstraint
{
get { throw ExceptionUtilities.Unreachable; }
}
internal override bool? ReferenceTypeConstraintIsNullable
{
get { throw ExceptionUtilities.Unreachable; }
}
public override bool HasUnmanagedTypeConstraint
{
get { throw ExceptionUtilities.Unreachable; }
}
public override bool HasConstructorConstraint
{
get { return false; }
}
public override Symbol ContainingSymbol
{
get
{
return null;
}
}
public override ImmutableArray<Location> Locations
{
get
{
return ImmutableArray<Location>.Empty;
}
}
public override ImmutableArray<SyntaxReference> DeclaringSyntaxReferences
{
get
{
return ImmutableArray<SyntaxReference>.Empty;
}
}
internal override void EnsureAllConstraintsAreResolved(bool early)
{
}
internal override ImmutableArray<TypeWithAnnotations> GetConstraintTypes(ConsList<TypeParameterSymbol> inProgress, bool early)
{
return ImmutableArray<TypeWithAnnotations>.Empty;
}
internal override ImmutableArray<NamedTypeSymbol> GetInterfaces(ConsList<TypeParameterSymbol> inProgress)
{
return ImmutableArray<NamedTypeSymbol>.Empty;
}
internal override NamedTypeSymbol GetEffectiveBaseClass(ConsList<TypeParameterSymbol> inProgress)
{
return null;
}
internal override TypeSymbol GetDeducedBaseType(ConsList<TypeParameterSymbol> inProgress)
{
return null;
}
public override bool IsImplicitlyDeclared
{
get { return true; }
}
}
}
......@@ -215,6 +215,16 @@ private void MethodChecks(MethodDeclarationSyntax syntax, Binder withTypeParamsB
diagnostics.Add(ErrorCode.WRN_FinalizeMethod, location);
}
// Any nullable typeParameter declared by the method in the signature of an override or explicit interface implementation is considered a Nullable<T>
if (syntax.ExplicitInterfaceSpecifier != null || IsOverride)
{
foreach (var param in _lazyParameters)
{
forceMethodTypeParametersAsNullable(param.TypeWithAnnotations);
}
forceMethodTypeParametersAsNullable(_lazyReturnType);
}
// errors relevant for extension methods
if (IsExtensionMethod)
{
......@@ -375,6 +385,20 @@ private void MethodChecks(MethodDeclarationSyntax syntax, Binder withTypeParamsB
}
CheckModifiers(_hasAnyBody, location, diagnostics);
return;
void forceMethodTypeParametersAsNullable(TypeWithAnnotations type)
{
type.VisitType<object>(null, (type, unused1, unused2) =>
{
if ((type.DefaultType as TypeParameterSymbol)?.DeclaringMethod == (object)this)
{
type.TryForceResolveAsNullableValueType();
}
return false;
}, typePredicateOpt: null, arg: null, canDigThroughNullable: false, useDefaultType: true);
}
}
// This is also used for async lambdas. Probably not the best place to locate this method, but where else could it go?
......
......@@ -590,15 +590,19 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref HashS
/// traversal stops and that type is returned from this method. Otherwise if traversal
/// completes without the predicate returning true for any type, this method returns null.
/// </summary>
/// <param name="useDefaultType">If true, use <see cref="TypeWithAnnotations.DefaultType"/>
/// instead of <see cref="TypeWithAnnotations.Type"/> to avoid early resolution of nullable types</param>
public static TypeSymbol VisitType<T>(
this TypeWithAnnotations typeWithAnnotationsOpt,
TypeSymbol typeOpt,
Func<TypeWithAnnotations, T, bool, bool> typeWithAnnotationsPredicateOpt,
Func<TypeSymbol, T, bool, bool> typePredicateOpt,
T arg,
bool canDigThroughNullable = false)
bool canDigThroughNullable = false,
bool useDefaultType = false)
{
Debug.Assert(typeWithAnnotationsOpt.HasType == (typeOpt is null));
Debug.Assert(canDigThroughNullable == false || useDefaultType == false, "digging through nullable will cause early resolution of nullable types");
// In order to handle extremely "deep" types like "int[][][][][][][][][]...[]"
// or int*****************...* we implement manual tail recursion rather than
......@@ -606,7 +610,7 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref HashS
while (true)
{
TypeSymbol current = typeOpt ?? typeWithAnnotationsOpt.Type;
TypeSymbol current = typeOpt ?? (useDefaultType ? typeWithAnnotationsOpt.DefaultType : typeWithAnnotationsOpt.Type);
bool isNestedNamedType = false;
// Visit containing types from outer-most to inner-most.
......@@ -622,7 +626,7 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref HashS
if ((object)containingType != null)
{
isNestedNamedType = true;
var result = VisitType(default, containingType, typeWithAnnotationsPredicateOpt, typePredicateOpt, arg, canDigThroughNullable);
var result = VisitType(default, containingType, typeWithAnnotationsPredicateOpt, typePredicateOpt, arg, canDigThroughNullable, useDefaultType);
if ((object)result != null)
{
return result;
......@@ -681,7 +685,8 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref HashS
typeWithAnnotationsPredicateOpt,
typePredicateOpt,
arg,
canDigThroughNullable);
canDigThroughNullable,
useDefaultType);
if ((object)result != null)
{
return result;
......
......@@ -56,7 +56,7 @@ internal bool InterlockedInitialize(TypeWithAnnotations type)
}
_nullableAnnotation = type.NullableAnnotation;
Interlocked.CompareExchange(ref _extensions, type._extensions, null);
return (object)Interlocked.CompareExchange(ref _defaultType, type._defaultType, null) == null;
return (object)Interlocked.CompareExchange(ref _defaultType, type.DefaultType, null) == null;
}
/// <summary>
......@@ -75,7 +75,7 @@ internal TypeWithAnnotations ToType()
/// <summary>
/// The underlying type, unless overridden by _extensions.
/// </summary>
private readonly TypeSymbol _defaultType;
internal readonly TypeSymbol DefaultType;
/// <summary>
/// Additional data or behavior. Such cases should be
......@@ -90,7 +90,7 @@ private TypeWithAnnotations(TypeSymbol defaultType, NullableAnnotation nullableA
Debug.Assert(defaultType?.IsNullableType() != true || (nullableAnnotation != NullableAnnotation.Oblivious && nullableAnnotation != NullableAnnotation.NotAnnotated));
Debug.Assert(extensions != null);
_defaultType = defaultType;
DefaultType = defaultType;
NullableAnnotation = nullableAnnotation;
_extensions = extensions;
}
......@@ -185,8 +185,7 @@ internal bool CanBeAssignedNull
private static bool IsIndexedTypeParameter(TypeSymbol typeSymbol)
{
return typeSymbol is IndexedTypeParameterSymbol ||
typeSymbol is IndexedTypeParameterSymbolForOverriding;
return typeSymbol is IndexedTypeParameterSymbol;
}
private static TypeWithAnnotations CreateNonLazyType(TypeSymbol typeSymbol, NullableAnnotation nullableAnnotation, ImmutableArray<CustomModifier> customModifiers)
......@@ -196,18 +195,18 @@ private static TypeWithAnnotations CreateNonLazyType(TypeSymbol typeSymbol, Null
private static TypeWithAnnotations CreateLazyNullableType(CSharpCompilation compilation, TypeWithAnnotations underlying)
{
return new TypeWithAnnotations(defaultType: underlying._defaultType, nullableAnnotation: NullableAnnotation.Annotated, Extensions.CreateLazy(compilation, underlying));
return new TypeWithAnnotations(defaultType: underlying.DefaultType, nullableAnnotation: NullableAnnotation.Annotated, Extensions.CreateLazy(compilation, underlying));
}
/// <summary>
/// True if the fields are unset. Appropriate when detecting if a lazily-initialized variable has been initialized.
/// </summary>
internal bool IsDefault => _defaultType is null && this.NullableAnnotation == 0 && (_extensions == null || _extensions == Extensions.Default);
internal bool IsDefault => DefaultType is null && this.NullableAnnotation == 0 && (_extensions == null || _extensions == Extensions.Default);
/// <summary>
/// True if the type is not null.
/// </summary>
internal bool HasType => !(_defaultType is null);
internal bool HasType => !(DefaultType is null);
public TypeWithAnnotations SetIsAnnotated(CSharpCompilation compilation)
{
......@@ -241,6 +240,15 @@ TypeWithAnnotations makeNullableT()
=> Create(compilation.GetSpecialType(SpecialType.System_Nullable_T).Construct(ImmutableArray.Create(typeSymbol)));
}
/// <summary>
/// If this is a lazy nullable type which has not yet been resolved, forces this to be resolved as a nullable value type, and return true.
/// Otherwise, return false.
/// </summary>
public bool TryForceResolveAsNullableValueType()
{
return _extensions.TryForceResolveAsNullableValueType();
}
private TypeWithAnnotations AsNullableReferenceType() => _extensions.AsNullableReferenceType(this);
public TypeWithAnnotations AsNotNullableReferenceType() => _extensions.AsNotNullableReferenceType(this);
......@@ -259,8 +267,8 @@ internal TypeWithAnnotations MergeNullability(TypeWithAnnotations other, Varianc
public TypeWithAnnotations WithModifiers(ImmutableArray<CustomModifier> customModifiers) =>
_extensions.WithModifiers(this, customModifiers);
public TypeSymbol Type => _extensions?.GetResolvedType(_defaultType);
public TypeSymbol NullableUnderlyingTypeOrSelf => _extensions.GetNullableUnderlyingTypeOrSelf(_defaultType);
public TypeSymbol Type => _extensions?.GetResolvedType(DefaultType);
public TypeSymbol NullableUnderlyingTypeOrSelf => _extensions.GetNullableUnderlyingTypeOrSelf(DefaultType);
/// <summary>
/// Is this System.Nullable`1 type, or its substitution.
......@@ -273,21 +281,21 @@ internal TypeWithAnnotations MergeNullability(TypeWithAnnotations other, Varianc
public ImmutableArray<CustomModifier> CustomModifiers => _extensions.CustomModifiers;
public TypeKind TypeKind => Type.TypeKind;
public SpecialType SpecialType => _extensions.GetSpecialType(_defaultType);
public SpecialType SpecialType => _extensions.GetSpecialType(DefaultType);
public Cci.PrimitiveTypeCode PrimitiveTypeCode => Type.PrimitiveTypeCode;
public bool IsVoid =>
_extensions.IsVoid(_defaultType);
_extensions.IsVoid(DefaultType);
public bool IsSZArray() =>
_extensions.IsSZArray(_defaultType);
_extensions.IsSZArray(DefaultType);
public bool IsStatic =>
_extensions.IsStatic(_defaultType);
_extensions.IsStatic(DefaultType);
public bool IsRestrictedType(bool ignoreSpanLikeTypes = false) =>
_extensions.IsRestrictedType(_defaultType, ignoreSpanLikeTypes);
_extensions.IsRestrictedType(DefaultType, ignoreSpanLikeTypes);
internal bool GetIsReferenceType(ConsList<TypeParameterSymbol> inProgress) =>
_extensions.GetIsReferenceType(_defaultType, inProgress);
_extensions.GetIsReferenceType(DefaultType, inProgress);
internal bool GetIsValueType(ConsList<TypeParameterSymbol> inProgress) =>
_extensions.GetIsValueType(_defaultType, inProgress);
_extensions.GetIsValueType(DefaultType, inProgress);
public string ToDisplayString(SymbolDisplayFormat format = null)
{
......@@ -512,14 +520,14 @@ private void ReportDiagnosticsIfObsoleteCore(Binder binder, SyntaxNode syntax, D
/// Extract type under assumption that there should be no custom modifiers or annotations.
/// The method asserts otherwise.
/// </summary>
public TypeSymbol AsTypeSymbolOnly() => _extensions.AsTypeSymbolOnly(_defaultType);
public TypeSymbol AsTypeSymbolOnly() => _extensions.AsTypeSymbolOnly(DefaultType);
/// <summary>
/// Is this the given type parameter?
/// </summary>
public bool Is(TypeParameterSymbol other)
{
return NullableAnnotation.IsOblivious() && ((object)_defaultType == other) &&
return NullableAnnotation.IsOblivious() && ((object)DefaultType == other) &&
CustomModifiers.IsEmpty;
}
......@@ -697,7 +705,7 @@ public override int GetHashCode()
// Field-wise ReferenceEquals.
internal bool IsSameAs(TypeWithAnnotations other)
{
return ReferenceEquals(_defaultType, other._defaultType) &&
return ReferenceEquals(DefaultType, other.DefaultType) &&
NullableAnnotation == other.NullableAnnotation &&
ReferenceEquals(_extensions, other._extensions);
}
......@@ -764,6 +772,8 @@ internal static Extensions CreateLazy(CSharpCompilation compilation, TypeWithAnn
internal abstract TypeWithAnnotations SubstituteType(TypeWithAnnotations type, AbstractTypeMap typeMap, bool withTupleUnification);
internal abstract TypeWithAnnotations TransformToTupleIfCompatible(TypeWithAnnotations type);
internal abstract void ReportDiagnosticsIfObsolete(TypeWithAnnotations type, Binder binder, SyntaxNode syntax, DiagnosticBag diagnostics);
internal abstract bool TryForceResolveAsNullableValueType();
}
private sealed class NonLazyType : Extensions
......@@ -807,7 +817,7 @@ internal override bool GetIsValueType(TypeSymbol typeSymbol, ConsList<TypeParame
internal override TypeWithAnnotations WithModifiers(TypeWithAnnotations type, ImmutableArray<CustomModifier> customModifiers)
{
return CreateNonLazyType(type._defaultType, type.NullableAnnotation, customModifiers);
return CreateNonLazyType(type.DefaultType, type.NullableAnnotation, customModifiers);
}
internal override TypeSymbol AsTypeSymbolOnly(TypeSymbol typeSymbol) => typeSymbol;
......@@ -819,12 +829,12 @@ internal override TypeWithAnnotations WithTypeAndModifiers(TypeWithAnnotations t
internal override TypeWithAnnotations AsNullableReferenceType(TypeWithAnnotations type)
{
return CreateNonLazyType(type._defaultType, NullableAnnotation.Annotated, _customModifiers);
return CreateNonLazyType(type.DefaultType, NullableAnnotation.Annotated, _customModifiers);
}
internal override TypeWithAnnotations AsNotNullableReferenceType(TypeWithAnnotations type)
{
var defaultType = type._defaultType;
var defaultType = type.DefaultType;
return CreateNonLazyType(defaultType, defaultType.IsNullableType() ? type.NullableAnnotation : NullableAnnotation.NotAnnotated, _customModifiers);
}
......@@ -840,7 +850,7 @@ internal override TypeWithAnnotations SubstituteType(TypeWithAnnotations type, A
internal override TypeWithAnnotations TransformToTupleIfCompatible(TypeWithAnnotations type)
{
var defaultType = type._defaultType;
var defaultType = type.DefaultType;
var transformedType = TupleTypeSymbol.TransformToTupleIfCompatible(defaultType);
if ((object)defaultType != transformedType)
{
......@@ -853,6 +863,11 @@ internal override void ReportDiagnosticsIfObsolete(TypeWithAnnotations type, Bin
{
type.ReportDiagnosticsIfObsoleteCore(binder, syntax, diagnostics);
}
internal override bool TryForceResolveAsNullableValueType()
{
return false;
}
}
/// <summary>
......@@ -888,9 +903,7 @@ private TypeSymbol GetResolvedType()
}
else
{
Interlocked.CompareExchange(ref _resolved,
_compilation.GetSpecialType(SpecialType.System_Nullable_T).Construct(ImmutableArray.Create(_underlying)),
null);
TryForceResolveAsNullableValueType();
}
}
......@@ -977,8 +990,7 @@ internal override TypeWithAnnotations SubstituteType(TypeWithAnnotations type, A
var newUnderlying = _underlying.SubstituteTypeCore(typeMap, withTupleUnification);
if (!newUnderlying.IsSameAs(this._underlying))
{
if ((newUnderlying.Type.Equals(this._underlying.Type, TypeCompareKind.ConsiderEverything) ||
newUnderlying.Type is IndexedTypeParameterSymbolForOverriding) &&
if (newUnderlying.Type.Equals(this._underlying.Type, TypeCompareKind.ConsiderEverything) &&
newUnderlying.CustomModifiers.IsEmpty)
{
return CreateLazyNullableType(_compilation, newUnderlying);
......@@ -1020,6 +1032,13 @@ internal override bool TypeSymbolEquals(TypeWithAnnotations type, TypeWithAnnota
return type.TypeSymbolEqualsCore(other, comparison);
}
internal override bool TryForceResolveAsNullableValueType()
{
return Interlocked.CompareExchange(ref _resolved,
_compilation.GetSpecialType(SpecialType.System_Nullable_T).Construct(ImmutableArray.Create(_underlying)),
null) is null;
}
}
}
}
......@@ -4672,6 +4672,56 @@ public class Class : Interface<int>
});
}
[Fact]
public void TestExplicitImplementationAmbiguousInterfaceMethodWithDifferingConstraints()
{
var text = @"
public interface Interface<T>
{
void Method<V>(int i) where V : new();
void Method<V>(T i);
}
public class Class : Interface<int>
{
void Interface<int>.Method<V>(int i) { _ = new V(); } //this explicitly implements both methods in Interface<int>
public void Method<V>(int i) { } //this is here to avoid CS0535 - not implementing interface method
}
";
CreateCompilation(text).VerifyDiagnostics(
// (10,25): warning CS0473: Explicit interface implementation 'Class.Interface<int>.Method<V>(int)' matches more than one interface member. Which interface member is actually chosen is implementation-dependent. Consider using a non-explicit implementation instead.
// void Interface<int>.Method<V>(int i) { _ = new V(); } //this explicitly implements both methods in Interface<int>
Diagnostic(ErrorCode.WRN_ExplicitImplCollision, "Method").WithArguments("Class.Interface<int>.Method<V>(int)").WithLocation(10, 25));
}
[Fact]
public void TestExplicitImplementationAmbiguousInterfaceMethodWithDifferingConstraints_OppositeDeclarationOrder()
{
var text = @"
public interface Interface<T>
{
void Method<V>(T i);
void Method<V>(int i) where V : new();
}
public class Class : Interface<int>
{
void Interface<int>.Method<V>(int i) { _ = new V(); } //this explicitly implements both methods in Interface<int>
public void Method<V>(int i) { } //this is here to avoid CS0535 - not implementing interface method
}
";
CreateCompilation(text).VerifyDiagnostics(
// (10,25): warning CS0473: Explicit interface implementation 'Class.Interface<int>.Method<V>(int)' matches more than one interface member. Which interface member is actually chosen is implementation-dependent. Consider using a non-explicit implementation instead.
// void Interface<int>.Method<V>(int i) { _ = new V(); } //this explicitly implements both methods in Interface<int>
Diagnostic(ErrorCode.WRN_ExplicitImplCollision, "Method").WithArguments("Class.Interface<int>.Method<V>(int)").WithLocation(10, 25),
// (10,48): error CS0304: Cannot create an instance of the variable type 'V' because it does not have the new() constraint
// void Interface<int>.Method<V>(int i) { _ = new V(); } //this explicitly implements both methods in Interface<int>
Diagnostic(ErrorCode.ERR_NoNewTyvar, "new V()").WithArguments("V").WithLocation(10, 48),
// (11,17): error CS0425: The constraints for type parameter 'V' of method 'Class.Method<V>(int)' must match the constraints for type parameter 'V' of interface method 'Interface<int>.Method<V>(int)'. Consider using an explicit interface implementation instead.
// public void Method<V>(int i) { } //this is here to avoid CS0535 - not implementing interface method
Diagnostic(ErrorCode.ERR_ImplBadConstraints, "Method").WithArguments("V", "Class.Method<V>(int)", "V", "Interface<int>.Method<V>(int)").WithLocation(11, 17));
}
[Fact]
public void TestExplicitImplementationAmbiguousInterfaceIndexer()
{
......@@ -8263,5 +8313,334 @@ public class D : C, I0<dynamic>
Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I0<dynamic>").WithArguments("D", "I0<dynamic>.M()").WithLocation(12, 21)
);
}
[Fact]
[WorkItem(34508, "https://github.com/dotnet/roslyn/issues/34508")]
public void ImplementMethodTakingNullableStructParameter_WithMethodTakingNullableStructParameter1()
{
var source = @"
interface I
{
void Goo<T>(T? value) where T : struct;
}
class C1 : I
{
public void Goo<T>(T? value) where T : struct { }
}
class C2 : I
{
void I.Goo<T>(T? value) { }
}
";
var comp = CreateCompilation(source).VerifyDiagnostics();
var c2Goo = (MethodSymbol)comp.GetMember("C2.I.Goo");
Assert.True(c2Goo.Parameters[0].Type.IsNullableType());
}
[Fact]
[WorkItem(34508, "https://github.com/dotnet/roslyn/issues/34508")]
public void ImplementMethodTakingNullableStructParameter_WithMethodTakingNullableStructParameter2()
{
var source = @"
interface I
{
void Goo<T>(T?[] value) where T : struct;
}
class C1 : I
{
public void Goo<T>(T?[] value) where T : struct { }
}
class C2 : I
{
void I.Goo<T>(T?[] value) { }
}
";
var comp = CreateCompilation(source).VerifyDiagnostics();
var c2Goo = (MethodSymbol)comp.GetMember("C2.I.Goo");
Assert.True(((ArrayTypeSymbol)c2Goo.Parameters[0].Type).ElementType.IsNullableType());
}
[Fact]
[WorkItem(34508, "https://github.com/dotnet/roslyn/issues/34508")]
public void ImplementMethodTakingNullableStructParameter_WithMethodTakingNullableStructParameter3()
{
var source = @"
interface I
{
void Goo<T>((T a, T? b)? value) where T : struct;
}
class C1 : I
{
public void Goo<T>((T a, T? b)? value) where T : struct { }
}
class C2 : I
{
void I.Goo<T>((T a, T? b)? value) { }
}
";
var comp = CreateCompilation(source).VerifyDiagnostics();
var c2Goo = (MethodSymbol)comp.GetMember("C2.I.Goo");
Assert.True(c2Goo.Parameters[0].Type.IsNullableType());
var tuple = c2Goo.Parameters[0].Type.GetMemberTypeArgumentsNoUseSiteDiagnostics()[0];
Assert.False(tuple.TupleElements[0].Type.IsNullableType());
Assert.True(tuple.TupleElements[1].Type.IsNullableType());
}
[Fact]
[WorkItem(34508, "https://github.com/dotnet/roslyn/issues/34508")]
public void ImplementMethodReturningNullableStructParameter_WithMethodReturningNullableStruct1()
{
var source = @"
interface I
{
T? Goo<T>() where T : struct;
}
class C1 : I
{
public T? Goo<T>() where T : struct => default;
}
class C2 : I
{
T? I.Goo<T>() => default;
}
";
var comp = CreateCompilation(source).VerifyDiagnostics();
var c2Goo = (MethodSymbol)comp.GetMember("C2.I.Goo");
Assert.True(c2Goo.ReturnType.IsNullableType());
}
[Fact]
[WorkItem(34508, "https://github.com/dotnet/roslyn/issues/34508")]
public void ImplementMethodReturningNullableStructParameter_WithMethodReturningNullableStruct2()
{
var source = @"
interface I
{
T?[] Goo<T>() where T : struct;
}
class C1 : I
{
public T?[] Goo<T>() where T : struct => default;
}
class C2 : I
{
T?[] I.Goo<T>() => default;
}
";
var comp = CreateCompilation(source).VerifyDiagnostics();
var c2Goo = (MethodSymbol)comp.GetMember("C2.I.Goo");
Assert.True(((ArrayTypeSymbol)c2Goo.ReturnType).ElementType.IsNullableType());
}
[Fact]
[WorkItem(34508, "https://github.com/dotnet/roslyn/issues/34508")]
public void ImplementMethodReturningNullableStructParameter_WithMethodReturningNullableStruct3()
{
var source = @"
interface I
{
(T a, T? b)? Goo<T>() where T : struct;
}
class C1 : I
{
public (T a, T? b)? Goo<T>() where T : struct => default;
}
class C2 : I
{
(T a, T? b)? I.Goo<T>() => default;
}
";
var comp = CreateCompilation(source).VerifyDiagnostics();
var c2Goo = (MethodSymbol)comp.GetMember("C2.I.Goo");
Assert.True(c2Goo.ReturnType.IsNullableType());
var tuple = c2Goo.ReturnType.GetMemberTypeArgumentsNoUseSiteDiagnostics()[0];
Assert.False(tuple.TupleElements[0].Type.IsNullableType());
Assert.True(tuple.TupleElements[1].Type.IsNullableType());
}
[Fact]
[WorkItem(34508, "https://github.com/dotnet/roslyn/issues/34508")]
public void OverrideMethodTakingNullableStructParameter_WithMethodTakingNullableStructParameter1()
{
var source = @"
abstract class Base
{
public abstract void Goo<T>(T? value) where T : struct;
}
class Derived : Base
{
public override void Goo<T>(T? value) { }
}
";
var comp = CreateCompilation(source).VerifyDiagnostics();
var dGoo = (MethodSymbol)comp.GetMember("Derived.Goo");
Assert.True(dGoo.Parameters[0].Type.IsNullableType());
}
[Fact]
[WorkItem(34508, "https://github.com/dotnet/roslyn/issues/34508")]
public void OverrideMethodTakingNullableStructParameter_WithMethodTakingNullableStructParameter2()
{
var source = @"
abstract class Base
{
public abstract void Goo<T>(T?[] value) where T : struct;
}
class Derived : Base
{
public override void Goo<T>(T?[] value) { }
}
";
var comp = CreateCompilation(source).VerifyDiagnostics();
var dGoo = (MethodSymbol)comp.GetMember("Derived.Goo");
Assert.True(((ArrayTypeSymbol)dGoo.Parameters[0].Type).ElementType.IsNullableType());
}
[Fact]
[WorkItem(34508, "https://github.com/dotnet/roslyn/issues/34508")]
public void OverrideMethodTakingNullableStructParameter_WithMethodTakingNullableStructParameter3()
{
var source = @"
abstract class Base
{
public abstract void Goo<T>((T a, T? b)? value) where T : struct;
}
class Derived : Base
{
public override void Goo<T>((T a, T? b)? value) { }
}
";
var comp = CreateCompilation(source).VerifyDiagnostics();
var dGoo = (MethodSymbol)comp.GetMember("Derived.Goo");
Assert.True(dGoo.Parameters[0].Type.IsNullableType());
var tuple = dGoo.Parameters[0].Type.GetMemberTypeArgumentsNoUseSiteDiagnostics()[0];
Assert.False(tuple.TupleElements[0].Type.IsNullableType());
Assert.True(tuple.TupleElements[1].Type.IsNullableType());
}
[Fact]
[WorkItem(34508, "https://github.com/dotnet/roslyn/issues/34508")]
public void OverrideMethodReturningNullableStructParameter_WithMethodReturningNullableStruct1()
{
var source = @"
abstract class Base
{
public abstract T? Goo<T>() where T : struct;
}
class Derived : Base
{
public override T? Goo<T>() => default;
}
";
var comp = CreateCompilation(source).VerifyDiagnostics();
var dGoo = (MethodSymbol)comp.GetMember("Derived.Goo");
Assert.True(dGoo.ReturnType.IsNullableType());
}
[Fact]
[WorkItem(34508, "https://github.com/dotnet/roslyn/issues/34508")]
public void OverrideMethodReturningNullableStructParameter_WithMethodReturningNullableStruct2()
{
var source = @"
abstract class Base
{
public abstract T?[] Goo<T>() where T : struct;
}
class Derived : Base
{
public override T?[] Goo<T>() => default;
}
";
var comp = CreateCompilation(source).VerifyDiagnostics();
var dGoo = (MethodSymbol)comp.GetMember("Derived.Goo");
Assert.True(((ArrayTypeSymbol)dGoo.ReturnType).ElementType.IsNullableType());
}
[Fact]
[WorkItem(34508, "https://github.com/dotnet/roslyn/issues/34508")]
public void OverrideMethodReturningNullableStructParameter_WithMethodReturningNullableStruct3()
{
var source = @"
abstract class Base
{
public abstract (T a, T? b)? Goo<T>() where T : struct;
}
class Derived : Base
{
public override (T a, T? b)? Goo<T>() => default;
}
";
var comp = CreateCompilation(source).VerifyDiagnostics();
var dGoo = (MethodSymbol)comp.GetMember("Derived.Goo");
Assert.True(dGoo.ReturnType.IsNullableType());
var tuple = dGoo.ReturnType.GetMemberTypeArgumentsNoUseSiteDiagnostics()[0];
Assert.False(tuple.TupleElements[0].Type.IsNullableType());
Assert.True(tuple.TupleElements[1].Type.IsNullableType());
}
[Fact]
[WorkItem(34583, "https://github.com/dotnet/roslyn/issues/34583")]
public void ExplicitImplementationOfNullableStructWithMultipleTypeParameters()
{
var source = @"
interface I
{
void Goo<T, U>(T? value) where T : struct;
}
class C1 : I
{
public void Goo<T, U>(T? value) where T : struct {}
}
class C2 : I
{
void I.Goo<T, U>(T? value) {}
}
";
var comp = CreateCompilation(source).VerifyDiagnostics();
}
}
}
......@@ -5606,24 +5606,24 @@ class B : A
}
";
CreateCompilation(source, options: TestOptions.ReleaseDll).VerifyDiagnostics(
// (4,20): error CS8627: A nullable type parameter must be known to be a value type or non-nullable reference type. Consider adding a 'class', 'struct', or type constraint.
// public virtual T? Goo<T>()
Diagnostic(ErrorCode.ERR_NullableUnconstrainedTypeParameter, "T?").WithLocation(4, 20),
// (4,21): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' context.
// public virtual T? Goo<T>()
Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(4, 21),
// (12,21): error CS8627: A nullable type parameter must be known to be a value type or non-nullable reference type. Consider adding a 'class', 'struct', or type constraint.
// public override T? Goo<T>()
Diagnostic(ErrorCode.ERR_NullableUnconstrainedTypeParameter, "T?").WithLocation(12, 21),
// (4,20): error CS8627: A nullable type parameter must be known to be a value type or non-nullable reference type. Consider adding a 'class', 'struct', or type constraint.
// public virtual T? Goo<T>()
Diagnostic(ErrorCode.ERR_NullableUnconstrainedTypeParameter, "T?").WithLocation(4, 20),
// (12,22): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' context.
// public override T? Goo<T>()
Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(12, 22),
// (12,24): error CS0508: 'B.Goo<T>()': return type must be 'T' to match overridden member 'A.Goo<T>()'
// public override T? Goo<T>()
Diagnostic(ErrorCode.ERR_CantChangeReturnTypeOnOverride, "Goo").WithArguments("B.Goo<T>()", "A.Goo<T>()", "T").WithLocation(12, 24),
// (12,24): error CS0453: The type 'T' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'Nullable<T>'
// public override T? Goo<T>()
Diagnostic(ErrorCode.ERR_ValConstraintNotSatisfied, "Goo").WithArguments("System.Nullable<T>", "T", "T").WithLocation(12, 24),
// (6,16): error CS0403: Cannot convert null to type parameter 'T' because it could be a non-nullable value type. Consider using 'default(T)' instead.
// return null;
Diagnostic(ErrorCode.ERR_TypeVarCantBeNull, "null").WithArguments("T").WithLocation(6, 16),
// (14,16): error CS0403: Cannot convert null to type parameter 'T' because it could be a non-nullable value type. Consider using 'default(T)' instead.
// return null;
Diagnostic(ErrorCode.ERR_TypeVarCantBeNull, "null").WithArguments("T").WithLocation(14, 16));
Diagnostic(ErrorCode.ERR_TypeVarCantBeNull, "null").WithArguments("T").WithLocation(6, 16));
}
[WorkItem(543710, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543710")]
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册