instantiate.go (4785B)
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 "slices" 11 "sync" 12 ) 13 14 // A generic records information about a generic origin function, 15 // including a cache of existing instantiations. 16 type generic struct { 17 instancesMu sync.Mutex 18 instances map[*typeList]*Function // canonical type arguments to an instance. 19 } 20 21 // instance returns a Function that is the instantiation of generic 22 // origin function fn with the type arguments rtargs and targs. 23 // 24 // Any created instance is added to cr. 25 // 26 // Acquires fn.generic.instancesMu. 27 func (fn *Function) instance(rtargs, targs []types.Type, b *builder) *Function { 28 key := fn.Prog.canon.List(slices.Concat(rtargs, targs)) 29 30 gen := fn.generic 31 32 gen.instancesMu.Lock() 33 defer gen.instancesMu.Unlock() 34 inst, ok := gen.instances[key] 35 if !ok { 36 inst = createInstance(fn, rtargs, targs) 37 inst.buildshared = b.shared() 38 b.enqueue(inst) 39 40 if gen.instances == nil { 41 gen.instances = make(map[*typeList]*Function) 42 } 43 gen.instances[key] = inst 44 } else { 45 b.waitForSharedFunction(inst) 46 } 47 return inst 48 } 49 50 // createInstance returns the instantiation of generic function fn using targs. 51 // If fn is a method on a generic type, fn's receiver type will be instantiated 52 // using rtargs. 53 // 54 // Requires fn.generic.instancesMu. 55 func createInstance(fn *Function, rtargs, targs []types.Type) *Function { 56 prog := fn.Prog 57 58 // Compute signature. 59 var sig *types.Signature 60 var obj *types.Func 61 if recv := fn.Signature.Recv(); recv != nil { 62 // method, len(rtargs) > 0 || len(targs) > 0 63 if len(rtargs) > 0 { 64 // possibly generic method on generic type 65 obj = prog.canon.instantiateMethod(fn.object, rtargs, prog.ctxt) 66 } else { 67 // generic method on non-generic type 68 obj = fn.object // instantiation does not exist yet 69 } 70 if len(targs) > 0 { 71 // generic method 72 instSig, err := types.Instantiate(prog.ctxt, obj.Signature(), targs, false) 73 if err != nil { 74 panic(err) 75 } 76 instance, ok := instSig.(*types.Signature) 77 if !ok { 78 panic("Instantiate of a Signature returned a non-signature") 79 } 80 // Do not canonicalize generic methods, because the receiver is not 81 // part of a Signature's type identity. For example, 82 // (*G[int]).m[int] and (G[int]).n[int] may be identical types even 83 // though their receiver types differ. 84 sig = instance 85 } else { 86 // non-generic method on generic type 87 sig = obj.Signature() 88 } 89 } else { 90 // function, len(rtargs) == 0 && len(targs) > 0 91 instSig, err := types.Instantiate(prog.ctxt, fn.Signature, targs, false) 92 if err != nil { 93 panic(err) 94 } 95 instance, ok := instSig.(*types.Signature) 96 if !ok { 97 panic("Instantiate of a Signature returned a non-signature") 98 } 99 obj = fn.object // instantiation does not exist yet 100 sig = prog.canon.Type(instance).(*types.Signature) 101 } 102 103 // Choose strategy (instance or wrapper). 104 var ( 105 synthetic string 106 subst *subster 107 build buildFunc 108 ) 109 if prog.mode&InstantiateGenerics != 0 && !prog.isParameterized(slices.Concat(rtargs, targs)...) { 110 synthetic = fmt.Sprintf("instance of %s", fn.Name()) 111 if fn.syntax != nil { 112 subst = makeSubster(prog.ctxt, obj, fn.recvtypeparams, rtargs, fn.typeparams, targs) 113 build = (*builder).buildFromSyntax 114 } else { 115 build = (*builder).buildParamsOnly 116 } 117 } else { 118 synthetic = fmt.Sprintf("instantiation wrapper of %s", fn.Name()) 119 build = (*builder).buildInstantiationWrapper 120 } 121 122 name := fn.Name() 123 if len(targs) > 0 { 124 name = fmt.Sprintf("%s%s", name, targstr(targs)) // may not be unique 125 } 126 127 /* generic instance or instantiation wrapper */ 128 return &Function{ 129 name: name, 130 object: obj, 131 Signature: sig, 132 Synthetic: synthetic, 133 syntax: fn.syntax, // \ 134 info: fn.info, // } empty for non-created packages 135 goversion: fn.goversion, // / 136 build: build, 137 topLevelOrigin: fn, 138 pos: obj.Pos(), 139 Pkg: nil, 140 Prog: fn.Prog, 141 recvtypeparams: fn.recvtypeparams, // share with origin 142 recvtypeargs: rtargs, 143 typeparams: fn.typeparams, // share with origin 144 typeargs: targs, 145 subst: subst, 146 } 147 } 148 149 // isParameterized reports whether any of the specified types contains 150 // a free type parameter. It is safe to call concurrently. 151 func (prog *Program) isParameterized(ts ...types.Type) bool { 152 prog.hasParamsMu.Lock() 153 defer prog.hasParamsMu.Unlock() 154 155 // TODO(adonovan): profile. If this operation is expensive, 156 // handle the most common but shallow cases such as T, pkg.T, 157 // *T without consulting the cache under the lock. 158 159 return slices.ContainsFunc(ts, prog.hasParams.Has) 160 }