// Copyright 2013 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package ssa // This file defines a number of miscellaneous utility functions. import ( "fmt" "go/ast" "go/token" "go/types" "io" "os" "sync" "golang.org/x/tools/go/ast/astutil" "golang.org/x/tools/go/types/typeutil" "golang.org/x/tools/internal/typeparams" ) //// Sanity checking utilities // assert panics with the mesage msg if p is false. // Avoid combining with expensive string formatting. func assert(p bool, msg string) { if !p { panic(msg) } } //// AST utilities func unparen(e ast.Expr) ast.Expr { return astutil.Unparen(e) } // isBlankIdent returns true iff e is an Ident with name "_". // They have no associated types.Object, and thus no type. func isBlankIdent(e ast.Expr) bool { id, ok := e.(*ast.Ident) return ok && id.Name == "_" } //// Type utilities. Some of these belong in go/types. // isPointer returns true for types whose underlying type is a pointer. func isPointer(typ types.Type) bool { _, ok := typ.Underlying().(*types.Pointer) return ok } // isNonTypeParamInterface reports whether t is an interface type but not a type parameter. func isNonTypeParamInterface(t types.Type) bool { return !typeparams.IsTypeParam(t) && types.IsInterface(t) } // isBasic reports whether t is a basic type. func isBasic(t types.Type) bool { _, ok := t.(*types.Basic) return ok } // isString reports whether t is exactly a string type. func isString(t types.Type) bool { return isBasic(t) && t.(*types.Basic).Info()&types.IsString != 0 } // isByteSlice reports whether t is []byte. func isByteSlice(t types.Type) bool { if b, ok := t.(*types.Slice); ok { e, _ := b.Elem().(*types.Basic) return e != nil && e.Kind() == types.Byte } return false } // isRuneSlice reports whether t is []rune. func isRuneSlice(t types.Type) bool { if b, ok := t.(*types.Slice); ok { e, _ := b.Elem().(*types.Basic) return e != nil && e.Kind() == types.Rune } return false } // isBasicConvType returns true when a type set can be // one side of a Convert operation. This is when: // - All are basic, []byte, or []rune. // - At least 1 is basic. // - At most 1 is []byte or []rune. func isBasicConvTypes(tset termList) bool { basics := 0 all := underIs(tset, func(t types.Type) bool { if isBasic(t) { basics++ return true } return isByteSlice(t) || isRuneSlice(t) }) return all && basics >= 1 && tset.Len()-basics <= 1 } // deref returns a pointer's element type; otherwise it returns typ. func deref(typ types.Type) types.Type { if p, ok := typ.Underlying().(*types.Pointer); ok { return p.Elem() } return typ } // recvType returns the receiver type of method obj. func recvType(obj *types.Func) types.Type { return obj.Type().(*types.Signature).Recv().Type() } // isUntyped returns true for types that are untyped. func isUntyped(typ types.Type) bool { b, ok := typ.(*types.Basic) return ok && b.Info()&types.IsUntyped != 0 } // logStack prints the formatted "start" message to stderr and // returns a closure that prints the corresponding "end" message. // Call using 'defer logStack(...)()' to show builder stack on panic. // Don't forget trailing parens! func logStack(format string, args ...interface{}) func() { msg := fmt.Sprintf(format, args...) io.WriteString(os.Stderr, msg) io.WriteString(os.Stderr, "\n") return func() { io.WriteString(os.Stderr, msg) io.WriteString(os.Stderr, " end\n") } } // newVar creates a 'var' for use in a types.Tuple. func newVar(name string, typ types.Type) *types.Var { return types.NewParam(token.NoPos, nil, name, typ) } // anonVar creates an anonymous 'var' for use in a types.Tuple. func anonVar(typ types.Type) *types.Var { return newVar("", typ) } var lenResults = types.NewTuple(anonVar(tInt)) // makeLen returns the len builtin specialized to type func(T)int. func makeLen(T types.Type) *Builtin { lenParams := types.NewTuple(anonVar(T)) return &Builtin{ name: "len", sig: types.NewSignature(nil, lenParams, lenResults, false), } } // nonbasicTypes returns a list containing all of the types T in ts that are non-basic. func nonbasicTypes(ts []types.Type) []types.Type { if len(ts) == 0 { return nil } added := make(map[types.Type]bool) // additionally filter duplicates var filtered []types.Type for _, T := range ts { if !isBasic(T) { if !added[T] { added[T] = true filtered = append(filtered, T) } } } return filtered } // receiverTypeArgs returns the type arguments to a function's reciever. // Returns an empty list if obj does not have a reciever or its reciever does not have type arguments. func receiverTypeArgs(obj *types.Func) []types.Type { rtype := recvType(obj) if rtype == nil { return nil } if isPointer(rtype) { rtype = rtype.(*types.Pointer).Elem() } named, ok := rtype.(*types.Named) if !ok { return nil } ts := typeparams.NamedTypeArgs(named) if ts.Len() == 0 { return nil } targs := make([]types.Type, ts.Len()) for i := 0; i < ts.Len(); i++ { targs[i] = ts.At(i) } return targs } // recvAsFirstArg takes a method signature and returns a function // signature with receiver as the first parameter. func recvAsFirstArg(sig *types.Signature) *types.Signature { params := make([]*types.Var, 0, 1+sig.Params().Len()) params = append(params, sig.Recv()) for i := 0; i < sig.Params().Len(); i++ { params = append(params, sig.Params().At(i)) } return typeparams.NewSignatureType(nil, nil, nil, types.NewTuple(params...), sig.Results(), sig.Variadic()) } // instance returns whether an expression is a simple or qualified identifier // that is a generic instantiation. func instance(info *types.Info, expr ast.Expr) bool { // Compare the logic here against go/types.instantiatedIdent, // which also handles *IndexExpr and *IndexListExpr. var id *ast.Ident switch x := expr.(type) { case *ast.Ident: id = x case *ast.SelectorExpr: id = x.Sel default: return false } _, ok := typeparams.GetInstances(info)[id] return ok } // instanceArgs returns the Instance[id].TypeArgs as a slice. func instanceArgs(info *types.Info, id *ast.Ident) []types.Type { targList := typeparams.GetInstances(info)[id].TypeArgs if targList == nil { return nil } targs := make([]types.Type, targList.Len()) for i, n := 0, targList.Len(); i < n; i++ { targs[i] = targList.At(i) } return targs } // Mapping of a type T to a canonical instance C s.t. types.Indentical(T, C). // Thread-safe. type canonizer struct { mu sync.Mutex types typeutil.Map // map from type to a canonical instance lists typeListMap // map from a list of types to a canonical instance } func newCanonizer() *canonizer { c := &canonizer{} h := typeutil.MakeHasher() c.types.SetHasher(h) c.lists.hasher = h return c } // List returns a canonical representative of a list of types. // Representative of the empty list is nil. func (c *canonizer) List(ts []types.Type) *typeList { if len(ts) == 0 { return nil } c.mu.Lock() defer c.mu.Unlock() return c.lists.rep(ts) } // Type returns a canonical representative of type T. func (c *canonizer) Type(T types.Type) types.Type { c.mu.Lock() defer c.mu.Unlock() if r := c.types.At(T); r != nil { return r.(types.Type) } c.types.Set(T, T) return T } // A type for representating an canonized list of types. type typeList []types.Type func (l *typeList) identical(ts []types.Type) bool { if l == nil { return len(ts) == 0 } n := len(*l) if len(ts) != n { return false } for i, left := range *l { right := ts[i] if !types.Identical(left, right) { return false } } return true } type typeListMap struct { hasher typeutil.Hasher buckets map[uint32][]*typeList } // rep returns a canonical representative of a slice of types. func (m *typeListMap) rep(ts []types.Type) *typeList { if m == nil || len(ts) == 0 { return nil } if m.buckets == nil { m.buckets = make(map[uint32][]*typeList) } h := m.hash(ts) bucket := m.buckets[h] for _, l := range bucket { if l.identical(ts) { return l } } // not present. create a representative. cp := make(typeList, len(ts)) copy(cp, ts) rep := &cp m.buckets[h] = append(bucket, rep) return rep } func (m *typeListMap) hash(ts []types.Type) uint32 { if m == nil { return 0 } // Some smallish prime far away from typeutil.Hash. n := len(ts) h := uint32(13619) + 2*uint32(n) for i := 0; i < n; i++ { h += 3 * m.hasher.Hash(ts[i]) } return h } // instantiateMethod instantiates m with targs and returns a canonical representative for this method. func (canon *canonizer) instantiateMethod(m *types.Func, targs []types.Type, ctxt *typeparams.Context) *types.Func { recv := recvType(m) if p, ok := recv.(*types.Pointer); ok { recv = p.Elem() } named := recv.(*types.Named) inst, err := typeparams.Instantiate(ctxt, typeparams.NamedTypeOrigin(named), targs, false) if err != nil { panic(err) } rep := canon.Type(inst) obj, _, _ := types.LookupFieldOrMethod(rep, true, m.Pkg(), m.Name()) return obj.(*types.Func) }