src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

util.go (10614B)


      1 // Copyright 2013 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 package ssa
      6 
      7 // This file defines a number of miscellaneous utility functions.
      8 
      9 import (
     10 	"fmt"
     11 	"go/ast"
     12 	"go/token"
     13 	"go/types"
     14 	"io"
     15 	"os"
     16 	"sync"
     17 	_ "unsafe" // for go:linkname hack
     18 
     19 	"golang.org/x/tools/go/types/typeutil"
     20 	"golang.org/x/tools/internal/typeparams"
     21 	"golang.org/x/tools/internal/typesinternal"
     22 )
     23 
     24 type unit struct{}
     25 
     26 //// Sanity checking utilities
     27 
     28 // assert panics with the message msg if p is false.
     29 // Avoid combining with expensive string formatting.
     30 func assert(p bool, msg string) {
     31 	if !p {
     32 		panic(msg)
     33 	}
     34 }
     35 
     36 //// AST utilities
     37 
     38 // isBlankIdent returns true iff e is an Ident with name "_".
     39 // They have no associated types.Object, and thus no type.
     40 func isBlankIdent(e ast.Expr) bool {
     41 	id, ok := e.(*ast.Ident)
     42 	return ok && id.Name == "_"
     43 }
     44 
     45 //// Type utilities.  Some of these belong in go/types.
     46 
     47 // isNonTypeParamInterface reports whether t is an interface type but not a type parameter.
     48 func isNonTypeParamInterface(t types.Type) bool {
     49 	return !typeparams.IsTypeParam(t) && types.IsInterface(t)
     50 }
     51 
     52 // isBasic reports whether t is a basic type.
     53 // t is assumed to be an Underlying type (not Named or Alias).
     54 func isBasic(t types.Type) bool {
     55 	_, ok := t.(*types.Basic)
     56 	return ok
     57 }
     58 
     59 // isString reports whether t is exactly a string type.
     60 // t is assumed to be an Underlying type (not Named or Alias).
     61 func isString(t types.Type) bool {
     62 	basic, ok := t.(*types.Basic)
     63 	return ok && basic.Info()&types.IsString != 0
     64 }
     65 
     66 // isByteSlice reports whether t is of the form []~bytes.
     67 // t is assumed to be an Underlying type (not Named or Alias).
     68 func isByteSlice(t types.Type) bool {
     69 	if b, ok := t.(*types.Slice); ok {
     70 		e, _ := b.Elem().Underlying().(*types.Basic)
     71 		return e != nil && e.Kind() == types.Byte
     72 	}
     73 	return false
     74 }
     75 
     76 // isRuneSlice reports whether t is of the form []~runes.
     77 // t is assumed to be an Underlying type (not Named or Alias).
     78 func isRuneSlice(t types.Type) bool {
     79 	if b, ok := t.(*types.Slice); ok {
     80 		e, _ := b.Elem().Underlying().(*types.Basic)
     81 		return e != nil && e.Kind() == types.Rune
     82 	}
     83 	return false
     84 }
     85 
     86 // isBasicConvTypes returns true when the type set of a type
     87 // can be one side of a Convert operation. This is when:
     88 // - All are basic, []byte, or []rune.
     89 // - At least 1 is basic.
     90 // - At most 1 is []byte or []rune.
     91 func isBasicConvTypes(typ types.Type) bool {
     92 	basics, cnt := 0, 0
     93 	ok := underIs(typ, func(t types.Type) bool {
     94 		cnt++
     95 		if isBasic(t) {
     96 			basics++
     97 			return true
     98 		}
     99 		return isByteSlice(t) || isRuneSlice(t)
    100 	})
    101 	return ok && basics >= 1 && cnt-basics <= 1
    102 }
    103 
    104 // isPointer reports whether t's underlying type is a pointer.
    105 func isPointer(t types.Type) bool {
    106 	return is[*types.Pointer](t.Underlying())
    107 }
    108 
    109 // isPointerCore reports whether t's core type is a pointer.
    110 //
    111 // (Most pointer manipulation is related to receivers, in which case
    112 // isPointer is appropriate. tecallers can use isPointer(t).
    113 func isPointerCore(t types.Type) bool {
    114 	return is[*types.Pointer](typeparams.CoreType(t))
    115 }
    116 
    117 func is[T any](x any) bool {
    118 	_, ok := x.(T)
    119 	return ok
    120 }
    121 
    122 // recvType returns the receiver type of method obj.
    123 func recvType(obj *types.Func) types.Type {
    124 	return obj.Signature().Recv().Type()
    125 }
    126 
    127 // fieldOf returns the index'th field of the (core type of) a struct type;
    128 // otherwise returns nil.
    129 func fieldOf(typ types.Type, index int) *types.Var {
    130 	if st, ok := typeparams.CoreType(typ).(*types.Struct); ok {
    131 		if 0 <= index && index < st.NumFields() {
    132 			return st.Field(index)
    133 		}
    134 	}
    135 	return nil
    136 }
    137 
    138 // isUntyped reports whether typ is the type of an untyped constant.
    139 func isUntyped(typ types.Type) bool {
    140 	// No Underlying/Unalias: untyped constant types cannot be Named or Alias.
    141 	b, ok := typ.(*types.Basic)
    142 	return ok && b.Info()&types.IsUntyped != 0
    143 }
    144 
    145 // declaredWithin reports whether an object is declared within a function.
    146 //
    147 // obj must not be a method or a field.
    148 func declaredWithin(obj types.Object, fn *types.Func) bool {
    149 	if obj.Pos() != token.NoPos {
    150 		return fn.Scope().Contains(obj.Pos()) // trust the positions if they exist.
    151 	}
    152 	if fn.Pkg() != obj.Pkg() {
    153 		return false // fast path for different packages
    154 	}
    155 
    156 	// Traverse Parent() scopes for fn.Scope().
    157 	for p := obj.Parent(); p != nil; p = p.Parent() {
    158 		if p == fn.Scope() {
    159 			return true
    160 		}
    161 	}
    162 	return false
    163 }
    164 
    165 // logStack prints the formatted "start" message to stderr and
    166 // returns a closure that prints the corresponding "end" message.
    167 // Call using 'defer logStack(...)()' to show builder stack on panic.
    168 // Don't forget trailing parens!
    169 func logStack(format string, args ...any) func() {
    170 	msg := fmt.Sprintf(format, args...)
    171 	io.WriteString(os.Stderr, msg)
    172 	io.WriteString(os.Stderr, "\n")
    173 	return func() {
    174 		io.WriteString(os.Stderr, msg)
    175 		io.WriteString(os.Stderr, " end\n")
    176 	}
    177 }
    178 
    179 // newVar creates a 'var' for use in a types.Tuple.
    180 func newVar(name string, typ types.Type) *types.Var {
    181 	return types.NewParam(token.NoPos, nil, name, typ)
    182 }
    183 
    184 var lenResults = typesinternal.TupleOf(tInt)
    185 
    186 // makeLen returns the len builtin specialized to type func(T)int.
    187 func makeLen(T types.Type) *Builtin {
    188 	return &Builtin{
    189 		name: "len",
    190 		sig:  types.NewSignatureType(nil, nil, nil, typesinternal.TupleOf(T), lenResults, false),
    191 	}
    192 }
    193 
    194 // receiverTypeArgs returns the type arguments to a method's receiver.
    195 // Returns an empty list if the receiver does not have type arguments.
    196 func receiverTypeArgs(method *types.Func) []types.Type {
    197 	recv := method.Signature().Recv()
    198 	_, named := typesinternal.ReceiverNamed(recv)
    199 	if named == nil {
    200 		return nil // recv is anonymous struct/interface
    201 	}
    202 	ts := named.TypeArgs()
    203 	if ts.Len() == 0 {
    204 		return nil
    205 	}
    206 	targs := make([]types.Type, ts.Len())
    207 	for i := 0; i < ts.Len(); i++ {
    208 		targs[i] = ts.At(i)
    209 	}
    210 	return targs
    211 }
    212 
    213 // recvAsFirstArg takes a method signature and returns a function
    214 // signature with receiver as the first parameter.
    215 func recvAsFirstArg(sig *types.Signature) *types.Signature {
    216 	params := make([]*types.Var, 0, 1+sig.Params().Len())
    217 	params = append(params, sig.Recv())
    218 	for v := range sig.Params().Variables() {
    219 		params = append(params, v)
    220 	}
    221 	return types.NewSignatureType(nil, nil, nil, types.NewTuple(params...), sig.Results(), sig.Variadic())
    222 }
    223 
    224 // instance returns whether an expression is a simple or qualified identifier
    225 // that is a generic instantiation.
    226 func instance(info *types.Info, expr ast.Expr) bool {
    227 	// Compare the logic here against go/types.instantiatedIdent,
    228 	// which also handles  *IndexExpr and *IndexListExpr.
    229 	var id *ast.Ident
    230 	switch x := expr.(type) {
    231 	case *ast.Ident:
    232 		id = x
    233 	case *ast.SelectorExpr:
    234 		id = x.Sel
    235 	default:
    236 		return false
    237 	}
    238 	_, ok := info.Instances[id]
    239 	return ok
    240 }
    241 
    242 // instanceArgs returns the Instance[id].TypeArgs as a slice.
    243 func instanceArgs(info *types.Info, id *ast.Ident) []types.Type {
    244 	targList := info.Instances[id].TypeArgs
    245 	if targList == nil {
    246 		return nil
    247 	}
    248 
    249 	targs := make([]types.Type, targList.Len())
    250 	for i, n := 0, targList.Len(); i < n; i++ {
    251 		targs[i] = targList.At(i)
    252 	}
    253 	return targs
    254 }
    255 
    256 // Mapping of a type T to a canonical instance C s.t. types.Identical(T, C).
    257 // Thread-safe.
    258 type canonizer struct {
    259 	mu    sync.Mutex
    260 	types typeutil.Map // map from type to a canonical instance
    261 	lists typeListMap  // map from a list of types to a canonical instance
    262 }
    263 
    264 func newCanonizer() *canonizer {
    265 	c := &canonizer{}
    266 	h := typeutil.MakeHasher()
    267 	c.types.SetHasher(h)
    268 	c.lists.hasher = h
    269 	return c
    270 }
    271 
    272 // List returns a canonical representative of a list of types.
    273 // Representative of the empty list is nil.
    274 func (c *canonizer) List(ts []types.Type) *typeList {
    275 	if len(ts) == 0 {
    276 		return nil
    277 	}
    278 
    279 	unaliasAll := func(ts []types.Type) []types.Type {
    280 		// Is there some top level alias?
    281 		var found bool
    282 		for _, t := range ts {
    283 			if _, ok := t.(*types.Alias); ok {
    284 				found = true
    285 				break
    286 			}
    287 		}
    288 		if !found {
    289 			return ts // no top level alias
    290 		}
    291 
    292 		cp := make([]types.Type, len(ts)) // copy with top level aliases removed.
    293 		for i, t := range ts {
    294 			cp[i] = types.Unalias(t)
    295 		}
    296 		return cp
    297 	}
    298 	l := unaliasAll(ts)
    299 
    300 	c.mu.Lock()
    301 	defer c.mu.Unlock()
    302 	return c.lists.rep(l)
    303 }
    304 
    305 // Type returns a canonical representative of type T.
    306 // Removes top-level aliases.
    307 //
    308 // For performance, reasons the canonical instance is order-dependent,
    309 // and may contain deeply nested aliases.
    310 func (c *canonizer) Type(T types.Type) types.Type {
    311 	T = types.Unalias(T) // remove the top level alias.
    312 
    313 	c.mu.Lock()
    314 	defer c.mu.Unlock()
    315 
    316 	if r := c.types.At(T); r != nil {
    317 		return r.(types.Type)
    318 	}
    319 	c.types.Set(T, T)
    320 	return T
    321 }
    322 
    323 // A type for representing a canonized list of types.
    324 type typeList []types.Type
    325 
    326 func (l *typeList) identical(ts []types.Type) bool {
    327 	if l == nil {
    328 		return len(ts) == 0
    329 	}
    330 	n := len(*l)
    331 	if len(ts) != n {
    332 		return false
    333 	}
    334 	for i, left := range *l {
    335 		right := ts[i]
    336 		if !types.Identical(left, right) {
    337 			return false
    338 		}
    339 	}
    340 	return true
    341 }
    342 
    343 type typeListMap struct {
    344 	hasher  typeutil.Hasher
    345 	buckets map[uint32][]*typeList
    346 }
    347 
    348 // rep returns a canonical representative of a slice of types.
    349 func (m *typeListMap) rep(ts []types.Type) *typeList {
    350 	if m == nil || len(ts) == 0 {
    351 		return nil
    352 	}
    353 
    354 	if m.buckets == nil {
    355 		m.buckets = make(map[uint32][]*typeList)
    356 	}
    357 
    358 	h := m.hash(ts)
    359 	bucket := m.buckets[h]
    360 	for _, l := range bucket {
    361 		if l.identical(ts) {
    362 			return l
    363 		}
    364 	}
    365 
    366 	// not present. create a representative.
    367 	cp := make(typeList, len(ts))
    368 	copy(cp, ts)
    369 	rep := &cp
    370 
    371 	m.buckets[h] = append(bucket, rep)
    372 	return rep
    373 }
    374 
    375 func (m *typeListMap) hash(ts []types.Type) uint32 {
    376 	if m == nil {
    377 		return 0
    378 	}
    379 	// Some smallish prime far away from typeutil.Hash.
    380 	n := len(ts)
    381 	h := uint32(13619) + 2*uint32(n)
    382 	for i := range n {
    383 		h += 3 * m.hasher.Hash(ts[i])
    384 	}
    385 	return h
    386 }
    387 
    388 // instantiateMethod instantiates m with targs and returns a canonical representative for this method.
    389 func (canon *canonizer) instantiateMethod(m *types.Func, targs []types.Type, ctxt *types.Context) *types.Func {
    390 	recv := recvType(m)
    391 	if p, ok := types.Unalias(recv).(*types.Pointer); ok {
    392 		recv = p.Elem()
    393 	}
    394 	named := types.Unalias(recv).(*types.Named)
    395 	inst, err := types.Instantiate(ctxt, named.Origin(), targs, false)
    396 	if err != nil {
    397 		panic(err)
    398 	}
    399 	rep := canon.Type(inst)
    400 	obj, _, _ := types.LookupFieldOrMethod(rep, true, m.Pkg(), m.Name())
    401 	return obj.(*types.Func)
    402 }
    403 
    404 // Exposed to ssautil using the linkname hack.
    405 //
    406 //go:linkname isSyntactic golang.org/x/tools/go/ssa.isSyntactic
    407 func isSyntactic(pkg *Package) bool { return pkg.syntax }