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

login
register
mail settings
Submitter Jason Merrill
Date May 18, 2011, 5:14 p.m.
Message ID <4DD3FE6D.5010300@redhat.com>
Download mbox | patch
Permalink /patch/96199/
State New
Headers show

Comments

Jason Merrill - May 18, 2011, 5:14 p.m.
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.

Patch

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" }
 };