Bug #2502
closedstrange behavior of anonymous class inside a proc
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
#<SubSeq:0x000000007a7e78 @first=11>
#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
Updated by naruse (Yui NARUSE) about 15 years ago
- Category set to core
=begin
This also affect Ruby 1.9.
=end
Updated by naruse (Yui NARUSE) almost 15 years ago
- Status changed from Open to Assigned
- Assignee set to ko1 (Koichi Sasada)
- Priority changed from 5 to 3
=begin
=end
Updated by mame (Yusuke Endoh) over 14 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=1class SubSeq
def initialize
@first=11
@first or fail
enddef subseq
@first or fail
end
endclass Indexed
def subseq
SubSeq.new
end
endOverlaid =proc do
p self
class<<self
def subseq
super.instance_eval(& Overlaid)
end
end
endmid=Indexed.new
mid.instance_eval(&Overlaid)
mid.subseq
p mid
mid.subseqThe output I get is like this:
#Indexed:0x000000007a8930
#<SubSeq:0x000000007a7e78 @first=11>
#Indexed:0x000000007a8930
subseq_first_nil.rb:10: warning: instance variable @first not initialized
subseq_first_nil.rb:10:insubseq': 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
Updated by wanabe (_ wanabe) over 14 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