[clang] range based for loops (PR #176643)
Utkarsh Saxena via cfe-commits
cfe-commits at lists.llvm.org
Sun Jan 18 03:58:17 PST 2026
https://github.com/usx95 created https://github.com/llvm/llvm-project/pull/176643
None
>From 55b52d314f3ee005ba8910d800c20761ad9ffe66 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Sun, 18 Jan 2026 11:49:39 +0000
Subject: [PATCH] range based for loops
---
.../LifetimeSafety/LifetimeAnnotations.cpp | 8 +++
clang/lib/Sema/SemaAttr.cpp | 10 ++--
.../Sema/warn-lifetime-analysis-nocfg.cpp | 57 +++++++++++++++++--
3 files changed, 65 insertions(+), 10 deletions(-)
diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
index 2772fe20de19b..2ddd4c72c30f4 100644
--- a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
@@ -99,6 +99,14 @@ bool shouldTrackImplicitObjectArg(const CXXMethodDecl *Callee) {
if (!isGslPointerType(Callee->getFunctionObjectParameterType()) &&
!isGslOwnerType(Callee->getFunctionObjectParameterType()))
return false;
+
+ // Track dereference operator for GSL pointers in STL.
+ if (isGslPointerType(Callee->getFunctionObjectParameterType()))
+ if (const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(Callee))
+ if (MD->getOverloadedOperator() == OverloadedOperatorKind::OO_Star ||
+ MD->getOverloadedOperator() == OverloadedOperatorKind::OO_Arrow)
+ return true;
+
if (isPointerLikeType(Callee->getReturnType())) {
if (!Callee->getIdentifier())
return false;
diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp
index 7729c113e422e..d98bcebbea8d7 100644
--- a/clang/lib/Sema/SemaAttr.cpp
+++ b/clang/lib/Sema/SemaAttr.cpp
@@ -138,12 +138,12 @@ void Sema::inferGslPointerAttribute(NamedDecl *ND,
"unordered_multimap",
};
- static const llvm::StringSet<> Iterators{"iterator", "const_iterator",
- "reverse_iterator",
- "const_reverse_iterator"};
+ static const llvm::StringSet<> Iterators{
+ "iterator", "const_iterator",
+ "reverse_iterator", "const_reverse_iterator",
+ "__wrap_iter", "__normal_iterator"};
- if (Parent->isInStdNamespace() && Iterators.count(ND->getName()) &&
- Containers.count(Parent->getName()))
+ if (Parent->isInStdNamespace() && Iterators.count(ND->getName()))
addGslOwnerPointerAttributeIfNotExisting<PointerAttr>(Context,
UnderlyingRecord);
}
diff --git a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
index 7fdc493dbd17a..7830017245b46 100644
--- a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
+++ b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
@@ -911,10 +911,13 @@ struct MySpan {
MySpan(const std::vector<T>& v);
~MySpan();
using iterator = std::iterator<T>;
- iterator begin() const [[clang::lifetimebound]];
+ // FIXME: It is not possible to annotate accessor methods of non-owning view types.
+ // Clang should provide another annotation to mark such functions as 'transparent'.
+ iterator begin() const;
};
+// FIXME: Same as above.
template <typename T>
-typename MySpan<T>::iterator ReturnFirstIt(const MySpan<T>& v [[clang::lifetimebound]]);
+typename MySpan<T>::iterator ReturnFirstIt(const MySpan<T>& v);
void test4() {
std::vector<int> v{1};
@@ -926,15 +929,59 @@ void test4() {
// Ideally, we would diagnose the following case, but due to implementation
// constraints, we do not.
const int& t4 = *MySpan<int>(std::vector<int>{}).begin();
+ use(t1, t2, t4);
- // FIXME: Detect this using the CFG-based lifetime analysis (constructor of a pointer).
- auto it1 = MySpan<int>(v).begin(); // expected-warning {{temporary whose address is use}}
- auto it2 = ReturnFirstIt(MySpan<int>(v)); // expected-warning {{temporary whose address is used}}
+ auto it1 = MySpan<int>(v).begin();
+ auto it2 = ReturnFirstIt(MySpan<int>(v));
use(it1, it2);
}
} // namespace LifetimeboundInterleave
+namespace range_based_for_loop_variables {
+std::string_view test_view_loop_var(std::vector<std::string> strings) {
+ for (std::string_view s : strings) { // cfg-warning {{address of stack memory is returned later}}
+ return s; //cfg-note {{returned here}}
+ }
+ return "";
+}
+
+const char* test_view_loop_var_with_data(std::vector<std::string> strings) {
+ for (std::string_view s : strings) { // cfg-warning {{address of stack memory is returned later}}
+ return s.data(); //cfg-note {{returned here}}
+ }
+ return "";
+}
+
+std::string_view test_no_error_for_views(std::vector<std::string_view> views) {
+ for (std::string_view s : views) {
+ return s;
+ }
+ return "";
+}
+
+std::string_view test_string_ref_var(std::vector<std::string> strings) {
+ for (const std::string& s : strings) { // cfg-warning {{address of stack memory is returned later}}
+ return s; //cfg-note {{returned here}}
+ }
+ return "";
+}
+
+std::string_view test_opt_strings(std::optional<std::vector<std::string>> strings_or) {
+ for (const std::string& s : *strings_or) { // cfg-warning {{address of stack memory is returned later}}
+ return s; //cfg-note {{returned here}}
+ }
+ return "";
+}
+} // namespace range_based_for_loop_variables
+
+namespace iterator_arrow {
+std::string_view test(std::vector<std::string> views) {
+ return views.begin()->data(); // expected-warning {{address of stack memory associated with parameter 'views' returned}} \
+ // cfg-warning {{address of stack memory is returned later}} cfg-note {{returned here}}
+}
+} // namespace iterator_arrow
+
namespace GH120206 {
struct S {
std::string_view s;
More information about the cfe-commits
mailing list