| // 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 <u.h> |
| #include <libc.h> |
| #include "go.h" |
| #include "y.tab.h" |
| |
| static void funcargs(Node*); |
| static void funcargs2(Type*); |
| |
| static int |
| dflag(void) |
| { |
| if(!debug['d']) |
| return 0; |
| if(debug['y']) |
| return 1; |
| if(incannedimport) |
| return 0; |
| return 1; |
| } |
| |
| /* |
| * declaration stack & operations |
| */ |
| |
| 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->lastlineno = lineno; |
| 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; |
| int lno; |
| |
| // if(dflag()) |
| // print("revert\n"); |
| |
| for(d=dclstack; d!=S; d=d->link) { |
| if(d->name == nil) |
| break; |
| s = pkglookup(d->name, d->pkg); |
| lno = s->lastlineno; |
| dcopy(s, d); |
| d->lastlineno = lno; |
| 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) |
| { |
| // pop the old marker and push a new one |
| // (cannot reuse the existing one) |
| // because we use the markers to identify blocks |
| // for the goto restriction checks. |
| popdcl(); |
| markdcl(); |
| } |
| |
| 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; |
| |
| USED(st); |
| |
| 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(" %S\n", s); |
| } |
| } |
| |
| void |
| testdclstack(void) |
| { |
| Sym *d; |
| |
| for(d=dclstack; d!=S; d=d->link) { |
| if(d->name == nil) { |
| if(nerrors != 0) |
| errorexit(); |
| yyerror("mark left on the stack"); |
| continue; |
| } |
| } |
| } |
| |
| void |
| redeclare(Sym *s, char *where) |
| { |
| Strlit *pkgstr; |
| int line1, line2; |
| |
| if(s->lastlineno == 0) { |
| pkgstr = s->origpkg ? s->origpkg->path : s->pkg->path; |
| yyerror("%S redeclared %s\n" |
| "\tprevious declaration during import \"%Z\"", |
| s, where, pkgstr); |
| } else { |
| line1 = parserline(); |
| line2 = s->lastlineno; |
| |
| // When an import and a declaration collide in separate files, |
| // present the import as the "redeclared", because the declaration |
| // is visible where the import is, but not vice versa. |
| // See issue 4510. |
| if(s->def == N) { |
| line2 = line1; |
| line1 = s->lastlineno; |
| } |
| |
| yyerrorl(line1, "%S redeclared %s\n" |
| "\tprevious declaration at %L", |
| s, where, line2); |
| } |
| } |
| |
| static int vargen; |
| |
| /* |
| * declare individual names - var, typ, const |
| */ |
| void |
| declare(Node *n, int ctxt) |
| { |
| Sym *s; |
| int gen; |
| static int typegen; |
| |
| if(ctxt == PDISCARD) |
| return; |
| |
| if(isblank(n)) |
| return; |
| |
| n->lineno = parserline(); |
| s = n->sym; |
| |
| // kludgy: typecheckok means we're past parsing. Eg genwrapper may declare out of package names later. |
| if(importpkg == nil && !typecheckok && s->pkg != localpkg) |
| yyerror("cannot declare name %S", s); |
| |
| if(ctxt == PEXTERN && strcmp(s->name, "init") == 0) |
| yyerror("cannot declare init - must be func", s); |
| |
| 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 && ctxt == PAUTO && strstr(s->name, "·") == nil) |
| gen = ++vargen; |
| pushdcl(s); |
| n->curfn = curfn; |
| } |
| if(ctxt == PAUTO) |
| n->xoffset = 0; |
| |
| if(s->block == block) { |
| // functype will print errors about duplicate function arguments. |
| // Don't repeat the error here. |
| if(ctxt != PPARAM && ctxt != PPARAMOUT) |
| 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 expression in var declaration"); |
| 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 expression in var declaration"); |
| 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("const declaration cannot have type without expression"); |
| cl = lastconst; |
| t = lasttype; |
| } else { |
| lastconst = cl; |
| lasttype = t; |
| } |
| cl = listtreecopy(cl); |
| |
| for(; vl; vl=vl->next) { |
| if(cl == nil) { |
| yyerror("missing value in const declaration"); |
| 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 expression in const declaration"); |
| 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; |
| n->addrtaken = 1; |
| c->closure = n; |
| c->xoffset = 0; |
| curfn->cvars = list(curfn->cvars, c); |
| } |
| // return ref to closure var, not original |
| return n->closure; |
| } |
| return n; |
| } |
| |
| /* |
| * := 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, nerr; |
| NodeList *l; |
| Node *n; |
| |
| for(l=left; l; l=l->next) |
| if(l->n->sym != S) |
| l->n->sym->flags |= SymUniq; |
| |
| nnew = 0; |
| nerr = 0; |
| for(l=left; l; l=l->next) { |
| n = l->n; |
| if(isblank(n)) |
| continue; |
| if(!colasname(n)) { |
| yyerrorl(defn->lineno, "non-name %N on left side of :=", n); |
| nerr++; |
| continue; |
| } |
| if((n->sym->flags & SymUniq) == 0) { |
| yyerrorl(defn->lineno, "%S repeated on left side of :=", n->sym); |
| n->diag++; |
| nerr++; |
| continue; |
| } |
| n->sym->flags &= ~SymUniq; |
| 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 && nerr == 0) |
| yyerrorl(defn->lineno, "no new variables on left side of :="); |
| } |
| |
| Node* |
| colas(NodeList *left, NodeList *right, int32 lno) |
| { |
| Node *as; |
| |
| as = nod(OAS2, N, N); |
| as->list = left; |
| as->rlist = right; |
| as->colas = 1; |
| as->lineno = lno; |
| 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 arguments in an |
| * interface field declaration. |
| */ |
| void |
| ifacedcl(Node *n) |
| { |
| if(n->op != ODCLFIELD || n->right == N) |
| fatal("ifacedcl"); |
| |
| if(isblank(n->left)) |
| yyerror("methods must have a unique non-blank name"); |
| |
| dclcontext = PPARAM; |
| markdcl(); |
| funcdepth++; |
| n->outer = curfn; |
| curfn = n; |
| funcargs(n->right); |
| |
| // funcbody is normally called after the parser has |
| // seen the body of a function but since an interface |
| // field declaration does not have a body, we must |
| // call it now to pop the current declaration context. |
| dclcontext = PAUTO; |
| funcbody(n); |
| } |
| |
| /* |
| * declare the function proper |
| * and declare the arguments. |
| * called in extern-declaration context |
| * returns in auto-declaration context. |
| */ |
| void |
| funchdr(Node *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 if (n->ntype) |
| funcargs(n->ntype); |
| else |
| funcargs2(n->type); |
| } |
| |
| static void |
| funcargs(Node *nt) |
| { |
| Node *n, *nn; |
| NodeList *l; |
| int gen; |
| |
| if(nt->op != OTFUNC) |
| fatal("funcargs %O", nt->op); |
| |
| // re-start the variable generation number |
| // we want to use small numbers for the return variables, |
| // so let them have the chunk starting at 1. |
| vargen = count(nt->rlist); |
| |
| // declare the receiver and in arguments. |
| // no n->defn because type checking of func header |
| // will not fill in the types until later |
| if(nt->left != N) { |
| n = nt->left; |
| if(n->op != ODCLFIELD) |
| fatal("funcargs receiver %O", n->op); |
| if(n->left != N) { |
| n->left->op = ONAME; |
| n->left->ntype = n->right; |
| declare(n->left, PPARAM); |
| if(dclcontext == PAUTO) |
| n->left->vargen = ++vargen; |
| } |
| } |
| for(l=nt->list; l; l=l->next) { |
| n = l->n; |
| if(n->op != ODCLFIELD) |
| fatal("funcargs in %O", n->op); |
| if(n->left != N) { |
| n->left->op = ONAME; |
| n->left->ntype = n->right; |
| declare(n->left, PPARAM); |
| if(dclcontext == PAUTO) |
| n->left->vargen = ++vargen; |
| } |
| } |
| |
| // declare the out arguments. |
| gen = count(nt->list); |
| int i = 0; |
| for(l=nt->rlist; l; l=l->next) { |
| n = l->n; |
| |
| if(n->op != ODCLFIELD) |
| fatal("funcargs out %O", n->op); |
| |
| if(n->left == N) { |
| // Name so that escape analysis can track it. ~r stands for 'result'. |
| snprint(namebuf, sizeof(namebuf), "~r%d", gen++); |
| n->left = newname(lookup(namebuf)); |
| // TODO: n->left->missing = 1; |
| } |
| |
| n->left->op = ONAME; |
| |
| if(isblank(n->left)) { |
| // Give it a name so we can assign to it during return. ~b stands for 'blank'. |
| // The name must be different from ~r above because if you have |
| // func f() (_ int) |
| // func g() int |
| // f is allowed to use a plain 'return' with no arguments, while g is not. |
| // So the two cases must be distinguished. |
| // We do not record a pointer to the original node (n->orig). |
| // Having multiple names causes too much confusion in later passes. |
| nn = nod(OXXX, N, N); |
| *nn = *n->left; |
| nn->orig = nn; |
| snprint(namebuf, sizeof(namebuf), "~b%d", gen++); |
| nn->sym = lookup(namebuf); |
| n->left = nn; |
| } |
| |
| n->left->ntype = n->right; |
| declare(n->left, PPARAMOUT); |
| if(dclcontext == PAUTO) |
| n->left->vargen = ++i; |
| } |
| } |
| |
| /* |
| * Same as funcargs, except run over an already constructed TFUNC. |
| * This happens during import, where the hidden_fndcl rule has |
| * used functype directly to parse the function's type. |
| */ |
| static void |
| funcargs2(Type *t) |
| { |
| Type *ft; |
| Node *n; |
| |
| if(t->etype != TFUNC) |
| fatal("funcargs2 %T", t); |
| |
| if(t->thistuple) |
| for(ft=getthisx(t)->type; ft; ft=ft->down) { |
| if(!ft->nname || !ft->nname->sym) |
| continue; |
| n = ft->nname; // no need for newname(ft->nname->sym) |
| n->type = ft->type; |
| declare(n, PPARAM); |
| } |
| |
| if(t->intuple) |
| for(ft=getinargx(t)->type; ft; ft=ft->down) { |
| if(!ft->nname || !ft->nname->sym) |
| continue; |
| n = ft->nname; |
| n->type = ft->type; |
| declare(n, PPARAM); |
| } |
| |
| if(t->outtuple) |
| for(ft=getoutargx(t)->type; ft; ft=ft->down) { |
| if(!ft->nname || !ft->nname->sym) |
| continue; |
| n = ft->nname; |
| n->type = ft->type; |
| declare(n, 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 = newname(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); |
| } |
| |
| /* |
| * structs, functions, and methods. |
| * they don't belong here, but where do they belong? |
| */ |
| |
| static void |
| checkembeddedtype(Type *t) |
| { |
| if (t == T) |
| return; |
| |
| if(t->sym == S && isptr[t->etype]) { |
| t = t->type; |
| if(t->etype == TINTER) |
| yyerror("embedded type cannot be a pointer to interface"); |
| } |
| if(isptr[t->etype]) |
| yyerror("embedded type cannot be a pointer"); |
| else if(t->etype == TFORW && t->embedlineno == 0) |
| t->embedlineno = lineno; |
| } |
| |
| static Type* |
| structfield(Node *n) |
| { |
| Type *f; |
| int lno; |
| |
| lno = lineno; |
| lineno = n->lineno; |
| |
| if(n->op != ODCLFIELD) |
| fatal("structfield: oops %N\n", n); |
| |
| f = typ(TFIELD); |
| f->isddd = n->isddd; |
| |
| if(n->right != N) { |
| typecheck(&n->right, Etype); |
| n->type = n->right->type; |
| if(n->left != N) |
| n->left->type = n->type; |
| if(n->embedded) |
| checkembeddedtype(n->type); |
| } |
| n->right = N; |
| |
| f->type = n->type; |
| if(f->type == T) |
| f->broke = 1; |
| |
| switch(n->val.ctype) { |
| case CTSTR: |
| f->note = n->val.u.sval; |
| break; |
| default: |
| yyerror("field annotation must be string"); |
| // fallthrough |
| case CTxxx: |
| f->note = nil; |
| break; |
| } |
| |
| if(n->left && n->left->op == ONAME) { |
| f->nname = n->left; |
| f->embedded = n->embedded; |
| f->sym = f->nname->sym; |
| } |
| |
| lineno = lno; |
| return f; |
| } |
| |
| static uint32 uniqgen; |
| |
| static void |
| checkdupfields(Type *t, char* what) |
| { |
| int lno; |
| |
| lno = lineno; |
| |
| for( ; t; t=t->down) { |
| if(t->sym && t->nname && !isblank(t->nname)) { |
| if(t->sym->uniqgen == uniqgen) { |
| lineno = t->nname->lineno; |
| yyerror("duplicate %s %s", what, t->sym->name); |
| } else |
| t->sym->uniqgen = uniqgen; |
| } |
| } |
| |
| lineno = lno; |
| } |
| |
| /* |
| * convert a parsed id/type list into |
| * a type for struct/interface/arglist |
| */ |
| Type* |
| tostruct(NodeList *l) |
| { |
| Type *t, *f, **tp; |
| t = typ(TSTRUCT); |
| |
| for(tp = &t->type; l; l=l->next) { |
| f = structfield(l->n); |
| |
| *tp = f; |
| tp = &f->down; |
| } |
| |
| for(f=t->type; f && !t->broke; f=f->down) |
| if(f->broke) |
| t->broke = 1; |
| |
| uniqgen++; |
| checkdupfields(t->type, "field"); |
| |
| if (!t->broke) |
| checkwidth(t); |
| |
| return t; |
| } |
| |
| static Type* |
| tofunargs(NodeList *l) |
| { |
| Type *t, *f, **tp; |
| |
| t = typ(TSTRUCT); |
| t->funarg = 1; |
| |
| for(tp = &t->type; l; l=l->next) { |
| f = structfield(l->n); |
| f->funarg = 1; |
| |
| // esc.c needs to find f given a PPARAM to add the tag. |
| if(l->n->left && l->n->left->class == PPARAM) |
| l->n->left->paramfld = f; |
| |
| *tp = f; |
| tp = &f->down; |
| } |
| |
| for(f=t->type; f && !t->broke; f=f->down) |
| if(f->broke) |
| t->broke = 1; |
| |
| return t; |
| } |
| |
| static Type* |
| interfacefield(Node *n) |
| { |
| Type *f; |
| int lno; |
| |
| lno = lineno; |
| lineno = n->lineno; |
| |
| if(n->op != ODCLFIELD) |
| fatal("interfacefield: oops %N\n", n); |
| |
| if (n->val.ctype != CTxxx) |
| yyerror("interface method cannot have annotation"); |
| |
| f = typ(TFIELD); |
| f->isddd = n->isddd; |
| |
| if(n->right != N) { |
| if(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->left->type = n->type; |
| queuemethod(n); |
| |
| if(n->left->op == ONAME) { |
| f->nname = n->left; |
| f->embedded = n->embedded; |
| f->sym = f->nname->sym; |
| } |
| |
| } else { |
| |
| typecheck(&n->right, Etype); |
| n->type = n->right->type; |
| |
| if(n->embedded) |
| checkembeddedtype(n->type); |
| |
| if(n->type) |
| switch(n->type->etype) { |
| case TINTER: |
| break; |
| case TFORW: |
| yyerror("interface type loop involving %T", n->type); |
| f->broke = 1; |
| break; |
| default: |
| yyerror("interface contains embedded non-interface %T", n->type); |
| f->broke = 1; |
| break; |
| } |
| } |
| } |
| |
| n->right = N; |
| |
| f->type = n->type; |
| if(f->type == T) |
| f->broke = 1; |
| |
| lineno = lno; |
| return f; |
| } |
| |
| Type* |
| tointerface(NodeList *l) |
| { |
| Type *t, *f, **tp, *t1; |
| |
| t = typ(TINTER); |
| |
| tp = &t->type; |
| for(; l; l=l->next) { |
| f = interfacefield(l->n); |
| |
| if (l->n->left == N && f->type->etype == TINTER) { |
| // embedded interface, inline methods |
| for(t1=f->type->type; t1; t1=t1->down) { |
| f = typ(TFIELD); |
| f->type = t1->type; |
| f->broke = t1->broke; |
| f->sym = t1->sym; |
| if(f->sym) |
| f->nname = newname(f->sym); |
| *tp = f; |
| tp = &f->down; |
| } |
| } else { |
| *tp = f; |
| tp = &f->down; |
| } |
| } |
| |
| for(f=t->type; f && !t->broke; f=f->down) |
| if(f->broke) |
| t->broke = 1; |
| |
| uniqgen++; |
| checkdupfields(t->type, "method"); |
| t = sortinter(t); |
| checkwidth(t); |
| |
| return t; |
| } |
| |
| Node* |
| embedded(Sym *s, Pkg *pkg) |
| { |
| 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; |
| } |
| |
| if(exportname(name)) |
| n = newname(lookup(name)); |
| else if(s->pkg == builtinpkg) |
| // The name of embedded builtins belongs to pkg. |
| n = newname(pkglookup(name, pkg)); |
| else |
| n = newname(pkglookup(name, s->pkg)); |
| 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; |
| } |
| |
| // during import l->n->op is OKEY, but l->n->left->sym == S |
| // means it was a '?', not that it was |
| // a lone type This doesn't matter for the exported |
| // declarations, which are parsed by rules that don't |
| // use checkargs, but can happen for func literals in |
| // the inline bodies. |
| // TODO(rsc) this can go when typefmt case TFIELD in exportmode fmt.c prints _ instead of ? |
| if(importpkg && n->sym == S) |
| 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; |
| Sym *s; |
| |
| t = typ(TFUNC); |
| |
| rcvr = nil; |
| if(this) |
| rcvr = list1(this); |
| t->type = tofunargs(rcvr); |
| t->type->down = tofunargs(out); |
| t->type->down->down = tofunargs(in); |
| |
| uniqgen++; |
| checkdupfields(t->type->type, "argument"); |
| checkdupfields(t->type->down->type, "argument"); |
| checkdupfields(t->type->down->down->type, "argument"); |
| |
| if (t->type->broke || t->type->down->broke || t->type->down->down->broke) |
| t->broke = 1; |
| |
| if(this) |
| t->thistuple = 1; |
| t->outtuple = count(out); |
| t->intuple = count(in); |
| t->outnamed = 0; |
| if(t->outtuple > 0 && out->n->left != N && out->n->left->orig != N) { |
| s = out->n->left->orig->sym; |
| if(s != S && (s->name[0] != '~' || s->name[1] != 'r')) // ~r%d is the name invented for an unnamed result |
| t->outnamed = 1; |
| } |
| |
| return t; |
| } |
| |
| Sym* |
| methodsym(Sym *nsym, Type *t0, int iface) |
| { |
| Sym *s; |
| char *p; |
| Type *t; |
| char *suffix; |
| Pkg *spkg; |
| static Pkg *toppkg; |
| |
| t = t0; |
| if(t == T) |
| goto bad; |
| s = t->sym; |
| if(s == S && isptr[t->etype]) { |
| t = t->type; |
| if(t == T) |
| goto bad; |
| s = t->sym; |
| } |
| spkg = nil; |
| if(s != S) |
| spkg = s->pkg; |
| |
| // 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"; |
| } |
| if((spkg == nil || nsym->pkg != spkg) && !exportname(nsym->name)) { |
| if(t0->sym == S && isptr[t0->etype]) |
| p = smprint("(%-hT).%s.%s%s", t0, nsym->pkg->prefix, nsym->name, suffix); |
| else |
| p = smprint("%-hT.%s.%s%s", t0, nsym->pkg->prefix, nsym->name, suffix); |
| } else { |
| if(t0->sym == S && isptr[t0->etype]) |
| p = smprint("(%-hT).%s%s", t0, nsym->name, suffix); |
| else |
| p = smprint("%-hT.%s%s", t0, nsym->name, suffix); |
| } |
| if(spkg == nil) { |
| if(toppkg == nil) |
| toppkg = mkpkg(strlit("go")); |
| spkg = toppkg; |
| } |
| s = pkglookup(p, spkg); |
| 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 = nil; |
| if(t->op == OIND) { |
| star = "*"; |
| t = t->left; |
| } |
| if(t->sym == S || isblank(n)) |
| return newname(n->sym); |
| |
| if(star) |
| p = smprint("(%s%S).%S", star, t->sym, n->sym); |
| else |
| p = smprint("%S.%S", t->sym, n->sym); |
| |
| if(exportname(t->sym->name)) |
| n = newname(lookup(p)); |
| else |
| 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, int nointerface) |
| { |
| Type *f, *d, *pa; |
| Node *n; |
| |
| // 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, 1); |
| if(f == T) { |
| t = pa; |
| if(t == T) // rely on typecheck having complained before |
| return; |
| if(t != T) { |
| if(isptr[t->etype]) { |
| if(t->sym != S) { |
| yyerror("invalid receiver type %T (%T is a pointer type)", pa, t); |
| return; |
| } |
| t = t->type; |
| } |
| if(t->broke) // rely on typecheck having complained before |
| return; |
| if(t->sym == S) { |
| yyerror("invalid receiver type %T (%T is an unnamed type)", pa, t); |
| return; |
| } |
| if(isptr[t->etype]) { |
| yyerror("invalid receiver type %T (%T is a pointer type)", pa, t); |
| return; |
| } |
| if(t->etype == TINTER) { |
| yyerror("invalid receiver type %T (%T is an interface type)", pa, t); |
| return; |
| } |
| } |
| // Should have picked off all the reasons above, |
| // but just in case, fall back to generic error. |
| yyerror("invalid receiver type %T (%lT / %lT)", pa, pa, t); |
| return; |
| } |
| |
| pa = f; |
| if(pa->etype == TSTRUCT) { |
| for(f=pa->type; f; f=f->down) { |
| if(f->sym == sf) { |
| yyerror("type %T has both field and method named %S", pa, sf); |
| return; |
| } |
| } |
| } |
| |
| if(local && !pa->local) { |
| // defining method on non-local type. |
| yyerror("cannot define new methods on non-local type %T", pa); |
| return; |
| } |
| |
| 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; |
| } |
| |
| f = structfield(n); |
| f->nointerface = nointerface; |
| |
| // during import unexported method names should be in the type's package |
| if(importpkg && f->sym && !exportname(f->sym->name) && f->sym->pkg != structpkg) |
| fatal("imported method name %+S in wrong package %s\n", f->sym, structpkg->name); |
| |
| if(d == T) |
| pa->method = f; |
| else |
| d->down = f; |
| 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. |
| // TODO(rsc): this is the old jit closure handling code. |
| // with the new closures, isclosure is always 0; delete this block. |
| 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); |
| |
| stksize = 0; |
| dclcontext = PAUTO; |
| funcdepth = n->funcdepth + 1; |
| compile(n); |
| curfn = nil; |
| funcdepth = 0; |
| dclcontext = PEXTERN; |
| } |
| |
| Sym* |
| funcsym(Sym *s) |
| { |
| char *p; |
| Sym *s1; |
| |
| p = smprint("%s·f", s->name); |
| s1 = pkglookup(p, s->pkg); |
| free(p); |
| if(s1->def == N) { |
| s1->def = newname(s1); |
| s1->def->shortname = newname(s); |
| funcsyms = list(funcsyms, s1->def); |
| } |
| return s1; |
| } |