diff mbox

C++ PATCHes to add __is_trivially_*

Message ID 542AE4B1.3080300@redhat.com
State New
Headers show

Commit Message

Jason Merrill Sept. 30, 2014, 5:13 p.m. UTC
Ville asked for help with the necessary compiler intrinsics for the 
is_trivially_* C++11 library traits.

The first patch cleans up a few oddities I noticed with the existing 
intrinsics.  __is_convertible_to was never implemented and isn't needed. 
  There's no need for a second grokdeclarator in trait parsing since 
cp_parser_type_id already does a grokdeclarator.  And the assert at the 
top of finish_trait_expr is redundant with the gcc_unreachable in the 
switch.

The second patch adds __is_trivially_copyable, which just uses the 
existing trivially_copyable_p predicate in the compiler.

The third patch adds __is_trivially_assignable and 
__is_trivially_constructible, which work by building up an expression 
representing assignment or object declaration and then scanning it for 
calls to functions other than trivial special member functions.  Note 
that there are still bugs in trivial_fn_p that are exposed by this 
intrinsic.

Tested x86_64-pc-linux-gnu, applying to trunk.

Comments

Paolo Carlini Sept. 30, 2014, 5:33 p.m. UTC | #1
Hi,

On 09/30/2014 07:13 PM, Jason Merrill wrote:
> Ville asked for help with the necessary compiler intrinsics for the 
> is_trivially_* C++11 library traits.
>
> The first patch cleans up a few oddities I noticed with the existing 
> intrinsics.  __is_convertible_to was never implemented and isn't 
> needed.  There's no need for a second grokdeclarator in trait parsing 
> since cp_parser_type_id already does a grokdeclarator.  And the assert 
> at the top of finish_trait_expr is redundant with the gcc_unreachable 
> in the switch.
>
> The second patch adds __is_trivially_copyable, which just uses the 
> existing trivially_copyable_p predicate in the compiler.
>
> The third patch adds __is_trivially_assignable and 
> __is_trivially_constructible, which work by building up an expression 
> representing assignment or object declaration and then scanning it for 
> calls to functions other than trivial special member functions.  Note 
> that there are still bugs in trivial_fn_p that are exposed by this 
> intrinsic.
Great. I think this can be as well marked as PR c++/26099.

By the way, if I remember correctly, the idea of having 
__is_convertible_to leading to unimplemented instead of simply being not 
recognized, goes back to this kind of idea:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2518.html

and Intel too was in favor of somewhat standardizing those intrinsics. 
In fact, both current icc and clang++ accept and implement 
__is_convertible_to.

Paolo.
diff mbox

Patch

commit 6d2c4c2a1d74c455d2a0fc381965a859088de606
Author: Jason Merrill <jason@redhat.com>
Date:   Mon Sep 29 16:39:55 2014 -0400

    c-family/
    	* c-common.h (enum rid): Add RID_IS_TRIVIALLY_ASSIGNABLE and
    	RID_IS_TRIVIALLY_CONSTRUCTIBLE.
    	* c-common.c (c_common_reswords): Add __is_trivially_copyable.
    cp/
    	* cp-tree.h (cp_trait_kind): Add CPTK_IS_TRIVIALLY_ASSIGNABLE and
    	CPTK_IS_TRIVIALLY_CONSTRUCTIBLE.
    	* cxx-pretty-print.c (pp_cxx_trait_expression): Likewise.
    	* parser.c (cp_parser_primary_expression): Likewise.
    	(cp_parser_trait_expr): Likewise.  Handle variadic trait.
    	* semantics.c (trait_expr_value): Likewise.
    	(finish_trait_expr): Likewise.
    	(check_trait_type): Handle variadic trait.  Return bool.
    	* method.c (build_stub_object): Add rvalue reference here.
    	(locate_fn_flags): Not here.
    	(check_nontriv, assignable_expr, constructible_expr): New.
    	(is_trivially_xible): New.

diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 482dd44..b16d030 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -480,6 +480,8 @@  const struct c_common_resword c_common_reswords[] =
   { "__is_polymorphic",	RID_IS_POLYMORPHIC, D_CXXONLY },
   { "__is_standard_layout", RID_IS_STD_LAYOUT, D_CXXONLY },
   { "__is_trivial",     RID_IS_TRIVIAL, D_CXXONLY },
+  { "__is_trivially_assignable", RID_IS_TRIVIALLY_ASSIGNABLE, D_CXXONLY },
+  { "__is_trivially_constructible", RID_IS_TRIVIALLY_CONSTRUCTIBLE, D_CXXONLY },
   { "__is_trivially_copyable", RID_IS_TRIVIALLY_COPYABLE, D_CXXONLY },
   { "__is_union",	RID_IS_UNION,	D_CXXONLY },
   { "__label__",	RID_LABEL,	0 },
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index b7e3385..1e3477f 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -143,6 +143,7 @@  enum rid
   RID_IS_FINAL,                RID_IS_LITERAL_TYPE,
   RID_IS_POD,                  RID_IS_POLYMORPHIC,
   RID_IS_STD_LAYOUT,           RID_IS_TRIVIAL,
