source: mainline/uspace/app/sbi/src/stype.c@ 074444f

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 074444f was 074444f, checked in by Jiri Svoboda <jiri@…>, 16 years ago

Update SBI to rev. 184.

  • Property mode set to 100644
File size: 18.0 KB
Line 
1/*
2 * Copyright (c) 2010 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/**
30 * @file Implements a walk on the program that computes and checks static
31 * types. 'Types' the program.
32 */
33
34#include <stdio.h>
35#include <stdlib.h>
36#include <assert.h>
37#include "debug.h"
38#include "intmap.h"
39#include "list.h"
40#include "mytypes.h"
41#include "run_texpr.h"
42#include "stree.h"
43#include "strtab.h"
44#include "stype_expr.h"
45#include "symbol.h"
46#include "tdata.h"
47
48#include "stype.h"
49
50static void stype_csi(stype_t *stype, stree_csi_t *csi);
51static void stype_fun(stype_t *stype, stree_fun_t *fun);
52static void stype_var(stype_t *stype, stree_var_t *var);
53static void stype_prop(stype_t *stype, stree_prop_t *prop);
54
55static void stype_block(stype_t *stype, stree_block_t *block);
56
57static void stype_vdecl(stype_t *stype, stree_vdecl_t *vdecl_s);
58static void stype_if(stype_t *stype, stree_if_t *if_s);
59static void stype_while(stype_t *stype, stree_while_t *while_s);
60static void stype_for(stype_t *stype, stree_for_t *for_s);
61static void stype_raise(stype_t *stype, stree_raise_t *raise_s);
62static void stype_return(stype_t *stype, stree_return_t *return_s);
63static void stype_exps(stype_t *stype, stree_exps_t *exp_s, bool_t want_value);
64static void stype_wef(stype_t *stype, stree_wef_t *wef_s);
65
66/** Type module */
67void stype_module(stype_t *stype, stree_module_t *module)
68{
69 list_node_t *mbr_n;
70 stree_modm_t *mbr;
71
72#ifdef DEBUG_TYPE_TRACE
73 printf("Type module.\n");
74#endif
75 stype->current_csi = NULL;
76 stype->proc_vr = NULL;
77
78 mbr_n = list_first(&module->members);
79 while (mbr_n != NULL) {
80 mbr = list_node_data(mbr_n, stree_modm_t *);
81 assert(mbr->mc == mc_csi);
82
83 stype_csi(stype, mbr->u.csi);
84
85 mbr_n = list_next(&module->members, mbr_n);
86 }
87}
88
89/** Type CSI */
90static void stype_csi(stype_t *stype, stree_csi_t *csi)
91{
92 list_node_t *csimbr_n;
93 stree_csimbr_t *csimbr;
94 stree_csi_t *prev_ctx;
95
96#ifdef DEBUG_TYPE_TRACE
97 printf("Type CSI '");
98 symbol_print_fqn(csi_to_symbol(csi));
99 printf("'.\n");
100#endif
101 prev_ctx = stype->current_csi;
102 stype->current_csi = csi;
103
104 csimbr_n = list_first(&csi->members);
105 while (csimbr_n != NULL) {
106 csimbr = list_node_data(csimbr_n, stree_csimbr_t *);
107
108 switch (csimbr->cc) {
109 case csimbr_csi: stype_csi(stype, csimbr->u.csi); break;
110 case csimbr_fun: stype_fun(stype, csimbr->u.fun); break;
111 case csimbr_var: stype_var(stype, csimbr->u.var); break;
112 case csimbr_prop: stype_prop(stype, csimbr->u.prop); break;
113 }
114
115 csimbr_n = list_next(&csi->members, csimbr_n);
116 }
117
118 stype->current_csi = prev_ctx;
119}
120
121/** Type function */
122static void stype_fun(stype_t *stype, stree_fun_t *fun)
123{
124 list_node_t *arg_n;
125 stree_proc_arg_t *arg;
126 stree_symbol_t *fun_sym;
127 tdata_item_t *titem;
128
129#ifdef DEBUG_TYPE_TRACE
130 printf("Type function '");
131 symbol_print_fqn(fun_to_symbol(fun));
132 printf("'.\n");
133#endif
134 fun_sym = fun_to_symbol(fun);
135
136 /*
137 * Type formal arguments.
138 * XXX Save the results.
139 */
140 arg_n = list_first(&fun->args);
141 while (arg_n != NULL) {
142 arg = list_node_data(arg_n, stree_proc_arg_t *);
143
144 /* XXX Because of overloaded builtin WriteLine. */
145 if (arg->type == NULL) {
146 arg_n = list_next(&fun->args, arg_n);
147 continue;
148 }
149
150 run_texpr(stype->program, fun_sym->outer_csi, arg->type,
151 &titem);
152
153 arg_n = list_next(&fun->args, arg_n);
154 }
155
156 /* Variadic argument */
157 if (fun->varg != NULL) {
158 /* Check type and verify it is an array. */
159 run_texpr(stype->program, fun_sym->outer_csi, fun->varg->type,
160 &titem);
161
162 if (titem->tic != tic_tarray && titem->tic != tic_ignore) {
163 printf("Error: Packed argument is not an array.\n");
164 stype_note_error(stype);
165 }
166 }
167
168 /*
169 * Type function body.
170 */
171
172 /* Builtin functions do not have a body. */
173 if (fun->proc->body == NULL)
174 return;
175
176 stype->proc_vr = stype_proc_vr_new();
177 stype->proc_vr->proc = fun->proc;
178 list_init(&stype->proc_vr->block_vr);
179
180 stype_block(stype, fun->proc->body);
181
182 free(stype->proc_vr);
183 stype->proc_vr = NULL;
184}
185
186/** Type member variable */
187static void stype_var(stype_t *stype, stree_var_t *var)
188{
189 tdata_item_t *titem;
190
191 (void) stype;
192 (void) var;
193
194 run_texpr(stype->program, stype->current_csi, var->type,
195 &titem);
196 if (titem->tic == tic_ignore) {
197 /* An error occured. */
198 stype_note_error(stype);
199 return;
200 }
201}
202
203/** Type property */
204static void stype_prop(stype_t *stype, stree_prop_t *prop)
205{
206#ifdef DEBUG_TYPE_TRACE
207 printf("Type property '");
208 symbol_print_fqn(prop_to_symbol(prop));
209 printf("'.\n");
210#endif
211 stype->proc_vr = stype_proc_vr_new();
212 list_init(&stype->proc_vr->block_vr);
213
214 if (prop->getter != NULL) {
215 stype->proc_vr->proc = prop->getter;
216 stype_block(stype, prop->getter->body);
217 }
218
219 if (prop->setter != NULL) {
220 stype->proc_vr->proc = prop->setter;
221 stype_block(stype, prop->setter->body);
222 }
223
224 free(stype->proc_vr);
225 stype->proc_vr = NULL;
226}
227
228/** Type statement block */
229static void stype_block(stype_t *stype, stree_block_t *block)
230{
231 stree_stat_t *stat;
232 list_node_t *stat_n;
233 stype_block_vr_t *block_vr;
234 list_node_t *bvr_n;
235
236#ifdef DEBUG_TYPE_TRACE
237 printf("Type block.\n");
238#endif
239
240 /* Create block visit record. */
241 block_vr = stype_block_vr_new();
242 intmap_init(&block_vr->vdecls);
243
244 /* Add block visit record to the stack. */
245 list_append(&stype->proc_vr->block_vr, block_vr);
246
247 stat_n = list_first(&block->stats);
248 while (stat_n != NULL) {
249 stat = list_node_data(stat_n, stree_stat_t *);
250 stype_stat(stype, stat, b_false);
251
252 stat_n = list_next(&block->stats, stat_n);
253 }
254
255 /* Remove block visit record from the stack, */
256 bvr_n = list_last(&stype->proc_vr->block_vr);
257 assert(list_node_data(bvr_n, stype_block_vr_t *) == block_vr);
258 list_remove(&stype->proc_vr->block_vr, bvr_n);
259}
260
261/** Type statement
262 *
263 * Types a statement. If @a want_value is @c b_true, then warning about
264 * ignored expression value will be supressed for this statement (but not
265 * for nested statemens). This is used in interactive mode.
266 *
267 * @param stype Static typer object.
268 * @param stat Statement to type.
269 * @param want_value @c b_true to allow ignoring expression value.
270 */
271void stype_stat(stype_t *stype, stree_stat_t *stat, bool_t want_value)
272{
273#ifdef DEBUG_TYPE_TRACE
274 printf("Type statement.\n");
275#endif
276 switch (stat->sc) {
277 case st_vdecl: stype_vdecl(stype, stat->u.vdecl_s); break;
278 case st_if: stype_if(stype, stat->u.if_s); break;
279 case st_while: stype_while(stype, stat->u.while_s); break;
280 case st_for: stype_for(stype, stat->u.for_s); break;
281 case st_raise: stype_raise(stype, stat->u.raise_s); break;
282 case st_return: stype_return(stype, stat->u.return_s); break;
283 case st_exps: stype_exps(stype, stat->u.exp_s, want_value); break;
284 case st_wef: stype_wef(stype, stat->u.wef_s); break;
285 }
286}
287
288/** Type local variable declaration */
289static void stype_vdecl(stype_t *stype, stree_vdecl_t *vdecl_s)
290{
291 stype_block_vr_t *block_vr;
292 stree_vdecl_t *old_vdecl;
293 tdata_item_t *titem;
294
295#ifdef DEBUG_TYPE_TRACE
296 printf("Type variable declaration statement.\n");
297#endif
298 block_vr = stype_get_current_block_vr(stype);
299 old_vdecl = (stree_vdecl_t *) intmap_get(&block_vr->vdecls,
300 vdecl_s->name->sid);
301
302 if (old_vdecl != NULL) {
303 printf("Error: Duplicate variable declaration '%s'.\n",
304 strtab_get_str(vdecl_s->name->sid));
305 stype_note_error(stype);
306 }
307
308 run_texpr(stype->program, stype->current_csi, vdecl_s->type,
309 &titem);
310 if (titem->tic == tic_ignore) {
311 /* An error occured. */
312 stype_note_error(stype);
313 return;
314 }
315
316 intmap_set(&block_vr->vdecls, vdecl_s->name->sid, vdecl_s);
317}
318
319/** Type @c if statement */
320static void stype_if(stype_t *stype, stree_if_t *if_s)
321{
322 stree_expr_t *ccond;
323
324#ifdef DEBUG_TYPE_TRACE
325 printf("Type 'if' statement.\n");
326#endif
327 /* Convert condition to boolean type. */
328 stype_expr(stype, if_s->cond);
329 ccond = stype_convert(stype, if_s->cond, stype_boolean_titem(stype));
330
331 /* Patch code with augmented expression. */
332 if_s->cond = ccond;
333
334 /* Type the @c if block */
335 stype_block(stype, if_s->if_block);
336
337 /* Type the @c else block */
338 if (if_s->else_block != NULL)
339 stype_block(stype, if_s->else_block);
340}
341
342/** Type @c while statement */
343static void stype_while(stype_t *stype, stree_while_t *while_s)
344{
345 stree_expr_t *ccond;
346
347#ifdef DEBUG_TYPE_TRACE
348 printf("Type 'while' statement.\n");
349#endif
350 /* Convert condition to boolean type. */
351 stype_expr(stype, while_s->cond);
352 ccond = stype_convert(stype, while_s->cond,
353 stype_boolean_titem(stype));
354
355 /* Patch code with augmented expression. */
356 while_s->cond = ccond;
357
358 /* Type the body of the loop */
359 stype_block(stype, while_s->body);
360}
361
362/** Type @c for statement */
363static void stype_for(stype_t *stype, stree_for_t *for_s)
364{
365#ifdef DEBUG_TYPE_TRACE
366 printf("Type 'for' statement.\n");
367#endif
368 stype_block(stype, for_s->body);
369}
370
371/** Type @c raise statement */
372static void stype_raise(stype_t *stype, stree_raise_t *raise_s)
373{
374#ifdef DEBUG_TYPE_TRACE
375 printf("Type 'raise' statement.\n");
376#endif
377 stype_expr(stype, raise_s->expr);
378}
379
380/** Type @c return statement */
381static void stype_return(stype_t *stype, stree_return_t *return_s)
382{
383 stree_symbol_t *outer_sym;
384 stree_fun_t *fun;
385 stree_prop_t *prop;
386
387 stree_expr_t *cexpr;
388 tdata_item_t *dtype;
389
390#ifdef DEBUG_TYPE_TRACE
391 printf("Type 'return' statement.\n");
392#endif
393 stype_expr(stype, return_s->expr);
394
395 /* Determine the type we need to return. */
396
397 outer_sym = stype->proc_vr->proc->outer_symbol;
398 switch (outer_sym->sc) {
399 case sc_fun:
400 fun = symbol_to_fun(outer_sym);
401 assert(fun != NULL);
402
403 /* XXX Memoize to avoid recomputing. */
404 run_texpr(stype->program, outer_sym->outer_csi, fun->rtype,
405 &dtype);
406 break;
407 case sc_prop:
408 prop = symbol_to_prop(outer_sym);
409 assert(prop != NULL);
410
411 if (stype->proc_vr->proc != prop->getter) {
412 printf("Error: Return statement in "
413 "setter.\n");
414 stype_note_error(stype);
415 }
416
417 /* XXX Memoize to avoid recomputing. */
418 run_texpr(stype->program, outer_sym->outer_csi, prop->type,
419 &dtype);
420 break;
421 default:
422 assert(b_false);
423 }
424
425 /* Convert to the return type. */
426 cexpr = stype_convert(stype, return_s->expr, dtype);
427
428 /* Patch code with the augmented expression. */
429 return_s->expr = cexpr;
430}
431
432/** Type expression statement */
433static void stype_exps(stype_t *stype, stree_exps_t *exp_s, bool_t want_value)
434{
435#ifdef DEBUG_TYPE_TRACE
436 printf("Type expression statement.\n");
437#endif
438 stype_expr(stype, exp_s->expr);
439
440 if (want_value == b_false && exp_s->expr->titem != NULL)
441 printf("Warning: Expression value ignored.\n");
442}
443
444/** Type With-Except-Finally statement */
445static void stype_wef(stype_t *stype, stree_wef_t *wef_s)
446{
447 list_node_t *ec_n;
448 stree_except_t *ec;
449
450#ifdef DEBUG_TYPE_TRACE
451 printf("Type WEF statement.\n");
452#endif
453 /* Type the @c with block. */
454 if (wef_s->with_block != NULL)
455 stype_block(stype, wef_s->with_block);
456
457 /* Type the @c except clauses. */
458 ec_n = list_first(&wef_s->except_clauses);
459 while (ec_n != NULL) {
460 ec = list_node_data(ec_n, stree_except_t *);
461 stype_block(stype, ec->block);
462
463 ec_n = list_next(&wef_s->except_clauses, ec_n);
464 }
465
466 /* Type the @c finally block. */
467 if (wef_s->finally_block != NULL)
468 stype_block(stype, wef_s->finally_block);
469}
470
471/** Convert expression of one type to another type.
472 *
473 * If the type of expression @a expr is not compatible with @a dtype
474 * (i.e. there does not exist an implicit conversion from @a expr->type to
475 * @a dtype), this function will produce an error (Cannot convert A to B).
476 *
477 * Otherwise it will either return the expression unmodified (if there is
478 * no action to take at run time) or it will return a new expression
479 * while clobbering the old one. Typically this would just attach the
480 * expression as a subtree of the conversion.
481 *
482 * Note: No conversion that would require modifying @a expr is implemented
483 * yet.
484 */
485stree_expr_t *stype_convert(stype_t *stype, stree_expr_t *expr,
486 tdata_item_t *dest)
487{
488 tdata_item_t *src;
489
490 (void) stype;
491 src = expr->titem;
492
493 if (dest == NULL) {
494 printf("Error: Conversion destination is not valid.\n");
495 stype_note_error(stype);
496 return expr;
497 }
498
499 if (src == NULL) {
500 printf("Error: Conversion source is not valid.\n");
501 stype_note_error(stype);
502 return expr;
503 }
504
505 if (dest->tic == tic_ignore || src->tic == tic_ignore)
506 return expr;
507
508 /*
509 * Special case: Nil to object.
510 */
511 if (src->tic == tic_tprimitive && src->u.tprimitive->tpc == tpc_nil) {
512 if (dest->tic == tic_tobject)
513 return expr;
514 }
515
516 if (src->tic != dest->tic)
517 goto failure;
518
519 switch (src->tic) {
520 case tic_tprimitive:
521 /* Check if both have the same tprimitive class. */
522 if (src->u.tprimitive->tpc != dest->u.tprimitive->tpc)
523 goto failure;
524 break;
525 case tic_tobject:
526 /* Check if @c src is derived from @c dest. */
527 if (stree_is_csi_derived_from_csi(src->u.tobject->csi,
528 dest->u.tobject->csi) != b_true) {
529 goto failure;
530 }
531 break;
532 case tic_tarray:
533 /* Compare rank and base type. */
534 if (src->u.tarray->rank != dest->u.tarray->rank)
535 goto failure;
536
537 /* XXX Should we convert each element? */
538 if (tdata_item_equal(src->u.tarray->base_ti,
539 dest->u.tarray->base_ti) != b_true)
540 goto failure;
541 break;
542 case tic_tfun:
543 printf("Error: Unimplemented: Converting '");
544 tdata_item_print(src);
545 printf("' to '");
546 tdata_item_print(dest);
547 printf("'.\n");
548 stype_note_error(stype);
549 break;
550 case tic_ignore:
551 assert(b_false);
552 }
553
554 return expr;
555
556failure:
557 printf("Error: Cannot convert ");
558 tdata_item_print(src);
559 printf(" to ");
560 tdata_item_print(dest);
561 printf(".\n");
562
563 stype_note_error(stype);
564 return expr;
565}
566
567/** Return a boolean type item */
568tdata_item_t *stype_boolean_titem(stype_t *stype)
569{
570 tdata_item_t *titem;
571 tdata_primitive_t *tprimitive;
572
573 (void) stype;
574
575 titem = tdata_item_new(tic_tprimitive);
576 tprimitive = tdata_primitive_new(tpc_bool);
577 titem->u.tprimitive = tprimitive;
578
579 return titem;
580}
581
582/** Find a local variable in the current function. */
583stree_vdecl_t *stype_local_vars_lookup(stype_t *stype, sid_t name)
584{
585 stype_proc_vr_t *proc_vr;
586 stype_block_vr_t *block_vr;
587 stree_vdecl_t *vdecl;
588 list_node_t *node;
589
590 proc_vr = stype->proc_vr;
591 node = list_last(&proc_vr->block_vr);
592
593 /* Walk through all block visit records. */
594 while (node != NULL) {
595 block_vr = list_node_data(node, stype_block_vr_t *);
596 vdecl = intmap_get(&block_vr->vdecls, name);
597 if (vdecl != NULL)
598 return vdecl;
599
600 node = list_prev(&proc_vr->block_vr, node);
601 }
602
603 /* No match */
604 return NULL;
605}
606
607/** Find argument of the current procedure. */
608stree_proc_arg_t *stype_proc_args_lookup(stype_t *stype, sid_t name)
609{
610 stype_proc_vr_t *proc_vr;
611
612 stree_symbol_t *outer_sym;
613 stree_fun_t *fun;
614 stree_prop_t *prop;
615
616 list_t *args;
617 list_node_t *arg_node;
618 stree_proc_arg_t *varg;
619 stree_proc_arg_t *arg;
620 stree_proc_arg_t *setter_arg;
621
622 proc_vr = stype->proc_vr;
623 outer_sym = proc_vr->proc->outer_symbol;
624
625 setter_arg = NULL;
626
627#ifdef DEBUG_TYPE_TRACE
628 printf("Look for argument named '%s'.\n", strtab_get_str(name));
629#endif
630
631 switch (outer_sym->sc) {
632 case sc_fun:
633 fun = symbol_to_fun(outer_sym);
634 assert(fun != NULL);
635 args = &fun->args;
636 varg = fun->varg;
637 break;
638 case sc_prop:
639 prop = symbol_to_prop(outer_sym);
640 assert(prop != NULL);
641 args = &prop->args;
642 varg = prop->varg;
643
644 /* If we are in a setter, look also at setter argument. */
645 if (prop->setter == proc_vr->proc)
646 setter_arg = prop->setter_arg;
647 break;
648 default:
649 assert(b_false);
650 }
651
652 arg_node = list_first(args);
653 while (arg_node != NULL) {
654 arg = list_node_data(arg_node, stree_proc_arg_t *);
655 if (arg->name->sid == name) {
656 /* Match */
657#ifdef DEBUG_TYPE_TRACE
658 printf("Found argument.\n");
659#endif
660 return arg;
661 }
662
663 arg_node = list_next(args, arg_node);
664 }
665
666 /* Variadic argument */
667 if (varg != NULL && varg->name->sid == name) {
668#ifdef DEBUG_TYPE_TRACE
669 printf("Found variadic argument.\n");
670#endif
671 return varg;
672}
673
674 /* Setter argument */
675 if (setter_arg != NULL && setter_arg->name->sid == name) {
676#ifdef DEBUG_TYPE_TRACE
677 printf("Found setter argument.\n");
678#endif
679 return setter_arg;
680
681 }
682
683#ifdef DEBUG_TYPE_TRACE
684 printf("Not found.\n");
685#endif
686 /* No match */
687 return NULL;
688}
689
690/** Note a static typing error that has been immediately recovered. */
691void stype_note_error(stype_t *stype)
692{
693 stype->error = b_true;
694}
695
696/** Construct a special type item for recovery. */
697tdata_item_t *stype_recovery_titem(stype_t *stype)
698{
699 tdata_item_t *titem;
700
701 (void) stype;
702
703 titem = tdata_item_new(tic_ignore);
704 return titem;
705}
706
707/** Get current block visit record. */
708stype_block_vr_t *stype_get_current_block_vr(stype_t *stype)
709{
710 list_node_t *node;
711
712 node = list_last(&stype->proc_vr->block_vr);
713 return list_node_data(node, stype_block_vr_t *);
714}
715
716stype_proc_vr_t *stype_proc_vr_new(void)
717{
718 stype_proc_vr_t *proc_vr;
719
720 proc_vr = calloc(1, sizeof(stype_proc_vr_t));
721 if (proc_vr == NULL) {
722 printf("Memory allocation failed.\n");
723 exit(1);
724 }
725
726 return proc_vr;
727}
728
729stype_block_vr_t *stype_block_vr_new(void)
730{
731 stype_block_vr_t *block_vr;
732
733 block_vr = calloc(1, sizeof(stype_block_vr_t));
734 if (block_vr == NULL) {
735 printf("Memory allocation failed.\n");
736 exit(1);
737 }
738
739 return block_vr;
740}
Note: See TracBrowser for help on using the repository browser.