| // 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**, int); | |
| 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: | |
| 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); | |
| } | |
| static int nwalkdeftype; | |
| static NodeList *methodqueue; | |
| static void | |
| domethod(Node *n) | |
| { | |
| Node *nt; | |
| nt = n->type->nname; | |
| typecheck(&nt, Etype); | |
| if(nt->type == T) { | |
| // type check failed; leave empty func | |
| n->type->etype = TFUNC; | |
| n->type->nod = N; | |
| return; | |
| } | |
| *n->type = *nt->type; | |
| n->type->nod = N; | |
| checkwidth(n->type); | |
| } | |
| static void | |
| walkdeftype(Node *n) | |
| { | |
| int maplineno, embedlineno, lno; | |
| Type *t; | |
| NodeList *l; | |
| nwalkdeftype++; | |
| lno = lineno; | |
| setlineno(n); | |
| 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->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"); | |
| } | |
| ret: | |
| lineno = lno; | |
| // if there are no type definitions going on, it's safe to | |
| // try to resolve the method types for the interfaces | |
| // we just read. | |
| if(nwalkdeftype == 1) { | |
| while((l = methodqueue) != nil) { | |
| methodqueue = nil; | |
| for(; l; l=l->next) | |
| domethod(l->n); | |
| } | |
| } | |
| nwalkdeftype--; | |
| } | |
| void | |
| queuemethod(Node *n) | |
| { | |
| if(nwalkdeftype == 0) { | |
| domethod(n); | |
| return; | |
| } | |
| methodqueue = list(methodqueue, n); | |
| } | |
| void | |
| walkdef(Node *n) | |
| { | |
| int lno; | |
| 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); | |
| if(!okforconst[t->etype]) | |
| yyerror("invalid constant type %T", 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; | |
| walkdeftype(n); | |
| 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 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 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; | |
| switch(n->left->op) { | |
| case OPRINT: | |
| case OPRINTN: | |
| case OPANIC: | |
| walkexprlist(n->left->list, &n->ninit); | |
| n->left = walkprint(n->left, &n->ninit, 1); | |
| break; | |
| default: | |
| walkexpr(&n->left, &n->ninit); | |
| break; | |
| } | |
| 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 | |
| walkexprlistsafe(NodeList *l, NodeList **init) | |
| { | |
| for(; l; l=l->next) { | |
| l->n = safeexpr(l->n, init); | |
| walkexpr(&l->n, init); | |
| } | |
| } | |
| void | |
| walkexpr(Node **np, NodeList **init) | |
| { | |
| Node *r, *l, *var, *a; | |
| NodeList *ll, *lr, *lpost; | |
| 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 OREAL: | |
| case OIMAG: | |
| 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: | |
| case OCMPLX: | |
| walkexpr(&n->left, init); | |
| walkexpr(&n->right, init); | |
| goto ret; | |
| case OPRINT: | |
| case OPRINTN: | |
| case OPANIC: | |
| walkexprlist(n->list, init); | |
| n = walkprint(n, init, 0); | |
| 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); | |
| n->left = safeexpr(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; | |
| walkexprlistsafe(n->list, init); | |
| walkexprlistsafe(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; | |
| walkexprlistsafe(n->list, init); | |
| walkexpr(&r, init); | |
| l = n->list->n; | |
| // all the really hard stuff - explicit function calls and so on - | |
| // is gone, but map assignments remain. | |
| // if there are map assignments here, assign via | |
| // temporaries, because ascompatet assumes | |
| // the targets can be addressed without function calls | |
| // and map index has an implicit one. | |
| lpost = nil; | |
| if(l->op == OINDEXMAP) { | |
| var = nod(OXXX, N, N); | |
| tempname(var, l->type); | |
| n->list->n = var; | |
| a = nod(OAS, l, var); | |
| typecheck(&a, Etop); | |
| lpost = list(lpost, a); | |
| } | |
| l = n->list->next->n; | |
| if(l->op == OINDEXMAP) { | |
| var = nod(OXXX, N, N); | |
| tempname(var, l->type); | |
| n->list->next->n = var; | |
| a = nod(OAS, l, var); | |
| typecheck(&a, Etop); | |
| lpost = list(lpost, a); | |
| } | |
| ll = ascompatet(n->op, n->list, &r->type, 0, init); | |
| walkexprlist(lpost, init); | |
| n = liststmt(concat(concat(list1(r), ll), lpost)); | |
| goto ret; | |
| case OAS2RECV: | |
| // a,b = <-c | |
| *init = concat(*init, n->ninit); | |
| n->ninit = nil; | |
| r = n->rlist->n; | |
| walkexprlistsafe(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; | |
| walkexprlistsafe(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; | |
| walkexprlistsafe(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; | |
| walkexprlistsafe(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: | |
| n->left = safeexpr(n->left, init); | |
| walkexpr(&n->left, init); | |
| l = n->left; | |
| walkexpr(&n->right, init); | |
| if(n->etype == OANDNOT) { | |
| n->etype = OAND; | |
| n->right = nod(OCOM, n->right, N); | |
| typecheck(&n->right, Erv); | |
| } | |
| /* | |
| * on 32-bit arch, rewrite 64-bit ops into l = l op r. | |
| * on 386, rewrite float ops into l = l op r. | |
| * everywhere, rewrite map ops into l = l op r. | |
| * everywhere, rewrite string += into l = l op r. | |
| * TODO(rsc): Maybe this rewrite should be done always? | |
| */ | |
| et = n->left->type->etype; | |
| if((widthptr == 4 && (et == TUINT64 || et == TINT64)) || | |
| (thechar == '8' && isfloat[et]) || | |
| l->op == OINDEXMAP || | |
| et == TSTRING) { | |
| l = safeexpr(n->left, init); | |
| a = l; | |
| if(a->op == OINDEXMAP) { | |
| // map index has "lhs" bit set in a->etype. | |
| // make a copy so we can clear it on the rhs. | |
| a = nod(OXXX, N, N); | |
| *a = *l; | |
| a->etype = 0; | |
| } | |
| r = nod(OAS, l, nod(n->etype, a, 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: | |
| walkexpr(&n->left, init); | |
| walkexpr(&n->right, init); | |
| /* | |
| * rewrite complex div into function call. | |
| */ | |
| et = n->left->type->etype; | |
| if(iscomplex[et] && n->op == ODIV) { | |
| n = mkcall("complex128div", n->type, init, | |
| conv(n->left, types[TCOMPLEX128]), | |
| conv(n->right, types[TCOMPLEX128])); | |
| goto ret; | |
| } | |
| /* | |
| * rewrite div and mod into function calls | |
| * on 32-bit architectures. | |
| */ | |
| 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); | |
| n->left = safeexpr(n->left, init); | |
| walkexpr(&n->right->left, init); | |
| n->right->left = safeexpr(n->right->left, init); | |
| walkexpr(&n->right->right, init); | |
| n->right->right = safeexpr(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); | |
| n->left = safeexpr(n->left, init); | |
| walkexpr(&n->right->left, init); | |
| n->right->left = safeexpr(n->right->left, init); | |
| walkexpr(&n->right->right, init); | |
| n->right->right = safeexpr(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 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, init, | |
| 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([]int) string; | |
| n = mkcall("sliceinttostring", n->type, init, n->left); | |
| goto ret; | |
| case OSTRARRAYBYTE: | |
| // stringtoslicebyte(string) []byte; | |
| n = mkcall("stringtoslicebyte", n->type, init, n->left); | |
| goto ret; | |
| case OSTRARRAYRUNE: | |
| // stringtosliceint(string) []int | |
| n = mkcall("stringtosliceint", 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; | |
| } | |
| static 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 | |
| */ | |
| // ensure order of evaluation for function calls | |
| for(ll=nl; ll; ll=ll->next) | |
| ll->n = safeexpr(ll->n, init); | |
| for(lr=nr; lr; lr=lr->next) | |
| lr->n = safeexpr(lr->n, init); | |
| 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) | |
| fatal("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; | |
| } | |
| /* | |
| * package all the arguments that match a ... T parameter into a []T. | |
| */ | |
| NodeList* | |
| mkdotargslice(NodeList *lr0, NodeList *nn, Type *l, int fp, NodeList **init) | |
| { | |
| Node *a, *n; | |
| Type *tslice; | |
| tslice = typ(TARRAY); | |
| tslice->type = l->type->type; | |
| tslice->bound = -1; | |
| n = nod(OCOMPLIT, N, typenod(tslice)); | |
| n->list = lr0; | |
| typecheck(&n, Erv); | |
| if(n->type == T) | |
| fatal("mkdotargslice: typecheck failed"); | |
| walkexpr(&n, init); | |
| a = nod(OAS, nodarg(l, fp), n); | |
| 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 && l->isddd) { | |
| // 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 && r->isddd && eqtype(l->type, 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) | |
| // TODO(rsc): delete in DDD cleanup. | |
| if(l->type->etype == TINTER) | |
| nn = mkdotargs(lr, nn, l, fp, init); | |
| else | |
| nn = mkdotargslice(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, int defer) | |
| { | |
| Node *r; | |
| Node *n; | |
| NodeList *l, *all; | |
| Node *on; | |
| Type *t; | |
| int notfirst, et, op; | |
| NodeList *calls, *intypes, *args; | |
| Fmt fmt; | |
| on = nil; | |
| op = nn->op; | |
| all = nn->list; | |
| calls = nil; | |
| notfirst = 0; | |
| intypes = nil; | |
| args = nil; | |
| memset(&fmt, 0, sizeof fmt); | |
| if(defer) { | |
| // defer print turns into defer printf with format string | |
| fmtstrinit(&fmt); | |
| intypes = list(intypes, nod(ODCLFIELD, N, typenod(types[TSTRING]))); | |
| args = list1(nod(OXXX, N, N)); | |
| } | |
| for(l=all; l; l=l->next) { | |
| if(notfirst) { | |
| if(defer) | |
| fmtprint(&fmt, " "); | |
| else | |
| calls = list(calls, mkcall("printsp", T, init)); | |
| } | |
| notfirst = op == OPRINTN; | |
| 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; | |
| t = n->type; | |
| et = n->type->etype; | |
| if(isinter(n->type)) { | |
| if(defer) { | |
| if(isnilinter(n->type)) | |
| fmtprint(&fmt, "%%e"); | |
| else | |
| fmtprint(&fmt, "%%i"); | |
| } else { | |
| 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) { | |
| if(defer) { | |
| fmtprint(&fmt, "%%p"); | |
| } else { | |
| on = syslook("printpointer", 1); | |
| argtype(on, n->type); // any-1 | |
| } | |
| } else if(isslice(n->type)) { | |
| if(defer) { | |
| fmtprint(&fmt, "%%a"); | |
| } else { | |
| on = syslook("printslice", 1); | |
| argtype(on, n->type); // any-1 | |
| } | |
| } else if(isint[et]) { | |
| if(defer) { | |
| if(et == TUINT64) | |
| fmtprint(&fmt, "%%U"); | |
| else { | |
| fmtprint(&fmt, "%%D"); | |
| t = types[TINT64]; | |
| } | |
| } else { | |
| if(et == TUINT64) | |
| on = syslook("printuint", 0); | |
| else | |
| on = syslook("printint", 0); | |
| } | |
| } else if(isfloat[et]) { | |
| if(defer) { | |
| fmtprint(&fmt, "%%f"); | |
| t = types[TFLOAT64]; | |
| } else | |
| on = syslook("printfloat", 0); | |
| } else if(iscomplex[et]) { | |
| if(defer) { | |
| fmtprint(&fmt, "%%C"); | |
| t = types[TCOMPLEX128]; | |
| } else | |
| on = syslook("printcomplex", 0); | |
| } else if(et == TBOOL) { | |
| if(defer) | |
| fmtprint(&fmt, "%%t"); | |
| else | |
| on = syslook("printbool", 0); | |
| } else if(et == TSTRING) { | |
| if(defer) | |
| fmtprint(&fmt, "%%S"); | |
| else | |
| on = syslook("printstring", 0); | |
| } else { | |
| badtype(OPRINT, n->type, T); | |
| continue; | |
| } | |
| if(!defer) { | |
| 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; | |
| } | |
| if(defer) { | |
| intypes = list(intypes, nod(ODCLFIELD, N, typenod(t))); | |
| args = list(args, n); | |
| } else { | |
| r = nod(OCALL, on, N); | |
| r->list = list1(n); | |
| calls = list(calls, r); | |
| } | |
| } | |
| if(defer) { | |
| if(op == OPRINTN) | |
| fmtprint(&fmt, "\n"); | |
| if(op == OPANIC) | |
| fmtprint(&fmt, "%%!"); | |
| on = syslook("printf", 1); | |
| on->type = functype(nil, intypes, nil); | |
| args->n = nod(OLITERAL, N, N); | |
| args->n->val.ctype = CTSTR; | |
| args->n->val.u.sval = strlit(fmtstrflush(&fmt)); | |
| r = nod(OCALL, on, N); | |
| r->list = args; | |
| typecheck(&r, Etop); | |
| walkexpr(&r, init); | |
| } else { | |
| if(op == OPRINTN) | |
| calls = list(calls, mkcall("printnl", T, nil)); | |
| typechecklist(calls, Etop); | |
| walkexprlist(calls, init); | |
| if(op == OPANIC) | |
| 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; | |
| } | |
| /* | |
| * 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)) { | |
| defaultlit(&n->right, T); | |
| 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 | |
| if(v->alloc == nil) | |
| v->alloc = callnew(v->type); | |
| 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; | |
| } |