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

<generator>: Test generator::iterator #4574

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions tests/std/include/test_generator_support.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#pragma once

#include <concepts>
#include <cstddef>
#include <memory>
#include <memory_resource>
#include <new>
#include <type_traits>

template <class T, class AlwaysEqual = std::true_type, std::signed_integral DifferenceType = std::ptrdiff_t>
class TestAllocator : public std::allocator<T> {
public:
using value_type = T;
using is_always_equal = AlwaysEqual;
using difference_type = DifferenceType;
using size_type = std::make_unsigned_t<difference_type>;

TestAllocator() = default;

template <class U>
TestAllocator(const TestAllocator<U, AlwaysEqual, DifferenceType>&) {}

T* allocate(const size_type s) {
return static_cast<T*>(::operator new(static_cast<size_t>(s * sizeof(T)), std::align_val_t{alignof(T)}));
}

void deallocate(T* const p, size_type s) {
::operator delete(p, s * sizeof(T), std::align_val_t{alignof(T)});
}

operator std::pmr::polymorphic_allocator<void>() const {
return {};
}

bool operator==(const TestAllocator&) const = default;
};

struct MoveOnly {
MoveOnly() = default;
MoveOnly(const MoveOnly&) = delete;
MoveOnly& operator=(const MoveOnly&) = delete;
MoveOnly(MoveOnly&&) = default;
MoveOnly& operator=(MoveOnly&&) = default;
};

static_assert(std::movable<MoveOnly>);
static_assert(!std::copyable<MoveOnly>);

struct Immovable {
Immovable() = default;
Immovable(Immovable&&) = delete;
Immovable& operator=(Immovable&&) = delete;
};

static_assert(!std::movable<Immovable>);

template <class T>
class Proxy {
public:
Proxy(const T&) {}
};

template <std::equality_comparable T>
class Proxy<T> {
public:
Proxy(const T& _value_) : value(_value_) {}

bool operator==(const Proxy& other) const {
return value == other.value;
}

bool operator==(const T& x) const {
return value == x;
}

private:
const T& value;
};
1 change: 1 addition & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,7 @@ tests\P2474R2_views_repeat_death
tests\P2494R2_move_only_range_adaptors
tests\P2502R2_generator
tests\P2502R2_generator_death
tests\P2502R2_generator_iterator
tests\P2502R2_generator_promise
tests\P2505R5_monadic_functions_for_std_expected
tests\P2510R3_text_formatting_pointers
Expand Down
1 change: 0 additions & 1 deletion tests/std/tests/P2502R2_generator/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ constexpr bool static_checks() {
// Non-portable size checks
static_assert(sizeof(G) == sizeof(void*));
static_assert(sizeof(typename G::promise_type) == 3 * sizeof(void*));
static_assert(sizeof(ranges::iterator_t<G>) == sizeof(void*));

return true;
}
Expand Down
4 changes: 4 additions & 0 deletions tests/std/tests/P2502R2_generator_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 ..\usual_latest_matrix.lst
177 changes: 177 additions & 0 deletions tests/std/tests/P2502R2_generator_iterator/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <cassert>
#include <concepts>
#include <generator>
#include <memory>
#include <memory_resource>
#include <ranges>
#include <type_traits>

#include "test_generator_support.hpp"

using namespace std;

template <class Ref, class V, class Alloc>
generator<Ref, V, Alloc> generate_zero() {
co_return;
}

template <class Ref, class V>
using gen_value_t = conditional_t<is_void_v<V>, remove_cvref_t<Ref>, V>;

template <class Ref, class V>
using gen_reference_t = conditional_t<is_void_v<V>, Ref&&, Ref>;

template <class Ref, class V, class Alloc, class ValueType = gen_value_t<Ref, V>>
requires default_initializable<ValueType>
&& (same_as<remove_cvref_t<Ref>, ValueType> || constructible_from<remove_cvref_t<Ref>, ValueType&>)
generator<Ref, V, Alloc> generate_one() {
if constexpr (same_as<remove_cvref_t<Ref>, ValueType>) {
// non-proxy reference case
if constexpr (is_reference_v<Ref>) {
remove_reference_t<Ref> val{};
co_yield static_cast<Ref>(val);
} else {
co_yield ValueType{};
}
} else {
ValueType val{};
// proxy reference case
if constexpr (is_reference_v<Ref>) {
// yielding a non-prvalue proxy reference is super weird, but not forbidden
remove_reference_t<Ref> ref{val};
co_yield static_cast<Ref>(ref);
} else {
co_yield Ref{val};
}
}
}

#if !(defined(__clang__) && defined(_M_IX86)) // TRANSITION, LLVM-56507
template <class Ref, class V, class Alloc>
generator<Ref, V, Alloc> generate_one_recursively() {
co_yield ranges::elements_of{generate_zero<Ref, V, Alloc>()};
co_yield ranges::elements_of{generate_one<Ref, V, Alloc>()};
co_yield ranges::elements_of{generate_zero<Ref, V, Alloc>()};
}
#endif // ^^^ no workaround ^^^

