Author: Alexander Kornienko
Date: 2026-07-22T15:29:37+02:00
New Revision: 150aa53d48aa6fdde26e00ea118582eb949f0dca

URL: 
https://github.com/llvm/llvm-project/commit/150aa53d48aa6fdde26e00ea118582eb949f0dca
DIFF: 
https://github.com/llvm/llvm-project/commit/150aa53d48aa6fdde26e00ea118582eb949f0dca.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

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 b844670543a55..81cff1e374aaf 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -5698,17 +5698,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());
   }
 
@@ -5723,51 +5718,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@+1 {{call to consteval function}}
-      [noisy = Noisy{x}] {}};
-};
-
-int foo();
-// expected-note@+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 {
 


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to