This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[C++ PATCH] Fix bug 775
- From: Nathan Sidwell <nathan at codesourcery dot com>
- To: gcc-patches at gcc dot gnu dot org
- Cc: mark at codesourcery dot com
- Date: Sat, 29 Dec 2001 12:22:20 +0000
- Subject: [C++ PATCH] Fix bug 775
- Organization: Codesourcery LLC
Hi,
this fixes bug 775. It is rather more parser oriented than I would have
liked, but that's the way it went. It is also a prerequisite to at least
one other upcoming patch (2023). I suspect some other bugs are fixed
by this too.
parse.y has one way of handling class-head with a qualified id, and
another for unqualified-ids. The unqualified id path DTRT for declarations
and definitions, because it tells them appart. The qualified-id path
does not distinguish, leading to problems with 'friend class qualified-id'.
This rearranges the grammar into more obvious (IMO) non-terminals, which
delay the processing in handle_class_head, until it is known whether a
declaration or definition is happening. Also both qualified and unqualified
ids go through handle_class_head.
No new shift/reduce or reduce/reduce conflicts.
built & tested on i686-pc-linux-gnu, ok?
nathan
--
Dr Nathan Sidwell :: http://www.codesourcery.com :: CodeSourcery LLC
'But that's a lie.' - 'Yes it is. What's your point?'
nathan@codesourcery.com : http://www.cs.bris.ac.uk/~nathan/ : nathan@acm.org
2001-12-28 Nathan Sidwell <nathan@codesourcery.com>
PR c++/775
* cp-tree.h (handle_class_head): Adjust prototype.
* decl2.c (handle_class_head): Add DEFN_P and NEW_TYPE_P
parameters. Use for all class heads.
* parse.y (named_class_head_sans_basetype, named_class_head,
named_complex_class_head_sans_basetype,
named_class_head_sans_basetype_defn,
unnamed_class_head): Remove.
(class_head, class_head_apparent_template): Recognize class heads
(class_head_decl, class_head_defn): New reductions. Process class
heads.
(structsp): Adjust class definition and class declaration
reductions.
(maybe_base_class_list): Give diagnostic on empty list.
Index: cp/cp-tree.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/cp-tree.h,v
retrieving revision 1.667
diff -c -3 -p -r1.667 cp-tree.h
*** cp-tree.h 2001/12/18 03:35:25 1.667
--- cp-tree.h 2001/12/28 23:58:24
*************** extern tree do_class_using_decl PARAMS
*** 3769,3775 ****
extern void do_using_directive PARAMS ((tree));
extern void check_default_args PARAMS ((tree));
extern void mark_used PARAMS ((tree));
! extern tree handle_class_head PARAMS ((tree, tree, tree));
extern tree lookup_arg_dependent PARAMS ((tree, tree, tree));
extern void finish_static_data_member_decl PARAMS ((tree, tree, tree, int));
extern tree build_artificial_parm PARAMS ((tree, tree));
--- 3769,3775 ----
extern void do_using_directive PARAMS ((tree));
extern void check_default_args PARAMS ((tree));
extern void mark_used PARAMS ((tree));
! extern tree handle_class_head PARAMS ((tree, tree, tree, int, int *));
extern tree lookup_arg_dependent PARAMS ((tree, tree, tree));
extern void finish_static_data_member_decl PARAMS ((tree, tree, tree, int));
extern tree build_artificial_parm PARAMS ((tree, tree));
Index: cp/decl2.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/decl2.c,v
retrieving revision 1.507
diff -c -3 -p -r1.507 decl2.c
*** decl2.c 2001/12/23 16:07:09 1.507
--- decl2.c 2001/12/28 23:59:15
*************** mark_used (decl)
*** 5158,5227 ****
instantiate_decl (decl, /*defer_ok=*/1);
}
! /* Helper function for named_class_head_sans_basetype nonterminal. We
! have just seen something of the form `AGGR SCOPE::ID'. Return a
! TYPE_DECL for the type declared by ID in SCOPE. */
tree
! handle_class_head (aggr, scope, id)
tree aggr, scope, id;
{
tree decl = NULL_TREE;
!
! if (TREE_CODE (id) == TYPE_DECL)
! /* We must bash typedefs back to the main decl of the type. Otherwise
! we become confused about scopes. */
! decl = TYPE_MAIN_DECL (TREE_TYPE (id));
! else if (DECL_CLASS_TEMPLATE_P (id))
! decl = DECL_TEMPLATE_RESULT (id);
! else
! {
! tree current = current_scope ();
! if (current == NULL_TREE)
! current = current_namespace;
! if (scope == NULL_TREE)
! scope = global_namespace;
! if (TYPE_P (scope))
{
! /* According to the suggested resolution of core issue 180,
! 'typename' is assumed after a class-key. */
! decl = make_typename_type (scope, id, 1);
! if (decl != error_mark_node)
! decl = TYPE_MAIN_DECL (decl);
else
! decl = NULL_TREE;
}
- else if (scope == current)
- {
- /* We've been given AGGR SCOPE::ID, when we're already inside SCOPE.
- Be nice about it. */
- if (pedantic)
- pedwarn ("extra qualification `%T::' on member `%D' ignored",
- FROB_CONTEXT (scope), id);
- }
- else if (scope != global_namespace)
- error ("`%T' does not have a nested type named `%D'", scope, id);
- else
- error ("no file-scope type named `%D'", id);
-
- /* Inject it at the current scope. */
- if (! decl)
- decl = TYPE_MAIN_DECL (xref_tag (aggr, id, 1));
}
!
! /* Enter the SCOPE. If this turns out not to be a definition, the
! parser must leave the scope. */
! push_scope (CP_DECL_CONTEXT (decl));
! /* If we see something like:
! template <typename T> struct S::I ....
!
! we must create a TEMPLATE_DECL for the nested type. */
! if (PROCESSING_REAL_TEMPLATE_DECL_P ())
! decl = push_template_decl (decl);
return decl;
}
--- 5158,5247 ----
instantiate_decl (decl, /*defer_ok=*/1);
}
! /* Helper function for class_head_decl and class_head_defn
! nonterminals. AGGR is the class, union or struct tag. SCOPE is the
! explicit scope used (NULL for no scope resolution). ID is the
! name. DEFN_P is true, if this is a definition of the class and
! NEW_TYPE_P is set to non-zero, if we push into the scope containing
! the to be defined aggregate.
!
! Return a TYPE_DECL for the type declared by ID in SCOPE. */
tree
! handle_class_head (aggr, scope, id, defn_p, new_type_p)
tree aggr, scope, id;
+ int defn_p;
+ int *new_type_p;
{
tree decl = NULL_TREE;
! tree current = current_scope ();
! bool xrefd_p = false;
! if (current == NULL_TREE)
! current = current_namespace;
! *new_type_p = 0;
!
! if (scope)
! {
! if (TREE_CODE (id) == TYPE_DECL)
! /* We must bash typedefs back to the main decl of the
! type. Otherwise we become confused about scopes. */
! decl = TYPE_MAIN_DECL (TREE_TYPE (id));
! else if (DECL_CLASS_TEMPLATE_P (id))
! decl = DECL_TEMPLATE_RESULT (id);
! else
{
! if (TYPE_P (scope))
! {
! /* According to the suggested resolution of core issue
! 180, 'typename' is assumed after a class-key. */
! decl = make_typename_type (scope, id, 1);
! if (decl != error_mark_node)
! decl = TYPE_MAIN_DECL (decl);
! else
! decl = NULL_TREE;
! }
! else if (scope == current)
! {
! /* We've been given AGGR SCOPE::ID, when we're already
! inside SCOPE. Be nice about it. */
! if (pedantic)
! pedwarn ("extra qualification `%T::' on member `%D' ignored",
! scope, id);
! }
else
! error ("`%T' does not have a class or union named `%D'",
! scope, id);
}
}
!
! if (!decl)
! {
! decl = TYPE_MAIN_DECL (xref_tag (aggr, id, !defn_p));
! xrefd_p = true;
! }
! if (!TYPE_BINFO (TREE_TYPE (decl)))
! {
! error ("`%T' is not a class or union type", decl);
! return error_mark_node;
! }
!
! if (defn_p)
! {
! /* For a definition, we want to enter the containing scope
! before looking up any base classes etc. Only do so, if this
! is different to the current scope. */
! tree context = CP_DECL_CONTEXT (decl);
! *new_type_p = current != context;
! if (*new_type_p)
! push_scope (context);
!
! if (!xrefd_p && PROCESSING_REAL_TEMPLATE_DECL_P ())
! decl = push_template_decl (decl);
! }
return decl;
}
Index: cp/parse.y
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/parse.y,v
retrieving revision 1.239
diff -c -3 -p -r1.239 parse.y
*** parse.y 2001/12/26 20:33:36 1.239
--- parse.y 2001/12/28 23:59:49
*************** cp_parse_init ()
*** 386,395 ****
%type <ttype> component_constructor_declarator
%type <ttype> fn.def2 return_id constructor_declarator
%type <ttype> .begin_function_body
! %type <ttype> named_class_head_sans_basetype
! %type <ftype> class_head named_class_head
! %type <ftype> named_complex_class_head_sans_basetype
! %type <ttype> unnamed_class_head
%type <ttype> base_class_list
%type <ttype> base_class_access_list
%type <ttype> base_class maybe_base_class_list base_class.1
--- 386,393 ----
%type <ttype> component_constructor_declarator
%type <ttype> fn.def2 return_id constructor_declarator
%type <ttype> .begin_function_body
! %type <ttype> class_head class_head_apparent_template
! %type <ftype> class_head_decl class_head_defn
%type <ttype> base_class_list
%type <ttype> base_class_access_list
%type <ttype> base_class maybe_base_class_list base_class.1
*************** cp_parse_init ()
*** 418,424 ****
%type <ttype> explicit_template_type
/* in order to recognize aggr tags as defining and thus shadowing. */
%token TYPENAME_DEFN IDENTIFIER_DEFN PTYPENAME_DEFN
- %type <ttype> named_class_head_sans_basetype_defn
%type <ttype> identifier_defn IDENTIFIER_DEFN TYPENAME_DEFN PTYPENAME_DEFN
%type <ttype> handler_args
%type <ttype> self_template_type .finish_template_type
--- 416,421 ----
*************** structsp:
*** 2277,2284 ****
if (!processing_template_decl)
pedwarn ("using `typename' outside of template"); }
/* C++ extensions, merged with C to avoid shift/reduce conflicts */
! | class_head '{'
! { $1.t = begin_class_definition ($1.t);
current_aggr = NULL_TREE; }
opt.component_decl_list '}' maybe_attribute
{
--- 2274,2294 ----
if (!processing_template_decl)
pedwarn ("using `typename' outside of template"); }
/* C++ extensions, merged with C to avoid shift/reduce conflicts */
! | class_head_defn maybe_base_class_list '{'
! {
! if ($2 && $1.t != error_mark_node)
! {
! tree type = TREE_TYPE ($1.t);
!
! if (TREE_CODE (type) == TYPENAME_TYPE)
! /* In a definition of a member class template,
! we will get here with an implicit typename,
! a TYPENAME_TYPE with a type. */
! type = TREE_TYPE (type);
! maybe_process_partial_specialization (type);
! xref_basetypes (current_aggr, $1.t, type, $2);
! }
! $1.t = begin_class_definition (TREE_TYPE ($1.t));
current_aggr = NULL_TREE; }
opt.component_decl_list '}' maybe_attribute
{
*************** structsp:
*** 2289,2296 ****
yychar = YYLEX;
semi = yychar == ';';
! t = finish_class_definition ($1.t, $6, semi,
! $1.new_type_flag);
$<ttype>$ = t;
/* restore current_aggr */
--- 2299,2305 ----
yychar = YYLEX;
semi = yychar == ';';
! t = finish_class_definition ($1.t, $7, semi, $1.new_type_flag);
$<ttype>$ = t;
/* restore current_aggr */
*************** structsp:
*** 2307,2338 ****
pending_inlines
{
finish_inline_definitions ();
! $$.t = $<ttype>7;
$$.new_type_flag = 1;
}
! | class_head %prec EMPTY
{
! if ($1.new_type_flag && $1.t != error_mark_node)
! pop_scope (CP_DECL_CONTEXT (TYPE_MAIN_DECL ($1.t)));
! $$.new_type_flag = 0;
! if ($1.t == error_mark_node)
! $$.t = $1.t;
! else if (TYPE_BINFO ($1.t) == NULL_TREE)
! {
! error ("%T is not a class type", $1.t);
! $$.t = error_mark_node;
! }
! else
! {
! $$.t = $1.t;
! /* struct B: public A; is not accepted by the standard grammar. */
! if (CLASS_TYPE_P ($$.t)
! && TYPE_BINFO_BASETYPES ($$.t)
! && !COMPLETE_TYPE_P ($$.t)
! && ! TYPE_BEING_DEFINED ($$.t))
! error ("base clause without member specification for `%#T'",
! $$.t);
! }
}
;
--- 2316,2328 ----
pending_inlines
{
finish_inline_definitions ();
! $$.t = $<ttype>8;
$$.new_type_flag = 1;
}
! | class_head_decl
{
! $$.t = TREE_TYPE ($1.t);
! $$.new_type_flag = $1.new_type_flag;
}
;
*************** aggr:
*** 2362,2501 ****
{ $$ = build_tree_list ($2, $1); }
;
! named_class_head_sans_basetype:
aggr identifier
! {
! current_aggr = $1;
! $$ = $2;
}
! ;
!
! named_class_head_sans_basetype_defn:
! aggr identifier_defn %prec EMPTY
! { current_aggr = $$; $$ = $2; }
! | named_class_head_sans_basetype '{'
! { yyungetc ('{', 1); }
! | named_class_head_sans_basetype ':'
! { yyungetc (':', 1); }
! ;
!
! named_complex_class_head_sans_basetype:
! aggr nested_name_specifier identifier
{
current_aggr = $1;
! $$.t = handle_class_head ($1, $2, $3);
! $$.new_type_flag = 1;
}
| aggr global_scope nested_name_specifier identifier
{
current_aggr = $1;
! $$.t = handle_class_head ($1, $3, $4);
! $$.new_type_flag = 1;
}
| aggr global_scope identifier
{
current_aggr = $1;
! $$.t = handle_class_head ($1, NULL_TREE, $3);
! $$.new_type_flag = 1;
}
! | aggr apparent_template_type
{
current_aggr = $1;
! $$.t = $2;
! $$.new_type_flag = 0;
}
| aggr nested_name_specifier apparent_template_type
{
current_aggr = $1;
! $$.t = $3;
! push_scope (CP_DECL_CONTEXT ($$.t));
! $$.new_type_flag = 1;
}
| aggr global_scope nested_name_specifier apparent_template_type
{
current_aggr = $1;
! $$.t = $4;
! push_scope (CP_DECL_CONTEXT ($$.t));
! $$.new_type_flag = 1;
}
;
! named_class_head:
! named_class_head_sans_basetype %prec EMPTY
! {
! $$.t = xref_tag (current_aggr, $1, 1);
! $$.new_type_flag = 0;
}
! | named_class_head_sans_basetype_defn
! { $<ttype>$ = xref_tag (current_aggr, $1, 0); }
! /* Class name is unqualified, so we look for base classes
! in the current scope. */
! maybe_base_class_list %prec EMPTY
! {
! $$.t = $<ttype>2;
! $$.new_type_flag = 0;
! if ($3)
! xref_basetypes (current_aggr, $1, $<ttype>2, $3);
}
! | named_complex_class_head_sans_basetype
! maybe_base_class_list
! {
! if ($1.t != error_mark_node)
! {
! tree type = TREE_TYPE ($1.t);
!
! $$.t = type;
! $$.new_type_flag = $1.new_type_flag;
! if ((current_aggr == union_type_node)
! != (TREE_CODE (type) == UNION_TYPE))
! pedwarn (current_aggr == union_type_node
! ? "`union' tag used in declaring `%#T'"
! : "non-`union' tag used in declaring `%#T'",
! type);
! else if (TREE_CODE (type) == RECORD_TYPE)
! /* We might be specializing a template with a different
! class-key; deal. */
! CLASSTYPE_DECLARED_CLASS (type)
! = (current_aggr == class_type_node);
! if ($2)
! {
! if (TREE_CODE (type) == TYPENAME_TYPE)
! /* In a definition of a member class template, we
! will get here with an implicit typename, a
! TYPENAME_TYPE with a type. */
! type = TREE_TYPE (type);
! maybe_process_partial_specialization (type);
! xref_basetypes (current_aggr, $1.t, type, $2);
! }
! }
}
;
-
- unnamed_class_head:
- aggr '{'
- { $$ = xref_tag ($$, make_anon_name (), 0);
- yyungetc ('{', 1); }
- ;
! /* The tree output of this nonterminal a declarationf or the type
! named. If NEW_TYPE_FLAG is set, then the name used in this
! class-head was explicitly qualified, e.g.: `struct X::Y'. We have
! already called push_scope for X. */
! class_head:
! unnamed_class_head
! {
$$.t = $1;
$$.new_type_flag = 0;
}
- | named_class_head
;
maybe_base_class_list:
! /* empty */ %prec EMPTY
{ $$ = NULL_TREE; }
! | ':' see_typename %prec EMPTY
! { yyungetc(':', 1); $$ = NULL_TREE; }
! | ':' see_typename base_class_list %prec EMPTY
{ $$ = $3; }
;
--- 2352,2477 ----
{ $$ = build_tree_list ($2, $1); }
;
! class_head:
aggr identifier
! {
! current_aggr = $1;
! $$ = build_tree_list (NULL_TREE, $2);
}
! | aggr nested_name_specifier identifier
{
current_aggr = $1;
! $$ = build_tree_list ($2, $3);
}
| aggr global_scope nested_name_specifier identifier
{
current_aggr = $1;
! $$ = build_tree_list ($3, $4);
}
| aggr global_scope identifier
{
current_aggr = $1;
! $$ = build_tree_list (global_namespace, $3);
}
! ;
!
! class_head_apparent_template:
! aggr apparent_template_type
{
current_aggr = $1;
! $$ = $2;
}
| aggr nested_name_specifier apparent_template_type
{
current_aggr = $1;
! $$ = $3;
}
| aggr global_scope nested_name_specifier apparent_template_type
{
current_aggr = $1;
! $$ = $4;
}
;
! class_head_decl:
! class_head %prec EMPTY
! {
! $$.t = handle_class_head (current_aggr,
! TREE_PURPOSE ($1), TREE_VALUE ($1),
! 0, &$$.new_type_flag);
}
! | aggr identifier_defn %prec EMPTY
! {
! current_aggr = $1;
! $$.t = TYPE_MAIN_DECL (xref_tag (current_aggr, $2, 0));
! $$.new_type_flag = 1;
}
! | class_head_apparent_template %prec EMPTY
! {
! $$.t = $1;
! $$.new_type_flag = 0;
}
;
! class_head_defn:
! class_head '{'
! {
! yyungetc ('{', 1);
! $$.t = handle_class_head (current_aggr,
! TREE_PURPOSE ($1), TREE_VALUE ($1),
! 1, &$$.new_type_flag);
! }
! | class_head ':'
! {
! yyungetc (':', 1);
! $$.t = handle_class_head (current_aggr,
! TREE_PURPOSE ($1), TREE_VALUE ($1),
! 1, &$$.new_type_flag);
! }
! | class_head_apparent_template '{'
! {
! yyungetc ('{', 1);
! $$.t = $1;
! $$.new_type_flag = 0;
! }
! | class_head_apparent_template ':'
! {
! yyungetc (':', 1);
$$.t = $1;
$$.new_type_flag = 0;
+ }
+ | aggr identifier_defn '{'
+ {
+ yyungetc ('{', 1);
+ current_aggr = $1;
+ $$.t = handle_class_head (current_aggr,
+ NULL_TREE, $2,
+ 1, &$$.new_type_flag);
+ }
+ | aggr identifier_defn ':'
+ {
+ yyungetc (':', 1);
+ current_aggr = $1;
+ $$.t = handle_class_head (current_aggr,
+ NULL_TREE, $2,
+ 1, &$$.new_type_flag);
+ }
+ | aggr '{'
+ {
+ current_aggr = $1;
+ $$.t = TYPE_MAIN_DECL (xref_tag ($1, make_anon_name (), 0));
+ $$.new_type_flag = 0;
+ yyungetc ('{', 1);
}
;
maybe_base_class_list:
! /* empty */
{ $$ = NULL_TREE; }
! | ':' see_typename
! { error ("no bases given following `:'");
! $$ = NULL_TREE; }
! | ':' see_typename base_class_list
{ $$ = $3; }
;
Index: testsuite/g++.old-deja/g++.brendan/crash8.C
===================================================================
RCS file: /cvs/gcc/gcc/gcc/testsuite/g++.old-deja/g++.brendan/crash8.C,v
retrieving revision 1.5
diff -c -3 -p -r1.5 crash8.C
*** crash8.C 2000/09/07 00:37:14 1.5
--- crash8.C 2001/12/28 23:59:49
***************
*** 1,8 ****
// Build don't link:
// GROUPS passed old-abort
template<int a, int b>
! class Elvis // ERROR - in template.*
! {
} ;
template<int a>
--- 1,8 ----
// Build don't link:
// GROUPS passed old-abort
template<int a, int b>
! class Elvis
! { // ERROR - in template.*
} ;
template<int a>
// { dg-do compile }
// Copyright (C) 2001 Free Software Foundation, Inc.
// Contributed by Nathan Sidwell 28 Dec 2001 <nathan@nathan@codesourcery.com>
// PR 775 friend classes with qualified names inside template classes.
struct A
{
struct B {
B () { }
};
};
template <class T>
struct C: A {
friend A::B::B (); // 2.95.2 ICE
friend struct A;
friend struct A::B; // 2.97 error
};
template class C<char>;
template <typename T> class TPL
{
class nested;
};
template <typename T> class TPL<T>::nested
{
};