// 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" #include "y.tab.h" static void funcargs(Node*); static int dflag(void) { if(!debug['d']) return 0; if(debug['y']) return 1; if(incannedimport) return 0; return 1; } /* * declaration stack & operations */ static Sym* dclstack; static void dcopy(Sym *a, Sym *b) { a->pkg = b->pkg; a->name = b->name; a->def = b->def; a->block = b->block; a->lastlineno = b->lastlineno; } static Sym* push(void) { Sym *d; d = mal(sizeof(*d)); d->link = dclstack; dclstack = d; return d; } static Sym* pushdcl(Sym *s) { Sym *d; d = push(); dcopy(d, s); if(dflag()) print("\t%L push %S %p\n", lineno, s, s->def); return d; } void popdcl(void) { Sym *d, *s; // if(dflag()) // print("revert\n"); for(d=dclstack; d!=S; d=d->link) { if(d->name == nil) break; s = pkglookup(d->name, d->pkg); dcopy(s, d); if(dflag()) print("\t%L pop %S %p\n", lineno, s, s->def); } if(d == S) fatal("popdcl: no mark"); dclstack = d->link; block = d->block; } void poptodcl(void) { Sym *d, *s; for(d=dclstack; d!=S; d=d->link) { if(d->name == nil) break; s = pkglookup(d->name, d->pkg); dcopy(s, d); if(dflag()) print("\t%L pop %S\n", lineno, s); } if(d == S) fatal("poptodcl: no mark"); dclstack = d; } void markdcl(void) { Sym *d; d = push(); d->name = nil; // used as a mark in fifo d->block = block; blockgen++; block = blockgen; // if(dflag()) // print("markdcl\n"); } void dumpdcl(char *st) { Sym *s, *d; int i; i = 0; for(d=dclstack; d!=S; d=d->link) { i++; print(" %.2d %p", i, d); if(d->name == nil) { print("\n"); continue; } print(" '%s'", d->name); s = pkglookup(d->name, d->pkg); print(" %lS\n", s); } } void testdclstack(void) { Sym *d; for(d=dclstack; d!=S; d=d->link) { if(d->name == nil) { yyerror("mark left on the stack"); continue; } } } void redeclare(Sym *s, char *where) { if(s->lastlineno == 0) yyerror("%S redeclared %s\n" "\tprevious declaration during import", s, where); else yyerror("%S redeclared %s\n" "\tprevious declaration at %L", s, where, s->lastlineno); } /* * declare individual names - var, typ, const */ void declare(Node *n, int ctxt) { Sym *s; int gen; static int typegen, vargen; if(isblank(n)) return; n->lineno = parserline(); s = n->sym; gen = 0; if(ctxt == PEXTERN) { externdcl = list(externdcl, n); if(dflag()) print("\t%L global decl %S %p\n", lineno, s, n); } else { if(curfn == nil && ctxt == PAUTO) fatal("automatic outside function"); if(curfn != nil) curfn->dcl = list(curfn->dcl, n); if(n->op == OTYPE) gen = ++typegen; else if(n->op == ONAME) gen = ++vargen; pushdcl(s); } if(ctxt == PAUTO) n->xoffset = BADWIDTH; if(s->block == block) redeclare(s, "in this block"); s->block = block; s->lastlineno = parserline(); s->def = n; n->vargen = gen; n->funcdepth = funcdepth; n->class = ctxt; autoexport(n, ctxt); } void addvar(Node *n, Type *t, int ctxt) { if(n==N || n->sym == S || (n->op != ONAME && n->op != ONONAME) || t == T) fatal("addvar: n=%N t=%T nil", n, t); n->op = ONAME; declare(n, ctxt); n->type = t; } /* * declare variables from grammar * new_name_list (type | [type] = expr_list) */ NodeList* variter(NodeList *vl, Node *t, NodeList *el) { int doexpr; Node *v, *e, *as2; NodeList *init; init = nil; doexpr = el != nil; if(count(el) == 1 && count(vl) > 1) { e = el->n; as2 = nod(OAS2, N, N); as2->list = vl; as2->rlist = list1(e); for(; vl; vl=vl->next) { v = vl->n; v->op = ONAME; declare(v, dclcontext); v->ntype = t; v->defn = as2; if(funcdepth > 0) init = list(init, nod(ODCL, v, N)); } return list(init, as2); } for(; vl; vl=vl->next) { if(doexpr) { if(el == nil) { yyerror("missing expr in var dcl"); break; } e = el->n; el = el->next; } else e = N; v = vl->n; v->op = ONAME; declare(v, dclcontext); v->ntype = t; if(e != N || funcdepth > 0 || isblank(v)) { if(funcdepth > 0) init = list(init, nod(ODCL, v, N)); e = nod(OAS, v, e); init = list(init, e); if(e->right != N) v->defn = e; } } if(el != nil) yyerror("extra expr in var dcl"); return init; } /* * declare constants from grammar * new_name_list [[type] = expr_list] */ NodeList* constiter(NodeList *vl, Node *t, NodeList *cl) { Node *v, *c; NodeList *vv; vv = nil; if(cl == nil) { if(t != N) yyerror("constdcl cannot have type without expr"); cl = lastconst; t = lasttype; } else { lastconst = cl; lasttype = t; } cl = listtreecopy(cl); for(; vl; vl=vl->next) { if(cl == nil) { yyerror("missing expr in const dcl"); break; } c = cl->n; cl = cl->next; v = vl->n; v->op = OLITERAL; declare(v, dclcontext); v->ntype = t; v->defn = c; vv = list(vv, nod(ODCLCONST, v, N)); } if(cl != nil) yyerror("extra expr in const dcl"); iota += 1; return vv; } /* * this generates a new name node, * typically for labels or other one-off names. */ Node* newname(Sym *s) { Node *n; if(s == S) fatal("newname nil"); n = nod(ONAME, N, N); n->sym = s; n->type = T; n->addable = 1; n->ullman = 1; n->xoffset = 0; return n; } /* * this generates a new name node for a name * being declared. */ Node* dclname(Sym *s) { Node *n; n = newname(s); n->op = ONONAME; // caller will correct it return n; } Node* typenod(Type *t) { // if we copied another type with *t = *u // then t->nod might be out of date, so // check t->nod->type too if(t->nod == N || t->nod->type != t) { t->nod = nod(OTYPE, N, N); t->nod->type = t; t->nod->sym = t->sym; } return t->nod; } /* * this will return an old name * that has already been pushed on the * declaration list. a diagnostic is * generated if no name has been defined. */ Node* oldname(Sym *s) { Node *n; Node *c; n = s->def; if(n == N) { // maybe a top-level name will come along // to give this a definition later. // walkdef will check s->def again once // all the input source has been processed. n = newname(s); n->op = ONONAME; n->iota = iota; // save current iota value in const declarations } if(curfn != nil && n->funcdepth > 0 && n->funcdepth != funcdepth && n->op == ONAME) { // inner func is referring to var in outer func. // // TODO(rsc): If there is an outer variable x and we // are parsing x := 5 inside the closure, until we get to // the := it looks like a reference to the outer x so we'll // make x a closure variable unnecessarily. if(n->closure == N || n->closure->funcdepth != funcdepth) { // create new closure var. c = nod(ONAME, N, N); c->sym = s; c->class = PPARAMREF; c->isddd = n->isddd; c->defn = n; c->addable = 0; c->ullman = 2; c->funcdepth = funcdepth; c->outer = n->closure; n->closure = c; c->closure = n; c->xoffset = 0; curfn->cvars = list(curfn->cvars, c); } // return ref to closure var, not original return n->closure; } return n; } /* * same for types */ Type* newtype(Sym *s) { Type *t; t = typ(TFORW); t->sym = s; t->type = T; return t; } /* * type check top level declarations */ void dclchecks(void) { NodeList *l; for(l=externdcl; l; l=l->next) { if(l->n->op != ONAME) continue; typecheck(&l->n, Erv); } } /* * := declarations */ static int colasname(Node *n) { switch(n->op) { case ONAME: case ONONAME: case OPACK: case OTYPE: case OLITERAL: return n->sym != S; } return 0; } void colasdefn(NodeList *left, Node *defn) { int nnew; NodeList *l; Node *n; nnew = 0; for(l=left; l; l=l->next) { n = l->n; if(isblank(n)) continue; if(!colasname(n)) { yyerror("non-name %#N on left side of :=", n); continue; } if(n->sym->block == block) continue; nnew++; n = newname(n->sym); declare(n, dclcontext); n->defn = defn; defn->ninit = list(defn->ninit, nod(ODCL, n, N)); l->n = n; } if(nnew == 0) yyerror("no new variables on left side of :="); } Node* colas(NodeList *left, NodeList *right) { Node *as; as = nod(OAS2, N, N); as->list = left; as->rlist = right; as->colas = 1; colasdefn(left, as); // make the tree prettier; not necessary if(count(left) == 1 && count(right) == 1) { as->left = as->list->n; as->right = as->rlist->n; as->list = nil; as->rlist = nil; as->op = OAS; } return as; } /* * declare the function proper * and declare the arguments. * called in extern-declaration context * returns in auto-declaration context. */ void funchdr(Node *n) { if(n->nname != N) { n->nname->op = ONAME; declare(n->nname, PFUNC); n->nname->defn = n; } // change the declaration context from extern to auto if(funcdepth == 0 && dclcontext != PEXTERN) fatal("funchdr: dclcontext"); dclcontext = PAUTO; markdcl(); funcdepth++; n->outer = curfn; curfn = n; if(n->nname) funcargs(n->nname->ntype); else funcargs(n->ntype); } static void funcargs(Node *nt) { Node *n; NodeList *l; if(nt->op != OTFUNC) fatal("funcargs %O", nt->op); // declare the receiver and in arguments. // no n->defn because type checking of func header // will fill in the types before we can demand them. if(nt->left != N) { n = nt->left; if(n->op != ODCLFIELD) fatal("funcargs1 %O", n->op); if(n->left != N) { n->left->op = ONAME; n->left->ntype = n->right; declare(n->left, PPARAM); } } for(l=nt->list; l; l=l->next) { n = l->n; if(n->op != ODCLFIELD) fatal("funcargs2 %O", n->op); if(n->left != N) { n->left->op = ONAME; n->left->ntype = n->right; declare(n->left, PPARAM); } } // declare the out arguments. for(l=nt->rlist; l; l=l->next) { n = l->n; if(n->op != ODCLFIELD) fatal("funcargs3 %O", n->op); if(n->left != N) { n->left->op = ONAME; n->left->ntype = n->right; declare(n->left, PPARAMOUT); } } } /* * finish the body. * called in auto-declaration context. * returns in extern-declaration context. */ void funcbody(Node *n) { // change the declaration context from auto to extern if(dclcontext != PAUTO) fatal("funcbody: dclcontext"); popdcl(); funcdepth--; curfn = n->outer; n->outer = N; if(funcdepth == 0) dclcontext = PEXTERN; } /* * new type being defined with name s. */ Node* typedcl0(Sym *s) { Node *n; n = dclname(s); n->op = OTYPE; declare(n, dclcontext); return n; } /* * node n, which was returned by typedcl0 * is being declared to have uncompiled type t. * return the ODCLTYPE node to use. */ Node* typedcl1(Node *n, Node *t, int local) { n->ntype = t; n->local = local; return nod(ODCLTYPE, n, N); } /* * typedcl1 but during imports */ void typedcl2(Type *pt, Type *t) { Node *n; if(pt->etype == TFORW) goto ok; if(!eqtype(pt->orig, t)) yyerror("inconsistent definition for type %S during import\n\t%lT\n\t%lT", pt->sym, pt, t); return; ok: n = pt->nod; *pt = *t; pt->method = nil; pt->nod = n; pt->sym = n->sym; pt->sym->lastlineno = parserline(); declare(n, PEXTERN); checkwidth(pt); } /* * structs, functions, and methods. * they don't belong here, but where do they belong? */ /* * turn a parsed struct into a type */ static Type** stotype(NodeList *l, int et, Type **t) { Type *f, *t1, *t2, **t0; Strlit *note; int lno; NodeList *init; Node *n; char *what; t0 = t; init = nil; lno = lineno; what = "field"; if(et == TINTER) what = "method"; for(; l; l=l->next) { n = l->n; lineno = n->lineno; note = nil; if(n->op != ODCLFIELD) fatal("stotype: oops %N\n", n); if(n->right != N) { if(et == TINTER && n->left != N) { // queue resolution of method type for later. // right now all we need is the name list. // avoids cycles for recursive interface types. n->type = typ(TINTERMETH); n->type->nname = n->right; n->right = N; n->left->type = n->type; queuemethod(n); } else { typecheck(&n->right, Etype); n->type = n->right->type; if(n->type == T) { *t0 = T; return t0; } if(n->left != N) n->left->type = n->type; n->right = N; if(n->embedded && n->type != T) { t1 = n->type; if(t1->sym == S && isptr[t1->etype]) t1 = t1->type; if(isptr[t1->etype]) yyerror("embedded type cannot be a pointer"); else if(t1->etype == TFORW && t1->embedlineno == 0) t1->embedlineno = lineno; } } } if(n->type == T) { // assume error already printed continue; } switch(n->val.ctype) { case CTSTR: if(et != TSTRUCT) yyerror("interface method cannot have annotation"); note = n->val.u.sval; break; default: if(et != TSTRUCT) yyerror("interface method cannot have annotation"); else yyerror("field annotation must be string"); case CTxxx: note = nil; break; } if(et == TINTER && n->left == N) { // embedded interface - inline the methods if(n->type->etype != TINTER) { if(n->type->etype == TFORW) yyerror("interface type loop involving %T", n->type); else yyerror("interface contains embedded non-interface %T", n->type); continue; } for(t1=n->type->type; t1!=T; t1=t1->down) { f = typ(TFIELD); f->type = t1->type; f->width = BADWIDTH; f->nname = newname(t1->sym); f->sym = t1->sym; for(t2=*t0; t2!=T; t2=t2->down) { if(t2->sym == f->sym) { yyerror("duplicate method %s", t2->sym->name); break; } } *t = f; t = &f->down; } continue; } f = typ(TFIELD); f->type = n->type; f->note = note; f->width = BADWIDTH; f->isddd = n->isddd; if(n->left != N && n->left->op == ONAME) { f->nname = n->left; f->embedded = n->embedded; f->sym = f->nname->sym; if(importpkg && !exportname(f->sym->name)) f->sym = pkglookup(f->sym->name, structpkg); if(f->sym && !isblank(f->nname)) { for(t1=*t0; t1!=T; t1=t1->down) { if(t1->sym == f->sym) { yyerror("duplicate %s %s", what, t1->sym->name); break; } } } } *t = f; t = &f->down; } *t = T; lineno = lno; return t; } Type* dostruct(NodeList *l, int et) { Type *t; int funarg; /* * convert a parsed id/type list into * a type for struct/interface/arglist */ funarg = 0; if(et == TFUNC) { funarg = 1; et = TSTRUCT; } t = typ(et); t->funarg = funarg; stotype(l, et, &t->type); if(t->type == T && l != nil) { t->broke = 1; return t; } if(!funarg) checkwidth(t); return t; } Node* embedded(Sym *s) { Node *n; char *name; // Names sometimes have disambiguation junk // appended after a center dot. Discard it when // making the name for the embedded struct field. enum { CenterDot = 0xB7 }; name = s->name; if(utfrune(s->name, CenterDot)) { name = strdup(s->name); *utfrune(name, CenterDot) = 0; } n = newname(lookup(name)); n = nod(ODCLFIELD, n, oldname(s)); n->embedded = 1; return n; } /* * check that the list of declarations is either all anonymous or all named */ static Node* findtype(NodeList *l) { for(; l; l=l->next) if(l->n->op == OKEY) return l->n->right; return N; } NodeList* checkarglist(NodeList *all, int input) { int named; Node *n, *t, *nextt; NodeList *l; named = 0; for(l=all; l; l=l->next) { if(l->n->op == OKEY) { named = 1; break; } } if(named) { n = N; for(l=all; l; l=l->next) { n = l->n; if(n->op != OKEY && n->sym == S) { yyerror("mixed named and unnamed function parameters"); break; } } if(l == nil && n != N && n->op != OKEY) yyerror("final function parameter must have type"); } nextt = nil; for(l=all; l; l=l->next) { // can cache result from findtype to avoid // quadratic behavior here, but unlikely to matter. n = l->n; if(named) { if(n->op == OKEY) { t = n->right; n = n->left; nextt = nil; } else { if(nextt == nil) nextt = findtype(l); t = nextt; } } else { t = n; n = N; } if(isblank(n)) n = N; if(n != N && n->sym == S) { t = n; n = N; } if(n != N) n = newname(n->sym); n = nod(ODCLFIELD, n, t); if(n->right != N && n->right->op == ODDD) { if(!input) yyerror("cannot use ... in output argument list"); else if(l->next != nil) yyerror("can only use ... as final argument in list"); n->right->op = OTARRAY; n->right->right = n->right->left; n->right->left = N; n->isddd = 1; if(n->left != N) n->left->isddd = 1; } l->n = n; } return all; } Node* fakethis(void) { Node *n; n = nod(ODCLFIELD, N, typenod(ptrto(typ(TSTRUCT)))); return n; } /* * Is this field a method on an interface? * Those methods have an anonymous * *struct{} as the receiver. * (See fakethis above.) */ int isifacemethod(Type *f) { Type *rcvr; Type *t; rcvr = getthisx(f)->type; if(rcvr->sym != S) return 0; t = rcvr->type; if(!isptr[t->etype]) return 0; t = t->type; if(t->sym != S || t->etype != TSTRUCT || t->type != T) return 0; return 1; } /* * turn a parsed function declaration * into a type */ Type* functype(Node *this, NodeList *in, NodeList *out) { Type *t; NodeList *rcvr; t = typ(TFUNC); rcvr = nil; if(this) rcvr = list1(this); t->type = dostruct(rcvr, TFUNC); t->type->down = dostruct(out, TFUNC); t->type->down->down = dostruct(in, TFUNC); if(this) t->thistuple = 1; t->outtuple = count(out); t->intuple = count(in); t->outnamed = t->outtuple > 0 && out->n->left != N; return t; } Sym* methodsym(Sym *nsym, Type *t0, int iface) { Sym *s; char *p; Type *t; char *suffix; t = t0; if(t == T) goto bad; s = t->sym; if(s == S) { if(!isptr[t->etype]) goto bad; t = t->type; if(t == T) goto bad; s = t->sym; if(s == S) goto bad; } // if t0 == *t and t0 has a sym, // we want to see *t, not t0, in the method name. if(t != t0 && t0->sym) t0 = ptrto(t); suffix = ""; if(iface) { dowidth(t0); if(t0->width < types[tptr]->width) suffix = "·i"; } p = smprint("%#hT·%s%s", t0, nsym->name, suffix); s = pkglookup(p, s->pkg); free(p); return s; bad: yyerror("illegal receiver type: %T", t0); return S; } Node* methodname(Node *n, Type *t) { Sym *s; s = methodsym(n->sym, t, 0); if(s == S) return n; return newname(s); } Node* methodname1(Node *n, Node *t) { char *star; char *p; star = ""; if(t->op == OIND) { star = "*"; t = t->left; } if(t->sym == S || isblank(n)) return newname(n->sym); p = smprint("%s%S·%S", star, t->sym, n->sym); n = newname(pkglookup(p, t->sym->pkg)); free(p); return n; } /* * add a method, declared as a function, * n is fieldname, pa is base type, t is function type */ void addmethod(Sym *sf, Type *t, int local) { Type *f, *d, *pa; Node *n; pa = nil; // get field sym if(sf == S) fatal("no method symbol"); // get parent type sym pa = getthisx(t)->type; // ptr to this structure if(pa == T) { yyerror("missing receiver"); return; } pa = pa->type; f = methtype(pa); if(f == T) { yyerror("invalid receiver type %T", pa); return; } pa = f; if(importpkg && !exportname(sf->name)) sf = pkglookup(sf->name, importpkg); n = nod(ODCLFIELD, newname(sf), N); n->type = t; d = T; // last found for(f=pa->method; f!=T; f=f->down) { d = f; if(f->etype != TFIELD) fatal("addmethod: not TFIELD: %N", f); if(strcmp(sf->name, f->sym->name) != 0) continue; if(!eqtype(t, f->type)) yyerror("method redeclared: %T.%S\n\t%T\n\t%T", pa, sf, f->type, t); return; } if(local && !pa->local) { // defining method on non-local type. yyerror("cannot define new methods on non-local type %T", pa); return; } if(d == T) stotype(list1(n), 0, &pa->method); else stotype(list1(n), 0, &d->down); return; } void funccompile(Node *n, int isclosure) { stksize = BADWIDTH; maxarg = 0; if(n->type == T) { if(nerrors == 0) fatal("funccompile missing type"); return; } // assign parameter offsets checkwidth(n->type); // record offset to actual frame pointer. // for closure, have to skip over leading pointers and PC slot. nodfp->xoffset = 0; if(isclosure) { NodeList *l; for(l=n->nname->ntype->list; l; l=l->next) { nodfp->xoffset += widthptr; if(l->n->left == N) // found slot for PC break; } } if(curfn) fatal("funccompile %S inside %S", n->nname->sym, curfn->nname->sym); curfn = n; typechecklist(n->nbody, Etop); curfn = nil; stksize = 0; dclcontext = PAUTO; funcdepth = n->funcdepth + 1; compile(n); curfn = nil; funcdepth = 0; dclcontext = PEXTERN; }