Skip to content

Add support for nullable analysis in interpolated string handler constructors#57780

Merged
333fred merged 18 commits intodotnet:mainfrom
333fred:interp-nullable-improvements
Dec 15, 2021
Merged

Add support for nullable analysis in interpolated string handler constructors#57780
333fred merged 18 commits intodotnet:mainfrom
333fred:interp-nullable-improvements

Conversation

@333fred
Copy link
Copy Markdown
Member

@333fred 333fred commented Nov 15, 2021

Implements most of #54583. Arguments with slots are treated like we do receivers: as though the previous argument value has already been loaded, and therefore postcondition attributes do not flow back to the original argument position (the effect of this can be seen in StringInterpolation_14 and _20), but future references to those locations will see the effects of the attribute.

I additionally looked at updating region analysis as part of this PR to see if I could share data between these passes, but I don't believe we can do much sharing here. There's too much nullable-specific with finding the current state of the expression being used for the mapping to work.

…tructors

Fixes dotnet#54583. Arguments with slots are treated like we do receivers: as though the previous argument value has already been loaded, and therefore postcondition attributes do not flow back to the original argument position (the effect of this can be seen in StringInterpolation_14 and _20), but future references to those locations will see the effects of the attribute.

I additionally looked at updating region analysis as part of this PR to see if I could share data between these passes, but I don't believe we can do much sharing here. There's too much nullable-specific with finding the current state of the expression being used for the mapping to work.
@333fred 333fred requested a review from a team as a code owner November 15, 2021 23:05
@ghost ghost added the Area-Compilers label Nov 15, 2021
@AlekseyTs
Copy link
Copy Markdown
Contributor

AlekseyTs commented Nov 19, 2021

@333fred It looks like there are legitimate test failures.


In reply to: 974333726

@AlekseyTs
Copy link
Copy Markdown
Contributor

AlekseyTs commented Nov 19, 2021

Arguments with slots are treated like we do receivers: as though the previous argument value has already been loaded, and therefore postcondition attributes do not flow back to the original argument position

Is this something that should be approved by LDM? Also, do we plan to update nullable specification?


In reply to: 974418424

@AlekseyTs
Copy link
Copy Markdown
Contributor

Done with review pass (commit 2)

@333fred 333fred marked this pull request as draft November 19, 2021 23:59
@333fred
Copy link
Copy Markdown
Member Author

333fred commented Nov 19, 2021

Converting this to a draft PR while I'm out on vacation.


In reply to: 974547865

@AlekseyTs
Copy link
Copy Markdown
Contributor

Done with review pass (commit 15)

// handler semantics), and then evaluate to the handler flow capture temp.

SpillEvalStack();
SpillEvalStack(spillDeclarationExpressions: true);
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs Dec 10, 2021

Choose a reason for hiding this comment

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

spillDeclarationExpressions: true

This doesn't feel sufficient. This parameter will make the difference only for entries that haven't been spilled already. And there is no guarantee that that will be the case. For example, if an argument following a DeclarationExpression has a control flow, it will case spilling for the DeclarationExpression and after that this call won't do anything about it. It should be easy to come up with a test case that exposes this issue.
Some options to address this that come to mind:

  • Unconditionally unwrap DeclarationExpressions during spilling. If we can run into tuple-like DeclarationExpressions, perhaps we can skip those.
  • Preemptively unwrap DeclarationExpressions when we push arguments to the stack.
  • Handle spilling of DeclarationExpressionout of order on demand once we realize that we need to use it multiple times due to the interpolation.
    We can discuss our options in more details offline. #Closed

@333fred
Copy link
Copy Markdown
Member Author

333fred commented Dec 10, 2021

@AlekseyTs I implemented the change we talked about offline. Please take another look when you get the chance.

}

private void SpillEvalStack()
private void SpillEvalStack(bool spillDeclarationExpressions = false)
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs Dec 10, 2021

Choose a reason for hiding this comment

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

bool spillDeclarationExpressions = false

Is the parameter still necessary? #Closed

