From patchwork Thu Sep 22 13:39:00 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marek Polacek X-Patchwork-Id: 1369 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a5d:5044:0:0:0:0:0 with SMTP id h4csp203379wrt; Thu, 22 Sep 2022 06:40:32 -0700 (PDT) X-Google-Smtp-Source: AMsMyM6j2HL4hosHF52xVDnXDndJT3eCk5YXdIizMtKgMkCoSwPfkxEviypg9eq6SHbJi+RAowJl X-Received: by 2002:a05:6402:748:b0:44e:b48f:f5ec with SMTP id p8-20020a056402074800b0044eb48ff5ecmr3536816edy.146.1663854032060; Thu, 22 Sep 2022 06:40:32 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1663854032; cv=none; d=google.com; s=arc-20160816; b=bEMrjZZN8lLDrYUdGmnaCJ1eA2mf/dH5fDN53kbePalFuk3T9jbhGftsm6mVP8jTxZ 1hLWJRg4dTijIRtfxHFnNZ9Wo+0k3RmseNcok7pUtIJ800JvDpNj23dDdg5oeDUNsAug ltV1+BTOB6sg23DrIoJD8gl806PKQSnnNPgOjbvyOlkv/mkctLLrJ4SRMEyry7zYpUoZ r8s4ATiy5CWWIdVi6/jlc3bXBkP5/igbU+/S5ENSQc2+tjpsHYhJ4lXCjrMFR4CNCBc6 CPGli4h471tJOhg10UxTiqcLqolMKV0dKWOP3Qi9uTvDUtudw026+3P3yYi+wmZGDeJB 0y2g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:reply-to:from:list-subscribe:list-help:list-post :list-archive:list-unsubscribe:list-id:precedence :content-transfer-encoding:mime-version:message-id:date:subject:to :dmarc-filter:delivered-to:dkim-signature:dkim-filter; bh=NR9J+ipw/7Uycx9LyEQ4Bid7W5Sg0AbDT4BKkwF2CkQ=; b=dEdQLZwF2fuXeYsww3oCVu5EQHcht7Kx2qAj4GgyC5ds4H+X+5PqA7r/xwGRbiHY0Z 4i87JyUQahgpXS4yNhf8bMyKYPA8saW0BlJXWnLwF6l8SWTChSbZ6WfUMmY13CrnblyG Gx2vd4gfQx26wq3XRbL2PPqFZaxc0j9UeoQ6ObAnN5NthLhQxwmBrakWPEof84CrmL6n WVJ05aYqBELx2TzgzriLF2QoBzziQ/W9x3YwPDpwxE2Dn49Y3COzmXR6Su43GiRwauV+ 4kmUTUAOxjj37IxLLxo4mIJud8NndpX4EW4lnEADKjBO3gaFwFZEzIg8+Qn64V/ByPYz Z2qg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gcc.gnu.org header.s=default header.b="oeZ/1TaN"; spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=gnu.org Received: from sourceware.org (server2.sourceware.org. [2620:52:3:1:0:246e:9693:128c]) by mx.google.com with ESMTPS id ht8-20020a170907608800b0077eb65c41e5si4737019ejc.22.2022.09.22.06.40.31 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 22 Sep 2022 06:40:32 -0700 (PDT) Received-SPF: pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) client-ip=2620:52:3:1:0:246e:9693:128c; Authentication-Results: mx.google.com; dkim=pass header.i=@gcc.gnu.org header.s=default header.b="oeZ/1TaN"; spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=gnu.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 25D103857BAC for ; Thu, 22 Sep 2022 13:40:08 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 25D103857BAC DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1663854008; bh=NR9J+ipw/7Uycx9LyEQ4Bid7W5Sg0AbDT4BKkwF2CkQ=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:From; b=oeZ/1TaNMe7oWzf7aDsEgkGanqhKo8rHFCDz1B8wuGXpCL9EhQg3JIGtOPYZSzWER 5milCswRDgCDnd7d0XNRSnxHUFcbGdgfJ+mCGhevxIROeBPkyEKxPuQL4eTsXGQYex AcUdnaPy7Dy49/N9Do2/Ty3FPVQ/5qljJedVZgEQ= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id F37223858422 for ; Thu, 22 Sep 2022 13:39:15 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org F37223858422 Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-591-62sKhlfjPP2vg7bkFAOQIg-1; Thu, 22 Sep 2022 09:39:12 -0400 X-MC-Unique: 62sKhlfjPP2vg7bkFAOQIg-1 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.rdu2.redhat.com [10.11.54.3]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 0A93E101A528; Thu, 22 Sep 2022 13:39:12 +0000 (UTC) Received: from pdp-11.lan (unknown [10.22.33.58]) by smtp.corp.redhat.com (Postfix) with ESMTP id C7B4D1121314; Thu, 22 Sep 2022 13:39:11 +0000 (UTC) To: GCC Patches , Jason Merrill , libstdc++ , Jonathan Wakely Subject: [PATCH] c++: Implement __is_{nothrow_,}convertible [PR106784] Date: Thu, 22 Sep 2022 09:39:00 -0400 Message-Id: <20220922133900.142238-1-polacek@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.1 on 10.11.54.3 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-12.4 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_LOW, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Marek Polacek via Gcc-patches From: Marek Polacek Reply-To: Marek Polacek Errors-To: gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org Sender: "Gcc-patches" X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1744677405455051198?= X-GMAIL-MSGID: =?utf-8?q?1744677405455051198?= 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. 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. 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 base-commit: 32d8123cd6ce87acb557aec230e8359051316f9f 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); + 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) +{ + 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"