Commit cda1947f authored by Ian Lance Taylor's avatar Ian Lance Taylor

runtime: don't say "different packages" if they may not be different

Fix the panic message produced for an interface conversion error to
only say "types from different packages" if they are definitely from
different packges. If they may be from the same package, say "types
from different scopes."

Updates #18911
Fixes #26094

Change-Id: I0cea50ba31007d88e70c067b4680009ede69bab9
Reviewed-on: https://go-review.googlesource.com/123395Reviewed-by: default avatarAustin Clements <austin@google.com>
parent 12ed0dde
...@@ -19,32 +19,37 @@ type Error interface { ...@@ -19,32 +19,37 @@ type Error interface {
// A TypeAssertionError explains a failed type assertion. // A TypeAssertionError explains a failed type assertion.
type TypeAssertionError struct { type TypeAssertionError struct {
interfaceString string _interface *_type
concreteString string concrete *_type
assertedString string asserted *_type
missingMethod string // one method needed by Interface, missing from Concrete missingMethod string // one method needed by Interface, missing from Concrete
} }
func (*TypeAssertionError) RuntimeError() {} func (*TypeAssertionError) RuntimeError() {}
func (e *TypeAssertionError) Error() string { func (e *TypeAssertionError) Error() string {
inter := e.interfaceString inter := "interface"
if inter == "" { if e._interface != nil {
inter = "interface" inter = e._interface.string()
} }
if e.concreteString == "" { as := e.asserted.string()
return "interface conversion: " + inter + " is nil, not " + e.assertedString if e.concrete == nil {
return "interface conversion: " + inter + " is nil, not " + as
} }
cs := e.concrete.string()
if e.missingMethod == "" { if e.missingMethod == "" {
msg := "interface conversion: " + inter + " is " + e.concreteString + msg := "interface conversion: " + inter + " is " + cs + ", not " + as
", not " + e.assertedString if cs == as {
if e.concreteString == e.assertedString {
// provide slightly clearer error message // provide slightly clearer error message
if e.concrete.pkgpath() != e.asserted.pkgpath() {
msg += " (types from different packages)" msg += " (types from different packages)"
} else {
msg += " (types from different scopes)"
}
} }
return msg return msg
} }
return "interface conversion: " + e.concreteString + " is not " + e.assertedString + return "interface conversion: " + cs + " is not " + as +
": missing method " + e.missingMethod ": missing method " + e.missingMethod
} }
......
...@@ -41,7 +41,7 @@ func getitab(inter *interfacetype, typ *_type, canfail bool) *itab { ...@@ -41,7 +41,7 @@ func getitab(inter *interfacetype, typ *_type, canfail bool) *itab {
return nil return nil
} }
name := inter.typ.nameOff(inter.mhdr[0].name) name := inter.typ.nameOff(inter.mhdr[0].name)
panic(&TypeAssertionError{"", typ.string(), inter.typ.string(), name.name()}) panic(&TypeAssertionError{nil, typ, &inter.typ, name.name()})
} }
var m *itab var m *itab
...@@ -82,7 +82,7 @@ finish: ...@@ -82,7 +82,7 @@ finish:
// The cached result doesn't record which // The cached result doesn't record which
// interface function was missing, so initialize // interface function was missing, so initialize
// the itab again to get the missing function name. // the itab again to get the missing function name.
panic(&TypeAssertionError{concreteString: typ.string(), assertedString: inter.typ.string(), missingMethod: m.init()}) panic(&TypeAssertionError{concrete: typ, asserted: &inter.typ, missingMethod: m.init()})
} }
// find finds the given interface/type pair in t. // find finds the given interface/type pair in t.
...@@ -245,11 +245,7 @@ func itabsinit() { ...@@ -245,11 +245,7 @@ func itabsinit() {
// want = the static type we're trying to convert to. // want = the static type we're trying to convert to.
// iface = the static type we're converting from. // iface = the static type we're converting from.
func panicdottypeE(have, want, iface *_type) { func panicdottypeE(have, want, iface *_type) {
haveString := "" panic(&TypeAssertionError{iface, have, want, ""})
if have != nil {
haveString = have.string()
}
panic(&TypeAssertionError{iface.string(), haveString, want.string(), ""})
} }
// panicdottypeI is called when doing an i.(T) conversion and the conversion fails. // panicdottypeI is called when doing an i.(T) conversion and the conversion fails.
...@@ -265,7 +261,7 @@ func panicdottypeI(have *itab, want, iface *_type) { ...@@ -265,7 +261,7 @@ func panicdottypeI(have *itab, want, iface *_type) {
// panicnildottype is called when doing a i.(T) conversion and the interface i is nil. // panicnildottype is called when doing a i.(T) conversion and the interface i is nil.
// want = the static type we're trying to convert to. // want = the static type we're trying to convert to.
func panicnildottype(want *_type) { func panicnildottype(want *_type) {
panic(&TypeAssertionError{"", "", want.string(), ""}) panic(&TypeAssertionError{nil, nil, want, ""})
// TODO: Add the static type we're converting from as well. // TODO: Add the static type we're converting from as well.
// It might generate a better error message. // It might generate a better error message.
// Just to match other nil conversion errors, we don't for now. // Just to match other nil conversion errors, we don't for now.
...@@ -516,7 +512,7 @@ func assertI2I(inter *interfacetype, i iface) (r iface) { ...@@ -516,7 +512,7 @@ func assertI2I(inter *interfacetype, i iface) (r iface) {
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.
panic(&TypeAssertionError{"", "", inter.typ.string(), ""}) panic(&TypeAssertionError{nil, nil, &inter.typ, ""})
} }
if tab.inter == inter { if tab.inter == inter {
r.tab = tab r.tab = tab
...@@ -549,7 +545,7 @@ func assertE2I(inter *interfacetype, e eface) (r iface) { ...@@ -549,7 +545,7 @@ func assertE2I(inter *interfacetype, e eface) (r iface) {
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.
panic(&TypeAssertionError{"", "", inter.typ.string(), ""}) panic(&TypeAssertionError{nil, nil, &inter.typ, ""})
} }
r.tab = getitab(inter, t, false) r.tab = getitab(inter, t, false)
r.data = e.data r.data = e.data
......
...@@ -131,6 +131,25 @@ func (t *_type) name() string { ...@@ -131,6 +131,25 @@ func (t *_type) name() string {
return s[i+1:] return s[i+1:]
} }
// pkgpath returns the path of the package where t was defined, if
// available. This is not the same as the reflect package's PkgPath
// method, in that it returns the package path for struct and interface
// types, not just named types.
func (t *_type) pkgpath() string {
if u := t.uncommon(); u != nil {
return t.nameOff(u.pkgpath).name()
}
switch t.kind & kindMask {
case kindStruct:
st := (*structtype)(unsafe.Pointer(t))
return st.pkgPath.name()
case kindInterface:
it := (*interfacetype)(unsafe.Pointer(t))
return it.pkgpath.name()
}
return ""
}
// reflectOffs holds type offsets defined at run time by the reflect package. // reflectOffs holds type offsets defined at run time by the reflect package.
// //
// When a type is defined at run time, its *rtype data lives on the heap. // When a type is defined at run time, its *rtype data lives on the heap.
......
// run
// Copyright 2018 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 "strings"
var X interface{}
type T struct{}
func scopes() {
p, ok := recover().(error)
if ok && strings.Contains(p.Error(), "different scopes") {
return
}
panic(p)
}
func F1() {
type T struct{}
X = T{}
}
func F2() {
type T struct{}
defer scopes()
_ = X.(T)
}
func F3() {
defer scopes()
_ = X.(T)
}
func F4() {
X = T{}
}
func main() {
F1() // set X to F1's T
F2() // check that X is not F2's T
F3() // check that X is not package T
F4() // set X to package T
F2() // check that X is not F2's T
}
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