1
0
mirror of https://github.com/golang/go synced 2024-11-25 13:47:57 -07:00

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

R=r
CC=golang-dev
https://golang.org/cl/805043
This commit is contained in:
Russ Cox 2010-03-31 15:55:10 -07:00
parent b6ad074efc
commit 63e878a750
10 changed files with 166 additions and 135 deletions

View File

@ -369,20 +369,6 @@ TEXT stackcheck(SB), 7, $0
INT $3 INT $3
RET 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 m0(SB), $1024
GLOBL g0(SB), $1024 GLOBL g0(SB), $1024
GLOBL tls0(SB), $32 GLOBL tls0(SB), $32

View File

@ -21,6 +21,7 @@ CFLAGS_mingw=-D__MINGW__
CFLAGS=-I$(GOOS) -I$(GOOS)/$(GOARCH) -wF $(CFLAGS_$(SIZE)) $(CFLAGS_$(GOARCH)) $(CFLAGS_$(GOOS)) CFLAGS=-I$(GOOS) -I$(GOOS)/$(GOARCH) -wF $(CFLAGS_$(SIZE)) $(CFLAGS_$(GOARCH)) $(CFLAGS_$(GOOS))
GOFILES=\ GOFILES=\
error.go\
extern.go\ extern.go\
type.go\ type.go\
version.go\ version.go\

View File

@ -311,16 +311,3 @@ TEXT stackcheck(SB), 7, $0
INT $3 INT $3
RET 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

View File

@ -264,18 +264,3 @@ TEXT abort(SB),7,$0
MOVW $0, R0 MOVW $0, R0
MOVW (R0), R1 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

112
src/pkg/runtime/error.go Normal file
View File

@ -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)
}
}

View File

@ -46,10 +46,13 @@ itab(InterfaceType *inter, Type *type, int32 canfail)
Itab *m; Itab *m;
UncommonType *x; UncommonType *x;
Type *itype; Type *itype;
Eface err;
if(inter->mhdr.len == 0) if(inter->mhdr.len == 0)
throw("internal error - misuse of itab"); throw("internal error - misuse of itab");
locked = 0;
// easy case // easy case
x = type->x; x = type->x;
if(x == nil) { if(x == nil) {
@ -114,9 +117,12 @@ search:
if(!canfail) { if(!canfail) {
throw: throw:
// didn't find method // didn't find method
printf("%S is not %S: missing method %S\n", ·newTypeAssertionError(nil, type, inter,
*type->string, *inter->string, *iname); nil, type->string, inter->string,
throw("interface conversion"); iname, &err);
if(locked)
unlock(&ifacelock);
·panic(err);
return nil; // not reached return nil; // not reached
} }
m->bad = 1; m->bad = 1;
@ -211,16 +217,21 @@ void
{ {
Itab *tab; Itab *tab;
byte *ret; byte *ret;
Eface err;
ret = (byte*)(&i+1); ret = (byte*)(&i+1);
tab = i.tab; tab = i.tab;
if(tab == nil) { if(tab == nil) {
printf("interface is nil, not %S\n", *t->string); ·newTypeAssertionError(nil, nil, t,
throw("interface conversion"); nil, nil, t->string,
nil, &err);
·panic(err);
} }
if(tab->type != t) { if(tab->type != t) {
printf("%S is %S, not %S\n", *tab->inter->string, *tab->type->string, *t->string); ·newTypeAssertionError(tab->inter, tab->type, t,
throw("interface conversion"); tab->inter->string, tab->type->string, t->string,
nil, &err);
·panic(err);
} }
copyout(t, &i.data, ret); copyout(t, &i.data, ret);
} }
@ -254,15 +265,21 @@ void
·ifaceE2T(Type *t, Eface e, ...) ·ifaceE2T(Type *t, Eface e, ...)
{ {
byte *ret; byte *ret;
Eface err;
ret = (byte*)(&e+1); 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 != t) {
if(e.type == nil) ·newTypeAssertionError(nil, e.type, t,
printf("interface is nil, not %S\n", *t->string); nil, e.type->string, t->string,
else nil, &err);
printf("interface is %S, not %S\n", *e.type->string, *t->string); ·panic(err);
throw("interface conversion");
} }
copyout(t, &e.data, ret); copyout(t, &e.data, ret);
} }
@ -336,12 +353,15 @@ void
·ifaceI2Ix(InterfaceType *inter, Iface i, Iface ret) ·ifaceI2Ix(InterfaceType *inter, Iface i, Iface ret)
{ {
Itab *tab; Itab *tab;
Eface err;
tab = i.tab; tab = i.tab;
if(tab == nil) { if(tab == nil) {
// explicit conversions require non-nil interface value. // explicit conversions require non-nil interface value.
printf("interface is nil, not %S\n", *inter->string); ·newTypeAssertionError(nil, nil, inter,
throw("interface conversion"); nil, nil, inter->string,
nil, &err);
·panic(err);
} else { } else {
ret = i; ret = i;
if(tab->inter != inter) if(tab->inter != inter)
@ -385,12 +405,15 @@ void
ifaceE2I(InterfaceType *inter, Eface e, Iface *ret) ifaceE2I(InterfaceType *inter, Eface e, Iface *ret)
{ {
Type *t; Type *t;
Eface err;
t = e.type; t = e.type;
if(t == nil) { if(t == nil) {
// explicit conversions require non-nil interface value. // explicit conversions require non-nil interface value.
printf("interface is nil, not %S\n", *inter->string); ·newTypeAssertionError(nil, nil, inter,
throw("interface conversion"); nil, nil, inter->string,
nil, &err);
·panic(err);
} else { } else {
ret->data = e.data; ret->data = e.data;
ret->tab = itab(inter, t, 0); ret->tab = itab(inter, t, 0);

View File

@ -348,68 +348,3 @@ void
{ {
write(fd, "\n", 1); 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;
}
}

View File

@ -935,7 +935,7 @@ printpanics(Panic *p)
printf("\t"); printf("\t");
} }
printf("panic: "); printf("panic: ");
printany(p->arg); ·printany(p->arg);
if(p->recovered) if(p->recovered)
printf(" [recovered]"); printf(" [recovered]");
printf("\n"); printf("\n");

View File

@ -361,7 +361,6 @@ int32 charntorune(int32*, uint8*, int32);
/* /*
* very low level c-called * very low level c-called
*/ */
void callString(void(*fn)(void), void *arg, String *out);
void gogo(Gobuf*, uintptr); void gogo(Gobuf*, uintptr);
void gogocall(Gobuf*, void(*)(void)); void gogocall(Gobuf*, void(*)(void));
uintptr gosave(Gobuf*); uintptr gosave(Gobuf*);
@ -372,7 +371,6 @@ void* getu(void);
void throw(int8*); void throw(int8*);
uint32 rnd(uint32, uint32); uint32 rnd(uint32, uint32);
void prints(int8*); void prints(int8*);
void printany(Eface);
void printf(int8*, ...); void printf(int8*, ...);
byte* mchr(byte*, byte, byte*); byte* mchr(byte*, byte, byte*);
void mcpy(byte*, byte*, uint32); void mcpy(byte*, byte*, uint32);
@ -432,6 +430,7 @@ bool sigsend(int32 sig);
void gettime(int64*, int32*); void gettime(int64*, int32*);
int32 callers(int32, uintptr*, int32); int32 callers(int32, uintptr*, int32);
int64 nanotime(void); int64 nanotime(void);
void panic(int32);
#pragma varargck argpos printf 1 #pragma varargck argpos printf 1
@ -530,8 +529,15 @@ void runtime_printuint(uint64);
void runtime_printhex(uint64); void runtime_printhex(uint64);
void runtime_printslice(Slice); void runtime_printslice(Slice);
void runtime_printcomplex(Complex128); void runtime_printcomplex(Complex128);
void panic(int32);
void reflect·call(byte*, byte*, uint32); 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 * wrapped for go users

View File

@ -116,14 +116,12 @@ PASS
== interface/ == interface/
=========== interface/fail.go =========== interface/fail.go
*main.S is not main.I: missing method Foo panic: interface conversion: *main.S is not main.I: missing method Foo
throw: interface conversion
panic PC=xxx panic PC=xxx
=========== interface/returntype.go =========== interface/returntype.go
*main.S is not main.I2: missing method Name panic: interface conversion: *main.S is not main.I2: missing method Name
throw: interface conversion
panic PC=xxx panic PC=xxx
@ -165,15 +163,13 @@ fixedbugs/bug081.go:9: typechecking loop
M M
=========== fixedbugs/bug113.go =========== fixedbugs/bug113.go
interface is int, not int32 panic: interface conversion: interface is int, not int32
throw: interface conversion
panic PC=xxx panic PC=xxx
=========== fixedbugs/bug148.go =========== fixedbugs/bug148.go
2 3 2 3
interface is main.T, not main.T panic: interface conversion: interface is main.T, not main.T
throw: interface conversion
panic PC=xxx panic PC=xxx