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