runtime, type switch: eliminate package global name space assumption

bonus: type switch now detects multiple uses of identical interface types.
bonus: interface types are now order-independent, following the spec.

R=ken2
CC=golang-dev
https://golang.org/cl/194053
diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h
index d7b716c..e5715e8 100644
--- a/src/cmd/gc/go.h
+++ b/src/cmd/gc/go.h
@@ -34,15 +34,6 @@
 	HISTSZ		= 10,
 
 	PRIME1		= 3,
-	PRIME2		= 10007,
-	PRIME3		= 10009,
-	PRIME4		= 10037,
-	PRIME5		= 10039,
-	PRIME6		= 10061,
-	PRIME7		= 10067,
-	PRIME8		= 10079,
-	PRIME9		= 10091,
-	PRIME10		= 10093,
 
 	AUNK		= 100,
 
@@ -549,8 +540,7 @@
 	Sym*	isym;
 	Sym*	tsym;
 	Type*	type;
-	uint32	hash;
-	int32	perm;
+	Type*	mtype;
 	int32	offset;
 	Sig*	link;
 };
@@ -733,7 +723,7 @@
 EXTERN	int	funcdepth;
 EXTERN	int	typecheckok;
 EXTERN	int	packagequotes;
-
+EXTERN	int	longsymnames;
 EXTERN	int	compiling_runtime;
 
 /*
@@ -951,7 +941,7 @@
 void	addmethod(Sym*, Type*, int);
 Node*	methodname(Node*, Type*);
 Node*	methodname1(Node*, Node*);
-Type*	methodfunc(Type*);
+Type*	methodfunc(Type*, int);
 Sym*	methodsym(Sym*, Type*);
 Type*	functype(Node*, NodeList*, NodeList*);
 char*	thistypenam(Node*);
diff --git a/src/cmd/gc/reflect.c b/src/cmd/gc/reflect.c
index 97217af..e1dba06 100644
--- a/src/cmd/gc/reflect.c
+++ b/src/cmd/gc/reflect.c
@@ -14,7 +14,18 @@
 static int
 sigcmp(Sig *a, Sig *b)
 {
-	return strcmp(a->name, b->name);
+	int i;
+	
+	i = strcmp(a->name, b->name);
+	if(i != 0)
+		return i;
+	if(a->pkg == b->pkg)
+		return 0;
+	if(a->pkg == nil)
+		return -1;
+	if(b->pkg == nil)
+		return +1;
+	return strcmp(a->pkg->path->s, b->pkg->path->s);
 }
 
 static Sig*
@@ -86,17 +97,17 @@
 
 /*
  * f is method type, with receiver.
- * return function type, receiver as first argument.
+ * return function type, receiver as first argument (or not).
  */
 Type*
