1
0
mirror of https://github.com/golang/go synced 2024-10-03 23:21:22 -06:00
go/src/cmd/gc/dcl.c

1141 lines
18 KiB
C
Raw Normal View History

// 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"
2008-06-11 22:06:26 -06:00
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;
for(; n->op == OLIST; n = n->right)
dodclvar(n->left, t);
dowidth(t);
addvar(n, t, dclcontext);
if(exportadj)
exportsym(n->sym);
}
void
dodclconst(Node *n, Node *e)
{
if(n == N)
return;
for(; n->op == OLIST; n=n->right)
dodclconst(n, e);
addconst(n, e, dclcontext);
if(exportadj)
exportsym(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->tblock == block) {
switch(s->otype->etype) {
case TFORWSTRUCT:
case TFORWINTER:
return s->otype;
}
}
// otherwise declare a new type
addtyp(n, dclcontext);
n->sym->local = 1;
if(exportadj)
exportsym(n->sym);
return n;
}
/*
* now we know what n is: it's t
*/
void
updatetype(Type *n, Type *t)
{
Sym *s;
s = n->sym;
if(s == S || s->otype != 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);
}
*n = *t;
n->sym = s;
// catch declaration of incomplete type
switch(n->etype) {
case TFORWSTRUCT:
case TFORWINTER:
break;
default:
checkwidth(n);
}
}
/*
* 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
*/
Type*
functype(Node *this, Node *in, Node *out)
{
Type *t;
t = typ(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);
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, 0))
return 0;
t1 = t1->down;
t2 = t2->down;
}
return 1;
}
Node*
methodname(Node *n, Type *t)
{
Sym *s;
char buf[NSYMB];
// caller has already called ismethod to obtain t
if(t == T)
goto bad;
s = t->sym;
if(s == S)
goto bad;
snprint(buf, sizeof(buf), "%s_%s", s->name, n->sym->name);
return newname(pkglookup(buf, s->opackage));
bad:
yyerror("illegal <this> type: %T", t);
return n;
}
/*
* 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 *st, *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;
// and finally the receiver sym
f = ismethod(pa);
if(f == T)
goto bad;
pa = f;
st = pa->sym;
if(st == S)
goto bad;
if(local && !st->local) {
yyerror("method receiver type must be locally defined: %S", st);
return;
}
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, 0))
yyerror("method redeclared: %S of type %S", sf, st);
return;
}
if(d == T)
stotype(n, &pa->method);
else
stotype(n, &d->down);
if(dflag())
print("method %S of type %T\n", sf, pa);
return;
bad:
yyerror("unknown method pointer: %T %S", pa, sf);
}
2008-07-19 19:39:12 -06:00
/*
* 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, the name, normally "pkg.init", is
* altered to "pkg.<file>_init".
*/
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->oname;
// check for same types
if(on != N) {
if(eqtype(n->type, on->type, 0)) {
if(!eqargs(n->type, on->type))
yyerror("forward declarations not the same: %S", s);
} else {
yyerror("redeclare of function: %S", s);
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, PEXTERN);
else
n->nname->class = PEXTERN;
} else {
// identical redeclaration
// steal previous names
n->nname = on;
n->type = on->type;
n->class = on->class;
n->sym = s;
2008-06-11 22:06:26 -06:00
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(dclcontext != PEXTERN)
fatal("funchdr: dclcontext");
dclcontext = PAUTO;
markdcl();
funcargs(n->type);
}
void
2008-07-16 19:31:01 -06:00
funcargs(Type *ft)
{
2008-07-16 19:31:01 -06:00
Type *t;
Iter save;
int all;
// declare the this/in arguments
2008-07-16 19:31:01 -06:00
t = funcfirst(&save, ft);
while(t != T) {
if(t->nname != N) {
t->nname->xoffset = t->width;
2008-07-16 19:31:01 -06:00
addvar(t->nname, t->type, PPARAM);
}
2008-07-16 19:31:01 -06:00
t = funcnext(&save);
}
// declare the outgoing arguments
all = 0;
2008-07-16 19:31:01 -06:00
t = structfirst(&save, getoutarg(ft));
while(t != T) {
if(t->nname != N)
t->nname->xoffset = t->width;
2008-07-16 19:31:01 -06:00
if(t->nname != N && t->nname->sym->name[0] != '_') {
addvar(t->nname, t->type, PPARAM);
all |= 1;
} else
all |= 2;
2008-07-16 19:31:01 -06:00
t = structnext(&save);
}
// this test is remarkedly similar to checkarglist
if(all == 3)
yyerror("cannot mix anonymous and named output arguments");
2008-07-16 19:31:01 -06:00
ft->outnamed = 0;
if(all == 1)
2008-07-16 19:31:01 -06:00
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();
dclcontext = PEXTERN;
}
/*
* turn a parsed struct into a type
*/
Type**
stotype(Node *n, Type **t)
{
Type *f;
Iter save;
char buf[100];
n = listfirst(&save, &n);
loop:
if(n == N) {
*t = T;
return t;
}
if(n->op == OLIST) {
// recursive because it can be lists of lists
t = stotype(n, t);
goto next;
}
if(n->op != ODCLFIELD || n->type == T)
fatal("stotype: oops %N\n", n);
if(n->type->etype == TARRAY && n->type->bound < 0)
yyerror("type of a structure field cannot be an open array");
f = typ(TFIELD);
f->type = n->type;
if(n->left != N && n->left->op == ONAME) {
f->nname = n->left;
} else {
vargen++;
snprint(buf, sizeof(buf), "_e%s_%.3ld", filename, vargen);
f->nname = newname(lookup(buf));
}
f->sym = f->nname->sym;
*t = f;
t = &f->down;
next:
n = listnext(&save);
goto loop;
}
Type*
dostruct(Node *n, int et)
{
Type *t;
/*
* convert a parsed id/type list into
* a type for struct/interface/arglist
*/
t = typ(et);
stotype(n, &t->type);
checkwidth(t);
return t;
}
Type*
sortinter(Type *t)
{
return t;
}
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->lexical = b->lexical;
a->undef = b->undef;
a->vargen = b->vargen;
a->vblock = b->vblock;
a->tblock = b->tblock;
a->local = b->local;
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;
2008-06-11 22:06:26 -06:00
// 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);
2008-06-11 22:06:26 -06:00
if(dflag())
print("\t%L pop %S\n", lineno, s);
}
if(d == S)
fatal("popdcl: no mark");
dclstack = d->link;
block = d->vblock;
}
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);
2008-06-11 22:06:26 -06:00
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->vblock = block;
blockgen++;
block = blockgen;
2008-06-11 22:06:26 -06:00
// if(dflag())
// print("markdcl\n");
}
void
markdclstack(void)
{
Sym *d, *s;
markdcl();
// 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
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;
}
}
}
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(s->vblock == block) {
if(s->oname != N) {
yyerror("var %S redeclared in this block"
"\n previous declaration at %L",
s, s->oname->lineno);
} else
yyerror("var %S redeclared in this block", s);
}
if(ctxt == PEXTERN) {
r = externdcl;
gen = 0;
vargen++; // just for diffing output against old compiler
} else {
r = autodcl;
vargen++;
gen = vargen;
pushdcl(s);
}
s->vargen = gen;
s->oname = n;
s->offset = 0;
s->vblock = block;
s->lexical = LNAME;
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;
2008-06-11 22:06:26 -06:00
if(dflag()) {
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(Type *n, int ctxt)
{
Dcl *r, *d;
Sym *s;
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);
}
vargen++; // just for diffing output against old compiler
if(s->tblock == block)
yyerror("type %S redeclared in this block %d", s, block);
s->otype = n;
s->lexical = LATYPE;
s->tblock = block;
d = dcl();
d->dsym = s;
d->dtype = n;
d->op = OTYPE;
r->back->forw = d;
r->back = d;
2008-06-11 22:06:26 -06:00
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)
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);
}
s->oconst = e;
s->lexical = LACONST;
d = dcl();
d->dsym = s;
d->dnode = e;
d->op = OCONST;
r->back->forw = d;
r->back = d;
if(dflag())
print("const-dcl %S %N\n", n->sym, n->sym->oconst);
}
Node*
fakethis(void)
{
Node *n;
Type *t;
n = nod(ODCLFIELD, N, N);
t = dostruct(N, TSTRUCT);
t = ptrto(t);
n->type = 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 = T;
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) {
n = nod(ONONAME, N, N);
n->sym = s;
n->type = T;
n->addable = 1;
n->ullman = 0;
}
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;
t = s->otype;
if(t == T)
fatal("%S not a type", s); // cant happen
return t;
}
Type*
forwdcl(Sym *s)
{
Type *t;
// this type has no meaning and
// will cause an error if referenced.
// it will be patched when/if the
// type is ever assigned.
t = typ(TFORW);
t = ptrto(t);
return t;
}
2008-07-19 14:38:29 -06:00
/*
* 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);
if(n->sym->lexical != LATYPE && n->sym->lexical != LBASETYPE) {
yyerror("%s is not a type", n->sym->name);
t = typ(TINT32);
} else
t = oldtype(n->sym);
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;
}
/*
* check that the list of declarations is either all anonymous or all named
*/
void
checkarglist(Node *n)
{
if(n->op != OLIST)
return;
if(n->left->op != ODCLFIELD)
fatal("checkarglist");
if(n->left->left != N) {
for(n=n->right; n->op == OLIST; n=n->right)
if(n->left->left == N)
goto mixed;
if(n->left == N)
goto mixed;
} else {
for(n=n->right; n->op == OLIST; n=n->right)
if(n->left->left != N)
goto mixed;
if(n->left != N)
goto mixed;
}
return;
mixed:
yyerror("cannot mix anonymous and named function arguments");
}
2008-07-19 14:38:29 -06:00
// hand-craft the following initialization code
2008-07-19 19:39:12 -06:00
// var init_<file>_done bool; (1)
// func init_<file>_function() (2)
// if init_<file>_done { return } (3)
// init_<file>_done = true; (4)
// // over all matching imported symbols
// <pkg>.init_<file>_function() (5)
// { <init stmts> } (6)
2008-07-19 14:38:29 -06:00
// init() // if any (7)
// return (8)
// }
2008-07-19 19:39:12 -06:00
// export init_<file>_function (9)
2008-07-19 14:38:29 -06:00
void
fninit(Node *n)
{
Node *done;
Node *a, *fn, *r;
uint32 h;
2008-07-19 14:38:29 -06:00
Sym *s;
r = N;
// (1)
2008-07-19 19:39:12 -06:00
snprint(namebuf, sizeof(namebuf), "init_%s_done", filename);
2008-07-19 14:38:29 -06:00
done = newname(lookup(namebuf));
addvar(done, types[TBOOL], PEXTERN);
// (2)
maxarg = 0;
stksize = 0;
2008-07-19 15:20:46 -06:00
snprint(namebuf, sizeof(namebuf), "init_%s_function", filename);
// this is a botch since we need a known name to
// call the top level init function out of rt0
2008-07-19 14:38:29 -06:00
if(strcmp(package, "main") == 0)
2008-07-19 15:20:46 -06:00
snprint(namebuf, sizeof(namebuf), "init_function");
fn = nod(ODCLFUNC, N, N);
fn->nname = newname(lookup(namebuf));
fn->type = functype(N, N, N);
funchdr(fn);
2008-07-19 14:38:29 -06:00
// (3)
a = nod(OIF, N, N);
a->ntest = done;
a->nbody = nod(ORETURN, N, N);
r = list(r, a);
// (4)
a = nod(OAS, done, booltrue);
r = list(r, a);
// (5)
for(h=0; h<NHASH; h++)
for(s = hash[h]; s != S; s = s->link) {
if(s->name[0] != 'i')
continue;
2008-07-19 19:39:12 -06:00
if(strstr(s->name, "init_") == nil)
2008-07-19 14:38:29 -06:00
continue;
2008-07-19 19:39:12 -06:00
if(strstr(s->name, "_function") == nil)
2008-07-19 14:38:29 -06:00
continue;
if(s->oname == N)
continue;
// could check that it is fn of no args/returns
2008-07-19 14:38:29 -06:00
a = nod(OCALL, s->oname, N);
r = list(r, a);
}
// (6)
r = list(r, n);
// (7)
// could check that it is fn of no args/returns
2008-07-19 19:39:12 -06:00
snprint(namebuf, sizeof(namebuf), "init_%s", filename);
s = lookup(namebuf);
if(s->oname != N) {
a = nod(OCALL, s->oname, N);
2008-07-19 14:38:29 -06:00
r = list(r, a);
}
// (8)
a = nod(ORETURN, N, N);
r = list(r, a);
// (9)
exportsym(fn->nname->sym);
2008-07-19 14:38:29 -06:00
fn->nbody = rev(r);
//dump("b", fn);
//dump("r", fn->nbody);
2008-07-19 14:38:29 -06:00
popdcl();
compile(fn);
2008-07-19 14:38:29 -06:00
}
/*
* 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;
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)
{
if(defercalc)
fatal("defercheckwidth");
defercalc = 1;
}
void
resumecheckwidth(void)
{
TypeList *l, *next;
if(!defercalc)
fatal("restartcheckwidth");
defercalc = 0;
for(l = tlq; l != nil; l = tlq) {
dowidth(l->t);
tlq = l->next;
l->next = tlfree;
tlfree = l;
}
}