diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index d8004b69508..ea344c45e76 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,29 @@ +2001-12-04 Nathan Sidwell + + PR g++/87 + * cp-tree.h (DECL_COPY_CONSTRUCTOR_P): Use copy_fn_p. + (copy_args_p): Rename to ... + (copy_fn_p): ... here. + (grok_special_member_properties): New function. + (grok_op_properties): Lose VIRTUALP parameter. + (copy_assignment_arg_p): Remove. + * call.c (build_over_call): Use copy_fn_p. + * decl.c (grokfndecl): Reformat. Adjust call to + grok_op_properties. + (copy_args_p): Rename to ... + (copy_fn_p): ... here. Reject template functions. Check for pass + by value. + (grok_special_member_properties): Remember special functions. + (grok_ctor_properties): Don't remember them here, just check. + (grok_op_properties): Likewise. + (start_method): Call grok_special_member_properties. + * decl2.c (grokfield): Likewise. + (copy_assignment_arg_p): Remove. + (grok_function_init): Don't remember abstract assignment here. + * pt.c (instantiate_class_template): Call + grok_special_member_properties. + (tsubst_decl): Adjust grok_op_properties call. + 2001-12-08 Aldy Hernandez * lex.c (rid_to_yy): Add RID_CHOOSE_EXPR and diff --git a/gcc/cp/call.c b/gcc/cp/call.c index 0a59b179de3..21049b8b3ef 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -4280,7 +4280,7 @@ build_over_call (cand, args, flags) } } else if (DECL_OVERLOADED_OPERATOR_P (fn) == NOP_EXPR - && copy_args_p (fn) + && copy_fn_p (fn) && TYPE_HAS_TRIVIAL_ASSIGN_REF (DECL_CONTEXT (fn))) { tree to = stabilize_reference diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 992c708aee2..6172adf2617 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -1841,7 +1841,7 @@ struct lang_decl /* Nonzero if NODE (a FUNCTION_DECL) is a copy constructor. */ #define DECL_COPY_CONSTRUCTOR_P(NODE) \ - (DECL_CONSTRUCTOR_P (NODE) && copy_args_p (NODE)) + (DECL_CONSTRUCTOR_P (NODE) && copy_fn_p (NODE) > 0) /* Nonzero if NODE is a destructor. */ #define DECL_DESTRUCTOR_P(NODE) \ @@ -3690,9 +3690,10 @@ extern int complete_array_type PARAMS ((tree, tree, int)); extern tree build_ptrmemfunc_type PARAMS ((tree)); /* the grokdeclarator prototype is in decl.h */ extern int parmlist_is_exprlist PARAMS ((tree)); -extern int copy_args_p PARAMS ((tree)); +extern int copy_fn_p PARAMS ((tree)); +extern void grok_special_member_properties PARAMS ((tree)); extern int grok_ctor_properties PARAMS ((tree, tree)); -extern void grok_op_properties PARAMS ((tree, int, int)); +extern void grok_op_properties PARAMS ((tree, int)); extern tree xref_tag PARAMS ((tree, tree, int)); extern tree xref_tag_from_type PARAMS ((tree, tree, int)); extern void xref_basetypes PARAMS ((tree, tree, tree, tree)); @@ -3757,7 +3758,6 @@ extern tree grokfield PARAMS ((tree, tree, tree, tree, tree)); extern tree grokbitfield PARAMS ((tree, tree, tree)); extern tree groktypefield PARAMS ((tree, tree)); extern tree grokoptypename PARAMS ((tree, tree)); -extern int copy_assignment_arg_p PARAMS ((tree, int)); extern void cplus_decl_attributes PARAMS ((tree *, tree, int)); extern tree constructor_name_full PARAMS ((tree)); extern tree constructor_name PARAMS ((tree)); diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 66aa70d89c6..26d3a2499ed 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -8789,9 +8789,7 @@ grokfndecl (ctype, type, declarator, orig_declarator, virtualp, flags, quals, tree t; if (raises) - { - type = build_exception_variant (type, raises); - } + type = build_exception_variant (type, raises); decl = build_lang_decl (FUNCTION_DECL, declarator, type); /* Propagate volatile out from type to decl. */ @@ -8902,7 +8900,7 @@ grokfndecl (ctype, type, declarator, orig_declarator, virtualp, flags, quals, } if (IDENTIFIER_OPNAME_P (DECL_NAME (decl))) - grok_op_properties (decl, virtualp, check < 0); + grok_op_properties (decl, friendp); if (ctype && decl_function_context (decl)) DECL_NO_STATIC_CHAIN (decl) = 1; @@ -12053,90 +12051,150 @@ grokparms (first_parm) } -/* D is a constructor or overloaded `operator='. Returns non-zero if - D's arguments allow it to be a copy constructor, or copy assignment +/* D is a constructor or overloaded `operator='. + + Let T be the class in which D is declared. Then, this function + returns: + + -1 if D's is an ill-formed constructor or copy assignment operator + whose first parameter is of type `T'. + 0 if D is not a copy constructor or copy assignment + operator. + 1 if D is a copy constructor or copy assignment operator whose + first parameter is a reference to const qualified T. + 2 if D is a copy constructor or copy assignment operator whose + first parameter is a reference to non-const qualified T. + + This function can be used as a predicate. Positive values indicate + a copy constructor and non-zero values indicate a copy assignment operator. */ int -copy_args_p (d) +copy_fn_p (d) tree d; { - tree t; + tree args; + tree arg_type; + int result = 1; + + my_friendly_assert (DECL_FUNCTION_MEMBER_P (d), 20011208); - if (!DECL_FUNCTION_MEMBER_P (d)) + if (DECL_TEMPLATE_INFO (d) && is_member_template (DECL_TI_TEMPLATE (d))) + /* Instantiations of template member functions are never copy + functions. Note that member functions of templated classes are + represented as template functions internally, and we must + accept those as copy functions. */ + return 0; + + args = FUNCTION_FIRST_USER_PARMTYPE (d); + if (!args) return 0; - t = FUNCTION_FIRST_USER_PARMTYPE (d); - if (t && TREE_CODE (TREE_VALUE (t)) == REFERENCE_TYPE - && (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_VALUE (t))) - == DECL_CONTEXT (d)) - && (TREE_CHAIN (t) == NULL_TREE - || TREE_CHAIN (t) == void_list_node - || TREE_PURPOSE (TREE_CHAIN (t)))) - return 1; - return 0; + arg_type = TREE_VALUE (args); + + if (TYPE_MAIN_VARIANT (arg_type) == DECL_CONTEXT (d)) + { + /* Pass by value copy assignment operator. */ + result = -1; + } + else if (TREE_CODE (arg_type) == REFERENCE_TYPE + && TYPE_MAIN_VARIANT (TREE_TYPE (arg_type)) == DECL_CONTEXT (d)) + { + if (CP_TYPE_CONST_P (TREE_TYPE (arg_type))) + result = 2; + } + else + return 0; + + args = TREE_CHAIN (args); + + if (args && args != void_list_node && !TREE_PURPOSE (args)) + /* There are more non-optional args. */ + return 0; + + return result; } -/* These memoizing functions keep track of special properties which - a class may have. `grok_ctor_properties' notices whether a class - has a constructor of the form X(X&), and also complains - if the class has a constructor of the form X(X). - `grok_op_properties' takes notice of the various forms of - operator= which are defined, as well as what sorts of type conversion - may apply. Both functions take a FUNCTION_DECL as an argument. */ +/* Remember any special properties of member function DECL. */ + +void grok_special_member_properties (decl) + tree decl; +{ + if (!DECL_NONSTATIC_MEMBER_FUNCTION_P(decl)) + ; /* Not special. */ + else if (DECL_CONSTRUCTOR_P (decl)) + { + int ctor = copy_fn_p (decl); + + if (ctor > 0) + { + /* [class.copy] + + A non-template constructor for class X is a copy + constructor if its first parameter is of type X&, const + X&, volatile X& or const volatile X&, and either there + are no other parameters or else all other parameters have + default arguments. */ + TYPE_HAS_INIT_REF (DECL_CONTEXT (decl)) = 1; + if (ctor > 1) + TYPE_HAS_CONST_INIT_REF (DECL_CONTEXT (decl)) = 1; + } + else if (sufficient_parms_p (FUNCTION_FIRST_USER_PARMTYPE (decl))) + TYPE_HAS_DEFAULT_CONSTRUCTOR (DECL_CONTEXT (decl)) = 1; + } + else if (DECL_OVERLOADED_OPERATOR_P (decl) == NOP_EXPR) + { + /* [class.copy] + + A non-template assignment operator for class X is a copy + assignment operator if its parameter is of type X, X&, const + X&, volatile X& or const volatile X&. */ + + int assop = copy_fn_p (decl); + + if (assop) + { + TYPE_HAS_ASSIGN_REF (DECL_CONTEXT (decl)) = 1; + if (assop != 1) + TYPE_HAS_CONST_ASSIGN_REF (DECL_CONTEXT (decl)) = 1; + if (DECL_PURE_VIRTUAL_P (decl)) + TYPE_HAS_ABSTRACT_ASSIGN_REF (DECL_CONTEXT (decl)) = 1; + } + } +} + +/* Check a constructor DECL has the correct form. Complains + if the class has a constructor of the form X(X). */ int grok_ctor_properties (ctype, decl) tree ctype, decl; { - tree parmtypes = FUNCTION_FIRST_USER_PARMTYPE (decl); - tree parmtype = parmtypes ? TREE_VALUE (parmtypes) : void_type_node; + int ctor_parm = copy_fn_p (decl); - /* [class.copy] - - A non-template constructor for class X is a copy constructor if - its first parameter is of type X&, const X&, volatile X& or const - volatile X&, and either there are no other parameters or else all - other parameters have default arguments. */ - if (TREE_CODE (parmtype) == REFERENCE_TYPE - && TYPE_MAIN_VARIANT (TREE_TYPE (parmtype)) == ctype - && sufficient_parms_p (TREE_CHAIN (parmtypes)) - && !(DECL_TEMPLATE_INSTANTIATION (decl) - && is_member_template (DECL_TI_TEMPLATE (decl)))) - { - TYPE_HAS_INIT_REF (ctype) = 1; - if (CP_TYPE_CONST_P (TREE_TYPE (parmtype))) - TYPE_HAS_CONST_INIT_REF (ctype) = 1; - } - /* [class.copy] - - A declaration of a constructor for a class X is ill-formed if its - first parameter is of type (optionally cv-qualified) X and either - there are no other parameters or else all other parameters have - default arguments. - - We *don't* complain about member template instantiations that - have this form, though; they can occur as we try to decide what - constructor to use during overload resolution. Since overload - resolution will never prefer such a constructor to the - non-template copy constructor (which is either explicitly or - implicitly defined), there's no need to worry about their - existence. Theoretically, they should never even be - instantiated, but that's hard to forestall. */ - else if (TYPE_MAIN_VARIANT (parmtype) == ctype - && sufficient_parms_p (TREE_CHAIN (parmtypes)) - && !(DECL_TEMPLATE_INSTANTIATION (decl) - && is_member_template (DECL_TI_TEMPLATE (decl)))) + if (ctor_parm < 0) { + /* [class.copy] + + A declaration of a constructor for a class X is ill-formed if + its first parameter is of type (optionally cv-qualified) X + and either there are no other parameters or else all other + parameters have default arguments. + + We *don't* complain about member template instantiations that + have this form, though; they can occur as we try to decide + what constructor to use during overload resolution. Since + overload resolution will never prefer such a constructor to + the non-template copy constructor (which is either explicitly + or implicitly defined), there's no need to worry about their + existence. Theoretically, they should never even be + instantiated, but that's hard to forestall. */ cp_error ("invalid constructor; you probably meant `%T (const %T&)'", ctype, ctype); SET_IDENTIFIER_ERROR_LOCUS (DECL_NAME (decl), ctype); return 0; } - else if (TREE_CODE (parmtype) == VOID_TYPE - || TREE_PURPOSE (parmtypes) != NULL_TREE) - TYPE_HAS_DEFAULT_CONSTRUCTOR (ctype) = 1; - + return 1; } @@ -12169,9 +12227,9 @@ unary_op_p (code) /* Do a little sanity-checking on how they declared their operator. */ void -grok_op_properties (decl, virtualp, friendp) +grok_op_properties (decl, friendp) tree decl; - int virtualp, friendp; + int friendp; { tree argtypes = TYPE_ARG_TYPES (TREE_TYPE (decl)); tree argtype; @@ -12341,37 +12399,7 @@ grok_op_properties (decl, virtualp, friendp) ref ? "a reference to " : "", what); } } - - if (DECL_ASSIGNMENT_OPERATOR_P (decl) - && operator_code == NOP_EXPR) - { - tree parmtype; - - if (arity != 2 && methodp) - { - cp_error ("`%D' must take exactly one argument", decl); - return; - } - parmtype = TREE_VALUE (TREE_CHAIN (argtypes)); - - /* [class.copy] - - A user-declared copy assignment operator X::operator= is - a non-static non-template member function of class X with - exactly one parameter of type X, X&, const X&, volatile - X& or const volatile X&. */ - if (copy_assignment_arg_p (parmtype, virtualp) - && !(DECL_TEMPLATE_INSTANTIATION (decl) - && is_member_template (DECL_TI_TEMPLATE (decl))) - && ! friendp) - { - TYPE_HAS_ASSIGN_REF (current_class_type) = 1; - if (TREE_CODE (parmtype) != REFERENCE_TYPE - || CP_TYPE_CONST_P (TREE_TYPE (parmtype))) - TYPE_HAS_CONST_ASSIGN_REF (current_class_type) = 1; - } - } - else if (operator_code == COND_EXPR) + if (operator_code == COND_EXPR) { /* 13.4.0.3 */ cp_error ("ISO C++ prohibits overloading operator ?:"); @@ -12507,7 +12535,7 @@ grok_op_properties (decl, virtualp, friendp) && TREE_CODE (TREE_TYPE (TREE_TYPE (decl))) == REFERENCE_TYPE) cp_warning ("`%D' should return by value", decl); - /* 13.4.0.8 */ + /* [over.oper]/8 */ for (; argtypes && argtypes != void_list_node; argtypes = TREE_CHAIN (argtypes)) if (TREE_PURPOSE (argtypes)) @@ -14244,14 +14272,7 @@ start_method (declspecs, declarator, attrlist) fndecl = copy_node (fndecl); TREE_CHAIN (fndecl) = NULL_TREE; } - - if (DECL_CONSTRUCTOR_P (fndecl)) - { - if (! grok_ctor_properties (current_class_type, fndecl)) - return void_type_node; - } - else if (IDENTIFIER_OPNAME_P (DECL_NAME (fndecl))) - grok_op_properties (fndecl, DECL_VIRTUAL_P (fndecl), 0); + grok_special_member_properties (fndecl); } cp_finish_decl (fndecl, NULL_TREE, NULL_TREE, 0); diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c index 737d2959c21..e625d963651 100644 --- a/gcc/cp/decl2.c +++ b/gcc/cp/decl2.c @@ -1645,6 +1645,9 @@ grokfield (declarator, declspecs, init, asmspec_tree, attrlist) SET_DECL_RTL (value, NULL_RTX); SET_DECL_ASSEMBLER_NAME (value, get_identifier (asmspec)); } + if (!DECL_FRIEND_P (value)) + grok_special_member_properties (value); + cp_finish_decl (value, init, asmspec_tree, flags); /* Pass friends back this way. */ @@ -1762,28 +1765,6 @@ grokoptypename (declspecs, declarator) */ -int -copy_assignment_arg_p (parmtype, virtualp) - tree parmtype; - int virtualp ATTRIBUTE_UNUSED; -{ - if (current_class_type == NULL_TREE) - return 0; - - if (TREE_CODE (parmtype) == REFERENCE_TYPE) - parmtype = TREE_TYPE (parmtype); - - if ((TYPE_MAIN_VARIANT (parmtype) == current_class_type) -#if 0 - /* Non-standard hack to support old Booch components. */ - || (! virtualp && DERIVED_FROM_P (parmtype, current_class_type)) -#endif - ) - return 1; - - return 0; -} - static void grok_function_init (decl, init) tree decl; @@ -1796,17 +1777,7 @@ grok_function_init (decl, init) if (TREE_CODE (type) == FUNCTION_TYPE) cp_error ("initializer specified for non-member function `%D'", decl); else if (integer_zerop (init)) - { - DECL_PURE_VIRTUAL_P (decl) = 1; - if (DECL_OVERLOADED_OPERATOR_P (decl) == NOP_EXPR) - { - tree parmtype - = TREE_VALUE (TREE_CHAIN (TYPE_ARG_TYPES (TREE_TYPE (decl)))); - - if (copy_assignment_arg_p (parmtype, 1)) - TYPE_HAS_ABSTRACT_ASSIGN_REF (current_class_type) = 1; - } - } + DECL_PURE_VIRTUAL_P (decl) = 1; else cp_error ("invalid initializer for virtual method `%D'", decl); } diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 8e620bea2c5..48f39518ab4 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -5186,6 +5186,7 @@ instantiate_class_template (type) { tree r = tsubst (t, args, /*complain=*/1, NULL_TREE); set_current_access_from_decl (r); + grok_special_member_properties (r); finish_member_declaration (r); } @@ -5895,10 +5896,10 @@ tsubst_decl (t, args, type) If it isn't, that'll be handled by clone_constructors_and_destructors. */ if (PRIMARY_TEMPLATE_P (gen_tmpl)) - clone_function_decl(r, /*update_method_vec_p=*/0); + clone_function_decl (r, /*update_method_vec_p=*/0); } else if (IDENTIFIER_OPNAME_P (DECL_NAME (r))) - grok_op_properties (r, DECL_VIRTUAL_P (r), DECL_FRIEND_P (r)); + grok_op_properties (r, DECL_FRIEND_P (r)); } break; diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 1320d3cedc9..9c774301cb9 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2001-12-09 Nathan Sidwell + + * g++.dg/other/copy1.C: New test. + 2001-10-08 Aldy Hernandez * gcc.c-torture/execute/builtin-types-compatible-p.c: New. diff --git a/gcc/testsuite/g++.dg/other/copy1.C b/gcc/testsuite/g++.dg/other/copy1.C new file mode 100644 index 00000000000..d02b08fce09 --- /dev/null +++ b/gcc/testsuite/g++.dg/other/copy1.C @@ -0,0 +1,83 @@ +// { dg-do run } + +// Copyright (C) 2000 Free Software Foundation, Inc. +// Contributed by Nathan Sidwell 30 Nov 2001 + +// PR 87 + +int assign = 0; +int ctor = 0; +int assignC = 0; + +struct A { + int i; + + template + void operator=(const T&) const + { + assign = 1; + } + + A () : i (0) {} + + template A (const T &) + { + ctor = 1; + } +}; + +struct B : A +{ +}; + +struct C +{ + int i; + + C (int i_) :i (i_) {} + + template + void operator= (const C &) + { + assignC = 1; + } +}; + + +int main() +{ + const A a; + A b; + B c; + + b = a; + if (assign) + return 5; + + b.i = 100; + c.i = 200; + + a = b; + + if (!assign) + return 1; + if (a.i) + return 2; + + A e (b); + if (ctor) + return 3; + + A d (c); + if (!ctor) + return 4; + + C c0 (0); + C c1 (1); + + c0 = c1; + if (assignC) + return 5; + + return 0; +}