https://github.com/Sirraide updated https://github.com/llvm/llvm-project/pull/84473
>From 870e6a6def8c17859ffbb30906f91912268f872d Mon Sep 17 00:00:00 2001 From: Sirraide <aeternalm...@gmail.com> Date: Fri, 8 Mar 2024 11:55:42 +0100 Subject: [PATCH 01/10] [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@-1 {{cannot assign to a variable captured by copy in a non-mutable lambda}} + // expected-note@-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 <aeternalm...@gmail.com> Date: Fri, 8 Mar 2024 13:37:57 +0100 Subject: [PATCH 02/10] [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 <aeternalm...@gmail.com> Date: Fri, 8 Mar 2024 15:34:05 +0100 Subject: [PATCH 03/10] [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}} +} +} >From 0a03b5fe9d1b5cb8481340fb30c761ff1217282d Mon Sep 17 00:00:00 2001 From: Sirraide <aeternalm...@gmail.com> Date: Fri, 8 Mar 2024 15:38:01 +0100 Subject: [PATCH 04/10] [Clang] Add release note --- clang/docs/ReleaseNotes.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 0ff4a93b15ea8f..123ce62d9eacca 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -319,6 +319,9 @@ Bug Fixes to C++ Support Fixes (#GH80630) - Fix a crash when an explicit template argument list is used with a name for which lookup finds a non-template function and a dependent using declarator. +- Clang now correctly tracks type dependence of by-value captures in lambdas with explicit + object parameters. + Fixes (#GH84163). Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ >From eeae945f9fc0c16e77b699625b151bce5af22012 Mon Sep 17 00:00:00 2001 From: Sirraide <aeternalm...@gmail.com> Date: Fri, 8 Mar 2024 15:45:08 +0100 Subject: [PATCH 05/10] [Clang] Add tests for issue #84425 --- clang/test/CodeGenCXX/cxx2b-deducing-this.cpp | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/clang/test/CodeGenCXX/cxx2b-deducing-this.cpp b/clang/test/CodeGenCXX/cxx2b-deducing-this.cpp index 4bc9bfc61124c7..6111a63c572d9f 100644 --- a/clang/test/CodeGenCXX/cxx2b-deducing-this.cpp +++ b/clang/test/CodeGenCXX/cxx2b-deducing-this.cpp @@ -121,3 +121,24 @@ void a() { l(); } } + +namespace GH84425 { +// Check that this doesn't crash. +void do_thing(int x) { + auto second = [&](this auto const& self, int b) -> int { + if (x) return x; + else return self(x); + }; + + second(1); +} + +void do_thing2(int x) { + auto second = [&](this auto const& self) { + if (true) return x; + else return x; + }; + + second(); +} +} >From 60917a0eacc94c5a8620df9bd3ffdfe989d5f3a7 Mon Sep 17 00:00:00 2001 From: Sirraide <aeternalm...@gmail.com> Date: Sat, 23 Mar 2024 16:45:31 +0100 Subject: [PATCH 06/10] [Clang] Add test for #86054 --- clang/test/SemaCXX/cxx2b-deducing-this.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/clang/test/SemaCXX/cxx2b-deducing-this.cpp b/clang/test/SemaCXX/cxx2b-deducing-this.cpp index 5c690342608ef0..26129716f30ba9 100644 --- a/clang/test/SemaCXX/cxx2b-deducing-this.cpp +++ b/clang/test/SemaCXX/cxx2b-deducing-this.cpp @@ -783,3 +783,16 @@ int f() { g(); // expected-note {{in instantiation of}} } } + +namespace GH86054 { +template<typename M> +struct unique_lock { + unique_lock(M&) {} +}; +int f() { + struct mutex {} cursor_guard; + [&cursor_guard](this auto self) { + unique_lock a(cursor_guard); + }(); +} +} >From 957dd9adb16811d14413a08cd58ac6c2b753afd9 Mon Sep 17 00:00:00 2001 From: Sirraide <aeternalm...@gmail.com> Date: Sat, 23 Mar 2024 16:58:05 +0100 Subject: [PATCH 07/10] [Clang] Add test for #86398 --- clang/test/SemaCXX/cxx2b-deducing-this.cpp | 30 ++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/clang/test/SemaCXX/cxx2b-deducing-this.cpp b/clang/test/SemaCXX/cxx2b-deducing-this.cpp index 26129716f30ba9..c14c971afd2359 100644 --- a/clang/test/SemaCXX/cxx2b-deducing-this.cpp +++ b/clang/test/SemaCXX/cxx2b-deducing-this.cpp @@ -796,3 +796,33 @@ int f() { }(); } } + +namespace GH86398 { +struct function {}; // expected-note 2 {{not viable}} +int f() { + function list; + [&list](this auto self) { + list = self; // expected-error {{no viable overloaded '='}} + }(); // expected-note {{in instantiation of}} +} + +struct function2 { + function2& operator=(function2 const&) = delete; // expected-note {{candidate function not viable}} +}; +int g() { + function2 list; + [&list](this auto self) { + list = self; // expected-error {{no viable overloaded '='}} + }(); // expected-note {{in instantiation of}} +} + +struct function3 { + function3& operator=(function3 const&) = delete; // expected-note {{has been explicitly deleted}} +}; +int h() { + function3 list; + [&list](this auto self) { + list = function3{}; // expected-error {{selected deleted operator '='}} + }(); +} +} >From f5964b982297619810765fd482cc1a4d158c2def Mon Sep 17 00:00:00 2001 From: Sirraide <aeternalm...@gmail.com> Date: Sat, 23 Mar 2024 17:12:49 +0100 Subject: [PATCH 08/10] [Clang] Add test for #86399 --- clang/test/CodeGenCXX/cxx2b-deducing-this.cpp | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/clang/test/CodeGenCXX/cxx2b-deducing-this.cpp b/clang/test/CodeGenCXX/cxx2b-deducing-this.cpp index 6111a63c572d9f..b2dd05cf73a898 100644 --- a/clang/test/CodeGenCXX/cxx2b-deducing-this.cpp +++ b/clang/test/CodeGenCXX/cxx2b-deducing-this.cpp @@ -110,6 +110,26 @@ void test_temporary() { //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 GH86399 { +volatile int a = 0; +struct function { + function& operator=(function const&) { + a = 1; + return *this; + } +}; + +void f() { + function list; + + //CHECK-LABEL: define internal void @"_ZZN7GH863991f{{.*}}"(ptr %{{.*}}) + //CHECK: call {{.*}} @_ZN7GH863998functionaSERKS0_ + //CHECK-NEXT: ret void + [&list](this auto self) { + list = function{}; + }(); +} +} namespace GH84163 { // Just check that this doesn't crash. >From 858cc50dac6ccd9f82a726b730e2370598e6d59c Mon Sep 17 00:00:00 2001 From: Sirraide <aeternalm...@gmail.com> Date: Sat, 23 Mar 2024 17:23:43 +0100 Subject: [PATCH 09/10] [Clang] Add test for #79754 --- clang/test/CodeGenCXX/cxx2b-deducing-this.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/clang/test/CodeGenCXX/cxx2b-deducing-this.cpp b/clang/test/CodeGenCXX/cxx2b-deducing-this.cpp index b2dd05cf73a898..c56263d6002ed3 100644 --- a/clang/test/CodeGenCXX/cxx2b-deducing-this.cpp +++ b/clang/test/CodeGenCXX/cxx2b-deducing-this.cpp @@ -132,7 +132,8 @@ void f() { } namespace GH84163 { -// Just check that this doesn't crash. +// Just check that this doesn't crash (we were previously not instantiating +// everything that needs instantiating in here). template <typename> struct S {}; void a() { @@ -143,7 +144,7 @@ void a() { } namespace GH84425 { -// Check that this doesn't crash. +// As above. void do_thing(int x) { auto second = [&](this auto const& self, int b) -> int { if (x) return x; @@ -162,3 +163,11 @@ void do_thing2(int x) { second(); } } + +namespace GH79754 { +// As above. +void f() { + int x; + [&x](this auto&&) {return x;}(); +} +} >From 5e3a72cb1c2f77257de148c17e828ed12077eae0 Mon Sep 17 00:00:00 2001 From: Sirraide <aeternalm...@gmail.com> Date: Sat, 23 Mar 2024 17:31:02 +0100 Subject: [PATCH 10/10] [Clang] Add test for #70604 --- clang/test/CodeGenCXX/cxx2b-deducing-this.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/clang/test/CodeGenCXX/cxx2b-deducing-this.cpp b/clang/test/CodeGenCXX/cxx2b-deducing-this.cpp index c56263d6002ed3..b755e80db35a12 100644 --- a/clang/test/CodeGenCXX/cxx2b-deducing-this.cpp +++ b/clang/test/CodeGenCXX/cxx2b-deducing-this.cpp @@ -171,3 +171,14 @@ void f() { [&x](this auto&&) {return x;}(); } } + +namespace GH70604 { +auto dothing(int num) +{ + auto fun = [&num](this auto&& self) -> void { + auto copy = num; + }; + + fun(); +} +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits