instantiate.go raw
1 // Copyright 2022 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package ssa
6
7 import (
8 "fmt"
9 "go/types"
10 "slices"
11 "sync"
12 )
13
14 // A generic records information about a generic origin function,
15 // including a cache of existing instantiations.
16 type generic struct {
17 instancesMu sync.Mutex
18 instances map[*typeList]*Function // canonical type arguments to an instance.
19 }
20
21 // instance returns a Function that is the instantiation of generic
22 // origin function fn with the type arguments targs.
23 //
24 // Any created instance is added to cr.
25 //
26 // Acquires fn.generic.instancesMu.
27 func (fn *Function) instance(targs []types.Type, b *builder) *Function {
28 key := fn.Prog.canon.List(targs)
29
30 gen := fn.generic
31
32 gen.instancesMu.Lock()
33 defer gen.instancesMu.Unlock()
34 inst, ok := gen.instances[key]
35 if !ok {
36 inst = createInstance(fn, targs)
37 inst.buildshared = b.shared()
38 b.enqueue(inst)
39
40 if gen.instances == nil {
41 gen.instances = make(map[*typeList]*Function)
42 }
43 gen.instances[key] = inst
44 } else {
45 b.waitForSharedFunction(inst)
46 }
47 return inst
48 }
49
50 // createInstance returns the instantiation of generic function fn using targs.
51 //
52 // Requires fn.generic.instancesMu.
53 func createInstance(fn *Function, targs []types.Type) *Function {
54 prog := fn.Prog
55
56 // Compute signature.
57 var sig *types.Signature
58 var obj *types.Func
59 if recv := fn.Signature.Recv(); recv != nil {
60 // method
61 obj = prog.canon.instantiateMethod(fn.object, targs, prog.ctxt)
62 sig = obj.Type().(*types.Signature)
63 } else {
64 // function
65 instSig, err := types.Instantiate(prog.ctxt, fn.Signature, targs, false)
66 if err != nil {
67 panic(err)
68 }
69 instance, ok := instSig.(*types.Signature)
70 if !ok {
71 panic("Instantiate of a Signature returned a non-signature")
72 }
73 obj = fn.object // instantiation does not exist yet
74 sig = prog.canon.Type(instance).(*types.Signature)
75 }
76
77 // Choose strategy (instance or wrapper).
78 var (
79 synthetic string
80 subst *subster
81 build buildFunc
82 )
83 if prog.mode&InstantiateGenerics != 0 && !prog.isParameterized(targs...) {
84 synthetic = fmt.Sprintf("instance of %s", fn.Name())
85 if fn.syntax != nil {
86 subst = makeSubster(prog.ctxt, obj, fn.typeparams, targs)
87 build = (*builder).buildFromSyntax
88 } else {
89 build = (*builder).buildParamsOnly
90 }
91 } else {
92 synthetic = fmt.Sprintf("instantiation wrapper of %s", fn.Name())
93 build = (*builder).buildInstantiationWrapper
94 }
95
96 /* generic instance or instantiation wrapper */
97 return &Function{
98 name: fmt.Sprintf("%s%s", fn.Name(), targs), // may not be unique
99 object: obj,
100 Signature: sig,
101 Synthetic: synthetic,
102 syntax: fn.syntax, // \
103 info: fn.info, // } empty for non-created packages
104 goversion: fn.goversion, // /
105 build: build,
106 topLevelOrigin: fn,
107 pos: obj.Pos(),
108 Pkg: nil,
109 Prog: fn.Prog,
110 typeparams: fn.typeparams, // share with origin
111 typeargs: targs,
112 subst: subst,
113 }
114 }
115
116 // isParameterized reports whether any of the specified types contains
117 // a free type parameter. It is safe to call concurrently.
118 func (prog *Program) isParameterized(ts ...types.Type) bool {
119 prog.hasParamsMu.Lock()
120 defer prog.hasParamsMu.Unlock()
121
122 // TODO(adonovan): profile. If this operation is expensive,
123 // handle the most common but shallow cases such as T, pkg.T,
124 // *T without consulting the cache under the lock.
125
126 return slices.ContainsFunc(ts, prog.hasParams.Has)
127 }
128