From: Caleb Clausen Date: 2010-05-22T10:31:04+09:00 Subject: [ruby-core:30361] Re: [Bug #2502] strange behavior of anonymous class inside a proc On 5/19/10, Yusuke Endoh wrote: > Issue #2502 has been updated by Yusuke Endoh. > > > Hi, > > 2009/12/19 caleb clausen : >> 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<> 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: >> # >> # >> # >> 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< include Workaround > end > end > > > You can also use eval (the lexical definition is duplicated): > > Overlaid = proc do > p self > class< eval %q{ > def subseq > super.instance_eval(& Overlaid) > end > } > end > end Thanks for the explanation. It's good to have some visibility into this issue. I will have to ponder this for a while before I fully understand it. I'll try your workaround but my recollection is that when I wrote this originally, modules would not work for me. Your way might tho.