diff mbox

C++ PATCH for c++/48948 (rejecting constexpr friend that takes the current class)

Message ID 4DCAFF44.3000801@redhat.com
State New
Headers show

Commit Message

Jason Merrill May 11, 2011, 9:27 p.m. UTC
We want to allow a constexpr friend function that takes the current 
class, so we need to defer checking the literality of parameter types 
until any classes involved are complete.  I broke the changes into a few 
different commits:

1) Only set DECL_DECLARED_CONSTEXPR_P in one place in grokdeclarator (so 
that it isn't re-set after validate_constexpr_fundecl clears it).

2) Add constexpr functions to the hash table when they're defined, not 
when they're declared.

3) Check DECL_TEMPLATE_INFO rather than DECL_TEMPLATE_INSTANTIATION when 
deciding whether to give an error for a function that doesn't satisfy 
the constexpr requirements.

4) Actually do the deferral, by deferring any function that we find to 
have a parameter type that's currently being defined and then trying 
again when we complete a class.

Tested x86_64-pc-linux-gnu, applying to trunk.
commit 63f5464dc11fef107096efbcf2a75ef3b792db57
Author: Jason Merrill <jason@redhat.com>
Date:   Wed May 11 00:29:14 2011 -0400

    	* decl.c (grokdeclarator): Only set DECL_DECLARED_CONSTEXPR_P once.
diff mbox

Patch

diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index eff2360..ad816f1 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -9933,7 +9933,6 @@  grokdeclarator (const cp_declarator *declarator,
 		      return error_mark_node;
 		  }
 
-                DECL_DECLARED_CONSTEXPR_P (decl) = constexpr_p;
 		decl = do_friend (ctype, unqualified_id, decl,
 				  *attrlist, flags,
 				  funcdef_flag);
@@ -10183,8 +10182,11 @@  grokdeclarator (const cp_declarator *declarator,
 	      }
 	  }
 	else if (constexpr_p && DECL_EXTERNAL (decl))
-	  error ("declaration of constexpr variable %qD is not a definition",
-		 decl);
+	  {
+	    error ("declaration of constexpr variable %qD is not a definition",
+		   decl);
+	    constexpr_p = false;
+	  }
       }
 
     if (storage_class == sc_extern && initialized && !funcdef_flag)
@@ -10213,8 +10215,8 @@  grokdeclarator (const cp_declarator *declarator,
     else if (storage_class == sc_static)
       DECL_THIS_STATIC (decl) = 1;
 
-    /* Don't forget constexprness.  */
-    if (constexpr_p)
+    /* Set constexpr flag on vars (functions got it in grokfndecl).  */
+    if (constexpr_p && TREE_CODE (decl) == VAR_DECL)
       DECL_DECLARED_CONSTEXPR_P (decl) = true;
 
     /* Record constancy and volatility on the DECL itself .  There's

commit 90613a6af841f2a8563c0669be47211c52a08d35
Author: Jason Merrill <jason@redhat.com>
Date:   Wed May 11 00:31:29 2011 -0400

    	* semantics.c (register_constexpr_fundef): Add to hash table here.
    	(validate_constexpr_fundecl): Not here.

diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index bfe233e..e12f036 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -5456,9 +5456,6 @@  is_valid_constexpr_fn (tree fun, bool complain)
 tree
 validate_constexpr_fundecl (tree fun)
 {
-  constexpr_fundef entry;
-  constexpr_fundef **slot;
-
   if (processing_template_decl || !DECL_DECLARED_CONSTEXPR_P (fun))
     return NULL;
   else if (DECL_CLONED_FUNCTION_P (fun))
@@ -5471,21 +5468,6 @@  validate_constexpr_fundecl (tree fun)
       return NULL;
     }
 
-  /* Create the constexpr function table if necessary.  */
-  if (constexpr_fundef_table == NULL)
-    constexpr_fundef_table = htab_create_ggc (101,
-                                              constexpr_fundef_hash,
-                                              constexpr_fundef_equal,
-                                              ggc_free);
-  entry.decl = fun;
-  entry.body = NULL;
-  slot = (constexpr_fundef **)
-    htab_find_slot (constexpr_fundef_table, &entry, INSERT);
-  if (*slot == NULL)
-    {
-      *slot = ggc_alloc_constexpr_fundef ();
-      **slot = entry;
-    }
   return fun;
 }
 
