This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
PATCH to fix some template problems
- To: egcs-patches at cygnus dot com
- Subject: PATCH to fix some template problems
- From: Mark Mitchell <mark at markmitchell dot com>
- Date: Fri, 31 Jul 1998 15:03:41 -0700
- Reply-to: mark at markmitchell dot com
Here's a patch that fixes a couple of problems reported earlier today,
as well as increasing the amount of error-checking we do for template
class declarations slightly.
--
Mark Mitchell mark@markmitchell.com
Mark Mitchell Consulting http://www.markmitchell.com
1998-07-31 Mark Mitchell <mark@markmitchell.com>
* cp-tree.h (PROCESSING_REAL_TEMPLATE_DECL_P): New macro.
(maybe_check_template_type): New function.
* decl.c (maybe_process_template_type_declaration): New function,
split out from pushtag Call maybe_check_template_type.
(pushtag): Use it. Use PROCESSING_REAL_TEMPLATE_DECL_P.
(xref_tag): Use PROCESSING_REAL_TEMPLATE_DECL_P.
* friend.c (do_friend): Use PROCESSING_REAL_TEMPLATE_DECL_P.
* pt.c (template_class_depth_real): Generalization of ...
(template_class_depth): Use it.
(register_specialization): Use duplicate_decls for duplicate
declarations of specializations.
(maybe_check_template_type): New function.
(push_template_decl_real): Fix comment.
(convert_nontype_argument): Likewise.
(lookup_template_class): Likewise. Avoid an infinite loop on
erroneous code.
(tsubst_friend_function): Fix comment.
(tsubst, case FUNCTION_DECL): Deal with a DECL_TI_TEMPLATE that is
an IDENTIFIER_NODE.
* semantics.c (begin_function_definition): Use
reset_specialization to note that template headers don't apply
directly to declarations after the opening curly for a function.
Index: testsuite/g++.old-deja/g++.pt/crash15.C
===================================================================
RCS file: crash15.C
diff -N crash15.C
*** /dev/null Mon Dec 31 20:00:00 1979
--- crash15.C Fri Jul 31 14:45:04 1998
***************
*** 0 ****
--- 1,10 ----
+ // Build don't link:
+
+ template <class T>
+ template <class U>
+ struct A { // ERROR - too many template parameter lists
+ public:
+ A() {}
+
+ A(const A<T>& b) {} // ERROR - invalid use of template
+ };
Index: testsuite/g++.old-deja/g++.pt/explicit34.C
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/testsuite/g++.old-deja/g++.pt/explicit34.C,v
retrieving revision 1.2
diff -c -p -r1.2 explicit34.C
*** explicit34.C 1998/01/20 00:57:36 1.2
--- explicit34.C 1998/07/31 21:45:04
*************** template <class T>
*** 4,10 ****
void foo(T t);
template <>
! void foo(int) {};
template <>
void foo<int>(int) {} // ERROR - duplicate specialization.
--- 4,10 ----
void foo(T t);
template <>
! void foo(int) {}; // ERROR - previously defined here.
template <>
void foo<int>(int) {} // ERROR - duplicate specialization.
Index: testsuite/g++.old-deja/g++.pt/friend28.C
===================================================================
RCS file: friend28.C
diff -N friend28.C
*** /dev/null Mon Dec 31 20:00:00 1979
--- friend28.C Fri Jul 31 14:45:04 1998
***************
*** 0 ****
--- 1,22 ----
+ // Build don't link:
+
+ class mystream;
+
+ template <class T> class a {
+ public:
+ friend mystream& operator>> <>( mystream&, a<T>& thea );
+ private:
+ T amember;
+ };
+
+ template <class T> mystream& operator>>( mystream& s, a<T>& thea );
+
+ template<> mystream& operator>> <int>( mystream& s, a<int>& thea );
+
+ template class a<int>;
+
+ template<> mystream& operator>> <int>( mystream& s, a<int>& thea )
+ {
+ thea.amember = 0;
+ return s;
+ }
Index: testsuite/g++.old-deja/g++.pt/friend29.C
===================================================================
RCS file: friend29.C
diff -N friend29.C
*** /dev/null Mon Dec 31 20:00:00 1979
--- friend29.C Fri Jul 31 14:45:04 1998
***************
*** 0 ****
--- 1,16 ----
+ // Build don't link:
+
+ template <class T> class a {
+ public:
+ friend void foo<>( a<T>& thea );
+ private:
+ T amember;
+ };
+
+ template <class T> void foo( a<T>& thea )
+ {
+ thea.amember = 0;
+ }
+
+ template class a<int>;
+
Index: cp/cp-tree.h
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/cp/cp-tree.h,v
retrieving revision 1.104
diff -c -p -r1.104 cp-tree.h
*** cp-tree.h 1998/07/28 01:02:48 1.104
--- cp-tree.h 1998/07/31 21:43:16
*************** extern int flag_new_for_scope;
*** 1692,1697 ****
--- 1692,1703 ----
#define SET_CLASSTYPE_EXPLICIT_INSTANTIATION(NODE) \
(CLASSTYPE_USE_TEMPLATE(NODE) = 3)
+ /* Non-zero iff we are currently processing a declaration for an
+ entity with its own template parameter list, and which is not a
+ full specialization. */
+ #define PROCESSING_REAL_TEMPLATE_DECL_P() \
+ (processing_template_decl > template_class_depth (current_class_type))
+
/* This function may be a guiding decl for a template. */
#define DECL_MAYBE_TEMPLATE(NODE) DECL_LANG_FLAG_4 (NODE)
/* We know what we're doing with this decl now. */
*************** extern int template_class_depth
*** 2794,2799 ****
--- 2800,2806 ----
extern int is_specialization_of PROTO((tree, tree));
extern int comp_template_args PROTO((tree, tree));
extern void maybe_process_partial_specialization PROTO((tree));
+ extern void maybe_check_template_type PROTO((tree));
extern int processing_specialization;
extern int processing_explicit_instantiation;
Index: cp/decl.c
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/cp/decl.c,v
retrieving revision 1.172
diff -c -p -r1.172 decl.c
*** decl.c 1998/07/29 14:56:33 1.172
--- decl.c 1998/07/31 21:43:58
*************** static int member_function_or_else PROTO
*** 176,181 ****
--- 176,182 ----
static void bad_specifiers PROTO((tree, char *, int, int, int, int,
int));
static void lang_print_error_function PROTO((char *));
+ static tree maybe_process_template_type_declaration PROTO((tree, int, struct binding_level*));
#if defined (DEBUG_CP_BINDING_LEVELS)
static void indent PROTO((void));
*************** pop_everything ()
*** 2223,2228 ****
--- 2224,2311 ----
#endif
}
+ /* The type TYPE is being declared. If it is a class template, or a
+ specialization of a class template, do any processing required and
+ perform error-checking. If IS_FRIEND is non-zero, this TYPE is
+ being declared a friend. B is the binding level at which this TYPE
+ should be bound.
+
+ Returns the TYPE_DECL for TYPE, which may have been altered by this
+ processing. */
+
+ static tree
+ maybe_process_template_type_declaration (type, globalize, b)
+ tree type;
+ int globalize;
+ struct binding_level* b;
+ {
+ tree decl = TYPE_NAME (type);
+
+ if (processing_template_parmlist)
+ /* You can't declare a new template type in a template parameter
+ list. But, you can declare a non-template type:
+
+ template <class A*> struct S;
+
+ is a forward-declaration of `A'. */
+ ;
+ else
+ {
+ maybe_check_template_type (type);
+
+ if (IS_AGGR_TYPE (type)
+ && (/* If !GLOBALIZE then we are looking at a definition.
+ It may not be a primary template. (For example, in:
+
+ template <class T>
+ struct S1 { class S2 {}; }
+
+ we have to push_template_decl for S2.) */
+ (processing_template_decl && !globalize)
+ /* If we are declaring a friend template class, we will
+ have GLOBALIZE set, since something like:
+
+ template <class T>
+ struct S1 {
+ template <class U>
+ friend class S2;
+ };
+
+ declares S2 to be at global scope. */
+ || PROCESSING_REAL_TEMPLATE_DECL_P ()))
+ {
+ /* This may change after the call to
+ push_template_decl_real, but we want the original value. */
+ tree name = DECL_NAME (decl);
+
+ decl = push_template_decl_real (decl, globalize);
+ /* If the current binding level is the binding level for the
+ template parameters (see the comment in
+ begin_template_parm_list) and the enclosing level is a class
+ scope, and we're not looking at a friend, push the
+ declaration of the member class into the class scope. In the
+ friend case, push_template_decl will already have put the
+ friend into global scope, if appropriate. */
+ if (!globalize && b->pseudo_global
+ && b->level_chain->parm_flag == 2)
+ {
+ pushdecl_with_scope (CLASSTYPE_TI_TEMPLATE (type),
+ b->level_chain);
+ /* Put this tag on the list of tags for the class, since
+ that won't happen below because B is not the class
+ binding level, but is instead the pseudo-global level. */
+ b->level_chain->tags =
+ saveable_tree_cons (name, type, b->level_chain->tags);
+ TREE_NONLOCAL_FLAG (type) = 1;
+ if (TYPE_SIZE (current_class_type) == NULL_TREE)
+ CLASSTYPE_TAGS (current_class_type) = b->level_chain->tags;
+ }
+ }
+ }
+
+ return decl;
+ }
+
/* Push a tag name NAME for struct/class/union/enum type TYPE.
Normally put it into the inner-most non-tag-transparent scope,
but if GLOBALIZE is true, put it in the inner-most non-class scope.
*************** pushtag (name, type, globalize)
*** 2297,2360 ****
TYPE_NAME (type) = d;
DECL_CONTEXT (d) = FROB_CONTEXT (context);
-
- if (processing_template_parmlist)
- /* You can't declare a new template type in a template
- parameter list. But, you can declare a non-template
- type:
-
- template <class A*> struct S;
-
- is a forward-declaration of `A'. */
- ;
- else if (IS_AGGR_TYPE (type)
- && (/* If !GLOBALIZE then we are looking at a
- definition. It may not be a primary template.
- (For example, in:
-
- template <class T>
- struct S1 { class S2 {}; }
-
- we have to push_template_decl for S2.) */
- (processing_template_decl && !globalize)
- /* If we are declaring a friend template class, we
- will have GLOBALIZE set, since something like:
-
- template <class T>
- struct S1 {
- template <class U>
- friend class S2;
- };
! declares S2 to be at global scope. */
! || (processing_template_decl >
! template_class_depth (current_class_type))))
! {
! d = push_template_decl_real (d, globalize);
! /* If the current binding level is the binding level for
! the template parameters (see the comment in
! begin_template_parm_list) and the enclosing level is
! a class scope, and we're not looking at a friend,
! push the declaration of the member class into the
! class scope. In the friend case, push_template_decl
! will already have put the friend into global scope,
! if appropriate. */
! if (!globalize && b->pseudo_global
! && b->level_chain->parm_flag == 2)
! {
! pushdecl_with_scope (CLASSTYPE_TI_TEMPLATE (type),
! b->level_chain);
! /* Put this tag on the list of tags for the class,
! since that won't happen below because B is not
! the class binding level, but is instead the
! pseudo-global level. */
! b->level_chain->tags =
! saveable_tree_cons (name, type, b->level_chain->tags);
! TREE_NONLOCAL_FLAG (type) = 1;
! if (TYPE_SIZE (current_class_type) == NULL_TREE)
! CLASSTYPE_TAGS (current_class_type) = b->level_chain->tags;
! }
! }
if (b->parm_flag == 2)
d = pushdecl_class_level (d);
--- 2380,2388 ----
TYPE_NAME (type) = d;
DECL_CONTEXT (d) = FROB_CONTEXT (context);
! d = maybe_process_template_type_declaration (type,
! globalize, b);
if (b->parm_flag == 2)
d = pushdecl_class_level (d);
*************** xref_tag (code_type_node, name, binfo, g
*** 11349,11356 ****
{
if (current_class_type
&& template_class_depth (current_class_type)
! && (processing_template_decl
! > template_class_depth (current_class_type)))
/* Since GLOBALIZE is non-zero, we are not looking at a
definition of this tag. Since, in addition, we are currently
processing a (member) template declaration of a template
--- 11387,11393 ----
{
if (current_class_type
&& template_class_depth (current_class_type)
! && PROCESSING_REAL_TEMPLATE_DECL_P ())
/* Since GLOBALIZE is non-zero, we are not looking at a
definition of this tag. Since, in addition, we are currently
processing a (member) template declaration of a template
Index: cp/friend.c
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/cp/friend.c,v
retrieving revision 1.23
diff -c -p -r1.23 friend.c
*** friend.c 1998/07/28 16:50:12 1.23
--- friend.c 1998/07/31 21:44:00
*************** do_friend (ctype, declarator, decl, parm
*** 354,361 ****
}
if (TREE_CODE (decl) == FUNCTION_DECL)
! is_friend_template = processing_template_decl >
! template_class_depth (current_class_type);
if (ctype)
{
--- 354,360 ----
}
if (TREE_CODE (decl) == FUNCTION_DECL)
! is_friend_template = PROCESSING_REAL_TEMPLATE_DECL_P ();
if (ctype)
{
Index: cp/pt.c
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/cp/pt.c,v
retrieving revision 1.170
diff -c -p -r1.170 pt.c
*** pt.c 1998/07/28 01:02:58 1.170
--- pt.c 1998/07/31 21:44:56
*************** static tree most_specialized PROTO((tree
*** 121,126 ****
--- 121,127 ----
static tree most_specialized_class PROTO((tree, tree));
static tree most_general_template PROTO((tree));
static void set_mangled_name_for_template_decl PROTO((tree));
+ static int template_class_depth_real PROTO((tree, int));
/* We use TREE_VECs to hold template arguments. If there is only one
level of template arguments, then the TREE_VEC contains the
*************** finish_member_template_decl (template_pa
*** 234,245 ****
struct B {};
};
! A<T>::B<U> has depth two, while A<T> has depth one. Also,
! both A<T>::B<int> and A<int>::B<U> have depth one. */
int
! template_class_depth (type)
tree type;
{
int depth;
--- 235,253 ----
struct B {};
};
! A<T>::B<U> has depth two, while A<T> has depth one.
! Both A<T>::B<int> and A<int>::B<U> have depth one, if
! COUNT_SPECIALIZATIONS is 0 or if they are instantiations, not
! specializations.
!
! This function is guaranteed to return 0 if passed NULL_TREE so
! that, for example, `template_class_depth (current_class_type)' is
! always safe. */
int
! template_class_depth_real (type, count_specializations)
tree type;
+ int count_specializations;
{
int depth;
*************** template_class_depth (type)
*** 249,260 ****
type = TYPE_CONTEXT (type))
if (CLASSTYPE_TEMPLATE_INFO (type)
&& PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (type))
! && uses_template_parms (CLASSTYPE_TI_ARGS (type)))
++depth;
return depth;
}
/* Returns 1 if processing DECL as part of do_pending_inlines
needs us to push template parms. */
--- 257,281 ----
type = TYPE_CONTEXT (type))
if (CLASSTYPE_TEMPLATE_INFO (type)
&& PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (type))
! && ((count_specializations
! && CLASSTYPE_TEMPLATE_SPECIALIZATION (type))
! || uses_template_parms (CLASSTYPE_TI_ARGS (type))))
++depth;
return depth;
}
+ /* Returns the template nesting level of the indicated class TYPE.
+ Like template_class_depth_real, but instantiations do not count in
+ the depth. */
+
+ int
+ template_class_depth (type)
+ tree type;
+ {
+ return template_class_depth_real (type, /*count_specializations=*/0);
+ }
+
/* Returns 1 if processing DECL as part of do_pending_inlines
needs us to push template parms. */
*************** register_specialization (spec, tmpl, arg
*** 742,752 ****
}
else if (DECL_TEMPLATE_SPECIALIZATION (fn))
{
! if (DECL_INITIAL (fn))
! cp_error ("duplicate specialization of %D", fn);
!
! TREE_VALUE (s) = spec;
! return spec;
}
}
}
--- 763,770 ----
}
else if (DECL_TEMPLATE_SPECIALIZATION (fn))
{
! duplicate_decls (spec, TREE_VALUE (s));
! return TREE_VALUE (s);
}
}
}
*************** check_explicit_specialization (declarato
*** 1300,1305 ****
--- 1318,1370 ----
return decl;
}
+ /* TYPE is being declared. Verify that the use of template headers
+ and such is reasonable. Issue error messages if not. */
+
+ void
+ maybe_check_template_type (type)
+ tree type;
+ {
+ if (template_header_count)
+ {
+ /* We are in the scope of some `template <...>' header. */
+
+ int context_depth
+ = template_class_depth_real (TYPE_CONTEXT (type),
+ /*count_specializations=*/1);
+
+ if (template_header_count <= context_depth)
+ /* This is OK; the template headers are for the context. We
+ are actually too lenient here; like
+ check_explicit_specialization we should consider the number
+ of template types included in the actual declaration. For
+ example,
+
+ template <class T> struct S {
+ template <class U> template <class V>
+ struct I {};
+ };
+
+ is illegal, but:
+
+ template <class T> struct S {
+ template <class U> struct I;
+ };
+
+ template <class T> template <class U.
+ struct S<T>::I {};
+
+ is not. */
+ ;
+ else if (template_header_count > context_depth + 1)
+ /* There are two many template parameter lists. */
+ cp_error ("too many template parameter lists in declaration of `%T'", type);
+
+ else if (TREE_CODE (type) == ENUMERAL_TYPE)
+ cp_error ("template declaration of `%#T'", type);
+ }
+ }
+
/* Returns 1 iff PARMS1 and PARMS2 are identical sets of template
parameters. These are represented in the same format used for
DECL_TEMPLATE_PARMS. */
*************** push_template_decl_real (decl, is_friend
*** 1951,1959 ****
/* Push template declarations for global functions and types. Note
that we do not try to push a global template friend declared in a
template class; such a thing may well depend on the template
! parameters of the class. With guiding declarations, however, we
! push the template so that subsequent declarations of the template
! will match this one. */
if (! ctx
&& !(is_friend && template_class_depth (current_class_type) > 0))
tmpl = pushdecl_namespace_level (tmpl);
--- 2016,2022 ----
/* Push template declarations for global functions and types. Note
that we do not try to push a global template friend declared in a
template class; such a thing may well depend on the template
! parameters of the class. */
if (! ctx
&& !(is_friend && template_class_depth (current_class_type) > 0))
tmpl = pushdecl_namespace_level (tmpl);
*************** convert_nontype_argument (type, expr)
*** 2278,2287 ****
if (TREE_CODE (type_referred_to) == FUNCTION_TYPE)
{
/* For a non-type template-parameter of type reference to
! function, no conversions apply. If the
! template-argument represents a set of overloaded
! functions, the matching function is selected from the
! set (_over.over_). */
tree fns = expr;
tree fn;
--- 2341,2350 ----
if (TREE_CODE (type_referred_to) == FUNCTION_TYPE)
{
/* For a non-type template-parameter of type reference to
! function, no conversions apply. If the
! template-argument represents a set of overloaded
! functions, the matching function is selected from the
! set (_over.over_). */
tree fns = expr;
tree fn;
*************** lookup_template_class (d1, arglist, in_d
*** 3096,3102 ****
if (arg_depth == 1 && parm_depth > 1)
{
! /* We've been with an incomplete set of template arguments.
For example, given:
template <class T> struct S1 {
--- 3159,3165 ----
if (arg_depth == 1 && parm_depth > 1)
{
! /* We've been given an incomplete set of template arguments.
For example, given:
template <class T> struct S1 {
*************** lookup_template_class (d1, arglist, in_d
*** 3109,3116 ****
<class U> struct S1<T>::S2'. We must fill in the missing
arguments. */
my_friendly_assert (context != NULL_TREE, 0);
! while (!IS_AGGR_TYPE_CODE (TREE_CODE (context)))
context = DECL_REAL_CONTEXT (context);
arglist = add_to_template_args (CLASSTYPE_TI_ARGS (context),
arglist);
arg_depth = TMPL_ARGS_DEPTH (arglist);
--- 3172,3199 ----
<class U> struct S1<T>::S2'. We must fill in the missing
arguments. */
my_friendly_assert (context != NULL_TREE, 0);
! while (!IS_AGGR_TYPE_CODE (TREE_CODE (context))
! && context != global_namespace)
context = DECL_REAL_CONTEXT (context);
+
+ if (context == global_namespace)
+ /* This is bad. We cannot get enough arguments, even from
+ the surrounding context, to resolve this class. One
+ case where this might happen is (illegal) code like:
+
+ template <class U>
+ template <class T>
+ struct S {
+ A(const A<T>& a) {}
+ };
+
+ We should catch this error sooner (at the opening curly
+ for `S', but it is better to be safe than sorry here. */
+ {
+ cp_error ("invalid use of `%D'", template);
+ return error_mark_node;
+ }
+
arglist = add_to_template_args (CLASSTYPE_TI_ARGS (context),
arglist);
arg_depth = TMPL_ARGS_DEPTH (arglist);
*************** tsubst_friend_function (decl, args)
*** 3667,3674 ****
args, NULL_TREE),
tsubst (DECL_TI_ARGS (decl),
args, NULL_TREE));
! /* FIXME: The decl we create via the next tsubst be created on a
! temporary obstack. */
new_friend = tsubst (decl, args, NULL_TREE);
tmpl = determine_specialization (template_id, new_friend,
&new_args,
--- 3750,3757 ----
args, NULL_TREE),
tsubst (DECL_TI_ARGS (decl),
args, NULL_TREE));
! /* FIXME: The decl we create via the next tsubst could be
! created on a temporary obstack. */
new_friend = tsubst (decl, args, NULL_TREE);
tmpl = determine_specialization (template_id, new_friend,
&new_args,
*************** tsubst (t, args, in_decl)
*** 4833,4844 ****
};
Here, the DECL_TI_TEMPLATE for the friend declaration
! will be a LOOKUP_EXPR. We are being called from
! tsubst_friend_function, and we want only to create a
! new decl (R) with appropriate types so that we can call
! determine_specialization. */
! my_friendly_assert (TREE_CODE (DECL_TI_TEMPLATE (t))
! == LOOKUP_EXPR, 0);
gen_tmpl = NULL_TREE;
}
--- 4916,4929 ----
};
Here, the DECL_TI_TEMPLATE for the friend declaration
! will be a LOOKUP_EXPR or an IDENTIFIER_NODE. We are
! being called from tsubst_friend_function, and we want
! only to create a new decl (R) with appropriate types so
! that we can call determine_specialization. */
! my_friendly_assert ((TREE_CODE (DECL_TI_TEMPLATE (t))
! == LOOKUP_EXPR)
! || (TREE_CODE (DECL_TI_TEMPLATE (t))
! == IDENTIFIER_NODE), 0);
gen_tmpl = NULL_TREE;
}
Index: cp/semantics.c
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/cp/semantics.c,v
retrieving revision 1.21
diff -c -p -r1.21 semantics.c
*** semantics.c 1998/07/28 01:03:00 1.21
--- semantics.c 1998/07/31 21:45:00
*************** begin_function_definition (decl_specs, d
*** 1098,1103 ****
--- 1094,1103 ----
return 0;
reinit_parse_for_function ();
+ /* The things we're about to see are not directly qualified by any
+ template headers we've seen thus far. */
+ reset_specialization ();
+
return 1;
}