+  RID_IS_TRIVIALLY_ASSIGNABLE, RID_IS_TRIVIALLY_CONSTRUCTIBLE,
   RID_IS_TRIVIALLY_COPYABLE,
   RID_IS_UNION,                RID_UNDERLYING_TYPE,
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index c22fbfa..cc11bba 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -653,6 +653,8 @@  typedef enum cp_trait_kind
   CPTK_IS_POLYMORPHIC,
   CPTK_IS_STD_LAYOUT,
   CPTK_IS_TRIVIAL,
+  CPTK_IS_TRIVIALLY_ASSIGNABLE,
+  CPTK_IS_TRIVIALLY_CONSTRUCTIBLE,
   CPTK_IS_TRIVIALLY_COPYABLE,
   CPTK_IS_UNION,
   CPTK_UNDERLYING_TYPE
@@ -5522,6 +5524,7 @@  extern tree make_thunk				(tree, bool, tree, tree);
 extern void finish_thunk			(tree);
 extern void use_thunk				(tree, bool);
 extern bool trivial_fn_p			(tree);
+extern bool is_trivially_xible			(enum tree_code, tree, tree);
 extern tree get_defaulted_eh_spec		(tree);
 extern tree unevaluated_noexcept_spec		(void);
 extern void after_nsdmi_defaulted_late_checks   (tree);
diff --git a/gcc/cp/cxx-pretty-print.c b/gcc/cp/cxx-pretty-print.c
index 7b2d7fd..67e84c0 100644
--- a/gcc/cp/cxx-pretty-print.c
+++ b/gcc/cp/cxx-pretty-print.c
@@ -2393,6 +2393,12 @@  pp_cxx_trait_expression (cxx_pretty_printer *pp, tree t)
     case CPTK_IS_TRIVIAL:
       pp_cxx_ws_string (pp, "__is_trivial");
       break;
+    case CPTK_IS_TRIVIALLY_ASSIGNABLE:
+      pp_cxx_ws_string (pp, "__is_trivially_assignable");
+      break;
+    case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE:
+      pp_cxx_ws_string (pp, "__is_trivially_constructible");
+      break;
     case CPTK_IS_TRIVIALLY_COPYABLE:
       pp_cxx_ws_string (pp, "__is_trivially_copyable");
       break;
diff --git a/gcc/cp/method.c b/gcc/cp/method.c
index b427d65..9a2bd0f 100644
--- a/gcc/cp/method.c
+++ b/gcc/cp/method.c
@@ -852,6 +852,8 @@  build_stub_type (tree type, int quals, bool rvalue)
 static tree
 build_stub_object (tree reftype)
 {
+  if (TREE_CODE (reftype) != REFERENCE_TYPE)
+    reftype = cp_build_reference_type (reftype, /*rval*/true);
   tree stub = build1 (CONVERT_EXPR, reftype, integer_one_node);
   return convert_from_reference (stub);
 }
@@ -889,8 +891,6 @@  locate_fn_flags (tree type, tree name, tree argtype, int flags,
 	       elt = TREE_CHAIN (elt))
 	    {
 	      tree type = TREE_VALUE (elt);
-	      if (TREE_CODE (type) != REFERENCE_TYPE)
-		type = cp_build_reference_type (type, /*rval*/true);
 	      tree arg = build_stub_object (type);
 	      vec_safe_push (args, arg);
 	    }
@@ -1001,6 +1001,113 @@  get_inherited_ctor (tree ctor)
   return fn;
 }
 
