Skip to content

MaybeNullWhen and NotNullWhen attribute affects callers#36172

Merged
jcouv merged 4 commits intodotnet:masterfrom
jcouv:cond-attributes
Jun 8, 2019
Merged

MaybeNullWhen and NotNullWhen attribute affects callers#36172
jcouv merged 4 commits intodotnet:masterfrom
jcouv:cond-attributes

Conversation

@jcouv
Copy link
Copy Markdown
Member

@jcouv jcouv commented Jun 5, 2019

Design notes:

  1. In an invocation, we first visit the arguments and save the results. We unsplit after each argument, except for those marked with AssertsTrue/AssertsFalse (which will be renamed to DoesNotReturnIf(bool) later on) where we drop half of the state.

  2. We then do type inference.

  3. We then validate inbound values and conversions, considering pre-condition attributes. This all happens in an unsplit state.

  4. We then validate outbound values and apply post-condition attributes:

    • for ref and out arguments, we track an assignment from parameter to argument
    • for in/by-value, we don't do any assignment, but we can still learn from various attributes

Relates to #35816
LDM notes: https://github.com/dotnet/csharplang/blob/master/meetings/2019/LDM-2019-05-15.md

Fixes #35949 (complexity issue with lambdas in invocations with nullability annotation attributes) because VisitArgumentsEvaluateHonoringAnnotations used to visit arguments more, but that was removed. We now visit lambdas as often as in initial binding.

@jcouv jcouv added Area-Compilers PR For Personal Review Only The PR doesn’t require anyone other than the developer to review it. Feature - Nullable Reference Types Nullable Reference Types labels Jun 5, 2019
@jcouv jcouv added this to the 16.2.P3 milestone Jun 5, 2019
@jcouv jcouv self-assigned this Jun 5, 2019
@jcouv jcouv force-pushed the cond-attributes branch 6 times, most recently from 07af513 to 9bef0fb Compare June 5, 2019 22:33
@jcouv jcouv marked this pull request as ready for review June 5, 2019 22:33
@jcouv jcouv requested a review from a team as a code owner June 5, 2019 22:33
@jcouv jcouv removed the PR For Personal Review Only The PR doesn’t require anyone other than the developer to review it. label Jun 5, 2019
@jcouv jcouv force-pushed the cond-attributes branch 2 times, most recently from 4115162 to 2f86a27 Compare June 6, 2019 14:45
@jcouv
Copy link
Copy Markdown
Member Author

jcouv commented Jun 6, 2019

@dotnet/roslyn-compiler for review. I'm still adding a few tests, but the change should be ready otherwise. I want to merge this before the weekend (6/10 deadline for corefx). Thanks #Resolved

{
builder.Add(VisitArgumentEvaluate(arguments[i], GetRefKind(refKindsOpt, i), preserveConditionalState: false));
(_, _, FlowAnalysisAnnotations parameterAnnotations) = GetCorrespondingParameter(i, parameters, argsToParamsOpt, expanded);

Copy link
Copy Markdown
Member

@gafter gafter Jun 6, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps simpler

                FlowAnalysisAnnotations parameterAnnotations = GetCorrespondingParameter(i, parameters, argsToParamsOpt, expanded).Annotations;

#Resolved

// check whether parameter would unsafely let a null out in the worse case
if (!argument.IsSuppressed)
{
ReportNullableAssignmentIfNecessary(parameterValue, lValueType, applyPostConditionsUnconditionally(parameterWithState, parameterAnnotations), useLegacyWarnings: false);
Copy link
Copy Markdown
Member

@gafter gafter Jun 6, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

false [](start = 189, length = 5)

Should this be UseLegacyWarnings(argument, result.LValueType)? In case the parameter is a local, I would expect it to be a legacy warning. (I'm not sure why we call it a legacy warning. I think the meaning is that legacy is a non-safety warning) #Resolved


void trackNullableStateForAssignment(BoundExpression parameterValue, TypeWithAnnotations lValueType, int targetSlot, TypeWithState parameterWithState, bool isSuppressed, FlowAnalysisAnnotations parameterAnnotations)
{
if (!IsConditionalState && !hasConditionalPostCondition(parameterAnnotations))
Copy link
Copy Markdown
Member

@gafter gafter Jun 6, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IsConditionalState [](start = 21, length = 18)

Is there a risk of duplicate diagnostics on following parameters? #Resolved

Copy link
Copy Markdown
Member Author

@jcouv jcouv Jun 7, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there is. Tracking assignments affects state, but does not produce diagnostics. #Resolved


return typeWithState;
}

Copy link
Copy Markdown
Member

@gafter gafter Jun 6, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These could be collapsed into a single function if you pass the whenTrue and whenFalse enum bits in as a parameter. #WontFix

Copy link
Copy Markdown
Member Author

@jcouv jcouv Jun 7, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm tempted to leave this as-is. I agree the code could be factored more, but I think it would become less readable. #Resolved

Copy link
Copy Markdown
Member

@gafter gafter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

@jcouv
Copy link
Copy Markdown
Member Author

jcouv commented Jun 7, 2019

Thanks Neal!
@dotnet/roslyn-compiler for a second review. I'm trying to merge this today so that corefx can consume it on Monday. Thanks #Resolved

{
_ = F1(t1, out var s2)
? s2.ToString() // 1
: s2.ToString(); // 2
Copy link
Copy Markdown
Member

@RikkiGibson RikkiGibson Jun 7, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s2.ToString(); // 2 [](start = 14, length = 19)

I don't understand why there would be a warning here? #Resolved

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Imagine that M1 is invoked with T = string?. Then we're looking at F1<string?>(t1, out string? t2) which yields a maybe-null t2 in both branches.


In reply to: 291751778 [](ancestors = 291751778)

Copy link
Copy Markdown
Member

@agocke agocke left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, with the assumption that design changes will come in later

UseRvalueOnly(argument); // force use of flow result
}
else
switch (annotations & (FlowAnalysisAnnotations.AssertsTrue | FlowAnalysisAnnotations.AssertsFalse))
Copy link
Copy Markdown
Member

@agocke agocke Jun 8, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could annotations have both AssertsTrue and AssertsFalse? #Resolved

Copy link
Copy Markdown
Member Author

@jcouv jcouv Jun 8, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. It looks like I unintentionally changed the behavior for that scenario. Added a test and fixed it. #Resolved

if ((annotations & FlowAnalysisAnnotations.MaybeNull) != 0)
{
// MaybeNull and MaybeNullWhen
return TypeWithState.Create(typeWithState.Type, NullableFlowState.MaybeNull);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned offline, this should produce a nullable type, if one exists, right?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a note to the work items list (#35816) and started thread with LDM.

if ((annotations & FlowAnalysisAnnotations.NotNull) == FlowAnalysisAnnotations.NotNull)
{
// NotNull
return TypeWithState.Create(typeWithState.Type, NullableFlowState.NotNull);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here as well?

@jcouv jcouv merged commit 9bb1b37 into dotnet:master Jun 8, 2019
@jcouv jcouv deleted the cond-attributes branch June 8, 2019 19:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Nullable reference types cause exponential compile / analysis time even when disabled

4 participants