| // 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* curfn; |
| |
| 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: |
| return 0; |
| |
| case OCALL: |
| if(n->left->op == ONAME) { |
| switch(n->left->etype) { |
| case OPANIC: |
| case OPANICN: |
| return 0; |
| } |
| } |
| break; |
| } |
| |
| // all other statements |
| // will flow to the end |
| return 1; |
| } |
| |
| void |
| walk(Node *fn) |
| { |
| char s[50]; |
| |
| 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"); |
| 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 *n, NodeList **init) |
| { |
| if(debug['W']) |
| dump("\nbefore gettype", n); |
| walkexpr(n, Erv, init); |
| if(debug['W']) |
| dump("after gettype", n); |
| } |
| |
| void |
| walkdeflist(NodeList *l) |
| { |
| for(; l; l=l->next) |
| walkdef(l->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->type != T || n->diag) |
| return; |
| |
| if(n->trecur) { |
| // TODO(rsc): better loop message |
| fatal("loop"); |
| } |
| n->trecur = 1; |
| |
| init = nil; |
| switch(n->op) { |
| case OLITERAL: |
| if(n->ntype != N) { |
| walkexpr(n->ntype, Etype, &init); |
| n->type = n->ntype->type; |
| n->ntype = N; |
| if(n->type == T) { |
| n->diag = 1; |
| goto ret; |
| } |
| } |
| e = n->defn; |
| if(e == N) { |
| dump("walkdef", n); |
| } |
| walkexpr(e, Erv, &init); |
| if(e->op != OLITERAL) { |
| yyerror("const initializer must be constant"); |
| goto ret; |
| } |
| t = n->type; |
| if(t != T) |
| convlit(&e, t); |
| n->val = e->val; |
| n->type = e->type; |
| break; |
| } |
| |
| ret: |
| lineno = lno; |
| n->trecur = 0; |
| } |
| |
| void |
| walkstmtlist(NodeList *l) |
| { |
| for(; l; l=l->next) |
| walkstmt(l->n); |
| } |
| |
| void |
| walkstmt(Node *n) |
| { |
| NodeList *init; |
| NodeList *ll; |
| int lno; |
| |
| 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 OCLOSE: |
| case OCLOSED: |
| case OCALLMETH: |
| case OCALLINTER: |
| case OCALL: |
| case OSEND: |
| case ORECV: |
| case OPRINT: |
| case OPRINTN: |
| case OPANIC: |
| case OPANICN: |
| case OEMPTY: |
| init = nil; |
| walkexpr(n, Etop, &init); |
| n->ninit = concat(n->ninit, init); |
| break; |
| |
| case OBREAK: |
| case ODCL: |
| case OCONTINUE: |
| case OFALL: |
| case OGOTO: |
| case OLABEL: |
| break; |
| |
| case OBLOCK: |
| walkstmtlist(n->list); |
| break; |
| |
| case OXCASE: |
| yyerror("case statement out of place"); |
| n->op = OCASE; |
| case OCASE: |
| walkstmt(n->right); |
| break; |
| |
| case ODEFER: |
| hasdefer = 1; |
| walkexpr(n->left, Etop, &n->ninit); |
| break; |
| |
| case OFOR: |
| walkstmtlist(n->ninit); |
| walkbool(&n->ntest); |
| walkstmt(n->nincr); |
| walkstmtlist(n->nbody); |
| break; |
| |
| case OIF: |
| walkstmtlist(n->ninit); |
| walkbool(&n->ntest); |
| walkstmtlist(n->nbody); |
| walkstmtlist(n->nelse); |
| break; |
| |
| case OPROC: |
| walkexpr(n->left, Etop, &n->ninit); |
| break; |
| |
| case ORETURN: |
| walkexprlist(n->list, Erv, &n->ninit); |
| if(curfn->type->outnamed && n->list == nil) { |
| // print("special return\n"); |
| 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 OXFALL: |
| yyerror("fallthrough statement out of place"); |
| n->op = OFALL; |
| break; |
| } |
| } |
| |
| void |
| indir(Node *nl, Node *nr) |
| { |
| if(nr != N && nl != nr) |
| *nl = *nr; |
| } |
| |
| void |
| implicitstar(Node **nn) |
| { |
| Type *t; |
| Node *n; |
| |
| // insert implicit * if needed |
| n = *nn; |
| t = n->type; |
| if(t == T || !isptr[t->etype]) |
| return; |
| t = t->type; |
| if(t == T) |
| return; |
| if(!isfixedarray(t)) |
| return; |
| n = nod(OIND, n, N); |
| walkexpr(n, Elv, nil); |
| *nn = 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, int top, NodeList **init) |
| { |
| for(; l; l=l->next) |
| walkexpr(l->n, top, init); |
| } |
| |
| void |
| walkexpr(Node *n, int top, NodeList **init) |
| { |
| Node *r, *l; |
| NodeList *ll, *lr; |
| Type *t; |
| Sym *s; |
| int et, cl, cr, typeok, op; |
| int32 lno; |
| |
| if(n == N) |
| return; |
| lno = setlineno(n); |
| typeok = top & Etype; |
| top &= ~Etype; |
| |
| loop: |
| if(n == N) |
| goto ret; |
| |
| setlineno(n); |
| |
| if(debug['w'] > 1 && top == Etop) |
| dump("walk-before", n); |
| |
| reswitch: |
| t = T; |
| et = Txxx; |
| |
| switch(n->op) { |
| case ONAME: |
| case OTYPE: |
| case OLITERAL: |
| case ONONAME: |
| if(n->sym != S && n->type == T) |
| walkdef(n); |
| break; |
| } |
| |
| switch(n->op) { |
| default: |
| dump("walk", n); |
| fatal("walkexpr: switch 1 unknown op %N", n); |
| goto ret; |
| |
| case OTYPE: |
| goto ret; |
| |
| case OTARRAY: |
| t = typ(TARRAY); |
| l = n->left; |
| r = n->right; |
| if(l == nil) { |
| t->bound = -1; |
| } else { |
| walkexpr(l, Erv | Etype, init); |
| switch(l->op) { |
| default: |
| yyerror("invalid array bound %O", l->op); |
| break; |
| |
| case OLITERAL: |
| if(consttype(l) == CTINT) { |
| t->bound = mpgetfix(l->val.u.xval); |
| if(t->bound < 0) { |
| yyerror("array bound must be non-negative"); |
| t->bound = 1; |
| } |
| } |
| break; |
| |
| case OTYPE: |
| if(l->type == T) |
| break; |
| if(l->type->etype != TDDD) |
| yyerror("invalid array bound %T", l->type); |
| t->bound = -100; |
| break; |
| } |
| } |
| walkexpr(r, Etype, init); |
| t->type = r->type; |
| n->op = OTYPE; |
| n->type = t; |
| checkwidth(t); |
| goto ret; |
| |
| case OTMAP: |
| l = n->left; |
| r = n->right; |
| walkexpr(l, Etype, init); |
| walkexpr(r, Etype, init); |
| n->op = OTYPE; |
| n->type = maptype(l->type, r->type); |
| goto ret; |
| |
| case OTCHAN: |
| t = typ(TCHAN); |
| l = n->left; |
| walkexpr(l, Etype, init); |
| t->type = l->type; |
| t->chan = n->etype; |
| n->op = OTYPE; |
| n->type = t; |
| goto ret; |
| |
| case OTSTRUCT: |
| n->op = OTYPE; |
| n->type = dostruct(n->list, TSTRUCT); |
| goto ret; |
| |
| case OTINTER: |
| n->op = OTYPE; |
| n->type = dostruct(n->list, TINTER); |
| n->type = sortinter(n->type); |
| goto ret; |
| |
| case OTFUNC: |
| n->op = OTYPE; |
| n->type = functype(n->left, n->list, n->rlist); |
| goto ret; |
| |
| case OKEY: |
| walkexpr(n->left, top | typeok, init); |
| n = n->right; |
| goto loop; |
| |
| case OPRINT: |
| if(top != Etop) |
| goto nottop; |
| walkexprlist(n->list, Erv, init); |
| indir(n, prcompat(n->list, 0, 0)); |
| goto ret; |
| |
| case OPRINTN: |
| if(top != Etop) |
| goto nottop; |
| walkexprlist(n->list, Erv, init); |
| indir(n, prcompat(n->list, 1, 0)); |
| goto ret; |
| |
| case OPANIC: |
| if(top != Etop) |
| goto nottop; |
| walkexprlist(n->list, Erv, init); |
| indir(n, prcompat(n->list, 0, 1)); |
| goto ret; |
| |
| case OPANICN: |
| if(top != Etop) |
| goto nottop; |
| walkexprlist(n->list, Erv, init); |
| indir(n, prcompat(n->list, 2, 1)); |
| goto ret; |
| |
| case OLITERAL: |
| if(top != Erv) |
| goto nottop; |
| n->addable = 1; |
| goto ret; |
| |
| case ONONAME: |
| s = n->sym; |
| if(n->diag == 0) { |
| s->undef = 1; |
| n->diag = 1; |
| yyerror("undefined: %S", s); |
| goto ret; |
| } |
| if(top == Etop) |
| goto nottop; |
| goto ret; |
| |
| case ONAME: |
| if(top == Etop) |
| goto nottop; |
| if(!(n->class & PHEAP) && n->class != PPARAMREF) |
| n->addable = 1; |
| if(n->type == T) { |
| s = n->sym; |
| if(s->undef == 0) { |
| if(n->etype != 0) |
| yyerror("walkexpr: %S must be called", s, init); |
| else |
| yyerror("walkexpr: %S undeclared", s, init); |
| s->undef = 1; |
| } |
| } |
| goto ret; |
| |
| case OCALLMETH: |
| case OCALLINTER: |
| case OCALL: |
| if(top == Elv) |
| goto nottop; |
| |
| if(n->type != T) |
| goto ret; |
| |
| if(n->left == N) |
| goto ret; |
| |
| if(n->left->op == ONAME && n->left->etype != 0) { |
| // builtin OLEN, OCAP, etc. |
| n->op = n->left->etype; |
| n->left = N; |
| goto reswitch; |
| } |
| |
| walkexpr(n->left, Erv | Etype, init); |
| defaultlit(&n->left, T); |
| |
| t = n->left->type; |
| if(t == T) |
| goto ret; |
| |
| if(n->left->op == ODOTMETH) |
| n->op = OCALLMETH; |
| if(n->left->op == ODOTINTER) |
| n->op = OCALLINTER; |
| if(n->left->op == OTYPE) { |
| n->op = OCONV; |
| if(top != Erv) |
| goto nottop; |
| // turn CALL(type, arg) into CONV(arg) w/ type. |
| n->type = n->left->type; |
| if(n->list == nil) { |
| yyerror("missing argument in type conversion"); |
| goto ret; |
| } |
| if(n->list->next != nil) { |
| yyerror("too many arguments in type conversion"); |
| goto ret; |
| } |
| n->left = n->list->n; |
| n->list = nil; |
| goto reswitch; |
| } |
| |
| if(t->etype != TFUNC) { |
| yyerror("call of a non-function: %T", t); |
| goto ret; |
| } |
| |
| dowidth(t); |
| n->type = *getoutarg(t); |
| switch(t->outtuple) { |
| case 0: |
| if(top == Erv) { |
| yyerror("function requires a return type"); |
| n->type = types[TINT]; |
| } |
| break; |
| |
| case 1: |
| if(n->type != T && n->type->type != T && n->type->type->type != T) |
| n->type = n->type->type->type; |
| break; |
| } |
| |
| walkexprlist(n->list, Erv, init); |
| |
| switch(n->op) { |
| default: |
| fatal("walk: op: %O", n->op); |
| |
| case OCALLINTER: |
| ll = ascompatte(n->op, getinarg(t), n->list, 0, init); |
| n->list = reorder1(ll); |
| break; |
| |
| case OCALL: |
| 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); |
| } |
| break; |
| |
| case OCALLMETH: |
| 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); |
| break; |
| } |
| goto ret; |
| |
| case OAS: |
| if(top != Etop) |
| goto nottop; |
| *init = concat(*init, n->ninit); |
| n->ninit = nil; |
| l = n->left; |
| r = n->right; |
| walkexpr(l, Elv, init); |
| if(l == N || r == N) |
| goto ret; |
| walkexpr(r, Erv, init); |
| indir(n, ascompatee1(n->op, n->left, n->right, init)); |
| goto ret; |
| |
| case OAS2: |
| if(top != Etop) |
| goto nottop; |
| *init = concat(*init, n->ninit); |
| n->ninit = nil; |
| |
| walkexprlist(n->list, Elv, init); |
| |
| cl = count(n->list); |
| cr = count(n->rlist); |
| if(cl == cr) { |
| multias: |
| walkexprlist(n->rlist, Erv, init); |
| ll = ascompatee(OAS, n->list, n->rlist, init); |
| ll = reorder3(ll); |
| indir(n, liststmt(ll)); |
| goto ret; |
| } |
| |
| l = n->list->n; |
| r = n->rlist->n; |
| |
| // count mismatch - special cases |
| switch(r->op) { |
| case OCALLMETH: |
| case OCALLINTER: |
| case OCALL: |
| if(cr == 1) { |
| // a,b,... = fn() |
| walkexpr(r, Erv, init); |
| if(r->type == T || r->type->etype != TSTRUCT) |
| break; |
| ll = ascompatet(n->op, n->list, &r->type, 0, init); |
| indir(n, liststmt(concat(list1(r), ll))); |
| goto ret; |
| } |
| break; |
| |
| case OINDEX: |
| if(cl == 2 && cr == 1) { |
| // a,b = map[] - mapaccess2 |
| walkexpr(r->left, Erv, init); |
| implicitstar(&r->left); |
| if(!istype(r->left->type, TMAP)) |
| break; |
| l = mapop(n, top, init); |
| if(l == N) |
| break; |
| indir(n, l); |
| goto ret; |
| } |
| break; |
| |
| case ORECV: |
| if(cl == 2 && cr == 1) { |
| // a,b = <chan - chanrecv2 |
| walkexpr(r->left, Erv, init); |
| if(!istype(r->left->type, TCHAN)) |
| break; |
| l = chanop(n, top, init); |
| if(l == N) |
| break; |
| indir(n, l); |
| goto ret; |
| } |
| break; |
| |
| case ODOTTYPE: |
| walkdottype(r, init); |
| if(cl == 2 && cr == 1) { |
| // a,b = i.(T) |
| if(r->left == N) |
| break; |
| et = ifaceas1(r->type, r->left->type, 1); |
| switch(et) { |
| case I2Isame: |
| case E2Esame: |
| n->rlist = list(list1(r->left), nodbool(1)); |
| goto multias; |
| case I2E: |
| n->list = list(list1(n->right), nodbool(1)); |
| goto multias; |
| 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); |
| ll = ascompatet(n->op, n->list, &r->type, 0, init); |
| indir(n, liststmt(concat(list1(r), ll))); |
| goto ret; |
| } |
| break; |
| } |
| |
| switch(l->op) { |
| case OINDEX: |
| if(cl == 1 && cr == 2) { |
| // map[] = a,b - mapassign2 |
| if(!istype(l->left->type, TMAP)) |
| break; |
| l = mapop(n, top, init); |
| if(l == N) |
| break; |
| indir(n, l); |
| goto ret; |
| } |
| break; |
| } |
| if(l->diag == 0) { |
| l->diag = 1; |
| yyerror("assignment count mismatch: %d = %d", cl, cr); |
| } |
| goto ret; |
| |
| case OINDREG: |
| case OEMPTY: |
| goto ret; |
| |
| case ODOTTYPE: |
| walkdottype(n, init); |
| // fall through |
| case OCONV: |
| if(top != Erv) |
| goto nottop; |
| walkconv(n, init); |
| goto ret; |
| |
| case OCONVNOP: |
| goto ret; |
| |
| case OCOMPMAP: |
| case OCOMPSLICE: |
| goto ret; |
| |
| case OCOMPOS: |
| walkexpr(n->right, Etype, init); |
| t = n->right->type; |
| n->type = t; |
| if(t == T) |
| goto ret; |
| |
| switch(t->etype) { |
| default: |
| yyerror("invalid type for composite literal: %T", t); |
| goto ret; |
| |
| case TSTRUCT: |
| r = structlit(n, N, init); |
| break; |
| |
| case TARRAY: |
| r = arraylit(n, N, init); |
| break; |
| |
| case TMAP: |
| r = maplit(n, N, init); |
| break; |
| } |
| indir(n, r); |
| goto ret; |
| |
| case ONOT: |
| if(top != Erv) |
| goto nottop; |
| evconst(n); |
| if(n->op == OLITERAL) |
| goto ret; |
| walkexpr(n->left, Erv, init); |
| if(n->left == N || n->left->type == T) |
| goto ret; |
| et = n->left->type->etype; |
| break; |
| |
| case OASOP: |
| if(top != Etop) |
| goto nottop; |
| walkexpr(n->left, Elv, init); |
| l = n->left; |
| if(l->op == OINDEX && istype(l->left->type, TMAP)) |
| indir(n, mapop(n, top, init)); |
| if(n->etype == OLSH || n->etype == ORSH) |
| goto shft; |
| goto com; |
| |
| case OLSH: |
| case ORSH: |
| if(top != Erv) |
| goto nottop; |
| walkexpr(n->left, Erv, init); |
| |
| shft: |
| walkexpr(n->right, Erv, init); |
| if(n->left == N || n->right == N) |
| goto ret; |
| evconst(n); |
| if(n->op == OLITERAL) |
| goto ret; |
| // do NOT defaultlit n->left. |
| // let parent defaultlit or convlit instead. |
| defaultlit(&n->right, types[TUINT]); |
| if(n->left->type == T || n->right->type == T) |
| goto ret; |
| et = n->right->type->etype; |
| if(issigned[et] || !isint[et]) |
| goto badt; |
| // check of n->left->type happens in second switch. |
| break; |
| |
| case OMOD: |
| case OAND: |
| case OANDNOT: |
| case OOR: |
| case OXOR: |
| case OANDAND: |
| case OOROR: |
| case OEQ: |
| case ONE: |
| case OLT: |
| case OLE: |
| case OGE: |
| case OGT: |
| case OADD: |
| case OSUB: |
| case OMUL: |
| case ODIV: |
| if(top != Erv) |
| goto nottop; |
| walkexpr(n->left, Erv, init); |
| |
| com: |
| walkexpr(n->right, Erv, init); |
| if(n->left == N || n->right == N) |
| goto ret; |
| evconst(n); |
| if(n->op == OLITERAL) |
| goto ret; |
| defaultlit2(&n->left, &n->right); |
| if(n->left->type == T || n->right->type == T) |
| goto ret; |
| if(!eqtype(n->left->type, n->right->type)) |
| goto badt; |
| |
| switch(n->op) { |
| case OANDNOT: |
| n->op = OAND; |
| n->right = nod(OCOM, n->right, N); |
| n->right->type = n->right->left->type; |
| break; |
| |
| case OASOP: |
| if(n->etype == OANDNOT) { |
| n->etype = OAND; |
| n->right = nod(OCOM, n->right, N); |
| n->right->type = n->right->left->type; |
| break; |
| } |
| if(istype(n->left->type, TSTRING)) { |
| indir(n, stringop(n, top, init)); |
| goto ret; |
| } |
| break; |
| |
| case OEQ: |
| case ONE: |
| case OLT: |
| case OLE: |
| case OGE: |
| case OGT: |
| case OADD: |
| if(istype(n->left->type, TSTRING)) { |
| indir(n, stringop(n, top, nil)); |
| goto ret; |
| } |
| break; |
| } |
| break; |
| |
| case OMINUS: |
| case OPLUS: |
| case OCOM: |
| if(top != Erv) |
| goto nottop; |
| walkexpr(n->left, Erv, init); |
| if(n->left == N) |
| goto ret; |
| evconst(n); |
| if(n->op == OLITERAL) |
| goto ret; |
| break; |
| |
| case OLEN: |
| if(top != Erv) |
| goto nottop; |
| if(n->left == N) { |
| if(n->list == nil) { |
| yyerror("missing argument to len"); |
| goto ret; |
| } |
| if(n->list->next) |
| yyerror("too many arguments to len"); |
| n->left = n->list->n; |
| } |
| walkexpr(n->left, Erv, init); |
| defaultlit(&n->left, T); |
| implicitstar(&n->left); |
| t = n->left->type; |
| if(t == T) |
| goto ret; |
| switch(t->etype) { |
| default: |
| goto badt; |
| case TSTRING: |
| if(isconst(n->left, CTSTR)) |
| nodconst(n, types[TINT], n->left->val.u.sval->len); |
| break; |
| case TMAP: |
| break; |
| case TARRAY: |
| if(t->bound >= 0) |
| nodconst(n, types[TINT], t->bound); |
| break; |
| } |
| n->type = types[TINT]; |
| goto ret; |
| |
| case OCAP: |
| if(top != Erv) |
| goto nottop; |
| if(n->left == N) { |
| if(n->list == nil) { |
| yyerror("missing argument to cap"); |
| goto ret; |
| } |
| if(n->list->next) |
| yyerror("too many arguments to cap"); |
| n->left = n->list->n; |
| } |
| walkexpr(n->left, Erv, init); |
| defaultlit(&n->left, T); |
| implicitstar(&n->left); |
| t = n->left->type; |
| if(t == T) |
| goto ret; |
| switch(t->etype) { |
| default: |
| goto badt; |
| case TARRAY: |
| if(t->bound >= 0) |
| nodconst(n, types[TINT], t->bound); |
| break; |
| } |
| n->type = types[TINT]; |
| goto ret; |
| |
| case OINDEX: |
| if(top == Etop) |
| goto nottop; |
| |
| walkexpr(n->left, Erv, init); |
| walkexpr(n->right, Erv, init); |
| |
| if(n->left == N || n->right == N) |
| goto ret; |
| |
| defaultlit(&n->left, T); |
| implicitstar(&n->left); |
| |
| t = n->left->type; |
| if(t == T) |
| goto ret; |
| |
| switch(t->etype) { |
| default: |
| defaultlit(&n->right, T); |
| goto badt; |
| |
| case TSTRING: |
| // right side must be an int |
| if(top != Erv) |
| goto nottop; |
| defaultlit(&n->right, types[TINT]); |
| if(n->right->type == T) |
| break; |
| if(!isint[n->right->type->etype]) |
| goto badt; |
| indir(n, stringop(n, top, nil)); |
| break; |
| |
| case TMAP: |
| // right side must be map type |
| defaultlit(&n->right, t->down); |
| if(n->right->type == T) |
| break; |
| if(!eqtype(n->right->type, t->down)) |
| goto badt; |
| n->type = t->type; |
| if(top == Erv) |
| indir(n, mapop(n, top, nil)); |
| break; |
| |
| case TARRAY: |
| // right side must be an int |
| defaultlit(&n->right, types[TINT]); |
| if(n->right->type == T) |
| break; |
| if(!isint[n->right->type->etype]) |
| goto badt; |
| n->type = t->type; |
| break; |
| } |
| goto ret; |
| |
| case OCLOSE: |
| if(top != Etop) |
| goto nottop; |
| walkexpr(n->left, Erv, init); // chan |
| indir(n, chanop(n, top, nil)); |
| goto ret; |
| |
| case OCLOSED: |
| if(top == Elv) |
| goto nottop; |
| walkexpr(n->left, Erv, init); // chan |
| indir(n, chanop(n, top, nil)); |
| goto ret; |
| |
| case OSEND: |
| if(top == Elv) |
| goto nottop; |
| walkexpr(n->left, Erv, init); // chan |
| walkexpr(n->right, Erv, init); // e |
| indir(n, chanop(n, top, nil)); |
| goto ret; |
| |
| case ORECV: |
| if(top == Elv) |
| goto nottop; |
| if(n->right == N) { |
| walkexpr(n->left, Erv, init); // chan |
| indir(n, chanop(n, top, init)); // returns e blocking |
| goto ret; |
| } |
| walkexpr(n->left, Elv, init); // e |
| walkexpr(n->right, Erv, init); // chan |
| indir(n, chanop(n, top, nil)); // returns bool non-blocking |
| goto ret; |
| |
| case OSLICE: |
| if(top == Etop) |
| goto nottop; |
| |
| walkexpr(n->left, top, init); |
| walkexpr(n->right, Erv, init); |
| if(n->left == N || n->right == N) |
| goto ret; |
| defaultlit(&n->left, T); |
| defaultlit(&n->right->left, types[TUINT]); |
| defaultlit(&n->right->right, types[TUINT]); |
| implicitstar(&n->left); |
| t = n->left->type; |
| if(t == T) |
| goto ret; |
| if(t->etype == TSTRING) { |
| indir(n, stringop(n, top, nil)); |
| goto ret; |
| } |
| if(t->etype == TARRAY) { |
| indir(n, arrayop(n, top)); |
| goto ret; |
| } |
| badtype(OSLICE, n->left->type, T); |
| goto ret; |
| |
| case ODOT: |
| case ODOTPTR: |
| case ODOTMETH: |
| case ODOTINTER: |
| if(top == Etop) |
| goto nottop; |
| defaultlit(&n->left, T); |
| walkdot(n, init); |
| goto ret; |
| |
| case OADDR: |
| if(top != Erv) |
| goto nottop; |
| defaultlit(&n->left, T); |
| if(n->left->op == OCOMPOS) { |
| walkexpr(n->left->right, Etype, init); |
| n->left->type = n->left->right->type; |
| if(n->left->type == T) |
| goto ret; |
| |
| Node *nvar, *nas, *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 |
| |
| nvar = nod(OXXX, N, N); |
| tempname(nvar, ptrto(n->left->type)); |
| |
| nas = nod(OAS, nvar, callnew(n->left->type)); |
| walkexpr(nas, Etop, init); |
| *init = list(*init, nas); |
| |
| nstar = nod(OIND, nvar, N); |
| nstar->type = n->left->type; |
| |
| switch(n->left->type->etype) { |
| case TSTRUCT: |
| structlit(n->left, nstar, init); |
| break; |
| case TARRAY: |
| arraylit(n->left, nstar, init); |
| break; |
| case TMAP: |
| maplit(n->left, nstar, init); |
| break; |
| default: |
| goto badlit; |
| } |
| |
| // walkexpr(n->left->left, Erv, init); |
| indir(n, nvar); |
| goto ret; |
| } |
| |
| badlit: |
| if(istype(n->left->type, TFUNC) && n->left->class == PFUNC) { |
| if(!n->diag) { |
| n->diag = 1; |
| yyerror("cannot take address of function"); |
| } |
| } |
| if(n->left == N) |
| goto ret; |
| walkexpr(n->left, Elv, init); |
| t = n->left->type; |
| if(t == T) |
| goto ret; |
| addrescapes(n->left); |
| n->type = ptrto(t); |
| goto ret; |
| |
| case OIND: |
| if(top == Etop) |
| goto nottop; |
| if(top == Elv) // even if n is lvalue, n->left is rvalue |
| top = Erv; |
| if(n->left == N) |
| goto ret; |
| walkexpr(n->left, top | Etype, init); |
| defaultlit(&n->left, T); |
| if(n->left->op == OTYPE) { |
| n->op = OTYPE; |
| n->type = ptrto(n->left->type); |
| goto ret; |
| } |
| t = n->left->type; |
| if(t == T) |
| goto ret; |
| if(!isptr[t->etype]) |
| goto badt; |
| n->type = t->type; |
| goto ret; |
| |
| case OMAKE: |
| if(top != Erv) |
| goto nottop; |
| indir(n, makecompat(n)); |
| goto ret; |
| |
| case ONEW: |
| if(top != Erv) |
| goto nottop; |
| if(n->list == nil) { |
| yyerror("missing argument to new"); |
| goto ret; |
| } |
| l = n->list->n; |
| if(n->list->next) |
| yyerror("too many arguments to new"); |
| walkexpr(l, Etype, init); |
| if((t = l->type) == T) |
| ; |
| else |
| indir(n, callnew(t)); |
| goto ret; |
| } |
| |
| /* |
| * ======== second switch ======== |
| */ |
| |
| op = n->op; |
| if(op == OASOP) |
| op = n->etype; |
| switch(op) { |
| default: |
| fatal("walkexpr: switch 2 unknown op %N", n, init); |
| goto ret; |
| |
| case OASOP: |
| break; |
| |
| case ONOT: |
| case OANDAND: |
| case OOROR: |
| if(n->left->type == T) |
| goto ret; |
| et = n->left->type->etype; |
| if(et != TBOOL) |
| goto badt; |
| t = types[TBOOL]; |
| break; |
| |
| case OEQ: |
| case ONE: |
| if(n->left->type == T) |
| goto ret; |
| et = n->left->type->etype; |
| if(!okforeq[et] && !isslice(n->left->type)) |
| goto badt; |
| if(isinter(n->left->type)) { |
| indir(n, ifaceop(n)); |
| goto ret; |
| } |
| t = types[TBOOL]; |
| break; |
| |
| case OLT: |
| case OLE: |
| case OGE: |
| case OGT: |
| if(n->left->type == T) |
| goto ret; |
| et = n->left->type->etype; |
| if(!okforadd[et] && et != TSTRING) |
| goto badt; |
| t = types[TBOOL]; |
| break; |
| |
| case OADD: |
| case OSUB: |
| case OMUL: |
| case ODIV: |
| case OPLUS: |
| if(n->left->type == T) |
| goto ret; |
| et = n->left->type->etype; |
| if(!okforadd[et]) |
| goto badt; |
| break; |
| |
| case OMINUS: |
| if(n->left->type == T) |
| goto ret; |
| et = n->left->type->etype; |
| if(!okforadd[et]) |
| goto badt; |
| if(isfloat[et]) { |
| // TODO(rsc): Can do this more efficiently, |
| // but OSUB is wrong. Should be in back end anyway. |
| indir(n, nod(OMUL, n->left, nodintconst(-1))); |
| walkexpr(n, Erv, init); |
| goto ret; |
| } |
| break; |
| |
| case OLSH: |
| case ORSH: |
| case OAND: |
| case OANDNOT: |
| case OOR: |
| case OXOR: |
| case OMOD: |
| case OCOM: |
| if(n->left->type == T) |
| goto ret; |
| et = n->left->type->etype; |
| if(et != TIDEAL && !okforand[et]) |
| goto badt; |
| break; |
| } |
| |
| /* |
| * rewrite div and mod into function calls |
| * on 32-bit architectures. |
| */ |
| switch(n->op) { |
| case ODIV: |
| case OMOD: |
| et = n->left->type->etype; |
| if(widthptr > 4 || (et != TUINT64 && et != TINT64)) |
| break; |
| if(et == TINT64) |
| strcpy(namebuf, "int64"); |
| else |
| strcpy(namebuf, "uint64"); |
| if(n->op == ODIV) |
| strcat(namebuf, "div"); |
| else |
| strcat(namebuf, "mod"); |
| l = syslook(namebuf, 0); |
| n->left = nod(OCONV, n->left, N); |
| n->left->type = types[et]; |
| n->right = nod(OCONV, n->right, N); |
| n->right->type = types[et]; |
| r = nod(OCALL, l, N); |
| r->list = list(list1(n->left), n->right); |
| r = nod(OCONV, r, N); |
| r->type = n->left->left->type; |
| walkexpr(r, Erv, init); |
| indir(n, r); |
| goto ret; |
| |
| case OASOP: |
| et = n->left->type->etype; |
| if(widthptr > 4 || (et != TUINT64 && et != TINT64)) |
| break; |
| l = saferef(n->left, init); |
| r = nod(OAS, l, nod(n->etype, l, n->right)); |
| walkexpr(r, Etop, init); |
| indir(n, r); |
| goto ret; |
| } |
| |
| if(t == T) |
| t = n->left->type; |
| n->type = t; |
| goto ret; |
| |
| nottop: |
| if(n->diag) |
| goto ret; |
| n->diag = 1; |
| switch(top | typeok) { |
| default: |
| yyerror("didn't expect %O here [top=%d]", n->op, top); |
| break; |
| case Etype: |
| yyerror("operation %O not allowed in type context", n->op); |
| break; |
| case Etop: |
| yyerror("operation %O not allowed in statement context", n->op); |
| break; |
| case Elv: |
| yyerror("operation %O not allowed in assignment context", n->op); |
| break; |
| case Erv: |
| case Erv | Etype: |
| yyerror("operation %O not allowed in expression context", n->op); |
| break; |
| } |
| goto ret; |
| |
| badt: |
| if(n->diag) |
| goto ret; |
| n->diag = 1; |
| if(n->right == N) { |
| if(n->left == N) { |
| badtype(n->op, T, T); |
| goto ret; |
| } |
| badtype(n->op, n->left->type, T); |
| goto ret; |
| } |
| op = n->op; |
| if(op == OASOP) |
| op = n->etype; |
| badtype(op, n->left->type, n->right->type); |
| goto ret; |
| |
| ret: |
| if(debug['w'] && top == Etop && n != N) |
| dump("walk", n); |
| |
| if(typeok && top == 0) { // must be type |
| if(n->op != OTYPE) { |
| if(n->sym) { |
| if(!n->sym->undef) |
| yyerror("%S is not a type", n->sym); |
| } else { |
| yyerror("expr %O is not type", n->op); |
| n->op = OTYPE; // leads to fewer errors later |
| n->type = T; |
| } |
| } |
| } |
| if(!typeok && n->op == OTYPE) |
| yyerror("cannot use type %T as expr", n->type); |
| |
| ullmancalc(n); |
| lineno = lno; |
| } |
| |
| void |
| walkbool(Node **np) |
| { |
| Node *n; |
| |
| n = *np; |
| if(n == N) |
| return; |
| walkexpr(n, Erv, &n->ninit); |
| defaultlit(np, T); |
| n = *np; |
| if(n->type != T && !eqtype(n->type, types[TBOOL])) |
| yyerror("IF and FOR require a boolean type"); |
| } |
| |
| void |
| walkdottype(Node *n, NodeList **init) |
| { |
| walkexpr(n->left, Erv, init); |
| if(n->left == N) |
| return; |
| defaultlit(&n->left, T); |
| if(!isinter(n->left->type)) |
| yyerror("type assertion requires interface on left, have %T", n->left->type); |
| if(n->right != N) { |
| walkexpr(n->right, Etype, init); |
| n->type = n->right->type; |
| n->right = N; |
| } |
| } |
| |
| void |
| walkconv(Node *n, NodeList **init) |
| { |
| int et; |
| char *what; |
| Type *t; |
| Node *l; |
| |
| t = n->type; |
| if(t == T) |
| return; |
| l = n->left; |
| if(l == N) |
| return; |
| walkexpr(l, Erv, init); |
| 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) |
| goto nop; |
| if(et != Inone) { |
| indir(n, ifacecvt(t, l, et)); |
| return; |
| } |
| goto bad; |
| } |
| |
| // otherwise, conversion. |
| convlit1(&n->left, t, 1); |
| l = n->left; |
| if(l->type == T) |
| return; |
| |
| // no-op conversion |
| if(cvttype(t, l->type) == 1) { |
| nop: |
| if(l->op == OLITERAL) { |
| indir(n, l); |
| l->type = t; |
| return; |
| } |
| // leave OCONV node in place |
| // in case tree gets walked again. |
| // back end will ignore. |
| n->op = OCONVNOP; |
| return; |
| } |
| |
| // to/from interface. |
| // ifaceas1 will generate a good error |
| // if the conversion is invalid. |
| if(t->etype == TINTER || l->type->etype == TINTER) { |
| indir(n, ifacecvt(t, l, ifaceas1(t, l->type, 0))); |
| return; |
| } |
| |
| // simple fix-float |
| if(isint[l->type->etype] || isfloat[l->type->etype]) |
| if(isint[t->etype] || isfloat[t->etype]) { |
| evconst(n); |
| return; |
| } |
| |
| // to string |
| if(l->type != T) |
| if(istype(t, TSTRING)) { |
| et = l->type->etype; |
| if(isint[et]) { |
| indir(n, stringop(n, Erv, nil)); |
| return; |
| } |
| |
| // can convert []byte and *[10]byte |
| if((isptr[et] && isfixedarray(l->type->type) && istype(l->type->type->type, TUINT8)) |
| || (isslice(l->type) && istype(l->type->type, TUINT8))) { |
| n->op = OARRAY; |
| indir(n, stringop(n, Erv, nil)); |
| return; |
| } |
| |
| // can convert []int and *[10]int |
| if((isptr[et] && isfixedarray(l->type->type) && istype(l->type->type->type, TINT)) |
| || (isslice(l->type) && istype(l->type->type, TINT))) { |
| n->op = OARRAY; |
| indir(n, stringop(n, Erv, nil)); |
| return; |
| } |
| } |
| |
| // convert dynamic to static generated by ONEW/OMAKE |
| if(isfixedarray(t) && isslice(l->type)) |
| return; |
| |
| // convert static array to dynamic array |
| if(isslice(t) && isptr[l->type->etype] && isfixedarray(l->type->type)) { |
| if(eqtype(t->type->type, l->type->type->type->type)) { |
| indir(n, arrayop(n, Erv)); |
| return; |
| } |
| } |
| |
| // convert to unsafe.pointer |
| if(isptrto(n->type, TANY)) { |
| if(isptr[l->type->etype]) |
| return; |
| if(l->type->etype == TUINTPTR) |
| return; |
| } |
| |
| // convert from unsafe.pointer |
| if(isptrto(l->type, TANY)) { |
| if(isptr[t->etype]) |
| return; |
| if(t->etype == TUINTPTR) |
| return; |
| } |
| |
| 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* |
| selcase(Node *n, Node *var, NodeList **init) |
| { |
| Node *a, *r, *on, *c; |
| Type *t; |
| NodeList *args; |
| |
| if(n->list == nil) |
| goto dflt; |
| c = n->list->n; |
| if(c->op == ORECV) |
| goto recv; |
| |
| walkexpr(c->left, Erv, init); // chan |
| walkexpr(c->right, Erv, init); // elem |
| |
| t = fixchan(c->left->type); |
| if(t == T) |
| return N; |
| |
| if(!(t->chan & Csend)) { |
| yyerror("cannot send on %T", t); |
| return N; |
| } |
| |
| convlit(&c->right, t->type); |
| if(!ascompat(t->type, c->right->type)) { |
| badtype(c->op, t->type, c->right->type); |
| return N; |
| } |
| |
| // selectsend(sel *byte, hchan *chan any, elem any) (selected bool); |
| on = syslook("selectsend", 1); |
| argtype(on, t->type); |
| argtype(on, t->type); |
| |
| a = var; // sel-var |
| args = list1(a); |
| a = c->left; // chan |
| args = list(args, a); |
| a = c->right; // elem |
| args = list(args, a); |
| goto out; |
| |
| recv: |
| if(c->right != N) |
| goto recv2; |
| |
| walkexpr(c->left, Erv, init); // chan |
| |
| t = fixchan(c->left->type); |
| if(t == T) |
| return N; |
| |
| if(!(t->chan & Crecv)) { |
| yyerror("cannot receive from %T", t); |
| return N; |
| } |
| |
| // selectrecv(sel *byte, hchan *chan any, elem *any) (selected bool); |
| on = syslook("selectrecv", 1); |
| argtype(on, t->type); |
| argtype(on, t->type); |
| |
| a = var; // sel-var |
| args = list1(a); |
| |
| a = c->left; // chan |
| args = list(args, a); |
| |
| a = c->left; // nil elem |
| a = nod(OLITERAL, N, N); |
| a->val.ctype = CTNIL; |
| a->type = types[TNIL]; |
| args = list(args, a); |
| goto out; |
| |
| recv2: |
| walkexpr(c->right, Erv, init); // chan |
| |
| t = fixchan(c->right->type); |
| if(t == T) |
| return N; |
| |
| if(!(t->chan & Crecv)) { |
| yyerror("cannot receive from %T", t); |
| return N; |
| } |
| |
| walkexpr(c->left, Elv, init); // check elem |
| convlit(&c->left, t->type); |
| if(!ascompat(t->type, c->left->type)) { |
| badtype(c->op, t->type, c->left->type); |
| return N; |
| } |
| |
| // selectrecv(sel *byte, hchan *chan any, elem *any) (selected bool); |
| on = syslook("selectrecv", 1); |
| argtype(on, t->type); |
| argtype(on, t->type); |
| |
| a = var; // sel-var |
| args = list1(a); |
| |
| a = c->right; // chan |
| args = list(args, a); |
| |
| a = c->left; // elem |
| a = nod(OADDR, a, N); |
| args = list(args, a); |
| goto out; |
| |
| dflt: |
| // selectdefault(sel *byte); |
| on = syslook("selectdefault", 0); |
| a = var; |
| args = list1(a); |
| goto out; |
| |
| out: |
| a = nod(OCALL, on, N); |
| a->list = args; |
| r = nod(OIF, N, N); |
| r->ntest = a; |
| |
| return r; |
| } |
| |
| /* |
| * enumerate the special cases |
| * of the case statement: |
| * case v := <-chan // select and switch |
| */ |
| Node* |
| selectas(Node *name, Node *expr, NodeList **init) |
| { |
| Type *t; |
| |
| if(expr == N || expr->op != ORECV) |
| goto bad; |
| |
| walkexpr(expr->left, Erv, init); |
| t = expr->left->type; |
| if(t == T) |
| goto bad; |
| if(t->etype != TCHAN) |
| goto bad; |
| t = t->type; |
| return old2new(name, t, init); |
| |
| bad: |
| return name; |
| } |
| |
| void |
| walkselect(Node *sel) |
| { |
| Node *n, *l, *oc, *on, *r; |
| Node *var, *def; |
| NodeList *args, *res, *bod, *nbod, *init, *ln; |
| int count, op; |
| int32 lno; |
| |
| lno = setlineno(sel); |
| |
| init = nil; |
| |
| // generate sel-struct |
| var = nod(OXXX, N, N); |
| tempname(var, ptrto(types[TUINT8])); |
| |
| if(sel->list == nil) { |
| yyerror("empty select"); |
| return; |
| } |
| |
| count = 0; // number of cases |
| res = nil; // entire select body |
| bod = nil; // body of each case |
| oc = N; // last case |
| def = N; // default case |
| for(ln=sel->list; ln; ln=ln->next) { |
| n = ln->n; |
| setlineno(n); |
| if(n->op != OXCASE) |
| fatal("walkselect %O", n->op); |
| |
| count++; |
| l = N; |
| if(n->list == nil) { |
| op = ORECV; // actual value not used |
| if(def != N) |
| yyerror("repeated default; first at %L", def->lineno); |
| def = n; |
| } else { |
| l = n->list->n; |
| op = l->op; |
| if(n->list->next) { |
| yyerror("select cases cannot be lists"); |
| continue; |
| } |
| } |
| |
| nbod = nil; |
| switch(op) { |
| default: |
| yyerror("select cases must be send, recv or default %O", op); |
| continue; |
| |
| case OAS: |
| // convert new syntax (a=recv(chan)) to (recv(a,chan)) |
| if(l->right == N || l->right->op != ORECV) { |
| yyerror("select cases must be send, recv or default %O", l->right->op); |
| break; |
| } |
| r = l->right; // rcv |
| r->right = r->left; |
| r->left = l->left; |
| n->list->n = r; |
| |
| // convert case x := foo: body |
| // to case tmp := foo: x := tmp; body. |
| // if x escapes and must be allocated |
| // on the heap, this delays the allocation |
| // until after the select has chosen this branch. |
| if(n->ninit != nil && n->ninit->n->op == ODCL) { |
| on = nod(OXXX, N, N); |
| tempname(on, l->left->type); |
| on->sym = lookup("!tmpselect!"); |
| r->left = on; |
| nbod = list(n->ninit, nod(OAS, l->left, on)); |
| n->ninit = nil; |
| } |
| break; |
| |
| case OSEND: |
| case ORECV: |
| break; |
| } |
| |
| nbod = concat(nbod, n->nbody); |
| nbod = list(nbod, nod(OBREAK, N, N)); |
| n->nbody = nil; |
| |
| oc = selcase(n, var, &init); |
| if(oc != N) { |
| oc->nbody = nbod; |
| res = list(res, oc); |
| } |
| } |
| setlineno(sel); |
| |
| // selectgo(sel *byte); |
| on = syslook("selectgo", 0); |
| r = nod(OCALL, on, N); |
| r->list = list1(var); // sel-var |
| res = list(res, r); |
| |
| // newselect(size uint32) (sel *byte); |
| on = syslook("newselect", 0); |
| |
| r = nod(OXXX, N, N); |
| nodconst(r, types[TINT], count); // count |
| args = list1(r); |
| r = nod(OCALL, on, N); |
| r->list = args; |
| r = nod(OAS, var, r); |
| |
| sel->ninit = list1(r); |
| sel->nbody = res; |
| sel->left = N; |
| |
| // TODO(rsc): is ninit a walkstmtlist or walkexprlist? |
| walkstmtlist(sel->ninit); |
| walkstmtlist(sel->nbody); |
| //dump("sel", sel); |
| |
| sel->ninit = concat(sel->ninit, init); |
| lineno = lno; |
| } |
| |
| Type* |
| lookdot1(Sym *s, Type *t, Type *f) |
| { |
| Type *r; |
| |
| r = T; |
| for(; f!=T; f=f->down) { |
| if(f->sym != s) |
| continue; |
| if(r != T) { |
| yyerror("ambiguous DOT reference %T.%S", t, s); |
| break; |
| } |
| r = f; |
| } |
| return r; |
| } |
| |
| int |
| lookdot(Node *n, Type *t) |
| { |
| Type *f1, *f2, *tt, *rcvr; |
| Sym *s; |
| |
| s = n->right->sym; |
| |
| f1 = T; |
| if(t->etype == TSTRUCT || t->etype == TINTER) |
| f1 = lookdot1(s, t, t->type); |
| |
| f2 = methtype(n->left->type); |
| if(f2 != T) |
| f2 = lookdot1(s, f2, f2->method); |
| |
| if(f1 != T) { |
| if(f2 != T) |
| yyerror("ambiguous DOT reference %S as both field and method", |
| n->right->sym); |
| n->xoffset = f1->width; |
| n->type = f1->type; |
| if(t->etype == TINTER) { |
| if(isptr[n->left->type->etype]) { |
| n->left = nod(OIND, n->left, N); // implicitstar |
| walkexpr(n->left, Elv, nil); |
| } |
| n->op = ODOTINTER; |
| } |
| return 1; |
| } |
| |
| if(f2 != T) { |
| tt = n->left->type; |
| rcvr = getthisx(f2->type)->type->type; |
| if(!eqtype(rcvr, tt)) { |
| if(rcvr->etype == tptr && eqtype(rcvr->type, tt)) { |
| walkexpr(n->left, Elv, nil); |
| addrescapes(n->left); |
| n->left = nod(OADDR, n->left, N); |
| n->left->type = ptrto(tt); |
| } else if(tt->etype == tptr && eqtype(tt->type, rcvr)) { |
| n->left = nod(OIND, n->left, N); |
| n->left->type = tt->type; |
| } else { |
| // method is attached to wrong type? |
| fatal("method mismatch: %T for %T", rcvr, tt); |
| } |
| } |
| n->right = methodname(n->right, n->left->type); |
| n->xoffset = f2->width; |
| n->type = f2->type; |
| n->op = ODOTMETH; |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| void |
| walkdot(Node *n, NodeList **init) |
| { |
| Type *t; |
| |
| walkexprlist(n->ninit, Etop, init); |
| if(n->ninit != nil) { |
| *init = concat(*init, n->ninit); |
| n->ninit = nil; |
| } |
| |
| if(n->left == N || n->right == N) |
| return; |
| switch(n->op) { |
| case ODOTINTER: |
| case ODOTMETH: |
| return; // already done |
| } |
| |
| walkexpr(n->left, Erv, init); |
| if(n->right->op != ONAME) { |
| yyerror("rhs of . must be a name"); |
| return; |
| } |
| |
| t = n->left->type; |
| if(t == T) |
| return; |
| |
| // as a structure field or pointer to structure field |
| if(isptr[t->etype]) { |
| t = t->type; |
| if(t == T) |
| return; |
| n->op = ODOTPTR; |
| } |
| |
| if(!lookdot(n, t)) { |
| if(!n->diag) { |
| n->diag = 1; |
| yyerror("undefined: %T field %S", n->left->type, n->right->sym); |
| } |
| } |
| } |
| |
| Node* |
| ascompatee1(int op, Node *l, Node *r, NodeList **init) |
| { |
| Node *a; |
| |
| /* |
| * check assign expression to |
| * a expression. called in |
| * expr = expr |
| */ |
| convlit(&r, l->type); |
| if(!ascompat(l->type, r->type)) { |
| badtype(op, l->type, r->type); |
| return nil; |
| } |
| if(l->op == ONAME && l->class == PFUNC) |
| yyerror("cannot assign to function"); |
| |
| a = nod(OAS, l, r); |
| a = convas(a, init); |
| return a; |
| } |
| |
| NodeList* |
| ascompatee(int op, NodeList *nl, NodeList *nr, NodeList **init) |
| { |
| NodeList *ll, *lr, *nn; |
| |
| /* |
| * check assign expression list to |
| * a expression list. called in |
| * expr-list = expr-list |
| */ |
| nn = nil; |
| for(ll=nl, lr=nr; ll && lr; ll=ll->next, lr=lr->next) |
| nn = list(nn, ascompatee1(op, ll->n, lr->n, init)); |
| |
| // cannot happen: caller checked that lists had same length |
| if(ll || lr) |
| yyerror("error in shape across %O", op); |
| return nn; |
| } |
| |
| /* |
| * n is an lv and t 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(!ascompat(l->type, r->type)) { |
| badtype(op, l->type, r->type); |
| return nil; |
| } |
| |
| // any lv that causes a fn call must be |
| // deferred until all the return arguments |
| // have been pulled from the output arguments |
| if(fncall(l, r->type)) { |
| tmp = nod(OXXX, N, N); |
| tempname(tmp, r->type); |
| a = nod(OAS, l, tmp); |
| a = convas(a, init); |
| mm = list(mm, a); |
| l = tmp; |
| } |
| |
| a = nod(OAS, l, nodarg(r, fp)); |
| a = convas(a, init); |
| ullmancalc(a); |
| if(a->ullman >= UINF) |
| ucount++; |
| nn = list(nn, a); |
| r = structnext(&saver); |
| } |
| |
| if(ll != nil || r != T) |
| yyerror("assignment count mismatch: %d = %d", |
| count(nl), structcount(*nr)); |
| if(ucount) |
| yyerror("reorder2: too many function calls evaluating parameters"); |
| return concat(nn, mm); |
| } |
| |
| /* |
| * make a tsig for the structure |
| * carrying the ... arguments |
| */ |
| Type* |
| sigtype(Type *st) |
| { |
| Sym *s; |
| Type *t; |
| static int sigdddgen; |
| |
| dowidth(st); |
| |
| sigdddgen++; |
| snprint(namebuf, sizeof(namebuf), "dsigddd_%d", sigdddgen); |
| s = lookup(namebuf); |
| t = newtype(s); |
| t = dodcltype(t); |
| updatetype(t, st); |
| t->local = 1; |
| return t; |
| } |
| |
| /* |
| * package all the arguments that |
| * match a ... parameter into an |
| * automatic structure. |
| * then call the ... arg (interface) |
| * with a pointer to the structure. |
| */ |
| NodeList* |
| mkdotargs(NodeList *lr0, NodeList *nn, Type *l, int fp, NodeList **init) |
| { |
| Node *r; |
| Type *t, *st, *ft; |
| Node *a, *var; |
| NodeList *lr, *n; |
| |
| n = nil; // list of assignments |
| |
| st = typ(TSTRUCT); // generated structure |
| ft = T; // last field |
| for(lr=lr0; lr; lr=lr->next) { |
| r = lr->n; |
| if(r->op == OLITERAL && r->val.ctype == CTNIL) { |
| if(r->type == T || r->type->etype == TNIL) { |
| yyerror("inappropriate use of nil in ... argument"); |
| return nil; |
| } |
| } |
| defaultlit(&r, T); |
| lr->n = r; |
| if(r->type == T) // type check failed |
| return nil; |
| |
| // generate the next structure field |
| t = typ(TFIELD); |
| t->type = r->type; |
| if(ft == T) |
| st->type = t; |
| else |
| ft->down = t; |
| ft = t; |
| |
| a = nod(OAS, N, r); |
| n = list(n, a); |
| } |
| |
| // make a named type for the struct |
| st = sigtype(st); |
| dowidth(st); |
| |
| // now we have the size, make the struct |
| var = nod(OXXX, N, N); |
| tempname(var, st); |
| var->sym = lookup(".ddd"); |
| |
| // assign the fields to the struct. |
| // use the init list so that reorder1 doesn't reorder |
| // these assignments after the interface conversion |
| // below. |
| t = st->type; |
| for(lr=n; lr; lr=lr->next) { |
| r = lr->n; |
| r->left = nod(OXXX, N, N); |
| *r->left = *var; |
| r->left->type = r->right->type; |
| r->left->xoffset += t->width; |
| walkexpr(r, Etop, init); |
| t = t->down; |
| } |
| *init = concat(*init, n); |
| |
| // last thing is to put assignment |
| // of the structure to the DDD parameter |
| a = nod(OAS, nodarg(l, fp), var); |
| nn = list(nn, convas(a, init)); |
| return nn; |
| } |
| |
| /* |
| * helpers for shape errors |
| */ |
| static void |
| dumptypes(Type **nl, char *what) |
| { |
| int first; |
| Type *l; |
| Iter savel; |
| |
| l = structfirst(&savel, nl); |
| print("\t"); |
| first = 1; |
| for(l = structfirst(&savel, nl); l != T; l = structnext(&savel)) { |
| if(first) |
| first = 0; |
| else |
| print(", "); |
| print("%T", l); |
| } |
| if(first) |
| print("[no arguments %s]", what); |
| print("\n"); |
| } |
| |
| static void |
| dumpnodetypes(NodeList *l, char *what) |
| { |
| int first; |
| Node *r; |
| |
| print("\t"); |
| first = 1; |
| for(; l; l=l->next) { |
| r = l->n; |
| if(first) |
| first = 0; |
| else |
| print(", "); |
| print("%T", r->type); |
| } |
| if(first) |
| print("[no arguments %s]", what); |
| print("\n"); |
| } |
| |
| /* |
| * check assign expression list to |
| * a type list. called in |
| * return expr-list |
| * func(expr-list) |
| */ |
| NodeList* |
| ascompatte(int op, Type **nl, NodeList *lr, int fp, NodeList **init) |
| { |
| Type *l, *ll; |
| Node *r, *a; |
| NodeList *nn, *lr0; |
| 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 |
| && eqtypenoname(r->type, *nl)) { |
| // clumsy check for differently aligned structs. |
| // now that output structs are aligned separately |
| // from the input structs, should never happen. |
| if(r->type->width != (*nl)->width) |
| fatal("misaligned multiple return\n\t%T\n\t%T", r->type, *nl); |
| a = nodarg(*nl, fp); |
| a->type = r->type; |
| return list1(convas(nod(OAS, a, r), init)); |
| } |
| |
| loop: |
| if(l != T && isddd(l->type)) { |
| // the ddd parameter must be last |
| ll = structnext(&savel); |
| if(ll != T) |
| yyerror("... must be last argument"); |
| |
| // special case -- |
| // only if we are assigning a single ddd |
| // argument to a ddd parameter then it is |
| // passed thru unencapsulated |
| if(r != N && lr->next == nil && isddd(r->type)) { |
| a = nod(OAS, nodarg(l, fp), r); |
| a = convas(a, init); |
| nn = list(nn, a); |
| return nn; |
| } |
| |
| // normal case -- make a structure of all |
| // remaining arguments and pass a pointer to |
| // it to the ddd parameter (empty interface) |
| return mkdotargs(lr, nn, l, fp, init); |
| } |
| |
| if(l == T || r == N) { |
| if(l != T || r != N) { |
| if(l != T) |
| yyerror("not enough arguments to %O", op); |
| else |
| yyerror("too many arguments to %O", op); |
| dumptypes(nl, "expected"); |
| dumpnodetypes(lr0, "given"); |
| } |
| return nn; |
| } |
| convlit(&r, l->type); |
| if(!ascompat(l->type, r->type)) { |
| badtype(op, l->type, r->type); |
| return nil; |
| } |
| |
| 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; |
| } |
| |
| /* |
| * do the export rules allow writing to this type? |
| * cannot be implicitly assigning to any type with |
| * an unavailable field. |
| */ |
| int |
| exportasok(Type *t) |
| { |
| Type *f; |
| Sym *s; |
| |
| if(t == T) |
| return 1; |
| switch(t->etype) { |
| default: |
| // most types can't contain others; they're all fine. |
| break; |
| case TSTRUCT: |
| for(f=t->type; f; f=f->down) { |
| if(f->etype != TFIELD) |
| fatal("structas: not field"); |
| s = f->sym; |
| // s == nil doesn't happen for embedded fields (they get the type symbol). |
| // it only happens for fields in a ... struct. |
| if(s != nil && !exportname(s->name) && strcmp(package, s->package) != 0) { |
| yyerror("implicit assignment of %T field '%s'", t, s->name); |
| return 0; |
| } |
| if(!exportasok(f->type)) |
| return 0; |
| } |
| break; |
| |
| case TARRAY: |
| if(t->bound < 0) // slices are pointers; that's fine |
| break; |
| if(!exportasok(t->type)) |
| return 0; |
| break; |
| } |
| return 1; |
| } |
| |
| /* |
| * can we assign var of type src to var of type dst? |
| * return 0 if not, 1 if conversion is trivial, 2 if conversion is non-trivial. |
| */ |
| int |
| ascompat(Type *dst, Type *src) |
| { |
| if(eqtype(dst, src)) { |
| exportasok(src); |
| return 1; |
| } |
| |
| if(dst == T || src == T) |
| return 0; |
| |
| if(dst->etype == TFORWINTER || dst->etype == TFORWSTRUCT || dst->etype == TFORW) |
| return 0; |
| if(src->etype == TFORWINTER || src->etype == TFORWSTRUCT || src->etype == TFORW) |
| return 0; |
| |
| // interfaces go through even if names don't match |
| if(isnilinter(dst) || isnilinter(src)) |
| return 2; |
| |
| if(isinter(dst) && isinter(src)) |
| return 2; |
| |
| if(isinter(dst) && methtype(src)) |
| return 2; |
| |
| if(isinter(src) && methtype(dst)) |
| return 2; |
| |
| // otherwise, if concrete types have names, they must match |
| if(dst->sym && src->sym && dst != src) |
| return 0; |
| |
| if(dst->etype == TCHAN && src->etype == TCHAN) { |
| if(!eqtype(dst->type, src->type)) |
| return 0; |
| if(dst->chan & ~src->chan) |
| return 0; |
| return 1; |
| } |
| |
| if(isslice(dst) |
| && isptr[src->etype] |
| && isfixedarray(src->type) |
| && eqtype(dst->type, src->type->type)) |
| return 2; |
| |
| return 0; |
| } |
| |
| // generate code for print |
| // fmt = 0: print |
| // fmt = 1: println |
| Node* |
| prcompat(NodeList *all, int fmt, int dopanic) |
| { |
| Node *r; |
| Node *n; |
| NodeList *l; |
| Node *on; |
| Type *t; |
| int notfirst, et; |
| NodeList *calls; |
| |
| calls = nil; |
| notfirst = 0; |
| |
| for(l=all; l; l=l->next) { |
| if(notfirst) { |
| on = syslook("printsp", 0); |
| calls = list(calls, nod(OCALL, on, N)); |
| } |
| notfirst = fmt; |
| |
| n = l->n; |
| walkexpr(n, Erv, nil); |
| if(n->op == OLITERAL) { |
| switch(n->val.ctype) { |
| case CTINT: |
| defaultlit(&n, types[TINT64]); |
| break; |
| case CTFLT: |
| defaultlit(&n, types[TFLOAT64]); |
| break; |
| } |
| } |
| defaultlit(&n, nil); |
| l->n = n; |
| if(n->type == T) |
| continue; |
| |
| et = n->type->etype; |
| if(isinter(n->type)) { |
| if(isnilinter(n->type)) |
| on = syslook("printeface", 1); |
| else |
| on = syslook("printiface", 1); |
| argtype(on, n->type); // any-1 |
| } else if(isptr[et] || et == TCHAN || et == TMAP || et == TFUNC) { |
| on = syslook("printpointer", 1); |
| argtype(on, n->type); // any-1 |
| } else if(isslice(n->type)) { |
| on = syslook("printarray", 1); |
| argtype(on, n->type); // any-1 |
| } else if(isint[et]) { |
| if(et == TUINT64) |
| on = syslook("printuint", 0); |
| else |
| on = syslook("printint", 0); |
| } else if(isfloat[et]) { |
| on = syslook("printfloat", 0); |
| } else if(et == TBOOL) { |
| on = syslook("printbool", 0); |
| } else if(et == TSTRING) { |
| on = syslook("printstring", 0); |
| } else { |
| badtype(OPRINT, n->type, T); |
| continue; |
| } |
| |
| t = *getinarg(on->type); |
| if(t != nil) |
| t = t->type; |
| if(t != nil) |
| t = t->type; |
| |
| if(!eqtype(t, n->type)) { |
| n = nod(OCONV, n, N); |
| n->type = t; |
| } |
| r = nod(OCALL, on, N); |
| r->list = list1(n); |
| calls = list(calls, r); |
| } |
| |
| if(fmt == 1 && !dopanic) { |
| on = syslook("printnl", 0); |
| calls = list(calls, nod(OCALL, on, N)); |
| } |
| walkexprlist(calls, Etop, nil); |
| |
| if(dopanic) |
| r = nodpanic(0); |
| else |
| r = nod(OEMPTY, N, N); |
| walkexpr(r, Etop, nil); |
| r->ninit = calls; |
| return r; |
| } |
| |
| Node* |
| nodpanic(int32 lineno) |
| { |
| Node *n, *on; |
| NodeList *args; |
| |
| on = syslook("panicl", 0); |
| n = nodintconst(lineno); |
| args = list1(n); |
| n = nod(OCALL, on, N); |
| n->list = args; |
| walkexpr(n, Etop, nil); |
| return n; |
| } |
| |
| Node* |
| makecompat(Node *n) |
| { |
| Type *t; |
| Node *l, *r; |
| NodeList *args, *init; |
| |
| args = n->list; |
| if(args == nil) { |
| yyerror("make requires type argument"); |
| return n; |
| } |
| l = args->n; |
| r = N; |
| args = args->next; |
| init = nil; |
| walkexpr(l, Etype, &init); |
| if(l->op != OTYPE) { |
| yyerror("cannot make(expr)"); |
| return n; |
| } |
| t = l->type; |
| n->type = t; |
| n->list = args; |
| |
| if(t != T) |
| switch(t->etype) { |
| case TARRAY: |
| if(!isslice(t)) |
| goto bad; |
| return arrayop(n, Erv); |
| case TMAP: |
| return mapop(n, Erv, nil); |
| case TCHAN: |
| return chanop(n, Erv, nil); |
| } |
| |
| bad: |
| if(!n->diag) { |
| n->diag = 1; |
| yyerror("cannot make(%T)", t); |
| } |
| return n; |
| } |
| |
| Node* |
| callnew(Type *t) |
| { |
| Node *r, *on; |
| NodeList *args; |
| |
| dowidth(t); |
| on = syslook("mal", 1); |
| argtype(on, t); |
| r = nodintconst(t->width); |
| args = list1(r); |
| r = nod(OCALL, on, N); |
| r->list = args; |
| walkexpr(r, Erv, nil); |
| return r; |
| } |
| |
| Node* |
| stringop(Node *n, int top, NodeList **init) |
| { |
| Node *r, *c, *on; |
| NodeList *args; |
| |
| switch(n->op) { |
| default: |
| fatal("stringop: unknown op %O", n->op); |
| |
| case OEQ: |
| case ONE: |
| case OGE: |
| case OGT: |
| case OLE: |
| case OLT: |
| // sys_cmpstring(s1, s2) :: 0 |
| on = syslook("cmpstring", 0); |
| r = nod(OCONV, n->left, N); |
| r->type = types[TSTRING]; |
| args = list1(r); |
| c = nod(OCONV, n->right, N); |
| c->type = types[TSTRING]; |
| args = list(args, c); |
| r = nod(OCALL, on, N); |
| r->list = args; |
| c = nodintconst(0); |
| r = nod(n->op, r, c); |
| break; |
| |
| case OADD: |
| // sys_catstring(s1, s2) |
| on = syslook("catstring", 0); |
| r = nod(OCONV, n->left, N); |
| r->type = types[TSTRING]; |
| args = list1(r); |
| c = nod(OCONV, n->right, N); |
| c->type = types[TSTRING]; |
| args = list(args, c); |
| r = nod(OCALL, on, N); |
| r->list = args; |
| break; |
| |
| case OASOP: |
| // sys_catstring(s1, s2) |
| switch(n->etype) { |
| default: |
| fatal("stringop: unknown op %O-%O", n->op, n->etype); |
| |
| case OADD: |
| // s1 = sys_catstring(s1, s2) |
| if(n->etype != OADD) |
| fatal("stringop: not cat"); |
| on = syslook("catstring", 0); |
| r = nod(OCONV, n->left, N); |
| r->type = types[TSTRING]; |
| args = list1(r); |
| c = nod(OCONV, n->right, N); |
| c->type = types[TSTRING]; |
| args = list(args, c); |
| r = nod(OCALL, on, N); |
| r->list = args; |
| r = nod(OAS, n->left, r); |
| break; |
| } |
| break; |
| |
| case OSLICE: |
| r = nod(OCONV, n->left, N); |
| r->type = types[TSTRING]; |
| args = list1(r); |
| |
| // sys_slicestring(s, lb, hb) |
| r = nod(OCONV, n->right->left, N); |
| r->type = types[TINT]; |
| args = list(args, r); |
| |
| c = nod(OCONV, n->right->right, N); |
| c->type = types[TINT]; |
| args = list(args, c); |
| |
| on = syslook("slicestring", 0); |
| r = nod(OCALL, on, N); |
| r->list = args; |
| break; |
| |
| case OINDEX: |
| // sys_indexstring(s, i) |
| r = nod(OCONV, n->left, N); |
| r->type = types[TSTRING]; |
| args = list1(r); |
| |
| r = nod(OCONV, n->right, N); |
| r->type = types[TINT]; |
| args = list(args, r); |
| on = syslook("indexstring", 0); |
| r = nod(OCALL, on, N); |
| r->list = args; |
| break; |
| |
| case OCONV: |
| // sys_intstring(v) |
| r = nod(OCONV, n->left, N); |
| r->type = types[TINT64]; |
| args = list1(r); |
| on = syslook("intstring", 0); |
| r = nod(OCALL, on, N); |
| r->list = args; |
| break; |
| |
| case OARRAY: |
| // arraystring([]byte) string; |
| on = syslook("arraystring", 0); |
| r = n->left; |
| |
| if(r->type != T && r->type->type != T) { |
| if(istype(r->type->type, TINT) || istype(r->type->type->type, TINT)) { |
| // arraystring([]byte) string; |
| on = syslook("arraystringi", 0); |
| } |
| } |
| args = list1(r); |
| r = nod(OCALL, on, N); |
| r->list = args; |
| break; |
| } |
| |
| walkexpr(r, top, init); |
| return r; |
| } |
| |
| Type* |
| fixmap(Type *t) |
| { |
| if(t == T) |
| goto bad; |
| if(t->etype != TMAP) |
| goto bad; |
| if(t->down == T || t->type == T) |
| goto bad; |
| |
| dowidth(t->down); |
| dowidth(t->type); |
| |
| return t; |
| |
| bad: |
| yyerror("not a map: %lT", t); |
| return T; |
| } |
| |
| Type* |
| fixchan(Type *t) |
| { |
| if(t == T) |
| goto bad; |
| if(t->etype != TCHAN) |
| goto bad; |
| if(t->type == T) |
| goto bad; |
| |
| dowidth(t->type); |
| |
| return t; |
| |
| bad: |
| yyerror("not a channel: %lT", t); |
| return T; |
| } |
| |
| Node* |
| mapop(Node *n, int top, NodeList **init) |
| { |
| Node *r, *a, *l; |
| Type *t; |
| Node *on; |
| int cl, cr; |
| NodeList *args; |
| |
| r = n; |
| switch(n->op) { |
| default: |
| fatal("mapop: unknown op %O", n->op); |
| |
| case OMAKE: |
| cl = count(n->list); |
| if(cl > 1) |
| yyerror("too many arguments to make map"); |
| |
| if(top != Erv) |
| goto nottop; |
| |
| // newmap(keysize int, valsize int, |
| // keyalg int, valalg int, |
| // hint int) (hmap map[any-1]any-2); |
| |
| t = fixmap(n->type); |
| if(t == T) |
| break; |
| |
| a = nodintconst(t->down->width); // key width |
| args = list1(a); |
| a = nodintconst(t->type->width); // val width |
| args = list(args, a); |
| a = nodintconst(algtype(t->down)); // key algorithm |
| args = list(args, a); |
| a = nodintconst(algtype(t->type)); // val algorithm |
| args = list(args, a); |
| |
| if(cl == 1) |
| a = n->list->n; // hint |
| else |
| a = nodintconst(0); |
| args = list(args, a); |
| |
| on = syslook("newmap", 1); |
| |
| argtype(on, t->down); // any-1 |
| argtype(on, t->type); // any-2 |
| |
| r = nod(OCALL, on, N); |
| r->list = args; |
| walkexpr(r, top, nil); |
| r->type = n->type; |
| break; |
| |
| case OINDEX: |
| if(top != Erv) |
| goto nottop; |
| // mapaccess1(hmap map[any]any, key any) (val any); |
| |
| t = fixmap(n->left->type); |
| if(t == T) |
| break; |
| |
| convlit(&n->right, t->down); |
| |
| if(!eqtype(n->right->type, t->down)) { |
| badtype(n->op, n->right->type, t->down); |
| break; |
| } |
| |
| a = n->left; // map |
| args = list1(a); |
| a = n->right; // key |
| args = list(args, a); |
| |
| on = syslook("mapaccess1", 1); |
| |
| argtype(on, t->down); // any-1 |
| argtype(on, t->type); // any-2 |
| argtype(on, t->down); // any-3 |
| argtype(on, t->type); // any-4 |
| |
| r = nod(OCALL, on, N); |
| r->list = args; |
| walkexpr(r, Erv, nil); |
| r->type = t->type; |
| break; |
| |
| case OAS: |
| // mapassign1(hmap map[any-1]any-2, key any-3, val any-4); |
| if(n->left->op != OINDEX) |
| goto shape; |
| |
| t = fixmap(n->left->left->type); |
| if(t == T) |
| break; |
| |
| a = n->left->left; // map |
| args = list1(a); |
| a = n->left->right; // key |
| args = list(args, a); |
| a = n->right; // val |
| args = list(args, a); |
| |
| on = syslook("mapassign1", 1); |
| |
| argtype(on, t->down); // any-1 |
| argtype(on, t->type); // any-2 |
| argtype(on, t->down); // any-3 |
| argtype(on, t->type); // any-4 |
| |
| r = nod(OCALL, on, N); |
| r->list = args; |
| walkexpr(r, Etop, init); |
| break; |
| |
| case OAS2: |
| cl = count(n->list); |
| cr = count(n->rlist); |
| |
| if(cl == 1 && cr == 2) |
| goto assign2; |
| if(cl == 2 && cr == 1) |
| goto access2; |
| goto shape; |
| |
| assign2: |
| // mapassign2(hmap map[any]any, key any, val any, pres bool); |
| l = n->list->n; |
| if(l->op != OINDEX) |
| goto shape; |
| |
| t = fixmap(l->left->type); |
| if(t == T) |
| break; |
| |
| args = list1(l->left); // map |
| args = list(args, l->right); // key |
| args = list(args, n->rlist->n); // val |
| args = list(args, n->rlist->next->n); // pres |
| |
| on = syslook("mapassign2", 1); |
| |
| argtype(on, t->down); // any-1 |
| argtype(on, t->type); // any-2 |
| argtype(on, t->down); // any-3 |
| argtype(on, t->type); // any-4 |
| |
| r = nod(OCALL, on, N); |
| r->list = args; |
| walkexpr(r, Etop, init); |
| break; |
| |
| access2: |
| // mapaccess2(hmap map[any-1]any-2, key any-3) (val-4 any, pres bool); |
| |
| //dump("access2", n); |
| r = n->rlist->n; |
| if(r->op != OINDEX) |
| goto shape; |
| |
| t = fixmap(r->left->type); |
| if(t == T) |
| break; |
| |
| args = list1(r->left); // map |
| args = list(args, r->right); // key |
| |
| on = syslook("mapaccess2", 1); |
| |
| argtype(on, t->down); // any-1 |
| argtype(on, t->type); // any-2 |
| argtype(on, t->down); // any-3 |
| argtype(on, t->type); // any-4 |
| |
| a = nod(OCALL, on, N); |
| a->list = args; |
| n->rlist = list1(a); |
| walkexpr(n, Etop, init); |
| r = n; |
| break; |
| |
| case OASOP: |
| // rewrite map[index] op= right |
| // into tmpi := index; map[tmpi] = map[tmpi] op right |
| // TODO(rsc): does this double-evaluate map? |
| |
| t = n->left->left->type; |
| a = nod(OXXX, N, N); |
| tempname(a, t->down); // tmpi |
| r = nod(OAS, a, n->left->right); // tmpi := index |
| n->left->right = a; // m[tmpi] |
| walkexpr(r, Etop, init); |
| *init = list(*init, r); |
| |
| a = nod(OXXX, N, N); |
| indir(a, n->left); // copy of map[tmpi] |
| a = nod(n->etype, a, n->right); // m[tmpi] op right |
| r = nod(OAS, n->left, a); // map[tmpi] = map[tmpi] op right |
| walkexpr(r, Etop, init); |
| break; |
| } |
| return r; |
| |
| shape: |
| dump("shape", n); |
| fatal("mapop: cl=%d cr=%d, %O", top, n->op); |
| return N; |
| |
| nottop: |
| yyerror("didn't expect %O here", n->op); |
| return N; |
| } |
| |
| Node* |
| chanop(Node *n, int top, NodeList **init) |
| { |
| Node *r, *a, *on; |
| NodeList *args; |
| Type *t; |
| int cl, cr; |
| |
| r = n; |
| switch(n->op) { |
| default: |
| fatal("chanop: unknown op %O", n->op); |
| |
| case OCLOSE: |
| cl = count(n->list); |
| if(cl > 1) |
| yyerror("too many arguments to close"); |
| else if(cl < 1) |
| yyerror("missing argument to close"); |
| n->left = n->list->n; |
| |
| // closechan(hchan *chan any); |
| t = fixchan(n->left->type); |
| if(t == T) |
| break; |
| |
| a = n->left; // chan |
| args = list1(a); |
| |
| on = syslook("closechan", 1); |
| argtype(on, t); // any-1 |
| |
| r = nod(OCALL, on, N); |
| r->list = args; |
| walkexpr(r, top, nil); |
| r->type = n->type; |
| break; |
| |
| case OCLOSED: |
| cl = count(n->list); |
| if(cl > 1) |
| yyerror("too many arguments to closed"); |
| else if(cl < 1) |
| yyerror("missing argument to closed"); |
| n->left = n->list->n; |
| |
| // closedchan(hchan *chan any) bool; |
| t = fixchan(n->left->type); |
| if(t == T) |
| break; |
| |
| a = n->left; // chan |
| args = list1(a); |
| |
| on = syslook("closedchan", 1); |
| argtype(on, t); // any-1 |
| |
| r = nod(OCALL, on, N); |
| r->list = args; |
| walkexpr(r, top, nil); |
| n->type = r->type; |
| break; |
| |
| case OMAKE: |
| cl = count(n->list); |
| if(cl > 1) |
| yyerror("too many arguments to make chan"); |
| |
| // newchan(elemsize int, elemalg int, |
| // hint int) (hmap *chan[any-1]); |
| |
| t = fixchan(n->type); |
| if(t == T) |
| break; |
| |
| a = nodintconst(t->type->width); // elem width |
| args = list1(a); |
| a = nodintconst(algtype(t->type)); // elem algorithm |
| args = list(args, a); |
| a = nodintconst(0); |
| if(cl == 1) { |
| // async buf size |
| a = nod(OCONV, n->list->n, N); |
| a->type = types[TINT]; |
| } |
| args = list(args, a); |
| |
| on = syslook("newchan", 1); |
| argtype(on, t->type); // any-1 |
| |
| r = nod(OCALL, on, N); |
| r->list = args; |
| walkexpr(r, top, nil); |
| r->type = n->type; |
| break; |
| |
| case OAS2: |
| cl = count(n->list); |
| cr = count(n->rlist); |
| |
| if(cl != 2 || cr != 1 || n->rlist->n->op != ORECV) |
| goto shape; |
| |
| // chanrecv2(hchan *chan any) (elem any, pres bool); |
| r = n->rlist->n; |
| defaultlit(&r->left, T); |
| t = fixchan(r->left->type); |
| if(t == T) |
| break; |
| |
| if(!(t->chan & Crecv)) { |
| yyerror("cannot receive from %T", t); |
| break; |
| } |
| |
| a = r->left; // chan |
| args = list1(a); |
| |
| on = syslook("chanrecv2", 1); |
| |
| argtype(on, t->type); // any-1 |
| argtype(on, t->type); // any-2 |
| r = nod(OCALL, on, N); |
| r->list = args; |
| n->rlist->n = r; |
| r = n; |
| walkexpr(r, Etop, init); |
| break; |
| |
| case ORECV: |
| // should not happen - nonblocking is OAS w/ ORECV now. |
| if(n->right != N) { |
| dump("recv2", n); |
| fatal("chanop recv2"); |
| } |
| |
| // chanrecv1(hchan *chan any) (elem any); |
| defaultlit(&n->left, T); |
| t = fixchan(n->left->type); |
| if(t == T) |
| break; |
| |
| if(!(t->chan & Crecv)) { |
| yyerror("cannot receive from %T", t); |
| break; |
| } |
| |
| a = n->left; // chan |
| args = list1(a); |
| |
| on = syslook("chanrecv1", 1); |
| |
| argtype(on, t->type); // any-1 |
| argtype(on, t->type); // any-2 |
| r = nod(OCALL, on, N); |
| r->list = args; |
| walkexpr(r, Erv, nil); |
| break; |
| |
| case OSEND: |
| t = fixchan(n->left->type); |
| if(t == T) |
| break; |
| if(!(t->chan & Csend)) { |
| yyerror("cannot send to %T", t); |
| break; |
| } |
| |
| if(top != Etop) |
| goto send2; |
| |
| // chansend1(hchan *chan any, elem any); |
| a = n->left; // chan |
| args = list1(a); |
| a = n->right; // e |
| args = list(args, a); |
| |
| on = syslook("chansend1", 1); |
| argtype(on, t->type); // any-1 |
| argtype(on, t->type); // any-2 |
| r = nod(OCALL, on, N); |
| r->list = args; |
| walkexpr(r, Etop, nil); |
| break; |
| |
| send2: |
| // chansend2(hchan *chan any, val any) (pres bool); |
| a = n->left; // chan |
| args = list1(a); |
| a = n->right; // e |
| args = list(args, a); |
| |
| on = syslook("chansend2", 1); |
| argtype(on, t->type); // any-1 |
| argtype(on, t->type); // any-2 |
| r = nod(OCALL, on, N); |
| r->list = args; |
| walkexpr(r, Etop, nil); |
| break; |
| } |
| return r; |
| |
| shape: |
| fatal("chanop: %O", n->op); |
| return N; |
| } |
| |
| Type* |
| fixarray(Type *t) |
| { |
| |
| if(t == T) |
| goto bad; |
| if(t->etype != TARRAY) |
| goto bad; |
| if(t->type == T) |
| goto bad; |
| dowidth(t); |
| return t; |
| |
| bad: |
| yyerror("not an array: %lT", t); |
| return T; |
| |
| } |
| |
| Node* |
| arrayop(Node *n, int top) |
| { |
| Node *r, *a; |
| NodeList *args; |
| Type *t, *tl; |
| Node *on; |
| int cl; |
| |
| r = n; |
| switch(n->op) { |
| default: |
| fatal("darrayop: unknown op %O", n->op); |
| |
| case OCONV: |
| // arrays2d(old *any, nel int) (ary []any) |
| if(n->left->type == T || !isptr[n->left->type->etype]) |
| break; |
| t = fixarray(n->left->type->type); |
| tl = fixarray(n->type); |
| if(t == T || tl == T) |
| break; |
| |
| args = list1(n->left); // old |
| |
| a = nodintconst(t->bound); // nel |
| a = nod(OCONV, a, N); |
| a->type = types[TINT]; |
| args = list(args, a); |
| |
| on = syslook("arrays2d", 1); |
| argtype(on, t); // any-1 |
| argtype(on, tl->type); // any-2 |
| r = nod(OCALL, on, N); |
| r->list = args; |
| n->left = r; |
| walkexpr(n, top, nil); |
| return n; |
| |
| case OAS: |
| r = nod(OCONV, n->right, N); |
| r->type = n->left->type; |
| n->right = arrayop(r, Erv); |
| return n; |
| |
| case OMAKE: |
| cl = count(n->list); |
| if(cl > 2) |
| yyerror("too many arguments to make array"); |
| |
| // newarray(nel int, max int, width int) (ary []any) |
| t = fixarray(n->type); |
| if(t == T) |
| break; |
| |
| // nel |
| a = n->list->n; |
| if(a == N) { |
| yyerror("new slice must have size"); |
| a = nodintconst(1); |
| } |
| a = nod(OCONV, a, N); |
| a->type = types[TINT]; |
| args = list1(a); |
| |
| // max |
| if(cl < 2) |
| a = nodintconst(0); |
| else |
| a = n->list->next->n; |
| a = nod(OCONV, a, N); |
| a->type = types[TINT]; |
| args = list(args, a); |
| |
| // width |
| a = nodintconst(t->type->width); // width |
| a = nod(OCONV, a, N); |
| a->type = types[TINT]; |
| args = list(args, a); |
| |
| on = syslook("newarray", 1); |
| argtype(on, t->type); // any-1 |
| r = nod(OCALL, on, N); |
| r->list = args; |
| walkexpr(r, top, nil); |
| r->type = t; // if t had a name, going through newarray lost it |
| break; |
| |
| case OSLICE: |
| // arrayslices(old any, nel int, lb int, hb int, width int) (ary []any) |
| // arraysliced(old []any, lb int, hb int, width int) (ary []any) |
| |
| t = fixarray(n->left->type); |
| if(t == T) |
| break; |
| |
| if(t->bound >= 0) { |
| // static slice |
| a = nod(OADDR, n->left, N); // old |
| args = list1(a); |
| |
| a = nodintconst(t->bound); // nel |
| a = nod(OCONV, a, N); |
| a->type = types[TINT]; |
| args = list(args, a); |
| |
| on = syslook("arrayslices", 1); |
| argtype(on, t); // any-1 |
| argtype(on, t->type); // any-2 |
| } else { |
| // dynamic slice |
| a = n->left; // old |
| args = list1(a); |
| |
| on = syslook("arraysliced", 1); |
| argtype(on, t->type); // any-1 |
| argtype(on, t->type); // any-2 |
| } |
| |
| a = nod(OCONV, n->right->left, N); // lb |
| a->type = types[TINT]; |
| args = list(args, a); |
| |
| a = nod(OCONV, n->right->right, N); // hb |
| a->type = types[TINT]; |
| args = list(args, a); |
| |
| a = nodintconst(t->type->width); // width |
| a = nod(OCONV, a, N); |
| a->type = types[TINT]; |
| args = list(args, a); |
| |
| r = nod(OCALL, on, N); |
| r->list = args; |
| walkexpr(r, top, nil); |
| break; |
| } |
| return r; |
| } |
| |
| /* |
| * assigning src to dst involving interfaces? |
| * return op to use. |
| */ |
| int |
| ifaceas1(Type *dst, Type *src, int explicit) |
| { |
| if(src == T || dst == T) |
| return Inone; |
| |
| if(explicit && !isinter(src)) |
| yyerror("cannot use .(T) on non-interface type %T", src); |
| |
| if(isinter(dst)) { |
| if(isinter(src)) { |
| if(isnilinter(dst)) { |
| if(isnilinter(src)) |
| return E2Esame; |
| return I2E; |
| } |
| if(eqtype(dst, src)) |
| return I2Isame; |
| ifacecheck(dst, src, lineno, explicit); |
| if(isnilinter(src)) |
| return E2I; |
| if(explicit) |
| return I2Ix; |
| return I2I; |
| } |
| if(isnilinter(dst)) |
| return T2E; |
| ifacecheck(dst, src, lineno, explicit); |
| return T2I; |
| } |
| if(isinter(src)) { |
| ifacecheck(dst, src, lineno, explicit); |
| if(isnilinter(src)) |
| return E2T; |
| return I2T; |
| } |
| return Inone; |
| } |
| |
| /* |
| * treat convert T to T as noop |
| */ |
| int |
| ifaceas(Type *dst, Type *src, int explicit) |
| { |
| int et; |
| |
| et = ifaceas1(dst, src, explicit); |
| if(et == I2Isame || et == E2Esame) |
| et = Inone; |
| return et; |
| } |
| |
| static char* |
| ifacename[] = |
| { |
| [I2T] = "ifaceI2T", |
| [I2T2] = "ifaceI2T2", |
| [I2I] = "ifaceI2I", |
| [I2Ix] = "ifaceI2Ix", |
| [I2I2] = "ifaceI2I2", |
| [I2Isame] = "ifaceI2Isame", |
| [E2T] = "ifaceE2T", |
| [E2T2] = "ifaceE2T2", |
| [E2I] = "ifaceE2I", |
| [E2I2] = "ifaceE2I2", |
| [I2E] = "ifaceI2E", |
| [I2E2] = "ifaceI2E2", |
| [T2I] = "ifaceT2I", |
| [T2E] = "ifaceT2E", |
| [E2Esame] = "ifaceE2Esame", |
| }; |
| |
| Node* |
| ifacecvt(Type *tl, Node *n, int et) |
| { |
| Type *tr; |
| Node *r, *on; |
| NodeList *args; |
| |
| tr = n->type; |
| |
| switch(et) { |
| default: |
| fatal("ifacecvt: unknown op %d\n", et); |
| |
| 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); |
| 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; |
| } |
| |
| r = nod(OCALL, on, N); |
| r->list = args; |
| walkexpr(r, Erv, nil); |
| return r; |
| } |
| |
| Node* |
| ifaceop(Node *n) |
| { |
| Node *r, *on; |
| NodeList *args; |
| |
| switch(n->op) { |
| default: |
| fatal("ifaceop %O", n->op); |
| |
| case OEQ: |
| case ONE: |
| // ifaceeq(i1 any-1, i2 any-2) (ret bool); |
| args = list1(n->left); // i1 |
| args = list(args, n->right); // i2 |
| |
| if(!eqtype(n->left->type, n->right->type)) |
| fatal("ifaceop %O %T %T", n->op, n->left->type, n->right->type); |
| if(isnilinter(n->left->type)) |
| on = syslook("efaceeq", 1); |
| else |
| on = syslook("ifaceeq", 1); |
| argtype(on, n->right->type); |
| argtype(on, n->left->type); |
| |
| r = nod(OCALL, on, N); |
| r->list = args; |
| if(n->op == ONE) |
| r = nod(ONOT, r, N); |
| walkexpr(r, Erv, nil); |
| 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); |
| |
| 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(n->left->op == OINDEX) |
| if(istype(n->left->left->type, TMAP)) { |
| indir(n, mapop(n, Elv, init)); |
| goto out; |
| } |
| |
| if(n->left->op == OSEND) |
| if(n->left->type != T) { |
| indir(n, chanop(n, Elv, init)); |
| goto out; |
| } |
| |
| if(eqtype(lt, rt)) |
| goto out; |
| |
| et = ifaceas(lt, rt, 0); |
| if(et != Inone) { |
| n->right = ifacecvt(lt, r, et); |
| goto out; |
| } |
| |
| if(isslice(lt) && isptr[rt->etype] && isfixedarray(rt->type)) { |
| if(!eqtype(lt->type->type, rt->type->type->type)) |
| goto bad; |
| indir(n, arrayop(n, Etop)); |
| goto out; |
| } |
| |
| if(ascompat(lt, rt)) |
| goto out; |
| |
| bad: |
| badtype(n->op, lt, rt); |
| |
| out: |
| ullmancalc(n); |
| return n; |
| } |
| |
| int |
| colasname(Node *n) |
| { |
| // TODO(rsc): can probably simplify |
| // once late-binding of names goes in |
| switch(n->op) { |
| case ONAME: |
| case ONONAME: |
| case OPACK: |
| break; |
| case OTYPE: |
| case OLITERAL: |
| if(n->sym != S) |
| break; |
| // fallthrough |
| default: |
| return 0; |
| } |
| return 1; |
| } |
| |
| Node* |
| old2new(Node *n, Type *t, NodeList **init) |
| { |
| Node *l; |
| |
| if(!colasname(n)) { |
| yyerror("left side of := must be a name"); |
| return n; |
| } |
| if(t != T && t->funarg) { |
| yyerror("use of multi func value as single value in :="); |
| return n; |
| } |
| l = newname(n->sym); |
| dodclvar(l, t, init); |
| return l; |
| } |
| |
| static Node* |
| mixedoldnew(Node *n, Type *t) |
| { |
| n = nod(OXXX, n, N); |
| n->type = t; |
| return n; |
| } |
| |
| static NodeList* |
| checkmixed(NodeList *nl, NodeList **init) |
| { |
| Node *a, *l; |
| NodeList *ll, *n; |
| Type *t; |
| int ntot, nred; |
| |
| // first pass, check if it is a special |
| // case of new and old declarations |
| |
| ntot = 0; // number assignments |
| nred = 0; // number redeclarations |
| for(ll=nl; ll; ll=ll->next) { |
| l = ll->n; |
| t = l->type; |
| l = l->left; |
| |
| if(!colasname(l)) |
| goto allnew; |
| if(l->sym->block == block) { |
| if(!eqtype(l->type, t)) |
| goto allnew; |
| nred++; |
| } |
| ntot++; |
| } |
| |
| // test for special case |
| // a) multi-assignment (ntot>1) |
| // b) at least one redeclaration (red>0) |
| // c) not all redeclarations (nred!=ntot) |
| if(nred == 0 || ntot <= 1 || nred == ntot) |
| goto allnew; |
| |
| n = nil; |
| for(ll=nl; ll; ll=ll->next) { |
| l = ll->n; |
| t = l->type; |
| l = l->left; |
| |
| a = l; |
| if(l->sym->block != block) |
| a = old2new(l, t, init); |
| |
| n = list(n, a); |
| } |
| return n; |
| |
| allnew: |
| // same as original |
| n = nil; |
| for(ll=nl; ll; ll=ll->next) { |
| l = ll->n; |
| t = l->type; |
| l = l->left; |
| |
| a = old2new(l, t, init); |
| n = list(n, a); |
| } |
| return n; |
| } |
| |
| Node* |
| colas(NodeList *ll, NodeList *lr) |
| { |
| Node *l, *r, *a, *nl, *nr; |
| Iter savet; |
| NodeList *init, *savel, *saver, *n; |
| Type *t; |
| int cl, cr; |
| |
| /* nl is an expression list. |
| * nr is an expression list. |
| * return a newname-list from |
| * types derived from the rhs. |
| */ |
| cr = count(lr); |
| cl = count(ll); |
| init = nil; |
| n = nil; |
| |
| /* check calls early, to give better message for a := f() */ |
| if(cr == 1) { |
| nr = lr->n; |
| switch(nr->op) { |
| case OCALL: |
| if(nr->left->op == ONAME && nr->left->etype != 0) |
| break; |
| walkexpr(nr->left, Erv | Etype, &init); |
| if(nr->left->op == OTYPE) |
| break; |
| goto call; |
| case OCALLMETH: |
| case OCALLINTER: |
| walkexpr(nr->left, Erv, &init); |
| call: |
| convlit(&nr->left, types[TFUNC]); |
| t = nr->left->type; |
| if(t == T) |
| goto outl; // error already printed |
| if(t->etype == tptr) |
| t = t->type; |
| if(t == T || t->etype != TFUNC) { |
| yyerror("cannot call %T", t); |
| goto outl; |
| } |
| if(t->outtuple != cl) { |
| cr = t->outtuple; |
| goto badt; |
| } |
| // finish call - first half above |
| t = structfirst(&savet, getoutarg(t)); |
| if(t == T) |
| goto outl; |
| for(savel=ll; savel; savel=savel->next) { |
| l = savel->n; |
| a = mixedoldnew(l, t->type); |
| n = list(n, a); |
| t = structnext(&savet); |
| } |
| n = checkmixed(n, &init); |
| goto out; |
| } |
| } |
| if(cl != cr) { |
| if(cr == 1) { |
| nr = lr->n; |
| goto multi; |
| } |
| goto badt; |
| } |
| |
| for(savel=ll, saver=lr; savel != nil; savel=savel->next, saver=saver->next) { |
| l = savel->n; |
| r = saver->n; |
| |
| walkexpr(r, Erv, &init); |
| defaultlit(&r, T); |
| saver->n = r; |
| a = mixedoldnew(l, r->type); |
| n = list(n, a); |
| } |
| n = checkmixed(n, &init); |
| goto out; |
| |
| multi: |
| /* |
| * there is a list on the left |
| * and a mono on the right. |
| * go into the right to get |
| * individual types for the left. |
| */ |
| switch(nr->op) { |
| default: |
| goto badt; |
| |
| case OINDEX: |
| // check if rhs is a map index. |
| // if so, types are valuetype,bool |
| if(cl != 2) |
| goto badt; |
| walkexpr(nr->left, Erv, &init); |
| implicitstar(&nr->left); |
| t = nr->left->type; |
| if(!istype(t, TMAP)) |
| goto badt; |
| a = mixedoldnew(ll->n, t->type); |
| n = list1(a); |
| a = mixedoldnew(ll->next->n, types[TBOOL]); |
| n = list(n, a); |
| n = checkmixed(n, &init); |
| break; |
| |
| case ODOTTYPE: |
| // a,b := i.(T) |
| walkdottype(nr, &init); |
| if(cl != 2) |
| goto badt; |
| // a,b = iface |
| a = mixedoldnew(ll->n, nr->type); |
| n = list1(a); |
| a = mixedoldnew(ll->next->n, types[TBOOL]); |
| n = list(n, a); |
| n = checkmixed(n, &init); |
| break; |
| |
| case ORECV: |
| if(cl != 2) |
| goto badt; |
| walkexpr(nr->left, Erv, &init); |
| t = nr->left->type; |
| if(!istype(t, TCHAN)) |
| goto badt; |
| a = mixedoldnew(ll->n, t->type); |
| n = list1(a); |
| a = mixedoldnew(ll->next->n, types[TBOOL]); |
| n = list(n, a); |
| n = checkmixed(n, &init); |
| break; |
| } |
| goto out; |
| |
| badt: |
| nl = ll->n; |
| if(nl->diag == 0) { |
| nl->diag = 1; |
| yyerror("assignment count mismatch: %d = %d", cl, cr); |
| } |
| outl: |
| n = ll; |
| |
| out: |
| // n is the lhs of the assignment. |
| // init holds the list of declarations. |
| a = nod(OAS2, N, N); |
| a->list = n; |
| a->rlist = lr; |
| a->ninit = init; |
| a->colas = 1; |
| return a; |
| } |
| |
| /* |
| * rewrite a range statement |
| * k and v are names/new_names |
| * m is an array or map |
| * local is 0 (meaning =) or 1 (meaning :=) |
| */ |
| Node* |
| dorange(Node *nn) |
| { |
| Node *k, *v, *m; |
| Node *n, *hv, *hc, *ha, *hk, *ohk, *on, *r, *a, *as; |
| NodeList *init, *args; |
| Type *t, *th; |
| int local; |
| NodeList *nl; |
| |
| if(nn->op != ORANGE) |
| fatal("dorange not ORANGE"); |
| |
| nl = nn->list; |
| k = nl->n; |
| if((nl = nl->next) != nil) { |
| v = nl->n; |
| nl = nl->next; |
| } else |
| v = N; |
| if(nl != nil) |
| yyerror("too many variables in range"); |
| |
| n = nod(OFOR, N, N); |
| init = nil; |
| |
| walkexpr(nn->right, Erv, &init); |
| implicitstar(&nn->right); |
| m = nn->right; |
| local = nn->etype; |
| |
| t = m->type; |
| if(t == T) |
| goto out; |
| if(t->etype == TARRAY) |
| goto ary; |
| if(t->etype == TMAP) |
| goto map; |
| if(t->etype == TCHAN) |
| goto chan; |
| if(t->etype == TSTRING) |
| goto strng; |
| |
| yyerror("range must be over map/array/chan/string"); |
| goto out; |
| |
| ary: |
| hk = nod(OXXX, N, N); // hidden key |
| tempname(hk, types[TINT]); |
| |
| ha = nod(OXXX, N, N); // hidden array |
| tempname(ha, t); |
| |
| a = nod(OAS, hk, nodintconst(0)); |
| init = list(init, a); |
| |
| a = nod(OAS, ha, m); |
| init = list(init, a); |
| |
| n->ntest = nod(OLT, hk, nod(OLEN, ha, N)); |
| n->nincr = nod(OASOP, hk, nodintconst(1)); |
| n->nincr->etype = OADD; |
| |
| if(local) |
| k = old2new(k, hk->type, &init); |
| n->nbody = list1(nod(OAS, k, hk)); |
| |
| if(v != N) { |
| if(local) |
| v = old2new(v, t->type, &init); |
| n->nbody = list(n->nbody, |
| nod(OAS, v, nod(OINDEX, ha, hk)) ); |
| } |
| goto out; |
| |
| map: |
| th = typ(TARRAY); |
| th->type = ptrto(types[TUINT8]); |
| th->bound = (sizeof(struct Hiter) + types[tptr]->width - 1) / |
| types[tptr]->width; |
| hk = nod(OXXX, N, N); // hidden iterator |
| tempname(hk, th); // hashmap hash_iter |
| |
| on = syslook("mapiterinit", 1); |
| argtype(on, t->down); |
| argtype(on, t->type); |
| argtype(on, th); |
| a = nod(OADDR, hk, N); |
| r = nod(OCALL, on, N); |
| r->list = list(list1(m), a); |
| |
| init = list(init, r); |
| |
| r = nod(OINDEX, hk, nodintconst(0)); |
| a = nod(OLITERAL, N, N); |
| a->val.ctype = CTNIL; |
| a->type = types[TNIL]; |
| r = nod(ONE, r, a); |
| n->ntest = r; |
| |
| on = syslook("mapiternext", 1); |
| argtype(on, th); |
| r = nod(OADDR, hk, N); |
| args = list1(r); |
| r = nod(OCALL, on, N); |
| r->list = args; |
| n->nincr = r; |
| |
| if(local) |
| k = old2new(k, t->down, &init); |
| if(v == N) { |
| on = syslook("mapiter1", 1); |
| argtype(on, th); |
| argtype(on, t->down); |
| r = nod(OADDR, hk, N); |
| args = list1(r); |
| r = nod(OCALL, on, N); |
| r->list = args; |
| n->nbody = list1(nod(OAS, k, r)); |
| goto out; |
| } |
| if(local) |
| v = old2new(v, t->type, &init); |
| on = syslook("mapiter2", 1); |
| argtype(on, th); |
| argtype(on, t->down); |
| argtype(on, t->type); |
| r = nod(OADDR, hk, N); |
| args = list1(r); |
| r = nod(OCALL, on, N); |
| r->list = args; |
| as = nod(OAS2, N, N); |
| as->list = list(list1(k), v); |
| as->rlist = list1(r); |
| n->nbody = list1(as); |
| goto out; |
| |
| chan: |
| if(v != N) |
| yyerror("chan range can only have one variable"); |
| |
| hc = nod(OXXX, N, N); // hidden chan |
| tempname(hc, t); |
| |
| hv = nod(OXXX, N, N); // hidden value |
| tempname(hv, t->type); |
| |
| a = nod(OAS, hc, m); |
| init = list(init, a); |
| |
| a = nod(ORECV, hc, N); |
| a = nod(OAS, hv, a); |
| init = list(init, a); |
| |
| a = nod(OCLOSED, N, N); |
| a->list = list1(hc); |
| n->ntest = nod(ONOT, a, N); |
| n->nincr = nod(OAS, hv, nod(ORECV, hc, N)); |
| |
| if(local) |
| k = old2new(k, hv->type, &init); |
| n->nbody = list1(nod(OAS, k, hv)); |
| |
| goto out; |
| |
| strng: |
| hk = nod(OXXX, N, N); // hidden key |
| tempname(hk, types[TINT]); |
| |
| ohk = nod(OXXX, N, N); // old hidden key |
| tempname(ohk, types[TINT]); |
| |
| ha = nod(OXXX, N, N); // hidden string |
| tempname(ha, types[TSTRING]); |
| |
| hv = N; |
| if(v != N) { |
| hv = nod(OXXX, N, N); // hidden value |
| tempname(hv, types[TINT]); |
| } |
| |
| if(local) { |
| k = old2new(k, types[TINT], &init); |
| if(v != N) |
| v = old2new(v, types[TINT], &init); |
| } |
| |
| // ha = s |
| a = nod(OCONV, m, N); |
| a->type = ha->type; |
| a = nod(OAS, ha, a); |
| init = list(init, a); |
| |
| // ohk = 0 |
| a = nod(OAS, ohk, nodintconst(0)); |
| init = list(init, a); |
| |
| // hk[,hv] = stringiter(ha,hk) |
| if(v != N) { |
| // hk,v = stringiter2(ha, hk) |
| on = syslook("stringiter2", 0); |
| a = nod(OCALL, on, N); |
| a->list = list(list1(ha), nodintconst(0)); |
| as = nod(OAS2, N, N); |
| as->list = list(list1(hk), hv); |
| as->rlist = list1(a); |
| a = as; |
| } else { |
| // hk = stringiter(ha, hk) |
| on = syslook("stringiter", 0); |
| a = nod(OCALL, on, N); |
| a->list = list(list1(ha), nodintconst(0)); |
| a = nod(OAS, hk, a); |
| } |
| init = list(init, a); |
| |
| // while(hk != 0) |
| n->ntest = nod(ONE, hk, nodintconst(0)); |
| |
| // hk[,hv] = stringiter(ha,hk) |
| if(v != N) { |
| // hk,hv = stringiter2(ha, hk) |
| on = syslook("stringiter2", 0); |
| a = nod(OCALL, on, N); |
| a->list = list(list1(ha), hk); |
| as = nod(OAS2, N, N); |
| as->list = list(list1(hk), hv); |
| as->rlist = list1(a); |
| a = as; |
| } else { |
| // hk = stringiter(ha, hk) |
| on = syslook("stringiter", 0); |
| a = nod(OCALL, on, N); |
| a->list = list(list1(ha), hk); |
| a = nod(OAS, hk, a); |
| } |
| n->nincr = a; |
| |
| // k,ohk[,v] = ohk,hk,[,hv] |
| a = nod(OAS, k, ohk); |
| n->nbody = list1(a); |
| a = nod(OAS, ohk, hk); |
| n->nbody = list(n->nbody, a); |
| if(v != N) { |
| a = nod(OAS, v, hv); |
| n->nbody = list(n->nbody, a); |
| } |
| |
| out: |
| n->ninit = concat(n->ninit, init); |
| 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, 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; // one fncall assigned to stack |
| r = nil; // non fncalls and tempnames assigned to stack |
| |
| for(l=all; l; l=l->next) { |
| n = l->n; |
| ullmancalc(n); |
| if(n->ullman < UINF) { |
| r = list(r, n); |
| continue; |
| } |
| if(f == N) { |
| 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) |
| return 0; |
| switch(l->op) { |
| case ONAME: |
| // match each left with all rights |
| 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)) { |
| 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; |
| } |
| |
| static void |
| fielddup(Node *n, Node *hash[], ulong nhash) |
| { |
| uint h; |
| char *s; |
| Node *a; |
| |
| if(n->op != ONAME) |
| fatal("fielddup: not ONAME"); |
| s = n->sym->name; |
| h = stringhash(s)%nhash; |
| for(a=hash[h]; a!=N; a=a->ntest) { |
| if(strcmp(a->sym->name, s) == 0) { |
| yyerror("duplicate field name in struct literal: %s", s); |
| return; |
| } |
| } |
| n->ntest = hash[h]; |
| hash[h] = n; |
| } |
| |
| Node* |
| structlit(Node *n, Node *var, NodeList **init) |
| { |
| Iter savel; |
| Type *l, *t; |
| Node *r, *a; |
| Node* hash[101]; |
| NodeList *nl; |
| int nerr; |
| |
| nerr = nerrors; |
| t = n->type; |
| if(t->etype != TSTRUCT) |
| fatal("structlit: not struct"); |
| |
| if(var == N) { |
| var = nod(OXXX, N, N); |
| tempname(var, t); |
| } |
| |
| nl = n->list; |
| if(nl == nil || nl->n->op == OKEY) |
| goto keyval; |
| |
| l = structfirst(&savel, &n->type); |
| for(; nl; nl=nl->next) { |
| r = nl->n; |
| // assignment to every field |
| if(l == T) |
| break; |
| if(r->op == OKEY) { |
| yyerror("mixture of value and field:value initializers"); |
| return var; |
| } |
| |
| // build list of var.field = expr |
| a = nod(ODOT, var, newname(l->sym)); |
| a = nod(OAS, a, r); |
| walkexpr(a, Etop, init); |
| if(nerr != nerrors) |
| return var; |
| *init = list(*init, a); |
| |
| l = structnext(&savel); |
| } |
| if(l != T) |
| yyerror("struct literal expect expr of type %T", l); |
| if(nl != nil) |
| yyerror("struct literal too many expressions"); |
| return var; |
| |
| keyval: |
| memset(hash, 0, sizeof(hash)); |
| a = nod(OAS, var, N); |
| walkexpr(a, Etop, init); |
| *init = list(*init, a); |
| |
| for(; nl; nl=nl->next) { |
| r = nl->n; |
| |
| // assignment to field:value elements |
| if(r->op != OKEY) { |
| yyerror("mixture of field:value and value initializers"); |
| break; |
| } |
| |
| // build list of var.field = expr |
| a = nod(ODOT, var, newname(r->left->sym)); |
| fielddup(a->right, hash, nelem(hash)); |
| if(nerr != nerrors) |
| break; |
| |
| a = nod(OAS, a, r->right); |
| walkexpr(a, Etop, init); |
| if(nerr != nerrors) |
| break; |
| |
| *init = list(*init, a); |
| } |
| return var; |
| } |
| |
| static void |
| indexdup(Node *n, Node *hash[], ulong nhash) |
| { |
| uint h; |
| Node *a; |
| ulong b, c; |
| |
| if(n->op != OLITERAL) |
| fatal("indexdup: not OLITERAL"); |
| |
| b = mpgetfix(n->val.u.xval); |
| h = b%nhash; |
| for(a=hash[h]; a!=N; a=a->ntest) { |
| c = mpgetfix(a->val.u.xval); |
| if(b == c) { |
| yyerror("duplicate index in array literal: %ld", b); |
| return; |
| } |
| } |
| n->ntest = hash[h]; |
| hash[h] = n; |
| } |
| |
| Node* |
| arraylit(Node *n, Node *var, NodeList **init) |
| { |
| Type *t; |
| Node *r, *a; |
| NodeList *l; |
| long ninit, b; |
| Node* hash[101]; |
| int nerr; |
| |
| nerr = nerrors; |
| t = n->type; |
| if(t->etype != TARRAY) |
| fatal("arraylit: not array"); |
| |
| // find max index |
| ninit = 0; |
| b = 0; |
| |
| for(l=n->list; l; l=l->next) { |
| r = l->n; |
| if(r->op == OKEY) { |
| evconst(r->left); |
| b = nonnegconst(r->left); |
| } |
| b++; |
| if(b > ninit) |
| ninit = b; |
| } |
| |
| b = t->bound; |
| if(b == -100) { |
| // flag for [...] |
| b = ninit; |
| if(var == N) |
| t = shallow(t); |
| t->bound = b; |
| } |
| |
| if(var == N) { |
| var = nod(OXXX, N, N); |
| tempname(var, t); |
| } |
| |
| if(b < 0) { |
| // slice |
| a = nod(OMAKE, N, N); |
| a->list = list(list1(typenod(t)), nodintconst(ninit)); |
| a = nod(OAS, var, a); |
| walkexpr(a, Etop, init); |
| *init = list(*init, a); |
| } else { |
| // if entire array isnt initialized, |
| // then clear the array |
| if(ninit < b) { |
| a = nod(OAS, var, N); |
| walkexpr(a, Etop, init); |
| *init = list(*init, a); |
| } |
| } |
| |
| b = 0; |
| memset(hash, 0, sizeof(hash)); |
| for(l=n->list; l; l=l->next) { |
| r = l->n; |
| // build list of var[c] = expr |
| if(r->op == OKEY) { |
| b = nonnegconst(r->left); |
| if(b < 0) { |
| yyerror("array index must be non-negative constant"); |
| break; |
| } |
| r = r->right; |
| } |
| |
| if(t->bound >= 0 && b > t->bound) { |
| yyerror("array index out of bounds"); |
| break; |
| } |
| |
| a = nodintconst(b); |
| indexdup(a, hash, nelem(hash)); |
| if(nerr != nerrors) |
| break; |
| |
| a = nod(OINDEX, var, a); |
| a = nod(OAS, a, r); |
| walkexpr(a, Etop, init); // add any assignments in r to top |
| if(nerr != nerrors) |
| break; |
| |
| *init = list(*init, a); |
| b++; |
| } |
| return var; |
| } |
| |
| static void |
| keydup(Node *n, Node *hash[], ulong nhash) |
| { |
| uint h; |
| ulong b; |
| double d; |
| int i; |
| Node *a; |
| Node cmp; |
| char *s; |
| |
| evconst(n); |
| if(n->op != OLITERAL) |
| return; // we dont check variables |
| |
| switch(n->val.ctype) { |
| default: // unknown, bool, nil |
| b = 23; |
| break; |
| case CTINT: |
| b = mpgetfix(n->val.u.xval); |
| break; |
| case CTFLT: |
| d = mpgetflt(n->val.u.fval); |
| s = (char*)&d; |
| b = 0; |
| for(i=sizeof(d); i>0; i--) |
| b = b*PRIME1 + *s++; |
| break; |
| case CTSTR: |
| b = 0; |
| s = n->val.u.sval->s; |
| for(i=n->val.u.sval->len; i>0; i--) |
| b = b*PRIME1 + *s++; |
| break; |
| } |
| |
| h = b%nhash; |
| memset(&cmp, 0, sizeof(cmp)); |
| for(a=hash[h]; a!=N; a=a->ntest) { |
| cmp.op = OEQ; |
| cmp.left = n; |
| cmp.right = a; |
| evconst(&cmp); |
| b = cmp.val.u.bval; |
| if(b) { |
| // too lazy to print the literal |
| yyerror("duplicate key in map literal"); |
| return; |
| } |
| } |
| n->ntest = hash[h]; |
| hash[h] = n; |
| } |
| |
| Node* |
| maplit(Node *n, Node *var, NodeList **init) |
| { |
| Type *t; |
| Node *r, *a; |
| Node* hash[101]; |
| NodeList *l; |
| int nerr; |
| |
| nerr = nerrors; |
| t = n->type; |
| if(t->etype != TMAP) |
| fatal("maplit: not map"); |
| |
| if(var == N) { |
| var = nod(OXXX, N, N); |
| tempname(var, t); |
| } |
| |
| a = nod(OMAKE, N, N); |
| a->list = list1(typenod(t)); |
| a = nod(OAS, var, a); |
| walkexpr(a, Etop, init); |
| *init = list(*init, a); |
| |
| memset(hash, 0, sizeof(hash)); |
| for(l=n->list; l; l=l->next) { |
| r = l->n; |
| if(r->op != OKEY) { |
| yyerror("map literal must have key:value pairs"); |
| break; |
| } |
| |
| // build list of var[c] = expr |
| keydup(r->left, hash, nelem(hash)); |
| if(nerr != nerrors) |
| break; |
| |
| a = nod(OINDEX, var, r->left); |
| a = nod(OAS, a, r->right); |
| walkexpr(a, Etop, init); |
| if(nerr != nerrors) |
| break; |
| |
| *init = list(*init, a); |
| } |
| return var; |
| } |
| |
| /* |
| * the address of n has been taken and might be used after |
| * the current function returns. mark any local vars |
| * as needing to move to the heap. |
| */ |
| void |
| addrescapes(Node *n) |
| { |
| char buf[100]; |
| switch(n->op) { |
| default: |
| // probably a type error already. |
| // dump("addrescapes", n); |
| break; |
| |
| case ONAME: |
| if(n->noescape) |
| break; |
| switch(n->class) { |
| case PPARAMOUT: |
| yyerror("cannot take address of out parameter %s", n->sym->name); |
| break; |
| case PAUTO: |
| case PPARAM: |
| // if func param, need separate temporary |
| // to hold heap pointer. |
| if(n->class == PPARAM) { |
| // expression to refer to stack copy |
| n->stackparam = nod(OPARAM, n, N); |
| n->stackparam->type = n->type; |
| n->stackparam->addable = 1; |
| n->stackparam->xoffset = n->xoffset; |
| } |
| |
| n->class |= PHEAP; |
| n->addable = 0; |
| n->ullman = 2; |
| n->alloc = callnew(n->type); |
| n->xoffset = 0; |
| |
| // create stack variable to hold pointer to heap |
| n->heapaddr = nod(0, N, N); |
| tempname(n->heapaddr, ptrto(n->type)); |
| snprint(buf, sizeof buf, "&%S", n->sym); |
| n->heapaddr->sym = lookup(buf); |
| break; |
| } |
| break; |
| |
| case OIND: |
| case ODOTPTR: |
| break; |
| |
| case ODOT: |
| case OINDEX: |
| // ODOTPTR has already been introduced, |
| // so these are the non-pointer ODOT and OINDEX. |
| // In &x[0], if x is a slice, then x does not |
| // escape--the pointer inside x does, but that |
| // is always a heap pointer anyway. |
| if(!isslice(n->left->type)) |
| addrescapes(n->left); |
| break; |
| } |
| } |
| |
| /* |
| * walk through argin parameters. |
| * generate and return code to allocate |
| * copies of escaped parameters to the heap. |
| */ |
| NodeList* |
| paramstoheap(Type **argin) |
| { |
| Type *t; |
| Iter savet; |
| Node *v; |
| NodeList *nn; |
| |
| nn = nil; |
| for(t = structfirst(&savet, argin); t != T; t = structnext(&savet)) { |
| v = t->nname; |
| if(v == N || !(v->class & PHEAP)) |
| continue; |
| |
| // generate allocation & copying code |
| nn = list(nn, nod(OAS, v->heapaddr, v->alloc)); |
| nn = list(nn, nod(OAS, v, v->stackparam)); |
| } |
| return nn; |
| } |
| |
| /* |
| * take care of migrating any function in/out args |
| * between the stack and the heap. adds code to |
| * curfn's before and after lists. |
| */ |
| void |
| heapmoves(void) |
| { |
| NodeList *nn; |
| |
| nn = paramstoheap(getthis(curfn->type)); |
| nn = concat(nn, paramstoheap(getinarg(curfn->type))); |
| curfn->enter = concat(curfn->enter, nn); |
| } |