[clang] 1b804e2 - [LifetimeSafety] Track origins through std::function (#191123)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Apr 14 02:31:41 PDT 2026
Author: Zhijie Wang
Date: 2026-04-14T15:01:35+05:30
New Revision: 1b804e20b9d258c8f0b454d43013cf2300f0df26
URL: https://github.com/llvm/llvm-project/commit/1b804e20b9d258c8f0b454d43013cf2300f0df26
DIFF: https://github.com/llvm/llvm-project/commit/1b804e20b9d258c8f0b454d43013cf2300f0df26.diff
LOG: [LifetimeSafety] Track origins through std::function (#191123)
1. Recognizes `std::function` and `std::move_only_function` as types
that can carry origins from a wrapped lambda's captures, propagating
origins through both construction and assignment.
2. Adds a kill-only mechanism (i.e., a new `KillOriginFact`) to clear
old loans when the RHS has no origins.
Fixes #186009
Added:
Modified:
clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
clang/lib/Analysis/LifetimeSafety/Dataflow.h
clang/lib/Analysis/LifetimeSafety/Facts.cpp
clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp
clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
clang/lib/Analysis/LifetimeSafety/Origins.cpp
clang/test/Sema/Inputs/lifetime-analysis.h
clang/test/Sema/warn-lifetime-safety-dangling-field.cpp
clang/test/Sema/warn-lifetime-safety-invalidations.cpp
clang/test/Sema/warn-lifetime-safety-noescape.cpp
clang/test/Sema/warn-lifetime-safety-suggestions.cpp
clang/test/Sema/warn-lifetime-safety.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
index 6be8f6e455bc2..b70fecd5ab1d1 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
@@ -55,6 +55,8 @@ class Fact {
OriginEscapes,
/// An origin is invalidated (e.g. vector resized).
InvalidateOrigin,
+ /// All loans of an origin are cleared.
+ KillOrigin,
};
private:
@@ -316,6 +318,24 @@ class TestPointFact : public Fact {
const OriginManager &) const override;
};
+/// All loans are cleared from an origin (e.g., assigning a callable without
+/// tracked origins to std::function).
+class KillOriginFact : public Fact {
+ OriginID OID;
+
+public:
+ static bool classof(const Fact *F) {
+ return F->getKind() == Kind::KillOrigin;
+ }
+
+ KillOriginFact(OriginID OID) : Fact(Kind::KillOrigin), OID(OID) {}
+
+ OriginID getKilledOrigin() const { return OID; }
+
+ void dump(llvm::raw_ostream &OS, const LoanManager &,
+ const OriginManager &OM) const override;
+};
+
class FactManager {
public:
FactManager(const AnalysisDeclContext &AC, const CFG &Cfg) : OriginMgr(AC) {
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
index aa9ae4b2a5e6a..098c15f4a7fb4 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
@@ -80,6 +80,10 @@ bool isUniquePtrRelease(const CXXMethodDecl &MD);
// https://en.cppreference.com/w/cpp/container#Iterator_invalidation
bool isContainerInvalidationMethod(const CXXMethodDecl &MD);
+/// Returns true for standard library callable wrappers (e.g., std::function)
+/// that can propagate the stored lambda's origins.
+bool isStdCallableWrapperType(const CXXRecordDecl *RD);
+
} // namespace clang::lifetimes
#endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMEANNOTATIONS_H
diff --git a/clang/lib/Analysis/LifetimeSafety/Dataflow.h b/clang/lib/Analysis/LifetimeSafety/Dataflow.h
index 0f64ac8a36ef7..fc3049c8bec84 100644
--- a/clang/lib/Analysis/LifetimeSafety/Dataflow.h
+++ b/clang/lib/Analysis/LifetimeSafety/Dataflow.h
@@ -180,6 +180,8 @@ class DataflowAnalysis {
return D->transfer(In, *F->getAs<TestPointFact>());
case Fact::Kind::InvalidateOrigin:
return D->transfer(In, *F->getAs<InvalidateOriginFact>());
+ case Fact::Kind::KillOrigin:
+ return D->transfer(In, *F->getAs<KillOriginFact>());
}
llvm_unreachable("Unknown fact kind");
}
@@ -193,6 +195,7 @@ class DataflowAnalysis {
Lattice transfer(Lattice In, const UseFact &) { return In; }
Lattice transfer(Lattice In, const TestPointFact &) { return In; }
Lattice transfer(Lattice In, const InvalidateOriginFact &) { return In; }
+ Lattice transfer(Lattice In, const KillOriginFact &) { return In; }
};
} // namespace clang::lifetimes::internal
#endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_DATAFLOW_H
diff --git a/clang/lib/Analysis/LifetimeSafety/Facts.cpp b/clang/lib/Analysis/LifetimeSafety/Facts.cpp
index 1bc0521a72359..3d7fbcdacc830 100644
--- a/clang/lib/Analysis/LifetimeSafety/Facts.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Facts.cpp
@@ -103,6 +103,13 @@ void TestPointFact::dump(llvm::raw_ostream &OS, const LoanManager &,
OS << "TestPoint (Annotation: \"" << getAnnotation() << "\")\n";
}
+void KillOriginFact::dump(llvm::raw_ostream &OS, const LoanManager &,
+ const OriginManager &OM) const {
+ OS << "KillOrigin (";
+ OM.dump(getKilledOrigin(), OS);
+ OS << ")\n";
+}
+
llvm::StringMap<ProgramPoint> FactManager::getTestPoints() const {
llvm::StringMap<ProgramPoint> AnnotationToPointMap;
for (const auto &BlockFacts : BlockToFacts) {
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index cae56ddd3d7c3..fcc6035c3c1c8 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -190,9 +190,8 @@ void FactsGenerator::VisitCXXConstructExpr(const CXXConstructExpr *CCE) {
return;
}
// For defaulted (implicit or `= default`) copy/move constructors, propagate
- // origins directly. User-defined copy/move constructors have opaque semantics
- // and fall through to `handleFunctionCall`, where [[clang::lifetimebound]] is
- // needed to propagate origins.
+ // origins directly. User-defined copy/move constructors are not handled here
+ // as they have opaque semantics.
if (CCE->getConstructor()->isCopyOrMoveConstructor() &&
CCE->getConstructor()->isDefaulted() && CCE->getNumArgs() == 1 &&
hasOrigins(CCE->getType())) {
@@ -202,6 +201,16 @@ void FactsGenerator::VisitCXXConstructExpr(const CXXConstructExpr *CCE) {
return;
}
}
+ // Standard library callable wrappers (e.g., std::function) propagate the
+ // stored lambda's origins.
+ if (const auto *RD = CCE->getType()->getAsCXXRecordDecl();
+ RD && isStdCallableWrapperType(RD) && CCE->getNumArgs() == 1) {
+ const Expr *Arg = CCE->getArg(0);
+ if (OriginList *ArgList = getRValueOrigins(Arg, getOriginsList(*Arg))) {
+ flow(getOriginsList(*CCE), ArgList, /*Kill=*/true);
+ return;
+ }
+ }
handleFunctionCall(CCE, CCE->getConstructor(),
{CCE->getArgs(), CCE->getNumArgs()},
/*IsGslConstruction=*/false);
@@ -400,6 +409,15 @@ void FactsGenerator::handleAssignment(const Expr *LHSExpr,
} else
markUseAsWrite(DRE_LHS);
}
+ if (!RHSList) {
+ // RHS has no tracked origins (e.g., assigning a callable without origins
+ // to std::function). Clear loans of the destination.
+ for (OriginList *LHSInner = LHSList->peelOuterOrigin(); LHSInner;
+ LHSInner = LHSInner->peelOuterOrigin())
+ CurrentBlockFacts.push_back(
+ FactMgr.createFact<KillOriginFact>(LHSInner->getOuterOriginID()));
+ return;
+ }
// Kill the old loans of the destination origin and flow the new loans
// from the source origin.
flow(LHSList->peelOuterOrigin(), RHSList, /*Kill=*/true);
@@ -493,6 +511,13 @@ void FactsGenerator::VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *OCE) {
handleAssignment(OCE->getArg(0), OCE->getArg(1));
return;
}
+ // Standard library callable wrappers (e.g., std::function) can propagate
+ // the stored lambda's origins.
+ if (const auto *RD = LHSTy->getAsCXXRecordDecl();
+ RD && isStdCallableWrapperType(RD)) {
+ handleAssignment(OCE->getArg(0), OCE->getArg(1));
+ return;
+ }
// Other tracked types: only defaulted operator= propagates origins.
// User-defined operator= has opaque semantics, so don't handle them now.
if (const auto *MD =
diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
index 4852f444a51b3..27d95821dd0b4 100644
--- a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
@@ -382,4 +382,12 @@ bool isContainerInvalidationMethod(const CXXMethodDecl &MD) {
return InvalidatingMethods->contains(MD.getName());
}
+
+bool isStdCallableWrapperType(const CXXRecordDecl *RD) {
+ if (!RD || !isInStlNamespace(RD))
+ return false;
+ StringRef Name = getName(*RD);
+ return Name == "function" || Name == "move_only_function";
+}
+
} // namespace clang::lifetimes
diff --git a/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp b/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp
index bc7494360624e..cfbcacf04b1b0 100644
--- a/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp
@@ -166,6 +166,10 @@ class AnalysisImpl
return Lattice(Factory.remove(In.LiveOrigins, OF.getDestOriginID()));
}
+ Lattice transfer(Lattice In, const KillOriginFact &F) {
+ return Lattice(Factory.remove(In.LiveOrigins, F.getKilledOrigin()));
+ }
+
Lattice transfer(Lattice In, const ExpireFact &F) {
if (auto OID = F.getOriginID())
return Lattice(Factory.remove(In.LiveOrigins, *OID));
diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
index e437fb7d41268..adbc0458516e1 100644
--- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
@@ -63,6 +63,9 @@ static llvm::BitVector computePersistentOrigins(const FactManager &FactMgr,
Cur = Cur->peelOuterOrigin())
CheckOrigin(Cur->getOuterOriginID());
break;
+ case Fact::Kind::KillOrigin:
+ CheckOrigin(F->getAs<KillOriginFact>()->getKilledOrigin());
+ break;
case Fact::Kind::MovedOrigin:
case Fact::Kind::OriginEscapes:
case Fact::Kind::Expire:
@@ -181,6 +184,10 @@ class AnalysisImpl
return setLoans(In, DestOID, MergedLoans);
}
+ Lattice transfer(Lattice In, const KillOriginFact &F) {
+ return setLoans(In, F.getKilledOrigin(), LoanSetFactory.getEmptySet());
+ }
+
Lattice transfer(Lattice In, const ExpireFact &F) {
if (auto OID = F.getOriginID())
return setLoans(In, *OID, LoanSetFactory.getEmptySet());
diff --git a/clang/lib/Analysis/LifetimeSafety/Origins.cpp b/clang/lib/Analysis/LifetimeSafety/Origins.cpp
index fdd2671dee2e0..033cbdd75352c 100644
--- a/clang/lib/Analysis/LifetimeSafety/Origins.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Origins.cpp
@@ -107,6 +107,10 @@ bool OriginManager::hasOrigins(QualType QT) const {
const auto *RD = QT->getAsCXXRecordDecl();
if (!RD)
return false;
+ // Standard library callable wrappers (e.g., std::function) can propagate the
+ // stored lambda's origins.
+ if (isStdCallableWrapperType(RD))
+ return true;
// TODO: Limit to lambdas for now. This will be extended to user-defined
// structs with pointer-like fields.
if (!RD->isLambda())
diff --git a/clang/test/Sema/Inputs/lifetime-analysis.h b/clang/test/Sema/Inputs/lifetime-analysis.h
index d1e847d20cc50..2b904f88bc475 100644
--- a/clang/test/Sema/Inputs/lifetime-analysis.h
+++ b/clang/test/Sema/Inputs/lifetime-analysis.h
@@ -268,4 +268,18 @@ struct true_type {
template<class T> struct is_pointer : false_type {};
template<class T> struct is_pointer<T*> : true_type {};
template<class T> struct is_pointer<T* const> : true_type {};
+
+template<class> class function;
+template<class R, class... Args>
+class function<R(Args...)> {
+public:
+ template<class F> function(F) {}
+ function(const function&) {}
+ function(function&&) {}
+ template<class F> function& operator=(F) { return *this; }
+ function& operator=(const function&) { return *this; }
+ function& operator=(function&&) { return *this; }
+ ~function();
+};
+
}
diff --git a/clang/test/Sema/warn-lifetime-safety-dangling-field.cpp b/clang/test/Sema/warn-lifetime-safety-dangling-field.cpp
index 79b0183ed91ec..2afcd4a3dd97b 100644
--- a/clang/test/Sema/warn-lifetime-safety-dangling-field.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-dangling-field.cpp
@@ -196,3 +196,16 @@ struct DerivedWithCtor : BaseWithPointer {
}
};
} // namespace DanglingPointerFieldInBaseClass
+
+namespace callable_wrappers {
+
+struct HasCallback {
+ std::function<void()> callback; // expected-note {{this field dangles}}
+
+ void set_callback() {
+ int local;
+ callback = [&local]() { (void)local; }; // expected-warning {{address of stack memory escapes to a field}}
+ }
+};
+
+} // namespace callable_wrappers
diff --git a/clang/test/Sema/warn-lifetime-safety-invalidations.cpp b/clang/test/Sema/warn-lifetime-safety-invalidations.cpp
index 5a77ac97d16b1..973e095fb68b4 100644
--- a/clang/test/Sema/warn-lifetime-safety-invalidations.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-invalidations.cpp
@@ -527,3 +527,15 @@ struct S {
}
};
} // namespace method_call_uses_field_invalidation
+
+namespace callable_wrappers {
+
+void function_captured_ref_invalidated() {
+ std::vector<int> v;
+ v.push_back(1);
+ std::function<void()> f = [&r = v[0]]() { (void)r; }; // expected-warning {{object whose reference is captured is later invalidated}}
+ v.push_back(2); // expected-note {{invalidated here}}
+ (void)f; // expected-note {{later used here}}
+}
+
+} // namespace callable_wrappers
diff --git a/clang/test/Sema/warn-lifetime-safety-noescape.cpp b/clang/test/Sema/warn-lifetime-safety-noescape.cpp
index f233ec546faa5..4bb57e6b9df95 100644
--- a/clang/test/Sema/warn-lifetime-safety-noescape.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-noescape.cpp
@@ -191,3 +191,11 @@ MyObj& return_ref_from_noescape_ptr(
int* return_spaced_brackets(int* p [ [clang::noescape] /*some comment*/ ]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes}}
return p; // expected-note {{returned here}}
}
+
+namespace callable_wrappers {
+
+std::function<void()> escape_noescape_via_function(int &x [[clang::noescape]]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes}}
+ return [&]() { (void)x; }; // expected-note {{returned here}}
+}
+
+} // namespace callable_wrappers
diff --git a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
index b3b13038dc344..d2cf1c175eb57 100644
--- a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
@@ -1,8 +1,8 @@
// RUN: rm -rf %t
// RUN: split-file %s %t
-// RUN: %clang_cc1 -fsyntax-only -flifetime-safety-inference -fexperimental-lifetime-safety-tu-analysis -Wlifetime-safety-suggestions -Wlifetime-safety -Wno-dangling -I%t -verify %t/test_source.cpp
-// RUN: %clang_cc1 -flifetime-safety-inference -fexperimental-lifetime-safety-tu-analysis -Wlifetime-safety-suggestions -Wlifetime-safety -Wno-dangling -I%t -fixit %t/test_source.cpp
-// RUN: %clang_cc1 -fsyntax-only -flifetime-safety-inference -fexperimental-lifetime-safety-tu-analysis -Wlifetime-safety-suggestions -Wno-dangling -I%t -Werror=lifetime-safety-suggestions %t/test_source.cpp
+// RUN: %clang_cc1 -fsyntax-only -flifetime-safety-inference -fexperimental-lifetime-safety-tu-analysis -Wlifetime-safety-suggestions -Wlifetime-safety -Wno-dangling -I%t -I%S -verify %t/test_source.cpp
+// RUN: %clang_cc1 -flifetime-safety-inference -fexperimental-lifetime-safety-tu-analysis -Wlifetime-safety-suggestions -Wlifetime-safety -Wno-dangling -I%t -I%S -fixit %t/test_source.cpp
+// RUN: %clang_cc1 -fsyntax-only -flifetime-safety-inference -fexperimental-lifetime-safety-tu-analysis -Wlifetime-safety-suggestions -Wno-dangling -I%t -I%S -Werror=lifetime-safety-suggestions %t/test_source.cpp
View definition_before_header(View a);
@@ -67,6 +67,7 @@ struct ReturnThisPointer {
//--- test_source.cpp
#include "test_header.h"
+#include "Inputs/lifetime-analysis.h"
View definition_before_header(View a) {
return a; // expected-note {{param returned here}}
@@ -529,3 +530,20 @@ CaptureRefToBaseView test_ref_to_base_view() {
return x; // expected-note {{returned here}}
}
} // namespace capturing_constructor
+
+namespace callable_wrappers {
+
+std::function<void()> return_lambda_capturing_param(int &x) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}
+ return [&]() { (void)x; }; // expected-note {{param returned here}}
+}
+
+void uaf_via_inferred_lifetimebound() {
+ std::function<void()> f = []() {};
+ {
+ int local;
+ f = return_lambda_capturing_param(local); // expected-warning {{object whose reference is captured does not live long enough}}
+ } // expected-note {{destroyed here}}
+ (void)f; // expected-note {{later used here}}
+}
+
+} // namespace callable_wrappers
diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp
index f87b5cbdd0230..c083c30f5855d 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -2619,3 +2619,110 @@ struct Y : X {
}
};
} // namespace base_class_fields
+
+namespace callable_wrappers {
+
+std::function<void()> direct_return() {
+ int x;
+ return [&x]() { (void)x; }; // expected-warning {{address of stack memory is returned later}} \
+ // expected-note {{returned here}}
+}
+
+std::function<void()> copy_function() {
+ int x;
+ std::function<void()> f = [&x]() { (void)x; }; // expected-warning {{address of stack memory is returned later}}
+ std::function<void()> f2 = f;
+ return f2; // expected-note {{returned here}}
+}
+
+std::function<void()> copy_assign() {
+ int x;
+ std::function<void()> f = [&x]() { (void)x; }; // expected-warning {{address of stack memory is returned later}}
+ std::function<void()> f2 = []() {};
+ f2 = f;
+ return f2; // expected-note {{returned here}}
+}
+
+// FIXME: False negative. std::move's lifetimebound handling in
+// `handleFunctionCall` only flows the outermost origin, missing inner origins
+// that carry the lambda's loans.
+std::function<void()> move_assign() {
+ int x;
+ std::function<void()> f = [&x]() { (void)x; }; // Should warn.
+ std::function<void()> f2 = []() {};
+ f2 = std::move(f);
+ return f2;
+}
+
+std::function<void()> reassign_safe_then_unsafe() {
+ static int safe = 1;
+ int local = 2;
+ std::function<void()> f = []() { (void)safe; };
+ f = [&local]() { (void)local; }; // expected-warning {{address of stack memory is returned later}}
+ return f; // expected-note {{returned here}}
+}
+
+std::function<void()> reassign_unsafe_then_safe() {
+ static int safe = 1;
+ int local = 2;
+ std::function<void()> f = [&local]() { (void)local; };
+ f = []() { (void)safe; };
+ return f;
+}
+
+std::function<void()> non_capturing_lambda() {
+ return []() {};
+}
+
+void free_function();
+
+std::function<void()> reassign_lambda_to_function_pointer() {
+ int local;
+ std::function<void()> f = [&local]() { (void)local; };
+ f = &free_function;
+ return f;
+}
+
+struct Functor { void operator()() const; };
+
+std::function<void()> reassign_lambda_to_functor() {
+ int local;
+ Functor c;
+ std::function<void()> f = [&local]() { (void)local; };
+ f = c;
+ return f;
+}
+
+std::function<void()> capture_lifetimebound_param(int &x [[clang::lifetimebound]]) {
+ return [&]() { (void)x; };
+}
+
+void uaf_via_lifetimebound() {
+ std::function<void()> f = []() {};
+ {
+ int local;
+ f = capture_lifetimebound_param(local); // expected-warning {{object whose reference is captured does not live long enough}}
+ } // expected-note {{destroyed here}}
+ (void)f; // expected-note {{later used here}}
+}
+
+} // namespace callable_wrappers
+
+namespace GH126600 {
+struct [[gsl::Pointer]] function_ref {
+ template <typename Callable>
+ function_ref(Callable &&callable [[clang::lifetimebound]]) : ref(callable) {}
+ void (*ref)();
+};
+
+// FIXME: The lifetimebound annotation tracks the outer callable object's
+// storage rather than what the callable captures. A mechanism like
+// lifetimebound(2) could enable tracking inner lifetimes, which would
+// avoid this warning for non-capturing lambdas.
+void assign_non_capturing_to_function_ref(function_ref &r) {
+ r = []() {}; // expected-warning {{object whose reference is captured does not live long enough}} \
+ // expected-note {{destroyed here}}
+ (void)r; // expected-note {{later used here}}
+}
+
+} // namespace GH126600
More information about the cfe-commits
mailing list