blob: 871d708ba07bdab8f0beaf565d78289ee1836eb4 [file] [log] [blame]
// 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
}
instance := createInstance(insts.fn, targs, insts.info, insts.syntax, cr)
// TODO(taking): Allow for the function to be built after monomorphization is supported.
instance.syntax = nil // treat instance as an external function to prevent building.
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
// instantiates m with targs and returns a canonical representative for this method.
m := fn.object.(*types.Func)
recv := recvType(m)
if p, ok := recv.(*types.Pointer); ok {
recv = p.Elem()
}
named := recv.(*types.Named)
inst, err := typeparams.Instantiate(prog.ctxt, typeparams.NamedTypeOrigin(named), targs, false)
if err != nil {
panic(err)
}
canon, _, _ := types.LookupFieldOrMethod(prog.canon.Type(inst), true, m.Pkg(), m.Name())
obj = canon.(*types.Func)
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,
syntax: syntax, // on synthetic packages syntax is nil.
_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),
}
cr.Add(instance)
return instance
}