[clang] 3d361b2 - [Clang][Sema] Tweak tryCaptureVariable for unevaluated lambdas (#93206)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Jun 3 21:44:04 PDT 2024
Author: Younan Zhang
Date: 2024-06-04T12:44:01+08:00
New Revision: 3d361b225fe89ce1d8c93639f27d689082bd8dad
URL: https://github.com/llvm/llvm-project/commit/3d361b225fe89ce1d8c93639f27d689082bd8dad
DIFF: https://github.com/llvm/llvm-project/commit/3d361b225fe89ce1d8c93639f27d689082bd8dad.diff
LOG: [Clang][Sema] Tweak tryCaptureVariable for unevaluated lambdas (#93206)
This patch picks up #78598 with the hope that we can address such
crashes in `tryCaptureVariable()` for unevaluated lambdas.
In addition to `tryCaptureVariable()`, this also contains several other
fixes on e.g. lambda parsing/dependencies.
Fixes #63845
Fixes #67260
Fixes #69307
Fixes #88081
Fixes #89496
Fixes #90669
Fixes #91633
Added:
Modified:
clang/docs/ReleaseNotes.rst
clang/include/clang/AST/DeclBase.h
clang/lib/Parse/ParseExprCXX.cpp
clang/lib/Sema/SemaExpr.cpp
clang/lib/Sema/SemaLambda.cpp
clang/lib/Sema/TreeTransform.h
clang/test/SemaCXX/lambda-unevaluated.cpp
Removed:
################################################################################
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 32515fbac64f6..99580c0d28a4f 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -824,6 +824,8 @@ Bug Fixes to C++ Support
- Fix a crash when a variable is captured by a block nested inside a lambda. (Fixes #GH93625).
- Fixed a type constraint substitution issue involving a generic lambda expression. (#GH93821)
- Fix a crash caused by improper use of ``__array_extent``. (#GH80474)
+- Fixed several bugs in capturing variables within unevaluated contexts. (#GH63845), (#GH67260), (#GH69307),
+ (#GH88081), (#GH89496), (#GH90669) and (#GH91633).
Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h
index e43e812cd9455..3a311d4c55916 100644
--- a/clang/include/clang/AST/DeclBase.h
+++ b/clang/include/clang/AST/DeclBase.h
@@ -2148,6 +2148,10 @@ class DeclContext {
getDeclKind() <= Decl::lastRecord;
}
+ bool isRequiresExprBody() const {
+ return getDeclKind() == Decl::RequiresExprBody;
+ }
+
bool isNamespace() const { return getDeclKind() == Decl::Namespace; }
bool isStdNamespace() const;
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index 6f21a4f9bd826..1d364f77a8146 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -1581,7 +1581,10 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
TrailingReturnTypeLoc, &DS),
std::move(Attributes), DeclEndLoc);
- Actions.ActOnLambdaClosureQualifiers(Intro, MutableLoc);
+ // We have called ActOnLambdaClosureQualifiers for parentheses-less cases
+ // above.
+ if (HasParentheses)
+ Actions.ActOnLambdaClosureQualifiers(Intro, MutableLoc);
if (HasParentheses && Tok.is(tok::kw_requires))
ParseTrailingRequiresClause(D);
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index fb4154757775b..a50d5c9d6cdc9 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -18768,6 +18768,10 @@ bool Sema::tryCaptureVariable(
DeclContext *VarDC = Var->getDeclContext();
DeclContext *DC = CurContext;
+ // Skip past RequiresExprBodys because they don't constitute function scopes.
+ while (DC->isRequiresExprBody())
+ DC = DC->getParent();
+
// tryCaptureVariable is called every time a DeclRef is formed,
// it can therefore have non-negigible impact on performances.
// For local variables and when there is no capturing scope,
@@ -18775,6 +18779,12 @@ bool Sema::tryCaptureVariable(
if (CapturingFunctionScopes == 0 && (!BuildAndDiagnose || VarDC == DC))
return true;
+ // Exception: Function parameters are not tied to the function's DeclContext
+ // until we enter the function definition. Capturing them anyway would result
+ // in an out-of-bounds error while traversing DC and its parents.
+ if (isa<ParmVarDecl>(Var) && !VarDC->isFunctionOrMethod())
+ return true;
+
const auto *VD = dyn_cast<VarDecl>(Var);
if (VD) {
if (VD->isInitCapture())
diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp
index 276a43ad79b91..e9476a0c93c5d 100644
--- a/clang/lib/Sema/SemaLambda.cpp
+++ b/clang/lib/Sema/SemaLambda.cpp
@@ -1076,16 +1076,27 @@ void Sema::ActOnLambdaExpressionAfterIntroducer(LambdaIntroducer &Intro,
// be dependent, because there are template parameters in scope.
CXXRecordDecl::LambdaDependencyKind LambdaDependencyKind =
CXXRecordDecl::LDK_Unknown;
- if (LSI->NumExplicitTemplateParams > 0) {
- Scope *TemplateParamScope = CurScope->getTemplateParamParent();
- assert(TemplateParamScope &&
- "Lambda with explicit template param list should establish a "
- "template param scope");
- assert(TemplateParamScope->getParent());
- if (TemplateParamScope->getParent()->getTemplateParamParent() != nullptr)
- LambdaDependencyKind = CXXRecordDecl::LDK_AlwaysDependent;
- } else if (CurScope->getTemplateParamParent() != nullptr) {
+ if (CurScope->getTemplateParamParent() != nullptr) {
LambdaDependencyKind = CXXRecordDecl::LDK_AlwaysDependent;
+ } else if (Scope *P = CurScope->getParent()) {
+ // Given a lambda defined inside a requires expression,
+ //
+ // struct S {
+ // S(auto var) requires requires { [&] -> decltype(var) { }; }
+ // {}
+ // };
+ //
+ // The parameter var is not injected into the function Decl at the point of
+ // parsing lambda. In such scenarios, perceiving it as dependent could
+ // result in the constraint being evaluated, which matches what GCC does.
+ while (P->getEntity() && P->getEntity()->isRequiresExprBody())
+ P = P->getParent();
+ if (P->isFunctionDeclarationScope() &&
+ llvm::any_of(P->decls(), [](Decl *D) {
+ return isa<ParmVarDecl>(D) &&
+ cast<ParmVarDecl>(D)->getType()->isTemplateTypeParmType();
+ }))
+ LambdaDependencyKind = CXXRecordDecl::LDK_AlwaysDependent;
}
CXXRecordDecl *Class = createLambdaClosureType(
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index efba99b85b0fb..70603ba6c2717 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -14247,7 +14247,7 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
// will be deemed as dependent even if there are no dependent template
// arguments.
// (A ClassTemplateSpecializationDecl is always a dependent context.)
- while (DC->getDeclKind() == Decl::Kind::RequiresExprBody)
+ while (DC->isRequiresExprBody())
DC = DC->getParent();
if ((getSema().isUnevaluatedContext() ||
getSema().isConstantEvaluatedContext()) &&
diff --git a/clang/test/SemaCXX/lambda-unevaluated.cpp b/clang/test/SemaCXX/lambda-unevaluated.cpp
index 10d4c2228ec9b..39ee89bc797f8 100644
--- a/clang/test/SemaCXX/lambda-unevaluated.cpp
+++ b/clang/test/SemaCXX/lambda-unevaluated.cpp
@@ -189,3 +189,78 @@ void recursive() {
}
}
+
+// GH63845: Test if we have skipped past RequiresExprBodyDecls in tryCaptureVariable().
+namespace GH63845 {
+
+template <bool> struct A {};
+
+struct true_type {
+ constexpr operator bool() noexcept { return true; }
+};
+
+constexpr bool foo() {
+ true_type x{};
+ return requires { typename A<x>; };
+}
+
+static_assert(foo());
+
+} // namespace GH63845
+
+// GH69307: Test if we can correctly handle param decls that have yet to get into the function scope.
+namespace GH69307 {
+
+constexpr auto ICE() {
+ constexpr auto b = 1;
+ return [=](auto c) -> int
+ requires requires { b + c; }
+ { return 1; };
+};
+
+constexpr auto Ret = ICE()(1);
+
+} // namespace GH69307
+
+// GH88081: Test if we evaluate the requires expression with lambda captures properly.
+namespace GH88081 {
+
+// Test that ActOnLambdaClosureQualifiers() is called only once.
+void foo(auto value)
+ requires requires { [&] -> decltype(value) {}; }
+ // expected-error at -1 {{non-local lambda expression cannot have a capture-default}}
+{}
+
+struct S { //#S
+ S(auto value) //#S-ctor
+ requires requires { [&] -> decltype(value) { return 2; }; } {} // #S-requires
+
+ static auto foo(auto value) -> decltype([&]() -> decltype(value) {}()) { return {}; } // #S-foo
+
+ // FIXME: 'value' does not constitute an ODR use here. Add a diagnostic for it.
+ static auto bar(auto value) -> decltype([&] { return value; }()) {
+ return "a"; // #bar-body
+ }
+};
+
+S s("a"); // #use
+// expected-error@#S-requires {{cannot initialize return object of type 'decltype(value)' (aka 'const char *') with an rvalue of type 'int'}}
+// expected-error@#use {{no matching constructor}}
+// expected-note@#S-requires {{substituting into a lambda expression here}}
+// expected-note@#S-requires {{substituting template arguments into constraint expression here}}
+// expected-note@#S-requires {{in instantiation of requirement here}}
+// expected-note@#use {{checking constraint satisfaction for template 'S<const char *>' required here}}
+// expected-note@#use {{requested here}}
+// expected-note-re@#S 2{{candidate constructor {{.*}} not viable}}
+// expected-note@#S-ctor {{constraints not satisfied}}
+// expected-note-re@#S-requires {{because {{.*}} would be invalid}}
+
+void func() {
+ S::foo(42);
+ S::bar("str");
+ S::bar(0.618);
+ // expected-error-re@#bar-body {{cannot initialize return object of type {{.*}} (aka 'double') with an lvalue of type 'const char[2]'}}
+ // expected-note at -2 {{requested here}}
+}
+
+} // namespace GH88081
More information about the cfe-commits
mailing list