Skip to content

Conversation

xal-0
Copy link
Member

@xal-0 xal-0 commented Mar 31, 2025

Overview

Currently, import and using statements are compiled into calls to
jl_toplevel_eval with the Expr as an argument. It can be useful in an
interactive environment to do import/using programmatically, for which
eval(Expr(:import, ...)) or a macro is the only option at the moment (see for
example InteractiveUtils.@activate).

This PR adds two builtins, _module_import and _module_using, with these
signatures, and removes Expr(:import/:using, ...) from the lowered IR:

_module_import(explicit::Bool, to::Module, ctx::Union{Expr, Nothing}, names::Expr...)
_module_using(to::Module, from::Expr{Symbol})

Lowering example

import statements and using X: statements are lowered to _module_import
like so:

import A             => _module_import(true,  Main, nothing,          Expr(:., :A))
import A.b           => _module_import(true,  Main, nothing,          Expr(:., :A, :b))
import A.b as c      => _module_import(true,  Main, nothing,          Expr(:as, Expr(:., :A, :b), :c))
import A.B: C.d, e   => _module_import(true,  Main, Expr(:., :A, :B), Expr(:., :C, :d), Expr(:., :e))
import A.B: C.d as e => _module_import(true,  Main, Expr(:., :A, :B), Expr(:as, Expr(:., :C, :d), :e))
using  A.B: C.d, e   => _module_import(false, Main, Expr(:., :A, :B), Expr(:., :C, :d), Expr(:., :e))

Plain using statements become _module_using:

using A.B            => _module_using(Main, Expr(:., :A, :B))

Multiple comma-separated using or import paths are lowered to multiple calls to the appropriate builtin:

julia> Meta.@lower using A.B, C
:($(Expr(:thunk, CodeInfo(
1 ─       builtin Core._module_using(Main, $(QuoteNode(:($(Expr(:., :A, :B))))))
│         builtin Core._module_using(Main, $(QuoteNode(:($(Expr(:., :C))))))
│       $(Expr(:latestworld))
└──     return nothing
))))

Alternative designs

Some alternatives to these builtins are worth considering.

The lowering pass could do even less work by inserting a call to
_module_import or _module_using that takes a single Expr argument for the
entire statement.

Another option is to do even more in lowering, introducing very low-level
builtins that wrap jl_module_import and jl_module_using directly. The
lowering pass would expand the import path into direct calls to Base.require,
Base.getproperty, etc. For example, import A.B: C.d, e could become
something like:

