https://github.com/aeft updated https://github.com/llvm/llvm-project/pull/184725
>From 8f6bd5019fdc151da437f6c6c3be6c4c05e0719d Mon Sep 17 00:00:00 2001 From: Alex Wang <[email protected]> Date: Wed, 4 Mar 2026 17:50:08 -0800 Subject: [PATCH 1/3] [LifetimeSafety] Fix false negative for GSL Owner type with arrow operator --- .../LifetimeSafety/LifetimeAnnotations.cpp | 7 +++- clang/test/Sema/Inputs/lifetime-analysis.h | 2 + .../Sema/warn-lifetime-analysis-nocfg.cpp | 40 +++++++++++++++++++ clang/test/Sema/warn-lifetime-safety.cpp | 29 ++++++++++++++ 4 files changed, 76 insertions(+), 2 deletions(-) diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp index 0d3da898137a6..1be35cd698669 100644 --- a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp +++ b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp @@ -152,8 +152,11 @@ bool shouldTrackImplicitObjectArg(const CXXMethodDecl *Callee, return false; if (isPointerLikeType(Callee->getReturnType())) { - if (!Callee->getIdentifier()) - return false; + if (!Callee->getIdentifier()) { + return Callee->getParent()->hasAttr<OwnerAttr>() && + Callee->getOverloadedOperator() == + OverloadedOperatorKind::OO_Arrow; + } return IteratorMembers.contains(Callee->getName()) || InnerPointerGetters.contains(Callee->getName()) || ContainerFindFns.contains(Callee->getName()); diff --git a/clang/test/Sema/Inputs/lifetime-analysis.h b/clang/test/Sema/Inputs/lifetime-analysis.h index 1b07f4f13467f..85b5a5fe5e07f 100644 --- a/clang/test/Sema/Inputs/lifetime-analysis.h +++ b/clang/test/Sema/Inputs/lifetime-analysis.h @@ -188,6 +188,7 @@ struct unique_ptr { ~unique_ptr(); T* release(); T &operator*(); + T *operator->(); T *get() const; }; @@ -204,6 +205,7 @@ struct optional { template<typename U> optional(optional<U>&& __t); + T *operator->(); T &operator*() &; T &&operator*() &&; T &value() &; diff --git a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp index 3305e9e270d86..534f075a6a8b9 100644 --- a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp +++ b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp @@ -1287,3 +1287,43 @@ void test() { const auto ptrTSC = StringTemplateSpecC<char>().data(); // Both have attribute // expected-warning {{temporary whose address is used}} } } // namespace GH175391 + +namespace owner_arrow { +struct TypeParamType { + std::string_view name() [[clang::lifetimebound]]; +}; + +void test_optional_arrow_lifetimebound() { + std::string_view a; + a = std::optional<TypeParamType>()->name(); // expected-warning {{object backing the pointer 'a' 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(a); // cfg-note {{later used here}} +} + +void test_optional_arrow_data() { + const char* p = std::optional<std::string>()->data(); // 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(p); // cfg-note {{later used here}} +} + +void test_optional_arrow_non_temporary() { + std::optional<std::string> opt; + const char* p = opt->data(); + use(p); +} + +void test_unique_ptr_arrow_data() { + const char* p = std::unique_ptr<std::string>()->data(); // 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(p); // cfg-note {{later used here}} +} + +void test_unique_ptr_arrow_non_temporary() { + std::unique_ptr<std::string> up; + const char* p = up->data(); + use(p); +} +} // namespace owner_arrow diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp index 097f3279d8e54..45cf26528d689 100644 --- a/clang/test/Sema/warn-lifetime-safety.cpp +++ b/clang/test/Sema/warn-lifetime-safety.cpp @@ -1746,3 +1746,32 @@ View test3(std::string a) { return b; // expected-note {{returned here}} } } // namespace non_trivial_views + +namespace OwnerArrowOperator { +void test_optional_arrow() { + const char* p; + { + std::optional<std::string> opt; + p = opt->data(); // expected-warning {{object whose reference is captured does not live long enough}} + } // expected-note {{destroyed here}} + (void)*p; // expected-note {{later used here}} +} + +void test_optional_arrow_lifetimebound() { + View v; + { + std::optional<MyObj> opt; + v = opt->getView(); // expected-warning {{object whose reference is captured does not live long enough}} + } // expected-note {{destroyed here}} + v.use(); // expected-note {{later used here}} +} + +void test_unique_ptr_arrow() { + const char* p; + { + std::unique_ptr<std::string> up; + p = up->data(); // expected-warning {{object whose reference is captured does not live long enough}} + } // expected-note {{destroyed here}} + (void)*p; // expected-note {{later used here}} +} +} // namespace OwnerArrowOperator >From 8951733d6f23cdc206c6a6f677f21ef305b21738 Mon Sep 17 00:00:00 2001 From: Alex Wang <[email protected]> Date: Thu, 5 Mar 2026 10:29:57 -0800 Subject: [PATCH 2/3] fix false positive for sema analysis --- .../LifetimeSafety/LifetimeAnnotations.cpp | 14 +++++++++++--- clang/test/Sema/warn-lifetime-analysis-nocfg.cpp | 16 ++++++++++++++++ clang/test/Sema/warn-lifetime-safety.cpp | 9 +++++++++ 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp index 1be35cd698669..2eb22f768cad9 100644 --- a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp +++ b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp @@ -153,9 +153,17 @@ bool shouldTrackImplicitObjectArg(const CXXMethodDecl *Callee, if (isPointerLikeType(Callee->getReturnType())) { if (!Callee->getIdentifier()) { - return Callee->getParent()->hasAttr<OwnerAttr>() && - Callee->getOverloadedOperator() == - OverloadedOperatorKind::OO_Arrow; + // e.g., std::optional<T>::operator->() returns T*. + if (Callee->getParent()->hasAttr<OwnerAttr>() && + Callee->getOverloadedOperator() == OverloadedOperatorKind::OO_Arrow) { + if (RunningUnderLifetimeSafety) + return true; + // For Sema analysis, don't track operator-> when the pointee is a GSL + // Pointer (e.g., optional<string_view>), as Sema can't distinguish the + // Pointer object's lifetime from the data it observes. + return !isGslPointerType(Callee->getReturnType()->getPointeeType()); + } + return false; } return IteratorMembers.contains(Callee->getName()) || InnerPointerGetters.contains(Callee->getName()) || diff --git a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp index 534f075a6a8b9..315c7ce0964d3 100644 --- a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp +++ b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp @@ -1326,4 +1326,20 @@ void test_unique_ptr_arrow_non_temporary() { const char* p = up->data(); use(p); } + +void test_optional_view_arrow_data() { + const char* p; + p = std::optional<std::string_view>()->data(); + (void)*p; +} + +void test_optional_view_arrow() { + // FIXME: Sema analysis doesn't warn here because we suppress operator-> + // tracking when the pointee is a GSL Pointer to avoid false positives (see + // test_optional_view_arrow_data). The CFG-based lifetime safety analysis + // handles this correctly. + std::string_view* p = std::optional<std::string_view>().operator->(); // cfg-warning {{object whose reference is captured does not live long enough}} \ + // cfg-note {{destroyed here}} + (void)*p; // cfg-note {{later used here}} +} } // namespace owner_arrow diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp index 45cf26528d689..a75c70aa3674a 100644 --- a/clang/test/Sema/warn-lifetime-safety.cpp +++ b/clang/test/Sema/warn-lifetime-safety.cpp @@ -1774,4 +1774,13 @@ void test_unique_ptr_arrow() { } // expected-note {{destroyed here}} (void)*p; // expected-note {{later used here}} } + +void test_optional_view_arrow() { + const char* p; + { + std::optional<std::string_view> opt; + p = opt->data(); + } + (void)*p; +} } // namespace OwnerArrowOperator >From bb4bbcdcd9d32f91c0afc6be7477736920e3a7be Mon Sep 17 00:00:00 2001 From: Alex Wang <[email protected]> Date: Thu, 5 Mar 2026 12:04:07 -0800 Subject: [PATCH 3/3] remove handling for sema analysis --- .../LifetimeSafety/LifetimeAnnotations.cpp | 15 ++--- .../Sema/warn-lifetime-analysis-nocfg.cpp | 58 +------------------ 2 files changed, 6 insertions(+), 67 deletions(-) diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp index 2eb22f768cad9..6cbb03720ed06 100644 --- a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp +++ b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp @@ -154,16 +154,11 @@ bool shouldTrackImplicitObjectArg(const CXXMethodDecl *Callee, if (isPointerLikeType(Callee->getReturnType())) { if (!Callee->getIdentifier()) { // e.g., std::optional<T>::operator->() returns T*. - if (Callee->getParent()->hasAttr<OwnerAttr>() && - Callee->getOverloadedOperator() == OverloadedOperatorKind::OO_Arrow) { - if (RunningUnderLifetimeSafety) - return true; - // For Sema analysis, don't track operator-> when the pointee is a GSL - // Pointer (e.g., optional<string_view>), as Sema can't distinguish the - // Pointer object's lifetime from the data it observes. - return !isGslPointerType(Callee->getReturnType()->getPointeeType()); - } - return false; + return RunningUnderLifetimeSafety + ? Callee->getParent()->hasAttr<OwnerAttr>() && + Callee->getOverloadedOperator() == + OverloadedOperatorKind::OO_Arrow + : false; } return IteratorMembers.contains(Callee->getName()) || InnerPointerGetters.contains(Callee->getName()) || diff --git a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp index 315c7ce0964d3..65d323b27c977 100644 --- a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp +++ b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp @@ -1286,60 +1286,4 @@ void test() { const auto ptrTSB = StringTemplateSpecB<char>().data(); // Definition-only attribute // expected-warning {{temporary whose address is used}} const auto ptrTSC = StringTemplateSpecC<char>().data(); // Both have attribute // expected-warning {{temporary whose address is used}} } -} // namespace GH175391 - -namespace owner_arrow { -struct TypeParamType { - std::string_view name() [[clang::lifetimebound]]; -}; - -void test_optional_arrow_lifetimebound() { - std::string_view a; - a = std::optional<TypeParamType>()->name(); // expected-warning {{object backing the pointer 'a' 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(a); // cfg-note {{later used here}} -} - -void test_optional_arrow_data() { - const char* p = std::optional<std::string>()->data(); // 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(p); // cfg-note {{later used here}} -} - -void test_optional_arrow_non_temporary() { - std::optional<std::string> opt; - const char* p = opt->data(); - use(p); -} - -void test_unique_ptr_arrow_data() { - const char* p = std::unique_ptr<std::string>()->data(); // 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(p); // cfg-note {{later used here}} -} - -void test_unique_ptr_arrow_non_temporary() { - std::unique_ptr<std::string> up; - const char* p = up->data(); - use(p); -} - -void test_optional_view_arrow_data() { - const char* p; - p = std::optional<std::string_view>()->data(); - (void)*p; -} - -void test_optional_view_arrow() { - // FIXME: Sema analysis doesn't warn here because we suppress operator-> - // tracking when the pointee is a GSL Pointer to avoid false positives (see - // test_optional_view_arrow_data). The CFG-based lifetime safety analysis - // handles this correctly. - std::string_view* p = std::optional<std::string_view>().operator->(); // cfg-warning {{object whose reference is captured does not live long enough}} \ - // cfg-note {{destroyed here}} - (void)*p; // cfg-note {{later used here}} -} -} // namespace owner_arrow +} // namespace GH175391 \ No newline at end of file _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
