// interfaceMethod returns a function pointer for the specified // interface and method pair. func (c *compiler) interfaceMethod(iface *LLVMValue, method *types.Func) *LLVMValue { lliface := iface.LLVMValue() llitab := c.builder.CreateExtractValue(lliface, 0, "") llvalue := c.builder.CreateExtractValue(lliface, 1, "") sig := method.Type().(*types.Signature) methodset := c.types.MethodSet(sig.Recv().Type()) // TODO(axw) cache ordered method index var index int for i := 0; i < methodset.Len(); i++ { if methodset.At(i).Obj() == method { index = i break } } llitab = c.builder.CreateBitCast(llitab, llvm.PointerType(c.runtime.itab.llvm, 0), "") llifn := c.builder.CreateGEP(llitab, []llvm.Value{ llvm.ConstInt(llvm.Int32Type(), 0, false), llvm.ConstInt(llvm.Int32Type(), 5, false), // index of itab.fun }, "") _ = index llifn = c.builder.CreateGEP(llifn, []llvm.Value{ llvm.ConstInt(llvm.Int32Type(), uint64(index), false), }, "") llifn = c.builder.CreateLoad(llifn, "") // Strip receiver. sig = types.NewSignature(nil, nil, sig.Params(), sig.Results(), sig.Variadic()) llfn := llvm.Undef(c.types.ToLLVM(sig)) llifn = c.builder.CreateIntToPtr(llifn, llfn.Type().StructElementTypes()[0], "") llfn = c.builder.CreateInsertValue(llfn, llifn, 0, "") llfn = c.builder.CreateInsertValue(llfn, llvalue, 1, "") return c.NewValue(llfn, sig) }
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)) }
// 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 }
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 (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 { 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 } }
// boundMethodWrapper returns a synthetic wrapper function that // delegates to a concrete or interface method. // The wrapper has one free variable, the method's receiver. // 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() } // // EXCLUSIVE_LOCKS_ACQUIRED(meth.Prog.methodsMu) // func boundMethodWrapper(prog *Program, obj *types.Func) *Function { prog.methodsMu.Lock() defer prog.methodsMu.Unlock() fn, ok := prog.boundMethodWrappers[obj] if !ok { description := fmt.Sprintf("bound method wrapper for %s", obj) if prog.mode&LogSource != 0 { defer logStack("%s", description)() } fn = &Function{ name: "bound$" + obj.FullName(), Signature: changeRecv(obj.Type().(*types.Signature), nil), // drop receiver Synthetic: description, Prog: prog, Pkg: prog.packages[obj.Pkg()], pos: obj.Pos(), } cap := &Capture{name: "recv", typ: recvType(obj), parent: fn} fn.FreeVars = []*Capture{cap} fn.startBody() createParams(fn) var c Call if _, ok := recvType(obj).Underlying().(*types.Interface); !ok { // concrete c.Call.Value = prog.declaredFunc(obj) c.Call.Args = []Value{cap} } else { c.Call.Value = cap c.Call.Method = obj } for _, arg := range fn.Params { c.Call.Args = append(c.Call.Args, arg) } emitTailCall(fn, &c) fn.finishBody() prog.boundMethodWrappers[obj] = fn } return fn }
// 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 }
// recvType returns the receiver type of method obj. func recvType(obj *types.Func) types.Type { return obj.Type().(*types.Signature).Recv().Type() }
// 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) }
// promoteInterfaceMethod promotes an interface method to a type // which has embedded the interface. // // TODO consolidate this and promoteMethod. func (c *compiler) promoteInterfaceMethod(iface *types.Interface, m *types.Func, methodIndex int, recv types.Type, indices []int) types.Object { var pkg *types.Package if recv, ok := recv.(*types.Named); ok { pkg = recv.Obj().Pkg() } recvvar := types.NewVar(token.NoPos, pkg, "", recv) sig := m.Type().(*types.Signature) sig = types.NewSignature(nil, recvvar, sig.Params(), sig.Results(), sig.IsVariadic()) f := &synthFunc{Func: m, pkg: pkg, typ: sig} ident := ast.NewIdent(f.Name()) var isptr bool if ptr, ok := recv.(*types.Pointer); ok { isptr = true recv = ptr.Elem() } c.typeinfo.Objects[ident] = f c.objectdata[f] = &ObjectData{Ident: ident} 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) args := llvmfn.Params() ifaceval := args[0] if !isptr { ptr := c.builder.CreateAlloca(ifaceval.Type(), "") c.builder.CreateStore(ifaceval, ptr) ifaceval = ptr } for _, i := range indices { if i == -1 { ifaceval = c.builder.CreateLoad(ifaceval, "") } else { ifaceval = c.builder.CreateStructGEP(ifaceval, i, "") } } recvarg := c.builder.CreateExtractValue(ifaceval, 0, "") ifn := c.builder.CreateExtractValue(ifaceval, methodIndex+2, "") // Add the receiver argument type. fntyp := ifn.Type().ElementType() returnType := fntyp.ReturnType() paramTypes := fntyp.ParamTypes() paramTypes = append([]llvm.Type{recvarg.Type()}, paramTypes...) vararg := fntyp.IsFunctionVarArg() fntyp = llvm.FunctionType(returnType, paramTypes, vararg) fnptrtyp := llvm.PointerType(fntyp, 0) ifn = c.builder.CreateBitCast(ifn, fnptrtyp, "") args[0] = recvarg result := c.builder.CreateCall(ifn, args, "") if sig.Results().Len() == 0 { c.builder.CreateRetVoid() } else { c.builder.CreateRet(result) } } return f }
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 }