[clang] [LifetimeSafety] Add support for iterator arithmetic (PR #195442)
via cfe-commits
cfe-commits at lists.llvm.org
Sun May 3 13:34:51 PDT 2026
https://github.com/NeKon69 updated https://github.com/llvm/llvm-project/pull/195442
>From 4dde5a695d765d7a168ad6844eb7084fcd65cc30 Mon Sep 17 00:00:00 2001
From: NeKon69 <nobodqwe at gmail.com>
Date: Sat, 2 May 2026 13:23:37 +0300
Subject: [PATCH 1/6] add support for iterator pointer arithmetic
---
.../LifetimeSafety/LifetimeAnnotations.h | 3 +++
.../Analysis/LifetimeSafety/FactsGenerator.cpp | 17 +++++++++++++++++
.../LifetimeSafety/LifetimeAnnotations.cpp | 18 ++++++++++++++++++
3 files changed, 38 insertions(+)
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
index 54d52fee6bea7..fdf6ccd211929 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
@@ -71,6 +71,9 @@ bool isGslOwnerType(QualType QT);
// when ownership is manually transferred.
bool isUniquePtrRelease(const CXXMethodDecl &MD);
+bool isIteratorType(const CXXRecordDecl *RD);
+
+bool isPropogatingIteratorOP(OverloadedOperatorKind OP);
// Returns true if the given method invalidates references tracked by lifetime
// analysis (e.g. vector::push_back). Methods that only invalidate iterators but
// not references (e.g. unordered_map::emplace) are not considered invalidating
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index dc80d2783fc03..1818d227d4d29 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -542,6 +542,23 @@ void FactsGenerator::VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *OCE) {
}
}
+ if (OCE->getNumArgs() < 3 && isPropogatingIteratorOP(OCE->getOperator())) {
+ const Expr *IteratorArg = nullptr;
+ for (const Expr *Arg : OCE->arguments()) {
+ if (isIteratorType(Arg->getType()->getAsCXXRecordDecl())) {
+ IteratorArg = Arg;
+ break;
+ }
+ }
+
+ if (IteratorArg) {
+ flow(getOriginsList(*OCE),
+ getRValueOrigins(IteratorArg, getOriginsList(*IteratorArg)),
+ /*Kill=*/true);
+ return;
+ }
+ }
+
ArrayRef Args = {OCE->getArgs(), OCE->getNumArgs()};
// For `static operator()`, the first argument is the object argument,
// remove it from the argument list to avoid off-by-one errors.
diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
index b33fb8edc100b..4b1fb83a5c06b 100644
--- a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
@@ -283,6 +283,24 @@ bool isUniquePtrRelease(const CXXMethodDecl &MD) {
MD.getNumParams() == 0 && isStdUniquePtr(*MD.getParent());
}
+bool isIteratorType(const CXXRecordDecl *RD) {
+ // FIXME: Add more iterator names in the future
+ static const llvm::StringSet<> Iterators = {
+ // Usually not an alias
+ "reverse_iterator",
+ // Alias for continuos iterators in gcc
+ "__normal_iterator",
+ // Alias for continuos iterators in clang
+ "__wrap_iter"};
+ return RD && isInStlNamespace(RD) && Iterators.contains(getName(*RD));
+}
+
+bool isPropogatingIteratorOP(OverloadedOperatorKind OP) {
+ llvm::SmallDenseSet<OverloadedOperatorKind> PropagatingOperators = {
+ OO_Plus, OO_Minus, OO_PlusPlus, OO_MinusMinus};
+ return PropagatingOperators.contains(OP);
+}
+
bool isInvalidationMethod(const CXXMethodDecl &MD) {
const CXXRecordDecl *RD = MD.getParent();
if (!isInStlNamespace(RD))
>From 7f0e7a0a4711b7fcb762dc35f4e9a7f7d0497ada Mon Sep 17 00:00:00 2001
From: NeKon69 <nobodqwe at gmail.com>
Date: Sat, 2 May 2026 13:56:34 +0300
Subject: [PATCH 2/6] add tests
---
clang/test/Sema/Inputs/lifetime-analysis.h | 55 ++++++++++++++++++-
.../warn-lifetime-safety-invalidations.cpp | 30 ++++++++++
2 files changed, 84 insertions(+), 1 deletion(-)
diff --git a/clang/test/Sema/Inputs/lifetime-analysis.h b/clang/test/Sema/Inputs/lifetime-analysis.h
index eaedba372ee5e..5f3f1a236a282 100644
--- a/clang/test/Sema/Inputs/lifetime-analysis.h
+++ b/clang/test/Sema/Inputs/lifetime-analysis.h
@@ -11,6 +11,27 @@ template<typename T>
bool operator==(basic_iterator<T>, basic_iterator<T>);
template<typename T>
bool operator!=(basic_iterator<T>, basic_iterator<T>);
+
+// These iterator spellings match libstdc++ names documented at:
+// https://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-4.2/namespace____gnu__cxx.html
+template <typename T>
+struct __normal_iterator {
+ __normal_iterator operator++();
+ __normal_iterator operator--();
+ __normal_iterator operator+(int) const;
+ __normal_iterator operator-(int) const;
+ T& operator*() const;
+ T* operator->() const;
+};
+
+template<typename T>
+bool operator==(__normal_iterator<T>, __normal_iterator<T>);
+template<typename T>
+bool operator!=(__normal_iterator<T>, __normal_iterator<T>);
+template<typename T>
+__normal_iterator<T> operator+(int, __normal_iterator<T>);
+template<typename T>
+__normal_iterator<T> operator-(int, __normal_iterator<T>);
}
namespace std {
@@ -47,11 +68,43 @@ struct initializer_list {
const T* ptr; size_t sz;
};
template<typename T> class allocator {};
+
+template <typename T>
+struct __wrap_iter {
+ __wrap_iter operator++();
+ __wrap_iter operator--();
+ __wrap_iter operator+(int) const;
+ __wrap_iter operator-(int) const;
+ T& operator*() const;
+ T* operator->() const;
+};
+
+template<typename T>
+bool operator==(__wrap_iter<T>, __wrap_iter<T>);
+template<typename T>
+bool operator!=(__wrap_iter<T>, __wrap_iter<T>);
+template<typename T>
+__wrap_iter<T> operator+(int, __wrap_iter<T>);
+template<typename T>
+__wrap_iter<T> operator-(int, __wrap_iter<T>);
+
+template <typename Iterator>
+struct reverse_iterator {
+ reverse_iterator operator++();
+ reverse_iterator operator--();
+ reverse_iterator operator+(int) const;
+ reverse_iterator operator-(int) const;
+ decltype(*Iterator()) operator*() const;
+};
+
template <typename T, typename Alloc = allocator<T>>
struct vector {
- typedef __gnu_cxx::basic_iterator<T> iterator;
+ using iterator = __wrap_iter<T>;
+ using reverse_iterator = reverse_iterator<iterator>;
iterator begin();
iterator end();
+ reverse_iterator rbegin();
+ reverse_iterator rend();
const T *data() const;
vector();
~vector();
diff --git a/clang/test/Sema/warn-lifetime-safety-invalidations.cpp b/clang/test/Sema/warn-lifetime-safety-invalidations.cpp
index f1044e2ad1cdd..9485a2cf273a3 100644
--- a/clang/test/Sema/warn-lifetime-safety-invalidations.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-invalidations.cpp
@@ -252,6 +252,36 @@ void IteratorUsedAfterPushBack(std::vector<int> v) {
}
++it; // expected-note {{later used here}}
}
+
+void IteratorUsedAfterPreIncrement() {
+ std::vector<int> v;
+ auto it = std::begin(v); // expected-warning {{object whose reference is captured is later invalidated}}
+ auto next = ++it;
+ v.push_back(4); // expected-note {{invalidated here}}
+ *next; // expected-note {{later used here}}
+}
+
+void IteratorUsedAfterPreDecrement(std::vector<int> v) {
+ auto it = std::end(v); // expected-warning {{object whose reference is captured is later invalidated}}
+ auto prev = --it;
+ v.resize(8); // expected-note {{invalidated here}}
+ *prev; // expected-note {{later used here}}
+}
+
+void IteratorUsedAfterAddition() {
+ std::vector<int> v;
+ auto it = std::begin(v); // expected-warning {{object whose reference is captured is later invalidated}}
+ auto next = it + 5;
+ v.insert(it, 0); // expected-note {{invalidated here}}
+ *next; // expected-note {{later used here}}
+}
+
+void IteratorUsedAfterReverseSubtraction(std::vector<int> v) {
+ auto it = std::end(v); // expected-warning {{object whose reference is captured is later invalidated}}
+ auto prev = 5 - it;
+ v.clear(); // expected-note {{invalidated here}}
+ *prev; // expected-note {{later used here}}
+}
} // namespace SimpleInvalidIterators
namespace ElementReferences {
>From 2c847bcd87a2b58eb0061e600519a1c63542a8ee Mon Sep 17 00:00:00 2001
From: NeKon69 <nobodqwe at gmail.com>
Date: Sat, 2 May 2026 14:07:34 +0300
Subject: [PATCH 3/6] cleanup
---
.../Analyses/LifetimeSafety/LifetimeAnnotations.h | 2 +-
clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp | 2 +-
.../Analysis/LifetimeSafety/LifetimeAnnotations.cpp | 11 ++++++-----
3 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
index fdf6ccd211929..2f814216a4285 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
@@ -73,7 +73,7 @@ bool isUniquePtrRelease(const CXXMethodDecl &MD);
bool isIteratorType(const CXXRecordDecl *RD);
-bool isPropogatingIteratorOP(OverloadedOperatorKind OP);
+bool isPropagatingIteratorOp(OverloadedOperatorKind OP);
// Returns true if the given method invalidates references tracked by lifetime
// analysis (e.g. vector::push_back). Methods that only invalidate iterators but
// not references (e.g. unordered_map::emplace) are not considered invalidating
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index 1818d227d4d29..f32e6b57c10e5 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -542,7 +542,7 @@ void FactsGenerator::VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *OCE) {
}
}
- if (OCE->getNumArgs() < 3 && isPropogatingIteratorOP(OCE->getOperator())) {
+ if (OCE->getNumArgs() < 3 && isPropagatingIteratorOp(OCE->getOperator())) {
const Expr *IteratorArg = nullptr;
for (const Expr *Arg : OCE->arguments()) {
if (isIteratorType(Arg->getType()->getAsCXXRecordDecl())) {
diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
index 4b1fb83a5c06b..15ee38ebd2b09 100644
--- a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
@@ -284,18 +284,19 @@ bool isUniquePtrRelease(const CXXMethodDecl &MD) {
}
bool isIteratorType(const CXXRecordDecl *RD) {
- // FIXME: Add more iterator names in the future
+ // FIXME: Extend this to cover more iterator wrapper types used by standard
+ // library implementations.
static const llvm::StringSet<> Iterators = {
- // Usually not an alias
+ // Standard reverse iterator wrapper.
"reverse_iterator",
- // Alias for continuos iterators in gcc
+ // libstdc++ contiguous iterator wrapper.
"__normal_iterator",
- // Alias for continuos iterators in clang
+ // libc++ contiguous iterator wrapper.
"__wrap_iter"};
return RD && isInStlNamespace(RD) && Iterators.contains(getName(*RD));
}
-bool isPropogatingIteratorOP(OverloadedOperatorKind OP) {
+bool isPropagatingIteratorOp(OverloadedOperatorKind OP) {
llvm::SmallDenseSet<OverloadedOperatorKind> PropagatingOperators = {
OO_Plus, OO_Minus, OO_PlusPlus, OO_MinusMinus};
return PropagatingOperators.contains(OP);
>From 4eda2377f023951566b1a5f4a1a6ced55e6bce39 Mon Sep 17 00:00:00 2001
From: NeKon69 <nobodqwe at gmail.com>
Date: Sat, 2 May 2026 14:13:04 +0300
Subject: [PATCH 4/6] cleanup tests
---
clang/test/Sema/Inputs/lifetime-analysis.h | 13 ++++++++++++-
.../warn-lifetime-safety-invalidations.cpp | 18 +++++++++---------
2 files changed, 21 insertions(+), 10 deletions(-)
diff --git a/clang/test/Sema/Inputs/lifetime-analysis.h b/clang/test/Sema/Inputs/lifetime-analysis.h
index 5f3f1a236a282..4c6f833de9e3b 100644
--- a/clang/test/Sema/Inputs/lifetime-analysis.h
+++ b/clang/test/Sema/Inputs/lifetime-analysis.h
@@ -97,14 +97,25 @@ struct reverse_iterator {
decltype(*Iterator()) operator*() const;
};
+template <typename Iterator>
+reverse_iterator<Iterator> operator+(int, reverse_iterator<Iterator>);
+template <typename Iterator>
+reverse_iterator<Iterator> operator-(int, reverse_iterator<Iterator>);
+
template <typename T, typename Alloc = allocator<T>>
struct vector {
using iterator = __wrap_iter<T>;
- using reverse_iterator = reverse_iterator<iterator>;
+ using const_iterator = __wrap_iter<const T>;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
iterator begin();
iterator end();
+ const_iterator cbegin() const;
+ const_iterator cend() const;
reverse_iterator rbegin();
reverse_iterator rend();
+ const_reverse_iterator crbegin() const;
+ const_reverse_iterator crend() const;
const T *data() const;
vector();
~vector();
diff --git a/clang/test/Sema/warn-lifetime-safety-invalidations.cpp b/clang/test/Sema/warn-lifetime-safety-invalidations.cpp
index 9485a2cf273a3..5c812ff318cee 100644
--- a/clang/test/Sema/warn-lifetime-safety-invalidations.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-invalidations.cpp
@@ -255,32 +255,32 @@ void IteratorUsedAfterPushBack(std::vector<int> v) {
void IteratorUsedAfterPreIncrement() {
std::vector<int> v;
- auto it = std::begin(v); // expected-warning {{object whose reference is captured is later invalidated}}
+ auto it = v.begin(); // expected-warning {{object whose reference is captured is later invalidated}}
auto next = ++it;
v.push_back(4); // expected-note {{invalidated here}}
- *next; // expected-note {{later used here}}
+ (void)*next; // expected-note {{later used here}}
}
void IteratorUsedAfterPreDecrement(std::vector<int> v) {
- auto it = std::end(v); // expected-warning {{object whose reference is captured is later invalidated}}
+ auto it = v.rbegin(); // expected-warning {{object whose reference is captured is later invalidated}}
auto prev = --it;
v.resize(8); // expected-note {{invalidated here}}
- *prev; // expected-note {{later used here}}
+ (void)*prev; // expected-note {{later used here}}
}
void IteratorUsedAfterAddition() {
std::vector<int> v;
- auto it = std::begin(v); // expected-warning {{object whose reference is captured is later invalidated}}
+ auto it = v.cbegin(); // expected-warning {{object whose reference is captured is later invalidated}}
auto next = it + 5;
- v.insert(it, 0); // expected-note {{invalidated here}}
- *next; // expected-note {{later used here}}
+ v.insert(v.begin(), 0); // expected-note {{invalidated here}}
+ (void)*next; // expected-note {{later used here}}
}
void IteratorUsedAfterReverseSubtraction(std::vector<int> v) {
- auto it = std::end(v); // expected-warning {{object whose reference is captured is later invalidated}}
+ auto it = v.crbegin(); // expected-warning {{object whose reference is captured is later invalidated}}
auto prev = 5 - it;
v.clear(); // expected-note {{invalidated here}}
- *prev; // expected-note {{later used here}}
+ (void)*prev; // expected-note {{later used here}}
}
} // namespace SimpleInvalidIterators
>From 5a0eff921fce9e6cfce02d4a64e38a18c1c9997a Mon Sep 17 00:00:00 2001
From: NeKon69 <nobodqwe at gmail.com>
Date: Sun, 3 May 2026 14:36:44 +0300
Subject: [PATCH 5/6] update tests
---
clang/test/Sema/Inputs/lifetime-analysis.h | 11 +++++++++++
.../Sema/warn-lifetime-safety-invalidations.cpp | 13 +++++++------
2 files changed, 18 insertions(+), 6 deletions(-)
diff --git a/clang/test/Sema/Inputs/lifetime-analysis.h b/clang/test/Sema/Inputs/lifetime-analysis.h
index 4c6f833de9e3b..2e7c7feab0b55 100644
--- a/clang/test/Sema/Inputs/lifetime-analysis.h
+++ b/clang/test/Sema/Inputs/lifetime-analysis.h
@@ -17,7 +17,9 @@ bool operator!=(basic_iterator<T>, basic_iterator<T>);
template <typename T>
struct __normal_iterator {
__normal_iterator operator++();
+ __normal_iterator operator++(int);
__normal_iterator operator--();
+ __normal_iterator operator--(int);
__normal_iterator operator+(int) const;
__normal_iterator operator-(int) const;
T& operator*() const;
@@ -72,7 +74,9 @@ template<typename T> class allocator {};
template <typename T>
struct __wrap_iter {
__wrap_iter operator++();
+ __wrap_iter operator++(int);
__wrap_iter operator--();
+ __wrap_iter operator--(int);
__wrap_iter operator+(int) const;
__wrap_iter operator-(int) const;
T& operator*() const;
@@ -91,7 +95,9 @@ __wrap_iter<T> operator-(int, __wrap_iter<T>);
template <typename Iterator>
struct reverse_iterator {
reverse_iterator operator++();
+ reverse_iterator operator++(int);
reverse_iterator operator--();
+ reverse_iterator operator--(int);
reverse_iterator operator+(int) const;
reverse_iterator operator-(int) const;
decltype(*Iterator()) operator*() const;
@@ -104,8 +110,13 @@ reverse_iterator<Iterator> operator-(int, reverse_iterator<Iterator>);
template <typename T, typename Alloc = allocator<T>>
struct vector {
+#ifdef USE_LIBSTDCPP_ITERATORS
+ using iterator = __gnu_cxx::__normal_iterator<T>;
+ using const_iterator = __gnu_cxx::__normal_iterator<const T>;
+#else
using iterator = __wrap_iter<T>;
using const_iterator = __wrap_iter<const T>;
+#endif
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
iterator begin();
diff --git a/clang/test/Sema/warn-lifetime-safety-invalidations.cpp b/clang/test/Sema/warn-lifetime-safety-invalidations.cpp
index 5c812ff318cee..9b613317374c0 100644
--- a/clang/test/Sema/warn-lifetime-safety-invalidations.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-invalidations.cpp
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -fsyntax-only -Wlifetime-safety -Wno-dangling -verify %s
+// RUN: %clang_cc1 -fsyntax-only -Wlifetime-safety -Wno-dangling -verify -DUSE_LIBSTDCPP_ITERATORS %s
#include "Inputs/lifetime-analysis.h"
@@ -257,14 +258,14 @@ void IteratorUsedAfterPreIncrement() {
std::vector<int> v;
auto it = v.begin(); // expected-warning {{object whose reference is captured is later invalidated}}
auto next = ++it;
- v.push_back(4); // expected-note {{invalidated here}}
+ v.push_back(1); // expected-note {{invalidated here}}
(void)*next; // expected-note {{later used here}}
}
-void IteratorUsedAfterPreDecrement(std::vector<int> v) {
+void IteratorUsedAfterPostDecrement(std::vector<int> v) {
auto it = v.rbegin(); // expected-warning {{object whose reference is captured is later invalidated}}
- auto prev = --it;
- v.resize(8); // expected-note {{invalidated here}}
+ auto prev = it--;
+ v.push_back(1); // expected-note {{invalidated here}}
(void)*prev; // expected-note {{later used here}}
}
@@ -272,14 +273,14 @@ void IteratorUsedAfterAddition() {
std::vector<int> v;
auto it = v.cbegin(); // expected-warning {{object whose reference is captured is later invalidated}}
auto next = it + 5;
- v.insert(v.begin(), 0); // expected-note {{invalidated here}}
+ v.push_back(1); // expected-note {{invalidated here}}
(void)*next; // expected-note {{later used here}}
}
void IteratorUsedAfterReverseSubtraction(std::vector<int> v) {
auto it = v.crbegin(); // expected-warning {{object whose reference is captured is later invalidated}}
auto prev = 5 - it;
- v.clear(); // expected-note {{invalidated here}}
+ v.push_back(1); // expected-note {{invalidated here}}
(void)*prev; // expected-note {{later used here}}
}
} // namespace SimpleInvalidIterators
>From 4c718b9a925345acbb1ce4da72cd3e11c2e7d154 Mon Sep 17 00:00:00 2001
From: NeKon69 <nobodqwe at gmail.com>
Date: Sun, 3 May 2026 23:34:30 +0300
Subject: [PATCH 6/6] switch to tracking all gsl::pointer types
---
.../LifetimeSafety/LifetimeAnnotations.h | 5 +-
.../LifetimeSafety/FactsGenerator.cpp | 19 +-----
.../LifetimeSafety/LifetimeAnnotations.cpp | 39 ++++++-------
clang/test/Sema/Inputs/lifetime-analysis.h | 58 +++----------------
4 files changed, 31 insertions(+), 90 deletions(-)
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
index 2f814216a4285..2e52c3a7f74a3 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
@@ -61,6 +61,8 @@ bool shouldTrackImplicitObjectArg(const CXXMethodDecl *Callee,
// std::any_cast.
bool shouldTrackFirstArgument(const FunctionDecl *FD);
+bool shouldTrackSecondArgument(const FunctionDecl *FD);
+
// Tells whether the type is annotated with [[gsl::Pointer]].
bool isGslPointerType(QualType QT);
// Tells whether the type is annotated with [[gsl::Owner]].
@@ -71,9 +73,6 @@ bool isGslOwnerType(QualType QT);
// when ownership is manually transferred.
bool isUniquePtrRelease(const CXXMethodDecl &MD);
-bool isIteratorType(const CXXRecordDecl *RD);
-
-bool isPropagatingIteratorOp(OverloadedOperatorKind OP);
// Returns true if the given method invalidates references tracked by lifetime
// analysis (e.g. vector::push_back). Methods that only invalidate iterators but
// not references (e.g. unordered_map::emplace) are not considered invalidating
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index f32e6b57c10e5..0a06548d881d1 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -542,23 +542,6 @@ void FactsGenerator::VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *OCE) {
}
}
- if (OCE->getNumArgs() < 3 && isPropagatingIteratorOp(OCE->getOperator())) {
- const Expr *IteratorArg = nullptr;
- for (const Expr *Arg : OCE->arguments()) {
- if (isIteratorType(Arg->getType()->getAsCXXRecordDecl())) {
- IteratorArg = Arg;
- break;
- }
- }
-
- if (IteratorArg) {
- flow(getOriginsList(*OCE),
- getRValueOrigins(IteratorArg, getOriginsList(*IteratorArg)),
- /*Kill=*/true);
- return;
- }
- }
-
ArrayRef Args = {OCE->getArgs(), OCE->getNumArgs()};
// For `static operator()`, the first argument is the object argument,
// remove it from the argument list to avoid off-by-one errors.
@@ -914,6 +897,8 @@ void FactsGenerator::handleFunctionCall(const Expr *Call,
PVD = Method->getParamDecl(I - 1);
} else if (I == 0 && shouldTrackFirstArgument(FD)) {
return true;
+ } else if (I == 1 && shouldTrackSecondArgument(FD)) {
+ return true;
} else if (I < FD->getNumParams()) {
// For free functions or static methods.
PVD = FD->getParamDecl(I);
diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
index 15ee38ebd2b09..b37ba403cd806 100644
--- a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
@@ -13,6 +13,7 @@
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/Type.h"
#include "clang/AST/TypeLoc.h"
+#include "clang/Basic/OperatorKinds.h"
#include "llvm/ADT/StringSet.h"
namespace clang::lifetimes {
@@ -139,8 +140,10 @@ bool shouldTrackImplicitObjectArg(const CXXMethodDecl *Callee,
if (RunningUnderLifetimeSafety &&
isGslPointerType(Callee->getFunctionObjectParameterType()) &&
isReferenceOrPointerLikeType(Callee->getReturnType())) {
- if (Callee->getOverloadedOperator() == OverloadedOperatorKind::OO_Star ||
- Callee->getOverloadedOperator() == OverloadedOperatorKind::OO_Arrow)
+ auto Op = Callee->getOverloadedOperator();
+ llvm::SmallDenseSet<OverloadedOperatorKind> PropagatingOps = {
+ OO_Star, OO_Arrow, OO_PlusPlus, OO_MinusMinus, OO_Plus, OO_Minus};
+ if (PropagatingOps.contains(Op))
return true;
if (Callee->getIdentifier() &&
(IteratorMembers.contains(Callee->getName()) ||
@@ -230,6 +233,19 @@ bool shouldTrackFirstArgument(const FunctionDecl *FD) {
return false;
}
+bool shouldTrackSecondArgument(const FunctionDecl *FD) {
+ if (FD->getNumParams() < 2)
+ return false;
+ if (!isInStlNamespace(FD))
+ return false;
+ const auto *RD = FD->getParamDecl(1)->getType()->getAsCXXRecordDecl();
+ if (!RD || !isInStlNamespace(RD))
+ return false;
+ return RD->hasAttr<PointerAttr>() &&
+ (FD->getOverloadedOperator() == OO_Plus ||
+ FD->getOverloadedOperator() == OO_Minus);
+}
+
template <typename T> static bool isRecordWithAttr(QualType Type) {
auto *RD = Type->getAsCXXRecordDecl();
if (!RD)
@@ -283,25 +299,6 @@ bool isUniquePtrRelease(const CXXMethodDecl &MD) {
MD.getNumParams() == 0 && isStdUniquePtr(*MD.getParent());
}
-bool isIteratorType(const CXXRecordDecl *RD) {
- // FIXME: Extend this to cover more iterator wrapper types used by standard
- // library implementations.
- static const llvm::StringSet<> Iterators = {
- // Standard reverse iterator wrapper.
- "reverse_iterator",
- // libstdc++ contiguous iterator wrapper.
- "__normal_iterator",
- // libc++ contiguous iterator wrapper.
- "__wrap_iter"};
- return RD && isInStlNamespace(RD) && Iterators.contains(getName(*RD));
-}
-
-bool isPropagatingIteratorOp(OverloadedOperatorKind OP) {
- llvm::SmallDenseSet<OverloadedOperatorKind> PropagatingOperators = {
- OO_Plus, OO_Minus, OO_PlusPlus, OO_MinusMinus};
- return PropagatingOperators.contains(OP);
-}
-
bool isInvalidationMethod(const CXXMethodDecl &MD) {
const CXXRecordDecl *RD = MD.getParent();
if (!isInStlNamespace(RD))
diff --git a/clang/test/Sema/Inputs/lifetime-analysis.h b/clang/test/Sema/Inputs/lifetime-analysis.h
index 2e7c7feab0b55..79b009170ffd0 100644
--- a/clang/test/Sema/Inputs/lifetime-analysis.h
+++ b/clang/test/Sema/Inputs/lifetime-analysis.h
@@ -3,6 +3,11 @@ namespace __gnu_cxx {
template <typename T>
struct basic_iterator {
basic_iterator operator++();
+ basic_iterator operator++(int);
+ basic_iterator operator--();
+ basic_iterator operator--(int);
+ basic_iterator operator+(int);
+ basic_iterator operator-(int);
T& operator*() const;
T* operator->() const;
};
@@ -11,29 +16,10 @@ template<typename T>
bool operator==(basic_iterator<T>, basic_iterator<T>);
template<typename T>
bool operator!=(basic_iterator<T>, basic_iterator<T>);
-
-// These iterator spellings match libstdc++ names documented at:
-// https://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-4.2/namespace____gnu__cxx.html
-template <typename T>
-struct __normal_iterator {
- __normal_iterator operator++();
- __normal_iterator operator++(int);
- __normal_iterator operator--();
- __normal_iterator operator--(int);
- __normal_iterator operator+(int) const;
- __normal_iterator operator-(int) const;
- T& operator*() const;
- T* operator->() const;
-};
-
-template<typename T>
-bool operator==(__normal_iterator<T>, __normal_iterator<T>);
template<typename T>
-bool operator!=(__normal_iterator<T>, __normal_iterator<T>);
+basic_iterator<T> operator+(int, basic_iterator<T>);
template<typename T>
-__normal_iterator<T> operator+(int, __normal_iterator<T>);
-template<typename T>
-__normal_iterator<T> operator-(int, __normal_iterator<T>);
+basic_iterator<T> operator-(int, basic_iterator<T>);
}
namespace std {
@@ -71,27 +57,6 @@ struct initializer_list {
};
template<typename T> class allocator {};
-template <typename T>
-struct __wrap_iter {
- __wrap_iter operator++();
- __wrap_iter operator++(int);
- __wrap_iter operator--();
- __wrap_iter operator--(int);
- __wrap_iter operator+(int) const;
- __wrap_iter operator-(int) const;
- T& operator*() const;
- T* operator->() const;
-};
-
-template<typename T>
-bool operator==(__wrap_iter<T>, __wrap_iter<T>);
-template<typename T>
-bool operator!=(__wrap_iter<T>, __wrap_iter<T>);
-template<typename T>
-__wrap_iter<T> operator+(int, __wrap_iter<T>);
-template<typename T>
-__wrap_iter<T> operator-(int, __wrap_iter<T>);
-
template <typename Iterator>
struct reverse_iterator {
reverse_iterator operator++();
@@ -110,13 +75,8 @@ reverse_iterator<Iterator> operator-(int, reverse_iterator<Iterator>);
template <typename T, typename Alloc = allocator<T>>
struct vector {
-#ifdef USE_LIBSTDCPP_ITERATORS
- using iterator = __gnu_cxx::__normal_iterator<T>;
- using const_iterator = __gnu_cxx::__normal_iterator<const T>;
-#else
- using iterator = __wrap_iter<T>;
- using const_iterator = __wrap_iter<const T>;
-#endif
+ using iterator = __gnu_cxx::basic_iterator<T>;
+ using const_iterator = __gnu_cxx::basic_iterator<const T>;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
iterator begin();
More information about the cfe-commits
mailing list