Skip to content
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

Merged
merged 1 commit into from
Feb 21, 2018
Merged

Make the "second Base copy" trick actually work #26111

merged 1 commit into from
Feb 21, 2018

Conversation

Keno
Copy link
Member

@Keno Keno commented Feb 19, 2018

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 modules 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.

@StefanKarpinski
Copy link
Member

These all seem like good changes to me which make the meaning of the code more precise.

ptrs[i] = unsafe_convert(P, root)::P
roots[i] = root
###
if parentmodule(@__MODULE__) === Main
Copy link
Member

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.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

@JeffBezanson
Copy link
Member

I suggest (and implement in this PR) to have the default import be the next topmodule along
the parent link chain

How does this work? It looks to me like the second loaded copy of Base does not set the topmodule flag.

@Keno
Copy link
Member Author

Keno commented Feb 20, 2018

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.
@Keno Keno merged commit b1189ce into master Feb 21, 2018
@StefanKarpinski StefanKarpinski deleted the kf/secondbase branch February 21, 2018 05:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants