1
0
mirror of https://github.com/golang/go synced 2024-11-26 00:38:00 -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->sym = s;
n->local = local; n->local = local;
n->siggen = 0; n->siggen = 0;
n->methptr = 0;
n->printed = 0; n->printed = 0;
n->method = nil; n->method = nil;
n->vargen = 0; n->vargen = 0;
@ -293,7 +292,7 @@ addmethod(Node *n, Type *t, int local)
if(pa == T) if(pa == T)
goto bad; goto bad;
f = dclmethod(pa); f = methtype(pa);
if(f == T) if(f == T)
goto bad; goto bad;

View File

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

View File

@ -140,6 +140,12 @@ ieeedtod(uint64 *ieee, double native)
*ieee = ((uint64)h << 32) | l; *ieee = ((uint64)h << 32) | l;
} }
static int
sigcmp(Sig *a, Sig *b)
{
return strcmp(a->name, b->name);
}
/* /*
* Add DATA for signature s. * Add DATA for signature s.
* progt - type in program * progt - type in program
@ -165,13 +171,6 @@ ieeedtod(uint64 *ieee, double native)
* } meth[1]; // one or more - last name is nil * } meth[1]; // one or more - last name is nil
* }; * };
*/ */
static int
sigcmp(Sig *a, Sig *b)
{
return strcmp(a->name, b->name);
}
void void
dumpsigt(Type *progt, Type *ifacet, Type *rcvrt, Type *methodt, Sym *s) 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; Sig *a, *b;
char buf[NSYMB]; char buf[NSYMB];
Type *this; Type *this;
Iter savet;
Prog *oldlist; Prog *oldlist;
Sym *method; Sym *method;
uint32 sighash; uint32 sighash;
int ot; 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; a = nil;
o = 0; o = 0;
oldlist = nil; oldlist = nil;
@ -201,6 +204,16 @@ dumpsigt(Type *progt, Type *ifacet, Type *rcvrt, Type *methodt, Sym *s)
if(method == nil) if(method == nil)
continue; 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 = mal(sizeof(*b));
b->link = a; b->link = a;
a = b; a = b;
@ -216,31 +229,19 @@ dumpsigt(Type *progt, Type *ifacet, Type *rcvrt, Type *methodt, Sym *s)
if(!a->sym->siggen) { if(!a->sym->siggen) {
a->sym->siggen = 1; a->sym->siggen = 1;
// TODO(rsc): This test is still not quite right.
this = structfirst(&savet, getthis(f->type))->type; if(!eqtype(this, ifacet, 0)) {
if(isptr[this->etype] != isptr[ifacet->etype]) {
if(oldlist == nil) if(oldlist == nil)
oldlist = pc; oldlist = pc;
// indirect vs direct mismatch // It would be okay to call genwrapper here always,
Sym *oldname, *newname; // but we can generate more efficient code
Type *oldthis, *newthis; // using genembedtramp if all that is necessary
// is a pointer adjustment and a JMP.
newthis = ifacet; if(f->embedded && isptr[ifacet->etype])
if(isptr[newthis->etype])
oldthis = ifacet->type;
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); genembedtramp(ifacet, a);
else
genwrapper(ifacet, f, a->sym);
} }
} }
o++; o++;
@ -449,23 +450,17 @@ dumpsignatures(void)
rcvrt = t; rcvrt = t;
// if there's a pointer, methods are on base. // if there's a pointer, methods are on base.
if(isptr[methodt->etype] && methodt->type->sym != S) { methodt = methtype(progt);
methodt = methodt->type; 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); 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 // if ifacet is too wide, the methods will see a pointer.
// will see a pointer anyway.
if(ifacet->width > 8) { if(ifacet->width > 8) {
ifacet = ptrto(progt); ifacet = ptrto(progt);
rcvrt = ptrto(progt); rcvrt = ptrto(progt);
@ -473,7 +468,7 @@ dumpsignatures(void)
// don't emit non-trivial signatures for types defined outside this file. // don't emit non-trivial signatures for types defined outside this file.
// non-trivial signatures might also drag in generated trampolines, // 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 // only pay attention to types with symbols, because
// the ... structs and maybe other internal structs // the ... structs and maybe other internal structs
// don't get marked as local. // don't get marked as local.

View File

@ -1495,7 +1495,7 @@ isddd(Type *t)
* return type to hang methods off (r). * return type to hang methods off (r).
*/ */
Type* Type*
dclmethod(Type *t) methtype(Type *t)
{ {
int ptr; int ptr;
@ -1517,15 +1517,6 @@ dclmethod(Type *t)
if(t->sym == S) if(t->sym == S)
return T; 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 // check types
if(!issimple[t->etype]) if(!issimple[t->etype])
switch(t->etype) { switch(t->etype) {
@ -1543,50 +1534,6 @@ dclmethod(Type *t)
return 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 int
iscomposite(Type *t) iscomposite(Type *t)
{ {
@ -2604,19 +2551,22 @@ struct Symlink
{ {
Type* field; Type* field;
uchar good; uchar good;
uchar followptr;
Symlink* link; Symlink* link;
}; };
static Symlink* slist; static Symlink* slist;
void static void
expand0(Type *t) expand0(Type *t, int followptr)
{ {
Type *f, *u; Type *f, *u;
Symlink *sl; Symlink *sl;
u = t; u = t;
if(isptr[u->etype]) if(isptr[u->etype]) {
followptr = 1;
u = u->type; u = u->type;
}
u = methtype(t); u = methtype(t);
if(u != T) { if(u != T) {
@ -2629,13 +2579,14 @@ expand0(Type *t)
sl = mal(sizeof(*sl)); sl = mal(sizeof(*sl));
sl->field = f; sl->field = f;
sl->link = slist; sl->link = slist;
sl->followptr = followptr;
slist = sl; slist = sl;
} }
} }
} }
void static void
expand1(Type *t, int d) expand1(Type *t, int d, int followptr)
{ {
Type *f, *u; Type *f, *u;
@ -2646,11 +2597,13 @@ expand1(Type *t, int d)
t->trecur = 1; t->trecur = 1;
if(d != nelem(dotlist)-1) if(d != nelem(dotlist)-1)
expand0(t); expand0(t, followptr);
u = t; u = t;
if(isptr[u->etype]) if(isptr[u->etype]) {
followptr = 1;
u = u->type; u = u->type;
}
if(u->etype != TSTRUCT && u->etype != TINTER) if(u->etype != TSTRUCT && u->etype != TINTER)
goto out; goto out;
@ -2659,7 +2612,7 @@ expand1(Type *t, int d)
continue; continue;
if(f->sym == S) if(f->sym == S)
continue; continue;
expand1(f->type, d-1); expand1(f->type, d-1, followptr);
} }
out: out:
@ -2682,7 +2635,7 @@ expandmeth(Sym *s, Type *t)
// generate all reachable methods // generate all reachable methods
slist = nil; slist = nil;
expand1(t, nelem(dotlist)-1); expand1(t, nelem(dotlist)-1, 0);
// check each method to be uniquely reachable // check each method to be uniquely reachable
for(sl=slist; sl!=nil; sl=sl->link) { for(sl=slist; sl!=nil; sl=sl->link) {
@ -2704,7 +2657,8 @@ expandmeth(Sym *s, Type *t)
f = typ(TFIELD); f = typ(TFIELD);
*f = *sl->field; *f = *sl->field;
f->embedded = 1; // needs a trampoline f->embedded = 1; // needs a trampoline
if(sl->followptr)
f->embedded = 2;
f->down = t->method; f->down = t->method;
t->method = f; t->method = f;
@ -2742,43 +2696,46 @@ structargs(Type **tl, int mustname)
} }
/* /*
* Generate a trampoline to convert * Generate a wrapper function to convert from
* from an indirect receiver to a direct receiver * a receiver of type T to a receiver of type U.
* or vice versa. * That is,
* *
* method - short name of method (Len) * func (t T) M() {
* 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, * already exists; this function generates
* receiver might be != oldthis. *
* newnam [sic] - new mangled method name (x·*y·Len) * func (u U) M() {
* newthis - new this type (*y) * 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 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; Iter savel;
if(debug['r']) { if(debug['r']) {
print("\ngenptrtramp method=%S oldname=%S oldthis=%T\n", print("genwrapper rcvrtype=%T method=%T newnam=%S\n",
method, oldname, oldthis); rcvrtype, method, newnam);
print("\toldtype=%T newnam=%S newthis=%T\n",
oldtype, newnam, newthis);
} }
dclcontext = PEXTERN; dclcontext = PEXTERN;
markdcl(); markdcl();
this = nametodcl(newname(lookup(".this")), newthis); this = nametodcl(newname(lookup(".this")), rcvrtype);
in = structargs(getinarg(oldtype), 1); in = structargs(getinarg(method->type), 1);
out = structargs(getoutarg(oldtype), 0); out = structargs(getoutarg(method->type), 0);
// fix up oldtype
markdcl();
oldtype = functype(nametodcl(newname(lookup(".this")), oldthis), in, out);
popdcl();
fn = nod(ODCLFUNC, N, N); fn = nod(ODCLFUNC, N, N);
fn->nname = newname(newnam); 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 = list(args, l->left);
args = rev(args); args = rev(args);
// method to call // generate call
if(isptr[oldthis->etype]) call = nod(OCALL, adddot(nod(ODOT, this->left, newname(method->sym))), args);
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);
fn->nbody = call; fn->nbody = call;
if(oldtype->outtuple > 0) if(method->type->outtuple > 0)
fn->nbody = nod(ORETURN, call, N); fn->nbody = nod(ORETURN, call, N);
if(debug['r']) if(debug['r'])
@ -2850,11 +2798,13 @@ ifacecheck(Type *dst, Type *src, int lineno, int explicit)
} }
Type* Type*
ifacelookdot(Sym *s, Type *t) ifacelookdot(Sym *s, Type *t, int *followptr)
{ {
int c, d; int i, c, d;
Type *m; Type *m;
*followptr = 0;
if(t == T) if(t == T)
return T; return T;
@ -2864,9 +2814,16 @@ ifacelookdot(Sym *s, Type *t)
yyerror("%T.%S is ambiguous", t, s); yyerror("%T.%S is ambiguous", t, s);
return T; 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 m;
} }
}
return T; return T;
} }
@ -2875,23 +2832,11 @@ ifacelookdot(Sym *s, Type *t)
int int
ifaceokT2I(Type *t0, Type *iface, Type **m) ifaceokT2I(Type *t0, Type *iface, Type **m)
{ {
Type *t, *im, *tm; Type *t, *im, *tm, *rcvr;
int imhash; int imhash, followptr;
t = methtype(t0); 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, // if this is too slow,
// could sort these first // could sort these first
// and then do one loop. // and then do one loop.
@ -2905,11 +2850,20 @@ ifaceokT2I(Type *t0, Type *iface, Type **m)
for(im=iface->type; im; im=im->down) { for(im=iface->type; im; im=im->down) {
imhash = typehash(im, 0, 0); imhash = typehash(im, 0, 0);
tm = ifacelookdot(im->sym, t); tm = ifacelookdot(im->sym, t, &followptr);
if(tm == T || typehash(tm, 0, 0) != imhash) { if(tm == T || typehash(tm, 0, 0) != imhash) {
*m = im; *m = im;
return 0; 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; return 1;
} }

View File

@ -1590,8 +1590,7 @@ lookdot1(Sym *s, Type *t, Type *f)
int int
lookdot(Node *n, Type *t) lookdot(Node *n, Type *t)
{ {
Type *f1, *f2, *tt; Type *f1, *f2, *tt, *rcvr;
int op;
Sym *s; Sym *s;
s = n->right->sym; s = n->right->sym;
@ -1618,18 +1617,19 @@ lookdot(Node *n, Type *t)
if(f2 != T) { if(f2 != T) {
tt = n->left->type; tt = n->left->type;
if((op = methconv(tt)) != 0) { rcvr = getthisx(f2->type)->type->type;
switch(op) { if(!eqtype(rcvr, tt, 0)) {
case OADDR: if(rcvr->etype == tptr && eqtype(rcvr->type, tt, 0)) {
walktype(n->left, Elv); walktype(n->left, Elv);
addrescapes(n->left); addrescapes(n->left);
n->left = nod(OADDR, n->left, N); n->left = nod(OADDR, n->left, N);
n->left->type = ptrto(tt); n->left->type = ptrto(tt);
break; } else if(tt->etype == tptr && eqtype(tt->type, rcvr, 0)) {
case OIND:
n->left = nod(OIND, n->left, N); n->left = nod(OIND, n->left, N);
n->left->type = tt->type; 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); n->right = methodname(n->right, n->left->type);