diff options
author | Marek Polacek <polacek@redhat.com> | 2022-09-20 15:48:52 -0400 |
---|---|---|
committer | Marek Polacek <polacek@redhat.com> | 2022-09-23 12:12:41 -0400 |
commit | 8a7bcf95a82c3dd68bd4bcfbd8432eb970575bc2 (patch) | |
tree | 391e7f2aef62a839154be40fd95c7a3ccf3f3c10 /gcc/cp | |
parent | 7d4df630c6cfb1909288a2796ec2f8b9ec4e8486 (diff) | |
download | gcc-8a7bcf95a82c3dd68bd4bcfbd8432eb970575bc2.zip gcc-8a7bcf95a82c3dd68bd4bcfbd8432eb970575bc2.tar.gz gcc-8a7bcf95a82c3dd68bd4bcfbd8432eb970575bc2.tar.bz2 |
c++: Implement __is_{nothrow_,}convertible [PR106784]
To improve compile times, the C++ library could use compiler built-ins
rather than implementing std::is_convertible (and _nothrow) as class
templates. This patch adds the built-ins. We already have
__is_constructible and __is_assignable, and the nothrow forms of those.
Microsoft (and clang, for compatibility) also provide an alias called
__is_convertible_to. I did not add it, but it would be trivial to do
so.
I noticed that our __is_assignable doesn't implement the "Access checks
are performed as if from a context unrelated to either type" requirement,
therefore std::is_assignable / __is_assignable give two different results
here:
class S {
operator int();
friend void g(); // #1
};
void
g ()
{
// #1 doesn't matter
static_assert(std::is_assignable<int&, S>::value, "");
static_assert(__is_assignable(int&, S), "");
}
This is not a problem if __is_assignable is not meant to be used by
the users.
This patch doesn't make libstdc++ use the new built-ins, but I had to
rename a class otherwise its name would clash with the new built-in.
PR c++/106784
gcc/c-family/ChangeLog:
* c-common.cc (c_common_reswords): Add __is_convertible and
__is_nothrow_convertible.
* c-common.h (enum rid): Add RID_IS_CONVERTIBLE and
RID_IS_NOTHROW_CONVERTIBLE.
gcc/cp/ChangeLog:
* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_CONVERTIBLE
and CPTK_IS_NOTHROW_CONVERTIBLE.
* cp-objcp-common.cc (names_builtin_p): Handle RID_IS_CONVERTIBLE
RID_IS_NOTHROW_CONVERTIBLE.
* cp-tree.h (enum cp_trait_kind): Add CPTK_IS_CONVERTIBLE and
CPTK_IS_NOTHROW_CONVERTIBLE.
(is_convertible): Declare.
(is_nothrow_convertible): Likewise.
* cxx-pretty-print.cc (pp_cxx_trait_expression): Handle
CPTK_IS_CONVERTIBLE and CPTK_IS_NOTHROW_CONVERTIBLE.
* method.cc (is_convertible): New.
(is_nothrow_convertible): Likewise.
* parser.cc (cp_parser_primary_expression): Handle RID_IS_CONVERTIBLE
and RID_IS_NOTHROW_CONVERTIBLE.
(cp_parser_trait_expr): Likewise.
* semantics.cc (trait_expr_value): Handle CPTK_IS_CONVERTIBLE and
CPTK_IS_NOTHROW_CONVERTIBLE.
(finish_trait_expr): Likewise.
libstdc++-v3/ChangeLog:
* include/std/type_traits: Rename __is_nothrow_convertible to
__is_nothrow_convertible_lib.
* testsuite/20_util/is_nothrow_convertible/value_ext.cc: Likewise.
gcc/testsuite/ChangeLog:
* g++.dg/ext/has-builtin-1.C: Enhance to test __is_convertible and
__is_nothrow_convertible.
* g++.dg/ext/is_convertible1.C: New test.
* g++.dg/ext/is_convertible2.C: New test.
* g++.dg/ext/is_nothrow_convertible1.C: New test.
* g++.dg/ext/is_nothrow_convertible2.C: New test.
Diffstat (limited to 'gcc/cp')
-rw-r--r-- | gcc/cp/constraint.cc | 6 | ||||
-rw-r--r-- | gcc/cp/cp-objcp-common.cc | 2 | ||||
-rw-r--r-- | gcc/cp/cp-tree.h | 4 | ||||
-rw-r--r-- | gcc/cp/cxx-pretty-print.cc | 6 | ||||
-rw-r--r-- | gcc/cp/method.cc | 31 | ||||
-rw-r--r-- | gcc/cp/parser.cc | 10 | ||||
-rw-r--r-- | gcc/cp/semantics.cc | 8 |
7 files changed, 67 insertions, 0 deletions
diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc index 568318f..5839bfb 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 %<nothrow%> convertible from %qE", t2, t1); + break; case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY: inform (loc, " %qT is not a reference that binds to a temporary " "object of type %qT (direct-initialization)", t1, t2); diff --git a/gcc/cp/cp-objcp-common.cc b/gcc/cp/cp-objcp-common.cc index 1ffac08..6497569 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 f19ecaf..e4d8920 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 4459083..e18143e3 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 573ef01..c35a59f 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 3cbe0d6..bb83d1c 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 8656207..92fc795 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) |