From: "Eregon (Benoit Daloze) via ruby-core" Date: 2025-02-14T13:30:21+00:00 Subject: [ruby-core:121046] [Ruby master Misc#21035] Clarify or redefine Module#autoload? and Module#const_defined? Issue #21035 has been updated by Eregon (Benoit Daloze). Another way to see it is that autoload and require are kind of bidirectional at least in the current CRuby behavior: ```ruby autoload :Foo, "foo.rb" ``` * If I `p Foo`, that requires `foo.rb`, the usual direction * But also if I `require "foo"` before any access to `Foo` then that also "starts" the autoload for all autoloads with path "foo.rb". So in your example in https://bugs.ruby-lang.org/issues/21035#note-3, the mere fact that there was a `require "foo"` means any autoload (present or (!) even future on CRuby as we see) are considered active while foo.rb is being required. On TruffleRuby the autoload is considered active only if the constant is being autoloaded (through triggering an autoload or through require), i.e. it doesn't consider future autoloads. So that script gives: ``` $ ruby -v -I. main.rb ruby 3.3.5 (2024-09-03 revision ef084cc8f4) [x86_64-linux] [:Foo] false nil $ ruby -v -I. main.rb truffleruby 24.1.1, like ruby 3.2.4, Oracle GraalVM Native [x86_64-linux] [:Foo] true "foo" ``` --- Could you point to the code in Zeitwerk which has to work around these subtle semantics? Is that code then incorrect due to the current semantics and there is no way or only a hacky way to address it? Maybe we could only change `autoload?` to not consider ongoing autoloads/requires, I think that should be compatible enough. Changing `defined?` or `const_defined?` OTOH seems much more risky for compatibility. ---------------------------------------- Misc #21035: Clarify or redefine Module#autoload? and Module#const_defined? https://bugs.ruby-lang.org/issues/21035#change-111958 * Author: fxn (Xavier Noria) * Status: Open ---------------------------------------- The documentation for `Module#autoload?` says: > Returns filename to be loaded if name is registered as autoload in the namespace of mod or one of its ancestors. As a user, if I declare an autoload, I expect this API: ```ruby module M autoload :Foo, 'foo' constants # => [:Foo] const_defined?(:Foo) # => true autoload?(:Foo) # => 'foo' end ``` That it is indeed how it generally works. Even if the autoload path does not exist. But there is an edge case. While `constants` does include always `:Foo` as far as I can tell, the return value of `const_defined?` and `autoload?` depends on the stack of features being loaded: The autoload path is resolved and if seen to be in the stack of features being loaded, the predicates return `false` and `nil`, respectively. Do you think that is intuitive? I find that logic totally unexpected. I just defined an autoload, therefore, I think it would be natural for `autoload?` to return what I just configured. Why should `const_defined?` return nothing but `true`? And why is it not consistent with `constants`? To me, it would make more sense that in the previous example `const_defined?` returns `true`, and `autoload?` returns `foo` unconditionally (and instantly, nowadays it takes a relative long time due to the lookup). Now, if the autoload is triggered in a lookup **then** I would expect `Kernel#require` logic to apply. But not when calling some simple predicates. Please, note that the present behavior is not documented, so on paper the change would not be backwards incompatible. If, on the other side, it is preferred to keep the behavior as it is, I guess it should be documented with precision (accounting for symlinks, relative paths in `$LOAD_PATH`, etc.) -- 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/lists/ruby-core.ml.ruby-lang.org/