Commit e512481b authored by Russ Cox's avatar Russ Cox

second pass on interface fixes and tests.

R=ken
OCL=22370
CL=22372
parent 8b8a862d
...@@ -257,7 +257,7 @@ void ...@@ -257,7 +257,7 @@ void
addmethod(Node *n, Type *t, int local) addmethod(Node *n, Type *t, int local)
{ {
Type *f, *d, *pa; Type *f, *d, *pa;
Sym *st, *sf; Sym *sf;
pa = nil; pa = nil;
sf = nil; sf = nil;
...@@ -282,19 +282,17 @@ addmethod(Node *n, Type *t, int local) ...@@ -282,19 +282,17 @@ addmethod(Node *n, Type *t, int local)
if(pa == T) if(pa == T)
goto bad; goto bad;
// and finally the receiver sym f = dclmethod(pa);
f = ismethod(pa);
if(f == T) if(f == T)
goto bad; goto bad;
pa = f;
st = pa->sym;
if(st == S)
goto bad;
if(local && !f->local) { 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; return;
} }
pa = f;
n = nod(ODCLFIELD, newname(sf), N); n = nod(ODCLFIELD, newname(sf), N);
n->type = t; n->type = t;
...@@ -308,7 +306,7 @@ addmethod(Node *n, Type *t, int local) ...@@ -308,7 +306,7 @@ addmethod(Node *n, Type *t, int local)
continue; continue;
} }
if(!eqtype(t, f->type, 0)) { 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); print("\t%T\n\t%T\n", f->type, t);
} }
return; return;
...@@ -324,7 +322,7 @@ addmethod(Node *n, Type *t, int local) ...@@ -324,7 +322,7 @@ addmethod(Node *n, Type *t, int local)
return; return;
bad: bad:
yyerror("unknown method pointer: %T %S", pa, sf); yyerror("invalid receiver type %T", pa);
} }
/* /*
......
...@@ -633,9 +633,9 @@ int isdarray(Type*); ...@@ -633,9 +633,9 @@ int isdarray(Type*);
int isinter(Type*); int isinter(Type*);
int isnilinter(Type*); int isnilinter(Type*);
int isddd(Type*); int isddd(Type*);
Type* ismethod(Type*); Type* dclmethod(Type*);
Type* methtype(Type*); Type* methtype(Type*);
int needaddr(Type*); int methconv(Type*);
Sym* signame(Type*); Sym* signame(Type*);
int bytearraysz(Type*); int bytearraysz(Type*);
int eqtype(Type*, Type*, int); int eqtype(Type*, Type*, int);
...@@ -682,6 +682,7 @@ int Wconv(Fmt*); ...@@ -682,6 +682,7 @@ int Wconv(Fmt*);
int Zconv(Fmt*); int Zconv(Fmt*);
int lookdot0(Sym*, Type*, Type**); int lookdot0(Sym*, Type*, Type**);
Type* lookdot1(Sym*, Type*, Type*);
int adddot1(Sym*, Type*, int, Type**); int adddot1(Sym*, Type*, int, Type**);
Node* adddot(Node*); Node* adddot(Node*);
void expand0(Type*); void expand0(Type*);
...@@ -798,7 +799,9 @@ Type* fixchan(Type*); ...@@ -798,7 +799,9 @@ Type* fixchan(Type*);
Node* chanop(Node*, int); Node* chanop(Node*, int);
Node* arrayop(Node*, int); Node* arrayop(Node*, int);
Node* ifaceop(Type*, 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*); Node* convas(Node*);
void arrayconv(Type*, Node*); void arrayconv(Type*, Node*);
Node* colas(Node*, Node*); Node* colas(Node*, Node*);
......
...@@ -1252,14 +1252,10 @@ fndcl: ...@@ -1252,14 +1252,10 @@ fndcl:
} }
| '(' oarg_type_list ')' new_name '(' oarg_type_list ')' fnres | '(' oarg_type_list ')' new_name '(' oarg_type_list ')' fnres
{ {
Type *t;
b0stack = dclstack; // mark base for fn literals b0stack = dclstack; // mark base for fn literals
$$ = nod(ODCLFUNC, N, N); $$ = nod(ODCLFUNC, N, N);
if(listcount($2) == 1) { if(listcount($2) == 1) {
t = ismethod($2->type);
$$->nname = $4; $$->nname = $4;
if(t != T)
$$->nname = methodname($4, $2->type); $$->nname = methodname($4, $2->type);
$$->type = functype($2, $6, $8); $$->type = functype($2, $6, $8);
funchdr($$); funchdr($$);
......
...@@ -90,6 +90,7 @@ mainlex(int argc, char *argv[]) ...@@ -90,6 +90,7 @@ mainlex(int argc, char *argv[])
nerrors = 0; nerrors = 0;
yyparse(); yyparse();
runifacechecks();
linehist(nil, 0); linehist(nil, 0);
if(curio.bin != nil) if(curio.bin != nil)
......
...@@ -1525,60 +1525,50 @@ isddd(Type *t) ...@@ -1525,60 +1525,50 @@ isddd(Type *t)
return 0; return 0;
} }
/*
* given receiver of type t (t == r or t == *r)
* return type to hang methods off (r).
*/
Type* Type*
ismethod(Type *t) dclmethod(Type *t)
{ {
int a; int ptr;
Sym *s;
if(t == T) if(t == T)
return T; return T;
// no interfaces // strip away pointer if it's there
if(t->etype == TINTER || (t->etype == tptr && t->type->etype == TINTER)) ptr = 0;
return T; if(isptr[t->etype]) {
if(t->sym != S)
a = algtype(t);
// direct receiver
s = t->sym;
if(s != S) {
if(t->methptr == 2)
goto both;
t->methptr |= 1;
goto out;
}
// pointer receiver
if(!isptr[t->etype])
return T; return T;
ptr = 1;
t = t->type; t = t->type;
if(t == T) if(t == T)
return T; return T;
s = t->sym;
if(s != S) {
if(t->methptr == 1)
goto both;
t->methptr |= 2;
goto out;
} }
// need a type name
if(t->sym == S)
return T; return T;
both: // check that all method receivers are consistent
yyerror("type %T used as both direct and indirect method", t); if(t->methptr != 0 && t->methptr != (1<<ptr)) {
if(t->methptr != 3) {
t->methptr = 3; t->methptr = 3;
yyerror("methods on both %T and *%T", t, t);
}
}
t->methptr |= 1<<ptr;
out: // check types
switch(a) { // TODO(rsc): map, chan etc are not quite right
if(!issimple[t->etype])
switch(t->etype) {
default: default:
yyerror("type %T cannot be used as a method", t); return T;
case ASIMP: case TSTRUCT:
case APTR: case TARRAY:
case ASTRING:
case ASLICE:
break; break;
} }
...@@ -1586,47 +1576,46 @@ out: ...@@ -1586,47 +1576,46 @@ out:
} }
/* /*
* this is ismethod() without side effects * this is dclmethod() without side effects.
*/ */
Type* Type*
methtype(Type *t) methtype(Type *t)
{ {
Sym *s;
if(t == T) if(t == T)
return T; return T;
if(t->etype == TINTER || (t->etype == tptr && t->type->etype == TINTER)) if(isptr[t->etype]) {
return T; if(t->sym != S)
s = t->sym;
if(s != S)
return t;
if(!isptr[t->etype])
return T; return T;
t = t->type; t = t->type;
if(t == T) }
if(t == T || t->etype == TINTER || t->sym == S)
return T; return T;
s = t->sym;
if(s != S)
return t; return t;
return T;
} }
/* /*
* this is another ismethod() * given type t in a method call, returns op
* returns 1 if t=T and method wants *T * 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 int
needaddr(Type *t) methconv(Type *t)
{ {
Sym *s; Type *m;
if(t == T) m = methtype(t);
if(m == T)
return 0; 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; return 0;
s = t->sym; }
if(s != S && t->methptr == 2) // want non-pointer
return 1; if(t != m)
return OIND;
return 0; return 0;
} }
...@@ -2735,3 +2724,111 @@ genptrtramp(Sym *method, Sym *oldname, Type *oldthis, Type *oldtype, Sym *newnam ...@@ -2735,3 +2724,111 @@ genptrtramp(Sym *method, Sym *oldname, Type *oldthis, Type *oldtype, Sym *newnam
funcbody(fn); 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;
}
...@@ -471,7 +471,7 @@ loop: ...@@ -471,7 +471,7 @@ loop:
walktype(r->left, Erv); walktype(r->left, Erv);
if(r->left == N) if(r->left == N)
break; break;
et = isandss(r->type, r->left); et = ifaceas(r->type, r->left->type);
switch(et) { switch(et) {
case I2T: case I2T:
et = I2T2; et = I2T2;
...@@ -604,8 +604,8 @@ loop: ...@@ -604,8 +604,8 @@ loop:
} }
} }
// interface and structure // interface assignment
et = isandss(n->type, l); et = ifaceas(n->type, l->type);
if(et != Inone) { if(et != Inone) {
indir(n, ifaceop(n->type, l, et)); indir(n, ifaceop(n->type, l, et));
goto ret; goto ret;
...@@ -1542,14 +1542,11 @@ walkselect(Node *sel) ...@@ -1542,14 +1542,11 @@ walkselect(Node *sel)
} }
Type* Type*
lookdot1(Node *n, Type *t, Type *f) lookdot1(Sym *s, Type *t, Type *f)
{ {
Type *r; Type *r;
Sym *s;
r = T; r = T;
s = n->sym;
for(; f!=T; f=f->down) { for(; f!=T; f=f->down) {
if(f->sym == S) if(f->sym == S)
continue; continue;
...@@ -1567,15 +1564,19 @@ lookdot1(Node *n, Type *t, Type *f) ...@@ -1567,15 +1564,19 @@ lookdot1(Node *n, Type *t, Type *f)
int int
lookdot(Node *n, Type *t) lookdot(Node *n, Type *t)
{ {
Type *f1, *f2; Type *f1, *f2, *tt;
int op;
Sym *s;
s = n->right->sym;
f1 = T; f1 = T;
if(t->etype == TSTRUCT || t->etype == TINTER) 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); f2 = methtype(n->left->type);
if(f2 != T) if(f2 != T)
f2 = lookdot1(n->right, f2, f2->method); f2 = lookdot1(s, f2, f2->method);
if(f1 != T) { if(f1 != T) {
if(f2 != T) if(f2 != T)
...@@ -1590,12 +1591,20 @@ lookdot(Node *n, Type *t) ...@@ -1590,12 +1591,20 @@ lookdot(Node *n, Type *t)
} }
if(f2 != T) { if(f2 != T) {
if(needaddr(n->left->type)) { tt = n->left->type;
if((op = methconv(tt)) != 0) {
switch(op) {
case OADDR:
walktype(n->left, Elv); walktype(n->left, Elv);
n->left = nod(OADDR, n->left, N); n->left = nod(OADDR, n->left, N);
n->left->type = ptrto(n->left->left->type); 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->right = methodname(n->right, n->left->type);
n->xoffset = f2->width; n->xoffset = f2->width;
n->type = f2->type; n->type = f2->type;
...@@ -1903,36 +1912,27 @@ loop: ...@@ -1903,36 +1912,27 @@ 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 int
ascompat(Type *t1, Type *t2) ascompat(Type *dst, Type *src)
{ {
if(eqtype(t1, t2, 0)) if(eqtype(dst, src, 0))
return 1; return 1;
// if(eqtype(t1, nilptr, 0)) if(isdarray(dst) && issarray(src))
// return 1;
// if(eqtype(t2, nilptr, 0))
// return 1;
if(isnilinter(t1))
return 1;
if(isinter(t1)) {
if(isinter(t2))
return 1; return 1;
if(ismethod(t2))
if(isnilinter(dst) || isnilinter(src))
return 1; return 1;
}
if(isnilinter(t2)) if(isinter(dst) && isinter(src))
return 1; return 1;
if(isinter(t2))
if(ismethod(t1)) if(isinter(dst) && methtype(src))
return 1; return 1;
if(isdarray(t1)) if(isinter(src) && methtype(dst))
if(issarray(t2))
return 1; return 1;
return 0; return 0;
...@@ -2817,33 +2817,33 @@ arrayop(Node *n, int top) ...@@ -2817,33 +2817,33 @@ arrayop(Node *n, int top)
return r; return r;
} }
/*
* assigning src to dst involving interfaces?
* return op to use.
*/
int 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(dst)) {
if(isinter(lt)) { if(isinter(src)) {
if(isinter(rt)) { if(eqtype(dst, src, 0))
if(isnilinter(lt) && isnilinter(rt))
return Inone; return Inone;
if(!eqtype(rt, lt, 0))
return I2I; return I2I;
return Inone;
} }
if(isnilinter(lt)) if(isnilinter(dst))
return T2I; return T2I;
if(ismethod(rt) != T) ifacecheck(dst, src, lineno);
return T2I; return T2I;
return Inone;
} }
if(isinter(src)) {
if(isinter(rt)) { if(isnilinter(src))
if(isnilinter(rt) || ismethod(lt) != T) return I2T;
ifacecheck(dst, src, lineno);
return I2T; return I2T;
return Inone;
} }
return Inone; return Inone;
} }
...@@ -2988,7 +2988,7 @@ convas(Node *n) ...@@ -2988,7 +2988,7 @@ convas(Node *n)
if(eqtype(lt, rt, 0)) if(eqtype(lt, rt, 0))
goto out; goto out;
et = isandss(lt, r); et = ifaceas(lt, rt);
if(et != Inone) { if(et != Inone) {
n->right = ifaceop(lt, r, et); n->right = ifaceop(lt, r, et);
goto out; goto out;
......
...@@ -8,7 +8,7 @@ package main ...@@ -8,7 +8,7 @@ package main
type T *struct {} type T *struct {}
func (x T) M () {} // ERROR "pointer" func (x T) M () {} // ERROR "pointer|receiver"
/* /*
bug046.go:7: illegal <this> pointer bug046.go:7: illegal <this> pointer
......
...@@ -15,7 +15,9 @@ type I interface { ...@@ -15,7 +15,9 @@ type I interface {
func main() { func main() {
var s *S; var s *S;
var i I; var i I;
i = s; var e interface {};
e = s;
i = e;
} }
// hide S down here to avoid static warning // hide S down here to avoid static warning
......
// $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)
}
}
// 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"
}
// $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());
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment