1
0
mirror of https://github.com/golang/go synced 2024-11-22 07:34:40 -07:00

6g: new interface rules (code got simpler!)

R=ken
OCL=28374
CL=28378
This commit is contained in:
Russ Cox 2009-05-06 17:05:35 -07:00
parent d76f095750
commit a6ba5ec535
5 changed files with 121 additions and 178 deletions

View File

@ -129,7 +129,6 @@ updatetype(Type *n, Type *t)
n->sym = s;
n->local = local;
n->siggen = 0;
n->methptr = 0;
n->printed = 0;
n->method = nil;
n->vargen = 0;
@ -293,7 +292,7 @@ addmethod(Node *n, Type *t, int local)
if(pa == T)
goto bad;
f = dclmethod(pa);
f = methtype(pa);
if(f == T)
goto bad;

View File

@ -137,7 +137,6 @@ struct Type
uchar chan;
uchar recur; // to detect loops
uchar trecur; // to detect loops
uchar methptr; // 1=direct 2=pointer
uchar printed;
uchar embedded; // TFIELD embedded type
uchar siggen;
@ -725,9 +724,7 @@ int isinter(Type*);
int isnilinter(Type*);
int isddd(Type*);
Type* maptype(Type*, Type*);
Type* dclmethod(Type*);
Type* methtype(Type*);
int methconv(Type*);
Sym* signame(Type*);
int eqtype(Type*, Type*, int);
int eqtypenoname(Type*, Type*);
@ -787,10 +784,8 @@ int lookdot0(Sym*, Type*, Type**);
Type* lookdot1(Sym*, Type*, Type*);
int adddot1(Sym*, Type*, int, Type**);
Node* adddot(Node*);
void expand0(Type*);
void expand1(Type*, int);
void expandmeth(Sym*, Type*);
void genptrtramp(Sym*, Sym*, Type*, Type*, Sym*, Type*);
void genwrapper(Type*, Type*, Sym*);
/*
* dcl.c

View File

@ -140,6 +140,12 @@ ieeedtod(uint64 *ieee, double native)
*ieee = ((uint64)h << 32) | l;
}
static int
sigcmp(Sig *a, Sig *b)
{
return strcmp(a->name, b->name);
}
/*
* Add DATA for signature s.
* progt - type in program
@ -165,13 +171,6 @@ ieeedtod(uint64 *ieee, double native)
* } meth[1]; // one or more - last name is nil
* };
*/
static int
sigcmp(Sig *a, Sig *b)
{
return strcmp(a->name, b->name);
}
void
dumpsigt(Type *progt, Type *ifacet, Type *rcvrt, Type *methodt, Sym *s)
{
@ -180,12 +179,16 @@ dumpsigt(Type *progt, Type *ifacet, Type *rcvrt, Type *methodt, Sym *s)
Sig *a, *b;
char buf[NSYMB];
Type *this;
Iter savet;
Prog *oldlist;
Sym *method;
uint32 sighash;
int ot;
if(debug['r']) {
print("dumpsigt progt=%T ifacet=%T rcvrt=%T methodt=%T s=%S\n",
progt, ifacet, rcvrt, methodt, s);
}
a = nil;
o = 0;
oldlist = nil;
@ -201,6 +204,16 @@ dumpsigt(Type *progt, Type *ifacet, Type *rcvrt, Type *methodt, Sym *s)
if(method == nil)
continue;
// get receiver type for this particular method.
this = getthisx(f->type)->type->type;
if(f->embedded != 2 && isptr[this->etype] && !isptr[progt->etype]) {
// pointer receiver method but value method set.
// ignore.
if(debug['r'])
print("ignore %T for %T\n", f, progt);
continue;
}
b = mal(sizeof(*b));
b->link = a;
a = b;
@ -216,31 +229,19 @@ dumpsigt(Type *progt, Type *ifacet, Type *rcvrt, Type *methodt, Sym *s)
if(!a->sym->siggen) {
a->sym->siggen = 1;
// TODO(rsc): This test is still not quite right.
this = structfirst(&savet, getthis(f->type))->type;
if(isptr[this->etype] != isptr[ifacet->etype]) {
if(!eqtype(this, ifacet, 0)) {
if(oldlist == nil)
oldlist = pc;
// indirect vs direct mismatch
Sym *oldname, *newname;
Type *oldthis, *newthis;
newthis = ifacet;
if(isptr[newthis->etype])
oldthis = ifacet->type;
// It would be okay to call genwrapper here always,
// but we can generate more efficient code
// using genembedtramp if all that is necessary
// is a pointer adjustment and a JMP.
if(f->embedded && isptr[ifacet->etype])
genembedtramp(ifacet, a);
else
oldthis = ptrto(ifacet);
newname = a->sym;
oldname = methodsym(method, oldthis);
genptrtramp(method, oldname, oldthis, f->type, newname, newthis);
} else
if(f->embedded) {
// TODO(rsc): only works for pointer receivers
if(oldlist == nil)
oldlist = pc;
genembedtramp(ifacet, a);
genwrapper(ifacet, f, a->sym);
}
}
o++;
@ -449,23 +450,17 @@ dumpsignatures(void)
rcvrt = t;
// if there's a pointer, methods are on base.
if(isptr[methodt->etype] && methodt->type->sym != S) {
methodt = methodt->type;
methodt = methtype(progt);
if(methodt == T) {
// if that failed, go back to progt,
// assuming we're writing out a signature
// for a type with no methods
methodt = progt;
} else {
expandmeth(methodt->sym, methodt);
// if methodt had a name, we don't want to see
// it in the method names that go into the sigt.
// e.g., if
// type item *rat
// then item needs its own sigt distinct from *rat,
// but it needs to have all of *rat's methods, using
// the *rat (not item) in the method names.
if(rcvrt->sym != S)
rcvrt = ptrto(methodt);
}
// and if ifacet is too wide, the methods
// will see a pointer anyway.
// if ifacet is too wide, the methods will see a pointer.
if(ifacet->width > 8) {
ifacet = ptrto(progt);
rcvrt = ptrto(progt);
@ -473,7 +468,7 @@ dumpsignatures(void)
// don't emit non-trivial signatures for types defined outside this file.
// non-trivial signatures might also drag in generated trampolines,
// and ar can't handle duplicates of the trampolines.
// and ar can't handle duplicate functions.
// only pay attention to types with symbols, because
// the ... structs and maybe other internal structs
// don't get marked as local.

View File

@ -1495,7 +1495,7 @@ isddd(Type *t)
* return type to hang methods off (r).
*/
Type*
dclmethod(Type *t)
methtype(Type *t)
{
int ptr;
@ -1517,15 +1517,6 @@ dclmethod(Type *t)
if(t->sym == S)
return T;
// check that all method receivers are consistent
if(t->methptr != 0 && t->methptr != (1<<ptr)) {
if(t->methptr != 3) {
t->methptr = 3;
yyerror("methods on both %T and *%T", t, t);
}
}
t->methptr |= 1<<ptr;
// check types
if(!issimple[t->etype])
switch(t->etype) {
@ -1543,50 +1534,6 @@ dclmethod(Type *t)
return t;
}
/*
* this is dclmethod() without side effects.
*/
Type*
methtype(Type *t)
{
if(t == T)
return T;
if(isptr[t->etype]) {
if(t->sym != S)
return T;
t = t->type;
}
if(t == T || t->etype == TINTER || t->sym == S)
return T;
return t;
}
/*
* given type t in a method call, returns op
* to convert t into appropriate receiver.
* returns OADDR if t==x and method takes *x
* returns OIND if t==*x and method takes x
*/
int
methconv(Type *t)
{
Type *m;
m = methtype(t);
if(m == T)
return 0;
if(m->methptr&2) {
// want pointer
if(t == m)
return OADDR;
return 0;
}
// want non-pointer
if(t != m)
return OIND;
return 0;
}
int
iscomposite(Type *t)
{
@ -2604,19 +2551,22 @@ struct Symlink
{
Type* field;
uchar good;
uchar followptr;
Symlink* link;
};
static Symlink* slist;
void
expand0(Type *t)
static void
expand0(Type *t, int followptr)
{
Type *f, *u;
Symlink *sl;
u = t;
if(isptr[u->etype])
if(isptr[u->etype]) {
followptr = 1;
u = u->type;
}
u = methtype(t);
if(u != T) {
@ -2629,13 +2579,14 @@ expand0(Type *t)
sl = mal(sizeof(*sl));
sl->field = f;
sl->link = slist;
sl->followptr = followptr;
slist = sl;
}
}
}
void
expand1(Type *t, int d)
static void
expand1(Type *t, int d, int followptr)
{
Type *f, *u;
@ -2646,11 +2597,13 @@ expand1(Type *t, int d)
t->trecur = 1;
if(d != nelem(dotlist)-1)
expand0(t);
expand0(t, followptr);
u = t;
if(isptr[u->etype])
if(isptr[u->etype]) {
followptr = 1;
u = u->type;
}
if(u->etype != TSTRUCT && u->etype != TINTER)
goto out;
@ -2659,7 +2612,7 @@ expand1(Type *t, int d)
continue;
if(f->sym == S)
continue;
expand1(f->type, d-1);
expand1(f->type, d-1, followptr);
}
out:
@ -2682,7 +2635,7 @@ expandmeth(Sym *s, Type *t)
// generate all reachable methods
slist = nil;
expand1(t, nelem(dotlist)-1);
expand1(t, nelem(dotlist)-1, 0);
// check each method to be uniquely reachable
for(sl=slist; sl!=nil; sl=sl->link) {
@ -2704,7 +2657,8 @@ expandmeth(Sym *s, Type *t)
f = typ(TFIELD);
*f = *sl->field;
f->embedded = 1; // needs a trampoline
if(sl->followptr)
f->embedded = 2;
f->down = t->method;
t->method = f;
@ -2742,43 +2696,46 @@ structargs(Type **tl, int mustname)
}
/*
* Generate a trampoline to convert
* from an indirect receiver to a direct receiver
* or vice versa.
* Generate a wrapper function to convert from
* a receiver of type T to a receiver of type U.
* That is,
*
* method - short name of method (Len)
* oldname - old mangled method name (x·y·Len)
* oldthis - old this type (y)
* oldtype - type of method being called;
* only in and out params are known okay,
* receiver might be != oldthis.
* newnam [sic] - new mangled method name (x·*y·Len)
* newthis - new this type (*y)
* func (t T) M() {
* ...
* }
*
* already exists; this function generates
*
* func (u U) M() {
* u.M()
* }
*
* where the types T and U are such that u.M() is valid
* and calls the T.M method.
* The resulting function is for use in method tables.
*
* rcvrtype - U
* method - M func (t T)(), a TFIELD type struct
* newnam - the eventual mangled name of this function
*/
void
genptrtramp(Sym *method, Sym *oldname, Type *oldthis, Type *oldtype, Sym *newnam, Type *newthis)
genwrapper(Type *rcvrtype, Type *method, Sym *newnam)
{
Node *fn, *args, *l, *in, *call, *out, *this, *rcvr, *meth;
Node *this, *in, *out, *fn, *args, *call;
Node *l;
Iter savel;
if(debug['r']) {
print("\ngenptrtramp method=%S oldname=%S oldthis=%T\n",
method, oldname, oldthis);
print("\toldtype=%T newnam=%S newthis=%T\n",
oldtype, newnam, newthis);
print("genwrapper rcvrtype=%T method=%T newnam=%S\n",
rcvrtype, method, newnam);
}
dclcontext = PEXTERN;
markdcl();
this = nametodcl(newname(lookup(".this")), newthis);
in = structargs(getinarg(oldtype), 1);
out = structargs(getoutarg(oldtype), 0);
// fix up oldtype
markdcl();
oldtype = functype(nametodcl(newname(lookup(".this")), oldthis), in, out);
popdcl();
this = nametodcl(newname(lookup(".this")), rcvrtype);
in = structargs(getinarg(method->type), 1);
out = structargs(getoutarg(method->type), 0);
fn = nod(ODCLFUNC, N, N);
fn->nname = newname(newnam);
@ -2791,19 +2748,10 @@ genptrtramp(Sym *method, Sym *oldname, Type *oldthis, Type *oldtype, Sym *newnam
args = list(args, l->left);
args = rev(args);
// method to call
if(isptr[oldthis->etype])
rcvr = nod(OADDR, this->left, N);
else
rcvr = nod(OIND, this->left, N);
gettype(rcvr, N);
meth = nod(ODOTMETH, rcvr, newname(oldname));
meth->xoffset = BADWIDTH; // TODO(rsc): necessary?
meth->type = oldtype;
call = nod(OCALL, meth, args);
// generate call
call = nod(OCALL, adddot(nod(ODOT, this->left, newname(method->sym))), args);
fn->nbody = call;
if(oldtype->outtuple > 0)
if(method->type->outtuple > 0)
fn->nbody = nod(ORETURN, call, N);
if(debug['r'])
@ -2850,11 +2798,13 @@ ifacecheck(Type *dst, Type *src, int lineno, int explicit)
}
Type*
ifacelookdot(Sym *s, Type *t)
ifacelookdot(Sym *s, Type *t, int *followptr)
{
int c, d;
int i, c, d;
Type *m;
*followptr = 0;
if(t == T)
return T;
@ -2864,8 +2814,15 @@ ifacelookdot(Sym *s, Type *t)
yyerror("%T.%S is ambiguous", t, s);
return T;
}
if(c == 1)
if(c == 1) {
for(i=0; i<d; i++) {
if(isptr[dotlist[i].field->type->etype]) {
*followptr = 1;
break;
}
}
return m;
}
}
return T;
}
@ -2875,23 +2832,11 @@ ifacelookdot(Sym *s, Type *t)
int
ifaceokT2I(Type *t0, Type *iface, Type **m)
{
Type *t, *im, *tm;
int imhash;
Type *t, *im, *tm, *rcvr;
int imhash, followptr;
t = methtype(t0);
// stopgap: check for
// non-pointer type in T2I, methods want pointers.
// supposed to do something better eventually
// but this will catch errors while we decide the
// details of the "better" solution.
// only warn if iface is not interface{}.
if(t == t0 && t->methptr == 2 && iface->type != T) {
yyerror("probably wanted *%T not %T", t, t);
*m = iface->type;
return 0;
}
// if this is too slow,
// could sort these first
// and then do one loop.
@ -2905,11 +2850,20 @@ ifaceokT2I(Type *t0, Type *iface, Type **m)
for(im=iface->type; im; im=im->down) {
imhash = typehash(im, 0, 0);
tm = ifacelookdot(im->sym, t);
tm = ifacelookdot(im->sym, t, &followptr);
if(tm == T || typehash(tm, 0, 0) != imhash) {
*m = im;
return 0;
}
// if pointer receiver in method,
// the method does not exist for value types.
rcvr = getthisx(tm->type)->type->type;
if(isptr[rcvr->etype] && !isptr[t0->etype] && !followptr) {
if(debug['r'])
yyerror("interface pointer mismatch");
*m = im;
return 0;
}
}
return 1;
}

View File

@ -1590,8 +1590,7 @@ lookdot1(Sym *s, Type *t, Type *f)
int
lookdot(Node *n, Type *t)
{
Type *f1, *f2, *tt;
int op;
Type *f1, *f2, *tt, *rcvr;
Sym *s;
s = n->right->sym;
@ -1618,18 +1617,19 @@ lookdot(Node *n, Type *t)
if(f2 != T) {
tt = n->left->type;
if((op = methconv(tt)) != 0) {
switch(op) {
case OADDR:
rcvr = getthisx(f2->type)->type->type;
if(!eqtype(rcvr, tt, 0)) {
if(rcvr->etype == tptr && eqtype(rcvr->type, tt, 0)) {
walktype(n->left, Elv);
addrescapes(n->left);
n->left = nod(OADDR, n->left, N);
n->left->type = ptrto(tt);
break;
case OIND:
} else if(tt->etype == tptr && eqtype(tt->type, rcvr, 0)) {
n->left = nod(OIND, n->left, N);
n->left->type = tt->type;
break;
} else {
// method is attached to wrong type?
fatal("method mismatch: %T for %T", rcvr, tt);
}
}
n->right = methodname(n->right, n->left->type);