[libc++] [ranges] Introduce _LIBCPP_AUTO_CAST(x) for auto(x).

Clang is gaining `auto(x)` support in D113393; sadly there
seems to be no feature-test macro for it. Zhihao is opening
a core issue for that macro.

Use `_LIBCPP_AUTO_CAST` where C++20 specifies we should use `auto(x)`;
stop using `__decay_copy(x)` in those places.
In fact, remove `__decay_copy` entirely. As of C++20, it's purely
a paper specification tool signifying "Return just `x`, but it was
perfect-forwarded, so we understand you're going to have to call
its move-constructor sometimes." I believe there's no reason we'd
ever need to do its operation explicitly in code.

This heisenbugs away a test failure on MinGW; see D112214.

Differential Revision: https://reviews.llvm.org/D115686
This commit is contained in:
Arthur O'Dwyer 2021-12-13 19:21:38 -05:00
parent 4a10457d33
commit cb8a0b0797
15 changed files with 79 additions and 98 deletions

View File

@ -25,7 +25,7 @@
"","","","","",""
"`P0288R9 <https://wg21.link/P0288R9>`__","LWG","``any_invocable``","October 2021","",""
"`P0798R8 <https://wg21.link/P0798R8>`__","LWG","Monadic operations for ``std::optional``","October 2021","|Complete|","14.0"
"`P0849R8 <https://wg21.link/P0849R8>`__","LWG","``auto(x)``: ``DECAY_COPY`` in the language","October 2021","",""
"`P0849R8 <https://wg21.link/P0849R8>`__","LWG","``auto(x)``: ``DECAY_COPY`` in the language","October 2021","|Complete|","14.0"
"`P1072R10 <https://wg21.link/P1072R10>`__","LWG","``basic_string::resize_and_overwrite``","October 2021","",""
"`P1147R1 <https://wg21.link/P1147R1>`__","LWG","Printing ``volatile`` Pointers","October 2021","|Complete|","14.0"
"`P1272R4 <https://wg21.link/P1272R4>`__","LWG","Byteswapping for fun&&nuf","October 2021","|Complete|","14.0"

1 Paper # Group Paper Name Meeting Status First released version
25
26 `P0288R9 <https://wg21.link/P0288R9>`__ LWG ``any_invocable`` October 2021
27 `P0798R8 <https://wg21.link/P0798R8>`__ LWG Monadic operations for ``std::optional`` October 2021 |Complete| 14.0
28 `P0849R8 <https://wg21.link/P0849R8>`__ LWG ``auto(x)``: ``DECAY_COPY`` in the language October 2021 |Complete| 14.0
29 `P1072R10 <https://wg21.link/P1072R10>`__ LWG ``basic_string::resize_and_overwrite`` October 2021
30 `P1147R1 <https://wg21.link/P1147R1>`__ LWG Printing ``volatile`` Pointers October 2021 |Complete| 14.0
31 `P1272R4 <https://wg21.link/P1272R4>`__ LWG Byteswapping for fun&&nuf October 2021 |Complete| 14.0

View File

