1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
|
// 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
}
|