@@ -4619,7 +4619,7 @@ def set_inference_error_fallback_type(self, var: Var, lvalue: Lvalue, type: Type
46194619 """Store best known type for variable if type inference failed.
46204620
46214621 If a program ignores error on type inference error, the variable should get some
4622- inferred type so that it can used later on in the program. Example:
4622+ inferred type so that it can be used later on in the program. Example:
46234623
46244624 x = [] # type: ignore
46254625 x.append(1) # Should be ok!
@@ -4654,6 +4654,82 @@ def simple_rvalue(self, rvalue: Expression) -> bool:
46544654 return not any (item .variables for item in typ .items )
46554655 return False
46564656
4657+ def infer_rvalue_with_fallback_context (
4658+ self ,
4659+ lvalue_type : Type | None ,
4660+ rvalue : Expression ,
4661+ preferred_context : Type | None ,
4662+ fallback_context : Type | None ,
4663+ inferred : Var | None ,
4664+ always_allow_any : bool ,
4665+ ) -> Type :
4666+ """Infer rvalue type in two type context and select the best one.
4667+
4668+ See comments below for supported fallback scenarios.
4669+ """
4670+ assert fallback_context is not preferred_context
4671+ # TODO: make assignment checking correct in presence of walrus in r.h.s.
4672+ # We may accept r.h.s. twice. In presence of walrus this can lead to weird
4673+ # false negatives and "back action". A proper solution would be to use
4674+ # binder.accumulate_type_assignments() and assign the types inferred for type
4675+ # context that is ultimately used. This is however tricky with redefinitions.
4676+ # For now we simply disable second accept in cases known to cause problems,
4677+ # see e.g. testAssignToOptionalTupleWalrus.
4678+ binder_version = self .binder .version
4679+
4680+ fallback_context_used = False
4681+ with (
4682+ self .msg .filter_errors (save_filtered_errors = True ) as local_errors ,
4683+ self .local_type_map as type_map ,
4684+ ):
4685+ rvalue_type = self .expr_checker .accept (
4686+ rvalue , type_context = preferred_context , always_allow_any = always_allow_any
4687+ )
4688+
4689+ # There are two cases where we want to try re-inferring r.h.s. in a fallback
4690+ # type context. First case is when redefinitions are allowed, and we got
4691+ # invalid type when using the preferred (empty) type context.
4692+ redefinition_fallback = inferred is not None and not is_valid_inferred_type (
4693+ rvalue_type , self .options
4694+ )
4695+ # Try re-inferring r.h.s. in empty context for union with explicit annotation,
4696+ # and use it results in a narrower type. This helps with various practical
4697+ # examples, see e.g. testOptionalTypeNarrowedByGenericCall.
4698+ union_fallback = (
4699+ inferred is None
4700+ and isinstance (get_proper_type (lvalue_type ), UnionType )
4701+ and binder_version == self .binder .version
4702+ )
4703+
4704+ # Skip literal types, as they have special logic (for better errors).
4705+ if (redefinition_fallback or union_fallback ) and not is_literal_type_like (rvalue_type ):
4706+ with (
4707+ self .msg .filter_errors (save_filtered_errors = True ) as alt_local_errors ,
4708+ self .local_type_map as alt_type_map ,
4709+ ):
4710+ alt_rvalue_type = self .expr_checker .accept (
4711+ rvalue , fallback_context , always_allow_any = always_allow_any
4712+ )
4713+ if (
4714+ not alt_local_errors .has_new_errors ()
4715+ and is_valid_inferred_type (alt_rvalue_type , self .options )
4716+ and (
4717+ # For redefinition fallback we are fine getting not a subtype.
4718+ redefinition_fallback
4719+ # Skip Any type, since it is special cased in binder.
4720+ or not isinstance (get_proper_type (alt_rvalue_type ), AnyType )
4721+ and is_proper_subtype (alt_rvalue_type , rvalue_type )
4722+ )
4723+ ):
4724+ fallback_context_used = True
4725+ rvalue_type = alt_rvalue_type
4726+ self .store_types (alt_type_map )
4727+
4728+ if not fallback_context_used :
4729+ self .msg .add_errors (local_errors .filtered_errors ())
4730+ self .store_types (type_map )
4731+ return rvalue_type
4732+
46574733 def check_simple_assignment (
46584734 self ,
46594735 lvalue_type : Type | None ,
@@ -4674,39 +4750,45 @@ def check_simple_assignment(
46744750 always_allow_any = lvalue_type is not None and not isinstance (
46754751 get_proper_type (lvalue_type ), AnyType
46764752 )
4677- if inferred is None or is_typeddict_type_context (lvalue_type ):
4678- type_context = lvalue_type
4753+
4754+ # If redefinitions are allowed (i.e. we have --allow-redefinition-new
4755+ # and a variable without annotation) then we start with an empty context,
4756+ # since this gives somewhat more intuitive behavior. The only exception
4757+ # is TypedDicts, they are often useless without context.
4758+ try_fallback = (
4759+ inferred is not None or isinstance (get_proper_type (lvalue_type ), UnionType )
4760+ ) and not self .simple_rvalue (rvalue )
4761+
4762+ if not try_fallback or lvalue_type is None or is_typeddict_type_context (lvalue_type ):
4763+ rvalue_type = self .expr_checker .accept (
4764+ rvalue , type_context = lvalue_type , always_allow_any = always_allow_any
4765+ )
46794766 else :
4680- type_context = None
4681-
4682- # TODO: make assignment checking correct in presence of walrus in r.h.s.
4683- # Right now we can accept the r.h.s. up to four(!) times. In presence of
4684- # walrus this can result in weird false negatives and "back action". A proper
4685- # solution would be to:
4686- # * Refactor the code to reduce number of times we accept the r.h.s.
4687- # (two should be enough: empty context + l.h.s. context).
4688- # * For each accept use binder.accumulate_type_assignments() and assign
4689- # the types inferred for context that is ultimately used.
4690- # For now we simply disable some logic that is known to cause problems in
4691- # presence of walrus, see e.g. testAssignToOptionalTupleWalrus.
4692- binder_version = self .binder .version
4767+ if inferred is not None :
4768+ preferred = None
4769+ fallback = lvalue_type
4770+ else :
4771+ preferred = lvalue_type
4772+ fallback = None
4773+
4774+ rvalue_type = self .infer_rvalue_with_fallback_context (
4775+ lvalue_type , rvalue , preferred , fallback , inferred , always_allow_any
4776+ )
46934777
4694- rvalue_type = self .expr_checker .accept (
4695- rvalue , type_context = type_context , always_allow_any = always_allow_any
4696- )
46974778 if (
4698- lvalue_type is not None
4699- and type_context is None
4779+ inferred is not None
47004780 and not is_valid_inferred_type (rvalue_type , self .options )
4701- ):
4702- # Inference in an empty type context didn't produce a valid type, so
4703- # try using lvalue type as context instead.
4704- rvalue_type = self .expr_checker .accept (
4705- rvalue , type_context = lvalue_type , always_allow_any = always_allow_any
4781+ and (
4782+ not inferred .type
4783+ or isinstance (inferred .type , PartialType )
4784+ # This additional check is to give an error instead of inferring
4785+ # a useless type like None | list[Never] in case of "double-partial"
4786+ # types that are not supported yet, see issue #20257.
4787+ or not is_proper_subtype (rvalue_type , inferred .type )
47064788 )
4707- if not is_valid_inferred_type ( rvalue_type , self . options ) and inferred is not None :
4708- self .msg .need_annotation_for_var (inferred , context , self .options )
4709- rvalue_type = rvalue_type .accept (SetNothingToAny ())
4789+ ) :
4790+ self .msg .need_annotation_for_var (inferred , inferred , self .options )
4791+ rvalue_type = rvalue_type .accept (SetNothingToAny ())
47104792
47114793 if (
47124794 isinstance (lvalue , NameExpr )
@@ -4715,49 +4797,25 @@ def check_simple_assignment(
47154797 and not inferred .is_final
47164798 ):
47174799 new_inferred = remove_instance_last_known_values (rvalue_type )
4718- if not is_same_type (inferred .type , new_inferred ):
4719- # Should we widen the inferred type or the lvalue? Variables defined
4720- # at module level or class bodies can't be widened in functions, or
4721- # in another module.
4722- if not self .refers_to_different_scope (lvalue ):
4723- lvalue_type = make_simplified_union ([inferred .type , new_inferred ])
4724- if not is_same_type (lvalue_type , inferred .type ) and not isinstance (
4725- inferred .type , PartialType
4726- ):
4727- # Widen the type to the union of original and new type.
4728- self .widened_vars .append (inferred .name )
4729- self .set_inferred_type (inferred , lvalue , lvalue_type )
4730- self .binder .put (lvalue , rvalue_type )
4731- # TODO: A bit hacky, maybe add a binder method that does put and
4732- # updates declaration?
4733- lit = literal_hash (lvalue )
4734- if lit is not None :
4735- self .binder .declarations [lit ] = lvalue_type
4736- if (
4737- isinstance (get_proper_type (lvalue_type ), UnionType )
4738- # Skip literal types, as they have special logic (for better errors).
4739- and not is_literal_type_like (rvalue_type )
4740- and not self .simple_rvalue (rvalue )
4741- and binder_version == self .binder .version
4742- ):
4743- # Try re-inferring r.h.s. in empty context, and use that if it
4744- # results in a narrower type. We don't do this always because this
4745- # may cause some perf impact, plus we want to partially preserve
4746- # the old behavior. This helps with various practical examples, see
4747- # e.g. testOptionalTypeNarrowedByGenericCall.
4748- with self .msg .filter_errors () as local_errors , self .local_type_map as type_map :
4749- alt_rvalue_type = self .expr_checker .accept (
4750- rvalue , None , always_allow_any = always_allow_any
4751- )
4800+ # Should we widen the inferred type or the lvalue? Variables defined
4801+ # at module level or class bodies can't be widened in functions, or
4802+ # in another module.
47524803 if (
4753- not local_errors .has_new_errors ()
4754- # Skip Any type, since it is special cased in binder.
4755- and not isinstance (get_proper_type (alt_rvalue_type ), AnyType )
4756- and is_valid_inferred_type (alt_rvalue_type , self .options )
4757- and is_proper_subtype (alt_rvalue_type , rvalue_type )
4804+ not self .refers_to_different_scope (lvalue )
4805+ and not isinstance (inferred .type , PartialType )
4806+ and not is_proper_subtype (new_inferred , inferred .type )
47584807 ):
4759- rvalue_type = alt_rvalue_type
4760- self .store_types (type_map )
4808+ lvalue_type = make_simplified_union ([inferred .type , new_inferred ])
4809+ # Widen the type to the union of original and new type.
4810+ self .widened_vars .append (inferred .name )
4811+ self .set_inferred_type (inferred , lvalue , lvalue_type )
4812+ self .binder .put (lvalue , rvalue_type )
4813+ # TODO: A bit hacky, maybe add a binder method that does put and
4814+ # updates declaration?
4815+ lit = literal_hash (lvalue )
4816+ if lit is not None :
4817+ self .binder .declarations [lit ] = lvalue_type
4818+
47614819 if isinstance (rvalue_type , DeletedType ):
47624820 self .msg .deleted_as_rvalue (rvalue_type , context )
47634821 if isinstance (lvalue_type , DeletedType ):
@@ -6065,7 +6123,7 @@ def partition_by_callable(
60656123
60666124 if isinstance (typ , UnionType ):
60676125 callables = []
6068- uncallables = []
6126+ uncallables : list [ Type ] = []
60696127 for subtype in typ .items :
60706128 # Use unsound_partition when handling unions in order to
60716129 # allow the expected type discrimination.
@@ -9479,11 +9537,15 @@ def ambiguous_enum_equality_keys(t: Type) -> set[str]:
94799537 return result
94809538
94819539
9482- def is_typeddict_type_context (lvalue_type : Type | None ) -> bool :
9483- if lvalue_type is None :
9484- return False
9485- lvalue_proper = get_proper_type (lvalue_type )
9486- return isinstance (lvalue_proper , TypedDictType )
9540+ def is_typeddict_type_context (lvalue_type : Type ) -> bool :
9541+ lvalue_type = get_proper_type (lvalue_type )
9542+ if isinstance (lvalue_type , TypedDictType ):
9543+ return True
9544+ if isinstance (lvalue_type , UnionType ):
9545+ for item in lvalue_type .items :
9546+ if is_typeddict_type_context (item ):
9547+ return True
9548+ return False
94879549
94889550
94899551def is_method (node : SymbolNode | None ) -> bool :
0 commit comments