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:
parent
b6ad074efc
commit
63e878a750
@ -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
|
||||||
|
@ -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\
|
||||||
|
@ -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
|
|
||||||
|
|
||||||
|
@ -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
112
src/pkg/runtime/error.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
@ -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");
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user