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.
This commit is contained in:
Ville Voutilainen 2020-10-26 15:36:24 +02:00
parent 783dc02d89
commit 9e2256dcd4
13 changed files with 148 additions and 53 deletions

View file

@ -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 },

View file

@ -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,

View file

@ -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);

View file

@ -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. */

View file

@ -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 ();
}

View file

@ -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;

View file

@ -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));

View file

@ -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));

View file

@ -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>();

View file

@ -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));
}

View file

@ -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>();
}

View file

@ -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>);

View file

@ -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>