Skip to content

Conversation

@H-G-Hristov
Copy link
Contributor

@H-G-Hristov H-G-Hristov commented Nov 14, 2025

@H-G-Hristov H-G-Hristov requested a review from a team as a code owner November 14, 2025 09:43
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Nov 14, 2025
@llvmbot
Copy link
Member

llvmbot commented Nov 14, 2025

@llvm/pr-subscribers-libcxx

Author: Hristo Hristov (H-G-Hristov)

Changes

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

2 Files Affected:

  • (modified) libcxx/include/span (+51-35)
  • (added) libcxx/test/libcxx/containers/views/views.span/nodiscard.verify.cpp (+78)
diff --git a/libcxx/include/span b/libcxx/include/span
index 3d4f9e4ba7831..c24c31282e2cb 100644
--- a/libcxx/include/span
+++ b/libcxx/include/span
@@ -310,30 +310,32 @@ public:
   }
 
   template <size_t _Count>
-  _LIBCPP_HIDE_FROM_ABI constexpr span<element_type, _Count> first() const noexcept {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr span<element_type, _Count> first() const noexcept {
     static_assert(_Count <= _Extent, "span<T, N>::first<Count>(): Count out of range");
     return span<element_type, _Count>{data(), _Count};
   }
 
   template <size_t _Count>
-  _LIBCPP_HIDE_FROM_ABI constexpr span<element_type, _Count> last() const noexcept {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr span<element_type, _Count> last() const noexcept {
     static_assert(_Count <= _Extent, "span<T, N>::last<Count>(): Count out of range");
     return span<element_type, _Count>{data() + size() - _Count, _Count};
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr span<element_type, dynamic_extent> first(size_type __count) const noexcept {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr span<element_type, dynamic_extent>
+  first(size_type __count) const noexcept {
     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__count <= size(), "span<T, N>::first(count): count out of range");
     return {data(), __count};
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr span<element_type, dynamic_extent> last(size_type __count) const noexcept {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr span<element_type, dynamic_extent>
+  last(size_type __count) const noexcept {
     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__count <= size(), "span<T, N>::last(count): count out of range");
     return {data() + size() - __count, __count};
   }
 
   template <size_t _Offset, size_t _Count = dynamic_extent>
-  _LIBCPP_HIDE_FROM_ABI constexpr auto
-  subspan() const noexcept -> span<element_type, _Count != dynamic_extent ? _Count : _Extent - _Offset> {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto subspan() const noexcept
+      -> span<element_type, _Count != dynamic_extent ? _Count : _Extent - _Offset> {
     static_assert(_Offset <= _Extent, "span<T, N>::subspan<Offset, Count>(): Offset out of range");
     static_assert(_Count == dynamic_extent || _Count <= _Extent - _Offset,
                   "span<T, N>::subspan<Offset, Count>(): Offset + Count out of range");
@@ -342,7 +344,7 @@ public:
     return _ReturnType{data() + _Offset, _Count == dynamic_extent ? size() - _Offset : _Count};
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr span<element_type, dynamic_extent>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr span<element_type, dynamic_extent>
   subspan(size_type __offset, size_type __count = dynamic_extent) const noexcept {
     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__offset <= size(), "span<T, N>::subspan(offset, count): offset out of range");
     if (__count == dynamic_extent)
@@ -352,8 +354,10 @@ public:
     return {data() + __offset, __count};
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr size_type size() const noexcept { return _Extent; }
-  _LIBCPP_HIDE_FROM_ABI constexpr size_type size_bytes() const noexcept { return _Extent * sizeof(element_type); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr size_type size() const noexcept { return _Extent; }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr size_type size_bytes() const noexcept {
+    return _Extent * sizeof(element_type);
+  }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool empty() const noexcept { return _Extent == 0; }
 
   _LIBCPP_HIDE_FROM_ABI constexpr reference operator[](size_type __idx) const noexcept {
@@ -362,42 +366,46 @@ public:
   }
 
 #    if _LIBCPP_STD_VER >= 26
-  _LIBCPP_HIDE_FROM_ABI constexpr reference at(size_type __index) const {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr reference at(size_type __index) const {
     if (__index >= size())
       std::__throw_out_of_range("span");
     return __data_[__index];
   }
 #    endif
 
-  _LIBCPP_HIDE_FROM_ABI constexpr reference front() const noexcept {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr reference front() const noexcept {
     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), "span<T, N>::front() on empty span");
     return __data_[0];
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr reference back() const noexcept {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr reference back() const noexcept {
     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), "span<T, N>::back() on empty span");
     return __data_[size() - 1];
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr pointer data() const noexcept { return __data_; }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr pointer data() const noexcept { return __data_; }
 
   // [span.iter], span iterator support
-  _LIBCPP_HIDE_FROM_ABI constexpr iterator begin() const noexcept {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator begin() const noexcept {
 #    ifdef _LIBCPP_ABI_BOUNDED_ITERATORS
     return std::__make_bounded_iter(data(), data(), data() + size());
 #    else
     return iterator(data());
 #    endif
   }
-  _LIBCPP_HIDE_FROM_ABI constexpr iterator end() const noexcept {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator end() const noexcept {
 #    ifdef _LIBCPP_ABI_BOUNDED_ITERATORS
     return std::__make_bounded_iter(data() + size(), data(), data() + size());
 #    else
     return iterator(data() + size());
 #    endif
   }
-  _LIBCPP_HIDE_FROM_ABI constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator(end()); }
-  _LIBCPP_HIDE_FROM_ABI constexpr reverse_iterator rend() const noexcept { return reverse_iterator(begin()); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr reverse_iterator rbegin() const noexcept {
+    return reverse_iterator(end());
+  }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr reverse_iterator rend() const noexcept {
+    return reverse_iterator(begin());
+  }
 
   _LIBCPP_HIDE_FROM_ABI span<const byte, _Extent * sizeof(element_type)> __as_bytes() const noexcept {
     return span<const byte, _Extent * sizeof(element_type)>{reinterpret_cast<const byte*>(data()), size_bytes()};
@@ -478,36 +486,38 @@ public:
       : __data_{__other.data()}, __size_{__other.size()} {}
 
   template <size_t _Count>
-  _LIBCPP_HIDE_FROM_ABI constexpr span<element_type, _Count> first() const noexcept {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr span<element_type, _Count> first() const noexcept {
     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(_Count <= size(), "span<T>::first<Count>(): Count out of range");
     return span<element_type, _Count>{data(), _Count};
   }
 
   template <size_t _Count>
-  _LIBCPP_HIDE_FROM_ABI constexpr span<element_type, _Count> last() const noexcept {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr span<element_type, _Count> last() const noexcept {
     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(_Count <= size(), "span<T>::last<Count>(): Count out of range");
     return span<element_type, _Count>{data() + size() - _Count, _Count};
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr span<element_type, dynamic_extent> first(size_type __count) const noexcept {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr span<element_type, dynamic_extent>
+  first(size_type __count) const noexcept {
     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__count <= size(), "span<T>::first(count): count out of range");
     return {data(), __count};
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr span<element_type, dynamic_extent> last(size_type __count) const noexcept {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr span<element_type, dynamic_extent>
+  last(size_type __count) const noexcept {
     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__count <= size(), "span<T>::last(count): count out of range");
     return {data() + size() - __count, __count};
   }
 
   template <size_t _Offset, size_t _Count = dynamic_extent>
-  _LIBCPP_HIDE_FROM_ABI constexpr span<element_type, _Count> subspan() const noexcept {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr span<element_type, _Count> subspan() const noexcept {
     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(_Offset <= size(), "span<T>::subspan<Offset, Count>(): Offset out of range");
     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(_Count == dynamic_extent || _Count <= size() - _Offset,
                                         "span<T>::subspan<Offset, Count>(): Offset + Count out of range");
     return span<element_type, _Count>{data() + _Offset, _Count == dynamic_extent ? size() - _Offset : _Count};
   }
 
-  constexpr span<element_type, dynamic_extent> _LIBCPP_HIDE_FROM_ABI
+  [[nodiscard]] constexpr span<element_type, dynamic_extent> _LIBCPP_HIDE_FROM_ABI
   subspan(size_type __offset, size_type __count = dynamic_extent) const noexcept {
     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__offset <= size(), "span<T>::subspan(offset, count): offset out of range");
     if (__count == dynamic_extent)
@@ -517,8 +527,10 @@ public:
     return {data() + __offset, __count};
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr size_type size() const noexcept { return __size_; }
-  _LIBCPP_HIDE_FROM_ABI constexpr size_type size_bytes() const noexcept { return __size_ * sizeof(element_type); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr size_type size() const noexcept { return __size_; }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr size_type size_bytes() const noexcept {
+    return __size_ * sizeof(element_type);
+  }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool empty() const noexcept { return __size_ == 0; }
 
   _LIBCPP_HIDE_FROM_ABI constexpr reference operator[](size_type __idx) const noexcept {
@@ -527,42 +539,46 @@ public:
   }
 
 #    if _LIBCPP_STD_VER >= 26
-  _LIBCPP_HIDE_FROM_ABI constexpr reference at(size_type __index) const {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr reference at(size_type __index) const {
     if (__index >= size())
       std::__throw_out_of_range("span");
     return __data_[__index];
   }
 #    endif
 
-  _LIBCPP_HIDE_FROM_ABI constexpr reference front() const noexcept {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr reference front() const noexcept {
     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), "span<T>::front() on empty span");
     return __data_[0];
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr reference back() const noexcept {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr reference back() const noexcept {
     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), "span<T>::back() on empty span");
     return __data_[size() - 1];
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr pointer data() const noexcept { return __data_; }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr pointer data() const noexcept { return __data_; }
 
   // [span.iter], span iterator support
-  _LIBCPP_HIDE_FROM_ABI constexpr iterator begin() const noexcept {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator begin() const noexcept {
 #    ifdef _LIBCPP_ABI_BOUNDED_ITERATORS
     return std::__make_bounded_iter(data(), data(), data() + size());
 #    else
     return iterator(data());
 #    endif
   }
-  _LIBCPP_HIDE_FROM_ABI constexpr iterator end() const noexcept {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator end() const noexcept {
 #    ifdef _LIBCPP_ABI_BOUNDED_ITERATORS
     return std::__make_bounded_iter(data() + size(), data(), data() + size());
 #    else
     return iterator(data() + size());
 #    endif
   }
-  _LIBCPP_HIDE_FROM_ABI constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator(end()); }
-  _LIBCPP_HIDE_FROM_ABI constexpr reverse_iterator rend() const noexcept { return reverse_iterator(begin()); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr reverse_iterator rbegin() const noexcept {
+    return reverse_iterator(end());
+  }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr reverse_iterator rend() const noexcept {
+    return reverse_iterator(begin());
+  }
 
   _LIBCPP_HIDE_FROM_ABI span<const byte, dynamic_extent> __as_bytes() const noexcept {
     return {reinterpret_cast<const byte*>(data()), size_bytes()};
@@ -585,13 +601,13 @@ inline constexpr bool ranges::enable_view<span<_ElementType, _Extent>> = true;
 
 //  as_bytes & as_writable_bytes
 template <class _Tp, size_t _Extent>
-_LIBCPP_HIDE_FROM_ABI auto as_bytes(span<_Tp, _Extent> __s) noexcept {
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI auto as_bytes(span<_Tp, _Extent> __s) noexcept {
   return __s.__as_bytes();
 }
 
 template <class _Tp, size_t _Extent>
   requires(!is_const_v<_Tp>)
-_LIBCPP_HIDE_FROM_ABI auto as_writable_bytes(span<_Tp, _Extent> __s) noexcept {
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI auto as_writable_bytes(span<_Tp, _Extent> __s) noexcept {
   return __s.__as_writable_bytes();
 }
 
diff --git a/libcxx/test/libcxx/containers/views/views.span/nodiscard.verify.cpp b/libcxx/test/libcxx/containers/views/views.span/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..16602715d280c
--- /dev/null
+++ b/libcxx/test/libcxx/containers/views/views.span/nodiscard.verify.cpp
@@ -0,0 +1,78 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+
+// <span>
+
+// Check that functions are marked [[nodiscard]]
+
+#include <array>
+#include <span>
+#include <vector>
+
+#include "test_macros.h"
+
+void test() {
+  { // Test with a static extent
+    std::array arr{94, 92};
+    std::span sp{arr};
+
+    sp.first<1>();      // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+    sp.last<1>();       // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+    sp.first(1);        // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+    sp.last(1);         // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+    sp.subspan<0, 1>(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+    sp.subspan(0, 1);   // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+    sp.size();          // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+    sp.size_bytes();    // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+    sp.empty();         // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#if TEST_STD_VER >= 26
+    sp.at(0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
+    sp.front();  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+    sp.back();   // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+    sp.data();   // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+    sp.begin();  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+    sp.end();    // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+    sp.rbegin(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+    sp.rend();   // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+
+    std::as_bytes(sp); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+    // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_writable_bytes(sp);
+  }
+  { // Test with a dynamic extent
+    std::vector vec{94, 92};
+    std::span sp{vec};
+
+    sp.first<1>();      // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+    sp.last<1>();       // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+    sp.first(1);        // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+    sp.last(1);         // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+    sp.subspan<0, 1>(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+    sp.subspan(0, 1);   // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+    sp.size();          // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+    sp.size_bytes();    // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+    sp.empty();         // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#if TEST_STD_VER >= 26
+    sp.at(0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
+    sp.front();  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+    sp.back();   // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+    sp.data();   // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+    sp.begin();  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+    sp.end();    // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+    sp.rbegin(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+    sp.rend();   // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+
+    std::as_bytes(sp); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+    // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_writable_bytes(sp);
+  }
+}

@H-G-Hristov H-G-Hristov force-pushed the hgh/libcxx-nodiscard-to-span branch from 9512c4a to 334f144 Compare November 14, 2025 09:49
Copy link
Contributor

@philnik777 philnik777 left a comment

Choose a reason for hiding this comment

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

LGTM % nits.

Zingam and others added 2 commits November 14, 2025 13:26
…y.cpp

Co-authored-by: Nikolas Klauser <nikolasklauser@berlin.de>
…y.cpp

Co-authored-by: Nikolas Klauser <nikolasklauser@berlin.de>
@Zingam Zingam merged commit 3fb98e7 into llvm:main Nov 18, 2025
80 checks passed
@H-G-Hristov H-G-Hristov deleted the hgh/libcxx-nodiscard-to-span branch November 18, 2025 04:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants