Repository navigation
Match MRI %c coercion, Hash#default named refs, and hex-float #/0-fill in sprintf - #522
Merged
Merged
Conversation
…ill in sprintf
Wire the format binding to the new go-ruby-format host hooks so
Kernel#sprintf / Kernel#format / String#% match MRI 4.0.6 for cases the
engine could not reach alone:
- formatValue.CoerceChar resolves a %c operand with MRI's to_str-then-to_int
protocol via the method table (never #respond_to?, so a BasicObject that
defines only #to_str is coerced): a String feeds first-character rendering,
an Integer a code point, a non-String/#to_int result raises the matching
"can't convert X into String/Integer" TypeError, and an object answering
neither raises "no implicit conversion of X into Integer".
- formatNamedArgs registers a NamedArgs default resolver so a %<name>/%{name}
reference to an absent key consults the hash's own #[] (Hash#default /
default_proc): a non-nil default is used, a nil one raises MRI's KeyError. A
present key (even a nil value) stays in the map and never hits the resolver.
The hex-float alternate-form (%#a) and zero-fill (%020a), the "%*1$.*2$3$d"
argument-numbered star width/precision, and the width/precision "too big"
ArgumentError are fixed in the engine (go-ruby-format/format#7) and land here
through the dependency bump.
core/string/modulo_spec: 252 -> 263 examples pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…sicObject String#% unpacked its right-hand side by asking respondsToDynamic whether the object answered #to_ary, whose fallback *sends* #respond_to? — which a BasicObject operand does not have, so `"%c" % basic_object_with_to_str` raised NoMethodError instead of coercing. Add formatRespondsTo, which probes the method table first and only dispatches #respond_to? (and thus #respond_to_missing?) to an object that actually provides it, matching MRI's rb_check_funcall. Use it for the %c to_str/to_int coercion and the to_ary unpacking. Covers CoerceChar and the named-hash default resolver with a new format_c_named_test.go. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Engine PR #7 merged; repoint from the PR-branch pseudoversion to the resulting main pseudoversion so the sprintf/format binding builds against released engine code. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
tannevaled
added a commit
that referenced
this pull request
Aug 31, 2026
* Raise ruby/spec floor to 17470 after wave-3 conformance work Locks in the gains from #521 (regexp \xHH), #523 (String#encode fallback/xml/IBM437/default_internal), #524 (Marshal Data + Regexp encoding), #525 (Module#define_method arity/visibility/hooks) and #522 (sprintf %c/named-default/hex-float). Measured 17470 passing on the pinned corpus, up from 17310. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ci: re-trigger after transient GOPROXY stream errors Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <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.
Summary
Advances MRI 4.0.6 conformance of
Kernel#sprintf/Kernel#format/String#%.core/string/modulo_specrises 252 → 263 examples passing (fail 11 → 6, error 8 → 2); the remaining modulo failures are all encoding-subsystem (Encoding::CompatibilityError, US-ASCII/BINARY tracking) or the$DEBUGIOStubharness, outside this cluster.Binding changes (
internal/vm/format_bind.go)%coperand coercion —formatValue.CoerceCharresolves a user object with MRI'sto_str-then-to_intprotocol: a String feeds first-character rendering, an Integer a code point, a non-String/#to_intresult raises the matchingcan't convert X into String/IntegerTypeError, and an object answering neither raisesno implicit conversion of X into Integer.%<name>/%{name}Hash#default—formatNamedArgsregisters aNamedArgsdefault resolver, so a reference to an absent key consults the hash's own#[](Hash#default/default_proc): a non-nil default is used, a nil one raises MRI'sKeyError(with#key/#receiverset).formatRespondsTo— probes a coercion method via the method table and only dispatches#respond_to?(hence#respond_to_missing?) to an object that actually provides it, matching MRI'srb_check_funcall. Fixes"%c" % basic_object_with_to_str, which previously raisedNoMethodError: undefined method 'respond_to?' for BasicObjectwhile unpacking the%right-hand side.The hex-float alternate-form (
%#a→0x1.p+4) and zero-fill (%020a→0x000…1.9p+3), the argument-numbered star width/precision (%*1$.*2$3$d), and the width/precisiontoo bigArgumentErrorare fixed in the format engine (go-ruby-format/format#7) and land here through the dependency bump.Dependency
Important
go.modbumpsgithub.com/go-ruby-format/formatto a commit of the branch behind go-ruby-format/format#7, which is not yet merged. Merge that engine PR first, then re-pin this module to the resultingmainpseudoversion before merging here.Testing
GOWORK=off CGO_ENABLED=0 go test ./...— green.GOOS=js GOARCH=wasm go build ./...— green.gofmt -lon changed files — empty.-coverpkgoverinternal/...) — 100.0%; the newCoerceChar,formatRespondsTo, andformatNamedArgsare each 100%.internal/vm/format_c_named_test.gocovers the coercion, default-resolver, andKeyErrorbranches; every case verified byte-for-byte against ruby 4.0.🤖 Generated with Claude Code