Skip to content

[libc] add basic lifetime annotations for support data structures #145933

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

SchrodingerZhu
Copy link
Contributor

@SchrodingerZhu SchrodingerZhu commented Jun 26, 2025

fix #145932

@llvmbot llvmbot added the libc label Jun 26, 2025
@llvmbot
Copy link
Member

llvmbot commented Jun 26, 2025

@llvm/pr-subscribers-libc

Author: Schrodinger ZHU Yifan (SchrodingerZhu)

Changes

fix #145932


Full diff: https://github.com/llvm/llvm-project/pull/145933.diff

12 Files Affected:

  • (modified) libc/src/__support/CPP/algorithm.h (+6-2)
  • (modified) libc/src/__support/CPP/array.h (+13-6)
  • (modified) libc/src/__support/CPP/atomic.h (+1-1)
  • (modified) libc/src/__support/CPP/mutex.h (+7-3)
  • (modified) libc/src/__support/CPP/optional.h (+8-4)
  • (modified) libc/src/__support/CPP/span.h (+4-3)
  • (modified) libc/src/__support/CPP/string.h (+11-4)
  • (modified) libc/src/__support/CPP/string_view.h (+6-1)
  • (modified) libc/src/__support/CPP/stringstream.h (+3-1)
  • (modified) libc/src/__support/CPP/utility/forward.h (+4-2)
  • (modified) libc/src/__support/CPP/utility/move.h (+2-1)
  • (modified) libc/src/__support/macros/attributes.h (+24)
diff --git a/libc/src/__support/CPP/algorithm.h b/libc/src/__support/CPP/algorithm.h
index 7704b3fa81f0c..3ea48db5b2d5e 100644
--- a/libc/src/__support/CPP/algorithm.h
+++ b/libc/src/__support/CPP/algorithm.h
@@ -18,11 +18,15 @@
 namespace LIBC_NAMESPACE_DECL {
 namespace cpp {
 
-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 2f00b3ed32811..4e084beaf8ef5 100644
--- a/libc/src/__support/CPP/atomic.h
+++ b/libc/src/__support/CPP/atomic.h
@@ -255,7 +255,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 c6474673de85a..72fb3bd6e204b 100644
--- a/libc/src/__support/macros/attributes.h
+++ b/libc/src/__support/macros/attributes.h
@@ -48,4 +48,28 @@
 #define LIBC_PREFERED_TYPE(TYPE)
 #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

@SchrodingerZhu SchrodingerZhu requested review from Copilot and lntue June 26, 2025 17:33
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Adds basic lifetime annotations to support data structures to improve static lifetime checking

  • Defines LIBC_LIFETIMEBOUND, LIBC_LIFETIME_CAPTURE_BY, LIBC_GSL_POINTER, and LIBC_GSL_OWNER macros
  • Annotates utility functions, classes, and methods (move, forward, span, optional, string, array, algorithm, mutex, atomic) with lifetime attributes
  • Adds [[nodiscard]] to StringStream::str()

Reviewed Changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
libc/src/__support/macros/attributes.h Introduce lifetime and GSL attribute macros
libc/src/__support/CPP/utility/move.h Annotate move() parameter with LIBC_LIFETIMEBOUND
libc/src/__support/CPP/utility/forward.h Annotate forward() parameters with LIBC_LIFETIMEBOUND
libc/src/__support/CPP/stringstream.h Add [[nodiscard]] to str()
libc/src/__support/CPP/string_view.h Annotate class and add lifetime-bound literal constructor
libc/src/__support/CPP/string.h Add LIBC_LIFETIMEBOUND to const overloads of front, back, and operator[]
libc/src/__support/CPP/span.h Annotate span class and constructors with lifetime attributes
libc/src/__support/CPP/optional.h Annotate value() and operator* overloads with LIBC_LIFETIMEBOUND
libc/src/__support/CPP/mutex.h Annotate lock_guard constructors with LIBC_LIFETIMEBOUND
libc/src/__support/CPP/atomic.h Annotate AtomicRef with LIBC_GSL_POINTER
libc/src/__support/CPP/array.h Annotate array front, back, and operator[] with LIBC_LIFETIMEBOUND
libc/src/__support/CPP/algorithm.h Annotate min/max parameters with LIBC_LIFETIMEBOUND
Comments suppressed due to low confidence (1)

libc/src/__support/CPP/string_view.h:82

  • The literal-pair constructor sets Len(N), which includes the null terminator in the view. It should use Len(N - 1) to exclude the trailing '\0'.
      : Data(Str), Len(N) {}

Copy link
Contributor

@michaelrj-google michaelrj-google left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are these attributes available for all the versions of clang we support? If not we may also need a version check

SchrodingerZhu and others added 2 commits June 28, 2025 23:54
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[libc] add basic lifetime annotations
3 participants