Skip to content

Commit

Permalink
<ranges>: Fix misused list-initializations (#3493)
Browse files Browse the repository at this point in the history
Co-authored-by: Stephan T. Lavavej <[email protected]>
  • Loading branch information
JMazurkiewicz and StephanTLavavej authored Feb 26, 2023
1 parent 165e96f commit 53e2da2
Show file tree
Hide file tree
Showing 8 changed files with 147 additions and 18 deletions.
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 @@ -2393,12 +2393,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 @@ -3707,7 +3705,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 @@ -4033,7 +4031,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 @@ -4117,7 +4115,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 @@ -4307,7 +4305,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 @@ -5558,7 +5556,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
17 changes: 11 additions & 6 deletions tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,7 @@
#include <utility>
#include <vector>

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

namespace ranges = std::ranges;

template <class>
inline constexpr bool always_false = false;
#include <range_algorithm_support.hpp>

template <class T>
using reference_to = T&;
Expand Down Expand Up @@ -3448,6 +3443,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
20 changes: 20 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,26 @@ constexpr void instantiation_test() {
#endif // TEST_EVERYTHING
}

// GH-3014 "<ranges>: list-initialization is misused"
void test_gh_3014() { // COMPILE-ONLY
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

0 comments on commit 53e2da2

Please sign in to comment.