]> Cypherpunks repositories - gostls13.git/commitdiff
second pass on interface fixes and tests.
authorRuss Cox <rsc@golang.org>
Fri, 9 Jan 2009 02:06:06 +0000 (18:06 -0800)
committerRuss Cox <rsc@golang.org>
Fri, 9 Jan 2009 02:06:06 +0000 (18:06 -0800)
R=ken
OCL=22370
CL=22372

src/cmd/gc/dcl.c
src/cmd/gc/go.h
src/cmd/gc/go.y
src/cmd/gc/lex.c
src/cmd/gc/subr.c
src/cmd/gc/walk.c
test/bugs/bug046.go
test/interface2.go
test/interface4.go [new file with mode: 0644]
test/interface5.go [new file with mode: 0644]
test/method3.go [new file with mode: 0644]

index a5bebed7bc7c5e80480a4867ac226466dd89c021..38174b828226adf56f3f13c31b9fafbaff9fb068 100644 (file)
@@ -257,7 +257,7 @@ void
 addmethod(Node *n, Type *t, int local)
 {
        Type *f, *d, *pa;
-       Sym *st, *sf;
+       Sym *sf;
 
        pa = nil;
        sf = nil;
@@ -282,19 +282,17 @@ addmethod(Node *n, Type *t, int local)
        if(pa == T)
                goto bad;
 
-       // and finally the receiver sym
-       f = ismethod(pa);
+       f = dclmethod(pa);
        if(f == T)
                goto bad;
-       pa = f;
-       st = pa->sym;
-       if(st == S)
-               goto bad;
+
        if(local && !f->local) {
-               yyerror("method receiver type must be locally defined: %T", f);
+               yyerror("cannot define methods on non-local type %T", t);
                return;
        }
 
+       pa = f;
+
        n = nod(ODCLFIELD, newname(sf), N);
        n->type = t;
 
@@ -308,7 +306,7 @@ addmethod(Node *n, Type *t, int local)
                        continue;
                }
                if(!eqtype(t, f->type, 0)) {
-                       yyerror("method redeclared: %S of type %S", sf, st);
+                       yyerror("method redeclared: %T.%S", pa, sf);
                        print("\t%T\n\t%T\n", f->type, t);
                }
                return;
@@ -324,7 +322,7 @@ addmethod(Node *n, Type *t, int local)
        return;
 
 bad:
-       yyerror("unknown method pointer: %T %S", pa, sf);
+       yyerror("invalid receiver type %T", pa);
 }
 
 /*
index 85bc749a8c366b372d1a02c0ac4db1271853d609..da4f871893579a4b39c22386ecd7c1f7dc575842 100644 (file)
@@ -633,9 +633,9 @@ int isdarray(Type*);
 int    isinter(Type*);
 int    isnilinter(Type*);
 int    isddd(Type*);
-Type*  ismethod(Type*);
+Type*  dclmethod(Type*);
 Type*  methtype(Type*);
-int    needaddr(Type*);
+int    methconv(Type*);
 Sym*   signame(Type*);
 int    bytearraysz(Type*);
 int    eqtype(Type*, Type*, int);
@@ -682,6 +682,7 @@ int Wconv(Fmt*);
 int    Zconv(Fmt*);
 
 int    lookdot0(Sym*, Type*, Type**);
+Type*  lookdot1(Sym*, Type*, Type*);
 int    adddot1(Sym*, Type*, int, Type**);
 Node*  adddot(Node*);
 void   expand0(Type*);
@@ -798,7 +799,9 @@ Type*       fixchan(Type*);
 Node*  chanop(Node*, int);
 Node*  arrayop(Node*, int);
 Node*  ifaceop(Type*, Node*, int);
-int    isandss(Type*, Node*);
+int    ifaceas(Type*, Type*);
+void   ifacecheck(Type*, Type*, int);
+void   runifacechecks(void);
 Node*  convas(Node*);
 void   arrayconv(Type*, Node*);
 Node*  colas(Node*, Node*);
index 5d13a7b10b2af2186e3d3ee1ff2bd38bd12e8fd4..313d48002536b42a3ddea9618a65b3dabafda147 100644 (file)
@@ -1252,15 +1252,11 @@ fndcl:
        }
 |      '(' oarg_type_list ')' new_name '(' oarg_type_list ')' fnres
        {
-               Type *t;
-
                b0stack = dclstack;     // mark base for fn literals
                $$ = nod(ODCLFUNC, N, N);
                if(listcount($2) == 1) {
-                       t = ismethod($2->type);
                        $$->nname = $4;
-                       if(t != T)
-                               $$->nname = methodname($4, $2->type);
+                       $$->nname = methodname($4, $2->type);
                        $$->type = functype($2, $6, $8);
                        funchdr($$);
                        addmethod($4, $$->type, 1);
index 642824a7742455a7640768dc3807997ff2f273e8..a98164e28b23cb3f8d2996b9da9670d437a74373 100644 (file)
@@ -90,6 +90,7 @@ mainlex(int argc, char *argv[])
 
        nerrors = 0;
        yyparse();
+       runifacechecks();
 
        linehist(nil, 0);
        if(curio.bin != nil)
index 3c369b05a807d8a09ab4da765507d995546a32fc..ac79087799fc2ace558aabcb2dd93a1915312cce 100644 (file)
@@ -1525,60 +1525,50 @@ isddd(Type *t)
        return 0;
 }
 
+/*
+ * given receiver of type t (t == r or t == *r)
+ * return type to hang methods off (r).
+ */
 Type*
-ismethod(Type *t)
+dclmethod(Type *t)
 {
-       int a;
-       Sym *s;
+       int ptr;
 
        if(t == T)
                return T;
 
-       // no interfaces
-       if(t->etype == TINTER || (t->etype == tptr && t->type->etype == TINTER))
-               return T;
-
-       a = algtype(t);
-
-       // direct receiver
-       s = t->sym;
-       if(s != S) {
-               if(t->methptr == 2)
-                       goto both;
-               t->methptr |= 1;
-               goto out;
+       // strip away pointer if it's there
+       ptr = 0;
+       if(isptr[t->etype]) {
+               if(t->sym != S)
+                       return T;
+               ptr = 1;
+               t = t->type;
+               if(t == T)
+                       return T;
        }
 
-       // pointer receiver
-       if(!isptr[t->etype])
-               return T;
-
-       t = t->type;
-       if(t == T)
+       // need a type name
+       if(t->sym == S)
                return T;
 
-       s = t->sym;
-       if(s != S) {
-               if(t->methptr == 1)
-                       goto both;
-               t->methptr |= 2;
-               goto out;
+       // 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;
 
-       return T;
-
-both:
-       yyerror("type %T used as both direct and indirect method", t);
-       t->methptr = 3;
-
-out:
-       switch(a) {
+       // check types
+       // TODO(rsc): map, chan etc are not quite right
+       if(!issimple[t->etype])
+       switch(t->etype) {
        default:
-               yyerror("type %T cannot be used as a method", t);
-       case ASIMP:
-       case APTR:
-       case ASTRING:
-       case ASLICE:
+               return T;
+       case TSTRUCT:
+       case TARRAY:
                break;
        }
 
@@ -1586,47 +1576,46 @@ out:
 }
 
 /*
- * this is ismethod() without side effects
+ * this is dclmethod() without side effects.
  */
 Type*
 methtype(Type *t)
 {
-       Sym *s;
-
        if(t == T)
                return T;
-       if(t->etype == TINTER || (t->etype == tptr && t->type->etype == TINTER))
-               return T;
-       s = t->sym;
-       if(s != S)
-               return t;
-       if(!isptr[t->etype])
-               return T;
-       t = t->type;
-       if(t == 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;
-       s = t->sym;
-       if(s != S)
-               return t;
-       return T;
+       return t;
 }
 
 /*
- * this is another ismethod()
- * returns 1 if t=T and method wants *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
-needaddr(Type *t)
+methconv(Type *t)
 {
-       Sym *s;
+       Type *m;
 
-       if(t == T)
+       m = methtype(t);
+       if(m == T)
                return 0;
-       if(t->etype == TINTER || (t->etype == tptr && t->type->etype == TINTER))
+       if(m->methptr&2) {
+               // want pointer
+               if(t == m)
+                       return OADDR;
                return 0;
-       s = t->sym;
-       if(s != S && t->methptr == 2)
-               return 1;
+       }
+       // want non-pointer
+       if(t != m)
+               return OIND;
        return 0;
 }
 
@@ -2735,3 +2724,111 @@ genptrtramp(Sym *method, Sym *oldname, Type *oldthis, Type *oldtype, Sym *newnam
        funcbody(fn);
 }
 
+/*
+ * delayed interface type check.
+ * remember that there is an interface conversion
+ * on the given line.  once the file is completely read
+ * and all methods are known, we can check that
+ * the conversions are valid.
+ */
+
+typedef struct Icheck Icheck;
+struct Icheck
+{
+       Icheck *next;
+       Type *dst;
+       Type *src;
+       int lineno;
+};
+Icheck *icheck;
+Icheck *ichecktail;
+
+void
+ifacecheck(Type *dst, Type *src, int lineno)
+{
+       Icheck *p;
+
+       p = mal(sizeof *p);
+       if(ichecktail)
+               ichecktail->next = p;
+       else
+               icheck = p;
+       p->dst = dst;
+       p->src = src;
+       p->lineno = lineno;
+       ichecktail = p;
+}
+
+Type*
+ifacelookdot(Sym *s, Type *t)
+{
+       int c, d;
+       Type *m;
+
+       for(d=0; d<nelem(dotlist); d++) {
+               c = adddot1(s, t, d, &m);
+               if(c > 1) {
+                       yyerror("%T.%S is ambiguous", t, s);
+                       return T;
+               }
+               if(c == 1)
+                       return m;
+       }
+       return T;
+}
+
+int
+hasiface(Type *t, Type *iface, Type **m)
+{
+       Type *im, *tm;
+       int imhash;
+
+       t = methtype(t);
+       if(t == T)
+               return 0;
+
+       // if this is too slow,
+       // could sort these first
+       // and then do one loop.
+
+       // could also do full type compare
+       // instead of using hash, but have to
+       // avoid checking receivers, and
+       // typehash already does that for us.
+       // also, it's what the runtime will do,
+       // so we can both be wrong together.
+
+       for(im=iface->type; im; im=im->down) {
+               imhash = typehash(im, 0);
+               tm = ifacelookdot(im->sym, t);
+               if(tm == T || typehash(tm, 0) != imhash) {
+                       *m = im;
+                       return 0;
+               }
+       }
+       return 1;
+}
+
+void
+runifacechecks(void)
+{
+       Icheck *p;
+       int lno;
+       Type *m, *l, *r;
+
+       lno = lineno;
+       for(p=icheck; p; p=p->next) {
+               lineno = p->lineno;
+               if(isinter(p->dst)) {
+                       l = p->src;
+                       r = p->dst;
+               } else {
+                       l = p->dst;
+                       r = p->src;
+               }
+               if(!hasiface(l, r, &m))
+                       yyerror("%T is not %T - missing %S%hT",
+                               l, r, m->sym, m->type);
+       }
+       lineno = lno;
+}
index cfc3154fdfd69f04cf30a9a23c0f2d0c65760981..36398781d997631cdfec2c161e4a17c68944d0cb 100644 (file)
@@ -471,7 +471,7 @@ loop:
                                walktype(r->left, Erv);
                                if(r->left == N)
                                        break;
-                               et = isandss(r->type, r->left);
+                               et = ifaceas(r->type, r->left->type);
                                switch(et) {
                                case I2T:
                                        et = I2T2;
@@ -604,8 +604,8 @@ loop:
                        }
                }
 
-               // interface and structure
-               et = isandss(n->type, l);
+               // interface assignment
+               et = ifaceas(n->type, l->type);
                if(et != Inone) {
                        indir(n, ifaceop(n->type, l, et));
                        goto ret;
@@ -1542,14 +1542,11 @@ walkselect(Node *sel)
 }
 
 Type*
-lookdot1(Node *n, Type *t, Type *f)
+lookdot1(Sym *s, Type *t, Type *f)
 {
        Type *r;
-       Sym *s;
 
        r = T;
-       s = n->sym;
-
        for(; f!=T; f=f->down) {
                if(f->sym == S)
                        continue;
@@ -1567,15 +1564,19 @@ lookdot1(Node *n, Type *t, Type *f)
 int
 lookdot(Node *n, Type *t)
 {
-       Type *f1, *f2;
+       Type *f1, *f2, *tt;
+       int op;
+       Sym *s;
+
+       s = n->right->sym;
 
        f1 = T;
        if(t->etype == TSTRUCT || t->etype == TINTER)
-               f1 = lookdot1(n->right, t, t->type);
+               f1 = lookdot1(s, t, t->type);
 
        f2 = methtype(n->left->type);
        if(f2 != T)
-               f2 = lookdot1(n->right, f2, f2->method);
+               f2 = lookdot1(s, f2, f2->method);
 
        if(f1 != T) {
                if(f2 != T)
@@ -1590,12 +1591,20 @@ lookdot(Node *n, Type *t)
        }
 
        if(f2 != T) {
-               if(needaddr(n->left->type)) {
-                       walktype(n->left, Elv);
-                       n->left = nod(OADDR, n->left, N);
-                       n->left->type = ptrto(n->left->left->type);
+               tt = n->left->type;
+               if((op = methconv(tt)) != 0) {
+                       switch(op) {
+                       case OADDR:
+                               walktype(n->left, Elv);
+                               n->left = nod(OADDR, n->left, N);
+                               n->left->type = ptrto(tt);
+                               break;
+                       case OIND:
+                               n->left = nod(OIND, n->left, N);
+                               n->left->type = tt->type;
+                               break;
+                       }
                }
-               ismethod(n->left->type);
                n->right = methodname(n->right, n->left->type);
                n->xoffset = f2->width;
                n->type = f2->type;
@@ -1903,37 +1912,28 @@ loop:
 }
 
 /*
- * can we assign var of type t2 to var of type t1
+ * can we assign var of type src to var of type dst
  */
 int
-ascompat(Type *t1, Type *t2)
+ascompat(Type *dst, Type *src)
 {
-       if(eqtype(t1, t2, 0))
+       if(eqtype(dst, src, 0))
                return 1;
 
-//     if(eqtype(t1, nilptr, 0))
-//             return 1;
-//     if(eqtype(t2, nilptr, 0))
-//             return 1;
+       if(isdarray(dst) && issarray(src))
+               return 1;
 
-       if(isnilinter(t1))
+       if(isnilinter(dst) || isnilinter(src))
                return 1;
-       if(isinter(t1)) {
-               if(isinter(t2))
-                       return 1;
-               if(ismethod(t2))
-                       return 1;
-       }
 
-       if(isnilinter(t2))
+       if(isinter(dst) && isinter(src))
                return 1;
-       if(isinter(t2))
-               if(ismethod(t1))
-                       return 1;
 
-       if(isdarray(t1))
-               if(issarray(t2))
-                       return 1;
+       if(isinter(dst) && methtype(src))
+               return 1;
+
+       if(isinter(src) && methtype(dst))
+               return 1;
 
        return 0;
 }
@@ -2817,33 +2817,33 @@ arrayop(Node *n, int top)
        return r;
 }
 
+/*
+ * assigning src to dst involving interfaces?
+ * return op to use.
+ */
 int
-isandss(Type *lt, Node *r)
+ifaceas(Type *dst, Type *src)
 {
-       Type *rt;
+       if(src == T || dst == T)
+               return Inone;
 
-       rt = r->type;
-       if(isinter(lt)) {
-               if(isinter(rt)) {
-                       if(isnilinter(lt) && isnilinter(rt))
+       if(isinter(dst)) {
+               if(isinter(src)) {
+                       if(eqtype(dst, src, 0))
                                return Inone;
-                       if(!eqtype(rt, lt, 0))
-                               return I2I;
-                       return Inone;
+                       return I2I;
                }
-               if(isnilinter(lt))
-                       return T2I;
-               if(ismethod(rt) != T)
+               if(isnilinter(dst))
                        return T2I;
-               return Inone;
+               ifacecheck(dst, src, lineno);
+               return T2I;
        }
-
-       if(isinter(rt)) {
-               if(isnilinter(rt) || ismethod(lt) != T)
+       if(isinter(src)) {
+               if(isnilinter(src))
                        return I2T;
-               return Inone;
+               ifacecheck(dst, src, lineno);
+               return I2T;
        }
-
        return Inone;
 }
 
@@ -2988,7 +2988,7 @@ convas(Node *n)
        if(eqtype(lt, rt, 0))
                goto out;
 
-       et = isandss(lt, r);
+       et = ifaceas(lt, rt);
        if(et != Inone) {
                n->right = ifaceop(lt, r, et);
                goto out;
index ba08f790e23ad5877520d4e9e417c6f396f4fca8..8a9b797074d6f831d39f40b3b6b071ac70d626af 100644 (file)
@@ -8,7 +8,7 @@ package main
 
 type T *struct {}
 
-func (x T) M () {}  // ERROR "pointer"
+func (x T) M () {}  // ERROR "pointer|receiver"
 
 /*
 bug046.go:7: illegal <this> pointer
index 8dfc9d8ff5ad6695205cc73bc22c4b4aa43d874a..1db033887f30c3073cd2eb933c924cfbb8a02d17 100644 (file)
@@ -15,7 +15,9 @@ type I interface {
 func main() {
        var s *S;
        var i I;
-       i = s;
+       var e interface {};
+       e = s;
+       i = e;
 }
 
 // hide S down here to avoid static warning
diff --git a/test/interface4.go b/test/interface4.go
new file mode 100644 (file)
index 0000000..0d72235
--- /dev/null
@@ -0,0 +1,75 @@
+// $G $D/$F.go && $L $F.$A && ./$A.out
+
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// check that big vs small, pointer vs not
+// interface methods work.
+
+package main
+
+type I interface { M() int64 }
+
+type BigPtr struct { a, b, c, d int64 }
+func (z *BigPtr) M() int64 { return z.a+z.b+z.c+z.d }
+
+type SmallPtr struct { a int32 }
+func (z *SmallPtr) M() int64 { return int64(z.a) }
+
+type IntPtr int32
+func (z *IntPtr) M() int64 { return int64(*z) }
+
+var bad bool
+
+func test(name string, i I) {
+       m := i.M();
+       if m != 12345 {
+               println(name, m);
+               bad = true;
+       }
+}
+
+func ptrs() {
+       var bigptr BigPtr = BigPtr{ 10000, 2000, 300, 45 };
+       var smallptr SmallPtr = SmallPtr{ 12345 };
+       var intptr IntPtr = 12345;
+
+       test("bigptr", bigptr);
+       test("&bigptr", &bigptr);
+       test("smallptr", smallptr);
+       test("&smallptr", &smallptr);
+       test("intptr", intptr);
+       test("&intptr", &intptr);
+}
+
+type Big struct { a, b, c, d int64 }
+func (z Big) M() int64 { return z.a+z.b+z.c+z.d }
+
+type Small struct { a int32 }
+func (z Small) M() int64 { return int64(z.a) }
+
+type Int int32
+func (z Int) M() int64 { return int64(z) }
+
+func nonptrs() {
+       var big Big = Big{ 10000, 2000, 300, 45 };
+       var small Small = Small{ 12345 };
+       var int Int = 12345;
+
+       test("big", big);
+       test("&big", &big);
+       test("small", small);
+       test("&small", &small);
+       test("int", int);
+       test("&int", &int);
+}
+
+func main() {
+       ptrs();
+       nonptrs();
+
+       if bad {
+               sys.exit(1)
+       }
+}
diff --git a/test/interface5.go b/test/interface5.go
new file mode 100644 (file)
index 0000000..a7d79a1
--- /dev/null
@@ -0,0 +1,21 @@
+// errchk $G $D/$F.go
+
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+type T struct { a int }
+var t *T
+
+type I interface { M() }
+var i I
+
+func main() {
+       // neither of these can work,
+       // because i has an extra method
+       // that t does not, so i cannot contain a t.
+       i = t;  // ERROR "missing"
+       t = i;  // ERROR "missing"
+}
diff --git a/test/method3.go b/test/method3.go
new file mode 100644 (file)
index 0000000..491bcda
--- /dev/null
@@ -0,0 +1,25 @@
+// $G $D/$F.go && $L $F.$A && ./$A.out || echo BUG method3
+
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// test that methods on slices work
+
+package main
+
+type T [] int
+func (t T) Len() int { return len(t) }
+
+type I interface {
+       Len() int
+}
+
+func main() {
+       var t T = T{0,1,2,3,4};
+       var i I;
+       i = t;
+       if i.Len() != 5 {
+               panicln("length", i.Len());
+       }
+}