Repository navigation
Kernel#inspect: honour #instance_variables_to_inspect, the 4.0 hook - #894
Merged
Merged
Conversation
Two more of the 27 examples that went red on the move to Ruby 4.0 (#799). `core/kernel/inspect_spec.rb` goes 6 → 8, fail=0. The other two examples in that `ruby_version_is "4.0"` block already passed, and that is worth saying plainly: they assert "show everything" for a nil return and for an undefined hook, which is what rbgo did anyway. Two of the four were green for a reason that had nothing to do with the feature. ## The reference object.c's rb_obj_inspect at v4.0.9: VALUE ivars = rb_check_funcall(obj, id_instance_variables_to_inspect, 0, 0); if (UNDEF_P(ivars) || NIL_P(ivars)) { n = rb_ivar_count(obj); ivars = Qnil; } else if (RB_TYPE_P(ivars, T_ARRAY)) { n = RARRAY_LEN(ivars); } else rb_raise(rb_eTypeError, "Expected #instance_variables_to_inspect to " "return an Array or nil, but it returned %"PRIsVALUE, ...); and Kernel gains a private method of that name returning nil (rb_obj_instance_variables_to_inspect). Its name was already in this repository's kernelPrivateNames, so the rehome pass was ready for it — nothing had ever defined it, and a listed name that does not exist reports the same `false` as one that was never meant to be there. ## Three details that were measured, not inferred Each came back from MRI 4.0.7 on this host and none of them is what reading the C would most naturally suggest: * The elements are compared as SYMBOLS, by identity. `["@host", "@user"]` matches nothing and inspects as `#<Object:0x…>` — n is 2, so the filtering branch runs, and a String that spells the name is not the name. * The ORDER is the object's, not the list's. `%i[@user @host]` still prints `@host=…, @user=…`, because inspect_i walks the object's variables and tests each for membership. * rb_check_funcall ignores visibility, so a hook defined public behaves exactly like the private one the documentation shows. ## The dispatch is skipped when it would answer nil #inspect is on the path of every `p` and every error message, and consulting this hook means a method call per inspect that almost never changes anything. inspectIvarFilter resolves the name and compares the record against Kernel's default; equal means "nobody overrode it" and the send is skipped. A test takes each way of overriding it — on the class, inherited, re-overridden below, mixed in from a module — because a fast path that swallowed one of those would pass every other row here. ## Measured rubydiff against MRI 4.0.7: AGREE on 14 probes covering the return shapes, the visibilities and the error messages, and on 7 more covering the override routes. specdelta, provenanced, over core/kernel, core/module, core/method, core/basicobject, core/objectspace and language — the directories where adding a private Kernel method would show up: 351 files, control 6048, treatment 6050, +2, 1 file moved, 0 regressed. Coverage: inspectIvarFilter and defaultObjectInspect at 100.0%. The last uncovered branch was the "hook not defined at all" path, which only a BasicObject reaches, through a bound Kernel#inspect. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
Two more of the 27 examples that went red on the move to Ruby 4.0 (#799).
core/kernel/inspect_spec.rbgoes 6 → 8, fail=0.The other two examples in that
ruby_version_is "4.0"block already passed,and that is worth saying plainly: they assert show everything for a nil return
and for an undefined hook, which is what rbgo did anyway. Half the block was
green for a reason that had nothing to do with the feature.
The reference
object.c'srb_obj_inspectatv4.0.9:Kernel also gains a private method of that name returning nil. Its name was
already in this repository's
kernelPrivateNames, so the rehome pass was readyfor it — nothing had ever defined it, and a listed name that does not exist
reports the same
falseas one that was never meant to be there.⛔ Three details that were measured, not inferred
None of these is what reading the C would most naturally suggest:
["@host", "@user"]#<Object:0x…>%i[@user @host]@host=…, @user=…inspect_iwalks the object's variables and tests membershiprb_check_funcallignores visibilityThe dispatch is skipped when it would answer nil
#inspectis on the path of everypand every error message, and consultingthis hook means a method call per inspect that almost never changes anything.
inspectIvarFilterresolves the name and compares the record against Kernel'sdefault; equal means nobody overrode it, and the send is skipped.
A test takes each way of overriding it — on the class, inherited,
re-overridden below, mixed in from a module — because a fast path that
swallowed one of those would still pass every other row.
Measured
rubydiffagainst MRI 4.0.7: AGREE on 14 probes covering the return shapes,the visibilities and the error messages, and on 7 more covering the override
routes.
specdelta, provenanced, overcore/kernel,core/module,core/method,core/basicobject,core/objectspaceandlanguage— the directories whereadding a private Kernel method would show up:
Coverage:
inspectIvarFilteranddefaultObjectInspectat 100.0%. The lastuncovered branch was the hook not defined at all path, which only a
BasicObject reaches, through a bound
Kernel#inspect.Independent of #892 and #893.
🤖 Generated with Claude Code