@@ -5722,8 +5704,8 @@  constexpr_fn_retval (tree body)
 tree
 register_constexpr_fundef (tree fun, tree body)
 {
-  constexpr_fundef *fundef = retrieve_constexpr_fundef (fun);
-  gcc_assert (fundef != NULL && fundef->body == NULL);
+  constexpr_fundef entry;
+  constexpr_fundef **slot;
 
   if (DECL_CONSTRUCTOR_P (fun))
     body = build_constexpr_constructor_member_initializers
@@ -5754,7 +5736,22 @@  register_constexpr_fundef (tree fun, tree body)
 	require_potential_rvalue_constant_expression (body);
       return NULL;
     }
-  fundef->body = body;
+
+  /* Create the constexpr function table if necessary.  */
+  if (constexpr_fundef_table == NULL)
+    constexpr_fundef_table = htab_create_ggc (101,
+                                              constexpr_fundef_hash,
+                                              constexpr_fundef_equal,
+                                              ggc_free);
+  entry.decl = fun;
+  entry.body = body;
+  slot = (constexpr_fundef **)
+    htab_find_slot (constexpr_fundef_table, &entry, INSERT);
+
+  gcc_assert (*slot == NULL);
+  *slot = ggc_alloc_constexpr_fundef ();
+  **slot = entry;
+
   return fun;
 }
 

commit 328b9d1a57762cb23d4572771ff8d2ee00d016e4
Author: Jason Merrill <jason@redhat.com>
Date:   Wed May 11 00:32:18 2011 -0400

    	* semantics.c (validate_constexpr_fundecl): Check DECL_TEMPLATE_INFO
    	rather than DECL_TEMPLATE_INSTANTIATION.
    	(cxx_eval_call_expression): Likewise.

diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index e12f036..2e15800 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -5462,7 +5462,7 @@  validate_constexpr_fundecl (tree fun)
     /* We already checked the original function.  */
     return fun;
 
