Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow fully oblivious types to coexist with nullable-aware base types in partial classes #55861

Merged
merged 7 commits into from
Aug 31, 2021
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -315,9 +315,33 @@ private Tuple<NamedTypeSymbol, ImmutableArray<NamedTypeSymbol>> MakeDeclaredBase
baseType = partBase;
baseTypeLocation = decl.NameLocation;
}
else if ((object)partBase != null && !TypeSymbol.Equals(partBase, baseType, TypeCompareKind.ConsiderEverything2) && partBase.TypeKind != TypeKind.Error)
else if ((object)partBase != null && !TypeSymbol.Equals(partBase, baseType, TypeCompareKind.ConsiderEverything) && partBase.TypeKind != TypeKind.Error)
{
// the parts do not agree
if (partBase.Equals(baseType, TypeCompareKind.ObliviousNullableModifierMatchesAny))
{
if (TypeWithAnnotations.Create(baseType).VisitType(
type: null,
(type, arg, flag) => !type.NullableAnnotation.IsOblivious(),
RikkiGibson marked this conversation as resolved.
Show resolved Hide resolved
typePredicate: null,
arg: (object)null) is null)
RikkiGibson marked this conversation as resolved.
Show resolved Hide resolved
{
// 'baseType' is completely oblivious. Prefer 'partBase' in this case.
baseType = partBase;
baseTypeLocation = decl.NameLocation;
continue;
}
else if (TypeWithAnnotations.Create(partBase).VisitType(
type: null,
(type, arg, flag) => !type.NullableAnnotation.IsOblivious(),
RikkiGibson marked this conversation as resolved.
Show resolved Hide resolved
typePredicate: null,
arg: (object)null) is null)
{
// 'partBase' is completely oblivious. Prefer 'baseType' in this case.
continue;
}
}

var info = diagnostics.Add(ErrorCode.ERR_PartialMultipleBases, Locations[0], this);
baseType = new ExtendedErrorTypeSymbol(baseType, LookupResultKind.Ambiguous, info);
baseTypeLocation = decl.NameLocation;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131349,6 +131349,304 @@ partial interface I4<out T> where T : I1
);
}

[Fact, WorkItem(45960, "https://github.com/dotnet/roslyn/issues/45960")]
public void PartialBaseTypeDifference_01()
{
var source0 = @"
#nullable enable

Base<string> base1 = new C();
Base<string?> base2 = new C(); // 1

class Base<T> { }
";
var source1 = @"
#nullable enable
partial class C : Base<string> { }
RikkiGibson marked this conversation as resolved.
Show resolved Hide resolved
";
var source2 = @"
#nullable disable
partial class C : Base<string> { }
";
var comp = CreateCompilation(new[] { source0, source1, source2 });
verify();

comp = CreateCompilation(new[] { source0, source2, source1 });
verify();

void verify()
{
comp.VerifyDiagnostics(
// (5,23): warning CS8619: Nullability of reference types in value of type 'C' doesn't match target type 'Base<string?>'.
// Base<string?> base2 = new C(); // 1
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "new C()").WithArguments("C", "Base<string?>").WithLocation(5, 23)
RikkiGibson marked this conversation as resolved.
Show resolved Hide resolved
);

var cClass = comp.GetMember<NamedTypeSymbol>("C");
var @base = cClass.BaseTypeNoUseSiteDiagnostics;
// note: the symbol model doesn't include a nullable annotation for base types or implemented interfaces.
Assert.Equal("Base<System.String!>", @base.ToTestDisplayString(includeNonNullable: true));
}
}

