Skip to content

Commit d1f7ea2

Browse files
committed
Merge branch 'master' into undotted_setindex
2 parents 7c3ce33 + b1f508a commit d1f7ea2

19 files changed

Lines changed: 310 additions & 136 deletions

File tree

JuliaLowering/src/eval.jl

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -765,17 +765,22 @@ end
765765

766766
#-------------------------------------------------------------------------------
767767
# Our version of eval - should be upstreamed though?
768-
@fzone "JL: eval" function eval(mod::Module, ex::SyntaxTree;
768+
@fzone "JL: eval" function eval(mod::Module, @nospecialize(ex);
769769
soft_scope::Union{Nothing,Bool}=nothing,
770770
expr_compat_mode::Bool=false)
771+
# Run the `eval` driver in the lowering world. Any internal operations
772+
# are required to `invokelatest` before executing any code that dispatches
773+
# on user code / types.
771774
ver = expr_compat_mode ? JL_OLD_SYNTAX_VERSION : JL_NEW_SYNTAX_VERSION
772-
iter = lower_init(ex, ver)
773-
_eval(mod, iter; soft_scope)
775+
return invoke_in_lowering_world(_lower_and_eval, mod, ex, ver, soft_scope)
774776
end
775777

776-
# Version of eval() taking `Expr` (or Expr tree leaves of any type)
777-
function eval(mod::Module, @nospecialize(ex); opts...)
778-
eval(mod, expr_to_est(ex); opts...)
778+
# `ex` may be a `SyntaxTree` or an `Expr` (or `Expr` tree leaves of any type).
779+
function _lower_and_eval(mod::Module, @nospecialize(ex), ver::VersionNumber,
780+
soft_scope::Union{Nothing,Bool})
781+
st = ex isa SyntaxTree ? ex : expr_to_est(ex)
782+
iter = lower_init(st, ver)
783+
return _eval(mod, iter; soft_scope)
779784
end
780785

781786
function _eval(mod::Module, iter::LoweringIterator; soft_scope::Union{Nothing,Bool}=nothing)
@@ -797,7 +802,7 @@ function _eval(mod::Module, iter::LoweringIterator; soft_scope::Union{Nothing,Bo
797802
result = pop!(modules)
798803
else
799804
@assert type == :thunk
800-
result = Core.eval(modules[end], thunk[2])
805+
result = Base.invokelatest(Core.eval, modules[end], thunk[2])
801806
end
802807
end
803808
@assert length(modules) === 1

JuliaLowering/src/hooks.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ function core_lowering_hook(@nospecialize(code), mod::Module, file::Union{String
1313
return Core.svec(code)
1414
end
1515

16+
if _has_v1_13_hooks && Core._lower === core_lowering_hook &&
17+
unsafe_load(cglobal(:jl_lowering_world, Csize_t)) == 0
18+
# Refuse to run as `Core._lower` without a pinned world
19+
error("`Core._lower` was set without pinning the lowering world; use `JuliaLowering.activate!()`")
20+
end
21+
1622
# TODO: fix in base
1723
file = file isa Ptr{UInt8} ? unsafe_string(file) : file
1824
line = !(line isa Int) ? Int(line) : line
@@ -59,7 +65,10 @@ function activate!(enable=true)
5965

6066
if enable
6167
Core._setlowerer!(core_lowering_hook)
68+
ccall(:jl_set_lowering_world, Cvoid, (Csize_t,), Base.get_world_counter())
6269
else
6370
Core._setlowerer!(Base.fl_lower)
71+
# Unlike JL, `jl_lower` dispatches the flisp wrapper at the latest world
72+
ccall(:jl_set_lowering_world, Cvoid, (Csize_t,), 0)
6473
end
6574
end

JuliaLowering/src/macro_expansion.jl

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ function _eval_dot(world::UInt, mod, ex::SyntaxTree)
155155
ex = ex[1]
156156
end
157157
kind(ex) in KSet"Identifier Symbol" && mod isa Module ?
158-
Base.invoke_in_world(world, getproperty, mod, Symbol(ex.name_val)) :
158+
_invoke_in_world(world, getproperty, mod, Symbol(ex.name_val)) :
159159
nothing
160160
end
161161

@@ -171,8 +171,8 @@ function eval_macro_name(ctx, mctx::MacroContext, st0::SyntaxTree)
171171
if kind(st) === K"Value"
172172
st.value
173173
elseif kind(st) === K"Identifier"
174-
Base.invoke_in_world(ctx.world, getproperty,
175-
syntax_module(st), Symbol(st.name_val))
174+
_invoke_in_world(ctx.world, getproperty,
175+
syntax_module(st), Symbol(st.name_val))
176176
elseif kind(st) === K"." &&
177177
# TODO: correct mod?
178178
(ed = _eval_dot(ctx.world, mod, st); !isnothing(ed))
@@ -223,16 +223,15 @@ function expand_macro(ctx::MacroExpansionContext, st::SyntaxTree)
223223
macfunc = eval_macro_name(ctx, mctx, macname)
224224
raw_args = st[3:end]
225225

226-
# TODO: hasmethod always returns false for our `typemax(UInt)` meaning
227-
# "latest world," which we shouldn't be using.
228-
has_new_macro = ctx.world === typemax(UInt) ?
229-
hasmethod(macfunc, Tuple{typeof(mctx), typeof.(raw_args)...}) :
230-
hasmethod(macfunc, Tuple{typeof(mctx), typeof.(raw_args)...}; world=ctx.world)
226+
# `ctx.world === typemax(UInt)` is our sentinel for "latest world"
227+
macro_world = ctx.world === typemax(UInt) ? Base.get_world_counter() : ctx.world
228+
has_new_macro = hasmethod(macfunc, Tuple{typeof(mctx), typeof.(raw_args)...}; world=macro_world)
231229

232230
if has_new_macro
233231
macro_args = [mctx, raw_args...]
232+
macro_mi = lookup_method_instance(macfunc, macro_args, macro_world)
234233
expanded = try
235-
Base.invoke_in_world(ctx.world, macfunc, macro_args...)
234+
_invoke_in_world(ctx.world, macfunc, macro_args...)
236235
catch exc
237236
newexc = exc isa MacroExpansionError ?
238237
MacroExpansionError(mctx, exc.ex, exc.msg, exc.position, exc.err) :
@@ -253,8 +252,9 @@ function expand_macro(ctx::MacroExpansionContext, st::SyntaxTree)
253252
@jl_assert kind(arg) !== K"VERSION" arg # handled in EST conversion
254253
push!(macro_args, est_to_expr(arg))
255254
end
255+
macro_mi = lookup_method_instance(macfunc, macro_args, macro_world)
256256
st_out = try
257-
Base.invoke_in_world(ctx.world, macfunc, macro_args...)
257+
_invoke_in_world(ctx.world, macfunc, macro_args...)
258258
catch exc
259259
if exc isa MethodError && exc.f === macfunc && !isempty(
260260
methods_in_world(macfunc, Tuple{typeof(mctx), Vararg{Any}}, ctx.world, st))
@@ -270,7 +270,7 @@ function expand_macro(ctx::MacroExpansionContext, st::SyntaxTree)
270270
end
271271
# Module scope for the returned AST is the module where this particular
272272
# method was defined (may be different from `parentmodule(macfunc)`)
273-
mod_for_ast = lookup_method_instance(macfunc, macro_args, ctx.world).def.module
273+
mod_for_ast = macro_mi !== nothing ? macro_mi.def.module : parentmodule(macfunc)
274274
sc2 = SyntaxContext(
275275
ScopeLayer(mod_for_ast, sc_in.layer), st,
276276
(has_new_macro ? JL_NEW_SYNTAX_VERSION : JL_OLD_SYNTAX_VERSION), false)

JuliaLowering/src/precompile.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ if Base.get_bool_env("JULIA_LOWERING_PRECOMPILE", true)
3434
end
3535
3636
macro _precompile_plus1(ex)
37-
:($ex + 1)
37+
:($(esc(ex)) + 1)
3838
end
3939
_precompile_usemac(x) = @_precompile_plus1(x)
4040
@@ -48,5 +48,5 @@ if Base.get_bool_env("JULIA_LOWERING_PRECOMPILE", true)
4848
_precompile_usemac(3)
4949
_precompile_genf(1.0)
5050
"""
51-
include_string(@__MODULE__, workload, @__FILE__)
51+
include_string(@__MODULE__, workload, @__FILE__; expr_compat_mode=true)
5252
end

JuliaLowering/src/runtime.jl

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,36 @@
77
#-------------------------------------------------------------------------------
88
# Functions/types used by code emitted from lowering, but not called by it directly
99

10+
@inline function _invoke_in_world(w::UInt, f::F, @nospecialize(args...)) where {F}
11+
if ccall(:jl_is_in_pure_context, Int8, ()) != 0
12+
# Similar to `Base.invoke_in_world` but also works inside generated-function
13+
# expansion (see `jl_code_for_staged`)
14+
return Core._call_in_world_total(w, f, args...)
15+
end
16+
return Base.invoke_in_world(w, f, args...)
17+
end
18+
19+
# Re-dispatch `f(args...)` at the pinned lowering world (see `jl_lowering_world`)
20+
@inline function invoke_in_lowering_world(f::F, @nospecialize(args...)) where {F}
21+
w = unsafe_load(cglobal(:jl_lowering_world, Csize_t))
22+
if w == 0
23+
# Fallback when the Base lowering hook is not set up
24+
w = Base.tls_world_age()
25+
# FIXME: as a side effect, enabling the Base lowering hook now affects
26+
# JuliaLowering execution not passing through the hook
27+
end
28+
return _invoke_in_world(w, f, args...)
29+
end
30+
1031
# Return the current exception. In JuliaLowering we use this rather than the
1132
# special form `K"the_exception"` to reduce the number of special forms.
1233
Base.@assume_effects :removable function current_exception()
1334
@ccall jl_current_exception(current_task()::Any)::Any
1435
end
1536

16-
function _interpolate_expr(@nospecialize(ex), depth, @nospecialize(vals::Tuple), val_i)
37+
function __interpolate_expr(@nospecialize(ex), depth, @nospecialize(vals::Tuple), val_i)
1738
if ex isa QuoteNode
18-
out = _interpolate_expr(Expr(:inert, ex.value), depth, vals, val_i)
39+
out = __interpolate_expr(Expr(:inert, ex.value), depth, vals, val_i)
1940
QuoteNode(only(out.args))
2041
elseif !(ex isa Expr)
2142
ex
@@ -30,18 +51,21 @@ function _interpolate_expr(@nospecialize(ex), depth, @nospecialize(vals::Tuple),
3051
push!(cs_out, v)
3152
end
3253
else
33-
push!(cs_out, _interpolate_expr(e, inner_depth, vals, val_i))
54+
push!(cs_out, __interpolate_expr(e, inner_depth, vals, val_i))
3455
end
3556
end
3657
Expr(ex.head, cs_out...)
3758
end
3859
end
39-
function interpolate_expr(@nospecialize(ex), @nospecialize(values...))
60+
function _interpolate_expr(@nospecialize(ex), @nospecialize(values::Tuple))
4061
@jl_assert !Meta.isexpr(ex, :$) (expr_to_est(ex), "expand_quote should handle this")
41-
_interpolate_expr(ex, 0, values, Ref(0))
62+
__interpolate_expr(ex, 0, values, Ref(0))
63+
end
64+
function interpolate_expr(@nospecialize(ex), @nospecialize(values...))
65+
return invoke_in_lowering_world(_interpolate_expr, ex, values)
4266
end
4367

44-
function _interpolate_syntax(st::SyntaxTree, depth, @nospecialize(vals), val_i)
68+
function __interpolate_syntax(st::SyntaxTree, depth, @nospecialize(vals), val_i)
4569
is_leaf(st) && return mkleaf(st)
4670
k = kind(st)
4771
inner_depth = k == K"syntaxquote" ? depth + 1 :
@@ -58,19 +82,22 @@ function _interpolate_syntax(st::SyntaxTree, depth, @nospecialize(vals), val_i)
5882
push!(cs_out, v2)
5983
end
6084
else
61-
push!(cs_out, _interpolate_syntax(c, inner_depth, vals, val_i))
85+
push!(cs_out, __interpolate_syntax(c, inner_depth, vals, val_i))
6286
end
6387
end
6488
mknode(st, cs_out)
6589
end
66-
function interpolate_syntax(st::SyntaxTree, @nospecialize(vals...))
90+
function _interpolate_syntax(st::SyntaxTree, @nospecialize(vals::Tuple))
6791
st = copy_ast(ensure_macro_attributes!(SyntaxGraph()), st)
6892
val_i = Ref(0)
69-
out = _interpolate_syntax((@ast st._graph st [K"None" st]), 0, vals, val_i)
93+
out = __interpolate_syntax((@ast st._graph st [K"None" st]), 0, vals, val_i)
7094
@jl_assert val_i[] == length(vals) st
7195
@jl_assert numchildren(out) == 1 st
7296
out[1]
7397
end
98+
function interpolate_syntax(st::SyntaxTree, @nospecialize(vals...))
99+
return invoke_in_lowering_world(_interpolate_syntax, st, vals)
100+
end
74101

75102
#--------------------------------------------------
76103
# Functions called by closure conversion
@@ -246,6 +273,18 @@ function (g::GeneratedFunctionStub)(world::UInt, source::Method, @nospecialize a
246273

247274
# Run code generator - this acts like a macro expander
248275
ex0 = g.gen(sc, args...)
276+
277+
# Note that we expand in `tls_world_age()` (see Core.GeneratedFunctionStub)
278+
world = Base.tls_world_age()
279+
280+
# Lower the generated code in the lowering world
281+
return invoke_in_lowering_world(_lower_generated_code, g, source, graph, sc,
282+
__module__, world, ex0)
283+
end
284+
285+
function _lower_generated_code(g::GeneratedFunctionStub, source::Method, graph,
286+
sc::SyntaxContext, __module__::Module,
287+
world::UInt, @nospecialize(ex0))
249288
if ex0 isa Expr
250289
ex0 = expr_to_est(
251290
graph, ex0, source_location(LineNumberNode, g.srcref))
@@ -261,8 +300,6 @@ function (g::GeneratedFunctionStub)(world::UInt, source::Method, @nospecialize a
261300
end
262301

263302
@jl_assert base_layer(sc).mod == __module__ ex0
264-
# Note that we expand in `tls_world_age()` (see Core.GeneratedFunctionStub)
265-
world = Base.tls_world_age()
266303
ex0 = JuliaSyntax.fill_context!(ex0, sc)
267304
ctx1 = MacroExpansionContext(ex0, world, true)
268305
ex1 = expand_forms_1(ctx1, ex0)
@@ -317,7 +354,7 @@ end
317354
#
318355
# (This should do what fl_defined_julia_global does for flisp lowering)
319356
function is_defined_and_owned_global(mod, name, world::UInt=Base.get_world_counter())
320-
return Base.invoke_in_world(world, Base.binding_kind, mod, name) === Base.PARTITION_KIND_GLOBAL
357+
return _invoke_in_world(world, Base.binding_kind, mod, name) === Base.PARTITION_KIND_GLOBAL
321358
end
322359

323360
# "Reserve" a binding: create the binding if it doesn't exist but do not assign

JuliaLowering/test/macros.jl

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1510,6 +1510,21 @@ code = JuliaLowering.include_string(test_mod, """Mod1.@indirect_MODULE()""")
15101510
end
15111511
end
15121512

1513+
@testset "(AI) old macro attribution survives a nested eval in its body (#32)" begin
1514+
Base.eval(test_mod, :(module MacDefMod
1515+
const secret = 99
1516+
macro getsecret()
1517+
__module__.eval(:(nested_eval_side_effect = 1 + 1))
1518+
return :(secret) # bare name -> resolves in the defining module
1519+
end
1520+
end))
1521+
Core.@latestworld
1522+
# `secret` must resolve in MacDefMod (== mod_for_ast), matching flisp.
1523+
@test JuliaLowering.include_string(test_mod, "MacDefMod.@getsecret()") == 99
1524+
@test test_mod.nested_eval_side_effect == 2
1525+
@test fl_eval(test_mod, :(MacDefMod.@getsecret())) == 99
1526+
end
1527+
15131528
@testset "macros defining macros" begin
15141529
@eval test_mod macro make_and_use_macro_toplevel()
15151530
Expr(:toplevel,

0 commit comments

Comments
 (0)