blob: 6cf10e16d2e1f2b1cda724f555f7cd11abbc4868 [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"
void
dodclvar(Node *n, Node *t)
{
loop:
if(n == N)
return;
if(n->op == OLIST) {
dodclvar(n->left, t);
n = n->right;
goto loop;
}
addvar(n, t, dclcontext);
}
void
dodcltype(Node *n, Node *t)
{
loop:
if(n == N)
return;
if(n->op == OLIST) {
dodcltype(n->left, t);
n = n->right;
goto loop;
}
addtyp(n, t, dclcontext);
}
void
dodclconst(Node *n, Node *e)
{
Sym *s;
Dcl *r, *d;
loop:
if(n == N)
return;
if(n->op == OLIST) {
dodclconst(n->left, e);
n = n->right;
goto loop;
}
if(n->op != ONAME)
fatal("dodclconst: not a name");
if(e->op != OLITERAL) {
yyerror("expression must be a constant");
goto loop;
}
s = n->sym;
s->oconst = e;
s->lexical = LACONST;
r = autodcl;
if(dclcontext == PEXTERN)
r = externdcl;
d = dcl();
d->dsym = s;
d->dnode = e;
d->op = OCONST;
r->back->forw = d;
r->back = d;
if(debug['d'])
print("const-dcl %S %N\n", n->sym, n->sym->oconst);
}
/*
* return nelem of list
*/
int
listcount(Node *n)
{
int v;
v = 0;
while(n != N) {
v++;
if(n->op != OLIST)
break;
n = n->right;
}
return v;
}
/*
* turn a parsed function declaration
* into a type
*/
Node*
functype(Node *this, Node *in, Node *out)
{
Node *t;
t = nod(OTYPE, N, N);
t->etype = TFUNC;
t->type = dostruct(this, TSTRUCT);
t->type->down = dostruct(out, TSTRUCT);
t->type->down->down = dostruct(in, TSTRUCT);
t->thistuple = listcount(this);
t->outtuple = listcount(out);
t->intuple = listcount(in);
return t;
}
void
funcnam(Node *t, char *nam)
{
Node *n;
Sym *s;
char buf[100];
if(nam == nil) {
vargen++;
snprint(buf, sizeof(buf), "_f%.3ld", vargen);
nam = buf;
}
if(t->etype != TFUNC)
fatal("funcnam: not func %T\n", t);
if(t->thistuple > 0) {
vargen++;
snprint(namebuf, sizeof(namebuf), "_t%.3ld", vargen);
s = lookup(namebuf);
addtyp(newtype(s), t->type, PEXTERN);
n = newname(s);
n->vargen = vargen;
t->type->nname = n;
}
if(t->outtuple > 0) {
vargen++;
snprint(namebuf, sizeof(namebuf), "_o%.3ld", vargen);
s = lookup(namebuf);
addtyp(newtype(s), t->type->down, PEXTERN);
n = newname(s);
n->vargen = vargen;
t->type->down->nname = n;
}
if(t->intuple > 0) {
vargen++;
snprint(namebuf, sizeof(namebuf), "_i%.3ld", vargen);
s = lookup(namebuf);
addtyp(newtype(s), t->type->down->down, PEXTERN);
n = newname(s);
n->vargen = vargen;
t->type->down->down->nname = n;
}
}
int
methcmp(Node *t1, Node *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 == N || t2 == N)
return 0;
if(t1->etype != TSTRUCT || t2->etype != TSTRUCT)
return 0;
if(!eqtype(t1->type, t2->type, 0))
return 0;
t1 = t1->down;
t2 = t2->down;
}
return 1;
}
/*
* add a method, declared as a function,
* into the structure
*/
void
addmethod(Node *n, Node *pa, Node *t)
{
Node *p, *f, *d;
Sym *s;
if(n->op != ONAME)
goto bad;
s = n->sym;
if(s == S)
goto bad;
if(pa == N)
goto bad;
if(pa->etype != TPTR)
goto bad;
p = pa->type;
if(p == N)
goto bad;
if(p->etype != TSTRUCT)
goto bad;
if(p->sym == S)
goto bad;
if(p->type == N) {
n = nod(ODCLFIELD, newname(s), N);
n->type = t;
stotype(n, &p->type, p);
return;
}
d = N; // last found
for(f=p->type; f!=N; f=f->down) {
if(f->etype != TFIELD)
fatal("addmethod: not TFIELD: %N", f);
if(strcmp(s->name, f->sym->name) != 0) {
d = f;
continue;
}
// if a field matches a non-this function
// then delete it and let it be redeclared
if(methcmp(t, f->type)) {
if(d == N) {
p->type = f->down;
continue;
}
d->down = f->down;
continue;
}
if(!eqtype(t, f->type, 0))
yyerror("field redeclared as method: %S", s);
return;
}
n = nod(ODCLFIELD, newname(s), N);
n->type = t;
if(d == N)
stotype(n, &p->type, p);
else
stotype(n, &d->down, p);
return;
bad:
yyerror("unknown method pointer: %T", pa);
}
/*
* 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->oname;
// check for same types
if(on != N) {
if(eqtype(n->type, on->type, 0)) {
if(!eqargs(n->type, on->type))
yyerror("foreward declarations not the same: %S", s);
} else
yyerror("redeclare of function: %S", s);
}
// check for foreward declaration
if(on == N || !eqtype(n->type, on->type, 0)) {
// initial declaration or redeclaration
// declare fun name, argument types and argument names
funcnam(n->type, s->name);
n->nname->type = n->type;
if(n->type->thistuple == 0)
addvar(n->nname, n->type, PEXTERN);
} else {
// identical redeclaration
// steal previous names
n->nname = on;
n->type = on->type;
n->sym = s;
if(debug['d'])
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(dclcontext != PEXTERN)
fatal("funchdr: dclcontext");
dclcontext = PAUTO;
markdcl("func");
funcargs(n->type);
if(n->type->thistuple > 0) {
Node *n1;
n1 = *getthis(n->type);
addmethod(n->nname, n1->type->type, n->type);
}
}
void
funcargs(Node *t)
{
Node *n1;
Iter save;
// declare the this argument
n1 = structfirst(&save, getthis(t));
if(n1 != N) {
if(n1->nname != N)
addvar(n1->nname, n1->type, PAUTO);
}
// declare the incoming arguments
n1 = structfirst(&save, getinarg(t));
while(n1 != N) {
if(n1->nname != N)
addvar(n1->nname, n1->type, PAUTO);
n1 = structnext(&save);
}
// declare the outgoing arguments
// n1 = structfirst(&save, getoutarg(t));
// while(n1 != N) {
// n1->left = newname(n1->sym);
// if(n1->nname != N)
// addvar(n1->nname, n1->type, PAUTO);
// n1 = structnext(&save);
// }
}
/*
* 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");
dclcontext = PEXTERN;
popdcl("func");
}
/*
* turn a parsed struct into a type
*/
Node**
stotype(Node *n, Node **t, Node *uber)
{
Node *f;
Iter save;
n = listfirst(&save, &n);
loop:
if(n == N) {
*t = N;
return t;
}
if(n->op == OLIST) {
// recursive because it can be lists of lists
t = stotype(n, t, uber);
goto next;
}
if(n->op != ODCLFIELD || n->type == N)
fatal("stotype: oops %N\n", n);
if(n->type->etype == TDARRAY)
yyerror("type of a structure field cannot be an open array");
f = nod(OTYPE, N, N);
f->etype = TFIELD;
f->type = n->type;
f->uberstruct = uber;
if(n->left != N && n->left->op == ONAME) {
f->nname = n->left;
} else {
vargen++;
snprint(namebuf, sizeof(namebuf), "_e%.3ld", vargen);
f->nname = newname(lookup(namebuf));
}
f->sym = f->nname->sym;
f->nname->uberstruct = uber; // can reach parent from element
*t = f;
t = &f->down;
next:
n = listnext(&save);
goto loop;
}
Node*
dostruct(Node *n, int et)
{
Node *t;
/*
* convert a parsed id/type list into
* a type for struct/interface/arglist
*/
t = nod(OTYPE, N, N);
stotype(n, &t->type, t);
t->etype = et;
return t;
}
Node*
sortinter(Node *n)
{
return n;
}
void
dcopy(Sym *a, Sym *b)
{
a->name = b->name;
a->oname = b->oname;
a->otype = b->otype;
a->oconst = b->oconst;
a->package = b->package;
a->opackage = b->opackage;
a->forwtype = b->forwtype;
a->lexical = b->lexical;
a->undef = b->undef;
a->vargen = b->vargen;
}
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(char *why)
{
Sym *d, *s;
// if(debug['d'])
// 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(debug['d'])
print("\t%ld pop %S\n", curio.lineno, s);
}
if(d == S)
fatal("popdcl: no mark");
if(strcmp(why, d->package) != 0)
fatal("popdcl: pushed as %s poped as %s", d->package, why);
dclstack = d->link;
}
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(debug['d'])
print("\t%ld pop %S\n", curio.lineno, s);
}
if(d == S)
fatal("poptodcl: no mark");
}
void
markdcl(char *why)
{
Sym *d;
d = push();
d->name = nil; // used as a mark in fifo
d->package = why; // diagnostic for unmatched
// if(debug['d'])
// print("markdcl\n");
}
void
markdclstack(void)
{
Sym *d, *s;
markdcl("fnlit");
// copy the entire pop of the stack
// all the way back to block0.
// after this the symbol table is at
// block0 and popdcl will restore it.
for(d=dclstack; d!=S; d=d->link) {
if(d == b0stack)
break;
if(d->name != nil) {
s = pkglookup(d->name, d->package);
pushdcl(s);
dcopy(s, d);
}
}
}
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
addvar(Node *n, Node *t, int ctxt)
{
Dcl *r, *d;
Sym *s;
Node *on;
int gen;
if(n==N || n->sym == S || n->op != ONAME || t == N)
fatal("addvar: n=%N t=%N nil", n, t);
on = t;
if(on->etype == TPTR)
on = on->type;
if(on->etype == TSTRUCT && on->vargen == 0) {
vargen++;
snprint(namebuf, sizeof(namebuf), "_s%.3ld", vargen);
addtyp(newtype(lookup(namebuf)), on, PEXTERN);
}
s = n->sym;
vargen++;
gen = vargen;
r = autodcl;
if(ctxt == PEXTERN) {
on = s->oname;
if(on != N) {
if(eqtype(t, on->type, 0)) {
warn("%S redeclared", s);
return;
}
yyerror("%S redeclared (%T %T)", s,
on->type, t);
}
r = externdcl;
gen = 0;
}
pushdcl(s);
s->vargen = gen;
s->oname = n;
n->type = t;
n->vargen = gen;
d = dcl();
d->dsym = s;
d->dnode = n;
d->op = ONAME;
r->back->forw = d;
r->back = d;
if(debug['d']) {
if(ctxt == PEXTERN)
print("extern var-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(Node *n, Node *t, int ctxt)
{
Dcl *r, *d;
Sym *s;
Node *f, *ot;
if(n==N || n->sym == S || n->op != OTYPE || t == N)
fatal("addtyp: n=%N t=%N nil", n, t);
s = n->sym;
r = autodcl;
if(ctxt == PEXTERN) {
ot = s->otype;
if(ot != N) {
// allow nil interface to be
// redeclared as an interface
if(ot->etype == TINTER && ot->type == N && t->etype == TINTER) {
if(debug['d'])
print("forew typ-dcl %S G%ld %T\n", s, s->vargen, t);
s->otype = t;
return;
}
if(eqtype(t, ot, 0)) {
warn("%S redeclared", s);
return;
}
yyerror("%S redeclared (%T %T)", s,
ot, t);
}
r = externdcl;
}
pushdcl(s);
vargen++;
s->vargen = vargen;
s->otype = t;
s->lexical = LATYPE;
if(t->sym != S)
warn("addtyp: renaming %S to %S", t->sym, s);
t->sym = s;
t->vargen = vargen;
for(f=s->forwtype; f!=N; f=f->nforw) {
if(f->op != OTYPE && f->etype != TPTR)
fatal("addtyp: foreward");
f->type = t;
}
s->forwtype = N;
d = dcl();
d->dsym = s;
d->dnode = t;
d->op = OTYPE;
r->back->forw = d;
r->back = d;
if(debug['d']) {
if(ctxt == PEXTERN)
print("extern typ-dcl %S G%ld %T\n", s, s->vargen, t);
else
print("auto typ-dcl %S G%ld %T\n", s, s->vargen, t);
}
}
/*
* make a new variable
*/
Node*
tempname(Node *t)
{
Sym *s;
Node *n;
if(t == N) {
yyerror("tempname called with nil type");
t = types[TINT32];
}
s = lookup("!tmpname!");
n = newname(s);
dodclvar(n, t);
return n;
}
/*
* 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 = N;
n->addable = 1;
n->ullman = 0;
return n;
}
/*
* 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;
n = s->oname;
if(n == N) {
yyerror("%S undefined", s);
n = newname(s);
dodclvar(n, types[TINT32]);
}
return n;
}
/*
* same for types
*/
Node*
newtype(Sym *s)
{
Node *n;
n = nod(OTYPE, N, N);
n->etype = TFORW;
n->sym = s;
n->type = N;
return n;
}
Node*
oldtype(Sym *s)
{
Node *n;
n = s->otype;
if(n == N)
fatal("%S not a type", s); // cant happen
return n;
}
Node*
forwdcl(Sym *s)
{
Node *n;
// this type has no meaning and
// will cause an error if referenced.
// it will be patched when/if the
// type is ever assigned.
n = nod(OTYPE, N, N);
n->etype = TFORW;
n = ptrto(n);
n->nforw = s->forwtype;
s->forwtype = n;
return n;
}