diff --git a/stl/inc/vector b/stl/inc/vector index c703d95aca..b03e0348dc 100644 --- a/stl/inc/vector +++ b/stl/inc/vector @@ -169,7 +169,7 @@ public: #if _HAS_CXX20 _NODISCARD constexpr strong_ordering operator<=>(const _Vector_const_iterator& _Right) const noexcept { _Compat(_Right); - return _Unfancy(_Ptr) <=> _Unfancy(_Right._Ptr); + return _STD _Unfancy_maybe_null(_Ptr) <=> _STD _Unfancy_maybe_null(_Right._Ptr); } #else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv _NODISCARD bool operator!=(const _Vector_const_iterator& _Right) const noexcept { @@ -214,11 +214,11 @@ public: using _Prevent_inheriting_unwrap = _Vector_const_iterator; _NODISCARD _CONSTEXPR20 const value_type* _Unwrapped() const noexcept { - return _Unfancy(_Ptr); + return _STD _Unfancy_maybe_null(_Ptr); } _CONSTEXPR20 void _Seek_to(const value_type* _It) noexcept { - _Ptr = _Refancy<_Tptr>(const_cast(_It)); + _Ptr = _STD _Refancy_maybe_null<_Tptr>(const_cast(_It)); } _Tptr _Ptr; // pointer to element in vector @@ -342,7 +342,7 @@ public: using _Prevent_inheriting_unwrap = _Vector_iterator; _NODISCARD _CONSTEXPR20 value_type* _Unwrapped() const noexcept { - return _Unfancy(this->_Ptr); + return _STD _Unfancy_maybe_null(this->_Ptr); } }; @@ -425,17 +425,6 @@ public: pointer _Myend; // pointer to end of array }; -template -constexpr auto _Unfancy_maybe_null(_Ptrty _Ptr) noexcept { - // converts from a (potentially null) fancy pointer to a plain pointer - return _Ptr ? _STD addressof(*_Ptr) : nullptr; -} - -template -constexpr _Ty* _Unfancy_maybe_null(_Ty* _Ptr) noexcept { // do nothing for plain pointers - return _Ptr; -} - _EXPORT_STD template > class vector { // varying size array of values private: diff --git a/stl/inc/xmemory b/stl/inc/xmemory index 3235bd5702..8e027db3e7 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -36,6 +36,17 @@ _NODISCARD constexpr _Ty* _Unfancy(_Ty* _Ptr) noexcept { // do nothing for plain return _Ptr; } +template +constexpr auto _Unfancy_maybe_null(_Ptrty _Ptr) noexcept { + // converts from a (potentially null) fancy pointer to a plain pointer + return _Ptr ? _STD addressof(*_Ptr) : nullptr; +} + +template +constexpr _Ty* _Unfancy_maybe_null(_Ty* _Ptr) noexcept { // do nothing for plain pointers + return _Ptr; +} + template struct _NODISCARD _Tidy_guard { // class with destructor that calls _Tidy _Ty* _Target; @@ -300,6 +311,16 @@ _CONSTEXPR20 _Pointer _Refancy(_Pointer _Ptr) noexcept { return _Ptr; } +template , int> = 0> +_CONSTEXPR20 _Pointer _Refancy_maybe_null(typename pointer_traits<_Pointer>::element_type* _Ptr) noexcept { + return _Ptr == nullptr ? _Pointer() : pointer_traits<_Pointer>::pointer_to(*_Ptr); +} + +template , int> = 0> +_CONSTEXPR20 _Pointer _Refancy_maybe_null(_Pointer _Ptr) noexcept { + return _Ptr; +} + template _CONSTEXPR20 void _Destroy_range(_NoThrowFwdIt _First, _NoThrowSentinel _Last) noexcept; diff --git a/stl/inc/xstring b/stl/inc/xstring index 6bc2b3d35c..034315462a 100644 --- a/stl/inc/xstring +++ b/stl/inc/xstring @@ -1984,7 +1984,7 @@ public: #if _HAS_CXX20 _NODISCARD constexpr strong_ordering operator<=>(const _String_const_iterator& _Right) const noexcept { _Compat(_Right); - return _Unfancy(_Ptr) <=> _Unfancy(_Right._Ptr); + return _STD _Unfancy_maybe_null(_Ptr) <=> _STD _Unfancy_maybe_null(_Right._Ptr); } #else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv _NODISCARD bool operator!=(const _String_const_iterator& _Right) const noexcept { @@ -2030,11 +2030,11 @@ public: using _Prevent_inheriting_unwrap = _String_const_iterator; _NODISCARD _CONSTEXPR20 const value_type* _Unwrapped() const noexcept { - return _Unfancy(_Ptr); + return _STD _Unfancy_maybe_null(_Ptr); } _CONSTEXPR20 void _Seek_to(const value_type* _It) noexcept { - _Ptr = _Refancy(const_cast(_It)); + _Ptr = _STD _Refancy_maybe_null(const_cast(_It)); } pointer _Ptr; // pointer to element in string @@ -2154,7 +2154,7 @@ public: using _Prevent_inheriting_unwrap = _String_iterator; _NODISCARD _CONSTEXPR20 value_type* _Unwrapped() const noexcept { - return const_cast(_Unfancy(this->_Ptr)); + return const_cast(_STD _Unfancy_maybe_null(this->_Ptr)); } }; diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index 3f938e603a..bef5ae1201 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -1011,14 +1011,6 @@ std/containers/sequences/vector.bool/vector_bool.pass.cpp FAIL std/containers/sequences/vector/iterators.pass.cpp:0 FAIL std/containers/sequences/vector/iterators.pass.cpp:1 FAIL -# Not analyzed. -# error C4854: binding dereferenced null pointer to reference has undefined behavior -# note: while evaluating constexpr function 'std::_Refancy' -std/containers/sequences/vector/vector.erasure/erase.pass.cpp:0 FAIL -std/containers/sequences/vector/vector.erasure/erase.pass.cpp:1 FAIL -std/containers/sequences/vector/vector.erasure/erase_if.pass.cpp:0 FAIL -std/containers/sequences/vector/vector.erasure/erase_if.pass.cpp:1 FAIL - # Not analyzed. Inspecting shift operators for quoted(). std/input.output/iostream.format/quoted.manip/quoted_traits.compile.pass.cpp FAIL diff --git a/tests/std/test.lst b/tests/std/test.lst index ee4fa316ec..dbf2d2a5f9 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -234,6 +234,7 @@ 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\GH_004275_seeking_fancy_iterators tests\LWG2381_num_get_floating_point tests\LWG2597_complex_branch_cut tests\LWG3018_shared_ptr_function diff --git a/tests/std/tests/GH_004275_seeking_fancy_iterators/env.lst b/tests/std/tests/GH_004275_seeking_fancy_iterators/env.lst new file mode 100644 index 0000000000..19f025bd0e --- /dev/null +++ b/tests/std/tests/GH_004275_seeking_fancy_iterators/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_004275_seeking_fancy_iterators/test.cpp b/tests/std/tests/GH_004275_seeking_fancy_iterators/test.cpp new file mode 100644 index 0000000000..5dbd840c2c --- /dev/null +++ b/tests/std/tests/GH_004275_seeking_fancy_iterators/test.cpp @@ -0,0 +1,455 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// some portions of this file are derived from libc++'s test files: +// * support/min_allocator.h + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +template > +class min_pointer; +template +class min_pointer; +template +class min_pointer; +template +class min_pointer; +template +class min_allocator; + +template +class min_pointer { + const void* ptr_; + +public: + min_pointer() = default; + min_pointer(nullptr_t) noexcept : ptr_(nullptr) {} + template + min_pointer(min_pointer p) noexcept : ptr_(p.ptr_) {} + + explicit operator bool() const { + return ptr_ != nullptr; + } + + friend bool operator==(min_pointer x, min_pointer y) { + return x.ptr_ == y.ptr_; + } + friend bool operator!=(min_pointer x, min_pointer y) { + return !(x == y); + } + template + friend class min_pointer; +}; + +template +class min_pointer { + void* ptr_; + +public: + min_pointer() = default; + constexpr min_pointer(nullptr_t) noexcept : ptr_(nullptr) {} + template >> + constexpr min_pointer(min_pointer p) noexcept : ptr_(p.ptr_) {} + + constexpr explicit operator bool() const { + return ptr_ != nullptr; + } + + friend constexpr bool operator==(min_pointer x, min_pointer y) { + return x.ptr_ == y.ptr_; + } + friend constexpr bool operator!=(min_pointer x, min_pointer y) { + return !(x == y); + } + template + friend class min_pointer; +}; + +template +class min_pointer { + T* ptr_; + + constexpr explicit min_pointer(T* p) noexcept : ptr_(p) {} + +public: + min_pointer() = default; + constexpr min_pointer(nullptr_t) noexcept : ptr_(nullptr) {} + constexpr explicit min_pointer(min_pointer p) noexcept : ptr_(static_cast(p.ptr_)) {} + + constexpr explicit operator bool() const { + return ptr_ != nullptr; + } + + using difference_type = ptrdiff_t; + using reference = T&; + using pointer = T*; + using value_type = T; + using iterator_category = random_access_iterator_tag; + + constexpr reference operator*() const { + if (ptr_ == nullptr) { + abort(); + } + return *ptr_; + } + constexpr pointer operator->() const { + return ptr_; + } + + constexpr min_pointer& operator++() { + ++ptr_; + return *this; + } + constexpr min_pointer operator++(int) { + min_pointer tmp(*this); + ++ptr_; + return tmp; + } + + constexpr min_pointer& operator--() { + --ptr_; + return *this; + } + constexpr min_pointer operator--(int) { + min_pointer tmp(*this); + --ptr_; + return tmp; + } + + constexpr min_pointer& operator+=(difference_type n) { + ptr_ += n; + return *this; + } + constexpr min_pointer& operator-=(difference_type n) { + ptr_ -= n; + return *this; + } + + constexpr min_pointer operator+(difference_type n) const { + min_pointer tmp(*this); + tmp += n; + return tmp; + } + + friend constexpr min_pointer operator+(difference_type n, min_pointer x) { + return x + n; + } + + constexpr min_pointer operator-(difference_type n) const { + min_pointer tmp(*this); + tmp -= n; + return tmp; + } + + friend constexpr difference_type operator-(min_pointer x, min_pointer y) { + return x.ptr_ - y.ptr_; + } + + constexpr reference operator[](difference_type n) const { + return ptr_[n]; + } + + friend constexpr bool operator<(min_pointer x, min_pointer y) { + return x.ptr_ < y.ptr_; + } + friend constexpr bool operator>(min_pointer x, min_pointer y) { + return y < x; + } + friend constexpr bool operator<=(min_pointer x, min_pointer y) { + return !(y < x); + } + friend constexpr bool operator>=(min_pointer x, min_pointer y) { + return !(x < y); + } + + static constexpr min_pointer pointer_to(T& t) { + return min_pointer(addressof(t)); + } + + friend constexpr bool operator==(min_pointer x, min_pointer y) { + return x.ptr_ == y.ptr_; + } + friend constexpr bool operator!=(min_pointer x, min_pointer y) { + return !(x == y); + } + template + friend class min_pointer; + template + friend class min_allocator; +}; + +template +class min_pointer { + const T* ptr_; + + constexpr explicit min_pointer(const T* p) : ptr_(p) {} + +public: + min_pointer() = default; + constexpr min_pointer(nullptr_t) : ptr_(nullptr) {} + constexpr min_pointer(min_pointer p) : ptr_(p.ptr_) {} + constexpr explicit min_pointer(min_pointer p) : ptr_(static_cast(p.ptr_)) {} + + constexpr explicit operator bool() const { + return ptr_ != nullptr; + } + + using difference_type = ptrdiff_t; + using reference = const T&; + using pointer = const T*; + using value_type = T; + using iterator_category = random_access_iterator_tag; + + constexpr reference operator*() const { + if (ptr_ == nullptr) { + abort(); + } + return *ptr_; + } + constexpr pointer operator->() const { + return ptr_; + } + + constexpr min_pointer& operator++() { + ++ptr_; + return *this; + } + constexpr min_pointer operator++(int) { + min_pointer tmp(*this); + ++ptr_; + return tmp; + } + + constexpr min_pointer& operator--() { + --ptr_; + return *this; + } + constexpr min_pointer operator--(int) { + min_pointer tmp(*this); + --ptr_; + return tmp; + } + + constexpr min_pointer& operator+=(difference_type n) { + ptr_ += n; + return *this; + } + constexpr min_pointer& operator-=(difference_type n) { + ptr_ -= n; + return *this; + } + + constexpr min_pointer operator+(difference_type n) const { + min_pointer tmp(*this); + tmp += n; + return tmp; + } + + friend constexpr min_pointer operator+(difference_type n, min_pointer x) { + return x + n; + } + + constexpr min_pointer operator-(difference_type n) const { + min_pointer tmp(*this); + tmp -= n; + return tmp; + } + + friend constexpr difference_type operator-(min_pointer x, min_pointer y) { + return x.ptr_ - y.ptr_; + } + + constexpr reference operator[](difference_type n) const { + return ptr_[n]; + } + + friend constexpr bool operator<(min_pointer x, min_pointer y) { + return x.ptr_ < y.ptr_; + } + friend constexpr bool operator>(min_pointer x, min_pointer y) { + return y < x; + } + friend constexpr bool operator<=(min_pointer x, min_pointer y) { + return !(y < x); + } + friend constexpr bool operator>=(min_pointer x, min_pointer y) { + return !(x < y); + } + + static constexpr min_pointer pointer_to(const T& t) { + return min_pointer(addressof(t)); + } + + friend constexpr bool operator==(min_pointer x, min_pointer y) { + return x.ptr_ == y.ptr_; + } + friend constexpr bool operator!=(min_pointer x, min_pointer y) { + return x.ptr_ != y.ptr_; + } + friend constexpr bool operator==(min_pointer x, nullptr_t) { + return x.ptr_ == nullptr; + } + friend constexpr bool operator!=(min_pointer x, nullptr_t) { + return x.ptr_ != nullptr; + } + friend constexpr bool operator==(nullptr_t, min_pointer x) { + return x.ptr_ == nullptr; + } + friend constexpr bool operator!=(nullptr_t, min_pointer x) { + return x.ptr_ != nullptr; + } + template + friend class min_pointer; +}; + +#if _HAS_CXX20 +#define CONSTEXPR20 constexpr +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv +#define CONSTEXPR20 inline +#endif // ^^^ !_HAS_CXX20 ^^^ + +template +class min_allocator { +public: + using value_type = T; + using pointer = min_pointer; + + min_allocator() = default; + template + constexpr min_allocator(min_allocator) noexcept {} + + CONSTEXPR20 pointer allocate(ptrdiff_t n) { + return pointer(allocator().allocate(n)); + } + + CONSTEXPR20 void deallocate(pointer p, ptrdiff_t n) { + allocator().deallocate(p.ptr_, n); + } + + friend constexpr bool operator==(min_allocator, min_allocator) { + return true; + } + friend constexpr bool operator!=(min_allocator x, min_allocator y) { + return !(x == y); + } +}; + +CONSTEXPR20 bool test_seeking_vector_iterators() { + using VIt = vector>::iterator; + assert(find(VIt{}, VIt{}, 0) == VIt{}); + + using VCIt = vector>::const_iterator; + assert(find(VCIt{}, VCIt{}, 0) == VCIt{}); + + return true; +} + +CONSTEXPR20 bool test_seeking_string_iterators() { + using SIt = basic_string, min_allocator>::iterator; + assert(find(SIt{}, SIt{}, '\0') == SIt{}); + + using SCIt = basic_string, min_allocator>::const_iterator; + assert(find(SCIt{}, SCIt{}, '\0') == SCIt{}); + + return true; +} + +CONSTEXPR20 bool test_vector_iterators_ordering() { + using VIt = vector>::iterator; + using VCIt = vector>::const_iterator; + + assert(!(VIt{} < VIt{})); + assert(!(VIt{} > VIt{})); + assert(VIt{} <= VIt{}); + assert(VIt{} >= VIt{}); + + assert(!(VCIt{} < VCIt{})); + assert(!(VCIt{} > VCIt{})); + assert(VCIt{} <= VCIt{}); + assert(VCIt{} >= VCIt{}); + + assert(!(VIt{} < VCIt{})); + assert(!(VIt{} > VCIt{})); + assert(VIt{} <= VCIt{}); + assert(VIt{} >= VCIt{}); + + assert(!(VCIt{} < VIt{})); + assert(!(VCIt{} > VIt{})); + assert(VCIt{} <= VIt{}); + assert(VCIt{} >= VIt{}); + +#if _HAS_CXX20 + assert(VIt{} <=> VIt{} == strong_ordering::equal); + assert(VCIt{} <=> VCIt{} == strong_ordering::equal); + assert(VIt{} <=> VCIt{} == strong_ordering::equal); + assert(VCIt{} <=> VIt{} == strong_ordering::equal); +#endif // _HAS_CXX20 + + return true; +} + +CONSTEXPR20 bool test_string_iterators_ordering() { + using SIt = basic_string, min_allocator>::iterator; + using SCIt = basic_string, min_allocator>::const_iterator; + + assert(!(SIt{} < SIt{})); + assert(!(SIt{} > SIt{})); + assert(SIt{} <= SIt{}); + assert(SIt{} >= SIt{}); + + assert(!(SCIt{} < SCIt{})); + assert(!(SCIt{} > SCIt{})); + assert(SCIt{} <= SCIt{}); + assert(SCIt{} >= SCIt{}); + + assert(!(SIt{} < SCIt{})); + assert(!(SIt{} > SCIt{})); + assert(SIt{} <= SCIt{}); + assert(SIt{} >= SCIt{}); + + assert(!(SCIt{} < SIt{})); + assert(!(SCIt{} > SIt{})); + assert(SCIt{} <= SIt{}); + assert(SCIt{} >= SIt{}); + +#if _HAS_CXX20 + assert(SIt{} <=> SIt{} == strong_ordering::equal); + assert(SCIt{} <=> SCIt{} == strong_ordering::equal); + assert(SIt{} <=> SCIt{} == strong_ordering::equal); + assert(SCIt{} <=> SIt{} == strong_ordering::equal); +#endif // _HAS_CXX20 + + return true; +} + +#if _HAS_CXX20 +static_assert(test_seeking_vector_iterators()); +static_assert(test_seeking_string_iterators()); +static_assert(test_vector_iterators_ordering()); +static_assert(test_string_iterators_ordering()); +#endif // _HAS_CXX20 + +int main() { + test_seeking_vector_iterators(); + test_seeking_string_iterators(); + test_vector_iterators_ordering(); + test_string_iterators_ordering(); +}