162 lines
4.8 KiB
Go
162 lines
4.8 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/ast"
|
|
"go/types"
|
|
|
|
"golang.org/x/tools/internal/typeparams"
|
|
)
|
|
|
|
// Instances returns all of the instances generated by runtime types for this function in an unspecified order.
|
|
//
|
|
// Thread-safe.
|
|
//
|
|
// This is an experimental interface! It may change without warning.
|
|
func (prog *Program) _Instances(fn *Function) []*Function {
|
|
if len(fn._TypeParams) == 0 {
|
|
return nil
|
|
}
|
|
|
|
prog.methodsMu.Lock()
|
|
defer prog.methodsMu.Unlock()
|
|
return prog.instances[fn].list()
|
|
}
|
|
|
|
// A set of instantiations of a generic function fn.
|
|
type instanceSet struct {
|
|
fn *Function // len(fn._TypeParams) > 0 and len(fn._TypeArgs) == 0.
|
|
instances map[*typeList]*Function // canonical type arguments to an instance.
|
|
syntax *ast.FuncDecl // fn.syntax copy for instantiating after fn is done. nil on synthetic packages.
|
|
info *types.Info // fn.pkg.info copy for building after fn is done.. nil on synthetic packages.
|
|
|
|
// TODO(taking): Consider ways to allow for clearing syntax and info when done building.
|
|
// May require a public API change as MethodValue can request these be built after prog.Build() is done.
|
|
}
|
|
|
|
func (insts *instanceSet) list() []*Function {
|
|
if insts == nil {
|
|
return nil
|
|
}
|
|
|
|
fns := make([]*Function, 0, len(insts.instances))
|
|
for _, fn := range insts.instances {
|
|
fns = append(fns, fn)
|
|
}
|
|
return fns
|
|
}
|
|
|
|
// createInstanceSet adds a new instanceSet for a generic function fn if one does not exist.
|
|
//
|
|
// Precondition: fn is a package level declaration (function or method).
|
|
//
|
|
// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodMu)
|
|
func (prog *Program) createInstanceSet(fn *Function) {
|
|
assert(len(fn._TypeParams) > 0 && len(fn._TypeArgs) == 0, "Can only create instance sets for generic functions")
|
|
|
|
prog.methodsMu.Lock()
|
|
defer prog.methodsMu.Unlock()
|
|
|
|
syntax, _ := fn.syntax.(*ast.FuncDecl)
|
|
assert((syntax == nil) == (fn.syntax == nil), "fn.syntax is either nil or a *ast.FuncDecl")
|
|
|
|
if _, ok := prog.instances[fn]; !ok {
|
|
prog.instances[fn] = &instanceSet{
|
|
fn: fn,
|
|
syntax: syntax,
|
|
info: fn.info,
|
|
}
|
|
}
|
|
}
|
|
|
|
// needsInstance returns an Function that that is the instantiation of fn with the type arguments targs.
|
|
//
|
|
// Any CREATEd instance is added to cr.
|
|
//
|
|
// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodMu)
|
|
func (prog *Program) needsInstance(fn *Function, targs []types.Type, cr *creator) *Function {
|
|
prog.methodsMu.Lock()
|
|
defer prog.methodsMu.Unlock()
|
|
|
|
return prog.instances[fn].lookupOrCreate(targs, cr)
|
|
}
|
|
|
|
// lookupOrCreate returns the instantiation of insts.fn using targs.
|
|
// If the instantiation is reported, this is added to cr.
|
|
func (insts *instanceSet) lookupOrCreate(targs []types.Type, cr *creator) *Function {
|
|
if insts.instances == nil {
|
|
insts.instances = make(map[*typeList]*Function)
|
|
}
|
|
|
|
// canonicalize on a tuple of targs. Sig is not unique.
|
|
//
|
|
// func A[T any]() {
|
|
// var x T
|
|
// fmt.Println("%T", x)
|
|
// }
|
|
key := insts.fn.Prog.canon.List(targs)
|
|
if inst, ok := insts.instances[key]; ok {
|
|
return inst
|
|
}
|
|
|
|
var syntax ast.Node
|
|
if insts.syntax != nil {
|
|
syntax = insts.syntax
|
|
}
|
|
instance := createInstance(insts.fn, targs, insts.info, syntax, cr)
|
|
insts.instances[key] = instance
|
|
return instance
|
|
}
|
|
|
|
// createInstance returns an CREATEd instantiation of fn using targs.
|
|
//
|
|
// Function is added to cr.
|
|
func createInstance(fn *Function, targs []types.Type, info *types.Info, syntax ast.Node, cr *creator) *Function {
|
|
prog := fn.Prog
|
|
var sig *types.Signature
|
|
var obj *types.Func
|
|
if recv := fn.Signature.Recv(); recv != nil {
|
|
// method
|
|
m := fn.object.(*types.Func)
|
|
obj = prog.canon.instantiateMethod(m, targs, prog.ctxt)
|
|
sig = obj.Type().(*types.Signature)
|
|
} else {
|
|
instSig, err := typeparams.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.(*types.Func) // instantiation does not exist yet
|
|
sig = prog.canon.Type(instance).(*types.Signature)
|
|
}
|
|
|
|
name := fmt.Sprintf("%s%s", fn.Name(), targs) // may not be unique
|
|
synthetic := fmt.Sprintf("instantiation of %s", fn.Name())
|
|
instance := &Function{
|
|
name: name,
|
|
object: obj,
|
|
Signature: sig,
|
|
Synthetic: synthetic,
|
|
_Origin: fn,
|
|
pos: obj.Pos(),
|
|
Pkg: nil,
|
|
Prog: fn.Prog,
|
|
_TypeParams: fn._TypeParams,
|
|
_TypeArgs: targs,
|
|
info: info, // on synthetic packages info is nil.
|
|
subst: makeSubster(prog.ctxt, fn._TypeParams, targs, false),
|
|
}
|
|
if prog.mode&InstantiateGenerics != 0 {
|
|
instance.syntax = syntax // otherwise treat instance as an external function.
|
|
}
|
|
cr.Add(instance)
|
|
return instance
|
|
}
|