Overloading resolution
In a call p(args) the routine p that matches best is selected. If multiple routines match equally well, the ambiguity is reported during semantic analysis.
Every arg in args needs to match. There are multiple different categories how an argument can match. Let f be the formal parameter’s type and a the type of the argument.
- Exact match: a and f are of the same type.
- Literal match: a is an integer literal of value v and f is a signed or unsigned integer type and v is in f’s range. Or: a is a floating point literal of value v and f is a floating point type and v is in f’s range.
- Generic match: f is a generic type and a matches, for instance a is int and f is a generic (constrained) parameter type (like in [T] or [T: int|char].
- Subrange or subtype match: a is a range[T] and T matches f exactly. Or: a is a subtype of f.
- Integral conversion match: a is convertible to f and f and a is some integer or floating point type.
- Conversion match: a is convertible to f, possibly via a user defined converter.
These matching categories have a priority: An exact match is better than a literal match and that is better than a generic match etc. In the following count(p, m) counts the number of matches of the matching category m for the routine p.
A routine p matches better than a routine q if the following algorithm returns true:
for each matching category m in ["exact match", "literal match",
"generic match", "subtype match",
"integral match", "conversion match"]:
if count(p, m) > count(q, m): return true
elif count(p, m) == count(q, m):
discard "continue with next category m"
else:
return false
return "ambiguous"
Some examples:
proc takesInt(x: int) = echo "int"
proc takesInt[T](x: T) = echo "T"
proc takesInt(x: int16) = echo "int16"
takesInt(4) # "int"
var x: int32
takesInt(x) # "T"
var y: int16
takesInt(y) # "int16"
var z: range[0..4] = 0
takesInt(z) # "T"
If this algorithm returns “ambiguous” further disambiguation is performed: If the argument a matches both the parameter type f of p and g of q via a subtyping relation, the inheritance depth is taken into account:
type
A = object of RootObj
B = object of A
C = object of B
proc p(obj: A) =
echo "A"
proc p(obj: B) =
echo "B"
var c = C()
# not ambiguous, calls 'B', not 'A' since B is a subtype of A
# but not vice versa:
p(c)
proc pp(obj: A, obj2: B) = echo "A B"
proc pp(obj: B, obj2: A) = echo "B A"
# but this is ambiguous:
pp(c, c)
Likewise for generic matches the most specialized generic type (that still matches) is preferred:
proc gen[T](x: ref ref T) = echo "ref ref T"
proc gen[T](x: ref T) = echo "ref T"
proc gen[T](x: T) = echo "T"
var ri: ref int
gen(ri) # "ref T"