I encountered what appears to be a compiler bug leading to a runtime segmentation fault when running a code snippet involving generic subclasses, modules as interfaces, and module-typed arrays. I know such structures have, in combination, historically had somewhat shaky support in Crystal, but I think the use case in simple enough to warrant a bug report.
I reduced the issue to the following standalone reproducer. I also included comments describing several changes that avoid the issue. The final line of the snippet is what results in the segmentation fault at runtime.
Some things to note:
- This only occurs in non-release mode. If I run with
--release, no seg-fault occurs.
- I could not reproduce in the public Crystal playground.
- I reproduced this locally with Crystal 1.20.1 + LLVM 20.1.8 on Ubuntu 24.04
- The segfault also appears in the official
crystallang/crystal:1.20.1 docker container.
# Line 77 results in a segfault.
# Following steps A, B, C, D, or E produces normal behavior.
module Foo
def method : Void
end
end
module CompoundFoo
include Foo
abstract def foos : Enumerable(Foo)
end
class GenericFoo(T)
include Foo
getter value : T
def initialize(@value : T)
end
end
# (A) De-generify SubGenericFoo to subclass GenericFoo(String) for normal behavior
class SubGenericFoo(T) < GenericFoo(T)
end
class SingletonFoo
include CompoundFoo
def foos : Array(Foo)
singleton = [] of Foo
# NOTE: Commenting out the next line has no bearing on the output
singleton << GenericFoo(String).new "singleton"
# NOTE: The existence of the next line results in line 75 printing an empty
# string for the second element. However, it has no bearing on the
# segfault, either when present or commented out.
puts singleton
# (B) Comment out the next line for normal behavior
singleton[0]
singleton
end
def method : Void
foos
end
end
class ArrayFoo
include CompoundFoo
getter foos : Array(Foo)
def initialize(@foos : Array(Foo))
end
end
# (C) Comment out the class SubArrayFoo for normal behavior
class SubArrayFoo < ArrayFoo
end
first_generic = GenericFoo.new "first-generic"
second_generic = GenericFoo.new "second-generic"
# (D) Remove the .select Foo on the next line for normal behavior
ids = [second_generic].select Foo
# (E) Comment out the next line for normal behavior
ids.each(&.method)
sub_generic = SubGenericFoo(String).new "sub-generic"
array = [] of Foo
array << first_generic
array << sub_generic
puts array
puts array[0]
puts array[1]
I encountered what appears to be a compiler bug leading to a runtime segmentation fault when running a code snippet involving generic subclasses, modules as interfaces, and module-typed arrays. I know such structures have, in combination, historically had somewhat shaky support in Crystal, but I think the use case in simple enough to warrant a bug report.
I reduced the issue to the following standalone reproducer. I also included comments describing several changes that avoid the issue. The final line of the snippet is what results in the segmentation fault at runtime.
Some things to note:
--release, no seg-fault occurs.crystallang/crystal:1.20.1docker container.