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

Implement iter_swap CPO #332

Merged
merged 1 commit into from
Aug 23, 2023
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
//===----------------------------------------------------------------------===//
//
// 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
// SPDX-FileCopyrightText: Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14

// template<class I1, class I2>
// concept indirectly_swappable;

#include <cuda/std/iterator>

#include "test_macros.h"

template<class T, class ValueType = T>
struct PointerTo {
using value_type = ValueType;
__host__ __device__ T& operator*() const;
};

static_assert(cuda::std::indirectly_swappable<PointerTo<int>>);
static_assert(cuda::std::indirectly_swappable<PointerTo<int>, PointerTo<int>>);

struct B;

struct A {
__host__ __device__ friend void iter_swap(const PointerTo<A>&, const PointerTo<A>&);
};

// Is indirectly swappable.
struct B {
__host__ __device__ friend void iter_swap(const PointerTo<B>&, const PointerTo<B>&);
__host__ __device__ friend void iter_swap(const PointerTo<A>&, const PointerTo<B>&);
__host__ __device__ friend void iter_swap(const PointerTo<B>&, const PointerTo<A>&);
};

// Valid except ranges::iter_swap(i2, i1).
struct C {
__host__ __device__ friend void iter_swap(const PointerTo<C>&, const PointerTo<C>&);
__host__ __device__ friend void iter_swap(const PointerTo<A>&, const PointerTo<C>&);
__host__ __device__ friend void iter_swap(const PointerTo<C>&, const PointerTo<A>&) = delete;
};

// Valid except ranges::iter_swap(i1, i2).
struct D {
__host__ __device__ friend void iter_swap(const PointerTo<D>&, const PointerTo<D>&);
__host__ __device__ friend void iter_swap(const PointerTo<A>&, const PointerTo<D>&) = delete;
__host__ __device__ friend void iter_swap(const PointerTo<D>&, const PointerTo<A>&);
};

// Valid except ranges::iter_swap(i2, i2).
struct E {
E operator=(const E&) = delete;
__host__ __device__ friend void iter_swap(const PointerTo<E>&, const PointerTo<E>&) = delete;
__host__ __device__ friend void iter_swap(const PointerTo<A>&, const PointerTo<E>&);
__host__ __device__ friend void iter_swap(const PointerTo<E>&, const PointerTo<A>&);
};

struct F {
__host__ __device__ friend void iter_swap(const PointerTo<F>&, const PointerTo<F>&) = delete;
};

// Valid except ranges::iter_swap(i1, i1).
struct G {
__host__ __device__ friend void iter_swap(const PointerTo<G>&, const PointerTo<G>&);
__host__ __device__ friend void iter_swap(const PointerTo<F>&, const PointerTo<G>&);
__host__ __device__ friend void iter_swap(const PointerTo<G>&, const PointerTo<F>&);
};


static_assert( cuda::std::indirectly_swappable<PointerTo<A>, PointerTo<B>>);
static_assert(!cuda::std::indirectly_swappable<PointerTo<A>, PointerTo<C>>);
static_assert(!cuda::std::indirectly_swappable<PointerTo<A>, PointerTo<D>>);
static_assert(!cuda::std::indirectly_swappable<PointerTo<A>, PointerTo<E>>);
static_assert(!cuda::std::indirectly_swappable<PointerTo<A>, PointerTo<G>>);

