Every type in a parameter restriction is effectively a covariant type parameter. One way to move forward is to make the covariance explicit via Upper-bounded free variables in def restrictions · Issue #11908 · crystal-lang/crystal · GitHub
The entire return type restriction is covariant. It must also name an actual valid type, so the following is currently illegal:
def foo(x : Array(Array)) : Array(Array)
x
end
foo([[1]]) # Error: method ::foo must return Array(Array(T)) but it is returning Array(Array(Int32))
Following the example Doubt about overloading - #4 by HertzDevil, if you know C++, the snippet in the OP would be analogous to:
std::convertible_to<std::variant<int, std::nullptr_t>> auto
foo(std::convertible_to<int> auto x) {
return
// x == 0 ? nullptr :
1 / x;
}
foo(3) + 1;