Skip to content

Commit 978d8bb

Browse files
committed
Merge pull request #11617 from JuliaLang/teh/robustmeta
Check for :meta expressions more thoroughly, and improve indexing performance
2 parents d586d8d + 6825f4d commit 978d8bb

File tree

4 files changed

+83
-42
lines changed

4 files changed

+83
-42
lines changed

base/abstractarray.jl

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,14 @@ macro _noinline_meta()
123123
end
124124

125125
## Bounds checking ##
126+
@generated function trailingsize{T,N,n}(A::AbstractArray{T,N}, ::Type{Val{n}})
127+
ex = :(size(A, $n))
128+
for m = n+1:N
129+
ex = :($ex * size(A, $m))
130+
end
131+
Expr(:block, Expr(:meta, :inline), ex)
132+
end
133+
126134
_checkbounds(sz, i::Integer) = 1 <= i <= sz
127135
_checkbounds(sz, i::Real) = 1 <= to_index(i) <= sz
128136
_checkbounds(sz, I::AbstractVector{Bool}) = length(I) == sz
@@ -149,17 +157,14 @@ function checkbounds(A::AbstractMatrix, I::Union(Real,AbstractArray,Colon), J::U
149157
end
150158
function checkbounds(A::AbstractArray, I::Union(Real,AbstractArray,Colon), J::Union(Real,AbstractArray,Colon))
151159
@_inline_meta
152-
(_checkbounds(size(A,1), I) && _checkbounds(trailingsize(A,2), J)) || throw_boundserror(A, (I, J))
160+
(_checkbounds(size(A,1), I) && _checkbounds(trailingsize(A,Val{2}), J)) || throw_boundserror(A, (I, J))
153161
end
154-
function checkbounds(A::AbstractArray, I::Union(Real,AbstractArray,Colon)...)
155-
@_inline_meta
156-
n = length(I)
157-
if n > 0
158-
for dim = 1:(n-1)
159-
_checkbounds(size(A,dim), I[dim]) || throw_boundserror(A, I)
160-
end
161-
_checkbounds(trailingsize(A,n), I[n]) || throw_boundserror(A, I)
162-
end
162+
@generated function checkbounds(A::AbstractArray, I::Union(Real,AbstractArray,Colon)...)
163+
meta = Expr(:meta, :inline)
164+
N = length(I)
165+
args = Expr[:(_checkbounds(size(A,$dim), I[$dim]) || throw_boundserror(A, I)) for dim in 1:N-1]
166+
push!(args, :(_checkbounds(trailingsize(A,Val{$N}), I[$N]) || throw_boundserror(A, I)))
167+
Expr(:block, meta, args...)
163168
end
164169

165170
## Bounds-checking without errors ##

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)