Skip to content

Collection expressions: method type re-inference#71214

Merged
jcouv merged 7 commits intodotnet:mainfrom
jcouv:coll-nullability2
Jan 11, 2024
Merged

Collection expressions: method type re-inference#71214
jcouv merged 7 commits intodotnet:mainfrom
jcouv:coll-nullability2

Conversation

@jcouv
Copy link
Member

@jcouv jcouv commented Dec 11, 2023

Implements part of #68786

Overview of the change:

  • Track which nodes are actually target-typed (this helps not register some completion continuations that later trigger assertions).
  • Add NestedVisitResults and StateForLambda to VisitResult.
    • NestedVisitResults allows to gather the VisitResults for elements, so that they can contribute to method type inference (MethodTypeInferrer handles unconverted collection expressions by making inferences from the elements directly). GetArgumentsForMethodTypeInference uses those nested visit results to prepare suitable arguments for method type inference.
    • StateForLambda allows tracking the State just prior to visiting a lambda expression. Previously, this was done with VisitArgumentResult, but that only works at the argument level. Now we need this for elements too.
  • Left for follow-ups (marked with tracking link to Implement nullable analysis for collection literals #68786):
    • Analysis of spreads
    • Check the conversion of elements to some iteration type in Add/initializer scenario (we need to determine the element type, but there may be multiple IEnumerable<T> interfaces). Code below should help once that element type is determined.

Update: there is one IDE test failure, for which I added a corresponding compiler repro. The problem is that the logic in NullableWalker.VisitArguments to get corresponding parameter types doesn't match the logic in Binder.BuildArgumentsForErrorRecovery. When we have a bad call, the binder tries to find corresponding parameter types across the various candidates, but the nullable walker logic only consider the method stored in BoundCall.Method (which is an ErrorMethodSymbol).
Because of this mismatch, the binder was able to convert the collection expression to a parameter type, but the nullable walker isn't, so it asserts because we detect that a converted target-typed collection wasn't fully processed.
As a workaround, the last commit softens an assertion (which was incorrect in main) and allows us to recover without triggering assertions. We could file a follow-up bug for this.

@jcouv jcouv added this to the 17.9 milestone Dec 11, 2023
@jcouv jcouv self-assigned this Dec 11, 2023
@ghost ghost added the untriaged Issues and PRs which have not yet been triaged by a lead label Dec 11, 2023
@jcouv jcouv force-pushed the coll-nullability2 branch from 648151d to a19fb43 Compare December 13, 2023 03:25
@jcouv jcouv force-pushed the coll-nullability2 branch 5 times, most recently from 7a3a0a2 to e2d587d Compare December 17, 2023 17:54
@jcouv jcouv marked this pull request as ready for review December 18, 2023 17:00
@jcouv jcouv requested a review from a team as a code owner December 18, 2023 17:00
}
void M3(string? maybeNull, string notNull)
{
M(ref notNull, [notNull, ""]);
Copy link
Contributor

@cston cston Dec 28, 2023

Choose a reason for hiding this comment

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

notNull

Should this be maybeNull? #Resolved

Julien Couvreur added 2 commits January 8, 2024 12:06
@jcouv jcouv requested a review from cston January 8, 2024 20:21
@jcouv
Copy link
Member Author

jcouv commented Jan 8, 2024

@333fred @RikkiGibson for second review. Thanks


public TypeWithState RValueType => VisitResult.RValueType;
public TypeWithAnnotations LValueType => VisitResult.LValueType;
return new VisitResult(RValueType, lvalueType);
Copy link
Contributor

@cston cston Jan 8, 2024

Choose a reason for hiding this comment

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

Can this be simplified to return new VisitResult(RValueType, lvalueType, StateForLambda);? #Resolved

@jcouv
Copy link
Member Author

jcouv commented Jan 9, 2024

@dotnet/roslyn-compiler for another review. Thanks

@RikkiGibson RikkiGibson self-assigned this Jan 9, 2024
Copy link
Member

@RikkiGibson RikkiGibson left a comment

Choose a reason for hiding this comment

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

Done review pass, just had a few clarifying questions etc

}
return;

bool shouldMakeNotNullRvalue(BoundExpression node) => node.IsSuppressed || node.HasAnyErrors || !IsReachable();
Copy link
Member

Choose a reason for hiding this comment

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

Consider inlining this local function and removing preceding return statement.

""";

// Missing diagnostics for conversion of element to iteration type in Add scenario.
// Tracked by https://github.com/dotnet/roslyn/issues/68786
Copy link
Member

@RikkiGibson RikkiGibson Jan 10, 2024

Choose a reason for hiding this comment

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

This feels like a very common case. Are we planning to address for 17.10? #Resolved

Copy link
Member Author

@jcouv jcouv Jan 10, 2024

Choose a reason for hiding this comment

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

That'll be a question for Chuck.
Let me investigate

Copy link
Member Author

Choose a reason for hiding this comment

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

Will fix the comment. This scenario is already tracked in code in VisitCollectionExpression

Copy link
Member Author

Choose a reason for hiding this comment

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

Btw, the reason this is not handled in this PR is it was not trivial to analyze this conversion given that we don't have the right/corresponding conversion node in the bound tree. I wasn't sure how to do that, maybe I'm missing something.

S<object?> x = [1];
x[0].ToString(); // 1
S<object> y = [null]; // 2
S<object> y = [null];
Copy link
Member

@RikkiGibson RikkiGibson Jan 10, 2024

Choose a reason for hiding this comment

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

Consider matching the format of the previous test, i.e. leaving in the // 2 comments and referencing the tracking issue. #Resolved

Copy link
Member Author

Choose a reason for hiding this comment

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

LDM decided that we should not analyze the Add method in collection expressions, so I don't expect we'll add this diagnostic later.
https://github.com/dotnet/csharplang/blob/main/meetings/2023/LDM-2023-11-15.md#nullability-analysis-of-collection-expressions

Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(8, 28),
// (13,11): warning CS8625: Cannot convert null literal to non-nullable reference type.
// x3 = [null];
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(13, 11)
Copy link
Member

@RikkiGibson RikkiGibson Jan 10, 2024

Choose a reason for hiding this comment

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

Is it expected for this diagnostic to be restored at some point in the future? Consider linking the issue which tracks that. #Resolved

Copy link
Member Author

Choose a reason for hiding this comment

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

Indeed. There should be nullability warnings for the Create method. Will add comment

var comp = CreateCompilation(src).VerifyEmitDiagnostics(
// (13,14): warning CS8618: Non-nullable field 'Element' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.
// public T Element;
Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Element").WithArguments("field", "Element").WithLocation(13, 14)
Copy link
Member

@RikkiGibson RikkiGibson Jan 10, 2024

Choose a reason for hiding this comment

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

warning feels irrelevant to the test, consider adding = null!; to suppress. #Resolved

model.GetSymbolInfo(invocation0).Symbol.ToTestDisplayString(includeNonNullable: true));

var invocation3 = GetSyntax<InvocationExpressionSyntax>(tree, "M([Copy(maybeNull, out var maybeNull2), maybeNull2.ToString()])");
Assert.Equal("System.Object! C.M<System.Object!>(System.Object![]! a)",
Copy link
Member

@RikkiGibson RikkiGibson Jan 10, 2024

Choose a reason for hiding this comment

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

What explains this difference? Perhaps for maybeNull in M2() the flow state is not-null after issuing the warning, resulting in a different inference for the containing M()? #Resolved

Copy link
Member Author

Choose a reason for hiding this comment

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

I think in the first scenario the second element (maybeNul2) contributes a maybe-null to method type inference, but in the second scenario the argument (maybeNull2.ToString()) contributes not-null.

Copy link
Member

@RikkiGibson RikkiGibson left a comment

Choose a reason for hiding this comment

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

I am curious if this PR resolves #71522 (where we saw that expressions were reported to have oblivious nullability, even though they were in a nullable-enabled context.)

(Now that I think of it, this seems unlikely, as the linked issue is complaining about an array-creation, not a collection-expression.)

@jcouv jcouv requested a review from RikkiGibson January 11, 2024 00:18
@jcouv jcouv enabled auto-merge (squash) January 11, 2024 01:00
@jcouv jcouv merged commit ab18bca into dotnet:main Jan 11, 2024
@ghost ghost modified the milestones: 17.9, Next Jan 11, 2024
@Cosifne Cosifne modified the milestones: Next, 17.10 P1 Jan 30, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area-Compilers untriaged Issues and PRs which have not yet been triaged by a lead

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants