This code compiles in 1.6.1 without errors:
abstract class Migration
module Meta
include Comparable(Meta)
abstract def timestamp : String
def <=>(other : self)
timestamp <=> other.timestamp
end
end
macro inherited
def self.timestamp : String
"dontcare"
end
end
extend Meta
def self.all : Array(Migration.class)
[M1] of Migration.class
end
end
class M1 < Migration
end
Migration.all.sort
See carc.in
But with 1.6.2 it fails with this compilation error:
199 | v = v1 <=> v2
^-
Error: expected argument #1 to 'M1.<=>' to be Migration::Meta, not Migration+.class
Overloads are:
- Migration::Meta#<=>(other : self)
The fix is to change the <=>
signature:
def <=>(other : self | Migration.class) # here
timestamp <=> other.timestamp
end
I don’t know why exactly the error occurs and can’t tell whether this is expected behavior or not, so i’m asking here first (My other projects compile and run fine btw, this is the only issue i found).
Reduced:
class Migration
module Meta
def foo(other : self)
end
end
extend Meta
end
class M1 < Migration
end
M1.foo(M1) # Error: no overload matches 'M1.foo' with type M1.class
This already fails on 1.6.1 (and earlier), but it can be worked around by an explicit cast to the virtual metaclass:
M1.as(Migration.class).foo(M1.as(Migration.class))
On 1.6.2 this work around does not work. So I guess 1.6.2 broke one edge case that happened to work before, while other similar cases did not even work.
1 Like
With the revert of #12632 the workaround works. (Note: there’s a typo in your workaround, the second .as(...)
should go inside the parens).
self
in restrictions always refers to the instance type of the type where the def is instantiated, so this example is not supposed to work; the restriction must be Meta
itself. The same goes for #<=>
in the original example.
2 Likes
You’re right, the current behavior seems correct then.
There are two possible fixes to the original code: change self
with self.class
in Meta
, or change everything to be Meta
as in:
abstract class Migration
module Meta
include Comparable(Meta)
abstract def timestamp : String
def <=>(other : Meta)
timestamp <=> other.timestamp
end
end
macro inherited
def self.timestamp : String
"dontcare"
end
end
extend Meta
def self.all : Array(Meta)
[M1] of Meta
end
end
class M1 < Migration
end
Migration.all.sort
1 Like
Alternatively, you can parametrize Meta
and then extend Meta(Migration.class)
.