Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for begin in at-view/at-views #35289

Merged
merged 3 commits into from
Apr 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ New library features
* `accumulate`, `cumsum`, and `cumprod` now support `Tuple` ([#34654]) and arbitrary iterators ([#34656]).
* In `splice!` with no replacement, values to be removed can now be specified with an
arbitrary iterable (instead of a `UnitRange`) ([#34524]).
* The `@view` and `@views` macros now support the `a[begin]` syntax that was introduced in Julia 1.4 ([#35289]).
* `open` for files now accepts a keyword argument `lock` controlling whether file operations
will acquire locks for safe multi-threaded access. Setting it to `false` provides better
performance when only one thread will access the file.
Expand Down
47 changes: 30 additions & 17 deletions base/views.jl
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# This file is a part of Julia. License is MIT: https://julialang.org/license

"""
replace_ref_end!(ex)
replace_ref_begin_end!(ex)

Recursively replace occurrences of the symbol :end in a "ref" expression (i.e. A[...]) `ex`
with the appropriate function calls (`lastindex` or `size`). Replacement uses
the closest enclosing ref, so
Recursively replace occurrences of the symbols `:begin` and `:end` in a "ref" expression
(i.e. `A[...]`) `ex` with the appropriate function calls (`firstindex` or `lastindex`).
Replacement uses the closest enclosing ref, so

A[B[end]]

Expand All @@ -14,16 +14,21 @@ should transform to
A[B[lastindex(B)]]

"""
replace_ref_end!(ex) = replace_ref_end_!(ex, nothing)[1]
# replace_ref_end_!(ex,withex) returns (new ex, whether withex was used)
function replace_ref_end_!(ex, withex)
replace_ref_begin_end!(ex) = replace_ref_begin_end_!(ex, nothing)[1]
# replace_ref_begin_end_!(ex,withex) returns (new ex, whether withex was used)
function replace_ref_begin_end_!(ex, withex)
used_withex = false
if isa(ex,Symbol) && ex === :end
withex === nothing && error("Invalid use of end")
return withex, true
if isa(ex,Symbol)
if ex === :begin
withex === nothing && error("Invalid use of begin")
return withex[1], true
elseif ex === :end
withex === nothing && error("Invalid use of end")
return withex[2], true
end
elseif isa(ex,Expr)
if ex.head === :ref
ex.args[1], used_withex = replace_ref_end_!(ex.args[1],withex)
ex.args[1], used_withex = replace_ref_begin_end_!(ex.args[1], withex)
S = isa(ex.args[1],Symbol) ? ex.args[1]::Symbol : gensym(:S) # temp var to cache ex.args[1] if needed
used_S = false # whether we actually need S
# new :ref, so redefine withex
Expand All @@ -32,12 +37,12 @@ function replace_ref_end_!(ex, withex)
return ex, used_withex
elseif nargs == 1
# replace with lastindex(S)
ex.args[2], used_S = replace_ref_end_!(ex.args[2],:($lastindex($S)))
ex.args[2], used_S = replace_ref_begin_end_!(ex.args[2], (:($firstindex($S)),:($lastindex($S))))
else
n = 1
J = lastindex(ex.args)
for j = 2:J
exj, used = replace_ref_end_!(ex.args[j],:($lastindex($S,$n)))
exj, used = replace_ref_begin_end_!(ex.args[j], (:($firstindex($S)),:($lastindex($S,$n))))
used_S |= used
ex.args[j] = exj
if isa(exj,Expr) && exj.head === :...
Expand All @@ -61,7 +66,7 @@ function replace_ref_end_!(ex, withex)
else
# recursive search
for i = eachindex(ex.args)
ex.args[i], used = replace_ref_end_!(ex.args[i],withex)
ex.args[i], used = replace_ref_begin_end_!(ex.args[i], withex)
used_withex |= used
end
end
Expand All @@ -77,6 +82,10 @@ reference expression (e.g. `@view A[1,2:end]`), and should *not* be used as the
an assignment (e.g. `@view(A[1,2:end]) = ...`). See also [`@views`](@ref)
to switch an entire block of code to use views for slicing.

!!! compat "Julia 1.5"
Using `begin` in an indexing expression to refer to the first index requires at least
Julia 1.5.

# Examples
```jldoctest
julia> A = [1 2; 3 4]
Expand All @@ -102,7 +111,7 @@ julia> A
"""
macro view(ex)
if Meta.isexpr(ex, :ref)
ex = replace_ref_end!(ex)
ex = replace_ref_begin_end!(ex)
if Meta.isexpr(ex, :ref)
ex = Expr(:call, view, ex.args...)
else # ex replaced by let ...; foo[...]; end
Expand All @@ -129,7 +138,7 @@ end
# _views implements the transformation for the @views macro.
# @views calls esc(_views(...)) to work around #20241,
# so any function calls we insert (to maybeview, or to
# lastindex in replace_ref_end!) must be interpolated
# firstindex and lastindex in replace_ref_begin_end!) must be interpolated
# as values rather than as symbols to ensure that they are called
# from Base rather than from the caller's scope.
_views(x) = x
Expand Down Expand Up @@ -194,6 +203,10 @@ unaffected.
that appear explicitly in the given `expression`, not array slicing that
occurs in functions called by that code.

!!! compat "Julia 1.5"
Using `begin` in an indexing expression to refer to the first index requires at least
Julia 1.5.

# Examples
```jldoctest
julia> A = zeros(3, 3);
Expand All @@ -211,5 +224,5 @@ julia> A
```
"""
macro views(x)
esc(_views(replace_ref_end!(x)))
esc(_views(replace_ref_begin_end!(x)))
end
34 changes: 17 additions & 17 deletions test/subarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,7 @@ end
@test Array(view(view(reshape(1:13^3, 13, 13, 13), 3:7, 6:6, :), 1:2:5, :, 1:2:5)) ==
cat([68,70,72],[406,408,410],[744,746,748]; dims=3)

# tests @view (and replace_ref_end!)
# tests @view (and replace_ref_begin_end!)

@test_throws ArgumentError(
"Invalid use of @view macro: argument must be a reference expression A[...]."
Expand All @@ -501,16 +501,16 @@ Y = 4:-1:1

@test isa(@view(X[1:3]), SubArray)

@test X[1:end] == @.(@view X[1:end]) # test compatibility of @. and @view
@test X[1:end-3] == @view X[1:end-3]
@test X[1:end,2,2] == @view X[1:end,2,2]
@test X[1,1:end-2,1] == @view X[1,1:end-2,1]
@test X[1,2,1:end-2] == @view X[1,2,1:end-2]
@test X[1,2,Y[2:end]] == @view X[1,2,Y[2:end]]
@test X[1:end,2,Y[2:end]] == @view X[1:end,2,Y[2:end]]
@test X[begin:end] == @.(@view X[begin:end]) # test compatibility of @. and @view
@test X[begin:end-3] == @view X[begin:end-3]
@test X[1:end,2,begin+1] == @view X[1:end,2,begin+1]
@test X[begin,1:end-2,1] == @view X[begin,1:end-2,1]
@test X[begin,begin+1,begin:end-2] == @view X[begin,begin+1,begin:end-2]
@test X[begin,2,Y[2:end]] == @view X[begin,2,Y[2:end]]
@test X[begin:end,2,Y[begin+1:end]] == @view X[begin:end,2,Y[begin+1:end]]

u = (1,2:3)
@test X[u...,2:end] == @view X[u...,2:end]
@test X[u...,begin+1:end] == @view X[u...,begin+1:end]
@test X[(1,)...,(2,)...,2:end] == @view X[(1,)...,(2,)...,2:end]

# test macro hygiene
Expand All @@ -525,7 +525,7 @@ let foo = [X]
end

# test @views macro
@views let f!(x) = x[1:end-1] .+= x[2:end].^2
@views let f!(x) = x[begin:end-1] .+= x[begin+1:end].^2
x = [1,2,3,4]
f!(x)
@test x == [5,11,19,4]
Expand All @@ -552,13 +552,13 @@ end
@test x == [5,8,0,0]
end
@views @test isa(X[1:3], SubArray)
@test X[1:end] == @views X[1:end]
@test X[1:end-3] == @views X[1:end-3]
@test X[1:end,2,2] == @views X[1:end,2,2]
@test X[1,2,1:end-2] == @views X[1,2,1:end-2]
@test X[1,2,Y[2:end]] == @views X[1,2,Y[2:end]]
@test X[1:end,2,Y[2:end]] == @views X[1:end,2,Y[2:end]]
@test X[u...,2:end] == @views X[u...,2:end]
@test X[begin:end] == @views X[begin:end]
@test X[begin:end-3] == @views X[begin:end-3]
@test X[1:end,2,begin+1] == @views X[1:end,2,begin+1]
@test X[begin,2,1:end-2] == @views X[begin,2,1:end-2]
@test X[begin,2,Y[2:end]] == @views X[begin,2,Y[2:end]]
@test X[begin:end,2,Y[begin+1:end]] == @views X[begin:end,2,Y[begin+1:end]]
@test X[u...,begin+1:end] == @views X[u...,begin+1:end]
@test X[(1,)...,(2,)...,2:end] == @views X[(1,)...,(2,)...,2:end]

# @views for zero dimensional arrays
Expand Down