Skip to content

Commit 72d9ab6

Browse files
committed
Overhaul overload semantics, remove erasure, add union math
This pull request: 1. Modifies how mypy handles overloads to match the proposal I made in the typing repo. 2. Starts removing type erasure from overload checks. 3. Adds support for basic union math. 4. Makes overloads respect keyword-only args This pull request does NOT implement the following: 1. Special-casing descriptors 2. Improving how operator methods are handled: I've tweaked one or two methods to take advantage of some of the changes I made in this PR, but stayed away from making any drastic changes for now. 3. Detecting partially overlapping argument types -- e.g. code like: @overload def f(x: Union[A, B]) -> str: ... @overload def f(x: Union[B, C]) -> int: ... @overload def g(x: Tuple[int, ...]) -> str: ... @overload def g(x: Tuple[int, int]) -> int: ... 4. Detecting overlapping "argument counts". For example, we should flag the following as an error since the first alternative could potentially overlap with the second. @overload def f(*args: int) -> int: ... @overload def f(x: int, y: int, z: int) -> str: ... 5. The "is-more-precise" relation. It's working in most normal cases but still contains a few bugs, mostly relating to type vars. For example, this currently isn't being flagged as an error: class Wrapper(Generic[T]): @overload def f(self, x: int) -> int: ... # No error? @overload def f(self, x: T) -> str: ... (This PR does the right thing if 'T' isn't bound to a containing class though:) class Wrapper: @overload def f(self, x: int, y: int) -> int: ... # Error @overload def f(self, x: T, y: T) -> str: ... Currently, what I'm doing is using the existing `is_more_precise` method, which calls `is_proper_subtype`. I think i'll either need to rewrite `is_more_precise` to do what I want with typevars or find a way of forcing the two methods to unify their typevars before running the `is_proper_subtype` check. The plan is to address these 5 TODOs in future pull requests. Items 1 and 2 are basically orthogonal to the overloads overhaul; items 3, 4, and 5 basically boil down to finding ways to teach mypy to detect if one thing is *potentially* compatible with another. For example, mypy contains code to tell if one type is *definitely* a subtype of another; fixing items 3 and 5 involve writing code to check if a type is *potentially* a subtype of another.
1 parent 22ee713 commit 72d9ab6

File tree

11 files changed

+846
-182
lines changed

11 files changed

+846
-182
lines changed

mypy/checker.py

Lines changed: 122 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
from mypy import messages
4040
from mypy.subtypes import (
4141
is_subtype, is_equivalent, is_proper_subtype, is_more_precise,
42-
restrict_subtype_away, is_subtype_ignoring_tvars, is_callable_subtype,
42+
restrict_subtype_away, is_subtype_ignoring_tvars, is_callable_compatible,
4343
unify_generic_callable, find_member
4444
)
4545
from mypy.maptype import map_instance_to_supertype
@@ -407,22 +407,32 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None:
407407
if defn.info:
408408
self.check_method_override(defn)
409409
self.check_inplace_operator_method(defn)
410-
self.check_overlapping_overloads(defn)
410+
if not defn.is_property:
411+
self.check_overlapping_overloads(defn)
411412
return None
412413

413414
def check_overlapping_overloads(self, defn: OverloadedFuncDef) -> None:
414415
# At this point we should have set the impl already, and all remaining
415416
# items are decorators
416417
for i, item in enumerate(defn.items):
418+
# TODO overloads involving decorators
417419
assert isinstance(item, Decorator)
418420
sig1 = self.function_type(item.func)
421+
419422
for j, item2 in enumerate(defn.items[i + 1:]):
420-
# TODO overloads involving decorators
421423
assert isinstance(item2, Decorator)
422424
sig2 = self.function_type(item2.func)
423-
if is_unsafe_overlapping_signatures(sig1, sig2):
424-
self.msg.overloaded_signatures_overlap(i + 1, i + j + 2,
425-
item.func)
425+
426+
assert isinstance(sig1, CallableType)
427+
assert isinstance(sig2, CallableType)
428+
429+
if not are_argument_counts_overlapping(sig1, sig2):
430+
continue
431+
432+
if if_overload_can_never_match(sig1, sig2):
433+
self.msg.overloaded_signature_will_never_match(i + 1, i + j + 2, item2.func)
434+
elif is_unsafe_overlapping_overload_signatures(sig1, sig2):
435+
self.msg.overloaded_signatures_overlap(i + 1, i + j + 2, item.func)
426436
if defn.impl:
427437
if isinstance(defn.impl, FuncDef):
428438
impl_type = defn.impl.type
@@ -437,7 +447,8 @@ def check_overlapping_overloads(self, defn: OverloadedFuncDef) -> None:
437447

438448
assert isinstance(impl_type, CallableType)
439449
assert isinstance(sig1, CallableType)
440-
if not is_callable_subtype(impl_type, sig1, ignore_return=True):
450+
if not is_callable_compatible(impl_type, sig1,
451+
is_compat=is_subtype, ignore_return=True):
441452
self.msg.overloaded_signatures_arg_specific(i + 1, defn.impl)
442453
impl_type_subst = impl_type
443454
if impl_type.variables:
@@ -1038,8 +1049,8 @@ def check_overlapping_op_methods(self,
10381049
fallback=self.named_type('builtins.function'),
10391050
name=reverse_type.name)
10401051

