public inbox for libstdc++-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r11-4386] c++: Implement __is_nothrow_constructible and __is_nothrow_assignable
@ 2020-10-26 13:37 Ville Voutilainen
  0 siblings, 0 replies; only message in thread
From: Ville Voutilainen @ 2020-10-26 13:37 UTC (permalink / raw)
  To: gcc-cvs, libstdc++-cvs

https://gcc.gnu.org/g:9e2256dcd481ffe3a8c79b65eba19fbb14b7ff8d

commit r11-4386-g9e2256dcd481ffe3a8c79b65eba19fbb14b7ff8d
Author: Ville Voutilainen <ville.voutilainen@gmail.com>
Date:   Mon Oct 26 15:36:24 2020 +0200

    c++: Implement __is_nothrow_constructible and __is_nothrow_assignable
    
    gcc/c-family/ChangeLog:
    
            * c-common.c (__is_nothrow_assignable): New.
            (__is_nothrow_constructible): Likewise.
            * c-common.h (RID_IS_NOTHROW_ASSIGNABLE): New.
            (RID_IS_NOTHROW_CONSTRUCTIBLE): Likewise.
    
    gcc/cp/ChangeLog:
    
            * cp-tree.h (CPTK_IS_NOTHROW_ASSIGNABLE): New.
            (CPTK_IS_NOTHROW_CONSTRUCTIBLE): Likewise.
            (is_nothrow_xible): Likewise.
            * method.c (is_nothrow_xible): New.
            (is_trivially_xible): Tweak.
            * parser.c (cp_parser_primary_expression): Handle the new RID_*.
            (cp_parser_trait_expr): Likewise.
            * semantics.c (trait_expr_value): Handle the new RID_*.
            (finish_trait_expr): Likewise.
    
    libstdc++-v3/ChangeLog:
    
            * include/std/type_traits (__is_nt_constructible_impl): Remove.
            (__is_nothrow_constructible_impl): Adjust.
            (is_nothrow_default_constructible): Likewise.
            (__is_nt_assignable_impl): Remove.
            (__is_nothrow_assignable_impl): Adjust.

Diff:
---
 gcc/c-family/c-common.c                            |  2 +
 gcc/c-family/c-common.h                            |  1 +
 gcc/cp/cp-tree.h                                   |  5 +-
 gcc/cp/method.c                                    | 17 +++++--
 gcc/cp/parser.c                                    | 10 ++++
 gcc/cp/semantics.c                                 |  8 ++++
 .../g++.dg/ext/is_nothrow_constructible1.C         | 48 ++++++++++++++++++++
 .../g++.dg/ext/is_nothrow_constructible2.C         | 15 ++++++
 .../g++.dg/ext/is_nothrow_constructible3.C         |  8 ++++
 .../g++.dg/ext/is_nothrow_constructible4.C         | 11 +++++
 .../g++.dg/ext/is_nothrow_constructible5.C         | 12 +++++
 .../g++.dg/ext/is_nothrow_constructible6.C         | 11 +++++
 libstdc++-v3/include/std/type_traits               | 53 ++--------------------
 13 files changed, 148 insertions(+), 53 deletions(-)

diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 1787dfdb1d8..d56238aeb01 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -527,6 +527,8 @@ const struct c_common_resword c_common_reswords[] =
   { "while",		RID_WHILE,	0 },
   { "__is_assignable", RID_IS_ASSIGNABLE, D_CXXONLY },
   { "__is_constructible", RID_IS_CONSTRUCTIBLE, D_CXXONLY },
+  { "__is_nothrow_assignable", RID_IS_NOTHROW_ASSIGNABLE, D_CXXONLY },
+  { "__is_nothrow_constructible", RID_IS_NOTHROW_CONSTRUCTIBLE, D_CXXONLY },
 
   /* C++ transactional memory.  */
   { "synchronized",	RID_SYNCHRONIZED, D_CXX_OBJC | D_TRANSMEM },
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index bb38e6c76a4..18b489d55a3 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -176,6 +176,7 @@ enum rid
   RID_IS_TRIVIALLY_COPYABLE,
   RID_IS_UNION,                RID_UNDERLYING_TYPE,
   RID_IS_ASSIGNABLE,           RID_IS_CONSTRUCTIBLE,
+  RID_IS_NOTHROW_ASSIGNABLE,   RID_IS_NOTHROW_CONSTRUCTIBLE,
 
   /* C++11 */
   RID_CONSTEXPR, RID_DECLTYPE, RID_NOEXCEPT, RID_NULLPTR, RID_STATIC_ASSERT,
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 5c06ac3789e..1ce20989e13 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -1323,7 +1323,9 @@ enum cp_trait_kind
   CPTK_IS_UNION,
   CPTK_UNDERLYING_TYPE,
   CPTK_IS_ASSIGNABLE,
-  CPTK_IS_CONSTRUCTIBLE
+  CPTK_IS_CONSTRUCTIBLE,
+  CPTK_IS_NOTHROW_ASSIGNABLE,
+  CPTK_IS_NOTHROW_CONSTRUCTIBLE
 };
 
 /* The types that we are processing.  */
@@ -6752,6 +6754,7 @@ extern void use_thunk				(tree, bool);
 extern bool trivial_fn_p			(tree);
 extern tree forward_parm			(tree);
 extern bool is_trivially_xible			(enum tree_code, tree, tree);
+extern bool is_nothrow_xible			(enum tree_code, tree, tree);
 extern bool is_xible				(enum tree_code, tree, tree);
 extern tree get_defaulted_eh_spec		(tree, tsubst_flags_t = tf_warning_or_error);
 extern bool maybe_explain_implicit_delete	(tree);
diff --git a/gcc/cp/method.c b/gcc/cp/method.c
index 6e4c5f7e83b..16e76351943 100644
--- a/gcc/cp/method.c
+++ b/gcc/cp/method.c
@@ -1924,15 +1924,26 @@ is_xible_helper (enum tree_code code, tree to, tree from, bool trivial)
 bool
 is_trivially_xible (enum tree_code code, tree to, tree from)
 {
-  tree expr;
-  expr = is_xible_helper (code, to, from, /*trivial*/true);
-
+  tree expr = is_xible_helper (code, to, from, /*trivial*/true);
   if (expr == NULL_TREE || expr == error_mark_node)
     return false;
   tree nt = cp_walk_tree_without_duplicates (&expr, check_nontriv, NULL);
   return !nt;
 }
 
+/* Returns true iff TO is nothrow assignable (if CODE is MODIFY_EXPR) or
+   constructible (otherwise) from FROM, which is a single type for
+   assignment or a list of types for construction.  */
+
+bool
+is_nothrow_xible (enum tree_code code, tree to, tree from)
+{
+  tree expr = is_xible_helper (code, to, from, /*trivial*/false);
+  if (expr == NULL_TREE || expr == error_mark_node)
+    return false;
+  return expr_noexcept_p (expr, tf_none);
+}
+
 /* Returns true iff TO is assignable (if CODE is MODIFY_EXPR) or
    constructible (otherwise) from FROM, which is a single type for
    assignment or a list of types for construction.  */
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 7ec7d42773c..cce3d0a679e 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -5637,6 +5637,8 @@ cp_parser_primary_expression (cp_parser *parser,
 	case RID_IS_UNION:
 	case RID_IS_ASSIGNABLE:
 	case RID_IS_CONSTRUCTIBLE:
+	case RID_IS_NOTHROW_ASSIGNABLE:
+	case RID_IS_NOTHROW_CONSTRUCTIBLE:
 	  return cp_parser_trait_expr (parser, token->keyword);
 
 	// C++ concepts
@@ -10501,6 +10503,14 @@ cp_parser_trait_expr (cp_parser* parser, enum rid keyword)
       kind = CPTK_IS_CONSTRUCTIBLE;
       variadic = true;
       break;
+    case RID_IS_NOTHROW_ASSIGNABLE:
+      kind = CPTK_IS_NOTHROW_ASSIGNABLE;
+      binary = true;
+      break;
+    case RID_IS_NOTHROW_CONSTRUCTIBLE:
+      kind = CPTK_IS_NOTHROW_CONSTRUCTIBLE;
+      variadic = true;
+      break;
     default:
       gcc_unreachable ();
     }
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 1e42cd799c2..ac488478f36 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -10133,6 +10133,12 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_CONSTRUCTIBLE:
       return is_xible (INIT_EXPR, type1, type2);
 
+    case CPTK_IS_NOTHROW_ASSIGNABLE:
+      return is_nothrow_xible (MODIFY_EXPR, type1, type2);
+
+    case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
+      return is_nothrow_xible (INIT_EXPR, type1, type2);
+
     default:
       gcc_unreachable ();
       return false;
@@ -10213,6 +10219,8 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
 
     case CPTK_IS_TRIVIALLY_ASSIGNABLE:
     case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE:
+    case CPTK_IS_NOTHROW_ASSIGNABLE:
+    case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
       if (!check_trait_type (type1)
 	  || !check_trait_type (type2))
 	return error_mark_node;
diff --git a/gcc/testsuite/g++.dg/ext/is_nothrow_constructible1.C b/gcc/testsuite/g++.dg/ext/is_nothrow_constructible1.C
new file mode 100644
index 00000000000..472acf9f88f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_nothrow_constructible1.C
@@ -0,0 +1,48 @@
+// { dg-do compile { target c++11 } }
+
+struct A { };
+struct B { B(); operator int(); };
+struct C {
+  C() = default;
+  C(const C&);
+  C(C&&) = default;
+  C& operator=(C&&);
+  C& operator= (const C&) = default;
+};
+struct D { ~D() noexcept(false) {} };
+
+#define SA(X) static_assert((X),#X)
+
+SA(__is_nothrow_constructible(A));
+SA(__is_nothrow_constructible(A,A));
+SA(!__is_nothrow_constructible(B));
+SA(__is_nothrow_constructible(B,B));
+
+SA(!__is_nothrow_constructible(A,B));
+SA(!__is_nothrow_constructible(B,A));
+
+SA(__is_nothrow_constructible(C));
+SA(__is_nothrow_constructible(C,C));
+SA(!__is_nothrow_constructible(C,C&));
+SA(__is_nothrow_assignable(C,C&));
+SA(!__is_nothrow_assignable(C,C));
+SA(!__is_nothrow_assignable(C,C&&));
+SA(!__is_nothrow_assignable(void,int));
+SA(!__is_nothrow_assignable(const void,int));
+SA(!__is_nothrow_assignable(volatile void,int));
+SA(!__is_nothrow_assignable(const volatile void,int));
+
+SA(__is_nothrow_constructible(int,int));
+SA(__is_nothrow_constructible(int,double));
+SA(!__is_nothrow_constructible(int,B));
+SA(!__is_nothrow_constructible(void,int));
+SA(!__is_nothrow_constructible(const void,int));
+SA(!__is_nothrow_constructible(volatile void,int));
+SA(!__is_nothrow_constructible(const volatile void,int));
+SA(!__is_nothrow_constructible(int, void*));
+SA(!__is_nothrow_constructible(int, int*));
+SA(!__is_nothrow_constructible(int, const int*));
+SA(!__is_nothrow_constructible(int*, void*));
+SA(!__is_nothrow_constructible(int*, const int*));
+
+SA(!__is_nothrow_constructible(D));
diff --git a/gcc/testsuite/g++.dg/ext/is_nothrow_constructible2.C b/gcc/testsuite/g++.dg/ext/is_nothrow_constructible2.C
new file mode 100644
index 00000000000..86b9668da6e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_nothrow_constructible2.C
@@ -0,0 +1,15 @@
+// { dg-do compile { target c++11 } }
+
+struct X {
+  X() = default;
+  template<class... U> X(U...) noexcept;
+};
+
+struct Y {
+  template<class... U> Y(U...);
+};
+
+#define SA(X) static_assert((X),#X)
+
+SA(__is_nothrow_constructible(X));
+SA(!__is_nothrow_constructible(Y));
diff --git a/gcc/testsuite/g++.dg/ext/is_nothrow_constructible3.C b/gcc/testsuite/g++.dg/ext/is_nothrow_constructible3.C
new file mode 100644
index 00000000000..220ee0bb89e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_nothrow_constructible3.C
@@ -0,0 +1,8 @@
+// { dg-do compile { target c++11 } }
+
+template <class T, class... Args> void bar() {
+  static_assert(__is_nothrow_constructible(T, Args...), "");
+}
+
+template void bar<int>();
+template void bar<int,int>();
diff --git a/gcc/testsuite/g++.dg/ext/is_nothrow_constructible4.C b/gcc/testsuite/g++.dg/ext/is_nothrow_constructible4.C
new file mode 100644
index 00000000000..9448c2d31e4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_nothrow_constructible4.C
@@ -0,0 +1,11 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+void f()
+{
+  int x;
+  auto l = [=]{ return x; };
+  typedef decltype(l) C;
+  SA(__is_nothrow_constructible(C,C));
+}
diff --git a/gcc/testsuite/g++.dg/ext/is_nothrow_constructible5.C b/gcc/testsuite/g++.dg/ext/is_nothrow_constructible5.C
new file mode 100644
index 00000000000..b8471130481
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_nothrow_constructible5.C
@@ -0,0 +1,12 @@
+// PR c++/80991
+// { dg-do compile { target c++11 } }
+
+template<bool> void foo()
+{
+  static_assert(__is_nothrow_constructible(int, int), "");
+}
+
+void bar()
+{
+  foo<true>();
+}
diff --git a/gcc/testsuite/g++.dg/ext/is_nothrow_constructible6.C b/gcc/testsuite/g++.dg/ext/is_nothrow_constructible6.C
new file mode 100644
index 00000000000..bdfdfb99de2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_nothrow_constructible6.C
@@ -0,0 +1,11 @@
+// { dg-do compile { target c++11 } }
+// PR c++/81589
+
+template <typename k>
+struct z {
+  z() noexcept {
+    k::error;
+  }
+};
+
+int x = __is_nothrow_constructible(z<int>);
diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 9994c9ae3d7..e9a0f55dd4a 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -963,47 +963,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	"template argument must be a complete class or an unbounded array");
     };
 
