golang.org/x/tools@v0.21.1-0.20240520172518-788d39e776b1/go/ssa/instantiate.go (about) 1 // Copyright 2022 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 import ( 8 "fmt" 9 "go/types" 10 "sync" 11 ) 12 13 // A generic records information about a generic origin function, 14 // including a cache of existing instantiations. 15 type generic struct { 16 instancesMu sync.Mutex 17 instances map[*typeList]*Function // canonical type arguments to an instance. 18 } 19 20 // instance returns a Function that is the instantiation of generic 21 // origin function fn with the type arguments targs. 22 // 23 // Any created instance is added to cr. 24 // 25 // Acquires fn.generic.instancesMu. 26 func (fn *Function) instance(targs []types.Type, cr *creator) *Function { 27 key := fn.Prog.canon.List(targs) 28 29 gen := fn.generic 30 31 gen.instancesMu.Lock() 32 defer gen.instancesMu.Unlock() 33 inst, ok := gen.instances[key] 34 if !ok { 35 inst = createInstance(fn, targs, cr) 36 if gen.instances == nil { 37 gen.instances = make(map[*typeList]*Function) 38 } 39 gen.instances[key] = inst 40 } 41 return inst 42 } 43 44 // createInstance returns the instantiation of generic function fn using targs. 45 // If the instantiation is created, this is added to cr. 46 // 47 // Requires fn.generic.instancesMu. 48 func createInstance(fn *Function, targs []types.Type, cr *creator) *Function { 49 prog := fn.Prog 50 51 // Compute signature. 52 var sig *types.Signature 53 var obj *types.Func 54 if recv := fn.Signature.Recv(); recv != nil { 55 // method 56 obj = prog.canon.instantiateMethod(fn.object, targs, prog.ctxt) 57 sig = obj.Type().(*types.Signature) 58 } else { 59 // function 60 instSig, err := types.Instantiate(prog.ctxt, fn.Signature, targs, false) 61 if err != nil { 62 panic(err) 63 } 64 instance, ok := instSig.(*types.Signature) 65 if !ok { 66 panic("Instantiate of a Signature returned a non-signature") 67 } 68 obj = fn.object // instantiation does not exist yet 69 sig = prog.canon.Type(instance).(*types.Signature) 70 } 71 72 // Choose strategy (instance or wrapper). 73 var ( 74 synthetic string 75 subst *subster 76 build buildFunc 77 ) 78 if prog.mode&InstantiateGenerics != 0 && !prog.isParameterized(targs...) { 79 synthetic = fmt.Sprintf("instance of %s", fn.Name()) 80 if fn.syntax != nil { 81 scope := obj.Origin().Scope() 82 subst = makeSubster(prog.ctxt, scope, fn.typeparams, targs, false) 83 build = (*builder).buildFromSyntax 84 } else { 85 build = (*builder).buildParamsOnly 86 } 87 } else { 88 synthetic = fmt.Sprintf("instantiation wrapper of %s", fn.Name()) 89 build = (*builder).buildInstantiationWrapper 90 } 91 92 /* generic instance or instantiation wrapper */ 93 instance := &Function{ 94 name: fmt.Sprintf("%s%s", fn.Name(), targs), // may not be unique 95 object: obj, 96 Signature: sig, 97 Synthetic: synthetic, 98 syntax: fn.syntax, // \ 99 info: fn.info, // } empty for non-created packages 100 goversion: fn.goversion, // / 101 build: build, 102 topLevelOrigin: fn, 103 pos: obj.Pos(), 104 Pkg: nil, 105 Prog: fn.Prog, 106 typeparams: fn.typeparams, // share with origin 107 typeargs: targs, 108 subst: subst, 109 } 110 cr.Add(instance) 111 return instance 112 } 113 114 // isParameterized reports whether any of the specified types contains 115 // a free type parameter. It is safe to call concurrently. 116 func (prog *Program) isParameterized(ts ...types.Type) bool { 117 prog.hasParamsMu.Lock() 118 defer prog.hasParamsMu.Unlock() 119 120 // TODO(adonovan): profile. If this operation is expensive, 121 // handle the most common but shallow cases such as T, pkg.T, 122 // *T without consulting the cache under the lock. 123 124 for _, t := range ts { 125 if prog.hasParams.Has(t) { 126 return true 127 } 128 } 129 return false 130 }