Bug #2502

strange behavior of anonymous class inside a proc

Added by caleb clausen about 5 years ago. Updated almost 4 years ago.

[ruby-core:27230]
Status:Closed
Priority:Low
Assignee:Koichi Sasada
ruby -v:ruby 1.9.1p376 (2009-12-07 revision 26040 [x86_64-linux] Backport:

Description

=begin
I have a problem in 1.9.1 when I run this code, extracted from a larger project:
$VERBOSE=1

class SubSeq
def initialize
@first=11
@first or fail
end

 def subseq 
   @first or fail
 end    

end

class Indexed

def subseq
SubSeq.new
end
end

Overlaid =proc do
p self
class<<self
def subseq
super.instance_eval(& Overlaid)
end
end
end

mid=Indexed.new
mid.instance_eval(&Overlaid)
mid.subseq
p mid
mid.subseq

The output I get is like this:
#Indexed:0x000000007a8930
#
#Indexed:0x000000007a8930
subseq_first_nil.rb:10: warning: instance variable @first not initialized
subseq_first_nil.rb:10:in subseq': unhandled exception
from subseq_first_nil.rb:24:in
subseq'
from subseq_first_nil.rb:33:in `'

This code should run without raising an exception, and does in ruby 1.8. The exception is raised from within the last line, the second call to mid.subseq. Both mid.subseq calls should behave exactly the same. The first appears to do all the right things. But in the second, it goes all awry. They should both call first the anonymous class's #subseq, then (via the super) Indexed#subseq, which ultimately returns a SubSeq object, also decorated by the anonymous class. SubSeq#subseq itself should never be called, but somehow (given the top line of the exception traceback and the warning) it is. SubSeq's @first should never be nil, since it is initialized in the constructor and never changed, but somehow it is.

This is the last remaining (serious) problem in porting redparse to 1.9. It causes redparse to incorrectly handle certain here documents which work fine when run in 1.8. I'd appreciate it very much of this problem can be fixed.

I've observed this in 1.9.1, but not 1.9.2.
=end


Related issues

Related to Ruby trunk - Bug #3351: stack overflow on super Open 05/27/2010
Duplicated by Ruby trunk - Bug #3136: reuse of singleton method definition causes SEGV Closed 04/12/2010

Associated revisions

Revision 29063
Added by _ wanabe over 4 years ago

  • vm.c (vm_define_method): copy iseq to avoid overwriting iseq->klass. #2502, #3136. see #2420.

Revision 29063
Added by _ wanabe over 4 years ago

  • vm.c (vm_define_method): copy iseq to avoid overwriting iseq->klass. #2502, #3136. see #2420.

History

#1 Updated by Yui NARUSE about 5 years ago

  • Category set to core

=begin
This also affect Ruby 1.9.
=end

#2 Updated by Yui NARUSE about 5 years ago

  • Status changed from Open to Assigned
  • Assignee set to Koichi Sasada
  • Priority changed from High to Low

=begin

=end

#3 Updated by Yusuke Endoh almost 5 years ago

=begin
Hi,

2009/12/19 caleb clausen redmine@ruby-lang.org:

I have a problem in 1.9.1 when I run this code, extracted from a larger project:
$VERBOSE=1

class SubSeq
def initialize
@first=11
@first or fail
end

def subseq
@first or fail
end
end

class Indexed
def subseq
SubSeq.new
end
end

Overlaid =proc do
p self
class<<self
def subseq
super.instance_eval(& Overlaid)
end
end
end

mid=Indexed.new
mid.instance_eval(&Overlaid)
mid.subseq
p mid
mid.subseq

The output I get is like this:
#Indexed:0x000000007a8930
#
#Indexed:0x000000007a8930
subseq_first_nil.rb:10: warning: instance variable @first not initialized
subseq_first_nil.rb:10:in subseq': unhandled exception
from subseq_first_nil.rb:24:in
subseq'
from subseq_first_nil.rb:33:in `'

This code should run without raising an exception, and does in ruby 1.8. The exception is raised from within the last line, the second call to mid.subseq. Both mid.subseq calls should behave exactly the same. The first appears to do all the right things. But in the second, it goes all awry. They should both call first the anonymous class's #subseq, then (via the super) Indexed#subseq, which ultimately returns a SubSeq object, also decorated by the anonymous class. SubSeq#subseq itself should never be called, but somehow (given the top line of the exception traceback and the warning) it is. SubSeq's @first should never be nil, since it is initialized in the constructor and never changed, but somehow it is.

This is the last remaining (serious) problem in porting redparse to 1.9. It causes redparse to incorrectly handle certain here documents which work fine when run in 1.8. I'd appreciate it very much of this problem can be fixed.

Definitely, this is a bug. But it is very difficult to fix because
this is a design flaw of YARV.

Each method definition (more precisely, rb_iseq_t) has information
of class that the method belongs to. This information is used to
identify the parent class and method when super is called.

This information belongs to each lexical method definition.
Your code uses the same definition of method `subseq' twice, to an
instance of Indexed, and to an instance of SubSeq.
The first definition sets the information to Indexed, and the second
overwrites it to SubSeq. This causes inconsistency between class
of self and class that a super'ed method belongs to.

To fix this issue, the information must be moved from rb_iseq_t to
stack frame, which requires major upgrade.

So, towards 1.9.2 release, we plan to pass on the fix of this issue,
by prohibiting super in such a situation (the above code raises an
NotImplementedError). We will fix it in 1.9.3 or later.

You can use Module#include as a workaround (`subseq' belong to the
Workaround module, so the above issue does not occur):

module Workaround
def subseq
super.instance_eval(& Overlaid)
end
end

Overlaid =proc do
p self
class<<self
include Workaround
end
end

You can also use eval (the lexical definition is duplicated):

Overlaid = proc do
p self
class<<self
eval %q{
def subseq
super.instance_eval(& Overlaid)
end
}
end
end

--
Yusuke Endoh mame@tsg.ne.jp
=end

#4 Updated by _ wanabe over 4 years ago

  • Status changed from Assigned to Closed

=begin
This issue was solved with changeset r29063.
Yusuke, thank you for reporting this issue.
Your contribution to Ruby is greatly appreciated.
May Ruby be with you.

=end

Also available in: Atom PDF