Skip to content

Commit

Permalink
P2278R4: cbegin should always return a constant iterator ("Iterator…
Browse files Browse the repository at this point in the history
…s" section only) (#3043)

Co-authored-by: Casey Carter <[email protected]>
Co-authored-by: Stephan T. Lavavej <[email protected]>
  • Loading branch information
3 people authored Oct 24, 2022
1 parent 18ecfbc commit bb0938b
Show file tree
Hide file tree
Showing 9 changed files with 844 additions and 1 deletion.
8 changes: 8 additions & 0 deletions stl/debugger/STL.natvis
Original file line number Diff line number Diff line change
Expand Up @@ -1404,6 +1404,14 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
</Type>


<Type Name="std::basic_const_iterator&lt;*&gt;">
<DisplayString>basic_const_iterator {_Current}</DisplayString>
<Expand>
<Item Name="current">_Current</Item>
</Expand>
</Type>


<Type Name="std::complex&lt;*&gt;">
<DisplayString Condition="(_Val[1] &lt; 0) &amp;&amp; (_Val[0] == 0)">-i*{-_Val[1]}</DisplayString>
<DisplayString Condition="(_Val[1] &lt; 0) &amp;&amp; (_Val[0] != 0)">{_Val[0]}-i*{-_Val[1]}</DisplayString>
Expand Down
339 changes: 339 additions & 0 deletions stl/inc/xutility
Original file line number Diff line number Diff line change
Expand Up @@ -1735,6 +1735,345 @@ _NODISCARD constexpr const _Elem* data(initializer_list<_Elem> _Ilist) noexcept
template <class _Ty1, class _Ty2>
concept _Not_same_as = (!same_as<remove_cvref_t<_Ty1>, remove_cvref_t<_Ty2>>);

#if _HAS_CXX23
_EXPORT_STD template <indirectly_readable _Ty>
using iter_const_reference_t = common_reference_t<const iter_value_t<_Ty>&&, iter_reference_t<_Ty>>;

template <class _Ty>
concept _Constant_iterator = input_iterator<_Ty> && same_as<iter_const_reference_t<_Ty>, iter_reference_t<_Ty>>;

_EXPORT_STD template <input_iterator _Iter>
class basic_const_iterator;

_EXPORT_STD template <input_iterator _Iter>
using const_iterator = conditional_t<_Constant_iterator<_Iter>, _Iter, basic_const_iterator<_Iter>>;

template <class _Sent>
struct _Const_sentinel {
using type = _Sent;
};

template <input_iterator _Sent>
struct _Const_sentinel<_Sent> {
using type = const_iterator<_Sent>;
};

_EXPORT_STD template <semiregular _Sent>
using const_sentinel = typename _Const_sentinel<_Sent>::type;

// clang-format off
template <class _Ty>
concept _Not_a_const_iterator = !_Is_specialization_v<_Ty, basic_const_iterator>;
// clang-format on

template <class>
struct _Basic_const_iterator_category {};

template <forward_iterator _Iter>
struct _Basic_const_iterator_category<_Iter> {
using iterator_category = typename iterator_traits<_Iter>::iterator_category;
};

// TRANSITION, LLVM-55945: These are distinct concepts as a workaround
template <class _Ty, class _Iter>
concept _Bci_order =
_Not_same_as<_Ty, basic_const_iterator<_Iter>> && random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Ty>;

template <class _Ty, class _Iter>
concept _Bci_order_3way = _Bci_order<_Ty, _Iter> && three_way_comparable_with<_Iter, _Ty>;

template <class _Ty, class _Iter>
concept _Not_bci_order =
_Not_a_const_iterator<_Ty> && random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Ty>;

_EXPORT_STD template <input_iterator _Iter>
class basic_const_iterator : public _Basic_const_iterator_category<_Iter> {
private:
/* [[no_unique_address]] */ _Iter _Current{};

using _Reference = iter_const_reference_t<_Iter>;

_NODISCARD static _CONSTEVAL auto _Get_iter_concept() noexcept {
if constexpr (contiguous_iterator<_Iter>) {
return contiguous_iterator_tag{};
} else if constexpr (random_access_iterator<_Iter>) {
return random_access_iterator_tag{};
} else if constexpr (bidirectional_iterator<_Iter>) {
return bidirectional_iterator_tag{};
} else if constexpr (forward_iterator<_Iter>) {
return forward_iterator_tag{};
} else {
return input_iterator_tag{};
}
}

public:
using iterator_concept = decltype(_Get_iter_concept());
using value_type = iter_value_t<_Iter>;
using difference_type = iter_difference_t<_Iter>;

// clang-format off
basic_const_iterator() requires default_initializable<_Iter> = default;
// clang-format on

constexpr basic_const_iterator(_Iter _Current_) noexcept(is_nothrow_move_constructible_v<_Iter>) // strengthened
: _Current(_STD move(_Current_)) {}

template <convertible_to<_Iter> _Other>
constexpr basic_const_iterator(basic_const_iterator<_Other> _Current_) noexcept(
is_nothrow_constructible_v<_Iter, _Other>) // strengthened
: _Current(_STD move(_Current_._Current)) {}

template <_Not_same_as<basic_const_iterator> _Other>
requires convertible_to<_Other, _Iter>
constexpr basic_const_iterator(_Other&& _Current_) noexcept(
is_nothrow_constructible_v<_Iter, _Other>) // strengthened
: _Current(_STD forward<_Other>(_Current_)) {}

_NODISCARD constexpr const _Iter& base() const& noexcept {
return _Current;
}

_NODISCARD constexpr _Iter base() && noexcept(is_nothrow_move_constructible_v<_Iter>) /* strengthened */ {
return _STD move(_Current);
}

_NODISCARD constexpr _Reference operator*() const
noexcept(noexcept(static_cast<_Reference>(*_Current))) /* strengthened */ {
return static_cast<_Reference>(*_Current);
}

_NODISCARD constexpr const value_type* operator->() const
noexcept(contiguous_iterator<_Iter> || noexcept(*_Current)) /* strengthened */
requires is_lvalue_reference_v<iter_reference_t<_Iter>>
&& same_as<remove_cvref_t<iter_reference_t<_Iter>>, value_type>
{
if constexpr (contiguous_iterator<_Iter>) {
return _STD to_address(_Current);
} else {
return _STD addressof(*_Current);
}
}

constexpr basic_const_iterator& operator++() noexcept(noexcept(++_Current)) /* strengthened */ {
++_Current;
return *this;
}

constexpr void operator++(int) noexcept(noexcept(++_Current)) /* strengthened */ {
++_Current;
}

constexpr basic_const_iterator operator++(int) noexcept(
noexcept(++*this) && is_nothrow_copy_constructible_v<basic_const_iterator>) // strengthened
requires forward_iterator<_Iter>
{
auto _Tmp = *this;
++*this;
return _Tmp;
}

constexpr basic_const_iterator& operator--() noexcept(noexcept(--_Current)) // strengthened
requires bidirectional_iterator<_Iter>
{
--_Current;
return *this;
}

constexpr basic_const_iterator operator--(int) noexcept(
noexcept(--*this) && is_nothrow_copy_constructible_v<basic_const_iterator>) // strengthened
requires bidirectional_iterator<_Iter>
{
auto _Tmp = *this;
--*this;
return _Tmp;
}

constexpr basic_const_iterator& operator+=(const difference_type _Off) noexcept(
noexcept(_Current += _Off)) // strengthened
requires random_access_iterator<_Iter>
{
_Current += _Off;
return *this;
}

constexpr basic_const_iterator& operator-=(const difference_type _Off) noexcept(
noexcept(_Current -= _Off)) // strengthened
requires random_access_iterator<_Iter>
{
_Current -= _Off;
return *this;
}

_NODISCARD constexpr _Reference operator[](const difference_type _Idx) const
noexcept(noexcept(static_cast<_Reference>(_Current[_Idx]))) // strengthened
requires random_access_iterator<_Iter>
{
return static_cast<_Reference>(_Current[_Idx]);
}

template <sentinel_for<_Iter> _Sent>
_NODISCARD constexpr bool operator==(const _Sent& _Se) const // per LWG-3769
noexcept(noexcept(_Fake_copy_init<bool>(_Current == _Se))) /* strengthened */ {
return _Current == _Se;
}

_NODISCARD_FRIEND constexpr bool
operator<(const basic_const_iterator& _Left, const basic_const_iterator& _Right) noexcept(
noexcept(_Fake_copy_init<bool>(_Left._Current < _Right._Current))) // strengthened
requires random_access_iterator<_Iter>
{
return _Left._Current < _Right._Current;
}

_NODISCARD_FRIEND constexpr bool
operator>(const basic_const_iterator& _Left, const basic_const_iterator& _Right) noexcept(
noexcept(_Fake_copy_init<bool>(_Left._Current > _Right._Current))) // strengthened
requires random_access_iterator<_Iter>
{
return _Left._Current > _Right._Current;
}

_NODISCARD_FRIEND constexpr bool
operator<=(const basic_const_iterator& _Left, const basic_const_iterator& _Right) noexcept(
noexcept(_Fake_copy_init<bool>(_Left._Current <= _Right._Current))) // strengthened
requires random_access_iterator<_Iter>
{
return _Left._Current <= _Right._Current;
}

_NODISCARD_FRIEND constexpr bool
operator>=(const basic_const_iterator& _Left, const basic_const_iterator& _Right) noexcept(
noexcept(_Fake_copy_init<bool>(_Left._Current >= _Right._Current))) // strengthened
requires random_access_iterator<_Iter>
{
return _Left._Current >= _Right._Current;
}

_NODISCARD_FRIEND constexpr auto operator<=>(const basic_const_iterator& _Left,
const basic_const_iterator& _Right) noexcept(noexcept(_Left._Current <=> _Right._Current)) // strengthened
requires random_access_iterator<_Iter> && three_way_comparable<_Iter>
{
return _Left._Current <=> _Right._Current;
}

template <_Bci_order<_Iter> _Other>
_NODISCARD_FRIEND constexpr bool operator<(const basic_const_iterator& _Left, const _Other& _Right) noexcept(
noexcept(_Fake_copy_init<bool>(_Left._Current < _Right))) /* strengthened */ {
return _Left._Current < _Right;
}

template <_Bci_order<_Iter> _Other>
_NODISCARD_FRIEND constexpr bool operator>(const basic_const_iterator& _Left, const _Other& _Right) noexcept(
noexcept(_Fake_copy_init<bool>(_Left._Current > _Right))) /* strengthened */ {
return _Left._Current > _Right;
}

template <_Bci_order<_Iter> _Other>
_NODISCARD_FRIEND constexpr bool operator<=(const basic_const_iterator& _Left, const _Other& _Right) noexcept(
noexcept(_Fake_copy_init<bool>(_Left._Current <= _Right))) /* strengthened */ {
return _Left._Current <= _Right;
}

template <_Bci_order<_Iter> _Other>
_NODISCARD_FRIEND constexpr bool operator>=(const basic_const_iterator& _Left, const _Other& _Right) noexcept(
noexcept(_Fake_copy_init<bool>(_Left._Current >= _Right))) /* strengthened */ {
return _Left._Current >= _Right;
}

template <_Not_bci_order<_Iter> _Other>
_NODISCARD_FRIEND constexpr bool operator<(const _Other& _Left, const basic_const_iterator& _Right) noexcept(
noexcept(_Fake_copy_init<bool>(_Left < _Right._Current))) /* strengthened */ {
return _Left < _Right._Current;
}

template <_Not_bci_order<_Iter> _Other>
_NODISCARD_FRIEND constexpr bool operator>(const _Other& _Left, const basic_const_iterator& _Right) noexcept(
noexcept(_Fake_copy_init<bool>(_Left > _Right._Current))) /* strengthened */ {
return _Left > _Right._Current;
}

template <_Not_bci_order<_Iter> _Other>
_NODISCARD_FRIEND constexpr bool operator<=(const _Other& _Left, const basic_const_iterator& _Right) noexcept(
noexcept(_Fake_copy_init<bool>(_Left <= _Right._Current))) /* strengthened */ {
return _Left <= _Right._Current;
}

template <_Not_bci_order<_Iter> _Other>
_NODISCARD_FRIEND constexpr bool operator>=(const _Other& _Left, const basic_const_iterator& _Right) noexcept(
noexcept(_Fake_copy_init<bool>(_Left >= _Right._Current))) /* strengthened */ {
return _Left >= _Right._Current;
}

template <_Bci_order_3way<_Iter> _Other>
_NODISCARD_FRIEND constexpr auto operator<=>(const basic_const_iterator& _Left, const _Other& _Right) noexcept(
noexcept(_Left._Current <=> _Right)) /* strengthened */ {
return _Left._Current <=> _Right;
}

_NODISCARD_FRIEND constexpr basic_const_iterator operator+(const basic_const_iterator& _It,
const difference_type _Off) noexcept(noexcept(basic_const_iterator{_It._Current + _Off})) // strengthened
requires random_access_iterator<_Iter>
{
return basic_const_iterator{_It._Current + _Off};
}

_NODISCARD_FRIEND constexpr basic_const_iterator operator+(const difference_type _Off,
const basic_const_iterator& _It) noexcept(noexcept(basic_const_iterator{_It._Current + _Off})) // strengthened
requires random_access_iterator<_Iter>
{
return basic_const_iterator{_It._Current + _Off};
}

_NODISCARD_FRIEND constexpr basic_const_iterator operator-(const basic_const_iterator& _It,
const difference_type _Off) noexcept(noexcept(basic_const_iterator{_It._Current - _Off})) // strengthened
requires random_access_iterator<_Iter>
{
return basic_const_iterator{_It._Current - _Off};
}

template <sized_sentinel_for<_Iter> _Sent>
_NODISCARD constexpr difference_type operator-(const _Sent& _Se) const // per LWG-3769
noexcept(noexcept(_Current - _Se)) /* strengthened */ {
return _Current - _Se;
}

template <_Not_a_const_iterator _Sent>
requires sized_sentinel_for<_Sent, _Iter>
_NODISCARD_FRIEND constexpr difference_type operator-(const _Sent& _Se, const basic_const_iterator& _It) noexcept(
noexcept(_Se - _It._Current)) /* strengthened */ { // per LWG-3769
return _Se - _It._Current;
}
};

template <class _Ty1, common_with<_Ty1> _Ty2>
struct common_type<basic_const_iterator<_Ty1>, _Ty2> {
using type = basic_const_iterator<common_type_t<_Ty1, _Ty2>>;
};

template <class _Ty1, common_with<_Ty1> _Ty2>
struct common_type<_Ty2, basic_const_iterator<_Ty1>> {
using type = basic_const_iterator<common_type_t<_Ty1, _Ty2>>;
};

template <class _Ty1, common_with<_Ty1> _Ty2>
struct common_type<basic_const_iterator<_Ty1>, basic_const_iterator<_Ty2>> {
using type = basic_const_iterator<common_type_t<_Ty1, _Ty2>>;
};

_EXPORT_STD template <input_iterator _Iter>
_NODISCARD constexpr const_iterator<_Iter> make_const_iterator(_Iter _It) noexcept(
is_nothrow_constructible_v<const_iterator<_Iter>, _Iter&>) /* strengthened */ {
return _It;
}

_EXPORT_STD template <semiregular _Sent>
_NODISCARD constexpr const_sentinel<_Sent> make_const_sentinel(_Sent _Se) noexcept(
is_nothrow_constructible_v<const_sentinel<_Sent>, _Sent&>) /* strengthened */ {
return _Se;
}
#endif // _HAS_CXX23

namespace ranges {
template <class>
inline constexpr bool _Has_complete_elements = false;
Expand Down
2 changes: 2 additions & 0 deletions stl/inc/yvals_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,8 @@
// P2166R1 Prohibiting basic_string And basic_string_view Construction From nullptr
// P2186R2 Removing Garbage Collection Support
// P2273R3 constexpr unique_ptr
// P2278R4 cbegin Should Always Return A Constant Iterator
// ("Iterators" section from the paper only)
// P2291R3 constexpr Integral <charconv>
// P2302R4 ranges::contains, ranges::contains_subrange
// P2321R2 zip
Expand Down
2 changes: 1 addition & 1 deletion tests/std/include/range_algorithm_support.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ namespace test {
template <class Category, class Element,
// Model sized_sentinel_for along with sentinel?
CanDifference Diff = CanDifference{derived_from<Category, random>},
// Model sentinel_for with self (and sized_sentinel_for if Diff; implies copyable)?
// Model sentinel_for with self (and sized_sentinel_for if Diff implies copyable)?
CanCompare Eq = CanCompare{derived_from<Category, fwd>},
// Use a ProxyRef reference type (instead of Element&)?
ProxyRef Proxy = ProxyRef{!derived_from<Category, contiguous>},
Expand Down
2 changes: 2 additions & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,8 @@ tests\P2136R3_invoke_r
tests\P2162R2_std_visit_for_derived_classes_from_variant
tests\P2231R1_complete_constexpr_optional_variant
tests\P2273R3_constexpr_unique_ptr
tests\P2278R4_basic_const_iterator
tests\P2278R4_ranges_const_iterator_machinery
tests\P2302R4_ranges_alg_contains
tests\P2302R4_ranges_alg_contains_subrange
tests\P2321R2_proxy_reference
Expand Down
4 changes: 4 additions & 0 deletions tests/std/tests/P2278R4_basic_const_iterator/env.lst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

RUNALL_INCLUDE ..\concepts_latest_matrix.lst
Loading

0 comments on commit bb0938b

Please sign in to comment.