Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

<ranges>: Fix misused list-initializations #3493

Merged
merged 9 commits into from
Feb 26, 2023
4 changes: 2 additions & 2 deletions stl/inc/iterator
Original file line number Diff line number Diff line change
Expand Up @@ -187,13 +187,13 @@ public:
constexpr move_sentinel() = default;

constexpr explicit move_sentinel(_Se _Val) noexcept(is_nothrow_move_constructible_v<_Se>) // strengthened
: _Last{_STD move(_Val)} {}
: _Last(_STD move(_Val)) {}

template <class _Se2>
requires convertible_to<const _Se2&, _Se>
constexpr move_sentinel(const move_sentinel<_Se2>& _Val) noexcept(
is_nothrow_constructible_v<_Se, const _Se2&>) // strengthened
: _Last{_Val._Get_last()} {}
: _Last(_Val._Get_last()) {}

template <class _Se2>
requires assignable_from<_Se&, const _Se2&>
Expand Down
18 changes: 8 additions & 10 deletions stl/inc/ranges
Original file line number Diff line number Diff line change
Expand Up @@ -2380,12 +2380,10 @@ namespace ranges {
#endif // _ITERATOR_DEBUG_LEVEL != 0
}

// clang-format off
constexpr _Iterator(_Iterator<!_Const> _It)
noexcept(is_nothrow_constructible_v<iterator_t<_Base>, iterator_t<_Vw>>) // strengthened
constexpr _Iterator(_Iterator<!_Const> _It) noexcept(
is_nothrow_constructible_v<iterator_t<_Base>, iterator_t<_Vw>>) // strengthened
requires _Const && convertible_to<iterator_t<_Vw>, iterator_t<_Base>>
: _Current{_STD move(_It._Current)}, _Parent{_It._Parent} {}
// clang-format on
: _Current(_STD move(_It._Current)), _Parent(_It._Parent) {}

