@@ -212,34 +212,49 @@ impl PyGenericAlias {
212212 }
213213}
214214
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-
224215pub ( crate ) fn make_parameters ( args : & Py < PyTuple > , vm : & VirtualMachine ) -> PyTupleRef {
225216 let mut parameters: Vec < PyObjectRef > = Vec :: with_capacity ( args. len ( ) ) ;
217+ let mut iparam = 0 ;
218+
226219 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 ;
230234 }
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+ }
233242 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 ;
236250 }
237251 }
238252 }
239253 }
240254 }
241- parameters. shrink_to_fit ( ) ;
242255
256+ // Resize to actual size
257+ parameters. truncate ( iparam) ;
243258 PyTuple :: new_ref ( parameters, & vm. ctx )
244259}
245260
@@ -248,6 +263,11 @@ fn tuple_index(tuple: &PyTupleRef, item: &PyObjectRef) -> Option<usize> {
248263 tuple. iter ( ) . position ( |element| element. is ( item) )
249264}
250265
266+ #[ inline]
267+ fn tuple_index_vec ( vec : & [ PyObjectRef ] , item : & PyObjectRef ) -> Option < usize > {
268+ vec. iter ( ) . position ( |element| element. is ( item) )
269+ }
270+
251271fn subs_tvars (
252272 obj : PyObjectRef ,
253273 params : & PyTupleRef ,
@@ -261,23 +281,40 @@ fn subs_tvars(
261281 . ok ( )
262282 . filter ( |sub_params| !sub_params. is_empty ( ) )
263283 . 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+ }
271302 }
272- } )
273- . collect :: < Vec < _ > > ( ) ;
303+
304+ sub_args. push ( substituted_arg. clone ( ) ) ;
305+ } else {
306+ sub_args. push ( arg. clone ( ) ) ;
307+ }
308+ }
309+
274310 let sub_args: PyObjectRef = PyTuple :: new_ref ( sub_args, & vm. ctx ) . into ( ) ;
275311 obj. get_item ( & * sub_args, vm)
276312 } )
277313 } )
278314 . unwrap_or ( Ok ( obj) )
279315}
280316
317+ // _Py_subs_parameters
281318pub fn subs_parameters < F : Fn ( & VirtualMachine ) -> PyResult < String > > (
282319 repr : F ,
283320 args : PyTupleRef ,
@@ -297,26 +334,62 @@ pub fn subs_parameters<F: Fn(&VirtualMachine) -> PyResult<String>>(
297334 } ;
298335
299336 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+ ) ) ) ;
307370 }
308371
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) ;
315382 } 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+ ) ) ) ;
317388 }
318- } )
319- . collect :: < PyResult < Vec < _ > > > ( ) ?;
389+ } else {
390+ new_args. push ( subs_tvars ( arg. clone ( ) , & parameters, arg_items, vm) ?) ;
391+ }
392+ }
320393
321394 Ok ( PyTuple :: new_ref ( new_args, & vm. ctx ) )
322395}
0 commit comments