From patchwork Tue Jul 13 22:29:51 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jason Merrill X-Patchwork-Id: 58821 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 280C6B6F0E for ; Wed, 14 Jul 2010 08:30:06 +1000 (EST) Received: (qmail 18034 invoked by alias); 13 Jul 2010 22:30:04 -0000 Received: (qmail 17861 invoked by uid 22791); 13 Jul 2010 22:30:00 -0000 X-SWARE-Spam-Status: No, hits=-5.9 required=5.0 tests=AWL, BAYES_00, RCVD_IN_DNSWL_HI, SPF_HELO_PASS, TW_VP, TW_VT, 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; Tue, 13 Jul 2010 22:29:54 +0000 Received: from int-mx03.intmail.prod.int.phx2.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.16]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id o6DMTq02003772 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Tue, 13 Jul 2010 18:29:52 -0400 Received: from [IPv6:::1] (ovpn01.gateway.prod.ext.phx2.redhat.com [10.5.9.1]) by int-mx03.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id o6DMTpE0015758 for ; Tue, 13 Jul 2010 18:29:52 -0400 Message-ID: <4C3CE8DF.7010504@redhat.com> Date: Tue, 13 Jul 2010 18:29:51 -0400 From: Jason Merrill User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.10) Gecko/20100619 Lightning/1.0b1 Shredder/3.0.6pre MIME-Version: 1.0 To: gcc-patches List Subject: C++ PATCH for c++/44909 (confusion with circular lazy declaration) 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 In this testcase, trying to lazily declare the Ray default constructor means lazily declaring the array copy constructor, which means seeing which Vector constructor will be used for the copy, which means seeing if there's a conversion from Vector to Ray, which means lazily declaring the Ray copy constructor, which means failing to lazily declare the array copy constructor. This patch avoids this issue by immediately declaring the constructors for array; at that point, Ray isn't a complete type, so there is no conversion from Vector to Ray, so the cycle is broken. Tested x86_64-pc-linux-gnu, applied to trunk. commit a2a65b8045e79a850558fe2e9157e224abf5f924 Author: Jason Merrill Date: Tue Jul 13 15:18:59 2010 -0400 PR c++/44909 * cp-tree.h (struct lang_type_class): Add has_user_opeq. (TYPE_HAS_USER_OPEQ): New. * decl.c (grok_special_member_properties): Set it. * class.c (add_implicitly_declared_members): Don't lazily declare constructors/operator= if a base or member has a user-declared one. (check_bases_and_members, check_bases): Adjust. (check_field_decls, check_field_decl): Adjust. * method.c (synthesized_method_walk): Initialize check_vdtor. diff --git a/gcc/cp/class.c b/gcc/cp/class.c index dfb2cd9..ed7367c 100644 --- a/gcc/cp/class.c +++ b/gcc/cp/class.c @@ -130,7 +130,7 @@ static void finish_struct_methods (tree); static void maybe_warn_about_overly_private_class (tree); static int method_name_cmp (const void *, const void *); static int resort_method_name_cmp (const void *, const void *); -static void add_implicitly_declared_members (tree, int, int); +static void add_implicitly_declared_members (tree, int, int, int, int); static tree fixed_type_or_null (tree, int *, int *); static tree build_simple_base_path (tree expr, tree binfo); static tree build_vtbl_ref_1 (tree, tree); @@ -139,13 +139,13 @@ static void build_vtbl_initializer (tree, tree, tree, tree, int *, static int count_fields (tree); static int add_fields_to_record_type (tree, struct sorted_fields_type*, int); static bool check_bitfield_decl (tree); -static void check_field_decl (tree, tree, int *, int *, int *); -static void check_field_decls (tree, tree *, int *, int *); +static void check_field_decl (tree, tree, int *, int *, int *, int *, int *); +static void check_field_decls (tree, tree *, int *, int *, int *, int *); static tree *build_base_field (record_layout_info, tree, splay_tree, tree *); static void build_base_fields (record_layout_info, splay_tree, tree *); static void check_methods (tree); static void remove_zero_width_bit_fields (tree); -static void check_bases (tree, int *, int *); +static void check_bases (tree, int *, int *, int *, int *); static void check_bases_and_members (tree); static tree create_vtable_ptr (tree, tree *); static void include_empty_classes (record_layout_info); @@ -1249,7 +1249,9 @@ handle_using_decl (tree using_decl, tree t) static void check_bases (tree t, int* cant_have_const_ctor_p, - int* no_const_asn_ref_p) + int* no_const_asn_ref_p, + int* cant_have_lazy_ctor, + int* cant_have_lazy_opeq) { int i; int seen_non_virtual_nearly_empty_base_p; @@ -1288,6 +1290,10 @@ check_bases (tree t, if (TYPE_HAS_COPY_ASSIGN (basetype) && !TYPE_HAS_CONST_COPY_ASSIGN (basetype)) *no_const_asn_ref_p = 1; + if (TYPE_HAS_USER_CONSTRUCTOR (basetype)) + *cant_have_lazy_ctor = 1; + if (TYPE_HAS_USER_OPEQ (basetype)) + *cant_have_lazy_opeq = 1; if (BINFO_VIRTUAL_P (base_binfo)) /* A virtual base does not effect nearly emptiness. */ @@ -2628,7 +2634,9 @@ maybe_add_class_template_decl_list (tree type, tree t, int friend_p) static void add_implicitly_declared_members (tree t, int cant_have_const_cctor, - int cant_have_const_assignment) + int cant_have_const_assignment, + int cant_have_lazy_ctor, + int cant_have_lazy_opeq) { /* Destructor. */ if (!CLASSTYPE_DESTRUCTORS (t)) @@ -2682,6 +2690,26 @@ add_implicitly_declared_members (tree t, CLASSTYPE_LAZY_MOVE_ASSIGN (t) = 1; } + /* If a base or member type has a user-declared constructor or operator=, + we need to declare ours now to avoid issues with circular lazy + declarations (cpp0x/implicit6.C). */ + if (cant_have_lazy_ctor) + { + if (CLASSTYPE_LAZY_DEFAULT_CTOR (t)) + lazily_declare_fn (sfk_constructor, t); + if (CLASSTYPE_LAZY_COPY_CTOR (t)) + lazily_declare_fn (sfk_copy_constructor, t); + if (CLASSTYPE_LAZY_MOVE_CTOR (t)) + lazily_declare_fn (sfk_move_constructor, t); + } + if (cant_have_lazy_opeq) + { + if (CLASSTYPE_LAZY_COPY_ASSIGN (t)) + lazily_declare_fn (sfk_copy_assignment, t); + if (CLASSTYPE_LAZY_MOVE_ASSIGN (t)) + lazily_declare_fn (sfk_move_assignment, t); + } + /* We can't be lazy about declaring functions that might override a virtual function from a base class. */ if (TYPE_POLYMORPHIC_P (t) @@ -2830,7 +2858,9 @@ check_field_decl (tree field, tree t, int* cant_have_const_ctor, int* no_const_asn_ref, - int* any_default_members) + int* any_default_members, + int* cant_have_lazy_ctor, + int* cant_have_lazy_opeq) { tree type = strip_array_types (TREE_TYPE (field)); @@ -2847,7 +2877,8 @@ check_field_decl (tree field, for (fields = TYPE_FIELDS (type); fields; fields = TREE_CHAIN (fields)) if (TREE_CODE (fields) == FIELD_DECL && !DECL_C_BIT_FIELD (field)) check_field_decl (fields, t, cant_have_const_ctor, - no_const_asn_ref, any_default_members); + no_const_asn_ref, any_default_members, + cant_have_lazy_ctor, cant_have_lazy_opeq); } /* Check members with class type for constructors, destructors, etc. */ @@ -2893,6 +2924,11 @@ check_field_decl (tree field, if (TYPE_HAS_COPY_ASSIGN (type) && !TYPE_HAS_CONST_COPY_ASSIGN (type)) *no_const_asn_ref = 1; + + if (TYPE_HAS_USER_CONSTRUCTOR (type)) + *cant_have_lazy_ctor = 1; + if (TYPE_HAS_USER_OPEQ (type)) + *cant_have_lazy_opeq = 1; } if (DECL_INITIAL (field) != NULL_TREE) { @@ -2932,7 +2968,9 @@ check_field_decl (tree field, static void check_field_decls (tree t, tree *access_decls, int *cant_have_const_ctor_p, - int *no_const_asn_ref_p) + int *no_const_asn_ref_p, + int *cant_have_lazy_ctor_p, + int *cant_have_lazy_opeq_p) { tree *field; tree *next; @@ -3124,7 +3162,9 @@ check_field_decls (tree t, tree *access_decls, check_field_decl (x, t, cant_have_const_ctor_p, no_const_asn_ref_p, - &any_default_members); + &any_default_members, + cant_have_lazy_ctor_p, + cant_have_lazy_opeq_p); /* If any field is const, the structure type is pseudo-const. */ if (CP_TYPE_CONST_P (type)) @@ -4447,6 +4487,8 @@ check_bases_and_members (tree t) /* Nonzero if the implicitly generated assignment operator should take a non-const reference argument. */ int no_const_asn_ref; + int cant_have_lazy_ctor = 0; + int cant_have_lazy_opeq = 0; tree access_decls; bool saved_complex_asn_ref; bool saved_nontrivial_dtor; @@ -4459,7 +4501,8 @@ check_bases_and_members (tree t) /* Check all the base-classes. */ check_bases (t, &cant_have_const_ctor, - &no_const_asn_ref); + &no_const_asn_ref, &cant_have_lazy_ctor, + &cant_have_lazy_opeq); /* Check all the method declarations. */ check_methods (t); @@ -4476,7 +4519,9 @@ check_bases_and_members (tree t) being set appropriately. */ check_field_decls (t, &access_decls, &cant_have_const_ctor, - &no_const_asn_ref); + &no_const_asn_ref, + &cant_have_lazy_ctor, + &cant_have_lazy_opeq); /* A nearly-empty class has to be vptr-containing; a nearly empty class contains just a vptr. */ @@ -4548,7 +4593,9 @@ check_bases_and_members (tree t) /* Synthesize any needed methods. */ add_implicitly_declared_members (t, cant_have_const_ctor, - no_const_asn_ref); + no_const_asn_ref, + cant_have_lazy_ctor, + cant_have_lazy_opeq); /* Check defaulted declarations here so we have cant_have_const_ctor and don't need to worry about clones. */ diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index cf128dc..8b076d3 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -1326,6 +1326,7 @@ struct GTY(()) lang_type_class { unsigned lazy_move_assign : 1; unsigned has_complex_move_ctor : 1; unsigned has_complex_move_assign : 1; + unsigned has_user_opeq : 1; /* When adding a flag here, consider whether or not it ought to apply to a template instance if it applies to the template. If @@ -1334,7 +1335,7 @@ struct GTY(()) lang_type_class { /* There are some bits left to fill out a 32-bit word. Keep track of this by updating the size of this bitfield whenever you add or remove a flag. */ - unsigned dummy : 4; + unsigned dummy : 3; tree primary_base; VEC(tree_pair_s,gc) *vcall_indices; @@ -3142,6 +3143,10 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter) user-declared constructor. */ #define TYPE_HAS_USER_CONSTRUCTOR(NODE) (TYPE_LANG_FLAG_1 (NODE)) +/* ...or a user-declared operator=. */ +#define TYPE_HAS_USER_OPEQ(NODE) \ + (LANG_TYPE_CLASS_CHECK (NODE)->has_user_opeq) + /* When appearing in an INDIRECT_REF, it means that the tree structure underneath is actually a call to a constructor. This is needed when the constructor must initialize local storage (which can diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 541f77e..1491720 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -10293,6 +10293,8 @@ grok_special_member_properties (tree decl) int assop = copy_fn_p (decl); + if (!DECL_ARTIFICIAL (decl)) + TYPE_HAS_USER_OPEQ (class_type) = 1; if (assop) { TYPE_HAS_COPY_ASSIGN (class_type) = 1; diff --git a/gcc/cp/method.c b/gcc/cp/method.c index ad41e9a..b09064b 100644 --- a/gcc/cp/method.c +++ b/gcc/cp/method.c @@ -1004,6 +1004,7 @@ synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p, #endif assign_p = false; + check_vdtor = false; switch (sfk) { case sfk_move_assignment: diff --git a/gcc/testsuite/g++.dg/cpp0x/implicit6.C b/gcc/testsuite/g++.dg/cpp0x/implicit6.C new file mode 100644 index 0000000..e517333 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/implicit6.C @@ -0,0 +1,25 @@ +// Circular implicit declarations were causing errors +// { dg-options -std=c++0x } + +struct Ray; + +struct Vector +{ + virtual void f(); // make non-trivially-copyable + Vector(); + Vector(const Ray &) ; +}; + +struct array +{ + Vector v; +}; + +struct Ray +{ + array a; + operator Vector(); +}; + +extern Ray r1; +Ray r2=r1; diff --git a/gcc/testsuite/g++.dg/parse/error28.C b/gcc/testsuite/g++.dg/parse/error28.C index 7e235a1..a0b1e7f 100644 --- a/gcc/testsuite/g++.dg/parse/error28.C +++ b/gcc/testsuite/g++.dg/parse/error28.C @@ -3,7 +3,7 @@ struct virt { virt () {} virt (int i) {} }; struct der : public virtual virt { // { dg-message "8:der::der" } - der (int i) : virt(i) {} // { dg-message "3:candidates are: der" } + der (int i) : virt(i) {} // { dg-message "3:der::der" } }; struct top : public der { top () {} // { dg-bogus "der\\(const" }