Skip to content

Commit

Permalink
P2165R4: Compatibility Between tuple, pair, And tuple-like Objects (
Browse files Browse the repository at this point in the history
#3372)

Co-authored-by: Stephan T. Lavavej <[email protected]>
Co-authored-by: Casey Carter <[email protected]>
  • Loading branch information
3 people authored Feb 26, 2023
1 parent 86acfb1 commit 165e96f
Show file tree
Hide file tree
Showing 19 changed files with 1,183 additions and 33 deletions.
6 changes: 5 additions & 1 deletion stl/inc/ranges
Original file line number Diff line number Diff line change
Expand Up @@ -5488,13 +5488,17 @@ namespace ranges {
#endif // _HAS_CXX23

template <class _Tuple, size_t _Index>
concept _Has_tuple_element = //
concept _Has_tuple_element =
#if _HAS_CXX23
_Tuple_like<_Tuple> && _Index < tuple_size_v<_Tuple>;
#else // ^^^ C++23 / C++20 vvv
requires(_Tuple __t) {
typename tuple_size<_Tuple>::type;
requires _Index < tuple_size_v<_Tuple>;
typename tuple_element_t<_Index, _Tuple>;
{ _STD get<_Index>(__t) } -> convertible_to<const tuple_element_t<_Index, _Tuple>&>;
};
#endif // C++20

template <class _Tuple, size_t _Index>
concept _Returnable_element = is_reference_v<_Tuple> || move_constructible<tuple_element_t<_Index, _Tuple>>;
Expand Down
296 changes: 284 additions & 12 deletions stl/inc/tuple

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions stl/inc/utility
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,7 @@ _NODISCARD constexpr bool operator>=(const pair<_Ty1, _Ty2>& _Left, const pair<_
}
#endif // ^^^ !defined(__cpp_lib_concepts) ^^^

#ifdef __cpp_lib_concepts
#if _HAS_CXX23 && defined(__cpp_lib_concepts)
template <class _Ty1, class _Ty2, class _Uty1, class _Uty2, template <class> class _TQual,
template <class> class _UQual>
requires requires {
Expand All @@ -540,7 +540,7 @@ template <class _Ty1, class _Ty2, class _Uty1, class _Uty2>
struct common_type<pair<_Ty1, _Ty2>, pair<_Uty1, _Uty2>> {
using type = pair<common_type_t<_Ty1, _Uty1>, common_type_t<_Ty2, _Uty2>>;
};
#endif // __cpp_lib_concepts
#endif // _HAS_CXX23 && defined(__cpp_lib_concepts)

template <class _Ty>
struct _Unrefwrap_helper { // leave unchanged if not a reference_wrapper
Expand Down
43 changes: 27 additions & 16 deletions stl/inc/xutility
Original file line number Diff line number Diff line change
Expand Up @@ -1096,14 +1096,30 @@ struct _Is_allocator<_Ty, void_t<typename _Ty::value_type, decltype(_STD declval

// deduction guide utilities (N4892 [associative.general]/2)
template <class _Iter>
using _Guide_key_t = remove_const_t<typename iterator_traits<_Iter>::value_type::first_type>;
using _Guide_key_t =
#if _HAS_CXX23
remove_const_t<tuple_element_t<0, typename iterator_traits<_Iter>::value_type>>;
#else // ^^^ C++23 / C++20 vvv
remove_const_t<typename iterator_traits<_Iter>::value_type::first_type>;
#endif // C++20

template <class _Iter>
using _Guide_val_t = typename iterator_traits<_Iter>::value_type::second_type;
using _Guide_val_t =
#if _HAS_CXX23
tuple_element_t<1, typename iterator_traits<_Iter>::value_type>;
#else // ^^^ C++23 / C++20 vvv
typename iterator_traits<_Iter>::value_type::second_type;
#endif // C++20

template <class _Iter>
using _Guide_pair_t = pair<add_const_t<typename iterator_traits<_Iter>::value_type::first_type>,
typename iterator_traits<_Iter>::value_type::second_type>;
using _Guide_pair_t =
#if _HAS_CXX23
pair<add_const_t<tuple_element_t<0, typename iterator_traits<_Iter>::value_type>>,
tuple_element_t<1, typename iterator_traits<_Iter>::value_type>>;
#else // ^^^ C++23 / C++20 vvv
pair<add_const_t<typename iterator_traits<_Iter>::value_type::first_type>,
typename iterator_traits<_Iter>::value_type::second_type>;
#endif // C++20

_EXPORT_STD template <class _Ty>
struct is_execution_policy : false_type {};
Expand Down Expand Up @@ -3603,17 +3619,6 @@ namespace ranges {
};
} // namespace ranges

// These declarations must be visible to qualified name lookup for _STD get in _Pair_like below, even if <tuple> hasn't
// yet been included.
_EXPORT_STD template <size_t _Index, class... _Types>
_NODISCARD constexpr tuple_element_t<_Index, tuple<_Types...>>& get(tuple<_Types...>& _Tuple) noexcept;
_EXPORT_STD template <size_t _Index, class... _Types>
_NODISCARD constexpr const tuple_element_t<_Index, tuple<_Types...>>& get(const tuple<_Types...>& _Tuple) noexcept;
_EXPORT_STD template <size_t _Index, class... _Types>
_NODISCARD constexpr tuple_element_t<_Index, tuple<_Types...>>&& get(tuple<_Types...>&& _Tuple) noexcept;
_EXPORT_STD template <size_t _Index, class... _Types>
_NODISCARD constexpr const tuple_element_t<_Index, tuple<_Types...>>&& get(const tuple<_Types...>&& _Tuple) noexcept;

namespace ranges {
template <class _From, class _To>
concept _Uses_nonqualification_pointer_conversion =
Expand All @@ -3624,6 +3629,7 @@ namespace ranges {
concept _Convertible_to_non_slicing = convertible_to<_From, _To>
&& (!_Uses_nonqualification_pointer_conversion<decay_t<_From>, decay_t<_To>>);

#if !_HAS_CXX23
template <class _Ty>
concept _Pair_like =
(!is_reference_v<_Ty>) && requires(_Ty __t) {
Expand All @@ -3634,9 +3640,14 @@ namespace ranges {
{ _STD get<0>(__t) } -> convertible_to<const tuple_element_t<0, _Ty>&>;
{ _STD get<1>(__t) } -> convertible_to<const tuple_element_t<1, _Ty>&>;
};
#endif // !_HAS_CXX23

template <class _Ty, class _First, class _Second>
concept _Pair_like_convertible_from = (!range<_Ty>) && _Pair_like<_Ty> && constructible_from<_Ty, _First, _Second>
concept _Pair_like_convertible_from = (!range<_Ty>)
#if _HAS_CXX23
&& (!is_reference_v<_Ty>)
#endif // _HAS_CXX23
&& _Pair_like<_Ty> && constructible_from<_Ty, _First, _Second>
&& _Convertible_to_non_slicing<_First, tuple_element_t<0, _Ty>>
&& convertible_to<_Second, tuple_element_t<1, _Ty>>;

Expand Down
8 changes: 6 additions & 2 deletions stl/inc/yvals_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,6 @@
// P2077R3 Heterogeneous Erasure Overloads For Associative Containers
// P2136R3 invoke_r()
// P2165R4 Compatibility Between tuple, pair, And tuple-like Objects
// (changes to views::zip and pair only)
// P2166R1 Prohibiting basic_string And basic_string_view Construction From nullptr
// P2186R2 Removing Garbage Collection Support
// P2273R3 constexpr unique_ptr
Expand Down Expand Up @@ -1738,7 +1737,12 @@ _EMIT_STL_ERROR(STL1004, "C++98 unexpected() is incompatible with C++23 unexpect
#define __cpp_lib_string_contains 202011L
#define __cpp_lib_string_resize_and_overwrite 202110L
#define __cpp_lib_to_underlying 202102L
#define __cpp_lib_unreachable 202202L

#ifdef __cpp_lib_concepts
#define __cpp_lib_tuple_like 202207L
#endif // __cpp_lib_concepts

#define __cpp_lib_unreachable 202202L
#endif // _HAS_CXX23

// macros with language mode sensitivity
Expand Down
3 changes: 3 additions & 0 deletions tests/libcxx/expected_results.txt
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ std/utilities/utility/mem.res/mem.res.pool/mem.res.pool.mem/unsync_deallocate_ma
# Too many constexpr operations
std/utilities/charconv/charconv.to.chars/integral.pass.cpp FAIL

# libc++ tests strengthened assignment operators (not compatible with P2165R4: "Compatibility Between tuple, pair, And tuple-like Objects")
std/utilities/tuple/tuple.tuple/tuple.assign/const_pair.pass.cpp FAIL

# libc++ has not implemented P2505R5: "Monadic Functions for std::expected"
std/language.support/support.limits/support.limits.general/expected.version.compile.pass.cpp FAIL

Expand Down
5 changes: 5 additions & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -551,7 +551,12 @@ tests\P1899R3_views_stride_death
tests\P1951R1_default_arguments_pair_forward_ctor
tests\P2136R3_invoke_r
tests\P2162R2_std_visit_for_derived_classes_from_variant
tests\P2165R4_tuple_like_common_reference
tests\P2165R4_tuple_like_common_type
tests\P2165R4_tuple_like_operations
tests\P2165R4_tuple_like_pair
tests\P2165R4_tuple_like_relational_operators
tests\P2165R4_tuple_like_tuple_members
tests\P2231R1_complete_constexpr_optional_variant
tests\P2273R3_constexpr_unique_ptr
tests\P2278R4_basic_const_iterator
Expand Down
14 changes: 14 additions & 0 deletions tests/std/tests/P0433R2_deduction_guides/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,20 @@ void test_map_or_multimap() {
static_assert(is_same_v<decltype(m15), M<long, char, MyGreater>>);
static_assert(is_same_v<decltype(m16), M<long, char, MyGreater, MyAlloc<CPurr>>>);
static_assert(is_same_v<decltype(m17), M<long, char, less<long>, MyAlloc<CPurr>>>);

{ // Verify changes from P2165R4
using TupleIter = tuple<int, double>*;
static_assert(is_same_v<decltype(M{TupleIter{}, TupleIter{}}), M<int, double>>);

using PairIter = pair<int, float>*;
static_assert(is_same_v<decltype(M{PairIter{}, PairIter{}}), M<int, float>>);

using ArrayIter = array<int, 2>*;
static_assert(is_same_v<decltype(M{ArrayIter{}, ArrayIter{}}), M<int, int>>);

using SubrangeIter = ranges::subrange<int*, int*>*;
static_assert(is_same_v<decltype(M{SubrangeIter{}, SubrangeIter{}}), M<int*, int*>>);
}
#endif // _HAS_CXX23 && defined(__cpp_lib_concepts)
}

Expand Down
4 changes: 4 additions & 0 deletions tests/std/tests/P2165R4_tuple_like_common_reference/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 ..\strict_concepts_latest_matrix.lst
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <array>
#include <ranges>
#include <tuple>
#include <type_traits>
#include <utility>

using namespace std;
using ranges::subrange;

template <class T, class U>
concept CanCommonReference = requires { typename common_reference<T, U>::type; };

template <class T, class U, class Expected>
requires CanCommonReference<T, U>
constexpr bool test_common_reference =
same_as<common_reference_t<T, U>, Expected> && same_as<common_reference_t<U, T>, Expected>;

struct Sentinel {
bool operator==(const auto&) const; // not defined
operator unreachable_sentinel_t() const; // not defined
};

template <>
struct std::common_type<Sentinel, unreachable_sentinel_t> {
using type = unreachable_sentinel_t;
};

template <>
struct std::common_type<unreachable_sentinel_t, Sentinel> {
using type = unreachable_sentinel_t;
};

static_assert(test_common_reference<Sentinel, unreachable_sentinel_t, unreachable_sentinel_t>);

// Test common_reference<tuple, tuple>
static_assert(test_common_reference<tuple<>, tuple<>, tuple<>>);
static_assert(test_common_reference<tuple<char&&>, tuple<const int&>, tuple<int>>);
static_assert(test_common_reference<tuple<int&>, tuple<const int&>, tuple<const int&>>);
static_assert(test_common_reference<tuple<int&>, tuple<int&&>, tuple<const int&>>);
static_assert(test_common_reference<tuple<short, int, long>, tuple<long, int, short>, tuple<long, int, long>>);

// Test common_reference<cvref tuple, cvref tuple>
static_assert(test_common_reference<tuple<>&, tuple<>&&, const tuple<>&>);
static_assert(
test_common_reference<const tuple<int, long>&, volatile tuple<int, long>&, const volatile tuple<int, long>&>);
static_assert(test_common_reference<tuple<int&, const int&&, volatile int&>&,
const tuple<const int&, const int, const int&>, tuple<const int&, int, const volatile int&>>);

// Test common_reference<tuple, tuple-like>, tuple-like is pair
static_assert(test_common_reference<tuple<int, int>, pair<int, int>, tuple<int, int>>);
static_assert(test_common_reference<tuple<int, long>, pair<short, long long>, tuple<int, long long>>);
static_assert(
test_common_reference<tuple<int&, long&&>, pair<const int&, const long&&>, tuple<const int&, const long&&>>);
static_assert(test_common_reference<tuple<short&, const volatile long&&>, pair<volatile int, long>, tuple<int, long>>);

// Test common_reference<cvref tuple, cvref tuple-like>, tuple-like is pair
static_assert(test_common_reference<tuple<int, int>&, pair<int, int>&, tuple<int&, int&>>);
static_assert(test_common_reference<const tuple<int, int>&, const pair<int, int>&, tuple<const int&, const int&>>);
static_assert(
test_common_reference<tuple<int&, long&&>&, const pair<const int&, const long&&>&, tuple<const int&, const long&>>);
static_assert(test_common_reference<const tuple<short&, const volatile long&&>, const pair<volatile int, long>&,
tuple<int, long>>);

// Test common_reference<tuple, tuple-like>, tuple-like is array
static_assert(test_common_reference<tuple<>, array<int, 0>, tuple<>>);
static_assert(test_common_reference<tuple<int>, array<int, 1>, tuple<int>>);
static_assert(test_common_reference<tuple<int, long>, array<int, 2>, tuple<int, long>>);
static_assert(test_common_reference<tuple<short, int&&, const long>, array<int, 3>, tuple<int, int, long>>);
static_assert(test_common_reference<tuple<short&, int&&, const volatile long>, array<int, 3>, tuple<int, int, long>>);

// Test common_reference<cvref tuple, cvref tuple-like>, tuple-like is array
static_assert(test_common_reference<const tuple<>&, array<int, 0>, tuple<>>);
static_assert(test_common_reference<tuple<int>&, array<int, 1>&, tuple<int&>>);
static_assert(test_common_reference<const tuple<int, long>&&, array<int, 2>&, tuple<const int&, long>>);

// Test common_reference<tuple, tuple-like>, tuple-like is ranges::subrange
static_assert(test_common_reference<tuple<int*, int*>, subrange<int*, int*>, tuple<int*, int*>>);
static_assert(
test_common_reference<tuple<int*, const int*>, subrange<const int*, int*>, tuple<const int*, const int*>>);
static_assert(test_common_reference<tuple<int*, Sentinel>, subrange<int*, unreachable_sentinel_t>,
tuple<int*, unreachable_sentinel_t>>);
static_assert(test_common_reference<tuple<const volatile int*, Sentinel>, subrange<int*, unreachable_sentinel_t>,
tuple<const volatile int*, unreachable_sentinel_t>>);

// Test common_reference<cvref tuple, cvref tuple-like>, tuple-like is ranges::subrange
static_assert(test_common_reference<const tuple<int*, int*>&&, const subrange<int*, int*>, tuple<int*, int*>>);
static_assert(
test_common_reference<const tuple<int*, const int*>&, subrange<const int*, int*>, tuple<const int*, const int*>>);

// Test invalid common_references
static_assert(!CanCommonReference<tuple<int>, array<int, 2>>);
static_assert(!CanCommonReference<tuple<int>, subrange<int*, int*>>);
static_assert(!CanCommonReference<tuple<int>, pair<int, int>>);
4 changes: 4 additions & 0 deletions tests/std/tests/P2165R4_tuple_like_common_type/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 ..\concepts_latest_matrix.lst
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <array>
#include <ranges>
#include <tuple>
#include <type_traits>
#include <utility>

using namespace std;
using ranges::subrange;

template <class T, class U>
concept CanCommonType = requires { typename common_type<T, U>::type; };

template <class T, class U, class Expected>
requires CanCommonType<T, U>
inline constexpr bool test_common_type =
same_as<common_type_t<T, U>, Expected> && same_as<common_type_t<U, T>, Expected>;

struct Sentinel {
bool operator==(const auto&) const; // not defined
operator unreachable_sentinel_t() const; // not defined
};

template <>
struct std::common_type<Sentinel, unreachable_sentinel_t> {
using type = unreachable_sentinel_t;
};

template <>
struct std::common_type<unreachable_sentinel_t, Sentinel> {
using type = unreachable_sentinel_t;
};

static_assert(test_common_type<Sentinel, unreachable_sentinel_t, unreachable_sentinel_t>);

// Test common_type<tuple, tuple>
static_assert(test_common_type<tuple<>, tuple<>, tuple<>>);
static_assert(test_common_type<tuple<char>, tuple<int>, tuple<int>>);
static_assert(test_common_type<tuple<int&>, tuple<const int&>, tuple<int>>);
static_assert(test_common_type<tuple<int&>, tuple<int&&>, tuple<int>>);
static_assert(test_common_type<tuple<short, long, long>, tuple<long, int, short>, tuple<long, long, long>>);

// Test common_reference<cvref tuple, cvref tuple>
static_assert(test_common_type<const tuple<>&, tuple<>&&, tuple<>>);
static_assert(test_common_type<tuple<char>&, const tuple<int>&&, tuple<int>>);
static_assert(test_common_type<tuple<int&>&, tuple<const int&>, tuple<int>>);
static_assert(test_common_type<volatile tuple<int&>&, tuple<int&&>, tuple<int>>);

// Test common_type<tuple, tuple-like>, tuple-like is pair
static_assert(test_common_type<tuple<int, int>, pair<int, int>, tuple<int, int>>);
static_assert(test_common_type<tuple<int, long>, pair<short, long long>, tuple<int, long long>>);
static_assert(test_common_type<tuple<int&, long&&>, pair<const int&, const long&&>, tuple<int, long>>);
static_assert(test_common_type<tuple<short&, const volatile long&&>, pair<volatile int, long>, tuple<int, long>>);

// Test common_type<cvref tuple, cvref tuple-like>, tuple-like is pair
static_assert(test_common_type<const tuple<int, int>&&, volatile pair<int, int>&, tuple<int, int>>);
static_assert(test_common_type<volatile tuple<int, long>&&, const pair<short, long long>&&, tuple<int, long long>>);

// Test common_type<tuple, tuple-like>, tuple-like is array
static_assert(test_common_type<tuple<>, array<int, 0>, tuple<>>);
static_assert(test_common_type<tuple<int>, array<int, 1>, tuple<int>>);
static_assert(test_common_type<tuple<int, long>, array<int, 2>, tuple<int, long>>);
static_assert(test_common_type<tuple<short, int, long>, array<int, 3>, tuple<int, int, long>>);
static_assert(test_common_type<tuple<short&, int&&, const volatile long>, array<int, 3>, tuple<int, int, long>>);

// Test common_type<cvref tuple, cvref tuple-like>, tuple-like is array
static_assert(test_common_type<const tuple<>&&, volatile array<int, 0>&, tuple<>>);
static_assert(test_common_type<const tuple<int>&, volatile array<int, 1>&&, tuple<int>>);

// Test common_type<tuple, tuple-like>, tuple-like is ranges::subrange
static_assert(test_common_type<tuple<int*, int*>, subrange<int*, int*>, tuple<int*, int*>>);
static_assert(test_common_type<tuple<int*, const int*>, subrange<const int*, int*>, tuple<const int*, const int*>>);
static_assert(test_common_type<tuple<int*, Sentinel>, subrange<int*, unreachable_sentinel_t>,
tuple<int*, unreachable_sentinel_t>>);
static_assert(test_common_type<tuple<const volatile int*, Sentinel>, subrange<int*, unreachable_sentinel_t>,
tuple<const volatile int*, unreachable_sentinel_t>>);

// Test common_type<cvref tuple, cvref tuple-like>, tuple-like is ranges::subrange
static_assert(test_common_type<const tuple<int*, int*>&, volatile subrange<int*, int*>&&, tuple<int*, int*>>);
static_assert(test_common_type<volatile tuple<int*, Sentinel>&, const subrange<int*, unreachable_sentinel_t>&&,
tuple<int*, unreachable_sentinel_t>>);

// Test invalid common_types
static_assert(!CanCommonType<tuple<int>, array<int, 2>>);
static_assert(!CanCommonType<tuple<int>, subrange<int*, int*>>);
static_assert(!CanCommonType<tuple<int>, pair<int, int>>);
4 changes: 4 additions & 0 deletions tests/std/tests/P2165R4_tuple_like_operations/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 ..\concepts_latest_matrix.lst
Loading

0 comments on commit 165e96f

Please sign in to comment.