Skip to content

Commit 1be9f2a

Browse files
timholytkelman
authored andcommitted
Check more than the first expression for :meta
This fixes the primary problem in JuliaLang#11595, but is not enough on its own to recapture full performance. Interestingly, this does not appear to cause a measurable slowing of the compilation of base.
1 parent 8d68131 commit 1be9f2a

File tree

3 files changed

+68
-32
lines changed

3 files changed

+68
-32
lines changed

base/expr.jl

Lines changed: 43 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -119,45 +119,58 @@ function pushmeta!(ex::Expr, sym::Symbol, args::Any...)
119119
else
120120
tag = Expr(sym, args...)
121121
end
122-
123-
if ex.head == :function
122+
found, metaex = findmeta(ex)
123+
if found
124+
push!(metaex.args, tag)
125+
else
124126
body::Expr = ex.args[2]
125-
if !isempty(body.args) && isa(body.args[1], Expr) && (body.args[1]::Expr).head == :meta
126-
push!((body.args[1]::Expr).args, tag)
127-
elseif isempty(body.args)
128-
push!(body.args, Expr(:meta, tag))
129-
push!(body.args, nothing)
130-
else
131-
unshift!(body.args, Expr(:meta, tag))
132-
end
133-
elseif (ex.head == :(=) && typeof(ex.args[1]) == Expr && ex.args[1].head == :call)
134-
ex = Expr(:function, ex.args[1], Expr(:block, Expr(:meta, tag), ex.args[2]))
135-
# else
136-
# ex = Expr(:withmeta, ex, sym)
127+
unshift!(body.args, Expr(:meta, tag))
137128
end
138129
ex
139130
end
140131

141132
function popmeta!(body::Expr, sym::Symbol)
142-
if isa(body.args[1],Expr) && (body.args[1]::Expr).head === :meta
143-
metaargs = (body.args[1]::Expr).args
144-
for i = 1:length(metaargs)
145-
if (isa(metaargs[i], Symbol) && metaargs[i] == sym) ||
146-
(isa(metaargs[i], Expr) && metaargs[i].head == sym)
147-
if length(metaargs) == 1
148-
shift!(body.args) # get rid of :meta Expr
149-
else
150-
deleteat!(metaargs, i) # delete this portion of the metadata
151-
end
133+
body.head == :block || return false, []
134+
found, metaex = findmeta_block(body)
135+
if !found
136+
return false, []
137+
end
138+
metaargs = metaex.args
139+
for i = 1:length(metaargs)
140+
if isa(metaargs[i], Symbol) && (metaargs[i]::Symbol) == sym
141+
deleteat!(metaargs, i)
142+
return true, []
143+
elseif isa(metaargs[i], Expr) && (metaargs[i]::Expr).head == sym
144+
ret = (metaargs[i]::Expr).args
145+
deleteat!(metaargs, i)
146+
return true, ret
147+
end
148+
end
149+
false, []
150+
end
151+
popmeta!(arg, sym) = (false, [])
152152

153-
if isa(metaargs[i], Symbol)
154-
return (true, [])
155-
elseif isa(metaargs[i], Expr)
156-
return (true, metaargs[i].args)
153+
function findmeta(ex::Expr)
154+
if ex.head == :function || (ex.head == :(=) && typeof(ex.args[1]) == Expr && ex.args[1].head == :call)
155+
body::Expr = ex.args[2]
156+
body.head == :block || error(body, " is not a block expression")
157+
return findmeta_block(ex)
158+
end
159+
error(ex, " is not a function expression")
160+
end
161+
162+
function findmeta_block(ex::Expr)
163+
for a in ex.args
164+
if isa(a, Expr)
165+
if (a::Expr).head == :meta
166+
return true, a::Expr
167+
elseif (a::Expr).head == :block
168+
found, exb = findmeta_block(a)
169+
if found
170+
return found, exb
157171
end
158172
end
159173
end
160174
end
161-
return (false, [])
175+
return false, Expr(:block)
162176
end
163-
popmeta!(arg, sym) = (false, [])

doc/devdocs/meta.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ that a given block of code has special properties: you might always
1010
want to inline it, or you might want to turn on special compiler
1111
optimization passes. Starting with version 0.4, julia has a
1212
convention that these instructions can be placed inside a ``:meta``
13-
expression, which must be the first expression in the body of a
14-
function.
13+
expression, which is typically (but not necessarily) the first
14+
expression in the body of a function.
1515

1616
``:meta`` expressions are created with macros. As an example, consider
1717
the implementation of the ``@inline`` macro::

test/meta.jl

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,29 @@ body.args = ast.args[3].args
6666
@test popmeta!(body, :test) == (true, [42])
6767
@test popmeta!(body, :nonexistent) == (false, [])
6868

69+
metaex = Expr(:meta, :foo)
70+
ex1 = quote
71+
$metaex
72+
x*x+1
73+
end
74+
metaex = Expr(:meta, :foo)
75+
ex2 = quote
76+
y = x
77+
$metaex
78+
y*y+1
79+
end
80+
metaex = Expr(:block, Expr(:meta, :foo))
81+
ex3 = quote
82+
y = x
83+
$metaex
84+
x*x+1
85+
end
86+
87+
@test popmeta!(ex1, :foo)[1]
88+
@test popmeta!(ex2, :foo)[1]
89+
@test popmeta!(ex3, :foo)[1]
90+
@test !(popmeta!(:(x*x+1), :foo)[1])
91+
6992
end
7093

7194

0 commit comments

Comments
 (0)