Skip to content

Commit f163ed8

Browse files
authored
[JuliaLowering] Reconstruct macro name before, not during, macro expansion (#60170)
On top of #60162. This simplification lets macro expansion handle the macro name as an identifier rather than the nonterminal `K"macro_name"` node. That and the old `MacroName` node (as well as `CmdMacroName`, `StrMacroName`) are mainly the result of restrictions on raw parser output: we can't parse `@Base.x` to a non-contiguous `@x`, even though that's what we want in the AST in almost every case.
1 parent 6c75e91 commit f163ed8

File tree

9 files changed

+61
-74
lines changed

9 files changed

+61
-74
lines changed

JuliaLowering/src/ast.jl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,7 @@ end
162162
function newleaf(ctx, srcref, k::Kind, @nospecialize(value))
163163
leaf = makeleaf(ctx, srcref, k)
164164
if k == K"Identifier" || k == K"core" || k == K"top" || k == K"Symbol" ||
165-
k == K"globalref" || k == K"Placeholder" ||
166-
k == K"StrMacroName" || k == K"CmdMacroName"
165+
k == K"globalref" || k == K"Placeholder"
167166
setattr!(leaf._graph, leaf._id, :name_val, value)
168167
elseif k == K"BindingId"
169168
setattr!(leaf._graph, leaf._id, :var_id, value)

JuliaLowering/src/compat.jl

Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -267,14 +267,10 @@ function _insert_convert_expr(@nospecialize(e), graph::SyntaxGraph, src::SourceA
267267
src = child_exprs[2]
268268
end
269269
deleteat!(child_exprs, 2)
270-
if a1 isa Symbol
271-
child_exprs[1] = a1_esc(Expr(:macro_name, a1))
272-
elseif a1 isa Expr && a1.head === :(.)
273-
a12,a12_esc = unwrap_esc(a1.args[2])
274-
if a12 isa QuoteNode
275-
child_exprs[1] = a1_esc(Expr(:(.), a1.args[1],
276-
Expr(:macro_name, a12_esc(a12.value))))
277-
end
270+
if a1 isa Symbol && a1 === Symbol("@__dot__")
271+
child_exprs[1] = Symbol("@.")
272+
elseif a1 isa Expr && nargs === 2 && a1.args[2] === Symbol("@__dot__")
273+
child_exprs[1] = Expr(a1.head, a1.args[1], Symbol("@."))
278274
elseif a1 isa GlobalRef && a1.mod === Core
279275
# Syntax-introduced macrocalls are listed here for reference. We
280276
# probably don't need to convert these.
@@ -412,16 +408,15 @@ function _insert_convert_expr(@nospecialize(e), graph::SyntaxGraph, src::SourceA
412408
# (do (call f args...) (-> (tuple lam_args...) (block ...)))
413409
# SyntaxTree:
414410
# (call f args... (do (tuple lam_args...) (block ...)))
415-
callargs = collect_expr_parameters(e.args[1], 2)
416411
if e.args[1].head === :macrocall
417412
st_k = K"macrocall"
413+
callargs = collect_expr_parameters(e.args[1], 3)
418414
if callargs[2] isa LineNumberNode
419415
src = callargs[2]
420416
end
421417
deleteat!(callargs, 2)
422-
c1,c1_esc = unwrap_esc(callargs[1])
423-
callargs[1] = c1_esc(Expr(:macro_name, c1))
424418
else
419+
callargs = collect_expr_parameters(e.args[1], 2)
425420
st_k = K"call"
426421
end
427422
child_exprs = Any[callargs..., Expr(:do_lambda, e.args[2].args...)]
@@ -538,20 +533,7 @@ function _insert_convert_expr(@nospecialize(e), graph::SyntaxGraph, src::SourceA
538533

539534
#---------------------------------------------------------------------------
540535
# Possibly-temporary heads introduced by us converting the parent expr
541-
if e.head === :macro_name
542-
@assert nargs === 1
543-
# Trim `@` for a correct SyntaxTree, although we need to add it back
544-
# later for finding the macro
545-
if e.args[1] === :(.)
546-
mac_name = string(e.args[1][2])
547-
mac_name = mac_name == "@__dot__" ? "." : mac_name[2:end]
548-
child_exprs[1] = Expr(:(.), e.args[1][1], Symbol(mac_name))
549-
else
550-
mac_name = string(e.args[1])
551-
mac_name = mac_name == "@__dot__" ? "." : mac_name[2:end]
552-
child_exprs[1] = Symbol(mac_name)
553-
end
554-
elseif e.head === :catch_var_placeholder
536+
if e.head === :catch_var_placeholder
555537
st_k = K"Placeholder"
556538
st_attrs[:name_val] = ""
557539
child_exprs = nothing

JuliaLowering/src/macro_expansion.jl

Lines changed: 2 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -138,27 +138,6 @@ function Base.showerror(io::IO, exc::MacroExpansionError)
138138
end
139139
end
140140

141-
function fixup_macro_name(ctx::MacroExpansionContext, ex::SyntaxTree)
142-
k = kind(ex)
143-
if k == K"StrMacroName" || k == K"CmdMacroName"
144-
layerid = get(ex, :scope_layer, current_layer_id(ctx))
145-
newname = JuliaSyntax.lower_identifier_name(ex.name_val, k)
146-
makeleaf(ctx, ex, ex, [:kind=>K"Identifier", :scope_layer=>layerid,
147-
:name_val=>newname])
148-
elseif k == K"macro_name"
149-
@chk numchildren(ex) === 1
150-
if kind(ex[1]) === K"."
151-
@ast ctx ex [K"." ex[1][1] [K"macro_name" ex[1][2]]]
152-
else
153-
layerid = get(ex, :scope_layer, current_layer_id(ctx))
154-
newname = JuliaSyntax.lower_identifier_name(ex[1].name_val, K"macro_name")
155-
makeleaf(ctx, ex[1], ex[1], [:kind=>kind(ex[1]), :name_val=>newname])
156-
end
157-
else
158-
mapchildren(e->fixup_macro_name(ctx,e), ctx, ex)
159-
end
160-
end
161-
162141
function _eval_dot(world::UInt, mod, ex::SyntaxTree)
163142
if kind(ex) === K"."
164143
mod = _eval_dot(world, mod, ex[1])
@@ -175,7 +154,7 @@ end
175154
# isn't clear the language is meant to support this).
176155
function eval_macro_name(ctx::MacroExpansionContext, mctx::MacroContext, ex0::SyntaxTree)
177156
mod = current_layer(ctx).mod
178-
ex = fixup_macro_name(ctx, expand_forms_1(ctx, ex0))
157+
ex = expand_forms_1(ctx, ex0)
179158
try
180159
if kind(ex) === K"Value"
181160
!(ex.value isa GlobalRef) ? ex.value :
@@ -433,10 +412,6 @@ function expand_forms_1(ctx::MacroExpansionContext, ex::SyntaxTree)
433412
scope_layer = get(ex, :scope_layer, current_layer_id(ctx))
434413
makeleaf(ctx, ex, ex, [:kind=>k, :scope_layer=>scope_layer])
435414
end
436-
elseif k == K"StrMacroName" || k == K"CmdMacroName" || k == K"macro_name"
437-
# These can appear outside of a macrocall, e.g. in `import`
438-
e2 = fixup_macro_name(ctx, ex)
439-
expand_forms_1(ctx, e2)
440415
elseif k == K"var" || k == K"char" || k == K"parens"
441416
# Strip "container" nodes
442417
@chk numchildren(ex) == 1
@@ -512,7 +487,7 @@ function expand_forms_1(ctx::MacroExpansionContext, ex::SyntaxTree)
512487
@ast ctx ex [K"." expand_forms_1(ctx, ex[1]) e2]
513488
elseif k == K"cmdstring"
514489
@chk numchildren(ex) == 1
515-
e2 = @ast ctx ex [K"macrocall" [K"macro_name" "cmd"::K"core"] ex[1]]
490+
e2 = @ast ctx ex [K"macrocall" "@cmd"::K"core" ex[1]]
516491
expand_macro(ctx, e2)
517492
elseif (k == K"call" || k == K"dotcall")
518493
# Do some initial desugaring of call and dotcall here to simplify

JuliaLowering/src/syntax_graph.jl

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# TODO: This whole file should probably be moved to JuliaSyntax.
22
import .JuliaSyntax: ParseStream, RedTreeCursor, reverse_toplevel_siblings,
33
has_toplevel_siblings, _unsafe_wrap_substring, parse_julia_literal, is_trivia,
4-
is_prefix_op_call, @isexpr, SyntaxHead, COLON_QUOTE, is_syntactic_operator
4+
is_prefix_op_call, @isexpr, SyntaxHead, COLON_QUOTE, is_syntactic_operator,
5+
lower_identifier_name
56

67
const NodeId = Int
78

@@ -417,7 +418,7 @@ attrsummary(name, value::Number) = "$name=$value"
417418

418419
function _value_string(ex)
419420
k = kind(ex)
420-
str = k in KSet"Identifier StrMacroName CmdMacroName" || is_operator(k) ? ex.name_val :
421+
str = k == K"Identifier" || is_operator(k) ? ex.name_val :
421422
k == K"Placeholder" ? ex.name_val :
422423
k == K"SSAValue" ? "%" :
423424
k == K"BindingId" ? "#" :
@@ -584,17 +585,11 @@ function _find_SyntaxTree_macro(ex, line)
584585
# We're in the line range. Either
585586
if firstline == line && kind(c) == K"macrocall" && begin
586587
name = c[1]
587-
if kind(name) == K"macro_name"
588-
name = name[1]
589-
end
590588
if kind(name) == K"."
591589
name = name[2]
592-
if kind(name) == K"macro_name"
593-
name = name[1]
594-
end
595590
end
596591
@assert kind(name) == K"Identifier"
597-
name.name_val == "SyntaxTree"
592+
name.name_val == "@SyntaxTree"
598593
end
599594
# We find the node we're looking for. NB: Currently assuming a max
600595
# of one @SyntaxTree invocation per line. Though we could relax
@@ -891,6 +886,24 @@ function _green_to_ast(parent::Kind, ex::SyntaxTree; eq_to_kw=false)
891886
elseif k === K"=" && eq_to_kw
892887
setattr!(makenode(graph, ex, ex, _map_green_to_ast(k, children(ex))),
893888
:kind, K"kw")
889+
elseif k === K"CmdMacroName" || k === K"StrMacroName"
890+
name = lower_identifier_name(ex.name_val, k)
891+
setattr!(makeleaf(graph, ex, K"Identifier"),
892+
:name_val, name)
893+
elseif k === K"macro_name"
894+
# M.@x parses to (. M (macro_name x))
895+
# @M.x parses to (macro_name (. M x))
896+
# We want (. M @x) (both identifiers) in either case
897+
@assert numchildren(ex) === 2 && kind(ex[1]) === K"@"
898+
id = ex[2]
899+
mname_raw = (kind(id) === K"." ? id[2] : id).name_val
900+
mac_id = setattr!(makeleaf(graph, ex, K"Identifier"), :name_val,
901+
lower_identifier_name(mname_raw, K"macro_name"))
902+
if kind(id) === K"."
903+
makenode(graph, ex, ex, NodeId[id[1]._id, mac_id._id])
904+
else
905+
mac_id
906+
end
894907
elseif is_leaf(ex)
895908
return ex
896909
else

JuliaLowering/test/compat.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -494,7 +494,7 @@ const JL = JuliaLowering
494494
# `@mac x` with macro name escaped
495495
@test JuliaLowering.expr_to_syntaxtree(Expr(:macrocall, esc(Symbol("@mac")), nothing, :x))
496496
@ast_ [K"macrocall"
497-
[K"escape" [K"macro_name" "mac"::K"Identifier"]]
497+
[K"escape" "@mac"::K"Identifier"]
498498
"x"::K"Identifier"
499499
]
500500

@@ -505,7 +505,7 @@ const JL = JuliaLowering
505505
[K"escape"
506506
[K"."
507507
"A"::K"Identifier"
508-
[K"macro_name" "mac"::K"Identifier"]
508+
"@mac"::K"Identifier"
509509
]
510510
]
511511
"x"::K"Identifier"
@@ -577,7 +577,7 @@ const JL = JuliaLowering
577577
Expr(:macrocall, Expr(:var"hygienic-scope", Symbol("@mac"), :other, :args), nothing, :x))
578578
@ast_ [K"macrocall"
579579
[K"hygienic_scope"
580-
[K"macro_name" "mac"::K"Identifier"]
580+
"@mac"::K"Identifier"
581581
"other"::K"Identifier" # (<- normally a Module)
582582
"args"::K"Identifier" # (<- normally a LineNumberNode)
583583
]
@@ -587,7 +587,7 @@ const JL = JuliaLowering
587587
# One example of double escaping
588588
@test JuliaLowering.expr_to_syntaxtree(Expr(:macrocall, esc(esc(Symbol("@mac"))), nothing, :x))
589589
@ast_ [K"macrocall"
590-
[K"escape" [K"escape" [K"macro_name" "mac"::K"Identifier"]]]
590+
[K"escape" [K"escape" "@mac"::K"Identifier"]]
591591
"x"::K"Identifier"
592592
]
593593

@@ -600,7 +600,7 @@ const JL = JuliaLowering
600600
@ast_ [K"macrocall"
601601
[K"hygienic_scope"
602602
[K"escape"
603-
[K"macro_name" "mac"::K"Identifier"]
603+
"@mac"::K"Identifier"
604604
]
605605
"other"::K"Identifier" # (<- normally a Module)
606606
"args"::K"Identifier" # (<- normally a LineNumberNode)

JuliaLowering/test/macros.jl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,14 @@ end
347347
GC.@preserve x unsafe_load(p)
348348
end""") === 101 # Expr(:gc_preserve)
349349

350+
# JuliaLowering.jl/issues/121
351+
@test JuliaLowering.include_string(test_mod, """
352+
GC.@preserve @static if true @__MODULE__ else end
353+
""") isa Module
354+
@test JuliaLowering.include_string(test_mod, """
355+
GC.@preserve @static if true v"1.14" else end
356+
""") isa VersionNumber
357+
350358
# only invokelatest produces :isglobal now, so MWE here
351359
Base.eval(test_mod, :(macro isglobal(x); esc(Expr(:isglobal, x)); end))
352360
@test JuliaLowering.include_string(test_mod, """

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.

JuliaLowering/test/utils.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,8 @@ end
167167
function format_ir_for_test(mod, case)
168168
ex = parsestmt(SyntaxTree, case.input)
169169
try
170-
if kind(ex) == K"macrocall" && kind(ex[1]) == K"macro_name" && ex[1][1].name_val == "ast_"
170+
if (kind(ex) == K"macrocall" && kind(ex[1]) == K"Identifier" &&
171+
ex[1].name_val == "@ast_")
171172
# Total hack, until @ast_ can be implemented in terms of new-style
172173
# macros.
173174
ex = Base.eval(mod, Expr(ex))

test/JuliaLowering_stdlibs.jl

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
11
import Libdl
22

3-
### 38 / 52 (non-sysimage) stdlibs precompile successfully with JuliaLowering ###
3+
# known precompilation failures under JL
44
const INCOMPATIBLE_STDLIBS = String[
5-
"Statistics", "JuliaSyntaxHighlighting", "Markdown", "LibGit2",
6-
"InteractiveUtils", "Test", "REPL", "Pkg", "LazyArtifacts", "SparseArrays",
7-
"TOML", "StyledStrings", "Profile", "SuiteSparse",
5+
"InteractiveUtils"
6+
"LazyArtifacts"
7+
"LibGit2"
8+
"Pkg"
9+
"REPL"
10+
"REPLExt"
11+
"SparseArrays"
12+
"SparseArraysExt"
13+
"Statistics"
14+
"SuiteSparse"
15+
"TOML"
16+
"Test"
817
]
918

1019
const JULIA_EXECUTABLE = Base.unsafe_string(Base.JLOptions().julia_bin)

0 commit comments

Comments
 (0)