Support nullable annotations on unconstrained type parameters#45993
Support nullable annotations on unconstrained type parameters#45993jaredpar merged 28 commits intodotnet:release/dev16.8-preview1from
Conversation
c22cd86 to
eb3bf46
Compare
4b2e7a2 to
378b787
Compare
src/Compilers/CSharp/Test/Semantic/Semantics/UninitializedNonNullableFieldTests.cs
Show resolved
Hide resolved
src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs
Show resolved
Hide resolved
| /// T where T : IComparable? => true | ||
| /// </summary> | ||
| public static bool IsTypeParameterDisallowingAnnotation(this TypeSymbol type) | ||
| public static bool IsTypeParameterDisallowingAnnotation(this TypeSymbol type) // The method name is misleading since annotations are allowed always. |
There was a problem hiding this comment.
Think we should move this comment to a <remarks> section. #Resolved
| /// If this is a lazy nullable type pending resolution, forces this to be resolved. | ||
| /// </summary> | ||
| public void TryForceResolveAsNullableValueType() | ||
| public void TryForceResolve(bool asValueTypeNotReferenceType) |
There was a problem hiding this comment.
Consider asValueType. #Resolved
| ? NullableAnnotation.NotAnnotated : NullableAnnotation.Annotated; | ||
| if (Type is null) | ||
| { | ||
| return TypeWithAnnotations.Create(Type, NullableAnnotation.Annotated); |
There was a problem hiding this comment.
| return TypeWithAnnotations.Create(Type, NullableAnnotation.Annotated); | |
| return TypeWithAnnotations.Create(null, NullableAnnotation.Annotated); |
Why do we unconditionally use NullableAnnotation.Annotated here for null types? #Resolved
src/Compilers/CSharp/Test/Semantic/Semantics/UninitializedNonNullableFieldTests.cs
Show resolved
Hide resolved
| var formatWithNullableModifier = formatWithoutModifiers.AddMiscellaneousOptions(SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier); | ||
| var formatWithBothModifiers = formatWithNullableModifier.AddMiscellaneousOptions(SymbolDisplayMiscellaneousOptions.IncludeNotNullableReferenceTypeModifier); | ||
|
|
||
| verify("C.F0", "T F0<T>()", "T F0<T>()", "T F0<T>()"); |
There was a problem hiding this comment.
Why is the final one not T! F0<T>()? #ByDesign
There was a problem hiding this comment.
The behavior of SymbolDisplayMiscellaneousOptions.IncludeNotNullableReferenceTypeModifier is unchanged. The type could be reported as T! here, and in T F8<T>() where T : notnull, independent of this PR.
In reply to: 455921736 [](ancestors = 455921736)
|
|
||
| if (Type is null) | ||
| { | ||
| return NullableAnnotation.NotAnnotated; |
There was a problem hiding this comment.
Why do we prefer NotAnnotated here over Oblivious? #Resolved
There was a problem hiding this comment.
I've removed the explicit Type is null case so the behavior should match the current behavior which returns this.NullableAnnotation. That said, I suspect NullableAnnotation is typically NotAnnotated when Type is null since that is the default value for the struct.
In reply to: 456081013 [](ancestors = 456081013)
| [InlineData(false)] | ||
| [InlineData(true)] |
There was a problem hiding this comment.
| [InlineData(false)] | |
| [InlineData(true)] | |
| [CombinatorialData] | |
| ``` #Resolved |
| </data> | ||
| <data name="ERR_NullableUnconstrainedTypeParameter" xml:space="preserve"> | ||
| <value>A nullable type parameter must be known to be a value type or non-nullable reference type. Consider adding a 'class', 'struct', or type constraint.</value> | ||
| <value>A nullable type parameter must be known to be a value type or non-nullable reference type. Please use language version '{0}' or greater, or consider adding a 'class', 'struct', or type constraint.</value> |
There was a problem hiding this comment.
Please use language version '{0}' [](start = 102, length = 34)
I think we should simply use regular language version message with specific feature name, etc. This way IDE will be able to suggest the change language version fix. #Closed
There was a problem hiding this comment.
This message has helpful suggestions for using T? in C#8 which the general language version message would not. We can update the IDE to recognize this ErrorCode if necessary.
In reply to: 456600909 [](ancestors = 456600909)
There was a problem hiding this comment.
| <value>null pointer constant pattern</value> | ||
| </data> | ||
| <data name="IDS_FeatureDefaultTypeParameterConstraint" xml:space="preserve"> | ||
| <value>default type parameter constraints</value> |
There was a problem hiding this comment.
default type parameter constraints [](start = 11, length = 34)
It feels like the name of the feature isn't very clear, especially that default is used in not very common cases. For example, a user typed T? and compiler reports that "default type parameter constraints" aren't supported by the language version. What constraints the message is talking about? This isn't a constraint. #Closed
There was a problem hiding this comment.
There was a problem hiding this comment.
This message is only reported for uses of where T : default. For uses of T?, we continue to report ErrorCode.ERR_NullableUnconstrainedTypeParameter.
In reply to: 456603253 [](ancestors = 456603253,456602711)
| return _syntaxFactory.ClassOrStructConstraint(SyntaxKind.ClassConstraint, classToken, questionToken); | ||
| case SyntaxKind.DefaultKeyword: | ||
| var defaultToken = this.EatToken(); | ||
| return _syntaxFactory.DefaultConstraint(defaultToken); |
There was a problem hiding this comment.
_syntaxFactory.DefaultConstraint(defaultToken) [](start = 27, length = 46)
Should we check language version here? #Closed
There was a problem hiding this comment.
The version check is in BindTypeParameterConstraints().
In reply to: 456659327 [](ancestors = 456659327)
There was a problem hiding this comment.
The version check is in BindTypeParameterConstraints().
I realized that. The question is why? Usually, for simple cases like that we perform language version checks in the parser.
In reply to: 456693574 [](ancestors = 456693574,456659327)
| case MessageID.IDS_FeatureRecords: | ||
| case MessageID.IDS_FeatureStaticAnonymousFunction: // syntax check | ||
| case MessageID.IDS_FeatureModuleInitializers: // semantic check on method attribute | ||
| case MessageID.IDS_FeatureDefaultTypeParameterConstraint: |
There was a problem hiding this comment.
case MessageID.IDS_FeatureDefaultTypeParameterConstraint: [](start = 16, length = 57)
Consider adding a comment if this is not a parsing time check #Closed
| { | ||
| if (type.Type.IsTypeParameterDisallowingAnnotation()) | ||
| { | ||
| var requiredVersion = MessageID.IDS_FeatureDefaultTypeParameterConstraint.RequiredVersion(); |
There was a problem hiding this comment.
IDS_FeatureDefaultTypeParameterConstraint [](start = 48, length = 41)
This is somewhat confusing because default constraint isn't involved. #Closed
There was a problem hiding this comment.
| private static bool IsPossiblyNullableTypeTypeParameter(in TypeWithAnnotations typeWithAnnotations) | ||
| { | ||
| var type = typeWithAnnotations.Type; | ||
| return type is object && |
There was a problem hiding this comment.
return type is object && [](start = 12, length = 24)
It looks like we dropped IsNotAnnotated check. Is this intentional? Could you please elaborate? #Closed
There was a problem hiding this comment.
It looks like we dropped IsNotAnnotated check. Is this intentional?
Yes. Callers above are explicitly checking NullableAnnotation, and requiring NullableAnnotation == NullableAnnotation.NotAnnotated here would mean T and T? would be considered identical in HasTopLevelNullabilityIdentityConversion(). For instance, that would allow assigning IEnumerable<T?> to IEnumerable<T> in UnconstrainedTypeParameter_21().
In reply to: 456696256 [](ancestors = 456696256)
b9d17f3 to
6fff28c
Compare
440cb3b to
280bdf4
Compare
|
The spanish failure is in UpdaterService which is flaky. Will filean issue shortly. Merging. |
…-pointers * upstream/master: (207 commits) Update argument state when parameter has not-null type (dotnet#46072) Fix TypeWithAnnotations.ToTypeWithState() for (untyped) null literal (dotnet#46344) Update README (dotnet#46136) Revert "Revert "Support nullable annotations on unconstrained type parameters"" Revert "Support nullable annotations on unconstrained type parameters (dotnet#45993)" Fix type in publish data Update VSIXExpInstaller version to one available on ADO Update publish data for 16.8 Update version of RichCodeNav.EnvVarDump A fixed initializer must be bound to its natural type (dotnet#46293) Update features merged into 16.7p4 (dotnet#46229) Async-streams: disposal should continue without jump within a finally (dotnet#46188) Recommend default in type constraint, but not record (dotnet#46311) Add use site diagnostics to IsUnmanaged (dotnet#46114) Add another flaky test. Ensure NuGet connections use TLS 1.2 Update to Microsoft.CodeAnalysis.Testing 1.0.1-beta1.20374.2 Skip flaky test. Fix build break. (dotnet#46303) Skip a flaky test Relates to dotnet#46304 ...
…to function-pointer-type-lookup * upstream/features/function-pointers: (212 commits) Correct public API number. Update argument state when parameter has not-null type (dotnet#46072) Fix TypeWithAnnotations.ToTypeWithState() for (untyped) null literal (dotnet#46344) Update README (dotnet#46136) Revert "Revert "Support nullable annotations on unconstrained type parameters"" Revert "Support nullable annotations on unconstrained type parameters (dotnet#45993)" Fix type in publish data Update VSIXExpInstaller version to one available on ADO Update publish data for 16.8 Update version of RichCodeNav.EnvVarDump A fixed initializer must be bound to its natural type (dotnet#46293) Update features merged into 16.7p4 (dotnet#46229) Async-streams: disposal should continue without jump within a finally (dotnet#46188) Recommend default in type constraint, but not record (dotnet#46311) Add use site diagnostics to IsUnmanaged (dotnet#46114) Add another flaky test. Ensure NuGet connections use TLS 1.2 Update to Microsoft.CodeAnalysis.Testing 1.0.1-beta1.20374.2 Skip flaky test. Fix build break. (dotnet#46303) ...
|
Why was T? on unconstrained types implemented as meaning ValueOrDefault instead of ValueOrNull? For ValueOrNull, we're still back to the old method of duplicating classes with struct/class constraints. |
|
Could you please share some specific scenarios that are negatively affected by this? |
|
The following code compiles but doesn't behave as one would expect. Previously it would fail to compile, now it just doesn't do what I'd expect it to. The solution is to write it like this This solution however requires a lot of code copy/paste, and inheritance references can get complicated, especially when the classes contain considerable code. And the syntax is weird for string: |
Support
?annotations on unconstrained type parameter references:?annotation is allowed for type parameters that are not constrained to reference types or value types?annotation is an error when used on unconstrained type parameter with/langversion:8Support
where T : defaultconstraint:defaultconstraint is allowed for method type parameters in overridden or explicitly implemented methods to disambiguate between overloads defined in the base or interfacedefaultconstraint is an error with/langversion:8defaultconstraint is an error when used outside of method overrides or explicit implementationsdefaultconstraint is an error when the type parameter is constrainedFixes #29146