Crystal: Regression typeof(sizeof(x)) should receive a type

Created on 13 Feb 2018  路  10Comments  路  Source: crystal-lang/crystal

After upgrading to crystal 0.24.1 from 0.23.1 I can no longer compile the timuckun/crystal-liblmdb shard.

Here's the crystal -v output:

Crystal 0.24.1 (2018-01-27)

LLVM: 5.0.1
Default target: x86_64-apple-macosx

The compilation fails with the following error in src/lmdb/base.cr:112:16:

Error target prget failed to compile:
BUG: sizeof(typeof(x)) at /Users/felix/dev/misc/prget/lib/lmdb/src/lmdb/base.cr:112:16 should receive a type
Crystal::MainVisitor#visit<Crystal::SizeOf>:Bool
Crystal::ASTNode+@Crystal::ASTNode#accept<Crystal::MainVisitor>:Nil
Crystal::MainVisitor#visit<Crystal::Assign>:Bool
Crystal::ASTNode+@Crystal::ASTNode#accept<Crystal::MainVisitor>:Nil
Crystal::ASTNode+@Crystal::ASTNode#accept<Crystal::MainVisitor>:Nil
Crystal::MainVisitor#visit<Crystal::If>:Bool
Crystal::ASTNode+@Crystal::ASTNode#accept<Crystal::MainVisitor>:Nil
Crystal::MainVisitor#visit<Crystal::If>:Bool
Crystal::ASTNode+@Crystal::ASTNode#accept<Crystal::MainVisitor>:Nil
Crystal::ASTNode+@Crystal::ASTNode#accept<Crystal::MainVisitor>:Nil
Crystal::ASTNode+@Crystal::ASTNode#accept<Crystal::MainVisitor>:Nil
Crystal::Call#instantiate<Crystal::Matches, Crystal::Type+, Nil>:Array(Crystal::Def+)
Crystal::Call#lookup_matches_in_type<Crystal::Type+, Array(Crystal::Type+), (Array(Crystal::NamedArgumentType) | Nil), Nil, String, Bool, Bool>:Array(Crystal::Def+)
Crystal::Call#lookup_matches_in<Crystal::Type+, Array(Crystal::Type+), (Array(Crystal::NamedArgumentType) | Nil)>:Array(Crystal::Def+)
Crystal::Call#recalculate:Nil
Crystal::MainVisitor#visit<Crystal::Call>:Bool
Crystal::ASTNode+@Crystal::ASTNode#accept<Crystal::MainVisitor>:Nil
Crystal::ASTNode+@Crystal::ASTNode#accept<Crystal::MainVisitor>:Nil
Crystal::Call#instantiate<Crystal::Matches, Crystal::Type+, Nil>:Array(Crystal::Def+)
Crystal::Call#lookup_matches_in_type<Crystal::Type+, Array(Crystal::Type+), (Array(Crystal::NamedArgumentType) | Nil), Nil, String, Bool, Bool>:Array(Crystal::Def+)
Crystal::Call#lookup_matches_in<Crystal::Type+, Array(Crystal::Type+), (Array(Crystal::NamedArgumentType) | Nil)>:Array(Crystal::Def+)
Crystal::Call#recalculate:Nil
Crystal::MainVisitor#visit<Crystal::Call>:Bool
Crystal::ASTNode+@Crystal::ASTNode#accept<Crystal::MainVisitor>:Nil
Crystal::Call#instantiate<Crystal::Matches, Crystal::Type+, Nil>:Array(Crystal::Def+)
Crystal::Call#lookup_matches_in_type<Crystal::Type+, Array(Crystal::Type+), (Array(Crystal::NamedArgumentType) | Nil), Nil, String, Bool, Bool>:Array(Crystal::Def+)
Crystal::Call#lookup_matches_in<Crystal::Type+, Array(Crystal::Type+), (Array(Crystal::NamedArgumentType) | Nil)>:Array(Crystal::Def+)
Crystal::Call#recalculate:Nil
Crystal::MainVisitor#visit<Crystal::Call>:Bool
Crystal::ASTNode+@Crystal::ASTNode#accept<Crystal::MainVisitor>:Nil
Crystal::MainVisitor#visit<Crystal::Assign>:Bool
Crystal::ASTNode+@Crystal::ASTNode#accept<Crystal::MainVisitor>:Nil
Crystal::ASTNode+@Crystal::ASTNode#accept<Crystal::MainVisitor>:Nil
Crystal::Call#instantiate<Crystal::Matches, Crystal::Type+, Nil>:Array(Crystal::Def+)
Crystal::Call#lookup_matches_in_type<Crystal::Type+, Array(Crystal::Type+), (Array(Crystal::NamedArgumentType) | Nil), Nil, String, Bool, Bool>:Array(Crystal::Def+)
Crystal::Call#lookup_matches_in<Crystal::Type+, Array(Crystal::Type+), (Array(Crystal::NamedArgumentType) | Nil)>:Array(Crystal::Def+)
Crystal::Call#recalculate:Nil
Crystal::MainVisitor#visit<Crystal::Call>:Bool
Crystal::ASTNode+@Crystal::ASTNode#accept<Crystal::MainVisitor>:Nil
Crystal::ASTNode+@Crystal::ASTNode#accept<Crystal::MainVisitor>:Nil
Crystal::Call#instantiate<Crystal::Matches, Crystal::Type+, Nil>:Array(Crystal::Def+)
Crystal::Call#lookup_matches_in_type<Crystal::Type+, Array(Crystal::Type+), (Array(Crystal::NamedArgumentType) | Nil), Nil, String, Bool, Bool>:Array(Crystal::Def+)
Crystal::Call#lookup_matches_in<Crystal::Type+, Array(Crystal::Type+), (Array(Crystal::NamedArgumentType) | Nil)>:Array(Crystal::Def+)
Crystal::Call#recalculate:Nil
Crystal::MainVisitor#visit<Crystal::Call>:Bool
Crystal::ASTNode+@Crystal::ASTNode#accept<Crystal::MainVisitor>:Nil
Crystal::ASTNode+@Crystal::ASTNode#accept<Crystal::MainVisitor>:Nil
Crystal::MainVisitor#visit<Crystal::Block>:(Bool | Nil)
Crystal::MainVisitor#visit<Crystal::Yield>:Bool
Crystal::ASTNode+@Crystal::ASTNode#accept<Crystal::MainVisitor>:Nil
Crystal::ASTNode+@Crystal::ASTNode#accept<Crystal::MainVisitor>:Nil
Crystal::MainVisitor#visit<Crystal::ExceptionHandler>:Bool
Crystal::ASTNode+@Crystal::ASTNode#accept<Crystal::MainVisitor>:Nil
Crystal::ASTNode+@Crystal::ASTNode#accept<Crystal::MainVisitor>:Nil
Crystal::ASTNode+@Crystal::ASTNode#accept<Crystal::MainVisitor>:Nil
Crystal::Call#instantiate<Crystal::Matches, Crystal::Type+, Nil>:Array(Crystal::Def+)
Crystal::Call#lookup_matches_in_type<Crystal::Type+, Array(Crystal::Type+), (Array(Crystal::NamedArgumentType) | Nil), Nil, String, Bool, Bool>:Array(Crystal::Def+)
Crystal::Call#lookup_matches_in<Crystal::Type+, Array(Crystal::Type+), (Array(Crystal::NamedArgumentType) | Nil)>:Array(Crystal::Def+)
Crystal::Call#recalculate:Nil
Crystal::MainVisitor#visit<Crystal::Call>:Bool
Crystal::ASTNode+@Crystal::ASTNode#accept<Crystal::MainVisitor>:Nil
Crystal::ASTNode+@Crystal::ASTNode#accept<Crystal::MainVisitor>:Nil
Crystal::Call#instantiate<Crystal::Matches, Crystal::Type+, Nil>:Array(Crystal::Def+)
Crystal::Call#lookup_matches_in_type<Crystal::Type+, Array(Crystal::Type+), (Array(Crystal::NamedArgumentType) | Nil), Nil, String, Bool, Bool>:Array(Crystal::Def+)
Crystal::Call#lookup_matches_in<Crystal::Type+, Array(Crystal::Type+), (Array(Crystal::NamedArgumentType) | Nil)>:Array(Crystal::Def+)
Crystal::Call#recalculate:Nil
Crystal::MainVisitor#visit<Crystal::Call>:Bool
Crystal::ASTNode+@Crystal::ASTNode#accept<Crystal::MainVisitor>:Nil
Crystal::MainVisitor#visit<Crystal::Assign>:Bool
Crystal::ASTNode+@Crystal::ASTNode#accept<Crystal::MainVisitor>:Nil
Crystal::MainVisitor#expand<Crystal::MultiAssign>:Bool
Crystal::ASTNode+@Crystal::ASTNode#accept<Crystal::MainVisitor>:Nil
Crystal::ASTNode+@Crystal::ASTNode#accept<Crystal::MainVisitor>:Nil
Crystal::MainVisitor#visit<Crystal::Block>:(Bool | Nil)
Crystal::MainVisitor#visit<Crystal::Yield>:Bool
Crystal::ASTNode+@Crystal::ASTNode#accept<Crystal::MainVisitor>:Nil
Crystal::MainVisitor#visit<Crystal::ExceptionHandler>:Bool
Crystal::ASTNode+@Crystal::ASTNode#accept<Crystal::MainVisitor>:Nil
Crystal::ASTNode+@Crystal::ASTNode#accept<Crystal::MainVisitor>:Nil
Crystal::Call#instantiate<Crystal::Matches, Crystal::Type+, Nil>:Array(Crystal::Def+)
Crystal::Call#lookup_matches_in_type<Crystal::Type+, Array(Crystal::Type+), (Array(Crystal::NamedArgumentType) | Nil), Nil, String, Bool, Bool>:Array(Crystal::Def+)
Crystal::Call#lookup_matches_in<Crystal::Type+, Array(Crystal::Type+), (Array(Crystal::NamedArgumentType) | Nil)>:Array(Crystal::Def+)
Crystal::Call#recalculate:Nil
Crystal::MainVisitor#visit<Crystal::Call>:Bool
Crystal::ASTNode+@Crystal::ASTNode#accept<Crystal::MainVisitor>:Nil
Crystal::ASTNode+@Crystal::ASTNode#accept<Crystal::MainVisitor>:Nil
Crystal::MainVisitor#visit<Crystal::If>:Bool
Crystal::ASTNode+@Crystal::ASTNode#accept<Crystal::MainVisitor>:Nil
Crystal::MainVisitor#visit<Crystal::If>:Bool
Crystal::ASTNode+@Crystal::ASTNode#accept<Crystal::MainVisitor>:Nil
Crystal::ASTNode+@Crystal::ASTNode#accept<Crystal::MainVisitor>:Nil
Crystal::ASTNode+@Crystal::ASTNode#accept<Crystal::MainVisitor>:Nil
Crystal::Call#instantiate<Crystal::Matches, Crystal::Type+, Nil>:Array(Crystal::Def+)
Crystal::Call#lookup_matches_in_type<Crystal::Type+, Array(Crystal::Type+), (Array(Crystal::NamedArgumentType) | Nil), Nil, String, Bool, Bool>:Array(Crystal::Def+)
Crystal::Call#lookup_matches_in<Crystal::Type+, Array(Crystal::Type+), (Array(Crystal::NamedArgumentType) | Nil)>:Array(Crystal::Def+)
Crystal::Call#recalculate:Nil
Crystal::MainVisitor#visit<Crystal::Call>:Bool
Crystal::ASTNode+@Crystal::ASTNode#accept<Crystal::MainVisitor>:Nil
Crystal::ASTNode+@Crystal::ASTNode#accept<Crystal::MainVisitor>:Nil
Crystal::Program#visit_main<Crystal::ASTNode+, Crystal::MainVisitor, Bool, Bool>:Crystal::ASTNode+
Crystal::Program#semantic<Crystal::ASTNode+, Bool>:Crystal::ASTNode+
Crystal::Compiler#compile<Array(Crystal::Compiler::Source), String>:Crystal::Compiler::Result
Crystal::Command#run:(Bool | Crystal::Compiler::Result | IO::FileDescriptor | Nil)
main

