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:
Robert Griesemer 2013-08-14 11:02:10 -07:00
Родитель 0b3996b1d3
Коммит ae874abc7a
8 изменённых файлов: 81 добавлений и 34 удалений

Просмотреть файл

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

26
go/types/testdata/methodsets.src поставляемый
Просмотреть файл

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