{
public static void M1(object o1, object o2)
/*<bind>*/{
C.M(out int i, o1 ?? o2, $""literal{1}"");
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs Dec 10, 2021

Choose a reason for hiding this comment

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

);

Consider adding a test with declaration expression between two interpolations and also a test demonstrating that declaration expressions at the end survive. #Closed

@AlekseyTs
Copy link
Copy Markdown
Contributor

AlekseyTs commented Dec 10, 2021

    private static readonly Func<IArgumentOperation, IOperation> UnwrapArgument = UnwrapArgumentDoNotCaptureDirectly;

Is this delegate used?


In reply to: 991351580 #Closed


Refers to: src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs:2092 in 733946f. [](commit_id = 733946f, deletion_comment = False)

// this check is more complexity than this scenario needs.
if (i < lastIndexForSpilling)
{
SpillEvalStack(spillDeclarationExpressions: true);
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs Dec 10, 2021

Choose a reason for hiding this comment

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

SpillEvalStack(spillDeclarationExpressions: true);

Instead of doing this, I would simply conditionally unwrap declaration expressions before calling VisitRequired above. #Closed

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Leaving the logic in SpillEvalStack feels fragile because it will continue to be a trap for user that might think that passing true actually provides guarantees on its own. Another shortcoming is that this call is going to unwrap declaration expressions that do not belong to this argument list. For example, arguments of an enclosing call. Please add a test case for this scenario.

@AlekseyTs
Copy link
Copy Markdown
Contributor

Done with review pass (commit 16).

@333fred
Copy link
Copy Markdown
Member Author

333fred commented Dec 10, 2021

@AlekseyTs addressed feedback.


In reply to: 991353417

// have unexpected affects on consumers. So we limit the spilling to those indexes before the last interpolated string
// handler. We _could_ limit this further by only spilling declaration expressions if the handler in question actually
// referenced a specific declaration expression in the argument list, but we think that the complexity in implementing
// this check is more complexity than this scenario needs.
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs Dec 10, 2021

Choose a reason for hiding this comment

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

complexity

"complexity" is used twice. Consider rephrasing.

Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs left a comment

Choose a reason for hiding this comment

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

LGTM (commit 17), assuming CI is passing.

@333fred
Copy link
Copy Markdown
Member Author

333fred commented Dec 13, 2021

@dotnet/roslyn-compiler @RikkiGibson for a second review.

@333fred 333fred enabled auto-merge (squash) December 15, 2021 19:40
333fred added a commit to 333fred/csharplang that referenced this pull request Dec 15, 2021
@333fred 333fred merged commit 9cbcb29 into dotnet:main Dec 15, 2021
@ghost ghost added this to the Next milestone Dec 15, 2021
@333fred 333fred deleted the interp-nullable-improvements branch December 15, 2021 23:36
333fred added a commit to 333fred/roslyn that referenced this pull request Dec 16, 2021
…rations

* upstream/main: (87 commits)
  Add support for nullable analysis in interpolated string handler constructors (dotnet#57780)
  Record list-patterns and newlines in interpolations as done (dotnet#58250)
  Swithc to acquiring the component model on a BG thread.
  Fix failure to propagate cancellation token
  [main] Update dependencies from dotnet/arcade (dotnet#58327)
  Change PROTOTYPE to issue reference (dotnet#58336)
  Resolve PROTOTYPE comments in param null checking (dotnet#58324)
  Address various minor param-nullchecking issues (dotnet#58321)
  Nullable enable GetTypeByMetadataName (dotnet#58317)
  Support CodeClass2.Parts returning parts in source generated files
  Allow the FileCodeModel.Parent to be null
  Ensure the CodeModel tests are using VisualStudioWorkspaceImpl
  Fix the bad words in TestDeepAlternation
  Fix regression in Equals/GetHashCode of LambdaSymbol. (dotnet#58247)
  Support "Enable nullable reference types" from disable keyword
  Support "Enable nullable reference types" from restore keyword
  Support "Enable nullable reference types" on the entire directive
  Add comment
  Remove descriptor
  Update src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.cs
  ...
@Cosifne Cosifne modified the milestones: Next, 17.1.P3 Jan 5, 2022
333fred added a commit to dotnet/csharplang that referenced this pull request Jan 31, 2022
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.

4 participants