c++: Implement __is_{nothrow_,}convertible [PR106784]
To improve compile times, the C++ library could use compiler built-ins rather than implementing std::is_convertible (and _nothrow) as class templates. This patch adds the built-ins. We already have __is_constructible and __is_assignable, and the nothrow forms of those. Microsoft (and clang, for compatibility) also provide an alias called __is_convertible_to. I did not add it, but it would be trivial to do so. I noticed that our __is_assignable doesn't implement the "Access checks are performed as if from a context unrelated to either type" requirement, therefore std::is_assignable / __is_assignable give two different results here: class S { operator int(); friend void g(); // #1 }; void g () { // #1 doesn't matter static_assert(std::is_assignable<int&, S>::value, ""); static_assert(__is_assignable(int&, S), ""); } This is not a problem if __is_assignable is not meant to be used by the users. This patch doesn't make libstdc++ use the new built-ins, but I had to rename a class otherwise its name would clash with the new built-in. PR c++/106784 gcc/c-family/ChangeLog: * c-common.cc (c_common_reswords): Add __is_convertible and __is_nothrow_convertible. * c-common.h (enum rid): Add RID_IS_CONVERTIBLE and RID_IS_NOTHROW_CONVERTIBLE. gcc/cp/ChangeLog: * constraint.cc (diagnose_trait_expr): Handle CPTK_IS_CONVERTIBLE and CPTK_IS_NOTHROW_CONVERTIBLE. * cp-objcp-common.cc (names_builtin_p): Handle RID_IS_CONVERTIBLE RID_IS_NOTHROW_CONVERTIBLE. * cp-tree.h (enum cp_trait_kind): Add CPTK_IS_CONVERTIBLE and CPTK_IS_NOTHROW_CONVERTIBLE. (is_convertible): Declare. (is_nothrow_convertible): Likewise. * cxx-pretty-print.cc (pp_cxx_trait_expression): Handle CPTK_IS_CONVERTIBLE and CPTK_IS_NOTHROW_CONVERTIBLE. * method.cc (is_convertible): New. (is_nothrow_convertible): Likewise. * parser.cc (cp_parser_primary_expression): Handle RID_IS_CONVERTIBLE and RID_IS_NOTHROW_CONVERTIBLE. (cp_parser_trait_expr): Likewise. * semantics.cc (trait_expr_value): Handle CPTK_IS_CONVERTIBLE and CPTK_IS_NOTHROW_CONVERTIBLE. (finish_trait_expr): Likewise. libstdc++-v3/ChangeLog: * include/std/type_traits: Rename __is_nothrow_convertible to __is_nothrow_convertible_lib. * testsuite/20_util/is_nothrow_convertible/value_ext.cc: Likewise. gcc/testsuite/ChangeLog: * g++.dg/ext/has-builtin-1.C: Enhance to test __is_convertible and __is_nothrow_convertible. * g++.dg/ext/is_convertible1.C: New test. * g++.dg/ext/is_convertible2.C: New test. * g++.dg/ext/is_nothrow_convertible1.C: New test. * g++.dg/ext/is_nothrow_convertible2.C: New test.
This commit is contained in:
parent
7d4df630c6
commit
8a7bcf95a8
16 changed files with 684 additions and 4 deletions
|
@ -541,6 +541,8 @@ const struct c_common_resword c_common_reswords[] =
|
|||
{ "__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 },
|
||||
{ "__is_convertible", RID_IS_CONVERTIBLE, D_CXXONLY },
|
||||
{ "__is_nothrow_convertible", RID_IS_NOTHROW_CONVERTIBLE, D_CXXONLY },
|
||||
{ "__reference_constructs_from_temporary", RID_REF_CONSTRUCTS_FROM_TEMPORARY,
|
||||
D_CXXONLY },
|
||||
{ "__reference_converts_from_temporary", RID_REF_CONVERTS_FROM_TEMPORARY,
|
||||
|
|
|
@ -184,6 +184,7 @@ enum rid
|
|||
RID_IS_UNION, RID_UNDERLYING_TYPE,
|
||||
RID_IS_ASSIGNABLE, RID_IS_CONSTRUCTIBLE,
|
||||
RID_IS_NOTHROW_ASSIGNABLE, RID_IS_NOTHROW_CONSTRUCTIBLE,
|
||||
RID_IS_CONVERTIBLE, RID_IS_NOTHROW_CONVERTIBLE,
|
||||
RID_REF_CONSTRUCTS_FROM_TEMPORARY,
|
||||
RID_REF_CONVERTS_FROM_TEMPORARY,
|
||||
|
||||
|
|
|
@ -3697,6 +3697,12 @@ diagnose_trait_expr (tree expr, tree args)
|
|||
case CPTK_HAS_UNIQUE_OBJ_REPRESENTATIONS:
|
||||
inform (loc, " %qT does not have unique object representations", t1);
|
||||
break;
|
||||
case CPTK_IS_CONVERTIBLE:
|
||||
inform (loc, " %qT is not convertible from %qE", t2, t1);
|
||||
break;
|
||||
case CPTK_IS_NOTHROW_CONVERTIBLE:
|
||||
inform (loc, " %qT is not %<nothrow%> convertible from %qE", t2, t1);
|
||||
break;
|
||||
case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
|
||||
inform (loc, " %qT is not a reference that binds to a temporary "
|
||||
"object of type %qT (direct-initialization)", t1, t2);
|
||||
|
|
|
@ -463,6 +463,8 @@ names_builtin_p (const char *name)
|
|||
case RID_IS_NOTHROW_ASSIGNABLE:
|
||||
case RID_IS_NOTHROW_CONSTRUCTIBLE:
|
||||
case RID_UNDERLYING_TYPE:
|
||||
case RID_IS_CONVERTIBLE:
|
||||
case RID_IS_NOTHROW_CONVERTIBLE:
|
||||
case RID_REF_CONSTRUCTS_FROM_TEMPORARY:
|
||||
case RID_REF_CONVERTS_FROM_TEMPORARY:
|
||||
return true;
|
||||
|
|
|
@ -1407,6 +1407,8 @@ enum cp_trait_kind
|
|||
CPTK_IS_CONSTRUCTIBLE,
|
||||
CPTK_IS_NOTHROW_ASSIGNABLE,
|
||||
CPTK_IS_NOTHROW_CONSTRUCTIBLE,
|
||||
CPTK_IS_CONVERTIBLE,
|
||||
CPTK_IS_NOTHROW_CONVERTIBLE,
|
||||
CPTK_REF_CONSTRUCTS_FROM_TEMPORARY,
|
||||
CPTK_REF_CONVERTS_FROM_TEMPORARY
|
||||
};
|
||||
|
@ -7116,6 +7118,8 @@ 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 bool is_convertible (tree, tree);
|
||||
extern bool is_nothrow_convertible (tree, tree);
|
||||
extern bool ref_xes_from_temporary (tree, tree, bool);
|
||||
extern tree get_defaulted_eh_spec (tree, tsubst_flags_t = tf_warning_or_error);
|
||||
extern bool maybe_explain_implicit_delete (tree);
|
||||
|
|
|
@ -2696,6 +2696,12 @@ pp_cxx_trait_expression (cxx_pretty_printer *pp, tree t)
|
|||
case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
|
||||
pp_cxx_ws_string (pp, "__is_nothrow_constructible");
|
||||
break;
|
||||
case CPTK_IS_CONVERTIBLE:
|
||||
pp_cxx_ws_string (pp, "__is_convertible");
|
||||
break;
|
||||
case CPTK_IS_NOTHROW_CONVERTIBLE:
|
||||
pp_cxx_ws_string (pp, "__is_nothrow_convertible");
|
||||
break;
|
||||
case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
|
||||
pp_cxx_ws_string (pp, "__reference_constructs_from_temporary");
|
||||
break;
|
||||
|
|
|
@ -2236,6 +2236,37 @@ ref_xes_from_temporary (tree to, tree from, bool direct_init_p)
|
|||
return ref_conv_binds_directly (to, val, direct_init_p).is_false ();
|
||||
}
|
||||
|
||||
/* Return true if FROM can be converted to TO using implicit conversions,
|
||||
or both FROM and TO are possibly cv-qualified void. NB: This doesn't
|
||||
implement the "Access checks are performed as if from a context unrelated
|
||||
to either type" restriction. */
|
||||
|
||||
bool
|
||||
is_convertible (tree from, tree to)
|
||||
{
|
||||
if (VOID_TYPE_P (from) && VOID_TYPE_P (to))
|
||||
return true;
|
||||
tree expr = build_stub_object (from);
|
||||
expr = perform_implicit_conversion (to, expr, tf_none);
|
||||
if (expr == error_mark_node)
|
||||
return false;
|
||||
return !!expr;
|
||||
}
|
||||
|
||||
/* Like is_convertible, but the conversion is also noexcept. */
|
||||
|
||||
bool
|
||||
is_nothrow_convertible (tree from, tree to)
|
||||
{
|
||||
if (VOID_TYPE_P (from) && VOID_TYPE_P (to))
|
||||
return true;
|
||||
tree expr = build_stub_object (from);
|
||||
expr = perform_implicit_conversion (to, expr, tf_none);
|
||||
if (expr == NULL_TREE || expr == error_mark_node)
|
||||
return false;
|
||||
return expr_noexcept_p (expr, tf_none);
|
||||
}
|
||||
|
||||
/* Categorize various special_function_kinds. */
|
||||
#define SFK_CTOR_P(sfk) \
|
||||
((sfk) >= sfk_constructor && (sfk) <= sfk_move_constructor)
|
||||
|
|
|
@ -5922,6 +5922,8 @@ cp_parser_primary_expression (cp_parser *parser,
|
|||
case RID_IS_CONSTRUCTIBLE:
|
||||
case RID_IS_NOTHROW_ASSIGNABLE:
|
||||
case RID_IS_NOTHROW_CONSTRUCTIBLE:
|
||||
case RID_IS_CONVERTIBLE:
|
||||
case RID_IS_NOTHROW_CONVERTIBLE:
|
||||
case RID_REF_CONSTRUCTS_FROM_TEMPORARY:
|
||||
case RID_REF_CONVERTS_FROM_TEMPORARY:
|
||||
return cp_parser_trait_expr (parser, token->keyword);
|
||||
|
@ -11008,6 +11010,14 @@ cp_parser_trait_expr (cp_parser* parser, enum rid keyword)
|
|||
kind = CPTK_IS_NOTHROW_CONSTRUCTIBLE;
|
||||
variadic = true;
|
||||
break;
|
||||
case RID_IS_CONVERTIBLE:
|
||||
kind = CPTK_IS_CONVERTIBLE;
|
||||
binary = true;
|
||||
break;
|
||||
case RID_IS_NOTHROW_CONVERTIBLE:
|
||||
kind = CPTK_IS_NOTHROW_CONVERTIBLE;
|
||||
binary = true;
|
||||
break;
|
||||
case RID_REF_CONSTRUCTS_FROM_TEMPORARY:
|
||||
kind = CPTK_REF_CONSTRUCTS_FROM_TEMPORARY;
|
||||
binary = true;
|
||||
|
|
|
@ -12044,6 +12044,12 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
|
|||
case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
|
||||
return is_nothrow_xible (INIT_EXPR, type1, type2);
|
||||
|
||||
case CPTK_IS_CONVERTIBLE:
|
||||
return is_convertible (type1, type2);
|
||||
|
||||
case CPTK_IS_NOTHROW_CONVERTIBLE:
|
||||
return is_nothrow_convertible (type1, type2);
|
||||
|
||||
case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
|
||||
return ref_xes_from_temporary (type1, type2, /*direct_init=*/true);
|
||||
|
||||
|
@ -12165,6 +12171,8 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
|
|||
case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE:
|
||||
case CPTK_IS_NOTHROW_ASSIGNABLE:
|
||||
case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
|
||||
case CPTK_IS_CONVERTIBLE:
|
||||
case CPTK_IS_NOTHROW_CONVERTIBLE:
|
||||
case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
|
||||
case CPTK_REF_CONVERTS_FROM_TEMPORARY:
|
||||
if (!check_trait_type (type1)
|
||||
|
|
|
@ -131,3 +131,9 @@
|
|||
#if !__has_builtin (__builtin_is_pointer_interconvertible_with_class)
|
||||
# error "__has_builtin (__builtin_is_pointer_interconvertible_with_class) failed"
|
||||
#endif
|
||||
#if !__has_builtin (__is_convertible)
|
||||
# error "__has_builtin (__is_convertible) failed"
|
||||
#endif
|
||||
#if !__has_builtin (__is_nothrow_convertible)
|
||||
# error "__has_builtin (__is_nothrow_convertible) failed"
|
||||
#endif
|
||||
|
|
269
gcc/testsuite/g++.dg/ext/is_convertible1.C
Normal file
269
gcc/testsuite/g++.dg/ext/is_convertible1.C
Normal file
|
@ -0,0 +1,269 @@
|
|||
// PR c++/106784
|
||||
// { dg-do compile { target c++11 } }
|
||||
|
||||
#define SA(X) static_assert((X),#X)
|
||||
|
||||
template<typename From, typename To>
|
||||
struct is_convertible {
|
||||
static const bool value = __is_convertible(From, To);
|
||||
};
|
||||
|
||||
struct from_int {
|
||||
from_int(int);
|
||||
};
|
||||
|
||||
struct from_charp {
|
||||
from_charp(const char *);
|
||||
};
|
||||
|
||||
struct to_int {
|
||||
operator int();
|
||||
};
|
||||
|
||||
typedef int Fn(int);
|
||||
typedef char Arr[10];
|
||||
enum E { XYZZY };
|
||||
|
||||
SA(!__is_convertible(int, void));
|
||||
SA(__is_convertible(int, int));
|
||||
SA(__is_convertible(int, from_int));
|
||||
SA(__is_convertible(long, from_int));
|
||||
SA(__is_convertible(double, from_int));
|
||||
SA(__is_convertible(const int, from_int));
|
||||
SA(__is_convertible(const int&, from_int));
|
||||
SA(__is_convertible(to_int, int));
|
||||
SA(__is_convertible(to_int, const int&));
|
||||
SA(__is_convertible(to_int, long));
|
||||
SA(!__is_convertible(to_int, int&));
|
||||
SA(!__is_convertible(to_int, from_int));
|
||||
SA(!__is_convertible(int, Fn));
|
||||
SA(!__is_convertible(int, Fn*));
|
||||
SA(!__is_convertible(int, Fn&));
|
||||
SA(!__is_convertible(int, Arr));
|
||||
SA(!__is_convertible(int, Arr&));
|
||||
SA(!__is_convertible(int, int&));
|
||||
SA(__is_convertible(int, const int&));
|
||||
SA(!__is_convertible(const int, int&));
|
||||
SA(__is_convertible(const int, const int&));
|
||||
SA(!__is_convertible(int, int*));
|
||||
|
||||
SA(!__is_convertible(int, E));
|
||||
SA(__is_convertible(E, int));
|
||||
|
||||
SA(__is_convertible(int&, int));
|
||||
SA(__is_convertible(int&, int&));
|
||||
SA(__is_convertible(int&, const int&));
|
||||
SA(!__is_convertible(const int&, int&));
|
||||
SA(__is_convertible(const int&, const int&));
|
||||
SA(!__is_convertible(int&, int*));
|
||||
SA(!__is_convertible(int&, void));
|
||||
SA(!__is_convertible(int&, Fn));
|
||||
SA(!__is_convertible(int&, Fn*));
|
||||
SA(!__is_convertible(int&, Fn&));
|
||||
SA(!__is_convertible(int&, Arr));
|
||||
SA(!__is_convertible(int&, Arr&));
|
||||
|
||||
SA(!__is_convertible(int*, int));
|
||||
SA(!__is_convertible(int*, int&));
|
||||
SA(!__is_convertible(int*, void));
|
||||
SA(__is_convertible(int*, int*));
|
||||
SA(__is_convertible(int*, const int*));
|
||||
SA(!__is_convertible(const int*, int*));
|
||||
SA(__is_convertible(const int*, const int*));
|
||||
SA(!__is_convertible(int*, Fn));
|
||||
SA(!__is_convertible(int*, Fn*));
|
||||
SA(!__is_convertible(int*, Fn&));
|
||||
SA(!__is_convertible(int*, Arr));
|
||||
SA(!__is_convertible(int*, Arr&));
|
||||
SA(!__is_convertible(int*, float*));
|
||||
|
||||
SA(__is_convertible(void, void));
|
||||
SA(!__is_convertible(void, char));
|
||||
SA(!__is_convertible(void, char&));
|
||||
SA(!__is_convertible(void, char*));
|
||||
SA(!__is_convertible(char, void));
|
||||
SA(__is_convertible(const void, void));
|
||||
SA(__is_convertible(void, const void));
|
||||
SA(__is_convertible(const void, const void));
|
||||
SA(!__is_convertible(void, Fn));
|
||||
SA(!__is_convertible(void, Fn&));
|
||||
SA(!__is_convertible(void, Fn*));
|
||||
SA(!__is_convertible(void, Arr));
|
||||
SA(!__is_convertible(void, Arr&));
|
||||
|
||||
SA(!__is_convertible(Fn, void));
|
||||
SA(!__is_convertible(Fn, Fn));
|
||||
SA(__is_convertible(Fn, Fn*));
|
||||
SA(__is_convertible(Fn, Fn&));
|
||||
SA(!__is_convertible(int(int), int(int)));
|
||||
SA(__is_convertible(int(int), int(&)(int)));
|
||||
SA(__is_convertible(int(int), int(&&)(int)));
|
||||
SA(__is_convertible(int(int), int(*)(int)));
|
||||
SA(__is_convertible(int(int), int(*const)(int)));
|
||||
SA(!__is_convertible(int(int), char));
|
||||
SA(!__is_convertible(int(int), char*));
|
||||
SA(!__is_convertible(int(int), char&));
|
||||
|
||||
SA(!__is_convertible(Fn&, void));
|
||||
SA(!__is_convertible(Fn&, Fn));
|
||||
SA(__is_convertible(Fn&, Fn&));
|
||||
SA(__is_convertible(Fn&, Fn*));
|
||||
SA(!__is_convertible(Fn&, Arr));
|
||||
SA(!__is_convertible(Fn&, Arr&));
|
||||
SA(!__is_convertible(Fn&, char));
|
||||
SA(!__is_convertible(Fn&, char&));
|
||||
SA(!__is_convertible(Fn&, char*));
|
||||
|
||||
SA(!__is_convertible(Fn*, void));
|
||||
SA(!__is_convertible(Fn*, Fn));
|
||||
SA(!__is_convertible(Fn*, Fn&));
|
||||
SA(__is_convertible(Fn*, Fn*));
|
||||
SA(!__is_convertible(Fn*, Arr));
|
||||
SA(!__is_convertible(Fn*, Arr&));
|
||||
SA(!__is_convertible(Fn*, char));
|
||||
SA(!__is_convertible(Fn*, char&));
|
||||
SA(!__is_convertible(Fn*, char*));
|
||||
|
||||
SA(!__is_convertible(Arr, void));
|
||||
SA(!__is_convertible(Arr, Fn));
|
||||
SA(!__is_convertible(Arr, Fn*));
|
||||
SA(!__is_convertible(Arr, Fn&));
|
||||
SA(!__is_convertible(Arr, Arr));
|
||||
SA(!__is_convertible(Arr, Arr&));
|
||||
SA(__is_convertible(Arr, const Arr&));
|
||||
SA(!__is_convertible(Arr, volatile Arr&));
|
||||
SA(!__is_convertible(Arr, const volatile Arr&));
|
||||
SA(!__is_convertible(const Arr, Arr&));
|
||||
SA(__is_convertible(const Arr, const Arr&));
|
||||
SA(__is_convertible(Arr, Arr&&));
|
||||
SA(__is_convertible(Arr, const Arr&&));
|
||||
SA(__is_convertible(Arr, volatile Arr&&));
|
||||
SA(__is_convertible(Arr, const volatile Arr&&));
|
||||
SA(__is_convertible(const Arr, const Arr&&));
|
||||
SA(!__is_convertible(Arr&, Arr&&));
|
||||
SA(!__is_convertible(Arr&&, Arr&));
|
||||
SA(!__is_convertible(Arr, char));
|
||||
SA(__is_convertible(Arr, char*));
|
||||
SA(__is_convertible(Arr, const char*));
|
||||
SA(!__is_convertible(Arr, char&));
|
||||
SA(!__is_convertible(const Arr, char*));
|
||||
SA(__is_convertible(const Arr, const char*));
|
||||
SA(!__is_convertible(int, int[1]));
|
||||
SA(!__is_convertible(int[1], int[1]));
|
||||
SA(!__is_convertible(int[1], int(&)[1]));
|
||||
SA(__is_convertible(int(&)[1], int(&)[1]));
|
||||
SA(__is_convertible(int(&)[1], const int(&)[1]));
|
||||
SA(!__is_convertible(const int(&)[1], int(&)[1]));
|
||||
SA(!__is_convertible(int[1][1], int*));
|
||||
SA(!__is_convertible(int[][1], int*));
|
||||
|
||||
SA(!__is_convertible(Arr&, void));
|
||||
SA(!__is_convertible(Arr&, Fn));
|
||||
SA(!__is_convertible(Arr&, Fn*));
|
||||
SA(!__is_convertible(Arr&, Fn&));
|
||||
SA(!__is_convertible(Arr&, Arr));
|
||||
SA(__is_convertible(Arr&, Arr&));
|
||||
SA(__is_convertible(Arr&, const Arr&));
|
||||
SA(!__is_convertible(const Arr&, Arr&));
|
||||
SA(__is_convertible(const Arr&, const Arr&));
|
||||
SA(!__is_convertible(Arr&, char));
|
||||
SA(__is_convertible(Arr&, char*));
|
||||
SA(__is_convertible(Arr&, const char*));
|
||||
SA(!__is_convertible(Arr&, char&));
|
||||
SA(!__is_convertible(const Arr&, char*));
|
||||
SA(__is_convertible(const Arr&, const char*));
|
||||
SA(__is_convertible(Arr, from_charp));
|
||||
SA(__is_convertible(Arr&, from_charp));
|
||||
|
||||
struct B { };
|
||||
struct D : B { };
|
||||
|
||||
SA(__is_convertible(D, B));
|
||||
SA(__is_convertible(D*, B*));
|
||||
SA(__is_convertible(D&, B&));
|
||||
SA(!__is_convertible(B, D));
|
||||
SA(!__is_convertible(B*, D*));
|
||||
SA(!__is_convertible(B&, D&));
|
||||
|
||||
/* These are taken from LLVM's test/SemaCXX/type-traits.cpp. */
|
||||
|
||||
struct I {
|
||||
int i;
|
||||
I(int _i) : i(_i) { }
|
||||
operator int() const {
|
||||
return i;
|
||||
}
|
||||
};
|
||||
|
||||
struct F
|
||||
{
|
||||
float f;
|
||||
F(float _f) : f(_f) {}
|
||||
F(const I& obj)
|
||||
: f(static_cast<float>(obj.i)) {}
|
||||
operator float() const {
|
||||
return f;
|
||||
}
|
||||
operator I() const {
|
||||
return I(static_cast<int>(f));
|
||||
}
|
||||
};
|
||||
|
||||
SA(__is_convertible(I, I));
|
||||
SA(__is_convertible(I, const I));
|
||||
SA(__is_convertible(I, int));
|
||||
SA(__is_convertible(int, I));
|
||||
SA(__is_convertible(I, F));
|
||||
SA(__is_convertible(F, I));
|
||||
SA(__is_convertible(F, float));
|
||||
SA(__is_convertible(float, F));
|
||||
|
||||
template<typename>
|
||||
struct X {
|
||||
template<typename U> X(const X<U>&);
|
||||
};
|
||||
|
||||
SA(__is_convertible(X<int>, X<float>));
|
||||
SA(__is_convertible(X<float>, X<int>));
|
||||
|
||||
struct Abstract {
|
||||
virtual void f() = 0;
|
||||
};
|
||||
|
||||
SA(!__is_convertible(Abstract, Abstract));
|
||||
|
||||
class hidden {
|
||||
hidden(const hidden&);
|
||||
friend void test ();
|
||||
};
|
||||
|
||||
SA(__is_convertible(hidden&, hidden&));
|
||||
SA(__is_convertible(hidden&, const hidden&));
|
||||
SA(__is_convertible(hidden&, volatile hidden&));
|
||||
SA(__is_convertible(hidden&, const volatile hidden&));
|
||||
SA(__is_convertible(const hidden&, const hidden&));
|
||||
SA(__is_convertible(const hidden&, const volatile hidden&));
|
||||
SA(__is_convertible(volatile hidden&, const volatile hidden&));
|
||||
SA(__is_convertible(const volatile hidden&, const volatile hidden&));
|
||||
SA(!__is_convertible(const hidden&, hidden&));
|
||||
|
||||
void
|
||||
test ()
|
||||
{
|
||||
/* __is_convertible(hidden, hidden) should be false despite the
|
||||
friend declaration above, because "Access checks are performed
|
||||
as if from a context unrelated to either type", but we don't
|
||||
implement that for the built-in (std::is_convertible works as
|
||||
expected). This is the case for __is_assignable as well. */
|
||||
//SA(!__is_convertible(hidden, hidden));
|
||||
}
|
||||
|
||||
void
|
||||
test2 ()
|
||||
{
|
||||
struct X { };
|
||||
struct Y {
|
||||
explicit Y(X); // not viable for implicit conversions
|
||||
};
|
||||
SA(!__is_convertible(X, Y));
|
||||
}
|
46
gcc/testsuite/g++.dg/ext/is_convertible2.C
Normal file
46
gcc/testsuite/g++.dg/ext/is_convertible2.C
Normal file
|
@ -0,0 +1,46 @@
|
|||
// PR c++/106784
|
||||
// { dg-do compile { target c++20 } }
|
||||
// Adapted from <https://en.cppreference.com/w/cpp/types/is_convertible>.
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#define SA(X) static_assert((X),#X)
|
||||
|
||||
class E { public: template<class T> E(T&&) { } };
|
||||
|
||||
int main()
|
||||
{
|
||||
class A {};
|
||||
class B : public A {};
|
||||
class C {};
|
||||
class D { public: operator C() { return c; } C c; };
|
||||
|
||||
SA(__is_convertible(B*, A*));
|
||||
SA(!__is_convertible(A*, B*));
|
||||
SA(__is_convertible(D, C));
|
||||
SA(!__is_convertible(B*, C*));
|
||||
SA(__is_convertible(A, E));
|
||||
|
||||
using std::operator "" s, std::operator "" sv;
|
||||
|
||||
auto stringify = []<typename T>(T x) {
|
||||
if constexpr (std::is_convertible_v<T, std::string> or
|
||||
std::is_convertible_v<T, std::string_view>) {
|
||||
return x;
|
||||
} else {
|
||||
return std::to_string(x);
|
||||
}
|
||||
};
|
||||
|
||||
const char* three = "three";
|
||||
|
||||
SA(!__is_convertible(std::string_view, std::string));
|
||||
SA(__is_convertible(std::string, std::string_view));
|
||||
|
||||
auto s1 = stringify("one"s);
|
||||
auto s2 = stringify("two"sv);
|
||||
auto s3 = stringify(three);
|
||||
auto s4 = stringify(42);
|
||||
auto s5 = stringify(42.);
|
||||
}
|
270
gcc/testsuite/g++.dg/ext/is_nothrow_convertible1.C
Normal file
270
gcc/testsuite/g++.dg/ext/is_nothrow_convertible1.C
Normal file
|
@ -0,0 +1,270 @@
|
|||
// PR c++/106784
|
||||
// { dg-do compile { target c++11 } }
|
||||
// Like is_convertible1.C, but conversion functions are made noexcept.
|
||||
|
||||
#define SA(X) static_assert((X),#X)
|
||||
|
||||
template<typename From, typename To>
|
||||
struct is_nothrow_convertible {
|
||||
static const bool value = __is_nothrow_convertible(From, To);
|
||||
};
|
||||
|
||||
struct from_int {
|
||||
from_int(int) noexcept;
|
||||
};
|
||||
|
||||
struct from_charp {
|
||||
from_charp(const char *) noexcept;
|
||||
};
|
||||
|
||||
struct to_int {
|
||||
operator int() noexcept;
|
||||
};
|
||||
|
||||
typedef int Fn(int);
|
||||
typedef char Arr[10];
|
||||
enum E { XYZZY };
|
||||
|
||||
SA(!__is_nothrow_convertible(int, void));
|
||||
SA(__is_nothrow_convertible(int, int));
|
||||
SA(__is_nothrow_convertible(int, from_int));
|
||||
SA(__is_nothrow_convertible(long, from_int));
|
||||
SA(__is_nothrow_convertible(double, from_int));
|
||||
SA(__is_nothrow_convertible(const int, from_int));
|
||||
SA(__is_nothrow_convertible(const int&, from_int));
|
||||
SA(__is_nothrow_convertible(to_int, int));
|
||||
SA(__is_nothrow_convertible(to_int, const int&));
|
||||
SA(__is_nothrow_convertible(to_int, long));
|
||||
SA(!__is_nothrow_convertible(to_int, int&));
|
||||
SA(!__is_nothrow_convertible(to_int, from_int));
|
||||
SA(!__is_nothrow_convertible(int, Fn));
|
||||
SA(!__is_nothrow_convertible(int, Fn*));
|
||||
SA(!__is_nothrow_convertible(int, Fn&));
|
||||
SA(!__is_nothrow_convertible(int, Arr));
|
||||
SA(!__is_nothrow_convertible(int, Arr&));
|
||||
SA(!__is_nothrow_convertible(int, int&));
|
||||
SA(__is_nothrow_convertible(int, const int&));
|
||||
SA(!__is_nothrow_convertible(const int, int&));
|
||||
SA(__is_nothrow_convertible(const int, const int&));
|
||||
SA(!__is_nothrow_convertible(int, int*));
|
||||
|
||||
SA(!__is_nothrow_convertible(int, E));
|
||||
SA(__is_nothrow_convertible(E, int));
|
||||
|
||||
SA(__is_nothrow_convertible(int&, int));
|
||||
SA(__is_nothrow_convertible(int&, int&));
|
||||
SA(__is_nothrow_convertible(int&, const int&));
|
||||
SA(!__is_nothrow_convertible(const int&, int&));
|
||||
SA(__is_nothrow_convertible(const int&, const int&));
|
||||
SA(!__is_nothrow_convertible(int&, int*));
|
||||
SA(!__is_nothrow_convertible(int&, void));
|
||||
SA(!__is_nothrow_convertible(int&, Fn));
|
||||
SA(!__is_nothrow_convertible(int&, Fn*));
|
||||
SA(!__is_nothrow_convertible(int&, Fn&));
|
||||
SA(!__is_nothrow_convertible(int&, Arr));
|
||||
SA(!__is_nothrow_convertible(int&, Arr&));
|
||||
|
||||
SA(!__is_nothrow_convertible(int*, int));
|
||||
SA(!__is_nothrow_convertible(int*, int&));
|
||||
SA(!__is_nothrow_convertible(int*, void));
|
||||
SA(__is_nothrow_convertible(int*, int*));
|
||||
SA(__is_nothrow_convertible(int*, const int*));
|
||||
SA(!__is_nothrow_convertible(const int*, int*));
|
||||
SA(__is_nothrow_convertible(const int*, const int*));
|
||||
SA(!__is_nothrow_convertible(int*, Fn));
|
||||
SA(!__is_nothrow_convertible(int*, Fn*));
|
||||
SA(!__is_nothrow_convertible(int*, Fn&));
|
||||
SA(!__is_nothrow_convertible(int*, Arr));
|
||||
SA(!__is_nothrow_convertible(int*, Arr&));
|
||||
SA(!__is_nothrow_convertible(int*, float*));
|
||||
|
||||
SA(__is_nothrow_convertible(void, void));
|
||||
SA(!__is_nothrow_convertible(void, char));
|
||||
SA(!__is_nothrow_convertible(void, char&));
|
||||
SA(!__is_nothrow_convertible(void, char*));
|
||||
SA(!__is_nothrow_convertible(char, void));
|
||||
SA(__is_nothrow_convertible(const void, void));
|
||||
SA(__is_nothrow_convertible(void, const void));
|
||||
SA(__is_nothrow_convertible(const void, const void));
|
||||
SA(!__is_nothrow_convertible(void, Fn));
|
||||
SA(!__is_nothrow_convertible(void, Fn&));
|
||||
SA(!__is_nothrow_convertible(void, Fn*));
|
||||
SA(!__is_nothrow_convertible(void, Arr));
|
||||
SA(!__is_nothrow_convertible(void, Arr&));
|
||||
|
||||
SA(!__is_nothrow_convertible(Fn, void));
|
||||
SA(!__is_nothrow_convertible(Fn, Fn));
|
||||
SA(__is_nothrow_convertible(Fn, Fn*));
|
||||
SA(__is_nothrow_convertible(Fn, Fn&));
|
||||
SA(!__is_nothrow_convertible(int(int), int(int)));
|
||||
SA(__is_nothrow_convertible(int(int), int(&)(int)));
|
||||
SA(__is_nothrow_convertible(int(int), int(&&)(int)));
|
||||
SA(__is_nothrow_convertible(int(int), int(*)(int)));
|
||||
SA(__is_nothrow_convertible(int(int), int(*const)(int)));
|
||||
SA(!__is_nothrow_convertible(int(int), char));
|
||||
SA(!__is_nothrow_convertible(int(int), char*));
|
||||
SA(!__is_nothrow_convertible(int(int), char&));
|
||||
|
||||
SA(!__is_nothrow_convertible(Fn&, void));
|
||||
SA(!__is_nothrow_convertible(Fn&, Fn));
|
||||
SA(__is_nothrow_convertible(Fn&, Fn&));
|
||||
SA(__is_nothrow_convertible(Fn&, Fn*));
|
||||
SA(!__is_nothrow_convertible(Fn&, Arr));
|
||||
SA(!__is_nothrow_convertible(Fn&, Arr&));
|
||||
SA(!__is_nothrow_convertible(Fn&, char));
|
||||
SA(!__is_nothrow_convertible(Fn&, char&));
|
||||
SA(!__is_nothrow_convertible(Fn&, char*));
|
||||
|
||||
SA(!__is_nothrow_convertible(Fn*, void));
|
||||
SA(!__is_nothrow_convertible(Fn*, Fn));
|
||||
SA(!__is_nothrow_convertible(Fn*, Fn&));
|
||||
SA(__is_nothrow_convertible(Fn*, Fn*));
|
||||
SA(!__is_nothrow_convertible(Fn*, Arr));
|
||||
SA(!__is_nothrow_convertible(Fn*, Arr&));
|
||||
SA(!__is_nothrow_convertible(Fn*, char));
|
||||
SA(!__is_nothrow_convertible(Fn*, char&));
|
||||
SA(!__is_nothrow_convertible(Fn*, char*));
|
||||
|
||||
SA(!__is_nothrow_convertible(Arr, void));
|
||||
SA(!__is_nothrow_convertible(Arr, Fn));
|
||||
SA(!__is_nothrow_convertible(Arr, Fn*));
|
||||
SA(!__is_nothrow_convertible(Arr, Fn&));
|
||||
SA(!__is_nothrow_convertible(Arr, Arr));
|
||||
SA(!__is_nothrow_convertible(Arr, Arr&));
|
||||
SA(__is_nothrow_convertible(Arr, const Arr&));
|
||||
SA(!__is_nothrow_convertible(Arr, volatile Arr&));
|
||||
SA(!__is_nothrow_convertible(Arr, const volatile Arr&));
|
||||
SA(!__is_nothrow_convertible(const Arr, Arr&));
|
||||
SA(__is_nothrow_convertible(const Arr, const Arr&));
|
||||
SA(__is_nothrow_convertible(Arr, Arr&&));
|
||||
SA(__is_nothrow_convertible(Arr, const Arr&&));
|
||||
SA(__is_nothrow_convertible(Arr, volatile Arr&&));
|
||||
SA(__is_nothrow_convertible(Arr, const volatile Arr&&));
|
||||
SA(__is_nothrow_convertible(const Arr, const Arr&&));
|
||||
SA(!__is_nothrow_convertible(Arr&, Arr&&));
|
||||
SA(!__is_nothrow_convertible(Arr&&, Arr&));
|
||||
SA(!__is_nothrow_convertible(Arr, char));
|
||||
SA(__is_nothrow_convertible(Arr, char*));
|
||||
SA(__is_nothrow_convertible(Arr, const char*));
|
||||
SA(!__is_nothrow_convertible(Arr, char&));
|
||||
SA(!__is_nothrow_convertible(const Arr, char*));
|
||||
SA(__is_nothrow_convertible(const Arr, const char*));
|
||||
SA(!__is_nothrow_convertible(int, int[1]));
|
||||
SA(!__is_nothrow_convertible(int[1], int[1]));
|
||||
SA(!__is_nothrow_convertible(int[1], int(&)[1]));
|
||||
SA(__is_nothrow_convertible(int(&)[1], int(&)[1]));
|
||||
SA(__is_nothrow_convertible(int(&)[1], const int(&)[1]));
|
||||
SA(!__is_nothrow_convertible(const int(&)[1], int(&)[1]));
|
||||
SA(!__is_nothrow_convertible(int[1][1], int*));
|
||||
SA(!__is_nothrow_convertible(int[][1], int*));
|
||||
|
||||
SA(!__is_nothrow_convertible(Arr&, void));
|
||||
SA(!__is_nothrow_convertible(Arr&, Fn));
|
||||
SA(!__is_nothrow_convertible(Arr&, Fn*));
|
||||
SA(!__is_nothrow_convertible(Arr&, Fn&));
|
||||
SA(!__is_nothrow_convertible(Arr&, Arr));
|
||||
SA(__is_nothrow_convertible(Arr&, Arr&));
|
||||
SA(__is_nothrow_convertible(Arr&, const Arr&));
|
||||
SA(!__is_nothrow_convertible(const Arr&, Arr&));
|
||||
SA(__is_nothrow_convertible(const Arr&, const Arr&));
|
||||
SA(!__is_nothrow_convertible(Arr&, char));
|
||||
SA(__is_nothrow_convertible(Arr&, char*));
|
||||
SA(__is_nothrow_convertible(Arr&, const char*));
|
||||
SA(!__is_nothrow_convertible(Arr&, char&));
|
||||
SA(!__is_nothrow_convertible(const Arr&, char*));
|
||||
SA(__is_nothrow_convertible(const Arr&, const char*));
|
||||
SA(__is_nothrow_convertible(Arr, from_charp));
|
||||
SA(__is_nothrow_convertible(Arr&, from_charp));
|
||||
|
||||
struct B { };
|
||||
struct D : B { };
|
||||
|
||||
SA(__is_nothrow_convertible(D, B));
|
||||
SA(__is_nothrow_convertible(D*, B*));
|
||||
SA(__is_nothrow_convertible(D&, B&));
|
||||
SA(!__is_nothrow_convertible(B, D));
|
||||
SA(!__is_nothrow_convertible(B*, D*));
|
||||
SA(!__is_nothrow_convertible(B&, D&));
|
||||
|
||||
/* These are taken from LLVM's test/SemaCXX/type-traits.cpp. */
|
||||
|
||||
struct I {
|
||||
int i;
|
||||
I(int _i) noexcept : i(_i) { }
|
||||
operator int() const noexcept {
|
||||
return i;
|
||||
}
|
||||
};
|
||||
|
||||
struct F
|
||||
{
|
||||
float f;
|
||||
F(float _f) noexcept : f(_f) {}
|
||||
F(const I& obj) noexcept
|
||||
: f(static_cast<float>(obj.i)) {}
|
||||
operator float() const noexcept {
|
||||
return f;
|
||||
}
|
||||
operator I() const noexcept {
|
||||
return I(static_cast<int>(f));
|
||||
}
|
||||
};
|
||||
|
||||
SA(__is_nothrow_convertible(I, I));
|
||||
SA(__is_nothrow_convertible(I, const I));
|
||||
SA(__is_nothrow_convertible(I, int));
|
||||
SA(__is_nothrow_convertible(int, I));
|
||||
SA(__is_nothrow_convertible(I, F));
|
||||
SA(__is_nothrow_convertible(F, I));
|
||||
SA(__is_nothrow_convertible(F, float));
|
||||
SA(__is_nothrow_convertible(float, F));
|
||||
|
||||
template<typename>
|
||||
struct X {
|
||||
template<typename U> X(const X<U>&) noexcept;
|
||||
};
|
||||
|
||||
SA(__is_nothrow_convertible(X<int>, X<float>));
|
||||
SA(__is_nothrow_convertible(X<float>, X<int>));
|
||||
|
||||
struct Abstract {
|
||||
virtual void f() = 0;
|
||||
};
|
||||
|
||||
SA(!__is_nothrow_convertible(Abstract, Abstract));
|
||||
|
||||
class hidden {
|
||||
hidden(const hidden&);
|
||||
friend void test ();
|
||||
};
|
||||
|
||||
SA(__is_nothrow_convertible(hidden&, hidden&));
|
||||
SA(__is_nothrow_convertible(hidden&, const hidden&));
|
||||
SA(__is_nothrow_convertible(hidden&, volatile hidden&));
|
||||
SA(__is_nothrow_convertible(hidden&, const volatile hidden&));
|
||||
SA(__is_nothrow_convertible(const hidden&, const hidden&));
|
||||
SA(__is_nothrow_convertible(const hidden&, const volatile hidden&));
|
||||
SA(__is_nothrow_convertible(volatile hidden&, const volatile hidden&));
|
||||
SA(__is_nothrow_convertible(const volatile hidden&, const volatile hidden&));
|
||||
SA(!__is_nothrow_convertible(const hidden&, hidden&));
|
||||
|
||||
void
|
||||
test ()
|
||||
{
|
||||
/* __is_nothrow_convertible(hidden, hidden) should be false despite the
|
||||
friend declaration above, because "Access checks are performed
|
||||
as if from a context unrelated to either type", but we don't
|
||||
implement that for the built-in (std::is_convertible works as
|
||||
expected). This is the case for __is_assignable as well. */
|
||||
//SA(!__is_nothrow_convertible(hidden, hidden));
|
||||
}
|
||||
|
||||
void
|
||||
test2 ()
|
||||
{
|
||||
struct X { };
|
||||
struct Y {
|
||||
explicit Y(X); // not viable for implicit conversions
|
||||
};
|
||||
SA(!__is_nothrow_convertible(X, Y));
|
||||
}
|
19
gcc/testsuite/g++.dg/ext/is_nothrow_convertible2.C
Normal file
19
gcc/testsuite/g++.dg/ext/is_nothrow_convertible2.C
Normal file
|
@ -0,0 +1,19 @@
|
|||
// PR c++/106784
|
||||
// { dg-do compile { target c++11 } }
|
||||
|
||||
#define SA(X) static_assert((X),#X)
|
||||
|
||||
struct A { };
|
||||
struct B { };
|
||||
|
||||
struct M {
|
||||
operator A();
|
||||
operator B() noexcept;
|
||||
M(const A&);
|
||||
M(const B&) noexcept;
|
||||
};
|
||||
|
||||
SA(!__is_nothrow_convertible(A, M));
|
||||
SA(!__is_nothrow_convertible(M, A));
|
||||
SA(__is_nothrow_convertible(B, M));
|
||||
SA(__is_nothrow_convertible(M, B));
|
|
@ -1453,7 +1453,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
|||
|
||||
// is_nothrow_convertible for C++11
|
||||
template<typename _From, typename _To>
|
||||
struct __is_nothrow_convertible
|
||||
struct __is_nothrow_convertible_lib
|
||||
: public __is_nt_convertible_helper<_From, _To>::type
|
||||
{ };
|
||||
|
||||
|
@ -2999,7 +2999,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
|||
struct __is_nt_invocable_impl<_Result, _Ret,
|
||||
__void_t<typename _Result::type>>
|
||||
: __or_<is_void<_Ret>,
|
||||
__is_nothrow_convertible<typename _Result::type, _Ret>>::type
|
||||
__is_nothrow_convertible_lib<typename _Result::type, _Ret>>::type
|
||||
{ };
|
||||
/// @endcond
|
||||
|
||||
|
|
|
@ -19,10 +19,10 @@
|
|||
|
||||
#include <type_traits>
|
||||
|
||||
// Test the non-standard __is_nothrow_convertible trait
|
||||
// Test the non-standard __is_nothrow_convertible_lib trait
|
||||
|
||||
template<typename From, typename To>
|
||||
using is_nothrow_convertible = std::__is_nothrow_convertible<From, To>;
|
||||
using is_nothrow_convertible = std::__is_nothrow_convertible_lib<From, To>;
|
||||
|
||||
#define IS_NT_CONVERTIBLE_DEFINED
|
||||
#include "value.cc"
|
||||
|
|
Loading…
Add table
Reference in a new issue