Patchwork C++0x constexpr patch #3: implicit constexpr

login
register
mail settings
Submitter Jason Merrill
Date Oct. 27, 2010, 8:10 p.m.
Message ID <4CC88731.5090301@redhat.com>
Download mbox | patch
Permalink /patch/69400/
State New
Headers show

Comments

Jason Merrill - Oct. 27, 2010, 8:10 p.m.
This patch adds support for constexpr on implicitly-declared 
constructors; a trivial copy constructor is constexpr, as is a 
synthesized default constructor for a class where all the members and 
bases have constexpr default constructors.

Tested x86_64-pc-linux-gnu, applied to trunk.
Gabriel Dos Reis - Oct. 27, 2010, 8:33 p.m.
On Wed, Oct 27, 2010 at 3:10 PM, Jason Merrill <jason@redhat.com> wrote:
> This patch adds support for constexpr on implicitly-declared constructors; a
> trivial copy constructor is constexpr, as is a synthesized default
> constructor for a class where all the members and bases have constexpr
> default constructors.
>
> Tested x86_64-pc-linux-gnu, applied to trunk.
>

Kudos to Jason and Benjamin for pushing the constexpr implementation through.

-- Gaby

Patch

commit 0b1009b4f069adc502a70aa1e0e9f985007b08e9
Author: Jason Merrill <jason@redhat.com>
Date:   Wed Oct 27 14:25:09 2010 -0400

    	* method.c (synthesized_method_walk): Track constexprness too.
    	(process_subob_fn, walk_field_subobs): Likewise.
    	(implicitly_declare_fn): Set DECL_DECLARED_CONSTEXPR_P.
    	(defaulted_late_check): Handle DECL_DECLARED_CONSTEXPR_P.
    	* class.c (add_implicitly_declared_members): Handle
    	constexpr default ctor.

diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index c3e3c53..a31aad3 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -2672,7 +2672,20 @@  add_implicitly_declared_members (tree t,
   if (! TYPE_HAS_USER_CONSTRUCTOR (t))
     {
       TYPE_HAS_DEFAULT_CONSTRUCTOR (t) = 1;
-      CLASSTYPE_LAZY_DEFAULT_CTOR (t) = 1;
+      if (TYPE_HAS_TRIVIAL_DFLT (t))
+	{
+	  /* A trivial default constructor is constexpr
+	     if there is nothing to initialize.  */
+	  if (cxx_dialect >= cxx0x && is_really_empty_class (t))
+	    TYPE_HAS_CONSTEXPR_CTOR (t) = 1;
+	  CLASSTYPE_LAZY_DEFAULT_CTOR (t) = 1;
+	}
+      else if (cxx_dialect >= cxx0x)
+	/* We need to go ahead and declare this to set
+	   TYPE_HAS_CONSTEXPR_CTOR.  */
+	lazily_declare_fn (sfk_constructor, t);
+      else
+	CLASSTYPE_LAZY_DEFAULT_CTOR (t) = 1;
     }
 
   /* [class.ctor]
diff --git a/gcc/cp/method.c b/gcc/cp/method.c
index 1083e16..6687c75 100644
--- a/gcc/cp/method.c
+++ b/gcc/cp/method.c
@@ -903,7 +903,8 @@  get_copy_assign (tree type)
 
 static void
 process_subob_fn (tree fn, bool move_p, tree *spec_p, bool *trivial_p,
-		  bool *deleted_p, const char *msg, tree arg)
+		  bool *deleted_p, bool *constexpr_p,
+		  const char *msg, tree arg)
 {
   if (!fn || fn == error_mark_node)
     goto bad;
@@ -935,6 +936,9 @@  process_subob_fn (tree fn, bool move_p, tree *spec_p, bool *trivial_p,
       goto bad;
     }
 
+  if (constexpr_p && !DECL_DECLARED_CONSTEXPR_P (fn))
+    *constexpr_p = false;
+
   return;
 
  bad:
@@ -949,7 +953,7 @@  static void
 walk_field_subobs (tree fields, tree fnname, special_function_kind sfk,
 		   int quals, bool copy_arg_p, bool move_p,
 		   bool assign_p, tree *spec_p, bool *trivial_p,
-		   bool *deleted_p, const char *msg,
+		   bool *deleted_p, bool *constexpr_p, const char *msg,
 		   int flags, tsubst_flags_t complain)
 {
   tree field;
@@ -1005,6 +1009,13 @@  walk_field_subobs (tree fields, tree fnname, special_function_kind sfk,
 
 	  if (bad && deleted_p)
 	    *deleted_p = true;
+
+	  /* For an implicitly-defined default constructor to be constexpr,
+	     every member must have a user-provided default constructor.  */
+	  /* FIXME will need adjustment for non-static data member
+	     initializers.  */
+	  if (constexpr_p && !CLASS_TYPE_P (mem_type))
+	    *constexpr_p = false;
 	}
 
       if (!CLASS_TYPE_P (mem_type))
