diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -342,6 +342,7 @@ __tuple __undef_macros __utility/as_const.h + __utility/auto_cast.h __utility/cmp.h __utility/decay_copy.h __utility/declval.h diff --git a/libcxx/include/__ranges/access.h b/libcxx/include/__ranges/access.h --- a/libcxx/include/__ranges/access.h +++ b/libcxx/include/__ranges/access.h @@ -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 #include @@ -44,7 +43,7 @@ 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; @@ -56,7 +55,7 @@ __can_borrow<_Tp> && __class_or_enum > && requires(_Tp && __t) { - { _VSTD::__decay_copy(begin(__t)) } -> input_or_output_iterator; + { _LIBCPP_AUTO_CAST(begin(__t)) } -> input_or_output_iterator; }; struct __fn { @@ -75,17 +74,17 @@ template 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 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; @@ -108,7 +107,7 @@ __can_borrow<_Tp> && requires(_Tp&& __t) { typename iterator_t<_Tp>; - { _VSTD::__decay_copy(_VSTD::forward<_Tp>(__t).end()) } -> sentinel_for >; + { _LIBCPP_AUTO_CAST(__t.end()) } -> sentinel_for >; }; void end(auto&) = delete; @@ -121,7 +120,7 @@ __class_or_enum > && requires(_Tp && __t) { typename iterator_t<_Tp>; - { _VSTD::__decay_copy(end(_VSTD::forward<_Tp>(__t))) } -> sentinel_for >; + { _LIBCPP_AUTO_CAST(end(__t)) } -> sentinel_for >; }; class __fn { @@ -140,17 +139,17 @@ template 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 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; diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h --- a/libcxx/include/__ranges/size.h +++ b/libcxx/include/__ranges/size.h @@ -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 #include @@ -26,7 +25,6 @@ #if !defined(_LIBCPP_HAS_NO_RANGES) -// clang-format off namespace ranges { template inline constexpr bool disable_sized_range = false; @@ -41,7 +39,7 @@ template 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 @@ -50,7 +48,7 @@ !__member_size<_Tp> && __class_or_enum> && requires(_Tp&& __t) { - { _VSTD::__decay_copy(size(_VSTD::forward<_Tp>(__t))) } -> __integer_like; + { _LIBCPP_AUTO_CAST(size(__t)) } -> __integer_like; }; template @@ -76,14 +74,14 @@ 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 @@ } // namespace __cpo } // namespace ranges -// clang-format off - #endif // !defined(_LIBCPP_HAS_NO_RANGES) _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/__utility/auto_cast.h b/libcxx/include/__utility/auto_cast.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__utility/auto_cast.h @@ -0,0 +1,22 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___UTILITY_AUTO_CAST_H +#define _LIBCPP___UTILITY_AUTO_CAST_H + +#include <__config> +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +#define _LIBCPP_AUTO_CAST(expr) static_cast>(expr) + +#endif // _LIBCPP___UTILITY_AUTO_CAST_H diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -894,6 +894,7 @@ 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" } diff --git a/libcxx/include/utility b/libcxx/include/utility --- a/libcxx/include/utility +++ b/libcxx/include/utility @@ -218,7 +218,9 @@ #include <__debug> #include <__tuple> #include <__utility/as_const.h> +#include <__utility/auto_cast.h> #include <__utility/cmp.h> +#include <__utility/decay_copy.h> #include <__utility/declval.h> #include <__utility/exchange.h> #include <__utility/forward.h> diff --git a/libcxx/test/std/ranges/range.access/range.access.begin/begin.pass.cpp b/libcxx/test/std/ranges/range.access/range.access.begin/begin.pass.cpp --- a/libcxx/test/std/ranges/range.access/range.access.begin/begin.pass.cpp +++ b/libcxx/test/std/ranges/range.access/range.access.begin/begin.pass.cpp @@ -245,28 +245,27 @@ ASSERT_NOEXCEPT(std::ranges::begin(std::declval())); ASSERT_NOEXCEPT(std::ranges::cbegin(std::declval())); -template struct NoThrowMemberBegin { - T begin() const noexcept; -}; -ASSERT_NOEXCEPT(std::ranges::begin(std::declval&>())); -ASSERT_NOEXCEPT(std::ranges::cbegin(std::declval&>())); -ASSERT_NOT_NOEXCEPT(std::ranges::begin(std::declval>&>())); -ASSERT_NOT_NOEXCEPT(std::ranges::cbegin(std::declval>&>())); + ThrowingIterator 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 struct NoThrowADLBegin { - friend T begin(NoThrowADLBegin&) noexcept { return T{}; } - friend T begin(NoThrowADLBegin const&) noexcept { return T{}; } -}; -ASSERT_NOEXCEPT(std::ranges::begin(std::declval&>())); -ASSERT_NOEXCEPT(std::ranges::cbegin(std::declval&>())); -ASSERT_NOT_NOEXCEPT(std::ranges::begin(std::declval>&>())); -ASSERT_NOT_NOEXCEPT(std::ranges::cbegin(std::declval>&>())); + friend ThrowingIterator begin(NoThrowADLBegin&) noexcept; // auto(begin(t)) doesn't throw + friend ThrowingIterator begin(const NoThrowADLBegin&) noexcept; +} ntab; +static_assert(noexcept(std::ranges::begin(ntab))); +static_assert(noexcept(std::ranges::cbegin(ntab))); + +struct NoThrowMemberBeginReturnsRef { + ThrowingIterator& 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))); diff --git a/libcxx/test/std/ranges/range.access/range.access.end/end.pass.cpp b/libcxx/test/std/ranges/range.access/range.access.end/end.pass.cpp --- a/libcxx/test/std/ranges/range.access/range.access.end/end.pass.cpp +++ b/libcxx/test/std/ranges/range.access/range.access.end/end.pass.cpp @@ -277,34 +277,34 @@ ASSERT_NOEXCEPT(std::ranges::end(std::declval())); ASSERT_NOEXCEPT(std::ranges::cend(std::declval())); -template struct NoThrowMemberEnd { - T begin() const; - T end() const noexcept; -}; -ASSERT_NOEXCEPT(std::ranges::end(std::declval&>())); -ASSERT_NOEXCEPT(std::ranges::cend(std::declval&>())); -ASSERT_NOT_NOEXCEPT(std::ranges::end(std::declval>&>())); -ASSERT_NOT_NOEXCEPT(std::ranges::cend(std::declval>&>())); + ThrowingIterator begin() const; + ThrowingIterator 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 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&>())); -ASSERT_NOEXCEPT(std::ranges::cend(std::declval&>())); -ASSERT_NOT_NOEXCEPT(std::ranges::end(std::declval>&>())); -ASSERT_NOT_NOEXCEPT(std::ranges::cend(std::declval>&>())); - -struct BeginReturnsArrayRef { + ThrowingIterator begin() const; + friend ThrowingIterator end(NoThrowADLEnd&) noexcept; // auto(end(t)) doesn't throw + friend ThrowingIterator end(const NoThrowADLEnd&) noexcept; +} ntae; +static_assert(noexcept(std::ranges::end(ntae))); +static_assert(noexcept(std::ranges::cend(ntae))); + +struct NoThrowMemberEndReturnsRef { + ThrowingIterator begin() const; + ThrowingIterator& 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(); diff --git a/libcxx/test/std/ranges/range.access/range.prim/size.pass.cpp b/libcxx/test/std/ranges/range.access/range.prim/size.pass.cpp --- a/libcxx/test/std/ranges/range.access/range.prim/size.pass.cpp +++ b/libcxx/test/std/ranges/range.access/range.prim/size.pass.cpp @@ -124,7 +124,7 @@ 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); assert(std::ranges::size(EnumSizeFunction()) == 42); assert(std::ranges::size(SizeFunctionConst()) == 42);