runtime: make type assertion a runtime.Error, the first of many

R=r
CC=golang-dev
https://golang.org/cl/805043
diff --git a/src/pkg/runtime/386/asm.s b/src/pkg/runtime/386/asm.s
index e2eca81..862c274 100644
--- a/src/pkg/runtime/386/asm.s
+++ b/src/pkg/runtime/386/asm.s
@@ -369,20 +369,6 @@
 	INT	$3
 	RET
 
-// callString(f, arg, out)
-// call Go f(arg), which returns a string, and store in out
-TEXT callString(SB), 7, $24
-	MOVL	arg+4(FP), BX
-	MOVL	f+0(FP), CX
-	MOVL	BX, 0(SP)
-	CALL	*CX
-	MOVL	out+8(FP), DI
-	LEAL	4(SP), SI
-	MOVSL
-	MOVSL
-	MOVSL
-	RET
-
 GLOBL m0(SB), $1024
 GLOBL g0(SB), $1024
 GLOBL tls0(SB), $32
diff --git a/src/pkg/runtime/Makefile b/src/pkg/runtime/Makefile
index 2ea11c0..8828426 100644
--- a/src/pkg/runtime/Makefile
+++ b/src/pkg/runtime/Makefile
@@ -21,6 +21,7 @@
 CFLAGS=-I$(GOOS) -I$(GOOS)/$(GOARCH) -wF $(CFLAGS_$(SIZE)) $(CFLAGS_$(GOARCH)) $(CFLAGS_$(GOOS))
 
 GOFILES=\
+	error.go\
 	extern.go\
 	type.go\
 	version.go\
diff --git a/src/pkg/runtime/amd64/asm.s b/src/pkg/runtime/amd64/asm.s
index fb32be05..9c966c5 100644
--- a/src/pkg/runtime/amd64/asm.s
+++ b/src/pkg/runtime/amd64/asm.s
@@ -311,16 +311,3 @@
 	INT	$3
 	RET
 
-// callString(f, arg, out)
-// call Go f(arg), which returns a string, and store in out
-TEXT callString(SB), 7, $24
-	MOVQ	arg+8(FP), BX
-	MOVQ	f+0(FP), CX
-	MOVQ	BX, 0(SP)
-	CALL	*CX
-	MOVQ	out+16(FP), DI
-	LEAQ	8(SP), SI
-	MOVSQ
-	MOVSQ
-	RET
-
diff --git a/src/pkg/runtime/arm/asm.s b/src/pkg/runtime/arm/asm.s
index 4961045..19fa1cc 100644
--- a/src/pkg/runtime/arm/asm.s
+++ b/src/pkg/runtime/arm/asm.s
@@ -264,18 +264,3 @@
 	MOVW	$0, R0
 	MOVW	(R0), R1
 
-// callString(f, arg, out)
-// call Go f(arg), which returns a string, and store in out
-TEXT callString(SB), 7, $24
-	MOVW	arg+4(FP), R1
-	MOVW	f+0(FP), R0
-	MOVW	R1, 0(SP)
-	BL	(R0)
-	MOVW	4(SP), R1
-	MOVW	8(SP), R2
-	MOVW	12(SP), R3
-	MOVW	out+8(FP), R0
-	MOVW	R1, 0(R0)
-	MOVW	R2, 4(R0)
-	MOVW	R3, 8(R0)
-	RET
diff --git a/src/pkg/runtime/error.go b/src/pkg/runtime/error.go
new file mode 100644
index 0000000..a7d3bed
--- /dev/null
+++ b/src/pkg/runtime/error.go
@@ -0,0 +1,112 @@
+// Copyright 2010 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 runtime
+
+// The Error interface identifies a run time error.
+type Error interface {
+	String() string
+	RuntimeError() // no-op that uniquely identifies runtime.Error
+}
+
+// A TypeAssertionError explains a failed type assertion.
+type TypeAssertionError struct {
+	interfaceType   Type // interface had this type
+	concreteType    Type // concrete value had this type
+	assertedType    Type // asserted type
+	interfaceString string
+	concreteString  string
+	assertedString  string
+	missingMethod   string // one method needed by Interface, missing from Concrete
+}
+
+func (e *TypeAssertionError) String() string {
+	inter := e.interfaceString
+	if inter == "" {
+		inter = "interface"
+	}
+	if e.concreteType == nil {
+		return "interface conversion: " + inter + " is nil, not " + e.assertedString
+	}
+	if e.missingMethod == "" {
+		return "interface conversion: " + inter + " is " + e.concreteString +
+			", not " + e.assertedString
+	}
+	return "interface conversion: " + e.concreteString + " is not " + e.assertedString +
+		": missing method " + e.missingMethod
+}
+
+// Concrete returns the type of the concrete value in the failed type assertion.
+// If the interface value was nil, Concrete returns nil.
+func (e *TypeAssertionError) Concrete() Type {
+	return e.concreteType
+}
+
+// Asserted returns the type incorrectly asserted by the type assertion.
+func (e *TypeAssertionError) Asserted() Type {
+	return e.assertedType
+}
+
+// If the type assertion is to an interface type, MissingMethod returns the
+// name of a method needed to satisfy that interface type but not implemented
+// by Concrete.  If there are multiple such methods,
+// MissingMethod returns one; which one is unspecified.
+// If the type assertion is not to an interface type, MissingMethod returns an empty string.
+func (e *TypeAssertionError) MissingMethod() string {
+	return e.missingMethod
+}
+
+func (*TypeAssertionError) RuntimeError() {}
+
+// For calling from C.
+func newTypeAssertionError(pt1, pt2, pt3 *Type, ps1, ps2, ps3 *string, pmeth *string, ret *interface{}) {
+	var t1, t2, t3 Type
+	var s1, s2, s3, meth string
+
+	if pt1 != nil {
+		t1 = *pt1
+	}
+	if pt2 != nil {
+		t2 = *pt2
+	}
+	if pt3 != nil {
+		t3 = *pt3
+	}
+	if ps1 != nil {
+		s1 = *ps1
+	}
+	if ps2 != nil {
+		s2 = *ps2
+	}
+	if ps3 != nil {
+		s3 = *ps3
+	}
+	if pmeth != nil {
+		meth = *pmeth
+	}
+	*ret = &TypeAssertionError{t1, t2, t3, s1, s2, s3, meth}
+}
+
+type stringer interface {
+	String() string
+}
+
+// For calling from C.
+// Prints an argument to panic.
+// There's room for arbitrary complexity here, but we keep it
+// simple and handle just a few important cases: int, string, and Stringer.
+func printany(i interface{}) {
+	switch v := i.(type) {
+	case nil:
+		print("nil")
+	case stringer:
+		print(v.String())
+	case int:
+		print(v)
+	case string:
+		print(v)
+	default:
+		print(i)
+	}
+}
diff --git a/src/pkg/runtime/iface.c b/src/pkg/runtime/iface.c
index ce42346..1af7ca7 100644
--- a/src/pkg/runtime/iface.c
+++ b/src/pkg/runtime/iface.c
@@ -46,10 +46,13 @@
 	Itab *m;
 	UncommonType *x;
 	Type *itype;
+	Eface err;
 
 	if(inter->mhdr.len == 0)
 		throw("internal error - misuse of itab");
 
+	locked = 0;
+
 	// easy case
 	x = type->x;
 	if(x == nil) {
@@ -114,9 +117,12 @@
 				if(!canfail) {
 				throw:
 					// didn't find method
-					printf("%S is not %S: missing method %S\n",
-						*type->string, *inter->string, *iname);
-					throw("interface conversion");
+					·newTypeAssertionError(nil, type, inter,
+						nil, type->string, inter->string,
+						iname, &err);
+					if(locked)
+						unlock(&ifacelock);
+					·panic(err);
 					return nil;	// not reached
 				}
 				m->bad = 1;
@@ -211,16 +217,21 @@
 {
 	Itab *tab;
 	byte *ret;
+	Eface err;
 
 	ret = (byte*)(&i+1);
 	tab = i.tab;
 	if(tab == nil) {
-		printf("interface is nil, not %S\n", *t->string);
-		throw("interface conversion");
+		·newTypeAssertionError(nil, nil, t,
+			nil, nil, t->string,
+			nil, &err);
+		·panic(err);
 	}
 	if(tab->type != t) {
-		printf("%S is %S, not %S\n", *tab->inter->string, *tab->type->string, *t->string);
-		throw("interface conversion");
+		·newTypeAssertionError(tab->inter, tab->type, t,
+			tab->inter->string, tab->type->string, t->string,
+			nil, &err);
+		·panic(err);
 	}
 	copyout(t, &i.data, ret);
 }
@@ -254,15 +265,21 @@
 ·ifaceE2T(Type *t, Eface e, ...)
 {
 	byte *ret;
+	Eface err;
 
 	ret = (byte*)(&e+1);
 
+	if(e.type == nil) {
+		·newTypeAssertionError(nil, nil, t,
+			nil, nil, t->string,
+			nil, &err);
+		·panic(err);
+	}
 	if(e.type != t) {
-		if(e.type == nil)
-			printf("interface is nil, not %S\n", *t->string);
-		else
-			printf("interface is %S, not %S\n", *e.type->string, *t->string);
-		throw("interface conversion");
+		·newTypeAssertionError(nil, e.type, t,
+			nil, e.type->string, t->string,
+			nil, &err);
+		·panic(err);
 	}
 	copyout(t, &e.data, ret);
 }
@@ -336,12 +353,15 @@
 ·ifaceI2Ix(InterfaceType *inter, Iface i, Iface ret)
 {
 	Itab *tab;
+	Eface err;
 
 	tab = i.tab;
 	if(tab == nil) {
 		// explicit conversions require non-nil interface value.
-		printf("interface is nil, not %S\n", *inter->string);
-		throw("interface conversion");
+		·newTypeAssertionError(nil, nil, inter,
+			nil, nil, inter->string,
+			nil, &err);
+		·panic(err);
 	} else {
 		ret = i;
 		if(tab->inter != inter)
@@ -385,12 +405,15 @@
 ifaceE2I(InterfaceType *inter, Eface e, Iface *ret)
 {
 	Type *t;
+	Eface err;
 
 	t = e.type;
 	if(t == nil) {
 		// explicit conversions require non-nil interface value.
-		printf("interface is nil, not %S\n", *inter->string);
-		throw("interface conversion");
+		·newTypeAssertionError(nil, nil, inter,
+			nil, nil, inter->string,
+			nil, &err);
+		·panic(err);
 	} else {
 		ret->data = e.data;
 		ret->tab = itab(inter, t, 0);
diff --git a/src/pkg/runtime/print.c b/src/pkg/runtime/print.c
index 5e4f2f5..1248432 100644
--- a/src/pkg/runtime/print.c
+++ b/src/pkg/runtime/print.c
@@ -348,68 +348,3 @@
 {
 	write(fd, "\n", 1);
 }
-
-// print an empty interface, for use by panic.
-// this could be arbitrarily complex in general,
-// so we pick off only a few important cases:
-// int, string, and values with a String() string method.
-void
-printany(Eface e)
-{
-	int32 i;
-	FuncType *ft;
-	Method *m;
-	String s;
-	Type *rt;
-	UncommonType *x;
-
-	if(e.type == nil) {
-		write(fd, "nil", 3);
-		return;
-	}
-
-	if((x=e.type->x) != nil) {
-		for(i=0; i<x->mhdr.len; i++) {
-			// Look for String() string method.
-			m = &x->m[i];
-			if(m->name->len == 6 &&
-			   mcmp(m->name->str, (byte*)"String", 6) == 0 &&
-			   // Found String; check method signature for func() string.
-			   m->mtyp->kind == KindFunc &&
-			   (ft = (FuncType*)m->mtyp)->in.len == 0 &&
-			   ft->out.len == 1 &&
-			   // Found single output.  Is it string?
-			   // Only base types have name != nil but pkgPath == nil.
-			   (rt = *(Type**)ft->out.array)->kind == KindString &&
-			   rt->x != nil &&
-			   rt->x->name != nil && rt->x->pkgPath == nil) {
-				// Found the method!
-				// Have to use assembly to call it
-				// and save the return value.
-				callString(m->ifn, e.data, &s);
-				·printstring(s);
-				return;
-			}
-		}
-	}
-
-	switch(e.type->kind & ~KindNoPointers) {
-	case KindInt:
-		mcpy((byte*)&i, (byte*)&e.data, sizeof(i));
-		·printint(i);
-		break;
-
-	case KindString:
-		·printstring(*(String*)e.data);
-		break;
-
-	default:
-		// Could print the other numeric types,
-		// but that's overkill: good panics have
-		// a string method anyway.
-		·printstring(*e.type->string);
-		write(fd, "(???)", 5);
-		break;
-	}
-
-}
diff --git a/src/pkg/runtime/proc.c b/src/pkg/runtime/proc.c
index 6001c22..3dd9977 100644
--- a/src/pkg/runtime/proc.c
+++ b/src/pkg/runtime/proc.c
@@ -935,7 +935,7 @@
 		printf("\t");
 	}
 	printf("panic: ");
-	printany(p->arg);
+	·printany(p->arg);
 	if(p->recovered)
 		printf(" [recovered]");
 	printf("\n");
diff --git a/src/pkg/runtime/runtime.h b/src/pkg/runtime/runtime.h
index adb8311..b4011b7 100644
--- a/src/pkg/runtime/runtime.h
+++ b/src/pkg/runtime/runtime.h
@@ -361,7 +361,6 @@
 /*
  * very low level c-called
  */
-void	callString(void(*fn)(void), void *arg, String *out);
 void	gogo(Gobuf*, uintptr);
 void	gogocall(Gobuf*, void(*)(void));
 uintptr	gosave(Gobuf*);
@@ -372,7 +371,6 @@
 void	throw(int8*);
 uint32	rnd(uint32, uint32);
 void	prints(int8*);
-void	printany(Eface);
 void	printf(int8*, ...);
 byte*	mchr(byte*, byte, byte*);
 void	mcpy(byte*, byte*, uint32);
@@ -432,6 +430,7 @@
 void	gettime(int64*, int32*);
 int32	callers(int32, uintptr*, int32);
 int64	nanotime(void);
+void	panic(int32);
 
 #pragma	varargck	argpos	printf	1
 
@@ -530,8 +529,15 @@
 void	runtime_printhex(uint64);
 void	runtime_printslice(Slice);
 void	runtime_printcomplex(Complex128);
-void	panic(int32);
 void	reflect·call(byte*, byte*, uint32);
+void	·panic(Eface);
+
+/*
+ * runtime c-called (but written in Go)
+ */
+void ·newError(String, Eface*);
+void	·printany(Eface);
+void	·newTypeAssertionError(Type*, Type*, Type*, String*, String*, String*, String*, Eface*);
 
 /*
  * wrapped for go users