From d3e97ebae9f1e8aaa56287fcc6d0cd792e6bd341 Mon Sep 17 00:00:00 2001 From: Eric Niebler Date: Mon, 18 Sep 2017 15:21:34 -0700 Subject: [PATCH] fix strange recursive std::is_constructible instantiation involving the Function converting constructor Summary: In rare and obscure cases, the fact that `folly::Function`'s perfectly-forwarding, implicit converting constructor was SFINAE-ing on the argument's constructibility was causing a recursive template instantiation of std::is_constructible. Switch to passing the argument by value to avoid the need to test for constructibility. Reviewed By: yfeldblum Differential Revision: D5847034 fbshipit-source-id: ce2ad1d2490206c6cae84c17544bd9fcd6ff47bb --- folly/Function.h | 43 +++++++++++++++++-------------------- folly/test/FunctionTest.cpp | 4 ++-- 2 files changed, 22 insertions(+), 25 deletions(-) diff --git a/folly/Function.h b/folly/Function.h index 05f8fbf7..55d7024c 100644 --- a/folly/Function.h +++ b/folly/Function.h @@ -246,22 +246,21 @@ union Data { std::aligned_storage<6 * sizeof(void*)>::type tiny; }; -template ::type> +template using IsSmall = Conjunction< - std::integral_constant, - std::is_nothrow_move_constructible>; + std::integral_constant, + std::is_nothrow_move_constructible>; using SmallTag = std::true_type; using HeapTag = std::false_type; -template +template struct NotFunction : std::true_type {}; -template +template struct NotFunction> : std::false_type {}; -template ::type> -using DecayIfConstructible = typename std::enable_if< - Conjunction, std::is_constructible>::value, - FunT>::type; +template +using EnableIfNotFunction = + typename std::enable_if::value>::type; struct CoerceTag {}; @@ -310,17 +309,16 @@ struct FunctionTraits { } ReturnType operator()(Args... args) { - auto& fn = *static_cast*>(this); + auto& fn = *static_cast*>(this); return fn.call_(fn.data_, static_cast(args)...); } class SharedProxy { - std::shared_ptr> sp_; + std::shared_ptr> sp_; public: - explicit SharedProxy(Function&& func) - : sp_(std::make_shared>( - std::move(func))) {} + explicit SharedProxy(Function&& func) + : sp_(std::make_shared>(std::move(func))) {} ReturnType operator()(Args&&... args) const { return (*sp_)(static_cast(args)...); } @@ -356,17 +354,16 @@ struct FunctionTraits { } ReturnType operator()(Args... args) const { - auto& fn = *static_cast*>(this); + auto& fn = *static_cast*>(this); return fn.call_(fn.data_, static_cast(args)...); } class SharedProxy { - std::shared_ptr> sp_; + std::shared_ptr> sp_; public: - explicit SharedProxy(Function&& func) - : sp_(std::make_shared>( - std::move(func))) {} + explicit SharedProxy(Function&& func) + : sp_(std::make_shared>(std::move(func))) {} ReturnType operator()(Args&&... args) const { return (*sp_)(static_cast(args)...); } @@ -529,10 +526,10 @@ class Function final : private detail::function::FunctionTraits { */ template < typename Fun, - typename FunT = detail::function::DecayIfConstructible, + typename = detail::function::EnableIfNotFunction, typename = typename Traits::template ResultOf> - /* implicit */ Function(Fun&& fun) noexcept( - IsSmall::value && noexcept(FunT(std::declval()))) + /* implicit */ Function(Fun fun) noexcept( + IsSmall::value && noexcept(Fun(std::declval()))) : Function(static_cast(fun), IsSmall{}) {} /** @@ -603,7 +600,7 @@ class Function final : private detail::function::FunctionTraits { * selected by overload resolution when `fun` is not a compatible function. */ template ()))> - Function& operator=(Fun&& fun) noexcept( + Function& operator=(Fun fun) noexcept( noexcept(/* implicit */ Function(std::declval()))) { // Doing this in place is more efficient when we can do so safely. if (noexcept(/* implicit */ Function(std::declval()))) { diff --git a/folly/test/FunctionTest.cpp b/folly/test/FunctionTest.cpp index a7391009..a062d933 100644 --- a/folly/test/FunctionTest.cpp +++ b/folly/test/FunctionTest.cpp @@ -584,7 +584,7 @@ TEST(Function, CaptureCopyMoveCount) { Function uf1 = std::move(lambda1); // Max copies: 0. Max copy+moves: 2. - EXPECT_LE(cmt.moveCount() + cmt.copyCount(), 2); + EXPECT_LE(cmt.moveCount() + cmt.copyCount(), 3); EXPECT_LE(cmt.copyCount(), 0); cmt.resetCounters(); @@ -596,7 +596,7 @@ TEST(Function, CaptureCopyMoveCount) { Function uf2 = lambda2; // Max copies: 1. Max copy+moves: 2. - EXPECT_LE(cmt.moveCount() + cmt.copyCount(), 2); + EXPECT_LE(cmt.moveCount() + cmt.copyCount(), 3); EXPECT_LE(cmt.copyCount(), 1); // Invoking Function must not make copies/moves of the callable -- 2.34.1