libstdc++: Fix is_trivially_constructible (PR 94033)
authorJonathan Wakely <jwakely@redhat.com>
Wed, 18 Mar 2020 23:19:12 +0000 (23:19 +0000)
committerJonathan Wakely <jwakely@redhat.com>
Wed, 18 Mar 2020 23:19:35 +0000 (23:19 +0000)
This attempts to make is_nothrow_constructible more robust (and
efficient to compile) by not depending on is_constructible. Instead the
__is_constructible intrinsic is used directly. The helper class
__is_nt_constructible_impl which checks whether the construction is
non-throwing now takes a bool template parameter that is substituted by
the result of the instrinsic. This fixes the reported bug by not using
the already-instantiated (and incorrect) value of std::is_constructible.
I don't think it really fixes the problem in general, because
std::is_nothrow_constructible itself could already have been
instantiated in a context where it gives the wrong result. A proper fix
needs to be done in the compiler.

PR libstdc++/94033
* include/std/type_traits (__is_nt_default_constructible_atom): Remove.
(__is_nt_default_constructible_impl): Remove.
(__is_nothrow_default_constructible_impl): Remove.
(__is_nt_constructible_impl): Add bool template parameter. Adjust
partial specializations.
(__is_nothrow_constructible_impl): Replace class template with alias
template.
(is_nothrow_default_constructible): Derive from alias template
__is_nothrow_constructible_impl instead of
__is_nothrow_default_constructible_impl.
* testsuite/20_util/is_nothrow_constructible/94003.cc: New test.

libstdc++-v3/ChangeLog
libstdc++-v3/include/std/type_traits
libstdc++-v3/testsuite/20_util/is_nothrow_constructible/94003.cc [new file with mode: 0644]

index a9cd25992437647498781c3352229c995f6edf9e..e58aef733aeb32e54f170218e9aa2a1e89e16ce5 100644 (file)
@@ -1,5 +1,18 @@
 2020-03-18  Jonathan Wakely  <jwakely@redhat.com>
 
+       PR libstdc++/94033
+       * include/std/type_traits (__is_nt_default_constructible_atom): Remove.
+       (__is_nt_default_constructible_impl): Remove.
+       (__is_nothrow_default_constructible_impl): Remove.
+       (__is_nt_constructible_impl): Add bool template parameter. Adjust
+       partial specializations.
+       (__is_nothrow_constructible_impl): Replace class template with alias
+       template.
+       (is_nothrow_default_constructible): Derive from alias template
+       __is_nothrow_constructible_impl instead of
+       __is_nothrow_default_constructible_impl.
+       * testsuite/20_util/is_nothrow_constructible/94003.cc: New test.
+
        * include/std/stop_token (stop_token::_Stop_state_ref): Define
        comparison operators explicitly if the compiler won't synthesize them.
 
index 14aa2b37a4f113e60e91c559a00b8ab3d2cfc39f..68abf148a3806bc3a1def4c1d98fccf6907e4c45 100644 (file)
@@ -961,62 +961,36 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        "template argument must be a complete class or an unbounded array");
     };
 
-  template<typename _Tp>
-    struct __is_nt_default_constructible_atom
-    : public integral_constant<bool, noexcept(_Tp())>
-    { };
-
-  template<typename _Tp, bool = is_array<_Tp>::value>
-    struct __is_nt_default_constructible_impl;
-
-  template<typename _Tp>
-    struct __is_nt_default_constructible_impl<_Tp, true>
-    : public __and_<__is_array_known_bounds<_Tp>,
-                   __is_nt_default_constructible_atom<typename
-                      remove_all_extents<_Tp>::type>>
-    { };
-
-  template<typename _Tp>
-    struct __is_nt_default_constructible_impl<_Tp, false>
-    : public __is_nt_default_constructible_atom<_Tp>
+  template<bool, typename _Tp, typename... _Args>
+    struct __is_nt_constructible_impl
+    : public false_type
     { };
 
