blob: 5113002dcace01cb335002a8c18083c7408e4bf2 [file] [log] [blame]
Peter Collingbourne56109b72015-01-13 20:45:08 +00001// 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
5package ssa
6
7// This file defines utilities for population of method sets.
8
9import (
10 "fmt"
11
12 "llvm.org/llgo/third_party/gotools/go/types"
13)
14
15// Method returns the Function implementing method sel, building
16// wrapper methods on demand. It returns nil if sel denotes an
17// abstract (interface) method.
18//
19// Precondition: sel.Kind() == MethodVal.
20//
21// TODO(adonovan): rename this to MethodValue because of the
22// precondition, and for consistency with functions in source.go.
23//
24// Thread-safe.
25//
26// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
27//
28func (prog *Program) Method(sel *types.Selection) *Function {
29 if sel.Kind() != types.MethodVal {
30 panic(fmt.Sprintf("Method(%s) kind != MethodVal", sel))
31 }
32 T := sel.Recv()
33 if isInterface(T) {
34 return nil // abstract method
35 }
36 if prog.mode&LogSource != 0 {
37 defer logStack("Method %s %v", T, sel)()
38 }
39
40 prog.methodsMu.Lock()
41 defer prog.methodsMu.Unlock()
42
43 return prog.addMethod(prog.createMethodSet(T), sel)
44}
45
46// LookupMethod returns the implementation of the method of type T
47// identified by (pkg, name). It returns nil if the method exists but
48// is abstract, and panics if T has no such method.
49//
50func (prog *Program) LookupMethod(T types.Type, pkg *types.Package, name string) *Function {
51 sel := prog.MethodSets.MethodSet(T).Lookup(pkg, name)
52 if sel == nil {
53 panic(fmt.Sprintf("%s has no method %s", T, types.Id(pkg, name)))
54 }
55 return prog.Method(sel)
56}
57
Peter Collingbourne56109b72015-01-13 20:45:08 +000058// methodSet contains the (concrete) methods of a non-interface type.
59type methodSet struct {
60 mapping map[string]*Function // populated lazily
61 complete bool // mapping contains all methods
62}
63
64// Precondition: !isInterface(T).
65// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
66func (prog *Program) createMethodSet(T types.Type) *methodSet {
67 mset, ok := prog.methodSets.At(T).(*methodSet)
68 if !ok {
69 mset = &methodSet{mapping: make(map[string]*Function)}
70 prog.methodSets.Set(T, mset)
71 }
72 return mset
73}
74
75// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
76func (prog *Program) addMethod(mset *methodSet, sel *types.Selection) *Function {
77 if sel.Kind() == types.MethodExpr {
78 panic(sel)
79 }
80 id := sel.Obj().Id()
81 fn := mset.mapping[id]
82 if fn == nil {
83 obj := sel.Obj().(*types.Func)
84
85 needsPromotion := len(sel.Index()) > 1
86 needsIndirection := !isPointer(recvType(obj)) && isPointer(sel.Recv())
87 if needsPromotion || needsIndirection {
88 fn = makeWrapper(prog, sel)
89 } else {
90 fn = prog.declaredFunc(obj)
91 }
92 if fn.Signature.Recv() == nil {
93 panic(fn) // missing receiver
94 }
95 mset.mapping[id] = fn
96 }
97 return fn
98}
99
Peter Collingbourne7d396412015-04-05 23:28:18 +0000100// RuntimeTypes returns a new unordered slice containing all
Peter Collingbourne56109b72015-01-13 20:45:08 +0000101// concrete types in the program for which a complete (non-empty)
102// method set is required at run-time.
103//
Peter Collingbourne56109b72015-01-13 20:45:08 +0000104// Thread-safe.
105//
106// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
107//
Peter Collingbourne7d396412015-04-05 23:28:18 +0000108func (prog *Program) RuntimeTypes() []types.Type {
Peter Collingbourne56109b72015-01-13 20:45:08 +0000109 prog.methodsMu.Lock()
110 defer prog.methodsMu.Unlock()
111
112 var res []types.Type
113 prog.methodSets.Iterate(func(T types.Type, v interface{}) {
114 if v.(*methodSet).complete {
115 res = append(res, T)
116 }
117 })
118 return res
119}
120
Peter Collingbourne56109b72015-01-13 20:45:08 +0000121// declaredFunc returns the concrete function/method denoted by obj.
122// Panic ensues if there is none.
123//
124func (prog *Program) declaredFunc(obj *types.Func) *Function {
125 if v := prog.packageLevelValue(obj); v != nil {
126 return v.(*Function)
127 }
128 panic("no concrete method: " + obj.String())
129}
Peter Collingbourne7d396412015-04-05 23:28:18 +0000130
131// needMethodsOf ensures that runtime type information (including the
132// complete method set) is available for the specified type T and all
133// its subcomponents.
134//
135// needMethodsOf must be called for at least every type that is an
136// operand of some MakeInterface instruction, and for the type of
137// every exported package member.
138//
139// Precondition: T is not a method signature (*Signature with Recv()!=nil).
140//
141// Thread-safe. (Called via emitConv from multiple builder goroutines.)
142//
143// TODO(adonovan): make this faster. It accounts for 20% of SSA build time.
144//
145// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
146//
147func (prog *Program) needMethodsOf(T types.Type) {
148 prog.methodsMu.Lock()
149 prog.needMethods(T, false)
150 prog.methodsMu.Unlock()
151}
152
153// Precondition: T is not a method signature (*Signature with Recv()!=nil).
154// Recursive case: skip => don't create methods for T.
155//
156// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
157//
158func (prog *Program) needMethods(T types.Type, skip bool) {
159 // Each package maintains its own set of types it has visited.
160 if prevSkip, ok := prog.runtimeTypes.At(T).(bool); ok {
161 // needMethods(T) was previously called
162 if !prevSkip || skip {
163 return // already seen, with same or false 'skip' value
164 }
165 }
166 prog.runtimeTypes.Set(T, skip)
167
168 tmset := prog.MethodSets.MethodSet(T)
169
170 if !skip && !isInterface(T) && tmset.Len() > 0 {
171 // Create methods of T.
172 mset := prog.createMethodSet(T)
173 if !mset.complete {
174 mset.complete = true
175 n := tmset.Len()
176 for i := 0; i < n; i++ {
177 prog.addMethod(mset, tmset.At(i))
178 }
179 }
180 }
181
182 // Recursion over signatures of each method.
183 for i := 0; i < tmset.Len(); i++ {
184 sig := tmset.At(i).Type().(*types.Signature)
185 prog.needMethods(sig.Params(), false)
186 prog.needMethods(sig.Results(), false)
187 }
188
189 switch t := T.(type) {
190 case *types.Basic:
191 // nop
192
193 case *types.Interface:
194 // nop---handled by recursion over method set.
195
196 case *types.Pointer:
197 prog.needMethods(t.Elem(), false)
198
199 case *types.Slice:
200 prog.needMethods(t.Elem(), false)
201
202 case *types.Chan:
203 prog.needMethods(t.Elem(), false)
204
205 case *types.Map:
206 prog.needMethods(t.Key(), false)
207 prog.needMethods(t.Elem(), false)
208
209 case *types.Signature:
210 if t.Recv() != nil {
211 panic(fmt.Sprintf("Signature %s has Recv %s", t, t.Recv()))
212 }
213 prog.needMethods(t.Params(), false)
214 prog.needMethods(t.Results(), false)
215
216 case *types.Named:
217 // A pointer-to-named type can be derived from a named
218 // type via reflection. It may have methods too.
219 prog.needMethods(types.NewPointer(T), false)
220
221 // Consider 'type T struct{S}' where S has methods.
222 // Reflection provides no way to get from T to struct{S},
223 // only to S, so the method set of struct{S} is unwanted,
224 // so set 'skip' flag during recursion.
225 prog.needMethods(t.Underlying(), true)
226
227 case *types.Array:
228 prog.needMethods(t.Elem(), false)
229
230 case *types.Struct:
231 for i, n := 0, t.NumFields(); i < n; i++ {
232 prog.needMethods(t.Field(i).Type(), false)
233 }
234
235 case *types.Tuple:
236 for i, n := 0, t.Len(); i < n; i++ {
237 prog.needMethods(t.At(i).Type(), false)
238 }
239
240 default:
241 panic(T)
242 }
243}