Project

General

Profile

Feature #14267

Lazy proc allocation introduced in #14045 creates regression

Added by myronmarston (Myron Marston) over 2 years ago. Updated 3 months ago.

Status:
Closed
Priority:
Normal
Assignee:
-
Target version:
-
[ruby-core:84583]
Tags:

Description

The following script consistently prints Proc equality: true on versions of Ruby before 2.5, but prints Proc equality: false on Ruby 2.5:

# regression.rb
def return_proc(&block)
  block
end

def return_procs(&block)
  block.inspect if ENV['INSPECT_BLOCK']

  proc_1 = return_proc(&block)
  proc_2 = return_proc(&block)

  return proc_1, proc_2
end

proc_1, proc_2 = return_procs { }

puts RUBY_VERSION
puts "Proc equality: #{proc_1 == proc_2}"

Here's the output on Ruby 2.4 and 2.5:

$ chruby 2.4
$ ruby regression.rb
2.4.2
Proc equality: true
$ chruby 2.5
$ ruby regression.rb
2.5.0
Proc equality: false

As the output shows, the two procs were equal on 2.4 but are no longer equal on 2.5. I believe this is due to the lazy proc allocation introduced in #14045. Note that if I call a method on the proc (such as inspect) it defeats the lazy allocation and "fixes" the regression:

$ chruby 2.5
$ INSPECT_BLOCK=1 ruby regression.rb
2.5.0
Proc equality: true

This caused a bug in RSpec, which I've worked around for now by calling __id__ on the proc.

Is there a way to keep the lazy proc allocation while fixing this regression?


Related issues

Related to Ruby master - Feature #14045: Lazy Proc allocation for block parametersClosedko1 (Koichi Sasada)Actions
#1

Updated by duerst (Martin Dürst) over 2 years ago

  • Related to Feature #14045: Lazy Proc allocation for block parameters added

Updated by jeremyevans0 (Jeremy Evans) 4 months ago

  • Backport deleted (2.3: UNKNOWN, 2.4: UNKNOWN, 2.5: UNKNOWN)
  • ruby -v deleted (ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin15])
  • Tracker changed from Bug to Feature

I don't think this is a bug, just a detail of the implementation that changed when the optimization was applied. The Proc objects created are distinct. As Proc#== and #eql? are not defined, Object#== and #eql? are used, and as the objects are distinct, two distinct procs will not be considered equal.

I do think adding Proc#== and Proc#eql? makes sense, so I added a pull request for it: https://github.com/ruby/ruby/pull/3174

Updated by jeremyevans0 (Jeremy Evans) 4 months ago

In my pull request, I propose the following definition of proc equivalence:

  • The procs have the same class
  • The procs either are both lambdas or both non-lambdas
  • The procs either are both created from a method or both not created from a method
  • The procs have the same block type (iseq, ifunc, symbol, proc)
    • If iseq, the proc blocks have the same ec and iseq
    • If ifunc, the proc blocks have the same ec and ifunc
    • If symbol, the proc blocks use the same symbol
    • If proc, the proc blocks use the same proc

I'm not sure if this definition is perfect, though it seems better than the object equivalence currently used. Someone with more knowledge of proc types should probably review and see if there are other conditions that should be met for equivalence.

Updated by mame (Yusuke Endoh) 4 months ago

I'm not so positive for the change. In general, comparing Procs is not a good idea, and I'm unsure if it is worth helping such a use case.

The motivation of OP is not explained, so I'm unsure if the PR help OP's problem. If we merge the PR, it would be good to confirm it.

Updated by ko1 (Koichi Sasada) 3 months ago

matz is positive.

Is RSpec problem solved with this patch?

Updated by jeremyevans0 (Jeremy Evans) 3 months ago

ko1 (Koichi Sasada) wrote in #note-5:

matz is positive.

Is RSpec problem solved with this patch?

I've added a GitHub issue asking the RSpec developers to confirm whether the patch removes the need for the workaround: https://github.com/rspec/rspec-core/issues/2740

Updated by jeremyevans0 (Jeremy Evans) 3 months ago

jeremyevans0 (Jeremy Evans) wrote in #note-6:

I've added a GitHub issue asking the RSpec developers to confirm whether the patch removes the need for the workaround: https://github.com/rspec/rspec-core/issues/2740

RSpec developers confirm the patch fixes the problem and removes the need for their workaround. Since matz is positive, I plan to merge the pull request later this week.

#8

Updated by jeremyevans (Jeremy Evans) 3 months ago

  • Status changed from Open to Closed

Applied in changeset git|878af5147def7fed089d3cc388742f0111db58ae.


Implement Proc#== and #eql?

Previously, these were not implemented, and Object#== and #eql?
were used. This tries to check the proc internals to make sure
that procs created from separate blocks are treated as not equal,
but procs created from the same block are treated as equal, even
when the lazy proc allocation optimization is used.

Implements [Feature #14267]

Also available in: Atom PDF