diff --git a/go/types/assignments.go b/go/types/assignments.go index f045acb4..de4b28fe 100644 --- a/go/types/assignments.go +++ b/go/types/assignments.go @@ -264,7 +264,7 @@ func (check *checker) shortVarDecl(lhs, rhs []ast.Expr) { // Use the correct obj if the ident is redeclared. The // variable's scope starts after the declaration; so we // must use Scope.Lookup here and call Scope.Insert later. - if alt := scope.Lookup(nil, ident.Name); alt != nil { + if alt := scope.Lookup(ident.Name); alt != nil { // redeclared object must be a variable if alt, _ := alt.(*Var); alt != nil { obj = alt diff --git a/go/types/call.go b/go/types/call.go index aa3da728..7e3786fd 100644 --- a/go/types/call.go +++ b/go/types/call.go @@ -171,7 +171,7 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) { if ident, ok := e.X.(*ast.Ident); ok { if pkg, ok := check.topScope.LookupParent(ident.Name).(*Package); ok { check.recordObject(ident, pkg) - exp := pkg.scope.Lookup(nil, sel) + exp := pkg.scope.Lookup(sel) if exp == nil { check.errorf(e.Pos(), "%s not declared by package %s", sel, ident) goto Error diff --git a/go/types/gcimporter.go b/go/types/gcimporter.go index ab249d08..cbd6f3e5 100644 --- a/go/types/gcimporter.go +++ b/go/types/gcimporter.go @@ -207,7 +207,7 @@ func declConst(pkg *Package, name string) *Const { // the constant may have been imported before - if it exists // already in the respective scope, return that constant scope := pkg.scope - if obj := scope.Lookup(nil, name); obj != nil { + if obj := scope.Lookup(name); obj != nil { return obj.(*Const) } // otherwise create a new constant and insert it into the scope @@ -218,7 +218,7 @@ func declConst(pkg *Package, name string) *Const { func declTypeName(pkg *Package, name string) *TypeName { scope := pkg.scope - if obj := scope.Lookup(nil, name); obj != nil { + if obj := scope.Lookup(name); obj != nil { return obj.(*TypeName) } obj := NewTypeName(token.NoPos, pkg, name, nil) @@ -231,7 +231,7 @@ func declTypeName(pkg *Package, name string) *TypeName { func declVar(pkg *Package, name string) *Var { scope := pkg.scope - if obj := scope.Lookup(nil, name); obj != nil { + if obj := scope.Lookup(name); obj != nil { return obj.(*Var) } obj := NewVar(token.NoPos, pkg, name, nil) @@ -241,7 +241,7 @@ func declVar(pkg *Package, name string) *Var { func declFunc(pkg *Package, name string) *Func { scope := pkg.scope - if obj := scope.Lookup(nil, name); obj != nil { + if obj := scope.Lookup(name); obj != nil { return obj.(*Func) } obj := NewFunc(token.NoPos, pkg, name, nil) @@ -390,7 +390,7 @@ func (p *gcParser) parseExportedName() (pkg *Package, name string) { // func (p *gcParser) parseBasicType() Type { id := p.expect(scanner.Ident) - obj := Universe.Lookup(nil, id) + obj := Universe.Lookup(id) if obj, ok := obj.(*TypeName); ok { return obj.typ } diff --git a/go/types/gcimporter_test.go b/go/types/gcimporter_test.go index ad9e8ddf..bc0378a8 100644 --- a/go/types/gcimporter_test.go +++ b/go/types/gcimporter_test.go @@ -146,7 +146,7 @@ func TestGcImportedTypes(t *testing.T) { continue } - obj := pkg.scope.Lookup(nil, objName) + obj := pkg.scope.Lookup(objName) // TODO(gri) should define an accessor on Object var kind ast.ObjKind diff --git a/go/types/objset.go b/go/types/objset.go new file mode 100644 index 00000000..afb92c10 --- /dev/null +++ b/go/types/objset.go @@ -0,0 +1,36 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements objsets. +// +// An objset is similar to a Scope but objset elements +// are identified by their unique id, instead of their +// object name. + +package types + +// An objset is a set of objects identified by their unique id. +// The zero value for objset is a ready-to-use empty objset. +type objset struct { + objmap map[string]Object // allocated lazily +} + +// insert attempts to insert an object obj into objset s. +// If s already contains an alternative object alt with +// the same name, insert leaves s unchanged and returns alt. +// Otherwise it inserts obj and returns nil. +// The object name must not be blank _. +func (s *objset) insert(obj Object) (alt Object) { + name := obj.Name() + assert(name != "_") + id := Id(obj.Pkg(), name) + if alt := s.objmap[id]; alt != nil { + return alt + } + if s.objmap == nil { + s.objmap = make(map[string]Object) + } + s.objmap[id] = obj + return nil +} diff --git a/go/types/resolver.go b/go/types/resolver.go index ceee0884..55733510 100644 --- a/go/types/resolver.go +++ b/go/types/resolver.go @@ -23,14 +23,29 @@ func (check *checker) reportAltDecl(obj Object) { } } -func (check *checker) declare(scope *Scope, id *ast.Ident, obj Object) { +func (check *checker) declareObj(scope *Scope, id *ast.Ident, obj Object) { if obj.Name() == "_" { // blank identifiers are not declared obj.setParent(scope) + obj = nil } else if alt := scope.Insert(obj); alt != nil { check.errorf(obj.Pos(), "%s redeclared in this block", obj.Name()) check.reportAltDecl(alt) - obj = nil // for callIdent below + obj = nil + } + if id != nil { + check.recordObject(id, obj) + } +} + +func (check *checker) declareFld(oset *objset, id *ast.Ident, obj Object) { + if obj.Name() == "_" { + // blank identifiers are not declared + obj = nil + } else if alt := oset.insert(obj); alt != nil { + check.errorf(obj.Pos(), "%s redeclared", obj.Name()) + check.reportAltDecl(alt) + obj = nil } if id != nil { check.recordObject(id, obj) @@ -128,7 +143,7 @@ func (check *checker) resolveFiles(files []*ast.File) { return } - check.declare(pkg.scope, ident, obj) + check.declareObj(pkg.scope, ident, obj) objList = append(objList, obj) objMap[obj] = declInfo{scope, typ, init, nil} } @@ -195,13 +210,13 @@ func (check *checker) resolveFiles(files []*ast.File) { if obj.IsExported() { // Note: This will change each imported object's scope! // May be an issue for types aliases. - check.declare(scope, nil, obj) + check.declareObj(scope, nil, obj) check.recordImplicit(s, obj) } } } else { // declare imported package object in file scope - check.declare(scope, nil, imp2) + check.declareObj(scope, nil, imp2) } case *ast.ValueSpec: @@ -281,7 +296,7 @@ func (check *checker) resolveFiles(files []*ast.File) { obj.parent = pkg.scope check.recordObject(d.Name, obj) } else { - check.declare(pkg.scope, d.Name, obj) + check.declareObj(pkg.scope, d.Name, obj) } } else { // Associate method with receiver base type name, if possible. @@ -313,7 +328,7 @@ func (check *checker) resolveFiles(files []*ast.File) { for _, scope := range scopes { for _, obj := range scope.entries { - if alt := pkg.scope.Lookup(nil, obj.Name()); alt != nil { + if alt := pkg.scope.Lookup(obj.Name()); alt != nil { check.errorf(alt.Pos(), "%s already declared in this file through import of package %s", obj.Name(), obj.Pkg().Name()) } } @@ -503,41 +518,39 @@ func (check *checker) typeDecl(obj *TypeName, typ ast.Expr, def *Named, cycleOk // spec: "For a base type, the non-blank names of methods bound // to it must be unique." - // => use a scope to determine redeclarations - scope := NewScope(nil) + // => use an objset to determine redeclarations + var mset objset // spec: "If the base type is a struct type, the non-blank method // and field names must be distinct." - // => pre-populate the scope to find conflicts + // => pre-populate the objset to find conflicts + // TODO(gri) consider keeping the objset with the struct instead if t, _ := named.underlying.(*Struct); t != nil { for _, fld := range t.fields { if fld.name != "_" { - scope.Insert(fld) + assert(mset.insert(fld) == nil) } } } // check each method for _, m := range methods { - assert(m.name != "_") // _ methods were excluded before - mdecl := check.objMap[m] - alt := scope.Insert(m) - m.parent = mdecl.file // correct parent scope (scope.Insert used scope) + ident := check.objMap[m].fdecl.Name - if alt != nil { - switch alt := alt.(type) { + if alt := mset.insert(m); alt != nil { + switch alt.(type) { case *Var: check.errorf(m.pos, "field and method with the same name %s", m.name) - check.reportAltDecl(alt) - m = nil case *Func: check.errorf(m.pos, "method %s already declared for %s", m.name, named) - check.reportAltDecl(alt) - m = nil + default: + unreachable() } + check.reportAltDecl(alt) + m = nil } - check.recordObject(mdecl.fdecl.Name, m) + check.recordObject(ident, m) // If the method is valid, type-check its signature, // and collect it with the named base type. @@ -604,7 +617,7 @@ func (check *checker) declStmt(decl ast.Decl) { check.arityMatch(s, last) for i, name := range s.Names { - check.declare(check.topScope, name, lhs[i]) + check.declareObj(check.topScope, name, lhs[i]) } case token.VAR: @@ -637,7 +650,7 @@ func (check *checker) declStmt(decl ast.Decl) { check.arityMatch(s, nil) for i, name := range s.Names { - check.declare(check.topScope, name, lhs[i]) + check.declareObj(check.topScope, name, lhs[i]) } default: @@ -646,7 +659,7 @@ func (check *checker) declStmt(decl ast.Decl) { case *ast.TypeSpec: obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil) - check.declare(check.topScope, s.Name, obj) + check.declareObj(check.topScope, s.Name, obj) check.typeDecl(obj, s.Type, nil, false) default: diff --git a/go/types/scope.go b/go/types/scope.go index faafa1c1..062dbd16 100644 --- a/go/types/scope.go +++ b/go/types/scope.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// This file implements Scopes. + package types import ( @@ -16,15 +18,16 @@ import ( // objects can use that information for better printing. // A Scope maintains a set of objects and links to its containing -// (parent) and contained (children) scopes. -// Objects may be inserted and looked up by name, or by package path -// and name. A nil *Scope acts like an empty scope for operations that -// do not modify the scope or access a scope's parent scope. +// (parent) and contained (children) scopes. Objects may be inserted +// and looked up by name. The zero value for Scope is a ready-to-use +// empty scope. type Scope struct { parent *Scope children []*Scope - entries []Object node ast.Node + + entries []Object + objmap map[string]Object // lazily allocated for large scopes } // NewScope returns a new, empty scope contained in the given parent @@ -60,74 +63,39 @@ func (s *Scope) Parent() *Scope { return s.parent } func (s *Scope) Node() ast.Node { return s.node } // NumEntries() returns the number of scope entries. -// If s == nil, the result is 0. -func (s *Scope) NumEntries() int { - if s == nil { - return 0 // empty scope - } - return len(s.entries) -} +func (s *Scope) NumEntries() int { return len(s.entries) } // At returns the i'th scope entry for 0 <= i < NumEntries(). func (s *Scope) At(i int) Object { return s.entries[i] } // NumChildren() returns the number of scopes nested in s. -// If s == nil, the result is 0. -func (s *Scope) NumChildren() int { - if s == nil { - return 0 - } - return len(s.children) -} +func (s *Scope) NumChildren() int { return len(s.children) } // Child returns the i'th child scope for 0 <= i < NumChildren(). func (s *Scope) Child(i int) *Scope { return s.children[i] } -// Lookup returns the object in scope s with the given package -// and name if such an object exists; otherwise the result is nil. -// A nil scope acts like an empty scope, and parent scopes are ignored. -// -// If pkg != nil, both pkg.Path() and name are used to identify an -// entry, per the Go rules for identifier equality. If pkg == nil, -// only the name is used and the package path is ignored. -func (s *Scope) Lookup(pkg *Package, name string) Object { - if s == nil { - return nil // empty scope +// Lookup returns the object in scope s with the given name if such an +// object exists; otherwise the result is nil. +func (s *Scope) Lookup(name string) Object { + if s.objmap != nil { + return s.objmap[name] } - - // fast path: only the name must match - if pkg == nil { - for _, obj := range s.entries { - if obj.Name() == name { - return obj - } - } - return nil - } - - // slow path: both pkg path and name must match for _, obj := range s.entries { - if obj.sameId(pkg, name) { + if obj.Name() == name { return obj } } - - // not found return nil - - // TODO(gri) Optimize Lookup by also maintaining a map representation - // for larger scopes. } -// LookupParent follows the parent chain of scopes starting with s until it finds -// a scope where Lookup(nil, name) returns a non-nil object, and then returns that -// object. If no such scope exists, the result is nil. +// LookupParent follows the parent chain of scopes starting with s until +// it finds a scope where Lookup(name) returns a non-nil object, and then +// returns that object. If no such scope exists, the result is nil. func (s *Scope) LookupParent(name string) Object { - for s != nil { - if obj := s.Lookup(nil, name); obj != nil { + for ; s != nil; s = s.parent { + if obj := s.Lookup(name); obj != nil { return obj } - s = s.parent } return nil } @@ -135,19 +103,34 @@ func (s *Scope) LookupParent(name string) Object { // TODO(gri): Should Insert not be exported? // Insert attempts to insert an object obj into scope s. -// If s already contains an object with the same package path -// and name, Insert leaves s unchanged and returns that object. -// Otherwise it inserts obj, sets the object's scope to s, and -// returns nil. The object must not have the blank _ name. -// +// If s already contains an alternative object alt with +// the same name, Insert leaves s unchanged and returns alt. +// Otherwise it inserts obj, sets the object's scope to +// s, and returns nil. The object name must not be blank _. func (s *Scope) Insert(obj Object) Object { name := obj.Name() assert(name != "_") - if alt := s.Lookup(obj.Pkg(), name); alt != nil { + if alt := s.Lookup(name); alt != nil { return alt } + + // populate parallel objmap for larger scopes + // TODO(gri) what is the right threshold? should we only use a map? + if len(s.entries) == 32 { + m := make(map[string]Object) + for _, obj := range s.entries { + m[obj.Name()] = obj + } + s.objmap = m + } + + // add object s.entries = append(s.entries, obj) + if s.objmap != nil { + s.objmap[name] = obj + } obj.setParent(s) + return nil } @@ -159,7 +142,7 @@ func (s *Scope) WriteTo(w io.Writer, n int, recurse bool) { const ind = ". " indn := strings.Repeat(ind, n) - if s.NumEntries() == 0 { + if len(s.entries) == 0 { fmt.Fprintf(w, "%sscope %p {}\n", indn, s) return } diff --git a/go/types/stmt.go b/go/types/stmt.go index 5930a3c3..a55bb5bd 100644 --- a/go/types/stmt.go +++ b/go/types/stmt.go @@ -85,7 +85,7 @@ func (check *checker) stmt(s ast.Stmt) { check.funcSig.labels = scope } label := s.Label - check.declare(scope, label, NewLabel(label.Pos(), label.Name)) + check.declareObj(scope, label, NewLabel(label.Pos(), label.Name)) check.stmt(s.Stmt) case *ast.ExprStmt: @@ -267,7 +267,7 @@ func (check *checker) stmt(s ast.Stmt) { if tag == nil { // use fake true tag value and position it at the opening { of the switch ident := &ast.Ident{NamePos: s.Body.Lbrace, Name: "true"} - check.recordObject(ident, Universe.Lookup(nil, "true")) + check.recordObject(ident, Universe.Lookup("true")) tag = ident } check.expr(&x, tag) @@ -416,7 +416,7 @@ func (check *checker) stmt(s ast.Stmt) { typ = x.typ } obj := NewVar(lhs.Pos(), check.pkg, lhs.Name, typ) - check.declare(check.topScope, nil, obj) + check.declareObj(check.topScope, nil, obj) check.recordImplicit(clause, obj) } check.stmtList(clause.Body) @@ -552,7 +552,7 @@ func (check *checker) stmt(s ast.Stmt) { // declare variables for i, ident := range idents { - check.declare(check.topScope, ident, vars[i]) + check.declareObj(check.topScope, ident, vars[i]) } } else { // ordinary assignment diff --git a/go/types/types.go b/go/types/types.go index 0a459d21..c68a7006 100644 --- a/go/types/types.go +++ b/go/types/types.go @@ -193,7 +193,7 @@ func (t *Tuple) At(i int) *Var { return t.vars[i] } // A Signature represents a (non-builtin) function type. type Signature struct { - scope *Scope // function scope + scope *Scope // function scope, always present labels *Scope // label scope, or nil (lazily allocated) recv *Var // nil if not a method params *Tuple // (incoming) parameters from left to right; or nil @@ -205,7 +205,9 @@ type Signature struct { // and results, either of which may be nil. If isVariadic is set, the function // is variadic, it must have at least one parameter, and the last parameter // must be of unnamed slice type. -func NewSignature(recv *Var, params, results *Tuple, isVariadic bool) *Signature { +func NewSignature(scope *Scope, recv *Var, params, results *Tuple, isVariadic bool) *Signature { + // TODO(gri) Should we rely on the correct (non-nil) incoming scope + // or should this function allocate and populate a scope? if isVariadic { n := params.Len() if n == 0 { @@ -215,7 +217,7 @@ func NewSignature(recv *Var, params, results *Tuple, isVariadic bool) *Signature panic("types.NewSignature: variadic parameter must be of unnamed slice type") } } - return &Signature{nil, nil, recv, params, results, isVariadic} + return &Signature{scope, nil, recv, params, results, isVariadic} } // Recv returns the receiver of signature s, or nil. diff --git a/go/types/types_test.go b/go/types/types_test.go index 869d10e3..a3dd631f 100644 --- a/go/types/types_test.go +++ b/go/types/types_test.go @@ -110,7 +110,7 @@ func TestTypes(t *testing.T) { t.Errorf("%s: %s", src, err) continue } - typ := pkg.scope.Lookup(nil, "T").Type().Underlying() + typ := pkg.scope.Lookup("T").Type().Underlying() str := typeString(typ) if str != test.str { t.Errorf("%s: got %s, want %s", test.src, str, test.str) diff --git a/go/types/typexpr.go b/go/types/typexpr.go index e649be18..91a93b13 100644 --- a/go/types/typexpr.go +++ b/go/types/typexpr.go @@ -360,7 +360,7 @@ func (check *checker) collectParams(scope *Scope, list *ast.FieldList, variadicO // named parameter for _, name := range field.Names { par := NewVar(name.Pos(), check.pkg, name.Name, typ) - check.declare(scope, name, par) + check.declareObj(scope, name, par) params = append(params, par) } } else { @@ -385,7 +385,8 @@ func (check *checker) collectMethods(recv Type, list *ast.FieldList, cycleOk boo return nil } - scope := NewScope(nil) + var mset objset + for _, f := range list.List { // TODO(gri) Consider calling funcType here. typ := check.typ(f.Type, nil, cycleOk) @@ -402,7 +403,7 @@ func (check *checker) collectMethods(recv Type, list *ast.FieldList, cycleOk boo sig.recv = NewVar(token.NoPos, check.pkg, "", recv) for _, name := range f.Names { m := NewFunc(name.Pos(), check.pkg, name.Name, sig) - check.declare(scope, name, m) + check.declareFld(&mset, name, m) methods = append(methods, m) } } else { @@ -417,7 +418,7 @@ func (check *checker) collectMethods(recv Type, list *ast.FieldList, cycleOk boo check.errorf(f.Type.Pos(), "reference to incomplete type %s - unimplemented", f.Type) case *Interface: for _, m := range t.methods { - check.declare(scope, nil, m) + check.declareFld(&mset, nil, m) methods = append(methods, m) } default: @@ -448,7 +449,7 @@ func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields [ return } - scope := NewScope(nil) + var fset objset var typ Type // current field typ var tag string // current field tag @@ -461,7 +462,7 @@ func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields [ } fld := NewFieldVar(pos, check.pkg, name, typ, anonymous) - check.declare(scope, ident, fld) + check.declareFld(&fset, ident, fld) fields = append(fields, fld) } diff --git a/go/types/universe.go b/go/types/universe.go index 75231c79..114f0744 100644 --- a/go/types/universe.go +++ b/go/types/universe.go @@ -116,7 +116,7 @@ func init() { def(NewFunc(token.NoPos, nil, f.name, f)) } - universeIota = Universe.Lookup(nil, "iota").(*Const) + universeIota = Universe.Lookup("iota").(*Const) } // Objects with names containing blanks are internal and not entered into diff --git a/importer/pkginfo.go b/importer/pkginfo.go index 07cb71ff..7392fe90 100644 --- a/importer/pkginfo.go +++ b/importer/pkginfo.go @@ -116,7 +116,7 @@ func (info *PackageInfo) IsType(e ast.Expr) bool { func (info *PackageInfo) IsPackageRef(sel *ast.SelectorExpr) types.Object { if id, ok := sel.X.(*ast.Ident); ok { if pkg, ok := info.ObjectOf(id).(*types.Package); ok { - return pkg.Scope().Lookup(nil, sel.Sel.Name) + return pkg.Scope().Lookup(sel.Sel.Name) } } return nil @@ -245,5 +245,5 @@ func (info *PackageInfo) BuiltinCallSignature(e *ast.CallExpr) *types.Signature panic("unknown builtin: " + builtin) } - return types.NewSignature(nil, types.NewTuple(params...), nil, isVariadic) + return types.NewSignature(nil, nil, types.NewTuple(params...), nil, isVariadic) } diff --git a/ssa/builder.go b/ssa/builder.go index 4cf400ad..50e1cbc3 100644 --- a/ssa/builder.go +++ b/ssa/builder.go @@ -1716,7 +1716,7 @@ func (b *builder) rangeIndexed(fn *Function, x Value, tv types.Type) (k, v Value } else { // length = len(x). var c Call - c.Call.Func = fn.Prog.builtins[types.Universe.Lookup(nil, "len")] + c.Call.Func = fn.Prog.builtins[types.Universe.Lookup("len")] c.Call.Args = []Value{x} c.setType(tInt) length = fn.emit(&c) diff --git a/ssa/interp/reflect.go b/ssa/interp/reflect.go index 3e3dd9c3..43632751 100644 --- a/ssa/interp/reflect.go +++ b/ssa/interp/reflect.go @@ -394,7 +394,7 @@ func newMethod(pkg *ssa.Package, recvType types.Type, name string) *ssa.Function // that is needed is the "pointerness" of Recv.Type, and for // now, we'll set it to always be false since we're only // concerned with rtype. Encapsulate this better. - sig := types.NewSignature(types.NewVar(token.NoPos, nil, "recv", recvType), nil, nil, false) + sig := types.NewSignature(nil, types.NewVar(token.NoPos, nil, "recv", recvType), nil, nil, false) fn := ssa.NewFunction(name, sig, "fake reflect method") fn.Pkg = pkg fn.Prog = pkg.Prog diff --git a/ssa/promote.go b/ssa/promote.go index 822e68c8..82323380 100644 --- a/ssa/promote.go +++ b/ssa/promote.go @@ -140,7 +140,7 @@ func makeMethod(prog *Program, typ types.Type, obj *types.Method) *Function { // func promotionWrapper(prog *Program, typ types.Type, obj *types.Method) *Function { old := obj.Func.Type().(*types.Signature) - sig := types.NewSignature(types.NewVar(token.NoPos, nil, "recv", typ), old.Params(), old.Results(), old.IsVariadic()) + sig := types.NewSignature(nil, types.NewVar(token.NoPos, nil, "recv", typ), old.Params(), old.Results(), old.IsVariadic()) // TODO(adonovan): include implicit field path in description. description := fmt.Sprintf("promotion wrapper for (%s).%s", old.Recv(), obj.Func.Name()) @@ -310,7 +310,7 @@ func boundMethodWrapper(meth *Function) *Function { s := meth.Signature fn = &Function{ name: "bound$" + meth.String(), - Signature: types.NewSignature(nil, s.Params(), s.Results(), s.IsVariadic()), // drop recv + Signature: types.NewSignature(nil, nil, s.Params(), s.Results(), s.IsVariadic()), // drop recv Synthetic: "bound method wrapper for " + meth.String(), Prog: prog, pos: meth.Pos(), @@ -359,7 +359,7 @@ func indirectionWrapper(meth *Function) *Function { // TODO(adonovan): is there a *types.Func for this method? fn = &Function{ name: meth.Name(), - Signature: types.NewSignature(recv, s.Params(), s.Results(), s.IsVariadic()), + Signature: types.NewSignature(nil, recv, s.Params(), s.Results(), s.IsVariadic()), Prog: prog, Synthetic: "receiver indirection wrapper for " + meth.String(), pos: meth.Pos(),