Skip to content

Commit

Permalink
Merge pull request #22659 from JuliaLang/jb/loopvars
Browse files Browse the repository at this point in the history
RFC: deprecate `for` loop vars that overwrite outer vars (#22314)
  • Loading branch information
JeffBezanson authored Aug 23, 2017
2 parents fe27001 + 9433618 commit edd8278
Show file tree
Hide file tree
Showing 36 changed files with 252 additions and 145 deletions.
5 changes: 5 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ Language changes
warning, so that this syntax can be disallowed or given a new meaning in a
future version ([#5148]).

* In `for i = ...`, if a local variable `i` already existed it would be overwritten
during the loop. This behavior is deprecated, and in the future `for` loop variables
will always be new variables local to the loop ([#22314]).
The old behavior of overwriting an existing variable is available via `for outer i = ...`.

* In `for i in x`, `x` used to be evaluated in a new scope enclosing the `for` loop.
Now it is evaluated in the scope outside the `for` loop.

Expand Down
4 changes: 2 additions & 2 deletions base/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ function firstcaller(bt::Array{Ptr{Void},1}, funcsyms)
lkup = StackTraces.UNKNOWN
for frame in bt
lkups = StackTraces.lookup(frame)
for lkup in lkups
for outer lkup in lkups
if lkup == StackTraces.UNKNOWN
continue
end
Expand Down Expand Up @@ -1067,7 +1067,7 @@ function partial_linear_indexing_warning_lookup(nidxs_remaining)
caller = StackTraces.UNKNOWN
for frame in bt
lkups = StackTraces.lookup(frame)
for caller in lkups
for outer caller in lkups
if caller == StackTraces.UNKNOWN
continue
end
Expand Down
4 changes: 2 additions & 2 deletions base/docs/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ function levsort(search, candidates)
scores = map(cand -> (levenshtein(search, cand), -fuzzyscore(search, cand)), candidates)
candidates = candidates[sortperm(scores)]
i = 0
for i = 1:length(candidates)
for outer i = 1:length(candidates)
levenshtein(search, candidates[i]) > 3 && break
end
return candidates[1:i]
Expand Down Expand Up @@ -328,7 +328,7 @@ printmatches(args...; cols = displaysize(STDOUT)[2]) = printmatches(STDOUT, args
function print_joined_cols(io::IO, ss, delim = "", last = delim; cols = displaysize(io)[2])
i = 0
total = 0
for i = 1:length(ss)
for outer i = 1:length(ss)
total += length(ss[i])
total + max(i-2,0)*length(delim) + (i>1 ? 1 : 0)*length(last) > cols && (i-=1; break)
end
Expand Down
20 changes: 10 additions & 10 deletions base/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3618,9 +3618,9 @@ end
function type_annotate!(sv::InferenceState)
# remove all unused ssa values
gt = sv.src.ssavaluetypes
for i = 1:length(gt)
if gt[i] === NF
gt[i] = Union{}
for j = 1:length(gt)
if gt[j] === NF
gt[j] = Union{}
end
end

Expand Down Expand Up @@ -3671,9 +3671,9 @@ function type_annotate!(sv::InferenceState)
end

# finish marking used-undef variables
for i = 1:nslots
if undefs[i]
src.slotflags[i] |= Slot_UsedUndef
for j = 1:nslots
if undefs[j]
src.slotflags[j] |= Slot_UsedUndef
end
end
nothing
Expand Down Expand Up @@ -4650,10 +4650,10 @@ function inlineable(@nospecialize(f), @nospecialize(ft), e::Expr, atypes::Vector
if !isempty(stmts) && !propagate_inbounds
# avoid redundant inbounds annotations
s_1, s_end = stmts[1], stmts[end]
i = 2
while length(stmts) > i && ((isa(s_1,Expr)&&s_1.head===:line) || isa(s_1,LineNumberNode))
s_1 = stmts[i]
i += 1
si = 2
while length(stmts) > si && ((isa(s_1,Expr)&&s_1.head===:line) || isa(s_1,LineNumberNode))
s_1 = stmts[si]
si += 1
end
if isa(s_1, Expr) && s_1.head === :inbounds && s_1.args[1] === false &&
isa(s_end, Expr) && s_end.head === :inbounds && s_end.args[1] === :pop
Expand Down
5 changes: 2 additions & 3 deletions base/intfuncs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -833,9 +833,8 @@ end

function factorial(n::Integer)
n < 0 && throw(DomainError(n, "`n` must be nonnegative."))
local f::typeof(n*n), i::typeof(n*n)
f = 1
for i = 2:n
f::typeof(n*n) = 1
for i::typeof(n*n) = 2:n
f *= i
end
return f
Expand Down
10 changes: 6 additions & 4 deletions base/linalg/triangular.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1836,7 +1836,8 @@ function logm(A0::UpperTriangular{T}) where T<:Union{Float64,Complex{Float64}}
d4 = norm(AmI^4, 1)^(1/4)
alpha3 = max(d3, d4)
if alpha3 <= theta[tmax]
for j = 3:tmax
local j
for outer j = 3:tmax
if alpha3 <= theta[j]
break
end
Expand All @@ -1856,7 +1857,7 @@ function logm(A0::UpperTriangular{T}) where T<:Union{Float64,Complex{Float64}}
eta = min(alpha3, alpha4)
if eta <= theta[tmax]
j = 0
for j = 6:tmax
for outer j = 6:tmax
if eta <= theta[j]
m = j
break
Expand Down Expand Up @@ -2035,7 +2036,8 @@ function invsquaring(A0::UpperTriangular, theta)
d4 = norm(AmI^4, 1)^(1/4)
alpha3 = max(d3, d4)
if alpha3 <= theta[tmax]
for j = 3:tmax
local j
for outer j = 3:tmax
if alpha3 <= theta[j]
break
elseif alpha3 / 2 <= theta[5] && p < 2
Expand All @@ -2059,7 +2061,7 @@ function invsquaring(A0::UpperTriangular, theta)
eta = min(alpha3, alpha4)
if eta <= theta[tmax]
j = 0
for j = 6:tmax
for outer j = 6:tmax
if eta <= theta[j]
m = j
break
Expand Down
4 changes: 2 additions & 2 deletions base/markdown/Markdown.jl
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ macro doc_str(s::AbstractString, t...)
docexpr(__source__, __module__, s, t...)
end

function Base.display(d::Base.REPL.REPLDisplay, md::Vector{MD})
for md in md
function Base.display(d::Base.REPL.REPLDisplay, mds::Vector{MD})
for md in mds
display(d, md)
end
end
Expand Down
4 changes: 2 additions & 2 deletions base/markdown/render/latex.jl
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ function latexinline(io::IO, code::Code)
end

function latex(io::IO, md::Paragraph)
for md in md.content
latexinline(io, md)
for mdc in md.content
latexinline(io, mdc)
end
println(io)
println(io)
Expand Down
4 changes: 2 additions & 2 deletions base/markdown/render/rst.jl
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ end

function rst(io::IO, l::LaTeX)
println(io, ".. math::\n")
for l in lines(l.formula)
println(io, " ", l)
for line in lines(l.formula)
println(io, " ", line)
end
end

Expand Down
4 changes: 2 additions & 2 deletions base/multidimensional.jl
Original file line number Diff line number Diff line change
Expand Up @@ -841,9 +841,9 @@ end

@noinline function _accumulate!(op, B, A, R1, ind, R2)
# Copy the initial element in each 1d vector along dimension `axis`
i = first(ind)
ii = first(ind)
@inbounds for J in R2, I in R1
B[I, i, J] = A[I, i, J]
B[I, ii, J] = A[I, ii, J]
end
# Accumulate
@inbounds for J in R2, i in first(ind)+1:last(ind), I in R1
Expand Down
2 changes: 1 addition & 1 deletion base/printf.jl
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ function parse(s::AbstractString)
i = 1
while i < length(list)
if isa(list[i],AbstractString)
for j = i+1:length(list)
for outer j = i+1:length(list)
if !isa(list[j],AbstractString)
j -= 1
break
Expand Down
4 changes: 2 additions & 2 deletions base/sparse/linalg.jl
Original file line number Diff line number Diff line change
Expand Up @@ -301,8 +301,8 @@ function A_rdiv_B!(A::SparseMatrixCSC{T}, D::Diagonal{T}) where T
if iszero(ddj)
throw(LinAlg.SingularException(j))
end
for k in nzrange(A, j)
nonz[k] /= ddj
for i in nzrange(A, j)
nonz[i] /= ddj
end
end
A
Expand Down
16 changes: 9 additions & 7 deletions doc/src/manual/variables-and-scoping.md
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ julia> Fs[2]()
2
```

`for` loops will reuse existing variables for its iteration variable:
A `for` loop or comprehension iteration variable is always a new variable:

```jldoctest
julia> i = 0;
Expand All @@ -444,18 +444,20 @@ julia> for i = 1:3
end
julia> i
3
0
```

However, comprehensions do not do this, and always freshly allocate their iteration variables:
However, it is occasionally useful to reuse an existing variable as the iteration variable.
This can be done conveniently by adding the keyword `outer`:

```jldoctest
julia> x = 0;
julia> i = 0;
julia> [ x for x = 1:3 ];
julia> for outer i = 1:3
end
julia> x
0
julia> i
3
```

## Constants
Expand Down
17 changes: 15 additions & 2 deletions src/julia-parser.scm
Original file line number Diff line number Diff line change
Expand Up @@ -1611,7 +1611,18 @@

;; as above, but allows both "i=r" and "i in r"
(define (parse-iteration-spec s)
(let* ((lhs (parse-pipes s))
(let* ((outer? (if (eq? (peek-token s) 'outer)
(begin
(take-token s)
(let ((nxt (peek-token s)))
(if (or (memq nxt '(= in ∈))
(not (symbol? nxt))
(operator? nxt))
(begin (ts:put-back! s 'outer #t)
#f)
#t)))
#f))
(lhs (parse-pipes s))
(t (peek-token s)))
(cond ((memq t '(= in ∈))
(take-token s)
Expand All @@ -1621,7 +1632,9 @@
;; should be: (error "invalid iteration specification")
(syntax-deprecation s (string "for " (deparse `(= ,lhs ,rhs)) " " t)
(string "for " (deparse `(= ,lhs ,rhs)) "; " t)))
`(= ,lhs ,rhs)))
(if outer?
`(= (outer ,lhs) ,rhs)
`(= ,lhs ,rhs))))
((and (eq? lhs ':) (closing-token? t))
':)
(else (error "invalid iteration specification")))))
Expand Down
Loading

0 comments on commit edd8278

Please sign in to comment.