summaryrefslogtreecommitdiff
path: root/src/cmd
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2010-09-28 13:43:50 -0400
committerRuss Cox <rsc@golang.org>2010-09-28 13:43:50 -0400
commit8dfaebb64408885d11bb50a0313960e5fa91fcb7 (patch)
tree2340387d89e05ebb5054ff3ba47fc18b50568e3c /src/cmd
parent76311722189984436f854c480f598cf266b15977 (diff)
downloadgo-8dfaebb64408885d11bb50a0313960e5fa91fcb7.tar.gz
gc: fix reflect table method receiver
Fixes issue 451. Fixes issue 770. R=ken2 CC=golang-dev http://codereview.appspot.com/2207045
Diffstat (limited to 'src/cmd')
-rw-r--r--src/cmd/gc/go.h2
-rw-r--r--src/cmd/gc/reflect.c59
-rw-r--r--src/cmd/gc/typecheck.c107
3 files changed, 133 insertions, 35 deletions
diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h
index 06bc57373..acbfde4ff 100644
--- a/src/cmd/gc/go.h
+++ b/src/cmd/gc/go.h
@@ -1010,7 +1010,7 @@ void walkrange(Node *n);
* reflect.c
*/
void dumptypestructs(void);
-Type* methodfunc(Type *f, int use_receiver);
+Type* methodfunc(Type *f, Type*);
Node* typename(Type *t);
Sym* typesym(Type *t);
diff --git a/src/cmd/gc/reflect.c b/src/cmd/gc/reflect.c
index 87b9b04ba..18b2a4fc6 100644
--- a/src/cmd/gc/reflect.c
+++ b/src/cmd/gc/reflect.c
@@ -100,16 +100,16 @@ lsort(Sig *l, int(*f)(Sig*, Sig*))
* return function type, receiver as first argument (or not).
*/
Type*
-methodfunc(Type *f, int use_receiver)
+methodfunc(Type *f, Type *receiver)
{
NodeList *in, *out;
Node *d;
Type *t;
in = nil;
- if(use_receiver) {
+ if(receiver) {
d = nod(ODCLFIELD, N, N);
- d->type = getthisx(f)->type->type;
+ d->type = receiver;
in = list(in, d);
}
for(t=getinargx(f)->type; t; t=t->down) {
@@ -185,8 +185,8 @@ methods(Type *t)
a->name = method->name;
a->isym = methodsym(method, it, 1);
a->tsym = methodsym(method, t, 0);
- a->type = methodfunc(f->type, 1);
- a->mtype = methodfunc(f->type, 0);
+ a->type = methodfunc(f->type, t);
+ a->mtype = methodfunc(f->type, nil);
if(!(a->isym->flags & SymSiggen)) {
a->isym->flags |= SymSiggen;
@@ -241,22 +241,27 @@ imethods(Type *t)
Sig *a, *all, *last;
int o;
Type *f;
+ Sym *method, *isym;
+ Prog *oldlist;
all = nil;
last = nil;
o = 0;
+ oldlist = nil;
for(f=t->type; f; f=f->down) {
if(f->etype != TFIELD)
fatal("imethods: not field");
if(f->type->etype != TFUNC || f->sym == nil)
continue;
+ method = f->sym;
a = mal(sizeof(*a));
- a->name = f->sym->name;
- if(!exportname(f->sym->name))
- a->pkg = f->sym->pkg;
+ a->name = method->name;
+ if(!exportname(method->name))
+ a->pkg = method->pkg;
a->mtype = f->type;
a->offset = 0;
- a->type = methodfunc(f->type, 0);
+ a->type = methodfunc(f->type, nil);
+
if(last && sigcmp(last, a) >= 0)
fatal("sigcmp vs sortinter %s %s", last->name, a->name);
if(last == nil)
@@ -264,7 +269,43 @@ imethods(Type *t)
else
last->link = a;
last = a;
+
+ // Compiler can only refer to wrappers for
+ // named interface types.
+ if(t->sym == S)
+ continue;
+
+ // NOTE(rsc): Perhaps an oversight that
+ // IfaceType.Method is not in the reflect data.
+ // Generate the method body, so that compiled
+ // code can refer to it.
+ isym = methodsym(method, t, 0);
+ if(!(isym->flags & SymSiggen)) {
+ isym->flags |= SymSiggen;
+ if(oldlist == nil)
+ oldlist = pc;
+ genwrapper(t, f, isym, 0);
+ }
+
+ // Generate wrapper for pointer to interface type.
+ isym = methodsym(method, ptrto(t), 0);
+ if(!(isym->flags & SymSiggen)) {
+ isym->flags |= SymSiggen;
+ if(oldlist == nil)
+ oldlist = pc;
+ genwrapper(ptrto(t), f, isym, 0);
+ }
}
+
+ if(oldlist) {
+ // old list ended with AEND; change to ANOP
+ // so that the trampolines that follow can be found.
+ nopout(oldlist);
+
+ // start new data list
+ newplist();
+ }
+
return all;
}
diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c
index 1c736d432..821d540fa 100644
--- a/src/cmd/gc/typecheck.c
+++ b/src/cmd/gc/typecheck.c
@@ -17,6 +17,7 @@ static void implicitstar(Node**);
static int onearg(Node*, char*, ...);
static int twoarg(Node*);
static int lookdot(Node*, Type*, int);
+static int looktypedot(Node*, Type*, int);
static void typecheckaste(int, int, Type*, NodeList*, char*);
static Type* lookdot1(Sym *s, Type *t, Type *f, int);
static int nokeys(NodeList*);
@@ -497,41 +498,42 @@ reswitch:
yyerror("rhs of . must be a name"); // impossible
goto error;
}
- if(isptr[t->etype]) {
- t = t->type;
- if(t == T)
- goto error;
- n->op = ODOTPTR;
- checkwidth(t);
- }
sym = n->right->sym;
- if(!lookdot(n, t, 0)) {
- if(lookdot(n, t, 1))
- yyerror("%#N undefined (cannot refer to unexported field %S)", n, n->right->sym);
- else
- yyerror("%#N undefined (type %T has no field %S)", n, t, n->right->sym);
- goto error;
- }
if(l->op == OTYPE) {
- if(n->type->etype != TFUNC || n->type->thistuple != 1) {
- yyerror("type %T has no method %hS", n->left->type, sym);
- n->type = T;
+ if(!looktypedot(n, t, 0)) {
+ if(looktypedot(n, t, 1))
+ yyerror("%#N undefined (cannot refer to unexported method %S)", n, n->right->sym);
+ else
+ yyerror("%#N undefined (type %T has no method %S)", n, t, n->right->sym);
goto error;
}
- if(t->etype == TINTER) {
- yyerror("method expression on interface not implemented");
+ if(n->type->etype != TFUNC || n->type->thistuple != 1) {
+ yyerror("type %T has no method %hS", n->left->type, sym);
n->type = T;
goto error;
}
n->op = ONAME;
n->sym = methodsym(sym, l->type, 0);
- n->type = methodfunc(n->type, 1);
+ n->type = methodfunc(n->type, l->type);
n->xoffset = 0;
- getinargx(n->type)->type->type = l->type; // fix up receiver
n->class = PFUNC;
ok = Erv;
goto ret;
}
+ if(isptr[t->etype]) {
+ t = t->type;
+ if(t == T)
+ goto error;
+ n->op = ODOTPTR;
+ checkwidth(t);
+ }
+ if(!lookdot(n, t, 0)) {
+ if(lookdot(n, t, 1))
+ yyerror("%#N undefined (cannot refer to unexported field or method %S)", n, n->right->sym);
+ else
+ yyerror("%#N undefined (type %T has no field or method %S)", n, t, n->right->sym);
+ goto error;
+ }
switch(n->op) {
case ODOTINTER:
case ODOTMETH:
@@ -1382,6 +1384,55 @@ lookdot1(Sym *s, Type *t, Type *f, int dostrcmp)
}
static int
+looktypedot(Node *n, Type *t, int dostrcmp)
+{
+ Type *f1, *f2, *tt;
+ Sym *s;
+
+ s = n->right->sym;
+
+ if(t->etype == TINTER) {
+ f1 = lookdot1(s, t, t->type, dostrcmp);
+ if(f1 == T)
+ return 0;
+
+ if(f1->width == BADWIDTH)
+ fatal("lookdot badwidth %T %p", f1, f1);
+ n->right = methodname(n->right, t);
+ n->xoffset = f1->width;
+ n->type = f1->type;
+ n->op = ODOTINTER;
+ return 1;
+ }
+
+ tt = t;
+ if(t->sym == S && isptr[t->etype])
+ tt = t->type;
+
+ f2 = methtype(tt);
+ if(f2 == T)
+ return 0;
+
+ expandmeth(f2->sym, f2);
+ f2 = lookdot1(s, f2, f2->xmethod, dostrcmp);
+
+ // disallow T.m if m requires *T receiver
+ if(isptr[getthisx(f2->type)->type->type->etype]
+ && !isptr[t->etype]
+ && f2->embedded != 2
+ && !isifacemethod(f2->type)) {
+ yyerror("invalid method expression %#N (needs pointer receiver: (*%T).%s)", n, t, f2->sym->name);
+ return 0;
+ }
+
+ n->right = methodname(n->right, t);
+ n->xoffset = f2->width;
+ n->type = f2->type;
+ n->op = ODOTMETH;
+ return 1;
+}
+
+static int
lookdot(Node *n, Type *t, int dostrcmp)
{
Type *f1, *f2, *tt, *rcvr;
@@ -1394,9 +1445,15 @@ lookdot(Node *n, Type *t, int dostrcmp)
if(t->etype == TSTRUCT || t->etype == TINTER)
f1 = lookdot1(s, t, t->type, dostrcmp);
- f2 = methtype(n->left->type);
- if(f2 != T)
- f2 = lookdot1(s, f2, f2->method, dostrcmp);
+ f2 = T;
+ if(n->left->type == t || n->left->type->sym == S) {
+ f2 = methtype(t);
+ if(f2 != T) {
+ // Use f2->method, not f2->xmethod: adddot has
+ // already inserted all the necessary embedded dots.
+ f2 = lookdot1(s, f2, f2->method, dostrcmp);
+ }
+ }
if(f1 != T) {
if(f2 != T)
@@ -1420,7 +1477,7 @@ lookdot(Node *n, Type *t, int dostrcmp)
tt = n->left->type;
dowidth(tt);
rcvr = getthisx(f2->type)->type->type;
- if(n->left->op != OTYPE && !eqtype(rcvr, tt)) {
+ if(!eqtype(rcvr, tt)) {
if(rcvr->etype == tptr && eqtype(rcvr->type, tt)) {
checklvalue(n->left, "call pointer method on");
addrescapes(n->left);