+{
+ 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)
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 3cbe0d69de1..bb83d1c78f6 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -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;
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 86562071612..92fc795df40 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -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)
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index fe25cb2f669..17dabf648cf 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -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
diff --git a/gcc/testsuite/g++.dg/ext/is_convertible1.C
b/gcc/testsuite/g++.dg/ext/is_convertible1.C
new file mode 100644
index 00000000000..2e72945bceb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_convertible1.C
@@ -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));
+}
diff --git a/gcc/testsuite/g++.dg/ext/is_convertible2.C
b/gcc/testsuite/g++.dg/ext/is_convertible2.C
new file mode 100644
index 00000000000..9b46e264379
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_convertible2.C
@@ -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.);
+}
diff --git a/gcc/testsuite/g++.dg/ext/is_nothrow_convertible1.C
b/gcc/testsuite/g++.dg/ext/is_nothrow_convertible1.C
new file mode 100644
index 00000000000..bb7243e4611
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_nothrow_convertible1.C
@@ -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));
+}
diff --git a/gcc/testsuite/g++.dg/ext/is_nothrow_convertible2.C
b/gcc/testsuite/g++.dg/ext/is_nothrow_convertible2.C
new file mode 100644
index 00000000000..aa089173b75
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_nothrow_convertible2.C
@@ -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));
diff --git a/libstdc++-v3/include/std/type_traits
b/libstdc++-v3/include/std/type_traits
index 94e73eafd2f..1797b9e97f7 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -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
diff --git a/libstdc++-v3/testsuite/20_util/is_nothrow_convertible/value_ext.cc b/libstdc++-v3/testsuite/20_util/is_nothrow_convertible/value_ext.cc
index 0f896428537..d736d2ca260 100644
--- a/libstdc++-v3/testsuite/20_util/is_nothrow_convertible/value_ext.cc
+++ b/libstdc++-v3/testsuite/20_util/is_nothrow_convertible/value_ext.cc
@@ -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"
base-commit: 32d8123cd6ce87acb557aec230e8359051316f9f