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:
parent
783dc02d89
commit
9e2256dcd4
13 changed files with 148 additions and 53 deletions
|
@ -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 },
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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 ();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
48
gcc/testsuite/g++.dg/ext/is_nothrow_constructible1.C
Normal file
48
gcc/testsuite/g++.dg/ext/is_nothrow_constructible1.C
Normal 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));
|
15
gcc/testsuite/g++.dg/ext/is_nothrow_constructible2.C
Normal file
15
gcc/testsuite/g++.dg/ext/is_nothrow_constructible2.C
Normal 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));
|
8
gcc/testsuite/g++.dg/ext/is_nothrow_constructible3.C
Normal file
8
gcc/testsuite/g++.dg/ext/is_nothrow_constructible3.C
Normal 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>();
|
11
gcc/testsuite/g++.dg/ext/is_nothrow_constructible4.C
Normal file
11
gcc/testsuite/g++.dg/ext/is_nothrow_constructible4.C
Normal 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));
|
||||
}
|
12
gcc/testsuite/g++.dg/ext/is_nothrow_constructible5.C
Normal file
12
gcc/testsuite/g++.dg/ext/is_nothrow_constructible5.C
Normal 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>();
|
||||
}
|
11
gcc/testsuite/g++.dg/ext/is_nothrow_constructible6.C
Normal file
11
gcc/testsuite/g++.dg/ext/is_nothrow_constructible6.C
Normal 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>);
|
|
@ -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>
|
||||
|
|
Loading…
Add table
Reference in a new issue