Looked through opened & closed issues, this doesn't seem to be reported yet, but I'm pretty unfamiliar with these kinds of errors.
class Foo
def bar
puts "works"
end
@callbacks : Array(Proc(Nil)) = [
->{ bar }
]
end
Foo.new
Module validation failed: Call parameter type does not match function signature!
i32 138
%Foo* call void @"*Foo#bar:Nil"(i32 138)
???
???
???
???
???
__crystal_main
main
__libc_start_main
_start
???
md5-7cc5350b6fdabc27a18ab1d45d696e2a
Crystal 0.24.1 (2017-12-20)
LLVM: 5.0.0
Default target: x86_64-unknown-linux-gnu
Array is not essential:
class Foo
def bar
puts "foo"
end
@callbacks : Proc(Nil) = ->{ bar }
end
Foo.new
The interesting thing is that typeof(bar) is Nil and typeof(-> { bar }) is Proc(Nil) so it should work.
Replacing puts "foo" with plain nil compiles.
It also compiles if bar is not an instance method:
def bar
puts "works"
end
class Foo
@callback : Proc(Nil) = ->{ bar }
end
Foo.new
It compiles if you wrap the @callback inside an init.
class Foo
@callback : Proc(Nil)
def initialize
@callback = ->{ bar }
end
def bar
puts "works"
end
end
Foo.new
Working example with array:
class Foo
@callbacks : Array(Proc(Nil))
def initialize
@callbacks = [->{ bar }, ->{ bar; bar }]
end
def bar
puts "foo"
end
def call
@callbacks.each do |callback|
callback.call
end
end
end
a = Foo.new
a.call
@Virtual-Machine Yeah, that's what I was doing originally. Good to point out though!
I basically had a variable in a superclass that I was trying to give a specific default value to in a subclass by overriding super's initialize. My library is a layer over another library, so if the super initialize changed, problems arose, so I tried a few ways to see if I could do it without redefining the method, and came across this issue in the process.
I did end up finding a much better way anyway :smile:
Note that I'll fix this by making the initialization of an instance variable run at the class level, not at the instance level.
The bug is basically that this compiles:
class Foo
@bar : Int32 = bar
def bar
1
end
end
It's a bit confusing that bar is found, given that's an instance method, and we are in the process of initializing an instance variable... that makes little sense.
So the above will give an error, but this will work:
class Foo
@bar : Int32 = bar # OK, refers to the class method
def self.bar
1
end
end
Of course you can always do:
class Foo
@bar : Int32
def initialize
@bar = bar
end
def bar
1
end
end
and problem solved, but there at least a few checks are made to make sure you don't access self or some other instance var before assigning it to @bar.
I just checked this with other languages: Java evaluates instance var initializers at the instance level. But C# does it at the class (static) level, which makes much more sense to me. I'll change it to behave like in C#.
Most helpful comment
I just checked this with other languages: Java evaluates instance var initializers at the instance level. But C# does it at the class (static) level, which makes much more sense to me. I'll change it to behave like in C#.