public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* C++ PATCH for c++/48948 (rejecting constexpr friend that takes the current class)
@ 2011-05-11 23:17 Jason Merrill
  2011-05-12 21:09 ` Jason Merrill
  2011-05-18 18:04 ` Jason Merrill
  0 siblings, 2 replies; 3+ messages in thread
From: Jason Merrill @ 2011-05-11 23:17 UTC (permalink / raw)
  To: gcc-patches List

[-- Attachment #1: Type: text/plain, Size: 867 bytes --]

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.

[-- Attachment #2: 48948.patch --]
[-- Type: text/plain, Size: 17115 bytes --]

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 --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) { }
+};

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: C++ PATCH for c++/48948 (rejecting constexpr friend that takes the current class)
  2011-05-11 23:17 C++ PATCH for c++/48948 (rejecting constexpr friend that takes the current class) Jason Merrill
@ 2011-05-12 21:09 ` Jason Merrill
  2011-05-18 18:04 ` Jason Merrill
  1 sibling, 0 replies; 3+ messages in thread
From: Jason Merrill @ 2011-05-12 21:09 UTC (permalink / raw)
  To: gcc-patches List

[-- Attachment #1: Type: text/plain, Size: 482 bytes --]

While thinking about this issue some more it occurred to me that this 
deferral can be problematic for constructors, since we use whether or 
not a constructor is really constexpr to decide whether or not a class 
is literal.  The rule that seems to me to make the most sense is to say 
that if we don't know yet whether a constructor is really constexpr, it 
doesn't satisfy the literal class requirement for a constexpr constructor.

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

[-- Attachment #2: cx-ctor.patch --]
[-- Type: text/plain, Size: 3723 bytes --]

commit 7919b46302ba6182d2dfa31d33eacbc2335de31e
Author: Jason Merrill <jason@redhat.com>
Date:   Thu May 12 12:33:08 2011 -0400

    	* cp-tree.h (DECL_DEFERRED_CONSTEXPR_CHECK): New.
    	* semantics.c (validate_constexpr_fundecl): Set it.
    	(check_deferred_constexpr_decls): Clear it.
    	(register_constexpr_fundef): Make sure it isn't set.
    	* decl.c (grok_special_member_properties): Check it.

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index ec59346..bcf78f8 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -93,6 +93,7 @@ c-common.h, not after.
       TYPENAME_IS_RESOLVING_P (in TYPE_NAME_TYPE)
       LAMBDA_EXPR_DEDUCE_RETURN_TYPE_P (in LAMBDA_EXPR)
       TARGET_EXPR_DIRECT_INIT_P (in TARGET_EXPR)
+      DECL_DEFERRED_CONSTEXPR_CHECK (in FUNCTION_DECL)
    3: (TREE_REFERENCE_EXPR) (in NON_LVALUE_EXPR) (commented-out).
       ICS_BAD_FLAG (in _CONV)
       FN_TRY_BLOCK_P (in TRY_BLOCK)
@@ -2338,6 +2339,11 @@ struct GTY((variable_size)) lang_decl {
 #define DECL_DECLARED_CONSTEXPR_P(DECL) \
   DECL_LANG_FLAG_8 (VAR_OR_FUNCTION_DECL_CHECK (STRIP_TEMPLATE (DECL)))
 
+/* True if we can't tell yet whether the argument/return types of DECL
+   are literal because one is still being defined.  */
+#define DECL_DEFERRED_CONSTEXPR_CHECK(DECL) \
+  TREE_LANG_FLAG_2 (FUNCTION_DECL_CHECK (STRIP_TEMPLATE (DECL)))
+
 /* Nonzero if this DECL is the __PRETTY_FUNCTION__ variable in a
    template function.  */
 #define DECL_PRETTY_FUNCTION_P(NODE) \
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 87be112..7939140 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -10681,6 +10681,9 @@ grok_special_member_properties (tree decl)
 	TYPE_HAS_LIST_CTOR (class_type) = 1;
 
       if (DECL_DECLARED_CONSTEXPR_P (decl)
+	  /* It doesn't count if we can't tell yet whether or not
+	     the constructor is actually constexpr.  */
+	  && !DECL_DEFERRED_CONSTEXPR_CHECK (decl)
 	  && !copy_fn_p (decl) && !move_fn_p (decl))
 	TYPE_HAS_CONSTEXPR_CTOR (class_type) = 1;
     }
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index ffabad1..f1f3121 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -5483,7 +5483,10 @@ check_deferred_constexpr_decls (void)
   deferred_constexpr_decls = NULL;
 
   FOR_EACH_VEC_ELT (tree, vec, i, fn)
-    validate_constexpr_fundecl (fn);
+    {
+      DECL_DEFERRED_CONSTEXPR_CHECK (fn) = false;
+      validate_constexpr_fundecl (fn);
+    }
 
   if (deferred_constexpr_decls == NULL)
     {
@@ -5516,6 +5519,7 @@ validate_constexpr_fundecl (tree fun)
 				 /*defer_ok=*/true);
   if (valid < 0)
     {
+      DECL_DEFERRED_CONSTEXPR_CHECK (fun) = true;
       VEC_safe_push (tree, gc, deferred_constexpr_decls, fun);
       return NULL;
     }
@@ -5764,6 +5768,9 @@ register_constexpr_fundef (tree fun, tree body)
   constexpr_fundef entry;
   constexpr_fundef **slot;
 
+  gcc_assert (DECL_DECLARED_CONSTEXPR_P (fun)
+	      && !DECL_DEFERRED_CONSTEXPR_CHECK (fun));
+
   if (DECL_CONSTRUCTOR_P (fun))
     body = build_constexpr_constructor_member_initializers
       (DECL_CONTEXT (fun), body);
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-incomplete2.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-incomplete2.C
new file mode 100644
index 0000000..7a9a24d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-incomplete2.C
@@ -0,0 +1,31 @@
+// A constructor that might or might not be constexpr doesn't make
+// its class literal.
+// { dg-options -std=c++0x }
+
+template <class T>
+struct B
+{
+  constexpr B(T) { }
+  constexpr B() {}
+};
+
+struct A
+{
+  B<A> b;
+};
+
+constexpr A a {};
+
+template <class T>
+struct C
+{
+  constexpr C(T) { }
+  C() {}
+};
+
+struct D
+{
+  C<D> c;
+};
+
+constexpr D d {};		// { dg-error "not literal" }

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: C++ PATCH for c++/48948 (rejecting constexpr friend that takes the current class)
  2011-05-11 23:17 C++ PATCH for c++/48948 (rejecting constexpr friend that takes the current class) Jason Merrill
  2011-05-12 21:09 ` Jason Merrill
@ 2011-05-18 18:04 ` Jason Merrill
  1 sibling, 0 replies; 3+ messages in thread
From: Jason Merrill @ 2011-05-18 18:04 UTC (permalink / raw)
  To: gcc-patches List

[-- Attachment #1: Type: text/plain, Size: 545 bytes --]

On 05/11/2011 05:27 PM, Jason Merrill wrote:
> 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.

It was pointed out to me that the restriction already only applies to 
function definitions, not declarations, which dramatically simplifies 
the code.  This patch reverts most of the previous one, and only checks 
return/parameter types at the point of definition.

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

[-- Attachment #2: 49015.patch --]
[-- Type: text/x-patch, Size: 13149 bytes --]

commit f2a2c7b6af06123b5f81bd474b60bddfe9b58550
Author: Jason Merrill <jason@redhat.com>
Date:   Mon May 16 17:21:17 2011 -0400

    	PR c++/48948
    	PR c++/49015
    	* class.c (finalize_literal_type_property): Do check
    	for constexpr member functions of non-literal class.
    	(finish_struct): Don't call check_deferred_constexpr_decls.
    	* cp-tree.h: Don't declare it.
    	(DECL_DEFERRED_CONSTEXPR_CHECK): Remove.
    	* decl.c (grok_special_member_properties): Don't check it
    	(grokfnedcl): Don't call validate_constexpr_fundecl.
    	(start_preparsed_function): Do call it.
    	* pt.c (tsubst_decl): Don't call it.
    	(instantiate_class_template_1): Don't call
    	check_deferred_constexpr_decls.
    	* semantics.c (literal_type_p): Check for any incompleteness.
    	(ensure_literal_type_for_constexpr_object): Likewise.
    	(is_valid_constexpr_fn): Revert deferral changes.
    	(validate_constexpr_fundecl): Likewise.
    	(register_constexpr_fundef): Likewise.
    	(check_deferred_constexpr_decls): Remove.

diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index dc2c509..4e52b18 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -4582,6 +4582,8 @@ 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.
@@ -4591,6 +4593,18 @@ 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;
+
+  if (!CLASSTYPE_LITERAL_P (t))
+    for (fn = TYPE_METHODS (t); fn; fn = DECL_CHAIN (fn))
+      if (DECL_DECLARED_CONSTEXPR_P (fn)
+	  && TREE_CODE (fn) != TEMPLATE_DECL
+	  && DECL_NONSTATIC_MEMBER_FUNCTION_P (fn)
+	  && !DECL_CONSTRUCTOR_P (fn))
+	{
+	  DECL_DECLARED_CONSTEXPR_P (fn) = false;
+	  if (!DECL_TEMPLATE_INFO (fn))
+	    error ("enclosing class of %q+#D is not a literal type", fn);
+	}
 }
 
 /* Check the validity of the bases and members declared in T.  Add any
@@ -5831,8 +5845,6 @@ 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 c0b5290..dfb2b66 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -93,7 +93,6 @@ c-common.h, not after.
       TYPENAME_IS_RESOLVING_P (in TYPE_NAME_TYPE)
       LAMBDA_EXPR_DEDUCE_RETURN_TYPE_P (in LAMBDA_EXPR)
       TARGET_EXPR_DIRECT_INIT_P (in TARGET_EXPR)
-      DECL_DEFERRED_CONSTEXPR_CHECK (in FUNCTION_DECL)
    3: (TREE_REFERENCE_EXPR) (in NON_LVALUE_EXPR) (commented-out).
       ICS_BAD_FLAG (in _CONV)
       FN_TRY_BLOCK_P (in TRY_BLOCK)
@@ -2345,11 +2344,6 @@ struct GTY((variable_size)) lang_decl {
 #define DECL_DECLARED_CONSTEXPR_P(DECL) \
   DECL_LANG_FLAG_8 (VAR_OR_FUNCTION_DECL_CHECK (STRIP_TEMPLATE (DECL)))
 
-/* True if we can't tell yet whether the argument/return types of DECL
-   are literal because one is still being defined.  */
-#define DECL_DEFERRED_CONSTEXPR_CHECK(DECL) \
-  TREE_LANG_FLAG_2 (FUNCTION_DECL_CHECK (STRIP_TEMPLATE (DECL)))
-
 /* Nonzero if this DECL is the __PRETTY_FUNCTION__ variable in a
    template function.  */
 #define DECL_PRETTY_FUNCTION_P(NODE) \
@@ -5337,7 +5331,6 @@ 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 7939140..e950c43 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -7200,10 +7200,7 @@ grokfndecl (tree ctype,
   if (inlinep)
     DECL_DECLARED_INLINE_P (decl) = 1;
   if (inlinep & 2)
-    {
-      DECL_DECLARED_CONSTEXPR_P (decl) = true;
-      validate_constexpr_fundecl (decl);
-    }
+    DECL_DECLARED_CONSTEXPR_P (decl) = true;
 
   DECL_EXTERNAL (decl) = 1;
   if (quals && TREE_CODE (type) == FUNCTION_TYPE)
@@ -10681,9 +10678,6 @@ grok_special_member_properties (tree decl)
 	TYPE_HAS_LIST_CTOR (class_type) = 1;
 
       if (DECL_DECLARED_CONSTEXPR_P (decl)
-	  /* It doesn't count if we can't tell yet whether or not
-	     the constructor is actually constexpr.  */
-	  && !DECL_DEFERRED_CONSTEXPR_CHECK (decl)
 	  && !copy_fn_p (decl) && !move_fn_p (decl))
 	TYPE_HAS_CONSTEXPR_CTOR (class_type) = 1;
     }
@@ -12524,6 +12518,10 @@ 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 cc14f02..75d0674 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -8595,8 +8595,6 @@ 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
@@ -9743,7 +9741,6 @@ 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 56d2411..8d0cce1 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -5337,9 +5337,9 @@ literal_type_p (tree t)
     return true;
   if (CLASS_TYPE_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));
+      t = complete_type (t);
+      gcc_assert (COMPLETE_TYPE_P (t) || errorcount);
+      return CLASSTYPE_LITERAL_P (t);
     }
   if (TREE_CODE (t) == ARRAY_TYPE)
     return literal_type_p (strip_array_types (t));
@@ -5356,7 +5356,7 @@ ensure_literal_type_for_constexpr_object (tree decl)
   if (TREE_CODE (decl) == VAR_DECL && DECL_DECLARED_CONSTEXPR_P (decl)
       && !processing_template_decl)
     {
-      if (CLASS_TYPE_P (type) && TYPE_BEING_DEFINED (type))
+      if (CLASS_TYPE_P (type) && !COMPLETE_TYPE_P (complete_type (type)))
 	/* Don't complain here, we'll complain about incompleteness
 	   when we try to initialize the variable.  */;
       else if (!literal_type_p (type))
@@ -5417,22 +5417,15 @@ retrieve_constexpr_fundef (tree fun)
 }
 
 /* Check whether the parameter and return types of FUN are valid for a
-   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.  */
+   constexpr function, and complain if COMPLAIN.  */
 
-static int
-is_valid_constexpr_fn (tree fun, bool complain, bool defer_ok)
+static bool
+is_valid_constexpr_fn (tree fun, bool complain)
 {
-#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_NON_LITERAL (TREE_TYPE (parm))
+    if (!literal_type_p (TREE_TYPE (parm)))
       {
 	ret = false;
 	if (complain)
@@ -5443,7 +5436,7 @@ is_valid_constexpr_fn (tree fun, bool complain, bool defer_ok)
   if (!DECL_CONSTRUCTOR_P (fun))
     {
       tree rettype = TREE_TYPE (TREE_TYPE (fun));
-      IF_NON_LITERAL (rettype)
+      if (!literal_type_p (rettype))
 	{
 	  ret = false;
 	  if (complain)
@@ -5451,54 +5444,19 @@ is_valid_constexpr_fn (tree fun, bool complain, bool defer_ok)
 		   rettype, fun);
 	}
 
-      if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fun))
+      /* Check this again here for cxx_eval_call_expression.  */
+      if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fun)
+	  && !CLASSTYPE_LITERAL_P (DECL_CONTEXT (fun)))
 	{
-	  IF_NON_LITERAL (DECL_CONTEXT (fun))
-	    {
-	      ret = false;
-	      if (complain)
-		error ("enclosing class of %q+#D is not a literal type", 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)
-    {
-      DECL_DEFERRED_CONSTEXPR_CHECK (fn) = false;
-      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
@@ -5507,23 +5465,13 @@ check_deferred_constexpr_decls (void)
 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;
 
-  valid = is_valid_constexpr_fn (fun, !DECL_TEMPLATE_INFO (fun),
-				 /*defer_ok=*/true);
-  if (valid < 0)
-    {
-      DECL_DEFERRED_CONSTEXPR_CHECK (fun) = true;
-      VEC_safe_push (tree, gc, deferred_constexpr_decls, fun);
-      return NULL;
-    }
-  else if (valid == 0)
+  if (!is_valid_constexpr_fn (fun, !DECL_TEMPLATE_INFO (fun)))
     {
       DECL_DECLARED_CONSTEXPR_P (fun) = false;
       return NULL;
@@ -5768,9 +5716,6 @@ register_constexpr_fundef (tree fun, tree body)
   constexpr_fundef entry;
   constexpr_fundef **slot;
 
-  gcc_assert (DECL_DECLARED_CONSTEXPR_P (fun)
-	      && !DECL_DEFERRED_CONSTEXPR_CHECK (fun));
-
   if (DECL_CONSTRUCTOR_P (fun))
     body = build_constexpr_constructor_member_initializers
       (DECL_CONTEXT (fun), body);
@@ -6143,7 +6088,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, /*defer_ok=*/false);
+	    is_valid_constexpr_fn (fun, true);
 	}
       *non_constant_p = true;
       return t;
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-incomplete2.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-incomplete2.C
index 7a9a24d..dc0b742 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-incomplete2.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-incomplete2.C
@@ -1,4 +1,4 @@
-// A constructor that might or might not be constexpr doesn't make
+// A constructor that might or might not be constexpr still makes
 // its class literal.
 // { dg-options -std=c++0x }
 
@@ -28,4 +28,4 @@ struct D
   C<D> c;
 };
 
-constexpr D d {};		// { dg-error "not literal" }
+constexpr D d {};		// { dg-error "not a constexpr function" }
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-incomplete3.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-incomplete3.C
new file mode 100644
index 0000000..81822b0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-incomplete3.C
@@ -0,0 +1,12 @@
+// PR c++/49015
+// { dg-options -std=c++0x }
+
+class A;
+
+class B {
+  friend constexpr B f(A); // Line 5
+};
+
+class A {};
+
+constexpr B f(A) { return B(); } // Line 10
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-memfn1.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-memfn1.C
index 4646f82..ef7ac6b 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-memfn1.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-memfn1.C
@@ -13,6 +13,6 @@ constexpr X X::g(X x) { return x; }
 struct Y
 {
   Y() { }
-  constexpr Y f(Y y);		// { dg-error "constexpr" }
-  static constexpr Y g(Y y);	// { dg-error "constexpr" }
+  constexpr Y f(Y y);		// { dg-error "not a literal type" }
+  static constexpr Y g(Y y) {}	// { dg-error "constexpr" }
 };

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2011-05-18 17:14 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-05-11 23:17 C++ PATCH for c++/48948 (rejecting constexpr friend that takes the current class) Jason Merrill
2011-05-12 21:09 ` Jason Merrill
2011-05-18 18:04 ` Jason Merrill

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).