diff --git a/libcxx/docs/Status/RangesPaper.csv b/libcxx/docs/Status/RangesPaper.csv --- a/libcxx/docs/Status/RangesPaper.csv +++ b/libcxx/docs/Status/RangesPaper.csv @@ -59,8 +59,8 @@ `[projected] `_,`ranges::projected `_,[iterator.concepts],Louis Dionne,✅ `[common.alg.req] `_: pt. 1,"| `indirectly_movable `_ | `indirectly_movable_storable `_ -| indirectly_copyable -| indirectly_copyable_storable",[iterator.concepts],Zoe Carver,In progress +| `indirectly_copyable `_ +| `indirectly_copyable_storable `_",[iterator.concepts],Zoe Carver and Konstantin Varlamov,✅ [common.alg.req]: pt. 2,indirectly_swappable,"| [iterator.concepts] | [iterator.cust.swap]",Zoe Carver,✅ [common.alg.req]: pt. 3,indirectly_comparable,[projected],Nikolas Klauser,✅ diff --git a/libcxx/include/__iterator/concepts.h b/libcxx/include/__iterator/concepts.h --- a/libcxx/include/__iterator/concepts.h +++ b/libcxx/include/__iterator/concepts.h @@ -254,6 +254,22 @@ constructible_from, iter_rvalue_reference_t<_In>> && assignable_from&, iter_rvalue_reference_t<_In>>; +template +concept indirectly_copyable = + indirectly_readable<_In> && + indirectly_writable<_Out, iter_reference_t<_In>>; + +template +concept indirectly_copyable_storable = + indirectly_copyable<_In, _Out> && + indirectly_writable<_Out, iter_value_t<_In>&> && + indirectly_writable<_Out, const iter_value_t<_In>&> && + indirectly_writable<_Out, iter_value_t<_In>&&> && + indirectly_writable<_Out, const iter_value_t<_In>&&> && + copyable> && + constructible_from, iter_reference_t<_In>> && + assignable_from&, iter_reference_t<_In>>; + // Note: indirectly_swappable is located in iter_swap.h to prevent a dependency cycle // (both iter_swap and indirectly_swappable require indirectly_readable). diff --git a/libcxx/include/iterator b/libcxx/include/iterator --- a/libcxx/include/iterator +++ b/libcxx/include/iterator @@ -136,6 +136,13 @@ template concept indirectly_movable_storable = see below; // since C++20 +// [alg.req.ind.copy], concept indirectly_copyable +template + concept indirectly_copyable = see below; // since C++20 + +template + concept indirectly_copyable_storable = see below; // since C++20 + // [alg.req.ind.swap], concept indirectly_swappable template concept indirectly_swappable = see below; // since C++20 diff --git a/libcxx/test/std/iterators/iterator.requirements/alg.req.ind.copy/indirectly_copyable.compile.pass.cpp b/libcxx/test/std/iterators/iterator.requirements/alg.req.ind.copy/indirectly_copyable.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/iterators/iterator.requirements/alg.req.ind.copy/indirectly_copyable.compile.pass.cpp @@ -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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts + +// template +// concept indirectly_copyable; + +#include + +#include "test_macros.h" + +struct Empty {}; + +struct MoveOnly { + MoveOnly(MoveOnly&&) = default; + MoveOnly(MoveOnly const&) = delete; + MoveOnly& operator=(MoveOnly&&) = default; + MoveOnly& operator=(MoveOnly const&) = delete; + MoveOnly() = default; +}; + +struct CopyOnly { + CopyOnly(CopyOnly&&) = delete; + CopyOnly(CopyOnly const&) = default; + CopyOnly& operator=(CopyOnly&&) = delete; + CopyOnly& operator=(CopyOnly const&) = default; + CopyOnly() = default; +}; + +template +struct PointerTo { + using value_type = T; + T& operator*() const; +}; + +// Can copy the underlying objects between pointers. +static_assert( std::indirectly_copyable); +static_assert( std::indirectly_copyable); + +// Can't copy if the output pointer is const. +static_assert(!std::indirectly_copyable); +static_assert(!std::indirectly_copyable); + +// Can copy from a pointer into an array but cannot copy from an array. +static_assert( std::indirectly_copyable); +static_assert(!std::indirectly_copyable); +static_assert(!std::indirectly_copyable); +static_assert(!std::indirectly_copyable); + +// Can't copy between non-pointer types. +static_assert(!std::indirectly_copyable); +static_assert(!std::indirectly_copyable); +static_assert(!std::indirectly_copyable); + +// Can copy empty objects. +static_assert( std::indirectly_copyable); + +// Can't copy move-only objects. +static_assert(!std::indirectly_copyable); +static_assert(!std::indirectly_copyable); +static_assert(!std::indirectly_copyable); +static_assert(!std::indirectly_copyable); + +// Can copy copy-only objects. +static_assert( std::indirectly_copyable); +static_assert(!std::indirectly_copyable); +static_assert( std::indirectly_copyable); +static_assert(!std::indirectly_copyable); + +// Can copy through a dereferenceable class. +static_assert( std::indirectly_copyable>); +static_assert(!std::indirectly_copyable>); +static_assert( std::indirectly_copyable, int*>); +static_assert(!std::indirectly_copyable>); +static_assert( std::indirectly_copyable>); +static_assert( std::indirectly_copyable, CopyOnly*>); +static_assert( std::indirectly_copyable, PointerTo>); diff --git a/libcxx/test/std/iterators/iterator.requirements/alg.req.ind.copy/indirectly_copyable.subsumption.compile.pass.cpp b/libcxx/test/std/iterators/iterator.requirements/alg.req.ind.copy/indirectly_copyable.subsumption.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/iterators/iterator.requirements/alg.req.ind.copy/indirectly_copyable.subsumption.compile.pass.cpp @@ -0,0 +1,30 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts + +// template +// concept indirectly_copyable; + +#include + +#include + +template +constexpr bool indirectly_copyable_subsumption() { + return false; +} + +template + requires std::indirectly_copyable +constexpr bool indirectly_copyable_subsumption() { + return true; +} + +static_assert(indirectly_copyable_subsumption()); diff --git a/libcxx/test/std/iterators/iterator.requirements/alg.req.ind.copy/indirectly_copyable_storable.compile.pass.cpp b/libcxx/test/std/iterators/iterator.requirements/alg.req.ind.copy/indirectly_copyable_storable.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/iterators/iterator.requirements/alg.req.ind.copy/indirectly_copyable_storable.compile.pass.cpp @@ -0,0 +1,217 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts + +// template +// concept indirectly_copyable_storable; + +#include + +#include "test_macros.h" + +struct Empty {}; + +struct MoveOnly { + MoveOnly(MoveOnly&&) = default; + MoveOnly(MoveOnly const&) = delete; + MoveOnly& operator=(MoveOnly&&) = default; + MoveOnly& operator=(MoveOnly const&) = delete; + MoveOnly() = default; +}; + +struct CopyOnly { + CopyOnly(CopyOnly&&) = delete; + CopyOnly(CopyOnly const&) = default; + CopyOnly& operator=(CopyOnly&&) = delete; + CopyOnly& operator=(CopyOnly const&) = default; + CopyOnly() = default; +}; + +template +struct PointerTo { + using value_type = ValueType; + T& operator*() const; +}; + +// Copying the underlying object between pointers (or dereferenceable classes) works. This is a non-exhaustive check +// because this functionality comes from `indirectly_copyable`. +static_assert( std::indirectly_copyable_storable); +static_assert( std::indirectly_copyable_storable); +static_assert( std::indirectly_copyable_storable); +static_assert( std::indirectly_copyable_storable); +static_assert(!std::indirectly_copyable_storable); +static_assert(!std::indirectly_copyable_storable, PointerTo>); +// `indirectly_copyable_storable` requires the type to be `copyable`, which in turns requires it to be `movable`. +static_assert(!std::indirectly_copyable_storable); +static_assert(!std::indirectly_copyable_storable, PointerTo>); + +struct FooConvertible; + +struct Foo { + Foo& operator=(FooConvertible const&) = delete; +}; + +// FooConvertible is convertible to Foo but not assignable to it. This is implemented by explicitly deleting +// "operator=(FooConvertible)" in Foo. +struct FooConvertible { + operator Foo&() const; +}; + +// The case when indirectly_writable but not indirectly_writable (you can +// do `Foo f = FooConvertible();` but not `f = FooConvertible();`). +using ProxyPointerFoo = PointerTo; +static_assert( std::indirectly_writable>); +static_assert(!std::indirectly_copyable_storable); + +struct DeletedCopyCtor { + DeletedCopyCtor(DeletedCopyCtor const&) = delete; + DeletedCopyCtor& operator=(DeletedCopyCtor const&) = default; +}; + +struct DeletedNonconstCopyCtor { + DeletedNonconstCopyCtor(DeletedNonconstCopyCtor const&) = default; + DeletedNonconstCopyCtor(DeletedNonconstCopyCtor&) = delete; + DeletedNonconstCopyCtor& operator=(DeletedNonconstCopyCtor const&) = default; +}; + +struct DeletedMoveCtor { + DeletedMoveCtor(DeletedMoveCtor&&) = delete; + DeletedMoveCtor& operator=(DeletedMoveCtor&&) = default; +}; + +struct DeletedConstMoveCtor { + DeletedConstMoveCtor(DeletedConstMoveCtor&&) = default; + DeletedConstMoveCtor(DeletedConstMoveCtor const&&) = delete; + DeletedConstMoveCtor& operator=(DeletedConstMoveCtor&&) = default; +}; + +struct DeletedCopyAssignment { + DeletedCopyAssignment(DeletedCopyAssignment const&) = default; + DeletedCopyAssignment& operator=(DeletedCopyAssignment const&) = delete; +}; + +struct DeletedNonconstCopyAssignment { + DeletedNonconstCopyAssignment(DeletedNonconstCopyAssignment const&) = default; + DeletedNonconstCopyAssignment& operator=(DeletedNonconstCopyAssignment const&) = default; + DeletedNonconstCopyAssignment& operator=(DeletedNonconstCopyAssignment&) = delete; +}; + +struct DeletedMoveAssignment { + DeletedMoveAssignment(DeletedMoveAssignment&&) = default; + DeletedMoveAssignment& operator=(DeletedMoveAssignment&&) = delete; +}; + +struct DeletedConstMoveAssignment { + DeletedConstMoveAssignment(DeletedConstMoveAssignment&&) = default; + DeletedConstMoveAssignment& operator=(DeletedConstMoveAssignment&&) = delete; +}; + +static_assert(!std::indirectly_copyable_storable); +static_assert(!std::indirectly_copyable_storable); +static_assert(!std::indirectly_copyable_storable); +static_assert(!std::indirectly_copyable_storable); +static_assert(!std::indirectly_copyable_storable); +static_assert(!std::indirectly_copyable_storable); +static_assert(!std::indirectly_copyable_storable); +static_assert(!std::indirectly_copyable_storable); + +struct AssignableToBar; + +struct Bar { + Bar& operator=(AssignableToBar const&); +}; + +struct AssignableToBar { + AssignableToBar() = default; + AssignableToBar(const Bar&); +}; + +// AssignableToBar can be constructed with a Bar and assigned to a Bar, so it does model indirectly_copyable_storable. +using ProxyPointerBar = PointerTo; +static_assert( std::indirectly_copyable_storable); + +struct CommonType { }; + +// ReferenceType is a (proxy) reference for ValueType, but ValueType is not constructible from ReferenceType. +struct NotConstructibleFromRefIn { + struct ReferenceType; + + struct ValueType { + ValueType(ReferenceType) = delete; + operator CommonType&() const; + }; + + struct ReferenceType { + operator CommonType&() const; + }; + + using value_type = ValueType; + ReferenceType& operator*() const; +}; + +template