diff options
Diffstat (limited to 'go/ssa/methods.go')
-rw-r--r-- | go/ssa/methods.go | 111 |
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) } |