When unpacking a std::tuple we know that the std::get calls are
noexcept, so only the invocation (for std::apply) and construction (for
std::make_from_tuple) can throw.

We also know the std::get calls won't throw for a std::array, but this
patch doesn't specialize the variable template for std::array. For an
arbitrary tuple-like type we don't know if the std::get calls will
throw, and so just use a potentially-throwing noexcept-specifier.

        * include/std/tuple (__unpack_std_tuple): New variable template and
        partial specializations.
        (apply, make_from_tuple): Add noexcept-specifier.
        * testsuite/20_util/tuple/apply/2.cc: New test.
        * testsuite/20_util/tuple/make_from_tuple/2.cc: New test.

Tested x86_64-linux, committed to trunk.


commit 3426bcb0546ef9b4bbc528204da7b79206f8aa87
Author: Jonathan Wakely <jwak...@redhat.com>
Date:   Thu Aug 8 21:44:13 2019 +0100

    Add noexcept-specifier to std::apply and std::make_from_tuple
    
    When unpacking a std::tuple we know that the std::get calls are
    noexcept, so only the invocation (for std::apply) and construction (for
    std::make_from_tuple) can throw.
    
    We also know the std::get calls won't throw for a std::array, but this
    patch doesn't specialize the variable template for std::array. For an
    arbitrary tuple-like type we don't know if the std::get calls will
    throw, and so just use a potentially-throwing noexcept-specifier.
    
            * include/std/tuple (__unpack_std_tuple): New variable template and
            partial specializations.
            (apply, make_from_tuple): Add noexcept-specifier.
            * testsuite/20_util/tuple/apply/2.cc: New test.
            * testsuite/20_util/tuple/make_from_tuple/2.cc: New test.

diff --git a/libstdc++-v3/include/std/tuple b/libstdc++-v3/include/std/tuple
index 980dd6d6270..dd966b3a0bc 100644
--- a/libstdc++-v3/include/std/tuple
+++ b/libstdc++-v3/include/std/tuple
@@ -1591,6 +1591,30 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       { }
 
 #if __cplusplus >= 201703L
+
+  // Unpack a std::tuple into a type trait and use its value.
+  // For cv std::tuple<_Up> the result is _Trait<_Tp, cv _Up...>::value.
+  // For cv std::tuple<_Up>& the result is _Trait<_Tp, cv _Up&...>::value.
+  // Otherwise the result is false (because we don't know if std::get throws).
+  template<template<typename...> class _Trait, typename _Tp, typename _Tuple>
+    inline constexpr bool __unpack_std_tuple = false;
+
+  template<template<typename...> class _Trait, typename _Tp, typename... _Up>
+    inline constexpr bool __unpack_std_tuple<_Trait, _Tp, tuple<_Up...>>
+      = _Trait<_Tp, _Up...>::value;
+
+  template<template<typename...> class _Trait, typename _Tp, typename... _Up>
+    inline constexpr bool __unpack_std_tuple<_Trait, _Tp, tuple<_Up...>&>
+      = _Trait<_Tp, _Up&...>::value;
+
+  template<template<typename...> class _Trait, typename _Tp, typename... _Up>
+    inline constexpr bool __unpack_std_tuple<_Trait, _Tp, const tuple<_Up...>>
+      = _Trait<_Tp, const _Up...>::value;
+
+  template<template<typename...> class _Trait, typename _Tp, typename... _Up>
+    inline constexpr bool __unpack_std_tuple<_Trait, _Tp, const tuple<_Up...>&>
+      = _Trait<_Tp, const _Up&...>::value;
+
 # define __cpp_lib_apply 201603
 
   template <typename _Fn, typename _Tuple, size_t... _Idx>
@@ -1604,6 +1628,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template <typename _Fn, typename _Tuple>
     constexpr decltype(auto)
     apply(_Fn&& __f, _Tuple&& __t)
