Skip to content

Commit 99f623d

Browse files
mlechuc42f
andauthored
Random lowering bugfix batch 2 (#45)
* fix showing MacroExpansionError when sourceref isa LineNumberNode * Support K"gc_preserve" (Note that this lowering may change in JuliaLang/julia#59129) * Support `Expr(:isglobal)` * Support K"cmdstring" (just convert it to a macrocall) * Delete two-arg dot form check (The number of forms isn't bounded) * Don't fail on `Expr(:inbounds)` * List known meta forms + improve compat.jl errors * Turn off flisp fallback for now --------- Co-authored-by: Claire Foster <[email protected]>
1 parent 1278421 commit 99f623d

File tree

8 files changed

+112
-21
lines changed

8 files changed

+112
-21
lines changed

src/compat.jl

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ function _insert_convert_expr(@nospecialize(e), graph::SyntaxGraph, src::SourceA
243243
elseif a1 isa Function
244244
# pass
245245
else
246-
error("Unknown macrocall form $(sprint(dump, e))")
246+
error("Unknown macrocall form at $src: $(sprint(dump, e))")
247247
@assert false
248248
end
249249
elseif e.head === Symbol("'")
@@ -258,9 +258,6 @@ function _insert_convert_expr(@nospecialize(e), graph::SyntaxGraph, src::SourceA
258258
child_exprs = pushfirst!(tuple_exprs, e.args[1])
259259
elseif a2 isa QuoteNode && a2.value isa Symbol
260260
child_exprs[2] = a2.value
261-
elseif a2 isa Expr && a2.head === :MacroName
262-
else
263-
@error "Unknown 2-arg dot form" e
264261
end
265262
elseif e.head === :for
266263
@assert nargs === 2
@@ -420,8 +417,15 @@ function _insert_convert_expr(@nospecialize(e), graph::SyntaxGraph, src::SourceA
420417
setmeta!(SyntaxTree(graph, st_id); nospecialize=true)
421418
return st_id, src
422419
end
420+
elseif e.args[1] in (:inline, :noinline, :generated, :generated_only,
421+
:max_methods, :optlevel, :toplevel, :push_loc, :pop_loc,
422+
:aggressive_constprop, :specialize, :compile, :infer,
423+
:nospecializeinfer)
424+
# TODO: Some need to be handled in lowering
425+
child_exprs[1] = Expr(:quoted_symbol, e.args[1])
423426
else
424-
@assert nargs === 1
427+
# Can't throw a hard error; it is explicitly tested that meta can take arbitrary keys.
428+
@error("Unknown meta form at $src: `$e`\n$(sprint(dump, e))")
425429
child_exprs[1] = Expr(:quoted_symbol, e.args[1])
426430
end
427431
elseif e.head === :scope_layer
@@ -436,11 +440,16 @@ function _insert_convert_expr(@nospecialize(e), graph::SyntaxGraph, src::SourceA
436440
st_k = e.head === :symbolicgoto ? K"symbolic_label" : K"symbolic_goto"
437441
st_attrs[:name_val] = string(e.args[1])
438442
child_exprs = nothing
439-
elseif e.head === :inline || e.head === :noinline
443+
elseif e.head in (:inline, :noinline)
440444
@assert nargs === 1 && e.args[1] isa Bool
441445
# TODO: JuliaLowering doesn't accept this (non-:meta) form yet
442446
st_k = K"TOMBSTONE"
443447
child_exprs = nothing
448+
elseif e.head === :inbounds
449+
@assert nargs === 1 && typeof(e.args[1]) in (Symbol, Bool)
450+
# TODO: JuliaLowering doesn't accept this form yet
451+
st_k = K"TOMBSTONE"
452+
child_exprs = nothing
444453
elseif e.head === :core
445454
@assert nargs === 1
446455
@assert e.args[1] isa Symbol
@@ -484,7 +493,7 @@ function _insert_convert_expr(@nospecialize(e), graph::SyntaxGraph, src::SourceA
484493
# Throw if this script isn't complete. Finally, insert a new node into the
485494
# graph and recurse on child_exprs
486495
if st_k === K"None"
487-
error("Unknown expr head `$(e.head)`\n$(sprint(dump, e))")
496+
error("Unknown expr head at $src: `$(e.head)`\n$(sprint(dump, e))")
488497
end
489498

490499
st_id = _insert_tree_node(graph, st_k, src, st_flags; st_attrs...)

src/desugaring.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4544,6 +4544,15 @@ function expand_forms_2(ctx::DesugaringContext, ex::SyntaxTree, docs=nothing)
45444544
]
45454545
elseif k == K"inert"
45464546
ex
4547+
elseif k == K"gc_preserve"
4548+
s = ssavar(ctx, ex)
4549+
r = ssavar(ctx, ex)
4550+
@ast ctx ex [K"block"
4551+
s := [K"gc_preserve_begin" children(ex)[2:end]...]
4552+
r := expand_forms_2(ctx, children(ex)[1])
4553+
[K"gc_preserve_end" s]
4554+
r
4555+
]
45474556
elseif k == K"&"
45484557
throw(LoweringError(ex, "invalid syntax"))
45494558
elseif k == K"$"

src/hooks.jl

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,14 @@ function core_lowering_hook(@nospecialize(code), mod::Module,
2525
ex = to_lowered_expr(mod, st5)
2626
return Core.svec(ex, st5, ctx5)
2727
catch exc
28-
@error("JuliaLowering failed — falling back to flisp!",
29-
exception=(exc,catch_backtrace()),
30-
code=code, file=file, line=line, mod=mod)
31-
return Base.fl_lower(code, mod, file, line, world, warn)
28+
@info("JuliaLowering threw given input:", code=code, st0=st0, file=file, line=line, mod=mod)
29+
rethrow(exc)
30+
31+
# TODO: Re-enable flisp fallback once we're done collecting errors
32+
# @error("JuliaLowering failed — falling back to flisp!",
33+
# exception=(exc,catch_backtrace()),
34+
# code=code, file=file, line=line, mod=mod)
35+
# return Base.fl_lower(code, mod, file, line, world, warn)
3236
end
3337
end
3438

src/kinds.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ function _register_kinds()
1111
# Flag for @generated parts of a functon
1212
"generated"
1313
# Temporary rooting of identifiers (GC.@preserve)
14+
"gc_preserve"
1415
"gc_preserve_begin"
1516
"gc_preserve_end"
1617
# A literal Julia value of any kind, as might be inserted into the
@@ -61,7 +62,7 @@ function _register_kinds()
6162
"static_eval"
6263
# Catch-all for additional syntax extensions without the need to
6364
# extend `Kind`. Known extensions include:
64-
# locals, islocal
65+
# locals, islocal, isglobal
6566
# The content of an assertion is not considered to be quoted, so
6667
# use K"Symbol" or K"inert" inside where necessary.
6768
"extension"

src/macro_expansion.jl

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -118,14 +118,18 @@ function Base.showerror(io::IO, exc::MacroExpansionError)
118118
# * How to deal with highlighting trivia? Could provide a token kind or
119119
# child position within the raw tree? How to abstract this??
120120
src = sourceref(exc.ex)
121-
fb = first_byte(src)
122-
lb = last_byte(src)
123-
pos = exc.position
124-
byterange = pos == :all ? (fb:lb) :
125-
pos == :begin ? (fb:fb-1) :
126-
pos == :end ? (lb+1:lb) :
127-
error("Unknown position $pos")
128-
highlight(io, src.file, byterange, note=exc.msg)
121+
if src isa LineNumberNode
122+
highlight(io, src, note=exc.msg)
123+
else
124+
fb = first_byte(src)
125+
lb = last_byte(src)
126+
pos = exc.position
127+
byterange = pos == :all ? (fb:lb) :
128+
pos == :begin ? (fb:fb-1) :
129+
pos == :end ? (lb+1:lb) :
130+
error("Unknown position $pos")
131+
highlight(io, src.file, byterange, note=exc.msg)
132+
end
129133
if !isnothing(exc.err)
130134
print(io, "\nCaused by:\n")
131135
showerror(io, exc.err)
@@ -238,7 +242,10 @@ function expand_macro(ctx, ex)
238242
else
239243
# Compat: attempt to invoke an old-style macro if there's no applicable
240244
# method for new-style macro arguments.
241-
macro_loc = source_location(LineNumberNode, ex)
245+
macro_loc = let loc = source_location(LineNumberNode, ex)
246+
# Some macros, e.g. @cmd, don't play nicely with file == nothing
247+
isnothing(loc.file) ? LineNumberNode(loc.line, :none) : loc
248+
end
242249
macro_args = Any[macro_loc, current_layer(ctx).mod]
243250
for arg in raw_args
244251
# For hygiene in old-style macros, we omit any additional scope
@@ -388,6 +395,10 @@ function expand_forms_1(ctx::MacroExpansionContext, ex::SyntaxTree)
388395
e2 = @ast ctx e2 e2=>K"Symbol"
389396
end
390397
@ast ctx ex [K"." expand_forms_1(ctx, ex[1]) e2]
398+
elseif k == K"cmdstring"
399+
@chk numchildren(ex) == 1
400+
e2 = @ast ctx ex [K"macrocall" "@cmd"::K"core" ex[1]]
401+
expand_macro(ctx, e2)
391402
elseif (k == K"call" || k == K"dotcall")
392403
# Do some initial desugaring of call and dotcall here to simplify
393404
# the later desugaring pass

src/scope_analysis.jl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,16 @@ function _resolve_scopes(ctx, ex::SyntaxTree)
494494
id = lookup_var(ctx, NameKey(ex[2]))
495495
islocal = !isnothing(id) && var_kind(ctx, id) != :global
496496
@ast ctx ex islocal::K"Bool"
497+
elseif etype == "isglobal"
498+
e2 = ex[2]
499+
@chk kind(e2) in KSet"Identifier Placeholder"
500+
isglobal = if kind(e2) == K"Identifier"
501+
id = lookup_var(ctx, NameKey(e2))
502+
isnothing(id) || var_kind(ctx, id) == :global
503+
else
504+
false
505+
end
506+
@ast ctx ex isglobal::K"Bool"
497507
elseif etype == "locals"
498508
stmts = SyntaxList(ctx)
499509
locals_dict = ssavar(ctx, ex, "locals_dict")

test/macros.jl

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,13 @@ MacroExpansionError while expanding @oldstyle_error in module Main.macros.test_m
239239
Caused by:
240240
Some error in old style macro"""
241241

242+
@test sprint(
243+
showerror,
244+
JuliaLowering.MacroExpansionError(
245+
JuliaLowering.expr_to_syntaxtree(:(foo), LineNumberNode(1)),
246+
"fake error")) ==
247+
"MacroExpansionError:\n#= line 1 =# - fake error"
248+
242249
# Old-style macros returning non-Expr values
243250
Base.eval(test_mod, :(
244251
macro oldstyle_non_Expr()
@@ -293,4 +300,33 @@ end
293300
MethodError: no method matching var"@sig_mismatch"(::JuliaLowering.MacroContext, ::JuliaLowering.SyntaxTree""")
294301
end
295302

303+
@testset "old macros producing exotic expr heads" begin
304+
@test JuliaLowering.include_string(test_mod, """
305+
let # example from @preserve docstring
306+
x = Ref{Int}(101)
307+
p = Base.unsafe_convert(Ptr{Int}, x)
308+
GC.@preserve x unsafe_load(p)
309+
end""") === 101 # Expr(:gc_preserve)
310+
311+
# only invokelatest produces :isglobal now, so MWE here
312+
Base.eval(test_mod, :(macro isglobal(x); esc(Expr(:isglobal, x)); end))
313+
@test JuliaLowering.include_string(test_mod, """
314+
some_global = 1
315+
function isglobal_chk(some_arg)
316+
local some_local = 1
317+
(@isglobal(some_undefined), @isglobal(some_global), @isglobal(some_arg), @isglobal(some_local))
318+
end
319+
isglobal_chk(1)
320+
""") === (true, true, false, false)
321+
# with K"Placeholder"s
322+
@test JuliaLowering.include_string(test_mod, """
323+
__ = 1
324+
function isglobal_chk(___)
325+
local ____ = 1
326+
(@isglobal(_), @isglobal(__), @isglobal(___), @isglobal(____))
327+
end
328+
isglobal_chk(1)
329+
""") === (false, false, false, false)
330+
end
331+
296332
end # module macros

test/macros_ir.jl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,3 +136,14 @@ Caused by:
136136
UndefVarError: `@m_not_exist` not defined in `Main.TestMod`
137137
Suggestion: check for spelling errors or missing imports.
138138

139+
########################################
140+
# Simple cmdstring
141+
`echo 1`
142+
#---------------------
143+
1 Base.cmd_gen
144+
2 (call core.tuple "echo")
145+
3 (call core.tuple "1")
146+
4 (call core.tuple %%₃)
147+
5 (call %%₄)
148+
6 (return %₅)
149+

0 commit comments

Comments
 (0)