Skip to content

Runtime Segfault during In-Bound Array Access in Non-Release Mode #16947

Description

@dhwa-kyle

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]

Metadata

Metadata

Assignees

No one assigned

    Labels

    kind:bugA bug in the code. Does not apply to documentation, specs, etc.

    Type

    Fields

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions