Respond_to? difference btwn 2.2.3 and 2.3.0

I’m surprised by this difference between Ruby 2.2.3 and 2.3.0:

···

====
$ ./ruby -e "puts RUBY_DESCRIPTION ; rand(BasicObject.new)"
ruby 2.2.3p173 (2015-08-18 revision 51636) [x86_64-darwin14]
-e:1:in `rand': undefined method `respond_to?' for #<BasicObject:0x007fa2d719bf78> (NoMethodError)
  from -e:1:in `<main>'

$ ./ruby -e "puts RUBY_DESCRIPTION ; rand(BasicObject.new)"
ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-darwin14]
-e:1:in `rand': undefined method `begin' for #<BasicObject:0x007fe0be4cb548> (NoMethodError)
  from -e:1:in `<main>'

Tracing it around in 2.3.0 leads to vm_respond_to, where method_entry_get is returning NULL, and that’s resulting in true being returned.

Has anyone seen any notes around this behavior change?

Thanks,

Tom

In general, I'd say the behaviour of methods when passed bad arguments is
left undefined.

If I call rand(invoice), Ruby is free to fail in different ways as a
side-effect of internal changes because there is no contract to guarantee.
It is even free to succeed and return a number, because undefined behaviour
includes returning something.

···

On Friday, 24 June 2016, Tom Copeland <tom@thomasleecopeland.com> wrote:

I’m surprised by this difference between Ruby 2.2.3 and 2.3.0:

====
$ ./ruby -e "puts RUBY_DESCRIPTION ; rand(BasicObject.new)"
ruby 2.2.3p173 (2015-08-18 revision 51636) [x86_64-darwin14]
-e:1:in `rand': undefined method `respond_to?' for
#<BasicObject:0x007fa2d719bf78> (NoMethodError)
        from -e:1:in `<main>'

$ ./ruby -e "puts RUBY_DESCRIPTION ; rand(BasicObject.new)"
ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-darwin14]
-e:1:in `rand': undefined method `begin' for
#<BasicObject:0x007fe0be4cb548> (NoMethodError)
        from -e:1:in `<main>'

Tracing it around in 2.3.0 leads to vm_respond_to, where method_entry_get
is returning NULL, and that’s resulting in true being returned.

Has anyone seen any notes around this behavior change?

Thanks,

Tom

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org <javascript:;>
?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk&gt;

--
Sent from Gmail Mobile

I agree that rand() shouldn’t have to handle any type as an argument. And generally it doesn’t, for example:

···

=====
ruby-2.3.0> $ ./ruby -e "rand({})"
-e:1:in `rand': no implicit conversion of Hash into Integer (TypeError)
  from -e:1:in `<main>’

ruby-2.3.0> $ ./ruby -e "class Foo ; end ; rand(Foo.new)"
-e:1:in `rand': no implicit conversion of Foo into Integer (TypeError)
  from -e:1:in `<main>'

That seems about right to me.

I guess I’m surprised to see the change in behavior with BasicObject though. I would have expected Ruby to bail out immediately when it can’t introspect on the argument to see if it is a Range… instead, it determines that the argument *is* a Range and carries on until it runs into a “harder” failure later on in rb_range_values.

FWIW, this all stemmed from a Rails 4.1 upgrade from 2.2.3 to 2.3.0; I had some code like this:

=====
rand(10.minutes)

and that turned up this (to me) oddity. The fix is easy, just:

=====
rand(10.minutes.to_i)

Anyhow, not a big deal, just something I found surprising. Maybe I can write it up a little more in a blog post.

On Jun 24, 2016, at 6:35 PM, Xavier Noria <fxn@hashref.com> wrote:

In general, I'd say the behaviour of methods when passed bad arguments is left undefined.

If I call rand(invoice), Ruby is free to fail in different ways as a side-effect of internal changes because there is no contract to guarantee. It is even free to succeed and return a number, because undefined behaviour includes returning something.

On Friday, 24 June 2016, Tom Copeland <tom@thomasleecopeland.com <mailto:tom@thomasleecopeland.com>> wrote:
I’m surprised by this difference between Ruby 2.2.3 and 2.3.0:

====
$ ./ruby -e "puts RUBY_DESCRIPTION ; rand(BasicObject.new)"
ruby 2.2.3p173 (2015-08-18 revision 51636) [x86_64-darwin14]
-e:1:in `rand': undefined method `respond_to?' for #<BasicObject:0x007fa2d719bf78> (NoMethodError)
        from -e:1:in `<main>'

$ ./ruby -e "puts RUBY_DESCRIPTION ; rand(BasicObject.new)"
ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-darwin14]
-e:1:in `rand': undefined method `begin' for #<BasicObject:0x007fe0be4cb548> (NoMethodError)
        from -e:1:in `<main>'

Tracing it around in 2.3.0 leads to vm_respond_to, where method_entry_get is returning NULL, and that’s resulting in true being returned.

Has anyone seen any notes around this behavior change?

Thanks,

Tom

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org <javascript:;>?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk&gt;

--
Sent from Gmail Mobile

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk&gt;

Ah ha! It was a Ruby bug:

I wrote up a post on how it manifested itself for me:

http://thomasleecopeland.com/2016/06/26/basicobject-is-a-range.html

···

On Jun 24, 2016, at 8:14 PM, Tom Copeland <tom@thomasleecopeland.com> wrote:

I agree that rand() shouldn’t have to handle any type as an argument. And generally it doesn’t, for example:

=====
ruby-2.3.0> $ ./ruby -e "rand({})"
-e:1:in `rand': no implicit conversion of Hash into Integer (TypeError)
  from -e:1:in `<main>’

ruby-2.3.0> $ ./ruby -e "class Foo ; end ; rand(Foo.new)"
-e:1:in `rand': no implicit conversion of Foo into Integer (TypeError)
  from -e:1:in `<main>'

That seems about right to me.

I guess I’m surprised to see the change in behavior with BasicObject though. I would have expected Ruby to bail out immediately when it can’t introspect on the argument to see if it is a Range… instead, it determines that the argument *is* a Range and carries on until it runs into a “harder” failure later on in rb_range_values.

FWIW, this all stemmed from a Rails 4.1 upgrade from 2.2.3 to 2.3.0; I had some code like this:

=====
rand(10.minutes)

and that turned up this (to me) oddity. The fix is easy, just:

=====
rand(10.minutes.to_i)

Anyhow, not a big deal, just something I found surprising. Maybe I can write it up a little more in a blog post.

On Jun 24, 2016, at 6:35 PM, Xavier Noria <fxn@hashref.com <mailto:fxn@hashref.com>> wrote:

In general, I'd say the behaviour of methods when passed bad arguments is left undefined.

If I call rand(invoice), Ruby is free to fail in different ways as a side-effect of internal changes because there is no contract to guarantee. It is even free to succeed and return a number, because undefined behaviour includes returning something.

On Friday, 24 June 2016, Tom Copeland <tom@thomasleecopeland.com <mailto:tom@thomasleecopeland.com>> wrote:
I’m surprised by this difference between Ruby 2.2.3 and 2.3.0:

====
$ ./ruby -e "puts RUBY_DESCRIPTION ; rand(BasicObject.new)"
ruby 2.2.3p173 (2015-08-18 revision 51636) [x86_64-darwin14]
-e:1:in `rand': undefined method `respond_to?' for #<BasicObject:0x007fa2d719bf78> (NoMethodError)
        from -e:1:in `<main>'

$ ./ruby -e "puts RUBY_DESCRIPTION ; rand(BasicObject.new)"
ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-darwin14]
-e:1:in `rand': undefined method `begin' for #<BasicObject:0x007fe0be4cb548> (NoMethodError)
        from -e:1:in `<main>'

Tracing it around in 2.3.0 leads to vm_respond_to, where method_entry_get is returning NULL, and that’s resulting in true being returned.

Has anyone seen any notes around this behavior change?

Thanks,

Tom

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org <javascript:;>?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk&gt;

--
Sent from Gmail Mobile

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk&gt;

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk&gt;