On https://github.com/will/crystal-pg/tree/cafe2be546ecf566b3c99210025f5f42e875ff87 using crystal head 9c7f351
crystal-pg ➤ bcrystal spec
Using compiled compiler at .build/crystal
Assertion failed: (castIsValid(op, S, Ty) && "Invalid cast!"), function Create, file /tmp/llvm20151214-69554-hagc7s/llvm-3.6.2.src/lib/IR/Instructions.cpp, line 2408.
[1] 55157 abort /Users/will/code/crystal/bin/crystal spec
I tried removing my cache build directory but it is still the same error
Thanks, I'll take a look. It might be because of the change to structs. I'll fix it before the release.
I do have a struct that inherits from an abstract struct that inherits from an abstract struct, but that appears to work
abstract struct A
end
abstract struct B < A
end
struct C < B
end
p C.new
/tmp ➤ bcrystal s.cr
Using compiled compiler at .build/crystal
C()
Reduced:
abstract struct Foo
end
struct Bar < Foo
end
struct Baz < Foo
end
instance = (Bar || Baz).new
I knew I forgot something, struct classes should also have a special treatment. It's good that you found this :-)
Please explain if you get a chance (Bar || Baz).new . Wouldn’t that always simplify down to Bar.new ? How does that trigger different behavior?
Oh, the || isn't optimized by the compiler for trivial cases (yet). Right now it's rewritten to:
if Bar
Bar
else
Baz
end
Maybe it's better if I use rand < 0.5 ? Bar : Baz, but of course || is shorter and we use it in specs to quickly create a union type.
So the final type of that expression would be Bar:Class | Baz:Class. However, similar to instance types, a union of two types under the same hierarchy gives a virtual type. So the type of the above is actually Foo+:Class... so a virtual type.
Before a few commits, virtual types were always reference types. Now they can be structs, so the codegen must be updated for this. The thing is, when you later do new on a Foo:Class+, the final result, before the structs change, would be a pointer... but now that's not true anymore. The buggy line is this one, which blindly casts the value to a pointer.
The reason why there's a virtual type for metaclass is because of this:
foo_plus = rand < 0.5 ? Bar.new : Baz.new
typeof(foo_plus) # Foo+
foo_plus.class # what's the type of this? ... Foo+:Class
Anyway, that's the technical explanation... I'm usually not good at explaining this, but little by little I'm adding more comments in the compiler.
Also know that we will _probably_ stop generating virtual types for union types. So Bar.new || Baz.new will be of type Bar | Baz in the future. Only when assigning it to a variable of type Foo+ they will get casted to this virtual type. This is probably more intuitive than what we have right now, but we'll have to test it.
Thanks!
@will It should be working now, I checked out that branch and run ccrystal spec with head and all specs pass :-)
It's so good that you reported this: the compiler and standard library don't use struct inheritance and the current specs were very poor. The handling of virtual structs was missing in a few places, and with crystal-pg working I think they are all covered now (I also checked all uses of VirtualType in the codegen).
Confirm, things look good now
I see your alias is ccrystal instead of my bcrystal for bin/crystal
Yeah, first thing that came to my mind... the first c doesn't mean anything, I just double tap it to use the next compiler :-)