func (g *goGen) genFunc(o *types.Func) { sig := o.Type().(*types.Signature) params := "(" + g.tupleString(sig.Params()) + ")" ret := g.tupleString(sig.Results()) if sig.Results().Len() > 1 { ret = "(" + ret + ") " } else { ret += " " } //funcName := o.Name() g.Printf(` //export GoPy_%[1]s // GoPy_%[1]s wraps %[2]s func GoPy_%[1]s%[3]v%[4]v{ `, o.Name(), o.FullName(), params, ret, ) g.Indent() g.genFuncBody(o) g.Outdent() g.Printf("}\n\n") }
func (g *goGen) genFunc(o *types.Func) { g.Printf("func proxy_%s(out, in *seq.Buffer) {\n", o.Name()) g.Indent() g.genFuncBody(o, g.pkg.Name()) g.Outdent() g.Printf("}\n\n") }
// lookupMethod returns the method set for type typ, which may be one // of the interpreter's fake types. func lookupMethod(i *interpreter, typ types.Type, meth *types.Func) *ssa.Function { switch typ { case rtypeType: return i.rtypeMethods[meth.Id()] case errorType: return i.errorMethods[meth.Id()] } return i.prog.LookupMethod(typ, meth.Pkg(), meth.Name()) }
func (g *goGen) genFuncBody(o *types.Func, selectorLHS string) { sig := o.Type().(*types.Signature) params := sig.Params() for i := 0; i < params.Len(); i++ { p := params.At(i) t := seqType(p.Type()) if t == "Ref" { name := p.Type().(*types.Named).Obj().Name() g.Printf("var param_%s %s.%s\n", p.Name(), g.pkg.Name(), name) g.Printf("param_%s_ref := in.ReadRef()\n", p.Name()) g.Printf("if param_%s_ref.Num < 0 {\n", p.Name()) g.Printf(" param_%s = param_%s_ref.Get().(%s.%s)\n", p.Name(), p.Name(), g.pkg.Name(), name) g.Printf("} else {\n") g.Printf(" param_%s = (*proxy%s)(param_%s_ref)\n", p.Name(), name, p.Name()) g.Printf("}\n") } else { g.Printf("param_%s := in.Read%s()\n", p.Name(), t) } } res := sig.Results() if res.Len() > 2 || res.Len() == 2 && !isErrorType(res.At(1).Type()) { g.errorf("functions and methods must return either zero or one values, and optionally an error") return } returnsValue := false returnsError := false if res.Len() == 1 { if isErrorType(res.At(0).Type()) { returnsError = true g.Printf("err := ") } else { returnsValue = true g.Printf("res := ") } } else if res.Len() == 2 { returnsValue = true returnsError = true g.Printf("res, err := ") } g.Printf("%s.%s(", selectorLHS, o.Name()) for i := 0; i < params.Len(); i++ { if i > 0 { g.Printf(", ") } g.Printf("param_%s", params.At(i).Name()) } g.Printf(")\n") if returnsValue { g.genWrite("res", "out", res.At(0).Type()) } if returnsError { g.genWrite("err", "out", res.At(res.Len()-1).Type()) } }
func (p *printer) printFunc(recvType types.Type, obj *types.Func) { p.print("func ") sig := obj.Type().(*types.Signature) if recvType != nil { p.print("(") p.writeType(p.pkg, recvType) p.print(") ") } p.print(obj.Name()) p.writeSignature(p.pkg, sig) }
func (g *javaGen) genFunc(o *types.Func, method bool) { if err := g.funcSignature(o, !method); err != nil { g.errorf("%v", err) return } sig := o.Type().(*types.Signature) res := sig.Results() g.Printf(" {\n") g.Indent() g.Printf("go.Seq _in = new go.Seq();\n") g.Printf("go.Seq _out = new go.Seq();\n") returnsError := false var resultType types.Type if res.Len() > 0 { if !isErrorType(res.At(0).Type()) { resultType = res.At(0).Type() } if res.Len() > 1 || isErrorType(res.At(0).Type()) { returnsError = true } } if resultType != nil { t := g.javaType(resultType) g.Printf("%s _result;\n", t) } if method { g.Printf("_in.writeRef(ref);\n") } params := sig.Params() for i := 0; i < params.Len(); i++ { p := params.At(i) g.Printf("_in.write%s;\n", seqWrite(p.Type(), paramName(params, i))) } g.Printf("Seq.send(DESCRIPTOR, CALL_%s, _in, _out);\n", o.Name()) if resultType != nil { g.genRead("_result", "_out", resultType) } if returnsError { g.Printf(`String _err = _out.readString(); if (_err != null) { throw new Exception(_err); } `) } if resultType != nil { g.Printf("return _result;\n") } g.Outdent() g.Printf("}\n\n") }
// check if function is a test function for the testing package // we don't unexport those func (e *Export) checkFunction(from *types.Func, to string) { if !strings.HasPrefix(from.Name(), "Test") { return } sig := from.Type().(*types.Signature) if sig.Params().Len() != 1 { return } if sig.Params().At(0).Type().String() == "*testing.T" { e.Conflicting = true return } }
func (g *goGen) genFuncBody(o *types.Func) { sig := o.Type().(*types.Signature) results := newVars(sig.Results()) for i := range results { if i > 0 { g.Printf(", ") } g.Printf("_gopy_%03d", i) } if len(results) > 0 { g.Printf(" := ") } g.Printf("%s.%s(", g.pkg.Name(), o.Name()) args := sig.Params() for i := 0; i < args.Len(); i++ { arg := args.At(i) tail := "" if i+1 < args.Len() { tail = ", " } g.Printf("%s%s", arg.Name(), tail) } g.Printf(")\n") if len(results) <= 0 { return } g.Printf("return ") for i, res := range results { if i > 0 { g.Printf(", ") } // if needWrap(res.GoType()) { // g.Printf("") // } if res.needWrap() { g.Printf("%s(unsafe.Pointer(&", res.dtype.cgotype) } g.Printf("_gopy_%03d /* %#v %v */", i, res, res.GoType().Underlying()) if res.needWrap() { g.Printf("))") } } g.Printf("\n") }
func (g *cpyGen) genFunc(o *types.Func) { g.impl.Printf(` /* pythonization of: %[2]s.%[1]s */ static PyObject* gopy_%[1]s(PyObject *self, PyObject *args) { `, o.Name(), g.pkg.pkg.Name(), ) g.impl.Indent() g.genFuncBody(o) g.impl.Outdent() g.impl.Printf("}\n\n") }
func (g *javaGen) funcSignature(o *types.Func, static bool) error { sig := o.Type().(*types.Signature) res := sig.Results() var returnsError bool var ret string switch res.Len() { case 2: if !isErrorType(res.At(1).Type()) { return fmt.Errorf("second result value must be of type error: %s", o) } returnsError = true ret = g.javaType(res.At(0).Type()) case 1: if isErrorType(res.At(0).Type()) { returnsError = true ret = "void" } else { ret = g.javaType(res.At(0).Type()) } case 0: ret = "void" default: return fmt.Errorf("too many result values: %s", o) } g.Printf("public ") if static { g.Printf("static ") } g.Printf("%s %s(", ret, o.Name()) params := sig.Params() for i := 0; i < params.Len(); i++ { if i > 0 { g.Printf(", ") } v := sig.Params().At(i) name := paramName(params, i) jt := g.javaType(v.Type()) g.Printf("%s %s", jt, name) } g.Printf(")") if returnsError { g.Printf(" throws Exception") } return nil }
func (g *goGen) genFuncBody(o *types.Func, selectorLHS string) { sig := o.Type().(*types.Signature) params := sig.Params() for i := 0; i < params.Len(); i++ { p := params.At(i) g.genRead("param_"+paramName(params, i), "in", p.Type()) } res := sig.Results() if res.Len() > 2 || res.Len() == 2 && !isErrorType(res.At(1).Type()) { g.errorf("functions and methods must return either zero or one values, and optionally an error") return } returnsValue := false returnsError := false if res.Len() == 1 { if isErrorType(res.At(0).Type()) { returnsError = true g.Printf("err := ") } else { returnsValue = true g.Printf("res := ") } } else if res.Len() == 2 { returnsValue = true returnsError = true g.Printf("res, err := ") } g.Printf("%s.%s(", selectorLHS, o.Name()) for i := 0; i < params.Len(); i++ { if i > 0 { g.Printf(", ") } g.Printf("param_%s", paramName(params, i)) } g.Printf(")\n") if returnsValue { g.genWrite("res", "out", res.At(0).Type()) } if returnsError { g.genWrite("err", "out", res.At(res.Len()-1).Type()) } }
func checkFuncValue(t *testing.T, prog *ssa.Program, obj *types.Func) { fn := prog.FuncValue(obj) // fmt.Printf("FuncValue(%s) = %s\n", obj, fn) // debugging if fn == nil { if obj.Name() != "interfaceMethod" { t.Errorf("FuncValue(%s) == nil", obj) } return } if fnobj := fn.Object(); fnobj != obj { t.Errorf("FuncValue(%s).Object() == %s; value was %s", obj, fnobj, fn.Name()) return } if !types.Identical(fn.Type(), obj.Type()) { t.Errorf("FuncValue(%s).Type() == %s", obj, fn.Type()) return } }
// 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 }
func (p *Processor) tryMatchNewFunc(models []*Model, fun *types.Func) { modelName := fun.Name()[len("new"):] for _, m := range models { if m.Name != modelName { continue } sig := fun.Type().(*types.Signature) if sig.Recv() != nil { continue } res := sig.Results() for i := 0; i < res.Len(); i++ { if isTypeOrPtrTo(res.At(i).Type(), m.CheckedNode) { m.NewFunc = fun return } } } }
// checkMethod performs safety checks for renaming a method. // There are three hazards: // - declaration conflicts // - selection ambiguity/changes // - entailed renamings of assignable concrete/interface types (for now, just reject) 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 } // As always, having to support concrete methods with pointer // and non-pointer receivers, and named vs unnamed types with // methods, makes tooling fun. // ASSIGNABILITY // // For now, if any method renaming breaks a required // assignability to another type, we reject it. // // TODO(adonovan): probably we should compute the entailed // renamings so that an interface method renaming causes // concrete methods to change too. But which ones? // // There is no correct answer, only heuristics, because Go's // "duck typing" doesn't distinguish intentional from contingent // assignability. There are two obvious approaches: // // (1) Update the minimum set of types to preserve the // assignability of types all syntactic assignments // (incl. implicit ones in calls, returns, sends, etc). // The satisfy.Finder enumerates these. // This is likely to be an underapproximation. // // (2) Update all types that are assignable to/from the changed // type. This requires computing the "implements" relation // for all pairs of types (as godoc and oracle do). // This is likely to be an overapproximation. // // If a concrete type is renamed, we probably do not want to // rename corresponding interfaces; interface renamings should // probably be initiated at the interface. (But what if a // concrete type implements multiple interfaces with the same // method? Then the user is stuck.) // // We need some experience before we decide how to implement this. // Check for conflict at point of declaration. // Check to ensure preservation of assignability requirements. recv := from.Type().(*types.Signature).Recv().Type() if isInterface(recv) { // Abstract method // declaration prev, _, _ := types.LookupFieldOrMethod(recv, 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 for T := range r.findAssignments(recv) { if obj, _, _ := types.LookupFieldOrMethod(T, false, from.Pkg(), from.Name()); obj == nil { continue } r.errorf(from.Pos(), "renaming this method %q to %q", from.Name(), r.to) var pos token.Pos var other string if named, ok := T.(*types.Named); ok { pos = named.Obj().Pos() other = named.Obj().Name() } else { pos = from.Pos() other = T.String() } r.errorf(pos, "\twould make %s no longer assignable to it", other) return } } else { // Concrete method // declaration prev, indices, _ := types.LookupFieldOrMethod(recv, 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 (of both T and *T) recvBase := deref(recv) for _, R := range []types.Type{recvBase, types.NewPointer(recvBase)} { for I := range r.findAssignments(R) { if obj, _, _ := types.LookupFieldOrMethod(I, true, from.Pkg(), from.Name()); obj == nil { continue } r.errorf(from.Pos(), "renaming this method %q to %q", from.Name(), r.to) var pos token.Pos var iface string 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 it no longer assignable to %s", iface) return // one is enough } } } // Check integrity of existing (field and method) selections. // We skip this if there were errors above, to avoid redundant errors. r.checkSelections(from) }
func (g *cpyGen) genFuncBody(o *types.Func) { funcArgs := []string{} sig := o.Type().(*types.Signature) res := newVars(sig.Results()) args := newVars(sig.Params()) for _, arg := range args { arg.genDecl(g.impl) funcArgs = append(funcArgs, arg.getFuncArg()) } // FIXME(sbinet) pythonize (turn errors into python exceptions) if len(res) > 0 { switch len(res) { case 1: ret := res[0] ret.genRetDecl(g.impl) default: g.impl.Printf("struct %[1]s_return c_gopy_ret;\n", o.Name()) /* for i := 0; i < res.Len(); i++ { ret := res.At(i) n := ret.Name() if n == "" { n = "gopy_" + strconv.Itoa(i) } g.impl.Printf("%[1]s c_%[2]s;\n", ctypeName(ret.Type()), n) } */ } } g.impl.Printf("\n") if len(args) > 0 { g.impl.Printf("if (!PyArg_ParseTuple(args, ") format := []string{} pyaddrs := []string{} for _, arg := range args { pyfmt, addr := arg.getArgParse() format = append(format, pyfmt) pyaddrs = append(pyaddrs, addr) } g.impl.Printf("%q, %s)) {\n", strings.Join(format, ""), strings.Join(pyaddrs, ", ")) g.impl.Indent() g.impl.Printf("return NULL;\n") g.impl.Outdent() g.impl.Printf("}\n\n") } if len(args) > 0 { for _, arg := range args { arg.genFuncPreamble(g.impl) } g.impl.Printf("\n") } if len(res) > 0 { g.impl.Printf("c_gopy_ret = ") } g.impl.Printf("GoPy_%[1]s(%[2]s);\n", o.Name(), strings.Join(funcArgs, ", ")) g.impl.Printf("\n") if len(res) <= 0 { g.impl.Printf("Py_INCREF(Py_None);\nreturn Py_None;\n") return } format := []string{} funcArgs = []string{} switch len(res) { case 1: ret := res[0] pyfmt, _ := ret.getArgParse() format = append(format, pyfmt) funcArgs = append(funcArgs, "c_gopy_ret") default: for _, ret := range res { pyfmt, _ := ret.getArgParse() format = append(format, pyfmt) funcArgs = append(funcArgs, ret.getFuncArg()) } } g.impl.Printf("return Py_BuildValue(%q, %s);\n", strings.Join(format, ""), strings.Join(funcArgs, ", "), ) //g.impl.Printf("return NULL;\n") }
// 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 *Unexporter) checkMethod(objsToUpdate map[types.Object]string, from *types.Func, to string) { // e.g. error.Error if from.Pkg() == nil { r.warn(from, 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(), to) if prev != nil { r.warn(from, r.errorf(from.Pos(), "renaming this interface method %q to %q", from.Name(), 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(), to) if t == nil { continue } r.warn(from, r.errorf(from.Pos(), "renaming this interface method %q to %q", from.Name(), 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(), 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 rename := r.errorf(from.Pos(), "renaming this method %q to %q", from.Name(), to) if delta == 0 { // analogous to same-block conflict r.warn(from, rename, r.errorf(keyPos, "\twould make the %s method of %s invoked via interface %s ambiguous", to, key.RHS, key.LHS), r.errorf(rto.Pos(), "\twith (%s).%s", recv(rto).Type(), to)) } else { // analogous to super-block conflict r.warn(from, rename, r.errorf(keyPos, "\twould change the %s method of %s invoked via interface %s", to, key.RHS, key.LHS), r.errorf(coupled.Pos(), "\tfrom (%s).%s", recv(coupled).Type(), to), r.errorf(rto.Pos(), "\tto (%s).%s", recv(rto).Type(), to)) } return // one error is enough } } if !r.changeMethods { // This should be unreachable. r.warn(from, 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(objsToUpdate, coupled, to) } } else { // Concrete method // declaration prev, indices, _ := types.LookupFieldOrMethod(R, true, from.Pkg(), to) if prev != nil && len(indices) == 1 { r.warn(from, r.errorf(from.Pos(), "renaming this method %q to %q", from.Name(), 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 { rename := r.errorf(from.Pos(), "renaming this method %q to %q", from.Name(), 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.warn(from, rename, 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(objsToUpdate, imeth, to) } } // Check integrity of existing (field and method) selections. // We skip this if there were errors above, to avoid redundant errors. r.checkSelections(objsToUpdate, from, to) }
func (g *objcGen) funcSummary(obj *types.Func) *funcSummary { s := &funcSummary{name: obj.Name()} sig := obj.Type().(*types.Signature) params := sig.Params() for i := 0; i < params.Len(); i++ { p := params.At(i) v := paramInfo{ typ: p.Type(), name: paramName(params, i), } s.params = append(s.params, v) } res := sig.Results() switch res.Len() { case 0: s.ret = "void" case 1: p := res.At(0) if isErrorType(p.Type()) { s.retParams = append(s.retParams, paramInfo{ typ: p.Type(), name: "error", }) s.ret = "BOOL" } else { name := p.Name() if name == "" || paramRE.MatchString(name) { name = "ret0_" } typ := p.Type() s.retParams = append(s.retParams, paramInfo{typ: typ, name: name}) s.ret = g.objcType(typ) } case 2: name := res.At(0).Name() if name == "" || paramRE.MatchString(name) { name = "ret0_" } s.retParams = append(s.retParams, paramInfo{ typ: res.At(0).Type(), name: name, }) if !isErrorType(res.At(1).Type()) { g.errorf("second result value must be of type error: %s", obj) return nil } s.retParams = append(s.retParams, paramInfo{ typ: res.At(1).Type(), name: "error", // TODO(hyangah): name collision check. }) s.ret = "BOOL" default: // TODO(hyangah): relax the constraint on multiple return params. g.errorf("too many result values: %s", obj) return nil } return s }
// On success, findObjects returns the list of objects named // spec.fromName matching the spec. On success, the result has exactly // one element unless spec.searchFor!="", in which case it has at least one // element. // func findObjects(info *loader.PackageInfo, spec *spec) ([]types.Object, error) { if spec.pkgMember == "" { if spec.searchFor == "" { panic(spec) } objects := searchDefs(&info.Info, spec.searchFor) if objects == nil { return nil, fmt.Errorf("no object %q declared in package %q", spec.searchFor, info.Pkg.Path()) } return objects, nil } pkgMember := info.Pkg.Scope().Lookup(spec.pkgMember) if pkgMember == nil { return nil, fmt.Errorf("package %q has no member %q", info.Pkg.Path(), spec.pkgMember) } var searchFunc *types.Func if spec.typeMember == "" { // package member if spec.searchFor == "" { return []types.Object{pkgMember}, nil } // Search within pkgMember, which must be a function. searchFunc, _ = pkgMember.(*types.Func) if searchFunc == nil { return nil, fmt.Errorf("cannot search for %q within %s %q", spec.searchFor, objectKind(pkgMember), pkgMember) } } else { // field/method of type // e.g. (encoding/json.Decoder).Decode // or ::x within it. tName, _ := pkgMember.(*types.TypeName) if tName == nil { return nil, fmt.Errorf("%s.%s is a %s, not a type", info.Pkg.Path(), pkgMember.Name(), objectKind(pkgMember)) } // search within named type. obj, _, _ := types.LookupFieldOrMethod(tName.Type(), true, info.Pkg, spec.typeMember) if obj == nil { return nil, fmt.Errorf("cannot find field or method %q of %s %s.%s", spec.typeMember, typeKind(tName.Type()), info.Pkg.Path(), tName.Name()) } if spec.searchFor == "" { return []types.Object{obj}, nil } searchFunc, _ = obj.(*types.Func) if searchFunc == nil { return nil, fmt.Errorf("cannot search for local name %q within %s (%s.%s).%s; need a function", spec.searchFor, objectKind(obj), info.Pkg.Path(), tName.Name(), obj.Name()) } if isInterface(tName.Type()) { return nil, fmt.Errorf("cannot search for local name %q within abstract method (%s.%s).%s", spec.searchFor, info.Pkg.Path(), tName.Name(), searchFunc.Name()) } } // -- search within function or method -- decl := funcDecl(info, searchFunc) if decl == nil { return nil, fmt.Errorf("cannot find syntax for %s", searchFunc) // can't happen? } var objects []types.Object for _, obj := range searchDefs(&info.Info, spec.searchFor) { // We use positions, not scopes, to determine whether // the obj is within searchFunc. This is clumsy, but the // alternative, using the types.Scope tree, doesn't // account for non-lexical objects like fields and // interface methods. if decl.Pos() <= obj.Pos() && obj.Pos() < decl.End() && obj != searchFunc { objects = append(objects, obj) } } if objects == nil { return nil, fmt.Errorf("no local definition of %q within %s", spec.searchFor, searchFunc) } return objects, nil }
// Implements displays the "implements" relation as it pertains to the // selected type. // If the selection is a method, 'implements' displays // the corresponding methods of the types that would have been reported // by an implements query on the receiver type. // func implements(q *Query) error { lconf := loader.Config{Build: q.Build} allowErrors(&lconf) qpkg, err := importQueryPackage(q.Pos, &lconf) if err != nil { return err } // Set the packages to search. if len(q.Scope) > 0 { // Inspect all packages in the analysis scope, if specified. if err := setPTAScope(&lconf, q.Scope); err != nil { return err } } else { // Otherwise inspect the forward and reverse // transitive closure of the selected package. // (In theory even this is incomplete.) _, rev, _ := importgraph.Build(q.Build) for path := range rev.Search(qpkg) { lconf.ImportWithTests(path) } // TODO(adonovan): for completeness, we should also // type-check and inspect function bodies in all // imported packages. This would be expensive, but we // could optimize by skipping functions that do not // contain type declarations. This would require // changing the loader's TypeCheckFuncBodies hook to // provide the []*ast.File. } // Load/parse/type-check the program. lprog, err := lconf.Load() if err != nil { return err } q.Fset = lprog.Fset qpos, err := parseQueryPos(lprog, q.Pos, false) if err != nil { return err } // Find the selected type. path, action := findInterestingNode(qpos.info, qpos.path) var method *types.Func var T types.Type // selected type (receiver if method != nil) switch action { case actionExpr: // method? if id, ok := path[0].(*ast.Ident); ok { if obj, ok := qpos.info.ObjectOf(id).(*types.Func); ok { recv := obj.Type().(*types.Signature).Recv() if recv == nil { return fmt.Errorf("this function is not a method") } method = obj T = recv.Type() } } case actionType: T = qpos.info.TypeOf(path[0].(ast.Expr)) } if T == nil { return fmt.Errorf("no type or method here") } // Find all named types, even local types (which can have // methods via promotion) and the built-in "error". var allNamed []types.Type for _, info := range lprog.AllPackages { for _, obj := range info.Defs { if obj, ok := obj.(*types.TypeName); ok { allNamed = append(allNamed, obj.Type()) } } } allNamed = append(allNamed, types.Universe.Lookup("error").Type()) var msets typeutil.MethodSetCache // Test each named type. var to, from, fromPtr []types.Type for _, U := range allNamed { if isInterface(T) { if msets.MethodSet(T).Len() == 0 { continue // empty interface } if isInterface(U) { if msets.MethodSet(U).Len() == 0 { continue // empty interface } // T interface, U interface if !types.Identical(T, U) { if types.AssignableTo(U, T) { to = append(to, U) } if types.AssignableTo(T, U) { from = append(from, U) } } } else { // T interface, U concrete if types.AssignableTo(U, T) { to = append(to, U) } else if pU := types.NewPointer(U); types.AssignableTo(pU, T) { to = append(to, pU) } } } else if isInterface(U) { if msets.MethodSet(U).Len() == 0 { continue // empty interface } // T concrete, U interface if types.AssignableTo(T, U) { from = append(from, U) } else if pT := types.NewPointer(T); types.AssignableTo(pT, U) { fromPtr = append(fromPtr, U) } } } var pos interface{} = qpos if nt, ok := deref(T).(*types.Named); ok { pos = nt.Obj() } // Sort types (arbitrarily) to ensure test determinism. sort.Sort(typesByString(to)) sort.Sort(typesByString(from)) sort.Sort(typesByString(fromPtr)) var toMethod, fromMethod, fromPtrMethod []*types.Selection // contain nils if method != nil { for _, t := range to { toMethod = append(toMethod, types.NewMethodSet(t).Lookup(method.Pkg(), method.Name())) } for _, t := range from { fromMethod = append(fromMethod, types.NewMethodSet(t).Lookup(method.Pkg(), method.Name())) } for _, t := range fromPtr { fromPtrMethod = append(fromPtrMethod, types.NewMethodSet(t).Lookup(method.Pkg(), method.Name())) } } q.result = &implementsResult{ qpos, T, pos, to, from, fromPtr, method, toMethod, fromMethod, fromPtrMethod, } return nil }
func (e *Export) checkMethod(from *types.Func, to string) { // e.g. error.Error if from.Pkg() == nil { e.Conflicting = true 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(), to) if prev != nil { e.Conflicting = true return } // Check all interfaces that embed this one for // declaration conflicts too. for _, info := range e.u.prog.AllPackages { // 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(), to) if t == nil { continue } e.Conflicting = true return } } } // 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 e.u.satisfy() { // key = (lhs, rhs) where lhs is always an interface. lsel := e.u.msets.MethodSet(key.LHS).Lookup(from.Pkg(), from.Name()) if lsel == nil { continue } rmethods := e.u.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(), to) if rtosel != nil { delta := len(rsel.Index()) - len(rtosel.Index()) if delta < 0 { continue // no ambiguity } e.Conflicting = true return } } // Rename the coupled method to preserve assignability. e.check(coupled, to) } } // Concrete method // declaration prev, indices, _ := types.LookupFieldOrMethod(R, true, from.Pkg(), to) if prev != nil && len(indices) == 1 { e.Conflicting = true 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 e.u.satisfy() { // key = (lhs, rhs) where lhs is always an interface. if isInterface(key.RHS) { continue } rsel := e.u.msets.MethodSet(key.RHS).Lookup(from.Pkg(), from.Name()) if rsel == nil || rsel.Obj() != from { continue // rhs does not have the method } lsel := e.u.msets.MethodSet(key.LHS).Lookup(from.Pkg(), from.Name()) if lsel == nil { continue } imeth := lsel.Obj().(*types.Func) e.check(imeth, to) } // Check integrity of existing (field and method) selections. // We skip this if there were errors above, to avoid redundant errors. e.checkSelections(from, to) }
// Implements displays the "implements" relation as it pertains to the // selected type. If the selection is a method, 'implements' displays // the corresponding methods of the types that would have been reported // by an implements query on the receiver type. // func implements(o *Oracle, qpos *QueryPos) (queryResult, error) { // Find the selected type. path, action := findInterestingNode(qpos.info, qpos.path) var method *types.Func var T types.Type // selected type (receiver if method != nil) switch action { case actionExpr: // method? if id, ok := path[0].(*ast.Ident); ok { if obj, ok := qpos.info.ObjectOf(id).(*types.Func); ok { recv := obj.Type().(*types.Signature).Recv() if recv == nil { return nil, fmt.Errorf("this function is not a method") } method = obj T = recv.Type() } } case actionType: T = qpos.info.TypeOf(path[0].(ast.Expr)) } if T == nil { return nil, fmt.Errorf("no type or method here") } // Find all named types, even local types (which can have // methods via promotion) and the built-in "error". // // TODO(adonovan): include all packages in PTA scope too? // i.e. don't reduceScope? // var allNamed []types.Type for _, info := range o.typeInfo { for _, obj := range info.Defs { if obj, ok := obj.(*types.TypeName); ok { allNamed = append(allNamed, obj.Type()) } } } allNamed = append(allNamed, types.Universe.Lookup("error").Type()) var msets types.MethodSetCache // Test each named type. var to, from, fromPtr []types.Type for _, U := range allNamed { if isInterface(T) { if msets.MethodSet(T).Len() == 0 { continue // empty interface } if isInterface(U) { if msets.MethodSet(U).Len() == 0 { continue // empty interface } // T interface, U interface if !types.Identical(T, U) { if types.AssignableTo(U, T) { to = append(to, U) } if types.AssignableTo(T, U) { from = append(from, U) } } } else { // T interface, U concrete if types.AssignableTo(U, T) { to = append(to, U) } else if pU := types.NewPointer(U); types.AssignableTo(pU, T) { to = append(to, pU) } } } else if isInterface(U) { if msets.MethodSet(U).Len() == 0 { continue // empty interface } // T concrete, U interface if types.AssignableTo(T, U) { from = append(from, U) } else if pT := types.NewPointer(T); types.AssignableTo(pT, U) { fromPtr = append(fromPtr, U) } } } var pos interface{} = qpos if nt, ok := deref(T).(*types.Named); ok { pos = nt.Obj() } // Sort types (arbitrarily) to ensure test determinism. sort.Sort(typesByString(to)) sort.Sort(typesByString(from)) sort.Sort(typesByString(fromPtr)) var toMethod, fromMethod, fromPtrMethod []*types.Selection // contain nils if method != nil { for _, t := range to { toMethod = append(toMethod, types.NewMethodSet(t).Lookup(method.Pkg(), method.Name())) } for _, t := range from { fromMethod = append(fromMethod, types.NewMethodSet(t).Lookup(method.Pkg(), method.Name())) } for _, t := range fromPtr { fromPtrMethod = append(fromPtrMethod, types.NewMethodSet(t).Lookup(method.Pkg(), method.Name())) } } return &implementsResult{qpos, T, pos, to, from, fromPtr, method, toMethod, fromMethod, fromPtrMethod}, nil }