Update (non-copy, non-move) assign operators.

Fix perfect-forwarded assign operator to look at condition to
decide whether it should participate in overload resolution.

Add Optional<U> copy- and move-like assign operators.
For that implementation, OptionalBase's copy-/move-assign
operators are slightly refactored.

BUG=784732
TEST=Ran trybot.

Change-Id: I69db9def857a1cce8e7b05f0c6e11922ee8d95db
Reviewed-on: https://chromium-review.googlesource.com/856539
Reviewed-by: danakj <danakj@chromium.org>
Commit-Queue: Hidehiko Abe <hidehiko@chromium.org>
Cr-Commit-Position: refs/heads/master@{#538699}

CrOS-Libchrome-Original-Commit: 40ee4aee34e39b711f266bbe3e03f4c033b9cf71
diff --git a/base/optional.h b/base/optional.h
index 9599baf..d65881b 100644
--- a/base/optional.h
+++ b/base/optional.h
@@ -239,37 +239,37 @@
   ~OptionalBase() = default;
 
   OptionalBase& operator=(const OptionalBase& other) {
-    if (!other.storage_.is_populated_) {
-      FreeIfNeeded();
-      return *this;
-    }
-
-    InitOrAssign(other.storage_.value_);
+    CopyAssign(other);
     return *this;
   }
 
   OptionalBase& operator=(OptionalBase&& other) {
-    if (!other.storage_.is_populated_) {
-      FreeIfNeeded();
-      return *this;
-    }
-
-    InitOrAssign(std::move(other.storage_.value_));
+    MoveAssign(std::move(other));
     return *this;
   }
 
-  void InitOrAssign(const T& value) {
-    if (!storage_.is_populated_)
-      storage_.Init(value);
+  template <typename U>
+  void CopyAssign(const OptionalBase<U>& other) {
+    if (other.storage_.is_populated_)
+      InitOrAssign(other.storage_.value_);
     else
-      storage_.value_ = value;
+      FreeIfNeeded();
   }
 
-  void InitOrAssign(T&& value) {
-    if (!storage_.is_populated_)
-      storage_.Init(std::move(value));
+  template <typename U>
+  void MoveAssign(OptionalBase<U>&& other) {
+    if (other.storage_.is_populated_)
+      InitOrAssign(std::move(other.storage_.value_));
     else
-      storage_.value_ = std::move(value);
+      FreeIfNeeded();
+  }
+
+  template <typename U>
+  void InitOrAssign(U&& value) {
+    if (storage_.is_populated_)
+      storage_.value_ = std::forward<U>(value);
+    else
+      storage_.Init(std::forward<U>(value));
   }
 
   void FreeIfNeeded() {
@@ -339,7 +339,7 @@
   MoveAssignable& operator=(MoveAssignable&&) = delete;
 };
 
-// Helper to conditionally enable converting constructors.
+// Helper to conditionally enable converting constructors and assign operators.
 template <typename T, typename U>
 struct IsConvertibleFromOptional
     : std::integral_constant<
@@ -353,6 +353,16 @@
               std::is_convertible<Optional<U>&&, T>::value ||
               std::is_convertible<const Optional<U>&&, T>::value> {};
 
+template <typename T, typename U>
+struct IsAssignableFromOptional
+    : std::integral_constant<
+          bool,
+          IsConvertibleFromOptional<T, U>::value ||
+              std::is_assignable<T&, Optional<U>&>::value ||
+              std::is_assignable<T&, const Optional<U>&>::value ||
+              std::is_assignable<T&, Optional<U>&&>::value ||
+              std::is_assignable<T&, const Optional<U>&&>::value> {};
+
 // Forward compatibility for C++20.
 template <typename T>
 using RemoveCvRefT = std::remove_cv_t<std::remove_reference_t<T>>;
@@ -486,14 +496,42 @@
     return *this;
   }
 
-  template <class U>
-  typename std::enable_if<std::is_same<std::decay_t<U>, T>::value,
-                          Optional&>::type
+  // Perfect-forwarded assignment.
+  template <typename U>
+  std::enable_if_t<
+      !std::is_same<internal::RemoveCvRefT<U>, Optional<T>>::value &&
+          std::is_constructible<T, U>::value &&
+          std::is_assignable<T&, U>::value &&
+          (!std::is_scalar<T>::value ||
+           !std::is_same<std::decay_t<U>, T>::value),
+      Optional&>
   operator=(U&& value) {
     InitOrAssign(std::forward<U>(value));
     return *this;
   }
 
+  // Copy assign the state of other.
+  template <typename U>
+  std::enable_if_t<!internal::IsAssignableFromOptional<T, U>::value &&
+                       std::is_constructible<T, const U&>::value &&
+                       std::is_assignable<T&, const U&>::value,
+                   Optional&>
+  operator=(const Optional<U>& other) {
+    CopyAssign(other);
+    return *this;
+  }
+
+  // Move assign the state of other.
+  template <typename U>
+  std::enable_if_t<!internal::IsAssignableFromOptional<T, U>::value &&
+                       std::is_constructible<T, U>::value &&
+                       std::is_assignable<T&, U>::value,
+                   Optional&>
+  operator=(Optional<U>&& other) {
+    MoveAssign(std::move(other));
+    return *this;
+  }
+
   constexpr const T* operator->() const {
     DCHECK(storage_.is_populated_);
     return &value();
@@ -605,8 +643,10 @@
  private:
   // Accessing template base class's protected member needs explicit
   // declaration to do so.
+  using internal::OptionalBase<T>::CopyAssign;
   using internal::OptionalBase<T>::FreeIfNeeded;
   using internal::OptionalBase<T>::InitOrAssign;
+  using internal::OptionalBase<T>::MoveAssign;
   using internal::OptionalBase<T>::storage_;
 };
 
diff --git a/base/optional_unittest.cc b/base/optional_unittest.cc
index b098c0e..17c9ee4 100644
--- a/base/optional_unittest.cc
+++ b/base/optional_unittest.cc
@@ -649,6 +649,37 @@
     EXPECT_TRUE(!!b);
     EXPECT_EQ(a->foo(), b->foo());
   }
+
+  // Converting assignment.
+  {
+    Optional<int> a(in_place, 1);
+    Optional<double> b;
+    b = a;
+
+    EXPECT_TRUE(!!a);
+    EXPECT_TRUE(!!b);
+    EXPECT_EQ(1, a.value());
+    EXPECT_EQ(1.0, b.value());
+  }
+
+  {
+    Optional<int> a(in_place, 42);
+    Optional<double> b(in_place, 1);
+    b = a;
+
+    EXPECT_TRUE(!!a);
+    EXPECT_TRUE(!!b);
+    EXPECT_EQ(42, a.value());
+    EXPECT_EQ(42.0, b.value());
+  }
+
+  {
+    Optional<int> a;
+    Optional<double> b(in_place, 1);
+    b = a;
+    EXPECT_FALSE(!!a);
+    EXPECT_FALSE(!!b);
+  }
 }
 
 TEST(OptionalTest, AssignObject_rvalue) {
@@ -717,6 +748,36 @@
     EXPECT_TRUE(!!b);
     EXPECT_EQ(42, b->foo());
   }
