Crystal: Generic type arguments are counted incorrectly when splatted into parameter lists with splat

Created on 26 Oct 2020  路  4Comments  路  Source: crystal-lang/crystal

Tested on 8738e6365. It appears that splats are supported inside the type argument list of a generic type:

class T(A, B) end
T(*{Int32, Bool}) # => T(Int32, Bool)

But when the type parameter list also contains a splat, Crystal fails to get the correct number of type vars:

class T(A, B, *C) end
T(Int32, Bool) # => T(Int32, Bool, )
T(*{Int32, Bool}) # Error: wrong number of type vars for T(A, B, C) (given 1, expected 2+)

In particular, if somehow an empty tuple is splatted, an internal bug is triggered:

class T(A, B, *C) end
EMPTY = Tuple.new
T(Int32, *EMPTY)
Index out of bounds (IndexError)
  from ../../../crystal/src/compiler/crystal/types.cr:1934:7 in 'instantiate'
  from ../../../crystal/src/compiler/crystal/semantic/bindings.cr:571:26 in 'visit'
  from ../../../crystal/src/compiler/crystal/syntax/visitor.cr:27:12 in 'accept'
  from ../../../crystal/src/compiler/crystal/semantic/main_visitor.cr:1324:21 in 'visit'
  from ../../../crystal/src/compiler/crystal/syntax/visitor.cr:27:12 in 'accept'
  from ../../../crystal/src/compiler/crystal/syntax/ast.cr:169:27 in 'accept'
  from ../../../crystal/src/compiler/crystal/semantic/main_visitor.cr:6:7 in 'visit_main'
  from ../../../crystal/src/compiler/crystal/semantic/main_visitor.cr:5:5 in 'semantic'
  from ../../../crystal/src/compiler/crystal/compiler.cr:171:14 in 'compile'
  from ../../../crystal/src/compiler/crystal/command.cr:210:5 in 'run_command'
  from ../../../crystal/src/compiler/crystal/command.cr:117:7 in 'run'
  from ../../../crystal/src/crystal/main.cr:105:5 in 'main'
  from src/env/__libc_start_main.c:94:2 in 'libc_start_main_stage2'
bug topicsemantic

All 4 comments

I have a fix for the first bug.

In particular, if somehow an empty tuple is splatted, an internal bug is triggered

That's not splatting an empty tuple. It's splatting a constant which happens to have the type of an empty tuple.

Splatting a constant that's not actually a type should be disallowed by the compiler. That's the actual bug.

For example this compiles fine with --no-codegen which means the semantic pass succeeds:

class T(A, B, *C) end
EMPTY = 1
T(Int32, *EMPTY)

But it shouldn't! What the compiler is doing is asking "What's the type of EMPTY? Oh, it's an Int32? Got it, that's the type I'm going to use". But it should disallow constants before asking that.

I would extract that latter issue to a separate issue, please 馃檹

Yeah it seems I mixed up the tuple notation in a typename context and in a code context. That bug shouldn't require a constant though:

class T(A, *B) end
T(*typeof(Tuple.new))
Index out of bounds (IndexError)
  from ../../../crystal/src/compiler/crystal/types.cr:1934:7 in 'instantiate'
  from ../../../crystal/src/compiler/crystal/semantic/bindings.cr:575:26 in 'visit'
  from ../../../crystal/src/compiler/crystal/syntax/visitor.cr:27:12 in 'accept'
  from ../../../crystal/src/compiler/crystal/syntax/ast.cr:169:27 in 'accept'
  from ../../../crystal/src/compiler/crystal/semantic/main_visitor.cr:6:7 in 'visit_main'
  from ../../../crystal/src/compiler/crystal/semantic/main_visitor.cr:5:5 in 'semantic'
  from ../../../crystal/src/compiler/crystal/compiler.cr:171:14 in 'compile'
  from ../../../crystal/src/compiler/crystal/command.cr:210:5 in 'run_command'
  from ../../../crystal/src/compiler/crystal/command.cr:62:5 in 'run'
  from ../../../crystal/src/crystal/main.cr:105:5 in 'main'
  from src/env/__libc_start_main.c:94:2 in 'libc_start_main_stage2'

@HertzDevil You are right!

That last snippet works in my PR :-)

Was this page helpful?
0 / 5 - 0 ratings