@ -360,8 +360,8 @@ set(files
__tuple
__undef_macros
__utility/as_const.h
__utility/auto_cast.h
__utility/cmp.h
__utility/decay_copy.h
__utility/declval.h
__utility/exchange.h
__utility/forward.h

View File

@ -14,8 +14,7 @@
#include <__iterator/readable_traits.h>
#include <__ranges/enable_borrowed_range.h>
#include <__utility/as_const.h>
#include <__utility/decay_copy.h>
#include <__utility/forward.h>
#include <__utility/auto_cast.h>
#include <concepts>
#include <type_traits>
@ -39,7 +38,7 @@ namespace ranges::__begin {
concept __member_begin =
__can_borrow<_Tp> &&
requires(_Tp&& __t) {
{ _VSTD::__decay_copy(__t.begin()) } -> input_or_output_iterator;
{ _LIBCPP_AUTO_CAST(__t.begin()) } -> input_or_output_iterator;
};
void begin(auto&) = delete;
@ -51,7 +50,7 @@ namespace ranges::__begin {
__can_borrow<_Tp> &&
__class_or_enum<remove_cvref_t<_Tp> > &&
requires(_Tp && __t) {
{ _VSTD::__decay_copy(begin(__t)) } -> input_or_output_iterator;
{ _LIBCPP_AUTO_CAST(begin(__t)) } -> input_or_output_iterator;
};
struct __fn {
@ -65,17 +64,17 @@ namespace ranges::__begin {
template <class _Tp>
requires __member_begin<_Tp>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
noexcept(noexcept(_VSTD::__decay_copy(__t.begin())))
noexcept(noexcept(_LIBCPP_AUTO_CAST(__t.begin())))
{
return __t.begin();
return _LIBCPP_AUTO_CAST(__t.begin());
}
template <class _Tp>
requires __unqualified_begin<_Tp>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
noexcept(noexcept(_VSTD::__decay_copy(begin(__t))))
noexcept(noexcept(_LIBCPP_AUTO_CAST(begin(__t))))
{
return begin(__t);
return _LIBCPP_AUTO_CAST(begin(__t));
}
void operator()(auto&&) const = delete;
@ -98,7 +97,7 @@ namespace ranges::__end {
__can_borrow<_Tp> &&
requires(_Tp&& __t) {
typename iterator_t<_Tp>;
{ _VSTD::__decay_copy(_VSTD::forward<_Tp>(__t).end()) } -> sentinel_for<iterator_t<_Tp> >;
{ _LIBCPP_AUTO_CAST(__t.end()) } -> sentinel_for<iterator_t<_Tp> >;
};
void end(auto&) = delete;
@ -111,7 +110,7 @@ namespace ranges::__end {
__class_or_enum<remove_cvref_t<_Tp> > &&
requires(_Tp && __t) {
typename iterator_t<_Tp>;
{ _VSTD::__decay_copy(end(_VSTD::forward<_Tp>(__t))) } -> sentinel_for<iterator_t<_Tp> >;
{ _LIBCPP_AUTO_CAST(end(__t)) } -> sentinel_for<iterator_t<_Tp> >;
};
class __fn {
@ -126,17 +125,17 @@ namespace ranges::__end {
template <class _Tp>
requires __member_end<_Tp>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
noexcept(noexcept(_VSTD::__decay_copy(__t.end())))
noexcept(noexcept(_LIBCPP_AUTO_CAST(__t.end())))
{
return _VSTD::forward<_Tp>(__t).end();
return _LIBCPP_AUTO_CAST(__t.end());
}
template <class _Tp>
requires __unqualified_end<_Tp>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
noexcept(noexcept(_VSTD::__decay_copy(end(__t))))
noexcept(noexcept(_LIBCPP_AUTO_CAST(end(__t))))
{
return end(__t);
return _LIBCPP_AUTO_CAST(end(__t));
}
void operator()(auto&&) const = delete;

View File

@ -17,7 +17,7 @@
#include <__ranges/range_adaptor.h>
#include <__ranges/ref_view.h>
#include <__ranges/subrange.h>
#include <__utility/decay_copy.h>
#include <__utility/auto_cast.h>
#include <__utility/declval.h>
#include <__utility/forward.h>
#include <type_traits>
@ -38,9 +38,9 @@ namespace __all {
requires ranges::view<decay_t<_Tp>>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI
constexpr auto operator()(_Tp&& __t) const
noexcept(noexcept(_VSTD::__decay_copy(_VSTD::forward<_Tp>(__t))))
noexcept(noexcept(_LIBCPP_AUTO_CAST(_VSTD::forward<_Tp>(__t))))
{
return _VSTD::forward<_Tp>(__t);
return _LIBCPP_AUTO_CAST(_VSTD::forward<_Tp>(__t));
}
template<class _Tp>

View File

@ -13,8 +13,7 @@
#include <__iterator/concepts.h>
#include <__iterator/iterator_traits.h>
#include <__ranges/access.h>
#include <__utility/decay_copy.h>
#include <__utility/forward.h>
#include <__utility/auto_cast.h>
#include <concepts>
#include <type_traits>
@ -26,7 +25,6 @@ _LIBCPP_BEGIN_NAMESPACE_STD
#if !defined(_LIBCPP_HAS_NO_RANGES)
// clang-format off
namespace ranges {
template<class>
inline constexpr bool disable_sized_range = false;
@ -41,7 +39,7 @@ namespace __size {
template <class _Tp>
concept __member_size = __size_enabled<_Tp> && requires(_Tp&& __t) {
{ _VSTD::__decay_copy(_VSTD::forward<_Tp>(__t).size()) } -> __integer_like;
{ _LIBCPP_AUTO_CAST(__t.size()) } -> __integer_like;
};
template <class _Tp>
@ -50,7 +48,7 @@ namespace __size {
!__member_size<_Tp> &&
__class_or_enum<remove_cvref_t<_Tp>> &&
requires(_Tp&& __t) {
{ _VSTD::__decay_copy(size(_VSTD::forward<_Tp>(__t))) } -> __integer_like;
{ _LIBCPP_AUTO_CAST(size(__t)) } -> __integer_like;
};
template <class _Tp>
@ -76,14 +74,14 @@ namespace __size {
template <__member_size _Tp>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
noexcept(noexcept(_VSTD::forward<_Tp>(__t).size())) {
return _VSTD::forward<_Tp>(__t).size();
noexcept(noexcept(_LIBCPP_AUTO_CAST(__t.size()))) {
return _LIBCPP_AUTO_CAST(__t.size());
}
template <__unqualified_size _Tp>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
noexcept(noexcept(size(_VSTD::forward<_Tp>(__t)))) {
return size(_VSTD::forward<_Tp>(__t));
noexcept(noexcept(_LIBCPP_AUTO_CAST(size(__t)))) {
return _LIBCPP_AUTO_CAST(size(__t));
}
template<__difference _Tp>
@ -118,8 +116,6 @@ inline namespace __cpo {
} // namespace __cpo
} // namespace ranges
// clang-format off
#endif // !defined(_LIBCPP_HAS_NO_RANGES)
_LIBCPP_END_NAMESPACE_STD

View File

@ -7,29 +7,16 @@
//
//===----------------------------------------------------------------------===//
#ifndef _LIBCPP___UTILITY_DECAY_COPY_H
#define _LIBCPP___UTILITY_DECAY_COPY_H
#ifndef _LIBCPP___UTILITY_AUTO_CAST_H
#define _LIBCPP___UTILITY_AUTO_CAST_H
#include <__config>
#include <__utility/forward.h>
#include <type_traits>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
#pragma GCC system_header
#endif
_LIBCPP_BEGIN_NAMESPACE_STD
#define _LIBCPP_AUTO_CAST(expr) static_cast<typename decay<decltype((expr))>::type>(expr)
template <class _Tp>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR
typename decay<_Tp>::type __decay_copy(_Tp&& __t)
#if _LIBCPP_STD_VER > 17
noexcept(is_nothrow_convertible_v<_Tp, decay_t<_Tp>>)
#endif
{
return _VSTD::forward<_Tp>(__t);
}
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP___UTILITY_DECAY_COPY_H
#endif // _LIBCPP___UTILITY_AUTO_CAST_H

View File

@ -366,7 +366,7 @@ template <class R, class Alloc> struct uses_allocator<packaged_task<R>, Alloc>;
#include <__debug>
#include <__memory/allocator_arg_t.h>
#include <__memory/uses_allocator.h>
#include <__utility/decay_copy.h>
#include <__utility/auto_cast.h>
#include <__utility/forward.h>
#include <chrono>
#include <exception>
@ -2207,16 +2207,16 @@ async(launch __policy, _Fp&& __f, _Args&&... __args)
{
#endif
if (__does_policy_contain(__policy, launch::async))
return _VSTD::__make_async_assoc_state<_Rp>(_BF(_VSTD::__decay_copy(_VSTD::forward<_Fp>(__f)),
_VSTD::__decay_copy(_VSTD::forward<_Args>(__args))...));
return _VSTD::__make_async_assoc_state<_Rp>(_BF(_LIBCPP_AUTO_CAST(_VSTD::forward<_Fp>(__f)),
_LIBCPP_AUTO_CAST(_VSTD::forward<_Args>(__args))...));
#ifndef _LIBCPP_NO_EXCEPTIONS
}
catch ( ... ) { if (__policy == launch::async) throw ; }
#endif
if (__does_policy_contain(__policy, launch::deferred))
return _VSTD::__make_deferred_assoc_state<_Rp>(_BF(_VSTD::__decay_copy(_VSTD::forward<_Fp>(__f)),
_VSTD::__decay_copy(_VSTD::forward<_Args>(__args))...));
return _VSTD::__make_deferred_assoc_state<_Rp>(_BF(_LIBCPP_AUTO_CAST(_VSTD::forward<_Fp>(__f)),
_LIBCPP_AUTO_CAST(_VSTD::forward<_Args>(__args))...));
return future<_Rp>{};
}

View File

@ -921,8 +921,8 @@ module std [system] {
module __utility {
module as_const { private header "__utility/as_const.h" }
module auto_cast { private header "__utility/auto_cast.h" }
module cmp { private header "__utility/cmp.h" }
module decay_copy { private header "__utility/decay_copy.h" }
module declval { private header "__utility/declval.h" }
module exchange { private header "__utility/exchange.h" }
module forward { private header "__utility/forward.h" }

View File

@ -88,7 +88,6 @@ void sleep_for(const chrono::duration<Rep, Period>& rel_time);
#include <__mutex_base>
#include <__thread/poll_with_backoff.h>
#include <__threading_support>
#include <__utility/decay_copy.h>
#include <__utility/forward.h>
#include <chrono>
#include <cstddef>
@ -303,8 +302,8 @@ thread::thread(_Fp&& __f, _Args&&... __args)
typedef tuple<_TSPtr, typename decay<_Fp>::type, typename decay<_Args>::type...> _Gp;
unique_ptr<_Gp> __p(
new _Gp(_VSTD::move(__tsp),
_VSTD::__decay_copy(_VSTD::forward<_Fp>(__f)),
_VSTD::__decay_copy(_VSTD::forward<_Args>(__args))...));
_VSTD::forward<_Fp>(__f),
_VSTD::forward<_Args>(__args)...));
int __ec = _VSTD::__libcpp_thread_create(&__t_, &__thread_proxy<_Gp>, __p.get());
if (__ec == 0)
__p.release();

View File

@ -218,6 +218,7 @@ template <class T>
#include <__debug>
#include <__tuple>
#include <__utility/as_const.h>
#include <__utility/auto_cast.h>
#include <__utility/cmp.h>
#include <__utility/declval.h>
#include <__utility/exchange.h>

View File

@ -11,5 +11,5 @@
// WARNING: This test was generated by 'generate_private_header_tests.py'
// and should not be edited manually.
// expected-error@*:* {{use of private header from outside its module: '__utility/decay_copy.h'}}
#include <__utility/decay_copy.h>
// expected-error@*:* {{use of private header from outside its module: '__utility/auto_cast.h'}}
#include <__utility/auto_cast.h>

View File

@ -249,28 +249,27 @@ constexpr bool testBeginFunction() {
ASSERT_NOEXCEPT(std::ranges::begin(std::declval<int (&)[10]>()));
ASSERT_NOEXCEPT(std::ranges::cbegin(std::declval<int (&)[10]>()));
template<class T>
struct NoThrowMemberBegin {
T begin() const noexcept;
};
ASSERT_NOEXCEPT(std::ranges::begin(std::declval<NoThrowMemberBegin<int*>&>()));
ASSERT_NOEXCEPT(std::ranges::cbegin(std::declval<NoThrowMemberBegin<int*>&>()));
ASSERT_NOT_NOEXCEPT(std::ranges::begin(std::declval<NoThrowMemberBegin<ThrowingIterator<int>>&>()));
ASSERT_NOT_NOEXCEPT(std::ranges::cbegin(std::declval<NoThrowMemberBegin<ThrowingIterator<int>>&>()));
ThrowingIterator<int> begin() const noexcept; // auto(t.begin()) doesn't throw
} ntmb;
static_assert(noexcept(std::ranges::begin(ntmb)));
static_assert(noexcept(std::ranges::cbegin(ntmb)));
template<class T>
struct NoThrowADLBegin {
friend T begin(NoThrowADLBegin&) noexcept { return T{}; }
friend T begin(NoThrowADLBegin const&) noexcept { return T{}; }
};
ASSERT_NOEXCEPT(std::ranges::begin(std::declval<NoThrowADLBegin<int*>&>()));
ASSERT_NOEXCEPT(std::ranges::cbegin(std::declval<NoThrowADLBegin<int*>&>()));
ASSERT_NOT_NOEXCEPT(std::ranges::begin(std::declval<NoThrowADLBegin<ThrowingIterator<int>>&>()));
ASSERT_NOT_NOEXCEPT(std::ranges::cbegin(std::declval<NoThrowADLBegin<ThrowingIterator<int>>&>()));
friend ThrowingIterator<int> begin(NoThrowADLBegin&) noexcept; // auto(begin(t)) doesn't throw
friend ThrowingIterator<int> begin(const NoThrowADLBegin&) noexcept;
} ntab;
static_assert(noexcept(std::ranges::begin(ntab)));
static_assert(noexcept(std::ranges::cbegin(ntab)));
struct NoThrowMemberBeginReturnsRef {
ThrowingIterator<int>& begin() const noexcept; // auto(t.begin()) may throw
} ntmbrr;
static_assert(!noexcept(std::ranges::begin(ntmbrr)));
static_assert(!noexcept(std::ranges::cbegin(ntmbrr)));
struct BeginReturnsArrayRef {
auto begin() const noexcept -> int(&)[10];
auto end() const noexcept -> int(&)[10];
} brar;
static_assert(noexcept(std::ranges::begin(brar)));
static_assert(noexcept(std::ranges::cbegin(brar)));

View File

@ -283,34 +283,34 @@ constexpr bool testEndFunction() {
ASSERT_NOEXCEPT(std::ranges::end(std::declval<int (&)[10]>()));
ASSERT_NOEXCEPT(std::ranges::cend(std::declval<int (&)[10]>()));
template<class T>
struct NoThrowMemberEnd {
T begin() const;
T end() const noexcept;
};
ASSERT_NOEXCEPT(std::ranges::end(std::declval<NoThrowMemberEnd<int*>&>()));
ASSERT_NOEXCEPT(std::ranges::cend(std::declval<NoThrowMemberEnd<int*>&>()));
ASSERT_NOT_NOEXCEPT(std::ranges::end(std::declval<NoThrowMemberEnd<ThrowingIterator<int>>&>()));
ASSERT_NOT_NOEXCEPT(std::ranges::cend(std::declval<NoThrowMemberEnd<ThrowingIterator<int>>&>()));
ThrowingIterator<int> begin() const;
ThrowingIterator<int> end() const noexcept; // auto(t.end()) doesn't throw
} ntme;
static_assert(noexcept(std::ranges::end(ntme)));
static_assert(noexcept(std::ranges::cend(ntme)));
template<class T>
struct NoThrowADLEnd {
T begin() const;
friend T end(NoThrowADLEnd&) noexcept { return T{}; }
friend T end(NoThrowADLEnd const&) noexcept { return T{}; }
};
ASSERT_NOEXCEPT(std::ranges::end(std::declval<NoThrowADLEnd<int*>&>()));
ASSERT_NOEXCEPT(std::ranges::cend(std::declval<NoThrowADLEnd<int*>&>()));
ASSERT_NOT_NOEXCEPT(std::ranges::end(std::declval<NoThrowADLEnd<ThrowingIterator<int>>&>()));
ASSERT_NOT_NOEXCEPT(std::ranges::cend(std::declval<NoThrowADLEnd<ThrowingIterator<int>>&>()));
ThrowingIterator<int> begin() const;
friend ThrowingIterator<int> end(NoThrowADLEnd&) noexcept; // auto(end(t)) doesn't throw
friend ThrowingIterator<int> end(const NoThrowADLEnd&) noexcept;
} ntae;
static_assert(noexcept(std::ranges::end(ntae)));
static_assert(noexcept(std::ranges::cend(ntae)));
struct BeginReturnsArrayRef {
struct NoThrowMemberEndReturnsRef {
ThrowingIterator<int> begin() const;
ThrowingIterator<int>& end() const noexcept; // auto(t.end()) may throw
} ntmerr;
static_assert(!noexcept(std::ranges::end(ntmerr)));
static_assert(!noexcept(std::ranges::cend(ntmerr)));
struct EndReturnsArrayRef {
auto begin() const noexcept -> int(&)[10];
auto end() const noexcept -> int(&)[10];
} brar;
static_assert(noexcept(std::ranges::end(brar)));
static_assert(noexcept(std::ranges::cend(brar)));
} erar;
static_assert(noexcept(std::ranges::end(erar)));
static_assert(noexcept(std::ranges::cend(erar)));
int main(int, char**) {
testArray();

View File

@ -135,7 +135,7 @@ struct SizeFunctionSigned {
bool constexpr testHasSizeFunction() {
assert(std::ranges::size(SizeFunction()) == 42);
ASSERT_SAME_TYPE(decltype(std::ranges::size(SizeFunction())), size_t);
assert(std::ranges::size(MoveOnlySizeFunction()) == 42);
static_assert(!std::is_invocable_v<RangeSizeT, MoveOnlySizeFunction>);
assert(std::ranges::size(EnumSizeFunction()) == 42);
assert(std::ranges::size(SizeFunctionConst()) == 42);

View File

@ -21,7 +21,7 @@
// (It's fixed in the upcoming Clang 14, by https://reviews.llvm.org/D109651.)
// Prior to the fix, when statically linked, the unwind info for the two
// (default and overridden) operator new implementations clash.
// XFAIL: target={{.+}}-windows-gnu && !windows-dll && clang-13
// UNSUPPORTED: target={{.+}}-windows-gnu && !windows-dll && clang-13
#include <thread>
#include <new>