You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Remove :globaldecl and :global lowered forms; add Core.declare_global (#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]>
0 commit comments