From 94336183a833c1f97e2e7b2942b6fb406de267e3 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Sun, 2 Jul 2017 00:09:30 -0600 Subject: [PATCH] deprecate `for` loop vars that overwrite outer vars (#22314) provide the old behavior via `for outer i = ...` --- NEWS.md | 5 ++ base/deprecated.jl | 4 +- base/docs/utils.jl | 4 +- base/inference.jl | 20 ++++---- base/intfuncs.jl | 5 +- base/linalg/triangular.jl | 10 ++-- base/markdown/Markdown.jl | 4 +- base/markdown/render/latex.jl | 4 +- base/markdown/render/rst.jl | 4 +- base/multidimensional.jl | 4 +- base/printf.jl | 2 +- base/sparse/linalg.jl | 4 +- doc/src/manual/variables-and-scoping.md | 16 +++--- src/julia-parser.scm | 17 ++++++- src/julia-syntax.scm | 66 ++++++++++++++++++++++--- test/arrayops.jl | 7 +-- test/bigint.jl | 5 +- test/channels.jl | 4 +- test/core.jl | 2 + test/dates/rounding.jl | 1 + test/distributed_exec.jl | 28 ++++++----- test/file.jl | 1 + test/float16.jl | 8 +-- test/linalg/matmul.jl | 18 ++++--- test/numbers.jl | 54 ++++++++++---------- test/random.jl | 3 ++ test/ranges.jl | 49 ++++++++++-------- test/read.jl | 10 ++-- test/sets.jl | 6 +-- test/show.jl | 3 +- test/sparse/cholmod.jl | 9 ++-- test/sparse/sparse.jl | 8 +-- test/spawn.jl | 4 +- test/strings/basic.jl | 1 + test/strings/types.jl | 3 ++ test/topology.jl | 4 +- 36 files changed, 252 insertions(+), 145 deletions(-) diff --git a/NEWS.md b/NEWS.md index d0ac72076cf43..ae91a03613029 100644 --- a/NEWS.md +++ b/NEWS.md @@ -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. diff --git a/base/deprecated.jl b/base/deprecated.jl index 3428b14900e8c..65a5cf71729c1 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -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 @@ -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 diff --git a/base/docs/utils.jl b/base/docs/utils.jl index 9f7a54f110f5b..7cf5b4f032462 100644 --- a/base/docs/utils.jl +++ b/base/docs/utils.jl @@ -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] @@ -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 diff --git a/base/inference.jl b/base/inference.jl index 0bfc01993cc2c..a9574ad95301c 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -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 @@ -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 @@ -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 diff --git a/base/intfuncs.jl b/base/intfuncs.jl index da4fa67d9f22a..7c33bc407b027 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -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 diff --git a/base/linalg/triangular.jl b/base/linalg/triangular.jl index 12aaa748770f2..616cb59855714 100644 --- a/base/linalg/triangular.jl +++ b/base/linalg/triangular.jl @@ -1831,7 +1831,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 @@ -1851,7 +1852,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 @@ -2030,7 +2031,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 @@ -2054,7 +2056,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 diff --git a/base/markdown/Markdown.jl b/base/markdown/Markdown.jl index 40f8b8955b273..9f2155fb2c3b5 100644 --- a/base/markdown/Markdown.jl +++ b/base/markdown/Markdown.jl @@ -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 diff --git a/base/markdown/render/latex.jl b/base/markdown/render/latex.jl index fb0650c5d6603..1e885a578d588 100644 --- a/base/markdown/render/latex.jl +++ b/base/markdown/render/latex.jl @@ -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) diff --git a/base/markdown/render/rst.jl b/base/markdown/render/rst.jl index d9fadc4e7910e..377aee7399926 100644 --- a/base/markdown/render/rst.jl +++ b/base/markdown/render/rst.jl @@ -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 diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 7680923cdea97..64cf42b8b44c7 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -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 diff --git a/base/printf.jl b/base/printf.jl index 47ade06597171..230ea2b983de6 100644 --- a/base/printf.jl +++ b/base/printf.jl @@ -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 diff --git a/base/sparse/linalg.jl b/base/sparse/linalg.jl index 5ae8cdda4129c..b3b78ecd50f13 100644 --- a/base/sparse/linalg.jl +++ b/base/sparse/linalg.jl @@ -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 diff --git a/doc/src/manual/variables-and-scoping.md b/doc/src/manual/variables-and-scoping.md index 22b1f2c8e7551..208a3f7d0a251 100644 --- a/doc/src/manual/variables-and-scoping.md +++ b/doc/src/manual/variables-and-scoping.md @@ -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; @@ -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 diff --git a/src/julia-parser.scm b/src/julia-parser.scm index 9ca2ad558242c..32281cca87b27 100644 --- a/src/julia-parser.scm +++ b/src/julia-parser.scm @@ -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) @@ -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"))))) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 040d4b04c9a2b..53cca2c341ec0 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -1671,12 +1671,20 @@ (if ,g ,g ,(loop (cdr tail))))))))))) +;; If true, this will warn on all `for` loop variables that overwrite outer variables. +;; If false, this will try to warn only for uses of the last value after the loop. +(define *warn-all-loop-vars* #f) + (define (expand-for while lhs X body) ;; (for (= lhs X) body) - (let ((coll (make-ssavalue)) - (state (gensy))) + (let* ((coll (make-ssavalue)) + (state (gensy)) + (outer? (and (pair? lhs) (eq? (car lhs) 'outer))) + (lhs (if outer? (cadr lhs) lhs))) `(block (= ,coll ,(expand-forms X)) (= ,state (call (top start) ,coll)) + ;; TODO avoid `local declared twice` error from this + ;;,@(if outer? `((local ,lhs)) '()) ,(expand-forms `(,while (call (top !) (call (top done) ,coll ,state)) @@ -1684,6 +1692,9 @@ (block ;; NOTE: enable this to force loop-local var #;,@(map (lambda (v) `(local ,v)) (lhs-vars lhs)) + ,@(if (and (not outer?) (or *depwarn* *deperror*)) + (map (lambda (v) `(warn-if-existing ,v)) (lhs-vars lhs)) + '()) ,(lower-tuple-assignment (list lhs state) `(call (top next) ,coll ,state)) ,body))))))) @@ -2535,7 +2546,7 @@ ((eq? (car e) 'break-block) (unbound-vars (caddr e) bound tab)) ((eq? (car e) 'with-static-parameters) (unbound-vars (cadr e) bound tab)) (else (for-each (lambda (x) (unbound-vars x bound tab)) - (cdr e)) + (cdr e)) tab))) ;; local variable identification and renaming, derived from: @@ -2551,6 +2562,10 @@ ((eq? (car e) 'local) '(null)) ;; remove local decls ((eq? (car e) 'local-def) '(null)) ;; remove local decls ((eq? (car e) 'implicit-global) '(null)) ;; remove implicit-global decls + ((eq? (car e) 'warn-if-existing) + (if (or (memq (cadr e) outerglobals) (memq (cadr e) implicitglobals)) + `(warn-loop-var ,(cadr e)) + '(null))) ((eq? (car e) 'lambda) (let* ((lv (lam:vars e)) (env (append lv env)) @@ -2591,6 +2606,9 @@ vars)))) (need-rename (need-rename? vars)) (need-rename-def (need-rename? vars-def)) + (deprecated-loop-vars + (filter (lambda (v) (and (memq v env) (not (memq v locals-declared)))) + (delete-duplicates (find-decls 'warn-if-existing blok)))) ;; new gensym names for conflicting variables (renamed (map named-gensy need-rename)) (renamed-def (map named-gensy need-rename-def)) @@ -2620,10 +2638,19 @@ (if lam ;; update in-place the list of local variables in lam (set-car! (cddr lam) (append real-new-vars real-new-vars-def (caddr lam)))) - (insert-after-meta ;; return the new, expanded scope-block - (blockify body) - (append! (map (lambda (v) `(local ,v)) real-new-vars) - (map (lambda (v) `(local-def ,v)) real-new-vars-def))))) + (let* ((warnings (map (lambda (v) `(warn-loop-var ,v)) deprecated-loop-vars)) + (body (if *warn-all-loop-vars* + body + (if (and (pair? body) (eq? (car body) 'block)) + (append body warnings) + `(block ,body ,@warnings))))) + (insert-after-meta ;; return the new, expanded scope-block + (blockify body) + (append! (map (lambda (v) `(local ,v)) real-new-vars) + (map (lambda (v) `(local-def ,v)) real-new-vars-def) + (if *warn-all-loop-vars* + (map (lambda (v) `(warn-loop-var ,v)) deprecated-loop-vars) + '())))))) ((eq? (car e) 'module) (error "module expression not at top level")) ((eq? (car e) 'break-block) @@ -3089,7 +3116,7 @@ f(x) = yt(x) ((atom? e) e) (else (case (car e) - ((quote top core globalref outerref line break inert module toplevel null meta) e) + ((quote top core globalref outerref line break inert module toplevel null meta warn-loop-var) e) ((=) (let ((var (cadr e)) (rhs (cl-convert (caddr e) fname lam namemap toplevel interp))) @@ -3335,6 +3362,11 @@ f(x) = yt(x) (else (for-each linearize (cdr e)))) e) +(define (deprecation-message msg) + (if *deperror* + (error msg) + (io.write *stderr* msg))) + ;; this pass behaves like an interpreter on the given code. ;; to perform stateful operations, it calls `emit` to record that something ;; needs to be done. in value position, it returns an expression computing @@ -3347,6 +3379,7 @@ f(x) = yt(x) (first-line #t) (current-loc #f) (rett #f) + (deprecated-loop-vars (table)) (arg-map #f) ;; map arguments to new names if they are assigned (label-counter 0) ;; counter for generating label addresses (label-map (table)) ;; maps label names to generated addresses @@ -3440,6 +3473,11 @@ f(x) = yt(x) (eq? (cadr e) '_)))) (syntax-deprecation #f (string "_ as an rvalue" (linenode-string current-loc)) "")) + (if (and (not *warn-all-loop-vars*) (has? deprecated-loop-vars e)) + (begin (deprecation-message + (string "Use of final value of loop variable \"" e "\"" (linenode-string current-loc) " " + "is deprecated. In the future the variable will be local to the loop instead." #\newline)) + (del! deprecated-loop-vars e))) (cond (tail (emit-return e1)) (value e1) ((or (eq? e1 'true) (eq? e1 'false)) #f) @@ -3477,6 +3515,8 @@ f(x) = yt(x) (lhs (if (and arg-map (symbol? lhs)) (get arg-map lhs lhs) lhs))) + (if (and (not *warn-all-loop-vars*) (has? deprecated-loop-vars lhs)) + (del! deprecated-loop-vars lhs)) (if value (let ((rr (if (or (atom? rhs) (ssavalue? rhs) (eq? (car rhs) 'null)) rhs (make-ssavalue)))) @@ -3670,6 +3710,16 @@ f(x) = yt(x) '(null)) (emit e))) ((isdefined) (if tail (emit-return e) e)) + ((warn-loop-var) + (if (or *warn-all-loop-vars* + (not (or (assq (cadr e) (car (lam:vinfo lam))) + (assq (cadr e) (cadr (lam:vinfo lam)))))) + (deprecation-message + (string "Loop variable \"" (cadr e) "\"" (linenode-string current-loc) " " + "overwrites a variable in an enclosing scope. " + "In the future the variable will be local to the loop instead." #\newline)) + (put! deprecated-loop-vars (cadr e) #t)) + '(null)) ;; top level expressions returning values ((abstract_type primitive_type struct_type thunk toplevel module) diff --git a/test/arrayops.jl b/test/arrayops.jl index 659a42502b12a..e52190f2e7629 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -1740,13 +1740,12 @@ module RetTypeDecl end # range, range ops -A = 1:5 -B = 1.5:5.5 -@test A + B == 2.5:2.0:10.5 +@test (1:5) + (1.5:5.5) == 2.5:2.0:10.5 @testset "slicedim" begin for A in (reshape(collect(1:20), 4, 5), reshape(1:20, 4, 5)) + local A @test slicedim(A, 1, 2) == collect(2:4:20) @test slicedim(A, 2, 2) == collect(5:8) @test_throws ArgumentError slicedim(A,0,1) @@ -1784,9 +1783,11 @@ S = view(A, :, :) @test isequal(B, A) for (a,b) in zip(A, B) + local a,b @test a == b end for (a,s) in zip(A, S) + local a,s @test a == s end diff --git a/test/bigint.jl b/test/bigint.jl index 341d692bf4d2a..71a2fd16e8cec 100644 --- a/test/bigint.jl +++ b/test/bigint.jl @@ -282,8 +282,8 @@ ndigits(big(rand(Int)), rand(63:typemax(Int))) ndigits(big(rand(Int)), big(2)^rand(2:999)) for x in big.([-20:20; rand(Int)]) - for b in -1:1 - @test_throws DomainError ndigits(x, b) + for _base in -1:1 + @test_throws DomainError ndigits(x, _base) end end @@ -344,6 +344,7 @@ end # respect 0-padding on big(0) for f in (bin, oct, dec, hex) + local f @test f(big(0), 0) == "" end @test base(rand(2:62), big(0), 0) == "" diff --git a/test/channels.jl b/test/channels.jl index fb6e5234edd2f..be01bf389c690 100644 --- a/test/channels.jl +++ b/test/channels.jl @@ -49,8 +49,8 @@ c=Channel(32) results=[] @sync begin for i in 1:20 - @async for i in c - push!(results, i) + @async for ii in c + push!(results, ii) end end sleep(1.0) diff --git a/test/core.jl b/test/core.jl index a8287f3e8c01f..a5c64f74629d1 100644 --- a/test/core.jl +++ b/test/core.jl @@ -5277,6 +5277,7 @@ x.u = initvalue(Base.uniontypes(U)[2]) @test x.u === initvalue(Base.uniontypes(U)[2]) for U in boxedunions + local U for N in (1, 2, 3, 4) A = Array{U}(ntuple(x->0, N)...) @test isempty(A) @@ -5296,6 +5297,7 @@ A5 = [1 2 3; 4 5 6] @test_throws ArgumentError unsafe_wrap(Array, convert(Ptr{Union{Int, Void}}, pointer(A5)), 6) for U in unboxedunions + local U for N in (1, 2, 3, 4) A = Array{U}(ntuple(x->0, N)...) @test isempty(A) diff --git a/test/dates/rounding.jl b/test/dates/rounding.jl index e32f592a0f301..57f999c4382cc 100644 --- a/test/dates/rounding.jl +++ b/test/dates/rounding.jl @@ -114,6 +114,7 @@ dt = Dates.DateTime(-1, 12, 29, 19, 19, 19, 19) # Test rounding for dates that should not need rounding for dt in [Dates.DateTime(2016, 1, 1), Dates.DateTime(-2016, 1, 1)] + local dt for p in [Dates.Year, Dates.Month, Dates.Day, Dates.Hour, Dates.Minute, Dates.Second] @test floor(dt, p) == dt @test ceil(dt, p) == dt diff --git a/test/distributed_exec.jl b/test/distributed_exec.jl index 65de2c21a4a9d..619a1a3bf5166 100644 --- a/test/distributed_exec.jl +++ b/test/distributed_exec.jl @@ -525,6 +525,7 @@ function finalize_and_test(r) end for id in [id_me, id_other] + local id finalize_and_test(Future(id)) finalize_and_test((r=Future(id); put!(r, 1); r)) finalize_and_test(RemoteChannel(id)) @@ -536,17 +537,19 @@ finalize(d) @test_throws BoundsError d[1] # Issue 22139 -aorig = a1 = SharedArray{Float64}((3, 3)) -a1 = remotecall_fetch(fill!, id_other, a1, 1.0) -@test object_id(aorig) == object_id(a1) -id = a1.id -aorig = nothing -a1 = remotecall_fetch(fill!, id_other, a1, 1.0) -gc(); gc() -a1 = remotecall_fetch(fill!, id_other, a1, 1.0) -@test haskey(Base.sa_refs, id) -finalize(a1) -@test !haskey(Base.sa_refs, id) +let + aorig = a1 = SharedArray{Float64}((3, 3)) + a1 = remotecall_fetch(fill!, id_other, a1, 1.0) + @test object_id(aorig) == object_id(a1) + id = a1.id + aorig = nothing + a1 = remotecall_fetch(fill!, id_other, a1, 1.0) + gc(); gc() + a1 = remotecall_fetch(fill!, id_other, a1, 1.0) + @test haskey(Base.sa_refs, id) + finalize(a1) + @test !haskey(Base.sa_refs, id) +end # Test @parallel load balancing - all processors should get either M or M+1 # iterations out of the loop range for some M. @@ -676,6 +679,7 @@ function test_remoteexception_thrown(expr) end for id in [id_other, id_me] + local id test_remoteexception_thrown() do remotecall_fetch(id) do throw(ErrorException("foobar")) @@ -1248,6 +1252,7 @@ let (p, p2) = filter!(p -> p != myid(), procs()) ex = Any[ (ex::CapturedException).ex for ex in (excpt::CompositeException).exceptions ] end for (p, ex) in zip(procs, ex) + local p if procs isa Int || p != myid() @test (ex::RemoteException).pid == p ex = ((ex::RemoteException).captured::CapturedException).ex @@ -1372,6 +1377,7 @@ end # Test addprocs/rmprocs from master node only for f in [ ()->addprocs(1; exeflags=test_exeflags), ()->rmprocs(workers()) ] + local f try remotecall_fetch(f, id_other) error("Unexpected") diff --git a/test/file.jl b/test/file.jl index 8e98b043759d6..c758edccd4ce8 100644 --- a/test/file.jl +++ b/test/file.jl @@ -934,6 +934,7 @@ for f in (mkdir, cd, Base.Filesystem.unlink, readlink, rm, touch, readdir, mkpat stat, lstat, ctime, mtime, filemode, filesize, uperm, gperm, operm, touch, isblockdev, ischardev, isdir, isfifo, isfile, islink, ispath, issetgid, issetuid, issocket, issticky, realpath, watch_file, poll_file) + local f @test_throws ArgumentError f("adir\0bad") end @test_throws ArgumentError chmod("ba\0d", 0o222) diff --git a/test/float16.jl b/test/float16.jl index e9a188329c978..81e77c8377b27 100644 --- a/test/float16.jl +++ b/test/float16.jl @@ -128,11 +128,11 @@ end # rounding in conversions let - for f in [.3325f0, -.3325f0] - f16 = Float16(f) + for ff in [.3325f0, -.3325f0] + f16 = Float16(ff) # need to round away from 0. make sure we picked closest number. - @test abs(f-f16) < abs(f-nextfloat(f16)) - @test abs(f-f16) < abs(f-prevfloat(f16)) + @test abs(ff-f16) < abs(ff-nextfloat(f16)) + @test abs(ff-f16) < abs(ff-prevfloat(f16)) end # halfway between and last bit is 1 f = reinterpret(Float32, 0b00111110101010100011000000000000) diff --git a/test/linalg/matmul.jl b/test/linalg/matmul.jl index 406d306dd49cc..5e27672df6a0d 100644 --- a/test/linalg/matmul.jl +++ b/test/linalg/matmul.jl @@ -333,14 +333,16 @@ import Base: *, transpose transpose(x::RootInt) = x @test Base.promote_op(*, RootInt, RootInt) === Int -a = [RootInt(3)] -C = [0] -A_mul_Bt!(C, a, a) -@test C[1] == 9 -a = [RootInt(2),RootInt(10)] -@test a*a' == [4 20; 20 100] -A = [RootInt(3) RootInt(5)] -@test A*a == [56] +let + a = [RootInt(3)] + C = [0] + A_mul_Bt!(C, a, a) + @test C[1] == 9 + a = [RootInt(2),RootInt(10)] + @test a*a' == [4 20; 20 100] + A = [RootInt(3) RootInt(5)] + @test A*a == [56] +end function test_mul(C, A, B) A_mul_B!(C, A, B) diff --git a/test/numbers.jl b/test/numbers.jl index 34586d4365e11..0f694fa410080 100644 --- a/test/numbers.jl +++ b/test/numbers.jl @@ -815,8 +815,8 @@ function _cmp_(x::Union{Int64,UInt64}, y::Float64) error("invalid: _cmp_($x,$y)") end -for x=Int64(2)^53-2:Int64(2)^53+5, - y=[2.0^53-2 2.0^53-1 2.0^53 2.0^53+2 2.0^53+4] +for x = Int64(2)^53-2:Int64(2)^53+5, + y = [2.0^53-2 2.0^53-1 2.0^53 2.0^53+2 2.0^53+4] u = UInt64(x) @test y == Float64(trunc(Int64,y)) @@ -1991,34 +1991,38 @@ for x in (12345.6789, 0, -12345.6789) @test y == floor(x, 1000) @test y == ceil(x, 1000) end -x = 12345.6789 -@test 0.0 == trunc(x, -1000) -@test 0.0 == round(x, -1000) -@test 0.0 == floor(x, -1000) -@test Inf == ceil(x, -1000) -x = -12345.6789 -@test -0.0 == trunc(x, -1000) -@test -0.0 == round(x, -1000) -@test -Inf == floor(x, -1000) -@test -0.0 == ceil(x, -1000) -x = 0.0 -@test 0.0 == trunc(x, -1000) -@test 0.0 == round(x, -1000) -@test 0.0 == floor(x, -1000) -@test 0.0 == ceil(x, -1000) +let x = 12345.6789 + @test 0.0 == trunc(x, -1000) + @test 0.0 == round(x, -1000) + @test 0.0 == floor(x, -1000) + @test Inf == ceil(x, -1000) +end +let x = -12345.6789 + @test -0.0 == trunc(x, -1000) + @test -0.0 == round(x, -1000) + @test -Inf == floor(x, -1000) + @test -0.0 == ceil(x, -1000) +end +let x = 0.0 + @test 0.0 == trunc(x, -1000) + @test 0.0 == round(x, -1000) + @test 0.0 == floor(x, -1000) + @test 0.0 == ceil(x, -1000) +end # rounding in other bases @test approx_eq(round(pi,2,2), 3.25) @test approx_eq(round(pi,3,2), 3.125) @test approx_eq(round(pi,3,5), 3.144) # vectorized trunc/round/floor/ceil with digits/base argument -a = rand(2, 2, 2) -for f in (round, trunc, floor, ceil) - @test f.(a[:, 1, 1], 2) == map(x->f(x, 2), a[:, 1, 1]) - @test f.(a[:, :, 1], 2) == map(x->f(x, 2), a[:, :, 1]) - @test f.(a, 9, 2) == map(x->f(x, 9, 2), a) - @test f.(a[:, 1, 1], 9, 2) == map(x->f(x, 9, 2), a[:, 1, 1]) - @test f.(a[:, :, 1], 9, 2) == map(x->f(x, 9, 2), a[:, :, 1]) - @test f.(a, 9, 2) == map(x->f(x, 9, 2), a) +let a = rand(2, 2, 2) + for f in (round, trunc, floor, ceil) + @test f.(a[:, 1, 1], 2) == map(x->f(x, 2), a[:, 1, 1]) + @test f.(a[:, :, 1], 2) == map(x->f(x, 2), a[:, :, 1]) + @test f.(a, 9, 2) == map(x->f(x, 9, 2), a) + @test f.(a[:, 1, 1], 9, 2) == map(x->f(x, 9, 2), a[:, 1, 1]) + @test f.(a[:, :, 1], 9, 2) == map(x->f(x, 9, 2), a[:, :, 1]) + @test f.(a, 9, 2) == map(x->f(x, 9, 2), a) + end end # significant digits (would be nice to have a smart vectorized # version of signif) diff --git a/test/random.jl b/test/random.jl index fd885163d8e77..22c4eb18a50d3 100644 --- a/test/random.jl +++ b/test/random.jl @@ -292,6 +292,7 @@ let mt = MersenneTwister(0) c = unsafe_wrap(Array, Ptr{Float64}(pc8), 1000) # Int(pointer(c)) % 16 == 8 for A in (a, b, c) + local A srand(mt, 0) rand(mt) # this is to fill mt.vals, cf. #9040 rand!(mt, A) # must not segfault even if Int(pointer(A)) % 16 != 0 @@ -373,6 +374,7 @@ for rng in ([], [MersenneTwister(0)], [RandomDevice()]) for T in (f! === rand! ? types : f! === randn! ? cftypes : ftypes) X = T == Bool ? T[0,1] : T[0,1,2] for A in (Array{T}(5), Array{T}(2, 3), GenericArray{T}(5), GenericArray{T}(2, 3)) + local A f!(rng..., A) ::typeof(A) if f! === rand! f!(rng..., A, X) ::typeof(A) @@ -393,6 +395,7 @@ for rng in ([], [MersenneTwister(0)], [RandomDevice()]) # Test that you cannot call randn or randexp with non-Float types. for r in [randn, randexp, randn!, randexp!] + local r @test_throws MethodError r(Int) @test_throws MethodError r(Int32) @test_throws MethodError r(Bool) diff --git a/test/ranges.jl b/test/ranges.jl index a953599223ad2..799cba53219cd 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -143,24 +143,25 @@ end # TwicePrecision test. These routines lose accuracy if you form # intermediate subnormals; with Float16, this happens so frequently, # let's only test Float32. -T = Float32 -Tw = widen(T) -slopbits = (Base.Math.significand_bits(Tw) + 1) - - 2*(Base.Math.significand_bits(T) + 1) -for i = 1:10^5 - x = Base.TwicePrecision{T}(rand()) - y = Base.TwicePrecision{T}(rand()) - xw, yw = asww(x), asww(y) - @test cmp_sn2(Tw(xw+yw), astuple(x+y)..., slopbits) - @test cmp_sn2(Tw(xw-yw), astuple(x-y)..., slopbits) - @test cmp_sn2(Tw(xw*yw), astuple(x*y)..., slopbits) - @test cmp_sn2(Tw(xw/yw), astuple(x/y)..., slopbits) - y = rand(T) - yw = widen(widen(y)) - @test cmp_sn2(Tw(xw+yw), astuple(x+y)..., slopbits) - @test cmp_sn2(Tw(xw-yw), astuple(x-y)..., slopbits) - @test cmp_sn2(Tw(xw*yw), astuple(x*y)..., slopbits) - @test cmp_sn2(Tw(xw/yw), astuple(x/y)..., slopbits) +let T = Float32 + Tw = widen(T) + slopbits = (Base.Math.significand_bits(Tw) + 1) - + 2*(Base.Math.significand_bits(T) + 1) + for i = 1:10^5 + x = Base.TwicePrecision{T}(rand()) + y = Base.TwicePrecision{T}(rand()) + xw, yw = asww(x), asww(y) + @test cmp_sn2(Tw(xw+yw), astuple(x+y)..., slopbits) + @test cmp_sn2(Tw(xw-yw), astuple(x-y)..., slopbits) + @test cmp_sn2(Tw(xw*yw), astuple(x*y)..., slopbits) + @test cmp_sn2(Tw(xw/yw), astuple(x/y)..., slopbits) + y = rand(T) + yw = widen(widen(y)) + @test cmp_sn2(Tw(xw+yw), astuple(x+y)..., slopbits) + @test cmp_sn2(Tw(xw-yw), astuple(x-y)..., slopbits) + @test cmp_sn2(Tw(xw*yw), astuple(x*y)..., slopbits) + @test cmp_sn2(Tw(xw/yw), astuple(x/y)..., slopbits) + end end x1 = Base.TwicePrecision{Float64}(1) @@ -518,8 +519,9 @@ end @test [0.0:prevfloat(0.1):0.3;] == [0.0, prevfloat(0.1), prevfloat(0.2), 0.3] @test [0.0:nextfloat(0.1):0.3;] == [0.0, nextfloat(0.1), nextfloat(0.2)] -for T = (Float32, Float64,),# BigFloat), - a = -5:25, s = [-5:-1;1:25;], d = 1:25, n = -1:15 +for T = (Float32, Float64,)# BigFloat), + local T + for a = -5:25, s = [-5:-1;1:25;], d = 1:25, n = -1:15 denom = convert(T,d) strt = convert(T,a)/denom Δ = convert(T,s)/denom @@ -536,6 +538,7 @@ for T = (Float32, Float64,),# BigFloat), @test [r[2:2:n];] == [r;][2:2:n] @test [r[n:-1:2];] == [r;][n:-1:2] @test [r[n:-2:1];] == [r;][n:-2:1] + end end # issue #20373 (unliftable ranges with exact end points) @@ -650,6 +653,7 @@ let 0.0:0.1:1.0, map(Float32,0.0:0.1:1.0), linspace(0, 1, 20), map(Float32, linspace(0, 1, 20))] for r in Rs + local r ar = collect(r) @test r != ar @test !isequal(r,ar) @@ -732,6 +736,7 @@ r7484 = 0.1:0.1:1 # issue #7387 for r in (0:1, 0.0:1.0) + local r @test [r+im;] == [r;]+im @test [r-im;] == [r;]-im @test [r*im;] == [r;]*im @@ -991,6 +996,7 @@ end # Issue #13738 for r in (big(1):big(2), UInt128(1):UInt128(2), 0x1:0x2) + local r rr = r[r] @test typeof(rr) == typeof(r) @test r[r] == r @@ -1044,6 +1050,7 @@ r = Base.OneTo(3) @test r+r === 2:2:6 k = 0 for i in r + local i @test i == (k+=1) end @test intersect(r, Base.OneTo(2)) == Base.OneTo(2) @@ -1074,6 +1081,7 @@ a, b = rand(10), rand(10) r = linspace(a, b, 5) @test r[1] == a && r[5] == b for i = 2:4 + local i x = ((5-i)//4)*a + ((i-1)//4)*b @test r[i] == x end @@ -1089,6 +1097,7 @@ r = @inferred(colon(big(1.0),big(2.0),big(5.0))) # issue #14420 for r in (linspace(0.10000000000000045, 1), 0.10000000000000045:(1-0.10000000000000045)/49:1) + local r @test r[1] === 0.10000000000000045 @test r[end] === 1.0 end diff --git a/test/read.jl b/test/read.jl index c68076d9010f9..e3b8cf002887e 100644 --- a/test/read.jl +++ b/test/read.jl @@ -123,8 +123,8 @@ end open_streams = [] function cleanup() - for s in open_streams - try close(s) end + for s_ in open_streams + try close(s_) end end empty!(open_streams) for tsk in tasks @@ -138,6 +138,7 @@ verbose = false for (name, f) in l + local f io = ()->(s=f(text); push!(open_streams, s); s) write(filename, text) @@ -176,13 +177,14 @@ for (name, f) in l old_text = text cleanup() - for text in [ + for text_ in [ old_text, String(Char['A' + i % 52 for i in 1:(div(Base.SZ_UNBUFFERED_IO,2))]), String(Char['A' + i % 52 for i in 1:( Base.SZ_UNBUFFERED_IO -1)]), String(Char['A' + i % 52 for i in 1:( Base.SZ_UNBUFFERED_IO )]), String(Char['A' + i % 52 for i in 1:( Base.SZ_UNBUFFERED_IO +1)]) ] + text = text_ write(filename, text) verbose && println("$name read(io, String)...") @@ -311,7 +313,7 @@ function test_read_nbyte() fn = tempname() # Write one byte. One byte read should work once # but 2-byte read should throw EOFError. - f = open(fn, "w+") do f + open(fn, "w+") do f write(f, 0x55) flush(f) seek(f, 0) diff --git a/test/sets.jl b/test/sets.jl index 93f6022d53e8e..3efb3804be7bf 100644 --- a/test/sets.jl +++ b/test/sets.jl @@ -120,9 +120,9 @@ let end # start, done, next -for data_in in ((7,8,4,5), - ("hello", 23, 2.7, (), [], (1,8))) - s = Set(data_in) +for data_ in ((7,8,4,5), + ("hello", 23, 2.7, (), [], (1,8))) + s = Set(data_) s_new = Set() for el in s diff --git a/test/show.jl b/test/show.jl index 5d576dcd2920d..3cdc15ef24706 100644 --- a/test/show.jl +++ b/test/show.jl @@ -343,6 +343,7 @@ end" # issue #9474 for s in ("(1::Int64 == 1::Int64)::Bool", "(1:2:3) + 4", "x = 1:2:3") + local s @test sprint(show, parse(s)) == ":("*s*")" end @@ -874,7 +875,7 @@ end (Pair{Integer,Int64}(1, 2) => 3) => "Pair{Integer,Int64}(1, 2) => 3", ((1+2im) => (3+4im)) => "1+2im => 3+4im", (1 => 2 => Pair{Real,Int64}(3, 4)) => "1 => (2=>Pair{Real,Int64}(3, 4))") - + local s @test sprint(show, p) == s end # - when the context has :compact=>false, print pair's member non-compactly diff --git a/test/sparse/cholmod.jl b/test/sparse/cholmod.jl index 936f6059e6a37..d4c4a5c211919 100644 --- a/test/sparse/cholmod.jl +++ b/test/sparse/cholmod.jl @@ -668,6 +668,7 @@ let Apre = sprandn(10, 10, 0.2) - I for A in (Symmetric(Apre), Hermitian(Apre), Symmetric(Apre + 10I), Hermitian(Apre + 10I), Hermitian(complex(Apre)), Hermitian(complex(Apre) + 10I)) + local A x = ones(10) b = A*x @test x ≈ A\b @@ -687,14 +688,13 @@ let A = sprandn(10, 10, 0.1) end end -@testset "Check inputs to Sparse. Related to #20024" for A in ( +@testset "Check inputs to Sparse. Related to #20024" for A_ in ( SparseMatrixCSC(2, 2, [1, 2], CHOLMOD.SuiteSparse_long[], Float64[]), SparseMatrixCSC(2, 2, [1, 2, 3], CHOLMOD.SuiteSparse_long[1], Float64[]), SparseMatrixCSC(2, 2, [1, 2, 3], CHOLMOD.SuiteSparse_long[], Float64[1.0]), SparseMatrixCSC(2, 2, [1, 2, 3], CHOLMOD.SuiteSparse_long[1], Float64[1.0])) - - @test_throws ArgumentError CHOLMOD.Sparse(size(A)..., A.colptr - 1, A.rowval - 1, A.nzval) - @test_throws ArgumentError CHOLMOD.Sparse(A) + @test_throws ArgumentError CHOLMOD.Sparse(size(A_)..., A_.colptr - 1, A_.rowval - 1, A_.nzval) + @test_throws ArgumentError CHOLMOD.Sparse(A_) end @testset "sparse right multiplication of Symmetric and Hermitian matrices #21431" begin @@ -714,6 +714,7 @@ AtA = A'*A; C0 = [1., 2., 0, 0, 0] #Test both cholfact and LDLt with and without automatic permutations for F in (cholfact(AtA), cholfact(AtA, perm=1:5), ldltfact(AtA), ldltfact(AtA, perm=1:5)) + local F B0 = F\ones(5) #Test both sparse/dense and vectors/matrices for Ctest in (C0, sparse(C0), [C0 2*C0], sparse([C0 2*C0])) diff --git a/test/sparse/sparse.jl b/test/sparse/sparse.jl index 30e62f354afab..e813328c835ee 100644 --- a/test/sparse/sparse.jl +++ b/test/sparse/sparse.jl @@ -1095,14 +1095,14 @@ end N=2^3 Irand = randperm(M) Jrand = randperm(N) - I = sort([Irand; Irand; Irand]) + II = sort([Irand; Irand; Irand]) J = [Jrand; Jrand] SA = [sprand(M, N, d) for d in [1., 0.1, 0.01, 0.001, 0.0001, 0.]] for S in SA res = Any[1,2,3] for searchtype in [0, 1, 2] - res[searchtype+1] = test_getindex_algs(S, I, J, searchtype) + res[searchtype+1] = test_getindex_algs(S, II, J, searchtype) end @test res[1] == res[2] == res[3] @@ -1110,12 +1110,12 @@ end M = 2^14 N=2^4 - I = randperm(M) + II = randperm(M) J = randperm(N) Jsorted = sort(J) SA = [sprand(M, N, d) for d in [1., 0.1, 0.01, 0.001, 0.0001, 0.]] - IA = [I[1:round(Int,n)] for n in [M, M*0.1, M*0.01, M*0.001, M*0.0001, 0.]] + IA = [II[1:round(Int,n)] for n in [M, M*0.1, M*0.01, M*0.001, M*0.0001, 0.]] debug = false if debug @printf(" | | | times | memory |\n") diff --git a/test/spawn.jl b/test/spawn.jl index 43787d66871d7..f87f6ba322e42 100644 --- a/test/spawn.jl +++ b/test/spawn.jl @@ -453,9 +453,7 @@ if Sys.isunix() isa(ex, Base.UVError) || rethrow(ex) @test ex.code in (Base.UV_EMFILE, Base.UV_ENFILE) finally - for p in ps - close(p) - end + foreach(close, ps) end end end diff --git a/test/strings/basic.jl b/test/strings/basic.jl index 9f06e4a287f7b..4c2069bcf9782 100644 --- a/test/strings/basic.jl +++ b/test/strings/basic.jl @@ -464,6 +464,7 @@ foobaz(ch) = reinterpret(Char, typemax(UInt32)) # issue #18280: next/nextind must return past String's underlying data for s in ("Hello", "Σ", "こんにちは", "😊😁") + local s @test next(s, endof(s))[2] > sizeof(s) @test nextind(s, endof(s)) > sizeof(s) end diff --git a/test/strings/types.jl b/test/strings/types.jl index c4ff5227a02e2..a37193c4735fb 100644 --- a/test/strings/types.jl +++ b/test/strings/types.jl @@ -114,16 +114,19 @@ let s="lorem ipsum", SubString(s,12,14)=>"" ) for (ss,s) in sdict + local ss for i in -1:12 @test isvalid(ss,i)==isvalid(s,i) end end for (ss,s) in sdict + local ss for i in 1:length(ss) @test ind2chr(ss,i)==ind2chr(s,i) end end for (ss,s) in sdict + local ss for i in 1:length(ss) @test chr2ind(ss,i)==chr2ind(s,i) end diff --git a/test/topology.jl b/test/topology.jl index 4c1b982152e31..75a4c3ae52322 100644 --- a/test/topology.jl +++ b/test/topology.jl @@ -77,8 +77,8 @@ while true end end -for p1 in workers() - for p2 in workers() +for outer p1 in workers() + for outer p2 in workers() i1 = map_pid_ident[p1] i2 = map_pid_ident[p2] if (iseven(i1) && iseven(i2)) || (isodd(i1) && isodd(i2))