1 // Copyright 2013 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // This file implements method sets.
15 // A MethodSet is an ordered set of concrete or abstract (interface) methods;
16 // a method is a MethodVal selection, and they are ordered by ascending m.Obj().Id().
17 // The zero value for a MethodSet is a ready-to-use empty method set.
18 type MethodSet
struct {
22 func (s
*MethodSet
) String() string {
27 var buf strings
.Builder
28 fmt
.Fprintln(&buf
, "MethodSet {")
29 for _
, f
:= range s
.list
{
30 fmt
.Fprintf(&buf
, "\t%s\n", f
)
32 fmt
.Fprintln(&buf
, "}")
36 // Len returns the number of methods in s.
37 func (s
*MethodSet
) Len() int { return len(s
.list
) }
39 // At returns the i'th method in s for 0 <= i < s.Len().
40 func (s
*MethodSet
) At(i
int) *Selection
{ return s
.list
[i
] }
42 // Lookup returns the method with matching package and name, or nil if not found.
43 func (s
*MethodSet
) Lookup(pkg
*Package
, name
string) *Selection
{
49 i
:= sort
.Search(len(s
.list
), func(i
int) bool {
51 return m
.obj
.Id() >= key
55 if m
.obj
.Id() == key
{
62 // Shared empty method set.
63 var emptyMethodSet MethodSet
65 // NewMethodSet returns the method set for the given type T.
66 // It always returns a non-nil method set, even if it is empty.
67 func NewMethodSet(T Type
) *MethodSet
{
68 // WARNING: The code in this function is extremely subtle - do not modify casually!
69 // This function and lookupFieldOrMethod should be kept in sync.
71 // method set up to the current depth, allocated lazily
74 typ
, isPtr
:= deref(T
)
76 // *typ where typ is an interface has no methods.
77 if isPtr
&& IsInterface(typ
) {
78 return &emptyMethodSet
81 // Start with typ as single entry at shallowest depth.
82 current
:= []embeddedType
{{typ
, nil, isPtr
, false}}
84 // Named types that we have seen already, allocated lazily.
85 // Used to avoid endless searches in case of recursive types.
86 // Since only Named types can be used for recursive types, we
87 // only need to track those.
88 // (If we ever allow type aliases to construct recursive types,
89 // we must use type identity rather than pointer equality for
90 // the map key comparison, as we do in consolidateMultiples.)
91 var seen
map[*Named
]bool
93 // collect methods at current depth
94 for len(current
) > 0 {
95 var next
[]embeddedType
// embedded types found at current depth
97 // field and method sets at current depth, allocated lazily
101 for _
, e
:= range current
{
104 // If we have a named type, we may have associated methods.
105 // Look for those first.
106 if named
, _
:= typ
.(*Named
); named
!= nil {
108 // We have seen this type before, at a more shallow depth
109 // (note that multiples of this type at the current depth
110 // were consolidated before). The type at that depth shadows
111 // this same type at the current depth, so we can ignore
116 seen
= make(map[*Named
]bool)
120 mset
= mset
.add(named
.methods
, e
.index
, e
.indirect
, e
.multiples
)
122 // continue with underlying type
123 typ
= named
.underlying
126 switch t
:= typ
.(type) {
128 for i
, f
:= range t
.fields
{
129 fset
= fset
.add(f
, e
.multiples
)
131 // Embedded fields are always of the form T or *T where
132 // T is a type name. If typ appeared multiple times at
133 // this depth, f.Type appears multiple times at the next
136 typ
, isPtr
:= deref(f
.typ
)
137 // TODO(gri) optimization: ignore types that can't
138 // have fields or methods (only Named, Struct, and
139 // Interface types need to be considered).
140 next
= append(next
, embeddedType
{typ
, concat(e
.index
, i
), e
.indirect || isPtr
, e
.multiples
})
145 mset
= mset
.add(t
.allMethods
, e
.index
, true, e
.multiples
)
149 // Add methods and collisions at this depth to base if no entries with matching
150 // names exist already.
151 for k
, m
:= range mset
{
152 if _
, found
:= base
[k
]; !found
{
153 // Fields collide with methods of the same name at this depth.
154 if _
, found
:= fset
[k
]; found
{
158 base
= make(methodSet
)
164 // Multiple fields with matching names collide at this depth and shadow all
165 // entries further down; add them as collisions to base if no entries with
166 // matching names exist already.
167 for k
, f
:= range fset
{
169 if _
, found
:= base
[k
]; !found
{
171 base
= make(methodSet
)
173 base
[k
] = nil // collision
178 current
= consolidateMultiples(next
)
182 return &emptyMethodSet
186 var list
[]*Selection
187 for _
, m
:= range base
{
190 list
= append(list
, m
)
193 // sort by unique name
194 sort
.Slice(list
, func(i
, j
int) bool {
195 return list
[i
].obj
.Id() < list
[j
].obj
.Id()
197 return &MethodSet
{list
}
200 // A fieldSet is a set of fields and name collisions.
201 // A collision indicates that multiple fields with the
202 // same unique id appeared.
203 type fieldSet
map[string]*Var
// a nil entry indicates a name collision
205 // Add adds field f to the field set s.
206 // If multiples is set, f appears multiple times
207 // and is treated as a collision.
208 func (s fieldSet
) add(f
*Var
, multiples
bool) fieldSet
{
213 // if f is not in the set, add it
215 if _
, found
:= s
[key
]; !found
{
220 s
[key
] = nil // collision
224 // A methodSet is a set of methods and name collisions.
225 // A collision indicates that multiple methods with the
226 // same unique id appeared.
227 type methodSet
map[string]*Selection
// a nil entry indicates a name collision
229 // Add adds all functions in list to the method set s.
230 // If multiples is set, every function in list appears multiple times
231 // and is treated as a collision.
232 func (s methodSet
) add(list
[]*Func
, index
[]int, indirect
bool, multiples
bool) methodSet
{
239 for i
, f
:= range list
{
241 // if f is not in the set, add it
243 // TODO(gri) A found method may not be added because it's not in the method set
244 // (!indirect && ptrRecv(f)). A 2nd method on the same level may be in the method
245 // set and may not collide with the first one, thus leading to a false positive.
246 // Is that possible? Investigate.
247 if _
, found
:= s
[key
]; !found
&& (indirect ||
!ptrRecv(f
)) {
248 s
[key
] = &Selection
{MethodVal
, nil, f
, concat(index
, i
), indirect
}
252 s
[key
] = nil // collision
257 // ptrRecv reports whether the receiver is of the form *T.
258 // The receiver must exist.
259 func ptrRecv(f
*Func
) bool {
260 _
, isPtr
:= deref(f
.typ
.(*Signature
).recv
.typ
)