[libcxx-commits] [libcxx] [libc++] Remove non-conforming `__bit_reference::operator&` (PR #188714)

A. Jiang via libcxx-commits libcxx-commits at lists.llvm.org
Thu Mar 26 02:56:39 PDT 2026


https://github.com/frederick-vs-ja created https://github.com/llvm/llvm-project/pull/188714

The overloaded `operator&` caused non-conforming behavior when
- using `operator==` to compare "addresses" of proxy reference objects, and
- relying on the exact type of `&ref`.

`__bit_const_reference::operator&` is kept, because when one defines `_LIBCPP_ABI_BITSET_VECTOR_BOOL_CONST_SUBSCRIPT_RETURN_BOOL` to make the libc++ implementation strategy conforming, the `operator&` will never be exposed to users.

Fixes #173711.

>From b5a161c6ee0d8a29eda4c758f90f2be97b663f28 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Thu, 26 Mar 2026 17:52:04 +0800
Subject: [PATCH] [libc++] Remove non-conforming `__bit_reference::operator&`

The overloaded `operator&` caused non-conforming behavior when
- using `operator==` to compare "addresses" of proxy reference objects,
and
- relying on the exact type of `&ref`.

`__bit_const_reference::operator&` is kept, because when one defines
`_LIBCPP_ABI_BITSET_VECTOR_BOOL_CONST_SUBSCRIPT_RETURN_BOOL` to make the
libc++ implementation strategy conforming, the `operator&` will never be
exposed to users.
---
 libcxx/docs/ReleaseNotes/23.rst               |   3 +
 libcxx/include/__bit_reference                |   3 -
 .../reference/builtin_address_of.pass.cpp     |  98 +++++++++++++++++
 .../reference.builtin_address_of.pass.cpp     | 104 ++++++++++++++++++
 4 files changed, 205 insertions(+), 3 deletions(-)
 create mode 100644 libcxx/test/std/containers/sequences/vector.bool/reference/builtin_address_of.pass.cpp
 create mode 100644 libcxx/test/std/utilities/template.bitset/bitset.members/reference.builtin_address_of.pass.cpp

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 9fcb0bbe98303..88282526859aa 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -58,6 +58,9 @@ Deprecations and Removals
 - In ``__wrap_iter`` (iterator wrapper type for ``array``, ``span``, ``string``, ``string_view`` and ``vector``), 
   the ``base()`` method and ``iterator_type`` member type have been removed as they are non-standard.
 
+- In ``__bit_reference`` (the proxy ``reference`` type of ``bitset`` and ``vector<bool>``), the overloaded ``operator&``
+  is removed as it is non-standard and causes non-conforming behavior.
+
 Potentially breaking changes
 ----------------------------
 
diff --git a/libcxx/include/__bit_reference b/libcxx/include/__bit_reference
index 5155f18754b58..6bbe28967f576 100644
--- a/libcxx/include/__bit_reference
+++ b/libcxx/include/__bit_reference
@@ -165,9 +165,6 @@ public:
   }
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void flip() _NOEXCEPT { *__seg_ ^= __mask_; }
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __bit_iterator<_Cp, false> operator&() const _NOEXCEPT {
-    return __bit_iterator<_Cp, false>(__seg_, static_cast<unsigned>(std::__countr_zero(__mask_)));
-  }
 
 private:
   _LIBCPP_HIDE_FROM_ABI
