go.tools/go/types: Method sets for all types
- fixed method set computation - lazily compute it for a type on demand - lazy allocation of various temporary maps Also: - removed Interface.IsEmpty, Scope.IsEmpty (these convenience functions didn't carry their weight) - cosmetic changes Next: - consolidate various lookup APIs - use lazily computed method sets for various checks instead of individual lookups R=adonovan CC=golang-dev https://golang.org/cl/11385044
This commit is contained in:
parent
2822addeae
commit
8901caa2b3
|
|
@ -236,7 +236,7 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) {
|
|||
}
|
||||
|
||||
// verify that m is in the method set of x.typ
|
||||
// (the receiver is nil if f is an interface method)
|
||||
// (the receiver is nil if m is an interface method)
|
||||
if recv := m.typ.(*Signature).recv; recv != nil {
|
||||
if _, isPtr := deref(recv.typ); isPtr && !indirect {
|
||||
check.invalidOp(e.Pos(), "%s is not in method set of %s", sel, x.typ)
|
||||
|
|
@ -264,14 +264,8 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) {
|
|||
case *Field:
|
||||
x.mode = variable
|
||||
x.typ = obj.typ
|
||||
case *Func:
|
||||
// TODO(gri) Temporary check to verify corresponding lookup via method sets.
|
||||
// Remove eventually.
|
||||
if m := NewMethodSet(x.typ).Lookup(check.pkg, sel); m != obj {
|
||||
check.dump("%s: %v", e.Pos(), obj.name)
|
||||
panic("method sets and lookup don't agree")
|
||||
}
|
||||
|
||||
case *Func:
|
||||
// TODO(gri) This code appears elsewhere, too. Factor!
|
||||
// verify that obj is in the method set of x.typ (or &(x.typ) if x is addressable)
|
||||
// (the receiver is nil if obj is an interface method)
|
||||
|
|
@ -287,8 +281,35 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) {
|
|||
}
|
||||
}
|
||||
|
||||
if debug {
|
||||
// Verify that LookupFieldOrMethod and MethodSet.Lookup agree.
|
||||
typ := x.typ
|
||||
if x.mode == variable {
|
||||
// If typ is not an (unnamed) pointer, use *typ instead,
|
||||
// because the the method set of *typ includes the methods
|
||||
// of typ.
|
||||
// Variables are addressable, so we can always take their
|
||||
// address.
|
||||
if _, isPtr := typ.(*Pointer); !isPtr {
|
||||
typ = &Pointer{base: typ}
|
||||
}
|
||||
}
|
||||
// If we created a synthetic pointer type above, we will throw
|
||||
// away the method set computed here after use.
|
||||
// TODO(gri) Consider also using a method set cache for the lifetime
|
||||
// of checker once we rely on MethodSet lookup instead of individual
|
||||
// lookup.
|
||||
mset := typ.MethodSet()
|
||||
if m := mset.Lookup(check.pkg, sel); m == nil || m.Func != obj {
|
||||
check.dump("%s: (%s).%v -> %s", e.Pos(), typ, obj.name, m)
|
||||
check.dump("%s\n", mset)
|
||||
panic("method sets and lookup don't agree")
|
||||
}
|
||||
}
|
||||
|
||||
x.mode = value
|
||||
x.typ = obj.typ
|
||||
|
||||
default:
|
||||
unreachable()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,10 +29,6 @@ func New(str string) Type {
|
|||
return typ
|
||||
}
|
||||
|
||||
// TODO(gri): Try to find a better name than Eval. Not everybody
|
||||
// agrees that it's the best name for the functionality provided.
|
||||
// Change EvalNode in correspondence.
|
||||
|
||||
// Eval returns the type and, if constant, the value for the
|
||||
// expression or type literal string str evaluated in scope.
|
||||
// If the expression contains function literals, the function
|
||||
|
|
|
|||
|
|
@ -430,7 +430,7 @@ func (check *checker) convertUntyped(x *operand, target Type) {
|
|||
return // error already reported
|
||||
}
|
||||
case *Interface:
|
||||
if !x.isNil() && !t.IsEmpty() /* empty interfaces are ok */ {
|
||||
if !x.isNil() && t.NumMethods() > 0 /* empty interfaces are ok */ {
|
||||
goto Error
|
||||
}
|
||||
// Update operand types to the default type rather then
|
||||
|
|
@ -443,7 +443,7 @@ func (check *checker) convertUntyped(x *operand, target Type) {
|
|||
target = Typ[UntypedNil]
|
||||
} else {
|
||||
// cannot assign untyped values to non-empty interfaces
|
||||
if !t.IsEmpty() {
|
||||
if t.NumMethods() > 0 {
|
||||
goto Error
|
||||
}
|
||||
target = defaultType(x.typ)
|
||||
|
|
|
|||
|
|
@ -72,8 +72,10 @@ func LookupFieldOrMethod(typ Type, pkg *Package, name string) (obj Object, index
|
|||
}
|
||||
|
||||
func lookupFieldOrMethod(typ Type, pkg *Package, name string) (obj Object, index []int, indirect bool) {
|
||||
// WARNING: The code in this function is extremely subtle - do not modify casually!
|
||||
|
||||
if name == "_" {
|
||||
return // empty fields/methods are never found
|
||||
return // blank fields/methods are never found
|
||||
}
|
||||
|
||||
// Start with typ as single entry at lowest depth.
|
||||
|
|
@ -82,14 +84,14 @@ func lookupFieldOrMethod(typ Type, pkg *Package, name string) (obj Object, index
|
|||
t, _ := typ.(*Named)
|
||||
current := []embeddedType{{t, nil, isPtr, false}}
|
||||
|
||||
// named types that we have seen already
|
||||
seen := make(map[*Named]bool)
|
||||
// named types that we have seen already, allocated lazily
|
||||
var seen map[*Named]bool
|
||||
|
||||
// search current depth
|
||||
for len(current) > 0 {
|
||||
var next []embeddedType // embedded types found at current depth
|
||||
|
||||
// look for (pkg, name) in all types at this depth
|
||||
// look for (pkg, name) in all types at current depth
|
||||
for _, e := range current {
|
||||
// The very first time only, e.typ may be nil.
|
||||
// In this case, we don't have a named type and
|
||||
|
|
@ -98,11 +100,14 @@ func lookupFieldOrMethod(typ Type, pkg *Package, name string) (obj Object, index
|
|||
if seen[e.typ] {
|
||||
// We have seen this type before, at a more shallow depth
|
||||
// (note that multiples of this type at the current depth
|
||||
// were eliminated before). The type at that depth shadows
|
||||
// were consolidated before). The type at that depth shadows
|
||||
// this same type at the current depth, so we can ignore
|
||||
// this one.
|
||||
continue
|
||||
}
|
||||
if seen == nil {
|
||||
seen = make(map[*Named]bool)
|
||||
}
|
||||
seen[e.typ] = true
|
||||
|
||||
// look for a matching attached method
|
||||
|
|
@ -224,7 +229,7 @@ func MissingMethod(typ Type, T *Interface) (method *Func, wrongType bool) {
|
|||
// Note: This is stronger than the current spec. Should the spec require this?
|
||||
|
||||
// fast path for common case
|
||||
if T.IsEmpty() {
|
||||
if T.NumMethods() == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,21 +10,52 @@ import (
|
|||
"bytes"
|
||||
"fmt"
|
||||
"sort"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// A MethodSet is an ordered set of methods.
|
||||
// TODO(gri) Move Method and accessors to objects.go.
|
||||
|
||||
// A Method represents a concrete or abstract (interface)
|
||||
// method of a method set.
|
||||
type Method struct {
|
||||
*Func
|
||||
recv Type
|
||||
index []int
|
||||
indirect bool
|
||||
}
|
||||
|
||||
// Recv returns the receiver type for m, which is the type
|
||||
// for which the method set containing m was computed. For
|
||||
// interface methods, the receiver type is the type of the
|
||||
// interface.
|
||||
func (m *Method) Recv() Type { return m.recv }
|
||||
|
||||
// Index describes the path to the concrete (possibly embedded)
|
||||
// function implementing this method. See LookupFieldOrMethod
|
||||
// for details.
|
||||
func (m *Method) Index() []int { return m.index }
|
||||
|
||||
// Indirect reports whether any pointer indirections was
|
||||
// required to get from a value of m's receiver type to
|
||||
// the receiver type of the concrete function implementing m.
|
||||
// For interface methods, Indirect is undefined.
|
||||
func (m *Method) Indirect() bool { return m.indirect }
|
||||
|
||||
// A MethodSet is an ordered set of concrete or abstract (interface) methods.
|
||||
// The zero value for a MethodSet is a ready-to-use empty method set.
|
||||
type MethodSet struct {
|
||||
list []*Func
|
||||
list []*Method
|
||||
}
|
||||
|
||||
func (s *MethodSet) String() string {
|
||||
var buf bytes.Buffer
|
||||
fmt.Fprint(&buf, "MethodSet{")
|
||||
if len(s.list) > 0 {
|
||||
fmt.Fprintln(&buf)
|
||||
if s.Len() == 0 {
|
||||
return "MethodSet {}"
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
fmt.Fprintln(&buf, "MethodSet {")
|
||||
for _, m := range s.list {
|
||||
fmt.Fprintf(&buf, "\t%s\n", m.uniqueName())
|
||||
fmt.Fprintf(&buf, "\t%s -> %s\n", m.uniqueName(), m.Func)
|
||||
}
|
||||
fmt.Fprintln(&buf, "}")
|
||||
return buf.String()
|
||||
|
|
@ -33,18 +64,20 @@ func (s *MethodSet) String() string {
|
|||
// Len returns the number of methods in s.
|
||||
func (s *MethodSet) Len() int { return len(s.list) }
|
||||
|
||||
// At returns the i'th method in s.
|
||||
func (s *MethodSet) At(i int) *Func { return s.list[i] }
|
||||
// At returns the i'th method in s for 0 <= i < s.Len().
|
||||
func (s *MethodSet) At(i int) *Method { return s.list[i] }
|
||||
|
||||
// Lookup returns the method with matching package and name, or nil if not found.
|
||||
func (s *MethodSet) Lookup(pkg *Package, name string) *Func {
|
||||
key := (&object{pkg: pkg, name: name}).uniqueName()
|
||||
func (s *MethodSet) Lookup(pkg *Package, name string) *Method {
|
||||
if s.Len() == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
key := (&object{pkg: pkg, name: name}).uniqueName()
|
||||
i := sort.Search(len(s.list), func(i int) bool {
|
||||
m := s.list[i]
|
||||
return m.uniqueName() >= key
|
||||
})
|
||||
|
||||
if i < len(s.list) {
|
||||
m := s.list[i]
|
||||
if m.uniqueName() == key {
|
||||
|
|
@ -54,12 +87,39 @@ func (s *MethodSet) Lookup(pkg *Package, name string) *Func {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Shared empty method set.
|
||||
var emptyMethodSet MethodSet
|
||||
|
||||
// A cachedMethodSet provides access to a method set
|
||||
// for a given type by computing it once on demand,
|
||||
// and then caching it for future use. Threadsafe.
|
||||
type cachedMethodSet struct {
|
||||
mset *MethodSet
|
||||
mu sync.RWMutex // protects mset
|
||||
}
|
||||
|
||||
// Of returns the (possibly cached) method set for typ.
|
||||
// Threadsafe.
|
||||
func (c *cachedMethodSet) of(typ Type) *MethodSet {
|
||||
c.mu.RLock()
|
||||
mset := c.mset
|
||||
c.mu.RUnlock()
|
||||
if mset == nil {
|
||||
mset = NewMethodSet(typ)
|
||||
c.mu.Lock()
|
||||
c.mset = mset
|
||||
c.mu.Unlock()
|
||||
}
|
||||
return mset
|
||||
}
|
||||
|
||||
// NewMethodSet computes the method set for the given type.
|
||||
// BUG(gri): The pointer-ness of the receiver type is still ignored.
|
||||
// It always returns a non-nil method set, even if it is empty.
|
||||
func NewMethodSet(typ Type) *MethodSet {
|
||||
// method set up to the current depth
|
||||
// TODO(gri) allocate lazily, method sets are often empty
|
||||
base := make(methodSet)
|
||||
// WARNING: The code in this function is extremely subtle - do not modify casually!
|
||||
|
||||
// method set up to the current depth, allocated lazily
|
||||
var base methodSet
|
||||
|
||||
// Start with typ as single entry at lowest depth.
|
||||
// If typ is not a named type, insert a nil type instead.
|
||||
|
|
@ -67,16 +127,16 @@ func NewMethodSet(typ Type) *MethodSet {
|
|||
t, _ := typ.(*Named)
|
||||
current := []embeddedType{{t, nil, isPtr, false}}
|
||||
|
||||
// named types that we have seen already
|
||||
seen := make(map[*Named]bool)
|
||||
// named types that we have seen already, allocated lazily
|
||||
var seen map[*Named]bool
|
||||
|
||||
// collect methods at current depth
|
||||
for len(current) > 0 {
|
||||
var next []embeddedType // embedded types found at current depth
|
||||
|
||||
// field and method sets for current depth
|
||||
fset := make(fieldSet)
|
||||
mset := make(methodSet)
|
||||
// field and method sets at current depth, allocated lazily
|
||||
var fset fieldSet
|
||||
var mset methodSet
|
||||
|
||||
for _, e := range current {
|
||||
// The very first time only, e.typ may be nil.
|
||||
|
|
@ -86,14 +146,17 @@ func NewMethodSet(typ Type) *MethodSet {
|
|||
if seen[e.typ] {
|
||||
// We have seen this type before, at a more shallow depth
|
||||
// (note that multiples of this type at the current depth
|
||||
// were eliminated before). The type at that depth shadows
|
||||
// were consolidated before). The type at that depth shadows
|
||||
// this same type at the current depth, so we can ignore
|
||||
// this one.
|
||||
continue
|
||||
}
|
||||
if seen == nil {
|
||||
seen = make(map[*Named]bool)
|
||||
}
|
||||
seen[e.typ] = true
|
||||
|
||||
mset.add(e.typ.methods, e.multiples)
|
||||
mset = mset.add(e.typ.methods, e.index, e.indirect, e.multiples)
|
||||
|
||||
// continue with underlying type
|
||||
typ = e.typ.underlying
|
||||
|
|
@ -101,8 +164,8 @@ func NewMethodSet(typ Type) *MethodSet {
|
|||
|
||||
switch t := typ.(type) {
|
||||
case *Struct:
|
||||
for _, f := range t.fields {
|
||||
fset.add(f, e.multiples)
|
||||
for i, f := range t.fields {
|
||||
fset = fset.add(f, e.multiples)
|
||||
|
||||
// Embedded fields are always of the form T or *T where
|
||||
// T is a named type. If typ appeared multiple times at
|
||||
|
|
@ -110,16 +173,16 @@ func NewMethodSet(typ Type) *MethodSet {
|
|||
// depth.
|
||||
if f.anonymous {
|
||||
// Ignore embedded basic types - only user-defined
|
||||
// named types can have methods or have struct fields.
|
||||
// named types can have methods or struct fields.
|
||||
typ, isPtr := deref(f.typ)
|
||||
if t, _ := typ.(*Named); t != nil {
|
||||
next = append(next, embeddedType{t, nil, e.indirect || isPtr, e.multiples})
|
||||
next = append(next, embeddedType{t, concat(e.index, i), e.indirect || isPtr, e.multiples})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case *Interface:
|
||||
mset.add(t.methods, e.multiples)
|
||||
mset = mset.add(t.methods, e.index, true, e.multiples)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -131,6 +194,9 @@ func NewMethodSet(typ Type) *MethodSet {
|
|||
if _, found := fset[k]; found {
|
||||
m = nil // collision
|
||||
}
|
||||
if base == nil {
|
||||
base = make(methodSet)
|
||||
}
|
||||
base[k] = m
|
||||
}
|
||||
}
|
||||
|
|
@ -141,7 +207,10 @@ func NewMethodSet(typ Type) *MethodSet {
|
|||
for k, f := range fset {
|
||||
if f == nil {
|
||||
if _, found := base[k]; !found {
|
||||
base[k] = nil
|
||||
if base == nil {
|
||||
base = make(methodSet)
|
||||
}
|
||||
base[k] = nil // collision
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -149,60 +218,84 @@ func NewMethodSet(typ Type) *MethodSet {
|
|||
current = consolidateMultiples(next)
|
||||
}
|
||||
|
||||
if len(base) == 0 {
|
||||
return &emptyMethodSet
|
||||
}
|
||||
|
||||
// collect methods
|
||||
var list []*Func
|
||||
var list []*Method
|
||||
for _, m := range base {
|
||||
if m != nil {
|
||||
m.recv = typ
|
||||
list = append(list, m)
|
||||
}
|
||||
}
|
||||
sort.Sort(byUniqueName(list))
|
||||
|
||||
return &MethodSet{list}
|
||||
}
|
||||
|
||||
// A fieldSet is a set of fields and name collisions.
|
||||
// A conflict indicates that multiple fields with the same package and name appeared.
|
||||
// A collision indicates that multiple fields with the
|
||||
// same unique name appeared.
|
||||
type fieldSet map[string]*Field // a nil entry indicates a name collision
|
||||
|
||||
// Add adds field f to the field set s.
|
||||
// If multiples is set, f appears multiple times
|
||||
// and is treated as a collision at this level.
|
||||
func (s fieldSet) add(f *Field, multiples bool) {
|
||||
// and is treated as a collision.
|
||||
func (s fieldSet) add(f *Field, multiples bool) fieldSet {
|
||||
if s == nil {
|
||||
s = make(fieldSet)
|
||||
}
|
||||
key := f.uniqueName()
|
||||
// if f is not in the set, add it
|
||||
if !multiples {
|
||||
if _, found := s[key]; !found {
|
||||
s[key] = f
|
||||
return
|
||||
return s
|
||||
}
|
||||
}
|
||||
s[key] = nil // collision
|
||||
return s
|
||||
}
|
||||
|
||||
// A methodSet is a set of methods and name collisions.
|
||||
// A conflict indicates that multiple methods with the same package and name appeared.
|
||||
type methodSet map[string]*Func // a nil entry indicates a name collision
|
||||
// A collision indicates that multiple methods with the
|
||||
// same unique name appeared.
|
||||
type methodSet map[string]*Method // a nil entry indicates a name collision
|
||||
|
||||
// Add adds all methods in list to the method set s.
|
||||
// If multiples is set, every method in list appears multiple times
|
||||
// and is treated as a collision at this level.
|
||||
func (s methodSet) add(list []*Func, multiples bool) {
|
||||
for _, m := range list {
|
||||
key := m.uniqueName()
|
||||
// if m is not in the set, add it
|
||||
// Add adds all functions in list to the method set s.
|
||||
// If multiples is set, every function in list appears multiple times
|
||||
// and is treated as a collision.
|
||||
func (s methodSet) add(list []*Func, index []int, indirect bool, multiples bool) methodSet {
|
||||
if len(list) == 0 {
|
||||
return s
|
||||
}
|
||||
if s == nil {
|
||||
s = make(methodSet)
|
||||
}
|
||||
for i, f := range list {
|
||||
key := f.uniqueName()
|
||||
// if f is not in the set, add it
|
||||
if !multiples {
|
||||
if _, found := s[key]; !found {
|
||||
s[key] = m
|
||||
if _, found := s[key]; !found && (indirect || !ptrRecv(f)) {
|
||||
s[key] = &Method{Func: f, index: concat(index, i), indirect: indirect}
|
||||
continue
|
||||
}
|
||||
}
|
||||
s[key] = nil // collision
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// ptrRecv reports whether the receiver is of the form *T.
|
||||
// The receiver must exist.
|
||||
func ptrRecv(f *Func) bool {
|
||||
_, isPtr := deref(f.typ.(*Signature).recv.typ)
|
||||
return isPtr
|
||||
}
|
||||
|
||||
// byUniqueName function lists can be sorted by their unique names.
|
||||
type byUniqueName []*Func
|
||||
type byUniqueName []*Method
|
||||
|
||||
func (a byUniqueName) Len() int { return len(a) }
|
||||
func (a byUniqueName) Less(i, j int) bool { return a[i].uniqueName() < a[j].uniqueName() }
|
||||
|
|
|
|||
|
|
@ -194,7 +194,7 @@ func (x *operand) isAssignableTo(ctxt *Context, T Type) bool {
|
|||
return Vb.kind == UntypedBool && isBoolean(Tu)
|
||||
}
|
||||
case *Interface:
|
||||
return x.isNil() || t.IsEmpty()
|
||||
return x.isNil() || t.NumMethods() == 0
|
||||
case *Pointer, *Signature, *Slice, *Map, *Chan:
|
||||
return x.isNil()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -510,7 +510,7 @@ func (check *checker) declareType(obj *TypeName, typ ast.Expr, def *Named, cycle
|
|||
}
|
||||
// typecheck method signatures
|
||||
var methods []*Func
|
||||
if !scope.IsEmpty() {
|
||||
if scope.NumEntries() > 0 {
|
||||
for _, obj := range scope.entries {
|
||||
m := obj.(*Func)
|
||||
|
||||
|
|
|
|||
|
|
@ -39,9 +39,7 @@ func NewScope(parent *Scope) *Scope {
|
|||
}
|
||||
|
||||
// Parent returns the scope's containing (parent) scope.
|
||||
func (s *Scope) Parent() *Scope {
|
||||
return s.parent
|
||||
}
|
||||
func (s *Scope) Parent() *Scope { return s.parent }
|
||||
|
||||
// Node returns the ast.Node responsible for this scope,
|
||||
// which may be one of the following:
|
||||
|
|
@ -59,9 +57,7 @@ func (s *Scope) Parent() *Scope {
|
|||
//
|
||||
// The result is nil if there is no corresponding node
|
||||
// (universe and package scopes).
|
||||
func (s *Scope) Node() ast.Node {
|
||||
return s.node
|
||||
}
|
||||
func (s *Scope) Node() ast.Node { return s.node }
|
||||
|
||||
// NumEntries() returns the number of scope entries.
|
||||
// If s == nil, the result is 0.
|
||||
|
|
@ -72,16 +68,8 @@ func (s *Scope) NumEntries() int {
|
|||
return len(s.entries)
|
||||
}
|
||||
|
||||
// IsEmpty reports whether the scope is empty.
|
||||
// If s == nil, the result is true.
|
||||
func (s *Scope) IsEmpty() bool {
|
||||
return s == nil || len(s.entries) == 0
|
||||
}
|
||||
|
||||
// At returns the i'th scope entry for 0 <= i < NumEntries().
|
||||
func (s *Scope) At(i int) Object {
|
||||
return s.entries[i]
|
||||
}
|
||||
func (s *Scope) At(i int) Object { return s.entries[i] }
|
||||
|
||||
// NumChildren() returns the number of scopes nested in s.
|
||||
// If s == nil, the result is 0.
|
||||
|
|
@ -93,9 +81,7 @@ func (s *Scope) NumChildren() int {
|
|||
}
|
||||
|
||||
// Child returns the i'th child scope for 0 <= i < NumChildren().
|
||||
func (s *Scope) Child(i int) *Scope {
|
||||
return s.children[i]
|
||||
}
|
||||
func (s *Scope) Child(i int) *Scope { return s.children[i] }
|
||||
|
||||
// Lookup returns the object in scope s with the given package
|
||||
// and name if such an object exists; otherwise the result is nil.
|
||||
|
|
|
|||
|
|
@ -14,6 +14,9 @@ type Type interface {
|
|||
// Underlying returns the underlying type of a type.
|
||||
Underlying() Type
|
||||
|
||||
// MethodSet returns the method set of a type.
|
||||
MethodSet() *MethodSet
|
||||
|
||||
// String returns a string representation of a type.
|
||||
String() string
|
||||
}
|
||||
|
|
@ -121,9 +124,11 @@ func (s *Slice) Elem() Type { return s.elt }
|
|||
|
||||
// A Struct represents a struct type.
|
||||
type Struct struct {
|
||||
fields []*Field
|
||||
tags []string // field tags; nil if there are no tags
|
||||
offsets []int64 // field offsets in bytes, lazily computed
|
||||
fields []*Field
|
||||
tags []string // field tags; nil if there are no tags
|
||||
// TODO(gri) access to offsets is not threadsafe - fix this
|
||||
offsets []int64 // field offsets in bytes, lazily initialized
|
||||
mset cachedMethodSet // method set, lazily initialized
|
||||
}
|
||||
|
||||
// NewStruct returns a new struct with the given fields and corresponding field tags.
|
||||
|
|
@ -150,11 +155,12 @@ func (s *Struct) Tag(i int) string {
|
|||
|
||||
// A Pointer represents a pointer type.
|
||||
type Pointer struct {
|
||||
base Type
|
||||
base Type // element type
|
||||
mset cachedMethodSet // method set, lazily initialized
|
||||
}
|
||||
|
||||
// NewPointer returns a new pointer type for the given element (base) type.
|
||||
func NewPointer(elem Type) *Pointer { return &Pointer{elem} }
|
||||
func NewPointer(elem Type) *Pointer { return &Pointer{base: elem} }
|
||||
|
||||
// Elem returns the element type for the given pointer p.
|
||||
func (p *Pointer) Elem() Type { return p.base }
|
||||
|
|
@ -266,30 +272,24 @@ type Builtin struct {
|
|||
}
|
||||
|
||||
// Name returns the name of the built-in function b.
|
||||
func (b *Builtin) Name() string {
|
||||
return b.name
|
||||
}
|
||||
func (b *Builtin) Name() string { return b.name }
|
||||
|
||||
// An Interface represents an interface type.
|
||||
type Interface struct {
|
||||
methods []*Func
|
||||
methods []*Func // methods declared with or embedded in this interface
|
||||
mset cachedMethodSet // method set for interface, lazily initialized
|
||||
}
|
||||
|
||||
// NewInterface returns a new interface for the given methods.
|
||||
func NewInterface(methods []*Func) *Interface {
|
||||
return &Interface{methods}
|
||||
return &Interface{methods: methods}
|
||||
}
|
||||
|
||||
// NumMethods returns the number of methods of interface t.
|
||||
func (t *Interface) NumMethods() int { return len(t.methods) }
|
||||
|
||||
// Method returns the i'th method of interface t for 0 <= i < t.NumMethods().
|
||||
func (t *Interface) Method(i int) *Func {
|
||||
return t.methods[i]
|
||||
}
|
||||
|
||||
// IsEmpty() reports whether t is an empty interface.
|
||||
func (t *Interface) IsEmpty() bool { return len(t.methods) == 0 }
|
||||
func (t *Interface) Method(i int) *Func { return t.methods[i] }
|
||||
|
||||
// A Map represents a map type.
|
||||
type Map struct {
|
||||
|
|
@ -326,10 +326,11 @@ func (c *Chan) Elem() Type { return c.elt }
|
|||
|
||||
// A Named represents a named type.
|
||||
type Named struct {
|
||||
obj *TypeName // corresponding declared object
|
||||
underlying Type // possibly a *Named if !complete; never a *Named if complete
|
||||
complete bool // if set, the underlying type has been determined
|
||||
methods []*Func // methods declared for this type (not the method set of this type)
|
||||
obj *TypeName // corresponding declared object
|
||||
underlying Type // possibly a *Named if !complete; never a *Named if complete
|
||||
complete bool // if set, the underlying type has been determined
|
||||
methods []*Func // methods declared for this type (not the method set of this type)
|
||||
mset cachedMethodSet // method set for this type, lazily initialized
|
||||
}
|
||||
|
||||
// NewNamed returns a new named type for the given type name, underlying type, and associated methods.
|
||||
|
|
@ -339,7 +340,7 @@ func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named {
|
|||
if _, ok := underlying.(*Named); ok {
|
||||
panic("types.NewNamed: underlying type must not be *Named")
|
||||
}
|
||||
typ := &Named{obj, underlying, true, methods}
|
||||
typ := &Named{obj: obj, underlying: underlying, complete: true, methods: methods}
|
||||
if obj.typ == nil {
|
||||
obj.typ = typ
|
||||
}
|
||||
|
|
@ -353,9 +354,7 @@ func (t *Named) Obj() *TypeName { return t.obj }
|
|||
func (t *Named) NumMethods() int { return len(t.methods) }
|
||||
|
||||
// Method returns the i'th method of named type t for 0 <= i < t.NumMethods().
|
||||
func (t *Named) Method(i int) *Func {
|
||||
return t.methods[i]
|
||||
}
|
||||
func (t *Named) Method(i int) *Func { return t.methods[i] }
|
||||
|
||||
// Implementations for Type methods.
|
||||
|
||||
|
|
@ -372,6 +371,19 @@ func (t *Map) Underlying() Type { return t }
|
|||
func (t *Chan) Underlying() Type { return t }
|
||||
func (t *Named) Underlying() Type { return t.underlying }
|
||||
|
||||
func (t *Basic) MethodSet() *MethodSet { return &emptyMethodSet }
|
||||
func (t *Array) MethodSet() *MethodSet { return &emptyMethodSet }
|
||||
func (t *Slice) MethodSet() *MethodSet { return &emptyMethodSet }
|
||||
func (t *Struct) MethodSet() *MethodSet { return t.mset.of(t) }
|
||||
func (t *Pointer) MethodSet() *MethodSet { return t.mset.of(t) }
|
||||
func (t *Tuple) MethodSet() *MethodSet { return &emptyMethodSet }
|
||||
func (t *Signature) MethodSet() *MethodSet { return &emptyMethodSet }
|
||||
func (t *Builtin) MethodSet() *MethodSet { return &emptyMethodSet }
|
||||
func (t *Interface) MethodSet() *MethodSet { return t.mset.of(t) }
|
||||
func (t *Map) MethodSet() *MethodSet { return &emptyMethodSet }
|
||||
func (t *Chan) MethodSet() *MethodSet { return &emptyMethodSet }
|
||||
func (t *Named) MethodSet() *MethodSet { return t.mset.of(t) }
|
||||
|
||||
func (t *Basic) String() string { return typeString(t) }
|
||||
func (t *Array) String() string { return typeString(t) }
|
||||
func (t *Slice) String() string { return typeString(t) }
|
||||
|
|
|
|||
|
|
@ -143,6 +143,10 @@ func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) {
|
|||
pkg.Prog.concreteMethods[method] = fn
|
||||
}
|
||||
|
||||
case *types.Method:
|
||||
// TODO(adonovan): do something more sensible here?
|
||||
memberFromObject(pkg, obj.Func, syntax)
|
||||
|
||||
default: // (incl. *types.Package)
|
||||
panic(fmt.Sprintf("unexpected Object type: %T", obj))
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue