In some circumstances, the compiler can't seem to differentiate which if branch to use for T based off conditional logic for an instance variable. I get the same result when trying to use an Int32 | Bool type
To explain let me provide a code snippet
class Test
@test : Int32? = 10
def test
if @test.nil?
0
else
@test + 1
end
end
end
Test.new.test
When running this snippet you get this result
Error in test.cr:13: instantiating 'Test#test()'
Test.new.test
^~~~
in test.cr:8: undefined method '+' for Nil (compile-time type is (Int32 | Nil))
@test + 1
^
Between the condition (@test.nil?) and the branch execution (@test + 1), the instance variable could have changed type (from a concurrent fiber, or maybe later, a thread), and the compiler is not sure that in the branch @test will still be non-nil.
To fix this you need to assign the instance variable to a local variable:
class Test
@test : Int32?
def initialize(@test = nil)
end
def test
if test = @test
test + 1
else
0
end
end
end
pp Test.new.test # => 0
pp Test.new(10).test # => 11
live at: https://carc.in/#/r/3n28
Looks like same bug - this won't compile:
class Test
property log = nil
def initialize()
@log = File.new("test_.txt", "a")
end
end
t = Test.new
if t.log
t.log.puts "test"
t.log.close
end
in that way - no problem
class Test
property log = nil
def initialize()
@log = File.new("test_.txt", "a")
end
end
t = Test.new
tmp = t.log
if tmp
tmp.puts "test"
tmp.close
end
It's not a bug, it's a failsafe!
Nothing prevents you to do:
if t.log
t.log = nil # We change log here!
t.log.puts "test" # Crash at runtime :(
t.log.close
end
Using a local variable, this cannot happen:
if logger = t.log
t.log = nil # We change log here!
logger.puts "test" # logger is still valid
logger.close
end
Not a bug. Should be a stackoverflow question.
Solutions:
if test = @test pattern 鈥攊nstance variables may be changed concurrently, but a local copy won't;def test; @test ||= something; end.You may alternatively transform the compile-time error into a runtime error, but you that should be limited to times where you're absolutely sure that value can't be nil (hint: almost always a wrong assumption):
.not_nil!;property!.That makes a lot of sense, I will close this issue, thank you.
Most helpful comment
It's not a bug, it's a failsafe!
Nothing prevents you to do:
Using a local variable, this cannot happen: