From 38675b5fc97872c3ee303654c8b2de8c305668ec Mon Sep 17 00:00:00 2001 From: JaylenLuc Date: Wed, 13 Sep 2023 17:13:27 -0700 Subject: [PATCH 1/4] fix issue #15747 --- mypy/argmap.py | 19 +++++++++++++-- mypy/checkexpr.py | 5 +++- mypy/constraints.py | 6 ++++- mypy/infer.py | 6 ++++- test-data/unit/check-unpack_iterable.test | 28 +++++++++++++++++++++++ 5 files changed, 59 insertions(+), 5 deletions(-) create mode 100644 test-data/unit/check-unpack_iterable.test diff --git a/mypy/argmap.py b/mypy/argmap.py index ec8463fd0625..d0e51e695b0b 100644 --- a/mypy/argmap.py +++ b/mypy/argmap.py @@ -160,13 +160,19 @@ def f(x: int, *args: str) -> None: ... needs a separate instance since instances have per-call state. """ - def __init__(self, context: ArgumentInferContext) -> None: + import mypy + + def __init__( + self, context: ArgumentInferContext, checker: mypy.checker.TypeChecker = None + ) -> None: # Next tuple *args index to use. self.tuple_index = 0 # Keyword arguments in TypedDict **kwargs used. self.kwargs_used: set[str] = set() # Type context for `*` and `**` arg kinds. self.context = context + # TypeChecker to check true argument types for iterables + self.checker = checker def expand_actual_type( self, @@ -214,7 +220,16 @@ def expand_actual_type( # ParamSpec is valid in *args but it can't be unpacked. return actual_type else: - return AnyType(TypeOfAny.from_error) + if self.checker is not None: + # get the true type of the arguments of the iterable + iterable_item_type = ( + self.checker.analyze_iterable_item_type_without_expression( + actual_type, self.context + )[1] + ) + return iterable_item_type + else: + return AnyType(TypeOfAny.from_error) elif actual_kind == nodes.ARG_STAR2: from mypy.subtypes import is_subtype diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 22a9852545b7..552fe6cc309b 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -1998,6 +1998,7 @@ def infer_function_type_arguments( pass1_args.append(arg) inferred_args, _ = infer_function_type_arguments( + self.chk, callee_type, pass1_args, arg_kinds, @@ -2059,6 +2060,7 @@ def infer_function_type_arguments( # potentially involving free variables. # TODO: support the similar inference for return type context. poly_inferred_args, free_vars = infer_function_type_arguments( + self.chk, callee_type, arg_types, arg_kinds, @@ -2139,6 +2141,7 @@ def infer_function_type_arguments_pass2( arg_types = self.infer_arg_types_in_context(callee_type, args, arg_kinds, formal_to_actual) inferred_args, _ = infer_function_type_arguments( + self.chk, callee_type, arg_types, arg_kinds, @@ -2379,7 +2382,7 @@ def check_argument_types( """ check_arg = check_arg or self.check_arg # Keep track of consumed tuple *arg items. - mapper = ArgTypeExpander(self.argument_infer_context()) + mapper = ArgTypeExpander(self.argument_infer_context(), self.chk) for i, actuals in enumerate(formal_to_actual): orig_callee_arg_type = get_proper_type(callee.arg_types[i]) diff --git a/mypy/constraints.py b/mypy/constraints.py index 0524e38f9643..ffce8f90896b 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -103,7 +103,11 @@ def __eq__(self, other: object) -> bool: return (self.type_var, self.op, self.target) == (other.type_var, other.op, other.target) +import mypy + + def infer_constraints_for_callable( + checker: mypy.checker.TypeChecker, callee: CallableType, arg_types: Sequence[Type | None], arg_kinds: list[ArgKind], @@ -116,7 +120,7 @@ def infer_constraints_for_callable( Return a list of constraints. """ constraints: list[Constraint] = [] - mapper = ArgTypeExpander(context) + mapper = ArgTypeExpander(context, checker) param_spec = callee.param_spec() param_spec_arg_types = [] diff --git a/mypy/infer.py b/mypy/infer.py index ba4a1d2bc9b1..3ca37cbf29c3 100644 --- a/mypy/infer.py +++ b/mypy/infer.py @@ -29,7 +29,11 @@ class ArgumentInferContext(NamedTuple): iterable_type: Instance +import mypy + + def infer_function_type_arguments( + checker: mypy.checker.TypeChecker, callee_type: CallableType, arg_types: Sequence[Type | None], arg_kinds: list[ArgKind], @@ -54,7 +58,7 @@ def infer_function_type_arguments( """ # Infer constraints. constraints = infer_constraints_for_callable( - callee_type, arg_types, arg_kinds, arg_names, formal_to_actual, context + checker, callee_type, arg_types, arg_kinds, arg_names, formal_to_actual, context ) # Solve constraints. diff --git a/test-data/unit/check-unpack_iterable.test b/test-data/unit/check-unpack_iterable.test new file mode 100644 index 000000000000..3d595ead4170 --- /dev/null +++ b/test-data/unit/check-unpack_iterable.test @@ -0,0 +1,28 @@ +[case checkiter] +# flags: --python-version 3.11 +import typing + +class Spam: + def __iter__(self, /) -> typing.Iterator[int]: + yield 1 + +a = Spam() + +# list[int] TODO: fix +# reveal_type(list(a)) + +# list[int] +reveal_type([i for i in a]) # N: Revealed type is "builtins.list[builtins.int]" + +# list[int] +reveal_type([*(i for i in a)]) # N: Revealed type is "builtins.list[builtins.int]" + +# list[int] +reveal_type([*a.__iter__()]) # N: Revealed type is "builtins.list[builtins.int]" + +# list[Any] ??? +reveal_type([*a]) # N: Revealed type is "builtins.list[builtins.int]" + +b, = a +# int +reveal_type(b) # N: Revealed type is "builtins.int" \ No newline at end of file From 5c80fdf25f7622a6ff2c770b3eb650aab10ceaaa Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 14 Sep 2023 00:19:47 +0000 Subject: [PATCH 2/4] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- test-data/unit/check-unpack_iterable.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-data/unit/check-unpack_iterable.test b/test-data/unit/check-unpack_iterable.test index 3d595ead4170..1a1e02783676 100644 --- a/test-data/unit/check-unpack_iterable.test +++ b/test-data/unit/check-unpack_iterable.test @@ -25,4 +25,4 @@ reveal_type([*a]) # N: Revealed type is "builtins.list[builtins.int]" b, = a # int -reveal_type(b) # N: Revealed type is "builtins.int" \ No newline at end of file +reveal_type(b) # N: Revealed type is "builtins.int" From 3f7aebdebf4278fc5493b64f260c7b03ba511e67 Mon Sep 17 00:00:00 2001 From: JaylenLuc Date: Wed, 13 Sep 2023 20:56:27 -0700 Subject: [PATCH 3/4] fix issue #15747 2 --- mypy/argmap.py | 9 +++++++-- mypy/checkexpr.py | 5 ++++- mypy/constraints.py | 3 ++- mypy/infer.py | 11 ++++++++++- test-data/unit/check-unpack_iterable.test | 2 +- 5 files changed, 24 insertions(+), 6 deletions(-) diff --git a/mypy/argmap.py b/mypy/argmap.py index d0e51e695b0b..ad92a2864b42 100644 --- a/mypy/argmap.py +++ b/mypy/argmap.py @@ -163,7 +163,10 @@ def f(x: int, *args: str) -> None: ... import mypy def __init__( - self, context: ArgumentInferContext, checker: mypy.checker.TypeChecker = None + self, + context: ArgumentInferContext, + context_check: mypy.nodes.Context, + checker: mypy.checker.TypeChecker | None = None, ) -> None: # Next tuple *args index to use. self.tuple_index = 0 @@ -174,6 +177,8 @@ def __init__( # TypeChecker to check true argument types for iterables self.checker = checker + self.context_check = context_check + def expand_actual_type( self, actual_type: Type, @@ -224,7 +229,7 @@ def expand_actual_type( # get the true type of the arguments of the iterable iterable_item_type = ( self.checker.analyze_iterable_item_type_without_expression( - actual_type, self.context + actual_type, self.context_check )[1] ) return iterable_item_type diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 552fe6cc309b..31b7e8686cd2 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -1998,6 +1998,7 @@ def infer_function_type_arguments( pass1_args.append(arg) inferred_args, _ = infer_function_type_arguments( + context, self.chk, callee_type, pass1_args, @@ -2060,6 +2061,7 @@ def infer_function_type_arguments( # potentially involving free variables. # TODO: support the similar inference for return type context. poly_inferred_args, free_vars = infer_function_type_arguments( + context, self.chk, callee_type, arg_types, @@ -2141,6 +2143,7 @@ def infer_function_type_arguments_pass2( arg_types = self.infer_arg_types_in_context(callee_type, args, arg_kinds, formal_to_actual) inferred_args, _ = infer_function_type_arguments( + context, self.chk, callee_type, arg_types, @@ -2382,7 +2385,7 @@ def check_argument_types( """ check_arg = check_arg or self.check_arg # Keep track of consumed tuple *arg items. - mapper = ArgTypeExpander(self.argument_infer_context(), self.chk) + mapper = ArgTypeExpander(self.argument_infer_context(), context, self.chk) for i, actuals in enumerate(formal_to_actual): orig_callee_arg_type = get_proper_type(callee.arg_types[i]) diff --git a/mypy/constraints.py b/mypy/constraints.py index ffce8f90896b..4d4a472cc715 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -107,6 +107,7 @@ def __eq__(self, other: object) -> bool: def infer_constraints_for_callable( + context_check: mypy.nodes.Context, checker: mypy.checker.TypeChecker, callee: CallableType, arg_types: Sequence[Type | None], @@ -120,7 +121,7 @@ def infer_constraints_for_callable( Return a list of constraints. """ constraints: list[Constraint] = [] - mapper = ArgTypeExpander(context, checker) + mapper = ArgTypeExpander(context, context_check, checker) param_spec = callee.param_spec() param_spec_arg_types = [] diff --git a/mypy/infer.py b/mypy/infer.py index 3ca37cbf29c3..b25c3345aa5b 100644 --- a/mypy/infer.py +++ b/mypy/infer.py @@ -33,6 +33,7 @@ class ArgumentInferContext(NamedTuple): def infer_function_type_arguments( + context_check: mypy.nodes.Context, checker: mypy.checker.TypeChecker, callee_type: CallableType, arg_types: Sequence[Type | None], @@ -57,8 +58,16 @@ def infer_function_type_arguments( formal_to_actual: mapping from formal to actual variable indices """ # Infer constraints. + # pass Context into this constraints = infer_constraints_for_callable( - checker, callee_type, arg_types, arg_kinds, arg_names, formal_to_actual, context + context_check, + checker, + callee_type, + arg_types, + arg_kinds, + arg_names, + formal_to_actual, + context, ) # Solve constraints. diff --git a/test-data/unit/check-unpack_iterable.test b/test-data/unit/check-unpack_iterable.test index 3d595ead4170..1a1e02783676 100644 --- a/test-data/unit/check-unpack_iterable.test +++ b/test-data/unit/check-unpack_iterable.test @@ -25,4 +25,4 @@ reveal_type([*a]) # N: Revealed type is "builtins.list[builtins.int]" b, = a # int -reveal_type(b) # N: Revealed type is "builtins.int" \ No newline at end of file +reveal_type(b) # N: Revealed type is "builtins.int" From dab8f9bd0fa3650f8f7fe78767893071c7ddef9e Mon Sep 17 00:00:00 2001 From: JaylenLuc Date: Thu, 14 Sep 2023 11:31:21 -0700 Subject: [PATCH 4/4] fix issue #15747 4 --- mypy/argmap.py | 3 ++- mypy/infer.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/mypy/argmap.py b/mypy/argmap.py index ad92a2864b42..c8188a98fe6f 100644 --- a/mypy/argmap.py +++ b/mypy/argmap.py @@ -160,7 +160,8 @@ def f(x: int, *args: str) -> None: ... needs a separate instance since instances have per-call state. """ - import mypy + import mypy.checker + import mypy.nodes def __init__( self, diff --git a/mypy/infer.py b/mypy/infer.py index b25c3345aa5b..0a2128f1c7f1 100644 --- a/mypy/infer.py +++ b/mypy/infer.py @@ -29,7 +29,8 @@ class ArgumentInferContext(NamedTuple): iterable_type: Instance -import mypy +import mypy.checker +import mypy.nodes def infer_function_type_arguments(