func (w *Walker) emitFunc(f *types.Func) { sig := f.Type().(*types.Signature) if sig.Recv() != nil { panic("method considered a regular function: " + f.String()) } w.emitf("func %s%s", f.Name(), w.signatureString(sig)) }
// FuncValue returns the Function denoted by the source-level named // function obj. // func (prog *Program) FuncValue(obj *types.Func) *Function { // Package-level function or declared method? if v := prog.packageLevelValue(obj); v != nil { return v.(*Function) } // Interface method wrapper? return prog.LookupMethod(recvType(obj), obj.Pkg(), obj.Name()) }
// promoteMethod promotes a named type's method to another type // which has embedded the named type. func (c *compiler) promoteMethod(m *types.Func, recv types.Type, indices []int) types.Object { var pkg *types.Package if recv, ok := recv.(*types.Named); ok { pkg = c.objectdata[recv.Obj()].Package } recvvar := types.NewVar(pkg, "", recv) sig := m.Type().(*types.Signature) sig = types.NewSignature(recvvar, sig.Params(), sig.Results(), sig.IsVariadic()) f := &synthFunc{pkg: pkg, name: m.Name(), typ: sig} ident := ast.NewIdent(f.Name()) var isptr bool if ptr, ok := recv.(*types.Pointer); ok { isptr = true recv = ptr.Elem() } c.objects[ident] = f c.objectdata[f] = &ObjectData{Ident: ident, Package: pkg} if pkg == nil || pkg == c.pkg { if currblock := c.builder.GetInsertBlock(); !currblock.IsNil() { defer c.builder.SetInsertPointAtEnd(currblock) } llvmfn := c.Resolve(ident).LLVMValue() llvmfn = c.builder.CreateExtractValue(llvmfn, 0, "") llvmfn.SetLinkage(llvm.LinkOnceODRLinkage) entry := llvm.AddBasicBlock(llvmfn, "entry") c.builder.SetInsertPointAtEnd(entry) realfn := c.Resolve(c.objectdata[m].Ident).LLVMValue() realfn = c.builder.CreateExtractValue(realfn, 0, "") args := llvmfn.Params() recvarg := args[0] if !isptr { ptr := c.builder.CreateAlloca(recvarg.Type(), "") c.builder.CreateStore(recvarg, ptr) recvarg = ptr } for _, i := range indices { if i == -1 { recvarg = c.builder.CreateLoad(recvarg, "") } else { recvarg = c.builder.CreateStructGEP(recvarg, i, "") } } args[0] = recvarg result := c.builder.CreateCall(realfn, args, "") if sig.Results().Len() == 0 { c.builder.CreateRetVoid() } else { c.builder.CreateRet(result) } } return f }
// 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 (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 (c *compiler) methodfunc(m *types.Func) *types.Func { // We're not privy to *Func objects for methods in // imported packages, so we must synthesise them. data := c.objectdata[m] if data == nil { ident := ast.NewIdent(m.Name()) data = &ObjectData{Ident: ident, Package: m.Pkg()} c.objects[ident] = m c.objectdata[m] = data } return m }
func (p *printer) printFunc(obj *types.Func) { p.print("func ") sig := obj.Type().(*types.Signature) if recv := sig.Recv(); recv != nil { p.print("(") if name := recv.Name(); name != "" { p.print(name) p.print(" ") } p.writeType(p.pkg, recv.Type()) p.print(") ") } p.print(obj.Name()) p.writeSignature(p.pkg, sig) }
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 }
// interfaceMethodWrapper returns a synthetic wrapper function // permitting an abstract method obj to be called like a standalone // function, e.g.: // // type I interface { f(x int) R } // m := I.f // wrapper // var i I // m(i, 0) // // The wrapper is defined as if by: // // func (i I) f(x int, ...) R { // return i.f(x, ...) // } // // typ is the type of the receiver (I here). It isn't necessarily // equal to the recvType(obj) because one interface may embed another. // TODO(adonovan): more tests. // // TODO(adonovan): opt: currently the stub is created even when used // in call position: I.f(i, 0). Clearly this is suboptimal. // // EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu) // func interfaceMethodWrapper(prog *Program, typ types.Type, obj *types.Func) *Function { // If one interface embeds another they'll share the same // wrappers for common methods. This is safe, but it might // confuse some tools because of the implicit interface // conversion applied to the first argument. If this becomes // a problem, we should include 'typ' in the memoization key. fn, ok := prog.ifaceMethodWrappers[obj] if !ok { description := "interface method wrapper" if prog.mode&LogSource != 0 { defer logStack("(%s).%s, %s", typ, obj.Name(), description)() } fn = &Function{ name: obj.Name(), object: obj, Signature: obj.Type().(*types.Signature), Synthetic: description, pos: obj.Pos(), Prog: prog, Pkg: prog.packages[obj.Pkg()], } fn.startBody() fn.addParam("recv", typ, token.NoPos) createParams(fn) var c Call c.Call.Method = obj c.Call.Value = fn.Params[0] for _, arg := range fn.Params[1:] { c.Call.Args = append(c.Call.Args, arg) } emitTailCall(fn, &c) fn.finishBody() prog.ifaceMethodWrappers[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 (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) }
// 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 }
func (c *compiler) VisitSelectorExpr(expr *ast.SelectorExpr) Value { // Imported package funcs/vars. if ident, ok := expr.X.(*ast.Ident); ok { if _, ok := c.objects[ident].(*types.Package); ok { return c.Resolve(expr.Sel) } } // Method expression. Returns an unbound function pointer. if c.isType(expr.X) { ftyp := c.types.expr[expr].Type.(*types.Signature) recvtyp := ftyp.Params().At(0).Type() var name *types.Named var isptr bool if ptrtyp, ok := recvtyp.(*types.Pointer); ok { isptr = true name = ptrtyp.Elem().(*types.Named) } else { name = recvtyp.(*types.Named) } obj := c.methods(name).lookup(expr.Sel.Name, isptr) method := c.Resolve(c.objectdata[obj].Ident).(*LLVMValue) return c.NewValue(method.value, ftyp) } // Interface: search for method by name. lhs := c.VisitExpr(expr.X) name := expr.Sel.Name if iface, ok := lhs.Type().Underlying().(*types.Interface); ok { // FIXME //i := sort.Search(len(iface.Methods), func(i int) bool { // return iface.Methods[i].Name >= name //}) var i int var m *types.Func for ; i < iface.NumMethods(); i++ { m = iface.Method(i) if m.Name() == name { break } } structValue := lhs.LLVMValue() receiver := c.builder.CreateExtractValue(structValue, 1, "") f := c.builder.CreateExtractValue(structValue, i+2, "") ftype := m.Type() types := []llvm.Type{f.Type(), receiver.Type()} llvmStructType := llvm.StructType(types, false) structValue = llvm.Undef(llvmStructType) structValue = c.builder.CreateInsertValue(structValue, f, 0, "") structValue = c.builder.CreateInsertValue(structValue, receiver, 1, "") return c.NewValue(structValue, ftype) } // Method. if typ, ok := c.types.expr[expr].Type.(*types.Signature); ok && typ.Recv() != nil { var isptr bool typ := lhs.Type() if ptr, ok := typ.(*types.Pointer); ok { typ = ptr.Elem() isptr = true } else { isptr = lhs.(*LLVMValue).pointer != nil } method := c.methods(typ).lookup(name, isptr) if method != nil { recv := lhs.(*LLVMValue) if isptr && typ == lhs.Type() { recv = recv.pointer } if f, ok := method.(*types.Func); ok { method = c.methodfunc(f) } methodValue := c.Resolve(c.objectdata[method].Ident).LLVMValue() methodValue = c.builder.CreateExtractValue(methodValue, 0, "") recvValue := recv.LLVMValue() types := []llvm.Type{methodValue.Type(), recvValue.Type()} structType := llvm.StructType(types, false) value := llvm.Undef(structType) value = c.builder.CreateInsertValue(value, methodValue, 0, "") value = c.builder.CreateInsertValue(value, recvValue, 1, "") return c.NewValue(value, method.Type()) } } // Otherwise, search for field, recursing through embedded types. var result selectorCandidate curr := []selectorCandidate{{nil, lhs.Type()}} for result.Type == nil && len(curr) > 0 { var next []selectorCandidate for _, candidate := range curr { indices := candidate.Indices[:] t := candidate.Type if ptr, ok := t.(*types.Pointer); ok { indices = append(indices, -1) t = ptr.Elem() } if t, ok := t.Underlying().(*types.Struct); ok { if i := fieldIndex(t, name); i != -1 { result.Indices = append(indices, i) result.Type = t.Field(i).Type break } else { // Add embedded types to the next set of types to check. for i := 0; i < t.NumFields(); i++ { field := t.Field(i) if field.IsAnonymous { indices := append(indices[:], i) t := field.Type candidate := selectorCandidate{indices, t} next = append(next, candidate) } } } } } curr = next } // Get a pointer to the field. fieldValue := lhs.(*LLVMValue) if len(result.Indices) > 0 { if fieldValue.pointer == nil { // If we've got a temporary (i.e. no pointer), // then load the value onto the stack. v := fieldValue.value stackptr := c.builder.CreateAlloca(v.Type(), "") c.builder.CreateStore(v, stackptr) ptrtyp := types.NewPointer(fieldValue.Type()) fieldValue = c.NewValue(stackptr, ptrtyp).makePointee() } for _, i := range result.Indices { if i == -1 { fieldValue = fieldValue.makePointee() } else { ptr := fieldValue.pointer.LLVMValue() structTyp := fieldValue.typ.Deref().Underlying().(*types.Struct) field := structTyp.Field(i) fieldPtr := c.builder.CreateStructGEP(ptr, i, "") fieldPtrTyp := types.NewPointer(field.Type.(types.Type)) fieldValue = c.NewValue(fieldPtr, fieldPtrTyp).makePointee() } } } return fieldValue }