// 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**); static Node* ascompatee1(int, Node*, Node*, NodeList**); static NodeList* ascompatee(int, NodeList*, NodeList*, NodeList**); static NodeList* ascompatet(int, NodeList*, Type**, int, NodeList**); static NodeList* ascompatte(int, int, Type**, NodeList*, int, NodeList**); static Node* convas(Node*, NodeList**); static void heapmoves(void); static NodeList* paramstoheap(Type **argin, int out); static NodeList* reorder1(NodeList*); static NodeList* reorder3(NodeList*); static Node* addstr(Node*, NodeList**); static Node* appendslice(Node*, NodeList**); static Node* append(Node*, NodeList**); // can this code branch reach the end // without an unconditional RETURN // this is hard, so it is conservative static 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"); 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 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; } static int paramoutheap(Node *fn) { NodeList *l; for(l=fn->dcl; l; l=l->next) { switch(l->n->class) { case PPARAMOUT|PHEAP: return 1; case PAUTO: case PAUTO|PHEAP: // stop early - parameters are over return 0; } } return 0; } void walkstmt(Node **np) { NodeList *init; NodeList *ll, *rl; int cl; Node *n, *f; n = *np; if(n == N) return; 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 OCOPY: case OCALLMETH: case OCALLINTER: case OCALL: case OCALLFUNC: case OSEND: case ORECV: case OPRINT: case OPRINTN: case OPANIC: case OEMPTY: case ORECOVER: if(n->typecheck == 0) { dump("missing typecheck:", n); 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: 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); init = n->ntest->ninit; n->ntest->ninit = nil; walkexpr(&n->ntest, &init); n->ntest->ninit = concat(init, n->ntest->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: switch(n->left->op) { case OPRINT: case OPRINTN: walkexprlist(n->left->list, &n->ninit); n->left = walkprint(n->left, &n->ninit, 1); break; default: walkexpr(&n->left, &n->ninit); break; } break; case ORETURN: walkexprlist(n->list, &n->ninit); if(n->list == nil) break; if((curfn->type->outnamed && count(n->list) > 1) || paramoutheap(curfn)) { // 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; } if(count(n->list) == 1 && count(rl) > 1) { // OAS2FUNC in disguise f = n->list->n; if(f->op != OCALLFUNC && f->op != OCALLMETH && f->op != OCALLINTER) fatal("expected return of call, have %#N", f); n->list = concat(list1(f), ascompatet(n->op, rl, &f->type, 0, &n->ninit)); break; } ll = ascompatee(n->op, rl, n->list, &n->ninit); n->list = reorder3(ll); break; } ll = ascompatte(n->op, 0, getoutarg(curfn->type), n->list, 1, &n->ninit); n->list = 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; int64 v, v1, v2, len; int32 lno; Node *n, *fn; char buf[100], *p; 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 OREAL: case OIMAG: case ODOT: case ODOTPTR: case ODOTMETH: case ODOTINTER: case OIND: walkexpr(&n->left, init); goto ret; case OLEN: case OCAP: walkexpr(&n->left, init); // replace len(*[10]int) with 10. // delayed until now to preserve side effects. t = n->left->type; if(isptr[t->etype]) t = t->type; if(isfixedarray(t)) { safeexpr(n->left, init); nodconst(n, n->type, t->bound); n->typecheck = 1; } goto ret; case OLSH: case ORSH: case OAND: case OOR: case OXOR: case OSUB: case OMUL: case OEQ: case ONE: case OLT: case OLE: case OGE: case OGT: case OADD: case OCOMPLEX: walkexpr(&n->left, init); walkexpr(&n->right, init); goto ret; case OANDAND: case OOROR: walkexpr(&n->left, init); // cannot put side effects from n->right on init, // because they cannot run before n->left is checked. // save elsewhere and store on the eventual n->right. ll = nil; walkexpr(&n->right, &ll); n->right->ninit = concat(n->right->ninit, ll); goto ret; case OPRINT: case OPRINTN: walkexprlist(n->list, init); n = walkprint(n, init, 0); goto ret; case OPANIC: n = mkcall("panic", T, init, n->left); goto ret; case ORECOVER: n = mkcall("recover", n->type, init, nod(OADDR, nodfp, N)); 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, n->isddd, 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; if(n->left->op == OCLOSURE) { walkcallclosure(n, init); t = n->left->type; } walkexpr(&n->left, init); walkexprlist(n->list, init); ll = ascompatte(n->op, n->isddd, getinarg(t), n->list, 0, init); n->list = reorder1(ll); 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, 0, getthis(t), list1(n->left->left), 0, init); lr = ascompatte(n->op, n->isddd, getinarg(t), n->list, 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); if(n->left != N && n->right != N) { r = convas(nod(OAS, n->left, n->right), init); r->dodata = n->dodata; n = r; } goto ret; case OAS2: *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: *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); r->op = ODOTTYPE2; walkexpr(&r, init); ll = ascompatet(n->op, n->list, &r->type, 0, init); n = liststmt(concat(list1(r), ll)); goto ret; case ODOTTYPE: case ODOTTYPE2: // Build name of function: assertI2E2 etc. strcpy(buf, "assert"); p = buf+strlen(buf); if(isnilinter(n->left->type)) *p++ = 'E'; else *p++ = 'I'; *p++ = '2'; if(isnilinter(n->type)) *p++ = 'E'; else if(isinter(n->type)) *p++ = 'I'; else *p++ = 'T'; if(n->op == ODOTTYPE2) *p++ = '2'; *p = '\0'; fn = syslook(buf, 1); ll = list1(typename(n->type)); ll = list(ll, n->left); argtype(fn, n->left->type); argtype(fn, n->type); n = nod(OCALL, fn, N); n->list = ll; typecheck(&n, Erv | Efnstruct); walkexpr(&n, init); goto ret; case OCONVIFACE: // Build name of function: convI2E etc. // Not all names are possible // (e.g., we'll never generate convE2E or convE2I). walkexpr(&n->left, init); strcpy(buf, "conv"); p = buf+strlen(buf); if(isnilinter(n->left->type)) *p++ = 'E'; else if(isinter(n->left->type)) *p++ = 'I'; else *p++ = 'T'; *p++ = '2'; if(isnilinter(n->type)) *p++ = 'E'; else *p++ = 'I'; *p = '\0'; fn = syslook(buf, 1); ll = nil; if(!isinter(n->left->type)) ll = list(ll, typename(n->left->type)); if(!isnilinter(n->type)) ll = list(ll, typename(n->type)); ll = list(ll, n->left); argtype(fn, n->left->type); argtype(fn, n->type); dowidth(fn->type); n = nod(OCALL, fn, N); n->list = ll; typecheck(&n, Erv); walkexpr(&n, init); goto ret; case OCONV: case OCONVNOP: if(thechar == '5') { if(isfloat[n->left->type->etype]) { if(n->type->etype == TINT64) { n = mkcall("float64toint64", n->type, init, conv(n->left, types[TFLOAT64])); goto ret; } if(n->type->etype == TUINT64) { n = mkcall("float64touint64", n->type, init, conv(n->left, types[TFLOAT64])); goto ret; } } if(isfloat[n->type->etype]) { if(n->left->type->etype == TINT64) { n = mkcall("int64tofloat64", n->type, init, conv(n->left, types[TINT64])); goto ret; } if(n->left->type->etype == TUINT64) { n = mkcall("uint64tofloat64", n->type, init, conv(n->left, types[TUINT64])); goto ret; } } } walkexpr(&n->left, init); goto ret; case OASOP: if(n->etype == OANDNOT) { n->etype = OAND; n->right = nod(OCOM, n->right, N); typecheck(&n->right, Erv); } n->left = safeexpr(n->left, init); walkexpr(&n->left, init); l = n->left; walkexpr(&n->right, init); /* * 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. * everywhere, rewrite complex /= 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 || (iscomplex[et] && n->etype == ODIV)) { 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) { t = n->type; n = mkcall("complex128div", types[TCOMPLEX128], init, conv(n->left, types[TCOMPLEX128]), conv(n->right, types[TCOMPLEX128])); n = conv(n, t); 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(isfixedarray(n->left->type)) if(n->right->type->width < 4) if((1<<(8*n->right->type->width)) <= n->left->type->bound) n->etype = 1; if(isconst(n->left, CTSTR)) if(n->right->type->width < 4) if((1<<(8*n->right->type->width)) <= n->left->val.u.sval->len) n->etype = 1; // check for static out of bounds if(isconst(n->right, CTINT) && !n->etype) { v = mpgetfix(n->right->val.u.xval); len = 1LL<<60; t = n->left->type; if(isconst(n->left, CTSTR)) len = n->left->val.u.sval->len; if(t != T && isptr[t->etype]) t = t->type; if(isfixedarray(t)) len = t->bound; if(v < 0 || v >= (1LL<<31) || v >= len) yyerror("index out of bounds"); else if(isconst(n->left, CTSTR)) { // replace "abc"[2] with 'b'. // delayed until now because "abc"[2] is not // an ideal constant. nodconst(n, n->type, n->left->val.u.sval->s[v]); } } 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: 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); len = 1LL<<60; t = n->left->type; if(t != T && isptr[t->etype]) t = t->type; if(isfixedarray(t)) len = t->bound; // check for static out of bounds // NOTE: v > len not v >= len. v1 = -1; v2 = -1; if(isconst(n->right->left, CTINT)) { v1 = mpgetfix(n->right->left->val.u.xval); if(v1 < 0 || v1 >= (1LL<<31) || v1 > len) { yyerror("slice index out of bounds"); v1 = -1; } } if(isconst(n->right->right, CTINT)) { v2 = mpgetfix(n->right->right->val.u.xval); if(v2 < 0 || v2 >= (1LL<<31) || v2 > len) { yyerror("slice index out of bounds"); v2 = -1; } } if(v1 >= 0 && v2 >= 0 && v1 > v2) yyerror("inverted slice range"); if(n->op == OSLICEARR) goto slicearray; // dynamic slice // sliceslice(old []any, lb uint64, hb uint64, width uint64) (ary []any) // sliceslice1(old []any, lb uint64, width uint64) (ary []any) t = n->type; et = n->etype; if(n->right->left == N) l = nodintconst(0); else l = conv(n->right->left, types[TUINT64]); 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, l, conv(n->right->right, types[TUINT64]), 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, l, nodintconst(t->type->width)); } n->etype = et; // preserve no-typecheck flag from OSLICE to the slice* call. goto ret; slicearray: // static slice // slicearray(old *any, uint64 nel, lb uint64, hb uint64, width uint64) (ary []any) t = n->type; fn = syslook("slicearray", 1); argtype(fn, n->left->type->type); // any-1 argtype(fn, t->type); // any-2 if(n->right->left == N) l = nodintconst(0); else l = conv(n->right->left, types[TUINT64]); if(n->right->right == N) r = nodintconst(n->left->type->type->bound); else r = conv(n->right->right, types[TUINT64]); n = mkcall1(fn, t, init, n->left, nodintconst(n->left->type->type->bound), l, r, nodintconst(t->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(0, 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: // If one argument to the comparison is an empty string, // comparing the lengths instead will yield the same result // without the function call. if((isconst(n->left, CTSTR) && n->left->val.u.sval->len == 0) || (isconst(n->right, CTSTR) && n->right->val.u.sval->len == 0)) { r = nod(n->etype, nod(OLEN, n->left, N), nod(OLEN, n->right, N)); typecheck(&r, Erv); walkexpr(&r, init); n = r; goto ret; } // s + "badgerbadgerbadger" == "badgerbadgerbadger" if((n->etype == OEQ || n->etype == ONE) && isconst(n->right, CTSTR) && n->left->op == OADDSTR && isconst(n->left->right, CTSTR) && cmpslit(n->right, n->left->right) == 0) { r = nod(n->etype, nod(OLEN, n->left->left, N), nodintconst(0)); typecheck(&r, Erv); walkexpr(&r, init); n = r; goto ret; } // prepare for rewrite below if(n->etype == OEQ || n->etype == ONE) { n->left = cheapexpr(n->left, init); n->right = cheapexpr(n->right, init); } // 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)); // quick check of len before full compare for == or != if(n->etype == OEQ || n->etype == ONE) { if(n->etype == OEQ) r = nod(OANDAND, nod(OEQ, nod(OLEN, n->left, N), nod(OLEN, n->right, N)), r); else r = nod(OOROR, nod(ONE, nod(OLEN, n->left, N), nod(OLEN, n->right, N)), r); typecheck(&r, Erv); walkexpr(&r, nil); } typecheck(&r, Erv); n = r; goto ret; case OADDSTR: n = addstr(n, init); goto ret; case OSLICESTR: // sys_slicestring(s, lb, hb) if(n->right->left == N) l = nodintconst(0); else l = conv(n->right->left, types[TINT]); if(n->right->right) { n = mkcall("slicestring", n->type, init, conv(n->left, types[TSTRING]), l, conv(n->right->right, types[TINT])); } else { n = mkcall("slicestring1", n->type, init, conv(n->left, types[TSTRING]), l); } goto ret; case OAPPEND: if(n->isddd) n = appendslice(n, init); else n = append(n, init); goto ret; case OCOPY: if(n->right->type->etype == TSTRING) fn = syslook("slicestringcopy", 1); else 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 OMAKECHAN: n = mkcall1(chanfn("makechan", 1, n->type), n->type, init, typename(n->type->type), conv(n->left, types[TINT64])); 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[TINT64])); goto ret; case OMAKESLICE: // makeslice(t *Type, nel int64, max int64) (ary []any) l = n->left; r = n->right; if(r == nil) l = r = safeexpr(l, init); t = n->type; fn = syslook("makeslice", 1); argtype(fn, t->type); // any-1 n = mkcall1(fn, n->type, init, typename(n->type), conv(l, types[TINT64]), conv(r, types[TINT64])); goto ret; case ORUNESTR: // sys_intstring(v) n = mkcall("intstring", n->type, init, conv(n->left, types[TINT64])); 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, conv(n->left, types[TSTRING])); 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(0, 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 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; } static Node* ascompatee1(int op, Node *l, Node *r, NodeList **init) { return convas(nod(OAS, l, r), init); } static 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 */ static int fncall(Node *l, Type *rt) { if(l->ullman >= UINF || l->op == OINDEXMAP) return 1; if(eqtype(l->type, rt)) return 0; return 1; } static 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); typecheck(&tmp, Erv); 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); } /* * package all the arguments that match a ... T parameter into a []T. */ static 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 char* dumptypes(Type **nl, char *what) { int first; Type *l; Iter savel; Fmt fmt; fmtstrinit(&fmt); fmtprint(&fmt, "\t"); l = structfirst(&savel, nl); first = 1; for(l = structfirst(&savel, nl); l != T; l = structnext(&savel)) { if(first) first = 0; else fmtprint(&fmt, ", "); fmtprint(&fmt, "%T", l); } if(first) fmtprint(&fmt, "[no arguments %s]", what); return fmtstrflush(&fmt); } static char* dumpnodetypes(NodeList *l, char *what) { int first; Node *r; Fmt fmt; fmtstrinit(&fmt); fmtprint(&fmt, "\t"); first = 1; for(; l; l=l->next) { r = l->n; if(first) first = 0; else fmtprint(&fmt, ", "); fmtprint(&fmt, "%T", r->type); } if(first) fmtprint(&fmt, "[no arguments %s]", what); return fmtstrflush(&fmt); } /* * check assign expression list to * a type list. called in * return expr-list * func(expr-list) */ static NodeList* ascompatte(int op, int isddd, Type **nl, NodeList *lr, int fp, NodeList **init) { Type *l, *ll; Node *r, *a; NodeList *nn, *lr0, *alist; Iter savel; char *l1, *l2; lr0 = lr; l = structfirst(&savel, nl); r = N; if(lr) r = lr->n; nn = nil; // f(g()) where g has multiple return values if(r != N && 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 && 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 slice of all // remaining arguments and pass it to // the ddd parameter. nn = mkdotargslice(lr, nn, l, fp, init); goto ret; } if(l == T || r == N) { if(l != T || r != N) { l1 = dumptypes(nl, "expected"); l2 = dumpnodetypes(lr0, "given"); if(l != T) yyerror("not enough arguments to %O\n%s\n%s", op, l1, l2); else yyerror("too many arguments to %O\n%s\n%s", op, l1, l2); } 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 || et == TUNSAFEPTR) { 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"); on = syslook("goprintf", 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); 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("new", 1); argtype(fn, t); return mkcall1(fn, ptrto(t), nil, nodintconst(t->width)); } static Node* convas(Node *n, NodeList **init) { Type *lt, *rt; if(n->op != OAS) fatal("convas: not OAS %O", n->op); n->typecheck = 1; if(n->left == N || n->right == N) goto out; lt = n->left->type; rt = n->right->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; n->right = assignconv(n->right, lt, "assignment"); walkexpr(&n->right, init); 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. */ static 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); } /* * walk through argin parameters. * generate and return code to allocate * copies of escaped parameters to the heap. */ static NodeList* paramstoheap(Type **argin, int out) { 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 && out && hasdefer) { // Defer might stop a panic and show the // return values as they exist at the time of panic. // Make sure to zero them on entry to the function. nn = list(nn, nod(OAS, nodarg(t, 1), N)); } 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)); if((v->class & ~PHEAP) != PPARAMOUT) nn = list(nn, nod(OAS, v, v->stackparam)); } return nn; } /* * walk through argout parameters copying back to stack */ static NodeList* returnsfromheap(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|PPARAMOUT)) continue; nn = list(nn, nod(OAS, v->stackparam, v)); } 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. */ static void heapmoves(void) { NodeList *nn; int32 lno; lno = lineno; lineno = curfn->lineno; nn = paramstoheap(getthis(curfn->type), 0); nn = concat(nn, paramstoheap(getinarg(curfn->type), 0)); nn = concat(nn, paramstoheap(getoutarg(curfn->type), 1)); curfn->enter = concat(curfn->enter, nn); lineno = curfn->endlineno; curfn->exit = returnsfromheap(getoutarg(curfn->type)); lineno = lno; } 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; ilist = 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; itype); 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; } static Node* addstr(Node *n, NodeList **init) { Node *r, *cat, *typstr; NodeList *in, *args; int i, count; count = 0; for(r=n; r->op == OADDSTR; r=r->left) count++; // r->right count++; // r // prepare call of runtime.catstring of type int, string, string, string // with as many strings as we have. cat = syslook("concatstring", 1); cat->type = T; cat->ntype = nod(OTFUNC, N, N); in = list1(nod(ODCLFIELD, N, typenod(types[TINT]))); // count typstr = typenod(types[TSTRING]); for(i=0; intype->list = in; cat->ntype->rlist = list1(nod(ODCLFIELD, N, typstr)); args = nil; for(r=n; r->op == OADDSTR; r=r->left) args = concat(list1(conv(r->right, types[TSTRING])), args); args = concat(list1(conv(r, types[TSTRING])), args); args = concat(list1(nodintconst(count)), args); r = nod(OCALL, cat, N); r->list = args; typecheck(&r, Erv); walkexpr(&r, init); r->type = n->type; return r; } static Node* appendslice(Node *n, NodeList **init) { Node *f; f = syslook("appendslice", 1); argtype(f, n->type); argtype(f, n->type->type); argtype(f, n->type); return mkcall1(f, n->type, init, typename(n->type), n->list->n, n->list->next->n); } // expand append(src, a [, b]* ) to // // init { // s := src // const argc = len(args) - 1 // if cap(s) - len(s) < argc { // s = growslice(s, argc) // } // n := len(s) // s = s[:n+argc] // s[n] = a // s[n+1] = b // ... // } // s static Node* append(Node *n, NodeList **init) { NodeList *l, *a; Node *nsrc, *ns, *nn, *na, *nx, *fn; int argc; walkexprlistsafe(n->list, init); nsrc = n->list->n; argc = count(n->list) - 1; if (argc < 1) { return nsrc; } l = nil; ns = nod(OXXX, N, N); // var s tempname(ns, nsrc->type); l = list(l, nod(OAS, ns, nsrc)); // s = src na = nodintconst(argc); // const argc nx = nod(OIF, N, N); // if cap(s) - len(s) < argc nx->ntest = nod(OLT, nod(OSUB, nod(OCAP, ns, N), nod(OLEN, ns, N)), na); fn = syslook("growslice", 1); // growslice(, old []T, n int64) (ret []T) argtype(fn, ns->type->type); // 1 old []any argtype(fn, ns->type->type); // 2 ret []any nx->nbody = list1(nod(OAS, ns, mkcall1(fn, ns->type, &nx->ninit, typename(ns->type), ns, conv(na, types[TINT64])))); l = list(l, nx); nn = nod(OXXX, N, N); // var n tempname(nn, types[TINT]); l = list(l, nod(OAS, nn, nod(OLEN, ns, N))); // n = len(s) nx = nod(OSLICE, ns, nod(OKEY, N, nod(OADD, nn, na))); // ...s[:n+argc] nx->etype = 1; // disable bounds check l = list(l, nod(OAS, ns, nx)); // s = s[:n+argc] for (a = n->list->next; a != nil; a = a->next) { nx = nod(OINDEX, ns, nn); // s[n] ... nx->etype = 1; // disable bounds check l = list(l, nod(OAS, nx, a->n)); // s[n] = arg if (a->next != nil) l = list(l, nod(OAS, nn, nod(OADD, nn, nodintconst(1)))); // n = n + 1 } typechecklist(l, Etop); walkstmtlist(l); *init = concat(*init, l); return ns; }