Remove folly::Future conversion constructor
authorNick Wolchko <nwolchko@fb.com>
Mon, 9 Jan 2017 17:20:48 +0000 (09:20 -0800)
committerFacebook Github Bot <facebook-github-bot@users.noreply.github.com>
Mon, 9 Jan 2017 17:33:03 +0000 (09:33 -0800)
Summary:
This constructor is unsafe. The check it uses before doing the comparison isn't a safe enough check to see if the cast is valid. For example, this is broken in the presence of multiple inheritance because it doesn't adjust the pointer offset to the correct vtable.

e.g.
  struct A {
    virtual ~A() {};
    virtual void doSomething() = 0;
  };
  struct B {
    virtual ~B() {}
    virtual void doSomethingElse() = 0;
  };
  struct C : public B, public A {
    virtual ~C() {}
    void doSomething() override {
      std::cout << "Something!" << std::endl;
    }
    void doSomethingElse() override {
      std::cout << "Something Else!" << std::endl;
    }
  };
  int main (int argc, char **argv) {
    auto c = folly::makeFuture<std::shared_ptr<C>>(std::make_shared<C>());
    folly::Future<std::shared_ptr<A>> a = std::move(c);
    a.get()->doSomething();
    return 0;
  }
This code will print "Something else!" when run.

Reviewed By: siyengar

Differential Revision: D3679673

fbshipit-source-id: dcbf40ca82d458f17ee11191591f8b8daf58c919

folly/futures/Future-inl.h
folly/futures/Future.h
folly/futures/detail/Core.h
folly/futures/test/ConversionTest.cpp [deleted file]
folly/test/Makefile.am

index f2c036994df7bba62837d708fa784a3cddd0bdeb..6ac7acdb561ca1e43d184f62c8b5fc7fab545b52 100644 (file)
@@ -62,20 +62,6 @@ Future<T>& Future<T>::operator=(Future<T>&& other) noexcept {
   return *this;
 }
 
-template <class T>
-template <typename U, typename>
-Future<T>::Future(Future<U>&& other) noexcept
-    : core_(detail::Core<T>::convert(other.core_)) {
-  other.core_ = nullptr;
-}
-
-template <class T>
-template <typename U, typename>
-Future<T>& Future<T>::operator=(Future<U>&& other) noexcept {
-  std::swap(core_, detail::Core<T>::convert(other.core_));
-  return *this;
-}
-
 template <class T>
 template <class T2, typename>
 Future<T>::Future(T2&& val)
