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
