crystal-lang / crystal

The Crystal Programming Language
https://crystal-lang.org
Apache License 2.0
19.45k stars 1.62k forks source link

Cannot getTypeInfo() on a type that is unsized! #11384

Open Blacksmoke16 opened 3 years ago

Blacksmoke16 commented 3 years ago

Was able to produce this error when using universal darwin release asset for 1.2.1:

Assertion failed: (Ty->isSized() && "Cannot getTypeInfo() on a type that is unsized!"), function getTypeSizeInBits, file /var/cache/omnibus/src/llvm/llvm-10.0.0.src/include/llvm/IR/DataLayout.h, line 625.
./crystal-1.2.1-1/bin/crystal: line 102: 80481 Abort trap: 6           "$INSTALL_DIR/embedded/bin/crystal" "$@"

Discovered via https://github.com/athena-framework/console/runs/4042123231?check_suite_focus=true. Reproduces on both MacOS 10 and 11. Does NOT reproduce on Brew installation, or when building this release/master locally. Possibly could just be an LLVM 10 bug that has since been fixed?

Minimal example:

module OutputInterface
end

class IOOutput
  include OutputInterface
end

abstract struct Style
  include OutputInterface

  @output : OutputInterface

  def initialize(@output : OutputInterface); end
end

class ConsoleOutput
  @stderr : OutputInterface = IOOutput.new
end

ConsoleOutput.new

Results in:

./crystal-1.2.1-1/bin/crystal run --prelude=empty --stats --progress --emit llvm-ir test.cr
Parse:                             00:00:00.000100458 (   0.76MB)
Semantic (top level):              00:00:00.022132003 (   9.28MB)
Semantic (new):                    00:00:00.000063414 (   9.28MB)
Semantic (type declarations):      00:00:00.000648307 (   9.28MB)
Semantic (abstract def check):     00:00:00.000012963 (   9.28MB)
Semantic (ivars initializers):     00:00:00.000263966 (   9.28MB)
Semantic (cvars initializers):     00:00:00.000066943 (   9.28MB)
Semantic (main):                   00:00:00.000693254 (  12.37MB)
Semantic (cleanup):                00:00:00.000001679 (  12.37MB)
Semantic (recursive struct check): 00:00:00.000041075 (  12.37MB)
Assertion failed: (Ty->isSized() && "Cannot getTypeInfo() on a type that is unsized!"), function getTypeSizeInBits, file /var/cache/omnibus/src/llvm/llvm-10.0.0.src/include/llvm/IR/DataLayout.h, line 625.
./crystal-1.2.1-1/bin/crystal: line 102: 76711 Abort trap: 6           "$INSTALL_DIR/embedded/bin/crystal" "$@"

No LLVM-IR is emitted either.

Blacksmoke16 commented 2 years ago

This still seems to be happening after #11390 on 1.2.2. See https://github.com/athena-framework/console/runs/4185063820?check_suite_focus=true.

HertzDevil commented 2 years ago

I don't think this should compile. Suppose that Style has a substruct T. Then OutputInterface's binary representation would be the same as the mixed union IOOutput | T; the type ID of the active union member, followed by the representation of that member. Since T contains @output : OutputInterface, this means T must be large enough to contain IOOutput | T, thus a recursive struct is formed. The reason it doesn't reproduce consistently is that LLVM might have been built with assertions disabled.

In fact, weird things happen if you try to subclass Style nonetheless:

module OutputInterface
end

abstract struct Style
  include OutputInterface

  @output : OutputInterface

  def initialize(@output : OutputInterface); end
end

struct Foo < Style
end

sizeof(OutputInterface) # Stack overflow
module OutputInterface
end

class IOOutput
  include OutputInterface
end

abstract struct Style
  include OutputInterface

  @output : OutputInterface

  def initialize(@output : OutputInterface); end
end

struct Foo < Style
end

Foo.new(Foo.new(IOOutput.new)) # Segmentation fault

These crashes are reproducible on 1.0.0 too.

straight-shoota commented 2 years ago

Yeah, this is essentially a recursive struct.