|
69 | 69 | try_expanding_sum_type_to_union, tuple_fallback, make_simplified_union,
|
70 | 70 | true_only, false_only, erase_to_union_or_bound, function_type,
|
71 | 71 | callable_type, try_getting_str_literals, custom_special_method,
|
72 |
| - is_literal_type_like, |
| 72 | + is_literal_type_like, simple_literal_type, |
73 | 73 | )
|
74 | 74 | from mypy.message_registry import ErrorMessage
|
75 | 75 | import mypy.errorcodes as codes
|
@@ -3853,26 +3853,43 @@ def visit_conditional_expr(self, e: ConditionalExpr, allow_none_return: bool = F
|
3853 | 3853 | if_type = self.analyze_cond_branch(if_map, e.if_expr, context=ctx,
|
3854 | 3854 | allow_none_return=allow_none_return)
|
3855 | 3855 |
|
| 3856 | + # we want to keep the narrowest value of if_type for union'ing the branches |
| 3857 | + # however, it would be silly to pass a literal as a type context. Pass the |
| 3858 | + # underlying fallback type instead. |
| 3859 | + if_type_fallback = simple_literal_type(get_proper_type(if_type)) or if_type |
| 3860 | + |
3856 | 3861 | # Analyze the right branch using full type context and store the type
|
3857 | 3862 | full_context_else_type = self.analyze_cond_branch(else_map, e.else_expr, context=ctx,
|
3858 | 3863 | allow_none_return=allow_none_return)
|
| 3864 | + |
3859 | 3865 | if not mypy.checker.is_valid_inferred_type(if_type):
|
3860 | 3866 | # Analyze the right branch disregarding the left branch.
|
3861 | 3867 | else_type = full_context_else_type
|
| 3868 | + # we want to keep the narrowest value of else_type for union'ing the branches |
| 3869 | + # however, it would be silly to pass a literal as a type context. Pass the |
| 3870 | + # underlying fallback type instead. |
| 3871 | + else_type_fallback = simple_literal_type(get_proper_type(else_type)) or else_type |
3862 | 3872 |
|
3863 | 3873 | # If it would make a difference, re-analyze the left
|
3864 | 3874 | # branch using the right branch's type as context.
|
3865 |
| - if ctx is None or not is_equivalent(else_type, ctx): |
| 3875 | + if ctx is None or not is_equivalent(else_type_fallback, ctx): |
3866 | 3876 | # TODO: If it's possible that the previous analysis of
|
3867 | 3877 | # the left branch produced errors that are avoided
|
3868 | 3878 | # using this context, suppress those errors.
|
3869 |
| - if_type = self.analyze_cond_branch(if_map, e.if_expr, context=else_type, |
| 3879 | + if_type = self.analyze_cond_branch(if_map, e.if_expr, context=else_type_fallback, |
3870 | 3880 | allow_none_return=allow_none_return)
|
3871 | 3881 |
|
| 3882 | + elif if_type_fallback == ctx: |
| 3883 | + # There is no point re-running the analysis if if_type is equal to ctx. |
| 3884 | + # That would be an exact duplicate of the work we just did. |
| 3885 | + # This optimization is particularly important to avoid exponential blowup with nested |
| 3886 | + # if/else expressions: https://github.com/python/mypy/issues/9591 |
| 3887 | + # TODO: would checking for is_proper_subtype also work and cover more cases? |
| 3888 | + else_type = full_context_else_type |
3872 | 3889 | else:
|
3873 | 3890 | # Analyze the right branch in the context of the left
|
3874 | 3891 | # branch's type.
|
3875 |
| - else_type = self.analyze_cond_branch(else_map, e.else_expr, context=if_type, |
| 3892 | + else_type = self.analyze_cond_branch(else_map, e.else_expr, context=if_type_fallback, |
3876 | 3893 | allow_none_return=allow_none_return)
|
3877 | 3894 |
|
3878 | 3895 | # Only create a union type if the type context is a union, to be mostly
|
|
0 commit comments