23
23
24
24
namespace Microsoft . Python . Analysis . Types {
25
25
internal partial class PythonClassType {
26
+ private readonly ReentrancyGuard < IPythonClassType > _genericSpecializationGuard = new ReentrancyGuard < IPythonClassType > ( ) ;
27
+ private readonly ReentrancyGuard < IPythonClassType > _genericResolutionGuard = new ReentrancyGuard < IPythonClassType > ( ) ;
28
+ private readonly object _genericParameterLock = new object ( ) ;
29
+
26
30
private bool _isGeneric ;
27
- private object _genericParameterLock = new object ( ) ;
28
31
private Dictionary < string , PythonClassType > _specificTypeCache ;
29
32
private Dictionary < IGenericTypeParameter , IPythonType > _genericParameters ;
30
33
private IReadOnlyList < IGenericTypeParameter > _parameters = new List < IGenericTypeParameter > ( ) ;
31
- private ReentrancyGuard < IPythonClassType > _genericSpecializationGuard = new ReentrancyGuard < IPythonClassType > ( ) ;
32
- private ReentrancyGuard < IPythonClassType > _genericResolutionGuard = new ReentrancyGuard < IPythonClassType > ( ) ;
33
34
34
35
#region IGenericType
35
36
/// <summary>
@@ -60,23 +61,19 @@ internal partial class PythonClassType {
60
61
/// </summary>
61
62
public IPythonType CreateSpecificType ( IArgumentSet args ) {
62
63
lock ( _genericParameterLock ) {
63
- var genericTypeParameters = GetTypeParameters ( ) ;
64
+ var newGenericTypeParameters = GetTypeParameters ( ) ;
64
65
var newBases = new List < IPythonType > ( ) ;
65
66
66
67
// Get map of generic type parameter to specific type - fill in what type goes to what
67
68
// type parameter T -> int, U -> str, etc.
68
- var genericTypeToSpecificType = GetSpecificTypes ( args , genericTypeParameters , newBases ) ;
69
+ var genericTypeToSpecificType = GetSpecificTypes ( args , newGenericTypeParameters , newBases ) ;
69
70
70
- PythonClassType classType = new PythonClassType ( BaseName , new Location ( DeclaringModule ) ) ;
71
+ var classType = new PythonClassType ( BaseName , new Location ( DeclaringModule ) ) ;
71
72
// Storing generic parameters allows methods returning generic types
72
73
// to know what type parameter returns what specific type
73
- StoreGenericParameters ( classType , genericTypeParameters , genericTypeToSpecificType ) ;
74
+ classType . StoreGenericParameters ( this , newGenericTypeParameters , genericTypeToSpecificType ) ;
74
75
75
76
// Set generic name
76
- if ( ! classType . _genericParameters . IsNullOrEmpty ( ) ) {
77
- classType . _genericName = CodeFormatter . FormatSequence ( BaseName , '[' , classType . _genericParameters . Values ) ;
78
- }
79
-
80
77
// Locking so threads can only access class after it's been initialized
81
78
// Store generic parameters first so name updates correctly, then check if class type has been cached
82
79
_specificTypeCache = _specificTypeCache ?? new Dictionary < string , PythonClassType > ( ) ;
@@ -105,8 +102,8 @@ public IPythonType CreateSpecificType(IArgumentSet args) {
105
102
// Get list of bases that are generic but not generic class parameters, e.g A[T], B[T] but not Generic[T1, T2]
106
103
var genericTypeBases = bases . Except ( genericClassParameters ) . OfType < IGenericType > ( ) . Where ( g => g . IsGeneric ) . ToArray ( ) ;
107
104
108
- // Removing all generic bases, and will only specialize genericTypeBases, remove generic class paramters entirely
109
- // We remove generic class paramters entirely because the type information is now stored in GenericParameters field
105
+ // Removing all generic bases, and will only specialize genericTypeBases, remove generic class parameters entirely
106
+ // We remove generic class parameters entirely because the type information is now stored in GenericParameters field
110
107
// We still need generic bases so we can specialize them
111
108
var specificBases = bases . Except ( genericTypeBases ) . Except ( genericClassParameters ) . ToList ( ) ;
112
109
@@ -127,12 +124,12 @@ public IPythonType CreateSpecificType(IArgumentSet args) {
127
124
}
128
125
129
126
// Set specific class bases
130
- classType . SetBases ( specificBases . Concat ( newBases ) ) ;
127
+ classType . SetBases ( specificBases . Concat ( newBases ) , args . Eval . CurrentScope ) ;
131
128
// Now that parameters are set, check if class is generic
132
129
classType . _parameters = classType . _genericParameters . Values . Distinct ( ) . OfType < IGenericTypeParameter > ( ) . ToList ( ) ;
133
130
classType . DecideGeneric ( ) ;
134
131
// Transfer members from generic to specific type.
135
- SetClassMembers ( classType , args ) ;
132
+ classType . SetClassMembers ( this , args ) ;
136
133
}
137
134
return classType ;
138
135
}
@@ -154,7 +151,7 @@ private IGenericTypeParameter[] GetTypeParameters() {
154
151
var genericClassParameter = bases . OfType < IGenericClassParameter > ( ) . FirstOrDefault ( ) ;
155
152
156
153
// If Generic[...] is present, ordering of type variables is determined from that
157
- if ( genericClassParameter != null && genericClassParameter . TypeParameters != null ) {
154
+ if ( genericClassParameter ? . TypeParameters != null ) {
158
155
fromBases . UnionWith ( genericClassParameter . TypeParameters ) ;
159
156
} else {
160
157
// otherwise look at the generic class bases
@@ -214,7 +211,7 @@ private IReadOnlyDictionary<IGenericTypeParameter, IPythonType> GetSpecificTypes
214
211
// for the copy constructor. Consider 'class A(Generic[K, V], Mapping[K, V])'
215
212
// constructed as 'd = {1:'a', 2:'b'}; A(d)'. Here we look through bases
216
213
// and see if any matches the builtin type id. For example, Mapping or Dict
217
- // will have BultinTypeId .Dict and we can figure out specific types from
214
+ // will have BuiltinTypeId .Dict and we can figure out specific types from
218
215
// the content of the collection.
219
216
var b = _bases . OfType < IGenericType > ( ) . Where ( g => g . IsGeneric ) . FirstOrDefault ( x => x . TypeId == type . TypeId ) ;
220
217
if ( b != null && ! b . Parameters . IsNullOrEmpty ( ) ) {
@@ -259,33 +256,40 @@ private IReadOnlyDictionary<IGenericTypeParameter, IPythonType> GetSpecificTypes
259
256
/// Points the generic type parameter in class type to their corresponding specific type (or a generic
260
257
/// type parameter if no specific type was provided)
261
258
/// </summary>
262
- private void StoreGenericParameters ( PythonClassType classType , IGenericTypeParameter [ ] genericParameters , IReadOnlyDictionary < IGenericTypeParameter , IPythonType > genericToSpecificTypes ) {
259
+ private void StoreGenericParameters (
260
+ IPythonClassType templateClass ,
261
+ IEnumerable < IGenericTypeParameter > newGenericParameters ,
262
+ IReadOnlyDictionary < IGenericTypeParameter , IPythonType > genericToSpecificTypes ) {
263
+
263
264
// copy original generic parameters over and try to fill them in
264
- classType . _genericParameters = new Dictionary < IGenericTypeParameter , IPythonType > ( GenericParameters . ToDictionary ( k => k . Key , k => k . Value ) ) ;
265
+ _genericParameters = templateClass . GenericParameters . ToDictionary ( k => k . Key , k => k . Value ) ;
265
266
266
267
// Case when creating a new specific class type
267
- if ( Parameters . Count == 0 ) {
268
+ if ( templateClass . Parameters . Count == 0 ) {
268
269
// Assign class type generic type parameters to specific types
269
- for ( var i = 0 ; i < genericParameters . Length ; i ++ ) {
270
- var gb = genericParameters [ i ] ;
271
- classType . _genericParameters [ gb ] = genericToSpecificTypes . TryGetValue ( gb , out var v ) ? v : null ;
270
+ foreach ( var gb in newGenericParameters ) {
271
+ _genericParameters [ gb ] = genericToSpecificTypes . TryGetValue ( gb , out var v ) ? v : null ;
272
272
}
273
273
} else {
274
274
// When Parameters field is not empty then need to update generic parameters field
275
- foreach ( var gp in GenericParameters . Keys ) {
276
- if ( GenericParameters [ gp ] is IGenericTypeParameter specificType ) {
275
+ foreach ( var gp in templateClass . GenericParameters . Keys ) {
276
+ if ( templateClass . GenericParameters [ gp ] is IGenericTypeParameter specificType ) {
277
277
// Get unfilled type parameter or type parameter that was filled with another type parameter
278
278
// and try to fill it in
279
279
// e.g
280
280
// class A(Generic[T]):
281
281
// class B(A[U])
282
282
// A has T => U
283
- classType . _genericParameters [ gp ] = genericToSpecificTypes . TryGetValue ( specificType , out var v ) ? v : null ;
283
+ _genericParameters [ gp ] = genericToSpecificTypes . TryGetValue ( specificType , out var v ) ? v : null ;
284
284
}
285
285
}
286
286
}
287
+
288
+ if ( ! _genericParameters . IsNullOrEmpty ( ) ) {
289
+ _genericName = CodeFormatter . FormatSequence ( BaseName , '[' , _genericParameters . Values ) ;
290
+ }
287
291
}
288
-
292
+
289
293
/// <summary>
290
294
/// Given generic type such as Generic[T1, T2, ...] attempts to extract specific types
291
295
/// for its parameters from an argument value. Handles common cases such as dictionary,
@@ -331,24 +335,24 @@ private void GetSpecificTypeFromArgumentValue(IGenericType gt, object argumentVa
331
335
/// Transfers members from generic class to the specific class type
332
336
/// while instantiating specific types for the members.
333
337
/// </summary>
334
- private void SetClassMembers ( PythonClassType classType , IArgumentSet args ) {
335
- // Add members from the template class (this one) .
338
+ private void SetClassMembers ( IPythonClassType templateClass , IArgumentSet args ) {
339
+ // Add members from the template class.
336
340
// Members must be clones rather than references since
337
341
// we are going to set specific types on them.
338
- classType . AddMembers ( this , true ) ;
342
+ AddMembers ( templateClass , true ) ;
339
343
340
344
// Resolve return types of methods, if any were annotated as generics
341
- var members = classType . GetMemberNames ( )
345
+ var members = GetMemberNames ( )
342
346
. Except ( new [ ] { "__class__" , "__bases__" , "__base__" } )
343
- . ToDictionary ( n => n , classType . GetMember ) ;
347
+ . ToDictionary ( n => n , GetMember ) ;
344
348
345
349
// Create specific types.
346
350
// Functions handle generics internally upon the call to Call.
347
351
foreach ( var m in members ) {
348
352
switch ( m . Value ) {
349
353
case IPythonTemplateType tt when tt . IsGeneric ( ) : {
350
354
var specificType = tt . CreateSpecificType ( args ) ;
351
- classType . AddMember ( m . Key , specificType , true ) ;
355
+ AddMember ( m . Key , specificType , true ) ;
352
356
break ;
353
357
}
354
358
case IPythonInstance inst : {
@@ -359,12 +363,12 @@ private void SetClassMembers(PythonClassType classType, IArgumentSet args) {
359
363
specificType = tt . CreateSpecificType ( args ) ;
360
364
break ;
361
365
case IGenericTypeParameter gtd :
362
- classType . GenericParameters . TryGetValue ( gtd , out specificType ) ;
366
+ GenericParameters . TryGetValue ( gtd , out specificType ) ;
363
367
break ;
364
368
}
365
369
366
370
if ( specificType != null ) {
367
- classType . AddMember ( m . Key , new PythonInstance ( specificType ) , true ) ;
371
+ AddMember ( m . Key , new PythonInstance ( specificType ) , true ) ;
368
372
}
369
373
break ;
370
374
}
0 commit comments