Error: you've found a bug in the Crystal compiler. Please open an issue, including source code that will allow us to reproduce the bug: https://github.com/crystal-lang/crystal/issues

The problem also occurs when cloning the shard and running crystal spec(you must have liblmdb installed, eg. using brew install lmdb on macOS).

I tried building a minimal reproduction, but the following code compiles fine on 0.24.1:

x = 1
case x
when Int32, UInt32, Float32, Int64, UInt64, Float64
  size = sizeof(typeof(x))
  puts size
end
bug compiler

Most helpful comment

Huh, i tried and looks like problem isn't so scary.
basically, test case is

v = nil
if v.nil?
  #
else
  sizeof(typeof(v))
end

in real code it happens because v is an input argument of a method, so usually it is a union or not nil, but sometimes it is called directly with nil.

All 10 comments

I tested compilation using 0.24.0 and it works, so regression happened some time after 0.24.0.

This is still broken in 0.24.2.

Same problem with https://github.com/Papierkorb/cannon - works in 0.24.0, broken in 0.24.2, #5807 doesn't fix it.

BUG: sizeof(typeof(value)) at expanded macro: fast_encode:1:51 should receive a type

@konovod Have you been able to create a standalone test case?

Huh, i tried and looks like problem isn't so scary.
basically, test case is