-methodfunc(Type *f)
+methodfunc(Type *f, int use_receiver)
 {
 	NodeList *in, *out;
 	Node *d;
 	Type *t;
 
 	in = nil;
-	if(!isifacemethod(f)) {
+	if(use_receiver) {
 		d = nod(ODCLFIELD, N, N);
 		d->type = getthisx(f)->type->type;
 		in = list(in, d);
@@ -118,8 +129,7 @@
 }
 
 /*
- * return methods of non-interface type t,
- * sorted by hash.
+ * return methods of non-interface type t, sorted by name.
  * generates stub functions as needed.
  */
 static Sig*
@@ -172,15 +182,10 @@
 		a = b;
 
 		a->name = method->name;
-		a->hash = PRIME8*stringhash(a->name) + PRIME9*typehash(f->type);
-		if(!exportname(a->name)) {
-			a->pkg = method->pkg;
-			a->hash += PRIME10*stringhash(a->pkg->name);
-		}
-		a->perm = o++;
 		a->isym = methodsym(method, it);
 		a->tsym = methodsym(method, t);
-		a->type = methodfunc(f->type);
+		a->type = methodfunc(f->type, 1);
+		a->mtype = methodfunc(f->type, 0);
 
 		if(!(a->isym->flags & SymSiggen)) {
 			a->isym->flags |= SymSiggen;
@@ -227,38 +232,39 @@
 }
 
 /*
- * return methods of interface type t, sorted by hash.
+ * return methods of interface type t, sorted by name.
  */
 Sig*
 imethods(Type *t)
 {
-	Sig *a, *b;
+	Sig *a, *all, *last;
 	int o;
 	Type *f;
 
-	a = nil;
+	all = nil;
+	last = nil;
 	o = 0;
 	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;
-		b = mal(sizeof(*b));
-		b->link = a;
-		a = b;
-
+		a = mal(sizeof(*a));
 		a->name = f->sym->name;
-		a->hash = PRIME8*stringhash(a->name) + PRIME9*typehash(f->type);
-		if(!exportname(a->name)) {
+		if(!exportname(f->sym->name))
 			a->pkg = f->sym->pkg;
-			a->hash += PRIME10*stringhash(a->pkg->name);
-		}
-		a->perm = o++;
+		a->mtype = f->type;
 		a->offset = 0;
-		a->type = methodfunc(f->type);
+		a->type = methodfunc(f->type, 0);
+		if(last && sigcmp(last, a) >= 0)
+			fatal("sigcmp vs sortinter %s %s", last->name, a->name);
+		if(last == nil)
+			all = a;
+		else
+			last->link = a;
+		last = a;
 	}
-
-	return lsort(a, sigcmp);
+	return all;
 }
 
 static int
@@ -349,10 +355,9 @@
 	for(a=m; a; a=a->link) {
 		// method
 		// ../../pkg/runtime/type.go:/method
-		ot = duint32(s, ot, a->hash);
-		ot = rnd(ot, widthptr);
 		ot = dgostringptr(s, ot, a->name);
 		ot = dgopkgpath(s, ot, a->pkg);
+		ot = dsymptr(s, ot, dtypesym(a->mtype), 0);
 		ot = dsymptr(s, ot, dtypesym(a->type), 0);
 		if(a->isym)
 			ot = dsymptr(s, ot, a->isym, 0);
@@ -575,7 +580,9 @@
 	if(!haspointers(t))
 		i |= KindNoPointers;
 	ot = duint8(s, ot, i);
-	p = smprint("%#-T", t);
+	longsymnames = 1;
+	p = smprint("%-T", t);
+	longsymnames = 0;
 	ot = dgostringptr(s, ot, p);	// string
 	free(p);
 	if(s1)
@@ -742,8 +749,6 @@
 		ot = duint32(s, ot, n);
 		for(a=m; a; a=a->link) {
 			// ../../pkg/runtime/type.go:/imethod
-			ot = duint32(s, ot, a->hash);
-			ot = duint32(s, ot, a->perm);
 			ot = dgostringptr(s, ot, a->name);
 			ot = dgopkgpath(s, ot, a->pkg);
 			ot = dsymptr(s, ot, dtypesym(a->type), 0);
diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c
index ad9c926..e8aaabc 100644
--- a/src/cmd/gc/subr.c
+++ b/src/cmd/gc/subr.c
@@ -462,10 +462,48 @@
 	return t;
 }
 
+static int
+methcmp(const void *va, const void *vb)
+{
+	Type *a, *b;
+	int i;
+	
+	a = *(Type**)va;
+	b = *(Type**)vb;
+	i = strcmp(a->sym->name, b->sym->name);
+	if(i != 0)
+		return i;
+	if(!exportname(a->sym->name)) {
+		i = strcmp(a->sym->pkg->path->s, b->sym->pkg->path->s);
+		if(i != 0)
+			return i;
+	}
+	return 0;
+}
 
 Type*
 sortinter(Type *t)
 {
+	Type *f;
+	int i;
+	Type **a;
+	
+	if(t->type == nil || t->type->down == nil)
+		return t;
+
+	i=0;
+	for(f=t->type; f; f=f->down)
+		i++;
+	a = mal(i*sizeof f);
+	i = 0;
+	for(f=t->type; f; f=f->down)
+		a[i++] = f;
+	qsort(a, i, sizeof a[0], methcmp);
+	while(i-- > 0) {
+		a[i]->down = f;
+		f = a[i];
+	}
+	t->type = f;
 	return t;
 }
 
@@ -974,22 +1012,20 @@
 		fmtstrcpy(fp, "<S>");
 		return 0;
 	}
-	
+
 	if(fp->flags & FmtShort)
 		goto shrt;
 
 	if(exporting || (fp->flags & FmtSharp)) {
 		if(packagequotes)
 			fmtprint(fp, "\"%Z\"", s->pkg->path);
-		else {
-			// PGNS: Should be s->pkg->prefix
-			fmtprint(fp, "%s", s->pkg->name);
-		}
+		else
+			fmtprint(fp, "%s", s->pkg->prefix);
 		fmtprint(fp, ".%s", s->name);
 		return 0;
 	}
 
-	if(s->pkg != localpkg || (fp->flags & FmtLong)) {
+	if(s->pkg != localpkg || longsymnames || (fp->flags & FmtLong)) {
 		fmtprint(fp, "%s.%s", s->pkg->name, s->name);
 		return 0;
 	}
@@ -997,22 +1033,6 @@
 shrt:
 	fmtstrcpy(fp, s->name);
 	return 0;
-
-	/*
-
-	if(!(fp->flags & FmtShort)) {
-		if((fp->flags & FmtLong) && packagequotes) {
-			fmtprint(fp, "\"%Z\".%s", s->pkg->path, nam);
-			return 0;
-		}
-		if((fp->flags & FmtLong) || strcmp(s->package, package) != 0) {
-			fmtprint(fp, "%s.%s", pkg, nam);
-			return 0;
-		}
-	}
-	fmtstrcpy(fp, nam);
-	*/
-	return 0;
 }
 
 static char*
@@ -2008,7 +2028,7 @@
  * compute a hash value for type t.
  * if t is a method type, ignore the receiver
  * so that the hash can be used in interface checks.
- * %#-T (which calls Tpretty, above) already contains
+ * %-T (which calls Tpretty, above) already contains
  * all the necessary logic to generate a representation
  * of the type that completely describes it.
  * using smprint here avoids duplicating that code.
@@ -2022,13 +2042,15 @@
 	char *p;
 	MD5 d;
 
+	longsymnames = 1;
 	if(t->thistuple) {
 		// hide method receiver from Tpretty
 		t->thistuple = 0;
-		p = smprint("%#-T", t);
+		p = smprint("%-T", t);
 		t->thistuple = 1;
 	}else
-		p = smprint("%#-T", t);
+		p = smprint("%-T", t);
+	longsymnames = 0;
 	md5reset(&d);
 	md5write(&d, (uchar*)p, strlen(p));
 	free(p);
@@ -2873,8 +2895,8 @@
 int
 ifaceokT2I(Type *t0, Type *iface, Type **m, Type **samename)
 {
-	Type *t, *im, *tm, *rcvr;
-	int imhash, followptr;
+	Type *t, *im, *tm, *rcvr, *imtype;
+	int followptr;
 
 	t = methtype(t0);
 
@@ -2882,17 +2904,10 @@
 	// 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->type);
+		imtype = methodfunc(im->type, 0);
 		tm = ifacelookdot(im->sym, t, &followptr);
-		if(tm == T || typehash(tm->type) != imhash) {
+		if(tm == T || !eqtype(methodfunc(tm->type, 0), imtype)) {
 			*m = im;
 			*samename = tm;
 			return 0;
@@ -2923,7 +2938,7 @@
 
 	for(m2=i2->type; m2; m2=m2->down) {
 		for(m1=i1->type; m1; m1=m1->down)
-			if(m1->sym == m2->sym && typehash(m1) == typehash(m2))
+			if(m1->sym == m2->sym && eqtype(m1, m2))
 				goto found;
 		*m = m2;
 		return 0;
diff --git a/src/cmd/gc/swt.c b/src/cmd/gc/swt.c
index 952c472..23bb395 100644
--- a/src/cmd/gc/swt.c
+++ b/src/cmd/gc/swt.c
@@ -163,6 +163,13 @@
 		return +1;
 	if(c1->hash < c2->hash)
 		return -1;
+
+	// sort by ordinal so duplicate error
+	// happens on later case.
+	if(c1->ordinal > c2->ordinal)
+		return +1;
+	if(c1->ordinal < c2->ordinal)
+		return -1;
 	return 0;
 }
 
@@ -336,7 +343,7 @@
 mkcaselist(Node *sw, int arg)
 {
 	Node *n;
-	Case *c, *c1;
+	Case *c, *c1, *c2;
 	NodeList *l;
 	int ord;
 
@@ -395,11 +402,16 @@
 	switch(arg) {
 	case Stype:
 		c = csort(c, typecmp);
-		for(c1=c; c1->link!=C; c1=c1->link) {
-			if(typecmp(c1, c1->link) != 0)
-				continue;
-			setlineno(c1->link->node);
-			yyerror("duplicate case in switch\n\tprevious case at %L", c1->node->lineno);
+		for(c1=c; c1!=C; c1=c1->link) {
+			for(c2=c1->link; c2!=C && c2->hash==c1->hash; c2=c2->link) {
+				if(c1->type == Ttypenil || c1->type == Tdefault)
+					break;
+				if(c2->type == Ttypenil || c2->type == Tdefault)
+					break;
+				if(!eqtype(c1->node->left->type, c2->node->left->type))
+					continue;
+				yyerrorl(c2->node->lineno, "duplicate case in switch\n\tprevious case at %L", c1->node->lineno);
+			}
 		}
 		break;
 	case Snorm:
@@ -604,38 +616,19 @@
 	Node *a, *n;
 	Case *c;
 	int i, half;
-	Val v;
 
 	cas = nil;
 
 	if(ncase < Ncase) {
 		for(i=0; i<ncase; i++) {
 			n = c0->node;
-
-			switch(c0->type) {
-
-			case Ttypenil:
-				v.ctype = CTNIL;
-				a = nod(OIF, N, N);
-				a->ntest = nod(OEQ, facename, nodlit(v));
-				typecheck(&a->ntest, Erv);
-				a->nbody = list1(n->right);		// if i==nil { goto l }
-				cas = list(cas, a);
-				break;
-
-			case Ttypevar:
-				a = typeone(n);
-				cas = list(cas, a);
-				break;
-
-			case Ttypeconst:
-				a = nod(OIF, N, N);
-				a->ntest = nod(OEQ, hashname, nodintconst(c0->hash));
-				typecheck(&a->ntest, Erv);
-				a->nbody = list1(typeone(n));
-				cas = list(cas, a);
-				break;
-			}
+			if(c0->type != Ttypeconst)
+				fatal("typebsw");
+			a = nod(OIF, N, N);
+			a->ntest = nod(OEQ, hashname, nodintconst(c0->hash));
+			typecheck(&a->ntest, Erv);
+			a->nbody = list1(n->right);
+			cas = list(cas, a);
 			c0 = c0->link;
 		}
 		return liststmt(cas);
@@ -663,11 +656,12 @@
 typeswitch(Node *sw)
 {
 	Node *def;
-	NodeList *cas;
-	Node *a;
+	NodeList *cas, *hash;
+	Node *a, *n;
 	Case *c, *c0, *c1;
 	int ncase;
 	Type *t;
+	Val v;
 
 	if(sw->ntest == nil)
 		return;
@@ -722,43 +716,80 @@
 	} else {
 		def = nod(OBREAK, N, N);
 	}
+	
+	/*
+	 * insert if statement into each case block
+	 */
+	for(c=c0; c!=C; c=c->link) {
+		n = c->node;
+		switch(c->type) {
 
-loop:
-	if(c0 == C) {
-		cas = list(cas, def);
-		sw->nbody = concat(cas, sw->nbody);
-		sw->list = nil;
-		walkstmtlist(sw->nbody);
-		return;
-	}
-
-	// deal with the variables one-at-a-time
-	if(c0->type != Ttypeconst) {
-		a = typebsw(c0, 1);
-		cas = list(cas, a);
-		c0 = c0->link;
-		goto loop;
-	}
-
-	// do binary search on run of constants
-	ncase = 1;
-	for(c=c0; c->link!=C; c=c->link) {
-		if(c->link->type != Ttypeconst)
+		case Ttypenil:
+			v.ctype = CTNIL;
+			a = nod(OIF, N, N);
+			a->ntest = nod(OEQ, facename, nodlit(v));
+			typecheck(&a->ntest, Erv);
+			a->nbody = list1(n->right);		// if i==nil { goto l }
+			n->right = a;
 			break;
-		ncase++;
+		
+		case Ttypevar:
+		case Ttypeconst:
+			n->right = typeone(n);
+			break;
+		}
 	}
 
-	// break the chain at the count
-	c1 = c->link;
-	c->link = C;
+	/*
+	 * generate list of if statements, binary search for constant sequences
+	 */
+	while(c0 != C) {
+		if(c0->type != Ttypeconst) {
+			n = c0->node;
+			cas = list(cas, n->right);
+			c0=c0->link;
+			continue;
+		}
+		
+		// identify run of constants
+		c1 = c = c0;
+		while(c->link!=C && c->link->type==Ttypeconst)
+			c = c->link;
+		c0 = c->link;
+		c->link = nil;
 
-	// sort and compile constants
-	c0 = csort(c0, typecmp);
-	a = typebsw(c0, ncase);
-	cas = list(cas, a);
+		// sort by hash
+		c1 = csort(c1, typecmp);
+		
+		// for debugging: linear search
+		if(0) {
+			for(c=c1; c!=C; c=c->link) {
+				n = c->node;
+				cas = list(cas, n->right);
+			}
+			continue;
+		}
 
-	c0 = c1;
-	goto loop;
+		// combine adjacent cases with the same hash
+		ncase = 0;
+		for(c=c1; c!=C; c=c->link) {
+			ncase++;
+			hash = list1(c->node->right);
+			while(c->link != C && c->link->hash == c->hash) {
+				hash = list(hash, c->link->node->right);
+				c->link = c->link->link;
+			}
+			c->node->right = liststmt(hash);
+		}
+		
+		// binary search among cases to narrow by hash
+		cas = list(cas, typebsw(c1, ncase));
+	}
+	
+	cas = list(cas, def);
+	sw->nbody = concat(cas, sw->nbody);
+	sw->list = nil;
+	walkstmtlist(sw->nbody);
 }
 
 void
diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c
index abadbfa..e8ed1dc 100644
--- a/src/cmd/gc/typecheck.c
+++ b/src/cmd/gc/typecheck.c
@@ -483,7 +483,7 @@
 			}
 			n->op = ONAME;
 			n->sym = methodsym(sym, l->type);
-			n->type = methodfunc(n->type);
+			n->type = methodfunc(n->type, 1);
 			getinargx(n->type)->type->type = l->type;	// fix up receiver
 			n->class = PFUNC;
 			ok = Erv;
diff --git a/src/pkg/reflect/type.go b/src/pkg/reflect/type.go
index 2c05992..cd838d7 100644
--- a/src/pkg/reflect/type.go
+++ b/src/pkg/reflect/type.go
@@ -45,9 +45,9 @@
 }
 
 type method struct {
-	hash    uint32
 	name    *string
 	pkgPath *string
+	mtyp    *runtime.Type
 	typ     *runtime.Type
 	ifn     unsafe.Pointer
 	tfn     unsafe.Pointer
@@ -182,8 +182,6 @@
 
 // Method on interface type
 type imethod struct {
-	hash    uint32
-	perm    uint32
 	name    *string
 	pkgPath *string
 	typ     *runtime.Type
diff --git a/src/pkg/reflect/value.go b/src/pkg/reflect/value.go
index 30314dd..14596ad 100644
--- a/src/pkg/reflect/value.go
+++ b/src/pkg/reflect/value.go
@@ -952,7 +952,7 @@
 	data := &value{Typeof((*byte)(nil)), addr(uintptr(v.addr) + ptrSize), true}
 
 	// Function pointer is at p.perm in the table.
-	fn := tab.Fn[p.perm]
+	fn := tab.Fn[i]
 	fv := &FuncValue{value: value{toType(*p.typ), addr(&fn), true}, first: data, isInterface: true}
 	return fv
 }
diff --git a/src/pkg/runtime/iface.c b/src/pkg/runtime/iface.c
index 4a961ce..bfe9c65 100644
--- a/src/pkg/runtime/iface.c
+++ b/src/pkg/runtime/iface.c
@@ -41,10 +41,11 @@
 	int32 ni;
 	Method *t, *et;
 	IMethod *i, *ei;
-	uint32 ihash, h;
+	uint32 h;
 	String *iname;
 	Itab *m;
 	UncommonType *x;
+	Type *itype;
 
 	if(inter->mhdr.len == 0)
 		throw("internal error - misuse of itab");
@@ -97,7 +98,8 @@
 	m->type = type;
 
 search:
-	// both inter and type have method sorted by hash,
+	// both inter and type have method sorted by name,
+	// and interface names are unique,
 	// so can iterate over both in lock step;
 	// the loop is O(ni+nt) not O(ni*nt).
 	i = inter->m;
@@ -105,7 +107,7 @@
 	t = x->m;
 	et = t + x->mhdr.len;
 	for(; i < ei; i++) {
-		ihash = i->hash;
+		itype = i->type;
 		iname = i->name;
 		for(;; t++) {
 			if(t >= et) {
@@ -120,11 +122,11 @@
 				m->bad = 1;
 				goto out;
 			}
-			if(t->hash == ihash && t->name == iname)
+			if(t->mtyp == itype && t->name == iname)
 				break;
 		}
 		if(m)
-			m->fun[i->perm] = t->ifn;
+			m->fun[i - inter->m] = t->ifn;
 	}
 
 out:
diff --git a/src/pkg/runtime/type.go b/src/pkg/runtime/type.go
index d983b6f..bf757c7 100644
--- a/src/pkg/runtime/type.go
+++ b/src/pkg/runtime/type.go
@@ -70,10 +70,10 @@
 
 // Method on non-interface type
 type method struct {
-	hash    uint32         // hash of name + pkg + typ
 	name    *string        // name of method
 	pkgPath *string        // nil for exported Names; otherwise import path
-	typ     *Type          // .(*FuncType) underneath
+	mtyp    *Type          // method type (without receiver)
+	typ     *Type          // .(*FuncType) underneath (with receiver)
 	ifn     unsafe.Pointer // fn used in interface call (one-word receiver)
 	tfn     unsafe.Pointer // fn used for normal method call
 }
@@ -181,8 +181,6 @@
 
 // Method on interface type
 type imethod struct {
-	hash    uint32  // hash of name + pkg + typ; same hash as method
-	perm    uint32  // index of function pointer in interface map
 	name    *string // name of method
 	pkgPath *string // nil for exported Names; otherwise import path
 	typ     *Type   // .(*FuncType) underneath
diff --git a/src/pkg/runtime/type.h b/src/pkg/runtime/type.h
index f81c986..9dc7881 100644
--- a/src/pkg/runtime/type.h
+++ b/src/pkg/runtime/type.h
@@ -60,9 +60,9 @@
 
 struct Method
 {
-	uint32 hash;
 	String *name;
 	String *pkgPath;
+	Type	*mtyp;
 	Type *typ;
 	void (*ifn)(void);
 	void (*tfn)(void);
@@ -85,8 +85,6 @@
 
 struct IMethod
 {
-	uint32 hash;
-	uint32 perm;
 	String *name;
 	String *pkgPath;
 	Type *type;
diff --git a/test/fixedbugs/bug248.dir/bug0.go b/test/fixedbugs/bug248.dir/bug0.go
new file mode 100644
index 0000000..7fc7401
--- /dev/null
+++ b/test/fixedbugs/bug248.dir/bug0.go
@@ -0,0 +1,9 @@
+package p
+
+type T struct {
+	X, Y int
+}
+
+type I interface {
+	M(T)
+}
diff --git a/test/fixedbugs/bug248.dir/bug1.go b/test/fixedbugs/bug248.dir/bug1.go
new file mode 100644
index 0000000..7fc7401
--- /dev/null
+++ b/test/fixedbugs/bug248.dir/bug1.go
@@ -0,0 +1,9 @@
+package p
+
+type T struct {
+	X, Y int
+}
+
+type I interface {
+	M(T)
+}
diff --git a/test/fixedbugs/bug248.dir/bug2.go b/test/fixedbugs/bug248.dir/bug2.go
new file mode 100644
index 0000000..d562bf6
--- /dev/null
+++ b/test/fixedbugs/bug248.dir/bug2.go
@@ -0,0 +1,92 @@
+package main
+
+import (
+	p0 "./bug0"
+	p1 "./bug1"
+
+	"reflect"
+	"strings"
+)
+
+var v0 p0.T
+var v1 p1.T
+
+type I0 interface {
+	M(p0.T)
+}
+
+type I1 interface {
+	M(p1.T)
+}
+
+type t0 int
+
+func (t0) M(p0.T) {}
+
+type t1 float
+
+func (t1) M(p1.T) {}
+
+var i0 I0 = t0(0) // ok
+var i1 I1 = t1(0) // ok
+
+var p0i p0.I = t0(0) // ok
+var p1i p1.I = t1(0) // ok
+
+func main() {
+	// check that reflect paths are correct,
+	// meaning that reflect data for v0, v1 didn't get confused.
+
+	// path is full (rooted) path name.  check suffix only.
+	if s := reflect.Typeof(v0).PkgPath(); !strings.HasSuffix(s, "/bug0") {
+		panicln("bad v0 path", len(s), s)
+	}
+	if s := reflect.Typeof(v1).PkgPath(); !strings.HasSuffix(s, "/bug1") {
+		panicln("bad v1 path", s)
+	}
+
+	// check that dynamic interface check doesn't get confused
+	var i interface{} = t0(0)
+	if _, ok := i.(I1); ok {
+		panicln("used t0 as i1")
+	}
+	if _, ok := i.(p1.I); ok {
+		panicln("used t0 as p1.I")
+	}
+
+	i = t1(1)
+	if _, ok := i.(I0); ok {
+		panicln("used t1 as i0")
+	}
+	if _, ok := i.(p0.I); ok {
+		panicln("used t1 as p0.I")
+	}
+
+	// check that type switch works.
+	// the worry is that if p0.T and p1.T have the same hash,
+	// the binary search will handle one of them incorrectly.
+	for j := 0; j < 3; j++ {
+		switch j {
+		case 0:
+			i = p0.T{}
+		case 1:
+			i = p1.T{}
+		case 2:
+			i = 3.14
+		}
+		switch k := i.(type) {
+		case p0.T:
+			if j != 0 {
+				panicln("type switch p0.T")
+			}
+		case p1.T:
+			if j != 1 {
+				panicln("type switch p1.T")
+			}
+		default:
+			if j != 2 {
+				panicln("type switch default", j)
+			}
+		}
+	}
+}
diff --git a/test/fixedbugs/bug248.dir/bug3.go b/test/fixedbugs/bug248.dir/bug3.go
new file mode 100644
index 0000000..e59982f
--- /dev/null
+++ b/test/fixedbugs/bug248.dir/bug3.go
@@ -0,0 +1,69 @@
+package main
+
+import (
+	p0 "./bug0"
+	p1 "./bug1"
+)
+
+// both p0.T and p1.T are struct { X, Y int }.
+
+var v0 p0.T
+var v1 p1.T
+
+// interfaces involving the two
+
+type I0 interface {
+	M(p0.T)
+}
+
+type I1 interface {
+	M(p1.T)
+}
+
+// t0 satisfies I0 and p0.I
+type t0 int
+
+func (t0) M(p0.T) {}
+
+// t1 satisfies I1 and p1.I
+type t1 float
+
+func (t1) M(p1.T) {}
+
+// check static interface assignments
+var i0 I0 = t0(0) // ok
+var i1 I1 = t1(0) // ok
+
+var i2 I0 = t1(0) // ERROR "is not"
+var i3 I1 = t0(0) // ERROR "is not"
+
+var p0i p0.I = t0(0) // ok
+var p1i p1.I = t1(0) // ok
+
+var p0i1 p0.I = t1(0) // ERROR "is not"
+var p0i2 p1.I = t0(0) // ERROR "is not"
+
+func main() {
+	// check that cannot assign one to the other,
+	// but can convert.
+	v0 = v1 // ERROR "assign"
+	v1 = v0 // ERROR "assign"
+
+	v0 = p0.T(v1)
+	v1 = p1.T(v0)
+
+	i0 = i1   // ERROR "need type assertion"
+	i1 = i0   // ERROR "need type assertion"
+	p0i = i1  // ERROR "need type assertion"
+	p1i = i0  // ERROR "need type assertion"
+	i0 = p1i  // ERROR "need type assertion"
+	i1 = p0i  // ERROR "need type assertion"
+	p0i = p1i // ERROR "need type assertion"
+	p1i = p0i // ERROR "need type assertion"
+
+	i0 = p0i
+	p0i = i0
+
+	i1 = p1i
+	p1i = i1
+}
diff --git a/test/fixedbugs/bug248.go b/test/fixedbugs/bug248.go
new file mode 100644
index 0000000..055bf1f
--- /dev/null
+++ b/test/fixedbugs/bug248.go
@@ -0,0 +1,12 @@
+// $G $D/$F.dir/bug0.go &&
+// $G $D/$F.dir/bug1.go &&
+// $G $D/$F.dir/bug2.go &&
+// errchk $G -e $D/$F.dir/bug3.go &&
+// $L bug2.$A &&
+// ./$A.out || echo BUG: failed to compile
+
+// 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.
+
+ignored
diff --git a/test/golden.out b/test/golden.out
index 906ecac..d87842e 100644
--- a/test/golden.out
+++ b/test/golden.out
@@ -138,7 +138,7 @@
 
 =========== fixedbugs/bug148.go
 2 3
-interface is main.T, not main.T·1
+interface is main.T, not main.T
 throw: interface conversion
 
 panic PC=xxx
diff --git a/test/typeswitch2.go b/test/typeswitch2.go
new file mode 100644
index 0000000..f8fe396
--- /dev/null
+++ b/test/typeswitch2.go
@@ -0,0 +1,28 @@
+// errchk $G -e $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
+
+import "io"
+
+func whatis(x interface{}) string {
+	switch x.(type) {
+	case int:
+		return "int"
+	case int: // ERROR "duplicate"
+		return "int8"
+	case io.Reader:
+		return "Reader1"
+	case io.Reader: // ERROR "duplicate"
+		return "Reader2"
+	case interface { r(); w() }:
+		return "rw"
+	case interface { w(); r() }:	// ERROR "duplicate"
+		return "wr"
+	
+	}
+	return ""
+}