+    noexcept(__unpack_std_tuple<is_nothrow_invocable, _Fn, _Tuple>)
     {
       using _Indices
        = make_index_sequence<tuple_size_v<remove_reference_t<_Tuple>>>;
@@ -1622,6 +1647,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template <typename _Tp, typename _Tuple>
     constexpr _Tp
     make_from_tuple(_Tuple&& __t)
+    noexcept(__unpack_std_tuple<is_nothrow_constructible, _Tp, _Tuple>)
     {
       return __make_from_tuple_impl<_Tp>(
         std::forward<_Tuple>(__t),
diff --git a/libstdc++-v3/testsuite/20_util/tuple/apply/2.cc 
b/libstdc++-v3/testsuite/20_util/tuple/apply/2.cc
new file mode 100644
index 00000000000..aa5968f397f
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/tuple/apply/2.cc
@@ -0,0 +1,62 @@
+// Copyright (C) 2019 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++17" }
+// { dg-do compile { target c++17 } }
+
+// Test noexcept-specifier on std::apply
+
+#include <tuple>
+
+using std::tuple;
+using std::declval;
+
+void f1();
+
+static_assert( !noexcept(apply(f1, declval<tuple<>>())) );
+static_assert( !noexcept(apply(f1, declval<tuple<>&>())) );
+static_assert( !noexcept(apply(f1, declval<const tuple<>>())) );
+static_assert( !noexcept(apply(f1, declval<const tuple<>&>())) );
+
+void f2() noexcept;
+
+static_assert( noexcept(apply(f2, declval<tuple<>>())) );
+static_assert( noexcept(apply(f2, declval<tuple<>&>())) );
+static_assert( noexcept(apply(f2, declval<const tuple<>>())) );
+static_assert( noexcept(apply(f2, declval<const tuple<>&>())) );
+
+struct F3 {
+  void operator()(int&);
+  void operator()(int&&) noexcept;
+  void operator()(const int&) noexcept;
+  void operator()(const int&&);
+} f3;
+
+static_assert( noexcept(apply(f3, declval<tuple<int>>())) );
+static_assert( !noexcept(apply(f3, declval<tuple<int>&>())) );
+static_assert( !noexcept(apply(f3, declval<const tuple<int>>())) );
+static_assert( noexcept(apply(f3, declval<const tuple<int>&>())) );
+
+struct F4 {
+  void operator()(int&, const int&);
+  void operator()(int&&, int&&) noexcept;
+} f4;
+
+static_assert( noexcept(apply(f4, declval<tuple<int, int>>())) );
+static_assert( !noexcept(apply(f4, declval<tuple<int, int>&>())) );
+static_assert( !noexcept(apply(f4, declval<tuple<int&, const int>>())) );
+static_assert( !noexcept(apply(f4, declval<tuple<int, const int>&>())) );
diff --git a/libstdc++-v3/testsuite/20_util/tuple/make_from_tuple/2.cc 
b/libstdc++-v3/testsuite/20_util/tuple/make_from_tuple/2.cc
new file mode 100644
index 00000000000..18a9466ef42
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/tuple/make_from_tuple/2.cc
@@ -0,0 +1,63 @@
+// Copyright (C) 2019 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++17" }
+// { dg-do compile { target c++17 } }
+
+// Test noexcept-specifier on std::make_from_tuple
+
+#include <tuple>
+
+using std::make_from_tuple;
+using std::tuple;
+using std::declval;
+
+struct T1 { T1(); };
+
+static_assert( !noexcept(make_from_tuple<T1>(declval<tuple<>>())) );
+static_assert( !noexcept(make_from_tuple<T1>(declval<tuple<>&>())) );
+static_assert( !noexcept(make_from_tuple<T1>(declval<const tuple<>>())) );
+static_assert( !noexcept(make_from_tuple<T1>(declval<const tuple<>&>())) );
+
+struct T2 { };
+
+static_assert( noexcept(make_from_tuple<T2>(declval<tuple<>>())) );
+static_assert( noexcept(make_from_tuple<T2>(declval<tuple<>&>())) );
+static_assert( noexcept(make_from_tuple<T2>(declval<const tuple<>>())) );
+static_assert( noexcept(make_from_tuple<T2>(declval<const tuple<>&>())) );
+
+struct T3 {
+  T3(int&);
+  T3(int&&) noexcept;
+  T3(const int&) noexcept;
+  T3(const int&&);
+};
+
+static_assert( noexcept(make_from_tuple<T3>(declval<tuple<int>>())) );
+static_assert( !noexcept(make_from_tuple<T3>(declval<tuple<int>&>())) );
+static_assert( !noexcept(make_from_tuple<T3>(declval<const tuple<int>>())) );
+static_assert( noexcept(make_from_tuple<T3>(declval<const tuple<int>&>())) );
+
+struct T4 {
+  T4(int&, const int&);
+  T4(int&&, int&&) noexcept;
+};
+
+static_assert( noexcept(make_from_tuple<T4>(declval<tuple<int, int>>())) );
+static_assert( !noexcept(make_from_tuple<T4>(declval<tuple<int, int>&>())) );
+static_assert( !noexcept(make_from_tuple<T4>(declval<tuple<int&, const 
int>>())) );
+static_assert( !noexcept(make_from_tuple<T4>(declval<tuple<int, const 
int>&>())) );

Reply via email to