diff --git a/stl/inc/array b/stl/inc/array index 1513b3829be..00b4bc37d14 100644 --- a/stl/inc/array +++ b/stl/inc/array @@ -775,7 +775,7 @@ _CONSTEXPR20 void swap(array<_Ty, _Size>& _Left, array<_Ty, _Size>& _Right) noex _EXPORT_STD template _NODISCARD _CONSTEXPR20 bool operator==(const array<_Ty, _Size>& _Left, const array<_Ty, _Size>& _Right) { - return _STD equal(_Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin()); + return _STD equal(_Left.data(), _Left.data() + _Size, _Right.data()); } #if !_HAS_CXX20 @@ -789,14 +789,13 @@ _NODISCARD bool operator!=(const array<_Ty, _Size>& _Left, const array<_Ty, _Siz _EXPORT_STD template _NODISCARD constexpr _Synth_three_way_result<_Ty> operator<=>( const array<_Ty, _Size>& _Left, const array<_Ty, _Size>& _Right) { - return _STD lexicographical_compare_three_way(_Left._Unchecked_begin(), _Left._Unchecked_end(), - _Right._Unchecked_begin(), _Right._Unchecked_end(), _Synth_three_way{}); + return _STD lexicographical_compare_three_way( + _Left.data(), _Left.data() + _Size, _Right.data(), _Right.data() + _Size, _Synth_three_way{}); } #else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv template _NODISCARD _CONSTEXPR20 bool operator<(const array<_Ty, _Size>& _Left, const array<_Ty, _Size>& _Right) { - return _STD lexicographical_compare( - _Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin(), _Right._Unchecked_end()); + return _STD lexicographical_compare(_Left.data(), _Left.data() + _Size, _Right.data(), _Right.data() + _Size); } template @@ -850,25 +849,49 @@ _NODISCARD constexpr array, _Size> to_array(_Ty (&&_Array)[_Siz _EXPORT_STD template _NODISCARD constexpr _Ty& get(array<_Ty, _Size>& _Arr) noexcept { static_assert(_Idx < _Size, "array index out of bounds"); - return _Arr._Elems[_Idx]; + if constexpr (_Has_unchecked_begin_end>) { + return _Arr._Elems[_Idx]; + } else { +#if _HAS_CXX17 + return _Arr[_Idx]; +#else // ^^^ _HAS_CXX17 / !_HAS_CXX17 vvv + return const_cast<_Ty&>(_STD as_const(_Arr)[_Idx]); +#endif // ^^^ !_HAS_CXX17 ^^^ + } } _EXPORT_STD template _NODISCARD constexpr const _Ty& get(const array<_Ty, _Size>& _Arr) noexcept { static_assert(_Idx < _Size, "array index out of bounds"); - return _Arr._Elems[_Idx]; + if constexpr (_Has_unchecked_begin_end>) { + return _Arr._Elems[_Idx]; + } else { + return _Arr[_Idx]; + } } _EXPORT_STD template _NODISCARD constexpr _Ty&& get(array<_Ty, _Size>&& _Arr) noexcept { static_assert(_Idx < _Size, "array index out of bounds"); - return _STD move(_Arr._Elems[_Idx]); + if constexpr (_Has_unchecked_begin_end>) { + return _STD move(_Arr._Elems[_Idx]); + } else { +#if _HAS_CXX17 + return _STD move(_Arr[_Idx]); +#else // ^^^ _HAS_CXX17 / !_HAS_CXX17 vvv + return const_cast<_Ty&&>(_STD move(_STD as_const(_Arr)[_Idx])); +#endif // ^^^ !_HAS_CXX17 ^^^ + } } _EXPORT_STD template _NODISCARD constexpr const _Ty&& get(const array<_Ty, _Size>&& _Arr) noexcept { static_assert(_Idx < _Size, "array index out of bounds"); - return _STD move(_Arr._Elems[_Idx]); + if constexpr (_Has_unchecked_begin_end>) { + return _STD move(_Arr._Elems[_Idx]); + } else { + return _STD move(_Arr[_Idx]); + } } #if _HAS_TR1_NAMESPACE diff --git a/stl/inc/xutility b/stl/inc/xutility index 351138b4716..e03a9c49991 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -585,6 +585,13 @@ struct _Unused_parameter { // generic unused parameter struct constexpr _Unused_parameter(_Ty&&) noexcept {} }; +template // checks whether a container/view is a non-customized specialization +_INLINE_VAR constexpr bool _Has_unchecked_begin_end = false; + +template +_INLINE_VAR constexpr bool _Has_unchecked_begin_end<_Ty, + void_t()._Unchecked_begin()), decltype(_STD declval<_Ty&>()._Unchecked_end())>> = true; + template using _Algorithm_int_t = conditional_t, _Ty, ptrdiff_t>; diff --git a/tests/std/test.lst b/tests/std/test.lst index 98d00840295..8c1a3602607 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -232,6 +232,7 @@ tests\GH_003735_char_traits_signatures tests\GH_003840_tellg_when_reading_lf_file_in_text_mode tests\GH_003867_output_nan tests\GH_004023_mdspan_fwd_prod_overflow +tests\GH_004040_container_nonmember_functions tests\LWG2381_num_get_floating_point tests\LWG2597_complex_branch_cut tests\LWG3018_shared_ptr_function diff --git a/tests/std/tests/GH_004040_container_nonmember_functions/env.lst b/tests/std/tests/GH_004040_container_nonmember_functions/env.lst new file mode 100644 index 00000000000..19f025bd0e6 --- /dev/null +++ b/tests/std/tests/GH_004040_container_nonmember_functions/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_matrix.lst diff --git a/tests/std/tests/GH_004040_container_nonmember_functions/test.cpp b/tests/std/tests/GH_004040_container_nonmember_functions/test.cpp new file mode 100644 index 00000000000..25c53023fa0 --- /dev/null +++ b/tests/std/tests/GH_004040_container_nonmember_functions/test.cpp @@ -0,0 +1,314 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include + +#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) + +#if _HAS_CXX20 +#define CONSTEXPR20 constexpr +#define CONSTEXPR17 constexpr +#define NODISCARD20 [[nodiscard]] +#elif _HAS_CXX17 // ^^^ _HAS_CXX20 / !_HAS_CXX20 && _HAS_CXX17 vvv +#define CONSTEXPR20 inline +#define CONSTEXPR17 constexpr +#define NODISCARD20 +#else // ^^^ !_HAS_CXX20 && _HAS_CXX17 / !_HAS_CXX17 vvv +#define CONSTEXPR20 inline +#define CONSTEXPR17 inline +#define NODISCARD20 +#endif // ^^^ !_HAS_CXX17 ^^^ + +using namespace std; + +struct Meow { +#if _HAS_CXX20 + friend constexpr auto operator<=>(Meow, Meow) = default; +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv + friend constexpr bool operator==(Meow, Meow) noexcept { + return true; + } + + friend constexpr bool operator!=(Meow, Meow) noexcept { + return false; + } + + friend constexpr bool operator<(Meow, Meow) noexcept { + return false; + } + + friend constexpr bool operator>(Meow, Meow) noexcept { + return false; + } + + friend constexpr bool operator<=(Meow, Meow) noexcept { + return true; + } + + friend constexpr bool operator>=(Meow, Meow) noexcept { + return true; + } +#endif // ^^^ !_HAS_CXX20 ^^^ +}; + +template +struct std::array { + using value_type = Meow; + using pointer = Meow*; + using const_pointer = const Meow*; + using reference = Meow&; + using const_reference = const Meow&; + using size_type = size_t; + using difference_type = ptrdiff_t; + using iterator = Meow*; + using const_iterator = const Meow*; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + CONSTEXPR20 void fill(const Meow&) {} // Meow is no-op assignable. + CONSTEXPR20 void swap(array&) noexcept {} // Meow is no-op swappable. + + CONSTEXPR17 iterator begin() noexcept { + return elems_; + } + CONSTEXPR17 const_iterator begin() const noexcept { + return elems_; + } + CONSTEXPR17 iterator end() noexcept { + return elems_ + N; + } + CONSTEXPR17 const_iterator end() const noexcept { + return elems_ + N; + } + + CONSTEXPR17 reverse_iterator rbegin() noexcept { + return reverse_iterator{elems_ + N}; + } + CONSTEXPR17 const_reverse_iterator rbegin() const noexcept { + return const_reverse_iterator{elems_ + N}; + } + CONSTEXPR17 reverse_iterator rend() noexcept { + return reverse_iterator{elems_}; + } + CONSTEXPR17 const_reverse_iterator rend() const noexcept { + return const_reverse_iterator{elems_}; + } + + CONSTEXPR17 const_iterator cbegin() const noexcept { + return elems_; + } + CONSTEXPR17 const_iterator cend() const noexcept { + return elems_ + N; + } + CONSTEXPR17 const_reverse_iterator crbegin() const noexcept { + return const_reverse_iterator{elems_ + N}; + } + CONSTEXPR17 const_reverse_iterator crend() const noexcept { + return const_reverse_iterator{elems_}; + } + + NODISCARD20 constexpr bool empty() const noexcept { + return false; + } + constexpr size_type size() const noexcept { + return N; + } + constexpr size_type max_size() const noexcept { + return N; + } + + CONSTEXPR17 reference operator[](size_type n) { + return elems_[n]; + } + constexpr const_reference operator[](size_type n) const { + return elems_[n]; + } + CONSTEXPR17 reference at(size_type n) { + return n < N ? elems_[n] : throw out_of_range{"bad array access"}; + } + constexpr const_reference at(size_type n) const { + return n < N ? elems_[n] : throw out_of_range{"bad array access"}; + } + CONSTEXPR17 reference front() { + return elems_[0]; + } + constexpr const_reference front() const { + return elems_[0]; + } + CONSTEXPR17 reference back() { + return elems_[N - 1]; + } + constexpr const_reference back() const { + return elems_[N - 1]; + } + + CONSTEXPR17 pointer data() noexcept { + return elems_; + } + CONSTEXPR17 const_pointer data() const noexcept { + return elems_; + } + + Meow elems_[N]; +}; + +template <> +struct std::array { + using value_type = Meow; + using pointer = Meow*; + using const_pointer = const Meow*; + using reference = Meow&; + using const_reference = const Meow&; + using size_type = size_t; + using difference_type = ptrdiff_t; + using iterator = Meow*; + using const_iterator = const Meow*; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + CONSTEXPR20 void fill(const Meow&) {} // Meow is no-op assignable. + CONSTEXPR20 void swap(array&) noexcept {} // Meow is no-op swappable. + + CONSTEXPR17 iterator begin() noexcept { + return nullptr; + } + CONSTEXPR17 const_iterator begin() const noexcept { + return nullptr; + } + CONSTEXPR17 iterator end() noexcept { + return nullptr; + } + CONSTEXPR17 const_iterator end() const noexcept { + return nullptr; + } + + CONSTEXPR17 reverse_iterator rbegin() noexcept { + return reverse_iterator{end()}; + } + CONSTEXPR17 const_reverse_iterator rbegin() const noexcept { + return const_reverse_iterator{end()}; + } + CONSTEXPR17 reverse_iterator rend() noexcept { + return reverse_iterator{begin()}; + } + CONSTEXPR17 const_reverse_iterator rend() const noexcept { + return const_reverse_iterator{begin()}; + } + + CONSTEXPR17 const_iterator cbegin() const noexcept { + return nullptr; + } + CONSTEXPR17 const_iterator cend() const noexcept { + return nullptr; + } + CONSTEXPR17 const_reverse_iterator crbegin() const noexcept { + return const_reverse_iterator{end()}; + } + CONSTEXPR17 const_reverse_iterator crend() const noexcept { + return const_reverse_iterator{begin()}; + } + + NODISCARD20 constexpr bool empty() const noexcept { + return true; + } + constexpr size_type size() const noexcept { + return 0; + } + constexpr size_type max_size() const noexcept { + return 0; + } + + // Perhaps these functions of array shouldn't be constexpr as an invocation is always throwing or UB. + reference operator[](size_type) { + abort(); // UB + } + const_reference operator[](size_type) const { + abort(); // UB + } + reference at(size_type) { + throw out_of_range{"bad array access"}; + } + const_reference at(size_type) const { + throw out_of_range{"bad array access"}; + } + reference front() { + abort(); // UB + } + const_reference front() const { + abort(); // UB + } + reference back() { + abort(); // UB + } + const_reference back() const { + abort(); // UB + } + + CONSTEXPR17 pointer data() noexcept { + return nullptr; + } + CONSTEXPR17 const_pointer data() const noexcept { + return nullptr; + } + + unsigned char dummy_[1]; +}; + +constexpr bool test_array_get() { + array a{}; + const auto& c = a; + + STATIC_ASSERT(is_same_v(a)), Meow&>); + STATIC_ASSERT(is_same_v(c)), const Meow&>); + STATIC_ASSERT(is_same_v(move(a))), Meow&&>); + STATIC_ASSERT(is_same_v(move(c))), const Meow&&>); + + assert(get<0>(a) == Meow{}); + assert(get<0>(c) == Meow{}); + assert(get<0>(move(a)) == Meow{}); + assert(get<0>(move(c)) == Meow{}); + + return true; +} + +CONSTEXPR20 bool test_array_comparison() { + using A0 = array; + assert(A0{} == A0{}); + assert(!(A0{} != A0{})); + assert(!(A0{} < A0{})); + assert(!(A0{} > A0{})); + assert(A0{} <= A0{}); + assert(A0{} >= A0{}); +#if _HAS_CXX20 && defined(__cpp_lib_concepts) // TRANSITION, GH-395 + assert(A0{} <=> A0{} == strong_ordering::equal); +#endif // _HAS_CXX20 && defined(__cpp_lib_concepts) + + using A1 = array; + assert(A1{} == A1{}); + assert(!(A1{} != A1{})); + assert(!(A1{} < A1{})); + assert(!(A1{} > A1{})); + assert(A1{} <= A1{}); + assert(A1{} >= A1{}); +#if _HAS_CXX20 && defined(__cpp_lib_concepts) // TRANSITION, GH-395 + assert(A1{} <=> A1{} == strong_ordering::equal); +#endif // _HAS_CXX20 && defined(__cpp_lib_concepts) + + return true; +} + +int main() { + test_array_get(); + STATIC_ASSERT(test_array_get()); + test_array_comparison(); +#if _HAS_CXX20 + static_assert(test_array_comparison()); +#endif // _HAS_CXX20 +}