Skip to content

Commit

Permalink
Preserve can_be_true and can_be_false when copying types (#7991)
Browse files Browse the repository at this point in the history
This pull request modifies the `last_known_value` eraser so it
always copies over the previously inferred value of the
`can_be_true` and `can_be_false` fields.

This isn't entirely typesafe -- for example, we can sometimes
accidentally infer the wrong result if we try reassigning variables
or call a mutating function :

```
    def bad1(a: List[str], b: bool) -> bool:
        x = a and b
        a = ["foo"]
        y: bool
        return x or y   # Should be an error

    def bad2(a: List[str], b: bool) -> bool:
        x = a and b
        a.append("foo")
        y: bool
        return x or y   # Should be an error
```

But this was apparently the old behavior/something mypy wasn't
previously able to detect, so I guess this is fine for now.

I also decided against modifying every `copy_modified` method in
all the type classes because of this reason: I wanted to limit
the spread of this potentially misleading additional inferred info.

Fixes #7986, probably.
  • Loading branch information
Michael0x2a authored and JukkaL committed Nov 21, 2019
1 parent 989626a commit a2e010d
Show file tree
Hide file tree
Showing 2 changed files with 13 additions and 1 deletion.
5 changes: 4 additions & 1 deletion mypy/erasetype.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,13 @@ class LastKnownValueEraser(TypeTranslator):
def visit_instance(self, t: Instance) -> Type:
if not t.last_known_value and not t.args:
return t
return t.copy_modified(
new_t = t.copy_modified(
args=[a.accept(self) for a in t.args],
last_known_value=None,
)
new_t.can_be_true = t.can_be_true
new_t.can_be_false = t.can_be_false
return new_t

def visit_type_alias_type(self, t: TypeAliasType) -> Type:
# Type aliases can't contain literal values, because they are
Expand Down
9 changes: 9 additions & 0 deletions test-data/unit/check-expressions.test
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,15 @@ b = bool()
reveal_type(s and b or b) # N: Revealed type is 'builtins.bool'
[builtins fixtures/bool.pyi]

[case testRestrictedBoolAndOrWithGenerics]
from typing import List

def f(a: List[str], b: bool) -> bool:
x = a and b
y: bool
return reveal_type(x or y) # N: Revealed type is 'builtins.bool'
[builtins fixtures/list.pyi]

[case testNonBooleanOr]
c, d, b = None, None, None # type: (C, D, bool)
if int():
Expand Down

0 comments on commit a2e010d

Please sign in to comment.