Bug #14895
closedInconsistent constant names when using const_set on a singleton class
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) over 6 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) over 6 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) over 6 years ago
silver_phoenix (Pedro Pinto) wrote:
Yes, I understand that, but why isn't
Z
named likeB
is?
Z
is#<Module:...>
(unnamed)
B
is#<...>::B
(named)
Because they are not "fully-named" yet.
Updated by silver_phoenix (Pedro Pinto) over 6 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) over 6 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) about 4 years 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
andc
should be treated identically. The right behavior would be for theirname
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) about 4 years 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) about 4 years 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).
Updated by sawa (Tsuyoshi Sawada) about 4 years ago
- Description updated (diff)
Fixed dangling participle.
Updated by Anonymous about 4 years 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]