-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Fix nullability check in override/implementation #49723
Conversation
c34b684
to
c5394af
Compare
c5394af
to
c3db802
Compare
Does this also fix #49071? #Resolved |
@RikkiGibson Thanks! The partials scenario can indeed be fixed with same approach. I'll update this PR> #Resolved |
} | ||
|
||
[Fact] | ||
public void DefaultParameterValue() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📝 some of the tests that I added are not so relevant to this new PR, I just moved all the new tests from my earlier PR. Seems okay to keep them. #Resolved
@@ -479,6 +483,10 @@ internal TypeWithAnnotations SubstituteTypeCore(AbstractTypeMap typeMap) | |||
{ | |||
newAnnotation = NullableAnnotation; | |||
} | |||
else if (newTypeWithModifiers.NullableAnnotation == NullableAnnotation.Ignored) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
newTypeWithModifiers.NullableAnnotation == NullableAnnotation.Ignore [](start = 21, length = 68)
It looks like we already have a special handling for indexed type parameters that has the same effect. If that is the case, I think we should reuse this mechanism rather than going through the trouble of dealing with the new annotation. We might need to use ConstructedFrom in diagnostics in affected components though. #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can't use indexed type parameters because (1) they don't have names, and (2) they don't have constraints (so the logic in AreParameterAnnotationsCompatible
which simulates an assignment produces wrong values/states in GetParameterState
).
We could fix the first problem by introducing a new type parameter symbol. But the second problem is tougher, and I don't see a good way to solve it.
So using a new annotation seems a better fit to solve this problem. #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can't use indexed type parameters because (1) they don't have names, and (2) they don't have constraints (so the logic in AreParameterAnnotationsCompatible which simulates an assignment produces wrong values/states in GetParameterState).
I would like to get a better understanding of these issues. Let's sync offline
In reply to: 535501474 [](ancestors = 535501474)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be possible to remove the special handling for indexed type parameters and use the new annotation instead?
In reply to: 535509257 [](ancestors = 535509257,535501474)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be possible to remove the special handling for indexed type parameters and use the new annotation instead?
Interesting idea. Thanks. Pushed a commit for that change #Resolved
@@ -721,7 +721,7 @@ private static void PartialMethodConstraintsChecks(SourceOrdinaryMethodSymbol de | |||
} | |||
|
|||
var typeParameters2 = implementation.TypeParameters; | |||
var indexedTypeParameters = IndexedTypeParameterSymbol.Take(arity); | |||
var indexedTypeParameters = TypeMap.TypeParametersAsTypeSymbolsWithIgnoredAnnotations(IndexedTypeParameterSymbol.Take(arity)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TypeParametersAsTypeSymbolsWithIgnoredAnnotations [](start = 48, length = 49)
It looks like we are missing a change in CompilationContext in EE #Closed
@@ -721,7 +721,7 @@ private static void PartialMethodConstraintsChecks(SourceOrdinaryMethodSymbol de | |||
} | |||
|
|||
var typeParameters2 = implementation.TypeParameters; | |||
var indexedTypeParameters = IndexedTypeParameterSymbol.Take(arity); | |||
var indexedTypeParameters = TypeMap.TypeParametersAsTypeSymbolsWithIgnoredAnnotations(IndexedTypeParameterSymbol.Take(arity)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Take [](start = 125, length = 4)
Consider changing what this method returns instead. It looks like majority of call sites actually need TypeWithAnnotations. For the case of just type parameters, we can add a special helper. #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was intentionally suggesting to keep the same name. There are often overloads that can handle arrays of both types, however we want default case to be TypeWithAnnotations with an ignored annotation. This way, every consumer of this method, existing or added later will be automatically guided into the right direction and will not require changes. Changes will be required only for exceptions, which aren't that many.
In reply to: 535737016 [](ancestors = 535737016)
@@ -32,5 +32,10 @@ internal enum NullableAnnotation : byte | |||
/// Type is annotated with '?' - string?, T?. | |||
/// </summary> | |||
Annotated, | |||
|
|||
/// <summary> | |||
/// Used for indexed type parameters and used locally in override/implementation checks. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Used for indexed type parameters and used locally in override/implementation checks. [](start = 12, length = 84)
Should probably provide details about how exactly it is ignored. So that one could predict the impact of usage of this annotation #Closed
NullableAnnotation.Oblivious when type.IsValueType => CodeAnalysis.NullableAnnotation.NotAnnotated, | ||
NullableAnnotation.Oblivious => CodeAnalysis.NullableAnnotation.None, | ||
|
||
// May occur as type argument in symbols in diagnostic |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// May occur as type argument in symbols in diagnostic [](start = 16, length = 54)
I think we should be able to avoid this if we use ConstructedFrom for diagnostic instead. #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should really make an effort to not leak a symbol with an annotation like that. Otherwise, equality is likely to misbehave, etc.
In reply to: 536200456 [](ancestors = 536200456)
Done with review pass (commit 8) #Closed |
@@ -1770,7 +1772,7 @@ internal static void CheckNullableReferenceTypeMismatchOnImplementingMember(Type | |||
|
|||
if (implementedMethod.IsGenericMethod) | |||
{ | |||
implementedMethod = implementedMethod.Construct(implementingMethod.TypeArgumentsWithAnnotations); | |||
implementedMethod = implementedMethod.Construct(TypeMap.TypeParametersAsTypeSymbolsWithIgnoredAnnotations(implementingMethod.TypeParameters)); | |||
} | |||
|
|||
SourceMemberContainerTypeSymbol.CheckValidNullableMethodOverride( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It feels like the code in this function should also dig to constructed from as appropriate. #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The lambdas that are passed in do use ConstrcutedFrom (actually, they don't need to dig into derived/implementating method, I was hasty and will revert that part). The rest of the method is fine (see earlier comment).
In reply to: 536304396 [](ancestors = 536304396)
Done with review pass (commit 9) #Closed |
In reply to: 738957157 [](ancestors = 738957157) Refers to: src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs:690 in dbfb16b. [](commit_id = dbfb16b, deletion_comment = False) |
This local function only reports on return and parameter types, but does not use method symbols in diagnostics In reply to: 738956098 [](ancestors = 738956098) Refers to: src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol_ImplementationChecks.cs:991 in dbfb16b. [](commit_id = dbfb16b, deletion_comment = False) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM (commit 10)
var method3 = model.GetSymbolInfo(invocations[1]).Symbol; | ||
Assert.True(method3.IsDefinition); | ||
|
||
// Note: definitions and subsituted symbols are not equal even when ignoring nullability |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Note: definitions and subsituted symbols are not equal even when ignoring nullability [](start = 12, length = 88)
@AlekseyTs @cston FYI This behavior seems wrong, but I didn't file an issue for this because from our discussion, trying to adjust this seems very risky (likely performance impact and other unknowns). #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This behavior seems wrong, but I didn't file an issue for this because from our discussion, trying to adjust this seems very risky (likely performance impact and other unknowns).
I do not remember us discussing this, unless this somehow involves "Ignored" annotation. It doesn't look like it does. I think we should fix this. Not necessary in this PR, unless the behavior is related to the changes in it.
In reply to: 536369020 [](ancestors = 536369020)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay. Thanks for the clarification. Filed #49798
Right, we discussed this for comparisons dealing with Ignored annotation, but we have the same problem for existing annotations (shortcuts we take when one of the symbols is a definition). #Resolved
Assert.True(method1.Equals(method3, SymbolEqualityComparer.IncludeNullability)); | ||
Assert.True(method3.Equals(method1, SymbolEqualityComparer.Default)); | ||
Assert.True(method3.Equals(method1, SymbolEqualityComparer.IncludeNullability)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For true
cases, please assert GetHashCode equality #Resolved
NullableAnnotation.Oblivious when type.IsValueType => CodeAnalysis.NullableAnnotation.NotAnnotated, | ||
NullableAnnotation.Oblivious => CodeAnalysis.NullableAnnotation.None, | ||
|
||
NullableAnnotation.Ignored => CodeAnalysis.NullableAnnotation.None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what motivated the choice here to not throw UnexpectedValue in retail builds? #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what motivated the choice here to not throw UnexpectedValue in retail builds?
Not worth crashing compiler over this
In reply to: 536442380 [](ancestors = 536442380)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Returning None
would be the right behavior if we expected symbols to leak through public APIs in some way, so doesn't seem harmful to handle (I don't think it's worth crashing if we have such a bug).
I don't feel very strongly about it
In reply to: 536442380 [](ancestors = 536442380)
@@ -27,6 +27,11 @@ internal static ImmutableArray<TypeWithAnnotations> TypeParametersAsTypeSymbolsW | |||
return typeParameters.SelectAsArray((tp) => TypeWithAnnotations.Create(tp)); | |||
} | |||
|
|||
internal static ImmutableArray<TypeWithAnnotations> TypeParametersAsTypeSymbolsWithIgnoredAnnotations(ImmutableArray<TypeParameterSymbol> typeParameters) | |||
{ | |||
return typeParameters.SelectAsArray((tp) => TypeWithAnnotations.Create(tp, NullableAnnotation.Ignored)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: static
@@ -37280,8 +37280,6 @@ public class Derived : Base | |||
} | |||
"; | |||
var comp = CreateNullableCompilation(new[] { NotNullWhenAttributeDefinition, source }); | |||
// Note: we're missing warnings on F1 because the overridden method with substituted T from Derived has an oblivious T | |||
// https://github.com/dotnet/roslyn/issues/41368 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't look like new diagnostics were added here. Was this issue already solved before this PR? #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment was incorrect or out-of-date (we already had the appropriate warnings on F1 for this scenario)
In reply to: 536444553 [](ancestors = 536444553)
@@ -203,7 +203,7 @@ public interface ISymbol : IEquatable<ISymbol?> | |||
ISymbol OriginalDefinition { get; } | |||
|
|||
void Accept(SymbolVisitor visitor); | |||
TResult Accept<TResult>(SymbolVisitor<TResult> visitor); | |||
TResult? Accept<TResult>(SymbolVisitor<TResult> visitor); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We may want to test insert this change before merging to roslyn master, so that we can prepare fixes for any VS-side build breaks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be okay (I've run a test insertion on previous PR with similar effect), but it's good to be sure. Started https://devdiv.visualstudio.com/DefaultCollection/DevDiv/_build/results?buildId=4276225&view=results
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Succeeded. I'll go ahead and merge
This PR focuses on user-facing issues related to type substitution affecting override/implementation checks, but without attempting to simplify type substitution at the same time.
Fixes #41368
Fixes #49131
Fixes #49071
Filed #49722 (type of
this
)