int main(int, char**)
{
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//===----------------------------------------------------------------------===//
//
// 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
// SPDX-FileCopyrightText: Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17

// template<class I1, class I2>
// concept indirectly_swappable;

#include <cuda/std/iterator>

#include <cuda/std/concepts>

template<class I1, class I2>
requires cuda::std::indirectly_readable<I1> && cuda::std::indirectly_readable<I2>
__host__ __device__ constexpr bool indirectly_swappable_subsumption() {
return false;
}

template<class I1, class I2>
requires cuda::std::indirectly_swappable<I1, I2>
__host__ __device__ constexpr bool indirectly_swappable_subsumption() {
return true;
}

static_assert(indirectly_swappable_subsumption<int*, int*>());

int main(int, char**)
{
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
//===----------------------------------------------------------------------===//
//
// 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
// SPDX-FileCopyrightText: Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14

// template<class I>
// unspecified iter_swap;

#include <cuda/std/iterator>

#include <cuda/std/array>
#include <cuda/std/cassert>

#include "test_iterators.h"
#include "test_macros.h"

#include "../unqualified_lookup_wrapper.h"

using IterSwapT = decltype(cuda::std::ranges::iter_swap);

struct HasIterSwap {
int &value_;
__host__ __device__ constexpr explicit HasIterSwap(int &value) : value_(value) { assert(value == 0); }

__host__ __device__ friend constexpr void iter_swap(HasIterSwap& a, HasIterSwap& b) {
a.value_ = 1;
b.value_ = 1;
}
__host__ __device__ friend constexpr void iter_swap(HasIterSwap& a, int& b) {
a.value_ = 2;
b = 2;
}
};

#ifndef _LIBCUDACXX_COMPILER_NVCC_BELOW_11_3 // nvcc segfaults here
static_assert( cuda::std::is_invocable_v<IterSwapT, HasIterSwap&, HasIterSwap&>);
static_assert( cuda::std::is_invocable_v<IterSwapT, HasIterSwap&, int&>);
static_assert(!cuda::std::is_invocable_v<IterSwapT, int&, HasIterSwap&>);

static_assert( cuda::std::is_invocable_v<IterSwapT&, HasIterSwap&, HasIterSwap&>);
static_assert( cuda::std::is_invocable_v<IterSwapT&, HasIterSwap&, int&>);
static_assert(!cuda::std::is_invocable_v<IterSwapT&, int&, HasIterSwap&>);

static_assert( cuda::std::is_invocable_v<IterSwapT&&, HasIterSwap&, HasIterSwap&>);
static_assert( cuda::std::is_invocable_v<IterSwapT&&, HasIterSwap&, int&>);
static_assert(!cuda::std::is_invocable_v<IterSwapT&&, int&, HasIterSwap&>);
#endif // _LIBCUDACXX_COMPILER_NVCC_BELOW_11_3

struct NodiscardIterSwap {
__host__ __device__ friend _LIBCUDACXX_NODISCARD_EXT int iter_swap(NodiscardIterSwap&, NodiscardIterSwap&) { return 0; }
};

__host__ __device__
void ensureVoidCast(NodiscardIterSwap& a, NodiscardIterSwap& b) { cuda::std::ranges::iter_swap(a, b); }

struct HasRangesSwap {
int &value_;
__host__ __device__ constexpr explicit HasRangesSwap(int &value) : value_(value) { assert(value == 0); }

__host__ __device__ friend constexpr void swap(HasRangesSwap& a, HasRangesSwap& b) {
a.value_ = 1;
b.value_ = 1;
}
__host__ __device__ friend constexpr void swap(HasRangesSwap& a, int& b) {
a.value_ = 2;
b = 2;
}
};

struct HasRangesSwapWrapper {
using value_type = HasRangesSwap;

HasRangesSwap &value_;
__host__ __device__ constexpr explicit HasRangesSwapWrapper(HasRangesSwap &value) : value_(value) {}

__host__ __device__ constexpr HasRangesSwap& operator*() const { return value_; }
};

#ifndef _LIBCUDACXX_COMPILER_NVCC_BELOW_11_3 // nvcc segfaults here
static_assert( cuda::std::is_invocable_v<IterSwapT, HasRangesSwapWrapper&, HasRangesSwapWrapper&>);
// Does not satisfy swappable_with, even though swap(X, Y) is valid.
static_assert(!cuda::std::is_invocable_v<IterSwapT, HasRangesSwapWrapper&, int&>);
static_assert(!cuda::std::is_invocable_v<IterSwapT, int&, HasRangesSwapWrapper&>);
#endif // _LIBCUDACXX_COMPILER_NVCC_BELOW_11_3

struct B;

struct A {
bool value = false;
__host__ __device__ constexpr A& operator=(const B&) {
value = true;
return *this;
};
};

struct B {
bool value = false;
__host__ __device__ constexpr B& operator=(const A&) {
value = true;
return *this;
};
};

struct MoveOnly2;

struct MoveOnly1 {
bool value = false;

MoveOnly1() = default;
MoveOnly1(MoveOnly1&&) = default;
MoveOnly1& operator=(MoveOnly1&&) = default;
MoveOnly1(const MoveOnly1&) = delete;
MoveOnly1& operator=(const MoveOnly1&) = delete;

__host__ __device__ constexpr MoveOnly1& operator=(MoveOnly2 &&) {
value = true;
return *this;
};
};

struct MoveOnly2 {
bool value = false;

MoveOnly2() = default;
MoveOnly2(MoveOnly2&&) = default;
MoveOnly2& operator=(MoveOnly2&&) = default;
MoveOnly2(const MoveOnly2&) = delete;
MoveOnly2& operator=(const MoveOnly2&) = delete;

__host__ __device__ constexpr MoveOnly2& operator=(MoveOnly1 &&) {
value = true;
return *this;
};
};

__host__ __device__ constexpr bool test()
{
{
int value1 = 0;
int value2 = 0;
HasIterSwap a(value1), b(value2);
cuda::std::ranges::iter_swap(a, b);
assert(value1 == 1 && value2 == 1);
}
{
int value1 = 0;
int value2 = 0;
HasRangesSwap c(value1), d(value2);
HasRangesSwapWrapper cWrapper(c), dWrapper(d);
cuda::std::ranges::iter_swap(cWrapper, dWrapper);
assert(value1 == 1 && value2 == 1);
}
{
int value1 = 0;
int value2 = 0;
HasRangesSwap c(value1), d(value2);
cuda::std::ranges::iter_swap(HasRangesSwapWrapper(c), HasRangesSwapWrapper(d));
assert(value1 == 1 && value2 == 1);
}
{
A e; B f;
A *ePtr = &e;
B *fPtr = &f;
cuda::std::ranges::iter_swap(ePtr, fPtr);
assert(e.value && f.value);
}
{
MoveOnly1 g; MoveOnly2 h;
cuda::std::ranges::iter_swap(&g, &h);
assert(g.value && h.value);
}
#if TEST_HAS_BUILTIN(__builtin_is_constant_evaluated)
{
move_tracker arr[2];
cuda::std::ranges::iter_swap(cuda::std::begin(arr), cuda::std::begin(arr) + 1);
if (__builtin_is_constant_evaluated()) {
assert(arr[0].moves() == 1 && arr[1].moves() == 3);
} else
{
assert(arr[0].moves() == 1 && arr[1].moves() == 2);
}
}
#endif
{
int buff[2] = {1, 2};
cuda::std::ranges::iter_swap(buff + 0, buff + 1);
assert(buff[0] == 2 && buff[1] == 1);

cuda::std::ranges::iter_swap(cpp20_input_iterator<int*>(buff), cpp20_input_iterator<int*>(buff + 1));
assert(buff[0] == 1 && buff[1] == 2);

cuda::std::ranges::iter_swap(cpp17_input_iterator<int*>(buff), cpp17_input_iterator<int*>(buff + 1));
assert(buff[0] == 2 && buff[1] == 1);

cuda::std::ranges::iter_swap(forward_iterator<int*>(buff), forward_iterator<int*>(buff + 1));
assert(buff[0] == 1 && buff[1] == 2);

cuda::std::ranges::iter_swap(bidirectional_iterator<int*>(buff), bidirectional_iterator<int*>(buff + 1));
assert(buff[0] == 2 && buff[1] == 1);

cuda::std::ranges::iter_swap(random_access_iterator<int*>(buff), random_access_iterator<int*>(buff + 1));
assert(buff[0] == 1 && buff[1] == 2);

cuda::std::ranges::iter_swap(contiguous_iterator<int*>(buff), contiguous_iterator<int*>(buff + 1));
assert(buff[0] == 2 && buff[1] == 1);
}
return true;
}

#ifndef _LIBCUDACXX_COMPILER_NVCC_BELOW_11_3 // nvcc segfaults here
static_assert(!cuda::std::is_invocable_v<IterSwapT, int*>); // too few arguments
static_assert(!cuda::std::is_invocable_v<IterSwapT, int*, int*, int*>); // too many arguments
static_assert(!cuda::std::is_invocable_v<IterSwapT, int, int*>);
static_assert(!cuda::std::is_invocable_v<IterSwapT, int*, int>);
static_assert(!cuda::std::is_invocable_v<IterSwapT, void*, void*>);
#endif // _LIBCUDACXX_COMPILER_NVCC_BELOW_11_3

#if TEST_STD_VER > 17
// Test ADL-proofing.
struct Incomplete;
template<class T> struct Holder { T t; };
static_assert(cuda::std::is_invocable_v<IterSwapT, Holder<Incomplete>**, Holder<Incomplete>**>);
static_assert(cuda::std::is_invocable_v<IterSwapT, Holder<Incomplete>**, Holder<Incomplete>**&>);
static_assert(cuda::std::is_invocable_v<IterSwapT, Holder<Incomplete>**&, Holder<Incomplete>**>);
static_assert(cuda::std::is_invocable_v<IterSwapT, Holder<Incomplete>**&, Holder<Incomplete>**&>);
#endif

int main(int, char**)
{
test();
static_assert(test());

return 0;
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@
// template<class F> constexpr optional or_else(F&&) &&;
// template<class F> constexpr optional or_else(F&&) const&;

#include "MoveOnly.h"

#include <cuda/std/cassert>
#include <cuda/std/optional>

#include "MoveOnly.h"

struct NonMovable {
NonMovable() = default;
NonMovable(NonMovable&&) = delete;
Expand Down
Loading