Skip to content
8 changes: 6 additions & 2 deletions libc/src/__support/CPP/algorithm.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_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(const T &a, const T &b) {
template <class T>
LIBC_INLINE constexpr const T &min(LIBC_LIFETIME_BOUND const T &a,
LIBC_LIFETIME_BOUND const T &b) {
return (a < b) ? a : b;
}

Expand Down
19 changes: 13 additions & 6 deletions libc/src/__support/CPP/array.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_LIFETIME_BOUND { return Data[0]; }
LIBC_INLINE constexpr const T &front() const LIBC_LIFETIME_BOUND {
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_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) { return Data[Index]; }
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_INLINE constexpr const T &
operator[](size_t Index) const LIBC_LIFETIME_BOUND {
return Data[Index];
}

Expand Down
2 changes: 1 addition & 1 deletion libc/src/__support/CPP/atomic.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
Expand Down
10 changes: 7 additions & 3 deletions libc/src/__support/CPP/mutex.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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_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.
lock_guard(MutexType &m, adopt_lock_t /* t */) : mutex(m) {}
LIBC_INLINE lock_guard(LIBC_LIFETIME_BOUND 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;
Expand Down
12 changes: 8 additions & 4 deletions libc/src/__support/CPP/optional.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_LIFETIME_BOUND {
return storage.stored_value;
}

LIBC_INLINE constexpr T &value() & { return storage.stored_value; }
LIBC_INLINE constexpr T &value() & LIBC_LIFETIME_BOUND {
return storage.stored_value;
}

LIBC_INLINE constexpr explicit operator bool() const {
return storage.in_use;
Expand All @@ -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_LIFETIME_BOUND {
return storage.stored_value;
}
LIBC_INLINE constexpr T &operator*() & LIBC_LIFETIME_BOUND {
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*() && {
Expand Down
7 changes: 4 additions & 3 deletions libc/src/__support/CPP/span.h
Original file line number Diff line number Diff line change
Expand Up @@ -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> &&
Expand Down Expand Up @@ -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_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(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>
Expand Down
19 changes: 13 additions & 6 deletions libc/src/__support/CPP/string.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 char &front() { return data()[0]; }
LIBC_INLINE constexpr const char &front() const LIBC_LIFETIME_BOUND {
return data()[0];
}
LIBC_INLINE char &front() LIBC_LIFETIME_BOUND { return data()[0]; }

LIBC_INLINE constexpr const char &back() const { return data()[size_ - 1]; }
LIBC_INLINE char &back() { return data()[size_ - 1]; }
LIBC_INLINE constexpr const char &back() const LIBC_LIFETIME_BOUND {
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_INLINE constexpr const char &
operator[](size_t index) const LIBC_LIFETIME_BOUND {
return data()[index];
}
LIBC_INLINE char &operator[](size_t index) LIBC_LIFETIME_BOUND {
return data()[index];
}
LIBC_INLINE char &operator[](size_t index) { return data()[index]; }

LIBC_INLINE const char *c_str() const { return data(); }

Expand Down
15 changes: 14 additions & 1 deletion libc/src/__support/CPP/string_view.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ namespace cpp {
// 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;
Expand All @@ -44,6 +44,15 @@ class 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);
Expand Down Expand Up @@ -77,6 +86,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_LIFETIME_BOUND const char (&Str)[N])
: Data(Str), Len(bounded_length(Str)) {}

LIBC_INLINE constexpr const char *data() const { return Data; }

// Returns the size of the string_view.
Expand Down
4 changes: 3 additions & 1 deletion libc/src/__support/CPP/stringstream.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
6 changes: 4 additions & 2 deletions libc/src/__support/CPP/utility/forward.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_LIFETIME_BOUND 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_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);
Expand Down
3 changes: 2 additions & 1 deletion libc/src/__support/CPP/utility/move.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_LIFETIME_BOUND T &&t) {
return static_cast<typename cpp::remove_reference_t<T> &&>(t);
}

Expand Down
80 changes: 80 additions & 0 deletions libc/src/__support/macros/attributes.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,84 @@ 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)
#define LIBC_LIFETIME_BOUND [[msvc::lifetimebound]]
#elif __has_cpp_attribute(lifetimebound)
#define LIBC_LIFETIME_BOUND [[lifetimebound]]
#else
#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
#define LIBC_GSL_OWNER
#endif

#endif // LLVM_LIBC_SRC___SUPPORT_MACROS_ATTRIBUTES_H
Loading