// 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" "slices" "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 rtargs and targs. // // Any created instance is added to cr. // // Acquires fn.generic.instancesMu. func (fn *Function) instance(rtargs, targs []types.Type, b *builder) *Function { key := fn.Prog.canon.List(slices.Concat(rtargs, targs)) gen := fn.generic gen.instancesMu.Lock() defer gen.instancesMu.Unlock() inst, ok := gen.instances[key] if !ok { inst = createInstance(fn, rtargs, 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. // If fn is a method on a generic type, fn's receiver type will be instantiated // using rtargs. // // Requires fn.generic.instancesMu. func createInstance(fn *Function, rtargs, 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, len(rtargs) > 0 || len(targs) > 0 if len(rtargs) > 0 { // possibly generic method on generic type obj = prog.canon.instantiateMethod(fn.object, rtargs, prog.ctxt) } else { // generic method on non-generic type obj = fn.object // instantiation does not exist yet } if len(targs) > 0 { // generic method instSig, err := types.Instantiate(prog.ctxt, obj.Signature(), targs, false) if err != nil { panic(err) } instance, ok := instSig.(*types.Signature) if !ok { panic("Instantiate of a Signature returned a non-signature") } // Do not canonicalize generic methods, because the receiver is not // part of a Signature's type identity. For example, // (*G[int]).m[int] and (G[int]).n[int] may be identical types even // though their receiver types differ. sig = instance } else { // non-generic method on generic type sig = obj.Signature() } } else { // function, len(rtargs) == 0 && len(targs) > 0 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(slices.Concat(rtargs, targs)...) { synthetic = fmt.Sprintf("instance of %s", fn.Name()) if fn.syntax != nil { subst = makeSubster(prog.ctxt, obj, fn.recvtypeparams, rtargs, fn.typeparams, targs) build = (*builder).buildFromSyntax } else { build = (*builder).buildParamsOnly } } else { synthetic = fmt.Sprintf("instantiation wrapper of %s", fn.Name()) build = (*builder).buildInstantiationWrapper } name := fn.Name() if len(targs) > 0 { name = fmt.Sprintf("%s%s", name, targstr(targs)) // may not be unique } /* generic instance or instantiation wrapper */ return &Function{ name: name, 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, recvtypeparams: fn.recvtypeparams, // share with origin recvtypeargs: rtargs, 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. return slices.ContainsFunc(ts, prog.hasParams.Has) }