[Fact, WorkItem(45960, "https://github.com/dotnet/roslyn/issues/45960")]
public void PartialBaseTypeDifference_02()
{
var source0 = @"
#nullable enable

Base<string> base1 = new C();
Base<string?> base2 = new C(); // 1

class Base<T> { }
";
var source1 = @"
#nullable enable
partial class C : Base<string> { }
RikkiGibson marked this conversation as resolved.
Show resolved Hide resolved
";
var source2 = @"
#nullable enable
partial class C : Base<
#nullable disable
string
#nullable enable
> { }
";
var comp = CreateCompilation(new[] { source0, source1, source2 });
verify();

comp = CreateCompilation(new[] { source0, source2, source1 });
verify();

void verify()
{
comp.VerifyDiagnostics(
// (5,23): warning CS8619: Nullability of reference types in value of type 'C' doesn't match target type 'Base<string?>'.
// Base<string?> base2 = new C(); // 1
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "new C()").WithArguments("C", "Base<string?>").WithLocation(5, 23)
);

var cClass = comp.GetMember<NamedTypeSymbol>("C");
var @base = cClass.BaseTypeNoUseSiteDiagnostics;
Assert.Equal("Base<System.String!>", @base.ToTestDisplayString(includeNonNullable: true));
}
}

[Fact, WorkItem(45960, "https://github.com/dotnet/roslyn/issues/45960")]
public void PartialBaseTypeDifference_03()
{
var source0 = @"
#nullable enable

Base<string> base1 = new C();
Base<string?> base2 = new C(); // 1

class Base<T> { }
";
var source1 = @"
#nullable enable
partial class C : Base<string> { }
";
var source2 = @"
#nullable enable
partial class C : Base<
#nullable disable
string
> { } // Note: the top-level nullability is oblivious here.
";
var comp = CreateCompilation(new[] { source0, source1, source2 });
verify();

comp = CreateCompilation(new[] { source0, source2, source1 });
verify();

void verify()
{
comp.VerifyDiagnostics(
// (5,23): warning CS8619: Nullability of reference types in value of type 'C' doesn't match target type 'Base<string?>'.
// Base<string?> base2 = new C(); // 1
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "new C()").WithArguments("C", "Base<string?>").WithLocation(5, 23)
);
}
}

[Fact, WorkItem(45960, "https://github.com/dotnet/roslyn/issues/45960")]
public void PartialBaseTypeDifference_04()
{
var source0 = @"
#nullable enable

Base<string, object?> base1 = new C();
Base<string, object> base2 = new C(); // 1
Base<string?, object?> base3 = new C(); // 2

class Base<T, U> { }
";
var source1 = @"
#nullable enable
partial class C : Base<string, object?> { }
";
var source2 = @"
#nullable disable
partial class C : Base<string, object> { }
";
var comp = CreateCompilation(new[] { source0, source1, source2 });
verify();

comp = CreateCompilation(new[] { source0, source2, source1 });
verify();

void verify()
{
comp.VerifyDiagnostics(
// (5,30): warning CS8619: Nullability of reference types in value of type 'C' doesn't match target type 'Base<string, object>'.
// Base<string, object> base2 = new C(); // 1
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "new C()").WithArguments("C", "Base<string, object>").WithLocation(5, 30),
// (6,32): warning CS8619: Nullability of reference types in value of type 'C' doesn't match target type 'Base<string?, object?>'.
// Base<string?, object?> base3 = new C(); // 2
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "new C()").WithArguments("C", "Base<string?, object?>").WithLocation(6, 32)
);
}
}

