Skip to content

Conversation

@jjonescz
Copy link
Member

@jjonescz jjonescz commented Jan 7, 2025

Alternative to #76237 and #76263.
Fixes #75802.
Fixes #63306.

There can be implicit calls generated by the compiler like collection.Add or interpolatedStringHandler.AppendFormatted that previously weren't fully analyzed by the ref safety analysis.

For collection initializers, the ref safety analysis went over all the initializers (Add calls) and intersected their val escape scopes, but that essentially did nothing since those Add methods usually return void so GetValEscape doesn't look at them.
For interpolated strings, the ref safety analysis went over all the arguments and intersected their val escape scopes.
But neither of those approaches detected when a ref passed as an in argument could be captured into the receiver (collection / interpolated string handler). So this PR analyzes the implicit calls properly to detect that.

All the existing get / check val escape utilities work on expressions and so they analyze calls only if they can actually return a ref. (Arg mixing checks if a ref can escape into the receiver but since here we are essentially trying to determine what the escape scope of the receiver should be, we cannot easily use that - I've tried that in the alternative PRs linked above but it wasn't very nice.) So I added other utilities to determine/check the escape scope of the receiver of a call.

@ghost ghost added the untriaged Issues and PRs which have not yet been triaged by a lead label Jan 7, 2025
@jjonescz jjonescz marked this pull request as ready for review January 7, 2025 17:14
@jjonescz jjonescz requested a review from a team as a code owner January 7, 2025 17:14
@jjonescz jjonescz requested review from RikkiGibson and cston January 7, 2025 17:15
@AlekseyTs
Copy link
Contributor

@jjonescz Could you provide more details about the change? How exactly it fixes the issues, etc.

@jjonescz
Copy link
Member Author

@dotnet/roslyn-compiler for reviews, thanks

@RikkiGibson
Copy link
Member

I will try to get to this next week

@jjonescz jjonescz requested review from a team and RikkiGibson and removed request for cston July 17, 2025 08:36
@jjonescz
Copy link
Member Author

@RikkiGibson @jaredpar @dotnet/roslyn-compiler for reviews, thanks

@RikkiGibson RikkiGibson self-assigned this Jul 21, 2025
else
{
// We have an extension method receiver.
Debug.Assert(methodInfo.Method?.IsExtensionMethod != false);
Copy link
Member

Choose a reason for hiding this comment

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

We'll want to either test new extensions here or add a tracking issue link for adding such tests.

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've realized the "correct" way is to call ReplaceWithExtensionImplementationIfNeeded like the ref safety analysis does in other places. I will update the code to do that.

}

private void GetInterpolatedStringHandlerArgumentsForEscape(BoundExpression expression, ArrayBuilder<BoundExpression> arguments)
private bool CheckValEscapeOfInterpolatedStringHandlerCalls(BoundExpression expression, SafeContext escapeFrom, SafeContext escapeTo, BindingDiagnosticBag diagnostics)
Copy link
Member

Choose a reason for hiding this comment

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

Does this need to take in a BoundExpression? Or would it work to take in a BoundInterpolatedString and make the expression used for traversal a local?

Copy link
Member Author

Choose a reason for hiding this comment

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

The expression can start as BoundBinaryOperator or BoundInterpolatedString. So yes, this needs to take BoundExpression.

@jjonescz jjonescz requested a review from a team July 23, 2025 11:35
@jjonescz
Copy link
Member Author

@dotnet/roslyn-compiler for a second review, thanks

1 similar comment
@jjonescz
Copy link
Member Author

@dotnet/roslyn-compiler for a second review, thanks

@jjonescz
Copy link
Member Author

jjonescz commented Aug 6, 2025

@dotnet/roslyn-compiler for a second review, thanks

Copy link
Member

@333fred 333fred left a comment

Choose a reason for hiding this comment

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

This will conflict with the refactoring in #79447. I think it will be easier to integrate that change into this PR, than the other way around, so I'd prefer to get that in first, if you don't mind.

return result;
}

private SafeContext GetValEscapeOfInterpolatedStringHandlerCalls(BoundExpression expression, SafeContext localScopeDepth)
Copy link
Member

Choose a reason for hiding this comment

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

Why do we need both of these functions? Surely Check can be implemented as a call to Get and a comparison to the allowed escape scope?

Copy link
Member Author

Choose a reason for hiding this comment

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

Same reason we have Get*/Check* pairs elsewhere in ref safety analysis - better error reporting.

@jjonescz jjonescz requested a review from 333fred August 11, 2025 11:38
@jjonescz
Copy link
Member Author

@333fred for another look, thanks

@jjonescz jjonescz merged commit d7d419f into dotnet:main Aug 13, 2025
24 checks passed
@jjonescz jjonescz deleted the 75802-RefSafety-EscapingIntoReceiver branch August 13, 2025 18:49
@dotnet-policy-service dotnet-policy-service bot added this to the Next milestone Aug 13, 2025
333fred added a commit to 333fred/roslyn that referenced this pull request Aug 13, 2025
* upstream/main: (87 commits)
  Fix ref safety of implicit calls that might capture refs in the receiver (dotnet#76657)
  Update dependencies from https://github.com/dotnet/dotnet build 278961 Updated Dependencies: System.CommandLine (Version 2.0.0-rc.1.25410.101 -> 2.0.0-rc.1.25411.109)
  Update checklist for adding new language version (dotnet#79881)
  Skip ValidateAllOptions flaky test
  Don't try to load file based projects unless we get a .cs file (dotnet#79844)
  Update package restore error message.
  [main] Source code updates from dotnet/dotnet (dotnet#79862)
  Fix failure to report integration test results when retrying Also fix newly failing tests
  feedback
  More semantic update changes (dotnet#79828)
  Fix flow analysis of extern local functions (dotnet#79741)
  Allow using `/main` with top-level statements (dotnet#79577)
  Delete
  Async
  JTF.run
  Delete comment
  Delete code
  Cancel in flight work
  Simplify
  Fix race condition
  ...
@RikkiGibson RikkiGibson modified the milestones: Next, 18.0 P1 Aug 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area-Compilers Feature - Ref Fields 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.

Collection initializers can leak refs Escape analysis ignores local passed by reference to interpolated string handler AppendFormatted() method

4 participants