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

LambdaRewriter should only assign proxies from locals in original frame in EE #19349

Merged
merged 1 commit into from
May 18, 2017

Conversation

cston
Copy link
Member

@cston cston commented May 8, 2017

Customer scenario

Evaluate expression containing nested lambdas where the inner lambda closes over a local in outer lambda.

Bugs this fixes:

#18273

Workarounds, if any

Avoid evaluating such expressions

Risk

Low

Performance impact

Low

Is this a regression from a previous update?

Probably not a regression. Unit test included.

@cston
Copy link
Member Author

cston commented May 8, 2017

@dotnet/roslyn-compiler, @tmat, @jinujoseph, @ivanbasov please review.

variableSlotAllocatorOpt,
diagnosticsThisMethod,
_debugDocumentProvider,
method.GenerateDebugInfo ? importChain : null,
Copy link
Member

Choose a reason for hiding this comment

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

[](start = 32, length = 4)

nit: Extra indent

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed.

// confuse the method body generator by making a fresh bag and then loading
// any diagnostics emitted into it back into the main diagnostic bag.
var diagnosticsThisMethod = DiagnosticBag.GetInstance();
// We make sure that an asynchronous mutation to the diagnostic bag does not
Copy link
Member

Choose a reason for hiding this comment

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

// We mak [](start = 20, length = 9)

Is this block just indented?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, the only change is to indenting.

@@ -101,7 +101,7 @@ public MappedLocalFunction(SynthesizedLambdaMethod symbol, ClosureKind closureKi
// True if the rewritten tree should include assignments of the
Copy link
Member

Choose a reason for hiding this comment

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

True [](start = 11, length = 4)

The comment needs update.

Copy link
Member Author

Choose a reason for hiding this comment

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

Updated comments.

@tmat
Copy link
Member

tmat commented May 8, 2017

    /// <param name="assignLocals">The rewritten tree should include assignments of the original locals to the lifted proxies</param>

Update comment


Refers to: src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaRewriter.cs:210 in 7497a20. [](commit_id = 7497a20ac2756ea95b89cb506e7d60af9cd688fd, deletion_comment = False)

@@ -101,7 +101,7 @@ public MappedLocalFunction(SynthesizedLambdaMethod symbol, ClosureKind closureKi
// True if the rewritten tree should include assignments of the
// original locals to the lifted proxies. This is only useful for the
// expression evaluator where the original locals are left as is.
private readonly bool _assignLocals;
private readonly HashSet<LocalSymbol> _assignLocals;
Copy link
Member

Choose a reason for hiding this comment

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

_assignLocals [](start = 46, length = 13)

If this is null then all locals are assigned, otherwise only those that are in this set?

Copy link
Member Author

Choose a reason for hiding this comment

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

If null, no locals are assigned. The use was incorrect. Fixed.

{
localsBuilder.Add(local);
localsSet.Add(local);
}
Copy link
Member

Choose a reason for hiding this comment

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

Can be simplified:

if (localsSet.Add(local))
{
   localsBuilder.Add(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.

Done.

@cston
Copy link
Member Author

cston commented May 10, 2017

@dotnet/roslyn-compiler, @tmat, please review.

@@ -830,6 +830,50 @@ End Class
End Sub

<Fact>
Public Sub CapturedLocalInNestedLocalFunction()
Copy link
Contributor

Choose a reason for hiding this comment

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

LocalFunction [](start = 40, length = 13)

Lambda? VB doesn't support local functions.

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed.

Copy link
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

@cston
Copy link
Member Author

cston commented May 11, 2017

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

@cston
Copy link
Member Author

cston commented May 17, 2017

@dotnet/roslyn-compiler for a second review for a small change to the compiler.

@MattGertz for approval since this is an EE fix.

@cston
Copy link
Member Author

cston commented May 17, 2017

@dotnet-bot retest this please

@cston
Copy link
Member Author

cston commented May 18, 2017

@dotnet/roslyn-compiler please provide a second review for 15.3. Thanks.

@cston
Copy link
Member Author

cston commented May 18, 2017

@dotnet-bot test windows_release_vs-integration_prtest please

@jcouv
Copy link
Member

jcouv commented May 18, 2017

📝 Notes from discussion with Chuck:
In EE scenarios, you can create a closure that didn't exist in original source. For instance, evaluating F(() => local).
In such case, the fields of the closure need to be populated with the value of their respective variables (locals, parameters, etc).
The local rewriter has logic to do this one-time copy.

But in the case of nested lambdas, it is possible to introduce a closure with a field which should not be initialized this way, because it does not exist in the original method.
For instance, if you evaluate F(z => G(() => local + z)). Then local field of the closure must be initialized, but z should not.

The EE already keeps track of all the variables in scope (including locals from source that were compiled/lifted into a closure and don't exist as locals in the IL), so we can use this information to decide which variables need this initialization and which do not. That's the fix in this PR :-)

Copy link
Member

@jcouv jcouv left a comment

Choose a reason for hiding this comment

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

LGTM. Thanks for the background explanation.

@cston cston merged commit 3f402e9 into dotnet:master May 18, 2017
@cston cston deleted the 18273 branch May 18, 2017 17:25
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.

7 participants