(block
  (= (ssavalue 1)
     (call (top getproperty)
           (call (top require) (thismodule) 'A)
           'B))
  (call (core module_import)
        (thismodule)
        (call (top getproperty) (ssavalue 1) 'C)
        'd)
  (call (core module_import) (thismodule) (ssavalue 1) 'd)
  (latestworld))

I like the idea of supporting calls to _module_import with a module for
ctx instead of an Expr, but this could be added to the proposed design too.

Removes the lowered IR forms Expr(:using, ...) and Expr(:import, ...).  `import`
and `using` expressions are now lowered to calls to `_module_import` like so:

  import A, B.c

    (block
      (call (core _module_import) (true) (thismodule) (null) (inert (|.| A)))
      (call (core _module_import) (true) (thismodule) (null) (inert (|.| B c)))
      (latestworld))

  import A.B: C.d, e as f

    (block
      (call (core _module_import) (true) (thismodule)
            (inert (|.| A B))
            (inert (|.| C d))
            (inert (as (|.| e) f))))

  using A.B: C.d

    (block
      (call (core _module_import) (false) (thismodule)
            (inert (|.| A B))
            (inert (|.| C d)))
      (latestworld))

Each comma-separated import is lowered to a call to the new _module_import
builtin, which takes an "explicit" flag (`using X:` is lowered to _module_import
with explicit=false), a "context" path (set to `nothing` for `import` without
`:`), and any number of Exprs of the form `(|.| ...)` or `(as (|.| ...) x)`
representing import paths under the context path.

Without `:`, using is lowered to the other new builtin:

    using A.B

    (block
      (call (core _module_using) (thismodule) (inert (|.| A B)))
      (latestworld))
@xal-0 xal-0 added compiler:lowering Syntax lowering (compiler front end, 2nd stage) modules labels Mar 31, 2025
@LilithHafner LilithHafner requested a review from topolarity March 31, 2025 17:32
@Keno
Copy link
Member

Keno commented Apr 1, 2025

Generally positive on the direction.

For this particular implementation, I'll need to think about it in a bit more depth, but my initial impression is that this feels too "syntax oriented" for a builtin. I mean this in two senses. The first is that these builtins are taking Expr, which is a syntax data structure and not really touched anywhere else by the runtime system. The second is that it conflates the resolution of the name to be imported with the establishment of the link between them. I'd have expected something closer to:

_module_import(::Binding, ::Binding, imported::Bool)
_module_using(::Module, ::Module)

From this, there could then be higher level functions or macros that implement the syntax parts. The primary downside I see is that this approach would prevent batching of the world age update. However, I think that is possibly better handled with a generic world agree compression optimization in the runtime rather than trying to design the syntax around it.

@vtjnash
Copy link
Member

vtjnash commented Apr 1, 2025

Agreed as to our eventual plan here. I think there are few phases, one (here) is keeping most of the existing code structure, but introducing a function form for it. Secondly we can move a lot of this C code into julia using some simpler builtins for the primitive bits. I think the tricky part of deciding on that form is dealing with the fact this is very much just interpreting the syntax, so it is hard to see what that julia function's arguments should be other than exactly the AST.

xal-0 added 5 commits April 3, 2025 16:21
Renames _module_import -> _eval_import and _module_using -> _eval_using (we have
jl_module_using and jl_module_using in C and it's confusing enough already), and
moves the logic into Julia.

Since we need _eval_import very early in bootstrapping, boot.jl provides a
minimal version that understands only `import M: a, b, ...`.  Some imports in
code included by Base_compiler.jl has been updated to match this.

The Julia _eval_* functions call two new low-level builtins:
- Core._import calls jl_module_import with the normal 5 argument form, or
  jl_import_module for the special case of importing a package to get a
  PARTITION_KIND_CONST_IMPORT binding (3 argument form).
- Core._using is a direct wrapper around jl_module_using.
@xal-0
Copy link
Member Author

xal-0 commented Apr 16, 2025

c638398 is a more-or-less direct translation of _module_import and _module_using into Julia, renamed to _eval_import and _eval_using because we already have jl_module_import and jl_import_module and it was a little confusing. The low-level builtins are now:

_import(to::Module, from::Module, asname::Symbol, [sym::Symbol, imported::Bool])

With all five arguments, imports sym from module from into to with name
asname. imported is true for bindings created with import (set it to
false for using A: ...).

With only the first three arguments, creates a binding for the module from
with name asname in to.

_using(to::Module, from::Module)

Add from to the usings list of to.

Core._using is a direct wrapper around jl_module_using. Core._import either calls jl_module_import (first form),or jl_import_module (second form, installs a PARTITION_KIND_CONST_IMPORT). If we could remove PARTITION_KIND_CONST_IMPORT it could be deleted.

@xal-0 xal-0 changed the title Add builtin functions _module_import, _module_using Add builtin functions Core._import, Core._using, implement import/using logic in Julia Apr 16, 2025
@xal-0 xal-0 changed the title Add builtin functions Core._import, Core._using, implement import/using logic in Julia Add builtin functions Core._import, Core._using; implement import/using logic in Julia Apr 16, 2025
base/module.jl Outdated
elseif v === :Base
m = Base
else
# TODO: JL_TIMING(LOAD_IMAGE, LOAD_Require)
Copy link
Member

Choose a reason for hiding this comment

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

Can use Core.Compiler.@zone now perhaps.

@vtjnash vtjnash merged commit 59e9ada into JuliaLang:master Apr 28, 2025
4 of 7 checks passed
serenity4 pushed a commit to serenity4/julia that referenced this pull request May 1, 2025
…ng logic in Julia (JuliaLang#57965)

Currently, `import` and `using` statements are compiled into calls to
`jl_toplevel_eval` with the Expr as an argument.  It can be useful in an
interactive environment to do import/using programmatically, for which
`eval(Expr(:import, ...))` or a macro is the only option at the moment
(see for example `InteractiveUtils.@activate`).

This PR adds two functions, `_module_import ` and `_module_using`, with
these signatures, and removes `Expr(:import/:using, ...)` from the lowered IR:

```
_module_import(explicit::Bool, to::Module, ctx::Union{Expr, Nothing}, names::Expr...)
_module_using(to::Module, from::Expr{Symbol})
```

## Lowering example

`import` statements and `using X:` statements are lowered to
`_module_import` like so:

```
import A             => _module_import(true,  Main, nothing,          Expr(:., :A))
import A.b           => _module_import(true,  Main, nothing,          Expr(:., :A, :b))
import A.b as c      => _module_import(true,  Main, nothing,          Expr(:as, Expr(:., :A, :b), :c))
import A.B: C.d, e   => _module_import(true,  Main, Expr(:., :A, :B), Expr(:., :C, :d), Expr(:., :e))
import A.B: C.d as e => _module_import(true,  Main, Expr(:., :A, :B), Expr(:as, Expr(:., :C, :d), :e))
using  A.B: C.d, e   => _module_import(false, Main, Expr(:., :A, :B), Expr(:., :C, :d), Expr(:., :e))
```

Plain `using` statements become `_module_using`:

```
using A.B            => _module_using(Main, Expr(:., :A, :B))
```

Multiple comma-separated `using` or `import` paths are lowered to
multiple calls to the appropriate builtin:
```
julia> Meta.@lower using A.B, C
:($(Expr(:thunk, CodeInfo(
1 ─       builtin Core._module_using(Main, $(QuoteNode(:($(Expr(:., :A, :B))))))
│         builtin Core._module_using(Main, $(QuoteNode(:($(Expr(:., :C))))))
│       $(Expr(:latestworld))
└──     return nothing
))))
```
LebedevRI pushed a commit to LebedevRI/julia that referenced this pull request May 2, 2025
…ng logic in Julia (JuliaLang#57965)

Currently, `import` and `using` statements are compiled into calls to
`jl_toplevel_eval` with the Expr as an argument.  It can be useful in an
interactive environment to do import/using programmatically, for which
`eval(Expr(:import, ...))` or a macro is the only option at the moment
(see for example `InteractiveUtils.@activate`).

This PR adds two functions, `_module_import ` and `_module_using`, with
these signatures, and removes `Expr(:import/:using, ...)` from the lowered IR:

```
_module_import(explicit::Bool, to::Module, ctx::Union{Expr, Nothing}, names::Expr...)
_module_using(to::Module, from::Expr{Symbol})
```

## Lowering example

`import` statements and `using X:` statements are lowered to
`_module_import` like so:

```
import A             => _module_import(true,  Main, nothing,          Expr(:., :A))
import A.b           => _module_import(true,  Main, nothing,          Expr(:., :A, :b))
import A.b as c      => _module_import(true,  Main, nothing,          Expr(:as, Expr(:., :A, :b), :c))
import A.B: C.d, e   => _module_import(true,  Main, Expr(:., :A, :B), Expr(:., :C, :d), Expr(:., :e))
import A.B: C.d as e => _module_import(true,  Main, Expr(:., :A, :B), Expr(:as, Expr(:., :C, :d), :e))
using  A.B: C.d, e   => _module_import(false, Main, Expr(:., :A, :B), Expr(:., :C, :d), Expr(:., :e))
```

Plain `using` statements become `_module_using`:

```
using A.B            => _module_using(Main, Expr(:., :A, :B))
```

Multiple comma-separated `using` or `import` paths are lowered to
multiple calls to the appropriate builtin:
```
julia> Meta.@lower using A.B, C
:($(Expr(:thunk, CodeInfo(
1 ─       builtin Core._module_using(Main, $(QuoteNode(:($(Expr(:., :A, :B))))))
│         builtin Core._module_using(Main, $(QuoteNode(:($(Expr(:., :C))))))
│       $(Expr(:latestworld))
└──     return nothing
))))
```
aviatesk added a commit to JuliaDebug/JuliaInterpreter.jl that referenced this pull request May 3, 2025
aviatesk added a commit to JuliaDebug/JuliaInterpreter.jl that referenced this pull request May 3, 2025
aviatesk added a commit to JuliaDebug/JuliaInterpreter.jl that referenced this pull request May 3, 2025
aviatesk added a commit to JuliaDebug/JuliaInterpreter.jl that referenced this pull request May 3, 2025
charleskawczynski pushed a commit to charleskawczynski/julia that referenced this pull request May 12, 2025
…ng logic in Julia (JuliaLang#57965)

Currently, `import` and `using` statements are compiled into calls to
`jl_toplevel_eval` with the Expr as an argument.  It can be useful in an
interactive environment to do import/using programmatically, for which
`eval(Expr(:import, ...))` or a macro is the only option at the moment
(see for example `InteractiveUtils.@activate`).

This PR adds two functions, `_module_import ` and `_module_using`, with
these signatures, and removes `Expr(:import/:using, ...)` from the lowered IR:

```
_module_import(explicit::Bool, to::Module, ctx::Union{Expr, Nothing}, names::Expr...)
_module_using(to::Module, from::Expr{Symbol})
```

## Lowering example

`import` statements and `using X:` statements are lowered to
`_module_import` like so:

```
import A             => _module_import(true,  Main, nothing,          Expr(:., :A))
import A.b           => _module_import(true,  Main, nothing,          Expr(:., :A, :b))
import A.b as c      => _module_import(true,  Main, nothing,          Expr(:as, Expr(:., :A, :b), :c))
import A.B: C.d, e   => _module_import(true,  Main, Expr(:., :A, :B), Expr(:., :C, :d), Expr(:., :e))
import A.B: C.d as e => _module_import(true,  Main, Expr(:., :A, :B), Expr(:as, Expr(:., :C, :d), :e))
using  A.B: C.d, e   => _module_import(false, Main, Expr(:., :A, :B), Expr(:., :C, :d), Expr(:., :e))
```

Plain `using` statements become `_module_using`:

```
using A.B            => _module_using(Main, Expr(:., :A, :B))
```

Multiple comma-separated `using` or `import` paths are lowered to
multiple calls to the appropriate builtin:
```
julia> Meta.@lower using A.B, C
:($(Expr(:thunk, CodeInfo(
1 ─       builtin Core._module_using(Main, $(QuoteNode(:($(Expr(:., :A, :B))))))
│         builtin Core._module_using(Main, $(QuoteNode(:($(Expr(:., :C))))))
│       $(Expr(:latestworld))
└──     return nothing
))))
```
@oscardssmith
Copy link
Member

is Core._using necessary to have as a builtin? Couldn't it have just been a wrapper over Core._import?

@xal-0
Copy link
Member Author

xal-0 commented Aug 12, 2025

is Core._using necessary to have as a builtin? Couldn't it have just been a wrapper over Core._import?

I thought it was nicest to keep them separate, and this distinction already existing in module.c: Core._using adds to the destination module's usings list, while Core._import always creates a single binding in the destination module (the two forms import a binding from the source module or create a const binding to the source module itself).

@c42f
Copy link
Member

c42f commented Aug 21, 2025

Drive by comment: this is great, it's exactly what I needed to fix some janky use of eval() in JuliaLowering.jl, thank you!

(It also breaks JuliaLowering, but that's a price I'm very much willing to pay 😆)

c42f added a commit to c42f/JuliaLowering.jl that referenced this pull request Aug 21, 2025
Calls to `import` and `using` are expanded by lowering as of the
changes in JuliaLang/julia#57965
and no longer dealt with by the C function `jl_toplevel_eval_flex`.

This implies we can't use `eval()` for these if we want to activate
JuliaLowering in Core, or we'll hit a stack overflow.

I've chosen to duplicate the flisp lowering here for consistency and
import paths are thus lowered to a restricted kind of quoted `Expr`.
(It's mildly annoying to rely on quoted `Expr` in the lowered paths than
the previous use of `Core.svec` but deleting the svec representation
allows us to use `Base._eval_import` and `Base._eval_using` directly so
seems like a worthy simplification.)
c42f added a commit to c42f/JuliaLowering.jl that referenced this pull request Aug 21, 2025
Calls to `import` and `using` are expanded by lowering as of the
changes in JuliaLang/julia#57965
and no longer dealt with by the C function `jl_toplevel_eval_flex`.

This implies we can't use `eval()` for these if we want to activate
JuliaLowering in Core, or we'll hit a stack overflow.

I've chosen to duplicate the flisp lowering here for consistency and
import paths are thus lowered to a restricted kind of quoted `Expr`.
(It's mildly annoying to rely on quoted `Expr` in the lowered paths than
the previous use of `Core.svec` but deleting the svec representation
allows us to use `Base._eval_import` and `Base._eval_using` directly so
seems like a worthy simplification.)

Similarly, use a precomputed vector of names in public/export
expansion - this list can be computed at expansion time rather
than emitting each element into the lowered code individually.

Includes minor test+CI fixes julia 1.12 in support of JETLS.
aviatesk pushed a commit to mlechu/JuliaLowering.jl that referenced this pull request Aug 21, 2025
Calls to `import` and `using` are expanded by lowering as of the
changes in JuliaLang/julia#57965
and no longer dealt with by the C function `jl_toplevel_eval_flex`.

This implies we can't use `eval()` for these if we want to activate
JuliaLowering in Core, or we'll hit a stack overflow.

I've chosen to duplicate the flisp lowering here for consistency and
import paths are thus lowered to a restricted kind of quoted `Expr`.
(It's mildly annoying to rely on quoted `Expr` in the lowered paths than
the previous use of `Core.svec` but deleting the svec representation
allows us to use `Base._eval_import` and `Base._eval_using` directly so
seems like a worthy simplification.)

Similarly, use a precomputed vector of names in public/export
expansion - this list can be computed at expansion time rather
than emitting each element into the lowered code individually.

Includes minor test+CI fixes julia 1.12 in support of JETLS.
@c42f c42f mentioned this pull request Sep 19, 2025
oscardssmith pushed a commit that referenced this pull request Sep 24, 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()`.
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()`.
xal-0 added a commit that referenced this pull request Oct 1, 2025
…#58279)

# Overview
In the spirit of #58187 and #57965, this PR lowers more surface syntax
to calls,
eliminating the lowered `:global` and `:globaldecl` operations in favour
of a
single `Core.declare_global` builtin.

`Core.declare_global` has the signature:
```
declare_global(module::Module, name::Symbol, strong::Bool=false, [ty::Type])
```

- When `strong = false`, it has the effect of `global name` at the top
level
(see the description for
[`PARTITION_KIND_DECLARED`](https://github.com/JuliaLang/julia/blob/d46b665067bd9fc352c89c9d0abb591eaa4f7695/src/julia.h#L706-L710)).
- With `strong = true`:
- No `ty` provided: if no global exists, creates a strong global with
type
`Any`. Has no effect if one already exists. This form is generated by
    global assignments with no type declaration.
- `ty` provided: always creates a new global with the given type,
failing if
    one already exists with a different declared type.

## Definition effects
One of the purposes of this change is to remove the definitions effects
for
`:global` and `:globaldecl`:

https://github.com/JuliaLang/julia/blob/d46b665067bd9fc352c89c9d0abb591eaa4f7695/src/method.c#L95-L105

The eventual goal is to make all the definition effects for a method
explicit
after lowering, simplifying interpreters for lowered IR.

## Minor lowering changes
### `global` permitted in more places
Adds a new ephemeral syntax head, `unused-only`, to wrap expressions
whose
result should not be used. It generates the `misplaced "global"
declaration`
error, and is slightly more forgiving than the old restriction. This was
necessary to permit `global` to be lowered in all contexts.  Old:

```
julia> global example

julia> begin
           global example
       end
ERROR: syntax: misplaced "global" declaration
Stacktrace:
 [1] top-level scope
   @ REPL[2]:1
```

New:
```
julia> global example

julia> begin
           global example
       end
```

### `global` always lowered
This change maintains support for some expressions that cannot be
produced by
the parser (similar to `Expr(:const, :foo)`):

https://github.com/JuliaLang/julia/blob/d46b665067bd9fc352c89c9d0abb591eaa4f7695/test/precompile.jl#L2036

This used to work by bypassing lowering but is now lowered to the
appropriate
`declare_global` call.

## Generated functions
After lowering the body AST returned by a `@generated` function, the
definition
effects are still performed.  Instead of relying on a check in
`jl_declare_global` to fail during this process, `GeneratedFunctionStub`
now
wraps the AST in a new Expr head, `Expr(:toplevel_pure, ...)`,
indicating
lowering should not produce toplevel side effects.

Currently, this is used only to omit calls to `declare_global` for
generated
functions, but it could also be used to improve the catch-all error
message when
lowering returns a thunk (telling the user if it failed because of a
closure,
generator, etc), or even to support some closures by making them opaque.

The error message for declaring a global as a side effect of a
`@generated`
function AST has changed, because it now fails when the assignment to an
undeclared global is performed.  Old:
```
julia> @generated function foo(x)
           :(global bar = x)
       end
foo (generic function with 1 method)

julia> foo(1)
ERROR: new strong globals cannot be created in a generated function. Declare them outside using `global x::Any`.
Stacktrace:
 [1] top-level scope
   @ REPL[2]:1
```

New:
```
julia> @generated function foo(x)
           :(global bar = x)
       end
foo (generic function with 1 method)

julia> foo(1)
ERROR: Global Main.bar does not exist and cannot be assigned.
Note: Julia 1.9 and 1.10 inadvertently omitted this error check (#56933).
Hint: Declare it using `global bar` inside `Main` before attempting assignment.
Stacktrace:
 [1] macro expansion
   @ ./REPL[1]:1 [inlined]
 [2] foo(x::Int64)
   @ Main ./REPL[1]:1
 [3] top-level scope
   @ REPL[2]:1
```


## Examples of the new lowering
Toplevel weak global:
```
julia> Meta.@lower global example
:($(Expr(:thunk, CodeInfo(
1 ─       builtin Core.declare_global(Main, :example, false)
│       $(Expr(:latestworld))
└──     return nothing
))))
```

Toplevel strong global declaration with type:
```
julia> Meta.@lower example::Int
:($(Expr(:thunk, CodeInfo(
1 ─ %1 = Main.example
│   %2 = Main.Int
│   %3 =   builtin Core.typeassert(%1, %2)
└──      return %3
))))
```

Toplevel strong global assignment:
```
julia> Meta.@lower example = 1
:($(Expr(:thunk, CodeInfo(
1 ─         builtin Core.declare_global(Main, :example, true)
│         $(Expr(:latestworld))
│   %3  =   builtin Core.get_binding_type(Main, :example)
│         #s1 = 1
│   %5  = #s1
│   %6  =   builtin %5 isa %3
└──       goto #3 if not %6
2 ─       goto #4
3 ─ %9  = #s1
└──       #s1 = Base.convert(%3, %9)
4 ┄ %11 = #s1
│           dynamic Base.setglobal!(Main, :example, %11)
└──       return 1
))))
```

Toplevel strong global assignment with type:
```
julia> Meta.@lower example::Int = 1
:($(Expr(:thunk, CodeInfo(
1 ─ %1  = Main.Int
│           builtin Core.declare_global(Main, :example, true, %1)
│         $(Expr(:latestworld))
│   %4  =   builtin Core.get_binding_type(Main, :example)
│         #s1 = 1
│   %6  = #s1
│   %7  =   builtin %6 isa %4
└──       goto #3 if not %7
2 ─       goto #4
3 ─ %10 = #s1
└──       #s1 = Base.convert(%4, %10)
4 ┄ %12 = #s1
│           dynamic Base.setglobal!(Main, :example, %12)
└──       return 1
))))
```

Global assignment inside function (call to `declare_global` hoisted to
top
level):
```
julia> Meta.@lower function f1(x)
           global example = x
       end
:($(Expr(:thunk, CodeInfo(
1 ─       $(Expr(:method, :(Main.f1)))
│         $(Expr(:latestworld))
│         $(Expr(:latestworld))
│           builtin Core.declare_global(Main, :example, false)
│         $(Expr(:latestworld))
│           builtin Core.declare_global(Main, :example, true)
│         $(Expr(:latestworld))
│   %8  = Main.f1
│   %9  =   dynamic Core.Typeof(%8)
│   %10 =   builtin Core.svec(%9, Core.Any)
│   %11 =   builtin Core.svec()
│   %12 =   builtin Core.svec(%10, %11, $(QuoteNode(:(#= REPL[7]:1 =#))))
│         $(Expr(:method, :(Main.f1), :(%12), CodeInfo(
    @ REPL[7]:2 within `unknown scope`
1 ─ %1  = x
│   %2  =   builtin Core.get_binding_type(Main, :example)
│         @_3 = %1
│   %4  = @_3
│   %5  =   builtin %4 isa %2
└──       goto #3 if not %5
2 ─       goto #4
3 ─ %8  = @_3
└──       @_3 = Base.convert(%2, %8)
4 ┄ %10 = @_3
│           dynamic Base.setglobal!(Main, :example, %10)
└──       return %1
)))
│         $(Expr(:latestworld))
│   %15 = Main.f1
└──       return %15
))))
```

---------

Co-authored-by: Jameson Nash <[email protected]>
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) modules
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants