From 6eb47b98291138b27e13cc8da738cd3d08ea80a0 Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Wed, 11 May 2011 17:30:18 -0400 Subject: [PATCH] re PR c++/48948 ([C++0x] constexpr friend function cannot be defined in-class) 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. From-SVN: r173683 --- gcc/cp/ChangeLog | 17 ++++ gcc/cp/class.c | 9 +- gcc/cp/cp-tree.h | 1 + gcc/cp/decl.c | 17 +--- gcc/cp/pt.c | 3 + gcc/cp/semantics.c | 95 +++++++++++++++---- gcc/testsuite/ChangeLog | 3 + gcc/testsuite/g++.dg/cpp0x/constexpr-friend.C | 23 +++++ .../g++.dg/cpp0x/constexpr-incomplete1.C | 7 ++ 9 files changed, 137 insertions(+), 38 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp0x/constexpr-friend.C create mode 100644 gcc/testsuite/g++.dg/cpp0x/constexpr-incomplete1.C diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 961b8bfcfe3..94f218179d6 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,5 +1,22 @@ 2011-05-11 Jason Merrill + 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. + * semantics.c (validate_constexpr_fundecl): Check DECL_TEMPLATE_INFO rather than DECL_TEMPLATE_INSTANTIATION. (cxx_eval_call_expression): Likewise. diff --git a/gcc/cp/class.c b/gcc/cp/class.c index 293dd1c0bd5..938d52201e3 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 220273650aa..ec59346be45 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -5325,6 +5325,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 ad816f16977..87be1120e54 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 4b32ce9ab1b..50ed1803443 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 2e15800f3eb..ffabad1802c 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/ChangeLog b/gcc/testsuite/ChangeLog index 51aacc5fddb..d6f1b4cde93 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,5 +1,8 @@ 2011-05-11 Jason Merrill + * g++.dg/cpp0x/constexpr-friend.C: New. + * g++.dg/cpp0x/constexpr-incomplete1.C: New. + * g++.dg/cpp0x/sfinae22.C: New. 2011-05-11 Tobias Burnus 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 00000000000..f1d9ccee790 --- /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 +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()); +constexpr int j = C().m(C()); +constexpr int k = C().m(A()); // { dg-error "not a constexpr function" } +constexpr int l = g(C(),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 00000000000..3f40e29aec0 --- /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) { } +}; -- 2.30.2