@@ -1014,7 +1025,7 @@  walk_field_subobs (tree fields, tree fnname, special_function_kind sfk,
 	{
 	  walk_field_subobs (TYPE_FIELDS (mem_type), fnname, sfk, quals,
 			     copy_arg_p, move_p, assign_p, spec_p, trivial_p,
-			     deleted_p, msg, flags, complain);
+			     deleted_p, constexpr_p, msg, flags, complain);
 	  continue;
 	}
 
@@ -1031,7 +1042,7 @@  walk_field_subobs (tree fields, tree fnname, special_function_kind sfk,
       rval = locate_fn_flags (mem_type, fnname, argtype, flags, complain);
 
       process_subob_fn (rval, move_p, spec_p, trivial_p, deleted_p,
-			msg, field);
+			constexpr_p, msg, field);
     }
 }
 
@@ -1044,7 +1055,7 @@  walk_field_subobs (tree fields, tree fnname, special_function_kind sfk,
 static void
 synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p,
 			 tree *spec_p, bool *trivial_p, bool *deleted_p,
-			 bool diag)
+			 bool *constexpr_p, bool diag)
 {
   tree binfo, base_binfo, scope, fnname, rval, argtype;
   bool move_p, copy_arg_p, assign_p, expected_trivial, check_vdtor;
@@ -1078,6 +1089,41 @@  synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p,
       *deleted_p = false;
     }
 
