2828 ProperType ,
2929 TupleType ,
3030 Type ,
31- TypeOfAny ,
3231 TypeType ,
3332 TypeVarType ,
3433 UnionType ,
@@ -73,6 +72,7 @@ class Frame:
7372 def __init__ (self , id : int , conditional_frame : bool = False ) -> None :
7473 self .id = id
7574 self .types : dict [Key , CurrentType ] = {}
75+ self .conditionally_narrowed_keys : set [Key ] = set ()
7676 self .unreachable = False
7777 self .conditional_frame = conditional_frame
7878 self .suppress_unreachable_warnings = False
@@ -301,8 +301,6 @@ def update_from_options(self, frames: list[Frame]) -> bool:
301301 """Update the frame to reflect that each key will be updated
302302 as in one of the frames. Return whether any item changes.
303303
304- If a key is declared as AnyType, only update it if all the
305- options are the same.
306304 """
307305 all_reachable = all (not f .unreachable for f in frames )
308306 if not all_reachable :
@@ -328,9 +326,9 @@ def update_from_options(self, frames: list[Frame]) -> bool:
328326 # know anything about key in at least one possible frame.
329327 continue
330328
331- resulting_values = [x for x in resulting_values if x is not None ]
329+ filtered_resulting_values = [x for x in resulting_values if x is not None ]
332330
333- if all_reachable and all (not x .from_assignment for x in resulting_values ):
331+ if all_reachable and all (not x .from_assignment for x in filtered_resulting_values ):
334332 # Do not synthesize a new type if we encountered a conditional block
335333 # (if, while or match-case) without assignments.
336334 # See check-isinstance.test::testNoneCheckDoesNotMakeTypeVarOptional
@@ -342,57 +340,43 @@ def update_from_options(self, frames: list[Frame]) -> bool:
342340 # a micro-optimization for --allow-redefinition-new.
343341 seen_types = set ()
344342 resulting_types = []
345- for rv in resulting_values :
346- assert rv is not None
343+ for rv in filtered_resulting_values :
347344 if rv .type in seen_types :
348345 continue
349346 resulting_types .append (rv .type )
350347 seen_types .add (rv .type )
351348
352- type = resulting_types [0 ]
353- declaration_type = get_proper_type (self .declarations .get (key ))
354- if isinstance (declaration_type , AnyType ):
355- # At this point resulting values can't contain None, see continue above
356- if not all (is_same_type (type , t ) for t in resulting_types [1 :]):
357- type = AnyType (TypeOfAny .from_another_any , source_any = declaration_type )
349+ declaration_type = self .declarations .get (key )
350+ if len (resulting_types ) == 1 :
351+ # This is to avoid calling get_proper_type() unless needed, as this may
352+ # interfere with our (hacky) TypeGuard support.
353+ type = resulting_types [0 ]
358354 else :
359- possible_types = []
360- for t in resulting_types :
361- assert t is not None
362- possible_types .append (t )
363- if len (possible_types ) == 1 :
364- # This is to avoid calling get_proper_type() unless needed, as this may
365- # interfere with our (hacky) TypeGuard support.
366- type = possible_types [0 ]
367- else :
368- type = make_simplified_union (possible_types )
369- # Legacy guard for corner case when the original type is TypeVarType.
370- if isinstance (declaration_type , TypeVarType ) and not is_subtype (
371- type , declaration_type
372- ):
373- type = declaration_type
374- # Try simplifying resulting type for unions involving variadic tuples.
375- # Technically, everything is still valid without this step, but if we do
376- # not do this, this may create long unions after exiting an if check like:
377- # x: tuple[int, ...]
378- # if len(x) < 10:
379- # ...
380- # We want the type of x to be tuple[int, ...] after this block (if it is
381- # still equivalent to such type).
382- if isinstance (type , UnionType ):
383- type = collapse_variadic_union (type )
384- if (
385- old_semantics
386- and isinstance (type , ProperType )
387- and isinstance (type , UnionType )
388- ):
389- # Simplify away any extra Any's that were added to the declared
390- # type when popping a frame.
391- simplified = UnionType .make_union (
392- [t for t in type .items if not isinstance (get_proper_type (t ), AnyType )]
393- )
394- if simplified == self .declarations [key ]:
395- type = simplified
355+ type = make_simplified_union (resulting_types )
356+ # Legacy guard for corner case when the original type is TypeVarType.
357+ proper_declaration_type = get_proper_type (declaration_type )
358+ if isinstance (proper_declaration_type , TypeVarType ) and not is_subtype (
359+ type , proper_declaration_type
360+ ):
361+ type = proper_declaration_type
362+ # Try simplifying resulting type for unions involving variadic tuples.
363+ # Technically, everything is still valid without this step, but if we do
364+ # not do this, this may create long unions after exiting an if check like:
365+ # x: tuple[int, ...]
366+ # if len(x) < 10:
367+ # ...
368+ # We want the type of x to be tuple[int, ...] after this block (if it is
369+ # still equivalent to such type).
370+ if isinstance (type , UnionType ):
371+ type = collapse_variadic_union (type )
372+ if old_semantics and isinstance (type , ProperType ) and isinstance (type , UnionType ):
373+ # Simplify away any extra Any's that were added to the declared
374+ # type when popping a frame.
375+ simplified = UnionType .make_union (
376+ [t for t in type .items if not isinstance (get_proper_type (t ), AnyType )]
377+ )
378+ if simplified == self .declarations [key ]:
379+ type = simplified
396380 if (
397381 current_value is None
398382 or not is_same_type (type , current_value .type )
@@ -482,6 +466,8 @@ def assign_type(self, expr: Expression, type: Type, declared_type: Type | None)
482466 return
483467 if not literal (expr ):
484468 return
469+ key = literal_hash (expr )
470+ assert key is not None
485471 self .invalidate_dependencies (expr )
486472
487473 if declared_type is None :
@@ -530,6 +516,8 @@ def assign_type(self, expr: Expression, type: Type, declared_type: Type | None)
530516 # has an explicit `Any` type annotation.
531517 if isinstance (expr , RefExpr ) and isinstance (expr .node , Var ) and expr .node .is_inferred :
532518 self .put (expr , type )
519+ elif any (key in f .conditionally_narrowed_keys for f in self .frames ):
520+ self .put (expr , type )
533521 else :
534522 self .put (expr , declared_type )
535523 else :
@@ -553,6 +541,16 @@ def invalidate_dependencies(self, expr: BindableExpression) -> None:
553541 for dep in self .dependencies .get (key , set ()):
554542 self ._cleanse_key (dep )
555543
544+ def record_conditional_type_map (self , type_map : dict [Expression , Type ] | None ) -> None :
545+ """Record expressions that are mentioned by the active conditional."""
546+ if type_map is None :
547+ return
548+ for expr in type_map :
549+ if self .can_put_directly (expr ):
550+ key = literal_hash (expr )
551+ assert key is not None
552+ self .frames [- 1 ].conditionally_narrowed_keys .add (key )
553+
556554 def allow_jump (self , index : int ) -> None :
557555 # self.frames and self.options_on_return have different lengths
558556 # so make sure the index is positive
0 commit comments