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'
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 :-)