Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
KotlinIsland committed Dec 20, 2024
1 parent 6423ef7 commit 3cd64fc
Show file tree
Hide file tree
Showing 49 changed files with 303 additions and 239 deletions.
1 change: 1 addition & 0 deletions .idea/watcherTasks.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions mypy/binder.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,8 +311,8 @@ def assign_type(
) -> None:
# We should erase last known value in binder, because if we are using it,
# it means that the target is not final, and therefore can't hold a literal.
if not mypy.options._based:
type = remove_instance_last_known_values(type)
# HUUHHH?????
# type = remove_instance_last_known_values(type)

if self.type_assignments is not None:
# We are in a multiassign from union, defer the actual binding,
Expand Down
21 changes: 10 additions & 11 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -3514,12 +3514,7 @@ def check_assignment(
if not (isinstance(lvalue, NameExpr) and lvalue.is_special_form):
self.binder.assign_type(lvalue, rvalue_type, lvalue_type, False)

elif (
not mypy.options._based
and lvalue.node
and lvalue.node.is_inferred
and rvalue_type
):
elif lvalue.node and lvalue.node.is_inferred and rvalue_type:
# for literal values
# Don't use type binder for definitions of special forms, like named tuples.
if not (isinstance(lvalue, NameExpr) and lvalue.is_special_form):
Expand All @@ -3531,14 +3526,16 @@ def check_assignment(
if inferred:
type_context = self.get_variable_type_context(inferred)
rvalue_type = self.expr_checker.accept(rvalue, type_context=type_context)
if not mypy.options._based and not isinstance(lvalue.node.type, PartialType):
self.binder.assign_type(lvalue, rvalue_type, rvalue_type, False)
original_rvalue_type = rvalue_type
if not (
inferred.is_final
or (isinstance(lvalue, NameExpr) and lvalue.name == "__match_args__")
):
rvalue_type = remove_instance_last_known_values(rvalue_type)
self.infer_variable_type(inferred, lvalue, rvalue_type, rvalue)
if self.infer_variable_type(inferred, lvalue, rvalue_type, rvalue):
self.binder.assign_type(
lvalue, original_rvalue_type, original_rvalue_type, False
)

self.check_assignment_to_slots(lvalue)

Expand Down Expand Up @@ -4491,12 +4488,13 @@ def is_definition(self, s: Lvalue) -> bool:

def infer_variable_type(
self, name: Var, lvalue: Lvalue, init_type: Type, context: Context
) -> None:
) -> bool:
"""Infer the type of initialized variables from initializer type."""
valid = True
if isinstance(init_type, DeletedType):
self.msg.deleted_as_rvalue(init_type, context)
elif (
not is_valid_inferred_type(init_type, is_lvalue_final=name.is_final)
not (valid := is_valid_inferred_type(init_type, is_lvalue_final=name.is_final))
and not self.no_partial_types
):
# We cannot use the type of the initialization expression for full type
Expand All @@ -4523,6 +4521,7 @@ def infer_variable_type(
init_type = strip_type(init_type)

self.set_inferred_type(name, lvalue, init_type)
return valid

def infer_partial_type(self, name: Var, lvalue: Lvalue, init_type: Type) -> bool:
init_type = get_proper_type(init_type)
Expand Down
4 changes: 4 additions & 0 deletions mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -3677,10 +3677,14 @@ def visit_bytes_expr(self, e: BytesExpr) -> Type:

def visit_float_expr(self, e: FloatExpr) -> Type:
"""Type check a float literal (trivial)."""
if mypy.options._based:
return self.infer_literal_expr_type(e.value, "builtins.float")
return self.named_type("builtins.float")

def visit_complex_expr(self, e: ComplexExpr) -> Type:
"""Type check a complex literal."""
if mypy.options._based:
return self.infer_literal_expr_type(e.value, "builtins.complex")
return self.named_type("builtins.complex")

def visit_ellipsis(self, e: EllipsisExpr) -> Type:
Expand Down
39 changes: 26 additions & 13 deletions mypy/expandtype.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

from contextlib import contextmanager
from typing import Final, Iterable, Mapping, Sequence, TypeVar, cast, overload

from mypy.nodes import ARG_STAR, FakeInfo, Var
Expand Down Expand Up @@ -185,6 +186,16 @@ def __init__(self, variables: Mapping[TypeVarId, Type]) -> None:
super().__init__()
self.variables = variables
self.recursive_tvar_guard: dict[TypeVarId, Type | None] = {}
self._erase_literals = False

@contextmanager
def erase_literals(self):
_erase_literals = self._erase_literals
self._erase_literals = True
try:
yield
finally:
self._erase_literals = _erase_literals

def visit_unbound_type(self, t: UnboundType) -> Type:
return t
Expand All @@ -211,7 +222,8 @@ def visit_erased_type(self, t: ErasedType) -> Type:
return t

def visit_instance(self, t: Instance) -> Type:
args = self.expand_types_with_unpack(list(t.args))
with self.erase_literals():
args = self.expand_types_with_unpack(list(t.args))

if isinstance(t.type, FakeInfo):
# The type checker expands function definitions and bodies
Expand All @@ -238,7 +250,7 @@ def visit_type_var(self, t: TypeVarType) -> Type:
if t.id.is_self():
t = t.copy_modified(upper_bound=t.upper_bound.accept(self))
repl = self.variables.get(t.id, t)
if not mypy.options._based and isinstance(repl, ProperType) and isinstance(repl, Instance):
if self._erase_literals and isinstance(repl, ProperType) and isinstance(repl, Instance):
# TODO: do we really need to do this?
# If I try to remove this special-casing ~40 tests fail on reveal_type().
return repl.copy_modified(last_known_value=None)
Expand Down Expand Up @@ -410,17 +422,18 @@ def visit_callable_type(self, t: CallableType) -> CallableType:

var_arg = t.var_arg()
needs_normalization = False
if var_arg is not None and isinstance(var_arg.typ, UnpackType):
needs_normalization = True
arg_types = self.interpolate_args_for_unpack(t, var_arg.typ)
else:
arg_types = self.expand_types(t.arg_types)
expanded = t.copy_modified(
arg_types=arg_types,
ret_type=t.ret_type.accept(self),
type_guard=t.type_guard and cast(TypeGuardType, t.type_guard.accept(self)),
type_is=(t.type_is.accept(self) if t.type_is is not None else None),
)
with self.erase_literals():
if var_arg is not None and isinstance(var_arg.typ, UnpackType):
needs_normalization = True
arg_types = self.interpolate_args_for_unpack(t, var_arg.typ)
else:
arg_types = self.expand_types(t.arg_types)
expanded = t.copy_modified(
arg_types=arg_types,
ret_type=t.ret_type.accept(self),
type_guard=t.type_guard and cast(TypeGuardType, t.type_guard.accept(self)),
type_is=(t.type_is.accept(self) if t.type_is is not None else None),
)
if needs_normalization:
return expanded.with_normalized_var_args()
return expanded
Expand Down
3 changes: 2 additions & 1 deletion mypy/join.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
is_protocol_implementation,
is_subtype,
)
from mypy.typeops import make_simplified_union
from mypy.types import (
AnyType,
CallableType,
Expand Down Expand Up @@ -232,7 +233,7 @@ def join_simple(declaration: Type | None, s: Type, t: Type) -> ProperType:
if declaration is None or is_subtype(value, declaration):
return value

return declaration
return value


def trivial_join(s: Type, t: Type) -> Type:
Expand Down
2 changes: 1 addition & 1 deletion mypy/meet.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ def narrow_declared_type(declared: Type, narrowed: Type) -> Type:
# Special case: 'int' can't be narrowed down to a native int type such as
# i64, since they have different runtime representations.
return original_declared
if mypy.options._based and isinstance(narrowed, Instance) and narrowed.last_known_value:
if isinstance(narrowed, Instance) and narrowed.last_known_value:
return narrowed
return meet_types(original_declared, original_narrowed)
elif isinstance(declared, (TupleType, TypeType, LiteralType)):
Expand Down
1 change: 0 additions & 1 deletion mypy/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -633,7 +633,6 @@ def __init__(
column: int = -1,
scopename: str | None = None,
) -> None:
print(f"{fullname} {id}")
super().__init__(name, fullname, id, upper_bound, default, line, column)
assert values is not None, "No restrictions must be represented by empty list"
self.values = values
Expand Down
15 changes: 0 additions & 15 deletions mypy/typeshed/stdlib/builtins.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -388,9 +388,6 @@ class int:
def __abs__(self) -> int: ...
@type_function
def __hash__(self) -> int: ...
@overload
def __bool__(self: 0) -> False: ...
@overload
def __bool__(self) -> bool: ...
@type_function
def __index__(self) -> int: ...
Expand Down Expand Up @@ -501,9 +498,6 @@ class float:
def __abs__(self) -> float: ...
@type_function
def __hash__(self) -> int: ...
@overload
def __bool__(self: 0.0) -> False: ...
@overload
def __bool__(self) -> bool: ...

class complex:
Expand Down Expand Up @@ -537,9 +531,6 @@ class complex:
def __pos__(self) -> complex: ...
def __abs__(self) -> float: ...
def __hash__(self) -> int: ...
# @overload
# def __bool__(self: 0j) -> False: ...
# @overload
def __bool__(self) -> bool: ...
if sys.version_info >= (3, 11):
def __complex__(self) -> complex: ...
Expand Down Expand Up @@ -687,9 +678,6 @@ class str(Sequence[str]):
def __ne__(self, value: object, /) -> bool: ...
def __rmul__(self, value: SupportsIndex, /) -> str: ... # type: ignore[misc]
def __getnewargs__(self) -> tuple[str]: ...
@overload
def __bool__(self: Literal[""]) -> False: ...
@overload
def __bool__(self) -> bool: ...

class bytes(Sequence[int]):
Expand Down Expand Up @@ -840,9 +828,6 @@ class bytes(Sequence[int]):
@type_function
def __ge__(self, value: bytes, /) -> bool: ...
def __getnewargs__(self) -> tuple[bytes]: ...
@overload
def __bool__(self: Literal[b""]) -> False: ...
@overload
def __bool__(self) -> bool: ...
if sys.version_info >= (3, 11):
@type_function
Expand Down
10 changes: 9 additions & 1 deletion test-data/unit/check-based-assignment.test
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[case narrowToLiteral]
[case testNarrowToLiteral]
a = "a"
reveal_type(a) # Revealed type is "'a'" (narrowed from "str")
a = "b"
Expand All @@ -7,3 +7,11 @@ b = a
reveal_type(b) # Revealed type is "'b'" (narrowed from "str")
b = "c"
reveal_type(b) # Revealed type is "'c'" (narrowed from "str")


[case testNarrowToLiteralGeneric]
from helper import T
def f(t: T) -> T: ...

a = f(1)
reveal_type(a) # revealed type is "1" (narrowed from "int")
21 changes: 16 additions & 5 deletions test-data/unit/check-based-bare-literals.test
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,21 @@ reveal_type(c) # N: Revealed type is "False"
c2: False | str
reveal_type(c2) # N: Revealed type is "False | str"

d: 1.1 # E: Invalid type: float literals cannot be used as a type [valid-type]
d2: 1.1 | str # E: Invalid type: float literals cannot be used as a type [valid-type]

e: 1j # E: Invalid type: complex literals cannot be used as a type [valid-type]
e2: 1j | str # E: Invalid type: complex literals cannot be used as a type [valid-type]
d: 1.1
reveal_type(d) # N: Revealed type is "1.1"
d2: 1.1 | str
reveal_type(d2) # N: Revealed type is "1.1 | str"
[builtins fixtures/tuple.pyi]


[case testBareLiteralLiteralsComplex]
e: 1j
reveal_type(e) # N: Revealed type is "1j"
e2: 1j | str
reveal_type(e2) # N: Revealed type is "1j | str"
[builtins fixtures/dict.pyi]


[case testNoBareLiteralLiterals]
# flags: --python-version 3.10

Expand Down Expand Up @@ -109,3 +116,7 @@ import a
from typing import TypeVar
T = TypeVar("T", 1, 2)
a: 1 | 2


[case testBareLiteralComplex-writescache]
a: 1j
6 changes: 3 additions & 3 deletions test-data/unit/check-based-misc.test
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ main:3:5:3:31: error: Unused "type: ignore" comment [unused-ignore]

[case testNarrowOnInitialAssignment]
a: object = 1
reveal_type(a) # N: Revealed type is "int" (narrowed from "object")
reveal_type(a) # N: Revealed type is "1" (narrowed from "object")


[case testNarrowWithAny]
Expand All @@ -35,7 +35,7 @@ from typing import Any, Union
a: Union[int, Any]
if bool():
a = 1
reveal_type(a) # N: Revealed type is "int" (narrowed from "int | Any")
reveal_type(a) # N: Revealed type is "1" (narrowed from "int | Any")
b: Any = 1
reveal_type(b) # N: Revealed type is "Any"
c: Any
Expand Down Expand Up @@ -199,7 +199,7 @@ a = False
b: object
if a:
b = 1
reveal_type(b) # N: Revealed type is "int" (narrowed from "object")
reveal_type(b) # N: Revealed type is "1" (narrowed from "object")


[case testRedundantExprOnWhileTrue]
Expand Down
4 changes: 4 additions & 0 deletions test-data/unit/check-based-type-function.test
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ class int:
def m(self, other: "int") -> "int":
return self + 1

class dict: ...


[case typeFunctionReportError]
from basedtyping import type_function, TypeFunctionError
Expand All @@ -36,6 +38,7 @@ def main():
F(False)
reveal_type(F(True)) # E: this is an error [the-code] \
# N: Revealed type is "Never"
[builtins fixtures/exception.pyi]


[case typeFunctionReportRaises]
Expand All @@ -50,3 +53,4 @@ def main():
F(False)
reveal_type(F(True)) # E: Invocation raises TypeError: this is an error [call-raises] \
# N: Revealed type is "Never"
[builtins fixtures/exception.pyi]
2 changes: 1 addition & 1 deletion test-data/unit/check-based-type-render.test
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ a = 1 # E: Incompatible types in assignment (expression has type "int", variabl
[case testNarrowedFrom]
a: object
a = 1
reveal_type(a) # N: Revealed type is "int" (narrowed from "object")
reveal_type(a) # N: Revealed type is "1" (narrowed from "object")


[case testNarrowedFromClass]
Expand Down
36 changes: 36 additions & 0 deletions test-data/unit/check-based-typevar.test
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,39 @@ class A(Generic[T]): ...

a = A[Any]() # E: Expression type contains "Any" (has type "A[Any]") [no-any-expr]
[builtins fixtures/tuple.pyi]


[case testInferLiteralGeneric]
def f0[T](t: T) -> T:
return t


reveal_type(f0(17)) # N: Revealed type is "17"


def f1[T](t: T) -> (list[T],):
return ([t],)


reveal_type(f1(17)) # N: Revealed type is "(list[int])"


def f2[T](t: T) -> (T,):
return (t,)


reveal_type(f2(17)) # N: Revealed type is "(17)"


def f3[T](t: T) -> list[T]:
return [t]


reveal_type(f3(17)) # N: Revealed type is "list[int]"


def f4[T](t: T) -> (T, list[T]):
return [t]

# maybe this should be: (int, list[int])
reveal_type(f4(17)) # N: Revealed type is "(1, list[int])"
Loading

0 comments on commit 3cd64fc

Please sign in to comment.