From: "byroot (Jean Boussier) via ruby-core" Date: 2023-05-20T18:10:42+00:00 Subject: [ruby-core:113564] [Ruby master Bug#19681] The final classpath of partially named modules is sometimes inconsistent once permanently named Issue #19681 has been updated by byroot (Jean Boussier). So to clarify my thinking, my own mental model, or interpretation of Ruby's intent, is that whichever constant you assign a Module/Class to become it's name, and that's final and permanent. ```ruby C = Class.new D = C Object.remove_const(:C) D.name # => "C" ``` That is why, by this logic, when you do: ```ruby m = Module.new m::C = Class.new ``` That class's name become `C`, and the fact that it was assigned to an anonymous module have no impact. I think it's even more obvious with the syntax used by the original repro: `class m::C; end`. Note that here I mean `name` (`C`), not `classpath` (`A::B::C`). A module may have a permanent name before it has a permanent classpath. That's why I implemented the patch the way I did. I totally understand other may have different mental model though. And looking at how it was implemented historically, it's not entirely clear this behavior was fully intended, given that if I read the code right the `classpath` field in `rb_classext_t` used to be a lazily set cache, and it's not 100% certain not clearing it on `remove_const` was a feature or an oversight. What is certain to me though, is that the current behavior rely on the name hash, hence is "semi-random", and is really undesirable. I think at this stage we'll have to put this at the dev meeting agenda to get Matz opinion. ---------------------------------------- Bug #19681: The final classpath of partially named modules is sometimes inconsistent once permanently named https://bugs.ruby-lang.org/issues/19681#change-103197 * Author: byroot (Jean Boussier) * Status: Open * Priority: Normal * Backport: 3.0: WONTFIX, 3.1: REQUIRED, 3.2: REQUIRED ---------------------------------------- Reported to me by @fxn ```ruby m = Module.new class m::C; end p m::C.name # => "#::C" m::D = m::C p m::D.name # => "#::C" M = m p M::C.name # => "M::D" ``` Expected behavior: ```ruby p M::C.name # => "M::C" ``` ### Reason When the parent is assigned its permanent classpath, we iterate over its `const_table` to recursively give a permanent name to all the constant it owns. However, `const_table` is an `id_table` so it doesn't retain the insertion order, which means that if the constant was aliased, we can no longer distinguish between the original name and its aliases, and whichever comes first in the `const_table` will be used as the permanent name. ### Potential solution I have a tentative fix for it in https://github.com/ruby/ruby/pull/7829. Instead of relying on the `const_table` key, it extract the original name from the temporary classpath. It does feel a bit wrong to do a string search in such a place, but it does work. -- https://bugs.ruby-lang.org/ ______________________________________________ ruby-core mailing list -- ruby-core@ml.ruby-lang.org To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/