]> git.buserror.net Git - polintos/scott/priv.git/blob - idlcomp/types.cc
Merge branch 'origin'
[polintos/scott/priv.git] / idlcomp / types.cc
1 // Semantic actions for types and namespaces
2 //
3 // This software is copyright (c) 2006 Scott Wood <scott@buserror.net>.
4 // 
5 // This software is provided 'as-is', without any express or implied warranty.
6 // In no event will the authors or contributors be held liable for any damages
7 // arising from the use of this software.
8 // 
9 // Permission is hereby granted to everyone, free of charge, to use, copy,
10 // modify, prepare derivative works of, publish, distribute, perform,
11 // sublicense, and/or sell copies of the Software, provided that the above
12 // copyright notice and disclaimer of warranty be included in all copies or
13 // substantial portions of this software.
14
15 #include <cfloat>
16 #include <sstream>
17
18 #include <idlc.h>
19 #include <parser.h>
20 #include <lang.h>
21
22 bool Datum::verify_const()
23 {
24         assert(def.flags.field.Const);
25         
26         // TOK_INVALID should have caused an abort in an earlier pass, and
27         // TOK_DCON should have been replaced by the final constant by now.
28         
29         assert(con_type == TOK_ICON || con_type == TOK_UCON || 
30                con_type == TOK_FCON || con_type == TOK_BOOL);
31
32         if (cbt->flags.field.Float) {
33                 if (con_type == TOK_UCON)
34                         def.fcon = static_cast<double>(def.ucon);
35                 else if (con_type == TOK_ICON)
36                         def.fcon = static_cast<double>(def.icon);
37                 else if (con_type == TOK_BOOL) {
38                         yyerrorfl(name->file, name->line, 
39                                   "%s is of floating-point type, but is initialized with "
40                                   "a boolean constant.", name->c_str());
41
42                         return false;
43                 }
44
45                 con_type = TOK_FCON;
46                 
47                 if (cbt->bits == 32) {
48                         if (def.fcon > FLT_MAX || def.fcon < FLT_MIN) {
49                                 yyerrorfl(name->file, name->line, 
50                                           "%s is out of range for a single-precision FP datum.",
51                                           name->c_str());
52                                 
53                                 return false;
54                         }
55                 }
56                 
57                 const_init = true;
58                 return true;
59         }
60
61         if (con_type == TOK_FCON) {
62                 yyerrorfl(name->file, name->line, 
63                           "%s is of %s type, but is initialized with "
64                           "a floating-point constant.", 
65                           cbt->flags.field.Bool ? "boolean" : "integral",
66                           name->c_str());
67
68                 return false;
69         }
70         
71         if (cbt->flags.field.Bool) {
72                 if (con_type != TOK_BOOL) {
73                         yyerrorfl(name->file, name->line, 
74                                   "%s is of boolean type, but is initialized with "
75                                   "a non-boolean constant.", name->c_str());
76
77                         return false;
78                 }
79                 
80                 assert(def.ucon == 0 || def.ucon == 1);
81                 
82                 const_init = true;
83                 return true;
84         }
85
86         if (con_type == TOK_BOOL) {
87                 yyerrorfl(name->file, name->line, 
88                           "%s is of integral type, but is initialized with "
89                           "a boolean constant.", name->c_str());
90
91                 return false;
92         }
93
94         if (cbt->flags.field.Unsigned) {
95                 if (con_type == TOK_ICON && def.icon < 0) {
96                         yyerrorfl(name->file, name->line, 
97                                   "Initializer for \"%s\" is out of range.",
98                                   name->c_str());
99                         return false;
100                 }
101
102                 if (cbt->bits < 64) {
103                         uint64_t max = (1ULL << cbt->bits) - 1;
104                         
105                         if (def.ucon > max) {
106                                 yyerrorfl(name->file, name->line, 
107                                           "Initializer for \"%s\" is out of range.",
108                                           name->c_str());
109                                 return false;
110                         }
111                 }
112         } else {
113                 if (con_type == TOK_UCON && def.icon < 0) {
114                         yyerrorfl(name->file, name->line, 
115                                   "Initializer for \"%s\" is out of range.",
116                                   name->c_str());
117                         return false;
118                 }
119                 
120                 if (cbt->bits < 64) {
121                         // Yes, the constant should be unsigned before conversion,
122                         // as the temporary value could overflow a signed datum
123                         // before the one is subtracted if bits == 63.  It won't
124                         // matter on most machines, but you never know...
125                         
126                         int64_t max = (1ULL << (cbt->bits - 1)) - 1;
127                         
128                         if (def.icon > max || def.icon < -max + 1) {
129                                 yyerrorfl(name->file, name->line, 
130                                           "Initializer for \"%s\" is out of range.",
131                                           name->c_str());
132                                 return false;
133                         }
134                 }
135         }
136
137         const_init = true;
138         return true;
139 }
140
141 void Datum::init_const_early(Con *con)
142 {
143         if (con) {
144                 if (con->type == TOK_DCON) {
145                         assert(current_pass == 1);
146                         const_val_name = con->con.dcon;
147                 }
148
149                 def.flags.field.Const = 1;
150                 con_type = con->type;
151                 def.ucon = con->con.ucon;
152         }
153 }
154
155 void Datum::init(StrList *type, Array *ARRAY, Con *con)
156 {
157         assert(!complete);
158         
159         type_name = type;
160         cbt = NULL;
161         
162         basic = false;
163         complete = true;
164
165         set_array(ARRAY);
166         init_const_early(con);
167 }
168
169 void Datum::init(CompiledBasicType &CBT, Array *ARRAY, Con *con)
170 {
171         assert(!complete);
172         
173         def.basictype = CBT;
174         cbt = &def.basictype;
175
176         basic = true;
177         complete = true;
178         
179         set_array(ARRAY);
180         init_const_early(con);
181 }
182
183 void Datum::process_type()
184 {
185         assert(current_pass >= 4);
186
187         if (cbt)
188                 return;
189         
190         assert(!type_fq_name);
191         assert(type_sym);
192         
193         Symbol *sym = type_sym->get_concrete_sym(false);
194         type = dynamic_cast<Type *>(sym);
195         
196         if (!type) {
197                 const String *str = type_name->back();
198                 yyerrorfl(str->file, str->line, "\"%s\" is not a type.",
199                           sym->get_fq_name()->flatten()->c_str());
200
201                 throw UserError();
202         }
203         
204         BasicType *bt = dynamic_cast<BasicType *>(type->get_concrete_sym());
205         
206         if (bt) {
207                 if (!bt->def.flags.field.TypeDef)
208                         use_anon_type(bt->def);
209                 else
210                         cbt = &bt->def;
211         } else if (const_val) {
212                 const String *str = type_name->back();
213                 yyerrorfl(str->file, str->line, 
214                           "\"%s\" is not a basic type (and thus \"%s\" cannot "
215                           "be const).",
216                           type->get_fq_name()->flatten()->c_str(),
217                           name->c_str());
218                 throw UserError();
219         }
220         
221         if (!basic) {
222                 type_fq_name = type->get_fq_name()->flatten();
223                 def.type.length = type_fq_name->length();
224         }
225 }
226
227 Datum *Datum::resolve_constant_chain()
228 {
229         if (chain_traversed == traversal) {
230                 yyerrorfl(name->file, name->line, 
231                           "Initialization of \"%s\" forms an infinite loop:",
232                           get_fq_name()->flatten()->c_str());
233                 return this;
234         }
235
236         chain_traversed = traversal;
237         
238         assert(!const_val);
239         assert(!const_init);
240
241         process_type();
242
243         if (const_val_sym) {
244                 const_val = dynamic_cast<Datum *>(const_val_sym->get_concrete_sym());
245                 
246                 if (!const_val) {
247                         const String *str = const_val_name->back();
248                         yyerrorfl(str->file, str->line, "\"%s\" is not a datum.",
249                                   const_val_sym->get_fq_name()->flatten()->c_str());
250                         throw UserError();
251                 }
252                 
253                 if (!const_val->def.flags.field.Const) {
254                         const String *str = const_val_name->back();
255                         yyerrorfl(str->file, str->line, "\"%s\" is not a constant.",
256                                   const_val->get_fq_name()->flatten()->c_str());
257                         throw UserError();
258                 }
259         
260                 if (!const_val->const_init) {
261                         Datum *d = const_val->resolve_constant_chain();
262                         if (d) {
263                                 yyerrorfl(name->file, name->line, "...initializes \"%s\"",
264                                           get_fq_name()->flatten()->c_str());
265                 
266                                 if (d == this)
267                                         throw UserError();
268                                 
269                                 return d;
270                         }
271                 }
272                 
273                 assert(const_val->const_init);
274         
275                 con_type = const_val->con_type;
276                 def.ucon = const_val->def.ucon;
277         }
278
279         if (!verify_const())
280                 throw UserError();
281
282         assert(const_init);
283         return NULL;
284 }
285
286 // FIXME: Check for inline struct loops.
287 void Datum::final_analysis()
288 {
289         Struct *str = dynamic_cast<Struct *>(*type);
290         if (str && str->is_inline())
291                 set_inline();
292
293         if (array) {
294                 array->final_analysis();
295                 def.basictype.array = array->ca;
296         } else {
297                 def.basictype.array.bounds[0] = 0;
298                 def.basictype.array.bounds[1] = 0;
299         }
300         
301         BitField *bfparent = dynamic_cast<BitField *>(get_ns());
302         if (bfparent) {
303                 assert(!array);
304                 int defsize;
305                 
306                 if (basic)
307                         defsize = 1;
308                 else
309                         defsize = type->get_default_bf_size();
310                 
311                 if (defsize == -1) {
312                         yyerrorfl(name->file, name->line,
313                                   "\"%s\" may not be the named type of bitfield entry "
314                                   "\"%s\", as it is not a bitfield or enumeration.",
315                                   type->get_fq_name()->flatten()->c_str(),
316                                   get_fq_name()->flatten()->c_str());
317                 } else if (def.icon == -1) {
318                         def.ucon = defsize;
319                 } else if (def.ucon < (unsigned int)defsize) {
320                         yyerrorfl(name->file, name->line,
321                                   "Explicit size %llu on \"%s\" is too small for "
322                                   "type \"%s\", which has a minimum size of %u",
323                                   def.ucon, get_fq_name()->flatten()->c_str(),
324                                   type->get_fq_name()->flatten()->c_str(), defsize);
325                 }
326         }
327 }
328
329 void Struct::add(Symbol *sym, bool from_import)
330 {
331         Datum *datum = dynamic_cast<Datum *>(sym);
332         
333         if (!datum && !dynamic_cast<Alias *>(sym) && 
334             !dynamic_cast<Type *>(sym) &&
335             !dynamic_cast<Method *>(sym))
336                 throw InvalidArgument();
337         
338         NameSpace::add(sym, from_import);
339
340         if (!from_import && datum && !datum->def.flags.field.Const)
341                 add_elem(datum);
342 }
343
344 void Struct::add_elem(Datum *d)
345 {
346         entries.push_back(d);
347         def.num_entries++;
348 }
349
350 Param *Param::declare(const String *name, NameSpace *parent,
351                            StrList *TYPE, CompiledParam::Flags flags,
352                            Array *array)
353 {
354         assert(parent);
355         assert(dynamic_cast<Method *>(parent));
356         
357         Param *p = new Param(name);
358         p->init(TYPE, flags, array);
359         
360         parent->add_user(p);
361         return p;
362 }
363
364 Method *Method::declare(const String *name, NameSpace *parent)
365 {
366         assert(parent);
367         assert(dynamic_cast<Interface *>(parent));
368
369         Method *m = new Method(name);
370
371         parent->add_user(m);
372         return m;
373 }
374
375 void Interface::add(Symbol *sym, bool from_import)
376 {
377         Datum *datum = dynamic_cast<Datum *>(sym);
378         Method *method = dynamic_cast<Method *>(sym);
379
380         if (!datum && !method && 
381             !dynamic_cast<Alias *>(sym) && 
382             !dynamic_cast<Type *>(sym))
383                 throw InvalidArgument();
384
385         if (datum && !datum->def.flags.field.Const)
386                 throw InvalidArgument();
387         
388         NameSpace::add(sym, from_import);
389         
390         if (!from_import && method)
391                 add_elem(method);
392 }
393
394 // FIXME: check for duplicate and looping inheritance
395 void Interface::add_elem(Method *method)
396 {
397         methods.push_back(method);
398         def.num_methods++;
399 }
400
401 void Enum::add(Symbol *sym, bool from_import)
402 {
403         Datum *datum = dynamic_cast<Datum *>(sym);
404
405         // It also must be const unsigned, but the const won't
406         // have been initialized yet.
407
408         if (!datum)
409                 throw InvalidArgument();
410         
411         NameSpace::add(sym, from_import);
412         
413         if (!from_import)
414                 add_elem(datum);
415 }
416
417 void Enum::add_elem(Datum *datum)
418 {
419         entries.push_back(datum);
420         def.num_entries++;
421 }
422
423 void BitField::add(Symbol *sym, bool from_import)
424 {
425         Datum *datum = dynamic_cast<Datum *>(sym);
426
427         if (!datum && !dynamic_cast<Enum *>(sym) &&
428             !dynamic_cast<BitField *>(sym))
429                 throw InvalidArgument();
430
431         NameSpace::add(sym, from_import);
432
433         if (!from_import && datum)
434                 add_elem(datum);
435 }
436
437 void BitField::add_elem(Datum *datum)
438 {
439         entries.push_back(datum);
440         def.num_entries++;
441 }
442
443 void Method::add(Symbol *sym, bool from_import)
444 {
445         Param *param = dynamic_cast<Param *>(sym);
446
447         if (!param)
448                 throw InvalidArgument();
449
450         NameSpace::add(sym, from_import);
451         if (!from_import)
452                 add_elem(param);
453 }
454
455 void Method::add_elem(Param *param)
456 {
457         entries.push_back(param);
458         def.num_entries++;
459 }
460
461 // Use this rather than lookup_sym if you want to check for built-in types.
462 // If only constructed types are valid, use lookup_sym.  If basic_types_only
463 // is set, then NULL will be returned if sl does not describe a built-in
464 // basic type (such as int, flong, octet, etc).  This option is used so that
465 // the first-pass code in idlparse.y can decide whether to create a BasicType
466 // or an Alias/TypeDef.
467
468 Type *lookup_type(StrList *sl, NameSpace *ctx, bool basic_types_only)
469 {
470         Type *type;
471         int token = sl->front()->token;
472
473         // These tokens aren't valid types, but the names can be
474         // used as identifiers.
475         switch (token) {
476                 case TOK_ASYNC:
477                 case TOK_INOUT:
478                 case TOK_OUT:
479                         token = TOK_IDENT;
480         }
481         
482         // Treat it as a user-defined type if it's either not a reserved word,
483         // or if there are multiple components.
484
485         if (token == TOK_IDENT || sl->front() != sl->back()) {
486                 if (basic_types_only)
487                         return false;
488         
489                 Symbol *sym = lookup_sym(toplevel, sl, ctx);
490                 type = dynamic_cast<Type *>(sym->get_concrete_sym(false));
491                 
492                 if (!type) {
493                         const String *str = sl->back();
494                         yyerrorfl(str->file, str->line, "\"%s\" is not a type.",
495                                   sym->get_fq_name()->flatten()->c_str());
496
497                         throw UserError();
498                 }
499         } else {
500                 CompiledBasicType cbt;
501                 memset(&cbt, 0, sizeof(cbt));
502                 
503                 switch (token) {
504                         case TOK_BOOL:
505                                 cbt.bits = 0;
506                                 cbt.flags.field.Bool = 1;
507                                 break;
508                         
509                         case TOK_OCTET:
510                         case TOK_CHAR:
511                                 cbt.bits = 8;
512                                 cbt.flags.field.Unsigned = 1;
513                                 break;
514                         
515                         case TOK_SHORT:
516                                 cbt.bits = 16;
517                                 break;
518                         
519                         case TOK_INT:
520                                 cbt.bits = 32;
521                                 break;
522                         
523                         case TOK_LONG:
524                                 cbt.bits = 64;
525                                 break;
526                         
527                         case TOK_USHORT:
528                                 cbt.bits = 16;
529                                 cbt.flags.field.Unsigned = 1;
530                                 break;
531                         
532                         case TOK_UINT:
533                                 cbt.bits = 32;
534                                 cbt.flags.field.Unsigned = 1;
535                                 break;
536                         
537                         case TOK_ULONG:
538                                 cbt.bits = 64;
539                                 cbt.flags.field.Unsigned = 1;
540                                 break;
541                         
542                         case TOK_FSHORT:
543                                 cbt.bits = 32;
544                                 cbt.flags.field.Float = 1;
545                                 break;
546                         
547                         case TOK_FLONG:
548                                 cbt.bits = 64;
549                                 cbt.flags.field.Float = 1;
550                                 break;
551                         
552                         default:
553                                 fprintf(stderr, "token %d\n", token);
554                                 BUG();
555                 }
556                 
557                 type = BasicType::declare(sl->front(), NULL, cbt);
558         }
559         
560         return type;
561 }
562
563 void declare_data(NameSpace *ns, StrList *ids, StrList *type,
564                   Array *array, StrList *attr)
565 {
566         bool is_inline = false;
567         bool is_immutable = false;
568         
569         if (attr) {
570                 for (StrList::iterator i = attr->begin(); i != attr->end(); ++i) {
571                         const String *this_attr = *i;
572         
573                         switch (this_attr->token) {
574                                 case TOK_INLINE:
575                                         is_inline = true;
576                                         break;
577                                 
578                                 case TOK_IMMUTABLE:
579                                         is_immutable = true;
580                                         break;
581                                 
582                                 default:
583                                         yyerrorfl(this_attr->file, this_attr->line,
584                                                   "Invalid attribute \"%s\"", (*i)->c_str());
585                         }
586                 }
587         }
588         
589         for (StrList::iterator i = ids->begin(); i != ids->end(); ++i) {
590                 try {
591                         Datum *d = Datum::declare(*i, ns, type, array);
592                         
593                         if (is_inline)
594                                 d->set_inline();
595                         
596                         if (is_immutable)
597                                 d->set_immutable();
598                 }
599         
600                 catch (UserError) {
601                         // An error has already been displayed, but try to
602                         // continue and find more errors.
603                 }
604         }
605 }
606
607 void declare_aliases(NameSpace *ns, StrList *ids, StrList *type,
608                      bool is_typedef)
609 {
610         StrList::iterator i;
611
612         for (i = ids->begin(); i != ids->end(); ++i) {
613                 try {
614                         if (is_typedef)
615                                 TypeDef::declare(*i, ns, type);
616                         else
617                                 Alias::declare(*i, ns, type);
618                 }
619         
620                 catch (UserError) {
621                         // An error has already been displayed, but try to
622                         // continue and find more errors.
623                 }
624         }
625 }
626
627 void declare_basictypes(NameSpace *ns, StrList *ids, 
628                         BasicType *type, bool is_typedef)
629 {
630         assert(!type->def.flags.field.TypeDef);
631         StrList::iterator i;
632         
633         for (i = ids->begin(); i != ids->end(); ++i) {
634                 try {
635                         BasicType *bt =
636                                 BasicType::declare(*i, ns, type->def);
637                         
638                         bt->def.flags.field.TypeDef = is_typedef;
639                 }
640         
641                 catch (UserError) {
642                         // An error has already been displayed, but try to
643                         // continue and find more errors.
644                 }
645         }
646 }
647
648 Array::Array(NameSpace *LOOKUP_CTX)
649 {
650         lookup_ctx = LOOKUP_CTX;
651         
652         for (int i = 0; i < 2; i++) {
653                 cons[i].con.ucon = 0;
654                 cons[i].type = TOK_UCON;
655         }
656 }
657
658 void Array::set_unbounded()
659 {
660         for (int i = 0; i < 2; i++)
661                 cons[i].type = TOK_NONE;
662 }
663
664 void Array::final_analysis()
665 {
666         for (int i = 0; i < 2; i++) {
667                 if (cons[i].type == TOK_NONE) {
668                         if (i == 0)
669                                 ca.bounds[0] = 0;
670                         else
671                                 ca.bounds[1] = -1;
672                         
673                         continue;
674                 }
675         
676                 if (cons[i].type == TOK_DCON) {
677                         Symbol *sym = lookup_sym(toplevel, dcons[i], lookup_ctx);
678                         datums[i] = dynamic_cast<Datum *>(sym);
679                         if (!datums[i]) {
680                                 const String *str = dcons[i]->back();
681                                 yyerrorfl(str->file, str->line, "\"%s\" is not a Datum.",
682                                           sym->get_fq_name()->flatten()->c_str());
683                                 continue;
684                         }
685                         
686                         ca.bounds[i] = datums[i]->get_ucon(dcons[i]->back());
687                         cons[i].type = datums[i]->con_type;
688                 } else {
689                         ca.bounds[i] = cons[i].con.ucon;
690                 }
691                 
692                 if (cons[i].type == TOK_FCON) {
693                         yyerrorfl(strs[i]->file, strs[i]->line, 
694                                   "\"%s\" is not an integral Datum.",
695                                   strs[i]->c_str());
696                         throw UserError();
697                 }
698
699                 if (ca.bounds[i] < 0) {
700                         yyerrorfl(strs[i]->file, strs[i]->line, 
701                                   "\"%s\" %s.", strs[i]->c_str(),
702                                   cons[i].type == TOK_UCON ?
703                                   "does not fit in a signed 64-bit integer" :
704                                   "is negative");
705                         throw UserError();
706                 }
707         }
708         
709         if (ca.bounds[1] >= 0 && ca.bounds[0] > ca.bounds[1]) {
710                 yyerrorfl(strs[0]->file, strs[0]->line,
711                           "\"%s\" is larger than \"%s\".",
712                           strs[0]->c_str(), strs[1]->c_str());
713                 throw UserError();
714         }
715 }
716
717 void Array::set_bound(Con &con, int bound)
718 {
719         assert(current_pass == 1);
720
721         if (con.type == TOK_FCON) {
722                 yyerrorf("Array limits must be integers.");
723                 throw UserError();
724         }
725         
726         if (con.type == TOK_ICON && con.con.icon <= 0) {
727                 yyerrorf("Array limits must be positive");
728                 throw UserError();
729         }
730
731         if (con.type == TOK_UCON && con.con.icon <= 0) {
732                 yyerrorf("Array limits must fit in a signed 64-bit integer");
733                 throw UserError();
734         }
735         
736         cons[bound] = con;
737         
738         if (con.type == TOK_DCON) {
739                 assert(con.con.dcon);
740                 dcons[bound] = con.con.dcon;
741                 strs[bound] = con.con.dcon->flatten();
742         } else {
743                 std::ostringstream ss(std::ostringstream::out);
744                 
745                 switch (con.type) {
746                         case TOK_FCON:
747                                 ss << con.con.fcon;
748                                 break;
749                         
750                         case TOK_UCON:
751                                 ss << con.con.ucon;
752                                 break;
753                         
754                         case TOK_ICON:
755                                 ss << con.con.icon;
756                                 break;
757                         
758                         case TOK_NONE:
759                                 if (bound == 0)
760                                         ss << "0";
761
762                                 break;
763                         
764                         default:
765                                 BUG();
766                 }
767                 
768                 strs[bound] = new String(ss.str().c_str(), cur_input_file, 
769                                          curline, con.type);
770         }
771 }
772
773
774 void UserNameSpace::output_lang(LangCallback *lcb, int arg1, void *arg2)
775 {
776         lcb->output(this, arg1, arg2);
777 }
778
779 void BasicType::output_lang(LangCallback *lcb, int arg1, void *arg2)
780 {
781         lcb->output(this, arg1, arg2);
782 }
783
784 void Interface::output_lang(LangCallback *lcb, int arg1, void *arg2)
785 {
786         lcb->output(this, arg1, arg2);
787 }
788
789 void Struct::output_lang(LangCallback *lcb, int arg1, void *arg2)
790 {
791         lcb->output(this, arg1, arg2);
792 }
793
794 void BitField::output_lang(LangCallback *lcb, int arg1, void *arg2)
795 {
796         lcb->output(this, arg1, arg2);
797 }
798
799 void Enum::output_lang(LangCallback *lcb, int arg1, void *arg2)
800 {
801         lcb->output(this, arg1, arg2);
802 }
803
804 void Alias::output_lang(LangCallback *lcb, int arg1, void *arg2)
805 {
806         lcb->output(this, arg1, arg2);
807 }
808
809 void TypeDef::output_lang(LangCallback *lcb, int arg1, void *arg2)
810 {
811         lcb->output(this, arg1, arg2);
812 }
813
814 void Datum::output_lang(LangCallback *lcb, int arg1, void *arg2)
815 {
816         if (def.flags.field.Const)
817                 lcb->output(this, arg1, arg2);
818 }
819
820 void Method::output_lang(LangCallback *lcb, int arg1, void *arg2)
821 {
822 }
823
824 void Param::output_lang(LangCallback *lcb, int arg1, void *arg2)
825 {
826 }
827
828 int Interface::all_supers_traversal;