+/* walk_tree helper function for is_trivially_xible.  If *TP is a call,
+   return it if it calls something other than a trivial special member
+   function.  */
+
+static tree
+check_nontriv (tree *tp, int *, void *)
+{
+  tree fn;
+  if (TREE_CODE (*tp) == CALL_EXPR)
+    fn = CALL_EXPR_FN (*tp);
+  else if (TREE_CODE (*tp) == AGGR_INIT_EXPR)
+    fn = AGGR_INIT_EXPR_FN (*tp);
+  else
+    return NULL_TREE;
+
+  if (TREE_CODE (fn) == ADDR_EXPR)
+    fn = TREE_OPERAND (fn, 0);
+
+  if (TREE_CODE (fn) != FUNCTION_DECL
+      || !trivial_fn_p (fn))
+    return fn;
+  return NULL_TREE;
+}
+
+/* Return declval<T>() = declval<U>() treated as an unevaluated operand.  */
+
+static tree
+assignable_expr (tree to, tree from)
+{
+  ++cp_unevaluated_operand;
+  to = build_stub_object (to);
+  from = build_stub_object (from);
+  tree r = cp_build_modify_expr (to, NOP_EXPR, from, tf_none);
+  --cp_unevaluated_operand;
+  return r;
+}
+
+/* The predicate condition for a template specialization
+   is_constructible<T, Args...> shall be satisfied if and only if the
+   following variable definition would be well-formed for some invented
+   variable t: T t(create<Args>()...);
+
+   Return something equivalent in well-formedness and triviality.  */
+
+static tree
+constructible_expr (tree to, tree from)
+{
+  tree expr;
+  if (CLASS_TYPE_P (to))
+    {
+      tree ctype = to;
+      vec<tree, va_gc> *args = NULL;
+      if (TREE_CODE (to) != REFERENCE_TYPE)
+	to = cp_build_reference_type (to, /*rval*/false);
+      tree ob = build_stub_object (to);
+      for (; from; from = TREE_CHAIN (from))
+	vec_safe_push (args, build_stub_object (TREE_VALUE (from)));
+      expr = build_special_member_call (ob, complete_ctor_identifier, &args,
+					ctype, LOOKUP_NORMAL, tf_none);
+      if (expr == error_mark_node)
+	return error_mark_node;
+      /* The current state of the standard vis-a-vis LWG 2116 is that
+	 is_*constructible involves destruction as well.  */
+      if (type_build_dtor_call (ctype))
+	{
+	  tree dtor = build_special_member_call (ob, complete_dtor_identifier,
+						 NULL, ctype, LOOKUP_NORMAL,
+						 tf_none);
+	  if (dtor == error_mark_node)
+	    return error_mark_node;
+	  if (!TYPE_HAS_TRIVIAL_DESTRUCTOR (ctype))
+	    expr = build2 (COMPOUND_EXPR, void_type_node, expr, dtor);
+	}
+    }
+  else
+    {
+      if (TREE_CHAIN (from))
+	return error_mark_node; // too many initializers
+      from = build_stub_object (TREE_VALUE (from));
+      expr = perform_direct_initialization_if_possible (to, from,
+							/*cast*/false,
+							tf_none);
+    }
+  return expr;
+}
+
+/* Returns true iff TO is trivially assignable (if CODE is MODIFY_EXPR) or
+   constructible (otherwise) from FROM, which is a single type for
+   assignment or a list of types for construction.  */
+
+bool
+is_trivially_xible (enum tree_code code, tree to, tree from)
+{
+  tree expr;
+  if (code == MODIFY_EXPR)
+    expr = assignable_expr (to, from);
+  else if (from && TREE_CHAIN (from))
+    return false; // only 0- and 1-argument ctors can be trivial
+  else
+    expr = constructible_expr (to, from);
+
+  if (expr == error_mark_node)
+    return false;
+  tree nt = cp_walk_tree_without_duplicates (&expr, check_nontriv, NULL);
+  return !nt;
+}
+
 /* Subroutine of synthesized_method_walk.  Update SPEC_P, TRIVIAL_P and
    DELETED_P or give an error message MSG with argument ARG.  */
 
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index b1feef5..e4aaf53 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -4490,6 +4490,8 @@  cp_parser_primary_expression (cp_parser *parser,
 	case RID_IS_POLYMORPHIC:
 	case RID_IS_STD_LAYOUT:
 	case RID_IS_TRIVIAL:
+	case RID_IS_TRIVIALLY_ASSIGNABLE:
+	case RID_IS_TRIVIALLY_CONSTRUCTIBLE:
 	case RID_IS_TRIVIALLY_COPYABLE:
 	case RID_IS_UNION:
 	  return cp_parser_trait_expr (parser, token->keyword);
@@ -8664,6 +8666,7 @@  cp_parser_trait_expr (cp_parser* parser, enum rid keyword)
   cp_trait_kind kind;
   tree type1, type2 = NULL_TREE;
   bool binary = false;
+  bool variadic = false;
 
   switch (keyword)
     {
@@ -8725,6 +8728,14 @@  cp_parser_trait_expr (cp_parser* parser, enum rid keyword)
     case RID_IS_TRIVIAL:
       kind = CPTK_IS_TRIVIAL;
       break;
+    case RID_IS_TRIVIALLY_ASSIGNABLE:
+      kind = CPTK_IS_TRIVIALLY_ASSIGNABLE;
+      binary = true;
+      break;
+    case RID_IS_TRIVIALLY_CONSTRUCTIBLE:
+      kind = CPTK_IS_TRIVIALLY_CONSTRUCTIBLE;
+      variadic = true;
+      break;
     case RID_IS_TRIVIALLY_COPYABLE:
       kind = CPTK_IS_TRIVIALLY_COPYABLE;
       break;
@@ -8763,6 +8774,17 @@  cp_parser_trait_expr (cp_parser* parser, enum rid keyword)
       if (type2 == error_mark_node)
 	return error_mark_node;
     }
+  else if (variadic)
+    {
+      while (cp_lexer_next_token_is (parser->lexer, CPP_COMMA))
+	{
+	  cp_lexer_consume_token (parser->lexer);
+	  tree elt = cp_parser_type_id (parser);
+	  if (elt == error_mark_node)
+	    return error_mark_node;
+	  type2 = tree_cons (NULL_TREE, elt, type2);
+	}
+    }
 
   cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
 
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 9bcc6d7..7569826 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -7379,6 +7379,12 @@  trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_TRIVIAL:
       return (trivial_type_p (type1));
 
+    case CPTK_IS_TRIVIALLY_ASSIGNABLE:
+      return is_trivially_xible (MODIFY_EXPR, type1, type2);
+
+    case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE:
+      return is_trivially_xible (INIT_EXPR, type1, type2);
+
     case CPTK_IS_TRIVIALLY_COPYABLE:
       return (trivially_copyable_p (type1));
 
@@ -7392,19 +7398,26 @@  trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
 }
 
 /* If TYPE is an array of unknown bound, or (possibly cv-qualified)
-   void, or a complete type, returns it, otherwise NULL_TREE.  */
+   void, or a complete type, returns true, otherwise false.  */
 
