[libc++] Uglify non-standard member typedef const_reference in bitset (#121620) According to [[template.bitset.general]](https://eel.is/c++draft/template.bitset.general), `std::bitset` is supposed to have only one (public) member typedef, `reference`. However, libc++'s implementation of `std::bitset` offers more that that. Specifically, it offers a public typedef `const_reference` and two private typedefs `size_type` and `difference_type`. These non-standard member typedefs, despite being private, can cause potential ambiguities in name lookup in user-defined classes, as demonstrated in issue #121618. Fixing the public member typedef `const_reference` is straightforward: we can simply replace it with an `__ugly_name` such as `__const_reference`. However, fixing the private member typedefs `size_type` and `difference_type` is not so straightforward as they are required by the `__bit_iterator` class and the corresponding algorithms optimized for `__bit_iterator`s (e.g., `ranges::fill`). This PR fixes the member typedef `const_reference` by using uglified name for it. Further work will be undertaken to address `size_type` and `difference_type`. Follows up #80706, #111127, and #112843, GitOrigin-RevId: 06673a9e9b186a65f5eb60a59a4ee9afba6637d4
diff --git a/docs/ReleaseNotes/20.rst b/docs/ReleaseNotes/20.rst index ecfbaa5..793b172 100644 --- a/docs/ReleaseNotes/20.rst +++ b/docs/ReleaseNotes/20.rst
@@ -125,9 +125,10 @@ supported as an extension anymore, please migrate any code that uses e.g. ``std::vector<const T>`` to be standards conforming. -- Non-conforming member typedefs ``base``, ``iterator`` and ``const_iterator`` of ``std::bitset``, and member typedef - ``base`` of ``std::forward_list`` and ``std::list`` are removed. Previously, they were private but could cause - ambiguity in name lookup. Code that expects such ambiguity will possibly not compile in LLVM 20. +- Non-conforming member typedefs ``base``, ``iterator``, ``const_iterator``, and ``const_reference`` of ``std::bitset``, + and member typedef ``base`` of ``std::forward_list`` and ``std::list`` are removed. Previously, these member typedefs + (except ``const_reference``) were private but could cause ambiguity in name lookup. Code that expects such ambiguity + will possibly not compile in LLVM 20. - The function ``__libcpp_verbose_abort()`` is now ``noexcept``, to match ``std::terminate()``. (The combination of ``noexcept`` and ``[[noreturn]]`` has special significance for function effects analysis.) For backwards compatibility,
diff --git a/include/bitset b/include/bitset index 919d2a0..c16635d 100644 --- a/include/bitset +++ b/include/bitset
@@ -189,7 +189,7 @@ __storage_type __first_[_N_words]; typedef __bit_reference<__bitset> reference; - typedef __bit_const_reference<__bitset> const_reference; + typedef __bit_const_reference<__bitset> __const_reference; typedef __bit_iterator<__bitset, false> __iterator; typedef __bit_iterator<__bitset, true> __const_iterator; @@ -199,8 +199,8 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 reference __make_ref(size_t __pos) _NOEXCEPT { return reference(__first_ + __pos / __bits_per_word, __storage_type(1) << __pos % __bits_per_word); } - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR const_reference __make_ref(size_t __pos) const _NOEXCEPT { - return const_reference(__first_ + __pos / __bits_per_word, __storage_type(1) << __pos % __bits_per_word); + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __const_reference __make_ref(size_t __pos) const _NOEXCEPT { + return __const_reference(__first_ + __pos / __bits_per_word, __storage_type(1) << __pos % __bits_per_word); } _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 __iterator __make_iter(size_t __pos) _NOEXCEPT { return __iterator(__first_ + __pos / __bits_per_word, __pos % __bits_per_word); @@ -451,7 +451,7 @@ __storage_type __first_; typedef __bit_reference<__bitset> reference; - typedef __bit_const_reference<__bitset> const_reference; + typedef __bit_const_reference<__bitset> __const_reference; typedef __bit_iterator<__bitset, false> __iterator; typedef __bit_iterator<__bitset, true> __const_iterator; @@ -461,8 +461,8 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 reference __make_ref(size_t __pos) _NOEXCEPT { return reference(&__first_, __storage_type(1) << __pos); } - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR const_reference __make_ref(size_t __pos) const _NOEXCEPT { - return const_reference(&__first_, __storage_type(1) << __pos); + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __const_reference __make_ref(size_t __pos) const _NOEXCEPT { + return __const_reference(&__first_, __storage_type(1) << __pos); } _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 __iterator __make_iter(size_t __pos) _NOEXCEPT { return __iterator(&__first_ + __pos / __bits_per_word, __pos % __bits_per_word); @@ -566,7 +566,7 @@ friend struct __bit_array<__bitset>; typedef __bit_reference<__bitset> reference; - typedef __bit_const_reference<__bitset> const_reference; + typedef __bit_const_reference<__bitset> __const_reference; typedef __bit_iterator<__bitset, false> __iterator; typedef __bit_iterator<__bitset, true> __const_iterator; @@ -576,8 +576,8 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 reference __make_ref(size_t) _NOEXCEPT { return reference(nullptr, 1); } - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR const_reference __make_ref(size_t) const _NOEXCEPT { - return const_reference(nullptr, 1); + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __const_reference __make_ref(size_t) const _NOEXCEPT { + return __const_reference(nullptr, 1); } _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 __iterator __make_iter(size_t) _NOEXCEPT { return __iterator(nullptr, 0); @@ -619,7 +619,7 @@ public: typedef typename __base::reference reference; - typedef typename __base::const_reference const_reference; + typedef typename __base::__const_reference __const_reference; // 23.3.5.1 constructors: _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bitset() _NOEXCEPT {} @@ -689,7 +689,7 @@ return __base::__make_ref(__p); } # else - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR const_reference operator[](size_t __p) const { + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __const_reference operator[](size_t __p) const { _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__p < _Size, "bitset::operator[] index out of bounds"); return __base::__make_ref(__p); }
diff --git a/test/std/utilities/template.bitset/bitset.members/index_const.pass.cpp b/test/std/utilities/template.bitset/bitset.members/index_const.pass.cpp index 77eb905..bb7e10a 100644 --- a/test/std/utilities/template.bitset/bitset.members/index_const.pass.cpp +++ b/test/std/utilities/template.bitset/bitset.members/index_const.pass.cpp
@@ -8,6 +8,8 @@ // constexpr bool operator[](size_t pos) const; // constexpr since C++23 +// XFAIL: FROZEN-CXX03-HEADERS-FIXME + #include <bitset> #include <cassert> #include <cstddef> @@ -18,17 +20,17 @@ template <std::size_t N> TEST_CONSTEXPR_CXX23 void test_index_const() { - std::vector<std::bitset<N> > const cases = get_test_cases<N>(); - for (std::size_t c = 0; c != cases.size(); ++c) { - std::bitset<N> const v = cases[c]; - if (v.size() > 0) { - assert(v[N/2] == v.test(N/2)); - } + std::vector<std::bitset<N> > const cases = get_test_cases<N>(); + for (std::size_t c = 0; c != cases.size(); ++c) { + std::bitset<N> const v = cases[c]; + if (v.size() > 0) { + assert(v[N / 2] == v.test(N / 2)); } + } #if !defined(_LIBCPP_VERSION) || defined(_LIBCPP_ABI_BITSET_VECTOR_BOOL_CONST_SUBSCRIPT_RETURN_BOOL) - ASSERT_SAME_TYPE(decltype(cases[0][0]), bool); + ASSERT_SAME_TYPE(decltype(cases[0][0]), bool); #else - ASSERT_SAME_TYPE(decltype(cases[0][0]), typename std::bitset<N>::const_reference); + ASSERT_SAME_TYPE(decltype(cases[0][0]), typename std::bitset<N>::__const_reference); #endif } @@ -43,10 +45,10 @@ test_index_const<65>(); std::bitset<1> set_; - set_[0] = false; + set_[0] = false; const auto& set = set_; - auto b = set[0]; - set_[0] = true; + auto b = set[0]; + set_[0] = true; #if !defined(_LIBCPP_VERSION) || defined(_LIBCPP_ABI_BITSET_VECTOR_BOOL_CONST_SUBSCRIPT_RETURN_BOOL) assert(!b); #else
diff --git a/test/std/utilities/template.bitset/bitset.members/nonstdmem.uglified.compile.pass.cpp b/test/std/utilities/template.bitset/bitset.members/nonstdmem.uglified.compile.pass.cpp index ae3ac81..ee5c64f 100644 --- a/test/std/utilities/template.bitset/bitset.members/nonstdmem.uglified.compile.pass.cpp +++ b/test/std/utilities/template.bitset/bitset.members/nonstdmem.uglified.compile.pass.cpp
@@ -8,10 +8,11 @@ // <bitset> -// This test ensures that we don't use a non-uglified name 'iterator', -// 'const_iterator', and 'base' in the implementation of bitset. +// This test ensures that we don't use a non-uglified name 'base', 'iterator', +// 'const_iterator', and `const_reference` in the implementation of bitset. // // See https://github.com/llvm/llvm-project/issues/111125. +// See https://github.com/llvm/llvm-project/issues/121618. // XFAIL: FROZEN-CXX03-HEADERS-FIXME @@ -23,6 +24,7 @@ typedef int* iterator; typedef const int* const_iterator; typedef my_base base; + typedef const int& const_reference; }; template <std::size_t N> @@ -57,3 +59,13 @@ static_assert(std::is_same<my_derived<48>::base, my_base>::value, ""); static_assert(std::is_same<my_derived<64>::base, my_base>::value, ""); static_assert(std::is_same<my_derived<96>::base, my_base>::value, ""); + +static_assert(std::is_same<my_derived<0>::const_reference, const int&>::value, ""); +static_assert(std::is_same<my_derived<1>::const_reference, const int&>::value, ""); +static_assert(std::is_same<my_derived<8>::const_reference, const int&>::value, ""); +static_assert(std::is_same<my_derived<12>::const_reference, const int&>::value, ""); +static_assert(std::is_same<my_derived<16>::const_reference, const int&>::value, ""); +static_assert(std::is_same<my_derived<32>::const_reference, const int&>::value, ""); +static_assert(std::is_same<my_derived<48>::const_reference, const int&>::value, ""); +static_assert(std::is_same<my_derived<64>::const_reference, const int&>::value, ""); +static_assert(std::is_same<my_derived<96>::const_reference, const int&>::value, "");