diff --git a/libcxx/test/std/containers/sequences/vector.bool/reference/builtin_address_of.pass.cpp b/libcxx/test/std/containers/sequences/vector.bool/reference/builtin_address_of.pass.cpp
new file mode 100644
index 0000000000000..fd7483624c03b
--- /dev/null
+++ b/libcxx/test/std/containers/sequences/vector.bool/reference/builtin_address_of.pass.cpp
@@ -0,0 +1,98 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <vector>
+
+// template<class Allocator>
+// class vector<bool, Allocator>::reference;
+
+// Verify that vector<bool, Allocator>::reference has no overloaded operator&.
+
+#include <cassert>
+#include <memory>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include "test_macros.h"
+#include "min_allocator.h"
+
+namespace test_overloads {
+
+struct dummy {};
+void operator&(dummy);
+
+template <class, class = void>
+struct has_nonmember_operator_address_of : std::false_type {};
+template <class T>
+struct has_nonmember_operator_address_of<T, decltype((void)operator&(std::declval<T>()))> : std::true_type {};
+
+template <class, class = void>
+struct has_member_operator_address_of : std::false_type {};
+template <class T>
+struct has_member_operator_address_of<T, decltype((void)std::declval<T>().operator&())> : std::true_type {};
+
+} // namespace test_overloads
+
+template <class, class = void>
+struct can_take_address : std::false_type {};
+template <class T>
+struct can_take_address<T, decltype((void)&std::declval<T>())> : std::true_type {};
+
+template <class, class = void>
+struct has_builtin_operator_address_of : std::false_type {};
+template <class T>
+struct has_builtin_operator_address_of<T, decltype((void)&std::declval<T>())>
+    : std::integral_constant<bool,
+                             !test_overloads::has_nonmember_operator_address_of<T>::value &&
+                                 !test_overloads::has_member_operator_address_of<T>::value> {};
+
+template <class Ref>
+TEST_CONSTEXPR_CXX20 void test_proxy_references(Ref& r1, Ref& r2) {
+  static_assert(can_take_address<Ref&>::value, "");
+  static_assert(can_take_address<const Ref&>::value, "");
+  static_assert(!can_take_address<Ref>::value, "");
+  static_assert(!can_take_address<const Ref>::value, "");
+
+  static_assert(has_builtin_operator_address_of<Ref&>::value, "");
+  static_assert(has_builtin_operator_address_of<const Ref&>::value, "");
+  static_assert(!has_builtin_operator_address_of<Ref>::value, "");
+  static_assert(!has_builtin_operator_address_of<const Ref>::value, "");
+
+  assert(std::addressof(r1) == &r1);
+  assert(std::addressof(r2) == &r2);
+  assert((std::addressof(r1) == std::addressof(r2)) == (&r1 == &r2));
+}
+
+template <class VB>
+TEST_CONSTEXPR_CXX20 void test() {
+  VB vec(1);
+
+  typename VB::reference r1 = vec[0];
+  typename VB::reference r2 = vec[0];
+  test_proxy_references(r1, r2);
+  assert(&r1 != &r2);
+}
+
+TEST_CONSTEXPR_CXX20 bool test() {
+  test<std::vector<bool> >();
+#if TEST_STD_VER >= 11
+  test<std::vector<bool, min_allocator<bool>>>();
+#endif
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 20
+  static_assert(test());
+#endif
+
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/template.bitset/bitset.members/reference.builtin_address_of.pass.cpp b/libcxx/test/std/utilities/template.bitset/bitset.members/reference.builtin_address_of.pass.cpp
new file mode 100644
index 0000000000000..141b850410877
--- /dev/null
+++ b/libcxx/test/std/utilities/template.bitset/bitset.members/reference.builtin_address_of.pass.cpp
@@ -0,0 +1,104 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <bitset>
+
+// template<size_t N>
+// class bitset<N>::reference;
+
+// Verify that bitset<N>::reference has no overloaded operator&.
+
+#include <cassert>
+#include <bitset>
+#include <memory>
+#include <type_traits>
+#include <utility>
+
+#include "test_macros.h"
+
+namespace test_overloads {
+
+struct dummy {};
+void operator&(dummy);
+
+template <class, class = void>
+struct has_nonmember_operator_address_of : std::false_type {};
+template <class T>
+struct has_nonmember_operator_address_of<T, decltype((void)operator&(std::declval<T>()))> : std::true_type {};
+
+template <class, class = void>
+struct has_member_operator_address_of : std::false_type {};
+template <class T>
+struct has_member_operator_address_of<T, decltype((void)std::declval<T>().operator&())> : std::true_type {};
+
+} // namespace test_overloads
+
+template <class, class = void>
+struct can_take_address : std::false_type {};
+template <class T>
+struct can_take_address<T, decltype((void)&std::declval<T>())> : std::true_type {};
+
+template <class, class = void>
+struct has_builtin_operator_address_of : std::false_type {};
+template <class T>
+struct has_builtin_operator_address_of<T, decltype((void)&std::declval<T>())>
+    : std::integral_constant<bool,
+                             !test_overloads::has_nonmember_operator_address_of<T>::value &&
+                                 !test_overloads::has_member_operator_address_of<T>::value> {};
+
+template <class Ref>
+TEST_CONSTEXPR_CXX23 void test_proxy_references(Ref& r1, Ref& r2) {
+  static_assert(can_take_address<Ref&>::value, "");
+  static_assert(can_take_address<const Ref&>::value, "");
+  static_assert(!can_take_address<Ref>::value, "");
+  static_assert(!can_take_address<const Ref>::value, "");
+
+  static_assert(has_builtin_operator_address_of<Ref&>::value, "");
+  static_assert(has_builtin_operator_address_of<const Ref&>::value, "");
+  static_assert(!has_builtin_operator_address_of<Ref>::value, "");
+  static_assert(!has_builtin_operator_address_of<const Ref>::value, "");
+
+  assert(std::addressof(r1) == &r1);
+  assert(std::addressof(r2) == &r2);
+  assert((std::addressof(r1) == std::addressof(r2)) == (&r1 == &r2));
+}
+
+template <class Bitset>
+TEST_CONSTEXPR_CXX23 void test() {
+  Bitset bs;
+
+  typename Bitset::reference r1 = bs[0];
+  typename Bitset::reference r2 = bs[0];
+  test_proxy_references(r1, r2);
+  assert(&r1 != &r2);
+}
+
+TEST_CONSTEXPR_CXX23 bool test() {
+  test<std::bitset<1> >();
+  test<std::bitset<8> >();
+  test<std::bitset<12> >();
+  test<std::bitset<16> >();
+  test<std::bitset<24> >();
+  test<std::bitset<32> >();
+  test<std::bitset<48> >();
+  test<std::bitset<64> >();
+  test<std::bitset<96> >();
+  test<std::bitset<128> >();
+  test<std::bitset<192> >();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 23
+  static_assert(test());
+#endif
+
+  return 0;
+}



More information about the libcxx-commits mailing list