blob: 38bc022d2889d026be81e8d3475c72ad6e7a5e36 [file] [log] [blame]
// 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"
int
dflag(void)
{
if(!debug['d'])
return 0;
if(debug['y'])
return 1;
if(inimportsys)
return 0;
return 1;
}
void
dodclvar(Node *n, Type *t)
{
if(n == N)
return;
if(t != T && (t->etype == TIDEAL || t->etype == TNIL))
fatal("dodclvar %T", t);
for(; n->op == OLIST; n = n->right)
dodclvar(n->left, t);
dowidth(t);
// in case of type checking error,
// use "undefined" type for variable type,
// to avoid fatal in addvar.
if(t == T)
t = typ(TFORW);
addvar(n, t, dclcontext);
autoexport(n->sym);
if(funcdepth > 0)
addtop = list(addtop, nod(ODCL, n, N));
}
void
dodclconst(Node *n, Node *e)
{
if(n == N)
return;
for(; n->op == OLIST; n=n->right)
dodclconst(n, e);
addconst(n, e, dclcontext);
autoexport(n->sym);
}
/*
* introduce a type named n
* but it is an unknown type for now
*/
Type*
dodcltype(Type *n)
{
Sym *s;
// if n has been forward declared,
// use the Type* created then
s = n->sym;
if(s->block == block && s->def != N && s->def->op == OTYPE) {
switch(s->def->type->etype) {
case TFORWSTRUCT:
case TFORWINTER:
n = s->def->type;
goto found;
}
}
// otherwise declare a new type
addtyp(n, dclcontext);
found:
n->local = 1;
autoexport(n->sym);
return n;
}
/*
* now we know what n is: it's t
*/
void
updatetype(Type *n, Type *t)
{
Sym *s;
int local;
int maplineno, lno;
s = n->sym;
if(s == S || s->def == N || s->def->op != OTYPE || s->def->type != n)
fatal("updatetype %T = %T", n, t);
switch(n->etype) {
case TFORW:
break;
case TFORWSTRUCT:
if(t->etype != TSTRUCT) {
yyerror("%T forward declared as struct", n);
return;
}
break;
case TFORWINTER:
if(t->etype != TINTER) {
yyerror("%T forward declared as interface", n);
return;
}
break;
default:
fatal("updatetype %T / %T", n, t);
}
// decl was
// type n t;
// copy t, but then zero out state associated with t
// that is no longer associated with n.
maplineno = n->maplineno;
local = n->local;
*n = *t;
n->sym = s;
n->local = local;
n->siggen = 0;
n->printed = 0;
n->method = nil;
n->vargen = 0;
n->nod = N;
// catch declaration of incomplete type
switch(n->etype) {
case TFORWSTRUCT:
case TFORWINTER:
break;
default:
checkwidth(n);
}
// double-check use of type as map key
if(maplineno) {
lno = lineno;
lineno = maplineno;
maptype(n, types[TBOOL]);
lineno = lno;
}
}
/*
* return nelem of list
*/
int
listcount(Node *n)
{
int v;
Iter s;
v = 0;
for(n = listfirst(&s, &n); n != N; n = listnext(&s))
v++;
return v;
}
int
structcount(Type *t)
{
int v;
Iter s;
v = 0;
for(t = structfirst(&s, &t); t != T; t = structnext(&s))
v++;
return v;
}
/*
* turn a parsed function declaration
* into a type
*/
Type*
functype(Node *this, Node *in, Node *out)
{
Type *t;
t = typ(TFUNC);
t->type = dostruct(this, TFUNC);
t->type->down = dostruct(out, TFUNC);
t->type->down->down = dostruct(in, TFUNC);
t->thistuple = listcount(this);
t->outtuple = listcount(out);
t->intuple = listcount(in);
checkwidth(t);
return t;
}
int
methcmp(Type *t1, Type *t2)
{
if(t1->etype != TFUNC)
return 0;
if(t2->etype != TFUNC)
return 0;
t1 = t1->type->down; // skip this arg
t2 = t2->type->down; // skip this arg
for(;;) {
if(t1 == t2)
break;
if(t1 == T || t2 == T)
return 0;
if(t1->etype != TSTRUCT || t2->etype != TSTRUCT)
return 0;
if(!eqtype(t1->type, t2->type))
return 0;
t1 = t1->down;
t2 = t2->down;
}
return 1;
}
Sym*
methodsym(Sym *nsym, Type *t0)
{
Sym *s;
char buf[NSYMB];
Type *t;
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);
snprint(buf, sizeof(buf), "%#hT·%s", t0, nsym->name);
//print("methodname %s\n", buf);
return pkglookup(buf, s->package);
bad:
yyerror("illegal <this> type: %T", t);
return S;
}
Node*
methodname(Node *n, Type *t)
{
Sym *s;
s = methodsym(n->sym, t);
if(s == S)
return n;
return newname(s);
}
/*
* add a method, declared as a function,
* n is fieldname, pa is base type, t is function type
*/
void
addmethod(Node *n, Type *t, int local)
{
Type *f, *d, *pa;
Sym *sf;
pa = nil;
sf = nil;
// get field sym
if(n == N)
goto bad;
if(n->op != ONAME)
goto bad;
sf = n->sym;
if(sf == S)
goto bad;
// get parent type sym
pa = *getthis(t); // ptr to this structure
if(pa == T)
goto bad;
pa = pa->type; // ptr to this field
if(pa == T)
goto bad;
pa = pa->type; // ptr to this type
if(pa == T)
goto bad;
f = methtype(pa);
if(f == T)
goto bad;
if(local && !f->local) {
yyerror("cannot define methods on non-local type %T", f);
return;
}
pa = f;
if(pkgimportname != S && !exportname(sf->name))
sf = pkglookup(sf->name, pkgimportname->name);
n = nod(ODCLFIELD, newname(sf), N);
n->type = t;
d = T; // last found
for(f=pa->method; f!=T; f=f->down) {
if(f->etype != TFIELD)
fatal("addmethod: not TFIELD: %N", f);
if(strcmp(sf->name, f->sym->name) != 0) {
d = f;
continue;
}
if(!eqtype(t, f->type)) {
yyerror("method redeclared: %T.%S", pa, sf);
print("\t%T\n\t%T\n", f->type, t);
}
return;
}
if(d == T)
stotype(n, 0, &pa->method);
else
stotype(n, 0, &d->down);
if(dflag())
print("method %S of type %T\n", sf, pa);
return;
bad:
yyerror("invalid receiver type %T", pa);
}
/*
* a function named init is a special case.
* it is called by the initialization before
* main is run. to make it unique within a
* package and also uncallable, the name,
* normally "pkg.init", is altered to "pkg.init·filename".
*/
Node*
renameinit(Node *n)
{
Sym *s;
s = n->sym;
if(s == S)
return n;
if(strcmp(s->name, "init") != 0)
return n;
snprint(namebuf, sizeof(namebuf), "init·%s", filename);
s = lookup(namebuf);
return newname(s);
}
/*
* declare the function proper.
* and declare the arguments
* called in extern-declaration context
* returns in auto-declaration context.
*/
void
funchdr(Node *n)
{
Node *on;
Sym *s;
s = n->nname->sym;
on = s->def;
if(on != N && (on->op != ONAME || on->builtin))
on = N;
// check for same types
if(on != N) {
if(eqtype(n->type, on->type)) {
if(!eqargs(n->type, on->type)) {
yyerror("function arg names changed: %S", s);
print("\t%T\n\t%T\n", on->type, n->type);
}
} else {
yyerror("function redeclared: %S", s);
print("\t%T\n\t%T\n", on->type, n->type);
on = N;
}
}
// check for forward declaration
if(on == N) {
// initial declaration or redeclaration
// declare fun name, argument types and argument names
n->nname->type = n->type;
if(n->type->thistuple == 0)
addvar(n->nname, n->type, PFUNC);
else
n->nname->class = PFUNC;
} else {
// identical redeclaration
// steal previous names
n->nname = on;
n->type = on->type;
n->class = on->class;
n->sym = s;
if(dflag())
print("forew var-dcl %S %T\n", n->sym, n->type);
}
// change the declaration context from extern to auto
autodcl = dcl();
autodcl->back = autodcl;
if(funcdepth == 0 && dclcontext != PEXTERN)
fatal("funchdr: dclcontext");
dclcontext = PAUTO;
markdcl();
funcargs(n->type);
}
void
funcargs(Type *ft)
{
Type *t;
Iter save;
int all;
funcdepth++;
// declare the this/in arguments
t = funcfirst(&save, ft);
while(t != T) {
if(t->nname != N) {
t->nname->xoffset = t->width;
addvar(t->nname, t->type, PPARAM);
}
t = funcnext(&save);
}
// declare the outgoing arguments
all = 0;
t = structfirst(&save, getoutarg(ft));
while(t != T) {
if(t->nname != N)
t->nname->xoffset = t->width;
if(t->nname != N) {
addvar(t->nname, t->type, PPARAMOUT);
all |= 1;
} else
all |= 2;
t = structnext(&save);
}
// this test is remarkedly similar to checkarglist
if(all == 3)
yyerror("cannot mix anonymous and named output arguments");
ft->outnamed = 0;
if(all == 1)
ft->outnamed = 1;
}
/*
* compile the function.
* called in auto-declaration context.
* returns in extern-declaration context.
*/
void
funcbody(Node *n)
{
compile(n);
// change the declaration context from auto to extern
if(dclcontext != PAUTO)
fatal("funcbody: dclcontext");
popdcl();
funcdepth--;
if(funcdepth == 0)
dclcontext = PEXTERN;
}
void
funclit0(Type *t)
{
Node *n;
n = nod(OXXX, N, N);
n->outer = funclit;
n->dcl = autodcl;
funclit = n;
// new declaration context
autodcl = dcl();
autodcl->back = autodcl;
funcargs(t);
}
Node*
funclit1(Type *type, Node *body)
{
Node *func;
Node *a, *d, *f, *n, *args, *clos, *in, *out;
Type *ft, *t;
Iter save;
int narg, shift;
popdcl();
func = funclit;
funclit = func->outer;
// build up type of func f that we're going to compile.
// as we referred to variables from the outer function,
// we accumulated a list of PHEAP names in func.
//
narg = 0;
if(func->cvars == N)
ft = type;
else {
// add PHEAP versions as function arguments.
in = N;
for(a=listfirst(&save, &func->cvars); a; a=listnext(&save)) {
d = nod(ODCLFIELD, a, N);
d->type = ptrto(a->type);
in = list(in, d);
// while we're here, set up a->heapaddr for back end
n = nod(ONAME, N, N);
snprint(namebuf, sizeof namebuf, "&%s", a->sym->name);
n->sym = lookup(namebuf);
n->type = ptrto(a->type);
n->class = PPARAM;
n->xoffset = narg*types[tptr]->width;
n->addable = 1;
n->ullman = 1;
narg++;
a->heapaddr = n;
a->xoffset = 0;
// unlink from actual ONAME in symbol table
a->closure->closure = a->outer;
}
// add a dummy arg for the closure's caller pc
d = nod(ODCLFIELD, a, N);
d->type = types[TUINTPTR];
in = list(in, d);
// slide param offset to make room for ptrs above.
// narg+1 to skip over caller pc.
shift = (narg+1)*types[tptr]->width;
// now the original arguments.
for(t=structfirst(&save, getinarg(type)); t; t=structnext(&save)) {
d = nod(ODCLFIELD, t->nname, N);
d->type = t->type;
in = list(in, d);
a = t->nname;
if(a != N) {
if(a->stackparam != N)
a = a->stackparam;
a->xoffset += shift;
}
}
in = rev(in);
// out arguments
out = N;
for(t=structfirst(&save, getoutarg(type)); t; t=structnext(&save)) {
d = nod(ODCLFIELD, t->nname, N);
d->type = t->type;
out = list(out, d);
a = t->nname;
if(a != N) {
if(a->stackparam != N)
a = a->stackparam;
a->xoffset += shift;
}
}
out = rev(out);
ft = functype(N, in, out);
ft->outnamed = type->outnamed;
}
// declare function.
vargen++;
snprint(namebuf, sizeof(namebuf), "_f%.3ld·%s", vargen, filename);
f = newname(lookup(namebuf));
addvar(f, ft, PFUNC);
f->funcdepth = 0;
// compile function
n = nod(ODCLFUNC, N, N);
n->nname = f;
n->type = ft;
if(body == N)
body = nod(ORETURN, N, N);
n->nbody = body;
compile(n);
funcdepth--;
autodcl = func->dcl;
// if there's no closure, we can use f directly
if(func->cvars == N)
return f;
// build up type for this instance of the closure func.
in = N;
d = nod(ODCLFIELD, N, N); // siz
d->type = types[TINT];
in = list(in, d);
d = nod(ODCLFIELD, N, N); // f
d->type = ft;
in = list(in, d);
for(a=listfirst(&save, &func->cvars); a; a=listnext(&save)) {
d = nod(ODCLFIELD, N, N); // arg
d->type = ptrto(a->type);
in = list(in, d);
}
in = rev(in);
d = nod(ODCLFIELD, N, N);
d->type = type;
out = d;
clos = syslook("closure", 1);
clos->type = functype(N, in, out);
// literal expression is sys.closure(siz, f, arg0, arg1, ...)
// which builds a function that calls f after filling in arg0,
// arg1, ... for the PHEAP arguments above.
args = N;
if(narg*widthptr > 100)
yyerror("closure needs too many variables; runtime will reject it");
a = nodintconst(narg*widthptr);
args = list(args, a); // siz
args = list(args, f); // f
for(a=listfirst(&save, &func->cvars); a; a=listnext(&save)) {
d = oldname(a->sym);
addrescapes(d);
args = list(args, nod(OADDR, d, N));
}
args = rev(args);
return nod(OCALL, clos, args);
}
/*
* turn a parsed struct into a type
*/
Type**
stotype(Node *n, int et, Type **t)
{
Type *f, *t1;
Iter save;
Strlit *note;
int lno;
lno = lineno;
n = listfirst(&save, &n);
loop:
note = nil;
if(n == N) {
*t = T;
lineno = lno;
return t;
}
lineno = n->lineno;
if(n->op == OLIST) {
// recursive because it can be lists of lists
t = stotype(n, et, t);
goto next;
}
if(n->op != ODCLFIELD)
fatal("stotype: oops %N\n", n);
if(n->type == T) {
// assume error already printed
goto next;
}
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) {
yyerror("interface contains embedded non-interface %T", t);
goto next;
}
for(t1=n->type->type; t1!=T; t1=t1->down) {
if(strcmp(t1->sym->package, package) != 0)
yyerror("embedded interface contains unexported method %S", t1->sym);
f = typ(TFIELD);
f->type = t1->type;
f->width = BADWIDTH;
f->nname = newname(t1->sym);
f->sym = t1->sym;
*t = f;
t = &f->down;
}
goto next;
}
f = typ(TFIELD);
f->type = n->type;
f->note = note;
f->width = BADWIDTH;
if(n->left != N && n->left->op == ONAME) {
f->nname = n->left;
f->embedded = n->embedded;
f->sym = f->nname->sym;
if(pkgimportname != S && !exportname(f->sym->name))
f->sym = pkglookup(f->sym->name, structpkg);
}
*t = f;
t = &f->down;
next:
n = listnext(&save);
goto loop;
}
Type*
dostruct(Node *n, 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(n, et, &t->type);
if(!funarg)
checkwidth(t);
return t;
}
Type*
sortinter(Type *t)
{
return t;
}
void
dcopy(Sym *a, Sym *b)
{
a->name = b->name;
a->def = b->def;
a->package = b->package;
a->undef = b->undef;
a->vargen = b->vargen;
a->block = b->block;
a->lastlineno = b->lastlineno;
a->offset = b->offset;
}
Sym*
push(void)
{
Sym *d;
d = mal(sizeof(*d));
d->link = dclstack;
dclstack = d;
return d;
}
Sym*
pushdcl(Sym *s)
{
Sym *d;
d = push();
dcopy(d, s);
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->package);
dcopy(s, d);
if(dflag())
print("\t%L pop %S\n", lineno, s);
}
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->package);
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;
print("\ndumpdcl: %s %p\n", st, b0stack);
i = 0;
for(d=dclstack; d!=S; d=d->link) {
i++;
print(" %.2d %p", i, d);
if(d == b0stack)
print(" (b0)");
if(d->name == nil) {
print("\n");
continue;
}
print(" '%s'", d->name);
s = pkglookup(d->name, d->package);
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;
}
}
}
static void
redeclare(char *str, Sym *s)
{
if(s->block == block) {
yyerror("%s %S redeclared in this block", str, s);
print(" previous declaration at %L\n", s->lastlineno);
}
s->block = block;
s->lastlineno = lineno;
}
void
addvar(Node *n, Type *t, int ctxt)
{
Dcl *r, *d;
Sym *s;
int gen;
if(n==N || n->sym == S || n->op != ONAME || t == T)
fatal("addvar: n=%N t=%T nil", n, t);
s = n->sym;
if(ctxt == PEXTERN || ctxt == PFUNC) {
r = externdcl;
gen = 0;
} else {
r = autodcl;
vargen++;
gen = vargen;
pushdcl(s);
}
redeclare("variable", s);
s->vargen = gen;
s->def = n;
s->offset = 0;
n->funcdepth = funcdepth;
n->type = t;
n->vargen = gen;
n->class = ctxt;
d = dcl();
d->dsym = s;
d->dnode = n;
d->op = ONAME;
r->back->forw = d;
r->back = d;
if(dflag()) {
if(ctxt == PEXTERN)
print("extern var-dcl %S G%ld %T\n", s, s->vargen, t);
else if(ctxt == PFUNC)
print("extern func-dcl %S G%ld %T\n", s, s->vargen, t);
else
print("auto var-dcl %S G%ld %T\n", s, s->vargen, t);
}
}
void
addtyp(Type *n, int ctxt)
{
Dcl *r, *d;
Sym *s;
static int typgen;
if(n==T || n->sym == S)
fatal("addtyp: n=%T t=%T nil", n);
s = n->sym;
if(ctxt == PEXTERN)
r = externdcl;
else {
r = autodcl;
pushdcl(s);
n->vargen = ++typgen;
}
redeclare("type", s);
s->def = typenod(n);
d = dcl();
d->dsym = s;
d->dtype = n;
d->op = OTYPE;
d->back = r->back;
r->back->forw = d;
r->back = d;
d = dcl();
d->dtype = n;
d->op = OTYPE;
r = typelist;
d->back = r->back;
r->back->forw = d;
r->back = d;
if(dflag()) {
if(ctxt == PEXTERN)
print("extern typ-dcl %S G%ld %T\n", s, s->vargen, n);
else
print("auto typ-dcl %S G%ld %T\n", s, s->vargen, n);
}
}
void
addconst(Node *n, Node *e, int ctxt)
{
Sym *s;
Dcl *r, *d;
if(n->op != ONAME && n->op != ONONAME)
fatal("addconst: not a name");
if(e->op != OLITERAL) {
yyerror("expression must be a constant");
return;
}
s = n->sym;
if(ctxt == PEXTERN)
r = externdcl;
else {
r = autodcl;
pushdcl(s);
}
redeclare("constant", s);
s->def = e;
e->sym = s;
d = dcl();
d->dsym = s;
d->dnode = e;
d->op = OLITERAL;
d->back = r->back;
r->back->forw = d;
r->back = d;
if(dflag())
print("const-dcl %S %N\n", n->sym, n->sym->def);
}
Node*
fakethis(void)
{
Node *n;
n = nod(ODCLFIELD, N, N);
n->type = 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)->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;
}
/*
* this generates a new name that is
* pushed down on the declaration list.
* no diagnostics are produced as this
* name will soon be declared.
*/
Node*
newname(Sym *s)
{
Node *n;
n = nod(ONAME, N, N);
n->sym = s;
n->type = T;
n->addable = 1;
n->ullman = 1;
n->xoffset = 0;
return n;
}
Node*
typenod(Type *t)
{
if(t->nod == N) {
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) {
n = nod(ONONAME, N, N);
n->sym = s;
n->type = T;
n->addable = 1;
n->ullman = 1;
}
if(n->op == OLITERAL) {
c = nod(OLITERAL, N, N);
c->sym = s;
c->val = n->val;
c->type = n->type;
c->iota = n->iota;
return c;
}
if(n->funcdepth > 0 && n->funcdepth != funcdepth && n->op == ONAME) {
// inner func is referring to var
// in outer func.
if(n->closure == N || n->closure->funcdepth != funcdepth) {
// create new closure var.
c = nod(ONAME, N, N);
c->sym = s;
c->class = PPARAMREF;
c->type = n->type;
c->addable = 0;
c->ullman = 2;
c->funcdepth = funcdepth;
c->outer = n->closure;
n->closure = c;
c->closure = n;
funclit->cvars = list(c, funclit->cvars);
}
// 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*
oldtype(Sym *s)
{
Type *t;
if(s == S)
return T;
if(s->def == N || s->def->op != OTYPE) {
yyerror("%S is not a type", s);
return T;
}
t = s->def->type;
/*
* If t is lowercase and not in our package
* and this isn't a reference during the parsing
* of import data, complain.
*/
if(pkgimportname == S && !exportname(s->name) && strcmp(s->package, package) != 0)
yyerror("cannot use type %T", t);
return t;
}
/*
* n is a node with a name (or a reversed list of them).
* make it an anonymous declaration of that name's type.
*/
Node*
nametoanondcl(Node *na)
{
Node **l, *n;
Type *t;
for(l=&na; (n=*l)->op == OLIST; l=&n->left)
n->right = nametoanondcl(n->right);
n = n->sym->def;
if(n == N || n->op != OTYPE || (t = n->type) == T) {
yyerror("%S is not a type", n->sym);
t = typ(TINT32);
}
n = nod(ODCLFIELD, N, N);
n->type = t;
*l = n;
return na;
}
/*
* n is a node with a name (or a reversed list of them).
* make it a declaration of the given type.
*/
Node*
nametodcl(Node *na, Type *t)
{
Node **l, *n;
for(l=&na; (n=*l)->op == OLIST; l=&n->left)
n->right = nametodcl(n->right, t);
n = nod(ODCLFIELD, n, N);
n->type = t;
*l = n;
return na;
}
/*
* make an anonymous declaration for t
*/
Node*
anondcl(Type *t)
{
Node *n;
n = nod(ODCLFIELD, N, N);
n->type = t;
return n;
}
static Node*
findtype(Node *n)
{
Node *r;
for(r=n; r->op==OLIST; r=r->right)
if(r->left->op == OKEY)
return r->left->right;
if(r->op == OKEY)
return r->right;
if(n->op == OLIST)
n = n->left;
return N;
}
static Node*
xanondcl(Node *nt, int dddok)
{
Node *n;
Type *t;
t = nt->type;
if(nt->op != OTYPE) {
yyerror("%S is not a type", nt->sym);
t = types[TINT32];
}
n = nod(ODCLFIELD, N, N);
n->type = t;
if(!dddok && t->etype == TDDD)
yyerror("only last argument can have type ...");
return n;
}
static Node*
namedcl(Node *nn, Node *nt, int dddok)
{
Node *n;
Type *t;
if(nn->op == OKEY)
nn = nn->left;
if(nn->op == OTYPE && nn->sym == S) {
yyerror("cannot mix anonymous %T with named arguments", nn->type);
return xanondcl(nn, dddok);
}
t = types[TINT32];
if(nt == N)
yyerror("missing type for argument %S", nn->sym);
else if(nt->op != OTYPE)
yyerror("%S is not a type", nt->sym);
else
t = nt->type;
n = nod(ODCLFIELD, newname(nn->sym), N);
n->type = t;
if(!dddok && t->etype == TDDD)
yyerror("only last argument can have type ...");
return n;
}
/*
* check that the list of declarations is either all anonymous or all named
*/
Node*
checkarglist(Node *n)
{
Node *r;
Node **l;
// check for all anonymous
for(r=n; r->op==OLIST; r=r->right)
if(r->left->op == OKEY)
goto named;
if(r->op == OKEY)
goto named;
// all anonymous - add names
for(l=&n; (r=*l)->op==OLIST; l=&r->right)
r->left = xanondcl(r->left, 0);
*l = xanondcl(r, 1);
return n;
named:
// otherwise, each run of names ends in a type.
// add a type to each one that needs one.
for(l=&n; (r=*l)->op==OLIST; l=&r->right)
r->left = namedcl(r->left, findtype(r), 0);
*l = namedcl(r, findtype(r), 1);
return n;
}
/*
* hand-craft the following initialization code
* var initdone·<file> uint8 (1)
* func Init·<file>() (2)
* if initdone·<file> { (3)
* if initdone·<file> == 2 (4)
* return
* throw(); (5)
* }
* initdone.<file>++; (6)
* // over all matching imported symbols
* <pkg>.init·<file>() (7)
* { <init stmts> } (8)
* init·<file>() // if any (9)
* initdone.<file>++; (10)
* return (11)
* }
*/
int
anyinit(Node *n)
{
uint32 h;
Sym *s;
// are there any init statements
if(n != N)
return 1;
// is this main
if(strcmp(package, "main") == 0)
return 1;
// is there an explicit init function
snprint(namebuf, sizeof(namebuf), "init·%s", filename);
s = lookup(namebuf);
if(s->def != N)
return 1;
// are there any imported init functions
for(h=0; h<NHASH; h++)
for(s = hash[h]; s != S; s = s->link) {
if(s->name[0] != 'I' || strncmp(s->name, "Init·", 6) != 0)
continue;
if(s->def == N)
continue;
return 1;
}
// then none
return 0;
}
void
fninit(Node *n)
{
Node *gatevar;
Node *a, *b, *fn, *r;
uint32 h;
Sym *s, *initsym;
if(strcmp(package, "PACKAGE") == 0) {
// sys.go or unsafe.go during compiler build
return;
}
if(!anyinit(n))
return;
r = N;
// (1)
snprint(namebuf, sizeof(namebuf), "initdone·%s", filename);
gatevar = newname(lookup(namebuf));
addvar(gatevar, types[TUINT8], PEXTERN);
// (2)
maxarg = 0;
stksize = initstksize;
snprint(namebuf, sizeof(namebuf), "Init·%s", filename);
// this is a botch since we need a known name to
// call the top level init function out of rt0
if(strcmp(package, "main") == 0)
snprint(namebuf, sizeof(namebuf), "init");
fn = nod(ODCLFUNC, N, N);
initsym = lookup(namebuf);
fn->nname = newname(initsym);
fn->type = functype(N, N, N);
funchdr(fn);
// (3)
a = nod(OIF, N, N);
a->ntest = nod(ONE, gatevar, nodintconst(0));
r = list(r, a);
// (4)
b = nod(OIF, N, N);
b->ntest = nod(OEQ, gatevar, nodintconst(2));
b->nbody = nod(ORETURN, N, N);
a->nbody = b;
// (5)
b = syslook("throwinit", 0);
b = nod(OCALL, b, N);
a->nbody = list(a->nbody, b);
// (6)
a = nod(OASOP, gatevar, nodintconst(1));
a->etype = OADD;
r = list(r, a);
// (7)
for(h=0; h<NHASH; h++)
for(s = hash[h]; s != S; s = s->link) {
if(s->name[0] != 'I' || strncmp(s->name, "Init·", 6) != 0)
continue;
if(s->def == N)
continue;
if(s == initsym)
continue;
// could check that it is fn of no args/returns
a = nod(OCALL, s->def, N);
r = list(r, a);
}
// (8)
r = list(r, initfix(n));
// (9)
// could check that it is fn of no args/returns
snprint(namebuf, sizeof(namebuf), "init·%s", filename);
s = lookup(namebuf);
if(s->def != N) {
a = nod(OCALL, s->def, N);
r = list(r, a);
}
// (10)
a = nod(OASOP, gatevar, nodintconst(1));
a->etype = OADD;
r = list(r, a);
// (11)
a = nod(ORETURN, N, N);
r = list(r, a);
exportsym(fn->nname->sym);
fn->nbody = rev(r);
//dump("b", fn);
//dump("r", fn->nbody);
popdcl();
initflag = 1; // flag for loader static initialization
compile(fn);
initflag = 0;
}
/*
* when a type's width should be known, we call checkwidth
* to compute it. during a declaration like
*
* type T *struct { next T }
*
* it is necessary to defer the calculation of the struct width
* until after T has been initialized to be a pointer to that struct.
* similarly, during import processing structs may be used
* before their definition. in those situations, calling
* defercheckwidth() stops width calculations until
* resumecheckwidth() is called, at which point all the
* checkwidths that were deferred are executed.
* sometimes it is okay to
*/
typedef struct TypeList TypeList;
struct TypeList {
Type *t;
TypeList *next;
};
static TypeList *tlfree;
static TypeList *tlq;
static int defercalc;
void
checkwidth(Type *t)
{
TypeList *l;
// function arg structs should not be checked
// outside of the enclosing function.
if(t->funarg)
fatal("checkwidth %T", t);
if(!defercalc) {
dowidth(t);
return;
}
l = tlfree;
if(l != nil)
tlfree = l->next;
else
l = mal(sizeof *l);
l->t = t;
l->next = tlq;
tlq = l;
}
void
defercheckwidth(void)
{
// we get out of sync on syntax errors, so don't be pedantic.
// if(defercalc)
// fatal("defercheckwidth");
defercalc = 1;
}
void
resumecheckwidth(void)
{
TypeList *l;
if(!defercalc)
fatal("restartcheckwidth");
defercalc = 0;
for(l = tlq; l != nil; l = tlq) {
dowidth(l->t);
tlq = l->next;
l->next = tlfree;
tlfree = l;
}
}
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, N);
n->embedded = 1;
if(s == S)
return n;
n->type = oldtype(s);
if(n->type != T && isptr[n->type->etype])
yyerror("embedded type cannot be a pointer");
return n;
}
/*
* declare variables from grammar
* new_name_list [type] = expr_list
*/
Node*
variter(Node *vv, Type *t, Node *ee)
{
Iter viter, eiter;
Node *v, *e, *r, *a;
vv = rev(vv);
ee = rev(ee);
v = listfirst(&viter, &vv);
e = listfirst(&eiter, &ee);
r = N;
loop:
if(v == N && e == N)
return rev(r);
if(v == N || e == N) {
yyerror("shape error in var dcl");
return rev(r);
}
a = nod(OAS, v, N);
if(t == T) {
gettype(e, a);
defaultlit(e, T);
dodclvar(v, e->type);
} else
dodclvar(v, t);
a->right = e;
r = list(r, a);
v = listnext(&viter);
e = listnext(&eiter);
goto loop;
}
/*
* declare constants from grammar
* new_name_list [[type] = expr_list]
*/
void
constiter(Node *vv, Type *t, Node *cc)
{
Iter viter, citer;
Node *v, *c, n1;
if(cc == N) {
if(t != T)
yyerror("constdcl cannot have type without expr");
cc = lastconst;
t = lasttype;
}
lastconst = cc;
lasttype = t;
vv = rev(vv);
cc = rev(treecopy(cc));
v = listfirst(&viter, &vv);
c = listfirst(&citer, &cc);
loop:
if(v == N && c == N) {
iota += 1;
return;
}
if(v == N || c == N) {
yyerror("shape error in const dcl");
iota += 1;
return;
}
memset(&n1, 0, sizeof n1);
gettype(c, &n1);
if(n1.ninit != nil) {
// the expression had extra code to run.
// dodclconst is going to print an error
// because the expression isn't constant,
// but out of paranoia, bump nerrors so
// that compile cannot succeed accidentally
nerrors++;
}
if(t != T)
convlit(c, t);
if(t == T)
lasttype = c->type;
dodclconst(v, c);
v = listnext(&viter);
c = listnext(&citer);
goto loop;
}
/*
* look for
* unsafe.Sizeof
* unsafe.Offsetof
* rewrite with a constant
*/
Node*
unsafenmagic(Node *l, Node *r)
{
Node *n;
Sym *s;
Type *t, *tr;
long v;
Val val;
if(l == N || r == N)
goto no;
if(l->op != ONAME)
goto no;
s = l->sym;
if(s == S)
goto no;
if(strcmp(s->package, "unsafe") != 0)
goto no;
if(strcmp(s->name, "Sizeof") == 0) {
walktype(r, Erv);
tr = r->type;
if(r->op == OLITERAL && r->val.ctype == CTSTR)
tr = types[TSTRING];
if(tr == T)
goto no;
v = tr->width;
goto yes;
}
if(strcmp(s->name, "Offsetof") == 0) {
if(r->op != ODOT && r->op != ODOTPTR)
goto no;
walktype(r, Erv);
v = r->xoffset;
goto yes;
}
if(strcmp(s->name, "Alignof") == 0) {
walktype(r, Erv);
tr = r->type;
if(r->op == OLITERAL && r->val.ctype == CTSTR)
tr = types[TSTRING];
if(tr == T)
goto no;
// make struct { byte; T; }
t = typ(TSTRUCT);
t->type = typ(TFIELD);
t->type->type = types[TUINT8];
t->type->down = typ(TFIELD);
t->type->down->type = tr;
// compute struct widths
dowidth(t);
// the offset of T is its required alignment
v = t->type->down->width;
goto yes;
}
no:
return N;
yes:
addtop = N; // any side effects disappear
val.ctype = CTINT;
val.u.xval = mal(sizeof(*n->val.u.xval));
mpmovecfix(val.u.xval, v);
n = nod(OLITERAL, N, N);
n->val = val;
n->type = types[TINT];
return n;
}