-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make the "second Base copy" trick actually work #26111
Conversation
These all seem like good changes to me which make the meaning of the code more precise. |
base/refpointer.jl
Outdated
ptrs[i] = unsafe_convert(P, root)::P | ||
roots[i] = root | ||
### | ||
if parentmodule(@__MODULE__) === Main |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be better not to rely on Main
for this. It's not clear if Main
should be the parent module of Base
and Core
; in fact that seems like a holdover from the old code loading model. For now this check should be factored into a variable like is_primary_base_module
or something.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
How does this work? It looks to me like the second loaded copy of Base does not set the topmodule flag. |
It sets the topmodule flag, it just doesn't set the primary flag. |
As was mentioned in #25988, there is a handy trick where you can load a second copy of Base on top of an existing copy. This is useful for at least two reasons: 1. Base printing is available, so things like MethodErrors print nicely 2. Even if the load fails, the resulting (broken) copy of base is inspectable by standard introspection tools from the REPL, as long as you're a bit careful not to mix types from the two copies of Base. However, as I mentioned in #26079, this only actually works until about version.jl, at which point things crash. This is because at that point it tries to use PCRE which uses `Ref(0)`, which is actually an abstract type in Core, even though the type of the constructed object (`RefValue`) is in Base. As a result, the new Base gets the wrong kind of `RefValue` (the one from the original `Base`) and things break. Luckily this is easily fixed by using an explicit `RefValue` call in the relevant places. A second problem we run into is that `module`s nested under our new `Base`, get a default import of the old `Base` (unless we declare the new Base to be the global top module, but that would break the REPL subsequent to loading the new Base, which breaks reason 2 above). I suggest (and implement in this PR) to have the default import be the next topmodule along the parent link chain (as we already do for syntax defined in Base), which makes this work. A small related detail is that in all such modules `import Base: x`, needs to be replaced by `import .Base: x`, to make sure we resolve the identifier `Base` (as imported from our new top module) rather than the global name `Base` (which still refers to the old module). I changed sysimg.jl to avoid loading stdlibs in second Base mode, to avoid having to implement the same changes there. Since the stdlibs are already decoupled from Base, they can already be developed separately fairly easily, so there's not much reason to include them in this trick. For completeness, there's a couple of ways to use this trick, but perhaps the simplest is: ``` cd("base") baremodule NotBase Core.include(NotBase, "sysimg.jl") end ``` from the REPL.
As was mentioned in #25988, there is a handy trick where you can load
a second copy of Base on top of an existing copy. This is useful for
at least two reasons:
by standard introspection tools from the REPL, as long as you're a bit
careful not to mix types from the two copies of Base.
However, as I mentioned in #26079, this only actually works until about version.jl,
at which point things crash. This is because at that point it tries to use PCRE
which uses
Ref(0)
, which is actually an abstract type in Core, even though thetype of the constructed object (
RefValue
) is in Base. As a result, the new Basegets the wrong kind of
RefValue
(the one from the originalBase
) and things break.Luckily this is easily fixed by using an explicit
RefValue
call in the relevant places.A second problem we run into is that
module
s nested under our newBase
, get a defaultimport of the old
Base
(unless we declare the new Base to be the global top module, butthat would break the REPL subsequent to loading the new Base, which breaks reason 2 above).
I suggest (and implement in this PR) to have the default import be the next topmodule along
the parent link chain (as we already do for syntax defined in Base), which makes this work.
A small related detail is that in all such modules
import Base: x
, needs to be replaced byimport .Base: x
, to make sure we resolve the identifierBase
(as imported from ournew top module) rather than the global name
Base
(which still refers to the old module).I changed sysimg.jl to avoid loading stdlibs in second Base mode, to avoid having to implement
the same changes there. Since the stdlibs are already decoupled from Base, they can already
be developed separately fairly easily, so there's not much reason to include them in this trick.
For completeness, there's a couple of ways to use this trick, but perhaps the simplest is:
from the REPL.