未验证 提交 9e3c4b8d 编写于 作者: A AlekseyTs 提交者: GitHub

Replace `dynamic` with `object` when substituting constraints. (#36379)

Replace `dynamic` wih `object` when substituting constraints.

Fixes #36276.
上级 67e54621
......@@ -325,12 +325,16 @@ internal ImmutableArray<TypeWithAnnotations> SubstituteTypes(ImmutableArray<Type
/// <summary>
/// Substitute types, and return the results without duplicates, preserving the original order.
/// Note, all occurrences of 'dynamic' in resulting types will be replaced with 'object'.
/// </summary>
internal void SubstituteConstraintTypesDistinctWithoutModifiers(
TypeParameterSymbol owner,
ImmutableArray<TypeWithAnnotations> original,
ArrayBuilder<TypeWithAnnotations> result,
HashSet<TypeParameterSymbol> ignoreTypesDependentOnTypeParametersOpt)
{
DynamicTypeEraser dynamicEraser = null;
if (original.Length == 0)
{
return;
......@@ -341,7 +345,7 @@ internal ImmutableArray<TypeWithAnnotations> SubstituteTypes(ImmutableArray<Type
if (ignoreTypesDependentOnTypeParametersOpt == null ||
!type.Type.ContainsTypeParameters(ignoreTypesDependentOnTypeParametersOpt))
{
result.Add(SubstituteType(type));
result.Add(substituteConstraintType(type));
}
}
else
......@@ -352,7 +356,7 @@ internal ImmutableArray<TypeWithAnnotations> SubstituteTypes(ImmutableArray<Type
if (ignoreTypesDependentOnTypeParametersOpt == null ||
!type.Type.ContainsTypeParameters(ignoreTypesDependentOnTypeParametersOpt))
{
var substituted = SubstituteType(type);
var substituted = substituteConstraintType(type);
if (!map.TryGetValue(substituted.Type, out int mergeWith))
{
......@@ -368,6 +372,18 @@ internal ImmutableArray<TypeWithAnnotations> SubstituteTypes(ImmutableArray<Type
map.Free();
}
TypeWithAnnotations substituteConstraintType(TypeWithAnnotations type)
{
if (dynamicEraser == null)
{
dynamicEraser = new DynamicTypeEraser(owner.ContainingAssembly.CorLibrary.GetSpecialType(SpecialType.System_Object));
}
TypeWithAnnotations substituted = SubstituteType(type);
return substituted.WithTypeAndModifiers(dynamicEraser.EraseDynamic(substituted.Type), substituted.CustomModifiers);
}
}
internal ImmutableArray<TypeParameterSymbol> SubstituteTypeParameters(ImmutableArray<TypeParameterSymbol> original)
......
......@@ -108,7 +108,6 @@ internal static class ConstraintsHelper
NamedTypeSymbol effectiveBaseClass = corLibrary.GetSpecialType(typeParameter.HasValueTypeConstraint ? SpecialType.System_ValueType : SpecialType.System_Object);
TypeSymbol deducedBaseType = effectiveBaseClass;
DynamicTypeEraser dynamicEraser = null;
if (constraintTypes.Length == 0)
{
......@@ -125,15 +124,13 @@ internal static class ConstraintsHelper
// interfaces, and filter out any constraint types that cause cycles.
foreach (var constraintType in constraintTypes)
{
Debug.Assert(!constraintType.Type.ContainsDynamic());
NamedTypeSymbol constraintEffectiveBase;
TypeSymbol constraintDeducedBase;
switch (constraintType.TypeKind)
{
case TypeKind.Dynamic:
Debug.Assert(inherited || currentCompilation == null);
continue;
case TypeKind.TypeParameter:
{
var constraintTypeParameter = (TypeParameterSymbol)constraintType.Type;
......@@ -188,35 +185,18 @@ internal static class ConstraintsHelper
case TypeKind.Interface:
case TypeKind.Class:
case TypeKind.Delegate:
NamedTypeSymbol erasedConstraintType;
if (inherited || currentCompilation == null)
{
// only inherited constraints may contain dynamic
if (dynamicEraser == null)
{
dynamicEraser = new DynamicTypeEraser(corLibrary.GetSpecialType(SpecialType.System_Object));
}
erasedConstraintType = (NamedTypeSymbol)dynamicEraser.EraseDynamic(constraintType.Type);
}
else
{
Debug.Assert(!constraintType.Type.ContainsDynamic());
Debug.Assert(constraintType.TypeKind != TypeKind.Delegate);
erasedConstraintType = (NamedTypeSymbol)constraintType.Type;
}
Debug.Assert(inherited || currentCompilation == null || constraintType.TypeKind != TypeKind.Delegate);
if (constraintType.Type.IsInterfaceType())
{
AddInterface(interfacesBuilder, erasedConstraintType);
AddInterface(interfacesBuilder, (NamedTypeSymbol)constraintType.Type);
constraintTypesBuilder.Add(constraintType);
continue;
}
else
{
constraintEffectiveBase = erasedConstraintType;
constraintEffectiveBase = (NamedTypeSymbol)constraintType.Type;
constraintDeducedBase = constraintType.Type;
break;
}
......@@ -920,7 +900,7 @@ private static bool HasDuplicateInterfaces(NamedTypeSymbol type, ConsList<TypeSy
// original definition of the type parameters using the map from the constructed symbol.
var constraintTypes = ArrayBuilder<TypeWithAnnotations>.GetInstance();
HashSet<DiagnosticInfo> useSiteDiagnostics = null;
substitution.SubstituteConstraintTypesDistinctWithoutModifiers(typeParameter.ConstraintTypesWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics), constraintTypes,
substitution.SubstituteConstraintTypesDistinctWithoutModifiers(typeParameter, typeParameter.ConstraintTypesWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics), constraintTypes,
ignoreTypeConstraintsDependentOnTypeParametersOpt);
bool hasError = false;
......
......@@ -100,7 +100,7 @@ public override ImmutableArray<CSharpAttributeData> GetAttributes()
internal override ImmutableArray<TypeWithAnnotations> GetConstraintTypes(ConsList<TypeParameterSymbol> inProgress)
{
var constraintTypes = ArrayBuilder<TypeWithAnnotations>.GetInstance();
_map.SubstituteConstraintTypesDistinctWithoutModifiers(_underlyingTypeParameter.GetConstraintTypes(inProgress), constraintTypes, null);
_map.SubstituteConstraintTypesDistinctWithoutModifiers(_underlyingTypeParameter, _underlyingTypeParameter.GetConstraintTypes(inProgress), constraintTypes, null);
TypeWithAnnotations bestObjectConstraint = default;
......@@ -160,7 +160,7 @@ internal override ImmutableArray<TypeWithAnnotations> GetConstraintTypes(ConsLis
else if (!HasNotNullConstraint && !HasValueTypeConstraint && !HasReferenceTypeConstraint)
{
var constraintTypes = ArrayBuilder<TypeWithAnnotations>.GetInstance();
_map.SubstituteConstraintTypesDistinctWithoutModifiers(_underlyingTypeParameter.GetConstraintTypes(ConsList<TypeParameterSymbol>.Empty), constraintTypes, null);
_map.SubstituteConstraintTypesDistinctWithoutModifiers(_underlyingTypeParameter, _underlyingTypeParameter.GetConstraintTypes(ConsList<TypeParameterSymbol>.Empty), constraintTypes, null);
return IsNotNullableIfReferenceTypeFromConstraintTypes(constraintTypes.ToImmutableAndFree());
}
......
......@@ -70210,6 +70210,219 @@ void symbolValidator(ModuleSymbol m)
}
}
[Fact]
public void DynamicConstraint_01()
{
var source =
@"
class B<S>
{
public static void F1<T1>(T1 t1) where T1 : dynamic
{
}
public static void F2<T2>(T2 t2) where T2 : B<dynamic>
{
}
}";
var comp = CreateCompilation(new[] { source });
comp.VerifyDiagnostics(
// (4,49): error CS1967: Constraint cannot be the dynamic type
// public static void F1<T1>(T1 t1) where T1 : dynamic
Diagnostic(ErrorCode.ERR_DynamicTypeAsBound, "dynamic").WithLocation(4, 49),
// (8,49): error CS1968: Constraint cannot be a dynamic type 'B<dynamic>'
// public static void F2<T2>(T2 t2) where T2 : B<dynamic>
Diagnostic(ErrorCode.ERR_ConstructedDynamicTypeAsBound, "B<dynamic>").WithArguments("B<dynamic>").WithLocation(8, 49)
);
var m = comp.SourceModule;
var f1 = (MethodSymbol)m.GlobalNamespace.GetMember("B.F1");
Assert.Equal("void B<S>.F1<T1>(T1 t1)", f1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints));
var f2 = (MethodSymbol)m.GlobalNamespace.GetMember("B.F2");
Assert.Equal("void B<S>.F2<T2>(T2 t2)", f2.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints));
}
[Fact]
[WorkItem(36276, "https://github.com/dotnet/roslyn/issues/36276")]
public void DynamicConstraint_02()
{
var source1 =
@"
#nullable enable
class Test1<T>
{
public virtual void M1<S>() where S : T
{
}
}
class Test2 : Test1<dynamic>
{
public override void M1<S>()
{
}
void Test()
{
base.M1<object?>();
this.M1<object?>();
}
}
";
var comp1 = CreateCompilation(new[] { source1 });
comp1.VerifyDiagnostics(
// (18,9): warning CS8631: The type 'object?' cannot be used as type parameter 'S' in the generic type or method 'Test1<dynamic>.M1<S>()'. Nullability of type argument 'object?' doesn't match constraint type 'object'.
// base.M1<object?>();
Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "base.M1<object?>").WithArguments("Test1<dynamic>.M1<S>()", "object", "S", "object?").WithLocation(18, 9),
// (19,9): warning CS8631: The type 'object?' cannot be used as type parameter 'S' in the generic type or method 'Test2.M1<S>()'. Nullability of type argument 'object?' doesn't match constraint type 'object'.
// this.M1<object?>();
Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "this.M1<object?>").WithArguments("Test2.M1<S>()", "object", "S", "object?").WithLocation(19, 9)
);
CompileAndVerify(comp1, sourceSymbolValidator: symbolValidator, symbolValidator: symbolValidator);
void symbolValidator(ModuleSymbol m)
{
var m1 = (MethodSymbol)m.GlobalNamespace.GetMember("Test2.M1");
Assert.Equal("void Test2.M1<S>() where S : System.Object!", m1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints));
Assert.True(m1.TypeParameters[0].IsNotNullableIfReferenceType);
var baseM1 = m1.OverriddenMethod;
Assert.Equal("void Test1<dynamic!>.M1<S>() where S : System.Object!", baseM1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints));
Assert.True(baseM1.TypeParameters[0].IsNotNullableIfReferenceType);
}
}
[Fact]
[WorkItem(36276, "https://github.com/dotnet/roslyn/issues/36276")]
public void DynamicConstraint_03()
{
var source1 =
@"
#nullable disable
class Test1<T>
{
public virtual void M1<S>() where S : T
{
}
}
class Test2 : Test1<dynamic>
{
public override void M1<S>()
{
}
void Test()
{
#nullable enable
base.M1<object?>();
this.M1<object?>();
}
}
";
var comp1 = CreateCompilation(new[] { source1 });
comp1.VerifyDiagnostics();
CompileAndVerify(comp1, sourceSymbolValidator: symbolValidator, symbolValidator: symbolValidator);
void symbolValidator(ModuleSymbol m)
{
var m1 = (MethodSymbol)m.GlobalNamespace.GetMember("Test2.M1");
Assert.Equal("void Test2.M1<S>()", m1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints));
Assert.Null(m1.TypeParameters[0].IsNotNullableIfReferenceType);
var baseM1 = m1.OverriddenMethod;
Assert.Equal("void Test1<dynamic>.M1<S>()", baseM1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints));
Assert.Null(baseM1.TypeParameters[0].IsNotNullableIfReferenceType);
}
}
[Fact]
public void DynamicConstraint_04()
{
var source1 =
@"
#nullable enable
class Test1<T>
{
public virtual void M1<S>() where S : T
{
}
}
class Test2 : Test1<Test1<dynamic>>
{
public override void M1<S>()
{
}
void Test()
{
base.M1<Test1<object?>>();
this.M1<Test1<object?>>();
}
}
";
var comp1 = CreateCompilation(new[] { source1 });
comp1.VerifyDiagnostics(
// (18,9): warning CS8631: The type 'Test1<object?>' cannot be used as type parameter 'S' in the generic type or method 'Test1<Test1<dynamic>>.M1<S>()'. Nullability of type argument 'Test1<object?>' doesn't match constraint type 'Test1<object>'.
// base.M1<Test1<object?>>();
Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "base.M1<Test1<object?>>").WithArguments("Test1<Test1<dynamic>>.M1<S>()", "Test1<object>", "S", "Test1<object?>").WithLocation(18, 9),
// (19,9): warning CS8631: The type 'Test1<object?>' cannot be used as type parameter 'S' in the generic type or method 'Test2.M1<S>()'. Nullability of type argument 'Test1<object?>' doesn't match constraint type 'Test1<object>'.
// this.M1<Test1<object?>>();
Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "this.M1<Test1<object?>>").WithArguments("Test2.M1<S>()", "Test1<object>", "S", "Test1<object?>").WithLocation(19, 9)
);
CompileAndVerify(comp1, sourceSymbolValidator: symbolValidator, symbolValidator: symbolValidator);
void symbolValidator(ModuleSymbol m)
{
var m1 = (MethodSymbol)m.GlobalNamespace.GetMember("Test2.M1");
Assert.Equal("void Test2.M1<S>() where S : Test1<System.Object!>!", m1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints));
Assert.True(m1.TypeParameters[0].IsNotNullableIfReferenceType);
var baseM1 = m1.OverriddenMethod;
Assert.Equal("void Test1<Test1<dynamic!>!>.M1<S>() where S : Test1<System.Object!>!", baseM1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints));
Assert.True(baseM1.TypeParameters[0].IsNotNullableIfReferenceType);
}
}
[Fact]
public void DynamicConstraint_05()
{
var source1 =
@"
#nullable enable
public class Test1<T>
{
public virtual void M1<S>(S x) where S : I1?, T {}
}
public interface I1 {}
public class Test2 : Test1<dynamic>
{
public override void M1<S>(S x)
{
}
}
";
var comp1 = CreateCompilation(new[] { source1 });
comp1.VerifyDiagnostics();
CompileAndVerify(comp1, sourceSymbolValidator: symbolValidator, symbolValidator: symbolValidator);
void symbolValidator(ModuleSymbol m)
{
var m1 = (MethodSymbol)m.GlobalNamespace.GetMember("Test2.M1");
Assert.Equal("void Test2.M1<S>(S x) where S : System.Object!, I1?", m1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints));
Assert.True(m1.TypeParameters[0].IsNotNullableIfReferenceType);
var baseM1 = m1.OverriddenMethod;
Assert.Equal("void Test1<dynamic!>.M1<S>(S x) where S : System.Object!, I1?", baseM1.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints));
Assert.True(baseM1.TypeParameters[0].IsNotNullableIfReferenceType);
}
}
[Fact]
public void NotNullConstraint_01()
{
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册