1041-
if is_unsafe_overlapping_signatures(forward_tweaked,
1042-
reverse_tweaked):
1052+
if is_unsafe_overlapping_operator_signatures(
1053+
forward_tweaked, reverse_tweaked):
10431054
self.msg.operator_method_signatures_overlap(
10441055
reverse_class, reverse_name,
10451056
forward_base, forward_name, context)
@@ -1832,10 +1843,18 @@ def check_multi_assignment_from_union(self, lvalues: List[Expression], rvalue: E
18321843
# Bind a union of types collected in 'assignments' to every expression.
18331844
if isinstance(expr, StarExpr):
18341845
expr = expr.expr
1835-
types, declared_types = zip(*items)
1846+
1847+
# TODO: See todo in binder.py, ConditionalTypeBinder.assign_type
1848+
# It's unclear why the 'declared_type' param is sometimes 'None'
1849+
clean_items = [] # type: List[Tuple[Type, Type]]
1850+
for type, declared_type in items:
1851+
assert declared_type is not None
1852+
clean_items.append((type, declared_type))
1853+
1854+
types, declared_types = zip(*clean_items)
18361855
self.binder.assign_type(expr,
1837-
UnionType.make_simplified_union(types),
1838-
UnionType.make_simplified_union(declared_types),
1856+
UnionType.make_simplified_union(list(types)),
1857+
UnionType.make_simplified_union(list(declared_types)),
18391858
False)
18401859
for union, lv in zip(union_types, self.flatten_lvalues(lvalues)):
18411860
# Properly store the inferred types.
@@ -3547,18 +3566,96 @@ def type(self, type: Type) -> Type:
35473566
return expand_type(type, self.map)
35483567

35493568

3550-
def is_unsafe_overlapping_signatures(signature: Type, other: Type) -> bool:
3551-
"""Check if two signatures may be unsafely overlapping.
3569+
def are_argument_counts_overlapping(t: CallableType, s: CallableType) -> bool:
3570+
"""Can a single call match both t and s, based just on positional argument counts?
3571+
"""
3572+
min_args = max(t.min_args, s.min_args)
3573+
max_args = min(t.max_possible_positional_args(), s.max_possible_positional_args())
3574+
return min_args <= max_args
35523575

3553-
Two signatures s and t are overlapping if both can be valid for the same
3576+
3577+
def is_unsafe_overlapping_overload_signatures(signature: CallableType,
3578+
other: CallableType) -> bool:
3579+
"""Check if two overloaded function signatures may be unsafely overlapping.
3580+
3581+
We consider two functions 's' and 't' to be unsafely overlapping both
3582+
of the following are true:
3583+
3584+
1. s's parameters are all more precise or partially overlapping with t's
3585+
1. s's return type is NOT a subtype of t's.
3586+
3587+
both can be valid for the same
35543588
statically typed values and the return types are incompatible.
35553589
3590+
Assumes that 'signature' appears earlier in the list of overload
3591+
alternatives then 'other' and that their argument counts are overlapping.
3592+
"""
3593+
# TODO: Handle partially overlapping parameter types and argument counts
3594+
#
3595+
# For example, the signatures "f(x: Union[A, B]) -> int" and "f(x: Union[B, C]) -> str"
3596+
# is unsafe: the parameter types are partially overlapping.
3597+
#
3598+
# To fix this, we need to either modify meet.is_overlapping_types or add a new
3599+
# function and use "is_more_precise(...) or is_partially_overlapping(...)" for the is_compat
3600+
# checks.
3601+
#
3602+
# Similarly, the signatures "f(x: A, y: A) -> str" and "f(*x: A) -> int" are also unsafe:
3603+
# the parameter *counts* or arity are partially overlapping.
3604+
#
3605+
# To fix this, we need to modify is_callable_compatible so it can optionally detect
3606+
# functions that are *potentially* compatible rather then *definitely* compatible.
3607+
3608+
# The reason we repeat this check twice is so we can do a slightly better job of
3609+
# checking for potentially overlapping param counts. Both calls will actually check
3610+
# the param and return types in the same "direction" -- the only thing that differs
3611+
# is how is_callable_compatible checks non-positional arguments.
3612+
return (is_callable_compatible(signature, other,
3613+
is_compat=is_more_precise,
3614+
is_compat_return=lambda l, r: not is_subtype(l, r),
3615+
check_args_covariantly=True) or
3616+
is_callable_compatible(other, signature,
3617+
is_compat=is_more_precise,
3618+
is_compat_return=lambda l, r: not is_subtype(r, l)))
3619+
3620+
3621+
def if_overload_can_never_match(signature: CallableType, other: CallableType) -> bool:
3622+
"""Check if the 'other' method can never be matched due to 'signature'.
3623+
3624+
This can happen if signature's parameters are all strictly broader then
3625+
other's parameters.
3626+
3627+
Assumes that both signatures have overlapping argument counts.
3628+
"""
3629+
return is_callable_compatible(signature, other,
3630+
is_compat=is_more_precise,
3631+
ignore_return=True)
3632+
3633+
3634+
def is_unsafe_overlapping_operator_signatures(signature: Type, other: Type) -> bool:
3635+
"""Check if two operator method signatures may be unsafely overlapping.
3636+
3637+
Two signatures s and t are overlapping if both can be valid for the same
3638+
statically typed values and the return types are incompatible.
3639+
35563640
Assume calls are first checked against 'signature', then against 'other'.
35573641
Thus if 'signature' is more general than 'other', there is no unsafe
35583642
overlapping.
35593643
3560-
TODO If argument types vary covariantly, the return type may vary
3561-
covariantly as well.
3644+
TODO: Clean up this function and make it not perform type erasure.
3645+
3646+
Context: This function was previously used to make sure both overloaded
3647+
functions and operator methods were not unsafely overlapping.
3648+
3649+
We changed the semantics for we should handle overloaded definitions,
3650+
but not operator functions. (We can't reuse the same semantics for both:
3651+
the overload semantics are too restrictive here).
3652+
3653+
We should rewrite this method so that:
3654+
3655+
1. It uses many of the improvements made to overloads: in particular,
3656+
eliminating type erasure.
3657+
3658+
2. It contains just the logic necessary for operator methods.
35623659
"""
35633660
if isinstance(signature, CallableType):
35643661
if isinstance(other, CallableType):
@@ -3601,12 +3698,11 @@ def is_more_general_arg_prefix(t: FunctionLike, s: FunctionLike) -> bool:
36013698
"""Does t have wider arguments than s?"""
36023699
# TODO should an overload with additional items be allowed to be more
36033700
# general than one with fewer items (or just one item)?
3604-
# TODO check argument kinds and otherwise make more general
36053701
if isinstance(t, CallableType):
36063702
if isinstance(s, CallableType):
3607-
t, s = unify_generic_callables(t, s)
3608-
return all(is_proper_subtype(args, argt)
3609-
for argt, args in zip(t.arg_types, s.arg_types))
3703+
return is_callable_compatible(t, s,
3704+
is_compat=is_proper_subtype,
3705+
ignore_return=True)
36103706
elif isinstance(t, FunctionLike):
36113707
if isinstance(s, FunctionLike):
36123708
if len(t.items()) == len(s.items()):
@@ -3615,29 +3711,6 @@ def is_more_general_arg_prefix(t: FunctionLike, s: FunctionLike) -> bool:
36153711
return False
36163712

36173713

3618-
def unify_generic_callables(t: CallableType,
3619-
s: CallableType) -> Tuple[CallableType,
3620-
CallableType]:
3621-
"""Make type variables in generic callables the same if possible.
3622-
3623-
Return updated callables. If we can't unify the type variables,
3624-
return the unmodified arguments.
3625-
"""
3626-
# TODO: Use this elsewhere when comparing generic callables.
3627-
if t.is_generic() and s.is_generic():
3628-
t_substitutions = {}
3629-
s_substitutions = {}
3630-
for tv1, tv2 in zip(t.variables, s.variables):
3631-
# Are these something we can unify?
3632-
if tv1.id != tv2.id and is_equivalent_type_var_def(tv1, tv2):
3633-
newdef = TypeVarDef.new_unification_variable(tv2)
3634-
t_substitutions[tv1.id] = TypeVarType(newdef)
3635-
s_substitutions[tv2.id] = TypeVarType(newdef)
3636-
return (cast(CallableType, expand_type(t, t_substitutions)),
3637-
cast(CallableType, expand_type(s, s_substitutions)))
3638-
return t, s
3639-
3640-
36413714
def is_equivalent_type_var_def(tv1: TypeVarDef, tv2: TypeVarDef) -> bool:
36423715
"""Are type variable definitions equivalent?
36433716
@@ -3653,17 +3726,17 @@ def is_equivalent_type_var_def(tv1: TypeVarDef, tv2: TypeVarDef) -> bool:
36533726

36543727

36553728
def is_same_arg_prefix(t: CallableType, s: CallableType) -> bool:
3656-
# TODO check argument kinds
3657-
return all(is_same_type(argt, args)
3658-
for argt, args in zip(t.arg_types, s.arg_types))
3729+
return is_callable_compatible(t, s,
3730+
is_compat=is_same_type,
3731+
ignore_return=True,
3732+
check_args_covariantly=True,
3733+
ignore_pos_arg_names=True)
36593734

36603735

36613736
def is_more_precise_signature(t: CallableType, s: CallableType) -> bool:
36623737
"""Is t more precise than s?
3663-
36643738
A signature t is more precise than s if all argument types and the return
36653739
type of t are more precise than the corresponding types in s.
3666-
36673740
Assume that the argument kinds and names are compatible, and that the
36683741
argument counts are overlapping.
36693742
"""

0 commit comments

Comments
 (0)