diff options
Diffstat (limited to 'src/old/c/walk.c')
-rw-r--r-- | src/old/c/walk.c | 978 |
1 files changed, 978 insertions, 0 deletions
diff --git a/src/old/c/walk.c b/src/old/c/walk.c new file mode 100644 index 000000000..a8552e512 --- /dev/null +++ b/src/old/c/walk.c @@ -0,0 +1,978 @@ +// 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; +} |