Skip to content

Conversation

c42f
Copy link
Member

@c42f c42f commented Sep 19, 2025

Provide a way to create new modules without resorting to passing an Expr(:module, ...) to eval() by splitting up jl_eval_module_expr() into three pieces:

Two module creation functions which are exported:

  • jl_begin_new_module()
    • Creates the module
    • Situates it within the module hierarchy (creating a binding within the parent module, setting the module parent and inheriting uuid, registering root modules)
    • Deals with global rooting during initialization and state for detecting whether the module is open for eval.
    • Creates standard imports and bindings for module-local eval and include
  • jl_end_new_module() cleans up some of this state and calls __init__() for the module and its children when necessary.

The Expr-related machinery is left in the internal function jl_eval_module_expr().

Something along these lines is required to avoid calling eval(Expr(:module, ...)) in the JuliaLowering implementation of module evaluation where we try never to construct Expr.

There's clearly further cleanup which could be done here. It would be nice to use less global state (jl_current_modules and jl_module_init_order) and to not to have the Base.__toplevel__ hack. However I wanted to keep this PR small so I've resisted the temptation to change anything more for now.

Similar to #57965 in the sense that it splits more functionality out of eval().

@c42f c42f requested review from xal-0, mlechu and topolarity September 19, 2025 10:16
Provide a way to create new modules without resorting to passing an
`Expr(:module, ...)` to `eval()` by splitting up `jl_eval_module_expr()`
into three pieces:

Two stateful module creation functions which are exported:

* `jl_begin_new_module()`
  - Creates the module
  - Situates it within the module hierarchy (creating a binding within
    the parent module, setting the module parent and inheriting uuid,
    registering root modules)
  - Deals with global rooting during initialization and state for
    detecting whether the module is open for eval.
  - Creates standard imports and bindings for module-local eval and
    include
* `jl_end_new_module()` cleans up some of this state and calls
  `__init__()` for the module and its children when necessary.

The `Expr`-related machinery is left in the internal function
`jl_eval_module_expr()`.
@c42f c42f force-pushed the caf/begin-new-module branch from 988acb9 to 388b0ef Compare September 19, 2025 13:09
@c42f
Copy link
Member Author

c42f commented Sep 19, 2025

Test failures look unrelated.

c42f added a commit to c42f/JuliaLowering.jl that referenced this pull request Sep 20, 2025
Julia's incrementally evaluated top level semantics make it rather
tricky to design a lowering interface for top level and module level
expressions. Currently these expressions are effectively *interpreted*
by eval rather than ever being processed by lowering.

However, I'd like a cleaner separation between "low level evaluation"
and lowering, such that Core can contain only the low level eval "driver
function". I'd like to propose the split as follows:

* "Low level" evaluation is about executing a sequence of thunks
  represented as `CodeInfo`, and potentially about creating modules for
  those to be executed inside.
* Lowering is about expression processing.

In principle, the runtime's view of `eval()` shouldn't know about `Expr`
or `SyntaxTree` (or whatever AST we use) - that should be left to the
compiler frontend. A useful way to think about the duties of the
frontend is to consider the question "What if we wanted to host another
language on top of the Julia runtime?". If we can eventually achieve
that without ever generating Julia `Expr` then we will have succeeded in
separating the frontend.

To implement all this I've recast lowering as an incremental iterative
API in this change. Thus it's the job of `eval()` to simply evaluate
thunks and create new modules as driven by lowering. (Perhaps we'd move
this definition of `eval()` over to the Julia runtime before 1.13.)

Depends on JuliaLang/julia#59604
@oscardssmith oscardssmith merged commit 6ede1b2 into master Sep 24, 2025
8 checks passed
@oscardssmith oscardssmith deleted the caf/begin-new-module branch September 24, 2025 16:44
@oscardssmith oscardssmith added the compiler:lowering Syntax lowering (compiler front end, 2nd stage) label Sep 24, 2025
@vtjnash
Copy link
Member

vtjnash commented Sep 24, 2025

We might also want to consider making these Builtin functions, so that they can be interpreted fully without requiring a ccall shim, but also not important right now

xal-0 pushed a commit to xal-0/julia that referenced this pull request Sep 30, 2025
Provide a way to create new modules without resorting to passing an
`Expr(:module, ...)` to `eval()` by splitting up `jl_eval_module_expr()`
into three pieces:

Two module creation functions which are exported:

* `jl_begin_new_module()`
  - Creates the module
- Situates it within the module hierarchy (creating a binding within the
parent module, setting the module parent and inheriting uuid,
registering root modules)
- Deals with global rooting during initialization and state for
detecting whether the module is open for eval.
- Creates standard imports and bindings for module-local eval and
include
* `jl_end_new_module()` cleans up some of this state and calls
`__init__()` for the module and its children when necessary.

The `Expr`-related machinery is left in the internal function
`jl_eval_module_expr()`.

Something along these lines is required to avoid calling
`eval(Expr(:module, ...))` in the JuliaLowering implementation of module
evaluation where we try never to construct `Expr`.

There's clearly further cleanup which could be done here. It would be
nice to use less global state (`jl_current_modules` and
`jl_module_init_order`) and to not to have the `Base.__toplevel__` hack.
However I wanted to keep this PR small so I've resisted the temptation
to change anything more for now.

Similar to JuliaLang#57965 in the sense that it splits more functionality out of
`eval()`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler:lowering Syntax lowering (compiler front end, 2nd stage)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants