-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtypeparam.go
164 lines (142 loc) · 5.1 KB
/
typeparam.go
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
// Copyright 2011 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 types2
import "sync/atomic"
// Note: This is a uint32 rather than a uint64 because the
// respective 64 bit atomic instructions are not available
// on all platforms.
var lastID atomic.Uint32
// nextID returns a value increasing monotonically by 1 with
// each call, starting with 1. It may be called concurrently.
func nextID() uint64 { return uint64(lastID.Add(1)) }
// A TypeParam represents the type of a type parameter in a generic declaration.
//
// A TypeParam has a name; use the [TypeParam.Obj] method to access
// its [TypeName] object.
type TypeParam struct {
check *Checker // for lazy type bound completion
id uint64 // unique id, for debugging only
obj *TypeName // corresponding type name
index int // type parameter index in source order, starting at 0
bound Type // any type, but underlying is eventually *Interface for correct programs (see TypeParam.iface)
}
// NewTypeParam returns a new TypeParam. Type parameters may be set on a Named
// type by calling SetTypeParams. Setting a type parameter on more than one type
// will result in a panic.
//
// The constraint argument can be nil, and set later via SetConstraint. If the
// constraint is non-nil, it must be fully defined.
func NewTypeParam(obj *TypeName, constraint Type) *TypeParam {
return (*Checker)(nil).newTypeParam(obj, constraint)
}
// check may be nil
func (check *Checker) newTypeParam(obj *TypeName, constraint Type) *TypeParam {
// Always increment lastID, even if it is not used.
id := nextID()
if check != nil {
check.nextID++
id = check.nextID
}
typ := &TypeParam{check: check, id: id, obj: obj, index: -1, bound: constraint}
if obj.typ == nil {
obj.typ = typ
}
// iface may mutate typ.bound, so we must ensure that iface() is called
// at least once before the resulting TypeParam escapes.
if check != nil {
check.needsCleanup(typ)
} else if constraint != nil {
typ.iface()
}
return typ
}
// Obj returns the type name for the type parameter t.
func (t *TypeParam) Obj() *TypeName { return t.obj }
// Index returns the index of the type param within its param list, or -1 if
// the type parameter has not yet been bound to a type.
func (t *TypeParam) Index() int {
return t.index
}
// Constraint returns the type constraint specified for t.
func (t *TypeParam) Constraint() Type {
return t.bound
}
// SetConstraint sets the type constraint for t.
//
// It must be called by users of NewTypeParam after the bound's underlying is
// fully defined, and before using the type parameter in any way other than to
// form other types. Once SetConstraint returns the receiver, t is safe for
// concurrent use.
func (t *TypeParam) SetConstraint(bound Type) {
if bound == nil {
panic("nil constraint")
}
t.bound = bound
// iface may mutate t.bound (if bound is not an interface), so ensure that
// this is done before returning.
t.iface()
}
// Underlying returns the [underlying type] of the type parameter t, which is
// the underlying type of its constraint. This type is always an interface.
//
// [underlying type]: https://go.dev/ref/spec#Underlying_types.
func (t *TypeParam) Underlying() Type {
return t.iface()
}
func (t *TypeParam) String() string { return TypeString(t, nil) }
// ----------------------------------------------------------------------------
// Implementation
func (t *TypeParam) cleanup() {
t.iface()
t.check = nil
}
// iface returns the constraint interface of t.
func (t *TypeParam) iface() *Interface {
bound := t.bound
// determine constraint interface
var ityp *Interface
switch u := under(bound).(type) {
case *Basic:
if !isValid(u) {
// error is reported elsewhere
return &emptyInterface
}
case *Interface:
if isTypeParam(bound) {
// error is reported in Checker.collectTypeParams
return &emptyInterface
}
ityp = u
}
// If we don't have an interface, wrap constraint into an implicit interface.
if ityp == nil {
ityp = NewInterfaceType(nil, []Type{bound})
ityp.implicit = true
t.bound = ityp // update t.bound for next time (optimization)
}
// compute type set if necessary
if ityp.tset == nil {
// pos is used for tracing output; start with the type parameter position.
pos := t.obj.pos
// use the (original or possibly instantiated) type bound position if we have one
if n := asNamed(bound); n != nil {
pos = n.obj.pos
}
computeInterfaceTypeSet(t.check, pos, ityp)
}
return ityp
}
// is calls f with the specific type terms of t's constraint and reports whether
// all calls to f returned true. If there are no specific terms, is
// returns the result of f(nil).
func (t *TypeParam) is(f func(*term) bool) bool {
return t.iface().typeSet().is(f)
}
// typeset is an iterator over the (type/underlying type) pairs of the
// specific type terms of t's constraint.
// If there are no specific terms, typeset calls yield with (nil, nil).
// In any case, typeset is guaranteed to call yield at least once.
func (t *TypeParam) typeset(yield func(t, u Type) bool) {
t.iface().typeSet().typeset(yield)
}