mirror of
https://github.com/golang/go
synced 2024-11-22 12:24:47 -07:00
607eaea456
Fixes #812. R=ken2 CC=golang-dev https://golang.org/cl/1904041
1197 lines
20 KiB
C
1197 lines
20 KiB
C
// 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;
|
|
}
|
|
|