In case statement, case nil and range

this code doesn’t compile.

ch = nil
case ch
when 'A' .. 'Z'
  puts "ch is capital"
when Nil
  puts "ch is nil"
end

In case statement, if no range expression, it is compiled successfully.

Put the when Nil first, would be the easiest way to fix this.

It’s a bug, please report it. Thank you!

1 Like

Following code still not work on 1.7.2, any one report bug on this?

ch : Char? = nil

case ch
when 'A'..'Z'
  puts "ch is capital"
when Nil
  puts "ch is nil"
end
In 1.cr:13:6

 13 | when 'A'..'Z'
           ^-
Error: instantiating 'Range(Char, Char)#===(Nil)'


In /home/zw963/Crystal/share/crystal/src/range.cr:327:5

 327 | includes?(value)
       ^--------
Error: instantiating 'includes?(Nil)'


In /home/zw963/Crystal/share/crystal/src/range.cr:298:32

 298 | (begin_value.nil? || value >= begin_value) &&
                                  ^
Error: undefined method '>=' for Nil

Nil trace:

  /home/zw963/Crystal/share/crystal/src/range.cr:298

        (begin_value.nil? || value >= begin_value) &&


  /home/zw963/Crystal/share/crystal/src/range.cr:293

      def includes?(value) : Bool

because it is not really a bug.

the switch case ... block is translated to a series of if ... then ... elsif ... then ....
another feature of crystal is can predict what a variable is null or not, by using if guard.

So if you put when Nil first if will be translated to:

if ch.is_a?(Nil)
...
else # we know that ch is not nil, thus `Char` here
....

I guess the compiler can be a little smarter to always put Nil on top of the expanded expression, but this is unexpected and can create subtle bugs for someone who expect the conditions match sequentially.
So unless we can make sure that Nil condition can be put at top without causing any problem it’s better not doing anything.

1 Like

Thanks, this error raised by following translated code.

if ('A'..'Z') === ch
  puts "ch is capital"
elsif Nil === ch
  puts "ch is nil"
end

Maybe we should mention this in our syntax_and_semantics/case.html, because this is still a surprise, This is also what the Ruby tries to avoid (matz’s principle of least surprise)

We should always check Nil out of case statement. e.g. like this:

ch : Char? = nil

if ch.nil?
  puts "ch is nil"
else
  case ch
  when 'A'..'Z'
    puts "ch is capital"
  when others
    # ...
  end
end

This is bug. Could you report it please? Thank you!

To clarify, the code should compile just fine. === must return nil if it can’t match the value, never give a compilation error.