Feature #2740

Extend const_missing to pass in the nesting

Added by Yehuda Katz about 4 years ago. Updated over 1 year ago.

[ruby-core:28154]
Status:Assigned
Priority:Normal
Assignee:Yukihiro Matsumoto
Category:core
Target version:next minor

Description

=begin
At the moment, it is impossible for const_missing to differentiate between these cases:

 class Foo::Bar
   Baz
 end

 class Foo
   class Bar
     Baz
   end
 end

In Rails, we implement a class loading system that loads classes from the file system if they are not found. In the above case, Foo::Baz might be stored in app/models/foo/baz.rb. We would like to be able to use the same Ruby constant lookup logic when looking up classes from the file system.

 class Foo::Bar
   Baz
 end

Here, we should look in "app/models/foo/bar/baz.rb" and in "app/models/baz.rb" just as Ruby would search for Foo::Bar::Baz and then Object::Baz.

 class Foo
   class Bar
     Baz
   end
 end

Here, we should look in "app/models/foo/bar/baz.rb", then "app/models/foo/baz.rb", and finally "app/models/baz.rb" just as Ruby would search for Foo::Bar::Baz, then Foo::Baz, and then Object::Baz.

In order to achieve this, I propose that we extend the const_missing method to take an optional second parameter containing the nesting:

 class Foo
   class Bar
     def self.const_missing(id, nesting)
       id == :Baz
       nesting == [Foo::Bar] # first case
       nesting == [Foo::Bar, Foo] # second case
     end
   end
 end

This would allow people who wish to do their own custom constant loading (such as Rails) to do so in a way that is consistent with Ruby's own constant lookup. In order to avoid backward-compatibility issues, we can check the arity of the const_missing method, and only pass in the nesting if a second parameter was provided.
=end

const_missing_nesting.diff Magnifier - Patch that provides the nesting to const_missing if arity != 1 (621 Bytes) Yehuda Katz, 02/13/2010 08:53 AM

const_missing_murphy.patch Magnifier - another patch for NoConstantError behavior (2.56 KB) Kornelius Kalnbach, 02/16/2010 11:53 AM

History

#1 Updated by Yui NARUSE about 4 years ago

  • Status changed from Open to Assigned
  • Assignee set to Yukihiro Matsumoto
  • Priority changed from Urgent to Normal

=begin

=end

#2 Updated by Kornelius Kalnbach about 4 years ago

=begin
Interesting idea. However, I see two problems with the proposal:

  1. It changes the const_missing API, breaking code. It would be nice to
    find a way around this.

  2. The feature Module.nesting is dependent on the "point of the call".
    As you described, constmissing is not. I'm not sure about the
    implications; Module.nesting is defined in eval.c, while
    const
    missing and const_get are defined in variable.c.

    There may be another way around this. methodmissing makes it possible to use Ruby's inheritance chain again by calling super. So, what if we just follow the constant lookup nesting chain again when constmissing didn't return anything?

    You wouldn't even have to write the logic yourself. The implementation could look something like this:

    class Module
    # classic-style constmissing
    def const
    missing(id)
    namespace = "#{name}::" unless self == Object
    puts "Searching for #{namespace}#{id}..."
    nil
    end
    end

    class Module
    # new-style constant lookup, given nesting
    # This would have to be implemented in C.
    def constget2 id, nesting
    nesting += [Object] # Don't forget top level.
    # As usual: Try to find existing constant (without const
    missing).
    for mod in nesting
    # imitating constant lookup without constmissing
    if mod.constants.map { |c| c.to
    sym }.include? id.tosym
    return mod.const
    get(id)
    end
    end
    # New style: Call constmissing on each nesting level.
    for mod in nesting
    value = mod.const
    missing id
    return value if value
    end
    # As usual: Raise NameError if nothing was found.
    raise NameError, "uninitialized constant #{name}::#{id}"
    end
    end

    class Foo
    class Bar
    const_get2(:Baz, Module.nesting)
    # >> Searching for Foo::Bar::Baz...
    # >> Searching for Foo::Baz...
    # >> Searching for Baz...
    #~> uninitialized constant Foo::Bar::Baz (NameError)
    end
    end

    class Foo::Bar
    const_get2(:Baz, Module.nesting)
    # >> Searching for Foo::Bar::Baz...
    # >> Searching for Baz...
    #~> uninitialized constant Foo::Bar::Baz (NameError)
    end

    This solves 1., but not 2. Calling const_get and would still depend on Module.nesting.

    Also, a constant with a value of nil or false would be interpreted as "not found". Maybe we could define a protocol for this, something like StopIteration?

    NoConstantError = Class.new NameError

    class Module
    def const_missing(id)
    ...
    raise NoConstantError
    end
    end

    class Module
    def constget2 id, nesting
    ...
    for mod in nesting
    begin
    return mod.const
    missing(id)
    rescue NoConstantError
    # continue
    end
    end
    ...
    end
    end

    On the other hand, maybe implicit constant lookup through the module nesting and automatic loading is a bit too much ;-)
    =end

