@@ -212,34 +212,49 @@ impl PyGenericAlias {
212
212
}
213
213
}
214
214
215
- pub ( crate ) fn is_typevar ( obj : & PyObjectRef , vm : & VirtualMachine ) -> bool {
216
- let class = obj. class ( ) ;
217
- "TypeVar" == & * class. slot_name ( )
218
- && class
219
- . get_attr ( identifier ! ( vm, __module__) )
220
- . and_then ( |o| o. downcast_ref :: < PyStr > ( ) . map ( |s| s. as_str ( ) == "typing" ) )
221
- . unwrap_or ( false )
222
- }
223
-
224
215
pub ( crate ) fn make_parameters ( args : & Py < PyTuple > , vm : & VirtualMachine ) -> PyTupleRef {
225
216
let mut parameters: Vec < PyObjectRef > = Vec :: with_capacity ( args. len ( ) ) ;
217
+ let mut iparam = 0 ;
218
+
226
219
for arg in args {
227
- if is_typevar ( arg, vm) {
228
- if !parameters. iter ( ) . any ( |param| param. is ( arg) ) {
229
- parameters. push ( arg. clone ( ) ) ;
220
+ // We don't want __parameters__ descriptor of a bare Python class.
221
+ if arg. class ( ) . is ( vm. ctx . types . type_type ) {
222
+ continue ;
223
+ }
224
+
225
+ // Check for __typing_subst__ attribute (like CPython)
226
+ if arg. get_attr ( identifier ! ( vm, __typing_subst__) , vm) . is_ok ( ) {
227
+ // Use tuple_add equivalent logic
228
+ if tuple_index_vec ( & parameters, arg) . is_none ( ) {
229
+ if iparam >= parameters. len ( ) {
230
+ parameters. resize ( iparam + 1 , vm. ctx . none ( ) ) ;
231
+ }
232
+ parameters[ iparam] = arg. clone ( ) ;
233
+ iparam += 1 ;
230
234
}
231
- } else if let Ok ( obj) = arg. get_attr ( identifier ! ( vm, __parameters__) , vm) {
232
- if let Ok ( sub_params) = obj. try_to_ref :: < PyTuple > ( vm) {
235
+ } else if let Ok ( subparams) = arg. get_attr ( identifier ! ( vm, __parameters__) , vm) {
236
+ if let Ok ( sub_params) = subparams. try_to_ref :: < PyTuple > ( vm) {
237
+ let len2 = sub_params. len ( ) ;
238
+ // Resize if needed
239
+ if iparam + len2 > parameters. len ( ) {
240
+ parameters. resize ( iparam + len2, vm. ctx . none ( ) ) ;
241
+ }
233
242
for sub_param in sub_params {
234
- if !parameters. iter ( ) . any ( |param| param. is ( sub_param) ) {
235
- parameters. push ( sub_param. clone ( ) ) ;
243
+ // Use tuple_add equivalent logic
244
+ if tuple_index_vec ( & parameters[ ..iparam] , sub_param) . is_none ( ) {
245
+ if iparam >= parameters. len ( ) {
246
+ parameters. resize ( iparam + 1 , vm. ctx . none ( ) ) ;
247
+ }
248
+ parameters[ iparam] = sub_param. clone ( ) ;
249
+ iparam += 1 ;
236
250
}
237
251
}
238
252
}
239
253
}
240
254
}
241
- parameters. shrink_to_fit ( ) ;
242
255
256
+ // Resize to actual size
257
+ parameters. truncate ( iparam) ;
243
258
PyTuple :: new_ref ( parameters, & vm. ctx )
244
259
}
245
260
@@ -248,6 +263,11 @@ fn tuple_index(tuple: &PyTupleRef, item: &PyObjectRef) -> Option<usize> {
248
263
tuple. iter ( ) . position ( |element| element. is ( item) )
249
264
}
250
265
266
+ #[ inline]
267
+ fn tuple_index_vec ( vec : & [ PyObjectRef ] , item : & PyObjectRef ) -> Option < usize > {
268
+ vec. iter ( ) . position ( |element| element. is ( item) )
269
+ }
270
+
251
271
fn subs_tvars (
252
272
obj : PyObjectRef ,
253
273
params : & PyTupleRef ,
@@ -261,23 +281,40 @@ fn subs_tvars(
261
281
. ok ( )
262
282
. filter ( |sub_params| !sub_params. is_empty ( ) )
263
283
. map ( |sub_params| {
264
- let sub_args = sub_params
265
- . iter ( )
266
- . map ( |arg| {
267
- if let Some ( idx) = tuple_index ( params, arg) {
268
- arg_items[ idx] . clone ( )
269
- } else {
270
- arg. clone ( )
284
+ let mut sub_args = Vec :: new ( ) ;
285
+
286
+ for arg in sub_params. iter ( ) {
287
+ if let Some ( idx) = tuple_index ( params, arg) {
288
+ let param = & params[ idx] ;
289
+ let substituted_arg = & arg_items[ idx] ;
290
+
291
+ // Check if this is a TypeVarTuple (has tp_iter)
292
+ if param. class ( ) . slots . iter . load ( ) . is_some ( )
293
+ && substituted_arg. try_to_ref :: < PyTuple > ( vm) . is_ok ( )
294
+ {
295
+ // TypeVarTuple case - extend with tuple elements
296
+ if let Ok ( tuple) = substituted_arg. try_to_ref :: < PyTuple > ( vm) {
297
+ for elem in tuple. iter ( ) {
298
+ sub_args. push ( elem. clone ( ) ) ;
299
+ }
300
+ continue ;
301
+ }
271
302
}
272
- } )
273
- . collect :: < Vec < _ > > ( ) ;
303
+
304
+ sub_args. push ( substituted_arg. clone ( ) ) ;
305
+ } else {
306
+ sub_args. push ( arg. clone ( ) ) ;
307
+ }
308
+ }
309
+
274
310
let sub_args: PyObjectRef = PyTuple :: new_ref ( sub_args, & vm. ctx ) . into ( ) ;
275
311
obj. get_item ( & * sub_args, vm)
276
312
} )
277
313
} )
278
314
. unwrap_or ( Ok ( obj) )
279
315
}
280
316
317
+ // _Py_subs_parameters
281
318
pub fn subs_parameters < F : Fn ( & VirtualMachine ) -> PyResult < String > > (
282
319
repr : F ,
283
320
args : PyTupleRef ,
@@ -297,26 +334,62 @@ pub fn subs_parameters<F: Fn(&VirtualMachine) -> PyResult<String>>(
297
334
} ;
298
335
299
336
let num_items = arg_items. len ( ) ;
300
- if num_params != num_items {
301
- let plural = if num_items > num_params {
302
- "many"
303
- } else {
304
- "few"
305
- } ;
306
- return Err ( vm. new_type_error ( format ! ( "Too {} arguments for {}" , plural, repr( vm) ?) ) ) ;
337
+
338
+ // Check if we need to apply default values
339
+ if num_items < num_params {
340
+ // Count how many parameters have defaults
341
+ let mut params_with_defaults = 0 ;
342
+ for param in parameters. iter ( ) . rev ( ) {
343
+ if let Ok ( has_default) = vm. call_method ( param, "has_default" , ( ) ) {
344
+ if has_default. try_to_bool ( vm) ? {
345
+ params_with_defaults += 1 ;
346
+ } else {
347
+ break ; // No more defaults from this point backwards
348
+ }
349
+ } else {
350
+ break ;
351
+ }
352
+ }
353
+
354
+ let min_required = num_params - params_with_defaults;
355
+ if num_items < min_required {
356
+ return Err ( vm. new_type_error ( format ! (
357
+ "Too few arguments for {}; actual {}, expected at least {}" ,
358
+ repr( vm) ?,
359
+ num_items,
360
+ min_required
361
+ ) ) ) ;
362
+ }
363
+ } else if num_items > num_params {
364
+ return Err ( vm. new_type_error ( format ! (
365
+ "Too many arguments for {}; actual {}, expected {}" ,
366
+ repr( vm) ?,
367
+ num_items,
368
+ num_params
369
+ ) ) ) ;
307
370
}
308
371
309
- let new_args = args
310
- . iter ( )
311
- . map ( |arg| {
312
- if is_typevar ( arg, vm) {
313
- let idx = tuple_index ( & parameters, arg) . unwrap ( ) ;
314
- Ok ( arg_items[ idx] . clone ( ) )
372
+ let mut new_args = Vec :: new ( ) ;
373
+
374
+ for arg in args. iter ( ) {
375
+ // Check for __typing_subst__ attribute directly (like CPython)
376
+ if let Ok ( subst) = arg. get_attr ( identifier ! ( vm, __typing_subst__) , vm) {
377
+ let idx = tuple_index ( & parameters, arg) . unwrap ( ) ;
378
+ if idx < num_items {
379
+ // Call __typing_subst__ with the argument
380
+ let substituted = subst. call ( ( arg_items[ idx] . clone ( ) , ) , vm) ?;
381
+ new_args. push ( substituted) ;
315
382
} else {
316
- subs_tvars ( arg. clone ( ) , & parameters, arg_items, vm)
383
+ // CPython doesn't support default values in this context
384
+ return Err ( vm. new_type_error ( format ! (
385
+ "No argument provided for parameter at index {}" ,
386
+ idx
387
+ ) ) ) ;
317
388
}
318
- } )
319
- . collect :: < PyResult < Vec < _ > > > ( ) ?;
389
+ } else {
390
+ new_args. push ( subs_tvars ( arg. clone ( ) , & parameters, arg_items, vm) ?) ;
391
+ }
392
+ }
320
393
321
394
Ok ( PyTuple :: new_ref ( new_args, & vm. ctx ) )
322
395
}
0 commit comments