Skip to content
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

Remove CapturedVariablesByLambda #20878

Merged
merged 3 commits into from
Jul 17, 2017
Merged

Conversation

agocke
Copy link
Member

@agocke agocke commented Jul 14, 2017

Swaps out all uses of CapturedVariablesByLambda for functions on the
Scope tree.

Fixes https://github.com/dotnet/roslyn/projects/26#card-3753318

Swaps out all uses of CapturedVariablesByLambda for functions on the
Scope tree.
/// <summary>
/// The root of the scope tree for this method.
/// </summary>
public Scope ScopeTree { get; private set; }
Copy link
Member

Choose a reason for hiding this comment

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

Delete the private set. It's unnecessary since you only modify the value in the ctor. Makes it clear this is a readonly property.

Copy link
Member Author

Choose a reason for hiding this comment

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

? It's set in the visitor, not in the constructor. I could set it in the constructor but that's a dangerous dance as I would be handing out this before fully constructed.

/// <returns>
/// A tuple of the found <see cref="Closure"/> and the <see cref="Scope"/> it was found in.
/// </returns>
public static (Closure, Scope) FindClosureInScope(Scope startingScope, MethodSymbol closureSymbol)
Copy link
Member

Choose a reason for hiding this comment

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

Consider GetClosureInScope vs. Find. The general rule for funtions like this is:

  • Find: returns null on failure. Essentially a method that expects failure as an outcome.
  • Get: throws on failure. A method which expects to find the value.

Copy link
Member

Choose a reason for hiding this comment

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

Hmm also consider simply GetClosure. The InScope part to me can read as "find a closure inside this scope" which is contrary to the implementation.

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 was trying to find some way to say "this method searches up", as opposed to the other one, which searches down. Any suggestions?

Copy link
Member

Choose a reason for hiding this comment

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

GetVisibleClosure?

/// <summary>
/// Finds a <see cref="Closure"/> with a matching original symbol somewhere in the given scope or nested scopes.
/// </summary>
public static Closure FindClosureInTree(Scope rootScope, MethodSymbol closureSymbol)
Copy link
Member

Choose a reason for hiding this comment

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

Another place to consider Get vs. Find

/// <summary>
/// Finds a <see cref="Closure"/> with a matching original symbol somewhere in the given scope or nested scopes.
/// </summary>
public static Closure FindClosureInTree(Scope rootScope, MethodSymbol closureSymbol)
Copy link
Member

Choose a reason for hiding this comment

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

Consider using treeRoot vs. rootScope. The term root scope has a particular meaning in this code base already: the absolute root of the scopes. This method though works on any arbitrary scope.

{
return result;
}
throw ExceptionUtilities.Unreachable;
Copy link
Member

Choose a reason for hiding this comment

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

return result ?? throw ...

Don't actually care if you do this, just a reminder to myself that you could 😄

Copy link
Member Author

Choose a reason for hiding this comment

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

niiice


Closure Helper(Scope scope)
{
foreach (var closure in scope.Closures)
Copy link
Member

Choose a reason for hiding this comment

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

In the FindClosureInScope you used SingleOrDefault to accomplish the exact same operation. Why use foreach here but SingleOrDefault there? Seems like these should do the same operation.

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'll change the other one to use a foreach loop too. Avoids allocating a capture environment and a delegate 😄

@agocke agocke requested a review from a team July 14, 2017 23:11
@jaredpar
Copy link
Member

A number of the tests needed to be re-baselined due to the generated closure names changing. Can you please add an explanation for why that happened? Want to make sure this is expected and we understand why it's changing.

@agocke
Copy link
Member Author

agocke commented Jul 14, 2017

@jaredpar Certainly.

The reason the baseline changed is that the order of enumerating closures changed. I briefly looked into matching the behavior, but the problem is that the closure order in the old visitors is determined by the visitation of captured variables, not visitation of the closures themselves. More specifically, an item is only added to the CapturedVariablesByLambda dictionary when ReferenceVariable encounters a captured variable.

In contrast, the visitation of the Scope tree looks at closures in an in-order traversal of the tree, stopping at the first introduced closure, not necessarily the closure which first captured a variable.

One obvious consequence of this changing is that the old ordering for a series of nested lambdas was effectively post-order -- the interior closures would be added to the visitation first, since they would be added from the perspective of the captured variable looking up, instead of the visitor looking down. This particular case is responsible for all the baseline changes that I have seen.

When I looked into replicating this traversal order I found it complicated and pretty counter-intuitive. Since the baseline change was so low in emitted IL, I thought it better to keep a very natural in-order tree traversal and just rebaseline a few tests.

@jaredpar
Copy link
Member

jaredpar commented Jul 14, 2017

@agocke

Thanks for the explanation. Could you find a way to get that explanation into one of the commit messages here? That way future code historians can understand why these baselines changed?

Would recomend just ammend the change that updated the baseline to have the commit message.

@agocke
Copy link
Member Author

agocke commented Jul 15, 2017

@jaredpar I plan on squashing these changes, so how about I add it to the squashed message?

@@ -328,22 +328,20 @@ protected override NamedTypeSymbol ContainingType
/// </summary>
private void MakeFrames(ArrayBuilder<ClosureDebugInfo> closureDebugInfo)
{
var closures = _analysis.CapturedVariablesByLambda.Keys;

foreach (var closure in closures)
Copy link
Member

Choose a reason for hiding this comment

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

Is this why the names changed? We are traversing closures in different order? I think this is ok.
I think we may be changing the order more in the future. It may make sense eventually for the frame generator to just traverse Scopes/DeclaredVariables instead.

Copy link
Member Author

Choose a reason for hiding this comment

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

Agreed

@VSadov
Copy link
Member

VSadov commented Jul 15, 2017

:shipit:

Copy link
Member

@VSadov VSadov left a comment

Choose a reason for hiding this comment

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

LGTM

@jaredpar
Copy link
Member

I plan on squashing these changes, so how about I add it to the squashed message?

That's fine. Achieves the goal of "developer sees change, uses git blame, and gets a good explanation".

@agocke agocke merged commit e240a70 into dotnet:master Jul 17, 2017
@agocke agocke deleted the remove-capture-by-lambda branch July 17, 2017 21:24
333fred added a commit to 333fred/roslyn that referenced this pull request Jul 20, 2017
…c-member-reference

* dotnet/features/ioperation: (134 commits)
  Fix build break in IOperation feature branch
  Fix unit tests that broke with previous commit
  Address PR feedback from Chuck
  Fix unit test
  Add IOperation support for variations of object creation expressions (type parameter, nopia and dynamic)
  Remove dead code from GenerateConstructor.
  Disable NETCore version check
  Fixed review notes.
  Add comment
  Disable NETCore version check
  Show modifier/method/type block declaration keywords after attribute lists
  Improve type argument list parsing in error conditions. (dotnet#19467)
  Don't report modifier errors while parsing. (dotnet#16339)
  Fix assert and merge
  Remove extra space
  Restore failure behavior.
  Simplify implementation of parsing constraints. (dotnet#16429)
  Fix csproj
  Remove CapturedVariablesByLambda (dotnet#20878)
  Remove testing wait
  ...
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