Bug 1624495 - Support move only arguments such as UniquePtr in FunctionRef. r=botond
authorChris Fronk <fronkc1@gmail.com>
Thu, 21 May 2020 01:56:57 +0000
changeset 531373 cf2455791acb8fb01373cf82109e39f9aafafc03
parent 531372 8fdc6cd9df8ba4cdd98380b51cc49a2f8a9ef867
child 531374 d10fc18b00d38c0c355797b8cce07d6d49dbfe23
push id37438
push userabutkovits@mozilla.com
push dateThu, 21 May 2020 09:36:57 +0000
treeherdermozilla-central@2d00a1a6495c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbotond
bugs1624495
milestone78.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1624495 - Support move only arguments such as UniquePtr in FunctionRef. r=botond Differential Revision: https://phabricator.services.mozilla.com/D68707
mfbt/FunctionRef.h
mfbt/tests/TestFunctionRef.cpp
--- a/mfbt/FunctionRef.h
+++ b/mfbt/FunctionRef.h
@@ -12,16 +12,17 @@
 
 #ifndef mozilla_FunctionRef_h
 #define mozilla_FunctionRef_h
 
 #include "mozilla/OperatorNewExtensions.h"  // mozilla::NotNull, ::operator new
 
 #include <cstddef>      // std::nullptr_t
 #include <type_traits>  // std::{declval,integral_constant}, std::is_{convertible,same,void}_v, std::{enable_if,remove_reference,remove_cv}_t
+#include <utility>      // std::forward
 
 // This concept and its implementation are substantially inspired by foonathan's
 // prior art:
 //
 // https://foonathan.net/2017/01/function-ref-implementation/
 // https://github.com/foonathan/type_safe/blob/2017851053f8dd268372f1612865792c5c621570/include/type_safe/reference.hpp
 
 namespace mozilla {
@@ -130,17 +131,17 @@ class FunctionRef<Ret(Params...)> {
     // |nullptr|.
     void* mObject;
   } mPayload;
 
   template <typename RealFuncPtr>
   static Ret CallFunctionPointer(const Payload& aPayload,
                                  Params... aParams) noexcept {
     auto func = reinterpret_cast<RealFuncPtr>(aPayload.mFuncPtr);
-    return static_cast<Ret>(func(static_cast<Params>(aParams)...));
+    return static_cast<Ret>(func(std::forward<Params>(aParams)...));
   }
 
   template <typename Ret2, typename... Params2>
   FunctionRef(detail::MatchingFunctionPointerTag, Ret2 (*aFuncPtr)(Params2...))
       : mAdaptor(&CallFunctionPointer<Ret2 (*)(Params2...)>) {
     ::new (KnownNotNull, &mPayload.mFuncPtr)
         FuncPtr(reinterpret_cast<FuncPtr>(aFuncPtr));
   }
@@ -173,16 +174,21 @@ class FunctionRef<Ret(Params...)> {
       typename = detail::EnableFunctionTag<detail::MatchingFunctorTag, Callable,
                                            Ret, Params...>,
       typename std::enable_if_t<!std::is_same_v<
           typename std::remove_reference_t<typename std::remove_cv_t<Callable>>,
           FunctionRef>>* = nullptr>
   MOZ_IMPLICIT FunctionRef(Callable& aCallable) noexcept
       : mAdaptor([](const Payload& aPayload, Params... aParams) {
           auto& func = *static_cast<Callable*>(aPayload.mObject);
+          // Unable to use std::forward here due to llvm windows bug
+          // https://bugs.llvm.org/show_bug.cgi?id=28299
+          //
+          // This prevents use of move-only arguments for functors and lambdas.
+          // Move only arguments can be used when using function pointers
           return static_cast<Ret>(func(static_cast<Params>(aParams)...));
         }) {
     ::new (KnownNotNull, &mPayload.mObject) void*(&aCallable);
   }
 
   /**
    * Constructs a |FunctionRef| from an value callable with |Params| arguments,
    * that returns a type convertible to |Ret|, where the callable is stateless
@@ -203,17 +209,17 @@ class FunctionRef<Ret(Params...)> {
   template <typename Callable,
             typename = detail::EnableFunctionTag<
                 detail::MatchingFunctionPointerTag, Callable, Ret, Params...>>
   MOZ_IMPLICIT FunctionRef(const Callable& aCallable) noexcept
       : FunctionRef(detail::MatchingFunctionPointerTag{}, +aCallable) {}
 
   /** Call the callable stored in this with the given arguments. */
   Ret operator()(Params... params) const {
-    return mAdaptor(mPayload, static_cast<Params>(params)...);
+    return mAdaptor(mPayload, std::forward<Params>(params)...);
   }
 
   /** Return true iff this wasn't created from |nullptr|. */
   explicit operator bool() const noexcept { return mAdaptor != nullptr; }
 };
 
 } /* namespace mozilla */
 
--- a/mfbt/tests/TestFunctionRef.cpp
+++ b/mfbt/tests/TestFunctionRef.cpp
@@ -1,29 +1,32 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/Assertions.h"
 #include "mozilla/FunctionRef.h"
+#include "mozilla/UniquePtr.h"
 
 #define CHECK(c)                                       \
   do {                                                 \
     bool cond = !!(c);                                 \
     MOZ_RELEASE_ASSERT(cond, "Failed assertion: " #c); \
   } while (false)
 
 int addConstRefs(const int& arg1, const int& arg2) { return arg1 + arg2; }
 
 void incrementPointer(int* arg) { (*arg)++; }
 
 int increment(int arg) { return arg + 1; }
 
+int incrementUnique(mozilla::UniquePtr<int> ptr) { return *ptr + 1; }
+
 static bool helloWorldCalled = false;
 
 void helloWorld() { helloWorldCalled = true; }
 
 struct S {
   static int increment(int arg) { return arg + 1; }
 };
 
@@ -109,24 +112,31 @@ static void TestImplicitLambdaTypeConver
 }
 
 static void TestImplicitFunctionPointerTypeConversion() {
   mozilla::FunctionRef<long(short)> f = &increment;
   short x = 1;
   CHECK(f(x) == 2);
 }
 
+static void TestMoveOnlyArguments() {
+  mozilla::FunctionRef<int(mozilla::UniquePtr<int>)> f(&incrementUnique);
+
+  CHECK(f(mozilla::MakeUnique<int>(5)) == 6);
+}
+
 int main() {
   TestNonmemberFunction();
   TestStaticMemberFunction();
   TestFunctionObject();
   TestLambda();
   TestOperatorBool();
   TestReferenceParameters();
   TestPointerParameters();
   TestVoidNoParameters();
   TestImplicitFunctorTypeConversion();
   TestImplicitLambdaTypeConversion();
   TestImplicitFunctionPointerTypeConversion();
+  TestMoveOnlyArguments();
 
   printf("TestFunctionRef OK!\n");
   return 0;
 }