-
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
Report error for T? when T is unconstrained #28804
Report error for T? when T is unconstrained #28804
Conversation
@@ -5363,6 +5363,9 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ | |||
<data name="ERR_ExplicitNullableAttribute" xml:space="preserve"> | |||
<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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prototype comment to address once TWA is a struct? #Closed
@@ -333,6 +333,15 @@ public bool GetUnificationUseSiteDiagnosticRecursive(ref DiagnosticInfo result, | |||
|
|||
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 |
@@ -395,6 +395,8 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r | |||
DeclaringCompilation.SynthesizeTupleNamesAttribute(type.TypeSymbol)); | |||
} | |||
|
|||
// PROTOTYPE(NullableReferenceTypes): type.ReportAnnotatedUnconstrainedTypeParameterIfAny() |
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.
Why can't we do this today? #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 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)
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 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (parameter.Type.ContainsNullableReferenceTypes()) | ||
var location = parameter.GetNonNullSyntaxNode().Location; | ||
var parameterType = parameter.Type; | ||
parameterType.ReportAnnotatedUnconstrainedTypeParameterIfAny(location, diagnostics); |
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.
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
@@ -303,26 +303,24 @@ internal override LexicalSortKey GetLexicalSortKey() | |||
|
|||
internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, DiagnosticBag diagnostics) | |||
{ | |||
Location getReturnTypeLocation() |
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.
getReturnTypeLocation() [](start = 25, length = 23)
Not related to this PR: Any idea why we were doing those gymnastics before? Seems really strange. #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.
{ | ||
this.DeclaringCompilation.EnsureNullableAttributeExists(diagnostics, Locations[0], modifyCompilation: true); | ||
TypeSymbolWithAnnotations.ReportAnnotatedUnconstrainedTypeParameter(location, diagnostics); |
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 we report more specific locations here too? #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.
Func<TypeSymbolWithAnnotations, T, bool, bool> typeWithAnnotationsPredicateOpt, | ||
Func<TypeSymbol, T, bool, bool> typePredicateOpt, | ||
T arg, | ||
bool canDigThroughNullable = 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.
{ [](start = 8, length = 1)
Consider asserting that we must have a Type or a TSWA, but not both. #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.
Done with review pass (iteration 10).
} | ||
ParameterHelpers.ReportAnnotatedUnconstrainedTypeParameters(Parameters, diagnostics); |
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.
ReportAnnotatedUnconstrainedTypeParameters [](start = 29, length = 42)
Is the operator case covered by test? #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.
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 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
== [](start = 57, length = 2)
nit: could be simplified with is null
#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.
LGTM Thanks (iteration 12).
No description provided.