| // 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" |
| "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 targs. |
| // |
| // Any created instance is added to cr. |
| // |
| // Acquires fn.generic.instancesMu. |
| func (fn *Function) instance(targs []types.Type, b *builder) *Function { |
| key := fn.Prog.canon.List(targs) |
| |
| gen := fn.generic |
| |
| gen.instancesMu.Lock() |
| defer gen.instancesMu.Unlock() |
| inst, ok := gen.instances[key] |
| if !ok { |
| inst = createInstance(fn, 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. |
| // |
| // Requires fn.generic.instancesMu. |
| func createInstance(fn *Function, 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 |
| obj = prog.canon.instantiateMethod(fn.object, targs, prog.ctxt) |
| sig = obj.Type().(*types.Signature) |
| } else { |
| // function |
| 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(targs...) { |
| synthetic = fmt.Sprintf("instance of %s", fn.Name()) |
| if fn.syntax != nil { |
| subst = makeSubster(prog.ctxt, obj, fn.typeparams, targs, false) |
| build = (*builder).buildFromSyntax |
| } else { |
| build = (*builder).buildParamsOnly |
| } |
| } else { |
| synthetic = fmt.Sprintf("instantiation wrapper of %s", fn.Name()) |
| build = (*builder).buildInstantiationWrapper |
| } |
| |
| /* generic instance or instantiation wrapper */ |
| return &Function{ |
| name: fmt.Sprintf("%s%s", fn.Name(), targs), // may not be unique |
| 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, |
| 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. |
| |
| for _, t := range ts { |
| if prog.hasParams.Has(t) { |
| return true |
| } |
| } |
| return false |
| } |