Skip to content

[libc++][span] LWG4243: as_bytes/as_writable_bytes is broken with span<volatile T>#200993

Merged
eiytoq merged 4 commits into
llvm:mainfrom
eiytoq:fix/lwg4243
Jun 3, 2026
Merged

[libc++][span] LWG4243: as_bytes/as_writable_bytes is broken with span<volatile T>#200993
eiytoq merged 4 commits into
llvm:mainfrom
eiytoq:fix/lwg4243

Conversation

@eiytoq
Copy link
Copy Markdown
Contributor

@eiytoq eiytoq commented Jun 2, 2026

Closes #171317

@eiytoq eiytoq requested a review from a team as a code owner June 2, 2026 03:17
@llvmorg-github-actions llvmorg-github-actions Bot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Jun 2, 2026
@llvmorg-github-actions
Copy link
Copy Markdown

@llvm/pr-subscribers-libcxx

Author: eiytoq (eiytoq)

Changes

Closes #171317


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

5 Files Affected:

  • (modified) libcxx/include/span (+3-1)
  • (modified) libcxx/test/std/containers/views/views.span/span.objectrep/as_bytes.pass.cpp (+5-5)
  • (added) libcxx/test/std/containers/views/views.span/span.objectrep/as_bytes.verify.cpp (+70)
  • (modified) libcxx/test/std/containers/views/views.span/span.objectrep/as_writable_bytes.pass.cpp (+5-5)
  • (modified) libcxx/test/std/containers/views/views.span/span.objectrep/as_writable_bytes.verify.cpp (+54-5)
diff --git a/libcxx/include/span b/libcxx/include/span
index 230ae3fa2b198..1a484478e19da 100644
--- a/libcxx/include/span
+++ b/libcxx/include/span
@@ -172,6 +172,7 @@ template<class R>
 #  include <__type_traits/is_convertible.h>
 #  include <__type_traits/is_integral.h>
 #  include <__type_traits/is_same.h>
+#  include <__type_traits/is_volatile.h>
 #  include <__type_traits/remove_const.h>
 #  include <__type_traits/remove_cv.h>
 #  include <__type_traits/remove_cvref.h>
@@ -585,12 +586,13 @@ inline constexpr bool ranges::enable_view<span<_ElementType, _Extent>> = true;
 
 //  as_bytes & as_writable_bytes
 template <class _Tp, size_t _Extent>
