| // 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 |
| } |