4545 Type ,
4646 TypedDictType ,
4747 TypeOfAny ,
48+ TypeVarTupleType ,
4849 UninhabitedType ,
4950 UnionType ,
51+ UnpackType ,
52+ find_unpack_in_list ,
5053 get_proper_type ,
54+ split_with_prefix_and_suffix ,
5155)
5256from mypy .typevars import fill_typevars
5357from mypy .visitor import PatternVisitor
@@ -239,13 +243,29 @@ def visit_sequence_pattern(self, o: SequencePattern) -> PatternType:
239243 #
240244 # get inner types of original type
241245 #
246+ unpack_index = None
242247 if isinstance (current_type , TupleType ):
243248 inner_types = current_type .items
244- size_diff = len (inner_types ) - required_patterns
245- if size_diff < 0 :
246- return self .early_non_match ()
247- elif size_diff > 0 and star_position is None :
248- return self .early_non_match ()
249+ unpack_index = find_unpack_in_list (inner_types )
250+ if unpack_index is None :
251+ size_diff = len (inner_types ) - required_patterns
252+ if size_diff < 0 :
253+ return self .early_non_match ()
254+ elif size_diff > 0 and star_position is None :
255+ return self .early_non_match ()
256+ else :
257+ normalized_inner_types = []
258+ for it in inner_types :
259+ # Unfortunately, it is not possible to "split" the TypeVarTuple
260+ # into individual items, so we just use its upper bound for the whole
261+ # analysis instead.
262+ if isinstance (it , UnpackType ) and isinstance (it .type , TypeVarTupleType ):
263+ it = UnpackType (it .type .upper_bound )
264+ normalized_inner_types .append (it )
265+ inner_types = normalized_inner_types
266+ current_type = current_type .copy_modified (items = normalized_inner_types )
267+ if len (inner_types ) - 1 > required_patterns and star_position is None :
268+ return self .early_non_match ()
249269 else :
250270 inner_type = self .get_sequence_type (current_type , o )
251271 if inner_type is None :
@@ -270,18 +290,18 @@ def visit_sequence_pattern(self, o: SequencePattern) -> PatternType:
270290 self .update_type_map (captures , type_map )
271291
272292 new_inner_types = self .expand_starred_pattern_types (
273- contracted_new_inner_types , star_position , len (inner_types )
293+ contracted_new_inner_types , star_position , len (inner_types ), unpack_index is not None
274294 )
275295 rest_inner_types = self .expand_starred_pattern_types (
276- contracted_rest_inner_types , star_position , len (inner_types )
296+ contracted_rest_inner_types , star_position , len (inner_types ), unpack_index is not None
277297 )
278298
279299 #
280300 # Calculate new type
281301 #
282302 new_type : Type
283303 rest_type : Type = current_type
284- if isinstance (current_type , TupleType ):
304+ if isinstance (current_type , TupleType ) and unpack_index is None :
285305 narrowed_inner_types = []
286306 inner_rest_types = []
287307 for inner_type , new_inner_type in zip (inner_types , new_inner_types ):
@@ -301,6 +321,14 @@ def visit_sequence_pattern(self, o: SequencePattern) -> PatternType:
301321 if all (is_uninhabited (typ ) for typ in inner_rest_types ):
302322 # All subpatterns always match, so we can apply negative narrowing
303323 rest_type = TupleType (rest_inner_types , current_type .partial_fallback )
324+ elif isinstance (current_type , TupleType ):
325+ # For variadic tuples it is too tricky to match individual items like for fixed
326+ # tuples, so we instead try to narrow the entire type.
327+ # TODO: use more precise narrowing when possible (e.g. for identical shapes).
328+ new_tuple_type = TupleType (new_inner_types , current_type .partial_fallback )
329+ new_type , rest_type = self .chk .conditional_types_with_intersection (
330+ new_tuple_type , [get_type_range (current_type )], o , default = new_tuple_type
331+ )
304332 else :
305333 new_inner_type = UninhabitedType ()
306334 for typ in new_inner_types :
@@ -345,17 +373,45 @@ def contract_starred_pattern_types(
345373
346374 If star_pos in None the types are returned unchanged.
347375 """
348- if star_pos is None :
349- return types
350- new_types = types [:star_pos ]
351- star_length = len (types ) - num_patterns
352- new_types .append (make_simplified_union (types [star_pos : star_pos + star_length ]))
353- new_types += types [star_pos + star_length :]
354-
355- return new_types
376+ unpack_index = find_unpack_in_list (types )
377+ if unpack_index is not None :
378+ # Variadic tuples require "re-shaping" to match the requested pattern.
379+ unpack = types [unpack_index ]
380+ assert isinstance (unpack , UnpackType )
381+ unpacked = get_proper_type (unpack .type )
382+ # This should be guaranteed by the normalization in the caller.
383+ assert isinstance (unpacked , Instance ) and unpacked .type .fullname == "builtins.tuple"
384+ if star_pos is None :
385+ missing = num_patterns - len (types ) + 1
386+ new_types = types [:unpack_index ]
387+ new_types += [unpacked .args [0 ]] * missing
388+ new_types += types [unpack_index + 1 :]
389+ return new_types
390+ prefix , middle , suffix = split_with_prefix_and_suffix (
391+ tuple ([UnpackType (unpacked ) if isinstance (t , UnpackType ) else t for t in types ]),
392+ star_pos ,
393+ num_patterns - star_pos ,
394+ )
395+ new_middle = []
396+ for m in middle :
397+ # The existing code expects the star item type, rather than the type of
398+ # the whole tuple "slice".
399+ if isinstance (m , UnpackType ):
400+ new_middle .append (unpacked .args [0 ])
401+ else :
402+ new_middle .append (m )
403+ return list (prefix ) + [make_simplified_union (new_middle )] + list (suffix )
404+ else :
405+ if star_pos is None :
406+ return types
407+ new_types = types [:star_pos ]
408+ star_length = len (types ) - num_patterns
409+ new_types .append (make_simplified_union (types [star_pos : star_pos + star_length ]))
410+ new_types += types [star_pos + star_length :]
411+ return new_types
356412
357413 def expand_starred_pattern_types (
358- self , types : list [Type ], star_pos : int | None , num_types : int
414+ self , types : list [Type ], star_pos : int | None , num_types : int , original_unpack : bool
359415 ) -> list [Type ]:
360416 """Undoes the contraction done by contract_starred_pattern_types.
361417
@@ -364,6 +420,17 @@ def expand_starred_pattern_types(
364420 """
365421 if star_pos is None :
366422 return types
423+ if original_unpack :
424+ # In the case where original tuple type has an unpack item, it is not practical
425+ # to coerce pattern type back to the original shape (and may not even be possible),
426+ # so we only restore the type of the star item.
427+ res = []
428+ for i , t in enumerate (types ):
429+ if i != star_pos :
430+ res .append (t )
431+ else :
432+ res .append (UnpackType (self .chk .named_generic_type ("builtins.tuple" , [t ])))
433+ return res
367434 new_types = types [:star_pos ]
368435 star_length = num_types - len (types ) + 1
369436 new_types += [types [star_pos ]] * star_length
@@ -459,7 +526,15 @@ def visit_class_pattern(self, o: ClassPattern) -> PatternType:
459526 return self .early_non_match ()
460527 if isinstance (type_info , TypeInfo ):
461528 any_type = AnyType (TypeOfAny .implementation_artifact )
462- typ : Type = Instance (type_info , [any_type ] * len (type_info .defn .type_vars ))
529+ args : list [Type ] = []
530+ for tv in type_info .defn .type_vars :
531+ if isinstance (tv , TypeVarTupleType ):
532+ args .append (
533+ UnpackType (self .chk .named_generic_type ("builtins.tuple" , [any_type ]))
534+ )
535+ else :
536+ args .append (any_type )
537+ typ : Type = Instance (type_info , args )
463538 elif isinstance (type_info , TypeAlias ):
464539 typ = type_info .target
465540 elif (
0 commit comments