Add NilClass#=~ - #6999
Merged
Merged
Conversation
`String#=~` dispatches an argument that is neither a Regexp nor a String as `other =~ self`, which is what CRuby's `rb_str_match` does too. CRuby has a defined answer for `nil` because `NilClass#=~` exists and returns nil; mruby had no `=~` on NilClass, so the dispatch raised NoMethodError for `"abc" =~ nil`. `Kernel#!~` is `!(self =~ y)`, so `nil !~ /a/` raised as well even though `!~` is listed among nil's methods. ```ruby "abc" =~ nil # CRuby: nil, mruby: NoMethodError nil !~ /a/ # CRuby: true, mruby: NoMethodError ``` `nil` is the only gap. `Object#=~` was removed in Ruby 3.2, so every other receiver without its own `=~` raises NoMethodError in CRuby too, and mruby already agrees. `=~` is not an ISO method of NilClass, so this is a core addition that CONTRIBUTING.md asks for a reason to make instead of reaching for an mrbgem. `mruby-object-ext` would otherwise be the natural home, since it already extends NilClass with `to_a`, `to_h`, `to_i` and `to_f`. The reason is `Kernel#!~`: it lives in core mrblib, so it is present even in a build that carries no gem at all, and `nil !~ x` is a hole no gem can close. CRuby also answers `nil.respond_to?(:=~)` with true without loading anything, and letting that depend on the gembox a build picks would cost more in surprise than the method costs in space. The method goes in the ROM table next to the other NilClass methods rather than in mrblib, and declaring it `MRB_ARGS_REQ(1)` gives the arity check without a `mrb_get_args()` call, as `false_and()` does. The entry and the function add 48 bytes to `object.o` on x86_64.
`NilClass#=~` gives the gem's `=~` overrides a defined answer for a nil argument, so record it where the dispatch actually happens: a nil argument returns nil and `!~` returns true, while an argument without its own `=~` still raises NoMethodError. These cases need Regexp, so they belong here rather than in the core NilClass test. The `symbol_regexp.rb` header comment claimed `:a =~ nil` raises NoMethodError where CRuby returns nil. That was the last difference the comment listed, and it is now gone: with `Object#=~` removed in Ruby 3.2 the remaining behaviour matches CRuby on every argument type.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (5)
📝 WalkthroughWalkthroughThe match operator now supports ChangesMatch operator behavior
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
This was referenced Aug 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
String#=~dispatches an argument that is neither a Regexp nor a String asother =~ self, which is what CRuby'srb_str_matchdoes too. CRuby has a definedanswer for
nilbecauseNilClass#=~exists and returnsnil; mruby had no=~onNilClass, so the dispatch raisedNoMethodError.Kernel#!~(mrblib/kernel.rb:38)is
!(self =~ y), so it inherited the same hole:!~is listed amongnil's methods,and calling it raised.
nilis the only gap.Object#=~was removed in Ruby 3.2, so every other receiverwithout its own
=~raisesNoMethodErrorin CRuby as well, and mruby already agrees.Why the core and not an mrbgem
=~is not an ISO method ofNilClass(ISO 15.2.4.3 lists&,|,^,nil?andto_s), soCONTRIBUTING.mdasks for a reason to add it to the core.mruby-object-extwould otherwise be the natural home: it already extends
NilClasswithto_a,to_h,to_iandto_fin a ROM table of its own.The reason is
Kernel#!~. It lives in core mrblib and is therefore present in everybuild, including one that carries no gem at all, so
nil !~ xis a hole no gem canclose. CRuby also answers
nil.respond_to?(:=~)with true without loading anything;making that depend on which gembox a build picks would be a worse surprise than the
space the method takes. That space is 48 bytes of
object.oon x86_64: the method goesinto the existing
NilClassROM table, so it adds one entry and one function, and noirep.
Declaring the entry
MRB_ARGS_REQ(1)gives the arity check without amrb_get_args()call, the same way
false_and()does.What is in the PR
src/object.c:nil_match()and its ROM table entry.test/t/nil.rb: the core cases, which need no Regexp: any argument answersnil,nil.respond_to?(:=~), the!~path, and the arity check.mrbgems/mruby-regexp/test/: the cases that do need Regexp, in the gem that owns thedispatch:
"abc" =~ nil,"abc" !~ nil,:hello =~ nil, and theNoMethodErrorrows above, which pin that this change does not turn
=~into a blanket type check.mrbgems/mruby-regexp/mrblib/symbol_regexp.rbnamed this gap asbehaviour inherited from
String#=~. WithNilClass#=~in place the argument handlingof
Symbol#=~agrees with CRuby on every argument type, so the comment is rewritten.=~deliberately keeps dispatching rather than type-checking its argument, unlikematchandmatch?(#6988): a blanketTypeErrorwould break theniland theNoMethodErrorrows above, both of which CRuby answers the way mruby now does.Compared against CRuby 4.0.6.
rake testpasses, andprek run --all-filesis clean.Summary by CodeRabbit
New Features
nil, returningnilfor all operands.nil !~expressions.Bug Fixes
niloperands return expected results.Tests