| // Copyright 2009 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| #include "go.h" | |
| static Node* sw1(Node*, Node*); | |
| static Node* sw2(Node*, Node*); | |
| static Node* sw3(Node*, Node*); | |
| static Node* curfn; | |
| void | |
| walk(Node *fn) | |
| { | |
| curfn = fn; | |
| walktype(fn->nbody, 1); | |
| } | |
| void | |
| walktype(Node *n, int top) | |
| { | |
| Node *t, *r; | |
| Sym *s; | |
| long lno; | |
| int et; | |
| /* | |
| * walk the whole tree of the body of a function. | |
| * the types expressions are calculated. | |
| * compile-time constants are evaluated. | |
| */ | |
| lno = dynlineno; | |
| loop: | |
| if(n == N) | |
| goto ret; | |
| if(n->op != ONAME) | |
| dynlineno = n->lineno; // for diagnostics | |
| t = N; | |
| et = Txxx; | |
| switch(n->op) { | |
| default: | |
| fatal("walktype: switch 1 unknown op %N", n); | |
| goto ret; | |
| case OPANIC: | |
| case OPRINT: | |
| walktype(n->left, 0); | |
| prcompat(&n->left); | |
| goto ret; | |
| case OLITERAL: | |
| n->addable = 1; | |
| ullmancalc(n); | |
| goto ret; | |
| case ONAME: | |
| n->addable = 1; | |
| ullmancalc(n); | |
| if(n->type == N) { | |
| s = n->sym; | |
| if(s->undef == 0) { | |
| yyerror("walktype: %N undeclared", n); | |
| s->undef = 1; | |
| } | |
| } | |
| goto ret; | |
| case OLIST: | |
| walktype(n->left, top); | |
| n = n->right; | |
| goto loop; | |
| case OFOR: | |
| if(!top) | |
| goto nottop; | |
| walktype(n->ninit, 1); | |
| walktype(n->ntest, 1); | |
| walktype(n->nincr, 1); | |
| n = n->nbody; | |
| goto loop; | |
| case OSWITCH: | |
| if(!top) | |
| goto nottop; | |
| if(n->ntest == N) | |
| n->ntest = booltrue; | |
| walktype(n->ninit, 1); | |
| walktype(n->ntest, 1); | |
| walktype(n->nbody, 1); | |
| // find common type | |
| if(n->ntest->type == N) | |
| n->ntest->type = walkswitch(n->ntest, n->nbody, sw1); | |
| // if that fails pick a type | |
| if(n->ntest->type == N) | |
| n->ntest->type = walkswitch(n->ntest, n->nbody, sw2); | |
| // set the type on all literals | |
| if(n->ntest->type != N) | |
| walkswitch(n->ntest, n->nbody, sw3); | |
| n = n->nincr; | |
| goto loop; | |
| case OEMPTY: | |
| if(!top) | |
| goto nottop; | |
| goto ret; | |
| case OIF: | |
| if(!top) | |
| goto nottop; | |
| walktype(n->ninit, 1); | |
| walktype(n->ntest, 1); | |
| walktype(n->nelse, 1); | |
| n = n->nbody; | |
| goto loop; | |
| case OCALL: | |
| case OCALLPTR: | |
| case OCALLMETH: | |
| case OCALLINTER: | |
| walktype(n->left, 0); | |
| if(n->left == N) | |
| goto ret; | |
| t = n->left->type; | |
| if(t == N) | |
| goto ret; | |
| if(n->left->op == ODOTMETH) | |
| n->op = OCALLMETH; | |
| if(n->left->op == ODOTINTER) | |
| n->op = OCALLINTER; | |
| if(t->etype == TPTR) { | |
| t = t->type; | |
| n->op = OCALLPTR; | |
| } | |
| if(t->etype != TFUNC) { | |
| yyerror("call of a non-function %T", t); | |
| goto ret; | |
| } | |
| n->type = *getoutarg(t); | |
| switch(t->outtuple) { | |
| default: | |
| n->kaka = PCALL_MULTI; | |
| if(!top) | |
| yyerror("function call must be single valued (%d)", et); | |
| break; | |
| case 0: | |
| n->kaka = PCALL_NIL; | |
| break; | |
| case 1: | |
| n->kaka = PCALL_SINGLE; | |
| n->type = n->type->type->type; | |
| break; | |
| } | |
| r = n->right; | |
| walktype(r, 0); | |
| ascompatte(n->op, getinarg(t), &n->right); | |
| goto ret; | |
| case OAS: | |
| if(!top) | |
| goto nottop; | |
| n->kaka = PAS_SINGLE; | |
| r = n->left; | |
| if(r != N && r->op == OLIST) | |
| n->kaka = PAS_MULTI; | |
| walktype(r, 0); | |
| r = n->right; | |
| if(r == N) | |
| goto ret; | |
| if(r->op == OCALL && n->kaka == PAS_MULTI) { | |
| walktype(r, 1); | |
| if(r->kaka == PCALL_MULTI) { | |
| ascompatet(n->op, &n->left, &r->type); | |
| n->kaka = PAS_CALLM; | |
| goto ret; | |
| } | |
| } | |
| walktype(n->right, 0); | |
| ascompatee(n->op, &n->left, &n->right); | |
| if(n->kaka == PAS_SINGLE) { | |
| t = n->right->type; | |
| if(t != N && t->etype == TSTRUCT) | |
| n->kaka = PAS_STRUCT; | |
| } | |
| goto ret; | |
| case OBREAK: | |
| case OCONTINUE: | |
| case OGOTO: | |
| case OLABEL: | |
| goto ret; | |
| case OXCASE: | |
| yyerror("case statement out of place"); | |
| n->op = OCASE; | |
| case OCASE: | |
| n = n->left; | |
| goto loop; | |
| case OXFALL: | |
| yyerror("fallthrough statement out of place"); | |
| n->op = OFALL; | |
| case OFALL: | |
| goto ret; | |
| case OCONV: | |
| walktype(n->left, 0); | |
| if(n->left == N) | |
| goto ret; | |
| convlit(n->left, n->type); | |
| if(eqtype(n->type, n->left->type, 0)) | |
| *n = *n->left; | |
| goto ret; | |
| case ORETURN: | |
| walktype(n->left, 0); | |
| ascompatte(n->op, getoutarg(curfn->type), &n->left); | |
| goto ret; | |
| case ONOT: | |
| walktype(n->left, 0); | |
| if(n->left == N || n->left->type == N) | |
| goto ret; | |
| et = n->left->type->etype; | |
| break; | |
| case OASOP: | |
| if(!top) | |
| goto nottop; | |
| case OLSH: | |
| case ORSH: | |
| case OMOD: | |
| case OAND: | |
| case OOR: | |
| case OXOR: | |
| case OANDAND: | |
| case OOROR: | |
| case OEQ: | |
| case ONE: | |
| case OLT: | |
| case OLE: | |
| case OGE: | |
| case OGT: | |
| case OADD: | |
| case OSUB: | |
| case OMUL: | |
| case ODIV: | |
| case OCAT: | |
| walktype(n->left, 0); | |
| walktype(n->right, 0); | |
| if(n->left == N || n->right == N) | |
| goto ret; | |
| convlit(n->left, n->right->type); | |
| convlit(n->right, n->left->type); | |
| evconst(n); | |
| if(n->op == OLITERAL) | |
| goto ret; | |
| if(n->left->type == N || n->right->type == N) | |
| goto ret; | |
| if(!ascompat(n->left->type, n->right->type)) | |
| goto badt; | |
| break; | |
| case OPLUS: | |
| case OMINUS: | |
| case OCOM: | |
| walktype(n->left, 0); | |
| if(n->left == N) | |
| goto ret; | |
| evconst(n); | |
| ullmancalc(n); | |
| if(n->op == OLITERAL) | |
| goto ret; | |
| break; | |
| case OLEN: | |
| walktype(n->left, 0); | |
| evconst(n); | |
| ullmancalc(n); | |
| t = n->left->type; | |
| if(t != N && t->etype == TPTR) | |
| t = t->type; | |
| if(t == N) | |
| goto ret; | |
| switch(t->etype) { | |
| default: | |
| goto badt; | |
| case TSTRING: | |
| break; | |
| } | |
| n->type = types[TINT32]; | |
| goto ret; | |
| case OINDEX: | |
| case OINDEXPTR: | |
| case OINDEXSTR: | |
| case OINDEXMAP: | |
| case OINDEXPTRMAP: | |
| walktype(n->left, 0); | |
| walktype(n->right, 0); | |
| ullmancalc(n); | |
| if(n->left == N || n->right == N) | |
| goto ret; | |
| t = n->left->type; | |
| if(t == N) | |
| goto ret; | |
| // map - left and right sides must match | |
| if(t->etype == TMAP || isptrto(t, TMAP)) { | |
| n->ullman = UINF; | |
| n->op = OINDEXMAP; | |
| if(isptrto(t, TMAP)) { | |
| n->op = OINDEXPTRMAP; | |
| t = t->type; | |
| if(t == N) | |
| goto ret; | |
| } | |
| convlit(n->right, t->down); | |
| if(!ascompat(t->down, n->right->type)) | |
| goto badt; | |
| n->type = t->type; | |
| goto ret; | |
| } | |
| // right side must be an int | |
| if(n->right->type == N) | |
| convlit(n->right, types[TINT32]); | |
| if(n->left->type == N || n->right->type == N) | |
| goto ret; | |
| if(!isint[n->right->type->etype]) | |
| goto badt; | |
| // left side is string | |
| if(isptrto(t, TSTRING)) { | |
| n->op = OINDEXSTR; | |
| n->type = types[TUINT8]; | |
| goto ret; | |
| } | |
| // left side is ptr to string | |
| if(isptrto(t, TPTR) && isptrto(t->type, TSTRING)) { | |
| n->op = OINDEXPTRSTR; | |
| n->type = types[TUINT8]; | |
| goto ret; | |
| } | |
| // left side is array | |
| if(t->etype == TPTR) { | |
| t = t->type; | |
| n->op = OINDEXPTR; | |
| } | |
| if(t->etype != TARRAY && t->etype != TDARRAY) | |
| goto badt; | |
| n->type = t->type; | |
| goto ret; | |
| case OSLICE: | |
| walkslice(n); | |
| goto ret; | |
| case ODOT: | |
| case ODOTPTR: | |
| case ODOTMETH: | |
| case ODOTINTER: | |
| walkdot(n); | |
| goto ret; | |
| case OADDR: | |
| walktype(n->left, 0); | |
| if(n->left == N) | |
| goto ret; | |
| t = n->left->type; | |
| if(t == N) | |
| goto ret; | |
| n->type = ptrto(t); | |
| goto ret; | |
| case OIND: | |
| walktype(n->left, 0); | |
| if(n->left == N) | |
| goto ret; | |
| t = n->left->type; | |
| if(t == N) | |
| goto ret; | |
| if(t->etype != TPTR) | |
| goto badt; | |
| n->type = t->type; | |
| goto ret; | |
| case ONEW: | |
| if(n->left != N) | |
| yyerror("dont know what new(,e) means"); | |
| goto ret; | |
| } | |
| /* | |
| * ======== second switch ======== | |
| */ | |
| switch(n->op) { | |
| default: | |
| fatal("walktype: switch 2 unknown op %N", n); | |
| goto ret; | |
| case OASOP: | |
| break; | |
| case ONOT: | |
| case OANDAND: | |
| case OOROR: | |
| et = n->left->type->etype; | |
| if(et != TBOOL) | |
| goto badt; | |
| t = types[TBOOL]; | |
| break; | |
| case OEQ: | |
| case ONE: | |
| et = n->left->type->etype; | |
| if(!okforeq[et]) | |
| goto badt; | |
| t = types[TBOOL]; | |
| break; | |
| case OLT: | |
| case OLE: | |
| case OGE: | |
| case OGT: | |
| et = n->left->type->etype; | |
| if(!okforadd[et]) | |
| if(!isptrto(n->left->type, TSTRING)) | |
| goto badt; | |
| t = types[TBOOL]; | |
| break; | |
| case OCAT: | |
| case OADD: | |
| if(isptrto(n->left->type, TSTRING)) { | |
| n->op = OCAT; | |
| break; | |
| } | |
| case OSUB: | |
| case OMUL: | |
| case ODIV: | |
| case OPLUS: | |
| case OMINUS: | |
| et = n->left->type->etype; | |
| if(!okforadd[et]) | |
| goto badt; | |
| break; | |
| case OLSH: | |
| case ORSH: | |
| case OAND: | |
| case OOR: | |
| case OXOR: | |
| case OMOD: | |
| case OCOM: | |
| et = n->left->type->etype; | |
| if(!okforand[et]) | |
| goto badt; | |
| break; | |
| } | |
| if(t == N) | |
| t = n->left->type; | |
| n->type = t; | |
| ullmancalc(n); | |
| goto ret; | |
| nottop: | |
| fatal("walktype: not top %O", n->op); | |
| badt: | |
| if(n->right == N) { | |
| if(n->left == N) { | |
| badtype(n->op, N, N); | |
| goto ret; | |
| } | |
| badtype(n->op, n->left->type, N); | |
| goto ret; | |
| } | |
| badtype(n->op, n->left->type, n->right->type); | |
| goto ret; | |
| ret: | |
| dynlineno = lno; | |
| } | |
| /* | |
| * return the first type | |
| */ | |
| Node* | |
| sw1(Node *c, Node *place) | |
| { | |
| if(place == N) | |
| return c->type; | |
| return place; | |
| } | |
| /* | |
| * return a suitable type | |
| */ | |
| Node* | |
| sw2(Node *c, Node *place) | |
| { | |
| return types[TINT32]; // botch | |
| } | |
| /* | |
| * check that selected type | |
| * is compat with all the cases | |
| */ | |
| Node* | |
| sw3(Node *c, Node *place) | |
| { | |
| if(place == N) | |
| return c->type; | |
| if(c->type == N) | |
| c->type = place; | |
| convlit(c, place); | |
| if(!ascompat(place, c->type)) | |
| badtype(OSWITCH, place, c->type); | |
| return place; | |
| } | |
| Node* | |
| walkswitch(Node *test, Node *body, Node*(*call)(Node*, Node*)) | |
| { | |
| Node *n, *c; | |
| Node *place; | |
| place = call(test, N); | |
| n = body; | |
| if(n->op == OLIST) | |
| n = n->left; | |
| for(; n!=N; n=n->right) { | |
| if(n->op != OCASE) | |
| fatal("walkswitch: not case %O\n", n->op); | |
| for(c=n->left; c!=N; c=c->right) { | |
| if(c->op != OLIST) { | |
| place = call(c, place); | |
| break; | |
| } | |
| place = call(c->left, place); | |
| } | |
| } | |
| return place; | |
| } | |
| int | |
| casebody(Node *n) | |
| { | |
| Node *oc, *ot, *t; | |
| Iter save; | |
| /* | |
| * look to see if statements at top level have | |
| * case labels attached to them. convert the illegal | |
| * ops XFALL and XCASE into legal ops FALL and CASE. | |
| * all unconverted ops will thus be caught as illegal | |
| */ | |
| oc = N; // last case statement | |
| ot = N; // last statement (look for XFALL) | |
| t = listfirst(&save, &n); | |
| if(t->op != OXCASE) | |
| return 0; | |
| loop: | |
| if(t == N) { | |
| if(oc == N) | |
| return 0; | |
| return 1; | |
| } | |
| if(t->op == OXCASE) { | |
| /* rewrite and link top level cases */ | |
| t->op = OCASE; | |
| if(oc != N) | |
| oc->right = t; | |
| oc = t; | |
| /* rewrite top fall that preceed case */ | |
| if(ot != N && ot->op == OXFALL) | |
| ot->op = OFALL; | |
| } | |
| /* if first statement is not case then return 0 */ | |
| if(oc == N) | |
| return 0; | |
| ot = t; | |
| t = listnext(&save); | |
| goto loop; | |
| } | |
| /* | |
| * allowable type combinations for | |
| * normal binary operations. | |
| */ | |
| Node* | |
| lookdot(Node *n, Node *t, int d) | |
| { | |
| Node *r, *f, *c; | |
| Sym *s; | |
| int o; | |
| r = N; | |
| s = n->sym; | |
| if(d > 0) | |
| goto deep; | |
| o = 0; | |
| for(f=t->type; f!=N; f=f->down) { | |
| f->kaka = o; | |
| o++; | |
| if(f->sym == S) | |
| continue; | |
| if(f->sym != s) | |
| continue; | |
| if(r != N) { | |
| yyerror("ambiguous DOT reference %s", s->name); | |
| break; | |
| } | |
| r = f; | |
| } | |
| return r; | |
| deep: | |
| /* deeper look after shallow failed */ | |
| for(f=t->type; f!=N; f=f->down) { | |
| // only look at unnamed sub-structures | |
| // BOTCH no such thing -- all are assigned temp names | |
| if(f->sym != S) | |
| continue; | |
| c = f->type; | |
| if(c->etype != TSTRUCT) | |
| continue; | |
| c = lookdot(n, c, d-1); | |
| if(c == N) | |
| continue; | |
| if(r != N) { | |
| yyerror("ambiguous unnamed DOT reference %s", s->name); | |
| break; | |
| } | |
| r = c; | |
| } | |
| return r; | |
| } | |
| void | |
| walkdot(Node *n) | |
| { | |
| Node *t, *f; | |
| int i; | |
| if(n->left == N || n->right == N) | |
| return; | |
| walktype(n->left, 0); | |
| if(n->right->op != ONAME) { | |
| yyerror("rhs of . must be a name"); | |
| return; | |
| } | |
| t = n->left->type; | |
| if(t == N) | |
| return; | |
| if(t->etype == TPTR) { | |
| t = t->type; | |
| if(t == N) | |
| return; | |
| n->op = ODOTPTR; | |
| } | |
| if(n->right->op != ONAME) | |
| fatal("walkdot: not name %O", n->right->op); | |
| switch(t->etype) { | |
| default: | |
| badtype(ODOT, t, N); | |
| return; | |
| case TSTRUCT: | |
| case TINTER: | |
| for(i=0; i<5; i++) { | |
| f = lookdot(n->right, t, i); | |
| if(f != N) | |
| break; | |
| } | |
| if(f == N) { | |
| yyerror("undefined DOT reference %N", n->right); | |
| break; | |
| } | |
| n->right = f->nname; // substitute real name | |
| n->type = f->type; | |
| if(n->type->etype == TFUNC) { | |
| n->op = ODOTMETH; | |
| if(t->etype == TINTER) { | |
| n->op = ODOTINTER; | |
| n->kaka = f->kaka; | |
| } | |
| } | |
| break; | |
| } | |
| } | |
| void | |
| walkslice(Node *n) | |
| { | |
| Node *l, *r; | |
| if(n->left == N || n->right == N) | |
| return; | |
| if(n->right->op != OLIST) | |
| fatal("slice not a list"); | |
| walktype(n->left, 0); | |
| if(isptrto(n->left->type, TSTRING)) { | |
| n->op = OSLICESTR; | |
| goto ok; | |
| } | |
| if(isptrto(n->left->type->type, TPTR) && isptrto(n->left->type->type, TSTRING)) { | |
| n->op = OSLICEPTRSTR; | |
| goto ok; | |
| } | |
| badtype(OSLICE, n->left->type, N); | |
| return; | |
| ok: | |
| // check for type errors | |
| walktype(n->right, 0); | |
| l = n->right->left; | |
| r = n->right->right; | |
| convlit(l, types[TINT32]); | |
| convlit(r, types[TINT32]); | |
| if(l == N || r == N || | |
| l->type == N || r->type == N) | |
| return; | |
| if(!isint[l->type->etype] || !isint[l->type->etype]) { | |
| badtype(OSLICE, l->type, r->type); | |
| return; | |
| } | |
| // now convert to int32 | |
| n->right->left = nod(OCONV, n->right->left, N); | |
| n->right->left->type = types[TINT32]; | |
| n->right->right = nod(OCONV, n->right->right, N); | |
| n->right->right->type = types[TINT32]; | |
| walktype(n->right, 0); | |
| n->type = n->left->type; | |
| } | |
| /* | |
| * test tuple type list against each other | |
| * called in four contexts | |
| * 1. a,b = c,d ...ee | |
| * 2. a,b = fn() ...et | |
| * 3. call(fn()) ...tt | |
| * 4. call(a,b) ...te | |
| */ | |
| void | |
| ascompatee(int op, Node **nl, Node **nr) | |
| { | |
| Node *l, *r; | |
| Iter savel, saver; | |
| int sa, na; | |
| l = listfirst(&savel, nl); | |
| r = listfirst(&saver, nr); | |
| na = 0; // number of assignments - looking for multi | |
| sa = 0; // one of the assignments is a structure assignment | |
| loop: | |
| if(l == N || r == N) { | |
| if(l != r) | |
| yyerror("error in shape across assignment"); | |
| if(sa != 0 && na > 1) | |
| yyerror("cant do multi-struct assignments"); | |
| return; | |
| } | |
| convlit(r, l->type); | |
| if(!ascompat(l->type, r->type)) { | |
| badtype(op, l->type, r->type); | |
| return; | |
| } | |
| if(l->type != N && l->type->etype == TSTRUCT) | |
| sa = 1; | |
| l = listnext(&savel); | |
| r = listnext(&saver); | |
| na++; | |
| goto loop; | |
| } | |
| void | |
| ascompatet(int op, Node **nl, Node **nr) | |
| { | |
| Node *l, *r; | |
| Iter savel, saver; | |
| l = listfirst(&savel, nl); | |
| r = structfirst(&saver, nr); | |
| loop: | |
| if(l == N || r == N) { | |
| if(l != r) | |
| yyerror("error in shape across assignment"); | |
| return; | |
| } | |
| if(!ascompat(l->type, r->type)) { | |
| badtype(op, l->type, r->type); | |
| return; | |
| } | |
| l = listnext(&savel); | |
| r = structnext(&saver); | |
| goto loop; | |
| } | |
| void | |
| ascompatte(int op, Node **nl, Node **nr) | |
| { | |
| Node *l, *r; | |
| Iter savel, saver; | |
| l = structfirst(&savel, nl); | |
| r = listfirst(&saver, nr); | |
| loop: | |
| if(l == N || r == N) { | |
| if(l != r) | |
| yyerror("error in shape across assignment"); | |
| return; | |
| } | |
| convlit(r, l->type); | |
| if(!ascompat(l->type, r->type)) { | |
| badtype(op, l->type, r->type); | |
| return; | |
| } | |
| l = structnext(&savel); | |
| r = listnext(&saver); | |
| goto loop; | |
| } | |
| void | |
| ascompattt(int op, Node **nl, Node **nr) | |
| { | |
| Node *l, *r; | |
| Iter savel, saver; | |
| l = structfirst(&savel, nl); | |
| r = structfirst(&saver, nr); | |
| loop: | |
| if(l == N || r == N) { | |
| if(l != r) | |
| yyerror("error in shape across assignment"); | |
| return; | |
| } | |
| if(!ascompat(l->type, r->type)) { | |
| badtype(op, l->type, r->type); | |
| return; | |
| } | |
| l = structnext(&savel); | |
| r = structnext(&saver); | |
| goto loop; | |
| } | |
| /* | |
| * can we assign var of type t2 to var of type t1 | |
| */ | |
| int | |
| ascompat(Node *t1, Node *t2) | |
| { | |
| if(eqtype(t1, t2, 0)) | |
| return 1; | |
| // if(eqtype(t1, nilptr, 0)) | |
| // return 1; | |
| // if(eqtype(t2, nilptr, 0)) | |
| // return 1; | |
| if(isinter(t1)) | |
| if(isptrto(t2, TSTRUCT) || isinter(t2)) | |
| return 1; | |
| if(isinter(t2)) | |
| if(isptrto(t1, TSTRUCT)) | |
| return 1; | |
| return 0; | |
| } | |
| void | |
| prcompat(Node **n) | |
| { | |
| Node *l, *t; | |
| Iter save; | |
| int w; | |
| l = listfirst(&save, n); | |
| loop: | |
| if(l == N) | |
| return; | |
| t = N; | |
| w = whatis(l); | |
| switch(w) { | |
| default: | |
| badtype((*n)->op, l->type, N); | |
| break; | |
| case Wtint: | |
| case Wtfloat: | |
| case Wtbool: | |
| case Wtstr: | |
| break; | |
| case Wlitint: | |
| t = types[TINT32]; | |
| break; | |
| case Wlitfloat: | |
| t = types[TFLOAT64]; | |
| break; | |
| case Wlitbool: | |
| t = types[TBOOL]; | |
| break; | |
| case Wlitstr: | |
| t = types[TSTRING]; | |
| break; | |
| } | |
| if(t != N) | |
| convlit(l, t); | |
| l = listnext(&save); | |
| goto loop; | |
| } |