addmethod(Node *n, Type *t, int local)
{
Type *f, *d, *pa;
- Sym *st, *sf;
+ Sym *sf;
pa = nil;
sf = nil;
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;
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;
return;
bad:
- yyerror("unknown method pointer: %T %S", pa, sf);
+ yyerror("invalid receiver type %T", pa);
}
/*
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);
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*);
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*);
}
| '(' 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);
nerrors = 0;
yyparse();
+ runifacechecks();
linehist(nil, 0);
if(curio.bin != nil)
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;
}
}
/*
- * 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;
}
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;
+}
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;
}
}
- // 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;
}
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;
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)
}
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;
}
/*
- * 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;
}
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;
}
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;
type T *struct {}
-func (x T) M () {} // ERROR "pointer"
+func (x T) M () {} // ERROR "pointer|receiver"
/*
bug046.go:7: illegal <this> pointer
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
--- /dev/null
+// $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)
+ }
+}
--- /dev/null
+// 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"
+}
--- /dev/null
+// $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());
+ }
+}