[llvm-branch-commits] [clang] 3eed3d7 - Revert "[Clang] Rebuild lambda captures in default member initializers while skipping body (#196597)" (#211001)
Tobias Hieta via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Wed Jul 22 23:03:20 PDT 2026
Author: Alexander Kornienko
Date: 2026-07-23T08:03:10+02:00
New Revision: 3eed3d7f35e313f3d5b51c695777196126112210
URL: https://github.com/llvm/llvm-project/commit/3eed3d7f35e313f3d5b51c695777196126112210
DIFF: https://github.com/llvm/llvm-project/commit/3eed3d7f35e313f3d5b51c695777196126112210.diff
LOG: Revert "[Clang] Rebuild lambda captures in default member initializers while skipping body (#196597)" (#211001)
This reverts commit 50f30bedaa81919915049474f4350ef19c36b7ca. The commit
causes a crash in clang:
https://github.com/llvm/llvm-project/pull/196597#issuecomment-4997866573
(cherry picked from commit 150aa53d48aa6fdde26e00ea118582eb949f0dca)
Added:
Modified:
clang/lib/Sema/SemaExpr.cpp
clang/test/SemaCXX/source_location.cpp
Removed:
clang/test/CodeGenCXX/gh196469-default-member-init-lambda-cleanup.cpp
clang/test/SemaCXX/gh196469-default-member-init-lambda-capture.cpp
################################################################################
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 6d7cdf9f60dea..5d4c5ff7d5c68 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -5704,17 +5704,12 @@ struct ImmediateCallVisitor : DynamicRecursiveASTVisitor {
}
// A nested lambda might have parameters with immediate invocations
- // in their default arguments, or init-captures that are evaluated in the
- // enclosing context.
+ // in their default arguments.
// The compound statement is not visited (as it does not constitute a
// subexpression).
+ // FIXME: We should consider visiting and transforming captures
+ // with init expressions.
bool VisitLambdaExpr(LambdaExpr *E) override {
- auto Init = E->capture_init_begin();
- for (auto C = E->capture_begin(), CEnd = E->capture_end(); C != CEnd;
- ++C, ++Init) {
- if (E->isInitCapture(C) && !TraverseLambdaCapture(E, C, *Init))
- return false;
- }
return VisitCXXMethodDecl(E->getCallOperator());
}
@@ -5729,51 +5724,16 @@ struct ImmediateCallVisitor : DynamicRecursiveASTVisitor {
struct EnsureImmediateInvocationInDefaultArgs
: TreeTransform<EnsureImmediateInvocationInDefaultArgs> {
- using Base = TreeTransform<EnsureImmediateInvocationInDefaultArgs>;
-
EnsureImmediateInvocationInDefaultArgs(Sema &SemaRef)
: TreeTransform(SemaRef) {}
bool AlwaysRebuild() { return true; }
- bool ReplacingOriginal() { return true; }
-
- // Lambda bodies are not subexpressions of the enclosing default initializer,
- // but init-capture expressions are evaluated in the enclosing context. Keep
- // the existing closure type and capture declarations so the existing body
- // still refers to the right declarations.
- ExprResult TransformLambdaExpr(LambdaExpr *E) {
- SmallVector<Expr *, 4> CaptureInits(E->capture_inits());
-
- bool Changed = false;
- for (unsigned I = 0, N = E->capture_size(); I != N; ++I) {
- const LambdaCapture *C = E->capture_begin() + I;
- if (!E->isInitCapture(C))
- continue;
-
- auto *VD = cast<VarDecl>(C->getCapturedVar());
- Expr *Init = CaptureInits[I];
- ExprResult NewInit =
- TransformInitializer(Init, VD->getInitStyle() == VarDecl::CallInit);
- if (NewInit.isInvalid())
- return ExprError();
- Changed |= NewInit.get() != Init;
- CaptureInits[I] = NewInit.get();
- }
- LambdaExpr *Lambda = E;
- if (Changed) {
- // Reuse the existing closure class: it owns the capture declarations,
- // fields, and call operator body. Only the LambdaExpr's capture
- // initializer list is replaced.
- Lambda = LambdaExpr::Create(
- SemaRef.Context, E->getLambdaClass(), E->getIntroducerRange(),
- E->getCaptureDefault(), E->getCaptureDefaultLoc(),
- E->hasExplicitParameters(), E->hasExplicitResultType(), CaptureInits,
- E->getEndLoc(), E->containsUnexpandedParameterPack());
- }
-
- return SemaRef.MaybeBindToTemporary(Lambda);
- }
+ // Lambda can only have immediate invocations in the default
+ // args of their parameters, which is transformed upon calling the closure.
+ // The body is not a subexpression, so we have nothing to do.
+ // FIXME: Immediate calls in capture initializers should be transformed.
+ ExprResult TransformLambdaExpr(LambdaExpr *E) { return E; }
ExprResult TransformBlockExpr(BlockExpr *E) { return E; }
// Make sure we don't rebuild the this pointer as it would
diff --git a/clang/test/CodeGenCXX/gh196469-default-member-init-lambda-cleanup.cpp b/clang/test/CodeGenCXX/gh196469-default-member-init-lambda-cleanup.cpp
deleted file mode 100644
index 905db0f583afd..0000000000000
--- a/clang/test/CodeGenCXX/gh196469-default-member-init-lambda-cleanup.cpp
+++ /dev/null
@@ -1,37 +0,0 @@
-// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -emit-llvm -o - %s | FileCheck %s
-
-struct Noisy {
- Noisy();
- ~Noisy();
-};
-
-struct Function {
- template <typename F> Function(F) {}
-};
-
-struct Options {
- Function function{[noisy = Noisy{}] {}};
-};
-
-Options kOptions{};
-
-int side();
-
-struct ReturnsCapture {
- int x;
- int value = [value = x] { return value; }();
-};
-
-ReturnsCapture kReturnsCapture{side()};
-
-// CHECK-LABEL: define internal void @__cxx_global_var_init
-// CHECK: call void @_ZN5NoisyC1Ev
-// CHECK: call void @_ZN8FunctionC1IN7Options8functionMUlvE_EEET_
-// CHECK: call void @_ZN7Options8functionMUlvE_D1Ev
-// CHECK: call {{.*}} @_ZNK14ReturnsCapture5valueMUlvE_clEv
-
-// CHECK-LABEL: define linkonce_odr {{.*}} @_ZNK14ReturnsCapture5valueMUlvE_clEv
-// CHECK: ret i32
-
-// CHECK-LABEL: define {{.*}} @_ZN7Options8functionMUlvE_D2Ev
-// CHECK: call void @_ZN5NoisyD1Ev
diff --git a/clang/test/SemaCXX/gh196469-default-member-init-lambda-capture.cpp b/clang/test/SemaCXX/gh196469-default-member-init-lambda-capture.cpp
deleted file mode 100644
index ed8335775ba67..0000000000000
--- a/clang/test/SemaCXX/gh196469-default-member-init-lambda-capture.cpp
+++ /dev/null
@@ -1,22 +0,0 @@
-// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify -verify-ignore-unexpected=note %s
-
-struct Noisy {
- int x;
- consteval Noisy(int x) : x(x) {}
- ~Noisy() {}
-};
-
-struct Function {
- template <typename F> Function(F) {}
-};
-
-struct Options {
- int x;
- Function function{ // expected-note {{declared here}}
- // expected-error at +1 {{call to consteval function}}
- [noisy = Noisy{x}] {}};
-};
-
-int foo();
-// expected-note at +1 {{in the default initializer of 'function'}}
-Options options{foo()};
diff --git a/clang/test/SemaCXX/source_location.cpp b/clang/test/SemaCXX/source_location.cpp
index 1ede22eaf458a..eaa6cb04c5d1c 100644
--- a/clang/test/SemaCXX/source_location.cpp
+++ b/clang/test/SemaCXX/source_location.cpp
@@ -862,11 +862,23 @@ struct CompoundLiteral {
static_assert(CompoundLiteral{}.a == __LINE__);
+// FIXME
+// Init captures are subexpressions of the lambda expression
+// so according to the standard immediate invocations in init captures
+// should be evaluated at the call site.
+// However Clang does not yet implement this as it would introduce
+// a fair bit of complexity.
+// We intend to implement that functionality once we find real world
+// use cases that require it.
constexpr int test_init_capture(int a =
[b = SL::current().line()] { return b; }()) {
return a;
}
+#if defined(USE_CONSTEVAL) && !defined(NEW_INTERP)
+static_assert(test_init_capture() == __LINE__ - 4);
+#else
static_assert(test_init_capture() == __LINE__ );
+#endif
namespace check_immediate_invocations_in_templates {
More information about the llvm-branch-commits
mailing list