// 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. /* * type check the whole tree of an expression. * calculates expression types. * evaluates compile time constants. * marks variables that escape the local frame. * rewrites n->op to be more specific in some cases. * sets n->walk to walking function. * * TODO: * trailing ... section of function calls */ #include "go.h" static void implicitstar(Node**); static int onearg(Node*); static int lookdot(Node*, Type*); static void typecheckaste(int, Type*, NodeList*, char*); static int exportassignok(Type*, char*); static Type* lookdot1(Sym *s, Type *t, Type *f); static int nokeys(NodeList*); static void typecheckcomplit(Node**); static void addrescapes(Node*); static void typecheckas2(Node*); static void typecheckas(Node*); static void typecheckfunc(Node*); static void checklvalue(Node*, char*); static void checkassign(Node*); static void checkassignlist(NodeList*); static int islvalue(Node*); static void toslice(Node**); void typechecklist(NodeList *l, int top) { for(; l; l=l->next) typecheck(&l->n, top); } /* * type check node *np. * replaces *np with a new pointer in some cases. * returns the final value of *np as a convenience. */ Node* typecheck(Node **np, int top) { int et, op; Node *n, *l, *r; NodeList *args; int lno, ok, ntop; Type *t; // cannot type check until all the source has been parsed if(!typecheckok) fatal("early typecheck"); n = *np; if(n == N) return N; // Skip typecheck if already done. // But re-typecheck ONAME/OTYPE/OLITERAL/OPACK node in case context has changed. if(n->typecheck == 1) { switch(n->op) { case ONAME: case OTYPE: case OLITERAL: case OPACK: break; default: return n; } } if(n->typecheck == 2) fatal("typecheck loop"); n->typecheck = 2; redo: lno = setlineno(n); if(n->sym) { walkdef(n); if(n->op == ONONAME) goto error; } reswitch: ok = 0; switch(n->op) { default: // until typecheck is complete, do nothing. dump("typecheck", n); fatal("typecheck %O", n->op); /* * names */ case OLITERAL: ok |= Erv; if(n->val.ctype == CTSTR) n->type = idealstring; goto ret; case ONONAME: ok |= Erv; goto ret; case ONAME: if(n->etype != 0) { ok |= Ecall; goto ret; } if(!(top & Easgn)) { // not a write to the variable if(isblank(n)) { yyerror("cannot use _ as value"); goto error; } n->used = 1; } ok |= Erv; goto ret; case OPACK: yyerror("use of package %S not in selector", n->sym); goto error; case OIOTA: // looked like iota during parsing but might // have been redefined. decide. if(n->left->op != ONONAME) n = n->left; else n = n->right; goto redo; /* * types (OIND is with exprs) */ case OTYPE: ok |= Etype; if(n->type == T) goto error; break; case OTARRAY: ok |= Etype; t = typ(TARRAY); l = n->left; r = n->right; if(l == nil) { t->bound = -1; } else { typecheck(&l, Erv | Etype); switch(l->op) { default: yyerror("invalid array bound %#N", l); goto error; case OLITERAL: if(consttype(l) == CTINT) { t->bound = mpgetfix(l->val.u.xval); if(t->bound < 0) { yyerror("array bound must be non-negative"); goto error; } } break; case OTYPE: if(l->type == T) goto error; if(l->type->etype != TDDD) { yyerror("invalid array bound %T", l->type); goto error; } t->bound = -100; break; } } typecheck(&r, Etype); if(r->type == T) goto error; t->type = r->type; n->op = OTYPE; n->type = t; n->left = N; n->right = N; if(t->bound != -100) checkwidth(t); break; case OTMAP: ok |= Etype; l = typecheck(&n->left, Etype); r = typecheck(&n->right, Etype); if(l->type == T || r->type == T) goto error; n->op = OTYPE; n->type = maptype(l->type, r->type); n->left = N; n->right = N; break; case OTCHAN: ok |= Etype; l = typecheck(&n->left, Etype); if(l->type == T) goto error; t = typ(TCHAN); t->type = l->type; t->chan = n->etype; n->op = OTYPE; n->type = t; n->left = N; n->etype = 0; break; case OTSTRUCT: ok |= Etype; n->op = OTYPE; n->type = dostruct(n->list, TSTRUCT); if(n->type == T) goto error; n->list = nil; break; case OTINTER: ok |= Etype; n->op = OTYPE; n->type = dostruct(n->list, TINTER); if(n->type == T) goto error; n->type = sortinter(n->type); break; case OTFUNC: ok |= Etype; n->op = OTYPE; n->type = functype(n->left, n->list, n->rlist); if(n->type == T) goto error; break; /* * type or expr */ case OIND: ntop = Erv | Etype; if(!(top & Eaddr)) ntop |= Eindir; l = typecheck(&n->left, ntop); if((t = l->type) == T) goto error; if(l->op == OTYPE) { ok |= Etype; n->op = OTYPE; n->type = ptrto(l->type); n->left = N; goto ret; } if(!isptr[t->etype]) { yyerror("invalid indirect of %+N", n->left); goto error; } ok |= Erv; n->type = t->type; goto ret; /* * arithmetic exprs */ case OASOP: ok |= Etop; l = typecheck(&n->left, Erv); checkassign(n->left); r = typecheck(&n->right, Erv); if(l->type == T || r->type == T) goto error; op = n->etype; goto arith; case OADD: case OAND: case OANDAND: case OANDNOT: case ODIV: case OEQ: case OGE: case OGT: case OLE: case OLT: case OLSH: case ORSH: case OMOD: case OMUL: case ONE: case OOR: case OOROR: case OSUB: case OXOR: ok |= Erv; l = typecheck(&n->left, Erv | (top & Eiota)); r = typecheck(&n->right, Erv | (top & Eiota)); if(l->type == T || r->type == T) goto error; op = n->op; arith: if(op == OLSH || op == ORSH) goto shift; // ideal mixed with non-ideal defaultlit2(&l, &r, 0); n->left = l; n->right = r; if(l->type == T || r->type == T) goto error; t = l->type; if(t->etype == TIDEAL) t = r->type; et = t->etype; if(et == TIDEAL) et = TINT; if(t->etype != TIDEAL && !eqtype(l->type, r->type)) { badbinary: defaultlit2(&l, &r, 1); yyerror("invalid operation: %#N (type %T %#O %T)", n, l->type, op, r->type); goto error; } if(!okfor[op][et]) goto badbinary; // okfor allows any array == array; // restrict to slice == nil and nil == slice. if(l->type->etype == TARRAY && !isslice(l->type)) goto badbinary; if(r->type->etype == TARRAY && !isslice(r->type)) goto badbinary; if(isslice(l->type) && !isnil(l) && !isnil(r)) goto badbinary; t = l->type; if(iscmp[n->op]) { evconst(n); t = types[TBOOL]; if(n->op != OLITERAL) { defaultlit2(&l, &r, 1); n->left = l; n->right = r; } } if(et == TSTRING) { if(iscmp[n->op]) { n->etype = n->op; n->op = OCMPSTR; } else if(n->op == OASOP) n->op = OAPPENDSTR; else if(n->op == OADD) n->op = OADDSTR; } if(et == TINTER) { if(l->op == OLITERAL && l->val.ctype == CTNIL) { // swap for back end n->left = r; n->right = l; } else if(r->op == OLITERAL && r->val.ctype == CTNIL) { // leave alone for back end } else { n->etype = n->op; n->op = OCMPIFACE; } } n->type = t; goto ret; shift: defaultlit(&r, types[TUINT]); n->right = r; t = r->type; if(!isint[t->etype] || issigned[t->etype]) { yyerror("invalid operation: %#N (shift count type %T)", n, r->type); goto error; } t = l->type; if(t != T && t->etype != TIDEAL && !isint[t->etype]) { yyerror("invalid operation: %#N (shift of type %T)", n, t); goto error; } // no defaultlit for left // the outer context gives the type n->type = l->type; goto ret; case OCOM: case OMINUS: case ONOT: case OPLUS: ok |= Erv; l = typecheck(&n->left, Erv | (top & Eiota)); if((t = l->type) == T) goto error; if(!okfor[n->op][t->etype]) { yyerror("invalid operation: %#O %T", n->op, t); goto error; } n->type = t; goto ret; /* * exprs */ case OADDR: ok |= Erv; typecheck(&n->left, Erv | Eaddr); if(n->left->type == T) goto error; switch(n->left->op) { case OMAPLIT: case OSTRUCTLIT: case OARRAYLIT: break; default: checklvalue(n->left, "take the address of"); } defaultlit(&n->left, T); l = n->left; if((t = l->type) == T) goto error; if(!(top & Eindir)) addrescapes(n->left); n->type = ptrto(t); goto ret; case OCOMPLIT: ok |= Erv; typecheckcomplit(&n); if(n->type == T) goto error; goto ret; case OXDOT: n = adddot(n); n->op = ODOT; // fall through case ODOT: l = typecheck(&n->left, Erv); if((t = l->type) == T) goto error; if(n->right->op != ONAME) { yyerror("rhs of . must be a name"); // impossible goto error; } if(isptr[t->etype]) { t = t->type; if(t == T) goto error; n->op = ODOTPTR; checkwidth(t); } if(!lookdot(n, t)) { yyerror("%#N undefined (type %T has no field %S)", n, t, n->right->sym); goto error; } switch(n->op) { case ODOTINTER: case ODOTMETH: ok |= Ecall; break; default: ok |= Erv; break; } goto ret; case ODOTTYPE: ok |= Erv; typecheck(&n->left, Erv); defaultlit(&n->left, T); l = n->left; if((t = l->type) == T) goto error; if(!isinter(t)) { yyerror("invalid type assertion: %#N (non-interface type %T on left)", n, t); goto error; } if(n->right != N) { typecheck(&n->right, Etype); n->type = n->right->type; n->right = N; if(n->type == T) goto error; } goto ret; case OINDEX: ok |= Erv; typecheck(&n->left, Erv); defaultlit(&n->left, T); implicitstar(&n->left); l = n->left; typecheck(&n->right, Erv); r = n->right; if((t = l->type) == T || r->type == T) goto error; switch(t->etype) { default: yyerror("invalid operation: %#N (index of type %T)", n, t); goto error; case TARRAY: defaultlit(&n->right, types[TUINT]); if(n->right->type != T && !isint[n->right->type->etype]) yyerror("non-integer array index %#N", n->right); n->type = t->type; break; case TMAP: n->etype = 0; defaultlit(&n->right, t->down); if(n->right->type != T && !eqtype(n->right->type, t->down)) yyerror("invalid map index %#N - need type %T", n->right, t->down); n->type = t->type; n->op = OINDEXMAP; break; case TSTRING: defaultlit(&n->right, types[TUINT]); if(n->right->type != T && !isint[n->right->type->etype]) yyerror("non-integer string index %#N", n->right); n->type = types[TUINT8]; n->op = OINDEXSTR; break; } goto ret; case ORECV: ok |= Etop | Erv; typecheck(&n->left, Erv); defaultlit(&n->left, T); l = n->left; if((t = l->type) == T) goto error; if(t->etype != TCHAN) { yyerror("invalid operation: %#N (receive from non-chan type %T)", n, t); goto error; } if(!(t->chan & Crecv)) { yyerror("invalid operation: %#N (receive from send-only type %T)", n, t); goto error; } n->type = t->type; goto ret; case OSEND: ok |= Etop | Erv; l = typecheck(&n->left, Erv); typecheck(&n->right, Erv); defaultlit(&n->left, T); l = n->left; if((t = l->type) == T) goto error; if(!(t->chan & Csend)) { yyerror("invalid operation: %#N (send to receive-only type %T)", n, t); goto error; } defaultlit(&n->right, t->type); r = n->right; if((t = r->type) == T) goto error; // TODO: more aggressive n->etype = 0; n->type = T; if(top & Erv) { n->op = OSENDNB; n->type = types[TBOOL]; } goto ret; case OSLICE: ok |= Erv; typecheck(&n->left, top); typecheck(&n->right->left, Erv); typecheck(&n->right->right, Erv); defaultlit(&n->left, T); defaultlit(&n->right->left, types[TUINT]); defaultlit(&n->right->right, types[TUINT]); implicitstar(&n->left); if(n->right->left == N) { yyerror("missing slice bounds?"); goto error; } if((t = n->right->left->type) == T) goto error; if(!isint[t->etype]) { yyerror("invalid slice index %#N (type %T)", n->right->left, t); goto error; } if(n->right->right != N) { if((t = n->right->right->type) == T) goto error; if(!isint[t->etype]) { yyerror("invalid slice index %#N (type %T)", n->right->right, t); goto error; } } l = n->left; if((t = l->type) == T) goto error; // TODO(rsc): 64-bit slice index needs to be checked // for overflow in generated code if(istype(t, TSTRING)) { n->type = t; n->op = OSLICESTR; goto ret; } if(isfixedarray(t)) { n->type = typ(TARRAY); n->type->type = t->type; n->type->bound = -1; dowidth(n->type); n->op = OSLICEARR; goto ret; } if(isslice(t)) { n->type = t; goto ret; } yyerror("cannot slice %#N (type %T)", l, t); goto error; /* * call and call like */ case OCALL: l = n->left; if(l->op == ONAME && l->etype != 0) { // builtin: OLEN, OCAP, etc. n->op = l->etype; n->left = n->right; n->right = N; goto reswitch; } if(l->op == ONAME && (r = unsafenmagic(l, n->list)) != N) { n = r; goto reswitch; } typecheck(&n->left, Erv | Etype | Ecall); defaultlit(&n->left, T); l = n->left; if(l->op == OTYPE) { // pick off before type-checking arguments ok |= Erv; // turn CALL(type, arg) into CONV(arg) w/ type n->left = N; if(onearg(n) < 0) goto error; n->op = OCONV; n->type = l->type; goto doconv; } if(count(n->list) == 1) typecheck(&n->list->n, Erv | Efnstruct); else typechecklist(n->list, Erv); if((t = l->type) == T) goto error; checkwidth(t); switch(l->op) { case ODOTINTER: n->op = OCALLINTER; break; case ODOTMETH: n->op = OCALLMETH; typecheckaste(OCALL, getthisx(t), list1(l->left), "method receiver"); break; default: n->op = OCALLFUNC; if(t->etype != TFUNC) { yyerror("cannot call non-function %#N (type %T)", l, t); goto error; } break; } typecheckaste(OCALL, getinargx(t), n->list, "function argument"); ok |= Etop; if(t->outtuple == 0) goto ret; ok |= Erv; if(t->outtuple == 1) { t = getoutargx(l->type)->type; if(t == T) goto error; if(t->etype == TFIELD) t = t->type; n->type = t; goto ret; } // multiple return if(!(top & (Efnstruct | Etop))) { yyerror("multiple-value %#N() in single-value context", l); goto ret; } n->type = getoutargx(l->type); goto ret; case OCAP: case OLEN: ok |= Erv; if(onearg(n) < 0) goto error; typecheck(&n->left, Erv); defaultlit(&n->left, T); implicitstar(&n->left); l = n->left; if((t = l->type) == T) goto error; switch(n->op) { case OCAP: if(!okforcap[t->etype]) goto badcall1; break; case OLEN: if(!okforlen[t->etype]) goto badcall1; break; } // might be constant switch(t->etype) { case TSTRING: if(isconst(l, CTSTR)) nodconst(n, types[TINT], l->val.u.sval->len); break; case TARRAY: if(t->bound >= 0) nodconst(n, types[TINT], t->bound); break; } n->type = types[TINT]; goto ret; case OCLOSED: case OCLOSE: if(onearg(n) < 0) goto error; typecheck(&n->left, Erv); defaultlit(&n->left, T); l = n->left; if((t = l->type) == T) goto error; if(t->etype != TCHAN) { yyerror("invalid operation: %#N (non-chan type %T)", n, t); goto error; } if(n->op == OCLOSED) { n->type = types[TBOOL]; ok |= Erv; } else ok |= Etop; goto ret; case OCOPY: ok |= Etop|Erv; args = n->list; if(args == nil || args->next == nil) { yyerror("missing arguments to copy"); goto error; } if(args->next->next != nil) { yyerror("too many arguments to copy"); goto error; } n->left = args->n; n->right = args->next->n; n->type = types[TINT]; typecheck(&n->left, Erv); typecheck(&n->right, Erv); if(n->left->type == T || n->right->type == T) goto error; toslice(&n->left); toslice(&n->right); if(!isslice(n->left->type) || !isslice(n->right->type)) { yyerror("arguments to copy must be slices or array pointers"); goto error; } if(!eqtype(n->left->type, n->right->type)) { yyerror("arguments to copy must have the same type element type"); goto error; } goto ret; case OCONV: doconv: ok |= Erv; typecheck(&n->left, Erv | (top & Eindir)); convlit1(&n->left, n->type, 1); if((t = n->left->type) == T || n->type == T) goto error; n = typecheckconv(n, n->left, n->type, 1, "conversion"); if(n->type == T) goto error; goto ret; case OMAKE: ok |= Erv; args = n->list; if(args == nil) { yyerror("missing argument to make"); goto error; } l = args->n; args = args->next; typecheck(&l, Etype); if((t = l->type) == T) goto error; switch(t->etype) { default: badmake: yyerror("cannot make type %T", t); goto error; case TARRAY: if(!isslice(t)) goto badmake; if(args == nil) { yyerror("missing len argument to make(%T)", t); goto error; } l = args->n; args = args->next; typecheck(&l, Erv); defaultlit(&l, types[TUINT]); r = N; if(args != nil) { r = args->n; args = args->next; typecheck(&r, Erv); defaultlit(&r, types[TUINT]); } if(l->type == T || (r && r->type == T)) goto error; if(!isint[l->type->etype]) { yyerror("non-integer len argument to make(%T)", t); goto error; } if(r && !isint[r->type->etype]) { yyerror("non-integer cap argument to make(%T)", t); goto error; } if(r == N) r = nodintconst(0); n->left = l; n->right = r; n->op = OMAKESLICE; break; case TMAP: if(args != nil) { l = args->n; args = args->next; typecheck(&l, Erv); defaultlit(&l, types[TUINT]); if(l->type == T) goto error; if(!isint[l->type->etype]) { yyerror("non-integer size argument to make(%T)", t); goto error; } n->left = l; } else n->left = nodintconst(0); n->op = OMAKEMAP; break; case TCHAN: l = N; if(args != nil) { l = args->n; args = args->next; typecheck(&l, Erv); defaultlit(&l, types[TUINT]); if(l->type == T) goto error; if(!isint[l->type->etype]) { yyerror("non-integer buffer argument to make(%T)", t); goto error; } n->left = l; } else n->left = nodintconst(0); n->op = OMAKECHAN; break; } if(args != nil) { yyerror("too many arguments to make(%T)", t); n->op = OMAKE; goto error; } n->type = t; goto ret; case ONEW: ok |= Erv; args = n->list; if(args == nil) { yyerror("missing argument to new"); goto error; } l = args->n; typecheck(&l, Etype); if((t = l->type) == T) goto error; if(args->next != nil) { yyerror("too many arguments to new(%T)", t); goto error; } n->left = l; n->type = ptrto(t); goto ret; case OPANIC: case OPANICN: case OPRINT: case OPRINTN: ok |= Etop; typechecklist(n->list, Erv); goto ret; case OCLOSURE: ok |= Erv; typecheckclosure(n); if(n->type == T) goto error; goto ret; /* * statements */ case OAS: ok |= Etop; typecheckas(n); goto ret; case OAS2: ok |= Etop; typecheckas2(n); goto ret; case OBREAK: case OCONTINUE: case ODCL: case OEMPTY: case OGOTO: case OLABEL: case OXFALL: ok |= Etop; goto ret; case ODEFER: case OPROC: ok |= Etop; typecheck(&n->left, Etop); goto ret; case OFOR: ok |= Etop; typechecklist(n->ninit, Etop); typecheck(&n->ntest, Erv); if(n->ntest != N && (t = n->ntest->type) != T && t->etype != TBOOL) yyerror("non-bool %+N used as for condition", n->ntest); typecheck(&n->nincr, Etop); typechecklist(n->nbody, Etop); goto ret; case OIF: ok |= Etop; typechecklist(n->ninit, Etop); typecheck(&n->ntest, Erv); if(n->ntest != N && (t = n->ntest->type) != T && t->etype != TBOOL) yyerror("non-bool %+N used as if condition", n->ntest); typechecklist(n->nbody, Etop); typechecklist(n->nelse, Etop); goto ret; case ORETURN: ok |= Etop; typechecklist(n->list, Erv | Efnstruct); if(curfn->type->outnamed && n->list == nil) goto ret; typecheckaste(ORETURN, getoutargx(curfn->type), n->list, "return argument"); goto ret; case OSELECT: ok |= Etop; typecheckselect(n); goto ret; case OSWITCH: ok |= Etop; typecheckswitch(n); goto ret; case ORANGE: ok |= Etop; typecheckrange(n); goto ret; case OTYPECASE: ok |= Etop | Erv; typecheck(&n->left, Erv); goto ret; case OTYPESW: yyerror("use of .(type) outside type switch"); goto error; case OXCASE: ok |= Etop; typechecklist(n->list, Erv); typechecklist(n->nbody, Etop); goto ret; case ODCLFUNC: ok |= Etop; typecheckfunc(n); goto ret; case ODCLCONST: ok |= Etop; typecheck(&n->left, Erv); goto ret; case ODCLTYPE: ok |= Etop; typecheck(&n->left, Etype); if(!incannedimport) checkwidth(n->left->type); goto ret; } ret: t = n->type; if(t && !t->funarg && n->op != OTYPE) { switch(t->etype) { case TFUNC: // might have TANY; wait until its called case TANY: case TFORW: case TIDEAL: case TNIL: case TBLANK: break; case TARRAY: if(t->bound == -100) { yyerror("use of [...] array outside of array literal"); t->bound = 1; } default: checkwidth(t); } } evconst(n); if(n->op == OTYPE && !(top & Etype)) { yyerror("type %T is not an expression", n->type); goto error; } if((top & (Erv|Etype)) == Etype && n->op != OTYPE) { yyerror("%#N is not a type", n); goto error; } if((ok & Ecall) && !(top & Ecall)) { yyerror("must call %#N", n); goto error; } // TODO(rsc): simplify if((top & (Ecall|Erv|Etype)) && !(top & Etop) && !(ok & (Erv|Etype|Ecall))) { yyerror("%#N used as value", n); goto error; } if((top & Etop) && !(top & (Ecall|Erv|Etype)) && !(ok & Etop)) { yyerror("%#N not used", n); goto error; } /* TODO if(n->type == T) fatal("typecheck nil type"); */ goto out; badcall1: yyerror("invalid argument %#N (type %T) for %#O", n->left, n->left->type, n->op); goto error; error: n->type = T; out: lineno = lno; n->typecheck = 1; *np = n; return n; } static 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); typecheck(&n, Erv); *nn = n; } static void toslice(Node **nn) { Node *n; Type *t; n = *nn; if(n->type == T) return; if(isptr[n->type->etype] && isfixedarray(n->type->type)) { // convert to slice t = typ(TARRAY); t->bound = -1; t->type = n->type->type->type; n = typecheckconv(nil, n, t, 0, "conversion of array pointer to slice"); *nn = n; } } static int onearg(Node *n) { if(n->left != N) return 0; if(n->list == nil) { yyerror("missing argument to %#O - %#N", n->op, n); return -1; } n->left = n->list->n; if(n->list->next != nil) { yyerror("too many arguments to %#O", n->op); n->list = nil; return -1; } n->list = nil; return 0; } static 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; } static int lookdot(Node *n, Type *t) { Type *f1, *f2, *tt, *rcvr; Sym *s; s = n->right->sym; dowidth(t); 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 typecheck(&n->left, Erv); } n->op = ODOTINTER; } return 1; } if(f2 != T) { tt = n->left->type; dowidth(tt); rcvr = getthisx(f2->type)->type->type; if(!eqtype(rcvr, tt)) { if(rcvr->etype == tptr && eqtype(rcvr->type, tt)) { typecheck(&n->left, Erv); checklvalue(n->left, "call pointer method on"); addrescapes(n->left); n->left = nod(OADDR, n->left, N); typecheck(&n->left, Erv); } else if(tt->etype == tptr && eqtype(tt->type, rcvr)) { n->left = nod(OIND, n->left, N); typecheck(&n->left, Erv); } 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; } static int nokeys(NodeList *l) { for(; l; l=l->next) if(l->n->op == OKEY) return 0; return 1; } /* * check implicit or explicit conversion from node type nt to type t. */ int checkconv(Type *nt, Type *t, int explicit, int *op, int *et, char *desc) { *op = OCONV; *et = 0; // preexisting error if(t == T || t->etype == TFORW) return 0; /* * implicit conversions */ if(nt == T) return 0; if(t->etype == TBLANK) { *op = OCONVNOP; return 0; } if(eqtype(t, nt)) { exportassignok(t, desc); *op = OCONVNOP; if(!explicit || t == nt) return 0; return 1; } // interfaces are not subject to the name restrictions below. // accept anything involving interfaces and let ifacecvt // generate a good message. some messages have to be // delayed anyway. // TODO(rsc): now that everything is delayed for whole-package // compilation, the messages could be generated right here. if(isnilinter(t) || isnilinter(nt) || isinter(t) || isinter(nt)) { *et = ifaceas1(t, nt, 0); *op = OCONVIFACE; return 1; } // otherwise, if concrete types have names, they must match. if(!explicit && t->sym && nt->sym && t != nt) return -1; // channel must not lose directionality if(t->etype == TCHAN && nt->etype == TCHAN) { if(t->chan & ~nt->chan) return -1; if(eqtype(t->type, nt->type)) { *op = OCONVNOP; return 1; } } // array to slice if(isslice(t) && isptr[nt->etype] && isfixedarray(nt->type) && eqtype(t->type, nt->type->type)) { *op = OCONVSLICE; return 1; } /* * explicit conversions */ if(!explicit) return -1; // same representation if(cvttype(t, nt)) { *op = OCONVNOP; return 1; } // simple fix-float if(isint[t->etype] || isfloat[t->etype]) if(isint[nt->etype] || isfloat[nt->etype]) return 1; // to string if(istype(t, TSTRING)) { // integer rune if(isint[nt->etype]) { *op = ORUNESTR; return 1; } // *[10]byte -> string // in preparation for next step if(isptr[nt->etype] && isfixedarray(nt->type)) { switch(nt->type->type->etype) { case TUINT8: *op = OARRAYBYTESTR; return 1; case TINT: *op = OARRAYRUNESTR; return 1; } } // []byte -> string if(isslice(nt)) { switch(nt->type->etype) { case TUINT8: *op = OARRAYBYTESTR; return 1; case TINT: *op = OARRAYRUNESTR; return 1; } } } // convert to unsafe pointer if(isptrto(t, TANY) && (isptr[nt->etype] || nt->etype == TUINTPTR)) return 1; // convert from unsafe pointer if(isptrto(nt, TANY) && (isptr[t->etype] || t->etype == TUINTPTR)) return 1; return -1; } Node* typecheckconv(Node *nconv, Node *n, Type *t, int explicit, char *desc) { int et, op; Node *n1; char *prefix; convlit1(&n, t, explicit); if(n->type == T) return n; if(n->op == OLITERAL) if(explicit || isideal(n->type)) if(cvttype(t, n->type)) { // can convert literal in place // TODO(rsc) is this needed? n1 = nod(OXXX, N, N); *n1 = *n; n1->type = t; return n1; } prefix = ""; if(desc != nil) prefix = " in "; else desc = ""; switch(checkconv(n->type, t, explicit, &op, &et, desc)) { case -1: if(explicit) yyerror("cannot convert %+N to type %T%s%s", n, t, prefix, desc); else yyerror("cannot use %+N as type %T%s%s", n, t, prefix, desc); return n; case 0: if(nconv) { nconv->op = OCONVNOP; return nconv; } return n; } if(op == OCONVIFACE) defaultlit(&n, T); if(nconv == N) nconv = nod(OCONV, n, N); nconv->op = op; nconv->etype = et; nconv->type = t; nconv->typecheck = 1; return nconv; } /* * typecheck assignment: type list = expression list */ static void typecheckaste(int op, Type *tstruct, NodeList *nl, char *desc) { Type *t, *tl, *tn; Node *n; int lno; lno = lineno; if(tstruct->broke) goto out; if(nl != nil && nl->next == nil && (n = nl->n)->type != T) if(n->type->etype == TSTRUCT && n->type->funarg) { setlineno(n); tn = n->type->type; for(tl=tstruct->type; tl; tl=tl->down) { int xx, yy; if(tn == T) { yyerror("not enough arguments to %#O", op); goto out; } if(isddd(tl->type)) goto out; if(checkconv(tn->type, tl->type, 0, &xx, &yy, desc) < 0) yyerror("cannot use type %T as type %T in %s", tn->type, tl->type, desc); tn = tn->down; } if(tn != T) yyerror("too many arguments to %#O", op); goto out; } for(tl=tstruct->type; tl; tl=tl->down) { t = tl->type; if(isddd(t)) { for(; nl; nl=nl->next) { setlineno(nl->n); defaultlit(&nl->n, T); } goto out; } if(nl == nil) { yyerror("not enough arguments to %#O", op); goto out; } n = nl->n; setlineno(nl->n); if(n->type != T) nl->n = typecheckconv(nil, n, t, 0, desc); nl = nl->next; } if(nl != nil) { yyerror("too many arguments to %#O", op); goto out; } out: lineno = lno; } /* * do the export rules allow writing to this type? * cannot be implicitly assigning to any type with * an unavailable field. */ static int exportassignok(Type *t, char *desc) { Type *f; Sym *s; if(t == T) return 1; if(t->trecur) return 1; t->trecur = 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) { char *prefix; prefix = ""; if(desc != nil) prefix = " in "; else desc = ""; yyerror("implicit assignment of %T field '%s'%s%s", t, s->name, prefix, desc); goto no; } if(!exportassignok(f->type, desc)) goto no; } break; case TARRAY: if(t->bound < 0) // slices are pointers; that's fine break; if(!exportassignok(t->type, desc)) goto no; break; } t->trecur = 0; return 1; no: t->trecur = 0; return 0; } /* * type check composite */ 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; } 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; } 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; } static void typecheckcomplit(Node **np) { int bad, i, len, nerr; Node *l, *n, *hash[101]; NodeList *ll; Type *t, *f; Sym *s; n = *np; memset(hash, 0, sizeof hash); l = typecheck(&n->right /* sic */, Etype /* TODO | Edotarray */); if((t = l->type) == T) goto error; nerr = nerrors; switch(t->etype) { default: yyerror("invalid type for composite literal: %T", t); n->type = T; break; case TARRAY: len = 0; i = 0; for(ll=n->list; ll; ll=ll->next) { l = ll->n; if(l->op == OKEY) { typecheck(&l->left, Erv); evconst(l->left); i = nonnegconst(l->left); if(i < 0) { yyerror("array index must be non-negative integer constant"); i = -(1<<30); // stay negative for a while } typecheck(&l->right, Erv); defaultlit(&l->right, t->type); l->right = typecheckconv(nil, l->right, t->type, 0, "array index"); } else { typecheck(&ll->n, Erv); defaultlit(&ll->n, t->type); ll->n = typecheckconv(nil, ll->n, t->type, 0, "array index"); ll->n = nod(OKEY, nodintconst(i), ll->n); ll->n->left->type = types[TINT]; ll->n->left->typecheck = 1; } if(i >= 0) indexdup(ll->n->left, hash, nelem(hash)); i++; if(i > len) { len = i; if(t->bound >= 0 && len > t->bound) { setlineno(l); yyerror("array index %d out of bounds [0:%d]", len, t->bound); t->bound = -1; // no more errors } } } if(t->bound == -100) t->bound = len; if(t->bound < 0) n->right = nodintconst(len); n->op = OARRAYLIT; break; case TMAP: for(ll=n->list; ll; ll=ll->next) { l = ll->n; if(l->op != OKEY) { typecheck(&ll->n, Erv); yyerror("missing key in map literal"); continue; } typecheck(&l->left, Erv); typecheck(&l->right, Erv); defaultlit(&l->left, t->down); defaultlit(&l->right, t->type); l->left = typecheckconv(nil, l->left, t->down, 0, "map key"); l->right = typecheckconv(nil, l->right, t->type, 0, "map value"); keydup(l->left, hash, nelem(hash)); } n->op = OMAPLIT; break; case TSTRUCT: bad = 0; if(n->list != nil && nokeys(n->list)) { // simple list of variables f = t->type; for(ll=n->list; ll; ll=ll->next) { typecheck(&ll->n, Erv); if(f == nil) { if(!bad++) yyerror("too many values in struct initializer"); continue; } s = f->sym; if(s != nil && !exportname(s->name) && strcmp(package, s->package) != 0) yyerror("implicit assignment of %T field '%s' in struct literal", t, s->name); ll->n = typecheckconv(nil, ll->n, f->type, 0, "field value"); ll->n = nod(OKEY, newname(f->sym), ll->n); ll->n->left->typecheck = 1; f = f->down; } if(f != nil) yyerror("too few values in struct initializer"); } else { // keyed list for(ll=n->list; ll; ll=ll->next) { l = ll->n; if(l->op != OKEY) { if(!bad++) yyerror("mixture of field:value and value initializers"); typecheck(&ll->n, Erv); continue; } s = l->left->sym; if(s == S) { yyerror("invalid field name %#N in struct initializer", l->left); typecheck(&l->right, Erv); continue; } l->left = newname(s); l->left->typecheck = 1; f = lookdot1(s, t, t->type); typecheck(&l->right, Erv); if(f == nil) { yyerror("unknown %T field '%s' in struct literal", t, s->name); continue; } s = f->sym; fielddup(newname(s), hash, nelem(hash)); l->right = typecheckconv(nil, l->right, f->type, 0, "field value"); } } n->op = OSTRUCTLIT; break; } if(nerr != nerrors) goto error; n->type = t; *np = n; return; error: n->type = T; *np = n; } /* * 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. */ static 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. // the function type has already been checked // (we're in the function body) // so the param already has a valid xoffset. 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; if(n->xoffset == BADWIDTH) fatal("addrescapes before param assignment"); n->stackparam->xoffset = n->xoffset; n->xoffset = 0; } 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(ONAME, N, N); n->heapaddr->type = ptrto(n->type); snprint(buf, sizeof buf, "&%S", n->sym); n->heapaddr->sym = lookup(buf); n->heapaddr->class = PHEAP-1; // defer tempname to allocparams curfn->dcl = list(curfn->dcl, n->heapaddr); 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; } } /* * lvalue etc */ static int islvalue(Node *n) { switch(n->op) { case OINDEX: case OIND: case ODOTPTR: return 1; case ODOT: return islvalue(n->left); case ONAME: if(n->class == PFUNC) return 0; return 1; } return 0; } static void checklvalue(Node *n, char *verb) { if(!islvalue(n)) yyerror("cannot %s %#N", verb, n); } static void checkassign(Node *n) { if(islvalue(n)) return; if(n->op == OINDEXMAP) { n->etype = 1; return; } yyerror("cannot assign to %#N", n); } static void checkassignlist(NodeList *l) { for(; l; l=l->next) checkassign(l->n); } /* * type check assignment. * if this assignment is the definition of a var on the left side, * fill in the var's type. */ static void typecheckas(Node *n) { // delicate little dance. // the definition of n may refer to this assignment // as its definition, in which case it will call typecheckas. // in that case, do not call typecheck back, or it will cycle. // if the variable has a type (ntype) then typechecking // will not look at defn, so it is okay (and desirable, // so that the conversion below happens). if(n->left->defn != n || n->left->ntype) typecheck(&n->left, Erv | Easgn); checkassign(n->left); typecheck(&n->right, Erv); if(n->right && n->right->type != T) { if(n->left->type != T) n->right = typecheckconv(nil, n->right, n->left->type, 0, "assignment"); else exportassignok(n->right->type, "assignment"); } if(n->left->defn == n && n->left->ntype == N) { defaultlit(&n->right, T); n->left->type = n->right->type; } // second half of dance. // now that right is done, typecheck the left // just to get it over with. see dance above. n->typecheck = 1; if(n->left->typecheck == 0) typecheck(&n->left, Erv | Easgn); } static void typecheckas2(Node *n) { int cl, cr, op, et; NodeList *ll, *lr; Node *l, *r; Iter s; Type *t; for(ll=n->list; ll; ll=ll->next) { // delicate little dance. if(ll->n->defn != n || ll->n->ntype) typecheck(&ll->n, Erv | Easgn); } cl = count(n->list); cr = count(n->rlist); checkassignlist(n->list); if(cl > 1 && cr == 1) typecheck(&n->rlist->n, Erv | Efnstruct); else typechecklist(n->rlist, Erv); if(cl == cr) { // easy for(ll=n->list, lr=n->rlist; ll; ll=ll->next, lr=lr->next) { if(ll->n->type != T && lr->n->type != T) lr->n = typecheckconv(nil, lr->n, ll->n->type, 0, nil); if(ll->n->defn == n && ll->n->ntype == N) { defaultlit(&lr->n, T); ll->n->type = lr->n->type; } } goto out; } l = n->list->n; r = n->rlist->n; // m[i] = x, ok if(cl == 1 && cr == 2 && l->op == OINDEXMAP) { if(l->type == T) goto out; n->op = OAS2MAPW; n->rlist->n = typecheckconv(nil, r, l->type, 0, nil); r = n->rlist->next->n; n->rlist->next->n = typecheckconv(nil, r, types[TBOOL], 0, nil); goto out; } // x,y,z = f() if(cr == 1) { if(r->type == T) goto out; switch(r->op) { case OCALLMETH: case OCALLINTER: case OCALLFUNC: if(r->type->etype != TSTRUCT || r->type->funarg == 0) break; cr = structcount(r->type); if(cr != cl) goto mismatch; n->op = OAS2FUNC; t = structfirst(&s, &r->type); for(ll=n->list; ll; ll=ll->next) { if(ll->n->type != T) if(checkconv(t->type, ll->n->type, 0, &op, &et, nil) < 0) yyerror("cannot assign type %T to %+N", t->type, ll->n); if(ll->n->defn == n && ll->n->ntype == N) ll->n->type = t->type; t = structnext(&s); } goto out; } } // x, ok = y if(cl == 2 && cr == 1) { if(r->type == T) goto out; switch(r->op) { case OINDEXMAP: n->op = OAS2MAPR; goto common; case ORECV: n->op = OAS2RECV; goto common; case ODOTTYPE: n->op = OAS2DOTTYPE; common: if(l->type != T && checkconv(r->type, l->type, 0, &op, &et, nil) < 0) yyerror("cannot assign %+N to %+N", r, l); if(l->defn == n) l->type = r->type; l = n->list->next->n; if(l->type != T && checkconv(types[TBOOL], l->type, 0, &op, &et, nil) < 0) yyerror("cannot assign bool value to %+N", l); if(l->defn == n && l->ntype == N) l->type = types[TBOOL]; goto out; } } mismatch: yyerror("assignment count mismatch: %d = %d", cl, cr); out: // second half of dance n->typecheck = 1; for(ll=n->list; ll; ll=ll->next) if(ll->n->typecheck == 0) typecheck(&ll->n, Erv | Easgn); } /* * type check function definition */ static void typecheckfunc(Node *n) { Type *t, *rcvr; //dump("nname", n->nname); typecheck(&n->nname, Erv | Easgn); if((t = n->nname->type) == T) return; n->type = t; rcvr = getthisx(t)->type; if(rcvr != nil && n->shortname != N && !isblank(n->shortname)) addmethod(n->shortname->sym, t, 1); }