Skip to content

Commit

Permalink
[clang] Correctly handle by-reference capture with an initializer tha…
Browse files Browse the repository at this point in the history
…t is a pack expansion in lambdas.

Ensure that the correct information whether an init-capture of a lambda
is passed by reference or by copy. This information is already computed
and has to be passed to the place where `NewInitCaptureType` is
created.

Before this fix it has been checked whether the VarDecl is a reference
type. This doesn't work for packed expansions, as the information
whether it is passed by reference or by copy is stored at the pattern of
a `PackExpansionType` and not at the type itself.

However, as the information has been already computed, we just have to
pass it.

Add tests that lambda captures with var decls which are reference types
are created in the AST and a disgnotics test for pack expansions.

Fixes #49266

Differential Revision: https://reviews.llvm.org/D139125
  • Loading branch information
jensmassberg committed Dec 7, 2022
1 parent 7203a86 commit edbea62
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 8 deletions.
3 changes: 2 additions & 1 deletion clang/include/clang/Sema/Sema.h
Expand Up @@ -7097,7 +7097,8 @@ class Sema final {
unsigned InitStyle, Expr *Init);

/// Add an init-capture to a lambda scope.
void addInitCapture(sema::LambdaScopeInfo *LSI, VarDecl *Var);
void addInitCapture(sema::LambdaScopeInfo *LSI, VarDecl *Var,
bool isReferenceType);

/// Note that we have finished the explicit captures for the
/// given lambda.
Expand Down
11 changes: 6 additions & 5 deletions clang/lib/Sema/SemaLambda.cpp
Expand Up @@ -887,11 +887,12 @@ VarDecl *Sema::createLambdaInitCaptureVarDecl(SourceLocation Loc,
return NewVD;
}

void Sema::addInitCapture(LambdaScopeInfo *LSI, VarDecl *Var) {
void Sema::addInitCapture(LambdaScopeInfo *LSI, VarDecl *Var,
bool isReferenceType) {
assert(Var->isInitCapture() && "init capture flag should be set");
LSI->addCapture(Var, /*isBlock*/false, Var->getType()->isReferenceType(),
/*isNested*/false, Var->getLocation(), SourceLocation(),
Var->getType(), /*Invalid*/false);
LSI->addCapture(Var, /*isBlock*/ false, isReferenceType,
/*isNested*/ false, Var->getLocation(), SourceLocation(),
Var->getType(), /*Invalid*/ false);
}

void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
Expand Down Expand Up @@ -1261,7 +1262,7 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
}

if (C->Init.isUsable()) {
addInitCapture(LSI, cast<VarDecl>(Var));
addInitCapture(LSI, cast<VarDecl>(Var), C->Kind == LCK_ByRef);
} else {
TryCaptureKind Kind = C->Kind == LCK_ByRef ? TryCapture_ExplicitByRef :
TryCapture_ExplicitByVal;
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/Sema/TreeTransform.h
Expand Up @@ -13149,7 +13149,7 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {

QualType NewInitCaptureType =
getSema().buildLambdaInitCaptureInitialization(
C->getLocation(), OldVD->getType()->isReferenceType(),
C->getLocation(), C->getCaptureKind() == LCK_ByRef,
EllipsisLoc, NumExpansions, OldVD->getIdentifier(),
cast<VarDecl>(C->getCapturedVar())->getInitStyle() !=
VarDecl::CInit,
Expand Down Expand Up @@ -13331,7 +13331,7 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
break;
}
NewVDs.push_back(NewVD);
getSema().addInitCapture(LSI, NewVD);
getSema().addInitCapture(LSI, NewVD, C->getCaptureKind() == LCK_ByRef);
}

if (Invalid)
Expand Down
22 changes: 22 additions & 0 deletions clang/test/SemaCXX/lambda-pack-expansion.cpp
@@ -0,0 +1,22 @@
// RUN: %clang_cc1 -std=c++20 -Wno-unused-value -fsyntax-only -verify %s

namespace GH49266 {
struct X {
X() = default;
X(X const&) = delete; // expected-note {{'X' has been explicitly marked deleted here}}
};

void take_by_copy(auto &...args) {
[...args = args] {}(); // expected-error {{call to deleted constructor}}
}

void take_by_ref(auto &...args) {
[&...args = args] {}(); // args is passed by reference and not copied.
}

void foo() {
X x;
take_by_copy(x); // expected-note {{in instantiation of function template specialization}}
take_by_ref(x);
}
}
24 changes: 24 additions & 0 deletions clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
Expand Up @@ -2306,6 +2306,30 @@ TEST_P(ASTMatchersTest,
hasName("cc"), hasInitializer(integerLiteral(equals(1))))))))));
}

TEST_P(ASTMatchersTest, LambdaCaptureTest_BindsToCaptureOfReferenceType) {
if (!GetParam().isCXX20OrLater()) {
return;
}
auto matcher = lambdaExpr(hasAnyCapture(
lambdaCapture(capturesVar(varDecl(hasType(referenceType()))))));
EXPECT_TRUE(matches("template <class ...T> void f(T &...args) {"
" [&...args = args] () mutable {"
" }();"
"}"
"int main() {"
" int a;"
" f(a);"
"}", matcher));
EXPECT_FALSE(matches("template <class ...T> void f(T &...args) {"
" [...args = args] () mutable {"
" }();"
"}"
"int main() {"
" int a;"
" f(a);"
"}", matcher));
}

TEST(ASTMatchersTestObjC, ObjCMessageCalees) {
StatementMatcher MessagingFoo =
objcMessageExpr(callee(objcMethodDecl(hasName("foo"))));
Expand Down

0 comments on commit edbea62

Please sign in to comment.