From patchwork Wed May 11 21:27:32 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jason Merrill X-Patchwork-Id: 95207 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) by ozlabs.org (Postfix) with SMTP id 578811007D9 for ; Thu, 12 May 2011 08:27:56 +1000 (EST) Received: (qmail 3140 invoked by alias); 11 May 2011 21:27:54 -0000 Received: (qmail 3102 invoked by uid 22791); 11 May 2011 21:27:50 -0000 X-SWARE-Spam-Status: No, hits=-6.2 required=5.0 tests=AWL, BAYES_00, RCVD_IN_DNSWL_HI, SPF_HELO_PASS, TW_CX, TW_FN, TW_KF, TW_TM, T_RP_MATCHES_RCVD X-Spam-Check-By: sourceware.org Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Wed, 11 May 2011 21:27:34 +0000 Received: from int-mx12.intmail.prod.int.phx2.redhat.com (int-mx12.intmail.prod.int.phx2.redhat.com [10.5.11.25]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id p4BLRXCD009545 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Wed, 11 May 2011 17:27:34 -0400 Received: from [127.0.0.1] (ovpn-113-60.phx2.redhat.com [10.3.113.60]) by int-mx12.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id p4BLRW77017891 for ; Wed, 11 May 2011 17:27:33 -0400 Message-ID: <4DCAFF44.3000801@redhat.com> Date: Wed, 11 May 2011 17:27:32 -0400 From: Jason Merrill User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.15) Gecko/20110421 Fedora/3.1.9-2.fc14 Lightning/1.0b2 Thunderbird/3.1.9 MIME-Version: 1.0 To: gcc-patches List Subject: C++ PATCH for c++/48948 (rejecting constexpr friend that takes the current class) Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org 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 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 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 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 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 +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 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) { } +};