summaryrefslogtreecommitdiff
path: root/rpc++/gcc-2.2.fix
blob: f684e5b128be17e862f67564fb8ebcae10d822ee (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
To: bug-g++@prep.ai.mit.edu
Subject: gcc-2.2 loops with template-local typedefs (bug&patch)
BCC: mnl,ulf
--text follows this line--
Hi,

trying to translate the following fragment on a Sparc running SunOs 4.1.2
with gcc-2.2 results in gcc infinitly looping.

---------------------------------------------------------------------------
// -*- c++ -*-

class AnyRpcCallback
{
protected:

public:
  inline virtual ~AnyRpcCallback () {}
  inline virtual void Do (void* in, void* out) = 0;
};

template<class T> class RpcCallback : public AnyRpcCallback
{
  typedef void (T::*Method)(void*, void*);
  typedef void (T::*MethodN)(void*, void**);
  typedef void (T::*Method1)(void*, void*);
  typedef void (T::*Method2)(void*, void*, void*);

private:
  T* object;
  void (T::*method)(void*, void*);

public:
  inline RpcCallback (T* o, void* m)
    { object = o; method = m; }
  inline void Do (void* in, void* out)
    { (object->*method)(in, out); }
};

class Test
{
public:
  void m (void*, void*);
};

main ()
{
  Test o;
  AnyRpcCallback* cb = new RpcCallback<Test> (&o, &Test::m);
}
---------------------------------------------------------------------------

PLEASE NOTE that you will get another loop due to a bug that I have
reported together with a patch earlier (it's appended to this mail).
So you won't be able to reproduce the bug reported in this mail unless
you have my previous patch applied. I am, however, definitely sure
(and the explanation below will confirm it) that the bug reported in
this mail is *NOT* caused by my patch!

The problem is, that the "chain" field of the tree-nodes used by gcc
for its internal representation is used for various purposes, and in
the case of this template-local typedef, someone lost track of its usage.

After parsing, the TYPE_DECL-node created for the typedef is appended
to the scope via "pushlevel". Types in the current scope are linked
using the "chain" field. At the same time, however, all components of
the template are linked together during parsing using the same "chain"
field. Parsing the second typedef, "pushlevel" makes the first typedef
a successor of the second typedef and the subsequent catenation of
components makes the second typedef a successor of the first typedef
thus creating a loop.

The resulting list of all components is used in routine
"finish_struct".

I think the most proper approach would be to use TREE_LIST nodes in
the list of components as indirect references to the typedef-nodes.
This is easy to achieve, it is, however, very hard to modify
finish_struct in a way that it handles these indirection properly.
Actually, I gave up when I tried to understand & modify the routine
that removes the duplicate declarations from the list of components.

There are two easier approaches: (1) Don't include typedefs in the
list of components, (2) use copies of the typedef-node which have an
unused chain field. The first approach assumes that finish_struct
doesn't do anything with typedefs, so it wouldn't be important if they
are missing from the list of components. If this is the case, however,
it can't hurt to use copies of the typedef-nodes (copies of the
originals that are linked in the scope-list), so the second approach
is safer. It can only fail if finish_struct modifies the typedef-nodes
and this modification is significant for the typedef-nodes in the
scope-list (which are, of course, not modified. Only the copies are).

So I think the patch is pretty safe. It fixes the problem and doesn't
seem to introduce new ones. I'm aware that typedefs that are local to
templates stretch language features to the limits, but it makes my
C++ interface to RPCs real nice (I'll post it one of these days).

Michael

*** .orig/cp-parse.y	Mon Jun 15 17:08:58 1992
--- cp-parse.y	Mon Jun 15 19:13:15 1992
***************
*** 2211,2217 ****
  		      if (TREE_CODE (t) == ENUMERAL_TYPE && TREE_NONLOCAL_FLAG (t))
  			$$ = grok_enum_decls (t, $2);
  		      else
! 			$$ = $2;
  		    }
  		  end_exception_decls ();
  		}
--- 2211,2233 ----
  		      if (TREE_CODE (t) == ENUMERAL_TYPE && TREE_NONLOCAL_FLAG (t))
  			$$ = grok_enum_decls (t, $2);
  		      else