+
+  // Converting assignment.
+  {
+    Optional<int> a(in_place, 1);
+    Optional<double> b;
+    b = std::move(a);
+
+    EXPECT_TRUE(!!a);
+    EXPECT_TRUE(!!b);
+    EXPECT_EQ(1.0, b.value());
+  }
+
+  {
+    Optional<int> a(in_place, 42);
+    Optional<double> b(in_place, 1);
+    b = std::move(a);
+
+    EXPECT_TRUE(!!a);
+    EXPECT_TRUE(!!b);
+    EXPECT_EQ(42.0, b.value());
+  }
+
+  {
+    Optional<int> a;
+    Optional<double> b(in_place, 1);
+    b = std::move(a);
+
+    EXPECT_FALSE(!!a);
+    EXPECT_FALSE(!!b);
+  }
 }
 
 TEST(OptionalTest, AssignNull) {
@@ -745,6 +806,190 @@
   }
 }
 
+TEST(OptionalTest, AssignOverload) {
+  struct Test1 {
+    enum class State {
+      CONSTRUCTED,
+      MOVED,
+    };
+    State state = State::CONSTRUCTED;
+  };
+
+  // Here, Optional<Test2> can be assigned from Optioanl<Test1>.
+  // In case of move, marks MOVED to Test1 instance.
+  struct Test2 {
+    enum class State {
+      DEFAULT_CONSTRUCTED,
+      COPY_CONSTRUCTED_FROM_TEST1,
+      MOVE_CONSTRUCTED_FROM_TEST1,
+      COPY_ASSIGNED_FROM_TEST1,
+      MOVE_ASSIGNED_FROM_TEST1,
+    };
+
+    Test2() = default;
+    explicit Test2(const Test1& test1)
+        : state(State::COPY_CONSTRUCTED_FROM_TEST1) {}
+    explicit Test2(Test1&& test1) : state(State::MOVE_CONSTRUCTED_FROM_TEST1) {
+      test1.state = Test1::State::MOVED;
+    }
+    Test2& operator=(const Test1& test1) {
+      state = State::COPY_ASSIGNED_FROM_TEST1;
+      return *this;
+    }
+    Test2& operator=(Test1&& test1) {
+      state = State::MOVE_ASSIGNED_FROM_TEST1;
+      test1.state = Test1::State::MOVED;
+      return *this;
+    }
+
+    State state = State::DEFAULT_CONSTRUCTED;
+  };
+
+  {
+    Optional<Test1> a(in_place);
+    Optional<Test2> b;
+
+    b = a;
+    EXPECT_TRUE(!!a);
+    EXPECT_TRUE(!!b);
+    EXPECT_EQ(Test1::State::CONSTRUCTED, a->state);
+    EXPECT_EQ(Test2::State::COPY_CONSTRUCTED_FROM_TEST1, b->state);
+  }
+
+  {
+    Optional<Test1> a(in_place);
+    Optional<Test2> b(in_place);
+
+    b = a;
+    EXPECT_TRUE(!!a);
+    EXPECT_TRUE(!!b);
+    EXPECT_EQ(Test1::State::CONSTRUCTED, a->state);
+    EXPECT_EQ(Test2::State::COPY_ASSIGNED_FROM_TEST1, b->state);
+  }
+
+  {
+    Optional<Test1> a(in_place);
+    Optional<Test2> b;
+
+    b = std::move(a);
+    EXPECT_TRUE(!!a);
+    EXPECT_TRUE(!!b);
+    EXPECT_EQ(Test1::State::MOVED, a->state);
+    EXPECT_EQ(Test2::State::MOVE_CONSTRUCTED_FROM_TEST1, b->state);
+  }
+
+  {
+    Optional<Test1> a(in_place);
+    Optional<Test2> b(in_place);
+
+    b = std::move(a);
+    EXPECT_TRUE(!!a);
+    EXPECT_TRUE(!!b);
+    EXPECT_EQ(Test1::State::MOVED, a->state);
+    EXPECT_EQ(Test2::State::MOVE_ASSIGNED_FROM_TEST1, b->state);
+  }
+
+  // Similar to Test2, but Test3 also has copy/move ctor and assign operators
+  // from Optional<Test1>, too. In this case, for a = b where a is
+  // Optional<Test3> and b is Optional<Test1>,
+  // Optional<T>::operator=(U&&) where U is Optional<Test1> should be used
+  // rather than Optional<T>::operator=(Optional<U>&&) where U is Test1.
+  struct Test3 {
+    enum class State {
+      DEFAULT_CONSTRUCTED,
+      COPY_CONSTRUCTED_FROM_TEST1,
+      MOVE_CONSTRUCTED_FROM_TEST1,
+      COPY_CONSTRUCTED_FROM_OPTIONAL_TEST1,
+      MOVE_CONSTRUCTED_FROM_OPTIONAL_TEST1,
+      COPY_ASSIGNED_FROM_TEST1,
+      MOVE_ASSIGNED_FROM_TEST1,
+      COPY_ASSIGNED_FROM_OPTIONAL_TEST1,
+      MOVE_ASSIGNED_FROM_OPTIONAL_TEST1,
+    };
+
+    Test3() = default;
+    explicit Test3(const Test1& test1)
+        : state(State::COPY_CONSTRUCTED_FROM_TEST1) {}
+    explicit Test3(Test1&& test1) : state(State::MOVE_CONSTRUCTED_FROM_TEST1) {
+      test1.state = Test1::State::MOVED;
+    }
+    explicit Test3(const Optional<Test1>& test1)
+        : state(State::COPY_CONSTRUCTED_FROM_OPTIONAL_TEST1) {}
+    explicit Test3(Optional<Test1>&& test1)
+        : state(State::MOVE_CONSTRUCTED_FROM_OPTIONAL_TEST1) {
+      // In the following senarios, given |test1| should always have value.
+      DCHECK(test1.has_value());
+      test1->state = Test1::State::MOVED;
+    }
+    Test3& operator=(const Test1& test1) {
+      state = State::COPY_ASSIGNED_FROM_TEST1;
+      return *this;
+    }
+    Test3& operator=(Test1&& test1) {
+      state = State::MOVE_ASSIGNED_FROM_TEST1;
+      test1.state = Test1::State::MOVED;
+      return *this;
+    }
+    Test3& operator=(const Optional<Test1>& test1) {
+      state = State::COPY_ASSIGNED_FROM_OPTIONAL_TEST1;
+      return *this;
+    }
+    Test3& operator=(Optional<Test1>&& test1) {
+      state = State::MOVE_ASSIGNED_FROM_OPTIONAL_TEST1;
+      // In the following senarios, given |test1| should always have value.
+      DCHECK(test1.has_value());
+      test1->state = Test1::State::MOVED;
+      return *this;
+    }
+
+    State state = State::DEFAULT_CONSTRUCTED;
+  };
+
+  {
+    Optional<Test1> a(in_place);
+    Optional<Test3> b;
+
+    b = a;
+    EXPECT_TRUE(!!a);
+    EXPECT_TRUE(!!b);
+    EXPECT_EQ(Test1::State::CONSTRUCTED, a->state);
+    EXPECT_EQ(Test3::State::COPY_CONSTRUCTED_FROM_OPTIONAL_TEST1, b->state);
+  }
+
+  {
+    Optional<Test1> a(in_place);
+    Optional<Test3> b(in_place);
+
+    b = a;
+    EXPECT_TRUE(!!a);
+    EXPECT_TRUE(!!b);
+    EXPECT_EQ(Test1::State::CONSTRUCTED, a->state);
+    EXPECT_EQ(Test3::State::COPY_ASSIGNED_FROM_OPTIONAL_TEST1, b->state);
+  }
+
+  {
+    Optional<Test1> a(in_place);
+    Optional<Test3> b;
+
+    b = std::move(a);
+    EXPECT_TRUE(!!a);
+    EXPECT_TRUE(!!b);
+    EXPECT_EQ(Test1::State::MOVED, a->state);
+    EXPECT_EQ(Test3::State::MOVE_CONSTRUCTED_FROM_OPTIONAL_TEST1, b->state);
+  }
+
+  {
+    Optional<Test1> a(in_place);
+    Optional<Test3> b(in_place);
+
+    b = std::move(a);
+    EXPECT_TRUE(!!a);
+    EXPECT_TRUE(!!b);
+    EXPECT_EQ(Test1::State::MOVED, a->state);
+    EXPECT_EQ(Test3::State::MOVE_ASSIGNED_FROM_OPTIONAL_TEST1, b->state);
+  }
+}
+
 TEST(OptionalTest, OperatorStar) {
   {
     Optional<float> a(0.1f);