I see your point. So maybe this would work:
- If you have a list of
@overload
and you mark one or more of them as@type_error
, all of the overloads marked as@type_error
must be in a single group, i.e., either:
- the first set of overloads with
@type_error
is followed by overloads without@type_error
; or, - all the overloads without
@type_error
come first, followed by a set of overloads without@type_error
.
- When matching a call to an overloaded function/method, the type checker first tries to match everything in the first group, which will either have all
@type_error
overloads, or none. If there is a match among those overloads, then do not try to match the overloads in the second group. If there is no match among the first group, move to the second group, and no longer consider the first group. - if
@type_error
is present, in the overloads (whether in the first group or the second group), the type checker tries to match any of the ones labeled as@type_error
using the same rules as is done today with overloads. If any match, an error is produced.
So I think my example above will work with this rule, and you could do Counter.__init__()
as follows (leaving out some of the other matches):
@overload
def __init__(self, Mapping[T, int]): ...
@overload
@type_error
def __init__(self, Mapping[T, object]): ...
So then you have 2 options as someone writing overloads. Either you put the group of overloads that are acceptable first, and errors second, or you put the ones that are errors first, and ones that are acceptable second.