#3 Updated by Yehuda Katz about 4 years ago

=begin

Interesting idea. However, I see two problems with the proposal:

  1. It changes the const_missing API, breaking code. It would be nice to find a way around this.

A simple arity check will get around this issue.

  1. The feature Module.nesting is dependent on the "point of the call". As you described, constmissing is not. I'm not sure about the implications; Module.nesting is defined in eval.c, while constmissing and const_get are defined in variable.c.

Module.nesting is essentially global (stored in a thread-local), so we can get it in constmissing via rbfuncall. If we made rbmodnesting not static, we could also access it via rbmodnesting.

I have attached a patch that makes nesting available in constmissing if the constmissing method has an arity other than 1.
=end

#4 Updated by Nobuyoshi Nakada about 4 years ago

=begin
Hi,

At Fri, 12 Feb 2010 11:15:14 +0900,
Yehuda Katz wrote in :

class Foo::Bar
  Baz
end

Here, we should look in "app/models/foo/bar/baz.rb" and in
"app/models/baz.rb" just as Ruby would search for
Foo::Bar::Baz and then Object::Baz.

class Foo
  class Bar
    Baz
  end
end

Here, we should look in "app/models/foo/bar/baz.rb", then
"app/models/foo/baz.rb", and finally "app/models/baz.rb" just
as Ruby would search for Foo::Bar::Baz, then Foo::Baz, and
then Object::Baz.

I don't think you can distinguish the latter from the following.

 class Foo
   class Bar
     Foo::Bar::Baz
   end
 end

Is it OK?

--
Nobu Nakada

=end

#5 Updated by Yusuke Endoh about 4 years ago

=begin
Hi,

2010/2/13 Yehuda Katz redmine@ruby-lang.org:

  1. It changes the const_missing API, breaking code. It would be nice to find a way around this.

A simple arity check will get around this issue.

It's ugly. The core should avoid such a dirty hack when possible.
I also think it would be nice to find another clean API.

I have attached a patch that makes nesting available in constmissing if the constmissing method has an arity other than 1.

It causes SEGV.

$ ./miniruby -e '
class C
def self.constmissing(id, nesting)
p id, nesting
end
end
C.enum
for(:const_get, :D).next
'
[BUG] Segmentation fault
ruby 1.9.2dev (2010-02-13 trunk 26659) [i686-linux]

-- control frame ----------
c:0005 p:---- s:0009 b:0009 l:000008 d:000008 CFUNC :nesting
c:0004 p:---- s:0007 b:0007 l:000006 d:000006 CFUNC :const_get
c:0003 p:---- s:0005 b:0005 l:000004 d:000004 CFUNC :each
c:0002 p:---- s:0003 b:0003 l:002168 d:000002 IFUNC
c:0001 p:---- s:0001 b:-001 l:000000 d:000000 ------


