*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
* } 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)
{
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;
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;
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++;
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);
// 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.
* return type to hang methods off (r).
*/
Type*
-dclmethod(Type *t)
+methtype(Type *t)
{
int ptr;
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) {
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)
{
{
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) {
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;
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;
continue;
if(f->sym == S)
continue;
- expand1(f->type, d-1);
+ expand1(f->type, d-1, followptr);
}
out:
// 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) {
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;
}
/*
- * 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,
+ *
+ * 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.
*
- * 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)
+ * 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);
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'])
}
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;
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;
}
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.
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;
}