handle: Improve noexcept qualification

Now that we are using functors, it should be possible to detect noexcept
state from the Drop / Ref functors and decide the proper noexcept values
for our functions based on that.

Change-Id: I30e7d71bab69a103ee9f713f4f029f4fab0b2dc8
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/src/stdplus/handle/copyable.hpp b/src/stdplus/handle/copyable.hpp
index a7419d5..86fc8b7 100644
--- a/src/stdplus/handle/copyable.hpp
+++ b/src/stdplus/handle/copyable.hpp
@@ -16,6 +16,10 @@
     template <typename Drop, typename Ref>
     class HandleF : public Managed<T, As...>::template HandleF<Drop>
     {
+      protected:
+        static constexpr bool ref_noexcept =
+            noexcept(Ref()(std::declval<T>(), std::declval<As&>()...));
+
       public:
         using MHandleF = typename Managed<T, As...>::template HandleF<Drop>;
 
@@ -24,13 +28,18 @@
          *  @param[in] maybeV - Optional object being managed
          */
         template <typename... Vs>
-        constexpr explicit HandleF(const std::optional<T>& maybeV, Vs&&... vs) :
+        constexpr explicit HandleF(const std::optional<T>& maybeV, Vs&&... vs) noexcept(
+            noexcept(MHandleF(std::nullopt, std::declval<Vs>()...)) && noexcept(
+                std::declval<HandleF>().reset(
+                    std::declval<const std::optional<T>&>()))) :
             MHandleF(std::nullopt, std::forward<Vs>(vs)...)
         {
             reset(maybeV);
         }
         template <typename... Vs>
-        constexpr explicit HandleF(const T& maybeV, Vs&&... vs) :
+        constexpr explicit HandleF(const T& maybeV, Vs&&... vs) noexcept(
+            noexcept(MHandleF(std::nullopt, std::declval<Vs>()...)) && noexcept(
+                std::declval<HandleF>().reset(std::declval<const T&>()))) :
             MHandleF(std::nullopt, std::forward<Vs>(vs)...)
         {
             reset(maybeV);
@@ -41,17 +50,29 @@
          *  @param[in] maybeV - Maybe the object being managed
          */
         template <typename... Vs>
-        constexpr explicit HandleF(std::optional<T>&& maybeV, Vs&&... vs) :
+        constexpr explicit HandleF(
+            std::optional<T>&& maybeV,
+            Vs&&... vs) noexcept(noexcept(MHandleF(std::
+                                                       declval<std::optional<
+                                                           T>&&>(),
+                                                   std::declval<Vs>()...))) :
             MHandleF(std::move(maybeV), std::forward<Vs>(vs)...)
         {
         }
         template <typename... Vs>
-        constexpr explicit HandleF(T&& maybeV, Vs&&... vs) :
+        constexpr explicit HandleF(T&& maybeV, Vs&&... vs) noexcept(
+            noexcept(MHandleF(std::declval<T&&>(), std::declval<Vs>()...))) :
             MHandleF(std::move(maybeV), std::forward<Vs>(vs)...)
         {
         }
 
-        constexpr HandleF(const HandleF& other) :
+        constexpr HandleF(const HandleF& other) noexcept(noexcept(MHandleF(
+            std::nullopt,
+            std::declval<const std::tuple<
+                As...>&>())) && noexcept(std::declval<HandleF>()
+                                             .reset(std::declval<
+                                                    const std::optional<
+                                                        T>&>()))) :
             MHandleF(std::nullopt, other.as)
         {
             reset(other.maybe_value());
@@ -63,7 +84,11 @@
         {
         }
 
-        constexpr HandleF& operator=(const HandleF& other)
+        constexpr HandleF& operator=(const HandleF& other) noexcept(
+            noexcept(std::declval<HandleF>().reset()) &&
+            std::is_nothrow_copy_constructible_v<std::tuple<As...>>&& noexcept(
+                std::declval<HandleF>().reset(
+                    std::declval<const std::optional<T>&>())))
         {
             if (this != &other)
             {
@@ -74,20 +99,18 @@
             return *this;
         }
 
-        constexpr HandleF& operator=(HandleF&& other)
+        constexpr HandleF& operator=(HandleF&& other) noexcept(
+            std::is_nothrow_move_assignable_v<MHandleF>)
         {
             MHandleF::operator=(std::move(other));
             return *this;
         }
 
         using MHandleF::reset;
-
-        /** @brief Resets the managed value to a new value
-         *         Takes a new reference on the value
-         *
-         *  @param[in] maybeV - Maybe the new value
-         */
-        constexpr void reset(const std::optional<T>& maybeV)
+        constexpr void reset(const std::optional<T>& maybeV) noexcept(
+            ref_noexcept&& noexcept(std::declval<HandleF>().reset(
+                std::declval<T>())) && noexcept(std::declval<HandleF>()
+                                                    .reset()))
         {
             if (maybeV)
             {
@@ -95,17 +118,19 @@
             }
             else
             {
-                reset(std::nullopt);
+                reset();
             }
         }
-        constexpr void reset(const T& maybeV)
+        constexpr void reset(const T& maybeV) noexcept(ref_noexcept&& noexcept(
+            std::declval<HandleF>().reset(std::declval<T>())))
         {
             reset(doRef(maybeV, std::index_sequence_for<As...>()));
         }
 
       private:
         template <size_t... Indices>
-        T doRef(const T& v, std::index_sequence<Indices...>)
+        T doRef(const T& v,
+                std::index_sequence<Indices...>) noexcept(ref_noexcept)
         {
             return Ref()(v, std::get<Indices>(this->as)...);
         }
diff --git a/src/stdplus/handle/managed.hpp b/src/stdplus/handle/managed.hpp
index df4a142..08cac4f 100644
--- a/src/stdplus/handle/managed.hpp
+++ b/src/stdplus/handle/managed.hpp
@@ -35,19 +35,29 @@
     template <typename Drop>
     class HandleF
     {
+      protected:
+        static constexpr bool drop_noexcept =
+            noexcept(Drop()(std::declval<T>(), std::declval<As&>()...));
+
       public:
         /** @brief Creates a handle owning the object
          *
          *  @param[in] maybeV - Maybe the object being managed
          */
         template <typename... Vs>
-        constexpr explicit HandleF(std::optional<T>&& maybeV, Vs&&... vs) :
-            as(std::forward<Vs>(vs)...), maybeT(std::move(maybeV))
+        constexpr explicit HandleF(std::optional<T>&& maybeV, Vs&&... vs) noexcept(
+            std::is_nothrow_move_constructible_v<std::optional<T>>&& noexcept(
+                std::tuple<As...>(std::declval<Vs>()...))) :
+            as(std::forward<Vs>(vs)...),
+            maybeT(std::move(maybeV))
         {
         }
         template <typename... Vs>
-        constexpr explicit HandleF(T&& maybeV, Vs&&... vs) :
-            as(std::forward<Vs>(vs)...), maybeT(std::move(maybeV))
+        constexpr explicit HandleF(T&& maybeV, Vs&&... vs) noexcept(
+            std::is_nothrow_move_constructible_v<std::optional<T>>&& noexcept(
+                std::tuple<As...>(std::declval<Vs>()...))) :
+            as(std::forward<Vs>(vs)...),
+            maybeT(std::move(maybeV))
         {
         }
 
@@ -63,7 +73,10 @@
             other.maybeT = std::nullopt;
         }
 
-        constexpr HandleF& operator=(HandleF&& other)
+        constexpr HandleF& operator=(HandleF&& other) noexcept(
+            std::is_nothrow_move_assignable_v<std::tuple<As...>>&& noexcept(
+                std::declval<HandleF>().reset(
+                    std::declval<std::optional<T>>())))
         {
             if (this != &other)
             {
@@ -74,16 +87,12 @@
             return *this;
         }
 
-        virtual ~HandleF()
+        virtual ~HandleF() noexcept(
+            std::is_nothrow_destructible_v<std::tuple<As...>>&&
+                std::is_nothrow_destructible_v<std::optional<T>>&& noexcept(
+                    std::declval<HandleF>().reset()))
         {
-            try
-            {
-                reset();
-            }
-            catch (...)
-            {
-                std::abort();
-            }
+            reset();
         }
 
         /** @brief Gets the managed object
@@ -147,12 +156,14 @@
          *
          *  @param[in] maybeV - Maybe the new value
          */
-        constexpr void reset(std::optional<T>&& maybeV)
+        constexpr void reset(std::optional<T>&& maybeV) noexcept(
+            drop_noexcept&& std::is_nothrow_move_assignable_v<std::optional<T>>)
         {
             maybeDrop(std::index_sequence_for<As...>());
             maybeT = std::move(maybeV);
         }
-        constexpr void reset(T&& maybeV)
+        constexpr void reset(T&& maybeV) noexcept(
+            drop_noexcept&& std::is_nothrow_move_assignable_v<std::optional<T>>)
         {
             maybeDrop(std::index_sequence_for<As...>());
             maybeT = std::move(maybeV);
@@ -161,7 +172,7 @@
         /** @brief A shorthand reset function for convenience
          *         Same as calling reset(std::nullopt)
          */
-        constexpr void reset()
+        constexpr void reset() noexcept(drop_noexcept)
         {
             reset(std::nullopt);
         }
@@ -213,7 +224,7 @@
         std::optional<T> maybeT;
 
         template <size_t... Indices>
-        void maybeDrop(std::index_sequence<Indices...>)
+        void maybeDrop(std::index_sequence<Indices...>) noexcept(drop_noexcept)
         {
             if (maybeT)
             {
diff --git a/test/handle/copyable.cpp b/test/handle/copyable.cpp
index b55b216..cd17c54 100644
--- a/test/handle/copyable.cpp
+++ b/test/handle/copyable.cpp
@@ -68,6 +68,18 @@
 using StoreHandle =
     Copyable<int, std::string, int>::HandleF<StoreDrop, StoreRef>;
 
+static_assert(std::is_nothrow_copy_constructible_v<SimpleHandle>);
+static_assert(std::is_nothrow_move_constructible_v<SimpleHandle>);
+static_assert(std::is_nothrow_move_assignable_v<SimpleHandle>);
+static_assert(std::is_nothrow_destructible_v<SimpleHandle>);
+static_assert(noexcept(std::declval<SimpleHandle>().reset()));
+static_assert(!std::is_nothrow_copy_constructible_v<StoreHandle>);
+// http://cplusplus.github.io/LWG/lwg-active.html#2116
+// static_assert(std::is_nothrow_move_constructible_v<StoreHandle>);
+static_assert(!std::is_nothrow_move_assignable_v<StoreHandle>);
+static_assert(!std::is_nothrow_destructible_v<StoreHandle>);
+static_assert(!noexcept(std::declval<StoreHandle>().reset()));
+
 class CopyableHandleTest : public ::testing::Test
 {
   protected:
diff --git a/test/handle/managed.cpp b/test/handle/managed.cpp
index fffffde..fb89195 100644
--- a/test/handle/managed.cpp
+++ b/test/handle/managed.cpp
@@ -41,6 +41,16 @@
 using SimpleHandle = Managed<int>::HandleF<SimpleDrop>;
 using StoreHandle = Managed<int, std::string, int>::HandleF<StoreDrop>;
 
+static_assert(std::is_nothrow_move_constructible_v<SimpleHandle>);
+static_assert(std::is_nothrow_move_assignable_v<SimpleHandle>);
+static_assert(std::is_nothrow_destructible_v<SimpleHandle>);
+static_assert(noexcept(std::declval<SimpleHandle>().reset()));
+// http://cplusplus.github.io/LWG/lwg-active.html#2116
+// static_assert(std::is_nothrow_move_constructible_v<StoreHandle>);
+static_assert(!std::is_nothrow_move_assignable_v<StoreHandle>);
+static_assert(!std::is_nothrow_destructible_v<StoreHandle>);
+static_assert(!noexcept(std::declval<StoreHandle>().reset()));
+
 class ManagedHandleTest : public ::testing::Test
 {
   protected: