Bugs: Browse | Submit New | Admin

[#16493] Bug involving arrays of dynamically generated modules

Date:
2007-12-21 15:52
Priority:
3
Submitted By:
Sander Land (sander)
Assigned To:
Yukihiro Matsumoto (matz)
Category:
Language / Runtime / Core Libraries
State:
Open
Platform:
 
Summary:
Bug involving arrays of dynamically generated modules

Detailed description
When using an array of dynamically generated modules, including one of these modules from the array does not work
properly.
The super-module of these included modules are not noticed (or possibly it is considered its own super-module), leading
to an infinite recursion.

A workaround is to avoid putting the modules in arrays. Also single-element arrays do not have this problem.
Ruby 1.8.6 also does not have this bug.

Simplest code I could make to reproduce this bug:

def wrap(m)
  Module.new{
    include m
    def foo
      super * 3
    end
  }
end

module A
  def foo
    "Hello!"
  end
end

# stack level too deep (SystemStackError)  in 1.9
$mod = [A,A]
$mod.map{|mod| wrap(mod) }.each{|m|
  p Class.new{ include m }.new.foo 
}


# NO error in 1.9 when removing the map
$mod.each{|mod| 
  m = wrap(mod)
  p Class.new{ include m }.new.foo
}

# Also an error on 'manual map'
$mod = [wrap(A),wrap(A)]
$mod.each{|m|
  p Class.new{ include m }.new.foo
}

# But no error on mapping single-element arrays
$mod = [A]
$mod.map{|mod| wrap(mod) }.each{|m|
  p Class.new{ include m }.new.foo
}

== INFO
Very recent ruby 1.9 version, straight from the SVN. Compiled with GCC 4.1 with no special CFLAGS or anything.

$ ruby1.9 -v
ruby 1.9.0 (2007-12-21 patchlevel 0) [i686-linux]











Add A Comment: Notepad

Please login


Followup

Message
Date: 2008-11-09 04:25
Sender: Roger Pack

thank you
-=R
Date: 2008-11-08 21:29
Sender: Sander Land

Submitted to the new tracker as requested:
http://redmine.ruby-lang.org/issues/show/730
Date: 2008-11-07 23:57
Sender: Roger Pack

looks like a bug.
Could you submit a new copy to the new tracker:
http://redmine.ruby-lang.org/
--this tracker is being shelved.
Thanks!
-=R
Date: 2007-12-29 14:29
Sender: Sander Land

Last comment can be found here in a more readable form:
http://pastie.caboo.se/132996
Date: 2007-12-29 14:24
Sender: Sander Land

Another follow up:

I tried going to the C code, and traced down where the
superclass is looked up.
I think the output I got may help to trace down the bug.

Standard case:

def genmod
  Module.new{
    def foo
      super 
    end
  }
end

mod = [genmod,genmod]
mod.each{|m|
  klass = Class.new{ include m }
  p mod, m, klass.ancestors
  x = klass.new
  p x.foo
}


Ruby Output:
[#<Module:0xb7c1c0c0>, #<Module:0xb7c1c034>]
#<Module:0xb7c1c0c0>
[#<Class:0xb7c1bf94>, #<Module:0xb7c1c0c0>, Object,
Kernel,
BasicObject]

C output: (obtained by just putting printf's in the C code
and recompiling)

[IN vm_search_normal_superclass:
klass=#<Module:0xb7c1c034>(b7c1c034)      -- wrong! should
be #<Module:0xb7c1c0c0>
 	.. BUILTIN_TYPE(klass) == T_MODULE 
  	 .. k=#<Class:0xb7c1bf94>(b7c1bf94) 	
RBASIC(k)->klass=Class(b7c1bf80)  
  	 .. k=Object(b7c1bf44) 	 		
RBASIC(k)->klass=#<Module:0xb7c1c0c0>(b7c1c0c0)   --
here is
the module, but it fails to get noticed
  	 .. k=Object(b7c629d0) 		 	
RBASIC(k)->klass=Class(b7c62930)  
  	 .. k=BasicObject(b7c62624) 		
RBASIC(k)->klass=Kernel(b7c62638)  
  	 .. k=BasicObject(b7c62a0c) 	 	
RBASIC(k)->klass=Class(b7c62958)  
  ...returns klass=#<Module:0xb7c1c034>(b7c1c034)]
[IN vm_search_superclass: ip->klass=b7c1c034 ret
klass=b7c1c034]  -- ip->klass wrong!

The klass passed from vm_search_superclass to
vm_search_normal_superclass is in fact the 
second element in the array, whereas the actual module
included is the first element.
This causes the 'super' lookup loop in 
vm_search_normal_superclass to fail, 
and the return value on failure is the argument itself.

The same happens for
mod = genmod
devnull = genmod
klass = Class.new{ include mod }
p [mod,devnull], klass.ancestors
p klass.new.foo

So this is in fact not a bug that has anything to do with
arrays.

There is probably a bug somewhere in the code that sets
ip->klass, but I can't even find where this is done.

=================================================================
===========================
If I instead replace the genmod function with

def genmod
  Module.new{
    define_method(:foo) {
      super()
    }
  }
end

It fails even when the function generating the module is not
called twice:

mod = genmod
klass = Class.new{ include mod }
p mod, klass.ancestors
p klass.new.foo


Ruby Output:
#<Module:0xb7bf9020>
[#<Class:0xb7bf8f44>, #<Module:0xb7bf9020>, Object,
Kernel,
BasicObject]

C output:

[IN vm_search_superclass (ip != ip->local_iseq):
lcfp->method_class=b7bf8f44]
[IN vm_search_normal_superclass:
klass=#<Class:0xb7bf8f44>(b7bf8f44) 
 	.. BUILTIN_TYPE(klass) == T_CLASS  	
RCLASS_SUPER(klass)=Object(b7bf8ee0)
  ...returns klass=Object(b7bf8ee0)]
[IN vm_search_superclass (ip != ip->local_iseq):
lcfp->method_class=b7bf8ee0]
[IN vm_search_normal_superclass: klass=Object(b7bf8ee0) 
 ...returns klass=Object(b7bf8ee0)]

I.e. it fails to even note the 'foo' method is in a module.
This may be caused by the same bug.

As always, I am running the latest version from the svn
(ruby 1.9.0 (2007-12-29 revision 0) [i686-linux])
Date: 2007-12-25 17:24
Sender: Sander Land

Small simplification: function arguments are not needed for
this bug to appear. The single most important factor seems
to be the fact that the array contains at least two elements
which are calls to the same function.

def genmod
  Module.new{
    def foo
      #  p caller  # shows recursion on next line
      super 
    end
  }
end

mod = [genmod,genmod]
mod.each{|m|
  p Class.new{ include m }.new.foo
}

------ results in 1.9  (2007-12-26 revision 0) -----
bug3.rb:5:in `foo': stack level too deep (SystemStackError)
        from bug3.rb:5:in `foo'
        from bug3.rb:5:in `foo'
        etc.
Date: 2007-12-24 14:04
Sender: Sander Land

I tried to simplify my test case some more, and basically
failed. There is some very strange behavior going on here.

I did however, notice some patterns in my test cases.
See the attachment bug2.rb for some of these.

Most importantly, the following case gives an infinite
recursion / SystemStackError in 1.9, and not "no superclass
method `foo'" as it should (and does, in Ruby 1.8).

mod = [wrap(Module.new),wrap(Module.new)]
mod.each{|m|
  p Class.new{ include m }.new.foo
}


I think this might be the bug:
When there:
- is an array
- with 2 or more modules which are results function calls to
the same function (but not necessarily with the same
parameters) and who contain a call to "super".
Then when any of these modules is included, it is considered
its own ancestor, resulting in infinite recursion on the
call to the function which contains the call to super.
Date: 2007-12-24 13:37
Sender: Shyouhei Urabe

Assigning to Matz; 1.9

Attached Files:

Name Description Download
bug19.rb code from the description Download
bug2.rb extra test cases Download

Changes:

Field Old Value Date By
File Added2999: bug2.rb2007-12-24 14:04sander
assigned_toshyouhei2007-12-24 13:37shyouhei
File Added2984: bug19.rb2007-12-21 15:52sander