+  ctor_p = false;
+  assign_p = false;
+  check_vdtor = false;
+  switch (sfk)
+    {
+    case sfk_move_assignment:
+    case sfk_copy_assignment:
+      assign_p = true;
+      fnname = ansi_assopname (NOP_EXPR);
+      break;
+
+    case sfk_destructor:
+      check_vdtor = true;
+      /* The synthesized method will call base dtors, but check complete
+	 here to avoid having to deal with VTT.  */
+      fnname = complete_dtor_identifier;
+      break;
+
+    case sfk_constructor:
+    case sfk_move_constructor:
+    case sfk_copy_constructor:
+      ctor_p = true;
+      fnname = complete_ctor_identifier;
+      break;
+
+    default:
+      gcc_unreachable ();
+    }
+
+  /* If that user-written default constructor would satisfy the
+     requirements of a constexpr constructor (7.1.5), the
+     implicitly-defined default constructor is constexpr.  */
+  if (constexpr_p)
+    *constexpr_p = ctor_p;
+
   move_p = false;
   switch (sfk)
     {
@@ -1114,35 +1160,6 @@  synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p,
     return;
 #endif
 
-  ctor_p = false;
-  assign_p = false;
-  check_vdtor = false;
-  switch (sfk)
-    {
-    case sfk_move_assignment:
-    case sfk_copy_assignment:
-      assign_p = true;
-      fnname = ansi_assopname (NOP_EXPR);
-      break;
-
-    case sfk_destructor:
-      check_vdtor = true;
-      /* The synthesized method will call base dtors, but check complete
-	 here to avoid having to deal with VTT.  */
-      fnname = complete_dtor_identifier;
-      break;
-
-    case sfk_constructor:
-    case sfk_move_constructor:
-    case sfk_copy_constructor:
-      ctor_p = true;
-      fnname = complete_ctor_identifier;
-      break;
-
-    default:
-      gcc_unreachable ();
-    }
-
   ++cp_unevaluated_operand;
   ++c_inhibit_evaluation_warnings;
 
@@ -1183,7 +1200,7 @@  synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p,
       rval = locate_fn_flags (base_binfo, fnname, argtype, flags, complain);
 
       process_subob_fn (rval, move_p, spec_p, trivial_p, deleted_p,
-			msg, basetype);
+			constexpr_p, msg, basetype);
       if (ctor_p && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (basetype))
 	{
 	  /* In a constructor we also need to check the subobject
@@ -1191,7 +1208,8 @@  synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p,
 	  rval = locate_fn_flags (base_binfo, complete_dtor_identifier,
 				  NULL_TREE, flags, complain);
 	  process_subob_fn (rval, false, &cleanup_spec, &cleanup_trivial,
-			    &cleanup_deleted, NULL, basetype);
+			    &cleanup_deleted, NULL, NULL,
+			    basetype);
 	}
 
       if (check_vdtor && type_has_virtual_destructor (basetype))
@@ -1221,6 +1239,8 @@  synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p,
       if (diag)
 	msg = ("virtual base %qT does not have a move constructor "
 	       "or trivial copy constructor");
+      if (vbases && constexpr_p)
+	*constexpr_p = false;
       FOR_EACH_VEC_ELT (tree, vbases, i, base_binfo)
 	{
 	  tree basetype = BINFO_TYPE (base_binfo);
@@ -1229,13 +1249,14 @@  synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p,
 	  rval = locate_fn_flags (base_binfo, fnname, argtype, flags, complain);
 
 	  process_subob_fn (rval, move_p, spec_p, trivial_p, deleted_p,
-			    msg, basetype);
+			    constexpr_p, msg, basetype);
 	  if (ctor_p && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (basetype))
 	    {
 	      rval = locate_fn_flags (base_binfo, complete_dtor_identifier,
 				      NULL_TREE, flags, complain);
 	      process_subob_fn (rval, false, &cleanup_spec, &cleanup_trivial,
-				&cleanup_deleted, NULL, basetype);
+				&cleanup_deleted, NULL, NULL,
+				basetype);
 	    }
 	}
     }
@@ -1249,12 +1270,13 @@  synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p,
 	   "constructor or trivial copy constructor");
   walk_field_subobs (TYPE_FIELDS (ctype), fnname, sfk, quals,
 		     copy_arg_p, move_p, assign_p, spec_p, trivial_p,
-		     deleted_p, msg, flags, complain);
+		     deleted_p, constexpr_p, msg, flags, complain);
   if (ctor_p)
     walk_field_subobs (TYPE_FIELDS (ctype), complete_dtor_identifier,
 		       sfk_destructor, TYPE_UNQUALIFIED, false,
 		       false, false, &cleanup_spec, &cleanup_trivial,
-		       &cleanup_deleted, NULL, flags, complain);
+		       &cleanup_deleted, NULL,
+		       NULL, flags, complain);
 
   pop_scope (scope);
 
@@ -1333,7 +1355,7 @@  maybe_explain_implicit_delete (tree decl)
 		 "definition would be ill-formed:", decl);
 	  pop_scope (scope);
 	  synthesized_method_walk (ctype, sfk, const_p,
-				   NULL, NULL, NULL, true);
+				   NULL, NULL, NULL, NULL, true);
 	}
 
       input_location = loc;
@@ -1362,6 +1384,7 @@  implicitly_declare_fn (special_function_kind kind, tree type, bool const_p)
   HOST_WIDE_INT saved_processing_template_decl;
   bool deleted_p;
   bool trivial_p;
+  bool constexpr_p;
 
   /* Because we create declarations for implicitly declared functions
      lazily, we may be creating the declaration for a member of TYPE
@@ -1431,7 +1454,15 @@  implicitly_declare_fn (special_function_kind kind, tree type, bool const_p)
     }
 
   synthesized_method_walk (type, kind, const_p, &raises, &trivial_p,
-			   &deleted_p, false);
+			   &deleted_p, &constexpr_p, false);
+  /* Don't bother marking a deleted constructor as constexpr.  */
+  if (deleted_p)
+    constexpr_p = false;
+  /* A trivial copy/move constructor is also a constexpr constructor.  */
+  else if (trivial_p && cxx_dialect >= cxx0x
+	   && (kind == sfk_copy_constructor
+	       || kind == sfk_move_constructor))
+    gcc_assert (constexpr_p);
 
   if (!trivial_p && type_has_trivial_fn (type, kind))
     type_set_nontrivial_flag (type, kind);
@@ -1481,7 +1512,10 @@  implicitly_declare_fn (special_function_kind kind, tree type, bool const_p)
   DECL_ARTIFICIAL (fn) = 1;
   DECL_DEFAULTED_FN (fn) = 1;
   if (cxx_dialect >= cxx0x)
-    DECL_DELETED_FN (fn) = deleted_p;
+    {
+      DECL_DELETED_FN (fn) = deleted_p;
+      DECL_DECLARED_CONSTEXPR_P (fn) = constexpr_p;
+    }
   DECL_NOT_REALLY_EXTERN (fn) = 1;
   DECL_DECLARED_INLINE_P (fn) = 1;
   gcc_assert (!TREE_USED (fn));
@@ -1521,6 +1555,19 @@  defaulted_late_check (tree fn)
     {
       tree eh_spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (implicit_fn));
       TREE_TYPE (fn) = build_exception_variant (TREE_TYPE (fn), eh_spec);
+      if (DECL_DECLARED_CONSTEXPR_P (implicit_fn))
+	/* Hmm...should we do this for out-of-class too? Should it be OK to
+	   add constexpr later like inline, rather than requiring
+	   declarations to match?  */
+	DECL_DECLARED_CONSTEXPR_P (fn) = true;
+    }
+
+  if (!DECL_DECLARED_CONSTEXPR_P (implicit_fn)
+      && DECL_DECLARED_CONSTEXPR_P (fn))
+    {
+      if (!CLASSTYPE_TEMPLATE_INSTANTIATION (ctx))
+	error ("%qD cannot be declared as constexpr", fn);
+      DECL_DECLARED_CONSTEXPR_P (fn) = false;
     }
 
   if (DECL_DELETED_FN (implicit_fn))
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-delete.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-delete.C
new file mode 100644
index 0000000..67c9503
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-delete.C
@@ -0,0 +1,3 @@ 
+// { dg-options -std=c++0x }
+
+constexpr bool never() = delete; // useless, but OK