-  if (!is_valid_constexpr_fn (fun, !DECL_TEMPLATE_INSTANTIATION (fun)))
+  if (!is_valid_constexpr_fn (fun, !DECL_TEMPLATE_INFO (fun)))
     {
       DECL_DECLARED_CONSTEXPR_P (fun) = false;
       return NULL;
@@ -5732,7 +5732,7 @@  register_constexpr_fundef (tree fun, tree body)
   if (!potential_rvalue_constant_expression (body))
     {
       DECL_DECLARED_CONSTEXPR_P (fun) = false;
-      if (!DECL_TEMPLATE_INSTANTIATION (fun))
+      if (!DECL_TEMPLATE_INFO (fun))
 	require_potential_rvalue_constant_expression (body);
       return NULL;
     }
@@ -6076,7 +6076,7 @@  cxx_eval_call_expression (const constexpr_call *old_call, tree t,
       if (!allow_non_constant)
 	{
 	  error_at (loc, "%qD is not a constexpr function", fun);
-	  if (DECL_TEMPLATE_INSTANTIATION (fun)
+	  if (DECL_TEMPLATE_INFO (fun)
 	      && DECL_DECLARED_CONSTEXPR_P (DECL_TEMPLATE_RESULT
 					    (DECL_TI_TEMPLATE (fun))))
 	    is_valid_constexpr_fn (fun, true);

commit abd449ca7061cfd47a36a65e52003dc1c823bfa0
Author: Jason Merrill <jason@redhat.com>
Date:   Wed May 11 00:34:33 2011 -0400

    	PR c++/48948
    	* semantics.c (validate_constexpr_fundecl): Defer checking if
    	an argument type is being defined.
    	(is_valid_constexpr_fn): Add defer_ok parm.
    	(cxx_eval_call_expression): Adjust.
    	(check_deferred_constexpr_decls): New.
    	(literal_type_p): Make sure type isn't being defined.
    	(ensure_literal_type_for_constexpr_object): Handle type being defined.
    	* cp-tree.h: Declare check_deferred_constexpr_decls.
    	* decl.c (grokfndecl): Call validate_constexpr_fundecl here.
    	(start_preparsed_function, cp_finish_decl): Not here.
    	* class.c (finalize_literal_type_property): Don't call
    	validate_constexpr_fundecl.
    	(finish_struct): Call check_deferred_constexpr_decls.
    	* pt.c (tsubst_decl): Call validate_constexpr_fundecl.
    	(instantiate_class_template): Call check_deferred_constexpr_decls.

diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index 293dd1c..938d522 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -4578,8 +4578,6 @@  type_requires_array_cookie (tree type)
 static void
 finalize_literal_type_property (tree t)
 {
-  tree fn;
-
   if (cxx_dialect < cxx0x
       || TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t)
       /* FIXME These constraints seem unnecessary; remove from standard.
@@ -4589,11 +4587,6 @@  finalize_literal_type_property (tree t)
   else if (CLASSTYPE_LITERAL_P (t) && !TYPE_HAS_TRIVIAL_DFLT (t)
 	   && !TYPE_HAS_CONSTEXPR_CTOR (t))
     CLASSTYPE_LITERAL_P (t) = false;
-
-  for (fn = TYPE_METHODS (t); fn; fn = DECL_CHAIN (fn))
-    if (DECL_DECLARED_CONSTEXPR_P (fn)
-	&& TREE_CODE (fn) != TEMPLATE_DECL)
-      validate_constexpr_fundecl (fn);
 }
 
 /* Check the validity of the bases and members declared in T.  Add any
@@ -5834,6 +5827,8 @@  finish_struct (tree t, tree attributes)
   else
     error ("trying to finish struct, but kicked out due to previous parse errors");
 
+  check_deferred_constexpr_decls ();
+
   if (processing_template_decl && at_function_scope_p ())
     add_stmt (build_min (TAG_DEFN, t));
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 1705232..bb684ba 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5323,6 +5323,7 @@  extern void finish_handler_parms		(tree, tree);
 extern void finish_handler			(tree);
 extern void finish_cleanup			(tree, tree);
 extern bool literal_type_p (tree);
+extern void check_deferred_constexpr_decls (void);
 extern tree validate_constexpr_fundecl (tree);
 extern tree register_constexpr_fundef (tree, tree);
 extern bool check_constexpr_ctor_body (tree, tree);
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index ad816f1..87be112 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -5911,13 +5911,7 @@  cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
 	}
     }
 
-  if (TREE_CODE (decl) == FUNCTION_DECL
-      /* For members, defer until finalize_literal_type_property.  */
-      && (!DECL_CLASS_SCOPE_P (decl)
-	  || !TYPE_BEING_DEFINED (DECL_CONTEXT (decl))))
-    validate_constexpr_fundecl (decl);
-
-  else if (!ensure_literal_type_for_constexpr_object (decl))
+  if (!ensure_literal_type_for_constexpr_object (decl))
     DECL_DECLARED_CONSTEXPR_P (decl) = 0;
 
   if (init && TREE_CODE (decl) == FUNCTION_DECL)
@@ -7206,7 +7200,10 @@  grokfndecl (tree ctype,
   if (inlinep)
     DECL_DECLARED_INLINE_P (decl) = 1;
   if (inlinep & 2)
-    DECL_DECLARED_CONSTEXPR_P (decl) = true;
+    {
+      DECL_DECLARED_CONSTEXPR_P (decl) = true;
+      validate_constexpr_fundecl (decl);
+    }
 
   DECL_EXTERNAL (decl) = 1;
   if (quals && TREE_CODE (type) == FUNCTION_TYPE)
@@ -12524,10 +12521,6 @@  start_preparsed_function (tree decl1, tree attrs, int flags)
 	maybe_apply_pragma_weak (decl1);
     }
 
-  /* constexpr functions must have literal argument types and
-     literal return type.  */
-  validate_constexpr_fundecl (decl1);
-
   /* Reset this in case the call to pushdecl changed it.  */
   current_function_decl = decl1;
 
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 4b32ce9..50ed180 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -8594,6 +8594,8 @@  instantiate_class_template_1 (tree type)
   pop_deferring_access_checks ();
   pop_tinst_level ();
 
+  check_deferred_constexpr_decls ();
+
   /* The vtable for a template class can be emitted in any translation
      unit in which the class is instantiated.  When there is no key
      method, however, finish_struct_1 will already have added TYPE to
@@ -9740,6 +9742,7 @@  tsubst_decl (tree t, tree args, tsubst_flags_t complain)
 	if (DECL_DEFAULTED_OUTSIDE_CLASS_P (r)
 	    && !processing_template_decl)
 	  defaulted_late_check (r);
+	validate_constexpr_fundecl (r);
 
 	apply_late_template_attributes (&r, DECL_ATTRIBUTES (r), 0,
 					args, complain, in_decl);
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 2e15800..ffabad1 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -5336,7 +5336,11 @@  literal_type_p (tree t)
       || TREE_CODE (t) == REFERENCE_TYPE)
     return true;
   if (CLASS_TYPE_P (t))
-    return CLASSTYPE_LITERAL_P (t);
+    {
+      /* We can't answer this question until the class is complete.  */
+      gcc_assert (!TYPE_BEING_DEFINED (t) || errorcount);
+      return CLASSTYPE_LITERAL_P (complete_type (t));
+    }
   if (TREE_CODE (t) == ARRAY_TYPE)
     return literal_type_p (strip_array_types (t));
   return false;
@@ -5350,13 +5354,17 @@  ensure_literal_type_for_constexpr_object (tree decl)
 {
   tree type = TREE_TYPE (decl);
   if (TREE_CODE (decl) == VAR_DECL && DECL_DECLARED_CONSTEXPR_P (decl)
-      && !processing_template_decl
-      /* The call to complete_type is just for initializer_list.  */
-      && !literal_type_p (complete_type (type)))
+      && !processing_template_decl)
     {
-      error ("the type %qT of constexpr variable %qD is not literal",
-             type, decl);
-      return NULL;
+      if (CLASS_TYPE_P (type) && TYPE_BEING_DEFINED (type))
+	/* Don't complain here, we'll complain about incompleteness
+	   when we try to initialize the variable.  */;
+      else if (!literal_type_p (type))
+	{
+	  error ("the type %qT of constexpr variable %qD is not literal",
+		 type, decl);
+	  return NULL;
+	}
     }
   return decl;
 }
@@ -5409,15 +5417,22 @@  retrieve_constexpr_fundef (tree fun)
 }
 
 /* Check whether the parameter and return types of FUN are valid for a
-   constexpr function, and complain if COMPLAIN.  */
+   constexpr function, and complain if COMPLAIN.  If DEFER_OK is true,
+   return -1 if we can't tell yet because some of the types are still being
+   defined.  */
 
-static bool
-is_valid_constexpr_fn (tree fun, bool complain)
+static int
+is_valid_constexpr_fn (tree fun, bool complain, bool defer_ok)
 {
+#define IF_NON_LITERAL(TYPE)						\
+  if (defer_ok && CLASS_TYPE_P (TYPE) && TYPE_BEING_DEFINED (TYPE))	\
+    return -1;								\
+  else if (!literal_type_p (TYPE))
+
   tree parm = FUNCTION_FIRST_USER_PARM (fun);
   bool ret = true;
   for (; parm != NULL; parm = TREE_CHAIN (parm))
-    if (!literal_type_p (TREE_TYPE (parm)))
+    IF_NON_LITERAL (TREE_TYPE (parm))
       {
 	ret = false;
 	if (complain)
@@ -5428,7 +5443,7 @@  is_valid_constexpr_fn (tree fun, bool complain)
   if (!DECL_CONSTRUCTOR_P (fun))
     {
       tree rettype = TREE_TYPE (TREE_TYPE (fun));
-      if (!literal_type_p (rettype))
+      IF_NON_LITERAL (rettype)
 	{
 	  ret = false;
 	  if (complain)
@@ -5436,18 +5451,51 @@  is_valid_constexpr_fn (tree fun, bool complain)
 		   rettype, fun);
 	}
 
-      if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fun)
-	  && !CLASSTYPE_LITERAL_P (DECL_CONTEXT (fun)))
+      if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fun))
 	{
-	  ret = false;
-	  if (complain)
-	    error ("enclosing class of %q+#D is not a literal type", fun);
+	  IF_NON_LITERAL (DECL_CONTEXT (fun))
+	    {
+	      ret = false;
+	      if (complain)
+		error ("enclosing class of %q+#D is not a literal type", fun);
+	    }
 	}
     }
 
   return ret;
 }
 
+/* We can't check the parameter and return types of a constexpr function
+   for literality until any open classes are complete, so we defer checking
+   of any constexpr functions declared in a class.  */
+
+static GTY(()) VEC(tree,gc) *deferred_constexpr_decls;
+
+void
+check_deferred_constexpr_decls (void)
+{
+  unsigned i;
+  tree fn;
+
+  /* Some of the deferred decls might still need to be deferred,
+     so move the vector out of the way.  */
+  VEC(tree,gc) *vec = deferred_constexpr_decls;
+  deferred_constexpr_decls = NULL;
+
+  FOR_EACH_VEC_ELT (tree, vec, i, fn)
+    validate_constexpr_fundecl (fn);
+
+  if (deferred_constexpr_decls == NULL)
+    {
+      /* If we didn't need to re-defer any, keep the same vector.  */
+      VEC_truncate (tree, vec, 0);
+      deferred_constexpr_decls = vec;
+    }
+  else
+    /* Otherwise, discard the old vector.  */
+    release_tree_vector (vec);
+}
+
 /* Return non-null if FUN certainly designates a valid constexpr function
    declaration.  Otherwise return NULL.  Issue appropriate diagnostics
    if necessary.  Note that we only check the declaration, not the body
@@ -5456,13 +5504,22 @@  is_valid_constexpr_fn (tree fun, bool complain)
 tree
 validate_constexpr_fundecl (tree fun)
 {
+  int valid;
+
   if (processing_template_decl || !DECL_DECLARED_CONSTEXPR_P (fun))
     return NULL;
   else if (DECL_CLONED_FUNCTION_P (fun))
     /* We already checked the original function.  */
     return fun;
 