+  requires(!is_volatile_v<_Tp>)
 [[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>)
+  requires(!is_const_v<_Tp> && !is_volatile_v<_Tp>)
 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI auto as_writable_bytes(span<_Tp, _Extent> __s) noexcept {
   return __s.__as_writable_bytes();
 }
diff --git a/libcxx/test/std/containers/views/views.span/span.objectrep/as_bytes.pass.cpp b/libcxx/test/std/containers/views/views.span/span.objectrep/as_bytes.pass.cpp
index 1b62fb94e9ab2..5c5892bc2567c 100644
--- a/libcxx/test/std/containers/views/views.span/span.objectrep/as_bytes.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.objectrep/as_bytes.pass.cpp
@@ -9,12 +9,12 @@
 
 // <span>
 
-// template <class ElementType, size_t Extent>
-//     span<const byte,
-//          Extent == dynamic_extent
-//              ? dynamic_extent
-//              : sizeof(ElementType) * Extent>
+// template<class ElementType, size_t Extent>
+//   span<const byte, Extent == dynamic_extent ? dynamic_extent : sizeof(ElementType) * Extent>
 //     as_bytes(span<ElementType, Extent> s) noexcept;
+//
+// Constraints:
+//   is_volatile_v<ElementType> is false.
 
 #include <cassert>
 #include <cstddef>
diff --git a/libcxx/test/std/containers/views/views.span/span.objectrep/as_bytes.verify.cpp b/libcxx/test/std/containers/views/views.span/span.objectrep/as_bytes.verify.cpp
new file mode 100644
index 0000000000000..cdc830884f23c
--- /dev/null
+++ b/libcxx/test/std/containers/views/views.span/span.objectrep/as_bytes.verify.cpp
@@ -0,0 +1,70 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <span>
+
+// template<class ElementType, size_t Extent>
+//   span<const byte, Extent == dynamic_extent ? dynamic_extent : sizeof(ElementType) * Extent>
+//     as_bytes(span<ElementType, Extent> s) noexcept;
+//
+// Constraints:
+//   is_volatile_v<ElementType> is false.
+
+#include <span>
+#include <string>
+
+#include "test_macros.h"
+
+struct A {};
+
+void f() {
+  std::as_bytes(std::span<volatile int>());
+  // expected-error@-1 {{no matching function for call to 'as_bytes'}}
+  std::as_bytes(std::span<volatile long>());
+  // expected-error@-1 {{no matching function for call to 'as_bytes'}}
+  std::as_bytes(std::span<volatile double>());
+  // expected-error@-1 {{no matching function for call to 'as_bytes'}}
+  std::as_bytes(std::span<volatile A>());
+  // expected-error@-1 {{no matching function for call to 'as_bytes'}}
+  std::as_bytes(std::span<volatile std::string>());
+  // expected-error@-1 {{no matching function for call to 'as_bytes'}}
+
+  std::as_bytes(std::span<const volatile int>());
+  // expected-error@-1 {{no matching function for call to 'as_bytes'}}
+  std::as_bytes(std::span<const volatile long>());
+  // expected-error@-1 {{no matching function for call to 'as_bytes'}}
+  std::as_bytes(std::span<const volatile double>());
+  // expected-error@-1 {{no matching function for call to 'as_bytes'}}
+  std::as_bytes(std::span<const volatile A>());
+  // expected-error@-1 {{no matching function for call to 'as_bytes'}}
+  std::as_bytes(std::span<const volatile std::string>());
+  // expected-error@-1 {{no matching function for call to 'as_bytes'}}
+
+  std::as_bytes(std::span<volatile int, 0>());
+  // expected-error@-1 {{no matching function for call to 'as_bytes'}}
+  std::as_bytes(std::span<volatile long, 0>());
+  // expected-error@-1 {{no matching function for call to 'as_bytes'}}
+  std::as_bytes(std::span<volatile double, 0>());
+  // expected-error@-1 {{no matching function for call to 'as_bytes'}}
+  std::as_bytes(std::span<volatile A, 0>());
+  // expected-error@-1 {{no matching function for call to 'as_bytes'}}
+  std::as_bytes(std::span<volatile std::string, (std::size_t)0>());
+  // expected-error@-1 {{no matching function for call to 'as_bytes'}}
+
+  std::as_bytes(std::span<const volatile int, 0>());
+  // expected-error@-1 {{no matching function for call to 'as_bytes'}}
+  std::as_bytes(std::span<const volatile long, 0>());
+  // expected-error@-1 {{no matching function for call to 'as_bytes'}}
+  std::as_bytes(std::span<const volatile double, 0>());
+  // expected-error@-1 {{no matching function for call to 'as_bytes'}}
+  std::as_bytes(std::span<const volatile A, 0>());
+  // expected-error@-1 {{no matching function for call to 'as_bytes'}}
+  std::as_bytes(std::span<const volatile std::string, (std::size_t)0>());
+  // expected-error@-1 {{no matching function for call to 'as_bytes'}}
+}
diff --git a/libcxx/test/std/containers/views/views.span/span.objectrep/as_writable_bytes.pass.cpp b/libcxx/test/std/containers/views/views.span/span.objectrep/as_writable_bytes.pass.cpp
index 40c6a581e9acf..be58e1f39f97b 100644
--- a/libcxx/test/std/containers/views/views.span/span.objectrep/as_writable_bytes.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.objectrep/as_writable_bytes.pass.cpp
@@ -9,12 +9,12 @@
 
 // <span>
 
-// template <class ElementType, size_t Extent>
-//     span<byte,
-//          Extent == dynamic_extent
-//              ? dynamic_extent
-//              : sizeof(ElementType) * Extent>
+// template<class ElementType, size_t Extent>
+//   span<byte, Extent == dynamic_extent ? dynamic_extent : sizeof(ElementType) * Extent>
 //     as_writable_bytes(span<ElementType, Extent> s) noexcept;
+//
+// Constraints:
+//   is_const_v<ElementType> is false and is_volatile_v<ElementType> is false.
 
 #include <cassert>
 #include <cstddef>
diff --git a/libcxx/test/std/containers/views/views.span/span.objectrep/as_writable_bytes.verify.cpp b/libcxx/test/std/containers/views/views.span/span.objectrep/as_writable_bytes.verify.cpp
index 9d22641ab2687..12a2229e130a8 100644
--- a/libcxx/test/std/containers/views/views.span/span.objectrep/as_writable_bytes.verify.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.objectrep/as_writable_bytes.verify.cpp
@@ -9,12 +9,12 @@
 
 // <span>
 
-// template <class ElementType, size_t Extent>
-//     span<byte,
-//          Extent == dynamic_extent
-//              ? dynamic_extent
-//              : sizeof(ElementType) * Extent>
+// template<class ElementType, size_t Extent>
+//   span<byte, Extent == dynamic_extent ? dynamic_extent : sizeof(ElementType) * Extent>
 //     as_writable_bytes(span<ElementType, Extent> s) noexcept;
+//
+// Constraints:
+//   is_const_v<ElementType> is false and is_volatile_v<ElementType> is false.
 
 #include <span>
 #include <string>
@@ -37,6 +37,28 @@ void f() {
   std::as_writable_bytes(std::span<const std::string>());
   // expected-error@-1 {{no matching function for call to 'as_writable_bytes'}}
 
+  std::as_writable_bytes(std::span<volatile int>());
+  // expected-error@-1 {{no matching function for call to 'as_writable_bytes'}}
+  std::as_writable_bytes(std::span<volatile long>());
+  // expected-error@-1 {{no matching function for call to 'as_writable_bytes'}}
+  std::as_writable_bytes(std::span<volatile double>());
+  // expected-error@-1 {{no matching function for call to 'as_writable_bytes'}}
+  std::as_writable_bytes(std::span<volatile A>());
+  // expected-error@-1 {{no matching function for call to 'as_writable_bytes'}}
+  std::as_writable_bytes(std::span<volatile std::string>());
+  // expected-error@-1 {{no matching function for call to 'as_writable_bytes'}}
+
+  std::as_writable_bytes(std::span<const volatile int>());
+  // expected-error@-1 {{no matching function for call to 'as_writable_bytes'}}
+  std::as_writable_bytes(std::span<const volatile long>());
+  // expected-error@-1 {{no matching function for call to 'as_writable_bytes'}}
+  std::as_writable_bytes(std::span<const volatile double>());
+  // expected-error@-1 {{no matching function for call to 'as_writable_bytes'}}
+  std::as_writable_bytes(std::span<const volatile A>());
+  // expected-error@-1 {{no matching function for call to 'as_writable_bytes'}}
+  std::as_writable_bytes(std::span<const volatile std::string>());
+  // expected-error@-1 {{no matching function for call to 'as_writable_bytes'}}
+
   std::as_writable_bytes(std::span<const int, 0>());
   // expected-error@-1 {{no matching function for call to 'as_writable_bytes'}}
   std::as_writable_bytes(std::span<const long, 0>());
@@ -48,8 +70,35 @@ void f() {
   std::as_writable_bytes(std::span<const std::string, (std::size_t)0>());
   // expected-error@-1 {{no matching function for call to 'as_writable_bytes'}}
 
+  std::as_writable_bytes(std::span<volatile int, 0>());
+  // expected-error@-1 {{no matching function for call to 'as_writable_bytes'}}
+  std::as_writable_bytes(std::span<volatile long, 0>());
+  // expected-error@-1 {{no matching function for call to 'as_writable_bytes'}}
+  std::as_writable_bytes(std::span<volatile double, 0>());
+  // expected-error@-1 {{no matching function for call to 'as_writable_bytes'}}
+  std::as_writable_bytes(std::span<volatile A, 0>());
+  // expected-error@-1 {{no matching function for call to 'as_writable_bytes'}}
+  std::as_writable_bytes(std::span<volatile std::string, (std::size_t)0>());
+  // expected-error@-1 {{no matching function for call to 'as_writable_bytes'}}
+
+  std::as_writable_bytes(std::span<const volatile int, 0>());
+  // expected-error@-1 {{no matching function for call to 'as_writable_bytes'}}
+  std::as_writable_bytes(std::span<const volatile long, 0>());
+  // expected-error@-1 {{no matching function for call to 'as_writable_bytes'}}
+  std::as_writable_bytes(std::span<const volatile double, 0>());
+  // expected-error@-1 {{no matching function for call to 'as_writable_bytes'}}
+  std::as_writable_bytes(std::span<const volatile A, 0>());
+  // expected-error@-1 {{no matching function for call to 'as_writable_bytes'}}
+  std::as_writable_bytes(std::span<const volatile std::string, (std::size_t)0>());
+  // expected-error@-1 {{no matching function for call to 'as_writable_bytes'}}
+
   std::as_writable_bytes(std::span<const int>(iArr2, 1));
   // expected-error@-1 {{no matching function for call to 'as_writable_bytes'}}
   std::as_writable_bytes(std::span<const int, 1>(iArr2 + 5, 1));
   // expected-error@-1 {{no matching function for call to 'as_writable_bytes'}}
+
+  std::as_writable_bytes(std::span<const volatile int>(iArr2, 1));
+  // expected-error@-1 {{no matching function for call to 'as_writable_bytes'}}
+  std::as_writable_bytes(std::span<const volatile int, 1>(iArr2 + 5, 1));
+  // expected-error@-1 {{no matching function for call to 'as_writable_bytes'}}
 }

Comment thread libcxx/test/std/containers/views/views.span/span.objectrep/as_bytes.verify.cpp Outdated
@eiytoq eiytoq requested a review from frederick-vs-ja June 2, 2026 04:26
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 2, 2026

✅ With the latest revision this PR passed the C/C++ code formatter.

Comment thread libcxx/test/std/containers/views/views.span/span.objectrep/as_bytes.pass.cpp Outdated
static_assert(hasAsBytes<const double, 0>());
static_assert(hasAsBytes<const A, 0>());
static_assert(hasAsBytes<const std::string, 0>());

Copy link
Copy Markdown
Contributor

@frederick-vs-ja frederick-vs-ja Jun 2, 2026

Choose a reason for hiding this comment

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

Oh, I'm sorry for ambiguity. I meant "positive" by "successfully compiling". It's better to have test cases for both true and false results, related to cv-qualifiers, from hasAsBytes (same for hasAsWritableBytes).

Copy link
Copy Markdown
Contributor Author

@eiytoq eiytoq Jun 2, 2026

Choose a reason for hiding this comment

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

Can we test the false cases in the pass test and remove the verify test? Since this is a constraints test.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can we test the 'false' cases in the pass test, and remove the verify test?

Yeah. I think this is conventional.

Copy link
Copy Markdown
Contributor

@frederick-vs-ja frederick-vs-ja left a comment

Choose a reason for hiding this comment

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

LGTM now.

@eiytoq eiytoq merged commit e202047 into llvm:main Jun 3, 2026
81 checks passed
@eiytoq eiytoq deleted the fix/lwg4243 branch June 3, 2026 08:34
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.

LWG4243: as_bytes/as_writable_bytes is broken with span<volatile T>

2 participants