This is an archive of the discontinued LLVM Phabricator instance.

[Sema] Add variable captured by a block to the enclosing lambda's potential capture list
ClosedPublic

Authored by ahatanak on Oct 13 2016, 6:36 AM.

Details

Summary

When compiling the following code, DoMarkVarDeclReferenced fails to capture variable "outerp":

auto lambda =[&](auto p) {
  return ^{
    return p + outerp;
  }();
};

This happens because Sema::getCurLambda() returns the lambda scope only when it's the last scope that has been pushed to Sema::FunctionScopes and therefore returns null if there is a block enclosed in the lambda. To fix this bug, this patch defines function Sema::getInnermostLambda() and uses it in DoMarkVarDeclReferenced to get the innermost lambda scope.

rdar://problem/28412462

Diff Detail

Repository
rL LLVM

Event Timeline

ahatanak updated this revision to Diff 74504.Oct 13 2016, 6:36 AM
ahatanak retitled this revision from to [Sema] Add variable captured by a block to the enclosing lambda's potential capture list.
ahatanak updated this object.
ahatanak added reviewers: rsmith, rjmccall.
ahatanak added a subscriber: cfe-commits.
rjmccall edited edge metadata.Oct 13 2016, 4:36 PM

Richard should probably weigh in about whether we should be recording potential captures at *all* capturing scopes. But at the very least, I think you have a bug here where the variable is declared outside of the block but within the lambda; in that case, it is definitely not a potential capture of the lambda.

Richard should probably weigh in about whether we should be recording potential captures at *all* capturing scopes. But at the very least, I think you have a bug here where the variable is declared outside of the block but within the lambda; in that case, it is definitely not a potential capture of the lambda.

Ah, that's a good point.

When a variable is declared outside of the block but within the lambda, CheckIfAnyEnclosingLambdasMustCaptureAnyPotentialCaptures in SemaExprCXX.cpp will not capture the variable because getStackIndexOfNearestEnclosingCaptureCapableLambda doesn't return a proper index, so it looks like it's still safe to add the variable here even when it cannot be captured by the lambda. I think it's possible to check that the variable is not declared within the lambda before adding it to the list of potential captures in the code above, but I'm not sure whether we have to do that.

Also, could you elaborate on when you think we don't have to record a variable as a potential capture? My understanding is that you need to record the variable as a potential capture when the variable is part of a full expression that depends on a generic parameter because you don't know whether the variable is odr-used and needs to be captured by the lambda until you look at the full expression. Perhaps there are exceptions to this rule when the variable is used in a block?

Richard should probably weigh in about whether we should be recording potential captures at *all* capturing scopes. But at the very least, I think you have a bug here where the variable is declared outside of the block but within the lambda; in that case, it is definitely not a potential capture of the lambda.

Ah, that's a good point.

When a variable is declared outside of the block but within the lambda, CheckIfAnyEnclosingLambdasMustCaptureAnyPotentialCaptures in SemaExprCXX.cpp will not capture the variable because getStackIndexOfNearestEnclosingCaptureCapableLambda doesn't return a proper index, so it looks like it's still safe to add the variable here even when it cannot be captured by the lambda. I think it's possible to check that the variable is not declared within the lambda before adding it to the list of potential captures in the code above, but I'm not sure whether we have to do that.

Also, could you elaborate on when you think we don't have to record a variable as a potential capture? My understanding is that you need to record the variable as a potential capture when the variable is part of a full expression that depends on a generic parameter because you don't know whether the variable is odr-used and needs to be captured by the lambda until you look at the full expression. Perhaps there are exceptions to this rule when the variable is used in a block?

I mean that it might be important for us to be recording this at block scopes, not just lambda scopes. Maybe not; I guess we do eagerly instantiate blocks, which we can't necessarily do with lambdas. This is something that Richard needs to weigh in on.

ahatanak updated this revision to Diff 75956.Oct 26 2016, 3:17 PM
ahatanak edited edge metadata.

Add the variable to the list of potential captures only if it's declared outside of the lambda expression. I don't think we need to check that the variable is declared outside of the lambda because getStackIndexOfNearestEnclosingCaptureCapableLambda will prevent it from being captured, but it's better not to add the variable to the list needlessly.

ahatanak updated this revision to Diff 82394.Dec 22 2016, 6:43 PM

Rebase.

r286584 changed getCurLambda to optionally skip CapturedRegionScopeInfos. This patch changes it to skip BlockScopeInfos too.

Add a few more people who have looked at this part of clang in the past to the reviewers list.

malcolm.parsons resigned from this revision.Jan 20 2017, 1:49 AM

I don't see anything wrong with this, but I don't think I'm qualified to approve it.

Well, I was hoping that Richard would weigh in, but absent that, I don't have any objections. Since it fixes a known bug, let's just go ahead and do it. LGTM.

This revision was automatically updated to reflect the committed changes.