I see your point. So maybe this would work:
- If you have a list of
@overloadand you mark one or more of them as@type_error, all of the overloads marked as@type_errormust be in a single group, i.e., either:
- the first set of overloads with
@type_erroris followed by overloads without@type_error; or, - all the overloads without
@type_errorcome 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_erroroverloads, 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_erroris 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_errorusing 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.