// 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 fn.typeparams.Len() == 0 || len(fn.typeargs) > 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 // fn.typeparams.Len() > 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(fn.typeparams.Len() > 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 a Function 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.lookupOrCreateInstance(fn, targs, cr) } // lookupOrCreateInstance returns a Function that is the instantiation of fn with the type arguments targs. // // Any CREATEd instance is added to cr. // // EXCLUSIVE_LOCKS_REQUIRED(prog.methodMu) func (prog *Program) lookupOrCreateInstance(fn *Function, targs []types.Type, cr *creator) *Function { return prog.instances[fn].lookupOrCreate(targs, &prog.parameterized, cr) } // lookupOrCreate returns the instantiation of insts.fn using targs. // If the instantiation is created, this is added to cr. func (insts *instanceSet) lookupOrCreate(targs []types.Type, parameterized *tpWalker, cr *creator) *Function { if insts.instances == nil { insts.instances = make(map[*typeList]*Function) } fn := insts.fn prog := fn.Prog // canonicalize on a tuple of targs. Sig is not unique. // // func A[T any]() { // var x T // fmt.Println("%T", x) // } key := prog.canon.List(targs) if inst, ok := insts.instances[key]; ok { return inst } // CREATE instance/instantiation wrapper var syntax ast.Node if insts.syntax != nil { syntax = insts.syntax } 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) } var synthetic string var subst *subster concrete := !parameterized.anyParameterized(targs) if prog.mode&InstantiateGenerics != 0 && concrete { synthetic = fmt.Sprintf("instance of %s", fn.Name()) subst = makeSubster(prog.ctxt, fn.typeparams, targs, false) } else { synthetic = fmt.Sprintf("instantiation wrapper of %s", fn.Name()) } name := fmt.Sprintf("%s%s", fn.Name(), targs) // may not be unique instance := &Function{ name: name, object: obj, Signature: sig, Synthetic: synthetic, syntax: syntax, topLevelOrigin: fn, pos: obj.Pos(), Pkg: nil, Prog: fn.Prog, typeparams: fn.typeparams, // share with origin typeargs: targs, info: insts.info, // on synthetic packages info is nil. subst: subst, } cr.Add(instance) insts.instances[key] = instance return instance }