未验证 提交 1de4314e 编写于 作者: C Charles Stoner 提交者: GitHub

Ignore nullability when checking for duplicate constraints (#28248)

上级 9df66a1f
......@@ -179,15 +179,23 @@ A warning is reported for inconsistent top-level nullability of constraint types
static void F4<T> where T : class, Stream? { } // warning
static void F5<T> where T : Stream?, IDisposable { } // warning
```
An error is reported for duplicate constraints. _Should the error be reported for duplicates that differ by top-level or nested nullability?_
An error is reported for duplicate constraints where constraints are compared ignoring top-level and nested nullability.
```c#
class C<T> where T : class
{
static void F1<U>() where U : T?, T? { } // error: duplicate constraint
static void F2<U>() where U : I<T?>, I<T?> { } // error: duplicate constraint
static void F3<U>() where U : T, T? { } // error?
static void F4<U>() where U : I<T>, I<T?> { } // error?
static void F1<U>() where U : T, T? { } // error: duplicate constraint
static void F2<U>() where U : I<T>, I<T?> { } // error: duplicate constraint
}
```
_What are the rules for annotated (unannotated) type arguments for generic type parameters from unannotated (annotated) types and methods?_
```c#
[NotNullTypes(false)] List<T> F1<T>(T t) where T : class { ... }
[NotNullTypes(true)] List<T> F2<T>(T t) where T : class { ... }
[NotNullTypes(true)] List<T?> F3<T>(T? t) where T : class { ... }
var x = F1(notNullString); // List<string!> or List<string~> ?
var y = F1(maybeNullString); // List<string?> or List<string~> ?
var z = F2(obliviousString); // List<string~>! or List<string!>! ?
var w = F3(obliviousString); // List<string~>! or List<string?>! ?
```
## Compiler switch
_Describe behavior when feature is disabled._
......@@ -209,12 +209,11 @@ internal partial class Binder
return false;
}
// PROTOTYPE(NullableReferenceTypes): Report ERR_DuplicateBound for
// duplicates that differ by top-level or nested nullability as well?
if (constraintTypes.Contains(c => type.Equals(c, TypeCompareKind.AllAspects)))
// Ignore nullability when comparing constraints.
if (constraintTypes.Contains(c => type.Equals(c, TypeCompareKind.ConsiderEverything)))
{
// "Duplicate constraint '{0}' for type parameter '{1}'"
Error(diagnostics, ErrorCode.ERR_DuplicateBound, syntax, type, typeParameterName);
Error(diagnostics, ErrorCode.ERR_DuplicateBound, syntax, type.TypeSymbol.SetUnknownNullabilityForReferenceTypes(), typeParameterName);
return false;
}
......
......@@ -34612,24 +34612,33 @@ class C<V> where V : V?, V? { }
where U3 : T3, T3?;";
var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics(
// (3,26): error CS0405: Duplicate constraint 'V?' for type parameter 'V'
// class C<V> where V : V?, V? { }
Diagnostic(ErrorCode.ERR_DuplicateBound, "V?").WithArguments("V?", "V").WithLocation(3, 26),
// (3,9): error CS0454: Circular constraint dependency involving 'V' and 'V'
// class C<V> where V : V?, V? { }
Diagnostic(ErrorCode.ERR_CircularConstraint, "V").WithArguments("V", "V").WithLocation(3, 9),
// (1,9): error CS0454: Circular constraint dependency involving 'T' and 'T'
// (1,25): error CS0405: Duplicate constraint 'T' for type parameter 'T'
// class A<T> where T : T, T? { }
Diagnostic(ErrorCode.ERR_CircularConstraint, "T").WithArguments("T", "T").WithLocation(1, 9),
Diagnostic(ErrorCode.ERR_DuplicateBound, "T?").WithArguments("T", "T").WithLocation(1, 25),
// (1,9): error CS0454: Circular constraint dependency involving 'T' and 'T'
// class A<T> where T : T, T? { }
Diagnostic(ErrorCode.ERR_CircularConstraint, "T").WithArguments("T", "T").WithLocation(1, 9),
// (2,9): error CS0454: Circular constraint dependency involving 'U' and 'U'
// (2,26): error CS0405: Duplicate constraint 'U' for type parameter 'U'
// class B<U> where U : U?, U { }
Diagnostic(ErrorCode.ERR_CircularConstraint, "U").WithArguments("U", "U").WithLocation(2, 9),
Diagnostic(ErrorCode.ERR_DuplicateBound, "U").WithArguments("U", "U").WithLocation(2, 26),
// (2,9): error CS0454: Circular constraint dependency involving 'U' and 'U'
// class B<U> where U : U?, U { }
Diagnostic(ErrorCode.ERR_CircularConstraint, "U").WithArguments("U", "U").WithLocation(2, 9));
Diagnostic(ErrorCode.ERR_CircularConstraint, "U").WithArguments("U", "U").WithLocation(2, 9),
// (3,26): error CS0405: Duplicate constraint 'V' for type parameter 'V'
// class C<V> where V : V?, V? { }
Diagnostic(ErrorCode.ERR_DuplicateBound, "V?").WithArguments("V", "V").WithLocation(3, 26),
// (3,9): error CS0454: Circular constraint dependency involving 'V' and 'V'
// class C<V> where V : V?, V? { }
Diagnostic(ErrorCode.ERR_CircularConstraint, "V").WithArguments("V", "V").WithLocation(3, 9),
// (5,20): error CS0405: Duplicate constraint 'T1' for type parameter 'U1'
// where U1 : T1, T1?;
Diagnostic(ErrorCode.ERR_DuplicateBound, "T1?").WithArguments("T1", "U1").WithLocation(5, 20),
// (7,28): error CS0405: Duplicate constraint 'T2' for type parameter 'U2'
// where U2 : class, T2?, T2;
Diagnostic(ErrorCode.ERR_DuplicateBound, "T2").WithArguments("T2", "U2").WithLocation(7, 28),
// (10,20): error CS0405: Duplicate constraint 'T3' for type parameter 'U3'
// where U3 : T3, T3?;
Diagnostic(ErrorCode.ERR_DuplicateBound, "T3?").WithArguments("T3", "U3").WithLocation(10, 20));
}
[Fact]
......@@ -34870,21 +34879,31 @@ class C<T> where T : class
static void F8<U>() where U : I<T?>, I<T?> { }
}";
var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8);
// PROTOTYPE(NullableReferenceTypes): Report ERR_DuplicateBound for
// duplicates that differ by top-level or nested nullability as well?
comp.VerifyDiagnostics(
// (4,38): error CS0405: Duplicate constraint 'T' for type parameter 'U'
// static void F1<U>() where U : T, T { }
Diagnostic(ErrorCode.ERR_DuplicateBound, "T").WithArguments("T", "U").WithLocation(4, 38),
// (7,39): error CS0405: Duplicate constraint 'T?' for type parameter 'U'
// (5,38): error CS0405: Duplicate constraint 'T' for type parameter 'U'
// static void F2<U>() where U : T, T? { }
Diagnostic(ErrorCode.ERR_DuplicateBound, "T?").WithArguments("T", "U").WithLocation(5, 38),
// (6,39): error CS0405: Duplicate constraint 'T' for type parameter 'U'
// static void F3<U>() where U : T?, T { }
Diagnostic(ErrorCode.ERR_DuplicateBound, "T").WithArguments("T", "U").WithLocation(6, 39),
// (7,39): error CS0405: Duplicate constraint 'T' for type parameter 'U'
// static void F4<U>() where U : T?, T? { }
Diagnostic(ErrorCode.ERR_DuplicateBound, "T?").WithArguments("T?", "U").WithLocation(7, 39),
Diagnostic(ErrorCode.ERR_DuplicateBound, "T?").WithArguments("T", "U").WithLocation(7, 39),
// (8,41): error CS0405: Duplicate constraint 'I<T>' for type parameter 'U'
// static void F5<U>() where U : I<T>, I<T> { }
Diagnostic(ErrorCode.ERR_DuplicateBound, "I<T>").WithArguments("I<T>", "U").WithLocation(8, 41),
// (11,42): error CS0405: Duplicate constraint 'I<T?>' for type parameter 'U'
// (9,41): error CS0405: Duplicate constraint 'I<T>' for type parameter 'U'
// static void F6<U>() where U : I<T>, I<T?> { }
Diagnostic(ErrorCode.ERR_DuplicateBound, "I<T?>").WithArguments("I<T>", "U").WithLocation(9, 41),
// (10,42): error CS0405: Duplicate constraint 'I<T>' for type parameter 'U'
// static void F7<U>() where U : I<T?>, I<T> { }
Diagnostic(ErrorCode.ERR_DuplicateBound, "I<T>").WithArguments("I<T>", "U").WithLocation(10, 42),
// (11,42): error CS0405: Duplicate constraint 'I<T>' for type parameter 'U'
// static void F8<U>() where U : I<T?>, I<T?> { }
Diagnostic(ErrorCode.ERR_DuplicateBound, "I<T?>").WithArguments("I<T?>", "U").WithLocation(11, 42));
Diagnostic(ErrorCode.ERR_DuplicateBound, "I<T?>").WithArguments("I<T>", "U").WithLocation(11, 42));
}
[Fact]
......@@ -34908,7 +34927,7 @@ public void TypeUnification_01()
@"interface I<T> { }
class C1<T, U> : I<T>, I<U> { }
class C2<T, U> : I<T>, I<U?> { }
class C3<T, U> : I<T?>, I<U?> { }
class C3<T, U> : I<T?>, I<U> { }
class C4<T, U> : I<T?>, I<U?> { }";
var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics(
......@@ -34919,8 +34938,8 @@ class C4<T, U> : I<T?>, I<U?> { }";
// class C2<T, U> : I<T>, I<U?> { }
Diagnostic(ErrorCode.ERR_UnifyingInterfaceInstantiations, "C2").WithArguments("C2<T, U>", "I<T>", "I<U?>").WithLocation(3, 7),
// (4,7): error CS0695: 'C3<T, U>' cannot implement both 'I<T?>' and 'I<U?>' because they may unify for some type parameter substitutions
// class C3<T, U> : I<T?>, I<U?> { }
Diagnostic(ErrorCode.ERR_UnifyingInterfaceInstantiations, "C3").WithArguments("C3<T, U>", "I<T?>", "I<U?>").WithLocation(4, 7),
// class C3<T, U> : I<T?>, I<U> { }
Diagnostic(ErrorCode.ERR_UnifyingInterfaceInstantiations, "C3").WithArguments("C3<T, U>", "I<T?>", "I<U>").WithLocation(4, 7),
// (5,7): error CS0695: 'C4<T, U>' cannot implement both 'I<T?>' and 'I<U?>' because they may unify for some type parameter substitutions
// class C4<T, U> : I<T?>, I<U?> { }
Diagnostic(ErrorCode.ERR_UnifyingInterfaceInstantiations, "C4").WithArguments("C4<T, U>", "I<T?>", "I<U?>").WithLocation(5, 7));
......@@ -34933,7 +34952,7 @@ public void TypeUnification_02()
@"interface I<T> { }
class C1<T, U> : I<T>, I<U> where T : struct { }
class C2<T, U> : I<T>, I<U?> where T : struct { }
class C3<T, U> : I<T?>, I<U?> where T : struct { }
class C3<T, U> : I<T?>, I<U> where T : struct { }
class C4<T, U> : I<T?>, I<U?> where T : struct { }";
var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics(
......@@ -34944,8 +34963,8 @@ class C4<T, U> : I<T?>, I<U?> where T : struct { }";
// class C2<T, U> : I<T>, I<U?> where T : struct { }
Diagnostic(ErrorCode.ERR_UnifyingInterfaceInstantiations, "C2").WithArguments("C2<T, U>", "I<T>", "I<U?>").WithLocation(3, 7),
// (4,7): error CS0695: 'C3<T, U>' cannot implement both 'I<T?>' and 'I<U?>' because they may unify for some type parameter substitutions
// class C3<T, U> : I<T?>, I<U?> where T : struct { }
Diagnostic(ErrorCode.ERR_UnifyingInterfaceInstantiations, "C3").WithArguments("C3<T, U>", "I<T?>", "I<U?>").WithLocation(4, 7),
// class C3<T, U> : I<T?>, I<U> where T : struct { }
Diagnostic(ErrorCode.ERR_UnifyingInterfaceInstantiations, "C3").WithArguments("C3<T, U>", "I<T?>", "I<U>").WithLocation(4, 7),
// (5,7): error CS0695: 'C4<T, U>' cannot implement both 'I<T?>' and 'I<U?>' because they may unify for some type parameter substitutions
// class C4<T, U> : I<T?>, I<U?> where T : struct { }
Diagnostic(ErrorCode.ERR_UnifyingInterfaceInstantiations, "C4").WithArguments("C4<T, U>", "I<T?>", "I<U?>").WithLocation(5, 7));
......@@ -34958,7 +34977,7 @@ public void TypeUnification_03()
@"interface I<T> { }
class C1<T, U> : I<T>, I<U> where T : class { }
class C2<T, U> : I<T>, I<U?> where T : class { }
class C3<T, U> : I<T?>, I<U?> where T : class { }
class C3<T, U> : I<T?>, I<U> where T : class { }
class C4<T, U> : I<T?>, I<U?> where T : class { }";
var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics(
......@@ -34969,8 +34988,8 @@ class C4<T, U> : I<T?>, I<U?> where T : class { }";
// class C2<T, U> : I<T>, I<U?> where T : class { }
Diagnostic(ErrorCode.ERR_UnifyingInterfaceInstantiations, "C2").WithArguments("C2<T, U>", "I<T>", "I<U?>").WithLocation(3, 7),
// (4,7): error CS0695: 'C3<T, U>' cannot implement both 'I<T?>' and 'I<U?>' because they may unify for some type parameter substitutions
// class C3<T, U> : I<T?>, I<U?> where T : class { }
Diagnostic(ErrorCode.ERR_UnifyingInterfaceInstantiations, "C3").WithArguments("C3<T, U>", "I<T?>", "I<U?>").WithLocation(4, 7),
// class C3<T, U> : I<T?>, I<U> where T : class { }
Diagnostic(ErrorCode.ERR_UnifyingInterfaceInstantiations, "C3").WithArguments("C3<T, U>", "I<T?>", "I<U>").WithLocation(4, 7),
// (5,7): error CS0695: 'C4<T, U>' cannot implement both 'I<T?>' and 'I<U?>' because they may unify for some type parameter substitutions
// class C4<T, U> : I<T?>, I<U?> where T : class { }
Diagnostic(ErrorCode.ERR_UnifyingInterfaceInstantiations, "C4").WithArguments("C4<T, U>", "I<T?>", "I<U?>").WithLocation(5, 7));
......@@ -34983,9 +35002,10 @@ public void TypeUnification_04()
@"interface I<T> { }
class C1<T, U> : I<T>, I<U> where T : struct where U : class { }
class C2<T, U> : I<T>, I<U?> where T : struct where U : class { }
class C3<T, U> : I<T?>, I<U?> where T : struct where U : class { }
class C3<T, U> : I<T?>, I<U> where T : struct where U : class { }
class C4<T, U> : I<T?>, I<U?> where T : struct where U : class { }";
var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8);
// Constraints are ignored when unifying types.
comp.VerifyDiagnostics(
// (2,7): error CS0695: 'C1<T, U>' cannot implement both 'I<T>' and 'I<U>' because they may unify for some type parameter substitutions
// class C1<T, U> : I<T>, I<U> where T : struct where U : class { }
......@@ -34994,8 +35014,8 @@ class C4<T, U> : I<T?>, I<U?> where T : struct where U : class { }";
// class C2<T, U> : I<T>, I<U?> where T : struct where U : class { }
Diagnostic(ErrorCode.ERR_UnifyingInterfaceInstantiations, "C2").WithArguments("C2<T, U>", "I<T>", "I<U?>").WithLocation(3, 7),
// (4,7): error CS0695: 'C3<T, U>' cannot implement both 'I<T?>' and 'I<U?>' because they may unify for some type parameter substitutions
// class C3<T, U> : I<T?>, I<U?> where T : struct where U : class { }
Diagnostic(ErrorCode.ERR_UnifyingInterfaceInstantiations, "C3").WithArguments("C3<T, U>", "I<T?>", "I<U?>").WithLocation(4, 7),
// class C3<T, U> : I<T?>, I<U> where T : struct where U : class { }
Diagnostic(ErrorCode.ERR_UnifyingInterfaceInstantiations, "C3").WithArguments("C3<T, U>", "I<T?>", "I<U>").WithLocation(4, 7),
// (5,7): error CS0695: 'C4<T, U>' cannot implement both 'I<T?>' and 'I<U?>' because they may unify for some type parameter substitutions
// class C4<T, U> : I<T?>, I<U?> where T : struct where U : class { }
Diagnostic(ErrorCode.ERR_UnifyingInterfaceInstantiations, "C4").WithArguments("C4<T, U>", "I<T?>", "I<U?>").WithLocation(5, 7));
......@@ -35008,7 +35028,7 @@ public void TypeUnification_05()
@"interface I<T> where T : class { }
class C1<T, U> : I<T>, I<U> where T : class where U : class { }
class C2<T, U> : I<T>, I<U?> where T : class where U : class { }
class C3<T, U> : I<T?>, I<U?> where T : class where U : class { }
class C3<T, U> : I<T?>, I<U> where T : class where U : class { }
class C4<T, U> : I<T?>, I<U?> where T : class where U : class { }";
var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics(
......@@ -35019,8 +35039,8 @@ class C4<T, U> : I<T?>, I<U?> where T : class where U : class { }";
// class C2<T, U> : I<T>, I<U?> where T : class where U : class { }
Diagnostic(ErrorCode.ERR_UnifyingInterfaceInstantiations, "C2").WithArguments("C2<T, U>", "I<T>", "I<U?>").WithLocation(3, 7),
// (4,7): error CS0695: 'C3<T, U>' cannot implement both 'I<T?>' and 'I<U?>' because they may unify for some type parameter substitutions
// class C3<T, U> : I<T?>, I<U?> where T : class where U : class { }
Diagnostic(ErrorCode.ERR_UnifyingInterfaceInstantiations, "C3").WithArguments("C3<T, U>", "I<T?>", "I<U?>").WithLocation(4, 7),
// class C3<T, U> : I<T?>, I<U> where T : class where U : class { }
Diagnostic(ErrorCode.ERR_UnifyingInterfaceInstantiations, "C3").WithArguments("C3<T, U>", "I<T?>", "I<U>").WithLocation(4, 7),
// (5,7): error CS0695: 'C4<T, U>' cannot implement both 'I<T?>' and 'I<U?>' because they may unify for some type parameter substitutions
// class C4<T, U> : I<T?>, I<U?> where T : class where U : class { }
Diagnostic(ErrorCode.ERR_UnifyingInterfaceInstantiations, "C4").WithArguments("C4<T, U>", "I<T?>", "I<U?>").WithLocation(5, 7));
......@@ -35311,6 +35331,92 @@ static void Main()
Assert.Equal("A2<System.Object?>!", typeParameters[1].ConstraintTypesNoUseSiteDiagnostics[0].ToTestDisplayString(true));
}
[Fact]
public void UnannotatedConstraint_Override()
{
var source0 =
@"using System.Runtime.CompilerServices;
public interface I<T> { }
public abstract class A<T> where T : class
{
[NonNullTypes(false)] public abstract void F1<U>() where U : T, I<T>;
[NonNullTypes(false)] public abstract void F2<U>() where U : T?, I<T?>;
[NonNullTypes(true)] public abstract void F3<U>() where U : T, I<T>;
[NonNullTypes(true)] public abstract void F4<U>() where U : T?, I<T?>;
}";
var comp0 = CreateCompilation(new[] { source0, NonNullTypesAttributesDefinition }, parseOptions: TestOptions.Regular8);
comp0.VerifyDiagnostics();
var ref0 = comp0.EmitToImageReference();
var source =
@"using System.Runtime.CompilerServices;
[NonNullTypes(false)]
class B1 : A<string>
{
public override void F1<U>() { }
public override void F2<U>() { }
public override void F3<U>() { }
public override void F4<U>() { }
}
[NonNullTypes(false)]
class B2 : A<string?>
{
public override void F1<U>() { }
public override void F2<U>() { }
public override void F3<U>() { }
public override void F4<U>() { }
}
[NonNullTypes(true)]
class B3 : A<string>
{
public override void F1<U>() { }
public override void F2<U>() { }
public override void F3<U>() { }
public override void F4<U>() { }
}
[NonNullTypes(true)]
class B4 : A<string?>
{
public override void F1<U>() { }
public override void F2<U>() { }
public override void F3<U>() { }
public override void F4<U>() { }
}";
var comp = CreateCompilation(source, references: new[] { new CSharpCompilationReference(comp0) }, parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics();
verifyAllConstraintTypes();
comp = CreateCompilation(source, references: new[] { comp0.EmitToImageReference() }, parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics();
verifyAllConstraintTypes();
void verifyAllConstraintTypes()
{
verifyConstraintTypes("B1.F1", "System.String", "I<System.String>");
verifyConstraintTypes("B1.F2", "System.String?", "I<System.String?>");
verifyConstraintTypes("B1.F3", "System.String", "I<System.String>");
verifyConstraintTypes("B1.F4", "System.String?", "I<System.String?>");
verifyConstraintTypes("B2.F1", "System.String?", "I<System.String?>");
verifyConstraintTypes("B2.F2", "System.String?", "I<System.String?>");
verifyConstraintTypes("B2.F3", "System.String?", "I<System.String?>");
verifyConstraintTypes("B2.F4", "System.String?", "I<System.String?>");
verifyConstraintTypes("B3.F1", "System.String!", "I<System.String!>");
verifyConstraintTypes("B3.F2", "System.String?", "I<System.String?>");
verifyConstraintTypes("B3.F3", "System.String!", "I<System.String!>");
verifyConstraintTypes("B3.F4", "System.String?", "I<System.String?>");
verifyConstraintTypes("B4.F1", "System.String?", "I<System.String?>");
verifyConstraintTypes("B4.F2", "System.String?", "I<System.String?>");
verifyConstraintTypes("B4.F3", "System.String?", "I<System.String?>");
verifyConstraintTypes("B4.F4", "System.String?", "I<System.String?>");
}
void verifyConstraintTypes(string methodName, params string[] expectedTypes)
{
var constraintTypes = comp.GetMember<MethodSymbol>(methodName).TypeParameters[0].ConstraintTypesNoUseSiteDiagnostics;
AssertEx.Equal(expectedTypes, constraintTypes.SelectAsArray(t => t.ToTestDisplayString(true)));
}
}
[Fact]
public void Constraint_Oblivious_01()
{
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册