Home | History | Annotate | Download | only in types
      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 various field and method lookup functions.
      6 
      7 package types
      8 
      9 // LookupFieldOrMethod looks up a field or method with given package and name
     10 // in T and returns the corresponding *Var or *Func, an index sequence, and a
     11 // bool indicating if there were any pointer indirections on the path to the
     12 // field or method. If addressable is set, T is the type of an addressable
     13 // variable (only matters for method lookups).
     14 //
     15 // The last index entry is the field or method index in the (possibly embedded)
     16 // type where the entry was found, either:
     17 //
     18 //	1) the list of declared methods of a named type; or
     19 //	2) the list of all methods (method set) of an interface type; or
     20 //	3) the list of fields of a struct type.
     21 //
     22 // The earlier index entries are the indices of the anonymous struct fields
     23 // traversed to get to the found entry, starting at depth 0.
     24 //
     25 // If no entry is found, a nil object is returned. In this case, the returned
     26 // index and indirect values have the following meaning:
     27 //
     28 //	- If index != nil, the index sequence points to an ambiguous entry
     29 //	(the same name appeared more than once at the same embedding level).
     30 //
     31 //	- If indirect is set, a method with a pointer receiver type was found
     32 //      but there was no pointer on the path from the actual receiver type to
     33 //	the method's formal receiver base type, nor was the receiver addressable.
     34 //
     35 func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) {
     36 	// Methods cannot be associated to a named pointer type
     37 	// (spec: "The type denoted by T is called the receiver base type;
     38 	// it must not be a pointer or interface type and it must be declared
     39 	// in the same package as the method.").
     40 	// Thus, if we have a named pointer type, proceed with the underlying
     41 	// pointer type but discard the result if it is a method since we would
     42 	// not have found it for T (see also issue 8590).
     43 	if t, _ := T.(*Named); t != nil {
     44 		if p, _ := t.underlying.(*Pointer); p != nil {
     45 			obj, index, indirect = lookupFieldOrMethod(p, false, pkg, name)
     46 			if _, ok := obj.(*Func); ok {
     47 				return nil, nil, false
     48 			}
     49 			return
     50 		}
     51 	}
     52 
     53 	return lookupFieldOrMethod(T, addressable, pkg, name)
     54 }
     55 
     56 // TODO(gri) The named type consolidation and seen maps below must be
     57 //           indexed by unique keys for a given type. Verify that named
     58 //           types always have only one representation (even when imported
     59 //           indirectly via different packages.)
     60 
     61 func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) {
     62 	// WARNING: The code in this function is extremely subtle - do not modify casually!
     63 	//          This function and NewMethodSet should be kept in sync.
     64 
     65 	if name == "_" {
     66 		return // blank fields/methods are never found
     67 	}
     68 
     69 	typ, isPtr := deref(T)
     70 	named, _ := typ.(*Named)
     71 
     72 	// *typ where typ is an interface has no methods.
     73 	if isPtr {
     74 		utyp := typ
     75 		if named != nil {
     76 			utyp = named.underlying
     77 		}
     78 		if _, ok := utyp.(*Interface); ok {
     79 			return
     80 		}
     81 	}
     82 
     83 	// Start with typ as single entry at shallowest depth.
     84 	// If typ is not a named type, insert a nil type instead.
     85 	current := []embeddedType{{named, nil, isPtr, false}}
     86 
     87 	// named types that we have seen already, allocated lazily
     88 	var seen map[*Named]bool
     89 
     90 	// search current depth
     91 	for len(current) > 0 {
     92 		var next []embeddedType // embedded types found at current depth
     93 
     94 		// look for (pkg, name) in all types at current depth
     95 		for _, e := range current {
     96 			// The very first time only, e.typ may be nil.
     97 			// In this case, we don't have a named type and
     98 			// we simply continue with the underlying type.
     99 			if e.typ != nil {
    100 				if seen[e.typ] {
    101 					// We have seen this type before, at a more shallow depth
    102 					// (note that multiples of this type at the current depth
    103 					// were consolidated before). The type at that depth shadows
    104 					// this same type at the current depth, so we can ignore
    105 					// this one.
    106 					continue
    107 				}
    108 				if seen == nil {
    109 					seen = make(map[*Named]bool)
    110 				}
    111 				seen[e.typ] = true
    112 
    113 				// look for a matching attached method
    114 				if i, m := lookupMethod(e.typ.methods, pkg, name); m != nil {
    115 					// potential match
    116 					assert(m.typ != nil)
    117 					index = concat(e.index, i)
    118 					if obj != nil || e.multiples {
    119 						return nil, index, false // collision
    120 					}
    121 					obj = m
    122 					indirect = e.indirect
    123 					continue // we can't have a matching field or interface method
    124 				}
    125 
    126 				// continue with underlying type
    127 				typ = e.typ.underlying
    128 			}
    129 
    130 			switch t := typ.(type) {
    131 			case *Struct:
    132 				// look for a matching field and collect embedded types
    133 				for i, f := range t.fields {
    134 					if f.sameId(pkg, name) {
    135 						assert(f.typ != nil)
    136 						index = concat(e.index, i)
    137 						if obj != nil || e.multiples {
    138 							return nil, index, false // collision
    139 						}
    140 						obj = f
    141 						indirect = e.indirect
    142 						continue // we can't have a matching interface method
    143 					}
    144 					// Collect embedded struct fields for searching the next
    145 					// lower depth, but only if we have not seen a match yet
    146 					// (if we have a match it is either the desired field or
    147 					// we have a name collision on the same depth; in either
    148 					// case we don't need to look further).
    149 					// Embedded fields are always of the form T or *T where
    150 					// T is a named type. If e.typ appeared multiple times at
    151 					// this depth, f.typ appears multiple times at the next
    152 					// depth.
    153 					if obj == nil && f.anonymous {
    154 						// Ignore embedded basic types - only user-defined
    155 						// named types can have methods or struct fields.
    156 						typ, isPtr := deref(f.typ)
    157 						if t, _ := typ.(*Named); t != nil {
    158 							next = append(next, embeddedType{t, concat(e.index, i), e.indirect || isPtr, e.multiples})
    159 						}
    160 					}
    161 				}
    162 
    163 			case *Interface:
    164 				// look for a matching method
    165 				// TODO(gri) t.allMethods is sorted - use binary search
    166 				if i, m := lookupMethod(t.allMethods, pkg, name); m != nil {
    167 					assert(m.typ != nil)
    168 					index = concat(e.index, i)
    169 					if obj != nil || e.multiples {
    170 						return nil, index, false // collision
    171 					}
    172 					obj = m
    173 					indirect = e.indirect
    174 				}
    175 			}
    176 		}
    177 
    178 		if obj != nil {
    179 			// found a potential match
    180 			// spec: "A method call x.m() is valid if the method set of (the type of) x
    181 			//        contains m and the argument list can be assigned to the parameter
    182 			//        list of m. If x is addressable and &x's method set contains m, x.m()
    183 			//        is shorthand for (&x).m()".
    184 			if f, _ := obj.(*Func); f != nil && ptrRecv(f) && !indirect && !addressable {
    185 				return nil, nil, true // pointer/addressable receiver required
    186 			}
    187 			return
    188 		}
    189 
    190 		current = consolidateMultiples(next)
    191 	}
    192 
    193 	return nil, nil, false // not found
    194 }
    195 
    196 // embeddedType represents an embedded named type
    197 type embeddedType struct {
    198 	typ       *Named // nil means use the outer typ variable instead
    199 	index     []int  // embedded field indices, starting with index at depth 0
    200 	indirect  bool   // if set, there was a pointer indirection on the path to this field
    201 	multiples bool   // if set, typ appears multiple times at this depth
    202 }
    203 
    204 // consolidateMultiples collects multiple list entries with the same type
    205 // into a single entry marked as containing multiples. The result is the
    206 // consolidated list.
    207 func consolidateMultiples(list []embeddedType) []embeddedType {
    208 	if len(list) <= 1 {
    209 		return list // at most one entry - nothing to do
    210 	}
    211 
    212 	n := 0                       // number of entries w/ unique type
    213 	prev := make(map[*Named]int) // index at which type was previously seen
    214 	for _, e := range list {
    215 		if i, found := prev[e.typ]; found {
    216 			list[i].multiples = true
    217 			// ignore this entry
    218 		} else {
    219 			prev[e.typ] = n
    220 			list[n] = e
    221 			n++
    222 		}
    223 	}
    224 	return list[:n]
    225 }
    226 
    227 // MissingMethod returns (nil, false) if V implements T, otherwise it
    228 // returns a missing method required by T and whether it is missing or
    229 // just has the wrong type.
    230 //
    231 // For non-interface types V, or if static is set, V implements T if all
    232 // methods of T are present in V. Otherwise (V is an interface and static
    233 // is not set), MissingMethod only checks that methods of T which are also
    234 // present in V have matching types (e.g., for a type assertion x.(T) where
    235 // x is of interface type V).
    236 //
    237 func MissingMethod(V Type, T *Interface, static bool) (method *Func, wrongType bool) {
    238 	// fast path for common case
    239 	if T.Empty() {
    240 		return
    241 	}
    242 
    243 	// TODO(gri) Consider using method sets here. Might be more efficient.
    244 
    245 	if ityp, _ := V.Underlying().(*Interface); ityp != nil {
    246 		// TODO(gri) allMethods is sorted - can do this more efficiently
    247 		for _, m := range T.allMethods {
    248 			_, obj := lookupMethod(ityp.allMethods, m.pkg, m.name)
    249 			switch {
    250 			case obj == nil:
    251 				if static {
    252 					return m, false
    253 				}
    254 			case !Identical(obj.Type(), m.typ):
    255 				return m, true
    256 			}
    257 		}
    258 		return
    259 	}
    260 
    261 	// A concrete type implements T if it implements all methods of T.
    262 	for _, m := range T.allMethods {
    263 		obj, _, _ := lookupFieldOrMethod(V, false, m.pkg, m.name)
    264 
    265 		f, _ := obj.(*Func)
    266 		if f == nil {
    267 			return m, false
    268 		}
    269 
    270 		if !Identical(f.typ, m.typ) {
    271 			return m, true
    272 		}
    273 	}
    274 
    275 	return
    276 }
    277 
    278 // assertableTo reports whether a value of type V can be asserted to have type T.
    279 // It returns (nil, false) as affirmative answer. Otherwise it returns a missing
    280 // method required by V and whether it is missing or just has the wrong type.
    281 func assertableTo(V *Interface, T Type) (method *Func, wrongType bool) {
    282 	// no static check is required if T is an interface
    283 	// spec: "If T is an interface type, x.(T) asserts that the
    284 	//        dynamic type of x implements the interface T."
    285 	if _, ok := T.Underlying().(*Interface); ok && !strict {
    286 		return
    287 	}
    288 	return MissingMethod(T, V, false)
    289 }
    290 
    291 // deref dereferences typ if it is a *Pointer and returns its base and true.
    292 // Otherwise it returns (typ, false).
    293 func deref(typ Type) (Type, bool) {
    294 	if p, _ := typ.(*Pointer); p != nil {
    295 		return p.base, true
    296 	}
    297 	return typ, false
    298 }
    299 
    300 // derefStructPtr dereferences typ if it is a (named or unnamed) pointer to a
    301 // (named or unnamed) struct and returns its base. Otherwise it returns typ.
    302 func derefStructPtr(typ Type) Type {
    303 	if p, _ := typ.Underlying().(*Pointer); p != nil {
    304 		if _, ok := p.base.Underlying().(*Struct); ok {
    305 			return p.base
    306 		}
    307 	}
    308 	return typ
    309 }
    310 
    311 // concat returns the result of concatenating list and i.
    312 // The result does not share its underlying array with list.
    313 func concat(list []int, i int) []int {
    314 	var t []int
    315 	t = append(t, list...)
    316 	return append(t, i)
    317 }
    318 
    319 // fieldIndex returns the index for the field with matching package and name, or a value < 0.
    320 func fieldIndex(fields []*Var, pkg *Package, name string) int {
    321 	if name != "_" {
    322 		for i, f := range fields {
    323 			if f.sameId(pkg, name) {
    324 				return i
    325 			}
    326 		}
    327 	}
    328 	return -1
    329 }
    330 
    331 // lookupMethod returns the index of and method with matching package and name, or (-1, nil).
    332 func lookupMethod(methods []*Func, pkg *Package, name string) (int, *Func) {
    333 	if name != "_" {
    334 		for i, m := range methods {
    335 			if m.sameId(pkg, name) {
    336 				return i, m
    337 			}
    338 		}
    339 	}
    340 	return -1, nil
    341 }
    342