Skip to content

Conversation

@byroot
Copy link
Member

@byroot byroot commented Nov 24, 2025

Fix: #35
[Bug #21708]

Trying to compile code to check if a method can use the delegation fastpath is a bit wasteful and cause RUPYOPT=-d to be full of misleading errors.

It's simpler and faster to use a simple regexp to do the same check.

@byroot byroot requested review from hsbt, jeremyevans and nobu November 24, 2025 07:52

method_call = ".__send__(:#{method}, *args, &block)"
if _valid_method?(method)
if method.match?(/\A[_a-zA-Z]\w*[?!]?\z/)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But this will fail if the method has any Unicode characters in its name, no?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By the way, if this is trying to check if the method name is a valid identifier then Prism has methods for doing that check out of the box. Maybe we should use those

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's checking for two things, first that it's a valid name, but also that it can be called with ..

e.g. foo= is valid, but _.foo = *args, &block isn't.

But this will fail if the method has any Unicode characters in its name, no?

Yes, but the idea is to be conservative, the regexp can be improved (or we could use prism) if we want, but as long as 99.9% of methods go in the fast path, it's fine IMO.

@byroot byroot force-pushed the avoid-syntax-errors branch from 508eed6 to 6a2eff9 Compare November 24, 2025 10:13
@Earlopain
Copy link

Earlopain commented Nov 24, 2025

You deleted forwardable/impl but the require for that file is still there. It works because it picks up the file that gets embedded/bundled in ruby itself (I think). Would probably fail CI once it actually gets synced over

Fix: ruby#35
[Bug #21708]

Trying to compile code to check if a method can use the delegation
fastpath is a bit wasteful and cause `RUPYOPT=-d` to be full of
misleading errors.

It's simpler and faster to use a simple regexp to do the same check.
@byroot byroot force-pushed the avoid-syntax-errors branch from 6a2eff9 to de1fbd1 Compare November 24, 2025 10:44
@byroot
Copy link
Member Author

byroot commented Nov 24, 2025

@Earlopain good catch, fixed now.

Copy link
Contributor

@jeremyevans jeremyevans left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the approach, I think there is just one issue with it.

end
end;
method_call = <<~RUBY.chomp
if defined?(_.#{method})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before Ruby 3, using defined? for methods does not work correctly if the method has been refined but the refinement is not activated. See https://bugs.ruby-lang.org/issues/16932 . I found this out the hard way in Sequel: jeremyevans/sequel#1919 .

In this case, defined? would return truthy if the method had an unactivated refinement. If the method being refined was private and the refinement method is public, I believe this would cause the fast path to be used in a case where it would fail.

Example:

module M
  refine Object do
    public :puts
  end
end

o = Object.new
o.puts '' if defined?(o.puts)

On Ruby < 3, we could probably use something like Kernel === _ && _.respond_to?(:#{method}) . Or we could bump the required_ruby_version to 3.0.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with making that change, but note that I only moved the defined? so it's a pre-existing bug, probably better to handle it in a dedicated PR?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, that makes sense.

Copy link
Member

@hsbt hsbt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't know how -d option worked until yesterday, but after talking with @ko1 and @mame yesterday, I finally understood.

I also like this.

@hsbt hsbt merged commit 6e3e2b3 into ruby:master Dec 10, 2025
26 checks passed
@byroot
Copy link
Member Author

byroot commented Dec 10, 2025

This seems to have broken Ruby master:

/Users/runner/work/ruby/ruby/build/install/lib/ruby/4.0.0+0/forwardable.rb:226:in 'Kernel#eval': 
/Users/runner/work/ruby/ruby/build/install/lib/ruby/4.0.0+0/forwardable.rb:229: syntax error found (SyntaxError)
  227 | proc do
  228 |   def load_tags=(...)
> 229 |     false(config())
      |          ^ unexpected '(', expecting end-of-input
  230 |     .__send__(:load_tags=, ...)
  231 |   end

	from /Users/runner/work/ruby/ruby/build/install/lib/ruby/4.0.0+0/forwardable.rb:226:in 'Forwardable._delegator_method'
	from /Users/runner/work/ruby/ruby/build/install/lib/ruby/4.0.0+0/forwardable.rb:189:in 'Forwardable#def_instance_delegator'
	from /Users/runner/work/ruby/ruby/build/install/lib/ruby/4.0.0+0/forwardable.rb:159:in 'block in Forwardable#def_instance_delegators'
	from /Users/runner/work/ruby/ruby/build/install/lib/ruby/4.0.0+0/forwardable.rb:157:in 'Array#each'
	from /Users/runner/work/ruby/ruby/build/install/lib/ruby/4.0.0+0/forwardable.rb:157:in 'Forwardable#def_instance_delegators'
	from /Users/runner/work/ruby/ruby/build/install/lib/ruby/gems/4.0.0+0/gems/psych-3.3.4/lib/psych.rb:654:in 'singleton class'
	from /Users/runner/work/ruby/ruby/build/install/lib/ruby/gems/4.0.0+0/gems/psych-3.3.4/lib/psych.rb:636:in '<module:Psych>'
	from /Users/runner/work/ruby/ruby/build/install/lib/ruby/gems/4.0.0+0/gems/psych-3.3.4/lib/psych.rb:234:in '<top (required)>'
	from /Users/runner/work/ruby/ruby/build/install/lib/ruby/4.0.0+0/bundled_gems.rb:60:in 'Kernel.require'

I'll look into why.

@hsbt
Copy link
Member

hsbt commented Dec 10, 2025

@byroot Thanks. I and @nobu also shared it to @k0kubun

@byroot
Copy link
Member Author

byroot commented Dec 10, 2025

So false(config()) suggest that somehow, the pre variable is set to false, but I really don't see how that can possibly happen...

@byroot
Copy link
Member Author

byroot commented Dec 10, 2025

World this be a ZJIT bug? Seems like the ruby-bench CI only runs with ZJIT?

@hsbt
Copy link
Member

hsbt commented Dec 10, 2025

World this be a ZJIT bug? Seems like the ruby-bench CI only runs with ZJIT?

@k0kubun said it expect nil instead of false.

@k0kubun
Copy link
Member

k0kubun commented Dec 10, 2025

It's a ZJIT bug. I excluded the failed benchmarks from the ZJIT CI for now ruby/ruby#15479, and also added a non-ZJIT ruby-bench job to make it easier to understand whether it's about ZJIT or not ruby/ruby#15480.

We'll work on fixing ZJIT to un-exclude the benchmarks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Ruby Debug showing very repeditive error in impl.rb

6 participants