From 9e2256dcd481ffe3a8c79b65eba19fbb14b7ff8d Mon Sep 17 00:00:00 2001 From: Ville Voutilainen Date: Mon, 26 Oct 2020 15:36:24 +0200 Subject: [PATCH] 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. --- 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(-) create mode 100644 gcc/testsuite/g++.dg/ext/is_nothrow_constructible1.C create mode 100644 gcc/testsuite/g++.dg/ext/is_nothrow_constructible2.C create mode 100644 gcc/testsuite/g++.dg/ext/is_nothrow_constructible3.C create mode 100644 gcc/testsuite/g++.dg/ext/is_nothrow_constructible4.C create mode 100644 gcc/testsuite/g++.dg/ext/is_nothrow_constructible5.C create mode 100644 gcc/testsuite/g++.dg/ext/is_nothrow_constructible6.C 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 X(U...) noexcept; +}; + +struct Y { + template 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 void bar() { + static_assert(__is_nothrow_constructible(T, Args...), ""); +} + +template void bar(); +template void bar(); 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 void foo() +{ + static_assert(__is_nothrow_constructible(int, int), ""); +} + +void bar() +{ + foo(); +} 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 +struct z { + z() noexcept { + k::error; + } +}; + +int x = __is_nothrow_constructible(z); 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 - struct __is_nt_constructible_impl - : public false_type - { }; - - template - struct __is_nt_constructible_impl - : public __bool_constant()...))> - { }; - - template - struct __is_nt_constructible_impl - : public __bool_constant(std::declval<_Arg>()))> - { }; - - template - struct __is_nt_constructible_impl - : public __bool_constant - { }; - - template - struct __is_nt_constructible_impl - : public __bool_constant::type())> - { }; - -#if __cpp_aggregate_paren_init - template - struct __is_nt_constructible_impl - : public __is_nt_constructible_impl - { }; - - template - struct __is_nt_constructible_impl - : public __and_<__is_nt_constructible_impl...> - { }; -#endif - template 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 @@ -1017,7 +979,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION /// is_nothrow_default_constructible template 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 - struct __is_nt_assignable_impl - : public integral_constant() = declval<_Up>())> - { }; - - template - 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 -- 2.30.2