v = nil
if v.nil?
  #
else
  sizeof(typeof(v))
end

in real code it happens because v is an input argument of a method, so usually it is a union or not nil, but sometimes it is called directly with nil.

I'm having the same problem using @konovod's snippet.

# tmp/bug.cr
v = nil
if v.nil?
  #
else
  sizeof(typeof(v))
end
crystal tmp/bug.cr

BUG: sizeof(typeof(v)) at /Users/scott.pierce/code/crystal/mandelbrot/tmp/bug.cr:5:3 should receive a type
Crystal::MainVisitor#visit<Crystal::SizeOf>:Bool
Crystal::ASTNode+@Crystal::ASTNode#accept<Crystal::MainVisitor>:Nil
Crystal::MainVisitor#visit<Crystal::If>:Bool
Crystal::ASTNode+@Crystal::ASTNode#accept<Crystal::MainVisitor>:Nil
Crystal::ASTNode+@Crystal::ASTNode#accept<Crystal::MainVisitor>:Nil
Crystal::Program#visit_main<Crystal::ASTNode+, Crystal::MainVisitor, Bool, Bool>:Crystal::ASTNode+
Crystal::Program#semantic<Crystal::ASTNode+, Bool>:Crystal::ASTNode+
Crystal::Compiler#compile<Array(Crystal::Compiler::Source), String>:Crystal::Compiler::Result
Crystal::Command#run_command<Bool>:Nil
Crystal::Command#run:(Bool | Crystal::Compiler::Result | IO::FileDescriptor | Nil)
main
crystal --version
Crystal 0.24.2 (2018-03-10)

LLVM: 5.0.1
Default target: x86_64-apple-macosx

via git bisect, 074d61c4845f22939b29a8024129f892e609d0f0 is the commit which introduces this problem.
I added the following spec to compiler/codegen/if_spec.cr to test for the condition.

  it "doesn't detect type in else condition (bug)" do
    codegen(%(
      v = nil : String
      if v.nil?
      else
        sizeof(typeof(v))
      end
      ))
  end

I'm still not familiar with why that code was added, but I'm still poking around.

I forked and branched to show the test:
https://github.com/ddrscott/crystal/tree/ISSUE_5717

So the problem is that sizeof should raise an error when called for an empty type, but sometimes this can happen on legitimate code (in a branch that is never executed). How can it be solved?

  • return 0 instead of raising an error? Will solve the issues but can sometimes hide an error.
  • return something that will raise at runtime (no idea on how to do it).

Is it possible to:

  • detect the unreachable branch and prune it?
  • keep the type with the nil value. So in this case I'd expect 8 to be returned.
Was this page helpful?
0 / 5 - 0 ratings