aboutsummaryrefslogtreecommitdiff
path: root/go/ssa/methods.go
diff options
context:
space:
mode:
Diffstat (limited to 'go/ssa/methods.go')
-rw-r--r--go/ssa/methods.go111
1 files changed, 72 insertions, 39 deletions
diff --git a/go/ssa/methods.go b/go/ssa/methods.go
index 22e1f3f0a..4185618cd 100644
--- a/go/ssa/methods.go
+++ b/go/ssa/methods.go
@@ -9,40 +9,55 @@ package ssa
import (
"fmt"
"go/types"
+
+ "golang.org/x/tools/internal/typeparams"
)
// MethodValue returns the Function implementing method sel, building
// wrapper methods on demand. It returns nil if sel denotes an
-// abstract (interface) method.
+// abstract (interface or parameterized) method.
//
// Precondition: sel.Kind() == MethodVal.
//
// Thread-safe.
//
// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
-//
func (prog *Program) MethodValue(sel *types.Selection) *Function {
if sel.Kind() != types.MethodVal {
panic(fmt.Sprintf("MethodValue(%s) kind != MethodVal", sel))
}
T := sel.Recv()
- if isInterface(T) {
- return nil // abstract method
+ if types.IsInterface(T) {
+ return nil // abstract method (interface, possibly type param)
}
if prog.mode&LogSource != 0 {
defer logStack("MethodValue %s %v", T, sel)()
}
+ var m *Function
+ b := builder{created: &creator{}}
+
prog.methodsMu.Lock()
- defer prog.methodsMu.Unlock()
+ // Checks whether a type param is reachable from T.
+ // This is an expensive check. May need to be optimized later.
+ if !prog.parameterized.isParameterized(T) {
+ m = prog.addMethod(prog.createMethodSet(T), sel, b.created)
+ }
+ prog.methodsMu.Unlock()
- return prog.addMethod(prog.createMethodSet(T), sel)
+ if m == nil {
+ return nil // abstract method (generic)
+ }
+ for !b.done() {
+ b.buildCreated()
+ b.needsRuntimeTypes()
+ }
+ return m
}
// LookupMethod returns the implementation of the method of type T
// identified by (pkg, name). It returns nil if the method exists but
// is abstract, and panics if T has no such method.
-//
func (prog *Program) LookupMethod(T types.Type, pkg *types.Package, name string) *Function {
sel := prog.MethodSets.MethodSet(T).Lookup(pkg, name)
if sel == nil {
@@ -51,15 +66,20 @@ func (prog *Program) LookupMethod(T types.Type, pkg *types.Package, name string)
return prog.MethodValue(sel)
}
-// methodSet contains the (concrete) methods of a non-interface type.
+// methodSet contains the (concrete) methods of a concrete type (non-interface, non-parameterized).
type methodSet struct {
mapping map[string]*Function // populated lazily
complete bool // mapping contains all methods
}
-// Precondition: !isInterface(T).
+// Precondition: T is a concrete type, e.g. !isInterface(T) and not parameterized.
// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
func (prog *Program) createMethodSet(T types.Type) *methodSet {
+ if prog.mode&SanityCheckFunctions != 0 {
+ if types.IsInterface(T) || prog.parameterized.isParameterized(T) {
+ panic("type is interface or parameterized")
+ }
+ }
mset, ok := prog.methodSets.At(T).(*methodSet)
if !ok {
mset = &methodSet{mapping: make(map[string]*Function)}
@@ -68,22 +88,29 @@ func (prog *Program) createMethodSet(T types.Type) *methodSet {
return mset
}
+// Adds any created functions to cr.
+// Precondition: T is a concrete type, e.g. !isInterface(T) and not parameterized.
// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
-func (prog *Program) addMethod(mset *methodSet, sel *types.Selection) *Function {
+func (prog *Program) addMethod(mset *methodSet, sel *types.Selection, cr *creator) *Function {
if sel.Kind() == types.MethodExpr {
panic(sel)
}
id := sel.Obj().Id()
fn := mset.mapping[id]
if fn == nil {
- obj := sel.Obj().(*types.Func)
+ sel := toSelection(sel)
+ obj := sel.obj.(*types.Func)
- needsPromotion := len(sel.Index()) > 1
- needsIndirection := !isPointer(recvType(obj)) && isPointer(sel.Recv())
+ needsPromotion := len(sel.index) > 1
+ needsIndirection := !isPointer(recvType(obj)) && isPointer(sel.recv)
if needsPromotion || needsIndirection {
- fn = makeWrapper(prog, sel)
+ fn = makeWrapper(prog, sel, cr)
} else {
- fn = prog.declaredFunc(obj)
+ fn = prog.originFunc(obj)
+ if fn.typeparams.Len() > 0 { // instantiate
+ targs := receiverTypeArgs(obj)
+ fn = prog.lookupOrCreateInstance(fn, targs, cr)
+ }
}
if fn.Signature.Recv() == nil {
panic(fn) // missing receiver
@@ -100,7 +127,6 @@ func (prog *Program) addMethod(mset *methodSet, sel *types.Selection) *Function
// Thread-safe.
//
// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
-//
func (prog *Program) RuntimeTypes() []types.Type {
prog.methodsMu.Lock()
defer prog.methodsMu.Unlock()
@@ -116,7 +142,6 @@ func (prog *Program) RuntimeTypes() []types.Type {
// declaredFunc returns the concrete function/method denoted by obj.
// Panic ensues if there is none.
-//
func (prog *Program) declaredFunc(obj *types.Func) *Function {
if v := prog.packageLevelMember(obj); v != nil {
return v.(*Function)
@@ -132,26 +157,28 @@ func (prog *Program) declaredFunc(obj *types.Func) *Function {
// operand of some MakeInterface instruction, and for the type of
// every exported package member.
//
+// Adds any created functions to cr.
+//
// Precondition: T is not a method signature (*Signature with Recv()!=nil).
+// Precondition: T is not parameterized.
//
-// Thread-safe. (Called via emitConv from multiple builder goroutines.)
+// Thread-safe. (Called via Package.build from multiple builder goroutines.)
//
// TODO(adonovan): make this faster. It accounts for 20% of SSA build time.
//
// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
-//
-func (prog *Program) needMethodsOf(T types.Type) {
+func (prog *Program) needMethodsOf(T types.Type, cr *creator) {
prog.methodsMu.Lock()
- prog.needMethods(T, false)
+ prog.needMethods(T, false, cr)
prog.methodsMu.Unlock()
}
// Precondition: T is not a method signature (*Signature with Recv()!=nil).
+// Precondition: T is not parameterized.
// Recursive case: skip => don't create methods for T.
//
// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
-//
-func (prog *Program) needMethods(T types.Type, skip bool) {
+func (prog *Program) needMethods(T types.Type, skip bool, cr *creator) {
// Each package maintains its own set of types it has visited.
if prevSkip, ok := prog.runtimeTypes.At(T).(bool); ok {
// needMethods(T) was previously called
@@ -163,14 +190,14 @@ func (prog *Program) needMethods(T types.Type, skip bool) {
tmset := prog.MethodSets.MethodSet(T)
- if !skip && !isInterface(T) && tmset.Len() > 0 {
+ if !skip && !types.IsInterface(T) && tmset.Len() > 0 {
// Create methods of T.
mset := prog.createMethodSet(T)
if !mset.complete {
mset.complete = true
n := tmset.Len()
for i := 0; i < n; i++ {
- prog.addMethod(mset, tmset.At(i))
+ prog.addMethod(mset, tmset.At(i), cr)
}
}
}
@@ -178,8 +205,8 @@ func (prog *Program) needMethods(T types.Type, skip bool) {
// Recursion over signatures of each method.
for i := 0; i < tmset.Len(); i++ {
sig := tmset.At(i).Type().(*types.Signature)
- prog.needMethods(sig.Params(), false)
- prog.needMethods(sig.Results(), false)
+ prog.needMethods(sig.Params(), false, cr)
+ prog.needMethods(sig.Results(), false, cr)
}
switch t := T.(type) {
@@ -190,49 +217,55 @@ func (prog *Program) needMethods(T types.Type, skip bool) {
// nop---handled by recursion over method set.
case *types.Pointer:
- prog.needMethods(t.Elem(), false)
+ prog.needMethods(t.Elem(), false, cr)
case *types.Slice:
- prog.needMethods(t.Elem(), false)
+ prog.needMethods(t.Elem(), false, cr)
case *types.Chan:
- prog.needMethods(t.Elem(), false)
+ prog.needMethods(t.Elem(), false, cr)
case *types.Map:
- prog.needMethods(t.Key(), false)
- prog.needMethods(t.Elem(), false)
+ prog.needMethods(t.Key(), false, cr)
+ prog.needMethods(t.Elem(), false, cr)
case *types.Signature:
if t.Recv() != nil {
panic(fmt.Sprintf("Signature %s has Recv %s", t, t.Recv()))
}
- prog.needMethods(t.Params(), false)
- prog.needMethods(t.Results(), false)
+ prog.needMethods(t.Params(), false, cr)
+ prog.needMethods(t.Results(), false, cr)
case *types.Named:
// A pointer-to-named type can be derived from a named
// type via reflection. It may have methods too.
- prog.needMethods(types.NewPointer(T), false)
+ prog.needMethods(types.NewPointer(T), false, cr)
// Consider 'type T struct{S}' where S has methods.
// Reflection provides no way to get from T to struct{S},
// only to S, so the method set of struct{S} is unwanted,
// so set 'skip' flag during recursion.
- prog.needMethods(t.Underlying(), true)
+ prog.needMethods(t.Underlying(), true, cr)
case *types.Array:
- prog.needMethods(t.Elem(), false)
+ prog.needMethods(t.Elem(), false, cr)
case *types.Struct:
for i, n := 0, t.NumFields(); i < n; i++ {
- prog.needMethods(t.Field(i).Type(), false)
+ prog.needMethods(t.Field(i).Type(), false, cr)
}
case *types.Tuple:
for i, n := 0, t.Len(); i < n; i++ {
- prog.needMethods(t.At(i).Type(), false)
+ prog.needMethods(t.At(i).Type(), false, cr)
}
+ case *typeparams.TypeParam:
+ panic(T) // type parameters are always abstract.
+
+ case *typeparams.Union:
+ // nop
+
default:
panic(T)
}