-- Ruby level backtrace information ----------------------------------------
-e:0:in each'
-e:0:in
const_get'
-e:0:in `nesting'

-- C level backtrace information -------------------------------------------
./miniruby(rbvmbugreport+0xbd) [0x81a5c6d]
./miniruby [0x808e69e]
./miniruby(rbbug+0x28) [0x808e758]
./miniruby [0x813a6b0]
[0xffffe40c]
./miniruby(rb
vmcref+0x38) [0x8193d88]
./miniruby [0x8090dc4]
./miniruby [0x819d889]
./miniruby(rb
funcall+0x92) [0x819e142]
./miniruby [0x817d571]
./miniruby [0x80db78c]
./miniruby [0x8191bfd]
./miniruby [0x819d889]
./miniruby [0x81a1c2e]
./miniruby(rbiterate+0xac) [0x8191dac]
./miniruby(rb
blockcall+0x3f) [0x8191f3f]
./miniruby [0x808c0d9]
./miniruby [0x819d889]
./miniruby [0x81a1c2e]
./miniruby(rb
iterate+0xac) [0x8191dac]
./miniruby(rbblockcall+0x3f) [0x8191f3f]
./miniruby [0x808c162]
./miniruby [0x8195d5b]
./miniruby(rbvminvokeproc+0x76) [0x819c816]
./miniruby(rb
fiberstart+0x1d2) [0x81aed52]
./miniruby [0x8091479]
./miniruby(ruby
runnode+0x32) [0x8092c92]
./miniruby(main+0x5a) [0x805a8da]
/lib/i686/cmov/libc.so.6(
libcstart_main+0xe5) [0xb7d89455]
./miniruby [0x805a7e1]

[NOTE]
You may have encountered a bug in the Ruby interpreter or extension libraries.
Bug reports are welcome.
For details: http://www.ruby-lang.org/bugreport.html

Aborted

--
Yusuke ENDOH mame@tsg.ne.jp

=end

#6 Updated by Yehuda Katz about 4 years ago

=begin
Upon further reflection, I really like the idea of raising an exception like NoConstantError from const_missing to tell Ruby to keep searching up the nesting chain.

 class Module
   def const_missing(name)
     puts "#{self} is missing #{name}"
     raise NoConstantError unless self == Object
   end
 end

 module Foo
   module Bar
     Baz
   end
 end

 # Output:
 # Foo::Bar is missing Baz
 # Foo is missing Baz
 # Object is missing Baz

 module Foo::Bar
   Baz
 end

 # Output
 # Foo::Bar is missing Baz
 # Object is missing Baz

 module Foo::Bar
   Foo::Bar::Baz
 end

 # Output
 # Foo::Bar is missing Baz

 module Foo
   Bar::Baz
 end

 # Output
 # Foo::Bar is missing Baz

Thoughts?
=end

#7 Updated by Nobuyoshi Nakada about 4 years ago

=begin
Hi,

At Mon, 15 Feb 2010 22:21:55 +0900,
Yehuda Katz wrote in :

Upon further reflection, I really like the idea of raising an
exception like NoConstantError from const_missing to tell
Ruby to keep searching up the nesting chain.

Maybe reasonable.

Index: error.c
===================================================================
--- error.c (revision 26674)
+++ error.c (working copy)
@@ -402,4 +402,6 @@ VALUE rbeSyntaxError;
VALUE rb
eLoadError;

+VALUE rbeNoConstantError;
+
VALUE rb
eSystemCallError;
VALUE rbmErrno;
@@ -1143,4 +1145,5 @@ Init
Exception(void)
rbdefinemethod(rbeNoMethodError, "initialize", nometherrinitialize, -1);
rb
definemethod(rbeNoMethodError, "args", nometherrargs, 0);
+ rbeNoConstantError = rbdefineclass("NoConstantError", rbeNameError);

  rb_eRuntimeError = rb_define_class("RuntimeError", rb_eStandardError);

Index: variable.c
===================================================================
--- variable.c (revision 26674)
+++ variable.c (working copy)
@@ -1356,7 +1356,29 @@ uninitialized_constant(VALUE klass, ID i

static VALUE
+constmissingcall(VALUE arg)
+{
+ VALUE *args = (VALUE *)arg;
+ ID constmissingid;
+ CONSTID(constmissingid, "constmissing");
+ return rbcheckfuncall(args[0], constmissingid, 1, &args[1]);
+}
+
+static VALUE
+constmissingrescue(VALUE arg, VALUE errinfo)
+{
+ return arg;
+}
+
+extern VALUE rbeNoConstantError;
+
+static VALUE
const
missing(VALUE klass, ID id)
{
- return rbfuncall(klass, rbintern("constmissing"), 1, ID2SYM(id));
+ VALUE args[2];
+ args[0] = klass;
+ args[1] = ID2SYM(id);
+ return rb
rescue2(constmissingcall, (VALUE)args,
+ constmissingrescue, (VALUE)Qundef,
+ rb_eNoConstantError, (VALUE)0);
}

@@ -1598,5 +1620,21 @@ rbconstget_0(VALUE klass, ID id, int e
}

  • value = const_missing(klass, id);
  • if ((value = const_missing(tmp, id)) == Qundef) {
  • NODE *rbvmcref(void);
  • NODE *cref = rbvmcref();
  • while (cref && cref->nd_next &&
  • ((cref->flags & NODEFLCREFPUSHEDBY_EVAL) ||
  • NILP(tmp = cref->ndclss) ||
  • (value = const_missing(tmp, id)) == Qundef)) {
  • cref = cref->nd_next;
  • }
  • if (value == Qundef) {
  • if (!exclude && BUILTINTYPE(klass) == TMODULE &&
  • (value = constmissing(rbcObject, id)) == Qundef) {
  • uninitialized_constant(klass, id);
  • }
  • }
  • } + rbvmincconstmissing_count(); return value; -- Nobu Nakada

=end

#8 Updated by Kornelius Kalnbach about 4 years ago

=begin
Nobu, great to see a patch for this! Thank you.

However, it didn't produce the desired results for me. After some debugging, I got a version that works for me:

class Object
def self.const_missing(id)
namespace = "#{name}::" unless self == Object
puts "Searching for #{namespace}#{id}..."
raise NoConstantError
end
end

class Foo
class Bar
p Module.nesting
Baz rescue nil
# [Foo::Bar, Foo]
# Searching for Foo::Bar::Baz...
# Searching for Foo::Baz...
# Searching for Baz...
end
end

class Foo::Bar
p Module.nesting
Baz rescue nil
# [Foo::Bar]
# Searching for Foo::Bar::Baz...
# Searching for Baz...
end

It also passes "make test".

Yehuda: Your example works as you described with this patch.

Index: error.c
===================================================================
--- error.c (revision 26674)
+++ error.c (working copy)
@@ -402,4 +402,6 @@ VALUE rbeSyntaxError;
VALUE rb
eLoadError;

+VALUE rbeNoConstantError;
+
VALUE rb
eSystemCallError;
VALUE rbmErrno;
@@ -1143,4 +1145,5 @@ Init
Exception(void)
rbdefinemethod(rbeNoMethodError, "initialize", nometherrinitialize, -1);
rb
definemethod(rbeNoMethodError, "args", nometherrargs, 0);
+ rbeNoConstantError = rbdefineclass("NoConstantError", rbeNameError);

  rb_eRuntimeError = rb_define_class("RuntimeError", rb_eStandardError);

Index: variable.c
===================================================================
--- variable.c (revision 26674)
+++ variable.c (working copy)
@@ -1355,9 +1355,31 @@
}

static VALUE
+constmissingcall(VALUE arg)
+{
+ VALUE *args = (VALUE *)arg;
+ ID constmissingid;
+ CONSTID(constmissingid, "constmissing");
+ return rbcheckfuncall(args[0], constmissingid, 1, &args[1]);
+}
+
+static VALUE
+constmissingrescue(VALUE arg, VALUE errinfo)
+{
+ return arg;
+}
+
+extern VALUE rbeNoConstantError;
+
+static VALUE
const
missing(VALUE klass, ID id)
{
- return rbfuncall(klass, rbintern("constmissing"), 1, ID2SYM(id));
+ VALUE args[2];
+ args[0] = klass;
+ args[1] = ID2SYM(id);
+ return rb
rescue2(constmissingcall, (VALUE)args,
+ constmissingrescue, (VALUE)Qundef,
+ rb_eNoConstantError, (VALUE)0);
}

@@ -1392,8 +1414,7 @@
VALUE
rbmodconstmissing(VALUE klass, VALUE name)
{
- rb
framepop(); /* pop frame for "constmissing" /
- uninitializedconstant(klass, rbtoid(name));
+ rb
raise(rb_eNoConstantError, "uninitialized constant");
return Qnil; /
not reached */
}

@@ -1596,8 +1617,26 @@
tmp = rbcObject;
goto retry;
}
+

+ tmp = klass;
+ if ((value = const
missing(tmp, id)) == Qundef) {
+ NODE *rbvmcref(void);
+ NODE *cref = rbvmcref();
+ cref = cref->ndnext;
+ while (cref && cref->nd
next &&
+ ((cref->flags & NODEFLCREFPUSHEDBYEVAL) ||
+ NIL
P(tmp = cref->ndclss) ||
+ (value = const
missing(tmp, id)) == Qundef)) {
+ cref = cref->ndnext;
+ }
+ if (value == Qundef) {
+ if (!exclude && (BUILTIN
TYPE(klass) == TMODULE || BUILTINTYPE(klass) == TCLASS) &&
+ (value = const
missing(rbcObject, id)) == Qundef) {
+ uninitialized
constant(klass, id);
+ }
+ }
+ }

  • value = constmissing(klass, id); rbvmincconstmissingcount(); return value; }

=end

#9 Updated by Yusuke Endoh about 4 years ago

=begin
Hi,

2010/2/15 Yehuda Katz redmine@ruby-lang.org:

Upon further reflection, I really like the idea of raising an exception like NoConstantError from const_missing to tell Ruby to keep searching up the nesting chain.

Cool! I agree with Yehuda.

Additional idea: NoConstantError may be raised when referring
uninitialized constant, to make it consistent with NoMethodError.

--
Yusuke ENDOH mame@tsg.ne.jp

=end

#10 Updated by Yusuke Endoh about 4 years ago

=begin
Hi,

Matz, do you accept Yehuda's suggestion?

Current status:

  • Three persons (nobu, murphy and I) have approved.
  • There is no objection.
  • There is a patch written by nobu and murphy, though it has a
    little bug. I revised a patch below.

    2010/2/15 Yehuda Katz redmine@ruby-lang.org:

    class Module
    def const_missing(name)
    puts "#{self} is missing #{name}"
    raise NoConstantError unless self == Object
    end
    end

    *snip

    module Foo::Bar
    Foo::Bar::Baz
    end

    # Output
    # Foo::Bar is missing Baz

    I'd like to make sure one thing; the above code should raise
    NoConstantError after the output, ok? This is because scoped
    constant reference does not check bindings of Object class.

    Instead of raising an error, nobu's patch leaks Qundef for the
    above code, which causes SEGV in RubySpec. It is clearly a
    problem.

    diff --git a/error.c b/error.c
    index 3fcd184..d0373fd 100644
    --- a/error.c
    +++ b/error.c
    @@ -401,6 +401,8 @@ VALUE rbeScriptError;
    VALUE rb
    eSyntaxError;
    VALUE rb_eLoadError;

    +VALUE rbeNoConstantError;
    +
    VALUE rb
    eSystemCallError;
    VALUE rbmErrno;
    static VALUE rb
    eNOERROR;
    @@ -701,18 +703,34 @@ exitsuccessp(VALUE exc)
    return Qfalse;
    }

    +static VALUE
    +newnameerror(VALUE klass, ID id, const char *fmt, va_list args)
    +{

  • VALUE argv[2];

  • argv[0] = rb_vsprintf(fmt, args);

  • argv[1] = ID2SYM(id);

  • return rbclassnewinstance(2, argv, klass);
    +}
    +
    void
    rb
    name_error(ID id, const char *fmt, ...)
    {

  • VALUE exc, argv[2];

  • VALUE exc;

    va_list args;

    va_start(args, fmt);

  • argv[0] = rb_vsprintf(fmt, args);

  • exc = newnameerror(rbeNameError, id, fmt, args);
    va
    end(args);

  • rbexcraise(exc);
    +}

  • argv[1] = ID2SYM(id);

  • exc = rbclassnewinstance(2, argv, rbeNameError);
    +void
    +rbnameerrorwithclass(VALUE klass, ID id, const char *fmt, ...)
    +{

  • VALUE exc;

  • va_list args;

  • va_start(args, fmt);

  • exc = newnameerror(klass, id, fmt, args);

  • vaend(args);
    rb
    exc_raise(exc);
    }

    @@ -1142,6 +1160,7 @@ InitException(void)
    rb
    eNoMethodError = rbdefineclass("NoMethodError", rbeNameError);
    rb
    definemethod(rbeNoMethodError, "initialize", nometherrinitialize, -1);
    rbdefinemethod(rbeNoMethodError, "args", nometherr_args, 0);

  • rbeNoConstantError = rbdefineclass("NoConstantError", rbeNameError);

    rbeRuntimeError = rbdefineclass("RuntimeError", rbeStandardError);
    rbeSecurityError = rbdefineclass("SecurityError", rbeException);
    diff --git a/test/rake/testtoplevelfunctions.rb b/test/rake/testtoplevelfunctions.rb
    index 12a8cd1..7315f88 100644
    --- a/test/rake/testtoplevelfunctions.rb
    +++ b/test/rake/test
    toplevelfunctions.rb
    @@ -86,6 +86,6 @@ class Rake::TestTopLevelFunctions < Test::Unit::TestCase
    end

    def testmissingother_constant

  • assertraise(NameError) do Object.constmissing(:Xyz) end

  • assertraise(NoConstantError) do Object.constmissing(:Xyz) end
    end
    end
    diff --git a/test/ruby/testbasicinstructions.rb b/test/ruby/testbasicinstructions.rb
    index ff14e4a..eb985ef 100644
    --- a/test/ruby/testbasicinstructions.rb
    +++ b/test/ruby/test
    basicinstructions.rb
    @@ -271,25 +271,25 @@ class TestBasicInstructions < Test::Unit::TestCase
    Const::A::B.removeconst :C
    assertequal 'Const::C', Const::C
    assert
    equal 'Const::A::C', Const::A::C

  • assert_raise(NameError) { Const::A::B::C }

  • assert_raise(NoConstantError) { Const::A::B::C }

    Const::A.removeconst :C
    assert_equal 'Const::C', Const::C

  • assert_raise(NameError) { Const::A::C }

  • assert_raise(NameError) { Const::A::B::C }

  • assert_raise(NoConstantError) { Const::A::C }

  • assert_raise(NoConstantError) { Const::A::B::C }

    Const.removeconst :C

  • assert_raise(NameError) { Const::C }

  • assert_raise(NameError) { Const::A::C }

  • assert_raise(NameError) { Const::A::B::C }

  • assert_raise(NoConstantError) { Const::C }

  • assert_raise(NoConstantError) { Const::A::C }

  • assert_raise(NoConstantError) { Const::A::B::C }

    Const::A.const_set :C, 'Const::A::C'

  • assert_raise(NameError) { Const::C }

  • assertraise(NoConstantError) { Const::C }
    assert
    equal 'Const::A::C', Const::A::C

  • assert_raise(NameError) { Const::A::B::C }

  • assert_raise(NoConstantError) { Const::A::B::C }

    Const::A::B.const_set :C, 'Const::A::B::C'

  • assert_raise(NameError) { Const::C }

  • assertraise(NoConstantError) { Const::C }
    assert
    equal 'Const::A::C', Const::A::C
    assert_equal 'Const::A::B::C', Const::A::B::C

    diff --git a/test/ruby/testmodule.rb b/test/ruby/testmodule.rb
    index f905431..fbeb29b 100644
    --- a/test/ruby/testmodule.rb
    +++ b/test/ruby/test
    module.rb
    @@ -451,13 +451,13 @@ class TestModule < Test::Unit::TestCase
    assertequal(:foo, c1::Foo)
    assert
    equal(:foo, c2::Foo)
    assertequal(:foo, c2.constget(:Foo))

  • assertraise(NameError) { c2.constget(:Foo, false) }

  • assertraise(NoConstantError) { c2.constget(:Foo, false) }

    eval("c1::Foo = :foo")

  • assert_raise(NameError) { c1::Bar }

  • assert_raise(NameError) { c2::Bar }

  • assertraise(NameError) { c2.constget(:Bar) }

  • assertraise(NameError) { c2.constget(:Bar, false) }

  • assert_raise(NoConstantError) { c1::Bar }

  • assert_raise(NoConstantError) { c2::Bar }

  • assertraise(NoConstantError) { c2.constget(:Bar) }

  • assertraise(NoConstantError) { c2.constget(:Bar, false) }

    c1.instanceeval do
    def const
    missing(x)
    diff --git a/test/ruby/testobject.rb b/test/ruby/testobject.rb
    index 0a49422..af91f75 100644
    --- a/test/ruby/testobject.rb
    +++ b/test/ruby/test
    object.rb
    @@ -437,7 +437,7 @@ class TestObject < Test::Unit::TestCase
    x = "foo".instanceexec("bar") {|a| self + a }
    assert
    equal("foobar", x)

  • assert_raise(NameError) do

  • assertraise(NoConstantError) do
    InstanceExec.new.instance
    exec { INSTANCEEXEC }
    end
    end
    diff --git a/variable.c b/variable.c
    index d7b99f1..4be8a8e 100644
    --- a/variable.c
    +++ b/variable.c
    @@ -1341,23 +1341,49 @@ rb
    objremoveinstance_variable(VALUE obj, VALUE name)
    return Qnil; /* not reached */
    }

    +extern VALUE rbeNoConstantError;
    +
    +PRINTF
    ARGS(NORETURN(void rbnameerrorwithclass(VALUE, ID, const char*, ...)), 3, 4);
    NORETURN(static void uninitializedconstant(VALUE, ID));
    static void
    uninitialized
    constant(VALUE klass, ID id)
    {

  • if (klass && klass != rb_cObject)

  • rbnameerror(id, "uninitialized constant %s::%s",

  • if (klass && klass != rb_cObject){

  • rbnameerrorwithclass(rb_eNoConstantError,

  •        id, "uninitialized constant %s::%s",
          rb_class2name(klass),
          rb_id2name(id));
    
  • }
    else {

  • rbnameerror(id, "uninitialized constant %s", rb_id2name(id));

  • rbnameerrorwithclass(rb_eNoConstantError,

  •        id, "uninitialized constant %s", rb_id2name(id));
    

    }
    }

    static VALUE
    +constmissingcall(VALUE arg)
    +{

  • VALUE *args = (VALUE *)arg;

  • ID constmissingid;

  • CONSTID(constmissingid, "constmissing");

  • return rbcheckfuncall(args[0], constmissingid, 1, &args[1]);
    +}
    +
    +static VALUE
    +constmissingrescue(VALUE arg, VALUE errinfo)
    +{

  • return arg;
    +}
    +
    +static VALUE
    const_missing(VALUE klass, ID id)
    {

  • return rbfuncall(klass, rbintern("const_missing"), 1, ID2SYM(id));

  • VALUE args[2];

  • args[0] = klass;

  • args[1] = ID2SYM(id);

  • return rbrescue2(constmissing_call, (VALUE)args,

  •        const_missing_rescue, (VALUE)Qundef,
    
  •        rb_eNoConstantError, (VALUE)0);
    

    }

    @@ -1597,7 +1623,27 @@ rbconstget_0(VALUE klass, ID id, int exclude, int recurse)
    goto retry;
    }

  • value = const_missing(klass, id);

  • tmp = klass;

  • if ((value = const_missing(tmp, id)) == Qundef) {

  • NODE *rbvmcref(void);

  • NODE *cref = rbvmcref();

  • cref = cref->nd_next;

  • while (cref && cref->nd_next &&

  •     ((cref->flags & NODE_FL_CREF_PUSHED_BY_EVAL) ||
    
  •  NIL_P(tmp = cref->nd_clss) ||
    
  •  (value = const_missing(tmp, id)) == Qundef)) {
    
  •  cref = cref->nd_next;
    
  • }

  • if (value == Qundef) {

  •  if (!exclude) {
    
  •  value = const_missing(rb_cObject, id);
    
  •  }
    
  •  if (value == Qundef) {
    
  •  uninitialized_constant(klass, id);
    
  •  }
    
  • }

  • }
    +
    rbvmincconstmissingcount();
    return value;
    }
    @@ -1655,7 +1701,7 @@ rb
    constremove(VALUE mod, ID id)
    rb
    nameerror(id, "cannot remove %s::%s",
    rb
    class2name(mod), rb_id2name(id));
    }

  • rbnameerror(id, "constant %s::%s not defined",

  • rbnameerrorwithclass(rbeNoConstantError, id, "constant %s::%s not defined",
    rb
    class2name(mod), rb_id2name(id));
    }

    Yusuke ENDOH mame@tsg.ne.jp
    =end

#11 Updated by Kazuhiro NISHIYAMA about 4 years ago

  • Target version changed from 1.9.2 to 2.0.0

=begin

=end

#12 Updated by Yehuda Katz over 2 years ago

What's the status of this?

#13 Updated by Aaron Patterson about 2 years ago

Bump

#14 Updated by Koichi Sasada over 1 year ago

  • Assignee changed from Yukihiro Matsumoto to Yusuke Endoh

Who's ball?
mame-san, could you give us the response?

#15 Updated by Yusuke Endoh over 1 year ago

  • Assignee changed from Yusuke Endoh to Yukihiro Matsumoto

It looks we need matz's approval.
Sorry I cannot remember the detailed status immediately.

Yusuke Endoh mame@tsg.ne.jp

#16 Updated by Yusuke Endoh over 1 year ago

  • Target version changed from 2.0.0 to next minor

Also available in: Atom PDF