[libc-commits] [libc] [libc] add basic lifetime annotations for support data structures (PR #145933)

Schrodinger ZHU Yifan via libc-commits libc-commits at lists.llvm.org
Fri Oct 17 10:52:07 PDT 2025


https://github.com/SchrodingerZhu updated https://github.com/llvm/llvm-project/pull/145933

>From 0205a04566b46be995ad24044ab8b8b192b884be Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Thu, 26 Jun 2025 13:24:48 -0400
Subject: [PATCH 01/10] [libc] add basic lifetime annotations for support data
 structures

---
 libc/src/__support/CPP/algorithm.h       |  8 ++++++--
 libc/src/__support/CPP/array.h           | 19 +++++++++++++------
 libc/src/__support/CPP/atomic.h          |  2 +-
 libc/src/__support/CPP/mutex.h           | 10 +++++++---
 libc/src/__support/CPP/optional.h        | 12 ++++++++----
 libc/src/__support/CPP/span.h            |  7 ++++---
 libc/src/__support/CPP/string.h          | 15 +++++++++++----
 libc/src/__support/CPP/string_view.h     |  7 ++++++-
 libc/src/__support/CPP/stringstream.h    |  4 +++-
 libc/src/__support/CPP/utility/forward.h |  6 ++++--
 libc/src/__support/CPP/utility/move.h    |  3 ++-
 libc/src/__support/macros/attributes.h   | 24 ++++++++++++++++++++++++
 12 files changed, 89 insertions(+), 28 deletions(-)

diff --git a/libc/src/__support/CPP/algorithm.h b/libc/src/__support/CPP/algorithm.h
index de0c47369d945..301b67842d87e 100644
--- a/libc/src/__support/CPP/algorithm.h
+++ b/libc/src/__support/CPP/algorithm.h
@@ -24,11 +24,15 @@ template <class T = void> struct bit_and {};
 template <class T = void> struct bit_or {};
 template <class T = void> struct bit_xor {};
 
-template <class T> LIBC_INLINE constexpr const T &max(const T &a, const T &b) {
+template <class T>
+LIBC_INLINE constexpr const T &max(LIBC_LIFETIMEBOUND const T &a,
+                                   LIBC_LIFETIMEBOUND const T &b) {
   return (a < b) ? b : a;
 }
 
-template <class T> LIBC_INLINE constexpr const T &min(const T &a, const T &b) {
+template <class T>
+LIBC_INLINE constexpr const T &min(LIBC_LIFETIMEBOUND const T &a,
+                                   LIBC_LIFETIMEBOUND const T &b) {
   return (a < b) ? a : b;
 }
 
diff --git a/libc/src/__support/CPP/array.h b/libc/src/__support/CPP/array.h
index db0a986b71205..b0c0add5f9727 100644
--- a/libc/src/__support/CPP/array.h
+++ b/libc/src/__support/CPP/array.h
@@ -31,15 +31,22 @@ template <class T, size_t N> struct array {
   LIBC_INLINE constexpr T *data() { return Data; }
   LIBC_INLINE constexpr const T *data() const { return Data; }
 
-  LIBC_INLINE constexpr T &front() { return Data[0]; }
-  LIBC_INLINE constexpr const T &front() const { return Data[0]; }
+  LIBC_INLINE constexpr T &front() LIBC_LIFETIMEBOUND { return Data[0]; }
+  LIBC_INLINE constexpr const T &front() const LIBC_LIFETIMEBOUND {
+    return Data[0];
+  }
 
-  LIBC_INLINE constexpr T &back() { return Data[N - 1]; }
-  LIBC_INLINE constexpr const T &back() const { return Data[N - 1]; }
+  LIBC_INLINE constexpr T &back() LIBC_LIFETIMEBOUND { return Data[N - 1]; }
+  LIBC_INLINE constexpr const T &back() const LIBC_LIFETIMEBOUND {
+    return Data[N - 1];
+  }
 
-  LIBC_INLINE constexpr T &operator[](size_t Index) { return Data[Index]; }
+  LIBC_INLINE constexpr T &operator[](size_t Index) LIBC_LIFETIMEBOUND {
+    return Data[Index];
+  }
 
-  LIBC_INLINE constexpr const T &operator[](size_t Index) const {
+  LIBC_INLINE constexpr const T &
+  operator[](size_t Index) const LIBC_LIFETIMEBOUND {
     return Data[Index];
   }
 
diff --git a/libc/src/__support/CPP/atomic.h b/libc/src/__support/CPP/atomic.h
index 53b583c04ba15..ac60f5e9cda09 100644
--- a/libc/src/__support/CPP/atomic.h
+++ b/libc/src/__support/CPP/atomic.h
@@ -256,7 +256,7 @@ template <typename T> struct Atomic {
   LIBC_INLINE void set(T rhs) { val = rhs; }
 };
 
-template <typename T> struct AtomicRef {
+template <typename T> struct LIBC_GSL_POINTER AtomicRef {
   static_assert(is_trivially_copyable_v<T> && is_copy_constructible_v<T> &&
                     is_move_constructible_v<T> && is_copy_assignable_v<T> &&
                     is_move_assignable_v<T>,
diff --git a/libc/src/__support/CPP/mutex.h b/libc/src/__support/CPP/mutex.h
index 8a3102426e2d6..407125c6e452b 100644
--- a/libc/src/__support/CPP/mutex.h
+++ b/libc/src/__support/CPP/mutex.h
@@ -9,6 +9,7 @@
 #ifndef LLVM_LIBC_SRC___SUPPORT_CPP_MUTEX_H
 #define LLVM_LIBC_SRC___SUPPORT_CPP_MUTEX_H
 
+#include "src/__support/macros/attributes.h"
 #include "src/__support/macros/config.h"
 
 namespace LIBC_NAMESPACE_DECL {
@@ -28,14 +29,17 @@ template <typename MutexType> class lock_guard {
 
 public:
   // Calls `m.lock()` upon resource acquisition.
-  explicit lock_guard(MutexType &m) : mutex(m) { mutex.lock(); }
+  LIBC_INLINE explicit lock_guard(LIBC_LIFETIMEBOUND MutexType &m) : mutex(m) {
+    mutex.lock();
+  }
 
   // Acquires ownership of the mutex object `m` without attempting to lock
   // it. The behavior is undefined if the current thread does not hold the
   // lock on `m`. Does not call `m.lock()` upon resource acquisition.
-  lock_guard(MutexType &m, adopt_lock_t /* t */) : mutex(m) {}
+  LIBC_INLINE lock_guard(LIBC_LIFETIMEBOUND MutexType &m, adopt_lock_t /* t */)
+      : mutex(m) {}
 
-  ~lock_guard() { mutex.unlock(); }
+  LIBC_INLINE ~lock_guard() { mutex.unlock(); }
 
   // non-copyable
   lock_guard &operator=(const lock_guard &) = delete;
diff --git a/libc/src/__support/CPP/optional.h b/libc/src/__support/CPP/optional.h
index aed2269db1b11..06cb0e3e992d1 100644
--- a/libc/src/__support/CPP/optional.h
+++ b/libc/src/__support/CPP/optional.h
@@ -108,11 +108,13 @@ template <typename T> class optional {
 
   LIBC_INLINE constexpr void reset() { storage.reset(); }
 
-  LIBC_INLINE constexpr const T &value() const & {
+  LIBC_INLINE constexpr const T &value() const &LIBC_LIFETIMEBOUND {
     return storage.stored_value;
   }
 
-  LIBC_INLINE constexpr T &value() & { return storage.stored_value; }
+  LIBC_INLINE constexpr T &value() & LIBC_LIFETIMEBOUND {
+    return storage.stored_value;
+  }
 
   LIBC_INLINE constexpr explicit operator bool() const {
     return storage.in_use;
@@ -122,10 +124,12 @@ template <typename T> class optional {
     return &storage.stored_value;
   }
   LIBC_INLINE constexpr T *operator->() { return &storage.stored_value; }
-  LIBC_INLINE constexpr const T &operator*() const & {
+  LIBC_INLINE constexpr const T &operator*() const &LIBC_LIFETIMEBOUND {
+    return storage.stored_value;
+  }
+  LIBC_INLINE constexpr T &operator*() & LIBC_LIFETIMEBOUND {
     return storage.stored_value;
   }
-  LIBC_INLINE constexpr T &operator*() & { return storage.stored_value; }
 
   LIBC_INLINE constexpr T &&value() && { return move(storage.stored_value); }
   LIBC_INLINE constexpr T &&operator*() && {
diff --git a/libc/src/__support/CPP/span.h b/libc/src/__support/CPP/span.h
index 9234a26d201cd..c02a139299e51 100644
--- a/libc/src/__support/CPP/span.h
+++ b/libc/src/__support/CPP/span.h
@@ -28,7 +28,7 @@ namespace cpp {
 // - No implicit type conversion (e.g. Span<B>, initialized with As where A
 //   inherits from B),
 // - No reverse iterators
-template <typename T> class span {
+template <typename T> class LIBC_GSL_POINTER span {
   template <typename U>
   LIBC_INLINE_VAR static constexpr bool is_const_view_v =
       !cpp::is_const_v<U> && cpp::is_const_v<T> &&
@@ -64,11 +64,12 @@ template <typename T> class span {
 
   template <typename U, size_t N,
             cpp::enable_if_t<is_compatible_v<U>, bool> = true>
-  LIBC_INLINE constexpr span(U (&arr)[N]) : span_data(arr), span_size(N) {}
+  LIBC_INLINE constexpr span(LIBC_LIFETIMEBOUND U (&arr)[N])
+      : span_data(arr), span_size(N) {}
 
   template <typename U, size_t N,
             cpp::enable_if_t<is_compatible_v<U>, bool> = true>
-  LIBC_INLINE constexpr span(array<U, N> &arr)
+  LIBC_INLINE constexpr span(LIBC_LIFETIMEBOUND array<U, N> &arr)
       : span_data(arr.data()), span_size(arr.size()) {}
 
   template <typename U, cpp::enable_if_t<is_compatible_v<U>, bool> = true>
diff --git a/libc/src/__support/CPP/string.h b/libc/src/__support/CPP/string.h
index 1ac04c7f1f9dc..27748eeb953e3 100644
--- a/libc/src/__support/CPP/string.h
+++ b/libc/src/__support/CPP/string.h
@@ -106,16 +106,23 @@ class string {
   LIBC_INLINE constexpr const char *end() const { return data() + size_; }
   LIBC_INLINE char *end() { return data() + size_; }
 
-  LIBC_INLINE constexpr const char &front() const { return data()[0]; }
+  LIBC_INLINE constexpr const char &front() const LIBC_LIFETIMEBOUND {
+    return data()[0];
+  }
   LIBC_INLINE char &front() { return data()[0]; }
 
-  LIBC_INLINE constexpr const char &back() const { return data()[size_ - 1]; }
+  LIBC_INLINE constexpr const char &back() const LIBC_LIFETIMEBOUND {
+    return data()[size_ - 1];
+  }
   LIBC_INLINE char &back() { return data()[size_ - 1]; }
 
-  LIBC_INLINE constexpr const char &operator[](size_t index) const {
+  LIBC_INLINE constexpr const char &
+  operator[](size_t index) const LIBC_LIFETIMEBOUND {
+    return data()[index];
+  }
+  LIBC_INLINE char &operator[](size_t index) LIBC_LIFETIMEBOUND {
     return data()[index];
   }
-  LIBC_INLINE char &operator[](size_t index) { return data()[index]; }
 
   LIBC_INLINE const char *c_str() const { return data(); }
 
diff --git a/libc/src/__support/CPP/string_view.h b/libc/src/__support/CPP/string_view.h
index aa15814b2e149..b9182cdaa1856 100644
--- a/libc/src/__support/CPP/string_view.h
+++ b/libc/src/__support/CPP/string_view.h
@@ -18,12 +18,13 @@
 namespace LIBC_NAMESPACE_DECL {
 namespace cpp {
 
+class string;
 // This is very simple alternate of the std::string_view class. There is no
 // bounds check performed in any of the methods. The callers are expected to
 // do the checks before invoking the methods.
 //
 // This class will be extended as needed in future.
-class string_view {
+class LIBC_GSL_POINTER string_view {
 private:
   const char *Data;
   size_t Len;
@@ -77,6 +78,10 @@ class string_view {
   LIBC_INLINE constexpr string_view(const char *Str, size_t N)
       : Data(Str), Len(N) {}
 
+  template <size_t N>
+  LIBC_INLINE constexpr string_view(LIBC_LIFETIMEBOUND const char (&Str)[N])
+      : Data(Str), Len(N) {}
+
   LIBC_INLINE constexpr const char *data() const { return Data; }
 
   // Returns the size of the string_view.
diff --git a/libc/src/__support/CPP/stringstream.h b/libc/src/__support/CPP/stringstream.h
index a16084c848688..a0039f2de4858 100644
--- a/libc/src/__support/CPP/stringstream.h
+++ b/libc/src/__support/CPP/stringstream.h
@@ -48,7 +48,9 @@ class StringStream {
   // null terminator was not explicitly written, then the return value
   // will not include one. In order to produce a string_view to a null
   // terminated string, write ENDS explicitly.
-  string_view str() const { return string_view(data.data(), write_ptr); }
+  [[nodiscard]] LIBC_INLINE string_view str() const {
+    return string_view(data.data(), write_ptr);
+  }
 
   // Write the characters from |str| to the stream.
   StringStream &operator<<(string_view str) {
diff --git a/libc/src/__support/CPP/utility/forward.h b/libc/src/__support/CPP/utility/forward.h
index 085b3d16f999b..7383687d7b7c8 100644
--- a/libc/src/__support/CPP/utility/forward.h
+++ b/libc/src/__support/CPP/utility/forward.h
@@ -18,12 +18,14 @@ namespace cpp {
 
 // forward
 template <typename T>
-LIBC_INLINE constexpr T &&forward(remove_reference_t<T> &value) {
+LIBC_INLINE constexpr T &&
+forward(LIBC_LIFETIMEBOUND remove_reference_t<T> &value) {
   return static_cast<T &&>(value);
 }
 
 template <typename T>
-LIBC_INLINE constexpr T &&forward(remove_reference_t<T> &&value) {
+LIBC_INLINE constexpr T &&
+forward(LIBC_LIFETIMEBOUND remove_reference_t<T> &&value) {
   static_assert(!is_lvalue_reference_v<T>,
                 "cannot forward an rvalue as an lvalue");
   return static_cast<T &&>(value);
diff --git a/libc/src/__support/CPP/utility/move.h b/libc/src/__support/CPP/utility/move.h
index b61f723e8d4cb..3bbc5f1df4498 100644
--- a/libc/src/__support/CPP/utility/move.h
+++ b/libc/src/__support/CPP/utility/move.h
@@ -17,7 +17,8 @@ namespace cpp {
 
 // move
 template <class T>
-LIBC_INLINE constexpr cpp::remove_reference_t<T> &&move(T &&t) {
+LIBC_INLINE constexpr cpp::remove_reference_t<T> &&
+move(LIBC_LIFETIMEBOUND T &&t) {
   return static_cast<typename cpp::remove_reference_t<T> &&>(t);
 }
 
diff --git a/libc/src/__support/macros/attributes.h b/libc/src/__support/macros/attributes.h
index d5ff028634940..785661ff97773 100644
--- a/libc/src/__support/macros/attributes.h
+++ b/libc/src/__support/macros/attributes.h
@@ -91,4 +91,28 @@ LIBC_THREAD_MODE_EXTERNAL.
 #define LIBC_NO_SANITIZE_OOB_ACCESS
 #endif
 
+#if __has_attribute(lifetimebound)
+#define LIBC_LIFETIMEBOUND [[clang::lifetimebound]]
+#else
+#define LIBC_LIFETIMEBOUND
+#endif
+
+#if __has_attribute(lifetime_capture_by)
+#define LIBC_LIFETIME_CAPTURE_BY(X) [[clang::lifetime_capture_by(X)]]
+#else
+#define LIBC_LIFETIME_CAPTURE_BY(X)
+#endif
+
+#if defined(__clang__)
+#define LIBC_GSL_POINTER [[gsl::Pointer]]
+#else
+#define LIBC_GSL_POINTER
+#endif
+
+#if defined(__clang__)
+#define LIBC_GSL_OWNER [[gsl::Owner]]
+#else
+#define LIBC_GSL_OWNER
+#endif
+
 #endif // LLVM_LIBC_SRC___SUPPORT_MACROS_ATTRIBUTES_H

>From f54f8ea1df2f5fa4026fee54dfc68af43e67ce6a Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Thu, 26 Jun 2025 13:27:03 -0400
Subject: [PATCH 02/10] more

---
 libc/src/__support/CPP/string_view.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libc/src/__support/CPP/string_view.h b/libc/src/__support/CPP/string_view.h
index b9182cdaa1856..bcdfe0eec4758 100644
--- a/libc/src/__support/CPP/string_view.h
+++ b/libc/src/__support/CPP/string_view.h
@@ -18,7 +18,6 @@
 namespace LIBC_NAMESPACE_DECL {
 namespace cpp {
 
-class string;
 // This is very simple alternate of the std::string_view class. There is no
 // bounds check performed in any of the methods. The callers are expected to
 // do the checks before invoking the methods.

>From 89c1587249292a6c5398656d39aecd92b99f7263 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Sat, 28 Jun 2025 23:54:05 -0400
Subject: [PATCH 03/10] Update libc/src/__support/CPP/string.h

Co-authored-by: Copilot <175728472+Copilot at users.noreply.github.com>
---
 libc/src/__support/CPP/string.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libc/src/__support/CPP/string.h b/libc/src/__support/CPP/string.h
index 27748eeb953e3..ad33fc8e536cd 100644
--- a/libc/src/__support/CPP/string.h
+++ b/libc/src/__support/CPP/string.h
@@ -109,7 +109,7 @@ class string {
   LIBC_INLINE constexpr const char &front() const LIBC_LIFETIMEBOUND {
     return data()[0];
   }
-  LIBC_INLINE char &front() { return data()[0]; }
+  LIBC_INLINE char &front() LIBC_LIFETIMEBOUND { return data()[0]; }
 
   LIBC_INLINE constexpr const char &back() const LIBC_LIFETIMEBOUND {
     return data()[size_ - 1];

>From 01fc5d506beb92bed1c0a48afee08f926cb63d84 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Sat, 28 Jun 2025 23:54:11 -0400
Subject: [PATCH 04/10] Update libc/src/__support/CPP/string.h

Co-authored-by: Copilot <175728472+Copilot at users.noreply.github.com>
---
 libc/src/__support/CPP/string.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libc/src/__support/CPP/string.h b/libc/src/__support/CPP/string.h
index ad33fc8e536cd..59d5cffa0ddb9 100644
--- a/libc/src/__support/CPP/string.h
+++ b/libc/src/__support/CPP/string.h
@@ -114,7 +114,7 @@ class string {
   LIBC_INLINE constexpr const char &back() const LIBC_LIFETIMEBOUND {
     return data()[size_ - 1];
   }
-  LIBC_INLINE char &back() { return data()[size_ - 1]; }
+  LIBC_INLINE char &back() LIBC_LIFETIMEBOUND { return data()[size_ - 1]; }
 
   LIBC_INLINE constexpr const char &
   operator[](size_t index) const LIBC_LIFETIMEBOUND {

>From 43126f7ac6fb66e2baf174ca48d4e87e138111f2 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Fri, 17 Oct 2025 11:28:54 -0400
Subject: [PATCH 05/10] [libc] address lntue's comments

---
 libc/src/__support/CPP/algorithm.h       |  8 ++++----
 libc/src/__support/CPP/array.h           | 12 ++++++------
 libc/src/__support/CPP/mutex.h           |  4 ++--
 libc/src/__support/CPP/optional.h        |  8 ++++----
 libc/src/__support/CPP/span.h            |  4 ++--
 libc/src/__support/CPP/string.h          | 12 ++++++------
 libc/src/__support/CPP/string_view.h     |  2 +-
 libc/src/__support/CPP/utility/forward.h |  4 ++--
 libc/src/__support/CPP/utility/move.h    |  2 +-
 libc/src/__support/macros/attributes.h   |  4 ++--
 10 files changed, 30 insertions(+), 30 deletions(-)

diff --git a/libc/src/__support/CPP/algorithm.h b/libc/src/__support/CPP/algorithm.h
index 301b67842d87e..1acd1581a942e 100644
--- a/libc/src/__support/CPP/algorithm.h
+++ b/libc/src/__support/CPP/algorithm.h
@@ -25,14 +25,14 @@ template <class T = void> struct bit_or {};
 template <class T = void> struct bit_xor {};
 
 template <class T>
-LIBC_INLINE constexpr const T &max(LIBC_LIFETIMEBOUND const T &a,
-                                   LIBC_LIFETIMEBOUND const T &b) {
+LIBC_INLINE constexpr const T &max(LIBC_LIFETIME_BOUND const T &a,
+                                   LIBC_LIFETIME_BOUND const T &b) {
   return (a < b) ? b : a;
 }
 
 template <class T>
-LIBC_INLINE constexpr const T &min(LIBC_LIFETIMEBOUND const T &a,
-                                   LIBC_LIFETIMEBOUND const T &b) {
+LIBC_INLINE constexpr const T &min(LIBC_LIFETIME_BOUND const T &a,
+                                   LIBC_LIFETIME_BOUND const T &b) {
   return (a < b) ? a : b;
 }
 
diff --git a/libc/src/__support/CPP/array.h b/libc/src/__support/CPP/array.h
index b0c0add5f9727..44c493fd2d50b 100644
--- a/libc/src/__support/CPP/array.h
+++ b/libc/src/__support/CPP/array.h
@@ -31,22 +31,22 @@ template <class T, size_t N> struct array {
   LIBC_INLINE constexpr T *data() { return Data; }
   LIBC_INLINE constexpr const T *data() const { return Data; }
 
-  LIBC_INLINE constexpr T &front() LIBC_LIFETIMEBOUND { return Data[0]; }
-  LIBC_INLINE constexpr const T &front() const LIBC_LIFETIMEBOUND {
+  LIBC_INLINE constexpr T &front() LIBC_LIFETIME_BOUND { return Data[0]; }
+  LIBC_INLINE constexpr const T &front() const LIBC_LIFETIME_BOUND {
     return Data[0];
   }
 
-  LIBC_INLINE constexpr T &back() LIBC_LIFETIMEBOUND { return Data[N - 1]; }
-  LIBC_INLINE constexpr const T &back() const LIBC_LIFETIMEBOUND {
+  LIBC_INLINE constexpr T &back() LIBC_LIFETIME_BOUND { return Data[N - 1]; }
+  LIBC_INLINE constexpr const T &back() const LIBC_LIFETIME_BOUND {
     return Data[N - 1];
   }
 
-  LIBC_INLINE constexpr T &operator[](size_t Index) LIBC_LIFETIMEBOUND {
+  LIBC_INLINE constexpr T &operator[](size_t Index) LIBC_LIFETIME_BOUND {
     return Data[Index];
   }
 
   LIBC_INLINE constexpr const T &
-  operator[](size_t Index) const LIBC_LIFETIMEBOUND {
+  operator[](size_t Index) const LIBC_LIFETIME_BOUND {
     return Data[Index];
   }
 
diff --git a/libc/src/__support/CPP/mutex.h b/libc/src/__support/CPP/mutex.h
index 407125c6e452b..2ad33da34a80d 100644
--- a/libc/src/__support/CPP/mutex.h
+++ b/libc/src/__support/CPP/mutex.h
@@ -29,14 +29,14 @@ template <typename MutexType> class lock_guard {
 
 public:
   // Calls `m.lock()` upon resource acquisition.
-  LIBC_INLINE explicit lock_guard(LIBC_LIFETIMEBOUND MutexType &m) : mutex(m) {
+  LIBC_INLINE explicit lock_guard(LIBC_LIFETIME_BOUND MutexType &m) : mutex(m) {
     mutex.lock();
   }
 
   // Acquires ownership of the mutex object `m` without attempting to lock
   // it. The behavior is undefined if the current thread does not hold the
   // lock on `m`. Does not call `m.lock()` upon resource acquisition.
-  LIBC_INLINE lock_guard(LIBC_LIFETIMEBOUND MutexType &m, adopt_lock_t /* t */)
+  LIBC_INLINE lock_guard(LIBC_LIFETIME_BOUND MutexType &m, adopt_lock_t /* t */)
       : mutex(m) {}
 
   LIBC_INLINE ~lock_guard() { mutex.unlock(); }
diff --git a/libc/src/__support/CPP/optional.h b/libc/src/__support/CPP/optional.h
index 06cb0e3e992d1..15f389c89627e 100644
--- a/libc/src/__support/CPP/optional.h
+++ b/libc/src/__support/CPP/optional.h
@@ -108,11 +108,11 @@ template <typename T> class optional {
 
   LIBC_INLINE constexpr void reset() { storage.reset(); }
 
-  LIBC_INLINE constexpr const T &value() const &LIBC_LIFETIMEBOUND {
+  LIBC_INLINE constexpr const T &value() const &LIBC_LIFETIME_BOUND {
     return storage.stored_value;
   }
 
-  LIBC_INLINE constexpr T &value() & LIBC_LIFETIMEBOUND {
+  LIBC_INLINE constexpr T &value() & LIBC_LIFETIME_BOUND {
     return storage.stored_value;
   }
 
@@ -124,10 +124,10 @@ template <typename T> class optional {
     return &storage.stored_value;
   }
   LIBC_INLINE constexpr T *operator->() { return &storage.stored_value; }
-  LIBC_INLINE constexpr const T &operator*() const &LIBC_LIFETIMEBOUND {
+  LIBC_INLINE constexpr const T &operator*() const &LIBC_LIFETIME_BOUND {
     return storage.stored_value;
   }
-  LIBC_INLINE constexpr T &operator*() & LIBC_LIFETIMEBOUND {
+  LIBC_INLINE constexpr T &operator*() & LIBC_LIFETIME_BOUND {
     return storage.stored_value;
   }
 
diff --git a/libc/src/__support/CPP/span.h b/libc/src/__support/CPP/span.h
index c02a139299e51..58db573ef4761 100644
--- a/libc/src/__support/CPP/span.h
+++ b/libc/src/__support/CPP/span.h
@@ -64,12 +64,12 @@ template <typename T> class LIBC_GSL_POINTER span {
 
   template <typename U, size_t N,
             cpp::enable_if_t<is_compatible_v<U>, bool> = true>
-  LIBC_INLINE constexpr span(LIBC_LIFETIMEBOUND U (&arr)[N])
+  LIBC_INLINE constexpr span(LIBC_LIFETIME_BOUND U (&arr)[N])
       : span_data(arr), span_size(N) {}
 
   template <typename U, size_t N,
             cpp::enable_if_t<is_compatible_v<U>, bool> = true>
-  LIBC_INLINE constexpr span(LIBC_LIFETIMEBOUND array<U, N> &arr)
+  LIBC_INLINE constexpr span(LIBC_LIFETIME_BOUND array<U, N> &arr)
       : span_data(arr.data()), span_size(arr.size()) {}
 
   template <typename U, cpp::enable_if_t<is_compatible_v<U>, bool> = true>
diff --git a/libc/src/__support/CPP/string.h b/libc/src/__support/CPP/string.h
index 59d5cffa0ddb9..6a3bf267bc021 100644
--- a/libc/src/__support/CPP/string.h
+++ b/libc/src/__support/CPP/string.h
@@ -106,21 +106,21 @@ class string {
   LIBC_INLINE constexpr const char *end() const { return data() + size_; }
   LIBC_INLINE char *end() { return data() + size_; }
 
-  LIBC_INLINE constexpr const char &front() const LIBC_LIFETIMEBOUND {
+  LIBC_INLINE constexpr const char &front() const LIBC_LIFETIME_BOUND {
     return data()[0];
   }
-  LIBC_INLINE char &front() LIBC_LIFETIMEBOUND { return data()[0]; }
+  LIBC_INLINE char &front() LIBC_LIFETIME_BOUND { return data()[0]; }
 
-  LIBC_INLINE constexpr const char &back() const LIBC_LIFETIMEBOUND {
+  LIBC_INLINE constexpr const char &back() const LIBC_LIFETIME_BOUND {
     return data()[size_ - 1];
   }
-  LIBC_INLINE char &back() LIBC_LIFETIMEBOUND { return data()[size_ - 1]; }
+  LIBC_INLINE char &back() LIBC_LIFETIME_BOUND { return data()[size_ - 1]; }
 
   LIBC_INLINE constexpr const char &
-  operator[](size_t index) const LIBC_LIFETIMEBOUND {
+  operator[](size_t index) const LIBC_LIFETIME_BOUND {
     return data()[index];
   }
-  LIBC_INLINE char &operator[](size_t index) LIBC_LIFETIMEBOUND {
+  LIBC_INLINE char &operator[](size_t index) LIBC_LIFETIME_BOUND {
     return data()[index];
   }
 
diff --git a/libc/src/__support/CPP/string_view.h b/libc/src/__support/CPP/string_view.h
index bcdfe0eec4758..c1be334131eb1 100644
--- a/libc/src/__support/CPP/string_view.h
+++ b/libc/src/__support/CPP/string_view.h
@@ -78,7 +78,7 @@ class LIBC_GSL_POINTER string_view {
       : Data(Str), Len(N) {}
 
   template <size_t N>
-  LIBC_INLINE constexpr string_view(LIBC_LIFETIMEBOUND const char (&Str)[N])
+  LIBC_INLINE constexpr string_view(LIBC_LIFETIME_BOUND const char (&Str)[N])
       : Data(Str), Len(N) {}
 
   LIBC_INLINE constexpr const char *data() const { return Data; }
diff --git a/libc/src/__support/CPP/utility/forward.h b/libc/src/__support/CPP/utility/forward.h
index 7383687d7b7c8..f367a34dea57f 100644
--- a/libc/src/__support/CPP/utility/forward.h
+++ b/libc/src/__support/CPP/utility/forward.h
@@ -19,13 +19,13 @@ namespace cpp {
 // forward
 template <typename T>
 LIBC_INLINE constexpr T &&
-forward(LIBC_LIFETIMEBOUND remove_reference_t<T> &value) {
+forward(LIBC_LIFETIME_BOUND remove_reference_t<T> &value) {
   return static_cast<T &&>(value);
 }
 
 template <typename T>
 LIBC_INLINE constexpr T &&
-forward(LIBC_LIFETIMEBOUND remove_reference_t<T> &&value) {
+forward(LIBC_LIFETIME_BOUND remove_reference_t<T> &&value) {
   static_assert(!is_lvalue_reference_v<T>,
                 "cannot forward an rvalue as an lvalue");
   return static_cast<T &&>(value);
diff --git a/libc/src/__support/CPP/utility/move.h b/libc/src/__support/CPP/utility/move.h
index 3bbc5f1df4498..e77d64066270b 100644
--- a/libc/src/__support/CPP/utility/move.h
+++ b/libc/src/__support/CPP/utility/move.h
@@ -18,7 +18,7 @@ namespace cpp {
 // move
 template <class T>
 LIBC_INLINE constexpr cpp::remove_reference_t<T> &&
-move(LIBC_LIFETIMEBOUND T &&t) {
+move(LIBC_LIFETIME_BOUND T &&t) {
   return static_cast<typename cpp::remove_reference_t<T> &&>(t);
 }
 
diff --git a/libc/src/__support/macros/attributes.h b/libc/src/__support/macros/attributes.h
index 785661ff97773..accce465af2f3 100644
--- a/libc/src/__support/macros/attributes.h
+++ b/libc/src/__support/macros/attributes.h
@@ -92,9 +92,9 @@ LIBC_THREAD_MODE_EXTERNAL.
 #endif
 
 #if __has_attribute(lifetimebound)
-#define LIBC_LIFETIMEBOUND [[clang::lifetimebound]]
+#define LIBC_LIFETIME_BOUND [[clang::lifetimebound]]
 #else
-#define LIBC_LIFETIMEBOUND
+#define LIBC_LIFETIME_BOUND
 #endif
 
 #if __has_attribute(lifetime_capture_by)

>From 8eed3a47477b6eda627346fb2a71366608105a57 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Fri, 17 Oct 2025 11:32:39 -0400
Subject: [PATCH 06/10] [libc] improve macro guards

---
 libc/src/__support/macros/attributes.h | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/libc/src/__support/macros/attributes.h b/libc/src/__support/macros/attributes.h
index accce465af2f3..027d5b4675516 100644
--- a/libc/src/__support/macros/attributes.h
+++ b/libc/src/__support/macros/attributes.h
@@ -91,25 +91,29 @@ LIBC_THREAD_MODE_EXTERNAL.
 #define LIBC_NO_SANITIZE_OOB_ACCESS
 #endif
 
-#if __has_attribute(lifetimebound)
+#if __has_cpp_attribute(clang::lifetimebound)
 #define LIBC_LIFETIME_BOUND [[clang::lifetimebound]]
+#elif __has_cpp_attribute(msvc::lifetimebound)
+#define LIBC_LIFETIME_BOUND [[msvc::lifetimebound]]
+#elif __has_cpp_attribute(lifetimebound)
+#define LIBC_LIFETIME_BOUND [[lifetimebound]]
 #else
 #define LIBC_LIFETIME_BOUND
 #endif
 
-#if __has_attribute(lifetime_capture_by)
+#if __has_cpp_attribute(clang::lifetime_capture_by)
 #define LIBC_LIFETIME_CAPTURE_BY(X) [[clang::lifetime_capture_by(X)]]
 #else
 #define LIBC_LIFETIME_CAPTURE_BY(X)
 #endif
 
-#if defined(__clang__)
+#if __has_cpp_attribute(gsl::Pointer)
 #define LIBC_GSL_POINTER [[gsl::Pointer]]
 #else
 #define LIBC_GSL_POINTER
 #endif
 
-#if defined(__clang__)
+#if __has_cpp_attribute(gsl::Owner)
 #define LIBC_GSL_OWNER [[gsl::Owner]]
 #else
 #define LIBC_GSL_OWNER

>From 978f7c62e992cb3e297adb4fb16475e106eb9071 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Fri, 17 Oct 2025 11:41:34 -0400
Subject: [PATCH 07/10] [libc] add comment

---
 libc/src/__support/macros/attributes.h | 52 ++++++++++++++++++++++++++
 1 file changed, 52 insertions(+)

diff --git a/libc/src/__support/macros/attributes.h b/libc/src/__support/macros/attributes.h
index 027d5b4675516..986434931d3c6 100644
--- a/libc/src/__support/macros/attributes.h
+++ b/libc/src/__support/macros/attributes.h
@@ -91,6 +91,17 @@ LIBC_THREAD_MODE_EXTERNAL.
 #define LIBC_NO_SANITIZE_OOB_ACCESS
 #endif
 
+// LIBC_LIFETIME_BOUND indicates that a function parameter's lifetime is tied
+// to the return value. This helps compilers detect use-after-free bugs.
+//
+// Example usage:
+//   const T &get_value(const Container &c LIBC_LIFETIME_BOUND,
+///                      const T &default_val LIBC_LIFETIME_BOUND);
+//   // Warns if temporary Container or default_val is bound to the result
+//
+// For member functions, apply after the function signature:
+//   const char *data() const LIBC_LIFETIME_BOUND;
+//   // The returned pointer should not outlive '*this'
 #if __has_cpp_attribute(clang::lifetimebound)
 #define LIBC_LIFETIME_BOUND [[clang::lifetimebound]]
 #elif __has_cpp_attribute(msvc::lifetimebound)
@@ -101,18 +112,59 @@ LIBC_THREAD_MODE_EXTERNAL.
 #define LIBC_LIFETIME_BOUND
 #endif
 
+// LIBC_LIFETIME_CAPTURE_BY(X) indicates that parameter X captures/stores a
+// reference to the annotated parameter. Warns if temporaries are passed.
+//
+// Example usage:
+//   void add_to_set(std::string_view a LIBC_LIFETIME_CAPTURE_BY(s),
+//                   std::set<std::string_view>& s) {
+//     s.insert(a); // 's' now holds a reference to 'a'
+//   }
+//   // Warns: add_to_set(std::string(), s); // temporary captured by 's'
+//
+// X can be: another parameter name, 'this', 'global', or 'unknown'
+// Multiple capturing entities: LIBC_LIFETIME_CAPTURE_BY(s1, s2)
+//
+// For member functions capturing 'this', apply after function signature:
+//   void capture_self(std::set<S*>& s) LIBC_LIFETIME_CAPTURE_BY(s);
 #if __has_cpp_attribute(clang::lifetime_capture_by)
 #define LIBC_LIFETIME_CAPTURE_BY(X) [[clang::lifetime_capture_by(X)]]
 #else
 #define LIBC_LIFETIME_CAPTURE_BY(X)
 #endif
 
+// LIBC_GSL_POINTER marks a class as a "view" type that points to data owned
+// elsewhere. Lifetime analysis treats it as potentially dangling when the
+// owner is destroyed. Use for types like string_view, span, or custom views.
+//
+// Example usage:
+//   class LIBC_GSL_POINTER StringView {
+//     const char *data_;
+//   public:
+//     StringView(const String& s); // Points into 's'
+//   };
+//   // Warns: StringView sv = String(); // sv points to destroyed temporary
+//
+// The attribute takes an optional type parameter (e.g., [[gsl::Pointer(int)]])
+// but it's typically omitted in libc usage.
 #if __has_cpp_attribute(gsl::Pointer)
 #define LIBC_GSL_POINTER [[gsl::Pointer]]
 #else
 #define LIBC_GSL_POINTER
 #endif
 
+// LIBC_GSL_OWNER marks a class as owning the data it manages. When an Owner
+// is destroyed, any Pointer constructed from it becomes dangling.
+//
+// Example usage:
+//   class LIBC_GSL_OWNER String {
+//     char *data_;
+//   public:
+//     ~String() { delete[] data_; }
+//   };
+//
+// Relationship: LIBC_GSL_POINTER types "point into" LIBC_GSL_OWNER types.
+// When the owner dies, pointers derived from it are considered dangling.
 #if __has_cpp_attribute(gsl::Owner)
 #define LIBC_GSL_OWNER [[gsl::Owner]]
 #else

>From e3b71d2007d3d4f3cfd87948da99c21b4427897f Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Fri, 17 Oct 2025 11:50:08 -0400
Subject: [PATCH 08/10] [libc] fix

---
 libc/src/__support/CPP/string_view.h | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/libc/src/__support/CPP/string_view.h b/libc/src/__support/CPP/string_view.h
index c1be334131eb1..03b9e75b3ba4b 100644
--- a/libc/src/__support/CPP/string_view.h
+++ b/libc/src/__support/CPP/string_view.h
@@ -77,9 +77,8 @@ class LIBC_GSL_POINTER string_view {
   LIBC_INLINE constexpr string_view(const char *Str, size_t N)
       : Data(Str), Len(N) {}
 
-  template <size_t N>
-  LIBC_INLINE constexpr string_view(LIBC_LIFETIME_BOUND const char (&Str)[N])
-      : Data(Str), Len(N) {}
+  LIBC_INLINE constexpr string_view(LIBC_LIFETIME_BOUND const char (&Str)[])
+      : Data(Str), Len(length(Str)) {}
 
   LIBC_INLINE constexpr const char *data() const { return Data; }
 

>From 5e3c2915cfa157bfe24103464315677694712a61 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Fri, 17 Oct 2025 11:52:53 -0400
Subject: [PATCH 09/10] [libc] fix

---
 libc/src/__support/CPP/string_view.h | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/libc/src/__support/CPP/string_view.h b/libc/src/__support/CPP/string_view.h
index 03b9e75b3ba4b..1a1601c0c7ba7 100644
--- a/libc/src/__support/CPP/string_view.h
+++ b/libc/src/__support/CPP/string_view.h
@@ -44,6 +44,15 @@ class LIBC_GSL_POINTER string_view {
         return static_cast<size_t>(End - Str);
   }
 
+  template <size_t N>
+  LIBC_INLINE static constexpr size_t
+  bounded_length(LIBC_LIFETIME_BOUND const char (&Str)[N]) {
+    for (size_t i = 0; i < N; ++i)
+      if (Str[i] == '\0')
+        return i;
+    return N;
+  }
+
   LIBC_INLINE bool equals(string_view Other) const {
     return (Len == Other.Len &&
             compareMemory(Data, Other.Data, Other.Len) == 0);
@@ -77,8 +86,9 @@ class LIBC_GSL_POINTER string_view {
   LIBC_INLINE constexpr string_view(const char *Str, size_t N)
       : Data(Str), Len(N) {}
 
-  LIBC_INLINE constexpr string_view(LIBC_LIFETIME_BOUND const char (&Str)[])
-      : Data(Str), Len(length(Str)) {}
+  template <size_t N>
+  LIBC_INLINE constexpr string_view(LIBC_LIFETIME_BOUND const char (&Str)[N])
+      : Data(Str), Len(bounded_length(Str)) {}
 
   LIBC_INLINE constexpr const char *data() const { return Data; }
 

>From 38b754516f6a44e282438ec4f83ba658c7775cdc Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Fri, 17 Oct 2025 13:51:45 -0400
Subject: [PATCH 10/10] [libc] use cpp:: namespace

---
 libc/src/__support/macros/attributes.h | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libc/src/__support/macros/attributes.h b/libc/src/__support/macros/attributes.h
index 986434931d3c6..a1129df936942 100644
--- a/libc/src/__support/macros/attributes.h
+++ b/libc/src/__support/macros/attributes.h
@@ -116,17 +116,17 @@ LIBC_THREAD_MODE_EXTERNAL.
 // reference to the annotated parameter. Warns if temporaries are passed.
 //
 // Example usage:
-//   void add_to_set(std::string_view a LIBC_LIFETIME_CAPTURE_BY(s),
-//                   std::set<std::string_view>& s) {
+//   void add_to_set(cpp::string_view a LIBC_LIFETIME_CAPTURE_BY(s),
+//                   cpp::set<cpp::string_view>& s) {
 //     s.insert(a); // 's' now holds a reference to 'a'
 //   }
-//   // Warns: add_to_set(std::string(), s); // temporary captured by 's'
+//   // Warns: add_to_set(cpp::string(), s); // temporary captured by 's'
 //
 // X can be: another parameter name, 'this', 'global', or 'unknown'
 // Multiple capturing entities: LIBC_LIFETIME_CAPTURE_BY(s1, s2)
 //
 // For member functions capturing 'this', apply after function signature:
-//   void capture_self(std::set<S*>& s) LIBC_LIFETIME_CAPTURE_BY(s);
+//   void capture_self(cpp::set<S*>& s) LIBC_LIFETIME_CAPTURE_BY(s);
 #if __has_cpp_attribute(clang::lifetime_capture_by)
 #define LIBC_LIFETIME_CAPTURE_BY(X) [[clang::lifetime_capture_by(X)]]
 #else



More information about the libc-commits mailing list