| // 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* walkprint(Node*, NodeList**); |
| static Node* conv(Node*, Type*); |
| static Node* mapfn(char*, Type*); |
| static Node* makenewvar(Type*, NodeList**, Node**); |
| enum |
| { |
| Inone, |
| I2T, |
| I2T2, |
| I2I, |
| I2Ix, |
| I2I2, |
| T2I, |
| I2Isame, |
| E2T, |
| E2T2, |
| E2I, |
| E2I2, |
| I2E, |
| I2E2, |
| T2E, |
| E2Esame, |
| }; |
| |
| // can this code branch reach the end |
| // without an undcontitional RETURN |
| // this is hard, so it is conservative |
| int |
| walkret(NodeList *l) |
| { |
| Node *n; |
| |
| loop: |
| while(l && l->next) |
| l = l->next; |
| if(l == nil) |
| return 1; |
| |
| // at this point, we have the last |
| // statement of the function |
| n = l->n; |
| switch(n->op) { |
| case OBLOCK: |
| l = n->list; |
| goto loop; |
| |
| case OGOTO: |
| case ORETURN: |
| case OPANIC: |
| case OPANICN: |
| return 0; |
| break; |
| } |
| |
| // all other statements |
| // will flow to the end |
| return 1; |
| } |
| |
| void |
| walk(Node *fn) |
| { |
| char s[50]; |
| NodeList *l; |
| Node *n; |
| int lno; |
| |
| curfn = fn; |
| if(debug['W']) { |
| snprint(s, sizeof(s), "\nbefore %S", curfn->nname->sym); |
| dumplist(s, curfn->nbody); |
| } |
| if(curfn->type->outtuple) |
| if(walkret(curfn->nbody)) |
| yyerror("function ends without a return statement"); |
| typechecklist(curfn->nbody, Etop); |
| lno = lineno; |
| for(l=fn->dcl; l; l=l->next) { |
| n = l->n; |
| if(n->op != ONAME || n->class != PAUTO) |
| continue; |
| lineno = n->lineno; |
| typecheck(&n, Erv | Easgn); // only needed for unused variables |
| if(!n->used && n->sym->name[0] != '&' && !nsyntaxerrors) |
| yyerror("%S declared and not used", n->sym); |
| } |
| lineno = lno; |
| if(nerrors != 0) |
| return; |
| walkstmtlist(curfn->nbody); |
| if(debug['W']) { |
| snprint(s, sizeof(s), "after walk %S", curfn->nname->sym); |
| dumplist(s, curfn->nbody); |
| } |
| heapmoves(); |
| if(debug['W'] && curfn->enter != nil) { |
| snprint(s, sizeof(s), "enter %S", curfn->nname->sym); |
| dumplist(s, curfn->enter); |
| } |
| } |
| |
| void |
| gettype(Node **np, NodeList **init) |
| { |
| if(debug['W']) |
| dump("\nbefore gettype", *np); |
| typecheck(np, Erv); |
| if(debug['W']) |
| dump("after gettype", *np); |
| } |
| |
| void |
| walkdeflist(NodeList *l) |
| { |
| for(; l; l=l->next) |
| walkdef(l->n); |
| } |
| |
| void |
| walkdef(Node *n) |
| { |
| int lno, maplineno, embedlineno; |
| NodeList *init; |
| Node *e; |
| Type *t; |
| |
| lno = lineno; |
| setlineno(n); |
| |
| if(n->op == ONONAME) { |
| if(!n->diag) { |
| n->diag = 1; |
| yyerror("undefined: %S", n->sym); |
| } |
| return; |
| } |
| |
| if(n->walkdef == 1) |
| return; |
| if(n->walkdef == 2) { |
| // TODO(rsc): better loop message |
| fatal("loop"); |
| } |
| n->walkdef = 2; |
| |
| if(n->type != T || n->sym == S) // builtin or no name |
| goto ret; |
| |
| init = nil; |
| switch(n->op) { |
| default: |
| fatal("walkdef %O", n->op); |
| |
| case OLITERAL: |
| if(n->ntype != N) { |
| typecheck(&n->ntype, Etype); |
| n->type = n->ntype->type; |
| n->ntype = N; |
| if(n->type == T) { |
| n->diag = 1; |
| goto ret; |
| } |
| } |
| e = n->defn; |
| n->defn = N; |
| if(e == N) { |
| lineno = n->lineno; |
| dump("walkdef nil defn", n); |
| yyerror("xxx"); |
| } |
| typecheck(&e, Erv | Eiota); |
| if(e->type != T && e->op != OLITERAL) { |
| yyerror("const initializer must be constant"); |
| goto ret; |
| } |
| t = n->type; |
| if(t != T) |
| convlit(&e, t); |
| n->val = e->val; |
| n->type = e->type; |
| break; |
| |
| case ONAME: |
| if(n->ntype != N) { |
| typecheck(&n->ntype, Etype); |
| n->type = n->ntype->type; |
| if(n->type == T) { |
| n->diag = 1; |
| goto ret; |
| } |
| } |
| if(n->type != T) |
| break; |
| if(n->defn == N) |
| fatal("var without type, init: %S", n->sym); |
| if(n->defn->op == ONAME) { |
| typecheck(&n->defn, Erv); |
| n->type = n->defn->type; |
| break; |
| } |
| typecheck(&n->defn, Etop); // fills in n->type |
| break; |
| |
| case OTYPE: |
| n->walkdef = 1; |
| n->type = typ(TFORW); |
| n->type->sym = n->sym; |
| n->typecheck = 1; |
| typecheck(&n->ntype, Etype); |
| if((t = n->ntype->type) == T) { |
| n->diag = 1; |
| goto ret; |
| } |
| |
| // copy new type and clear fields |
| // that don't come along |
| maplineno = n->type->maplineno; |
| embedlineno = n->type->embedlineno; |
| *n->type = *t; |
| t = n->type; |
| t->sym = n->sym; |
| t->local = n->local; |
| t->vargen = n->vargen; |
| t->siggen = 0; |
| t->printed = 0; |
| t->method = nil; |
| t->nod = N; |
| t->printed = 0; |
| t->deferwidth = 0; |
| |
| // double-check use of type as map key |
| // TODO(rsc): also use of type as receiver? |
| if(maplineno) { |
| lineno = maplineno; |
| maptype(n->type, types[TBOOL]); |
| } |
| if(embedlineno) { |
| lineno = embedlineno; |
| if(isptr[t->etype]) |
| yyerror("embedded type cannot be a pointer"); |
| } |
| break; |
| |
| case OPACK: |
| // nothing to see here |
| break; |
| } |
| |
| ret: |
| lineno = lno; |
| n->walkdef = 1; |
| } |
| |
| void |
| walkstmtlist(NodeList *l) |
| { |
| for(; l; l=l->next) |
| walkstmt(&l->n); |
| } |
| |
| static int |
| samelist(NodeList *a, NodeList *b) |
| { |
| for(; a && b; a=a->next, b=b->next) |
| if(a->n != b->n) |
| return 0; |
| return a == b; |
| } |
| |
| |
| void |
| walkstmt(Node **np) |
| { |
| NodeList *init; |
| NodeList *ll, *rl; |
| int cl, lno; |
| Node *n; |
| |
| n = *np; |
| if(n == N) |
| return; |
| |
| lno = lineno; |
| setlineno(n); |
| |
| switch(n->op) { |
| default: |
| if(n->op == ONAME) |
| yyerror("%S is not a top level statement", n->sym); |
| else |
| yyerror("%O is not a top level statement", n->op); |
| dump("nottop", n); |
| break; |
| |
| case OAPPENDSTR: |
| case OASOP: |
| case OAS: |
| case OAS2: |
| case OAS2DOTTYPE: |
| case OAS2RECV: |
| case OAS2FUNC: |
| case OAS2MAPW: |
| case OAS2MAPR: |
| case OCLOSE: |
| case OCLOSED: |
| case OCOPY: |
| case OCALLMETH: |
| case OCALLINTER: |
| case OCALL: |
| case OCALLFUNC: |
| case OSEND: |
| case ORECV: |
| case OPRINT: |
| case OPRINTN: |
| case OPANIC: |
| case OPANICN: |
| case OEMPTY: |
| if(n->typecheck == 0) |
| fatal("missing typecheck"); |
| init = n->ninit; |
| n->ninit = nil; |
| walkexpr(&n, &init); |
| n->ninit = concat(init, n->ninit); |
| break; |
| |
| case OBREAK: |
| case ODCL: |
| case OCONTINUE: |
| case OFALL: |
| case OGOTO: |
| case OLABEL: |
| case ODCLCONST: |
| case ODCLTYPE: |
| break; |
| |
| case OBLOCK: |
| walkstmtlist(n->list); |
| break; |
| |
| case OXCASE: |
| yyerror("case statement out of place"); |
| n->op = OCASE; |
| case OCASE: |
| walkstmt(&n->right); |
| break; |
| |
| case ODEFER: |
| hasdefer = 1; |
| walkexpr(&n->left, &n->ninit); |
| break; |
| |
| case OFOR: |
| walkstmtlist(n->ninit); |
| if(n->ntest != N) { |
| walkstmtlist(n->ntest->ninit); |
| walkexpr(&n->ntest, &n->ninit); |
| } |
| walkstmt(&n->nincr); |
| walkstmtlist(n->nbody); |
| break; |
| |
| case OIF: |
| walkstmtlist(n->ninit); |
| walkexpr(&n->ntest, &n->ninit); |
| walkstmtlist(n->nbody); |
| walkstmtlist(n->nelse); |
| break; |
| |
| case OPROC: |
| walkexpr(&n->left, &n->ninit); |
| break; |
| |
| case ORETURN: |
| walkexprlist(n->list, &n->ninit); |
| if(curfn->type->outnamed && count(n->list) != 1) { |
| if(n->list == nil) { |
| // print("special return\n"); |
| break; |
| } |
| // assign to the function out parameters, |
| // so that reorder3 can fix up conflicts |
| rl = nil; |
| for(ll=curfn->dcl; ll != nil; ll=ll->next) { |
| cl = ll->n->class & ~PHEAP; |
| if(cl == PAUTO) |
| break; |
| if(cl == PPARAMOUT) |
| rl = list(rl, ll->n); |
| } |
| if(samelist(rl, n->list)) { |
| // special return in disguise |
| n->list = nil; |
| break; |
| } |
| ll = ascompatee(n->op, rl, n->list, &n->ninit); |
| n->list = reorder3(ll); |
| break; |
| } |
| ll = ascompatte(n->op, getoutarg(curfn->type), n->list, 1, &n->ninit); |
| n->list = reorder4(ll); |
| break; |
| |
| case OSELECT: |
| walkselect(n); |
| break; |
| |
| case OSWITCH: |
| walkswitch(n); |
| break; |
| |
| case ORANGE: |
| walkrange(n); |
| break; |
| |
| case OXFALL: |
| yyerror("fallthrough statement out of place"); |
| n->op = OFALL; |
| break; |
| } |
| |
| *np = n; |
| } |
| |
| |
| /* |
| * walk the whole tree of the body of an |
| * expression or simple statement. |
| * the types expressions are calculated. |
| * compile-time constants are evaluated. |
| * complex side effects like statements are appended to init |
| */ |
| |
| void |
| walkexprlist(NodeList *l, NodeList **init) |
| { |
| for(; l; l=l->next) |
| walkexpr(&l->n, init); |
| } |
| |
| void |
| walkexpr(Node **np, NodeList **init) |
| { |
| Node *r, *l; |
| NodeList *ll, *lr; |
| Type *t; |
| int et; |
| int32 lno; |
| Node *n, *fn; |
| |
| n = *np; |
| |
| if(n == N) |
| return; |
| |
| if(init == &n->ninit) { |
| // not okay to use n->ninit when walking n, |
| // because we might replace n with some other node |
| // and would lose the init list. |
| fatal("walkexpr init == &n->ninit"); |
| } |
| |
| // annoying case - not typechecked |
| if(n->op == OKEY) { |
| walkexpr(&n->left, init); |
| walkexpr(&n->right, init); |
| return; |
| } |
| |
| lno = setlineno(n); |
| |
| if(debug['w'] > 1) |
| dump("walk-before", n); |
| |
| if(n->typecheck != 1) { |
| dump("missed typecheck", n); |
| fatal("missed typecheck"); |
| } |
| |
| t = T; |
| et = Txxx; |
| |
| switch(n->op) { |
| default: |
| dump("walk", n); |
| fatal("walkexpr: switch 1 unknown op %N", n); |
| goto ret; |
| |
| case OTYPE: |
| case ONONAME: |
| case OINDREG: |
| case OEMPTY: |
| goto ret; |
| |
| case ONOT: |
| case OMINUS: |
| case OPLUS: |
| case OCOM: |
| case OLEN: |
| case OCAP: |
| case ODOT: |
| case ODOTPTR: |
| case ODOTMETH: |
| case ODOTINTER: |
| case OIND: |
| walkexpr(&n->left, init); |
| goto ret; |
| |
| case OLSH: |
| case ORSH: |
| case OAND: |
| case OOR: |
| case OXOR: |
| case OANDAND: |
| case OOROR: |
| case OSUB: |
| case OMUL: |
| case OEQ: |
| case ONE: |
| case OLT: |
| case OLE: |
| case OGE: |
| case OGT: |
| case OADD: |
| walkexpr(&n->left, init); |
| walkexpr(&n->right, init); |
| goto ret; |
| |
| case OPRINT: |
| case OPRINTN: |
| case OPANIC: |
| case OPANICN: |
| walkexprlist(n->list, init); |
| n = walkprint(n, init); |
| goto ret; |
| |
| case OLITERAL: |
| n->addable = 1; |
| goto ret; |
| |
| case ONAME: |
| if(!(n->class & PHEAP) && n->class != PPARAMREF) |
| n->addable = 1; |
| goto ret; |
| |
| case OCALLINTER: |
| t = n->left->type; |
| if(n->list && n->list->n->op == OAS) |
| goto ret; |
| walkexpr(&n->left, init); |
| walkexprlist(n->list, init); |
| ll = ascompatte(n->op, getinarg(t), n->list, 0, init); |
| n->list = reorder1(ll); |
| goto ret; |
| |
| case OCALLFUNC: |
| t = n->left->type; |
| if(n->list && n->list->n->op == OAS) |
| goto ret; |
| walkexpr(&n->left, init); |
| walkexprlist(n->list, init); |
| ll = ascompatte(n->op, getinarg(t), n->list, 0, init); |
| n->list = reorder1(ll); |
| if(isselect(n)) { |
| // special prob with selectsend and selectrecv: |
| // if chan is nil, they don't know big the channel |
| // element is and therefore don't know how to find |
| // the output bool, so we clear it before the call. |
| Node *b; |
| b = nodbool(0); |
| lr = ascompatte(n->op, getoutarg(t), list1(b), 0, init); |
| n->list = concat(n->list, lr); |
| } |
| goto ret; |
| |
| case OCALLMETH: |
| t = n->left->type; |
| if(n->list && n->list->n->op == OAS) |
| goto ret; |
| walkexpr(&n->left, init); |
| walkexprlist(n->list, init); |
| ll = ascompatte(n->op, getinarg(t), n->list, 0, init); |
| lr = ascompatte(n->op, getthis(t), list1(n->left->left), 0, init); |
| ll = concat(ll, lr); |
| n->left->left = N; |
| ullmancalc(n->left); |
| n->list = reorder1(ll); |
| goto ret; |
| |
| case OAS: |
| *init = concat(*init, n->ninit); |
| n->ninit = nil; |
| walkexpr(&n->left, init); |
| if(oaslit(n, init)) |
| goto ret; |
| walkexpr(&n->right, init); |
| l = n->left; |
| r = n->right; |
| if(l == N || r == N) |
| goto ret; |
| r = ascompatee1(n->op, l, r, init); |
| if(r != N) |
| n = r; |
| goto ret; |
| |
| case OAS2: |
| as2: |
| *init = concat(*init, n->ninit); |
| n->ninit = nil; |
| walkexprlist(n->list, init); |
| walkexprlist(n->rlist, init); |
| ll = ascompatee(OAS, n->list, n->rlist, init); |
| ll = reorder3(ll); |
| n = liststmt(ll); |
| goto ret; |
| |
| case OAS2FUNC: |
| as2func: |
| // a,b,... = fn() |
| *init = concat(*init, n->ninit); |
| n->ninit = nil; |
| r = n->rlist->n; |
| walkexprlist(n->list, init); |
| walkexpr(&r, init); |
| ll = ascompatet(n->op, n->list, &r->type, 0, init); |
| n = liststmt(concat(list1(r), ll)); |
| goto ret; |
| |
| case OAS2RECV: |
| // a,b = <-c |
| *init = concat(*init, n->ninit); |
| n->ninit = nil; |
| r = n->rlist->n; |
| walkexprlist(n->list, init); |
| walkexpr(&r->left, init); |
| fn = chanfn("chanrecv2", 2, r->left->type); |
| r = mkcall1(fn, getoutargx(fn->type), init, r->left); |
| n->rlist->n = r; |
| n->op = OAS2FUNC; |
| goto as2func; |
| |
| case OAS2MAPR: |
| // a,b = m[i]; |
| *init = concat(*init, n->ninit); |
| n->ninit = nil; |
| r = n->rlist->n; |
| walkexprlist(n->list, init); |
| walkexpr(&r->left, init); |
| fn = mapfn("mapaccess2", r->left->type); |
| r = mkcall1(fn, getoutargx(fn->type), init, r->left, r->right); |
| n->rlist = list1(r); |
| n->op = OAS2FUNC; |
| goto as2func; |
| |
| case OAS2MAPW: |
| // map[] = a,b - mapassign2 |
| // a,b = m[i]; |
| *init = concat(*init, n->ninit); |
| n->ninit = nil; |
| walkexprlist(n->list, init); |
| l = n->list->n; |
| t = l->left->type; |
| n = mkcall1(mapfn("mapassign2", t), T, init, l->left, l->right, n->rlist->n, n->rlist->next->n); |
| goto ret; |
| |
| case OAS2DOTTYPE: |
| // a,b = i.(T) |
| *init = concat(*init, n->ninit); |
| n->ninit = nil; |
| r = n->rlist->n; |
| walkexprlist(n->list, init); |
| walkdottype(r, init); |
| et = ifaceas1(r->type, r->left->type, 1); |
| switch(et) { |
| case I2Isame: |
| case E2Esame: |
| n->rlist = list(list1(r->left), nodbool(1)); |
| typechecklist(n->rlist, Erv); |
| goto as2; |
| case I2E: |
| n->list = list(list1(n->right), nodbool(1)); |
| typechecklist(n->rlist, Erv); |
| goto as2; |
| case I2T: |
| et = I2T2; |
| break; |
| case I2Ix: |
| et = I2I2; |
| break; |
| case E2I: |
| et = E2I2; |
| break; |
| case E2T: |
| et = E2T2; |
| break; |
| default: |
| et = Inone; |
| break; |
| } |
| if(et == Inone) |
| break; |
| r = ifacecvt(r->type, r->left, et, init); |
| ll = ascompatet(n->op, n->list, &r->type, 0, init); |
| n = liststmt(concat(list1(r), ll)); |
| goto ret; |
| |
| case ODOTTYPE: |
| walkdottype(n, init); |
| walkconv(&n, init); |
| goto ret; |
| |
| case OCONV: |
| case OCONVNOP: |
| if(thechar == '5') { |
| if(isfloat[n->left->type->etype] && (n->type->etype == TINT64 || n->type->etype == TUINT64)) { |
| n = mkcall("float64toint64", n->type, init, conv(n->left, types[TFLOAT64])); |
| goto ret; |
| } |
| if((n->left->type->etype == TINT64 || n->left->type->etype == TUINT64) && isfloat[n->type->etype]) { |
| n = mkcall("int64tofloat64", n->type, init, conv(n->left, types[TINT64])); |
| goto ret; |
| } |
| } |
| walkexpr(&n->left, init); |
| goto ret; |
| |
| case OASOP: |
| walkexpr(&n->left, init); |
| l = n->left; |
| if(l->op == OINDEXMAP) |
| n = mapop(n, init); |
| walkexpr(&n->right, init); |
| if(n->etype == OANDNOT) { |
| n->etype = OAND; |
| n->right = nod(OCOM, n->right, N); |
| typecheck(&n->right, Erv); |
| goto ret; |
| } |
| |
| /* |
| * on 32-bit arch, rewrite 64-bit ops into l = l op r |
| */ |
| et = n->left->type->etype; |
| if(widthptr == 4 && (et == TUINT64 || et == TINT64)) { |
| l = saferef(n->left, init); |
| r = nod(OAS, l, nod(n->etype, l, n->right)); |
| typecheck(&r, Etop); |
| walkexpr(&r, init); |
| n = r; |
| } |
| goto ret; |
| |
| case OANDNOT: |
| walkexpr(&n->left, init); |
| walkexpr(&n->right, init); |
| n->op = OAND; |
| n->right = nod(OCOM, n->right, N); |
| typecheck(&n->right, Erv); |
| goto ret; |
| |
| case ODIV: |
| case OMOD: |
| /* |
| * rewrite div and mod into function calls |
| * on 32-bit architectures. |
| */ |
| walkexpr(&n->left, init); |
| walkexpr(&n->right, init); |
| et = n->left->type->etype; |
| if(widthptr > 4 || (et != TUINT64 && et != TINT64)) |
| goto ret; |
| if(et == TINT64) |
| strcpy(namebuf, "int64"); |
| else |
| strcpy(namebuf, "uint64"); |
| if(n->op == ODIV) |
| strcat(namebuf, "div"); |
| else |
| strcat(namebuf, "mod"); |
| n = mkcall(namebuf, n->type, init, |
| conv(n->left, types[et]), conv(n->right, types[et])); |
| goto ret; |
| |
| case OINDEX: |
| walkexpr(&n->left, init); |
| walkexpr(&n->right, init); |
| |
| // if range of type cannot exceed static array bound, |
| // disable bounds check |
| if(!isslice(n->left->type)) |
| if(n->right->type->width < 4) |
| if((1<<(8*n->right->type->width)) <= n->left->type->bound) |
| n->etype = 1; |
| |
| goto ret; |
| |
| case OINDEXMAP: |
| if(n->etype == 1) |
| goto ret; |
| t = n->left->type; |
| n = mkcall1(mapfn("mapaccess1", t), t->type, init, n->left, n->right); |
| goto ret; |
| |
| case ORECV: |
| walkexpr(&n->left, init); |
| walkexpr(&n->right, init); |
| n = mkcall1(chanfn("chanrecv1", 2, n->left->type), n->type, init, n->left); |
| goto ret; |
| |
| case OSLICE: |
| walkexpr(&n->left, init); |
| walkexpr(&n->right->left, init); |
| walkexpr(&n->right->right, init); |
| // dynamic slice |
| // sliceslice(old []any, lb int, hb int, width int) (ary []any) |
| // sliceslice1(old []any, lb int, width int) (ary []any) |
| t = n->type; |
| if(n->right->right != N) { |
| fn = syslook("sliceslice", 1); |
| argtype(fn, t->type); // any-1 |
| argtype(fn, t->type); // any-2 |
| n = mkcall1(fn, t, init, |
| n->left, |
| conv(n->right->left, types[TINT]), |
| conv(n->right->right, types[TINT]), |
| nodintconst(t->type->width)); |
| } else { |
| fn = syslook("sliceslice1", 1); |
| argtype(fn, t->type); // any-1 |
| argtype(fn, t->type); // any-2 |
| n = mkcall1(fn, t, init, |
| n->left, |
| conv(n->right->left, types[TINT]), |
| nodintconst(t->type->width)); |
| } |
| goto ret; |
| |
| case OSLICEARR: |
| walkexpr(&n->left, init); |
| walkexpr(&n->right->left, init); |
| walkexpr(&n->right->right, init); |
| // static slice |
| // slicearray(old *any, nel int, lb int, hb int, width int) (ary []any) |
| t = n->type; |
| fn = syslook("slicearray", 1); |
| argtype(fn, n->left->type); // any-1 |
| argtype(fn, t->type); // any-2 |
| if(n->right->right == N) |
| r = nodintconst(n->left->type->bound); |
| else |
| r = conv(n->right->right, types[TINT]); |
| n = mkcall1(fn, t, init, |
| nod(OADDR, n->left, N), nodintconst(n->left->type->bound), |
| conv(n->right->left, types[TINT]), |
| r, |
| nodintconst(t->type->width)); |
| goto ret; |
| |
| case OCONVSLICE: |
| // slicearray(old *any, nel int, lb int, hb int, width int) (ary []any) |
| fn = syslook("slicearray", 1); |
| argtype(fn, n->left->type->type); // any-1 |
| argtype(fn, n->type->type); // any-2 |
| n = mkcall1(fn, n->type, init, n->left, |
| nodintconst(n->left->type->type->bound), |
| nodintconst(0), |
| nodintconst(n->left->type->type->bound), |
| nodintconst(n->type->type->width)); |
| goto ret; |
| |
| case OADDR:; |
| Node *nvar, *nstar; |
| |
| // turn &Point(1, 2) or &[]int(1, 2) or &[...]int(1, 2) into allocation. |
| // initialize with |
| // nvar := new(*Point); |
| // *nvar = Point(1, 2); |
| // and replace expression with nvar |
| switch(n->left->op) { |
| case OARRAYLIT: |
| case OMAPLIT: |
| case OSTRUCTLIT: |
| nvar = makenewvar(n->type, init, &nstar); |
| anylit(n->left, nstar, init); |
| n = nvar; |
| goto ret; |
| } |
| |
| walkexpr(&n->left, init); |
| goto ret; |
| |
| case ONEW: |
| n = callnew(n->type->type); |
| goto ret; |
| |
| case OCMPSTR: |
| // sys_cmpstring(s1, s2) :: 0 |
| r = mkcall("cmpstring", types[TINT], init, |
| conv(n->left, types[TSTRING]), |
| conv(n->right, types[TSTRING])); |
| r = nod(n->etype, r, nodintconst(0)); |
| typecheck(&r, Erv); |
| n = r; |
| goto ret; |
| |
| case OADDSTR: |
| // sys_catstring(s1, s2) |
| n = mkcall("catstring", n->type, init, |
| conv(n->left, types[TSTRING]), |
| conv(n->right, types[TSTRING])); |
| goto ret; |
| |
| case OAPPENDSTR: |
| // s1 = sys_catstring(s1, s2) |
| if(n->etype != OADD) |
| fatal("walkasopstring: not add"); |
| r = mkcall("catstring", n->left->type, init, |
| conv(n->left, types[TSTRING]), |
| conv(n->right, types[TSTRING])); |
| r = nod(OAS, n->left, r); |
| n = r; |
| goto ret; |
| |
| case OSLICESTR: |
| // sys_slicestring(s, lb, hb) |
| if(n->right->right) { |
| n = mkcall("slicestring", n->type, init, |
| conv(n->left, types[TSTRING]), |
| conv(n->right->left, types[TINT]), |
| conv(n->right->right, types[TINT])); |
| } else { |
| n = mkcall("slicestring1", n->type, init, |
| conv(n->left, types[TSTRING]), |
| conv(n->right->left, types[TINT])); |
| } |
| goto ret; |
| |
| case OINDEXSTR: |
| // TODO(rsc): should be done in back end |
| // sys_indexstring(s, i) |
| n = mkcall("indexstring", n->type, init, |
| conv(n->left, types[TSTRING]), |
| conv(n->right, types[TINT])); |
| goto ret; |
| |
| case OCOPY: |
| fn = syslook("slicecopy", 1); |
| argtype(fn, n->left->type); |
| argtype(fn, n->right->type); |
| n = mkcall1(fn, n->type, init, |
| n->left, n->right, |
| nodintconst(n->left->type->type->width)); |
| goto ret; |
| |
| case OCLOSE: |
| // cannot use chanfn - closechan takes any, not chan any |
| fn = syslook("closechan", 1); |
| argtype(fn, n->left->type); |
| n = mkcall1(fn, T, init, n->left); |
| goto ret; |
| |
| case OCLOSED: |
| // cannot use chanfn - closechan takes any, not chan any |
| fn = syslook("closedchan", 1); |
| argtype(fn, n->left->type); |
| n = mkcall1(fn, n->type, init, n->left); |
| goto ret; |
| |
| case OMAKECHAN: |
| n = mkcall1(chanfn("makechan", 1, n->type), n->type, init, |
| typename(n->type->type), |
| conv(n->left, types[TINT])); |
| goto ret; |
| |
| case OMAKEMAP: |
| t = n->type; |
| |
| fn = syslook("makemap", 1); |
| argtype(fn, t->down); // any-1 |
| argtype(fn, t->type); // any-2 |
| |
| n = mkcall1(fn, n->type, init, |
| typename(t->down), // key type |
| typename(t->type), // value type |
| conv(n->left, types[TINT])); |
| goto ret; |
| |
| case OMAKESLICE: |
| // makeslice(nel int, max int, width int) (ary []any) |
| t = n->type; |
| fn = syslook("makeslice", 1); |
| argtype(fn, t->type); // any-1 |
| n = mkcall1(fn, n->type, nil, |
| typename(n->type), |
| conv(n->left, types[TINT]), |
| conv(n->right, types[TINT])); |
| goto ret; |
| |
| case ORUNESTR: |
| // sys_intstring(v) |
| n = mkcall("intstring", n->type, init, |
| conv(n->left, types[TINT64])); // TODO(rsc): int64?! |
| goto ret; |
| |
| case OARRAYBYTESTR: |
| // slicebytetostring([]byte) string; |
| n = mkcall("slicebytetostring", n->type, init, n->left); |
| goto ret; |
| |
| case OARRAYRUNESTR: |
| // sliceinttostring([]byte) string; |
| n = mkcall("sliceinttostring", n->type, init, n->left); |
| goto ret; |
| |
| case OCMPIFACE: |
| // ifaceeq(i1 any-1, i2 any-2) (ret bool); |
| if(!eqtype(n->left->type, n->right->type)) |
| fatal("ifaceeq %O %T %T", n->op, n->left->type, n->right->type); |
| if(isnilinter(n->left->type)) |
| fn = syslook("efaceeq", 1); |
| else |
| fn = syslook("ifaceeq", 1); |
| argtype(fn, n->right->type); |
| argtype(fn, n->left->type); |
| r = mkcall1(fn, n->type, init, n->left, n->right); |
| if(n->etype == ONE) { |
| r = nod(ONOT, r, N); |
| typecheck(&r, Erv); |
| } |
| n = r; |
| goto ret; |
| |
| case OARRAYLIT: |
| case OMAPLIT: |
| case OSTRUCTLIT: |
| nvar = nod(OXXX, N, N); |
| tempname(nvar, n->type); |
| anylit(n, nvar, init); |
| n = nvar; |
| goto ret; |
| |
| case OSEND: |
| n = mkcall1(chanfn("chansend1", 2, n->left->type), T, init, n->left, n->right); |
| goto ret; |
| |
| case OSENDNB: |
| n = mkcall1(chanfn("chansend2", 2, n->left->type), n->type, init, n->left, n->right); |
| goto ret; |
| |
| case OCONVIFACE: |
| walkexpr(&n->left, init); |
| n = ifacecvt(n->type, n->left, n->etype, init); |
| goto ret; |
| |
| case OCLOSURE: |
| n = walkclosure(n, init); |
| goto ret; |
| } |
| fatal("missing switch %O", n->op); |
| |
| ret: |
| if(debug['w'] && n != N) |
| dump("walk", n); |
| |
| ullmancalc(n); |
| lineno = lno; |
| *np = n; |
| } |
| |
| Node* |
| makenewvar(Type *t, NodeList **init, Node **nstar) |
| { |
| Node *nvar, *nas; |
| |
| nvar = nod(OXXX, N, N); |
| tempname(nvar, t); |
| nas = nod(OAS, nvar, callnew(t->type)); |
| typecheck(&nas, Etop); |
| walkexpr(&nas, init); |
| *init = list(*init, nas); |
| |
| *nstar = nod(OIND, nvar, N); |
| typecheck(nstar, Erv); |
| return nvar; |
| } |
| |
| // TODO(rsc): cut |
| void |
| walkdottype(Node *n, NodeList **init) |
| { |
| walkexpr(&n->left, init); |
| if(n->left == N) |
| return; |
| if(n->right != N) { |
| walkexpr(&n->right, init); |
| n->type = n->right->type; |
| n->right = N; |
| } |
| } |
| |
| // TODO(rsc): cut |
| void |
| walkconv(Node **np, NodeList **init) |
| { |
| int et; |
| char *what; |
| Type *t; |
| Node *l; |
| Node *n; |
| |
| n = *np; |
| t = n->type; |
| if(t == T) |
| return; |
| walkexpr(&n->left, init); |
| l = n->left; |
| if(l == N) |
| return; |
| if(l->type == T) |
| return; |
| |
| // if using .(T), interface assertion. |
| if(n->op == ODOTTYPE) { |
| et = ifaceas1(t, l->type, 1); |
| if(et == I2Isame || et == E2Esame) { |
| n->op = OCONVNOP; |
| return; |
| } |
| if(et != Inone) { |
| n = ifacecvt(t, l, et, init); |
| *np = n; |
| return; |
| } |
| goto bad; |
| } |
| |
| fatal("walkconv"); |
| |
| bad: |
| if(n->diag) |
| return; |
| n->diag = 1; |
| if(n->op == ODOTTYPE) |
| what = "type assertion"; |
| else |
| what = "conversion"; |
| if(l->type != T) |
| yyerror("invalid %s: %T to %T", what, l->type, t); |
| } |
| |
| Node* |
| ascompatee1(int op, Node *l, Node *r, NodeList **init) |
| { |
| return convas(nod(OAS, l, r), init); |
| } |
| |
| NodeList* |
| ascompatee(int op, NodeList *nl, NodeList *nr, NodeList **init) |
| { |
| NodeList *ll, *lr, *nn; |
| |
| /* |
| * check assign expression list to |
| * a expression list. called in |
| * expr-list = expr-list |
| */ |
| nn = nil; |
| for(ll=nl, lr=nr; ll && lr; ll=ll->next, lr=lr->next) |
| nn = list(nn, ascompatee1(op, ll->n, lr->n, init)); |
| |
| // cannot happen: caller checked that lists had same length |
| if(ll || lr) |
| yyerror("error in shape across %O", op); |
| return nn; |
| } |
| |
| /* |
| * l is an lv and rt is the type of an rv |
| * return 1 if this implies a function call |
| * evaluating the lv or a function call |
| * in the conversion of the types |
| */ |
| int |
| fncall(Node *l, Type *rt) |
| { |
| if(l->ullman >= UINF) |
| return 1; |
| if(eqtype(l->type, rt)) |
| return 0; |
| return 1; |
| } |
| |
| NodeList* |
| ascompatet(int op, NodeList *nl, Type **nr, int fp, NodeList **init) |
| { |
| Node *l, *tmp, *a; |
| NodeList *ll; |
| Type *r; |
| Iter saver; |
| int ucount; |
| NodeList *nn, *mm; |
| |
| /* |
| * check assign type list to |
| * a expression list. called in |
| * expr-list = func() |
| */ |
| r = structfirst(&saver, nr); |
| nn = nil; |
| mm = nil; |
| ucount = 0; |
| for(ll=nl; ll; ll=ll->next) { |
| if(r == T) |
| break; |
| l = ll->n; |
| if(isblank(l)) { |
| r = structnext(&saver); |
| continue; |
| } |
| |
| // any lv that causes a fn call must be |
| // deferred until all the return arguments |
| // have been pulled from the output arguments |
| if(fncall(l, r->type)) { |
| tmp = nod(OXXX, N, N); |
| tempname(tmp, r->type); |
| a = nod(OAS, l, tmp); |
| a = convas(a, init); |
| mm = list(mm, a); |
| l = tmp; |
| } |
| |
| a = nod(OAS, l, nodarg(r, fp)); |
| a = convas(a, init); |
| ullmancalc(a); |
| if(a->ullman >= UINF) |
| ucount++; |
| nn = list(nn, a); |
| r = structnext(&saver); |
| } |
| |
| if(ll != nil || r != T) |
| yyerror("assignment count mismatch: %d = %d", |
| count(nl), structcount(*nr)); |
| if(ucount) |
| yyerror("reorder2: too many function calls evaluating parameters"); |
| return concat(nn, mm); |
| } |
| |
| /* |
| * make a tsig for the structure |
| * carrying the ... arguments |
| */ |
| Type* |
| sigtype(Type *st) |
| { |
| Sym *s; |
| Type *t; |
| static int sigdddgen; |
| |
| dowidth(st); |
| |
| sigdddgen++; |
| snprint(namebuf, sizeof(namebuf), "dsigddd_%d", sigdddgen); |
| s = lookup(namebuf); |
| t = newtype(s); |
| t = dodcltype(t); |
| updatetype(t, st); |
| t->local = 1; |
| return t; |
| } |
| |
| /* |
| * package all the arguments that |
| * match a ... parameter into an |
| * automatic structure. |
| * then call the ... arg (interface) |
| * with a pointer to the structure. |
| */ |
| NodeList* |
| mkdotargs(NodeList *lr0, NodeList *nn, Type *l, int fp, NodeList **init) |
| { |
| Node *r; |
| Type *t, *st, *ft; |
| Node *a, *var; |
| NodeList *lr, *n; |
| |
| n = nil; // list of assignments |
| |
| st = typ(TSTRUCT); // generated structure |
| ft = T; // last field |
| for(lr=lr0; lr; lr=lr->next) { |
| r = lr->n; |
| if(r->op == OLITERAL && r->val.ctype == CTNIL) { |
| if(r->type == T || r->type->etype == TNIL) { |
| yyerror("inappropriate use of nil in ... argument"); |
| return nil; |
| } |
| } |
| defaultlit(&r, T); |
| lr->n = r; |
| if(r->type == T) // type check failed |
| return nil; |
| |
| // generate the next structure field |
| t = typ(TFIELD); |
| t->type = r->type; |
| if(ft == T) |
| st->type = t; |
| else |
| ft->down = t; |
| ft = t; |
| |
| a = nod(OAS, N, r); |
| n = list(n, a); |
| } |
| |
| // make a named type for the struct |
| st = sigtype(st); |
| dowidth(st); |
| |
| // now we have the size, make the struct |
| var = nod(OXXX, N, N); |
| tempname(var, st); |
| var->sym = lookup(".ddd"); |
| |
| // assign the fields to the struct. |
| // use the init list so that reorder1 doesn't reorder |
| // these assignments after the interface conversion |
| // below. |
| t = st->type; |
| for(lr=n; lr; lr=lr->next) { |
| r = lr->n; |
| r->left = nod(OXXX, N, N); |
| *r->left = *var; |
| r->left->type = r->right->type; |
| r->left->xoffset += t->width; |
| typecheck(&r, Etop); |
| walkexpr(&r, init); |
| lr->n = r; |
| t = t->down; |
| } |
| *init = concat(*init, n); |
| |
| // last thing is to put assignment |
| // of the structure to the DDD parameter |
| a = nod(OAS, nodarg(l, fp), var); |
| nn = list(nn, convas(a, init)); |
| return nn; |
| } |
| |
| /* |
| * helpers for shape errors |
| */ |
| static void |
| dumptypes(Type **nl, char *what) |
| { |
| int first; |
| Type *l; |
| Iter savel; |
| |
| l = structfirst(&savel, nl); |
| print("\t"); |
| first = 1; |
| for(l = structfirst(&savel, nl); l != T; l = structnext(&savel)) { |
| if(first) |
| first = 0; |
| else |
| print(", "); |
| print("%T", l); |
| } |
| if(first) |
| print("[no arguments %s]", what); |
| print("\n"); |
| } |
| |
| static void |
| dumpnodetypes(NodeList *l, char *what) |
| { |
| int first; |
| Node *r; |
| |
| print("\t"); |
| first = 1; |
| for(; l; l=l->next) { |
| r = l->n; |
| if(first) |
| first = 0; |
| else |
| print(", "); |
| print("%T", r->type); |
| } |
| if(first) |
| print("[no arguments %s]", what); |
| print("\n"); |
| } |
| |
| /* |
| * check assign expression list to |
| * a type list. called in |
| * return expr-list |
| * func(expr-list) |
| */ |
| NodeList* |
| ascompatte(int op, Type **nl, NodeList *lr, int fp, NodeList **init) |
| { |
| Type *l, *ll; |
| Node *r, *a; |
| NodeList *nn, *lr0, *alist; |
| Iter savel, peekl; |
| |
| lr0 = lr; |
| l = structfirst(&savel, nl); |
| r = N; |
| if(lr) |
| r = lr->n; |
| nn = nil; |
| |
| // 1 to many |
| peekl = savel; |
| if(l != T && r != N && structnext(&peekl) != T && lr->next == nil |
| && r->type->etype == TSTRUCT && r->type->funarg) { |
| // optimization - can do block copy |
| if(eqtypenoname(r->type, *nl)) { |
| a = nodarg(*nl, fp); |
| a->type = r->type; |
| nn = list1(convas(nod(OAS, a, r), init)); |
| goto ret; |
| } |
| // conversions involved. |
| // copy into temporaries. |
| alist = nil; |
| for(l=structfirst(&savel, &r->type); l; l=structnext(&savel)) { |
| a = nod(OXXX, N, N); |
| tempname(a, l->type); |
| alist = list(alist, a); |
| } |
| a = nod(OAS2, N, N); |
| a->list = alist; |
| a->rlist = lr; |
| typecheck(&a, Etop); |
| walkstmt(&a); |
| *init = list(*init, a); |
| lr = alist; |
| r = lr->n; |
| l = structfirst(&savel, nl); |
| } |
| |
| loop: |
| if(l != T && isddd(l->type)) { |
| // the ddd parameter must be last |
| ll = structnext(&savel); |
| if(ll != T) |
| yyerror("... must be last argument"); |
| |
| // special case -- |
| // only if we are assigning a single ddd |
| // argument to a ddd parameter then it is |
| // passed thru unencapsulated |
| if(r != N && lr->next == nil && isddd(r->type)) { |
| a = nod(OAS, nodarg(l, fp), r); |
| a = convas(a, init); |
| nn = list(nn, a); |
| goto ret; |
| } |
| |
| // normal case -- make a structure of all |
| // remaining arguments and pass a pointer to |
| // it to the ddd parameter (empty interface) |
| nn = mkdotargs(lr, nn, l, fp, init); |
| goto ret; |
| } |
| |
| if(l == T || r == N) { |
| if(l != T || r != N) { |
| if(l != T) |
| yyerror("xxx not enough arguments to %O", op); |
| else |
| yyerror("xxx too many arguments to %O", op); |
| dumptypes(nl, "expected"); |
| dumpnodetypes(lr0, "given"); |
| } |
| goto ret; |
| } |
| |
| a = nod(OAS, nodarg(l, fp), r); |
| a = convas(a, init); |
| nn = list(nn, a); |
| |
| l = structnext(&savel); |
| r = N; |
| lr = lr->next; |
| if(lr != nil) |
| r = lr->n; |
| goto loop; |
| |
| ret: |
| for(lr=nn; lr; lr=lr->next) |
| lr->n->typecheck = 1; |
| return nn; |
| } |
| |
| // generate code for print |
| static Node* |
| walkprint(Node *nn, NodeList **init) |
| { |
| Node *r; |
| Node *n; |
| NodeList *l, *all; |
| Node *on; |
| Type *t; |
| int notfirst, et, op; |
| NodeList *calls; |
| |
| op = nn->op; |
| all = nn->list; |
| calls = nil; |
| notfirst = 0; |
| |
| for(l=all; l; l=l->next) { |
| if(notfirst) |
| calls = list(calls, mkcall("printsp", T, init)); |
| notfirst = op == OPRINTN || op == OPANICN; |
| |
| n = l->n; |
| if(n->op == OLITERAL) { |
| switch(n->val.ctype) { |
| case CTINT: |
| defaultlit(&n, types[TINT64]); |
| break; |
| case CTFLT: |
| defaultlit(&n, types[TFLOAT64]); |
| break; |
| } |
| } |
| if(n->op != OLITERAL && n->type && n->type->etype == TIDEAL) |
| defaultlit(&n, types[TINT64]); |
| defaultlit(&n, nil); |
| l->n = n; |
| if(n->type == T || n->type->etype == TFORW) |
| continue; |
| |
| et = n->type->etype; |
| if(isinter(n->type)) { |
| if(isnilinter(n->type)) |
| on = syslook("printeface", 1); |
| else |
| on = syslook("printiface", 1); |
| argtype(on, n->type); // any-1 |
| } else if(isptr[et] || et == TCHAN || et == TMAP || et == TFUNC) { |
| on = syslook("printpointer", 1); |
| argtype(on, n->type); // any-1 |
| } else if(isslice(n->type)) { |
| on = syslook("printslice", 1); |
| argtype(on, n->type); // any-1 |
| } else if(isint[et]) { |
| if(et == TUINT64) |
| on = syslook("printuint", 0); |
| else |
| on = syslook("printint", 0); |
| } else if(isfloat[et]) { |
| on = syslook("printfloat", 0); |
| } else if(et == TBOOL) { |
| on = syslook("printbool", 0); |
| } else if(et == TSTRING) { |
| on = syslook("printstring", 0); |
| } else { |
| badtype(OPRINT, n->type, T); |
| continue; |
| } |
| |
| t = *getinarg(on->type); |
| if(t != nil) |
| t = t->type; |
| if(t != nil) |
| t = t->type; |
| |
| if(!eqtype(t, n->type)) { |
| n = nod(OCONV, n, N); |
| n->type = t; |
| } |
| r = nod(OCALL, on, N); |
| r->list = list1(n); |
| calls = list(calls, r); |
| } |
| |
| if(op == OPRINTN) |
| calls = list(calls, mkcall("printnl", T, nil)); |
| typechecklist(calls, Etop); |
| walkexprlist(calls, init); |
| |
| if(op == OPANIC || op == OPANICN) |
| r = mkcall("panicl", T, nil); |
| else |
| r = nod(OEMPTY, N, N); |
| typecheck(&r, Etop); |
| walkexpr(&r, init); |
| r->ninit = calls; |
| return r; |
| } |
| |
| Node* |
| callnew(Type *t) |
| { |
| Node *fn; |
| |
| dowidth(t); |
| fn = syslook("mal", 1); |
| argtype(fn, t); |
| return mkcall1(fn, ptrto(t), nil, nodintconst(t->width)); |
| } |
| |
| Type* |
| fixchan(Type *t) |
| { |
| if(t == T) |
| goto bad; |
| if(t->etype != TCHAN) |
| goto bad; |
| if(t->type == T) |
| goto bad; |
| |
| dowidth(t->type); |
| |
| return t; |
| |
| bad: |
| yyerror("not a channel: %lT", t); |
| return T; |
| } |
| |
| Node* |
| mapop(Node *n, NodeList **init) |
| { |
| Node *r, *a; |
| |
| r = n; |
| switch(n->op) { |
| default: |
| fatal("mapop: unknown op %O", n->op); |
| case OASOP: |
| // rewrite map[index] op= right |
| // into tmpi := index; map[tmpi] = map[tmpi] op right |
| |
| // make it ok to double-evaluate map[tmpi] |
| n->left->left = safeval(n->left->left, init); |
| n->left->right = safeval(n->left->right, init); |
| |
| a = nod(OXXX, N, N); |
| *a = *n->left; // copy of map[tmpi] |
| a->etype = 0; |
| a = nod(n->etype, a, n->right); // m[tmpi] op right |
| r = nod(OAS, n->left, a); // map[tmpi] = map[tmpi] op right |
| typecheck(&r, Etop); |
| walkexpr(&r, init); |
| break; |
| } |
| return r; |
| } |
| |
| /* |
| * assigning src to dst involving interfaces? |
| * return op to use. |
| */ |
| int |
| ifaceas1(Type *dst, Type *src, int explicit) |
| { |
| if(src == T || dst == T) |
| return Inone; |
| |
| if(explicit && !isinter(src)) |
| yyerror("cannot use .(T) on non-interface type %T", src); |
| |
| if(isinter(dst)) { |
| if(isinter(src)) { |
| if(isnilinter(dst)) { |
| if(isnilinter(src)) |
| return E2Esame; |
| return I2E; |
| } |
| if(eqtype(dst, src)) |
| return I2Isame; |
| ifacecheck(dst, src, lineno, explicit); |
| if(isnilinter(src)) |
| return E2I; |
| if(explicit) |
| return I2Ix; |
| return I2I; |
| } |
| if(isnilinter(dst)) |
| return T2E; |
| ifacecheck(dst, src, lineno, explicit); |
| return T2I; |
| } |
| if(isinter(src)) { |
| ifacecheck(dst, src, lineno, explicit); |
| if(isnilinter(src)) |
| return E2T; |
| return I2T; |
| } |
| return Inone; |
| } |
| |
| /* |
| * treat convert T to T as noop |
| */ |
| int |
| ifaceas(Type *dst, Type *src, int explicit) |
| { |
| int et; |
| |
| et = ifaceas1(dst, src, explicit); |
| if(et == I2Isame || et == E2Esame) |
| et = Inone; |
| return et; |
| } |
| |
| static char* |
| ifacename[] = |
| { |
| [I2T] = "ifaceI2T", |
| [I2T2] = "ifaceI2T2", |
| [I2I] = "ifaceI2I", |
| [I2Ix] = "ifaceI2Ix", |
| [I2I2] = "ifaceI2I2", |
| [I2Isame] = "ifaceI2Isame", |
| [E2T] = "ifaceE2T", |
| [E2T2] = "ifaceE2T2", |
| [E2I] = "ifaceE2I", |
| [E2I2] = "ifaceE2I2", |
| [I2E] = "ifaceI2E", |
| [I2E2] = "ifaceI2E2", |
| [T2I] = "ifaceT2I", |
| [T2E] = "ifaceT2E", |
| [E2Esame] = "ifaceE2Esame", |
| }; |
| |
| Node* |
| ifacecvt(Type *tl, Node *n, int et, NodeList **init) |
| { |
| Type *tr; |
| Node *r, *on; |
| NodeList *args; |
| |
| tr = n->type; |
| |
| switch(et) { |
| default: |
| fatal("ifacecvt: unknown op %d\n", et); |
| |
| case I2Isame: |
| case E2Esame: |
| return n; |
| |
| case T2I: |
| // ifaceT2I(sigi *byte, sigt *byte, elem any) (ret any); |
| args = list1(typename(tl)); // sigi |
| args = list(args, typename(tr)); // sigt |
| args = list(args, n); // elem |
| |
| on = syslook("ifaceT2I", 1); |
| argtype(on, tr); |
| argtype(on, tl); |
| dowidth(on->type); |
| break; |
| |
| case I2T: |
| case I2T2: |
| case I2I: |
| case I2Ix: |
| case I2I2: |
| case E2T: |
| case E2T2: |
| case E2I: |
| case E2I2: |
| // iface[IT]2[IT][2](sigt *byte, iface any) (ret any[, ok bool]); |
| args = list1(typename(tl)); // sigi or sigt |
| args = list(args, n); // iface |
| |
| on = syslook(ifacename[et], 1); |
| argtype(on, tr); |
| argtype(on, tl); |
| break; |
| |
| case I2E: |
| // TODO(rsc): Should do this in back end, without a call. |
| // ifaceI2E(elem any) (ret any); |
| args = list1(n); // elem |
| |
| on = syslook("ifaceI2E", 1); |
| argtype(on, tr); |
| argtype(on, tl); |
| break; |
| |
| case T2E: |
| // TODO(rsc): Should do this in back end for pointer case, without a call. |
| // ifaceT2E(sigt *byte, elem any) (ret any); |
| args = list1(typename(tr)); // sigt |
| args = list(args, n); // elem |
| |
| on = syslook("ifaceT2E", 1); |
| argtype(on, tr); |
| argtype(on, tl); |
| break; |
| } |
| |
| dowidth(on->type); |
| r = nod(OCALL, on, N); |
| r->list = args; |
| typecheck(&r, Erv | Efnstruct); |
| walkexpr(&r, init); |
| return r; |
| } |
| |
| Node* |
| convas(Node *n, NodeList **init) |
| { |
| Node *l, *r; |
| Type *lt, *rt; |
| int et; |
| |
| if(n->op != OAS) |
| fatal("convas: not OAS %O", n->op); |
| n->typecheck = 1; |
| |
| lt = T; |
| rt = T; |
| |
| l = n->left; |
| r = n->right; |
| if(l == N || r == N) |
| goto out; |
| |
| lt = l->type; |
| rt = r->type; |
| if(lt == T || rt == T) |
| goto out; |
| |
| if(isblank(n->left)) |
| goto out; |
| |
| if(n->left->op == OINDEXMAP) { |
| n = mkcall1(mapfn("mapassign1", n->left->left->type), T, init, |
| n->left->left, n->left->right, n->right); |
| goto out; |
| } |
| |
| if(eqtype(lt, rt)) |
| goto out; |
| |
| et = ifaceas(lt, rt, 0); |
| if(et != Inone) { |
| n->right = ifacecvt(lt, r, et, init); |
| goto out; |
| } |
| |
| out: |
| ullmancalc(n); |
| return n; |
| } |
| |
| /* |
| * from ascompat[te] |
| * evaluating actual function arguments. |
| * f(a,b) |
| * if there is exactly one function expr, |
| * then it is done first. otherwise must |
| * make temp variables |
| */ |
| NodeList* |
| reorder1(NodeList *all) |
| { |
| Node *f, *a, *n; |
| NodeList *l, *r, *g; |
| int c, d, t; |
| |
| c = 0; // function calls |
| t = 0; // total parameters |
| |
| for(l=all; l; l=l->next) { |
| n = l->n; |
| t++; |
| ullmancalc(n); |
| if(n->ullman >= UINF) |
| c++; |
| } |
| if(c == 0 || t == 1) |
| return all; |
| |
| g = nil; // fncalls assigned to tempnames |
| f = N; // last fncall assigned to stack |
| r = nil; // non fncalls and tempnames assigned to stack |
| d = 0; |
| for(l=all; l; l=l->next) { |
| n = l->n; |
| if(n->ullman < UINF) { |
| r = list(r, n); |
| continue; |
| } |
| d++; |
| if(d == c) { |
| f = n; |
| continue; |
| } |
| |
| // make assignment of fncall to tempname |
| a = nod(OXXX, N, N); |
| tempname(a, n->right->type); |
| a = nod(OAS, a, n->right); |
| g = list(g, a); |
| |
| // put normal arg assignment on list |
| // with fncall replaced by tempname |
| n->right = a->left; |
| r = list(r, n); |
| } |
| |
| if(f != N) |
| g = list(g, f); |
| return concat(g, r); |
| } |
| |
| /* |
| * from ascompat[ee] |
| * a,b = c,d |
| * simultaneous assignment. there cannot |
| * be later use of an earlier lvalue. |
| */ |
| |
| int |
| vmatch2(Node *l, Node *r) |
| { |
| NodeList *ll; |
| |
| /* |
| * isolate all right sides |
| */ |
| if(r == N) |
| return 0; |
| switch(r->op) { |
| case ONAME: |
| // match each right given left |
| if(l == r) |
| return 1; |
| case OLITERAL: |
| return 0; |
| } |
| if(vmatch2(l, r->left)) |
| return 1; |
| if(vmatch2(l, r->right)) |
| return 1; |
| for(ll=r->list; ll; ll=ll->next) |
| if(vmatch2(l, ll->n)) |
| return 1; |
| return 0; |
| } |
| |
| int |
| vmatch1(Node *l, Node *r) |
| { |
| NodeList *ll; |
| |
| /* |
| * isolate all left sides |
| */ |
| if(l == N || r == N) |
| return 0; |
| switch(l->op) { |
| case ONAME: |
| switch(l->class) { |
| case PPARAM: |
| case PPARAMREF: |
| case PAUTO: |
| break; |
| default: |
| // assignment to non-stack variable |
| // must be delayed if right has function calls. |
| if(r->ullman >= UINF) |
| return 1; |
| break; |
| } |
| return vmatch2(l, r); |
| case OLITERAL: |
| return 0; |
| } |
| if(vmatch1(l->left, r)) |
| return 1; |
| if(vmatch1(l->right, r)) |
| return 1; |
| for(ll=l->list; ll; ll=ll->next) |
| if(vmatch1(ll->n, r)) |
| return 1; |
| return 0; |
| } |
| |
| NodeList* |
| reorder3(NodeList *all) |
| { |
| Node *n1, *n2, *q; |
| int c1, c2; |
| NodeList *l1, *l2, *r; |
| |
| r = nil; |
| for(l1=all, c1=0; l1; l1=l1->next, c1++) { |
| n1 = l1->n; |
| for(l2=all, c2=0; l2; l2=l2->next, c2++) { |
| n2 = l2->n; |
| if(c2 > c1) { |
| if(vmatch1(n1->left, n2->right)) { |
| // delay assignment to n1->left |
| q = nod(OXXX, N, N); |
| tempname(q, n1->right->type); |
| q = nod(OAS, n1->left, q); |
| n1->left = q->right; |
| r = list(r, q); |
| break; |
| } |
| } |
| } |
| } |
| return concat(all, r); |
| } |
| |
| NodeList* |
| reorder4(NodeList *ll) |
| { |
| /* |
| * from ascompat[te] |
| * return c,d |
| * return expression assigned to output |
| * parameters. there may be no problems. |
| * |
| * TODO(rsc): i don't believe that. |
| * func f() (a, b int) { |
| * a, b = 1, 2; |
| * return b, a; |
| * } |
| */ |
| return ll; |
| } |
| |
| /* |
| * walk through argin parameters. |
| * generate and return code to allocate |
| * copies of escaped parameters to the heap. |
| */ |
| NodeList* |
| paramstoheap(Type **argin) |
| { |
| Type *t; |
| Iter savet; |
| Node *v; |
| NodeList *nn; |
| |
| nn = nil; |
| for(t = structfirst(&savet, argin); t != T; t = structnext(&savet)) { |
| v = t->nname; |
| if(v == N || !(v->class & PHEAP)) |
| continue; |
| |
| // generate allocation & copying code |
| nn = list(nn, nod(OAS, v->heapaddr, v->alloc)); |
| nn = list(nn, nod(OAS, v, v->stackparam)); |
| } |
| return nn; |
| } |
| |
| /* |
| * take care of migrating any function in/out args |
| * between the stack and the heap. adds code to |
| * curfn's before and after lists. |
| */ |
| void |
| heapmoves(void) |
| { |
| NodeList *nn; |
| |
| nn = paramstoheap(getthis(curfn->type)); |
| nn = concat(nn, paramstoheap(getinarg(curfn->type))); |
| curfn->enter = concat(curfn->enter, nn); |
| } |
| |
| static Node* |
| vmkcall(Node *fn, Type *t, NodeList **init, va_list va) |
| { |
| int i, n; |
| Node *r; |
| NodeList *args; |
| |
| if(fn->type == T || fn->type->etype != TFUNC) |
| fatal("mkcall %#N %T", fn, fn->type); |
| |
| args = nil; |
| n = fn->type->intuple; |
| for(i=0; i<n; i++) |
| args = list(args, va_arg(va, Node*)); |
| |
| r = nod(OCALL, fn, N); |
| r->list = args; |
| if(fn->type->outtuple > 0) |
| typecheck(&r, Erv | Efnstruct); |
| else |
| typecheck(&r, Etop); |
| walkexpr(&r, init); |
| r->type = t; |
| return r; |
| } |
| |
| Node* |
| mkcall(char *name, Type *t, NodeList **init, ...) |
| { |
| Node *r; |
| va_list va; |
| |
| va_start(va, init); |
| r = vmkcall(syslook(name, 0), t, init, va); |
| va_end(va); |
| return r; |
| } |
| |
| Node* |
| mkcall1(Node *fn, Type *t, NodeList **init, ...) |
| { |
| Node *r; |
| va_list va; |
| |
| va_start(va, init); |
| r = vmkcall(fn, t, init, va); |
| va_end(va); |
| return r; |
| } |
| |
| static Node* |
| conv(Node *n, Type *t) |
| { |
| if(eqtype(n->type, t)) |
| return n; |
| n = nod(OCONV, n, N); |
| n->type = t; |
| typecheck(&n, Erv); |
| return n; |
| } |
| |
| Node* |
| chanfn(char *name, int n, Type *t) |
| { |
| Node *fn; |
| int i; |
| |
| if(t->etype != TCHAN) |
| fatal("chanfn %T", t); |
| fn = syslook(name, 1); |
| for(i=0; i<n; i++) |
| argtype(fn, t->type); |
| return fn; |
| } |
| |
| static Node* |
| mapfn(char *name, Type *t) |
| { |
| Node *fn; |
| |
| if(t->etype != TMAP) |
| fatal("mapfn %T", t); |
| fn = syslook(name, 1); |
| argtype(fn, t->down); |
| argtype(fn, t->type); |
| argtype(fn, t->down); |
| argtype(fn, t->type); |
| return fn; |
| } |