Skip to content

Commit

Permalink
c++: Implement __is_{nothrow_,}convertible [PR106784]
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
mpolacek committed Sep 23, 2022
1 parent 7d4df63 commit 8a7bcf9
Show file tree
Hide file tree
Showing 16 changed files with 684 additions and 4 deletions.
2 changes: 2 additions & 0 deletions gcc/c-family/c-common.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions gcc/c-family/c-common.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,

Expand Down
6 changes: 6 additions & 0 deletions gcc/cp/constraint.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions gcc/cp/cp-objcp-common.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions gcc/cp/cp-tree.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
};
Expand Down Expand Up @@ -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);
Expand Down
6 changes: 6 additions & 0 deletions gcc/cp/cxx-pretty-print.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
31 changes: 31 additions & 0 deletions gcc/cp/method.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
10 changes: 10 additions & 0 deletions gcc/cp/parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down
8 changes: 8 additions & 0 deletions gcc/cp/semantics.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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)
Expand Down
6 changes: 6 additions & 0 deletions gcc/testsuite/g++.dg/ext/has-builtin-1.C
Original file line number Diff line number Diff line change
Expand Up @@ -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
Loading

0 comments on commit 8a7bcf9

Please sign in to comment.