Hi,
Apart from https://github.com/crystal-lang/crystal/issues/4454 I was able to find another bug. This causes the compiler to crash.
Code snippet:
# Tuple as part of the alias breaks things:
alias Type = String | Hash(Type, Type) | Tuple(Type, Type)
# This works just fine:
# alias Type = String | Hash(Type, Type)
class Val
getter raw : Type
def initialize(@raw)
end
def first
object = @raw
if object.responds_to?(:first) #is_a?(Enumerable) - Same result
object.first
end
end
end
pp Val.new("foo").first # One of them alone work fine
({"foo".as(Type) => "bar".as(Type)}) # But with both, the compiler crashes
Results in the following output:
Nil assertion failed
Error opening file 'crystal' with mode 'r': No such file or directory (Errno)
Failed to raise an exception: END_OF_STACK
[0x409dc7] __crystal_raise +503
[0x40eb0e] ???
[0x4278e8] ???
[0x41c369] ???
[0x41aa95] ???
[0x413a26] ???
[0x43c35b] main +17739
[0x7f841023f511] __libc_start_main +241
[0x4097da] _start +42
[0x0] ???
Just removing the actually unused Tuple(Type, Type) part fixes the issue. Also, commenting just one (or both) of the last two lines in the snippet fixes the issue too.
Interestingly, if we don't store the value inside the class, but pass it as argument to #first instead, the program works as expected again:
alias Type = String | Hash(Type, Type) | Tuple(Type, Type)
class Val
def first(object)
if object.responds_to?(:first) #is_a?(Enumerable) - Same result
object.first
end
end
end
pp Val.new.first("foo")
({"foo".as(Type) => "bar".as(Type)})
Thanks
I've tried to implement the behaviour of Hash#first inside the method and create the tuple manually:
alias Type = String | Hash(Type, Type) | Tuple(Type, Type)
class Val
getter raw : Type
def initialize(@raw)
end
def first
object = @raw
if object.is_a?(Hash)
{object.first_key.as(Type), object.first_value.as(Type)}
end
end
end
pp Val.new({"foo".as(Type) => "bar".as(Type)}).first
This compiles, but results in an invalid memory access:
Invalid memory access (signal 11) at address 0x4f0000
[0x464be5] *CallStack::print_backtrace:Int32 +117
[0x4590ad] __crystal_sigfault_handler +61
[0x4be768] sigfault_handler +40
[0x7f84919f03d0] ???
[0x45b34b] *Pointer(UInt8) +27
[0x46e793] *String#unsafe_byte_at<Int32>:UInt8 +35
[0x48145e] *Char::Reader#byte_at<Int32>:UInt32 +30
[0x480530] *Char::Reader#decode_current_char:Char +48
[0x4815c3] *Char::Reader#next_char:Char +51
[0x470dc4] *String#inspect<String::Builder>:String::Builder +468
[0x470bda] *String +74
[0x470b68] *String#pretty_print<PrettyPrint>:(Int32 | Nil) +24
[0x49d1e1] *PrettyPrint#list<String, Tuple(Type, Type), String>:Nil +993
[0x4a122f] *Tuple(Type, Type) +63
[0x49cc41] *PrettyPrint::format<(Tuple(Type, Type) | Nil), IO::FileDescriptor, Int32, String, Int32>:IO::FileDescriptor +177
[0x49cb88] *PrettyPrint::format:width:indent<(Tuple(Type, Type) | Nil), IO::FileDescriptor, Int32, Int32>:IO::FileDescriptor +88
[0x448c6e] ???
[0x458f89] main +41
[0x7f8491014830] __libc_start_main +240
[0x448369] _start +41
[0x0] ???
Seems that compiler isn't checking recursive structs properly since Tuple is a struct and crystal don't allow recursive structs
Maybe related #1056
Most helpful comment
Seems that compiler isn't checking recursive structs properly since Tuple is a struct and crystal don't allow recursive structs
Maybe related #1056