func (c *converter) convertFunc(v *gotypes.Func) *types.Func { if v == nil { return nil } if v, ok := c.converted[v]; ok { return v.(*types.Func) } ret := types.NewFunc( token.Pos(v.Pos()), c.ret, v.Name(), c.convertSignature(v.Type().(*gotypes.Signature)), ) c.converted[v] = ret return ret }
// makeBound returns a bound method wrapper (or "bound"), a synthetic // function that delegates to a concrete or interface method denoted // by obj. The resulting function has no receiver, but has one free // variable which will be used as the method's receiver in the // tail-call. // // Use MakeClosure with such a wrapper to construct a bound method // closure. e.g.: // // type T int or: type T interface { meth() } // func (t T) meth() // var t T // f := t.meth // f() // calls t.meth() // // f is a closure of a synthetic wrapper defined as if by: // // f := func() { return t.meth() } // // Unlike makeWrapper, makeBound need perform no indirection or field // selections because that can be done before the closure is // constructed. // // EXCLUSIVE_LOCKS_ACQUIRED(meth.Prog.methodsMu) // func makeBound(prog *Program, obj *types.Func) *Function { prog.methodsMu.Lock() defer prog.methodsMu.Unlock() fn, ok := prog.bounds[obj] if !ok { description := fmt.Sprintf("bound method wrapper for %s", obj) if prog.mode&LogSource != 0 { defer logStack("%s", description)() } fn = &Function{ name: obj.Name() + "$bound", object: obj, Signature: changeRecv(obj.Type().(*types.Signature), nil), // drop receiver Synthetic: description, Prog: prog, pos: obj.Pos(), } fv := &FreeVar{name: "recv", typ: recvType(obj), parent: fn} fn.FreeVars = []*FreeVar{fv} fn.startBody() createParams(fn, 0) var c Call if !isInterface(recvType(obj)) { // concrete c.Call.Value = prog.declaredFunc(obj) c.Call.Args = []Value{fv} } else { c.Call.Value = fv c.Call.Method = obj } for _, arg := range fn.Params { c.Call.Args = append(c.Call.Args, arg) } emitTailCall(fn, &c) fn.finishBody() prog.bounds[obj] = fn } return fn }
// checkMethod performs safety checks for renaming a method. // There are three hazards: // - declaration conflicts // - selection ambiguity/changes // - entailed renamings of assignable concrete/interface types. // We reject renamings initiated at concrete methods if it would // change the assignability relation. For renamings of abstract // methods, we rename all methods transitively coupled to it via // assignability. func (r *renamer) checkMethod(from *types.Func) { // e.g. error.Error if from.Pkg() == nil { r.errorf(from.Pos(), "you cannot rename built-in method %s", from) return } // ASSIGNABILITY: We reject renamings of concrete methods that // would break a 'satisfy' constraint; but renamings of abstract // methods are allowed to proceed, and we rename affected // concrete and abstract methods as necessary. It is the // initial method that determines the policy. // Check for conflict at point of declaration. // Check to ensure preservation of assignability requirements. R := recv(from).Type() if isInterface(R) { // Abstract method // declaration prev, _, _ := types.LookupFieldOrMethod(R, false, from.Pkg(), r.to) if prev != nil { r.errorf(from.Pos(), "renaming this interface method %q to %q", from.Name(), r.to) r.errorf(prev.Pos(), "\twould conflict with this method") return } // Check all interfaces that embed this one for // declaration conflicts too. for _, info := range r.packages { // Start with named interface types (better errors) for _, obj := range info.Defs { if obj, ok := obj.(*types.TypeName); ok && isInterface(obj.Type()) { f, _, _ := types.LookupFieldOrMethod( obj.Type(), false, from.Pkg(), from.Name()) if f == nil { continue } t, _, _ := types.LookupFieldOrMethod( obj.Type(), false, from.Pkg(), r.to) if t == nil { continue } r.errorf(from.Pos(), "renaming this interface method %q to %q", from.Name(), r.to) r.errorf(t.Pos(), "\twould conflict with this method") r.errorf(obj.Pos(), "\tin named interface type %q", obj.Name()) } } // Now look at all literal interface types (includes named ones again). for e, tv := range info.Types { if e, ok := e.(*ast.InterfaceType); ok { _ = e _ = tv.Type.(*types.Interface) // TODO(adonovan): implement same check as above. } } } // assignability // // Find the set of concrete or abstract methods directly // coupled to abstract method 'from' by some // satisfy.Constraint, and rename them too. for key := range r.satisfy() { // key = (lhs, rhs) where lhs is always an interface. lsel := r.msets.MethodSet(key.LHS).Lookup(from.Pkg(), from.Name()) if lsel == nil { continue } rmethods := r.msets.MethodSet(key.RHS) rsel := rmethods.Lookup(from.Pkg(), from.Name()) if rsel == nil { continue } // If both sides have a method of this name, // and one of them is m, the other must be coupled. var coupled *types.Func switch from { case lsel.Obj(): coupled = rsel.Obj().(*types.Func) case rsel.Obj(): coupled = lsel.Obj().(*types.Func) default: continue } // We must treat concrete-to-interface // constraints like an implicit selection C.f of // each interface method I.f, and check that the // renaming leaves the selection unchanged and // unambiguous. // // Fun fact: the implicit selection of C.f // type I interface{f()} // type C struct{I} // func (C) g() // var _ I = C{} // here // yields abstract method I.f. This can make error // messages less than obvious. // if !isInterface(key.RHS) { // The logic below was derived from checkSelections. rtosel := rmethods.Lookup(from.Pkg(), r.to) if rtosel != nil { rto := rtosel.Obj().(*types.Func) delta := len(rsel.Index()) - len(rtosel.Index()) if delta < 0 { continue // no ambiguity } // TODO(adonovan): record the constraint's position. keyPos := token.NoPos r.errorf(from.Pos(), "renaming this method %q to %q", from.Name(), r.to) if delta == 0 { // analogous to same-block conflict r.errorf(keyPos, "\twould make the %s method of %s invoked via interface %s ambiguous", r.to, key.RHS, key.LHS) r.errorf(rto.Pos(), "\twith (%s).%s", recv(rto).Type(), r.to) } else { // analogous to super-block conflict r.errorf(keyPos, "\twould change the %s method of %s invoked via interface %s", r.to, key.RHS, key.LHS) r.errorf(coupled.Pos(), "\tfrom (%s).%s", recv(coupled).Type(), r.to) r.errorf(rto.Pos(), "\tto (%s).%s", recv(rto).Type(), r.to) } return // one error is enough } } if !r.changeMethods { // This should be unreachable. r.errorf(from.Pos(), "internal error: during renaming of abstract method %s", from) r.errorf(coupled.Pos(), "\tchangedMethods=false, coupled method=%s", coupled) r.errorf(from.Pos(), "\tPlease file a bug report") return } // Rename the coupled method to preserve assignability. r.check(coupled) } } else { // Concrete method // declaration prev, indices, _ := types.LookupFieldOrMethod(R, true, from.Pkg(), r.to) if prev != nil && len(indices) == 1 { r.errorf(from.Pos(), "renaming this method %q to %q", from.Name(), r.to) r.errorf(prev.Pos(), "\twould conflict with this %s", objectKind(prev)) return } // assignability // // Find the set of abstract methods coupled to concrete // method 'from' by some satisfy.Constraint, and rename // them too. // // Coupling may be indirect, e.g. I.f <-> C.f via type D. // // type I interface {f()} // type C int // type (C) f() // type D struct{C} // var _ I = D{} // for key := range r.satisfy() { // key = (lhs, rhs) where lhs is always an interface. if isInterface(key.RHS) { continue } rsel := r.msets.MethodSet(key.RHS).Lookup(from.Pkg(), from.Name()) if rsel == nil || rsel.Obj() != from { continue // rhs does not have the method } lsel := r.msets.MethodSet(key.LHS).Lookup(from.Pkg(), from.Name()) if lsel == nil { continue } imeth := lsel.Obj().(*types.Func) // imeth is the abstract method (e.g. I.f) // and key.RHS is the concrete coupling type (e.g. D). if !r.changeMethods { r.errorf(from.Pos(), "renaming this method %q to %q", from.Name(), r.to) var pos token.Pos var iface string I := recv(imeth).Type() if named, ok := I.(*types.Named); ok { pos = named.Obj().Pos() iface = "interface " + named.Obj().Name() } else { pos = from.Pos() iface = I.String() } r.errorf(pos, "\twould make %s no longer assignable to %s", key.RHS, iface) r.errorf(imeth.Pos(), "\t(rename %s.%s if you intend to change both types)", I, from.Name()) return // one error is enough } // Rename the coupled interface method to preserve assignability. r.check(imeth) } } // Check integrity of existing (field and method) selections. // We skip this if there were errors above, to avoid redundant errors. r.checkSelections(from) }