class X val x = new X val y = x: x.type object Overload { def foo(arg: Any) = 1 def foo(arg: x.type) = 2 } println(Overload.foo(x)) println(Overload.foo(y: y.type))
Explanation
If the method Overload.foo(x) is invoked, then overload resolution (SLS §6.26.3) comes into play. Both alternatives foo(Any) and foo(x.type) are potentially applicable candidates (based on the shape of the arguments). As a next step in overload resolution, the argument x is typed without an expected type and is checked against each candidate method for applicability. The type of the argument x is X (and not x.type) and therefore only the method foo(arg: Any) remains as an alternative and is chosen. This explains why Overload.foo(arg: Any) returns 1.
If a path y has singleton type x.type, then x.type and y.type are equivalent, i.e. they are interchangeable in all contexts (SLS §3.5.1). When Overload.foo(y : y.type) is invoked, then typing the argument results in y.type, and this type is compatible with the parameter type x.type and this variant is applicable. During overload resolution, the most specific alternative is chosen from the two remaining candidates, and this is foo(x.type).
Interesting: in the non-overloaded case the argument x is compatible with parameters of type x.type. This typing succeeds and therefore the method is applicable to the argument x without an explicit type ascription:
scala> def bar(arg: x.type) = 3
bar: (arg: x.type)Int
scala> bar(x)
res1: Int = 3