Ambiguous overloadings with inheritance

class A; end
class B < A; end
class C; end
class D < C; end

def f(x1 : B, x2 : C); 1 end
def f(x1 : A, x2 : D); 2 end

puts f(B.new, D.new) #=> 1

This code will call the first function defined and print 1. If you change the order of two function definitions, it print 2 instead.
But for C++, same code cause an error: error: call of overloaded ‘fun(B&, D&)’ is ambiguous

#include <iostream>

class A {};
class B : public A {};
class C {};
class D : public C {};

int fun(A x1, D x2) {return 1;}
int fun(B x1, C x2) {return 2;}

int main() {
	B b;
	D d;
	printf("%d", fun(b, d));
}

Why crystal don’t raise the same error as C++?

The way Crystal works, it tries to sort overloads if possible, otherwise the first overload that matches the call argument is picked up. This is not by design, but it’s how things work now.

I think we never bothered handling this case because it’s pretty uncommon. Is there a real use case for this?

It’s interesting to see C++ handles this case well. I guess we can do the same thing for Crystal. But how do you disambiguate the call in C++?

It’s not a real use case :smiley:
In my opinion this case should cause the failure of compilation because ambiguity is not allowed in programming.

In Crystal’s case there’s no ambiguity. If two overloads are ambiguous, the first one is chosen. That removes the ambiguity :slight_smile:

Of course this isn’t perfect, but it’s deterministic.

2 Likes

And there is no perfect one-fits-all solution for solving ambiguities: either you restrict valid cases, or you use a predictable heuristic (like “pick the first one”). Part of Crystal’s power comes from its flexibility, which is undoubtedly a two-sided sword: subtlety changing the type of an object may silently change the overloading that is picked, and the behavior of the program. If, on the other hand, we decide to have a stricter language, then the compiler will annoy you when there’s no real reason for it.

I’m not saying Crystal’s approach is right here, I’m just pointing out that the cost of such checks aren’t free. And many times they even won’t remove entirely the ambiguity. Many compilers attempt to be perfectly unambiguous but are nevertheless easy to fool.

3 Likes

Maybe a warning could help in such cases that the compiler solves the ambiguity by picking the first one? :thinking:

2 Likes