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