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. 4 5// This file implements method sets. 6 7package types 8 9import ( 10 "fmt" 11 "sort" 12 "strings" 13) 14 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. 18type MethodSet struct { 19 list []*Selection 20} 21 22func (s *MethodSet) String() string { 23 if s.Len() == 0 { 24 return "MethodSet {}" 25 } 26 27 var buf strings.Builder 28 fmt.Fprintln(&buf, "MethodSet {") 29 for _, f := range s.list { 30 fmt.Fprintf(&buf, "\t%s\n", f) 31 } 32 fmt.Fprintln(&buf, "}") 33 return buf.String() 34} 35 36// Len returns the number of methods in s. 37func (s *MethodSet) Len() int { return len(s.list) } 38 39// At returns the i'th method in s for 0 <= i < s.Len(). 40func (s *MethodSet) At(i int) *Selection { return s.list[i] } 41 42// Lookup returns the method with matching package and name, or nil if not found. 43func (s *MethodSet) Lookup(pkg *Package, name string) *Selection { 44 if s.Len() == 0 { 45 return nil 46 } 47 48 key := Id(pkg, name) 49 i := sort.Search(len(s.list), func(i int) bool { 50 m := s.list[i] 51 return m.obj.Id() >= key 52 }) 53 if i < len(s.list) { 54 m := s.list[i] 55 if m.obj.Id() == key { 56 return m 57 } 58 } 59 return nil 60} 61 62// Shared empty method set. 63var emptyMethodSet MethodSet 64 65// Note: NewMethodSet is intended for external use only as it 66// requires interfaces to be complete. If may be used 67// internally if LookupFieldOrMethod completed the same 68// interfaces beforehand. 69 70// NewMethodSet returns the method set for the given type T. 71// It always returns a non-nil method set, even if it is empty. 72func NewMethodSet(T Type) *MethodSet { 73 // WARNING: The code in this function is extremely subtle - do not modify casually! 74 // This function and lookupFieldOrMethod should be kept in sync. 75 76 // method set up to the current depth, allocated lazily 77 var base methodSet 78 79 typ, isPtr := deref(T) 80 81 // *typ where typ is an interface has no methods. 82 if isPtr && IsInterface(typ) { 83 return &emptyMethodSet 84 } 85 86 // Start with typ as single entry at shallowest depth. 87 current := []embeddedType{{typ, nil, isPtr, false}} 88 89 // Named types that we have seen already, allocated lazily. 90 // Used to avoid endless searches in case of recursive types. 91 // Since only Named types can be used for recursive types, we 92 // only need to track those. 93 // (If we ever allow type aliases to construct recursive types, 94 // we must use type identity rather than pointer equality for 95 // the map key comparison, as we do in consolidateMultiples.) 96 var seen map[*Named]bool 97 98 // collect methods at current depth 99 for len(current) > 0 { 100 var next []embeddedType // embedded types found at current depth 101 102 // field and method sets at current depth, indexed by names (Id's), and allocated lazily 103 var fset map[string]bool // we only care about the field names 104 var mset methodSet 105 106 for _, e := range current { 107 typ := e.typ 108 109 // If we have a named type, we may have associated methods. 110 // Look for those first. 111 if named, _ := typ.(*Named); named != nil { 112 if seen[named] { 113 // We have seen this type before, at a more shallow depth 114 // (note that multiples of this type at the current depth 115 // were consolidated before). The type at that depth shadows 116 // this same type at the current depth, so we can ignore 117 // this one. 118 continue 119 } 120 if seen == nil { 121 seen = make(map[*Named]bool) 122 } 123 seen[named] = true 124 125 mset = mset.add(named.methods, e.index, e.indirect, e.multiples) 126 127 // continue with underlying type 128 typ = named.underlying 129 } 130 131 switch t := typ.(type) { 132 case *Struct: 133 for i, f := range t.fields { 134 if fset == nil { 135 fset = make(map[string]bool) 136 } 137 fset[f.Id()] = true 138 139 // Embedded fields are always of the form T or *T where 140 // T is a type name. If typ appeared multiple times at 141 // this depth, f.Type appears multiple times at the next 142 // depth. 143 if f.embedded { 144 typ, isPtr := deref(f.typ) 145 // TODO(gri) optimization: ignore types that can't 146 // have fields or methods (only Named, Struct, and 147 // Interface types need to be considered). 148 next = append(next, embeddedType{typ, concat(e.index, i), e.indirect || isPtr, e.multiples}) 149 } 150 } 151 152 case *Interface: 153 mset = mset.add(t.allMethods, e.index, true, e.multiples) 154 } 155 } 156 157 // Add methods and collisions at this depth to base if no entries with matching 158 // names exist already. 159 for k, m := range mset { 160 if _, found := base[k]; !found { 161 // Fields collide with methods of the same name at this depth. 162 if fset[k] { 163 m = nil // collision 164 } 165 if base == nil { 166 base = make(methodSet) 167 } 168 base[k] = m 169 } 170 } 171 172 // Add all (remaining) fields at this depth as collisions (since they will 173 // hide any method further down) if no entries with matching names exist already. 174 for k := range fset { 175 if _, found := base[k]; !found { 176 if base == nil { 177 base = make(methodSet) 178 } 179 base[k] = nil // collision 180 } 181 } 182 183 // It's ok to call consolidateMultiples with a nil *Checker because 184 // MethodSets are not used internally (outside debug mode). When used 185 // externally, interfaces are expected to be completed and then we do 186 // not need a *Checker to complete them when (indirectly) calling 187 // Checker.identical via consolidateMultiples. 188 current = (*Checker)(nil).consolidateMultiples(next) 189 } 190 191 if len(base) == 0 { 192 return &emptyMethodSet 193 } 194 195 // collect methods 196 var list []*Selection 197 for _, m := range base { 198 if m != nil { 199 m.recv = T 200 list = append(list, m) 201 } 202 } 203 // sort by unique name 204 sort.Slice(list, func(i, j int) bool { 205 return list[i].obj.Id() < list[j].obj.Id() 206 }) 207 return &MethodSet{list} 208} 209 210// A methodSet is a set of methods and name collisions. 211// A collision indicates that multiple methods with the 212// same unique id, or a field with that id appeared. 213type methodSet map[string]*Selection // a nil entry indicates a name collision 214 215// Add adds all functions in list to the method set s. 216// If multiples is set, every function in list appears multiple times 217// and is treated as a collision. 218func (s methodSet) add(list []*Func, index []int, indirect bool, multiples bool) methodSet { 219 if len(list) == 0 { 220 return s 221 } 222 if s == nil { 223 s = make(methodSet) 224 } 225 for i, f := range list { 226 key := f.Id() 227 // if f is not in the set, add it 228 if !multiples { 229 // TODO(gri) A found method may not be added because it's not in the method set 230 // (!indirect && ptrRecv(f)). A 2nd method on the same level may be in the method 231 // set and may not collide with the first one, thus leading to a false positive. 232 // Is that possible? Investigate. 233 if _, found := s[key]; !found && (indirect || !ptrRecv(f)) { 234 s[key] = &Selection{MethodVal, nil, f, concat(index, i), indirect} 235 continue 236 } 237 } 238 s[key] = nil // collision 239 } 240 return s 241} 242 243// ptrRecv reports whether the receiver is of the form *T. 244func ptrRecv(f *Func) bool { 245 // If a method's receiver type is set, use that as the source of truth for the receiver. 246 // Caution: Checker.funcDecl (decl.go) marks a function by setting its type to an empty 247 // signature. We may reach here before the signature is fully set up: we must explicitly 248 // check if the receiver is set (we cannot just look for non-nil f.typ). 249 if sig, _ := f.typ.(*Signature); sig != nil && sig.recv != nil { 250 _, isPtr := deref(sig.recv.typ) 251 return isPtr 252 } 253 254 // If a method's type is not set it may be a method/function that is: 255 // 1) client-supplied (via NewFunc with no signature), or 256 // 2) internally created but not yet type-checked. 257 // For case 1) we can't do anything; the client must know what they are doing. 258 // For case 2) we can use the information gathered by the resolver. 259 return f.hasPtrRecv 260} 261