From: "fxn (Xavier Noria)" Date: 2022-03-13T12:34:54+00:00 Subject: [ruby-core:107879] [Ruby master Bug#18622] const_get still looks in Object, while lexical constant lookup no longer does Issue #18622 has been updated by fxn (Xavier Noria). > Actually I'd argue it's in the nesting (just not shown by Module.nesting), the outermost (implicit) "root" lexical constant scope is always Object (and in fact it's implemented that way in TruffleRuby, maybe in CRuby too). I believe that is confounding the contract with your implementation. From the POV of the programmer, `Module.nesting` tells you the nesting, `Object` is not in the nesting. Relative constant lookup checks first the nesting, _if not found there_, then you check the ancestors. If not found there, and the cref is a module, you additionally check `Object`. This is consistent with what Flanagan & Matz say on page 261: > Ruby first attempts to resolve a constant in the lexical scope f the reference. This means that it first checks the class or module that encloses the constant reference to see if that class or module defines the constant. If not, it checks the next enclosing class or module. This continues until there are no more classes or modules. Note that top-level or "global" constants are not considered part of the lexical scope and are not considered during this part of constant lookup. The class method Module.nesting returns the list of classes and modules that are searched in this step, in the order they are searched. > If no constant definition is found in the lexically closing scope, Ruby next tries to resolve the constant in the inheritance hierarchy by checking the ancestors of the class or module that referred to the constant. > If no constant definition is found in the inheritance hierarchy, then top-level constant definitions are checked. Now, I don't know how TruffleRuby _implements_ lookup, but I am pretty certain the `Object` does not belong to the nesting, and that the public contract, conceptually is that one. > And A.const_get(:B) is not considered like A::B but like module A; B; end? This is what I am trying to say in my previous comments. And that is why looking at `Object` is not inconsistent. What happens in `A::B` is not relevant for `const_get`. > This issue is mostly about do we still need to look in Object, in which cases, and could there be simpler overall rules for the various constant lookups? You look at `Object` by hand, because people expect `String` to be available everywhere (except in `BasicObject`, which is an edge case). So, whether the ancestors have `Object` or not is a technicality, from the POV of the user, you want `module M; String; end` to work. And that means you need to introduce a special rule for modules. Personally, I think the lookup is pretty intuitive and easy to understand once documented. ---------------------------------------- Bug #18622: const_get still looks in Object, while lexical constant lookup no longer does https://bugs.ruby-lang.org/issues/18622#change-96821 * Author: Eregon (Benoit Daloze) * Status: Open * Priority: Normal * ruby -v: ruby 3.0.2p107 (2021-07-07 revision 0db68f0233) [x86_64-linux] * Backport: 2.6: UNKNOWN, 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN ---------------------------------------- There is some inconsistency here between literal constant lookup and the meta API (const_get). Lexical constant lookup no longer uses a special case for Object, and this is good as it avoids surprises: #11547 However, `const_get` still looks in Object, even though that's confusing, inconsistent and IMHO shouldn't really happen. ```ruby module ConstantSpecsTwo Foo = :cs_two_foo end module ConstantSpecs end p ConstantSpecs.const_get("ConstantSpecsTwo::Foo") # => :cs_two_foo p ConstantSpecs::ConstantSpecsTwo::Foo # => const_get.rb:9:in `
': uninitialized constant ConstantSpecs::ConstantSpecsTwo (NameError) ``` I think we should change it so both behave the same (i.e., NameError). It's like if `cd /foo/bar` would go to `/bar` if `/foo/bar` does not exist and `/bar` does. `const_get` is a meta API so it cannot know the surrounding `Module.nesting`, but so I think it should consider the receiver of `const_get` as the only nesting (so just `ConstantSpecs` in this case, not `Object`). Note this does not affect nested constants inside the `const_get` like `Foo` above (another way to look at the inconsistency that the first component is treated differently). From https://bugs.ruby-lang.org/issues/11547#note-19 -- https://bugs.ruby-lang.org/ Unsubscribe: