-
-
Notifications
You must be signed in to change notification settings - Fork 3k
Explicit types for bindables ("Tighten Types" continued) #2238
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
Changes from all commits
d01d2f1
8966207
a99dfcb
e742453
d634539
06fe586
1f8adb8
98167cd
0dab466
6f4b29c
8e9ea44
1989243
201c744
208c16d
5f36c72
5925fd0
4dfc2c7
43e9f03
2396458
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,12 @@ | |
from mypy.join import join_simple | ||
from mypy.sametypes import is_same_type | ||
|
||
from mypy.nodes import IndexExpr, MemberExpr, NameExpr | ||
|
||
|
||
BindableTypes = (IndexExpr, MemberExpr, NameExpr) | ||
BindableExpression = Union[IndexExpr, MemberExpr, NameExpr] | ||
|
||
|
||
class Frame(Dict[Key, Type]): | ||
"""A Frame represents a specific point in the execution of a program. | ||
|
@@ -92,7 +98,7 @@ def push_frame(self) -> Frame: | |
self.options_on_return.append([]) | ||
return f | ||
|
||
def _push(self, key: Key, type: Type, index: int=-1) -> None: | ||
def _put(self, key: Key, type: Type, index: int=-1) -> None: | ||
self.frames[index][key] = type | ||
|
||
def _get(self, key: Key, index: int=-1) -> Type: | ||
|
@@ -103,19 +109,23 @@ def _get(self, key: Key, index: int=-1) -> Type: | |
return self.frames[i][key] | ||
return None | ||
|
||
def push(self, node: Node, typ: Type) -> None: | ||
if not node.literal: | ||
def put(self, expr: Expression, typ: Type) -> None: | ||
# TODO: replace with isinstance(expr, BindableTypes) | ||
if not isinstance(expr, (IndexExpr, MemberExpr, NameExpr)): | ||
return | ||
key = node.literal_hash | ||
if not expr.literal: | ||
return | ||
key = expr.literal_hash | ||
if key not in self.declarations: | ||
self.declarations[key] = self.get_declaration(node) | ||
assert isinstance(expr, BindableTypes) | ||
self.declarations[key] = get_declaration(expr) | ||
self._add_dependencies(key) | ||
self._push(key, typ) | ||
self._put(key, typ) | ||
|
||
def unreachable(self) -> None: | ||
self.frames[-1].unreachable = True | ||
|
||
def get(self, expr: Union[Expression, Var]) -> Type: | ||
def get(self, expr: Expression) -> Type: | ||
return self._get(expr.literal_hash) | ||
|
||
def is_unreachable(self) -> bool: | ||
|
@@ -163,7 +173,7 @@ def update_from_options(self, frames: List[Frame]) -> bool: | |
for other in resulting_values[1:]: | ||
type = join_simple(self.declarations[key], type, other) | ||
if not is_same_type(type, current_value): | ||
self._push(key, type) | ||
self._put(key, type) | ||
changed = True | ||
|
||
self.frames[-1].unreachable = not frames | ||
|
@@ -189,19 +199,13 @@ def pop_frame(self, can_skip: bool, fall_through: int) -> Frame: | |
|
||
return result | ||
|
||
def get_declaration(self, expr: Node) -> Type: | ||
if isinstance(expr, RefExpr) and isinstance(expr.node, Var): | ||
type = expr.node.type | ||
if isinstance(type, PartialType): | ||
return None | ||
return type | ||
else: | ||
return None | ||
|
||
def assign_type(self, expr: Expression, | ||
type: Type, | ||
declared_type: Type, | ||
restrict_any: bool = False) -> None: | ||
# TODO: replace with isinstance(expr, BindableTypes) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you file an issue for that regression? What does reveal_type(BindableTypes) show at this point? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Filed: #3068. It will show |
||
if not isinstance(expr, (IndexExpr, MemberExpr, NameExpr)): | ||
return None | ||
if not expr.literal: | ||
return | ||
self.invalidate_dependencies(expr) | ||
|
@@ -226,16 +230,16 @@ def assign_type(self, expr: Expression, | |
and not restrict_any): | ||
pass | ||
elif isinstance(type, AnyType): | ||
self.push(expr, declared_type) | ||
self.put(expr, declared_type) | ||
else: | ||
self.push(expr, type) | ||
self.put(expr, type) | ||
|
||
for i in self.try_frames: | ||
# XXX This should probably not copy the entire frame, but | ||
# just copy this variable into a single stored frame. | ||
self.allow_jump(i) | ||
|
||
def invalidate_dependencies(self, expr: Expression) -> None: | ||
def invalidate_dependencies(self, expr: BindableExpression) -> None: | ||
"""Invalidate knowledge of types that include expr, but not expr itself. | ||
|
||
For example, when expr is foo.bar, invalidate foo.bar.baz. | ||
|
@@ -246,11 +250,11 @@ def invalidate_dependencies(self, expr: Expression) -> None: | |
for dep in self.dependencies.get(expr.literal_hash, set()): | ||
self._cleanse_key(dep) | ||
|
||
def most_recent_enclosing_type(self, expr: Expression, type: Type) -> Type: | ||
def most_recent_enclosing_type(self, expr: BindableExpression, type: Type) -> Type: | ||
if isinstance(type, AnyType): | ||
return self.get_declaration(expr) | ||
return get_declaration(expr) | ||
key = expr.literal_hash | ||
enclosers = ([self.get_declaration(expr)] + | ||
enclosers = ([get_declaration(expr)] + | ||
[f[key] for f in self.frames | ||
if key in f and is_subtype(type, f[key])]) | ||
return enclosers[-1] | ||
|
@@ -334,3 +338,11 @@ def top_frame_context(self) -> Iterator[Frame]: | |
assert len(self.frames) == 1 | ||
yield self.push_frame() | ||
self.pop_frame(True, 0) | ||
|
||
|
||
def get_declaration(expr: BindableExpression) -> Type: | ||
if isinstance(expr, RefExpr) and isinstance(expr.node, Var): | ||
type = expr.node.type | ||
if not isinstance(type, PartialType): | ||
return type | ||
return None |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -160,18 +160,13 @@ def analyze_ref_expr(self, e: RefExpr, lvalue: bool = False) -> Type: | |
return result | ||
|
||
def analyze_var_ref(self, var: Var, context: Context) -> Type: | ||
if not var.type: | ||
if var.type: | ||
return var.type | ||
else: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just double-checking that what you changed here isn't an accidental merge conflict? You've done more here than just revers the sense of the check and swap the then/else clauses. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This part is related to #2222 - There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. BTW, I would like to extract literal-handling to a couple of functions, and avoid putting them in the Expression classes. I already have the branch ready. Will you consider it? It does not solve anything, but I think it's nice to have this information in a single place, instead of scattered around. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be worth calling out this "change" explicitly in the commit message, since it does not obviously look like a mere refactoring. |
||
if not var.is_ready and self.chk.in_checked_function(): | ||
self.chk.handle_cannot_determine_type(var.name(), context) | ||
# Implicit 'Any' type. | ||
return AnyType() | ||
else: | ||
# Look up local type of variable with type (inferred or explicit). | ||
val = self.chk.binder.get(var) | ||
if val is None: | ||
return var.type | ||
else: | ||
return val | ||
|
||
def visit_call_expr(self, e: CallExpr, allow_none_return: bool = False) -> Type: | ||
"""Type check a call expression.""" | ||
|
@@ -1954,7 +1949,7 @@ def check_for_comp(self, e: Union[GeneratorExpr, DictionaryComprehension]) -> No | |
|
||
if true_map: | ||
for var, type in true_map.items(): | ||
self.chk.binder.push(var, type) | ||
self.chk.binder.put(var, type) | ||
|
||
def visit_conditional_expr(self, e: ConditionalExpr) -> Type: | ||
cond_type = self.accept(e.cond) | ||
|
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 don't see the point of introducing this
BindableExpression
. It seems to duplicate information with whichExpression
s have (non-None
)literal
s. It should be fine to just useExpression
everywhere.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.
Lists of literals have non-None literals.
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.
Here's the branch with literal() and literal_hash() functions:
https://github.com/elazarg/mypy/blob/literal_hash/mypy/nodes.py#L2351-L2421