Author: Utkarsh Saxena Date: 2026-01-19T14:55:47+01:00 New Revision: 777c7b85ff218b043f67d5bedebb2338f9c1c149
URL: https://github.com/llvm/llvm-project/commit/777c7b85ff218b043f67d5bedebb2338f9c1c149 DIFF: https://github.com/llvm/llvm-project/commit/777c7b85ff218b043f67d5bedebb2338f9c1c149.diff LOG: [LifetimeSafety] Fix handling of reference-type DeclRefExpr (#176728) Fix handling of reference-typed DeclRefExpr in lifetime analysis Fixes https://github.com/llvm/llvm-project/issues/176399 This PR fixes a bug in the lifetime analysis where reference-typed DeclRefExpr nodes were incorrectly handled. The analysis was incorrectly removing the outer layer of origin for reference types, which led to missing some dangling reference warnings. The fix adds a check to only remove the outer layer of origin when the declaration is not a reference type. Added: Modified: clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp clang/test/Sema/Inputs/lifetime-analysis.h clang/test/Sema/warn-lifetime-analysis-nocfg.cpp clang/test/Sema/warn-lifetime-safety.cpp Removed: ################################################################################ diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp index 54e19b58bd7d5..5d1afd15c795d 100644 --- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp +++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp @@ -589,9 +589,10 @@ void FactsGenerator::handleUse(const DeclRefExpr *DRE) { OriginList *List = getOriginsList(*DRE); if (!List) return; - // Remove the outer layer of origin which borrows from the decl directly. This - // is a use of the underlying decl. - List = getRValueOrigins(DRE, List); + // Remove the outer layer of origin which borrows from the decl directly + // (e.g., when this is not a reference). This is a use of the underlying decl. + if (!DRE->getDecl()->getType()->isReferenceType()) + List = getRValueOrigins(DRE, List); // Skip if there is no inner origin (e.g., when it is not a pointer type). if (!List) return; diff --git a/clang/test/Sema/Inputs/lifetime-analysis.h b/clang/test/Sema/Inputs/lifetime-analysis.h index 918547dfcec51..b47fe78bdf0fb 100644 --- a/clang/test/Sema/Inputs/lifetime-analysis.h +++ b/clang/test/Sema/Inputs/lifetime-analysis.h @@ -49,7 +49,8 @@ struct vector { template<typename InputIterator> vector(InputIterator first, InputIterator __last); - T &at(int n); + T & at(int n) &; + T && at(int n) &&; void push_back(const T&); void push_back(T&&); diff --git a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp index 441f9fc602916..77fb39ebb10f3 100644 --- a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp +++ b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp @@ -280,13 +280,21 @@ std::string_view danglingRefToOptionalFromTemp4() { } void danglingReferenceFromTempOwner() { + int &&r = *std::optional<int>(); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \ + // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} // FIXME: Detect this using the CFG-based lifetime analysis. // https://github.com/llvm/llvm-project/issues/175893 - int &&r = *std::optional<int>(); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} int &&r2 = *std::optional<int>(5); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} + + // FIXME: Detect this using the CFG-based lifetime analysis. + // https://github.com/llvm/llvm-project/issues/175893 int &&r3 = std::optional<int>(5).value(); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} - int &r4 = std::vector<int>().at(3); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} - use(r, r2, r3, r4); + + const int &r4 = std::vector<int>().at(3); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \ + // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} + int &&r5 = std::vector<int>().at(3); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \ + // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} + use(r, r2, r3, r4, r5); // cfg-note 3 {{later used here}} std::string_view sv = *getTempOptStr(); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \ // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} @@ -299,8 +307,8 @@ std::optional<std::vector<int>> getTempOptVec(); void testLoops() { for (auto i : getTempVec()) // ok ; - // FIXME: Detect this using the CFG-based lifetime analysis. - for (auto i : *getTempOptVec()) // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} + for (auto i : *getTempOptVec()) // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \ + // cfg-warning {{object whose reference is captured does not live long enough}} cfg-note {{destroyed here}} cfg-note {{later used here}} ; } diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp index 24ac72e7aad4d..13666b0402522 100644 --- a/clang/test/Sema/warn-lifetime-safety.cpp +++ b/clang/test/Sema/warn-lifetime-safety.cpp @@ -1453,3 +1453,42 @@ void bar() { (void)x; // expected-note {{used here}} } } + +namespace reference_type_decl_ref_expr { +struct S { + S(); + ~S(); + const std::string& x() const [[clang::lifetimebound]]; +}; + +const std::string& identity(const std::string& in [[clang::lifetimebound]]); +const S& identity(const S& in [[clang::lifetimebound]]); + +void test_temporary() { + const std::string& x = S().x(); // expected-warning {{object whose reference is captured does not live long enough}} expected-note {{destroyed here}} + (void)x; // expected-note {{later used here}} + + const std::string& y = identity(S().x()); // expected-warning {{object whose reference is captured does not live long enough}} expected-note {{destroyed here}} + (void)y; // expected-note {{later used here}} + + std::string_view z; + { + S s; + const std::string& zz = s.x(); // expected-warning {{object whose reference is captured does not live long enough}} + z = zz; + } // expected-note {{destroyed here}} + (void)z; // expected-note {{later used here}} +} + +void test_lifetime_extension_ok() { + const S& x = S(); + (void)x; + const S& y = identity(S()); // expected-warning {{object whose reference is captured does not live long enough}} expected-note {{destroyed here}} + (void)y; // expected-note {{later used here}} +} + +const std::string& test_return() { + const std::string& x = S().x(); // expected-warning {{object whose reference is captured does not live long enough}} expected-note {{destroyed here}} + return x; // expected-note {{later used here}} +} +} // namespace reference_type_decl_ref_expr _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
