Search code examples
crystal-lang

Nil check in `||`, `!a || a.method` can't be done with multiple variables


For example, if we want to check an array is either nil or empty, we can write as follows:

if !a || a.empty?
    puts "nil or empty!"
end
#-> OK

however, if we want to check two arrays in the same way, an error occurs:

if !a || !b || a.empty? || b.empty?
    puts "nil or empty!"
end
#-> Error, `undefined method 'empty?' for Nil`
#          in the expression `a.empty?`

Swapping the position of !b and a.empty? doesn't help, but different place is pointed to as an error:

if !a || a.empty? || !b || b.empty?
    puts "nil or empty!"
end
#-> Still error, `undefined method 'empty?' for Nil`
#                but this error is in the expression `b.empty?`

Why in this (multiple variable's) case compiler can't infer that a and b are non-nil when a.empty? and b.empty? are called, respectively?

The whole reproducible code is following.

def foo (flag)
    if flag
        [] of Int32
    else
        nil
    end
end

# note, foo returns `Array(T) | Nil`
a = foo(true)
b = foo(true)

if !a || a.empty?
    puts "nil or empty!"
end
#-> OK

if !a || !b || a.empty? || b.empty?
    puts "nil or empty!"
end
#-> Error, `undefined method 'empty?' for Nil`
#          in the expression `a.empty?`

if !a || a.empty? || !b || b.empty?
    puts "nil or empty!"
end
#-> Still error, `undefined method 'empty?' for Nil`
#                but this error is in the expression `b.empty?`

Solution

  • It seems like a compiler bug.

    But you can just put the conditions in parenthesis to make it work: (!a || a.empty?) || (!b || b.empty?)