index ec2a8ac64dd736f10191064393523ccf715b2c5b..2acc1b94cd85cff51b0dbb17c841a45fba6995a6 100644 (file)
@@ -53,18 +53,6 @@ class Future {
   Future(Future&&) noexcept;
   Future& operator=(Future&&) noexcept;
 
-  // conversion constructors
-  template <
-      class U,
-      typename = typename std::enable_if<std::is_convertible<U, T>::value &&
-                                         sizeof(U) == sizeof(T)>::type>
-  /* implicit */ Future(Future<U>&&) noexcept;
-  template <
-      class U,
-      typename = typename std::enable_if<std::is_convertible<U, T>::value &&
-                                         sizeof(U) == sizeof(T)>::type>
-  /* implicit */ Future& operator=(Future<U>&&) noexcept;
-
   /// Construct a Future from a value (perfect forwarding)
   template <class T2 = T, typename =
             typename std::enable_if<
index 7822fa9a39de5b1284e7ac8218a00fa1c4054d72..41fcdab2106376fe8939fd883741692377801f2e 100644 (file)
@@ -100,26 +100,6 @@ class Core final {
   Core(Core&&) noexcept = delete;
   Core& operator=(Core&&) = delete;
 
-  // Core is assumed to be convertible only if the type is convertible
-  // and the size is the same. This is a compromise for the complexity
-  // of having to make Core truly have a conversion constructor which
-  // would cause various other problems.
-  // If we made Core move constructible then we would need to update the
-  // Promise and Future with the location of the new Core. This is complex
-  // and may be inefficient.
-  // Core should only be modified so that for size(T) == size(U),
-  // sizeof(Core<T>) == size(Core<U>).
-  // This assumption is used as a proxy to make sure that
-  // the members of Core<T> and Core<U> line up so that we can use a
-  // reinterpret cast.
-  template <
-      class U,
-      typename = typename std::enable_if<std::is_convertible<U, T>::value &&
-                                         sizeof(U) == sizeof(T)>::type>
-  static Core<T>* convert(Core<U>* from) {
-    return reinterpret_cast<Core<T>*>(from);
-  }
-
   /// May call from any thread
   bool hasResult() const {
     switch (fsm_.getState()) {
@@ -417,9 +397,6 @@ class Core final {
     }
   }
 
-  // Core should only be modified so that for size(T) == size(U),
-  // sizeof(Core<T>) == size(Core<U>).
-  // See Core::convert for details.
 
   folly::Function<void(Try<T>&&)> callback_;
   // place result_ next to increase the likelihood that the value will be
diff --git a/folly/futures/test/ConversionTest.cpp b/folly/futures/test/ConversionTest.cpp
deleted file mode 100644 (file)
index 59f4820..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright 2017 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <folly/futures/Future.h>
-#include <folly/portability/GTest.h>
-
-#include <type_traits>
-
-using namespace folly;
-
-class A {
- public:
-  virtual ~A() {}
-};
-
-class B : public A {
- public:
-  explicit B(std::string msg) : msg_(msg) {}
-
-  const std::string& getMsg() { return msg_; }
-  std::string msg_;
-};
-
-TEST(Convert, subclassConstruct) {
-  Future<std::unique_ptr<A>> future =
-      makeFuture<std::unique_ptr<B>>(folly::make_unique<B>("hello world"));
-  std::unique_ptr<A> a = future.get();
-  A* aptr = a.get();
-  B* b = dynamic_cast<B*>(aptr);
-  EXPECT_NE(nullptr, b);
-  EXPECT_EQ("hello world", b->getMsg());
-}
-
-TEST(Convert, subclassAssign) {
-  Future<std::unique_ptr<B>> f1 =
-      makeFuture<std::unique_ptr<B>>(folly::make_unique<B>("hello world"));
-  Future<std::unique_ptr<A>> f2 = std::move(f1);
-  std::unique_ptr<A> a = f2.get();
-  A* aptr = a.get();
-  B* b = dynamic_cast<B*>(aptr);
-  EXPECT_NE(nullptr, b);
-  EXPECT_EQ("hello world", b->getMsg());
-}
-
-TEST(Convert, ConversionTests) {
-  static_assert(std::is_convertible<Future<std::unique_ptr<B>>,
-                                    Future<std::unique_ptr<A>>>::value,
-                "Unique ptr not convertible");
-  static_assert(!std::is_convertible<Future<std::unique_ptr<A>>,
-                                     Future<std::unique_ptr<B>>>::value,
-                "Underlying types are not convertible");
-  static_assert(!std::is_convertible<Future<B>, Future<A>>::value,
-                "Underlying types are not the same size");
-  static_assert(sizeof(detail::Core<std::unique_ptr<A>>) ==
-                    sizeof(detail::Core<std::unique_ptr<B>>),
-                "Sizes of types are not the same");
-}
index afc119d49fd9cfc841bc3919499a266fcc2ed057..409467e395964816cb263747b2830b5e2dec7eb5 100644 (file)
@@ -264,7 +264,6 @@ TESTS += unit_test
 futures_test_SOURCES = \
     ../futures/test/CollectTest.cpp \
     ../futures/test/ContextTest.cpp \
-    ../futures/test/ConversionTest.cpp \
     ../futures/test/CoreTest.cpp \
     ../futures/test/EnsureTest.cpp \
     ../futures/test/ExecutorTest.cpp \