Allow overriding members with nullable variance#34718
Conversation
959c234 to
418e01a
Compare
418e01a to
6c4cee4
Compare
|
@dotnet/roslyn-compiler for review #Closed |
|
@dotnet/roslyn-compiler ping for review #Closed |
src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs
Outdated
Show resolved
Hide resolved
...Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol_ImplementationChecks.cs
Outdated
Show resolved
Hide resolved
...Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol_ImplementationChecks.cs
Outdated
Show resolved
Hide resolved
|
@cston Did you have any more comments? #Closed |
src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs
Outdated
Show resolved
Hide resolved
...Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol_ImplementationChecks.cs
Outdated
Show resolved
Hide resolved
...Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol_ImplementationChecks.cs
Outdated
Show resolved
Hide resolved
...Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol_ImplementationChecks.cs
Outdated
Show resolved
Hide resolved
What is the It looks like https://github.com/dotnet/csharplang/blob/master/meetings/2019/LDM-2019-01-07.md suggest that we would want to check nullability against base override. Refers to: src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol_ImplementationChecks.cs:703 in d9ddb61. [](commit_id = d9ddb61, deletion_comment = False) |
src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs
Outdated
Show resolved
Hide resolved
src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs
Outdated
Show resolved
Hide resolved
Can we remove this comment now and resolve the issue once this PR is merged? #Closed Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs:11206 in d9ddb61. [](commit_id = d9ddb61, deletion_comment = False) |
There was a problem hiding this comment.
)); [](start = 131, length = 3)
)); [](start = 131, length = 3)
Consider preserving original formatting #Closed
src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs
Outdated
Show resolved
Hide resolved
|
Done with review pass (iteration 6) #Closed |
2c79032 to
2c8a461
Compare
|
@agocke Could you please resolve merge conflicts and figure out what is wrong with CI failures? #Closed |
Implements a design change where overrides are allowed to change the type of the member as long as there is an implicit nullable reference conversion from the overriding type to the overridden type according to the nullable variance rules. Fixes dotnet#23268
|
@gafter You looked at an earlier iteration but I've made substantial changes since then. Can you take another look? #Closed |
|
ping @gafter #Closed |
|
Looking #Closed |
| overridingMethod is null || | ||
| compilation is null || | ||
| ((CSharpParseOptions)overridingMemberLocation.SourceTree?.Options) | ||
| ?.IsFeatureEnabled(MessageID.IDS_FeatureNullableReferenceTypes) != true) |
There was a problem hiding this comment.
This can be compilation.IsFeatureEnabled(MessageID.IDS_FeatureNullableReferenceTypes) since all parse trees are checked that they have the same version and features. #Resolved
| if (!isValidNullableConversion( | ||
| conversions, | ||
| overridingMethod.RefKind, | ||
| overridingMethod.ReturnTypeWithAnnotations, |
There was a problem hiding this comment.
overridingMethod.ReturnTypeWithAnnotations [](start = 24, length = 42)
Has one of these been alpha-renamed to match the other, e.g. in case the method is generic and returns T?
| for (int i = 0; i < overriddenMethod.ParameterCount; i++) | ||
| { | ||
| var overriddenParameterType = overriddenParameters[i].TypeWithAnnotations; | ||
| var overridingParameterType = overridingParameters[i].TypeWithAnnotations; |
There was a problem hiding this comment.
overridingParameters[i] [](start = 50, length = 23)
Same question: have the methods been alpha-renamed to match each other?
gafter
left a comment
There was a problem hiding this comment.
This needs tests showing the behavior for overriding generic methods, methods in generic classes.
Needs tests for overriding generic methods with variance.
|
|
||
| if (overriddenMember.Kind == SymbolKind.Method && (overriddenMethod = (MethodSymbol)overriddenMember).IsGenericMethod) | ||
| { | ||
| overriddenParameters = overriddenMethod.Construct(((MethodSymbol)overridingMember).TypeArgumentsWithAnnotations).Parameters; |
There was a problem hiding this comment.
overriddenParameters = overriddenMethod.Construct(((MethodSymbol)overridingMember).TypeArgumentsWithAnnotations).Parameters; [](start = 32, length = 124)
It looks like @gafter is correct, there is a problem with overriding generic methods and that is because this logic is not preserved.
There was a problem hiding this comment.
As I mentioned offline, I think this logic is performed on line 955 above, but this code was using overriddenMember for checking, instead of the variable overriddenMethod, which contains the constructed member.
|
@gafter Anything else? #Closed |
|
|
||
| return; | ||
|
|
||
| bool isOrContainsErrorType(TypeSymbol typeSymbol) |
There was a problem hiding this comment.
bool [](start = 12, length = 4)
nit: not related to this PR, static too? #Closed
|
|
||
| return; | ||
|
|
||
| static bool isValidNullableConversion( |
There was a problem hiding this comment.
isValidNullableConversion [](start = 28, length = 25)
If you don't mind, could we move this local function out one level, to avoid nested local functions? #Closed
| // (17,29): warning CS8610: Nullability of reference types in type of parameter 't' doesn't match overridden member. | ||
| // public override List<T> M<T>(List<T> t) => null!; | ||
| Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOnOverride, "M").WithArguments("t").WithLocation(17, 29)); | ||
| } |
There was a problem hiding this comment.
} [](start = 8, length = 1)
Some test ideas:
I think we should cover some generic cases as well:
class C : Base<object_> // where object_ is object? or object! or object~
{
override object_ M() { ... } // ditto
}We should also cover some scenarios with multiple layers of overrides (with various combination of ?, ! and ~ for string_:
class A
{
virtual string_ Property { get; }
}
class B : A
{
override string_ Property { get; }
}
class C : B
{
override string_ Property { get; }
}Could you also add some tests verifying the behavior for callers?
new Derived().Property.ToString(); // warning depends on overriding member, not overridden memberIt would also be good to cover a sublety in isValidNullableConversion: it compares ignoring all differences, except nullability.
class Base
{
virtual object? M() ...
}
class Derived : Base
{
override dynamic M() ...
}
Feels like there should be a mismatch warning here. using System;
public class Derived : Base {
override event Action<string?> Event;
public void M() {
Action<string?> x = Event; // is Event abiding by the contract of Base or of C? Is it safe to give it a null argument?
}
}#Closed Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs:11148 in 0c532f5. [](commit_id = 0c532f5, deletion_comment = False) |
It would be good to verify the result type that NullableWalker gets for Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs:11456 in 0c532f5. [](commit_id = 0c532f5, deletion_comment = False) |
There is no concept of copy for events. Also, it looks like there is a warning for this line: In reply to: 489261259 [](ancestors = 489261259) Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs:11148 in 0c532f5. [](commit_id = 0c532f5, deletion_comment = False) |
I'm not sure what you're arguing (terminology or use case).
Ah, thanks. In reply to: 489263473 [](ancestors = 489263473,489261259) Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs:11148 in 0c532f5. [](commit_id = 0c532f5, deletion_comment = False) |
jcouv
left a comment
There was a problem hiding this comment.
Done with review pass (iteration 6)
Resolved offline. In reply to: 489266871 [](ancestors = 489266871,489263473,489261259) Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs:11148 in 0c532f5. [](commit_id = 0c532f5, deletion_comment = False) |
|
@jcouv Added tests for overriding a constructed type. The test for dynamic/object? is superseded by the test we have in the dynamic tests for object/dynamic. The multiple levels of overrides I have additional tests for. |
Implements a design change where overrides are allowed to change the
type of the member as long as there is an implicit nullable reference
conversion from the overriding type to the overridden type according to
the nullable variance rules.
Fixes #23268
Fixes #30958