func describeType(o *Oracle, qpos *QueryPos, path []ast.Node) (*describeTypeResult, error) { var description string var t types.Type switch n := path[0].(type) { case *ast.Ident: t = qpos.info.TypeOf(n) switch t := t.(type) { case *types.Basic: description = "reference to built-in type " + t.String() case *types.Named: isDef := t.Obj().Pos() == n.Pos() // see caveats at isDef above if isDef { description = "definition of type " + t.String() } else { description = "reference to type " + t.String() } } case ast.Expr: t = qpos.info.TypeOf(n) description = "type " + t.String() default: // Unreachable? return nil, fmt.Errorf("unexpected AST for type: %T", n) } return &describeTypeResult{ node: path[0], description: description, typ: t, methods: accessibleMethods(t, qpos.info.Pkg), }, nil }
// 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 }
// hashFor computes the hash of t. func (h Hasher) hashFor(t types.Type) uint32 { // See IsIdentical for rationale. switch t := t.(type) { case *types.Basic: return uint32(t.Kind()) case *types.Array: return 9043 + 2*uint32(t.Len()) + 3*h.Hash(t.Elem()) case *types.Slice: return 9049 + 2*h.Hash(t.Elem()) case *types.Struct: var hash uint32 = 9059 for i, n := 0, t.NumFields(); i < n; i++ { f := t.Field(i) if f.Anonymous() { hash += 8861 } hash += hashString(t.Tag(i)) hash += hashString(f.Name()) // (ignore f.Pkg) hash += h.Hash(f.Type()) } return hash case *types.Pointer: return 9067 + 2*h.Hash(t.Elem()) case *types.Signature: var hash uint32 = 9091 if t.IsVariadic() { hash *= 8863 } return hash + 3*h.hashTuple(t.Params()) + 5*h.hashTuple(t.Results()) case *types.Interface: var hash uint32 = 9103 for i, n := 0, t.NumMethods(); i < n; i++ { // See go/types.identicalMethods for rationale. // Method order is not significant. // Ignore m.Pkg(). m := t.Method(i) hash += 3*hashString(m.Name()) + 5*h.Hash(m.Type()) } return hash case *types.Map: return 9109 + 2*h.Hash(t.Key()) + 3*h.Hash(t.Elem()) case *types.Chan: return 9127 + 2*uint32(t.Dir()) + 3*h.Hash(t.Elem()) case *types.Named: // Not safe with a copying GC; objects may move. return uint32(uintptr(unsafe.Pointer(t.Obj()))) } panic("unexpected type") }
// CanHaveDynamicTypes reports whether the type T can "hold" dynamic types, // i.e. is an interface (incl. reflect.Type) or a reflect.Value. // func CanHaveDynamicTypes(T types.Type) bool { switch T := T.(type) { case *types.Named: if obj := T.Obj(); obj.Name() == "Value" && obj.Pkg().Path() == "reflect" { return true // reflect.Value } return CanHaveDynamicTypes(T.Underlying()) case *types.Interface: return true } return false }
// CanPoint reports whether the type T is pointerlike, // for the purposes of this analysis. func CanPoint(T types.Type) bool { switch T := T.(type) { case *types.Named: if obj := T.Obj(); obj.Name() == "Value" && obj.Pkg().Path() == "reflect" { return true // treat reflect.Value like interface{} } return CanPoint(T.Underlying()) case *types.Pointer, *types.Interface, *types.Map, *types.Chan, *types.Signature, *types.Slice: return true } return false // array struct tuple builtin basic }
func describeType(o *Oracle, qpos *QueryPos, path []ast.Node) (*describeTypeResult, error) { var description string var t types.Type switch n := path[0].(type) { case *ast.Ident: t = qpos.info.TypeOf(n) switch t := t.(type) { case *types.Basic: description = "reference to built-in " case *types.Named: isDef := t.Obj().Pos() == n.Pos() // see caveats at isDef above if isDef { description = "definition of " } else { description = "reference to " } } case ast.Expr: t = qpos.info.TypeOf(n) default: // Unreachable? return nil, fmt.Errorf("unexpected AST for type: %T", n) } description = description + "type " + qpos.TypeString(t) // Show sizes for structs and named types (it's fairly obvious for others). switch t.(type) { case *types.Named, *types.Struct: // TODO(adonovan): use o.imp.Config().TypeChecker.Sizes when // we add the Config() method (needs some thought). szs := types.StdSizes{8, 8} description = fmt.Sprintf("%s (size %d, align %d)", description, szs.Sizeof(t), szs.Alignof(t)) } return &describeTypeResult{ qpos: qpos, node: path[0], description: description, typ: t, methods: accessibleMethods(t, qpos.info.Pkg), }, nil }
// 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, methodIndex int, recv types.Type, indices []int) types.Object { m := iface.Method(methodIndex) 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) 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 (w *Walker) writeType(buf *bytes.Buffer, typ types.Type) { switch typ := typ.(type) { case *types.Basic: s := typ.Name() switch typ.Kind() { case types.UnsafePointer: s = "unsafe.Pointer" case types.UntypedBool: s = "ideal-bool" case types.UntypedInt: s = "ideal-int" case types.UntypedRune: // "ideal-char" for compatibility with old tool // TODO(gri) change to "ideal-rune" s = "ideal-char" case types.UntypedFloat: s = "ideal-float" case types.UntypedComplex: s = "ideal-complex" case types.UntypedString: s = "ideal-string" case types.UntypedNil: panic("should never see untyped nil type") default: switch s { case "byte": s = "uint8" case "rune": s = "int32" } } buf.WriteString(s) case *types.Array: fmt.Fprintf(buf, "[%d]", typ.Len()) w.writeType(buf, typ.Elem()) case *types.Slice: buf.WriteString("[]") w.writeType(buf, typ.Elem()) case *types.Struct: buf.WriteString("struct") case *types.Pointer: buf.WriteByte('*') w.writeType(buf, typ.Elem()) case *types.Tuple: panic("should never see a tuple type") case *types.Signature: buf.WriteString("func") w.writeSignature(buf, typ) case *types.Interface: buf.WriteString("interface{") if typ.NumMethods() > 0 { buf.WriteByte(' ') buf.WriteString(strings.Join(sortedMethodNames(typ), ", ")) buf.WriteByte(' ') } buf.WriteString("}") case *types.Map: buf.WriteString("map[") w.writeType(buf, typ.Key()) buf.WriteByte(']') w.writeType(buf, typ.Elem()) case *types.Chan: var s string switch typ.Dir() { case ast.SEND: s = "chan<- " case ast.RECV: s = "<-chan " default: s = "chan " } buf.WriteString(s) w.writeType(buf, typ.Elem()) case *types.Named: obj := typ.Obj() pkg := obj.Pkg() if pkg != nil && pkg != w.current { buf.WriteString(pkg.Name()) buf.WriteByte('.') } buf.WriteString(typ.Obj().Name()) default: panic(fmt.Sprintf("unknown type %T", typ)) } }