Inserting an element into a recursively typed Hash will cause the compiler to crash due to what looks like stack corruption:
alias RecHash = Hash( String, String | RecHash )
x = {} of String => String | RecHash
y = {} of String => String | RecHash
x["y"] = y
Compiler output:
Invalid memory access (signal 11) at address 0xbf151fec
[134668725] __crystal_sigfault_handler +85
[153873279] sigfault_handler +31
[-1217295360] ???
[151857938] ???
[138754615] ???
[138732613] ???
[151856008] ???
[151856650] ???
[151852676] ???
[138714919] ???
[138752655] ???
[138732613] ???
[151856008] ???
[151856650] ???
[151852676] ???
[138714919] ???
<keeps going>
Crystal compiler version: Crystal 0.20.1 [18e7617] (2016-12-05), OS: Ubuntu 15.10.
Workaround (might also help narrowing down the bug):
class RecHash
def initialize
@inner = {} of String => String | RecHash
end
def []( key )
@inner[key]
end
def []=( key, value )
@inner[key] = value
end
end
x = RecHash.new
y = RecHash.new
x["y"] = y
May be unrelated, but I got some additional results while playing around with this issue.
This gives me an error message I wasn't expecting. What I expected was a warning that you can't instantiate union types.
alias RecHash = String | Hash(String, RecHash)
puts RecHash.new
Output:
Error in crystal-3804.cr:2: undefined method 'new' for RecHash:Class
puts RecHash.new
^~~
The following gives me the correct warning:
alias Foo = String | Hash(String, Int32)
puts Foo.new
Output:
Error in crystal-3804.cr:2: instantiating '(Hash(String, Int32) | String):Class#new()'
puts Foo.new
^~~
in /usr/local/Cellar/crystal-lang/0.20.3/src/union.cr:19: can't create instance of a union type
struct Union
^
Edit: Crystal version Crystal 0.20.3 (2016-12-23) on macOS Sierra 10.12.2
Looks like it can be closed?
Checked in Crystal 0.31.1 on MacOS. It does not crash anymore. It now shows an error:
$ crystal test.cr
Showing last frame. Use --error-trace for full trace.
In /usr/local/Cellar/crystal/0.31.1/src/hash.cr:1825:46
1825 | def initialize(@hash : UInt32, @key : K, @value : V)
^-----
Error: instance variable '@value' of Hash::Entry(String, Hash(String, RecHash | String) | String) must be (Hash(String, RecHash | String) | String), not Hash(String, Hash(String, RecHash | String) | String)
$ crystal --version
Crystal 0.31.1 (2019-10-02)
LLVM: 8.0.1
Default target: x86_64-apple-macosx
Yes, it seems we made some changes to alias and now this correctly doesn't compile.
I would still like the compiler to give a better error message for recursive aliases without a case base. Or otherwise remove recursive aliases.
I'd be fine removing recursive aliases actually, since we can always re-add them after 1.0.