_NODISCARD constexpr const iterator_t<_Base>& base() const& noexcept {
return _Current;
Expand Down Expand Up @@ -3694,7 +3692,7 @@ namespace ranges {
constexpr _Iterator(_Iterator<!_Const> _It)
requires _Const && convertible_to<iterator_t<_Vw>, _OuterIter>
&& convertible_to<iterator_t<_InnerRng<false>>, _InnerIter>
: _Outer{_STD move(_It._Outer)}, _Inner{_STD move(_It._Inner)}, _Parent{_It._Parent} {}
: _Outer(_STD move(_It._Outer)), _Inner(_STD move(_It._Inner)), _Parent(_It._Parent) {}

_NODISCARD constexpr decltype(auto) operator*() const noexcept(noexcept(**_Inner)) /* strengthened */ {
#if _ITERATOR_DEBUG_LEVEL != 0
Expand Down Expand Up @@ -4020,7 +4018,7 @@ namespace ranges {
_Variantish<_PatternIter, _InnerIter> _Inner_it{};

constexpr _Iterator(_Parent_t& _Parent_, iterator_t<_Base> _Outer_)
: _Parent{_STD addressof(_Parent_)}, _Outer_it{_STD move(_Outer_)} {
: _Parent(_STD addressof(_Parent_)), _Outer_it(_STD move(_Outer_)) {
if (_Outer_it != _RANGES end(_Parent->_Range)) {
auto&& _Inner = _Update_inner();
_Inner_it._Emplace_second(_RANGES begin(_Inner));
Expand Down Expand Up @@ -4104,7 +4102,7 @@ namespace ranges {
&& convertible_to<iterator_t<_Vw>, _OuterIter> //
&& convertible_to<iterator_t<_InnerRng<false>>, _InnerIter> //
&& convertible_to<iterator_t<_Pat>, _PatternIter> //
: _Outer_it{_STD move(_It._Outer_it)}, _Parent{_It._Parent} {
: _Parent(_It._Parent), _Outer_it(_STD move(_It._Outer_it)) {
switch (_It._Inner_it._Contains) {
case _Variantish_state::_Holds_first:
_Inner_it._Emplace_first(_STD move(_It._Inner_it._First));
Expand Down Expand Up @@ -4294,7 +4292,7 @@ namespace ranges {
constexpr _Sentinel(_Sentinel<!_Const> _Se) noexcept(
is_nothrow_constructible_v<sentinel_t<_Base>, sentinel_t<_Vw>>) // strengthened
requires _Const && convertible_to<sentinel_t<_Vw>, sentinel_t<_Base>>
: _Last{_STD move(_Se._Last)} {}
: _Last(_STD move(_Se._Last)) {}

template <bool _OtherConst>
requires sentinel_for<sentinel_t<_Base>, iterator_t<_Maybe_const<_OtherConst, _Vw>>>
Expand Down Expand Up @@ -5541,7 +5539,7 @@ namespace ranges {
constexpr _Iterator(_Iterator<!_Const> _It) noexcept(
is_nothrow_constructible_v<iterator_t<_Base>, iterator_t<_Vw>>) // strengthened
requires _Const && convertible_to<iterator_t<_Vw>, iterator_t<_Base>>
: _Current{_STD move(_It._Current)} {}
: _Current(_STD move(_It._Current)) {}

_NODISCARD constexpr const iterator_t<_Base>& base() const& noexcept {
return _Current;
Expand Down
31 changes: 31 additions & 0 deletions tests/std/include/range_algorithm_support.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,37 @@ struct std::basic_common_reference<::test::proxy_reference<Cat1, Elem1>, ::test:
};

namespace test {
template <class T>
struct init_list_not_constructible_sentinel {
init_list_not_constructible_sentinel() = default;
init_list_not_constructible_sentinel(T*) {}

template <class U>
init_list_not_constructible_sentinel(std::initializer_list<U>) = delete;
};

template <class T>
struct init_list_not_constructible_iterator {
using difference_type = int;
using value_type = T;

init_list_not_constructible_iterator() = default;
init_list_not_constructible_iterator(T*) {}

template <class U>
init_list_not_constructible_iterator(std::initializer_list<U>) = delete;

T& operator*() const; // not defined
init_list_not_constructible_iterator& operator++(); // not defined
init_list_not_constructible_iterator operator++(int); // not defined

bool operator==(init_list_not_constructible_sentinel<T>) const; // not defined
};

static_assert(std::input_iterator<init_list_not_constructible_iterator<int>>);
static_assert(
std::sentinel_for<init_list_not_constructible_sentinel<int>, init_list_not_constructible_iterator<int>>);

template <class Category, class Element,
// Model sized_sentinel_for along with sentinel?
CanDifference Diff = CanDifference{derived_from<Category, random>},
Expand Down
15 changes: 12 additions & 3 deletions tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,12 @@
#include <utility>
#include <vector>

#include <range_algorithm_support.hpp>

#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__)

namespace ranges = std::ranges;
StephanTLavavej marked this conversation as resolved.
Show resolved Hide resolved

template <class>
inline constexpr bool always_false = false;

template <class T>
using reference_to = T&;
template <class T>
Expand Down Expand Up @@ -3448,6 +3447,16 @@ namespace move_iterator_test {
STATIC_ASSERT(!three_way_comparable<move_iterator<simple_random_iter<sentinel_base>>,
move_sentinel<std::default_sentinel_t>>);

// GH-3014 "<ranges>: list-initialization is misused"
void test_gh_3014() { // COMPILE-ONLY
using S = test::init_list_not_constructible_sentinel<int>;
S s;
[[maybe_unused]] move_sentinel<S> y{s}; // Check 'move_sentinel(S s)'

move_sentinel<int*> s2;
[[maybe_unused]] move_sentinel<S> z{s2}; // Check 'move_sentinel(const move_sentinel<S2>& s2)'
}

constexpr bool test() {
// Validate iter_move
int count = 0;
Expand Down
21 changes: 21 additions & 0 deletions tests/std/tests/P0896R4_views_elements/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,27 @@ constexpr void instantiation_test() {
#endif // TEST_EVERYTHING
}

// GH-3014 "<ranges>: list-initialization is misused"
void test_gh_3014() { // COMPILE-ONLY
using P = pair<int, int>;
StephanTLavavej marked this conversation as resolved.
Show resolved Hide resolved
struct InRange {
P* begin() {
return nullptr;
}

test::init_list_not_constructible_iterator<P> begin() const {
return nullptr;
}

unreachable_sentinel_t end() const {
return {};
}
};

auto r = InRange{} | views::elements<0>;
[[maybe_unused]] decltype(as_const(r).begin()) i = r.begin(); // Check 'iterator(iterator<!Const> i)'
}

int main() {
{ // Validate copyable views
constexpr span<const P> s{some_pairs};
Expand Down
20 changes: 20 additions & 0 deletions tests/std/tests/P0896R4_views_join/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,26 @@ void test_non_trivially_destructible_type() { // COMPILE-ONLY
auto r2 = views::empty<Inner> | views::transform([](Inner& r) { return r; }) | views::join;
}

// GH-3014 "<ranges>: list-initialization is misused"
void test_gh_3014() { // COMPILE-ONLY
struct InRange {
string* begin() {
return nullptr;
}

test::init_list_not_constructible_iterator<string> begin() const {
return nullptr;
}

unreachable_sentinel_t end() const {
return {};
}
};

auto r = InRange{} | views::join;
[[maybe_unused]] decltype(as_const(r).begin()) i = r.begin(); // Check 'iterator(iterator<!Const> i)'
}

int main() {
// Validate views
constexpr string_view expected = "Hello World!"sv;
Expand Down
20 changes: 20 additions & 0 deletions tests/std/tests/P0896R4_views_transform/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,26 @@ void test_gh_1709() {
}
}

// GH-3014 "<ranges>: list-initialization is misused"
void test_gh_3014() { // COMPILE-ONLY
struct InRange {
int* begin() {
return nullptr;
}

test::init_list_not_constructible_iterator<int> begin() const {
return nullptr;
}

unreachable_sentinel_t end() const {
return {};
}
};

auto r = InRange{} | views::transform(identity{});
[[maybe_unused]] decltype(as_const(r).begin()) i = r.begin(); // Check 'iterator(iterator<!Const> i)'
}

int main() {
{ // Validate copyable views
constexpr span<const int> s{some_ints};
Expand Down
35 changes: 35 additions & 0 deletions tests/std/tests/P2441R2_views_join_with/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,41 @@ void test_valueless_iterator() {
}
}

// GH-3014 "<ranges>: list-initialization is misused"
struct FakeStr {
const char* begin() {
return nullptr;
}

unreachable_sentinel_t end() {
return {};
}
};

void test_gh_3014() { // COMPILE-ONLY
struct InRange {
FakeStr* begin() {
return nullptr;
}

test::init_list_not_constructible_iterator<FakeStr> begin() const {
return nullptr;
}

FakeStr* end() {
return nullptr;
}

test::init_list_not_constructible_sentinel<FakeStr> end() const {
return nullptr;
}
};

auto r = InRange{} | views::join_with('-');
[[maybe_unused]] decltype(as_const(r).begin()) i = r.begin(); // Check 'iterator(iterator<!Const> i)'
[[maybe_unused]] decltype(as_const(r).end()) s = r.end(); // Check 'sentinel(sentinel<!Const> s)'
}

int main() {
{
auto filtered_and_joined =
Expand Down