From 00ffd59c1a5a76bb4016b2ddd6fb78831eba8037 Mon Sep 17 00:00:00 2001 From: Russ Cox <rsc@golang.org> Date: Tue, 28 Sep 2010 13:43:50 -0400 Subject: [PATCH] gc: fix reflect table method receiver Fixes #451. Fixes #770. R=ken2 CC=golang-dev https://golang.org/cl/2207045 --- doc/go_spec.html | 2 +- src/cmd/gc/go.h | 2 +- src/cmd/gc/reflect.c | 59 ++++++++++++++++--- src/cmd/gc/typecheck.c | 107 +++++++++++++++++++++++++++-------- src/pkg/reflect/all_test.go | 49 +++++++++++++++- src/pkg/template/template.go | 5 +- test/method.go | 29 ++++++++-- test/method2.go | 14 ++++- 8 files changed, 219 insertions(+), 48 deletions(-) diff --git a/doc/go_spec.html b/doc/go_spec.html index 8735d4e8d3..ea7a75c497 100644 --- a/doc/go_spec.html +++ b/doc/go_spec.html @@ -5168,6 +5168,6 @@ The following minimal alignment properties are guaranteed: <h2 id="Implementation_differences"><span class="alert">Implementation differences - TODO</span></h2> <ul> <li><span class="alert">Implementation does not honor the restriction on goto statements and targets (no intervening declarations).</span></li> - <li><span class="alert">Method expressions are partially implemented.</span></li> + <li><span class="alert">Gccgo: Method expressions are partially implemented.</span></li> <li><span class="alert">Gccgo: allows only one init() function per source file.</span></li> </ul> diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h index 06bc573733..acbfde4ff7 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 87b9b04ba2..18b2a4fc65 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 1c736d4329..821d540fa1 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: @@ -1381,6 +1383,55 @@ lookdot1(Sym *s, Type *t, Type *f, int dostrcmp) return r; } +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) { @@ -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); diff --git a/src/pkg/reflect/all_test.go b/src/pkg/reflect/all_test.go index 61d7f2c247..39d43d17a6 100644 --- a/src/pkg/reflect/all_test.go +++ b/src/pkg/reflect/all_test.go @@ -1046,6 +1046,11 @@ func TestMethod(t *testing.T) { t.Errorf("Type Method returned %d; want 250", i) } + i = Typeof(&p).Method(0).Func.Call([]Value{NewValue(&p), NewValue(10)})[0].(*IntValue).Get() + if i != 250 { + t.Errorf("Pointer Type Method returned %d; want 250", i) + } + // Curried method of value. i = NewValue(p).Method(0).Call([]Value{NewValue(10)})[0].(*IntValue).Get() if i != 250 { @@ -1288,9 +1293,12 @@ func TestDotDotDot(t *testing.T) { t.Error(s) } -type inner struct{} +type inner struct { + x int +} type outer struct { + y int inner } @@ -1307,3 +1315,42 @@ func TestNestedMethods(t *testing.T) { } } } + +type innerInt struct { + x int +} + +type outerInt struct { + y int + innerInt +} + +func (i *innerInt) m() int { + return i.x +} + +func TestEmbeddedMethods(t *testing.T) { + typ := Typeof((*outerInt)(nil)) + if typ.NumMethod() != 1 || typ.Method(0).Func.Get() != NewValue((*outerInt).m).(*FuncValue).Get() { + t.Errorf("Wrong method table for outerInt: (m=%p)", (*outerInt).m) + for i := 0; i < typ.NumMethod(); i++ { + m := typ.Method(i) + t.Errorf("\t%d: %s %#x\n", i, m.Name, m.Func.Get()) + } + } + + i := &innerInt{3} + if v := NewValue(i).Method(0).Call(nil)[0].(*IntValue).Get(); v != 3 { + t.Errorf("i.m() = %d, want 3", v) + } + + o := &outerInt{1, innerInt{2}} + if v := NewValue(o).Method(0).Call(nil)[0].(*IntValue).Get(); v != 2 { + t.Errorf("i.m() = %d, want 2", v) + } + + f := (*outerInt).m + if v := f(o); v != 2 { + t.Errorf("f(o) = %d, want 2", v) + } +} diff --git a/src/pkg/template/template.go b/src/pkg/template/template.go index 0defe948fe..455b6ccb91 100644 --- a/src/pkg/template/template.go +++ b/src/pkg/template/template.go @@ -597,10 +597,7 @@ func lookup(v reflect.Value, name string) reflect.Value { for i := 0; i < n; i++ { m := typ.Method(i) mtyp := m.Type - // We must check receiver type because of a bug in the reflection type tables: - // it should not be possible to find a method with the wrong receiver type but - // this can happen due to value/pointer receiver mismatch. - if m.Name == name && mtyp.NumIn() == 1 && mtyp.NumOut() == 1 && mtyp.In(0) == typ { + if m.Name == name && mtyp.NumIn() == 1 && mtyp.NumOut() == 1 { return v.Method(i).Call(nil)[0] } } diff --git a/test/method.go b/test/method.go index c751c1f1b5..b52d97894c 100644 --- a/test/method.go +++ b/test/method.go @@ -19,7 +19,7 @@ func (s S) val() int { return 1 } func (s *S1) val() int { return 2 } func (i I) val() int { return 3 } func (i *I1) val() int { return 4 } -//func (t T) val() int { return 7 } +func (t T) val() int { return 7 } func (t *T1) val() int { return 8 } type Val interface { @@ -34,6 +34,8 @@ func main() { var i I var pi *I1 var pt *T1 + var t T + var v Val if s.val() != 1 { println("s.val:", s.val()) @@ -75,7 +77,10 @@ func main() { println("(*I1).val(pi):", (*I1).val(pi)) panic("fail") } - // if t.val() != 7 { prinln("t.val:", t.val()); panic("fail") } + if t.val() != 7 { + println("t.val:", t.val()) + panic("fail") + } if pt.val() != 8 { println("pt.val:", pt.val()) panic("fail") @@ -101,11 +106,27 @@ func main() { println("pi.val:", val(pi)) panic("fail") } - // if val(t) != 7 { println("t.val:", val(t)); panic("fail") } + if val(t) != 7 { + println("t.val:", val(t)) + panic("fail") + } if val(pt) != 8 { println("pt.val:", val(pt)) panic("fail") } - // if Val.val(i) != 3 { println("Val.val(i):", Val.val(i)); panic("fail") } + if Val.val(i) != 3 { + println("Val.val(i):", Val.val(i)) + panic("fail") + } + v = i + if Val.val(v) != 3 { + println("Val.val(v):", Val.val(v)) + panic("fail") + } + pv := &v + if pv.val() != 3 { + println("pv.val():", pv.val()) + panic("fail") + } } diff --git a/test/method2.go b/test/method2.go index 3ee0ae1364..cda6d9aadf 100644 --- a/test/method2.go +++ b/test/method2.go @@ -6,9 +6,17 @@ package main -type T struct {a int} +type T struct { + a int +} type P *T type P1 *T -func (p P) val() int { return 1 } // ERROR "receiver" -func (p *P1) val() int { return 1 } // ERROR "receiver" +func (p P) val() int { return 1 } // ERROR "receiver" +func (p *P1) val() int { return 1 } // ERROR "receiver" + +type Val interface { + val() int +} + +var _ = (*Val).val // ERROR "method" -- 2.30.9