Report error for T? when T is unconstrained#28804
Report error for T? when T is unconstrained#28804cston merged 13 commits intodotnet:features/NullableReferenceTypesfrom
Conversation
| <value>Explicit application of 'System.Runtime.CompilerServices.NullableAttribute' is not allowed.</value> | ||
| </data> | ||
| <data name="ERR_NullableUnconstrainedTypeParameter" xml:space="preserve"> | ||
| <value>A nullable type parameter must have a value type or reference type constraint.</value> |
There was a problem hiding this comment.
Consider something more specific to guide users:
"... must be known to be a value or reference type. Consider adding a class, struct or type constraint." #Closed
| public static TypeSymbol VisitType<T>( | ||
| this TypeSymbolWithAnnotations type, | ||
| Func<TypeSymbolWithAnnotations, T, bool, bool> predicate1, | ||
| Func<TypeSymbol, T, bool, bool> predicate2, |
There was a problem hiding this comment.
Can we find better named than predicate1 and predicate2? #Closed
| // The first unconstrained type parameter in the type. | ||
| var typeParameter = this.VisitType( | ||
| (t, a, b) => t.IsAnnotated && t.TypeSymbol.IsUnconstrainedTypeParameter(), | ||
| (t, a, b) => false, |
There was a problem hiding this comment.
Once the parameters have more meaningful names. Please use named arguments here. #Closed
| } | ||
|
|
||
| private static TypeSymbol VisitType<T>( | ||
| TypeSymbolWithAnnotations typeWithAnnotationsOpt, |
There was a problem hiding this comment.
Prototype comment to address once TWA is a struct? #Closed
| public void CheckAllConstraints(ConversionsBase conversions, Location location, DiagnosticBag diagnostics) | ||
| { | ||
| // The first unconstrained type parameter in the type. | ||
| var typeParameter = this.VisitType( |
There was a problem hiding this comment.
Note: This code path only handles a fraction of the cases where T? can appear. (See failing tests.) The diagnostic probably needs to be reported elsewhere. #Resolved
|
@cston test failures appear legitimate. #Closed |
|
@dotnet/roslyn-compiler please review. #Closed |
| DeclaringCompilation.SynthesizeTupleNamesAttribute(type.TypeSymbol)); | ||
| } | ||
|
|
||
| // PROTOTYPE(NullableReferenceTypes): type.ReportAnnotatedUnconstrainedTypeParameterIfAny() |
There was a problem hiding this comment.
Why can't we do this today? #Resolved
There was a problem hiding this comment.
We should be able to do these today, but these were cases where we may need to pass through a DiagnosticBag. It seemed more important to get the general approach for now rather than covering all the corner cases.
In reply to: 205633140 [](ancestors = 205633140)
333fred
left a comment
There was a problem hiding this comment.
LGTM other than the question on the prototype comment additions.
|
@jcouv, please review, thanks. #Closed |
| /// traversal stops and that type is returned from this method. Otherwise if traversal | ||
| /// completes without the predicate returning true for any type, this method returns null. | ||
| /// </summary> | ||
| public static TypeSymbol VisitType<T>( |
There was a problem hiding this comment.
This signature, with two exclusive parameters, seems strange. Could we have one VisitType for TypeSymbol and one for TypeSymbolWithAnnotations?
Both would receive both predicates, and they would be mutually recursive (the overload for TSWA is a trivial wrapper for the other overload).
Alternatively, would it help to bundle the TypeSymbol and TSWA for this API? #Closed
There was a problem hiding this comment.
One predicate is not sufficient in two cases: when the top-level type is a TypeSymbol rather than TypeSymbolWithAnnotations, and when walking up the containing types which are also TypeSymbols.
In reply to: 205834509 [](ancestors = 205834509)
There was a problem hiding this comment.
I didn't mean bundle the predicates (I understand we need to keep two). I meant the TypeSymbol typeOpt and TSWA ... parameters. #Closed
There was a problem hiding this comment.
I agree, it's strange to have two mutually exclusive parameters. But it does allow writing specific helper methods that encapsulate the call to VisitType but that also take either a TypeSymbol or a TypeSymbolWithAnnotations (see TypeSymbolWithAnnotations.ContainsNullableReferenceTypes for instance).
In reply to: 205853384 [](ancestors = 205853384)
| if (returnType.ContainsAnnotatedUnconstrainedTypeParameter() || | ||
| parameters.Any(p => p.Type.ContainsAnnotatedUnconstrainedTypeParameter())) | ||
| { | ||
| TypeSymbolWithAnnotations.ReportAnnotatedUnconstrainedTypeParameter(location, _diagnostics); |
There was a problem hiding this comment.
ReportAnnotatedUnconstrainedTypeParameter [](start = 42, length = 41)
I'm confused. If I read this code correctly, it seems like we're not reporting on the specific location that caused the problem.
Yet, I think this is covered by NullableT_LocalFunction which seems to report a specific diagnostic (either on return type or parameter type).
I don't understand how that works.
Also, it feels strange to report this kind of diagnostic (for lambdas and local functions) during lowering. Could the check be moved into the relevant symbols? #Closed
There was a problem hiding this comment.
Looking at LocalFunctionSymbol, I see that the check is performed there. That's why the NullableT_LocalFunction test works.
Then I wonder which scenarios/tests hit this code path?
In reply to: 205838678 [](ancestors = 205838678)
There was a problem hiding this comment.
| if (parameter.Type.ContainsNullableReferenceTypes()) | ||
| var location = parameter.GetNonNullSyntaxNode().Location; | ||
| var parameterType = parameter.Type; | ||
| parameterType.ReportAnnotatedUnconstrainedTypeParameterIfAny(location, diagnostics); |
There was a problem hiding this comment.
ReportAnnotatedUnconstrainedTypeParameterIfAny [](start = 30, length = 46)
I understand the appeal of bundling this check here (paired with check for existence of Nullable attribute, as in other places). Maybe the method should be renamed to reflect that.
Maybe CheckParametersForNullability? (or some better name)
Or maybe create a new method ParameterHelpersReportAnnotatedUnconstrainedTypeParameters? #Resolved
|
|
||
| internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, DiagnosticBag diagnostics) | ||
| { | ||
| Location getReturnTypeLocation() |
There was a problem hiding this comment.
getReturnTypeLocation() [](start = 25, length = 23)
Not related to this PR: Any idea why we were doing those gymnastics before? Seems really strange. #Closed
| interfaces.Any(t => t.ContainsAnnotatedUnconstrainedTypeParameter())) | ||
| { | ||
| this.DeclaringCompilation.EnsureNullableAttributeExists(diagnostics, Locations[0], modifyCompilation: true); | ||
| TypeSymbolWithAnnotations.ReportAnnotatedUnconstrainedTypeParameter(location, diagnostics); |
There was a problem hiding this comment.
Should we report more specific locations here too? #Closed
There was a problem hiding this comment.
| Func<TypeSymbol, T, bool, bool> typePredicateOpt, | ||
| T arg, | ||
| bool canDigThroughNullable = false) | ||
| { |
There was a problem hiding this comment.
{ [](start = 8, length = 1)
Consider asserting that we must have a Type or a TSWA, but not both. #Closed
jcouv
left a comment
There was a problem hiding this comment.
Done with review pass (iteration 10).
| this.DeclaringCompilation.EnsureNullableAttributeExists(diagnostics, ReturnTypeSyntax.Location, modifyCompilation: true); | ||
| this.DeclaringCompilation.EnsureNullableAttributeExists(diagnostics, location, modifyCompilation: true); | ||
| } | ||
| ParameterHelpers.ReportAnnotatedUnconstrainedTypeParameters(Parameters, diagnostics); |
There was a problem hiding this comment.
ReportAnnotatedUnconstrainedTypeParameters [](start = 29, length = 42)
Is the operator case covered by test? #Resolved
There was a problem hiding this comment.
There was a problem hiding this comment.
The operator case is covered by NullableT_Members. I've added a constructor case to that test.
In reply to: 205909782 [](ancestors = 205909782,205892348)
| T arg, | ||
| bool canDigThroughNullable = false) | ||
| { | ||
| Debug.Assert(((object)typeWithAnnotationsOpt == null) != ((object)typeOpt == null)); |
There was a problem hiding this comment.
== [](start = 57, length = 2)
nit: could be simplified with is null #Resolved
No description provided.