I’ve thought of this overnight and I think I may have found an algorithm to check if a set of overloaded methods aren’t ambiguous 
Some assumptions
In the main part of my algorithm description I assumed we have got no other knowledge of an overloaded methods set than their argument names, types and kinds (‘position only’,‘positional’,‘vararg’,‘keyword only’ and ‘keyword vararg’). Also, for simplicity, I assumed method’s arguments have got no default values (later I’ll show a version without this assumption).
Positional arguments
Let’s say we have got this:
class Klass:
@overload
def F(a:int, /, b:str) #1
@overload
def F(a:int, /, b:str, c:list, *args:bool) #2
@overload
def F(a:str) #3
@overload
def F(a:int, b:str, *args) #4
(1) From it we create a set of lists containing the types of arguments:
F = {
[int, str], #1
[int, str, list, (bool, ...)], #2
[str], #3
[int, str, (Any, ...)] #4
}
Some notes:
- Position only and positional arguments are treated the same and are added in the order they are appearing in the source code,
- If an overload contains an
*args argument, we make an “infinite” list. I mean we thread the list as if it had an infinite list of the type of *args appended at the end,
- Per convention if the
*args has got no written type, we give it the Any type
(2) We divide the set into subsets whose the first type in a list is identical in all lists in a subset:
F = {
{
[int, str],
[int, str, list, (bool, ...)],
[int, str, (Any, ...)]
},
{
[str]
}
}
(3) We remove the first item in every list (unless the item is our infinite ending):
F = {
{
[str],
[str, list, (bool, ...)],
[str, (Any, ...)]
},
{
[]
}
}
If after this one subset contains an empty list that is the only list in this subset, then we mark the owner method of it (in the above case it is #3) as “unambiguous”. We then remove the subset as it won’t be used in our future calculations.
Now we do the steps (2) and (3) again:
F = {
[],
[list, (bool, ...)],
[(Any, ...)]
}
If we are at the situation when there are lists that are either empty or contain an infinite ending and there are more than one of such a list in a subset, then we mark every owner of such lists as “ambiguous”, and remove them from the set.
Now we have got this situation:
F = {
[list, (bool, ...)]
}
Because it is the only member of the set we mark its owner method as “unambiguous” and finish the algorithm.
In the next post I’ll show how to deal with keyword arguments.