From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id 970463858D38 for ; Thu, 22 Sep 2022 22:14:49 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 970463858D38 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1663884889; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=s3B89jZAQdoIPpBV97QUFYnUrPfM4L48LPYN25ImQuU=; b=h4YfscKHfmlAt1kY9iZOqbwYRnsDF2nV6mO770hps2Dw9zMClCWyH/VcJteArr/Q6/Lxb3 B0A2boEVgGjTTv4cT2bEcpcvKdpy+9IsEBtmzlDvxE29SeznqeShhVyu7YeOappR4cMcnD VV0bhzoBCtc21RNqL6OPWUIEf0Yn6ZM= Received: from mail-qk1-f199.google.com (mail-qk1-f199.google.com [209.85.222.199]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_128_GCM_SHA256) id us-mta-262-aD-WtibYM4akZcsJQ7Ip8g-1; Thu, 22 Sep 2022 18:14:48 -0400 X-MC-Unique: aD-WtibYM4akZcsJQ7Ip8g-1 Received: by mail-qk1-f199.google.com with SMTP id bs33-20020a05620a472100b006cef8cfabe2so7606554qkb.12 for ; Thu, 22 Sep 2022 15:14:48 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:in-reply-to:from:references:to :content-language:subject:user-agent:mime-version:date:message-id :x-gm-message-state:from:to:cc:subject:date; bh=s3B89jZAQdoIPpBV97QUFYnUrPfM4L48LPYN25ImQuU=; b=U5Yt9egdZkCqg344P+ZbrfJDUqTnRi7FCIgvYgOj5GPf9x4EtWechdFMYG4VTYXU4W NUq53bLtCaf+mzmpmxoBa2/u42paWUgVI/WZW8QKNH7FCZNRo/ZP+7oGIMFKJJ9LuocI eiMaM0todWprA59xIrT8FlBuNmcq/LyY41mDkNpLJdwjyVhrLUKfY5JDfiLSDBYc+x60 BKi6h/8eDv9gq3xD6EzDY/RPQUnDFEu/M4R9lD0dfIt2zYIhsuVzYJpiqczC1VtAHKXh DolDeS1y6Jz7GRcrzLX1s+QcfpnmuFWsW/bW2XYb74E/OzvUOn3y5TxhnequgxU63Ens 8BDw== X-Gm-Message-State: ACrzQf3O5lwq9+NMhQfy3ckPxQYbKMKhS092rHglw78cfHtD5iQYQGiW S1O5vjIYnFuD5L0j0Ip9SKBekbl+FsjRNpYH8qre2py0jGKFeBJaZeRX4KXmUEs38XsX3mAY7K7 efIlV7zInlE9KjNI= X-Received: by 2002:a37:9a84:0:b0:6cd:dbad:a207 with SMTP id c126-20020a379a84000000b006cddbada207mr3790379qke.169.1663884887220; Thu, 22 Sep 2022 15:14:47 -0700 (PDT) X-Google-Smtp-Source: AMsMyM4afvqrdiEKTtGYaygSCw0gEVsrzBbX1HoEBN1Q4bIlPeQFWDfoLhYxHLhhQ4Ie03TlBKSeig== X-Received: by 2002:a37:9a84:0:b0:6cd:dbad:a207 with SMTP id c126-20020a379a84000000b006cddbada207mr3790337qke.169.1663884886639; Thu, 22 Sep 2022 15:14:46 -0700 (PDT) Received: from [192.168.1.101] (130-44-159-43.s15913.c3-0.arl-cbr1.sbo-arl.ma.cable.rcncustomer.com. [130.44.159.43]) by smtp.gmail.com with ESMTPSA id 139-20020a370a91000000b006ce60296f97sm4505572qkk.68.2022.09.22.15.14.45 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Thu, 22 Sep 2022 15:14:46 -0700 (PDT) Message-ID: Date: Thu, 22 Sep 2022 18:14:44 -0400 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Thunderbird/91.13.0 Subject: Re: [PATCH] c++: Implement __is_{nothrow_,}convertible [PR106784] To: Marek Polacek , GCC Patches , libstdc++ , Jonathan Wakely References: <20220922133900.142238-1-polacek@redhat.com> From: Jason Merrill In-Reply-To: <20220922133900.142238-1-polacek@redhat.com> X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Language: en-US Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit X-Spam-Status: No, score=-13.9 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,GIT_PATCH_0,NICE_REPLY_A,RCVD_IN_DNSWL_LOW,SPF_HELO_NONE,SPF_NONE,TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: On 9/22/22 09:39, Marek Polacek wrote: > 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::value, ""); > static_assert(__is_assignable(int&, S), ""); > } > > This is not a problem if __is_assignable is not meant to be used by > the users. That's fine, it's not. > 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. Sigh, that's going to be a hassle when comparing compiler versions on preprocessed code. > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? > > 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. > --- > gcc/c-family/c-common.cc | 2 + > gcc/c-family/c-common.h | 1 + > gcc/cp/constraint.cc | 6 + > gcc/cp/cp-objcp-common.cc | 2 + > gcc/cp/cp-tree.h | 4 + > gcc/cp/cxx-pretty-print.cc | 6 + > gcc/cp/method.cc | 31 ++ > gcc/cp/parser.cc | 10 + > gcc/cp/semantics.cc | 8 + > gcc/testsuite/g++.dg/ext/has-builtin-1.C | 6 + > gcc/testsuite/g++.dg/ext/is_convertible1.C | 269 +++++++++++++++++ > gcc/testsuite/g++.dg/ext/is_convertible2.C | 46 +++ > .../g++.dg/ext/is_nothrow_convertible1.C | 270 ++++++++++++++++++ > .../g++.dg/ext/is_nothrow_convertible2.C | 19 ++ > libstdc++-v3/include/std/type_traits | 4 +- > .../is_nothrow_convertible/value_ext.cc | 4 +- > 16 files changed, 684 insertions(+), 4 deletions(-) > create mode 100644 gcc/testsuite/g++.dg/ext/is_convertible1.C > create mode 100644 gcc/testsuite/g++.dg/ext/is_convertible2.C > create mode 100644 gcc/testsuite/g++.dg/ext/is_nothrow_convertible1.C > create mode 100644 gcc/testsuite/g++.dg/ext/is_nothrow_convertible2.C > > diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc > index c0f15f4cab1..dce3045c9f2 100644 > --- a/gcc/c-family/c-common.cc > +++ b/gcc/c-family/c-common.cc > @@ -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, > diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h > index 2f592f5cd58..31397d80029 100644 > --- a/gcc/c-family/c-common.h > +++ b/gcc/c-family/c-common.h > @@ -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, > > diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc > index 568318f0ba1..5839bfb4b52 100644 > --- a/gcc/cp/constraint.cc > +++ b/gcc/cp/constraint.cc > @@ -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 % convertible from %qE", t2, t1); It's odd that the existing diagnostics quote "nothrow", which is not a keyword. I wonder why these library traits didn't use "noexcept"? > + 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); > diff --git a/gcc/cp/cp-objcp-common.cc b/gcc/cp/cp-objcp-common.cc > index 1ffac08c32f..64975699351 100644 > --- a/gcc/cp/cp-objcp-common.cc > +++ b/gcc/cp/cp-objcp-common.cc > @@ -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; > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h > index f19ecafc266..e4d89207e2a 100644 > --- a/gcc/cp/cp-tree.h > +++ b/gcc/cp/cp-tree.h > @@ -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); > diff --git a/gcc/cp/cxx-pretty-print.cc b/gcc/cp/cxx-pretty-print.cc > index 44590830a61..e18143e39a9 100644 > --- a/gcc/cp/cxx-pretty-print.cc > +++ b/gcc/cp/cxx-pretty-print.cc > @@ -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; > diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc > index 573ef016f82..c35a59fe56c 100644 > --- a/gcc/cp/method.cc > +++ b/gcc/cp/method.cc > @@ -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) You didn't want to add conversion to is*_xible? > +{ > + 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 > +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(obj.i)) {} > + operator float() const { > + return f; > + } > + operator I() const { > + return I(static_cast(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 > +struct X { > + template X(const X&); > +}; > + > +SA(__is_convertible(X, X)); > +SA(__is_convertible(X, X)); > + > +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 . > + > +#include > +#include > + > +#define SA(X) static_assert((X),#X) > + > +class E { public: template 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 = [](T x) { > + if constexpr (std::is_convertible_v or > + std::is_convertible_v) { > + 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 > +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(obj.i)) {} > + operator float() const noexcept { > + return f; > + } > + operator I() const noexcept { > + return I(static_cast(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 > +struct X { > + template X(const X&) noexcept; > +}; > + > +SA(__is_nothrow_convertible(X, X)); > +SA(__is_nothrow_convertible(X, X)); > + > +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 > - 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> > : __or_, > - __is_nothrow_convertible>::type > + __is_nothrow_convertible_lib>::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 > > -// Test the non-standard __is_nothrow_convertible trait > +// Test the non-standard __is_nothrow_convertible_lib trait > > template > - using is_nothrow_convertible = std::__is_nothrow_convertible; > + using is_nothrow_convertible = std::__is_nothrow_convertible_lib; > > #define IS_NT_CONVERTIBLE_DEFINED > #include "value.cc" > > base-commit: 32d8123cd6ce87acb557aec230e8359051316f9f