source: mainline/uspace/app/sbi/src/stype.c@ 23de644

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

Update SBI to rev. 174.

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