-  if (!is_valid_constexpr_fn (fun, !DECL_TEMPLATE_INFO (fun)))
+  valid = is_valid_constexpr_fn (fun, !DECL_TEMPLATE_INFO (fun),
+				 /*defer_ok=*/true);
+  if (valid < 0)
+    {
+      VEC_safe_push (tree, gc, deferred_constexpr_decls, fun);
+      return NULL;
+    }
+  else if (valid == 0)
     {
       DECL_DECLARED_CONSTEXPR_P (fun) = false;
       return NULL;
@@ -6079,7 +6136,7 @@  cxx_eval_call_expression (const constexpr_call *old_call, tree t,
 	  if (DECL_TEMPLATE_INFO (fun)
 	      && DECL_DECLARED_CONSTEXPR_P (DECL_TEMPLATE_RESULT
 					    (DECL_TI_TEMPLATE (fun))))
-	    is_valid_constexpr_fn (fun, true);
+	    is_valid_constexpr_fn (fun, true, /*defer_ok=*/false);
 	}
       *non_constant_p = true;
       return t;
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-friend.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-friend.C
new file mode 100644
index 0000000..f1d9cce
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-friend.C
@@ -0,0 +1,23 @@ 
+// PR c++/48948
+// { dg-options -std=c++0x }
+
+struct A { A(); };
+
+struct B {
+  friend constexpr int f(B) { return 0; } // OK
+  friend constexpr int f(A) { return 0; } // { dg-error "constexpr" }
+};
+
+template <class T>
+struct C
+{
+  friend constexpr int f(C) { return 0; }
+  friend constexpr int g(C, A) { return 0; } // { dg-error "double" }
+  constexpr int m(C) { return 0; }
+  constexpr int m(A) { return 0; } // { dg-error "double" }
+};
+
+constexpr int i = f(C<int>());
+constexpr int j = C<int>().m(C<int>());
+constexpr int k = C<double>().m(A()); // { dg-error "not a constexpr function" }
+constexpr int l = g(C<double>(),A()); // { dg-error "not a constexpr function" }
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-incomplete1.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-incomplete1.C
new file mode 100644
index 0000000..3f40e29
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-incomplete1.C
@@ -0,0 +1,7 @@ 
+// { dg-options -std=c++0x }
+
+struct A
+{
+  static constexpr A a = 1;	// { dg-error "incomplete" }
+  constexpr A(int i) { }
+};