-static tree
+static bool
 check_trait_type (tree type)
 {
+  if (type == NULL_TREE)
+    return true;
+
+  if (TREE_CODE (type) == TREE_LIST)
+    return (check_trait_type (TREE_VALUE (type))
+	    && check_trait_type (TREE_CHAIN (type)));
+
   if (TREE_CODE (type) == ARRAY_TYPE && !TYPE_DOMAIN (type)
       && COMPLETE_TYPE_P (TREE_TYPE (type)))
-    return type;
+    return true;
 
   if (VOID_TYPE_P (type))
-    return type;
+    return true;
 
-  return complete_type_or_else (strip_array_types (type), NULL_TREE);
+  return !!complete_type_or_else (strip_array_types (type), NULL_TREE);
 }
 
 /* Process a trait expression.  */
@@ -7413,8 +7426,7 @@  tree
 finish_trait_expr (cp_trait_kind kind, tree type1, tree type2)
 {
   if (type1 == error_mark_node
-      || ((kind == CPTK_IS_BASE_OF)
-	  && type2 == error_mark_node))
+      || type2 == error_mark_node)
     return error_mark_node;
 
   if (processing_template_decl)
@@ -7450,6 +7462,13 @@  finish_trait_expr (cp_trait_kind kind, tree type1, tree type2)
 	return error_mark_node;
       break;
 
+    case CPTK_IS_TRIVIALLY_ASSIGNABLE:
+    case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE:
+      if (!check_trait_type (type1)
+	  || !check_trait_type (type2))
+	return error_mark_node;
+      break;
+
     case CPTK_IS_BASE_OF:
       if (NON_UNION_CLASS_TYPE_P (type1) && NON_UNION_CLASS_TYPE_P (type2)
 	  && !same_type_ignoring_top_level_qualifiers_p (type1, type2)
diff --git a/gcc/testsuite/g++.dg/ext/is_trivially_constructible1.C b/gcc/testsuite/g++.dg/ext/is_trivially_constructible1.C
new file mode 100644
index 0000000..f558538
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_trivially_constructible1.C
@@ -0,0 +1,35 @@ 
+// { dg-do compile { target c++11 } }
+
+struct A { };
+struct B { B(); operator int(); };
+struct C {
+  C() = default;
+  C(const C&);
+  C(C&&) = default;
+  C& operator=(C&&);
+  C& operator= (const C&) = default;
+};
+struct D { ~D() {} };
+
+#define SA(X) static_assert((X),#X)
+
+SA(__is_trivially_constructible(A));
+SA(__is_trivially_constructible(A,A));
+SA(!__is_trivially_constructible(B));
+SA(__is_trivially_constructible(B,B));
+
+SA(!__is_trivially_constructible(A,B));
+SA(!__is_trivially_constructible(B,A));
+
+SA(__is_trivially_constructible(C));
+SA(__is_trivially_constructible(C,C));
+SA(!__is_trivially_constructible(C,C&));
+SA(__is_trivially_assignable(C,C&));
+SA(!__is_trivially_assignable(C,C));
+SA(!__is_trivially_assignable(C,C&&));
+
+SA(__is_trivially_constructible(int,int));
+SA(__is_trivially_constructible(int,double));
+SA(!__is_trivially_constructible(int,B));
+
+SA(!__is_trivially_constructible(D));