161 lines
4.7 KiB
Go
161 lines
4.7 KiB
Go
// 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"
|
|
"slices"
|
|
"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 rtargs and targs.
|
|
//
|
|
// Any created instance is added to cr.
|
|
//
|
|
// Acquires fn.generic.instancesMu.
|
|
func (fn *Function) instance(rtargs, targs []types.Type, b *builder) *Function {
|
|
key := fn.Prog.canon.List(slices.Concat(rtargs, targs))
|
|
|
|
gen := fn.generic
|
|
|
|
gen.instancesMu.Lock()
|
|
defer gen.instancesMu.Unlock()
|
|
inst, ok := gen.instances[key]
|
|
if !ok {
|
|
inst = createInstance(fn, rtargs, 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.
|
|
// If fn is a method on a generic type, fn's receiver type will be instantiated
|
|
// using rtargs.
|
|
//
|
|
// Requires fn.generic.instancesMu.
|
|
func createInstance(fn *Function, rtargs, 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, len(rtargs) > 0 || len(targs) > 0
|
|
if len(rtargs) > 0 {
|
|
// possibly generic method on generic type
|
|
obj = prog.canon.instantiateMethod(fn.object, rtargs, prog.ctxt)
|
|
} else {
|
|
// generic method on non-generic type
|
|
obj = fn.object // instantiation does not exist yet
|
|
}
|
|
if len(targs) > 0 {
|
|
// generic method
|
|
instSig, err := types.Instantiate(prog.ctxt, obj.Signature(), targs, false)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
instance, ok := instSig.(*types.Signature)
|
|
if !ok {
|
|
panic("Instantiate of a Signature returned a non-signature")
|
|
}
|
|
// Do not canonicalize generic methods, because the receiver is not
|
|
// part of a Signature's type identity. For example,
|
|
// (*G[int]).m[int] and (G[int]).n[int] may be identical types even
|
|
// though their receiver types differ.
|
|
sig = instance
|
|
} else {
|
|
// non-generic method on generic type
|
|
sig = obj.Signature()
|
|
}
|
|
} else {
|
|
// function, len(rtargs) == 0 && len(targs) > 0
|
|
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(slices.Concat(rtargs, targs)...) {
|
|
synthetic = fmt.Sprintf("instance of %s", fn.Name())
|
|
if fn.syntax != nil {
|
|
subst = makeSubster(prog.ctxt, obj, fn.recvtypeparams, rtargs, fn.typeparams, targs)
|
|
build = (*builder).buildFromSyntax
|
|
} else {
|
|
build = (*builder).buildParamsOnly
|
|
}
|
|
} else {
|
|
synthetic = fmt.Sprintf("instantiation wrapper of %s", fn.Name())
|
|
build = (*builder).buildInstantiationWrapper
|
|
}
|
|
|
|
name := fn.Name()
|
|
if len(targs) > 0 {
|
|
name = fmt.Sprintf("%s%s", name, targstr(targs)) // may not be unique
|
|
}
|
|
|
|
/* generic instance or instantiation wrapper */
|
|
return &Function{
|
|
name: name,
|
|
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,
|
|
recvtypeparams: fn.recvtypeparams, // share with origin
|
|
recvtypeargs: rtargs,
|
|
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.
|
|
|
|
return slices.ContainsFunc(ts, prog.hasParams.Has)
|
|
}
|