Skip to content

Commit 84f90da

Browse files
committed
[JuliaLowering] get macro name in ctx.world; fix lowering iterator
1 parent 625e8c7 commit 84f90da

File tree

4 files changed

+75
-24
lines changed

4 files changed

+75
-24
lines changed

JuliaLowering/src/eval.jl

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ function lower_init(ex::SyntaxTree, mod::Module, macro_world::UInt; expr_compat_
4444
LoweringIterator{typeof(graph)}(ctx, [(ex, false, 0)])
4545
end
4646

47-
function lower_step(iter, push_mod=nothing)
47+
function lower_step(iter, world=Base.get_world_counter(), push_mod=nothing)
4848
if !isnothing(push_mod)
4949
push_layer!(iter.ctx, push_mod, false)
5050
end
@@ -71,7 +71,12 @@ function lower_step(iter, push_mod=nothing)
7171

7272
k = kind(ex)
7373
if !(k in KSet"toplevel module")
74-
ex = expand_forms_1(iter.ctx, ex)
74+
ctx1 = let c = iter.ctx
75+
MacroExpansionContext(
76+
c.graph, c.bindings, c.scope_layers, c.scope_layer_stack,
77+
c.expr_compat_mode, world)
78+
end
79+
ex = expand_forms_1(ctx1, ex)
7580
k = kind(ex)
7681
end
7782
if k == K"toplevel"
@@ -93,7 +98,7 @@ function lower_step(iter, push_mod=nothing)
9398
return Core.svec(:begin_module, newmod_name, std_defs, loc)
9499
else
95100
# Non macro expansion parts of lowering
96-
ctx2, ex2 = expand_forms_2(iter.ctx, ex)
101+
ctx2, ex2 = expand_forms_2(ctx1, ex)
97102
ctx3, ex3 = resolve_scopes(ctx2, ex2)
98103
ctx4, ex4 = convert_closures(ctx3, ex3)
99104
ctx5, ex5 = linearize_ir(ctx4, ex4)
@@ -378,7 +383,7 @@ function _to_lowered_expr(ex::SyntaxTree, stmt_offset::Int)
378383
ir
379384
end
380385
elseif k == K"Value"
381-
ex.value
386+
ex.value isa LineNumberNode ? QuoteNode(ex.value) : ex.value
382387
elseif k == K"goto"
383388
Core.GotoNode(ex[1].id + stmt_offset)
384389
elseif k == K"gotoifnot"
@@ -473,7 +478,7 @@ function _eval(mod, iter)
473478
new_mod = nothing
474479
result = nothing
475480
while true
476-
thunk = lower_step(iter, new_mod)::Core.SimpleVector
481+
thunk = lower_step(iter, Base.get_world_counter(), new_mod)::Core.SimpleVector
477482
new_mod = nothing
478483
type = thunk[1]::Symbol
479484
if type == :done
@@ -503,7 +508,7 @@ function _eval(mod, iter, new_mod=nothing)
503508
in_new_mod = !isnothing(new_mod)
504509
result = nothing
505510
while true
506-
thunk = lower_step(iter, new_mod)::Core.SimpleVector
511+
thunk = lower_step(iter, Base.get_world_counter(), new_mod)::Core.SimpleVector
507512
new_mod = nothing
508513
type = thunk[1]::Symbol
509514
if type == :done

JuliaLowering/src/macro_expansion.jl

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -158,25 +158,49 @@ function fixup_macro_name(ctx::MacroExpansionContext, ex::SyntaxTree)
158158
end
159159
end
160160

161-
function eval_macro_name(ctx::MacroExpansionContext, mctx::MacroContext, ex::SyntaxTree)
162-
# `ex1` might contain a nontrivial mix of scope layers so we can't just
163-
# `eval()` it, as it's already been partially lowered by this point.
164-
# Instead, we repeat the latter parts of `lower()` here.
165-
ex1 = expand_forms_1(ctx, fixup_macro_name(ctx, ex))
166-
ctx2, ex2 = expand_forms_2(ctx, ex1)
167-
ctx3, ex3 = resolve_scopes(ctx2, ex2)
168-
ctx4, ex4 = convert_closures(ctx3, ex3)
169-
ctx5, ex5 = linearize_ir(ctx4, ex4)
161+
function _eval_dot(world::UInt, mod, ex::SyntaxTree)
162+
if kind(ex) === K"."
163+
mod = _eval_dot(world, mod, ex[1])
164+
ex = ex[2]
165+
end
166+
kind(ex) in KSet"Identifier Symbol" && mod isa Module ?
167+
Base.invoke_in_world(world, getproperty, mod, Symbol(ex.name_val)) :
168+
nothing
169+
end
170+
171+
# If macroexpand(ex[1]) is an identifier or dot-expression, we can simply grab
172+
# it from the scope layer's module in ctx.macro_world. Otherwise, we need to
173+
# eval arbitrary code (which, TODO: does not use the correct world age, and it
174+
# isn't clear the language is meant to support this).
175+
function eval_macro_name(ctx::MacroExpansionContext, mctx::MacroContext, ex0::SyntaxTree)
170176
mod = current_layer(ctx).mod
171-
expr_form = to_lowered_expr(ex5)
177+
ex = fixup_macro_name(ctx, expand_forms_1(ctx, ex0))
172178
try
173-
# Using Core.eval here fails when precompiling packages since we hit the
174-
# user-facing error (in `jl_check_top_level_effect`) that warns that
175-
# effects won't persist when eval-ing into a closed module.
176-
# `jl_invoke_julia_macro` bypasses this by calling `jl_toplevel_eval` on
177-
# the macro name. This is fine assuming the first argument to the
178-
# macrocall is effect-free.
179-
ccall(:jl_toplevel_eval, Any, (Any, Any), mod, expr_form)
179+
if kind(ex) === K"Value"
180+
!(ex.value isa GlobalRef) ? ex.value :
181+
Base.invoke_in_world(ctx.macro_world, getglobal,
182+
ex.value.mod, ex.value.name)
183+
elseif kind(ex) === K"Identifier"
184+
layer = get(ex, :scope_layer, nothing)
185+
if !isnothing(layer)
186+
mod = ctx.scope_layers[layer].mod
187+
end
188+
Base.invoke_in_world(ctx.macro_world, getproperty,
189+
mod, Symbol(ex.name_val))
190+
elseif kind(ex) === K"." &&
191+
(ed = _eval_dot(ctx.macro_world, mod, ex); !isnothing(ed))
192+
ed
193+
else
194+
# `ex` might contain a nontrivial mix of scope layers so we can't
195+
# just `eval()` it, as it's already been partially lowered by this
196+
# point. Instead, we repeat the latter parts of `lower()` here.
197+
ctx2, ex2 = expand_forms_2(ctx, ex)
198+
ctx3, ex3 = resolve_scopes(ctx2, ex2)
199+
ctx4, ex4 = convert_closures(ctx3, ex3)
200+
ctx5, ex5 = linearize_ir(ctx4, ex4)
201+
expr_form = to_lowered_expr(ex5)
202+
ccall(:jl_toplevel_eval, Any, (Any, Any), mod, expr_form)
203+
end
180204
catch err
181205
throw(MacroExpansionError(mctx, ex, "Macro not found", :all, err))
182206
end

JuliaLowering/test/macros.jl

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,4 +483,26 @@ end
483483
@test_broken JuliaLowering.eval(test_mod, code) == ("outer x", "inner x")
484484
end
485485

486+
# the lowering/eval iterator needs to expand in the correct world age (currently
487+
# the only way to hit this from user code is macros producing toplevel)
488+
@testset "macros defining macros" begin
489+
@eval test_mod macro make_and_use_macro_toplevel()
490+
Expr(:toplevel,
491+
esc(:(macro from_toplevel_expansion()
492+
:(123)
493+
end)),
494+
esc(:(@from_toplevel_expansion())))
495+
end
496+
497+
@test JuliaLowering.include_string(
498+
test_mod, "@make_and_use_macro_toplevel()"; expr_compat_mode=true) === 123
499+
500+
if isdefined(test_mod, Symbol("@from_toplevel_expansion"))
501+
Base.delete_binding(test_mod, Symbol("@from_toplevel_expansion"))
502+
end
503+
504+
@test JuliaLowering.include_string(
505+
test_mod, "@make_and_use_macro_toplevel()"; expr_compat_mode=false) === 123
506+
end
507+
486508
end

JuliaLowering/test/macros_ir.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ _never_exist = @m_not_exist 42
147147
#---------------------
148148
MacroExpansionError while expanding @m_not_exist in module Main.TestMod:
149149
_never_exist = @m_not_exist 42
150-
# └──────────┘ ── Macro not found
150+
# ─────────┘ ── Macro not found
151151
Caused by:
152152
UndefVarError: `@m_not_exist` not defined in `Main.TestMod`
153153
Suggestion: check for spelling errors or missing imports.

0 commit comments

Comments
 (0)