Bug #6151

ArgumentError of lambda shows wrong location

Added by Thomas Sawyer about 2 years ago. Updated about 1 year ago.

[ruby-core:43314]
Status:Closed
Priority:Normal
Assignee:-
Category:core
Target version:2.0.0
ruby -v:ruby 1.9.3p0 (2011-10-30 revision 33570) [x86_64-linux] Backport:

Description

=begin

Perhaps there is a reason, but it sure seems like a bug to me. Given this simple class:

  #
  class BlockParser < BasicObject
    def initialize(data={}, &block)
      @data = data
      instance_eval(&block)
    end

    def method_missing(name, *args, &block)
      if block
        @data[name] = {}
        BlockParser.new(@data[name], &block)
      else
        case args.size
        when 0
          @data[name]
        when 1
          @data[name] = args.first
        else
          @data[name] = args
        end
      end
    end
  end

Then we can do:

  data = {}
  BlockParser.new(data) do
    name 'Tommy'
  end
  data #=> {:name=>'Tommy'}

But,

  data = {}
  blk  = lambda do
    name 'Tommy'
  end
  BlockParser.new(data, &blk)
  ArgumentError: wrong number of arguments (1 for 0)
    from (irb):44:in `block in irb_binding'
    from (irb):16:in `instance_eval'
    from (irb):16:in `initialize'
    from (irb):46:in `new'
    from (irb):46
    from /home/trans/.rbfu/rubies/1.9.3-p0/bin/irb:12:in `<main>'

If I use (({Proc.new})) instead of (({lambda})) it works. But since I am using (({#instance_eval})) to evaluate the Proc, that shouldn't matter.

Note the reported line number of the error corresponds to (({name 'Tommy'})).
=end

Associated revisions

Revision 35052
Added by Nobuyoshi Nakada about 2 years ago

  • vminsnhelper.c (argumenterror): use line number at the beginning of lambda, not the first code ob its body. [Bug #6151]

History

#1 Updated by Thomas Sawyer about 2 years ago

That's fun, go to report a bug in Ruby and hit a bug in Ruby's Bug tracker.

Try again, this time with no RD crap.

Given:

  #
  class BlockParser < BasicObject
    def initialize(data={}, &block)
      @data = data
      instance_eval(&block)
    end

    def method_missing(name, *args, &block)
      if block
        @data[name] = {}
        BlockParser.new(@data[name], &block)
      else
        case args.size
        when 0
          @data[name]
        when 1
          @data[name] = args.first
        else
          @data[name] = args
        end
      end
    end
  end

  data = {}
  blk = lambda do
    name 'tommy'
  end

  BlockParser.new(data, &blk)
  ArgumentError: wrong number of arguments (1 for 0)

But it works if we use:

  blk = Proc.new do
    name 'tommy'
  end

Since BlockParser is using instance_eval, I don't see why this should be an error. Indeed, how can I depend on it if my API simply accepts a block --I don't know if it's a Proc or a lambda.

#2 Updated by Nobuyoshi Nakada about 2 years ago

  • Description updated (diff)

#3 Updated by Nobuyoshi Nakada about 2 years ago

  • Subject changed from BasicObject instance_eval of lambda causes errors to ArgumentError of lambda shows wrong location
  • Target version set to 2.0.0

You need to indent with spaces, before tabs.

#4 Updated by Nobuyoshi Nakada about 2 years ago

  • Status changed from Open to Closed
  • % Done changed from 0 to 100

fixed in r35051-r35053.

#5 Updated by John Firebaugh about 2 years ago

The reason it raises an error is that instance_eval yields one argument (the receiver), and lambdas are strict about arity -- see #2476.

#6 Updated by Thomas Sawyer about 2 years ago

Just to be clear, where both issues fixed? i.e. the fact that an error was raised in this case at all, as well as the location of lambda errors?

#7 Updated by John Firebaugh about 2 years ago

Just the location was fixed. The error is "by design" -- as you discovered, you will need to use Proc.new (or lambda {|*| ... }).

#8 Updated by Thomas Sawyer about 2 years ago

How can that be by design? What kind of design criteria says that "instance_eval should blow up if a lambda is passed to it"?

So you are saying that instance_eval passes the receiver into the Proc as an argument? Why would it do this when the Block has zero arity? Why not just check the arity of the block and pass it if it can take an argument otherwise not?

This snafu is really ashame for me too, b/c I finally had a good reason to use Ruby 1.9's -> operator!

#9 Updated by Tyler Rick about 1 year ago

I agree, @trans, this is a very surprising behavior. I was expecting instanceeval to call the block I passed to it without any args. Since self is already implicitly available from the block, I just don't understand why instanceeval would yield self as an argument.

Fortunately, I think instanceexec <http://ruby-doc.org/core-2.0/BasicObject.html#method-i-instanceexec> does what we are wanting so I'm using it instead.

We should update the documentation. http://ruby-doc.org/core-2.0/BasicObject.html#method-i-instance_eval does not mention that it yields self as the first argument.

Also available in: Atom PDF