re PR c++/87 (member template assignment operator)

cp:
	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.
testsuite:
	* g++.dg/other/copy1.C: New test.

From-SVN: r47813
This commit is contained in:
Nathan Sidwell 2001-12-09 16:33:44 +00:00 committed by Nathan Sidwell
parent b365613798
commit 271e6f02a1
8 changed files with 254 additions and 148 deletions

View File

@ -1,3 +1,29 @@
2001-12-04 Nathan Sidwell <nathan@codesourcery.com>
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 <aldyh@redhat.com>
* lex.c (rid_to_yy): Add RID_CHOOSE_EXPR and

View File

@ -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

View File

@ -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));

View File

@ -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);

View File

@ -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);
}

View File

@ -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;

View File

@ -1,3 +1,7 @@
2001-12-09 Nathan Sidwell <nathan@codesourcery.com>
* g++.dg/other/copy1.C: New test.
2001-10-08 Aldy Hernandez <aldyh@redhat.com>
* gcc.c-torture/execute/builtin-types-compatible-p.c: New.

View File

@ -0,0 +1,83 @@
// { dg-do run }
// Copyright (C) 2000 Free Software Foundation, Inc.
// Contributed by Nathan Sidwell 30 Nov 2001 <nathan@nathan@codesourcery.com>
// PR 87
int assign = 0;
int ctor = 0;
int assignC = 0;
struct A {
int i;
template<class T>
void operator=(const T&) const
{
assign = 1;
}
A () : i (0) {}
template <typename T> A (const T &)
{
ctor = 1;
}
};
struct B : A
{
};
struct C
{
int i;
C (int i_) :i (i_) {}
template <int I>
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;
}