! 			{
! 			  /* if a component is a typedef, it is inserted
! 			     in the list of nodes that make up the valid
! 			     types in the scope. Thus its chain field is
! 			     used and can't be used a second time for linking
! 			     the components of the struct. So, we make a copy
! 			     here. This apparently works. The proper thing
! 			     to do, however, would be to use a TREE_LIST
! 			     node to reference the typedef. I tried to rewrite
! 			     finish_struct accordingly (i.e., ``dereference''
! 			     components TREE_LIST before use, but I gave up.
! 			     mnl@dtro.e-technik.th-darmstadt.de */
! 			  if (TREE_CODE ($2) == TYPE_DECL)
! 			    $$ = copy_node ($2);
! 			  else
! 			    $$ = $2;
! 			}
  		    }
  		  end_exception_decls ();
  		}

===========================================================================
The previous bug:
---------------------------------------------------------------------------
Return-Path: <mnl>
Date: Wed, 10 Jun 92 19:31:13 +0200
From: "Michael N. Lipp" <mnl>
To: bug-g++@prep.ai.mit.edu
Subject: gcc-2.2 bug&patch: typedef in template

Hi,

gcc-2.2 on a sparc running SunOS 4.1.2 enters an infinite loop when
compiling this:

-----------------------------------------------------------------------------
// -*- c++ -*-

class AnyRpcCallback
{
protected:

public:
  inline virtual ~AnyRpcCallback () {}
  inline virtual void Do (void* in, void* out) = 0;
};

template<class T> class RpcCallback : public AnyRpcCallback
{
  typedef void (T::*Method)(void*, void*);

private:
  T* object;
  void (T::*method)(void*, void*);

public:
  inline RpcCallback (T* o, void* m)
    { object = o; method = m; }
  inline void Do (void* in, void* out)
    { (object->*method)(in, out); }
};

class Test
{
public:
  void m (void*, void*);
};

main ()
{
  Test o;
  AnyRpcCallback* cb = new RpcCallback<Test> (&o, &Test::m);
}
-----------------------------------------------------------------------------

This is quite an improvement over gcc-2.1 which dumped core with this
source.

I tracked the cause down: grokdeclarator does a pushlevel(0), then
calls start_decl, which in turn calls grokdeclarator again which does
a poplevel_class. This poplevel_class pops the level pushed by
pushlevel(0) and so the poplevel performed by grokdeclarator to match
its pushlevel(0) pops quite a different level! This can easily be
observed by compiling cp-decl.c with -DDEBUG_CP_BINDING_LEVELS.

Here is a patch that fixes the bug. I don't think it hits the real
cause of this problem, but it works.

*** .orig/cp-decl.c	Wed Jun 10 14:06:26 1992
--- cp-decl.c	Wed Jun 10 15:20:38 1992
***************
*** 6874,6882 ****
--- 6874,6889 ----
  	  tree loc_typedecl;
  	  register int i = sizeof (struct lang_decl_flags) / sizeof (int);
  	  register int *pi;
+ 	  struct binding_level *local_binding_level;
  
  	  /* keep `grokdeclarator' from thinking we are in PARM context.  */
  	  pushlevel (0);
+ 	  /* poplevel_class may be called by grokdeclarator which is called in
+ 	     start_decl which is called below. In this case, our pushed level
+ 	     may vanish and poplevel mustn't be called. So remember what we
+ 	     have pushed and pop only if that is matched by 
+ 	     current_binding_level later. mnl@dtro.e-technik.th-darmstadt.de */
+ 	  local_binding_level = current_binding_level;
  	  loc_typedecl = start_decl (declarator, declspecs, initialized, NULL_TREE);
  
  	  pi = (int *) permalloc (sizeof (struct lang_decl_flags));
***************
*** 6883,6889 ****
  	  while (i > 0)
  	    pi[--i] = 0;
  	  DECL_LANG_SPECIFIC (loc_typedecl) = (struct lang_decl *) pi;
! 	  poplevel (0, 0, 0);
  
  #if 0
  	  if (TREE_CODE (TREE_TYPE (loc_typedecl)) == ENUMERAL_TYPE)
--- 6890,6897 ----
  	  while (i > 0)
  	    pi[--i] = 0;
  	  DECL_LANG_SPECIFIC (loc_typedecl) = (struct lang_decl *) pi;
! 	  if (current_binding_level == local_binding_level)
! 	    poplevel (0, 0, 0);
  
  #if 0
  	  if (TREE_CODE (TREE_TYPE (loc_typedecl)) == ENUMERAL_TYPE)

Michael

-----------------,------------------------------,------------------------------
Michael N. Lipp  !  Institut fuer Datentechnik  !  Phone: 49-6151-163776
                 !  Merckstr. 25     ,----------'  Fax:   49-6151-164976
                 !  D-6100 Darmstadt ! E-Mail:
                 !  (Germany)        !     mnl@dtro.e-technik.th-darmstadt.de
-----------------'-------------------'-----------------------------------------