summaryrefslogtreecommitdiff
path: root/src/old/c/walk.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/old/c/walk.c')
-rw-r--r--src/old/c/walk.c978
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;
+}