зеркало из https://github.com/golang/tools.git
go.tools/go/types: *interface types have no methods
Fixes golang/go#5918. R=adonovan CC=golang-dev https://golang.org/cl/12909043
This commit is contained in:
Родитель
0b3996b1d3
Коммит
ae874abc7a
|
@ -298,17 +298,22 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) {
|
||||||
// Verify that LookupFieldOrMethod and MethodSet.Lookup agree.
|
// Verify that LookupFieldOrMethod and MethodSet.Lookup agree.
|
||||||
typ := x.typ
|
typ := x.typ
|
||||||
if x.mode == variable {
|
if x.mode == variable {
|
||||||
// If typ is not an (unnamed) pointer, use *typ instead,
|
// If typ is not an (unnamed) pointer or an interface,
|
||||||
// because the method set of *typ includes the methods
|
// use *typ instead, because the method set of *typ
|
||||||
// of typ.
|
// includes the methods of typ.
|
||||||
// Variables are addressable, so we can always take their
|
// Variables are addressable, so we can always take their
|
||||||
// address.
|
// address.
|
||||||
if _, isPtr := typ.(*Pointer); !isPtr {
|
if _, ok := typ.(*Pointer); !ok {
|
||||||
typ = &Pointer{base: typ}
|
if _, ok := typ.Underlying().(*Interface); !ok {
|
||||||
|
typ = &Pointer{base: typ}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If we created a synthetic pointer type above, we will throw
|
// If we created a synthetic pointer type above, we will throw
|
||||||
// away the method set computed here after use.
|
// away the method set computed here after use.
|
||||||
|
// TODO(gri) Method set computation should probably always compute
|
||||||
|
// both, the value and the pointer receiver method set and represent
|
||||||
|
// them in a single structure.
|
||||||
// TODO(gri) Consider also using a method set cache for the lifetime
|
// TODO(gri) Consider also using a method set cache for the lifetime
|
||||||
// of checker once we rely on MethodSet lookup instead of individual
|
// of checker once we rely on MethodSet lookup instead of individual
|
||||||
// lookup.
|
// lookup.
|
||||||
|
|
|
@ -12,9 +12,9 @@ package types
|
||||||
// indirectly via different packages.)
|
// indirectly via different packages.)
|
||||||
|
|
||||||
// LookupFieldOrMethod looks up a field or method with given package and name
|
// LookupFieldOrMethod looks up a field or method with given package and name
|
||||||
// in typ and returns the corresponding *Var or *Func, an index sequence,
|
// in T and returns the corresponding *Var or *Func, an index sequence, and a
|
||||||
// and a bool indicating if there were any pointer indirections on the path
|
// bool indicating if there were any pointer indirections on the path to the
|
||||||
// to the field or method.
|
// field or method.
|
||||||
//
|
//
|
||||||
// The last index entry is the field or method index in the (possibly embedded)
|
// The last index entry is the field or method index in the (possibly embedded)
|
||||||
// type where the entry was found, either:
|
// type where the entry was found, either:
|
||||||
|
@ -29,8 +29,8 @@ package types
|
||||||
// If no entry is found, a nil object is returned. In this case, the returned
|
// If no entry is found, a nil object is returned. In this case, the returned
|
||||||
// index sequence points to an ambiguous entry if it exists, or it is nil.
|
// index sequence points to an ambiguous entry if it exists, or it is nil.
|
||||||
//
|
//
|
||||||
func LookupFieldOrMethod(typ Type, pkg *Package, name string) (obj Object, index []int, indirect bool) {
|
func LookupFieldOrMethod(T Type, pkg *Package, name string) (obj Object, index []int, indirect bool) {
|
||||||
obj, index, indirect = lookupFieldOrMethod(typ, pkg, name)
|
obj, index, indirect = lookupFieldOrMethod(T, pkg, name)
|
||||||
if obj != nil {
|
if obj != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ func LookupFieldOrMethod(typ Type, pkg *Package, name string) (obj Object, index
|
||||||
// TODO(gri) WTF? There isn't a more direct way? Perhaps we should
|
// TODO(gri) WTF? There isn't a more direct way? Perhaps we should
|
||||||
// outlaw named types to pointer types - they are almost
|
// outlaw named types to pointer types - they are almost
|
||||||
// never what one wants, anyway.
|
// never what one wants, anyway.
|
||||||
if t, _ := typ.(*Named); t != nil {
|
if t, _ := T.(*Named); t != nil {
|
||||||
u := t.underlying
|
u := t.underlying
|
||||||
if _, ok := u.(*Pointer); ok {
|
if _, ok := u.(*Pointer); ok {
|
||||||
// typ is a named type with an underlying type of the form *T,
|
// typ is a named type with an underlying type of the form *T,
|
||||||
|
@ -71,18 +71,31 @@ func LookupFieldOrMethod(typ Type, pkg *Package, name string) (obj Object, index
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func lookupFieldOrMethod(typ Type, pkg *Package, name string) (obj Object, index []int, indirect bool) {
|
func lookupFieldOrMethod(T Type, pkg *Package, name string) (obj Object, index []int, indirect bool) {
|
||||||
// WARNING: The code in this function is extremely subtle - do not modify casually!
|
// WARNING: The code in this function is extremely subtle - do not modify casually!
|
||||||
|
// This function and NewMethodSet should kept in sync.
|
||||||
|
|
||||||
if name == "_" {
|
if name == "_" {
|
||||||
return // blank fields/methods are never found
|
return // blank fields/methods are never found
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typ, isPtr := deref(T)
|
||||||
|
named, _ := typ.(*Named)
|
||||||
|
|
||||||
|
// *typ where typ is an interface has no methods.
|
||||||
|
if isPtr {
|
||||||
|
utyp := typ
|
||||||
|
if named != nil {
|
||||||
|
utyp = named.underlying
|
||||||
|
}
|
||||||
|
if _, ok := utyp.(*Interface); ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Start with typ as single entry at shallowest depth.
|
// Start with typ as single entry at shallowest depth.
|
||||||
// If typ is not a named type, insert a nil type instead.
|
// If typ is not a named type, insert a nil type instead.
|
||||||
typ, isPtr := deref(typ)
|
current := []embeddedType{{named, nil, isPtr, false}}
|
||||||
t, _ := typ.(*Named)
|
|
||||||
current := []embeddedType{{t, nil, isPtr, false}}
|
|
||||||
|
|
||||||
// named types that we have seen already, allocated lazily
|
// named types that we have seen already, allocated lazily
|
||||||
var seen map[*Named]bool
|
var seen map[*Named]bool
|
||||||
|
|
|
@ -28,7 +28,7 @@ func (s *MethodSet) String() string {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
fmt.Fprintln(&buf, "MethodSet {")
|
fmt.Fprintln(&buf, "MethodSet {")
|
||||||
for _, f := range s.list {
|
for _, f := range s.list {
|
||||||
fmt.Fprintln(&buf, f)
|
fmt.Fprintf(&buf, "\t%s\n", f)
|
||||||
}
|
}
|
||||||
fmt.Fprintln(&buf, "}")
|
fmt.Fprintln(&buf, "}")
|
||||||
return buf.String()
|
return buf.String()
|
||||||
|
@ -90,15 +90,28 @@ func (c *cachedMethodSet) of(typ Type) *MethodSet {
|
||||||
// It always returns a non-nil method set, even if it is empty.
|
// It always returns a non-nil method set, even if it is empty.
|
||||||
func NewMethodSet(T Type) *MethodSet {
|
func NewMethodSet(T Type) *MethodSet {
|
||||||
// WARNING: The code in this function is extremely subtle - do not modify casually!
|
// WARNING: The code in this function is extremely subtle - do not modify casually!
|
||||||
|
// This function and lookupFieldOrMethod should be kept in sync.
|
||||||
|
|
||||||
// method set up to the current depth, allocated lazily
|
// method set up to the current depth, allocated lazily
|
||||||
var base methodSet
|
var base methodSet
|
||||||
|
|
||||||
|
typ, isPtr := deref(T)
|
||||||
|
named, _ := typ.(*Named)
|
||||||
|
|
||||||
|
// *typ where typ is an interface has no methods.
|
||||||
|
if isPtr {
|
||||||
|
utyp := typ
|
||||||
|
if named != nil {
|
||||||
|
utyp = named.underlying
|
||||||
|
}
|
||||||
|
if _, ok := utyp.(*Interface); ok {
|
||||||
|
return &emptyMethodSet
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Start with typ as single entry at shallowest depth.
|
// Start with typ as single entry at shallowest depth.
|
||||||
// If typ is not a named type, insert a nil type instead.
|
// If typ is not a named type, insert a nil type instead.
|
||||||
typ, isPtr := deref(T)
|
current := []embeddedType{{named, nil, isPtr, false}}
|
||||||
t, _ := typ.(*Named)
|
|
||||||
current := []embeddedType{{t, nil, isPtr, false}}
|
|
||||||
|
|
||||||
// named types that we have seen already, allocated lazily
|
// named types that we have seen already, allocated lazily
|
||||||
var seen map[*Named]bool
|
var seen map[*Named]bool
|
||||||
|
|
|
@ -188,3 +188,29 @@ func _() {
|
||||||
(&T3{}).v2()
|
(&T3{}).v2()
|
||||||
(&T3{}).p2()
|
(&T3{}).p2()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// *T has no methods if T is an interface type
|
||||||
|
func issue5918() {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
_ = err.Error()
|
||||||
|
_ func() string = err.Error
|
||||||
|
_ func(error) string = error.Error
|
||||||
|
|
||||||
|
perr = &err
|
||||||
|
_ = perr /* ERROR "no field or method" */ .Error()
|
||||||
|
_ func() string = perr /* ERROR "no field or method" */ .Error
|
||||||
|
_ func(*error) string = ( /* ERROR "no field or method" */ *error).Error
|
||||||
|
)
|
||||||
|
|
||||||
|
type T *interface{ m() int }
|
||||||
|
var (
|
||||||
|
x T
|
||||||
|
_ = (*x).m()
|
||||||
|
_ = (*x).m
|
||||||
|
|
||||||
|
_ = x /* ERROR "no field or method" */ .m()
|
||||||
|
_ = x /* ERROR "no field or method" */ .m
|
||||||
|
_ = T /* ERROR "no field or method" */ .m
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -389,8 +389,8 @@ func (p *Package) DumpTo(w io.Writer) {
|
||||||
// Iterate over the keys of mset(*T) since they
|
// Iterate over the keys of mset(*T) since they
|
||||||
// are a superset of mset(T)'s keys.
|
// are a superset of mset(T)'s keys.
|
||||||
// The keys of a types.MethodSet are sorted (by Id).
|
// The keys of a types.MethodSet are sorted (by Id).
|
||||||
mset := methodSetOf(mem.Type())
|
mset := mem.Type().MethodSet()
|
||||||
pmset := methodSetOf(types.NewPointer(mem.Type()))
|
pmset := types.NewPointer(mem.Type()).MethodSet()
|
||||||
for i, n := 0, pmset.Len(); i < n; i++ {
|
for i, n := 0, pmset.Len(); i < n; i++ {
|
||||||
meth := pmset.At(i)
|
meth := pmset.At(i)
|
||||||
// If the method also exists in mset(T), show that instead.
|
// If the method also exists in mset(T), show that instead.
|
||||||
|
|
|
@ -22,16 +22,6 @@ func recvType(obj *types.Func) types.Type {
|
||||||
return obj.Type().(*types.Signature).Recv().Type()
|
return obj.Type().(*types.Signature).Recv().Type()
|
||||||
}
|
}
|
||||||
|
|
||||||
func methodSetOf(typ types.Type) *types.MethodSet {
|
|
||||||
// TODO(adonovan): temporary workaround. Inline it away when fixed.
|
|
||||||
if _, ok := deref(typ).Underlying().(*types.Interface); ok && isPointer(typ) {
|
|
||||||
// TODO(gri): fix: go/types bug: pointer-to-interface
|
|
||||||
// has no methods---yet go/types says it has!
|
|
||||||
return new(types.MethodSet)
|
|
||||||
}
|
|
||||||
return typ.MethodSet()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method returns the Function implementing method meth, building
|
// Method returns the Function implementing method meth, building
|
||||||
// wrapper methods on demand.
|
// wrapper methods on demand.
|
||||||
//
|
//
|
||||||
|
|
|
@ -104,7 +104,7 @@ func findNamedFunc(pkg *Package, pos token.Pos) *Function {
|
||||||
return mem
|
return mem
|
||||||
}
|
}
|
||||||
case *Type:
|
case *Type:
|
||||||
mset := methodSetOf(types.NewPointer(mem.Type()))
|
mset := types.NewPointer(mem.Type()).MethodSet()
|
||||||
for i, n := 0, mset.Len(); i < n; i++ {
|
for i, n := 0, mset.Len(); i < n; i++ {
|
||||||
// Don't call Program.Method: avoid creating wrappers.
|
// Don't call Program.Method: avoid creating wrappers.
|
||||||
obj := mset.At(i).Obj().(*types.Func)
|
obj := mset.At(i).Obj().(*types.Func)
|
||||||
|
@ -188,7 +188,7 @@ func (prog *Program) FuncValue(obj *types.Func) Value {
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
// Interface method wrapper?
|
// Interface method wrapper?
|
||||||
meth := methodSetOf(recvType(obj)).Lookup(obj.Pkg(), obj.Name())
|
meth := recvType(obj).MethodSet().Lookup(obj.Pkg(), obj.Name())
|
||||||
return prog.Method(meth)
|
return prog.Method(meth)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ func (visit *visitor) program() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (visit *visitor) methodSet(typ types.Type) {
|
func (visit *visitor) methodSet(typ types.Type) {
|
||||||
mset := methodSetOf(typ)
|
mset := typ.MethodSet()
|
||||||
for i, n := 0, mset.Len(); i < n; i++ {
|
for i, n := 0, mset.Len(); i < n; i++ {
|
||||||
// Side-effect: creates all wrapper methods.
|
// Side-effect: creates all wrapper methods.
|
||||||
visit.function(visit.prog.Method(mset.At(i)))
|
visit.function(visit.prog.Method(mset.At(i)))
|
||||||
|
|
Загрузка…
Ссылка в новой задаче