-  template<typename _Tp>
-    using __is_nothrow_default_constructible_impl
-      = __and_<__is_constructible_impl<_Tp>,
-              __is_nt_default_constructible_impl<_Tp>>;
-
-  /// is_nothrow_default_constructible
-  template<typename _Tp>
-    struct is_nothrow_default_constructible
-    : public __is_nothrow_default_constructible_impl<_Tp>::type
-    {
-      static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}),
-       "template argument must be a complete class or an unbounded array");
-    };
-
   template<typename _Tp, typename... _Args>
-    struct __is_nt_constructible_impl
-    : public integral_constant<bool, noexcept(_Tp(declval<_Args>()...))>
+    struct __is_nt_constructible_impl<true, _Tp, _Args...>
+    : public __bool_constant<noexcept(_Tp(std::declval<_Args>()...))>
     { };
 
   template<typename _Tp, typename _Arg>
-    struct __is_nt_constructible_impl<_Tp, _Arg>
-    : public integral_constant<bool,
-                               noexcept(static_cast<_Tp>(declval<_Arg>()))>
+    struct __is_nt_constructible_impl<true, _Tp, _Arg>
+    : public __bool_constant<noexcept(static_cast<_Tp>(std::declval<_Arg>()))>
     { };
 
   template<typename _Tp>
-    struct __is_nt_constructible_impl<_Tp>
-    : public __is_nothrow_default_constructible_impl<_Tp>
+    struct __is_nt_constructible_impl<true, _Tp>
+    : public __bool_constant<noexcept(_Tp())>
     { };
 
-  template<typename _Tp, typename... _Args>
-    struct __is_nothrow_constructible_impl
-    : public __and_<__is_constructible_impl<_Tp, _Args...>,
-                   __is_nt_constructible_impl<_Tp, _Args...>>
+  template<typename _Tp, size_t _Num>
+    struct __is_nt_constructible_impl<true, _Tp[_Num]>
+    : public __bool_constant<noexcept(typename remove_all_extents<_Tp>::type())>
     { };
 
+  template<typename _Tp, typename... _Args>
+    using __is_nothrow_constructible_impl
+      = __is_nt_constructible_impl<__is_constructible(_Tp, _Args...),
+                                  _Tp, _Args...>;
+
   /// is_nothrow_constructible
   template<typename _Tp, typename... _Args>
     struct is_nothrow_constructible
@@ -1026,6 +1000,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        "template argument must be a complete class or an unbounded array");
     };
 
+  /// is_nothrow_default_constructible
+  template<typename _Tp>
+    struct is_nothrow_default_constructible
+    : public __is_nothrow_constructible_impl<_Tp>::type
+    {
+      static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}),
+       "template argument must be a complete class or an unbounded array");
+    };
+
+
   template<typename _Tp, bool = __is_referenceable<_Tp>::value>
     struct __is_nothrow_copy_constructible_impl;
 
diff --git a/libstdc++-v3/testsuite/20_util/is_nothrow_constructible/94003.cc b/libstdc++-v3/testsuite/20_util/is_nothrow_constructible/94003.cc
new file mode 100644 (file)
index 0000000..392a087
--- /dev/null
@@ -0,0 +1,46 @@
+// Copyright (C) 2020 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 } }
+
+#include <optional>
+#include <tuple>
+
+template <bool B> struct abc {};
+
+template <typename T>
+
+struct future : public abc<std::is_trivially_constructible_v<std::tuple<T>>> {};
+
+class mutation {
+  mutation();
+  friend class std::optional<mutation>;
+};
+
+using mutation_opt = std::optional<mutation>;
+
+future<mutation_opt> foo();
+
+template <typename Consumer> future<mutation_opt> consume_partitions() {
+  return foo();
+}
+
+future<mutation_opt> bar() { return consume_partitions<int>(); }
+
+future<mutation> zed();
+future<mutation> apply_counter_update() { return zed(); }