Project

General

Profile

Bug #14895

Inconsistent constant names when using const_set on a singleton class

Added by silver_phoenix (Pedro Pinto) about 2 years ago. Updated 26 days ago.

Status:
Closed
Priority:
Normal
Assignee:
-
Target version:
-
ruby -v:
ruby 2.6.0preview2 (2018-05-31 trunk 63539) [x86_64-linux]
[ruby-core:87787]
Tags:

Description

Constants defined in the namespace of a class or a module are named on the fly:

class X; end
X.const_set(:Y, Module.new) # => X::Y
module M; end
M.const_set(:N, Module.new) # => M::N

Constants defined on a singleton class seem to differ. Modules are named only after a module is created through the module keyword.

s = Object.new.singleton_class
s.const_set(:Z, Module.new) # => #<Module:0x000055886ec59a80>
s.class_eval "module A; self end" # => #<Class:0x000055886ec59d00>::A
s.const_set(:B, Module.new) # => #<Class:0x000055886ec59d00>::B

I would expect module Z to be named. For consistency, if module B is named, shouldn't module Z be named as well?

This also happens in these ruby versions:

  • ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux]
  • ruby 2.5.1p57 (2018-03-29 revision 63029) [i386-mingw32]

Updated by nobu (Nobuyoshi Nakada) about 2 years ago

The names of A, B, and Z are temporary names at the moments of assignment.
Once you assign x.singleton_class to a constant, say C, they appear as C::A, C::B, and C::Z, respectively.

Updated by silver_phoenix (Pedro Pinto) about 2 years ago

Yes, I understand that, but why isn't Z named like B is?

Z is #<Module:...> (unnamed)
B is #<...>::B (named)

There's something not being initialized on the singleton class, which is only triggered after adding a named module explicitly (like what's being done with the A module example).

Updated by nobu (Nobuyoshi Nakada) about 2 years ago

silver_phoenix (Pedro Pinto) wrote:

Yes, I understand that, but why isn't Z named like B is?

Z is #<Module:...> (unnamed)
B is #<...>::B (named)

Because they are not "fully-named" yet.

Updated by silver_phoenix (Pedro Pinto) about 2 years ago

So, what you're saying is that the singleton class is being assigned to a constant when it creates module A, so modules are only named after this step?

Is this expected behavior then?

Updated by marcandre (Marc-Andre Lafortune) about 2 years ago

This looks like a bug to me.

Here's a simplified example:

s = Object.new.singleton_class
a = s.const_set(:A, Module.new)       # => #<Module:0x00007fed619915f0>
b = s.class_eval "module B; self end" # => #<Class:0x00007fed6198a020>::B
c = s.const_set(:C, Module.new)       # => #<Class:0x00007fed6198a020>::C

a and c should be treated identically. The right behavior would be for their name to be #<Class:0x...>::A/C

Note that naming the singleton class afterwards changes all names correctly:

S = s # => #<Class:#<Object:0x00007fed6198a1b0>> 
a   # => S::A 
b   # => S::B 
c   # => S::C 

Updated by jeremyevans0 (Jeremy Evans) 27 days ago

  • Status changed from Open to Closed

marcandre (Marc-Andre Lafortune) wrote in #note-5:

This looks like a bug to me.

Here's a simplified example:

s = Object.new.singleton_class
a = s.const_set(:A, Module.new)       # => #<Module:0x00007fed619915f0>
b = s.class_eval "module B; self end" # => #<Class:0x00007fed6198a020>::B
c = s.const_set(:C, Module.new)       # => #<Class:0x00007fed6198a020>::C

a and c should be treated identically. The right behavior would be for their name to be #<Class:0x...>::A/C

Ruby 2.7 changed the behavior so that a and c are treated identically, but that c is like a:

a # #<Module:0x00000b0bda620368>
b # #<Class:0x00000b0bda6203b8>::B
c # #<Module:0x00000b0bdcdd3c20>

I think that is sufficient for this bug to be considered closed. However, if you would like the singleton classes named, here is a patch for that:

diff --git a/object.c b/object.c
index 08fec850d3..1181b45b3c 100644
--- a/object.c
+++ b/object.c
@@ -2536,6 +2536,11 @@ rb_mod_const_set(VALUE mod, VALUE name, VALUE value)
 {
     ID id = id_for_var(mod, name, const);
     if (!id) id = rb_intern_str(name);
+    if (rb_attr_get(mod, rb_intern("__tmp_classpath__")) == Qnil &&
+           rb_attr_get(mod, rb_intern("__classpath__")) == Qnil) {
+        rb_ivar_set(mod, rb_intern("__tmp_classpath__"),
+            rb_funcall(mod, rb_intern("to_s"), 0));
+    }
     rb_const_set(mod, id, value);

     return value;

Output:

a # #<Class:#<Object:0x0000003003436178>>::A
b # #<Class:#<Object:0x0000003003436178>>::B
c # #<Class:#<Object:0x0000003003436178>>::C

I kind of prefer this as it shows the module is defined under a singleton class. If you like that idea, please submit a feature request for it (or switch this to a feature request and reopen).

Updated by marcandre (Marc-Andre Lafortune) 27 days ago

  • Status changed from Closed to Open

jeremyevans0 (Jeremy Evans) wrote in #note-6:

Output:

a # #<Class:#<Object:0x0000003003436178>>::A
b # #<Class:#<Object:0x0000003003436178>>::B
c # #<Class:#<Object:0x0000003003436178>>::C

I kind of prefer this as it shows the module is defined under a singleton class.

... and it shows the name 'A' and 'C'. That's much better!

If you like that idea, please submit a feature request for it (or switch this to a feature request and reopen).

...or consider the current output a bug (even if now it is consistently buggy 😆)

class << o
  module Bar
  end
end
o.singleton_class::Bar # => #<Class:0x00007f90f3300008>::Bar

So I suggest I'll commit your patch, unless there's an objection.

Updated by jeremyevans0 (Jeremy Evans) 27 days ago

marcandre (Marc-Andre Lafortune) wrote in #note-7:

So I suggest I'll commit your patch, unless there's an objection.

I have no objection, so if you consider the current behavior a bug, please commit the patch (assuming it doesn't cause test or spec failures).

#9

Updated by sawa (Tsuyoshi Sawada) 27 days ago

  • Description updated (diff)

Fixed dangling participle.

#10

Updated by Anonymous 26 days ago

  • Status changed from Open to Closed

Applied in changeset git|5e16857315bf55307c5fc887ca6f03bfa0630a93.


Fix constant names set using const_set on a singleton class

Fixes [Bug #14895]

Also available in: Atom PDF