template <class Ref, class V = void, class Alloc = void>
void test_one() {
using Gen = generator<Ref, V, Alloc>;
using Iter = ranges::iterator_t<Gen>;
static_assert(input_iterator<Iter>);
static_assert(sizeof(Iter) == sizeof(void*)); // NB: implementation defined

// Test member types
static_assert(same_as<typename Iter::value_type, gen_value_t<Ref, V>>);
static_assert(same_as<typename Iter::difference_type, ptrdiff_t>);

// Test copying functions
static_assert(!is_copy_constructible_v<Iter>);
static_assert(!is_copy_assignable_v<Iter>);

{ // Test move constructor
Gen g = generate_zero<Ref, V, Alloc>();
Iter i = g.begin();
Iter j = move(i);
assert(j == default_sentinel);

static_assert(is_nothrow_move_constructible_v<Iter>);
}

{ // Test move assignment operator
Gen g1 = generate_one<Ref, V, Alloc>();
Iter i = g1.begin();
Gen g2 = generate_zero<Ref, V, Alloc>();
Iter j = g2.begin();

same_as<Iter&> decltype(auto) k = (i = move(j));
assert(&k == &i);
assert(k == default_sentinel);
static_assert(is_nothrow_move_assignable_v<Iter>);
}

{ // Test indirection
auto g = generate_one<Ref, V, Alloc>();
auto i = g.begin();

same_as<gen_reference_t<Ref, V>> decltype(auto) r = *i;

using ValueType = gen_value_t<Ref, V>;
if constexpr (default_initializable<ValueType> && equality_comparable<ValueType>) {
assert(r == ValueType{});
}
}

#if !(defined(__clang__) && defined(_M_IX86)) // TRANSITION, LLVM-56507
{ // Test pre-incrementation
auto g = generate_one_recursively<Ref, V, Alloc>();
auto i = g.begin();

same_as<Iter&> decltype(auto) i_ref = ++i;
assert(&i_ref == &i);
assert(i_ref == default_sentinel);
}

{ // Test post-incrementation
auto g = generate_one_recursively<Ref, V, Alloc>();
auto i = g.begin();
i++;
assert(i == default_sentinel);

static_assert(is_void_v<decltype(i++)>);
}
#endif // ^^^ no workaround ^^^

{ // Test equal operator
auto g1 = generate_one<Ref, V, Alloc>();
auto i = g1.begin();
auto g2 = generate_zero<Ref, V, Alloc>();
auto j = g2.begin();

same_as<bool> decltype(auto) b1 = i == default_sentinel;
assert(!b1);

same_as<bool> decltype(auto) b2 = default_sentinel == j;
assert(b2);

same_as<bool> decltype(auto) b3 = i != default_sentinel;
assert(b3);

same_as<bool> decltype(auto) b4 = default_sentinel != j;
assert(!b4);
}
}

template <class Ref, class V = void>
void test_with_allocator() {
test_one<Ref, V>();
test_one<Ref, V, allocator<void>>();
test_one<Ref, V, pmr::polymorphic_allocator<void>>();
test_one<Ref, V, TestAllocator<void>>();
}

template <class T>
void test_with_type() {
test_with_allocator<T>();
test_with_allocator<T&>();
test_with_allocator<const T&>();
test_with_allocator<T&&>();
test_with_allocator<const T&&>();

test_with_allocator<Proxy<T>, T>();
test_with_allocator<Proxy<T>&, T>();
test_with_allocator<const Proxy<T>&, T>();
test_with_allocator<Proxy<T>&&, T>();
test_with_allocator<const Proxy<T>&&, T>();
}

int main() {
test_with_type<int>();
test_with_type<float>();
test_with_type<string>();
test_with_type<MoveOnly>();
}
51 changes: 1 addition & 50 deletions tests/std/tests/P2502R2_generator_promise/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,37 +18,10 @@
#include <vector>

#include "range_algorithm_support.hpp"
#include "test_generator_support.hpp"

using namespace std;

template <class T, class AlwaysEqual = true_type, signed_integral DifferenceType = ptrdiff_t>
class TestAllocator : public allocator<T> {
public:
using value_type = T;
using is_always_equal = AlwaysEqual;
using difference_type = DifferenceType;
using size_type = make_unsigned_t<difference_type>;

TestAllocator() = default;

template <class U>
TestAllocator(const TestAllocator<U, AlwaysEqual, DifferenceType>&) {}

T* allocate(const size_type s) {
return static_cast<T*>(::operator new(static_cast<size_t>(s * sizeof(T)), align_val_t{alignof(T)}));
}

void deallocate(T* const p, size_type s) {
::operator delete(p, s * sizeof(T), align_val_t{alignof(T)});
}

operator pmr::polymorphic_allocator<void>() const {
return {};
}

bool operator==(const TestAllocator&) const = default;
};

template <class Promise, class... Args>
concept HasOperatorNew = requires(Args&&... args) {
{ Promise::operator new(forward<Args>(args)...) } -> same_as<void*>;
Expand All @@ -62,28 +35,6 @@ struct generator_allocator<generator<Ref, V, Alloc>> {
using type = Alloc;
};

struct MoveOnly {
MoveOnly(const MoveOnly&) = delete;
MoveOnly& operator=(const MoveOnly&) = delete;
MoveOnly(MoveOnly&&) = default;
MoveOnly& operator=(MoveOnly&&) = default;
};

static_assert(movable<MoveOnly>);
static_assert(!copyable<MoveOnly>);

struct Immovable {
Immovable(Immovable&&) = delete;
Immovable& operator=(Immovable&&) = delete;
};

static_assert(!movable<Immovable>);

template <class T>
struct Proxy {
Proxy(const T&); // not defined
};

template <class Gen, class Range>
requires convertible_to<ranges::range_reference_t<Range&>, typename Gen::yielded>
void test_yield_elements_of_range(typename Gen::promise_type& p) {
Expand Down