@@ -22,8 +22,9 @@ type Named struct {
22
22
targs * TypeList // type arguments (after instantiation), or nil
23
23
methods []* Func // methods declared for this type (not the method set of this type); signatures are type-checked lazily
24
24
25
- resolve func (* Named ) ([]* TypeParam , Type , []* Func )
26
- once sync.Once
25
+ // resolver may be provided to lazily resolve type parameters, underlying, and methods.
26
+ resolver func (* Environment , * Named ) (tparams * TypeParamList , underlying Type , methods []* Func )
27
+ once sync.Once // ensures that tparams, underlying, and methods are resolved before accessing
27
28
}
28
29
29
30
// NewNamed returns a new named type for the given type name, underlying type, and associated methods.
@@ -36,43 +37,22 @@ func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named {
36
37
return (* Checker )(nil ).newNamed (obj , nil , underlying , nil , methods )
37
38
}
38
39
39
- func (t * Named ) load () * Named {
40
- // If t is an instantiated type, it derives its methods and tparams from its
41
- // base type. Since we expect type parameters and methods to be set after a
42
- // call to load, we must load the base and copy here.
43
- //
44
- // underlying is set when t is expanded.
45
- //
46
- // By convention, a type instance is loaded iff its tparams are set.
47
- if t .targs .Len () > 0 && t .tparams == nil {
48
- t .orig .load ()
49
- t .tparams = t .orig .tparams
50
- t .methods = t .orig .methods
51
- }
52
- if t .resolve == nil {
40
+ func (t * Named ) resolve (env * Environment ) * Named {
41
+ if t .resolver == nil {
53
42
return t
54
43
}
55
44
56
45
t .once .Do (func () {
57
- // TODO(mdempsky): Since we're passing t to resolve anyway
46
+ // TODO(mdempsky): Since we're passing t to the resolver anyway
58
47
// (necessary because types2 expects the receiver type for methods
59
48
// on defined interface types to be the Named rather than the
60
49
// underlying Interface), maybe it should just handle calling
61
50
// SetTypeParams, SetUnderlying, and AddMethod instead? Those
62
- // methods would need to support reentrant calls though. It would
51
+ // methods would need to support reentrant calls though. It would
63
52
// also make the API more future-proof towards further extensions
64
53
// (like SetTypeParams).
65
-
66
- tparams , underlying , methods := t .resolve (t )
67
-
68
- switch underlying .(type ) {
69
- case nil , * Named :
70
- panic ("invalid underlying type" )
71
- }
72
-
73
- t .tparams = bindTParams (tparams )
74
- t .underlying = underlying
75
- t .methods = methods
54
+ t .tparams , t .underlying , t .methods = t .resolver (env , t )
55
+ t .fromRHS = t .underlying // for cycle detection
76
56
})
77
57
return t
78
58
}
@@ -121,19 +101,19 @@ func (t *Named) _Orig() *Named { return t.orig }
121
101
122
102
// TypeParams returns the type parameters of the named type t, or nil.
123
103
// The result is non-nil for an (originally) parameterized type even if it is instantiated.
124
- func (t * Named ) TypeParams () * TypeParamList { return t .load ( ).tparams }
104
+ func (t * Named ) TypeParams () * TypeParamList { return t .resolve ( nil ).tparams }
125
105
126
106
// SetTypeParams sets the type parameters of the named type t.
127
- func (t * Named ) SetTypeParams (tparams []* TypeParam ) { t .load ( ).tparams = bindTParams (tparams ) }
107
+ func (t * Named ) SetTypeParams (tparams []* TypeParam ) { t .resolve ( nil ).tparams = bindTParams (tparams ) }
128
108
129
109
// TypeArgs returns the type arguments used to instantiate the named type t.
130
110
func (t * Named ) TypeArgs () * TypeList { return t .targs }
131
111
132
112
// NumMethods returns the number of explicit methods whose receiver is named type t.
133
- func (t * Named ) NumMethods () int { return len (t .load ( ).methods ) }
113
+ func (t * Named ) NumMethods () int { return len (t .resolve ( nil ).methods ) }
134
114
135
115
// Method returns the i'th method of named type t for 0 <= i < t.NumMethods().
136
- func (t * Named ) Method (i int ) * Func { return t .load ( ).methods [i ] }
116
+ func (t * Named ) Method (i int ) * Func { return t .resolve ( nil ).methods [i ] }
137
117
138
118
// SetUnderlying sets the underlying type and marks t as complete.
139
119
func (t * Named ) SetUnderlying (underlying Type ) {
@@ -143,18 +123,18 @@ func (t *Named) SetUnderlying(underlying Type) {
143
123
if _ , ok := underlying .(* Named ); ok {
144
124
panic ("underlying type must not be *Named" )
145
125
}
146
- t .load ( ).underlying = underlying
126
+ t .resolve ( nil ).underlying = underlying
147
127
}
148
128
149
129
// AddMethod adds method m unless it is already in the method list.
150
130
func (t * Named ) AddMethod (m * Func ) {
151
- t .load ( )
131
+ t .resolve ( nil )
152
132
if i , _ := lookupMethod (t .methods , m .pkg , m .name ); i < 0 {
153
133
t .methods = append (t .methods , m )
154
134
}
155
135
}
156
136
157
- func (t * Named ) Underlying () Type { return t .load (). expand (nil ).underlying }
137
+ func (t * Named ) Underlying () Type { return t .resolve (nil ).underlying }
158
138
func (t * Named ) String () string { return TypeString (t , nil ) }
159
139
160
140
// ----------------------------------------------------------------------------
@@ -240,43 +220,37 @@ func (n *Named) setUnderlying(typ Type) {
240
220
}
241
221
}
242
222
243
- // expand ensures that the underlying type of n is instantiated.
223
+ // expandNamed ensures that the underlying type of n is instantiated.
244
224
// The underlying type will be Typ[Invalid] if there was an error.
245
- func (n * Named ) expand (env * Environment ) * Named {
246
- if n .instPos != nil {
247
- // n must be loaded before instantiation, in order to have accurate
248
- // tparams. This is done implicitly by the call to n.TypeParams, but making
249
- // it explicit is harmless: load is idempotent.
250
- n .load ()
251
- var u Type
252
- if n .check .validateTArgLen (* n .instPos , n .tparams .Len (), n .targs .Len ()) {
253
- // TODO(rfindley): handling an optional Checker and Environment here (and
254
- // in subst) feels overly complicated. Can we simplify?
255
- if env == nil {
256
- if n .check != nil {
257
- env = n .check .conf .Environment
258
- } else {
259
- // If we're instantiating lazily, we might be outside the scope of a
260
- // type-checking pass. In that case we won't have a pre-existing
261
- // environment, but don't want to create a duplicate of the current
262
- // instance in the process of expansion.
263
- env = NewEnvironment ()
264
- }
265
- h := env .typeHash (n .orig , n .targs .list ())
266
- // add the instance to the environment to avoid infinite recursion.
267
- // addInstance may return a different, existing instance, but we
268
- // shouldn't return that instance from expand.
269
- env .typeForHash (h , n )
225
+ func expandNamed (env * Environment , n * Named ) (* TypeParamList , Type , []* Func ) {
226
+ n .orig .resolve (env )
227
+
228
+ var u Type
229
+ if n .check .validateTArgLen (* n .instPos , n .orig .tparams .Len (), n .targs .Len ()) {
230
+ // TODO(rfindley): handling an optional Checker and Environment here (and
231
+ // in subst) feels overly complicated. Can we simplify?
232
+ if env == nil {
233
+ if n .check != nil {
234
+ env = n .check .conf .Environment
235
+ } else {
236
+ // If we're instantiating lazily, we might be outside the scope of a
237
+ // type-checking pass. In that case we won't have a pre-existing
238
+ // environment, but don't want to create a duplicate of the current
239
+ // instance in the process of expansion.
240
+ env = NewEnvironment ()
270
241
}
271
- u = n .check .subst (* n .instPos , n .orig .underlying , makeSubstMap (n .TypeParams ().list (), n .targs .list ()), env )
272
- } else {
273
- u = Typ [Invalid ]
242
+ h := env .typeHash (n .orig , n .targs .list ())
243
+ // add the instance to the environment to avoid infinite recursion.
244
+ // addInstance may return a different, existing instance, but we
245
+ // shouldn't return that instance from expand.
246
+ env .typeForHash (h , n )
274
247
}
275
- n . underlying = u
276
- n . fromRHS = u
277
- n . instPos = nil
248
+ u = n . check . subst ( * n . instPos , n . orig . underlying , makeSubstMap ( n . orig . tparams . list (), n . targs . list ()), env )
249
+ } else {
250
+ u = Typ [ Invalid ]
278
251
}
279
- return n
252
+ n .instPos = nil
253
+ return n .orig .tparams , u , n .orig .methods
280
254
}
281
255
282
256
// safeUnderlying returns the underlying of typ without expanding instances, to
@@ -285,7 +259,7 @@ func (n *Named) expand(env *Environment) *Named {
285
259
// TODO(rfindley): eliminate this function or give it a better name.
286
260
func safeUnderlying (typ Type ) Type {
287
261
if t , _ := typ .(* Named ); t != nil {
288
- return t .load ( ).underlying
262
+ return t .resolve ( nil ).underlying
289
263
}
290
264
return typ .Underlying ()
291
265
}
0 commit comments