[clang] [Clang] [Sema] WIP: Fix dependence of DREs in lambdas with an explicit object parameter (PR #84473)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Mar 8 06:34:36 PST 2024
https://github.com/Sirraide updated https://github.com/llvm/llvm-project/pull/84473
>From 870e6a6def8c17859ffbb30906f91912268f872d Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Fri, 8 Mar 2024 11:55:42 +0100
Subject: [PATCH 1/3] [Clang] Fix dependence of DREs in lambdas with an
explicit object parameter
---
clang/lib/Sema/SemaExpr.cpp | 44 +++++++++---
clang/lib/Sema/TreeTransform.h | 4 +-
clang/test/SemaCXX/cxx2b-deducing-this.cpp | 81 ++++++++++++++++++++++
3 files changed, 116 insertions(+), 13 deletions(-)
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 47bb263f56aade..700769860aa806 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -20687,20 +20687,42 @@ void Sema::MarkVariableReferenced(SourceLocation Loc, VarDecl *Var) {
static void FixDependencyOfIdExpressionsInLambdaWithDependentObjectParameter(
Sema &SemaRef, ValueDecl *D, Expr *E) {
auto *ID = dyn_cast<DeclRefExpr>(E);
- if (!ID || ID->isTypeDependent())
+ if (!ID || ID->isTypeDependent() || !ID->refersToEnclosingVariableOrCapture())
return;
+ // If any enclosing lambda with a dependent explicit object parameter either
+ // explicitly captures the variable by value, or has a capture default of '='
+ // and does not capture the variable by reference, then the type of the DRE
+ // is dependent on the type of that lambda's explicit object parameter.
auto IsDependent = [&]() {
- const LambdaScopeInfo *LSI = SemaRef.getCurLambda();
- if (!LSI)
- return false;
- if (!LSI->ExplicitObjectParameter ||
- !LSI->ExplicitObjectParameter->getType()->isDependentType())
- return false;
- if (!LSI->CaptureMap.count(D))
- return false;
- const Capture &Cap = LSI->getCapture(D);
- return !Cap.isCopyCapture();
+ for (auto *Scope : llvm::reverse(SemaRef.FunctionScopes)) {
+ auto *LSI = dyn_cast<sema::LambdaScopeInfo>(Scope);
+ if (!LSI)
+ continue;
+
+ if (LSI->Lambda && !LSI->Lambda->Encloses(SemaRef.CurContext) &&
+ LSI->AfterParameterList)
+ return false;
+
+ const auto *MD = LSI->CallOperator;
+ if (MD->getType().isNull())
+ continue;
+
+ const auto *Ty = cast<FunctionProtoType>(MD->getType());
+ if (!Ty || !MD->isExplicitObjectMemberFunction() ||
+ !Ty->getParamType(0)->isDependentType())
+ continue;
+
+ if (auto *C = LSI->CaptureMap.count(D) ? &LSI->getCapture(D) : nullptr) {
+ if (C->isCopyCapture())
+ return true;
+ continue;
+ }
+
+ if (LSI->ImpCaptureStyle == LambdaScopeInfo::ImpCap_LambdaByval)
+ return true;
+ }
+ return false;
}();
ID->setCapturedByCopyInLambdaWithExplicitObjectParameter(
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 7389a48fe56fcc..867efd1ce5c80a 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -11099,8 +11099,8 @@ TreeTransform<Derived>::TransformDeclRefExpr(DeclRefExpr *E) {
}
if (!getDerived().AlwaysRebuild() &&
- QualifierLoc == E->getQualifierLoc() &&
- ND == E->getDecl() &&
+ E->getDependence() == ExprDependence::None &&
+ QualifierLoc == E->getQualifierLoc() && ND == E->getDecl() &&
Found == E->getFoundDecl() &&
NameInfo.getName() == E->getDecl()->getDeclName() &&
!E->hasExplicitTemplateArgs()) {
diff --git a/clang/test/SemaCXX/cxx2b-deducing-this.cpp b/clang/test/SemaCXX/cxx2b-deducing-this.cpp
index b8ddb9ad300034..6c21954554d281 100644
--- a/clang/test/SemaCXX/cxx2b-deducing-this.cpp
+++ b/clang/test/SemaCXX/cxx2b-deducing-this.cpp
@@ -200,6 +200,87 @@ void TestMutationInLambda() {
[i = 0](this auto){ i++; }();
[i = 0](this const auto&){ i++; }();
// expected-error at -1 {{cannot assign to a variable captured by copy in a non-mutable lambda}}
+ // expected-note at -2 {{in instantiation of}}
+
+ int x;
+ const auto l1 = [x](this auto&) { x = 42; }; // expected-error {{cannot assign to a variable captured by copy in a non-mutable lambda}}
+ const auto l2 = [=](this auto&) { x = 42; }; // expected-error {{cannot assign to a variable captured by copy in a non-mutable lambda}}
+
+ const auto l3 = [&x](this auto&) {
+ const auto l3a = [x](this auto&) { x = 42; }; // expected-error {{cannot assign to a variable captured by copy in a non-mutable lambda}}
+ l3a(); // expected-note {{in instantiation of}}
+ };
+
+ const auto l4 = [&x](this auto&) {
+ const auto l4a = [=](this auto&) { x = 42; }; // expected-error {{cannot assign to a variable captured by copy in a non-mutable lambda}}
+ l4a(); // expected-note {{in instantiation of}}
+ };
+
+ const auto l5 = [x](this auto&) {
+ const auto l5a = [x](this auto&) { x = 42; }; // expected-error {{cannot assign to a variable captured by copy in a non-mutable lambda}}
+ l5a(); // expected-note {{in instantiation of}}
+ };
+
+ const auto l6 = [=](this auto&) {
+ const auto l6a = [=](this auto&) { x = 42; }; // expected-error {{cannot assign to a variable captured by copy in a non-mutable lambda}}
+ l6a(); // expected-note {{in instantiation of}}
+ };
+
+ const auto l7 = [x](this auto&) {
+ const auto l7a = [=](this auto&) { x = 42; }; // expected-error {{cannot assign to a variable captured by copy in a non-mutable lambda}}
+ l7a(); // expected-note {{in instantiation of}}
+ };
+
+ const auto l8 = [=](this auto&) {
+ const auto l8a = [x](this auto&) { x = 42; }; // expected-error {{cannot assign to a variable captured by copy in a non-mutable lambda}}
+ l8a(); // expected-note {{in instantiation of}}
+ };
+
+ const auto l9 = [&](this auto&) {
+ const auto l9a = [x](this auto&) { x = 42; }; // expected-error {{cannot assign to a variable captured by copy in a non-mutable lambda}}
+ l9a(); // expected-note {{in instantiation of}}
+ };
+
+ const auto l10 = [&](this auto&) {
+ const auto l10a = [=](this auto&) { x = 42; }; // expected-error {{cannot assign to a variable captured by copy in a non-mutable lambda}}
+ l10a(); // expected-note {{in instantiation of}}
+ };
+
+ const auto l11 = [x](this auto&) {
+ const auto l11a = [&x](this auto&) { x = 42; }; // expected-error {{cannot assign to a variable captured by copy in a non-mutable lambda}} expected-note {{while substituting}}
+ l11a();
+ };
+
+ const auto l12 = [x](this auto&) {
+ const auto l12a = [&](this auto&) { x = 42; }; // expected-error {{cannot assign to a variable captured by copy in a non-mutable lambda}} expected-note {{while substituting}}
+ l12a();
+ };
+
+ const auto l13 = [=](this auto&) {
+ const auto l13a = [&x](this auto&) { x = 42; }; // expected-error {{cannot assign to a variable captured by copy in a non-mutable lambda}} expected-note {{while substituting}}
+ l13a();
+ };
+
+ l1(); // expected-note {{in instantiation of}}
+ l2(); // expected-note {{in instantiation of}}
+ l3(); // expected-note {{in instantiation of}}
+ l4(); // expected-note {{in instantiation of}}
+ l5(); // expected-note {{in instantiation of}}
+ l6(); // expected-note {{in instantiation of}}
+ l7(); // expected-note {{in instantiation of}}
+ l8(); // expected-note {{in instantiation of}}
+ l9(); // expected-note {{in instantiation of}}
+ l10(); // expected-note {{in instantiation of}}
+ l11(); // expected-note {{in instantiation of}}
+ l12(); // expected-note {{in instantiation of}}
+ l13(); // expected-note {{in instantiation of}}
+
+ {
+ const auto l1 = [&x](this auto&) { x = 42; };
+ const auto l2 = [&](this auto&) { x = 42; };
+ l1();
+ l2();
+ }
}
struct Over_Call_Func_Example {
>From bf4c193f6e91d4dae4fa20c805b49955d95666cd Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Fri, 8 Mar 2024 13:37:57 +0100
Subject: [PATCH 2/3] [Clang] Better dependence check for DREs in TreeTransform
---
clang/lib/Sema/TreeTransform.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 867efd1ce5c80a..e537dfef767df8 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -11099,7 +11099,7 @@ TreeTransform<Derived>::TransformDeclRefExpr(DeclRefExpr *E) {
}
if (!getDerived().AlwaysRebuild() &&
- E->getDependence() == ExprDependence::None &&
+ !E->isCapturedByCopyInLambdaWithExplicitObjectParameter() &&
QualifierLoc == E->getQualifierLoc() && ND == E->getDecl() &&
Found == E->getFoundDecl() &&
NameInfo.getName() == E->getDecl()->getDeclName() &&
>From 2812fd944f2aa107504b01fd06eb63fa5befb23d Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Fri, 8 Mar 2024 15:34:05 +0100
Subject: [PATCH 3/3] [Clang] Fix dependence of `this` in explicit object
lambdas
---
clang/include/clang/AST/ExprCXX.h | 10 ++++
clang/include/clang/AST/Stmt.h | 5 ++
clang/lib/AST/ComputeDependence.cpp | 10 ++++
clang/lib/AST/StmtProfile.cpp | 1 +
clang/lib/AST/TextNodeDumper.cpp | 7 ++-
clang/lib/Sema/SemaExprCXX.cpp | 36 +++++++++++++
clang/lib/Sema/TreeTransform.h | 14 +++--
clang/lib/Serialization/ASTReaderStmt.cpp | 1 +
clang/lib/Serialization/ASTWriterStmt.cpp | 1 +
clang/test/CodeGenCXX/cxx2b-deducing-this.cpp | 12 +++++
clang/test/PCH/cxx23-deducing-this-lambda.cpp | 35 +++++++++++++
clang/test/SemaCXX/cxx2b-deducing-this.cpp | 52 +++++++++++++++++++
12 files changed, 180 insertions(+), 4 deletions(-)
create mode 100644 clang/test/PCH/cxx23-deducing-this-lambda.cpp
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index a0e467b35778c5..9f7973a5169f3c 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -1149,6 +1149,7 @@ class CXXThisExpr : public Expr {
CXXThisExpr(SourceLocation L, QualType Ty, bool IsImplicit, ExprValueKind VK)
: Expr(CXXThisExprClass, Ty, VK, OK_Ordinary) {
CXXThisExprBits.IsImplicit = IsImplicit;
+ CXXThisExprBits.CapturedByCopyInLambdaWithExplicitObjectParameter = false;
CXXThisExprBits.Loc = L;
setDependence(computeDependence(this));
}
@@ -1170,6 +1171,15 @@ class CXXThisExpr : public Expr {
bool isImplicit() const { return CXXThisExprBits.IsImplicit; }
void setImplicit(bool I) { CXXThisExprBits.IsImplicit = I; }
+ bool isCapturedByCopyInLambdaWithExplicitObjectParameter() const {
+ return CXXThisExprBits.CapturedByCopyInLambdaWithExplicitObjectParameter;
+ }
+
+ void setCapturedByCopyInLambdaWithExplicitObjectParameter(bool Set) {
+ CXXThisExprBits.CapturedByCopyInLambdaWithExplicitObjectParameter = Set;
+ setDependence(computeDependence(this));
+ }
+
static bool classof(const Stmt *T) {
return T->getStmtClass() == CXXThisExprClass;
}
diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h
index 55eca4007d17ea..a1312de0abf9a7 100644
--- a/clang/include/clang/AST/Stmt.h
+++ b/clang/include/clang/AST/Stmt.h
@@ -782,6 +782,11 @@ class alignas(void *) Stmt {
LLVM_PREFERRED_TYPE(bool)
unsigned IsImplicit : 1;
+ /// Whether there is a lambda with an explicit object parameter that
+ /// captures this "this" by copy.
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned CapturedByCopyInLambdaWithExplicitObjectParameter : 1;
+
/// The location of the "this".
SourceLocation Loc;
};
diff --git a/clang/lib/AST/ComputeDependence.cpp b/clang/lib/AST/ComputeDependence.cpp
index 9d3856b8f7e08a..cec7fe9b4e7758 100644
--- a/clang/lib/AST/ComputeDependence.cpp
+++ b/clang/lib/AST/ComputeDependence.cpp
@@ -310,6 +310,16 @@ ExprDependence clang::computeDependence(CXXThisExpr *E) {
// 'this' is type-dependent if the class type of the enclosing
// member function is dependent (C++ [temp.dep.expr]p2)
auto D = toExprDependenceForImpliedType(E->getType()->getDependence());
+
+ // If a lambda with an explicit object parameter captures '*this', then
+ // 'this' now refers to the captured copy of lambda, and if the lambda
+ // is type-dependent, so is the object and thus 'this'.
+ //
+ // Note: The standard does not mention this case explicitly, but we need
+ // to do this so we can mark NSDM accesses as dependent.
+ if (E->isCapturedByCopyInLambdaWithExplicitObjectParameter())
+ D |= ExprDependence::Type;
+
assert(!(D & ExprDependence::UnexpandedPack));
return D;
}
diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp
index b545ff472e5a2b..366a0e660012df 100644
--- a/clang/lib/AST/StmtProfile.cpp
+++ b/clang/lib/AST/StmtProfile.cpp
@@ -2011,6 +2011,7 @@ void StmtProfiler::VisitMSPropertySubscriptExpr(
void StmtProfiler::VisitCXXThisExpr(const CXXThisExpr *S) {
VisitExpr(S);
ID.AddBoolean(S->isImplicit());
+ ID.AddBoolean(S->isCapturedByCopyInLambdaWithExplicitObjectParameter());
}
void StmtProfiler::VisitCXXThrowExpr(const CXXThrowExpr *S) {
diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp
index b683eb1edd8f13..57aaa826ce7deb 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -1180,8 +1180,11 @@ void TextNodeDumper::VisitDeclRefExpr(const DeclRefExpr *Node) {
case NOUR_Constant: OS << " non_odr_use_constant"; break;
case NOUR_Discarded: OS << " non_odr_use_discarded"; break;
}
- if (Node->refersToEnclosingVariableOrCapture())
+ if (Node->isCapturedByCopyInLambdaWithExplicitObjectParameter())
+ OS << " dependent_capture";
+ else if (Node->refersToEnclosingVariableOrCapture())
OS << " refers_to_enclosing_variable_or_capture";
+
if (Node->isImmediateEscalating())
OS << " immediate-escalating";
}
@@ -1337,6 +1340,8 @@ void TextNodeDumper::VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *Node) {
void TextNodeDumper::VisitCXXThisExpr(const CXXThisExpr *Node) {
if (Node->isImplicit())
OS << " implicit";
+ if (Node->isCapturedByCopyInLambdaWithExplicitObjectParameter())
+ OS << " dependent_capture";
OS << " this";
}
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index c34a40fa7c81ac..9600f295cebaca 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -1446,6 +1446,42 @@ Expr *Sema::BuildCXXThisExpr(SourceLocation Loc, QualType Type,
void Sema::MarkThisReferenced(CXXThisExpr *This) {
CheckCXXThisCapture(This->getExprLoc());
+ if (This->isTypeDependent())
+ return;
+
+ // Check if 'this' is captured by value in a lambda with a dependent explicit
+ // object parameter, and mark it as type-dependent as well if so.
+ auto IsDependent = [&]() {
+ for (auto *Scope : llvm::reverse(FunctionScopes)) {
+ auto *LSI = dyn_cast<sema::LambdaScopeInfo>(Scope);
+ if (!LSI)
+ continue;
+
+ if (LSI->Lambda && !LSI->Lambda->Encloses(CurContext) &&
+ LSI->AfterParameterList)
+ return false;
+
+ // If this lambda captures 'this' by value, then 'this' is dependent iff
+ // this lambda has a dependent explicit object parameter. If we can't
+ // determine whether it does (e.g. because the CXXMethodDecl's type is
+ // null), assume it doesn't.
+ if (LSI->isCXXThisCaptured()) {
+ if (!LSI->getCXXThisCapture().isCopyCapture())
+ continue;
+
+ const auto *MD = LSI->CallOperator;
+ if (MD->getType().isNull())
+ return false;
+
+ const auto *Ty = cast<FunctionProtoType>(MD->getType());
+ return Ty && MD->isExplicitObjectMemberFunction() &&
+ Ty->getParamType(0)->isDependentType();
+ }
+ }
+ return false;
+ }();
+
+ This->setCapturedByCopyInLambdaWithExplicitObjectParameter(IsDependent);
}
bool Sema::isThisOutsideMemberFunctionBody(QualType BaseType) {
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index e537dfef767df8..5c32ebb3bdf0e2 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -12563,9 +12563,17 @@ TreeTransform<Derived>::TransformCXXThisExpr(CXXThisExpr *E) {
//
// In other contexts, the type of `this` may be overrided
// for type deduction, so we need to recompute it.
- QualType T = getSema().getCurLambda() ?
- getDerived().TransformType(E->getType())
- : getSema().getCurrentThisType();
+ //
+ // Always recompute the type if we're in the body of a lambda, and
+ // 'this' is dependent on a lambda's explicit object parameter.
+ QualType T = [&]() {
+ auto &S = getSema();
+ if (E->isCapturedByCopyInLambdaWithExplicitObjectParameter())
+ return S.getCurrentThisType();
+ if (S.getCurLambda())
+ return getDerived().TransformType(E->getType());
+ return S.getCurrentThisType();
+ }();
if (!getDerived().AlwaysRebuild() && T == E->getType()) {
// Mark it referenced in the new context regardless.
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index 3da44ffccc38a2..66eacc0994f063 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -1849,6 +1849,7 @@ void ASTStmtReader::VisitCXXThisExpr(CXXThisExpr *E) {
VisitExpr(E);
E->setLocation(readSourceLocation());
E->setImplicit(Record.readInt());
+ E->setCapturedByCopyInLambdaWithExplicitObjectParameter(Record.readInt());
}
void ASTStmtReader::VisitCXXThrowExpr(CXXThrowExpr *E) {
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index 484621ae813093..371a82c4dc5bd7 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -1842,6 +1842,7 @@ void ASTStmtWriter::VisitCXXThisExpr(CXXThisExpr *E) {
VisitExpr(E);
Record.AddSourceLocation(E->getLocation());
Record.push_back(E->isImplicit());
+ Record.push_back(E->isCapturedByCopyInLambdaWithExplicitObjectParameter());
Code = serialization::EXPR_CXX_THIS;
}
diff --git a/clang/test/CodeGenCXX/cxx2b-deducing-this.cpp b/clang/test/CodeGenCXX/cxx2b-deducing-this.cpp
index de8c124c050eb0..4bc9bfc61124c7 100644
--- a/clang/test/CodeGenCXX/cxx2b-deducing-this.cpp
+++ b/clang/test/CodeGenCXX/cxx2b-deducing-this.cpp
@@ -109,3 +109,15 @@ void test_temporary() {
//CHECK: %ref.tmp = alloca %struct.MaterializedTemporary, align 1
//CHECK: call void @_ZN21MaterializedTemporaryC1Ev(ptr noundef nonnull align 1 dereferenceable(1) %ref.tmp){{.*}}
//CHECK invoke void @_ZNH21MaterializedTemporary3fooEOS_(ptr noundef nonnull align 1 dereferenceable(1) %ref.tmp){{.*}}
+
+
+namespace GH84163 {
+// Just check that this doesn't crash.
+template <typename> struct S {};
+
+void a() {
+ int x;
+ const auto l = [&x](this auto&) { S<decltype(x)> q; };
+ l();
+}
+}
diff --git a/clang/test/PCH/cxx23-deducing-this-lambda.cpp b/clang/test/PCH/cxx23-deducing-this-lambda.cpp
new file mode 100644
index 00000000000000..21b4bf0d633f2b
--- /dev/null
+++ b/clang/test/PCH/cxx23-deducing-this-lambda.cpp
@@ -0,0 +1,35 @@
+// RUN: %clang_cc1 -emit-pch -std=c++23 -o %t %s
+// RUN: %clang_cc1 -include-pch %t -verify -fsyntax-only -DTEST -std=c++23 %s
+
+// Test that dependence of 'this' and DREs due to by-value capture by a
+// lambda with an explicit object parameter is serialised/deserialised
+// properly.
+
+#ifndef HEADER
+#define HEADER
+struct S {
+ int x;
+ auto f() {
+ return [*this] (this auto&&) {
+ int y;
+ x = 42;
+
+ const auto l = [y] (this auto&&) { y = 42; };
+ l();
+ };
+ }
+};
+#endif
+
+// expected-error@* {{read-only variable is not assignable}}
+// expected-error@* {{cannot assign to a variable captured by copy in a non-mutable lambda}}
+// expected-note@* 2 {{in instantiation of}}
+
+#ifdef TEST
+void f() {
+ const auto l = S{}.f();
+ l(); // expected-note {{in instantiation of}}
+}
+#endif
+
+
diff --git a/clang/test/SemaCXX/cxx2b-deducing-this.cpp b/clang/test/SemaCXX/cxx2b-deducing-this.cpp
index 6c21954554d281..5c690342608ef0 100644
--- a/clang/test/SemaCXX/cxx2b-deducing-this.cpp
+++ b/clang/test/SemaCXX/cxx2b-deducing-this.cpp
@@ -261,6 +261,36 @@ void TestMutationInLambda() {
l13a();
};
+ struct S {
+ int x;
+ auto f() {
+ return [*this] (this auto&&) {
+ x = 42; // expected-error {{read-only variable is not assignable}}
+ [*this] () mutable { x = 42; } ();
+ [*this] (this auto&&) { x = 42; } ();
+ [*this] () { x = 42; } (); // expected-error {{read-only variable is not assignable}}
+ const auto l = [*this] (this auto&&) { x = 42; }; // expected-error {{read-only variable is not assignable}}
+ l(); // expected-note {{in instantiation of}}
+
+ struct T {
+ int x;
+ auto g() {
+ return [&] (this auto&&) {
+ x = 42;
+ const auto l = [*this] (this auto&&) { x = 42; }; // expected-error {{read-only variable is not assignable}}
+ l(); // expected-note {{in instantiation of}}
+ };
+ }
+ };
+
+ const auto l2 = T{}.g();
+ l2(); // expected-note {{in instantiation of}}
+ };
+ }
+ };
+
+ const auto l14 = S{}.f();
+
l1(); // expected-note {{in instantiation of}}
l2(); // expected-note {{in instantiation of}}
l3(); // expected-note {{in instantiation of}}
@@ -274,6 +304,7 @@ void TestMutationInLambda() {
l11(); // expected-note {{in instantiation of}}
l12(); // expected-note {{in instantiation of}}
l13(); // expected-note {{in instantiation of}}
+ l14(); // expected-note 3 {{in instantiation of}}
{
const auto l1 = [&x](this auto&) { x = 42; };
@@ -731,3 +762,24 @@ int bug() {
S{}.f(0);
}
}
+
+namespace GH84163 {
+struct S {
+ int x;
+
+ auto foo() {
+ return [*this](this auto&&) {
+ x = 10; // expected-error {{read-only variable is not assignable}}
+ };
+ }
+};
+
+int f() {
+ S s{ 5 };
+ const auto l = s.foo();
+ l(); // expected-note {{in instantiation of}}
+
+ const auto g = [x = 10](this auto&& self) { x = 20; }; // expected-error {{cannot assign to a variable captured by copy in a non-mutable lambda}}
+ g(); // expected-note {{in instantiation of}}
+}
+}
More information about the cfe-commits
mailing list