crystal-lang / crystal

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

Compiler bug during type downcasting #12606

Open Vici37 opened 1 year ago

Vici37 commented 1 year ago

This may be related to #12472 but I wasn't positive, so I'm creating a new bug report just in case.

Below causes a compiler bug error:

def call_proc(pr : Proc(String, Nil) | Proc(String, String?) | Proc(String, String))
  pr.call("Hello World!")
end

call_proc(->(str : String) { "test" })

Which causes the error:

BUG: trying to downcast (Proc(HTTP::Server::Context, (String | Nil)) | Proc(HTTP::Server::Context, Nil)) (Crystal::MixedUnionType) <- Proc(HTTP::Server::Context, (String | Nil)) (Crystal::ProcInstanceType) (Exception)            [14/9115]
  from /crystal/src/compiler/crystal/codegen/cast.cr:505:5 in 'downcast_distinct'
  from /crystal/src/indexable.cr:73:26 in 'downcast'                                                                   
  from /crystal/src/compiler/crystal/codegen/call.cr:92:20 in 'prepare_call_args_non_external'
  from /crystal/src/compiler/crystal/codegen/call.cr:46:7 in 'visit'       
  from /crystal/src/compiler/crystal/syntax/visitor.cr:27:12 in 'accept'
  from /crystal/src/compiler/crystal/codegen/codegen.cr:2255:9 in 'prepare_call_args_non_external'
  from /crystal/src/compiler/crystal/codegen/call.cr:46:7 in 'visit'
  from /crystal/src/compiler/crystal/syntax/visitor.cr:27:12 in 'accept'
  from /crystal/src/compiler/crystal/codegen/codegen.cr:665:9 in 'accept'
  from /crystal/src/compiler/crystal/codegen/codegen.cr:2272:7 in 'codegen_fun'                                                                                                                                                               
  from /crystal/src/compiler/crystal/codegen/fun.cr:51:3 in 'target_def_fun'
  from /crystal/src/compiler/crystal/codegen/call.cr:437:5 in 'visit'
  from /crystal/src/compiler/crystal/syntax/visitor.cr:27:12 in 'accept'                                                                                                                                                                      
  from /crystal/src/compiler/crystal/codegen/codegen.cr:665:9 in 'accept'
  from /crystal/src/compiler/crystal/codegen/codegen.cr:2272:7 in 'codegen_fun'
  from /crystal/src/compiler/crystal/codegen/fun.cr:51:3 in 'target_def_fun'
  from /crystal/src/compiler/crystal/codegen/call.cr:437:5 in 'visit'
  from /crystal/src/compiler/crystal/syntax/visitor.cr:27:12 in 'accept'
  from /crystal/src/compiler/crystal/codegen/codegen.cr:665:9 in 'accept'
  from /crystal/src/compiler/crystal/codegen/codegen.cr:2272:7 in 'codegen_fun'
  from /crystal/src/compiler/crystal/codegen/fun.cr:51:3 in 'target_def_fun'
  from /crystal/src/compiler/crystal/codegen/call.cr:437:5 in 'visit'
  from /crystal/src/compiler/crystal/semantic/bindings.cr:12:7 in 'visit'
  from /crystal/src/compiler/crystal/syntax/visitor.cr:27:12 in 'accept'
  from /crystal/src/compiler/crystal/codegen/codegen.cr:665:9 in 'accept'
  from /crystal/src/compiler/crystal/codegen/codegen.cr:2255:9 in 'accept'
  from /crystal/src/compiler/crystal/codegen/codegen.cr:665:9 in 'accept'
  from /crystal/src/compiler/crystal/codegen/codegen.cr:2255:9 in 'visit'
  from /crystal/src/compiler/crystal/syntax/visitor.cr:27:12 in 'accept'
  from /crystal/src/compiler/crystal/codegen/codegen.cr:2255:9 in 'visit'
  from /crystal/src/compiler/crystal/syntax/visitor.cr:27:12 in 'accept'
  from /crystal/src/compiler/crystal/codegen/codegen.cr:665:9 in 'accept'
  from /crystal/src/compiler/crystal/codegen/codegen.cr:2255:9 in 'accept'
  from /crystal/src/compiler/crystal/codegen/codegen.cr:2255:9 in 'accept'
  from /crystal/src/compiler/crystal/codegen/codegen.cr:665:9 in 'accept'
  from /crystal/src/compiler/crystal/codegen/codegen.cr:2255:9 in 'accept'
  from /crystal/src/compiler/crystal/codegen/codegen.cr:665:9 in 'accept'
  from /crystal/src/compiler/crystal/codegen/codegen.cr:2255:9 in 'visit'
  from /crystal/src/compiler/crystal/syntax/visitor.cr:27:12 in 'accept'
  from /crystal/src/compiler/crystal/codegen/codegen.cr:2255:9 in 'visit'
  from /crystal/src/compiler/crystal/syntax/visitor.cr:27:12 in 'accept'
  from /crystal/src/compiler/crystal/codegen/codegen.cr:665:9 in 'accept'
  from /crystal/src/compiler/crystal/codegen/codegen.cr:2255:9 in 'accept'
  from /crystal/src/compiler/crystal/codegen/codegen.cr:665:9 in 'accept'
  from /crystal/src/compiler/crystal/codegen/codegen.cr:2255:9 in 'visit'
  from /crystal/src/compiler/crystal/syntax/visitor.cr:27:12 in 'accept'
  from /crystal/src/compiler/crystal/codegen/codegen.cr:2255:9 in 'visit'
  from /crystal/src/compiler/crystal/syntax/visitor.cr:27:12 in 'accept'
  from /crystal/src/compiler/crystal/codegen/codegen.cr:665:9 in 'accept'
  from /crystal/src/compiler/crystal/codegen/codegen.cr:2272:7 in 'codegen_fun'
  from /crystal/src/compiler/crystal/codegen/fun.cr:51:3 in 'target_def_fun'
  from /crystal/src/compiler/crystal/codegen/call.cr:437:5 in 'visit'
  from /crystal/src/compiler/crystal/syntax/visitor.cr:27:12 in 'accept'
  from /crystal/src/compiler/crystal/codegen/codegen.cr:437:17 in 'accept'
  from /crystal/src/compiler/crystal/codegen/codegen.cr:665:9 in 'accept'
  from /crystal/src/compiler/crystal/codegen/codegen.cr:437:17 in 'accept'
  from /crystal/src/compiler/crystal/codegen/codegen.cr:665:9 in 'accept'
  from /crystal/src/compiler/crystal/codegen/codegen.cr:437:17 in 'accept'
  from /crystal/src/compiler/crystal/codegen/codegen.cr:665:9 in 'accept'
  from /crystal/src/compiler/crystal/codegen/codegen.cr:437:17 in 'accept'
  from /crystal/src/compiler/crystal/codegen/codegen.cr:665:9 in 'accept'
  from /crystal/src/compiler/crystal/codegen/codegen.cr:2225:7 in 'codegen'
  from /crystal/src/compiler/crystal/compiler.cr:162:16 in 'compile'
  from /crystal/src/compiler/crystal/command/spec.cr:98:14 in 'spec'
  from /crystal/src/hash.cr:908:11 in '__crystal_main'
  from /crystal/src/crystal/main.cr:115:5 in 'main'
  from src/env/__libc_start_main.c:95:2 in 'libc_start_main_stage2'
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

For my use case, I was trying to create a list of procs that would accept an argument and return a String, nil, or undetermined between those two. Using the naive Proc(String, String?) kept throwing errors when I constructed a proc that guaranteed only returned String or nil, so expanded the union as needed, and here we are :)

asterite commented 1 year ago

Reduced:

def call(pr : Proc(Nil) | Proc(String))
end

call(->{ "" })

Error:

BUG: trying to downcast (Proc(Nil) | Proc(String)) (Crystal::MixedUnionType) <- Proc(String) (Crystal::ProcInstanceType) (Exception)

I guess the implicit casting of Proc(T) to Proc(Nil) is happening here somehow.

I won't fix this because I think first we should disallow such casting. It's wrong and it has a wrong codegen semantic.

See https://github.com/crystal-lang/crystal/issues/10911