[Fact, WorkItem(45960, "https://github.com/dotnet/roslyn/issues/45960")]
public void PartialBaseTypeDifference_05()
{
var source0 = @"
#nullable enable

Base<string, object?> base1 = new C(); // 1
Base<string, object> base2 = new C(); // 2
Base<string?, object?> base3 = new C(); // 3

class Base<T, U> { }
";
var source1 = @"
#nullable enable
partial class C : Base<string, object?> { } // 4
";
var source2 = @"
#nullable disable
partial class C : Base<string, object> { }
";
var source3 = @"
#nullable enable
partial class C : Base<string?, object> { }
";
var comp = CreateCompilation(new[] { source0, source1, source2, source3 });
verify();

comp = CreateCompilation(new[] { source0, source3, source2, source1 });
verify();

void verify()
{
comp.VerifyDiagnostics(
// (3,15): error CS0263: Partial declarations of 'C' must not specify different base classes
// partial class C : Base<string, object?> { } // 4
Diagnostic(ErrorCode.ERR_PartialMultipleBases, "C").WithArguments("C").WithLocation(3, 15),
// (4,31): error CS0029: Cannot implicitly convert type 'C' to 'Base<string, object?>'
// Base<string, object?> base1 = new C(); // 1
Diagnostic(ErrorCode.ERR_NoImplicitConv, "new C()").WithArguments("C", "Base<string, object?>").WithLocation(4, 31),
// (5,30): error CS0029: Cannot implicitly convert type 'C' to 'Base<string, object>'
// Base<string, object> base2 = new C(); // 2
Diagnostic(ErrorCode.ERR_NoImplicitConv, "new C()").WithArguments("C", "Base<string, object>").WithLocation(5, 30),
// (6,32): error CS0029: Cannot implicitly convert type 'C' to 'Base<string?, object?>'
// Base<string?, object?> base3 = new C(); // 3
Diagnostic(ErrorCode.ERR_NoImplicitConv, "new C()").WithArguments("C", "Base<string?, object?>").WithLocation(6, 32)
);
}
}

[Fact, WorkItem(45960, "https://github.com/dotnet/roslyn/issues/45960")]
public void PartialBaseTypeDifference_06()
{
var source0 = @"
#nullable enable

Base<S, object?> base1 = new C1();
Base<S, object> base2 = new C1(); // 1

Base<object?, S> base3 = new C2(); // 2
Base<object, S> base4 = new C2();

class Base<T, U> { }

struct S { }
";
var source1 = @"
#nullable enable
partial class C1 : Base<S, object?> { }
RikkiGibson marked this conversation as resolved.
Show resolved Hide resolved
partial class C2 : Base<object, S> { }
RikkiGibson marked this conversation as resolved.
Show resolved Hide resolved
";
var source2 = @"
#nullable disable
partial class C1 : Base<S, object> { }
partial class C2 : Base<object, S> { }
";
var comp = CreateCompilation(new[] { source0, source1, source2 });
verify();

comp = CreateCompilation(new[] { source0, source2, source1 });
verify();

void verify()
{
comp.VerifyDiagnostics(
// (5,25): warning CS8619: Nullability of reference types in value of type 'C1' doesn't match target type 'Base<S, object>'.
// Base<S, object> base2 = new C1(); // 1
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "new C1()").WithArguments("C1", "Base<S, object>").WithLocation(5, 25),
// (7,26): warning CS8619: Nullability of reference types in value of type 'C2' doesn't match target type 'Base<object?, S>'.
// Base<object?, S> base3 = new C2(); // 2
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "new C2()").WithArguments("C2", "Base<object?, S>").WithLocation(7, 26)
);
}
}

[Fact, WorkItem(45960, "https://github.com/dotnet/roslyn/issues/45960")]
public void PartialBaseTypeDifference_07()
{
var source0 = @"
#nullable enable

class Base<T, U> { }

struct S { }
";
var source1 = @"
#nullable enable
partial class C1 : Base<S, object?> { } // 1
RikkiGibson marked this conversation as resolved.
Show resolved Hide resolved
partial class C2 : Base<object, S> { }
";
var source2 = @"
#nullable enable
partial class C1 : Base<
S,
#nullable disable
object> { }

#nullable enable
partial class C2 : Base<
object,
#nullable disable
S> { }
";
var comp = CreateCompilation(new[] { source0, source1, source2 });
verify();

comp = CreateCompilation(new[] { source0, source2, source1 });
verify();

void verify()
{
comp.VerifyDiagnostics(
// (3,15): error CS0263: Partial declarations of 'C1' must not specify different base classes
RikkiGibson marked this conversation as resolved.
Show resolved Hide resolved
// partial class C1 : Base<S, object?> { } // 1
Diagnostic(ErrorCode.ERR_PartialMultipleBases, "C1").WithArguments("C1").WithLocation(3, 15)
);
}
}

[Fact]
public void PartialMethodsWithConstraints_01()
{
Expand Down