crystal-lang / crystal

The Crystal Programming Language
https://crystal-lang.org
Apache License 2.0
19.45k stars 1.62k forks source link

`('A'..'Z') === nil` must return nil if it can’t match the value instead of give a compilation error. #13094

Open zw963 opened 1 year ago

zw963 commented 1 year ago

Bug Report

For more details, check discuss on https://forum.crystal-lang.org/t/in-case-statement-case-nil-and-range/2623/2

Following code not work on Crystal 1.7.2

ch : Char? = nil

case ch
when 'A'..'Z'
  puts "ch is capital"
when Nil
  puts "ch is nil"
end
Click me to see back trace ``` 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 ```

Fix is simple, just swap the position.

ch : Char? = nil

case ch
when Nil
  puts "ch is nil"
when 'A'..'Z'
  puts "ch is capital"
end
devnote-dev commented 1 year ago

You're comparing against Nil the class, not nil the type.

Nevermind, it seems to expand to the class either way.

straight-shoota commented 1 year ago

Object#===(other) is defined for every argument type (it just returns false by default). It's not expected to be type-save. And so must Range#===(other).

Fixing the generic implementation is a bit tricky, though. It needs to check whether the comparison between other and begin/end are allowed.