diff --git a/llvm/include/llvm/ADT/STLExtras.h b/llvm/include/llvm/ADT/STLExtras.h --- a/llvm/include/llvm/ADT/STLExtras.h +++ b/llvm/include/llvm/ADT/STLExtras.h @@ -50,14 +50,67 @@ // using SFINAE to test for the existence of member functions. template struct SameType; +namespace adl_detail { + +using std::begin; + +template +constexpr auto begin_impl(RangeT &&range) + -> decltype(begin(std::forward(range))) { + return begin(std::forward(range)); +} + +using std::end; + +template +constexpr auto end_impl(RangeT &&range) + -> decltype(end(std::forward(range))) { + return end(std::forward(range)); +} + +using std::swap; + +template +constexpr void swap_impl(T &&lhs, + T &&rhs) noexcept(noexcept(swap(std::declval(), + std::declval()))) { + swap(std::forward(lhs), std::forward(rhs)); +} + +} // end namespace adl_detail + +/// Returns the begin iterator to \p range using `std::begin` and +/// function found through Argument-Dependent Lookup (ADL). +template +constexpr auto adl_begin(RangeT &&range) + -> decltype(adl_detail::begin_impl(std::forward(range))) { + return adl_detail::begin_impl(std::forward(range)); +} + +/// Returns the end iterator to \p range using `std::end` and +/// functions found through Argument-Dependent Lookup (ADL). +template +constexpr auto adl_end(RangeT &&range) + -> decltype(adl_detail::end_impl(std::forward(range))) { + return adl_detail::end_impl(std::forward(range)); +} + +/// Swaps \p lhs with \p rhs using `std::swap` and functions found through +/// Argument-Dependent Lookup (ADL). +template +constexpr void adl_swap(T &&lhs, T &&rhs) noexcept( + noexcept(adl_detail::swap_impl(std::declval(), std::declval()))) { + adl_detail::swap_impl(std::forward(lhs), std::forward(rhs)); +} + namespace detail { template -using IterOfRange = decltype(std::begin(std::declval())); +using IterOfRange = decltype(adl_begin(std::declval())); template using ValueOfRange = - std::remove_reference_t()))>; + std::remove_reference_t()))>; } // end namespace detail @@ -333,48 +386,6 @@ } // namespace callable_detail -namespace adl_detail { - -using std::begin; - -template -decltype(auto) adl_begin(ContainerTy &&container) { - return begin(std::forward(container)); -} - -using std::end; - -template -decltype(auto) adl_end(ContainerTy &&container) { - return end(std::forward(container)); -} - -using std::swap; - -template -void adl_swap(T &&lhs, T &&rhs) noexcept(noexcept(swap(std::declval(), - std::declval()))) { - swap(std::forward(lhs), std::forward(rhs)); -} - -} // end namespace adl_detail - -template -decltype(auto) adl_begin(ContainerTy &&container) { - return adl_detail::adl_begin(std::forward(container)); -} - -template -decltype(auto) adl_end(ContainerTy &&container) { - return adl_detail::adl_end(std::forward(container)); -} - -template -void adl_swap(T &&lhs, T &&rhs) noexcept( - noexcept(adl_detail::adl_swap(std::declval(), std::declval()))) { - adl_detail::adl_swap(std::forward(lhs), std::forward(rhs)); -} - /// Returns true if the given container only contains a single element. template bool hasSingleElement(ContainerTy &&C) { auto B = std::begin(C), E = std::end(C); diff --git a/llvm/unittests/ADT/STLExtrasTest.cpp b/llvm/unittests/ADT/STLExtrasTest.cpp --- a/llvm/unittests/ADT/STLExtrasTest.cpp +++ b/llvm/unittests/ADT/STLExtrasTest.cpp @@ -12,6 +12,7 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +#include #include #include #include @@ -394,6 +395,10 @@ lhs.swap_val = "lhs"; rhs.swap_val = "rhs"; } + +struct requires_move {}; +int *begin(requires_move &&) { return nullptr; } +int *end(requires_move &&) { return nullptr; } } // namespace some_namespace TEST(STLExtrasTest, ADLTest) { @@ -409,7 +414,25 @@ int count = 0; llvm::for_each(s, [&count](int) { ++count; }); - EXPECT_EQ(5, count); + EXPECT_EQ(count, 5); +} + +TEST(STLExtrasTest, ADLTestTemporaryRange) { + EXPECT_EQ(adl_begin(some_namespace::requires_move{}), nullptr); + EXPECT_EQ(adl_end(some_namespace::requires_move{}), nullptr); +} + +TEST(STLExtrasTest, ADLTestConstexpr) { + // `std::begin`/`std::end` are marked as `constexpr`; check that + // `adl_begin`/`adl_end` also work in constant-evaluated contexts. + static constexpr int c_arr[] = {7, 8, 9}; + static_assert(adl_begin(c_arr) == c_arr); + static_assert(adl_end(c_arr) == c_arr + 3); + + static constexpr std::array std_arr = {1, 2}; + static_assert(adl_begin(std_arr) == std_arr.begin()); + static_assert(adl_end(std_arr) == std_arr.end()); + SUCCEED(); } TEST(STLExtrasTest, DropBeginTest) {