-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add arguments to callee context in a call expression #3116
Conversation
mypy/checkexpr.py
Outdated
context_type = CallableType([self.accept(a) for a in e.args], e.arg_kinds, e.arg_names, | ||
ret_type=self.type_context[-1] or AnyType(), | ||
fallback=self.named_type('builtins.function')) | ||
callee_type = self.accept(e.callee, context_type) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it is too big a hammer to use on every call? should I add if isinstance(e.callee, LambdaExpr):
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably best to only do this for lambda expressions, as it could interact badly with generic functions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In such a case we may want to have a test that catches such an interaction (as if this PR is merged and someone opened an issue). But I am not sure how would such an interaction look like.
(lambda *, x, y: x + y)(x=1, y="") # E: Unsupported operand types for + ("int" and "str") | ||
[out] | ||
main:1: error: Incompatible types in assignment (expression has type "int", variable has type "str") | ||
main:1: error: Incompatible return value type (got "int", expected "str") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having two error messages is slightly awkward
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you know why we are getting two error messages here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because we check the lambda against the full context, including the return type, and it fails to check. Then we check the call expression against the variable, which again fails. Maybe it is possible to check the lambda with return type defined to be object
mypy/checkexpr.py
Outdated
context_type = CallableType([self.accept(a) for a in e.args], e.arg_kinds, e.arg_names, | ||
ret_type=self.type_context[-1] or AnyType(), | ||
fallback=self.named_type('builtins.function')) | ||
callee_type = self.accept(e.callee, context_type) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably best to only do this for lambda expressions, as it could interact badly with generic functions.
test-data/unit/check-inference.test
Outdated
@@ -686,7 +686,7 @@ g('a')() # E: List[str] not callable | |||
# call, would be preferable here. | |||
g(['a']) # E: Argument 1 to "g" has incompatible type List[str]; expected List[<uninhabited>] | |||
|
|||
h(g(['a'])) | |||
h(g(['a'])) # E: Argument 1 to "g" has incompatible type List[str]; expected List[<uninhabited>] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like a regression, and I suspect that there are other cases that could be affected. By special casing the fix to lambda expressions this should hopefully be fixed.
(lambda *, x, y: x + y)(x=1, y="") # E: Unsupported operand types for + ("int" and "str") | ||
[out] | ||
main:1: error: Incompatible types in assignment (expression has type "int", variable has type "str") | ||
main:1: error: Incompatible return value type (got "int", expected "str") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you know why we are getting two error messages here?
Now it's pretty surgical. But it bothers me that I do |
I think limiting the context to ((lambda x: x+1) if condition else (lambda x: x+2))("1") So I would like to understand what are the potential problems with always propagating the context. Maybe there's another way to avoid these problems. |
Agreed that the limitation makes this not very useful. The change in the output of a test case indicates that there was a behavior change elsewhere, and it might be because there is no type context when we infer the argument types, but I'm not sure. There is also the fact that the argument types are inferred repeatedly, which might result in poor performance when type checking large expressions, such as those that are machine-generated. I wouldn't recommend making this much more complicated, since the code around type checking calls is already very difficult to understand. Maybe also special casing |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I realized that this only works reliably with positional arguments.
mypy/checkexpr.py
Outdated
@@ -204,7 +204,13 @@ def visit_call_expr(self, e: CallExpr, allow_none_return: bool = False) -> Type: | |||
or isinstance(typ, NameExpr) and node and node.kind == nodes.TYPE_ALIAS): | |||
self.msg.type_arguments_not_allowed(e) | |||
self.try_infer_partial_type(e) | |||
callee_type = self.accept(e.callee, always_allow_any=True) | |||
if isinstance(e.callee, LambdaExpr): | |||
type_context = CallableType([self.accept(a) for a in e.args], e.arg_kinds, e.arg_names, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You need to take the argument kinds and names from the lambda expression (not the call expression) and map the actual argument to formal arguments. Currently this doesn't work correctly, for example:
(lambda s, i: s[i])(i=0, s='x')
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the updates! Looks good now.
Fix #3097.