-  template<bool, typename _Tp, typename... _Args>
-    struct __is_nt_constructible_impl
-    : public false_type
-    { };
-
-  template<typename _Tp, typename... _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<true, _Tp, _Arg>
-    : public __bool_constant<noexcept(static_cast<_Tp>(std::declval<_Arg>()))>
-    { };
-
-  template<typename _Tp>
-    struct __is_nt_constructible_impl<true, _Tp>
-    : public __bool_constant<noexcept(_Tp())>
-    { };
-
-  template<typename _Tp, size_t _Num>
-    struct __is_nt_constructible_impl<true, _Tp[_Num]>
-    : public __bool_constant<noexcept(typename remove_all_extents<_Tp>::type())>
-    { };
-
-#if __cpp_aggregate_paren_init
-  template<typename _Tp, size_t _Num, typename _Arg>
-    struct __is_nt_constructible_impl<true, _Tp[_Num], _Arg>
-    : public __is_nt_constructible_impl<true, _Tp, _Arg>
-    { };
-
-  template<typename _Tp, size_t _Num, typename... _Args>
-    struct __is_nt_constructible_impl<true, _Tp[_Num], _Args...>
-    : public __and_<__is_nt_constructible_impl<true, _Tp, _Args>...>
-    { };
-#endif
-
   template<typename _Tp, typename... _Args>
     using __is_nothrow_constructible_impl
-      = __is_nt_constructible_impl<__is_constructible(_Tp, _Args...),
-				   _Tp, _Args...>;
+      = __bool_constant<__is_nothrow_constructible(_Tp, _Args...)>;
 
   /// is_nothrow_constructible
   template<typename _Tp, typename... _Args>
@@ -1017,7 +979,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   /// is_nothrow_default_constructible
   template<typename _Tp>
     struct is_nothrow_default_constructible
-    : public __is_nothrow_constructible_impl<_Tp>::type
+    : public __bool_constant<__is_nothrow_constructible(_Tp)>
     {
       static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}),
 	"template argument must be a complete class or an unbounded array");
@@ -1118,15 +1080,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     };
 
   template<typename _Tp, typename _Up>
-    struct __is_nt_assignable_impl
-    : public integral_constant<bool, noexcept(declval<_Tp>() = declval<_Up>())>
-    { };
-
-  template<typename _Tp, typename _Up>
-    struct __is_nothrow_assignable_impl
-    : public __and_<__bool_constant<__is_assignable(_Tp, _Up)>,
-		    __is_nt_assignable_impl<_Tp, _Up>>
-    { };
+    using __is_nothrow_assignable_impl
+      = __bool_constant<__is_nothrow_assignable(_Tp, _Up)>;
 
   /// is_nothrow_assignable
   template<typename _Tp, typename _Up>


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2020-10-26 13:37 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-10-26 13:37 [gcc r11-4386] c++: Implement __is_nothrow_constructible and __is_nothrow_assignable Ville Voutilainen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).