From 4a470cb174f8083acb79843261b1aab20d2fdf99 Mon Sep 17 00:00:00 2001 From: Ben Arthur Date: Mon, 29 May 2017 12:44:49 -0400 Subject: [PATCH 001/324] fix extrema(A,dim) when length(dim)>1 --- base/multidimensional.jl | 37 +++++++++++++++++-------------------- test/reduce.jl | 14 +++++++++++--- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 09fb0757562d5..b74a041c32798 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -1446,26 +1446,23 @@ function extrema(A::AbstractArray, dims) return extrema!(B, A) end -@generated function extrema!(B, A::AbstractArray{T,N}) where {T,N} - return quote - sA = size(A) - sB = size(B) - @nloops $N i B begin - AI = @nref $N A i - (@nref $N B i) = (AI, AI) - end - Bmax = sB - Istart = Int[sB[i] == 1 != sA[i] ? 2 : 1 for i = 1:ndims(A)] - @inbounds @nloops $N i d->(Istart[d]:size(A,d)) begin - AI = @nref $N A i - @nexprs $N d->(j_d = min(Bmax[d], i_{d})) - BJ = @nref $N B j - if AI < BJ[1] - (@nref $N B j) = (AI, BJ[2]) - elseif AI > BJ[2] - (@nref $N B j) = (BJ[1], AI) - end +@noinline function extrema!(B, A) + sA = size(A) + sB = size(B) + for I in CartesianRange(sB) + AI = A[I] + B[I] = (AI, AI) + end + Bmax = CartesianIndex(sB) + @inbounds @simd for I in CartesianRange(sA) + J = min(Bmax,I) + BJ = B[J] + AI = A[I] + if AI < BJ[1] + B[J] = (AI, BJ[2]) + elseif AI > BJ[2] + B[J] = (BJ[1], AI) end - return B end + return B end diff --git a/test/reduce.jl b/test/reduce.jl index cf89d564262f8..e537a83638b24 100644 --- a/test/reduce.jl +++ b/test/reduce.jl @@ -197,9 +197,17 @@ prod2(itr) = invoke(prod, Tuple{Any}, itr) @test maximum(collect(Int16(1):Int16(100))) === Int16(100) @test maximum(Int32[1,2]) === Int32(2) -@test extrema(reshape(1:24,2,3,4),1) == reshape([(1,2),(3,4),(5,6),(7,8),(9,10),(11,12),(13,14),(15,16),(17,18),(19,20),(21,22),(23,24)],1,3,4) -@test extrema(reshape(1:24,2,3,4),2) == reshape([(1,5),(2,6),(7,11),(8,12),(13,17),(14,18),(19,23),(20,24)],2,1,4) -@test extrema(reshape(1:24,2,3,4),3) == reshape([(1,19),(2,20),(3,21),(4,22),(5,23),(6,24)],2,3,1) +A = circshift(reshape(1:24,2,3,4), (0,1,1)) +@test extrema(A,1) == reshape([(23,24),(19,20),(21,22),(5,6),(1,2),(3,4),(11,12),(7,8),(9,10),(17,18),(13,14),(15,16)],1,3,4) +@test extrema(A,2) == reshape([(19,23),(20,24),(1,5),(2,6),(7,11),(8,12),(13,17),(14,18)],2,1,4) +@test extrema(A,3) == reshape([(5,23),(6,24),(1,19),(2,20),(3,21),(4,22)],2,3,1) +@test extrema(A,(1,2)) == reshape([(19,24),(1,6),(7,12),(13,18)],1,1,4) +@test extrema(A,(1,3)) == reshape([(5,24),(1,20),(3,22)],1,3,1) +@test extrema(A,(2,3)) == reshape([(1,23),(2,24)],2,1,1) +@test extrema(A,(1,2,3)) == reshape([(1,24)],1,1,1) +@test size(extrema(A,1)) == size(maximum(A,1)) +@test size(extrema(A,(1,2))) == size(maximum(A,(1,2))) +@test size(extrema(A,(1,2,3))) == size(maximum(A,(1,2,3))) # any & all From fe77221a74d0dbec27ca5a0d8debffbf8324ad83 Mon Sep 17 00:00:00 2001 From: "Scott R. Parish" Date: Sat, 3 Jan 2015 20:03:24 -0800 Subject: [PATCH 002/324] REPL/LineEdit to support "undo": part of #8447 The REPL now supports undo via Ctrl-^ or Ctrl-_. This should very closely minic the behavior of other readline/emacs shells, except it doesn't let the user goto a historical entry (A), edit it, goto a different historical entry (B), return to the first (A) and undo the edits. --- NEWS.md | 2 + base/repl/LineEdit.jl | 69 ++++++++++++++---- doc/src/manual/interacting-with-julia.md | 1 + test/lineedit.jl | 93 ++++++++++++++++++++++++ 4 files changed, 152 insertions(+), 13 deletions(-) diff --git a/NEWS.md b/NEWS.md index d35daff87fedb..7ffec7886f718 100644 --- a/NEWS.md +++ b/NEWS.md @@ -128,6 +128,8 @@ Library improvements * `Diagonal` is now parameterized on the type of the wrapped vector. This allows for `Diagonal` matrices with arbitrary `AbstractVector`s ([#22718]). + * REPL Undo via Ctrl-/ and Ctrl-_ + Compiler/Runtime improvements ----------------------------- diff --git a/base/repl/LineEdit.jl b/base/repl/LineEdit.jl index f76af08c8bf9d..2e9323d4d842e 100644 --- a/base/repl/LineEdit.jl +++ b/base/repl/LineEdit.jl @@ -60,6 +60,7 @@ mutable struct PromptState <: ModeState terminal::AbstractTerminal p::Prompt input_buffer::IOBuffer + undo_buffers::Vector{IOBuffer} ias::InputAreaState indent::Int end @@ -89,7 +90,8 @@ terminal(s::PromptState) = s.terminal for f in [:terminal, :edit_insert, :on_enter, :add_history, :buffer, :edit_backspace, :(Base.isempty), :replace_line, :refresh_multi_line, :input_string, :edit_move_left, :edit_move_right, - :edit_move_word_left, :edit_move_word_right, :update_display_buffer] + :edit_move_word_left, :edit_move_word_right, :update_display_buffer, + :empty_undo, :push_undo, :pop_undo] @eval ($f)(s::MIState, args...) = $(f)(s.mode_state[s.current_mode], args...) end @@ -148,16 +150,14 @@ function complete_line(s::PromptState, repeats) elseif length(completions) == 1 # Replace word by completion prev_pos = position(s.input_buffer) - seek(s.input_buffer, prev_pos-sizeof(partial)) - edit_replace(s, position(s.input_buffer), prev_pos, completions[1]) + edit_replace(s, prev_pos-sizeof(partial), prev_pos, completions[1]) else p = common_prefix(completions) if !isempty(p) && p != partial # All possible completions share the same prefix, so we might as # well complete that prev_pos = position(s.input_buffer) - seek(s.input_buffer, prev_pos-sizeof(partial)) - edit_replace(s, position(s.input_buffer), prev_pos, p) + edit_replace(s, prev_pos-sizeof(partial), prev_pos, p) elseif repeats > 0 show_completions(s, completions) end @@ -440,10 +440,12 @@ function splice_buffer!(buf::IOBuffer, r::UnitRange{<:Integer}, ins::AbstractStr end function edit_replace(s, from, to, str) + push_undo(s) splice_buffer!(buffer(s), from:to-1, str) end function edit_insert(s::PromptState, c) + push_undo(s) buf = s.input_buffer function line_size() p = position(buf) @@ -476,9 +478,11 @@ function edit_insert(buf::IOBuffer, c) end function edit_backspace(s::PromptState) + push_undo(s) if edit_backspace(s.input_buffer) refresh_line(s) else + pop_undo(s) beep(terminal(s)) end end @@ -493,7 +497,15 @@ function edit_backspace(buf::IOBuffer) end end -edit_delete(s) = edit_delete(buffer(s)) ? refresh_line(s) : beep(terminal(s)) +function edit_delete(s) + push_undo(s) + if edit_delete(buffer(s)) + refresh_line(s) + else + pop_undo(s) + beep(terminal(s)) + end +end function edit_delete(buf::IOBuffer) eof(buf) && return false oldpos = position(buf) @@ -511,7 +523,8 @@ function edit_werase(buf::IOBuffer) true end function edit_werase(s) - edit_werase(buffer(s)) && refresh_line(s) + push_undo(s) + edit_werase(buffer(s)) ? refresh_line(s) : pop_undo(s) end function edit_delete_prev_word(buf::IOBuffer) @@ -523,7 +536,8 @@ function edit_delete_prev_word(buf::IOBuffer) true end function edit_delete_prev_word(s) - edit_delete_prev_word(buffer(s)) && refresh_line(s) + push_undo(s) + edit_delete_prev_word(buffer(s)) ? refresh_line(s) : pop_undo(s) end function edit_delete_next_word(buf::IOBuffer) @@ -535,15 +549,18 @@ function edit_delete_next_word(buf::IOBuffer) true end function edit_delete_next_word(s) - edit_delete_next_word(buffer(s)) && refresh_line(s) + push_undo(s) + edit_delete_next_word(buffer(s)) ? refresh_line(s) : pop_undo(s) end function edit_yank(s::MIState) + push_undo(s) edit_insert(buffer(s), s.kill_buffer) refresh_line(s) end function edit_kill_line(s::MIState) + push_undo(s) buf = buffer(s) pos = position(buf) killbuf = readline(buf, chomp=false) @@ -557,7 +574,10 @@ function edit_kill_line(s::MIState) refresh_line(s) end -edit_transpose(s) = edit_transpose(buffer(s)) && refresh_line(s) +function edit_transpose(s) + push_undo(s) + edit_transpose(buffer(s)) ? refresh_line(s) : pop_undo(s) +end function edit_transpose(buf::IOBuffer) position(buf) == 0 && return false eof(buf) && char_move_left(buf) @@ -572,15 +592,18 @@ end edit_clear(buf::IOBuffer) = truncate(buf, 0) function edit_clear(s::MIState) + push_undo(s) edit_clear(buffer(s)) refresh_line(s) end function replace_line(s::PromptState, l::IOBuffer) + empty_undo(s) s.input_buffer = copy(l) end function replace_line(s::PromptState, l) + empty_undo(s) s.input_buffer.ptr = 1 s.input_buffer.size = 0 write(s.input_buffer, l) @@ -1120,8 +1143,7 @@ function complete_line(s::SearchState, repeats) # For now only allow exact completions in search mode if length(completions) == 1 prev_pos = position(s.query_buffer) - seek(s.query_buffer, prev_pos-sizeof(partial)) - edit_replace(s, position(s.query_buffer), prev_pos, completions[1]) + edit_replace(s, prev_pos-sizeof(partial), prev_pos, completions[1]) end end @@ -1390,6 +1412,7 @@ AnyDict( # Meta Enter "\e\r" => (s,o...)->(edit_insert(s, '\n')), "\e\n" => "\e\r", + "^_" => (s,o...)->(pop_undo(s) ? refresh_line(s) : beep(terminal(s))), # Simply insert it into the buffer by default "*" => (s,data,c)->(edit_insert(s, c)), "^U" => (s,o...)->edit_clear(s), @@ -1531,6 +1554,7 @@ function reset_state(s::PromptState) s.input_buffer.size = 0 s.input_buffer.ptr = 1 end + empty_undo(s) s.ias = InputAreaState(0, 0) end @@ -1559,7 +1583,9 @@ end run_interface(::Prompt) = nothing -init_state(terminal, prompt::Prompt) = PromptState(terminal, prompt, IOBuffer(), InputAreaState(1, 1), #=indent(spaces)=#strwidth(prompt.prompt)) +init_state(terminal, prompt::Prompt) = + PromptState(terminal, prompt, IOBuffer(), IOBuffer[], InputAreaState(1, 1), + #=indent(spaces)=#strwidth(prompt.prompt)) function init_state(terminal, m::ModalInterface) s = MIState(m, m.modes[1], false, Dict{Any,Any}()) @@ -1592,6 +1618,23 @@ buffer(s::PromptState) = s.input_buffer buffer(s::SearchState) = s.query_buffer buffer(s::PrefixSearchState) = s.response_buffer +function empty_undo(s::PromptState) + empty!(s.undo_buffers) +end +empty_undo(s) = nothing + +function push_undo(s::PromptState) + push!(s.undo_buffers, copy(s.input_buffer)) +end +push_undo(s) = nothing + +function pop_undo(s::PromptState) + length(s.undo_buffers) > 0 || return false + s.input_buffer = pop!(s.undo_buffers) + true +end +pop_undo(s) = nothing + keymap(s::PromptState, prompt::Prompt) = prompt.keymap_dict keymap_data(s::PromptState, prompt::Prompt) = prompt.keymap_func_data keymap(ms::MIState, m::ModalInterface) = keymap(ms.mode_state[ms.current_mode], ms.current_mode) diff --git a/doc/src/manual/interacting-with-julia.md b/doc/src/manual/interacting-with-julia.md index 27ea6a752be51..a50130defc506 100644 --- a/doc/src/manual/interacting-with-julia.md +++ b/doc/src/manual/interacting-with-julia.md @@ -172,6 +172,7 @@ to do so). | `^Y` | "Yank" insert the text from the kill buffer | | `^T` | Transpose the characters about the cursor | | `^Q` | Write a number in REPL and press `^Q` to open editor at corresponding stackframe or method | +| `^/`, `^_` | Undo | ### Customizing keybindings diff --git a/test/lineedit.jl b/test/lineedit.jl index 4aeca1b38797c..3abc6a4ee722a 100644 --- a/test/lineedit.jl +++ b/test/lineedit.jl @@ -401,3 +401,96 @@ let Base.LineEdit.InputAreaState(0,0), "julia> ", indent = 7) @test s == Base.LineEdit.InputAreaState(3,1) end + +# test Undo +let + term = TestHelpers.FakeTerminal(IOBuffer(), IOBuffer(), IOBuffer()) + s = LineEdit.init_state(term, ModalInterface([Prompt("test> ")])) + function bufferdata(s) + buf = LineEdit.buffer(s) + String(buf.data[1:buf.size]) + end + + LineEdit.edit_insert(s, "one two three") + + LineEdit.edit_delete_prev_word(s) + @test bufferdata(s) == "one two " + LineEdit.pop_undo(s) + @test bufferdata(s) == "one two three" + + LineEdit.edit_insert(s, " four") + LineEdit.edit_insert(s, " five") + @test bufferdata(s) == "one two three four five" + LineEdit.pop_undo(s) + @test bufferdata(s) == "one two three four" + LineEdit.pop_undo(s) + @test bufferdata(s) == "one two three" + + LineEdit.edit_clear(s) + @test bufferdata(s) == "" + LineEdit.pop_undo(s) + @test bufferdata(s) == "one two three" + + LineEdit.edit_move_left(s) + LineEdit.edit_move_left(s) + LineEdit.edit_transpose(s) + @test bufferdata(s) == "one two there" + LineEdit.pop_undo(s) + @test bufferdata(s) == "one two three" + + LineEdit.move_line_start(s) + LineEdit.edit_kill_line(s) + @test bufferdata(s) == "" + LineEdit.pop_undo(s) + @test bufferdata(s) == "one two three" + + LineEdit.move_line_start(s) + LineEdit.edit_kill_line(s) + LineEdit.edit_yank(s) + LineEdit.edit_yank(s) + @test bufferdata(s) == "one two threeone two three" + LineEdit.pop_undo(s) + @test bufferdata(s) == "one two three" + LineEdit.pop_undo(s) + @test bufferdata(s) == "" + LineEdit.pop_undo(s) + @test bufferdata(s) == "one two three" + + LineEdit.move_line_end(s) + LineEdit.edit_backspace(s) + LineEdit.edit_backspace(s) + LineEdit.edit_backspace(s) + @test bufferdata(s) == "one two th" + LineEdit.pop_undo(s) + @test bufferdata(s) == "one two thr" + LineEdit.pop_undo(s) + @test bufferdata(s) == "one two thre" + LineEdit.pop_undo(s) + @test bufferdata(s) == "one two three" + + LineEdit.edit_replace(s, 4, 7, "stott") + @test bufferdata(s) == "one stott three" + LineEdit.pop_undo(s) + @test bufferdata(s) == "one two three" + + LineEdit.edit_move_left(s) + LineEdit.edit_move_left(s) + LineEdit.edit_move_left(s) + LineEdit.edit_delete(s) + @test bufferdata(s) == "one two thee" + LineEdit.pop_undo(s) + @test bufferdata(s) == "one two three" + + LineEdit.edit_move_word_left(s) + LineEdit.edit_werase(s) + LineEdit.edit_delete_next_word(s) + @test bufferdata(s) == "one " + LineEdit.pop_undo(s) + @test bufferdata(s) == "one three" + LineEdit.pop_undo(s) + @test bufferdata(s) == "one two three" + + # pop initial insert of "one two three" + LineEdit.pop_undo(s) + @test bufferdata(s) == "" +end From 52d0affb7ae8c48090b5c5beb4f0955fcddd7fb9 Mon Sep 17 00:00:00 2001 From: Stefan Karpinski Date: Mon, 31 Jul 2017 00:16:39 -0400 Subject: [PATCH 003/324] VersionNumber: make number type platform-independent (UInt32) It seemed strange that we could represent higher version numbers on 64-bit systems than 32-bit systems (not that it practically matters). This commit makes the representation of version numbers platform-independent, using UInt32 everywhere instead of Int. --- base/pkg/resolve/versionweight.jl | 4 +- base/version.jl | 64 +++++++++++++++++-------------- test/version.jl | 4 ++ 3 files changed, 42 insertions(+), 30 deletions(-) diff --git a/base/pkg/resolve/versionweight.jl b/base/pkg/resolve/versionweight.jl index 440483181c4c9..38c091cafc12f 100644 --- a/base/pkg/resolve/versionweight.jl +++ b/base/pkg/resolve/versionweight.jl @@ -74,7 +74,7 @@ struct VWPreBuildItem i::Int end VWPreBuildItem() = VWPreBuildItem(0, HierarchicalValue(Int), 0) -VWPreBuildItem(i::Int) = VWPreBuildItem(1, HierarchicalValue(Int), i) +VWPreBuildItem(i::Integer) = VWPreBuildItem(1, HierarchicalValue(Int), i) VWPreBuildItem(s::String) = VWPreBuildItem(1, HierarchicalValue(Int[s...]), 0) Base.zero(::Type{VWPreBuildItem}) = VWPreBuildItem() @@ -105,7 +105,7 @@ end const _vwprebuild_zero = VWPreBuild(0, HierarchicalValue(VWPreBuildItem)) -function VWPreBuild(ispre::Bool, desc::Tuple{Vararg{Union{Int,String}}}) +function VWPreBuild(ispre::Bool, desc::Tuple{Vararg{Union{Integer,String}}}) isempty(desc) && return _vwprebuild_zero desc == ("",) && return VWPreBuild(ispre ? -1 : 1, HierarchicalValue(VWPreBuildItem[])) nonempty = ispre ? -1 : 0 diff --git a/base/version.jl b/base/version.jl index 05019bb0eb34f..9e18099faeae4 100644 --- a/base/version.jl +++ b/base/version.jl @@ -2,21 +2,23 @@ ## semantic version numbers (http://semver.org) +const VInt = UInt32 + struct VersionNumber - major::Int - minor::Int - patch::Int - prerelease::Tuple{Vararg{Union{Int,String}}} - build::Tuple{Vararg{Union{Int,String}}} - - function VersionNumber(major::Int, minor::Int, patch::Int, - pre::Tuple{Vararg{Union{Int,String}}}, - bld::Tuple{Vararg{Union{Int,String}}}) + major::VInt + minor::VInt + patch::VInt + prerelease::Tuple{Vararg{Union{UInt64,String}}} + build::Tuple{Vararg{Union{UInt64,String}}} + + function VersionNumber(major::VInt, minor::VInt, patch::VInt, + pre::Tuple{Vararg{Union{UInt64,String}}}, + bld::Tuple{Vararg{Union{UInt64,String}}}) major >= 0 || throw(ArgumentError("invalid negative major version: $major")) minor >= 0 || throw(ArgumentError("invalid negative minor version: $minor")) patch >= 0 || throw(ArgumentError("invalid negative patch version: $patch")) for ident in pre - if isa(ident,Int) + if ident isa Integer ident >= 0 || throw(ArgumentError("invalid negative pre-release identifier: $ident")) else if !ismatch(r"^(?:|[0-9a-z-]*[a-z-][0-9a-z-]*)$"i, ident) || @@ -26,7 +28,7 @@ struct VersionNumber end end for ident in bld - if isa(ident,Int) + if ident isa Integer ident >= 0 || throw(ArgumentError("invalid negative build identifier: $ident")) else if !ismatch(r"^(?:|[0-9a-z-]*[a-z-][0-9a-z-]*)$"i, ident) || @@ -41,9 +43,9 @@ end VersionNumber(major::Integer, minor::Integer = 0, patch::Integer = 0, pre::Tuple{Vararg{Union{Integer,AbstractString}}} = (), bld::Tuple{Vararg{Union{Integer,AbstractString}}} = ()) = - VersionNumber(Int(major), Int(minor), Int(patch), - map(x->isa(x,Integer) ? Int(x) : String(x), pre), - map(x->isa(x,Integer) ? Int(x) : String(x), bld)) + VersionNumber(VInt(major), VInt(minor), VInt(patch), + map(x->x isa Integer ? UInt64(x) : String(x), pre), + map(x->x isa Integer ? UInt64(x) : String(x), bld)) function print(io::IO, v::VersionNumber) v == typemax(VersionNumber) && return print(io, "∞") @@ -82,7 +84,7 @@ function split_idents(s::AbstractString) idents = split(s, '.') ntuple(length(idents)) do i ident = idents[i] - ismatch(r"^\d+$", ident) ? parse(Int, ident) : String(ident) + ismatch(r"^\d+$", ident) ? parse(UInt64, ident) : String(ident) end end @@ -91,9 +93,9 @@ function VersionNumber(v::AbstractString) m = match(VERSION_REGEX, v) m === nothing && throw(ArgumentError("invalid version string: $v")) major, minor, patch, minus, prerl, plus, build = m.captures - major = parse(Int, major) - minor = minor !== nothing ? parse(Int, minor) : 0 - patch = patch !== nothing ? parse(Int, patch) : 0 + major = parse(VInt, major) + minor = minor !== nothing ? parse(VInt, minor) : VInt(0) + patch = patch !== nothing ? parse(VInt, patch) : VInt(0) if prerl !== nothing && !isempty(prerl) && prerl[1] == '-' prerl = prerl[2:end] # strip leading '-' end @@ -107,15 +109,21 @@ convert(::Type{VersionNumber}, v::AbstractString) = VersionNumber(v) macro v_str(v); VersionNumber(v); end typemin(::Type{VersionNumber}) = v"0-" -typemax(::Type{VersionNumber}) = VersionNumber(typemax(Int),typemax(Int),typemax(Int),(),("",)) -ident_cmp(a::Int, b::Int) = cmp(a,b) -ident_cmp(a::Int, b::String) = isempty(b) ? +1 : -1 -ident_cmp(a::String, b::Int) = isempty(a) ? -1 : +1 -ident_cmp(a::String, b::String) = cmp(a,b) +function typemax(::Type{VersionNumber}) + ∞ = typemax(VInt) + VersionNumber(∞, ∞, ∞, (), ("",)) +end + +ident_cmp(a::Integer, b::Integer) = cmp(a, b) +ident_cmp(a::Integer, b::String ) = isempty(b) ? +1 : -1 +ident_cmp(a::String, b::Integer) = isempty(a) ? -1 : +1 +ident_cmp(a::String, b::String ) = cmp(a, b) -function ident_cmp(A::Tuple{Vararg{Union{Int,String}}}, - B::Tuple{Vararg{Union{Int,String}}}) +function ident_cmp( + A::Tuple{Vararg{Union{Integer,String}}}, + B::Tuple{Vararg{Union{Integer,String}}}, +) i = start(A) j = start(B) while !done(A,i) && !done(B,i) @@ -132,8 +140,8 @@ function ==(a::VersionNumber, b::VersionNumber) (a.major != b.major) && return false (a.minor != b.minor) && return false (a.patch != b.patch) && return false - (ident_cmp(a.prerelease,b.prerelease) != 0) && return false - (ident_cmp(a.build,b.build) != 0) && return false + (ident_cmp(a.prerelease, b.prerelease) != 0) && return false + (ident_cmp(a.build, b.build) != 0) && return false return true end @@ -186,7 +194,7 @@ function check_new_version(existing::Vector{VersionNumber}, ver::VersionNumber) end error("$ver is not a valid initial version (try 0.0.0, 0.0.1, 0.1 or 1.0)") end - idx = searchsortedlast(existing,ver) + idx = searchsortedlast(existing, ver) prv = existing[idx] ver == prv && error("version $ver already exists") nxt = thismajor(ver) != thismajor(prv) ? nextmajor(prv) : diff --git a/test/version.jl b/test/version.jl index 172e5e2cac3b6..6bc50a54843b7 100644 --- a/test/version.jl +++ b/test/version.jl @@ -95,6 +95,10 @@ show(io,v"4.3.2+1.a") # typemin and typemax @test typemin(VersionNumber) == v"0-" @test typemax(VersionNumber) == v"∞" +let ∞ = typemax(UInt32) + @test typemin(VersionNumber) == VersionNumber(0, 0, 0, ("",), ()) + @test typemax(VersionNumber) == VersionNumber(∞, ∞, ∞, (), ("",)) +end # issupbuild import Base.issupbuild From 4d0bb42eab2615cc476f53fcb9728a908d174fd5 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Wed, 9 Aug 2017 17:36:34 +0200 Subject: [PATCH 004/324] REPL: complete after using: modules can contain dots e.g. in the following, the key should complete only with modules names, i.e. "Random", but not "RandomDevice" julia> using Base.Test, Base.Random --- base/repl/REPLCompletions.jl | 2 +- test/replcompletions.jl | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/base/repl/REPLCompletions.jl b/base/repl/REPLCompletions.jl index ded84842751ab..bdb84d972b933 100644 --- a/base/repl/REPLCompletions.jl +++ b/base/repl/REPLCompletions.jl @@ -368,7 +368,7 @@ function afterusing(string::String, startpos::Int) r = search(rstr, r"\s(gnisu|tropmi)\b") isempty(r) && return false fr = reverseind(str, last(r)) - return ismatch(r"^\b(using|import)\s*(\w+\s*,\s*)*\w*$", str[fr:end]) + return ismatch(r"^\b(using|import)\s*((\w+[.])*\w+\s*,\s*)*\w*$", str[fr:end]) end function bslash_completions(string, pos) diff --git a/test/replcompletions.jl b/test/replcompletions.jl index 43f8f8732e6b3..6e4630aa52eaf 100644 --- a/test/replcompletions.jl +++ b/test/replcompletions.jl @@ -162,6 +162,11 @@ c,r = test_complete(s) @test r == 19:23 @test s[r] == "getin" +# issue #23193: after `using`, identifiers can be prefixed by module names +s = "using Base.Test, Base.Random" +c,r = test_complete(s) +@test !("RandomDevice" in c) + # inexistent completion inside a string s = "Pkg.add(\"lol" c,r,res = test_complete(s) From 5f582aece525b2ab6cc7a9cfdf40085c75d34bb8 Mon Sep 17 00:00:00 2001 From: Klaus Crusius Date: Wed, 9 Aug 2017 22:47:25 +0200 Subject: [PATCH 005/324] min, max, minmax use isless for comparison (fix #23094) (#23155) --- base/array.jl | 10 ++++++---- base/operators.jl | 6 +++--- test/arrayops.jl | 14 ++++++++++++++ test/operators.jl | 23 +++++++++++++++++++++++ 4 files changed, 46 insertions(+), 7 deletions(-) diff --git a/base/array.jl b/base/array.jl index d88aac162d9f1..300512537ad8d 100644 --- a/base/array.jl +++ b/base/array.jl @@ -1697,7 +1697,7 @@ end Returns the maximum element of the collection `itr` and its index. If there are multiple maximal elements, then the first one will be returned. `NaN` values are ignored, unless -all elements are `NaN`. +all elements are `NaN`. Other than the treatment of `NaN`, the result is in line with `max`. The collection must not be empty. @@ -1723,7 +1723,8 @@ function findmax(a) while !done(a, s) ai, s = next(a, s) i += 1 - if ai > m || m!=m + ai != ai && continue # assume x != x => x is a NaN + if m != m || isless(m, ai) m = ai mi = i end @@ -1736,7 +1737,7 @@ end Returns the minimum element of the collection `itr` and its index. If there are multiple minimal elements, then the first one will be returned. `NaN` values are ignored, unless -all elements are `NaN`. +all elements are `NaN`. Other than the treatment of `NaN`, the result is in line with `min`. The collection must not be empty. @@ -1762,7 +1763,8 @@ function findmin(a) while !done(a, s) ai, s = next(a, s) i += 1 - if ai < m || m!=m + ai != ai && continue + if m != m || isless(ai, m) m = ai mi = i end diff --git a/base/operators.jl b/base/operators.jl index afdd22823fd06..0228d78bc6d4a 100644 --- a/base/operators.jl +++ b/base/operators.jl @@ -359,7 +359,7 @@ julia> max(2, 5, 1) 5 ``` """ -max(x, y) = ifelse(y < x, x, y) +max(x, y) = ifelse(isless(y, x), x, y) """ min(x, y, ...) @@ -373,7 +373,7 @@ julia> min(2, 5, 1) 1 ``` """ -min(x,y) = ifelse(y < x, y, x) +min(x,y) = ifelse(isless(y, x), y, x) """ minmax(x, y) @@ -386,7 +386,7 @@ julia> minmax('c','b') ('b', 'c') ``` """ -minmax(x,y) = y < x ? (y, x) : (x, y) +minmax(x,y) = isless(y, x) ? (y, x) : (x, y) scalarmax(x,y) = max(x,y) scalarmax(x::AbstractArray, y::AbstractArray) = throw(ArgumentError("ordering is not well-defined for arrays")) diff --git a/test/arrayops.jl b/test/arrayops.jl index 50ef1ca46a443..9055e55ba49df 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -495,6 +495,20 @@ end @test indmax(5:-2:1) == 1 @test findmin(5:-2:1) == (1,3) @test indmin(5:-2:1) == 3 + + #23094 + @test findmax(Set(["abc"])) === ("abc", 1) + @test findmin(["abc", "a"]) === ("a", 2) + @test_throws MethodError findmax([Set([1]), Set([2])]) + @test findmin([0.0, -0.0]) === (-0.0, 2) + @test findmax([0.0, -0.0]) === (0.0, 1) + @test findmin([-0.0, 0.0]) === (-0.0, 1) + @test findmax([-0.0, 0.0]) === (0.0, 2) + @test isnan(findmin([NaN, NaN, 0.0/0.0])[1]) + @test findmin([NaN, NaN, 0.0/0.0])[2] == 1 + @test isnan(findmax([NaN, NaN, 0.0/0.0])[1]) + @test findmax([NaN, NaN, 0.0/0.0])[2] == 1 + end @testset "permutedims" begin diff --git a/test/operators.jl b/test/operators.jl index e79d3f5a93d53..72effdbf33f51 100644 --- a/test/operators.jl +++ b/test/operators.jl @@ -58,6 +58,29 @@ p = 1=>:foo @test_throws ArgumentError Base.scalarmax('a',['c','d']) @test_throws ArgumentError Base.scalarmax(['a','b'],'c') +@test_throws MethodError min(Set([1]), Set([2])) +@test_throws MethodError max(Set([1]), Set([2])) +@test_throws MethodError minmax(Set([1]), Set([2])) + +# Test if isless (not <) is used by min, max, minmax +# and commutativity. +struct TO23094 + x::Int +end +Base.isless(a::TO23094, b::TO23094) = isless(a.x, b.x) +Base.isequal(a::TO23094, b::TO23094) = isequal(a.x, b.x) +import Base.< +<(a::TO23094, b::TO23094) = error("< should not be called") + +@test isequal(min(TO23094(1), TO23094(2)), TO23094(1)) +@test isequal(min(TO23094(2), TO23094(1)), TO23094(1)) +@test isequal(max(TO23094(1), TO23094(2)), TO23094(2)) +@test isequal(max(TO23094(2), TO23094(1)), TO23094(2)) +@test isequal(minmax(TO23094(1), TO23094(2))[1], TO23094(1)) +@test isequal(minmax(TO23094(1), TO23094(2))[2], TO23094(2)) +@test isequal(minmax(TO23094(2), TO23094(1))[1], TO23094(1)) +@test isequal(minmax(TO23094(2), TO23094(1))[2], TO23094(2)) + @test lexless('a','b') @test 1 .!= 2 From 4e471fe09411e1aa23ec28c0e8ec974132067ee4 Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Wed, 9 Aug 2017 19:01:54 -0400 Subject: [PATCH 006/324] Adding methods of A?_mul_B? when A and B are both diagonal (#22428) * Add methods of A?_mul_B? in diagonal cases --- base/linalg/diagonal.jl | 7 +++++++ test/linalg/diagonal.jl | 15 +++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/base/linalg/diagonal.jl b/base/linalg/diagonal.jl index f146773d7e9b0..439e409958c52 100644 --- a/base/linalg/diagonal.jl +++ b/base/linalg/diagonal.jl @@ -186,6 +186,7 @@ function A_mul_B!(D::Diagonal, B::UnitUpperTriangular) UpperTriangular(B.data) end +Ac_mul_B(D::Diagonal, B::Diagonal) = Diagonal(ctranspose.(D.diag) .* B.diag) Ac_mul_B(A::AbstractTriangular, D::Diagonal) = A_mul_B!(ctranspose(A), D) function Ac_mul_B(A::AbstractMatrix, D::Diagonal) Ac = similar(A, promote_op(*, eltype(A), eltype(D.diag)), (size(A, 2), size(A, 1))) @@ -193,6 +194,7 @@ function Ac_mul_B(A::AbstractMatrix, D::Diagonal) A_mul_B!(Ac, D) end +At_mul_B(D::Diagonal, B::Diagonal) = Diagonal(transpose.(D.diag) .* B.diag) At_mul_B(A::AbstractTriangular, D::Diagonal) = A_mul_B!(transpose(A), D) function At_mul_B(A::AbstractMatrix, D::Diagonal) At = similar(A, promote_op(*, eltype(A), eltype(D.diag)), (size(A, 2), size(A, 1))) @@ -200,6 +202,7 @@ function At_mul_B(A::AbstractMatrix, D::Diagonal) A_mul_B!(At, D) end +A_mul_Bc(D::Diagonal, B::Diagonal) = Diagonal(D.diag .* ctranspose.(B.diag)) A_mul_Bc(D::Diagonal, B::AbstractTriangular) = A_mul_B!(D, ctranspose(B)) A_mul_Bc(D::Diagonal, Q::Union{Base.LinAlg.QRCompactWYQ,Base.LinAlg.QRPackedQ}) = A_mul_Bc!(Array(D), Q) function A_mul_Bc(D::Diagonal, A::AbstractMatrix) @@ -208,6 +211,7 @@ function A_mul_Bc(D::Diagonal, A::AbstractMatrix) A_mul_B!(D, Ac) end +A_mul_Bt(D::Diagonal, B::Diagonal) = Diagonal(D.diag .* transpose.(B.diag)) A_mul_Bt(D::Diagonal, B::AbstractTriangular) = A_mul_B!(D, transpose(B)) function A_mul_Bt(D::Diagonal, A::AbstractMatrix) At = similar(A, promote_op(*, eltype(A), eltype(D.diag)), (size(A, 2), size(A, 1))) @@ -215,6 +219,9 @@ function A_mul_Bt(D::Diagonal, A::AbstractMatrix) A_mul_B!(D, At) end +Ac_mul_Bc(D::Diagonal, B::Diagonal) = Diagonal(ctranspose.(D.diag) .* ctranspose.(B.diag)) +At_mul_Bt(D::Diagonal, B::Diagonal) = Diagonal(transpose.(D.diag) .* transpose.(B.diag)) + A_mul_B!(A::Diagonal,B::Diagonal) = throw(MethodError(A_mul_B!, Tuple{Diagonal,Diagonal})) At_mul_B!(A::Diagonal,B::Diagonal) = throw(MethodError(At_mul_B!, Tuple{Diagonal,Diagonal})) Ac_mul_B!(A::Diagonal,B::Diagonal) = throw(MethodError(Ac_mul_B!, Tuple{Diagonal,Diagonal})) diff --git a/test/linalg/diagonal.jl b/test/linalg/diagonal.jl index a159e441bb6cc..b1e8df0bd3ffb 100644 --- a/test/linalg/diagonal.jl +++ b/test/linalg/diagonal.jl @@ -400,3 +400,18 @@ end end end end + +@testset "multiplication of transposes of Diagonal (#22428)" begin + for T in (Float64, Complex{Float64}) + D = Diagonal(randn(T, 5, 5)) + B = Diagonal(randn(T, 5, 5)) + DD = Diagonal([randn(T, 2, 2), rand(T, 2, 2)]) + BB = Diagonal([randn(T, 2, 2), rand(T, 2, 2)]) + fullDD = copy!(Matrix{Matrix{T}}(2, 2), DD) + fullBB = copy!(Matrix{Matrix{T}}(2, 2), BB) + for f in (*, Ac_mul_B, A_mul_Bc, Ac_mul_Bc, At_mul_B, A_mul_Bt, At_mul_Bt) + @test f(D, B)::typeof(D) == f(Matrix(D), Matrix(B)) + @test f(DD, BB)::typeof(DD) == f(fullDD, fullBB) + end + end +end From 01dd35ac09eb378252bf5583240056356e4e8c1a Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Thu, 10 Aug 2017 14:28:47 +0200 Subject: [PATCH 007/324] deprecate cpad (#23187) --- NEWS.md | 3 +++ base/deprecated.jl | 3 +++ base/pkg/entry.jl | 3 ++- base/strings/util.jl | 1 - 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index 07ccb1881e301..c222644b90680 100644 --- a/NEWS.md +++ b/NEWS.md @@ -299,6 +299,9 @@ Deprecated or removed * Calling `union` with no arguments is deprecated; construct an empty set with an appropriate element type using `Set{T}()` instead ([#23144]). + * `Base.cpad` has been removed; use an appropriate combination of `rpad` and `lpad` + instead ([#23187]). + Julia v0.6.0 Release Notes ========================== diff --git a/base/deprecated.jl b/base/deprecated.jl index 196ca8270a25b..54f57c816bf34 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1642,6 +1642,9 @@ end end end +# PR #23187 +@deprecate cpad(s, n::Integer, p=" ") rpad(lpad(s, div(n+strwidth(s), 2), p), n, p) false + # END 0.7 deprecations # BEGIN 1.0 deprecations diff --git a/base/pkg/entry.jl b/base/pkg/entry.jl index 41f638102cfdb..58487eac71db1 100644 --- a/base/pkg/entry.jl +++ b/base/pkg/entry.jl @@ -572,7 +572,8 @@ end function warnbanner(msg...; label="[ WARNING ]", prefix="") cols = Base.displaysize(STDERR)[2] - warn(prefix="", Base.cpad(label,cols,"=")) + str = rpad(lpad(label, div(cols+strwidth(label), 2), "="), cols, "=") + warn(prefix="", str) println(STDERR) warn(prefix=prefix, msg...) println(STDERR) diff --git a/base/strings/util.jl b/base/strings/util.jl index d8f665a28b13e..f7297ae6810fc 100644 --- a/base/strings/util.jl +++ b/base/strings/util.jl @@ -256,7 +256,6 @@ julia> rpad("March",20) ``` """ rpad(s, n::Integer, p=" ") = rpad(string(s),n,string(p)) -cpad(s, n::Integer, p=" ") = rpad(lpad(s,div(n+strwidth(s),2),p),n,p) # splitter can be a Char, Vector{Char}, AbstractString, Regex, ... # any splitter that provides search(s::AbstractString, splitter) From 8c7f01e974de7ae779f22343086fdd400efe16f2 Mon Sep 17 00:00:00 2001 From: Sacha Verweij Date: Thu, 10 Aug 2017 10:12:12 -0700 Subject: [PATCH 008/324] Touch up news entry for #23117 and add link to PR. (#23196) --- NEWS.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/NEWS.md b/NEWS.md index c222644b90680..81209168d808d 100644 --- a/NEWS.md +++ b/NEWS.md @@ -55,13 +55,13 @@ Language changes contains `sin` eagerly, rather than delaying that decision until `f` is run. ([#22984]). * Dispatch rules have been simplified: - matching methods is now determined exclusively by subtyping; - the rule that method type parameters must be also be captured has been removed. - Instead, attempting to access the uncontrained parameters will throw an `UndefVarError`. + method matching is now determined exclusively by subtyping; + the rule that method type parameters must also be captured has been removed. + Instead, attempting to access the unconstrained parameters will throw an `UndefVarError`. Linting in package tests is recommended to confirm that the set of methods which might throw `UndefVarError` when accessing the static parameters (`need_to_handle_undef_sparam = Set{Any}(m.sig for m in Test.detect_unbound_args(Base, recursive=true))`) - is equal (`==`) to some known set (`expected = Set()`). ([#TBD]) + is equal (`==`) to some known set (`expected = Set()`). ([#23117]) Breaking changes @@ -1151,4 +1151,8 @@ Command-line option changes [#22868]: https://github.com/JuliaLang/julia/issues/22868 [#22925]: https://github.com/JuliaLang/julia/issues/22925 [#22961]: https://github.com/JuliaLang/julia/issues/22961 +[#22984]: https://github.com/JuliaLang/julia/issues/22984 [#23035]: https://github.com/JuliaLang/julia/issues/23035 +[#23117]: https://github.com/JuliaLang/julia/issues/23117 +[#23144]: https://github.com/JuliaLang/julia/issues/23144 +[#23157]: https://github.com/JuliaLang/julia/issues/23157 From f87dea2d0075596a3548db1451db3594fdcd9422 Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Thu, 10 Aug 2017 12:43:31 -0500 Subject: [PATCH 009/324] Fix style issues with manual METADATA publishing (#23184) --- doc/src/manual/packages.md | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/doc/src/manual/packages.md b/doc/src/manual/packages.md index 569806656811f..ea024571f6f32 100644 --- a/doc/src/manual/packages.md +++ b/doc/src/manual/packages.md @@ -978,25 +978,20 @@ By "forking" the main METADATA repository, you can create a personal copy (of ME your GitHub account. Once that copy exists, you can push your local changes to your copy (just like any other GitHub project). -1. go to [https://github.com/login?return_to=https%3A%2F%2Fgithub.com%2FJuliaLang%2FMETADATA.jl%2Ffork](https://github.com/login?return_to=https%3A%2F%2Fgithub.com%2FJuliaLang%2FMETADATA.jl%2Ffork) -and create your own fork. +1. Create a [fork of METADATA.jl](https://github.com/login?return_to=https%3A%2F%2Fgithub.com%2FJuliaLang%2FMETADATA.jl%2Ffork). -2. add your fork as a remote repository for the METADATA repository on your local computer (in -the terminal where USERNAME is your github username): +2. Add your fork as a remote repository for the METADATA repository on your local computer (in + the terminal where USERNAME is your github username): -``` -cd ~/.julia/v0.6/METADATA -git remote add USERNAME https://github.com/USERNAME/METADATA.jl.git -``` + cd ~/.julia/v0.6/METADATA + git remote add USERNAME https://github.com/USERNAME/METADATA.jl.git -1. push your changes to your fork: +3. Push your changes to your fork: - ``` - git push USERNAME metadata-v2 - ``` + git push USERNAME metadata-v2 4. If all of that works, then go back to the GitHub page for your fork, and click the "pull request" -link. + link. ## Fixing Package Requirements From 59e434d58c340d1a112a41f0f823f4441b1806bb Mon Sep 17 00:00:00 2001 From: Simon Byrne Date: Thu, 10 Aug 2017 14:33:03 -0700 Subject: [PATCH 010/324] Deprecate hex2num and num2hex (#22088) See #22031. --- base/deprecated.jl | 17 +++++++++++++++++ base/docs/helpdb/Base.jl | 20 -------------------- base/exports.jl | 2 -- base/floatfuncs.jl | 14 -------------- base/intfuncs.jl | 2 -- doc/src/stdlib/numbers.md | 2 -- test/floatfuncs.jl | 6 ------ test/intfuncs.jl | 1 - test/numbers.jl | 14 -------------- 9 files changed, 17 insertions(+), 61 deletions(-) diff --git a/base/deprecated.jl b/base/deprecated.jl index 54f57c816bf34..54f98bdf96a54 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1346,6 +1346,7 @@ end @deprecate srand(filename::AbstractString, n::Integer=4) srand(read!(filename, Array{UInt32}(Int(n)))) @deprecate MersenneTwister(filename::AbstractString) srand(MersenneTwister(0), read!(filename, Array{UInt32}(Int(4)))) + # PR #21974 @deprecate versioninfo(verbose::Bool) versioninfo(verbose=verbose) @deprecate versioninfo(io::IO, verbose::Bool) versioninfo(io, verbose=verbose) @@ -1645,6 +1646,22 @@ end # PR #23187 @deprecate cpad(s, n::Integer, p=" ") rpad(lpad(s, div(n+strwidth(s), 2), p), n, p) false +# PR #22088 +function hex2num(s::AbstractString) + depwarn("hex2num(s) is deprecated. Use reinterpret(Float64, parse(UInt64, s, 16)) instead.", :hex2num) + if length(s) <= 4 + return reinterpret(Float16, parse(UInt16, s, 16)) + end + if length(s) <= 8 + return reinterpret(Float32, parse(UInt32, s, 16)) + end + return reinterpret(Float64, parse(UInt64, s, 16)) +end + +@deprecate num2hex(x::Union{Float16,Float32,Float64}) hex(reintepret(Unsigned, x), sizeof(x)*2) +@deprecate num2hex(n::Integer) hex(n, sizeof(n)*2) + + # END 0.7 deprecations # BEGIN 1.0 deprecations diff --git a/base/docs/helpdb/Base.jl b/base/docs/helpdb/Base.jl index 47a32ae5cf895..71011c1d32047 100644 --- a/base/docs/helpdb/Base.jl +++ b/base/docs/helpdb/Base.jl @@ -428,19 +428,6 @@ original string, otherwise they must be from distinct character ranges. """ eachmatch -""" - num2hex(f) - -Get a hexadecimal string of the binary representation of a floating point number. - -# Examples -```jldoctest -julia> num2hex(2.2) -"400199999999999a" -``` -""" -num2hex - """ truncate(file,n) @@ -1301,13 +1288,6 @@ false """ isempty -""" - hex2num(str) - -Convert a hexadecimal string to the floating point number it represents. -""" -hex2num - """ InexactError(name::Symbol, T, val) diff --git a/base/exports.jl b/base/exports.jl index 80601067a1dce..eecd75f36d46e 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -338,7 +338,6 @@ export gamma, gcd, gcdx, - hex2num, hypot, imag, inv, @@ -377,7 +376,6 @@ export nextpow2, nextprod, numerator, - num2hex, one, oneunit, powermod, diff --git a/base/floatfuncs.jl b/base/floatfuncs.jl index 32612f3525ea2..5e04b96128d96 100644 --- a/base/floatfuncs.jl +++ b/base/floatfuncs.jl @@ -37,20 +37,6 @@ maxintfloat() = maxintfloat(Float64) isinteger(x::AbstractFloat) = (x - trunc(x) == 0) -num2hex(x::Float16) = hex(bitcast(UInt16, x), 4) -num2hex(x::Float32) = hex(bitcast(UInt32, x), 8) -num2hex(x::Float64) = hex(bitcast(UInt64, x), 16) - -function hex2num(s::AbstractString) - if length(s) <= 4 - return bitcast(Float16, parse(UInt16, s, 16)) - end - if length(s) <= 8 - return bitcast(Float32, parse(UInt32, s, 16)) - end - return bitcast(Float64, parse(UInt64, s, 16)) -end - """ round([T,] x, [digits, [base]], [r::RoundingMode]) diff --git a/base/intfuncs.jl b/base/intfuncs.jl index b95c54ef6f9ee..d6c782e33a89e 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -584,8 +584,6 @@ function hex(x::Unsigned, pad::Int, neg::Bool) String(a) end -num2hex(n::Integer) = hex(n, sizeof(n)*2) - const base36digits = ['0':'9';'a':'z'] const base62digits = ['0':'9';'A':'Z';'a':'z'] diff --git a/doc/src/stdlib/numbers.md b/doc/src/stdlib/numbers.md index d04e7510a536f..91941c4a338fa 100644 --- a/doc/src/stdlib/numbers.md +++ b/doc/src/stdlib/numbers.md @@ -58,8 +58,6 @@ Base.Math.significand Base.Math.exponent Base.complex(::Complex) Base.bswap -Base.num2hex -Base.hex2num Base.hex2bytes Base.bytes2hex ``` diff --git a/test/floatfuncs.jl b/test/floatfuncs.jl index d1796d68d5a18..c97f077f05438 100644 --- a/test/floatfuncs.jl +++ b/test/floatfuncs.jl @@ -39,12 +39,6 @@ for elty in (Float16,Float32,Float64) @test !isinteger(elty(NaN)) end -# num2hex, hex2num -for elty in (Float16,Float32,Float64), _ = 1:10 - x = rand(elty) - @test hex2num(num2hex(x)) ≈ x -end - # round for elty in (Float32,Float64) x = rand(elty) diff --git a/test/intfuncs.jl b/test/intfuncs.jl index 7498927596c11..0ecf36d203a97 100644 --- a/test/intfuncs.jl +++ b/test/intfuncs.jl @@ -123,7 +123,6 @@ end @test hex(12) == "c" @test hex(-12, 3) == "-00c" -@test num2hex(1243) == (Int == Int32 ? "000004db" : "00000000000004db") @test base(2, 5, 7) == "0000101" diff --git a/test/numbers.jl b/test/numbers.jl index 9ce99dcd79840..45603503c66bf 100644 --- a/test/numbers.jl +++ b/test/numbers.jl @@ -387,20 +387,6 @@ end @test base(12,typemin(Int128)) == "-2a695925806818735399a37a20a31b3534a8" @test base(12,typemax(Int128)) == "2a695925806818735399a37a20a31b3534a7" -@test hex2num("3ff0000000000000") == 1. -@test hex2num("bff0000000000000") == -1. -@test hex2num("4000000000000000") == 2. -@test hex2num("7ff0000000000000") == Inf -@test hex2num("fff0000000000000") == -Inf -@test isnan(hex2num("7ff8000000000000")) -@test isnan(hex2num("fff8000000000000")) -@test hex2num("3f800000") == 1.0f0 -@test hex2num("bf800000") == -1.0f0 -@test hex2num("7f800000") == Inf32 -@test hex2num("ff800000") == -Inf32 -@test isnan(hex2num("7fc00000")) -@test isnan(hex2num("ffc00000")) - # floating-point printing @test repr(1.0) == "1.0" @test repr(-1.0) == "-1.0" From bbd581e90e57ccf4765fc24aab31227d3dc5d8b8 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Fri, 11 Aug 2017 12:58:40 +0200 Subject: [PATCH 011/324] fix indentation for varying-width function prompt (#23190) Since #22809 a function can be used as the prompt, but the indentation of non-prompt lines was still determined by the first value of the prompt (usually "julia> "). Now, the indentation is determined by the currently printed prompt, which can vary from one keypress to the next. --- base/repl/LineEdit.jl | 22 +++++++++++++++------- test/lineedit.jl | 26 ++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/base/repl/LineEdit.jl b/base/repl/LineEdit.jl index f5410022da8e9..8c894bf32ea3f 100644 --- a/base/repl/LineEdit.jl +++ b/base/repl/LineEdit.jl @@ -62,6 +62,8 @@ mutable struct PromptState <: ModeState p::Prompt input_buffer::IOBuffer ias::InputAreaState + # indentation of lines which do not include the prompt + # if negative, the width of the prompt is used indent::Int end @@ -201,11 +203,9 @@ function refresh_multi_line(termbuf::TerminalBuffer, terminal::UnixTerminal, buf buf_pos = position(buf) line_pos = buf_pos # Write out the prompt string - write_prompt(termbuf, prompt) - prompt = prompt_string(prompt) + lindent = write_prompt(termbuf, prompt) # Count the '\n' at the end of the line if the terminal emulator does (specific to DOS cmd prompt) miscountnl = @static Sys.iswindows() ? (isa(Terminals.pipe_reader(terminal), Base.TTY) && !Base.ispty(Terminals.pipe_reader(terminal))) : false - lindent = strwidth(prompt) # Now go through the buffer line by line seek(buf, 0) @@ -244,7 +244,7 @@ function refresh_multi_line(termbuf::TerminalBuffer, terminal::UnixTerminal, buf end end cur_row += div(max(lindent + llength + miscountnl - 1, 0), cols) - lindent = indent + lindent = indent < 0 ? lindent : indent end seek(buf, buf_pos) @@ -627,16 +627,24 @@ default_completion_cb(::IOBuffer) = [] default_enter_cb(_) = true write_prompt(terminal, s::PromptState) = write_prompt(terminal, s.p) + function write_prompt(terminal, p::Prompt) prefix = prompt_string(p.prompt_prefix) suffix = prompt_string(p.prompt_suffix) write(terminal, prefix) write(terminal, Base.text_colors[:bold]) - write(terminal, prompt_string(p.prompt)) + width = write_prompt(terminal, p.prompt) write(terminal, Base.text_colors[:normal]) write(terminal, suffix) + width +end + +# returns the width of the written prompt +function write_prompt(terminal, s::Union{AbstractString,Function}) + promptstr = prompt_string(s) + write(terminal, promptstr) + strwidth(promptstr) end -write_prompt(terminal, s::Union{AbstractString,Function}) = write(terminal, prompt_string(s)) ### Keymap Support @@ -1568,7 +1576,7 @@ run_interface(::Prompt) = nothing init_state(terminal, prompt::Prompt) = PromptState(terminal, prompt, IOBuffer(), InputAreaState(1, 1), - #=indent(spaces)=# strwidth(prompt_string(prompt))) + #=indent(spaces)=# -1) function init_state(terminal, m::ModalInterface) s = MIState(m, m.modes[1], false, Dict{Any,Any}()) diff --git a/test/lineedit.jl b/test/lineedit.jl index 4aeca1b38797c..ee558e91db875 100644 --- a/test/lineedit.jl +++ b/test/lineedit.jl @@ -401,3 +401,29 @@ let Base.LineEdit.InputAreaState(0,0), "julia> ", indent = 7) @test s == Base.LineEdit.InputAreaState(3,1) end + +@testset "function prompt indentation" begin + term = TestHelpers.FakeTerminal(IOBuffer(), IOBuffer(), IOBuffer(), false) + # default prompt: PromptState.indent should not be set to a final fixed value + s = LineEdit.init_state(term, ModalInterface([Prompt("julia> ")])) + ps::LineEdit.PromptState = s.mode_state[s.current_mode] + @test ps.indent == -1 + # the prompt is modified afterwards to a function + ps.p.prompt = let i = 0 + () -> ["Julia is Fun! > ", "> "][mod1(i+=1, 2)] # lengths are 16 and 2 + end + buf = LineEdit.buffer(ps) + write(buf, "begin\n julia = :fun\nend") + outbuf = IOBuffer() + termbuf = Base.Terminals.TerminalBuffer(outbuf) + LineEdit.refresh_multi_line(termbuf, term, ps) + @test String(take!(outbuf)) == + "\r\e[0K\e[1mJulia is Fun! > \e[0m\r\e[16Cbegin\n" * + "\r\e[16C julia = :fun\n" * + "\r\e[16Cend\r\e[19C" + LineEdit.refresh_multi_line(termbuf, term, ps) + @test String(take!(copy(outbuf))) == + "\r\e[0K\e[1A\r\e[0K\e[1A\r\e[0K\e[1m> \e[0m\r\e[2Cbegin\n" * + "\r\e[2C julia = :fun\n" * + "\r\e[2Cend\r\e[5C" +end From 9c8d46c5facf498f6033b1641c9a41263f5c64c5 Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Fri, 11 Aug 2017 13:01:26 +0200 Subject: [PATCH 012/324] remove duplicate isequal doc string (#23195) --- doc/src/stdlib/base.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/src/stdlib/base.md b/doc/src/stdlib/base.md index 63e39784a2990..6ba310f9cb9bd 100644 --- a/doc/src/stdlib/base.md +++ b/doc/src/stdlib/base.md @@ -59,8 +59,7 @@ ans ```@docs Core.:(===) Core.isa -Base.isequal(::Any, ::Any) -Base.isequal(::Nullable, ::Nullable) +Base.isequal Base.isless Base.isless(::Nullable, ::Nullable) Base.ifelse From 55ab3e81b37e9d2159a16b4dfa75dcede0a144df Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Fri, 11 Aug 2017 13:01:53 +0200 Subject: [PATCH 013/324] fix array printing when small number of rows (#23112) --- base/show.jl | 23 ++++++++++++++++++----- test/show.jl | 28 ++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/base/show.jl b/base/show.jl index 0c54db1d9a283..303055c80b633 100644 --- a/base/show.jl +++ b/base/show.jl @@ -1585,11 +1585,11 @@ function print_matrix(io::IO, X::AbstractVecOrMat, print(io, i == first(rowsA) ? pre : presp) print_matrix_row(io, X,A,i,colsA,sep) print(io, i == last(rowsA) ? post : postsp) - if i != rowsA[end]; println(io); end + if i != rowsA[end] || i == rowsA[halfheight]; println(io); end if i == rowsA[halfheight] print(io, i == first(rowsA) ? pre : presp) print_matrix_vdots(io, vdots,A,sep,vmod,1) - println(io, i == last(rowsA) ? post : postsp) + print(io, i == last(rowsA) ? post : postsp * '\n') end end else # neither rows nor cols fit, so use all 3 kinds of dots @@ -1604,16 +1604,22 @@ function print_matrix(io::IO, X::AbstractVecOrMat, print(io, (i - first(rowsA)) % hmod == 0 ? hdots : repeat(" ", length(hdots))) print_matrix_row(io, X,Ralign,i,n-length(Ralign)+colsA,sep) print(io, i == last(rowsA) ? post : postsp) - if i != rowsA[end]; println(io); end + if i != rowsA[end] || i == rowsA[halfheight]; println(io); end if i == rowsA[halfheight] print(io, i == first(rowsA) ? pre : presp) print_matrix_vdots(io, vdots,Lalign,sep,vmod,1) print(io, ddots) print_matrix_vdots(io, vdots,Ralign,sep,vmod,r) - println(io, i == last(rowsA) ? post : postsp) + print(io, i == last(rowsA) ? post : postsp * '\n') end end end + if isempty(rowsA) + print(io, pre) + print(io, vdots) + length(colsA) > 1 && print(io, " ", ddots) + print(io, post) + end end end @@ -1760,7 +1766,14 @@ function showarray(io::IO, X::AbstractArray, repr::Bool = true; header = true) end (!repr && header) && print(io, summary(X)) if !isempty(X) - (!repr && header) && println(io, ":") + if !repr && header + print(io, ":") + if get(io, :limit, false) && displaysize(io)[1]-4 <= 0 + return print(io, " …") + else + println(io) + end + end if ndims(X) == 0 if isassigned(X) return show(io, X[]) diff --git a/test/show.jl b/test/show.jl index 7c3434e598d41..0a17da25462ba 100644 --- a/test/show.jl +++ b/test/show.jl @@ -919,3 +919,31 @@ end @test replstr(zeros(Complex{Int}, 1, 2, 1)) == "1×2×1 Array{Complex{$Int},3}:\n[:, :, 1] =\n 0+0im 0+0im" end + +@testset "Array printing with limited rows" begin + arrstr = let buf = IOBuffer() + function (A, rows) + Base.showarray(IOContext(buf, displaysize=(rows, 80), limit=true), + A, false, header=true) + String(take!(buf)) + end + end + A = Int64[1] + @test arrstr(A, 4) == "1-element Array{Int64,1}: …" + @test arrstr(A, 5) == "1-element Array{Int64,1}:\n 1" + push!(A, 2) + @test arrstr(A, 5) == "2-element Array{Int64,1}:\n ⋮" + @test arrstr(A, 6) == "2-element Array{Int64,1}:\n 1\n 2" + push!(A, 3) + @test arrstr(A, 6) == "3-element Array{Int64,1}:\n 1\n ⋮" + + @test arrstr(zeros(4, 3), 4) == "4×3 Array{Float64,2}: …" + @test arrstr(zeros(4, 30), 4) == "4×30 Array{Float64,2}: …" + @test arrstr(zeros(4, 3), 5) == "4×3 Array{Float64,2}:\n ⋮ ⋱ " + @test arrstr(zeros(4, 30), 5) == "4×30 Array{Float64,2}:\n ⋮ ⋱ " + @test arrstr(zeros(4, 3), 6) == "4×3 Array{Float64,2}:\n 0.0 0.0 0.0\n ⋮ " + @test arrstr(zeros(4, 30), 6) == + string("4×30 Array{Float64,2}:\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 … 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " ⋮ ⋮ ⋱ ⋮ ") +end From 07b4eac754f4b54cf3ff7570aebc2a3e072ae1e7 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Thu, 29 Jun 2017 18:05:33 +0200 Subject: [PATCH 014/324] faster rand(::MersenneTwister, BitInteger) --- base/random.jl | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/base/random.jl b/base/random.jl index b6dd5b4827319..c8ca64d16eacf 100644 --- a/base/random.jl +++ b/base/random.jl @@ -164,7 +164,16 @@ end @inline rand_ui52_raw_inbounds(r::MersenneTwister) = reinterpret(UInt64, rand_inbounds(r, Close1Open2)) @inline rand_ui52_raw(r::MersenneTwister) = (reserve_1(r); rand_ui52_raw_inbounds(r)) -@inline rand_ui2x52_raw(r::MersenneTwister) = rand_ui52_raw(r) % UInt128 << 64 | rand_ui52_raw(r) + +@inline function rand_ui2x52_raw(r::MersenneTwister) + reserve(r, 2) + rand_ui52_raw_inbounds(r) % UInt128 << 64 | rand_ui52_raw_inbounds(r) +end + +@inline function rand_ui104_raw(r::MersenneTwister) + reserve(r, 2) + rand_ui52_raw_inbounds(r) % UInt128 << 52 ⊻ rand_ui52_raw_inbounds(r) +end function srand(r::MersenneTwister, seed::Vector{UInt32}) copy!(resize!(r.seed, length(seed)), seed) @@ -744,6 +753,34 @@ end rand(rng::AbstractRNG, r::UnitRange{<:Union{Signed,Unsigned,BigInt,Bool}}) = rand(rng, RangeGenerator(r)) +## special case for MersenneTwister +@inline function rand_lteq(r::AbstractRNG, randfun, u::U, mask::U) where U<:Integer + while true + x = randfun(r) & mask + x <= u && return x + end +end + +function rand(rng::MersenneTwister, r::UnitRange{T}) where T<:Union{Base.BitInteger64,Bool} + isempty(r) && throw(ArgumentError("range must be non-empty")) + m = last(r) % UInt64 - first(r) % UInt64 + bw = (64 - leading_zeros(m)) % UInt # bit-width + mask = (1 % UInt64 << bw) - (1 % UInt64) + x = bw <= 52 ? rand_lteq(rng, rand_ui52_raw, m, mask) : + rand_lteq(rng, rng->rand(rng, UInt64), m, mask) + (x + first(r) % UInt64) % T +end + +function rand(rng::MersenneTwister, r::UnitRange{T}) where T<:Union{Int128,UInt128} + isempty(r) && throw(ArgumentError("range must be non-empty")) + m = (last(r)-first(r)) % UInt128 + bw = (128 - leading_zeros(m)) % UInt # bit-width + mask = (1 % UInt128 << bw) - (1 % UInt128) + x = bw <= 52 ? rand_lteq(rng, rand_ui52_raw, m % UInt64, mask % UInt64) % UInt128 : + bw <= 104 ? rand_lteq(rng, rand_ui104_raw, m, mask) : + rand_lteq(rng, rng->rand(rng, UInt128), m, mask) + x % T + first(r) +end # Randomly draw a sample from an AbstractArray r # (e.g. r is a range 0:2:8 or a vector [2, 3, 5, 7]) From bbf558458583bce45256effcb78577256cf92f58 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Fri, 11 Aug 2017 07:04:38 -0700 Subject: [PATCH 015/324] isapprox: test max(atol,rtol*...) rather than atol+rtol*... (#22742) * isapprox now tests max(atol,rtol*...) rather than atol+rtol*... * move news to breaking section * in isapprox, make default rtol=0 if atol>0 is specified * consistent isapprox for UniformScaling * whoops, remove old code * deprecate 2-arg rtoldefault --- NEWS.md | 4 ++++ base/deprecated.jl | 2 ++ base/floatfuncs.jl | 20 ++++++++++++-------- base/linalg/generic.jl | 7 ++++--- base/linalg/uniformscaling.jl | 10 +++++----- test/math.jl | 5 +++++ 6 files changed, 32 insertions(+), 16 deletions(-) diff --git a/NEWS.md b/NEWS.md index 81209168d808d..e170bb45091f3 100644 --- a/NEWS.md +++ b/NEWS.md @@ -120,6 +120,10 @@ This section lists changes that do not have deprecation warnings. `Bidiagonal{T,V<:AbstractVector{T}}` and `SymTridiagonal{T,V<:AbstractVector{T}}` respectively ([#22718], [#22925], [#23035]). + * `isapprox(x,y)` now tests `norm(x-y) <= max(atol, rtol*max(norm(x), norm(y)))` + rather than `norm(x-y) <= atol + ...`, and `rtol` defaults to zero + if an `atol > 0` is specified ([#22742]). + * Spaces are no longer allowed between `@` and the name of a macro in a macro call ([#22868]). * Juxtaposition of a non-literal with a macro call (`x@macro`) is no longer valid syntax ([#22868]). diff --git a/base/deprecated.jl b/base/deprecated.jl index 54f98bdf96a54..9c60b9a645c31 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1661,6 +1661,8 @@ end @deprecate num2hex(x::Union{Float16,Float32,Float64}) hex(reintepret(Unsigned, x), sizeof(x)*2) @deprecate num2hex(n::Integer) hex(n, sizeof(n)*2) +# PR #22742: change in isapprox semantics +@deprecate rtoldefault(x,y) rtoldefault(x,y,0) false # END 0.7 deprecations diff --git a/base/floatfuncs.jl b/base/floatfuncs.jl index 5e04b96128d96..39b30c73c2e6b 100644 --- a/base/floatfuncs.jl +++ b/base/floatfuncs.jl @@ -189,15 +189,16 @@ end # isapprox: approximate equality of numbers """ - isapprox(x, y; rtol::Real=sqrt(eps), atol::Real=0, nans::Bool=false, norm::Function) + isapprox(x, y; rtol::Real=atol>0 ? √eps : 0, atol::Real=0, nans::Bool=false, norm::Function) -Inexact equality comparison: `true` if `norm(x-y) <= atol + rtol*max(norm(x), norm(y))`. The +Inexact equality comparison: `true` if `norm(x-y) <= max(atol, rtol*max(norm(x), norm(y)))`. The default `atol` is zero and the default `rtol` depends on the types of `x` and `y`. The keyword argument `nans` determines whether or not NaN values are considered equal (defaults to false). -For real or complex floating-point values, `rtol` defaults to -`sqrt(eps(typeof(real(x-y))))`. This corresponds to requiring equality of about half of the -significand digits. For other types, `rtol` defaults to zero. +For real or complex floating-point values, if an `atol > 0` is not specified, `rtol` defaults to +the square root of [`eps`](@ref) of the type of `x` or `y`, whichever is bigger (least precise). +This corresponds to requiring equality of about half of the significand digits. Otherwise, +e.g. for integer arguments or if an `atol > 0` is supplied, `rtol` defaults to zero. `x` and `y` may also be arrays of numbers, in which case `norm` defaults to `vecnorm` but may be changed by passing a `norm::Function` keyword argument. (For numbers, `norm` is the @@ -220,8 +221,8 @@ julia> isapprox([10.0^9, 1.0], [10.0^9, 2.0]) true ``` """ -function isapprox(x::Number, y::Number; rtol::Real=rtoldefault(x,y), atol::Real=0, nans::Bool=false) - x == y || (isfinite(x) && isfinite(y) && abs(x-y) <= atol + rtol*max(abs(x), abs(y))) || (nans && isnan(x) && isnan(y)) +function isapprox(x::Number, y::Number; atol::Real=0, rtol::Real=rtoldefault(x,y,atol), nans::Bool=false) + x == y || (isfinite(x) && isfinite(y) && abs(x-y) <= max(atol, rtol*max(abs(x), abs(y)))) || (nans && isnan(x) && isnan(y)) end const ≈ = isapprox @@ -230,7 +231,10 @@ const ≈ = isapprox # default tolerance arguments rtoldefault(::Type{T}) where {T<:AbstractFloat} = sqrt(eps(T)) rtoldefault(::Type{<:Real}) = 0 -rtoldefault(x::Union{T,Type{T}}, y::Union{S,Type{S}}) where {T<:Number,S<:Number} = max(rtoldefault(real(T)),rtoldefault(real(S))) +function rtoldefault(x::Union{T,Type{T}}, y::Union{S,Type{S}}, atol::Real) where {T<:Number,S<:Number} + rtol = max(rtoldefault(real(T)),rtoldefault(real(S))) + return atol > 0 ? zero(rtol) : rtol +end # fused multiply-add fma_libm(x::Float32, y::Float32, z::Float32) = diff --git a/base/linalg/generic.jl b/base/linalg/generic.jl index c1899f4a21942..8c69ebe1548b6 100644 --- a/base/linalg/generic.jl +++ b/base/linalg/generic.jl @@ -1296,11 +1296,12 @@ promote_leaf_eltypes(x::Union{AbstractArray,Tuple}) = mapreduce(promote_leaf_elt # Supports nested arrays; e.g., for `a = [[1,2, [3,4]], 5.0, [6im, [7.0, 8.0]]]` # `a ≈ a` is `true`. function isapprox(x::AbstractArray, y::AbstractArray; - rtol::Real=Base.rtoldefault(promote_leaf_eltypes(x),promote_leaf_eltypes(y)), - atol::Real=0, nans::Bool=false, norm::Function=vecnorm) + atol::Real=0, + rtol::Real=Base.rtoldefault(promote_leaf_eltypes(x),promote_leaf_eltypes(y),atol), + nans::Bool=false, norm::Function=vecnorm) d = norm(x - y) if isfinite(d) - return d <= atol + rtol*max(norm(x), norm(y)) + return d <= max(atol, rtol*max(norm(x), norm(y))) else # Fall back to a component-wise approximate comparison return all(ab -> isapprox(ab[1], ab[2]; rtol=rtol, atol=atol, nans=nans), zip(x, y)) diff --git a/base/linalg/uniformscaling.jl b/base/linalg/uniformscaling.jl index fcde2ca755253..b0feaac7d74f0 100644 --- a/base/linalg/uniformscaling.jl +++ b/base/linalg/uniformscaling.jl @@ -197,15 +197,16 @@ broadcast(::typeof(/), J::UniformScaling,x::Number) = UniformScaling(J.λ/x) ==(J1::UniformScaling,J2::UniformScaling) = (J1.λ == J2.λ) function isapprox(J1::UniformScaling{T}, J2::UniformScaling{S}; - rtol::Real=Base.rtoldefault(T,S), atol::Real=0, nans::Bool=false) where {T<:Number,S<:Number} + atol::Real=0, rtol::Real=Base.rtoldefault(T,S,atol), nans::Bool=false) where {T<:Number,S<:Number} isapprox(J1.λ, J2.λ, rtol=rtol, atol=atol, nans=nans) end function isapprox(J::UniformScaling,A::AbstractMatrix; - rtol::Real=rtoldefault(promote_leaf_eltypes(A),eltype(J)), - atol::Real=0, nans::Bool=false, norm::Function=vecnorm) + atol::Real=0, + rtol::Real=rtoldefault(promote_leaf_eltypes(A),eltype(J),atol), + nans::Bool=false, norm::Function=vecnorm) n = checksquare(A) Jnorm = norm === vecnorm ? abs(J.λ)*sqrt(n) : (norm === Base.norm ? abs(J.λ) : norm(diagm(fill(J.λ, n)))) - return norm(A - J) <= atol + rtol*max(norm(A), Jnorm) + return norm(A - J) <= max(atol, rtol*max(norm(A), Jnorm)) end isapprox(A::AbstractMatrix,J::UniformScaling;kwargs...) = isapprox(J,A;kwargs...) @@ -335,4 +336,3 @@ UniformScaling{Float64} ``` """ chol(J::UniformScaling, args...) = ((C, info) = _chol!(J, nothing); @assertposdef C info) - diff --git a/test/math.jl b/test/math.jl index f3c997fceb261..cebe2fa0ea4e2 100644 --- a/test/math.jl +++ b/test/math.jl @@ -669,6 +669,11 @@ end @test exp10(Float16(1.0)) === Float16(exp10(1.0)) end +# #22742: updated isapprox semantics +@test !isapprox(1.0, 1.0+1e-12, atol=1e-14) +@test isapprox(1.0, 1.0+0.5*sqrt(eps(1.0))) +@test !isapprox(1.0, 1.0+1.5*sqrt(eps(1.0)), atol=sqrt(eps(1.0))) + # test AbstractFloat fallback pr22716 struct Float22716{T<:AbstractFloat} <: AbstractFloat x::T From 1fcc47c9deb37ed4c2e2f5752090bb55980b62c6 Mon Sep 17 00:00:00 2001 From: Jarrett Revels Date: Fri, 11 Aug 2017 11:22:35 -0400 Subject: [PATCH 016/324] add utilities for performing CodeInfo validation passes (enabled for debug builds only) (#22938) --- base/codevalidation.jl | 155 +++++++++++++++++++++++++++++++++++++++++ base/coreimg.jl | 1 + base/inference.jl | 57 ++++++++++----- test/choosetests.jl | 2 +- test/codevalidation.jl | 136 ++++++++++++++++++++++++++++++++++++ 5 files changed, 332 insertions(+), 19 deletions(-) create mode 100644 base/codevalidation.jl create mode 100644 test/codevalidation.jl diff --git a/base/codevalidation.jl b/base/codevalidation.jl new file mode 100644 index 0000000000000..7605a1654af16 --- /dev/null +++ b/base/codevalidation.jl @@ -0,0 +1,155 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# Expr head => argument count bounds +const VALID_EXPR_HEADS = ObjectIdDict( + :call => 1:typemax(Int), + :invoke => 2:typemax(Int), + :static_parameter => 1:1, + :line => 1:3, + :gotoifnot => 2:2, + :(&) => 1:1, + :(=) => 2:2, + :method => 1:4, + :const => 1:1, + :null => 0:0, # TODO from @vtjnash: remove this + any :null handling code in Base + :new => 1:typemax(Int), + :return => 1:1, + :the_exception => 0:0, + :enter => 1:1, + :leave => 1:1, + :inbounds => 1:1, + :boundscheck => 1:1, + :copyast => 1:1, + :meta => 0:typemax(Int), + :global => 1:1, + :foreigncall => 3:typemax(Int), + :isdefined => 1:1, + :simdloop => 0:0 +) + +const ASSIGNED_FLAG = 0x02 + +# @enum isn't defined yet, otherwise I'd use it for this +const INVALID_EXPR_HEAD = "invalid expression head" +const INVALID_EXPR_NARGS = "invalid number of expression args" +const INVALID_LVALUE = "invalid LHS value" +const INVALID_RVALUE = "invalid RHS value" +const INVALID_CALL_ARG = "invalid :call argument" +const EMPTY_SLOTNAMES = "slotnames field is empty" +const SLOTFLAGS_MISMATCH = "length(slotnames) != length(slotflags)" +const SLOTTYPES_MISMATCH = "length(slotnames) != length(slottypes)" +const SLOTTYPES_MISMATCH_UNINFERRED = "uninferred CodeInfo slottypes field is not `nothing`" +const SSAVALUETYPES_MISMATCH = "not all SSAValues in AST have a type in ssavaluetypes" +const SSAVALUETYPES_MISMATCH_UNINFERRED = "uninferred CodeInfo ssavaluetypes field does not equal the number of present SSAValues" +const INVALID_ASSIGNMENT_SLOTFLAG = "slot has wrong assignment slotflag setting (bit flag 2 not set)" +const NON_TOP_LEVEL_METHOD = "encountered `Expr` head `:method` in non-top-level code (i.e. `nargs` > 0)" +const SIGNATURE_NARGS_MISMATCH = "method signature does not match number of method arguments" +const SLOTNAMES_NARGS_MISMATCH = "CodeInfo for method contains fewer slotnames than the number of method arguments" + +struct InvalidCodeError <: Exception + kind::String + meta::Any +end + +InvalidCodeError(kind) = InvalidCodeError(kind, nothing) + +""" + validate_code!(errors::Vector{>:InvalidCodeError}, c::CodeInfo) + +Validate `c`, logging any violation by pushing an `InvalidCodeError` into `errors`. +""" +function validate_code!(errors::Vector{>:InvalidCodeError}, c::CodeInfo, is_top_level::Bool = false) + ssavals = IntSet() + lhs_slotnums = IntSet() + walkast(c.code) do x + if isa(x, Expr) + !is_top_level && x.head == :method && push!(errors, InvalidCodeError(NON_TOP_LEVEL_METHOD)) + narg_bounds = get(VALID_EXPR_HEADS, x.head, -1:-1) + nargs = length(x.args) + if narg_bounds == -1:-1 + push!(errors, InvalidCodeError(INVALID_EXPR_HEAD, (x.head, x))) + elseif !in(nargs, narg_bounds) + push!(errors, InvalidCodeError(INVALID_EXPR_NARGS, (x.head, nargs, x))) + elseif x.head == :(=) + lhs, rhs = x.args + if !is_valid_lvalue(lhs) + push!(errors, InvalidCodeError(INVALID_LVALUE, lhs)) + elseif isa(lhs, SlotNumber) && !in(lhs.id, lhs_slotnums) + n = lhs.id + if isassigned(c.slotflags, n) && !is_flag_set(c.slotflags[n], ASSIGNED_FLAG) + push!(errors, InvalidCodeError(INVALID_ASSIGNMENT_SLOTFLAG, lhs)) + end + push!(lhs_slotnums, n) + end + if !is_valid_rvalue(rhs) + push!(errors, InvalidCodeError(INVALID_RVALUE, rhs)) + end + elseif x.head == :call || x.head == :invoke + for arg in x.args + if !is_valid_rvalue(arg) + push!(errors, InvalidCodeError(INVALID_CALL_ARG, arg)) + end + end + end + elseif isa(x, SSAValue) + id = x.id + 1 # ensures that id > 0 for use with IntSet + !in(id, ssavals) && push!(ssavals, id) + end + end + nslotnames = length(c.slotnames) + nslotflags = length(c.slotflags) + nssavals = length(ssavals) + nslotnames == 0 && push!(errors, InvalidCodeError(EMPTY_SLOTNAMES)) + nslotnames != nslotflags && push!(errors, InvalidCodeError(SLOTFLAGS_MISMATCH, (nslotnames, nslotflags))) + if c.inferred + nslottypes = length(c.slottypes) + nssavaluetypes = length(c.ssavaluetypes) + nslottypes != nslotnames && push!(errors, InvalidCodeError(SLOTTYPES_MISMATCH, (nslotnames, nslottypes))) + nssavaluetypes < nssavals && push!(errors, InvalidCodeError(SSAVALUETYPES_MISMATCH, (nssavals, nssavaluetypes))) + else + c.slottypes !== nothing && push!(errors, InvalidCodeError(SLOTTYPES_MISMATCH_UNINFERRED, c.slottypes)) + c.ssavaluetypes != nssavals && push!(errors, InvalidCodeError(SSAVALUETYPES_MISMATCH_UNINFERRED, (nssavals, c.ssavaluetypes))) + end + return errors +end + +""" + validate_code!(errors::Vector{>:InvalidCodeError}, mi::MethodInstance, + c::Union{Void,CodeInfo} = Core.Inference.retrieve_code_info(mi)) + +Validate `mi`, logging any violation by pushing an `InvalidCodeError` into `errors`. + +If `isa(c, CodeInfo)`, also call `validate_code!(errors, c)`. It is assumed that `c` is +the `CodeInfo` instance associated with `mi`. +""" +function validate_code!(errors::Vector{>:InvalidCodeError}, mi::Core.MethodInstance, + c::Union{Void,CodeInfo} = Core.Inference.retrieve_code_info(mi)) + m = mi.def::Method + n_sig_params = length(Core.Inference.unwrap_unionall(m.sig).parameters) + if (m.isva ? (n_sig_params < (m.nargs - 1)) : (n_sig_params != m.nargs)) + push!(errors, InvalidCodeError(SIGNATURE_NARGS_MISMATCH, (m.isva, n_sig_params, m.nargs))) + end + if isa(c, CodeInfo) + m.nargs > length(c.slotnames) && push!(errors, InvalidCodeError(SLOTNAMES_NARGS_MISMATCH)) + validate_code!(errors, c, m.nargs == 0) + end + return errors +end + +validate_code(args...) = validate_code!(Vector{InvalidCodeError}(), args...) + +function walkast(f, stmts::Array) + for stmt in stmts + f(stmt) + isa(stmt, Expr) && walkast(f, stmt.args) + end +end + +is_valid_lvalue(x) = isa(x, SlotNumber) || isa(x, SSAValue) || isa(x, GlobalRef) + +function is_valid_rvalue(x) + isa(x, Expr) && return !in(x.head, (:gotoifnot, :line, :const, :meta)) + return !isa(x, GotoNode) && !isa(x, LabelNode) && !isa(x, LineNumberNode) +end + +is_flag_set(byte::UInt8, flag::UInt8) = (byte & flag) == flag diff --git a/base/coreimg.jl b/base/coreimg.jl index 5739e8136084f..200bce4d7a8e8 100644 --- a/base/coreimg.jl +++ b/base/coreimg.jl @@ -65,6 +65,7 @@ include("associative.jl") include("docs/core.jl") # compiler +include("codevalidation.jl") include("inference.jl") ccall(:jl_set_typeinf_func, Void, (Any,), typeinf_ext) diff --git a/base/inference.jl b/base/inference.jl index 0c4a90e27aa2f..2c899590a290a 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -289,24 +289,16 @@ end function InferenceState(linfo::MethodInstance, optimize::Bool, cached::Bool, params::InferenceParams) # prepare an InferenceState object for inferring lambda - # create copies of the CodeInfo definition, and any fields that type-inference might modify - m = linfo.def::Method - if isdefined(m, :generator) - try - # user code might throw errors – ignore them - src = get_staged(linfo) - catch - return nothing - end - else - # TODO: post-inference see if we can swap back to the original arrays? - if isa(m.source, Array{UInt8,1}) - src = ccall(:jl_uncompress_ast, Any, (Any, Any), m, m.source) - else - src = ccall(:jl_copy_code_info, Ref{CodeInfo}, (Any,), m.source) - src.code = copy_exprargs(src.code) - src.slotnames = copy(src.slotnames) - src.slotflags = copy(src.slotflags) + src = retrieve_code_info(linfo) + src === nothing && return nothing + if JLOptions().debug_level == 2 + # this is a debug build of julia, so let's validate linfo + errors = validate_code(linfo, src) + if !isempty(errors) + for e in errors + println(STDERR, "WARNING: Encountered invalid lowered code for method ", + linfo.def, ": ", e) + end end end return InferenceState(linfo, src, optimize, cached, params) @@ -332,6 +324,35 @@ end #### helper functions #### +# create copies of the CodeInfo definition, and any fields that type-inference might modify +function copy_code_info(c::CodeInfo) + cnew = ccall(:jl_copy_code_info, Ref{CodeInfo}, (Any,), c) + cnew.code = copy_exprargs(cnew.code) + cnew.slotnames = copy(cnew.slotnames) + cnew.slotflags = copy(cnew.slotflags) + return cnew +end + +function retrieve_code_info(linfo::MethodInstance) + m = linfo.def::Method + if isdefined(m, :generator) + try + # user code might throw errors – ignore them + c = get_staged(linfo) + catch + return nothing + end + else + # TODO: post-inference see if we can swap back to the original arrays? + if isa(m.source, Array{UInt8,1}) + c = ccall(:jl_uncompress_ast, Any, (Any, Any), m, m.source) + else + c = copy_code_info(m.source) + end + end + return c +end + @inline slot_id(s) = isa(s, SlotNumber) ? (s::SlotNumber).id : (s::TypedSlot).id # using a function to ensure we can infer this # avoid cycle due to over-specializing `any` when used by inference diff --git a/test/choosetests.jl b/test/choosetests.jl index 7558b74179bc4..56e42ab8a072c 100644 --- a/test/choosetests.jl +++ b/test/choosetests.jl @@ -35,7 +35,7 @@ function choosetests(choices = []) "enums", "cmdlineargs", "i18n", "workspace", "libdl", "int", "checked", "intset", "floatfuncs", "compile", "distributed", "inline", "boundscheck", "error", "ambiguous", "cartesian", "asmvariant", "osutils", - "channels", "iostream", "specificity", "codegen" + "channels", "iostream", "specificity", "codegen", "codevalidation" ] profile_skipped = false if startswith(string(Sys.ARCH), "arm") diff --git a/test/codevalidation.jl b/test/codevalidation.jl new file mode 100644 index 0000000000000..0ed65869dcad5 --- /dev/null +++ b/test/codevalidation.jl @@ -0,0 +1,136 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +using Base.Test + +function f22938(a, b, x...) + d = 1 + a = d + for i in 1:b + d += i + end + return i * a +end + +msig = Tuple{typeof(f22938),Int,Int,Int,Int} +world = typemax(UInt) +_, msp, m = Base._methods_by_ftype(msig, -1, world)[] +mi = Core.Inference.code_for_method(m, msig, msp, world, false) +c0 = Core.Inference.retrieve_code_info(mi) + +@test isempty(Core.Inference.validate_code(mi)) +@test isempty(Core.Inference.validate_code(c0)) + +# INVALID_EXPR_HEAD +c = Core.Inference.copy_code_info(c0) +insert!(c.code, 4, Expr(:(=), SlotNumber(2), Expr(:invalid, 1))) +errors = Core.Inference.validate_code(c) +@test length(errors) == 1 +@test errors[1].kind === Core.Inference.INVALID_EXPR_HEAD + +# INVALID_LVALUE +c = Core.Inference.copy_code_info(c0) +insert!(c.code, 4, Expr(:(=), LabelNode(1), 1)) +insert!(c.code, 2, Expr(:(=), :x, 1)) +insert!(c.code, 10, Expr(:(=), 3, 1)) +errors = Core.Inference.validate_code(c) +@test length(errors) == 3 +@test all(e.kind === Core.Inference.INVALID_LVALUE for e in errors) + +# INVALID_RVALUE +c = Core.Inference.copy_code_info(c0) +insert!(c.code, 2, Expr(:(=), SlotNumber(2), GotoNode(1))) +insert!(c.code, 4, Expr(:(=), SlotNumber(2), LabelNode(2))) +insert!(c.code, 10, Expr(:(=), SlotNumber(2), LineNumberNode(2))) +for h in (:gotoifnot, :line, :const, :meta) + push!(c.code, Expr(:(=), SlotNumber(2), Expr(h))) +end +errors = Core.Inference.validate_code(c) +@test length(errors) == 10 +@test count(e.kind === Core.Inference.INVALID_RVALUE for e in errors) == 7 +@test count(e.kind === Core.Inference.INVALID_EXPR_NARGS for e in errors) == 3 + +# INVALID_CALL_ARG/INVALID_EXPR_NARGS +c = Core.Inference.copy_code_info(c0) +insert!(c.code, 2, Expr(:(=), SlotNumber(2), Expr(:call, :+, SlotNumber(2), GotoNode(1)))) +insert!(c.code, 4, Expr(:call, :-, Expr(:call, :sin, LabelNode(2)), 3)) +insert!(c.code, 10, Expr(:call, LineNumberNode(2))) +for h in (:gotoifnot, :line, :const, :meta) + push!(c.code, Expr(:call, :f, Expr(h))) +end +errors = Core.Inference.validate_code(c) +@test length(errors) == 10 +@test count(e.kind === Core.Inference.INVALID_CALL_ARG for e in errors) == 7 +@test count(e.kind === Core.Inference.INVALID_EXPR_NARGS for e in errors) == 3 + +# EMPTY_SLOTNAMES +c = Core.Inference.copy_code_info(c0) +empty!(c.slotnames) +errors = Core.Inference.validate_code(c) +@test length(errors) == 2 +@test any(e.kind === Core.Inference.EMPTY_SLOTNAMES for e in errors) +@test any(e.kind === Core.Inference.SLOTFLAGS_MISMATCH for e in errors) + +# SLOTFLAGS_MISMATCH +c = Core.Inference.copy_code_info(c0) +push!(c.slotnames, :dummy) +errors = Core.Inference.validate_code(c) +@test length(errors) == 1 +@test errors[1].kind === Core.Inference.SLOTFLAGS_MISMATCH + +# SLOTTYPES_MISMATCH +c = @code_typed(f22938(1,2,3,4))[1] +pop!(c.slottypes) +errors = Core.Inference.validate_code(c) +@test length(errors) == 1 +@test errors[1].kind === Core.Inference.SLOTTYPES_MISMATCH + +# SLOTTYPES_MISMATCH_UNINFERRED +c = Core.Inference.copy_code_info(c0) +c.slottypes = 1 +errors = Core.Inference.validate_code(c) +@test length(errors) == 1 +@test errors[1].kind === Core.Inference.SLOTTYPES_MISMATCH_UNINFERRED + +# SSAVALUETYPES_MISMATCH +c = @code_typed(f22938(1,2,3,4))[1] +empty!(c.ssavaluetypes) +errors = Core.Inference.validate_code(c) +@test length(errors) == 1 +@test errors[1].kind === Core.Inference.SSAVALUETYPES_MISMATCH + +# SSAVALUETYPES_MISMATCH_UNINFERRED +c = Core.Inference.copy_code_info(c0) +c.ssavaluetypes -= 1 +errors = Core.Inference.validate_code(c) +@test length(errors) == 1 +@test errors[1].kind === Core.Inference.SSAVALUETYPES_MISMATCH_UNINFERRED + +# INVALID_ASSIGNMENT_SLOTFLAG +c = Core.Inference.copy_code_info(c0) +c.slotflags[8] = 0x00 +errors = Core.Inference.validate_code(c) +@test length(errors) == 1 +@test errors[1].kind === Core.Inference.INVALID_ASSIGNMENT_SLOTFLAG + +# SIGNATURE_NARGS_MISMATCH +old_sig = mi.def.sig +mi.def.sig = Tuple{1,2} +errors = Core.Inference.validate_code(mi) +mi.def.sig = old_sig +@test length(errors) == 1 +@test errors[1].kind === Core.Inference.SIGNATURE_NARGS_MISMATCH + +# NON_TOP_LEVEL_METHOD +c = Core.Inference.copy_code_info(c0) +push!(c.code, Expr(:method, :dummy)) +errors = Core.Inference.validate_code(c) +@test length(errors) == 1 +@test errors[1].kind === Core.Inference.NON_TOP_LEVEL_METHOD + +# SLOTNAMES_NARGS_MISMATCH +mi.def.nargs += 20 +errors = Core.Inference.validate_code(mi) +mi.def.nargs -= 20 +@test length(errors) == 2 +@test count(e.kind === Core.Inference.SLOTNAMES_NARGS_MISMATCH for e in errors) == 1 +@test count(e.kind === Core.Inference.SIGNATURE_NARGS_MISMATCH for e in errors) == 1 From 104dce7fcf26f47163b20ffc4b1a628a564d6e96 Mon Sep 17 00:00:00 2001 From: Nathan Smith Date: Fri, 11 Aug 2017 09:01:11 -0700 Subject: [PATCH 017/324] remove duplicate skipchars documentation (#22311) * remove duplicate skipchars documentation --- base/docs/helpdb/Base.jl | 10 ---------- base/io.jl | 8 ++++---- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/base/docs/helpdb/Base.jl b/base/docs/helpdb/Base.jl index 71011c1d32047..616c7ca0dd268 100644 --- a/base/docs/helpdb/Base.jl +++ b/base/docs/helpdb/Base.jl @@ -1977,16 +1977,6 @@ Values for `String` can be of that type, or `Vector{UInt8}`. """ isvalid(T,value) -""" - skipchars(stream, predicate; linecomment::Char) - -Advance the stream until before the first character for which `predicate` returns `false`. -For example `skipchars(stream, isspace)` will skip all whitespace. If keyword argument -`linecomment` is specified, characters from that character through the end of a line will -also be skipped. -""" -skipchars - """ pop!(collection, key[, default]) diff --git a/base/io.jl b/base/io.jl index 6af638cc97a76..5959e94434b64 100644 --- a/base/io.jl +++ b/base/io.jl @@ -660,11 +660,11 @@ flush(io::IO) = nothing """ skipchars(io::IO, predicate; linecomment=nothing) -Skip forward in `io` until `predicate` returns `false`. If `linecomment` -is defined, all characters after the `linecomment` character are ignored -until the next line. +Advance the stream `io` such that the next-read character will be the first remaining for +which `predicate` returns `false`. If the keyword argument `linecomment` is specified, all +characters from that character until the start of the next line are ignored. -```jldoctext +```jldoctest julia> buf = IOBuffer(" text") IOBuffer(data=UInt8[...], readable=true, writable=false, seekable=true, append=false, size=8, maxsize=Inf, ptr=1, mark=-1) From eabd0212ed974b7128ec77180424090edb4a6d04 Mon Sep 17 00:00:00 2001 From: Mus M Date: Fri, 11 Aug 2017 16:34:39 -0400 Subject: [PATCH 018/324] Make fast_math FloatTypes typealias a const (#23219) --- base/fastmath.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/fastmath.jl b/base/fastmath.jl index 7fff15810f590..0256d0e8e0761 100644 --- a/base/fastmath.jl +++ b/base/fastmath.jl @@ -153,7 +153,7 @@ end # Basic arithmetic -FloatTypes = Union{Float32, Float64} +const FloatTypes = Union{Float32,Float64} sub_fast(x::FloatTypes) = neg_float_fast(x) From e4bb6d76c790c906ff13c78b67bb619d337f3e51 Mon Sep 17 00:00:00 2001 From: Sacha Verweij Date: Fri, 11 Aug 2017 17:24:27 -0700 Subject: [PATCH 019/324] deprecate vectorized methods hiding in Dates (#23207) * Deprecate vectorized DateTime methods in favor of compact broadcast syntax. * Deprecate vectorized Date methods in favor of compact broadcast syntax. * Deprecate vectorized Dates.format methods in favor of compact broadcast syntax. * Change a few Dates tests to use the dateformat string macro. --- NEWS.md | 5 +++++ base/dates/io.jl | 21 --------------------- base/deprecated.jl | 20 ++++++++++++++++++++ test/dates/io.jl | 13 ++++++------- 4 files changed, 31 insertions(+), 28 deletions(-) diff --git a/NEWS.md b/NEWS.md index e170bb45091f3..65dbd86392592 100644 --- a/NEWS.md +++ b/NEWS.md @@ -303,6 +303,9 @@ Deprecated or removed * Calling `union` with no arguments is deprecated; construct an empty set with an appropriate element type using `Set{T}()` instead ([#23144]). + * Vectorized `DateTime`, `Date`, and `format` methods have been deprecated in favor of + dot-syntax ([#23207]). + * `Base.cpad` has been removed; use an appropriate combination of `rpad` and `lpad` instead ([#23187]). @@ -1160,3 +1163,5 @@ Command-line option changes [#23117]: https://github.com/JuliaLang/julia/issues/23117 [#23144]: https://github.com/JuliaLang/julia/issues/23144 [#23157]: https://github.com/JuliaLang/julia/issues/23157 +[#23187]: https://github.com/JuliaLang/julia/issues/23187 +[#23207]: https://github.com/JuliaLang/julia/issues/23207 diff --git a/base/dates/io.jl b/base/dates/io.jl index 2005b941effab..78f9ef4bb5346 100644 --- a/base/dates/io.jl +++ b/base/dates/io.jl @@ -557,24 +557,3 @@ function Base.string(dt::Date) dd = lpad(d, 2, "0") return "$yy-$mm-$dd" end - -# vectorized -function DateTime(Y::AbstractArray{<:AbstractString}, f::AbstractString; locale::Locale=ENGLISH) - DateTime(Y, DateFormat(f, locale)) -end -function DateTime(Y::AbstractArray{<:AbstractString}, df::DateFormat=ISODateTimeFormat) - return reshape(DateTime[parse(DateTime, y, df) for y in Y], size(Y)) -end -function Date(Y::AbstractArray{<:AbstractString}, f::AbstractString; locale::Locale=ENGLISH) - Date(Y, DateFormat(f, locale)) -end -function Date(Y::AbstractArray{<:AbstractString}, df::DateFormat=ISODateFormat) - return reshape(Date[Date(parse(Date, y, df)) for y in Y], size(Y)) -end - -function format(Y::AbstractArray{<:TimeType}, f::AbstractString; locale::Locale=ENGLISH) - format(Y, DateFormat(f, locale)) -end -function format(Y::AbstractArray{T}, df::DateFormat=default_format(T)) where T<:TimeType - return reshape([format(y, df) for y in Y], size(Y)) -end diff --git a/base/deprecated.jl b/base/deprecated.jl index 9c60b9a645c31..ab6ee91e54797 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1585,6 +1585,26 @@ for op in (:exp, :exp2, :exp10, :log, :log2, :log10, @eval @deprecate ($op)(x::AbstractSparseVector{<:Number,<:Integer}) ($op).(x) end +# deprecate remaining vectorized methods from Base.Dates +@eval Dates @deprecate( + DateTime(Y::AbstractArray{<:AbstractString}, f::AbstractString; locale::Locale=ENGLISH), + DateTime.(Y, f; locale=locale) ) +@eval Dates @deprecate( + DateTime(Y::AbstractArray{<:AbstractString}, df::DateFormat=ISODateTimeFormat), + DateTime.(Y, df) ) +@eval Dates @deprecate( + Date(Y::AbstractArray{<:AbstractString}, f::AbstractString; locale::Locale=ENGLISH), + Date.(Y, f; locale=locale) ) +@eval Dates @deprecate( + Date(Y::AbstractArray{<:AbstractString}, df::DateFormat=ISODateFormat), + Date.(Y, df) ) +@eval Dates @deprecate( + format(Y::AbstractArray{<:TimeType}, f::AbstractString; locale::Locale=ENGLISH), + format.(Y, f; locale=locale) ) +@eval Dates @deprecate( + format(Y::AbstractArray{T}, df::DateFormat=default_format(T)) where {T<:TimeType}, + format.(Y, df) ) + # PR #22182 @deprecate is_apple Sys.isapple @deprecate is_bsd Sys.isbsd diff --git a/test/dates/io.jl b/test/dates/io.jl index 4a34d87859637..c7523277e4535 100644 --- a/test/dates/io.jl +++ b/test/dates/io.jl @@ -319,19 +319,18 @@ f = "ymd" @test Dates.Date(string(Dates.Date(dt))) == Dates.Date(dt) @test Dates.DateTime(string(dt)) == dt -# Vectorized +# formerly vectorized Date/DateTime/format methods dr = ["2000-01-01", "2000-01-02", "2000-01-03", "2000-01-04", "2000-01-05", "2000-01-06", "2000-01-07", "2000-01-08", "2000-01-09", "2000-01-10"] dr2 = [Dates.Date(2000) : Dates.Date(2000, 1, 10);] -@test Dates.Date(dr) == dr2 -@test Dates.Date(dr, "yyyy-mm-dd") == dr2 +@test Dates.Date.(dr) == dr2 +@test Dates.Date.(dr, dateformat"yyyy-mm-dd") == dr2 @test Dates.DateTime.(dr) == Dates.DateTime.(dr2) -@test Dates.DateTime(dr, "yyyy-mm-dd") == Dates.DateTime.(dr2) +@test Dates.DateTime.(dr, dateformat"yyyy-mm-dd") == Dates.DateTime.(dr2) -@test Dates.format(dr2) == dr -@test Dates.format(dr2, "yyyy-mm-dd") == dr +@test Dates.format.(dr2, "yyyy-mm-dd") == dr -@test typeof(Dates.Date(dr)) == Array{Date, 1} +@test typeof(Dates.Date.(dr)) == Array{Date, 1} # Issue 13 t = Dates.DateTime(1, 1, 1, 14, 51, 0, 118) From 6fe3b8e7e83a567bb2d88ce30edcd6daca7041db Mon Sep 17 00:00:00 2001 From: Simon Byrne Date: Fri, 11 Aug 2017 18:30:37 -0700 Subject: [PATCH 020/324] Correct typo in num2hex deprecation (#23222) Introduced by #22088. --- base/deprecated.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/deprecated.jl b/base/deprecated.jl index ab6ee91e54797..a9a5c93e269e5 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1678,7 +1678,7 @@ function hex2num(s::AbstractString) return reinterpret(Float64, parse(UInt64, s, 16)) end -@deprecate num2hex(x::Union{Float16,Float32,Float64}) hex(reintepret(Unsigned, x), sizeof(x)*2) +@deprecate num2hex(x::Union{Float16,Float32,Float64}) hex(reinterpret(Unsigned, x), sizeof(x)*2) @deprecate num2hex(n::Integer) hex(n, sizeof(n)*2) # PR #22742: change in isapprox semantics From 1343e235f9fa15c69aab73b685d9ed20066ba964 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Sat, 12 Aug 2017 11:04:33 +0200 Subject: [PATCH 021/324] REPL completion: indentifers after using must be comma separated --- base/repl/REPLCompletions.jl | 2 +- test/replcompletions.jl | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/base/repl/REPLCompletions.jl b/base/repl/REPLCompletions.jl index bdb84d972b933..32abb00134f56 100644 --- a/base/repl/REPLCompletions.jl +++ b/base/repl/REPLCompletions.jl @@ -368,7 +368,7 @@ function afterusing(string::String, startpos::Int) r = search(rstr, r"\s(gnisu|tropmi)\b") isempty(r) && return false fr = reverseind(str, last(r)) - return ismatch(r"^\b(using|import)\s*((\w+[.])*\w+\s*,\s*)*\w*$", str[fr:end]) + return ismatch(r"^\b(using|import)\s*((\w+[.])*\w+\s*,\s*)*$", str[fr:end]) end function bslash_completions(string, pos) diff --git a/test/replcompletions.jl b/test/replcompletions.jl index 6e4630aa52eaf..20c0a0d1d89aa 100644 --- a/test/replcompletions.jl +++ b/test/replcompletions.jl @@ -167,6 +167,11 @@ s = "using Base.Test, Base.Random" c,r = test_complete(s) @test !("RandomDevice" in c) +# issue #23226: identifiers must be separated by a comma (not a newline) +s = "using Base\nusi" +c,r = test_complete(s) +@test "using" in c + # inexistent completion inside a string s = "Pkg.add(\"lol" c,r,res = test_complete(s) From 4c5cc04156ba074a8baa028c2a8a41b9e70d56ee Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Sat, 12 Aug 2017 15:47:22 +0200 Subject: [PATCH 022/324] remove .jl extension in Pkg.update (#23214) * remove .jl extension in Pkg.update * add test --- base/pkg/pkg.jl | 2 +- test/pkg.jl | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/base/pkg/pkg.jl b/base/pkg/pkg.jl index 7d60ec9e6ce9d..2614c8d3d3939 100644 --- a/base/pkg/pkg.jl +++ b/base/pkg/pkg.jl @@ -225,7 +225,7 @@ optimal set of packages versions. Without arguments, updates all installed packages. When one or more package names are provided as arguments, only those packages and their dependencies are updated. """ -update(upkgs::AbstractString...) = cd(Entry.update,Dir.getmetabranch(),Set{String}([upkgs...])) +update(upkgs::AbstractString...) = cd(Entry.update,Dir.getmetabranch(),Set{String}(splitjl.([upkgs...]))) """ resolve() diff --git a/test/pkg.jl b/test/pkg.jl index dbb8cf1161e39..937ec7bba2c72 100644 --- a/test/pkg.jl +++ b/test/pkg.jl @@ -588,6 +588,7 @@ end Pkg.add("Example.jl") @test [keys(Pkg.installed())...] == ["Example"] iob = IOBuffer() + Pkg.update("Example.jl") Pkg.checkout("Example.jl") Pkg.status("Example.jl", iob) str = chomp(String(take!(iob))) From 61d3c50d4c29a429abf52293f0dcc4d9bd35532f Mon Sep 17 00:00:00 2001 From: Yichao Yu Date: Sun, 13 Aug 2017 17:53:33 -0400 Subject: [PATCH 023/324] Fix jl_option initialization --- src/jloptions.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/jloptions.c b/src/jloptions.c index 61698c7c38e3d..57eac8f2eaa15 100644 --- a/src/jloptions.c +++ b/src/jloptions.c @@ -69,6 +69,7 @@ jl_options_t jl_options = { 0, // quiet NULL, // bind-to NULL, // output-bc NULL, // output-unopt-bc + NULL, // output-jit-bc NULL, // output-o NULL, // output-ji 0, // incremental From cfe3fa7834bd99c861b53d19a0a1b12528fd713f Mon Sep 17 00:00:00 2001 From: Yichao Yu Date: Sun, 13 Aug 2017 17:54:32 -0400 Subject: [PATCH 024/324] Fix codevalidation recompilation --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 3ce18c5d0d42f..8de1178e4c154 100644 --- a/Makefile +++ b/Makefile @@ -187,6 +187,7 @@ CORE_SRCS := $(addprefix $(JULIAHOME)/, \ base/array.jl \ base/bool.jl \ base/associative.jl \ + base/codevalidation.jl \ base/error.jl \ base/essentials.jl \ base/generator.jl \ From 34a606fc5b892b817d25b09cfa4ed7412aa3c552 Mon Sep 17 00:00:00 2001 From: Yichao Yu Date: Sun, 13 Aug 2017 17:58:16 -0400 Subject: [PATCH 025/324] Hide/remove unused slot flags after lowering This gives us more freedom to pick slot flags in inference. Also remove the corresponding code validation tests. --- base/codevalidation.jl | 6 ------ base/inference.jl | 3 +-- src/method.c | 4 +++- test/codevalidation.jl | 7 ------- 4 files changed, 4 insertions(+), 16 deletions(-) diff --git a/base/codevalidation.jl b/base/codevalidation.jl index 7605a1654af16..10507aea2f386 100644 --- a/base/codevalidation.jl +++ b/base/codevalidation.jl @@ -27,8 +27,6 @@ const VALID_EXPR_HEADS = ObjectIdDict( :simdloop => 0:0 ) -const ASSIGNED_FLAG = 0x02 - # @enum isn't defined yet, otherwise I'd use it for this const INVALID_EXPR_HEAD = "invalid expression head" const INVALID_EXPR_NARGS = "invalid number of expression args" @@ -41,7 +39,6 @@ const SLOTTYPES_MISMATCH = "length(slotnames) != length(slottypes)" const SLOTTYPES_MISMATCH_UNINFERRED = "uninferred CodeInfo slottypes field is not `nothing`" const SSAVALUETYPES_MISMATCH = "not all SSAValues in AST have a type in ssavaluetypes" const SSAVALUETYPES_MISMATCH_UNINFERRED = "uninferred CodeInfo ssavaluetypes field does not equal the number of present SSAValues" -const INVALID_ASSIGNMENT_SLOTFLAG = "slot has wrong assignment slotflag setting (bit flag 2 not set)" const NON_TOP_LEVEL_METHOD = "encountered `Expr` head `:method` in non-top-level code (i.e. `nargs` > 0)" const SIGNATURE_NARGS_MISMATCH = "method signature does not match number of method arguments" const SLOTNAMES_NARGS_MISMATCH = "CodeInfo for method contains fewer slotnames than the number of method arguments" @@ -76,9 +73,6 @@ function validate_code!(errors::Vector{>:InvalidCodeError}, c::CodeInfo, is_top_ push!(errors, InvalidCodeError(INVALID_LVALUE, lhs)) elseif isa(lhs, SlotNumber) && !in(lhs.id, lhs_slotnums) n = lhs.id - if isassigned(c.slotflags, n) && !is_flag_set(c.slotflags[n], ASSIGNED_FLAG) - push!(errors, InvalidCodeError(INVALID_ASSIGNMENT_SLOTFLAG, lhs)) - end push!(lhs_slotnums, n) end if !is_valid_rvalue(rhs) diff --git a/base/inference.jl b/base/inference.jl index 2c899590a290a..78aaf2adbd8f1 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -53,7 +53,6 @@ end # cond && use(a) # slot property bit flags -const Slot_Assigned = 2 const Slot_AssignedOnce = 16 const Slot_UsedUndef = 32 @@ -5110,7 +5109,7 @@ function add_slot!(src::CodeInfo, @nospecialize(typ), is_sa::Bool, name::Symbol= id = length(src.slotnames) + 1 push!(src.slotnames, name) push!(src.slottypes, typ) - push!(src.slotflags, Slot_Assigned + is_sa * Slot_AssignedOnce) + push!(src.slotflags, is_sa * Slot_AssignedOnce) return SlotNumber(id) end diff --git a/src/method.c b/src/method.c index 6f43a7b95d499..d32a6c19d881e 100644 --- a/src/method.c +++ b/src/method.c @@ -170,6 +170,8 @@ static void jl_code_info_set_ast(jl_code_info_t *li, jl_expr_t *ast) jl_gc_wb(li, li->slotflags); li->ssavaluetypes = jl_box_long(nssavalue); jl_gc_wb(li, li->ssavaluetypes); + // Flags that need to be copied to slotflags + const uint8_t vinfo_mask = 16 | 32 | 64; int i; for (i = 0; i < nslots; i++) { jl_value_t *vi = jl_array_ptr_ref(vis, i); @@ -187,7 +189,7 @@ static void jl_code_info_set_ast(jl_code_info_t *li, jl_expr_t *ast) } } jl_array_ptr_set(li->slotnames, i, name); - jl_array_uint8_set(li->slotflags, i, jl_unbox_long(jl_array_ptr_ref(vi, 2))); + jl_array_uint8_set(li->slotflags, i, vinfo_mask & jl_unbox_long(jl_array_ptr_ref(vi, 2))); } } diff --git a/test/codevalidation.jl b/test/codevalidation.jl index 0ed65869dcad5..829df9759c473 100644 --- a/test/codevalidation.jl +++ b/test/codevalidation.jl @@ -105,13 +105,6 @@ errors = Core.Inference.validate_code(c) @test length(errors) == 1 @test errors[1].kind === Core.Inference.SSAVALUETYPES_MISMATCH_UNINFERRED -# INVALID_ASSIGNMENT_SLOTFLAG -c = Core.Inference.copy_code_info(c0) -c.slotflags[8] = 0x00 -errors = Core.Inference.validate_code(c) -@test length(errors) == 1 -@test errors[1].kind === Core.Inference.INVALID_ASSIGNMENT_SLOTFLAG - # SIGNATURE_NARGS_MISMATCH old_sig = mi.def.sig mi.def.sig = Tuple{1,2} From 8f8c951c907841c2711ea1c941b7606834417294 Mon Sep 17 00:00:00 2001 From: Yichao Yu Date: Sun, 13 Aug 2017 18:01:12 -0400 Subject: [PATCH 026/324] Make tests more robusts for linear IR * Do not assume `boot.jl` won't appear in backtrace. It will because of `eval`. * Try harder to avoid inference removing slots. --- test/reflection.jl | 38 ++++++++++++++++++++++++++++++-------- test/test.jl | 2 +- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/test/reflection.jl b/test/reflection.jl index 10e2a17f9aafe..3ae431d744e7c 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -389,8 +389,17 @@ function g15714(array_var15714) for index_var15714 in eachindex(array_var15714) array_var15714[index_var15714] += 0 end - for index_var15714 in eachindex(array_var15714) - array_var15714[index_var15714] += 0 + let index_var15714 + for index_var15714 in eachindex(array_var15714) + array_var15714[index_var15714] += 0 + end + index_var15714 + end + let index_var15714 + for index_var15714 in eachindex(array_var15714) + array_var15714[index_var15714] += 0 + end + index_var15714 end end @@ -411,6 +420,10 @@ function test_typed_ast_printing(Base.@nospecialize(f), Base.@nospecialize(types for name in must_used_vars @test name in slotnames end + must_used_checked = Dict{Symbol,Bool}() + for sym in must_used_vars + must_used_checked[sym] = false + end for str in (sprint(code_warntype, f, types), stringmime("text/plain", src)) for var in must_used_vars @@ -422,31 +435,40 @@ function test_typed_ast_printing(Base.@nospecialize(f), Base.@nospecialize(types for i in 1:length(src.slotnames) name = src.slotnames[i] if name in dupnames - @test contains(str, "_$i") - if name in must_used_vars + if name in must_used_vars && ismatch(Regex("_$i\\b"), str) + must_used_checked[name] = true global used_dup_var_tested15714 = true end else - @test !contains(str, "_$i") + @test !ismatch(Regex("_$i\\b"), str) if name in must_used_vars global used_unique_var_tested15714 = true end end end end + for sym in must_used_vars + if sym in dupnames + @test must_used_checked[sym] + end + must_used_checked[sym] = false + end # Make sure printing an AST outside CodeInfo still works. str = sprint(show, src.code) # Check that we are printing the slot numbers when we don't have the context # Use the variable names that we know should be present in the optimized AST for i in 2:length(src.slotnames) name = src.slotnames[i] - if name in must_used_vars - @test contains(str, "_$i") + if name in must_used_vars && ismatch(Regex("_$i\\b"), str) + must_used_checked[name] = true end end + for sym in must_used_vars + @test must_used_checked[sym] + end end test_typed_ast_printing(f15714, Tuple{Vector{Float32}}, - [:array_var15714, :index_var15714]) + [:array_var15714]) test_typed_ast_printing(g15714, Tuple{Vector{Float32}}, [:array_var15714, :index_var15714]) @test used_dup_var_tested15714 diff --git a/test/test.jl b/test/test.jl index 5bb97f6510bd6..96897aae0170b 100644 --- a/test/test.jl +++ b/test/test.jl @@ -521,7 +521,7 @@ str = String(take!(io)) # NOTE: This test depends on the code generated by @testset getting compiled, # to get good backtraces. If it fails, check the implementation of `testset_beginend`. @test contains(str, "test.jl") -@test !contains(str, "boot.jl") +@test !contains(str, "client.jl") let io = IOBuffer() exc = Test.TestSetException(1,2,3,4,Vector{Union{Base.Test.Error, Base.Test.Fail}}()) From 131807cc574bc8452b463247d4903d2eb6cfec6b Mon Sep 17 00:00:00 2001 From: Simon Byrne Date: Mon, 14 Aug 2017 01:45:13 -0700 Subject: [PATCH 027/324] Remove vestigial srand (#19980) This should only be merged after: - https://github.com/JuliaLang/Rmath-julia/pull/14 - https://github.com/JuliaStats/Rmath.jl/pull/21 have been merged and releases of those packages tagged. --- base/random.jl | 9 --------- 1 file changed, 9 deletions(-) diff --git a/base/random.jl b/base/random.jl index c8ca64d16eacf..6a332d05f555d 100644 --- a/base/random.jl +++ b/base/random.jl @@ -281,21 +281,12 @@ true srand(r::MersenneTwister) = srand(r, make_seed()) srand(r::MersenneTwister, n::Integer) = srand(r, make_seed(n)) - -function dsfmt_gv_srand() - # Temporary fix for #8874 and #9124: update global RNG for Rmath - dsfmt_gv_init_by_array(GLOBAL_RNG.seed+UInt32(1)) - return GLOBAL_RNG -end - function srand() srand(GLOBAL_RNG) - dsfmt_gv_srand() end function srand(seed::Union{Integer,Vector{UInt32}}) srand(GLOBAL_RNG, seed) - dsfmt_gv_srand() end ## Global RNG From d962dd5c92d46e48fb28c496da079578db2ae4d9 Mon Sep 17 00:00:00 2001 From: "Scott P. Jones" Date: Mon, 14 Aug 2017 10:26:44 -0400 Subject: [PATCH 028/324] preserve NaN payload in convert from 16 to 32 bit --- base/float.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/float.jl b/base/float.jl index 40242fab6b112..c24ee9b452041 100644 --- a/base/float.jl +++ b/base/float.jl @@ -182,7 +182,7 @@ function convert(::Type{Float32}, val::Float16) ret = 0xff800000 end else # NaN - ret = 0x7fc00000 | (sign<<31) + ret = 0x7fc00000 | (sign<<31) | (sig<<(23-10)) end else sign = sign << 31 From 7c9bf01046c59b95324c9bfb28e00d91b71aa5e3 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Mon, 14 Aug 2017 17:48:21 +0200 Subject: [PATCH 029/324] add command option to remove method redefinition warnings (#23002) the warning is enabled only for sysimg, Pkg.test, and precompile --- Makefile | 2 +- NEWS.md | 6 ++++++ base/loading.jl | 2 +- base/options.jl | 1 + base/pkg/entry.jl | 1 + doc/man/julia.1 | 4 ++++ doc/src/manual/getting-started.md | 1 + src/gf.c | 2 +- src/jloptions.c | 12 ++++++++++++ src/julia.h | 4 ++++ test/cmdlineargs.jl | 27 +++++++++++++++++++++++++++ test/core.jl | 11 ----------- test/misc.jl | 2 +- 13 files changed, 60 insertions(+), 15 deletions(-) diff --git a/Makefile b/Makefile index 3ce18c5d0d42f..b15e234ae8391 100644 --- a/Makefile +++ b/Makefile @@ -218,7 +218,7 @@ define sysimg_builder $$(build_private_libdir)/sys$1.o: $$(build_private_libdir)/inference.ji $$(JULIAHOME)/VERSION $$(BASE_SRCS) @$$(call PRINT_JULIA, cd $$(JULIAHOME)/base && \ $$(call spawn,$3) $2 -C $$(JULIA_CPU_TARGET) --output-o $$(call cygpath_w,$$@) $$(JULIA_SYSIMG_BUILD_FLAGS) \ - --startup-file=no --sysimage $$(call cygpath_w,$$<) sysimg.jl $$(RELBUILDROOT) \ + --startup-file=no --warn-overwrite=yes --sysimage $$(call cygpath_w,$$<) sysimg.jl $$(RELBUILDROOT) \ || { echo '*** This error is usually fixed by running `make clean`. If the error persists$$(COMMA) try `make cleanall`. ***' && false; } ) .SECONDARY: $(build_private_libdir)/sys$1.o endef diff --git a/NEWS.md b/NEWS.md index 65dbd86392592..02a4cb0beb31b 100644 --- a/NEWS.md +++ b/NEWS.md @@ -309,6 +309,12 @@ Deprecated or removed * `Base.cpad` has been removed; use an appropriate combination of `rpad` and `lpad` instead ([#23187]). +Command-line option changes +--------------------------- + + * New option `--warn-overwrite={yes|no}` to control the warning for overwriting method + definitions. The default is `no` ([#23002]). + Julia v0.6.0 Release Notes ========================== diff --git a/base/loading.jl b/base/loading.jl index b366015918401..12c0ba917c728 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -509,7 +509,7 @@ function create_expr_cache(input::String, output::String, concrete_deps::Vector{ """ io = open(pipeline(detach(`$(julia_cmd()) -O0 --output-ji $output --output-incremental=yes - --startup-file=no --history-file=no + --startup-file=no --history-file=no --warn-overwrite=yes --color=$(have_color ? "yes" : "no") --eval $code_object`), stderr=STDERR), "w", STDOUT) diff --git a/base/options.jl b/base/options.jl index 872fa771ec291..32530a17be183 100644 --- a/base/options.jl +++ b/base/options.jl @@ -23,6 +23,7 @@ struct JLOptions debug_level::Int8 check_bounds::Int8 depwarn::Int8 + warn_overwrite::Int8 can_inline::Int8 polly::Int8 fast_math::Int8 diff --git a/base/pkg/entry.jl b/base/pkg/entry.jl index 58487eac71db1..6dc47473cd17b 100644 --- a/base/pkg/entry.jl +++ b/base/pkg/entry.jl @@ -719,6 +719,7 @@ function test!(pkg::AbstractString, --color=$(Base.have_color ? "yes" : "no") --compilecache=$(Bool(Base.JLOptions().use_compilecache) ? "yes" : "no") --check-bounds=yes + --warn-overwrite=yes --startup-file=$(Base.JLOptions().startupfile != 2 ? "yes" : "no") $test_path ``` diff --git a/doc/man/julia.1 b/doc/man/julia.1 index cc77d36e07969..b1f40f9dabc37 100644 --- a/doc/man/julia.1 +++ b/doc/man/julia.1 @@ -160,6 +160,10 @@ or adhere to declarations in source code --depwarn={yes|no|error} Enable or disable syntax and method deprecation warnings ('error' turns warnings into errors) +.TP +--warn-overwrite={yes|no} +Enable or disable method overwrite warnings + .TP --output-o Generate an object file (including system image data) diff --git a/doc/src/manual/getting-started.md b/doc/src/manual/getting-started.md index d41d6b1bdfa03..42dee20115868 100644 --- a/doc/src/manual/getting-started.md +++ b/doc/src/manual/getting-started.md @@ -127,6 +127,7 @@ julia [switches] -- [programfile] [args...] --math-mode={ieee,fast} Disallow or enable unsafe floating point optimizations (overrides @fastmath declaration) --depwarn={yes|no|error} Enable or disable syntax and method deprecation warnings ("error" turns warnings into errors) + --warn-overwrite={yes|no} Enable or disable method overwrite warnings --output-o name Generate an object file (including system image data) --output-ji name Generate a system image data file (.ji) diff --git a/src/gf.c b/src/gf.c index 42c4d60fe7c52..210b6cef01ce9 100644 --- a/src/gf.c +++ b/src/gf.c @@ -1223,7 +1223,7 @@ static void method_overwrite(jl_typemap_entry_t *newentry, jl_method_t *oldvalue jl_method_t *method = (jl_method_t*)newentry->func.method; jl_module_t *newmod = method->module; jl_module_t *oldmod = oldvalue->module; - if (newmod != jl_main_module || oldmod != jl_main_module) { + if (jl_options.warn_overwrite == JL_OPTIONS_WARN_OVERWRITE_ON) { JL_STREAM *s = JL_STDERR; jl_printf(s, "WARNING: Method definition "); jl_static_show_func_sig(s, (jl_value_t*)newentry->sig); diff --git a/src/jloptions.c b/src/jloptions.c index 61698c7c38e3d..ece10e240904a 100644 --- a/src/jloptions.c +++ b/src/jloptions.c @@ -58,6 +58,7 @@ jl_options_t jl_options = { 0, // quiet #endif JL_OPTIONS_CHECK_BOUNDS_DEFAULT, // check_bounds 1, // deprecation warning + 0, // method overwrite warning 1, // can_inline JL_OPTIONS_POLLY_ON, // polly JL_OPTIONS_FAST_MATH_DEFAULT, @@ -123,6 +124,7 @@ static const char opts[] = // error and warning options " --depwarn={yes|no|error} Enable or disable syntax and method deprecation warnings (\"error\" turns warnings into errors)\n\n" + " --warn-overwrite={yes|no} Enable or disable method overwrite warnings" // compiler output options " --output-o name Generate an object file (including system image data)\n" @@ -156,6 +158,7 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) opt_output_unopt_bc, opt_output_bc, opt_depwarn, + opt_warn_overwrite, opt_inline, opt_polly, opt_math_mode, @@ -201,6 +204,7 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) { "output-ji", required_argument, 0, opt_output_ji }, { "output-incremental",required_argument, 0, opt_incremental }, { "depwarn", required_argument, 0, opt_depwarn }, + { "warn-overwrite", required_argument, 0, opt_warn_overwrite }, { "inline", required_argument, 0, opt_inline }, { "polly", required_argument, 0, opt_polly }, { "math-mode", required_argument, 0, opt_math_mode }, @@ -478,6 +482,14 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) else jl_errorf("julia: invalid argument to --depwarn={yes|no|error} (%s)", optarg); break; + case opt_warn_overwrite: + if (!strcmp(optarg,"yes")) + jl_options.warn_overwrite = JL_OPTIONS_WARN_OVERWRITE_ON; + else if (!strcmp(optarg,"no")) + jl_options.warn_overwrite = JL_OPTIONS_WARN_OVERWRITE_OFF; + else + jl_errorf("julia: invalid argument to --warn-overwrite={yes|no|} (%s)", optarg); + break; case opt_inline: if (!strcmp(optarg,"yes")) jl_options.can_inline = 1; diff --git a/src/julia.h b/src/julia.h index 83a77a9126810..6a0768575e597 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1692,6 +1692,7 @@ typedef struct { int8_t debug_level; int8_t check_bounds; int8_t depwarn; + int8_t warn_overwrite; int8_t can_inline; int8_t polly; int8_t fast_math; @@ -1752,6 +1753,9 @@ JL_DLLEXPORT int jl_generating_output(void); #define JL_OPTIONS_DEPWARN_ON 1 #define JL_OPTIONS_DEPWARN_ERROR 2 +#define JL_OPTIONS_WARN_OVERWRITE_OFF 0 +#define JL_OPTIONS_WARN_OVERWRITE_ON 1 + #define JL_OPTIONS_POLLY_ON 1 #define JL_OPTIONS_POLLY_OFF 0 diff --git a/test/cmdlineargs.jl b/test/cmdlineargs.jl index d60b44536bcc0..386504033579e 100644 --- a/test/cmdlineargs.jl +++ b/test/cmdlineargs.jl @@ -429,3 +429,30 @@ for precomp in ("yes", "no") @test length(lno.captures) == 1 @test parse(Int, lno.captures[1]) > 0 end + +# PR #23002 +let exename = `$(Base.julia_cmd()) --startup-file=no` + for (mac, flag, pfix, msg) in [("@test_nowarn", ``, "_1", ""), + ("@test_warn", `--warn-overwrite=yes`, "_2", "\"WARNING: Method definition\"")] + str = """ + using Base.Test + try + # issue #18725 + $mac $msg @eval Main begin + f18725$(pfix)(x) = 1 + f18725$(pfix)(x) = 2 + end + @test Main.f18725$(pfix)(0) == 2 + # PR #23030 + $mac $msg @eval Main module Module23030$(pfix) + f23030$(pfix)(x) = 1 + f23030$(pfix)(x) = 2 + end + catch + exit(-1) + end + exit(0) + """ + run(`$exename $flag -e $str`) + end +end diff --git a/test/core.jl b/test/core.jl index e760be057451d..0818c8cccd80c 100644 --- a/test/core.jl +++ b/test/core.jl @@ -4680,17 +4680,6 @@ end @test f14893() == 14893 @test M14893.f14893() == 14893 -# issue #18725 -@test_nowarn @eval Main begin - f18725(x) = 1 - f18725(x) = 2 -end -@test Main.f18725(0) == 2 -@test_warn "WARNING: Method definition f18725(Any) in module Module18725" @eval Main module Module18725 - f18725(x) = 1 - f18725(x) = 2 -end - # issue #19599 f19599(x::((S)->Vector{S})(T)...) where {T} = 1 @test f19599([1],[1]) == 1 diff --git a/test/misc.jl b/test/misc.jl index 7ca7084a9fd77..094e8dc0c9328 100644 --- a/test/misc.jl +++ b/test/misc.jl @@ -239,7 +239,7 @@ let redir_err = "redirect_stderr(STDOUT)" exename = Base.julia_cmd() script = "$redir_err; module A; f() = 1; end; A.f() = 1" - warning_str = read(`$exename --startup-file=no -e $script`, String) + warning_str = read(`$exename --warn-overwrite=yes --startup-file=no -e $script`, String) @test contains(warning_str, "f()") end From b5057f0e6593cc158bbe7d15a0f515004e6164bb Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Mon, 14 Aug 2017 11:49:45 -0400 Subject: [PATCH 030/324] fix #23218, lowering error in `(a,) = (b...,)` (#23242) --- src/julia-syntax.scm | 55 ++++++++++++++++++++++++++++---------------- test/core.jl | 6 +++++ test/fastmath.jl | 6 +++++ 3 files changed, 47 insertions(+), 20 deletions(-) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 172478f3550fc..0f177d8cee508 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -1397,24 +1397,32 @@ (unnecessary (tuple ,@(reverse elts)))) (let ((L (car lhss)) (R (car rhss))) - (if (and (symbol-like? L) - (or (not (pair? R)) (quoted? R) (equal? R '(null))) - ;; overwrite var immediately if it doesn't occur elsewhere - (not (contains (lambda (e) (eq-sym? e L)) (cdr rhss))) - (not (contains (lambda (e) (eq-sym? e R)) assigned))) - (loop (cdr lhss) - (cons L assigned) - (cdr rhss) - (cons (make-assignment L R) stmts) - after - (cons R elts)) - (let ((temp (if (eventually-call? L) (gensy) (make-ssavalue)))) - (loop (cdr lhss) - (cons L assigned) - (cdr rhss) - (cons (make-assignment temp R) stmts) - (cons (make-assignment L temp) after) - (cons temp elts)))))))) + (cond ((and (symbol-like? L) + (or (not (pair? R)) (quoted? R) (equal? R '(null))) + ;; overwrite var immediately if it doesn't occur elsewhere + (not (contains (lambda (e) (eq-sym? e L)) (cdr rhss))) + (not (contains (lambda (e) (eq-sym? e R)) assigned))) + (loop (cdr lhss) + (cons L assigned) + (cdr rhss) + (cons (make-assignment L R) stmts) + after + (cons R elts))) + ((vararg? R) + (let ((temp (make-ssavalue))) + `(block ,@(reverse stmts) + ,(make-assignment temp (cadr R)) + ,@(reverse after) + (= (tuple ,@lhss) ,temp) + (unnecessary (tuple ,@(reverse elts) (... ,temp)))))) + (else + (let ((temp (if (eventually-call? L) (gensy) (make-ssavalue)))) + (loop (cdr lhss) + (cons L assigned) + (cdr rhss) + (cons (make-assignment temp R) stmts) + (cons (make-assignment L temp) after) + (cons temp elts))))))))) ;; convert (lhss...) = x to tuple indexing (define (lower-tuple-assignment lhss x) @@ -2012,13 +2020,20 @@ ;; multiple assignment (let ((lhss (cdr lhs)) (x (caddr e))) + (define (sides-match? l r) + ;; l and r either have equal lengths, or r has a trailing ... + (cond ((null? l) (null? r)) + ((null? r) #f) + ((vararg? (car r)) (null? (cdr r))) + (else (sides-match? (cdr l) (cdr r))))) (if (and (pair? x) (pair? lhss) (eq? (car x) 'tuple) - (length= lhss (length (cdr x)))) + (sides-match? lhss (cdr x))) ;; (a, b, ...) = (x, y, ...) (expand-forms (tuple-to-assignments lhss x)) ;; (a, b, ...) = other - (let* ((xx (if (and (symbol? x) (not (memq x lhss))) + (let* ((xx (if (or (and (symbol? x) (not (memq x lhss))) + (ssavalue? x)) x (make-ssavalue))) (ini (if (eq? x xx) '() `((= ,xx ,(expand-forms x))))) (st (gensy))) diff --git a/test/core.jl b/test/core.jl index 0818c8cccd80c..1faaba8622958 100644 --- a/test/core.jl +++ b/test/core.jl @@ -5174,3 +5174,9 @@ module GlobalDef18933 @test @isdefined sincos @test sincos === Base.sincos end + +# issue #23218 +let idx = (7,5,9) + (v,) = (idx...,) + @test v == 7 +end diff --git a/test/fastmath.jl b/test/fastmath.jl index bac571c5bf2ab..8aaec484e81e1 100644 --- a/test/fastmath.jl +++ b/test/fastmath.jl @@ -202,3 +202,9 @@ let a = ones(2,2), b = ones(2,2) local c = 0 @test @fastmath(c |= 1) == 1 end + +# issue #23218 +let a = zeros(1), b = ones(1), idx = (1,) + @fastmath a[idx...] += b[idx...] + @test a == b +end From 37655e3f8fa4a2d2c89bcd51d3afb96feeea528e Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Mon, 14 Aug 2017 11:50:20 -0400 Subject: [PATCH 031/324] fix #23236 (#23243) comprehension eta-reduction needs to know that dot operators are not function names. --- src/julia-syntax.scm | 1 + test/broadcast.jl | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 0f177d8cee508..e6481ffdefe2d 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -1715,6 +1715,7 @@ (if (and (null? splat) (length= expr 3) (eq? (car expr) 'call) (eq? (caddr expr) argname) + (not (dotop? (cadr expr))) (not (expr-contains-eq argname (cadr expr)))) (cadr expr) ;; eta reduce `x->f(x)` => `f` `(-> ,argname (block ,@splat ,expr))))) diff --git a/test/broadcast.jl b/test/broadcast.jl index aa84edb2c700f..dc69b534234cf 100644 --- a/test/broadcast.jl +++ b/test/broadcast.jl @@ -247,6 +247,11 @@ let x = [1:4;] @test sin.(f17300kw.(x, y=1)) == sin.(f17300kw.(x; y=1)) == sin.(x .+ 1) end +# issue #23236 +let X = [[true,false],[false,true]] + @test [.!x for x in X] == [[false,true],[true,false]] +end + # splice escaping of @. let x = [4, -9, 1, -16] @test [2, 3, 4, 5] == @.(1 + sqrt($sort(abs(x)))) From c52fe6979e56c4a96c9818e06fe877bd008e96fe Mon Sep 17 00:00:00 2001 From: Stefan Karpinski Date: Mon, 14 Aug 2017 13:42:34 -0400 Subject: [PATCH 032/324] fix #23244, fix #23250: NaN16 <=> NaN32 convert --- base/float.jl | 4 ++++ test/numbers.jl | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/base/float.jl b/base/float.jl index c24ee9b452041..bdf9a47f0adcc 100644 --- a/base/float.jl +++ b/base/float.jl @@ -134,6 +134,10 @@ end function convert(::Type{Float16}, val::Float32) f = reinterpret(UInt32, val) + if isnan(val) + t = 0x8000 ⊻ (0x8000 & ((f >> 0x10) % UInt16)) + return reinterpret(Float16, t ⊻ ((f >> 0xd) % UInt16)) + end i = (f >> 23) & 0x1ff + 1 sh = shifttable[i] f &= 0x007fffff diff --git a/test/numbers.jl b/test/numbers.jl index 45603503c66bf..7caafb9d15717 100644 --- a/test/numbers.jl +++ b/test/numbers.jl @@ -2975,3 +2975,51 @@ f20065(B, i) = UInt8(B[i]) end @test inv(3//4) === 4//3 === 1 / (3//4) === 1 // (3//4) + +# issues #23244 & #23250 +@testset "convert preserves NaN payloads" begin + @testset "smallest NaNs" begin + @test convert(Float32, NaN16) === NaN32 + @test convert(Float32, -NaN16) === -NaN32 + @test convert(Float64, NaN16) === NaN64 + @test convert(Float64, -NaN16) === -NaN64 + @test convert(Float16, NaN32) === NaN16 + @test convert(Float16, -NaN32) === -NaN16 + @test convert(Float64, NaN32) === NaN64 + @test convert(Float64, -NaN32) === -NaN64 + @test convert(Float32, NaN64) === NaN32 + @test convert(Float32, -NaN64) === -NaN32 + @test convert(Float16, NaN64) === NaN16 + @test convert(Float16, -NaN64) === -NaN16 + end + + @testset "largest NaNs" begin + @test convert(Float32, reinterpret(Float16, typemax(UInt16))) === + reinterpret(Float32, typemax(UInt32) >> 13 << 13) + @test convert(Float64, reinterpret(Float16, typemax(UInt16))) === + reinterpret(Float64, typemax(UInt64) >> 42 << 42) + @test convert(Float16, reinterpret(Float32, typemax(UInt32))) === + reinterpret(Float16, typemax(UInt16) >> 00 << 00) + @test convert(Float64, reinterpret(Float32, typemax(UInt32))) === + reinterpret(Float64, typemax(UInt64) >> 29 << 29) + @test convert(Float32, reinterpret(Float64, typemax(UInt64))) === + reinterpret(Float32, typemax(UInt32) >> 00 << 00) + @test convert(Float16, reinterpret(Float64, typemax(UInt64))) === + reinterpret(Float16, typemax(UInt16) >> 00 << 00) + end + + @testset "random NaNs" begin + nans = AbstractFloat[NaN16, NaN32, NaN64] + F = [Float16, Float32, Float64] + U = [UInt16, UInt32, UInt64] + sig = [11, 24, 53] + for i = 1:length(F), j = 1:length(F) + for _ = 1:100 + nan = reinterpret(F[i], rand(U[i]) | reinterpret(U[i], nans[i])) + z = sig[i] - sig[j] + nan′ = i <= j ? nan : reinterpret(F[i], reinterpret(U[i], nan) >> z << z) + @test convert(F[i], convert(F[j], nan)) === nan′ + end + end + end +end From 771bae01cd5a9cce2daaed40d21f8606ef944a75 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Mon, 14 Aug 2017 21:47:07 -0400 Subject: [PATCH 033/324] fix #23234, parsing `function (x=0) ...` (#23258) --- src/julia-parser.scm | 11 ++++++++--- test/parse.jl | 9 +++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/julia-parser.scm b/src/julia-parser.scm index b7ca3a19577c6..f44659f19b076 100644 --- a/src/julia-parser.scm +++ b/src/julia-parser.scm @@ -1238,6 +1238,11 @@ (and (eq? (car sig) 'where) (valid-func-sig? paren (cadr sig)))))) +(define (valid-1arg-func-sig? sig) + (or (symbol? sig) + (and (pair? sig) (eq? (car sig) '|::|) + (symbol? (cadr sig))))) + (define (unwrap-where x) (if (and (pair? x) (eq? (car x) 'where)) (unwrap-where (cadr x)) @@ -1361,9 +1366,9 @@ (take-token s) `(function ,sig)) (let* ((usig (unwrap-where sig)) - (def (if (or (symbol? usig) - (and (pair? usig) (eq? (car usig) '|::|) - (symbol? (cadr usig)))) + (def (if (or (valid-1arg-func-sig? usig) + (and (assignment? usig) + (valid-1arg-func-sig? (cadr usig)))) (if paren ;; in "function (x)" the (x) is a tuple (rewrap-where `(tuple ,usig) sig) diff --git a/test/parse.jl b/test/parse.jl index befe1913f55fc..0711dce02b192 100644 --- a/test/parse.jl +++ b/test/parse.jl @@ -1300,3 +1300,12 @@ end # issue #23173 @test_throws ErrorException("invalid module path") eval(:(import $(:.))) + +# issue #23234 +let + f = function (x=0) + x + end + @test f() == 0 + @test f(2) == 2 +end From 52b9b574e41ee3605b5c2e5a07022654b3170940 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Mon, 14 Aug 2017 22:37:22 -0400 Subject: [PATCH 034/324] use more specific `Array` constructors in a few places (#23262) --- base/abstractarray.jl | 4 ++-- base/linalg/lapack.jl | 4 ++-- base/util.jl | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index a74127c3c770f..3e60aeb387646 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -1107,8 +1107,8 @@ vcat(X::T...) where {T<:Number} = T[ X[i] for i=1:length(X) ] hcat(X::T...) where {T} = T[ X[j] for i=1:1, j=1:length(X) ] hcat(X::T...) where {T<:Number} = T[ X[j] for i=1:1, j=1:length(X) ] -vcat(X::Number...) = hvcat_fill(Array{promote_typeof(X...)}(length(X)), X) -hcat(X::Number...) = hvcat_fill(Array{promote_typeof(X...)}(1,length(X)), X) +vcat(X::Number...) = hvcat_fill(Vector{promote_typeof(X...)}(length(X)), X) +hcat(X::Number...) = hvcat_fill(Matrix{promote_typeof(X...)}(1,length(X)), X) typed_vcat(::Type{T}, X::Number...) where {T} = hvcat_fill(Array{T,1}(length(X)), X) typed_hcat(::Type{T}, X::Number...) where {T} = hvcat_fill(Array{T,2}(1,length(X)), X) diff --git a/base/linalg/lapack.jl b/base/linalg/lapack.jl index 3a70e5a865d8a..fcc66b0409c8d 100644 --- a/base/linalg/lapack.jl +++ b/base/linalg/lapack.jl @@ -1546,8 +1546,8 @@ for (geev, gesvd, gesdd, ggsvd, elty, relty) in S = similar(A, $relty, minmn) cmplx = eltype(A)<:Complex if cmplx - rwork = Array{$relty}(job == 'N' ? 7*minmn : - minmn*max(5*minmn+7, 2*max(m,n)+2*minmn+1)) + rwork = Vector{$relty}(job == 'N' ? 7*minmn : + minmn*max(5*minmn+7, 2*max(m,n)+2*minmn+1)) end iwork = Vector{BlasInt}(8*minmn) info = Ref{BlasInt}() diff --git a/base/util.jl b/base/util.jl index 3edd92a861675..5011e1d1f7445 100644 --- a/base/util.jl +++ b/base/util.jl @@ -827,7 +827,7 @@ function crc32c(io::IO, nb::Integer, crc::UInt32=0x00000000) nb < 0 && throw(ArgumentError("number of bytes to checksum must be ≥ 0")) # use block size 24576=8192*3, since that is the threshold for # 3-way parallel SIMD code in the underlying jl_crc32c C function. - buf = Array{UInt8}(min(nb, 24576)) + buf = Vector{UInt8}(min(nb, 24576)) while !eof(io) && nb > 24576 n = readbytes!(io, buf) crc = unsafe_crc32c(buf, n, crc) From bdd0e99d50dd358180965a7de5176bca83727417 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Tue, 15 Aug 2017 10:46:33 -0400 Subject: [PATCH 035/324] reorganize a bit of early library and init code (#23256) Remove base.jl, since it no longer had a clear purpose. Its contents are moved to various more specific files. Move docs for Core from helpdb to basedocs.jl. --- base/array.jl | 11 ++ base/associative.jl | 10 + base/base.jl | 153 ---------------- base/boot.jl | 36 +++- base/docs/basedocs.jl | 383 +++++++++++++++++++++++++++++++++++++-- base/docs/helpdb/Base.jl | 317 -------------------------------- base/error.jl | 4 +- base/exports.jl | 7 - base/gcutils.jl | 27 +++ base/io.jl | 21 +++ base/nullabletype.jl | 9 + base/parse.jl | 10 + base/sysimg.jl | 5 +- src/abi_x86.cpp | 16 +- src/datatype.c | 9 + src/init.c | 19 +- src/jltypes.c | 5 - src/julia.h | 1 - src/staticdata.c | 4 - src/toplevel.c | 1 - 20 files changed, 522 insertions(+), 526 deletions(-) delete mode 100644 base/base.jl create mode 100644 base/gcutils.jl create mode 100644 base/nullabletype.jl diff --git a/base/array.jl b/base/array.jl index 300512537ad8d..7d3f231f691b8 100644 --- a/base/array.jl +++ b/base/array.jl @@ -2,6 +2,17 @@ ## array.jl: Dense arrays +""" + DimensionMismatch([msg]) + +The objects called do not have matching dimensionality. Optional argument `msg` is a +descriptive error string. +""" +mutable struct DimensionMismatch <: Exception + msg::AbstractString +end +DimensionMismatch() = DimensionMismatch("") + ## Type aliases for convenience ## """ AbstractVector{T} diff --git a/base/associative.jl b/base/associative.jl index 1d4f48bda6aa5..ba9c7e3ebc10b 100644 --- a/base/associative.jl +++ b/base/associative.jl @@ -2,6 +2,16 @@ # generic operations on associative collections +""" + KeyError(key) + +An indexing operation into an `Associative` (`Dict`) or `Set` like object tried to access or +delete a non-existent element. +""" +mutable struct KeyError <: Exception + key +end + const secret_table_token = :__c782dbf1cf4d6a2e5e3865d7e95634f2e09b5902__ haskey(d::Associative, k) = in(k,keys(d)) diff --git a/base/base.jl b/base/base.jl deleted file mode 100644 index 1130730737476..0000000000000 --- a/base/base.jl +++ /dev/null @@ -1,153 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -""" - SystemError(prefix::AbstractString, [errno::Int32]) - -A system call failed with an error code (in the `errno` global variable). -""" -mutable struct SystemError <: Exception - prefix::AbstractString - errnum::Int32 - extrainfo - SystemError(p::AbstractString, e::Integer, extrainfo) = new(p, e, extrainfo) - SystemError(p::AbstractString, e::Integer) = new(p, e, nothing) - SystemError(p::AbstractString) = new(p, Libc.errno()) -end - -""" - ParseError(msg) - -The expression passed to the `parse` function could not be interpreted as a valid Julia -expression. -""" -mutable struct ParseError <: Exception - msg::AbstractString -end - -""" - ArgumentError(msg) - -The parameters to a function call do not match a valid signature. Argument `msg` is a -descriptive error string. -""" -mutable struct ArgumentError <: Exception - msg::AbstractString -end - -""" - KeyError(key) - -An indexing operation into an `Associative` (`Dict`) or `Set` like object tried to access or -delete a non-existent element. -""" -mutable struct KeyError <: Exception - key -end - -""" - MethodError(f, args) - -A method with the required type signature does not exist in the given generic function. -Alternatively, there is no unique most-specific method. -""" -mutable struct MethodError <: Exception - f - args - world::UInt - MethodError(@nospecialize(f), @nospecialize(args), world::UInt) = new(f, args, world) -end -MethodError(@nospecialize(f), @nospecialize(args)) = MethodError(f, args, typemax(UInt)) - -""" - EOFError() - -No more data was available to read from a file or stream. -""" -mutable struct EOFError <: Exception end - -""" - DimensionMismatch([msg]) - -The objects called do not have matching dimensionality. Optional argument `msg` is a -descriptive error string. -""" -mutable struct DimensionMismatch <: Exception - msg::AbstractString -end -DimensionMismatch() = DimensionMismatch("") - -""" - AssertionError([msg]) - -The asserted condition did not evaluate to `true`. -Optional argument `msg` is a descriptive error string. -""" -mutable struct AssertionError <: Exception - msg::AbstractString - AssertionError() = new("") - AssertionError(msg) = new(msg) -end - -#Generic wrapping of arbitrary exceptions -#Subtypes should put the exception in an 'error' field -abstract type WrappedException <: Exception end - -""" - LoadError(file::AbstractString, line::Int, error) - -An error occurred while `include`ing, `require`ing, or `using` a file. The error specifics -should be available in the `.error` field. -""" -mutable struct LoadError <: WrappedException - file::AbstractString - line::Int - error -end - -""" - InitError(mod::Symbol, error) - -An error occurred when running a module's `__init__` function. The actual error thrown is -available in the `.error` field. -""" -mutable struct InitError <: WrappedException - mod::Symbol - error -end - -ccall(:jl_get_system_hooks, Void, ()) - - -==(w::WeakRef, v::WeakRef) = isequal(w.value, v.value) -==(w::WeakRef, v) = isequal(w.value, v) -==(w, v::WeakRef) = isequal(w, v.value) - -function finalizer(@nospecialize(o), @nospecialize(f)) - if isimmutable(o) - error("objects of type ", typeof(o), " cannot be finalized") - end - ccall(:jl_gc_add_finalizer_th, Void, (Ptr{Void}, Any, Any), - Core.getptls(), o, f) -end -function finalizer(o::T, f::Ptr{Void}) where T - @_inline_meta - if isimmutable(T) - error("objects of type ", T, " cannot be finalized") - end - ccall(:jl_gc_add_ptr_finalizer, Void, (Ptr{Void}, Any, Ptr{Void}), - Core.getptls(), o, f) -end - -finalize(@nospecialize(o)) = ccall(:jl_finalize_th, Void, (Ptr{Void}, Any,), - Core.getptls(), o) - -gc(full::Bool=true) = ccall(:jl_gc_collect, Void, (Int32,), full) -gc_enable(on::Bool) = ccall(:jl_gc_enable, Int32, (Int32,), on) != 0 - -struct Nullable{T} - hasvalue::Bool - value::T - - Nullable{T}() where {T} = new(false) - Nullable{T}(value::T, hasvalue::Bool=true) where {T} = new(hasvalue, value) -end diff --git a/base/boot.jl b/base/boot.jl index db7cdcbd1159e..be8f10aadd8dd 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -135,7 +135,7 @@ export ErrorException, BoundsError, DivideError, DomainError, Exception, InterruptException, InexactError, OutOfMemoryError, ReadOnlyMemoryError, OverflowError, StackOverflowError, SegmentationFault, UndefRefError, UndefVarError, - TypeError, + TypeError, ArgumentError, MethodError, AssertionError, LoadError, InitError, # AST representation Expr, GotoNode, LabelNode, LineNumberNode, QuoteNode, GlobalRef, NewvarNode, SSAValue, Slot, SlotNumber, TypedSlot, @@ -244,6 +244,40 @@ struct OverflowError <: Exception msg end +mutable struct ArgumentError <: Exception + msg::AbstractString +end + +mutable struct MethodError <: Exception + f + args + world::UInt + MethodError(@nospecialize(f), @nospecialize(args), world::UInt) = new(f, args, world) +end +const typemax_UInt = ccall(:jl_typemax_uint, Any, (Any,), UInt) +MethodError(@nospecialize(f), @nospecialize(args)) = MethodError(f, args, typemax_UInt) + +mutable struct AssertionError <: Exception + msg::AbstractString + AssertionError() = new("") + AssertionError(msg) = new(msg) +end + +#Generic wrapping of arbitrary exceptions +#Subtypes should put the exception in an 'error' field +abstract type WrappedException <: Exception end + +mutable struct LoadError <: WrappedException + file::AbstractString + line::Int + error +end + +mutable struct InitError <: WrappedException + mod::Symbol + error +end + abstract type DirectIndexString <: AbstractString end String(s::String) = s # no constructor yet diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index 40680e42c4972..1a2691f324782 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -682,6 +682,20 @@ A variable referring to the last computed value, automatically set at the intera """ kw"ans" +""" + DevNull + +Used in a stream redirect to discard all data written to it. Essentially equivalent to +/dev/null on Unix or NUL on Windows. Usage: + +```julia +run(pipeline(`cat test.txt`, DevNull)) +``` +""" +DevNull + +# doc strings for code in boot.jl and built-ins + """ nothing @@ -697,18 +711,6 @@ The singleton type containing only the value `Union{}`. """ Core.TypeofBottom -""" - DevNull - -Used in a stream redirect to discard all data written to it. Essentially equivalent to -/dev/null on Unix or NUL on Windows. Usage: - -```julia -run(pipeline(`cat test.txt`, DevNull)) -``` -""" -DevNull - """ Function @@ -727,4 +729,361 @@ true """ Function +""" + ReadOnlyMemoryError() + +An operation tried to write to memory that is read-only. +""" +ReadOnlyMemoryError + +""" + ErrorException(msg) + +Generic error type. The error message, in the `.msg` field, may provide more specific details. +""" +ErrorException + +""" + UndefRefError() + +The item or field is not defined for the given object. +""" +UndefRefError + +""" + Float64(x [, mode::RoundingMode]) + +Create a Float64 from `x`. If `x` is not exactly representable then `mode` determines how +`x` is rounded. + +# Examples +```jldoctest +julia> Float64(pi, RoundDown) +3.141592653589793 + +julia> Float64(pi, RoundUp) +3.1415926535897936 +``` + +See [`RoundingMode`](@ref) for available rounding modes. +""" +Float64(x) + +""" + OutOfMemoryError() + +An operation allocated too much memory for either the system or the garbage collector to +handle properly. +""" +OutOfMemoryError + +""" + BoundsError([a],[i]) + +An indexing operation into an array, `a`, tried to access an out-of-bounds element at index `i`. + +# Examples +```jldoctest +julia> A = ones(7); + +julia> A[8] +ERROR: BoundsError: attempt to access 7-element Array{Float64,1} at index [8] +Stacktrace: + [1] getindex(::Array{Float64,1}, ::Int64) at ./array.jl:586 + +julia> B = ones(2, 3); + +julia> B[2, 4] +ERROR: BoundsError: attempt to access 2×3 Array{Float64,2} at index [2, 4] +Stacktrace: + [1] getindex(::Array{Float64,2}, ::Int64, ::Int64) at ./array.jl:587 + +julia> B[9] +ERROR: BoundsError: attempt to access 2×3 Array{Float64,2} at index [9] +Stacktrace: + [1] getindex(::Array{Float64,2}, ::Int64) at ./array.jl:586 +``` +""" +BoundsError + +""" + InexactError(name::Symbol, T, val) + +Cannot exactly convert `val` to type `T` in a method of function `name`. + +# Examples +```jldoctest +julia> convert(Float64, 1+2im) +ERROR: InexactError: convert(Float64, 1 + 2im) +Stacktrace: + [1] convert(::Type{Float64}, ::Complex{Int64}) at ./complex.jl:37 +``` +""" +InexactError + +""" + DomainError(val) + DomainError(val, msg) + +The argument `val` to a function or constructor is outside the valid domain. + +# Examples +```jldoctest +julia> sqrt(-1) +ERROR: DomainError with -1.0: +sqrt will only return a complex result if called with a complex argument. Try sqrt(Complex(x)). +Stacktrace: + [1] throw_complex_domainerror(::Symbol, ::Float64) at ./math.jl:31 + [2] sqrt at ./math.jl:462 [inlined] + [3] sqrt(::Int64) at ./math.jl:472 +``` +""" +DomainError + +""" + Task(func) + +Create a `Task` (i.e. coroutine) to execute the given function (which must be +callable with no arguments). The task exits when this function returns. + +# Examples +```jldoctest +julia> a() = det(rand(1000, 1000)); + +julia> b = Task(a); +``` + +In this example, `b` is a runnable `Task` that hasn't started yet. +""" +Task + +""" + StackOverflowError() + +The function call grew beyond the size of the call stack. This usually happens when a call +recurses infinitely. +""" +StackOverflowError + +""" + nfields(x) -> Int + +Get the number of fields in the given object. +""" +nfields + +""" + UndefVarError(var::Symbol) + +A symbol in the current scope is not defined. +""" +UndefVarError + +""" + OverflowError(msg) + +The result of an expression is too large for the specified type and will cause a wraparound. +""" +OverflowError + +""" + TypeError(func::Symbol, context::AbstractString, expected::Type, got) + +A type assertion failure, or calling an intrinsic function with an incorrect argument type. +""" +TypeError + +""" + InterruptException() + +The process was stopped by a terminal interrupt (CTRL+C). +""" +InterruptException + +""" + applicable(f, args...) -> Bool + +Determine whether the given generic function has a method applicable to the given arguments. + +# Examples +```jldoctest +julia> function f(x, y) + x + y + end; + +julia> applicable(f, 1) +false + +julia> applicable(f, 1, 2) +true +``` +""" +applicable + +""" + invoke(f, argtypes::Type, args...; kwargs...) + +Invoke a method for the given generic function `f` matching the specified types `argtypes` on the +specified arguments `args` and passing the keyword arguments `kwargs`. The arguments `args` must +conform with the specified types in `argtypes`, i.e. conversion is not automatically performed. +This method allows invoking a method other than the most specific matching method, which is useful +when the behavior of a more general definition is explicitly needed (often as part of the +implementation of a more specific method of the same function). + +# Examples +```jldoctest +julia> f(x::Real) = x^2; + +julia> f(x::Integer) = 1 + invoke(f, Tuple{Real}, x); + +julia> f(2) +5 +``` +""" +invoke + +""" + isa(x, type) -> Bool + +Determine whether `x` is of the given `type`. Can also be used as an infix operator, e.g. +`x isa type`. +""" +isa + +""" + DivideError() + +Integer division was attempted with a denominator value of 0. + +# Examples +```jldoctest +julia> 2/0 +Inf + +julia> div(2, 0) +ERROR: DivideError: integer division error +Stacktrace: + [1] div(::Int64, ::Int64) at ./int.jl:183 +``` +""" +DivideError + +""" + Number + +Abstract supertype for all number types. +""" +Number + +""" + Real <: Number + +Abstract supertype for all real numbers. +""" +Real + +""" + AbstractFloat <: Real + +Abstract supertype for all floating point numbers. +""" +AbstractFloat + +""" + Integer <: Real + +Abstract supertype for all integers. +""" +Integer + +""" + Signed <: Integer + +Abstract supertype for all signed integers. +""" +Signed + +""" + Unsigned <: Integer + +Abstract supertype for all unsigned integers. +""" +Unsigned + +""" + Bool <: Integer + +Boolean type. +""" +Bool + +for bit in (16, 32, 64) + @eval begin + """ + Float$($bit) <: AbstractFloat + + $($bit)-bit floating point number type. + """ + $(Symbol("Float", bit)) + end +end + +for bit in (8, 16, 32, 64, 128) + @eval begin + """ + Int$($bit) <: Signed + + $($bit)-bit signed integer type. + """ + $(Symbol("Int", bit)) + + """ + UInt$($bit) <: Unsigned + + $($bit)-bit unsigned integer type. + """ + $(Symbol("UInt", bit)) + end +end + +""" + ArgumentError(msg) + +The parameters to a function call do not match a valid signature. Argument `msg` is a +descriptive error string. +""" +ArgumentError + +""" + MethodError(f, args) + +A method with the required type signature does not exist in the given generic function. +Alternatively, there is no unique most-specific method. +""" +MethodError + +""" + AssertionError([msg]) + +The asserted condition did not evaluate to `true`. +Optional argument `msg` is a descriptive error string. +""" +AssertionError + +""" + LoadError(file::AbstractString, line::Int, error) + +An error occurred while `include`ing, `require`ing, or `using` a file. The error specifics +should be available in the `.error` field. +""" +LoadError + +""" + InitError(mod::Symbol, error) + +An error occurred when running a module's `__init__` function. The actual error thrown is +available in the `.error` field. +""" +InitError + end diff --git a/base/docs/helpdb/Base.jl b/base/docs/helpdb/Base.jl index 616c7ca0dd268..bd96ddbae5b05 100644 --- a/base/docs/helpdb/Base.jl +++ b/base/docs/helpdb/Base.jl @@ -269,13 +269,6 @@ Stacktrace: """ sizeof(::Type) -""" - ReadOnlyMemoryError() - -An operation tried to write to memory that is read-only. -""" -ReadOnlyMemoryError - """ ceil([T,] x, [digits, [base]]) @@ -490,13 +483,6 @@ not representable. """ floor -""" - ErrorException(msg) - -Generic error type. The error message, in the `.msg` field, may provide more specific details. -""" -ErrorException - """ reverse(v [, start=1 [, stop=length(v) ]] ) @@ -546,13 +532,6 @@ In-place version of [`reverse`](@ref). """ reverse! -""" - UndefRefError() - -The item or field is not defined for the given object. -""" -UndefRefError - """ append!(collection, collection2) -> collection. @@ -675,25 +654,6 @@ julia> a """ select! -""" - Float64(x [, mode::RoundingMode]) - -Create a Float64 from `x`. If `x` is not exactly representable then `mode` determines how -`x` is rounded. - -# Examples -```jldoctest -julia> Float64(pi, RoundDown) -3.141592653589793 - -julia> Float64(pi, RoundUp) -3.1415926535897936 -``` - -See [`RoundingMode`](@ref) for available rounding modes. -""" -Float64(x) - """ union(s1,s2...) ∪(s1,s2...) @@ -865,14 +825,6 @@ Suggest that collection `s` reserve capacity for at least `n` elements. This can """ sizehint! -""" - OutOfMemoryError() - -An operation allocated too much memory for either the system or the garbage collector to -handle properly. -""" -OutOfMemoryError - """ finalize(x) @@ -880,57 +832,6 @@ Immediately run finalizers registered for object `x`. """ finalize -""" - BoundsError([a],[i]) - -An indexing operation into an array, `a`, tried to access an out-of-bounds element at index `i`. - -# Examples -```jldoctest -julia> A = ones(7); - -julia> A[8] -ERROR: BoundsError: attempt to access 7-element Array{Float64,1} at index [8] -Stacktrace: - [1] getindex(::Array{Float64,1}, ::Int64) at ./array.jl:586 - -julia> B = ones(2, 3); - -julia> B[2, 4] -ERROR: BoundsError: attempt to access 2×3 Array{Float64,2} at index [2, 4] -Stacktrace: - [1] getindex(::Array{Float64,2}, ::Int64, ::Int64) at ./array.jl:587 - -julia> B[9] -ERROR: BoundsError: attempt to access 2×3 Array{Float64,2} at index [9] -Stacktrace: - [1] getindex(::Array{Float64,2}, ::Int64) at ./array.jl:586 -``` -""" -BoundsError - -""" - invoke(f, argtypes::Type, args...; kwargs...) - -Invoke a method for the given generic function `f` matching the specified types `argtypes` on the -specified arguments `args` and passing the keyword arguments `kwargs`. The arguments `args` must -conform with the specified types in `argtypes`, i.e. conversion is not automatically performed. -This method allows invoking a method other than the most specific matching method, which is useful -when the behavior of a more general definition is explicitly needed (often as part of the -implementation of a more specific method of the same function). - -# Examples -```jldoctest -julia> f(x::Real) = x^2; - -julia> f(x::Integer) = 1 + invoke(f, Tuple{Real}, x); - -julia> f(2) -5 -``` -""" -invoke - """ parse(str, start; greedy=true, raise=true) @@ -1288,21 +1189,6 @@ false """ isempty -""" - InexactError(name::Symbol, T, val) - -Cannot exactly convert `val` to type `T` in a method of function `name`. - -# Examples -```jldoctest -julia> convert(Float64, 1+2im) -ERROR: InexactError: convert(Float64, 1 + 2im) -Stacktrace: - [1] convert(::Type{Float64}, ::Complex{Int64}) at ./complex.jl:37 -``` -""" -InexactError - """ typemax(T) @@ -1310,50 +1196,6 @@ The highest value representable by the given (real) numeric `DataType`. """ typemax -""" - DomainError(val) - DomainError(val, msg) - -The argument `val` to a function or constructor is outside the valid domain. - -# Examples -```jldoctest -julia> sqrt(-1) -ERROR: DomainError with -1.0: -sqrt will only return a complex result if called with a complex argument. Try sqrt(Complex(x)). -Stacktrace: - [1] throw_complex_domainerror(::Symbol, ::Float64) at ./math.jl:31 - [2] sqrt at ./math.jl:462 [inlined] - [3] sqrt(::Int64) at ./math.jl:472 -``` -""" -DomainError - -""" - Task(func) - -Create a `Task` (i.e. coroutine) to execute the given function (which must be -callable with no arguments). The task exits when this function returns. - -# Examples -```jldoctest -julia> a() = det(rand(1000, 1000)); - -julia> b = Task(a); -``` - -In this example, `b` is a runnable `Task` that hasn't started yet. -""" -Task - -""" - StackOverflowError() - -The function call grew beyond the size of the call stack. This usually happens when a call -recurses infinitely. -""" -StackOverflowError - """ ==(x, y) @@ -1378,13 +1220,6 @@ Seek a stream to its beginning. """ seekstart -""" - nfields(x) -> Int - -Get the number of fields in the given object. -""" -nfields - """ show(stream, mime, x) @@ -1441,13 +1276,6 @@ Equivalent to [`readdlm`](@ref) with `delim` set to comma, and type optionally d """ readcsv -""" - UndefVarError(var::Symbol) - -A symbol in the current scope is not defined. -""" -UndefVarError - """ gc() @@ -1499,13 +1327,6 @@ used only with extreme caution, as it can cause memory use to grow without bound """ gc_enable -""" - OverflowError(msg) - -The result of an expression is too large for the specified type and will cause a wraparound. -""" -OverflowError - """ object_id(x) @@ -1548,13 +1369,6 @@ unpredictable. """ finalizer -""" - TypeError(func::Symbol, context::AbstractString, expected::Type, got) - -A type assertion failure, or calling an intrinsic function with an incorrect argument type. -""" -TypeError - """ setfield!(value, name::Symbol, x) @@ -1668,13 +1482,6 @@ julia> length([1 2; 3 4]) """ length(collection) -""" - InterruptException() - -The process was stopped by a terminal interrupt (CTRL+C). -""" -InterruptException - """ issubnormal(f) -> Bool @@ -1760,14 +1567,6 @@ julia> start([4;2;3]) """ start -""" - isa(x, type) -> Bool - -Determine whether `x` is of the given `type`. Can also be used as an infix operator, e.g. -`x isa type`. -""" -isa - """ done(iter, state) -> Bool @@ -1858,26 +1657,6 @@ true """ convert -""" - applicable(f, args...) -> Bool - -Determine whether the given generic function has a method applicable to the given arguments. - -# Examples -```jldoctest -julia> function f(x, y) - x + y - end; - -julia> applicable(f, 1) -false - -julia> applicable(f, 1, 2) -true -``` -""" -applicable - """ fma(x, y, z) @@ -2045,102 +1824,6 @@ Seek a stream to its end. """ seekend -""" - DivideError() - -Integer division was attempted with a denominator value of 0. - -# Examples -```jldoctest -julia> 2/0 -Inf - -julia> div(2, 0) -ERROR: DivideError: integer division error -Stacktrace: - [1] div(::Int64, ::Int64) at ./int.jl:183 -``` -""" -DivideError - -""" - Number - -Abstract supertype for all number types. -""" -Number - -""" - Real <: Number - -Abstract supertype for all real numbers. -""" -Real - -""" - AbstractFloat <: Real - -Abstract supertype for all floating point numbers. -""" -AbstractFloat - -""" - Integer <: Real - -Abstract supertype for all integers. -""" -Integer - -""" - Signed <: Integer - -Abstract supertype for all signed integers. -""" -Signed - -""" - Unsigned <: Integer - -Abstract supertype for all unsigned integers. -""" -Unsigned - -""" - Bool <: Integer - -Boolean type. -""" -Bool - -for bit in (16, 32, 64) - @eval begin - """ - Float$($bit) <: AbstractFloat - - $($bit)-bit floating point number type. - """ - $(Symbol("Float", bit)) - end -end - -for bit in (8, 16, 32, 64, 128) - @eval begin - """ - Int$($bit) <: Signed - - $($bit)-bit signed integer type. - """ - $(Symbol("Int", bit)) - - """ - UInt$($bit) <: Unsigned - - $($bit)-bit unsigned integer type. - """ - $(Symbol("UInt", bit)) - end -end - """ Vector{T}(n) diff --git a/base/error.jl b/base/error.jl index 60f37eb6c1467..06508810e00b0 100644 --- a/base/error.jl +++ b/base/error.jl @@ -85,7 +85,7 @@ systemerror(p, b::Bool; extrainfo=nothing) = b ? throw(Main.Base.SystemError(str Throw an [`AssertionError`](@ref) if `cond` is `false`. Also available as the macro [`@assert`](@ref). """ -assert(x) = x ? nothing : throw(Main.Base.AssertionError()) +assert(x) = x ? nothing : throw(AssertionError()) """ @assert cond [text] @@ -114,7 +114,7 @@ macro assert(ex, msgs...) # string() might not be defined during bootstrap msg = :(Main.Base.string($(Expr(:quote,msg)))) end - return :($(esc(ex)) ? $(nothing) : throw(Main.Base.AssertionError($msg))) + return :($(esc(ex)) ? $(nothing) : throw(AssertionError($msg))) end struct ExponentialBackOff diff --git a/base/exports.jl b/base/exports.jl index eecd75f36d46e..8a086f914ca3a 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -151,22 +151,15 @@ export Cwstring, # Exceptions - ArgumentError, DimensionMismatch, CapturedException, CompositeException, EOFError, - ErrorException, InvalidStateException, KeyError, - LoadError, - InitError, - MethodError, NullException, ParseError, SystemError, - TypeError, - AssertionError, UnicodeError, # Global constants and variables diff --git a/base/gcutils.jl b/base/gcutils.jl new file mode 100644 index 0000000000000..3d8f04362ba57 --- /dev/null +++ b/base/gcutils.jl @@ -0,0 +1,27 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +==(w::WeakRef, v::WeakRef) = isequal(w.value, v.value) +==(w::WeakRef, v) = isequal(w.value, v) +==(w, v::WeakRef) = isequal(w, v.value) + +function finalizer(@nospecialize(o), @nospecialize(f)) + if isimmutable(o) + error("objects of type ", typeof(o), " cannot be finalized") + end + ccall(:jl_gc_add_finalizer_th, Void, (Ptr{Void}, Any, Any), + Core.getptls(), o, f) +end +function finalizer(o::T, f::Ptr{Void}) where T + @_inline_meta + if isimmutable(T) + error("objects of type ", T, " cannot be finalized") + end + ccall(:jl_gc_add_ptr_finalizer, Void, (Ptr{Void}, Any, Ptr{Void}), + Core.getptls(), o, f) +end + +finalize(@nospecialize(o)) = ccall(:jl_finalize_th, Void, (Ptr{Void}, Any,), + Core.getptls(), o) + +gc(full::Bool=true) = ccall(:jl_gc_collect, Void, (Int32,), full) +gc_enable(on::Bool) = ccall(:jl_gc_enable, Int32, (Int32,), on) != 0 diff --git a/base/io.jl b/base/io.jl index 5959e94434b64..b08f62e5df4c1 100644 --- a/base/io.jl +++ b/base/io.jl @@ -2,6 +2,27 @@ # Generic IO stubs -- all subtypes should implement these (if meaningful) +""" + EOFError() + +No more data was available to read from a file or stream. +""" +mutable struct EOFError <: Exception end + +""" + SystemError(prefix::AbstractString, [errno::Int32]) + +A system call failed with an error code (in the `errno` global variable). +""" +mutable struct SystemError <: Exception + prefix::AbstractString + errnum::Int32 + extrainfo + SystemError(p::AbstractString, e::Integer, extrainfo) = new(p, e, extrainfo) + SystemError(p::AbstractString, e::Integer) = new(p, e, nothing) + SystemError(p::AbstractString) = new(p, Libc.errno()) +end + lock(::IO) = nothing unlock(::IO) = nothing reseteof(x::IO) = nothing diff --git a/base/nullabletype.jl b/base/nullabletype.jl new file mode 100644 index 0000000000000..8c1c9c3d1d91c --- /dev/null +++ b/base/nullabletype.jl @@ -0,0 +1,9 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +struct Nullable{T} + hasvalue::Bool + value::T + + Nullable{T}() where {T} = new(false) + Nullable{T}(value::T, hasvalue::Bool=true) where {T} = new(hasvalue, value) +end diff --git a/base/parse.jl b/base/parse.jl index 87b7c6aed6c92..1ef3f91278cb6 100644 --- a/base/parse.jl +++ b/base/parse.jl @@ -217,6 +217,16 @@ float(a::AbstractArray{<:AbstractString}) = map!(float, similar(a,typeof(float(0 ## interface to parser ## +""" + ParseError(msg) + +The expression passed to the `parse` function could not be interpreted as a valid Julia +expression. +""" +mutable struct ParseError <: Exception + msg::AbstractString +end + function parse(str::AbstractString, pos::Int; greedy::Bool=true, raise::Bool=true) # pos is one based byte offset. # returns (expr, end_pos). expr is () in case of parse error. diff --git a/base/sysimg.jl b/base/sysimg.jl index a30ec87bf7f5e..dec8dc77b5586 100644 --- a/base/sysimg.jl +++ b/base/sysimg.jl @@ -51,7 +51,7 @@ convert(::Type{T}, arg) where {T<:VecElement} = T(arg) convert(::Type{T}, arg::T) where {T<:VecElement} = arg # init core docsystem -import Core: @doc, @__doc__, @doc_str +import Core: @doc, @__doc__, @doc_str, WrappedException if isdefined(Core, :Inference) import Core.Inference.CoreDocs Core.atdoc!(CoreDocs.docm) @@ -72,7 +72,8 @@ end ## Load essential files and libraries include("essentials.jl") include("ctypes.jl") -include("base.jl") +include("gcutils.jl") +include("nullabletype.jl") include("generator.jl") include("reflection.jl") include("options.jl") diff --git a/src/abi_x86.cpp b/src/abi_x86.cpp index 12870c00d816d..7a65de028e083 100644 --- a/src/abi_x86.cpp +++ b/src/abi_x86.cpp @@ -39,18 +39,22 @@ struct ABI_x86Layout : AbiLayout { +STATIC_INLINE bool is_complex_type(jl_datatype_t *dt) +{ + static jl_sym_t *Complex_sym = NULL; + if (Complex_sym == NULL) + Complex_sym = jl_symbol("Complex"); + return jl_is_datatype(dt) && dt->name->name == Complex_sym && dt->name->module == jl_base_module; +} + inline bool is_complex64(jl_datatype_t *dt) const { - return jl_complex_type != NULL && jl_is_datatype(dt) && - ((jl_datatype_t*)dt)->name == ((jl_datatype_t*)jl_unwrap_unionall((jl_value_t*)jl_complex_type))->name && - jl_tparam0(dt) == (jl_value_t*)jl_float32_type; + return is_complex_type(dt) && jl_tparam0(dt) == (jl_value_t*)jl_float32_type; } inline bool is_complex128(jl_datatype_t *dt) const { - return jl_complex_type != NULL && jl_is_datatype(dt) && - ((jl_datatype_t*)dt)->name == ((jl_datatype_t*)jl_unwrap_unionall((jl_value_t*)jl_complex_type))->name && - jl_tparam0(dt) == (jl_value_t*)jl_float64_type; + return is_complex_type(dt) && jl_tparam0(dt) == (jl_value_t*)jl_float64_type; } bool use_sret(jl_datatype_t *dt) override diff --git a/src/datatype.c b/src/datatype.c index 72f21e2beaf2c..c6bc2e61557ad 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -484,6 +484,15 @@ JL_DLLEXPORT jl_value_t *jl_new_bits(jl_value_t *bt, void *data) return jl_new_bits_internal(bt, data, &len); } +// used by boot.jl +JL_DLLEXPORT jl_value_t *jl_typemax_uint(jl_value_t *bt) +{ + uint64_t data = 0xffffffffffffffffULL; + jl_value_t *v = jl_gc_alloc(jl_get_ptls_states(), sizeof(size_t), bt); + memcpy(jl_data_ptr(v), &data, sizeof(size_t)); + return v; +} + void jl_assign_bits(void *dest, jl_value_t *bits) { size_t nb = jl_datatype_size(jl_typeof(bits)); diff --git a/src/init.c b/src/init.c index 9d9ef5273e1bc..d708b89e158bb 100644 --- a/src/init.c +++ b/src/init.c @@ -744,11 +744,6 @@ static jl_value_t *core(const char *name) return jl_get_global(jl_core_module, jl_symbol(name)); } -static jl_value_t *basemod(const char *name) -{ - return jl_get_global(jl_base_module, jl_symbol(name)); -} - // fetch references to things defined in boot.jl void jl_get_builtin_hooks(void) { @@ -799,17 +794,11 @@ void jl_get_builtin_hooks(void) jl_weakref_type = (jl_datatype_t*)core("WeakRef"); jl_vecelement_typename = ((jl_datatype_t*)jl_unwrap_unionall(core("VecElement")))->name; -} - -JL_DLLEXPORT void jl_get_system_hooks(void) -{ - if (jl_argumenterror_type) return; // only do this once - jl_argumenterror_type = (jl_datatype_t*)basemod("ArgumentError"); - jl_methoderror_type = (jl_datatype_t*)basemod("MethodError"); - jl_loaderror_type = (jl_datatype_t*)basemod("LoadError"); - jl_initerror_type = (jl_datatype_t*)basemod("InitError"); - jl_complex_type = (jl_unionall_t*)basemod("Complex"); + jl_argumenterror_type = (jl_datatype_t*)core("ArgumentError"); + jl_methoderror_type = (jl_datatype_t*)core("MethodError"); + jl_loaderror_type = (jl_datatype_t*)core("LoadError"); + jl_initerror_type = (jl_datatype_t*)core("InitError"); } void jl_get_builtins(void) diff --git a/src/jltypes.c b/src/jltypes.c index d50ad990ae9d8..78c294740e3e4 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -65,7 +65,6 @@ jl_datatype_t *jl_float32_type; jl_datatype_t *jl_float64_type; jl_datatype_t *jl_floatingpoint_type; jl_datatype_t *jl_number_type; -jl_unionall_t *jl_complex_type; jl_datatype_t *jl_signed_type; JL_DLLEXPORT jl_value_t *jl_emptytuple=NULL; @@ -1832,14 +1831,10 @@ void jl_init_types(void) jl_emptytuple_type->instance = jl_emptytuple; // non-primitive definitions follow - jl_int32_type = NULL; jl_int32_type = jl_new_primitivetype((jl_value_t*)jl_symbol("Int32"), core, jl_any_type, jl_emptysvec, 32); - jl_int64_type = NULL; jl_int64_type = jl_new_primitivetype((jl_value_t*)jl_symbol("Int64"), core, jl_any_type, jl_emptysvec, 64); - - jl_uint8_type = NULL; jl_uint8_type = jl_new_primitivetype((jl_value_t*)jl_symbol("UInt8"), core, jl_any_type, jl_emptysvec, 8); diff --git a/src/julia.h b/src/julia.h index 6a0768575e597..c35e0b2273719 100644 --- a/src/julia.h +++ b/src/julia.h @@ -550,7 +550,6 @@ extern JL_DLLEXPORT jl_datatype_t *jl_float64_type; extern JL_DLLEXPORT jl_datatype_t *jl_floatingpoint_type; extern JL_DLLEXPORT jl_datatype_t *jl_number_type; extern JL_DLLEXPORT jl_datatype_t *jl_void_type; -extern JL_DLLEXPORT jl_unionall_t *jl_complex_type; extern JL_DLLEXPORT jl_datatype_t *jl_signed_type; extern JL_DLLEXPORT jl_datatype_t *jl_voidpointer_type; extern JL_DLLEXPORT jl_unionall_t *jl_pointer_type; diff --git a/src/staticdata.c b/src/staticdata.c index 2a2b00c94cd73..499378aa62404 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -1340,7 +1340,6 @@ JL_DLLEXPORT void jl_save_system_image(const char *fname) extern int jl_boot_file_loaded; extern void jl_get_builtins(void); extern void jl_get_builtin_hooks(void); -extern void jl_get_system_hooks(void); extern void jl_gc_set_permalloc_region(void *start, void *end); // Takes in a path of the form "usr/lib/julia/sys.so" (jl_restore_system_image should be passed the same string) @@ -1496,9 +1495,6 @@ static void jl_restore_system_image_from_stream(ios_t *f) jl_get_builtins(); jl_get_builtin_hooks(); - if (jl_base_module) { - jl_get_system_hooks(); - } jl_boot_file_loaded = 1; jl_init_box_caches(); diff --git a/src/toplevel.c b/src/toplevel.c index d113857aff4b0..c53b07f402fcf 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -120,7 +120,6 @@ static void jl_module_load_time_initialize(jl_module_t *m) } } -extern void jl_get_system_hooks(void); jl_value_t *jl_eval_module_expr(jl_module_t *parent_module, jl_expr_t *ex) { jl_ptls_t ptls = jl_get_ptls_states(); From 0df3f7dc0280525349ae23e3ac4fc96621befc50 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Tue, 15 Aug 2017 12:49:47 -0400 Subject: [PATCH 036/324] deprecate `const` on local variables, which used to be ignored. (#23259) part of #5148 --- NEWS.md | 3 +++ base/complex.jl | 14 +++++++------- base/deprecated.jl | 3 +++ base/libc.jl | 10 +++++----- base/libdl.jl | 8 ++++---- base/loading.jl | 2 +- base/math.jl | 6 +++--- base/regex.jl | 8 ++++---- base/repl/REPL.jl | 2 +- base/repl/REPLCompletions.jl | 15 ++++++++------- base/sparse/sparsematrix.jl | 12 ++++++------ doc/src/manual/variables-and-scoping.md | 3 ++- src/julia-syntax.scm | 16 ++++++++++++---- test/TestHelpers.jl | 4 ++-- test/core.jl | 13 +++++++------ test/dates/io.jl | 2 +- test/misc.jl | 2 +- test/perf/shootout/binary_trees.jl | 6 +++--- test/perf/threads/lbm3d/lbm3d.jl | 10 +++++----- test/perf/threads/stockcorr/pstockcorr.jl | 16 ++++++++-------- test/pkg.jl | 4 ++-- test/repl.jl | 2 +- test/replcompletions.jl | 2 +- test/subtype.jl | 4 ++-- 24 files changed, 92 insertions(+), 75 deletions(-) diff --git a/NEWS.md b/NEWS.md index 02a4cb0beb31b..4f001ae743fec 100644 --- a/NEWS.md +++ b/NEWS.md @@ -63,6 +63,9 @@ Language changes (`need_to_handle_undef_sparam = Set{Any}(m.sig for m in Test.detect_unbound_args(Base, recursive=true))`) is equal (`==`) to some known set (`expected = Set()`). ([#23117]) + * `const` declarations on local variables were previously ignored. They now give a + warning, so that this syntax can be disallowed or given a new meaning in a + future version ([#5148]). Breaking changes ---------------- diff --git a/base/complex.jl b/base/complex.jl index 64b80ae149076..0664f9679c248 100644 --- a/base/complex.jl +++ b/base/complex.jl @@ -499,9 +499,9 @@ julia> rad2deg(angle(-1 - im)) angle(z::Complex) = atan2(imag(z), real(z)) function log(z::Complex{T}) where T<:AbstractFloat - const T1::T = 1.25 - const T2::T = 3 - const ln2::T = log(convert(T,2)) #0.6931471805599453 + T1::T = 1.25 + T2::T = 3 + ln2::T = log(convert(T,2)) #0.6931471805599453 x, y = reim(z) ρ, k = ssqs(x,y) ax = abs(x) @@ -835,7 +835,7 @@ function cosh(z::Complex) end function tanh(z::Complex{T}) where T<:AbstractFloat - const Ω = prevfloat(typemax(T)) + Ω = prevfloat(typemax(T)) ξ, η = reim(z) if isnan(ξ) && η==0 return Complex(ξ, η) end if 4*abs(ξ) > asinh(Ω) #Overflow? @@ -880,9 +880,9 @@ function acosh(z::Complex) end function atanh(z::Complex{T}) where T<:AbstractFloat - const Ω = prevfloat(typemax(T)) - const θ = sqrt(Ω)/4 - const ρ = 1/θ + Ω = prevfloat(typemax(T)) + θ = sqrt(Ω)/4 + ρ = 1/θ x, y = reim(z) ax = abs(x) ay = abs(y) diff --git a/base/deprecated.jl b/base/deprecated.jl index a9a5c93e269e5..aff647f193df4 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1684,6 +1684,9 @@ end # PR #22742: change in isapprox semantics @deprecate rtoldefault(x,y) rtoldefault(x,y,0) false +# issue #5148, PR #23259 +# warning for `const` on locals should be changed to an error in julia-syntax.scm + # END 0.7 deprecations # BEGIN 1.0 deprecations diff --git a/base/libc.jl b/base/libc.jl index fe3f46d446f9c..98f254416166d 100644 --- a/base/libc.jl +++ b/base/libc.jl @@ -39,7 +39,7 @@ if Sys.iswindows() function dup(src::WindowsRawSocket) new_handle = Ref{Ptr{Void}}(-1) my_process = ccall(:GetCurrentProcess, stdcall, Ptr{Void}, ()) - const DUPLICATE_SAME_ACCESS = 0x2 + DUPLICATE_SAME_ACCESS = 0x2 status = ccall(:DuplicateHandle, stdcall, Int32, (Ptr{Void}, Ptr{Void}, Ptr{Void}, Ptr{Ptr{Void}}, UInt32, Int32, UInt32), my_process, src.handle, my_process, new_handle, 0, false, DUPLICATE_SAME_ACCESS) @@ -290,10 +290,10 @@ if Sys.iswindows() GetLastError() = ccall(:GetLastError, stdcall, UInt32, ()) function FormatMessage(e=GetLastError()) - const FORMAT_MESSAGE_ALLOCATE_BUFFER = UInt32(0x100) - const FORMAT_MESSAGE_FROM_SYSTEM = UInt32(0x1000) - const FORMAT_MESSAGE_IGNORE_INSERTS = UInt32(0x200) - const FORMAT_MESSAGE_MAX_WIDTH_MASK = UInt32(0xFF) + FORMAT_MESSAGE_ALLOCATE_BUFFER = UInt32(0x100) + FORMAT_MESSAGE_FROM_SYSTEM = UInt32(0x1000) + FORMAT_MESSAGE_IGNORE_INSERTS = UInt32(0x200) + FORMAT_MESSAGE_MAX_WIDTH_MASK = UInt32(0xFF) lpMsgBuf = Ref{Ptr{UInt16}}() lpMsgBuf[] = 0 len = ccall(:FormatMessageW, stdcall, UInt32, (Cint, Ptr{Void}, Cint, Cint, Ptr{Ptr{UInt16}}, Cint, Ptr{Void}), diff --git a/base/libdl.jl b/base/libdl.jl index 9c2b8255f7788..cd08d66cf2f78 100644 --- a/base/libdl.jl +++ b/base/libdl.jl @@ -234,8 +234,8 @@ function dllist() dynamic_libraries = Vector{AbstractString}(0) @static if Sys.islinux() - const callback = cfunction(dl_phdr_info_callback, Cint, - Tuple{Ref{dl_phdr_info}, Csize_t, Ref{Vector{AbstractString}}}) + callback = cfunction(dl_phdr_info_callback, Cint, + Tuple{Ref{dl_phdr_info}, Csize_t, Ref{Vector{AbstractString}}}) ccall(:dl_iterate_phdr, Cint, (Ptr{Void}, Ref{Vector{AbstractString}}), callback, dynamic_libraries) end @@ -254,8 +254,8 @@ function dllist() end @static if Sys.isbsd() && !Sys.isapple() - const callback = cfunction(dl_phdr_info_callback, Cint, - Tuple{Ref{dl_phdr_info}, Csize_t, Ref{Vector{AbstractString}}}) + callback = cfunction(dl_phdr_info_callback, Cint, + Tuple{Ref{dl_phdr_info}, Csize_t, Ref{Vector{AbstractString}}}) ccall(:dl_iterate_phdr, Cint, (Ptr{Void}, Ref{Vector{AbstractString}}), callback, dynamic_libraries) shift!(dynamic_libraries) end diff --git a/base/loading.jl b/base/loading.jl index 12c0ba917c728..c3be02d771fb7 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -46,7 +46,7 @@ elseif Sys.isapple() isfile(path) || return false path_basename = String(basename(path)) local casepreserved_basename - const header_size = 12 + header_size = 12 buf = Vector{UInt8}(length(path_basename) + header_size + 1) while true ret = ccall(:getattrlist, Cint, diff --git a/base/math.jl b/base/math.jl index 07e17d69d57c1..64f957d7a1467 100644 --- a/base/math.jl +++ b/base/math.jl @@ -758,9 +758,9 @@ end @inline literal_pow(::typeof(^), x::Float16, ::Val{p}) where {p} = Float16(literal_pow(^,Float32(x),Val(p))) function angle_restrict_symm(theta) - const P1 = 4 * 7.8539812564849853515625e-01 - const P2 = 4 * 3.7748947079307981766760e-08 - const P3 = 4 * 2.6951514290790594840552e-15 + P1 = 4 * 7.8539812564849853515625e-01 + P2 = 4 * 3.7748947079307981766760e-08 + P3 = 4 * 2.6951514290790594840552e-15 y = 2*floor(theta/(2*pi)) r = ((theta - y*P1) - y*P2) - y*P3 diff --git a/base/regex.jl b/base/regex.jl index 75e8dcc70e821..fec921d0c810d 100644 --- a/base/regex.jl +++ b/base/regex.jl @@ -251,10 +251,10 @@ function _write_capture(io, re, group) end function _replace(io, repl_s::SubstitutionString, str, r, re) - const SUB_CHAR = '\\' - const GROUP_CHAR = 'g' - const LBRACKET = '<' - const RBRACKET = '>' + SUB_CHAR = '\\' + GROUP_CHAR = 'g' + LBRACKET = '<' + RBRACKET = '>' repl = repl_s.string i = start(repl) e = endof(repl) diff --git a/base/repl/REPL.jl b/base/repl/REPL.jl index 4309a83efb7e1..d08e3fca01e3f 100644 --- a/base/repl/REPL.jl +++ b/base/repl/REPL.jl @@ -807,7 +807,7 @@ function setup_interface( extra_repl_keymap = [extra_repl_keymap] end - const repl_keymap = AnyDict( + repl_keymap = AnyDict( ';' => function (s,o...) if isempty(s) || position(LineEdit.buffer(s)) == 0 buf = copy(LineEdit.buffer(s)) diff --git a/base/repl/REPLCompletions.jl b/base/repl/REPLCompletions.jl index 32abb00134f56..e680425e94469 100644 --- a/base/repl/REPLCompletions.jl +++ b/base/repl/REPLCompletions.jl @@ -82,14 +82,15 @@ function complete_symbol(sym, ffunc) suggestions end +const sorted_keywords = [ + "abstract type", "baremodule", "begin", "break", "catch", "ccall", + "const", "continue", "do", "else", "elseif", "end", "export", "false", + "finally", "for", "function", "global", "if", "import", + "importall", "let", "local", "macro", "module", "mutable struct", + "primitive type", "quote", "return", "struct", + "true", "try", "using", "while"] + function complete_keyword(s::String) - const sorted_keywords = [ - "abstract type", "baremodule", "begin", "break", "catch", "ccall", - "const", "continue", "do", "else", "elseif", "end", "export", "false", - "finally", "for", "function", "global", "if", "import", - "importall", "let", "local", "macro", "module", "mutable struct", - "primitive type", "quote", "return", "struct", - "true", "try", "using", "while"] r = searchsorted(sorted_keywords, s) i = first(r) n = length(sorted_keywords) diff --git a/base/sparse/sparsematrix.jl b/base/sparse/sparsematrix.jl index 11a0a434edc7e..212565d4cddcc 100644 --- a/base/sparse/sparsematrix.jl +++ b/base/sparse/sparsematrix.jl @@ -2042,8 +2042,8 @@ function getindex_I_sorted(A::SparseMatrixCSC{Tv,Ti}, I::AbstractVector, J::Abst end function getindex_I_sorted_bsearch_A(A::SparseMatrixCSC{Tv,Ti}, I::AbstractVector, J::AbstractVector) where {Tv,Ti} - const nI = length(I) - const nJ = length(J) + nI = length(I) + nJ = length(J) colptrA = A.colptr; rowvalA = A.rowval; nzvalA = A.nzval colptrS = Vector{Ti}(nJ+1) @@ -2101,8 +2101,8 @@ function getindex_I_sorted_bsearch_A(A::SparseMatrixCSC{Tv,Ti}, I::AbstractVecto end function getindex_I_sorted_linear(A::SparseMatrixCSC{Tv,Ti}, I::AbstractVector, J::AbstractVector) where {Tv,Ti} - const nI = length(I) - const nJ = length(J) + nI = length(I) + nJ = length(J) colptrA = A.colptr; rowvalA = A.rowval; nzvalA = A.nzval colptrS = Vector{Ti}(nJ+1) @@ -2160,8 +2160,8 @@ function getindex_I_sorted_linear(A::SparseMatrixCSC{Tv,Ti}, I::AbstractVector, end function getindex_I_sorted_bsearch_I(A::SparseMatrixCSC{Tv,Ti}, I::AbstractVector, J::AbstractVector) where {Tv,Ti} - const nI = length(I) - const nJ = length(J) + nI = length(I) + nJ = length(J) colptrA = A.colptr; rowvalA = A.rowval; nzvalA = A.nzval colptrS = Vector{Ti}(nJ+1) diff --git a/doc/src/manual/variables-and-scoping.md b/doc/src/manual/variables-and-scoping.md index f1df11b7ed5a3..22b1f2c8e7551 100644 --- a/doc/src/manual/variables-and-scoping.md +++ b/doc/src/manual/variables-and-scoping.md @@ -475,7 +475,8 @@ their values (or even their types) might change at almost any time. If a global not change, adding a `const` declaration solves this performance problem. Local constants are quite different. The compiler is able to determine automatically when a local -variable is constant, so local constant declarations are not necessary, and are currently just ignored. +variable is constant, so local constant declarations are not necessary, and in fact are currently +not supported. Special top-level assignments, such as those performed by the `function` and `struct` keywords, are constant by default. diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index e6481ffdefe2d..2e1d2d7dcc8fc 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -1320,7 +1320,7 @@ (if (null? params) (error (string "empty type parameter list in \"" (deparse `(= (curly ,name) ,type-ex)) "\""))) `(block - (const ,name) + (const-if-global ,name) ,(expand-forms `(= ,name (where ,type-ex ,@params))))) (expand-forms @@ -3115,11 +3115,12 @@ f(x) = yt(x) (if (vinfo:never-undef vi) '(null) `(newvar ,(cadr e)))))) - ((const) + ((const) e) + ((const-if-global) (if (or (assq (cadr e) (car (lam:vinfo lam))) (assq (cadr e) (cadr (lam:vinfo lam)))) '(null) - e)) + `(const ,(cadr e)))) ((isdefined) ;; convert isdefined expr to function for closure converted variables (let* ((sym (cadr e)) (vi (and (symbol? sym) (assq sym (car (lam:vinfo lam))))) @@ -3661,7 +3662,14 @@ f(x) = yt(x) ((local-def) #f) ((local) #f) ((implicit-global) #f) - ((const) (emit e)) + ((const) + (if (or (assq (cadr e) (car (lam:vinfo lam))) + (assq (cadr e) (cadr (lam:vinfo lam)))) + (begin + (syntax-deprecation #f (string "`const` declaration on local variable" (linenode-string current-loc)) + "") + '(null)) + (emit e))) ((isdefined) (if tail (emit-return e) e)) ;; top level expressions returning values diff --git a/test/TestHelpers.jl b/test/TestHelpers.jl index 4f99773aa2a7a..0ee2501159c98 100644 --- a/test/TestHelpers.jl +++ b/test/TestHelpers.jl @@ -24,8 +24,8 @@ function open_fake_pty() error("Unable to create a fake PTY in Windows") end - const O_RDWR = Base.Filesystem.JL_O_RDWR - const O_NOCTTY = Base.Filesystem.JL_O_NOCTTY + O_RDWR = Base.Filesystem.JL_O_RDWR + O_NOCTTY = Base.Filesystem.JL_O_NOCTTY fdm = ccall(:posix_openpt, Cint, (Cint,), O_RDWR|O_NOCTTY) fdm == -1 && error("Failed to open PTY master") diff --git a/test/core.jl b/test/core.jl index 1faaba8622958..81704008c6b7f 100644 --- a/test/core.jl +++ b/test/core.jl @@ -438,7 +438,8 @@ function const_implies_local() x = 1 local y let - const x = 0 + # TODO: change back to `const` if that's ever allowed + local x = 0 y = x end x, y @@ -2055,11 +2056,11 @@ end # issue #8798 let - const npy_typestrs = Dict("b1"=>Bool, - "i1"=>Int8, "u1"=>UInt8, - "i2"=>Int16, "u2"=>UInt16, - "i4"=>Int32, "u4"=>UInt32, - "i8"=>Int64, "u8"=>UInt64) + npy_typestrs = Dict("b1"=>Bool, + "i1"=>Int8, "u1"=>UInt8, + "i2"=>Int16, "u2"=>UInt16, + "i4"=>Int32, "u4"=>UInt32, + "i8"=>Int64, "u8"=>UInt64) sizeof_lookup() = sizeof(npy_typestrs["i8"]) @test sizeof_lookup() == 8 end diff --git a/test/dates/io.jl b/test/dates/io.jl index c7523277e4535..55e01c6ab3a61 100644 --- a/test/dates/io.jl +++ b/test/dates/io.jl @@ -378,7 +378,7 @@ end # Issue: https://github.com/quinnj/TimeZones.jl/issues/19 let - const Zulu = String + Zulu = String function Dates.tryparsenext(d::Dates.DatePart{'Z'}, str, i, len) Dates.tryparsenext_word(str, i, len, Dates.min_width(d), Dates.max_width(d)) diff --git a/test/misc.jl b/test/misc.jl index 094e8dc0c9328..840bb5ce56e00 100644 --- a/test/misc.jl +++ b/test/misc.jl @@ -576,7 +576,7 @@ if Sys.iswindows() let addr = cfunction(WeVirtualProtectThisToRWX, UInt64, (UInt64, UInt64)) addr = addr-(UInt64(addr)%4096) - const PAGE_EXECUTE_READWRITE = 0x40 + PAGE_EXECUTE_READWRITE = 0x40 oldPerm = Ref{UInt32}() err18083 = ccall(:VirtualProtect,stdcall,Cint, (Ptr{Void}, Csize_t, UInt32, Ptr{UInt32}), diff --git a/test/perf/shootout/binary_trees.jl b/test/perf/shootout/binary_trees.jl index 042fd81568514..f81469ea00a3f 100644 --- a/test/perf/shootout/binary_trees.jl +++ b/test/perf/shootout/binary_trees.jl @@ -44,9 +44,9 @@ function loop_depths(d, min_depth, max_depth) end function binary_trees(N::Int=10) - const min_depth = 4 - const max_depth = N - const stretch_depth = max_depth + 1 + min_depth = 4 + max_depth = N + stretch_depth = max_depth + 1 # create and check stretch tree let c = check(make(0, stretch_depth)) diff --git a/test/perf/threads/lbm3d/lbm3d.jl b/test/perf/threads/lbm3d/lbm3d.jl index 14df2cea3c46f..11fcee4c37c92 100644 --- a/test/perf/threads/lbm3d/lbm3d.jl +++ b/test/perf/threads/lbm3d/lbm3d.jl @@ -119,11 +119,11 @@ end precompile(calc_equi!, (Array{Float64,4}, Array{Float64,4}, Array{Float64,3}, Array{Float64,3}, Array{Float64,3}, Array{Float64,2}, Array{Float64,3}, Array{Float64,3}, Array{Float64,3}, Array{Float64,3}, Int64, Int64, Int64, Float64)) function lbm3d(n) - const nx = n - const ny = nx - const nz = nx - const omega = 1.0 - const density = 1.0 + nx = n + ny = nx + nz = nx + omega = 1.0 + density = 1.0 # Implementation note: setting nchunk to nthreads() is a hack # to simulate the previous implementation's use of parallel regions. diff --git a/test/perf/threads/stockcorr/pstockcorr.jl b/test/perf/threads/stockcorr/pstockcorr.jl index 0b00c158f82e8..33e055dc5006e 100644 --- a/test/perf/threads/stockcorr/pstockcorr.jl +++ b/test/perf/threads/stockcorr/pstockcorr.jl @@ -61,15 +61,15 @@ end # Threaded version function pstockcorr(n) ## Correlated asset information - const CurrentPrice = [78. 102.] # Initial Prices of the two stocks - const Corr = [1. 0.4; 0.4 1.] # Correlation Matrix - const T = 500 # Number of days to simulate = 2years = 500days - const dt = 1/250 # Time step (1year = 250days) - const Div=[0.01 0.01] # Dividend - const Vol=[0.2 0.3] # Volatility + CurrentPrice = [78. 102.] # Initial Prices of the two stocks + Corr = [1. 0.4; 0.4 1.] # Correlation Matrix + T = 500 # Number of days to simulate = 2years = 500days + dt = 1/250 # Time step (1year = 250days) + Div=[0.01 0.01] # Dividend + Vol=[0.2 0.3] # Volatility ## Market Information - const r = 0.03 # Risk-free rate + r = 0.03 # Risk-free rate ## Define storages SimulPriceA = zeros(T,n) # Simulated Price of Asset A @@ -78,7 +78,7 @@ function pstockcorr(n) SimulPriceB[1,:] = CurrentPrice[2] ## Generating the paths of stock prices by Geometric Brownian Motion - const UpperTriangle = full(chol(Corr)) # UpperTriangle Matrix by Cholesky decomposition + UpperTriangle = full(chol(Corr)) # UpperTriangle Matrix by Cholesky decomposition # Optimization: pre-allocate these for performance # NOTE: the new GC will hopefully fix this, but currently GC time diff --git a/test/pkg.jl b/test/pkg.jl index 937ec7bba2c72..3df2f55032942 100644 --- a/test/pkg.jl +++ b/test/pkg.jl @@ -480,7 +480,7 @@ temp_pkg_dir() do nothingtodomsg) Pkg.update("Example") metadata_dir = Pkg.dir("METADATA") - const old_commit = "313bfaafa301e82d40574a778720e893c559a7e2" + old_commit = "313bfaafa301e82d40574a778720e893c559a7e2" # Force a METADATA rollback to an old version, so that we will install some # outdated versions of some packages and then update some of those @@ -525,7 +525,7 @@ temp_pkg_dir() do Pkg.rm(package) # Remove package if installed metadata_dir = Pkg.dir("METADATA") - const old_commit = "83ff7116e51fc9cdbd7e67affbd344b9f5c9dbf2" + old_commit = "83ff7116e51fc9cdbd7e67affbd344b9f5c9dbf2" # Reset METADATA to the second to last update of Example.jl LibGit2.with(LibGit2.GitRepo, metadata_dir) do repo diff --git a/test/repl.jl b/test/repl.jl index 3c3fc9a1cbf6f..727bb33d4770c 100644 --- a/test/repl.jl +++ b/test/repl.jl @@ -232,7 +232,7 @@ function AddCustomMode(repl, prompt) hp.mode_mapping[:foobar] = foobar_mode foobar_mode.hist = hp - const foobar_keymap = Dict{Any,Any}( + foobar_keymap = Dict{Any,Any}( '<' => function (s,args...) if isempty(s) if !haskey(s.mode_state,foobar_mode) diff --git a/test/replcompletions.jl b/test/replcompletions.jl index 20c0a0d1d89aa..1c22d90c4efe1 100644 --- a/test/replcompletions.jl +++ b/test/replcompletions.jl @@ -76,7 +76,7 @@ function temp_pkg_dir_noinit(fn::Function) # Used in tests below to set up and tear down a sandboxed package directory # Unlike the version in test/pkg.jl, this does not run Pkg.init so does not # clone METADATA (only pkg and libgit2-online tests should need internet access) - const tmpdir = joinpath(tempdir(),randstring()) + tmpdir = joinpath(tempdir(),randstring()) withenv("JULIA_PKGDIR" => tmpdir) do @test !isdir(Pkg.dir()) try diff --git a/test/subtype.jl b/test/subtype.jl index 458843661792e..e18f4d6593f29 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -1094,12 +1094,12 @@ f20103(::Type{TT20103{X,Y}},x::X,y::Y) where {X,Y} = 1 f20103(::Type{TT20103{X,X}},x::X) where {X} = 100 @test_broken typeintersect(Type{NTuple{N,E}} where E where N, Type{NTuple{N,E} where N} where E) == Union{} # use @testintersect once fixed let ints = (Int, Int32, UInt, UInt32) - const Ints = Union{ints...} + Ints = Union{ints...} vecs = [] for i = 2:4, t in ints push!(vecs, NTuple{i, t}) end - const Vecs = Union{vecs...} + Vecs = Union{vecs...} T = Type{Tuple{V, I}} where V <: Vecs where I <: Ints @testintersect(T, T, T) test(a::Type{Tuple{V, I}}) where {V <: Vecs, I <: Ints} = I From b029344ae581aaf51251183a87a2a6beedecd093 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 3 May 2017 18:11:39 -0400 Subject: [PATCH 037/324] simple pass at giving union fields an optimized layout unlike codegen, only bitstypes (!isptr) fields are permitted in the union and the offset count starts from 0 instead of 1 but otherwise the tindex counter is compatible --- src/cgutils.cpp | 78 ++++++++++++++++++++++++++++++++++--- src/codegen.cpp | 31 ++------------- src/datatype.c | 93 ++++++++++++++++++++++++++++++++++++-------- src/dump.c | 34 ++++++++++++---- src/intrinsics.cpp | 3 ++ src/jltypes.c | 16 ++++++++ src/julia_internal.h | 1 + 7 files changed, 199 insertions(+), 57 deletions(-) diff --git a/src/cgutils.cpp b/src/cgutils.cpp index c0a3b41b21118..caf0d330853a7 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -1141,6 +1141,33 @@ static Value *emit_bounds_check(jl_codectx_t &ctx, const jl_cgval_t &ainfo, jl_v // --- loading and storing --- +static Value *compute_box_tindex(Value *datatype, jl_value_t *supertype, jl_value_t *ut, jl_codectx_t *ctx) +{ + Value *tindex = ConstantInt::get(T_int8, 0); + unsigned counter = 0; + for_each_uniontype_small( + [&](unsigned idx, jl_datatype_t *jt) { + if (jl_subtype((jl_value_t*)jt, supertype)) { + Value *cmp = builder.CreateICmpEQ(literal_pointer_val((jl_value_t*)jt), datatype); + tindex = builder.CreateSelect(cmp, ConstantInt::get(T_int8, idx), tindex); + } + }, + ut, + counter); + return tindex; +} + +// get the runtime tindex value +static Value *compute_tindex_unboxed(const jl_cgval_t &val, jl_value_t *typ, jl_codectx_t *ctx) +{ + if (val.constant) + return ConstantInt::get(T_int8, get_box_tindex((jl_datatype_t*)jl_typeof(val.constant), typ)); + if (val.isboxed) + return compute_box_tindex(emit_typeof_boxed(val, ctx), val.typ, typ, ctx); + assert(val.TIndex); + return builder.CreateAnd(val.TIndex, ConstantInt::get(T_int8, 0x7f)); +} + // If given alignment is 0 and LLVM's assumed alignment for a load/store via ptr // might be stricter than the Julia alignment for jltype, return the alignment of jltype. // Otherwise return the given alignment. @@ -1374,6 +1401,9 @@ static jl_cgval_t emit_getfield_knownidx(jl_codectx_t &ctx, const jl_cgval_t &st addr = ctx.builder.CreateStructGEP(lt, ptr, idx); } } + int align = jl_field_offset(jt, idx); + align |= 16; + align &= -align; if (jl_field_isptr(jt, idx)) { bool maybe_null = idx >= (unsigned)jt->ninitialized; Instruction *Load = maybe_mark_load_dereferenceable( @@ -1385,6 +1415,29 @@ static jl_cgval_t emit_getfield_knownidx(jl_codectx_t &ctx, const jl_cgval_t &st null_pointer_check(ctx, fldv); return mark_julia_type(ctx, fldv, true, jfty, strct.gcroot || !strct.isimmutable); } + else if (jl_is_uniontype(jfty)) { + int fsz = jl_field_size(jt, idx); + Value *ptindex = builder.CreateGEP(LLVM37_param(T_int8) emit_bitcast(addr, T_pint8), ConstantInt::get(T_size, fsz - 1)); + Value *tindex = builder.CreateNUWAdd(ConstantInt::get(T_int8, 1), builder.CreateLoad(ptindex)); + bool isimmutable = strct.isimmutable; + Value *gcroot = strct.gcroot; + if (jt->mutabl) { + // move value to an immutable stack slot + Type *AT = ArrayType::get(IntegerType::get(jl_LLVMContext, 8 * align), (fsz + align - 2) / align); + AllocaInst *lv = emit_static_alloca(AT, ctx); + if (align > 1) + lv->setAlignment(align); + Value *nbytes = ConstantInt::get(T_size, fsz - 1); + builder.CreateMemCpy(lv, addr, nbytes, align); + addr = lv; + isimmutable = true; + gcroot = NULL; + } + jl_cgval_t fieldval = mark_julia_slot(addr, jfty, tindex, strct.tbaa); + fieldval.isimmutable = isimmutable; + fieldval.gcroot = gcroot; + return fieldval; + } else if (!jt->mutabl) { // just compute the pointer and let user load it when necessary jl_cgval_t fieldval = mark_julia_slot(addr, jfty, NULL, strct.tbaa); @@ -2006,7 +2059,7 @@ static void emit_unionmove(jl_codectx_t &ctx, Value *dest, const jl_cgval_t &src jl_value_t *typ = src.constant ? jl_typeof(src.constant) : src.typ; Type *store_ty = julia_type_to_llvm(typ); assert(skip || jl_isbits(typ)); - if (jl_isbits(typ)) { + if (jl_isbits(typ) && jl_datatype_size(typ) > 0) { if (!src.ispointer() || src.constant) { emit_unbox(ctx, store_ty, src, typ, dest, isVolatile); } @@ -2164,11 +2217,24 @@ static void emit_setfield(jl_codectx_t &ctx, emit_checked_write_barrier(ctx, boxed(ctx, strct), r); } else { - int align = jl_field_offset(sty, idx0); - align |= 16; - align &= -align; - typed_store(ctx, addr, ConstantInt::get(T_size, 0), rhs, jfty, - strct.tbaa, data_pointer(ctx, strct, T_pjlvalue), align); + if (jl_is_uniontype(jfty)) { + int fsz = jl_field_size(sty, idx0); + // compute tindex from rhs + jl_cgval_t rhs_union = convert_julia_type(rhs, jfty, ctx); + Value *ptindex = builder.CreateGEP(LLVM37_param(T_int8) emit_bitcast(addr, T_pint8), ConstantInt::get(T_size, fsz - 1)); + Value *tindex = compute_tindex_unboxed(rhs_union, jfty, ctx); + tindex = builder.CreateNUWSub(tindex, ConstantInt::get(T_int8, 1)); + builder.CreateStore(tindex, ptindex); + // copy data + emit_unionmove(addr, rhs, NULL, false, NULL, ctx); + } + else { + int align = jl_field_offset(sty, idx0); + align |= 16; + align &= -align; + typed_store(addr, ConstantInt::get(T_size, 0), rhs, jfty, ctx, + strct.tbaa, data_pointer(ctx, strct, T_pjlvalue), align); + } } } else { diff --git a/src/codegen.cpp b/src/codegen.cpp index 4d19a5adac3aa..e14ad61b85966 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -738,6 +738,8 @@ static inline jl_cgval_t update_julia_type(jl_codectx_t &ctx, const jl_cgval_t & return jl_cgval_t(v, typ, NULL); } +static jl_cgval_t convert_julia_type(const jl_cgval_t &v, jl_value_t *typ, jl_codectx_t *ctx, bool needsroot = true); + // --- allocating local variables --- static jl_sym_t *slot_symbol(jl_codectx_t &ctx, int s) @@ -818,7 +820,7 @@ static void jl_rethrow_with_add(const char *fmt, ...) } // given a value marked with type `v.typ`, compute the mapping and/or boxing to return a value of type `typ` -static jl_cgval_t convert_julia_type(jl_codectx_t &ctx, const jl_cgval_t &v, jl_value_t *typ, bool needsroot = true) +static jl_cgval_t convert_julia_type(jl_codectx_t &ctx, const jl_cgval_t &v, jl_value_t *typ, bool needsroot) { if (typ == (jl_value_t*)jl_typeofbottom_type) return ghostValue(typ); // normalize TypeofBottom to Type{Union{}} @@ -3309,33 +3311,6 @@ static Value *try_emit_union_alloca(jl_codectx_t &ctx, jl_uniontype_t *ut, bool return NULL; } -static Value *compute_box_tindex(jl_codectx_t &ctx, Value *datatype, jl_value_t *supertype, jl_value_t *ut) -{ - Value *tindex = ConstantInt::get(T_int8, 0); - unsigned counter = 0; - for_each_uniontype_small( - [&](unsigned idx, jl_datatype_t *jt) { - if (jl_subtype((jl_value_t*)jt, supertype)) { - Value *cmp = ctx.builder.CreateICmpEQ(maybe_decay_untracked(literal_pointer_val(ctx, (jl_value_t*)jt)), datatype); - tindex = ctx.builder.CreateSelect(cmp, ConstantInt::get(T_int8, idx), tindex); - } - }, - ut, - counter); - return tindex; -} - -// get the runtime tindex value -static Value *compute_tindex_unboxed(jl_codectx_t &ctx, const jl_cgval_t &val, jl_value_t *typ) -{ - if (val.constant) - return ConstantInt::get(T_int8, get_box_tindex((jl_datatype_t*)jl_typeof(val.constant), typ)); - if (val.isboxed) - return compute_box_tindex(ctx, emit_typeof_boxed(ctx, val), val.typ, typ); - assert(val.TIndex); - return ctx.builder.CreateAnd(val.TIndex, ConstantInt::get(T_int8, 0x7f)); -} - static void emit_assignment(jl_codectx_t &ctx, jl_value_t *l, jl_value_t *r) { if (jl_is_ssavalue(l)) { diff --git a/src/datatype.c b/src/datatype.c index c6bc2e61557ad..6abed317ebeb8 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -230,6 +230,38 @@ STATIC_INLINE void jl_allocate_singleton_instance(jl_datatype_t *st) } } +static int jl_layout_isbits(jl_value_t *ty) +{ + if (jl_isbits(ty) && jl_is_leaf_type(ty)) { + if (((jl_datatype_t*)ty)->layout) // layout check handles possible layout recursion + return 1; + } + return 0; +} + +static unsigned jl_union_isbits(jl_value_t *ty, size_t *nbytes, size_t *align) +{ + if (jl_is_uniontype(ty)) { + unsigned na = jl_union_isbits(((jl_uniontype_t*)ty)->a, nbytes, align); + if (na == 0) + return 0; + unsigned nb = jl_union_isbits(((jl_uniontype_t*)ty)->b, nbytes, align); + if (nb == 0) + return 0; + return na + nb; + } + if (jl_layout_isbits(ty)) { + size_t sz = jl_datatype_size(ty); + size_t al = ((jl_datatype_t*)ty)->layout->alignment; + if (*nbytes < sz) + *nbytes = sz; + if (*align < al) + *align = al; + return 1; + } + return 0; +} + void jl_compute_field_offsets(jl_datatype_t *st) { size_t sz = 0, alignm = 1; @@ -293,16 +325,22 @@ void jl_compute_field_offsets(jl_datatype_t *st) for (size_t i = 0; i < nfields; i++) { jl_value_t *ty = jl_field_type(st, i); - size_t fsz, al; - if (jl_isbits(ty) && jl_is_leaf_type(ty) && ((jl_datatype_t*)ty)->layout) { - fsz = jl_datatype_size(ty); + size_t fsz = 0, al = 0; + unsigned countbits = jl_union_isbits(ty, &fsz, &al); + if (countbits > 0 && countbits < 127) { // Should never happen if (__unlikely(fsz > max_size)) goto throw_ovf; al = jl_datatype_align(ty); desc[i].isptr = 0; - if (((jl_datatype_t*)ty)->layout->haspadding) + if (jl_is_uniontype(ty)) { haspadding = 1; + fsz += 1; // selector byte + } + else { // isbits struct + if (((jl_datatype_t*)ty)->layout->haspadding) + haspadding = 1; + } } else { fsz = sizeof(void*); @@ -328,7 +366,7 @@ void jl_compute_field_offsets(jl_datatype_t *st) goto throw_ovf; sz += fsz; } - if (homogeneous && lastty!=NULL && jl_is_tuple_type(st)) { + if (homogeneous && lastty != NULL && jl_is_tuple_type(st)) { // Some tuples become LLVM vectors with stronger alignment than what was calculated above. unsigned al = jl_special_vector_alignment(nfields, lastty); assert(al % alignm == 0); @@ -731,37 +769,60 @@ JL_DLLEXPORT jl_value_t *jl_get_nth_field(jl_value_t *v, size_t i) { jl_datatype_t *st = (jl_datatype_t*)jl_typeof(v); assert(i < jl_datatype_nfields(st)); - size_t offs = jl_field_offset(st,i); - if (jl_field_isptr(st,i)) { + size_t offs = jl_field_offset(st, i); + if (jl_field_isptr(st, i)) { return *(jl_value_t**)((char*)v + offs); } - return jl_new_bits(jl_field_type(st,i), (char*)v + offs); + jl_value_t *ty = jl_field_type(st, i); + if (jl_is_uniontype(ty)) { + uint8_t sel = ((uint8_t*)v)[offs + jl_field_size(st, i) - 1]; + ty = jl_nth_union_component(ty, sel); + } + return jl_new_bits(ty, (char*)v + offs); } JL_DLLEXPORT jl_value_t *jl_get_nth_field_checked(jl_value_t *v, size_t i) { jl_datatype_t *st = (jl_datatype_t*)jl_typeof(v); if (i >= jl_datatype_nfields(st)) - jl_bounds_error_int(v, i+1); - size_t offs = jl_field_offset(st,i); - if (jl_field_isptr(st,i)) { + jl_bounds_error_int(v, i + 1); + size_t offs = jl_field_offset(st, i); + if (jl_field_isptr(st, i)) { jl_value_t *fval = *(jl_value_t**)((char*)v + offs); if (fval == NULL) jl_throw(jl_undefref_exception); return fval; } - return jl_new_bits(jl_field_type(st,i), (char*)v + offs); + jl_value_t *ty = jl_field_type(st, i); + if (jl_is_uniontype(ty)) { + size_t fsz = jl_field_size(st, i); + uint8_t sel = ((uint8_t*)v)[offs + fsz - 1]; + ty = jl_nth_union_component(ty, sel); + if (jl_is_datatype_singleton((jl_datatype_t*)ty)) + return ((jl_datatype_t*)ty)->instance; + } + return jl_new_bits(ty, (char*)v + offs); } JL_DLLEXPORT void jl_set_nth_field(jl_value_t *v, size_t i, jl_value_t *rhs) { jl_datatype_t *st = (jl_datatype_t*)jl_typeof(v); - size_t offs = jl_field_offset(st,i); - if (jl_field_isptr(st,i)) { + size_t offs = jl_field_offset(st, i); + if (jl_field_isptr(st, i)) { *(jl_value_t**)((char*)v + offs) = rhs; if (rhs != NULL) jl_gc_wb(v, rhs); } else { + jl_value_t *ty = jl_field_type(st, i); + if (jl_is_uniontype(ty)) { + uint8_t *psel = &((uint8_t*)v)[offs + jl_field_size(st, i) - 1]; + unsigned nth = 0; + if (!jl_find_union_component(ty, jl_typeof(rhs), &nth)) + assert(0 && "invalid field assignment to isbits union"); + *psel = nth; + if (jl_is_datatype_singleton((jl_datatype_t*)ty)) + return; + } jl_assign_bits((char*)v + offs, rhs); } } @@ -769,8 +830,8 @@ JL_DLLEXPORT void jl_set_nth_field(jl_value_t *v, size_t i, jl_value_t *rhs) JL_DLLEXPORT int jl_field_isdefined(jl_value_t *v, size_t i) { jl_datatype_t *st = (jl_datatype_t*)jl_typeof(v); - size_t offs = jl_field_offset(st,i); - if (jl_field_isptr(st,i)) { + size_t offs = jl_field_offset(st, i); + if (jl_field_isptr(st, i)) { return *(jl_value_t**)((char*)v + offs) != NULL; } return 1; diff --git a/src/dump.c b/src/dump.c index 0480d1a5b09c0..7909a0865f082 100644 --- a/src/dump.c +++ b/src/dump.c @@ -721,8 +721,11 @@ static void jl_serialize_value_(jl_serializer_state *s, jl_value_t *v, int as_li size_t i, nf = jl_datatype_nfields(jl_typemap_entry_type); while ((jl_value_t*)te != jl_nothing) { for (i = 1; i < nf; i++) { - if (jl_field_size(jl_typemap_entry_type, i) > 0) + if (jl_field_size(jl_typemap_entry_type, i) > 0) { jl_serialize_value(s, jl_get_nth_field((jl_value_t*)te, i)); + if (!jl_field_isptr(jl_typemap_entry_type, i)) + write_int8(s->s, 0); + } } te = te->next; } @@ -808,7 +811,7 @@ static void jl_serialize_value_(jl_serializer_state *s, jl_value_t *v, int as_li return; } size_t nf = jl_datatype_nfields(t); - if (nf == 0 && jl_datatype_size(t)>0) { + if (nf == 0 && jl_datatype_size(t) > 0) { if (t->name == jl_pointer_typename && jl_unbox_voidpointer(v) != (void*)-1) { // normalize most pointers to NULL, to help catch memory errors // but permit MAP_FAILED / INVALID_HANDLE to be stored unchanged @@ -824,8 +827,17 @@ static void jl_serialize_value_(jl_serializer_state *s, jl_value_t *v, int as_li else { size_t i; for (i = 0; i < nf; i++) { - if (jl_field_size(t, i) > 0) { + size_t offs = jl_field_offset(t, i); + size_t fsz = jl_field_size(t, i); + if (fsz > 0) { jl_serialize_value(s, jl_get_nth_field(v, i)); + if (!jl_field_isptr(t, i)) { + uint8_t sel = 0; + if (jl_is_uniontype(jl_field_type(t, i))) { + sel = ((uint8_t*)v)[offs + fsz - 1]; + } + write_int8(s->s, sel); + } } } } @@ -1589,13 +1601,21 @@ static void jl_deserialize_struct(jl_serializer_state *s, jl_value_t *v, size_t size_t i, nf = jl_datatype_nfields(dt); char *data = (char*)jl_data_ptr(v); for (i = startfield; i < nf; i++) { - if (jl_field_size(dt, i) > 0) { + size_t offs = jl_field_offset(dt, i); + size_t fsz = jl_field_size(dt, i); + jl_value_t **fld = (jl_value_t**)(data + offs); + if (fsz > 0) { if (jl_field_isptr(dt, i)) { - jl_value_t **fld = (jl_value_t**)(data+jl_field_offset(dt, i)); *fld = jl_deserialize_value(s, fld); } else { - jl_set_nth_field(v, i, jl_deserialize_value(s, NULL)); + jl_value_t *fldval = jl_deserialize_value(s, NULL); + jl_assign_bits((char*)fld, fldval); + uint8_t union_selector = read_uint8(s->s); + if (union_selector) { + uint8_t *psel = (uint8_t*)fld + fsz - 1; + *psel = union_selector - 1; + } } } } @@ -1674,7 +1694,7 @@ static jl_value_t *jl_deserialize_value_any(jl_serializer_state *s, jl_value_t * } } jl_set_typeof(v, dt); - if (jl_datatype_nfields(dt) == 0 && jl_datatype_size(dt)>0) { + if (jl_datatype_nfields(dt) == 0 && jl_datatype_size(dt) > 0) { int nby = jl_datatype_size(dt); ios_read(s->s, (char*)jl_data_ptr(v), nby); } diff --git a/src/intrinsics.cpp b/src/intrinsics.cpp index 5b7397ddc80d0..9b20cb4a32884 100644 --- a/src/intrinsics.cpp +++ b/src/intrinsics.cpp @@ -302,6 +302,9 @@ static Value *emit_unbox(jl_codectx_t &ctx, Type *to, const jl_cgval_t &x, jl_va } if (!dest) return unboxed; + Type *dest_ty = unboxed->getType()->getPointerTo(); + if (dest->getType() != dest_ty) + dest = emit_bitcast(dest, dest_ty); ctx.builder.CreateStore(unboxed, dest, volatile_store); return NULL; } diff --git a/src/jltypes.c b/src/jltypes.c index 78c294740e3e4..3a2bdcd00ffb9 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -368,6 +368,22 @@ jl_value_t *jl_nth_union_component(jl_value_t *v, int i) return nth_union_component(v, &i); } +// inverse of jl_nth_union_component +int jl_find_union_component(jl_value_t *haystack, jl_value_t *needle, unsigned *nth) +{ + if (jl_is_uniontype(haystack)) { + if (jl_find_union_component(((jl_uniontype_t*)haystack)->a, needle, nth)) + return 1; + if (jl_find_union_component(((jl_uniontype_t*)haystack)->b, needle, nth)) + return 1; + return 0; + } + if (needle == haystack) + return 1; + (*nth)++; + return 0; +} + static void flatten_type_union(jl_value_t **types, size_t n, jl_value_t **out, size_t *idx) { size_t i; diff --git a/src/julia_internal.h b/src/julia_internal.h index 0970ae02fa611..f5b4c584dc954 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -475,6 +475,7 @@ jl_value_t *jl_substitute_var(jl_value_t *t, jl_tvar_t *var, jl_value_t *val); jl_svec_t *jl_outer_unionall_vars(jl_value_t *u); int jl_count_union_components(jl_value_t *v); jl_value_t *jl_nth_union_component(jl_value_t *v, int i); +int jl_find_union_component(jl_value_t *haystack, jl_value_t *needle, unsigned *nth); jl_datatype_t *jl_new_uninitialized_datatype(void); void jl_precompute_memoized_dt(jl_datatype_t *dt); jl_datatype_t *jl_wrap_Type(jl_value_t *t); // x -> Type{x} From 7906a3bb1df2d981f87dc7b7d828facce6272aa6 Mon Sep 17 00:00:00 2001 From: quinnj Date: Tue, 11 Jul 2017 07:20:13 -0600 Subject: [PATCH 038/324] Rebase --- src/cgutils.cpp | 20 ++++++++++---------- src/codegen.cpp | 4 ++-- src/intrinsics.cpp | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/cgutils.cpp b/src/cgutils.cpp index caf0d330853a7..ee04b725b0326 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -1417,18 +1417,18 @@ static jl_cgval_t emit_getfield_knownidx(jl_codectx_t &ctx, const jl_cgval_t &st } else if (jl_is_uniontype(jfty)) { int fsz = jl_field_size(jt, idx); - Value *ptindex = builder.CreateGEP(LLVM37_param(T_int8) emit_bitcast(addr, T_pint8), ConstantInt::get(T_size, fsz - 1)); - Value *tindex = builder.CreateNUWAdd(ConstantInt::get(T_int8, 1), builder.CreateLoad(ptindex)); + Value *ptindex = ctx.builder.CreateGEP(T_int8, emit_bitcast(ctx, addr, T_pint8), ConstantInt::get(T_size, fsz - 1)); + Value *tindex = ctx.builder.CreateNUWAdd(ConstantInt::get(T_int8, 1), ctx.builder.CreateLoad(ptindex)); bool isimmutable = strct.isimmutable; Value *gcroot = strct.gcroot; if (jt->mutabl) { // move value to an immutable stack slot Type *AT = ArrayType::get(IntegerType::get(jl_LLVMContext, 8 * align), (fsz + align - 2) / align); - AllocaInst *lv = emit_static_alloca(AT, ctx); + AllocaInst *lv = emit_static_alloca(ctx, AT); if (align > 1) lv->setAlignment(align); Value *nbytes = ConstantInt::get(T_size, fsz - 1); - builder.CreateMemCpy(lv, addr, nbytes, align); + ctx.builder.CreateMemCpy(lv, addr, nbytes, align); addr = lv; isimmutable = true; gcroot = NULL; @@ -2220,11 +2220,11 @@ static void emit_setfield(jl_codectx_t &ctx, if (jl_is_uniontype(jfty)) { int fsz = jl_field_size(sty, idx0); // compute tindex from rhs - jl_cgval_t rhs_union = convert_julia_type(rhs, jfty, ctx); - Value *ptindex = builder.CreateGEP(LLVM37_param(T_int8) emit_bitcast(addr, T_pint8), ConstantInt::get(T_size, fsz - 1)); - Value *tindex = compute_tindex_unboxed(rhs_union, jfty, ctx); - tindex = builder.CreateNUWSub(tindex, ConstantInt::get(T_int8, 1)); - builder.CreateStore(tindex, ptindex); + jl_cgval_t rhs_union = convert_julia_type(ctx, rhs, jfty); + Value *tindex = compute_tindex_unboxed(ctx, rhs_union, jfty); + tindex = ctx.builder.CreateNUWSub(tindex, ConstantInt::get(T_int8, 1)); + Value *ptindex = ctx.builder.CreateGEP(T_int8, emit_bitcast(ctx, addr, T_pint8), ConstantInt::get(T_size, fsz - 1)); + ctx.builder.CreateStore(tindex, ptindex); // copy data emit_unionmove(addr, rhs, NULL, false, NULL, ctx); } @@ -2232,7 +2232,7 @@ static void emit_setfield(jl_codectx_t &ctx, int align = jl_field_offset(sty, idx0); align |= 16; align &= -align; - typed_store(addr, ConstantInt::get(T_size, 0), rhs, jfty, ctx, + typed_store(ctx, addr, ConstantInt::get(T_size, 0), rhs, jfty, strct.tbaa, data_pointer(ctx, strct, T_pjlvalue), align); } } diff --git a/src/codegen.cpp b/src/codegen.cpp index e14ad61b85966..d18eb96cd8e84 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -738,7 +738,7 @@ static inline jl_cgval_t update_julia_type(jl_codectx_t &ctx, const jl_cgval_t & return jl_cgval_t(v, typ, NULL); } -static jl_cgval_t convert_julia_type(const jl_cgval_t &v, jl_value_t *typ, jl_codectx_t *ctx, bool needsroot = true); +static jl_cgval_t convert_julia_type(jl_codectx_t &ctx, const jl_cgval_t &v, jl_value_t *typ, bool needsroot = true); // --- allocating local variables --- @@ -5998,7 +5998,7 @@ static void init_julia_llvm_env(Module *m) StructType::create(jl_LLVMContext, ArrayRef(vaelts,sizeof(vaelts)/sizeof(vaelts[0])), "jl_array_t"); - jl_parray_llvmt = PointerType::get(jl_array_llvmt,0); + jl_parray_llvmt = PointerType::get(jl_array_llvmt, 0); global_to_llvm("__stack_chk_guard", (void*)&__stack_chk_guard, m); Function *jl__stack_chk_fail = diff --git a/src/intrinsics.cpp b/src/intrinsics.cpp index 9b20cb4a32884..0d5e5264685ff 100644 --- a/src/intrinsics.cpp +++ b/src/intrinsics.cpp @@ -304,7 +304,7 @@ static Value *emit_unbox(jl_codectx_t &ctx, Type *to, const jl_cgval_t &x, jl_va return unboxed; Type *dest_ty = unboxed->getType()->getPointerTo(); if (dest->getType() != dest_ty) - dest = emit_bitcast(dest, dest_ty); + dest = emit_bitcast(ctx, dest, dest_ty); ctx.builder.CreateStore(unboxed, dest, volatile_store); return NULL; } From 442b02d87f8ead69e4f9fa4c863fa11ac83423cc Mon Sep 17 00:00:00 2001 From: quinnj Date: Tue, 11 Jul 2017 07:22:41 -0600 Subject: [PATCH 039/324] A few fixes for union isbits struct fields --- src/cgutils.cpp | 12 +++++------- src/datatype.c | 2 +- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/cgutils.cpp b/src/cgutils.cpp index ee04b725b0326..70e65e5bae878 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -1193,10 +1193,9 @@ static jl_cgval_t typed_load(jl_codectx_t &ctx, Value *ptr, Value *idx_0based, j Type *elty = julia_type_to_llvm(jltype, &isboxed); if (type_is_ghost(elty)) return ghostValue(jltype); - Value *data; if (isboxed) elty = T_prjlvalue; - // TODO: preserving_pointercast? + Value *data = ptr; if (ptr->getType()->getContainedType(0) != elty) data = emit_bitcast(ctx, ptr, PointerType::get(elty, 0)); else @@ -1445,9 +1444,6 @@ static jl_cgval_t emit_getfield_knownidx(jl_codectx_t &ctx, const jl_cgval_t &st fieldval.gcroot = strct.gcroot; return fieldval; } - int align = jl_field_offset(jt, idx); - align |= 16; - align &= -align; return typed_load(ctx, addr, ConstantInt::get(T_size, 0), jfty, strct.tbaa, true, align); } else if (isa(strct.V)) { @@ -2059,7 +2055,7 @@ static void emit_unionmove(jl_codectx_t &ctx, Value *dest, const jl_cgval_t &src jl_value_t *typ = src.constant ? jl_typeof(src.constant) : src.typ; Type *store_ty = julia_type_to_llvm(typ); assert(skip || jl_isbits(typ)); - if (jl_isbits(typ) && jl_datatype_size(typ) > 0) { + if (jl_isbits(typ)) { if (!src.ispointer() || src.constant) { emit_unbox(ctx, store_ty, src, typ, dest, isVolatile); } @@ -2226,7 +2222,9 @@ static void emit_setfield(jl_codectx_t &ctx, Value *ptindex = ctx.builder.CreateGEP(T_int8, emit_bitcast(ctx, addr, T_pint8), ConstantInt::get(T_size, fsz - 1)); ctx.builder.CreateStore(tindex, ptindex); // copy data - emit_unionmove(addr, rhs, NULL, false, NULL, ctx); + if (!rhs.isghost) { + emit_unionmove(ctx, addr, rhs, NULL, false, NULL); + } } else { int align = jl_field_offset(sty, idx0); diff --git a/src/datatype.c b/src/datatype.c index 6abed317ebeb8..0f0dcfbadd57b 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -820,7 +820,7 @@ JL_DLLEXPORT void jl_set_nth_field(jl_value_t *v, size_t i, jl_value_t *rhs) if (!jl_find_union_component(ty, jl_typeof(rhs), &nth)) assert(0 && "invalid field assignment to isbits union"); *psel = nth; - if (jl_is_datatype_singleton((jl_datatype_t*)ty)) + if (jl_is_datatype_singleton((jl_datatype_t*)jl_typeof(rhs))) return; } jl_assign_bits((char*)v + offs, rhs); From bf78898a686cfb87e7db7e140a2adb94ad7d3310 Mon Sep 17 00:00:00 2001 From: quinnj Date: Tue, 11 Jul 2017 07:24:31 -0600 Subject: [PATCH 040/324] Inline support for isbits union array element types --- base/array.jl | 34 +++++++- src/array.c | 214 ++++++++++++++++++++++++++++++++++++----------- src/cgutils.cpp | 54 ++++++------ src/codegen.cpp | 75 +++++++++++++---- src/datatype.c | 20 +++-- src/dump.c | 6 +- src/gc.c | 3 + src/julia.h | 1 + src/staticdata.c | 3 +- 9 files changed, 302 insertions(+), 108 deletions(-) diff --git a/base/array.jl b/base/array.jl index 7d3f231f691b8..22f1a6ac6ee90 100644 --- a/base/array.jl +++ b/base/array.jl @@ -109,8 +109,36 @@ size(a::Array{<:Any,N}) where {N} = (@_inline_meta; ntuple(M -> size(a, M), Val( asize_from(a::Array, n) = n > ndims(a) ? () : (arraysize(a,n), asize_from(a, n+1)...) +""" + Base.isbitsunion(::Type{T}) + +Return whether a type is an "is-bits" Union type, meaning each type included in a Union is `isbits`. +""" +function isbitsunion end + +function isbitsunion(U::Union) + for u in Base.uniontypes(U) + isbits(u) || return false + end + return true +end +isbitsunion(T) = false + +""" + Base.bitsunionsize(U::Union) + +For a Union of `isbits` types, return the size of the largest type. +""" +function bitsunionsize(U::Union) + sz = 0 + for u in Base.uniontypes(U) + sz = max(sz, sizeof(u)) + end + return sz +end + length(a::Array) = arraylen(a) -elsize(a::Array{T}) where {T} = isbits(T) ? sizeof(T) : sizeof(Ptr) +elsize(a::Array{T}) where {T} = isbits(T) ? sizeof(T) : (isbitsunion(T) ? bitsunionsize(T) : sizeof(Ptr)) sizeof(a::Array) = Core.sizeof(a) function isassigned(a::Array, i::Int...) @@ -154,7 +182,7 @@ copy!(dest::Array{T}, src::Array{T}) where {T} = copy!(dest, 1, src, 1, length(s copy(a::T) where {T<:Array} = ccall(:jl_array_copy, Ref{T}, (Any,), a) function reinterpret(::Type{T}, a::Array{S,1}) where T where S - nel = Int(div(length(a)*sizeof(S),sizeof(T))) + nel = Int(div(length(a) * sizeof(S), sizeof(T))) # TODO: maybe check that remainder is zero? return reinterpret(T, a, (nel,)) end @@ -173,7 +201,7 @@ function reinterpret(::Type{T}, a::Array{S}, dims::NTuple{N,Int}) where T where end isbits(T) || throwbits(S, T, T) isbits(S) || throwbits(S, T, S) - nel = div(length(a)*sizeof(S),sizeof(T)) + nel = div(length(a) * sizeof(S), sizeof(T)) if prod(dims) != nel _throw_dmrsa(dims, nel) end diff --git a/src/array.c b/src/array.c index 3e77a0e9cbc30..8c90de9b85b8d 100644 --- a/src/array.c +++ b/src/array.c @@ -22,16 +22,10 @@ extern "C" { // array constructors --------------------------------------------------------- -static inline int store_unboxed(jl_value_t *el_type) // jl_isbits +JL_DLLEXPORT int jl_array_store_unboxed(jl_value_t *eltype) { - return jl_is_leaf_type(el_type) && jl_is_immutable(el_type) && - ((jl_datatype_t*)el_type)->layout && - ((jl_datatype_t*)el_type)->layout->npointers == 0; -} - -int jl_array_store_unboxed(jl_value_t *el_type) -{ - return store_unboxed(el_type); + size_t fsz = 0, al = 0; + return jl_islayout_inline(eltype, &fsz, &al); } STATIC_INLINE jl_value_t *jl_array_owner(jl_array_t *a) @@ -67,16 +61,20 @@ static jl_array_t *_new_array_(jl_value_t *atype, uint32_t ndims, size_t *dims, jl_error("invalid Array dimensions"); nel = prod; } - + int isunion = atype != NULL && jl_is_uniontype(jl_tparam0(atype)); if (isunboxed) { wideint_t prod = (wideint_t)elsz * (wideint_t)nel; if (prod > (wideint_t) MAXINTVAL) jl_error("invalid Array size"); tot = prod; - if (elsz == 1) { + if (elsz == 1 && !isunion) { // extra byte for all julia allocated byte arrays tot++; } + if (isunion) { + // an extra byte for each isbits union array element, stored directly after the last array element + tot += nel; + } } else { wideint_t prod = (wideint_t)sizeof(void*) * (wideint_t)nel; @@ -97,7 +95,7 @@ static jl_array_t *_new_array_(jl_value_t *atype, uint32_t ndims, size_t *dims, // No allocation or safepoint allowed after this a->flags.how = 0; data = (char*)a + doffs; - if (tot > 0 && !isunboxed) + if ((tot > 0 && !isunboxed) || isunion) memset(data, 0, tot); } else { @@ -109,7 +107,8 @@ static jl_array_t *_new_array_(jl_value_t *atype, uint32_t ndims, size_t *dims, // No allocation or safepoint allowed after this a->flags.how = 2; jl_gc_track_malloced_array(ptls, a); - if (!isunboxed) + if (!isunboxed || isunion) + // need to zero out isbits union array selector bytes to ensure a valid type index memset(data, 0, tot); } a->flags.pooled = tsz <= GC_MAX_SZCLASS; @@ -141,11 +140,14 @@ static jl_array_t *_new_array_(jl_value_t *atype, uint32_t ndims, size_t *dims, static inline jl_array_t *_new_array(jl_value_t *atype, uint32_t ndims, size_t *dims) { - int isunboxed=0, elsz=sizeof(void*); - jl_value_t *el_type = jl_tparam0(atype); - isunboxed = store_unboxed(el_type); - if (isunboxed) - elsz = jl_datatype_size(el_type); + jl_value_t *eltype = jl_tparam0(atype); + size_t elsz = 0, al = 0; + int isunboxed = jl_islayout_inline(eltype, &elsz, &al); + if (!isunboxed) { + elsz = sizeof(void*); + al = elsz; + } + return _new_array_(atype, ndims, dims, isunboxed, elsz); } @@ -180,7 +182,7 @@ JL_DLLEXPORT jl_array_t *jl_reshape_array(jl_value_t *atype, jl_array_t *data, size_t *dims = (size_t*)_dims; int ndimwords = jl_array_ndimwords(ndims); - int tsz = JL_ARRAY_ALIGN(sizeof(jl_array_t) + ndimwords*sizeof(size_t) + sizeof(void*), JL_SMALL_BYTE_ALIGNMENT); + int tsz = JL_ARRAY_ALIGN(sizeof(jl_array_t) + ndimwords * sizeof(size_t) + sizeof(void*), JL_SMALL_BYTE_ALIGNMENT); a = (jl_array_t*)jl_gc_alloc(ptls, tsz, atype); // No allocation or safepoint allowed after this a->flags.pooled = tsz <= GC_MAX_SZCLASS; @@ -189,18 +191,24 @@ JL_DLLEXPORT jl_array_t *jl_reshape_array(jl_value_t *atype, jl_array_t *data, a->data = NULL; a->flags.isaligned = data->flags.isaligned; jl_array_t *owner = (jl_array_t*)jl_array_owner(data); - jl_value_t *el_type = jl_tparam0(atype); - assert(store_unboxed(el_type) == !data->flags.ptrarray); - if (!data->flags.ptrarray) { - a->elsize = jl_datatype_size(el_type); - unsigned align = jl_datatype_align(el_type); + jl_value_t *eltype = jl_tparam0(atype); + size_t elsz = 0, align = 0; + int isboxed = !jl_islayout_inline(eltype, &elsz, &align); + assert(isboxed == data->flags.ptrarray); + if (!isboxed) { + a->elsize = elsz; jl_value_t *ownerty = jl_typeof(owner); - unsigned oldalign = (ownerty == (jl_value_t*)jl_string_type ? 1 : - jl_datatype_align(jl_tparam0(ownerty))); + size_t oldelsz = 0, oldalign = 0; + if (ownerty == (jl_value_t*)jl_string_type) { + oldalign = 1; + } + else { + jl_islayout_inline(jl_tparam0(ownerty), &oldelsz, &oldalign); + } if (oldalign < align) jl_exceptionf(jl_argumenterror_type, - "reinterpret from alignment %u bytes to alignment %u bytes not allowed", - oldalign, align); + "reinterpret from alignment %d bytes to alignment %d bytes not allowed", + (int) oldalign, (int) align); a->flags.ptrarray = 0; } else { @@ -276,14 +284,17 @@ JL_DLLEXPORT jl_array_t *jl_ptr_to_array_1d(jl_value_t *atype, void *data, { jl_ptls_t ptls = jl_get_ptls_states(); jl_array_t *a; - jl_value_t *el_type = jl_tparam0(atype); + jl_value_t *eltype = jl_tparam0(atype); - int isunboxed = store_unboxed(el_type); + int isunboxed = jl_array_store_unboxed(eltype); size_t elsz; unsigned align; + if (isunboxed && jl_is_uniontype(eltype)) + jl_exceptionf(jl_argumenterror_type, + "unsafe_wrap: unspecified layout for union element type"); if (isunboxed) { - elsz = jl_datatype_size(el_type); - align = jl_datatype_align(el_type); + elsz = jl_datatype_size(eltype); + align = jl_datatype_align(eltype); } else { align = elsz = sizeof(void*); @@ -339,14 +350,17 @@ JL_DLLEXPORT jl_array_t *jl_ptr_to_array(jl_value_t *atype, void *data, } if (__unlikely(ndims == 1)) return jl_ptr_to_array_1d(atype, data, nel, own_buffer); - jl_value_t *el_type = jl_tparam0(atype); + jl_value_t *eltype = jl_tparam0(atype); - int isunboxed = store_unboxed(el_type); + int isunboxed = jl_array_store_unboxed(eltype); size_t elsz; unsigned align; + if (isunboxed && jl_is_uniontype(eltype)) + jl_exceptionf(jl_argumenterror_type, + "unsafe_wrap: unspecified layout for union element type"); if (isunboxed) { - elsz = jl_datatype_size(el_type); - align = jl_datatype_align(el_type); + elsz = jl_datatype_size(eltype); + align = jl_datatype_align(eltype); } else { align = elsz = sizeof(void*); @@ -485,8 +499,15 @@ JL_DLLEXPORT jl_value_t *jl_arrayref(jl_array_t *a, size_t i) assert(i < jl_array_len(a)); jl_value_t *elt; if (!a->flags.ptrarray) { - jl_value_t *el_type = (jl_value_t*)jl_tparam0(jl_typeof(a)); - elt = jl_new_bits(el_type, &((char*)a->data)[i*a->elsize]); + jl_value_t *eltype = (jl_value_t*)jl_tparam0(jl_typeof(a)); + if (jl_is_uniontype(eltype)) { + // isbits union selector bytes are always stored directly after the last array element + uint8_t sel = ((uint8_t*)a->data)[jl_array_len(a) * a->elsize + i]; + eltype = jl_nth_union_component(eltype, sel); + if (jl_is_datatype_singleton((jl_datatype_t*)eltype)) + return ((jl_datatype_t*)eltype)->instance; + } + elt = jl_new_bits(eltype, &((char*)a->data)[i * a->elsize]); } else { elt = ((jl_value_t**)a->data)[i]; @@ -539,13 +560,22 @@ int jl_array_isdefined(jl_value_t **args0, int nargs) JL_DLLEXPORT void jl_arrayset(jl_array_t *a, jl_value_t *rhs, size_t i) { assert(i < jl_array_len(a)); - jl_value_t *el_type = jl_tparam0(jl_typeof(a)); - if (el_type != (jl_value_t*)jl_any_type) { - if (!jl_isa(rhs, el_type)) - jl_type_error("arrayset", el_type, rhs); + jl_value_t *eltype = jl_tparam0(jl_typeof(a)); + if (eltype != (jl_value_t*)jl_any_type) { + if (!jl_isa(rhs, eltype)) + jl_type_error("arrayset", eltype, rhs); } if (!a->flags.ptrarray) { - jl_assign_bits(&((char*)a->data)[i*a->elsize], rhs); + if (jl_is_uniontype(eltype)) { + uint8_t *psel = &((uint8_t*)a->data)[jl_array_len(a) * a->elsize + i]; + unsigned nth = 0; + if (!jl_find_union_component(eltype, jl_typeof(rhs), &nth)) + assert(0 && "invalid arrayset to isbits union"); + *psel = nth; + if (jl_is_datatype_singleton((jl_datatype_t*)jl_typeof(rhs))) + return; + } + jl_assign_bits(&((char*)a->data)[i * a->elsize], rhs); } else { ((jl_value_t**)a->data)[i] = rhs; @@ -586,6 +616,10 @@ static int NOINLINE array_resize_buffer(jl_array_t *a, size_t newlen) nbytes++; oldnbytes++; } + if (!a->flags.ptrarray && jl_is_uniontype(jl_tparam0(jl_typeof(a)))) { + nbytes += newlen; + oldnbytes += oldlen; + } int newbuf = 0; if (a->flags.how == 2) { // already malloc'd - use realloc @@ -649,6 +683,9 @@ static void NOINLINE array_try_unshare(jl_array_t *a) size_t len = jl_array_nrows(a); size_t es = a->elsize; size_t nbytes = len * es; + if (!a->flags.ptrarray && jl_is_uniontype(jl_tparam0(jl_typeof(a)))) { + nbytes += len; + } char *olddata = (char*)a->data; int newbuf = array_resize_buffer(a, len); assert(newbuf); @@ -688,11 +725,16 @@ STATIC_INLINE void jl_array_grow_at_beg(jl_array_t *a, size_t idx, size_t inc, size_t nbinc = inc * elsz; char *data = (char*)a->data; char *newdata; + int isbitsunion = !a->flags.ptrarray && jl_is_uniontype(jl_tparam0(jl_typeof(a))); if (a->offset >= inc) { newdata = data - nbinc; a->offset -= inc; - if (idx > 0) { + if (idx > 0) memmove(newdata, data, idx * elsz); + if (isbitsunion) { + // move isbits union select bytes back by `inc` & zero out new selector bytes + memmove(data + n * elsz + idx + inc, data + n * elsz + idx, n - idx); + memset(data + n * elsz + idx, 0, inc); } } else { @@ -707,6 +749,11 @@ STATIC_INLINE void jl_array_grow_at_beg(jl_array_t *a, size_t idx, size_t inc, if (!array_resize_buffer(a, newlen)) data = (char*)a->data + oldoffsnb; newdata = (char*)a->data + newoffset * elsz; + if (isbitsunion) { + memmove(newdata + newnrows * elsz, data + n * elsz, idx); + memmove(newdata + newnrows * elsz + idx + inc, data + n * elsz + idx, n - idx); + memset(newdata + newnrows * elsz + idx, 0, inc); + } // We could use memcpy if resizing allocates a new buffer, // hopefully it's not a particularly important optimization. if (idx > 0 && newdata < data) @@ -719,6 +766,10 @@ STATIC_INLINE void jl_array_grow_at_beg(jl_array_t *a, size_t idx, size_t inc, else { a->offset = (a->maxsize - newnrows) / 2; newdata = data - oldoffsnb + a->offset * elsz; + if (isbitsunion) { + memmove(newdata + newnrows * elsz + idx + inc, data + n * elsz + idx, n - idx); + memset(newdata + newnrows * elsz + idx, 0, inc); + } // We could use memcpy if resizing allocates a new buffer, // hopefully it's not a particularly important optimization. if (idx > 0 && newdata < data) @@ -754,6 +805,8 @@ STATIC_INLINE void jl_array_grow_at_end(jl_array_t *a, size_t idx, } size_t elsz = a->elsize; char *data = (char*)a->data; + int isbitsunion = !a->flags.ptrarray && jl_is_uniontype(jl_tparam0(jl_typeof(a))); + size_t oldmaxsize = jl_array_len(a); int has_gap = n > idx; size_t reqmaxsize = a->offset + n + inc; if (__unlikely(reqmaxsize > a->maxsize)) { @@ -769,19 +822,43 @@ STATIC_INLINE void jl_array_grow_at_end(jl_array_t *a, size_t idx, char *newdata = (char*)a->data + a->offset * elsz; if (newbuf) { memcpy(newdata, data, nb1); + if (isbitsunion) { + memmove(newdata + (oldmaxsize + inc) * elsz, data + oldmaxsize * elsz, idx); + } if (has_gap) { memcpy(newdata + nb1 + nbinc, data + nb1, n * elsz - nb1); + if (isbitsunion) { + memmove(newdata + (oldmaxsize + inc) * elsz + idx + inc, data + oldmaxsize * elsz + idx, n - idx); + memset(newdata + (oldmaxsize + inc) * elsz + idx, 0, inc); + } } } else if (has_gap) { + if (isbitsunion) { + memmove(newdata + (oldmaxsize + inc) * elsz, newdata + oldmaxsize * elsz, idx); + memmove(newdata + (oldmaxsize + inc) * elsz + idx + inc, newdata + oldmaxsize * elsz + idx, n - idx); + memset(newdata + (oldmaxsize + inc) * elsz + idx, 0, inc); + } memmove(newdata + nb1 + nbinc, newdata + nb1, n * elsz - nb1); } a->data = data = newdata; } else if (has_gap) { size_t nb1 = idx * elsz; + if (isbitsunion) { + memmove(data + (n + inc) * elsz + idx + inc, data + n * elsz + idx, n - idx); + memmove(data + (n + inc) * elsz, data + n * elsz, idx); + memset(data + (n + inc) * elsz + idx, 0, inc); + } memmove(data + nb1 + inc * elsz, data + nb1, n * elsz - nb1); } + else { + if (isbitsunion) { + // need to move isbits union selector bytes back & zero out new bytes + memmove(data + (n + inc) * elsz, data + n * elsz, oldmaxsize); + memset(data + (n + inc) * elsz + idx, 0, inc); + } + } size_t newnrows = n + inc; #ifdef STORE_ARRAY_LEN a->length = newnrows; @@ -840,6 +917,7 @@ STATIC_INLINE void jl_array_del_at_beg(jl_array_t *a, size_t idx, size_t dec, // assume inbounds, assume unshared size_t elsz = a->elsize; size_t offset = a->offset; + int isbitsunion = !a->flags.ptrarray && jl_is_uniontype(jl_tparam0(jl_typeof(a))); offset += dec; #ifdef STORE_ARRAY_LEN a->length = n - dec; @@ -854,15 +932,28 @@ STATIC_INLINE void jl_array_del_at_beg(jl_array_t *a, size_t idx, size_t dec, size_t nb1 = idx * elsz; // size in bytes of the first block size_t nbtotal = a->nrows * elsz; // size in bytes of the new array // Implicit '\0' for byte arrays - if (elsz == 1) + if (elsz == 1 && !isbitsunion) nbtotal++; - if (idx > 0) + if (idx > 0) { memmove(newdata, olddata, nb1); + if (isbitsunion) { + memmove(newdata + nbtotal, olddata + n * elsz, idx); + memset(newdata + nbtotal + idx, 0, dec); + } + } memmove(newdata + nb1, olddata + nb1 + nbdec, nbtotal - nb1); + if (isbitsunion) { + memmove(newdata + nbtotal + idx, olddata + n * elsz + idx + dec, n - idx); + } a->data = newdata; } else { - a->data = (char*)a->data + nbdec; + char *data = (char*)a->data; + a->data = data + nbdec; + if (isbitsunion) { + // move isbits union selector bytes forward, overwriting the deleted bytes + memmove(data + elsz * n, data + elsz * n + dec, n - dec); + } } a->offset = newoffs; } @@ -874,16 +965,25 @@ STATIC_INLINE void jl_array_del_at_end(jl_array_t *a, size_t idx, size_t dec, // assume inbounds, assume unshared char *data = (char*)a->data; size_t elsz = a->elsize; + int isbitsunion = !a->flags.ptrarray && jl_is_uniontype(jl_tparam0(jl_typeof(a))); size_t last = idx + dec; - if (n > last) + if (n > last) { memmove(data + idx * elsz, data + last * elsz, (n - last) * elsz); + if (isbitsunion) { + memmove(data + n * elsz + idx, data + n * elsz + last, n - last); + } + } n -= dec; - if (elsz == 1) + if (elsz == 1 && !isbitsunion) data[n] = 0; a->nrows = n; #ifdef STORE_ARRAY_LEN a->length = n; #endif + if (isbitsunion) { + // move last isbits union selector bytes forward to close the gap of deleted elements + memmove(data + n * elsz, data + (n + dec) * elsz, n); + } } JL_DLLEXPORT void jl_array_del_at(jl_array_t *a, ssize_t idx, size_t dec) @@ -941,9 +1041,15 @@ JL_DLLEXPORT void jl_array_sizehint(jl_array_t *a, size_t sz) JL_DLLEXPORT jl_array_t *jl_array_copy(jl_array_t *ary) { size_t elsz = ary->elsize; + size_t len = jl_array_len(ary); jl_array_t *new_ary = _new_array_(jl_typeof(ary), jl_array_ndims(ary), &ary->nrows, !ary->flags.ptrarray, elsz); - memcpy(new_ary->data, ary->data, jl_array_len(ary) * elsz); + memcpy(new_ary->data, ary->data, len * elsz); + // ensure isbits union arrays copy their selector bytes correctly + if (!ary->flags.ptrarray && jl_is_uniontype(jl_tparam0(jl_typeof(ary)))) { + memcpy((char*)new_ary->data + len * elsz, + (char*)ary->data + len * elsz, len); + } return new_ary; } @@ -985,6 +1091,14 @@ static NOINLINE ssize_t jl_array_ptr_copy_backward(jl_value_t *owner, JL_DLLEXPORT void jl_array_ptr_copy(jl_array_t *dest, void **dest_p, jl_array_t *src, void **src_p, ssize_t n) { + // need to intercept union isbits arrays here since they're unboxed + if (!src->flags.ptrarray && jl_is_uniontype(jl_tparam0(jl_typeof(src))) && + !dest->flags.ptrarray && jl_is_uniontype(jl_tparam0(jl_typeof(dest)))) { + memcpy(dest_p, src_p, n * src->elsize); + memcpy((char*)dest->data + jl_array_len(dest) * dest->elsize, + (char*)src->data + jl_array_len(src) * src->elsize, n); + return; + } assert(dest->flags.ptrarray && src->flags.ptrarray); jl_value_t *owner = jl_array_owner(dest); // Destination is old and doesn't refer to any young object diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 70e65e5bae878..63434b182c0d3 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -1139,35 +1139,6 @@ static Value *emit_bounds_check(jl_codectx_t &ctx, const jl_cgval_t &ainfo, jl_v return im1; } -// --- loading and storing --- - -static Value *compute_box_tindex(Value *datatype, jl_value_t *supertype, jl_value_t *ut, jl_codectx_t *ctx) -{ - Value *tindex = ConstantInt::get(T_int8, 0); - unsigned counter = 0; - for_each_uniontype_small( - [&](unsigned idx, jl_datatype_t *jt) { - if (jl_subtype((jl_value_t*)jt, supertype)) { - Value *cmp = builder.CreateICmpEQ(literal_pointer_val((jl_value_t*)jt), datatype); - tindex = builder.CreateSelect(cmp, ConstantInt::get(T_int8, idx), tindex); - } - }, - ut, - counter); - return tindex; -} - -// get the runtime tindex value -static Value *compute_tindex_unboxed(const jl_cgval_t &val, jl_value_t *typ, jl_codectx_t *ctx) -{ - if (val.constant) - return ConstantInt::get(T_int8, get_box_tindex((jl_datatype_t*)jl_typeof(val.constant), typ)); - if (val.isboxed) - return compute_box_tindex(emit_typeof_boxed(val, ctx), val.typ, typ, ctx); - assert(val.TIndex); - return builder.CreateAnd(val.TIndex, ConstantInt::get(T_int8, 0x7f)); -} - // If given alignment is 0 and LLVM's assumed alignment for a load/store via ptr // might be stricter than the Julia alignment for jltype, return the alignment of jltype. // Otherwise return the given alignment. @@ -1925,7 +1896,32 @@ static Value *_boxed_special(jl_codectx_t &ctx, const jl_cgval_t &vinfo, Type *t return box; } +static Value *compute_box_tindex(jl_codectx_t &ctx, Value *datatype, jl_value_t *supertype, jl_value_t *ut) +{ + Value *tindex = ConstantInt::get(T_int8, 0); + unsigned counter = 0; + for_each_uniontype_small( + [&](unsigned idx, jl_datatype_t *jt) { + if (jl_subtype((jl_value_t*)jt, supertype)) { + Value *cmp = ctx.builder.CreateICmpEQ(maybe_decay_untracked(literal_pointer_val(ctx, (jl_value_t*)jt)), datatype); + tindex = ctx.builder.CreateSelect(cmp, ConstantInt::get(T_int8, idx), tindex); + } + }, + ut, + counter); + return tindex; +} +// get the runtime tindex value +static Value *compute_tindex_unboxed(jl_codectx_t &ctx, const jl_cgval_t &val, jl_value_t *typ) +{ + if (val.constant) + return ConstantInt::get(T_int8, get_box_tindex((jl_datatype_t*)jl_typeof(val.constant), typ)); + if (val.isboxed) + return compute_box_tindex(ctx, emit_typeof_boxed(ctx, val), val.typ, typ); + assert(val.TIndex); + return ctx.builder.CreateAnd(val.TIndex, ConstantInt::get(T_int8, 0x7f)); +} static Value *box_union(jl_codectx_t &ctx, const jl_cgval_t &vinfo, const SmallBitVector &skip) { diff --git a/src/codegen.cpp b/src/codegen.cpp index d18eb96cd8e84..5f21a5d59b3fb 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -236,6 +236,7 @@ static MDNode *tbaa_arraysize; // A size in a jl_array_t static MDNode *tbaa_arraylen; // The len in a jl_array_t static MDNode *tbaa_arrayflags; // The flags in a jl_array_t static MDNode *tbaa_const; // Memory that is immutable by the time LLVM can see it +static MDNode *tbaa_arrayselbyte; // a selector byte in a isbits Union jl_array_t // Basic DITypes static DICompositeType *jl_value_dillvmt; @@ -2400,18 +2401,35 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, jl_value_t *ndp = jl_tparam1(aty_dt); if (!jl_has_free_typevars(ety) && (jl_is_long(ndp) || nargs == 2)) { jl_value_t *ary_ex = jl_exprarg(ex, 1); - if (!jl_array_store_unboxed(ety)) + size_t elsz = 0, al = 0; + bool isboxed = !jl_islayout_inline(ety, &elsz, &al); + if (isboxed) ety = (jl_value_t*)jl_any_type; ssize_t nd = jl_is_long(ndp) ? jl_unbox_long(ndp) : -1; Value *idx = emit_array_nd_index(ctx, ary, ary_ex, nd, &argv[2], nargs - 1); - if (jl_array_store_unboxed(ety) && jl_datatype_size(ety) == 0) { - assert(jl_is_datatype(ety)); + if (!isboxed && jl_is_datatype(ety) && jl_datatype_size(ety) == 0) { assert(((jl_datatype_t*)ety)->instance != NULL); *ret = ghostValue(ety); } + else if (!isboxed && jl_is_uniontype(ety)) { + Value *nbytes = ConstantInt::get(T_size, elsz); + Value *data = emit_bitcast(ctx, emit_arrayptr(ctx, ary, ary_ex), T_pint8); + // isbits union selector bytes are stored directly after the last array element + Value *selidx = ctx.builder.CreateMul(emit_arraylen_prim(ctx, ary), nbytes); + selidx = ctx.builder.CreateAdd(selidx, idx); + Value *ptindex = ctx.builder.CreateGEP(T_int8, data, selidx); + Value *tindex = ctx.builder.CreateNUWAdd(ConstantInt::get(T_int8, 1), tbaa_decorate(tbaa_arrayselbyte, ctx.builder.CreateLoad(T_int8, ptindex))); + Type *AT = ArrayType::get(IntegerType::get(jl_LLVMContext, 8 * al), (elsz + al - 1) / al); + AllocaInst *lv = emit_static_alloca(ctx, AT); + if (al > 1) + lv->setAlignment(al); + Value *elidx = ctx.builder.CreateMul(idx, nbytes); + ctx.builder.CreateMemCpy(lv, ctx.builder.CreateGEP(T_int8, data, elidx), nbytes, al); + *ret = mark_julia_slot(lv, ety, tindex, tbaa_stack); + } else { *ret = typed_load(ctx, emit_arrayptr(ctx, ary, ary_ex), idx, ety, - jl_array_store_unboxed(ety) ? tbaa_arraybuf : tbaa_ptrarraybuf); + !isboxed ? tbaa_arraybuf : tbaa_ptrarraybuf); } return true; } @@ -2434,14 +2452,15 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, jl_value_t *ndp = jl_tparam1(aty_dt); if (!jl_has_free_typevars(ety) && (jl_is_long(ndp) || nargs == 3)) { if (jl_subtype(val.typ, ety)) { // TODO: probably should just convert this to a type-assert - bool isboxed = !jl_array_store_unboxed(ety); + size_t elsz = 0, al = 0; + bool isboxed = !jl_islayout_inline(ety, &elsz, &al); if (isboxed) ety = (jl_value_t*)jl_any_type; jl_value_t *ary_ex = jl_exprarg(ex, 1); ssize_t nd = jl_is_long(ndp) ? jl_unbox_long(ndp) : -1; Value *idx = emit_array_nd_index(ctx, ary, ary_ex, nd, &argv[3], nargs - 2); - if (!isboxed && jl_datatype_size(ety) == 0) { - assert(jl_is_datatype(ety)); + if (!isboxed && jl_is_datatype(ety) && jl_datatype_size(ety) == 0) { + // no-op } else { PHINode *data_owner = NULL; // owner object against which the write barrier must check @@ -2478,15 +2497,40 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, data_owner->addIncoming(aryv, curBB); data_owner->addIncoming(own_ptr, ownedBB); } - typed_store(ctx, - emit_arrayptr(ctx, ary, ary_ex, isboxed), - idx, val, ety, - !isboxed ? tbaa_arraybuf : tbaa_ptrarraybuf, - data_owner, 0, - false); // don't need to root the box if we had to make one since it's being stored in the array immediatly + if (jl_is_uniontype(ety)) { + Value *nbytes = ConstantInt::get(T_size, elsz); + Value *data = emit_bitcast(ctx, emit_arrayptr(ctx, ary, ary_ex), T_pint8); + if (!(val.typ == jl_bottom_type)) { + // compute tindex from val + jl_cgval_t rhs_union = convert_julia_type(ctx, val, ety); + Value *tindex = compute_tindex_unboxed(ctx, rhs_union, ety); + tindex = ctx.builder.CreateNUWSub(tindex, ConstantInt::get(T_int8, 1)); + Value *selidx = ctx.builder.CreateMul(emit_arraylen_prim(ctx, ary), nbytes); + selidx = ctx.builder.CreateAdd(selidx, idx); + Value *ptindex = ctx.builder.CreateGEP(T_int8, data, selidx); + ctx.builder.CreateStore(tindex, ptindex); + if (jl_is_datatype(val.typ) && jl_datatype_size(val.typ) == 0) { + // no-op + } + else { + // copy data + Value *elidx = ctx.builder.CreateMul(idx, nbytes); + Value *addr = ctx.builder.CreateGEP(T_int8, data, elidx); + emit_unionmove(ctx, addr, val, NULL, false, NULL); + } + } + } + else { + typed_store(ctx, + emit_arrayptr(ctx, ary, ary_ex, isboxed), + idx, val, ety, + !isboxed ? tbaa_arraybuf : tbaa_ptrarraybuf, + data_owner, 0, + false); // don't need to root the box if we had to make one since it's being stored in the array immediatly + } + *ret = ary; + return true; } - *ret = ary; - return true; } } } @@ -5881,6 +5925,7 @@ static void init_julia_llvm_meta(void) tbaa_arraylen = tbaa_make_child("jtbaa_arraylen", tbaa_array_scalar).first; tbaa_arrayflags = tbaa_make_child("jtbaa_arrayflags", tbaa_array_scalar).first; tbaa_const = tbaa_make_child("jtbaa_const", nullptr, true).first; + tbaa_arrayselbyte = tbaa_make_child("jtbaa_arrayselbyte", tbaa_array_scalar).first; } static void init_julia_llvm_env(Module *m) diff --git a/src/datatype.c b/src/datatype.c index 0f0dcfbadd57b..c67f55d72080c 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -239,20 +239,20 @@ static int jl_layout_isbits(jl_value_t *ty) return 0; } -static unsigned jl_union_isbits(jl_value_t *ty, size_t *nbytes, size_t *align) +static unsigned union_isbits(jl_value_t *ty, size_t *nbytes, size_t *align) { if (jl_is_uniontype(ty)) { - unsigned na = jl_union_isbits(((jl_uniontype_t*)ty)->a, nbytes, align); + unsigned na = union_isbits(((jl_uniontype_t*)ty)->a, nbytes, align); if (na == 0) return 0; - unsigned nb = jl_union_isbits(((jl_uniontype_t*)ty)->b, nbytes, align); + unsigned nb = union_isbits(((jl_uniontype_t*)ty)->b, nbytes, align); if (nb == 0) return 0; return na + nb; } if (jl_layout_isbits(ty)) { size_t sz = jl_datatype_size(ty); - size_t al = ((jl_datatype_t*)ty)->layout->alignment; + size_t al = jl_datatype_align(ty); if (*nbytes < sz) *nbytes = sz; if (*align < al) @@ -262,6 +262,12 @@ static unsigned jl_union_isbits(jl_value_t *ty, size_t *nbytes, size_t *align) return 0; } +JL_DLLEXPORT int jl_islayout_inline(jl_value_t *eltype, size_t *fsz, size_t *al) +{ + unsigned countbits = union_isbits(eltype, fsz, al); + return countbits > 0 && countbits < 127; +} + void jl_compute_field_offsets(jl_datatype_t *st) { size_t sz = 0, alignm = 1; @@ -326,12 +332,10 @@ void jl_compute_field_offsets(jl_datatype_t *st) for (size_t i = 0; i < nfields; i++) { jl_value_t *ty = jl_field_type(st, i); size_t fsz = 0, al = 0; - unsigned countbits = jl_union_isbits(ty, &fsz, &al); - if (countbits > 0 && countbits < 127) { - // Should never happen + if (jl_islayout_inline(ty, &fsz, &al)) { if (__unlikely(fsz > max_size)) + // Should never happen goto throw_ovf; - al = jl_datatype_align(ty); desc[i].isptr = 0; if (jl_is_uniontype(ty)) { haspadding = 1; diff --git a/src/dump.c b/src/dump.c index 7909a0865f082..f7840eb8042c6 100644 --- a/src/dump.c +++ b/src/dump.c @@ -570,7 +570,8 @@ static void jl_serialize_value_(jl_serializer_state *s, jl_value_t *v, int as_li jl_serialize_value(s, jl_box_long(jl_array_dim(ar,i))); jl_serialize_value(s, jl_typeof(ar)); if (!ar->flags.ptrarray) { - size_t tot = jl_array_len(ar) * ar->elsize; + size_t extra = jl_is_uniontype(jl_tparam0(jl_typeof(ar))) ? jl_array_len(ar) : 0; + size_t tot = jl_array_len(ar) * ar->elsize + extra; ios_write(s->s, (char*)jl_array_data(ar), tot); } else { @@ -1346,7 +1347,8 @@ static jl_value_t *jl_deserialize_value_array(jl_serializer_state *s, jl_value_t jl_value_t *aty = jl_deserialize_value(s, &jl_astaggedvalue(a)->type); jl_set_typeof(a, aty); if (!a->flags.ptrarray) { - size_t tot = jl_array_len(a) * a->elsize; + size_t extra = jl_is_uniontype(jl_tparam0(aty)) ? jl_array_len(a) : 0; + size_t tot = jl_array_len(a) * a->elsize + extra; ios_read(s->s, (char*)jl_array_data(a), tot); } else { diff --git a/src/gc.c b/src/gc.c index 3e59fdeb534d0..0a5d051dc0f19 100644 --- a/src/gc.c +++ b/src/gc.c @@ -857,6 +857,9 @@ static size_t array_nbytes(jl_array_t *a) sz = a->elsize * a->maxsize + (a->elsize == 1 ? 1 : 0); else sz = a->elsize * jl_array_len(a); + if (!a->flags.ptrarray && jl_is_uniontype(jl_tparam0(jl_typeof(a)))) + // account for isbits Union array selector bytes + sz += jl_array_len(a); return sz; } diff --git a/src/julia.h b/src/julia.h index c35e0b2273719..65ad583b02d45 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1180,6 +1180,7 @@ JL_DLLEXPORT void jl_set_nth_field(jl_value_t *v, size_t i, JL_DLLEXPORT int jl_field_isdefined(jl_value_t *v, size_t i); JL_DLLEXPORT jl_value_t *jl_get_field(jl_value_t *o, const char *fld); JL_DLLEXPORT jl_value_t *jl_value_ptr(jl_value_t *a); +JL_DLLEXPORT int jl_islayout_inline(jl_value_t *eltype, size_t *fsz, size_t *al); // arrays JL_DLLEXPORT jl_array_t *jl_new_array(jl_value_t *atype, jl_value_t *dims); diff --git a/src/staticdata.c b/src/staticdata.c index 499378aa62404..d08f5d0ee6bdb 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -589,7 +589,8 @@ static void jl_write_values(jl_serializer_state *s) // make some header modifications in-place jl_array_t *newa = (jl_array_t*)&s->s->buf[reloc_offset]; size_t alen = jl_array_len(ar); - size_t tot = alen * ar->elsize; + size_t extra = (!ar->flags.ptrarray && jl_is_uniontype(jl_tparam0(jl_typeof(ar)))) ? alen : 0; + size_t tot = alen * ar->elsize + extra; if (newa->flags.ndims == 1) newa->maxsize = alen; newa->offset = 0; From 88341b99345a708c6b74d7d7355a5f0afa9dcf95 Mon Sep 17 00:00:00 2001 From: quinnj Date: Tue, 11 Jul 2017 07:24:47 -0600 Subject: [PATCH 041/324] Add tests for isbits union optimizations --- test/core.jl | 231 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 231 insertions(+) diff --git a/test/core.jl b/test/core.jl index 81704008c6b7f..0d3ed00af6af3 100644 --- a/test/core.jl +++ b/test/core.jl @@ -5181,3 +5181,234 @@ let idx = (7,5,9) (v,) = (idx...,) @test v == 7 end + +module UnionOptimizations + +using Base.Test + +const boxedunions = [Union{}, Union{String, Void}] +const unboxedunions = [Union{Int8, Void}, Union{Int8, Float16, Void}, + Union{Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128}, + Union{Char, Date, Int}] + +@test !Base.isbitsunion(boxedunions[1]) +@test !Base.isbitsunion(boxedunions[2]) +@test Base.isbitsunion(unboxedunions[1]) +@test Base.isbitsunion(unboxedunions[2]) +@test Base.isbitsunion(unboxedunions[3]) + +@test Base.bitsunionsize(unboxedunions[1]) == 1 +@test Base.bitsunionsize(unboxedunions[2]) == 2 +@test Base.bitsunionsize(unboxedunions[3]) == 16 +@test Base.bitsunionsize(unboxedunions[4]) == 8 + +initvalue(::Type{Void}) = nothing +initvalue(::Type{Char}) = '\0' +initvalue(::Type{Date}) = Date(0, 12, 31) +initvalue(::Type{T}) where {T <: Number} = T(0) + +initvalue2(::Type{Void}) = nothing +initvalue2(::Type{Char}) = Char(0x01) +initvalue2(::Type{Date}) = Date(1) +initvalue2(::Type{T}) where {T <: Number} = T(1) + +U = unboxedunions[1] + +mutable struct UnionField + u::U +end + +x = UnionField(initvalue(Base.uniontypes(U)[1])) +@test x.u === initvalue(Base.uniontypes(U)[1]) +x.u = initvalue2(Base.uniontypes(U)[1]) +@test x.u === initvalue2(Base.uniontypes(U)[1]) +x.u = initvalue(Base.uniontypes(U)[2]) +@test x.u === initvalue(Base.uniontypes(U)[2]) + +for U in boxedunions + for N in (1, 2, 3, 4) + A = Array{U}(ntuple(x->0, N)...) + @test isempty(A) + @test Core.sizeof(A) == 0 + + A = Array{U}(ntuple(x->10, N)...) + @test length(A) == 10^N + @test Core.sizeof(A) == sizeof(Int) * (10^N) + @test !isassigned(A, 1) + end +end + +# unsafe_wrap +A4 = [1, 2, 3] +@test_throws ArgumentError unsafe_wrap(Array, convert(Ptr{Union{Int, Void}}, pointer(A4)), 3) +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 + for N in (1, 2, 3, 4) + A = Array{U}(ntuple(x->0, N)...) + @test isempty(A) + @test Core.sizeof(A) == 0 + + len = ntuple(x->10, N) + mxsz = maximum(sizeof, Base.uniontypes(U)) + A = Array{U}(len) + @test length(A) == prod(len) + @test Core.sizeof(A) == prod(len) * mxsz + @test isassigned(A, 1) + @test isassigned(A, length(A)) + + # arrayref / arrayset + F = Base.uniontypes(U)[1] + @test A[1] === initvalue(F) + A[1] = initvalue2(F) + @test A[1] === initvalue2(F) + + F2 = Base.uniontypes(U)[2] + A[2] = initvalue(F2) + @test A[2] === initvalue(F2) + + for (i, U2) in enumerate(Base.uniontypes(U)) + A[i] = initvalue2(U2) + @test A[i] === initvalue2(U2) + end + + # serialize / deserialize + io = IOBuffer() + serialize(io, A) + seekstart(io) + A2 = deserialize(io) + @test A == A2 + + # reshape + A3 = reshape(A, (div(prod(len), 2), 2)) + @test Core.sizeof(A) == prod(len) * mxsz + @test isassigned(A, 1) + @test A[1] === initvalue2(F) + + # copy + A4 = copy(A) + @test A == A4 + + if N == 1 + ## Dequeue functions + # pop! + F2 = Base.uniontypes(U)[2] + len = len[1] + A = U[initvalue2(F2) for i = 1:len] + for i = 1:len + @test A[end] === initvalue2(F2) + v = pop!(A) + @test v === initvalue2(F2) + end + @test isempty(A) + + # shift! + A = U[initvalue2(F2) for i = 1:len] + for i = 1:len + @test A[1] === initvalue2(F2) + shift!(A) + end + @test isempty(A) + + # empty! + A = U[initvalue2(F2) for i = 1:len] + empty!(A) + @test isempty(A) + + # resize! + A = U[initvalue2(F2) for i = 1:len] + resize!(A, 1) + @test length(A) === 1 + @test A[1] === initvalue2(F2) + resize!(A, len) + @test length(A) === len + @test A[1] === initvalue2(F2) + @test typeof(A[end]) === F + + # deleteat! + F = Base.uniontypes(U)[2] + A = U[rand(F(1):F(len)) for i = 1:len] + deleteat!(A, map(Int, sort!(unique(A[1:4])))) + A = U[initvalue2(F2) for i = 1:len] + deleteat!(A, 1:2) + @test length(A) == len - 2 + @test all(A .== initvalue2(F2)) + deleteat!(A, 1:2) + @test length(A) == len - 4 + @test all(A .== initvalue2(F2)) + A = U[initvalue2(F2) for i = 1:len] + deleteat!(A, length(A)-1:length(A)) + @test length(A) == len - 2 + @test all(A .== initvalue2(F2)) + deleteat!(A, length(A)-1:length(A)) + @test length(A) == len - 4 + @test all(A .== initvalue2(F2)) + A = U[initvalue2(F2) for i = 1:len] + deleteat!(A, 2:3) + @test length(A) == len - 2 + @test all(A .== initvalue2(F2)) + A = U[initvalue2(F2) for i = 1:len] + deleteat!(A, length(A)-2:length(A)-1) + @test length(A) == len - 2 + @test all(A .== initvalue2(F2)) + + # unshift! + A = U[initvalue2(F2) for i = 1:len] + for i = 1:5 + unshift!(A, initvalue2(F)) + unshift!(A, initvalue(F2)) + @test A[1] === initvalue(F2) + @test A[2] === initvalue2(F) + end + + # push! / append! / prepend! + A = U[initvalue2(F2) for i = 1:len] + push!(A, initvalue2(F)) + @test A[end] === initvalue2(F) + push!(A, initvalue2(F2)) + @test A[end] === initvalue2(F2) + append!(A, [initvalue(F), initvalue2(F)]) + @test A[end] === initvalue2(F) + @test A[end-1] === initvalue(F) + prepend!(A, [initvalue(F), initvalue2(F)]) + @test A[2] === initvalue2(F) + @test A[1] === initvalue(F) + + # insert! + A = U[initvalue2(F2) for i = 1:len] + insert!(A, 2, initvalue2(F)) + @test A[2] === initvalue2(F) + @test A[1] === initvalue2(F2) + @test A[3] === initvalue2(F2) + @test A[end] === initvalue2(F2) + A = U[initvalue2(F2) for i = 1:len] + insert!(A, 8, initvalue2(F)) + @test A[8] === initvalue2(F) + @test A[7] === initvalue2(F2) + @test A[9] === initvalue2(F2) + @test A[end] === initvalue2(F2) + + # splice! + A = U[initvalue2(F2) for i = 1:len] + V = splice!(A, 1:2) + @test length(A) == len - 2 + @test length(V) == 2 + @test V[1] == initvalue2(F2) + @test V[2] == initvalue2(F2) + @test A[1] == initvalue2(F2) + @test A[end] == initvalue2(F2) + + A = U[initvalue2(F2) for i = 1:len] + V = splice!(A, 4:5) + @test length(A) == len - 2 + @test length(V) == 2 + @test V[1] == initvalue2(F2) + @test V[2] == initvalue2(F2) + @test A[1] == initvalue2(F2) + @test A[end] == initvalue2(F2) + end + end +end + +end # module UnionOptimizations From 3b51323fdd489e463f0f88559da84a77d027bcec Mon Sep 17 00:00:00 2001 From: Tony Kelman Date: Tue, 15 Aug 2017 13:49:35 -0700 Subject: [PATCH 042/324] Evaluate Circle CI (23171 but from a branch in JuliaLang) (#23188) This runs in circleci's python 2.7 docker image which is based on debian jessie, mostly for convenience since that image has a non-root user added and some of the Julia tests work differently when run as root. Circle CI caching is a little odd - I'm using ccache here since it's actually fast enough to handle it pretty well, about 15 minutes to build deps from scratch and about 3 minutes to build deps with a primed ccache. We save the ARCH, HOME (default location where ccache saves its data, in case we switch containers and try to run tests as a different user), and year and week number into a tag file, then determine which saved circleci cache tag to load from based on the checksum of that tag file. So this will throw out the cache and have to rebuild it once a week, but since that only adds about 10-15 minutes to a build that's not a big deal. It would be better if circle's cache worked like Travis and AppVeyor's where it loads the latest and checksums the content to decide whether to upload a new replacement cache, but that's up to circle to fix. This has to skip the socket tests, since circle doesn't currently support ipv6 in their docker workers. They have other types of workers, but they aren't anywhere near as fast to build or run tests. This currently doesn't do a doc build or deployment, that's a TODO if we decide to switch to this. It also looks like it checks out the branch tip instead of the merge commit, but that should be fixable. Here I've set JULIA_CPU_CORES to 6 - in some testing on my fork this was a few minutes faster, but may run a higher risk of getting OOM killed. The merge commit is checked out manually due to Circle limitations. --- circle.yml | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 circle.yml diff --git a/circle.yml b/circle.yml new file mode 100644 index 0000000000000..c520f272981ce --- /dev/null +++ b/circle.yml @@ -0,0 +1,80 @@ +version: 2 +workflows: + version: 2 + linux-builds: + jobs: + - build-i686 + - build-x86_64 + +jobs: + build-i686: + docker: + - image: circleci/python:2.7 + environment: + JULIA_CPU_CORES: 6 + JULIA_TEST_MAXRSS_MB: 800 + ARCH: i686 + steps: &steps + - run: | # install build dependencies + sudo apt-get install -y g++-4.8-multilib gfortran-4.8-multilib \ + time ccache bar && + for prog in gcc g++ gfortran; do + sudo ln -s /usr/bin/$prog-4.8 /usr/local/bin/$prog; + done + - checkout # circle ci code checkout step +# (FIXME: need to unset url."ssh://git@github.com".insteadOf or libgit2 tests fail) + - run: | # checkout merge commit, set versioning info and Make.user variables + git config --global --unset url."ssh://git@github.com".insteadOf && + if [ -n "$CIRCLE_PULL_REQUEST" ]; then + git fetch origin +refs/pull/$(basename $CIRCLE_PULL_REQUEST)/merge && + git checkout -qf FETCH_HEAD; + fi && + make -C base version_git.jl.phony && + echo "override ARCH = $ARCH" | tee -a Make.user && + for var in FORCE_ASSERTIONS LLVM_ASSERTIONS USECCACHE NO_GIT; do + echo "override $var = 1" | tee -a Make.user; + done && + echo "$ARCH $HOME $(date +%Y%W)" | tee /tmp/weeknumber + - restore_cache: # silly to take a checksum of the tag file here instead of + keys: # its contents but this is the only thing that works on circle + - ccache-{{ checksum "/tmp/weeknumber" }} + - run: | # compile julia deps + contrib/download_cmake.sh && + make -j8 -C deps || make + - run: | # build julia, output ccache stats when done + make -j8 all && + make prefix=/tmp/julia install && + ccache -s && + make build-stats + - run: | # move source tree out of the way, run tests from install tree + cd .. && + mv project julia-src && + /tmp/julia/bin/julia -e 'versioninfo()' && + /tmp/julia/bin/julia --precompiled=no -e 'true' && + /tmp/julia/bin/julia-debug --precompiled=no -e 'true' && + pushd /tmp/julia/share/julia/test && + /tmp/julia/bin/julia --check-bounds=yes runtests.jl all --skip socket | bar -i 30 && + /tmp/julia/bin/julia --check-bounds=yes runtests.jl libgit2-online download pkg && + popd && + mkdir /tmp/embedding-test && + make check -C /tmp/julia/share/doc/julia/examples/embedding \ + JULIA=/tmp/julia/bin/julia BIN=/tmp/embedding-test \ + "$(cd julia-src && make print-CC)" && + mv julia-src project +# - run: cd project && make -C doc deploy +# - run: +# command: sudo dmesg +# when: on_fail + - save_cache: + key: ccache-{{ checksum "/tmp/weeknumber" }} + paths: + - ~/.ccache + + build-x86_64: + docker: + - image: circleci/python:2.7 + environment: + JULIA_CPU_CORES: 6 + JULIA_TEST_MAXRSS_MB: 800 + ARCH: x86_64 + steps: *steps From 5ef2ca966f595d73136007d912c110ffed3bf280 Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Sun, 6 Aug 2017 18:05:58 -0500 Subject: [PATCH 043/324] Add minimal CredentialPayload implementation LibGit2 credential callback now always uses a CredentialPayload struct which allows us keep callback state separate from credentials. Implementation at this point is minimal to allow us to more thoroughly test the existing behaviour. --- base/libgit2/callbacks.jl | 32 ++++-- base/libgit2/types.jl | 38 ++++++- test/libgit2-helpers.jl | 35 ++++--- test/libgit2.jl | 207 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 285 insertions(+), 27 deletions(-) diff --git a/base/libgit2/callbacks.jl b/base/libgit2/callbacks.jl index 74fb15bd17946..8e79db06ef4ea 100644 --- a/base/libgit2/callbacks.jl +++ b/base/libgit2/callbacks.jl @@ -48,8 +48,9 @@ function user_abort() return Cint(Error.EAUTH) end -function authenticate_ssh(creds::SSHCredentials, libgit2credptr::Ptr{Ptr{Void}}, +function authenticate_ssh(libgit2credptr::Ptr{Ptr{Void}}, p::CredentialPayload, username_ptr, schema, host) + creds = Base.get(p.credential)::SSHCredentials isusedcreds = checkused!(creds) # Note: The same SSHCredentials can be used to authenticate separate requests using the @@ -167,8 +168,9 @@ function authenticate_ssh(creds::SSHCredentials, libgit2credptr::Ptr{Ptr{Void}}, libgit2credptr, creds.user, creds.pubkey, creds.prvkey, creds.pass) end -function authenticate_userpass(creds::UserPasswordCredentials, libgit2credptr::Ptr{Ptr{Void}}, +function authenticate_userpass(libgit2credptr::Ptr{Ptr{Void}}, p::CredentialPayload, schema, host, urlusername) + creds = Base.get(p.credential)::UserPasswordCredentials isusedcreds = checkused!(creds) if creds.prompt_if_incorrect @@ -211,7 +213,7 @@ end """Credentials callback function Function provides different credential acquisition functionality w.r.t. a connection protocol. -If a payload is provided then `payload_ptr` should contain a `LibGit2.AbstractCredentials` object. +If a payload is provided then `payload_ptr` should contain a `LibGit2.CredentialPayload` object. For `LibGit2.Consts.CREDTYPE_USERPASS_PLAINTEXT` type, if the payload contains fields: `user` & `pass`, they are used to create authentication credentials. @@ -240,21 +242,30 @@ function credentials_callback(libgit2credptr::Ptr{Ptr{Void}}, url_ptr::Cstring, err = Cint(0) url = unsafe_string(url_ptr) + # get `CredentialPayload` object from payload pointer + @assert payload_ptr != C_NULL + p = unsafe_pointer_to_objref(payload_ptr)[]::CredentialPayload + # parse url for schema and host urlparts = match(URL_REGEX, url) schema = urlparts[:scheme] === nothing ? "" : urlparts[:scheme] urlusername = urlparts[:user] === nothing ? "" : urlparts[:user] host = urlparts[:host] - # get credentials object from payload pointer - @assert payload_ptr != C_NULL - creds = unsafe_pointer_to_objref(payload_ptr) - explicit = !isnull(creds[]) && !isa(Base.get(creds[]), CachedCredentials) + if !isnull(p.credential) + creds = unsafe_get(p.credential) + explicit = true + else + creds = Base.get(p.cache, nothing) + explicit = false + end + # use ssh key or ssh-agent if isset(allowed_types, Cuint(Consts.CREDTYPE_SSH_KEY)) sshcreds = get_creds!(creds, "ssh://$host", reset!(SSHCredentials(true), -1)) if isa(sshcreds, SSHCredentials) - err = authenticate_ssh(sshcreds, libgit2credptr, username_ptr, schema, host) + p.credential = Nullable(sshcreds) + err = authenticate_ssh(libgit2credptr, p, username_ptr, schema, host) err == 0 && return err end end @@ -268,9 +279,10 @@ function credentials_callback(libgit2credptr::Ptr{Ptr{Void}}, url_ptr::Cstring, # credentials if !isa(upcreds, UserPasswordCredentials) upcreds = defaultcreds - isa(Base.get(creds[]), CachedCredentials) && (Base.get(creds[]).creds[credid] = upcreds) + isa(creds, CachedCredentials) && (creds.creds[credid] = upcreds) end - return authenticate_userpass(upcreds, libgit2credptr, schema, host, urlusername) + p.credential = Nullable(upcreds) + return authenticate_userpass(libgit2credptr, p, schema, host, urlusername) end # No authentication method we support succeeded. The most likely cause is diff --git a/base/libgit2/types.jl b/base/libgit2/types.jl index 65a6cbc65ff60..c223ef8416146 100644 --- a/base/libgit2/types.jl +++ b/base/libgit2/types.jl @@ -161,6 +161,8 @@ Matches the [`git_checkout_options`](https://libgit2.github.com/libgit2/#HEAD/ty perfdata_payload::Ptr{Void} end +abstract type Payload end + """ LibGit2.RemoteCallbacks @@ -183,12 +185,16 @@ Matches the [`git_remote_callbacks`](https://libgit2.github.com/libgit2/#HEAD/ty payload::Ptr{Void} end -function RemoteCallbacks(credentials::Ptr{Void}, payload::Ref{Nullable{AbstractCredentials}}) - RemoteCallbacks(credentials=credentials, payload=pointer_from_objref(payload)) +function RemoteCallbacks(credentials_cb::Ptr{Void}, payload::Ref{<:Payload}) + RemoteCallbacks(credentials=credentials_cb, payload=pointer_from_objref(payload)) +end + +function RemoteCallbacks(credentials_cb::Ptr{Void}, payload::Payload) + RemoteCallbacks(credentials_cb, Ref(payload)) end -function RemoteCallbacks(credentials::Ptr{Void}, payload::Nullable{<:AbstractCredentials}) - RemoteCallbacks(credentials, Ref{Nullable{AbstractCredentials}}(payload)) +function RemoteCallbacks(credentials_cb::Ptr{Void}, credentials) + RemoteCallbacks(credentials_cb, CredentialPayload(credentials)) end """ @@ -920,3 +926,27 @@ function securezero!(p::CachedCredentials) foreach(securezero!, values(p.cred)) return p end + +""" + LibGit2.CredentialPayload + +Retains state between multiple calls to the credential callback. A single +`CredentialPayload` instance will be used when authentication fails for a URL but different +instances will be used when the URL has changed. +""" +mutable struct CredentialPayload <: Payload + credential::Nullable{AbstractCredentials} + cache::Nullable{CachedCredentials} +end + +function CredentialPayload(credential::Nullable{<:AbstractCredentials}) + CredentialPayload(credential, Nullable{CachedCredentials}()) +end + +function CredentialPayload(cache::Nullable{CachedCredentials}) + CredentialPayload(Nullable{AbstractCredentials}(), cache) +end + +function CredentialPayload() + CredentialPayload(Nullable{AbstractCredentials}(), Nullable{CachedCredentials}()) +end diff --git a/test/libgit2-helpers.jl b/test/libgit2-helpers.jl index 5b02c4346cf8d..13e5f163569a1 100644 --- a/test/libgit2-helpers.jl +++ b/test/libgit2-helpers.jl @@ -1,6 +1,7 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -import Base.LibGit2: AbstractCredentials, UserPasswordCredentials, SSHCredentials, CachedCredentials +import Base.LibGit2: AbstractCredentials, UserPasswordCredentials, SSHCredentials, + CachedCredentials, CredentialPayload, Payload """ Emulates the LibGit2 credential loop to allows testing of the credential_callback function @@ -11,10 +12,10 @@ function credential_loop( url::AbstractString, user::Nullable{<:AbstractString}, allowed_types::UInt32, - cache::CachedCredentials=CachedCredentials()) + payload::CredentialPayload) cb = Base.LibGit2.credentials_cb() libgitcred_ptr_ptr = Ref{Ptr{Void}}(C_NULL) - payload_ptr = Ref(Nullable{AbstractCredentials}(cache)) + payload_ptr = Ref(payload) # Number of times credentials were authenticated against. With the real LibGit2 # credential loop this would be how many times we sent credentials to the remote. @@ -29,7 +30,7 @@ function credential_loop( num_authentications += 1 # Check if the callback provided us with valid credentials - if length(cache.cred) == 1 && first(values(cache.cred)) == valid_credential + if !isnull(payload.credential) && get(payload.credential) == valid_credential break end @@ -44,38 +45,46 @@ end function credential_loop( valid_credential::UserPasswordCredentials, url::AbstractString, - user::Nullable{<:AbstractString}=Nullable{String}()) - credential_loop(valid_credential, url, user, 0x000001) + user::Nullable{<:AbstractString}=Nullable{String}(), + payload::CredentialPayload=CredentialPayload()) + credential_loop(valid_credential, url, user, 0x000001, payload) end function credential_loop( valid_credential::SSHCredentials, url::AbstractString, - user::Nullable{<:AbstractString}=Nullable{String}(); + user::Nullable{<:AbstractString}=Nullable{String}(), + payload::CredentialPayload=CredentialPayload(); use_ssh_agent::Bool=false) - cache = CachedCredentials() if !use_ssh_agent + if isnull(payload.cache) + payload.cache = Nullable(CachedCredentials()) + end + cache = get(payload.cache) + m = match(LibGit2.URL_REGEX, url) default_cred = LibGit2.reset!(SSHCredentials(true), -1) default_cred.usesshagent = "N" LibGit2.get_creds!(cache, "ssh://$(m[:host])", default_cred) end - credential_loop(valid_credential, url, user, 0x000046, cache) + credential_loop(valid_credential, url, user, 0x000046, payload) end function credential_loop( valid_credential::UserPasswordCredentials, url::AbstractString, - user::AbstractString) - credential_loop(valid_credential, url, Nullable(user)) + user::AbstractString, + payload::CredentialPayload=CredentialPayload()) + credential_loop(valid_credential, url, Nullable(user), payload) end function credential_loop( valid_credential::SSHCredentials, url::AbstractString, - user::AbstractString; + user::AbstractString, + payload::CredentialPayload=CredentialPayload(); use_ssh_agent::Bool=false) - credential_loop(valid_credential, url, Nullable(user), use_ssh_agent=use_ssh_agent) + credential_loop(valid_credential, url, Nullable(user), payload, use_ssh_agent=use_ssh_agent) end diff --git a/test/libgit2.jl b/test/libgit2.jl index e63f989a7a7d4..d5a3ba483bf27 100644 --- a/test/libgit2.jl +++ b/test/libgit2.jl @@ -1613,6 +1613,10 @@ mktempdir() do dir LibGit2.Error.Callback, LibGit2.Error.EAUTH, "Aborting, user cancelled credential request.") + eauth_error = LibGit2.GitError( + LibGit2.Error.None, LibGit2.Error.EAUTH, + "No errors") + @testset "SSH credential prompt" begin url = "git@github.com:test/package.jl" @@ -1896,6 +1900,209 @@ mktempdir() do dir @test err == 0 @test auth_attempts == 5 end + + @testset "SSH explicit credentials" begin + url = "git@github.com:test/package.jl" + + invalid_key = joinpath(KEY_DIR, "invalid") + valid_p_key = joinpath(KEY_DIR, "valid-passphrase") + username = "git" + passphrase = "secret" + + valid_cmd = """ + include("$LIBGIT2_HELPER_PATH") + valid_cred = LibGit2.SSHCredentials("$username", "$passphrase", "$valid_p_key", "$valid_p_key.pub") + payload = CredentialPayload(Nullable(valid_cred)) + err, auth_attempts = credential_loop(valid_cred, "$url", "$username", payload) + (err < 0 ? LibGit2.GitError(err) : err, auth_attempts) + """ + + invalid_cmd = """ + include("$LIBGIT2_HELPER_PATH") + valid_cred = LibGit2.SSHCredentials("$username", "$passphrase", "$valid_p_key", "$valid_p_key.pub") + invalid_cred = LibGit2.SSHCredentials("$username", "", "$invalid_key", "$invalid_key.pub") + payload = CredentialPayload(Nullable(invalid_cred)) + err, auth_attempts = credential_loop(valid_cred, "$url", "$username", payload) + (err < 0 ? LibGit2.GitError(err) : err, auth_attempts) + """ + + # Explicitly provided credential is correct + err, auth_attempts = challenge_prompt(valid_cmd, []) + @test err == 0 + @test auth_attempts == 1 + + # TODO: Currently infinite loops + # Explicitly provided credential is incorrect + #= + err, auth_attempts = challenge_prompt(invalid_cmd, []) + @test err == 0 + @test auth_attempts == 1 + =# + end + + @testset "HTTPS explicit credentials" begin + url = "https://github.com/test/package.jl" + + valid_username = "julia" + valid_password = randstring(16) + invalid_username = "alice" + invalid_password = randstring(15) + + valid_cmd = """ + include("$LIBGIT2_HELPER_PATH") + valid_cred = LibGit2.UserPasswordCredentials("$valid_username", "$valid_password") + payload = CredentialPayload(Nullable(valid_cred)) + err, auth_attempts = credential_loop(valid_cred, "$url", "", payload) + (err < 0 ? LibGit2.GitError(err) : err, auth_attempts) + """ + + invalid_cmd = """ + include("$LIBGIT2_HELPER_PATH") + valid_cred = LibGit2.UserPasswordCredentials("$valid_username", "$valid_password") + invalid_cred = LibGit2.UserPasswordCredentials("$invalid_username", "$invalid_password") + payload = CredentialPayload(Nullable(invalid_cred)) + err, auth_attempts = credential_loop(valid_cred, "$url", "", payload) + (err < 0 ? LibGit2.GitError(err) : err, auth_attempts) + """ + + # Explicitly provided credential is correct + err, auth_attempts = challenge_prompt(valid_cmd, []) + @test err == 0 + @test auth_attempts == 1 + + # Explicitly provided credential is incorrect + err, auth_attempts = challenge_prompt(invalid_cmd, []) + @test err == eauth_error + @test auth_attempts == 4 + end + + @testset "Cached credentials" begin + url = "https://github.com/test/package.jl" + cred_id = "https://github.com" + + valid_username = "julia" + valid_password = randstring(16) + invalid_username = "alice" + invalid_password = randstring(15) + + valid_cmd = """ + include("$LIBGIT2_HELPER_PATH") + valid_cred = LibGit2.UserPasswordCredentials("$valid_username", "$valid_password") + cache = CachedCredentials() + LibGit2.get_creds!(cache, "$cred_id", valid_cred) + payload = CredentialPayload(Nullable(cache)) + err, auth_attempts = credential_loop(valid_cred, "$url", "", payload) + (err < 0 ? LibGit2.GitError(err) : err, auth_attempts) + """ + + add_cmd = """ + include("$LIBGIT2_HELPER_PATH") + valid_cred = LibGit2.UserPasswordCredentials("$valid_username", "$valid_password") + cache = CachedCredentials() + payload = CredentialPayload(Nullable(cache)) + err, auth_attempts = credential_loop(valid_cred, "$url", "", payload) + (err < 0 ? LibGit2.GitError(err) : err, auth_attempts, cache) + """ + + replace_cmd = """ + include("$LIBGIT2_HELPER_PATH") + valid_cred = LibGit2.UserPasswordCredentials("$valid_username", "$valid_password") + invalid_cred = LibGit2.UserPasswordCredentials("$invalid_username", "$invalid_password", true) + cache = CachedCredentials() + LibGit2.get_creds!(cache, "$cred_id", invalid_cred) + payload = CredentialPayload(Nullable(cache)) + err, auth_attempts = credential_loop(valid_cred, "$url", "", payload) + (err < 0 ? LibGit2.GitError(err) : err, auth_attempts, cache) + """ + + # Cache contains a correct credential + err, auth_attempts = challenge_prompt(valid_cmd, []) + @test err == 0 + @test auth_attempts == 1 + + # Add a credential into the cache + challenges = [ + "Username for 'https://github.com':" => "$valid_username\n", + "Password for 'https://$valid_username@github.com':" => "$valid_password\n", + ] + expected_cred = LibGit2.UserPasswordCredentials(valid_username, valid_password) + err, auth_attempts, cache = challenge_prompt(add_cmd, challenges) + @test err == 0 + @test auth_attempts == 1 + @test typeof(cache) == LibGit2.CachedCredentials + @test cache.cred == Dict(cred_id => expected_cred) + + # Replace a credential in the cache + challenges = [ + "Username for 'https://github.com' [alice]:" => "$valid_username\n", + "Password for 'https://$valid_username@github.com':" => "$valid_password\n", + ] + expected_cred = LibGit2.UserPasswordCredentials(valid_username, valid_password) + err, auth_attempts, cache = challenge_prompt(replace_cmd, challenges) + @test err == 0 + @test auth_attempts == 4 + @test typeof(cache) == LibGit2.CachedCredentials + @test cache.cred == Dict(cred_id => expected_cred) + end + + @testset "Incompatible explicit credentials" begin + # User provides a user/password credential where a SSH credential is required. + ssh_cmd = """ + include("$LIBGIT2_HELPER_PATH") + valid_cred = LibGit2.UserPasswordCredentials("foo", "bar") + payload = CredentialPayload(Nullable(valid_cred)) + err, auth_attempts = credential_loop(valid_cred, "ssh://github.com/repo", + Nullable(""), Cuint(LibGit2.Consts.CREDTYPE_SSH_KEY), payload) + (err < 0 ? LibGit2.GitError(err) : err, auth_attempts) + """ + + # User provides a SSH credential where a user/password credential is required. + https_cmd = """ + include("$LIBGIT2_HELPER_PATH") + valid_cred = LibGit2.SSHCredentials("foo", "", "", "") + payload = CredentialPayload(Nullable(valid_cred)) + err, auth_attempts = credential_loop(valid_cred, "https://github.com/repo", + Nullable(""), Cuint(LibGit2.Consts.CREDTYPE_USERPASS_PLAINTEXT), payload) + (err < 0 ? LibGit2.GitError(err) : err, auth_attempts) + """ + + # TODO: Currently a warning is reported about the explicit credential being + # incompatible with the authentication method. We should change this to an + # error. + err, auth_attempts = challenge_prompt(ssh_cmd, []) + @test err == eauth_error + @test auth_attempts == 1 + + # TODO: Providing an explicit SSH credential which is incompatible with the + # authentication method triggers a prompt... + challenges = [ + "Username for 'https://github.com':" => "\x04", + ] + err, auth_attempts = challenge_prompt(https_cmd, challenges) + @test err == abort_prompt + @test auth_attempts == 1 + end + + # A hypothetical scenario where the the allowed authentication can either be + # SSH or username/password. + @testset "SSH & HTTPS authentication" begin + allowed_types = Cuint(LibGit2.Consts.CREDTYPE_SSH_KEY) | + Cuint(LibGit2.Consts.CREDTYPE_USERPASS_PLAINTEXT) + + # User provides a user/password credential where a SSH credential is required. + cmd = """ + include("$LIBGIT2_HELPER_PATH") + valid_cred = LibGit2.UserPasswordCredentials("foo", "bar") + payload = CredentialPayload(Nullable(valid_cred)) + err, auth_attempts = credential_loop(valid_cred, "foo://github.com/repo", + Nullable(""), Cuint($allowed_types), payload) + (err < 0 ? LibGit2.GitError(err) : err, auth_attempts) + """ + + err, auth_attempts = challenge_prompt(cmd, []) + @test err == 0 + @test auth_attempts == 1 + end end #= temporarily disabled until working on the buildbots, ref https://github.com/JuliaLang/julia/pull/17651#issuecomment-238211150 From cebf09ae74016cd158bb7be93afd1a9d3b15debb Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Fri, 11 Aug 2017 23:45:39 -0500 Subject: [PATCH 044/324] Correct SSH explicit test --- test/libgit2.jl | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/test/libgit2.jl b/test/libgit2.jl index d5a3ba483bf27..ede2ba1445822 100644 --- a/test/libgit2.jl +++ b/test/libgit2.jl @@ -1921,6 +1921,7 @@ mktempdir() do dir include("$LIBGIT2_HELPER_PATH") valid_cred = LibGit2.SSHCredentials("$username", "$passphrase", "$valid_p_key", "$valid_p_key.pub") invalid_cred = LibGit2.SSHCredentials("$username", "", "$invalid_key", "$invalid_key.pub") + invalid_cred.usesshagent = "N" # Disable SSH agent use payload = CredentialPayload(Nullable(invalid_cred)) err, auth_attempts = credential_loop(valid_cred, "$url", "$username", payload) (err < 0 ? LibGit2.GitError(err) : err, auth_attempts) @@ -1931,13 +1932,11 @@ mktempdir() do dir @test err == 0 @test auth_attempts == 1 - # TODO: Currently infinite loops # Explicitly provided credential is incorrect - #= + # TODO: Unless the SSH agent is disabled we may get caught in an infinite loop err, auth_attempts = challenge_prompt(invalid_cmd, []) - @test err == 0 - @test auth_attempts == 1 - =# + @test err == eauth_error + @test auth_attempts == 4 end @testset "HTTPS explicit credentials" begin From ecd4285c13557d4394074e2fee010d6791f89097 Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Fri, 11 Aug 2017 09:23:52 -0500 Subject: [PATCH 045/324] Error on incompatible explicit credential --- base/libgit2/callbacks.jl | 7 ++++--- test/libgit2.jl | 18 ++++++++++-------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/base/libgit2/callbacks.jl b/base/libgit2/callbacks.jl index 8e79db06ef4ea..e4e3a59b899a7 100644 --- a/base/libgit2/callbacks.jl +++ b/base/libgit2/callbacks.jl @@ -287,11 +287,12 @@ function credentials_callback(libgit2credptr::Ptr{Ptr{Void}}, url_ptr::Cstring, # No authentication method we support succeeded. The most likely cause is # that explicit credentials were passed in, but said credentials are incompatible - # with the remote host. + # with the requested authentication method. if err == 0 if explicit - warn("The explicitly provided credentials were incompatible with " * - "the server's supported authentication methods") + ccall((:giterr_set_str, :libgit2), Void, (Cint, Cstring), Cint(Error.Callback), + "The explicitly provided credential is incompatible with the requested " * + "authentication methods.") end err = Cint(Error.EAUTH) end diff --git a/test/libgit2.jl b/test/libgit2.jl index ede2ba1445822..ba6b06d3a7477 100644 --- a/test/libgit2.jl +++ b/test/libgit2.jl @@ -1613,6 +1613,11 @@ mktempdir() do dir LibGit2.Error.Callback, LibGit2.Error.EAUTH, "Aborting, user cancelled credential request.") + incompatible_error = LibGit2.GitError( + LibGit2.Error.Callback, LibGit2.Error.EAUTH, + "The explicitly provided credential is incompatible with the requested " * + "authentication methods.") + eauth_error = LibGit2.GitError( LibGit2.Error.None, LibGit2.Error.EAUTH, "No errors") @@ -2046,7 +2051,7 @@ mktempdir() do dir @testset "Incompatible explicit credentials" begin # User provides a user/password credential where a SSH credential is required. - ssh_cmd = """ + expect_ssh_cmd = """ include("$LIBGIT2_HELPER_PATH") valid_cred = LibGit2.UserPasswordCredentials("foo", "bar") payload = CredentialPayload(Nullable(valid_cred)) @@ -2056,7 +2061,7 @@ mktempdir() do dir """ # User provides a SSH credential where a user/password credential is required. - https_cmd = """ + expect_https_cmd = """ include("$LIBGIT2_HELPER_PATH") valid_cred = LibGit2.SSHCredentials("foo", "", "", "") payload = CredentialPayload(Nullable(valid_cred)) @@ -2065,11 +2070,8 @@ mktempdir() do dir (err < 0 ? LibGit2.GitError(err) : err, auth_attempts) """ - # TODO: Currently a warning is reported about the explicit credential being - # incompatible with the authentication method. We should change this to an - # error. - err, auth_attempts = challenge_prompt(ssh_cmd, []) - @test err == eauth_error + err, auth_attempts = challenge_prompt(expect_ssh_cmd, []) + @test err == incompatible_error @test auth_attempts == 1 # TODO: Providing an explicit SSH credential which is incompatible with the @@ -2077,7 +2079,7 @@ mktempdir() do dir challenges = [ "Username for 'https://github.com':" => "\x04", ] - err, auth_attempts = challenge_prompt(https_cmd, challenges) + err, auth_attempts = challenge_prompt(expect_https_cmd, challenges) @test err == abort_prompt @test auth_attempts == 1 end From 3d4ff9d1ef9406292239d4c4d779d123368e80e7 Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Sun, 6 Aug 2017 21:52:54 -0500 Subject: [PATCH 046/324] Store parsed callback URL in payload --- base/libgit2/callbacks.jl | 44 ++++++++++++++++++++------------------- base/libgit2/types.jl | 8 +++++++ 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/base/libgit2/callbacks.jl b/base/libgit2/callbacks.jl index e4e3a59b899a7..2d6e54320dcb4 100644 --- a/base/libgit2/callbacks.jl +++ b/base/libgit2/callbacks.jl @@ -48,8 +48,7 @@ function user_abort() return Cint(Error.EAUTH) end -function authenticate_ssh(libgit2credptr::Ptr{Ptr{Void}}, p::CredentialPayload, - username_ptr, schema, host) +function authenticate_ssh(libgit2credptr::Ptr{Ptr{Void}}, p::CredentialPayload, username_ptr) creds = Base.get(p.credential)::SSHCredentials isusedcreds = checkused!(creds) @@ -76,7 +75,7 @@ function authenticate_ssh(libgit2credptr::Ptr{Ptr{Void}}, p::CredentialPayload, username = username_ptr != Cstring(C_NULL) ? unsafe_string(username_ptr) : "" if isempty(username) uname = creds.user # check if credentials were already used - prompt_url = git_url(scheme=schema, host=host) + prompt_url = git_url(scheme=p.scheme, host=p.host) if !isusedcreds username = uname else @@ -86,7 +85,7 @@ function authenticate_ssh(libgit2credptr::Ptr{Ptr{Void}}, p::CredentialPayload, end end - prompt_url = git_url(scheme=schema, host=host, username=username) + prompt_url = git_url(scheme=p.scheme, host=p.host, username=username) # For SSH we need a private key location privatekey = if haskey(ENV,"SSH_KEY_PATH") @@ -168,29 +167,28 @@ function authenticate_ssh(libgit2credptr::Ptr{Ptr{Void}}, p::CredentialPayload, libgit2credptr, creds.user, creds.pubkey, creds.prvkey, creds.pass) end -function authenticate_userpass(libgit2credptr::Ptr{Ptr{Void}}, p::CredentialPayload, - schema, host, urlusername) +function authenticate_userpass(libgit2credptr::Ptr{Ptr{Void}}, p::CredentialPayload) creds = Base.get(p.credential)::UserPasswordCredentials isusedcreds = checkused!(creds) if creds.prompt_if_incorrect username = creds.user userpass = creds.pass - prompt_url = git_url(scheme=schema, host=host) + prompt_url = git_url(scheme=p.scheme, host=p.host) if Sys.iswindows() if isempty(username) || isempty(userpass) || isusedcreds response = Base.winprompt("Please enter your credentials for '$prompt_url'", "Credentials required", - isempty(username) ? urlusername : username; prompt_username = true) + isempty(username) ? p.username : username; prompt_username = true) isnull(response) && return user_abort() username, userpass = unsafe_get(response) end elseif isusedcreds response = Base.prompt("Username for '$prompt_url'", - default=isempty(username) ? urlusername : username) + default=isempty(username) ? p.username : username) isnull(response) && return user_abort() username = unsafe_get(response) - prompt_url = git_url(scheme=schema, host=host, username=username) + prompt_url = git_url(scheme=p.scheme, host=p.host, username=username) response = Base.prompt("Password for '$prompt_url'", password=true) isnull(response) && return user_abort() userpass = unsafe_get(response) @@ -240,17 +238,21 @@ function credentials_callback(libgit2credptr::Ptr{Ptr{Void}}, url_ptr::Cstring, username_ptr::Cstring, allowed_types::Cuint, payload_ptr::Ptr{Void}) err = Cint(0) - url = unsafe_string(url_ptr) # get `CredentialPayload` object from payload pointer @assert payload_ptr != C_NULL p = unsafe_pointer_to_objref(payload_ptr)[]::CredentialPayload - # parse url for schema and host - urlparts = match(URL_REGEX, url) - schema = urlparts[:scheme] === nothing ? "" : urlparts[:scheme] - urlusername = urlparts[:user] === nothing ? "" : urlparts[:user] - host = urlparts[:host] + # Parse URL only during the first call to this function. Future calls will use the + # information cached inside the payload. + if isempty(p.host) + url = match(URL_REGEX, unsafe_string(url_ptr)) + + p.scheme = url[:scheme] === nothing ? "" : url[:scheme] + p.username = url[:user] === nothing ? "" : url[:user] + p.host = url[:host] + p.path = url[:path] + end if !isnull(p.credential) creds = unsafe_get(p.credential) @@ -262,17 +264,17 @@ function credentials_callback(libgit2credptr::Ptr{Ptr{Void}}, url_ptr::Cstring, # use ssh key or ssh-agent if isset(allowed_types, Cuint(Consts.CREDTYPE_SSH_KEY)) - sshcreds = get_creds!(creds, "ssh://$host", reset!(SSHCredentials(true), -1)) + sshcreds = get_creds!(creds, "ssh://$(p.host)", reset!(SSHCredentials(p.username, "", true), -1)) if isa(sshcreds, SSHCredentials) p.credential = Nullable(sshcreds) - err = authenticate_ssh(libgit2credptr, p, username_ptr, schema, host) + err = authenticate_ssh(libgit2credptr, p, username_ptr) err == 0 && return err end end if isset(allowed_types, Cuint(Consts.CREDTYPE_USERPASS_PLAINTEXT)) - defaultcreds = reset!(UserPasswordCredentials(true), -1) - credid = "$(isempty(schema) ? "ssh" : schema)://$host" + defaultcreds = reset!(UserPasswordCredentials(p.username, "", true), -1) + credid = "$(isempty(p.scheme) ? "ssh" : p.scheme)://$(p.host)" upcreds = get_creds!(creds, credid, defaultcreds) # If there were stored SSH credentials, but we ended up here that must # mean that something went wrong. Replace the SSH credentials by user/pass @@ -282,7 +284,7 @@ function credentials_callback(libgit2credptr::Ptr{Ptr{Void}}, url_ptr::Cstring, isa(creds, CachedCredentials) && (creds.creds[credid] = upcreds) end p.credential = Nullable(upcreds) - return authenticate_userpass(libgit2credptr, p, schema, host, urlusername) + return authenticate_userpass(libgit2credptr, p) end # No authentication method we support succeeded. The most likely cause is diff --git a/base/libgit2/types.jl b/base/libgit2/types.jl index c223ef8416146..f061de12f00bb 100644 --- a/base/libgit2/types.jl +++ b/base/libgit2/types.jl @@ -937,6 +937,14 @@ instances will be used when the URL has changed. mutable struct CredentialPayload <: Payload credential::Nullable{AbstractCredentials} cache::Nullable{CachedCredentials} + scheme::String + username::String + host::String + path::String + + function CredentialPayload(credential::Nullable{<:AbstractCredentials}, cache::Nullable{CachedCredentials}) + new(credential, cache, "", "", "", "") + end end function CredentialPayload(credential::Nullable{<:AbstractCredentials}) From 5cd220c23db14635389c030de6104832207dcab1 Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Sun, 6 Aug 2017 18:05:58 -0500 Subject: [PATCH 047/324] Make credential handling more consistent The two code paths for handling the the SSH and username/password `allowed_types` differ enough that some bugs have cropped up. These changes make the two code paths nearly identical which should remove any inconsistences. --- base/libgit2/callbacks.jl | 57 +++++++++++++++++++++++---------------- test/libgit2.jl | 9 ++----- 2 files changed, 36 insertions(+), 30 deletions(-) diff --git a/base/libgit2/callbacks.jl b/base/libgit2/callbacks.jl index 2d6e54320dcb4..4923318738b95 100644 --- a/base/libgit2/callbacks.jl +++ b/base/libgit2/callbacks.jl @@ -238,6 +238,7 @@ function credentials_callback(libgit2credptr::Ptr{Ptr{Void}}, url_ptr::Cstring, username_ptr::Cstring, allowed_types::Cuint, payload_ptr::Ptr{Void}) err = Cint(0) + explicit = false # get `CredentialPayload` object from payload pointer @assert payload_ptr != C_NULL @@ -252,39 +253,49 @@ function credentials_callback(libgit2credptr::Ptr{Ptr{Void}}, url_ptr::Cstring, p.username = url[:user] === nothing ? "" : url[:user] p.host = url[:host] p.path = url[:path] - end - if !isnull(p.credential) - creds = unsafe_get(p.credential) - explicit = true - else - creds = Base.get(p.cache, nothing) - explicit = false + # When an explicit credential is supplied we will make sure to use the given + # credential during the first callback by modifying the allowed types. The + # modification only is in effect for the first callback since `allowed_types` cannot + # be mutated. + if !isnull(p.credential) + explicit = true + cred = unsafe_get(p.credential) + if isa(cred, SSHCredentials) + allowed_types &= Cuint(Consts.CREDTYPE_SSH_KEY) + elseif isa(cred, UserPasswordCredentials) + allowed_types &= Cuint(Consts.CREDTYPE_USERPASS_PLAINTEXT) + else + allowed_types &= Cuint(0) # Unhandled credential type + end + end end # use ssh key or ssh-agent if isset(allowed_types, Cuint(Consts.CREDTYPE_SSH_KEY)) - sshcreds = get_creds!(creds, "ssh://$(p.host)", reset!(SSHCredentials(p.username, "", true), -1)) - if isa(sshcreds, SSHCredentials) - p.credential = Nullable(sshcreds) - err = authenticate_ssh(libgit2credptr, p, username_ptr) - err == 0 && return err + if isnull(p.credential) || !isa(unsafe_get(p.credential), SSHCredentials) + creds = reset!(SSHCredentials(p.username, "", true), -1) + if !isnull(p.cache) + credid = "ssh://$(p.host)" + creds = get_creds!(unsafe_get(p.cache), credid, creds) + end + p.credential = Nullable(creds) end + err = authenticate_ssh(libgit2credptr, p, username_ptr) + err == 0 && return err end if isset(allowed_types, Cuint(Consts.CREDTYPE_USERPASS_PLAINTEXT)) - defaultcreds = reset!(UserPasswordCredentials(p.username, "", true), -1) - credid = "$(isempty(p.scheme) ? "ssh" : p.scheme)://$(p.host)" - upcreds = get_creds!(creds, credid, defaultcreds) - # If there were stored SSH credentials, but we ended up here that must - # mean that something went wrong. Replace the SSH credentials by user/pass - # credentials - if !isa(upcreds, UserPasswordCredentials) - upcreds = defaultcreds - isa(creds, CachedCredentials) && (creds.creds[credid] = upcreds) + if isnull(p.credential) || !isa(unsafe_get(p.credential), UserPasswordCredentials) + creds = reset!(UserPasswordCredentials(p.username, "", true), -1) + if !isnull(p.cache) + credid = "$(isempty(p.scheme) ? "ssh" : p.scheme)://$(p.host)" + creds = get_creds!(unsafe_get(p.cache), credid, creds) + end + p.credential = Nullable(creds) end - p.credential = Nullable(upcreds) - return authenticate_userpass(libgit2credptr, p) + err = authenticate_userpass(libgit2credptr, p) + err == 0 && return err end # No authentication method we support succeeded. The most likely cause is diff --git a/test/libgit2.jl b/test/libgit2.jl index ba6b06d3a7477..72c964cb9be0a 100644 --- a/test/libgit2.jl +++ b/test/libgit2.jl @@ -2074,13 +2074,8 @@ mktempdir() do dir @test err == incompatible_error @test auth_attempts == 1 - # TODO: Providing an explicit SSH credential which is incompatible with the - # authentication method triggers a prompt... - challenges = [ - "Username for 'https://github.com':" => "\x04", - ] - err, auth_attempts = challenge_prompt(expect_https_cmd, challenges) - @test err == abort_prompt + err, auth_attempts = challenge_prompt(expect_https_cmd, []) + @test err == incompatible_error @test auth_attempts == 1 end From 393ec1c66e3abc5e460ae5066ab121f24b2ee369 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Tue, 15 Aug 2017 14:18:31 -0400 Subject: [PATCH 048/324] move doc strings from helpdb to definition sites --- base/abstractarray.jl | 47 + base/array.jl | 312 ++++++ base/c.jl | 23 + base/coreio.jl | 1 - base/datafmt.jl | 5 + base/dict.jl | 122 +++ base/docs/basedocs.jl | 175 +++ base/docs/helpdb.jl | 3 - base/docs/helpdb/Base.jl | 1882 --------------------------------- base/error.jl | 5 + base/essentials.jl | 224 +++- base/event.jl | 23 + base/float.jl | 63 +- base/floatfuncs.jl | 10 + base/gcutils.jl | 26 + base/gmp.jl | 8 + base/hashing.jl | 11 + base/int.jl | 82 ++ base/intfuncs.jl | 16 + base/io.jl | 20 + base/iostream.jl | 39 + base/libc.jl | 6 + base/mmap.jl | 83 ++ base/multidimensional.jl | 32 + base/multimedia.jl | 31 + base/nullable.jl | 16 + base/number.jl | 15 + base/operators.jl | 26 + base/parse.jl | 66 ++ base/pointer.jl | 24 + base/process.jl | 5 + base/promotion.jl | 27 + base/random.jl | 7 + base/reflection.jl | 6 +- base/regex.jl | 27 + base/serialize.jl | 18 + base/set.jl | 52 + base/show.jl | 16 + base/socket.jl | 7 + base/sort.jl | 76 ++ base/special/exp10.jl | 14 + base/strings/utf8proc.jl | 18 + base/subarray.jl | 24 + base/sysimg.jl | 12 +- doc/src/stdlib/base.md | 4 +- doc/src/stdlib/c.md | 5 +- doc/src/stdlib/collections.md | 8 +- doc/src/stdlib/io-network.md | 3 +- 48 files changed, 1806 insertions(+), 1919 deletions(-) delete mode 100644 base/docs/helpdb.jl delete mode 100644 base/docs/helpdb/Base.jl diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 3e60aeb387646..df165c1c49916 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -123,10 +123,42 @@ ndims(::AbstractArray{T,N}) where {T,N} = N ndims(::Type{AbstractArray{T,N}}) where {T,N} = N ndims(::Type{T}) where {T<:AbstractArray} = ndims(supertype(T)) +""" + length(collection) -> Integer + +Return the number of elements in the collection. + +Use [`endof`](@ref) to get the last valid index of an indexable collection. + +# Examples +```jldoctest +julia> length(1:5) +5 + +julia> length([1, 2, 3, 4]) +4 + +julia> length([1 2; 3 4]) +4 +``` +""" length(t::AbstractArray) = (@_inline_meta; prod(size(t))) _length(A::AbstractArray) = (@_inline_meta; prod(map(unsafe_length, indices(A)))) # circumvent missing size _length(A) = (@_inline_meta; length(A)) + +""" + endof(collection) -> Integer + +Returns the last index of the collection. + +# Examples +```jldoctest +julia> endof([1,2,4]) +3 +``` +""" endof(a::AbstractArray) = (@_inline_meta; last(linearindices(a))) + first(a::AbstractArray) = a[first(eachindex(a))] """ @@ -1346,6 +1378,21 @@ hcat(X...) = cat(Val(2), X...) typed_vcat(T::Type, X...) = cat_t(Val(1), T, X...) typed_hcat(T::Type, X...) = cat_t(Val(2), T, X...) +""" + cat(dims, A...) + +Concatenate the input arrays along the specified dimensions in the iterable `dims`. For +dimensions not in `dims`, all input arrays should have the same size, which will also be the +size of the output array along that dimension. For dimensions in `dims`, the size of the +output array is the sum of the sizes of the input arrays along that dimension. If `dims` is +a single number, the different arrays are tightly stacked along that dimension. If `dims` is +an iterable containing several dimensions, this allows one to construct block diagonal +matrices and their higher-dimensional analogues by simultaneously increasing several +dimensions for every new input array and putting zero blocks elsewhere. For example, +`cat([1,2], matrices...)` builds a block diagonal matrix, i.e. a block matrix with +`matrices[1]`, `matrices[2]`, ... as diagonal blocks and matching zero blocks away from the +diagonal. +""" cat(catdims, A::AbstractArray{T}...) where {T} = cat_t(catdims, T, A...) # The specializations for 1 and 2 inputs are important diff --git a/base/array.jl b/base/array.jl index 7d3f231f691b8..86acf04af3086 100644 --- a/base/array.jl +++ b/base/array.jl @@ -122,6 +122,16 @@ end ## copy ## +""" + unsafe_copy!(dest::Ptr{T}, src::Ptr{T}, N) + +Copy `N` elements from a source pointer to a destination, with no checking. The size of an +element is determined by the type of the pointers. + +The `unsafe` prefix on this function indicates that no validation is performed on the +pointers `dest` and `src` to ensure that they are valid. Incorrect usage may corrupt or +segfault your program, in the same manner as C. +""" function unsafe_copy!(dest::Ptr{T}, src::Ptr{T}, n) where T # Do not use this to copy data between pointer arrays. # It can't be made safe no matter how carefully you checked. @@ -130,6 +140,16 @@ function unsafe_copy!(dest::Ptr{T}, src::Ptr{T}, n) where T return dest end +""" + unsafe_copy!(dest::Array, do, src::Array, so, N) + +Copy `N` elements from a source array to a destination, starting at offset `so` in the +source and `do` in the destination (1-indexed). + +The `unsafe` prefix on this function indicates that no validation is performed to ensure +that N is inbounds on either array. Incorrect usage may corrupt or segfault your program, in +the same manner as C. +""" function unsafe_copy!(dest::Array{T}, doffs, src::Array{T}, soffs, n) where T if isbits(T) unsafe_copy!(pointer(dest, doffs), pointer(src, soffs), n) @@ -140,6 +160,12 @@ function unsafe_copy!(dest::Array{T}, doffs, src::Array{T}, soffs, n) where T return dest end +""" + copy!(dest, do, src, so, N) + +Copy `N` elements from collection `src` starting at offset `so`, to array `dest` starting at +offset `do`. Returns `dest`. +""" function copy!(dest::Array{T}, doffs::Integer, src::Array{T}, soffs::Integer, n::Integer) where T n == 0 && return dest n > 0 || throw(ArgumentError(string("tried to copy n=", n, " elements, but n should be nonnegative"))) @@ -151,6 +177,13 @@ end copy!(dest::Array{T}, src::Array{T}) where {T} = copy!(dest, 1, src, 1, length(src)) +""" + copy(x) + +Create a shallow copy of `x`: the outer structure is copied, but not all internal values. +For example, copying an array produces a new array with identically-same elements as the +original. +""" copy(a::T) where {T<:Array} = ccall(:jl_array_copy, Ref{T}, (Any,), a) function reinterpret(::Type{T}, a::Array{S,1}) where T where S @@ -295,6 +328,92 @@ dims)` will return an array filled with the result of evaluating `Foo()` once. fill(v, dims::Dims) = fill!(Array{typeof(v)}(dims), v) fill(v, dims::Integer...) = fill!(Array{typeof(v)}(dims...), v) +""" + zeros([A::AbstractArray,] [T=eltype(A)::Type,] [dims=size(A)::Tuple]) + +Create an array of all zeros with the same layout as `A`, element type `T` and size `dims`. +The `A` argument can be skipped, which behaves like `Array{Float64,0}()` was passed. +For convenience `dims` may also be passed in variadic form. + +# Examples +```jldoctest +julia> zeros(1) +1-element Array{Float64,1}: + 0.0 + +julia> zeros(Int8, 2, 3) +2×3 Array{Int8,2}: + 0 0 0 + 0 0 0 + +julia> A = [1 2; 3 4] +2×2 Array{Int64,2}: + 1 2 + 3 4 + +julia> zeros(A) +2×2 Array{Int64,2}: + 0 0 + 0 0 + +julia> zeros(A, Float64) +2×2 Array{Float64,2}: + 0.0 0.0 + 0.0 0.0 + +julia> zeros(A, Bool, (3,)) +3-element Array{Bool,1}: + false + false + false +``` +See also [`ones`](@ref), [`similar`](@ref). +""" +function zeros end + +""" + ones([A::AbstractArray,] [T=eltype(A)::Type,] [dims=size(A)::Tuple]) + +Create an array of all ones with the same layout as `A`, element type `T` and size `dims`. +The `A` argument can be skipped, which behaves like `Array{Float64,0}()` was passed. +For convenience `dims` may also be passed in variadic form. + +# Examples +```jldoctest +julia> ones(Complex128, 2, 3) +2×3 Array{Complex{Float64},2}: + 1.0+0.0im 1.0+0.0im 1.0+0.0im + 1.0+0.0im 1.0+0.0im 1.0+0.0im + +julia> ones(1,2) +1×2 Array{Float64,2}: + 1.0 1.0 + +julia> A = [1 2; 3 4] +2×2 Array{Int64,2}: + 1 2 + 3 4 + +julia> ones(A) +2×2 Array{Int64,2}: + 1 1 + 1 1 + +julia> ones(A, Float64) +2×2 Array{Float64,2}: + 1.0 1.0 + 1.0 1.0 + +julia> ones(A, Bool, (3,)) +3-element Array{Bool,1}: + true + true + true +``` +See also [`zeros`](@ref), [`similar`](@ref). +""" +function ones end + for (fname, felt) in ((:zeros,:zero), (:ones,:one)) @eval begin # allow signature of similar @@ -593,6 +712,25 @@ done(a::Array,i) = (@_inline_meta; i == length(a)+1) ## Indexing: getindex ## +""" + getindex(collection, key...) + +Retrieve the value(s) stored at the given key or index within a collection. The syntax +`a[i,j,...]` is converted by the compiler to `getindex(a, i, j, ...)`. + +# Examples +```jldoctest +julia> A = Dict("a" => 1, "b" => 2) +Dict{String,Int64} with 2 entries: + "b" => 2 + "a" => 1 + +julia> getindex(A, "a") +1 +``` +""" +function getindex end + # This is more complicated than it needs to be in order to get Win64 through bootstrap getindex(A::Array, i1::Int) = arrayref(A, i1) getindex(A::Array, i1::Int, i2::Int, I::Int...) = (@_inline_meta; arrayref(A, i1, i2, I...)) # TODO: REMOVE FOR #14770 @@ -623,6 +761,15 @@ function getindex(A::Array{S}, I::Range{Int}) where S end ## Indexing: setindex! ## + +""" + setindex!(collection, value, key...) + +Store the given value at the given key or index within a collection. The syntax `a[i,j,...] = +x` is converted by the compiler to `(setindex!(a, x, i, j, ...); x)`. +""" +function setindex! end + setindex!(A::Array{T}, x, i1::Int) where {T} = arrayset(A, convert(T,x)::T, i1) setindex!(A::Array{T}, x, i1::Int, i2::Int, I::Int...) where {T} = (@_inline_meta; arrayset(A, convert(T,x)::T, i1, i2, I...)) # TODO: REMOVE FOR #14770 @@ -697,6 +844,29 @@ _deleteat!(a::Vector, i::Integer, delta::Integer) = ## Dequeue functionality ## +""" + push!(collection, items...) -> collection + +Insert one or more `items` at the end of `collection`. + +# Examples +```jldoctest +julia> push!([1, 2, 3], 4, 5, 6) +6-element Array{Int64,1}: + 1 + 2 + 3 + 4 + 5 + 6 +``` + +Use [`append!`](@ref) to add all the elements of another collection to +`collection`. The result of the preceding example is equivalent to `append!([1, 2, 3], [4, +5, 6])`. +""" +function push! end + function push!(a::Array{T,1}, item) where T # convert first so we don't grow the array if the assignment won't work itemT = convert(T, item) @@ -711,6 +881,33 @@ function push!(a::Array{Any,1}, @nospecialize item) return a end +""" + append!(collection, collection2) -> collection. + +Add the elements of `collection2` to the end of `collection`. + +# Examples +```jldoctest +julia> append!([1],[2,3]) +3-element Array{Int64,1}: + 1 + 2 + 3 + +julia> append!([1, 2, 3], [4, 5, 6]) +6-element Array{Int64,1}: + 1 + 2 + 3 + 4 + 5 + 6 +``` + +Use [`push!`](@ref) to add individual items to `collection` which are not already +themselves in another collection. The result is of the preceding example is equivalent to +`push!([1, 2, 3], 4, 5, 6)`. +""" function append!(a::Array{<:Any,1}, items::AbstractVector) itemindices = eachindex(items) n = length(itemindices) @@ -832,11 +1029,53 @@ function resize!(a::Vector, nl::Integer) return a end +""" + sizehint!(s, n) + +Suggest that collection `s` reserve capacity for at least `n` elements. This can improve performance. +""" +function sizehint! end + function sizehint!(a::Vector, sz::Integer) ccall(:jl_array_sizehint, Void, (Any, UInt), a, sz) a end +""" + pop!(collection) -> item + +Remove an item in `collection` and return it. If `collection` is an +ordered container, the last item is returned. + +# Examples +```jldoctest +julia> A=[1, 2, 3] +3-element Array{Int64,1}: + 1 + 2 + 3 + +julia> pop!(A) +3 + +julia> A +2-element Array{Int64,1}: + 1 + 2 + +julia> S = Set([1, 2]) +Set([2, 1]) + +julia> pop!(S) +2 + +julia> S +Set([1]) + +julia> pop!(Dict(1=>2)) +1 => 2 +``` +""" function pop!(a::Vector) if isempty(a) throw(ArgumentError("array must be non-empty")) @@ -870,6 +1109,34 @@ function unshift!(a::Array{T,1}, item) where T return a end +""" + shift!(collection) -> item + +Remove the first `item` from `collection`. + +# Examples +```jldoctest +julia> A = [1, 2, 3, 4, 5, 6] +6-element Array{Int64,1}: + 1 + 2 + 3 + 4 + 5 + 6 + +julia> shift!(A) +1 + +julia> A +5-element Array{Int64,1}: + 2 + 3 + 4 + 5 + 6 +``` +""" function shift!(a::Vector) if isempty(a) throw(ArgumentError("array must be non-empty")) @@ -1158,6 +1425,46 @@ function ==(a::Arr, b::Arr) where Arr <: BitIntegerArray{1} :memcmp, Int32, (Ptr{Void}, Ptr{Void}, UInt), a, b, sizeof(eltype(Arr)) * len) end +""" + reverse(v [, start=1 [, stop=length(v) ]] ) + +Return a copy of `v` reversed from start to stop. + +# Examples +```jldoctest +julia> A = collect(1:5) +5-element Array{Int64,1}: + 1 + 2 + 3 + 4 + 5 + +julia> reverse(A) +5-element Array{Int64,1}: + 5 + 4 + 3 + 2 + 1 + +julia> reverse(A, 1, 4) +5-element Array{Int64,1}: + 4 + 3 + 2 + 1 + 5 + +julia> reverse(A, 3, 5) +5-element Array{Int64,1}: + 1 + 2 + 5 + 4 + 3 +``` +""" function reverse(A::AbstractVector, s=first(linearindices(A)), n=last(linearindices(A))) B = similar(A) for i = first(linearindices(A)):s-1 @@ -1176,6 +1483,11 @@ function reverseind(a::AbstractVector, i::Integer) first(li) + last(li) - i end +""" + reverse!(v [, start=1 [, stop=length(v) ]]) -> v + +In-place version of [`reverse`](@ref). +""" function reverse!(v::AbstractVector, s=first(linearindices(v)), n=last(linearindices(v))) liv = linearindices(v) if n <= s # empty case; ok diff --git a/base/c.jl b/base/c.jl index bd9a37ac5eaab..c57ce27d6130b 100644 --- a/base/c.jl +++ b/base/c.jl @@ -4,6 +4,18 @@ import Core.Intrinsics: cglobal, bitcast +""" + cglobal((symbol, library) [, type=Void]) + +Obtain a pointer to a global variable in a C-exported shared library, specified exactly as +in [`ccall`](@ref). +Returns a `Ptr{Type}`, defaulting to `Ptr{Void}` if no `Type` argument is +supplied. +The values can be read or written by [`unsafe_load`](@ref) or [`unsafe_store!`](@ref), +respectively. +""" +cglobal + """ cfunction(f::Function, returntype::Type, argtypes::Type) -> Ptr{Void} @@ -87,6 +99,17 @@ convert(::Type{Ptr{Cwchar_t}}, p::Cwstring) = bitcast(Ptr{Cwchar_t}, p) # construction from untyped pointers convert(::Type{T}, p::Ptr{Void}) where {T<:Union{Cstring,Cwstring}} = bitcast(T, p) +""" + pointer(array [, index]) + +Get the native address of an array or string element. Be careful to ensure that a Julia +reference to `a` exists as long as this pointer will be used. This function is "unsafe" like +`unsafe_convert`. + +Calling `Ref(array[, index])` is generally preferable to this function. +""" +function pointer end + pointer(p::Cstring) = convert(Ptr{UInt8}, p) pointer(p::Cwstring) = convert(Ptr{Cwchar_t}, p) diff --git a/base/coreio.jl b/base/coreio.jl index b15fca1ea1003..fcc6bd519e355 100644 --- a/base/coreio.jl +++ b/base/coreio.jl @@ -1,6 +1,5 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -show(x) = show(STDOUT::IO, x) print(xs...) = print(STDOUT::IO, xs...) println(xs...) = println(STDOUT::IO, xs...) println(io::IO) = print(io, '\n') diff --git a/base/datafmt.jl b/base/datafmt.jl index 746da60f004b3..573350853bbfd 100644 --- a/base/datafmt.jl +++ b/base/datafmt.jl @@ -616,6 +616,11 @@ function dlm_parse(dbuff::String, eol::D, dlm::D, qchar::D, cchar::D, return (nrows, ncols) end +""" + readcsv(source, [T::Type]; options...) + +Equivalent to [`readdlm`](@ref) with `delim` set to comma, and type optionally defined by `T`. +""" readcsv(io; opts...) = readdlm(io, ','; opts...) readcsv(io, T::Type; opts...) = readdlm(io, ',', T; opts...) diff --git a/base/dict.jl b/base/dict.jl index e0ddcaaee649e..4dad63bed4d51 100644 --- a/base/dict.jl +++ b/base/dict.jl @@ -437,7 +437,50 @@ function setindex!(h::Dict{K,V}, v0, key::K) where V where K return h end +""" + get!(collection, key, default) + +Return the value stored for the given key, or if no mapping for the key is present, store +`key => default`, and return `default`. + +# Examples +```jldoctest +julia> d = Dict("a"=>1, "b"=>2, "c"=>3); + +julia> get!(d, "a", 5) +1 + +julia> get!(d, "d", 4) +4 + +julia> d +Dict{String,Int64} with 4 entries: + "c" => 3 + "b" => 2 + "a" => 1 + "d" => 4 +``` +""" +get!(collection, key, default) + get!(h::Dict{K,V}, key0, default) where {K,V} = get!(()->default, h, key0) + +""" + get!(f::Function, collection, key) + +Return the value stored for the given key, or if no mapping for the key is present, store +`key => f()`, and return `f()`. + +This is intended to be called using `do` block syntax: +```julia +get!(dict, key) do + # default value calculated here + time() +end +``` +""" +get!(f::Function, collection, key) + function get!(default::Callable, h::Dict{K,V}, key0) where V where K key = convert(K, key0) if !isequal(key, key0) @@ -480,11 +523,47 @@ function getindex(h::Dict{K,V}, key) where V where K return (index < 0) ? throw(KeyError(key)) : h.vals[index]::V end +""" + get(collection, key, default) + +Return the value stored for the given key, or the given default value if no mapping for the +key is present. + +# Examples +```jldoctest +julia> d = Dict("a"=>1, "b"=>2); + +julia> get(d, "a", 3) +1 + +julia> get(d, "c", 3) +3 +``` +""" +get(collection, key, default) + function get(h::Dict{K,V}, key, default) where V where K index = ht_keyindex(h, key) return (index < 0) ? default : h.vals[index]::V end +""" + get(f::Function, collection, key) + +Return the value stored for the given key, or if no mapping for the key is present, return +`f()`. Use [`get!`](@ref) to also store the default value in the dictionary. + +This is intended to be called using `do` block syntax + +```julia +get(dict, key) do + # default value calculated here + time() +end +``` +""" +get(::Function, collection, key) + function get(default::Callable, h::Dict{K,V}, key) where V where K index = ht_keyindex(h, key) return (index < 0) ? default() : h.vals[index]::V @@ -545,6 +624,30 @@ function pop!(h::Dict, key) return index > 0 ? _pop!(h, index) : throw(KeyError(key)) end +""" + pop!(collection, key[, default]) + +Delete and return the mapping for `key` if it exists in `collection`, otherwise return +`default`, or throw an error if `default` is not specified. + +# Examples +```jldoctest +julia> d = Dict("a"=>1, "b"=>2, "c"=>3); + +julia> pop!(d, "a") +1 + +julia> pop!(d, "d") +ERROR: KeyError: key "d" not found +Stacktrace: + [1] pop!(::Dict{String,Int64}, ::String) at ./dict.jl:539 + +julia> pop!(d, "e", 4) +4 +``` +""" +pop!(collection, key, default) + function pop!(h::Dict, key, default) index = ht_keyindex(h, key) return index > 0 ? _pop!(h, index) : default @@ -569,6 +672,25 @@ function _delete!(h::Dict, index) return h end +""" + delete!(collection, key) + +Delete the mapping for the given key in a collection, and return the collection. + +# Examples +```jldoctest +julia> d = Dict("a"=>1, "b"=>2) +Dict{String,Int64} with 2 entries: + "b" => 2 + "a" => 1 + +julia> delete!(d, "b") +Dict{String,Int64} with 1 entry: + "a" => 1 +``` +""" +delete!(collection, key) + function delete!(h::Dict, key) index = ht_keyindex(h, key) if index > 0 diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index 1a2691f324782..c64b71aaa4786 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -750,6 +750,25 @@ The item or field is not defined for the given object. """ UndefRefError +""" + Float32(x [, mode::RoundingMode]) + +Create a Float32 from `x`. If `x` is not exactly representable then `mode` determines how +`x` is rounded. + +# Examples +```jldoctest +julia> Float32(1/3, RoundDown) +0.3333333f0 + +julia> Float32(1/3, RoundUp) +0.33333334f0 +``` + +See [`RoundingMode`](@ref) for available rounding modes. +""" +Float32(x) + """ Float64(x [, mode::RoundingMode]) @@ -1046,6 +1065,162 @@ for bit in (8, 16, 32, 64, 128) end end +""" + Symbol(x...) -> Symbol + +Create a `Symbol` by concatenating the string representations of the arguments together. +""" +Symbol + +""" + tuple(xs...) + +Construct a tuple of the given objects. + +# Examples +```jldoctest +julia> tuple(1, 'a', pi) +(1, 'a', π = 3.1415926535897...) +``` +""" +tuple + +""" + getfield(value, name::Symbol) + +Extract a named field from a `value` of composite type. The syntax `a.b` calls +`getfield(a, :b)`. + +# Examples +```jldoctest +julia> a = 1//2 +1//2 + +julia> getfield(a, :num) +1 +``` +""" +getfield + +""" + setfield!(value, name::Symbol, x) + +Assign `x` to a named field in `value` of composite type. The syntax `a.b = c` calls +`setfield!(a, :b, c)`. +""" +setfield! + +""" + typeof(x) + +Get the concrete type of `x`. +""" +typeof + +""" + isdefined(m::Module, s::Symbol) + isdefined(object, s::Symbol) + isdefined(object, index::Int) + +Tests whether an assignable location is defined. The arguments can be a module and a symbol +or a composite object and field name (as a symbol) or index. +""" +isdefined + + +""" + Vector{T}(n) + +Construct an uninitialized [`Vector{T}`](@ref) of length `n`. + +# Examples +```julia-repl +julia> Vector{Float64}(3) +3-element Array{Float64,1}: + 6.90966e-310 + 6.90966e-310 + 6.90966e-310 +``` +""" +Vector{T}(n) + +""" + Matrix{T}(m, n) + +Construct an uninitialized [`Matrix{T}`](@ref) of size `m`×`n`. + +# Examples +```julia-repl +julia> Matrix{Float64}(2, 3) +2×3 Array{Float64,2}: + 6.93517e-310 6.93517e-310 6.93517e-310 + 6.93517e-310 6.93517e-310 1.29396e-320 +``` +""" +Matrix{T}(m, n) + +""" + Array{T}(dims) + Array{T,N}(dims) + +Construct an uninitialized `N`-dimensional [`Array`](@ref) +containing elements of type `T`. `N` can either be supplied explicitly, +as in `Array{T,N}(dims)`, or be determined by the length or number of `dims`. +`dims` may be a tuple or a series of integer arguments corresponding to the lengths +in each dimension. If the rank `N` is supplied explicitly, then it must +match the length or number of `dims`. + +# Examples +```julia-repl +julia> A = Array{Float64,2}(2, 3) # N given explicitly +2×3 Array{Float64,2}: + 6.90198e-310 6.90198e-310 6.90198e-310 + 6.90198e-310 6.90198e-310 0.0 + +julia> B = Array{Float64}(2) # N determined by the input +2-element Array{Float64,1}: + 1.87103e-320 + 0.0 +``` +""" +Array{T,N}(dims) + +""" + +(x, y...) + +Addition operator. `x+y+z+...` calls this function with all arguments, i.e. `+(x, y, z, ...)`. +""" +(+)(x, y...) + +""" + -(x) + +Unary minus operator. +""" +-(x) + +""" + -(x, y) + +Subtraction operator. +""" +-(x, y) + +""" + *(x, y...) + +Multiplication operator. `x*y*z*...` calls this function with all arguments, i.e. `*(x, y, z, ...)`. +""" +(*)(x, y...) + +""" + /(x, y) + +Right division operator: multiplication of `x` by the inverse of `y` on the right. Gives +floating-point results for integer arguments. +""" +/(x, y) + """ ArgumentError(msg) diff --git a/base/docs/helpdb.jl b/base/docs/helpdb.jl deleted file mode 100644 index 5e2a4fe44b28a..0000000000000 --- a/base/docs/helpdb.jl +++ /dev/null @@ -1,3 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -include(joinpath("helpdb", "Base.jl")) diff --git a/base/docs/helpdb/Base.jl b/base/docs/helpdb/Base.jl deleted file mode 100644 index bd96ddbae5b05..0000000000000 --- a/base/docs/helpdb/Base.jl +++ /dev/null @@ -1,1882 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -# Base - -""" - fill!(A, x) - -Fill array `A` with the value `x`. If `x` is an object reference, all elements will refer to -the same object. `fill!(A, Foo())` will return `A` filled with the result of evaluating -`Foo()` once. - -# Examples -```jldoctest -julia> A = zeros(2,3) -2×3 Array{Float64,2}: - 0.0 0.0 0.0 - 0.0 0.0 0.0 - -julia> fill!(A, 2.) -2×3 Array{Float64,2}: - 2.0 2.0 2.0 - 2.0 2.0 2.0 - -julia> a = [1, 1, 1]; A = fill!(Vector{Vector{Int}}(3), a); a[1] = 2; A -3-element Array{Array{Int64,1},1}: - [2, 1, 1] - [2, 1, 1] - [2, 1, 1] - -julia> x = 0; f() = (global x += 1; x); fill!(Vector{Int}(3), f()) -3-element Array{Int64,1}: - 1 - 1 - 1 -``` -""" -fill! - -""" - read!(stream::IO, array::Union{Array, BitArray}) - read!(filename::AbstractString, array::Union{Array, BitArray}) - -Read binary data from an I/O stream or file, filling in `array`. -""" -read! - -""" - pointer(array [, index]) - -Get the native address of an array or string element. Be careful to ensure that a Julia -reference to `a` exists as long as this pointer will be used. This function is "unsafe" like -`unsafe_convert`. - -Calling `Ref(array[, index])` is generally preferable to this function. -""" -pointer - -""" - precision(num::AbstractFloat) - -Get the precision of a floating point number, as defined by the effective number of bits in -the mantissa. -""" -precision - -""" - -(x) - -Unary minus operator. -""" --(x) - -""" - -(x, y) - -Subtraction operator. -""" --(x, y) - -""" - bits(n) - -A string giving the literal bit representation of a number. - -# Examples -```jldoctest -julia> bits(4) -"0000000000000000000000000000000000000000000000000000000000000100" - -julia> bits(2.2) -"0100000000000001100110011001100110011001100110011001100110011010" -``` -""" -bits - -""" - getindex(collection, key...) - -Retrieve the value(s) stored at the given key or index within a collection. The syntax -`a[i,j,...]` is converted by the compiler to `getindex(a, i, j, ...)`. - -# Examples -```jldoctest -julia> A = Dict("a" => 1, "b" => 2) -Dict{String,Int64} with 2 entries: - "b" => 2 - "a" => 1 - -julia> getindex(A, "a") -1 -``` -""" -getindex(collection, key...) - -""" - cconvert(T,x) - -Convert `x` to a value to be passed to C code as type `T`, typically by calling `convert(T, x)`. - -In cases where `x` cannot be safely converted to `T`, unlike [`convert`](@ref), `cconvert` may -return an object of a type different from `T`, which however is suitable for -[`unsafe_convert`](@ref) to handle. The result of this function should be kept valid (for the GC) -until the result of [`unsafe_convert`](@ref) is not needed anymore. -This can be used to allocate memory that will be accessed by the `ccall`. -If multiple objects need to be allocated, a tuple of the objects can be used as return value. - -Neither `convert` nor `cconvert` should take a Julia object and turn it into a `Ptr`. -""" -cconvert - -""" - unsafe_copy!(dest::Ptr{T}, src::Ptr{T}, N) - -Copy `N` elements from a source pointer to a destination, with no checking. The size of an -element is determined by the type of the pointers. - -The `unsafe` prefix on this function indicates that no validation is performed on the -pointers `dest` and `src` to ensure that they are valid. Incorrect usage may corrupt or -segfault your program, in the same manner as C. -""" -unsafe_copy!{T}(dest::Ptr{T}, src::Ptr{T}, N) - -""" - unsafe_copy!(dest::Array, do, src::Array, so, N) - -Copy `N` elements from a source array to a destination, starting at offset `so` in the -source and `do` in the destination (1-indexed). - -The `unsafe` prefix on this function indicates that no validation is performed to ensure -that N is inbounds on either array. Incorrect usage may corrupt or segfault your program, in -the same manner as C. -""" -unsafe_copy!(dest::Array, d, src::Array, so, N) - -""" - Float32(x [, mode::RoundingMode]) - -Create a Float32 from `x`. If `x` is not exactly representable then `mode` determines how -`x` is rounded. - -# Examples -```jldoctest -julia> Float32(1/3, RoundDown) -0.3333333f0 - -julia> Float32(1/3, RoundUp) -0.33333334f0 -``` - -See [`RoundingMode`](@ref) for available rounding modes. -""" -Float32(x) - -""" - Mmap.mmap(io::Union{IOStream,AbstractString,Mmap.AnonymousMmap}[, type::Type{Array{T,N}}, dims, offset]; grow::Bool=true, shared::Bool=true) - Mmap.mmap(type::Type{Array{T,N}}, dims) - -Create an `Array` whose values are linked to a file, using memory-mapping. This provides a -convenient way of working with data too large to fit in the computer's memory. - -The type is an `Array{T,N}` with a bits-type element of `T` and dimension `N` that -determines how the bytes of the array are interpreted. Note that the file must be stored in -binary format, and no format conversions are possible (this is a limitation of operating -systems, not Julia). - -`dims` is a tuple or single [`Integer`](@ref) specifying the size or length of the array. - -The file is passed via the stream argument, either as an open `IOStream` or filename string. -When you initialize the stream, use `"r"` for a "read-only" array, and `"w+"` to create a -new array used to write values to disk. - -If no `type` argument is specified, the default is `Vector{UInt8}`. - -Optionally, you can specify an offset (in bytes) if, for example, you want to skip over a -header in the file. The default value for the offset is the current stream position for an -`IOStream`. - -The `grow` keyword argument specifies whether the disk file should be grown to accommodate -the requested size of array (if the total file size is < requested array size). Write -privileges are required to grow the file. - -The `shared` keyword argument specifies whether the resulting `Array` and changes made to it -will be visible to other processes mapping the same file. - -For example, the following code - -```julia -# Create a file for mmapping -# (you could alternatively use mmap to do this step, too) -A = rand(1:20, 5, 30) -s = open("/tmp/mmap.bin", "w+") -# We'll write the dimensions of the array as the first two Ints in the file -write(s, size(A,1)) -write(s, size(A,2)) -# Now write the data -write(s, A) -close(s) - -# Test by reading it back in -s = open("/tmp/mmap.bin") # default is read-only -m = read(s, Int) -n = read(s, Int) -A2 = Mmap.mmap(s, Matrix{Int}, (m,n)) -``` - -creates a `m`-by-`n` `Matrix{Int}`, linked to the file associated with stream `s`. - -A more portable file would need to encode the word size -- 32 bit or 64 bit -- and endianness -information in the header. In practice, consider encoding binary data using standard formats -like HDF5 (which can be used with memory-mapping). -""" -Mmap.mmap(io, ::Type, dims, offset) - -""" - Mmap.mmap(io, BitArray, [dims, offset]) - -Create a `BitArray` whose values are linked to a file, using memory-mapping; it has the same -purpose, works in the same way, and has the same arguments, as [`mmap`](@ref Mmap.mmap), but -the byte representation is different. - -**Example**: `B = Mmap.mmap(s, BitArray, (25,30000))` - -This would create a 25-by-30000 `BitArray`, linked to the file associated with stream `s`. -""" -Mmap.mmap(io, ::BitArray, dims, offset) - -""" - sizeof(T) - -Size, in bytes, of the canonical binary representation of the given DataType `T`, if any. - -# Examples -```jldoctest -julia> sizeof(Float32) -4 - -julia> sizeof(Complex128) -16 -``` - -If `T` does not have a specific size, an error is thrown. - -```jldoctest -julia> sizeof(Base.LinAlg.LU) -ERROR: argument is an abstract type; size is indeterminate -Stacktrace: - [1] sizeof(::Type{T} where T) at ./essentials.jl:150 -``` -""" -sizeof(::Type) - -""" - ceil([T,] x, [digits, [base]]) - -`ceil(x)` returns the nearest integral value of the same type as `x` that is greater than or -equal to `x`. - -`ceil(T, x)` converts the result to type `T`, throwing an `InexactError` if the value is not -representable. - -`digits` and `base` work as for [`round`](@ref). -""" -ceil - -""" - oftype(x, y) - -Convert `y` to the type of `x` (`convert(typeof(x), y)`). -""" -oftype - -""" - push!(collection, items...) -> collection - -Insert one or more `items` at the end of `collection`. - -# Examples -```jldoctest -julia> push!([1, 2, 3], 4, 5, 6) -6-element Array{Int64,1}: - 1 - 2 - 3 - 4 - 5 - 6 -``` - -Use [`append!`](@ref) to add all the elements of another collection to -`collection`. The result of the preceding example is equivalent to `append!([1, 2, 3], [4, -5, 6])`. -""" -push! - -""" - promote(xs...) - -Convert all arguments to their common promotion type (if any), and return them all (as a tuple). - -# Examples -```jldoctest -julia> promote(Int8(1), Float16(4.5), Float32(4.1)) -(1.0f0, 4.5f0, 4.1f0) -``` -""" -promote - -""" - fd(stream) - -Returns the file descriptor backing the stream or file. Note that this function only applies -to synchronous `File`'s and `IOStream`'s not to any of the asynchronous streams. -""" -fd - -""" - ones([A::AbstractArray,] [T=eltype(A)::Type,] [dims=size(A)::Tuple]) - -Create an array of all ones with the same layout as `A`, element type `T` and size `dims`. -The `A` argument can be skipped, which behaves like `Array{Float64,0}()` was passed. -For convenience `dims` may also be passed in variadic form. - -# Examples -```jldoctest -julia> ones(Complex128, 2, 3) -2×3 Array{Complex{Float64},2}: - 1.0+0.0im 1.0+0.0im 1.0+0.0im - 1.0+0.0im 1.0+0.0im 1.0+0.0im - -julia> ones(1,2) -1×2 Array{Float64,2}: - 1.0 1.0 - -julia> A = [1 2; 3 4] -2×2 Array{Int64,2}: - 1 2 - 3 4 - -julia> ones(A) -2×2 Array{Int64,2}: - 1 1 - 1 1 - -julia> ones(A, Float64) -2×2 Array{Float64,2}: - 1.0 1.0 - 1.0 1.0 - -julia> ones(A, Bool, (3,)) -3-element Array{Bool,1}: - true - true - true -``` -See also [`zeros`](@ref), [`similar`](@ref). -""" -ones - -""" - randsubseq!(S, A, p) - -Like [`randsubseq`](@ref), but the results are stored in `S` -(which is resized as needed). -""" -randsubseq! - -""" - /(x, y) - -Right division operator: multiplication of `x` by the inverse of `y` on the right. Gives -floating-point results for integer arguments. -""" -Base.:(/) - -""" - dump(x) - -Show every part of the representation of a value. -""" -dump - -""" - tuple(xs...) - -Construct a tuple of the given objects. - -# Examples -```jldoctest -julia> tuple(1, 'a', pi) -(1, 'a', π = 3.1415926535897...) -``` -""" -tuple - -""" - eachmatch(r::Regex, s::AbstractString[, overlap::Bool=false]) - -Search for all matches of a the regular expression `r` in `s` and return a iterator over the -matches. If overlap is `true`, the matching sequences are allowed to overlap indices in the -original string, otherwise they must be from distinct character ranges. -""" -eachmatch - -""" - truncate(file,n) - -Resize the file or buffer given by the first argument to exactly `n` bytes, filling -previously unallocated space with '\\0' if the file or buffer is grown. -""" -truncate - -""" - exp10(x) - -Compute ``10^x``. - -# Examples -```jldoctest -julia> exp10(2) -100.0 - -julia> exp10(0.2) -1.5848931924611136 -``` -""" -exp10 - -""" - select(v, k, [by=,] [lt=,] [rev=false]) - -Variant of [`select!`](@ref) which copies `v` before partially sorting it, thereby returning the -same thing as `select!` but leaving `v` unmodified. -""" -select - -""" - accept(server[,client]) - -Accepts a connection on the given server and returns a connection to the client. An -uninitialized client stream may be provided, in which case it will be used instead of -creating a new stream. -""" -accept - -""" - Mmap.Anonymous(name, readonly, create) - -Create an `IO`-like object for creating zeroed-out mmapped-memory that is not tied to a file -for use in `Mmap.mmap`. Used by `SharedArray` for creating shared memory arrays. -""" -Mmap.Anonymous - -""" - floor([T,] x, [digits, [base]]) - -`floor(x)` returns the nearest integral value of the same type as `x` that is less than or -equal to `x`. - -`floor(T, x)` converts the result to type `T`, throwing an `InexactError` if the value is -not representable. - -`digits` and `base` work as for [`round`](@ref). -""" -floor - -""" - reverse(v [, start=1 [, stop=length(v) ]] ) - -Return a copy of `v` reversed from start to stop. - -# Examples -```jldoctest -julia> A = collect(1:5) -5-element Array{Int64,1}: - 1 - 2 - 3 - 4 - 5 - -julia> reverse(A) -5-element Array{Int64,1}: - 5 - 4 - 3 - 2 - 1 - -julia> reverse(A, 1, 4) -5-element Array{Int64,1}: - 4 - 3 - 2 - 1 - 5 - -julia> reverse(A, 3, 5) -5-element Array{Int64,1}: - 1 - 2 - 5 - 4 - 3 -``` -""" -reverse - -""" - reverse!(v [, start=1 [, stop=length(v) ]]) -> v - -In-place version of [`reverse`](@ref). -""" -reverse! - -""" - append!(collection, collection2) -> collection. - -Add the elements of `collection2` to the end of `collection`. - -# Examples -```jldoctest -julia> append!([1],[2,3]) -3-element Array{Int64,1}: - 1 - 2 - 3 - -julia> append!([1, 2, 3], [4, 5, 6]) -6-element Array{Int64,1}: - 1 - 2 - 3 - 4 - 5 - 6 -``` - -Use [`push!`](@ref) to add individual items to `collection` which are not already -themselves in another collection. The result is of the preceding example is equivalent to -`push!([1, 2, 3], 4, 5, 6)`. -""" -append! - -""" - skip(s, offset) - -Seek a stream relative to the current position. -""" -skip - -""" - copysign(x, y) -> z - -Return `z` which has the magnitude of `x` and the same sign as `y`. - -# Examples -```jldoctest -julia> copysign(1, -2) --1 - -julia> copysign(-1, 2) -1 -``` -""" -copysign - -""" - getfield(value, name::Symbol) - -Extract a named field from a `value` of composite type. The syntax `a.b` calls -`getfield(a, :b)`. - -# Examples -```jldoctest -julia> a = 1//2 -1//2 - -julia> getfield(a, :num) -1 -``` -""" -getfield - -""" - select!(v, k, [by=,] [lt=,] [rev=false]) - -Partially sort the vector `v` in place, according to the order specified by `by`, `lt` and -`rev` so that the value at index `k` (or range of adjacent values if `k` is a range) occurs -at the position where it would appear if the array were fully sorted via a non-stable -algorithm. If `k` is a single index, that value is returned; if `k` is a range, an array of -values at those indices is returned. Note that `select!` does not fully sort the input -array. - -# Examples -```jldoctest -julia> a = [1, 2, 4, 3, 4] -5-element Array{Int64,1}: - 1 - 2 - 4 - 3 - 4 - -julia> select!(a, 4) -4 - -julia> a -5-element Array{Int64,1}: - 1 - 2 - 3 - 4 - 4 - -julia> a = [1, 2, 4, 3, 4] -5-element Array{Int64,1}: - 1 - 2 - 4 - 3 - 4 - -julia> select!(a, 4, rev=true) -2 - -julia> a -5-element Array{Int64,1}: - 4 - 4 - 3 - 2 - 1 -``` -""" -select! - -""" - union(s1,s2...) - ∪(s1,s2...) - -Construct the union of two or more sets. Maintains order with arrays. - -# Examples -```jldoctest -julia> union([1, 2], [3, 4]) -4-element Array{Int64,1}: - 1 - 2 - 3 - 4 - -julia> union([1, 2], [2, 4]) -3-element Array{Int64,1}: - 1 - 2 - 4 - -julia> union([4, 2], [1, 2]) -3-element Array{Int64,1}: - 4 - 2 - 1 -``` -""" -union - -""" - realmax(T) - -The highest finite value representable by the given floating-point DataType `T`. - -# Examples -```jldoctest -julia> realmax(Float16) -Float16(6.55e4) - -julia> realmax(Float32) -3.4028235f38 -``` -""" -realmax - -""" - serialize(stream, value) - -Write an arbitrary value to a stream in an opaque format, such that it can be read back by -[`deserialize`](@ref). The read-back value will be as identical as possible to the original. In -general, this process will not work if the reading and writing are done by different -versions of Julia, or an instance of Julia with a different system image. `Ptr` values are -serialized as all-zero bit patterns (`NULL`). -""" -serialize - -""" - typemin(T) - -The lowest value representable by the given (real) numeric DataType `T`. - -# Examples -```jldoctest -julia> typemin(Float16) --Inf16 - -julia> typemin(Float32) --Inf32 -``` -""" -typemin - -""" - typeof(x) - -Get the concrete type of `x`. -""" -typeof - -""" - trunc([T,] x, [digits, [base]]) - -`trunc(x)` returns the nearest integral value of the same type as `x` whose absolute value -is less than or equal to `x`. - -`trunc(T, x)` converts the result to type `T`, throwing an `InexactError` if the value is -not representable. - -`digits` and `base` work as for [`round`](@ref). -""" -trunc - -""" - unsafe_convert(T,x) - -Convert `x` to a C argument of type `T` -where the input `x` must be the return value of `cconvert(T, ...)`. - -In cases where [`convert`](@ref) would need to take a Julia object -and turn it into a `Ptr`, this function should be used to define and perform -that conversion. - -Be careful to ensure that a Julia reference to `x` exists as long as the result of this -function will be used. Accordingly, the argument `x` to this function should never be an -expression, only a variable name or field reference. For example, `x=a.b.c` is acceptable, -but `x=[a,b,c]` is not. - -The `unsafe` prefix on this function indicates that using the result of this function after -the `x` argument to this function is no longer accessible to the program may cause undefined -behavior, including program corruption or segfaults, at any later time. - -See also [`cconvert`](@ref) -""" -unsafe_convert - -""" - seek(s, pos) - -Seek a stream to the given position. -""" -seek - -""" - cglobal((symbol, library) [, type=Void]) - -Obtain a pointer to a global variable in a C-exported shared library, specified exactly as -in [`ccall`](@ref). -Returns a `Ptr{Type}`, defaulting to `Ptr{Void}` if no `Type` argument is -supplied. -The values can be read or written by [`unsafe_load`](@ref) or [`unsafe_store!`](@ref), -respectively. -""" -cglobal - -""" - endof(collection) -> Integer - -Returns the last index of the collection. - -# Examples -```jldoctest -julia> endof([1,2,4]) -3 -``` -""" -endof - -""" - next(iter, state) -> item, state - -For a given iterable object and iteration state, return the current item and the next iteration state. - -# Examples -```jldoctest -julia> next(1:5, 3) -(3, 4) - -julia> next(1:5, 5) -(5, 6) -``` -""" -next - -""" - sizehint!(s, n) - -Suggest that collection `s` reserve capacity for at least `n` elements. This can improve performance. -""" -sizehint! - -""" - finalize(x) - -Immediately run finalizers registered for object `x`. -""" -finalize - -""" - parse(str, start; greedy=true, raise=true) - -Parse the expression string and return an expression (which could later be passed to eval -for execution). `start` is the index of the first character to start parsing. If `greedy` is -`true` (default), `parse` will try to consume as much input as it can; otherwise, it will -stop as soon as it has parsed a valid expression. Incomplete but otherwise syntactically -valid expressions will return `Expr(:incomplete, "(error message)")`. If `raise` is `true` -(default), syntax errors other than incomplete expressions will raise an error. If `raise` -is `false`, `parse` will return an expression that will raise an error upon evaluation. - -```jldoctest -julia> parse("x = 3, y = 5", 7) -(:(y = 5), 13) - -julia> parse("x = 3, y = 5", 5) -(:((3, y) = 5), 13) -``` -""" -parse(str, start) - -""" - parse(str; raise=true) - -Parse the expression string greedily, returning a single expression. An error is thrown if -there are additional characters after the first expression. If `raise` is `true` (default), -syntax errors will raise an error; otherwise, `parse` will return an expression that will -raise an error upon evaluation. - -```jldoctest -julia> parse("x = 3") -:(x = 3) - -julia> parse("x = ") -:($(Expr(:incomplete, "incomplete: premature end of input"))) - -julia> parse("1.0.2") -ERROR: ParseError("invalid numeric constant \\\"1.0.\\\"") -Stacktrace: -[...] - -julia> parse("1.0.2"; raise = false) -:($(Expr(:error, "invalid numeric constant \"1.0.\""))) -``` -""" -parse(str) - -""" - parse(type, str, [base]) - -Parse a string as a number. If the type is an integer type, then a base can be specified -(the default is 10). If the type is a floating point type, the string is parsed as a decimal -floating point number. If the string does not contain a valid number, an error is raised. - -```jldoctest -julia> parse(Int, "1234") -1234 - -julia> parse(Int, "1234", 5) -194 - -julia> parse(Int, "afc", 16) -2812 - -julia> parse(Float64, "1.2e-3") -0.0012 -``` -""" -parse(T::Type, str, base=Int) - -""" - position(s) - -Get the current position of a stream. -""" -position - -""" - selectperm(v, k, [alg=,] [by=,] [lt=,] [rev=false]) - -Return a partial permutation of the vector `v`, according to the order specified by -`by`, `lt` and `rev`, so that `v[output]` returns the first `k` (or range of adjacent values -if `k` is a range) values of a fully sorted version of `v`. If `k` is a single index -(Integer), an array of the first `k` indices is returned; if `k` is a range, an array of -those indices is returned. Note that the handling of integer values for `k` is different -from [`select`](@ref) in that it returns a vector of `k` elements instead of just the `k` th -element. Also note that this is equivalent to, but more efficient than, calling -`sortperm(...)[k]`. -""" -selectperm - -""" - reinterpret(type, A) - -Change the type-interpretation of a block of memory. -For arrays, this constructs an array with the same binary data as the given -array, but with the specified element type. -For example, -`reinterpret(Float32, UInt32(7))` interprets the 4 bytes corresponding to `UInt32(7)` as a -[`Float32`](@ref). - -!!! warning - - It is not allowed to `reinterpret` an array to an element type with a larger alignment then - the alignment of the array. For a normal `Array`, this is the alignment of its element type. - For a reinterpreted array, this is the alignment of the `Array` it was reinterpreted from. - For example, `reinterpret(UInt32, UInt8[0, 0, 0, 0])` is not allowed but - `reinterpret(UInt32, reinterpret(UInt8, Float32[1.0]))` is allowed. - -# Examples -```jldoctest -julia> reinterpret(Float32, UInt32(7)) -1.0f-44 - -julia> reinterpret(Float32, UInt32[1 2 3 4 5]) -1×5 Array{Float32,2}: - 1.4013f-45 2.8026f-45 4.2039f-45 5.60519f-45 7.00649f-45 -``` -""" -reinterpret - -""" - bswap(n) - -Byte-swap an integer. Flip the bits of its binary representation. - -# Examples -```jldoctest -julia> a = bswap(4) -288230376151711744 - -julia> bswap(a) -4 - -julia> bin(1) -"1" - -julia> bin(bswap(1)) -"100000000000000000000000000000000000000000000000000000000" -``` -""" -bswap - -""" - delete!(collection, key) - -Delete the mapping for the given key in a collection, and return the collection. - -# Examples -```jldoctest -julia> d = Dict("a"=>1, "b"=>2) -Dict{String,Int64} with 2 entries: - "b" => 2 - "a" => 1 - -julia> delete!(d, "b") -Dict{String,Int64} with 1 entry: - "a" => 1 -``` -""" -delete! - -""" - big(x) - -Convert a number to a maximum precision representation (typically [`BigInt`](@ref) or -`BigFloat`). See [`BigFloat`](@ref) for information about some pitfalls with floating-point numbers. -""" -big - -""" - typejoin(T, S) - -Compute a type that contains both `T` and `S`. -""" -typejoin - -""" - selectperm!(ix, v, k, [alg=,] [by=,] [lt=,] [rev=false,] [initialized=false]) - -Like [`selectperm`](@ref), but accepts a preallocated index vector `ix`. If `initialized` is `false` -(the default), ix is initialized to contain the values `1:length(ix)`. -""" -selectperm! - -""" - precompile(f,args::Tuple{Vararg{Any}}) - -Compile the given function `f` for the argument tuple (of types) `args`, but do not execute it. -""" -precompile - -""" - get(collection, key, default) - -Return the value stored for the given key, or the given default value if no mapping for the -key is present. - -# Examples -```jldoctest -julia> d = Dict("a"=>1, "b"=>2); - -julia> get(d, "a", 3) -1 - -julia> get(d, "c", 3) -3 -``` -""" -get(collection,key,default) - -""" - get(f::Function, collection, key) - -Return the value stored for the given key, or if no mapping for the key is present, return -`f()`. Use [`get!`](@ref) to also store the default value in the dictionary. - -This is intended to be called using `do` block syntax - -```julia -get(dict, key) do - # default value calculated here - time() -end -``` -""" -get - -""" - Mmap.sync!(array) - -Forces synchronization between the in-memory version of a memory-mapped `Array` or -`BitArray` and the on-disk version. -""" -Mmap.sync! - -""" - hash(x[, h::UInt]) - -Compute an integer hash code such that `isequal(x,y)` implies `hash(x)==hash(y)`. The -optional second argument `h` is a hash code to be mixed with the result. - -New types should implement the 2-argument form, typically by calling the 2-argument `hash` -method recursively in order to mix hashes of the contents with each other (and with `h`). -Typically, any type that implements `hash` should also implement its own `==` (hence -`isequal`) to guarantee the property mentioned above. -""" -hash - -""" - read(stream::IO, T) - -Read a single value of type `T` from `stream`, in canonical binary representation. - - read(stream::IO, String) - -Read the entirety of `stream`, as a String. -""" -read(stream, t) - -""" - shift!(collection) -> item - -Remove the first `item` from `collection`. - -# Examples -```jldoctest -julia> A = [1, 2, 3, 4, 5, 6] -6-element Array{Int64,1}: - 1 - 2 - 3 - 4 - 5 - 6 - -julia> shift!(A) -1 - -julia> A -5-element Array{Int64,1}: - 2 - 3 - 4 - 5 - 6 -``` -""" -shift! - -""" - spawn(command) - -Run a command object asynchronously, returning the resulting `Process` object. -""" -spawn - -""" - isdefined(m::Module, s::Symbol) - isdefined(object, s::Symbol) - isdefined(object, index::Int) - -Tests whether an assignable location is defined. The arguments can be a module and a symbol -or a composite object and field name (as a symbol) or index. -""" -isdefined - -""" - wait([x]) - -Block the current task until some event occurs, depending on the type of the argument: - -* [`RemoteChannel`](@ref) : Wait for a value to become available on the specified remote - channel. -* [`Future`](@ref) : Wait for a value to become available for the specified future. -* [`Channel`](@ref): Wait for a value to be appended to the channel. -* [`Condition`](@ref): Wait for [`notify`](@ref) on a condition. -* `Process`: Wait for a process or process chain to exit. The `exitcode` field of a process - can be used to determine success or failure. -* [`Task`](@ref): Wait for a `Task` to finish, returning its result value. If the task fails - with an exception, the exception is propagated (re-thrown in the task that called `wait`). -* `RawFD`: Wait for changes on a file descriptor (see [`poll_fd`](@ref) for keyword - arguments and return code) - -If no argument is passed, the task blocks for an undefined period. A task can only be -restarted by an explicit call to [`schedule`](@ref) or [`yieldto`](@ref). - -Often `wait` is called within a `while` loop to ensure a waited-for condition is met before -proceeding. -""" -wait - -""" - copy(x) - -Create a shallow copy of `x`: the outer structure is copied, but not all internal values. -For example, copying an array produces a new array with identically-same elements as the -original. -""" -copy - -""" - isempty(collection) -> Bool - -Determine whether a collection is empty (has no elements). - -# Examples -```jldoctest -julia> isempty([]) -true - -julia> isempty([1 2 3]) -false -``` -""" -isempty - -""" - typemax(T) - -The highest value representable by the given (real) numeric `DataType`. -""" -typemax - -""" - ==(x, y) - -Generic equality operator, giving a single [`Bool`](@ref) result. Falls back to `===`. -Should be implemented for all types with a notion of equality, based on the abstract value -that an instance represents. For example, all numeric types are compared by numeric value, -ignoring type. Strings are compared as sequences of characters, ignoring encoding. - -Follows IEEE semantics for floating-point numbers. - -Collections should generally implement `==` by calling `==` recursively on all contents. - -New numeric types should implement this function for two arguments of the new type, and -handle comparison to other types via promotion rules where possible. -""" -Base.:(==) - -""" - seekstart(s) - -Seek a stream to its beginning. -""" -seekstart - -""" - show(stream, mime, x) - -The [`display`](@ref) functions ultimately call `show` in order to write an object `x` as a -given `mime` type to a given I/O `stream` (usually a memory buffer), if possible. In order -to provide a rich multimedia representation of a user-defined type `T`, it is only necessary -to define a new `show` method for `T`, via: `show(stream, ::MIME"mime", x::T) = ...`, -where `mime` is a MIME-type string and the function body calls `write` (or similar) to write -that representation of `x` to `stream`. (Note that the `MIME""` notation only supports -literal strings; to construct `MIME` types in a more flexible manner use -`MIME{Symbol("")}`.) - -For example, if you define a `MyImage` type and know how to write it to a PNG file, you -could define a function `show(stream, ::MIME"image/png", x::MyImage) = ...` to allow -your images to be displayed on any PNG-capable `Display` (such as IJulia). As usual, be sure -to `import Base.show` in order to add new methods to the built-in Julia function -`show`. - -The default MIME type is `MIME"text/plain"`. There is a fallback definition for `text/plain` -output that calls `show` with 2 arguments. Therefore, this case should be handled by -defining a 2-argument `show(stream::IO, x::MyType)` method. - -Technically, the `MIME"mime"` macro defines a singleton type for the given `mime` string, -which allows us to exploit Julia's dispatch mechanisms in determining how to display objects -of any given type. - -The first argument to `show` can be an [`IOContext`](@ref) specifying output format properties. -See [`IOContext`](@ref) for details. -""" -show(stream, mime, x) - -""" - isless(x, y) - -Test whether `x` is less than `y`, according to a canonical total order. Values that are -normally unordered, such as `NaN`, are ordered in an arbitrary but consistent fashion. This -is the default comparison used by [`sort`](@ref). Non-numeric types with a canonical total order -should implement this function. Numeric types only need to implement it if they have special -values such as `NaN`. -""" -isless - -""" - error(message::AbstractString) - -Raise an `ErrorException` with the given message. -""" -error - -""" - readcsv(source, [T::Type]; options...) - -Equivalent to [`readdlm`](@ref) with `delim` set to comma, and type optionally defined by `T`. -""" -readcsv - -""" - gc() - -Perform garbage collection. This should not generally be used. -""" -gc - -""" - unsafe_trunc(T, x) - -`unsafe_trunc(T, x)` returns the nearest integral value of type `T` whose absolute value is -less than or equal to `x`. If the value is not representable by `T`, an arbitrary value will -be returned. -""" -unsafe_trunc - -""" - parent(A) - -Returns the "parent array" of an array view type (e.g., `SubArray`), or the array itself if -it is not a view. - -# Examples -```jldoctest -julia> a = [1 2; 3 4] -2×2 Array{Int64,2}: - 1 2 - 3 4 - -julia> s_a = Symmetric(a) -2×2 Symmetric{Int64,Array{Int64,2}}: - 1 2 - 2 4 - -julia> parent(s_a) -2×2 Array{Int64,2}: - 1 2 - 3 4 -``` -""" -parent - -""" - gc_enable(on::Bool) - -Control whether garbage collection is enabled using a boolean argument (`true` for enabled, -`false` for disabled). Returns previous GC state. Disabling garbage collection should be -used only with extreme caution, as it can cause memory use to grow without bound. -""" -gc_enable - -""" - object_id(x) - -Get a hash value for `x` based on object identity. `object_id(x)==object_id(y)` if `x === y`. -""" -object_id - -""" - cat(dims, A...) - -Concatenate the input arrays along the specified dimensions in the iterable `dims`. For -dimensions not in `dims`, all input arrays should have the same size, which will also be the -size of the output array along that dimension. For dimensions in `dims`, the size of the -output array is the sum of the sizes of the input arrays along that dimension. If `dims` is -a single number, the different arrays are tightly stacked along that dimension. If `dims` is -an iterable containing several dimensions, this allows one to construct block diagonal -matrices and their higher-dimensional analogues by simultaneously increasing several -dimensions for every new input array and putting zero blocks elsewhere. For example, -`cat([1,2], matrices...)` builds a block diagonal matrix, i.e. a block matrix with -`matrices[1]`, `matrices[2]`, ... as diagonal blocks and matching zero blocks away from the -diagonal. -""" -cat - -""" - show(x) - -Write an informative text representation of a value to the current output stream. New types -should overload `show(io, x)` where the first argument is a stream. The representation used -by `show` generally includes Julia-specific formatting and type information. -""" -show(x) - -""" - finalizer(x, f) - -Register a function `f(x)` to be called when there are no program-accessible references to -`x`. The type of `x` must be a `mutable struct`, otherwise the behavior of this function is -unpredictable. -""" -finalizer - -""" - setfield!(value, name::Symbol, x) - -Assign `x` to a named field in `value` of composite type. The syntax `a.b = c` calls -`setfield!(a, :b, c)`. -""" -setfield! - -""" -``` -*(x, y...) -``` - -Multiplication operator. `x*y*z*...` calls this function with all arguments, i.e. `*(x, y, z, ...)`. -""" -Base.:(*)(x, y...) - -""" - time() - -Get the system time in seconds since the epoch, with fairly high (typically, microsecond) resolution. -""" -time() - -""" - ismatch(r::Regex, s::AbstractString) -> Bool - -Test whether a string contains a match of the given regular expression. -""" -ismatch - -""" - matchall(r::Regex, s::AbstractString[, overlap::Bool=false]) -> Vector{AbstractString} - -Return a vector of the matching substrings from [`eachmatch`](@ref). -""" -matchall - -""" - get!(collection, key, default) - -Return the value stored for the given key, or if no mapping for the key is present, store -`key => default`, and return `default`. - -# Examples -```jldoctest -julia> d = Dict("a"=>1, "b"=>2, "c"=>3); - -julia> get!(d, "a", 5) -1 - -julia> get!(d, "d", 4) -4 - -julia> d -Dict{String,Int64} with 4 entries: - "c" => 3 - "b" => 2 - "a" => 1 - "d" => 4 -``` -""" -get!(collection,key,default) - -""" - get!(f::Function, collection, key) - -Return the value stored for the given key, or if no mapping for the key is present, store -`key => f()`, and return `f()`. - -This is intended to be called using `do` block syntax: -```julia -get!(dict, key) do - # default value calculated here - time() -end -``` -""" -get!(f::Function,collection,key) -:@assert - -""" - deserialize(stream) - -Read a value written by [`serialize`](@ref). `deserialize` assumes the binary data read from -`stream` is correct and has been serialized by a compatible implementation of [`serialize`](@ref). -It has been designed with simplicity and performance as a goal and does not validate -the data read. Malformed data can result in process termination. The caller has to ensure -the integrity and correctness of data read from `stream`. -""" -deserialize - -""" - length(collection) -> Integer - -Return the number of elements in the collection. - -Use [`endof`](@ref) to get the last valid index of an indexable collection. - -# Examples -```jldoctest -julia> length(1:5) -5 - -julia> length([1, 2, 3, 4]) -4 - -julia> length([1 2; 3 4]) -4 -``` -""" -length(collection) - -""" - issubnormal(f) -> Bool - -Test whether a floating point number is subnormal. -""" -issubnormal - -""" - NullException() - -An attempted access to a [`Nullable`](@ref) with no defined value. - -# Examples -```jldoctest -julia> a = Nullable{Int}() -Nullable{Int64}() - -julia> get(a) -ERROR: NullException() -Stacktrace: - [1] get(::Nullable{Int64}) at ./nullable.jl:92 -``` -""" -NullException - -""" - intersect(s1,s2...) - ∩(s1,s2) - -Construct the intersection of two or more sets. -Maintains order and multiplicity of the first argument for arrays and ranges. - -# Examples -```jldoctest -julia> intersect([1, 2, 3], [3, 4, 5]) -1-element Array{Int64,1}: - 3 - -julia> intersect([1, 4, 4, 5, 6], [4, 6, 6, 7, 8]) -3-element Array{Int64,1}: - 4 - 4 - 6 -``` -""" -intersect - -""" - promote_rule(type1, type2) - -Specifies what type should be used by [`promote`](@ref) when given values of types `type1` and -`type2`. This function should not be called directly, but should have definitions added to -it for new types as appropriate. -""" -promote_rule - -""" - match(r::Regex, s::AbstractString[, idx::Integer[, addopts]]) - -Search for the first match of the regular expression `r` in `s` and return a `RegexMatch` -object containing the match, or nothing if the match failed. The matching substring can be -retrieved by accessing `m.match` and the captured sequences can be retrieved by accessing -`m.captures` The optional `idx` argument specifies an index at which to start the search. -""" -match - -""" - start(iter) -> state - -Get initial iteration state for an iterable object. - -# Examples -```jldoctest -julia> start(1:5) -1 - -julia> start([1;2;3]) -1 - -julia> start([4;2;3]) -1 -``` -""" -start - -""" - done(iter, state) -> Bool - -Test whether we are done iterating. - -# Examples -```jldoctest -julia> done(1:5, 3) -false - -julia> done(1:5, 5) -false - -julia> done(1:5, 6) -true -``` -""" -done - -""" - convert(T, x) - -Convert `x` to a value of type `T`. - -If `T` is an [`Integer`](@ref) type, an [`InexactError`](@ref) will be raised if `x` -is not representable by `T`, for example if `x` is not integer-valued, or is outside the -range supported by `T`. - -# Examples -```jldoctest -julia> convert(Int, 3.0) -3 - -julia> convert(Int, 3.5) -ERROR: InexactError: convert(Int64, 3.5) -Stacktrace: - [1] convert(::Type{Int64}, ::Float64) at ./float.jl:680 -``` - -If `T` is a [`AbstractFloat`](@ref) or [`Rational`](@ref) type, -then it will return the closest value to `x` representable by `T`. - -```jldoctest -julia> x = 1/3 -0.3333333333333333 - -julia> convert(Float32, x) -0.33333334f0 - -julia> convert(Rational{Int32}, x) -1//3 - -julia> convert(Rational{Int64}, x) -6004799503160661//18014398509481984 -``` - -If `T` is a collection type and `x` a collection, the result of `convert(T, x)` may alias -`x`. -```jldoctest -julia> x = Int[1,2,3]; - -julia> y = convert(Vector{Int}, x); - -julia> y === x -true -``` -Similarly, if `T` is a composite type and `x` a related instance, the result of -`convert(T, x)` may alias part or all of `x`. -```jldoctest -julia> x = speye(5); - -julia> typeof(x) -SparseMatrixCSC{Float64,Int64} - -julia> y = convert(SparseMatrixCSC{Float64,Int64}, x); - -julia> z = convert(SparseMatrixCSC{Float32,Int64}, y); - -julia> y === x -true - -julia> z === x -false - -julia> z.colptr === x.colptr -true -``` -""" -convert - -""" - fma(x, y, z) - -Computes `x*y+z` without rounding the intermediate result `x*y`. On some systems this is -significantly more expensive than `x*y+z`. `fma` is used to improve accuracy in certain -algorithms. See [`muladd`](@ref). -""" -fma - -""" - copy!(dest, do, src, so, N) - -Copy `N` elements from collection `src` starting at offset `so`, to array `dest` starting at -offset `do`. Returns `dest`. -""" -copy!(dest,d,src,so,N) - -""" - +(x, y...) - -Addition operator. `x+y+z+...` calls this function with all arguments, i.e. `+(x, y, z, ...)`. -""" -+ - -""" - setindex!(collection, value, key...) - -Store the given value at the given key or index within a collection. The syntax `a[i,j,...] = -x` is converted by the compiler to `(setindex!(a, x, i, j, ...); x)`. -""" -setindex!(collection,value,key...) - -""" - zeros([A::AbstractArray,] [T=eltype(A)::Type,] [dims=size(A)::Tuple]) - -Create an array of all zeros with the same layout as `A`, element type `T` and size `dims`. -The `A` argument can be skipped, which behaves like `Array{Float64,0}()` was passed. -For convenience `dims` may also be passed in variadic form. - -# Examples -```jldoctest -julia> zeros(1) -1-element Array{Float64,1}: - 0.0 - -julia> zeros(Int8, 2, 3) -2×3 Array{Int8,2}: - 0 0 0 - 0 0 0 - -julia> A = [1 2; 3 4] -2×2 Array{Int64,2}: - 1 2 - 3 4 - -julia> zeros(A) -2×2 Array{Int64,2}: - 0 0 - 0 0 - -julia> zeros(A, Float64) -2×2 Array{Float64,2}: - 0.0 0.0 - 0.0 0.0 - -julia> zeros(A, Bool, (3,)) -3-element Array{Bool,1}: - false - false - false -``` -See also [`ones`](@ref), [`similar`](@ref). -""" -zeros - -""" - Symbol(x...) -> Symbol - -Create a `Symbol` by concatenating the string representations of the arguments together. -""" -Symbol - -""" - isvalid(value) -> Bool - -Returns `true` if the given value is valid for its type, which currently can be either -`Char` or `String`. -""" -isvalid(value) - -""" - isvalid(T, value) -> Bool - -Returns `true` if the given value is valid for that type. Types currently can -be either `Char` or `String`. Values for `Char` can be of type `Char` or [`UInt32`](@ref). -Values for `String` can be of that type, or `Vector{UInt8}`. -""" -isvalid(T,value) - -""" - pop!(collection, key[, default]) - -Delete and return the mapping for `key` if it exists in `collection`, otherwise return -`default`, or throw an error if `default` is not specified. - -# Examples -```jldoctest -julia> d = Dict("a"=>1, "b"=>2, "c"=>3); - -julia> pop!(d, "a") -1 - -julia> pop!(d, "d") -ERROR: KeyError: key "d" not found -Stacktrace: - [1] pop!(::Dict{String,Int64}, ::String) at ./dict.jl:539 - -julia> pop!(d, "e", 4) -4 -``` -""" -pop!(collection,key,default) - -""" - pop!(collection) -> item - -Remove an item in `collection` and return it. If `collection` is an -ordered container, the last item is returned. - -# Examples -```jldoctest -julia> A=[1, 2, 3] -3-element Array{Int64,1}: - 1 - 2 - 3 - -julia> pop!(A) -3 - -julia> A -2-element Array{Int64,1}: - 1 - 2 - -julia> S = Set([1, 2]) -Set([2, 1]) - -julia> pop!(S) -2 - -julia> S -Set([1]) - -julia> pop!(Dict(1=>2)) -1 => 2 -``` -""" -pop!(collection) - -""" - seekend(s) - -Seek a stream to its end. -""" -seekend - -""" - Vector{T}(n) - -Construct an uninitialized [`Vector{T}`](@ref) of length `n`. - -# Examples -```julia-repl -julia> Vector{Float64}(3) -3-element Array{Float64,1}: - 6.90966e-310 - 6.90966e-310 - 6.90966e-310 -``` -""" -Vector{T}(n) - -""" - Matrix{T}(m, n) - -Construct an uninitialized [`Matrix{T}`](@ref) of size `m`×`n`. - -# Examples -```julia-repl -julia> Matrix{Float64}(2, 3) -2×3 Array{Float64,2}: - 6.93517e-310 6.93517e-310 6.93517e-310 - 6.93517e-310 6.93517e-310 1.29396e-320 -``` -""" -Matrix{T}(m, n) - -""" - Array{T}(dims) - Array{T,N}(dims) - -Construct an uninitialized `N`-dimensional [`Array`](@ref) -containing elements of type `T`. `N` can either be supplied explicitly, -as in `Array{T,N}(dims)`, or be determined by the length or number of `dims`. -`dims` may be a tuple or a series of integer arguments corresponding to the lengths -in each dimension. If the rank `N` is supplied explicitly, then it must -match the length or number of `dims`. - -# Examples -```julia-repl -julia> A = Array{Float64,2}(2, 3) # N given explicitly -2×3 Array{Float64,2}: - 6.90198e-310 6.90198e-310 6.90198e-310 - 6.90198e-310 6.90198e-310 0.0 - -julia> B = Array{Float64}(2) # N determined by the input -2-element Array{Float64,1}: - 1.87103e-320 - 0.0 -``` -""" -Array{T,N}(dims) diff --git a/base/error.jl b/base/error.jl index 06508810e00b0..170a5252cefc5 100644 --- a/base/error.jl +++ b/base/error.jl @@ -25,6 +25,11 @@ throw ## native julia error handling ## +""" + error(message::AbstractString) + +Raise an `ErrorException` with the given message. +""" error(s::AbstractString) = throw(ErrorException(s)) """ diff --git a/base/essentials.jl b/base/essentials.jl index 95f1dc75c86bb..183a13615d31b 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -61,6 +61,77 @@ macro _propagate_inbounds_meta() Expr(:meta, :inline, :propagate_inbounds) end +""" + convert(T, x) + +Convert `x` to a value of type `T`. + +If `T` is an [`Integer`](@ref) type, an [`InexactError`](@ref) will be raised if `x` +is not representable by `T`, for example if `x` is not integer-valued, or is outside the +range supported by `T`. + +# Examples +```jldoctest +julia> convert(Int, 3.0) +3 + +julia> convert(Int, 3.5) +ERROR: InexactError: convert(Int64, 3.5) +Stacktrace: + [1] convert(::Type{Int64}, ::Float64) at ./float.jl:680 +``` + +If `T` is a [`AbstractFloat`](@ref) or [`Rational`](@ref) type, +then it will return the closest value to `x` representable by `T`. + +```jldoctest +julia> x = 1/3 +0.3333333333333333 + +julia> convert(Float32, x) +0.33333334f0 + +julia> convert(Rational{Int32}, x) +1//3 + +julia> convert(Rational{Int64}, x) +6004799503160661//18014398509481984 +``` + +If `T` is a collection type and `x` a collection, the result of `convert(T, x)` may alias +`x`. +```jldoctest +julia> x = Int[1,2,3]; + +julia> y = convert(Vector{Int}, x); + +julia> y === x +true +``` +Similarly, if `T` is a composite type and `x` a related instance, the result of +`convert(T, x)` may alias part or all of `x`. +```jldoctest +julia> x = speye(5); + +julia> typeof(x) +SparseMatrixCSC{Float64,Int64} + +julia> y = convert(SparseMatrixCSC{Float64,Int64}, x); + +julia> z = convert(SparseMatrixCSC{Float32,Int64}, y); + +julia> y === x +true + +julia> z === x +false + +julia> z.colptr === x.colptr +true +``` +""" +function convert end + convert(::Type{Any}, @nospecialize(x)) = x convert(::Type{T}, x::T) where {T} = x @@ -201,7 +272,12 @@ convert(::Type{T}, x::Tuple{Any, Vararg{Any}}) where {T<:Tuple} = #convert(::Type{Tuple{}}, ::Tuple{}) = () #convert(::Type{Tuple{Vararg{S}}} where S, ::Tuple{}) = () -oftype(x, c) = convert(typeof(x), c) +""" + oftype(x, y) + +Convert `y` to the type of `x` (`convert(typeof(x), y)`). +""" +oftype(x, y) = convert(typeof(x), y) unsigned(x::Int) = reinterpret(UInt, x) signed(x::UInt) = reinterpret(Int, x) @@ -211,16 +287,83 @@ ptr_arg_cconvert(::Type{Ptr{T}}, x) where {T} = cconvert(T, x) ptr_arg_unsafe_convert(::Type{Ptr{T}}, x) where {T} = unsafe_convert(T, x) ptr_arg_unsafe_convert(::Type{Ptr{Void}}, x) = x +""" + cconvert(T,x) + +Convert `x` to a value to be passed to C code as type `T`, typically by calling `convert(T, x)`. + +In cases where `x` cannot be safely converted to `T`, unlike [`convert`](@ref), `cconvert` may +return an object of a type different from `T`, which however is suitable for +[`unsafe_convert`](@ref) to handle. The result of this function should be kept valid (for the GC) +until the result of [`unsafe_convert`](@ref) is not needed anymore. +This can be used to allocate memory that will be accessed by the `ccall`. +If multiple objects need to be allocated, a tuple of the objects can be used as return value. + +Neither `convert` nor `cconvert` should take a Julia object and turn it into a `Ptr`. +""" +function cconvert end + cconvert(T::Type, x) = convert(T, x) # do the conversion eagerly in most cases cconvert(::Type{<:Ptr}, x) = x # but defer the conversion to Ptr to unsafe_convert unsafe_convert(::Type{T}, x::T) where {T} = x # unsafe_convert (like convert) defaults to assuming the convert occurred unsafe_convert(::Type{T}, x::T) where {T<:Ptr} = x # to resolve ambiguity with the next method unsafe_convert(::Type{P}, x::Ptr) where {P<:Ptr} = convert(P, x) +""" + reinterpret(type, A) + +Change the type-interpretation of a block of memory. +For arrays, this constructs an array with the same binary data as the given +array, but with the specified element type. +For example, +`reinterpret(Float32, UInt32(7))` interprets the 4 bytes corresponding to `UInt32(7)` as a +[`Float32`](@ref). + +!!! warning + + It is not allowed to `reinterpret` an array to an element type with a larger alignment then + the alignment of the array. For a normal `Array`, this is the alignment of its element type. + For a reinterpreted array, this is the alignment of the `Array` it was reinterpreted from. + For example, `reinterpret(UInt32, UInt8[0, 0, 0, 0])` is not allowed but + `reinterpret(UInt32, reinterpret(UInt8, Float32[1.0]))` is allowed. + +# Examples +```jldoctest +julia> reinterpret(Float32, UInt32(7)) +1.0f-44 + +julia> reinterpret(Float32, UInt32[1 2 3 4 5]) +1×5 Array{Float32,2}: + 1.4013f-45 2.8026f-45 4.2039f-45 5.60519f-45 7.00649f-45 +``` +""" reinterpret(::Type{T}, x) where {T} = bitcast(T, x) reinterpret(::Type{Unsigned}, x::Float16) = reinterpret(UInt16,x) reinterpret(::Type{Signed}, x::Float16) = reinterpret(Int16,x) +""" + sizeof(T) + +Size, in bytes, of the canonical binary representation of the given DataType `T`, if any. + +# Examples +```jldoctest +julia> sizeof(Float32) +4 + +julia> sizeof(Complex128) +16 +``` + +If `T` does not have a specific size, an error is thrown. + +```jldoctest +julia> sizeof(Base.LinAlg.LU) +ERROR: argument is an abstract type; size is indeterminate +Stacktrace: + [1] sizeof(::Type{T} where T) at ./essentials.jl:150 +``` +""" sizeof(x) = Core.sizeof(x) function append_any(xs...) @@ -247,6 +390,11 @@ end # simple Array{Any} operations needed for bootstrap setindex!(A::Array{Any}, @nospecialize(x), i::Int) = Core.arrayset(A, x, i) +""" + precompile(f, args::Tuple{Vararg{Any}}) + +Compile the given function `f` for the argument tuple (of types) `args`, but do not execute it. +""" function precompile(@nospecialize(f), args::Tuple) ccall(:jl_compile_hint, Int32, (Any,), Tuple{Core.Typeof(f), args...}) != 0 end @@ -442,8 +590,6 @@ function as_kwargs(xs) return to end -isempty(itr) = done(itr, start(itr)) - """ invokelatest(f, args...; kwargs...) @@ -459,3 +605,75 @@ function invokelatest(f, args...; kwargs...) inner() = f(args...; kwargs...) Core._apply_latest(inner) end + +# iteration protocol + +""" + next(iter, state) -> item, state + +For a given iterable object and iteration state, return the current item and the next iteration state. + +# Examples +```jldoctest +julia> next(1:5, 3) +(3, 4) + +julia> next(1:5, 5) +(5, 6) +``` +""" +function next end + +""" + start(iter) -> state + +Get initial iteration state for an iterable object. + +# Examples +```jldoctest +julia> start(1:5) +1 + +julia> start([1;2;3]) +1 + +julia> start([4;2;3]) +1 +``` +""" +function start end + +""" + done(iter, state) -> Bool + +Test whether we are done iterating. + +# Examples +```jldoctest +julia> done(1:5, 3) +false + +julia> done(1:5, 5) +false + +julia> done(1:5, 6) +true +``` +""" +function done end + +""" + isempty(collection) -> Bool + +Determine whether a collection is empty (has no elements). + +# Examples +```jldoctest +julia> isempty([]) +true + +julia> isempty([1 2 3]) +false +``` +""" +isempty(itr) = done(itr, start(itr)) diff --git a/base/event.jl b/base/event.jl index 6dfbe2a8fc176..74bb6ba3069a3 100644 --- a/base/event.jl +++ b/base/event.jl @@ -18,6 +18,29 @@ mutable struct Condition Condition() = new([]) end +""" + wait([x]) + +Block the current task until some event occurs, depending on the type of the argument: + +* [`RemoteChannel`](@ref) : Wait for a value to become available on the specified remote + channel. +* [`Future`](@ref) : Wait for a value to become available for the specified future. +* [`Channel`](@ref): Wait for a value to be appended to the channel. +* [`Condition`](@ref): Wait for [`notify`](@ref) on a condition. +* `Process`: Wait for a process or process chain to exit. The `exitcode` field of a process + can be used to determine success or failure. +* [`Task`](@ref): Wait for a `Task` to finish, returning its result value. If the task fails + with an exception, the exception is propagated (re-thrown in the task that called `wait`). +* `RawFD`: Wait for changes on a file descriptor (see [`poll_fd`](@ref) for keyword + arguments and return code) + +If no argument is passed, the task blocks for an undefined period. A task can only be +restarted by an explicit call to [`schedule`](@ref) or [`yieldto`](@ref). + +Often `wait` is called within a `while` loop to ensure a waited-for condition is met before +proceeding. +""" function wait(c::Condition) ct = current_task() diff --git a/base/float.jl b/base/float.jl index bdf9a47f0adcc..8a611cf38f8e8 100644 --- a/base/float.jl +++ b/base/float.jl @@ -278,6 +278,15 @@ Float64 float(::Type{T}) where {T<:Number} = typeof(float(zero(T))) float(::Type{T}) where {T<:AbstractFloat} = T +""" + unsafe_trunc(T, x) + +`unsafe_trunc(T, x)` returns the nearest integral value of type `T` whose absolute value is +less than or equal to `x`. If the value is not representable by `T`, an arbitrary value will +be returned. +""" +function unsafe_trunc end + for Ti in (Int8, Int16, Int32, Int64) @eval begin unsafe_trunc(::Type{$Ti}, x::Float16) = unsafe_trunc($Ti, Float32(x)) @@ -565,7 +574,14 @@ hash(x::Float64, h::UInt) = isnan(x) ? (hx_NaN ⊻ h) : hx(fptoui(UInt64, abs(x) hash(x::Union{Bool,Int8,UInt8,Int16,UInt16,Int32,UInt32}, h::UInt) = hash(Int64(x), h) hash(x::Float32, h::UInt) = hash(Float64(x), h) -## precision, as defined by the effective number of bits in the mantissa ## +""" + precision(num::AbstractFloat) + +Get the precision of a floating point number, as defined by the effective number of bits in +the mantissa. +""" +function precision end + precision(::Type{Float16}) = 11 precision(::Type{Float32}) = 24 precision(::Type{Float64}) = 53 @@ -690,6 +706,13 @@ for Ti in (Int8, Int16, Int32, Int64, Int128, UInt8, UInt16, UInt32, UInt64, UIn end end +""" + issubnormal(f) -> Bool + +Test whether a floating point number is subnormal. +""" +function issubnormal end + @eval begin issubnormal(x::Float32) = (abs(x) < $(bitcast(Float32, 0x00800000))) & (x!=0) issubnormal(x::Float64) = (abs(x) < $(bitcast(Float64, 0x0010000000000000))) & (x!=0) @@ -710,17 +733,6 @@ end realmax(::Type{Float32}) = $(bitcast(Float32, 0x7f7fffff)) realmax(::Type{Float64}) = $(bitcast(Float64, 0x7fefffffffffffff)) - """ - realmin(T) - - The smallest in absolute value non-subnormal value representable by the given - floating-point DataType `T`. - """ - realmin(x::T) where {T<:AbstractFloat} = realmin(T) - realmax(x::T) where {T<:AbstractFloat} = realmax(T) - realmin() = realmin(Float64) - realmax() = realmax(Float64) - eps(x::AbstractFloat) = isfinite(x) ? abs(x) >= realmin(x) ? ldexp(eps(typeof(x)), exponent(x)) : nextfloat(zero(x)) : oftype(x, NaN) eps(::Type{Float16}) = $(bitcast(Float16, 0x1400)) eps(::Type{Float32}) = $(bitcast(Float32, 0x34000000)) @@ -728,6 +740,33 @@ end eps() = eps(Float64) end +""" + realmin(T) + +The smallest in absolute value non-subnormal value representable by the given +floating-point DataType `T`. +""" +realmin(x::T) where {T<:AbstractFloat} = realmin(T) + +""" + realmax(T) + +The highest finite value representable by the given floating-point DataType `T`. + +# Examples +```jldoctest +julia> realmax(Float16) +Float16(6.55e4) + +julia> realmax(Float32) +3.4028235f38 +``` +""" +realmax(x::T) where {T<:AbstractFloat} = realmax(T) + +realmin() = realmin(Float64) +realmax() = realmax(Float64) + """ eps(::Type{T}) where T<:AbstractFloat eps() diff --git a/base/floatfuncs.jl b/base/floatfuncs.jl index 39b30c73c2e6b..6cde2b86a9bc0 100644 --- a/base/floatfuncs.jl +++ b/base/floatfuncs.jl @@ -237,6 +237,16 @@ function rtoldefault(x::Union{T,Type{T}}, y::Union{S,Type{S}}, atol::Real) where end # fused multiply-add + +""" + fma(x, y, z) + +Computes `x*y+z` without rounding the intermediate result `x*y`. On some systems this is +significantly more expensive than `x*y+z`. `fma` is used to improve accuracy in certain +algorithms. See [`muladd`](@ref). +""" +function fma end + fma_libm(x::Float32, y::Float32, z::Float32) = ccall(("fmaf", libm_name), Float32, (Float32,Float32,Float32), x, y, z) fma_libm(x::Float64, y::Float64, z::Float64) = diff --git a/base/gcutils.jl b/base/gcutils.jl index 3d8f04362ba57..e42cc9e6de303 100644 --- a/base/gcutils.jl +++ b/base/gcutils.jl @@ -4,6 +4,13 @@ ==(w::WeakRef, v) = isequal(w.value, v) ==(w, v::WeakRef) = isequal(w, v.value) +""" + finalizer(x, f) + +Register a function `f(x)` to be called when there are no program-accessible references to +`x`. The type of `x` must be a `mutable struct`, otherwise the behavior of this function is +unpredictable. +""" function finalizer(@nospecialize(o), @nospecialize(f)) if isimmutable(o) error("objects of type ", typeof(o), " cannot be finalized") @@ -11,6 +18,7 @@ function finalizer(@nospecialize(o), @nospecialize(f)) ccall(:jl_gc_add_finalizer_th, Void, (Ptr{Void}, Any, Any), Core.getptls(), o, f) end + function finalizer(o::T, f::Ptr{Void}) where T @_inline_meta if isimmutable(T) @@ -20,8 +28,26 @@ function finalizer(o::T, f::Ptr{Void}) where T Core.getptls(), o, f) end +""" + finalize(x) + +Immediately run finalizers registered for object `x`. +""" finalize(@nospecialize(o)) = ccall(:jl_finalize_th, Void, (Ptr{Void}, Any,), Core.getptls(), o) +""" + gc() + +Perform garbage collection. This should not generally be used. +""" gc(full::Bool=true) = ccall(:jl_gc_collect, Void, (Int32,), full) + +""" + gc_enable(on::Bool) + +Control whether garbage collection is enabled using a boolean argument (`true` for enabled, +`false` for disabled). Returns previous GC state. Disabling garbage collection should be +used only with extreme caution, as it can cause memory use to grow without bound. +""" gc_enable(on::Bool) = ccall(:jl_gc_enable, Int32, (Int32,), on) != 0 diff --git a/base/gmp.jl b/base/gmp.jl index 6ae010c511521..bf12ec5401307 100644 --- a/base/gmp.jl +++ b/base/gmp.jl @@ -392,6 +392,14 @@ convert(::Type{Float16}, n::BigInt) = Float16(n,RoundNearest) promote_rule(::Type{BigInt}, ::Type{<:Integer}) = BigInt +""" + big(x) + +Convert a number to a maximum precision representation (typically [`BigInt`](@ref) or +`BigFloat`). See [`BigFloat`](@ref) for information about some pitfalls with floating-point numbers. +""" +function big end + big(::Type{<:Integer}) = BigInt big(::Type{<:Rational}) = Rational{BigInt} diff --git a/base/hashing.jl b/base/hashing.jl index 331d6c214ea3e..ee4be4d384941 100644 --- a/base/hashing.jl +++ b/base/hashing.jl @@ -2,6 +2,17 @@ ## hashing a single value ## +""" + hash(x[, h::UInt]) + +Compute an integer hash code such that `isequal(x,y)` implies `hash(x)==hash(y)`. The +optional second argument `h` is a hash code to be mixed with the result. + +New types should implement the 2-argument form, typically by calling the 2-argument `hash` +method recursively in order to mix hashes of the contents with each other (and with `h`). +Typically, any type that implements `hash` should also implement its own `==` (hence +`isequal`) to guarantee the property mentioned above. +""" hash(x::Any) = hash(x, zero(UInt)) hash(w::WeakRef, h::UInt) = hash(w.value, h) diff --git a/base/int.jl b/base/int.jl index 29f206dab146c..7650c6e5a1bb6 100644 --- a/base/int.jl +++ b/base/int.jl @@ -294,6 +294,26 @@ julia> 4 | 1 (|)(x::T, y::T) where {T<:BitInteger} = or_int(x, y) xor(x::T, y::T) where {T<:BitInteger} = xor_int(x, y) +""" + bswap(n) + +Byte-swap an integer. Flip the bits of its binary representation. + +# Examples +```jldoctest +julia> a = bswap(4) +288230376151711744 + +julia> bswap(a) +4 + +julia> bin(1) +"1" + +julia> bin(bswap(1)) +"100000000000000000000000000000000000000000000000000000000" +``` +""" bswap(x::Union{Int8, UInt8}) = x bswap(x::Union{Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128}) = bswap_int(x) @@ -513,6 +533,45 @@ convert(::Type{Unsigned}, x::Union{Float32, Float64, Bool}) = convert(UInt, x) convert(::Type{Integer}, x::Integer) = x convert(::Type{Integer}, x::Real) = convert(Signed, x) +""" + trunc([T,] x, [digits, [base]]) + +`trunc(x)` returns the nearest integral value of the same type as `x` whose absolute value +is less than or equal to `x`. + +`trunc(T, x)` converts the result to type `T`, throwing an `InexactError` if the value is +not representable. + +`digits` and `base` work as for [`round`](@ref). +""" +function trunc end + +""" + floor([T,] x, [digits, [base]]) + +`floor(x)` returns the nearest integral value of the same type as `x` that is less than or +equal to `x`. + +`floor(T, x)` converts the result to type `T`, throwing an `InexactError` if the value is +not representable. + +`digits` and `base` work as for [`round`](@ref). +""" +function floor end + +""" + ceil([T,] x, [digits, [base]]) + +`ceil(x)` returns the nearest integral value of the same type as `x` that is greater than or +equal to `x`. + +`ceil(T, x)` converts the result to type `T`, throwing an `InexactError` if the value is not +representable. + +`digits` and `base` work as for [`round`](@ref). +""" +function ceil end + round(x::Integer) = x trunc(x::Integer) = x floor(x::Integer) = x @@ -568,6 +627,29 @@ _default_type(::Union{Type{Integer},Type{Signed}}) = Int ## traits ## +""" + typemin(T) + +The lowest value representable by the given (real) numeric DataType `T`. + +# Examples +```jldoctest +julia> typemin(Float16) +-Inf16 + +julia> typemin(Float32) +-Inf32 +``` +""" +function typemin end + +""" + typemax(T) + +The highest value representable by the given (real) numeric `DataType`. +""" +function typemax end + typemin(::Type{Int8 }) = Int8(-128) typemax(::Type{Int8 }) = Int8(127) typemin(::Type{UInt8 }) = UInt8(0) diff --git a/base/intfuncs.jl b/base/intfuncs.jl index d6c782e33a89e..da4fa67d9f22a 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -700,6 +700,22 @@ julia> dec(20, 3) """ dec +""" + bits(n) + +A string giving the literal bit representation of a number. + +# Examples +```jldoctest +julia> bits(4) +"0000000000000000000000000000000000000000000000000000000000000100" + +julia> bits(2.2) +"0100000000000001100110011001100110011001100110011001100110011010" +``` +""" +function bits end + bits(x::Union{Bool,Int8,UInt8}) = bin(reinterpret(UInt8,x),8) bits(x::Union{Int16,UInt16,Float16}) = bin(reinterpret(UInt16,x),16) bits(x::Union{Char,Int32,UInt32,Float32}) = bin(reinterpret(UInt32,x),32) diff --git a/base/io.jl b/base/io.jl index b08f62e5df4c1..34829dae03a0e 100644 --- a/base/io.jl +++ b/base/io.jl @@ -77,6 +77,17 @@ function iswritable end function copy end function eof end +""" + read(stream::IO, T) + +Read a single value of type `T` from `stream`, in canonical binary representation. + + read(stream::IO, String) + +Read the entirety of `stream`, as a String. +""" +read(stream, t) + """ write(stream::IO, x) write(filename::AbstractString, x) @@ -190,6 +201,15 @@ Open a file and read its contents. `args` is passed to `read`: this is equivalen Read the entire contents of a file as a string. """ read(filename::AbstractString, args...) = open(io->read(io, args...), filename) + +""" + read!(stream::IO, array::Union{Array, BitArray}) + read!(filename::AbstractString, array::Union{Array, BitArray}) + +Read binary data from an I/O stream or file, filling in `array`. +""" +function read! end + read!(filename::AbstractString, a) = open(io->read!(io, a), filename) """ diff --git a/base/iostream.jl b/base/iostream.jl index 3ee3c315d36c3..7ae9f112f50f6 100644 --- a/base/iostream.jl +++ b/base/iostream.jl @@ -26,7 +26,15 @@ IOStream(name::AbstractString) = IOStream(name, true) unsafe_convert(T::Type{Ptr{Void}}, s::IOStream) = convert(T, pointer(s.ios)) show(io::IO, s::IOStream) = print(io, "IOStream(", s.name, ")") + +""" + fd(stream) + +Returns the file descriptor backing the stream or file. Note that this function only applies +to synchronous `File`'s and `IOStream`'s not to any of the asynchronous streams. +""" fd(s::IOStream) = Int(ccall(:jl_ios_fd, Clong, (Ptr{Void},), s.ios)) + stat(s::IOStream) = stat(fd(s)) close(s::IOStream) = ccall(:ios_close, Void, (Ptr{Void},), s.ios) isopen(s::IOStream) = ccall(:ios_isopen, Cint, (Ptr{Void},), s.ios)!=0 @@ -39,11 +47,22 @@ end iswritable(s::IOStream) = ccall(:ios_get_writable, Cint, (Ptr{Void},), s.ios)!=0 isreadable(s::IOStream) = ccall(:ios_get_readable, Cint, (Ptr{Void},), s.ios)!=0 +""" + truncate(file,n) + +Resize the file or buffer given by the first argument to exactly `n` bytes, filling +previously unallocated space with '\\0' if the file or buffer is grown. +""" function truncate(s::IOStream, n::Integer) systemerror("truncate", ccall(:ios_trunc, Cint, (Ptr{Void}, Csize_t), s.ios, n) != 0) return s end +""" + seek(s, pos) + +Seek a stream to the given position. +""" function seek(s::IOStream, n::Integer) ret = ccall(:ios_seek, Int64, (Ptr{Void}, Int64), s.ios, n) systemerror("seek", ret == -1) @@ -51,13 +70,28 @@ function seek(s::IOStream, n::Integer) return s end +""" + seekstart(s) + +Seek a stream to its beginning. +""" seekstart(s::IO) = seek(s,0) +""" + seekend(s) + +Seek a stream to its end. +""" function seekend(s::IOStream) systemerror("seekend", ccall(:ios_seek_end, Int64, (Ptr{Void},), s.ios) != 0) return s end +""" + skip(s, offset) + +Seek a stream relative to the current position. +""" function skip(s::IOStream, delta::Integer) ret = ccall(:ios_skip, Int64, (Ptr{Void}, Int64), s.ios, delta) systemerror("skip", ret == -1) @@ -65,6 +99,11 @@ function skip(s::IOStream, delta::Integer) return s end +""" + position(s) + +Get the current position of a stream. +""" function position(s::IOStream) pos = ccall(:ios_pos, Int64, (Ptr{Void},), s.ios) systemerror("position", pos == -1) diff --git a/base/libc.jl b/base/libc.jl index 98f254416166d..810cda11e448f 100644 --- a/base/libc.jl +++ b/base/libc.jl @@ -220,6 +220,12 @@ end Converts a `TmStruct` struct to a number of seconds since the epoch. """ time(tm::TmStruct) = Float64(ccall(:mktime, Int, (Ptr{TmStruct},), &tm)) + +""" + time() + +Get the system time in seconds since the epoch, with fairly high (typically, microsecond) resolution. +""" time() = ccall(:jl_clock_now, Float64, ()) ## process-related functions ## diff --git a/base/mmap.jl b/base/mmap.jl index edc8c332ee7a4..c8d111e037330 100644 --- a/base/mmap.jl +++ b/base/mmap.jl @@ -11,7 +11,14 @@ mutable struct Anonymous <: IO create::Bool end +""" + Mmap.Anonymous(name, readonly, create) + +Create an `IO`-like object for creating zeroed-out mmapped-memory that is not tied to a file +for use in `Mmap.mmap`. Used by `SharedArray` for creating shared memory arrays. +""" Anonymous() = Anonymous("",false,true) + Base.isopen(::Anonymous) = true Base.isreadable(::Anonymous) = true Base.iswritable(a::Anonymous) = !a.readonly @@ -94,6 +101,65 @@ else end # os-test # core implementation of mmap + +""" + Mmap.mmap(io::Union{IOStream,AbstractString,Mmap.AnonymousMmap}[, type::Type{Array{T,N}}, dims, offset]; grow::Bool=true, shared::Bool=true) + Mmap.mmap(type::Type{Array{T,N}}, dims) + +Create an `Array` whose values are linked to a file, using memory-mapping. This provides a +convenient way of working with data too large to fit in the computer's memory. + +The type is an `Array{T,N}` with a bits-type element of `T` and dimension `N` that +determines how the bytes of the array are interpreted. Note that the file must be stored in +binary format, and no format conversions are possible (this is a limitation of operating +systems, not Julia). + +`dims` is a tuple or single [`Integer`](@ref) specifying the size or length of the array. + +The file is passed via the stream argument, either as an open `IOStream` or filename string. +When you initialize the stream, use `"r"` for a "read-only" array, and `"w+"` to create a +new array used to write values to disk. + +If no `type` argument is specified, the default is `Vector{UInt8}`. + +Optionally, you can specify an offset (in bytes) if, for example, you want to skip over a +header in the file. The default value for the offset is the current stream position for an +`IOStream`. + +The `grow` keyword argument specifies whether the disk file should be grown to accommodate +the requested size of array (if the total file size is < requested array size). Write +privileges are required to grow the file. + +The `shared` keyword argument specifies whether the resulting `Array` and changes made to it +will be visible to other processes mapping the same file. + +For example, the following code + +```julia +# Create a file for mmapping +# (you could alternatively use mmap to do this step, too) +A = rand(1:20, 5, 30) +s = open("/tmp/mmap.bin", "w+") +# We'll write the dimensions of the array as the first two Ints in the file +write(s, size(A,1)) +write(s, size(A,2)) +# Now write the data +write(s, A) +close(s) + +# Test by reading it back in +s = open("/tmp/mmap.bin") # default is read-only +m = read(s, Int) +n = read(s, Int) +A2 = Mmap.mmap(s, Matrix{Int}, (m,n)) +``` + +creates a `m`-by-`n` `Matrix{Int}`, linked to the file associated with stream `s`. + +A more portable file would need to encode the word size -- 32 bit or 64 bit -- and endianness +information in the header. In practice, consider encoding binary data using standard formats +like HDF5 (which can be used with memory-mapping). +""" function mmap(io::IO, ::Type{Array{T,N}}=Vector{UInt8}, dims::NTuple{N,Integer}=(div(filesize(io)-position(io),sizeof(T)),), @@ -165,6 +231,17 @@ mmap(file::AbstractString, ::Type{T}, len::Integer, offset::Integer=Int64(0); gr mmap(::Type{T}, dims::NTuple{N,Integer}; shared::Bool=true) where {T<:Array,N} = mmap(Anonymous(), T, dims, Int64(0); shared=shared) mmap(::Type{T}, i::Integer...; shared::Bool=true) where {T<:Array} = mmap(Anonymous(), T, convert(Tuple{Vararg{Int}},i), Int64(0); shared=shared) +""" + Mmap.mmap(io, BitArray, [dims, offset]) + +Create a `BitArray` whose values are linked to a file, using memory-mapping; it has the same +purpose, works in the same way, and has the same arguments, as [`mmap`](@ref Mmap.mmap), but +the byte representation is different. + +**Example**: `B = Mmap.mmap(s, BitArray, (25,30000))` + +This would create a 25-by-30000 `BitArray`, linked to the file associated with stream `s`. +""" function mmap(io::IOStream, ::Type{<:BitArray}, dims::NTuple{N,Integer}, offset::Int64=position(io); grow::Bool=true, shared::Bool=true) where N n = prod(dims) @@ -204,6 +281,12 @@ const MS_ASYNC = 1 const MS_INVALIDATE = 2 const MS_SYNC = 4 +""" + Mmap.sync!(array) + +Forces synchronization between the in-memory version of a memory-mapped `Array` or +`BitArray` and the on-disk version. +""" function sync!(m::Array{T}, flags::Integer=MS_SYNC) where T offset = rem(UInt(pointer(m)), PAGESIZE) ptr = pointer(m) - offset diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 36afa13727d0b..a70c0dd5b2e2a 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -854,6 +854,38 @@ end ### from abstractarray.jl +""" + fill!(A, x) + +Fill array `A` with the value `x`. If `x` is an object reference, all elements will refer to +the same object. `fill!(A, Foo())` will return `A` filled with the result of evaluating +`Foo()` once. + +# Examples +```jldoctest +julia> A = zeros(2,3) +2×3 Array{Float64,2}: + 0.0 0.0 0.0 + 0.0 0.0 0.0 + +julia> fill!(A, 2.) +2×3 Array{Float64,2}: + 2.0 2.0 2.0 + 2.0 2.0 2.0 + +julia> a = [1, 1, 1]; A = fill!(Vector{Vector{Int}}(3), a); a[1] = 2; A +3-element Array{Array{Int64,1},1}: + [2, 1, 1] + [2, 1, 1] + [2, 1, 1] + +julia> x = 0; f() = (global x += 1; x); fill!(Vector{Int}(3), f()) +3-element Array{Int64,1}: + 1 + 1 + 1 +``` +""" function fill!(A::AbstractArray{T}, x) where T xT = convert(T, x) for I in eachindex(A) diff --git a/base/multimedia.jl b/base/multimedia.jl index 31e35bfbd4ad8..59fdd6aab7d20 100644 --- a/base/multimedia.jl +++ b/base/multimedia.jl @@ -44,6 +44,37 @@ false mimewritable(::MIME{mime}, x) where {mime} = method_exists(show, Tuple{IO, MIME{mime}, typeof(x)}) +""" + show(stream, mime, x) + +The [`display`](@ref) functions ultimately call `show` in order to write an object `x` as a +given `mime` type to a given I/O `stream` (usually a memory buffer), if possible. In order +to provide a rich multimedia representation of a user-defined type `T`, it is only necessary +to define a new `show` method for `T`, via: `show(stream, ::MIME"mime", x::T) = ...`, +where `mime` is a MIME-type string and the function body calls `write` (or similar) to write +that representation of `x` to `stream`. (Note that the `MIME""` notation only supports +literal strings; to construct `MIME` types in a more flexible manner use +`MIME{Symbol("")}`.) + +For example, if you define a `MyImage` type and know how to write it to a PNG file, you +could define a function `show(stream, ::MIME"image/png", x::MyImage) = ...` to allow +your images to be displayed on any PNG-capable `Display` (such as IJulia). As usual, be sure +to `import Base.show` in order to add new methods to the built-in Julia function +`show`. + +The default MIME type is `MIME"text/plain"`. There is a fallback definition for `text/plain` +output that calls `show` with 2 arguments. Therefore, this case should be handled by +defining a 2-argument `show(stream::IO, x::MyType)` method. + +Technically, the `MIME"mime"` macro defines a singleton type for the given `mime` string, +which allows us to exploit Julia's dispatch mechanisms in determining how to display objects +of any given type. + +The first argument to `show` can be an [`IOContext`](@ref) specifying output format properties. +See [`IOContext`](@ref) for details. +""" +show(stream, mime, x) + # it is convenient to accept strings instead of ::MIME show(io::IO, m::AbstractString, x) = show(io, MIME(m), x) mimewritable(m::AbstractString, x) = mimewritable(MIME(m), x) diff --git a/base/nullable.jl b/base/nullable.jl index 8d7d6f685825a..5a4d4f0c46fc4 100644 --- a/base/nullable.jl +++ b/base/nullable.jl @@ -1,5 +1,21 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license +""" + NullException() + +An attempted access to a [`Nullable`](@ref) with no defined value. + +# Examples +```jldoctest +julia> a = Nullable{Int}() +Nullable{Int64}() + +julia> get(a) +ERROR: NullException() +Stacktrace: + [1] get(::Nullable{Int64}) at ./nullable.jl:92 +``` +""" struct NullException <: Exception end diff --git a/base/number.jl b/base/number.jl index 255ceebf9af6c..1a95f2dee6a77 100644 --- a/base/number.jl +++ b/base/number.jl @@ -146,6 +146,21 @@ julia> flipsign(5, -3) ``` """ flipsign(x::Real, y::Real) = ifelse(signbit(y), -x, +x) # the + is for type-stability on Bool + +""" + copysign(x, y) -> z + +Return `z` which has the magnitude of `x` and the same sign as `y`. + +# Examples +```jldoctest +julia> copysign(1, -2) +-1 + +julia> copysign(-1, 2) +1 +``` +""" copysign(x::Real, y::Real) = ifelse(signbit(x)!=signbit(y), -x, +x) conj(x::Real) = x diff --git a/base/operators.jl b/base/operators.jl index 0228d78bc6d4a..08fa05c50705c 100644 --- a/base/operators.jl +++ b/base/operators.jl @@ -50,6 +50,21 @@ end ## generic comparison ## +""" + ==(x, y) + +Generic equality operator, giving a single [`Bool`](@ref) result. Falls back to `===`. +Should be implemented for all types with a notion of equality, based on the abstract value +that an instance represents. For example, all numeric types are compared by numeric value, +ignoring type. Strings are compared as sequences of characters, ignoring encoding. + +Follows IEEE semantics for floating-point numbers. + +Collections should generally implement `==` by calling `==` recursively on all contents. + +New numeric types should implement this function for two arguments of the new type, and +handle comparison to other types via promotion rules where possible. +""" ==(x, y) = x === y """ @@ -95,6 +110,17 @@ isequal(x::AbstractFloat, y::AbstractFloat) = (isnan(x) & isnan(y)) | signequal( isequal(x::Real, y::AbstractFloat) = (isnan(x) & isnan(y)) | signequal(x, y) & (x == y) isequal(x::AbstractFloat, y::Real ) = (isnan(x) & isnan(y)) | signequal(x, y) & (x == y) +""" + isless(x, y) + +Test whether `x` is less than `y`, according to a canonical total order. Values that are +normally unordered, such as `NaN`, are ordered in an arbitrary but consistent fashion. This +is the default comparison used by [`sort`](@ref). Non-numeric types with a canonical total order +should implement this function. Numeric types only need to implement it if they have special +values such as `NaN`. +""" +function isless end + isless(x::AbstractFloat, y::AbstractFloat) = (!isnan(x) & isnan(y)) | signless(x, y) | (x < y) isless(x::Real, y::AbstractFloat) = (!isnan(x) & isnan(y)) | signless(x, y) | (x < y) isless(x::AbstractFloat, y::Real ) = (!isnan(x) & isnan(y)) | signless(x, y) | (x < y) diff --git a/base/parse.jl b/base/parse.jl index 1ef3f91278cb6..91e1d0f5ab422 100644 --- a/base/parse.jl +++ b/base/parse.jl @@ -4,6 +4,29 @@ import Base.Checked: add_with_overflow, mul_with_overflow ## string to integer functions ## +""" + parse(type, str, [base]) + +Parse a string as a number. If the type is an integer type, then a base can be specified +(the default is 10). If the type is a floating point type, the string is parsed as a decimal +floating point number. If the string does not contain a valid number, an error is raised. + +```jldoctest +julia> parse(Int, "1234") +1234 + +julia> parse(Int, "1234", 5) +194 + +julia> parse(Int, "afc", 16) +2812 + +julia> parse(Float64, "1.2e-3") +0.0012 +``` +""" +parse(T::Type, str, base=Int) + function parse(::Type{T}, c::Char, base::Integer=36) where T<:Integer a::Int = (base <= 36 ? 10 : 36) 2 <= base <= 62 || throw(ArgumentError("invalid base: base must be 2 ≤ base ≤ 62, got $base")) @@ -227,6 +250,25 @@ mutable struct ParseError <: Exception msg::AbstractString end +""" + parse(str, start; greedy=true, raise=true) + +Parse the expression string and return an expression (which could later be passed to eval +for execution). `start` is the index of the first character to start parsing. If `greedy` is +`true` (default), `parse` will try to consume as much input as it can; otherwise, it will +stop as soon as it has parsed a valid expression. Incomplete but otherwise syntactically +valid expressions will return `Expr(:incomplete, "(error message)")`. If `raise` is `true` +(default), syntax errors other than incomplete expressions will raise an error. If `raise` +is `false`, `parse` will return an expression that will raise an error upon evaluation. + +```jldoctest +julia> parse("x = 3, y = 5", 7) +(:(y = 5), 13) + +julia> parse("x = 3, y = 5", 5) +(:((3, y) = 5), 13) +``` +""" function parse(str::AbstractString, pos::Int; greedy::Bool=true, raise::Bool=true) # pos is one based byte offset. # returns (expr, end_pos). expr is () in case of parse error. @@ -244,6 +286,30 @@ function parse(str::AbstractString, pos::Int; greedy::Bool=true, raise::Bool=tru return ex, pos+1 # C is zero-based, Julia is 1-based end +""" + parse(str; raise=true) + +Parse the expression string greedily, returning a single expression. An error is thrown if +there are additional characters after the first expression. If `raise` is `true` (default), +syntax errors will raise an error; otherwise, `parse` will return an expression that will +raise an error upon evaluation. + +```jldoctest +julia> parse("x = 3") +:(x = 3) + +julia> parse("x = ") +:($(Expr(:incomplete, "incomplete: premature end of input"))) + +julia> parse("1.0.2") +ERROR: ParseError("invalid numeric constant \\\"1.0.\\\"") +Stacktrace: +[...] + +julia> parse("1.0.2"; raise = false) +:($(Expr(:error, "invalid numeric constant \"1.0.\""))) +``` +""" function parse(str::AbstractString; raise::Bool=true) ex, pos = parse(str, 1, greedy=true, raise=raise) if isa(ex,Expr) && ex.head === :error diff --git a/base/pointer.jl b/base/pointer.jl index fee9420a39950..2e3e0e957d8e5 100644 --- a/base/pointer.jl +++ b/base/pointer.jl @@ -32,6 +32,30 @@ convert(::Type{Ptr{T}}, p::Ptr{T}) where {T} = p convert(::Type{Ptr{T}}, p::Ptr) where {T} = bitcast(Ptr{T}, p) # object to pointer (when used with ccall) + +""" + unsafe_convert(T, x) + +Convert `x` to a C argument of type `T` +where the input `x` must be the return value of `cconvert(T, ...)`. + +In cases where [`convert`](@ref) would need to take a Julia object +and turn it into a `Ptr`, this function should be used to define and perform +that conversion. + +Be careful to ensure that a Julia reference to `x` exists as long as the result of this +function will be used. Accordingly, the argument `x` to this function should never be an +expression, only a variable name or field reference. For example, `x=a.b.c` is acceptable, +but `x=[a,b,c]` is not. + +The `unsafe` prefix on this function indicates that using the result of this function after +the `x` argument to this function is no longer accessible to the program may cause undefined +behavior, including program corruption or segfaults, at any later time. + +See also [`cconvert`](@ref) +""" +function unsafe_convert end + unsafe_convert(::Type{Ptr{UInt8}}, x::Symbol) = ccall(:jl_symbol_name, Ptr{UInt8}, (Any,), x) unsafe_convert(::Type{Ptr{Int8}}, x::Symbol) = ccall(:jl_symbol_name, Ptr{Int8}, (Any,), x) unsafe_convert(::Type{Ptr{UInt8}}, s::String) = convert(Ptr{UInt8}, pointer_from_objref(s)+sizeof(Int)) diff --git a/base/process.jl b/base/process.jl index 31791b8d83af8..781e1d3ea1f94 100644 --- a/base/process.jl +++ b/base/process.jl @@ -550,6 +550,11 @@ spawn_opts_inherit(stdios::StdIOSet) = (stdios,) spawn_opts_inherit(in::Redirectable=RawFD(0), out::Redirectable=RawFD(1), err::Redirectable=RawFD(2), args...) = ((in, out, err), args...) +""" + spawn(command) + +Run a command object asynchronously, returning the resulting `Process` object. +""" spawn(cmds::AbstractCmd, args...; chain::Nullable{ProcessChain}=Nullable{ProcessChain}()) = spawn(cmds, spawn_opts_swallow(args...)...; chain=chain) diff --git a/base/promotion.jl b/base/promotion.jl index ca79a451bfa07..d51f4811e9d61 100644 --- a/base/promotion.jl +++ b/base/promotion.jl @@ -2,6 +2,11 @@ ## type join (closest common ancestor, or least upper bound) ## +""" + typejoin(T, S) + +Compute a type that contains both `T` and `S`. +""" typejoin() = (@_pure_meta; Bottom) typejoin(@nospecialize(t)) = (@_pure_meta; t) typejoin(@nospecialize(t), ts...) = (@_pure_meta; typejoin(t, typejoin(ts...))) @@ -163,6 +168,15 @@ function promote_type(::Type{T}, ::Type{S}) where {T,S} promote_result(T, S, promote_rule(T,S), promote_rule(S,T)) end +""" + promote_rule(type1, type2) + +Specifies what type should be used by [`promote`](@ref) when given values of types `type1` and +`type2`. This function should not be called directly, but should have definitions added to +it for new types as appropriate. +""" +function promote_rule end + promote_rule(::Type{<:Any}, ::Type{<:Any}) = Bottom promote_result(::Type{<:Any},::Type{<:Any},::Type{T},::Type{S}) where {T,S} = (@_inline_meta; promote_type(T,S)) @@ -170,6 +184,19 @@ promote_result(::Type{<:Any},::Type{<:Any},::Type{T},::Type{S}) where {T,S} = (@ # case use typejoin on the original types instead. promote_result(::Type{T},::Type{S},::Type{Bottom},::Type{Bottom}) where {T,S} = (@_inline_meta; typejoin(T, S)) +""" + promote(xs...) + +Convert all arguments to their common promotion type (if any), and return them all (as a tuple). + +# Examples +```jldoctest +julia> promote(Int8(1), Float16(4.5), Float32(4.1)) +(1.0f0, 4.5f0, 4.1f0) +``` +""" +function promote end + promote() = () promote(x) = (x,) function promote(x::T, y::S) where {T,S} diff --git a/base/random.jl b/base/random.jl index 6a332d05f555d..04a7fcb39fe25 100644 --- a/base/random.jl +++ b/base/random.jl @@ -1683,6 +1683,13 @@ end # each element of A is included in S with independent probability p. # (Note that this is different from the problem of finding a random # size-m subset of A where m is fixed!) + +""" + randsubseq!(S, A, p) + +Like [`randsubseq`](@ref), but the results are stored in `S` +(which is resized as needed). +""" function randsubseq!(r::AbstractRNG, S::AbstractArray, A::AbstractArray, p::Real) 0 <= p <= 1 || throw(ArgumentError("probability $p not in [0,1]")) n = length(A) diff --git a/base/reflection.jl b/base/reflection.jl index 032c20c4b4009..f654458446948 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -216,7 +216,11 @@ macro isdefined(s::Symbol) return Expr(:isdefined, esc(s)) end -# return an integer such that object_id(x)==object_id(y) if x===y +""" + object_id(x) + +Get a hash value for `x` based on object identity. `object_id(x)==object_id(y)` if `x === y`. +""" object_id(@nospecialize(x)) = ccall(:jl_object_id, UInt, (Any,), x) struct DataTypeLayout diff --git a/base/regex.jl b/base/regex.jl index fec921d0c810d..ffea88ec79eb8 100644 --- a/base/regex.jl +++ b/base/regex.jl @@ -141,6 +141,11 @@ function getindex(m::RegexMatch, name::Symbol) end getindex(m::RegexMatch, name::AbstractString) = m[Symbol(name)] +""" + ismatch(r::Regex, s::AbstractString) -> Bool + +Test whether a string contains a match of the given regular expression. +""" function ismatch(r::Regex, s::AbstractString, offset::Integer=0) compile(r) return PCRE.exec(r.regex, String(s), offset, r.match_options, @@ -155,6 +160,16 @@ end (r::Regex)(s) = ismatch(r, s) +""" + match(r::Regex, s::AbstractString[, idx::Integer[, addopts]]) + +Search for the first match of the regular expression `r` in `s` and return a `RegexMatch` +object containing the match, or nothing if the match failed. The matching substring can be +retrieved by accessing `m.match` and the captured sequences can be retrieved by accessing +`m.captures` The optional `idx` argument specifies an index at which to start the search. +""" +function match end + function match(re::Regex, str::Union{SubString{String}, String}, idx::Integer, add_opts::UInt32=UInt32(0)) compile(re) opts = re.match_options | add_opts @@ -175,6 +190,11 @@ match(r::Regex, s::AbstractString, i::Integer) = throw(ArgumentError( "regex matching is only available for the String type; use String(s) to convert" )) +""" + matchall(r::Regex, s::AbstractString[, overlap::Bool=false]) -> Vector{AbstractString} + +Return a vector of the matching substrings from [`eachmatch`](@ref). +""" function matchall(re::Regex, str::String, overlap::Bool=false) regex = compile(re).regex n = sizeof(str) @@ -362,6 +382,13 @@ function eachmatch(re::Regex, str::AbstractString, ovr::Bool) RegexMatchIterator(re,str,ovr) end +""" + eachmatch(r::Regex, s::AbstractString[, overlap::Bool=false]) + +Search for all matches of a the regular expression `r` in `s` and return a iterator over the +matches. If overlap is `true`, the matching sequences are allowed to overlap indices in the +original string, otherwise they must be from distinct character ranges. +""" eachmatch(re::Regex, str::AbstractString) = RegexMatchIterator(re,str) ## comparison ## diff --git a/base/serialize.jl b/base/serialize.jl index 6a6847a850e90..b78b658430fe5 100644 --- a/base/serialize.jl +++ b/base/serialize.jl @@ -637,10 +637,28 @@ function serialize_any(s::AbstractSerializer, @nospecialize(x)) end end +""" + serialize(stream, value) + +Write an arbitrary value to a stream in an opaque format, such that it can be read back by +[`deserialize`](@ref). The read-back value will be as identical as possible to the original. In +general, this process will not work if the reading and writing are done by different +versions of Julia, or an instance of Julia with a different system image. `Ptr` values are +serialized as all-zero bit patterns (`NULL`). +""" serialize(s::IO, x) = serialize(SerializationState(s), x) ## deserializing values ## +""" + deserialize(stream) + +Read a value written by [`serialize`](@ref). `deserialize` assumes the binary data read from +`stream` is correct and has been serialized by a compatible implementation of [`serialize`](@ref). +It has been designed with simplicity and performance as a goal and does not validate +the data read. Malformed data can result in process termination. The caller has to ensure +the integrity and correctness of data read from `stream`. +""" deserialize(s::IO) = deserialize(SerializationState(s)) function deserialize(s::AbstractSerializer) diff --git a/base/set.jl b/base/set.jl index 51603130e01b7..f5ced5e45d779 100644 --- a/base/set.jl +++ b/base/set.jl @@ -65,6 +65,36 @@ done(s::Set, state) = done(s.dict, state) # NOTE: manually optimized to take advantage of Dict representation next(s::Set, i) = (s.dict.keys[i], skip_deleted(s.dict, i+1)) +""" + union(s1,s2...) + ∪(s1,s2...) + +Construct the union of two or more sets. Maintains order with arrays. + +# Examples +```jldoctest +julia> union([1, 2], [3, 4]) +4-element Array{Int64,1}: + 1 + 2 + 3 + 4 + +julia> union([1, 2], [2, 4]) +3-element Array{Int64,1}: + 1 + 2 + 4 + +julia> union([4, 2], [1, 2]) +3-element Array{Int64,1}: + 4 + 2 + 1 +``` +""" +function union end + union(s::Set) = copy(s) function union(s::Set, sets...) u = Set{join_eltype(s, sets...)}() @@ -103,6 +133,28 @@ end join_eltype() = Bottom join_eltype(v1, vs...) = typejoin(eltype(v1), join_eltype(vs...)) +""" + intersect(s1,s2...) + ∩(s1,s2) + +Construct the intersection of two or more sets. +Maintains order and multiplicity of the first argument for arrays and ranges. + +# Examples +```jldoctest +julia> intersect([1, 2, 3], [3, 4, 5]) +1-element Array{Int64,1}: + 3 + +julia> intersect([1, 4, 4, 5, 6], [4, 6, 6, 7, 8]) +3-element Array{Int64,1}: + 4 + 4 + 6 +``` +""" +function intersect end + intersect(s::Set) = copy(s) function intersect(s::Set, sets...) i = similar(s) diff --git a/base/show.jl b/base/show.jl index 303055c80b633..79678aab589a3 100644 --- a/base/show.jl +++ b/base/show.jl @@ -122,7 +122,17 @@ function show_circular(io::IOContext, @nospecialize(x)) return false end +""" + show(x) + +Write an informative text representation of a value to the current output stream. New types +should overload `show(io, x)` where the first argument is a stream. The representation used +by `show` generally includes Julia-specific formatting and type information. +""" +show(x) = show(STDOUT::IO, x) + show(io::IO, @nospecialize(x)) = show_default(io, x) + function show_default(io::IO, @nospecialize(x)) t = typeof(x)::DataType show(io, t) @@ -1362,6 +1372,12 @@ end dump(io::IO, x::DataType; maxdepth=8) = ((x.abstract ? dumptype : dump)(io, x, maxdepth, ""); println(io)) dump(io::IO, arg; maxdepth=8) = (dump(io, arg, maxdepth, ""); println(io)) + +""" + dump(x) + +Show every part of the representation of a value. +""" dump(arg; maxdepth=8) = dump(IOContext(STDOUT::IO, :limit => true), arg; maxdepth=maxdepth) diff --git a/base/socket.jl b/base/socket.jl index fd1298fdb5f9f..588e0097a69a5 100644 --- a/base/socket.jl +++ b/base/socket.jl @@ -333,6 +333,13 @@ _jl_sockaddr_from_addrinfo(addrinfo::Ptr{Void}) = _jl_sockaddr_set_port(ptr::Ptr{Void}, port::UInt16) = ccall(:jl_sockaddr_set_port, Void, (Ptr{Void}, UInt16), ptr, port) +""" + accept(server[,client]) + +Accepts a connection on the given server and returns a connection to the client. An +uninitialized client stream may be provided, in which case it will be used instead of +creating a new stream. +""" accept(server::TCPServer) = accept(server, TCPSocket()) # Libuv will internally reset the readable and writable flags on diff --git a/base/sort.jl b/base/sort.jl index 717de598a23aa..090a2f880a957 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -87,10 +87,68 @@ function select!(v::AbstractVector, k::Union{Int,OrdinalRange}, o::Ordering) sort!(v, first(inds), last(inds), PartialQuickSort(k), o) v[k] end + +""" + select!(v, k, [by=,] [lt=,] [rev=false]) + +Partially sort the vector `v` in place, according to the order specified by `by`, `lt` and +`rev` so that the value at index `k` (or range of adjacent values if `k` is a range) occurs +at the position where it would appear if the array were fully sorted via a non-stable +algorithm. If `k` is a single index, that value is returned; if `k` is a range, an array of +values at those indices is returned. Note that `select!` does not fully sort the input +array. + +# Examples +```jldoctest +julia> a = [1, 2, 4, 3, 4] +5-element Array{Int64,1}: + 1 + 2 + 4 + 3 + 4 + +julia> select!(a, 4) +4 + +julia> a +5-element Array{Int64,1}: + 1 + 2 + 3 + 4 + 4 + +julia> a = [1, 2, 4, 3, 4] +5-element Array{Int64,1}: + 1 + 2 + 4 + 3 + 4 + +julia> select!(a, 4, rev=true) +2 + +julia> a +5-element Array{Int64,1}: + 4 + 4 + 3 + 2 + 1 +``` +""" select!(v::AbstractVector, k::Union{Int,OrdinalRange}; lt=isless, by=identity, rev::Bool=false, order::Ordering=Forward) = select!(v, k, ord(lt,by,rev,order)) +""" + select(v, k, [by=,] [lt=,] [rev=false]) + +Variant of [`select!`](@ref) which copies `v` before partially sorting it, thereby returning the +same thing as `select!` but leaving `v` unmodified. +""" select(v::AbstractVector, k::Union{Int,OrdinalRange}; kws...) = select!(copymutable(v), k; kws...) @@ -611,9 +669,27 @@ sort(v::AbstractVector; kws...) = sort!(copymutable(v); kws...) ## selectperm: the permutation to sort the first k elements of an array ## +""" + selectperm(v, k, [alg=,] [by=,] [lt=,] [rev=false]) + +Return a partial permutation of the vector `v`, according to the order specified by +`by`, `lt` and `rev`, so that `v[output]` returns the first `k` (or range of adjacent values +if `k` is a range) values of a fully sorted version of `v`. If `k` is a single index +(Integer), an array of the first `k` indices is returned; if `k` is a range, an array of +those indices is returned. Note that the handling of integer values for `k` is different +from [`select`](@ref) in that it returns a vector of `k` elements instead of just the `k` th +element. Also note that this is equivalent to, but more efficient than, calling +`sortperm(...)[k]`. +""" selectperm(v::AbstractVector, k::Union{Integer,OrdinalRange}; kwargs...) = selectperm!(similar(Vector{eltype(k)}, indices(v,1)), v, k; kwargs..., initialized=false) +""" + selectperm!(ix, v, k, [alg=,] [by=,] [lt=,] [rev=false,] [initialized=false]) + +Like [`selectperm`](@ref), but accepts a preallocated index vector `ix`. If `initialized` is `false` +(the default), ix is initialized to contain the values `1:length(ix)`. +""" function selectperm!(ix::AbstractVector{<:Integer}, v::AbstractVector, k::Union{Int, OrdinalRange}; lt::Function=isless, diff --git a/base/special/exp10.jl b/base/special/exp10.jl index 2cfb4ff50057e..d8ce280468b6c 100644 --- a/base/special/exp10.jl +++ b/base/special/exp10.jl @@ -67,6 +67,20 @@ MIN_EXP10(::Type{Float32}) = -45.15449934959718f0 # log10 2^-150 @eval exp10_small_thres(::Type{Float64}) = $(2.0^-29) @eval exp10_small_thres(::Type{Float32}) = $(2.0f0^-14) +""" + exp10(x) + +Compute ``10^x``. + +# Examples +```jldoctest +julia> exp10(2) +100.0 + +julia> exp10(0.2) +1.5848931924611136 +``` +""" function exp10(x::T) where T<:Union{Float32,Float64} xa = reinterpret(Unsigned, x) & ~sign_mask(T) xsb = signbit(x) diff --git a/base/strings/utf8proc.jl b/base/strings/utf8proc.jl index a3ebd46ca0414..434ceb06bf85e 100644 --- a/base/strings/utf8proc.jl +++ b/base/strings/utf8proc.jl @@ -13,6 +13,24 @@ export normalize_string, graphemes, is_assigned_char, charwidth, isvalid, iscntrl, ispunct, isspace, isprint, isgraph # whether codepoints are valid Unicode scalar values, i.e. 0-0xd7ff, 0xe000-0x10ffff + +""" + isvalid(value) -> Bool + +Returns `true` if the given value is valid for its type, which currently can be either +`Char` or `String`. +""" +isvalid(value) + +""" + isvalid(T, value) -> Bool + +Returns `true` if the given value is valid for that type. Types currently can +be either `Char` or `String`. Values for `Char` can be of type `Char` or [`UInt32`](@ref). +Values for `String` can be of that type, or `Vector{UInt8}`. +""" +isvalid(T,value) + isvalid(::Type{Char}, ch::Unsigned) = !((ch - 0xd800 < 0x800) | (ch > 0x10ffff)) isvalid(::Type{Char}, ch::Integer) = isvalid(Char, Unsigned(ch)) isvalid(::Type{Char}, ch::Char) = isvalid(Char, UInt32(ch)) diff --git a/base/subarray.jl b/base/subarray.jl index ad591d6ad5e9c..b2a9c3e258834 100644 --- a/base/subarray.jl +++ b/base/subarray.jl @@ -57,6 +57,30 @@ size(V::SubArray) = (@_inline_meta; map(n->Int(unsafe_length(n)), indices(V))) similar(V::SubArray, T::Type, dims::Dims) = similar(V.parent, T, dims) +""" + parent(A) + +Returns the "parent array" of an array view type (e.g., `SubArray`), or the array itself if +it is not a view. + +# Examples +```jldoctest +julia> a = [1 2; 3 4] +2×2 Array{Int64,2}: + 1 2 + 3 4 + +julia> s_a = Symmetric(a) +2×2 Symmetric{Int64,Array{Int64,2}}: + 1 2 + 2 4 + +julia> parent(s_a) +2×2 Array{Int64,2}: + 1 2 + 3 4 +``` +""" parent(V::SubArray) = V.parent parentindexes(V::SubArray) = V.indexes diff --git a/base/sysimg.jl b/base/sysimg.jl index dec8dc77b5586..df4a606d00bc2 100644 --- a/base/sysimg.jl +++ b/base/sysimg.jl @@ -163,6 +163,12 @@ include("reshapedarray.jl") include("bitarray.jl") include("intset.jl") include("associative.jl") + +if !isdefined(Core, :Inference) + include("docs/core.jl") + Core.atdoc!(CoreDocs.docm) +end + include("dict.jl") include("set.jl") include("iterators.jl") @@ -189,11 +195,6 @@ include(string((length(Core.ARGS)>=2 ? Core.ARGS[2] : ""), "version_git.jl")) # include("osutils.jl") include("c.jl") -if !isdefined(Core, :Inference) - include("docs/core.jl") - Core.atdoc!(CoreDocs.docm) -end - # Core I/O include("io.jl") include("iostream.jl") @@ -417,7 +418,6 @@ include("threadcall.jl") include("deprecated.jl") # Some basic documentation -include("docs/helpdb.jl") include("docs/basedocs.jl") # Documentation -- should always be included last in sysimg. diff --git a/doc/src/stdlib/base.md b/doc/src/stdlib/base.md index 6ba310f9cb9bd..663abf299dd99 100644 --- a/doc/src/stdlib/base.md +++ b/doc/src/stdlib/base.md @@ -140,8 +140,8 @@ Base.@nospecialize Base.gensym Base.@gensym Base.@polly -Base.parse(::Any, ::Any) -Base.parse(::Any) +Base.parse(::AbstractString, ::Int) +Base.parse(::AbstractString) ``` ## Nullables diff --git a/doc/src/stdlib/c.md b/doc/src/stdlib/c.md index 42199b752c001..19ecaa72c0c22 100644 --- a/doc/src/stdlib/c.md +++ b/doc/src/stdlib/c.md @@ -9,9 +9,8 @@ Base.cconvert Base.unsafe_load Base.unsafe_store! Base.unsafe_copy!{T}(::Ptr{T}, ::Ptr{T}, ::Any) -Base.unsafe_copy!(::Array, ::Any, ::Array, ::Any, ::Any) -Base.copy!(::Any, ::Any) -Base.copy!(::Any, ::Any, ::Any, ::Any, ::Any) +Base.unsafe_copy!{T}(::Array{T}, ::Any, ::Array{T}, ::Any, ::Any) +Base.copy! Base.pointer Base.unsafe_wrap{T,N}(::Union{Type{Array},Type{Array{T}},Type{Array{T,N}}}, ::Ptr{T}, ::NTuple{N,Int}) Base.pointer_from_objref diff --git a/doc/src/stdlib/collections.md b/doc/src/stdlib/collections.md index 9415ae0fc1ce6..9b611ed598174 100644 --- a/doc/src/stdlib/collections.md +++ b/doc/src/stdlib/collections.md @@ -53,7 +53,7 @@ Fully implemented by: ```@docs Base.isempty Base.empty! -Base.length(::Any) +Base.length ``` Fully implemented by: @@ -137,8 +137,8 @@ Base.filter! ## Indexable Collections ```@docs -Base.getindex(::Any, ::Any...) -Base.setindex!(::Any, ::Any, ::Any...) +Base.getindex +Base.setindex! Base.endof ``` @@ -249,7 +249,7 @@ Partially implemented by: ```@docs Base.push! -Base.pop!(::Any) +Base.pop! Base.unshift! Base.shift! Base.insert! diff --git a/doc/src/stdlib/io-network.md b/doc/src/stdlib/io-network.md index 3c94efcc2e339..6b750b7cad0e5 100644 --- a/doc/src/stdlib/io-network.md +++ b/doc/src/stdlib/io-network.md @@ -148,8 +148,7 @@ Base.Multimedia.istextmime ```@docs Base.Mmap.Anonymous -Base.Mmap.mmap(::Any, ::Type, ::Any, ::Any) -Base.Mmap.mmap(::Any, ::BitArray, ::Any, ::Any) +Base.Mmap.mmap Base.Mmap.sync! ``` From 42abb037cec37e06e1d8c96282e2498605b7459b Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Wed, 16 Aug 2017 10:02:55 +0200 Subject: [PATCH 049/324] REPL: "Alt-<" jumps first to first history entry of current session (#23213) Instead of jumping to the beginning of all accumulated history (which can contain 10s or 100s thousand entries), it seems more useful to jump to the first entry of the current session. Then it's enough to press "Alt-<" again if one wants to jump to the absolute first entry. --- base/repl/REPL.jl | 3 +- doc/src/manual/interacting-with-julia.md | 70 ++++++++++++------------ test/repl.jl | 11 ++++ 3 files changed, 48 insertions(+), 36 deletions(-) diff --git a/base/repl/REPL.jl b/base/repl/REPL.jl index d08e3fca01e3f..92d348ab84c66 100644 --- a/base/repl/REPL.jl +++ b/base/repl/REPL.jl @@ -507,7 +507,8 @@ function history_next(s::LineEdit.MIState, hist::REPLHistoryProvider, end history_first(s::LineEdit.MIState, hist::REPLHistoryProvider) = - history_prev(s, hist, hist.cur_idx - 1) + history_prev(s, hist, hist.cur_idx - 1 - + (hist.cur_idx > hist.start_idx+1 ? hist.start_idx : 0)) history_last(s::LineEdit.MIState, hist::REPLHistoryProvider) = history_next(s, hist, length(hist.history) - hist.cur_idx + 1) diff --git a/doc/src/manual/interacting-with-julia.md b/doc/src/manual/interacting-with-julia.md index 515a640dad5d8..c8ba66d6236f5 100644 --- a/doc/src/manual/interacting-with-julia.md +++ b/doc/src/manual/interacting-with-julia.md @@ -139,41 +139,41 @@ control-key, there are also meta-key bindings. These vary more by platform, but default to using alt- or option- held down with a key to send the meta-key (or can be configured to do so). -| Keybinding | Description | -|:------------------- |:------------------------------------------------------------------------------------------ | -| **Program control** |   | -| `^D` | Exit (when buffer is empty) | -| `^C` | Interrupt or cancel | -| `^L` | Clear console screen | -| Return/Enter, `^J` | New line, executing if it is complete | -| meta-Return/Enter | Insert new line without executing it | -| `?` or `;` | Enter help or shell mode (when at start of a line) | -| `^R`, `^S` | Incremental history search, described above | -| **Cursor movement** |   | -| Right arrow, `^F` | Move right one character | -| Left arrow, `^B` | Move left one character | -| Home, `^A` | Move to beginning of line | -| End, `^E` | Move to end of line | -| `^P` | Change to the previous or next history entry | -| `^N` | Change to the next history entry | -| Up arrow | Move up one line (or to the previous history entry) | -| Down arrow | Move down one line (or to the next history entry) | -| Page-up | Change to the previous history entry that matches the text before the cursor | -| Page-down | Change to the next history entry that matches the text before the cursor | -| `meta-F` | Move right one word | -| `meta-B` | Move left one word | -| `meta-<` | Change to the first history entry | -| `meta->` | Change to the last history entry | -| **Editing** |   | -| Backspace, `^H` | Delete the previous character | -| Delete, `^D` | Forward delete one character (when buffer has text) | -| meta-Backspace | Delete the previous word | -| `meta-D` | Forward delete the next word | -| `^W` | Delete previous text up to the nearest whitespace | -| `^K` | "Kill" to end of line, placing the text in a buffer | -| `^Y` | "Yank" insert the text from the kill buffer | -| `^T` | Transpose the characters about the cursor | -| `^Q` | Write a number in REPL and press `^Q` to open editor at corresponding stackframe or method | +| Keybinding | Description | +|:------------------- |:---------------------------------------------------------------------------------------------------------- | +| **Program control** |   | +| `^D` | Exit (when buffer is empty) | +| `^C` | Interrupt or cancel | +| `^L` | Clear console screen | +| Return/Enter, `^J` | New line, executing if it is complete | +| meta-Return/Enter | Insert new line without executing it | +| `?` or `;` | Enter help or shell mode (when at start of a line) | +| `^R`, `^S` | Incremental history search, described above | +| **Cursor movement** |   | +| Right arrow, `^F` | Move right one character | +| Left arrow, `^B` | Move left one character | +| Home, `^A` | Move to beginning of line | +| End, `^E` | Move to end of line | +| `^P` | Change to the previous or next history entry | +| `^N` | Change to the next history entry | +| Up arrow | Move up one line (or to the previous history entry) | +| Down arrow | Move down one line (or to the next history entry) | +| Page-up | Change to the previous history entry that matches the text before the cursor | +| Page-down | Change to the next history entry that matches the text before the cursor | +| `meta-F` | Move right one word | +| `meta-B` | Move left one word | +| `meta-<` | Change to the first history entry (of the current session if it is before the current position in history) | +| `meta->` | Change to the last history entry | +| **Editing** |   | +| Backspace, `^H` | Delete the previous character | +| Delete, `^D` | Forward delete one character (when buffer has text) | +| meta-Backspace | Delete the previous word | +| `meta-D` | Forward delete the next word | +| `^W` | Delete previous text up to the nearest whitespace | +| `^K` | "Kill" to end of line, placing the text in a buffer | +| `^Y` | "Yank" insert the text from the kill buffer | +| `^T` | Transpose the characters about the cursor | +| `^Q` | Write a number in REPL and press `^Q` to open editor at corresponding stackframe or method | ### Customizing keybindings diff --git a/test/repl.jl b/test/repl.jl index 727bb33d4770c..8a1b07d3ea9f7 100644 --- a/test/repl.jl +++ b/test/repl.jl @@ -359,6 +359,17 @@ for prompt = ["TestΠ", () -> randstring(rand(1:10))] LineEdit.history_last(s, hp) @test buffercontents(LineEdit.buffer(s)) == "wip" @test position(LineEdit.buffer(s)) == 3 + # test that history_first jumps to beginning of current session's history + hp.start_idx -= 5 # temporarily alter history + LineEdit.history_first(s, hp) + @test hp.cur_idx == 6 + # we are at the beginning of current session's history, so history_first + # must now jump to the beginning of all history + LineEdit.history_first(s, hp) + @test hp.cur_idx == 1 + LineEdit.history_last(s, hp) + @test hp.cur_idx-1 == length(hp.history) + hp.start_idx += 5 LineEdit.move_line_start(s) @test position(LineEdit.buffer(s)) == 0 From d153ebe247bee08a60256d18777c2eee50c889aa Mon Sep 17 00:00:00 2001 From: Tony Kelman Date: Thu, 3 Aug 2017 21:38:41 -0700 Subject: [PATCH 050/324] improve build parallelization on appveyor running `make -j3 all` before `make -j3 install` will parallelize the release and debug sysimg builds --- contrib/windows/appveyor_build.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/windows/appveyor_build.sh b/contrib/windows/appveyor_build.sh index ee42adab148a9..927151e5d407b 100755 --- a/contrib/windows/appveyor_build.sh +++ b/contrib/windows/appveyor_build.sh @@ -204,6 +204,7 @@ fi echo 'FORCE_ASSERTIONS = 1' >> Make.user cat Make.user +make -j3 VERBOSE=1 all make -j3 VERBOSE=1 install make VERBOSE=1 -C examples cp usr/bin/busybox.exe julia-*/bin From 68ade7929f164ad85232359eb04ee02585e836f0 Mon Sep 17 00:00:00 2001 From: Tony Kelman Date: Wed, 16 Aug 2017 02:35:32 -0700 Subject: [PATCH 051/324] Switch appveyor to use kernel.org mirror --- contrib/windows/Vagrantfile | 2 +- contrib/windows/install-cygwin.ps1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/windows/Vagrantfile b/contrib/windows/Vagrantfile index 246bb68f4aba0..c3da1be4df6a7 100644 --- a/contrib/windows/Vagrantfile +++ b/contrib/windows/Vagrantfile @@ -13,7 +13,7 @@ mkdir -Force $cyginstall | Out-Null foreach ($pkg in @("git,make,curl,patch,python,gcc-g++,m4,cmake,p7zip", "mingw64-$arch-gcc-g++,mingw64-$arch-gcc-fortran")) { & "$cyginstall\\$setup" -q -n -R $cyginstall -l $cyginstall\\packages ` - -s http://mirrors.mit.edu/cygwin -g -P $pkg | Where-Object ` + -s http://mirrors.kernel.org/sourceware/cygwin -g -P $pkg | Where-Object ` -FilterScript {$_ -notlike "Installing file *"} | Write-Output } & "$cyginstall\\bin\\sh" -lc "if ! [ -e julia$bits ]; then git clone \\ diff --git a/contrib/windows/install-cygwin.ps1 b/contrib/windows/install-cygwin.ps1 index 3e1a832537e34..81088a2999510 100644 --- a/contrib/windows/install-cygwin.ps1 +++ b/contrib/windows/install-cygwin.ps1 @@ -4,6 +4,6 @@ mkdir -Force C:\cygdownloads | Out-Null (new-object net.webclient).DownloadFile( "http://cygwin.com/$setup", "C:\cygdownloads\$setup") & "C:\cygdownloads\$setup" -q -n -R C:\cygwin-$env:ARCH ` - -l C:\cygdownloads -s http://mirrors.mit.edu/cygwin -g -I ` + -l C:\cygdownloads -s http://mirrors.kernel.org/sourceware/cygwin -g -I ` -P "make,curl,time,p7zip,mingw64-$env:ARCH-gcc-g++,mingw64-$env:ARCH-gcc-fortran" | Where-Object ` -FilterScript {$_ -notlike "Installing file *"} | Write-Output From 271f2103277a95302ebddcff4f9b9673a0ecda43 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 16 Aug 2017 10:11:39 -0400 Subject: [PATCH 052/324] fix macro goto/label hygiene (#23202) same basic idea as PR #22985 did for macro variables also fixes #23135 --- base/essentials.jl | 4 +-- src/julia-syntax.scm | 2 +- src/macroexpand.scm | 47 ++++++++++++++++----------------- test/goto.jl | 62 ++++++++++++++++++++++++++++++++------------ 4 files changed, 71 insertions(+), 44 deletions(-) diff --git a/base/essentials.jl b/base/essentials.jl index 95f1dc75c86bb..7e57b60ff973c 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -300,11 +300,11 @@ macro inbounds(blk) end macro label(name::Symbol) - Expr(:symboliclabel, name) + return esc(Expr(:symboliclabel, name)) end macro goto(name::Symbol) - Expr(:symbolicgoto, name) + return esc(Expr(:symbolicgoto, name)) end # SimpleVector diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 2e1d2d7dcc8fc..1ed2e3330838f 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -2554,7 +2554,7 @@ (define (resolve-scopes- e env outerglobals implicitglobals lam renames newlam) (cond ((symbol? e) (let ((r (assq e renames))) (if r (cdr r) e))) ;; return the renaming for e, or e - ((or (not (pair? e)) (quoted? e) (memq (car e) '(toplevel global))) e) + ((or (not (pair? e)) (quoted? e) (memq (car e) '(toplevel global symbolicgoto symboliclabel))) e) ((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 diff --git a/src/macroexpand.scm b/src/macroexpand.scm index ba99b97daaffc..b850366b8ca52 100644 --- a/src/macroexpand.scm +++ b/src/macroexpand.scm @@ -453,39 +453,36 @@ ;; and wrap globals in (globalref module var) for macro's home module (resolve-expansion-vars-with-new-env e '() m '() #f #t)) -(define (find-symbolic-labels e) - (let ((defs (table)) - (refs (table))) - (find-symbolic-label-defs e defs) - (find-symbolic-label-refs e refs) - (table.foldl - (lambda (label v labels) - (if (has? refs label) - (cons label labels) - labels)) - '() defs))) - -(define (rename-symbolic-labels- e relabel) +(define (rename-symbolic-labels- e relabels parent-scope) (cond ((or (not (pair? e)) (quoted? e)) e) - ((eq? (car e) 'symbolicgoto) - (let ((newlabel (assq (cadr e) relabel))) - (if newlabel `(symbolicgoto ,(cdr newlabel)) e))) - ((eq? (car e) 'symboliclabel) - (let ((newlabel (assq (cadr e) relabel))) - (if newlabel `(symboliclabel ,(cdr newlabel)) e))) - (else (map (lambda (x) (rename-symbolic-labels- x relabel)) e)))) + ((eq? (car e) 'hygienic-scope) + (let ((parent-scope (list relabels parent-scope)) + (body (cadr e)) + (m (caddr e))) + `(hygienic-scope ,(rename-symbolic-labels- (cadr e) (table) parent-scope) ,m))) + ((and (eq? (car e) 'escape) (not (null? parent-scope))) + `(escape ,(apply rename-symbolic-labels- (cadr e) parent-scope))) + ((or (eq? (car e) 'symbolicgoto) (eq? (car e) 'symboliclabel)) + (let* ((s (cadr e)) + (havelabel (if (or (null? parent-scope) (not (symbol? s))) s (get relabels s #f))) + (newlabel (if havelabel havelabel (named-gensy s)))) + (if (not havelabel) (put! relabels s newlabel)) + `(,(car e) ,newlabel))) + (else + (cons (car e) + (map (lambda (x) (rename-symbolic-labels- x relabels parent-scope)) + (cdr e)))))) (define (rename-symbolic-labels e) - (let* ((labels (find-symbolic-labels e)) - (relabel (pair-with-gensyms labels))) - (rename-symbolic-labels- e relabel))) + (rename-symbolic-labels- e (table) '())) ;; macro expander entry point (define (julia-expand-macros e (max-depth -1)) (julia-expand-macroscopes - (julia-expand-macros- '() e max-depth))) + (rename-symbolic-labels + (julia-expand-macros- '() e max-depth)))) (define (julia-expand-macros- m e max-depth) (cond ((= max-depth 0) e) @@ -504,7 +501,7 @@ (let ((form (car form)) ;; form is the expression returned from expand-macros (modu (cdr form))) ;; modu is the macro's def module `(hygienic-scope - ,(julia-expand-macros- (cons modu m) (rename-symbolic-labels form) (- max-depth 1)) + ,(julia-expand-macros- (cons modu m) form (- max-depth 1)) ,modu)))) ((eq? (car e) 'module) e) ((eq? (car e) 'escape) diff --git a/test/goto.jl b/test/goto.jl index 69a181756036f..b56cdf58b2064 100644 --- a/test/goto.jl +++ b/test/goto.jl @@ -1,6 +1,7 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license # Basic goto tests +expand(x) = Base.expand(@__MODULE__, x) function goto_test1() @goto a @@ -11,8 +12,13 @@ end @test goto_test1() -@test_throws ErrorException eval( - quote +@test eval(:(@label a)) === nothing + +@test Expr(:error, "label \"a\" referenced but not defined") == + expand(:(@goto a)) + +@test Expr(:error, "label \"a\" defined multiple times") == + expand(quote function goto_test2() @goto a @label a @@ -22,17 +28,16 @@ end end) -@test_throws ErrorException eval( - quote +@test Expr(:error, "label \"a\" referenced but not defined") == + expand(quote function goto_test3() @goto a return end end) - -@test_throws ErrorException eval( - quote +@test Expr(:error, "misplaced label") == + expand(quote function goto_test4() @goto a try @@ -43,23 +48,48 @@ end end) -# test that labels in macros are reassigned -macro goto_test5_macro() - @label a +# test that labels in macros are reassigned if unescaped +macro goto_test5_macro1() + return :(@label a) +end +macro goto_test5_macro2() + return :(@goto a) +end +macro goto_test5_macro3() + return esc(:(@label a)) end -@test_throws ErrorException eval( - quote - function goto_test5() +@test Expr(:error, "label \"a\" referenced but not defined") == + expand(quote + function goto_test5_1() @goto a - @goto_test5_macro + @goto_test5_macro1 return end end) +let e = expand(quote + function goto_test5_2() + @goto_test5_macro2 + @label a + return + end + end) + @test (e::Expr).head === :error + @test ismatch(r"label \"#\d+#a\" referenced but not defined", e.args[1]) +end + +function goto_test5_3() + @goto a + return false + @goto_test5_macro3 + return true +end +@test goto_test5_3() + -@test_throws ErrorException eval( - quote +@test Expr(:error, "goto from a try/finally block is not permitted") == + expand(quote function goto_test6() try @goto a From 67f3fc0f9a80ad2adf919097a9d3cdc6560fbaf5 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Wed, 16 Aug 2017 19:43:16 +0200 Subject: [PATCH 053/324] REPL: tab/backspace aligns to 4 (#22939) * REPL: tab/backspace aligns to 4 * when pressing tab, compute the number of spaces to insert so that the cursor is aligned to a multiple of 4 chars * when pressing backspace, delete up to 4 spaces so that the cursor is aligned to a multiple of 4 chars * multispaces defaults to false, and de-activate in search-mode * tab: jump to end of line; backspace: try to keep right side aligned * cosmetic changes --- base/repl/LineEdit.jl | 121 +++++++++++++++++++++++++++++------------- test/lineedit.jl | 80 ++++++++++++++++++++++++++++ 2 files changed, 165 insertions(+), 36 deletions(-) diff --git a/base/repl/LineEdit.jl b/base/repl/LineEdit.jl index 8c894bf32ea3f..7a355e80da9f2 100644 --- a/base/repl/LineEdit.jl +++ b/base/repl/LineEdit.jl @@ -478,22 +478,49 @@ function edit_insert(buf::IOBuffer, c) end end -function edit_backspace(s::PromptState) - if edit_backspace(s.input_buffer) - refresh_line(s) - else - beep(terminal(s)) - end +# align: delete up to 4 spaces to align to a multiple of 4 chars +# adjust: also delete spaces on the right of the cursor to try to keep aligned what is +# on the right +edit_backspace(s::PromptState, align::Bool=false, adjust=align) = + edit_backspace(s.input_buffer, align) ? refresh_line(s) : beep(terminal(s)) + +const _newline = UInt8('\n') +const _space = UInt8(' ') + +_notspace(c) = c != _space + +beginofline(buf, pos=position(buf)) = findprev(buf.data, _newline, pos) + +function endofline(buf, pos=position(buf)) + eol = findnext(buf.data[pos+1:buf.size], _newline, 1) + eol == 0 ? buf.size : pos + eol - 1 end -function edit_backspace(buf::IOBuffer) - if position(buf) > 0 - oldpos = position(buf) - char_move_left(buf) - splice_buffer!(buf, position(buf):oldpos-1) - return true - else - return false + +function edit_backspace(buf::IOBuffer, align::Bool=false, adjust::Bool=align) + !align && adjust && + throw(DomainError((align, adjust), + "if `adjust` is `true`, `align` must be `true`")) + oldpos = position(buf) + oldpos == 0 && return false + c = char_move_left(buf) + newpos = position(buf) + if align && c == ' ' # maybe delete multiple spaces + beg = beginofline(buf, newpos) + align = strwidth(String(buf.data[1+beg:newpos])) % 4 + nonspace = findprev(_notspace, buf.data, newpos) + if newpos - align >= nonspace + newpos -= align + seek(buf, newpos) + if adjust + spaces = findnext(_notspace, buf.data[newpos+2:buf.size], 1) + oldpos = spaces == 0 ? buf.size : + buf.data[newpos+1+spaces] == _newline ? newpos+spaces : + newpos + min(spaces, 4) + end + end end + splice_buffer!(buf, newpos:oldpos-1) + return true end edit_delete(s) = edit_delete(buffer(s)) ? refresh_line(s) : beep(terminal(s)) @@ -1337,30 +1364,52 @@ function bracketed_paste(s) return replace(input, '\t', " "^tabwidth) end +function tab_should_complete(s) + # Yes, we are ignoring the possiblity + # the we could be in the middle of a multi-byte + # sequence, here but that's ok, since any + # whitespace we're interested in is only one byte + buf = buffer(s) + pos = position(buf) + pos == 0 && return true + c = buf.data[pos] + c != _newline && c != UInt8('\t') && + # hack to allow path completion in cmds + # after a space, e.g., `cd `, while still + # allowing multiple indent levels + (c != _space || pos <= 3 || buf.data[pos-1] != _space) +end + +# jump_spaces: if cursor is on a ' ', move it to the first non-' ' char on the right +# if `delete_trailing`, ignore trailing ' ' by deleting them +function edit_tab(s, jump_spaces=false, delete_trailing=jump_spaces) + tab_should_complete(s) ? + complete_line(s) : + edit_tab(buffer(s), jump_spaces, delete_trailing) + refresh_line(s) +end + +function edit_tab(buf::IOBuffer, jump_spaces=false, delete_trailing=jump_spaces) + i = position(buf) + if jump_spaces && i < buf.size && buf.data[i+1] == _space + spaces = findnext(_notspace, buf.data[i+1:buf.size], 1) + if delete_trailing && (spaces == 0 || buf.data[i+spaces] == _newline) + splice_buffer!(buf, i:(spaces == 0 ? buf.size-1 : i+spaces-2)) + else + jump = spaces == 0 ? buf.size : i+spaces-1 + return seek(buf, jump) + end + end + # align to multiples of 4: + align = 4 - strwidth(String(buf.data[1+beginofline(buf, i):i])) % 4 + return edit_insert(buf, ' '^align) +end + + const default_keymap = AnyDict( # Tab - '\t' => (s,o...)->begin - buf = buffer(s) - # Yes, we are ignoring the possiblity - # the we could be in the middle of a multi-byte - # sequence, here but that's ok, since any - # whitespace we're interested in is only one byte - i = position(buf) - if i != 0 - c = buf.data[i] - if c == UInt8('\n') || c == UInt8('\t') || - # hack to allow path completion in cmds - # after a space, e.g., `cd `, while still - # allowing multiple indent levels - (c == UInt8(' ') && i > 3 && buf.data[i-1] == UInt8(' ')) - edit_insert(s, " "^4) - return - end - end - complete_line(s) - refresh_line(s) - end, + '\t' => (s,o...)->edit_tab(s, true), # Enter '\r' => (s,o...)->begin if on_enter(s) || (eof(buffer(s)) && s.key_repeats > 1) @@ -1372,7 +1421,7 @@ AnyDict( end, '\n' => KeyAlias('\r'), # Backspace/^H - '\b' => (s,o...)->edit_backspace(s), + '\b' => (s,o...)->edit_backspace(s, true), 127 => KeyAlias('\b'), # Meta Backspace "\e\b" => (s,o...)->edit_delete_prev_word(s), diff --git a/test/lineedit.jl b/test/lineedit.jl index ee558e91db875..c882dc5dc586d 100644 --- a/test/lineedit.jl +++ b/test/lineedit.jl @@ -427,3 +427,83 @@ end "\r\e[2C julia = :fun\n" * "\r\e[2Cend\r\e[5C" end + +@testset "tab/backspace alignment feature" begin + term = TestHelpers.FakeTerminal(IOBuffer(), IOBuffer(), IOBuffer()) + s = LineEdit.init_state(term, ModalInterface([Prompt("test> ")])) + function bufferdata(s) + buf = LineEdit.buffer(s) + String(buf.data[1:buf.size]) + end + move_left(s, n) = for x = 1:n + LineEdit.edit_move_left(s) + end + + bufpos(s::Base.LineEdit.MIState) = position(LineEdit.buffer(s)) + + LineEdit.edit_insert(s, "for x=1:10\n") + LineEdit.edit_tab(s) + @test bufferdata(s) == "for x=1:10\n " + LineEdit.edit_backspace(s, true, false) + @test bufferdata(s) == "for x=1:10\n" + LineEdit.edit_insert(s, " ") + @test bufpos(s) == 13 + LineEdit.edit_tab(s) + @test bufferdata(s) == "for x=1:10\n " + LineEdit.edit_insert(s, " ") + LineEdit.edit_backspace(s, true, false) + @test bufferdata(s) == "for x=1:10\n " + LineEdit.edit_insert(s, "éé=3 ") + LineEdit.edit_tab(s) + @test bufferdata(s) == "for x=1:10\n éé=3 " + LineEdit.edit_backspace(s, true, false) + @test bufferdata(s) == "for x=1:10\n éé=3" + LineEdit.edit_insert(s, "\n 1∉x ") + LineEdit.edit_tab(s) + @test bufferdata(s) == "for x=1:10\n éé=3\n 1∉x " + LineEdit.edit_backspace(s, false, false) + @test bufferdata(s) == "for x=1:10\n éé=3\n 1∉x " + LineEdit.edit_backspace(s, true, false) + @test bufferdata(s) == "for x=1:10\n éé=3\n 1∉x " + LineEdit.edit_move_word_left(s) + LineEdit.edit_tab(s) + @test bufferdata(s) == "for x=1:10\n éé=3\n 1∉x " + LineEdit.move_line_start(s) + @test bufpos(s) == 22 + LineEdit.edit_tab(s, true) + @test bufferdata(s) == "for x=1:10\n éé=3\n 1∉x " + @test bufpos(s) == 30 + LineEdit.edit_move_left(s) + @test bufpos(s) == 29 + LineEdit.edit_backspace(s, true, true) + @test bufferdata(s) == "for x=1:10\n éé=3\n 1∉x " + @test bufpos(s) == 26 + LineEdit.edit_tab(s, false) # same as edit_tab(s, true) here + @test bufpos(s) == 30 + move_left(s, 6) + @test bufpos(s) == 24 + LineEdit.edit_backspace(s, true, true) + @test bufferdata(s) == "for x=1:10\n éé=3\n 1∉x " + @test bufpos(s) == 22 + LineEdit.edit_kill_line(s) + LineEdit.edit_insert(s, ' '^10) + move_left(s, 7) + @test bufferdata(s) == "for x=1:10\n éé=3\n " + @test bufpos(s) == 25 + LineEdit.edit_tab(s, true, false) + @test bufpos(s) == 32 + move_left(s, 7) + LineEdit.edit_tab(s, true, true) + @test bufpos(s) == 26 + @test bufferdata(s) == "for x=1:10\n éé=3\n " + # test again the same, when there is a next line + LineEdit.edit_insert(s, " \nend") + move_left(s, 11) + @test bufpos(s) == 25 + LineEdit.edit_tab(s, true, false) + @test bufpos(s) == 32 + move_left(s, 7) + LineEdit.edit_tab(s, true, true) + @test bufpos(s) == 26 + @test bufferdata(s) == "for x=1:10\n éé=3\n \nend" +end From b36fc63257313e45a4d63458b0cfa51dda80d9d5 Mon Sep 17 00:00:00 2001 From: Jarrett Revels Date: Wed, 16 Aug 2017 14:55:24 -0400 Subject: [PATCH 054/324] fix incorrect static parameter munging as part of inlineable (#23274) --- base/inference.jl | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/base/inference.jl b/base/inference.jl index 78aaf2adbd8f1..3d37c611c6aa6 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -3757,7 +3757,9 @@ function substitute!(@nospecialize(e), na::Int, argexprs::Vector{Any}, @nospecia e = e::Expr head = e.head if head === :static_parameter - return spvals[e.args[1]] + sp = spvals[e.args[1]] + is_self_quoting(sp) && return sp + return QuoteNode(sp) elseif head === :foreigncall @assert !isa(spsig,UnionAll) || !isempty(spvals) for i = 1:length(e.args) @@ -4459,17 +4461,6 @@ function inlineable(@nospecialize(f), @nospecialize(ft), e::Expr, atypes::Vector add_backedge!(linfo, sv) end - spvals = Any[] - for i = 1:length(methsp) - push!(spvals, methsp[i]) - end - for i = 1:length(spvals) - si = spvals[i] - if isa(si, Symbol) || isa(si, SSAValue) || isa(si, Slot) - spvals[i] = QuoteNode(si) - end - end - nm = length(unwrap_unionall(metharg).parameters) body = Expr(:block) @@ -4535,7 +4526,7 @@ function inlineable(@nospecialize(f), @nospecialize(ft), e::Expr, atypes::Vector end # ok, substitute argument expressions for argument names in the body - body = substitute!(body, na, argexprs, method.sig, spvals, length(sv.src.slotnames) - na) + body = substitute!(body, na, argexprs, method.sig, Any[methsp...], length(sv.src.slotnames) - na) append!(sv.src.slotnames, src.slotnames[(na + 1):end]) append!(sv.src.slottypes, src.slottypes[(na + 1):end]) append!(sv.src.slotflags, src.slotflags[(na + 1):end]) From 33f8f6482304f744f10e38c576e434097cf1550a Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Tue, 25 Jul 2017 09:13:02 +0200 Subject: [PATCH 055/324] REPL: newline auto-indents like line above --- base/repl/LineEdit.jl | 24 +++++++++++++++++++----- test/lineedit.jl | 23 +++++++++++++++++++++++ 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/base/repl/LineEdit.jl b/base/repl/LineEdit.jl index 7a355e80da9f2..fa76131476569 100644 --- a/base/repl/LineEdit.jl +++ b/base/repl/LineEdit.jl @@ -90,9 +90,10 @@ complete_line(c::EmptyCompletionProvider, s) = [], true, true terminal(s::IO) = s terminal(s::PromptState) = s.terminal -for f in [:terminal, :edit_insert, :on_enter, :add_history, :buffer, :edit_backspace, :(Base.isempty), - :replace_line, :refresh_multi_line, :input_string, :edit_move_left, :edit_move_right, - :edit_move_word_left, :edit_move_word_right, :update_display_buffer] +for f in [:terminal, :edit_insert, :edit_insert_newline, :on_enter, :add_history, + :buffer, :edit_backspace, :(Base.isempty), :replace_line, :refresh_multi_line, + :input_string, :edit_move_left, :edit_move_right, + :edit_move_word_left, :edit_move_word_right, :update_display_buffer] @eval ($f)(s::MIState, args...) = $(f)(s.mode_state[s.current_mode], args...) end @@ -478,6 +479,19 @@ function edit_insert(buf::IOBuffer, c) end end +# align: number of ' ' to insert after '\n' +# if align < 0: align like line above +function edit_insert_newline(s::PromptState, align=-1) + buf = buffer(s) + if align < 0 + beg = beginofline(buf) + align = findnext(_notspace, buf.data[beg+1:buf.size], 1) - 1 + align < 0 && (align = buf.size-beg) + end + edit_insert(buf, '\n' * ' '^align) + refresh_line(s) +end + # align: delete up to 4 spaces to align to a multiple of 4 chars # adjust: also delete spaces on the right of the cursor to try to keep aligned what is # on the right @@ -1416,7 +1430,7 @@ AnyDict( commit_line(s) return :done else - edit_insert(s, '\n') + edit_insert_newline(s) end end, '\n' => KeyAlias('\r'), @@ -1450,7 +1464,7 @@ AnyDict( # Ctrl-Right Arrow on rxvt "\eOc" => "\ef", # Meta Enter - "\e\r" => (s,o...)->(edit_insert(s, '\n')), + "\e\r" => (s,o...)->edit_insert_newline(s), "\e\n" => "\e\r", # Simply insert it into the buffer by default "*" => (s,data,c)->(edit_insert(s, c)), diff --git a/test/lineedit.jl b/test/lineedit.jl index c882dc5dc586d..89437b61d523a 100644 --- a/test/lineedit.jl +++ b/test/lineedit.jl @@ -507,3 +507,26 @@ end @test bufpos(s) == 26 @test bufferdata(s) == "for x=1:10\n éé=3\n \nend" end + +@testset "newline alignment feature" begin + term = TestHelpers.FakeTerminal(IOBuffer(), IOBuffer(), IOBuffer()) + s = LineEdit.init_state(term, ModalInterface([Prompt("test> ")])) + function bufferdata(s) + buf = LineEdit.buffer(s) + String(buf.data[1:buf.size]) + end + + LineEdit.edit_insert(s, "for x=1:10\n é = 1") + LineEdit.edit_insert_newline(s) + @test bufferdata(s) == "for x=1:10\n é = 1\n " + LineEdit.edit_insert(s, " b = 2") + LineEdit.edit_insert_newline(s) + @test bufferdata(s) == "for x=1:10\n é = 1\n b = 2\n " + # after an empty line, should still insert the expected number of spaces + LineEdit.edit_insert_newline(s) + @test bufferdata(s) == "for x=1:10\n é = 1\n b = 2\n \n " + LineEdit.edit_insert_newline(s, 0) + @test bufferdata(s) == "for x=1:10\n é = 1\n b = 2\n \n \n" + LineEdit.edit_insert_newline(s, 2) + @test bufferdata(s) == "for x=1:10\n é = 1\n b = 2\n \n \n\n " +end From d6d9185154a5860b9382a2f1146a23764ae748c8 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Mon, 10 Jul 2017 17:20:52 +0200 Subject: [PATCH 056/324] split random.jl into random/ --- base/random/RNGs.jl | 455 +++++++++ base/{ => random}/dSFMT.jl | 0 base/random/generation.jl | 334 +++++++ base/random/misc.jl | 455 +++++++++ base/{random.jl => random/normal.jl} | 1277 -------------------------- base/random/random.jl | 45 + base/sysimg.jl | 4 +- 7 files changed, 1291 insertions(+), 1279 deletions(-) create mode 100644 base/random/RNGs.jl rename base/{ => random}/dSFMT.jl (100%) create mode 100644 base/random/generation.jl create mode 100644 base/random/misc.jl rename base/{random.jl => random/normal.jl} (52%) create mode 100644 base/random/random.jl diff --git a/base/random/RNGs.jl b/base/random/RNGs.jl new file mode 100644 index 0000000000000..edf9363704e6c --- /dev/null +++ b/base/random/RNGs.jl @@ -0,0 +1,455 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +## RandomDevice + +const BoolBitIntegerType = Union{Type{Bool},Base.BitIntegerType} +const BoolBitIntegerArray = Union{Array{Bool},Base.BitIntegerArray} + +if Sys.iswindows() + struct RandomDevice <: AbstractRNG + buffer::Vector{UInt128} + + RandomDevice() = new(Vector{UInt128}(1)) + end + + function rand(rd::RandomDevice, T::BoolBitIntegerType) + win32_SystemFunction036!(rd.buffer) + @inbounds return rd.buffer[1] % T + end + + rand!(rd::RandomDevice, A::BoolBitIntegerArray) = (win32_SystemFunction036!(A); A) +else # !windows + struct RandomDevice <: AbstractRNG + file::IOStream + unlimited::Bool + + RandomDevice(unlimited::Bool=true) = new(open(unlimited ? "/dev/urandom" : "/dev/random"), unlimited) + end + + rand(rd::RandomDevice, T::BoolBitIntegerType) = read( rd.file, T) + rand!(rd::RandomDevice, A::BoolBitIntegerArray) = read!(rd.file, A) +end # os-test + +""" + RandomDevice() + +Create a `RandomDevice` RNG object. Two such objects will always generate different streams of random numbers. +""" +RandomDevice + + +rand(rng::RandomDevice, ::Type{Close1Open2}) = + reinterpret(Float64, 0x3ff0000000000000 | rand(rng, UInt64) & 0x000fffffffffffff) + +rand(rng::RandomDevice, ::Type{CloseOpen}) = rand(rng, Close1Open2) - 1.0 + + +## MersenneTwister + +const MTCacheLength = dsfmt_get_min_array_size() + +mutable struct MersenneTwister <: AbstractRNG + seed::Vector{UInt32} + state::DSFMT_state + vals::Vector{Float64} + idx::Int + + function MersenneTwister(seed, state, vals, idx) + if !(length(vals) == MTCacheLength && 0 <= idx <= MTCacheLength) + throw(DomainError(idx, "`length(vals)` and `idx` must be consistent with $MTCacheLength")) + end + new(seed, state, vals, idx) + end +end + +MersenneTwister(seed::Vector{UInt32}, state::DSFMT_state) = + MersenneTwister(seed, state, zeros(Float64, MTCacheLength), MTCacheLength) + +""" + MersenneTwister(seed) + +Create a `MersenneTwister` RNG object. Different RNG objects can have their own seeds, which +may be useful for generating different streams of random numbers. + +# Examples +```jldoctest +julia> rng = MersenneTwister(1234); + +julia> x1 = rand(rng, 2) +2-element Array{Float64,1}: + 0.590845 + 0.766797 + +julia> rng = MersenneTwister(1234); + +julia> x2 = rand(rng, 2) +2-element Array{Float64,1}: + 0.590845 + 0.766797 + +julia> x1 == x2 +true +``` +""" +MersenneTwister(seed) = srand(MersenneTwister(Vector{UInt32}(), DSFMT_state()), seed) + +function copy!(dst::MersenneTwister, src::MersenneTwister) + copy!(resize!(dst.seed, length(src.seed)), src.seed) + copy!(dst.state, src.state) + copy!(dst.vals, src.vals) + dst.idx = src.idx + dst +end + +copy(src::MersenneTwister) = + MersenneTwister(copy(src.seed), copy(src.state), copy(src.vals), src.idx) + +==(r1::MersenneTwister, r2::MersenneTwister) = + r1.seed == r2.seed && r1.state == r2.state && isequal(r1.vals, r2.vals) && r1.idx == r2.idx + +hash(r::MersenneTwister, h::UInt) = foldr(hash, h, (r.seed, r.state, r.vals, r.idx)) + +## Low level API for MersenneTwister + +@inline mt_avail(r::MersenneTwister) = MTCacheLength - r.idx +@inline mt_empty(r::MersenneTwister) = r.idx == MTCacheLength +@inline mt_setfull!(r::MersenneTwister) = r.idx = 0 +@inline mt_setempty!(r::MersenneTwister) = r.idx = MTCacheLength +@inline mt_pop!(r::MersenneTwister) = @inbounds return r.vals[r.idx+=1] + +function gen_rand(r::MersenneTwister) + dsfmt_fill_array_close1_open2!(r.state, pointer(r.vals), length(r.vals)) + mt_setfull!(r) +end + +@inline reserve_1(r::MersenneTwister) = (mt_empty(r) && gen_rand(r); nothing) +# `reserve` allows one to call `rand_inbounds` n times +# precondition: n <= MTCacheLength +@inline reserve(r::MersenneTwister, n::Int) = (mt_avail(r) < n && gen_rand(r); nothing) + +# precondition: !mt_empty(r) +@inline rand_inbounds(r::MersenneTwister, ::Type{Close1Open2}) = mt_pop!(r) +@inline rand_inbounds(r::MersenneTwister, ::Type{CloseOpen}) = rand_inbounds(r, Close1Open2) - 1.0 +@inline rand_inbounds(r::MersenneTwister) = rand_inbounds(r, CloseOpen) + +# produce Float64 values +@inline rand(r::MersenneTwister, ::Type{I}) where {I<:FloatInterval} = (reserve_1(r); rand_inbounds(r, I)) + +@inline rand_ui52_raw_inbounds(r::MersenneTwister) = reinterpret(UInt64, rand_inbounds(r, Close1Open2)) +@inline rand_ui52_raw(r::MersenneTwister) = (reserve_1(r); rand_ui52_raw_inbounds(r)) + +@inline function rand_ui2x52_raw(r::MersenneTwister) + reserve(r, 2) + rand_ui52_raw_inbounds(r) % UInt128 << 64 | rand_ui52_raw_inbounds(r) +end + +@inline function rand_ui104_raw(r::MersenneTwister) + reserve(r, 2) + rand_ui52_raw_inbounds(r) % UInt128 << 52 ⊻ rand_ui52_raw_inbounds(r) +end + +function srand(r::MersenneTwister, seed::Vector{UInt32}) + copy!(resize!(r.seed, length(seed)), seed) + dsfmt_init_by_array(r.state, r.seed) + mt_setempty!(r) + return r +end + +# MersenneTwister jump + +""" + randjump(r::MersenneTwister, jumps::Integer, [jumppoly::AbstractString=dSFMT.JPOLY1e21]) -> Vector{MersenneTwister} + +Create an array of the size `jumps` of initialized `MersenneTwister` RNG objects. The +first RNG object given as a parameter and following `MersenneTwister` RNGs in the array are +initialized such that a state of the RNG object in the array would be moved forward (without +generating numbers) from a previous RNG object array element on a particular number of steps +encoded by the jump polynomial `jumppoly`. + +Default jump polynomial moves forward `MersenneTwister` RNG state by `10^20` steps. +""" +function randjump(mt::MersenneTwister, jumps::Integer, jumppoly::AbstractString) + mts = MersenneTwister[] + push!(mts, mt) + for i in 1:jumps-1 + cmt = mts[end] + push!(mts, MersenneTwister(copy(cmt.seed), dSFMT.dsfmt_jump(cmt.state, jumppoly))) + end + return mts +end +randjump(r::MersenneTwister, jumps::Integer) = randjump(r, jumps, dSFMT.JPOLY1e21) + + +## make_seed() +# make_seed methods produce values of type Array{UInt32}, suitable for MersenneTwister seeding + +function make_seed() + try + return rand(RandomDevice(), UInt32, 4) + catch + println(STDERR, "Entropy pool not available to seed RNG; using ad-hoc entropy sources.") + seed = reinterpret(UInt64, time()) + seed = hash(seed, UInt64(getpid())) + try + seed = hash(seed, parse(UInt64, read(pipeline(`ifconfig`, `sha1sum`), String)[1:40], 16)) + end + return make_seed(seed) + end +end + +function make_seed(n::Integer) + n < 0 && throw(DomainError(n, "`n` must be non-negative.")) + seed = UInt32[] + while true + push!(seed, n & 0xffffffff) + n >>= 32 + if n == 0 + return seed + end + end +end + +## srand() + +""" + srand([rng=GLOBAL_RNG], seed) -> rng + srand([rng=GLOBAL_RNG]) -> rng + +Reseed the random number generator. If a `seed` is provided, the RNG will give a +reproducible sequence of numbers, otherwise Julia will get entropy from the system. For +`MersenneTwister`, the `seed` may be a non-negative integer or a vector of [`UInt32`](@ref) +integers. `RandomDevice` does not support seeding. + +# Examples +```jldoctest +julia> srand(1234); + +julia> x1 = rand(2) +2-element Array{Float64,1}: + 0.590845 + 0.766797 + +julia> srand(1234); + +julia> x2 = rand(2) +2-element Array{Float64,1}: + 0.590845 + 0.766797 + +julia> x1 == x2 +true +``` +""" +srand(r::MersenneTwister) = srand(r, make_seed()) +srand(r::MersenneTwister, n::Integer) = srand(r, make_seed(n)) + +function srand() + srand(GLOBAL_RNG) +end + +function srand(seed::Union{Integer,Vector{UInt32}}) + srand(GLOBAL_RNG, seed) +end + +## Global RNG + +const GLOBAL_RNG = MersenneTwister(0) +globalRNG() = GLOBAL_RNG + + +## random generation for RandomDevice & MersenneTwister + +@inline rand(r::Union{RandomDevice,MersenneTwister}, ::Type{Float64}) = rand(r, CloseOpen) + +rand_ui10_raw(r::MersenneTwister) = rand_ui52_raw(r) +rand_ui23_raw(r::MersenneTwister) = rand_ui52_raw(r) + +rand(r::Union{RandomDevice,MersenneTwister}, ::Type{Float16}) = + Float16(reinterpret(Float32, (rand_ui10_raw(r) % UInt32 << 13) & 0x007fe000 | 0x3f800000) - 1) + +rand(r::Union{RandomDevice,MersenneTwister}, ::Type{Float32}) = + reinterpret(Float32, rand_ui23_raw(r) % UInt32 & 0x007fffff | 0x3f800000) - 1 + +@inline rand(r::MersenneTwister, ::Type{T}) where {T<:Union{Bool,Int8,UInt8,Int16,UInt16,Int32,UInt32}} = + rand_ui52_raw(r) % T + +function rand(r::MersenneTwister, ::Type{UInt64}) + reserve(r, 2) + rand_ui52_raw_inbounds(r) << 32 ⊻ rand_ui52_raw_inbounds(r) +end + +function rand(r::MersenneTwister, ::Type{UInt128}) + reserve(r, 3) + xor(rand_ui52_raw_inbounds(r) % UInt128 << 96, + rand_ui52_raw_inbounds(r) % UInt128 << 48, + rand_ui52_raw_inbounds(r)) +end + +rand(r::MersenneTwister, ::Type{Int64}) = reinterpret(Int64, rand(r, UInt64)) +rand(r::MersenneTwister, ::Type{Int128}) = reinterpret(Int128, rand(r, UInt128)) + +function rand_AbstractArray_Float64!(r::MersenneTwister, A::AbstractArray{Float64}, + n=length(A), ::Type{I}=CloseOpen) where I<:FloatInterval + # what follows is equivalent to this simple loop but more efficient: + # for i=1:n + # @inbounds A[i] = rand(r, I) + # end + m = 0 + while m < n + s = mt_avail(r) + if s == 0 + gen_rand(r) + s = mt_avail(r) + end + m2 = min(n, m+s) + for i=m+1:m2 + @inbounds A[i] = rand_inbounds(r, I) + end + m = m2 + end + A +end + +rand!(r::MersenneTwister, A::AbstractArray{Float64}) = rand_AbstractArray_Float64!(r, A) + +fill_array!(s::DSFMT_state, A::Ptr{Float64}, n::Int, ::Type{CloseOpen}) = dsfmt_fill_array_close_open!(s, A, n) +fill_array!(s::DSFMT_state, A::Ptr{Float64}, n::Int, ::Type{Close1Open2}) = dsfmt_fill_array_close1_open2!(s, A, n) + +function rand!(r::MersenneTwister, A::Array{Float64}, n::Int=length(A), ::Type{I}=CloseOpen) where I<:FloatInterval + # depending on the alignment of A, the data written by fill_array! may have + # to be left-shifted by up to 15 bytes (cf. unsafe_copy! below) for + # reproducibility purposes; + # so, even for well aligned arrays, fill_array! is used to generate only + # the n-2 first values (or n-3 if n is odd), and the remaining values are + # generated by the scalar version of rand + if n > length(A) + throw(BoundsError(A,n)) + end + n2 = (n-2) ÷ 2 * 2 + if n2 < dsfmt_get_min_array_size() + rand_AbstractArray_Float64!(r, A, n, I) + else + pA = pointer(A) + align = Csize_t(pA) % 16 + if align > 0 + pA2 = pA + 16 - align + fill_array!(r.state, pA2, n2, I) # generate the data in-place, but shifted + unsafe_copy!(pA, pA2, n2) # move the data to the beginning of the array + else + fill_array!(r.state, pA, n2, I) + end + for i=n2+1:n + @inbounds A[i] = rand(r, I) + end + end + A +end + +@inline mask128(u::UInt128, ::Type{Float16}) = (u & 0x03ff03ff03ff03ff03ff03ff03ff03ff) | 0x3c003c003c003c003c003c003c003c00 +@inline mask128(u::UInt128, ::Type{Float32}) = (u & 0x007fffff007fffff007fffff007fffff) | 0x3f8000003f8000003f8000003f800000 + +function rand!(r::MersenneTwister, A::Union{Array{Float16},Array{Float32}}, ::Type{Close1Open2}) + T = eltype(A) + n = length(A) + n128 = n * sizeof(T) ÷ 16 + rand!(r, unsafe_wrap(Array, convert(Ptr{Float64}, pointer(A)), 2*n128), 2*n128, Close1Open2) + A128 = unsafe_wrap(Array, convert(Ptr{UInt128}, pointer(A)), n128) + @inbounds for i in 1:n128 + u = A128[i] + u ⊻= u << 26 + # at this point, the 64 low bits of u, "k" being the k-th bit of A128[i] and "+" the bit xor, are: + # [..., 58+32,..., 53+27, 52+26, ..., 33+7, 32+6, ..., 27+1, 26, ..., 1] + # the bits needing to be random are + # [1:10, 17:26, 33:42, 49:58] (for Float16) + # [1:23, 33:55] (for Float32) + # this is obviously satisfied on the 32 low bits side, and on the high side, the entropy comes + # from bits 33:52 of A128[i] and then from bits 27:32 (which are discarded on the low side) + # this is similar for the 64 high bits of u + A128[i] = mask128(u, T) + end + for i in 16*n128÷sizeof(T)+1:n + @inbounds A[i] = rand(r, T) + oneunit(T) + end + A +end + +function rand!(r::MersenneTwister, A::Union{Array{Float16},Array{Float32}}, ::Type{CloseOpen}) + rand!(r, A, Close1Open2) + I32 = one(Float32) + for i in eachindex(A) + @inbounds A[i] = Float32(A[i])-I32 # faster than "A[i] -= one(T)" for T==Float16 + end + A +end + +rand!(r::MersenneTwister, A::Union{Array{Float16},Array{Float32}}) = rand!(r, A, CloseOpen) + +function rand!(r::MersenneTwister, A::Array{UInt128}, n::Int=length(A)) + if n > length(A) + throw(BoundsError(A,n)) + end + Af = unsafe_wrap(Array, convert(Ptr{Float64}, pointer(A)), 2n) + i = n + while true + rand!(r, Af, 2i, Close1Open2) + n < 5 && break + i = 0 + @inbounds while n-i >= 5 + u = A[i+=1] + A[n] ⊻= u << 48 + A[n-=1] ⊻= u << 36 + A[n-=1] ⊻= u << 24 + A[n-=1] ⊻= u << 12 + n-=1 + end + end + if n > 0 + u = rand_ui2x52_raw(r) + for i = 1:n + @inbounds A[i] ⊻= u << (12*i) + end + end + A +end + +# A::Array{UInt128} will match the specialized method above +function rand!(r::MersenneTwister, A::Base.BitIntegerArray) + n = length(A) + T = eltype(A) + n128 = n * sizeof(T) ÷ 16 + rand!(r, unsafe_wrap(Array, convert(Ptr{UInt128}, pointer(A)), n128)) + for i = 16*n128÷sizeof(T)+1:n + @inbounds A[i] = rand(r, T) + end + A +end + +## special case for range generation with MersenneTwister + +@inline function rand_lteq(r::AbstractRNG, randfun, u::U, mask::U) where U<:Integer + while true + x = randfun(r) & mask + x <= u && return x + end +end + +function rand(rng::MersenneTwister, r::UnitRange{T}) where T<:Union{Base.BitInteger64,Bool} + isempty(r) && throw(ArgumentError("range must be non-empty")) + m = last(r) % UInt64 - first(r) % UInt64 + bw = (64 - leading_zeros(m)) % UInt # bit-width + mask = (1 % UInt64 << bw) - (1 % UInt64) + x = bw <= 52 ? rand_lteq(rng, rand_ui52_raw, m, mask) : + rand_lteq(rng, rng->rand(rng, UInt64), m, mask) + (x + first(r) % UInt64) % T +end + +function rand(rng::MersenneTwister, r::UnitRange{T}) where T<:Union{Int128,UInt128} + isempty(r) && throw(ArgumentError("range must be non-empty")) + m = (last(r)-first(r)) % UInt128 + bw = (128 - leading_zeros(m)) % UInt # bit-width + mask = (1 % UInt128 << bw) - (1 % UInt128) + x = bw <= 52 ? rand_lteq(rng, rand_ui52_raw, m % UInt64, mask % UInt64) % UInt128 : + bw <= 104 ? rand_lteq(rng, rand_ui104_raw, m, mask) : + rand_lteq(rng, rng->rand(rng, UInt128), m, mask) + x % T + first(r) +end diff --git a/base/dSFMT.jl b/base/random/dSFMT.jl similarity index 100% rename from base/dSFMT.jl rename to base/random/dSFMT.jl diff --git a/base/random/generation.jl b/base/random/generation.jl new file mode 100644 index 0000000000000..bda4bd669a8c4 --- /dev/null +++ b/base/random/generation.jl @@ -0,0 +1,334 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# rand: a non-specified RNG defaults to GLOBAL_RNG + +""" + rand([rng=GLOBAL_RNG], [S], [dims...]) + +Pick a random element or array of random elements from the set of values specified by `S`; `S` can be + +* an indexable collection (for example `1:n` or `['x','y','z']`), +* an `Associative` or `AbstractSet` object, +* a string (considered as a collection of characters), or +* a type: the set of values to pick from is then equivalent to `typemin(S):typemax(S)` for + integers (this is not applicable to [`BigInt`](@ref)), and to ``[0, 1)`` for floating + point numbers; + +`S` defaults to [`Float64`](@ref). + +# Examples +```julia-repl +julia> rand(Int, 2) +2-element Array{Int64,1}: + 1339893410598768192 + 1575814717733606317 + +julia> rand(MersenneTwister(0), Dict(1=>2, 3=>4)) +1=>2 +``` + +!!! note + The complexity of `rand(rng, s::Union{Associative,AbstractSet})` + is linear in the length of `s`, unless an optimized method with + constant complexity is available, which is the case for `Dict`, + `Set` and `IntSet`. For more than a few calls, use `rand(rng, + collect(s))` instead, or either `rand(rng, Dict(s))` or `rand(rng, + Set(s))` as appropriate. +""" +@inline rand() = rand(GLOBAL_RNG, CloseOpen) +@inline rand(T::Type) = rand(GLOBAL_RNG, T) +rand(dims::Dims) = rand(GLOBAL_RNG, dims) +rand(dims::Integer...) = rand(convert(Dims, dims)) +rand(T::Type, dims::Dims) = rand(GLOBAL_RNG, T, dims) +rand(T::Type, d1::Integer, dims::Integer...) = rand(T, tuple(Int(d1), convert(Dims, dims)...)) +rand!(A::AbstractArray) = rand!(GLOBAL_RNG, A) + +rand(r::AbstractArray) = rand(GLOBAL_RNG, r) + +""" + rand!([rng=GLOBAL_RNG], A, [S=eltype(A)]) + +Populate the array `A` with random values. If `S` is specified +(`S` can be a type or a collection, cf. [`rand`](@ref) for details), +the values are picked randomly from `S`. +This is equivalent to `copy!(A, rand(rng, S, size(A)))` +but without allocating a new array. + +# Examples +```jldoctest +julia> rng = MersenneTwister(1234); + +julia> rand!(rng, zeros(5)) +5-element Array{Float64,1}: + 0.590845 + 0.766797 + 0.566237 + 0.460085 + 0.794026 +``` +""" +rand!(A::AbstractArray, r::AbstractArray) = rand!(GLOBAL_RNG, A, r) + +rand(r::AbstractArray, dims::Dims) = rand(GLOBAL_RNG, r, dims) +rand(r::AbstractArray, dims::Integer...) = rand(GLOBAL_RNG, r, convert(Dims, dims)) + +## random floating point values + +@inline rand(r::AbstractRNG) = rand(r, CloseOpen) + +rand_ui10_raw(r::AbstractRNG) = rand(r, UInt16) +rand_ui23_raw(r::AbstractRNG) = rand(r, UInt32) + +## random integers + +@inline rand_ui52_raw(r::AbstractRNG) = reinterpret(UInt64, rand(r, Close1Open2)) +@inline rand_ui52(r::AbstractRNG) = rand_ui52_raw(r) & 0x000fffffffffffff + +## random Complex values + +rand(r::AbstractRNG, ::Type{Complex{T}}) where {T<:Real} = complex(rand(r, T), rand(r, T)) + +# random Char values +# returns a random valid Unicode scalar value (i.e. 0 - 0xd7ff, 0xe000 - # 0x10ffff) +function rand(r::AbstractRNG, ::Type{Char}) + c = rand(r, 0x00000000:0x0010f7ff) + (c < 0xd800) ? Char(c) : Char(c+0x800) +end + +# random values from Dict, Set, IntSet (for efficiency) +function rand(r::AbstractRNG, t::Dict) + isempty(t) && throw(ArgumentError("collection must be non-empty")) + rg = RangeGenerator(1:length(t.slots)) + while true + i = rand(r, rg) + Base.isslotfilled(t, i) && @inbounds return (t.keys[i] => t.vals[i]) + end +end + +rand(r::AbstractRNG, s::Set) = rand(r, s.dict).first + +function rand(r::AbstractRNG, s::IntSet) + isempty(s) && throw(ArgumentError("collection must be non-empty")) + # s can be empty while s.bits is not, so we cannot rely on the + # length check in RangeGenerator below + rg = RangeGenerator(1:length(s.bits)) + while true + n = rand(r, rg) + @inbounds b = s.bits[n] + b && return n + end +end + +function nth(iter, n::Integer)::eltype(iter) + for (i, x) in enumerate(iter) + i == n && return x + end +end +nth(iter::AbstractArray, n::Integer) = iter[n] + +rand(r::AbstractRNG, s::Union{Associative,AbstractSet}) = nth(s, rand(r, 1:length(s))) + +rand(s::Union{Associative,AbstractSet}) = rand(GLOBAL_RNG, s) + +## Arrays of random numbers + +rand(r::AbstractRNG, dims::Dims) = rand(r, Float64, dims) +rand(r::AbstractRNG, dims::Integer...) = rand(r, convert(Dims, dims)) + +rand(r::AbstractRNG, T::Type, dims::Dims) = rand!(r, Array{T}(dims)) +rand(r::AbstractRNG, T::Type, d1::Integer, dims::Integer...) = rand(r, T, tuple(Int(d1), convert(Dims, dims)...)) +# note: the above method would trigger an ambiguity warning if d1 was not separated out: +# rand(r, ()) would match both this method and rand(r, dims::Dims) +# moreover, a call like rand(r, NotImplementedType()) would be an infinite loop + +function rand!(r::AbstractRNG, A::AbstractArray{T}, ::Type{X}=T) where {T,X} + for i in eachindex(A) + @inbounds A[i] = rand(r, X) + end + A +end + +rand!(A::AbstractArray, ::Type{X}) where {X} = rand!(GLOBAL_RNG, A, X) + +function rand!(r::AbstractRNG, A::AbstractArray, s::Union{Dict,Set,IntSet}) + for i in eachindex(A) + @inbounds A[i] = rand(r, s) + end + A +end + +# avoid linear complexity for repeated calls with generic containers +rand!(r::AbstractRNG, A::AbstractArray, s::Union{Associative,AbstractSet}) = rand!(r, A, collect(s)) + +rand!(A::AbstractArray, s::Union{Associative,AbstractSet}) = rand!(GLOBAL_RNG, A, s) + +rand(r::AbstractRNG, s::Associative{K,V}, dims::Dims) where {K,V} = rand!(r, Array{Pair{K,V}}(dims), s) +rand(r::AbstractRNG, s::AbstractSet{T}, dims::Dims) where {T} = rand!(r, Array{T}(dims), s) +rand(r::AbstractRNG, s::Union{Associative,AbstractSet}, dims::Integer...) = rand(r, s, convert(Dims, dims)) +rand(s::Union{Associative,AbstractSet}, dims::Integer...) = rand(GLOBAL_RNG, s, convert(Dims, dims)) +rand(s::Union{Associative,AbstractSet}, dims::Dims) = rand(GLOBAL_RNG, s, dims) + + +## Generate random integer within a range + +# remainder function according to Knuth, where rem_knuth(a, 0) = a +rem_knuth(a::UInt, b::UInt) = a % (b + (b == 0)) + a * (b == 0) +rem_knuth(a::T, b::T) where {T<:Unsigned} = b != 0 ? a % b : a + +# maximum multiple of k <= 2^bits(T) decremented by one, +# that is 0xFFFF...FFFF if k = typemax(T) - typemin(T) with intentional underflow +# see http://stackoverflow.com/questions/29182036/integer-arithmetic-add-1-to-uint-max-and-divide-by-n-without-overflow +maxmultiple(k::T) where {T<:Unsigned} = (div(typemax(T) - k + oneunit(k), k + (k == 0))*k + k - oneunit(k))::T + +# maximum multiple of k within 1:2^32 or 1:2^64 decremented by one, depending on size +maxmultiplemix(k::UInt64) = if k >> 32 != 0; maxmultiple(k); else (div(0x0000000100000000, k + (k == 0))*k - oneunit(k))::UInt64; end + +abstract type RangeGenerator end + +struct RangeGeneratorInt{T<:Integer,U<:Unsigned} <: RangeGenerator + a::T # first element of the range + k::U # range length or zero for full range + u::U # rejection threshold +end +# generators with 32, 128 bits entropy +RangeGeneratorInt(a::T, k::U) where {T,U<:Union{UInt32,UInt128}} = RangeGeneratorInt{T,U}(a, k, maxmultiple(k)) +# mixed 32/64 bits entropy generator +RangeGeneratorInt(a::T, k::UInt64) where {T} = RangeGeneratorInt{T,UInt64}(a, k, maxmultiplemix(k)) +# generator for ranges +function RangeGenerator(r::UnitRange{T}) where T<:Unsigned + if isempty(r) + throw(ArgumentError("range must be non-empty")) + end + RangeGeneratorInt(first(r), last(r) - first(r) + oneunit(T)) +end + +# specialized versions +for (T, U) in [(UInt8, UInt32), (UInt16, UInt32), + (Int8, UInt32), (Int16, UInt32), (Int32, UInt32), (Int64, UInt64), (Int128, UInt128), + (Bool, UInt32)] + + @eval RangeGenerator(r::UnitRange{$T}) = begin + if isempty(r) + throw(ArgumentError("range must be non-empty")) + end + RangeGeneratorInt(first(r), convert($U, unsigned(last(r) - first(r)) + one($U))) # overflow ok + end +end + +struct RangeGeneratorBigInt <: RangeGenerator + a::BigInt # first + m::BigInt # range length - 1 + nlimbs::Int # number of limbs in generated BigInt's (z ∈ [0, m]) + nlimbsmax::Int # max number of limbs for z+a + mask::Limb # applied to the highest limb +end + + +function RangeGenerator(r::UnitRange{BigInt}) + m = last(r) - first(r) + m < 0 && throw(ArgumentError("range must be non-empty")) + nd = ndigits(m, 2) + nlimbs, highbits = divrem(nd, 8*sizeof(Limb)) + highbits > 0 && (nlimbs += 1) + mask = highbits == 0 ? ~zero(Limb) : one(Limb)<> 32 == 0 + x = rand(rng, UInt32) + while x > g.u + x = rand(rng, UInt32) + end + else + x = rand(rng, UInt64) + while x > g.u + x = rand(rng, UInt64) + end + end + return reinterpret(T, reinterpret(UInt64, g.a) + rem_knuth(x, g.k)) +end + +function rand(rng::AbstractRNG, g::RangeGeneratorInt{T,U}) where U<:Unsigned where T<:Integer + x = rand(rng, U) + while x > g.u + x = rand(rng, U) + end + (unsigned(g.a) + rem_knuth(x, g.k)) % T +end + +function rand(rng::AbstractRNG, g::RangeGeneratorBigInt) + x = MPZ.realloc2(g.nlimbsmax*8*sizeof(Limb)) + limbs = unsafe_wrap(Array, x.d, g.nlimbs) + while true + rand!(rng, limbs) + @inbounds limbs[end] &= g.mask + MPZ.mpn_cmp(x, g.m, g.nlimbs) <= 0 && break + end + # adjust x.size (normally done by mpz_limbs_finish, in GMP version >= 6) + x.size = g.nlimbs + while x.size > 0 + @inbounds limbs[x.size] != 0 && break + x.size -= 1 + end + MPZ.add!(x, g.a) +end + +rand(rng::AbstractRNG, r::UnitRange{<:Union{Signed,Unsigned,BigInt,Bool}}) = rand(rng, RangeGenerator(r)) + + +# Randomly draw a sample from an AbstractArray r +# (e.g. r is a range 0:2:8 or a vector [2, 3, 5, 7]) +rand(rng::AbstractRNG, r::AbstractArray) = @inbounds return r[rand(rng, 1:length(r))] + +function rand!(rng::AbstractRNG, A::AbstractArray, g::RangeGenerator) + for i in eachindex(A) + @inbounds A[i] = rand(rng, g) + end + return A +end + +rand!(rng::AbstractRNG, A::AbstractArray, r::UnitRange{<:Union{Signed,Unsigned,BigInt,Bool,Char}}) = rand!(rng, A, RangeGenerator(r)) + +function rand!(rng::AbstractRNG, A::AbstractArray, r::AbstractArray) + g = RangeGenerator(1:(length(r))) + for i in eachindex(A) + @inbounds A[i] = r[rand(rng, g)] + end + return A +end + +rand(rng::AbstractRNG, r::AbstractArray{T}, dims::Dims) where {T} = rand!(rng, Array{T}(dims), r) +rand(rng::AbstractRNG, r::AbstractArray, dims::Integer...) = rand(rng, r, convert(Dims, dims)) + +# rand from a string + +isvalid_unsafe(s::String, i) = !Base.is_valid_continuation(unsafe_load(pointer(s), i)) +isvalid_unsafe(s::AbstractString, i) = isvalid(s, i) +_endof(s::String) = sizeof(s) +_endof(s::AbstractString) = endof(s) + +function rand(rng::AbstractRNG, s::AbstractString)::Char + g = RangeGenerator(1:_endof(s)) + while true + pos = rand(rng, g) + isvalid_unsafe(s, pos) && return s[pos] + end +end + +rand(s::AbstractString) = rand(GLOBAL_RNG, s) + +## rand from a string for arrays +# we use collect(str), which is most of the time more efficient than specialized methods +# (except maybe for very small arrays) +rand!(rng::AbstractRNG, A::AbstractArray, str::AbstractString) = rand!(rng, A, collect(str)) +rand!(A::AbstractArray, str::AbstractString) = rand!(GLOBAL_RNG, A, str) +rand(rng::AbstractRNG, str::AbstractString, dims::Dims) = rand!(rng, Array{eltype(str)}(dims), str) +rand(rng::AbstractRNG, str::AbstractString, d1::Integer, dims::Integer...) = rand(rng, str, convert(Dims, tuple(d1, dims...))) +rand(str::AbstractString, dims::Dims) = rand(GLOBAL_RNG, str, dims) +rand(str::AbstractString, d1::Integer, dims::Integer...) = rand(GLOBAL_RNG, str, d1, dims...) diff --git a/base/random/misc.jl b/base/random/misc.jl new file mode 100644 index 0000000000000..7d2971d449983 --- /dev/null +++ b/base/random/misc.jl @@ -0,0 +1,455 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +## random BitArrays (AbstractRNG) + +function rand!(rng::AbstractRNG, B::BitArray) + isempty(B) && return B + Bc = B.chunks + rand!(rng, Bc) + Bc[end] &= Base._msk_end(B) + return B +end + +""" + bitrand([rng=GLOBAL_RNG], [dims...]) + +Generate a `BitArray` of random boolean values. + +# Examples +```jldoctest +julia> rng = MersenneTwister(1234); + +julia> bitrand(rng, 10) +10-element BitArray{1}: + true + true + true + false + true + false + false + true + false + true +``` +""" +bitrand(r::AbstractRNG, dims::Dims) = rand!(r, BitArray(dims)) +bitrand(r::AbstractRNG, dims::Integer...) = rand!(r, BitArray(convert(Dims, dims))) + +bitrand(dims::Dims) = rand!(BitArray(dims)) +bitrand(dims::Integer...) = rand!(BitArray(convert(Dims, dims))) + + +# return a random string (often useful for temporary filenames/dirnames) + +""" + randstring([rng=GLOBAL_RNG], [chars], [len=8]) + +Create a random string of length `len`, consisting of characters from +`chars`, which defaults to the set of upper- and lower-case letters +and the digits 0-9. The optional `rng` argument specifies a random +number generator, see [Random Numbers](@ref). + +# Examples +```jldoctest +julia> srand(0); randstring() +"c03rgKi1" + +julia> randstring(MersenneTwister(0), 'a':'z', 6) +"wijzek" + +julia> randstring("ACGT") +"TATCGGTC" +``` + +!!! note + `chars` can be any collection of characters, of type `Char` or + `UInt8` (more efficient), provided [`rand`](@ref) can randomly + pick characters from it. +""" +function randstring end + +let b = UInt8['0':'9';'A':'Z';'a':'z'] + global randstring + randstring(r::AbstractRNG, chars=b, n::Integer=8) = String(rand(r, chars, n)) + randstring(r::AbstractRNG, n::Integer) = randstring(r, b, n) + randstring(chars=b, n::Integer=8) = randstring(GLOBAL_RNG, chars, n) + randstring(n::Integer) = randstring(GLOBAL_RNG, b, n) +end + + +# Fill S (resized as needed) with a random subsequence of A, where +# each element of A is included in S with independent probability p. +# (Note that this is different from the problem of finding a random +# size-m subset of A where m is fixed!) +function randsubseq!(r::AbstractRNG, S::AbstractArray, A::AbstractArray, p::Real) + 0 <= p <= 1 || throw(ArgumentError("probability $p not in [0,1]")) + n = length(A) + p == 1 && return copy!(resize!(S, n), A) + empty!(S) + p == 0 && return S + nexpected = p * length(A) + sizehint!(S, round(Int,nexpected + 5*sqrt(nexpected))) + if p > 0.15 # empirical threshold for trivial O(n) algorithm to be better + for i = 1:n + rand(r) <= p && push!(S, A[i]) + end + else + # Skip through A, in order, from each element i to the next element i+s + # included in S. The probability that the next included element is + # s==k (k > 0) is (1-p)^(k-1) * p, and hence the probability (CDF) that + # s is in {1,...,k} is 1-(1-p)^k = F(k). Thus, we can draw the skip s + # from this probability distribution via the discrete inverse-transform + # method: s = ceil(F^{-1}(u)) where u = rand(), which is simply + # s = ceil(log(rand()) / log1p(-p)). + # -log(rand()) is an exponential variate, so can use randexp(). + L = -1 / log1p(-p) # L > 0 + i = 0 + while true + s = randexp(r) * L + s >= n - i && return S # compare before ceil to avoid overflow + push!(S, A[i += ceil(Int,s)]) + end + # [This algorithm is similar in spirit to, but much simpler than, + # the one by Vitter for a related problem in "Faster methods for + # random sampling," Comm. ACM Magazine 7, 703-718 (1984).] + end + return S +end + +""" + randsubseq!(S, A, p) + +Like [`randsubseq`](@ref), but the results are stored in `S` +(which is resized as needed). +""" +randsubseq!(S::AbstractArray, A::AbstractArray, p::Real) = randsubseq!(GLOBAL_RNG, S, A, p) + +randsubseq(r::AbstractRNG, A::AbstractArray{T}, p::Real) where {T} = randsubseq!(r, T[], A, p) + +""" + randsubseq(A, p) -> Vector + +Return a vector consisting of a random subsequence of the given array `A`, where each +element of `A` is included (in order) with independent probability `p`. (Complexity is +linear in `p*length(A)`, so this function is efficient even if `p` is small and `A` is +large.) Technically, this process is known as "Bernoulli sampling" of `A`. +""" +randsubseq(A::AbstractArray, p::Real) = randsubseq(GLOBAL_RNG, A, p) + +"Return a random `Int` (masked with `mask`) in ``[0, n)``, when `n <= 2^52`." +@inline function rand_lt(r::AbstractRNG, n::Int, mask::Int=nextpow2(n)-1) + # this duplicates the functionality of RangeGenerator objects, + # to optimize this special case + while true + x = (rand_ui52_raw(r) % Int) & mask + x < n && return x + end +end + +""" + shuffle!([rng=GLOBAL_RNG,] v::AbstractArray) + +In-place version of [`shuffle`](@ref): randomly permute `v` in-place, +optionally supplying the random-number generator `rng`. + +# Examples +```jldoctest +julia> rng = MersenneTwister(1234); + +julia> shuffle!(rng, collect(1:16)) +16-element Array{Int64,1}: + 2 + 15 + 5 + 14 + 1 + 9 + 10 + 6 + 11 + 3 + 16 + 7 + 4 + 12 + 8 + 13 +``` +""" +function shuffle!(r::AbstractRNG, a::AbstractArray) + n = length(a) + @assert n <= Int64(2)^52 + mask = nextpow2(n) - 1 + for i = n:-1:2 + (mask >> 1) == i && (mask >>= 1) + j = 1 + rand_lt(r, i, mask) + a[i], a[j] = a[j], a[i] + end + return a +end + +shuffle!(a::AbstractArray) = shuffle!(GLOBAL_RNG, a) + +""" + shuffle([rng=GLOBAL_RNG,] v::AbstractArray) + +Return a randomly permuted copy of `v`. The optional `rng` argument specifies a random +number generator (see [Random Numbers](@ref)). +To permute `v` in-place, see [`shuffle!`](@ref). To obtain randomly permuted +indices, see [`randperm`](@ref). + +# Examples +```jldoctest +julia> rng = MersenneTwister(1234); + +julia> shuffle(rng, collect(1:10)) +10-element Array{Int64,1}: + 6 + 1 + 10 + 2 + 3 + 9 + 5 + 7 + 4 + 8 +``` +""" +shuffle(r::AbstractRNG, a::AbstractArray) = shuffle!(r, copymutable(a)) +shuffle(a::AbstractArray) = shuffle(GLOBAL_RNG, a) + +""" + randperm([rng=GLOBAL_RNG,] n::Integer) + +Construct a random permutation of length `n`. The optional `rng` argument specifies a random +number generator (see [Random Numbers](@ref)). +To randomly permute an arbitrary vector, see [`shuffle`](@ref) +or [`shuffle!`](@ref). + +# Examples +```jldoctest +julia> randperm(MersenneTwister(1234), 4) +4-element Array{Int64,1}: + 2 + 1 + 4 + 3 +``` +""" +randperm(r::AbstractRNG, n::Integer) = randperm!(r, Vector{Int}(n)) +randperm(n::Integer) = randperm(GLOBAL_RNG, n) + +""" + randperm!([rng=GLOBAL_RNG,] A::Array{<:Integer}) + +Construct in `A` a random permutation of length `length(A)`. The +optional `rng` argument specifies a random number generator (see +[Random Numbers](@ref)). To randomly permute an arbitrary vector, see +[`shuffle`](@ref) or [`shuffle!`](@ref). + +# Examples +```jldoctest +julia> randperm!(MersenneTwister(1234), Vector{Int}(4)) +4-element Array{Int64,1}: + 2 + 1 + 4 + 3 +``` +""" +function randperm!(r::AbstractRNG, a::Array{<:Integer}) + n = length(a) + @assert n <= Int64(2)^52 + n == 0 && return a + a[1] = 1 + mask = 3 + @inbounds for i = 2:n + j = 1 + rand_lt(r, i, mask) + if i != j # a[i] is uninitialized (and could be #undef) + a[i] = a[j] + end + a[j] = i + i == 1+mask && (mask = 2mask + 1) + end + return a +end + +randperm!(a::Array{<:Integer}) = randperm!(GLOBAL_RNG, a) + + +""" + randcycle([rng=GLOBAL_RNG,] n::Integer) + +Construct a random cyclic permutation of length `n`. The optional `rng` +argument specifies a random number generator, see [Random Numbers](@ref). + +# Examples +```jldoctest +julia> randcycle(MersenneTwister(1234), 6) +6-element Array{Int64,1}: + 3 + 5 + 4 + 6 + 1 + 2 +``` +""" +randcycle(r::AbstractRNG, n::Integer) = randcycle!(r, Vector{Int}(n)) +randcycle(n::Integer) = randcycle(GLOBAL_RNG, n) + +""" + randcycle!([rng=GLOBAL_RNG,] A::Array{<:Integer}) + +Construct in `A` a random cyclic permutation of length `length(A)`. +The optional `rng` argument specifies a random number generator, see +[Random Numbers](@ref). + +# Examples +```jldoctest +julia> randcycle!(MersenneTwister(1234), Vector{Int}(6)) +6-element Array{Int64,1}: + 3 + 5 + 4 + 6 + 1 + 2 +``` +""" +function randcycle!(r::AbstractRNG, a::Array{<:Integer}) + n = length(a) + n == 0 && return a + @assert n <= Int64(2)^52 + a[1] = 1 + mask = 3 + @inbounds for i = 2:n + j = 1 + rand_lt(r, i-1, mask) + a[i] = a[j] + a[j] = i + i == 1+mask && (mask = 2mask + 1) + end + return a +end + +randcycle!(a::Array{<:Integer}) = randcycle!(GLOBAL_RNG, a) + + +## random UUID generation + +struct UUID + value::UInt128 + + UUID(u::UInt128) = new(u) +end + +""" + uuid1([rng::AbstractRNG=GLOBAL_RNG]) -> UUID + +Generates a version 1 (time-based) universally unique identifier (UUID), as specified +by RFC 4122. Note that the Node ID is randomly generated (does not identify the host) +according to section 4.5 of the RFC. + +# Examples +```jldoctest +julia> rng = MersenneTwister(1234); + +julia> Base.Random.uuid1(rng) +2cc938da-5937-11e7-196e-0f4ef71aa64b +``` +""" +function uuid1(rng::AbstractRNG=GLOBAL_RNG) + u = rand(rng, UInt128) + + # mask off clock sequence and node + u &= 0x00000000000000003fffffffffffffff + + # set the unicast/multicast bit and version + u |= 0x00000000000010000000010000000000 + + # 0x01b21dd213814000 is the number of 100 nanosecond intervals + # between the UUID epoch and Unix epoch + timestamp = round(UInt64, time() * 1e7) + 0x01b21dd213814000 + ts_low = timestamp & typemax(UInt32) + ts_mid = (timestamp >> 32) & typemax(UInt16) + ts_hi = (timestamp >> 48) & 0x0fff + + u |= UInt128(ts_low) << 96 + u |= UInt128(ts_mid) << 80 + u |= UInt128(ts_hi) << 64 + + UUID(u) +end + +""" + uuid4([rng::AbstractRNG=GLOBAL_RNG]) -> UUID + +Generates a version 4 (random or pseudo-random) universally unique identifier (UUID), +as specified by RFC 4122. + +# Examples +```jldoctest +julia> rng = MersenneTwister(1234); + +julia> Base.Random.uuid4(rng) +82015f10-44cc-4827-996e-0f4ef71aa64b +``` +""" +function uuid4(rng::AbstractRNG=GLOBAL_RNG) + u = rand(rng, UInt128) + u &= 0xffffffffffff0fff3fffffffffffffff + u |= 0x00000000000040008000000000000000 + UUID(u) +end + +""" + uuid_version(u::UUID) -> Integer + +Inspects the given UUID and returns its version (see RFC 4122). + +# Examples +```jldoctest +julia> rng = MersenneTwister(1234); + +julia> Base.Random.uuid_version(Base.Random.uuid4(rng)) +4 +``` +""" +function uuid_version(u::UUID) + Int((u.value >> 76) & 0xf) +end + +Base.convert(::Type{UInt128}, u::UUID) = u.value + +function Base.convert(::Type{UUID}, s::AbstractString) + s = lowercase(s) + + if !ismatch(r"^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$", s) + throw(ArgumentError("Malformed UUID string")) + end + + u = UInt128(0) + for i in [1:8; 10:13; 15:18; 20:23; 25:36] + u <<= 4 + d = s[i]-'0' + u |= 0xf & (d-39*(d>9)) + end + return UUID(u) +end + +function Base.repr(u::UUID) + u = u.value + a = Vector{UInt8}(36) + for i = [36:-1:25; 23:-1:20; 18:-1:15; 13:-1:10; 8:-1:1] + d = u & 0xf + a[i] = '0'+d+39*(d>9) + u >>= 4 + end + a[[24,19,14,9]] = '-' + + return String(a) +end + +Base.show(io::IO, u::UUID) = write(io, Base.repr(u)) diff --git a/base/random.jl b/base/random/normal.jl similarity index 52% rename from base/random.jl rename to base/random/normal.jl index 04a7fcb39fe25..0de252d4cb9cf 100644 --- a/base/random.jl +++ b/base/random/normal.jl @@ -1,868 +1,5 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -module Random - -using Base.dSFMT -using Base.GMP: Limb, MPZ -import Base: copymutable, copy, copy!, ==, hash - -export srand, - rand, rand!, - randn, randn!, - randexp, randexp!, - bitrand, - randstring, - randsubseq, randsubseq!, - shuffle, shuffle!, - randperm, randperm!, - randcycle, randcycle!, - AbstractRNG, MersenneTwister, RandomDevice, - GLOBAL_RNG, randjump - - -abstract type AbstractRNG end - -abstract type FloatInterval end -mutable struct CloseOpen <: FloatInterval end -mutable struct Close1Open2 <: FloatInterval end - - -## RandomDevice - -const BoolBitIntegerType = Union{Type{Bool},Base.BitIntegerType} -const BoolBitIntegerArray = Union{Array{Bool},Base.BitIntegerArray} - -if Sys.iswindows() - struct RandomDevice <: AbstractRNG - buffer::Vector{UInt128} - - RandomDevice() = new(Vector{UInt128}(1)) - end - - function rand(rd::RandomDevice, T::BoolBitIntegerType) - win32_SystemFunction036!(rd.buffer) - @inbounds return rd.buffer[1] % T - end - - rand!(rd::RandomDevice, A::BoolBitIntegerArray) = (win32_SystemFunction036!(A); A) -else # !windows - struct RandomDevice <: AbstractRNG - file::IOStream - unlimited::Bool - - RandomDevice(unlimited::Bool=true) = new(open(unlimited ? "/dev/urandom" : "/dev/random"), unlimited) - end - - rand(rd::RandomDevice, T::BoolBitIntegerType) = read( rd.file, T) - rand!(rd::RandomDevice, A::BoolBitIntegerArray) = read!(rd.file, A) -end # os-test - -""" - RandomDevice() - -Create a `RandomDevice` RNG object. Two such objects will always generate different streams of random numbers. -""" -RandomDevice - - -rand(rng::RandomDevice, ::Type{Close1Open2}) = - reinterpret(Float64, 0x3ff0000000000000 | rand(rng, UInt64) & 0x000fffffffffffff) - -rand(rng::RandomDevice, ::Type{CloseOpen}) = rand(rng, Close1Open2) - 1.0 - - -## MersenneTwister - -const MTCacheLength = dsfmt_get_min_array_size() - -mutable struct MersenneTwister <: AbstractRNG - seed::Vector{UInt32} - state::DSFMT_state - vals::Vector{Float64} - idx::Int - - function MersenneTwister(seed, state, vals, idx) - if !(length(vals) == MTCacheLength && 0 <= idx <= MTCacheLength) - throw(DomainError(idx, "`length(vals)` and `idx` must be consistent with $MTCacheLength")) - end - new(seed, state, vals, idx) - end -end - -MersenneTwister(seed::Vector{UInt32}, state::DSFMT_state) = - MersenneTwister(seed, state, zeros(Float64, MTCacheLength), MTCacheLength) - -""" - MersenneTwister(seed) - -Create a `MersenneTwister` RNG object. Different RNG objects can have their own seeds, which -may be useful for generating different streams of random numbers. - -# Examples -```jldoctest -julia> rng = MersenneTwister(1234); - -julia> x1 = rand(rng, 2) -2-element Array{Float64,1}: - 0.590845 - 0.766797 - -julia> rng = MersenneTwister(1234); - -julia> x2 = rand(rng, 2) -2-element Array{Float64,1}: - 0.590845 - 0.766797 - -julia> x1 == x2 -true -``` -""" -MersenneTwister(seed) = srand(MersenneTwister(Vector{UInt32}(), DSFMT_state()), seed) - -function copy!(dst::MersenneTwister, src::MersenneTwister) - copy!(resize!(dst.seed, length(src.seed)), src.seed) - copy!(dst.state, src.state) - copy!(dst.vals, src.vals) - dst.idx = src.idx - dst -end - -copy(src::MersenneTwister) = - MersenneTwister(copy(src.seed), copy(src.state), copy(src.vals), src.idx) - -==(r1::MersenneTwister, r2::MersenneTwister) = - r1.seed == r2.seed && r1.state == r2.state && isequal(r1.vals, r2.vals) && r1.idx == r2.idx - -hash(r::MersenneTwister, h::UInt) = foldr(hash, h, (r.seed, r.state, r.vals, r.idx)) - -## Low level API for MersenneTwister - -@inline mt_avail(r::MersenneTwister) = MTCacheLength - r.idx -@inline mt_empty(r::MersenneTwister) = r.idx == MTCacheLength -@inline mt_setfull!(r::MersenneTwister) = r.idx = 0 -@inline mt_setempty!(r::MersenneTwister) = r.idx = MTCacheLength -@inline mt_pop!(r::MersenneTwister) = @inbounds return r.vals[r.idx+=1] - -function gen_rand(r::MersenneTwister) - dsfmt_fill_array_close1_open2!(r.state, pointer(r.vals), length(r.vals)) - mt_setfull!(r) -end - -@inline reserve_1(r::MersenneTwister) = (mt_empty(r) && gen_rand(r); nothing) -# `reserve` allows one to call `rand_inbounds` n times -# precondition: n <= MTCacheLength -@inline reserve(r::MersenneTwister, n::Int) = (mt_avail(r) < n && gen_rand(r); nothing) - -# precondition: !mt_empty(r) -@inline rand_inbounds(r::MersenneTwister, ::Type{Close1Open2}) = mt_pop!(r) -@inline rand_inbounds(r::MersenneTwister, ::Type{CloseOpen}) = rand_inbounds(r, Close1Open2) - 1.0 -@inline rand_inbounds(r::MersenneTwister) = rand_inbounds(r, CloseOpen) - -# produce Float64 values -@inline rand(r::MersenneTwister, ::Type{I}) where {I<:FloatInterval} = (reserve_1(r); rand_inbounds(r, I)) - -@inline rand_ui52_raw_inbounds(r::MersenneTwister) = reinterpret(UInt64, rand_inbounds(r, Close1Open2)) -@inline rand_ui52_raw(r::MersenneTwister) = (reserve_1(r); rand_ui52_raw_inbounds(r)) - -@inline function rand_ui2x52_raw(r::MersenneTwister) - reserve(r, 2) - rand_ui52_raw_inbounds(r) % UInt128 << 64 | rand_ui52_raw_inbounds(r) -end - -@inline function rand_ui104_raw(r::MersenneTwister) - reserve(r, 2) - rand_ui52_raw_inbounds(r) % UInt128 << 52 ⊻ rand_ui52_raw_inbounds(r) -end - -function srand(r::MersenneTwister, seed::Vector{UInt32}) - copy!(resize!(r.seed, length(seed)), seed) - dsfmt_init_by_array(r.state, r.seed) - mt_setempty!(r) - return r -end - -# MersenneTwister jump - -""" - randjump(r::MersenneTwister, jumps::Integer, [jumppoly::AbstractString=dSFMT.JPOLY1e21]) -> Vector{MersenneTwister} - -Create an array of the size `jumps` of initialized `MersenneTwister` RNG objects. The -first RNG object given as a parameter and following `MersenneTwister` RNGs in the array are -initialized such that a state of the RNG object in the array would be moved forward (without -generating numbers) from a previous RNG object array element on a particular number of steps -encoded by the jump polynomial `jumppoly`. - -Default jump polynomial moves forward `MersenneTwister` RNG state by `10^20` steps. -""" -function randjump(mt::MersenneTwister, jumps::Integer, jumppoly::AbstractString) - mts = MersenneTwister[] - push!(mts, mt) - for i in 1:jumps-1 - cmt = mts[end] - push!(mts, MersenneTwister(copy(cmt.seed), dSFMT.dsfmt_jump(cmt.state, jumppoly))) - end - return mts -end -randjump(r::MersenneTwister, jumps::Integer) = randjump(r, jumps, dSFMT.JPOLY1e21) - -## initialization - -function __init__() - try - srand() - catch ex - Base.showerror_nostdio(ex, - "WARNING: Error during initialization of module Random") - end -end - - -## make_seed() -# make_seed methods produce values of type Array{UInt32}, suitable for MersenneTwister seeding - -function make_seed() - try - return rand(RandomDevice(), UInt32, 4) - catch - println(STDERR, "Entropy pool not available to seed RNG; using ad-hoc entropy sources.") - seed = reinterpret(UInt64, time()) - seed = hash(seed, UInt64(getpid())) - try - seed = hash(seed, parse(UInt64, read(pipeline(`ifconfig`, `sha1sum`), String)[1:40], 16)) - end - return make_seed(seed) - end -end - -function make_seed(n::Integer) - n < 0 && throw(DomainError(n, "`n` must be non-negative.")) - seed = UInt32[] - while true - push!(seed, n & 0xffffffff) - n >>= 32 - if n == 0 - return seed - end - end -end - -## srand() - -""" - srand([rng=GLOBAL_RNG], seed) -> rng - srand([rng=GLOBAL_RNG]) -> rng - -Reseed the random number generator. If a `seed` is provided, the RNG will give a -reproducible sequence of numbers, otherwise Julia will get entropy from the system. For -`MersenneTwister`, the `seed` may be a non-negative integer or a vector of [`UInt32`](@ref) -integers. `RandomDevice` does not support seeding. - -# Examples -```jldoctest -julia> srand(1234); - -julia> x1 = rand(2) -2-element Array{Float64,1}: - 0.590845 - 0.766797 - -julia> srand(1234); - -julia> x2 = rand(2) -2-element Array{Float64,1}: - 0.590845 - 0.766797 - -julia> x1 == x2 -true -``` -""" -srand(r::MersenneTwister) = srand(r, make_seed()) -srand(r::MersenneTwister, n::Integer) = srand(r, make_seed(n)) - -function srand() - srand(GLOBAL_RNG) -end - -function srand(seed::Union{Integer,Vector{UInt32}}) - srand(GLOBAL_RNG, seed) -end - -## Global RNG - -const GLOBAL_RNG = MersenneTwister(0) -globalRNG() = GLOBAL_RNG - -# rand: a non-specified RNG defaults to GLOBAL_RNG - -""" - rand([rng=GLOBAL_RNG], [S], [dims...]) - -Pick a random element or array of random elements from the set of values specified by `S`; `S` can be - -* an indexable collection (for example `1:n` or `['x','y','z']`), -* an `Associative` or `AbstractSet` object, -* a string (considered as a collection of characters), or -* a type: the set of values to pick from is then equivalent to `typemin(S):typemax(S)` for - integers (this is not applicable to [`BigInt`](@ref)), and to ``[0, 1)`` for floating - point numbers; - -`S` defaults to [`Float64`](@ref). - -# Examples -```julia-repl -julia> rand(Int, 2) -2-element Array{Int64,1}: - 1339893410598768192 - 1575814717733606317 - -julia> rand(MersenneTwister(0), Dict(1=>2, 3=>4)) -1=>2 -``` - -!!! note - The complexity of `rand(rng, s::Union{Associative,AbstractSet})` - is linear in the length of `s`, unless an optimized method with - constant complexity is available, which is the case for `Dict`, - `Set` and `IntSet`. For more than a few calls, use `rand(rng, - collect(s))` instead, or either `rand(rng, Dict(s))` or `rand(rng, - Set(s))` as appropriate. -""" -@inline rand() = rand(GLOBAL_RNG, CloseOpen) -@inline rand(T::Type) = rand(GLOBAL_RNG, T) -rand(dims::Dims) = rand(GLOBAL_RNG, dims) -rand(dims::Integer...) = rand(convert(Dims, dims)) -rand(T::Type, dims::Dims) = rand(GLOBAL_RNG, T, dims) -rand(T::Type, d1::Integer, dims::Integer...) = rand(T, tuple(Int(d1), convert(Dims, dims)...)) -rand!(A::AbstractArray) = rand!(GLOBAL_RNG, A) - -rand(r::AbstractArray) = rand(GLOBAL_RNG, r) - -""" - rand!([rng=GLOBAL_RNG], A, [S=eltype(A)]) - -Populate the array `A` with random values. If `S` is specified -(`S` can be a type or a collection, cf. [`rand`](@ref) for details), -the values are picked randomly from `S`. -This is equivalent to `copy!(A, rand(rng, S, size(A)))` -but without allocating a new array. - -# Examples -```jldoctest -julia> rng = MersenneTwister(1234); - -julia> rand!(rng, zeros(5)) -5-element Array{Float64,1}: - 0.590845 - 0.766797 - 0.566237 - 0.460085 - 0.794026 -``` -""" -rand!(A::AbstractArray, r::AbstractArray) = rand!(GLOBAL_RNG, A, r) - -rand(r::AbstractArray, dims::Dims) = rand(GLOBAL_RNG, r, dims) -rand(r::AbstractArray, dims::Integer...) = rand(GLOBAL_RNG, r, convert(Dims, dims)) - -## random floating point values - -@inline rand(r::AbstractRNG) = rand(r, CloseOpen) - -# MersenneTwister & RandomDevice -@inline rand(r::Union{RandomDevice,MersenneTwister}, ::Type{Float64}) = rand(r, CloseOpen) - -rand_ui10_raw(r::MersenneTwister) = rand_ui52_raw(r) -rand_ui23_raw(r::MersenneTwister) = rand_ui52_raw(r) -rand_ui10_raw(r::AbstractRNG) = rand(r, UInt16) -rand_ui23_raw(r::AbstractRNG) = rand(r, UInt32) - -rand(r::Union{RandomDevice,MersenneTwister}, ::Type{Float16}) = - Float16(reinterpret(Float32, (rand_ui10_raw(r) % UInt32 << 13) & 0x007fe000 | 0x3f800000) - 1) - -rand(r::Union{RandomDevice,MersenneTwister}, ::Type{Float32}) = - reinterpret(Float32, rand_ui23_raw(r) % UInt32 & 0x007fffff | 0x3f800000) - 1 - - -## random integers - -@inline rand_ui52_raw(r::AbstractRNG) = reinterpret(UInt64, rand(r, Close1Open2)) -@inline rand_ui52(r::AbstractRNG) = rand_ui52_raw(r) & 0x000fffffffffffff - -# MersenneTwister - -@inline rand(r::MersenneTwister, ::Type{T}) where {T<:Union{Bool,Int8,UInt8,Int16,UInt16,Int32,UInt32}} = - rand_ui52_raw(r) % T - -function rand(r::MersenneTwister, ::Type{UInt64}) - reserve(r, 2) - rand_ui52_raw_inbounds(r) << 32 ⊻ rand_ui52_raw_inbounds(r) -end - -function rand(r::MersenneTwister, ::Type{UInt128}) - reserve(r, 3) - xor(rand_ui52_raw_inbounds(r) % UInt128 << 96, - rand_ui52_raw_inbounds(r) % UInt128 << 48, - rand_ui52_raw_inbounds(r)) -end - -rand(r::MersenneTwister, ::Type{Int64}) = reinterpret(Int64, rand(r, UInt64)) -rand(r::MersenneTwister, ::Type{Int128}) = reinterpret(Int128, rand(r, UInt128)) - -## random Complex values - -rand(r::AbstractRNG, ::Type{Complex{T}}) where {T<:Real} = complex(rand(r, T), rand(r, T)) - -# random Char values -# returns a random valid Unicode scalar value (i.e. 0 - 0xd7ff, 0xe000 - # 0x10ffff) -function rand(r::AbstractRNG, ::Type{Char}) - c = rand(r, 0x00000000:0x0010f7ff) - (c < 0xd800) ? Char(c) : Char(c+0x800) -end - -# random values from Dict, Set, IntSet (for efficiency) -function rand(r::AbstractRNG, t::Dict) - isempty(t) && throw(ArgumentError("collection must be non-empty")) - rg = RangeGenerator(1:length(t.slots)) - while true - i = rand(r, rg) - Base.isslotfilled(t, i) && @inbounds return (t.keys[i] => t.vals[i]) - end -end - -rand(r::AbstractRNG, s::Set) = rand(r, s.dict).first - -function rand(r::AbstractRNG, s::IntSet) - isempty(s) && throw(ArgumentError("collection must be non-empty")) - # s can be empty while s.bits is not, so we cannot rely on the - # length check in RangeGenerator below - rg = RangeGenerator(1:length(s.bits)) - while true - n = rand(r, rg) - @inbounds b = s.bits[n] - b && return n - end -end - -function nth(iter, n::Integer)::eltype(iter) - for (i, x) in enumerate(iter) - i == n && return x - end -end -nth(iter::AbstractArray, n::Integer) = iter[n] - -rand(r::AbstractRNG, s::Union{Associative,AbstractSet}) = nth(s, rand(r, 1:length(s))) - -rand(s::Union{Associative,AbstractSet}) = rand(GLOBAL_RNG, s) - -## Arrays of random numbers - -rand(r::AbstractRNG, dims::Dims) = rand(r, Float64, dims) -rand(r::AbstractRNG, dims::Integer...) = rand(r, convert(Dims, dims)) - -rand(r::AbstractRNG, T::Type, dims::Dims) = rand!(r, Array{T}(dims)) -rand(r::AbstractRNG, T::Type, d1::Integer, dims::Integer...) = rand(r, T, tuple(Int(d1), convert(Dims, dims)...)) -# note: the above method would trigger an ambiguity warning if d1 was not separated out: -# rand(r, ()) would match both this method and rand(r, dims::Dims) -# moreover, a call like rand(r, NotImplementedType()) would be an infinite loop - -function rand!(r::AbstractRNG, A::AbstractArray{T}, ::Type{X}=T) where {T,X} - for i in eachindex(A) - @inbounds A[i] = rand(r, X) - end - A -end - -rand!(A::AbstractArray, ::Type{X}) where {X} = rand!(GLOBAL_RNG, A, X) - -function rand!(r::AbstractRNG, A::AbstractArray, s::Union{Dict,Set,IntSet}) - for i in eachindex(A) - @inbounds A[i] = rand(r, s) - end - A -end - -# avoid linear complexity for repeated calls with generic containers -rand!(r::AbstractRNG, A::AbstractArray, s::Union{Associative,AbstractSet}) = rand!(r, A, collect(s)) - -rand!(A::AbstractArray, s::Union{Associative,AbstractSet}) = rand!(GLOBAL_RNG, A, s) - -rand(r::AbstractRNG, s::Associative{K,V}, dims::Dims) where {K,V} = rand!(r, Array{Pair{K,V}}(dims), s) -rand(r::AbstractRNG, s::AbstractSet{T}, dims::Dims) where {T} = rand!(r, Array{T}(dims), s) -rand(r::AbstractRNG, s::Union{Associative,AbstractSet}, dims::Integer...) = rand(r, s, convert(Dims, dims)) -rand(s::Union{Associative,AbstractSet}, dims::Integer...) = rand(GLOBAL_RNG, s, convert(Dims, dims)) -rand(s::Union{Associative,AbstractSet}, dims::Dims) = rand(GLOBAL_RNG, s, dims) - -# MersenneTwister - -function rand_AbstractArray_Float64!(r::MersenneTwister, A::AbstractArray{Float64}, - n=length(A), ::Type{I}=CloseOpen) where I<:FloatInterval - # what follows is equivalent to this simple loop but more efficient: - # for i=1:n - # @inbounds A[i] = rand(r, I) - # end - m = 0 - while m < n - s = mt_avail(r) - if s == 0 - gen_rand(r) - s = mt_avail(r) - end - m2 = min(n, m+s) - for i=m+1:m2 - @inbounds A[i] = rand_inbounds(r, I) - end - m = m2 - end - A -end - -rand!(r::MersenneTwister, A::AbstractArray{Float64}) = rand_AbstractArray_Float64!(r, A) - -fill_array!(s::DSFMT_state, A::Ptr{Float64}, n::Int, ::Type{CloseOpen}) = dsfmt_fill_array_close_open!(s, A, n) -fill_array!(s::DSFMT_state, A::Ptr{Float64}, n::Int, ::Type{Close1Open2}) = dsfmt_fill_array_close1_open2!(s, A, n) - -function rand!(r::MersenneTwister, A::Array{Float64}, n::Int=length(A), ::Type{I}=CloseOpen) where I<:FloatInterval - # depending on the alignment of A, the data written by fill_array! may have - # to be left-shifted by up to 15 bytes (cf. unsafe_copy! below) for - # reproducibility purposes; - # so, even for well aligned arrays, fill_array! is used to generate only - # the n-2 first values (or n-3 if n is odd), and the remaining values are - # generated by the scalar version of rand - if n > length(A) - throw(BoundsError(A,n)) - end - n2 = (n-2) ÷ 2 * 2 - if n2 < dsfmt_get_min_array_size() - rand_AbstractArray_Float64!(r, A, n, I) - else - pA = pointer(A) - align = Csize_t(pA) % 16 - if align > 0 - pA2 = pA + 16 - align - fill_array!(r.state, pA2, n2, I) # generate the data in-place, but shifted - unsafe_copy!(pA, pA2, n2) # move the data to the beginning of the array - else - fill_array!(r.state, pA, n2, I) - end - for i=n2+1:n - @inbounds A[i] = rand(r, I) - end - end - A -end - -@inline mask128(u::UInt128, ::Type{Float16}) = (u & 0x03ff03ff03ff03ff03ff03ff03ff03ff) | 0x3c003c003c003c003c003c003c003c00 -@inline mask128(u::UInt128, ::Type{Float32}) = (u & 0x007fffff007fffff007fffff007fffff) | 0x3f8000003f8000003f8000003f800000 - -function rand!(r::MersenneTwister, A::Union{Array{Float16},Array{Float32}}, ::Type{Close1Open2}) - T = eltype(A) - n = length(A) - n128 = n * sizeof(T) ÷ 16 - rand!(r, unsafe_wrap(Array, convert(Ptr{Float64}, pointer(A)), 2*n128), 2*n128, Close1Open2) - A128 = unsafe_wrap(Array, convert(Ptr{UInt128}, pointer(A)), n128) - @inbounds for i in 1:n128 - u = A128[i] - u ⊻= u << 26 - # at this point, the 64 low bits of u, "k" being the k-th bit of A128[i] and "+" the bit xor, are: - # [..., 58+32,..., 53+27, 52+26, ..., 33+7, 32+6, ..., 27+1, 26, ..., 1] - # the bits needing to be random are - # [1:10, 17:26, 33:42, 49:58] (for Float16) - # [1:23, 33:55] (for Float32) - # this is obviously satisfied on the 32 low bits side, and on the high side, the entropy comes - # from bits 33:52 of A128[i] and then from bits 27:32 (which are discarded on the low side) - # this is similar for the 64 high bits of u - A128[i] = mask128(u, T) - end - for i in 16*n128÷sizeof(T)+1:n - @inbounds A[i] = rand(r, T) + oneunit(T) - end - A -end - -function rand!(r::MersenneTwister, A::Union{Array{Float16},Array{Float32}}, ::Type{CloseOpen}) - rand!(r, A, Close1Open2) - I32 = one(Float32) - for i in eachindex(A) - @inbounds A[i] = Float32(A[i])-I32 # faster than "A[i] -= one(T)" for T==Float16 - end - A -end - -rand!(r::MersenneTwister, A::Union{Array{Float16},Array{Float32}}) = rand!(r, A, CloseOpen) - -function rand!(r::MersenneTwister, A::Array{UInt128}, n::Int=length(A)) - if n > length(A) - throw(BoundsError(A,n)) - end - Af = unsafe_wrap(Array, convert(Ptr{Float64}, pointer(A)), 2n) - i = n - while true - rand!(r, Af, 2i, Close1Open2) - n < 5 && break - i = 0 - @inbounds while n-i >= 5 - u = A[i+=1] - A[n] ⊻= u << 48 - A[n-=1] ⊻= u << 36 - A[n-=1] ⊻= u << 24 - A[n-=1] ⊻= u << 12 - n-=1 - end - end - if n > 0 - u = rand_ui2x52_raw(r) - for i = 1:n - @inbounds A[i] ⊻= u << (12*i) - end - end - A -end - -# A::Array{UInt128} will match the specialized method above -function rand!(r::MersenneTwister, A::Base.BitIntegerArray) - n = length(A) - T = eltype(A) - n128 = n * sizeof(T) ÷ 16 - rand!(r, unsafe_wrap(Array, convert(Ptr{UInt128}, pointer(A)), n128)) - for i = 16*n128÷sizeof(T)+1:n - @inbounds A[i] = rand(r, T) - end - A -end - -## Generate random integer within a range - -# remainder function according to Knuth, where rem_knuth(a, 0) = a -rem_knuth(a::UInt, b::UInt) = a % (b + (b == 0)) + a * (b == 0) -rem_knuth(a::T, b::T) where {T<:Unsigned} = b != 0 ? a % b : a - -# maximum multiple of k <= 2^bits(T) decremented by one, -# that is 0xFFFF...FFFF if k = typemax(T) - typemin(T) with intentional underflow -# see http://stackoverflow.com/questions/29182036/integer-arithmetic-add-1-to-uint-max-and-divide-by-n-without-overflow -maxmultiple(k::T) where {T<:Unsigned} = (div(typemax(T) - k + oneunit(k), k + (k == 0))*k + k - oneunit(k))::T - -# maximum multiple of k within 1:2^32 or 1:2^64 decremented by one, depending on size -maxmultiplemix(k::UInt64) = if k >> 32 != 0; maxmultiple(k); else (div(0x0000000100000000, k + (k == 0))*k - oneunit(k))::UInt64; end - -abstract type RangeGenerator end - -struct RangeGeneratorInt{T<:Integer,U<:Unsigned} <: RangeGenerator - a::T # first element of the range - k::U # range length or zero for full range - u::U # rejection threshold -end -# generators with 32, 128 bits entropy -RangeGeneratorInt(a::T, k::U) where {T,U<:Union{UInt32,UInt128}} = RangeGeneratorInt{T,U}(a, k, maxmultiple(k)) -# mixed 32/64 bits entropy generator -RangeGeneratorInt(a::T, k::UInt64) where {T} = RangeGeneratorInt{T,UInt64}(a, k, maxmultiplemix(k)) -# generator for ranges -function RangeGenerator(r::UnitRange{T}) where T<:Unsigned - if isempty(r) - throw(ArgumentError("range must be non-empty")) - end - RangeGeneratorInt(first(r), last(r) - first(r) + oneunit(T)) -end - -# specialized versions -for (T, U) in [(UInt8, UInt32), (UInt16, UInt32), - (Int8, UInt32), (Int16, UInt32), (Int32, UInt32), (Int64, UInt64), (Int128, UInt128), - (Bool, UInt32)] - - @eval RangeGenerator(r::UnitRange{$T}) = begin - if isempty(r) - throw(ArgumentError("range must be non-empty")) - end - RangeGeneratorInt(first(r), convert($U, unsigned(last(r) - first(r)) + one($U))) # overflow ok - end -end - -struct RangeGeneratorBigInt <: RangeGenerator - a::BigInt # first - m::BigInt # range length - 1 - nlimbs::Int # number of limbs in generated BigInt's (z ∈ [0, m]) - nlimbsmax::Int # max number of limbs for z+a - mask::Limb # applied to the highest limb -end - - -function RangeGenerator(r::UnitRange{BigInt}) - m = last(r) - first(r) - m < 0 && throw(ArgumentError("range must be non-empty")) - nd = ndigits(m, 2) - nlimbs, highbits = divrem(nd, 8*sizeof(Limb)) - highbits > 0 && (nlimbs += 1) - mask = highbits == 0 ? ~zero(Limb) : one(Limb)<> 32 == 0 - x = rand(rng, UInt32) - while x > g.u - x = rand(rng, UInt32) - end - else - x = rand(rng, UInt64) - while x > g.u - x = rand(rng, UInt64) - end - end - return reinterpret(T, reinterpret(UInt64, g.a) + rem_knuth(x, g.k)) -end - -function rand(rng::AbstractRNG, g::RangeGeneratorInt{T,U}) where U<:Unsigned where T<:Integer - x = rand(rng, U) - while x > g.u - x = rand(rng, U) - end - (unsigned(g.a) + rem_knuth(x, g.k)) % T -end - -function rand(rng::AbstractRNG, g::RangeGeneratorBigInt) - x = MPZ.realloc2(g.nlimbsmax*8*sizeof(Limb)) - limbs = unsafe_wrap(Array, x.d, g.nlimbs) - while true - rand!(rng, limbs) - @inbounds limbs[end] &= g.mask - MPZ.mpn_cmp(x, g.m, g.nlimbs) <= 0 && break - end - # adjust x.size (normally done by mpz_limbs_finish, in GMP version >= 6) - x.size = g.nlimbs - while x.size > 0 - @inbounds limbs[x.size] != 0 && break - x.size -= 1 - end - MPZ.add!(x, g.a) -end - -rand(rng::AbstractRNG, r::UnitRange{<:Union{Signed,Unsigned,BigInt,Bool}}) = rand(rng, RangeGenerator(r)) - -## special case for MersenneTwister -@inline function rand_lteq(r::AbstractRNG, randfun, u::U, mask::U) where U<:Integer - while true - x = randfun(r) & mask - x <= u && return x - end -end - -function rand(rng::MersenneTwister, r::UnitRange{T}) where T<:Union{Base.BitInteger64,Bool} - isempty(r) && throw(ArgumentError("range must be non-empty")) - m = last(r) % UInt64 - first(r) % UInt64 - bw = (64 - leading_zeros(m)) % UInt # bit-width - mask = (1 % UInt64 << bw) - (1 % UInt64) - x = bw <= 52 ? rand_lteq(rng, rand_ui52_raw, m, mask) : - rand_lteq(rng, rng->rand(rng, UInt64), m, mask) - (x + first(r) % UInt64) % T -end - -function rand(rng::MersenneTwister, r::UnitRange{T}) where T<:Union{Int128,UInt128} - isempty(r) && throw(ArgumentError("range must be non-empty")) - m = (last(r)-first(r)) % UInt128 - bw = (128 - leading_zeros(m)) % UInt # bit-width - mask = (1 % UInt128 << bw) - (1 % UInt128) - x = bw <= 52 ? rand_lteq(rng, rand_ui52_raw, m % UInt64, mask % UInt64) % UInt128 : - bw <= 104 ? rand_lteq(rng, rand_ui104_raw, m, mask) : - rand_lteq(rng, rng->rand(rng, UInt128), m, mask) - x % T + first(r) -end - -# Randomly draw a sample from an AbstractArray r -# (e.g. r is a range 0:2:8 or a vector [2, 3, 5, 7]) -rand(rng::AbstractRNG, r::AbstractArray) = @inbounds return r[rand(rng, 1:length(r))] - -function rand!(rng::AbstractRNG, A::AbstractArray, g::RangeGenerator) - for i in eachindex(A) - @inbounds A[i] = rand(rng, g) - end - return A -end - -rand!(rng::AbstractRNG, A::AbstractArray, r::UnitRange{<:Union{Signed,Unsigned,BigInt,Bool,Char}}) = rand!(rng, A, RangeGenerator(r)) - -function rand!(rng::AbstractRNG, A::AbstractArray, r::AbstractArray) - g = RangeGenerator(1:(length(r))) - for i in eachindex(A) - @inbounds A[i] = r[rand(rng, g)] - end - return A -end - -rand(rng::AbstractRNG, r::AbstractArray{T}, dims::Dims) where {T} = rand!(rng, Array{T}(dims), r) -rand(rng::AbstractRNG, r::AbstractArray, dims::Integer...) = rand(rng, r, convert(Dims, dims)) - -# rand from a string - -isvalid_unsafe(s::String, i) = !Base.is_valid_continuation(unsafe_load(pointer(s), i)) -isvalid_unsafe(s::AbstractString, i) = isvalid(s, i) -_endof(s::String) = sizeof(s) -_endof(s::AbstractString) = endof(s) - -function rand(rng::AbstractRNG, s::AbstractString)::Char - g = RangeGenerator(1:_endof(s)) - while true - pos = rand(rng, g) - isvalid_unsafe(s, pos) && return s[pos] - end -end - -rand(s::AbstractString) = rand(GLOBAL_RNG, s) - -## rand from a string for arrays -# we use collect(str), which is most of the time more efficient than specialized methods -# (except maybe for very small arrays) -rand!(rng::AbstractRNG, A::AbstractArray, str::AbstractString) = rand!(rng, A, collect(str)) -rand!(A::AbstractArray, str::AbstractString) = rand!(GLOBAL_RNG, A, str) -rand(rng::AbstractRNG, str::AbstractString, dims::Dims) = rand!(rng, Array{eltype(str)}(dims), str) -rand(rng::AbstractRNG, str::AbstractString, d1::Integer, dims::Integer...) = rand(rng, str, convert(Dims, tuple(d1, dims...))) -rand(str::AbstractString, dims::Dims) = rand(GLOBAL_RNG, str, dims) -rand(str::AbstractString, d1::Integer, dims::Integer...) = rand(GLOBAL_RNG, str, d1, dims...) - -## random BitArrays (AbstractRNG) - -function rand!(rng::AbstractRNG, B::BitArray) - isempty(B) && return B - Bc = B.chunks - rand!(rng, Bc) - Bc[end] &= Base._msk_end(B) - return B -end - -""" - bitrand([rng=GLOBAL_RNG], [dims...]) - -Generate a `BitArray` of random boolean values. - -# Examples -```jldoctest -julia> rng = MersenneTwister(1234); - -julia> bitrand(rng, 10) -10-element BitArray{1}: - true - true - true - false - true - false - false - true - false - true -``` -""" -bitrand(r::AbstractRNG, dims::Dims) = rand!(r, BitArray(dims)) -bitrand(r::AbstractRNG, dims::Integer...) = rand!(r, BitArray(convert(Dims, dims))) - -bitrand(dims::Dims) = rand!(BitArray(dims)) -bitrand(dims::Integer...) = rand!(BitArray(convert(Dims, dims))) - ## randn() - Normally distributed random numbers using Ziggurat algorithm # The Ziggurat Method for generating random variables - Marsaglia and Tsang @@ -1524,417 +661,3 @@ end Base.@irrational SQRT_HALF 0.7071067811865475244008 sqrt(big(0.5)) randn(rng::AbstractRNG, ::Type{Complex{T}}) where {T<:AbstractFloat} = Complex{T}(SQRT_HALF * randn(rng, T), SQRT_HALF * randn(rng, T)) - -## random UUID generation - -struct UUID - value::UInt128 - - UUID(u::UInt128) = new(u) -end - -""" - uuid1([rng::AbstractRNG=GLOBAL_RNG]) -> UUID - -Generates a version 1 (time-based) universally unique identifier (UUID), as specified -by RFC 4122. Note that the Node ID is randomly generated (does not identify the host) -according to section 4.5 of the RFC. - -# Examples -```jldoctest -julia> rng = MersenneTwister(1234); - -julia> Base.Random.uuid1(rng) -2cc938da-5937-11e7-196e-0f4ef71aa64b -``` -""" -function uuid1(rng::AbstractRNG=GLOBAL_RNG) - u = rand(rng, UInt128) - - # mask off clock sequence and node - u &= 0x00000000000000003fffffffffffffff - - # set the unicast/multicast bit and version - u |= 0x00000000000010000000010000000000 - - # 0x01b21dd213814000 is the number of 100 nanosecond intervals - # between the UUID epoch and Unix epoch - timestamp = round(UInt64, time() * 1e7) + 0x01b21dd213814000 - ts_low = timestamp & typemax(UInt32) - ts_mid = (timestamp >> 32) & typemax(UInt16) - ts_hi = (timestamp >> 48) & 0x0fff - - u |= UInt128(ts_low) << 96 - u |= UInt128(ts_mid) << 80 - u |= UInt128(ts_hi) << 64 - - UUID(u) -end - -""" - uuid4([rng::AbstractRNG=GLOBAL_RNG]) -> UUID - -Generates a version 4 (random or pseudo-random) universally unique identifier (UUID), -as specified by RFC 4122. - -# Examples -```jldoctest -julia> rng = MersenneTwister(1234); - -julia> Base.Random.uuid4(rng) -82015f10-44cc-4827-996e-0f4ef71aa64b -``` -""" -function uuid4(rng::AbstractRNG=GLOBAL_RNG) - u = rand(rng, UInt128) - u &= 0xffffffffffff0fff3fffffffffffffff - u |= 0x00000000000040008000000000000000 - UUID(u) -end - -""" - uuid_version(u::UUID) -> Integer - -Inspects the given UUID and returns its version (see RFC 4122). - -# Examples -```jldoctest -julia> rng = MersenneTwister(1234); - -julia> Base.Random.uuid_version(Base.Random.uuid4(rng)) -4 -``` -""" -function uuid_version(u::UUID) - Int((u.value >> 76) & 0xf) -end - -Base.convert(::Type{UInt128}, u::UUID) = u.value - -function Base.convert(::Type{UUID}, s::AbstractString) - s = lowercase(s) - - if !ismatch(r"^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$", s) - throw(ArgumentError("Malformed UUID string")) - end - - u = UInt128(0) - for i in [1:8; 10:13; 15:18; 20:23; 25:36] - u <<= 4 - d = s[i]-'0' - u |= 0xf & (d-39*(d>9)) - end - return UUID(u) -end - -function Base.repr(u::UUID) - u = u.value - a = Vector{UInt8}(36) - for i = [36:-1:25; 23:-1:20; 18:-1:15; 13:-1:10; 8:-1:1] - d = u & 0xf - a[i] = '0'+d+39*(d>9) - u >>= 4 - end - a[[24,19,14,9]] = '-' - - return String(a) -end - -Base.show(io::IO, u::UUID) = write(io, Base.repr(u)) - -# return a random string (often useful for temporary filenames/dirnames) - -""" - randstring([rng=GLOBAL_RNG], [chars], [len=8]) - -Create a random string of length `len`, consisting of characters from -`chars`, which defaults to the set of upper- and lower-case letters -and the digits 0-9. The optional `rng` argument specifies a random -number generator, see [Random Numbers](@ref). - -# Examples -```jldoctest -julia> srand(0); randstring() -"c03rgKi1" - -julia> randstring(MersenneTwister(0), 'a':'z', 6) -"wijzek" - -julia> randstring("ACGT") -"TATCGGTC" -``` - -!!! note - `chars` can be any collection of characters, of type `Char` or - `UInt8` (more efficient), provided [`rand`](@ref) can randomly - pick characters from it. -""" -function randstring end - -let b = UInt8['0':'9';'A':'Z';'a':'z'] - global randstring - randstring(r::AbstractRNG, chars=b, n::Integer=8) = String(rand(r, chars, n)) - randstring(r::AbstractRNG, n::Integer) = randstring(r, b, n) - randstring(chars=b, n::Integer=8) = randstring(GLOBAL_RNG, chars, n) - randstring(n::Integer) = randstring(GLOBAL_RNG, b, n) -end - -# Fill S (resized as needed) with a random subsequence of A, where -# each element of A is included in S with independent probability p. -# (Note that this is different from the problem of finding a random -# size-m subset of A where m is fixed!) - -""" - randsubseq!(S, A, p) - -Like [`randsubseq`](@ref), but the results are stored in `S` -(which is resized as needed). -""" -function randsubseq!(r::AbstractRNG, S::AbstractArray, A::AbstractArray, p::Real) - 0 <= p <= 1 || throw(ArgumentError("probability $p not in [0,1]")) - n = length(A) - p == 1 && return copy!(resize!(S, n), A) - empty!(S) - p == 0 && return S - nexpected = p * length(A) - sizehint!(S, round(Int,nexpected + 5*sqrt(nexpected))) - if p > 0.15 # empirical threshold for trivial O(n) algorithm to be better - for i = 1:n - rand(r) <= p && push!(S, A[i]) - end - else - # Skip through A, in order, from each element i to the next element i+s - # included in S. The probability that the next included element is - # s==k (k > 0) is (1-p)^(k-1) * p, and hence the probability (CDF) that - # s is in {1,...,k} is 1-(1-p)^k = F(k). Thus, we can draw the skip s - # from this probability distribution via the discrete inverse-transform - # method: s = ceil(F^{-1}(u)) where u = rand(), which is simply - # s = ceil(log(rand()) / log1p(-p)). - # -log(rand()) is an exponential variate, so can use randexp(). - L = -1 / log1p(-p) # L > 0 - i = 0 - while true - s = randexp(r) * L - s >= n - i && return S # compare before ceil to avoid overflow - push!(S, A[i += ceil(Int,s)]) - end - # [This algorithm is similar in spirit to, but much simpler than, - # the one by Vitter for a related problem in "Faster methods for - # random sampling," Comm. ACM Magazine 7, 703-718 (1984).] - end - return S -end -randsubseq!(S::AbstractArray, A::AbstractArray, p::Real) = randsubseq!(GLOBAL_RNG, S, A, p) - -randsubseq(r::AbstractRNG, A::AbstractArray{T}, p::Real) where {T} = randsubseq!(r, T[], A, p) - -""" - randsubseq(A, p) -> Vector - -Return a vector consisting of a random subsequence of the given array `A`, where each -element of `A` is included (in order) with independent probability `p`. (Complexity is -linear in `p*length(A)`, so this function is efficient even if `p` is small and `A` is -large.) Technically, this process is known as "Bernoulli sampling" of `A`. -""" -randsubseq(A::AbstractArray, p::Real) = randsubseq(GLOBAL_RNG, A, p) - -"Return a random `Int` (masked with `mask`) in ``[0, n)``, when `n <= 2^52`." -@inline function rand_lt(r::AbstractRNG, n::Int, mask::Int=nextpow2(n)-1) - # this duplicates the functionality of RangeGenerator objects, - # to optimize this special case - while true - x = (rand_ui52_raw(r) % Int) & mask - x < n && return x - end -end - -""" - shuffle!([rng=GLOBAL_RNG,] v::AbstractArray) - -In-place version of [`shuffle`](@ref): randomly permute `v` in-place, -optionally supplying the random-number generator `rng`. - -# Examples -```jldoctest -julia> rng = MersenneTwister(1234); - -julia> shuffle!(rng, collect(1:16)) -16-element Array{Int64,1}: - 2 - 15 - 5 - 14 - 1 - 9 - 10 - 6 - 11 - 3 - 16 - 7 - 4 - 12 - 8 - 13 -``` -""" -function shuffle!(r::AbstractRNG, a::AbstractArray) - n = length(a) - @assert n <= Int64(2)^52 - mask = nextpow2(n) - 1 - for i = n:-1:2 - (mask >> 1) == i && (mask >>= 1) - j = 1 + rand_lt(r, i, mask) - a[i], a[j] = a[j], a[i] - end - return a -end - -shuffle!(a::AbstractArray) = shuffle!(GLOBAL_RNG, a) - -""" - shuffle([rng=GLOBAL_RNG,] v::AbstractArray) - -Return a randomly permuted copy of `v`. The optional `rng` argument specifies a random -number generator (see [Random Numbers](@ref)). -To permute `v` in-place, see [`shuffle!`](@ref). To obtain randomly permuted -indices, see [`randperm`](@ref). - -# Examples -```jldoctest -julia> rng = MersenneTwister(1234); - -julia> shuffle(rng, collect(1:10)) -10-element Array{Int64,1}: - 6 - 1 - 10 - 2 - 3 - 9 - 5 - 7 - 4 - 8 -``` -""" -shuffle(r::AbstractRNG, a::AbstractArray) = shuffle!(r, copymutable(a)) -shuffle(a::AbstractArray) = shuffle(GLOBAL_RNG, a) - -""" - randperm([rng=GLOBAL_RNG,] n::Integer) - -Construct a random permutation of length `n`. The optional `rng` argument specifies a random -number generator (see [Random Numbers](@ref)). -To randomly permute an arbitrary vector, see [`shuffle`](@ref) -or [`shuffle!`](@ref). - -# Examples -```jldoctest -julia> randperm(MersenneTwister(1234), 4) -4-element Array{Int64,1}: - 2 - 1 - 4 - 3 -``` -""" -randperm(r::AbstractRNG, n::Integer) = randperm!(r, Vector{Int}(n)) -randperm(n::Integer) = randperm(GLOBAL_RNG, n) - -""" - randperm!([rng=GLOBAL_RNG,] A::Array{<:Integer}) - -Construct in `A` a random permutation of length `length(A)`. The -optional `rng` argument specifies a random number generator (see -[Random Numbers](@ref)). To randomly permute an arbitrary vector, see -[`shuffle`](@ref) or [`shuffle!`](@ref). - -# Examples -```jldoctest -julia> randperm!(MersenneTwister(1234), Vector{Int}(4)) -4-element Array{Int64,1}: - 2 - 1 - 4 - 3 -``` -""" -function randperm!(r::AbstractRNG, a::Array{<:Integer}) - n = length(a) - @assert n <= Int64(2)^52 - n == 0 && return a - a[1] = 1 - mask = 3 - @inbounds for i = 2:n - j = 1 + rand_lt(r, i, mask) - if i != j # a[i] is uninitialized (and could be #undef) - a[i] = a[j] - end - a[j] = i - i == 1+mask && (mask = 2mask + 1) - end - return a -end - -randperm!(a::Array{<:Integer}) = randperm!(GLOBAL_RNG, a) - - -""" - randcycle([rng=GLOBAL_RNG,] n::Integer) - -Construct a random cyclic permutation of length `n`. The optional `rng` -argument specifies a random number generator, see [Random Numbers](@ref). - -# Examples -```jldoctest -julia> randcycle(MersenneTwister(1234), 6) -6-element Array{Int64,1}: - 3 - 5 - 4 - 6 - 1 - 2 -``` -""" -randcycle(r::AbstractRNG, n::Integer) = randcycle!(r, Vector{Int}(n)) -randcycle(n::Integer) = randcycle(GLOBAL_RNG, n) - -""" - randcycle!([rng=GLOBAL_RNG,] A::Array{<:Integer}) - -Construct in `A` a random cyclic permutation of length `length(A)`. -The optional `rng` argument specifies a random number generator, see -[Random Numbers](@ref). - -# Examples -```jldoctest -julia> randcycle!(MersenneTwister(1234), Vector{Int}(6)) -6-element Array{Int64,1}: - 3 - 5 - 4 - 6 - 1 - 2 -``` -""" -function randcycle!(r::AbstractRNG, a::Array{<:Integer}) - n = length(a) - n == 0 && return a - @assert n <= Int64(2)^52 - a[1] = 1 - mask = 3 - @inbounds for i = 2:n - j = 1 + rand_lt(r, i-1, mask) - a[i] = a[j] - a[j] = i - i == 1+mask && (mask = 2mask + 1) - end - return a -end - -randcycle!(a::Array{<:Integer}) = randcycle!(GLOBAL_RNG, a) - -end # module diff --git a/base/random/random.jl b/base/random/random.jl new file mode 100644 index 0000000000000..673c59d93dbf1 --- /dev/null +++ b/base/random/random.jl @@ -0,0 +1,45 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +module Random + +using Base.dSFMT +using Base.GMP: Limb, MPZ +import Base: copymutable, copy, copy!, ==, hash + +export srand, + rand, rand!, + randn, randn!, + randexp, randexp!, + bitrand, + randstring, + randsubseq, randsubseq!, + shuffle, shuffle!, + randperm, randperm!, + randcycle, randcycle!, + AbstractRNG, MersenneTwister, RandomDevice, + GLOBAL_RNG, randjump + + +abstract type AbstractRNG end + +abstract type FloatInterval end +mutable struct CloseOpen <: FloatInterval end +mutable struct Close1Open2 <: FloatInterval end + +## initialization + +function __init__() + try + srand() + catch ex + Base.showerror_nostdio(ex, + "WARNING: Error during initialization of module Random") + end +end + +include("RNGs.jl") +include("generation.jl") +include("normal.jl") +include("misc.jl") + +end # module diff --git a/base/sysimg.jl b/base/sysimg.jl index df4a606d00bc2..de68ed222632b 100644 --- a/base/sysimg.jl +++ b/base/sysimg.jl @@ -325,8 +325,8 @@ include("hashing2.jl") include("irrationals.jl") # random number generation -include("dSFMT.jl") -include("random.jl") +include("random/dSFMT.jl") +include("random/random.jl") importall .Random # (s)printf macros From 72b5b8012d58f4b1662abf541553f8d2e7b1a35d Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Tue, 11 Jul 2017 09:47:12 +0200 Subject: [PATCH 057/324] reorganize some methods and improve structure (headers) --- base/random/RNGs.jl | 189 +++++++++++++-------------- base/random/dSFMT.jl | 19 +-- base/random/generation.jl | 266 +++++++++++++++++--------------------- base/random/misc.jl | 17 ++- base/random/normal.jl | 27 +++- base/random/random.jl | 95 +++++++++++++- 6 files changed, 346 insertions(+), 267 deletions(-) diff --git a/base/random/RNGs.jl b/base/random/RNGs.jl index edf9363704e6c..b7def1dd42886 100644 --- a/base/random/RNGs.jl +++ b/base/random/RNGs.jl @@ -13,11 +13,15 @@ if Sys.iswindows() end function rand(rd::RandomDevice, T::BoolBitIntegerType) - win32_SystemFunction036!(rd.buffer) + rand!(rd, rd.buffer) @inbounds return rd.buffer[1] % T end - rand!(rd::RandomDevice, A::BoolBitIntegerArray) = (win32_SystemFunction036!(A); A) + function rand!(rd::RandomDevice, A::BoolBitIntegerArray) + ccall((:SystemFunction036, :Advapi32), stdcall, UInt8, (Ptr{Void}, UInt32), + A, sizeof(A)) + A + end else # !windows struct RandomDevice <: AbstractRNG file::IOStream @@ -37,12 +41,21 @@ Create a `RandomDevice` RNG object. Two such objects will always generate differ """ RandomDevice +### generation of floats rand(rng::RandomDevice, ::Type{Close1Open2}) = reinterpret(Float64, 0x3ff0000000000000 | rand(rng, UInt64) & 0x000fffffffffffff) rand(rng::RandomDevice, ::Type{CloseOpen}) = rand(rng, Close1Open2) - 1.0 +@inline rand(r::RandomDevice, ::Type{Float64}) = rand(r, CloseOpen) + +rand(r::RandomDevice, ::Type{Float16}) = + Float16(reinterpret(Float32, (rand_ui10_raw(r) % UInt32 << 13) & 0x007fe000 | 0x3f800000) - 1) + +rand(r::RandomDevice, ::Type{Float32}) = + reinterpret(Float32, rand_ui23_raw(r) % UInt32 & 0x007fffff | 0x3f800000) - 1 + ## MersenneTwister @@ -109,7 +122,8 @@ copy(src::MersenneTwister) = hash(r::MersenneTwister, h::UInt) = foldr(hash, h, (r.seed, r.state, r.vals, r.idx)) -## Low level API for MersenneTwister + +### low level API @inline mt_avail(r::MersenneTwister) = MTCacheLength - r.idx @inline mt_empty(r::MersenneTwister) = r.idx == MTCacheLength @@ -127,62 +141,12 @@ end # precondition: n <= MTCacheLength @inline reserve(r::MersenneTwister, n::Int) = (mt_avail(r) < n && gen_rand(r); nothing) -# precondition: !mt_empty(r) -@inline rand_inbounds(r::MersenneTwister, ::Type{Close1Open2}) = mt_pop!(r) -@inline rand_inbounds(r::MersenneTwister, ::Type{CloseOpen}) = rand_inbounds(r, Close1Open2) - 1.0 -@inline rand_inbounds(r::MersenneTwister) = rand_inbounds(r, CloseOpen) -# produce Float64 values -@inline rand(r::MersenneTwister, ::Type{I}) where {I<:FloatInterval} = (reserve_1(r); rand_inbounds(r, I)) +### seeding -@inline rand_ui52_raw_inbounds(r::MersenneTwister) = reinterpret(UInt64, rand_inbounds(r, Close1Open2)) -@inline rand_ui52_raw(r::MersenneTwister) = (reserve_1(r); rand_ui52_raw_inbounds(r)) +#### make_seed() -@inline function rand_ui2x52_raw(r::MersenneTwister) - reserve(r, 2) - rand_ui52_raw_inbounds(r) % UInt128 << 64 | rand_ui52_raw_inbounds(r) -end - -@inline function rand_ui104_raw(r::MersenneTwister) - reserve(r, 2) - rand_ui52_raw_inbounds(r) % UInt128 << 52 ⊻ rand_ui52_raw_inbounds(r) -end - -function srand(r::MersenneTwister, seed::Vector{UInt32}) - copy!(resize!(r.seed, length(seed)), seed) - dsfmt_init_by_array(r.state, r.seed) - mt_setempty!(r) - return r -end - -# MersenneTwister jump - -""" - randjump(r::MersenneTwister, jumps::Integer, [jumppoly::AbstractString=dSFMT.JPOLY1e21]) -> Vector{MersenneTwister} - -Create an array of the size `jumps` of initialized `MersenneTwister` RNG objects. The -first RNG object given as a parameter and following `MersenneTwister` RNGs in the array are -initialized such that a state of the RNG object in the array would be moved forward (without -generating numbers) from a previous RNG object array element on a particular number of steps -encoded by the jump polynomial `jumppoly`. - -Default jump polynomial moves forward `MersenneTwister` RNG state by `10^20` steps. -""" -function randjump(mt::MersenneTwister, jumps::Integer, jumppoly::AbstractString) - mts = MersenneTwister[] - push!(mts, mt) - for i in 1:jumps-1 - cmt = mts[end] - push!(mts, MersenneTwister(copy(cmt.seed), dSFMT.dsfmt_jump(cmt.state, jumppoly))) - end - return mts -end -randjump(r::MersenneTwister, jumps::Integer) = randjump(r, jumps, dSFMT.JPOLY1e21) - - -## make_seed() # make_seed methods produce values of type Array{UInt32}, suitable for MersenneTwister seeding - function make_seed() try return rand(RandomDevice(), UInt32, 4) @@ -209,67 +173,64 @@ function make_seed(n::Integer) end end -## srand() - -""" - srand([rng=GLOBAL_RNG], seed) -> rng - srand([rng=GLOBAL_RNG]) -> rng - -Reseed the random number generator. If a `seed` is provided, the RNG will give a -reproducible sequence of numbers, otherwise Julia will get entropy from the system. For -`MersenneTwister`, the `seed` may be a non-negative integer or a vector of [`UInt32`](@ref) -integers. `RandomDevice` does not support seeding. +#### srand() -# Examples -```jldoctest -julia> srand(1234); +function srand(r::MersenneTwister, seed::Vector{UInt32}) + copy!(resize!(r.seed, length(seed)), seed) + dsfmt_init_by_array(r.state, r.seed) + mt_setempty!(r) + return r +end -julia> x1 = rand(2) -2-element Array{Float64,1}: - 0.590845 - 0.766797 +srand(r::MersenneTwister=GLOBAL_RNG) = srand(r, make_seed()) +srand(r::MersenneTwister, n::Integer) = srand(r, make_seed(n)) +srand(seed::Union{Integer,Vector{UInt32}}) = srand(GLOBAL_RNG, seed) -julia> srand(1234); -julia> x2 = rand(2) -2-element Array{Float64,1}: - 0.590845 - 0.766797 +### Global RNG (must be defined after srand) -julia> x1 == x2 -true -``` -""" -srand(r::MersenneTwister) = srand(r, make_seed()) -srand(r::MersenneTwister, n::Integer) = srand(r, make_seed(n)) +const GLOBAL_RNG = MersenneTwister(0) -function srand() - srand(GLOBAL_RNG) -end -function srand(seed::Union{Integer,Vector{UInt32}}) - srand(GLOBAL_RNG, seed) -end +### generation -## Global RNG +#### helper functions -const GLOBAL_RNG = MersenneTwister(0) -globalRNG() = GLOBAL_RNG +# precondition: !mt_empty(r) +@inline rand_inbounds(r::MersenneTwister, ::Type{Close1Open2}) = mt_pop!(r) +@inline rand_inbounds(r::MersenneTwister, ::Type{CloseOpen}) = rand_inbounds(r, Close1Open2) - 1.0 +@inline rand_inbounds(r::MersenneTwister) = rand_inbounds(r, CloseOpen) +@inline rand_ui52_raw_inbounds(r::MersenneTwister) = reinterpret(UInt64, rand_inbounds(r, Close1Open2)) +@inline rand_ui52_raw(r::MersenneTwister) = (reserve_1(r); rand_ui52_raw_inbounds(r)) -## random generation for RandomDevice & MersenneTwister +@inline function rand_ui2x52_raw(r::MersenneTwister) + reserve(r, 2) + rand_ui52_raw_inbounds(r) % UInt128 << 64 | rand_ui52_raw_inbounds(r) +end -@inline rand(r::Union{RandomDevice,MersenneTwister}, ::Type{Float64}) = rand(r, CloseOpen) +@inline function rand_ui104_raw(r::MersenneTwister) + reserve(r, 2) + rand_ui52_raw_inbounds(r) % UInt128 << 52 ⊻ rand_ui52_raw_inbounds(r) +end rand_ui10_raw(r::MersenneTwister) = rand_ui52_raw(r) rand_ui23_raw(r::MersenneTwister) = rand_ui52_raw(r) -rand(r::Union{RandomDevice,MersenneTwister}, ::Type{Float16}) = +#### floats + +@inline rand(r::MersenneTwister, ::Type{I}) where {I<:FloatInterval} = (reserve_1(r); rand_inbounds(r, I)) + +@inline rand(r::MersenneTwister, ::Type{Float64}) = rand(r, CloseOpen) + +rand(r::MersenneTwister, ::Type{Float16}) = Float16(reinterpret(Float32, (rand_ui10_raw(r) % UInt32 << 13) & 0x007fe000 | 0x3f800000) - 1) -rand(r::Union{RandomDevice,MersenneTwister}, ::Type{Float32}) = +rand(r::MersenneTwister, ::Type{Float32}) = reinterpret(Float32, rand_ui23_raw(r) % UInt32 & 0x007fffff | 0x3f800000) - 1 +#### integers + @inline rand(r::MersenneTwister, ::Type{T}) where {T<:Union{Bool,Int8,UInt8,Int16,UInt16,Int32,UInt32}} = rand_ui52_raw(r) % T @@ -285,8 +246,10 @@ function rand(r::MersenneTwister, ::Type{UInt128}) rand_ui52_raw_inbounds(r)) end -rand(r::MersenneTwister, ::Type{Int64}) = reinterpret(Int64, rand(r, UInt64)) -rand(r::MersenneTwister, ::Type{Int128}) = reinterpret(Int128, rand(r, UInt128)) +rand(r::MersenneTwister, ::Type{Int64}) = reinterpret(Int64, rand(r, UInt64)) +rand(r::MersenneTwister, ::Type{Int128}) = reinterpret(Int128, rand(r, UInt128)) + +#### arrays of floats function rand_AbstractArray_Float64!(r::MersenneTwister, A::AbstractArray{Float64}, n=length(A), ::Type{I}=CloseOpen) where I<:FloatInterval @@ -384,6 +347,8 @@ end rand!(r::MersenneTwister, A::Union{Array{Float16},Array{Float32}}) = rand!(r, A, CloseOpen) +#### arrays of integers + function rand!(r::MersenneTwister, A::Array{UInt128}, n::Int=length(A)) if n > length(A) throw(BoundsError(A,n)) @@ -424,7 +389,7 @@ function rand!(r::MersenneTwister, A::Base.BitIntegerArray) A end -## special case for range generation with MersenneTwister +#### from a range @inline function rand_lteq(r::AbstractRNG, randfun, u::U, mask::U) where U<:Integer while true @@ -453,3 +418,29 @@ function rand(rng::MersenneTwister, r::UnitRange{T}) where T<:Union{Int128,UInt1 rand_lteq(rng, rng->rand(rng, UInt128), m, mask) x % T + first(r) end + + +### randjump + +""" + randjump(r::MersenneTwister, jumps::Integer, [jumppoly::AbstractString=dSFMT.JPOLY1e21]) -> Vector{MersenneTwister} + +Create an array of the size `jumps` of initialized `MersenneTwister` RNG objects. The +first RNG object given as a parameter and following `MersenneTwister` RNGs in the array are +initialized such that a state of the RNG object in the array would be moved forward (without +generating numbers) from a previous RNG object array element on a particular number of steps +encoded by the jump polynomial `jumppoly`. + +Default jump polynomial moves forward `MersenneTwister` RNG state by `10^20` steps. +""" +function randjump(mt::MersenneTwister, jumps::Integer, jumppoly::AbstractString) + mts = MersenneTwister[] + push!(mts, mt) + for i in 1:jumps-1 + cmt = mts[end] + push!(mts, MersenneTwister(copy(cmt.seed), dSFMT.dsfmt_jump(cmt.state, jumppoly))) + end + return mts +end + +randjump(r::MersenneTwister, jumps::Integer) = randjump(r, jumps, dSFMT.JPOLY1e21) diff --git a/base/random/dSFMT.jl b/base/random/dSFMT.jl index 11dbf0a079f4d..32f86da9da2a1 100644 --- a/base/random/dSFMT.jl +++ b/base/random/dSFMT.jl @@ -11,16 +11,20 @@ export DSFMT_state, dsfmt_get_min_array_size, dsfmt_get_idstring, "Mersenne Exponent" const MEXP = 19937 + "DSFMT internal state array size of N 128-bit integers." const N = floor(Int, ((MEXP - 128) / 104 + 1)) + """Julia DSFMT state representation size counted in 32-bit integers. Size: (DSFMT state array of Int128 + 1)*4 + Int32 index + Int32 padding """ const JN32 = (N+1)*4+1+1 + "Jump polynomial for 10^20 steps for dSFMT with exponent 19937" const JPOLY1e21 = "e172e20c5d2de26b567c0cace9e7c6cc4407bd5ffcd22ca59d37b73d54fdbd937cd3abc6f502e8c186dbd4f1a06b9e2b894f31be77424f94dddfd5a45888a84ca66eeeb242eefe6764ed859dafccae7a6a635b3a63fe9dfbbd5f2d3f2610d39388f53060e84edae75be4f4f2272c0f1f26d1231836ad040ab091550f8a3a5423fb3ab83e068fe2684057f15691c4dc757a3aee4bca8595bf1ad03500d9620a5dbe3b2d64380694895d2f379ca928238293ea267ce14236d5be816a61f018fe4f6bc3c9865f5d4d4186e320ab653d1f3c035ae83e2ad725648a67d3480331e763a1dcdfb5711b56796170b124f5febd723a664a2deefbfa9999d922a108b0e683582ae8d3baacb5bb56683405ea9e6e0d71ddb24b2229c72bb9d07061f2d1fa097ade823b607a2029d6e121ae09d93de01a154199e8e6a6e77c970bda72ba8079b2b3a15dd494a3188b1d94a25ae108a8a5bd0b050e6ce64a365a21420e07fdeebecae02eb68a4304b59283055d22c27d680ea35952834d828c9b9b9dd1a886b4f7fe82fe8f2a962e1e5390e563dc281c799aee2a441b7a813facb6ff5e94c059710dcfe7e6b1635e21ae0dc878dd5f7cc0e1101a74452495a67d23a2672c939f32c81d4a2611073990e92a084cc3a62fd42ee566f29d963a9cc5100ccd0a200f49ce0a74fa891efa1b974d342b7fedf9269e40d9b34e3c59c3d37201aecd5a04f4ae3d0c9a68c7ab78c662390e4cf36cb63ea3539c442efd0bf4aace4b8c8bde93c3d84b4d6290adfae1c5e3fcd457b6f3159e501f17b72ff6bc13d6bf61fbdafabefd16ac1dae0bca667e4e16a2b800732f1d0a9274c8a4c6cccd2db62fc275dc308c31c11cd6fda78de2f81f0e542b76b42b2cc09ed8f965d94c714c9918064f53af5379cfbbc31edf9cbce694f63a75f122048de6e57b094908f749661456813a908027f5d8397ab7962bf75ac779a3e1b7ae3fbc93397a67b486bb849befff1de6162ef2819715a88f41881e366ace692a900796a2806393898dd1750ac2b4ca3d34ca48942322fb6375f0c9a00c9701048ee8d7d7a17e11739177a7ad5027556e85835daf8594d84a97fe6621c0fce1495ae6ab8676cdc992d247acf5a4e5ec8c4755fde28117228d2c3ecf89edb91e93d949e2174924572265e36d176d082ed1be884e51d885ba3cda175c51edcee5042eaf519d292aa05aa4185b03858d710a9d0880b3d4e5111f858a52fe352cbe0a24f06a3d977ae2eb85e2a03a68131d0ab91dac4941067cf90ecd0fce156bcd40b8968cd4aa11e0b4353b14508d79d13ac00af4a4d452496b7f2393699889aa1e508427dbf0be3db91d955feb51e559af57640c6b3f9d5f95609852c28f9462a9869dd93acbdb1aafb2381ebb886a0b3fcec278f8bb0f62c23e157e49b89245b0881268ce594acbddd3605b9eaa77c9ff513e0dbad514914136d96fe2843fe2b4e886a0b718a9b8d1132132110618d0d3595da284cd2a4c9d09386199e4f4d7723983d3a374b51cf20dac5cabb4ff7e7197c2ebd9318463409baa583d6a6115c1b768282ff37b0fe152c97671e400d5ccba7d6875df0bf95c5d91257fedb124de393f31908d0e36251326aa29dd5be86291c80b4bf78f419ec151eeaeff643a58b48ab35ad2cd2c0b77b1965966ef3db6b6373cb2c4b590cef2f16f4d6f62f13a6cbf1a481565b5935edd4e76f7b6a8fd0d74bc336b40a803aec38125c006c877dfdcdb9ba2b7aecab5cafe6076e024c73e3567adf97f607a71d180402c22a20a8388f517484cc4198f97c2fe4f3407e0dc577e61f0f71354aa601cf4e3e42e1edd8722d50f5af3441f68caa568cc1c3a19956c1233f265bb47236afab24ee42b27b0042b90693d77c1923147360ae6503f6ba6abbc9dd52a7b4c36a3b6b55f6a80cfa7f101dd9f1bfc7d7eaf09a5d636b510228f245bfb37b4625025d2c911435cdf6f878113753e0804ab8ecab870ad733b9728d7636b17578b41239393e7de47cbce871137d2b61729dda67b2b84cd3363aad64c5dd5bd172f1f091305b1ff78982abe7dab1588036d097cf497e300e6c78a926048febd1b9462c07f5868928357b74297c87f503056b89f786d22a538b6702e290bca04639a0f1d0939b67f409e5e58e472a6a07fa543e2531c2567ec73c41f6769b6ba94c5aa0a030d006f5b6b1c5fb218b86a8f63a48bc867466f20f699859e87956f48a182d26ed451861dd21201ecc7239037ada67319bdf0849c387c73a110af798b4c5f9018bc97993e060ea2a2937fa2eb095d65ec07009fc407a350f1d6fb3c98a0a5f204be985b0cb6962f0eb7844a179c4598a92ea32d2d706c800034d2e960ded5b476d77073316b933fb3e6ba2f4f24a3b73a1e4d8ed1491d757ecf56fd72465dac0000736744d28d29073091587c8bccad302f7054e8a32bb8724974d9f3e449fc70b2a41f0008b548f717ac0a2c3a6580bfb50774933a578ad6acdcb89940bb406ea540893f097d8a88d1609ed605f25499de939083a0c8a7c6db462df5dfa06c298dd233e249433a54267d5cdc22e5524705b7d6b16b96bb2cb83e00cef62e21d91528a74cf95bfd1d391590c93f4058e9bb02656fd087a5b63d738d1c3b5cf533fd59c81cf9136bfcd3e955c19daf9906ef175791fde6a1d98155d7881e241c3522551cf9fcae42e1e46929ea39fd00943446823f9755085ccc8456a3090b73a3031a201d9c704a4ad4868dd9b6d06205560013973f60d637de2f18354bf4523d9d81dc2a7e78cd42c586364bbe0ed86fde0f081f801c1a4abb830839b7796d9a01f141bec8bd93144104c6dc59170162c0a5a639eb63a0a164970de50eb2e04f027394b26ed48d341f7851994df79d7cd663672a556f25e5e16a3adbe1003d631de938fabfed234df12b5ff3027f4a2da823834cb098e0f977a4eb9614579d5c7a1d400a1a933a657aef8ea1a66743d73b0cf37a7d64e9a63e4c7b09945f0db750b311b39783fb5ea216616751967d480a630d3da7c89d1c7beae20369137e96734a4cfedca56a7887f076fe4fe97534ad3e4f74d1a81750581a5ea214b440c7f30331ab86c257534c71175d1e731303a48b01c589fda4fb0d4368b4dd63d91204cb6fc389b2202aa94391907bfb72902a4031f5589ed5f391c2ce92aa998c200ba3c77d8bd747b9d0a29fa85cda3949a6d2bd0c3402e68f98fd451aa27b6c2dfd170e004577cbdb25e3a1b9852e9f66a370789c47bfce722446dade1b32ceae71ee0e1d96edf7ed08a93e3690056f46c3d8e63f88e53673ee71d72cfedbeba493ee91333120e09e9ce9f9c9a7a400f814ea618b1de48f9805e092f4e20f301fbb65caa83735a2a5c89befe4bce4116dca3688e1e14c6f09a945671dedbb5c0ba526842b6cae31d8b5ff978bae928a17a75c134630dd9de988f6ad3d89a071b33775a9660a40b48ec61ad3f93ac81cb1c65d8b0bab5c214786abd13cc10a8ea2e2a370e86e2fa1a372d83c9697b5e37b281e51507685f714fdaebe49ffc93a5582e1936eaee8e4140a4b72" + mutable struct DSFMT_state val::Vector{Int32} @@ -35,6 +39,9 @@ copy(src::DSFMT_state) = DSFMT_state(copy(src.val)) hash(s::DSFMT_state, h::UInt) = hash(s.val, h) + +## wrapper functions + function dsfmt_get_idstring() idstring = ccall((:dsfmt_get_idstring,:libdSFMT), Ptr{UInt8}, @@ -89,7 +96,9 @@ function dsfmt_fill_array_close_open!(s::DSFMT_state, A::Ptr{Float64}, n::Int) s.val, A, n) end -# dSFMT jump + +## dSFMT jump + function dsfmt_jump(s::DSFMT_state, jp::AbstractString) val = s.val nval = length(val) @@ -163,12 +172,4 @@ function dsfmt_jump_next_state!(mts::Vector{UInt64}) return mts end -## Windows entropy - -if Sys.iswindows() - function win32_SystemFunction036!(a::Array) - ccall((:SystemFunction036, :Advapi32), stdcall, UInt8, (Ptr{Void}, UInt32), a, sizeof(a)) - end -end - end # module diff --git a/base/random/generation.jl b/base/random/generation.jl index bda4bd669a8c4..c3f384a8221d2 100644 --- a/base/random/generation.jl +++ b/base/random/generation.jl @@ -1,145 +1,38 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -# rand: a non-specified RNG defaults to GLOBAL_RNG - -""" - rand([rng=GLOBAL_RNG], [S], [dims...]) - -Pick a random element or array of random elements from the set of values specified by `S`; `S` can be - -* an indexable collection (for example `1:n` or `['x','y','z']`), -* an `Associative` or `AbstractSet` object, -* a string (considered as a collection of characters), or -* a type: the set of values to pick from is then equivalent to `typemin(S):typemax(S)` for - integers (this is not applicable to [`BigInt`](@ref)), and to ``[0, 1)`` for floating - point numbers; - -`S` defaults to [`Float64`](@ref). - -# Examples -```julia-repl -julia> rand(Int, 2) -2-element Array{Int64,1}: - 1339893410598768192 - 1575814717733606317 - -julia> rand(MersenneTwister(0), Dict(1=>2, 3=>4)) -1=>2 -``` - -!!! note - The complexity of `rand(rng, s::Union{Associative,AbstractSet})` - is linear in the length of `s`, unless an optimized method with - constant complexity is available, which is the case for `Dict`, - `Set` and `IntSet`. For more than a few calls, use `rand(rng, - collect(s))` instead, or either `rand(rng, Dict(s))` or `rand(rng, - Set(s))` as appropriate. -""" -@inline rand() = rand(GLOBAL_RNG, CloseOpen) -@inline rand(T::Type) = rand(GLOBAL_RNG, T) -rand(dims::Dims) = rand(GLOBAL_RNG, dims) -rand(dims::Integer...) = rand(convert(Dims, dims)) -rand(T::Type, dims::Dims) = rand(GLOBAL_RNG, T, dims) -rand(T::Type, d1::Integer, dims::Integer...) = rand(T, tuple(Int(d1), convert(Dims, dims)...)) -rand!(A::AbstractArray) = rand!(GLOBAL_RNG, A) +# Uniform random generation -rand(r::AbstractArray) = rand(GLOBAL_RNG, r) - -""" - rand!([rng=GLOBAL_RNG], A, [S=eltype(A)]) - -Populate the array `A` with random values. If `S` is specified -(`S` can be a type or a collection, cf. [`rand`](@ref) for details), -the values are picked randomly from `S`. -This is equivalent to `copy!(A, rand(rng, S, size(A)))` -but without allocating a new array. - -# Examples -```jldoctest -julia> rng = MersenneTwister(1234); - -julia> rand!(rng, zeros(5)) -5-element Array{Float64,1}: - 0.590845 - 0.766797 - 0.566237 - 0.460085 - 0.794026 -``` -""" -rand!(A::AbstractArray, r::AbstractArray) = rand!(GLOBAL_RNG, A, r) +## from types: rand(::Type, [dims...]) -rand(r::AbstractArray, dims::Dims) = rand(GLOBAL_RNG, r, dims) -rand(r::AbstractArray, dims::Integer...) = rand(GLOBAL_RNG, r, convert(Dims, dims)) +### GLOBAL_RNG fallback for all types + +@inline rand(T::Type) = rand(GLOBAL_RNG, T) -## random floating point values +### random floats -@inline rand(r::AbstractRNG) = rand(r, CloseOpen) +@inline rand(r::AbstractRNG=GLOBAL_RNG) = rand(r, CloseOpen) -rand_ui10_raw(r::AbstractRNG) = rand(r, UInt16) -rand_ui23_raw(r::AbstractRNG) = rand(r, UInt32) +### random integers -## random integers +rand_ui10_raw(r::AbstractRNG) = rand(r, UInt16) +rand_ui23_raw(r::AbstractRNG) = rand(r, UInt32) @inline rand_ui52_raw(r::AbstractRNG) = reinterpret(UInt64, rand(r, Close1Open2)) @inline rand_ui52(r::AbstractRNG) = rand_ui52_raw(r) & 0x000fffffffffffff -## random Complex values +### random complex numbers rand(r::AbstractRNG, ::Type{Complex{T}}) where {T<:Real} = complex(rand(r, T), rand(r, T)) -# random Char values +### random characters + # returns a random valid Unicode scalar value (i.e. 0 - 0xd7ff, 0xe000 - # 0x10ffff) function rand(r::AbstractRNG, ::Type{Char}) c = rand(r, 0x00000000:0x0010f7ff) (c < 0xd800) ? Char(c) : Char(c+0x800) end -# random values from Dict, Set, IntSet (for efficiency) -function rand(r::AbstractRNG, t::Dict) - isempty(t) && throw(ArgumentError("collection must be non-empty")) - rg = RangeGenerator(1:length(t.slots)) - while true - i = rand(r, rg) - Base.isslotfilled(t, i) && @inbounds return (t.keys[i] => t.vals[i]) - end -end - -rand(r::AbstractRNG, s::Set) = rand(r, s.dict).first - -function rand(r::AbstractRNG, s::IntSet) - isempty(s) && throw(ArgumentError("collection must be non-empty")) - # s can be empty while s.bits is not, so we cannot rely on the - # length check in RangeGenerator below - rg = RangeGenerator(1:length(s.bits)) - while true - n = rand(r, rg) - @inbounds b = s.bits[n] - b && return n - end -end - -function nth(iter, n::Integer)::eltype(iter) - for (i, x) in enumerate(iter) - i == n && return x - end -end -nth(iter::AbstractArray, n::Integer) = iter[n] - -rand(r::AbstractRNG, s::Union{Associative,AbstractSet}) = nth(s, rand(r, 1:length(s))) - -rand(s::Union{Associative,AbstractSet}) = rand(GLOBAL_RNG, s) - -## Arrays of random numbers - -rand(r::AbstractRNG, dims::Dims) = rand(r, Float64, dims) -rand(r::AbstractRNG, dims::Integer...) = rand(r, convert(Dims, dims)) - -rand(r::AbstractRNG, T::Type, dims::Dims) = rand!(r, Array{T}(dims)) -rand(r::AbstractRNG, T::Type, d1::Integer, dims::Integer...) = rand(r, T, tuple(Int(d1), convert(Dims, dims)...)) -# note: the above method would trigger an ambiguity warning if d1 was not separated out: -# rand(r, ()) would match both this method and rand(r, dims::Dims) -# moreover, a call like rand(r, NotImplementedType()) would be an infinite loop +### arrays of random numbers function rand!(r::AbstractRNG, A::AbstractArray{T}, ::Type{X}=T) where {T,X} for i in eachindex(A) @@ -149,28 +42,32 @@ function rand!(r::AbstractRNG, A::AbstractArray{T}, ::Type{X}=T) where {T,X} end rand!(A::AbstractArray, ::Type{X}) where {X} = rand!(GLOBAL_RNG, A, X) +# NOTE: if the second parameter above is defaulted to eltype(A) and the +# method below is removed, then some specialized methods (e.g. for +# rand!(::Array{Float64})) will fail to be called +rand!(A::AbstractArray) = rand!(GLOBAL_RNG, A) -function rand!(r::AbstractRNG, A::AbstractArray, s::Union{Dict,Set,IntSet}) - for i in eachindex(A) - @inbounds A[i] = rand(r, s) - end - A -end - -# avoid linear complexity for repeated calls with generic containers -rand!(r::AbstractRNG, A::AbstractArray, s::Union{Associative,AbstractSet}) = rand!(r, A, collect(s)) -rand!(A::AbstractArray, s::Union{Associative,AbstractSet}) = rand!(GLOBAL_RNG, A, s) +rand(r::AbstractRNG, dims::Dims) = rand(r, Float64, dims) +rand( dims::Dims) = rand(GLOBAL_RNG, dims) +rand(r::AbstractRNG, dims::Integer...) = rand(r, Dims(dims)) +rand( dims::Integer...) = rand(Dims(dims)) -rand(r::AbstractRNG, s::Associative{K,V}, dims::Dims) where {K,V} = rand!(r, Array{Pair{K,V}}(dims), s) -rand(r::AbstractRNG, s::AbstractSet{T}, dims::Dims) where {T} = rand!(r, Array{T}(dims), s) -rand(r::AbstractRNG, s::Union{Associative,AbstractSet}, dims::Integer...) = rand(r, s, convert(Dims, dims)) -rand(s::Union{Associative,AbstractSet}, dims::Integer...) = rand(GLOBAL_RNG, s, convert(Dims, dims)) -rand(s::Union{Associative,AbstractSet}, dims::Dims) = rand(GLOBAL_RNG, s, dims) +rand(r::AbstractRNG, T::Type, dims::Dims) = rand!(r, Array{T}(dims)) +rand( T::Type, dims::Dims) = rand(GLOBAL_RNG, T, dims) +rand(r::AbstractRNG, T::Type, d1::Integer, dims::Integer...) = rand(r, T, tuple(Int(d1), convert(Dims, dims)...)) +rand( T::Type, d1::Integer, dims::Integer...) = rand(T, tuple(Int(d1), convert(Dims, dims)...)) +# note: the above methods would trigger an ambiguity warning if d1 was not separated out: +# rand(r, ()) would match both this method and rand(r, dims::Dims) +# moreover, a call like rand(r, NotImplementedType()) would be an infinite loop ## Generate random integer within a range +abstract type RangeGenerator end + +### RangeGenerator for BitInteger + # remainder function according to Knuth, where rem_knuth(a, 0) = a rem_knuth(a::UInt, b::UInt) = a % (b + (b == 0)) + a * (b == 0) rem_knuth(a::T, b::T) where {T<:Unsigned} = b != 0 ? a % b : a @@ -183,18 +80,17 @@ maxmultiple(k::T) where {T<:Unsigned} = (div(typemax(T) - k + oneunit(k), k + (k # maximum multiple of k within 1:2^32 or 1:2^64 decremented by one, depending on size maxmultiplemix(k::UInt64) = if k >> 32 != 0; maxmultiple(k); else (div(0x0000000100000000, k + (k == 0))*k - oneunit(k))::UInt64; end -abstract type RangeGenerator end - struct RangeGeneratorInt{T<:Integer,U<:Unsigned} <: RangeGenerator a::T # first element of the range k::U # range length or zero for full range u::U # rejection threshold end + # generators with 32, 128 bits entropy RangeGeneratorInt(a::T, k::U) where {T,U<:Union{UInt32,UInt128}} = RangeGeneratorInt{T,U}(a, k, maxmultiple(k)) # mixed 32/64 bits entropy generator RangeGeneratorInt(a::T, k::UInt64) where {T} = RangeGeneratorInt{T,UInt64}(a, k, maxmultiplemix(k)) -# generator for ranges + function RangeGenerator(r::UnitRange{T}) where T<:Unsigned if isempty(r) throw(ArgumentError("range must be non-empty")) @@ -202,7 +98,6 @@ function RangeGenerator(r::UnitRange{T}) where T<:Unsigned RangeGeneratorInt(first(r), last(r) - first(r) + oneunit(T)) end -# specialized versions for (T, U) in [(UInt8, UInt32), (UInt16, UInt32), (Int8, UInt32), (Int16, UInt32), (Int32, UInt32), (Int64, UInt64), (Int128, UInt128), (Bool, UInt32)] @@ -215,6 +110,8 @@ for (T, U) in [(UInt8, UInt32), (UInt16, UInt32), end end +### RangeGenerator for BigInt + struct RangeGeneratorBigInt <: RangeGenerator a::BigInt # first m::BigInt # range length - 1 @@ -235,6 +132,7 @@ function RangeGenerator(r::UnitRange{BigInt}) return RangeGeneratorBigInt(first(r), m, nlimbs, nlimbsmax, mask) end +### rand(::RangeGenerator) # this function uses 32 bit entropy for small ranges of length <= typemax(UInt32) + 1 # RangeGeneratorInt is responsible for providing the right value of k @@ -279,12 +177,7 @@ function rand(rng::AbstractRNG, g::RangeGeneratorBigInt) MPZ.add!(x, g.a) end -rand(rng::AbstractRNG, r::UnitRange{<:Union{Signed,Unsigned,BigInt,Bool}}) = rand(rng, RangeGenerator(r)) - - -# Randomly draw a sample from an AbstractArray r -# (e.g. r is a range 0:2:8 or a vector [2, 3, 5, 7]) -rand(rng::AbstractRNG, r::AbstractArray) = @inbounds return r[rand(rng, 1:length(r))] +#### arrays function rand!(rng::AbstractRNG, A::AbstractArray, g::RangeGenerator) for i in eachindex(A) @@ -293,8 +186,20 @@ function rand!(rng::AbstractRNG, A::AbstractArray, g::RangeGenerator) return A end +### random values from UnitRange + +rand(rng::AbstractRNG, r::UnitRange{<:Union{Signed,Unsigned,BigInt,Bool}}) = rand(rng, RangeGenerator(r)) + rand!(rng::AbstractRNG, A::AbstractArray, r::UnitRange{<:Union{Signed,Unsigned,BigInt,Bool,Char}}) = rand!(rng, A, RangeGenerator(r)) + +## random values from AbstractArray + +rand(rng::AbstractRNG, r::AbstractArray) = @inbounds return r[rand(rng, 1:length(r))] +rand( r::AbstractArray) = rand(GLOBAL_RNG, r) + +### arrays + function rand!(rng::AbstractRNG, A::AbstractArray, r::AbstractArray) g = RangeGenerator(1:(length(r))) for i in eachindex(A) @@ -303,10 +208,72 @@ function rand!(rng::AbstractRNG, A::AbstractArray, r::AbstractArray) return A end +rand!(A::AbstractArray, r::AbstractArray) = rand!(GLOBAL_RNG, A, r) + rand(rng::AbstractRNG, r::AbstractArray{T}, dims::Dims) where {T} = rand!(rng, Array{T}(dims), r) -rand(rng::AbstractRNG, r::AbstractArray, dims::Integer...) = rand(rng, r, convert(Dims, dims)) +rand( r::AbstractArray, dims::Dims) = rand(GLOBAL_RNG, r, dims) +rand(rng::AbstractRNG, r::AbstractArray, dims::Integer...) = rand(rng, r, convert(Dims, dims)) +rand( r::AbstractArray, dims::Integer...) = rand(GLOBAL_RNG, r, convert(Dims, dims)) + + +## random values from Dict, Set, IntSet + +function rand(r::AbstractRNG, t::Dict) + isempty(t) && throw(ArgumentError("collection must be non-empty")) + rg = RangeGenerator(1:length(t.slots)) + while true + i = rand(r, rg) + Base.isslotfilled(t, i) && @inbounds return (t.keys[i] => t.vals[i]) + end +end -# rand from a string +rand(r::AbstractRNG, s::Set) = rand(r, s.dict).first + +function rand(r::AbstractRNG, s::IntSet) + isempty(s) && throw(ArgumentError("collection must be non-empty")) + # s can be empty while s.bits is not, so we cannot rely on the + # length check in RangeGenerator below + rg = RangeGenerator(1:length(s.bits)) + while true + n = rand(r, rg) + @inbounds b = s.bits[n] + b && return n + end +end + +function nth(iter, n::Integer)::eltype(iter) + for (i, x) in enumerate(iter) + i == n && return x + end +end +nth(iter::AbstractArray, n::Integer) = iter[n] + +rand(r::AbstractRNG, s::Union{Associative,AbstractSet}) = nth(s, rand(r, 1:length(s))) + +rand(s::Union{Associative,AbstractSet}) = rand(GLOBAL_RNG, s) + +### arrays + +function rand!(r::AbstractRNG, A::AbstractArray, s::Union{Dict,Set,IntSet}) + for i in eachindex(A) + @inbounds A[i] = rand(r, s) + end + A +end + +# avoid linear complexity for repeated calls with generic containers +rand!(r::AbstractRNG, A::AbstractArray, s::Union{Associative,AbstractSet}) = rand!(r, A, collect(s)) + +rand!(A::AbstractArray, s::Union{Associative,AbstractSet}) = rand!(GLOBAL_RNG, A, s) + +rand(r::AbstractRNG, s::Associative{K,V}, dims::Dims) where {K,V} = rand!(r, Array{Pair{K,V}}(dims), s) +rand(r::AbstractRNG, s::AbstractSet{T}, dims::Dims) where {T} = rand!(r, Array{T}(dims), s) +rand(r::AbstractRNG, s::Union{Associative,AbstractSet}, dims::Integer...) = rand(r, s, convert(Dims, dims)) +rand(s::Union{Associative,AbstractSet}, dims::Integer...) = rand(GLOBAL_RNG, s, convert(Dims, dims)) +rand(s::Union{Associative,AbstractSet}, dims::Dims) = rand(GLOBAL_RNG, s, dims) + + +## random characters from a string isvalid_unsafe(s::String, i) = !Base.is_valid_continuation(unsafe_load(pointer(s), i)) isvalid_unsafe(s::AbstractString, i) = isvalid(s, i) @@ -323,7 +290,8 @@ end rand(s::AbstractString) = rand(GLOBAL_RNG, s) -## rand from a string for arrays +### arrays + # we use collect(str), which is most of the time more efficient than specialized methods # (except maybe for very small arrays) rand!(rng::AbstractRNG, A::AbstractArray, str::AbstractString) = rand!(rng, A, collect(str)) diff --git a/base/random/misc.jl b/base/random/misc.jl index 7d2971d449983..d18fb2fdf7795 100644 --- a/base/random/misc.jl +++ b/base/random/misc.jl @@ -1,6 +1,6 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -## random BitArrays (AbstractRNG) +## rand!(::BitArray) && bitrand function rand!(rng::AbstractRNG, B::BitArray) isempty(B) && return B @@ -40,7 +40,7 @@ bitrand(dims::Dims) = rand!(BitArray(dims)) bitrand(dims::Integer...) = rand!(BitArray(convert(Dims, dims))) -# return a random string (often useful for temporary filenames/dirnames) +## randstring (often useful for temporary filenames/dirnames) """ randstring([rng=GLOBAL_RNG], [chars], [len=8]) @@ -78,6 +78,8 @@ let b = UInt8['0':'9';'A':'Z';'a':'z'] end +## randsubseq & randsubseq! + # Fill S (resized as needed) with a random subsequence of A, where # each element of A is included in S with independent probability p. # (Note that this is different from the problem of finding a random @@ -137,6 +139,9 @@ large.) Technically, this process is known as "Bernoulli sampling" of `A`. """ randsubseq(A::AbstractArray, p::Real) = randsubseq(GLOBAL_RNG, A, p) + +## rand_lt (helper function) + "Return a random `Int` (masked with `mask`) in ``[0, n)``, when `n <= 2^52`." @inline function rand_lt(r::AbstractRNG, n::Int, mask::Int=nextpow2(n)-1) # this duplicates the functionality of RangeGenerator objects, @@ -147,6 +152,9 @@ randsubseq(A::AbstractArray, p::Real) = randsubseq(GLOBAL_RNG, A, p) end end + +## shuffle & shuffle! + """ shuffle!([rng=GLOBAL_RNG,] v::AbstractArray) @@ -220,6 +228,9 @@ julia> shuffle(rng, collect(1:10)) shuffle(r::AbstractRNG, a::AbstractArray) = shuffle!(r, copymutable(a)) shuffle(a::AbstractArray) = shuffle(GLOBAL_RNG, a) + +## randperm & randperm! + """ randperm([rng=GLOBAL_RNG,] n::Integer) @@ -279,6 +290,8 @@ end randperm!(a::Array{<:Integer}) = randperm!(GLOBAL_RNG, a) +## randcycle & randcycle! + """ randcycle([rng=GLOBAL_RNG,] n::Integer) diff --git a/base/random/normal.jl b/base/random/normal.jl index 0de252d4cb9cf..1a51b2db1b724 100644 --- a/base/random/normal.jl +++ b/base/random/normal.jl @@ -1,12 +1,14 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -## randn() - Normally distributed random numbers using Ziggurat algorithm +# Normally distributed random numbers using Ziggurat algorithm # The Ziggurat Method for generating random variables - Marsaglia and Tsang # Paper and reference code: http://www.jstatsoft.org/v05/i08/ # randmtzig (covers also exponential variates) + ## Tables for normal variates + const ki = UInt64[0x0007799ec012f7b2,0x0000000000000000,0x0006045f4c7de363,0x0006d1aa7d5ec0a5, 0x000728fb3f60f777,0x0007592af4e9fbc0,0x000777a5c0bf655d,0x00078ca3857d2256, @@ -248,6 +250,7 @@ const fi = 1.2602859304985975e-03] ## Tables for exponential variates + const ke = UInt64[0x000e290a13924be3,0x0000000000000000,0x0009beadebce18bf,0x000c377ac71f9e08, 0x000d4ddb99075857,0x000de893fb8ca23e,0x000e4a8e87c4328d,0x000e8dff16ae1cb9, @@ -489,11 +492,15 @@ const fe = 2.1459677437189063e-03,1.5362997803015724e-03,9.6726928232717454e-04, 4.5413435384149677e-04] +## Constants const ziggurat_nor_r = 3.6541528853610087963519472518 const ziggurat_nor_inv_r = inv(ziggurat_nor_r) const ziggurat_exp_r = 7.6971174701310497140446280481 + +## randn + """ randn([rng=GLOBAL_RNG], [T=Float64], [dims...]) @@ -543,6 +550,16 @@ function randn_unlikely(rng, idx, rabs, x) end end +### complex randn + +Base.@irrational SQRT_HALF 0.7071067811865475244008 sqrt(big(0.5)) + +randn(rng::AbstractRNG, ::Type{Complex{T}}) where {T<:AbstractFloat} = + Complex{T}(SQRT_HALF * randn(rng, T), SQRT_HALF * randn(rng, T)) + + +## randexp + """ randexp([rng=GLOBAL_RNG], [T=Float64], [dims...]) @@ -585,6 +602,9 @@ function randexp_unlikely(rng, idx, x) end end + +## arrays & other scalar methods + """ randn!([rng=GLOBAL_RNG], A::AbstractArray) -> A @@ -656,8 +676,3 @@ for randfun in [:randn, :randexp] $randfun( dims::Integer... ) = $randfun(GLOBAL_RNG, Float64, dims...) end end - -# complex randn -Base.@irrational SQRT_HALF 0.7071067811865475244008 sqrt(big(0.5)) -randn(rng::AbstractRNG, ::Type{Complex{T}}) where {T<:AbstractFloat} = - Complex{T}(SQRT_HALF * randn(rng, T), SQRT_HALF * randn(rng, T)) diff --git a/base/random/random.jl b/base/random/random.jl index 673c59d93dbf1..225c649eb9484 100644 --- a/base/random/random.jl +++ b/base/random/random.jl @@ -26,8 +26,6 @@ abstract type FloatInterval end mutable struct CloseOpen <: FloatInterval end mutable struct Close1Open2 <: FloatInterval end -## initialization - function __init__() try srand() @@ -42,4 +40,97 @@ include("generation.jl") include("normal.jl") include("misc.jl") + +## rand & rand! & srand docstrings + +""" + rand([rng=GLOBAL_RNG], [S], [dims...]) + +Pick a random element or array of random elements from the set of values specified by `S`; `S` can be + +* an indexable collection (for example `1:n` or `['x','y','z']`), +* an `Associative` or `AbstractSet` object, +* a string (considered as a collection of characters), or +* a type: the set of values to pick from is then equivalent to `typemin(S):typemax(S)` for + integers (this is not applicable to [`BigInt`](@ref)), and to ``[0, 1)`` for floating + point numbers; + +`S` defaults to [`Float64`](@ref). + +# Examples +```julia-repl +julia> rand(Int, 2) +2-element Array{Int64,1}: + 1339893410598768192 + 1575814717733606317 + +julia> rand(MersenneTwister(0), Dict(1=>2, 3=>4)) +1=>2 +``` + +!!! note + The complexity of `rand(rng, s::Union{Associative,AbstractSet})` + is linear in the length of `s`, unless an optimized method with + constant complexity is available, which is the case for `Dict`, + `Set` and `IntSet`. For more than a few calls, use `rand(rng, + collect(s))` instead, or either `rand(rng, Dict(s))` or `rand(rng, + Set(s))` as appropriate. +""" +rand + +""" + rand!([rng=GLOBAL_RNG], A, [S=eltype(A)]) + +Populate the array `A` with random values. If `S` is specified +(`S` can be a type or a collection, cf. [`rand`](@ref) for details), +the values are picked randomly from `S`. +This is equivalent to `copy!(A, rand(rng, S, size(A)))` +but without allocating a new array. + +# Examples +```jldoctest +julia> rng = MersenneTwister(1234); + +julia> rand!(rng, zeros(5)) +5-element Array{Float64,1}: + 0.590845 + 0.766797 + 0.566237 + 0.460085 + 0.794026 +``` +""" +rand! + +""" + srand([rng=GLOBAL_RNG], seed) -> rng + srand([rng=GLOBAL_RNG]) -> rng + +Reseed the random number generator. If a `seed` is provided, the RNG will give a +reproducible sequence of numbers, otherwise Julia will get entropy from the system. For +`MersenneTwister`, the `seed` may be a non-negative integer or a vector of [`UInt32`](@ref) +integers. `RandomDevice` does not support seeding. + +# Examples +```jldoctest +julia> srand(1234); + +julia> x1 = rand(2) +2-element Array{Float64,1}: + 0.590845 + 0.766797 + +julia> srand(1234); + +julia> x2 = rand(2) +2-element Array{Float64,1}: + 0.590845 + 0.766797 + +julia> x1 == x2 +true +``` +""" +srand + end # module From 1dd8a7e7f173cc80bc22414b907cfd8c350e74d5 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Tue, 11 Jul 2017 09:55:51 +0200 Subject: [PATCH 058/324] move static (and visually big) data at bottom of files --- base/random/dSFMT.jl | 8 +- base/random/normal.jl | 357 +++++++++++++++++++++--------------------- 2 files changed, 182 insertions(+), 183 deletions(-) diff --git a/base/random/dSFMT.jl b/base/random/dSFMT.jl index 32f86da9da2a1..a62e1b7a328d8 100644 --- a/base/random/dSFMT.jl +++ b/base/random/dSFMT.jl @@ -13,7 +13,7 @@ export DSFMT_state, dsfmt_get_min_array_size, dsfmt_get_idstring, const MEXP = 19937 "DSFMT internal state array size of N 128-bit integers." -const N = floor(Int, ((MEXP - 128) / 104 + 1)) +const N = floor(Int, ((MEXP - 128) / 104 + 1)) """Julia DSFMT state representation size counted in 32-bit integers. @@ -21,9 +21,6 @@ Size: (DSFMT state array of Int128 + 1)*4 + Int32 index + Int32 padding """ const JN32 = (N+1)*4+1+1 -"Jump polynomial for 10^20 steps for dSFMT with exponent 19937" -const JPOLY1e21 = "e172e20c5d2de26b567c0cace9e7c6cc4407bd5ffcd22ca59d37b73d54fdbd937cd3abc6f502e8c186dbd4f1a06b9e2b894f31be77424f94dddfd5a45888a84ca66eeeb242eefe6764ed859dafccae7a6a635b3a63fe9dfbbd5f2d3f2610d39388f53060e84edae75be4f4f2272c0f1f26d1231836ad040ab091550f8a3a5423fb3ab83e068fe2684057f15691c4dc757a3aee4bca8595bf1ad03500d9620a5dbe3b2d64380694895d2f379ca928238293ea267ce14236d5be816a61f018fe4f6bc3c9865f5d4d4186e320ab653d1f3c035ae83e2ad725648a67d3480331e763a1dcdfb5711b56796170b124f5febd723a664a2deefbfa9999d922a108b0e683582ae8d3baacb5bb56683405ea9e6e0d71ddb24b2229c72bb9d07061f2d1fa097ade823b607a2029d6e121ae09d93de01a154199e8e6a6e77c970bda72ba8079b2b3a15dd494a3188b1d94a25ae108a8a5bd0b050e6ce64a365a21420e07fdeebecae02eb68a4304b59283055d22c27d680ea35952834d828c9b9b9dd1a886b4f7fe82fe8f2a962e1e5390e563dc281c799aee2a441b7a813facb6ff5e94c059710dcfe7e6b1635e21ae0dc878dd5f7cc0e1101a74452495a67d23a2672c939f32c81d4a2611073990e92a084cc3a62fd42ee566f29d963a9cc5100ccd0a200f49ce0a74fa891efa1b974d342b7fedf9269e40d9b34e3c59c3d37201aecd5a04f4ae3d0c9a68c7ab78c662390e4cf36cb63ea3539c442efd0bf4aace4b8c8bde93c3d84b4d6290adfae1c5e3fcd457b6f3159e501f17b72ff6bc13d6bf61fbdafabefd16ac1dae0bca667e4e16a2b800732f1d0a9274c8a4c6cccd2db62fc275dc308c31c11cd6fda78de2f81f0e542b76b42b2cc09ed8f965d94c714c9918064f53af5379cfbbc31edf9cbce694f63a75f122048de6e57b094908f749661456813a908027f5d8397ab7962bf75ac779a3e1b7ae3fbc93397a67b486bb849befff1de6162ef2819715a88f41881e366ace692a900796a2806393898dd1750ac2b4ca3d34ca48942322fb6375f0c9a00c9701048ee8d7d7a17e11739177a7ad5027556e85835daf8594d84a97fe6621c0fce1495ae6ab8676cdc992d247acf5a4e5ec8c4755fde28117228d2c3ecf89edb91e93d949e2174924572265e36d176d082ed1be884e51d885ba3cda175c51edcee5042eaf519d292aa05aa4185b03858d710a9d0880b3d4e5111f858a52fe352cbe0a24f06a3d977ae2eb85e2a03a68131d0ab91dac4941067cf90ecd0fce156bcd40b8968cd4aa11e0b4353b14508d79d13ac00af4a4d452496b7f2393699889aa1e508427dbf0be3db91d955feb51e559af57640c6b3f9d5f95609852c28f9462a9869dd93acbdb1aafb2381ebb886a0b3fcec278f8bb0f62c23e157e49b89245b0881268ce594acbddd3605b9eaa77c9ff513e0dbad514914136d96fe2843fe2b4e886a0b718a9b8d1132132110618d0d3595da284cd2a4c9d09386199e4f4d7723983d3a374b51cf20dac5cabb4ff7e7197c2ebd9318463409baa583d6a6115c1b768282ff37b0fe152c97671e400d5ccba7d6875df0bf95c5d91257fedb124de393f31908d0e36251326aa29dd5be86291c80b4bf78f419ec151eeaeff643a58b48ab35ad2cd2c0b77b1965966ef3db6b6373cb2c4b590cef2f16f4d6f62f13a6cbf1a481565b5935edd4e76f7b6a8fd0d74bc336b40a803aec38125c006c877dfdcdb9ba2b7aecab5cafe6076e024c73e3567adf97f607a71d180402c22a20a8388f517484cc4198f97c2fe4f3407e0dc577e61f0f71354aa601cf4e3e42e1edd8722d50f5af3441f68caa568cc1c3a19956c1233f265bb47236afab24ee42b27b0042b90693d77c1923147360ae6503f6ba6abbc9dd52a7b4c36a3b6b55f6a80cfa7f101dd9f1bfc7d7eaf09a5d636b510228f245bfb37b4625025d2c911435cdf6f878113753e0804ab8ecab870ad733b9728d7636b17578b41239393e7de47cbce871137d2b61729dda67b2b84cd3363aad64c5dd5bd172f1f091305b1ff78982abe7dab1588036d097cf497e300e6c78a926048febd1b9462c07f5868928357b74297c87f503056b89f786d22a538b6702e290bca04639a0f1d0939b67f409e5e58e472a6a07fa543e2531c2567ec73c41f6769b6ba94c5aa0a030d006f5b6b1c5fb218b86a8f63a48bc867466f20f699859e87956f48a182d26ed451861dd21201ecc7239037ada67319bdf0849c387c73a110af798b4c5f9018bc97993e060ea2a2937fa2eb095d65ec07009fc407a350f1d6fb3c98a0a5f204be985b0cb6962f0eb7844a179c4598a92ea32d2d706c800034d2e960ded5b476d77073316b933fb3e6ba2f4f24a3b73a1e4d8ed1491d757ecf56fd72465dac0000736744d28d29073091587c8bccad302f7054e8a32bb8724974d9f3e449fc70b2a41f0008b548f717ac0a2c3a6580bfb50774933a578ad6acdcb89940bb406ea540893f097d8a88d1609ed605f25499de939083a0c8a7c6db462df5dfa06c298dd233e249433a54267d5cdc22e5524705b7d6b16b96bb2cb83e00cef62e21d91528a74cf95bfd1d391590c93f4058e9bb02656fd087a5b63d738d1c3b5cf533fd59c81cf9136bfcd3e955c19daf9906ef175791fde6a1d98155d7881e241c3522551cf9fcae42e1e46929ea39fd00943446823f9755085ccc8456a3090b73a3031a201d9c704a4ad4868dd9b6d06205560013973f60d637de2f18354bf4523d9d81dc2a7e78cd42c586364bbe0ed86fde0f081f801c1a4abb830839b7796d9a01f141bec8bd93144104c6dc59170162c0a5a639eb63a0a164970de50eb2e04f027394b26ed48d341f7851994df79d7cd663672a556f25e5e16a3adbe1003d631de938fabfed234df12b5ff3027f4a2da823834cb098e0f977a4eb9614579d5c7a1d400a1a933a657aef8ea1a66743d73b0cf37a7d64e9a63e4c7b09945f0db750b311b39783fb5ea216616751967d480a630d3da7c89d1c7beae20369137e96734a4cfedca56a7887f076fe4fe97534ad3e4f74d1a81750581a5ea214b440c7f30331ab86c257534c71175d1e731303a48b01c589fda4fb0d4368b4dd63d91204cb6fc389b2202aa94391907bfb72902a4031f5589ed5f391c2ce92aa998c200ba3c77d8bd747b9d0a29fa85cda3949a6d2bd0c3402e68f98fd451aa27b6c2dfd170e004577cbdb25e3a1b9852e9f66a370789c47bfce722446dade1b32ceae71ee0e1d96edf7ed08a93e3690056f46c3d8e63f88e53673ee71d72cfedbeba493ee91333120e09e9ce9f9c9a7a400f814ea618b1de48f9805e092f4e20f301fbb65caa83735a2a5c89befe4bce4116dca3688e1e14c6f09a945671dedbb5c0ba526842b6cae31d8b5ff978bae928a17a75c134630dd9de988f6ad3d89a071b33775a9660a40b48ec61ad3f93ac81cb1c65d8b0bab5c214786abd13cc10a8ea2e2a370e86e2fa1a372d83c9697b5e37b281e51507685f714fdaebe49ffc93a5582e1936eaee8e4140a4b72" - mutable struct DSFMT_state val::Vector{Int32} @@ -172,4 +169,7 @@ function dsfmt_jump_next_state!(mts::Vector{UInt64}) return mts end +"Jump polynomial for 10^20 steps for dSFMT with exponent 19937" +const JPOLY1e21 = "e172e20c5d2de26b567c0cace9e7c6cc4407bd5ffcd22ca59d37b73d54fdbd937cd3abc6f502e8c186dbd4f1a06b9e2b894f31be77424f94dddfd5a45888a84ca66eeeb242eefe6764ed859dafccae7a6a635b3a63fe9dfbbd5f2d3f2610d39388f53060e84edae75be4f4f2272c0f1f26d1231836ad040ab091550f8a3a5423fb3ab83e068fe2684057f15691c4dc757a3aee4bca8595bf1ad03500d9620a5dbe3b2d64380694895d2f379ca928238293ea267ce14236d5be816a61f018fe4f6bc3c9865f5d4d4186e320ab653d1f3c035ae83e2ad725648a67d3480331e763a1dcdfb5711b56796170b124f5febd723a664a2deefbfa9999d922a108b0e683582ae8d3baacb5bb56683405ea9e6e0d71ddb24b2229c72bb9d07061f2d1fa097ade823b607a2029d6e121ae09d93de01a154199e8e6a6e77c970bda72ba8079b2b3a15dd494a3188b1d94a25ae108a8a5bd0b050e6ce64a365a21420e07fdeebecae02eb68a4304b59283055d22c27d680ea35952834d828c9b9b9dd1a886b4f7fe82fe8f2a962e1e5390e563dc281c799aee2a441b7a813facb6ff5e94c059710dcfe7e6b1635e21ae0dc878dd5f7cc0e1101a74452495a67d23a2672c939f32c81d4a2611073990e92a084cc3a62fd42ee566f29d963a9cc5100ccd0a200f49ce0a74fa891efa1b974d342b7fedf9269e40d9b34e3c59c3d37201aecd5a04f4ae3d0c9a68c7ab78c662390e4cf36cb63ea3539c442efd0bf4aace4b8c8bde93c3d84b4d6290adfae1c5e3fcd457b6f3159e501f17b72ff6bc13d6bf61fbdafabefd16ac1dae0bca667e4e16a2b800732f1d0a9274c8a4c6cccd2db62fc275dc308c31c11cd6fda78de2f81f0e542b76b42b2cc09ed8f965d94c714c9918064f53af5379cfbbc31edf9cbce694f63a75f122048de6e57b094908f749661456813a908027f5d8397ab7962bf75ac779a3e1b7ae3fbc93397a67b486bb849befff1de6162ef2819715a88f41881e366ace692a900796a2806393898dd1750ac2b4ca3d34ca48942322fb6375f0c9a00c9701048ee8d7d7a17e11739177a7ad5027556e85835daf8594d84a97fe6621c0fce1495ae6ab8676cdc992d247acf5a4e5ec8c4755fde28117228d2c3ecf89edb91e93d949e2174924572265e36d176d082ed1be884e51d885ba3cda175c51edcee5042eaf519d292aa05aa4185b03858d710a9d0880b3d4e5111f858a52fe352cbe0a24f06a3d977ae2eb85e2a03a68131d0ab91dac4941067cf90ecd0fce156bcd40b8968cd4aa11e0b4353b14508d79d13ac00af4a4d452496b7f2393699889aa1e508427dbf0be3db91d955feb51e559af57640c6b3f9d5f95609852c28f9462a9869dd93acbdb1aafb2381ebb886a0b3fcec278f8bb0f62c23e157e49b89245b0881268ce594acbddd3605b9eaa77c9ff513e0dbad514914136d96fe2843fe2b4e886a0b718a9b8d1132132110618d0d3595da284cd2a4c9d09386199e4f4d7723983d3a374b51cf20dac5cabb4ff7e7197c2ebd9318463409baa583d6a6115c1b768282ff37b0fe152c97671e400d5ccba7d6875df0bf95c5d91257fedb124de393f31908d0e36251326aa29dd5be86291c80b4bf78f419ec151eeaeff643a58b48ab35ad2cd2c0b77b1965966ef3db6b6373cb2c4b590cef2f16f4d6f62f13a6cbf1a481565b5935edd4e76f7b6a8fd0d74bc336b40a803aec38125c006c877dfdcdb9ba2b7aecab5cafe6076e024c73e3567adf97f607a71d180402c22a20a8388f517484cc4198f97c2fe4f3407e0dc577e61f0f71354aa601cf4e3e42e1edd8722d50f5af3441f68caa568cc1c3a19956c1233f265bb47236afab24ee42b27b0042b90693d77c1923147360ae6503f6ba6abbc9dd52a7b4c36a3b6b55f6a80cfa7f101dd9f1bfc7d7eaf09a5d636b510228f245bfb37b4625025d2c911435cdf6f878113753e0804ab8ecab870ad733b9728d7636b17578b41239393e7de47cbce871137d2b61729dda67b2b84cd3363aad64c5dd5bd172f1f091305b1ff78982abe7dab1588036d097cf497e300e6c78a926048febd1b9462c07f5868928357b74297c87f503056b89f786d22a538b6702e290bca04639a0f1d0939b67f409e5e58e472a6a07fa543e2531c2567ec73c41f6769b6ba94c5aa0a030d006f5b6b1c5fb218b86a8f63a48bc867466f20f699859e87956f48a182d26ed451861dd21201ecc7239037ada67319bdf0849c387c73a110af798b4c5f9018bc97993e060ea2a2937fa2eb095d65ec07009fc407a350f1d6fb3c98a0a5f204be985b0cb6962f0eb7844a179c4598a92ea32d2d706c800034d2e960ded5b476d77073316b933fb3e6ba2f4f24a3b73a1e4d8ed1491d757ecf56fd72465dac0000736744d28d29073091587c8bccad302f7054e8a32bb8724974d9f3e449fc70b2a41f0008b548f717ac0a2c3a6580bfb50774933a578ad6acdcb89940bb406ea540893f097d8a88d1609ed605f25499de939083a0c8a7c6db462df5dfa06c298dd233e249433a54267d5cdc22e5524705b7d6b16b96bb2cb83e00cef62e21d91528a74cf95bfd1d391590c93f4058e9bb02656fd087a5b63d738d1c3b5cf533fd59c81cf9136bfcd3e955c19daf9906ef175791fde6a1d98155d7881e241c3522551cf9fcae42e1e46929ea39fd00943446823f9755085ccc8456a3090b73a3031a201d9c704a4ad4868dd9b6d06205560013973f60d637de2f18354bf4523d9d81dc2a7e78cd42c586364bbe0ed86fde0f081f801c1a4abb830839b7796d9a01f141bec8bd93144104c6dc59170162c0a5a639eb63a0a164970de50eb2e04f027394b26ed48d341f7851994df79d7cd663672a556f25e5e16a3adbe1003d631de938fabfed234df12b5ff3027f4a2da823834cb098e0f977a4eb9614579d5c7a1d400a1a933a657aef8ea1a66743d73b0cf37a7d64e9a63e4c7b09945f0db750b311b39783fb5ea216616751967d480a630d3da7c89d1c7beae20369137e96734a4cfedca56a7887f076fe4fe97534ad3e4f74d1a81750581a5ea214b440c7f30331ab86c257534c71175d1e731303a48b01c589fda4fb0d4368b4dd63d91204cb6fc389b2202aa94391907bfb72902a4031f5589ed5f391c2ce92aa998c200ba3c77d8bd747b9d0a29fa85cda3949a6d2bd0c3402e68f98fd451aa27b6c2dfd170e004577cbdb25e3a1b9852e9f66a370789c47bfce722446dade1b32ceae71ee0e1d96edf7ed08a93e3690056f46c3d8e63f88e53673ee71d72cfedbeba493ee91333120e09e9ce9f9c9a7a400f814ea618b1de48f9805e092f4e20f301fbb65caa83735a2a5c89befe4bce4116dca3688e1e14c6f09a945671dedbb5c0ba526842b6cae31d8b5ff978bae928a17a75c134630dd9de988f6ad3d89a071b33775a9660a40b48ec61ad3f93ac81cb1c65d8b0bab5c214786abd13cc10a8ea2e2a370e86e2fa1a372d83c9697b5e37b281e51507685f714fdaebe49ffc93a5582e1936eaee8e4140a4b72" + end # module diff --git a/base/random/normal.jl b/base/random/normal.jl index 1a51b2db1b724..6a01c3b9ec10f 100644 --- a/base/random/normal.jl +++ b/base/random/normal.jl @@ -7,6 +7,184 @@ # randmtzig (covers also exponential variates) +## randn + +""" + randn([rng=GLOBAL_RNG], [T=Float64], [dims...]) + +Generate a normally-distributed random number of type `T` with mean 0 and standard deviation 1. +Optionally generate an array of normally-distributed random numbers. +The `Base` module currently provides an implementation for the types +[`Float16`](@ref), [`Float32`](@ref), and [`Float64`](@ref) (the default), and their +[`Complex`](@ref) counterparts. When the type argument is complex, the values are drawn +from the circularly symmetric complex normal distribution. + +# Examples +```jldoctest +julia> rng = MersenneTwister(1234); + +julia> randn(rng, Complex128) +0.6133070881429037 - 0.6376291670853887im + +julia> randn(rng, Complex64, (2, 3)) +2×3 Array{Complex{Float32},2}: + -0.349649-0.638457im 0.376756-0.192146im -0.396334-0.0136413im + 0.611224+1.56403im 0.355204-0.365563im 0.0905552+1.31012im +``` +""" +@inline function randn(rng::AbstractRNG=GLOBAL_RNG) + @inbounds begin + r = rand_ui52(rng) + rabs = Int64(r>>1) # One bit for the sign + idx = rabs & 0xFF + x = ifelse(r % Bool, -rabs, rabs)*wi[idx+1] + rabs < ki[idx+1] && return x # 99.3% of the time we return here 1st try + return randn_unlikely(rng, idx, rabs, x) + end +end + +# this unlikely branch is put in a separate function for better efficiency +function randn_unlikely(rng, idx, rabs, x) + @inbounds if idx == 0 + while true + xx = -ziggurat_nor_inv_r*log(rand(rng)) + yy = -log(rand(rng)) + yy+yy > xx*xx && return (rabs >> 8) % Bool ? -ziggurat_nor_r-xx : ziggurat_nor_r+xx + end + elseif (fi[idx] - fi[idx+1])*rand(rng) + fi[idx+1] < exp(-0.5*x*x) + return x # return from the triangular area + else + return randn(rng) + end +end + +### complex randn + +Base.@irrational SQRT_HALF 0.7071067811865475244008 sqrt(big(0.5)) + +randn(rng::AbstractRNG, ::Type{Complex{T}}) where {T<:AbstractFloat} = + Complex{T}(SQRT_HALF * randn(rng, T), SQRT_HALF * randn(rng, T)) + + +## randexp + +""" + randexp([rng=GLOBAL_RNG], [T=Float64], [dims...]) + +Generate a random number of type `T` according to the exponential distribution with scale 1. +Optionally generate an array of such random numbers. +The `Base` module currently provides an implementation for the types +[`Float16`](@ref), [`Float32`](@ref), and [`Float64`](@ref) (the default). + +# Examples +```jldoctest +julia> rng = MersenneTwister(1234); + +julia> randexp(rng, Float32) +2.4835055f0 + +julia> randexp(rng, 3, 3) +3×3 Array{Float64,2}: + 1.5167 1.30652 0.344435 + 0.604436 2.78029 0.418516 + 0.695867 0.693292 0.643644 +``` +""" +@inline function randexp(rng::AbstractRNG=GLOBAL_RNG) + @inbounds begin + ri = rand_ui52(rng) + idx = ri & 0xFF + x = ri*we[idx+1] + ri < ke[idx+1] && return x # 98.9% of the time we return here 1st try + return randexp_unlikely(rng, idx, x) + end +end + +function randexp_unlikely(rng, idx, x) + @inbounds if idx == 0 + return ziggurat_exp_r - log(rand(rng)) + elseif (fe[idx] - fe[idx+1])*rand(rng) + fe[idx+1] < exp(-x) + return x # return from the triangular area + else + return randexp(rng) + end +end + + +## arrays & other scalar methods + +""" + randn!([rng=GLOBAL_RNG], A::AbstractArray) -> A + +Fill the array `A` with normally-distributed (mean 0, standard deviation 1) random numbers. +Also see the [`rand`](@ref) function. + +# Examples +```jldoctest +julia> rng = MersenneTwister(1234); + +julia> randn!(rng, zeros(5)) +5-element Array{Float64,1}: + 0.867347 + -0.901744 + -0.494479 + -0.902914 + 0.864401 +``` +""" +function randn! end + +""" + randexp!([rng=GLOBAL_RNG], A::AbstractArray) -> A + +Fill the array `A` with random numbers following the exponential distribution (with scale 1). + +# Examples +```jldoctest +julia> rng = MersenneTwister(1234); + +julia> randexp!(rng, zeros(5)) +5-element Array{Float64,1}: + 2.48351 + 1.5167 + 0.604436 + 0.695867 + 1.30652 +``` +""" +function randexp! end + +for randfun in [:randn, :randexp] + randfun! = Symbol(randfun, :!) + @eval begin + # scalars + $randfun(rng::AbstractRNG, T::Union{Type{Float16},Type{Float32},Type{Float64}}) = + convert(T, $randfun(rng)) + $randfun(::Type{T}) where {T} = $randfun(GLOBAL_RNG, T) + + # filling arrays + function $randfun!(rng::AbstractRNG, A::AbstractArray{T}) where T + for i in eachindex(A) + @inbounds A[i] = $randfun(rng, T) + end + A + end + + $randfun!(A::AbstractArray) = $randfun!(GLOBAL_RNG, A) + + # generating arrays + $randfun(rng::AbstractRNG, ::Type{T}, dims::Dims ) where {T} = $randfun!(rng, Array{T}(dims)) + # Note that this method explicitly does not define $randfun(rng, T), in order to prevent an infinite recursion. + $randfun(rng::AbstractRNG, ::Type{T}, dim1::Integer, dims::Integer...) where {T} = $randfun!(rng, Array{T}(dim1, dims...)) + $randfun( ::Type{T}, dims::Dims ) where {T} = $randfun(GLOBAL_RNG, T, dims) + $randfun( ::Type{T}, dims::Integer... ) where {T} = $randfun(GLOBAL_RNG, T, dims...) + $randfun(rng::AbstractRNG, dims::Dims ) = $randfun(rng, Float64, dims) + $randfun(rng::AbstractRNG, dims::Integer... ) = $randfun(rng, Float64, dims...) + $randfun( dims::Dims ) = $randfun(GLOBAL_RNG, Float64, dims) + $randfun( dims::Integer... ) = $randfun(GLOBAL_RNG, Float64, dims...) + end +end + ## Tables for normal variates const ki = @@ -497,182 +675,3 @@ const fe = const ziggurat_nor_r = 3.6541528853610087963519472518 const ziggurat_nor_inv_r = inv(ziggurat_nor_r) const ziggurat_exp_r = 7.6971174701310497140446280481 - - -## randn - -""" - randn([rng=GLOBAL_RNG], [T=Float64], [dims...]) - -Generate a normally-distributed random number of type `T` with mean 0 and standard deviation 1. -Optionally generate an array of normally-distributed random numbers. -The `Base` module currently provides an implementation for the types -[`Float16`](@ref), [`Float32`](@ref), and [`Float64`](@ref) (the default), and their -[`Complex`](@ref) counterparts. When the type argument is complex, the values are drawn -from the circularly symmetric complex normal distribution. - -# Examples -```jldoctest -julia> rng = MersenneTwister(1234); - -julia> randn(rng, Complex128) -0.6133070881429037 - 0.6376291670853887im - -julia> randn(rng, Complex64, (2, 3)) -2×3 Array{Complex{Float32},2}: - -0.349649-0.638457im 0.376756-0.192146im -0.396334-0.0136413im - 0.611224+1.56403im 0.355204-0.365563im 0.0905552+1.31012im -``` -""" -@inline function randn(rng::AbstractRNG=GLOBAL_RNG) - @inbounds begin - r = rand_ui52(rng) - rabs = Int64(r>>1) # One bit for the sign - idx = rabs & 0xFF - x = ifelse(r % Bool, -rabs, rabs)*wi[idx+1] - rabs < ki[idx+1] && return x # 99.3% of the time we return here 1st try - return randn_unlikely(rng, idx, rabs, x) - end -end - -# this unlikely branch is put in a separate function for better efficiency -function randn_unlikely(rng, idx, rabs, x) - @inbounds if idx == 0 - while true - xx = -ziggurat_nor_inv_r*log(rand(rng)) - yy = -log(rand(rng)) - yy+yy > xx*xx && return (rabs >> 8) % Bool ? -ziggurat_nor_r-xx : ziggurat_nor_r+xx - end - elseif (fi[idx] - fi[idx+1])*rand(rng) + fi[idx+1] < exp(-0.5*x*x) - return x # return from the triangular area - else - return randn(rng) - end -end - -### complex randn - -Base.@irrational SQRT_HALF 0.7071067811865475244008 sqrt(big(0.5)) - -randn(rng::AbstractRNG, ::Type{Complex{T}}) where {T<:AbstractFloat} = - Complex{T}(SQRT_HALF * randn(rng, T), SQRT_HALF * randn(rng, T)) - - -## randexp - -""" - randexp([rng=GLOBAL_RNG], [T=Float64], [dims...]) - -Generate a random number of type `T` according to the exponential distribution with scale 1. -Optionally generate an array of such random numbers. -The `Base` module currently provides an implementation for the types -[`Float16`](@ref), [`Float32`](@ref), and [`Float64`](@ref) (the default). - -# Examples -```jldoctest -julia> rng = MersenneTwister(1234); - -julia> randexp(rng, Float32) -2.4835055f0 - -julia> randexp(rng, 3, 3) -3×3 Array{Float64,2}: - 1.5167 1.30652 0.344435 - 0.604436 2.78029 0.418516 - 0.695867 0.693292 0.643644 -``` -""" -@inline function randexp(rng::AbstractRNG=GLOBAL_RNG) - @inbounds begin - ri = rand_ui52(rng) - idx = ri & 0xFF - x = ri*we[idx+1] - ri < ke[idx+1] && return x # 98.9% of the time we return here 1st try - return randexp_unlikely(rng, idx, x) - end -end - -function randexp_unlikely(rng, idx, x) - @inbounds if idx == 0 - return ziggurat_exp_r - log(rand(rng)) - elseif (fe[idx] - fe[idx+1])*rand(rng) + fe[idx+1] < exp(-x) - return x # return from the triangular area - else - return randexp(rng) - end -end - - -## arrays & other scalar methods - -""" - randn!([rng=GLOBAL_RNG], A::AbstractArray) -> A - -Fill the array `A` with normally-distributed (mean 0, standard deviation 1) random numbers. -Also see the [`rand`](@ref) function. - -# Examples -```jldoctest -julia> rng = MersenneTwister(1234); - -julia> randn!(rng, zeros(5)) -5-element Array{Float64,1}: - 0.867347 - -0.901744 - -0.494479 - -0.902914 - 0.864401 -``` -""" -function randn! end - -""" - randexp!([rng=GLOBAL_RNG], A::AbstractArray) -> A - -Fill the array `A` with random numbers following the exponential distribution (with scale 1). - -# Examples -```jldoctest -julia> rng = MersenneTwister(1234); - -julia> randexp!(rng, zeros(5)) -5-element Array{Float64,1}: - 2.48351 - 1.5167 - 0.604436 - 0.695867 - 1.30652 -``` -""" -function randexp! end - -for randfun in [:randn, :randexp] - randfun! = Symbol(randfun, :!) - @eval begin - # scalars - $randfun(rng::AbstractRNG, T::Union{Type{Float16},Type{Float32},Type{Float64}}) = - convert(T, $randfun(rng)) - $randfun(::Type{T}) where {T} = $randfun(GLOBAL_RNG, T) - - # filling arrays - function $randfun!(rng::AbstractRNG, A::AbstractArray{T}) where T - for i in eachindex(A) - @inbounds A[i] = $randfun(rng, T) - end - A - end - - $randfun!(A::AbstractArray) = $randfun!(GLOBAL_RNG, A) - - # generating arrays - $randfun(rng::AbstractRNG, ::Type{T}, dims::Dims ) where {T} = $randfun!(rng, Array{T}(dims)) - # Note that this method explicitly does not define $randfun(rng, T), in order to prevent an infinite recursion. - $randfun(rng::AbstractRNG, ::Type{T}, dim1::Integer, dims::Integer...) where {T} = $randfun!(rng, Array{T}(dim1, dims...)) - $randfun( ::Type{T}, dims::Dims ) where {T} = $randfun(GLOBAL_RNG, T, dims) - $randfun( ::Type{T}, dims::Integer... ) where {T} = $randfun(GLOBAL_RNG, T, dims...) - $randfun(rng::AbstractRNG, dims::Dims ) = $randfun(rng, Float64, dims) - $randfun(rng::AbstractRNG, dims::Integer... ) = $randfun(rng, Float64, dims...) - $randfun( dims::Dims ) = $randfun(GLOBAL_RNG, Float64, dims) - $randfun( dims::Integer... ) = $randfun(GLOBAL_RNG, Float64, dims...) - end -end From d7de9e3c44d60bbc85a3c1082fb4c8f679e7a2b1 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Tue, 11 Jul 2017 10:50:20 +0200 Subject: [PATCH 059/324] wrap lines at 92 chars --- base/random/RNGs.jl | 89 +++++++++++++++++++++++++-------------- base/random/dSFMT.jl | 10 +++-- base/random/generation.jl | 79 ++++++++++++++++++++-------------- base/random/misc.jl | 11 +++-- base/random/normal.jl | 15 ++++--- base/random/random.jl | 3 +- 6 files changed, 129 insertions(+), 78 deletions(-) diff --git a/base/random/RNGs.jl b/base/random/RNGs.jl index b7def1dd42886..c60317cff9f94 100644 --- a/base/random/RNGs.jl +++ b/base/random/RNGs.jl @@ -27,7 +27,8 @@ else # !windows file::IOStream unlimited::Bool - RandomDevice(unlimited::Bool=true) = new(open(unlimited ? "/dev/urandom" : "/dev/random"), unlimited) + RandomDevice(unlimited::Bool=true) = + new(open(unlimited ? "/dev/urandom" : "/dev/random"), unlimited) end rand(rd::RandomDevice, T::BoolBitIntegerType) = read( rd.file, T) @@ -37,7 +38,8 @@ end # os-test """ RandomDevice() -Create a `RandomDevice` RNG object. Two such objects will always generate different streams of random numbers. +Create a `RandomDevice` RNG object. +Two such objects will always generate different streams of random numbers. """ RandomDevice @@ -51,7 +53,8 @@ rand(rng::RandomDevice, ::Type{CloseOpen}) = rand(rng, Close1Open2) - 1.0 @inline rand(r::RandomDevice, ::Type{Float64}) = rand(r, CloseOpen) rand(r::RandomDevice, ::Type{Float16}) = - Float16(reinterpret(Float32, (rand_ui10_raw(r) % UInt32 << 13) & 0x007fe000 | 0x3f800000) - 1) + Float16(reinterpret(Float32, + (rand_ui10_raw(r) % UInt32 << 13) & 0x007fe000 | 0x3f800000) - 1) rand(r::RandomDevice, ::Type{Float32}) = reinterpret(Float32, rand_ui23_raw(r) % UInt32 & 0x007fffff | 0x3f800000) - 1 @@ -68,9 +71,9 @@ mutable struct MersenneTwister <: AbstractRNG idx::Int function MersenneTwister(seed, state, vals, idx) - if !(length(vals) == MTCacheLength && 0 <= idx <= MTCacheLength) - throw(DomainError(idx, "`length(vals)` and `idx` must be consistent with $MTCacheLength")) - end + length(vals) == MTCacheLength && 0 <= idx <= MTCacheLength || + throw(DomainError((length(vals), idx), + "`length(vals)` and `idx` must be consistent with $MTCacheLength")) new(seed, state, vals, idx) end end @@ -81,8 +84,9 @@ MersenneTwister(seed::Vector{UInt32}, state::DSFMT_state) = """ MersenneTwister(seed) -Create a `MersenneTwister` RNG object. Different RNG objects can have their own seeds, which -may be useful for generating different streams of random numbers. +Create a `MersenneTwister` RNG object. Different RNG objects can have +their own seeds, which may be useful for generating different streams +of random numbers. # Examples ```jldoctest @@ -118,7 +122,8 @@ copy(src::MersenneTwister) = MersenneTwister(copy(src.seed), copy(src.state), copy(src.vals), src.idx) ==(r1::MersenneTwister, r2::MersenneTwister) = - r1.seed == r2.seed && r1.state == r2.state && isequal(r1.vals, r2.vals) && r1.idx == r2.idx + r1.seed == r2.seed && r1.state == r2.state && isequal(r1.vals, r2.vals) && + r1.idx == r2.idx hash(r::MersenneTwister, h::UInt) = foldr(hash, h, (r.seed, r.state, r.vals, r.idx)) @@ -146,16 +151,19 @@ end #### make_seed() -# make_seed methods produce values of type Array{UInt32}, suitable for MersenneTwister seeding +# make_seed produces values of type Vector{UInt32}, suitable for MersenneTwister seeding function make_seed() try return rand(RandomDevice(), UInt32, 4) catch - println(STDERR, "Entropy pool not available to seed RNG; using ad-hoc entropy sources.") + println(STDERR, + "Entropy pool not available to seed RNG; using ad-hoc entropy sources.") seed = reinterpret(UInt64, time()) seed = hash(seed, UInt64(getpid())) try - seed = hash(seed, parse(UInt64, read(pipeline(`ifconfig`, `sha1sum`), String)[1:40], 16)) + seed = hash(seed, parse(UInt64, + read(pipeline(`ifconfig`, `sha1sum`), String)[1:40], + 16)) end return make_seed(seed) end @@ -198,10 +206,12 @@ const GLOBAL_RNG = MersenneTwister(0) # precondition: !mt_empty(r) @inline rand_inbounds(r::MersenneTwister, ::Type{Close1Open2}) = mt_pop!(r) -@inline rand_inbounds(r::MersenneTwister, ::Type{CloseOpen}) = rand_inbounds(r, Close1Open2) - 1.0 +@inline rand_inbounds(r::MersenneTwister, ::Type{CloseOpen}) = + rand_inbounds(r, Close1Open2) - 1.0 @inline rand_inbounds(r::MersenneTwister) = rand_inbounds(r, CloseOpen) -@inline rand_ui52_raw_inbounds(r::MersenneTwister) = reinterpret(UInt64, rand_inbounds(r, Close1Open2)) +@inline rand_ui52_raw_inbounds(r::MersenneTwister) = + reinterpret(UInt64, rand_inbounds(r, Close1Open2)) @inline rand_ui52_raw(r::MersenneTwister) = (reserve_1(r); rand_ui52_raw_inbounds(r)) @inline function rand_ui2x52_raw(r::MersenneTwister) @@ -219,20 +229,23 @@ rand_ui23_raw(r::MersenneTwister) = rand_ui52_raw(r) #### floats -@inline rand(r::MersenneTwister, ::Type{I}) where {I<:FloatInterval} = (reserve_1(r); rand_inbounds(r, I)) +@inline rand(r::MersenneTwister, ::Type{I}) where {I<:FloatInterval} = + (reserve_1(r); rand_inbounds(r, I)) @inline rand(r::MersenneTwister, ::Type{Float64}) = rand(r, CloseOpen) rand(r::MersenneTwister, ::Type{Float16}) = - Float16(reinterpret(Float32, (rand_ui10_raw(r) % UInt32 << 13) & 0x007fe000 | 0x3f800000) - 1) + Float16(reinterpret(Float32, + (rand_ui10_raw(r) % UInt32 << 13) & 0x007fe000 | 0x3f800000) - 1) rand(r::MersenneTwister, ::Type{Float32}) = reinterpret(Float32, rand_ui23_raw(r) % UInt32 & 0x007fffff | 0x3f800000) - 1 #### integers -@inline rand(r::MersenneTwister, ::Type{T}) where {T<:Union{Bool,Int8,UInt8,Int16,UInt16,Int32,UInt32}} = - rand_ui52_raw(r) % T +@inline rand(r::MersenneTwister, + ::Type{T}) where {T<:Union{Bool,Int8,UInt8,Int16,UInt16,Int32,UInt32}} = + rand_ui52_raw(r) % T function rand(r::MersenneTwister, ::Type{UInt64}) reserve(r, 2) @@ -252,7 +265,8 @@ rand(r::MersenneTwister, ::Type{Int128}) = reinterpret(Int128, rand(r, UInt128)) #### arrays of floats function rand_AbstractArray_Float64!(r::MersenneTwister, A::AbstractArray{Float64}, - n=length(A), ::Type{I}=CloseOpen) where I<:FloatInterval + n=length(A), + ::Type{I}=CloseOpen) where I<:FloatInterval # what follows is equivalent to this simple loop but more efficient: # for i=1:n # @inbounds A[i] = rand(r, I) @@ -275,10 +289,14 @@ end rand!(r::MersenneTwister, A::AbstractArray{Float64}) = rand_AbstractArray_Float64!(r, A) -fill_array!(s::DSFMT_state, A::Ptr{Float64}, n::Int, ::Type{CloseOpen}) = dsfmt_fill_array_close_open!(s, A, n) -fill_array!(s::DSFMT_state, A::Ptr{Float64}, n::Int, ::Type{Close1Open2}) = dsfmt_fill_array_close1_open2!(s, A, n) +fill_array!(s::DSFMT_state, A::Ptr{Float64}, n::Int, ::Type{CloseOpen}) = + dsfmt_fill_array_close_open!(s, A, n) + +fill_array!(s::DSFMT_state, A::Ptr{Float64}, n::Int, ::Type{Close1Open2}) = + dsfmt_fill_array_close1_open2!(s, A, n) -function rand!(r::MersenneTwister, A::Array{Float64}, n::Int=length(A), ::Type{I}=CloseOpen) where I<:FloatInterval +function rand!(r::MersenneTwister, A::Array{Float64}, n::Int=length(A), + ::Type{I}=CloseOpen) where I<:FloatInterval # depending on the alignment of A, the data written by fill_array! may have # to be left-shifted by up to 15 bytes (cf. unsafe_copy! below) for # reproducibility purposes; @@ -308,25 +326,32 @@ function rand!(r::MersenneTwister, A::Array{Float64}, n::Int=length(A), ::Type{I A end -@inline mask128(u::UInt128, ::Type{Float16}) = (u & 0x03ff03ff03ff03ff03ff03ff03ff03ff) | 0x3c003c003c003c003c003c003c003c00 -@inline mask128(u::UInt128, ::Type{Float32}) = (u & 0x007fffff007fffff007fffff007fffff) | 0x3f8000003f8000003f8000003f800000 +@inline mask128(u::UInt128, ::Type{Float16}) = + (u & 0x03ff03ff03ff03ff03ff03ff03ff03ff) | 0x3c003c003c003c003c003c003c003c00 + +@inline mask128(u::UInt128, ::Type{Float32}) = + (u & 0x007fffff007fffff007fffff007fffff) | 0x3f8000003f8000003f8000003f800000 -function rand!(r::MersenneTwister, A::Union{Array{Float16},Array{Float32}}, ::Type{Close1Open2}) +function rand!(r::MersenneTwister, A::Union{Array{Float16},Array{Float32}}, + ::Type{Close1Open2}) T = eltype(A) n = length(A) n128 = n * sizeof(T) ÷ 16 - rand!(r, unsafe_wrap(Array, convert(Ptr{Float64}, pointer(A)), 2*n128), 2*n128, Close1Open2) + rand!(r, unsafe_wrap(Array, convert(Ptr{Float64}, pointer(A)), 2*n128), + 2*n128, Close1Open2) A128 = unsafe_wrap(Array, convert(Ptr{UInt128}, pointer(A)), n128) @inbounds for i in 1:n128 u = A128[i] u ⊻= u << 26 - # at this point, the 64 low bits of u, "k" being the k-th bit of A128[i] and "+" the bit xor, are: + # at this point, the 64 low bits of u, "k" being the k-th bit of A128[i] and "+" + # the bit xor, are: # [..., 58+32,..., 53+27, 52+26, ..., 33+7, 32+6, ..., 27+1, 26, ..., 1] # the bits needing to be random are # [1:10, 17:26, 33:42, 49:58] (for Float16) # [1:23, 33:55] (for Float32) - # this is obviously satisfied on the 32 low bits side, and on the high side, the entropy comes - # from bits 33:52 of A128[i] and then from bits 27:32 (which are discarded on the low side) + # this is obviously satisfied on the 32 low bits side, and on the high side, + # the entropy comes from bits 33:52 of A128[i] and then from bits 27:32 + # (which are discarded on the low side) # this is similar for the 64 high bits of u A128[i] = mask128(u, T) end @@ -336,7 +361,8 @@ function rand!(r::MersenneTwister, A::Union{Array{Float16},Array{Float32}}, ::Ty A end -function rand!(r::MersenneTwister, A::Union{Array{Float16},Array{Float32}}, ::Type{CloseOpen}) +function rand!(r::MersenneTwister, A::Union{Array{Float16},Array{Float32}}, + ::Type{CloseOpen}) rand!(r, A, Close1Open2) I32 = one(Float32) for i in eachindex(A) @@ -423,7 +449,8 @@ end ### randjump """ - randjump(r::MersenneTwister, jumps::Integer, [jumppoly::AbstractString=dSFMT.JPOLY1e21]) -> Vector{MersenneTwister} + randjump(r::MersenneTwister, jumps::Integer, + [jumppoly::AbstractString=dSFMT.JPOLY1e21]) -> Vector{MersenneTwister} Create an array of the size `jumps` of initialized `MersenneTwister` RNG objects. The first RNG object given as a parameter and following `MersenneTwister` RNGs in the array are diff --git a/base/random/dSFMT.jl b/base/random/dSFMT.jl index a62e1b7a328d8..2061cc54f9741 100644 --- a/base/random/dSFMT.jl +++ b/base/random/dSFMT.jl @@ -15,7 +15,8 @@ const MEXP = 19937 "DSFMT internal state array size of N 128-bit integers." const N = floor(Int, ((MEXP - 128) / 104 + 1)) -"""Julia DSFMT state representation size counted in 32-bit integers. +""" +Julia DSFMT state representation size counted in 32-bit integers. Size: (DSFMT state array of Int128 + 1)*4 + Int32 index + Int32 padding """ @@ -25,8 +26,11 @@ const JN32 = (N+1)*4+1+1 mutable struct DSFMT_state val::Vector{Int32} - DSFMT_state(val::Vector{Int32} = zeros(Int32, JN32)) = - new(length(val) == JN32 ? val : throw(DomainError(length(val), string("Expected length ", JN32, '.')))) + function DSFMT_state(val::Vector{Int32} = zeros(Int32, JN32)) + length(val) == JN32 || + throw(DomainError(length(val), "Expected length: $JN32.")) + new(val) + end end copy!(dst::DSFMT_state, src::DSFMT_state) = (copy!(dst.val, src.val); dst) diff --git a/base/random/generation.jl b/base/random/generation.jl index c3f384a8221d2..bceeb6dbd116f 100644 --- a/base/random/generation.jl +++ b/base/random/generation.jl @@ -53,11 +53,11 @@ rand( dims::Dims) = rand(GLOBAL_RNG, dims) rand(r::AbstractRNG, dims::Integer...) = rand(r, Dims(dims)) rand( dims::Integer...) = rand(Dims(dims)) -rand(r::AbstractRNG, T::Type, dims::Dims) = rand!(r, Array{T}(dims)) -rand( T::Type, dims::Dims) = rand(GLOBAL_RNG, T, dims) -rand(r::AbstractRNG, T::Type, d1::Integer, dims::Integer...) = rand(r, T, tuple(Int(d1), convert(Dims, dims)...)) -rand( T::Type, d1::Integer, dims::Integer...) = rand(T, tuple(Int(d1), convert(Dims, dims)...)) -# note: the above methods would trigger an ambiguity warning if d1 was not separated out: +rand(r::AbstractRNG, T::Type, dims::Dims) = rand!(r, Array{T}(dims)) +rand( T::Type, dims::Dims) = rand(GLOBAL_RNG, T, dims) +rand(r::AbstractRNG, T::Type, d::Integer, dims::Integer...) = rand(r, T, Dims((d, dims...))) +rand( T::Type, d::Integer, dims::Integer...) = rand(T, Dims((d, dims...))) +# note: the above methods would trigger an ambiguity warning if d was not separated out: # rand(r, ()) would match both this method and rand(r, dims::Dims) # moreover, a call like rand(r, NotImplementedType()) would be an infinite loop @@ -75,10 +75,13 @@ rem_knuth(a::T, b::T) where {T<:Unsigned} = b != 0 ? a % b : a # maximum multiple of k <= 2^bits(T) decremented by one, # that is 0xFFFF...FFFF if k = typemax(T) - typemin(T) with intentional underflow # see http://stackoverflow.com/questions/29182036/integer-arithmetic-add-1-to-uint-max-and-divide-by-n-without-overflow -maxmultiple(k::T) where {T<:Unsigned} = (div(typemax(T) - k + oneunit(k), k + (k == 0))*k + k - oneunit(k))::T +maxmultiple(k::T) where {T<:Unsigned} = + (div(typemax(T) - k + oneunit(k), k + (k == 0))*k + k - oneunit(k))::T # maximum multiple of k within 1:2^32 or 1:2^64 decremented by one, depending on size -maxmultiplemix(k::UInt64) = if k >> 32 != 0; maxmultiple(k); else (div(0x0000000100000000, k + (k == 0))*k - oneunit(k))::UInt64; end +maxmultiplemix(k::UInt64) = k >> 32 != 0 ? + maxmultiple(k) : + (div(0x0000000100000000, k + (k == 0))*k - oneunit(k))::UInt64 struct RangeGeneratorInt{T<:Integer,U<:Unsigned} <: RangeGenerator a::T # first element of the range @@ -87,26 +90,26 @@ struct RangeGeneratorInt{T<:Integer,U<:Unsigned} <: RangeGenerator end # generators with 32, 128 bits entropy -RangeGeneratorInt(a::T, k::U) where {T,U<:Union{UInt32,UInt128}} = RangeGeneratorInt{T,U}(a, k, maxmultiple(k)) +RangeGeneratorInt(a::T, k::U) where {T,U<:Union{UInt32,UInt128}} = + RangeGeneratorInt{T,U}(a, k, maxmultiple(k)) + # mixed 32/64 bits entropy generator -RangeGeneratorInt(a::T, k::UInt64) where {T} = RangeGeneratorInt{T,UInt64}(a, k, maxmultiplemix(k)) +RangeGeneratorInt(a::T, k::UInt64) where {T} = + RangeGeneratorInt{T,UInt64}(a, k, maxmultiplemix(k)) function RangeGenerator(r::UnitRange{T}) where T<:Unsigned - if isempty(r) - throw(ArgumentError("range must be non-empty")) - end + isempty(r) && throw(ArgumentError("range must be non-empty")) RangeGeneratorInt(first(r), last(r) - first(r) + oneunit(T)) end for (T, U) in [(UInt8, UInt32), (UInt16, UInt32), - (Int8, UInt32), (Int16, UInt32), (Int32, UInt32), (Int64, UInt64), (Int128, UInt128), - (Bool, UInt32)] + (Int8, UInt32), (Int16, UInt32), (Int32, UInt32), + (Int64, UInt64), (Int128, UInt128), (Bool, UInt32)] @eval RangeGenerator(r::UnitRange{$T}) = begin - if isempty(r) - throw(ArgumentError("range must be non-empty")) - end - RangeGeneratorInt(first(r), convert($U, unsigned(last(r) - first(r)) + one($U))) # overflow ok + isempty(r) && throw(ArgumentError("range must be non-empty")) + # overflow ok: + RangeGeneratorInt(first(r), convert($U, unsigned(last(r) - first(r)) + one($U))) end end @@ -152,7 +155,7 @@ function rand(rng::AbstractRNG, g::RangeGeneratorInt{T,UInt64}) where T<:Union{U return reinterpret(T, reinterpret(UInt64, g.a) + rem_knuth(x, g.k)) end -function rand(rng::AbstractRNG, g::RangeGeneratorInt{T,U}) where U<:Unsigned where T<:Integer +function rand(rng::AbstractRNG, g::RangeGeneratorInt{T,U}) where {T<:Integer,U<:Unsigned} x = rand(rng, U) while x > g.u x = rand(rng, U) @@ -188,9 +191,12 @@ end ### random values from UnitRange -rand(rng::AbstractRNG, r::UnitRange{<:Union{Signed,Unsigned,BigInt,Bool}}) = rand(rng, RangeGenerator(r)) +rand(rng::AbstractRNG, r::UnitRange{<:Union{Signed,Unsigned,BigInt,Bool}}) = + rand(rng, RangeGenerator(r)) -rand!(rng::AbstractRNG, A::AbstractArray, r::UnitRange{<:Union{Signed,Unsigned,BigInt,Bool,Char}}) = rand!(rng, A, RangeGenerator(r)) +rand!(rng::AbstractRNG, A::AbstractArray, + r::UnitRange{<:Union{Signed,Unsigned,BigInt,Bool,Char}}) = + rand!(rng, A, RangeGenerator(r)) ## random values from AbstractArray @@ -210,10 +216,11 @@ end rand!(A::AbstractArray, r::AbstractArray) = rand!(GLOBAL_RNG, A, r) -rand(rng::AbstractRNG, r::AbstractArray{T}, dims::Dims) where {T} = rand!(rng, Array{T}(dims), r) -rand( r::AbstractArray, dims::Dims) = rand(GLOBAL_RNG, r, dims) -rand(rng::AbstractRNG, r::AbstractArray, dims::Integer...) = rand(rng, r, convert(Dims, dims)) -rand( r::AbstractArray, dims::Integer...) = rand(GLOBAL_RNG, r, convert(Dims, dims)) +rand(rng::AbstractRNG, r::AbstractArray{T}, dims::Dims) where {T} = + rand!(rng, Array{T}(dims), r) +rand( r::AbstractArray, dims::Dims) = rand(GLOBAL_RNG, r, dims) +rand(rng::AbstractRNG, r::AbstractArray, dims::Integer...) = rand(rng, r, Dims(dims)) +rand( r::AbstractArray, dims::Integer...) = rand(GLOBAL_RNG, r, Dims(dims)) ## random values from Dict, Set, IntSet @@ -262,14 +269,18 @@ function rand!(r::AbstractRNG, A::AbstractArray, s::Union{Dict,Set,IntSet}) end # avoid linear complexity for repeated calls with generic containers -rand!(r::AbstractRNG, A::AbstractArray, s::Union{Associative,AbstractSet}) = rand!(r, A, collect(s)) +rand!(r::AbstractRNG, A::AbstractArray, s::Union{Associative,AbstractSet}) = + rand!(r, A, collect(s)) rand!(A::AbstractArray, s::Union{Associative,AbstractSet}) = rand!(GLOBAL_RNG, A, s) -rand(r::AbstractRNG, s::Associative{K,V}, dims::Dims) where {K,V} = rand!(r, Array{Pair{K,V}}(dims), s) +rand(r::AbstractRNG, s::Associative{K,V}, dims::Dims) where {K,V} = + rand!(r, Array{Pair{K,V}}(dims), s) + rand(r::AbstractRNG, s::AbstractSet{T}, dims::Dims) where {T} = rand!(r, Array{T}(dims), s) -rand(r::AbstractRNG, s::Union{Associative,AbstractSet}, dims::Integer...) = rand(r, s, convert(Dims, dims)) -rand(s::Union{Associative,AbstractSet}, dims::Integer...) = rand(GLOBAL_RNG, s, convert(Dims, dims)) +rand(r::AbstractRNG, s::Union{Associative,AbstractSet}, dims::Integer...) = + rand(r, s, Dims(dims)) +rand(s::Union{Associative,AbstractSet}, dims::Integer...) = rand(GLOBAL_RNG, s, Dims(dims)) rand(s::Union{Associative,AbstractSet}, dims::Dims) = rand(GLOBAL_RNG, s, dims) @@ -296,7 +307,11 @@ rand(s::AbstractString) = rand(GLOBAL_RNG, s) # (except maybe for very small arrays) rand!(rng::AbstractRNG, A::AbstractArray, str::AbstractString) = rand!(rng, A, collect(str)) rand!(A::AbstractArray, str::AbstractString) = rand!(GLOBAL_RNG, A, str) -rand(rng::AbstractRNG, str::AbstractString, dims::Dims) = rand!(rng, Array{eltype(str)}(dims), str) -rand(rng::AbstractRNG, str::AbstractString, d1::Integer, dims::Integer...) = rand(rng, str, convert(Dims, tuple(d1, dims...))) +rand(rng::AbstractRNG, str::AbstractString, dims::Dims) = + rand!(rng, Array{eltype(str)}(dims), str) + +rand(rng::AbstractRNG, str::AbstractString, d::Integer, dims::Integer...) = + rand(rng, str, Dims((d, dims...))) + rand(str::AbstractString, dims::Dims) = rand(GLOBAL_RNG, str, dims) -rand(str::AbstractString, d1::Integer, dims::Integer...) = rand(GLOBAL_RNG, str, d1, dims...) +rand(str::AbstractString, d::Integer, dims::Integer...) = rand(GLOBAL_RNG, str, d, dims...) diff --git a/base/random/misc.jl b/base/random/misc.jl index d18fb2fdf7795..0d5f1123a41fc 100644 --- a/base/random/misc.jl +++ b/base/random/misc.jl @@ -127,7 +127,8 @@ Like [`randsubseq`](@ref), but the results are stored in `S` """ randsubseq!(S::AbstractArray, A::AbstractArray, p::Real) = randsubseq!(GLOBAL_RNG, S, A, p) -randsubseq(r::AbstractRNG, A::AbstractArray{T}, p::Real) where {T} = randsubseq!(r, T[], A, p) +randsubseq(r::AbstractRNG, A::AbstractArray{T}, p::Real) where {T} = + randsubseq!(r, T[], A, p) """ randsubseq(A, p) -> Vector @@ -234,8 +235,8 @@ shuffle(a::AbstractArray) = shuffle(GLOBAL_RNG, a) """ randperm([rng=GLOBAL_RNG,] n::Integer) -Construct a random permutation of length `n`. The optional `rng` argument specifies a random -number generator (see [Random Numbers](@ref)). +Construct a random permutation of length `n`. The optional `rng` +argument specifies a random number generator (see [Random Numbers](@ref)). To randomly permute an arbitrary vector, see [`shuffle`](@ref) or [`shuffle!`](@ref). @@ -430,9 +431,7 @@ julia> Base.Random.uuid_version(Base.Random.uuid4(rng)) 4 ``` """ -function uuid_version(u::UUID) - Int((u.value >> 76) & 0xf) -end +uuid_version(u::UUID) = Int((u.value >> 76) & 0xf) Base.convert(::Type{UInt128}, u::UUID) = u.value diff --git a/base/random/normal.jl b/base/random/normal.jl index 6a01c3b9ec10f..7e9f76362b812 100644 --- a/base/random/normal.jl +++ b/base/random/normal.jl @@ -12,7 +12,8 @@ """ randn([rng=GLOBAL_RNG], [T=Float64], [dims...]) -Generate a normally-distributed random number of type `T` with mean 0 and standard deviation 1. +Generate a normally-distributed random number of type `T` +with mean 0 and standard deviation 1. Optionally generate an array of normally-distributed random numbers. The `Base` module currently provides an implementation for the types [`Float16`](@ref), [`Float32`](@ref), and [`Float64`](@ref) (the default), and their @@ -49,7 +50,8 @@ function randn_unlikely(rng, idx, rabs, x) while true xx = -ziggurat_nor_inv_r*log(rand(rng)) yy = -log(rand(rng)) - yy+yy > xx*xx && return (rabs >> 8) % Bool ? -ziggurat_nor_r-xx : ziggurat_nor_r+xx + yy+yy > xx*xx && + return (rabs >> 8) % Bool ? -ziggurat_nor_r-xx : ziggurat_nor_r+xx end elseif (fi[idx] - fi[idx+1])*rand(rng) + fi[idx+1] < exp(-0.5*x*x) return x # return from the triangular area @@ -71,7 +73,8 @@ randn(rng::AbstractRNG, ::Type{Complex{T}}) where {T<:AbstractFloat} = """ randexp([rng=GLOBAL_RNG], [T=Float64], [dims...]) -Generate a random number of type `T` according to the exponential distribution with scale 1. +Generate a random number of type `T` according to the +exponential distribution with scale 1. Optionally generate an array of such random numbers. The `Base` module currently provides an implementation for the types [`Float16`](@ref), [`Float32`](@ref), and [`Float64`](@ref) (the default). @@ -137,7 +140,8 @@ function randn! end """ randexp!([rng=GLOBAL_RNG], A::AbstractArray) -> A -Fill the array `A` with random numbers following the exponential distribution (with scale 1). +Fill the array `A` with random numbers following the exponential distribution +(with scale 1). # Examples ```jldoctest @@ -174,7 +178,8 @@ for randfun in [:randn, :randexp] # generating arrays $randfun(rng::AbstractRNG, ::Type{T}, dims::Dims ) where {T} = $randfun!(rng, Array{T}(dims)) - # Note that this method explicitly does not define $randfun(rng, T), in order to prevent an infinite recursion. + # Note that this method explicitly does not define $randfun(rng, T), + # in order to prevent an infinite recursion. $randfun(rng::AbstractRNG, ::Type{T}, dim1::Integer, dims::Integer...) where {T} = $randfun!(rng, Array{T}(dim1, dims...)) $randfun( ::Type{T}, dims::Dims ) where {T} = $randfun(GLOBAL_RNG, T, dims) $randfun( ::Type{T}, dims::Integer... ) where {T} = $randfun(GLOBAL_RNG, T, dims...) diff --git a/base/random/random.jl b/base/random/random.jl index 225c649eb9484..ad63869c40c77 100644 --- a/base/random/random.jl +++ b/base/random/random.jl @@ -46,7 +46,8 @@ include("misc.jl") """ rand([rng=GLOBAL_RNG], [S], [dims...]) -Pick a random element or array of random elements from the set of values specified by `S`; `S` can be +Pick a random element or array of random elements from the set of values specified by `S`; +`S` can be * an indexable collection (for example `1:n` or `['x','y','z']`), * an `Associative` or `AbstractSet` object, From af44fe5598d7dbb17d9adc4e28fb6ffbad1631a1 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Mon, 14 Aug 2017 16:23:20 +0200 Subject: [PATCH 060/324] introduce rand_generic to factor out functionalities --- base/random/RNGs.jl | 18 ++---------------- base/random/generation.jl | 11 +++++++++++ base/random/normal.jl | 3 +-- base/random/random.jl | 2 ++ 4 files changed, 16 insertions(+), 18 deletions(-) diff --git a/base/random/RNGs.jl b/base/random/RNGs.jl index c60317cff9f94..ff33d5c3f46e7 100644 --- a/base/random/RNGs.jl +++ b/base/random/RNGs.jl @@ -50,14 +50,7 @@ rand(rng::RandomDevice, ::Type{Close1Open2}) = rand(rng::RandomDevice, ::Type{CloseOpen}) = rand(rng, Close1Open2) - 1.0 -@inline rand(r::RandomDevice, ::Type{Float64}) = rand(r, CloseOpen) - -rand(r::RandomDevice, ::Type{Float16}) = - Float16(reinterpret(Float32, - (rand_ui10_raw(r) % UInt32 << 13) & 0x007fe000 | 0x3f800000) - 1) - -rand(r::RandomDevice, ::Type{Float32}) = - reinterpret(Float32, rand_ui23_raw(r) % UInt32 & 0x007fffff | 0x3f800000) - 1 +@inline rand(r::RandomDevice, T::BitFloatType) = rand_generic(r, T) ## MersenneTwister @@ -232,14 +225,7 @@ rand_ui23_raw(r::MersenneTwister) = rand_ui52_raw(r) @inline rand(r::MersenneTwister, ::Type{I}) where {I<:FloatInterval} = (reserve_1(r); rand_inbounds(r, I)) -@inline rand(r::MersenneTwister, ::Type{Float64}) = rand(r, CloseOpen) - -rand(r::MersenneTwister, ::Type{Float16}) = - Float16(reinterpret(Float32, - (rand_ui10_raw(r) % UInt32 << 13) & 0x007fe000 | 0x3f800000) - 1) - -rand(r::MersenneTwister, ::Type{Float32}) = - reinterpret(Float32, rand_ui23_raw(r) % UInt32 & 0x007fffff | 0x3f800000) - 1 +@inline rand(r::MersenneTwister, T::BitFloatType) = rand_generic(r, T) #### integers diff --git a/base/random/generation.jl b/base/random/generation.jl index bceeb6dbd116f..14cde41b37b95 100644 --- a/base/random/generation.jl +++ b/base/random/generation.jl @@ -12,6 +12,17 @@ @inline rand(r::AbstractRNG=GLOBAL_RNG) = rand(r, CloseOpen) +# generic random generation function which can be used by RNG implementors +# it is not defined as a fallback rand method as this could create ambiguities +@inline rand_generic(r::AbstractRNG, ::Type{Float64}) = rand(r, CloseOpen) + +rand_generic(r::AbstractRNG, ::Type{Float16}) = + Float16(reinterpret(Float32, + (rand_ui10_raw(r) % UInt32 << 13) & 0x007fe000 | 0x3f800000) - 1) + +rand_generic(r::AbstractRNG, ::Type{Float32}) = + reinterpret(Float32, rand_ui23_raw(r) % UInt32 & 0x007fffff | 0x3f800000) - 1 + ### random integers rand_ui10_raw(r::AbstractRNG) = rand(r, UInt16) diff --git a/base/random/normal.jl b/base/random/normal.jl index 7e9f76362b812..c78f6e4976ee7 100644 --- a/base/random/normal.jl +++ b/base/random/normal.jl @@ -162,8 +162,7 @@ for randfun in [:randn, :randexp] randfun! = Symbol(randfun, :!) @eval begin # scalars - $randfun(rng::AbstractRNG, T::Union{Type{Float16},Type{Float32},Type{Float64}}) = - convert(T, $randfun(rng)) + $randfun(rng::AbstractRNG, T::BitFloatType) = convert(T, $randfun(rng)) $randfun(::Type{T}) where {T} = $randfun(GLOBAL_RNG, T) # filling arrays diff --git a/base/random/random.jl b/base/random/random.jl index ad63869c40c77..7d88cbd5cb388 100644 --- a/base/random/random.jl +++ b/base/random/random.jl @@ -26,6 +26,8 @@ abstract type FloatInterval end mutable struct CloseOpen <: FloatInterval end mutable struct Close1Open2 <: FloatInterval end +const BitFloatType = Union{Type{Float16},Type{Float32},Type{Float64}} + function __init__() try srand() From f987dd81e9aadacfb7048a3503eca04e013eecc3 Mon Sep 17 00:00:00 2001 From: Mus M Date: Thu, 17 Aug 2017 09:49:04 -0400 Subject: [PATCH 061/324] Make default_precision in mpfr.jl a ref --- base/mpfr.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/base/mpfr.jl b/base/mpfr.jl index 29644bd2e7bf4..d60cba416b1d6 100644 --- a/base/mpfr.jl +++ b/base/mpfr.jl @@ -42,7 +42,7 @@ function __init__() end const ROUNDING_MODE = Ref{Cint}(0) -const DEFAULT_PRECISION = [256] +const DEFAULT_PRECISION = Ref(256) # Basic type and initialization definitions @@ -740,7 +740,7 @@ end Get the precision (in bits) currently used for [`BigFloat`](@ref) arithmetic. """ -precision(::Type{BigFloat}) = DEFAULT_PRECISION[end] # precision of the type BigFloat itself +precision(::Type{BigFloat}) = DEFAULT_PRECISION[] # precision of the type BigFloat itself """ setprecision([T=BigFloat,] precision::Int) @@ -751,7 +751,7 @@ function setprecision(::Type{BigFloat}, precision::Int) if precision < 2 throw(DomainError(precision, "`precision` cannot be less than 2.")) end - DEFAULT_PRECISION[end] = precision + DEFAULT_PRECISION[] = precision end setprecision(precision::Int) = setprecision(BigFloat, precision) From 6898379b3dd8ec0486f2c700967a5560bec6ebff Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 17 Aug 2017 11:30:02 -0400 Subject: [PATCH 062/324] fix macroexpansion scope miscalculation (#23247) previously, we were treating hygienic-scope as also introducing a new scope-block that was wrong (and inconsistent with previous behavior) also, we were failing to set the outermost flag when entering a hygienic-scope block, further compounding the above error fix #23239 --- src/macroexpand.scm | 38 ++++++++++++++++++++++++++++++-------- test/core.jl | 32 +++++++++++++++++++++++++++++++- test/parse.jl | 26 ++++++++++++++++++-------- 3 files changed, 79 insertions(+), 17 deletions(-) diff --git a/src/macroexpand.scm b/src/macroexpand.scm index b850366b8ca52..8161be6fff2ae 100644 --- a/src/macroexpand.scm +++ b/src/macroexpand.scm @@ -377,7 +377,7 @@ (let ((parent-scope (cons (list env m) parent-scope)) (body (cadr e)) (m (caddr e))) - (resolve-expansion-vars-with-new-env body env m parent-scope inarg))) + (resolve-expansion-vars-with-new-env body env m parent-scope inarg #t))) ;; todo: trycatch (else @@ -407,10 +407,30 @@ (and (eq? (car e) '=) (length= e 3) (eventually-call? (cadr e)))))) +;; count hygienic / escape pairs +;; and fold together a list resulting from applying the function to +;; any block at the same hygienic scope +(define (resume-on-escape lam e nblocks) + (if (or (not (pair? e)) (quoted? e)) + '() + (cond ((memq (car e) '(lambda module toplevel)) + '()) + ((eq? (car e) 'hygienic-scope) + (resume-on-escape lam (cadr e) (+ nblocks 1))) + ((eq? (car e) 'escape) + (if (= nblocks 0) + (lam (cadr e)) + (resume-on-escape lam (cadr e) (- nblocks 1)))) + (else + (foldl (lambda (a l) (append! l (resume-on-escape lam a nblocks))) + '() + (cdr e)))))) + (define (find-declared-vars-in-expansion e decl (outer #t)) (cond ((or (not (pair? e)) (quoted? e)) '()) ((eq? (car e) 'escape) '()) - ((eq? (car e) 'hygienic-scope) '()) + ((eq? (car e) 'hygienic-scope) + (resume-on-escape (lambda (e) (find-declared-vars-in-expansion e decl outer)) (cadr e) 0)) ((eq? (car e) decl) (map decl-var* (cdr e))) ((and (not outer) (function-def? e)) '()) (else @@ -421,7 +441,8 @@ (define (find-assigned-vars-in-expansion e (outer #t)) (cond ((or (not (pair? e)) (quoted? e)) '()) ((eq? (car e) 'escape) '()) - ((eq? (car e) 'hygienic-scope) '()) + ((eq? (car e) 'hygienic-scope) + (resume-on-escape (lambda (e) (find-assigned-vars-in-expansion e outer)) (cadr e) 0)) ((and (not outer) (function-def? e)) ;; pick up only function name (let ((fname (cond ((eq? (car e) '=) (decl-var* (cadr e))) @@ -498,11 +519,12 @@ (error (string "macro \"" (cadr e) "\" not defined"))) (if (and (pair? form) (eq? (car form) 'error)) (error (cadr form))) - (let ((form (car form)) ;; form is the expression returned from expand-macros - (modu (cdr form))) ;; modu is the macro's def module - `(hygienic-scope - ,(julia-expand-macros- (cons modu m) form (- max-depth 1)) - ,modu)))) + (let* ((modu (cdr form)) ;; modu is the macro's def module + (form (car form)) ;; form is the expression returned from expand-macros + (form (julia-expand-macros- (cons modu m) form (- max-depth 1)))) + (if (and (pair? form) (eq? (car form) 'escape)) + (cadr form) ; immediately fold away (hygienic-scope (escape ...)) + `(hygienic-scope ,form ,modu))))) ((eq? (car e) 'module) e) ((eq? (car e) 'escape) (let ((m (if (null? m) m (cdr m)))) diff --git a/test/core.jl b/test/core.jl index 81704008c6b7f..e552879f5cfd3 100644 --- a/test/core.jl +++ b/test/core.jl @@ -4989,7 +4989,7 @@ let a_foo = Foo22256(Bar22256{true}(2)) @test a_foo.bar.inner == 3 end -# macro hygiene scope (#22307) +# macro hygiene scope (#22307, #23239) macro a22307() return esc(:a22307) end @@ -5003,6 +5003,36 @@ end a22307 = 2 @test c22307() == 2 +macro identity23239b(x) + return esc(x) +end +macro identity23239c(x) + return quote + $(esc(x)) + end +end +macro assign23239d(x, v) + return esc(:($x = $v)) +end +macro assign23239e(x, v) + return quote + $(esc(:($x = $v))) + end +end +macro aa23239() + return quote + a = 1 + @identity23239b b = 2 + @identity23239c c = 3 + @assign23239d d 4 + @assign23239e e 5 + (a, b, c, d, e) + end +end +f23239() = @aa23239() +@test @inferred(f23239()) === (1, 2, 3, 4, 5) + + # issue #22026 module M22026 diff --git a/test/parse.jl b/test/parse.jl index 0711dce02b192..2f57e98593752 100644 --- a/test/parse.jl +++ b/test/parse.jl @@ -771,19 +771,29 @@ macro iter() end end let ex = expand(M16096, :(@iter)) - @test isa(ex, Expr) && ex.head === :body + @test isa(ex, Expr) && ex.head === :thunk end let ex = expand(Main, :($M16096.@iter)) - @test isa(ex, Expr) && ex.head === :body + @test isa(ex, Expr) && ex.head === :thunk end -let ex = expand(@__MODULE__, :(@M16096.iter)) - @test isa(ex, Expr) && ex.head === :body +let thismodule = @__MODULE__, + ex = expand(thismodule, :(@M16096.iter)) + @test isa(ex, Expr) && ex.head === :thunk @test !isdefined(M16096, :foo16096) - @test eval(@__MODULE__, ex) === nothing + local_foo16096 = eval(@__MODULE__, ex) + @test local_foo16096(2.0) == 1 @test !@isdefined foo16096 - @test isdefined(M16096, :foo16096) + @test !@isdefined it + @test !isdefined(M16096, :foo16096) + @test !isdefined(M16096, :it) + @test typeof(local_foo16096).name.module === thismodule + @test typeof(local_foo16096).name.mt.module === thismodule + @test getfield(thismodule, typeof(local_foo16096).name.mt.name) === local_foo16096 + @test getfield(thismodule, typeof(local_foo16096).name.name) === typeof(local_foo16096) + @test !isdefined(M16096, typeof(local_foo16096).name.mt.name) + @test !isdefined(M16096, typeof(local_foo16096).name.name) end -@test M16096.foo16096(2.0) == 1 + macro f16096() quote g16096($(esc(:x))) = 2x @@ -794,7 +804,7 @@ let g = @f16096 end macro f16096_2() quote - g16096_2(;$(esc(:x))=2) = 2x + g16096_2(; $(esc(:x))=2) = 2x end end let g = @f16096_2 From a945af3aaef528da7813174c17d705b6383f7ea3 Mon Sep 17 00:00:00 2001 From: Jarrett Revels Date: Thu, 17 Aug 2017 12:48:41 -0400 Subject: [PATCH 063/324] fix WarnType color-based reflection tests run from the REPL (#23252) --- test/reflection.jl | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/test/reflection.jl b/test/reflection.jl index 3ae431d744e7c..b77e7d4e778a8 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -67,7 +67,7 @@ end pos_stable(x) = x > 0 ? x : zero(x) pos_unstable(x) = x > 0 ? x : 0 -tag = Base.have_color ? Base.error_color() : "UNION" +tag = Base.have_color ? Base.text_colors[Base.error_color()] : "UNION" @test warntype_hastag(pos_unstable, Tuple{Float64}, tag) @test !warntype_hastag(pos_stable, Tuple{Float64}, tag) @@ -80,13 +80,13 @@ end Base.getindex(A::Stable, i) = A.A[i] Base.getindex(A::Unstable, i) = A.A[i] -tag = Base.have_color ? Base.error_color() : "ARRAY{FLOAT64,N}" +tag = Base.have_color ? Base.text_colors[Base.error_color()] : "ARRAY{FLOAT64,N}" @test warntype_hastag(getindex, Tuple{Unstable{Float64},Int}, tag) @test !warntype_hastag(getindex, Tuple{Stable{Float64,2},Int}, tag) @test warntype_hastag(getindex, Tuple{Stable{Float64},Int}, tag) # Make sure emphasis is not used for other functions -tag = Base.have_color ? Base.error_color() : "ANY" +tag = Base.have_color ? Base.text_colors[Base.error_color()] : "ANY" iob = IOBuffer() show(iob, expand(Main, :(x -> x^2))) str = String(take!(iob)) @@ -100,7 +100,8 @@ import Core.Intrinsics: sqrt_llvm, bitcast sqrt15819(x::Float64) = bitcast(Float64, sqrt_llvm(x)) # Use fully qualified name sqrt15819(x::Float32) = bitcast(Float32, Core.Intrinsics.sqrt_llvm(x)) -end +end # module ImportIntrinsics15819 + foo11122(x) = @fastmath x - 1.0 # issue #11122, #13568 and #15819 @@ -118,7 +119,7 @@ foo11122(x) = @fastmath x - 1.0 @test !warntype_hastag(ImportIntrinsics15819.sqrt15819, Tuple{Float64}, tag) @test !warntype_hastag(ImportIntrinsics15819.sqrt15819, Tuple{Float32}, tag) -end +end # module WarnType # isbits From 05f5452d9ad14fa5d03b0d83f158fcf456fb4590 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Thu, 17 Aug 2017 17:28:47 -0400 Subject: [PATCH 064/324] fix #17886, `filter[!]` on dicts should pass 1 argument to the function --- NEWS.md | 3 +++ base/associative.jl | 57 ++++++++++++++++++++++++++++++++++--------- base/deprecated.jl | 3 +++ base/dict.jl | 18 +++++++++----- base/inference.jl | 2 +- base/pkg/read.jl | 2 +- base/repl/LineEdit.jl | 2 +- base/weakkeydict.jl | 9 +------ test/dict.jl | 4 +-- 9 files changed, 70 insertions(+), 30 deletions(-) diff --git a/NEWS.md b/NEWS.md index 4f001ae743fec..ea7294579758d 100644 --- a/NEWS.md +++ b/NEWS.md @@ -312,6 +312,9 @@ Deprecated or removed * `Base.cpad` has been removed; use an appropriate combination of `rpad` and `lpad` instead ([#23187]). + * `filter` and `filter!` on dictionaries now pass a single `key=>value` pair to the + argument function, instead of two arguments ([#17886]). + Command-line option changes --------------------------- diff --git a/base/associative.jl b/base/associative.jl index ba9c7e3ebc10b..44ecd6faed3d5 100644 --- a/base/associative.jl +++ b/base/associative.jl @@ -317,7 +317,7 @@ end filter!(f, d::Associative) Update `d`, removing elements for which `f` is `false`. -The function `f` is passed two arguments (key and value). +The function `f` is passed `key=>value` pairs. # Example ```jldoctest @@ -327,7 +327,7 @@ Dict{Int64,String} with 3 entries: 3 => "c" 1 => "a" -julia> filter!((x,y)->isodd(x), d) +julia> filter!(p->isodd(p.first), d) Dict{Int64,String} with 2 entries: 3 => "c" 1 => "a" @@ -335,10 +335,14 @@ Dict{Int64,String} with 2 entries: """ function filter!(f, d::Associative) badkeys = Vector{keytype(d)}(0) - for (k,v) in d - # don't delete!(d, k) here, since associative types - # may not support mutation during iteration - f(k,v) || push!(badkeys, k) + try + for (k,v) in d + # don't delete!(d, k) here, since associative types + # may not support mutation during iteration + f(k => v) || push!(badkeys, k) + end + catch e + return filter!_dict_deprecation(e, f, d) end for k in badkeys delete!(d, k) @@ -346,11 +350,29 @@ function filter!(f, d::Associative) return d end +function filter!_dict_deprecation(e, f, d::Associative) + if isa(e, MethodError) && e.f === f + depwarn("In `filter!(f, dict)`, `f` is now passed a single pair instead of two arguments.", :filter!) + badkeys = Vector{keytype(d)}(0) + for (k,v) in d + # don't delete!(d, k) here, since associative types + # may not support mutation during iteration + f(k, v) || push!(badkeys, k) + end + for k in badkeys + delete!(d, k) + end + else + rethrow(e) + end + return d +end + """ filter(f, d::Associative) Return a copy of `d`, removing elements for which `f` is `false`. -The function `f` is passed two arguments (key and value). +The function `f` is passed `key=>value` pairs. # Examples ```jldoctest @@ -359,7 +381,7 @@ Dict{Int64,String} with 2 entries: 2 => "b" 1 => "a" -julia> filter((x,y)->isodd(x), d) +julia> filter(p->isodd(p.first), d) Dict{Int64,String} with 1 entry: 1 => "a" ``` @@ -367,9 +389,22 @@ Dict{Int64,String} with 1 entry: function filter(f, d::Associative) # don't just do filter!(f, copy(d)): avoid making a whole copy of d df = similar(d) - for (k,v) in d - if f(k,v) - df[k] = v + try + for (k, v) in d + if f(k => v) + df[k] = v + end + end + catch e + if isa(e, MethodError) && e.f === f + depwarn("In `filter(f, dict)`, `f` is now passed a single pair instead of two arguments.", :filter) + for (k, v) in d + if f(k, v) + df[k] = v + end + end + else + rethrow(e) end end return df diff --git a/base/deprecated.jl b/base/deprecated.jl index aff647f193df4..79375cc913450 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1687,6 +1687,9 @@ end # issue #5148, PR #23259 # warning for `const` on locals should be changed to an error in julia-syntax.scm +# issue #17886 +# deprecations for filter[!] with 2-arg functions are in associative.jl + # END 0.7 deprecations # BEGIN 1.0 deprecations diff --git a/base/dict.jl b/base/dict.jl index 4dad63bed4d51..10c326b1358a1 100644 --- a/base/dict.jl +++ b/base/dict.jl @@ -721,17 +721,23 @@ length(t::Dict) = t.count next(v::KeyIterator{<:Dict}, i) = (v.dict.keys[i], skip_deleted(v.dict,i+1)) next(v::ValueIterator{<:Dict}, i) = (v.dict.vals[i], skip_deleted(v.dict,i+1)) -# For these Associative types, it is safe to implement filter! -# by deleting keys during iteration. -function filter!(f, d::Union{ObjectIdDict,Dict}) - for (k,v) in d - if !f(k,v) - delete!(d,k) +function filter_in_one_pass!(f, d::Associative) + try + for (k, v) in d + if !f(k => v) + delete!(d, k) + end end + catch e + return filter!_dict_deprecation(e, f, d) end return d end +# For these Associative types, it is safe to implement filter! +# by deleting keys during iteration. +filter!(f, d::Union{ObjectIdDict,Dict}) = filter_in_one_pass!(f, d) + struct ImmutableDict{K,V} <: Associative{K,V} parent::ImmutableDict{K,V} key::K diff --git a/base/inference.jl b/base/inference.jl index 3d37c611c6aa6..0bfc01993cc2c 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -5294,7 +5294,7 @@ function find_sa_vars(src::CodeInfo, nargs::Int) end end end - filter!((v, _) -> !haskey(av2, v), av) + filter!(p -> !haskey(av2, p.first), av) return av end diff --git a/base/pkg/read.jl b/base/pkg/read.jl index cd1f4461d1259..23945d4330210 100644 --- a/base/pkg/read.jl +++ b/base/pkg/read.jl @@ -134,7 +134,7 @@ function installed_version(pkg::AbstractString, prepo::LibGit2.GitRepo, avail::D end isempty(head) && return typemin(VersionNumber) - vers = collect(keys(filter((ver,info)->info.sha1==head, avail))) + vers = collect(keys(filter(#=ver,info=#p->p[2].sha1==head, avail))) !isempty(vers) && return maximum(vers) cache = Cache.path(pkg) diff --git a/base/repl/LineEdit.jl b/base/repl/LineEdit.jl index 7a355e80da9f2..2ee272e43a6aa 100644 --- a/base/repl/LineEdit.jl +++ b/base/repl/LineEdit.jl @@ -895,7 +895,7 @@ end # source is the keymap specified by the user (with normalized keys) function keymap_merge(target,source) ret = copy(target) - direct_keys = filter((k,v) -> isa(v, Union{Function, KeyAlias, Void}), source) + direct_keys = filter(p -> isa(p.second, Union{Function, KeyAlias, Void}), source) # first direct entries for key in keys(direct_keys) add_nested_key!(ret, key, source[key]; override = true) diff --git a/base/weakkeydict.jl b/base/weakkeydict.jl index 17712ebc2f9f6..1ff64f5b172b1 100644 --- a/base/weakkeydict.jl +++ b/base/weakkeydict.jl @@ -137,11 +137,4 @@ function next(t::WeakKeyDict{K,V}, i) where V where K return (kv, (i, gc_token)) end -function filter!(f, d::WeakKeyDict) - for (k, v) in d - if !f(k, v) - delete!(d, k) - end - end - return d -end +filter!(f, d::WeakKeyDict) = filter_in_one_pass!(f, d) diff --git a/test/dict.jl b/test/dict.jl index 2f0a43ce9afd6..3954b342ed05f 100644 --- a/test/dict.jl +++ b/test/dict.jl @@ -511,7 +511,7 @@ let d = ImmutableDict{String, String}(), end # filtering -let d = Dict(zip(1:1000,1:1000)), f = (k,v) -> iseven(k) +let d = Dict(zip(1:1000,1:1000)), f = p -> iseven(p.first) @test filter(f, d) == filter!(f, copy(d)) == invoke(filter!, Tuple{Function,Associative}, f, copy(d)) == Dict(zip(2:2:1000, 2:2:1000)) @@ -636,7 +636,7 @@ Dict(1 => rand(2,3), 'c' => "asdf") # just make sure this does not trigger a dep @test 4 ∉ values(wkd) @test length(wkd) == 2 @test !isempty(wkd) - wkd = filter!( (k,v) -> k != B, wkd) + wkd = filter!( p -> p.first != B, wkd) @test B ∉ keys(wkd) @test 3 ∉ values(wkd) @test length(wkd) == 1 From 88a553a3f8fe9749a656242a191d4f589c428106 Mon Sep 17 00:00:00 2001 From: Andreas Noack Date: Fri, 18 Aug 2017 16:28:42 -0400 Subject: [PATCH 065/324] Fix promotion in cov and mean to handle integer and rational matrices (#23285) Fixes #8080 --- base/statistics.jl | 18 ++++++++++++------ test/statistics.jl | 8 ++++++++ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/base/statistics.jl b/base/statistics.jl index e0e5a3ea2da15..e6d5748423782 100644 --- a/base/statistics.jl +++ b/base/statistics.jl @@ -59,7 +59,7 @@ julia> mean!([1. 1.], v) """ function mean!(R::AbstractArray, A::AbstractArray) sum!(R, A; init=true) - scale!(R, _length(R) / _length(A)) + scale!(R, _length(R) // max(1, _length(A))) return R end @@ -175,7 +175,7 @@ function varm!(R::AbstractArray{S}, A::AbstractArray, m::AbstractArray; correcte fill!(R, convert(S, NaN)) else rn = div(_length(A), _length(R)) - Int(corrected) - scale!(centralize_sumabs2!(R, A, m), one(S)/rn) + scale!(centralize_sumabs2!(R, A, m), 1//rn) end return R end @@ -335,12 +335,18 @@ unscaled_covzm(x::AbstractMatrix, y::AbstractMatrix, vardim::Int) = # covzm (with centered data) covzm(x::AbstractVector; corrected::Bool=true) = unscaled_covzm(x) / (_length(x) - Int(corrected)) -covzm(x::AbstractMatrix, vardim::Int=1; corrected::Bool=true) = - scale!(unscaled_covzm(x, vardim), inv(size(x,vardim) - Int(corrected))) +function covzm(x::AbstractMatrix, vardim::Int=1; corrected::Bool=true) + C = unscaled_covzm(x, vardim) + T = promote_type(typeof(first(C) / 1), eltype(C)) + return scale!(convert(AbstractMatrix{T}, C), 1//(size(x, vardim) - corrected)) +end covzm(x::AbstractVector, y::AbstractVector; corrected::Bool=true) = unscaled_covzm(x, y) / (_length(x) - Int(corrected)) -covzm(x::AbstractVecOrMat, y::AbstractVecOrMat, vardim::Int=1; corrected::Bool=true) = - scale!(unscaled_covzm(x, y, vardim), inv(_getnobs(x, y, vardim) - Int(corrected))) +function covzm(x::AbstractVecOrMat, y::AbstractVecOrMat, vardim::Int=1; corrected::Bool=true) + C = unscaled_covzm(x, y, vardim) + T = promote_type(typeof(first(C) / 1), eltype(C)) + return scale!(convert(AbstractArray{T}, C), 1//(_getnobs(x, y, vardim) - corrected)) +end # covm (with provided mean) diff --git a/test/statistics.jl b/test/statistics.jl index 55dd0fdf71c2b..1e05780a3ca40 100644 --- a/test/statistics.jl +++ b/test/statistics.jl @@ -432,3 +432,11 @@ end @test quantile(x, 0.5) === 3.0 @test quantile(x, 1//2) === 3//1 end + +@testset "Promotion in covzm. Issue #8080" begin + A = [1 -1 -1; -1 1 1; -1 1 -1; 1 -1 -1; 1 -1 1] + @test Base.covzm(A) - mean(A, 1)'*mean(A, 1)*size(A, 1)/(size(A, 1) - 1) ≈ cov(A) + A = [1//1 -1 -1; -1 1 1; -1 1 -1; 1 -1 -1; 1 -1 1] + @test (A'A - size(A, 1)*Base.mean(A, 1)'*Base.mean(A, 1))/4 == cov(A) +end + From 5fd053faf6513e9b086e7799cc0588eb46218601 Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Fri, 18 Aug 2017 23:56:22 +0200 Subject: [PATCH 066/324] diag of SparseMatrixCSC should always return SparseVector (#23261) * diag of SparseMatrixCSC should always return SparseVector * remove SpDiagIterator --- NEWS.md | 2 ++ base/sparse/linalg.jl | 2 +- base/sparse/sparsematrix.jl | 51 ++++++++++++++++++------------------- test/sparse/sparse.jl | 21 +++++++++++++++ 4 files changed, 49 insertions(+), 27 deletions(-) diff --git a/NEWS.md b/NEWS.md index 4f001ae743fec..0be83e8075b2d 100644 --- a/NEWS.md +++ b/NEWS.md @@ -312,6 +312,8 @@ Deprecated or removed * `Base.cpad` has been removed; use an appropriate combination of `rpad` and `lpad` instead ([#23187]). + * `Base.SparseArrays.SpDiagIterator` has been removed ([#23261]). + Command-line option changes --------------------------- diff --git a/base/sparse/linalg.jl b/base/sparse/linalg.jl index e1403d2967cb8..ad56b6ed057cc 100644 --- a/base/sparse/linalg.jl +++ b/base/sparse/linalg.jl @@ -879,7 +879,7 @@ for f in (:\, :Ac_ldiv_B, :At_ldiv_B) if m == n if istril(A) if istriu(A) - return ($f)(Diagonal(A), B) + return ($f)(Diagonal(Vector(diag(A))), B) else return ($f)(LowerTriangular(A), B) end diff --git a/base/sparse/sparsematrix.jl b/base/sparse/sparsematrix.jl index 212565d4cddcc..0c501c8c72bfa 100644 --- a/base/sparse/sparsematrix.jl +++ b/base/sparse/sparsematrix.jl @@ -3380,40 +3380,39 @@ function expandptr(V::Vector{<:Integer}) res end -## diag and related using an iterator -mutable struct SpDiagIterator{Tv,Ti} - A::SparseMatrixCSC{Tv,Ti} - n::Int -end -SpDiagIterator(A::SparseMatrixCSC) = SpDiagIterator(A,minimum(size(A))) - -length(d::SpDiagIterator) = d.n -start(d::SpDiagIterator) = 1 -done(d::SpDiagIterator, j) = j > d.n - -function next(d::SpDiagIterator{Tv}, j) where Tv - A = d.A - r1 = Int(A.colptr[j]) - r2 = Int(A.colptr[j+1]-1) - (r1 > r2) && (return (zero(Tv), j+1)) - r1 = searchsortedfirst(A.rowval, j, r1, r2, Forward) - (((r1 > r2) || (A.rowval[r1] != j)) ? zero(Tv) : A.nzval[r1], j+1) +function diag(A::SparseMatrixCSC{Tv,Ti}, d::Integer=0) where {Tv,Ti} + m, n = size(A) + k = Int(d) + if !(-m <= k <= n) + throw(ArgumentError("requested diagonal, $k, out of bounds in matrix of size ($m, $n)")) + end + l = k < 0 ? min(m+k,n) : min(n-k,m) + r, c = k <= 0 ? (-k, 0) : (0, k) # start row/col -1 + ind = Vector{Ti}() + val = Vector{Tv}() + for i in 1:l + r += 1; c += 1 + r1 = Int(A.colptr[c]) + r2 = Int(A.colptr[c+1]-1) + r1 > r2 && continue + r1 = searchsortedfirst(A.rowval, r, r1, r2, Forward) + ((r1 > r2) || (A.rowval[r1] != r)) && continue + push!(ind, i) + push!(val, A.nzval[r1]) + end + return SparseVector{Tv,Ti}(l, ind, val) end function trace(A::SparseMatrixCSC{Tv}) where Tv - if size(A,1) != size(A,2) - throw(DimensionMismatch("expected square matrix")) - end + n = checksquare(A) s = zero(Tv) - for d in SpDiagIterator(A) - s += d + for i in 1:n + s += A[i,i] end - s + return s end -diag(A::SparseMatrixCSC{Tv}) where {Tv} = Tv[d for d in SpDiagIterator(A)] - function diagm(v::SparseMatrixCSC{Tv,Ti}) where {Tv,Ti} if size(v,1) != 1 && size(v,2) != 1 throw(DimensionMismatch("input should be nx1 or 1xn")) diff --git a/test/sparse/sparse.jl b/test/sparse/sparse.jl index 714c8fea0f582..38e25d0a3a5a8 100644 --- a/test/sparse/sparse.jl +++ b/test/sparse/sparse.jl @@ -1323,6 +1323,27 @@ end @test diagm(sparse(ones(5,1))) == speye(5) end +@testset "diag" begin + for T in (Float64, Complex128) + S1 = sprand(T, 5, 5, 0.5) + S2 = sprand(T, 10, 5, 0.5) + S3 = sprand(T, 5, 10, 0.5) + for S in (S1, S2, S3) + A = Matrix(S) + @test diag(S)::SparseVector{T,Int} == diag(A) + for k in -size(S,1):size(S,2) + @test diag(S, k)::SparseVector{T,Int} == diag(A, k) + end + @test_throws ArgumentError diag(S, -size(S,1)-1) + @test_throws ArgumentError diag(S, size(S,2)+1) + end + end + # test that stored zeros are still stored zeros in the diagonal + S = sparse([1,3],[1,3],[0.0,0.0]); V = diag(S) + @test V.nzind == [1,3] + @test V.nzval == [0.0,0.0] +end + @testset "expandptr" begin A = speye(5) @test Base.SparseArrays.expandptr(A.colptr) == collect(1:5) From feaa2f6f65dada4ab1ade813e825d4847c4849cf Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Sat, 19 Aug 2017 00:41:16 +0200 Subject: [PATCH 067/324] export hex2num function (deprecated in #22088) (#23325) --- base/deprecated.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/deprecated.jl b/base/deprecated.jl index aff647f193df4..0671d1db0fbe3 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1346,7 +1346,6 @@ end @deprecate srand(filename::AbstractString, n::Integer=4) srand(read!(filename, Array{UInt32}(Int(n)))) @deprecate MersenneTwister(filename::AbstractString) srand(MersenneTwister(0), read!(filename, Array{UInt32}(Int(4)))) - # PR #21974 @deprecate versioninfo(verbose::Bool) versioninfo(verbose=verbose) @deprecate versioninfo(io::IO, verbose::Bool) versioninfo(io, verbose=verbose) @@ -1677,6 +1676,7 @@ function hex2num(s::AbstractString) end return reinterpret(Float64, parse(UInt64, s, 16)) end +export hex2num @deprecate num2hex(x::Union{Float16,Float32,Float64}) hex(reinterpret(Unsigned, x), sizeof(x)*2) @deprecate num2hex(n::Integer) hex(n, sizeof(n)*2) From d5379993a6c13906a2e720fe4ab7075ac7b136c1 Mon Sep 17 00:00:00 2001 From: quinnj Date: Fri, 18 Aug 2017 22:13:41 -0600 Subject: [PATCH 068/324] Ensure sizeof returns correctly for isbits Union arrays. Fixes #23321. Also defines sizeof on any isbits Union types --- src/builtins.c | 7 ++++++- src/codegen.cpp | 6 ++++-- test/core.jl | 15 ++++++++++----- test/reflection.jl | 4 ++-- 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/builtins.c b/src/builtins.c index 4d099f4156a8a..c3f89ab676558 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -259,6 +259,10 @@ JL_CALLABLE(jl_f_sizeof) jl_value_t *x = args[0]; if (jl_is_unionall(x) || jl_is_uniontype(x)) { x = jl_unwrap_unionall(x); + size_t elsize = 0, al = 0; + int isinline = jl_islayout_inline(x, &elsize, &al); + if (isinline) + return jl_box_long(elsize); if (!jl_is_datatype(x)) jl_error("argument is an abstract type; size is indeterminate"); } @@ -270,8 +274,9 @@ JL_CALLABLE(jl_f_sizeof) jl_error("type does not have a fixed size"); return jl_box_long(jl_datatype_size(x)); } - if (jl_is_array(x)) + if (jl_is_array(x)) { return jl_box_long(jl_array_len(x) * ((jl_array_t*)x)->elsize); + } if (jl_is_string(x)) return jl_box_long(jl_string_len(x)); if (jl_is_symbol(x)) diff --git a/src/codegen.cpp b/src/codegen.cpp index 5f21a5d59b3fb..53c5996dec3ca 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2718,12 +2718,14 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, auto len = emit_arraylen(ctx, obj, ary_ex); jl_value_t *ety = jl_tparam0(sty); Value *elsize; + size_t elsz = 0, al = 0; + bool isboxed = !jl_islayout_inline(ety, &elsz, &al); if (!jl_has_free_typevars(ety)) { - if (!jl_array_store_unboxed(ety)) { + if (isboxed) { elsize = ConstantInt::get(T_size, sizeof(void*)); } else { - elsize = ConstantInt::get(T_size, jl_datatype_size(ety)); + elsize = ConstantInt::get(T_size, elsz); } } else { diff --git a/test/core.jl b/test/core.jl index f1b3b42c1f6a8..7c53987223365 100644 --- a/test/core.jl +++ b/test/core.jl @@ -5232,6 +5232,11 @@ const unboxedunions = [Union{Int8, Void}, Union{Int8, Float16, Void}, @test Base.bitsunionsize(unboxedunions[3]) == 16 @test Base.bitsunionsize(unboxedunions[4]) == 8 +@test sizeof(unboxedunions[1]) == 1 +@test sizeof(unboxedunions[2]) == 2 +@test sizeof(unboxedunions[3]) == 16 +@test sizeof(unboxedunions[4]) == 8 + initvalue(::Type{Void}) = nothing initvalue(::Type{Char}) = '\0' initvalue(::Type{Date}) = Date(0, 12, 31) @@ -5259,11 +5264,11 @@ for U in boxedunions for N in (1, 2, 3, 4) A = Array{U}(ntuple(x->0, N)...) @test isempty(A) - @test Core.sizeof(A) == 0 + @test sizeof(A) == 0 A = Array{U}(ntuple(x->10, N)...) @test length(A) == 10^N - @test Core.sizeof(A) == sizeof(Int) * (10^N) + @test sizeof(A) == sizeof(Int) * (10^N) @test !isassigned(A, 1) end end @@ -5278,13 +5283,13 @@ for U in unboxedunions for N in (1, 2, 3, 4) A = Array{U}(ntuple(x->0, N)...) @test isempty(A) - @test Core.sizeof(A) == 0 + @test sizeof(A) == 0 len = ntuple(x->10, N) mxsz = maximum(sizeof, Base.uniontypes(U)) A = Array{U}(len) @test length(A) == prod(len) - @test Core.sizeof(A) == prod(len) * mxsz + @test sizeof(A) == prod(len) * mxsz @test isassigned(A, 1) @test isassigned(A, length(A)) @@ -5312,7 +5317,7 @@ for U in unboxedunions # reshape A3 = reshape(A, (div(prod(len), 2), 2)) - @test Core.sizeof(A) == prod(len) * mxsz + @test sizeof(A) == prod(len) * mxsz @test isassigned(A, 1) @test A[1] === initvalue2(F) diff --git a/test/reflection.jl b/test/reflection.jl index b77e7d4e778a8..ae3c9aace9434 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -690,8 +690,8 @@ end @test sizeof(Symbol("")) == 0 @test_throws(ErrorException("argument is an abstract type; size is indeterminate"), sizeof(Real)) -@test_throws ErrorException sizeof(Union{Complex64,Complex128}) -@test_throws ErrorException sizeof(Union{Int8,UInt8}) +@test sizeof(Union{Complex64,Complex128}) == 16 +@test sizeof(Union{Int8,UInt8}) == 1 @test_throws ErrorException sizeof(AbstractArray) @test_throws ErrorException sizeof(Tuple) @test_throws ErrorException sizeof(Tuple{Any,Any}) From 992377f3f43ac3a6c6f16e2525792a67479a5424 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Thu, 17 Aug 2017 16:47:15 -0400 Subject: [PATCH 069/324] fix #16356, deprecate juxtaposing bin, oct, and hex literals --- NEWS.md | 3 +++ src/julia-parser.scm | 19 +++++++++++++------ test/bitarray.jl | 8 ++++---- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/NEWS.md b/NEWS.md index 0be83e8075b2d..d0a38c92e9ac6 100644 --- a/NEWS.md +++ b/NEWS.md @@ -19,6 +19,9 @@ Language changes * In string and character literals, backslash `\` may no longer precede unrecognized escape characters ([#22800]). + * Juxtaposing binary, octal, and hexadecimal literals is deprecated, since it can lead to + confusing code such as `0xapi == 0xa * pi` ([#16356]). + * Declaring arguments as `x::ANY` to avoid specialization has been replaced by `@nospecialize x`. ([#22666]). diff --git a/src/julia-parser.scm b/src/julia-parser.scm index f44659f19b076..9ca2ad558242c 100644 --- a/src/julia-parser.scm +++ b/src/julia-parser.scm @@ -337,13 +337,20 @@ (write-char (read-char port) str) (read-digs #t #f) (disallow-dot)) - (io.ungetc port c)))) + (io.ungetc port c))))) + (if (and (char? c) + (or (eq? pred char-bin?) (eq? pred char-oct?) + (and (eq? pred char-hex?) (not is-hex-float-literal))) + (or (char-numeric? c) + (and (identifier-start-char? c) + (syntax-deprecation port ;; remove after v0.7 + (string (get-output-string str) c) + (string (get-output-string str) " * " c)) + #f))) ;; remove after v0.7 ;; disallow digits after binary or octal literals, e.g., 0b12 - (if (and (or (eq? pred char-bin?) (eq? pred char-oct?)) - (not (eof-object? c)) - (char-numeric? c)) - (error (string "invalid numeric constant \"" - (get-output-string str) c "\""))))) + ;; and disallow identifier chars after hex literals. + (error (string "invalid numeric constant \"" + (get-output-string str) c "\"")))) (let* ((s (get-output-string str)) (r (cond ((eq? pred char-hex?) 16) ((eq? pred char-oct?) 8) diff --git a/test/bitarray.jl b/test/bitarray.jl index b1b5655e0d459..fa31ad32cfbca 100644 --- a/test/bitarray.jl +++ b/test/bitarray.jl @@ -885,8 +885,8 @@ timesofar("unary arithmetic") @check_bit_operation broadcast(^, 1.0im, b2) Matrix{Complex128} @check_bit_operation broadcast(^, 0im, b2) Matrix{Complex{Int}} @check_bit_operation broadcast(^, 1im, b2) Matrix{Complex{Int}} - @check_bit_operation broadcast(^, 0x0im, b2) Matrix{Complex{UInt8}} - @check_bit_operation broadcast(^, 0x1im, b2) Matrix{Complex{UInt8}} + @check_bit_operation broadcast(^, 0x0*im, b2) Matrix{Complex{UInt8}} + @check_bit_operation broadcast(^, 0x1*im, b2) Matrix{Complex{UInt8}} end @testset "Matrix/Number" begin @@ -982,7 +982,7 @@ timesofar("unary arithmetic") @check_bit_operation broadcast(^, b1, 0.0) Matrix{Float64} @check_bit_operation broadcast(^, b1, 1.0) Matrix{Float64} @check_bit_operation broadcast(^, b1, 0.0im) Matrix{Complex128} - @check_bit_operation broadcast(^, b1, 0x0im) Matrix{Complex128} + @check_bit_operation broadcast(^, b1, 0x0*im) Matrix{Complex128} @check_bit_operation broadcast(^, b1, 0im) Matrix{Complex128} @test_throws DomainError broadcast(^, b1, -1) @@ -991,7 +991,7 @@ timesofar("unary arithmetic") @check_bit_operation broadcast(^, b1, 1.0im) Matrix{Complex128} @check_bit_operation broadcast(^, b1, -1im) Matrix{Complex128} @check_bit_operation broadcast(^, b1, 1im) Matrix{Complex128} - @check_bit_operation broadcast(^, b1, 0x1im) Matrix{Complex128} + @check_bit_operation broadcast(^, b1, 0x1*im) Matrix{Complex128} end end From da0e8a52302df88416623439e07bcca70784ad12 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Sun, 6 Aug 2017 13:36:58 -0400 Subject: [PATCH 070/324] fixes for very-linear mode --- src/julia-syntax.scm | 72 +++++++++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 31 deletions(-) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 1ed2e3330838f..42cbb9fecf7d7 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -359,7 +359,7 @@ types))) (call (core svec) ,@temps))) ,body ,isstaged)))) - (if (symbol? name) + (if (or (symbol? name) (globalref? name)) `(block (method ,name) ,mdef (unnecessary ,name)) ;; return the function mdef))))) @@ -1011,7 +1011,7 @@ (dcl (and (pair? name) (eq? (car name) '|::|))) (rett (if dcl (caddr name) '(core Any))) (name (if dcl (cadr name) name))) - (cond ((and (length= e 2) (symbol? name)) + (cond ((and (length= e 2) (or (symbol? name) (globalref? name))) (if (or (eq? name 'true) (eq? name 'false)) (error (string "invalid function name \"" name "\""))) `(method ,name)) @@ -3458,15 +3458,23 @@ f(x) = yt(x) (else #f))) (case (car e) ((call new foreigncall) - (let* ((args (if (eq? (car e) 'foreigncall) - ;; NOTE: 2nd to 5th arguments of ccall must be left in place - ;; the 1st should be compiled if an atom. - (append (list) - (cond (atom? (cadr e) (compile-args (list (cadr e)) break-labels linearize-args)) - (else (cadr e))) - (list-head (cddr e) 4) - (compile-args (list-tail e 6) break-labels linearize-args)) - (compile-args (cdr e) break-labels linearize-args))) + (let* ((args + (cond ((eq? (car e) 'foreigncall) + ;; NOTE: 2nd to 5th arguments of ccall must be left in place + ;; the 1st should be compiled if an atom. + (append (if (atom? (cadr e)) + (compile-args (list (cadr e)) break-labels linearize-args) + (list (cadr e))) + (list-head (cddr e) 4) + (compile-args (list-tail e 6) break-labels linearize-args))) + ;; TODO: evaluate first argument to cglobal some other way + ((and (length> e 2) + (or (eq? (cadr e) 'cglobal) + (equal? (cadr e) '(outerref cglobal)))) + (list* (cadr e) (caddr e) + (compile-args (cdddr e) break-labels linearize-args))) + (else + (compile-args (cdr e) break-labels linearize-args)))) (callex (cons (car e) args))) (cond (tail (emit-return callex)) (value callex) @@ -3674,26 +3682,28 @@ f(x) = yt(x) ;; top level expressions returning values ((abstract_type primitive_type struct_type thunk toplevel module) - (case (car e) - ((abstract_type) - (let* ((para (compile (caddr e) break-labels #t #f)) - (supe (compile (cadddr e) break-labels #t #f))) - (emit `(abstract_type ,(cadr e) ,para ,supe)))) - ((primitive_type) - (let* ((para (compile (caddr e) break-labels #t #f)) - (supe (compile (list-ref e 4) break-labels #t #f))) - (emit `(primitive_type ,(cadr e) ,para ,(cadddr e) ,supe)))) - ((struct_type) - (let* ((para (compile (caddr e) break-labels #t #f)) - (supe (compile (list-ref e 4) break-labels #t #f)) - ;; struct_type has an unconventional evaluation rule that - ;; needs to do work around the evaluation of the field types, - ;; so the field type expressions need to be kept in place as - ;; much as possible. (part of issue #21923) - (ftys (compile (list-ref e 5) break-labels #t #f #f))) - (emit `(struct_type ,(cadr e) ,para ,(cadddr e) ,supe ,ftys ,@(list-tail e 6))))) - (else - (emit e))) + (with-bindings + ((*very-linear-mode* #f)) ;; type defs use nonstandard evaluation order + (case (car e) + ((abstract_type) + (let* ((para (compile (caddr e) break-labels #t #f)) + (supe (compile (cadddr e) break-labels #t #f))) + (emit `(abstract_type ,(cadr e) ,para ,supe)))) + ((primitive_type) + (let* ((para (compile (caddr e) break-labels #t #f)) + (supe (compile (list-ref e 4) break-labels #t #f))) + (emit `(primitive_type ,(cadr e) ,para ,(cadddr e) ,supe)))) + ((struct_type) + (let* ((para (compile (caddr e) break-labels #t #f)) + (supe (compile (list-ref e 4) break-labels #t #f)) + ;; struct_type has an unconventional evaluation rule that + ;; needs to do work around the evaluation of the field types, + ;; so the field type expressions need to be kept in place as + ;; much as possible. (part of issue #21923) + (ftys (compile (list-ref e 5) break-labels #t #f #f))) + (emit `(struct_type ,(cadr e) ,para ,(cadddr e) ,supe ,ftys ,@(list-tail e 6))))) + (else + (emit e)))) (if tail (emit-return '(null))) '(null)) From 1c2e689dd476e507fca2e468c1de82e88cfc0808 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Sat, 19 Aug 2017 09:14:24 -0500 Subject: [PATCH 071/324] Fix eltype conversion for StepRangeLen{BigFloat} (fixes #23300) --- base/twiceprecision.jl | 3 ++- test/ranges.jl | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/base/twiceprecision.jl b/base/twiceprecision.jl index 6607c12e1d1ce..8a849dcb9af68 100644 --- a/base/twiceprecision.jl +++ b/base/twiceprecision.jl @@ -422,7 +422,7 @@ function rat(x) y = x a = d = 1 b = c = 0 - m = maxintfloat(narrow(typeof(x))) + m = maxintfloat(narrow(typeof(x)), Int) while abs(y) <= m f = trunc(Int,y) y -= f @@ -435,6 +435,7 @@ function rat(x) return a, b end +narrow(::Type{T}) where {T<:AbstractFloat} = Float64 narrow(::Type{Float64}) = Float32 narrow(::Type{Float32}) = Float16 narrow(::Type{Float16}) = Float16 diff --git a/test/ranges.jl b/test/ranges.jl index 6ec04c6f89038..1b04b345d8eab 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -957,3 +957,10 @@ end @test logspace(a, b, n, base=base) == base.^linspace(a, b, n) end end + +# Issue #23300 +x = -5:big(1.0):5 +@test map(Float64, x) === -5.0:1.0:5.0 +@test map(Float32, x) === -5.0f0:1.0f0:5.0f0 +@test map(Float16, x) === Float16(-5.0):Float16(1.0):Float16(5.0) +@test map(BigFloat, x) === x From f55ad3790d8f7a3bd81e665574bd13919d3f71a9 Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Sun, 6 Aug 2017 22:33:20 -0500 Subject: [PATCH 072/324] Remove unnecessary `get_creds!` methods --- base/libgit2/types.jl | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/base/libgit2/types.jl b/base/libgit2/types.jl index f061de12f00bb..eca2143bef607 100644 --- a/base/libgit2/types.jl +++ b/base/libgit2/types.jl @@ -911,16 +911,6 @@ reset!(p::CachedCredentials) = (foreach(reset!, values(p.cred)); p) "Obtain the cached credentials for the given host+protocol (credid), or return and store the default if not found" get_creds!(collection::CachedCredentials, credid, default) = get!(collection.cred, credid, default) -get_creds!(creds::AbstractCredentials, credid, default) = creds -get_creds!(creds::Void, credid, default) = default -function get_creds!(creds::Ref{Nullable{AbstractCredentials}}, credid, default) - if isnull(creds[]) - creds[] = Nullable{AbstractCredentials}(default) - return default - else - get_creds!(Base.get(creds[]), credid, default) - end -end function securezero!(p::CachedCredentials) foreach(securezero!, values(p.cred)) From 18b2235d7fe668b31ff3085a0b4dbac06e166b51 Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Sun, 6 Aug 2017 22:35:28 -0500 Subject: [PATCH 073/324] Move AbstractCredentials near other cred types --- base/libgit2/types.jl | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/base/libgit2/types.jl b/base/libgit2/types.jl index eca2143bef607..6d54fc48514f8 100644 --- a/base/libgit2/types.jl +++ b/base/libgit2/types.jl @@ -116,15 +116,6 @@ function free(buf_ref::Base.Ref{Buffer}) ccall((:git_buf_free, :libgit2), Void, (Ptr{Buffer},), buf_ref) end -"Abstract credentials payload" -abstract type AbstractCredentials end - -"Checks if credentials were used" -checkused!(p::AbstractCredentials) = true -checkused!(p::Void) = false -"Resets credentials for another use" -reset!(p::AbstractCredentials, cnt::Int=3) = nothing - """ LibGit2.CheckoutOptions @@ -837,6 +828,15 @@ end import Base.securezero! +"Abstract credentials payload" +abstract type AbstractCredentials end + +"Checks if credentials were used" +checkused!(p::AbstractCredentials) = true +checkused!(p::Void) = false +"Resets credentials for another use" +reset!(p::AbstractCredentials, cnt::Int=3) = nothing + "Credentials that support only `user` and `password` parameters" mutable struct UserPasswordCredentials <: AbstractCredentials user::String From b301f4570e0f7936b5be3210178f05d309f5d869 Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Sat, 19 Aug 2017 10:48:48 -0500 Subject: [PATCH 074/324] Remove unused checkused! method --- base/libgit2/types.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/base/libgit2/types.jl b/base/libgit2/types.jl index 6d54fc48514f8..dd20f9dfe48d9 100644 --- a/base/libgit2/types.jl +++ b/base/libgit2/types.jl @@ -833,7 +833,6 @@ abstract type AbstractCredentials end "Checks if credentials were used" checkused!(p::AbstractCredentials) = true -checkused!(p::Void) = false "Resets credentials for another use" reset!(p::AbstractCredentials, cnt::Int=3) = nothing From 916bdaaabce8fe48d6fc2cb28907004e47245c8d Mon Sep 17 00:00:00 2001 From: Yichao Yu Date: Sat, 19 Aug 2017 21:24:51 -0400 Subject: [PATCH 075/324] Quote JULIA_CPU_TARGET in makefile In order to allow special characters. --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 4dbe062d54f36..d34d715fcc2cf 100644 --- a/Makefile +++ b/Makefile @@ -210,7 +210,7 @@ BASE_SRCS := $(sort $(shell find $(JULIAHOME)/base -name \*.jl) $(shell find $(B $(build_private_libdir)/inference.ji: $(CORE_SRCS) | $(build_private_libdir) @$(call PRINT_JULIA, cd $(JULIAHOME)/base && \ - $(call spawn,$(JULIA_EXECUTABLE)) -C $(JULIA_CPU_TARGET) --output-ji $(call cygpath_w,$@) \ + $(call spawn,$(JULIA_EXECUTABLE)) -C "$(JULIA_CPU_TARGET)" --output-ji $(call cygpath_w,$@) \ --startup-file=no -g0 -O0 coreimg.jl) RELBUILDROOT := $(shell $(JULIAHOME)/contrib/relative_path.sh "$(JULIAHOME)/base" "$(BUILDROOT)/base/") @@ -218,7 +218,7 @@ COMMA:=, define sysimg_builder $$(build_private_libdir)/sys$1.o: $$(build_private_libdir)/inference.ji $$(JULIAHOME)/VERSION $$(BASE_SRCS) @$$(call PRINT_JULIA, cd $$(JULIAHOME)/base && \ - $$(call spawn,$3) $2 -C $$(JULIA_CPU_TARGET) --output-o $$(call cygpath_w,$$@) $$(JULIA_SYSIMG_BUILD_FLAGS) \ + $$(call spawn,$3) $2 -C "$$(JULIA_CPU_TARGET)" --output-o $$(call cygpath_w,$$@) $$(JULIA_SYSIMG_BUILD_FLAGS) \ --startup-file=no --warn-overwrite=yes --sysimage $$(call cygpath_w,$$<) sysimg.jl $$(RELBUILDROOT) \ || { echo '*** This error is usually fixed by running `make clean`. If the error persists$$(COMMA) try `make cleanall`. ***' && false; } ) .SECONDARY: $(build_private_libdir)/sys$1.o From 67a860503a26774fd2739706ad22d50c6d3ecff9 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Tue, 15 Aug 2017 12:54:35 -0500 Subject: [PATCH 076/324] Better TwicePrecision utilities --- base/float.jl | 41 ++--- base/math.jl | 8 +- base/special/exp.jl | 4 +- base/special/exp10.jl | 4 +- base/sysimg.jl | 2 +- base/twiceprecision.jl | 334 ++++++++++++++++++++++++++++++----------- test/ranges.jl | 184 ++++++++++++++++++++++- 7 files changed, 452 insertions(+), 125 deletions(-) diff --git a/base/float.jl b/base/float.jl index 8a611cf38f8e8..23c0db43c37af 100644 --- a/base/float.jl +++ b/base/float.jl @@ -1,5 +1,7 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license +const IEEEFloat = Union{Float16, Float32, Float64} + ## floating point traits ## """ @@ -605,7 +607,7 @@ uabs(x::Signed) = unsigned(abs(x)) The result of `n` iterative applications of `nextfloat` to `x` if `n >= 0`, or `-n` applications of `prevfloat` if `n < 0`. """ -function nextfloat(f::Union{Float16,Float32,Float64}, d::Integer) +function nextfloat(f::IEEEFloat, d::Integer) F = typeof(f) fumax = reinterpret(Unsigned, F(Inf)) U = typeof(fumax) @@ -711,12 +713,12 @@ end Test whether a floating point number is subnormal. """ -function issubnormal end +function issubnormal(x::T) where {T<:IEEEFloat} + y = reinterpret(Unsigned, x) + (y & exponent_mask(T) == 0) & (y & significand_mask(T) != 0) +end @eval begin - issubnormal(x::Float32) = (abs(x) < $(bitcast(Float32, 0x00800000))) & (x!=0) - issubnormal(x::Float64) = (abs(x) < $(bitcast(Float64, 0x0010000000000000))) & (x!=0) - typemin(::Type{Float16}) = $(bitcast(Float16, 0xfc00)) typemax(::Type{Float16}) = $(Inf16) typemin(::Type{Float32}) = $(-Inf32) @@ -864,32 +866,11 @@ exponent_half(::Type{Float16}) = 0x3800 significand_mask(::Type{Float16}) = 0x03ff # integer size of float -fpinttype(::Type{Float64}) = UInt64 -fpinttype(::Type{Float32}) = UInt32 -fpinttype(::Type{Float16}) = UInt16 - -## TwicePrecision utilities -# The numeric constants are half the number of bits in the mantissa -for (F, T, n) in ((Float16, UInt16, 5), (Float32, UInt32, 12), (Float64, UInt64, 26)) - @eval begin - function truncbits(x::$F, nb) - @_inline_meta - truncmask(x, typemax($T) << nb) - end - function truncmask(x::$F, mask) - @_inline_meta - reinterpret($F, mask & reinterpret($T, x)) - end - function splitprec(x::$F) - @_inline_meta - hi = truncmask(x, typemax($T) << $n) - hi, x-hi - end - end -end +uinttype(::Type{Float64}) = UInt64 +uinttype(::Type{Float32}) = UInt32 +uinttype(::Type{Float16}) = UInt16 -truncbits(x, nb) = x -truncmask(x, mask) = x +Base.iszero(x::Float16) = reinterpret(UInt16, x) & ~sign_mask(Float16) == 0x0000 ## Array operations on floating point numbers ## diff --git a/base/math.jl b/base/math.jl index 64f957d7a1467..b90caf4574315 100644 --- a/base/math.jl +++ b/base/math.jl @@ -21,11 +21,11 @@ import Base: log, exp, sin, cos, tan, sinh, cosh, tanh, asin, exp10, expm1, log1p using Base: sign_mask, exponent_mask, exponent_one, - exponent_half, fpinttype, significand_mask + exponent_half, uinttype, significand_mask using Core.Intrinsics: sqrt_llvm -const IEEEFloat = Union{Float16, Float32, Float64} +using Base.IEEEFloat @noinline function throw_complex_domainerror(f, x) throw(DomainError(x, string("$f will only return a complex result if called with a ", @@ -588,7 +588,7 @@ function ldexp(x::T, e::Integer) where T<:IEEEFloat return flipsign(T(Inf), x) end if k > 0 # normal case - xu = (xu & ~exponent_mask(T)) | (rem(k, fpinttype(T)) << significand_bits(T)) + xu = (xu & ~exponent_mask(T)) | (rem(k, uinttype(T)) << significand_bits(T)) return reinterpret(T, xu) else # subnormal case if k <= -significand_bits(T) # underflow @@ -598,7 +598,7 @@ function ldexp(x::T, e::Integer) where T<:IEEEFloat end k += significand_bits(T) z = T(2.0)^-significand_bits(T) - xu = (xu & ~exponent_mask(T)) | (rem(k, fpinttype(T)) << significand_bits(T)) + xu = (xu & ~exponent_mask(T)) | (rem(k, uinttype(T)) << significand_bits(T)) return z*reinterpret(T, xu) end end diff --git a/base/special/exp.jl b/base/special/exp.jl index e49bfb5ed942b..ccabe295597db 100644 --- a/base/special/exp.jl +++ b/base/special/exp.jl @@ -117,11 +117,11 @@ function exp(x::T) where T<:Union{Float32,Float64} if k > -significand_bits(T) # multiply by 2.0 first to prevent overflow, which helps extends the range k == exponent_max(T) && return y * T(2.0) * T(2.0)^(exponent_max(T) - 1) - twopk = reinterpret(T, rem(exponent_bias(T) + k, fpinttype(T)) << significand_bits(T)) + twopk = reinterpret(T, rem(exponent_bias(T) + k, uinttype(T)) << significand_bits(T)) return y*twopk else # add significand_bits(T) + 1 to lift the range outside the subnormals - twopk = reinterpret(T, rem(exponent_bias(T) + significand_bits(T) + 1 + k, fpinttype(T)) << significand_bits(T)) + twopk = reinterpret(T, rem(exponent_bias(T) + significand_bits(T) + 1 + k, uinttype(T)) << significand_bits(T)) return y * twopk * T(2.0)^(-significand_bits(T) - 1) end elseif xa < reinterpret(Unsigned, exp_small_thres(T)) # |x| < exp_small_thres diff --git a/base/special/exp10.jl b/base/special/exp10.jl index d8ce280468b6c..711da3b04549b 100644 --- a/base/special/exp10.jl +++ b/base/special/exp10.jl @@ -119,11 +119,11 @@ function exp10(x::T) where T<:Union{Float32,Float64} if k > -significand_bits(T) # multiply by 2.0 first to prevent overflow, extending the range k == exponent_max(T) && return y * T(2.0) * T(2.0)^(exponent_max(T) - 1) - twopk = reinterpret(T, rem(exponent_bias(T) + k, fpinttype(T)) << significand_bits(T)) + twopk = reinterpret(T, rem(exponent_bias(T) + k, uinttype(T)) << significand_bits(T)) return y*twopk else # add significand_bits(T) + 1 to lift the range outside the subnormals - twopk = reinterpret(T, rem(exponent_bias(T) + significand_bits(T) + 1 + k, fpinttype(T)) << significand_bits(T)) + twopk = reinterpret(T, rem(exponent_bias(T) + significand_bits(T) + 1 + k, uinttype(T)) << significand_bits(T)) return y * twopk * T(2.0)^(-significand_bits(T) - 1) end elseif xa < reinterpret(Unsigned, exp10_small_thres(T)) # |x| < exp10_small_thres diff --git a/base/sysimg.jl b/base/sysimg.jl index de68ed222632b..8b180d773cdef 100644 --- a/base/sysimg.jl +++ b/base/sysimg.jl @@ -84,7 +84,6 @@ include("tuple.jl") include("pair.jl") include("traits.jl") include("range.jl") -include("twiceprecision.jl") include("expr.jl") include("error.jl") @@ -135,6 +134,7 @@ include("hashing.jl") include("rounding.jl") importall .Rounding include("float.jl") +include("twiceprecision.jl") include("complex.jl") include("rational.jl") include("multinverses.jl") diff --git a/base/twiceprecision.jl b/base/twiceprecision.jl index 8a849dcb9af68..16408776b2a17 100644 --- a/base/twiceprecision.jl +++ b/base/twiceprecision.jl @@ -6,6 +6,157 @@ # that return r[3] == 0.3. Otherwise, we have roundoff error due to # 0.1 + 2*0.1 = 0.30000000000000004 +""" + hi, lo = splitprec(F::Type{<:AbstractFloat}, i::Integer) + +Represent an integer `i` as a pair of floating-point numbers `hi` and +`lo` (of type `F`) such that: +- `widen(hi) + widen(lo) ≈ i`. It is exact if 1.5 * (number of precision bits in `F`) is greater than the number of bits in `i`. +- all bits in `hi` are more significant than any of the bits in `lo` +- `hi` can be exactly multiplied by the `hi` component of another call to `splitprec`. + +In particular, while `convert(Float64, i)` can be lossy since Float64 +has only 53 bits of precision, `splitprec(Float64, i)` is exact for +any Int64/UInt64. +""" +function splitprec(::Type{F}, i::Integer) where {F<:AbstractFloat} + hi = truncbits(F(i), cld(precision(F), 2)) + ihi = oftype(i, hi) + hi, F(i - ihi) +end + +function truncmask(x::F, mask) where {F<:IEEEFloat} + reinterpret(F, mask & reinterpret(uinttype(F), x)) +end +truncmask(x, mask) = x + +function truncbits(x::F, nb) where {F<:IEEEFloat} + truncmask(x, typemax(uinttype(F)) << nb) +end +truncbits(x, nb) = x + + +## Dekker arithmetic + +""" + hi, lo = canonicalize2(big, little) + +Generate a representation where all the nonzero bits in `hi` are more +significant than any of the nonzero bits in `lo`. `big` must be larger +in absolute value than `little`. +""" +function canonicalize2(big, little) + h = big+little + h, (big - h) + little +end + +""" + zhi, zlo = add12(x, y) + +A high-precision representation of `x + y` for floating-point +numbers. Mathematically, `zhi + zlo = x + y`, where `zhi` contains the +most significant bits and `zlo` the least significant. + +Because of the way floating-point numbers are printed, `lo` may not +look the way you might expect from the standpoint of decimal +representation, even though it is exact from the standpoint of binary +representation. + +Example: +```julia +julia> 1.0 + 1.0001e-15 +1.000000000000001 + +julia> big(1.0) + big(1.0001e-15) +1.000000000000001000100000000000020165767380775934141445417482375879192346701529 + +julia> hi, lo = Base.add12(1.0, 1.0001e-15) +(1.000000000000001, -1.1012302462515652e-16) + +julia> big(hi) + big(lo) +1.000000000000001000100000000000020165767380775934141445417482375879192346701529 +``` + +`lo` differs from 1.0e-19 because `hi` is not exactly equal to +the first 16 decimal digits of the answer. +""" +function add12(x::T, y::T) where {T} + x, y = ifelse(abs(y) > abs(x), (y, x), (x, y)) + canonicalize2(x, y) +end +add12(x, y) = add12(promote_noncircular(x, y)...) + +""" + zhi, zlo = mul12(x, y) + +A high-precision representation of `x * y` for floating-point +numbers. Mathematically, `zhi + zlo = x * y`, where `zhi` contains the +most significant bits and `zlo` the least significant. + +Example: +```julia +julia> x = Float32(π) +3.1415927f0 + +julia> x * x +9.869605f0 + +julia> Float64(x) * Float64(x) +9.869604950382893 + +julia> hi, lo = Base.mul12(x, x) +(9.869605f0, -1.140092f-7) + +julia> Float64(hi) + Float64(lo) +9.869604950382893 +``` +""" +function mul12(x::T, y::T) where {T<:AbstractFloat} + h = x * y + ifelse(iszero(h) | !isfinite(h), (h, h), canonicalize2(h, fma(x, y, -h))) +end +mul12(x::T, y::T) where {T} = (p = x * y; (p, zero(p))) +mul12(x, y) = mul12(promote_noncircular(x, y)...) + +""" + zhi, zlo = div12(x, y) + +A high-precision representation of `x / y` for floating-point +numbers. Mathematically, `zhi + zlo ≈ x / y`, where `zhi` contains the +most significant bits and `zlo` the least significant. + +Example: +```julia +julia> x, y = Float32(π), 3.1f0 +(3.1415927f0, 3.1f0) + +julia> x / y +1.013417f0 + +julia> Float64(x) / Float64(y) +1.0134170444063078 + +julia> hi, lo = Base.div12(x, y) +(1.013417f0, 3.8867366f-8) + +julia> Float64(hi) + Float64(lo) +1.0134170444063066 +""" +function div12(x::T, y::T) where {T<:AbstractFloat} + # We lose precision if any intermediate calculation results in a subnormal. + # To prevent this from happening, standardize the values. + xs, xe = frexp(x) + ys, ye = frexp(y) + r = xs / ys + rh, rl = canonicalize2(r, -fma(r, ys, -xs)/ys) + ifelse(iszero(r) | !isfinite(r), (r, r), (ldexp(rh, xe-ye), ldexp(rl, xe-ye))) +end +div12(x::T, y::T) where {T} = (p = x / y; (p, zero(p))) +div12(x, y) = div12(promote_noncircular(x, y)...) + + +## TwicePrecision + """ TwicePrecision{T}(hi::T, lo::T) TwicePrecision{T}((num, denom)) @@ -16,7 +167,7 @@ Float64`. `hi` represents the high bits (most significant bits) and `num//denom` can be approximated conveniently using the syntax `TwicePrecision{T}((num, denom))`. -When used with `T<:AbstractFloat` to construct an exact +When used with `T<:Union{Float16,Float32,Float64}` to construct an "exact" `StepRangeLen`, `ref` should be the range element with smallest magnitude and `offset` set to the corresponding index. For efficiency, multiplication of `step` by the index is not performed at @@ -35,30 +186,52 @@ struct TwicePrecision{T} lo::T # least significant bits end -function TwicePrecision{T}(nd::Tuple{I,I}) where {T,I} +TwicePrecision{T}(x::T) where {T} = TwicePrecision{T}(x, zero(T)) + +function TwicePrecision{T}(x) where {T} + xT = convert(T, x) + Δx = x - xT + TwicePrecision{T}(xT, T(Δx)) +end + +TwicePrecision{T}(i::Integer) where {T<:AbstractFloat} = + TwicePrecision{T}(canonicalize2(splitprec(T, i)...)...) + +TwicePrecision(x) = TwicePrecision{typeof(x)}(x) + +# Numerator/Denominator constructors +function TwicePrecision{T}(nd::Tuple{Integer,Integer}) where {T<:Union{Float16,Float32}} + n, d = nd + TwicePrecision{T}(n/d) +end + +function TwicePrecision{T}(nd::Tuple{Any,Any}) where {T} n, d = nd - TwicePrecision{T}(n, zero(T)) / d + TwicePrecision{T}(n) / d end function TwicePrecision{T}(nd::Tuple{I,I}, nb::Integer) where {T,I} twiceprecision(TwicePrecision{T}(nd), nb) end -function twiceprecision(val::T, nb::Integer) where T<:Number +# Truncating constructors. Useful for generating values that can be +# exactly multiplied by small integers. +function twiceprecision(val::T, nb::Integer) where {T<:IEEEFloat} hi = truncbits(val, nb) TwicePrecision{T}(hi, val - hi) end -function twiceprecision(val::TwicePrecision{T}, nb::Integer) where T<:Number +function twiceprecision(val::TwicePrecision{T}, nb::Integer) where {T<:IEEEFloat} hi = truncbits(val.hi, nb) TwicePrecision{T}(hi, (val.hi - hi) + val.lo) end nbitslen(r::StepRangeLen) = nbitslen(eltype(r), length(r), r.offset) -nbitslen(::Type{Float64}, len, offset) = min(26, nbitslen(len, offset)) -nbitslen(::Type{Float32}, len, offset) = min(12, nbitslen(len, offset)) -nbitslen(::Type{Float16}, len, offset) = min(5, nbitslen(len, offset)) -nbitslen(len, offset) = len < 2 ? 0 : ceil(Int, log2(max(offset-1, len-offset))) +nbitslen(::Type{T}, len, offset) where {T<:IEEEFloat} = + min(cld(precision(T), 2), nbitslen(len, offset)) +# The +1 here is for safety, because the precision of the significand +# is 1 bit higher than the number that are explicitly stored. +nbitslen(len, offset) = len < 2 ? 0 : ceil(Int, log2(max(offset-1, len-offset))) + 1 eltype(::Type{TwicePrecision{T}}) where {T} = T @@ -83,6 +256,56 @@ big(x::TwicePrecision) = big(x.hi) + big(x.lo) zero(::Type{TwicePrecision{T}}) where {T} = TwicePrecision{T}(0, 0) +# Arithmetic + +function +(x::TwicePrecision, y::Number) + s_hi, s_lo = add12(x.hi, y) + TwicePrecision(canonicalize2(s_hi, s_lo+x.lo)...) +end ++(x::Number, y::TwicePrecision) = y+x + +function +(x::TwicePrecision{T}, y::TwicePrecision{T}) where T + r = x.hi + y.hi + s = abs(x.hi) > abs(y.hi) ? (((x.hi - r) + y.hi) + y.lo) + x.lo : (((y.hi - r) + x.hi) + x.lo) + y.lo + TwicePrecision(canonicalize2(r, s)...) +end ++(x::TwicePrecision, y::TwicePrecision) = +(promote_noncircular(x, y)...) + +-(x::TwicePrecision, y::TwicePrecision) = x + (-y) +-(x::TwicePrecision, y::Number) = x + (-y) +-(x::Number, y::TwicePrecision) = x + (-y) + +function *(x::TwicePrecision, v::Number) + v == 0 && return TwicePrecision(x.hi*v, x.lo*v) + x * TwicePrecision{typeof(x.hi*v)}(v) +end +function *(x::TwicePrecision{<:IEEEFloat}, v::Integer) + v == 0 && return TwicePrecision(x.hi*v, x.lo*v) + nb = ceil(Int, log2(abs(v))) + u = truncbits(x.hi, nb) + TwicePrecision(canonicalize2(u*v, ((x.hi-u) + x.lo)*v)...) +end +*(v::Number, x::TwicePrecision) = x*v + +function *(x::TwicePrecision{T}, y::TwicePrecision{T}) where {T} + zh, zl = mul12(x.hi, y.hi) + ret = TwicePrecision{T}(canonicalize2(zh, (x.hi * y.lo + x.lo * y.hi) + zl)...) + ifelse(iszero(zh) | !isfinite(zh), TwicePrecision{T}(zh, zh), ret) +end +*(x::TwicePrecision, y::TwicePrecision) = *(promote_noncircular(x, y)...) + +function /(x::TwicePrecision, v::Number) + x / TwicePrecision{typeof(x.hi/v)}(v) +end + +function /(x::TwicePrecision, y::TwicePrecision) + hi = x.hi / y.hi + uh, ul = mul12(hi, y.hi) + lo = ((((x.hi - uh) - ul) + x.lo) - hi*y.lo)/y.hi + ret = TwicePrecision(canonicalize2(hi, lo)...) + ifelse(iszero(hi) | !isfinite(hi), TwicePrecision(hi, hi), ret) +end + ## StepRangeLen # If using TwicePrecision numbers, deliberately force user to specify offset @@ -158,6 +381,7 @@ function colon(start::T, step::T, stop::T) where T<:Union{Float16,Float32,Float6 # if we've overshot the end, subtract one: len -= (start < stop < stop′) + (start > stop > stop′) end + StepRangeLen(TwicePrecision(start, zero(T)), twiceprecision(step, nbitslen(T, len, 1)), len) end @@ -189,24 +413,23 @@ end # This assumes that r.step has already been split so that (0:len-1)*r.step.hi is exact function unsafe_getindex(r::StepRangeLen{T,<:TwicePrecision,<:TwicePrecision}, i::Integer) where T - # Very similar to _getindex_hiprec, but optimized to avoid a 2nd call to add2 + # Very similar to _getindex_hiprec, but optimized to avoid a 2nd call to add12 @_inline_meta u = i - r.offset shift_hi, shift_lo = u*r.step.hi, u*r.step.lo - x_hi, x_lo = add2(r.ref.hi, shift_hi) + x_hi, x_lo = add12(r.ref.hi, shift_hi) T(x_hi + (x_lo + (shift_lo + r.ref.lo))) end function _getindex_hiprec(r::StepRangeLen{<:Any,<:TwicePrecision,<:TwicePrecision}, i::Integer) u = i - r.offset shift_hi, shift_lo = u*r.step.hi, u*r.step.lo - x_hi, x_lo = add2(r.ref.hi, shift_hi) - x_hi, x_lo = add2(x_hi, x_lo + (shift_lo + r.ref.lo)) + x_hi, x_lo = add12(r.ref.hi, shift_hi) + x_hi, x_lo = add12(x_hi, x_lo + (shift_lo + r.ref.lo)) TwicePrecision(x_hi, x_lo) end function getindex(r::StepRangeLen{T,<:TwicePrecision,<:TwicePrecision}, s::OrdinalRange{<:Integer}) where T - @_inline_meta @boundscheck checkbounds(r, s) soffset = 1 + round(Int, (r.offset - first(s))/step(s)) soffset = clamp(soffset, 1, length(s)) @@ -234,10 +457,10 @@ convert(::Type{StepRangeLen{T,R,S}}, r::StepRangeLen{T,R,S}) where {T<:AbstractF convert(::Type{StepRangeLen{T,R,S}}, r::StepRangeLen) where {T<:AbstractFloat,R<:TwicePrecision,S<:TwicePrecision} = _convertSRL(StepRangeLen{T,R,S}, r) -convert(::Type{StepRangeLen{T}}, r::StepRangeLen) where {T<:Union{Float16,Float32,Float64}} = +convert(::Type{StepRangeLen{T}}, r::StepRangeLen) where {T<:IEEEFloat} = _convertSRL(StepRangeLen{T,TwicePrecision{T},TwicePrecision{T}}, r) -convert(::Type{StepRangeLen{T}}, r::Range) where {T<:Union{Float16,Float32,Float64}} = +convert(::Type{StepRangeLen{T}}, r::Range) where {T<:IEEEFloat} = _convertSRL(StepRangeLen{T,TwicePrecision{T},TwicePrecision{T}}, r) function _convertSRL(::Type{StepRangeLen{T,R,S}}, r::StepRangeLen{<:Integer}) where {T,R,S} @@ -285,12 +508,12 @@ function sum(r::StepRangeLen) sp, sn = sumpair(np), sumpair(nn) tp = _prod(r.step, sp[1], sp[2]) tn = _prod(r.step, sn[1], sn[2]) - s_hi, s_lo = add2(tp.hi, -tn.hi) + s_hi, s_lo = add12(tp.hi, -tn.hi) s_lo += tp.lo - tn.lo # Add in contributions of ref ref = r.ref * l - sm_hi, sm_lo = add2(s_hi, ref.hi) - add2(sm_hi, sm_lo + ref.lo)[1] + sm_hi, sm_lo = add12(s_hi, ref.hi) + add12(sm_hi, sm_lo + ref.lo)[1] end # sum(1:n) as a product of two integers @@ -316,7 +539,7 @@ end ## LinSpace # For Float16, Float32, and Float64, linspace returns a StepRangeLen -function linspace(start::T, stop::T, len::Integer) where T<:Union{Float16,Float32,Float64} +function linspace(start::T, stop::T, len::Integer) where {T<:IEEEFloat} len < 2 && return _linspace1(T, start, stop, len) if start == stop return StepRangeLen(TwicePrecision(start,zero(T)), TwicePrecision(zero(T),zero(T)), len) @@ -338,15 +561,15 @@ function linspace(start::T, stop::T, len::Integer) where T<:Union{Float16,Float3 _linspace(start, stop, len) end -function _linspace(start::T, stop::T, len::Integer) where T<:Union{Float16,Float32,Float64} +function _linspace(start::T, stop::T, len::Integer) where {T<:IEEEFloat} (isfinite(start) && isfinite(stop)) || throw(ArgumentError("start and stop must be finite, got $start and $stop")) # Find the index that returns the smallest-magnitude element Δ, Δfac = stop-start, 1 if !isfinite(Δ) # handle overflow for large endpoints Δ, Δfac = stop/len - start/len, Int(len) end - tmin = -(start/Δ)/Δfac # interpolation t such that return value is 0 - imin = round(Int, tmin*(len-1)+1) + tmin = -(start/Δ)/Δfac # t such that (1-t)*start + t*stop == 0 + imin = round(Int, tmin*(len-1)+1) # index approximately corresponding to t if 1 < imin < len # The smallest-magnitude element is in the interior t = (imin-1)/(len-1) @@ -373,8 +596,8 @@ function _linspace(start::T, stop::T, len::Integer) where T<:Union{Float16,Float step_hi_pre = clamp(step, max(-(m+ref)/k, (-m+ref)/k), min((m-ref)/k, (m+ref)/k)) nb = nbitslen(T, len, imin) step_hi = truncbits(step_hi_pre, nb) - x1_hi, x1_lo = add2((1-imin)*step_hi, ref) - x2_hi, x2_lo = add2((len-imin)*step_hi, ref) + x1_hi, x1_lo = add12((1-imin)*step_hi, ref) + x2_hi, x2_lo = add12((len-imin)*step_hi, ref) a, b = (start - x1_hi) - x1_lo, (stop - x2_hi) - x2_lo step_lo = (b - a)/(len - 1) ref_lo = a - (1 - imin)*step_lo @@ -440,70 +663,11 @@ narrow(::Type{Float64}) = Float32 narrow(::Type{Float32}) = Float16 narrow(::Type{Float16}) = Float16 -function add2(u::T, v::T) where T<:Number - @_inline_meta - u, v = ifelse(abs(v) > abs(u), (v, u), (u, v)) - w = u + v - w, (u-w) + v -end - -add2(u, v) = _add2(promote(u, v)...) -_add2(u::T, v::T) where {T<:Number} = add2(u, v) -_add2(u, v) = error("$u::$(typeof(u)) and $v::$(typeof(v)) cannot be promoted to a common type") - -function +(x::TwicePrecision, y::Number) - s_hi, s_lo = add2(x.hi, y) - TwicePrecision(s_hi, s_lo+x.lo) -end -+(x::Number, y::TwicePrecision) = y+x - -function +(x::TwicePrecision{T}, y::TwicePrecision{T}) where T - r = x.hi + y.hi - s = abs(x.hi) > abs(y.hi) ? (((x.hi - r) + y.hi) + y.lo) + x.lo : (((y.hi - r) + x.hi) + x.lo) + y.lo - TwicePrecision(r, s) -end -+(x::TwicePrecision, y::TwicePrecision) = _add2(promote(x, y)...) -_add2(x::T, y::T) where {T<:TwicePrecision} = x + y -_add2(x::TwicePrecision, y::TwicePrecision) = TwicePrecision(x.hi+y.hi, x.lo+y.lo) - -function *(x::TwicePrecision, v::Integer) - v == 0 && return TwicePrecision(x.hi*v, x.lo*v) - nb = ceil(Int, log2(abs(v))) - u = truncbits(x.hi, nb) - y_hi, y_lo = add2(u*v, ((x.hi-u) + x.lo)*v) - TwicePrecision(y_hi, y_lo) -end - -function _mul2(x::TwicePrecision{T}, v::T) where T<:Union{Float16,Float32,Float64} - v == 0 && return TwicePrecision(T(0), T(0)) - xhh, xhl = splitprec(x.hi) - vh, vl = splitprec(v) - y_hi, y_lo = add2(xhh*vh, xhh*vl + xhl*vh) - TwicePrecision(y_hi, y_lo + xhl*vl + x.lo*v) -end - -_mul2(x::TwicePrecision, v::Number) = TwicePrecision(x.hi*v, x.lo*v) - -function *(x::TwicePrecision{R}, v::S) where R where S<:Number - T = promote_type(R, S) - _mul2(convert(TwicePrecision{T}, x), convert(T, v)) -end - -*(v::Number, x::TwicePrecision) = x*v - -function /(x::TwicePrecision, v::Number) - hi = x.hi/v - w = TwicePrecision(hi, zero(hi)) * v - lo = (((x.hi - w.hi) - w.lo) + x.lo)/v - y_hi, y_lo = add2(hi, lo) - TwicePrecision(y_hi, y_lo) -end - # hi-precision version of prod(num)/prod(den) # num and den are tuples to avoid risk of overflow function proddiv(T, num, den) @_inline_meta - t = TwicePrecision(T(num[1]), zero(T)) + t = TwicePrecision{T}(num[1]) t = _prod(t, tail(num)...) _divt(t, den...) end diff --git a/test/ranges.jl b/test/ranges.jl index 1b04b345d8eab..76ab1384e8755 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -1,6 +1,188 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -# ranges +# Because ranges rely on high precision arithmetic, test those utilities first +for (I, T) in ((Int16, Float16), (Int32, Float32), (Int64, Float64)), i = 1:10^3 + i = rand(I) >> 1 # test large values below + hi, lo = Base.splitprec(T, i) + @test widen(hi) + widen(lo) == i + @test endswith(bits(hi), repeat('0', Base.Math.significand_bits(T) ÷ 2)) +end +for (I, T) in ((Int16, Float16), (Int32, Float32), (Int64, Float64)) + x = T(typemax(I)) + Δi = ceil(I, eps(x)) + for i = typemax(I)-2Δi:typemax(I)-Δi + hi, lo = Base.splitprec(T, i) + @test widen(hi) + widen(lo) == i + @test endswith(bits(hi), repeat('0', Base.Math.significand_bits(T) ÷ 2)) + end + for i = typemin(I):typemin(I)+Δi + hi, lo = Base.splitprec(T, i) + @test widen(hi) + widen(lo) == i + @test endswith(bits(hi), repeat('0', Base.Math.significand_bits(T) ÷ 2)) + end +end + +# Compare precision in a manner sensitive to subnormals, which lose +# precision compared to widening. +function cmp_sn(w, hi, lo, slopbits=0) + if !isfinite(hi) + if abs(w) > realmax(typeof(hi)) + return isinf(hi) && sign(w) == sign(hi) + end + if isnan(w) && isnan(hi) + return true + end + return w == hi + end + if abs(w) < subnormalmin(typeof(hi)) + return (hi == zero(hi) || abs(w - widen(hi)) < abs(w)) && lo == zero(hi) + end + # Compare w == hi + lo unless `lo` issubnormal + z = widen(hi) + widen(lo) + if !issubnormal(lo) && lo != 0 + if slopbits == 0 + return z == w + end + wr, zr = roundshift(w, slopbits), roundshift(z, slopbits) + return max(wr-1, zero(wr)) <= zr <= wr+1 + end + # round w to the same number of bits as z + zu = asbits(z) + wu = asbits(w) + lastbit = false + while zu > 0 && !isodd(zu) + lastbit = isodd(wu) + zu = zu >> 1 + wu = wu >> 1 + end + return wu <= zu <= wu + lastbit +end + +asbits(x) = reinterpret(Base.uinttype(typeof(x)), x) + +function roundshift(x, n) + xu = asbits(x) + lastbit = false + for i = 1:n + lastbit = isodd(xu) + xu = xu >> 1 + end + xu + lastbit +end + +subnormalmin(::Type{T}) where T = reinterpret(T, Base.uinttype(T)(1)) + +function highprec_pair(x, y) + slopbits = (Base.Math.significand_bits(typeof(widen(x))) + 1) - + 2*(Base.Math.significand_bits(typeof(x)) + 1) + hi, lo = Base.add12(x, y) + @test cmp_sn(widen(x) + widen(y), hi, lo) + hi, lo = Base.mul12(x, y) + @test cmp_sn(widen(x) * widen(y), hi, lo) + y == 0 && return nothing + hi, lo = Base.div12(x, y) + @test cmp_sn(widen(x) / widen(y), hi, lo, slopbits) + nothing +end + +# # This tests every possible pair of Float16s. It takes too long for +# # ordinary use, which is why it's commented out. +# function pair16() +# for yu in 0x0000:0xffff +# for xu in 0x0000:0xffff +# x, y = reinterpret(Float16, xu), reinterpret(Float16, yu) +# highprec_pair(x, y) +# end +# end +# end + +for T in (Float16, Float32) # skip Float64 (bit representation of BigFloat is not available) + for i = 1:10^5 + x, y = rand(T), rand(T) + highprec_pair(x, y) + highprec_pair(-x, y) + highprec_pair(x, -y) + highprec_pair(-x, -y) + end + # Make sure we test dynamic range too + for i = 1:10^5 + x, y = rand(T), rand(T) + x == 0 || y == 0 && continue + x, y = log(x), log(y) + highprec_pair(x, y) + end +end + +asww(x) = widen(widen(x.hi)) + widen(widen(x.lo)) +astuple(x) = (x.hi, x.lo) + +function cmp_sn2(w, hi, lo, slopbits=0) + if !isfinite(hi) + if abs(w) > realmax(typeof(hi)) + return isinf(hi) && sign(w) == sign(hi) + end + if isnan(w) && isnan(hi) + return true + end + return w == hi + end + if abs(w) < subnormalmin(typeof(hi)) + return (hi == zero(hi) || abs(w - widen(hi)) < abs(w)) && lo == zero(hi) + end + z = widen(hi) + widen(lo) + zu, wu = asbits(z), asbits(w) + while zu > 0 && !isodd(zu) + zu = zu >> 1 + wu = wu >> 1 + end + zu = zu >> slopbits + wu = wu >> slopbits + return wu - 1 <= zu <= wu + 1 +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) +end + +x1 = Base.TwicePrecision{Float64}(1) +x0 = Base.TwicePrecision{Float64}(0) +xinf = Base.TwicePrecision{Float64}(Inf) +@test Float64(x1+x0) == 1 +@test Float64(x1+0) == 1 +@test Float64(x1+0.0) == 1 +@test Float64(x1*x0) == 0 +@test Float64(x1*0) == 0 +@test Float64(x1*0.0) == 0 +@test Float64(x1/x0) == Inf +@test Float64(x1/0) == Inf +@test Float64(xinf*x1) == Inf +@test isnan(Float64(xinf*x0)) +@test isnan(Float64(xinf*0)) +@test isnan(Float64(xinf*0.0)) +@test isnan(Float64(x0/x0)) +@test isnan(Float64(x0/0)) +@test isnan(Float64(x0/0.0)) + +## ranges @test size(10:1:0) == (0,) @test length(1:.2:2) == 6 @test length(1.:.2:2.) == 6 From b07b94dba75ba93bf73c1ba511d128e19c04d4da Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Wed, 9 Aug 2017 04:18:53 -0500 Subject: [PATCH 077/324] Fix overflow in linspace construction. Fixes #23178 --- base/twiceprecision.jl | 15 ++++++--------- test/ranges.jl | 4 ++++ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/base/twiceprecision.jl b/base/twiceprecision.jl index 16408776b2a17..7e0a3be6ba870 100644 --- a/base/twiceprecision.jl +++ b/base/twiceprecision.jl @@ -612,16 +612,13 @@ function linspace(::Type{T}, start_n::Integer, stop_n::Integer, len::Integer, de tmin = -start_n/(Float64(stop_n) - Float64(start_n)) imin = round(Int, tmin*(len-1)+1) imin = clamp(imin, 1, Int(len)) - # Compute (1-t)*a and t*b separately in 2x precision (itp = interpolant)... - dent = (den, len-1) # represent products as a tuple to eliminate risk of overflow - start_itp = proddiv(T, (len-imin, start_n), dent) - stop_itp = proddiv(T, (imin-1, stop_n), dent) - # ...and then combine them to make ref - ref = start_itp + stop_itp + ref_num = Int128(len-imin) * start_n + Int128(imin-1) * stop_n + ref_denom = Int128(len-1) * den + ref = ratio128(T, ref_num, ref_denom) # Compute step to 2x precision without risking overflow... - rend = proddiv(T, (stop_n,), dent) - rbeg = proddiv(T, (-start_n,), dent) - step = twiceprecision(rbeg + rend, nbitslen(T, len, imin)) # ...and truncate hi-bits as needed + step_full = ratio128(T, Int128(stop_n) - Int128(start_n), ref_denom) + # ...and truncate hi-bits as needed + step = twiceprecision(step_full, nbitslen(T, len, imin)) StepRangeLen(ref, step, Int(len), imin) end diff --git a/test/ranges.jl b/test/ranges.jl index 76ab1384e8755..db1480be0d761 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -1074,6 +1074,10 @@ for i = 2:4 @test r[i] == x end +r = linspace(Float16(0.1094), Float16(0.9697), 300) +@test r[1] == Float16(0.1094) +@test r[end] == Float16(0.9697) + # issue #20382 r = @inferred(colon(big(1.0),big(2.0),big(5.0))) @test eltype(r) == BigFloat From a903c87f433b1b7b8127bb41fc560084cfaef20d Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Tue, 15 Aug 2017 13:21:51 -0500 Subject: [PATCH 078/324] Hit linspace endpoints exactly and fix range tests. Fixes #20521. --- base/twiceprecision.jl | 21 +++------------- test/ranges.jl | 56 ++++++++++++++++++++++-------------------- 2 files changed, 34 insertions(+), 43 deletions(-) diff --git a/base/twiceprecision.jl b/base/twiceprecision.jl index 7e0a3be6ba870..4b3b1df86b9cf 100644 --- a/base/twiceprecision.jl +++ b/base/twiceprecision.jl @@ -614,10 +614,8 @@ function linspace(::Type{T}, start_n::Integer, stop_n::Integer, len::Integer, de imin = clamp(imin, 1, Int(len)) ref_num = Int128(len-imin) * start_n + Int128(imin-1) * stop_n ref_denom = Int128(len-1) * den - ref = ratio128(T, ref_num, ref_denom) - # Compute step to 2x precision without risking overflow... - step_full = ratio128(T, Int128(stop_n) - Int128(start_n), ref_denom) - # ...and truncate hi-bits as needed + ref = TwicePrecision{T}((ref_num, ref_denom)) + step_full = TwicePrecision{T}((Int128(stop_n) - Int128(start_n), ref_denom)) step = twiceprecision(step_full, nbitslen(T, len, imin)) StepRangeLen(ref, step, Int(len), imin) end @@ -660,23 +658,12 @@ narrow(::Type{Float64}) = Float32 narrow(::Type{Float32}) = Float16 narrow(::Type{Float16}) = Float16 -# hi-precision version of prod(num)/prod(den) -# num and den are tuples to avoid risk of overflow -function proddiv(T, num, den) - @_inline_meta - t = TwicePrecision{T}(num[1]) - t = _prod(t, tail(num)...) - _divt(t, den...) -end function _prod(t::TwicePrecision, x, y...) @_inline_meta _prod(t * x, y...) end _prod(t::TwicePrecision) = t -function _divt(t::TwicePrecision, x, y...) - @_inline_meta - _divt(t / x, y...) -end -_divt(t::TwicePrecision) = t +<(x::TwicePrecision{T}, y::TwicePrecision{T}) where {T} = + x.hi < y.hi || ((x.hi == y.hi) & (x.lo < y.lo)) isbetween(a, x, b) = a <= x <= b || b <= x <= a diff --git a/test/ranges.jl b/test/ranges.jl index db1480be0d761..0cebbc644810b 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -520,11 +520,11 @@ end for T = (Float32, Float64,),# BigFloat), a = -5:25, s = [-5:-1;1:25;], d = 1:25, n = -1:15 - den = convert(T,d) - start = convert(T,a)/den - step = convert(T,s)/den - stop = convert(T,(a+(n-1)*s))/den - vals = T[a:s:a+(n-1)*s;]./den + denom = convert(T,d) + start = convert(T,a)/denom + step = convert(T,s)/denom + stop = convert(T,(a+(n-1)*s))/denom + vals = T[a:s:a+(n-1)*s;]./denom r = start:step:stop @test [r;] == vals @test [linspace(start, stop, length(r));] == vals @@ -544,27 +544,31 @@ end @test [-3*0.05:-0.05:-0.2;] == [linspace(-3*0.05,-0.2,2);] == [-3*0.05,-0.2] @test [-0.2:0.05:-3*0.05;] == [linspace(-0.2,-3*0.05,2);] == [-0.2,-3*0.05] -for T = (Float32, Float64,), i = 1:2^15, n = 1:5 - start, step = randn(T), randn(T) - step == 0 && continue - stop = start + (n-1)*step - # `n` is not necessarily unique s.t. `start + (n-1)*step == stop` - # so test that `length(start:step:stop)` satisfies this identity - # and is the closest value to `(stop-start)/step` to do so - lo = hi = n - while start + (lo-1)*step == stop; lo -= 1; end - while start + (hi-1)*step == stop; hi += 1; end - m = clamp(round(Int, (stop-start)/step) + 1, lo+1, hi-1) - r = start:step:stop - @test m == length(r) - # FIXME: these fail some small portion of the time - @test_skip start == first(r) - @test_skip stop == last(r) - l = linspace(start,stop,n) - @test n == length(l) - # FIXME: these fail some small portion of the time - @test_skip start == first(l) - @test_skip stop == last(l) +function range_fuzztests(::Type{T}, niter, nrange) where {T} + for i = 1:niter, n in nrange + start, Δ = randn(T), randn(T) + Δ == 0 && continue + stop = start + (n-1)*Δ + # `n` is not necessarily unique s.t. `start + (n-1)*Δ == stop` + # so test that `length(start:Δ:stop)` satisfies this identity + # and is the closest value to `(stop-start)/Δ` to do so + lo = hi = n + while start + (lo-1)*Δ == stop; lo -= 1; end + while start + (hi-1)*Δ == stop; hi += 1; end + m = clamp(round(Int, (stop-start)/Δ) + 1, lo+1, hi-1) + r = start:Δ:stop + @test m == length(r) + @test start == first(r) + @test Δ == step(r) + @test_skip stop == last(r) + l = linspace(start,stop,n) + @test n == length(l) + @test start == first(l) + @test stop == last(l) + end +end +for T = (Float32, Float64,) + range_fuzztests(T, 2^15, 1:5) end # Inexact errors on 32 bit architectures. #22613 From a698230daf6ed8413bb728dae3f6eb0cec4a78f4 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Tue, 15 Aug 2017 15:29:52 -0500 Subject: [PATCH 079/324] Ranges: use Float64 rather than TwicePrecision for Float16, Float32 --- base/float.jl | 3 +- base/range.jl | 46 ++++++++++++++-------- base/twiceprecision.jl | 87 +++++++++++++++++++++++++++--------------- test/ranges.jl | 35 ++++++++--------- 4 files changed, 107 insertions(+), 64 deletions(-) diff --git a/base/float.jl b/base/float.jl index 23c0db43c37af..4d65e56be4ded 100644 --- a/base/float.jl +++ b/base/float.jl @@ -885,7 +885,8 @@ end float(r::StepRange) = float(r.start):float(r.step):float(last(r)) float(r::UnitRange) = float(r.start):float(last(r)) -float(r::StepRangeLen) = StepRangeLen(float(r.ref), float(r.step), length(r), r.offset) +float(r::StepRangeLen{T}) where {T} = + StepRangeLen{typeof(float(T(r.ref)))}(float(r.ref), float(r.step), length(r), r.offset) function float(r::LinSpace) LinSpace(float(r.start), float(r.stop), length(r)) end diff --git a/base/range.jl b/base/range.jl index 381efd9ea5f2f..5bf8a2896767c 100644 --- a/base/range.jl +++ b/base/range.jl @@ -205,6 +205,8 @@ end StepRangeLen(ref::R, step::S, len::Integer, offset::Integer = 1) where {R,S} = StepRangeLen{typeof(ref+0*step),R,S}(ref, step, len, offset) +StepRangeLen{T}(ref::R, step::S, len::Integer, offset::Integer = 1) where {T,R,S} = + StepRangeLen{T,R,S}(ref, step, len, offset) ## linspace and logspace @@ -370,9 +372,12 @@ julia> step(linspace(2.5,10.9,85)) """ step(r::StepRange) = r.step step(r::AbstractUnitRange) = 1 -step(r::StepRangeLen) = r.step +step(r::StepRangeLen{T}) where {T} = T(r.step) step(r::LinSpace) = (last(r)-first(r))/r.lendiv +step_hp(r::StepRangeLen) = r.step +step_hp(r::Range) = step(r) + unsafe_length(r::Range) = length(r) # generic fallback function unsafe_length(r::StepRange) @@ -455,10 +460,9 @@ done(r::StepRange, i) = isempty(r) | (i < min(r.start, r.stop)) | (i > max(r.sta done(r::StepRange, i::Integer) = isempty(r) | (i == oftype(i, r.stop) + r.step) -# see also twiceprecision.jl -start(r::StepRangeLen) = (unsafe_getindex(r, 1), 1) -next(r::StepRangeLen{T}, s) where {T} = s[1], (T(s[1]+r.step), s[2]+1) -done(r::StepRangeLen, s) = s[2] > length(r) +start(r::StepRangeLen) = 1 +next(r::StepRangeLen{T}, i) where {T} = unsafe_getindex(r, i), i+1 +done(r::StepRangeLen, i) = i > length(r) start(r::UnitRange{T}) where {T} = oftype(r.start + oneunit(T), r.start) next(r::AbstractUnitRange{T}, i) where {T} = (convert(T, i), i + oneunit(T)) @@ -496,7 +500,7 @@ end function getindex(v::Range{T}, i::Integer) where T @_inline_meta - ret = convert(T, first(v) + (i - 1)*step(v)) + ret = convert(T, first(v) + (i - 1)*step_hp(v)) ok = ifelse(step(v) > zero(step(v)), (ret <= v.stop) & (ret >= v.start), (ret <= v.start) & (ret >= v.stop)) @@ -516,6 +520,11 @@ function unsafe_getindex(r::StepRangeLen{T}, i::Integer) where T T(r.ref + u*r.step) end +function _getindex_hiprec(r::StepRangeLen, i::Integer) # without rounding by T + u = i - r.offset + r.ref + u*r.step +end + function unsafe_getindex(r::LinSpace, i::Integer) lerpi.(i-1, r.lendiv, r.start, r.stop) end @@ -556,11 +565,14 @@ function getindex(r::StepRange, s::Range{<:Integer}) range(st, step(r)*step(s), length(s)) end -function getindex(r::StepRangeLen, s::OrdinalRange{<:Integer}) +function getindex(r::StepRangeLen{T}, s::OrdinalRange{<:Integer}) where {T} @_inline_meta @boundscheck checkbounds(r, s) - vfirst = unsafe_getindex(r, first(s)) - return StepRangeLen(vfirst, r.step*step(s), length(s)) + # Find closest approach to offset by s + ind = linearindices(s) + offset = max(min(1 + round(Int, (r.offset - first(s))/step(s)), last(ind)), first(ind)) + ref = _getindex_hiprec(r, first(s) + (offset-1)*step(s)) + return StepRangeLen{T}(ref, r.step*step(s), length(s), offset) end function getindex(r::LinSpace, s::OrdinalRange{<:Integer}) @@ -725,16 +737,17 @@ end ## linear operations on ranges ## -(r::OrdinalRange) = range(-first(r), -step(r), length(r)) --(r::StepRangeLen) = StepRangeLen(-r.ref, -r.step, length(r), r.offset) +-(r::StepRangeLen{T,R,S}) where {T,R,S} = + StepRangeLen{T,R,S}(-r.ref, -r.step, length(r), r.offset) -(r::LinSpace) = LinSpace(-r.start, -r.stop, length(r)) +(x::Real, r::AbstractUnitRange) = range(x + first(r), length(r)) # For #18336 we need to prevent promotion of the step type: +(x::Number, r::AbstractUnitRange) = range(x + first(r), step(r), length(r)) +(x::Number, r::Range) = (x+first(r)):step(r):(x+last(r)) -function +(x::Number, r::StepRangeLen) +function +(x::Number, r::StepRangeLen{T}) where T newref = x + r.ref - StepRangeLen{eltype(newref),typeof(newref),typeof(r.step)}(newref, r.step, length(r), r.offset) + StepRangeLen{typeof(T(r.ref) + x)}(newref, r.step, length(r), r.offset) end function +(x::Number, r::LinSpace) LinSpace(x + r.start, x + r.stop, r.len) @@ -750,15 +763,18 @@ end -(r::Range, x::Number) = +(-x, r) *(x::Number, r::Range) = range(x*first(r), x*step(r), length(r)) -*(x::Number, r::StepRangeLen) = StepRangeLen(x*r.ref, x*r.step, length(r), r.offset) +*(x::Number, r::StepRangeLen{T}) where {T} = + StepRangeLen{typeof(x*T(r.ref))}(x*r.ref, x*r.step, length(r), r.offset) *(x::Number, r::LinSpace) = LinSpace(x * r.start, x * r.stop, r.len) # separate in case of noncommutative multiplication *(r::Range, x::Number) = range(first(r)*x, step(r)*x, length(r)) -*(r::StepRangeLen, x::Number) = StepRangeLen(r.ref*x, r.step*x, length(r), r.offset) +*(r::StepRangeLen{T}, x::Number) where {T} = + StepRangeLen{typeof(T(r.ref)*x)}(r.ref*x, r.step*x, length(r), r.offset) *(r::LinSpace, x::Number) = LinSpace(r.start * x, r.stop * x, r.len) /(r::Range, x::Number) = range(first(r)/x, step(r)/x, length(r)) -/(r::StepRangeLen, x::Number) = StepRangeLen(r.ref/x, r.step/x, length(r), r.offset) +/(r::StepRangeLen{T}, x::Number) where {T} = + StepRangeLen{typeof(T(r.ref)/x)}(r.ref/x, r.step/x, length(r), r.offset) /(r::LinSpace, x::Number) = LinSpace(r.start / x, r.stop / x, r.len) /(x::Number, r::Range) = [ x/y for y=r ] diff --git a/base/twiceprecision.jl b/base/twiceprecision.jl index 4b3b1df86b9cf..1baf1b9a921bd 100644 --- a/base/twiceprecision.jl +++ b/base/twiceprecision.jl @@ -308,23 +308,57 @@ end ## StepRangeLen -# If using TwicePrecision numbers, deliberately force user to specify offset -StepRangeLen(ref::TwicePrecision{T}, step::TwicePrecision{T}, len::Integer, offset::Integer) where {T} = +# Use TwicePrecision only for Float64; use Float64 for T<:Union{Float16,Float32} +# Ratio-of-integers constructors +function steprangelen_hp(::Type{Float64}, ref::Tuple{Integer,Integer}, + step::Tuple{Integer,Integer}, nb::Integer, + len::Integer, offset::Integer) + StepRangeLen(TwicePrecision{Float64}(ref), + TwicePrecision{Float64}(step, nb), Int(len), offset) +end + +function steprangelen_hp(::Type{T}, ref::Tuple{Integer,Integer}, + step::Tuple{Integer,Integer}, nb::Integer, + len::Integer, offset::Integer) where {T<:IEEEFloat} + StepRangeLen{T}(ref[1]/ref[2], step[1]/step[2], Int(len), offset) +end + +# AbstractFloat constructors (can supply a single number or a 2-tuple +const F_or_FF = Union{AbstractFloat, Tuple{AbstractFloat,AbstractFloat}} +asF64(x::AbstractFloat) = Float64(x) +asF64(x::Tuple{AbstractFloat,AbstractFloat}) = Float64(x[1]) + Float64(x[2]) + +function steprangelen_hp(::Type{Float64}, ref::F_or_FF, + step::F_or_FF, nb::Integer, + len::Integer, offset::Integer) + StepRangeLen(TwicePrecision{Float64}(ref...), + twiceprecision(TwicePrecision{Float64}(step...), nb), Int(len), offset) +end + +function steprangelen_hp(::Type{T}, ref::F_or_FF, + step::F_or_FF, nb::Integer, + len::Integer, offset::Integer) where {T<:IEEEFloat} + StepRangeLen{T}(asF64(ref), + asF64(step), Int(len), offset) +end + + + +StepRangeLen(ref::TwicePrecision{T}, step::TwicePrecision{T}, + len::Integer, offset::Integer=1) where {T} = StepRangeLen{T,TwicePrecision{T},TwicePrecision{T}}(ref, step, len, offset) # Construct range for rational start=start_n/den, step=step_n/den function floatrange(::Type{T}, start_n::Integer, step_n::Integer, len::Integer, den::Integer) where T if len < 2 - return StepRangeLen(TwicePrecision{T}((start_n, den)), - TwicePrecision{T}((step_n, den)), Int(len), 1) + return steprangelen_hp(T, (start_n, den), (step_n, den), 0, Int(len), 1) end # index of smallest-magnitude value imin = clamp(round(Int, -start_n/step_n+1), 1, Int(len)) # Compute smallest-magnitude element to 2x precision ref_n = start_n+(imin-1)*step_n # this shouldn't overflow, so don't check nb = nbitslen(T, len, imin) - StepRangeLen(TwicePrecision{T}((ref_n, den)), - TwicePrecision{T}((step_n, den), nb), Int(len), imin) + steprangelen_hp(T, (ref_n, den), (step_n, den), nb, Int(len), imin) end function floatrange(a::AbstractFloat, st::AbstractFloat, len::Real, divisor::AbstractFloat) @@ -339,8 +373,7 @@ function floatrange(a::AbstractFloat, st::AbstractFloat, len::Real, divisor::Abs end # Fallback (misses the opportunity to set offset different from 1, # but otherwise this is still high-precision) - StepRangeLen(TwicePrecision{T}((a,divisor)), - TwicePrecision{T}((st,divisor), nbitslen(T, len, 1)), Int(len), 1) + steprangelen_hp(T, (a,divisor), (st,divisor), nbitslen(T, len, 1), Int(len), 1) end function colon(start::T, step::T, stop::T) where T<:Union{Float16,Float32,Float64} @@ -381,8 +414,7 @@ function colon(start::T, step::T, stop::T) where T<:Union{Float16,Float32,Float6 # if we've overshot the end, subtract one: len -= (start < stop < stop′) + (start > stop > stop′) end - - StepRangeLen(TwicePrecision(start, zero(T)), twiceprecision(step, nbitslen(T, len, 1)), len) + steprangelen_hp(T, start, step, 0, len, 1) end function range(a::T, st::T, len::Integer) where T<:Union{Float16,Float32,Float64} @@ -399,16 +431,7 @@ function range(a::T, st::T, len::Integer) where T<:Union{Float16,Float32,Float64 return floatrange(T, start_n, step_n, len, den) end end - StepRangeLen(TwicePrecision(a, zero(T)), TwicePrecision(st, zero(T)), len) -end - -step(r::StepRangeLen{T,R,S}) where {T,R,S<:TwicePrecision} = convert(eltype(S), r.step) - -start(r::StepRangeLen{<:Any,<:TwicePrecision,<:TwicePrecision}) = 1 -done(r::StepRangeLen{<:Any,<:TwicePrecision,<:TwicePrecision}, i::Int) = length(r) < i -function next(r::StepRangeLen{<:Any,<:TwicePrecision,<:TwicePrecision}, i::Int) - @_inline_meta - unsafe_getindex(r, i), i+1 + steprangelen_hp(T, a, st, 0, len, 1) end # This assumes that r.step has already been split so that (0:len-1)*r.step.hi is exact @@ -457,11 +480,15 @@ convert(::Type{StepRangeLen{T,R,S}}, r::StepRangeLen{T,R,S}) where {T<:AbstractF convert(::Type{StepRangeLen{T,R,S}}, r::StepRangeLen) where {T<:AbstractFloat,R<:TwicePrecision,S<:TwicePrecision} = _convertSRL(StepRangeLen{T,R,S}, r) +convert(::Type{StepRangeLen{Float64}}, r::StepRangeLen) = + _convertSRL(StepRangeLen{Float64,TwicePrecision{Float64},TwicePrecision{Float64}}, r) convert(::Type{StepRangeLen{T}}, r::StepRangeLen) where {T<:IEEEFloat} = - _convertSRL(StepRangeLen{T,TwicePrecision{T},TwicePrecision{T}}, r) + _convertSRL(StepRangeLen{T,Float64,Float64}, r) +convert(::Type{StepRangeLen{Float64}}, r::Range) = + _convertSRL(StepRangeLen{Float64,TwicePrecision{Float64},TwicePrecision{Float64}}, r) convert(::Type{StepRangeLen{T}}, r::Range) where {T<:IEEEFloat} = - _convertSRL(StepRangeLen{T,TwicePrecision{T},TwicePrecision{T}}, r) + _convertSRL(StepRangeLen{T,Float64,Float64}, r) function _convertSRL(::Type{StepRangeLen{T,R,S}}, r::StepRangeLen{<:Integer}) where {T,R,S} StepRangeLen{T,R,S}(R(r.ref), S(r.step), length(r), r.offset) @@ -542,7 +569,7 @@ end function linspace(start::T, stop::T, len::Integer) where {T<:IEEEFloat} len < 2 && return _linspace1(T, start, stop, len) if start == stop - return StepRangeLen(TwicePrecision(start,zero(T)), TwicePrecision(zero(T),zero(T)), len) + return steprangelen_hp(T, start, zero(T), 0, len, 1) end # Attempt to find exact rational approximations start_n, start_d = rat(start) @@ -587,8 +614,7 @@ function _linspace(start::T, stop::T, len::Integer) where {T<:IEEEFloat} if len == 2 && !isfinite(step) # For very large endpoints where step overflows, exploit the # split-representation to handle the overflow - return StepRangeLen(TwicePrecision(start, zero(T)), - TwicePrecision(-start, stop), 2) + return steprangelen_hp(T, start, (-start, stop), 0, 2, 1) end # 2x calculations to get high precision endpoint matching while also # preventing overflow in ref_hi+(i-offset)*step_hi @@ -601,23 +627,22 @@ function _linspace(start::T, stop::T, len::Integer) where {T<:IEEEFloat} a, b = (start - x1_hi) - x1_lo, (stop - x2_hi) - x2_lo step_lo = (b - a)/(len - 1) ref_lo = a - (1 - imin)*step_lo - StepRangeLen(TwicePrecision(ref, ref_lo), TwicePrecision(step_hi, step_lo), Int(len), imin) + steprangelen_hp(T, (ref, ref_lo), (step_hi, step_lo), 0, Int(len), imin) end # linspace for rational numbers, start = start_n/den, stop = stop_n/den # Note this returns a StepRangeLen function linspace(::Type{T}, start_n::Integer, stop_n::Integer, len::Integer, den::Integer) where T len < 2 && return _linspace1(T, start_n/den, stop_n/den, len) - start_n == stop_n && return StepRangeLen(TwicePrecision{T}((start_n, den)), zero(TwicePrecision{T}), len) + start_n == stop_n && return steprangelen_hp(T, (start_n, den), (zero(start_n), den), 0, len) tmin = -start_n/(Float64(stop_n) - Float64(start_n)) imin = round(Int, tmin*(len-1)+1) imin = clamp(imin, 1, Int(len)) ref_num = Int128(len-imin) * start_n + Int128(imin-1) * stop_n ref_denom = Int128(len-1) * den - ref = TwicePrecision{T}((ref_num, ref_denom)) - step_full = TwicePrecision{T}((Int128(stop_n) - Int128(start_n), ref_denom)) - step = twiceprecision(step_full, nbitslen(T, len, imin)) - StepRangeLen(ref, step, Int(len), imin) + ref = (ref_num, ref_denom) + step_full = (Int128(stop_n) - Int128(start_n), ref_denom) + steprangelen_hp(T, ref, step_full, nbitslen(T, len, imin), Int(len), imin) end # For len < 2 diff --git a/test/ranges.jl b/test/ranges.jl index 0cebbc644810b..a953599223ad2 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -521,13 +521,13 @@ end for T = (Float32, Float64,),# BigFloat), a = -5:25, s = [-5:-1;1:25;], d = 1:25, n = -1:15 denom = convert(T,d) - start = convert(T,a)/denom - step = convert(T,s)/denom + strt = convert(T,a)/denom + Δ = convert(T,s)/denom stop = convert(T,(a+(n-1)*s))/denom vals = T[a:s:a+(n-1)*s;]./denom - r = start:step:stop + r = strt:Δ:stop @test [r;] == vals - @test [linspace(start, stop, length(r));] == vals + @test [linspace(strt, stop, length(r));] == vals # issue #7420 n = length(r) @test [r[1:n];] == [r;] @@ -546,24 +546,24 @@ end function range_fuzztests(::Type{T}, niter, nrange) where {T} for i = 1:niter, n in nrange - start, Δ = randn(T), randn(T) + strt, Δ = randn(T), randn(T) Δ == 0 && continue - stop = start + (n-1)*Δ - # `n` is not necessarily unique s.t. `start + (n-1)*Δ == stop` - # so test that `length(start:Δ:stop)` satisfies this identity - # and is the closest value to `(stop-start)/Δ` to do so + stop = strt + (n-1)*Δ + # `n` is not necessarily unique s.t. `strt + (n-1)*Δ == stop` + # so test that `length(strt:Δ:stop)` satisfies this identity + # and is the closest value to `(stop-strt)/Δ` to do so lo = hi = n - while start + (lo-1)*Δ == stop; lo -= 1; end - while start + (hi-1)*Δ == stop; hi += 1; end - m = clamp(round(Int, (stop-start)/Δ) + 1, lo+1, hi-1) - r = start:Δ:stop + while strt + (lo-1)*Δ == stop; lo -= 1; end + while strt + (hi-1)*Δ == stop; hi += 1; end + m = clamp(round(Int, (stop-strt)/Δ) + 1, lo+1, hi-1) + r = strt:Δ:stop @test m == length(r) - @test start == first(r) + @test strt == first(r) @test Δ == step(r) @test_skip stop == last(r) - l = linspace(start,stop,n) + l = linspace(strt,stop,n) @test n == length(l) - @test start == first(l) + @test strt == first(l) @test stop == last(l) end end @@ -1078,6 +1078,7 @@ for i = 2:4 @test r[i] == x end +# issue #23178 r = linspace(Float16(0.1094), Float16(0.9697), 300) @test r[1] == Float16(0.1094) @test r[end] == Float16(0.9697) @@ -1148,7 +1149,7 @@ end end end -# Issue #23300 +# issue #23300 x = -5:big(1.0):5 @test map(Float64, x) === -5.0:1.0:5.0 @test map(Float32, x) === -5.0f0:1.0f0:5.0f0 From 953bfc3da6d3e3bbf70ac65f045558e2ff5f3677 Mon Sep 17 00:00:00 2001 From: Jarrett Revels Date: Sun, 20 Aug 2017 10:31:55 -0400 Subject: [PATCH 080/324] add optional argument to code_lowered to enable generator expansion (#22979) --- base/reflection.jl | 65 ++++++++++++++++++++++++++++++++++------------ test/reflection.jl | 29 +++++++++++++++++++++ 2 files changed, 77 insertions(+), 17 deletions(-) diff --git a/base/reflection.jl b/base/reflection.jl index f654458446948..50e26e1cc040c 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -558,20 +558,43 @@ function to_tuple_type(@nospecialize(t)) t end -tt_cons(@nospecialize(t), @nospecialize(tup)) = (@_pure_meta; Tuple{t, (isa(tup, Type) ? tup.parameters : tup)...}) +function signature_type(@nospecialize(f), @nospecialize(args)) + f_type = isa(f, Type) ? Type{f} : typeof(f) + arg_types = isa(args, Type) ? args.parameters : args + return Tuple{f_type, arg_types...} +end """ - code_lowered(f, types) + code_lowered(f, types, expand_generated = true) + +Return an array of lowered ASTs for the methods matching the given generic function and type signature. + +If `expand_generated` is `false`, then the `CodeInfo` instances returned for `@generated` +methods will correspond to the generators' lowered ASTs. If `expand_generated` is `true`, +these `CodeInfo` instances will correspond to the lowered ASTs of the method bodies yielded +by expanding the generators. -Returns an array of lowered ASTs for the methods matching the given generic function and type signature. +Note that an error will be thrown if `types` are not leaf types when `expand_generated` is +`true` and the corresponding method is a `@generated` method. """ -function code_lowered(@nospecialize(f), @nospecialize t = Tuple) - asts = map(methods(f, t)) do m - return uncompressed_ast(m::Method) +function code_lowered(@nospecialize(f), @nospecialize(t = Tuple), expand_generated::Bool = true) + return map(method_instances(f, t)) do m + if expand_generated && isgenerated(m) + if isa(m, Core.MethodInstance) + return Core.Inference.get_staged(m) + else # isa(m, Method) + error("Could not expand generator for `@generated` method ", m, ". ", + "This can happen if the provided argument types (", t, ") are ", + "not leaf types, but the `expand_generated` argument is `true`.") + end + end + return uncompressed_ast(m) end - return asts end +isgenerated(m::Method) = isdefined(m, :generator) +isgenerated(m::Core.MethodInstance) = isgenerated(m.def) + # low-level method lookup functions used by the compiler unionlen(x::Union) = unionlen(x.a) + unionlen(x.b) @@ -582,8 +605,7 @@ _uniontypes(@nospecialize(x), ts) = (push!(ts, x); ts) uniontypes(@nospecialize(x)) = _uniontypes(x, Any[]) function _methods(@nospecialize(f), @nospecialize(t), lim::Int, world::UInt) - ft = isa(f,Type) ? Type{f} : typeof(f) - tt = isa(t,Type) ? Tuple{ft, t.parameters...} : Tuple{ft, t...} + tt = signature_type(f, t) return _methods_by_ftype(tt, lim, world) end @@ -635,8 +657,7 @@ end methods(f::Core.Builtin) = MethodList(Method[], typeof(f).name.mt) function methods_including_ambiguous(@nospecialize(f), @nospecialize(t)) - ft = isa(f,Type) ? Type{f} : typeof(f) - tt = isa(t,Type) ? Tuple{ft, t.parameters...} : Tuple{ft, t...} + tt = signature_type(f, t) world = typemax(UInt) min = UInt[typemin(UInt)] max = UInt[typemax(UInt)] @@ -686,9 +707,21 @@ function length(mt::MethodTable) end isempty(mt::MethodTable) = (mt.defs === nothing) -uncompressed_ast(m::Method) = uncompressed_ast(m, isdefined(m,:source) ? m.source : m.generator.inferred) +uncompressed_ast(m::Method) = uncompressed_ast(m, isdefined(m, :source) ? m.source : m.generator.inferred) uncompressed_ast(m::Method, s::CodeInfo) = s uncompressed_ast(m::Method, s::Array{UInt8,1}) = ccall(:jl_uncompress_ast, Any, (Any, Any), m, s)::CodeInfo +uncompressed_ast(m::Core.MethodInstance) = uncompressed_ast(m.def) + +function method_instances(@nospecialize(f), @nospecialize(t), world::UInt = typemax(UInt)) + tt = signature_type(f, t) + results = Vector{Union{Method,Core.MethodInstance}}() + for method_data in _methods_by_ftype(tt, -1, world) + mtypes, msp, m = method_data + instance = Core.Inference.code_for_method(m, mtypes, msp, world, false) + push!(results, ifelse(isa(instance, Core.MethodInstance), instance, m)) + end + return results +end # this type mirrors jl_cghooks_t (documented in julia.h) struct CodegenHooks @@ -739,8 +772,7 @@ function _dump_function(@nospecialize(f), @nospecialize(t), native::Bool, wrappe world = typemax(UInt) meth = which(f, t) t = to_tuple_type(t) - ft = isa(f, Type) ? Type{f} : typeof(f) - tt = Tuple{ft, t.parameters...} + tt = signature_type(f, t) (ti, env) = ccall(:jl_type_intersection_with_env, Any, (Any, Any), tt, meth.sig)::SimpleVector meth = func_for_method_checked(meth, ti) linfo = ccall(:jl_specializations_get_linfo, Ref{Core.MethodInstance}, (Any, Any, Any, UInt), meth, ti, env, world) @@ -871,8 +903,7 @@ function which(@nospecialize(f), @nospecialize(t)) length(ms)!=1 && error("no unique matching method for the specified argument types") return first(ms) else - ft = isa(f,Type) ? Type{f} : typeof(f) - tt = Tuple{ft, t.parameters...} + tt = signature_type(f, t) m = ccall(:jl_gf_invoke_lookup, Any, (Any, UInt), tt, typemax(UInt)) if m === nothing error("no method found for the specified argument types") @@ -979,7 +1010,7 @@ true """ function method_exists(@nospecialize(f), @nospecialize(t), world=typemax(UInt)) t = to_tuple_type(t) - t = Tuple{isa(f,Type) ? Type{f} : typeof(f), t.parameters...} + t = signature_type(f, t) return ccall(:jl_method_exists, Cint, (Any, Any, UInt), typeof(f).name.mt, t, world) != 0 end diff --git a/test/reflection.jl b/test/reflection.jl index b77e7d4e778a8..d9629147deef7 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -714,3 +714,32 @@ end @test_throws ErrorException fieldcount(Real) @test_throws ErrorException fieldcount(AbstractArray) @test_throws ErrorException fieldcount(Tuple{Any,Vararg{Any}}) + +# PR #22979 + +function test_similar_codeinfo(a, b) + @test a.code == b.code + @test a.slotnames == b.slotnames + @test a.slotflags == b.slotflags +end + +@generated f22979(x...) = (y = 1; :(x[1] + x[2])) +x22979 = (1, 2.0, 3.0 + im) +T22979 = Tuple{typeof(f22979),typeof.(x22979)...} +world = typemax(UInt) +mtypes, msp, m = Base._methods_by_ftype(T22979, -1, world)[] +instance = Core.Inference.code_for_method(m, mtypes, msp, world, false) +cinfo_generated = Core.Inference.get_staged(instance) +cinfo_ungenerated = Base.uncompressed_ast(m) + +test_similar_codeinfo(@code_lowered(f22979(x22979...)), cinfo_generated) + +cinfos = code_lowered(f22979, typeof.(x22979), true) +@test length(cinfos) == 1 +cinfo = cinfos[] +test_similar_codeinfo(cinfo, cinfo_generated) + +cinfos = code_lowered(f22979, typeof.(x22979), false) +@test length(cinfos) == 1 +cinfo = cinfos[] +test_similar_codeinfo(cinfo, cinfo_ungenerated) From 1323561dac0f5fc2b26de36409e155fbd3e99c6b Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Sun, 20 Aug 2017 10:33:41 -0700 Subject: [PATCH 081/324] Examples for path fxns? (#23362) --- base/path.jl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/base/path.jl b/base/path.jl index c042bef833e1f..eb118b3d78541 100644 --- a/base/path.jl +++ b/base/path.jl @@ -88,6 +88,7 @@ end Determines whether a path is absolute (begins at the root directory). +# Examples ```jldoctest julia> isabspath("/home") true @@ -103,6 +104,7 @@ isabspath(path::AbstractString) Determines whether a path refers to a directory (for example, ends with a path separator). +# Examples ```jldoctest julia> isdirpath("/home") false @@ -118,6 +120,7 @@ isdirpath(path::String) = ismatch(path_directory_re, splitdrive(path)[2]) Split a path into a tuple of the directory name and file name. +# Examples ```jldoctest julia> splitdir("/home/myuser") ("/home", "myuser") @@ -136,6 +139,7 @@ end Get the directory part of a path. +# Examples ```jldoctest julia> dirname("/home/myuser") "/home" @@ -150,6 +154,7 @@ See also: [`basename`](@ref) Get the file name part of a path. +# Examples ```jldoctest julia> basename("/home/myuser/example.jl") "example.jl" @@ -166,6 +171,7 @@ If the last component of a path contains a dot, split the path into everything b dot and everything including and after the dot. Otherwise, return a tuple of the argument unmodified and the empty string. +# Examples ```jldoctest julia> splitext("/home/myuser/example.jl") ("/home/myuser/example", ".jl") @@ -197,6 +203,7 @@ joinpath(a::AbstractString) = a Join path components into a full path. If some argument is an absolute path, then prior components are dropped. +# Examples ```jldoctest julia> joinpath("/home/myuser","example.jl") "/home/myuser/example.jl" @@ -221,6 +228,7 @@ joinpath(a::AbstractString, b::AbstractString) = joinpath(String(a), String(b)) Normalize a path, removing "." and ".." entries. +# Examples ```jldoctest julia> normpath("/home/myuser/../example.jl") "/home/example.jl" From 8f5f981a094e1f85290e8d394337a181a849bccf Mon Sep 17 00:00:00 2001 From: Mus M Date: Sun, 20 Aug 2017 18:15:41 -0400 Subject: [PATCH 082/324] Add patch to MPFR version (#23287) --- base/mpfr.jl | 7 +++++-- test/mpfr.jl | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/base/mpfr.jl b/base/mpfr.jl index 29644bd2e7bf4..d872e62e849d9 100644 --- a/base/mpfr.jl +++ b/base/mpfr.jl @@ -27,8 +27,11 @@ import Base.Math.lgamma_r import Base.FastMath.sincos_fast -get_version() = VersionNumber(unsafe_string(ccall((:mpfr_get_version,:libmpfr), Ptr{Cchar}, ()))) -get_patches() = split(unsafe_string(ccall((:mpfr_get_patches,:libmpfr), Ptr{Cchar}, ())),' ') +function get_version() + version = unsafe_string(ccall((:mpfr_get_version,:libmpfr), Ptr{Cchar}, ())) + build = replace(unsafe_string(ccall((:mpfr_get_patches,:libmpfr), Ptr{Cchar}, ())), ' ', '.') + isempty(build) ? VersionNumber(version) : VersionNumber(version * '+' * build) +end function __init__() try diff --git a/test/mpfr.jl b/test/mpfr.jl index 3344b4cc4a9bb..fbb6c0cfe016c 100644 --- a/test/mpfr.jl +++ b/test/mpfr.jl @@ -887,7 +887,7 @@ setprecision(256) do end # issue #22758 -if MPFR.get_version() > v"3.1.5" || "r11590" in MPFR.get_patches() +if MPFR.get_version() > v"3.1.5" || "r11590" in MPFR.get_version().build setprecision(2_000_000) do @test abs(sin(big(pi)/6) - 0.5) < ldexp(big(1.0),-1_999_000) end From 7a1156c0104f8a02064bfdd18c8b44f1f177037f Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Sun, 20 Aug 2017 15:48:43 -0700 Subject: [PATCH 083/324] A bunch more doctests for string funcs (#23361) --- base/strings/utf8proc.jl | 93 ++++++++++++++++++++++++++++++++++++++++ base/strings/util.jl | 12 ++++++ 2 files changed, 105 insertions(+) diff --git a/base/strings/utf8proc.jl b/base/strings/utf8proc.jl index 434ceb06bf85e..2cf9bfdb51d24 100644 --- a/base/strings/utf8proc.jl +++ b/base/strings/utf8proc.jl @@ -212,6 +212,15 @@ end charwidth(c) Gives the number of columns needed to print a character. + +# Examples +```jldoctest +julia> charwidth('α') +1 + +julia> charwidth('❤') +2 +``` """ charwidth(c::Char) = Int(ccall(:utf8proc_charwidth, Cint, (UInt32,), c)) @@ -243,6 +252,18 @@ is_assigned_char(c) = category_code(c) != UTF8PROC_CATEGORY_CN Tests whether a character is a lowercase letter. A character is classified as lowercase if it belongs to Unicode category Ll, Letter: Lowercase. + +# Examples +```jldoctest +julia> islower('α') +true + +julia> islower('Γ') +false + +julia> islower('❤') +false +``` """ islower(c::Char) = (category_code(c) == UTF8PROC_CATEGORY_LL) @@ -254,6 +275,18 @@ islower(c::Char) = (category_code(c) == UTF8PROC_CATEGORY_LL) Tests whether a character is an uppercase letter. A character is classified as uppercase if it belongs to Unicode category Lu, Letter: Uppercase, or Lt, Letter: Titlecase. + +# Examples +```jldoctest +julia> isupper('γ') +false + +julia> isupper('Γ') +true + +julia> isupper('❤') +false +``` """ function isupper(c::Char) ccode = category_code(c) @@ -264,6 +297,18 @@ end isdigit(c::Char) -> Bool Tests whether a character is a numeric digit (0-9). + +# Examples +```jldoctest +julia> isdigit('❤') +false + +julia> isdigit('9') +true + +julia> isdigit('α') +false +``` """ isdigit(c::Char) = ('0' <= c <= '9') @@ -273,6 +318,18 @@ isdigit(c::Char) = ('0' <= c <= '9') Tests whether a character is alphabetic. A character is classified as alphabetic if it belongs to the Unicode general category Letter, i.e. a character whose category code begins with 'L'. + +# Examples +```jldoctest +julia> isalpha('❤') +false + +julia> isalpha('α') +true + +julia> isalpha('9') +false +``` """ isalpha(c::Char) = (UTF8PROC_CATEGORY_LU <= category_code(c) <= UTF8PROC_CATEGORY_LO) @@ -282,6 +339,18 @@ isalpha(c::Char) = (UTF8PROC_CATEGORY_LU <= category_code(c) <= UTF8PROC_CATEGO Tests whether a character is numeric. A character is classified as numeric if it belongs to the Unicode general category Number, i.e. a character whose category code begins with 'N'. + +# Examples +```jldoctest +julia> isnumber('9') +true + +julia> isnumber('α') +false + +julia> isnumber('❤') +false +``` """ isnumber(c::Char) = (UTF8PROC_CATEGORY_ND <= category_code(c) <= UTF8PROC_CATEGORY_NO) @@ -291,6 +360,18 @@ isnumber(c::Char) = (UTF8PROC_CATEGORY_ND <= category_code(c) <= UTF8PROC_CATEGO Tests whether a character is alphanumeric. A character is classified as alphabetic if it belongs to the Unicode general category Letter or Number, i.e. a character whose category code begins with 'L' or 'N'. + +# Examples +```jldoctest +julia> isalnum('❤') +false + +julia> isalnum('9') +true + +julia> isalnum('α') +true +``` """ function isalnum(c::Char) ccode = category_code(c) @@ -313,6 +394,18 @@ iscntrl(c::Char) = (c <= Char(0x1f) || Char(0x7f) <= c <= Char(0x9f)) Tests whether a character belongs to the Unicode general category Punctuation, i.e. a character whose category code begins with 'P'. + +# Examples +```jldoctest +julia> ispunct('α') +false + +julia> ispunct('/') +true + +julia> ispunct(';') +true +``` """ ispunct(c::Char) = (UTF8PROC_CATEGORY_PC <= category_code(c) <= UTF8PROC_CATEGORY_PO) diff --git a/base/strings/util.jl b/base/strings/util.jl index f7297ae6810fc..783ac0a6b20cb 100644 --- a/base/strings/util.jl +++ b/base/strings/util.jl @@ -415,6 +415,18 @@ is a function, each occurrence is replaced with `r(s)` where `s` is the matched If `pat` is a regular expression and `r` is a `SubstitutionString`, then capture group references in `r` are replaced with the corresponding matched text. To remove instances of `pat` from `string`, set `r` to the empty `String` (`""`). + +# Examples +```jldoctest +julia> replace("Python is a programming language.", "Python", "Julia") +"Julia is a programming language." + +julia> replace("The quick foxes run quickly.", "quick", "slow", 1) +"The slow foxes run quickly." + +julia> replace("The quick foxes run quickly.", "quick", "", 1) +"The foxes run quickly." +``` """ replace(s::AbstractString, pat, f) = replace_new(String(s), pat, f, typemax(Int)) # TODO: change this to the following when `replace` is removed from deprecated.jl: From bbe3f756acd18fd3f518fd6f781ba8696fa0d0ea Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Mon, 21 Aug 2017 00:56:52 +0200 Subject: [PATCH 084/324] use showerror in ARPACKException --- base/linalg/exceptions.jl | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/base/linalg/exceptions.jl b/base/linalg/exceptions.jl index 4b0a72e251950..185346fccf0db 100644 --- a/base/linalg/exceptions.jl +++ b/base/linalg/exceptions.jl @@ -11,18 +11,22 @@ struct LAPACKException <: Exception end struct ARPACKException <: Exception - info::String + info::BlasInt end -function ARPACKException(i::Integer) - if i == -8 - return ARPACKException("error return from calculation of a real Schur form.") - elseif i == -9 - return ARPACKException("error return from calculation of eigenvectors.") - elseif i == -14 - return ARPACKException("did not find any eigenvalues to sufficient accuracy. Try with a different starting vector or more Lanczos vectors by increasing the value of ncv.") +function Base.showerror(io::IO, ex::ARPACKException) + print(io, "ARPACKException: ") + if ex.info == -8 + print(io, "error return from calculation of a real Schur form.") + elseif ex.info == -9 + print(io, "error return from calculation of eigenvectors.") + elseif ex.info == -14 + print(io, string("did not find any eigenvalues to sufficient accuracy. ", + "Try with a different starting vector or more Lanczos vectors ", + "by increasing the value of ncv.")) + else + print(io, "unspecified ARPACK error: $(ex.info)") end - return ARPACKException("unspecified ARPACK error: $i") end struct SingularException <: Exception From eae45d60c5b539d75ea96fd2b9708da8a28a44d6 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Sat, 19 Aug 2017 10:09:44 +0200 Subject: [PATCH 085/324] introduce unsafe_{chr2ind,ind2chr} * these allow to request the position past the end (at least for `String`); this is useful when working with `IOBuffer`, which has one more position than there are valid indexes in the corresponding `String`. * ind2chr becomes slightly faster as a result of removing one unnecessary `next` call * the main logic of these functions is factored out into one single function --- base/strings/basic.jl | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/base/strings/basic.jl b/base/strings/basic.jl index 6609cd1417b96..d332ecb528f2f 100644 --- a/base/strings/basic.jl +++ b/base/strings/basic.jl @@ -277,16 +277,7 @@ julia> chr2ind(str, 2) """ function ind2chr(s::AbstractString, i::Integer) s[i] # throws error if invalid - j = 1 - k = start(s) - while true - c, l = next(s,k) - if i <= k - return j - end - j += 1 - k = l - end + unsafe_ind2chr(s, i) end """ @@ -309,18 +300,25 @@ julia> ind2chr(str, 3) """ function chr2ind(s::AbstractString, i::Integer) i < start(s) && throw(BoundsError(s, i)) + k = unsafe_chr2ind(s, i) + s[k] # throws error if invalid + k +end + +function map_chr_ind(s::AbstractString, i::Integer, stop, ret) j = 1 k = start(s) while true - c, l = next(s,k) - if i == j - return k - end + i == stop((j, k)) && return ret((j, k)) # k could point after the last character + _, k = next(s, k) j += 1 - k = l end end +unsafe_ind2chr(s::AbstractString, i::Integer) = map_chr_ind(s, i, last, first) +unsafe_chr2ind(s::AbstractString, i::Integer) = map_chr_ind(s, i, first, last) + + struct EachStringIndex{T<:AbstractString} s::T end From 547ca1ce0833976163aed8bc1733bfb153d933af Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Mon, 21 Aug 2017 16:56:50 +0200 Subject: [PATCH 086/324] REPL: fix edit_insert when indent < 0 (#23290) --- base/repl/LineEdit.jl | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/base/repl/LineEdit.jl b/base/repl/LineEdit.jl index 2ee272e43a6aa..ad43a01780766 100644 --- a/base/repl/LineEdit.jl +++ b/base/repl/LineEdit.jl @@ -448,18 +448,13 @@ end function edit_insert(s::PromptState, c) buf = s.input_buffer - function line_size() - p = position(buf) - seek(buf, rsearch(buf.data, '\n', p)) - ls = p - position(buf) - seek(buf, p) - return ls - end str = string(c) edit_insert(buf, str) - offset = s.ias.curs_row == 1 ? sizeof(prompt_string(s.p.prompt)) : s.indent + offset = s.ias.curs_row == 1 || s.indent < 0 ? + sizeof(prompt_string(s.p.prompt)) : s.indent if !('\n' in str) && eof(buf) && - ((line_size() + offset + sizeof(str) - 1) < width(terminal(s))) + ((position(buf) - beginofline(buf) + # size of current line + offset + sizeof(str) - 1) < width(terminal(s))) # Avoid full update when appending characters to the end # and an update of curs_row isn't necessary (conservatively estimated) write(terminal(s), str) From 3cfd75b849ef60eeeb6b00c325cf40f17278cb77 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Mon, 21 Aug 2017 17:22:16 +0200 Subject: [PATCH 087/324] define Symbol(s::Symbol) = s for performance Before, `Symbol(:symbol)` was calling the generic `Symbo(x...)` method which in turns calls `string(x...)`; this is too slow for just returning the argument. --- base/boot.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/base/boot.jl b/base/boot.jl index be8f10aadd8dd..222a9766ab2f4 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -389,6 +389,7 @@ function Symbol(a::Array{UInt8,1}) ccall(:jl_array_ptr, Ptr{UInt8}, (Any,), a), Intrinsics.arraylen(a)) end +Symbol(s::Symbol) = s # docsystem basics macro doc(x...) From d3ba26e3655193db28b9c3e8f300e876dfe14b73 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Fri, 7 Jul 2017 13:37:30 +0200 Subject: [PATCH 088/324] Random: CloseOpen, Close1Open2: use instances instead of types --- base/random/RNGs.jl | 40 +++++++++++++++++++-------------------- base/random/generation.jl | 6 +++--- base/random/random.jl | 14 +++++++++++--- 3 files changed, 33 insertions(+), 27 deletions(-) diff --git a/base/random/RNGs.jl b/base/random/RNGs.jl index ff33d5c3f46e7..8959e830d7a96 100644 --- a/base/random/RNGs.jl +++ b/base/random/RNGs.jl @@ -45,10 +45,10 @@ RandomDevice ### generation of floats -rand(rng::RandomDevice, ::Type{Close1Open2}) = +rand(rng::RandomDevice, ::Close1Open2_64) = reinterpret(Float64, 0x3ff0000000000000 | rand(rng, UInt64) & 0x000fffffffffffff) -rand(rng::RandomDevice, ::Type{CloseOpen}) = rand(rng, Close1Open2) - 1.0 +rand(rng::RandomDevice, ::CloseOpen_64) = rand(rng, Close1Open2()) - 1.0 @inline rand(r::RandomDevice, T::BitFloatType) = rand_generic(r, T) @@ -198,13 +198,13 @@ const GLOBAL_RNG = MersenneTwister(0) #### helper functions # precondition: !mt_empty(r) -@inline rand_inbounds(r::MersenneTwister, ::Type{Close1Open2}) = mt_pop!(r) -@inline rand_inbounds(r::MersenneTwister, ::Type{CloseOpen}) = - rand_inbounds(r, Close1Open2) - 1.0 -@inline rand_inbounds(r::MersenneTwister) = rand_inbounds(r, CloseOpen) +@inline rand_inbounds(r::MersenneTwister, ::Close1Open2_64) = mt_pop!(r) +@inline rand_inbounds(r::MersenneTwister, ::CloseOpen_64) = + rand_inbounds(r, Close1Open2()) - 1.0 +@inline rand_inbounds(r::MersenneTwister) = rand_inbounds(r, CloseOpen()) @inline rand_ui52_raw_inbounds(r::MersenneTwister) = - reinterpret(UInt64, rand_inbounds(r, Close1Open2)) + reinterpret(UInt64, rand_inbounds(r, Close1Open2())) @inline rand_ui52_raw(r::MersenneTwister) = (reserve_1(r); rand_ui52_raw_inbounds(r)) @inline function rand_ui2x52_raw(r::MersenneTwister) @@ -222,8 +222,7 @@ rand_ui23_raw(r::MersenneTwister) = rand_ui52_raw(r) #### floats -@inline rand(r::MersenneTwister, ::Type{I}) where {I<:FloatInterval} = - (reserve_1(r); rand_inbounds(r, I)) +@inline rand(r::MersenneTwister, I::FloatInterval_64) = (reserve_1(r); rand_inbounds(r, I)) @inline rand(r::MersenneTwister, T::BitFloatType) = rand_generic(r, T) @@ -251,8 +250,7 @@ rand(r::MersenneTwister, ::Type{Int128}) = reinterpret(Int128, rand(r, UInt128)) #### arrays of floats function rand_AbstractArray_Float64!(r::MersenneTwister, A::AbstractArray{Float64}, - n=length(A), - ::Type{I}=CloseOpen) where I<:FloatInterval + n=length(A), I::FloatInterval_64=CloseOpen()) # what follows is equivalent to this simple loop but more efficient: # for i=1:n # @inbounds A[i] = rand(r, I) @@ -275,14 +273,14 @@ end rand!(r::MersenneTwister, A::AbstractArray{Float64}) = rand_AbstractArray_Float64!(r, A) -fill_array!(s::DSFMT_state, A::Ptr{Float64}, n::Int, ::Type{CloseOpen}) = +fill_array!(s::DSFMT_state, A::Ptr{Float64}, n::Int, ::CloseOpen_64) = dsfmt_fill_array_close_open!(s, A, n) -fill_array!(s::DSFMT_state, A::Ptr{Float64}, n::Int, ::Type{Close1Open2}) = +fill_array!(s::DSFMT_state, A::Ptr{Float64}, n::Int, ::Close1Open2_64) = dsfmt_fill_array_close1_open2!(s, A, n) function rand!(r::MersenneTwister, A::Array{Float64}, n::Int=length(A), - ::Type{I}=CloseOpen) where I<:FloatInterval + I::FloatInterval_64=CloseOpen()) # depending on the alignment of A, the data written by fill_array! may have # to be left-shifted by up to 15 bytes (cf. unsafe_copy! below) for # reproducibility purposes; @@ -319,12 +317,12 @@ end (u & 0x007fffff007fffff007fffff007fffff) | 0x3f8000003f8000003f8000003f800000 function rand!(r::MersenneTwister, A::Union{Array{Float16},Array{Float32}}, - ::Type{Close1Open2}) + ::Close1Open2_64) T = eltype(A) n = length(A) n128 = n * sizeof(T) ÷ 16 rand!(r, unsafe_wrap(Array, convert(Ptr{Float64}, pointer(A)), 2*n128), - 2*n128, Close1Open2) + 2*n128, Close1Open2()) A128 = unsafe_wrap(Array, convert(Ptr{UInt128}, pointer(A)), n128) @inbounds for i in 1:n128 u = A128[i] @@ -347,9 +345,8 @@ function rand!(r::MersenneTwister, A::Union{Array{Float16},Array{Float32}}, A end -function rand!(r::MersenneTwister, A::Union{Array{Float16},Array{Float32}}, - ::Type{CloseOpen}) - rand!(r, A, Close1Open2) +function rand!(r::MersenneTwister, A::Union{Array{Float16},Array{Float32}}, ::CloseOpen_64) + rand!(r, A, Close1Open2()) I32 = one(Float32) for i in eachindex(A) @inbounds A[i] = Float32(A[i])-I32 # faster than "A[i] -= one(T)" for T==Float16 @@ -357,7 +354,8 @@ function rand!(r::MersenneTwister, A::Union{Array{Float16},Array{Float32}}, A end -rand!(r::MersenneTwister, A::Union{Array{Float16},Array{Float32}}) = rand!(r, A, CloseOpen) +rand!(r::MersenneTwister, A::Union{Array{Float16},Array{Float32}}) = + rand!(r, A, CloseOpen()) #### arrays of integers @@ -368,7 +366,7 @@ function rand!(r::MersenneTwister, A::Array{UInt128}, n::Int=length(A)) Af = unsafe_wrap(Array, convert(Ptr{Float64}, pointer(A)), 2n) i = n while true - rand!(r, Af, 2i, Close1Open2) + rand!(r, Af, 2i, Close1Open2()) n < 5 && break i = 0 @inbounds while n-i >= 5 diff --git a/base/random/generation.jl b/base/random/generation.jl index 14cde41b37b95..c534410a66e6c 100644 --- a/base/random/generation.jl +++ b/base/random/generation.jl @@ -10,11 +10,11 @@ ### random floats -@inline rand(r::AbstractRNG=GLOBAL_RNG) = rand(r, CloseOpen) +@inline rand(r::AbstractRNG=GLOBAL_RNG) = rand(r, CloseOpen()) # generic random generation function which can be used by RNG implementors # it is not defined as a fallback rand method as this could create ambiguities -@inline rand_generic(r::AbstractRNG, ::Type{Float64}) = rand(r, CloseOpen) +@inline rand_generic(r::AbstractRNG, ::Type{Float64}) = rand(r, CloseOpen()) rand_generic(r::AbstractRNG, ::Type{Float16}) = Float16(reinterpret(Float32, @@ -28,7 +28,7 @@ rand_generic(r::AbstractRNG, ::Type{Float32}) = rand_ui10_raw(r::AbstractRNG) = rand(r, UInt16) rand_ui23_raw(r::AbstractRNG) = rand(r, UInt32) -@inline rand_ui52_raw(r::AbstractRNG) = reinterpret(UInt64, rand(r, Close1Open2)) +@inline rand_ui52_raw(r::AbstractRNG) = reinterpret(UInt64, rand(r, Close1Open2())) @inline rand_ui52(r::AbstractRNG) = rand_ui52_raw(r) & 0x000fffffffffffff ### random complex numbers diff --git a/base/random/random.jl b/base/random/random.jl index 7d88cbd5cb388..988bc925003a3 100644 --- a/base/random/random.jl +++ b/base/random/random.jl @@ -22,9 +22,17 @@ export srand, abstract type AbstractRNG end -abstract type FloatInterval end -mutable struct CloseOpen <: FloatInterval end -mutable struct Close1Open2 <: FloatInterval end +abstract type FloatInterval{T<:AbstractFloat} end + +struct CloseOpen{ T<:AbstractFloat} <: FloatInterval{T} end # interval [0,1) +struct Close1Open2{T<:AbstractFloat} <: FloatInterval{T} end # interval [1,2) + +const FloatInterval_64 = FloatInterval{Float64} +const CloseOpen_64 = CloseOpen{Float64} +const Close1Open2_64 = Close1Open2{Float64} + +CloseOpen() = CloseOpen{Float64}() +Close1Open2() = Close1Open2{Float64}() const BitFloatType = Union{Type{Float16},Type{Float32},Type{Float64}} From 379f032a19adc0bcb32d89195a21a2cd5a7d1fe1 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Thu, 17 Aug 2017 19:19:33 +0200 Subject: [PATCH 089/324] Random: reorganize some generic fallback methods for floats --- base/random/RNGs.jl | 9 ++------- base/random/generation.jl | 27 +++++++++++++++++++++++---- base/random/random.jl | 4 ++-- 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/base/random/RNGs.jl b/base/random/RNGs.jl index 8959e830d7a96..6a12e3877f814 100644 --- a/base/random/RNGs.jl +++ b/base/random/RNGs.jl @@ -45,12 +45,7 @@ RandomDevice ### generation of floats -rand(rng::RandomDevice, ::Close1Open2_64) = - reinterpret(Float64, 0x3ff0000000000000 | rand(rng, UInt64) & 0x000fffffffffffff) - -rand(rng::RandomDevice, ::CloseOpen_64) = rand(rng, Close1Open2()) - 1.0 - -@inline rand(r::RandomDevice, T::BitFloatType) = rand_generic(r, T) +@inline rand(r::RandomDevice, I::FloatInterval) = rand_generic(r, I) ## MersenneTwister @@ -224,7 +219,7 @@ rand_ui23_raw(r::MersenneTwister) = rand_ui52_raw(r) @inline rand(r::MersenneTwister, I::FloatInterval_64) = (reserve_1(r); rand_inbounds(r, I)) -@inline rand(r::MersenneTwister, T::BitFloatType) = rand_generic(r, T) +@inline rand(r::MersenneTwister, I::FloatInterval) = rand_generic(r, I) #### integers diff --git a/base/random/generation.jl b/base/random/generation.jl index c534410a66e6c..927c1685efac9 100644 --- a/base/random/generation.jl +++ b/base/random/generation.jl @@ -10,19 +10,25 @@ ### random floats -@inline rand(r::AbstractRNG=GLOBAL_RNG) = rand(r, CloseOpen()) +# CloseOpen(T) is the fallback for an AbstractFloat T +@inline rand(r::AbstractRNG=GLOBAL_RNG, ::Type{T}=Float64) where {T<:AbstractFloat} = + rand(r, CloseOpen(T)) # generic random generation function which can be used by RNG implementors # it is not defined as a fallback rand method as this could create ambiguities -@inline rand_generic(r::AbstractRNG, ::Type{Float64}) = rand(r, CloseOpen()) -rand_generic(r::AbstractRNG, ::Type{Float16}) = +rand_generic(r::AbstractRNG, ::CloseOpen{Float16}) = Float16(reinterpret(Float32, (rand_ui10_raw(r) % UInt32 << 13) & 0x007fe000 | 0x3f800000) - 1) -rand_generic(r::AbstractRNG, ::Type{Float32}) = +rand_generic(r::AbstractRNG, ::CloseOpen{Float32}) = reinterpret(Float32, rand_ui23_raw(r) % UInt32 & 0x007fffff | 0x3f800000) - 1 +rand_generic(r::AbstractRNG, ::Close1Open2_64) = + reinterpret(Float64, 0x3ff0000000000000 | rand(r, UInt64) & 0x000fffffffffffff) + +rand_generic(r::AbstractRNG, ::CloseOpen_64) = rand(r, Close1Open2()) - 1.0 + ### random integers rand_ui10_raw(r::AbstractRNG) = rand(r, UInt16) @@ -72,6 +78,19 @@ rand( T::Type, d::Integer, dims::Integer...) = rand(T, Dims((d, d # rand(r, ()) would match both this method and rand(r, dims::Dims) # moreover, a call like rand(r, NotImplementedType()) would be an infinite loop +#### arrays of floats + +rand!(r::AbstractRNG, A::AbstractArray, ::Type{T}) where {T<:AbstractFloat} = + rand!(r, A, CloseOpen{T}()) + +function rand!(r::AbstractRNG, A::AbstractArray, I::FloatInterval) + for i in eachindex(A) + @inbounds A[i] = rand(r, I) + end + A +end + +rand!(A::AbstractArray, I::FloatInterval) = rand!(GLOBAL_RNG, A, I) ## Generate random integer within a range diff --git a/base/random/random.jl b/base/random/random.jl index 988bc925003a3..2535443f9a8d4 100644 --- a/base/random/random.jl +++ b/base/random/random.jl @@ -31,8 +31,8 @@ const FloatInterval_64 = FloatInterval{Float64} const CloseOpen_64 = CloseOpen{Float64} const Close1Open2_64 = Close1Open2{Float64} -CloseOpen() = CloseOpen{Float64}() -Close1Open2() = Close1Open2{Float64}() +CloseOpen( ::Type{T}=Float64) where {T<:AbstractFloat} = CloseOpen{T}() +Close1Open2(::Type{T}=Float64) where {T<:AbstractFloat} = Close1Open2{T}() const BitFloatType = Union{Type{Float16},Type{Float32},Type{Float64}} From c3c950708166a0909bc237db1e3bb3b6ec0eb780 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Fri, 7 Jul 2017 19:59:19 +0200 Subject: [PATCH 090/324] implement rand(::BigFloat) (close #13948) --- NEWS.md | 2 ++ base/random/generation.jl | 69 +++++++++++++++++++++++++++++++++++++++ doc/src/stdlib/numbers.md | 12 +++---- test/random.jl | 21 +++++++----- 4 files changed, 90 insertions(+), 14 deletions(-) diff --git a/NEWS.md b/NEWS.md index 1e3c1729e1659..1fc535948e05e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -203,6 +203,8 @@ Library improvements * Mutating versions of `randperm` and `randcycle` have been added: `randperm!` and `randcycle!` ([#22723]). + * `BigFloat` random numbers can now be generated ([#22720]). + Compiler/Runtime improvements ----------------------------- diff --git a/base/random/generation.jl b/base/random/generation.jl index 927c1685efac9..5c65633216e9e 100644 --- a/base/random/generation.jl +++ b/base/random/generation.jl @@ -29,6 +29,67 @@ rand_generic(r::AbstractRNG, ::Close1Open2_64) = rand_generic(r::AbstractRNG, ::CloseOpen_64) = rand(r, Close1Open2()) - 1.0 +#### BigFloat + +const bits_in_Limb = sizeof(Limb) << 3 +const Limb_high_bit = one(Limb) << (bits_in_Limb-1) + +struct BigFloatRandGenerator + prec::Int + nlimbs::Int + limbs::Vector{Limb} + shift::UInt + + function BigFloatRandGenerator(prec::Int=precision(BigFloat)) + nlimbs = (prec-1) ÷ bits_in_Limb + 1 + limbs = Vector{Limb}(nlimbs) + shift = nlimbs * bits_in_Limb - prec + new(prec, nlimbs, limbs, shift) + end +end + +function _rand(rng::AbstractRNG, gen::BigFloatRandGenerator) + z = BigFloat() + limbs = gen.limbs + rand!(rng, limbs) + @inbounds begin + limbs[1] <<= gen.shift + randbool = iszero(limbs[end] & Limb_high_bit) + limbs[end] |= Limb_high_bit + end + z.sign = 1 + unsafe_copy!(z.d, pointer(limbs), gen.nlimbs) + (z, randbool) +end + +function rand(rng::AbstractRNG, gen::BigFloatRandGenerator, ::Close1Open2{BigFloat}) + z = _rand(rng, gen)[1] + z.exp = 1 + z +end + +function rand(rng::AbstractRNG, gen::BigFloatRandGenerator, ::CloseOpen{BigFloat}) + z, randbool = _rand(rng, gen) + z.exp = 0 + randbool && + ccall((:mpfr_sub_d, :libmpfr), Int32, + (Ptr{BigFloat}, Ptr{BigFloat}, Cdouble, Int32), + &z, &z, 0.5, Base.MPFR.ROUNDING_MODE[]) + z +end + +# alternative, with 1 bit less of precision +# TODO: make an API for requesting full or not-full precision +function rand(rng::AbstractRNG, gen::BigFloatRandGenerator, ::CloseOpen{BigFloat}, ::Void) + z = rand(rng, Close1Open2(BigFloat), gen) + ccall((:mpfr_sub_ui, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Culong, Int32), + &z, &z, 1, Base.MPFR.ROUNDING_MODE[]) + z +end + +rand_generic(rng::AbstractRNG, I::FloatInterval{BigFloat}) = + rand(rng, BigFloatRandGenerator(), I) + ### random integers rand_ui10_raw(r::AbstractRNG) = rand(r, UInt16) @@ -90,6 +151,14 @@ function rand!(r::AbstractRNG, A::AbstractArray, I::FloatInterval) A end +function rand!(rng::AbstractRNG, A::AbstractArray, I::FloatInterval{BigFloat}) + gen = BigFloatRandGenerator() + for i in eachindex(A) + @inbounds A[i] = rand(rng, gen, I) + end + A +end + rand!(A::AbstractArray, I::FloatInterval) = rand!(GLOBAL_RNG, A, I) ## Generate random integer within a range diff --git a/doc/src/stdlib/numbers.md b/doc/src/stdlib/numbers.md index 91941c4a338fa..edcecf2f85e0a 100644 --- a/doc/src/stdlib/numbers.md +++ b/doc/src/stdlib/numbers.md @@ -143,12 +143,12 @@ dimension specifications `dims...` (which can be given as a tuple) to generate a values. A `MersenneTwister` or `RandomDevice` RNG can generate random numbers of the following types: -[`Float16`](@ref), [`Float32`](@ref), [`Float64`](@ref), [`Bool`](@ref), [`Int8`](@ref), -[`UInt8`](@ref), [`Int16`](@ref), [`UInt16`](@ref), [`Int32`](@ref), [`UInt32`](@ref), -[`Int64`](@ref), [`UInt64`](@ref), [`Int128`](@ref), [`UInt128`](@ref), [`BigInt`](@ref) -(or complex numbers of those types). Random floating point numbers are generated uniformly -in ``[0, 1)``. As `BigInt` represents unbounded integers, the interval must be specified -(e.g. `rand(big(1:6))`). +[`Float16`](@ref), [`Float32`](@ref), [`Float64`](@ref), [`BigFloat`](@ref), [`Bool`](@ref), +[`Int8`](@ref), [`UInt8`](@ref), [`Int16`](@ref), [`UInt16`](@ref), [`Int32`](@ref), +[`UInt32`](@ref), [`Int64`](@ref), [`UInt64`](@ref), [`Int128`](@ref), [`UInt128`](@ref), +[`BigInt`](@ref) (or complex numbers of those types). +Random floating point numbers are generated uniformly in ``[0, 1)``. As `BigInt` represents +unbounded integers, the interval must be specified (e.g. `rand(big(1:6))`). ```@docs Base.Random.srand diff --git a/test/random.jl b/test/random.jl index fd885163d8e77..30c7dc6d54966 100644 --- a/test/random.jl +++ b/test/random.jl @@ -308,7 +308,7 @@ end for rng in ([], [MersenneTwister(0)], [RandomDevice()]) ftypes = [Float16, Float32, Float64] cftypes = [Complex32, Complex64, Complex128, ftypes...] - types = [Bool, Char, Base.BitInteger_types..., ftypes...] + types = [Bool, Char, BigFloat, Base.BitInteger_types..., ftypes...] randset = Set(rand(Int, 20)) randdict = Dict(zip(rand(Int,10), rand(Int, 10))) collections = [IntSet(rand(1:100, 20)) => Int, @@ -322,6 +322,9 @@ for rng in ([], [MersenneTwister(0)], [RandomDevice()]) Float64 => Float64, "qwèrtï" => Char, GenericString("qwèrtï") => Char] + functypes = Dict(rand => types, randn => cftypes, randexp => ftypes, + rand! => types, randn! => cftypes, randexp! => ftypes) + b2 = big(2) u3 = UInt(3) for f in [rand, randn, randexp] @@ -330,7 +333,7 @@ for rng in ([], [MersenneTwister(0)], [RandomDevice()]) f(rng..., 2, 3) ::Array{Float64, 2} f(rng..., b2, u3) ::Array{Float64, 2} f(rng..., (2, 3)) ::Array{Float64, 2} - for T in (f === rand ? types : f === randn ? cftypes : ftypes) + for T in functypes[f] a0 = f(rng..., T) ::T a1 = f(rng..., T, 5) ::Vector{T} a2 = f(rng..., T, 2, 3) ::Array{T, 2} @@ -370,7 +373,7 @@ for rng in ([], [MersenneTwister(0)], [RandomDevice()]) @test_throws ArgumentError rand(rng..., C, 5) end for f! in [rand!, randn!, randexp!] - for T in (f! === rand! ? types : f! === randn! ? cftypes : ftypes) + for T in functypes[f!] 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)) f!(rng..., A) ::typeof(A) @@ -409,17 +412,19 @@ for rng in ([], [MersenneTwister(0)], [RandomDevice()]) end end -function hist(X,n) - v = zeros(Int,n) +function hist(X, n) + v = zeros(Int, n) for x in X - v[floor(Int,x*n)+1] += 1 + v[floor(Int, x*n) + 1] += 1 end v end # test uniform distribution of floats -for rng in [srand(MersenneTwister(0)), RandomDevice()] - for T in [Float16,Float32,Float64] +for rng in [srand(MersenneTwister(0)), RandomDevice()], + T in [Float16, Float32, Float64, BigFloat], + prec in (T == BigFloat ? [3, 53, 64, 100, 256, 1000] : [256]) + setprecision(BigFloat, prec) do # array version counts = hist(rand(rng, T, 2000), 4) @test minimum(counts) > 300 # should fail with proba < 1e-26 From dcb46b36d83962bdd6170db66644573503b499b8 Mon Sep 17 00:00:00 2001 From: Harrison Grodin Date: Mon, 21 Aug 2017 12:02:58 -0400 Subject: [PATCH 091/324] Replace --quiet with --banner (#23343) --- NEWS.md | 3 +++ base/client.jl | 10 +++++----- base/deprecated.jl | 3 +++ base/options.jl | 2 +- doc/man/julia.1 | 4 ++-- doc/src/manual/getting-started.md | 2 +- src/jloptions.c | 23 +++++++++++++++++------ src/julia.h | 2 +- test/cmdlineargs.jl | 4 ++-- test/repl.jl | 4 ++-- 10 files changed, 37 insertions(+), 20 deletions(-) diff --git a/NEWS.md b/NEWS.md index 1e3c1729e1659..49b16cd7741c5 100644 --- a/NEWS.md +++ b/NEWS.md @@ -326,6 +326,8 @@ Command-line option changes * New option `--warn-overwrite={yes|no}` to control the warning for overwriting method definitions. The default is `no` ([#23002]). + * The `-q` and `--quiet` flags have been deprecated in favor of `--banner={yes|no}` ([#23342]). + Julia v0.6.0 Release Notes ========================== @@ -1182,3 +1184,4 @@ Command-line option changes [#23157]: https://github.com/JuliaLang/julia/issues/23157 [#23187]: https://github.com/JuliaLang/julia/issues/23187 [#23207]: https://github.com/JuliaLang/julia/issues/23207 +[#23342]: https://github.com/JuliaLang/julia/issues/23342 diff --git a/base/client.jl b/base/client.jl index c46d5acccc95c..0a6a51e0d1a99 100644 --- a/base/client.jl +++ b/base/client.jl @@ -253,7 +253,7 @@ function process_options(opts::JLOptions) repl = true startup = (opts.startupfile != 2) history_file = (opts.historyfile != 0) - quiet = (opts.quiet != 0) + banner = (opts.banner != 0) color_set = (opts.color != 0) global have_color = (opts.color == 1) global is_interactive = (opts.isinteractive != 0) @@ -317,7 +317,7 @@ function process_options(opts::JLOptions) break end repl |= is_interactive - return (quiet,repl,startup,color_set,history_file) + return (banner,repl,startup,color_set,history_file) end function load_juliarc() @@ -380,7 +380,7 @@ function _start() opts = JLOptions() @eval Main include(x) = $include(Main, x) try - (quiet,repl,startup,color_set,history_file) = process_options(opts) + (banner,repl,startup,color_set,history_file) = process_options(opts) local term global active_repl @@ -393,10 +393,10 @@ function _start() term = Terminals.TTYTerminal(get(ENV, "TERM", @static Sys.iswindows() ? "" : "dumb"), STDIN, STDOUT, STDERR) global is_interactive = true color_set || (global have_color = Terminals.hascolor(term)) - quiet || REPL.banner(term,term) + banner && REPL.banner(term,term) if term.term_type == "dumb" active_repl = REPL.BasicREPL(term) - quiet || warn("Terminal not fully functional") + banner && warn("Terminal not fully functional") else active_repl = REPL.LineEditREPL(term, true) active_repl.history_file = history_file diff --git a/base/deprecated.jl b/base/deprecated.jl index 97ec06ecb4484..b4dc3e0c509c2 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1687,6 +1687,9 @@ export hex2num # issue #5148, PR #23259 # warning for `const` on locals should be changed to an error in julia-syntax.scm +# issue #23342, PR #23343 +# `-q` and `--quiet` are deprecated in jloptions.c + # issue #17886 # deprecations for filter[!] with 2-arg functions are in associative.jl diff --git a/base/options.jl b/base/options.jl index 32530a17be183..7c9ffb8c01f37 100644 --- a/base/options.jl +++ b/base/options.jl @@ -2,7 +2,7 @@ # NOTE: This type needs to be kept in sync with jl_options in src/julia.h struct JLOptions - quiet::Int8 + banner::Int8 julia_home::Ptr{UInt8} julia_bin::Ptr{UInt8} eval::Ptr{UInt8} diff --git a/doc/man/julia.1 b/doc/man/julia.1 index b1f40f9dabc37..2944eca7f4436 100644 --- a/doc/man/julia.1 +++ b/doc/man/julia.1 @@ -108,8 +108,8 @@ Run processes on hosts listed in Interactive mode; REPL runs and isinteractive() is true .TP --q, --quiet -Quiet startup without banner +--banner={yes|no} +Enable or disable startup banner .TP --color={yes|no} diff --git a/doc/src/manual/getting-started.md b/doc/src/manual/getting-started.md index 42dee20115868..aaafc82a2b769 100644 --- a/doc/src/manual/getting-started.md +++ b/doc/src/manual/getting-started.md @@ -114,7 +114,7 @@ julia [switches] -- [programfile] [args...] --machinefile Run processes on hosts listed in -i Interactive mode; REPL runs and isinteractive() is true - -q, --quiet Quiet startup (no banner) + --banner={yes|no} Enable or disable startup banner --color={yes|no} Enable or disable color text --history-file={yes|no} Load or save history diff --git a/src/jloptions.c b/src/jloptions.c index 0d8e46dc24839..e91303e1e21de 100644 --- a/src/jloptions.c +++ b/src/jloptions.c @@ -33,7 +33,7 @@ JL_DLLEXPORT const char *jl_get_default_sysimg_path(void) } -jl_options_t jl_options = { 0, // quiet +jl_options_t jl_options = { 1, // banner NULL, // julia_home NULL, // julia_bin NULL, // eval @@ -102,7 +102,7 @@ static const char opts[] = // interactive options " -i Interactive mode; REPL runs and isinteractive() is true\n" - " -q, --quiet Quiet startup (no banner)\n" + " --banner={yes|no} Enable or disable startup banner\n" " --color={yes|no} Enable or disable color text\n" " --history-file={yes|no} Load or save history\n\n" @@ -170,7 +170,8 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) opt_output_ji, opt_use_precompiled, opt_use_compilecache, - opt_incremental + opt_incremental, + opt_banner }; static const char* const shortopts = "+vhqH:e:E:L:J:C:ip:O:g:"; static const struct option longopts[] = { @@ -180,6 +181,7 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) { "version", no_argument, 0, 'v' }, { "help", no_argument, 0, 'h' }, { "quiet", no_argument, 0, 'q' }, + { "banner", required_argument, 0, opt_banner }, { "home", required_argument, 0, 'H' }, { "eval", required_argument, 0, 'e' }, { "print", required_argument, 0, 'E' }, @@ -280,9 +282,6 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) case 'h': // help jl_printf(JL_STDOUT, "%s%s", usage, opts); jl_exit(0); - case 'q': // quiet - jl_options.quiet = 1; - break; case 'g': // debug info if (optarg != NULL) { if (!strcmp(optarg,"0")) @@ -315,6 +314,18 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) jl_options.image_file = strdup(optarg); jl_options.image_file_specified = 1; break; + case 'q': // quiet + jl_printf(JL_STDERR, "-q and --quiet are deprecated, use --banner=no instead\n"); + jl_options.banner = 0; + break; + case opt_banner: // banner + if (!strcmp(optarg,"yes")) + jl_options.banner = 1; + else if (!strcmp(optarg,"no")) + jl_options.banner = 0; + else + jl_errorf("julia: invalid argument to --banner={yes|no} (%s)", optarg); + break; case opt_use_precompiled: if (!strcmp(optarg,"yes")) jl_options.use_precompiled = JL_OPTIONS_USE_PRECOMPILED_YES; diff --git a/src/julia.h b/src/julia.h index 65ad583b02d45..cdf838287fb0a 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1671,7 +1671,7 @@ JL_DLLEXPORT void jl_(void *jl_value); // julia options ----------------------------------------------------------- // NOTE: This struct needs to be kept in sync with JLOptions type in base/options.jl typedef struct { - int8_t quiet; + int8_t banner; const char *julia_home; const char *julia_bin; const char *eval; diff --git a/test/cmdlineargs.jl b/test/cmdlineargs.jl index 386504033579e..290365651521f 100644 --- a/test/cmdlineargs.jl +++ b/test/cmdlineargs.jl @@ -22,7 +22,7 @@ let exename = `$(Base.julia_cmd()) --precompiled=yes --startup-file=no` @test startswith(read(`$exename --help`, String), header) end - # --quiet + # --banner # This flag is indirectly tested in test/repl.jl # --home @@ -72,7 +72,7 @@ let exename = `$(Base.julia_cmd()) --precompiled=yes --startup-file=no` end # --procs - @test readchomp(`$exename -q -p 2 -e "println(nworkers())"`) == "2" + @test readchomp(`$exename --banner=no -p 2 -e "println(nworkers())"`) == "2" @test !success(`$exename -p 0`) @test !success(`$exename --procs=1.0`) diff --git a/test/repl.jl b/test/repl.jl index 8a1b07d3ea9f7..5b28c8ff6fda9 100644 --- a/test/repl.jl +++ b/test/repl.jl @@ -614,7 +614,7 @@ let exename = Base.julia_cmd() TestHelpers.with_fake_pty() do slave, master nENV = copy(ENV) nENV["TERM"] = "dumb" - p = spawn(setenv(`$exename --startup-file=no --quiet`,nENV),slave,slave,slave) + p = spawn(setenv(`$exename --startup-file=no --banner=no`,nENV),slave,slave,slave) output = readuntil(master,"julia> ") if ccall(:jl_running_on_valgrind,Cint,()) == 0 # If --trace-children=yes is passed to valgrind, we will get a @@ -631,7 +631,7 @@ let exename = Base.julia_cmd() end # Test stream mode - outs, ins, p = readandwrite(`$exename --startup-file=no --quiet`) + outs, ins, p = readandwrite(`$exename --startup-file=no --banner=no`) write(ins,"1\nquit()\n") @test read(outs, String) == "1\n" end # let exename From 6e9c3ce39ad89b15a98e2bd6361f179d789198c5 Mon Sep 17 00:00:00 2001 From: Vaibhavdixit02 Date: Mon, 21 Aug 2017 22:14:04 +0530 Subject: [PATCH 092/324] Float16/32 inconsistent compact NaN printing #23248 (#23334) --- base/grisu/grisu.jl | 2 +- test/numbers.jl | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/base/grisu/grisu.jl b/base/grisu/grisu.jl index 0474372233516..64942d2775ba0 100644 --- a/base/grisu/grisu.jl +++ b/base/grisu/grisu.jl @@ -116,7 +116,7 @@ end function Base.show(io::IO, x::Union{Float64,Float32}) if get(io, :compact, false) - _show(io, x, PRECISION, 6, true, true) + _show(io, x, PRECISION, 6, x isa Float64, true) else _show(io, x, SHORTEST, 0, true, false) end diff --git a/test/numbers.jl b/test/numbers.jl index 7caafb9d15717..34586d4365e11 100644 --- a/test/numbers.jl +++ b/test/numbers.jl @@ -3023,3 +3023,10 @@ end end end end + +@testset "compact NaN printing" begin + @test sprint(io->show(IOContext(io, :compact => true), NaN16)) == "NaN" + @test sprint(io->show(IOContext(io, :compact => true), NaN32)) == "NaN" + @test sprint(io->show(IOContext(io, :compact => true), NaN64)) == "NaN" + @test_broken sprint(io->show(IOContext(io, :compact => true), big(NaN))) == "NaN" +end From 5838d5a301733f17ed2d8b500c52f19e81e46a92 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Sat, 19 Aug 2017 10:25:40 +0200 Subject: [PATCH 093/324] REPL: transpose words with Alt-t Fixes part of #8447. --- base/repl/LineEdit.jl | 34 ++++++++++++++++++++++--- test/lineedit.jl | 58 +++++++++++++++++++++++++++++++++++-------- 2 files changed, 78 insertions(+), 14 deletions(-) diff --git a/base/repl/LineEdit.jl b/base/repl/LineEdit.jl index 2ee272e43a6aa..6bf65416526db 100644 --- a/base/repl/LineEdit.jl +++ b/base/repl/LineEdit.jl @@ -587,8 +587,9 @@ function edit_kill_line(s::MIState) refresh_line(s) end -edit_transpose(s) = edit_transpose(buffer(s)) && refresh_line(s) -function edit_transpose(buf::IOBuffer) +edit_transpose_chars(s) = edit_transpose_chars(buffer(s)) && refresh_line(s) + +function edit_transpose_chars(buf::IOBuffer) position(buf) == 0 && return false eof(buf) && char_move_left(buf) char_move_left(buf) @@ -599,6 +600,32 @@ function edit_transpose(buf::IOBuffer) return true end +edit_transpose_words(s) = edit_transpose_words(buffer(s)) && refresh_line(s) + +function edit_transpose_words(buf::IOBuffer, mode=:emacs) + mode in [:readline, :emacs] || + throw(ArgumentError("`mode` must be `:readline` or `:emacs`")) + pos = position(buf) + if mode == :emacs + char_move_word_left(buf) + char_move_word_right(buf) + end + char_move_word_right(buf) + e2 = position(buf) + char_move_word_left(buf) + b2 = position(buf) + char_move_word_left(buf) + b1 = position(buf) + char_move_word_right(buf) + e1 = position(buf) + e1 >= b2 && (seek(buf, pos); return false) + word2 = splice!(buf.data, b2+1:e2, buf.data[b1+1:e1]) + splice!(buf.data, b1+1:e1, word2) + seek(buf, e2) + true +end + + edit_clear(buf::IOBuffer) = truncate(buf, 0) function edit_clear(s::MIState) @@ -1492,7 +1519,8 @@ AnyDict( input = bracketed_paste(s) edit_insert(s, input) end, - "^T" => (s,o...)->edit_transpose(s) + "^T" => (s,o...)->edit_transpose_chars(s), + "\et" => (s,o...)->edit_transpose_words(s), ) const history_keymap = AnyDict( diff --git a/test/lineedit.jl b/test/lineedit.jl index c882dc5dc586d..3d2778e8212cf 100644 --- a/test/lineedit.jl +++ b/test/lineedit.jl @@ -308,41 +308,77 @@ let buf = IOBuffer() @test String(buf.data[1:buf.size]) == "a" end -## edit_transpose ## +## edit_transpose_chars ## let buf = IOBuffer() LineEdit.edit_insert(buf, "abcde") seek(buf,0) - LineEdit.edit_transpose(buf) + LineEdit.edit_transpose_chars(buf) @test String(buf.data[1:buf.size]) == "abcde" LineEdit.char_move_right(buf) - LineEdit.edit_transpose(buf) + LineEdit.edit_transpose_chars(buf) @test String(buf.data[1:buf.size]) == "bacde" - LineEdit.edit_transpose(buf) + LineEdit.edit_transpose_chars(buf) @test String(buf.data[1:buf.size]) == "bcade" seekend(buf) - LineEdit.edit_transpose(buf) + LineEdit.edit_transpose_chars(buf) @test String(buf.data[1:buf.size]) == "bcaed" - LineEdit.edit_transpose(buf) + LineEdit.edit_transpose_chars(buf) @test String(buf.data[1:buf.size]) == "bcade" seek(buf, 0) LineEdit.edit_clear(buf) LineEdit.edit_insert(buf, "αβγδε") seek(buf,0) - LineEdit.edit_transpose(buf) + LineEdit.edit_transpose_chars(buf) @test String(buf.data[1:buf.size]) == "αβγδε" LineEdit.char_move_right(buf) - LineEdit.edit_transpose(buf) + LineEdit.edit_transpose_chars(buf) @test String(buf.data[1:buf.size]) == "βαγδε" - LineEdit.edit_transpose(buf) + LineEdit.edit_transpose_chars(buf) @test String(buf.data[1:buf.size]) == "βγαδε" seekend(buf) - LineEdit.edit_transpose(buf) + LineEdit.edit_transpose_chars(buf) @test String(buf.data[1:buf.size]) == "βγαεδ" - LineEdit.edit_transpose(buf) + LineEdit.edit_transpose_chars(buf) @test String(buf.data[1:buf.size]) == "βγαδε" end +@testset "edit_word_transpose" begin + buf = IOBuffer() + mode = Ref{Symbol}() + function transpose!(i) # i: char indice + seek(buf, Base.unsafe_chr2ind(String(take!(copy(buf))), i+1)-1) + LineEdit.edit_transpose_words(buf, mode[]) + str = String(take!(copy(buf))) + str, Base.unsafe_ind2chr(str, position(buf)+1)-1 + end + + mode[] = :readline + LineEdit.edit_insert(buf, "àbç def gh ") + @test transpose!(0) == ("àbç def gh ", 0) + @test transpose!(1) == ("àbç def gh ", 1) + @test transpose!(2) == ("àbç def gh ", 2) + @test transpose!(3) == ("def àbç gh ", 7) + @test transpose!(4) == ("àbç def gh ", 7) + @test transpose!(5) == ("def àbç gh ", 7) + @test transpose!(6) == ("àbç def gh ", 7) + @test transpose!(7) == ("àbç gh def ", 11) + @test transpose!(10) == ("àbç def gh ", 11) + @test transpose!(11) == ("àbç gh def", 12) + LineEdit.edit_insert(buf, " ") + @test transpose!(13) == ("àbç def gh", 13) + + take!(buf) + mode[] = :emacs + LineEdit.edit_insert(buf, "àbç def gh ") + @test transpose!(0) == ("def àbç gh ", 7) + @test transpose!(4) == ("àbç def gh ", 7) + @test transpose!(5) == ("àbç gh def ", 11) + @test transpose!(10) == ("àbç def gh", 12) + LineEdit.edit_insert(buf, " ") + @test transpose!(13) == ("àbç gh def", 13) +end + let term = TestHelpers.FakeTerminal(IOBuffer(), IOBuffer(), IOBuffer()) s = LineEdit.init_state(term, ModalInterface([Prompt("test> ")])) From a56c9ebf700c081170042e1d8d249cee857680f1 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 21 Aug 2017 13:42:27 -0400 Subject: [PATCH 094/324] return file-polling errors to the caller instead of throwing them (#17528) this allows the user to poll for the creation of a non-existent file --- base/poll.jl | 34 ++++++++++++++++++++++------------ test/file.jl | 7 +++++-- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/base/poll.jl b/base/poll.jl index 87b157b1208b3..7f1ef55034cd7 100644 --- a/base/poll.jl +++ b/base/poll.jl @@ -12,7 +12,7 @@ export import Base: @handle_as, wait, close, uvfinalize, eventloop, notify_error, stream_wait, _sizeof_uv_poll, _sizeof_uv_fs_poll, _sizeof_uv_fs_event, _uv_hook_close, - associate_julia_struct, disassociate_julia_struct, | + associate_julia_struct, disassociate_julia_struct, isreadable, iswritable, | if Sys.iswindows() import Base.WindowsRawSocket end @@ -82,9 +82,10 @@ mutable struct PollingFileWatcher interval::UInt32 notify::Condition active::Bool + busy_polling::Int32 function PollingFileWatcher(file::AbstractString, interval::Float64=5.007) # same default as nodejs handle = Libc.malloc(_sizeof_uv_fs_poll) - this = new(handle, file, round(UInt32, interval * 1000), Condition(), false) + this = new(handle, file, round(UInt32, interval * 1000), Condition(), false, 0) associate_julia_struct(handle, this) err = ccall(:uv_fs_poll_init, Int32, (Ptr{Void}, Ptr{Void}), eventloop(), handle) if err != 0 @@ -250,7 +251,7 @@ end function _uv_hook_close(uv::PollingFileWatcher) uv.handle = C_NULL uv.active = false - notify(uv.notify, (StatStruct(), StatStruct())) + notify(uv.notify, (StatStruct(), EOFError())) nothing end @@ -299,11 +300,10 @@ end function uv_fspollcb(handle::Ptr{Void}, status::Int32, prev::Ptr, curr::Ptr) t = @handle_as handle PollingFileWatcher - if status != 0 - notify_error(t.notify, UVError("PollingFileWatcher", status)) - else + if status == 0 || status != t.busy_polling + t.busy_polling = status prev_stat = StatStruct(convert(Ptr{UInt8}, prev)) - curr_stat = StatStruct(convert(Ptr{UInt8}, curr)) + curr_stat = (status == 0) ? StatStruct(convert(Ptr{UInt8}, curr)) : UVError("PollingFileWatcher", status) notify(t.notify, (prev_stat, curr_stat)) end nothing @@ -511,15 +511,20 @@ function watch_file(s::AbstractString, timeout_s::Real=-1) end """ - poll_file(path::AbstractString, interval_s::Real=5.007, timeout_s::Real=-1) -> (previous::StatStruct, current::StatStruct) + poll_file(path::AbstractString, interval_s::Real=5.007, timeout_s::Real=-1) -> (previous::StatStruct, current) Monitor a file for changes by polling every `interval_s` seconds until a change occurs or `timeout_s` seconds have elapsed. The `interval_s` should be a long period; the default is 5.007 seconds. -Returns a pair of `StatStruct` objects `(previous, current)` when a change is detected. +Returns a pair of status objects `(previous, current)` when a change is detected. +The `previous` status is always a `StatStruct`, but it may have all of the fields zeroed +(indicating the file didn't previously exist, or wasn't previously accessible). + +The `current` status object may be a `StatStruct`, an `EOFError` (indicating the timeout elapsed), +or some other `Exception` subtype (if the `stat` operation failed - for example, if the path does not exist). -To determine when a file was modified, compare `mtime(prev) != mtime(current)` to detect +To determine when a file was modified, compare `current isa StatStruct && mtime(prev) != mtime(current)` to detect notification of changes. However, using [`watch_file`](@ref) for this operation is preferred, since it is more reliable and efficient, although in some situations it may not be available. """ @@ -532,7 +537,12 @@ function poll_file(s::AbstractString, interval_seconds::Real=5.007, timeout_s::R @schedule begin try - result = wait(pfw) + statdiff = wait(pfw) + if statdiff[1] == StatStruct() && isa(statdiff[2], UVError) + # file didn't initially exist, continue watching for it to be created (or the error to change) + statdiff = wait(pfw) + end + result = statdiff catch e notify_error(wt, e) return @@ -543,7 +553,7 @@ function poll_file(s::AbstractString, interval_seconds::Real=5.007, timeout_s::R wait(wt) if result === :timeout - return (StatStruct(), StatStruct()) + return (StatStruct(), EOFError()) end return result else diff --git a/test/file.jl b/test/file.jl index be85fab05de7e..8e98b043759d6 100644 --- a/test/file.jl +++ b/test/file.jl @@ -52,6 +52,7 @@ end ####################################################################### # This section tests some of the features of the stat-based file info # ####################################################################### +@test !isfile(Base.Filesystem.StatStruct()) @test isdir(dir) @test !isfile(dir) @test !islink(dir) @@ -196,7 +197,7 @@ function test_timeout(tval) @async test_file_poll(channel, 10, tval) tr = take!(channel) t_elapsed = toq() - @test !ispath(tr[1]) && !ispath(tr[2]) + @test tr[1] === Base.Filesystem.StatStruct() && tr[2] === EOFError() @test tval <= t_elapsed end @@ -268,7 +269,9 @@ test_watch_file_timeout(0.1) test_watch_file_change(6) @test_throws Base.UVError watch_file("____nonexistent_file", 10) -@test_throws Base.UVError poll_file("____nonexistent_file", 2, 10) +@test(@elapsed( + @test(poll_file("____nonexistent_file", 1, 3.1) === + (Base.Filesystem.StatStruct(), EOFError()))) > 3) ############## # mark/reset # From 33fbfad3fd118cb89b63a241f35c92e20d466337 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Mon, 21 Aug 2017 21:30:14 +0200 Subject: [PATCH 095/324] no longer warn in code_warntype for unused variables (#23280) --- base/interactiveutil.jl | 25 +++++++++++++++++++++++-- test/reflection.jl | 5 +++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/base/interactiveutil.jl b/base/interactiveutil.jl index 4ad8d2795ad6f..1e50a94bebbf5 100644 --- a/base/interactiveutil.jl +++ b/base/interactiveutil.jl @@ -347,14 +347,35 @@ problematic for performance, so the results need to be used judiciously. See [`@code_warntype`](@ref man-code-warntype) for more information. """ function code_warntype(io::IO, f, @nospecialize(t)) + function slots_used(ci, slotnames) + used = falses(length(slotnames)) + scan_exprs!(used, ci.code) + return used + end + + function scan_exprs!(used, exprs) + for ex in exprs + if isa(ex, Slot) + used[ex.id] = true + elseif isa(ex, Expr) + scan_exprs!(used, ex.args) + end + end + end + emph_io = IOContext(io, :TYPEEMPHASIZE => true) for (src, rettype) in code_typed(f, t) println(emph_io, "Variables:") slotnames = sourceinfo_slotnames(src) + used_slotids = slots_used(src, slotnames) for i = 1:length(slotnames) print(emph_io, " ", slotnames[i]) - if isa(src.slottypes, Array) - show_expr_type(emph_io, src.slottypes[i], true) + if used_slotids[i] + if isa(src.slottypes, Array) + show_expr_type(emph_io, src.slottypes[i], true) + end + else + print(emph_io, " ") end print(emph_io, '\n') end diff --git a/test/reflection.jl b/test/reflection.jl index ab127862f034a..f49296657ffbc 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -92,6 +92,11 @@ show(iob, expand(Main, :(x -> x^2))) str = String(take!(iob)) @test isempty(search(str, tag)) +# Make sure non used variables are not emphasized +has_unused() = (a = rand(5)) +@test !warntype_hastag(has_unused, Tuple{}, tag) +@test warntype_hastag(has_unused, Tuple{}, "") + module ImportIntrinsics15819 # Make sure changing the lookup path of an intrinsic doesn't break # the heuristic for type instability warning. From 288dbb0f0dd852b088c4dd419abb688ccb3f00c0 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Mon, 21 Aug 2017 21:32:33 +0200 Subject: [PATCH 096/324] add VS Code to list of supported IDEs (#23293) * add vscode to list of supported IDEs * Update README.md * fix trailing whitespace [av skip] --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d6b9b28893ded..90b248c40542c 100644 --- a/README.md +++ b/README.md @@ -434,8 +434,10 @@ editors. While Julia modes for others such as Textmate, Notepad++, and Kate, are in `contrib/`. -Two major IDEs are supported for Julia: [Juno](http://junolab.org/), -which is based on [Atom](https://atom.io/), and +Three major IDEs are supported for Julia: [Juno](http://junolab.org/) +which is based on [Atom](https://atom.io/), +[julia-vscode](https://github.com/JuliaEditorSupport/julia-vscode) +based on [VS Code](https://code.visualstudio.com/), and [JuliaDT](https://github.com/JuliaComputing/JuliaDT), which is an [Eclipse](http://eclipse.org) plugin. A [Jupyter](http://jupyter.org/) notebooks interface is available through From 792fb1dc6d1435d70faff9ac7e7e27367400b20e Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Mon, 21 Aug 2017 16:20:47 -0400 Subject: [PATCH 097/324] fix over-eager removal of `newvar` nodes in the presence of `isdefined` --- src/julia-syntax.scm | 23 ++++++++++++++++++----- test/core.jl | 9 +++++++++ 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 42cbb9fecf7d7..02af416bba517 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -3768,12 +3768,23 @@ f(x) = yt(x) '() arg-map)) body)))) -;; find newvar nodes that are unnecessary because (1) the variable is not +(define (for-each-isdefined f e) + (cond ((or (atom? e) (quoted? e)) #f) + ((and (pair? e) (eq? (car e) 'isdefined)) + (f (cadr e))) + (else + (for-each (lambda (x) (for-each-isdefined f x)) + (cdr e))))) + +;; Find newvar nodes that are unnecessary because (1) the variable is not ;; captured, and (2) the variable is assigned before any branches. -;; this is used to remove newvar nodes that are not needed for re-initializing -;; variables to undefined (see issue #11065). it doesn't look for variable -;; *uses*, because any variables used-before-def that also pass this test -;; are *always* used undefined, and therefore don't need to be *re*-initialized. +;; This is used to remove newvar nodes that are not needed for re-initializing +;; variables to undefined (see issue #11065). +;; It doesn't look for variable *uses*, because any variables used-before-def +;; that also pass this test are *always* used undefined, and therefore don't need +;; to be *re*-initialized. +;; The one exception to that is `@isdefined`, which can observe an undefined +;; variable without throwing an error. (define (definitely-initialized-vars stmts vi) (let ((vars (table)) (di (table))) @@ -3782,6 +3793,8 @@ f(x) = yt(x) di (begin (let ((e (car stmts))) + (for-each-isdefined (lambda (x) (if (has? vars x) (del! vars x))) + e) (cond ((and (pair? e) (eq? (car e) 'newvar)) (let ((vinf (var-info-for (cadr e) vi))) (if (and vinf (not (vinfo:capt vinf))) diff --git a/test/core.jl b/test/core.jl index 7c53987223365..80381bc93c824 100644 --- a/test/core.jl +++ b/test/core.jl @@ -5117,6 +5117,15 @@ f_isdefined_va(::T...) where {T} = @isdefined T @test !f_isdefined_va() @test f_isdefined_va(1, 2, 3) +# @isdefined in a loop +let a = [] + for i = 1:2 + push!(a, @isdefined(j)) + local j = 1 + end + @test a == [false, false] +end + mutable struct MyStruct22929 x::MyStruct22929 MyStruct22929() = new() From 1dedf5053487d8b0a65556f39b1e1fbc42548b41 Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Mon, 21 Aug 2017 22:40:09 +0200 Subject: [PATCH 098/324] parametrize Tridiagonal on the wrapped vector type (#23154) --- NEWS.md | 21 ++++---- base/deprecated.jl | 8 +++ base/linalg/bidiag.jl | 6 ++- base/linalg/bunchkaufman.jl | 2 +- base/linalg/lu.jl | 27 +++++----- base/linalg/tridiag.jl | 100 ++++++++++++++++-------------------- test/linalg/tridiag.jl | 7 +++ test/show.jl | 2 +- 8 files changed, 90 insertions(+), 83 deletions(-) diff --git a/NEWS.md b/NEWS.md index 49b16cd7741c5..56ab9c8aaf929 100644 --- a/NEWS.md +++ b/NEWS.md @@ -121,10 +121,11 @@ This section lists changes that do not have deprecation warnings. longer present. Use `first(R)` and `last(R)` to obtain start/stop. ([#20974]) - * The `Diagonal`, `Bidiagonal` and `SymTridiagonal` type definitions have changed from - `Diagonal{T}`, `Bidiagonal{T}` and `SymTridiagonal{T}` to `Diagonal{T,V<:AbstractVector{T}}`, - `Bidiagonal{T,V<:AbstractVector{T}}` and `SymTridiagonal{T,V<:AbstractVector{T}}` - respectively ([#22718], [#22925], [#23035]). + * The `Diagonal`, `Bidiagonal`, `Tridiagonal` and `SymTridiagonal` type definitions have + changed from `Diagonal{T}`, `Bidiagonal{T}`, `Tridiagonal{T}` and `SymTridiagonal{T}` + to `Diagonal{T,V<:AbstractVector{T}}`, `Bidiagonal{T,V<:AbstractVector{T}}`, + `Tridiagonal{T,V<:AbstractVector{T}}` and `SymTridiagonal{T,V<:AbstractVector{T}}` + respectively ([#22718], [#22925], [#23035], [#23154]). * `isapprox(x,y)` now tests `norm(x-y) <= max(atol, rtol*max(norm(x), norm(y)))` rather than `norm(x-y) <= atol + ...`, and `rtol` defaults to zero @@ -196,9 +197,10 @@ Library improvements * `Char`s can now be concatenated with `String`s and/or other `Char`s using `*` ([#22532]). - * `Diagonal`, `Bidiagonal` and `SymTridiagonal` are now parameterized on the type of the - wrapped vectors, allowing `Diagonal`, `Bidiagonal` and `SymTridiagonal` matrices with - arbitrary `AbstractVector`s ([#22718], [#22925], [#23035]). + * `Diagonal`, `Bidiagonal`, `Tridiagonal` and `SymTridiagonal` are now parameterized on + the type of the wrapped vectors, allowing `Diagonal`, `Bidiagonal`, `Tridiagonal` and + `SymTridiagonal` matrices with arbitrary `AbstractVector`s + ([#22718], [#22925], [#23035], [#23154]). * Mutating versions of `randperm` and `randcycle` have been added: `randperm!` and `randcycle!` ([#22723]). @@ -261,8 +263,9 @@ Deprecated or removed * `Bidiagonal` constructors now use a `Symbol` (`:U` or `:L`) for the upper/lower argument, instead of a `Bool` or a `Char` ([#22703]). - * `Bidiagonal` and `SymTridiagonal` constructors that automatically converted the input - vectors to the same type are deprecated in favor of explicit conversion ([#22925], [#23035]). + * `Bidiagonal`, `Tridiagonal` and `SymTridiagonal` constructors that automatically + converted the input vectors to the same type are deprecated in favor of explicit + conversion ([#22925], [#23035], [#23154]. * Calling `nfields` on a type to find out how many fields its instances have is deprecated. Use `fieldcount` instead. Use `nfields` only to get the number of fields in a specific object ([#22350]). diff --git a/base/deprecated.jl b/base/deprecated.jl index b4dc3e0c509c2..926ce2f4b3b79 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1652,6 +1652,14 @@ function SymTridiagonal(dv::AbstractVector{T}, ev::AbstractVector{S}) where {T,S SymTridiagonal(convert(Vector{R}, dv), convert(Vector{R}, ev)) end +# PR #23154 +# also uncomment constructor tests in test/linalg/tridiag.jl +function Tridiagonal(dl::AbstractVector{Tl}, d::AbstractVector{Td}, du::AbstractVector{Tu}) where {Tl,Td,Tu} + depwarn(string("Tridiagonal(dl::AbstractVector{Tl}, d::AbstractVector{Td}, du::AbstractVector{Tu}) ", + "where {Tl, Td, Tu} is deprecated; convert all vectors to the same type instead."), :Tridiagonal) + Tridiagonal(map(v->convert(Vector{promote_type(Tl,Td,Tu)}, v), (dl, d, du))...) +end + # PR #23092 @eval LibGit2 begin function prompt(msg::AbstractString; default::AbstractString="", password::Bool=false) diff --git a/base/linalg/bidiag.jl b/base/linalg/bidiag.jl index 4c18334f4f172..cce36f22dbb76 100644 --- a/base/linalg/bidiag.jl +++ b/base/linalg/bidiag.jl @@ -153,8 +153,10 @@ promote_rule(::Type{Matrix{T}}, ::Type{Bidiagonal{S}}) where {T,S} = Matrix{prom #Converting from Bidiagonal to Tridiagonal Tridiagonal(M::Bidiagonal{T}) where {T} = convert(Tridiagonal{T}, M) function convert(::Type{Tridiagonal{T}}, A::Bidiagonal) where T - z = zeros(T, size(A)[1]-1) - A.uplo == 'U' ? Tridiagonal(z, convert(Vector{T},A.dv), convert(Vector{T},A.ev)) : Tridiagonal(convert(Vector{T},A.ev), convert(Vector{T},A.dv), z) + dv = convert(AbstractVector{T}, A.dv) + ev = convert(AbstractVector{T}, A.ev) + z = fill!(similar(ev), zero(T)) + A.uplo == 'U' ? Tridiagonal(z, dv, ev) : Tridiagonal(ev, dv, z) end promote_rule(::Type{Tridiagonal{T}}, ::Type{Bidiagonal{S}}) where {T,S} = Tridiagonal{promote_type(T,S)} diff --git a/base/linalg/bunchkaufman.jl b/base/linalg/bunchkaufman.jl index 12763756fca5b..3d3865b64fbb6 100644 --- a/base/linalg/bunchkaufman.jl +++ b/base/linalg/bunchkaufman.jl @@ -116,7 +116,7 @@ julia> A = [1 2 3; 2 1 2; 3 2 1] julia> F = bkfact(Symmetric(A, :L)) Base.LinAlg.BunchKaufman{Float64,Array{Float64,2}} D factor: -3×3 Tridiagonal{Float64}: +3×3 Tridiagonal{Float64,Array{Float64,1}}: 1.0 3.0 ⋅ 3.0 1.0 0.0 ⋅ 0.0 -1.0 diff --git a/base/linalg/lu.jl b/base/linalg/lu.jl index d25f61a2a406f..fcab79e899e68 100644 --- a/base/linalg/lu.jl +++ b/base/linalg/lu.jl @@ -327,14 +327,14 @@ end # Tridiagonal # See dgttrf.f -function lufact!(A::Tridiagonal{T}, pivot::Union{Val{false}, Val{true}} = Val(true)) where T +function lufact!(A::Tridiagonal{T,V}, pivot::Union{Val{false}, Val{true}} = Val(true)) where {T,V} n = size(A, 1) info = 0 ipiv = Vector{BlasInt}(n) dl = A.dl d = A.d du = A.du - du2 = A.du2 + du2 = fill!(similar(d, n-2), 0)::V @inbounds begin for i = 1:n @@ -389,12 +389,13 @@ function lufact!(A::Tridiagonal{T}, pivot::Union{Val{false}, Val{true}} = Val(tr end end end - LU{T,Tridiagonal{T}}(A, ipiv, convert(BlasInt, info)) + B = Tridiagonal{T,V}(dl, d, du, du2) + LU{T,Tridiagonal{T,V}}(B, ipiv, convert(BlasInt, info)) end factorize(A::Tridiagonal) = lufact(A) -function getindex(F::Base.LinAlg.LU{T,Tridiagonal{T}}, d::Symbol) where T +function getindex(F::LU{T,Tridiagonal{T,V}}, d::Symbol) where {T,V} m, n = size(F) if d == :L L = Array(Bidiagonal(ones(T, n), F.factors.dl, d)) @@ -419,7 +420,7 @@ function getindex(F::Base.LinAlg.LU{T,Tridiagonal{T}}, d::Symbol) where T end # See dgtts2.f -function A_ldiv_B!(A::LU{T,Tridiagonal{T}}, B::AbstractVecOrMat) where T +function A_ldiv_B!(A::LU{T,Tridiagonal{T,V}}, B::AbstractVecOrMat) where {T,V} n = size(A,1) if n != size(B,1) throw(DimensionMismatch("matrix has dimensions ($n,$n) but right hand side has $(size(B,1)) rows")) @@ -450,7 +451,7 @@ function A_ldiv_B!(A::LU{T,Tridiagonal{T}}, B::AbstractVecOrMat) where T return B end -function At_ldiv_B!(A::LU{T,Tridiagonal{T}}, B::AbstractVecOrMat) where T +function At_ldiv_B!(A::LU{T,Tridiagonal{T,V}}, B::AbstractVecOrMat) where {T,V} n = size(A,1) if n != size(B,1) throw(DimensionMismatch("matrix has dimensions ($n,$n) but right hand side has $(size(B,1)) rows")) @@ -485,7 +486,7 @@ function At_ldiv_B!(A::LU{T,Tridiagonal{T}}, B::AbstractVecOrMat) where T end # Ac_ldiv_B!(A::LU{T,Tridiagonal{T}}, B::AbstractVecOrMat) where {T<:Real} = At_ldiv_B!(A,B) -function Ac_ldiv_B!(A::LU{T,Tridiagonal{T}}, B::AbstractVecOrMat) where T +function Ac_ldiv_B!(A::LU{T,Tridiagonal{T,V}}, B::AbstractVecOrMat) where {T,V} n = size(A,1) if n != size(B,1) throw(DimensionMismatch("matrix has dimensions ($n,$n) but right hand side has $(size(B,1)) rows")) @@ -528,7 +529,7 @@ convert(::Type{Matrix}, F::LU) = convert(Array, convert(AbstractArray, F)) convert(::Type{Array}, F::LU) = convert(Matrix, F) full(F::LU) = convert(AbstractArray, F) -function convert(::Type{Tridiagonal}, F::Base.LinAlg.LU{T,Tridiagonal{T}}) where T +function convert(::Type{Tridiagonal}, F::Base.LinAlg.LU{T,Tridiagonal{T,V}}) where {T,V} n = size(F, 1) dl = copy(F.factors.dl) @@ -562,12 +563,12 @@ function convert(::Type{Tridiagonal}, F::Base.LinAlg.LU{T,Tridiagonal{T}}) where end return Tridiagonal(dl, d, du) end -convert(::Type{AbstractMatrix}, F::Base.LinAlg.LU{T,Tridiagonal{T}}) where {T} = +convert(::Type{AbstractMatrix}, F::LU{T,Tridiagonal{T,V}}) where {T,V} = convert(Tridiagonal, F) -convert(::Type{AbstractArray}, F::Base.LinAlg.LU{T,Tridiagonal{T}}) where {T} = +convert(::Type{AbstractArray}, F::LU{T,Tridiagonal{T,V}}) where {T,V} = convert(AbstractMatrix, F) -convert(::Type{Matrix}, F::Base.LinAlg.LU{T,Tridiagonal{T}}) where {T} = +convert(::Type{Matrix}, F::LU{T,Tridiagonal{T,V}}) where {T,V} = convert(Array, convert(AbstractArray, F)) -convert(::Type{Array}, F::Base.LinAlg.LU{T,Tridiagonal{T}}) where {T} = +convert(::Type{Array}, F::LU{T,Tridiagonal{T,V}}) where {T,V} = convert(Matrix, F) -full(F::Base.LinAlg.LU{T,Tridiagonal{T}}) where {T} = convert(AbstractArray, F) +full(F::LU{T,Tridiagonal{T,V}}) where {T,V} = convert(AbstractArray, F) diff --git a/base/linalg/tridiag.jl b/base/linalg/tridiag.jl index 5fcaf92009201..d3ae5e1eb9bcf 100644 --- a/base/linalg/tridiag.jl +++ b/base/linalg/tridiag.jl @@ -399,71 +399,58 @@ function setindex!(A::SymTridiagonal, x, i::Integer, j::Integer) end ## Tridiagonal matrices ## -struct Tridiagonal{T} <: AbstractMatrix{T} - dl::Vector{T} # sub-diagonal - d::Vector{T} # diagonal - du::Vector{T} # sup-diagonal - du2::Vector{T} # supsup-diagonal for pivoting +struct Tridiagonal{T,V<:AbstractVector{T}} <: AbstractMatrix{T} + dl::V # sub-diagonal + d::V # diagonal + du::V # sup-diagonal + du2::V # supsup-diagonal for pivoting in LU + function Tridiagonal{T}(dl::V, d::V, du::V) where {T,V<:AbstractVector{T}} + n = length(d) + if (length(dl) != n-1 || length(du) != n-1) + throw(ArgumentError(string("cannot construct Tridiagonal from incompatible ", + "lengths of subdiagonal, diagonal and superdiagonal: ", + "($(length(dl)), $(length(d)), $(length(du)))"))) + end + new{T,V}(dl, d, du) + end + # constructor used in lufact! + function Tridiagonal{T,V}(dl::V, d::V, du::V, du2::V) where {T,V<:AbstractVector{T}} + new{T,V}(dl, d, du, du2) + end end """ - Tridiagonal(dl, d, du) + Tridiagonal(dl::V, d::V, du::V) where V <: AbstractVector Construct a tridiagonal matrix from the first subdiagonal, diagonal, and first superdiagonal, -respectively. The result is of type `Tridiagonal` and provides efficient specialized linear +respectively. The result is of type `Tridiagonal` and provides efficient specialized linear solvers, but may be converted into a regular matrix with [`convert(Array, _)`](@ref) (or `Array(_)` for short). The lengths of `dl` and `du` must be one less than the length of `d`. # Examples ```jldoctest -julia> dl = [1; 2; 3] -3-element Array{Int64,1}: - 1 - 2 - 3 +julia> dl = [1, 2, 3]; -julia> du = [4; 5; 6] -3-element Array{Int64,1}: - 4 - 5 - 6 +julia> du = [4, 5, 6]; -julia> d = [7; 8; 9; 0] -4-element Array{Int64,1}: - 7 - 8 - 9 - 0 +julia> d = [7, 8, 9, 0]; julia> Tridiagonal(dl, d, du) -4×4 Tridiagonal{Int64}: +4×4 Tridiagonal{Int64,Array{Int64,1}}: 7 4 ⋅ ⋅ 1 8 5 ⋅ ⋅ 2 9 6 ⋅ ⋅ 3 0 ``` """ -# Basic constructor takes in three dense vectors of same type -function Tridiagonal(dl::Vector{T}, d::Vector{T}, du::Vector{T}) where T - n = length(d) - if (length(dl) != n-1 || length(du) != n-1) - throw(ArgumentError("cannot make Tridiagonal from incompatible lengths of subdiagonal, diagonal and superdiagonal: ($(length(dl)), $(length(d)), $(length(du))")) - end - Tridiagonal(dl, d, du, zeros(T,n-2)) -end - -# Construct from diagonals of any abstract vector, any eltype -function Tridiagonal(dl::AbstractVector{Tl}, d::AbstractVector{Td}, du::AbstractVector{Tu}) where {Tl,Td,Tu} - Tridiagonal(map(v->convert(Vector{promote_type(Tl,Td,Tu)}, v), (dl, d, du))...) -end +Tridiagonal(dl::V, d::V, du::V) where {T,V<:AbstractVector{T}} = Tridiagonal{T}(dl, d, du) -# Provide a constructor Tridiagonal(A) similar to the triangulars, diagonal, symmetric """ Tridiagonal(A) -returns a `Tridiagonal` array based on (abstract) matrix `A`, using its first lower diagonal, -main diagonal, and first upper diagonal. +Construct a tridiagonal matrix from the first sub-diagonal, +diagonal and first super-diagonal of the matrix `A`. # Examples ```jldoctest @@ -475,16 +462,14 @@ julia> A = [1 2 3 4; 1 2 3 4; 1 2 3 4; 1 2 3 4] 1 2 3 4 julia> Tridiagonal(A) -4×4 Tridiagonal{Int64}: +4×4 Tridiagonal{Int64,Array{Int64,1}}: 1 2 ⋅ ⋅ 1 2 3 ⋅ ⋅ 2 3 4 ⋅ ⋅ 3 4 ``` """ -function Tridiagonal(A::AbstractMatrix) - return Tridiagonal(diag(A,-1), diag(A), diag(A,+1)) -end +Tridiagonal(A::AbstractMatrix) = Tridiagonal(diag(A,-1), diag(A,0), diag(A,1)) size(M::Tridiagonal) = (length(M.d), length(M.d)) function size(M::Tridiagonal, d::Integer) @@ -512,31 +497,31 @@ convert(::Type{Matrix}, M::Tridiagonal{T}) where {T} = convert(Matrix{T}, M) convert(::Type{Array}, M::Tridiagonal) = convert(Matrix, M) full(M::Tridiagonal) = convert(Array, M) function similar(M::Tridiagonal, ::Type{T}) where T - Tridiagonal{T}(similar(M.dl, T), similar(M.d, T), similar(M.du, T), similar(M.du2, T)) + Tridiagonal{T}(similar(M.dl, T), similar(M.d, T), similar(M.du, T)) end # Operations on Tridiagonal matrices -copy!(dest::Tridiagonal, src::Tridiagonal) = Tridiagonal(copy!(dest.dl, src.dl), copy!(dest.d, src.d), copy!(dest.du, src.du), copy!(dest.du2, src.du2)) +copy!(dest::Tridiagonal, src::Tridiagonal) = (copy!(dest.dl, src.dl); copy!(dest.d, src.d); copy!(dest.du, src.du); dest) #Elementary operations -broadcast(::typeof(abs), M::Tridiagonal) = Tridiagonal(abs.(M.dl), abs.(M.d), abs.(M.du), abs.(M.du2)) -broadcast(::typeof(round), M::Tridiagonal) = Tridiagonal(round.(M.dl), round.(M.d), round.(M.du), round.(M.du2)) -broadcast(::typeof(trunc), M::Tridiagonal) = Tridiagonal(trunc.(M.dl), trunc.(M.d), trunc.(M.du), trunc.(M.du2)) -broadcast(::typeof(floor), M::Tridiagonal) = Tridiagonal(floor.(M.dl), floor.(M.d), floor.(M.du), floor.(M.du2)) -broadcast(::typeof(ceil), M::Tridiagonal) = Tridiagonal(ceil.(M.dl), ceil.(M.d), ceil.(M.du), ceil.(M.du2)) +broadcast(::typeof(abs), M::Tridiagonal) = Tridiagonal(abs.(M.dl), abs.(M.d), abs.(M.du)) +broadcast(::typeof(round), M::Tridiagonal) = Tridiagonal(round.(M.dl), round.(M.d), round.(M.du)) +broadcast(::typeof(trunc), M::Tridiagonal) = Tridiagonal(trunc.(M.dl), trunc.(M.d), trunc.(M.du)) +broadcast(::typeof(floor), M::Tridiagonal) = Tridiagonal(floor.(M.dl), floor.(M.d), floor.(M.du)) +broadcast(::typeof(ceil), M::Tridiagonal) = Tridiagonal(ceil.(M.dl), ceil.(M.d), ceil.(M.du)) for func in (:conj, :copy, :real, :imag) @eval function ($func)(M::Tridiagonal) - Tridiagonal(($func)(M.dl), ($func)(M.d), ($func)(M.du), ($func)(M.du2)) + Tridiagonal(($func)(M.dl), ($func)(M.d), ($func)(M.du)) end end broadcast(::typeof(round), ::Type{T}, M::Tridiagonal) where {T<:Integer} = - Tridiagonal(round.(T, M.dl), round.(T, M.d), round.(T, M.du), round.(T, M.du2)) + Tridiagonal(round.(T, M.dl), round.(T, M.d), round.(T, M.du)) broadcast(::typeof(trunc), ::Type{T}, M::Tridiagonal) where {T<:Integer} = - Tridiagonal(trunc.(T, M.dl), trunc.(T, M.d), trunc.(T, M.du), trunc.(T, M.du2)) + Tridiagonal(trunc.(T, M.dl), trunc.(T, M.d), trunc.(T, M.du)) broadcast(::typeof(floor), ::Type{T}, M::Tridiagonal) where {T<:Integer} = - Tridiagonal(floor.(T, M.dl), floor.(T, M.d), floor.(T, M.du), floor.(T, M.du2)) + Tridiagonal(floor.(T, M.dl), floor.(T, M.d), floor.(T, M.du)) broadcast(::typeof(ceil), ::Type{T}, M::Tridiagonal) where {T<:Integer} = - Tridiagonal(ceil.(T, M.dl), ceil.(T, M.d), ceil.(T, M.du), ceil.(T, M.du2)) + Tridiagonal(ceil.(T, M.dl), ceil.(T, M.d), ceil.(T, M.du)) transpose(M::Tridiagonal) = Tridiagonal(M.du, M.d, M.dl) ctranspose(M::Tridiagonal) = conj(transpose(M)) @@ -646,7 +631,8 @@ end inv(A::Tridiagonal) = inv_usmani(A.dl, A.d, A.du) det(A::Tridiagonal) = det_usmani(A.dl, A.d, A.du) -convert(::Type{Tridiagonal{T}},M::Tridiagonal) where {T} = Tridiagonal(convert(Vector{T}, M.dl), convert(Vector{T}, M.d), convert(Vector{T}, M.du), convert(Vector{T}, M.du2)) +convert(::Type{Tridiagonal{T}},M::Tridiagonal) where {T} = + Tridiagonal(convert(AbstractVector{T}, M.dl), convert(AbstractVector{T}, M.d), convert(AbstractVector{T}, M.du)) convert(::Type{AbstractMatrix{T}},M::Tridiagonal) where {T} = convert(Tridiagonal{T}, M) convert(::Type{Tridiagonal{T}}, M::SymTridiagonal{T}) where {T} = Tridiagonal(M) function convert(::Type{SymTridiagonal{T}}, M::Tridiagonal) where T diff --git a/test/linalg/tridiag.jl b/test/linalg/tridiag.jl index 6027c7981fe9c..2e231a7ec337a 100644 --- a/test/linalg/tridiag.jl +++ b/test/linalg/tridiag.jl @@ -41,10 +41,17 @@ B = randn(n,2) @test ST == Matrix(ST) @test ST.dv === x @test ST.ev === y + TT = (Tridiagonal(y, x, y))::Tridiagonal{elty, typeof(x)} + @test TT == Matrix(TT) + @test TT.dl === y + @test TT.d === x + @test TT.du === y end # enable when deprecations for 0.7 are dropped # @test_throws MethodError SymTridiagonal(dv, GenericArray(ev)) # @test_throws MethodError SymTridiagonal(GenericArray(dv), ev) + # @test_throws MethodError Tridiagonal(GenericArray(ev), dv, GenericArray(ev)) + # @test_throws MethodError Tridiagonal(ev, GenericArray(dv), ev) end @testset "size and Array" begin diff --git a/test/show.jl b/test/show.jl index 0a17da25462ba..5d576dcd2920d 100644 --- a/test/show.jl +++ b/test/show.jl @@ -598,7 +598,7 @@ A = reshape(1:16,4,4) @test replstr(Bidiagonal(A,:U)) == "4×4 Bidiagonal{$(Int),Array{$(Int),1}}:\n 1 5 ⋅ ⋅\n ⋅ 6 10 ⋅\n ⋅ ⋅ 11 15\n ⋅ ⋅ ⋅ 16" @test replstr(Bidiagonal(A,:L)) == "4×4 Bidiagonal{$(Int),Array{$(Int),1}}:\n 1 ⋅ ⋅ ⋅\n 2 6 ⋅ ⋅\n ⋅ 7 11 ⋅\n ⋅ ⋅ 12 16" @test replstr(SymTridiagonal(A+A')) == "4×4 SymTridiagonal{$(Int),Array{$(Int),1}}:\n 2 7 ⋅ ⋅\n 7 12 17 ⋅\n ⋅ 17 22 27\n ⋅ ⋅ 27 32" -@test replstr(Tridiagonal(diag(A,-1),diag(A),diag(A,+1))) == "4×4 Tridiagonal{$Int}:\n 1 5 ⋅ ⋅\n 2 6 10 ⋅\n ⋅ 7 11 15\n ⋅ ⋅ 12 16" +@test replstr(Tridiagonal(diag(A,-1),diag(A),diag(A,+1))) == "4×4 Tridiagonal{$(Int),Array{$(Int),1}}:\n 1 5 ⋅ ⋅\n 2 6 10 ⋅\n ⋅ 7 11 15\n ⋅ ⋅ 12 16" @test replstr(UpperTriangular(copy(A))) == "4×4 UpperTriangular{$Int,Array{$Int,2}}:\n 1 5 9 13\n ⋅ 6 10 14\n ⋅ ⋅ 11 15\n ⋅ ⋅ ⋅ 16" @test replstr(LowerTriangular(copy(A))) == "4×4 LowerTriangular{$Int,Array{$Int,2}}:\n 1 ⋅ ⋅ ⋅\n 2 6 ⋅ ⋅\n 3 7 11 ⋅\n 4 8 12 16" From 2c915ab6d9580424f1365b38639d3096b8cf133d Mon Sep 17 00:00:00 2001 From: Mus M Date: Mon, 21 Aug 2017 17:05:01 -0400 Subject: [PATCH 099/324] Update deprecated ccall syntax in arpack (#23307) --- base/linalg/arpack.jl | 182 +++++++++++++++++++++--------------------- 1 file changed, 92 insertions(+), 90 deletions(-) diff --git a/base/linalg/arpack.jl b/base/linalg/arpack.jl index c268920fc3a91..c96f8362fb912 100644 --- a/base/linalg/arpack.jl +++ b/base/linalg/arpack.jl @@ -12,8 +12,7 @@ function aupd_wrapper(T, matvecA!::Function, matvecB::Function, solveSI::Functio tol::Real, maxiter::Integer, mode::Integer, v0::Vector) lworkl = cmplx ? ncv * (3*ncv + 5) : (sym ? ncv * (ncv + 8) : ncv * (3*ncv + 6) ) TR = cmplx ? T.types[1] : T - TOL = Vector{TR}(1) - TOL[1] = tol + TOL = Ref{TR}(tol) v = Matrix{T}(n, ncv) workd = Vector{T}(3*n) @@ -22,14 +21,14 @@ function aupd_wrapper(T, matvecA!::Function, matvecB::Function, solveSI::Functio if isempty(v0) resid = Vector{T}(n) - info = zeros(BlasInt, 1) + info = Ref{BlasInt}(0) else resid = deepcopy(v0) - info = ones(BlasInt, 1) + info = Ref{BlasInt}(1) end iparam = zeros(BlasInt, 11) ipntr = zeros(BlasInt, (sym && !cmplx) ? 11 : 14) - ido = zeros(BlasInt, 1) + ido = Ref{BlasInt}(0) iparam[1] = BlasInt(1) # ishifts iparam[3] = BlasInt(maxiter) # maxiter @@ -48,50 +47,50 @@ function aupd_wrapper(T, matvecA!::Function, matvecB::Function, solveSI::Functio naupd(ido, bmat, n, which, nev, TOL, resid, ncv, v, n, iparam, ipntr, workd, workl, lworkl, info) end - if info[1] != 0 - throw(ARPACKException(info[1])) + if info[] != 0 + throw(ARPACKException(info[])) end x = view(workd, ipntr[1]+zernm1) y = view(workd, ipntr[2]+zernm1) if mode == 1 # corresponds to dsdrv1, dndrv1 or zndrv1 - if ido[1] == 1 + if ido[] == 1 matvecA!(y, x) - elseif ido[1] == 99 + elseif ido[] == 99 break else throw(ARPACKException("unexpected behavior")) end elseif mode == 3 && bmat == "I" # corresponds to dsdrv2, dndrv2 or zndrv2 - if ido[1] == -1 || ido[1] == 1 + if ido[] == -1 || ido[] == 1 y[:] = solveSI(x) - elseif ido[1] == 99 + elseif ido[] == 99 break else throw(ARPACKException("unexpected behavior")) end elseif mode == 2 # corresponds to dsdrv3, dndrv3 or zndrv3 - if ido[1] == -1 || ido[1] == 1 + if ido[] == -1 || ido[] == 1 matvecA!(y, x) if sym x[:] = y # overwrite as per Remark 5 in dsaupd.f end y[:] = solveSI(y) - elseif ido[1] == 2 + elseif ido[] == 2 y[:] = matvecB(x) - elseif ido[1] == 99 + elseif ido[] == 99 break else throw(ARPACKException("unexpected behavior")) end elseif mode == 3 && bmat == "G" # corresponds to dsdrv4, dndrv4 or zndrv4 - if ido[1] == -1 + if ido[] == -1 y[:] = solveSI(matvecB(x)) - elseif ido[1] == 1 + elseif ido[] == 1 y[:] = solveSI(view(workd,ipntr[3]+zernm1)) - elseif ido[1] == 2 + elseif ido[] == 2 y[:] = matvecB(x) - elseif ido[1] == 99 + elseif ido[] == 99 break else throw(ARPACKException("unexpected behavior")) @@ -106,63 +105,63 @@ end function eupd_wrapper(T, n::Integer, sym::Bool, cmplx::Bool, bmat::String, nev::Integer, which::String, ritzvec::Bool, - TOL::Array, resid, ncv::Integer, v, ldv, sigma, iparam, ipntr, + TOL::Ref, resid, ncv::Integer, v, ldv, sigma, iparam, ipntr, workd, workl, lworkl, rwork) howmny = "A" select = Vector{BlasInt}(ncv) - info = zeros(BlasInt, 1) + info = Ref{BlasInt}(0) - dmap = x->abs.(x) + dmap = x -> abs.(x) if iparam[7] == 3 # shift-and-invert - dmap = x->abs.(1 ./ (x .- sigma)) + dmap = x -> abs.(1 ./ (x .- sigma)) elseif which == "LR" || which == "LA" || which == "BE" dmap = real elseif which == "SR" || which == "SA" - dmap = x->-real(x) + dmap = x -> -real(x) elseif which == "LI" dmap = imag elseif which == "SI" - dmap = x->-imag(x) + dmap = x -> -imag(x) end if cmplx d = Vector{T}(nev+1) - sigmar = ones(T, 1)*sigma + sigmar = Ref{T}(sigma) workev = Vector{T}(2ncv) neupd(ritzvec, howmny, select, d, v, ldv, sigmar, workev, bmat, n, which, nev, TOL, resid, ncv, v, ldv, iparam, ipntr, workd, workl, lworkl, rwork, info) - if info[1] != 0 - throw(ARPACKException(info[1])) + if info[] != 0 + throw(ARPACKException(info[])) end p = sortperm(dmap(d[1:nev]), rev=true) return ritzvec ? (d[p], v[1:n, p],iparam[5],iparam[3],iparam[9],resid) : (d[p],iparam[5],iparam[3],iparam[9],resid) elseif sym d = Vector{T}(nev) - sigmar = ones(T, 1)*sigma + sigmar = Ref{T}(sigma) seupd(ritzvec, howmny, select, d, v, ldv, sigmar, bmat, n, which, nev, TOL, resid, ncv, v, ldv, iparam, ipntr, workd, workl, lworkl, info) - if info[1] != 0 - throw(ARPACKException(info[1])) + if info[] != 0 + throw(ARPACKException(info[])) end p = sortperm(dmap(d), rev=true) return ritzvec ? (d[p], v[1:n, p],iparam[5],iparam[3],iparam[9],resid) : (d[p],iparam[5],iparam[3],iparam[9],resid) else - dr = Vector{T}(nev+1) - di = Vector{T}(nev+1) + dr = Vector{T}(nev+1) + di = Vector{T}(nev+1) fill!(dr,NaN) fill!(di,NaN) - sigmar = ones(T, 1)*real(sigma) - sigmai = ones(T, 1)*imag(sigma) + sigmar = Ref{T}(real(sigma)) + sigmai = Ref{T}(imag(sigma)) workev = Vector{T}(3*ncv) neupd(ritzvec, howmny, select, dr, di, v, ldv, sigmar, sigmai, workev, bmat, n, which, nev, TOL, resid, ncv, v, ldv, iparam, ipntr, workd, workl, lworkl, info) - if info[1] != 0 - throw(ARPACKException(info[1])) + if info[] != 0 + throw(ARPACKException(info[])) end evec = complex.(Matrix{T}(n, nev+1), Matrix{T}(n, nev+1)) @@ -203,52 +202,54 @@ for (T, saupd_name, seupd_name, naupd_name, neupd_name) in ((:Float64, :dsaupd_, :dseupd_, :dnaupd_, :dneupd_), (:Float32, :ssaupd_, :sseupd_, :snaupd_, :sneupd_)) @eval begin - function naupd(ido, bmat, n, evtype, nev, TOL::Array{$T}, resid::Array{$T}, ncv, v::Array{$T}, ldv, - iparam, ipntr, workd::Array{$T}, workl::Array{$T}, lworkl, info) + function naupd(ido, bmat, n, evtype, nev, TOL::Ref{$T}, resid::Vector{$T}, ncv, v::Matrix{$T}, ldv, + iparam, ipntr, workd::Vector{$T}, workl::Vector{$T}, lworkl, info) ccall(($(string(naupd_name)), :libarpack), Void, - (Ptr{BlasInt}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{UInt8}, Ptr{BlasInt}, - Ptr{$T}, Ptr{$T}, Ptr{BlasInt}, Ptr{$T}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ptr{BlasInt}, Ptr{BlasInt}, Clong, Clong), - ido, bmat, &n, evtype, &nev, TOL, resid, &ncv, v, &ldv, - iparam, ipntr, workd, workl, &lworkl, info, sizeof(bmat), sizeof(evtype)) + (Ref{BlasInt}, Ptr{UInt8}, Ref{BlasInt}, Ptr{UInt8}, Ref{BlasInt}, + Ptr{$T}, Ptr{$T}, Ref{BlasInt}, Ptr{$T}, Ref{BlasInt}, + Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ref{BlasInt}, Ref{BlasInt}, Clong, Clong), + ido, bmat, n, evtype, nev, + TOL, resid, ncv, v, ldv, + iparam, ipntr, workd, workl, lworkl, info, sizeof(bmat), sizeof(evtype)) end function neupd(rvec, howmny, select, dr, di, z, ldz, sigmar, sigmai, - workev::Array{$T}, bmat, n, evtype, nev, TOL::Array{$T}, resid::Array{$T}, ncv, v, ldv, - iparam, ipntr, workd::Array{$T}, workl::Array{$T}, lworkl, info) + workev::Vector{$T}, bmat, n, evtype, nev, TOL::Ref{$T}, resid::Vector{$T}, ncv, v, ldv, + iparam, ipntr, workd::Vector{$T}, workl::Vector{$T}, lworkl, info) ccall(($(string(neupd_name)), :libarpack), Void, - (Ptr{BlasInt}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ptr{$T}, - Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ptr{$T}, Ptr{UInt8}, Ptr{BlasInt}, - Ptr{UInt8}, Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ptr{BlasInt}, Ptr{$T}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, - Ptr{BlasInt}, Ptr{BlasInt}, Clong, Clong, Clong), - &rvec, howmny, select, dr, di, z, &ldz, sigmar, sigmai, - workev, bmat, &n, evtype, &nev, TOL, resid, &ncv, v, &ldv, - iparam, ipntr, workd, workl, &lworkl, info, - sizeof(howmny), sizeof(bmat), sizeof(evtype)) + (Ref{BlasInt}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ptr{$T}, Ref{BlasInt}, + Ref{$T}, Ref{$T}, Ptr{$T}, Ptr{UInt8}, Ref{BlasInt}, Ptr{UInt8}, Ref{BlasInt}, + Ptr{$T}, Ptr{$T}, Ref{BlasInt}, Ptr{$T}, Ref{BlasInt}, + Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ref{BlasInt}, Ref{BlasInt}, Clong, Clong, Clong), + rvec, howmny, select, dr, di, z, ldz, + sigmar, sigmai, workev, bmat, n, evtype, nev, + TOL, resid, ncv, v, ldv, + iparam, ipntr, workd, workl, lworkl, info, sizeof(howmny), sizeof(bmat), sizeof(evtype)) end - function saupd(ido, bmat, n, which, nev, TOL::Array{$T}, resid::Array{$T}, ncv, v::Array{$T}, ldv, - iparam, ipntr, workd::Array{$T}, workl::Array{$T}, lworkl, info) + function saupd(ido, bmat, n, which, nev, TOL::Ref{$T}, resid::Vector{$T}, ncv, v::Matrix{$T}, ldv, + iparam, ipntr, workd::Vector{$T}, workl::Vector{$T}, lworkl, info) ccall(($(string(saupd_name)), :libarpack), Void, - (Ptr{BlasInt}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{UInt8}, Ptr{BlasInt}, - Ptr{$T}, Ptr{$T}, Ptr{BlasInt}, Ptr{$T}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ptr{BlasInt}, Ptr{BlasInt}, Clong, Clong), - ido, bmat, &n, which, &nev, TOL, resid, &ncv, v, &ldv, - iparam, ipntr, workd, workl, &lworkl, info, sizeof(bmat), sizeof(which)) + (Ref{BlasInt}, Ptr{UInt8}, Ref{BlasInt}, Ptr{UInt8}, Ref{BlasInt}, + Ptr{$T}, Ptr{$T}, Ref{BlasInt}, Ptr{$T}, Ref{BlasInt}, + Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ref{BlasInt}, Ref{BlasInt}, Clong, Clong), + ido, bmat, n, which, nev, + TOL, resid, ncv, v, ldv, + iparam, ipntr, workd, workl, lworkl, info, sizeof(bmat), sizeof(which)) end function seupd(rvec, howmny, select, d, z, ldz, sigma, - bmat, n, evtype, nev, TOL::Array{$T}, resid::Array{$T}, ncv, v::Array{$T}, ldv, - iparam, ipntr, workd::Array{$T}, workl::Array{$T}, lworkl, info) + bmat, n, evtype, nev, TOL::Ref{$T}, resid::Vector{$T}, ncv, v::Matrix{$T}, ldv, + iparam, ipntr, workd::Vector{$T}, workl::Vector{$T}, lworkl, info) ccall(($(string(seupd_name)), :libarpack), Void, - (Ptr{BlasInt}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ptr{BlasInt}, Ptr{$T}, - Ptr{UInt8}, Ptr{BlasInt}, Ptr{UInt8}, Ptr{BlasInt}, - Ptr{$T}, Ptr{$T}, Ptr{BlasInt}, Ptr{$T}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ptr{BlasInt}, Ptr{BlasInt}, Clong, Clong, Clong), - &rvec, howmny, select, d, z, &ldz, sigma, - bmat, &n, evtype, &nev, TOL, resid, &ncv, v, &ldv, - iparam, ipntr, workd, workl, &lworkl, info, sizeof(howmny), sizeof(bmat), sizeof(evtype)) + (Ref{BlasInt}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ref{BlasInt}, + Ptr{$T}, Ptr{UInt8}, Ref{BlasInt}, Ptr{UInt8}, Ref{BlasInt}, + Ptr{$T}, Ptr{$T}, Ref{BlasInt}, Ptr{$T}, Ref{BlasInt}, + Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ref{BlasInt}, Ref{BlasInt}, Clong, Clong, Clong), + rvec, howmny, select, d, z, ldz, + sigma, bmat, n, evtype, nev, + TOL, resid, ncv, v, ldv, + iparam, ipntr, workd, workl, lworkl, info, sizeof(howmny), sizeof(bmat), sizeof(evtype)) end end end @@ -257,30 +258,31 @@ for (T, TR, naupd_name, neupd_name) in ((:Complex128, :Float64, :znaupd_, :zneupd_), (:Complex64, :Float32, :cnaupd_, :cneupd_)) @eval begin - function naupd(ido, bmat, n, evtype, nev, TOL::Array{$TR}, resid::Array{$T}, ncv, v::Array{$T}, ldv, - iparam, ipntr, workd::Array{$T}, workl::Array{$T}, lworkl, - rwork::Array{$TR}, info) + function naupd(ido, bmat, n, evtype, nev, TOL::Ref{$TR}, resid::Vector{$T}, ncv, v::Matrix{$T}, ldv, + iparam, ipntr, workd::Vector{$T}, workl::Vector{$T}, lworkl, + rwork::Vector{$TR}, info) ccall(($(string(naupd_name)), :libarpack), Void, - (Ptr{BlasInt}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{UInt8}, Ptr{BlasInt}, - Ptr{$TR}, Ptr{$T}, Ptr{BlasInt}, Ptr{$T}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ptr{BlasInt}, - Ptr{$TR}, Ptr{BlasInt}), - ido, bmat, &n, evtype, &nev, TOL, resid, &ncv, v, &ldv, - iparam, ipntr, workd, workl, &lworkl, rwork, info) + (Ref{BlasInt}, Ptr{UInt8}, Ref{BlasInt}, Ptr{UInt8}, Ref{BlasInt}, + Ptr{$TR}, Ptr{$T}, Ref{BlasInt}, Ptr{$T}, Ref{BlasInt}, + Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ref{BlasInt}, Ptr{$TR}, Ref{BlasInt}), + ido, bmat, n, evtype, nev, + TOL, resid, ncv, v, ldv, + iparam, ipntr, workd, workl, lworkl, rwork, info) end - function neupd(rvec, howmny, select, d, z, ldz, sigma, workev::Array{$T}, - bmat, n, evtype, nev, TOL::Array{$TR}, resid::Array{$T}, ncv, v::Array{$T}, ldv, - iparam, ipntr, workd::Array{$T}, workl::Array{$T}, lworkl, - rwork::Array{$TR}, info) + function neupd(rvec, howmny, select, d, z, ldz, sigma, workev::Vector{$T}, + bmat, n, evtype, nev, TOL::Ref{$TR}, resid::Vector{$T}, ncv, v::Matrix{$T}, ldv, + iparam, ipntr, workd::Vector{$T}, workl::Vector{$T}, lworkl, + rwork::Vector{$TR}, info) ccall(($(string(neupd_name)), :libarpack), Void, - (Ptr{BlasInt}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ptr{BlasInt}, - Ptr{$T}, Ptr{$T}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{UInt8}, Ptr{BlasInt}, - Ptr{$TR}, Ptr{$T}, Ptr{BlasInt}, Ptr{$T}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ptr{BlasInt}, Ptr{$TR}, Ptr{BlasInt}), - &rvec, howmny, select, d, z, &ldz, sigma, workev, - bmat, &n, evtype, &nev, TOL, resid, &ncv, v, &ldv, - iparam, ipntr, workd, workl, &lworkl, rwork, info) + (Ref{BlasInt}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ref{BlasInt}, + Ptr{$T}, Ptr{$T}, Ptr{UInt8}, Ref{BlasInt}, Ptr{UInt8}, Ref{BlasInt}, + Ptr{$TR}, Ptr{$T}, Ref{BlasInt}, Ptr{$T}, Ref{BlasInt}, + Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ref{BlasInt}, Ptr{$TR}, Ref{BlasInt}), + rvec, howmny, select, d, z, ldz, + sigma, workev, bmat, n, evtype, nev, + TOL, resid, ncv, v, ldv, + iparam, ipntr, workd, workl, lworkl, rwork, info) end end end From db88dd1e906085c14d1cb9f0376c26f0fcf109c8 Mon Sep 17 00:00:00 2001 From: Andreas Noack Date: Mon, 21 Aug 2017 17:11:39 -0400 Subject: [PATCH 100/324] Fix Schur Factorization for 0x0 matrices (#23360) Fixes #23359 --- base/linalg/lapack.jl | 32 ++++++++++++++++---------------- test/linalg/schur.jl | 6 ++++++ 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/base/linalg/lapack.jl b/base/linalg/lapack.jl index fcc66b0409c8d..847e0c13479b0 100644 --- a/base/linalg/lapack.jl +++ b/base/linalg/lapack.jl @@ -5558,15 +5558,15 @@ for (gees, gges, elty) in # $ WR( * ) function gees!(jobvs::Char, A::StridedMatrix{$elty}) chkstride1(A) - n = checksquare(A) - sdim = Vector{BlasInt}(1) - wr = similar(A, $elty, n) - wi = similar(A, $elty, n) - ldvs = jobvs == 'V' ? n : 1 - vs = similar(A, $elty, ldvs, n) - work = Vector{$elty}(1) + n = checksquare(A) + sdim = Vector{BlasInt}(1) + wr = similar(A, $elty, n) + wi = similar(A, $elty, n) + vs = similar(A, $elty, jobvs == 'V' ? n : 0, n) + ldvs = max(size(vs, 1), 1) + work = Vector{$elty}(1) lwork = BlasInt(-1) - info = Ref{BlasInt}() + info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($gees), liblapack), Void, (Ptr{UInt8}, Ptr{UInt8}, Ptr{Void}, Ptr{BlasInt}, @@ -5651,16 +5651,16 @@ for (gees, gges, elty, relty) in # COMPLEX*16 A( LDA, * ), VS( LDVS, * ), W( * ), WORK( * ) function gees!(jobvs::Char, A::StridedMatrix{$elty}) chkstride1(A) - n = checksquare(A) - sort = 'N' - sdim = BlasInt(0) - w = similar(A, $elty, n) - ldvs = jobvs == 'V' ? n : 1 - vs = similar(A, $elty, ldvs, n) - work = Vector{$elty}(1) + n = checksquare(A) + sort = 'N' + sdim = BlasInt(0) + w = similar(A, $elty, n) + vs = similar(A, $elty, jobvs == 'V' ? n : 1, n) + ldvs = max(size(vs, 1), 1) + work = Vector{$elty}(1) lwork = BlasInt(-1) rwork = Vector{$relty}(n) - info = Ref{BlasInt}() + info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($gees), liblapack), Void, (Ptr{UInt8}, Ptr{UInt8}, Ptr{Void}, Ptr{BlasInt}, diff --git a/test/linalg/schur.jl b/test/linalg/schur.jl index 953c250c8ff3c..dd88bda0c0554 100644 --- a/test/linalg/schur.jl +++ b/test/linalg/schur.jl @@ -109,4 +109,10 @@ aimg = randn(n,n)/2 @test NS[:Z] ≈ sZ end end + @testset "0x0 matrix" for A in (zeros(eltya, 0, 0), view(rand(eltya, 2, 2), 1:0, 1:0)) + T, Z, λ = Base.LinAlg.schur(A) + @test T == A + @test Z == A + @test λ == zeros(0) + end end From df9b1b08070cd809375fff9d6897ebb326c935dd Mon Sep 17 00:00:00 2001 From: Andy Ferris Date: Tue, 22 Aug 2017 10:48:36 +1000 Subject: [PATCH 101/324] Rename `ctranspose` to `adjoint` (#23235) * Rename `ctranspose` to `adjoint` * Add PR number and missing `!` * Remove dated commented out test --- NEWS.md | 3 +++ base/deprecated.jl | 6 +++++- base/exports.jl | 4 ++-- base/linalg/bidiag.jl | 4 ++-- base/linalg/bitarray.jl | 2 +- base/linalg/cholesky.jl | 2 +- base/linalg/conjarray.jl | 2 +- base/linalg/diagonal.jl | 24 ++++++++++++------------ base/linalg/factorization.jl | 2 +- base/linalg/generic.jl | 2 +- base/linalg/givens.jl | 4 ++-- base/linalg/linalg.jl | 6 +++--- base/linalg/lq.jl | 6 +++--- base/linalg/lu.jl | 4 ++-- base/linalg/matmul.jl | 16 ++++++++-------- base/linalg/qr.jl | 6 +++--- base/linalg/rowvector.jl | 32 ++++++++++++++++---------------- base/linalg/symmetric.jl | 12 ++++++------ base/linalg/transpose.jl | 22 +++++++++++----------- base/linalg/triangular.jl | 34 +++++++++++++++++----------------- base/linalg/tridiag.jl | 4 ++-- base/linalg/uniformscaling.jl | 4 ++-- base/number.jl | 2 +- base/operators.jl | 24 ++++++++++++------------ base/sparse/cholmod.jl | 24 ++++++++++++------------ base/sparse/linalg.jl | 8 ++++---- base/sparse/sparse.jl | 4 ++-- base/sparse/sparsematrix.jl | 4 ++-- base/sparse/sparsevector.jl | 2 +- doc/src/manual/functions.md | 2 +- doc/src/stdlib/linalg.md | 4 ++-- src/julia-syntax.scm | 2 +- test/arrayops.jl | 12 ++++++------ test/distributed_exec.jl | 2 +- test/linalg/bidiag.jl | 2 +- test/linalg/diagonal.jl | 4 ++-- test/linalg/generic.jl | 2 +- test/linalg/givens.jl | 8 ++++---- test/linalg/qr.jl | 6 +++--- test/linalg/symmetric.jl | 8 ++++---- test/linalg/triangular.jl | 12 ++++++------ test/linalg/tridiag.jl | 4 ++-- test/operators.jl | 2 -- test/sparse/sparse.jl | 10 +++++----- test/sparse/sparsevector.jl | 2 +- 45 files changed, 178 insertions(+), 173 deletions(-) diff --git a/NEWS.md b/NEWS.md index 56ab9c8aaf929..3d8d2927676a4 100644 --- a/NEWS.md +++ b/NEWS.md @@ -318,6 +318,9 @@ Deprecated or removed * `Base.cpad` has been removed; use an appropriate combination of `rpad` and `lpad` instead ([#23187]). + * `ctranspose` and `ctranspose!` have been deprecated in favor of `adjoint` and `adjoint!`, + respectively ([#23235]). + * `filter` and `filter!` on dictionaries now pass a single `key=>value` pair to the argument function, instead of two arguments ([#17886]). diff --git a/base/deprecated.jl b/base/deprecated.jl index 926ce2f4b3b79..ce59333032c4c 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1421,7 +1421,7 @@ end module Operators for op in [:!, :(!=), :(!==), :%, :&, :*, :+, :-, :/, ://, :<, :<:, :<<, :(<=), :<|, :(==), :(===), :>, :>:, :(>=), :>>, :>>>, :\, :^, :colon, - :ctranspose, :getindex, :hcat, :hvcat, :setindex!, :transpose, :vcat, + :adjoint, :getindex, :hcat, :hvcat, :setindex!, :transpose, :vcat, :xor, :|, :|>, :~, :×, :÷, :∈, :∉, :∋, :∌, :∘, :√, :∛, :∩, :∪, :≠, :≤, :≥, :⊆, :⊈, :⊊, :⊻, :⋅] if isdefined(Base, op) @@ -1692,6 +1692,10 @@ export hex2num # PR #22742: change in isapprox semantics @deprecate rtoldefault(x,y) rtoldefault(x,y,0) false +# PR #23235 +@deprecate ctranspose adjoint +@deprecate ctranspose! adjoint! + # issue #5148, PR #23259 # warning for `const` on locals should be changed to an error in julia-syntax.scm diff --git a/base/exports.jl b/base/exports.jl index 8a086f914ca3a..49d3ceb5d3568 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -555,8 +555,8 @@ export cond, condskeel, cross, - ctranspose!, - ctranspose, + adjoint!, + adjoint, det, diag, diagind, diff --git a/base/linalg/bidiag.jl b/base/linalg/bidiag.jl index cce36f22dbb76..9e3d08ceef2ad 100644 --- a/base/linalg/bidiag.jl +++ b/base/linalg/bidiag.jl @@ -223,7 +223,7 @@ broadcast(::typeof(floor), ::Type{T}, M::Bidiagonal) where {T<:Integer} = Bidiag broadcast(::typeof(ceil), ::Type{T}, M::Bidiagonal) where {T<:Integer} = Bidiagonal(ceil.(T, M.dv), ceil.(T, M.ev), M.uplo) transpose(M::Bidiagonal) = Bidiagonal(M.dv, M.ev, M.uplo == 'U' ? :L : :U) -ctranspose(M::Bidiagonal) = Bidiagonal(conj(M.dv), conj(M.ev), M.uplo == 'U' ? :L : :U) +adjoint(M::Bidiagonal) = Bidiagonal(conj(M.dv), conj(M.ev), M.uplo == 'U' ? :L : :U) istriu(M::Bidiagonal) = M.uplo == 'U' || iszero(M.ev) istril(M::Bidiagonal) = M.uplo == 'L' || iszero(M.ev) @@ -458,7 +458,7 @@ end #Linear solvers A_ldiv_B!(A::Union{Bidiagonal, AbstractTriangular}, b::AbstractVector) = naivesub!(A, b) At_ldiv_B!(A::Bidiagonal, b::AbstractVector) = A_ldiv_B!(transpose(A), b) -Ac_ldiv_B!(A::Bidiagonal, b::AbstractVector) = A_ldiv_B!(ctranspose(A), b) +Ac_ldiv_B!(A::Bidiagonal, b::AbstractVector) = A_ldiv_B!(adjoint(A), b) function A_ldiv_B!(A::Union{Bidiagonal,AbstractTriangular}, B::AbstractMatrix) nA,mA = size(A) tmp = similar(B,size(B,1)) diff --git a/base/linalg/bitarray.jl b/base/linalg/bitarray.jl index b591b94d0e358..4df257bfa75a6 100644 --- a/base/linalg/bitarray.jl +++ b/base/linalg/bitarray.jl @@ -295,4 +295,4 @@ function transpose(B::BitMatrix) return Bt end -ctranspose(B::Union{BitMatrix,BitVector}) = transpose(B) +adjoint(B::Union{BitMatrix,BitVector}) = transpose(B) diff --git a/base/linalg/cholesky.jl b/base/linalg/cholesky.jl index fe3bb861f5a58..8ada08741687d 100644 --- a/base/linalg/cholesky.jl +++ b/base/linalg/cholesky.jl @@ -150,7 +150,7 @@ function chol(A::RealHermSymComplexHerm) if A.uplo == 'U' copy!(AA, A.data) else - Base.ctranspose!(AA, A.data) + Base.adjoint!(AA, A.data) end chol!(Hermitian(AA, :U)) end diff --git a/base/linalg/conjarray.jl b/base/linalg/conjarray.jl index 0cd982761157b..c43bc0436b0b1 100644 --- a/base/linalg/conjarray.jl +++ b/base/linalg/conjarray.jl @@ -5,7 +5,7 @@ A lazy-view wrapper of an `AbstractArray`, taking the elementwise complex conjugate. This type is usually constructed (and unwrapped) via the [`conj`](@ref) function (or related -[`ctranspose`](@ref)), but currently this is the default behavior for `RowVector` only. For +[`adjoint`](@ref)), but currently this is the default behavior for `RowVector` only. For other arrays, the `ConjArray` constructor can be used directly. # Examples diff --git a/base/linalg/diagonal.jl b/base/linalg/diagonal.jl index 439e409958c52..3b0cd7d58a2f1 100644 --- a/base/linalg/diagonal.jl +++ b/base/linalg/diagonal.jl @@ -186,11 +186,11 @@ function A_mul_B!(D::Diagonal, B::UnitUpperTriangular) UpperTriangular(B.data) end -Ac_mul_B(D::Diagonal, B::Diagonal) = Diagonal(ctranspose.(D.diag) .* B.diag) -Ac_mul_B(A::AbstractTriangular, D::Diagonal) = A_mul_B!(ctranspose(A), D) +Ac_mul_B(D::Diagonal, B::Diagonal) = Diagonal(adjoint.(D.diag) .* B.diag) +Ac_mul_B(A::AbstractTriangular, D::Diagonal) = A_mul_B!(adjoint(A), D) function Ac_mul_B(A::AbstractMatrix, D::Diagonal) Ac = similar(A, promote_op(*, eltype(A), eltype(D.diag)), (size(A, 2), size(A, 1))) - ctranspose!(Ac, A) + adjoint!(Ac, A) A_mul_B!(Ac, D) end @@ -202,12 +202,12 @@ function At_mul_B(A::AbstractMatrix, D::Diagonal) A_mul_B!(At, D) end -A_mul_Bc(D::Diagonal, B::Diagonal) = Diagonal(D.diag .* ctranspose.(B.diag)) -A_mul_Bc(D::Diagonal, B::AbstractTriangular) = A_mul_B!(D, ctranspose(B)) +A_mul_Bc(D::Diagonal, B::Diagonal) = Diagonal(D.diag .* adjoint.(B.diag)) +A_mul_Bc(D::Diagonal, B::AbstractTriangular) = A_mul_B!(D, adjoint(B)) A_mul_Bc(D::Diagonal, Q::Union{Base.LinAlg.QRCompactWYQ,Base.LinAlg.QRPackedQ}) = A_mul_Bc!(Array(D), Q) function A_mul_Bc(D::Diagonal, A::AbstractMatrix) Ac = similar(A, promote_op(*, eltype(A), eltype(D.diag)), (size(A, 2), size(A, 1))) - ctranspose!(Ac, A) + adjoint!(Ac, A) A_mul_B!(D, Ac) end @@ -219,7 +219,7 @@ function A_mul_Bt(D::Diagonal, A::AbstractMatrix) A_mul_B!(D, At) end -Ac_mul_Bc(D::Diagonal, B::Diagonal) = Diagonal(ctranspose.(D.diag) .* ctranspose.(B.diag)) +Ac_mul_Bc(D::Diagonal, B::Diagonal) = Diagonal(adjoint.(D.diag) .* adjoint.(B.diag)) At_mul_Bt(D::Diagonal, B::Diagonal) = Diagonal(transpose.(D.diag) .* transpose.(B.diag)) A_mul_B!(A::Diagonal,B::Diagonal) = throw(MethodError(A_mul_B!, Tuple{Diagonal,Diagonal})) @@ -235,11 +235,11 @@ A_mul_Bc!(A::AbstractMatrix,B::Diagonal) = scale!(A,conj(B.diag)) # Get ambiguous method if try to unify AbstractVector/AbstractMatrix here using AbstractVecOrMat A_mul_B!(out::AbstractVector, A::Diagonal, in::AbstractVector) = out .= A.diag .* in -Ac_mul_B!(out::AbstractVector, A::Diagonal, in::AbstractVector) = out .= ctranspose.(A.diag) .* in +Ac_mul_B!(out::AbstractVector, A::Diagonal, in::AbstractVector) = out .= adjoint.(A.diag) .* in At_mul_B!(out::AbstractVector, A::Diagonal, in::AbstractVector) = out .= transpose.(A.diag) .* in A_mul_B!(out::AbstractMatrix, A::Diagonal, in::AbstractMatrix) = out .= A.diag .* in -Ac_mul_B!(out::AbstractMatrix, A::Diagonal, in::AbstractMatrix) = out .= ctranspose.(A.diag) .* in +Ac_mul_B!(out::AbstractMatrix, A::Diagonal, in::AbstractMatrix) = out .= adjoint.(A.diag) .* in At_mul_B!(out::AbstractMatrix, A::Diagonal, in::AbstractMatrix) = out .= transpose.(A.diag) .* in # ambiguities with Symmetric/Hermitian @@ -306,13 +306,13 @@ A_rdiv_Bt!(A::AbstractMatrix{T}, D::Diagonal{T}) where {T} = A_rdiv_B!(A, D) # Methods to resolve ambiguities with `Diagonal` @inline *(rowvec::RowVector, D::Diagonal) = transpose(D * transpose(rowvec)) @inline A_mul_Bt(D::Diagonal, rowvec::RowVector) = D*transpose(rowvec) -@inline A_mul_Bc(D::Diagonal, rowvec::RowVector) = D*ctranspose(rowvec) +@inline A_mul_Bc(D::Diagonal, rowvec::RowVector) = D*adjoint(rowvec) conj(D::Diagonal) = Diagonal(conj(D.diag)) transpose(D::Diagonal{<:Number}) = D transpose(D::Diagonal) = Diagonal(transpose.(D.diag)) -ctranspose(D::Diagonal{<:Number}) = conj(D) -ctranspose(D::Diagonal) = Diagonal(ctranspose.(D.diag)) +adjoint(D::Diagonal{<:Number}) = conj(D) +adjoint(D::Diagonal) = Diagonal(adjoint.(D.diag)) diag(D::Diagonal) = D.diag trace(D::Diagonal) = sum(D.diag) diff --git a/base/linalg/factorization.jl b/base/linalg/factorization.jl index e3b10d258857c..581f711657398 100644 --- a/base/linalg/factorization.jl +++ b/base/linalg/factorization.jl @@ -6,7 +6,7 @@ abstract type Factorization{T} end eltype(::Type{Factorization{T}}) where {T} = T transpose(F::Factorization) = error("transpose not implemented for $(typeof(F))") -ctranspose(F::Factorization) = error("ctranspose not implemented for $(typeof(F))") +adjoint(F::Factorization) = error("adjoint not implemented for $(typeof(F))") macro assertposdef(A, info) :($(esc(info)) == 0 ? $(esc(A)) : throw(PosDefException($(esc(info))))) diff --git a/base/linalg/generic.jl b/base/linalg/generic.jl index 8c69ebe1548b6..b08354ca30ecd 100644 --- a/base/linalg/generic.jl +++ b/base/linalg/generic.jl @@ -942,7 +942,7 @@ function ishermitian(A::AbstractMatrix) return false end for i = indsn, j = i:last(indsn) - if A[i,j] != ctranspose(A[j,i]) + if A[i,j] != adjoint(A[j,i]) return false end end diff --git a/base/linalg/givens.jl b/base/linalg/givens.jl index d822499d94c43..de621983494d4 100644 --- a/base/linalg/givens.jl +++ b/base/linalg/givens.jl @@ -41,8 +41,8 @@ convert(::Type{Rotation{T}}, R::Rotation) where {T} = Rotation{T}([convert(Given convert(::Type{AbstractRotation{T}}, G::Givens) where {T} = convert(Givens{T}, G) convert(::Type{AbstractRotation{T}}, R::Rotation) where {T} = convert(Rotation{T}, R) -ctranspose(G::Givens) = Givens(G.i1, G.i2, conj(G.c), -G.s) -ctranspose(R::Rotation{T}) where {T} = Rotation{T}(reverse!([ctranspose(r) for r in R.rotations])) +adjoint(G::Givens) = Givens(G.i1, G.i2, conj(G.c), -G.s) +adjoint(R::Rotation{T}) where {T} = Rotation{T}(reverse!([adjoint(r) for r in R.rotations])) realmin2(::Type{Float32}) = reinterpret(Float32, 0x26000000) realmin2(::Type{Float64}) = reinterpret(Float64, 0x21a0000000000000) diff --git a/base/linalg/linalg.jl b/base/linalg/linalg.jl index a510f0efc8b51..52228e1910d60 100644 --- a/base/linalg/linalg.jl +++ b/base/linalg/linalg.jl @@ -6,7 +6,7 @@ import Base: \, /, *, ^, +, -, == import Base: A_mul_Bt, At_ldiv_Bt, A_rdiv_Bc, At_ldiv_B, Ac_mul_Bc, A_mul_Bc, Ac_mul_B, Ac_ldiv_B, Ac_ldiv_Bc, At_mul_Bt, A_rdiv_Bt, At_mul_B import Base: USE_BLAS64, abs, big, broadcast, ceil, conj, convert, copy, copy!, - ctranspose, eltype, eye, findmax, findmin, fill!, floor, full, getindex, + adjoint, eltype, eye, findmax, findmin, fill!, floor, full, getindex, hcat, imag, indices, inv, isapprox, isone, IndexStyle, kron, length, map, ndims, oneunit, parent, power_by_squaring, print_matrix, promote_rule, real, round, setindex!, show, similar, size, transpose, trunc, typed_hcat @@ -63,8 +63,8 @@ export copy!, copy_transpose!, cross, - ctranspose, - ctranspose!, + adjoint, + adjoint!, det, diag, diagind, diff --git a/base/linalg/lq.jl b/base/linalg/lq.jl index b6b43289385fb..3c535433e6cf5 100644 --- a/base/linalg/lq.jl +++ b/base/linalg/lq.jl @@ -56,7 +56,7 @@ convert(::Type{Matrix}, A::LQ) = convert(Array, convert(AbstractArray, A)) convert(::Type{Array}, A::LQ) = convert(Matrix, A) full(A::LQ) = convert(AbstractArray, A) -ctranspose(A::LQ{T}) where {T} = QR{T,typeof(A.factors)}(A.factors', A.τ) +adjoint(A::LQ{T}) where {T} = QR{T,typeof(A.factors)}(A.factors', A.τ) function getindex(A::LQ, d::Symbol) m, n = size(A) @@ -164,7 +164,7 @@ for (f1, f2) in ((:A_mul_Bc, :A_mul_B!), function ($f1)(A::LQPackedQ, B::StridedVecOrMat) TAB = promote_type(eltype(A), eltype(B)) BB = similar(B, TAB, (size(B, 2), size(B, 1))) - ctranspose!(BB, B) + adjoint!(BB, B) return ($f2)(A, BB) end end @@ -198,7 +198,7 @@ for (f1, f2) in ((:Ac_mul_B, :A_mul_B!), function ($f1)(A::StridedMatrix, B::LQPackedQ) TAB = promote_type(eltype(A), eltype(B)) AA = similar(A, TAB, (size(A, 2), size(A, 1))) - ctranspose!(AA, A) + adjoint!(AA, A) return ($f2)(AA, B) end end diff --git a/base/linalg/lu.jl b/base/linalg/lu.jl index fcab79e899e68..8e6630a00bd41 100644 --- a/base/linalg/lu.jl +++ b/base/linalg/lu.jl @@ -275,8 +275,8 @@ At_ldiv_Bt(A::LU{T,<:StridedMatrix}, B::StridedVecOrMat{T}) where {T<:BlasFloat} At_ldiv_Bt(A::LU, B::StridedVecOrMat) = At_ldiv_B(A, transpose(B)) Ac_ldiv_Bc(A::LU{T,<:StridedMatrix}, B::StridedVecOrMat{T}) where {T<:BlasComplex} = - @assertnonsingular LAPACK.getrs!('C', A.factors, A.ipiv, ctranspose(B)) A.info -Ac_ldiv_Bc(A::LU, B::StridedVecOrMat) = Ac_ldiv_B(A, ctranspose(B)) + @assertnonsingular LAPACK.getrs!('C', A.factors, A.ipiv, adjoint(B)) A.info +Ac_ldiv_Bc(A::LU, B::StridedVecOrMat) = Ac_ldiv_B(A, adjoint(B)) function det(F::LU{T}) where T n = checksquare(F) diff --git a/base/linalg/matmul.jl b/base/linalg/matmul.jl index 6112d29be3b54..7badc4be6176b 100644 --- a/base/linalg/matmul.jl +++ b/base/linalg/matmul.jl @@ -663,14 +663,14 @@ function matmul2x2!(C::AbstractMatrix, tA, tB, A::AbstractMatrix, B::AbstractMat if tA == 'T' A11 = transpose(A[1,1]); A12 = transpose(A[2,1]); A21 = transpose(A[1,2]); A22 = transpose(A[2,2]) elseif tA == 'C' - A11 = ctranspose(A[1,1]); A12 = ctranspose(A[2,1]); A21 = ctranspose(A[1,2]); A22 = ctranspose(A[2,2]) + A11 = adjoint(A[1,1]); A12 = adjoint(A[2,1]); A21 = adjoint(A[1,2]); A22 = adjoint(A[2,2]) else A11 = A[1,1]; A12 = A[1,2]; A21 = A[2,1]; A22 = A[2,2] end if tB == 'T' B11 = transpose(B[1,1]); B12 = transpose(B[2,1]); B21 = transpose(B[1,2]); B22 = transpose(B[2,2]) elseif tB == 'C' - B11 = ctranspose(B[1,1]); B12 = ctranspose(B[2,1]); B21 = ctranspose(B[1,2]); B22 = ctranspose(B[2,2]) + B11 = adjoint(B[1,1]); B12 = adjoint(B[2,1]); B21 = adjoint(B[1,2]); B22 = adjoint(B[2,2]) else B11 = B[1,1]; B12 = B[1,2]; B21 = B[2,1]; B22 = B[2,2] end @@ -697,9 +697,9 @@ function matmul3x3!(C::AbstractMatrix, tA, tB, A::AbstractMatrix, B::AbstractMat A21 = transpose(A[1,2]); A22 = transpose(A[2,2]); A23 = transpose(A[3,2]) A31 = transpose(A[1,3]); A32 = transpose(A[2,3]); A33 = transpose(A[3,3]) elseif tA == 'C' - A11 = ctranspose(A[1,1]); A12 = ctranspose(A[2,1]); A13 = ctranspose(A[3,1]) - A21 = ctranspose(A[1,2]); A22 = ctranspose(A[2,2]); A23 = ctranspose(A[3,2]) - A31 = ctranspose(A[1,3]); A32 = ctranspose(A[2,3]); A33 = ctranspose(A[3,3]) + A11 = adjoint(A[1,1]); A12 = adjoint(A[2,1]); A13 = adjoint(A[3,1]) + A21 = adjoint(A[1,2]); A22 = adjoint(A[2,2]); A23 = adjoint(A[3,2]) + A31 = adjoint(A[1,3]); A32 = adjoint(A[2,3]); A33 = adjoint(A[3,3]) else A11 = A[1,1]; A12 = A[1,2]; A13 = A[1,3] A21 = A[2,1]; A22 = A[2,2]; A23 = A[2,3] @@ -711,9 +711,9 @@ function matmul3x3!(C::AbstractMatrix, tA, tB, A::AbstractMatrix, B::AbstractMat B21 = transpose(B[1,2]); B22 = transpose(B[2,2]); B23 = transpose(B[3,2]) B31 = transpose(B[1,3]); B32 = transpose(B[2,3]); B33 = transpose(B[3,3]) elseif tB == 'C' - B11 = ctranspose(B[1,1]); B12 = ctranspose(B[2,1]); B13 = ctranspose(B[3,1]) - B21 = ctranspose(B[1,2]); B22 = ctranspose(B[2,2]); B23 = ctranspose(B[3,2]) - B31 = ctranspose(B[1,3]); B32 = ctranspose(B[2,3]); B33 = ctranspose(B[3,3]) + B11 = adjoint(B[1,1]); B12 = adjoint(B[2,1]); B13 = adjoint(B[3,1]) + B21 = adjoint(B[1,2]); B22 = adjoint(B[2,2]); B23 = adjoint(B[3,2]) + B31 = adjoint(B[1,3]); B32 = adjoint(B[2,3]); B33 = adjoint(B[3,3]) else B11 = B[1,1]; B12 = B[1,2]; B13 = B[1,3] B21 = B[2,1]; B22 = B[2,2]; B23 = B[2,3] diff --git a/base/linalg/qr.jl b/base/linalg/qr.jl index a6e1e7522c429..46b46d3cb3cf3 100644 --- a/base/linalg/qr.jl +++ b/base/linalg/qr.jl @@ -595,7 +595,7 @@ for (f1, f2) in ((:A_mul_Bc, :A_mul_B!), function ($f1)(Q::AbstractQ, B::StridedVecOrMat) TQB = promote_type(eltype(Q), eltype(B)) Bc = similar(B, TQB, (size(B, 2), size(B, 1))) - ctranspose!(Bc, B) + adjoint!(Bc, B) return ($f2)(convert(AbstractMatrix{TQB}, Q), Bc) end end @@ -678,7 +678,7 @@ function A_mul_Bc(A::StridedMatrix, B::AbstractQ) throw(DimensionMismatch("matrix A has dimensions $(size(A)) but matrix B has dimensions $(size(B))")) end end -@inline A_mul_Bc(rowvec::RowVector, B::AbstractQ) = ctranspose(B*ctranspose(rowvec)) +@inline A_mul_Bc(rowvec::RowVector, B::AbstractQ) = adjoint(B*adjoint(rowvec)) ### AcQ/AcQc @@ -688,7 +688,7 @@ for (f1, f2) in ((:Ac_mul_B, :A_mul_B!), function ($f1)(A::StridedVecOrMat, Q::AbstractQ) TAQ = promote_type(eltype(A), eltype(Q)) Ac = similar(A, TAQ, (size(A, 2), size(A, 1))) - ctranspose!(Ac, A) + adjoint!(Ac, A) return ($f2)(Ac, convert(AbstractMatrix{TAQ}, Q)) end end diff --git a/base/linalg/rowvector.jl b/base/linalg/rowvector.jl index eb24fa5792517..b7b3270a507ef 100644 --- a/base/linalg/rowvector.jl +++ b/base/linalg/rowvector.jl @@ -6,7 +6,7 @@ A lazy-view wrapper of an [`AbstractVector`](@ref), which turns a length-`n` vector into a `1×n` shaped row vector and represents the transpose of a vector (the elements are also transposed recursively). This type is usually constructed (and unwrapped) via the [`transpose`](@ref) -function or `.'` operator (or related [`ctranspose`](@ref) or `'` operator). +function or `.'` operator (or related [`adjoint`](@ref) or `'` operator). By convention, a vector can be multiplied by a matrix on its left (`A * v`) whereas a row vector can be multiplied by a matrix on its right (such that `v.' * A = (A.' * v).'`). It @@ -75,12 +75,12 @@ julia> transpose(v) ``` """ @inline transpose(vec::AbstractVector) = RowVector(vec) -@inline ctranspose(vec::AbstractVector) = RowVector(_conj(vec)) +@inline adjoint(vec::AbstractVector) = RowVector(_conj(vec)) @inline transpose(rowvec::RowVector) = rowvec.vec @inline transpose(rowvec::ConjRowVector) = copy(rowvec.vec) # remove the ConjArray wrapper from any raw vector -@inline ctranspose(rowvec::RowVector) = conj(rowvec.vec) -@inline ctranspose(rowvec::RowVector{<:Real}) = rowvec.vec +@inline adjoint(rowvec::RowVector) = conj(rowvec.vec) +@inline adjoint(rowvec::RowVector{<:Real}) = rowvec.vec parent(rowvec::RowVector) = rowvec.vec @@ -208,24 +208,24 @@ At_mul_B(vec::AbstractVector, rowvec::RowVector) = throw(DimensionMismatch( # Conjugated forms A_mul_Bc(::RowVector, ::AbstractVector) = throw(DimensionMismatch("Cannot multiply two transposed vectors")) -@inline A_mul_Bc(rowvec::RowVector, mat::AbstractMatrix) = ctranspose(mat * ctranspose(rowvec)) -@inline A_mul_Bc(rowvec1::RowVector, rowvec2::RowVector) = rowvec1 * ctranspose(rowvec2) +@inline A_mul_Bc(rowvec::RowVector, mat::AbstractMatrix) = adjoint(mat * adjoint(rowvec)) +@inline A_mul_Bc(rowvec1::RowVector, rowvec2::RowVector) = rowvec1 * adjoint(rowvec2) A_mul_Bc(vec::AbstractVector, rowvec::RowVector) = throw(DimensionMismatch("Cannot multiply two vectors")) -@inline A_mul_Bc(vec1::AbstractVector, vec2::AbstractVector) = vec1 * ctranspose(vec2) -@inline A_mul_Bc(mat::AbstractMatrix, rowvec::RowVector) = mat * ctranspose(rowvec) +@inline A_mul_Bc(vec1::AbstractVector, vec2::AbstractVector) = vec1 * adjoint(vec2) +@inline A_mul_Bc(mat::AbstractMatrix, rowvec::RowVector) = mat * adjoint(rowvec) -@inline Ac_mul_Bc(rowvec::RowVector, vec::AbstractVector) = ctranspose(rowvec) * ctranspose(vec) -@inline Ac_mul_Bc(vec::AbstractVector, mat::AbstractMatrix) = ctranspose(mat * vec) +@inline Ac_mul_Bc(rowvec::RowVector, vec::AbstractVector) = adjoint(rowvec) * adjoint(vec) +@inline Ac_mul_Bc(vec::AbstractVector, mat::AbstractMatrix) = adjoint(mat * vec) Ac_mul_Bc(rowvec1::RowVector, rowvec2::RowVector) = throw(DimensionMismatch("Cannot multiply two vectors")) -@inline Ac_mul_Bc(vec::AbstractVector, rowvec::RowVector) = ctranspose(vec)*ctranspose(rowvec) +@inline Ac_mul_Bc(vec::AbstractVector, rowvec::RowVector) = adjoint(vec)*adjoint(rowvec) Ac_mul_Bc(vec::AbstractVector, rowvec::AbstractVector) = throw(DimensionMismatch("Cannot multiply two transposed vectors")) -@inline Ac_mul_Bc(mat::AbstractMatrix, rowvec::RowVector) = mat' * ctranspose(rowvec) +@inline Ac_mul_Bc(mat::AbstractMatrix, rowvec::RowVector) = mat' * adjoint(rowvec) Ac_mul_B(::RowVector, ::AbstractVector) = throw(DimensionMismatch("Cannot multiply two vectors")) -@inline Ac_mul_B(vec::AbstractVector, mat::AbstractMatrix) = ctranspose(Ac_mul_B(mat,vec)) -@inline Ac_mul_B(rowvec1::RowVector, rowvec2::RowVector) = ctranspose(rowvec1) * rowvec2 +@inline Ac_mul_B(vec::AbstractVector, mat::AbstractMatrix) = adjoint(Ac_mul_B(mat,vec)) +@inline Ac_mul_B(rowvec1::RowVector, rowvec2::RowVector) = adjoint(rowvec1) * rowvec2 Ac_mul_B(vec::AbstractVector, rowvec::RowVector) = throw(DimensionMismatch("Cannot multiply two transposed vectors")) -@inline Ac_mul_B(vec1::AbstractVector, vec2::AbstractVector) = ctranspose(vec1)*vec2 +@inline Ac_mul_B(vec1::AbstractVector, vec2::AbstractVector) = adjoint(vec1)*vec2 # Left Division # @@ -237,4 +237,4 @@ Ac_ldiv_B(mat::AbstractMatrix, rowvec::RowVector) = throw(DimensionMismatch("Can @inline /(rowvec::RowVector, mat::AbstractMatrix) = transpose(transpose(mat) \ transpose(rowvec)) @inline A_rdiv_Bt(rowvec::RowVector, mat::AbstractMatrix) = transpose(mat \ transpose(rowvec)) -@inline A_rdiv_Bc(rowvec::RowVector, mat::AbstractMatrix) = ctranspose(mat \ ctranspose(rowvec)) +@inline A_rdiv_Bc(rowvec::RowVector, mat::AbstractMatrix) = adjoint(mat \ adjoint(rowvec)) diff --git a/base/linalg/symmetric.jl b/base/linalg/symmetric.jl index 8d98709daaf7f..1a890da1a5007 100644 --- a/base/linalg/symmetric.jl +++ b/base/linalg/symmetric.jl @@ -177,7 +177,7 @@ function copy!(dest::Hermitian, src::Hermitian) if src.uplo == dest.uplo copy!(dest.data, src.data) else - ctranspose!(dest.data, src.data) + adjoint!(dest.data, src.data) end return dest end @@ -212,16 +212,16 @@ issymmetric(A::Hermitian{<:Complex}) = isreal(A) issymmetric(A::Symmetric) = true transpose(A::Symmetric) = A transpose(A::Hermitian{<:Real}) = A -ctranspose(A::Symmetric{<:Real}) = A -function ctranspose(A::Symmetric) - AC = ctranspose(A.data) +adjoint(A::Symmetric{<:Real}) = A +function adjoint(A::Symmetric) + AC = adjoint(A.data) return Symmetric(AC, ifelse(A.uplo == 'U', :L, :U)) end function transpose(A::Hermitian) AT = transpose(A.data) return Hermitian(AT, ifelse(A.uplo == 'U', :L, :U)) end -ctranspose(A::Hermitian) = A +adjoint(A::Hermitian) = A trace(A::Hermitian) = real(trace(A.data)) Base.conj(A::HermOrSym) = typeof(A)(conj(A.data), A.uplo) @@ -307,7 +307,7 @@ A_mul_B!(C::StridedMatrix{T}, A::StridedMatrix{T}, B::Hermitian{T,<:StridedMatri At_mul_B(A::RealHermSymComplexSym, B::AbstractVector) = A*B At_mul_B(A::RealHermSymComplexSym, B::AbstractMatrix) = A*B A_mul_Bt(A::AbstractMatrix, B::RealHermSymComplexSym) = A*B -## Hermitian{<:Number} and Symmetric{<:Real} are invariant to ctranspose; peel off the c +## Hermitian{<:Number} and Symmetric{<:Real} are invariant to adjoint; peel off the c Ac_mul_B(A::RealHermSymComplexHerm, B::AbstractVector) = A*B Ac_mul_B(A::RealHermSymComplexHerm, B::AbstractMatrix) = A*B A_mul_Bc(A::AbstractMatrix, B::RealHermSymComplexHerm) = A*B diff --git a/base/linalg/transpose.jl b/base/linalg/transpose.jl index c19730f9f58fc..0baf0f9393f6e 100644 --- a/base/linalg/transpose.jl +++ b/base/linalg/transpose.jl @@ -1,6 +1,6 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -ctranspose(a::AbstractArray) = error("ctranspose not defined for $(typeof(a)). Consider using `permutedims` for higher-dimensional arrays.") +adjoint(a::AbstractArray) = error("adjoint not defined for $(typeof(a)). Consider using `permutedims` for higher-dimensional arrays.") transpose(a::AbstractArray) = error("transpose not defined for $(typeof(a)). Consider using `permutedims` for higher-dimensional arrays.") ## Matrix transposition ## @@ -16,14 +16,14 @@ regions. transpose!(B::AbstractMatrix, A::AbstractMatrix) = transpose_f!(transpose, B, A) """ - ctranspose!(dest,src) + adjoint!(dest,src) Conjugate transpose array `src` and store the result in the preallocated array `dest`, which should have a size corresponding to `(size(src,2),size(src,1))`. No in-place transposition is supported and unexpected results will happen if `src` and `dest` have overlapping memory regions. """ -ctranspose!(B::AbstractMatrix, A::AbstractMatrix) = transpose_f!(ctranspose, B, A) +adjoint!(B::AbstractMatrix, A::AbstractMatrix) = transpose_f!(adjoint, B, A) function transpose!(B::AbstractVector, A::AbstractMatrix) indices(B,1) == indices(A,2) && indices(A,1) == 1:1 || throw(DimensionMismatch("transpose")) copy!(B, A) @@ -32,11 +32,11 @@ function transpose!(B::AbstractMatrix, A::AbstractVector) indices(B,2) == indices(A,1) && indices(B,1) == 1:1 || throw(DimensionMismatch("transpose")) copy!(B, A) end -function ctranspose!(B::AbstractVector, A::AbstractMatrix) +function adjoint!(B::AbstractVector, A::AbstractMatrix) indices(B,1) == indices(A,2) && indices(A,1) == 1:1 || throw(DimensionMismatch("transpose")) ccopy!(B, A) end -function ctranspose!(B::AbstractMatrix, A::AbstractVector) +function adjoint!(B::AbstractMatrix, A::AbstractVector) indices(B,2) == indices(A,1) && indices(B,1) == 1:1 || throw(DimensionMismatch("transpose")) ccopy!(B, A) end @@ -85,11 +85,11 @@ function ccopy!(B, A) RB, RA = eachindex(B), eachindex(A) if RB == RA for i = RB - B[i] = ctranspose(A[i]) + B[i] = adjoint(A[i]) end else for (i,j) = zip(RB, RA) - B[i] = ctranspose(A[j]) + B[i] = adjoint(A[j]) end end end @@ -119,14 +119,14 @@ function transpose(A::AbstractMatrix) B = similar(A, (ind2, ind1)) transpose!(B, A) end -function ctranspose(A::AbstractMatrix) +function adjoint(A::AbstractMatrix) ind1, ind2 = indices(A) B = similar(A, (ind2, ind1)) - ctranspose!(B, A) + adjoint!(B, A) end -@inline ctranspose(A::AbstractVector{<:Real}) = transpose(A) -@inline ctranspose(A::AbstractMatrix{<:Real}) = transpose(A) +@inline adjoint(A::AbstractVector{<:Real}) = transpose(A) +@inline adjoint(A::AbstractMatrix{<:Real}) = transpose(A) function copy_transpose!(B::AbstractVecOrMat, ir_dest::Range{Int}, jr_dest::Range{Int}, A::AbstractVecOrMat, ir_src::Range{Int}, jr_src::Range{Int}) diff --git a/base/linalg/triangular.jl b/base/linalg/triangular.jl index 79ba4820a7fb1..12aaa748770f2 100644 --- a/base/linalg/triangular.jl +++ b/base/linalg/triangular.jl @@ -329,19 +329,19 @@ transpose(A::LowerTriangular) = UpperTriangular(transpose(A.data)) transpose(A::UnitLowerTriangular) = UnitUpperTriangular(transpose(A.data)) transpose(A::UpperTriangular) = LowerTriangular(transpose(A.data)) transpose(A::UnitUpperTriangular) = UnitLowerTriangular(transpose(A.data)) -ctranspose(A::LowerTriangular) = UpperTriangular(ctranspose(A.data)) -ctranspose(A::UnitLowerTriangular) = UnitUpperTriangular(ctranspose(A.data)) -ctranspose(A::UpperTriangular) = LowerTriangular(ctranspose(A.data)) -ctranspose(A::UnitUpperTriangular) = UnitLowerTriangular(ctranspose(A.data)) +adjoint(A::LowerTriangular) = UpperTriangular(adjoint(A.data)) +adjoint(A::UnitLowerTriangular) = UnitUpperTriangular(adjoint(A.data)) +adjoint(A::UpperTriangular) = LowerTriangular(adjoint(A.data)) +adjoint(A::UnitUpperTriangular) = UnitLowerTriangular(adjoint(A.data)) transpose!(A::LowerTriangular) = UpperTriangular(copytri!(A.data, 'L')) transpose!(A::UnitLowerTriangular) = UnitUpperTriangular(copytri!(A.data, 'L')) transpose!(A::UpperTriangular) = LowerTriangular(copytri!(A.data, 'U')) transpose!(A::UnitUpperTriangular) = UnitLowerTriangular(copytri!(A.data, 'U')) -ctranspose!(A::LowerTriangular) = UpperTriangular(copytri!(A.data, 'L' , true)) -ctranspose!(A::UnitLowerTriangular) = UnitUpperTriangular(copytri!(A.data, 'L' , true)) -ctranspose!(A::UpperTriangular) = LowerTriangular(copytri!(A.data, 'U' , true)) -ctranspose!(A::UnitUpperTriangular) = UnitLowerTriangular(copytri!(A.data, 'U' , true)) +adjoint!(A::LowerTriangular) = UpperTriangular(copytri!(A.data, 'L' , true)) +adjoint!(A::UnitLowerTriangular) = UnitUpperTriangular(copytri!(A.data, 'L' , true)) +adjoint!(A::UpperTriangular) = LowerTriangular(copytri!(A.data, 'U' , true)) +adjoint!(A::UnitUpperTriangular) = UnitLowerTriangular(copytri!(A.data, 'U' , true)) diag(A::LowerTriangular) = diag(A.data) diag(A::UnitLowerTriangular) = ones(eltype(A), size(A,1)) @@ -445,8 +445,8 @@ A_mul_B!(C::AbstractVector, A::AbstractTriangular, B::AbstractVector) = A_mul_B! A_mul_B!(C::AbstractMatrix, A::AbstractTriangular, B::AbstractVecOrMat) = A_mul_B!(A, copy!(C, B)) A_mul_B!(C::AbstractVecOrMat, A::AbstractTriangular, B::AbstractVecOrMat) = A_mul_B!(A, copy!(C, B)) A_mul_Bt!(C::AbstractVecOrMat, A::AbstractTriangular, B::AbstractVecOrMat) = A_mul_B!(A, transpose!(C, B)) -A_mul_Bc!(C::AbstractMatrix, A::AbstractTriangular, B::AbstractVecOrMat) = A_mul_B!(A, ctranspose!(C, B)) -A_mul_Bc!(C::AbstractVecOrMat, A::AbstractTriangular, B::AbstractVecOrMat) = A_mul_B!(A, ctranspose!(C, B)) +A_mul_Bc!(C::AbstractMatrix, A::AbstractTriangular, B::AbstractVecOrMat) = A_mul_B!(A, adjoint!(C, B)) +A_mul_Bc!(C::AbstractVecOrMat, A::AbstractTriangular, B::AbstractVecOrMat) = A_mul_B!(A, adjoint!(C, B)) for (t, uploc, isunitc) in ((:LowerTriangular, 'L', 'N'), (:UnitLowerTriangular, 'L', 'U'), @@ -1667,9 +1667,9 @@ end # below might compute an unnecessary copy. Eliminating the copy requires adding # all the promotion logic here once again. Since these methods are probably relatively # rare, we chose not to bother for now. -Ac_mul_B(A::AbstractMatrix, B::AbstractTriangular) = (*)(ctranspose(A), B) +Ac_mul_B(A::AbstractMatrix, B::AbstractTriangular) = (*)(adjoint(A), B) At_mul_B(A::AbstractMatrix, B::AbstractTriangular) = (*)(transpose(A), B) -A_mul_Bc(A::AbstractTriangular, B::AbstractMatrix) = (*)(A, ctranspose(B)) +A_mul_Bc(A::AbstractTriangular, B::AbstractMatrix) = (*)(A, adjoint(B)) A_mul_Bt(A::AbstractTriangular, B::AbstractMatrix) = (*)(A, transpose(B)) Ac_mul_Bc(A::AbstractTriangular, B::AbstractTriangular) = Ac_mul_B(A, B') Ac_mul_Bc(A::AbstractTriangular, B::AbstractMatrix) = Ac_mul_B(A, B') @@ -1683,9 +1683,9 @@ At_mul_Bt(A::AbstractMatrix, B::AbstractTriangular) = A_mul_Bt(A.', B) @inline A_mul_Bt(rowvec::RowVector, A::AbstractTriangular) = transpose(A * transpose(rowvec)) @inline A_mul_Bt(A::AbstractTriangular, rowvec::RowVector) = A * transpose(rowvec) @inline At_mul_Bt(A::AbstractTriangular, rowvec::RowVector) = A.' * transpose(rowvec) -@inline A_mul_Bc(rowvec::RowVector, A::AbstractTriangular) = ctranspose(A * ctranspose(rowvec)) -@inline A_mul_Bc(A::AbstractTriangular, rowvec::RowVector) = A * ctranspose(rowvec) -@inline Ac_mul_Bc(A::AbstractTriangular, rowvec::RowVector) = A' * ctranspose(rowvec) +@inline A_mul_Bc(rowvec::RowVector, A::AbstractTriangular) = adjoint(A * adjoint(rowvec)) +@inline A_mul_Bc(A::AbstractTriangular, rowvec::RowVector) = A * adjoint(rowvec) +@inline Ac_mul_Bc(A::AbstractTriangular, rowvec::RowVector) = A' * adjoint(rowvec) @inline /(rowvec::RowVector, A::Union{UpperTriangular,LowerTriangular}) = transpose(transpose(A) \ transpose(rowvec)) @inline /(rowvec::RowVector, A::Union{UnitUpperTriangular,UnitLowerTriangular}) = transpose(transpose(A) \ transpose(rowvec)) @@ -1693,8 +1693,8 @@ At_mul_Bt(A::AbstractMatrix, B::AbstractTriangular) = A_mul_Bt(A.', B) @inline A_rdiv_Bt(rowvec::RowVector, A::Union{UpperTriangular,LowerTriangular}) = transpose(A \ transpose(rowvec)) @inline A_rdiv_Bt(rowvec::RowVector, A::Union{UnitUpperTriangular,UnitLowerTriangular}) = transpose(A \ transpose(rowvec)) -@inline A_rdiv_Bc(rowvec::RowVector, A::Union{UpperTriangular,LowerTriangular}) = ctranspose(A \ ctranspose(rowvec)) -@inline A_rdiv_Bc(rowvec::RowVector, A::Union{UnitUpperTriangular,UnitLowerTriangular}) = ctranspose(A \ ctranspose(rowvec)) +@inline A_rdiv_Bc(rowvec::RowVector, A::Union{UpperTriangular,LowerTriangular}) = adjoint(A \ adjoint(rowvec)) +@inline A_rdiv_Bc(rowvec::RowVector, A::Union{UnitUpperTriangular,UnitLowerTriangular}) = adjoint(A \ adjoint(rowvec)) \(::Union{UpperTriangular,LowerTriangular}, ::RowVector) = throw(DimensionMismatch("Cannot left-divide matrix by transposed vector")) \(::Union{UnitUpperTriangular,UnitLowerTriangular}, ::RowVector) = throw(DimensionMismatch("Cannot left-divide matrix by transposed vector")) diff --git a/base/linalg/tridiag.jl b/base/linalg/tridiag.jl index d3ae5e1eb9bcf..299bd571b6dd7 100644 --- a/base/linalg/tridiag.jl +++ b/base/linalg/tridiag.jl @@ -125,7 +125,7 @@ broadcast(::typeof(floor), ::Type{T}, M::SymTridiagonal) where {T<:Integer} = Sy broadcast(::typeof(ceil), ::Type{T}, M::SymTridiagonal) where {T<:Integer} = SymTridiagonal(ceil.(T, M.dv), ceil.(T, M.ev)) transpose(M::SymTridiagonal) = M #Identity operation -ctranspose(M::SymTridiagonal) = conj(M) +adjoint(M::SymTridiagonal) = conj(M) function diag(M::SymTridiagonal{T}, n::Integer=0) where T absn = abs(n) @@ -524,7 +524,7 @@ broadcast(::typeof(ceil), ::Type{T}, M::Tridiagonal) where {T<:Integer} = Tridiagonal(ceil.(T, M.dl), ceil.(T, M.d), ceil.(T, M.du)) transpose(M::Tridiagonal) = Tridiagonal(M.du, M.d, M.dl) -ctranspose(M::Tridiagonal) = conj(transpose(M)) +adjoint(M::Tridiagonal) = conj(transpose(M)) function diag(M::Tridiagonal{T}, n::Integer=0) where T if n == 0 diff --git a/base/linalg/uniformscaling.jl b/base/linalg/uniformscaling.jl index b0feaac7d74f0..c1e2b86da06de 100644 --- a/base/linalg/uniformscaling.jl +++ b/base/linalg/uniformscaling.jl @@ -1,6 +1,6 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -import Base: copy, ctranspose, getindex, show, transpose, one, zero, inv, +import Base: copy, adjoint, getindex, show, transpose, one, zero, inv, hcat, vcat, hvcat import Base.LinAlg: SingularException @@ -63,7 +63,7 @@ end copy(J::UniformScaling) = UniformScaling(J.λ) transpose(J::UniformScaling) = J -ctranspose(J::UniformScaling) = UniformScaling(conj(J.λ)) +adjoint(J::UniformScaling) = UniformScaling(conj(J.λ)) one(::Type{UniformScaling{T}}) where {T} = UniformScaling(one(T)) one(J::UniformScaling{T}) where {T} = one(UniformScaling{T}) diff --git a/base/number.jl b/base/number.jl index 1a95f2dee6a77..2732e98b825ba 100644 --- a/base/number.jl +++ b/base/number.jl @@ -165,7 +165,7 @@ copysign(x::Real, y::Real) = ifelse(signbit(x)!=signbit(y), -x, +x) conj(x::Real) = x transpose(x::Number) = x -ctranspose(x::Number) = conj(x) +adjoint(x::Number) = conj(x) angle(z::Real) = atan2(zero(z), z) """ diff --git a/base/operators.jl b/base/operators.jl index 08fa05c50705c..138e6b7c2dcf1 100644 --- a/base/operators.jl +++ b/base/operators.jl @@ -740,7 +740,7 @@ fldmod1(x::T, y::T) where {T<:Integer} = (fld1(x,y), mod1(x,y)) # transpose """ - ctranspose(A) + adjoint(A) The conjugate transposition operator (`'`). @@ -751,13 +751,13 @@ julia> A = [3+2im 9+2im; 8+7im 4+6im] 3+2im 9+2im 8+7im 4+6im -julia> ctranspose(A) +julia> adjoint(A) 2×2 Array{Complex{Int64},2}: 3-2im 8-7im 9-2im 4-6im ``` """ -ctranspose(x) = conj(transpose(x)) +adjoint(x) = conj(transpose(x)) conj(x) = x # transposed multiply @@ -767,21 +767,21 @@ conj(x) = x For matrices or vectors ``A`` and ``B``, calculates ``Aᴴ⋅B``. """ -Ac_mul_B(a,b) = ctranspose(a)*b +Ac_mul_B(a,b) = adjoint(a)*b """ A_mul_Bc(A, B) For matrices or vectors ``A`` and ``B``, calculates ``A⋅Bᴴ``. """ -A_mul_Bc(a,b) = a*ctranspose(b) +A_mul_Bc(a,b) = a*adjoint(b) """ Ac_mul_Bc(A, B) For matrices or vectors ``A`` and ``B``, calculates ``Aᴴ Bᴴ``. """ -Ac_mul_Bc(a,b) = ctranspose(a)*ctranspose(b) +Ac_mul_Bc(a,b) = adjoint(a)*adjoint(b) """ At_mul_B(A, B) @@ -811,21 +811,21 @@ At_mul_Bt(a,b) = transpose(a)*transpose(b) For matrices or vectors ``A`` and ``B``, calculates ``Aᴴ / B``. """ -Ac_rdiv_B(a,b) = ctranspose(a)/b +Ac_rdiv_B(a,b) = adjoint(a)/b """ A_rdiv_Bc(A, B) For matrices or vectors ``A`` and ``B``, calculates ``A / Bᴴ``. """ -A_rdiv_Bc(a,b) = a/ctranspose(b) +A_rdiv_Bc(a,b) = a/adjoint(b) """ Ac_rdiv_Bc(A, B) For matrices or vectors ``A`` and ``B``, calculates ``Aᴴ / Bᴴ``. """ -Ac_rdiv_Bc(a,b) = ctranspose(a)/ctranspose(b) +Ac_rdiv_Bc(a,b) = adjoint(a)/adjoint(b) """ At_rdiv_B(A, B) @@ -853,21 +853,21 @@ At_rdiv_Bt(a,b) = transpose(a)/transpose(b) For matrices or vectors ``A`` and ``B``, calculates ``Aᴴ`` \\ ``B``. """ -Ac_ldiv_B(a,b) = ctranspose(a)\b +Ac_ldiv_B(a,b) = adjoint(a)\b """ A_ldiv_Bc(A, B) For matrices or vectors ``A`` and ``B``, calculates ``A`` \\ ``Bᴴ``. """ -A_ldiv_Bc(a,b) = a\ctranspose(b) +A_ldiv_Bc(a,b) = a\adjoint(b) """ Ac_ldiv_Bc(A, B) For matrices or vectors ``A`` and ``B``, calculates ``Aᴴ`` \\ ``Bᴴ``. """ -Ac_ldiv_Bc(a,b) = ctranspose(a)\ctranspose(b) +Ac_ldiv_Bc(a,b) = adjoint(a)\adjoint(b) """ At_ldiv_B(A, B) diff --git a/base/sparse/cholmod.jl b/base/sparse/cholmod.jl index 5351a0717c041..2644bd7b5cd41 100644 --- a/base/sparse/cholmod.jl +++ b/base/sparse/cholmod.jl @@ -3,7 +3,7 @@ module CHOLMOD import Base: (*), convert, copy, eltype, getindex, show, size, - IndexStyle, IndexLinear, IndexCartesian, ctranspose + IndexStyle, IndexLinear, IndexCartesian, adjoint import Base.LinAlg: (\), A_mul_Bc, A_mul_Bt, Ac_ldiv_B, Ac_mul_B, At_ldiv_B, At_mul_B, cholfact, cholfact!, det, diag, ishermitian, isposdef, @@ -1209,15 +1209,15 @@ IndexStyle(::Dense) = IndexLinear() size(FC::FactorComponent, i::Integer) = size(FC.F, i) size(FC::FactorComponent) = size(FC.F) -ctranspose(FC::FactorComponent{Tv,:L}) where {Tv} = FactorComponent{Tv,:U}(FC.F) -ctranspose(FC::FactorComponent{Tv,:U}) where {Tv} = FactorComponent{Tv,:L}(FC.F) -ctranspose(FC::FactorComponent{Tv,:PtL}) where {Tv} = FactorComponent{Tv,:UP}(FC.F) -ctranspose(FC::FactorComponent{Tv,:UP}) where {Tv} = FactorComponent{Tv,:PtL}(FC.F) -ctranspose(FC::FactorComponent{Tv,:D}) where {Tv} = FC -ctranspose(FC::FactorComponent{Tv,:LD}) where {Tv} = FactorComponent{Tv,:DU}(FC.F) -ctranspose(FC::FactorComponent{Tv,:DU}) where {Tv} = FactorComponent{Tv,:LD}(FC.F) -ctranspose(FC::FactorComponent{Tv,:PtLD}) where {Tv} = FactorComponent{Tv,:DUP}(FC.F) -ctranspose(FC::FactorComponent{Tv,:DUP}) where {Tv} = FactorComponent{Tv,:PtLD}(FC.F) +adjoint(FC::FactorComponent{Tv,:L}) where {Tv} = FactorComponent{Tv,:U}(FC.F) +adjoint(FC::FactorComponent{Tv,:U}) where {Tv} = FactorComponent{Tv,:L}(FC.F) +adjoint(FC::FactorComponent{Tv,:PtL}) where {Tv} = FactorComponent{Tv,:UP}(FC.F) +adjoint(FC::FactorComponent{Tv,:UP}) where {Tv} = FactorComponent{Tv,:PtL}(FC.F) +adjoint(FC::FactorComponent{Tv,:D}) where {Tv} = FC +adjoint(FC::FactorComponent{Tv,:LD}) where {Tv} = FactorComponent{Tv,:DU}(FC.F) +adjoint(FC::FactorComponent{Tv,:DU}) where {Tv} = FactorComponent{Tv,:LD}(FC.F) +adjoint(FC::FactorComponent{Tv,:PtLD}) where {Tv} = FactorComponent{Tv,:DUP}(FC.F) +adjoint(FC::FactorComponent{Tv,:DUP}) where {Tv} = FactorComponent{Tv,:PtLD}(FC.F) function getindex(A::Dense, i::Integer) s = unsafe_load(pointer(A)) @@ -1651,8 +1651,8 @@ function (\)(L::FactorComponent, B::SparseVecOrMat) sparse(L\Sparse(B,0)) end -Ac_ldiv_B(L::FactorComponent, B) = ctranspose(L)\B -Ac_ldiv_B(L::FactorComponent, B::RowVector) = ctranspose(L)\B # ambiguity +Ac_ldiv_B(L::FactorComponent, B) = adjoint(L)\B +Ac_ldiv_B(L::FactorComponent, B::RowVector) = adjoint(L)\B # ambiguity (\)(L::Factor{T}, B::Dense{T}) where {T<:VTypes} = solve(CHOLMOD_A, L, B) # Explicit typevars are necessary to avoid ambiguities with defs in linalg/factorizations.jl diff --git a/base/sparse/linalg.jl b/base/sparse/linalg.jl index ad56b6ed057cc..5ae8cdda4129c 100644 --- a/base/sparse/linalg.jl +++ b/base/sparse/linalg.jl @@ -44,7 +44,7 @@ end # In matrix-vector multiplication, the correct orientation of the vector is assumed. for (f, op, transp) in ((:A_mul_B, :identity, false), - (:Ac_mul_B, :ctranspose, true), + (:Ac_mul_B, :adjoint, true), (:At_mul_B, :transpose, true)) @eval begin function $(Symbol(f,:!))(α::Number, A::SparseMatrixCSC, B::StridedVecOrMat, β::Number, C::StridedVecOrMat) @@ -124,11 +124,11 @@ end (*)(A::SparseMatrixCSC{Tv,Ti}, B::SparseMatrixCSC{Tv,Ti}) where {Tv,Ti} = spmatmul(A,B) for (f, opA, opB) in ((:A_mul_Bt, :identity, :transpose), - (:A_mul_Bc, :identity, :ctranspose), + (:A_mul_Bc, :identity, :adjoint), (:At_mul_B, :transpose, :identity), - (:Ac_mul_B, :ctranspose, :identity), + (:Ac_mul_B, :adjoint, :identity), (:At_mul_Bt, :transpose, :transpose), - (:Ac_mul_Bc, :ctranspose, :ctranspose)) + (:Ac_mul_Bc, :adjoint, :adjoint)) @eval begin function ($f)(A::SparseMatrixCSC{Tv,Ti}, B::SparseMatrixCSC{Tv,Ti}) where {Tv,Ti} spmatmul(($opA)(A), ($opB)(B)) diff --git a/base/sparse/sparse.jl b/base/sparse/sparse.jl index 28126d551c10b..81cb3f3a95a1a 100644 --- a/base/sparse/sparse.jl +++ b/base/sparse/sparse.jl @@ -14,12 +14,12 @@ import Base.LinAlg: At_ldiv_B!, Ac_ldiv_B!, A_rdiv_B!, A_rdiv_Bc! import Base: @get!, acos, acosd, acot, acotd, acsch, asech, asin, asind, asinh, atan, atand, atanh, broadcast!, chol, conj!, cos, cosc, cosd, cosh, cospi, cot, - cotd, coth, countnz, csc, cscd, csch, ctranspose!, diag, diff, done, dot, eig, + cotd, coth, countnz, csc, cscd, csch, adjoint!, diag, diff, done, dot, eig, exp10, exp2, eye, findn, floor, hash, indmin, inv, issymmetric, istril, istriu, log10, log2, lu, next, sec, secd, sech, show, sin, sinc, sind, sinh, sinpi, squeeze, start, sum, summary, tan, tand, tanh, trace, transpose!, tril!, triu!, trunc, vecnorm, abs, abs2, - broadcast, ceil, complex, cond, conj, convert, copy, copy!, ctranspose, diagm, + broadcast, ceil, complex, cond, conj, convert, copy, copy!, adjoint, diagm, exp, expm1, factorize, find, findmax, findmin, findnz, float, full, getindex, vcat, hcat, hvcat, cat, imag, indmax, ishermitian, kron, length, log, log1p, max, min, maximum, minimum, norm, one, promote_eltype, real, reinterpret, reshape, rot180, diff --git a/base/sparse/sparsematrix.jl b/base/sparse/sparsematrix.jl index 0c501c8c72bfa..4bd62a54a25b8 100644 --- a/base/sparse/sparsematrix.jl +++ b/base/sparse/sparsematrix.jl @@ -853,14 +853,14 @@ function ftranspose!(X::SparseMatrixCSC{Tv,Ti}, A::SparseMatrixCSC{Tv,Ti}, f::Fu halfperm!(X, A, 1:A.n, f) end transpose!(X::SparseMatrixCSC{Tv,Ti}, A::SparseMatrixCSC{Tv,Ti}) where {Tv,Ti} = ftranspose!(X, A, identity) -ctranspose!(X::SparseMatrixCSC{Tv,Ti}, A::SparseMatrixCSC{Tv,Ti}) where {Tv,Ti} = ftranspose!(X, A, conj) +adjoint!(X::SparseMatrixCSC{Tv,Ti}, A::SparseMatrixCSC{Tv,Ti}) where {Tv,Ti} = ftranspose!(X, A, conj) function ftranspose(A::SparseMatrixCSC{Tv,Ti}, f::Function) where {Tv,Ti} X = SparseMatrixCSC(A.n, A.m, Vector{Ti}(A.m+1), Vector{Ti}(nnz(A)), Vector{Tv}(nnz(A))) halfperm!(X, A, 1:A.n, f) end transpose(A::SparseMatrixCSC) = ftranspose(A, identity) -ctranspose(A::SparseMatrixCSC) = ftranspose(A, conj) +adjoint(A::SparseMatrixCSC) = ftranspose(A, conj) """ unchecked_noalias_permute!(X::SparseMatrixCSC{Tv,Ti}, diff --git a/base/sparse/sparsevector.jl b/base/sparse/sparsevector.jl index 80225e4d328c8..09bb230524f9d 100644 --- a/base/sparse/sparsevector.jl +++ b/base/sparse/sparsevector.jl @@ -1401,7 +1401,7 @@ vecnorm(x::SparseVectorUnion, p::Real=2) = vecnorm(nonzeros(x), p) # Transpose # (The only sparse matrix structure in base is CSC, so a one-row sparse matrix is worse than dense) @inline transpose(sv::SparseVector) = RowVector(sv) -@inline ctranspose(sv::SparseVector) = RowVector(conj(sv)) +@inline adjoint(sv::SparseVector) = RowVector(conj(sv)) ### BLAS Level-1 diff --git a/doc/src/manual/functions.md b/doc/src/manual/functions.md index 2f417d9466596..a5d28296daed5 100644 --- a/doc/src/manual/functions.md +++ b/doc/src/manual/functions.md @@ -163,7 +163,7 @@ A few special expressions correspond to calls to functions with non-obvious name | `[A B C ...]` | [`hcat()`](@ref) | | `[A; B; C; ...]` | [`vcat()`](@ref) | | `[A B; C D; ...]` | [`hvcat()`](@ref) | -| `A'` | [`ctranspose()`](@ref) | +| `A'` | [`adjoint()`](@ref) | | `A.'` | [`transpose()`](@ref) | | `1:n` | [`colon()`](@ref) | | `A[i]` | [`getindex()`](@ref) | diff --git a/doc/src/stdlib/linalg.md b/doc/src/stdlib/linalg.md index e9421cb216857..935ea52e50a04 100644 --- a/doc/src/stdlib/linalg.md +++ b/doc/src/stdlib/linalg.md @@ -109,8 +109,8 @@ Base.LinAlg.RowVector Base.LinAlg.ConjArray Base.transpose Base.transpose! -Base.ctranspose -Base.ctranspose! +Base.adjoint +Base.adjoint! Base.LinAlg.eigs(::Any) Base.LinAlg.eigs(::Any, ::Any) Base.LinAlg.svds diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 42cbb9fecf7d7..386c423d3bf65 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -2369,7 +2369,7 @@ ,.(apply append rows))) `(call (top typed_vcat) ,t ,@a))))) - '|'| (lambda (e) `(call ctranspose ,(expand-forms (cadr e)))) + '|'| (lambda (e) `(call adjoint ,(expand-forms (cadr e)))) '|.'| (lambda (e) `(call transpose ,(expand-forms (cadr e)))) 'generator diff --git a/test/arrayops.jl b/test/arrayops.jl index 9055e55ba49df..776e73fab5e9b 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -1685,12 +1685,12 @@ end d = ones(Complex,6) @test_throws DimensionMismatch transpose!(a,d) @test_throws DimensionMismatch transpose!(d,a) - @test_throws DimensionMismatch ctranspose!(a,d) - @test_throws DimensionMismatch ctranspose!(d,a) + @test_throws DimensionMismatch adjoint!(a,d) + @test_throws DimensionMismatch adjoint!(d,a) @test_throws DimensionMismatch transpose!(b,c) - @test_throws DimensionMismatch ctranspose!(b,c) + @test_throws DimensionMismatch adjoint!(b,c) @test_throws DimensionMismatch transpose!(c,b) - @test_throws DimensionMismatch ctranspose!(c,b) + @test_throws DimensionMismatch adjoint!(c,b) transpose!(b,a) @test b == ones(Complex,5) b = ones(Complex,5) @@ -1698,10 +1698,10 @@ end transpose!(a,b) @test a == ones(Complex,1,5) b = zeros(Complex,5) - ctranspose!(b,a) + adjoint!(b,a) @test b == ones(Complex,5) a = zeros(Complex,1,5) - ctranspose!(a,b) + adjoint!(a,b) @test a == ones(Complex,1,5) end diff --git a/test/distributed_exec.jl b/test/distributed_exec.jl index 0b0893d76005f..65de2c21a4a9d 100644 --- a/test/distributed_exec.jl +++ b/test/distributed_exec.jl @@ -469,7 +469,7 @@ d[5,1:2:4,8] = 19 AA = rand(4,2) A = @inferred(convert(SharedArray, AA)) B = @inferred(convert(SharedArray, AA')) -@test B*A == ctranspose(AA)*AA +@test B*A == adjoint(AA)*AA d=SharedArray{Int64,2}((10,10); init = D->fill!(D.loc_subarr_1d, myid()), pids=[id_me, id_other]) d2 = map(x->1, d) diff --git a/test/linalg/bidiag.jl b/test/linalg/bidiag.jl index 7c9d85b43e5cc..2ecd02da6b572 100644 --- a/test/linalg/bidiag.jl +++ b/test/linalg/bidiag.jl @@ -105,7 +105,7 @@ srand(1) @test Array(imag(T)) == imag(diagm(dv)) + imag(diagm(ev, uplo == :U ? 1 : -1)) end - @testset for func in (conj, transpose, ctranspose) + @testset for func in (conj, transpose, adjoint) @test func(func(T)) == T end diff --git a/test/linalg/diagonal.jl b/test/linalg/diagonal.jl index b1e8df0bd3ffb..d9d5805b8915c 100644 --- a/test/linalg/diagonal.jl +++ b/test/linalg/diagonal.jl @@ -100,7 +100,7 @@ srand(1) @test A_ldiv_B!(D, copy(U)) ≈ DM\U atol=atol_three @test At_ldiv_B!(D, copy(U)) ≈ DM\U atol=atol_three @test Ac_ldiv_B!(conj(D), copy(U)) ≈ DM\U atol=atol_three - Uc = ctranspose(U) + Uc = adjoint(U) target = scale!(Uc, inv.(D.diag)) @test A_rdiv_B!(Uc, D) ≈ target atol=atol_three @test_throws DimensionMismatch A_rdiv_B!(eye(elty, n-1), D) @@ -222,7 +222,7 @@ srand(1) @test transpose(D) == D if elty <: BlasComplex @test Array(conj(D)) ≈ conj(DM) - @test ctranspose(D) == conj(D) + @test adjoint(D) == conj(D) end # Translates to Ac/t_mul_B, which is specialized after issue 21286 @test(D' * v == conj(D) * v) diff --git a/test/linalg/generic.jl b/test/linalg/generic.jl index 4b92bf758c6fe..c6a0115b65697 100644 --- a/test/linalg/generic.jl +++ b/test/linalg/generic.jl @@ -371,6 +371,6 @@ Base.transpose(a::ModInt{n}) where {n} = a # see Issue 20978 end @testset "fallback throws properly for AbstractArrays with dimension > 2" begin - @test_throws ErrorException ctranspose(rand(2,2,2,2)) + @test_throws ErrorException adjoint(rand(2,2,2,2)) @test_throws ErrorException transpose(rand(2,2,2,2)) end diff --git a/test/linalg/givens.jl b/test/linalg/givens.jl index 49e0158491a1d..c13ddd2fcbb3b 100644 --- a/test/linalg/givens.jl +++ b/test/linalg/givens.jl @@ -22,8 +22,8 @@ using Base.Test @test A_mul_B!(G,eye(elty,10,10)) == [G[i,j] for i=1:10,j=1:10] @testset "transposes" begin - @test ctranspose(G)*G*eye(10) ≈ eye(elty, 10) - @test ctranspose(R)*(R*eye(10)) ≈ eye(elty, 10) + @test adjoint(G)*G*eye(10) ≈ eye(elty, 10) + @test adjoint(R)*(R*eye(10)) ≈ eye(elty, 10) @test_throws ErrorException transpose(G) @test_throws ErrorException transpose(R) end @@ -38,9 +38,9 @@ using Base.Test @test norm(R*eye(elty, 10)) ≈ one(elty) G, _ = givens(one(elty),zero(elty),9,10) - @test ctranspose(G*eye(elty,10))*(G*eye(elty,10)) ≈ eye(elty, 10) + @test adjoint(G*eye(elty,10))*(G*eye(elty,10)) ≈ eye(elty, 10) K, _ = givens(zero(elty),one(elty),9,10) - @test ctranspose(K*eye(elty,10))*(K*eye(elty,10)) ≈ eye(elty, 10) + @test adjoint(K*eye(elty,10))*(K*eye(elty,10)) ≈ eye(elty, 10) @testset "Givens * vectors" begin if isa(A, Array) diff --git a/test/linalg/qr.jl b/test/linalg/qr.jl index 282321086fd1a..25728ee0c8572 100644 --- a/test/linalg/qr.jl +++ b/test/linalg/qr.jl @@ -150,11 +150,11 @@ end @testset "transpose errors" begin @test_throws ErrorException transpose(qrfact(randn(3,3))) - @test_throws ErrorException ctranspose(qrfact(randn(3,3))) + @test_throws ErrorException adjoint(qrfact(randn(3,3))) @test_throws ErrorException transpose(qrfact(randn(3,3), Val(false))) - @test_throws ErrorException ctranspose(qrfact(randn(3,3), Val(false))) + @test_throws ErrorException adjoint(qrfact(randn(3,3), Val(false))) @test_throws ErrorException transpose(qrfact(big.(randn(3,3)))) - @test_throws ErrorException ctranspose(qrfact(big.(randn(3,3)))) + @test_throws ErrorException adjoint(qrfact(big.(randn(3,3)))) end @testset "Issue 7304" begin diff --git a/test/linalg/symmetric.jl b/test/linalg/symmetric.jl index 54cfb2753af27..296d599390d89 100644 --- a/test/linalg/symmetric.jl +++ b/test/linalg/symmetric.jl @@ -131,16 +131,16 @@ end end end - @testset "transpose, ctranspose" begin + @testset "transpose, adjoint" begin S = Symmetric(asym) H = Hermitian(aherm) @test transpose(S) === S == asym - @test ctranspose(H) === H == aherm + @test adjoint(H) === H == aherm if eltya <: Real - @test ctranspose(S) === S == asym + @test adjoint(S) === S == asym @test transpose(H) === H == aherm else - @test ctranspose(S) == Symmetric(conj(asym)) + @test adjoint(S) == Symmetric(conj(asym)) @test transpose(H) == Hermitian(transpose(aherm)) end end diff --git a/test/linalg/triangular.jl b/test/linalg/triangular.jl index f6dbe41552cc0..203d0640ca1a2 100644 --- a/test/linalg/triangular.jl +++ b/test/linalg/triangular.jl @@ -22,7 +22,7 @@ for elty1 in (Float32, Float64, BigFloat, Complex64, Complex128, Complex{BigFloa (UnitLowerTriangular, :L)) # Construct test matrix - A1 = t1(elty1 == Int ? rand(1:7, n, n) : convert(Matrix{elty1}, (elty1 <: Complex ? complex.(randn(n, n), randn(n, n)) : randn(n, n)) |> t -> chol(t't) |> t -> uplo1 == :U ? t : ctranspose(t))) + A1 = t1(elty1 == Int ? rand(1:7, n, n) : convert(Matrix{elty1}, (elty1 <: Complex ? complex.(randn(n, n), randn(n, n)) : randn(n, n)) |> t -> chol(t't) |> t -> uplo1 == :U ? t : adjoint(t))) debug && println("elty1: $elty1, A1: $t1") @@ -132,15 +132,15 @@ for elty1 in (Float32, Float64, BigFloat, Complex64, Complex128, Complex{BigFloa # transpose @test full(A1.') == full(A1).' @test full(viewA1.') == full(viewA1).' - # ctranspose + # adjoint @test full(A1') == full(A1)' @test full(viewA1') == full(viewA1)' # transpose! @test transpose!(copy(A1)) == A1.' @test transpose!(t1(view(copy(A1).data, vrange, vrange))) == viewA1.' - # ctranspose! - @test ctranspose!(copy(A1)) == A1' - @test ctranspose!(t1(view(copy(A1).data, vrange, vrange))) == viewA1' + # adjoint! + @test adjoint!(copy(A1)) == A1' + @test adjoint!(t1(view(copy(A1).data, vrange, vrange))) == viewA1' end # diag @@ -274,7 +274,7 @@ for elty1 in (Float32, Float64, BigFloat, Complex64, Complex128, Complex{BigFloa debug && println("elty1: $elty1, A1: $t1, elty2: $elty2") - A2 = t2(elty2 == Int ? rand(1:7, n, n) : convert(Matrix{elty2}, (elty2 <: Complex ? complex.(randn(n, n), randn(n, n)) : randn(n, n)) |> t -> chol(t't) |> t -> uplo2 == :U ? t : ctranspose(t))) + A2 = t2(elty2 == Int ? rand(1:7, n, n) : convert(Matrix{elty2}, (elty2 <: Complex ? complex.(randn(n, n), randn(n, n)) : randn(n, n)) |> t -> chol(t't) |> t -> uplo2 == :U ? t : adjoint(t))) # Convert if elty1 <: Real && !(elty2 <: Integer) diff --git a/test/linalg/tridiag.jl b/test/linalg/tridiag.jl index 2e231a7ec337a..ba52628695c50 100644 --- a/test/linalg/tridiag.jl +++ b/test/linalg/tridiag.jl @@ -65,7 +65,7 @@ B = randn(n,2) @testset "elementary operations" begin @test conj(T) == Tridiagonal(conj(dl), conj(d), conj(du)) @test transpose(T) == Tridiagonal(du, d, dl) - @test ctranspose(T) == Tridiagonal(conj(du), conj(d), conj(dl)) + @test adjoint(T) == Tridiagonal(conj(du), conj(d), conj(dl)) @test abs.(T) == Tridiagonal(abs.(dl),abs.(d),abs.(du)) if elty <: Real @@ -265,7 +265,7 @@ let n = 12 #Size of matrix problem to test @test_throws ArgumentError diag(A,n+1) end @testset "Idempotent tests" begin - for func in (conj, transpose, ctranspose) + for func in (conj, transpose, adjoint) @test func(func(A)) == A end end diff --git a/test/operators.jl b/test/operators.jl index 72effdbf33f51..e661968bf8e43 100644 --- a/test/operators.jl +++ b/test/operators.jl @@ -49,8 +49,6 @@ p = 1=>:foo @test xor(2) == 2 @test (⊻)(2) == 2 -# @test ctranspose('a') == 'a' # (c)transpose of Chars no longer supported - @test_throws ArgumentError Base.scalarmin(['a','b'],['c','d']) @test_throws ArgumentError Base.scalarmin('a',['c','d']) @test_throws ArgumentError Base.scalarmin(['a','b'],'c') diff --git a/test/sparse/sparse.jl b/test/sparse/sparse.jl index 38e25d0a3a5a8..8e43393ad1e61 100644 --- a/test/sparse/sparse.jl +++ b/test/sparse/sparse.jl @@ -401,11 +401,11 @@ end fullAt = transpose(Array(A)) @test transpose(A) == fullAt @test transpose!(similar(At), A) == fullAt - # ctranspose[!] + # adjoint[!] C = A + im*A/2 - fullCh = ctranspose(Array(C)) - @test ctranspose(C) == fullCh - @test ctranspose!(similar(sparse(fullCh)), C) == fullCh + fullCh = adjoint(Array(C)) + @test adjoint(C) == fullCh + @test adjoint!(similar(sparse(fullCh)), C) == fullCh # permute[!] p = randperm(m) q = randperm(n) @@ -423,7 +423,7 @@ end @testset "transpose of SubArrays" begin A = view(sprandn(10, 10, 0.3), 1:4, 1:4) @test transpose(Array(A)) == Array(transpose(A)) - @test ctranspose(Array(A)) == Array(ctranspose(A)) + @test adjoint(Array(A)) == Array(adjoint(A)) end @testset "exp" begin diff --git a/test/sparse/sparsevector.jl b/test/sparse/sparsevector.jl index 4a61940f53677..4d849f2080b9c 100644 --- a/test/sparse/sparsevector.jl +++ b/test/sparse/sparsevector.jl @@ -296,7 +296,7 @@ let a = SparseVector(8, [2, 5, 6], Int32[12, 35, 72]) @test complex(acp) == acp @test isa(acp, SparseVector{Complex128,Int}) @test exact_equal(acp, SparseVector(8, [2, 5, 6], complex([12., 35., 72.]))) - @test sparsevec(ctranspose(ctranspose(acp))) == acp + @test sparsevec(adjoint(adjoint(acp))) == acp end let x1 = SparseVector(8, [2, 5, 6], [12.2, 1.4, 5.0]) From 52543dfc303fbb4de04cc67e38d2cbcca4fb03f7 Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Tue, 22 Aug 2017 05:01:26 +0200 Subject: [PATCH 102/324] Don't throw in cholfact if matrix is not Hermitian (#23315) * dont throw in cholfact if matrix is not hermitian * use info code -1 for non hermitian matrix --- base/linalg/cholesky.jl | 66 ++++++++++++++++++--------------------- base/linalg/exceptions.jl | 9 ++++++ test/linalg/cholesky.jl | 10 +++--- 3 files changed, 44 insertions(+), 41 deletions(-) diff --git a/base/linalg/cholesky.jl b/base/linalg/cholesky.jl index 8ada08741687d..f2aebadcbe735 100644 --- a/base/linalg/cholesky.jl +++ b/base/linalg/cholesky.jl @@ -52,6 +52,9 @@ function CholeskyPivoted(A::AbstractMatrix{T}, uplo::Char, piv::Vector{BlasInt}, CholeskyPivoted{T,typeof(A)}(A, uplo, piv, rank, tol, info) end +# make a copy that allow inplace Cholesky factorization +@inline choltype(A) = promote_type(typeof(chol(one(eltype(A)))), Float32) +@inline cholcopy(A) = copy_oftype(A, choltype(A)) # _chol!. Internal methods for calling unpivoted Cholesky ## BLAS/LAPACK element types @@ -63,6 +66,13 @@ function _chol!(A::StridedMatrix{<:BlasFloat}, ::Type{LowerTriangular}) C, info = LAPACK.potrf!('L', A) return LowerTriangular(C), info end +function _chol!(A::StridedMatrix) + if !ishermitian(A) # return with info = -1 if not Hermitian + return UpperTriangular(A), convert(BlasInt, -1) + else + return _chol!(A, UpperTriangular) + end +end ## Non BLAS/LAPACK element types (generic) function _chol!(A::AbstractMatrix, ::Type{UpperTriangular}) @@ -124,10 +134,6 @@ end chol!(x::Number, uplo) = ((C, info) = _chol!(x, uplo); @assertposdef C info) -non_hermitian_error(f) = throw(ArgumentError("matrix is not symmetric/" * - "Hermitian. This error can be avoided by calling $f(Hermitian(A)) " * - "which will ignore either the upper or lower triangle of the matrix.")) - # chol!. Destructive methods for computing Cholesky factor of real symmetric or Hermitian # matrix function chol!(A::RealHermSymComplexHerm{<:Real,<:StridedMatrix}) @@ -135,8 +141,7 @@ function chol!(A::RealHermSymComplexHerm{<:Real,<:StridedMatrix}) @assertposdef C info end function chol!(A::StridedMatrix) - ishermitian(A) || non_hermitian_error("chol!") - C, info = _chol!(A, UpperTriangular) + C, info = _chol!(A) @assertposdef C info end @@ -145,8 +150,7 @@ end # chol. Non-destructive methods for computing Cholesky factor of a real symmetric or # Hermitian matrix. Promotes elements to a type that is stable under square roots. function chol(A::RealHermSymComplexHerm) - T = promote_type(typeof(chol(one(eltype(A)))), Float32) - AA = similar(A, T, size(A)) + AA = similar(A, choltype(A), size(A)) if A.uplo == 'U' copy!(AA, A.data) else @@ -180,10 +184,7 @@ julia> U'U 2.0 50.0 ``` """ -function chol(A::AbstractMatrix) - ishermitian(A) || non_hermitian_error("chol") - return chol(Hermitian(A)) -end +chol(A::AbstractMatrix) = chol!(cholcopy(A)) ## Numbers """ @@ -235,8 +236,11 @@ ERROR: InexactError: convert(Int64, 6.782329983125268) ``` """ function cholfact!(A::StridedMatrix, ::Val{false}=Val(false)) - ishermitian(A) || non_hermitian_error("cholfact!") - return cholfact!(Hermitian(A), Val(false)) + if !ishermitian(A) # return with info = -1 if not Hermitian + return Cholesky(A, 'U', convert(BlasInt, -1)) + else + return cholfact!(Hermitian(A), Val(false)) + end end @@ -250,9 +254,8 @@ end ### Non BLAS/LAPACK element types (generic). Since generic fallback for pivoted Cholesky ### is not implemented yet we throw an error -cholfact!(A::RealHermSymComplexHerm{<:Real}, ::Val{true}; - tol = 0.0) = - throw(ArgumentError("generic pivoted Cholesky factorization is not implemented yet")) +cholfact!(A::RealHermSymComplexHerm{<:Real}, ::Val{true}; tol = 0.0) = + throw(ArgumentError("generic pivoted Cholesky factorization is not implemented yet")) ### for StridedMatrices, check that matrix is symmetric/Hermitian """ @@ -264,17 +267,17 @@ factorization produces a number not representable by the element type of `A`, e.g. for integer types. """ function cholfact!(A::StridedMatrix, ::Val{true}; tol = 0.0) - ishermitian(A) || non_hermitian_error("cholfact!") - return cholfact!(Hermitian(A), Val(true); tol = tol) + if !ishermitian(A) # return with info = -1 if not Hermitian + return CholeskyPivoted(A, 'U', Vector{BlasInt}(),convert(BlasInt, 1), + tol, convert(BlasInt, -1)) + else + return cholfact!(Hermitian(A), Val(true); tol = tol) + end end # cholfact. Non-destructive methods for computing Cholesky factorization of real symmetric # or Hermitian matrix ## No pivoting (default) -cholfact(A::RealHermSymComplexHerm{<:Real,<:StridedMatrix}, ::Val{false}=Val(false)) = - cholfact!(copy_oftype(A, promote_type(typeof(chol(one(eltype(A)))),Float32))) - -### for StridedMatrices, check that matrix is symmetric/Hermitian """ cholfact(A, Val(false)) -> Cholesky @@ -314,18 +317,11 @@ julia> C[:L] * C[:U] == A true ``` """ -function cholfact(A::StridedMatrix, ::Val{false}=Val(false)) - ishermitian(A) || non_hermitian_error("cholfact") - return cholfact(Hermitian(A)) -end +cholfact(A::Union{StridedMatrix,RealHermSymComplexHerm{<:Real,<:StridedMatrix}}, + ::Val{false}=Val(false)) = cholfact!(cholcopy(A)) ## With pivoting -cholfact(A::RealHermSymComplexHerm{<:Real,<:StridedMatrix}, ::Val{true}; tol = 0.0) = - cholfact!(copy_oftype(A, promote_type(typeof(chol(one(eltype(A)))),Float32)), - Val(true); tol = tol) - -### for StridedMatrices, check that matrix is symmetric/Hermitian """ cholfact(A, Val(true); tol = 0.0) -> CholeskyPivoted @@ -338,10 +334,8 @@ The following functions are available for `PivotedCholesky` objects: The argument `tol` determines the tolerance for determining the rank. For negative values, the tolerance is the machine precision. """ -function cholfact(A::StridedMatrix, ::Val{true}; tol = 0.0) - ishermitian(A) || non_hermitian_error("cholfact") - return cholfact(Hermitian(A), Val(true); tol = tol) -end +cholfact(A::Union{StridedMatrix,RealHermSymComplexHerm{<:Real,<:StridedMatrix}}, + ::Val{true}; tol = 0.0) = cholfact!(cholcopy(A), Val(true); tol = tol) ## Number function cholfact(x::Number, uplo::Symbol=:U) diff --git a/base/linalg/exceptions.jl b/base/linalg/exceptions.jl index 4b0a72e251950..9a8b358d3e864 100644 --- a/base/linalg/exceptions.jl +++ b/base/linalg/exceptions.jl @@ -32,6 +32,15 @@ end struct PosDefException <: Exception info::BlasInt end +function Base.showerror(io::IO, ex::PosDefException) + print(io, "PosDefException: matrix is not ") + if ex.info == -1 + print(io, "Hermitian") + else + print(io, "positive definite") + end + print(io, "; Cholesky factorization failed.") +end struct RankDeficientException <: Exception info::BlasInt diff --git a/test/linalg/cholesky.jl b/test/linalg/cholesky.jl index cda0c07e8e1e8..3bc0694bb5a78 100644 --- a/test/linalg/cholesky.jl +++ b/test/linalg/cholesky.jl @@ -261,14 +261,14 @@ end end end -@testset "throw if non-Hermitian" begin +@testset "handling of non-Hermitian" begin R = randn(5, 5) C = complex.(R, R) for A in (R, C) - @test_throws ArgumentError cholfact(A) - @test_throws ArgumentError cholfact!(copy(A)) - @test_throws ArgumentError chol(A) - @test_throws ArgumentError Base.LinAlg.chol!(copy(A)) + @test !LinAlg.issuccess(cholfact(A)) + @test !LinAlg.issuccess(cholfact!(copy(A))) + @test_throws PosDefException chol(A) + @test_throws PosDefException Base.LinAlg.chol!(copy(A)) end end From f1d355672a364b8f849703ffa06f9617655cb65c Mon Sep 17 00:00:00 2001 From: Martin Holters Date: Tue, 22 Aug 2017 05:22:35 +0200 Subject: [PATCH 103/324] RowVector, pinv, and quasi-division (#23067) * Make pinv(::AbstractVector) return a RowVector Also broaden from StridedVector to AbstractVector while at it and don't employ full matrix SVD. * Add pinv(::RowVector) * Add /(::Number, ::AbstractVector) Also start testing consistency between division and multiplication with pseudo-inverse involving vectors. * Add \(::RowVector, ::RowVector), returning a scalar * Fix \ for AbstractVector LHS Let \(::AbstractVector, ::AbstractMatrix) return a RowVector and \(::AbstractVector, ::AbstractVector) return a scalar. --- base/linalg/dense.jl | 1 - base/linalg/generic.jl | 16 +++++++++++++++- base/linalg/rowvector.jl | 4 ++++ test/linalg/dense.jl | 39 ++++++++++++++++++++++++++++++++++++++- test/linalg/pinv.jl | 16 ++++++++++++++++ 5 files changed, 73 insertions(+), 3 deletions(-) diff --git a/base/linalg/dense.jl b/base/linalg/dense.jl index bde22d2c2d58a..6eab8b46d145d 100644 --- a/base/linalg/dense.jl +++ b/base/linalg/dense.jl @@ -895,7 +895,6 @@ function pinv(A::StridedMatrix{T}) where T tol = eps(real(float(one(T))))*maximum(size(A)) return pinv(A, tol) end -pinv(a::StridedVector) = pinv(reshape(a, length(a), 1)) function pinv(x::Number) xi = inv(x) return ifelse(isfinite(xi), xi, zero(xi)) diff --git a/base/linalg/generic.jl b/base/linalg/generic.jl index b08354ca30ecd..4af5f66639381 100644 --- a/base/linalg/generic.jl +++ b/base/linalg/generic.jl @@ -794,6 +794,19 @@ function inv(A::AbstractMatrix{T}) where T A_ldiv_B!(factorize(convert(AbstractMatrix{S}, A)), eye(S0, checksquare(A))) end +function pinv(v::AbstractVector{T}, tol::Real=real(zero(T))) where T + res = similar(v, typeof(zero(T) / (abs2(one(T)) + abs2(one(T)))))' + den = sum(abs2, v) + # as tol is the threshold relative to the maximum singular value, for a vector with + # single singular value σ=√den, σ ≦ tol*σ is equivalent to den=0 ∨ tol≥1 + if iszero(den) || tol >= one(tol) + fill!(res, zero(eltype(res))) + else + res .= v' ./ den + end + return res +end + """ \\(A, B) @@ -841,10 +854,11 @@ function (\)(A::AbstractMatrix, B::AbstractVecOrMat) return qrfact(A,Val(true)) \ B end -(\)(a::AbstractVector, b::AbstractArray) = reshape(a, length(a), 1) \ b +(\)(a::AbstractVector, b::AbstractArray) = pinv(a) * b (/)(A::AbstractVecOrMat, B::AbstractVecOrMat) = (B' \ A')' # \(A::StridedMatrix,x::Number) = inv(A)*x Should be added at some point when the old elementwise version has been deprecated long enough # /(x::Number,A::StridedMatrix) = x*inv(A) +/(x::Number, v::AbstractVector) = x*pinv(v) cond(x::Number) = x == 0 ? Inf : 1.0 cond(x::Number, p) = cond(x) diff --git a/base/linalg/rowvector.jl b/base/linalg/rowvector.jl index b7b3270a507ef..37af261e3ea35 100644 --- a/base/linalg/rowvector.jl +++ b/base/linalg/rowvector.jl @@ -227,8 +227,12 @@ Ac_mul_B(::RowVector, ::AbstractVector) = throw(DimensionMismatch("Cannot multip Ac_mul_B(vec::AbstractVector, rowvec::RowVector) = throw(DimensionMismatch("Cannot multiply two transposed vectors")) @inline Ac_mul_B(vec1::AbstractVector, vec2::AbstractVector) = adjoint(vec1)*vec2 +# Pseudo-inverse +pinv(v::RowVector, tol::Real=0) = pinv(v', tol)' + # Left Division # +\(rowvec1::RowVector, rowvec2::RowVector) = pinv(rowvec1) * rowvec2 \(mat::AbstractMatrix, rowvec::RowVector) = throw(DimensionMismatch("Cannot left-divide transposed vector by matrix")) At_ldiv_B(mat::AbstractMatrix, rowvec::RowVector) = throw(DimensionMismatch("Cannot left-divide transposed vector by matrix")) Ac_ldiv_B(mat::AbstractMatrix, rowvec::RowVector) = throw(DimensionMismatch("Cannot left-divide transposed vector by matrix")) diff --git a/test/linalg/dense.jl b/test/linalg/dense.jl index 7eca457de6f2b..1a15a481d407e 100644 --- a/test/linalg/dense.jl +++ b/test/linalg/dense.jl @@ -76,7 +76,7 @@ bimg = randn(n,2)/2 @test_throws DimensionMismatch b'\b @test_throws DimensionMismatch b\b' @test norm(a*x - b, 1)/norm(b) < ε*κ*n*2 # Ad hoc, revisit! - @test zeros(eltya,n)\ones(eltya,n) ≈ zeros(eltya,n,1)\ones(eltya,n,1) + @test zeros(eltya,n)\ones(eltya,n) ≈ (zeros(eltya,n,1)\ones(eltya,n,1))[1,1] end @testset "Test nullspace" begin @@ -613,6 +613,43 @@ end end end +function test_rdiv_pinv_consistency(a, b) + @test (a*b)/b ≈ a*(b/b) ≈ (a*b)*pinv(b) ≈ a*(b*pinv(b)) + @test typeof((a*b)/b) == typeof(a*(b/b)) == typeof((a*b)*pinv(b)) == typeof(a*(b*pinv(b))) +end +function test_ldiv_pinv_consistency(a, b) + @test a\(a*b) ≈ (a\a)*b ≈ (pinv(a)*a)*b ≈ pinv(a)*(a*b) + @test typeof(a\(a*b)) == typeof((a\a)*b) == typeof((pinv(a)*a)*b) == typeof(pinv(a)*(a*b)) +end +function test_div_pinv_consistency(a, b) + test_rdiv_pinv_consistency(a, b) + test_ldiv_pinv_consistency(a, b) +end + +@testset "/ and \\ consistency with pinv for vectors" begin + @testset "Tests for type $elty" for elty in (Float32, Float64, Complex64, Complex128) + c = rand(elty, 5) + r = rand(elty, 5)' + cm = rand(elty, 5, 1) + rm = rand(elty, 1, 5) + @testset "inner prodcuts" begin + test_div_pinv_consistency(r, c) + test_div_pinv_consistency(rm, c) + test_div_pinv_consistency(r, cm) + test_div_pinv_consistency(rm, cm) + end + @testset "outer prodcuts" begin + test_div_pinv_consistency(c, r) + test_div_pinv_consistency(cm, rm) + end + @testset "matrix/vector" begin + m = rand(5, 5) + test_ldiv_pinv_consistency(m, c) + test_rdiv_pinv_consistency(r, m) + end + end +end + @testset "test ops on Numbers for $elty" for elty in [Float32,Float64,Complex64,Complex128] a = rand(elty) @test expm(a) == exp(a) diff --git a/test/linalg/pinv.jl b/test/linalg/pinv.jl index f95f5472b5f8b..0dd73997c441a 100644 --- a/test/linalg/pinv.jl +++ b/test/linalg/pinv.jl @@ -129,6 +129,18 @@ end a = onediag_sparse(eltya, m) test_pinv(a, m, m, default_tol, default_tol, default_tol) end + @testset "Vector" begin + a = rand(eltya, m) + apinv = @inferred pinv(a) + @test pinv(hcat(a)) ≈ apinv + @test apinv isa RowVector{eltya} + end + @testset "RowVector" begin + a = rand(eltya, m)' + apinv = @inferred pinv(a) + @test pinv(vcat(a)) ≈ apinv + @test apinv isa Vector{eltya} + end end end @@ -141,6 +153,10 @@ end @test a[1] ≈ 0.0 @test a[2] ≈ 0.0 + a = pinv([zero(eltya); zero(eltya)]') + @test a[1] ≈ 0.0 + @test a[2] ≈ 0.0 + a = pinv(Diagonal([zero(eltya); zero(eltya)])) @test a.diag[1] ≈ 0.0 @test a.diag[2] ≈ 0.0 From 3a24cbbb4b35bbcfd974362aeb6edb2ae30914c3 Mon Sep 17 00:00:00 2001 From: Ivan Gonzalez Date: Tue, 22 Aug 2017 01:08:54 -0400 Subject: [PATCH 104/324] [DOC] Improve the docstrings for quit() and exit() (#23309) Fixes #22765 --- base/initdefs.jl | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/base/initdefs.jl b/base/initdefs.jl index 2cab2349383cd..ff96f21855e3a 100644 --- a/base/initdefs.jl +++ b/base/initdefs.jl @@ -19,10 +19,12 @@ An array of the command line arguments passed to Julia, as strings. const ARGS = String[] """ - exit([code]) + exit(code=0) + +Quit the program with an exit code. The default exit code is zero, indicating that the +program completed successfully (see also [`quit`](@ref)). In an interactive session, +`exit()` can be called with the keyboard shorcut `^D`. -Quit (or control-D at the prompt). The default exit code is zero, indicating that the -processes completed successfully. """ exit(n) = ccall(:jl_exit, Void, (Int32,), n) exit() = exit(0) @@ -30,8 +32,9 @@ exit() = exit(0) """ quit() -Quit the program indicating that the processes completed successfully. This function calls -`exit(0)` (see [`exit`](@ref)). +Quit the program indicating successful completion. This function is equivalent to +`exit(0)` (see [`exit`](@ref)). In an interactive session, `quit()` can be called +with the keyboard shorcut `^D`. """ quit() = exit() From eaca8f278707a9bf9fa6bd1215420aed0a520a5e Mon Sep 17 00:00:00 2001 From: Sambit Kumar Dash Date: Tue, 22 Aug 2017 10:49:38 +0530 Subject: [PATCH 105/324] hex2bytes for Vector{UInt8} (#23267) * hex2bytes for Vector{UInt8} * Incorporated review comments. * Added example code. * Modified to support AbstractArray * Fixed comments and changed to AbstractVector * Example output fixed. * updated the description. * Add method to stdlib index * length should not be used with AbstractArray() in exception handling as it can raise exception. * removed resize as it may not work for subarrays. * Added subarray test cases. * typo fixed * Review comments incorporated related to comments and test cases. * Checking in code provided by stevengj There are some test case failures reported related to views. * Update util.jl further condense implementation and documentation, merging with `hex2bytes(s::AbstractString)` * test updates use ===, not ==, to test that operation occurs in-place, and `hex2bytes!` now throws an error for incorrect output length * fix hex2bytes for views (where start is not an index) * rm redundant length check --- base/exports.jl | 1 + base/strings/util.jl | 76 ++++++++++++++++++++++++++------------- doc/src/stdlib/numbers.md | 1 + test/strings/util.jl | 21 +++++++++++ 4 files changed, 74 insertions(+), 25 deletions(-) diff --git a/base/exports.jl b/base/exports.jl index 49d3ceb5d3568..96d860e7d6bb7 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -755,6 +755,7 @@ export graphemes, hex, hex2bytes, + hex2bytes!, ind2chr, info, is_assigned_char, diff --git a/base/strings/util.jl b/base/strings/util.jl index 783ac0a6b20cb..665fae4c6398d 100644 --- a/base/strings/util.jl +++ b/base/strings/util.jl @@ -437,50 +437,76 @@ replace(s::AbstractString, pat, f) = replace_new(String(s), pat, f, typemax(Int) # hex <-> bytes conversion """ - hex2bytes(s::AbstractString) + hex2bytes(s::Union{AbstractString,AbstractVector{UInt8}}) -Convert an arbitrarily long hexadecimal string to its binary representation. Returns an -`Array{UInt8,1}`, i.e. an array of bytes. +Given a string or array `s` of ASCII codes for a sequence of hexadecimal digits, returns a +`Vector{UInt8}` of bytes corresponding to the binary representation: each successive pair +of hexadecimal digits in `s` gives the value of one byte in the return vector. + +The length of `s` must be even, and the returned array has half of the length of `s`. +See also [`hex2bytes!`](@ref) for an in-place version, and [`bytes2hex`](@ref) for the inverse. # Examples ```jldoctest -julia> a = hex(12345) +julia> s = hex(12345) "3039" -julia> hex2bytes(a) +julia> hex2bytes(s) 2-element Array{UInt8,1}: 0x30 0x39 + +julia> a = b"01abEF" +6-element Array{UInt8,1}: + 0x30 + 0x31 + 0x61 + 0x62 + 0x45 + 0x46 + +julia> hex2bytes(a) +3-element Array{UInt8,1}: + 0x01 + 0xab + 0xef ``` """ -function hex2bytes(s::AbstractString) - a = zeros(UInt8, div(endof(s), 2)) - i, j = start(s), 0 - while !done(s, i) - c, i = next(s, i) - n = '0' <= c <= '9' ? c - '0' : - 'a' <= c <= 'f' ? c - 'a' + 10 : - 'A' <= c <= 'F' ? c - 'A' + 10 : - throw(ArgumentError("not a hexadecimal string: $(repr(s))")) - done(s, i) && - throw(ArgumentError("string length must be even: length($(repr(s))) == $(length(s))")) - c, i = next(s, i) - n = '0' <= c <= '9' ? n << 4 + c - '0' : - 'a' <= c <= 'f' ? n << 4 + c - 'a' + 10 : - 'A' <= c <= 'F' ? n << 4 + c - 'A' + 10 : - throw(ArgumentError("not a hexadecimal string: $(repr(s))")) - a[j += 1] = n +function hex2bytes end + +hex2bytes(s::AbstractString) = hex2bytes(Vector{UInt8}(String(s))) +hex2bytes(s::AbstractVector{UInt8}) = hex2bytes!(Vector{UInt8}(length(s) >> 1), s) + +""" + hex2bytes!(d::AbstractVector{UInt8}, s::AbstractVector{UInt8}) + +Convert an array `s` of bytes representing a hexadecimal string to its binary +representation, similar to [`hex2bytes`](@ref) except that the output is written in-place +in `d`. The length of `s` must be exactly twice the length of `d`. +""" +function hex2bytes!(d::AbstractVector{UInt8}, s::AbstractVector{UInt8}) + if 2length(d) != length(s) + isodd(length(s)) && throw(ArgumentError("input hex array must have even length")) + throw(ArgumentError("output array must be half length of input array")) end - resize!(a, j) - return a + j = first(eachindex(d)) - 1 + for i = first(eachindex(s)):2:endof(s) + @inbounds d[j += 1] = number_from_hex(s[i]) << 4 + number_from_hex(s[i+1]) + end + return d end +@inline number_from_hex(c) = + (UInt8('0') <= c <= UInt8('9')) ? c - UInt8('0') : + (UInt8('A') <= c <= UInt8('F')) ? c - (UInt8('A') - 0x0a) : + (UInt8('a') <= c <= UInt8('f')) ? c - (UInt8('a') - 0x0a) : + throw(ArgumentError("byte is not an ASCII hexadecimal digit")) + """ bytes2hex(bin_arr::Array{UInt8, 1}) -> String Convert an array of bytes to its hexadecimal representation. All characters are in lower-case. - # Examples ```jldoctest julia> a = hex(12345) diff --git a/doc/src/stdlib/numbers.md b/doc/src/stdlib/numbers.md index 91941c4a338fa..bd4aea8f52f8d 100644 --- a/doc/src/stdlib/numbers.md +++ b/doc/src/stdlib/numbers.md @@ -59,6 +59,7 @@ Base.Math.exponent Base.complex(::Complex) Base.bswap Base.hex2bytes +Base.hex2bytes! Base.bytes2hex ``` diff --git a/test/strings/util.jl b/test/strings/util.jl index 7185964ae7aec..18f2d5002a6e8 100644 --- a/test/strings/util.jl +++ b/test/strings/util.jl @@ -247,3 +247,24 @@ bin_val = hex2bytes("07bf") #non-hex characters @test_throws ArgumentError hex2bytes("0123456789abcdefABCDEFGH") + +@testset "Issue 23161" begin + arr = b"0123456789abcdefABCDEF" + arr1 = Vector{UInt8}(length(arr) >> 1) + @test hex2bytes!(arr1, arr) === arr1 # check in-place + @test "0123456789abcdefabcdef" == bytes2hex(arr1) + @test hex2bytes("0123456789abcdefABCDEF") == hex2bytes(arr) + @test_throws ArgumentError hex2bytes!(arr1, b"") # incorrect arr1 length + @test hex2bytes(b"") == UInt8[] + @test hex2bytes(view(b"012345",1:6)) == UInt8[0x01,0x23,0x45] + @test begin + s = view(b"012345ab",1:6) + d = view(zeros(UInt8, 10),1:3) + hex2bytes!(d,s) == UInt8[0x01,0x23,0x45] + end + # odd size + @test_throws ArgumentError hex2bytes(b"0123456789abcdefABCDEF0") + + #non-hex characters + @test_throws ArgumentError hex2bytes(b"0123456789abcdefABCDEFGH") +end From 6a6929d9823b2243aa14a51b5577a49377eafe75 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Tue, 22 Aug 2017 01:19:49 -0400 Subject: [PATCH 106/324] evaluate `for` iterator in outer scope --- NEWS.md | 3 +++ src/ast.scm | 7 +++++++ src/julia-syntax.scm | 39 +++++++++++++++------------------------ test/core.jl | 7 +++++++ 4 files changed, 32 insertions(+), 24 deletions(-) diff --git a/NEWS.md b/NEWS.md index 3d8d2927676a4..912bacd080e0a 100644 --- a/NEWS.md +++ b/NEWS.md @@ -70,6 +70,9 @@ Language changes warning, so that this syntax can be disallowed or given a new meaning in a future version ([#5148]). + * 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. + Breaking changes ---------------- diff --git a/src/ast.scm b/src/ast.scm index 4b9d730a392b1..b4c46ab4a3509 100644 --- a/src/ast.scm +++ b/src/ast.scm @@ -305,6 +305,13 @@ (define (eq-sym? a b) (or (eq? a b) (and (ssavalue? a) (ssavalue? b) (eqv? (cdr a) (cdr b))))) +(define (blockify e) + (if (and (pair? e) (eq? (car e) 'block)) + (if (null? (cdr e)) + `(block (null)) + e) + `(block ,e))) + (define (make-var-info name) (list name '(core Any) 0)) (define vinfo:name car) (define vinfo:type cadr) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 15f340da072fd..040d4b04c9a2b 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -153,11 +153,7 @@ ;; except for rest arg (define (method-lambda-expr argl body rett) (let ((argl (map arg-name argl)) - (body (if (and (pair? body) (eq? (car body) 'block)) - (if (null? (cdr body)) - `(block (null)) - body) - `(block ,body)))) + (body (blockify body))) `(lambda ,argl () (scope-block ,(if (equal? rett '(core Any)) @@ -381,9 +377,7 @@ (if (nospecialize-meta? a) (caddr a) a)) kargl)) (pargl (cdr argl)) ;; positional args - (body (if (and (pair? body) (eq? (car body) 'block)) - body - `(block ,body))) + (body (blockify body)) (ftype (decl-type (car pargl))) ;; 1-element list of vararg argument, or empty if none (vararg (let ((l (if (null? pargl) '() (last pargl)))) @@ -1681,19 +1675,18 @@ ;; (for (= lhs X) body) (let ((coll (make-ssavalue)) (state (gensy))) - `(scope-block - (block (= ,coll ,(expand-forms X)) - (= ,state (call (top start) ,coll)) - ,(expand-forms - `(,while - (call (top !) (call (top done) ,coll ,state)) - (scope-block - (block - ;; NOTE: enable this to force loop-local var - #;,@(map (lambda (v) `(local ,v)) (lhs-vars lhs)) - ,(lower-tuple-assignment (list lhs state) - `(call (top next) ,coll ,state)) - ,body)))))))) + `(block (= ,coll ,(expand-forms X)) + (= ,state (call (top start) ,coll)) + ,(expand-forms + `(,while + (call (top !) (call (top done) ,coll ,state)) + (scope-block + (block + ;; NOTE: enable this to force loop-local var + #;,@(map (lambda (v) `(local ,v)) (lhs-vars lhs)) + ,(lower-tuple-assignment (list lhs state) + `(call (top next) ,coll ,state)) + ,body))))))) ;; convert an operator parsed as (op a b) to (call op a b) (define (syntactic-op-to-call e) @@ -2628,9 +2621,7 @@ (set-car! (cddr lam) (append real-new-vars real-new-vars-def (caddr lam)))) (insert-after-meta ;; return the new, expanded scope-block - (if (and (pair? body) (eq? (car body) 'block)) - body - `(block ,body)) + (blockify body) (append! (map (lambda (v) `(local ,v)) real-new-vars) (map (lambda (v) `(local-def ,v)) real-new-vars-def))))) ((eq? (car e) 'module) diff --git a/test/core.jl b/test/core.jl index 80381bc93c824..a8287f3e8c01f 100644 --- a/test/core.jl +++ b/test/core.jl @@ -2921,6 +2921,13 @@ function f11065() end @test_throws UndefVarError f11065() +# for loop iterator expression should be evaluated in outer scope +let + for i in (local a = 1:2) + end + @test a == 1:2 +end + # issue #11295 function f11295(x...) call = Expr(x...) From 1ad17ba145ab6350fcde8cc8c818d717c2d3026d Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Thu, 17 Aug 2017 20:55:17 -0400 Subject: [PATCH 107/324] refactor String constructors - converting between strings, Symbols, and Vectors should be a constructor, not `convert` - define everything as constructors, with a single general `convert` method for all string types --- base/deprecated.jl | 8 ++++++++ base/stacktraces.jl | 3 ++- base/strings/basic.jl | 20 +++++++------------- base/strings/string.jl | 16 ++++++++++++---- base/strings/types.jl | 9 ++++----- base/strings/utf8proc.jl | 2 -- base/test.jl | 1 - test/strings/basic.jl | 9 ++++----- test/unicode/utf8.jl | 6 +++--- test/unicode/utf8proc.jl | 1 - 10 files changed, 40 insertions(+), 35 deletions(-) diff --git a/base/deprecated.jl b/base/deprecated.jl index ce59333032c4c..e957579bad82e 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1696,6 +1696,14 @@ export hex2num @deprecate ctranspose adjoint @deprecate ctranspose! adjoint! +@deprecate convert(::Type{Vector{UInt8}}, s::AbstractString) Vector{UInt8}(s) +@deprecate convert(::Type{Array{UInt8}}, s::AbstractString) Vector{UInt8}(s) +@deprecate convert(::Type{Vector{Char}}, s::AbstractString) Vector{Char}(s) +@deprecate convert(::Type{Symbol}, s::AbstractString) Symbol(s) +@deprecate convert(::Type{String}, s::Symbol) String(s) +@deprecate convert(::Type{String}, v::Vector{UInt8}) String(v) +@deprecate convert(::Type{S}, g::UTF8proc.GraphemeIterator) where {S<:AbstractString} convert(S, g.s) + # issue #5148, PR #23259 # warning for `const` on locals should be changed to an error in julia-syntax.scm diff --git a/base/stacktraces.jl b/base/stacktraces.jl index 8fbe116a6270c..a12d9c5888590 100644 --- a/base/stacktraces.jl +++ b/base/stacktraces.jl @@ -59,7 +59,8 @@ struct StackFrame # this type should be kept platform-agnostic so that profiles pointer::UInt64 # Large enough to be read losslessly on 32- and 64-bit machines. end -StackFrame(func, file, line) = StackFrame(func, file, line, Nullable{Core.MethodInstance}(), false, false, 0) +StackFrame(func, file, line) = StackFrame(Symbol(func), Symbol(file), line, + Nullable{Core.MethodInstance}(), false, false, 0) """ StackTrace diff --git a/base/strings/basic.jl b/base/strings/basic.jl index 6609cd1417b96..b3bdd085ab3ec 100644 --- a/base/strings/basic.jl +++ b/base/strings/basic.jl @@ -10,20 +10,15 @@ next(s::AbstractString, i::Integer) = next(s,Int(i)) string() = "" string(s::AbstractString) = s -""" - String(s::AbstractString) +(::Type{Vector{UInt8}})(s::AbstractString) = Vector{UInt8}(String(s)) +(::Type{Array{UInt8}})(s::AbstractString) = Vector{UInt8}(s) +(::Type{Vector{Char}})(s::AbstractString) = collect(s) -Convert a string to a contiguous byte array representation encoded as UTF-8 bytes. -This representation is often appropriate for passing strings to C. -""" -String(s::AbstractString) = print_to_string(s) +Symbol(s::AbstractString) = Symbol(String(s)) -convert(::Type{Vector{UInt8}}, s::AbstractString) = convert(Vector{UInt8}, String(s)) -convert(::Type{Array{UInt8}}, s::AbstractString) = convert(Vector{UInt8}, s) -convert(::Type{String}, s::AbstractString) = String(s) -convert(::Type{Vector{Char}}, s::AbstractString) = collect(s) -convert(::Type{Symbol}, s::AbstractString) = Symbol(s) -convert(::Type{String}, s::Symbol) = unsafe_string(Cstring(s)) +# string types are convertible +convert(::Type{T}, s::T) where {T<:AbstractString} = s +convert(::Type{T}, s::AbstractString) where {T<:AbstractString} = T(s) ## generic supplied functions ## @@ -40,7 +35,6 @@ getindex(s::AbstractString, v::AbstractVector{Bool}) = throw(ArgumentError("logical indexing not supported for strings")) get(s::AbstractString, i::Integer, default) = isvalid(s,i) ? s[i] : default -Symbol(s::AbstractString) = Symbol(String(s)) """ sizeof(s::AbstractString) diff --git a/base/strings/string.jl b/base/strings/string.jl index 6a9ebb3880e39..4b43c2a4d7b44 100644 --- a/base/strings/string.jl +++ b/base/strings/string.jl @@ -42,9 +42,17 @@ end _string_n(n::Integer) = ccall(:jl_alloc_string, Ref{String}, (Csize_t,), n) -convert(::Type{Vector{UInt8}}, s::String) = ccall(:jl_string_to_array, Ref{Vector{UInt8}}, (Any,), s) -convert(::Type{String}, s::String) = s -convert(::Type{String}, v::Vector{UInt8}) = String(v) +""" + String(s::AbstractString) + +Convert a string to a contiguous byte array representation encoded as UTF-8 bytes. +This representation is often appropriate for passing strings to C. +""" +String(s::AbstractString) = print_to_string(s) + +String(s::Symbol) = unsafe_string(Cstring(s)) + +(::Type{Vector{UInt8}})(s::String) = ccall(:jl_string_to_array, Ref{Vector{UInt8}}, (Any,), s) ## low-level functions ## @@ -394,7 +402,7 @@ function string(a::Union{String,Char}...) end function reverse(s::String) - dat = convert(Vector{UInt8},s) + dat = Vector{UInt8}(s) n = length(dat) n <= 1 && return s buf = StringVector(n) diff --git a/base/strings/types.jl b/base/strings/types.jl index 3fe9b0ca0fb78..4df69ea67f3a1 100644 --- a/base/strings/types.jl +++ b/base/strings/types.jl @@ -30,6 +30,10 @@ SubString(s::T, i::Int, j::Int) where {T<:AbstractString} = SubString{T}(s, i, j SubString(s::SubString, i::Int, j::Int) = SubString(s.string, s.offset+i, s.offset+j) SubString(s::AbstractString, i::Integer, j::Integer) = SubString(s, Int(i), Int(j)) SubString(s::AbstractString, i::Integer) = SubString(s, i, endof(s)) +SubString{T}(s::T) where {T<:AbstractString} = SubString(s, 1, endof(s)) + +String(p::SubString{String}) = + unsafe_string(pointer(p.string, p.offset+1), nextind(p, p.endof)-1) sizeof(s::SubString{String}) = s.endof == 0 ? 0 : nextind(s, s.endof) - 1 @@ -73,11 +77,6 @@ chr2ind(s::SubString{<:DirectIndexString}, i::Integer) = begin checkbounds(s,i); nextind(s::SubString, i::Integer) = nextind(s.string, i+s.offset)-s.offset prevind(s::SubString, i::Integer) = prevind(s.string, i+s.offset)-s.offset -convert(::Type{SubString{T}}, s::T) where {T<:AbstractString} = SubString(s, 1, endof(s)) - -String(p::SubString{String}) = - unsafe_string(pointer(p.string, p.offset+1), nextind(p, p.endof)-1) - function getindex(s::AbstractString, r::UnitRange{Int}) checkbounds(s, r) || throw(BoundsError(s, r)) SubString(s, first(r), last(r)) diff --git a/base/strings/utf8proc.jl b/base/strings/utf8proc.jl index 2cf9bfdb51d24..3c82902a1c01b 100644 --- a/base/strings/utf8proc.jl +++ b/base/strings/utf8proc.jl @@ -500,8 +500,6 @@ end hash(g::GraphemeIterator, h::UInt) = hash(g.s, h) isless(g1::GraphemeIterator, g2::GraphemeIterator) = isless(g1.s, g2.s) -convert(::Type{S}, g::GraphemeIterator) where {S<:AbstractString} = convert(S, g.s) - show(io::IO, g::GraphemeIterator{S}) where {S} = print(io, "length-$(length(g)) GraphemeIterator{$S} for \"$(g.s)\"") ############################################################################ diff --git a/base/test.jl b/base/test.jl index 9faad132c9902..08d7a399056ff 100644 --- a/base/test.jl +++ b/base/test.jl @@ -1366,7 +1366,6 @@ with string types besides the standard `String` type. struct GenericString <: AbstractString string::AbstractString end -Base.convert(::Type{GenericString}, s::AbstractString) = GenericString(s) Base.endof(s::GenericString) = endof(s.string) Base.next(s::GenericString, i::Int) = next(s.string, i) diff --git a/test/strings/basic.jl b/test/strings/basic.jl index 638c26a21113e..9f06e4a287f7b 100644 --- a/test/strings/basic.jl +++ b/test/strings/basic.jl @@ -189,16 +189,15 @@ end struct tstStringType <: AbstractString data::Array{UInt8,1} end -tstr = tstStringType("12") +tstr = tstStringType(Vector{UInt8}("12")) @test_throws ErrorException endof(tstr) @test_throws ErrorException next(tstr, Bool(1)) gstr = GenericString("12") -@test typeof(string(gstr))==GenericString +@test string(gstr) isa GenericString -@test convert(Array{UInt8}, gstr) ==[49;50] -@test convert(Array{Char,1}, gstr) ==['1';'2'] -@test convert(Symbol, gstr)==Symbol("12") +@test Array{UInt8}(gstr) == [49, 50] +@test Array{Char,1}(gstr) == ['1', '2'] @test gstr[1] == '1' @test gstr[1:1] == "1" diff --git a/test/unicode/utf8.jl b/test/unicode/utf8.jl index 093c899e0f3ee..bdb9664fc26fb 100644 --- a/test/unicode/utf8.jl +++ b/test/unicode/utf8.jl @@ -4,7 +4,7 @@ let ch = 0x10000 for hi = 0xd800:0xdbff for lo = 0xdc00:0xdfff - @test convert(String, Vector{UInt8}(String(Char[hi, lo]))) == string(Char(ch)) + @test String(Vector{UInt8}(String(Char[hi, lo]))) == string(Char(ch)) ch += 1 end end @@ -41,7 +41,7 @@ end end @testset "string convert" begin - @test convert(String, b"this is a test\xed\x80\x80") == "this is a test\ud000" + @test String(b"this is a test\xed\x80\x80") == "this is a test\ud000" ## Specifically check UTF-8 string whose lead byte is same as a surrogate - @test convert(String, b"\xed\x9f\xbf") == "\ud7ff" + @test String(b"\xed\x9f\xbf") == "\ud7ff" end diff --git a/test/unicode/utf8proc.jl b/test/unicode/utf8proc.jl index d06106a9abefc..2c70ed21291f8 100644 --- a/test/unicode/utf8proc.jl +++ b/test/unicode/utf8proc.jl @@ -310,7 +310,6 @@ end g = graphemes(str) h = hash(str) @test hash(g) == h - @test convert(GenericString, g) == str @test repr(g) == "length-14 GraphemeIterator{String} for \"$str\"" end end From 8404314384fd519248dec8fce64472819a787ce1 Mon Sep 17 00:00:00 2001 From: Ivan Gonzalez Date: Tue, 22 Aug 2017 02:13:22 -0400 Subject: [PATCH 108/324] WIP: [DOC] Fix some doctest errors (#23331) * [DOC] Fix some doctest errors Running `make -C doc checks` gives a bunch of warnings and errors. This removes some of them. * Fix doctest error in base/channels.jl * [DOC] Fix more doctest errors * [DOC] Fix broken link in the manual * [DOC] Clarify that redefining built-in is not recommended * Fix more doctest errors --- base/channels.jl | 4 ++-- base/docs/basedocs.jl | 8 ++++---- base/essentials.jl | 4 ++-- base/nullable.jl | 4 ++-- base/number.jl | 6 ++++-- base/test.jl | 2 +- doc/src/devdocs/types.md | 2 +- doc/src/manual/arrays.md | 4 ++-- doc/src/manual/dates.md | 2 +- doc/src/manual/faq.md | 8 ++++---- doc/src/manual/networking-and-streams.md | 4 ++-- doc/src/manual/noteworthy-differences.md | 2 +- doc/src/manual/strings.md | 4 ++-- doc/src/manual/types.md | 20 +++++++++--------- doc/src/manual/variables.md | 26 ++++++++++++++++-------- 15 files changed, 55 insertions(+), 45 deletions(-) diff --git a/base/channels.jl b/base/channels.jl index 5c73db99e5be2..f862f00c6274b 100644 --- a/base/channels.jl +++ b/base/channels.jl @@ -196,8 +196,8 @@ julia> take!(c) julia> put!(c,1); ERROR: foo Stacktrace: - [1] check_channel_state(::Channel{Any}) at ./channels.jl:131 - [2] put!(::Channel{Any}, ::Int64) at ./channels.jl:261 + [1] check_channel_state at ./channels.jl:132 [inlined] + [2] put!(::Channel{Any}, ::Int64) at ./channels.jl:263 ``` """ function bind(c::Channel, task::Task) diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index c64b71aaa4786..f3bdfb8ffb1d2 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -808,19 +808,19 @@ julia> A = ones(7); julia> A[8] ERROR: BoundsError: attempt to access 7-element Array{Float64,1} at index [8] Stacktrace: - [1] getindex(::Array{Float64,1}, ::Int64) at ./array.jl:586 + [1] getindex(::Array{Float64,1}, ::Int64) at ./array.jl:763 julia> B = ones(2, 3); julia> B[2, 4] ERROR: BoundsError: attempt to access 2×3 Array{Float64,2} at index [2, 4] Stacktrace: - [1] getindex(::Array{Float64,2}, ::Int64, ::Int64) at ./array.jl:587 + [1] getindex(::Array{Float64,2}, ::Int64, ::Int64) at ./array.jl:764 julia> B[9] ERROR: BoundsError: attempt to access 2×3 Array{Float64,2} at index [9] Stacktrace: - [1] getindex(::Array{Float64,2}, ::Int64) at ./array.jl:586 + [1] getindex(::Array{Float64,2}, ::Int64) at ./array.jl:763 ``` """ BoundsError @@ -982,7 +982,7 @@ Inf julia> div(2, 0) ERROR: DivideError: integer division error Stacktrace: - [1] div(::Int64, ::Int64) at ./int.jl:183 + [1] div(::Int64, ::Int64) at ./int.jl:220 ``` """ DivideError diff --git a/base/essentials.jl b/base/essentials.jl index f9450df49d487..fe5a1646ed3ef 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -78,7 +78,7 @@ julia> convert(Int, 3.0) julia> convert(Int, 3.5) ERROR: InexactError: convert(Int64, 3.5) Stacktrace: - [1] convert(::Type{Int64}, ::Float64) at ./float.jl:680 + [1] convert(::Type{Int64}, ::Float64) at ./float.jl:701 ``` If `T` is a [`AbstractFloat`](@ref) or [`Rational`](@ref) type, @@ -361,7 +361,7 @@ If `T` does not have a specific size, an error is thrown. julia> sizeof(Base.LinAlg.LU) ERROR: argument is an abstract type; size is indeterminate Stacktrace: - [1] sizeof(::Type{T} where T) at ./essentials.jl:150 + [1] sizeof(::Type{T} where T) at ./essentials.jl:367 ``` """ sizeof(x) = Core.sizeof(x) diff --git a/base/nullable.jl b/base/nullable.jl index 5a4d4f0c46fc4..64e0069999c8c 100644 --- a/base/nullable.jl +++ b/base/nullable.jl @@ -13,7 +13,7 @@ Nullable{Int64}() julia> get(a) ERROR: NullException() Stacktrace: - [1] get(::Nullable{Int64}) at ./nullable.jl:92 + [1] get(::Nullable{Int64}) at ./nullable.jl:118 ``` """ struct NullException <: Exception @@ -140,7 +140,7 @@ Nullable{String}() julia> unsafe_get(x) ERROR: UndefRefError: access to undefined reference Stacktrace: - [1] unsafe_get(::Nullable{String}) at ./nullable.jl:136 + [1] unsafe_get(::Nullable{String}) at ./nullable.jl:152 julia> x = 1 1 diff --git a/base/number.jl b/base/number.jl index 2732e98b825ba..4fb47f8c54cf3 100644 --- a/base/number.jl +++ b/base/number.jl @@ -306,8 +306,10 @@ julia> factorial(6) 720 julia> factorial(21) -ERROR: OverflowError() -[...] +ERROR: OverflowError: 21 is too large to look up in the table +Stacktrace: + [1] factorial_lookup at ./combinatorics.jl:19 [inlined] + [2] factorial(::Int64) at ./combinatorics.jl:27 julia> factorial(21.0) 5.109094217170944e19 diff --git a/base/test.jl b/base/test.jl index 9faad132c9902..16a19277030f4 100644 --- a/base/test.jl +++ b/base/test.jl @@ -1140,7 +1140,7 @@ Body: julia> @inferred f(1,2,3) ERROR: return type Int64 does not match inferred return type Union{Float64, Int64} Stacktrace: - [1] error(::String) at ./error.jl:21 + [1] error(::String) at ./error.jl:33 julia> @inferred max(1,2) 2 diff --git a/doc/src/devdocs/types.md b/doc/src/devdocs/types.md index 3008a5990611e..c43e8c846e050 100644 --- a/doc/src/devdocs/types.md +++ b/doc/src/devdocs/types.md @@ -28,7 +28,7 @@ julia> typeintersect(Int, Float64) Union{} julia> Union{Int, Float64} -Union{Float64, Int64} +Union{Int64, Float64} julia> typejoin(Int, Float64) Real diff --git a/doc/src/manual/arrays.md b/doc/src/manual/arrays.md index ecdcf33cb3855..f91b93f98ec0b 100644 --- a/doc/src/manual/arrays.md +++ b/doc/src/manual/arrays.md @@ -600,8 +600,8 @@ it also handles tuples and treats any argument that is not an array, tuple or `R ```jldoctest julia> convert.(Float32, [1, 2]) 2-element Array{Float32,1}: - 1.0 - 2.0 + 1.0f0 + 2.0f0 julia> ceil.((UInt8,), [1.2 3.4; 5.6 6.7]) 2×2 Array{UInt8,2}: diff --git a/doc/src/manual/dates.md b/doc/src/manual/dates.md index ec2c5eb54ed1a..e9b34ac5d0e01 100644 --- a/doc/src/manual/dates.md +++ b/doc/src/manual/dates.md @@ -350,7 +350,7 @@ little as possible when doing [`Period`](@ref) arithmetic. This approach is also *calendrical* arithmetic or what you would probably guess if someone were to ask you the same calculation in a conversation. Why all the fuss about this? Let's take a classic example: add 1 month to January 31st, 2014. What's the answer? Javascript will say [March 3](http://www.markhneedham.com/blog/2009/01/07/javascript-add-a-month-to-a-date/) -(assumes 31 days). PHP says [March 2](http://stackoverflow.com/questions/5760262/php-adding-months-to-a-date-while-not-exceeding-the-last-day-of-the-month) +(assumes 31 days). PHP says [March 2](https://stackoverflow.com/questions/5760262/php-adding-months-to-a-date-while-not-exceeding-the-last-day-of-the-month) (assumes 30 days). The fact is, there is no right answer. In the `Dates` module, it gives the result of February 28th. How does it figure that out? I like to think of the classic 7-7-7 gambling game in casinos. diff --git a/doc/src/manual/faq.md b/doc/src/manual/faq.md index cb0d7c9a39696..776f26ff04e86 100644 --- a/doc/src/manual/faq.md +++ b/doc/src/manual/faq.md @@ -235,10 +235,10 @@ ERROR: DomainError with -5: Cannot raise an integer x to a negative power -5. Make x a float by adding a zero decimal (e.g., 2.0^-5 instead of 2^-5), or write 1/x^5, float(x)^-5, or (x//1)^-5 Stacktrace: - [1] throw_domerr_powbysq(::Int64) at ./intfuncs.jl:163 - [2] power_by_squaring at ./intfuncs.jl:178 [inlined] - [3] ^ at ./intfuncs.jl:202 [inlined] - [4] literal_pow(::Base.#^, ::Int64, ::Val{-5}) at ./intfuncs.jl:213 + [1] throw_domerr_powbysq(::Int64) at ./intfuncs.jl:164 + [2] power_by_squaring at ./intfuncs.jl:179 [inlined] + [3] ^ at ./intfuncs.jl:203 [inlined] + [4] literal_pow(::Base.#^, ::Int64, ::Val{-5}) at ./intfuncs.jl:214 ``` This behavior is an inconvenient consequence of the requirement for type-stability. In the case diff --git a/doc/src/manual/networking-and-streams.md b/doc/src/manual/networking-and-streams.md index d2708a16bba69..00ee502a61639 100644 --- a/doc/src/manual/networking-and-streams.md +++ b/doc/src/manual/networking-and-streams.md @@ -226,8 +226,8 @@ Base.PipeServer(active) Note that the return type of the last invocation is different. This is because this server does not listen on TCP, but rather on a named pipe (Windows) or UNIX domain socket. Also note that Windows named pipe format has to be a specific pattern such that the name prefix (`\\.\pipe\`) uniquely -identifies the [file type](https://msdn.microsoft.com/en- -us/library/windows/desktop/aa365783(v=vs.85).aspx). The difference between TCP and named pipes or +identifies the [file type](https://msdn.microsoft.com/en-us/library/windows/desktop/aa365783(v=vs.85).aspx). +The difference between TCP and named pipes or UNIX domain sockets is subtle and has to do with the [`accept()`](@ref) and [`connect()`](@ref) methods. The [`accept()`](@ref) method retrieves a connection to the client that is connecting on the server we just created, while the [`connect()`](@ref) function connects to a server using the diff --git a/doc/src/manual/noteworthy-differences.md b/doc/src/manual/noteworthy-differences.md index c94e231252499..72a3ea4467fa4 100644 --- a/doc/src/manual/noteworthy-differences.md +++ b/doc/src/manual/noteworthy-differences.md @@ -140,7 +140,7 @@ For users coming to Julia from R, these are some noteworthy differences: * Julia cannot assign to the results of function calls on the left hand side of an assignment operation: you cannot write `diag(M) = ones(n)`. * Julia discourages populating the main namespace with functions. Most statistical functionality - for Julia is found in [packages](http://pkg.julialang.org/) under the [JuliaStats organization](https://github.com/JuliaStats). + for Julia is found in [packages](https://pkg.julialang.org/) under the [JuliaStats organization](https://github.com/JuliaStats). For example: * Functions pertaining to probability distributions are provided by the [Distributions package](https://github.com/JuliaStats/Distributions.jl). diff --git a/doc/src/manual/strings.md b/doc/src/manual/strings.md index 2eb91e53ae111..8f7957c7250ed 100644 --- a/doc/src/manual/strings.md +++ b/doc/src/manual/strings.md @@ -648,7 +648,7 @@ julia> m.match "acd" julia> m.captures -3-element Array{Union{SubString{String}, Void},1}: +3-element Array{Union{Void, SubString{String}},1}: "a" "c" "d" @@ -669,7 +669,7 @@ julia> m.match "ad" julia> m.captures -3-element Array{Union{SubString{String}, Void},1}: +3-element Array{Union{Void, SubString{String}},1}: "a" nothing "d" diff --git a/doc/src/manual/types.md b/doc/src/manual/types.md index 7f19fcac08a67..5825d121ff81f 100644 --- a/doc/src/manual/types.md +++ b/doc/src/manual/types.md @@ -69,7 +69,7 @@ an exception is thrown, otherwise, the left-hand value is returned: ```jldoctest julia> (1+2)::AbstractFloat -ERROR: TypeError: typeassert: expected AbstractFloat, got Int64 +ERROR: TypeError: in typeassert, expected AbstractFloat, got Int64 julia> (1+2)::Int 3 @@ -345,7 +345,7 @@ must be convertible to `Int`: julia> Foo((), 23.5, 1) ERROR: InexactError: convert(Int64, 23.5) Stacktrace: - [1] convert at ./float.jl:681 [inlined] + [1] convert at ./float.jl:701 [inlined] [2] Foo(::Tuple{}, ::Float64, ::Int64) at ./none:2 ``` @@ -483,7 +483,7 @@ argument types, constructed using the special `Union` function: ```jldoctest julia> IntOrString = Union{Int,AbstractString} -Union{AbstractString, Int64} +Union{Int64, AbstractString} julia> 1 :: IntOrString 1 @@ -492,7 +492,7 @@ julia> "Hello!" :: IntOrString "Hello!" julia> 1.0 :: IntOrString -ERROR: TypeError: typeassert: expected Union{AbstractString, Int64}, got Float64 +ERROR: TypeError: in typeassert, expected Union{Int64, AbstractString}, got Float64 ``` The compilers for many languages have an internal union construct for reasoning about types; Julia @@ -651,7 +651,7 @@ ERROR: MethodError: Cannot `convert` an object of type Float64 to an object of t This may have arisen from a call to the constructor Point{Float64}(...), since type constructors fall back to convert methods. Stacktrace: - [1] Point{Float64}(::Float64) at ./sysimg.jl:102 + [1] Point{Float64}(::Float64) at ./sysimg.jl:103 julia> Point{Float64}(1.0,2.0,3.0) ERROR: MethodError: no method matching Point{Float64}(::Float64, ::Float64, ::Float64) @@ -801,10 +801,10 @@ julia> Pointy{Real} Pointy{Real} julia> Pointy{AbstractString} -ERROR: TypeError: Pointy: in T, expected T<:Real, got Type{AbstractString} +ERROR: TypeError: in Pointy, in T, expected T<:Real, got Type{AbstractString} julia> Pointy{1} -ERROR: TypeError: Pointy: in T, expected T<:Real, got Int64 +ERROR: TypeError: in Pointy, in T, expected T<:Real, got Int64 ``` Type parameters for parametric composite types can be restricted in the same manner: @@ -1163,8 +1163,8 @@ is raised: julia> supertype(Union{Float64,Int64}) ERROR: MethodError: no method matching supertype(::Type{Union{Float64, Int64}}) Closest candidates are: - supertype(!Matched::DataType) at operators.jl:41 - supertype(!Matched::UnionAll) at operators.jl:46 + supertype(!Matched::DataType) at operators.jl:42 + supertype(!Matched::UnionAll) at operators.jl:47 ``` ## Custom pretty-printing @@ -1423,7 +1423,7 @@ You can safely access the value of a `Nullable` object using [`get()`](@ref): julia> get(Nullable{Float64}()) ERROR: NullException() Stacktrace: - [1] get(::Nullable{Float64}) at ./nullable.jl:92 + [1] get(::Nullable{Float64}) at ./nullable.jl:118 julia> get(Nullable(1.0)) 1.0 diff --git a/doc/src/manual/variables.md b/doc/src/manual/variables.md index 8683b3ace795b..700adc4b916ef 100644 --- a/doc/src/manual/variables.md +++ b/doc/src/manual/variables.md @@ -59,29 +59,37 @@ name `δ` can be entered by typing `\delta`-*tab*, or even `α̂₂` by `\alpha` that you don't know how to type, the REPL help will tell you: just type `?` and then paste the symbol.) -Julia will even let you redefine built-in constants and functions if needed: +Julia will even let you redefine built-in constants and functions if needed (although +this is not recommended to avoid potential confusions): ```jldoctest -julia> pi -π = 3.1415926535897... - julia> pi = 3 -WARNING: imported binding for pi overwritten in module Main 3 julia> pi 3 +julia> sqrt = 4 +4 +``` + +However, if you try to redefine a built-in constant or function already in use, Julia will give +you an error: + +```jldoctest +julia> pi +π = 3.1415926535897... + +julia> pi = 3 +ERROR: cannot assign variable Base.pi from module Main + julia> sqrt(100) 10.0 julia> sqrt = 4 -WARNING: imported binding for sqrt overwritten in module Main -4 +ERROR: cannot assign variable Base.sqrt from module Main ``` -However, this is obviously not recommended to avoid potential confusion. - ## Allowed Variable Names Variable names must begin with a letter (A-Z or a-z), underscore, or a subset of Unicode code From d00deb7ce962ae76f99e93b4ce97f6a1c4e27f18 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Tue, 22 Aug 2017 09:12:06 +0200 Subject: [PATCH 109/324] add RowVectors to to list of types included in sparse concatinations (#23297) --- base/sparse/sparsevector.jl | 8 ++++---- test/arrayops.jl | 4 ++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/base/sparse/sparsevector.jl b/base/sparse/sparsevector.jl index 09bb230524f9d..86784a8bca367 100644 --- a/base/sparse/sparsevector.jl +++ b/base/sparse/sparsevector.jl @@ -955,7 +955,7 @@ vcat(X::Union{Vector,SparseVector}...) = vcat(map(sparse, X)...) # TODO: A definition similar to the third exists in base/linalg/bidiag.jl. These definitions # should be consolidated in a more appropriate location, e.g. base/linalg/special.jl. -const _SparseArrays = Union{SparseVector, SparseMatrixCSC} +const _SparseArrays = Union{SparseVector, SparseMatrixCSC, RowVector{<:Any, <:SparseVector}} const _SpecialArrays = Union{Diagonal, Bidiagonal, Tridiagonal, SymTridiagonal} const _SparseConcatArrays = Union{_SpecialArrays, _SparseArrays} @@ -970,9 +970,9 @@ const _Triangular_DenseArrays{T,A<:Matrix} = Base.LinAlg.AbstractTriangular{T,A} const _Annotated_DenseArrays = Union{_Triangular_DenseArrays, _Symmetric_DenseArrays, _Hermitian_DenseArrays} const _Annotated_Typed_DenseArrays{T} = Union{_Triangular_DenseArrays{T}, _Symmetric_DenseArrays{T}, _Hermitian_DenseArrays{T}} -const _SparseConcatGroup = Union{Vector, Matrix, _SparseConcatArrays, _Annotated_SparseConcatArrays, _Annotated_DenseArrays} -const _DenseConcatGroup = Union{Vector, Matrix, _Annotated_DenseArrays} -const _TypedDenseConcatGroup{T} = Union{Vector{T}, Matrix{T}, _Annotated_Typed_DenseArrays{T}} +const _SparseConcatGroup = Union{Vector, RowVector{<:Any, <:Vector}, Matrix, _SparseConcatArrays, _Annotated_SparseConcatArrays, _Annotated_DenseArrays} +const _DenseConcatGroup = Union{Vector, RowVector{<:Any, <:Vector}, Matrix, _Annotated_DenseArrays} +const _TypedDenseConcatGroup{T} = Union{Vector{T}, RowVector{T,Vector{T}}, Matrix{T}, _Annotated_Typed_DenseArrays{T}} # Concatenations involving un/annotated sparse/special matrices/vectors should yield sparse arrays function cat(catdims, Xin::_SparseConcatGroup...) diff --git a/test/arrayops.jl b/test/arrayops.jl index 776e73fab5e9b..659a42502b12a 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -2020,6 +2020,10 @@ end # module AutoRetType @test isa(hvcat((2,), densearray, densearray), Array) @test isa(cat((1,2), densearray, densearray), Array) end + @test isa([[1,2,3]'; [1,2,3]'], Matrix{Int}) + @test isa([[1,2,3]' [1,2,3]'], RowVector{Int, Vector{Int}}) + @test isa([Any[1.0, 2]'; Any[2.0, 2]'], Matrix{Any}) + @test isa([Any[1.0, 2]' Any[2.0, 2']'], RowVector{Any, Vector{Any}}) # Test that concatenations of heterogeneous Matrix-Vector pairs yield dense matrices @test isa(hcat(densemat, densevec), Array) @test isa(hcat(densevec, densemat), Array) From bc66f023f2c3e710da31e702006eaa6924020110 Mon Sep 17 00:00:00 2001 From: Christopher Rackauckas Date: Tue, 22 Aug 2017 07:06:46 -0700 Subject: [PATCH 110/324] det(I) (#23364) * det(I) The determinant for the UniformScaling operator was missing. * Update uniformscaling.jl * Update uniformscaling.jl * Update uniformscaling.jl * Update uniformscaling.jl * Update uniformscaling.jl * Update uniformscaling.jl * Update uniformscaling.jl * Add UniformScaling det tests * Update uniformscaling.jl * Use a long form definition to keep line length down * update tests --- base/linalg/uniformscaling.jl | 10 ++++++++++ test/linalg/uniformscaling.jl | 9 +++++++++ 2 files changed, 19 insertions(+) diff --git a/base/linalg/uniformscaling.jl b/base/linalg/uniformscaling.jl index c1e2b86da06de..9273659baadbc 100644 --- a/base/linalg/uniformscaling.jl +++ b/base/linalg/uniformscaling.jl @@ -168,6 +168,16 @@ end inv(J::UniformScaling) = UniformScaling(inv(J.λ)) norm(J::UniformScaling, p::Real=2) = abs(J.λ) +function det(J::UniformScaling{T}) where T + if isone(J.λ) + one(T) + elseif iszero(J.λ) + zero(T) + else + throw(ArgumentError("Determinant of UniformScaling is only well-defined when λ = 0 or 1.")) + end +end + *(J1::UniformScaling, J2::UniformScaling) = UniformScaling(J1.λ*J2.λ) *(B::BitArray{2}, J::UniformScaling) = *(Array(B), J::UniformScaling) *(J::UniformScaling, B::BitArray{2}) = *(J::UniformScaling, Array(B)) diff --git a/test/linalg/uniformscaling.jl b/test/linalg/uniformscaling.jl index 9bdde7b7470e8..e745e3ac1f441 100644 --- a/test/linalg/uniformscaling.jl +++ b/test/linalg/uniformscaling.jl @@ -43,6 +43,15 @@ end @test UniformScaling(α)./α == UniformScaling(1.0) end +@testset "det and logdet" begin + @test det(I) === 1 + @test det(1.0I) === 1.0 + @test det(0I) === 0 + @test det(0.0I) === 0.0 + @test logdet(I) == 0 + @test_throws ArgumentError det(2I) +end + @test copy(UniformScaling(one(Float64))) == UniformScaling(one(Float64)) @test sprint(show,UniformScaling(one(Complex128))) == "UniformScaling{Complex{Float64}}\n(1.0 + 0.0im)*I" @test sprint(show,UniformScaling(one(Float32))) == "UniformScaling{Float32}\n1.0*I" From ce402ecf4242e2f56353f76f3c6afab2385fd63e Mon Sep 17 00:00:00 2001 From: Harmen Stoppels Date: Tue, 22 Aug 2017 17:13:03 +0200 Subject: [PATCH 111/324] Add a BLAS wrapper for axpby (#23291) * Wrap axpby as well * Update method description for noncommutative operators * * Use Ref over Ptr + line length * Add generic axpby! with tests --- base/linalg/blas.jl | 49 ++++++++++++++++++++++++++++++++++++++++++ base/linalg/generic.jl | 10 +++++++++ base/linalg/linalg.jl | 1 + test/linalg/blas.jl | 7 ++++-- test/linalg/generic.jl | 20 +++++++++++------ 5 files changed, 78 insertions(+), 9 deletions(-) diff --git a/base/linalg/blas.jl b/base/linalg/blas.jl index 7da0bba72cb4b..88cbfab64b95a 100644 --- a/base/linalg/blas.jl +++ b/base/linalg/blas.jl @@ -463,6 +463,55 @@ function axpy!(alpha::Number, x::Array{T}, rx::Union{UnitRange{Ti},Range{Ti}}, y end +""" + axpby!(a, X, b, Y) + +Overwrite `Y` with `X*a + Y*b`, where `a` and `b` are scalars. Return `Y`. + +# Examples +```jldoctest +julia> x = [1., 2, 3]; + +julia> y = [4., 5, 6]; + +julia> Base.BLAS.axpby!(2., x, 3., y) +3-element Array{Float64,1}: +14.0 +19.0 +24.0 +``` +""" +function axpby! end + +for (fname, elty) in ((:daxpby_,:Float64), (:saxpby_,:Float32), + (:zaxpby_,:Complex128), (:caxpby_,:Complex64)) + @eval begin + # SUBROUTINE DAXPBY(N,DA,DX,INCX,DB,DY,INCY) + # DY <- DA*DX + DB*DY + #* .. Scalar Arguments .. + # DOUBLE PRECISION DA,DB + # INTEGER INCX,INCY,N + #* .. Array Arguments .. + # DOUBLE PRECISION DX(*),DY(*) + function axpby!(n::Integer, alpha::($elty), dx::Union{Ptr{$elty}, + DenseArray{$elty}}, incx::Integer, beta::($elty), + dy::Union{Ptr{$elty}, DenseArray{$elty}}, incy::Integer) + ccall((@blasfunc($fname), libblas), Void, (Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, + Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}), + n, alpha, dx, incx, beta, dy, incy) + dy + end + end +end + +function axpby!(alpha::Number, x::Union{DenseArray{T},StridedVector{T}}, beta::Number, y::Union{DenseArray{T},StridedVector{T}}) where T<:BlasFloat + if length(x) != length(y) + throw(DimensionMismatch("x has length $(length(x)), but y has length $(length(y))")) + end + axpby!(length(x), convert(T,alpha), pointer(x), stride(x, 1), convert(T,beta), pointer(y), stride(y, 1)) + y +end + ## iamax for (fname, elty) in ((:idamax_,:Float64), (:isamax_,:Float32), diff --git a/base/linalg/generic.jl b/base/linalg/generic.jl index 4af5f66639381..bfb3f33bb6d5a 100644 --- a/base/linalg/generic.jl +++ b/base/linalg/generic.jl @@ -1169,6 +1169,16 @@ function axpy!(α, x::AbstractArray, rx::AbstractArray{<:Integer}, y::AbstractAr y end +function axpby!(α, x::AbstractArray, β, y::AbstractArray) + if _length(x) != _length(y) + throw(DimensionMismatch("x has length $(_length(x)), but y has length $(_length(y))")) + end + for (IX, IY) in zip(eachindex(x), eachindex(y)) + @inbounds y[IY] = x[IX]*α + y[IY]*β + end + y +end + # Elementary reflection similar to LAPACK. The reflector is not Hermitian but # ensures that tridiagonalization of Hermitian matrices become real. See lawn72 diff --git a/base/linalg/linalg.jl b/base/linalg/linalg.jl index 52228e1910d60..0f28267729635 100644 --- a/base/linalg/linalg.jl +++ b/base/linalg/linalg.jl @@ -53,6 +53,7 @@ export # Functions axpy!, + axpby!, bkfact, bkfact!, chol, diff --git a/test/linalg/blas.jl b/test/linalg/blas.jl index 56ebdde4ab6ff..1c637a10aac0b 100644 --- a/test/linalg/blas.jl +++ b/test/linalg/blas.jl @@ -74,13 +74,16 @@ srand(100) @test BLAS.iamax(z) == indmax(map(x -> abs(real(x)) + abs(imag(x)), z)) end end - @testset "axpy" begin + @testset "axp(b)y" begin if elty <: Real x1 = convert(Vector{elty}, randn(n)) x2 = convert(Vector{elty}, randn(n)) α = rand(elty) - @test BLAS.axpy!(α,copy(x1),copy(x2)) ≈ x2 + α*x1 + β = rand(elty) + @test BLAS.axpy!(α,copy(x1),copy(x2)) ≈ α*x1 + x2 + @test BLAS.axpby!(α,copy(x1),β,copy(x2)) ≈ α*x1 + β*x2 @test_throws DimensionMismatch BLAS.axpy!(α, copy(x1), rand(elty, n + 1)) + @test_throws DimensionMismatch BLAS.axpby!(α, copy(x1), β, rand(elty, n + 1)) @test_throws DimensionMismatch BLAS.axpy!(α, copy(x1), 1:div(n,2), copy(x2), 1:n) @test_throws ArgumentError BLAS.axpy!(α, copy(x1), 0:div(n,2), copy(x2), 1:(div(n, 2) + 1)) @test_throws ArgumentError BLAS.axpy!(α, copy(x1), 1:div(n,2), copy(x2), 0:(div(n, 2) - 1)) diff --git a/test/linalg/generic.jl b/test/linalg/generic.jl index c6a0115b65697..aa340bc112947 100644 --- a/test/linalg/generic.jl +++ b/test/linalg/generic.jl @@ -160,8 +160,9 @@ end @testset "generic axpy" begin x = ['a','b','c','d','e'] y = ['a','b','c','d','e'] - α = 'f' + α, β = 'f', 'g' @test_throws DimensionMismatch Base.LinAlg.axpy!(α,x,['g']) + @test_throws DimensionMismatch Base.LinAlg.axpby!(α,x,β,['g']) @test_throws BoundsError Base.LinAlg.axpy!(α,x,collect(-1:5),y,collect(1:7)) @test_throws BoundsError Base.LinAlg.axpy!(α,x,collect(1:7),y,collect(-1:5)) @test_throws BoundsError Base.LinAlg.axpy!(α,x,collect(1:7),y,collect(1:7)) @@ -276,12 +277,17 @@ end @test norm(x, 3) ≈ cbrt(sqrt(125)+125) end -@testset "LinAlg.axpy! for element type without commutative multiplication" begin - α = ones(Int, 2, 2) - x = fill([1 0; 1 1], 3) - y = fill(zeros(Int, 2, 2), 3) - @test LinAlg.axpy!(α, x, deepcopy(y)) == x .* Matrix{Int}[α] - @test LinAlg.axpy!(α, x, deepcopy(y)) != Matrix{Int}[α] .* x +@testset "LinAlg.axp(b)y! for element type without commutative multiplication" begin + α = [1 2; 3 4] + β = [5 6; 7 8] + x = fill([ 9 10; 11 12], 3) + y = fill([13 14; 15 16], 3) + axpy = LinAlg.axpy!(α, x, deepcopy(y)) + axpby = LinAlg.axpby!(α, x, β, deepcopy(y)) + @test axpy == x .* [α] .+ y + @test axpy != [α] .* x .+ y + @test axpby == x .* [α] .+ y .* [β] + @test axpby != [α] .* x .+ [β] .* y end @testset "LinAlg.axpy! for x and y of different dimensions" begin From 1db7b8f08cf04ea01aebc1b9c941b7f16d48a045 Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Tue, 22 Aug 2017 18:53:18 +0200 Subject: [PATCH 112/324] deprecate diagm(A::SparseMatrixCSC) in favor of spdiagm(sparsevec(A)) (#23341) * deprecate diagm(A::SparseMatrixCSC) in favor of diagm(sparsevec(A)) * use spdiagm instead --- NEWS.md | 3 +++ base/deprecated.jl | 3 +++ base/sparse/sparsematrix.jl | 43 ------------------------------------- test/sparse/sparse.jl | 10 ++++----- 4 files changed, 11 insertions(+), 48 deletions(-) diff --git a/NEWS.md b/NEWS.md index 3d8d2927676a4..d5fb7f049c57e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -326,6 +326,9 @@ Deprecated or removed * `Base.SparseArrays.SpDiagIterator` has been removed ([#23261]). + * `diagm(A::SparseMatrixCSC)` has been deprecated in favor of + `spdiagm(sparsevec(A))` ([#23341]). + Command-line option changes --------------------------- diff --git a/base/deprecated.jl b/base/deprecated.jl index ce59333032c4c..fa1bf8a1c14e9 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1705,6 +1705,9 @@ export hex2num # issue #17886 # deprecations for filter[!] with 2-arg functions are in associative.jl +# PR 23341 +@deprecate diagm(A::SparseMatrixCSC) spdiagm(sparsevec(A)) + # END 0.7 deprecations # BEGIN 1.0 deprecations diff --git a/base/sparse/sparsematrix.jl b/base/sparse/sparsematrix.jl index 4bd62a54a25b8..e58530b4fb115 100644 --- a/base/sparse/sparsematrix.jl +++ b/base/sparse/sparsematrix.jl @@ -3413,49 +3413,6 @@ function trace(A::SparseMatrixCSC{Tv}) where Tv return s end -function diagm(v::SparseMatrixCSC{Tv,Ti}) where {Tv,Ti} - if size(v,1) != 1 && size(v,2) != 1 - throw(DimensionMismatch("input should be nx1 or 1xn")) - end - - n = length(v) - numnz = nnz(v) - colptr = Vector{Ti}(n+1) - rowval = Vector{Ti}(numnz) - nzval = Vector{Tv}(numnz) - - if size(v,1) == 1 - copy!(colptr, 1, v.colptr, 1, n+1) - ptr = 1 - for col = 1:n - if colptr[col] != colptr[col+1] - rowval[ptr] = col - nzval[ptr] = v.nzval[ptr] - ptr += 1 - end - end - else - copy!(rowval, 1, v.rowval, 1, numnz) - copy!(nzval, 1, v.nzval, 1, numnz) - colptr[1] = 1 - ptr = 1 - col = 1 - while col <= n && ptr <= numnz - while rowval[ptr] > col - colptr[col+1] = colptr[col] - col += 1 - end - colptr[col+1] = colptr[col] + 1 - ptr += 1 - col += 1 - end - if col <= n - colptr[(col+1):(n+1)] = colptr[col] - end - end - - return SparseMatrixCSC(n, n, colptr, rowval, nzval) -end # Sort all the indices in each column of a CSC sparse matrix # sortSparseMatrixCSC!(A, sortindices = :sortcols) # Sort each column with sort() diff --git a/test/sparse/sparse.jl b/test/sparse/sparse.jl index 8e43393ad1e61..30e62f354afab 100644 --- a/test/sparse/sparse.jl +++ b/test/sparse/sparse.jl @@ -1316,11 +1316,11 @@ end @test trace(speye(5)) == 5 end -@testset "diagm on a matrix" begin - @test_throws DimensionMismatch diagm(sparse(ones(5,2))) - @test_throws DimensionMismatch diagm(sparse(ones(2,5))) - @test diagm(sparse(ones(1,5))) == speye(5) - @test diagm(sparse(ones(5,1))) == speye(5) +@testset "spdiagm" begin + v = sprand(10, 0.4) + @test spdiagm(v)::SparseMatrixCSC == diagm(Vector(v)) + @test spdiagm(sparse(ones(5)))::SparseMatrixCSC == speye(5) + @test spdiagm(sparse(zeros(5)))::SparseMatrixCSC == spzeros(5,5) end @testset "diag" begin From 685e7c912b19d2f5098d8c299319b0f3f3fda5eb Mon Sep 17 00:00:00 2001 From: Mus M Date: Thu, 17 Aug 2017 15:17:51 -0400 Subject: [PATCH 113/324] Use a StringVector buffer for strftime --- base/libc.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/libc.jl b/base/libc.jl index 810cda11e448f..585d3c94a186d 100644 --- a/base/libc.jl +++ b/base/libc.jl @@ -170,13 +170,13 @@ library. strftime(t) = strftime("%c", t) strftime(fmt::AbstractString, t::Real) = strftime(fmt, TmStruct(t)) function strftime(fmt::AbstractString, tm::TmStruct) - timestr = Vector{UInt8}(128) + timestr = Base.StringVector(128) n = ccall(:strftime, Int, (Ptr{UInt8}, Int, Cstring, Ptr{TmStruct}), timestr, length(timestr), fmt, &tm) if n == 0 return "" end - return String(timestr[1:n]) + return String(resize!(timestr,n)) end """ From eeecf0503765c40f989efdb2b142ed2474560c6b Mon Sep 17 00:00:00 2001 From: Andreas Noack Date: Tue, 22 Aug 2017 14:53:24 -0400 Subject: [PATCH 114/324] Fix bug introduced in #23285 for means along dimensions of empty arrays (#23385) --- base/statistics.jl | 2 +- test/statistics.jl | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/base/statistics.jl b/base/statistics.jl index e6d5748423782..b15669fc9bf90 100644 --- a/base/statistics.jl +++ b/base/statistics.jl @@ -59,7 +59,7 @@ julia> mean!([1. 1.], v) """ function mean!(R::AbstractArray, A::AbstractArray) sum!(R, A; init=true) - scale!(R, _length(R) // max(1, _length(A))) + scale!(R, max(1, _length(R)) // _length(A)) return R end diff --git a/test/statistics.jl b/test/statistics.jl index 1e05780a3ca40..2962853b42eb9 100644 --- a/test/statistics.jl +++ b/test/statistics.jl @@ -440,3 +440,13 @@ end @test (A'A - size(A, 1)*Base.mean(A, 1)'*Base.mean(A, 1))/4 == cov(A) end +@testset "Mean along dimension of empty array" begin + a0 = zeros(0) + a00 = zeros(0, 0) + a01 = zeros(0, 1) + a10 = zeros(1, 0) + @test isequal(mean(a0, 1) , fill(NaN, 1)) + @test isequal(mean(a00, (1, 2)), fill(NaN, 1, 1)) + @test isequal(mean(a01, 1) , fill(NaN, 1, 1)) + @test isequal(mean(a10, 2) , fill(NaN, 1, 1)) +end From f7d16c68676d9eb0a51eba0389e767e32b0dd0c4 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Tue, 22 Aug 2017 20:54:12 +0200 Subject: [PATCH 115/324] fix repl completions on unions and Any (#23345) --- base/repl/REPLCompletions.jl | 12 +++++++----- test/replcompletions.jl | 8 +++++++- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/base/repl/REPLCompletions.jl b/base/repl/REPLCompletions.jl index e680425e94469..516846570ffd6 100644 --- a/base/repl/REPLCompletions.jl +++ b/base/repl/REPLCompletions.jl @@ -71,11 +71,13 @@ function complete_symbol(sym, ffunc) end else # Looking for a member of a type - fields = fieldnames(t) - for field in fields - s = string(field) - if startswith(s, name) - push!(suggestions, s) + if t isa DataType && t != Any + fields = fieldnames(t) + for field in fields + s = string(field) + if startswith(s, name) + push!(suggestions, s) + end end end end diff --git a/test/replcompletions.jl b/test/replcompletions.jl index 1c22d90c4efe1..ba9fcf35fabb8 100644 --- a/test/replcompletions.jl +++ b/test/replcompletions.jl @@ -51,7 +51,8 @@ ex = quote test5(x::Float64) = pass const a=x->x test6()=[a, a] - + test7() = rand() > 0.5 ? 1 : 1.0 + test8() = Any[1][1] kwtest(; x=1, y=2, w...) = pass array = [1, 1] @@ -172,6 +173,11 @@ s = "using Base\nusi" c,r = test_complete(s) @test "using" in c +# issue 23292 +@test_nowarn test_complete("test7().") +c,r = test_complete("test8().") +@test isempty(c) + # inexistent completion inside a string s = "Pkg.add(\"lol" c,r,res = test_complete(s) From eba7fa61ecd7087a8181d1bfb789fa271da702d1 Mon Sep 17 00:00:00 2001 From: kshyatt Date: Tue, 22 Aug 2017 12:25:20 -0700 Subject: [PATCH 116/324] Last docstrings for LibGit2 GitIndex --- base/libgit2/index.jl | 38 ++++++++++++++++++++++++++++++++++++++ doc/src/devdocs/libgit2.md | 3 +++ 2 files changed, 41 insertions(+) diff --git a/base/libgit2/index.jl b/base/libgit2/index.jl index e75ecf7c14185..2ca285ec788b1 100644 --- a/base/libgit2/index.jl +++ b/base/libgit2/index.jl @@ -74,6 +74,28 @@ end read_tree!(idx::GitIndex, hash::AbstractGitHash) = read_tree!(idx, GitTree(repository(idx), hash)) +""" + add!(repo::GitRepo, files::AbstractString...; flags::Cuint = Consts.INDEX_ADD_DEFAULT) + add!(idx::GitIndex, files::AbstractString...; flags::Cuint = Consts.INDEX_ADD_DEFAULT) + +Add all the files with paths specified by `files` to the index `idx` (or the index +of the `repo`). If the file already exists, the index entry will be updated. +If the file does not exist already, it will be newly added into the index. +`files` may contain glob patterns which will be expanded and any matching files will +be added (unless `INDEX_ADD_DISABLE_PATHSPEC_MATCH` is set, see below). +If a file has been ignored (in `.gitignore` or in the config), it *will not* be +added, *unless* it is already being tracked in the index, in which case it *will* be +updated. The keyword argument `flags` is a set of bit-flags which control the behavior +with respect to ignored files: + * `Consts.INDEX_ADD_DEFAULT` - default, described above. + * `Consts.INDEX_ADD_FORCE` - disregard the existing ignore rules and force addition of + the file to the index even if it is already ignored. + * `Consts.INDEX_ADD_CHECK_PATHSPEC` - cannot be used at the same time as `INDEX_ADD_FORCE`. + Check that each file in `files` which exists on disk is not in the ignore list. If one + of the files *is* ignored, the function will return `EINVALIDSPEC`. + * `Consts.INDEX_ADD_DISABLE_PATHSPEC_MATCH` - turn off glob matching, and only add files + to the index which exactly match the paths specified in `files`. +""" function add!(idx::GitIndex, files::AbstractString...; flags::Cuint = Consts.INDEX_ADD_DEFAULT) @check ccall((:git_index_add_all, :libgit2), Cint, @@ -81,12 +103,28 @@ function add!(idx::GitIndex, files::AbstractString...; idx.ptr, collect(files), flags, C_NULL, C_NULL) end +""" + update!(repo::GitRepo, files::AbstractString...) + update!(idx::GitIndex, files::AbstractString...) + +Update all the files with paths specified by `files` in the index `idx` (or the index +of the `repo`). Match the state of each file in the index with the current state on +disk, removing it if it has been removed on disk, or updating its entry in the object +database. +""" function update!(idx::GitIndex, files::AbstractString...) @check ccall((:git_index_update_all, :libgit2), Cint, (Ptr{Void}, Ptr{StrArrayStruct}, Ptr{Void}, Ptr{Void}), idx.ptr, collect(files), C_NULL, C_NULL) end +""" + remove!(repo::GitRepo, files::AbstractString...) + remove!(idx::GitIndex, files::AbstractString...) + +Remove all the files with paths specified by `files` in the index `idx` (or the index +of the `repo`). +""" function remove!(idx::GitIndex, files::AbstractString...) @check ccall((:git_index_remove_all, :libgit2), Cint, (Ptr{Void}, Ptr{StrArrayStruct}, Ptr{Void}, Ptr{Void}), diff --git a/doc/src/devdocs/libgit2.md b/doc/src/devdocs/libgit2.md index fb86054d00811..dc7ca8b54aef3 100644 --- a/doc/src/devdocs/libgit2.md +++ b/doc/src/devdocs/libgit2.md @@ -50,6 +50,7 @@ Base.LibGit2.StatusOptions Base.LibGit2.StrArrayStruct Base.LibGit2.TimeStruct Base.LibGit2.UserPasswordCredentials +Base.LibGit2.add! Base.LibGit2.add_fetch! Base.LibGit2.add_push! Base.LibGit2.addblob! @@ -104,6 +105,7 @@ Base.LibGit2.rebase! Base.LibGit2.ref_list Base.LibGit2.reftype Base.LibGit2.remotes +Base.LibGit2.remove! Base.LibGit2.reset! Base.LibGit2.restore Base.LibGit2.revcount @@ -118,6 +120,7 @@ Base.LibGit2.tag_list Base.LibGit2.target Base.LibGit2.treewalk Base.LibGit2.upstream +Base.LibGit2.update! Base.LibGit2.url Base.LibGit2.with Base.LibGit2.workdir From 49c44dedfe0af10f0e8a7372c800ff666c4914c1 Mon Sep 17 00:00:00 2001 From: Patrick Kofod Mogensen Date: Tue, 22 Aug 2017 22:06:33 +0200 Subject: [PATCH 117/324] Add Julia port of Openlibm asin (#23088) * Add asin tests. * Add Julia implementation of asin. * Change asin_p and asin_q for arc_p and arc_q for future use in acos. --- base/fastmath.jl | 4 +- base/math.jl | 4 +- base/special/trig.jl | 112 +++++++++++++++++++++++++++++++++++++++++-- test/math.jl | 20 ++++++++ 4 files changed, 135 insertions(+), 5 deletions(-) diff --git a/base/fastmath.jl b/base/fastmath.jl index 0256d0e8e0761..b9c4f7db748b7 100644 --- a/base/fastmath.jl +++ b/base/fastmath.jl @@ -270,7 +270,7 @@ sqrt_fast(x::FloatTypes) = sqrt_llvm(x) const libm = Base.libm_name -for f in (:acos, :acosh, :asin, :asinh, :atan, :atanh, :cbrt, :cos, +for f in (:acos, :acosh, :asinh, :atan, :atanh, :cbrt, :cos, :cosh, :exp2, :expm1, :lgamma, :log10, :log1p, :log2, :log, :sin, :sinh, :tan, :tanh) f_fast = fast_op[f] @@ -292,6 +292,8 @@ atan2_fast(x::Float32, y::Float32) = atan2_fast(x::Float64, y::Float64) = ccall(("atan2",libm), Float64, (Float64,Float64), x, y) +asin_fast(x::FloatTypes) = asin(x) + # explicit implementations # FIXME: Change to `ccall((:sincos, libm))` when `Ref` calling convention can be diff --git a/base/math.jl b/base/math.jl index b90caf4574315..5cb9cd0f09710 100644 --- a/base/math.jl +++ b/base/math.jl @@ -436,7 +436,7 @@ julia> log1p(0) ``` """ log1p(x) -for f in (:sin, :cos, :tan, :asin, :acos, :acosh, :atanh, :log, :log2, :log10, +for f in (:sin, :cos, :tan, :acos, :acosh, :atanh, :log, :log2, :log10, :lgamma, :log1p) @eval begin @inline ($f)(x::Float64) = nan_dom_err(ccall(($(string(f)), libm), Float64, (Float64,), x), x) @@ -445,6 +445,8 @@ for f in (:sin, :cos, :tan, :asin, :acos, :acosh, :atanh, :log, :log2, :log10, end end +@inline asin(x::Real) = asin(float(x)) + """ sincos(x) diff --git a/base/special/trig.jl b/base/special/trig.jl index 6c1c81c300d93..2cd837fe3293b 100644 --- a/base/special/trig.jl +++ b/base/special/trig.jl @@ -9,9 +9,10 @@ struct DoubleFloat32 hi::Float64 end -# *_kernel functions are only valid for |x| < pi/4 = 0.7854 -# translated from openlibm code: k_sin.c, k_cos.c, k_sinf.c, k_cosf.c -# which are made available under the following licence: +# sin_kernel and cos_kernel functions are only valid for |x| < pi/4 = 0.7854 +# translated from openlibm code: k_sin.c, k_cos.c, k_sinf.c, k_cosf.c. +# asin functions are based on openlibm code: e_asin.c, e_asinf.c. The above +# functions are made available under the following licence: ## Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. ## @@ -80,6 +81,111 @@ end sin_kernel(x::Real) = sin(x) cos_kernel(x::Real) = cos(x) +# Inverse trigonometric functions + +# asin methods +ASIN_X_MIN_THRESHOLD(::Type{Float32}) = 2.0f0^-12 +ASIN_X_MIN_THRESHOLD(::Type{Float64}) = sqrt(eps(Float64)) + +arc_p(t::Float64) = + t*@horner(t, + 1.66666666666666657415e-01, + -3.25565818622400915405e-01, + 2.01212532134862925881e-01, + -4.00555345006794114027e-02, + 7.91534994289814532176e-04, + 3.47933107596021167570e-05) + +arc_q(t::Float64) = + @horner(t, + 1.0, + -2.40339491173441421878e+00, + 2.02094576023350569471e+00, + -6.88283971605453293030e-01, + 7.70381505559019352791e-02) + +arc_p(t::Float32) = + t*@horner(t, + 1.6666586697f-01, + -4.2743422091f-02, + -8.6563630030f-03) + +arc_q(t::Float32) = @horner(t, 1.0f0, -7.0662963390f-01) + +@inline function asin_kernel(t::Float64, x::Float64) + pio2_lo = 6.12323399573676603587e-17 + s = sqrt_llvm(t) + p = arc_p(t) # numerator polynomial + q = arc_q(t) # denominator polynomial + if abs(x) >= 0.975 # |x| > 0.975 + Rx = p/q + return flipsign(pi/2 - (2.0*(s + s*Rx) - pio2_lo), x) + else + s0 = reinterpret(Float64, (reinterpret(UInt64, s) >> 32) << 32) + c = (t - s0*s0)/(s + s0) + Rx = p/q + p = 2.0*s*Rx - (pio2_lo - 2.0*c) + q = pi/4 - 2.0*s0 + return flipsign(pi/4 - (p-q), x) + end +end +@inline function asin_kernel(t::Float32, x::Float32) + s = sqrt_llvm(Float64(t)) + p = arc_p(t) # numerator polynomial + q = arc_q(t) # denominator polynomial + Rx = p/q # rational approximation + flipsign(Float32(pi/2 - 2*(s + s*Rx)), x) +end + +@noinline asin_domain_error(x) = throw(DomainError(x, "asin(x) is not defined for |x|>1.")) +function asin(x::T) where T<:Union{Float32, Float64} + # Method : + # Since asin(x) = x + x^3/6 + x^5*3/40 + x^7*15/336 + ... + # we approximate asin(x) on [0,0.5] by + # asin(x) = x + x*x^2*R(x^2) + # where + # R(x^2) is a rational approximation of (asin(x)-x)/x^3 + # and its remez error is bounded by + # |(asin(x)-x)/x^3 - R(x^2)| < 2^(-58.75) + # + # For x in [0.5,1] + # asin(x) = pi/2-2*asin(sqrt((1-x)/2)) + # Let y = (1-x), z = y/2, s := sqrt(z), and pio2_hi+pio2_lo=pi/2; + # then for x>0.98 + # asin(x) = pi/2 - 2*(s+s*z*R(z)) + # = pio2_hi - (2*(s+s*z*R(z)) - pio2_lo) + # For x<=0.98, let pio4_hi = pio2_hi/2, then + # f = hi part of s; + # c = sqrt(z) - f = (z-f*f)/(s+f) ...f+c=sqrt(z) + # and + # asin(x) = pi/2 - 2*(s+s*z*R(z)) + # = pio4_hi+(pio4-2s)-(2s*z*R(z)-pio2_lo) + # = pio4_hi+(pio4-2f)-(2s*z*R(z)-(pio2_lo+2c)) + # + # Special cases: + # if |x|>1, throw DomainError + absx = abs(x) + if absx >= T(1.0) # |x|>= 1 + if absx == T(1.0) + return flipsign(T(pi)/2, x) + end + asin_domain_error(x) + elseif absx < T(1.0)/2 + # if |x| sufficiently small, |x| is a good approximation + if absx < ASIN_X_MIN_THRESHOLD(T) + return x + end + # else if |x|<0.5 we use a rational approximation R(x)=p(x)/q(x) such that + # tan(x) ≈ x+x*R(x) + x² = x*x + Rx = arc_p(x²)/arc_q(x²) # rational approximation + return muladd(x, Rx, x) + end + # else 1/2 <= |x| < 1 + t = (T(1.0) - absx)/2 + return asin_kernel(t, x) +end + # multiply in extended precision function mulpi_ext(x::Float64) m = 3.141592653589793 diff --git a/test/math.jl b/test/math.jl index cebe2fa0ea4e2..deba71da7dc5b 100644 --- a/test/math.jl +++ b/test/math.jl @@ -683,3 +683,23 @@ let x = 2.0 @test exp2(Float22716(x)) === 2^x @test exp10(Float22716(x)) === 10^x end + +@testset "asin #23088" begin + for T in (Float32, Float64) + @test asin(zero(T)) === zero(T) + @test asin(-zero(T)) === -zero(T) + @test asin(nextfloat(zero(T))) === nextfloat(zero(T)) + @test asin(prevfloat(zero(T))) === prevfloat(zero(T)) + @test asin(one(T)) === T(pi)/2 + @test asin(-one(T)) === -T(pi)/2 + for x in (0.45, 0.6, 0.98) + by = T(asin(big(x))) + @test abs(asin(T(x)) - by)/eps(by) <= one(T) + bym = T(asin(big(-x))) + @test abs(asin(T(-x)) - bym)/eps(bym) <= one(T) + end + @test_throws DomainError asin(-T(Inf)) + @test_throws DomainError asin(T(Inf)) + @test asin(T(NaN)) === T(NaN) + end +end From 086eca171b3f238996da5e4a51f237e4b670eec4 Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Tue, 22 Aug 2017 17:21:33 -0700 Subject: [PATCH 118/324] Convert special tests to testsets (#23111) * Convert special tests to testsets * Fix spacing. Remove extraneous testsets. --- test/linalg/special.jl | 191 ++++++++++++++++++++--------------------- 1 file changed, 94 insertions(+), 97 deletions(-) diff --git a/test/linalg/special.jl b/test/linalg/special.jl index e65a7c14309b8..c4f6e180a1f4f 100644 --- a/test/linalg/special.jl +++ b/test/linalg/special.jl @@ -1,132 +1,129 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license using Base.Test -debug = false n= 10 #Size of matrix to test srand(1) -debug && println("Test interconversion between special matrix types") -let a=[1.0:n;] - A=Diagonal(a) - for newtype in [Diagonal, Bidiagonal, SymTridiagonal, Tridiagonal, Matrix] - debug && println("newtype is $(newtype)") +@testset "Interconversion between special matrix types" begin + a = [1.0:n;] + A = Diagonal(a) + @testset for newtype in [Diagonal, Bidiagonal, SymTridiagonal, Tridiagonal, Matrix] @test full(convert(newtype, A)) == full(A) - end + end - for isupper in (true, false) - debug && println("isupper is $(isupper)") - A=Bidiagonal(a, [1.0:n-1;], ifelse(isupper, :U, :L)) - for newtype in [Bidiagonal, Tridiagonal, Matrix] - debug && println("newtype is $(newtype)") + @testset for isupper in (true, false) + A = Bidiagonal(a, [1.0:n-1;], ifelse(isupper, :U, :L)) + for newtype in [Bidiagonal, Tridiagonal, Matrix] @test full(convert(newtype, A)) == full(A) @test full(newtype(A)) == full(A) - end - @test_throws ArgumentError convert(SymTridiagonal, A) - tritype = isupper ? UpperTriangular : LowerTriangular - @test full(tritype(A)) == full(A) + end + @test_throws ArgumentError convert(SymTridiagonal, A) + tritype = isupper ? UpperTriangular : LowerTriangular + @test full(tritype(A)) == full(A) - A=Bidiagonal(a, zeros(n-1), ifelse(isupper, :U, :L)) #morally Diagonal - for newtype in [Diagonal, Bidiagonal, SymTridiagonal, Tridiagonal, Matrix] - debug && println("newtype is $(newtype)") + A = Bidiagonal(a, zeros(n-1), ifelse(isupper, :U, :L)) #morally Diagonal + for newtype in [Diagonal, Bidiagonal, SymTridiagonal, Tridiagonal, Matrix] @test full(convert(newtype, A)) == full(A) @test full(newtype(A)) == full(A) - end - @test full(tritype(A)) == full(A) - end + end + @test full(tritype(A)) == full(A) + end - A = SymTridiagonal(a, [1.0:n-1;]) - for newtype in [Tridiagonal, Matrix] + A = SymTridiagonal(a, [1.0:n-1;]) + for newtype in [Tridiagonal, Matrix] @test full(convert(newtype, A)) == full(A) - end - for newtype in [Diagonal, Bidiagonal] + end + for newtype in [Diagonal, Bidiagonal] @test_throws ArgumentError convert(newtype,A) - end - A = SymTridiagonal(a, zeros(n-1)) - @test full(convert(Bidiagonal,A)) == full(A) + end + A = SymTridiagonal(a, zeros(n-1)) + @test full(convert(Bidiagonal,A)) == full(A) - A = Tridiagonal(zeros(n-1), [1.0:n;], zeros(n-1)) #morally Diagonal - for newtype in [Diagonal, Bidiagonal, SymTridiagonal, Matrix] + A = Tridiagonal(zeros(n-1), [1.0:n;], zeros(n-1)) #morally Diagonal + for newtype in [Diagonal, Bidiagonal, SymTridiagonal, Matrix] @test full(convert(newtype, A)) == full(A) - end - A = Tridiagonal(ones(n-1), [1.0:n;], ones(n-1)) #not morally Diagonal - for newtype in [SymTridiagonal, Matrix] + end + A = Tridiagonal(ones(n-1), [1.0:n;], ones(n-1)) #not morally Diagonal + for newtype in [SymTridiagonal, Matrix] @test full(convert(newtype, A)) == full(A) - end - for newtype in [Diagonal, Bidiagonal] - @test_throws ArgumentError convert(newtype,A) - end - A = Tridiagonal(zeros(n-1), [1.0:n;], ones(n-1)) #not morally Diagonal - @test full(convert(Bidiagonal, A)) == full(A) - A = UpperTriangular(Tridiagonal(zeros(n-1), [1.0:n;], ones(n-1))) - @test full(convert(Bidiagonal, A)) == full(A) - A = Tridiagonal(ones(n-1), [1.0:n;], zeros(n-1)) #not morally Diagonal - @test full(convert(Bidiagonal, A)) == full(A) - A = LowerTriangular(Tridiagonal(ones(n-1), [1.0:n;], zeros(n-1))) - @test full(convert(Bidiagonal, A)) == full(A) - @test_throws ArgumentError convert(SymTridiagonal,A) + end + for newtype in [Diagonal, Bidiagonal] + @test_throws ArgumentError convert(newtype,A) + end + A = Tridiagonal(zeros(n-1), [1.0:n;], ones(n-1)) #not morally Diagonal + @test full(convert(Bidiagonal, A)) == full(A) + A = UpperTriangular(Tridiagonal(zeros(n-1), [1.0:n;], ones(n-1))) + @test full(convert(Bidiagonal, A)) == full(A) + A = Tridiagonal(ones(n-1), [1.0:n;], zeros(n-1)) #not morally Diagonal + @test full(convert(Bidiagonal, A)) == full(A) + A = LowerTriangular(Tridiagonal(ones(n-1), [1.0:n;], zeros(n-1))) + @test full(convert(Bidiagonal, A)) == full(A) + @test_throws ArgumentError convert(SymTridiagonal,A) - A = LowerTriangular(full(Diagonal(a))) #morally Diagonal - for newtype in [Diagonal, Bidiagonal, SymTridiagonal, LowerTriangular, Matrix] - @test full(convert(newtype, A)) == full(A) - end - A = UpperTriangular(full(Diagonal(a))) #morally Diagonal - for newtype in [Diagonal, Bidiagonal, SymTridiagonal, UpperTriangular, Matrix] - @test full(convert(newtype, A)) == full(A) - end - A = UpperTriangular(triu(rand(n,n))) - for newtype in [Diagonal, Bidiagonal, Tridiagonal, SymTridiagonal] - @test_throws ArgumentError convert(newtype,A) - end + A = LowerTriangular(full(Diagonal(a))) #morally Diagonal + for newtype in [Diagonal, Bidiagonal, SymTridiagonal, LowerTriangular, Matrix] + @test full(convert(newtype, A)) == full(A) + end + A = UpperTriangular(full(Diagonal(a))) #morally Diagonal + for newtype in [Diagonal, Bidiagonal, SymTridiagonal, UpperTriangular, Matrix] + @test full(convert(newtype, A)) == full(A) + end + A = UpperTriangular(triu(rand(n,n))) + for newtype in [Diagonal, Bidiagonal, Tridiagonal, SymTridiagonal] + @test_throws ArgumentError convert(newtype,A) + end end -# Binary ops among special types -let a=[1.0:n;] - A=Diagonal(a) - Spectypes = [Diagonal, Bidiagonal, Tridiagonal, Matrix] - for (idx, type1) in enumerate(Spectypes) - for type2 in Spectypes +@testset "Binary ops among special types" begin + a=[1.0:n;] + A=Diagonal(a) + Spectypes = [Diagonal, Bidiagonal, Tridiagonal, Matrix] + for (idx, type1) in enumerate(Spectypes) + for type2 in Spectypes B = convert(type1,A) C = convert(type2,A) @test full(B + C) ≈ full(A + A) @test full(B - C) ≈ full(A - A) end - end - B = SymTridiagonal(a, ones(n-1)) - for Spectype in [Diagonal, Bidiagonal, Tridiagonal, Matrix] - @test full(B + convert(Spectype,A)) ≈ full(B + A) - @test full(convert(Spectype,A) + B) ≈ full(B + A) - @test full(B - convert(Spectype,A)) ≈ full(B - A) - @test full(convert(Spectype,A) - B) ≈ full(A - B) - end + end + B = SymTridiagonal(a, ones(n-1)) + for Spectype in [Diagonal, Bidiagonal, Tridiagonal, Matrix] + @test full(B + convert(Spectype,A)) ≈ full(B + A) + @test full(convert(Spectype,A) + B) ≈ full(B + A) + @test full(B - convert(Spectype,A)) ≈ full(B - A) + @test full(convert(Spectype,A) - B) ≈ full(A - B) + end - C = rand(n,n) - for TriType in [Base.LinAlg.UnitLowerTriangular, Base.LinAlg.UnitUpperTriangular, UpperTriangular, LowerTriangular] - D = TriType(C) - for Spectype in [Diagonal, Bidiagonal, Tridiagonal, Matrix] - @test full(D + convert(Spectype,A)) ≈ full(D + A) - @test full(convert(Spectype,A) + D) ≈ full(A + D) - @test full(D - convert(Spectype,A)) ≈ full(D - A) - @test full(convert(Spectype,A) - D) ≈ full(A - D) - end - end + C = rand(n,n) + for TriType in [Base.LinAlg.UnitLowerTriangular, Base.LinAlg.UnitUpperTriangular, UpperTriangular, LowerTriangular] + D = TriType(C) + for Spectype in [Diagonal, Bidiagonal, Tridiagonal, Matrix] + @test full(D + convert(Spectype,A)) ≈ full(D + A) + @test full(convert(Spectype,A) + D) ≈ full(A + D) + @test full(D - convert(Spectype,A)) ≈ full(D - A) + @test full(convert(Spectype,A) - D) ≈ full(A - D) + end + end end -#Triangular Types and QR -for typ in [UpperTriangular,LowerTriangular,Base.LinAlg.UnitUpperTriangular,Base.LinAlg.UnitLowerTriangular] - a = rand(n,n) - atri = typ(a) - b = rand(n,n) - qrb = qrfact(b,Val(true)) - @test Base.LinAlg.A_mul_Bc(atri,qrb[:Q]) ≈ full(atri) * qrb[:Q]' - @test Base.LinAlg.A_mul_Bc!(copy(atri),qrb[:Q]) ≈ full(atri) * qrb[:Q]' - qrb = qrfact(b,Val(false)) - @test Base.LinAlg.A_mul_Bc(atri,qrb[:Q]) ≈ full(atri) * qrb[:Q]' - @test Base.LinAlg.A_mul_Bc!(copy(atri),qrb[:Q]) ≈ full(atri) * qrb[:Q]' +@testset "Triangular Types and QR" begin + for typ in [UpperTriangular,LowerTriangular,Base.LinAlg.UnitUpperTriangular,Base.LinAlg.UnitLowerTriangular] + a = rand(n,n) + atri = typ(a) + b = rand(n,n) + qrb = qrfact(b,Val(true)) + @test Base.LinAlg.A_mul_Bc(atri,qrb[:Q]) ≈ full(atri) * qrb[:Q]' + @test Base.LinAlg.A_mul_Bc!(copy(atri),qrb[:Q]) ≈ full(atri) * qrb[:Q]' + qrb = qrfact(b,Val(false)) + @test Base.LinAlg.A_mul_Bc(atri,qrb[:Q]) ≈ full(atri) * qrb[:Q]' + @test Base.LinAlg.A_mul_Bc!(copy(atri),qrb[:Q]) ≈ full(atri) * qrb[:Q]' + end end -# Test that concatenations of combinations of special and other matrix types yield sparse arrays -let N = 4 +# should all yield sparse arrays +@testset "concatenations of combinations of special and other matrix types" begin + N = 4 # Test concatenating pairwise combinations of special matrices diagmat = Diagonal(ones(N)) bidiagmat = Bidiagonal(ones(N), ones(N-1), :U) @@ -169,7 +166,7 @@ end # TODO: As with the associated code, these tests should be moved to a more appropriate # location, particularly some future equivalent of base/linalg/special.jl dedicated to # intereactions between a broader set of matrix types -let +@testset "concatenations of annotated types" begin N = 4 # The tested annotation types testfull = Bool(parse(Int,(get(ENV, "JULIA_TESTFULL", "0")))) From 94336183a833c1f97e2e7b2942b6fb406de267e3 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Sun, 2 Jul 2017 00:09:30 -0600 Subject: [PATCH 119/324] 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)) From 7d0d3bcd25055585978cc3c6f20a5494b12ca77f Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 14 Aug 2017 10:51:12 +0200 Subject: [PATCH 120/324] add a list of common parametric method patterns to the manual this section will hopefully be helpful to new users both as motivational examples demonstrating the usefulness of the ideas behind dispatch as well as showing design patterns for more advanced users to refer to when developing generic library code Updated by mauro3 to convert .rst -> .md [ci skip] --- doc/src/manual/methods.md | 215 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 215 insertions(+) diff --git a/doc/src/manual/methods.md b/doc/src/manual/methods.md index 89f9be82bf159..28b774c740327 100644 --- a/doc/src/manual/methods.md +++ b/doc/src/manual/methods.md @@ -515,6 +515,221 @@ julia> wait(schedule(t, 1)) "definition for Int" ``` +## Design Patterns with Parametric Methods + + +While complex dispatch logic is not required for performance or usability, +sometimes it can be the best way to express some algorithm. +Here are a few common design patterns that come up sometimes when using dispatch in this way. + +### Extracting the type parameter from a super-type + + +Here is the correct code template for returning the element-type `T` +of any arbitrary subtype of `AbstractArray`: + +```julia +abstract AbstractArray{T, N} +eltype(::Type{AbstractArray}) = Any +eltype{T}(::Type{AbstractArray{T}}) = T +eltype{T, N}(::Type{AbstractArray{T, N}}) = T +eltype{A<:AbstractArray}(::Type{A}) = eltype(supertype(A)) +``` + +One common mistake here is to try to extract the type-parameter directly:: + +```julia +eltype_wrong{A<:AbstractArray, T}(::Type{A{T}}) = T +``` + +Or (when that fails to compile), by using introspection:: + +```julia +@pure eltype_wrong{A<:AbstractArray}(::Type{A}) = A.parameters[1] +``` + +However, it is not hard to construct cases where this will fail:: + +```julia +type BitVector <: AbstractArray{Bool, 1} +``` + +Here we have created a type `BitVector` which has no parameters, +but where the element-type is still fully specified, with `T` equal to `Bool`! + + +### Building a similar type with a different type parameter + +When building generic code, there is often a need for constructing a similar +object with some change made to the layout of the type. +For instance, you might have some sort of abstract array with an arbitrary element type +and want to write the computation on it with a specific element type. + +Another implication of the common mistake discussed above mentioned above, +is that we must implement a method for each type that describes how to compute this type transform. +There is no general transform of one subtype into another subtype with a different parameter. +(Quick review: do you see why this is?) + +The subtypes of `AbstractArray` typically implement two methods for expressing this: +A method to convert the input to a subtype of a specific `AbstractArray{T, N}` abstract type; +and a method to make a new uninitialized matrix with a specific element type. +Sample implementations of these can be found in the standard library. +Here is a basic example usage of them: + +```julia +input = convert(AbstractArray{Eltype}, input) +output = similar(Eltype, input) +``` + +As an extension of this, in cases where the algorithm wants to mutate the input array, +`convert` is insufficient as the return value may alias the original input. +Combining `similar` (to make the output array) and `copy!` (to fill it with the input data) +is a generic way to express the requirement for a mutable copy of the input argument: + +```julia +copy_with_eltype(Eltype, input) = copy!(similar(Eltype, input), input) +``` + +### Iterated dispatch + +In order to dispatch a multi-level parametric argument list, +often it is best to separate each level of dispatch into separate functions. +This may sound similar in approach to single-dispatch, but as we shall see below, it is still more flexible. + +For example, trying to dispatch on the element-type of an array will often run into ambiguous situations. +Instead, commonly code will dispatch first on the container type, +then recurse down to a more specific method based on eltype. +In most cases, the algorithms lend themselves conveniently to this hierarchical approach, +while in other cases, this rigor must be resolved manually. +This dispatching branching can be observed, for example, in the logic to sum two matrices: + +```julia +# First dispatch selects the map algorithm for element-wise summation. ++(a::Matrix, b::Matrix) = map(+, a, b) +# Then dispatch handles each element and selects the appropriate +# common element type for the computation. ++(a, b) = +(promote(a, b)...) +# Once the elements have the same type, they can be added. +# For example, via primitive operations exposed by the processor. ++(a::Float, b::Float) = Core.add(a, b) +``` + +### Trait-based dispatch + +A natural extension to the iterated dispatch above is to add a layer to +method selection that identifies classes of types. +We could do this by writing out a `Union` of the types with similar characteristics. +But then this list would not be extensible. +However, by expressing the property as a "trait", +it is possible to express type behaviors in the abstract +in a way that is flexible to new additions, but which has no performance impact. + +A trait is implemented by defining a pure generic function which returns a +singleton value from a particular set, as a computation on the types of its arguments. + +The example above glossed over the implementation details of `map` and `promote`, +since those both operate in terms of type traits. +When iterating over a matrix, such as in the implementation of `map`, +one important question is what order to use to traverse the data. +When `AbstractArray` subtypes implement the `linearindexing` trait, +other functions such as `map` can dispatch on this information to pick the best algorithm. +This means that each subtypes does not need to implement a custom version of `map`, +since the generic definitions + trait classes will enable the system to select the fastest version. + +```julia +map(a::AbstractArray, b::AbstractArray) = map(Base.linearindexing(a, b), a, b) +map(::LinearSlow, a::AbstractArray, b::AbstractArray) = # generic implementation +map(::LinearFast, a::AbstractArray, b::AbstractArray) = # linear-fast implementation +``` + +This trait-based mechanism is also present in the `promote` mechanism used by `+`. +That mechanism uses the types to compute the optimal common type for computing the operation. +This makes it possible to reduce the problem of implementing every function for every pair of possible type arguments, +to the much smaller problem of implementing a conversion operation from each type to a common type, +plus a table of preferred pair-wise promotion rules. + + +### Output-type Computation + +The discussion of trait-based promotion provides a transition into our next design pattern: +computing the output element type for a matrix operation. + +For implementing primitive operations, such as addition, +we use the `promote_type` function to compute the desired output type. +(As before, we saw this at work in the `promote` call in the call to `+`). + +For more complex functions on matrices, it may be necessary to compute the expected return +type for a more complex sequence of operations. +This is often performed by the following steps: + +1. Write a small function `op` that expresses the set of operations performed by the kernel of the algorithm. +2. Compute the element type `R` of the result matrix as `promote_op(op, argument_types...)`, + where `argument_types` is computed from `eltype` applied to each input array. +3. Build the output matrix as `similar(R, dims)`, where `dims` is the desired dimensions of the output array. + +For a more specific example, a generic matrix multiply pseudo-code might look like: + +```julia +function matmul(a::AbstractMatrix, b::AbstractMatrix) + op = (ai, bi) -> ai * bi + ai * bi + + ## this is insufficient because it assumes `one(eltype(a))` is constructable: + # R = typeof(op(one(eltype(a)), one(eltype(b)))) + + ## this fails because it assumes `a[1]` exists and is representative of all elements of the array + # R = typeof(op(a[1], b[1])) + + ## this is incorrect because it assumes that `+` calls `promote_type` + ## but this is not true for some types, such as Bool: + # R = promote_type(ai, bi) + + # this is wrong, since depending on the return value + # of type-inference is very brittle (as well as not being optimizable): + # R = return_types(ap, (eltype(a), eltype(b))) + + ## but, finally, this works: + R = promote_op(op, eltype(a), eltype(b)) + ## although sometimes it may give a larger type than desired + ## it will always give a correct type + + output = similar(b, R, (size(a, 1), size(b, 2))) + if size(a, 2) > 0 + for j in 1:size(b, 2) + for i in 1:size(i, 1) + ## here we don't use `ab = zero(R)`, + ## since `R` might be `Any` and `zero(Any)` is not defined + ## we also must declare `ab::R` to make the type of `ab` constant in the loop, + ## since it is possible that typeof(a * b) != typeof(a * b + a * b) == R + ab::R = a[i, 1] * b[1, j] + for k in 2:size(a, 2) + ab += a[i, k] * b[k, j] + end + output[i, j] = ab + end + end + end + return output +end +``` + +### Separate convert and kernel logic + +One way to significantly cut down on compile-times and testing complexity is to isolate +the logic for converting to the desired type and the computation. +This lets the compiler specialize and inline the conversion logic independent +from the rest of the body of the larger kernel. + +This is a common pattern seen when converting from a larger class of types +to the one specific argument type that is actually supported by the algorithm: + +```julia +complexfunction(arg::Int) = ... +complexfunction(arg::Any) = complexfunction(convert(Int, arg)) + +matmul(a::T, b::T) = ... +matmul(a, b) = matmul(promote(a, b)...) +``` + ## Parametrically-constrained Varargs methods Function parameters can also be used to constrain the number of arguments that may be supplied From 6d38500f142cf213630edcc346abc6d5b073e2fd Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Wed, 23 Aug 2017 09:51:03 +0200 Subject: [PATCH 121/324] at-deprecate diagm(A::BitMatrix) diagm(vec(A)) (#23373) --- NEWS.md | 2 ++ base/deprecated.jl | 3 +++ base/linalg/bitarray.jl | 3 +-- test/bitarray.jl | 3 --- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/NEWS.md b/NEWS.md index d0ac72076cf43..009c1debb9bfa 100644 --- a/NEWS.md +++ b/NEWS.md @@ -332,6 +332,8 @@ Deprecated or removed * `diagm(A::SparseMatrixCSC)` has been deprecated in favor of `spdiagm(sparsevec(A))` ([#23341]). + * `diagm(A::BitMatrix)` has been deprecated, use `diagm(vec(A))` instead ([#23373]). + Command-line option changes --------------------------- diff --git a/base/deprecated.jl b/base/deprecated.jl index 3428b14900e8c..a2b7a78baa660 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1716,6 +1716,9 @@ export hex2num # PR 23341 @deprecate diagm(A::SparseMatrixCSC) spdiagm(sparsevec(A)) +# PR #23373 +@deprecate diagm(A::BitMatrix) diagm(vec(A)) + # END 0.7 deprecations # BEGIN 1.0 deprecations diff --git a/base/linalg/bitarray.jl b/base/linalg/bitarray.jl index 4df257bfa75a6..7b86c75d8e56a 100644 --- a/base/linalg/bitarray.jl +++ b/base/linalg/bitarray.jl @@ -86,8 +86,7 @@ function diag(B::BitMatrix) v end -function diagm(v::Union{BitVector,BitMatrix}) - isa(v, BitMatrix) && size(v,1)==1 || size(v,2)==1 || throw(DimensionMismatch()) +function diagm(v::BitVector) n = length(v) a = falses(n, n) for i=1:n diff --git a/test/bitarray.jl b/test/bitarray.jl index fa31ad32cfbca..b1704bbf0fd02 100644 --- a/test/bitarray.jl +++ b/test/bitarray.jl @@ -1395,9 +1395,6 @@ timesofar("cat") b1 = bitrand(v1) @check_bit_operation diagm(b1) BitMatrix - b1 = bitrand(n1, n2) - @test_throws DimensionMismatch diagm(b1) - b1 = bitrand(n1, n1) @check_bit_operation diag(b1) end From 4cfcba2caebf6a387248b6d05a7c7ca86528b78c Mon Sep 17 00:00:00 2001 From: Mauro Werder Date: Mon, 14 Aug 2017 11:09:57 +0200 Subject: [PATCH 122/324] Some extra updates to the manual section of common parametric method patterns - linked references - where syntax - extra bits on `eltype` section - clarification on traits section --- doc/src/manual/methods.md | 127 +++++++++++++++++++++++--------------- 1 file changed, 78 insertions(+), 49 deletions(-) diff --git a/doc/src/manual/methods.md b/doc/src/manual/methods.md index 28b774c740327..489aa1b45ed8d 100644 --- a/doc/src/manual/methods.md +++ b/doc/src/manual/methods.md @@ -528,6 +528,17 @@ Here are a few common design patterns that come up sometimes when using dispatch Here is the correct code template for returning the element-type `T` of any arbitrary subtype of `AbstractArray`: +```julia +abstract AbstractArray{T, N} +eltype(::Type{<:AbstractArray{T}}) where {T} = T +``` +using so-called triangular dispatch. Note that if `T` is a `UnionAll` +type, as e.g. `eltype(Array{T} where T <: Integer)`, then `Any` is +returned (as does the the version of `eltype` in `Base`). + +Another way, which used to be the only correct way before the advent of +triangular dispatch in Julia v0.6, is: + ```julia abstract AbstractArray{T, N} eltype(::Type{AbstractArray}) = Any @@ -536,22 +547,28 @@ eltype{T, N}(::Type{AbstractArray{T, N}}) = T eltype{A<:AbstractArray}(::Type{A}) = eltype(supertype(A)) ``` -One common mistake here is to try to extract the type-parameter directly:: - +Another possibility is the following, which could useful to adapt +to cases where the parameter `T` would need to be matched more +narrowly: ```julia -eltype_wrong{A<:AbstractArray, T}(::Type{A{T}}) = T +eltype(::Type{AbstractArray{T, N} where {T<:S, N<:M}}) where {M, S} = Any +eltype(::Type{AbstractArray{T, N} where {T<:S}}) where {N, S} = Any +eltype(::Type{AbstractArray{T, N} where {N<:M}}) where {M, T} = T +eltype(::Type{AbstractArray{T, N}}) where {T, N} = T +eltype(::Type{A}) where {A <: AbstractArray} = eltype(supertype(A)) ``` -Or (when that fails to compile), by using introspection:: + +One common mistake is to try and get the element-type by using introspection: ```julia -@pure eltype_wrong{A<:AbstractArray}(::Type{A}) = A.parameters[1] +eltype_wrong(::Type{A}) where {A<:AbstractArray} = A.parameters[1] ``` -However, it is not hard to construct cases where this will fail:: +However, it is not hard to construct cases where this will fail: ```julia -type BitVector <: AbstractArray{Bool, 1} +struct BitVector <: AbstractArray{Bool, 1}; end ``` Here we have created a type `BitVector` which has no parameters, @@ -561,39 +578,41 @@ but where the element-type is still fully specified, with `T` equal to `Bool`! ### Building a similar type with a different type parameter When building generic code, there is often a need for constructing a similar -object with some change made to the layout of the type. +object with some change made to the layout of the type, also +necessitating a change of the type parameters. For instance, you might have some sort of abstract array with an arbitrary element type -and want to write the computation on it with a specific element type. - -Another implication of the common mistake discussed above mentioned above, -is that we must implement a method for each type that describes how to compute this type transform. +and want to write your computation on it with a specific element type. +We must implement a method for each `AbstractArray{T}` subtype that describes how to compute this type transform. There is no general transform of one subtype into another subtype with a different parameter. (Quick review: do you see why this is?) -The subtypes of `AbstractArray` typically implement two methods for expressing this: -A method to convert the input to a subtype of a specific `AbstractArray{T, N}` abstract type; -and a method to make a new uninitialized matrix with a specific element type. +The subtypes of `AbstractArray` typically implement two methods to +achieve this: +A method to convert the input array to a subtype of a specific `AbstractArray{T, N}` abstract type; +and a method to make a new uninitialized array with a specific element type. Sample implementations of these can be found in the standard library. -Here is a basic example usage of them: +Here is a basic example usage of them, guaranteeing that `input` and +`output` are of the same type: ```julia input = convert(AbstractArray{Eltype}, input) -output = similar(Eltype, input) +output = similar(input, Eltype) ``` -As an extension of this, in cases where the algorithm wants to mutate the input array, -`convert` is insufficient as the return value may alias the original input. -Combining `similar` (to make the output array) and `copy!` (to fill it with the input data) +As an extension of this, in cases where the algorithm needs a copy of +the input array, +[`convert`](@ref) is insufficient as the return value may alias the original input. +Combining [`similar`](@ref) (to make the output array) and [`copy!`](@ref) (to fill it with the input data) is a generic way to express the requirement for a mutable copy of the input argument: ```julia -copy_with_eltype(Eltype, input) = copy!(similar(Eltype, input), input) +copy_with_eltype(input, Eltype) = copy!(similar(input, Eltype), input) ``` ### Iterated dispatch In order to dispatch a multi-level parametric argument list, -often it is best to separate each level of dispatch into separate functions. +often it is best to separate each level of dispatch into distinct functions. This may sound similar in approach to single-dispatch, but as we shall see below, it is still more flexible. For example, trying to dispatch on the element-type of an array will often run into ambiguous situations. @@ -611,51 +630,61 @@ This dispatching branching can be observed, for example, in the logic to sum two +(a, b) = +(promote(a, b)...) # Once the elements have the same type, they can be added. # For example, via primitive operations exposed by the processor. -+(a::Float, b::Float) = Core.add(a, b) ++(a::Float64, b::Float64) = Core.add(a, b) ``` ### Trait-based dispatch A natural extension to the iterated dispatch above is to add a layer to -method selection that identifies classes of types. -We could do this by writing out a `Union` of the types with similar characteristics. -But then this list would not be extensible. -However, by expressing the property as a "trait", -it is possible to express type behaviors in the abstract -in a way that is flexible to new additions, but which has no performance impact. - -A trait is implemented by defining a pure generic function which returns a -singleton value from a particular set, as a computation on the types of its arguments. - -The example above glossed over the implementation details of `map` and `promote`, -since those both operate in terms of type traits. +method selection that allows to dispatch on sets of types which are +independent from the sets defined by the type hierarchy. +We could construct such a set by writing out a `Union` of the types in question, +but then this set would not be extensible as `Union`-types cannot be +altered after creation. +However, such an extensible set can be programmed with a design pattern +often referred to as a +["Holy-trait"](https://github.com/JuliaLang/julia/issues/2345#issuecomment-54537633). + +This pattern is implemented by defining a generic function which +computes a different singleton value (or type) for each trait-set to which the +function arguments may belong to. If this function is pure there is +no impact on performance compared to normal dispatch. + +The example in the previous section glossed over the implementation details of +[`map`](@ref) and [`promote`](@ref), which both operate in terms of these traits. When iterating over a matrix, such as in the implementation of `map`, one important question is what order to use to traverse the data. -When `AbstractArray` subtypes implement the `linearindexing` trait, -other functions such as `map` can dispatch on this information to pick the best algorithm. -This means that each subtypes does not need to implement a custom version of `map`, +When `AbstractArray` subtypes implement the [`Base.IndexStyle`](@ref) trait, +other functions such as `map` can dispatch on this information to pick +the best algorithm (see [Abstract Array Interface](@ref man-interface-array)). +This means that each subtype does not need to implement a custom version of `map`, since the generic definitions + trait classes will enable the system to select the fastest version. +Here a toy implementation of `map` illustrating the trait-based dispatch: ```julia -map(a::AbstractArray, b::AbstractArray) = map(Base.linearindexing(a, b), a, b) -map(::LinearSlow, a::AbstractArray, b::AbstractArray) = # generic implementation -map(::LinearFast, a::AbstractArray, b::AbstractArray) = # linear-fast implementation +map(f, a::AbstractArray, b::AbstractArray) = map(Base.IndexStyle(a, b), f, a, b) +# generic implementation: +map(::Base.IndexCartesian, f, a::AbstractArray, b::AbstractArray) = ... +# linear-indexing implementation (faster) +map(::Base.IndexLinear, f, a::AbstractArray, b::AbstractArray) = ... ``` -This trait-based mechanism is also present in the `promote` mechanism used by `+`. -That mechanism uses the types to compute the optimal common type for computing the operation. +This trait-based approach is also present in the [`promote`](@ref) +mechanism employed by the scalar `+`. +It uses [`promote_type`](@ref), which returns the optimal common type to +compute the operation given the two types of the operands. This makes it possible to reduce the problem of implementing every function for every pair of possible type arguments, to the much smaller problem of implementing a conversion operation from each type to a common type, plus a table of preferred pair-wise promotion rules. -### Output-type Computation +### Output-type computation The discussion of trait-based promotion provides a transition into our next design pattern: computing the output element type for a matrix operation. For implementing primitive operations, such as addition, -we use the `promote_type` function to compute the desired output type. +we use the [`promote_type`](@ref) function to compute the desired output type. (As before, we saw this at work in the `promote` call in the call to `+`). For more complex functions on matrices, it may be necessary to compute the expected return @@ -665,9 +694,9 @@ This is often performed by the following steps: 1. Write a small function `op` that expresses the set of operations performed by the kernel of the algorithm. 2. Compute the element type `R` of the result matrix as `promote_op(op, argument_types...)`, where `argument_types` is computed from `eltype` applied to each input array. -3. Build the output matrix as `similar(R, dims)`, where `dims` is the desired dimensions of the output array. +3. Build the output matrix as `similar(R, dims)`, where `dims` are the desired dimensions of the output array. -For a more specific example, a generic matrix multiply pseudo-code might look like: +For a more specific example, a generic square-matrix multiply pseudo-code might look like: ```julia function matmul(a::AbstractMatrix, b::AbstractMatrix) @@ -685,7 +714,7 @@ function matmul(a::AbstractMatrix, b::AbstractMatrix) # this is wrong, since depending on the return value # of type-inference is very brittle (as well as not being optimizable): - # R = return_types(ap, (eltype(a), eltype(b))) + # R = return_types(op, (eltype(a), eltype(b))) ## but, finally, this works: R = promote_op(op, eltype(a), eltype(b)) @@ -695,7 +724,7 @@ function matmul(a::AbstractMatrix, b::AbstractMatrix) output = similar(b, R, (size(a, 1), size(b, 2))) if size(a, 2) > 0 for j in 1:size(b, 2) - for i in 1:size(i, 1) + for i in 1:size(b, 1) ## here we don't use `ab = zero(R)`, ## since `R` might be `Any` and `zero(Any)` is not defined ## we also must declare `ab::R` to make the type of `ab` constant in the loop, From 7568df4d9fa4ed9ac99cbc941344ed790ee7cef5 Mon Sep 17 00:00:00 2001 From: Mus M Date: Wed, 23 Aug 2017 09:38:49 -0400 Subject: [PATCH 123/324] Update deprecated `&x` syntax in cholmod (#23401) --- base/sparse/cholmod.jl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/base/sparse/cholmod.jl b/base/sparse/cholmod.jl index 2644bd7b5cd41..b29f4ed701103 100644 --- a/base/sparse/cholmod.jl +++ b/base/sparse/cholmod.jl @@ -487,21 +487,21 @@ function allocate_sparse(nrow::Integer, ncol::Integer, nzmax::Integer, end function free_sparse!(ptr::Ptr{C_Sparse{Tv}}) where Tv<:VTypes @isok ccall((@cholmod_name("free_sparse", SuiteSparse_long), :libcholmod), Cint, - (Ptr{Ptr{C_Sparse{Tv}}}, Ptr{UInt8}), - &ptr, common()) + (Ref{Ptr{C_Sparse{Tv}}}, Ptr{UInt8}), + ptr, common()) end function free_sparse!(ptr::Ptr{C_SparseVoid}) @isok ccall((@cholmod_name("free_sparse", SuiteSparse_long), :libcholmod), Cint, - (Ptr{Ptr{C_SparseVoid}}, Ptr{UInt8}), - &ptr, common()) + (Ref{Ptr{C_SparseVoid}}, Ptr{UInt8}), + ptr, common()) end function free_factor!(ptr::Ptr{C_Factor{Tv}}) where Tv<:VTypes # Warning! Important that finalizer doesn't modify the global Common struct. @isok ccall((@cholmod_name("free_factor", SuiteSparse_long), :libcholmod), Cint, - (Ptr{Ptr{C_Factor{Tv}}}, Ptr{Void}), - &ptr, common()) + (Ref{Ptr{C_Factor{Tv}}}, Ptr{Void}), + ptr, common()) end function aat(A::Sparse{Tv}, fset::Vector{SuiteSparse_long}, mode::Integer) where Tv<:VRealTypes @@ -1725,7 +1725,7 @@ function logdet(F::Factor{Tv}) where Tv<:VTypes f = unsafe_load(pointer(F)) res = zero(Tv) for d in diag(F); res += log(abs(d)) end - f.is_ll!=0 ? 2res : res + f.is_ll != 0 ? 2res : res end det(L::Factor) = exp(logdet(L)) From 96e3a29fb3a256410ca82c0a0a6daca63d405df4 Mon Sep 17 00:00:00 2001 From: Jiahao Chen Date: Wed, 23 Aug 2017 09:41:49 -0400 Subject: [PATCH 124/324] Add missing three-parameter methods for A[tc]_mul_B!(::Triangular) (#12812) These methods are needed for compatibility with ordinary dense matrices, which have the three-parameter forms. --- base/linalg/triangular.jl | 11 ++++++++--- test/linalg/triangular.jl | 4 +++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/base/linalg/triangular.jl b/base/linalg/triangular.jl index 12aaa748770f2..558ebf9541611 100644 --- a/base/linalg/triangular.jl +++ b/base/linalg/triangular.jl @@ -441,12 +441,17 @@ scale!(c::Number, A::Union{UpperTriangular,LowerTriangular}) = scale!(A,c) A_mul_B!(A::Tridiagonal, B::AbstractTriangular) = A*full!(B) A_mul_B!(C::AbstractMatrix, A::AbstractTriangular, B::Tridiagonal) = A_mul_B!(C, full(A), B) A_mul_B!(C::AbstractMatrix, A::Tridiagonal, B::AbstractTriangular) = A_mul_B!(C, A, full(B)) -A_mul_B!(C::AbstractVector, A::AbstractTriangular, B::AbstractVector) = A_mul_B!(A, copy!(C, B)) -A_mul_B!(C::AbstractMatrix, A::AbstractTriangular, B::AbstractVecOrMat) = A_mul_B!(A, copy!(C, B)) -A_mul_B!(C::AbstractVecOrMat, A::AbstractTriangular, B::AbstractVecOrMat) = A_mul_B!(A, copy!(C, B)) A_mul_Bt!(C::AbstractVecOrMat, A::AbstractTriangular, B::AbstractVecOrMat) = A_mul_B!(A, transpose!(C, B)) A_mul_Bc!(C::AbstractMatrix, A::AbstractTriangular, B::AbstractVecOrMat) = A_mul_B!(A, adjoint!(C, B)) A_mul_Bc!(C::AbstractVecOrMat, A::AbstractTriangular, B::AbstractVecOrMat) = A_mul_B!(A, adjoint!(C, B)) +# The three methods are neceesary to avoid ambiguities with definitions in matmul.jl +for f in (:A_mul_B!, :Ac_mul_B!, :At_mul_B!) + @eval begin + ($f)(C::AbstractVector , A::AbstractTriangular, B::AbstractVector) = ($f)(A, copy!(C, B)) + ($f)(C::AbstractMatrix , A::AbstractTriangular, B::AbstractVecOrMat) = ($f)(A, copy!(C, B)) + ($f)(C::AbstractVecOrMat, A::AbstractTriangular, B::AbstractVecOrMat) = ($f)(A, copy!(C, B)) + end +end for (t, uploc, isunitc) in ((:LowerTriangular, 'L', 'N'), (:UnitLowerTriangular, 'L', 'U'), diff --git a/test/linalg/triangular.jl b/test/linalg/triangular.jl index 203d0640ca1a2..ba16777bb5538 100644 --- a/test/linalg/triangular.jl +++ b/test/linalg/triangular.jl @@ -342,9 +342,11 @@ for elty1 in (Float32, Float64, BigFloat, Complex64, Complex128, Complex{BigFloa @test B'A1' ≈ B'full(A1)' if eltyB == elty1 - @test A_mul_B!(zeros(B),A1,B) ≈ A1*B + @test A_mul_B!(zeros(B),A1,B) ≈ A1*B @test A_mul_Bc!(zeros(B),A1,B) ≈ A1*B' @test A_mul_Bt!(zeros(B),A1,B) ≈ A1*B.' + @test Ac_mul_B!(zeros(B),A1,B) ≈ A1'*B + @test At_mul_B!(zeros(B),A1,B) ≈ A1.'*B end #error handling @test_throws DimensionMismatch Base.LinAlg.A_mul_B!(A1, ones(eltyB,n+1)) From af2fd69aef0738d6c877163223fce2d2df1c9a43 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Tue, 15 Aug 2017 14:41:57 +0200 Subject: [PATCH 125/324] define IOContext(io, pairs...) and remove IOContext(io; kw...) * Usually a context is defined via a pair (`IOContext(io, :k=>v)`), but if another pair has to be pushed, one had to either add an IOContext call (`IOContext(IOContext(io, :k=>v), :x=>y)`) or to switch to kwargs syntax (`IOContext(io, k=v, x=y)`), none of which is satisfactory. * As a consequence of allowing passing multiple pairs, the method using keyword arguments is obsoleted (and hence deprecated). * Also, the method `IOContext(io, key, value)` is deprecated in favor of `IOContext(io, key=>value)`. --- base/deprecated.jl | 8 ++++++++ base/interactiveutil.jl | 3 +-- base/replutil.jl | 5 +++-- base/show.jl | 39 +++++++++++++++------------------------ test/dict.jl | 6 +++--- test/offsetarray.jl | 2 +- test/ranges.jl | 2 +- test/replutil.jl | 2 +- test/show.jl | 4 ++-- test/sparse/sparse.jl | 8 ++++---- 10 files changed, 39 insertions(+), 40 deletions(-) diff --git a/base/deprecated.jl b/base/deprecated.jl index a2b7a78baa660..7d1201f00db2a 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1719,6 +1719,14 @@ export hex2num # PR #23373 @deprecate diagm(A::BitMatrix) diagm(vec(A)) +# PR #23271 +function IOContext(io::IO; kws...) + depwarn("IOContext(io, k=v, ...) is deprecated, use IOContext(io, :k => v, ...) instead.", :IOContext) + IOContext(io, (k=>v for (k, v) in kws)...) +end + +@deprecate IOContext(io::IO, key, value) IOContext(io, key=>value) + # END 0.7 deprecations # BEGIN 1.0 deprecations diff --git a/base/interactiveutil.jl b/base/interactiveutil.jl index 1e50a94bebbf5..aa0ad2e7ffa5b 100644 --- a/base/interactiveutil.jl +++ b/base/interactiveutil.jl @@ -384,8 +384,7 @@ function code_warntype(io::IO, f, @nospecialize(t)) body.args = src.code body.typ = rettype # Fix slot names and types in function body - show_unquoted(IOContext(IOContext(emph_io, :SOURCEINFO => src), - :SOURCE_SLOTNAMES => slotnames), + show_unquoted(IOContext(emph_io, :SOURCEINFO => src, :SOURCE_SLOTNAMES => slotnames), body, 2) print(emph_io, '\n') end diff --git a/base/replutil.jl b/base/replutil.jl index d5329602aacc0..5582c7a6b01fb 100644 --- a/base/replutil.jl +++ b/base/replutil.jl @@ -242,7 +242,8 @@ function showerror(io::IO, ex::DomainError, bt; backtrace=true) if isa(ex.val, AbstractArray) compact = get(io, :compact, true) limit = get(io, :limit, true) - print(IOContext(io, compact=compact, limit=limit), "DomainError with ", ex.val) + print(IOContext(io, :compact => compact, :limit => limit), + "DomainError with ", ex.val) else print(io, "DomainError with ", ex.val) end @@ -360,7 +361,7 @@ function showerror(io::IO, ex::MethodError) print(io, "; ") for (i, (k, v)) in enumerate(kwargs) print(io, k, "=") - show(IOContext(io, :limit=>true), v) + show(IOContext(io, :limit => true), v) i == length(kwargs) || print(io, ", ") end end diff --git a/base/show.jl b/base/show.jl index 79678aab589a3..38a8343af2d6e 100644 --- a/base/show.jl +++ b/base/show.jl @@ -20,41 +20,32 @@ struct IOContext{IO_t <: IO} <: AbstractPipe end end -""" - IOContext(io::IO; properties...) +unwrapcontext(io::IO) = io, ImmutableDict{Symbol,Any}() +unwrapcontext(io::IOContext) = io.io, io.dict -The same as `IOContext(io::IO, KV::Pair)`, but accepting properties as keyword arguments. -""" -IOContext(io::IO; kws...) = IOContext(convert(IOContext, io); kws...) -function IOContext(io::IOContext; kws...) - for (k, v) in kws - io = IOContext(io, k, v) - end - return io +function IOContext(io::IO, dict::ImmutableDict) + io0 = unwrapcontext(io)[1] + IOContext{typeof(io0)}(io0, dict) end -convert(::Type{IOContext}, io::IOContext) = io -convert(::Type{IOContext}, io::IO) = IOContext(io, ImmutableDict{Symbol, Any}()) +convert(::Type{IOContext}, io::IO) = IOContext(unwrapcontext(io)...) -IOContext(io::IOContext, dict::ImmutableDict) = typeof(io)(io.io, dict) -IOContext(io::IO, dict::ImmutableDict) = IOContext{typeof(io)}(io, dict) - -IOContext(io::IOContext, key, value) = IOContext(io.io, ImmutableDict{Symbol, Any}(io.dict, key, value)) -IOContext(io::IO, key, value) = IOContext(io, ImmutableDict{Symbol, Any}(key, value)) - -IOContext(io::IO, context::IO) = convert(IOContext, io) +function IOContext(io::IO, KV::Pair) + io0, d = unwrapcontext(io) + IOContext(io0, ImmutableDict{Symbol,Any}(d, KV[1], KV[2])) +end """ IOContext(io::IO, context::IOContext) Create an `IOContext` that wraps an alternate `IO` but inherits the properties of `context`. """ -IOContext(io::IO, context::IOContext) = IOContext(io, context.dict) +IOContext(io::IO, context::IO) = IOContext(unwrapcontext(io)[1], unwrapcontext(context)[2]) """ - IOContext(io::IO, KV::Pair) + IOContext(io::IO, KV::Pair...) -Create an `IOContext` that wraps a given stream, adding the specified `key=>value` pair to +Create an `IOContext` that wraps a given stream, adding the specified `key=>value` pairs to the properties of that stream (note that `io` can itself be an `IOContext`). - use `(key => value) in dict` to see if this particular combination is in the properties set @@ -87,7 +78,7 @@ julia> f(IOContext(STDOUT, :short => true)) short ``` """ -IOContext(io::IO, KV::Pair) = IOContext(io, KV[1], KV[2]) +IOContext(io::IO, KV::Pair, KVs::Pair...) = IOContext(IOContext(io, KV), KVs...) show(io::IO, ctx::IOContext) = (print(io, "IOContext("); show(io, ctx.io); print(io, ")")) @@ -1335,7 +1326,7 @@ function dumpsubtypes(io::IO, x::DataType, m::Module, n::Int, indent) tp = t while true show(tvar_io, tp.var) - tvar_io = IOContext(tvar_io, :unionall_env, tp.var) + tvar_io = IOContext(tvar_io, :unionall_env => tp.var) tp = tp.body if isa(tp, UnionAll) print(io, ", ") diff --git a/test/dict.jl b/test/dict.jl index 3954b342ed05f..dbd4771b2dfe1 100644 --- a/test/dict.jl +++ b/test/dict.jl @@ -268,7 +268,7 @@ for d in (Dict("\n" => "\n", "1" => "\n", "\n" => "2"), for cols in (12, 40, 80), rows in (2, 10, 24) # Ensure output is limited as requested s = IOBuffer() - io = Base.IOContext(Base.IOContext(s, :limit => true), :displaysize => (rows, cols)) + io = Base.IOContext(s, :limit => true, :displaysize => (rows, cols)) Base.show(io, MIME("text/plain"), d) out = split(String(take!(s)),'\n') for line in out[2:end] @@ -278,7 +278,7 @@ for d in (Dict("\n" => "\n", "1" => "\n", "\n" => "2"), for f in (keys, values) s = IOBuffer() - io = Base.IOContext(Base.IOContext(s, :limit => true), :displaysize => (rows, cols)) + io = Base.IOContext(s, :limit => true, :displaysize => (rows, cols)) Base.show(io, MIME("text/plain"), f(d)) out = split(String(take!(s)),'\n') for line in out[2:end] @@ -311,7 +311,7 @@ end mutable struct Alpha end Base.show(io::IO, ::Alpha) = print(io,"α") let sbuff = IOBuffer(), - io = Base.IOContext(Base.IOContext(sbuff, :limit => true), :displaysize => (10, 20)) + io = Base.IOContext(sbuff, :limit => true, :displaysize => (10, 20)) Base.show(io, MIME("text/plain"), Dict(Alpha()=>1)) local str = String(take!(sbuff)) diff --git a/test/offsetarray.jl b/test/offsetarray.jl index 46ace5061822f..b2c642cdfced3 100644 --- a/test/offsetarray.jl +++ b/test/offsetarray.jl @@ -147,7 +147,7 @@ smry = summary(v) @test contains(smry, "OffsetArray{Float64,1") @test contains(smry, "with indices -1:1") function cmp_showf(printfunc, io, A) - ioc = IOContext(IOContext(io, :limit => true), :compact => true) + ioc = IOContext(io, :limit => true, :compact => true) printfunc(ioc, A) str1 = String(take!(io)) printfunc(ioc, parent(A)) diff --git a/test/ranges.jl b/test/ranges.jl index a953599223ad2..69fecf89d2630 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -850,7 +850,7 @@ end # stringmime/show should display the range or linspace nicely # to test print_range in range.jl -replstrmime(x) = sprint((io,x) -> show(IOContext(io, limit=true, displaysize=(24, 80)), MIME("text/plain"), x), x) +replstrmime(x) = sprint((io,x) -> show(IOContext(io, :limit => true, :displaysize => (24, 80)), MIME("text/plain"), x), x) @test replstrmime(1:4) == "1:4" @test stringmime("text/plain", 1:4) == "1:4" @test stringmime("text/plain", linspace(1,5,7)) == "1.0:0.6666666666666666:5.0" diff --git a/test/replutil.jl b/test/replutil.jl index 7049511f1ae52..4f91dd949ffdb 100644 --- a/test/replutil.jl +++ b/test/replutil.jl @@ -610,7 +610,7 @@ end @testset "Dict printing with limited rows" begin buf = IOBuffer() - io = IOContext(IOContext(buf, :displaysize => (4, 80)), :limit => true) + io = IOContext(buf, :displaysize => (4, 80), :limit => true) d = Base.ImmutableDict(1=>2) show(io, MIME"text/plain"(), d) @test String(take!(buf)) == "Base.ImmutableDict{$Int,$Int} with 1 entry: …" diff --git a/test/show.jl b/test/show.jl index 5d576dcd2920d..ebe4a6681f40f 100644 --- a/test/show.jl +++ b/test/show.jl @@ -3,7 +3,7 @@ # For curmod_* include("testenv.jl") -replstr(x) = sprint((io,x) -> show(IOContext(io, limit=true, displaysize=(24, 80)), MIME("text/plain"), x), x) +replstr(x) = sprint((io,x) -> show(IOContext(io, :limit => true, :displaysize => (24, 80)), MIME("text/plain"), x), x) @test replstr(Array{Any}(2)) == "2-element Array{Any,1}:\n #undef\n #undef" @test replstr(Array{Any}(2,2)) == "2×2 Array{Any,2}:\n #undef #undef\n #undef #undef" @@ -923,7 +923,7 @@ end @testset "Array printing with limited rows" begin arrstr = let buf = IOBuffer() function (A, rows) - Base.showarray(IOContext(buf, displaysize=(rows, 80), limit=true), + Base.showarray(IOContext(buf, :displaysize => (rows, 80), :limit => true), A, false, header=true) String(take!(buf)) end diff --git a/test/sparse/sparse.jl b/test/sparse/sparse.jl index 30e62f354afab..10719cce3c0c1 100644 --- a/test/sparse/sparse.jl +++ b/test/sparse/sparse.jl @@ -1794,14 +1794,14 @@ end show(io, MIME"text/plain"(), spzeros(Float32, Int64, 2, 2)) @test String(take!(io)) == "2×2 SparseMatrixCSC{Float32,Int64} with 0 stored entries" - ioc = IOContext(io, displaysize = (5, 80), limit = true) + ioc = IOContext(io, :displaysize => (5, 80), :limit => true) show(ioc, MIME"text/plain"(), sparse(Int64[1], Int64[1], [1.0])) @test String(take!(io)) == "1×1 SparseMatrixCSC{Float64,Int64} with 1 stored entry:\n [1, 1] = 1.0" show(ioc, MIME"text/plain"(), sparse(Int64[1, 1], Int64[1, 2], [1.0, 2.0])) @test String(take!(io)) == "1×2 SparseMatrixCSC{Float64,Int64} with 2 stored entries:\n ⋮" # even number of rows - ioc = IOContext(io, displaysize = (8, 80), limit = true) + ioc = IOContext(io, :displaysize => (8, 80), :limit => true) show(ioc, MIME"text/plain"(), sparse(Int64[1,2,3,4], Int64[1,1,2,2], [1.0,2.0,3.0,4.0])) @test String(take!(io)) == string("4×2 SparseMatrixCSC{Float64,Int64} with 4 stored entries:\n [1, 1]", " = 1.0\n [2, 1] = 2.0\n [3, 2] = 3.0\n [4, 2] = 4.0") @@ -1815,7 +1815,7 @@ end " = 1.0\n ⋮\n [5, 3] = 1.0") # odd number of rows - ioc = IOContext(io, displaysize = (9, 80), limit = true) + ioc = IOContext(io, :displaysize => (9, 80), :limit => true) show(ioc, MIME"text/plain"(), sparse(Int64[1,2,3,4,5], Int64[1,1,2,2,3], [1.0,2.0,3.0,4.0,5.0])) @test String(take!(io)) == string("5×3 SparseMatrixCSC{Float64,Int64} with 5 stored entries:\n [1, 1]", " = 1.0\n [2, 1] = 2.0\n [3, 2] = 3.0\n [4, 2] = 4.0\n [5, 3] = 5.0") @@ -1828,7 +1828,7 @@ end @test String(take!(io)) == string("6×3 SparseMatrixCSC{Float64,$Int} with 18 stored entries:\n [1, 1]", " = 1.0\n [2, 1] = 1.0\n ⋮\n [5, 3] = 1.0\n [6, 3] = 1.0") - ioc = IOContext(io, displaysize = (9, 80)) + ioc = IOContext(io, :displaysize => (9, 80)) show(ioc, MIME"text/plain"(), sparse(Int64[1,2,3,4,5,6], Int64[1,1,2,2,3,3], [1.0,2.0,3.0,4.0,5.0,6.0])) @test String(take!(io)) == string("6×3 SparseMatrixCSC{Float64,Int64} with 6 stored entries:\n [1, 1] = 1.0\n", " [2, 1] = 2.0\n [3, 2] = 3.0\n [4, 2] = 4.0\n [5, 3] = 5.0\n [6, 3] = 6.0") From f321fee3100ff5a2590851616711922a5972094a Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Wed, 23 Aug 2017 22:16:08 +0200 Subject: [PATCH 126/324] add missing import for axpby! (#23405) --- base/linalg/blas.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/base/linalg/blas.jl b/base/linalg/blas.jl index 88cbfab64b95a..27cc4ca3ebed9 100644 --- a/base/linalg/blas.jl +++ b/base/linalg/blas.jl @@ -2,13 +2,14 @@ module BLAS -import ..axpy! +import ..axpy!, ..axpby! import Base: copy! export # Level 1 asum, axpy!, + axpby!, blascopy!, dot, dotc, From b330b0eb0cf88bcd2f07873923ccf69c8c622719 Mon Sep 17 00:00:00 2001 From: Mus M Date: Wed, 23 Aug 2017 16:20:55 -0400 Subject: [PATCH 127/324] Add cfunction tuple of types deprecation (#23066) * Add cfunction tuple of types deprecation * Update docs --- NEWS.md | 3 +++ base/deprecated.jl | 3 +++ doc/src/manual/calling-c-and-fortran-code.md | 6 ++--- examples/embedding/embedding.c | 2 +- src/codegen.cpp | 4 ---- test/ambiguous.jl | 4 ++-- test/ccall.jl | 24 ++++++++++---------- test/core.jl | 2 +- test/misc.jl | 2 +- test/reflection.jl | 4 ++-- test/spawn.jl | 2 +- test/staged.jl | 2 +- 12 files changed, 30 insertions(+), 28 deletions(-) diff --git a/NEWS.md b/NEWS.md index 23211ddceeb40..fcfbba30d856d 100644 --- a/NEWS.md +++ b/NEWS.md @@ -331,6 +331,9 @@ Deprecated or removed * `Base.SparseArrays.SpDiagIterator` has been removed ([#23261]). + * The tuple-of-types form of `cfunction`, `cfunction(f, returntype, (types...))`, has been deprecated + in favor of the tuple-type form `cfunction(f, returntype, Tuple{types...})` ([#23066]). + * `diagm(A::SparseMatrixCSC)` has been deprecated in favor of `spdiagm(sparsevec(A))` ([#23341]). diff --git a/base/deprecated.jl b/base/deprecated.jl index a2b7a78baa660..2a414203a24b4 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1713,6 +1713,9 @@ export hex2num # issue #17886 # deprecations for filter[!] with 2-arg functions are in associative.jl +# PR #23066 +@deprecate cfunction(f, r, a::Tuple) cfunction(f, r, Tuple{a...}) + # PR 23341 @deprecate diagm(A::SparseMatrixCSC) spdiagm(sparsevec(A)) diff --git a/doc/src/manual/calling-c-and-fortran-code.md b/doc/src/manual/calling-c-and-fortran-code.md index 6c0070dedfed7..7b7ec77e641f5 100644 --- a/doc/src/manual/calling-c-and-fortran-code.md +++ b/doc/src/manual/calling-c-and-fortran-code.md @@ -158,7 +158,7 @@ Julia function. Arguments to [`cfunction()`](@ref) are as follows: 1. A Julia Function 2. Return type -3. A tuple of input types +3. A tuple type of input types Only platform-default C calling convention is supported. `cfunction`-generated pointers cannot be used in calls where WINAPI expects `stdcall` function on 32-bit windows, but can be used on WIN64 @@ -192,11 +192,11 @@ a C `int`, so we must be sure to return `Cint` via a call to `convert` and a `ty In order to pass this function to C, we obtain its address using the function `cfunction`: ```jldoctest mycompare -julia> const mycompare_c = cfunction(mycompare, Cint, (Ref{Cdouble}, Ref{Cdouble})); +julia> const mycompare_c = cfunction(mycompare, Cint, Tuple{Ref{Cdouble}, Ref{Cdouble}}); ``` [`cfunction()`](@ref) accepts three arguments: the Julia function (`mycompare`), the return type -(`Cint`), and a tuple of the argument types, in this case to sort an array of `Cdouble` +(`Cint`), and a tuple type of the input argument types, in this case to sort an array of `Cdouble` ([`Float64`](@ref)) elements. The final call to `qsort` looks like this: diff --git a/examples/embedding/embedding.c b/examples/embedding/embedding.c index c820e5b3c02ad..712e46dd8b6b4 100644 --- a/examples/embedding/embedding.c +++ b/examples/embedding/embedding.c @@ -142,7 +142,7 @@ int main() " nothing\n" "end"); typedef void (*Func_VOID__VOID)(void); - jl_value_t *pbar = jl_eval_string("cfunction(bar_from_c, Void, ())"); + jl_value_t *pbar = jl_eval_string("cfunction(bar_from_c, Void, Tuple{})"); Func_VOID__VOID bar = (Func_VOID__VOID)jl_unbox_voidpointer(pbar); bar(); checked_eval_string("bar() = println(\"calling new bar\")"); diff --git a/src/codegen.cpp b/src/codegen.cpp index 53c5996dec3ca..3a4362653b471 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1422,10 +1422,6 @@ extern "C" JL_DLLEXPORT void *jl_function_ptr(jl_function_t *f, jl_value_t *rt, jl_value_t *argt) { JL_GC_PUSH1(&argt); - if (jl_is_tuple(argt)) { - // TODO: maybe deprecation warning, better checking - argt = (jl_value_t*)jl_apply_tuple_type_v((jl_value_t**)jl_data_ptr(argt), jl_nfields(argt)); - } JL_LOCK(&codegen_lock); Function *llvmf = jl_cfunction_object(f, rt, (jl_tupletype_t*)argt); JL_GC_POP(); diff --git a/test/ambiguous.jl b/test/ambiguous.jl index 9c1b8d71b148c..1f4f63296369c 100644 --- a/test/ambiguous.jl +++ b/test/ambiguous.jl @@ -67,7 +67,7 @@ end # Test that non-ambiguous cases work io = IOBuffer() @test precompile(ambig, (Int, Int)) == true -cfunction(ambig, Int, (Int, Int)) +cfunction(ambig, Int, Tuple{Int, Int}) @test length(code_lowered(ambig, (Int, Int))) == 1 @test length(code_typed(ambig, (Int, Int))) == 1 code_llvm(io, ambig, (Int, Int)) @@ -75,7 +75,7 @@ code_native(io, ambig, (Int, Int)) # Test that ambiguous cases fail appropriately @test precompile(ambig, (UInt8, Int)) == false -cfunction(ambig, Int, (UInt8, Int)) # test for a crash (doesn't throw an error) +cfunction(ambig, Int, Tuple{UInt8, Int}) # test for a crash (doesn't throw an error) @test_throws ErrorException which(ambig, (UInt8, Int)) @test_throws ErrorException code_llvm(io, ambig, (UInt8, Int)) @test_throws ErrorException code_native(io, ambig, (UInt8, Int)) diff --git a/test/ccall.jl b/test/ccall.jl index 560705055554f..9ce6c764de033 100644 --- a/test/ccall.jl +++ b/test/ccall.jl @@ -791,57 +791,57 @@ for (t,v) in ((Complex{Int32},:ci32),(Complex{Int64},:ci64), function $fname(s) @assert false end - b = ccall(cfunction($fname1,Ref{$t},(Ref{$t},)),Ref{$t},(Ref{$t},),a) + b = ccall(cfunction($fname1, Ref{$t}, Tuple{Ref{$t}}), Ref{$t}, (Ref{$t},), a) verbose && println("C: ",b) @test b == $v @test b === a @test b === c - b = ccall(cfunction($fname,$t,($t,)),$t,($t,),a) + b = ccall(cfunction($fname, $t, Tuple{$t}), $t, ($t,), a) verbose && println("C: ",b) @test b == $v if ($(t).mutable) @test !(b === c) @test !(b === a) end - b = ccall(cfunction($fname1,$t,(Ref{$t},)),$t,(Ref{$t},),a) + b = ccall(cfunction($fname1, $t, Tuple{Ref{$t}}), $t, (Ref{$t},), a) verbose && println("C: ",b) @test b == $v if ($(t).mutable) @test !(b === c) @test !(b === a) end - b = ccall(cfunction($fname,Ref{$t},($t,)),Ref{$t},($t,),a) + b = ccall(cfunction($fname, Ref{$t}, Tuple{$t}), Ref{$t}, ($t,), a) verbose && println("C: ",b) @test b == $v @test b === c if ($(t).mutable) @test !(b === a) end - b = ccall(cfunction($fname,Any,(Ref{$t},)),Any,(Ref{$t},),$v) + b = ccall(cfunction($fname, Any, Tuple{Ref{$t}}), Any, (Ref{$t},), $v) verbose && println("C: ",b) @test b == $v @test b === c if ($(t).mutable) @test !(b === a) end - b = ccall(cfunction($fname,Any,(Ref{Any},)),Any,(Ref{Any},),$v) + b = ccall(cfunction($fname, Any, Tuple{Ref{Any}}), Any, (Ref{Any},), $v) @test b == $v @test b === c if ($(t).mutable) @test !(b === a) end - @test_throws TypeError ccall(cfunction($fname,Ref{AbstractString},(Ref{Any},)),Any,(Ref{Any},),$v) - @test_throws TypeError ccall(cfunction($fname,AbstractString,(Ref{Any},)),Any,(Ref{Any},),$v) + @test_throws TypeError ccall(cfunction($fname, Ref{AbstractString}, Tuple{Ref{Any}}), Any, (Ref{Any},), $v) + @test_throws TypeError ccall(cfunction($fname, AbstractString, Tuple{Ref{Any}}), Any, (Ref{Any},), $v) end end # issue 13031 foo13031(x) = Cint(1) -foo13031p = cfunction(foo13031, Cint, (Ref{Tuple{}},)) +foo13031p = cfunction(foo13031, Cint, Tuple{Ref{Tuple{}}}) ccall(foo13031p, Cint, (Ref{Tuple{}},), ()) foo13031(x,y,z) = z -foo13031p = cfunction(foo13031, Cint, (Ref{Tuple{}},Ref{Tuple{}},Cint)) +foo13031p = cfunction(foo13031, Cint, Tuple{Ref{Tuple{}}, Ref{Tuple{}}, Cint}) ccall(foo13031p, Cint, (Ref{Tuple{}},Ref{Tuple{}},Cint), (), (), 8) # issue 17219 @@ -993,7 +993,7 @@ if Sys.ARCH === :x86_64 T = NTuple{4, VecElement{s}} @eval function rt_sse(a1::$T, a2::$T, a3::$T, a4::$T) return ccall( - cfunction(foo_ams, $T, ($T, $T, $T, $T)), + cfunction(foo_ams, $T, Tuple{$T, $T, $T, $T}), $T, ($T, $T, $T, $T), a1, a2, a3, a4) @@ -1281,7 +1281,7 @@ end evalf_callback_19805(ci::callinfos_19805{FUNC_FT}) where {FUNC_FT} = ci.f(0.5)::Float64 evalf_callback_c_19805(ci::callinfos_19805{FUNC_FT}) where {FUNC_FT} = cfunction( - evalf_callback_19805, Float64, (callinfos_19805{FUNC_FT},)) + evalf_callback_19805, Float64, Tuple{callinfos_19805{FUNC_FT}}) @test_throws(ErrorException("ccall: the type of argument 1 doesn't correspond to a C type"), evalf_callback_c_19805( callinfos_19805(sin) )) diff --git a/test/core.jl b/test/core.jl index a8287f3e8c01f..21e8b74219b49 100644 --- a/test/core.jl +++ b/test/core.jl @@ -4404,7 +4404,7 @@ end function f18054() return Cint(0) end -cfunction(f18054, Cint, ()) +cfunction(f18054, Cint, Tuple{}) # issue #18986: the ccall optimization of cfunction leaves JL_TRY stack in bad state dummy18996() = return nothing diff --git a/test/misc.jl b/test/misc.jl index 840bb5ce56e00..8692d41cf122a 100644 --- a/test/misc.jl +++ b/test/misc.jl @@ -574,7 +574,7 @@ if Sys.iswindows() x+y end - let addr = cfunction(WeVirtualProtectThisToRWX, UInt64, (UInt64, UInt64)) + let addr = cfunction(WeVirtualProtectThisToRWX, UInt64, Tuple{UInt64, UInt64}) addr = addr-(UInt64(addr)%4096) PAGE_EXECUTE_READWRITE = 0x40 oldPerm = Ref{UInt32}() diff --git a/test/reflection.jl b/test/reflection.jl index f49296657ffbc..11c23ea08f96d 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -495,7 +495,7 @@ end tracefoo(x, y) = x+y didtrace = false tracer(x::Ptr{Void}) = (@test isa(unsafe_pointer_to_objref(x), Core.MethodInstance); global didtrace = true; nothing) -ccall(:jl_register_method_tracer, Void, (Ptr{Void},), cfunction(tracer, Void, (Ptr{Void},))) +ccall(:jl_register_method_tracer, Void, (Ptr{Void},), cfunction(tracer, Void, Tuple{Ptr{Void}})) meth = which(tracefoo,Tuple{Any,Any}) ccall(:jl_trace_method, Void, (Any,), meth) @test tracefoo(1, 2) == 3 @@ -508,7 +508,7 @@ ccall(:jl_register_method_tracer, Void, (Ptr{Void},), C_NULL) # Method Tracing test methtracer(x::Ptr{Void}) = (@test isa(unsafe_pointer_to_objref(x), Method); global didtrace = true; nothing) -ccall(:jl_register_newmeth_tracer, Void, (Ptr{Void},), cfunction(methtracer, Void, (Ptr{Void},))) +ccall(:jl_register_newmeth_tracer, Void, (Ptr{Void},), cfunction(methtracer, Void, Tuple{Ptr{Void}})) tracefoo2(x, y) = x*y @test didtrace didtrace = false diff --git a/test/spawn.jl b/test/spawn.jl index 43787d66871d7..4a98e696e2634 100644 --- a/test/spawn.jl +++ b/test/spawn.jl @@ -254,7 +254,7 @@ let fname = tempname() oldhandle = OLD_STDERR.handle OLD_STDERR.status = Base.StatusClosing OLD_STDERR.handle = C_NULL - ccall(:uv_close, Void, (Ptr{Void}, Ptr{Void}), oldhandle, cfunction(thrash, Void, (Ptr{Void},))) + ccall(:uv_close, Void, (Ptr{Void}, Ptr{Void}), oldhandle, cfunction(thrash, Void, Tuple{Ptr{Void}})) sleep(1) import Base.zzzInvalidIdentifier """ diff --git a/test/staged.jl b/test/staged.jl index 95a9b25ae30a0..37ed2e7a35338 100644 --- a/test/staged.jl +++ b/test/staged.jl @@ -147,7 +147,7 @@ module TestGeneratedThrow foo() = (bar(rand() > 0.5 ? 1 : 1.0); error("foo")) function __init__() code_typed(foo,(); optimize = false) - cfunction(foo,Void,()) + cfunction(foo,Void,Tuple{}) end end From 9a98dcb565046b0fa6c2418945d05cdc45d66f0a Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Wed, 23 Aug 2017 16:26:16 -0400 Subject: [PATCH 128/324] parseable printing of all type names and TypeVars also adjust generic function printing so that when `(::x)` is printed, `x` is more likely to be the type of the function. --- base/methodshow.jl | 3 +- base/replutil.jl | 3 +- base/show.jl | 93 +++++++++++++++++++++++++++++++++++++++++----- test/replutil.jl | 4 +- test/show.jl | 34 ++++++++++++++++- 5 files changed, 121 insertions(+), 16 deletions(-) diff --git a/base/methodshow.jl b/base/methodshow.jl index 095cb2f56b72f..286980c197be0 100644 --- a/base/methodshow.jl +++ b/base/methodshow.jl @@ -144,7 +144,8 @@ function show_method_table(io::IO, ms::MethodList, max::Int=-1, header::Bool=tru n = length(ms) if header m = n==1 ? "method" : "methods" - ns = isself ? string(name) : string("(::", name, ")") + sname = string(name) + ns = (isself || '#' in sname) ? sname : string("(::", name, ")") what = startswith(ns, '@') ? "macro" : "generic function" print(io, "# $n $m for ", what, " \"", ns, "\":") end diff --git a/base/replutil.jl b/base/replutil.jl index d5329602aacc0..7ef361e4971ed 100644 --- a/base/replutil.jl +++ b/base/replutil.jl @@ -109,7 +109,8 @@ function show(io::IO, ::MIME"text/plain", f::Function) ft == typeof(getfield(ft.name.module, name)) n = length(methods(f)) m = n==1 ? "method" : "methods" - ns = isself ? string(name) : string("(::", name, ")") + sname = string(name) + ns = (isself || '#' in sname) ? sname : string("(::", ft, ")") what = startswith(ns, '@') ? "macro" : "generic function" print(io, ns, " (", what, " with $n $m)") end diff --git a/base/show.jl b/base/show.jl index 79678aab589a3..8127830ad1dee 100644 --- a/base/show.jl +++ b/base/show.jl @@ -181,10 +181,15 @@ end function show(io::IO, f::Function) ft = typeof(f) mt = ft.name.mt - if !isdefined(mt, :module) || is_exported_from_stdlib(mt.name, mt.module) || mt.module === Main - print(io, mt.name) + if isdefined(mt, :module) && isdefined(mt.module, mt.name) && + getfield(mt.module, mt.name) === f + if is_exported_from_stdlib(mt.name, mt.module) || mt.module === Main + print(io, mt.name) + else + print(io, mt.module, ".", mt.name) + end else - print(io, mt.module, ".", mt.name) + show_default(io, f) end end @@ -208,10 +213,36 @@ function print_without_params(@nospecialize(x)) return false end +has_typevar(@nospecialize(t), v::TypeVar) = ccall(:jl_has_typevar, Cint, (Any, Any), t, v)!=0 + +function io_has_tvar_name(io::IOContext, name::Symbol, @nospecialize(x)) + for (key, val) in io.dict + if key === :unionall_env && val isa TypeVar && val.name === name && has_typevar(x, val) + return true + end + end + return false +end +io_has_tvar_name(io::IO, name::Symbol, @nospecialize(x)) = false + function show(io::IO, x::UnionAll) if print_without_params(x) return show(io, unwrap_unionall(x).name) end + + if x.var.name == :_ || io_has_tvar_name(io, x.var.name, x) + counter = 1 + while true + newname = Symbol(x.var.name, counter) + if !io_has_tvar_name(io, newname, x) + newtv = TypeVar(newname, x.var.lb, x.var.ub) + x = UnionAll(newtv, x{newtv}) + break + end + counter += 1 + end + end + show(IOContext(io, :unionall_env => x.var), x.body) print(io, " where ") show(io, x.var) @@ -219,6 +250,52 @@ end show(io::IO, x::DataType) = show_datatype(io, x) +function show_type_name(io::IO, tn::TypeName) + globname = isdefined(tn, :mt) ? tn.mt.name : nothing + globfunc = false + if globname !== nothing + globname_str = string(globname) + if ('#' ∉ globname_str && '@' ∉ globname_str && isdefined(tn, :module) && + isbindingresolved(tn.module, globname) && isdefined(tn.module, globname) && + isa(getfield(tn.module, globname), tn.wrapper) && isleaftype(tn.wrapper)) + globfunc = true + end + end + sym = globfunc ? globname : tn.name + sym_str = string(sym) + hidden = !globfunc && '#' ∈ sym_str + quo = false + if hidden + print(io, "getfield(") + elseif globfunc + print(io, "typeof(") + end + if isdefined(tn, :module) && !(is_exported_from_stdlib(sym, tn.module) || (tn.module === Main && !hidden)) + show(io, tn.module) + if !hidden + print(io, ".") + if globfunc && !is_id_start_char(first(sym_str)) + print(io, ":") + if sym == :(==) + print(io, "(") + quo = true + end + end + end + end + if hidden + print(io, ", Symbol(\"", sym_str, "\"))") + else + print(io, sym_str) + if globfunc + print(io, ")") + if quo + print(io, ")") + end + end + end +end + function show_datatype(io::IO, x::DataType) istuple = x.name === Tuple.name if (!isempty(x.parameters) || istuple) && x !== Tuple @@ -228,7 +305,7 @@ function show_datatype(io::IO, x::DataType) if istuple && n > 3 && all(i -> (x.parameters[1] === i), x.parameters) print(io, "NTuple{", n, ',', x.parameters[1], "}") else - show(io, x.name) + show_type_name(io, x.name) # Do not print the type parameters for the primary type if we are # printing a method signature or type parameter. # Always print the type parameter if we are printing the type directly @@ -241,7 +318,7 @@ function show_datatype(io::IO, x::DataType) print(io, '}') end else - show(io, x.name) + show_type_name(io, x.name) end end @@ -272,11 +349,7 @@ macro show(exs...) end function show(io::IO, tn::TypeName) - if is_exported_from_stdlib(tn.name, tn.module) || tn.module === Main - print(io, tn.name) - else - print(io, tn.module, '.', tn.name) - end + show_type_name(io, tn) end show(io::IO, ::Void) = print(io, "nothing") diff --git a/test/replutil.jl b/test/replutil.jl index 7049511f1ae52..fc0434ed9abe4 100644 --- a/test/replutil.jl +++ b/test/replutil.jl @@ -347,7 +347,7 @@ let err_str, err_str = @except_str randn(1)() MethodError @test contains(err_str, "MethodError: objects of type Array{Float64,1} are not callable") end -@test stringmime("text/plain", FunctionLike()) == "(::FunctionLike) (generic function with 0 methods)" +@test stringmime("text/plain", FunctionLike()) == "(::$(curmod_prefix)FunctionLike) (generic function with 0 methods)" @test ismatch(r"^@doc \(macro with \d+ method[s]?\)$", stringmime("text/plain", getfield(Base, Symbol("@doc")))) method_defs_lineno = @__LINE__() + 1 @@ -385,7 +385,7 @@ let err_str, "@doc(__source__::LineNumberNode, __module__::Module, x...) in Core at boot.jl:") @test startswith(sprint(show, which(FunctionLike(), Tuple{})), "(::$(curmod_prefix)FunctionLike)() in $curmod_str at $sp:$(method_defs_lineno + 7)") - @test stringmime("text/plain", FunctionLike()) == "(::FunctionLike) (generic function with 1 method)" + @test stringmime("text/plain", FunctionLike()) == "(::$(curmod_prefix)FunctionLike) (generic function with 1 method)" @test stringmime("text/plain", Core.arraysize) == "arraysize (built-in function)" err_str = @except_stackframe Symbol() ErrorException diff --git a/test/show.jl b/test/show.jl index 5d576dcd2920d..1366b8ef26cc8 100644 --- a/test/show.jl +++ b/test/show.jl @@ -507,7 +507,7 @@ function f13127() show(buf, f) String(take!(buf)) end -@test f13127() == "$(curmod_prefix)f" +@test startswith(f13127(), "getfield($(@__MODULE__), Symbol(\"") #test methodshow.jl functions @test Base.inbase(Base) @@ -736,7 +736,7 @@ let sv = Core.svec(:a, :b, :c) @test repr == "SimpleVector\n 1: Symbol a\n 2: Symbol b\n 3: #undef\n" end let repr = sprint(dump, sin) - @test repr == "sin (function of type Base.#sin)\n" + @test repr == "sin (function of type typeof(sin))\n" end let repr = sprint(dump, Base.Test) @test repr == "Module Base.Test\n" @@ -947,3 +947,33 @@ end " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 … 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", " ⋮ ⋮ ⋱ ⋮ ") end + +module UnexportedOperators +function + end +function == end +end + +@testset "Parseable printing of types" begin + @test repr(typeof(print)) == "typeof(print)" + @test repr(typeof(Base.show_default)) == "typeof(Base.show_default)" + @test repr(typeof(UnexportedOperators.:+)) == "typeof($(curmod_prefix)UnexportedOperators.:+)" + @test repr(typeof(UnexportedOperators.:(==))) == "typeof($(curmod_prefix)UnexportedOperators.:(==))" + anonfn = x->2x + modname = string(@__MODULE__) + anonfn_type_repr = "getfield($modname, Symbol(\"$(typeof(anonfn).name.name)\"))" + @test repr(typeof(anonfn)) == anonfn_type_repr + @test repr(anonfn) == anonfn_type_repr * "()" + @test stringmime("text/plain", anonfn) == "$(typeof(anonfn).name.mt.name) (generic function with 1 method)" + mkclosure = x->y->x+y + clo = mkclosure(10) + @test stringmime("text/plain", clo) == "$(typeof(clo).name.mt.name) (generic function with 1 method)" +end + +let x = TypeVar(:_), y = TypeVar(:_) + @test repr(UnionAll(x, UnionAll(y, Pair{x,y}))) == "Pair{_1,_2} where _2 where _1" + @test repr(UnionAll(x, UnionAll(y, Pair{UnionAll(x,Ref{x}),y}))) == "Pair{Ref{_1} where _1,_1} where _1" + x = TypeVar(:a) + y = TypeVar(:a) + z = TypeVar(:a) + @test repr(UnionAll(z, UnionAll(x, UnionAll(y, Tuple{x,y,z})))) == "Tuple{a1,a2,a} where a2 where a1 where a" +end From fe27001391a0cb07dd6a08034c943ad0ff38cb80 Mon Sep 17 00:00:00 2001 From: Ivan Gonzalez Date: Wed, 23 Aug 2017 16:32:48 -0400 Subject: [PATCH 129/324] [DOC] Fix small typo in subarray docs (#23298) * [DOC] Fix small typo in subarray docs * More updates, make it a doctest. * linebreak * make arrays smaller --- doc/src/devdocs/subarrays.md | 37 ++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/doc/src/devdocs/subarrays.md b/doc/src/devdocs/subarrays.md index d77c7f68fb96b..e030434d25b57 100644 --- a/doc/src/devdocs/subarrays.md +++ b/doc/src/devdocs/subarrays.md @@ -36,15 +36,31 @@ cartesian, rather than linear, indexing. Consider making 2d slices of a 3d array: -```julia -S1 = view(A, :, 5, 2:6) -S2 = view(A, 5, :, 2:6) +```@meta +DocTestSetup = :(srand(1234)) +``` +```jldoctest subarray +julia> A = rand(2,3,4); + +julia> S1 = view(A, :, 1, 2:3) +2×2 SubArray{Float64,2,Array{Float64,3},Tuple{Base.Slice{Base.OneTo{Int64}},Int64,UnitRange{Int64}},false}: + 0.200586 0.066423 + 0.298614 0.956753 + +julia> S2 = view(A, 1, :, 2:3) +3×2 SubArray{Float64,2,Array{Float64,3},Tuple{Int64,Base.Slice{Base.OneTo{Int64}},UnitRange{Int64}},true}: + 0.200586 0.066423 + 0.246837 0.646691 + 0.648882 0.276021 +``` +```@meta +DocTestSetup = nothing ``` `view` drops "singleton" dimensions (ones that are specified by an `Int`), so both `S1` and `S2` are two-dimensional `SubArray`s. Consequently, the natural way to index these is with `S1[i,j]`. - To extract the value from the parent array `A`, the natural approach is to replace `S1[i,j]` -with `A[i,5,(2:6)[j]]` and `S2[i,j]` with `A[5,i,(2:6)[j]]`. +To extract the value from the parent array `A`, the natural approach is to replace `S1[i,j]` +with `A[i,1,(2:3)[j]]` and `S2[i,j]` with `A[1,i,(2:3)[j]]`. The key feature of the design of SubArrays is that this index replacement can be performed without any runtime overhead. @@ -71,13 +87,14 @@ a `Tuple` of the types of the indices for each dimension. The final one, `L`, is as a convenience for dispatch; it's a boolean that represents whether the index types support fast linear indexing. More on that later. -If in our example above `A` is a `Array{Float64, 3}`, our `S1` case above would be a `SubArray{Int64,2,Array{Int64,3},Tuple{Colon,Int64,UnitRange{Int64}},false}`. +If in our example above `A` is a `Array{Float64, 3}`, our `S1` case above would be a +`SubArray{Float64,2,Array{Float64,3},Tuple{Base.Slice{Base.OneTo{Int64}},Int64,UnitRange{Int64}},false}`. Note in particular the tuple parameter, which stores the types of the indices used to create -`S1`. Likewise, +`S1`. Likewise, -```julia-repl +```jldoctest subarray julia> S1.indexes -(Colon(),5,2:6) +(Base.Slice(Base.OneTo(2)), 1, 2:3) ``` Storing these values allows index replacement, and having the types encoded as parameters allows @@ -138,7 +155,7 @@ For `SubArray` types, the availability of efficient linear indexing is based pur of the indices, and does not depend on values like the size of the parent array. You can ask whether a given set of indices supports fast linear indexing with the internal `Base.viewindexing` function: -```julia-repl +```jldoctest subarray julia> Base.viewindexing(S1.indexes) IndexCartesian() From 31ec9da58adf695ea39c38b282337086e5fa269c Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Wed, 23 Aug 2017 16:10:32 -0500 Subject: [PATCH 130/324] Add method for get(f::Function, ::EnvHash, key) --- base/env.jl | 1 + test/env.jl | 1 + 2 files changed, 2 insertions(+) diff --git a/base/env.jl b/base/env.jl index c6010c193cc6d..582f0d823436c 100644 --- a/base/env.jl +++ b/base/env.jl @@ -77,6 +77,7 @@ similar(::EnvHash) = Dict{String,String}() getindex(::EnvHash, k::AbstractString) = access_env(k->throw(KeyError(k)), k) get(::EnvHash, k::AbstractString, def) = access_env(k->def, k) +get(f::Callable, ::EnvHash, k::AbstractString) = access_env(k->f(), k) in(k::AbstractString, ::KeyIterator{EnvHash}) = _hasenv(k) pop!(::EnvHash, k::AbstractString) = (v = ENV[k]; _unsetenv(k); v) pop!(::EnvHash, k::AbstractString, def) = haskey(ENV,k) ? pop!(ENV,k) : def diff --git a/test/env.jl b/test/env.jl index 10c75d4da66b8..2a52d3b415d4b 100644 --- a/test/env.jl +++ b/test/env.jl @@ -30,6 +30,7 @@ key = randstring(25) @test !haskey(ENV,key) @test_throws KeyError ENV[key] @test get(ENV,key,"default") == "default" +@test get(() -> "default", ENV, key) == "default" # Test for #17956 @test length(ENV) > 1 From c22b2e9033559fdea7dac942513e3b1b192d9129 Mon Sep 17 00:00:00 2001 From: kshyatt Date: Wed, 23 Aug 2017 16:53:21 -0700 Subject: [PATCH 131/324] A bunch of libgit2 docs --- base/libgit2/config.jl | 2 +- base/libgit2/remote.jl | 14 +++++++------ base/libgit2/repository.jl | 36 ++++++++++++++++++++++++++++++++++ base/libgit2/types.jl | 40 ++++++++++++++++++++++++++++++++++++++ doc/src/devdocs/libgit2.md | 1 + 5 files changed, 86 insertions(+), 7 deletions(-) diff --git a/base/libgit2/config.jl b/base/libgit2/config.jl index 2adf7a82e99b9..9f5a7549b9b48 100644 --- a/base/libgit2/config.jl +++ b/base/libgit2/config.jl @@ -68,7 +68,7 @@ end addfile(cfg::GitConfig, path::AbstractString, level::Consts.GIT_CONFIG=Consts.CONFIG_LEVEL_APP, force::Bool=false) Add an existing git configuration file located at `path` to the current -[`GitConfig`](@ref) `cfg`. If the file does not exist, it will be created. +`GitConfig` `cfg`. If the file does not exist, it will be created. `level` sets the git configuration priority level and is determined by [`Consts.GIT_CONFIG`](@ref). If `force` is `false` and a configuration for the given priority level already exists, `addfile` will error. If `force` is diff --git a/base/libgit2/remote.jl b/base/libgit2/remote.jl index df80810fb0231..f69299c96ae1d 100644 --- a/base/libgit2/remote.jl +++ b/base/libgit2/remote.jl @@ -276,6 +276,7 @@ Fetch from the specified `rmt` remote git repository, using `refspecs` to determine which remote branch(es) to fetch. The keyword arguments are: * `options`: determines the options for the fetch, e.g. whether to prune afterwards. + See [`FetchOptions`](@ref) for more information. * `msg`: a message to insert into the reflogs. """ function fetch(rmt::GitRemote, refspecs::Vector{<:AbstractString}; @@ -295,6 +296,7 @@ determine which remote branch(es) to push to. The keyword arguments are: * `force`: if `true`, a force-push will occur, disregarding conflicts. * `options`: determines the options for the push, e.g. which proxy headers to use. + See [`PushOptions`](@ref) for more information. !!! note You can add information about the push refspecs in two other ways: by setting @@ -328,8 +330,8 @@ Base.show(io::IO, rmt::GitRemote) = print(io, "GitRemote:\nRemote name: ", name( set_remote_fetch_url(repo::GitRepo, remote_name, url) set_remote_fetch_url(path::String, remote_name, url) -Set the fetch `url` for the specified `remote_name` for the GitRepo or the git repository -located at `path`. Typically git repos use "origin" as the remote name. +Set the fetch `url` for the specified `remote_name` for the [`GitRepo`](@ref) or the git repository +located at `path`. Typically git repos use `"origin"` as the remote name. """ function set_remote_fetch_url end @@ -350,8 +352,8 @@ end set_remote_push_url(repo::GitRepo, remote_name, url) set_remote_push_url(path::String, remote_name, url) -Set the push `url` for the specified `remote_name` for the GitRepo or the git repository -located at `path`. Typically git repos use "origin" as the remote name. +Set the push `url` for the specified `remote_name` for the [`GitRepo`](@ref) or the git repository +located at `path`. Typically git repos use `"origin"` as the remote name. """ function set_remote_push_url end @@ -372,8 +374,8 @@ end set_remote_url(repo::GitRepo, remote_name, url) set_remote_url(repo::String, remote_name, url) -Set both the fetch and push `url` for `remote_name` for the GitRepo or the git repository -located at `path`. Typically git repos use "origin" as the remote name. +Set both the fetch and push `url` for `remote_name` for the [`GitRepo`](@ref) or the git repository +located at `path`. Typically git repos use `"origin"` as the remote name. # Examples ```julia diff --git a/base/libgit2/repository.jl b/base/libgit2/repository.jl index 84e7c70681665..41456f0863154 100644 --- a/base/libgit2/repository.jl +++ b/base/libgit2/repository.jl @@ -365,6 +365,19 @@ function reset!(repo::GitRepo, obj::GitObject, mode::Cint; return head_oid(repo) end +""" + clone(repo_url::AbstractString, repo_path::AbstractString, clone_opts::CloneOptions) + +Clone the remote repository at `repo_url` (which can be a remote URL or a path on the local +filesystem) to `repo_path` (which must be a path on the local filesystem). Options for the +clone, such as whether to perform a bare clone or not, are set by [`CloneOptions`](@ref). + +# Examples +```julia +repo_url = "https://github.com/JuliaLang/Example.jl" +repo = LibGit2.clone(repo_url, "/home/me/projects/Example") +``` +""" function clone(repo_url::AbstractString, repo_path::AbstractString, clone_opts::CloneOptions) clone_opts_ref = Ref(clone_opts) @@ -375,6 +388,29 @@ function clone(repo_url::AbstractString, repo_path::AbstractString, return GitRepo(repo_ptr_ptr[]) end +""" + fetchheads(repo::GitRepo) -> Vector{FetchHead} + +Return the list of all the fetch heads for `repo`, each represented as a [`FetchHead`](@ref), +including their names, URLs, and merge statuses. + +# Examples +```julia-repl +julia> fetch_heads = LibGit2.fetchheads(repo); + +julia> fetch_heads[1].name +"refs/heads/master" + +julia> fetch_heads[1].ismerge +true + +julia> fetch_heads[2].name +"refs/heads/test_branch" + +julia> fetch_heads[2].ismerge +false +``` +""" function fetchheads(repo::GitRepo) fhr = Ref{Vector{FetchHead}}(FetchHead[]) ffcb = fetchhead_foreach_cb() diff --git a/base/libgit2/types.jl b/base/libgit2/types.jl index dd20f9dfe48d9..e8d111605d9d3 100644 --- a/base/libgit2/types.jl +++ b/base/libgit2/types.jl @@ -236,6 +236,20 @@ end LibGit2.FetchOptions Matches the [`git_fetch_options`](https://libgit2.github.com/libgit2/#HEAD/type/git_fetch_options) struct. + +The fields represent: + * `version`: version of the struct in use, in case this changes later. For now, always `1`. + * `callbacks`: remote callbacks to use during the fetch. + * `prune`: whether to perform a prune after the fetch or not. The default is to + use the setting from the `GitConfig`. + * `update_fetchhead`: whether to update the [`FetchHead`](@ref) after the fetch. + The default is to perform the update, which is the normal git behavior. + * `download_tags`: whether to download tags present at the remote or not. The default + is to request the tags for objects which are being downloaded anyway from the server. + * `proxy_opts`: options for connecting to the remote through a proxy. See [`ProxyOptions`](@ref). + Only present on libgit2 versions newer than 0.25. + * `custom_headers`: any extra headers needed for the fetch. Only present on libgit2 versions + newer than 0.24. """ @kwdef struct FetchOptions version::Cuint = 1 @@ -255,6 +269,23 @@ end LibGit2.CloneOptions Matches the [`git_clone_options`](https://libgit2.github.com/libgit2/#HEAD/type/git_clone_options) struct. + +The fields represent: + * `version`: version of the struct in use, in case this changes later. For now, always `1`. + * `checkout_opts`: The options for performing the checkout of the remote as part of the clone. + * `fetch_opts`: The options for performing the pre-checkout fetch of the remote as part of the clone. + * `bare`: If `0`, clone the full remote repository. If non-zero, perform a bare clone, in which + there is no local copy of the source files in the repository and the [`gitdir`](@ref) and [`workdir`](@ref) + are the same. + * `localclone`: Flag whether to clone a local object database or do a fetch. The default is to let git decide. + It will not use the git-aware transport for a local clone, but will use it for URLs which begin with `file://`. + * `checkout_branch`: The name of the branch to checkout. If an empty string, the default branch of the + remote will be checked out. + * `repository_cb`: An optional callback which will be used to create the *new* repository into which + the clone is made. + * `repository_cb_payload`: The payload for the repository callback. + * `remote_cb`: An optional callback used to create the [`GitRemote`](@ref) before making the clone from it. + * `remote_cb_payload`: The payload for the remote callback. """ @kwdef struct CloneOptions version::Cuint = 1 @@ -582,6 +613,15 @@ end Contains the information about HEAD during a fetch, including the name and URL of the branch fetched from, the oid of the HEAD, and whether the fetched HEAD has been merged locally. + +The fields represent: + * `name`: The name in the local reference database of the fetch head, for example, + `"refs/heads/master"`. + * `url`: The URL of the fetch head. + * `oid`: The [`GitHash`](@ref) of the tip of the fetch head. + * `ismerge`: Boolean flag indicating whether the changes at the + remote have been merged into the local copy yet or not. If `true`, the local + copy is up to date with the remote fetch head. """ struct FetchHead name::String diff --git a/doc/src/devdocs/libgit2.md b/doc/src/devdocs/libgit2.md index dc7ca8b54aef3..91d8fca17e2d3 100644 --- a/doc/src/devdocs/libgit2.md +++ b/doc/src/devdocs/libgit2.md @@ -70,6 +70,7 @@ Base.LibGit2.default_signature Base.LibGit2.delete_branch Base.LibGit2.diff_files Base.LibGit2.fetch +Base.LibGit2.fetchheads Base.LibGit2.fetch_refspecs Base.LibGit2.fetchhead_foreach_cb Base.LibGit2.merge!(::Base.LibGit2.GitRepo; ::Any...) From 6378d7d7dd6647e0c930ee967df27f492aaac2c0 Mon Sep 17 00:00:00 2001 From: Andras Niedermayer Date: Thu, 24 Aug 2017 02:58:06 +0200 Subject: [PATCH 132/324] Make behavior of `^(::Matrix{<:Integer},::Integer)` consistent with behavior of `^(::Integer, ::Integer)` (#23368) * Make `^(::Matrix{S},::T) where {S<:Integer, T<:Integer}` type stable. Also enure that the same promotion rules as `^(::S, ::T)` * Make `^(::Matrix{S},::T) where {S<:Integer, T<:Integer}` type stable. Also enure that the same promotion rules as `^(::S, ::T)` * fix typo * fixed typo * Adjust tests for `^(::Matrix{Int64},Int64)` for symmetric/hermitian matrices * avoid code duplication by using the same code for `^(::Matrix{<:Integer},::Integer)` as for `^(::Integer,::Integer)` * added specialized domain error message for `^(::Matrix{<:Integer},::Integer)`, added `@inferred` and comments to test * added specialized domain error message for `^(::Matrix{<:Integer},::Integer)`, added `@inferred` and comments to test * reverted change in faq * different throw_domerr_powbysq for Any and Integer, simplified conversion * Adjusted "Breaking changes" section of news * split news item into two bullets * remove unnecessary Base. qualification * remove more unnecessary Base. qualifications --- NEWS.md | 8 ++++++++ base/intfuncs.jl | 25 ++++++++++++++++--------- base/linalg/dense.jl | 10 ++++++++-- test/linalg/dense.jl | 23 +++++++++++++++++++++++ test/linalg/symmetric.jl | 12 ++++++++++-- 5 files changed, 65 insertions(+), 13 deletions(-) diff --git a/NEWS.md b/NEWS.md index 7aebe9b19a7bb..fd41f79b99772 100644 --- a/NEWS.md +++ b/NEWS.md @@ -155,6 +155,14 @@ This section lists changes that do not have deprecation warnings. * Worker-worker connections are setup lazily for an `:all_to_all` topology. Use keyword arg `lazy=false` to force all connections to be setup during a `addprocs` call. ([#22814]) + * `^(A::AbstractMatrix{<:Integer}, p::Integer)` now throws a `DomainError` + if `p < 0`, unless `A == one(A)` or `A == -one(A)` (same as for + `^(A::Integer, p::Integer)`) ([#23366]). + + * `^(A::AbstractMatrix{<:Integer}, p::Integer)` now promotes the element type in the same + way as `^(A::Integer, p::Integer)`. This means, for instance, that `[1 1; 0 1]^big(1)` + will return a `Matrix{BigInt}` instead of a `Matrix{Int}` ([#23366]). + Library improvements -------------------- diff --git a/base/intfuncs.jl b/base/intfuncs.jl index 7c33bc407b027..e34986037b5fa 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -159,12 +159,19 @@ end invmod(n::Integer, m::Integer) = invmod(promote(n,m)...) # ^ for any x supporting * -to_power_type(x::Number) = oftype(x*x, x) -to_power_type(x) = x -@noinline throw_domerr_powbysq(p) = throw(DomainError(p, +to_power_type(x) = convert(promote_op(*, typeof(x), typeof(x)), x) +@noinline throw_domerr_powbysq(::Any, p) = throw(DomainError(p, string("Cannot raise an integer x to a negative power ", p, '.', - "\nMake x a float by adding a zero decimal (e.g., 2.0^$p instead ", - "of 2^$p), or write 1/x^$(-p), float(x)^$p, or (x//1)^$p"))) + "\nConvert input to float."))) +@noinline throw_domerr_powbysq(::Integer, p) = throw(DomainError(p, + string("Cannot raise an integer x to a negative power ", p, '.', + "\nMake x a float by adding a zero decimal (e.g., 2.0^$p instead ", + "of 2^$p), or write 1/x^$(-p), float(x)^$p, or (x//1)^$p"))) +@noinline throw_domerr_powbysq(::AbstractMatrix, p) = throw(DomainError(p, + string("Cannot raise an integer matrix x to a negative power ", p, '.', + "\nMake x a float matrix by adding a zero decimal ", + "(e.g., [2.0 1.0;1.0 0.0]^$p instead ", + "of [2 1;1 0]^$p), or write float(x)^$p or Rational.(x)^$p"))) function power_by_squaring(x_, p::Integer) x = to_power_type(x_) if p == 1 @@ -174,9 +181,9 @@ function power_by_squaring(x_, p::Integer) elseif p == 2 return x*x elseif p < 0 - x == 1 && return copy(x) - x == -1 && return iseven(p) ? one(x) : copy(x) - throw_domerr_powbysq(p) + isone(x) && return copy(x) + isone(-x) && return iseven(p) ? one(x) : copy(x) + throw_domerr_powbysq(x, p) end t = trailing_zeros(p) + 1 p >>= t @@ -196,7 +203,7 @@ function power_by_squaring(x_, p::Integer) end power_by_squaring(x::Bool, p::Unsigned) = ((p==0) | x) function power_by_squaring(x::Bool, p::Integer) - p < 0 && !x && throw_domerr_powbysq(p) + p < 0 && !x && throw_domerr_powbysq(x, p) return (p==0) | x end diff --git a/base/linalg/dense.jl b/base/linalg/dense.jl index 6eab8b46d145d..9b150df45c56f 100644 --- a/base/linalg/dense.jl +++ b/base/linalg/dense.jl @@ -357,9 +357,15 @@ kron(a::AbstractMatrix, b::AbstractVector) = kron(a, reshape(b, length(b), 1)) kron(a::AbstractVector, b::AbstractMatrix) = kron(reshape(a, length(a), 1), b) # Matrix power -(^)(A::AbstractMatrix, p::Integer) = p < 0 ? Base.power_by_squaring(inv(A), -p) : Base.power_by_squaring(A, p) +(^)(A::AbstractMatrix, p::Integer) = p < 0 ? power_by_squaring(inv(A), -p) : power_by_squaring(A, p) +function (^)(A::AbstractMatrix{T}, p::Integer) where T<:Integer + # make sure that e.g. [1 1;1 0]^big(3) + # gets promotes in a similar way as 2^big(3) + TT = promote_op(^, T, typeof(p)) + return power_by_squaring(convert(AbstractMatrix{TT}, A), p) +end function integerpow(A::AbstractMatrix{T}, p) where T - TT = Base.promote_op(^, T, typeof(p)) + TT = promote_op(^, T, typeof(p)) return (TT == T ? A : copy!(similar(A, TT), A))^Integer(p) end function schurpow(A::AbstractMatrix, p) diff --git a/test/linalg/dense.jl b/test/linalg/dense.jl index 1a15a481d407e..b10f1f606f2b1 100644 --- a/test/linalg/dense.jl +++ b/test/linalg/dense.jl @@ -582,6 +582,29 @@ end end end +@testset "issue #23366 (Int Matrix to Int power)" begin + @testset "Tests for $elty" for elty in (Int128, Int16, Int32, Int64, Int8, + UInt128, UInt16, UInt32, UInt64, UInt8, + BigInt) + @test_throws DomainError elty[1 1;1 0]^-2 + @test (@inferred elty[1 1;1 0]^2) == elty[2 1;1 1] + I_ = elty[1 0;0 1] + @test I_^-1 == I_ + if !(elty<:Unsigned) + @test (@inferred (-I_)^-1) == -I_ + @test (@inferred (-I_)^-2) == I_ + end + # make sure that type promotion for ^(::Matrix{<:Integer}, ::Integer) + # is analogous to type promotion for ^(::Integer, ::Integer) + # e.g. [1 1;1 0]^big(10000) should return Matrix{BigInt}, the same + # way as 2^big(10000) returns BigInt + for elty2 = (Int64, BigInt) + TT = Base.promote_op(^, elty, elty2) + @test (@inferred elty[1 1;1 0]^elty2(1))::Matrix{TT} == [1 1;1 0] + end + end +end + @testset "Least squares solutions" begin a = [ones(20) 1:20 1:20] b = reshape(eye(8, 5), 20, 2) diff --git a/test/linalg/symmetric.jl b/test/linalg/symmetric.jl index 296d599390d89..df89577dcb1e1 100644 --- a/test/linalg/symmetric.jl +++ b/test/linalg/symmetric.jl @@ -245,10 +245,18 @@ end @testset "pow" begin # Integer power @test (asym)^2 ≈ (Symmetric(asym)^2)::Symmetric - @test (asym)^-2 ≈ (Symmetric(asym)^-2)::Symmetric + if eltya <: Integer && !isone(asym) && !isone(-asym) + @test_throws DomainError (asym)^-2 + else + @test (asym)^-2 ≈ (Symmetric(asym)^-2)::Symmetric + end @test (aposs)^2 ≈ (Symmetric(aposs)^2)::Symmetric @test (aherm)^2 ≈ (Hermitian(aherm)^2)::Hermitian - @test (aherm)^-2 ≈ (Hermitian(aherm)^-2)::Hermitian + if eltya <: Integer && !isone(aherm) && !isone(-aherm) + @test_throws DomainError (aherm)^-2 + else + @test (aherm)^-2 ≈ (Hermitian(aherm)^-2)::Hermitian + end @test (apos)^2 ≈ (Hermitian(apos)^2)::Hermitian # integer floating point power @test (asym)^2.0 ≈ (Symmetric(asym)^2.0)::Symmetric From 98c7a06f1a90cd7ee17d280ccb0d871c497aa025 Mon Sep 17 00:00:00 2001 From: Harmen Stoppels Date: Thu, 24 Aug 2017 02:59:20 +0200 Subject: [PATCH 133/324] Fix LU factorization in-place operations (#22774) * Add tests for in-place operations of A_ldiv_B!(y, LU, x) for LU factorizations * Do Ax_ldiv_B! for LU factorizations in-place * Remove incorrect condition in LU test. The transpose should be tested for complex numbers, and might be skipped for real numbers; not the other way around. For clarity just test both unconditionally. * Get rid of allocations, apply the (inverse) permutation in-place --- base/linalg/lu.jl | 57 ++++++++++++++++++++++++++++++++--------------- test/linalg/lu.jl | 27 ++++++++++++++++++---- 2 files changed, 62 insertions(+), 22 deletions(-) diff --git a/base/linalg/lu.jl b/base/linalg/lu.jl index 8e6630a00bd41..8e0c0451f41b4 100644 --- a/base/linalg/lu.jl +++ b/base/linalg/lu.jl @@ -241,34 +241,55 @@ function show(io::IO, F::LU) print(io, "\nsuccessful: $(issuccess(F))") end +_apply_ipiv!(A::LU, B::StridedVecOrMat) = _ipiv!(A, 1 : length(A.ipiv), B) +_apply_inverse_ipiv!(A::LU, B::StridedVecOrMat) = _ipiv!(A, length(A.ipiv) : -1 : 1, B) + +function _ipiv!(A::LU, order::OrdinalRange, B::StridedVecOrMat) + for i = order + if i != A.ipiv[i] + _swap_rows!(B, i, A.ipiv[i]) + end + end + B +end + +function _swap_rows!(B::StridedVector, i::Integer, j::Integer) + B[i], B[j] = B[j], B[i] + B +end + +function _swap_rows!(B::StridedMatrix, i::Integer, j::Integer) + for col = 1 : size(B, 2) + B[i,col], B[j,col] = B[j,col], B[i,col] + end + B +end + A_ldiv_B!(A::LU{T,<:StridedMatrix}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = @assertnonsingular LAPACK.getrs!('N', A.factors, A.ipiv, B) A.info -A_ldiv_B!(A::LU{<:Any,<:StridedMatrix}, b::StridedVector) = - A_ldiv_B!(UpperTriangular(A.factors), - A_ldiv_B!(UnitLowerTriangular(A.factors), b[ipiv2perm(A.ipiv, length(b))])) -A_ldiv_B!(A::LU{<:Any,<:StridedMatrix}, B::StridedMatrix) = - A_ldiv_B!(UpperTriangular(A.factors), - A_ldiv_B!(UnitLowerTriangular(A.factors), B[ipiv2perm(A.ipiv, size(B, 1)),:])) + +function A_ldiv_B!(A::LU{<:Any,<:StridedMatrix}, B::StridedVecOrMat) + _apply_ipiv!(A, B) + A_ldiv_B!(UpperTriangular(A.factors), A_ldiv_B!(UnitLowerTriangular(A.factors), B)) +end At_ldiv_B!(A::LU{T,<:StridedMatrix}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = @assertnonsingular LAPACK.getrs!('T', A.factors, A.ipiv, B) A.info -At_ldiv_B!(A::LU{<:Any,<:StridedMatrix}, b::StridedVector) = - At_ldiv_B!(UnitLowerTriangular(A.factors), - At_ldiv_B!(UpperTriangular(A.factors), b))[invperm(ipiv2perm(A.ipiv, length(b)))] -At_ldiv_B!(A::LU{<:Any,<:StridedMatrix}, B::StridedMatrix) = - At_ldiv_B!(UnitLowerTriangular(A.factors), - At_ldiv_B!(UpperTriangular(A.factors), B))[invperm(ipiv2perm(A.ipiv, size(B,1))),:] + +function At_ldiv_B!(A::LU{<:Any,<:StridedMatrix}, B::StridedVecOrMat) + At_ldiv_B!(UnitLowerTriangular(A.factors), At_ldiv_B!(UpperTriangular(A.factors), B)) + _apply_inverse_ipiv!(A, B) +end Ac_ldiv_B!(F::LU{T,<:StridedMatrix}, B::StridedVecOrMat{T}) where {T<:Real} = At_ldiv_B!(F, B) Ac_ldiv_B!(A::LU{T,<:StridedMatrix}, B::StridedVecOrMat{T}) where {T<:BlasComplex} = @assertnonsingular LAPACK.getrs!('C', A.factors, A.ipiv, B) A.info -Ac_ldiv_B!(A::LU{<:Any,<:StridedMatrix}, b::StridedVector) = - Ac_ldiv_B!(UnitLowerTriangular(A.factors), - Ac_ldiv_B!(UpperTriangular(A.factors), b))[invperm(ipiv2perm(A.ipiv, length(b)))] -Ac_ldiv_B!(A::LU{<:Any,<:StridedMatrix}, B::StridedMatrix) = - Ac_ldiv_B!(UnitLowerTriangular(A.factors), - Ac_ldiv_B!(UpperTriangular(A.factors), B))[invperm(ipiv2perm(A.ipiv, size(B,1))),:] + +function Ac_ldiv_B!(A::LU{<:Any,<:StridedMatrix}, B::StridedVecOrMat) + Ac_ldiv_B!(UnitLowerTriangular(A.factors), Ac_ldiv_B!(UpperTriangular(A.factors), B)) + _apply_inverse_ipiv!(A, B) +end At_ldiv_Bt(A::LU{T,<:StridedMatrix}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = @assertnonsingular LAPACK.getrs!('T', A.factors, A.ipiv, transpose(B)) A.info diff --git a/test/linalg/lu.jl b/test/linalg/lu.jl index 6730a6cd07fd6..dd5a84b7904d1 100644 --- a/test/linalg/lu.jl +++ b/test/linalg/lu.jl @@ -84,11 +84,30 @@ dimg = randn(n)/2 @test norm(a*(lua\c) - c, 1) < ε*κ*n # c is a vector @test norm(a'*(lua'\c) - c, 1) < ε*κ*n # c is a vector @test AbstractArray(lua) ≈ a - if eltya <: Real && eltyb <: Real - @test norm(a.'*(lua.'\b) - b,1) < ε*κ*n*2 # Two because the right hand side has two columns - @test norm(a.'*(lua.'\c) - c,1) < ε*κ*n - end + @test norm(a.'*(lua.'\b) - b,1) < ε*κ*n*2 # Two because the right hand side has two columns + @test norm(a.'*(lua.'\c) - c,1) < ε*κ*n end + + # Test whether Ax_ldiv_B!(y, LU, x) indeed overwrites y + resultT = typeof(oneunit(eltyb) / oneunit(eltya)) + + b_dest = similar(b, resultT) + c_dest = similar(c, resultT) + + A_ldiv_B!(b_dest, lua, b) + A_ldiv_B!(c_dest, lua, c) + @test norm(b_dest - lua \ b, 1) < ε*κ*2n + @test norm(c_dest - lua \ c, 1) < ε*κ*n + + At_ldiv_B!(b_dest, lua, b) + At_ldiv_B!(c_dest, lua, c) + @test norm(b_dest - lua.' \ b, 1) < ε*κ*2n + @test norm(c_dest - lua.' \ c, 1) < ε*κ*n + + Ac_ldiv_B!(b_dest, lua, b) + Ac_ldiv_B!(c_dest, lua, c) + @test norm(b_dest - lua' \ b, 1) < ε*κ*2n + @test norm(c_dest - lua' \ c, 1) < ε*κ*n end if eltya <: BlasFloat && eltyb <: BlasFloat e = rand(eltyb,n,n) From 9f04577f4316f0ae923a102a40c7b828b026eda1 Mon Sep 17 00:00:00 2001 From: Sascha Mann Date: Thu, 24 Aug 2017 15:02:37 +0200 Subject: [PATCH 134/324] Remove error thrown by joinpath (fixes #20912) (#22978) `joinpath` now returns the second path instead of throwing an `ArgumentError` if the drives of the two arguments do not match. --- NEWS.md | 5 +++++ base/path.jl | 9 +++++---- test/path.jl | 10 ++++++++++ 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/NEWS.md b/NEWS.md index fd41f79b99772..eca1009dfe373 100644 --- a/NEWS.md +++ b/NEWS.md @@ -155,6 +155,11 @@ This section lists changes that do not have deprecation warnings. * Worker-worker connections are setup lazily for an `:all_to_all` topology. Use keyword arg `lazy=false` to force all connections to be setup during a `addprocs` call. ([#22814]) + * In `joinpath(a, b)` on Windows, if the drive specifications of `a` and `b` do not match, + `joinpath` now returns `b` instead of throwing an `ArgumentError`. `joinpath(path...)` is + defined to be left associative, so if any argument has a drive path which does not match + the drive of the join of the preceding paths, the prior ones are dropped. ([#20912]) + * `^(A::AbstractMatrix{<:Integer}, p::Integer)` now throws a `DomainError` if `p < 0`, unless `A == one(A)` or `A == -one(A)` (same as for `^(A::Integer, p::Integer)`) ([#23366]). diff --git a/base/path.jl b/base/path.jl index eb118b3d78541..323e33eb3dbaf 100644 --- a/base/path.jl +++ b/base/path.jl @@ -200,12 +200,13 @@ joinpath(a::AbstractString) = a """ joinpath(parts...) -> AbstractString -Join path components into a full path. If some argument is an absolute path, then prior -components are dropped. +Join path components into a full path. If some argument is an absolute path or +(on Windows) has a drive specification that doesn't match the drive computed for +the join of the preceding paths, then prior components are dropped. # Examples ```jldoctest -julia> joinpath("/home/myuser","example.jl") +julia> joinpath("/home/myuser", "example.jl") "/home/myuser/example.jl" ``` """ @@ -215,7 +216,7 @@ function joinpath(a::String, b::String) isabspath(b) && return b A, a = splitdrive(a) B, b = splitdrive(b) - !isempty(B) && A != B && throw(ArgumentError("drive mismatch: $A$a $B$b")) + !isempty(B) && A != B && return string(B,b) C = isempty(B) ? A : B isempty(a) ? string(C,b) : ismatch(path_separator_re, a[end:end]) ? string(C,a,b) : diff --git a/test/path.jl b/test/path.jl index 3a27bcb271ab6..2b6ac751660c3 100644 --- a/test/path.jl +++ b/test/path.jl @@ -27,6 +27,16 @@ for S in (String, GenericString) @test joinpath(S("foo"), S(homedir())) == homedir() @test joinpath(S(abspath("foo")), S(homedir())) == homedir() + if Sys.iswindows() + @test joinpath(S("foo"),S("bar:baz")) == "bar:baz" + @test joinpath(S("C:"),S("foo"),S("D:"),S("bar")) == "D:bar" + @test joinpath(S("C:"),S("foo"),S("D:bar"),S("baz")) == "D:bar$(sep)baz" + elseif Sys.isunix() + @test joinpath(S("foo"),S("bar:baz")) == "foo$(sep)bar:baz" + @test joinpath(S("C:"),S("foo"),S("D:"),S("bar")) == "C:$(sep)foo$(sep)D:$(sep)bar" + @test joinpath(S("C:"),S("foo"),S("D:"),S("bar"),S("baz")) == "C:$(sep)foo$(sep)D:$(sep)bar$(sep)baz" + end + @test normpath(S(joinpath("."))) == "." @test normpath(S(joinpath(".."))) == ".." @test normpath(S(joinpath("..","."))) == ".." From 3b767aa43a746c96df6d441d403e1d73761868f4 Mon Sep 17 00:00:00 2001 From: Iblis Lin Date: Thu, 24 Aug 2017 21:45:55 +0800 Subject: [PATCH 135/324] prevent versioninfo(verbose=true) init Pkg accidentally (#23075) * prevent versioninfo(verbose=true) init Pkg accidentally. * Update interactiveutil.jl remove blank line * test/pkg: add test cases for versioninfo(verbose=true) Make sure that it do not raise error if JULIA_PKGDIR is set. * test/version: move versioninfo tests from test/pkg --- base/interactiveutil.jl | 9 +++++++-- test/pkg.jl | 8 -------- test/version.jl | 18 ++++++++++++++++++ 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/base/interactiveutil.jl b/base/interactiveutil.jl index aa0ad2e7ffa5b..3f6a5dce96192 100644 --- a/base/interactiveutil.jl +++ b/base/interactiveutil.jl @@ -330,8 +330,13 @@ function versioninfo(io::IO=STDOUT; verbose::Bool=false, packages::Bool=false) if packages || verbose println(io, "Packages:") println(io, " Package Directory: ", Pkg.dir()) - println(io, " Package Status:") - Pkg.status(io) + print(io, " Package Status:") + if isdir(Pkg.dir()) + println(io, "") + Pkg.status(io) + else + println(io, " no packages installed") + end end end diff --git a/test/pkg.jl b/test/pkg.jl index 3df2f55032942..288cc657c0146 100644 --- a/test/pkg.jl +++ b/test/pkg.jl @@ -65,14 +65,6 @@ temp_pkg_dir() do @test_throws PkgError Pkg.installed("MyFakePackage") @test Pkg.installed("Example") === nothing - # check that versioninfo(io; verbose=true) doesn't error and produces some output - # (done here since it calls Pkg.status which might error or clone metadata) - buf = PipeBuffer() - versioninfo(buf, verbose=true) - ver = read(buf, String) - @test startswith(ver, "Julia Version $VERSION") - @test contains(ver, "Environment:") - # Check that setprotocol! works. begin try diff --git a/test/version.jl b/test/version.jl index 6bc50a54843b7..f1e94fd0df477 100644 --- a/test/version.jl +++ b/test/version.jl @@ -279,3 +279,21 @@ for t = 1:1_000 @test (v ∈ a && v ∈ b) ? (v ∈ i) : (v ∉ i) end end + +# PR #23075 +@testset "versioninfo" begin + # check that versioninfo(io; verbose=true) doesn't error, produces some output + # and doesn't invoke Pkg.status which will error if JULIA_PKGDIR is set + mktempdir() do dir + withenv("JULIA_PKGDIR" => dir) do + buf = PipeBuffer() + versioninfo(buf, verbose=true) + ver = read(buf, String) + @test startswith(ver, "Julia Version $VERSION") + @test contains(ver, "Environment:") + @test contains(ver, "Package Status:") + @test contains(ver, "no packages installed") + @test isempty(readdir(dir)) + end + end +end From 4ae9389637520094cf2c4272014845e8e97a45dc Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Wed, 28 Jun 2017 07:29:49 +0200 Subject: [PATCH 136/324] Make addOptimizationPasses target-agnostic. --- src/codegen.cpp | 3 +-- src/jitlayers.cpp | 14 +++++++++++--- src/jitlayers.h | 1 + 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 3a4362653b471..ca125ac26d66d 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -41,7 +41,6 @@ #include #include #include -#include #include // IR building @@ -6551,7 +6550,7 @@ static void init_julia_llvm_env(Module *m) add_named_global(jlgetworld_global, &jl_world_counter); jl_globalPM = new legacy::PassManager(); - jl_globalPM->add(new TargetLibraryInfoWrapperPass(Triple(jl_TargetMachine->getTargetTriple()))); + addTargetPasses(jl_globalPM, jl_TargetMachine); addOptimizationPasses(jl_globalPM, jl_options.opt_level); } diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index 83fd0f31852c9..656e6fe2398bf 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -92,7 +92,14 @@ void jl_init_jit(Type *T_pjlvalue_) // Except for parts of this file which were copied from LLVM, under the UIUC license (marked below). -// this defines the set of optimization passes defined for Julia at various optimization levels +void addTargetPasses(legacy::PassManagerBase *PM, TargetMachine *TM) +{ + PM->add(new TargetLibraryInfoWrapperPass(Triple(TM->getTargetTriple()))); + PM->add(createTargetTransformInfoWrapperPass(TM->getTargetIRAnalysis())); +} + +// this defines the set of optimization passes defined for Julia at various optimization levels. +// it assumes that the TLI and TTI wrapper passes have already been added. void addOptimizationPasses(legacy::PassManagerBase *PM, int opt_level) { #ifdef JL_DEBUG_BUILD @@ -132,7 +139,6 @@ void addOptimizationPasses(legacy::PassManagerBase *PM, int opt_level) return; } PM->add(createPropagateJuliaAddrspaces()); - PM->add(createTargetTransformInfoWrapperPass(jl_TargetMachine->getTargetIRAnalysis())); PM->add(createTypeBasedAAWrapperPass()); if (jl_options.opt_level >= 3) { PM->add(createBasicAAWrapperPass()); @@ -485,6 +491,7 @@ JuliaOJIT::JuliaOJIT(TargetMachine &TM) CompilerT(this) ) { + addTargetPasses(&PM, &TM); addOptimizationPasses(&PM, jl_generating_output() ? 0 : jl_options.opt_level); if (TM.addPassesToEmitMC(PM, Ctx, ObjStream)) llvm_unreachable("Target does not support MC emission."); @@ -1137,7 +1144,7 @@ void jl_dump_native(const char *bc_fname, const char *unopt_bc_fname, const char )); legacy::PassManager PM; - PM.add(new TargetLibraryInfoWrapperPass(Triple(TM->getTargetTriple()))); + addTargetPasses(&PM, TM.get()); // set up optimization passes std::unique_ptr unopt_bc_OS; @@ -1269,6 +1276,7 @@ class JuliaPipeline : public Pass { (void)jl_init_llvm(); PMTopLevelManager *TPM = Stack.top()->getTopLevelManager(); TPMAdapter Adapter(TPM); + addTargetPasses(&Adapter, jl_TargetMachine); addOptimizationPasses(&Adapter, OptLevel); } JuliaPipeline() : Pass(PT_PassManager, ID) {} diff --git a/src/jitlayers.h b/src/jitlayers.h index 96e0c3bc19f69..5ebfad68ad093 100644 --- a/src/jitlayers.h +++ b/src/jitlayers.h @@ -42,6 +42,7 @@ extern size_t jltls_offset_idx; typedef struct {Value *gv; int32_t index;} jl_value_llvm; // uses 1-based indexing +void addTargetPasses(legacy::PassManagerBase *PM, TargetMachine *TM); void addOptimizationPasses(legacy::PassManagerBase *PM, int opt_level); void* jl_emit_and_add_to_shadow(GlobalVariable *gv, void *gvarinit = NULL); GlobalVariable *jl_emit_sysimg_slot(Module *m, Type *typ, const char *name, From 5a002bff411ec701fdc102a4a3e501340169c080 Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Thu, 24 Aug 2017 08:33:06 -0700 Subject: [PATCH 137/324] Doc checkout methods and type (#23410) * Doc checkout methods and type --- base/libgit2/repository.jl | 24 ++++++++++++++++++++++++ base/libgit2/types.jl | 28 ++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/base/libgit2/repository.jl b/base/libgit2/repository.jl index 41456f0863154..38a326f5b6823 100644 --- a/base/libgit2/repository.jl +++ b/base/libgit2/repository.jl @@ -307,6 +307,13 @@ function Base.show(io::IO, result::GitDescribeResult) println(io, fmt_desc) end +""" + checkout_tree(repo::GitRepo, obj::GitObject; options::CheckoutOptions = CheckoutOptions()) + +Update the working tree and index of `repo` to match the tree pointed to by `obj`. +`obj` can be a commit, a tag, or a tree. `options` controls how the checkout will +be performed. See [`CheckoutOptions`](@ref) for more information. +""" function checkout_tree(repo::GitRepo, obj::GitObject; options::CheckoutOptions = CheckoutOptions()) @check ccall((:git_checkout_tree, :libgit2), Cint, @@ -314,6 +321,13 @@ function checkout_tree(repo::GitRepo, obj::GitObject; repo.ptr, obj.ptr, Ref(options)) end +""" + checkout_index(repo::GitRepo, idx::Nullable{GitIndex} = Nullable{GitIndex}(); options::CheckoutOptions = CheckoutOptions()) + +Update the working tree of `repo` to match the index `idx`. If `idx` is null, the +index of `repo` will be used. `options` controls how the checkout will be performed. +See [`CheckoutOptions`](@ref) for more information. +""" function checkout_index(repo::GitRepo, idx::Nullable{GitIndex} = Nullable{GitIndex}(); options::CheckoutOptions = CheckoutOptions()) @check ccall((:git_checkout_index, :libgit2), Cint, @@ -323,6 +337,16 @@ function checkout_index(repo::GitRepo, idx::Nullable{GitIndex} = Nullable{GitInd Ref(options)) end +""" + checkout_head(repo::GitRepo; options::CheckoutOptions = CheckoutOptions()) + +Update the index and working tree of `repo` to match the commit pointed to by HEAD. +`options` controls how the checkout will be performed. See [`CheckoutOptions`](@ref) for more information. + +!!! warning + *Do not* use this function to switch branches! Doing so will cause checkout + conflicts. +""" function checkout_head(repo::GitRepo; options::CheckoutOptions = CheckoutOptions()) @check ccall((:git_checkout_head, :libgit2), Cint, (Ptr{Void}, Ptr{CheckoutOptions}), diff --git a/base/libgit2/types.jl b/base/libgit2/types.jl index e8d111605d9d3..2dad2ab662482 100644 --- a/base/libgit2/types.jl +++ b/base/libgit2/types.jl @@ -120,6 +120,34 @@ end LibGit2.CheckoutOptions Matches the [`git_checkout_options`](https://libgit2.github.com/libgit2/#HEAD/type/git_checkout_options) struct. + +The fields represent: + * `version`: version of the struct in use, in case this changes later. For now, always `1`. + * `checkout_strategy`: determine how to handle conflicts and whether to force the + checkout/recreate missing files. + * `disable_filters`: if nonzero, do not apply filters like CLRF (to convert file newlines between UNIX and DOS). + * `dir_mode`: read/write/access mode for any directories involved in the checkout. Default is `0755`. + * `file_mode`: read/write/access mode for any files involved in the checkout. + Default is `0755` or `0644`, depeding on the blob. + * `file_open_flags`: bitflags used to open any files during the checkout. + * `notify_flags`: Flags for what sort of conflicts the user should be notified about. + * `notify_cb`: An optional callback function to notify the user if a checkout conflict occurs. + If this function returns a non-zero value, the checkout will be cancelled. + * `notify_payload`: Payload for the notify callback function. + * `progress_cb`: An optional callback function to display checkout progress. + * `progress_payload`: Payload for the progress callback. + * `paths`: If not empty, describes which paths to search during the checkout. + If empty, the checkout will occur over all files in the repository. + * `baseline`: Expected content of the [`workdir`](@ref), captured in a (pointer to a) + [`GitTree`](@ref). Defaults to the state of the tree at HEAD. + * `baseline_index`: Expected content of the [`workdir`](@ref), captured in a (pointer to a) + `GitIndex`. Defaults to the state of the index at HEAD. + * `target_directory`: If not empty, checkout to this directory instead of the `workdir`. + * `ancestor_label`: In case of conflicts, the name of the common ancestor side. + * `our_label`: In case of conflicts, the name of "our" side. + * `their_label`: In case of conflicts, the name of "their" side. + * `perfdata_cb`: An optional callback function to display performance data. + * `perfdata_payload`: Payload for the performance callback. """ @kwdef struct CheckoutOptions version::Cuint = 1 From 2767355c86750287cd787608fc1e2803ba38bd91 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Thu, 24 Aug 2017 18:01:35 +0200 Subject: [PATCH 138/324] REPL: fix newline indentation when point before 1st non-space (#23402) --- base/repl/LineEdit.jl | 3 ++- test/lineedit.jl | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/base/repl/LineEdit.jl b/base/repl/LineEdit.jl index f803325cdbaf5..89e3bbb367b53 100644 --- a/base/repl/LineEdit.jl +++ b/base/repl/LineEdit.jl @@ -480,7 +480,8 @@ function edit_insert_newline(s::PromptState, align=-1) buf = buffer(s) if align < 0 beg = beginofline(buf) - align = findnext(_notspace, buf.data[beg+1:buf.size], 1) - 1 + align = min(findnext(_notspace, buf.data[beg+1:buf.size], 1) - 1, + position(buf) - beg) # indentation must not increase align < 0 && (align = buf.size-beg) end edit_insert(buf, '\n' * ' '^align) diff --git a/test/lineedit.jl b/test/lineedit.jl index 2d9e68e84bad2..71aed1ffe6421 100644 --- a/test/lineedit.jl +++ b/test/lineedit.jl @@ -565,4 +565,12 @@ end @test bufferdata(s) == "for x=1:10\n é = 1\n b = 2\n \n \n" LineEdit.edit_insert_newline(s, 2) @test bufferdata(s) == "for x=1:10\n é = 1\n b = 2\n \n \n\n " + # test when point before first letter of the line + for i=6:10 + LineEdit.edit_clear(s) + LineEdit.edit_insert(s, "begin\n x") + seek(LineEdit.buffer(s), i) + LineEdit.edit_insert_newline(s) + @test bufferdata(s) == "begin\n" * ' '^(i-6) * "\n x" + end end From 0f4baeee4e426f59e8ef8a748d2a78fcb9b93144 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Thu, 24 Aug 2017 18:03:54 +0200 Subject: [PATCH 139/324] fix prompt-indentation when bracket pasting (#23406) Cf. problem reported there: https://github.com/JuliaLang/julia/pull/22948#issuecomment-324093541 Bracket pasting was not removing the indentation corresponding to the prompt (i.e. 7 spaces for "julia> "). --- base/repl/REPL.jl | 6 ++++++ test/repl.jl | 12 ++++++++++++ 2 files changed, 18 insertions(+) diff --git a/base/repl/REPL.jl b/base/repl/REPL.jl index 92d348ab84c66..589a1e1ebfd2d 100644 --- a/base/repl/REPL.jl +++ b/base/repl/REPL.jl @@ -889,6 +889,9 @@ function setup_interface( # (avoids modifying the user's current leading wip line) tail = lstrip(tail) end + if isprompt_paste # remove indentation spaces corresponding to the prompt + tail = replace(tail, r"^ {7}"m, "") # 7: jl_prompt_len + end LineEdit.replace_line(s, tail) LineEdit.refresh_line(s) break @@ -896,6 +899,9 @@ function setup_interface( # get the line and strip leading and trailing whitespace line = strip(input[oldpos:prevind(input, pos)]) if !isempty(line) + if isprompt_paste # remove indentation spaces corresponding to the prompt + line = replace(line, r"^ {7}"m, "") # 7: jl_prompt_len + end # put the line on the screen and history LineEdit.replace_line(s, line) LineEdit.commit_line(s) diff --git a/test/repl.jl b/test/repl.jl index 5b28c8ff6fda9..510e79ed2c780 100644 --- a/test/repl.jl +++ b/test/repl.jl @@ -561,6 +561,18 @@ fake_repl() do stdin_write, stdout_read, repl wait(c) @test Main.A == 1 + # Test that indentation corresponding to the prompt is removed + sendrepl2("""\e[200~julia> begin\n α=1\n β=2\n end\n\e[201~""") + wait(c) + readuntil(stdout_read, "begin") + @test readuntil(stdout_read, "end") == "\n\r\e[7C α=1\n\r\e[7C β=2\n\r\e[7Cend" + # for incomplete input (`end` below is added after the end of bracket paste) + sendrepl2("""\e[200~julia> begin\n α=1\n β=2\n\e[201~end""") + wait(c) + readuntil(stdout_read, "begin") + readuntil(stdout_read, "begin") + @test readuntil(stdout_read, "end") == "\n\r\e[7C α=1\n\r\e[7C β=2\n\r\e[7Cend" + # Close repl write(stdin_write, '\x04') wait(repltask) From 92f70e2d3a2a36b115b0f4b2cc2b1d2899443a28 Mon Sep 17 00:00:00 2001 From: Stefan Karpinski Date: Tue, 22 Aug 2017 16:56:49 -0400 Subject: [PATCH 140/324] Make --quiet and --banner independent (fix #23380) --- NEWS.md | 5 ++++- base/client.jl | 9 +++++---- base/deprecated.jl | 3 --- base/options.jl | 1 + src/jloptions.c | 9 ++++++--- src/julia.h | 1 + test/cmdlineargs.jl | 16 +++++++++++++--- test/repl.jl | 4 ++-- 8 files changed, 32 insertions(+), 16 deletions(-) diff --git a/NEWS.md b/NEWS.md index d0ac72076cf43..1bf0135b08ddb 100644 --- a/NEWS.md +++ b/NEWS.md @@ -338,7 +338,10 @@ Command-line option changes * New option `--warn-overwrite={yes|no}` to control the warning for overwriting method definitions. The default is `no` ([#23002]). - * The `-q` and `--quiet` flags have been deprecated in favor of `--banner={yes|no}` ([#23342]). + * New option `--banner={yes,no}` allows suppressing or forcing the printing of the + startup banner, overriding the default behavior (banner in REPL, no banner otherwise). + The `--quiet` option implies `--banner=no` even in REPL mode but can be overridden by + passing `--quiet` together with `--banner=yes` ([#23342]). Julia v0.6.0 Release Notes ========================== diff --git a/base/client.jl b/base/client.jl index 0a6a51e0d1a99..3f2f434bf9d27 100644 --- a/base/client.jl +++ b/base/client.jl @@ -251,9 +251,10 @@ function process_options(opts::JLOptions) length(idxs) > 0 && deleteat!(ARGS, idxs[1]) end repl = true + quiet = (opts.quiet != 0) + banner = (opts.banner == 1 || opts.banner != 0 && opts.isinteractive != 0) startup = (opts.startupfile != 2) history_file = (opts.historyfile != 0) - banner = (opts.banner != 0) color_set = (opts.color != 0) global have_color = (opts.color == 1) global is_interactive = (opts.isinteractive != 0) @@ -317,7 +318,7 @@ function process_options(opts::JLOptions) break end repl |= is_interactive - return (banner,repl,startup,color_set,history_file) + return (quiet,banner,repl,startup,color_set,history_file) end function load_juliarc() @@ -380,7 +381,7 @@ function _start() opts = JLOptions() @eval Main include(x) = $include(Main, x) try - (banner,repl,startup,color_set,history_file) = process_options(opts) + (quiet,banner,repl,startup,color_set,history_file) = process_options(opts) local term global active_repl @@ -396,7 +397,7 @@ function _start() banner && REPL.banner(term,term) if term.term_type == "dumb" active_repl = REPL.BasicREPL(term) - banner && warn("Terminal not fully functional") + quiet || warn("Terminal not fully functional") else active_repl = REPL.LineEditREPL(term, true) active_repl.history_file = history_file diff --git a/base/deprecated.jl b/base/deprecated.jl index 3428b14900e8c..ac6c075414b01 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1707,9 +1707,6 @@ export hex2num # issue #5148, PR #23259 # warning for `const` on locals should be changed to an error in julia-syntax.scm -# issue #23342, PR #23343 -# `-q` and `--quiet` are deprecated in jloptions.c - # issue #17886 # deprecations for filter[!] with 2-arg functions are in associative.jl diff --git a/base/options.jl b/base/options.jl index 7c9ffb8c01f37..b4a4583aabfab 100644 --- a/base/options.jl +++ b/base/options.jl @@ -2,6 +2,7 @@ # NOTE: This type needs to be kept in sync with jl_options in src/julia.h struct JLOptions + quiet::Int8 banner::Int8 julia_home::Ptr{UInt8} julia_bin::Ptr{UInt8} diff --git a/src/jloptions.c b/src/jloptions.c index e91303e1e21de..5da1d79159ed4 100644 --- a/src/jloptions.c +++ b/src/jloptions.c @@ -33,7 +33,8 @@ JL_DLLEXPORT const char *jl_get_default_sysimg_path(void) } -jl_options_t jl_options = { 1, // banner +jl_options_t jl_options = { 0, // quiet + -1, // banner NULL, // julia_home NULL, // julia_bin NULL, // eval @@ -102,6 +103,7 @@ static const char opts[] = // interactive options " -i Interactive mode; REPL runs and isinteractive() is true\n" + " -q, --quiet Quiet startup: no banner, suppress REPL warnings\n" " --banner={yes|no} Enable or disable startup banner\n" " --color={yes|no} Enable or disable color text\n" " --history-file={yes|no} Load or save history\n\n" @@ -315,8 +317,9 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) jl_options.image_file_specified = 1; break; case 'q': // quiet - jl_printf(JL_STDERR, "-q and --quiet are deprecated, use --banner=no instead\n"); - jl_options.banner = 0; + jl_options.quiet = 1; + if (jl_options.banner < 0) + jl_options.banner = 0; break; case opt_banner: // banner if (!strcmp(optarg,"yes")) diff --git a/src/julia.h b/src/julia.h index cdf838287fb0a..5fda2e5415f25 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1671,6 +1671,7 @@ JL_DLLEXPORT void jl_(void *jl_value); // julia options ----------------------------------------------------------- // NOTE: This struct needs to be kept in sync with JLOptions type in base/options.jl typedef struct { + int8_t quiet; int8_t banner; const char *julia_home; const char *julia_bin; diff --git a/test/cmdlineargs.jl b/test/cmdlineargs.jl index 290365651521f..b7160b2da8a46 100644 --- a/test/cmdlineargs.jl +++ b/test/cmdlineargs.jl @@ -22,8 +22,18 @@ let exename = `$(Base.julia_cmd()) --precompiled=yes --startup-file=no` @test startswith(read(`$exename --help`, String), header) end - # --banner - # This flag is indirectly tested in test/repl.jl + # --quiet, --banner + let t(q,b) = "Base.JLOptions().quiet == $q && Base.JLOptions().banner == $b" + @test success(`$exename -e $(t(0, -1))`) + @test success(`$exename -q -e $(t(1, 0))`) + @test success(`$exename --quiet -e $(t(1, 0))`) + @test success(`$exename --banner=no -e $(t(0, 0))`) + @test success(`$exename --banner=yes -e $(t(0, 1))`) + @test success(`$exename -q --banner=no -e $(t(1, 0))`) + @test success(`$exename -q --banner=yes -e $(t(1, 1))`) + @test success(`$exename --banner=no -q -e $(t(1, 0))`) + @test success(`$exename --banner=yes -q -e $(t(1, 1))`) + end # --home @test success(`$exename -H $JULIA_HOME`) @@ -72,7 +82,7 @@ let exename = `$(Base.julia_cmd()) --precompiled=yes --startup-file=no` end # --procs - @test readchomp(`$exename --banner=no -p 2 -e "println(nworkers())"`) == "2" + @test readchomp(`$exename -q -p 2 -e "println(nworkers())"`) == "2" @test !success(`$exename -p 0`) @test !success(`$exename --procs=1.0`) diff --git a/test/repl.jl b/test/repl.jl index 5b28c8ff6fda9..1c4e066024b03 100644 --- a/test/repl.jl +++ b/test/repl.jl @@ -614,7 +614,7 @@ let exename = Base.julia_cmd() TestHelpers.with_fake_pty() do slave, master nENV = copy(ENV) nENV["TERM"] = "dumb" - p = spawn(setenv(`$exename --startup-file=no --banner=no`,nENV),slave,slave,slave) + p = spawn(setenv(`$exename --startup-file=no -q`,nENV),slave,slave,slave) output = readuntil(master,"julia> ") if ccall(:jl_running_on_valgrind,Cint,()) == 0 # If --trace-children=yes is passed to valgrind, we will get a @@ -631,7 +631,7 @@ let exename = Base.julia_cmd() end # Test stream mode - outs, ins, p = readandwrite(`$exename --startup-file=no --banner=no`) + outs, ins, p = readandwrite(`$exename --startup-file=no -q`) write(ins,"1\nquit()\n") @test read(outs, String) == "1\n" end # let exename From aa3d2be60fa5fccd7f4550026c96cd3fe62d63b8 Mon Sep 17 00:00:00 2001 From: Mus M Date: Thu, 24 Aug 2017 13:10:36 -0400 Subject: [PATCH 141/324] Int constructor instead of div for Int returns in gmp (#23329) --- base/gmp.jl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/base/gmp.jl b/base/gmp.jl index bf12ec5401307..4eb7378020791 100644 --- a/base/gmp.jl +++ b/base/gmp.jl @@ -178,9 +178,9 @@ for (op, T) in ((:fac_ui, Culong), (:set_ui, Culong), (:set_si, Clong), (:set_d, end end -popcount(a::BigInt) = ccall((:__gmpz_popcount, :libgmp), Culong, (mpz_t,), &a) % Int +popcount(a::BigInt) = Int(ccall((:__gmpz_popcount, :libgmp), Culong, (mpz_t,), &a)) -mpn_popcount(d::Ptr{Limb}, s::Integer) = ccall((:__gmpn_popcount, :libgmp), Culong, (Ptr{Limb}, Csize_t), d, s) % Int +mpn_popcount(d::Ptr{Limb}, s::Integer) = Int(ccall((:__gmpn_popcount, :libgmp), Culong, (Ptr{Limb}, Csize_t), d, s)) mpn_popcount(a::BigInt) = mpn_popcount(a.d, abs(a.size)) function tdiv_qr!(x::BigInt, y::BigInt, a::BigInt, b::BigInt) @@ -201,16 +201,16 @@ function gcdext!(x::BigInt, y::BigInt, z::BigInt, a::BigInt, b::BigInt) end gcdext(a::BigInt, b::BigInt) = gcdext!(BigInt(), BigInt(), BigInt(), a, b) -cmp(a::BigInt, b::BigInt) = ccall((:__gmpz_cmp, :libgmp), Cint, (mpz_t, mpz_t), &a, &b) % Int -cmp_si(a::BigInt, b) = ccall((:__gmpz_cmp_si, :libgmp), Cint, (mpz_t, Clong), &a, b) % Int -cmp_ui(a::BigInt, b) = ccall((:__gmpz_cmp_ui, :libgmp), Cint, (mpz_t, Culong), &a, b) % Int -cmp_d(a::BigInt, b) = ccall((:__gmpz_cmp_d, :libgmp), Cint, (mpz_t, Cdouble), &a, b) % Int +cmp(a::BigInt, b::BigInt) = Int(ccall((:__gmpz_cmp, :libgmp), Cint, (mpz_t, mpz_t), &a, &b)) +cmp_si(a::BigInt, b) = Int(ccall((:__gmpz_cmp_si, :libgmp), Cint, (mpz_t, Clong), &a, b)) +cmp_ui(a::BigInt, b) = Int(ccall((:__gmpz_cmp_ui, :libgmp), Cint, (mpz_t, Culong), &a, b)) +cmp_d(a::BigInt, b) = Int(ccall((:__gmpz_cmp_d, :libgmp), Cint, (mpz_t, Cdouble), &a, b)) mpn_cmp(a::Ptr{Limb}, b::Ptr{Limb}, c) = ccall((:__gmpn_cmp, :libgmp), Cint, (Ptr{Limb}, Ptr{Limb}, Clong), a, b, c) mpn_cmp(a::BigInt, b::BigInt, c) = mpn_cmp(a.d, b.d, c) get_str!(x, a, b::BigInt) = (ccall((:__gmpz_get_str,:libgmp), Ptr{Cchar}, (Ptr{Cchar}, Cint, mpz_t), x, a, &b); x) -set_str!(x::BigInt, a, b) = ccall((:__gmpz_set_str, :libgmp), Cint, (mpz_t, Ptr{UInt8}, Cint), &x, a, b) % Int +set_str!(x::BigInt, a, b) = Int(ccall((:__gmpz_set_str, :libgmp), Cint, (mpz_t, Ptr{UInt8}, Cint), &x, a, b)) get_d(a::BigInt) = ccall((:__gmpz_get_d, :libgmp), Cdouble, (mpz_t,), &a) limbs_write!(x::BigInt, a) = ccall((:__gmpz_limbs_write, :libgmp), Ptr{Limb}, (mpz_t, Clong), &x, a) From 03c5d7df220ad8b5d1ae41104c03f6e7f9f19e69 Mon Sep 17 00:00:00 2001 From: Sacha Verweij Date: Thu, 24 Aug 2017 11:15:01 -0700 Subject: [PATCH 142/324] deprecate expm in favor of exp (#23233) * Deprecate expm in favor of exp. * Remove reference to expm in the linalg docs --- NEWS.md | 3 +++ base/deprecated.jl | 8 ++++++-- base/exports.jl | 1 - base/linalg/dense.jl | 15 +++++++-------- base/linalg/diagonal.jl | 3 +-- base/linalg/linalg.jl | 3 +-- base/linalg/symmetric.jl | 4 ++-- doc/src/manual/linear-algebra.md | 4 ++-- doc/src/stdlib/linalg.md | 1 - test/linalg/dense.jl | 33 ++++++++++++++++---------------- test/linalg/diagonal.jl | 4 ++-- test/linalg/lapack.jl | 2 +- test/linalg/symmetric.jl | 6 +++--- test/linalg/triangular.jl | 4 ++-- 14 files changed, 46 insertions(+), 45 deletions(-) diff --git a/NEWS.md b/NEWS.md index eca1009dfe373..c1d99f7c7ad73 100644 --- a/NEWS.md +++ b/NEWS.md @@ -332,6 +332,8 @@ Deprecated or removed full path if you need access to executables or libraries in the `JULIA_HOME` directory, e.g. `joinpath(JULIA_HOME, "7z.exe")` for `7z.exe` ([#21540]). + * `expm` has been deprecated in favor of `exp` ([#23233]). + * Calling `union` with no arguments is deprecated; construct an empty set with an appropriate element type using `Set{T}()` instead ([#23144]). @@ -1221,4 +1223,5 @@ Command-line option changes [#23157]: https://github.com/JuliaLang/julia/issues/23157 [#23187]: https://github.com/JuliaLang/julia/issues/23187 [#23207]: https://github.com/JuliaLang/julia/issues/23207 +[#23233]: https://github.com/JuliaLang/julia/issues/23233 [#23342]: https://github.com/JuliaLang/julia/issues/23342 diff --git a/base/deprecated.jl b/base/deprecated.jl index 4fdee606464c6..b47fd63238add 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -220,7 +220,7 @@ for f in (:sin, :sinh, :sind, :asin, :asinh, :asind, :tan, :tanh, :tand, :atan, :atanh, :atand, :sinpi, :cosc, :ceil, :floor, :trunc, :round, :log1p, :expm1, :abs, :abs2, - :log, :log2, :log10, :exp, :exp2, :exp10, :sinc, :cospi, + :log, :log2, :log10, :exp2, :exp10, :sinc, :cospi, :cos, :cosh, :cosd, :acos, :acosd, :cot, :coth, :cotd, :acot, :acotd, :sec, :sech, :secd, :asech, @@ -251,7 +251,7 @@ for f in ( # base/special/gamma.jl :gamma, :lfact, # base/math.jl - :cbrt, :sinh, :cosh, :tanh, :atan, :asinh, :exp, :exp2, + :cbrt, :sinh, :cosh, :tanh, :atan, :asinh, :exp2, :expm1, :exp10, :sin, :cos, :tan, :asin, :acos, :acosh, :atanh, #=:log,=# :log2, :log10, :lgamma, #=:log1p,=# :sqrt, # base/floatfuncs.jl @@ -1660,6 +1660,10 @@ function Tridiagonal(dl::AbstractVector{Tl}, d::AbstractVector{Td}, du::Abstract Tridiagonal(map(v->convert(Vector{promote_type(Tl,Td,Tu)}, v), (dl, d, du))...) end +# deprecate expm in favor of exp +@deprecate expm! exp! +@deprecate expm exp + # PR #23092 @eval LibGit2 begin function prompt(msg::AbstractString; default::AbstractString="", password::Bool=false) diff --git a/base/exports.jl b/base/exports.jl index 96d860e7d6bb7..4563ccdefb455 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -572,7 +572,6 @@ export eigvals, eigvals!, eigvecs, - expm, eye, factorize, givens, diff --git a/base/linalg/dense.jl b/base/linalg/dense.jl index 9b150df45c56f..42228106ee3e5 100644 --- a/base/linalg/dense.jl +++ b/base/linalg/dense.jl @@ -426,12 +426,12 @@ function (^)(A::AbstractMatrix{T}, p::Real) where T # Otherwise, use Schur decomposition return schurpow(A, p) end -(^)(A::AbstractMatrix, p::Number) = expm(p*logm(A)) +(^)(A::AbstractMatrix, p::Number) = exp(p*logm(A)) # Matrix exponential """ - expm(A) + exp(A::AbstractMatrix) Compute the matrix exponential of `A`, defined by @@ -451,22 +451,21 @@ julia> A = eye(2, 2) 1.0 0.0 0.0 1.0 -julia> expm(A) +julia> exp(A) 2×2 Array{Float64,2}: 2.71828 0.0 0.0 2.71828 ``` """ -expm(A::StridedMatrix{<:BlasFloat}) = expm!(copy(A)) -expm(A::StridedMatrix{<:Integer}) = expm!(float(A)) -expm(x::Number) = exp(x) +exp(A::StridedMatrix{<:BlasFloat}) = exp!(copy(A)) +exp(A::StridedMatrix{<:Integer}) = exp!(float(A)) ## Destructive matrix exponential using algorithm from Higham, 2008, ## "Functions of Matrices: Theory and Computation", SIAM -function expm!(A::StridedMatrix{T}) where T<:BlasFloat +function exp!(A::StridedMatrix{T}) where T<:BlasFloat n = checksquare(A) if ishermitian(A) - return full(expm(Hermitian(A))) + return full(exp(Hermitian(A))) end ilo, ihi, scale = LAPACK.gebal!('B', A) # modifies A nA = norm(A, 1) diff --git a/base/linalg/diagonal.jl b/base/linalg/diagonal.jl index 3b0cd7d58a2f1..7161d20450b6c 100644 --- a/base/linalg/diagonal.jl +++ b/base/linalg/diagonal.jl @@ -326,8 +326,7 @@ end eye(::Type{Diagonal{T}}, n::Int) where {T} = Diagonal(ones(T,n)) # Matrix functions -expm(D::Diagonal) = Diagonal(exp.(D.diag)) -expm(D::Diagonal{<:AbstractMatrix}) = Diagonal(expm.(D.diag)) +exp(D::Diagonal) = Diagonal(exp.(D.diag)) logm(D::Diagonal) = Diagonal(log.(D.diag)) logm(D::Diagonal{<:AbstractMatrix}) = Diagonal(logm.(D.diag)) sqrtm(D::Diagonal) = Diagonal(sqrt.(D.diag)) diff --git a/base/linalg/linalg.jl b/base/linalg/linalg.jl index 0f28267729635..6eaea00de1b4d 100644 --- a/base/linalg/linalg.jl +++ b/base/linalg/linalg.jl @@ -6,7 +6,7 @@ import Base: \, /, *, ^, +, -, == import Base: A_mul_Bt, At_ldiv_Bt, A_rdiv_Bc, At_ldiv_B, Ac_mul_Bc, A_mul_Bc, Ac_mul_B, Ac_ldiv_B, Ac_ldiv_Bc, At_mul_Bt, A_rdiv_Bt, At_mul_B import Base: USE_BLAS64, abs, big, broadcast, ceil, conj, convert, copy, copy!, - adjoint, eltype, eye, findmax, findmin, fill!, floor, full, getindex, + adjoint, eltype, exp, eye, findmax, findmin, fill!, floor, full, getindex, hcat, imag, indices, inv, isapprox, isone, IndexStyle, kron, length, map, ndims, oneunit, parent, power_by_squaring, print_matrix, promote_rule, real, round, setindex!, show, similar, size, transpose, trunc, typed_hcat @@ -81,7 +81,6 @@ export eigvals, eigvals!, eigvecs, - expm, eye, factorize, givens, diff --git a/base/linalg/symmetric.jl b/base/linalg/symmetric.jl index 1a890da1a5007..92fe036dbf94f 100644 --- a/base/linalg/symmetric.jl +++ b/base/linalg/symmetric.jl @@ -589,11 +589,11 @@ function ^(A::Hermitian{T}, p::Real) where T end end -function expm(A::Symmetric) +function exp(A::Symmetric) F = eigfact(A) return Symmetric((F.vectors * Diagonal(exp.(F.values))) * F.vectors') end -function expm(A::Hermitian{T}) where T +function exp(A::Hermitian{T}) where T n = checksquare(A) F = eigfact(A) retmat = (F.vectors * Diagonal(exp.(F.values))) * F.vectors' diff --git a/doc/src/manual/linear-algebra.md b/doc/src/manual/linear-algebra.md index 8bb0e2101a8d6..3a0fb0fb4e6cc 100644 --- a/doc/src/manual/linear-algebra.md +++ b/doc/src/manual/linear-algebra.md @@ -177,8 +177,8 @@ as well as whether hooks to various optimized methods for them in LAPACK are ava | Matrix type | `+` | `-` | `*` | `\` | Other functions with optimized methods | |:------------------------- |:--- |:--- |:--- |:--- |:------------------------------------------------------------------- | -| [`Symmetric`](@ref) | | | | MV | [`inv()`](@ref), [`sqrtm()`](@ref), [`expm()`](@ref) | -| [`Hermitian`](@ref) | | | | MV | [`inv()`](@ref), [`sqrtm()`](@ref), [`expm()`](@ref) | +| [`Symmetric`](@ref) | | | | MV | [`inv()`](@ref), [`sqrtm()`](@ref), [`exp()`](@ref) | +| [`Hermitian`](@ref) | | | | MV | [`inv()`](@ref), [`sqrtm()`](@ref), [`exp()`](@ref) | | [`UpperTriangular`](@ref) | | | MV | MV | [`inv()`](@ref), [`det()`](@ref) | | [`LowerTriangular`](@ref) | | | MV | MV | [`inv()`](@ref), [`det()`](@ref) | | [`SymTridiagonal`](@ref) | M | M | MS | MV | [`eigmax()`](@ref), [`eigmin()`](@ref) | diff --git a/doc/src/stdlib/linalg.md b/doc/src/stdlib/linalg.md index 935ea52e50a04..66dd343d3f195 100644 --- a/doc/src/stdlib/linalg.md +++ b/doc/src/stdlib/linalg.md @@ -92,7 +92,6 @@ Base.repmat Base.kron Base.SparseArrays.blkdiag Base.LinAlg.linreg -Base.LinAlg.expm Base.LinAlg.logm Base.LinAlg.sqrtm Base.LinAlg.lyap diff --git a/test/linalg/dense.jl b/test/linalg/dense.jl index b10f1f606f2b1..30fcf9ace436c 100644 --- a/test/linalg/dense.jl +++ b/test/linalg/dense.jl @@ -139,7 +139,6 @@ bimg = randn(n,2)/2 A = zeros(eltya,1,1) A[1,1] = α @test diagm(α) == A # Test behavior of `diagm` when passed a scalar - @test expm(α) == exp(α) # `expm` should behave like `exp` with scalar argument end @testset "Factorize" begin @@ -416,7 +415,7 @@ end eA1 = convert(Matrix{elty}, [147.866622446369 127.781085523181 127.781085523182; 183.765138646367 183.765138646366 163.679601723179; 71.797032399996 91.8825693231832 111.968106246371]') - @test expm(A1) ≈ eA1 + @test exp(A1) ≈ eA1 A2 = convert(Matrix{elty}, [29.87942128909879 0.7815750847907159 -2.289519314033932; @@ -426,21 +425,21 @@ end [ 5496313853692458.0 -18231880972009236.0 -30475770808580460.0; -18231880972009252.0 60605228702221920.0 101291842930249760.0; -30475770808580480.0 101291842930249728.0 169294411240851968.0]) - @test expm(A2) ≈ eA2 + @test exp(A2) ≈ eA2 A3 = convert(Matrix{elty}, [-131 19 18;-390 56 54;-387 57 52]) eA3 = convert(Matrix{elty}, [-1.50964415879218 -5.6325707998812 -4.934938326092; 0.367879439109187 1.47151775849686 1.10363831732856; 0.135335281175235 0.406005843524598 0.541341126763207]') - @test expm(A3) ≈ eA3 + @test exp(A3) ≈ eA3 A4 = convert(Matrix{elty}, [0.25 0.25; 0 0]) eA4 = convert(Matrix{elty}, [1.2840254166877416 0.2840254166877415; 0 1]) - @test expm(A4) ≈ eA4 + @test exp(A4) ≈ eA4 A5 = convert(Matrix{elty}, [0 0.02; 0 0]) eA5 = convert(Matrix{elty}, [1 0.02; 0 1]) - @test expm(A5) ≈ eA5 + @test exp(A5) ≈ eA5 # Hessenberg @test hessfact(A1)[:H] ≈ convert(Matrix{elty}, @@ -454,20 +453,20 @@ end 1/3 1/4 1/5 1/6; 1/4 1/5 1/6 1/7; 1/5 1/6 1/7 1/8]) - @test expm(logm(A4)) ≈ A4 + @test exp(logm(A4)) ≈ A4 A5 = convert(Matrix{elty}, [1 1 0 1; 0 1 1 0; 0 0 1 1; 1 0 0 1]) - @test expm(logm(A5)) ≈ A5 + @test exp(logm(A5)) ≈ A5 A6 = convert(Matrix{elty}, [-5 2 0 0 ; 1/2 -7 3 0; 0 1/3 -9 4; 0 0 1/4 -11]) - @test expm(logm(A6)) ≈ A6 + @test exp(logm(A6)) ≈ A6 A7 = convert(Matrix{elty}, [1 0 0 1e-8; 0 1 0 0; 0 0 1 0; 0 0 0 1]) - @test expm(logm(A7)) ≈ A7 + @test exp(logm(A7)) ≈ A7 end A8 = 100 * [-1+1im 0 0 1e-8; 0 1 0 0; 0 0 1 0; 0 0 0 1] - @test expm(logm(A8)) ≈ A8 + @test exp(logm(A8)) ≈ A8 end @testset "issue 5116" begin @@ -476,19 +475,19 @@ end 0.006540706968939 -0.999786072879326 0.0 0.0 0.0 0.0 1.0 0.0 0.013081413937878 -3.999572145758650 0.0 1.0] - @test expm(A9) ≈ eA9 + @test exp(A9) ≈ eA9 A10 = [ 0. 0. 0. 0. ; 0. 0. -im 0.; 0. im 0. 0.; 0. 0. 0. 0.] eA10 = [ 1.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 1.543080634815244+0.0im 0.0-1.175201193643801im 0.0+0.0im 0.0+0.0im 0.0+1.175201193643801im 1.543080634815243+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 1.0+0.0im] - @test expm(A10) ≈ eA10 + @test exp(A10) ≈ eA10 end @testset "Additional matrix logarithm tests" for elty in (Float64, Complex{Float64}) A11 = convert(Matrix{elty}, [3 2; -5 -3]) - @test expm(logm(A11)) ≈ A11 + @test exp(logm(A11)) ≈ A11 A12 = convert(Matrix{elty}, [1 -1; 1 -1]) @test typeof(logm(A12)) == Array{Complex{Float64}, 2} @@ -498,7 +497,7 @@ end 0.2310490602 1.295566591 0.2651438179; 0.2310490602 0.1969543025 1.363756107]) @test logm(A1) ≈ logmA1 - @test expm(logm(A1)) ≈ A1 + @test exp(logm(A1)) ≈ A1 A4 = convert(Matrix{elty}, [1/2 1/3 1/4 1/5+eps(); 1/3 1/4 1/5 1/6; @@ -509,7 +508,7 @@ end 0.4462766564 2.994142974 -7.351095988 3.318413247; 0.2414170219 0.5865285289 3.318413247 -5.444632124]) @test logm(A4) ≈ logmA4 - @test expm(logm(A4)) ≈ A4 + @test exp(logm(A4)) ≈ A4 end @testset "issue #7181" begin @@ -675,7 +674,7 @@ end @testset "test ops on Numbers for $elty" for elty in [Float32,Float64,Complex64,Complex128] a = rand(elty) - @test expm(a) == exp(a) + @test exp(a) == exp(a) @test isposdef(one(elty)) @test sqrtm(a) == sqrt(a) @test logm(a) ≈ log(a) diff --git a/test/linalg/diagonal.jl b/test/linalg/diagonal.jl index d9d5805b8915c..3ec850f125591 100644 --- a/test/linalg/diagonal.jl +++ b/test/linalg/diagonal.jl @@ -60,7 +60,7 @@ srand(1) @test func(D) ≈ func(DM) atol=n^2*eps(relty)*(1+(elty<:Complex)) end if relty <: BlasFloat - for func in (expm,) + for func in (exp,) @test func(D) ≈ func(DM) atol=n^3*eps(relty) end @test logm(Diagonal(abs.(D.diag))) ≈ logm(abs.(DM)) atol=n^3*eps(relty) @@ -381,7 +381,7 @@ end @test ishermitian(Dherm) == true @test ishermitian(Dsym) == false - @test expm(D) == Diagonal([expm([1 2; 3 4]), expm([1 2; 3 4])]) + @test exp(D) == Diagonal([exp([1 2; 3 4]), exp([1 2; 3 4])]) @test logm(D) == Diagonal([logm([1 2; 3 4]), logm([1 2; 3 4])]) @test sqrtm(D) == Diagonal([sqrtm([1 2; 3 4]), sqrtm([1 2; 3 4])]) end diff --git a/test/linalg/lapack.jl b/test/linalg/lapack.jl index 7d5d92b5204db..56838b1410f2a 100644 --- a/test/linalg/lapack.jl +++ b/test/linalg/lapack.jl @@ -613,7 +613,7 @@ end # Issue 13976 let A = [NaN 0.0 NaN; 0 0 0; NaN 0 NaN] - @test_throws ArgumentError expm(A) + @test_throws ArgumentError exp(A) end # Issue 14065 (and 14220) diff --git a/test/linalg/symmetric.jl b/test/linalg/symmetric.jl index df89577dcb1e1..09b4370ed922c 100644 --- a/test/linalg/symmetric.jl +++ b/test/linalg/symmetric.jl @@ -12,16 +12,16 @@ end @testset "Hermitian matrix exponential/log" begin A1 = randn(4,4) + im*randn(4,4) A2 = A1 + A1' - @test expm(A2) ≈ expm(Hermitian(A2)) + @test exp(A2) ≈ exp(Hermitian(A2)) @test logm(A2) ≈ logm(Hermitian(A2)) A3 = A1 * A1' # posdef - @test expm(A3) ≈ expm(Hermitian(A3)) + @test exp(A3) ≈ exp(Hermitian(A3)) @test logm(A3) ≈ logm(Hermitian(A3)) A1 = randn(4,4) A3 = A1 * A1' A4 = A1 + A1.' - @test expm(A4) ≈ expm(Symmetric(A4)) + @test exp(A4) ≈ exp(Symmetric(A4)) @test logm(A3) ≈ logm(Symmetric(A3)) @test logm(A3) ≈ logm(Hermitian(A3)) end diff --git a/test/linalg/triangular.jl b/test/linalg/triangular.jl index ba16777bb5538..eb0fc7cd57717 100644 --- a/test/linalg/triangular.jl +++ b/test/linalg/triangular.jl @@ -174,9 +174,9 @@ for elty1 in (Float32, Float64, BigFloat, Complex64, Complex128, Complex{BigFloa @test B == viewA1.' end - #expm/logm + #exp/logm if (elty1 == Float64 || elty1 == Complex128) && (t1 == UpperTriangular || t1 == LowerTriangular) - @test expm(full(logm(A1))) ≈ full(A1) + @test exp(full(logm(A1))) ≈ full(A1) end # scale From 8809617f6f520ab8653dcbc3ee401dc7f6f01058 Mon Sep 17 00:00:00 2001 From: Moritz Schauer Date: Fri, 25 Aug 2017 00:09:42 +0200 Subject: [PATCH 143/324] Doc-strings for some internals. (#23333) * REPL-documentation for more internals. * Copy edit 'REPL-documentation for more internals.' * New section "Special Types" --- base/docs/basedocs.jl | 127 +++++++++++++++++++++++++++++++++++++++++ doc/src/stdlib/base.md | 17 +++++- 2 files changed, 141 insertions(+), 3 deletions(-) diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index f3bdfb8ffb1d2..b4b9a94cb89f9 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -1261,4 +1261,131 @@ available in the `.error` field. """ InitError +""" + Any::DataType + +`Any` is the union of all types. It has the defining property `isa(x, Any) == true` for any `x`. `Any` therefore +describes the entire universe of possible values. For example `Integer` is a subset of `Any` that includes `Int`, +`Int8`, and other integer types. +""" +Any + +""" + Union{} + +`Union{}`, the empty [`Union`](@ref) of types, is the type that has no values. That is, it has the defining +property `isa(x, Union{}) == false` for any `x`. `Base.Bottom` is defined as its alias and the type of `Union{}` +is `Core.TypeofBottom`. + +# Examples +```jldoctest +julia> isa(nothing, Union{}) +false +``` +""" +kw"Union{}", Base.Bottom + +""" + Union{Types...} + +A type union is an abstract type which includes all instances of any of its argument types. The empty +union [`Union{}`](@ref) is the bottom type of Julia. + +# Examples +```jldoctest +julia> IntOrString = Union{Int,AbstractString} +Union{AbstractString, Int64} + +julia> 1 :: IntOrString +1 + +julia> "Hello!" :: IntOrString +"Hello!" + +julia> 1.0 :: IntOrString +ERROR: TypeError: typeassert: expected Union{AbstractString, Int64}, got Float64 +``` +""" +Union + + +""" + UnionAll + +A union of types over all values of a type parameter. `UnionAll` is used to describe parametric types +where the values of some parameters are not known. + +# Examples +```jldoctest +julia> typeof(Vector) +UnionAll + +julia> typeof(Vector{Int}) +DataType +``` +""" +UnionAll + +""" + :: + +With the `::`-operator type annotations are attached to expressions and variables in programs. +See the manual section on [Type Declarations](@ref). + +Outside of declarations `::` is used to assert that expressions and variables in programs have a given type. + +# Examples +```jldoctest +julia> (1+2)::AbstractFloat +ERROR: TypeError: typeassert: expected AbstractFloat, got Int64 + +julia> (1+2)::Int +3 +``` +""" +kw"::" + +""" + Vararg{T,N} + +The last parameter of a tuple type [`Tuple`](@ref) can be the special type `Vararg`, which denotes any +number of trailing elements. The type `Vararg{T,N}` corresponds to exactly `N` elements of type `T`. +`Vararg{T}` corresponds to zero or more elements of type `T`. `Vararg` tuple types are used to represent the +arguments accepted by varargs methods (see the section on [Varargs Functions](@ref) in the manual.) + +# Examples +```jldoctest +julia> mytupletype = Tuple{AbstractString,Vararg{Int}} +Tuple{AbstractString,Vararg{Int64,N} where N} + +julia> isa(("1",), mytupletype) +true + +julia> isa(("1",1), mytupletype) +true + +julia> isa(("1",1,2), mytupletype) +true + +julia> isa(("1",1,2,3.0), mytupletype) +false +``` +""" +Vararg + +""" + Tuple{Types...} + +Tuples are an abstraction of the arguments of a function – without the function itself. The salient aspects of +a function's arguments are their order and their types. Therefore a tuple type is similar to a parameterized +immutable type where each parameter is the type of one field. Tuple types may have any number of parameters. + +Tuple types are covariant in their parameters: `Tuple{Int}` is a subtype of `Tuple{Any}`. Therefore `Tuple{Any}` +is considered an abstract type, and tuple types are only concrete if their parameters are. Tuples do not have +field names; fields are only accessed by index. + +See the manual section on [Tuple Types](@ref). +""" +Tuple + end diff --git a/doc/src/stdlib/base.md b/doc/src/stdlib/base.md index 663abf299dd99..b2127bf65bed0 100644 --- a/doc/src/stdlib/base.md +++ b/doc/src/stdlib/base.md @@ -83,7 +83,7 @@ Base.widen Base.identity ``` -## Types +## Dealing with Types ```@docs Base.supertype @@ -109,11 +109,22 @@ Base.isbits Base.isleaftype Base.typejoin Base.typeintersect -Base.Val -Base.Enums.@enum Base.instances ``` +## Special Types + +```@docs +Core.Any +Base.Enums.@enum +Core.Union +Union{} +Core.UnionAll +Core.Tuple +Base.Val +Core.Vararg +``` + ## Generic Functions ```@docs From 50e72086bb921909ec3ae59baf66d0c8d3ead2b6 Mon Sep 17 00:00:00 2001 From: kshyatt Date: Thu, 24 Aug 2017 15:27:25 -0700 Subject: [PATCH 144/324] Document merge base and GitAnnotated --- base/libgit2/merge.jl | 19 +++++++++++++++++++ doc/src/devdocs/libgit2.md | 1 + 2 files changed, 20 insertions(+) diff --git a/base/libgit2/merge.jl b/base/libgit2/merge.jl index 4a85128e6500d..f0439b9b71e45 100644 --- a/base/libgit2/merge.jl +++ b/base/libgit2/merge.jl @@ -1,5 +1,18 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license +""" + GitAnnotated(repo::GitRepo, commit_id::GitHash) + GitAnnotated(repo::GitRepo, ref::GitReference) + GitAnnotated(repo::GitRepo, fh::FetchHead) + GitAnnotated(repo::GitRepo, comittish::AbstractString) + +An annotated git commit carries with it information about how it was looked up and +why, so that rebase or merge operations have more information about the context of +the commit. Conflict files contain information about the source/target branches in +the merge which are conflicting, for instance. An annotated commit can refer to the +tip of a remote branch, for instance when a [`FetchHead`](@ref) is passed, or to a +branch head described using `GitReference`. +""" function GitAnnotated(repo::GitRepo, commit_id::GitHash) ann_ptr_ptr = Ref{Ptr{Void}}(C_NULL) @check ccall((:git_annotated_commit_lookup, :libgit2), Cint, @@ -140,6 +153,12 @@ function merge!(repo::GitRepo, anns::Vector{GitAnnotated}, fastforward::Bool; return merge_result end +""" + merge_base(repo::GitRepo, one::AbstractString, two::AbstractString) -> GitHash + +Find a merge base (a common ancestor) between the commits `one` and `two`. +`one` and `two` may both be in string form. Return the `GitHash` of the merge base. +""" function merge_base(repo::GitRepo, one::AbstractString, two::AbstractString) oid1_ptr = Ref(GitHash(one)) oid2_ptr = Ref(GitHash(two)) diff --git a/doc/src/devdocs/libgit2.md b/doc/src/devdocs/libgit2.md index 91d8fca17e2d3..2d487ffd61841 100644 --- a/doc/src/devdocs/libgit2.md +++ b/doc/src/devdocs/libgit2.md @@ -73,6 +73,7 @@ Base.LibGit2.fetch Base.LibGit2.fetchheads Base.LibGit2.fetch_refspecs Base.LibGit2.fetchhead_foreach_cb +Base.LibGit2.merge_base Base.LibGit2.merge!(::Base.LibGit2.GitRepo; ::Any...) Base.LibGit2.ffmerge! Base.LibGit2.fullname From b92e612d381a575f053f952a07cf4d46a7238198 Mon Sep 17 00:00:00 2001 From: kshyatt Date: Thu, 24 Aug 2017 15:56:52 -0700 Subject: [PATCH 145/324] Add docs for transact --- base/libgit2/libgit2.jl | 10 +++++++++- doc/src/devdocs/libgit2.md | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/base/libgit2/libgit2.jl b/base/libgit2/libgit2.jl index c9f69d0cb6980..4eec2372f5f68 100644 --- a/base/libgit2/libgit2.jl +++ b/base/libgit2/libgit2.jl @@ -522,7 +522,7 @@ function reset!(repo::GitRepo, committish::AbstractString, pathspecs::AbstractSt end """ - reset!(repo::GitRepo, id::GitHash, mode::Cint = Consts.RESET_MIXED) + reset!(repo::GitRepo, id::GitHash, mode::Cint=Consts.RESET_MIXED) Reset the repository `repo` to its state at `id`, using one of three modes set by `mode`: @@ -861,6 +861,14 @@ function restore(s::State, repo::GitRepo) reset!(repo, s.head, Consts.RESET_SOFT) # restore head end +""" + transact(f::Function, repo::GitRepo) + +Apply function `f` to the git repository `repo`, taking a [`snapshot`](@ref) before +applying `f`. If an error occurs within `f`, `repo` will be returned to its snapshot +state using [`restore`](@ref). The error which occurred will be rethrown, but the +state of `repo` will not be corrupted. +""" function transact(f::Function, repo::GitRepo) state = snapshot(repo) try f(repo) catch diff --git a/doc/src/devdocs/libgit2.md b/doc/src/devdocs/libgit2.md index 91d8fca17e2d3..ee924b93e872a 100644 --- a/doc/src/devdocs/libgit2.md +++ b/doc/src/devdocs/libgit2.md @@ -119,6 +119,7 @@ Base.LibGit2.tag_create Base.LibGit2.tag_delete Base.LibGit2.tag_list Base.LibGit2.target +Base.LibGit2.transact Base.LibGit2.treewalk Base.LibGit2.upstream Base.LibGit2.update! From 2406b6e450c9d54ac2b42459bf9cec66557008d7 Mon Sep 17 00:00:00 2001 From: Mus M Date: Thu, 24 Aug 2017 19:04:26 -0400 Subject: [PATCH 146/324] Unify the method name to get library versions (#23323) --- NEWS.md | 5 +++++ base/deprecated.jl | 8 ++++++++ base/gmp.jl | 22 +++++++++++----------- base/libgit2/utils.jl | 2 +- base/linalg/lapack.jl | 8 ++++---- base/linalg/svd.jl | 4 ++-- base/mpfr.jl | 2 +- test/linalg/lapack.jl | 2 +- test/mpfr.jl | 2 +- 9 files changed, 34 insertions(+), 21 deletions(-) diff --git a/NEWS.md b/NEWS.md index c1d99f7c7ad73..d27714f3f4231 100644 --- a/NEWS.md +++ b/NEWS.md @@ -359,6 +359,11 @@ Deprecated or removed * `diagm(A::BitMatrix)` has been deprecated, use `diagm(vec(A))` instead ([#23373]). + * `GMP.gmp_version()`, `GMP.GMP_VERSION`, `GMP.gmp_bits_per_limb()`, and `GMP.GMP_BITS_PER_LIBM` + have been renamed to `GMP.version()`, `GMP.VERSION`, `GMP.bits_per_libm()`, and `GMP.BITS_PER_LIBM`, + respectively. Similarly, `MPFR.get_version()`, has been renamed to `MPFR.version()` ([#23323]). Also, + `LinAlg.LAPACK.laver()` has been renamed to `LinAlg.LAPACK.version()` and now returns a `VersionNumber`. + Command-line option changes --------------------------- diff --git a/base/deprecated.jl b/base/deprecated.jl index b47fd63238add..34403afcd8121 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1726,6 +1726,14 @@ export hex2num # PR #23373 @deprecate diagm(A::BitMatrix) diagm(vec(A)) +# PR 23341 +@eval GMP @deprecate gmp_version() version() false +@eval GMP @Base.deprecate_binding GMP_VERSION VERSION false +@eval GMP @deprecate gmp_bits_per_limb() bits_per_limb() false +@eval GMP @Base.deprecate_binding GMP_BITS_PER_LIMB BITS_PER_LIMB false +@eval MPFR @deprecate get_version() version() false +@eval LinAlg.LAPACK @deprecate laver() version() false + # PR #23271 function IOContext(io::IO; kws...) depwarn("IOContext(io, k=v, ...) is deprecated, use IOContext(io, :k => v, ...) instead.", :IOContext) diff --git a/base/gmp.jl b/base/gmp.jl index 4eb7378020791..b352f2f4683f1 100644 --- a/base/gmp.jl +++ b/base/gmp.jl @@ -21,25 +21,25 @@ else end const CdoubleMax = Union{Float16, Float32, Float64} -gmp_version() = VersionNumber(unsafe_string(unsafe_load(cglobal((:__gmp_version, :libgmp), Ptr{Cchar})))) -gmp_bits_per_limb() = Int(unsafe_load(cglobal((:__gmp_bits_per_limb, :libgmp), Cint))) +version() = VersionNumber(unsafe_string(unsafe_load(cglobal((:__gmp_version, :libgmp), Ptr{Cchar})))) +bits_per_limb() = Int(unsafe_load(cglobal((:__gmp_bits_per_limb, :libgmp), Cint))) -const GMP_VERSION = gmp_version() -const GMP_BITS_PER_LIMB = gmp_bits_per_limb() +const VERSION = version() +const BITS_PER_LIMB = bits_per_limb() # GMP's mp_limb_t is by default a typedef of `unsigned long`, but can also be configured to be either # `unsigned int` or `unsigned long long int`. The correct unsigned type is here named Limb, and must # be used whenever mp_limb_t is in the signature of ccall'ed GMP functions. -if GMP_BITS_PER_LIMB == 32 +if BITS_PER_LIMB == 32 const Limb = UInt32 const SLimbMax = Union{Int8, Int16, Int32} const ULimbMax = Union{UInt8, UInt16, UInt32} -elseif GMP_BITS_PER_LIMB == 64 +elseif BITS_PER_LIMB == 64 const Limb = UInt64 const SLimbMax = Union{Int8, Int16, Int32, Int64} const ULimbMax = Union{UInt8, UInt16, UInt32, UInt64} else - error("GMP: cannot determine the type mp_limb_t (__gmp_bits_per_limb == $GMP_BITS_PER_LIMB)") + error("GMP: cannot determine the type mp_limb_t (__gmp_bits_per_limb == $BITS_PER_LIMB)") end """ @@ -82,10 +82,10 @@ BigInt(x) function __init__() try - if gmp_version().major != GMP_VERSION.major || gmp_bits_per_limb() != GMP_BITS_PER_LIMB - msg = gmp_bits_per_limb() != GMP_BITS_PER_LIMB ? error : warn - msg(string("The dynamically loaded GMP library (version $(gmp_version()) with __gmp_bits_per_limb == $(gmp_bits_per_limb()))\n", - "does not correspond to the compile time version (version $GMP_VERSION with __gmp_bits_per_limb == $GMP_BITS_PER_LIMB).\n", + if version().major != VERSION.major || bits_per_limb() != BITS_PER_LIMB + msg = bits_per_limb() != BITS_PER_LIMB ? error : warn + msg(string("The dynamically loaded GMP library (version $(version()) with __gmp_bits_per_limb == $(bits_per_limb()))\n", + "does not correspond to the compile time version (version $VERSION with __gmp_bits_per_limb == $BITS_PER_LIMB).\n", "Please rebuild Julia.")) end diff --git a/base/libgit2/utils.jl b/base/libgit2/utils.jl index 07906deb9ede6..aecbc88f03139 100644 --- a/base/libgit2/utils.jl +++ b/base/libgit2/utils.jl @@ -26,7 +26,7 @@ function version() minor = Ref{Cint}(0) patch = Ref{Cint}(0) ccall((:git_libgit2_version, :libgit2), Void, - (Ptr{Cint}, Ptr{Cint}, Ptr{Cint}), major, minor, patch) + (Ref{Cint}, Ref{Cint}, Ref{Cint}), major, minor, patch) return VersionNumber(major[], minor[], patch[]) end const VERSION = version() diff --git a/base/linalg/lapack.jl b/base/linalg/lapack.jl index 847e0c13479b0..58aea6a9519e9 100644 --- a/base/linalg/lapack.jl +++ b/base/linalg/lapack.jl @@ -92,14 +92,14 @@ function chkfinite(A::StridedMatrix) end # LAPACK version number -function laver() +function version() major = Ref{BlasInt}(0) minor = Ref{BlasInt}(0) patch = Ref{BlasInt}(0) ccall((@blasfunc(ilaver_), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}), - major, minor, patch) - return major[], minor[], patch[] + (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}), + major, minor, patch) + return VersionNumber(major[], minor[], patch[]) end # (GB) general banded matrices, LU decomposition and solver diff --git a/base/linalg/svd.jl b/base/linalg/svd.jl index 3cb28e9a2bb23..f18faf9ded8b3 100644 --- a/base/linalg/svd.jl +++ b/base/linalg/svd.jl @@ -193,7 +193,7 @@ end """ function svdfact!(A::StridedMatrix{T}, B::StridedMatrix{T}) where T<:BlasFloat # xggsvd3 replaced xggsvd in LAPACK 3.6.0 - if LAPACK.laver() < (3, 6, 0) + if LAPACK.version() < v"3.6.0" U, V, Q, a, b, k, l, R = LAPACK.ggsvd!('U', 'V', 'Q', A, B) else U, V, Q, a, b, k, l, R = LAPACK.ggsvd3!('U', 'V', 'Q', A, B) @@ -290,7 +290,7 @@ end function svdvals!(A::StridedMatrix{T}, B::StridedMatrix{T}) where T<:BlasFloat # xggsvd3 replaced xggsvd in LAPACK 3.6.0 - if LAPACK.laver() < (3, 6, 0) + if LAPACK.version() < v"3.6.0" _, _, _, a, b, k, l, _ = LAPACK.ggsvd!('N', 'N', 'N', A, B) else _, _, _, a, b, k, l, _ = LAPACK.ggsvd3!('N', 'N', 'N', A, B) diff --git a/base/mpfr.jl b/base/mpfr.jl index 05129d39cdc8d..8e2b275258c66 100644 --- a/base/mpfr.jl +++ b/base/mpfr.jl @@ -27,7 +27,7 @@ import Base.Math.lgamma_r import Base.FastMath.sincos_fast -function get_version() +function version() version = unsafe_string(ccall((:mpfr_get_version,:libmpfr), Ptr{Cchar}, ())) build = replace(unsafe_string(ccall((:mpfr_get_patches,:libmpfr), Ptr{Cchar}, ())), ' ', '.') isempty(build) ? VersionNumber(version) : VersionNumber(version * '+' * build) diff --git a/test/linalg/lapack.jl b/test/linalg/lapack.jl index 56838b1410f2a..91bbf937e40e2 100644 --- a/test/linalg/lapack.jl +++ b/test/linalg/lapack.jl @@ -172,7 +172,7 @@ end @test V' ≈ lVt B = rand(elty,10,10) # xggsvd3 replaced xggsvd in LAPACK 3.6.0 - if LAPACK.laver() < (3, 6, 0) + if LAPACK.version() < v"3.6.0" @test_throws DimensionMismatch LAPACK.ggsvd!('S','S','S',A,B) else @test_throws DimensionMismatch LAPACK.ggsvd3!('S','S','S',A,B) diff --git a/test/mpfr.jl b/test/mpfr.jl index fbb6c0cfe016c..0ec518a80a8bb 100644 --- a/test/mpfr.jl +++ b/test/mpfr.jl @@ -887,7 +887,7 @@ setprecision(256) do end # issue #22758 -if MPFR.get_version() > v"3.1.5" || "r11590" in MPFR.get_version().build +if MPFR.version() > v"3.1.5" || "r11590" in MPFR.version().build setprecision(2_000_000) do @test abs(sin(big(pi)/6) - 0.5) < ldexp(big(1.0),-1_999_000) end From 5e8eecd9397cbd5db69b9588324134ae76bdc917 Mon Sep 17 00:00:00 2001 From: kshyatt Date: Thu, 24 Aug 2017 16:05:52 -0700 Subject: [PATCH 147/324] Add more information for CherrypickOptions --- base/libgit2/types.jl | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/base/libgit2/types.jl b/base/libgit2/types.jl index 2dad2ab662482..342290010790f 100644 --- a/base/libgit2/types.jl +++ b/base/libgit2/types.jl @@ -527,6 +527,15 @@ end LibGit2.CherrypickOptions Matches the [`git_cherrypick_options`](https://libgit2.github.com/libgit2/#HEAD/type/git_cherrypick_options) struct. + +The fields represent: + * `version`: version of the struct in use, in case this changes later. For now, always `1`. + * `mainline`: if cherrypicking a merge commit, specifies the parent number (starting at `1`) + which will allow cherrypick to apply the changes relative to that parent. Only relevant if + cherrypicking a merge commit. Default is `0`. + * `merge_opts`: options for merging the changes in. See [`MergeOptions`](@ref) for more information. + * `checkout_opts`: options for the checkout of the commit being cherrypicked. See [`CheckoutOptions`](@ref) + for more information. """ @kwdef struct CherrypickOptions version::Cuint = 1 From b46c74cc9c61769aade33e407c202bf88aaf39d1 Mon Sep 17 00:00:00 2001 From: Alex Arslan Date: Thu, 24 Aug 2017 16:08:58 -0700 Subject: [PATCH 148/324] Preserve the input element type in unique (#23208) Previously the element type of the output was the smallest type that would fit the union of the input's individual element types. Now the output has an identical element type to the input. Fixes #22696. --- NEWS.md | 5 +++++ base/set.jl | 10 ++++++++-- test/core.jl | 3 ++- test/sets.jl | 2 ++ 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/NEWS.md b/NEWS.md index d27714f3f4231..bbd4f44b6517e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -168,6 +168,10 @@ This section lists changes that do not have deprecation warnings. way as `^(A::Integer, p::Integer)`. This means, for instance, that `[1 1; 0 1]^big(1)` will return a `Matrix{BigInt}` instead of a `Matrix{Int}` ([#23366]). + * The element type of the input is now preserved in `unique`. Previously the element type + of the output was shrunk to fit the union of the type of each element in the input. + ([#22696]) + Library improvements -------------------- @@ -1204,6 +1208,7 @@ Command-line option changes [#22588]: https://github.com/JuliaLang/julia/issues/22588 [#22605]: https://github.com/JuliaLang/julia/issues/22605 [#22666]: https://github.com/JuliaLang/julia/issues/22666 +[#22696]: https://github.com/JuliaLang/julia/issues/22696 [#22703]: https://github.com/JuliaLang/julia/issues/22703 [#22712]: https://github.com/JuliaLang/julia/issues/22712 [#22718]: https://github.com/JuliaLang/julia/issues/22718 diff --git a/base/set.jl b/base/set.jl index f5ced5e45d779..86f1e93b889b4 100644 --- a/base/set.jl +++ b/base/set.jl @@ -240,7 +240,8 @@ const ⊆ = issubset Return an array containing only the unique elements of collection `itr`, as determined by [`isequal`](@ref), in the order that the first of each -set of equivalent elements originally appears. +set of equivalent elements originally appears. The element type of the +input is preserved. # Examples ```jldoctest @@ -249,6 +250,11 @@ julia> unique([1, 2, 6, 2]) 1 2 6 + +julia> unique(Real[1, 1.0, 2]) +2-element Array{Real,1}: + 1 + 2 ``` """ function unique(itr) @@ -260,7 +266,7 @@ function unique(itr) return out end x, i = next(itr, i) - if !isleaftype(T) + if !isleaftype(T) && iteratoreltype(itr) == EltypeUnknown() S = typeof(x) return _unique_from(itr, S[x], Set{S}((x,)), i) end diff --git a/test/core.jl b/test/core.jl index adf0778660968..08c3986562915 100644 --- a/test/core.jl +++ b/test/core.jl @@ -5382,7 +5382,8 @@ for U in unboxedunions # deleteat! F = Base.uniontypes(U)[2] A = U[rand(F(1):F(len)) for i = 1:len] - deleteat!(A, map(Int, sort!(unique(A[1:4])))) + # The 2-arg `unique` method works around #22688 + deleteat!(A, map(Int, sort!(unique(identity, A[1:4])))) A = U[initvalue2(F2) for i = 1:len] deleteat!(A, 1:2) @test length(A) == len - 2 diff --git a/test/sets.jl b/test/sets.jl index 3efb3804be7bf..7041e475295b1 100644 --- a/test/sets.jl +++ b/test/sets.jl @@ -233,6 +233,8 @@ u = unique([1,1,2]) # issue 20105 @test @inferred(unique(x for x in 1:1)) == [1] @test unique(x for x in Any[1,1.0])::Vector{Real} == [1] +@test unique(x for x in Real[1,1.0])::Vector{Real} == [1] +@test unique(Integer[1,1,2])::Vector{Integer} == [1,2] # unique! @testset "unique!" begin From 9ab6acac48a2b6cdc7b4c4c5b9a473aed97a761f Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Fri, 25 Aug 2017 08:56:34 +0200 Subject: [PATCH 149/324] REPL: implement Alt-{u,c,l} to change the case of the next word (#23379) Fixes part of #8447. --- base/repl/LineEdit.jl | 22 ++++++++++++++++++++++ doc/src/manual/interacting-with-julia.md | 3 +++ test/lineedit.jl | 12 ++++++++++++ 3 files changed, 37 insertions(+) diff --git a/base/repl/LineEdit.jl b/base/repl/LineEdit.jl index 89e3bbb367b53..f29956839b0da 100644 --- a/base/repl/LineEdit.jl +++ b/base/repl/LineEdit.jl @@ -636,6 +636,25 @@ function edit_transpose_words(buf::IOBuffer, mode=:emacs) end +edit_upper_case(s) = edit_replace_word_right(s, uppercase) +edit_lower_case(s) = edit_replace_word_right(s, lowercase) +edit_title_case(s) = edit_replace_word_right(s, ucfirst) + +edit_replace_word_right(s, replace::Function) = + edit_replace_word_right(buffer(s), replace) && refresh_line(s) + +function edit_replace_word_right(buf::IOBuffer, replace::Function) + # put the cursor at the beginning of the next word + skipchars(buf, is_non_word_char) + b = position(buf) + char_move_word_right(buf) + e = position(buf) + e == b && return false + newstr = replace(String(buf.data[b+1:e])) + splice_buffer!(buf, b:e-1, newstr) + true +end + edit_clear(buf::IOBuffer) = truncate(buf, 0) function edit_clear(s::MIState) @@ -1531,6 +1550,9 @@ AnyDict( end, "^T" => (s,o...)->edit_transpose_chars(s), "\et" => (s,o...)->edit_transpose_words(s), + "\eu" => (s,o...)->edit_upper_case(s), + "\el" => (s,o...)->edit_lower_case(s), + "\ec" => (s,o...)->edit_title_case(s), ) const history_keymap = AnyDict( diff --git a/doc/src/manual/interacting-with-julia.md b/doc/src/manual/interacting-with-julia.md index c8ba66d6236f5..03653d2cfeaf8 100644 --- a/doc/src/manual/interacting-with-julia.md +++ b/doc/src/manual/interacting-with-julia.md @@ -173,6 +173,9 @@ to do so). | `^K` | "Kill" to end of line, placing the text in a buffer | | `^Y` | "Yank" insert the text from the kill buffer | | `^T` | Transpose the characters about the cursor | +| `meta-u` | Change the next word to uppercase | +| `meta-c` | Change the next word to titlecase | +| `meta-l` | Change the next word to lowercase | | `^Q` | Write a number in REPL and press `^Q` to open editor at corresponding stackframe or method | diff --git a/test/lineedit.jl b/test/lineedit.jl index 71aed1ffe6421..8bbf881d7adb4 100644 --- a/test/lineedit.jl +++ b/test/lineedit.jl @@ -574,3 +574,15 @@ end @test bufferdata(s) == "begin\n" * ' '^(i-6) * "\n x" end end + +@testset "change case on the right" begin + buf = IOBuffer() + LineEdit.edit_insert(buf, "aa bb CC") + seekstart(buf) + LineEdit.edit_upper_case(buf) + LineEdit.edit_title_case(buf) + @test String(take!(copy(buf))) == "AA Bb CC" + @test position(buf) == 5 + LineEdit.edit_lower_case(buf) + @test String(take!(copy(buf))) == "AA Bb cc" +end From 6922ce8f271fe85fe5777cc379a2bd3f7cccf68b Mon Sep 17 00:00:00 2001 From: Mus M Date: Fri, 25 Aug 2017 03:50:38 -0400 Subject: [PATCH 150/324] Misc remove `&x` ccall syntax (#23301) --- base/hashing2.jl | 2 +- base/irrationals.jl | 3 +-- base/libc.jl | 17 +++++++---------- base/printf.jl | 6 +++--- base/socket.jl | 8 ++++---- 5 files changed, 16 insertions(+), 20 deletions(-) diff --git a/base/hashing2.jl b/base/hashing2.jl index 37a401ac974f0..b4c548b2ccab1 100644 --- a/base/hashing2.jl +++ b/base/hashing2.jl @@ -136,7 +136,7 @@ function decompose(x::BigFloat)::Tuple{BigInt, Int, Int} s = BigInt() s.size = cld(x.prec, 8*sizeof(GMP.Limb)) # limbs b = s.size * sizeof(GMP.Limb) # bytes - ccall((:__gmpz_realloc2, :libgmp), Void, (Ptr{BigInt}, Culong), &s, 8b) # bits + ccall((:__gmpz_realloc2, :libgmp), Void, (Ref{BigInt}, Culong), s, 8b) # bits ccall(:memcpy, Ptr{Void}, (Ptr{Void}, Ptr{Void}, Csize_t), s.d, x.d, b) # bytes s, x.exp - 8b, x.sign end diff --git a/base/irrationals.jl b/base/irrationals.jl index 27b94a5968903..1d53477674423 100644 --- a/base/irrationals.jl +++ b/base/irrationals.jl @@ -121,8 +121,7 @@ macro irrational(sym, val, def) function Base.convert(::Type{BigFloat}, ::Irrational{$qsym}) c = BigFloat() ccall(($(string("mpfr_const_", def)), :libmpfr), - Cint, (Ptr{BigFloat}, Int32), - &c, MPFR.ROUNDING_MODE[]) + Cint, (Ref{BigFloat}, Int32), c, MPFR.ROUNDING_MODE[]) return c end end : quote diff --git a/base/libc.jl b/base/libc.jl index 585d3c94a186d..34301239516ca 100644 --- a/base/libc.jl +++ b/base/libc.jl @@ -155,7 +155,7 @@ mutable struct TmStruct t = floor(t) tm = TmStruct() # TODO: add support for UTC via gmtime_r() - ccall(:localtime_r, Ptr{TmStruct}, (Ptr{Int}, Ptr{TmStruct}), &t, &tm) + ccall(:localtime_r, Ptr{TmStruct}, (Ref{Int}, Ref{TmStruct}), t, tm) return tm end end @@ -171,11 +171,9 @@ strftime(t) = strftime("%c", t) strftime(fmt::AbstractString, t::Real) = strftime(fmt, TmStruct(t)) function strftime(fmt::AbstractString, tm::TmStruct) timestr = Base.StringVector(128) - n = ccall(:strftime, Int, (Ptr{UInt8}, Int, Cstring, Ptr{TmStruct}), - timestr, length(timestr), fmt, &tm) - if n == 0 - return "" - end + n = ccall(:strftime, Int, (Ptr{UInt8}, Int, Cstring, Ref{TmStruct}), + timestr, length(timestr), fmt, tm) + n == 0 && return "" return String(resize!(timestr,n)) end @@ -192,8 +190,7 @@ determine the timezone. strptime(timestr::AbstractString) = strptime("%c", timestr) function strptime(fmt::AbstractString, timestr::AbstractString) tm = TmStruct() - r = ccall(:strptime, Cstring, (Cstring, Cstring, Ptr{TmStruct}), - timestr, fmt, &tm) + r = ccall(:strptime, Cstring, (Cstring, Cstring, Ref{TmStruct}), timestr, fmt, tm) # the following would tell mktime() that this is a local time, and that # it should try to guess the timezone. not sure if/how this should be # exposed in the API. @@ -206,7 +203,7 @@ function strptime(fmt::AbstractString, timestr::AbstractString) # if we didn't explicitly parse the weekday or year day, use mktime # to fill them in automatically. if !ismatch(r"([^%]|^)%(a|A|j|w|Ow)", fmt) - ccall(:mktime, Int, (Ptr{TmStruct},), &tm) + ccall(:mktime, Int, (Ref{TmStruct},), tm) end end return tm @@ -219,7 +216,7 @@ end Converts a `TmStruct` struct to a number of seconds since the epoch. """ -time(tm::TmStruct) = Float64(ccall(:mktime, Int, (Ptr{TmStruct},), &tm)) +time(tm::TmStruct) = Float64(ccall(:mktime, Int, (Ref{TmStruct},), tm)) """ time() diff --git a/base/printf.jl b/base/printf.jl index 230ea2b983de6..4fcde4a41f4dd 100644 --- a/base/printf.jl +++ b/base/printf.jl @@ -1102,7 +1102,7 @@ ini_hex(out, d::BigFloat, ndigits::Int, flags::String, width::Int, precision::In ini_HEX(out, d::BigFloat, ndigits::Int, flags::String, width::Int, precision::Int, c::Char) = bigfloat_printf(out, d, flags, width, precision, c) ini_hex(out, d::BigFloat, flags::String, width::Int, precision::Int, c::Char) = bigfloat_printf(out, d, flags, width, precision, c) ini_HEX(out, d::BigFloat, flags::String, width::Int, precision::Int, c::Char) = bigfloat_printf(out, d, flags, width, precision, c) -function bigfloat_printf(out, d, flags::String, width::Int, precision::Int, c::Char) +function bigfloat_printf(out, d::BigFloat, flags::String, width::Int, precision::Int, c::Char) fmt_len = sizeof(flags)+4 if width > 0 fmt_len += ndigits(width) @@ -1130,8 +1130,8 @@ function bigfloat_printf(out, d, flags::String, width::Int, precision::Int, c::C @assert length(printf_fmt) == fmt_len bufsiz = length(DIGITS) lng = ccall((:mpfr_snprintf,:libmpfr), Int32, - (Ptr{UInt8}, Culong, Ptr{UInt8}, Ptr{BigFloat}...), - DIGITS, bufsiz, printf_fmt, &d) + (Ptr{UInt8}, Culong, Ptr{UInt8}, Ref{BigFloat}...), + DIGITS, bufsiz, printf_fmt, d) lng > 0 || error("invalid printf formatting for BigFloat") unsafe_write(out, pointer(DIGITS), min(lng, bufsiz-1)) return (false, ()) diff --git a/base/socket.jl b/base/socket.jl index 588e0097a69a5..997e6d2a5cb04 100644 --- a/base/socket.jl +++ b/base/socket.jl @@ -551,8 +551,8 @@ function _send(sock::UDPSocket, ipaddr::IPv4, port::UInt16, buf) end function _send(sock::UDPSocket, ipaddr::IPv6, port::UInt16, buf) - ccall(:jl_udp_send6, Cint, (Ptr{Void}, UInt16, Ptr{UInt128}, Ptr{UInt8}, Csize_t, Ptr{Void}), - sock.handle, hton(port), &hton(ipaddr.host), buf, sizeof(buf), uv_jl_sendcb::Ptr{Void}) + ccall(:jl_udp_send6, Cint, (Ptr{Void}, UInt16, Ref{UInt128}, Ptr{UInt8}, Csize_t, Ptr{Void}), + sock.handle, hton(port), hton(ipaddr.host), buf, sizeof(buf), uv_jl_sendcb::Ptr{Void}) end """ @@ -726,8 +726,8 @@ function connect!(sock::TCPSocket, host::IPv6, port::Integer) if !(0 <= port <= typemax(UInt16)) throw(ArgumentError("port out of range, must be 0 ≤ port ≤ 65535, got $port")) end - uv_error("connect", ccall(:jl_tcp6_connect, Int32, (Ptr{Void}, Ptr{UInt128}, UInt16, Ptr{Void}), - sock.handle, &hton(host.host), hton(UInt16(port)), uv_jl_connectcb::Ptr{Void})) + uv_error("connect", ccall(:jl_tcp6_connect, Int32, (Ptr{Void}, Ref{UInt128}, UInt16, Ptr{Void}), + sock.handle, hton(host.host), hton(UInt16(port)), uv_jl_connectcb::Ptr{Void})) sock.status = StatusConnecting nothing end From c44461a4418c381bba3614ecc896bdca2977e272 Mon Sep 17 00:00:00 2001 From: Mus M Date: Fri, 25 Aug 2017 03:53:43 -0400 Subject: [PATCH 151/324] Use iszero and isone in MPFR (#23322) --- base/mpfr.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/base/mpfr.jl b/base/mpfr.jl index 8e2b275258c66..ceea0f6e92433 100644 --- a/base/mpfr.jl +++ b/base/mpfr.jl @@ -237,7 +237,7 @@ floor(::Type{Integer}, x::BigFloat) = floor(BigInt, x) ceil(::Type{Integer}, x::BigFloat) = ceil(BigInt, x) round(::Type{Integer}, x::BigFloat) = round(BigInt, x) -convert(::Type{Bool}, x::BigFloat) = x==0 ? false : x==1 ? true : +convert(::Type{Bool}, x::BigFloat) = iszero(x) ? false : isone(x) ? true : throw(InexactError(:convert, Bool, x)) function convert(::Type{BigInt},x::BigFloat) isinteger(x) || throw(InexactError(:convert, BigInt, x)) @@ -278,7 +278,7 @@ big(::Type{<:AbstractFloat}) = BigFloat function convert(::Type{Rational{BigInt}}, x::AbstractFloat) if isnan(x); return zero(BigInt)//zero(BigInt); end if isinf(x); return copysign(one(BigInt),x)//zero(BigInt); end - if x == 0; return zero(BigInt) // one(BigInt); end + if iszero(x); return zero(BigInt) // one(BigInt); end s = max(precision(x) - exponent(x), 0) BigInt(ldexp(x,s)) // (BigInt(1) << s) end @@ -798,7 +798,7 @@ function copysign(x::BigFloat, y::BigFloat) end function exponent(x::BigFloat) - if x == 0 || !isfinite(x) + if iszero(x) || !isfinite(x) throw(DomainError(x, "`x` must be non-zero and finite.")) end # The '- 1' is to make it work as Base.exponent @@ -926,7 +926,7 @@ function string(x::BigFloat) buf = Base.StringVector(lng + 1) lng = ccall((:mpfr_snprintf,:libmpfr), Int32, (Ptr{UInt8}, Culong, Ptr{UInt8}, Ptr{BigFloat}...), buf, lng + 1, "%.Re", &x) end - n = (1 <= x < 10 || -10 < x <= -1 || x == 0) ? lng - 4 : lng + n = (1 <= x < 10 || -10 < x <= -1 || iszero(x)) ? lng - 4 : lng return String(resize!(buf,n)) end From daa5575e39c3b69e6d7b7e0f42aba712acc1ff77 Mon Sep 17 00:00:00 2001 From: 0x47 <0x47@users.noreply.github.com> Date: Fri, 25 Aug 2017 13:22:59 +0200 Subject: [PATCH 152/324] Disable globbing for curl download (#23254) --- base/interactiveutil.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/interactiveutil.jl b/base/interactiveutil.jl index 3f6a5dce96192..748f075b4b81b 100644 --- a/base/interactiveutil.jl +++ b/base/interactiveutil.jl @@ -648,7 +648,7 @@ else rethrow() end elseif downloadcmd == :curl - run(`curl -L -f -o $filename $url`) + run(`curl -g -L -f -o $filename $url`) elseif downloadcmd == :fetch run(`fetch -f $filename $url`) else From 9944d3dd253dd337d73969a88eb3aa291b3138a3 Mon Sep 17 00:00:00 2001 From: Ivan Gonzalez Date: Fri, 25 Aug 2017 08:07:33 -0400 Subject: [PATCH 153/324] [DOC] Document Void type (#23324) Fixes #22806 --- base/docs/basedocs.jl | 7 +++++++ doc/src/stdlib/base.md | 1 + 2 files changed, 8 insertions(+) diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index b4b9a94cb89f9..366c1db1bd01e 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -696,6 +696,13 @@ DevNull # doc strings for code in boot.jl and built-ins +""" + Void + +A type with no fields that is the type [`nothing`](@ref). +""" +Void + """ nothing diff --git a/doc/src/stdlib/base.md b/doc/src/stdlib/base.md index b2127bf65bed0..cee6d5872db44 100644 --- a/doc/src/stdlib/base.md +++ b/doc/src/stdlib/base.md @@ -115,6 +115,7 @@ Base.instances ## Special Types ```@docs +Core.Void Core.Any Base.Enums.@enum Core.Union From 90a33006980bf982f223d209671e9dd87641b2f2 Mon Sep 17 00:00:00 2001 From: Mus M Date: Fri, 25 Aug 2017 09:58:09 -0400 Subject: [PATCH 154/324] Use refs instead of Vector in Lapack (#23419) * Use refs instead of Vector in Lapack * Use uninitialized refs * Manual constructors * Simply --- base/linalg/blas.jl | 6 ++-- base/linalg/lapack.jl | 80 +++++++++++++++++++++---------------------- 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/base/linalg/blas.jl b/base/linalg/blas.jl index 27cc4ca3ebed9..530413a0edcf2 100644 --- a/base/linalg/blas.jl +++ b/base/linalg/blas.jl @@ -143,11 +143,11 @@ function check() (_, info) = LinAlg.LAPACK.potrf!('U', [1.0 0.0; 0.0 -1.0]) if info != 2 # mangled info code if info == 2^33 - error("""BLAS and LAPACK are compiled with 32-bit integer support, but Julia expects 64-bit integers. Please build Julia with USE_BLAS64=0.""") + error("BLAS and LAPACK are compiled with 32-bit integer support, but Julia expects 64-bit integers. Please build Julia with USE_BLAS64=0.") elseif info == 0 - error("""BLAS and LAPACK are compiled with 64-bit integer support but Julia expects 32-bit integers. Please build Julia with USE_BLAS64=1.""") + error("BLAS and LAPACK are compiled with 64-bit integer support but Julia expects 32-bit integers. Please build Julia with USE_BLAS64=1.") else - error("""The LAPACK library produced an undefined error code. Please verify the installation of BLAS and LAPACK.""") + error("The LAPACK library produced an undefined error code. Please verify the installation of BLAS and LAPACK.") end end diff --git a/base/linalg/lapack.jl b/base/linalg/lapack.jl index 58aea6a9519e9..c73ea95df6b4c 100644 --- a/base/linalg/lapack.jl +++ b/base/linalg/lapack.jl @@ -148,7 +148,7 @@ for (gbtrf, gbtrs, elty) in end ccall((@blasfunc($gbtrs), liblapack), Void, (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), &trans, &n, &kl, &ku, &size(B,2), AB, &max(1,stride(AB,2)), ipiv, B, &max(1,stride(B,2)), info) @@ -1040,7 +1040,7 @@ for (gesvx, elty) in ldaf = stride(AF,2) nrhs = size(B,2) ldb = stride(B,2) - rcond = Vector{$elty}(1) + rcond = Ref{$elty}() ferr = similar(A, $elty, nrhs) berr = similar(A, $elty, nrhs) work = Vector{$elty}(4n) @@ -1062,7 +1062,7 @@ for (gesvx, elty) in chknonsingular(info[]) end #WORK(1) contains the reciprocal pivot growth factor norm(A)/norm(U) - X, equed, R, C, B, rcond[1], ferr, berr, work[1] + X, equed, R, C, B, rcond[], ferr, berr, work[1] end function gesvx!(A::StridedMatrix{$elty}, B::StridedVecOrMat{$elty}) @@ -1109,7 +1109,7 @@ for (gesvx, elty, relty) in ldaf = stride(AF,2) nrhs = size(B,2) ldb = stride(B,2) - rcond = Vector{$relty}(1) + rcond = Ref{$relty}() ferr = similar(A, $relty, nrhs) berr = similar(A, $relty, nrhs) work = Vector{$elty}(2n) @@ -1131,7 +1131,7 @@ for (gesvx, elty, relty) in chknonsingular(info[]) end #RWORK(1) contains the reciprocal pivot growth factor norm(A)/norm(U) - X, equed, R, C, B, rcond[1], ferr, berr, rwork[1] + X, equed, R, C, B, rcond[], ferr, berr, rwork[1] end #Wrapper for the no-equilibration, no-transpose calculation @@ -1205,8 +1205,7 @@ for (gelsd, gelsy, elty) in end newB = [B; zeros($elty, max(0, n - size(B, 1)), size(B, 2))] s = similar(A, $elty, min(m, n)) - rcond = convert($elty, rcond) - rnk = Vector{BlasInt}(1) + rnk = Ref{BlasInt}() info = Ref{BlasInt}() work = Vector{$elty}(1) lwork = BlasInt(-1) @@ -1215,10 +1214,12 @@ for (gelsd, gelsy, elty) in ccall((@blasfunc($gelsd), liblapack), Void, (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, + Ptr{$elty}, Ref{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}), - &m, &n, &size(B,2), A, &max(1,stride(A,2)), - newB, &max(1,stride(B,2),n), s, &rcond, rnk, work, &lwork, iwork, info) + &m, &n, &size(B,2), + A, &max(1,stride(A,2)), newB, &max(1,stride(B,2),n), + s, $elty(rcond), rnk, work, + &lwork, iwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -1226,7 +1227,7 @@ for (gelsd, gelsy, elty) in resize!(iwork, iwork[1]) end end - subsetrows(B, newB, n), rnk[1] + subsetrows(B, newB, n), rnk[] end # SUBROUTINE DGELSY( M, N, NRHS, A, LDA, B, LDB, JPVT, RCOND, RANK, @@ -1250,8 +1251,7 @@ for (gelsd, gelsy, elty) in lda = max(1, m) ldb = max(1, m, n) jpvt = zeros(BlasInt, n) - rcond = convert($elty, rcond) - rnk = Vector{BlasInt}(1) + rnk = Ref{BlasInt}() work = Vector{$elty}(1) lwork = BlasInt(-1) info = Ref{BlasInt}() @@ -1259,11 +1259,11 @@ for (gelsd, gelsy, elty) in ccall((@blasfunc($gelsy), liblapack), Void, (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + Ref{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), &m, &n, &nrhs, A, &lda, newB, &ldb, jpvt, - &rcond, rnk, work, &lwork, + $elty(rcond), rnk, work, &lwork, info) chklapackerror(info[]) if i == 1 @@ -1271,7 +1271,7 @@ for (gelsd, gelsy, elty) in resize!(work, lwork) end end - subsetrows(B, newB, n), rnk[1] + subsetrows(B, newB, n), rnk[] end end end @@ -1298,8 +1298,7 @@ for (gelsd, gelsy, elty, relty) in end newB = [B; zeros($elty, max(0, n - size(B, 1)), size(B, 2))] s = similar(A, $relty, min(m, n)) - rcond = convert($relty, rcond) - rnk = Vector{BlasInt}(1) + rnk = Ref{BlasInt}() info = Ref{BlasInt}() work = Vector{$elty}(1) lwork = BlasInt(-1) @@ -1309,10 +1308,12 @@ for (gelsd, gelsy, elty, relty) in ccall((@blasfunc($gelsd), liblapack), Void, (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$relty}, - Ptr{$relty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$relty}, Ptr{BlasInt}, Ptr{BlasInt}), - &m, &n, &size(B,2), A, &max(1,stride(A,2)), - newB, &max(1,stride(B,2),n), s, &rcond, rnk, work, &lwork, rwork, iwork, info) + Ref{$relty}, Ref{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + Ptr{$relty}, Ref{BlasInt}, Ref{BlasInt}), + &m, &n, &size(B,2), A, + &max(1,stride(A,2)), newB, &max(1,stride(B,2),n), s, + $relty(rcond), rnk, work, &lwork, + rwork, iwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -1321,7 +1322,7 @@ for (gelsd, gelsy, elty, relty) in resize!(iwork, iwork[1]) end end - subsetrows(B, newB, n), rnk[1] + subsetrows(B, newB, n), rnk[] end # SUBROUTINE ZGELSY( M, N, NRHS, A, LDA, B, LDB, JPVT, RCOND, RANK, @@ -1345,8 +1346,7 @@ for (gelsd, gelsy, elty, relty) in lda = max(1, m) ldb = max(1, m, n) jpvt = zeros(BlasInt, n) - rcond = convert($relty, rcond) - rnk = Vector{BlasInt}(1) + rnk = Ref{BlasInt}(1) work = Vector{$elty}(1) lwork = BlasInt(-1) rwork = Vector{$relty}(2n) @@ -1355,11 +1355,11 @@ for (gelsd, gelsy, elty, relty) in ccall((@blasfunc($gelsy), liblapack), Void, (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + Ref{$relty}, Ref{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$relty}, Ptr{BlasInt}), &m, &n, &nrhs, A, &lda, newB, &ldb, jpvt, - &rcond, rnk, work, &lwork, + $relty(rcond), rnk, work, &lwork, rwork, info) chklapackerror(info[]) if i == 1 @@ -1367,7 +1367,7 @@ for (gelsd, gelsy, elty, relty) in resize!(work, lwork) end end - subsetrows(B, newB, n), rnk[1] + subsetrows(B, newB, n), rnk[] end end end @@ -3334,17 +3334,17 @@ for (trcon, trevc, trrfs, elty) in chkdiag(diag) n = checksquare(A) chkuplo(uplo) - rcond = Vector{$elty}(1) + rcond = Ref{$elty}() work = Vector{$elty}(3n) iwork = Vector{BlasInt}(n) info = Ref{BlasInt}() ccall((@blasfunc($trcon), liblapack), Void, (Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), + Ptr{$elty}, Ptr{BlasInt}, Ref{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), &norm, &uplo, &diag, &n, A, &max(1,stride(A,2)), rcond, work, iwork, info) chklapackerror(info[]) - rcond[1] + rcond[] end # SUBROUTINE DTREVC( SIDE, HOWMNY, SELECT, N, T, LDT, VL, LDVL, VR, @@ -3462,17 +3462,17 @@ for (trcon, trevc, trrfs, elty, relty) in n = checksquare(A) chkuplo(uplo) chkdiag(diag) - rcond = Vector{$relty}(1) + rcond = Ref{$relty}(1) work = Vector{$elty}(2n) rwork = Vector{$relty}(n) info = Ref{BlasInt}() ccall((@blasfunc($trcon), liblapack), Void, (Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$relty}, Ptr{$elty}, Ptr{$relty}, Ptr{BlasInt}), + Ptr{$elty}, Ptr{BlasInt}, Ref{$relty}, Ptr{$elty}, Ptr{$relty}, Ptr{BlasInt}), &norm, &uplo, &diag, &n, A, &max(1,stride(A,2)), rcond, work, rwork, info) chklapackerror(info[]) - rcond[1] + rcond[] end # SUBROUTINE ZTREVC( SIDE, HOWMNY, SELECT, N, T, LDT, VL, LDVL, VR, @@ -5337,18 +5337,18 @@ for (gecon, elty) in chkstride1(A) n = checksquare(A) lda = max(1, stride(A, 2)) - rcond = Vector{$elty}(1) + rcond = Ref{$elty}() work = Vector{$elty}(4n) iwork = Vector{BlasInt}(n) info = Ref{BlasInt}() ccall((@blasfunc($gecon), liblapack), Void, (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, + Ptr{$elty}, Ref{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), &normtype, &n, A, &lda, &anorm, rcond, work, iwork, info) chklapackerror(info[]) - rcond[1] + rcond[] end end end @@ -5371,18 +5371,18 @@ for (gecon, elty, relty) in chkstride1(A) n = checksquare(A) lda = max(1, stride(A, 2)) - rcond = Vector{$relty}(1) + rcond = Ref{$relty}() work = Vector{$elty}(2n) rwork = Vector{$relty}(2n) info = Ref{BlasInt}() ccall((@blasfunc($gecon), liblapack), Void, (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ptr{$relty}, + Ptr{$relty}, Ref{$relty}, Ptr{$elty}, Ptr{$relty}, Ptr{BlasInt}), &normtype, &n, A, &lda, &anorm, rcond, work, rwork, info) chklapackerror(info[]) - rcond[1] + rcond[] end end end From 4d4b9ce06de296df3d51646c66192681ca1a8788 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Fri, 25 Aug 2017 10:54:04 -0400 Subject: [PATCH 155/324] fix #23430, counter overflow in subtyping --- src/subtype.c | 5 +++-- test/subtype.jl | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index 78a39b6ca481d..d713acdc2e67c 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -420,9 +420,10 @@ static int subtype_ufirst(jl_value_t *x, jl_value_t *y, jl_stenv_t *e) static void record_var_occurrence(jl_varbinding_t *vb, jl_stenv_t *e, int param) { if (vb != NULL && param) { - if (param == 2 && e->invdepth > vb->depth0) + // saturate counters at 2; we don't need values bigger than that + if (param == 2 && e->invdepth > vb->depth0 && vb->occurs_inv < 2) vb->occurs_inv++; - else + else if (vb->occurs_cov < 2) vb->occurs_cov++; } } diff --git a/test/subtype.jl b/test/subtype.jl index e18f4d6593f29..050dc8af0a6fb 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -1130,3 +1130,22 @@ end @testintersect(Tuple{DataType, Any}, Tuple{Type{T}, Int} where T, Tuple{DataType, Int}) + +# issue #23430 +@test [0 0.; 0 0.; 0 0.; 0 0.; 0 0.; 0 0.; 0 0.; 0 0.; 0 0.; 0 0.; 0 0.; 0 0.; + 0 0.; 0 0.; 0 0.; 0 0.; 0 0.; 0 0.; 0 0.; 0 0.; 0 0.; 0 0.; 0 0.; 0 0.; + 0 0.; 0 0.; 0 0.; 0 0.; 0 0.; 0 0.; 0 0.; 0 0.; 0 0.; 0 0.; 0 0.; 0 0.; + 0 0.; 0 0.; 0 0.; 0 0.; 0 0.; 0 0.; 0 0.; 0 0.; 0 0.; 0 0.; 0 0.; 0 0.; + 0 0.; 0 0.; 0 0.; 0 0.; 0 0.; 0 0.; 0 0.; 0 0.; 0 0.; 0 0.; 0 0.; 0 0.; + 0 0.; 0 0.; 0 0.; 0 0.] isa Matrix{Float64} +@test !(Tuple{Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64, + Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64, + Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64, + Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64, + Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64, + Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64, + Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64, + Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64, + Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64, + Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64, + Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64} <: (Tuple{Vararg{T}} where T<:Number)) From 706e72051799d8e25c08f2cd20d7e31e598f2e41 Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Fri, 25 Aug 2017 08:19:54 -0700 Subject: [PATCH 156/324] Add docs for GitRevWalker and push_head! (#22654) * Add docs for GitRevWalker and push_head! --- base/libgit2/walker.jl | 77 ++++++++++++++++++++++++++++++++++++++ doc/src/devdocs/libgit2.md | 5 +++ 2 files changed, 82 insertions(+) diff --git a/base/libgit2/walker.jl b/base/libgit2/walker.jl index 41b44fcbc14b4..ecac18dbeb7de 100644 --- a/base/libgit2/walker.jl +++ b/base/libgit2/walker.jl @@ -1,5 +1,23 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license +""" + GitRevWalker(repo::GitRepo) + +A `GitRevWalker` *walks* through the *revisions* (i.e. commits) of +a git repository `repo`. It is a collection of the commits +in the repository, and supports iteration and calls to [`map`](@ref LibGit2.map) +and [`count`](@ref LibGit2.count) (for instance, `count` could be used to determine +what percentage of commits in a repository were made by a certain +author). + +```julia +cnt = LibGit2.with(LibGit2.GitRevWalker(repo)) do walker + count((oid,repo)->(oid == commit_oid1), walker, oid=commit_oid1, by=LibGit2.Consts.SORT_TIME) +end +``` +Here, `count` finds the number of commits along the walk with a certain `GitHash`. +Since the `GitHash` is unique to a commit, `cnt` will be `1`. +""" function GitRevWalker(repo::GitRepo) w_ptr = Ref{Ptr{Void}}(C_NULL) @check ccall((:git_revwalk_new, :libgit2), Cint, @@ -27,11 +45,25 @@ end Base.iteratorsize(::Type{GitRevWalker}) = Base.SizeUnknown() +""" + LibGit2.push_head!(w::GitRevWalker) + +Push the HEAD commit and its ancestors onto the [`GitRevWalker`](@ref) +`w`. This ensures that HEAD and all its ancestor commits will be encountered +during the walk. +""" function push_head!(w::GitRevWalker) @check ccall((:git_revwalk_push_head, :libgit2), Cint, (Ptr{Void},), w.ptr) return w end +""" + LibGit2.push!(w::GitRevWalker, cid::GitHash) + +Start the [`GitRevWalker`](@ref) `walker` at commit `cid`. This function can be used +to apply a function to all commits since a certain year, by passing the first commit +of that year as `cid` and then passing the resulting `w` to [`map`](@ref LibGit2.map). +""" function Base.push!(w::GitRevWalker, cid::GitHash) @check ccall((:git_revwalk_push, :libgit2), Cint, (Ptr{Void}, Ptr{GitHash}), w.ptr, Ref(cid)) return w @@ -50,6 +82,27 @@ end repository(w::GitRevWalker) = w.owner +""" + LibGit2.map(f::Function, walker::GitRevWalker; oid::GitHash=GitHash(), by::Cint=Consts.SORT_NONE, rev::Bool=false) + +Using the [`GitRevWalker`](@ref) `walker` to "walk" over every commit in the repository's history, +apply `f` to each commit in the walk. The keyword arguments are: + * `oid`: The [`GitHash`](@ref) of the commit to begin the walk from. The default is to use + [`push_head!`](@ref) and therefore the HEAD commit and all its ancestors. + * `by`: The sorting method. The default is not to sort. Other options are to sort by + topology (`LibGit2.Consts.SORT_TOPOLOGICAL`), to sort forwards in time + (`LibGit2.Consts.SORT_TIME`, most ancient first) or to sort backwards in time + (`LibGit2.Consts.SORT_REVERSE`, most recent first). + * `rev`: Whether to reverse the sorted order (for instance, if topological sorting is used). + +# Examples +```julia +oids = LibGit2.with(LibGit2.GitRevWalker(repo)) do walker + LibGit2.map((oid, repo)->string(oid), walker, by=LibGit2.Consts.SORT_TIME) +end +``` +Here, `map` visits each commit using the `GitRevWalker` and finds its `GitHash`. +""" function Base.map(f::Function, walker::GitRevWalker; oid::GitHash=GitHash(), range::AbstractString="", @@ -79,6 +132,30 @@ function Base.map(f::Function, walker::GitRevWalker; return res end +""" + LibGit2.count(f::Function, walker::GitRevWalker; oid::GitHash=GitHash(), by::Cint=Consts.SORT_NONE, rev::Bool=false) + +Using the [`GitRevWalker`](@ref) `walker` to "walk" over every commit in the repository's history, +find the number of commits which return `true` when `f` is applied to them. The keyword arguments +are: + * `oid`: The [`GitHash`](@ref) of the commit to begin the walk from. The default is to use + [`push_head!`](@ref) and therefore the HEAD commit and all its ancestors. + * `by`: The sorting method. The default is not to sort. Other options are to sort by + topology (`LibGit2.Consts.SORT_TOPOLOGICAL`), to sort forwards in time + (`LibGit2.Consts.SORT_TIME`, most ancient first) or to sort backwards in time + (`LibGit2.Consts.SORT_REVERSE`, most recent first). + * `rev`: Whether to reverse the sorted order (for instance, if topological sorting is used). + +# Examples +```julia +cnt = LibGit2.with(LibGit2.GitRevWalker(repo)) do walker + count((oid, repo)->(oid == commit_oid1), walker, oid=commit_oid1, by=LibGit2.Consts.SORT_TIME) +end +``` +`count` finds the number of commits along the walk with a certain `GitHash` `commit_oid1`, starting +the walk from that commit and moving forwards in time from it. Since the `GitHash` is unique to +a commit, `cnt` will be `1`. +""" function Base.count(f::Function, walker::GitRevWalker; oid::GitHash=GitHash(), by::Cint = Consts.SORT_NONE, diff --git a/doc/src/devdocs/libgit2.md b/doc/src/devdocs/libgit2.md index 91d8fca17e2d3..7f0f8c7e0b9aa 100644 --- a/doc/src/devdocs/libgit2.md +++ b/doc/src/devdocs/libgit2.md @@ -30,6 +30,7 @@ Base.LibGit2.GitRemote Base.LibGit2.GitRemoteAnon Base.LibGit2.GitRepo Base.LibGit2.GitRepoExt +Base.LibGit2.GitRevWalker Base.LibGit2.GitShortHash Base.LibGit2.GitSignature Base.LibGit2.GitStatus @@ -63,6 +64,7 @@ Base.LibGit2.checkused! Base.LibGit2.clone Base.LibGit2.commit Base.LibGit2.committer +Base.LibGit2.count(::Function, ::Base.LibGit2.GitRevWalker; ::Base.LibGit2.GitHash, ::Cint, ::Bool) Base.LibGit2.create_branch Base.LibGit2.credentials_callback Base.LibGit2.credentials_cb @@ -90,6 +92,7 @@ Base.LibGit2.isdiff Base.LibGit2.isdirty Base.LibGit2.isorphan Base.LibGit2.lookup_branch +Base.LibGit2.map(::Function, ::Base.LibGit2.GitRevWalker; ::Base.LibGit2.GitHash, ::Cint, ::Bool) Base.LibGit2.mirror_callback Base.LibGit2.mirror_cb Base.LibGit2.message @@ -100,6 +103,8 @@ Base.LibGit2.path Base.LibGit2.peel Base.LibGit2.posixpath Base.LibGit2.push +Base.LibGit2.push!(::Base.LibGit2.GitRevWalker, ::Base.LibGit2.GitHash) +Base.LibGit2.push_head! Base.LibGit2.push_refspecs Base.LibGit2.read_tree! Base.LibGit2.rebase! From 2301d7010f8780eef1f695a4bd4d099baa77f4fc Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Thu, 24 Aug 2017 14:31:42 -0400 Subject: [PATCH 157/324] restrict `Bool` `*` methods to `AbstractFloat`, avoiding some ambiguities part of #19168 --- base/bool.jl | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/base/bool.jl b/base/bool.jl index 2e78c2f824fcb..be60d2758337e 100644 --- a/base/bool.jl +++ b/base/bool.jl @@ -95,18 +95,17 @@ isone(x::Bool) = x ^(x::Bool, y::Bool) = x | !y ^(x::Integer, y::Bool) = ifelse(y, x, one(x)) +# preserve -0.0 in `false + -0.0` function +(x::Bool, y::T)::promote_type(Bool,T) where T<:AbstractFloat return ifelse(x, oneunit(y) + y, y) end +(y::AbstractFloat, x::Bool) = x + y -function *(x::Bool, y::T)::promote_type(Bool,T) where T<:Number +# make `false` a "strong zero": false*NaN == 0.0 +function *(x::Bool, y::T)::promote_type(Bool,T) where T<:AbstractFloat return ifelse(x, y, copysign(zero(y), y)) end -function *(x::Bool, y::T)::promote_type(Bool,T) where T<:Unsigned - return ifelse(x, y, zero(y)) -end -*(y::Number, x::Bool) = x * y +*(y::AbstractFloat, x::Bool) = x * y div(x::Bool, y::Bool) = y ? x : throw(DivideError()) fld(x::Bool, y::Bool) = div(x,y) From 11aeba3a01566643ff5e6766972858bc2ef8277c Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Tue, 22 Aug 2017 16:04:59 -0400 Subject: [PATCH 158/324] allocate fresh bindings on each iteration of `while` loops also remove the outer scope "wrapper" from `while` loops, similar to #23387 --- NEWS.md | 3 +++ doc/src/manual/variables-and-scoping.md | 7 +++-- src/julia-syntax.scm | 35 +++++++++++-------------- test/core.jl | 11 ++++++++ 4 files changed, 33 insertions(+), 23 deletions(-) diff --git a/NEWS.md b/NEWS.md index 6a99d66cf3df4..b2b977999cea7 100644 --- a/NEWS.md +++ b/NEWS.md @@ -78,6 +78,9 @@ Language changes * 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. + * Variable bindings local to `while` loop bodies are now freshly allocated on each loop iteration, + matching the behavior of `for` loops. + Breaking changes ---------------- diff --git a/doc/src/manual/variables-and-scoping.md b/doc/src/manual/variables-and-scoping.md index 208a3f7d0a251..d2f511a0dd965 100644 --- a/doc/src/manual/variables-and-scoping.md +++ b/doc/src/manual/variables-and-scoping.md @@ -416,10 +416,9 @@ outer local `x`. ### For Loops and Comprehensions -`for` loops and [Comprehensions](@ref) have the following behavior: any new variables introduced -in their body scopes are freshly allocated for each loop iteration. This is in contrast to `while` -loops which reuse the variables for all iterations. Therefore these constructs are similar to -`while` loops with `let` blocks inside: +`for` loops, `while` loops, and [Comprehensions](@ref) have the following behavior: any new variables +introduced in their body scopes are freshly allocated for each loop iteration, as if the loop body +were surrounded by a `let` block: ```jldoctest julia> Fs = Array{Any}(2); diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 53cca2c341ec0..be83d782b61b5 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -1688,16 +1688,15 @@ ,(expand-forms `(,while (call (top !) (call (top done) ,coll ,state)) - (scope-block - (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))))))) + (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)))))) ;; convert an operator parsed as (op a b) to (call op a b) (define (syntactic-op-to-call e) @@ -2243,18 +2242,16 @@ 'while (lambda (e) - `(scope-block - (break-block loop-exit - (_while ,(expand-forms (cadr e)) - (break-block loop-cont - ,(expand-forms (caddr e))))))) + `(break-block loop-exit + (_while ,(expand-forms (cadr e)) + (break-block loop-cont + (scope-block ,(blockify (expand-forms (caddr e)))))))) 'inner-while (lambda (e) - `(scope-block - (_while ,(expand-forms (cadr e)) - (break-block loop-cont - ,(expand-forms (caddr e)))))) + `(_while ,(expand-forms (cadr e)) + (break-block loop-cont + (scope-block ,(blockify (expand-forms (caddr e))))))) 'break (lambda (e) diff --git a/test/core.jl b/test/core.jl index 08c3986562915..d2fde612e3c39 100644 --- a/test/core.jl +++ b/test/core.jl @@ -5133,6 +5133,17 @@ let a = [] @test a == [false, false] end +# while loop scope +let a = [], i = 0 + while i < (local b = 2) + push!(a, @isdefined(j)) + local j = 1 + i += 1 + end + @test a == [false, false] + @test b == 2 +end + mutable struct MyStruct22929 x::MyStruct22929 MyStruct22929() = new() From f82fc0f4fedc914de8eca0576d56f70a0ec4f876 Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Fri, 25 Aug 2017 11:57:15 -0700 Subject: [PATCH 159/324] Doc rebase stuff (#23432) * Doc rebase stuff * fix version stuff picked "newer than or equal to" for consistency with existing docstrings --- base/libgit2/consts.jl | 15 ++++++++++++++- base/libgit2/types.jl | 35 +++++++++++++++++++++++++++++++++-- 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/base/libgit2/consts.jl b/base/libgit2/consts.jl index 0f4ab0989271a..39597dc90bdc9 100644 --- a/base/libgit2/consts.jl +++ b/base/libgit2/consts.jl @@ -176,7 +176,20 @@ module Consts const RESET_MIXED = Cint(2) # SOFT plus reset index to the commit const RESET_HARD = Cint(3) # MIXED plus changes in working tree discarded - #rebase + # rebase + """ Options for what rebase operation is currently being performed on a commit. + * `REBASE_OPERATION_PICK`: cherry-pick the commit in question. + * `REBASE_OPERATION_REWORD`: cherry-pick the commit in question, but rewrite its + message using the prompt. + * `REBASE_OPERATION_EDIT`: cherry-pick the commit in question, but allow the user + to edit the commit's contents and its message. + * `REBASE_OPERATION_SQUASH`: squash the commit in question into the previous commit. + The commit messages of the two commits will be merged. + * `REBASE_OPERATION_FIXUP`: squash the commit in question into the previous commit. + Only the commit message of the previous commit will be used. + * `REBASE_OPERATION_EXEC`: do not cherry-pick a commit. Run a command and continue if + the command exits successfully. + """ @enum(GIT_REBASE_OPERATION, REBASE_OPERATION_PICK = Cint(0), REBASE_OPERATION_REWORD = Cint(1), REBASE_OPERATION_EDIT = Cint(2), diff --git a/base/libgit2/types.jl b/base/libgit2/types.jl index 2dad2ab662482..192c392b197e8 100644 --- a/base/libgit2/types.jl +++ b/base/libgit2/types.jl @@ -275,9 +275,9 @@ The fields represent: * `download_tags`: whether to download tags present at the remote or not. The default is to request the tags for objects which are being downloaded anyway from the server. * `proxy_opts`: options for connecting to the remote through a proxy. See [`ProxyOptions`](@ref). - Only present on libgit2 versions newer than 0.25. + Only present on libgit2 versions newer than or equal to 0.25.0. * `custom_headers`: any extra headers needed for the fetch. Only present on libgit2 versions - newer than 0.24. + newer than or equal to 0.24.0. """ @kwdef struct FetchOptions version::Cuint = 1 @@ -576,6 +576,20 @@ Base.show(io::IO, ie::IndexEntry) = print(io, "IndexEntry($(string(ie.id)))") LibGit2.RebaseOptions Matches the `git_rebase_options` struct. + +The fields represent: + * `version`: version of the struct in use, in case this changes later. For now, always `1`. + * `quiet`: inform other git clients helping with/working on the rebase that the rebase + should be done "quietly". Used for interoperability. The default is `1`. + * `inmemory`: start an in-memory rebase. Callers working on the rebase can go through its + steps and commit any changes, but cannot rewind HEAD or update the repository. The + [`workdir`](@ref) will not be modified. Only present on libgit2 versions newer than or equal to 0.24.0. + * `rewrite_notes_ref`: name of the reference to notes to use to rewrite the commit notes as + the rebase is finished. + * `merge_opts`: merge options controlling how the trees will be merged at each rebase step. + Only present on libgit2 versions newer than or equal to 0.24.0. + * `checkout_opts`: checkout options for writing files when initializing the rebase, stepping + through it, and aborting it. See [`CheckoutOptions`](@ref) for more information. """ @kwdef struct RebaseOptions version::Cuint = 1 @@ -595,6 +609,23 @@ end Describes a single instruction/operation to be performed during the rebase. Matches the [`git_rebase_operation`](https://libgit2.github.com/libgit2/#HEAD/type/git_rebase_operation_t) struct. + +The fields represent: + * `optype`: the type of rebase operation currently being performed. The options are: + - `REBASE_OPERATION_PICK`: cherry-pick the commit in question. + - `REBASE_OPERATION_REWORD`: cherry-pick the commit in question, but rewrite its + message using the prompt. + - `REBASE_OPERATION_EDIT`: cherry-pick the commit in question, but allow the user + to edit the commit's contents and its message. + - `REBASE_OPERATION_SQUASH`: squash the commit in question into the previous commit. + The commit messages of the two commits will be merged. + - `REBASE_OPERATION_FIXUP`: squash the commit in question into the previous commit. + Only the commit message of the previous commit will be used. + - `REBASE_OPERATION_EXEC`: do not cherry-pick a commit. Run a command and continue if + the command exits successfully. + * `id`: the [`GitHash`](@ref) of the commit being worked on during this rebase step. + * `exec`: in case `REBASE_OPERATION_EXEC` is used, the command to run during this step + (for instance, running the test suite after each commit). """ struct RebaseOperation optype::Cint From 8c4930303bd2d2382d08494638b2f658374731af Mon Sep 17 00:00:00 2001 From: Stefan Karpinski Date: Fri, 25 Aug 2017 17:01:56 -0400 Subject: [PATCH 160/324] fix PR #23399 accidentally turning off banner --- base/client.jl | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/base/client.jl b/base/client.jl index 3f2f434bf9d27..7f8c5b11370b2 100644 --- a/base/client.jl +++ b/base/client.jl @@ -252,7 +252,6 @@ function process_options(opts::JLOptions) end repl = true quiet = (opts.quiet != 0) - banner = (opts.banner == 1 || opts.banner != 0 && opts.isinteractive != 0) startup = (opts.startupfile != 2) history_file = (opts.historyfile != 0) color_set = (opts.color != 0) @@ -318,7 +317,7 @@ function process_options(opts::JLOptions) break end repl |= is_interactive - return (quiet,banner,repl,startup,color_set,history_file) + return (quiet,repl,startup,color_set,history_file) end function load_juliarc() @@ -381,7 +380,8 @@ function _start() opts = JLOptions() @eval Main include(x) = $include(Main, x) try - (quiet,banner,repl,startup,color_set,history_file) = process_options(opts) + (quiet,repl,startup,color_set,history_file) = process_options(opts) + banner = opts.banner == 1 local term global active_repl @@ -389,10 +389,13 @@ function _start() if repl if !isa(STDIN,TTY) global is_interactive |= !isa(STDIN, Union{File, IOStream}) + banner |= opts.banner != 0 && is_interactive color_set || (global have_color = false) else - term = Terminals.TTYTerminal(get(ENV, "TERM", @static Sys.iswindows() ? "" : "dumb"), STDIN, STDOUT, STDERR) + term_env = get(ENV, "TERM", @static Sys.iswindows() ? "" : "dumb") + term = Terminals.TTYTerminal(term_env, STDIN, STDOUT, STDERR) global is_interactive = true + banner |= opts.banner != 0 color_set || (global have_color = Terminals.hascolor(term)) banner && REPL.banner(term,term) if term.term_type == "dumb" @@ -407,6 +410,8 @@ function _start() # REPLDisplay pushdisplay(REPL.REPLDisplay(active_repl)) end + else + banner |= opts.banner != 0 && is_interactive end if repl From 182336d7445f495643f86d32ac9ea3170b712ab0 Mon Sep 17 00:00:00 2001 From: kshyatt Date: Fri, 25 Aug 2017 15:19:45 -0700 Subject: [PATCH 161/324] Docs for libgit2 utils --- base/libgit2/utils.jl | 29 +++++++++++++++++++++++++++++ doc/src/devdocs/libgit2.md | 5 +++++ 2 files changed, 34 insertions(+) diff --git a/base/libgit2/utils.jl b/base/libgit2/utils.jl index aecbc88f03139..27e531776a9e2 100644 --- a/base/libgit2/utils.jl +++ b/base/libgit2/utils.jl @@ -21,6 +21,11 @@ const URL_REGEX = r""" $ """x +""" + version() -> VersionNumber + +Return the version of libgit2 in use, as a [`VersionNumber`](@ref man-version-number-literals). +""" function version() major = Ref{Cint}(0) minor = Ref{Cint}(0) @@ -31,10 +36,34 @@ function version() end const VERSION = version() +""" + isset(val::Integer, flag::Integer) + +Test whether the bits of `val` indexed by `flag` are set (`1`) or unset (`0`). +""" isset(val::Integer, flag::Integer) = (val & flag == flag) + +""" + reset(val::Integer, flag::Integer) + +Unset the bits of `val` indexed by `flag`, returning them to `0`. +""" reset(val::Integer, flag::Integer) = (val &= ~flag) + +""" + toggle(val::Integer, flag::Integer) + +Flip the bits of `val` indexed by `flag`, so that if a bit is `0` it +will be `1` after the toggle, and vice-versa. +""" toggle(val::Integer, flag::Integer) = (val |= flag) +""" + features() + +Return a list of git features the current version of libgit2 supports, such as +threading or using HTTPS or SSH. +""" function features() feat = ccall((:git_libgit2_features, :libgit2), Cint, ()) res = Consts.GIT_FEATURE[] diff --git a/doc/src/devdocs/libgit2.md b/doc/src/devdocs/libgit2.md index d49f9a7294157..3e4b4f85888f0 100644 --- a/doc/src/devdocs/libgit2.md +++ b/doc/src/devdocs/libgit2.md @@ -79,6 +79,7 @@ Base.LibGit2.merge_base Base.LibGit2.merge!(::Base.LibGit2.GitRepo; ::Any...) Base.LibGit2.ffmerge! Base.LibGit2.fullname +Base.LibGit2.features Base.LibGit2.get_creds! Base.LibGit2.gitdir Base.LibGit2.head @@ -92,6 +93,7 @@ Base.LibGit2.iscommit Base.LibGit2.isdiff Base.LibGit2.isdirty Base.LibGit2.isorphan +Base.LibGit2.isset Base.LibGit2.lookup_branch Base.LibGit2.map(::Function, ::Base.LibGit2.GitRevWalker; ::Base.LibGit2.GitHash, ::Cint, ::Bool) Base.LibGit2.mirror_callback @@ -113,6 +115,7 @@ Base.LibGit2.ref_list Base.LibGit2.reftype Base.LibGit2.remotes Base.LibGit2.remove! +Base.LibGit2.reset Base.LibGit2.reset! Base.LibGit2.restore Base.LibGit2.revcount @@ -125,11 +128,13 @@ Base.LibGit2.tag_create Base.LibGit2.tag_delete Base.LibGit2.tag_list Base.LibGit2.target +Base.LibGit2.toggle Base.LibGit2.transact Base.LibGit2.treewalk Base.LibGit2.upstream Base.LibGit2.update! Base.LibGit2.url +Base.LibGit2.version Base.LibGit2.with Base.LibGit2.workdir ``` From 62d8671dce857a281060fd74e8f85df6110d8b07 Mon Sep 17 00:00:00 2001 From: kshyatt Date: Fri, 25 Aug 2017 15:44:47 -0700 Subject: [PATCH 162/324] Docs for a bunch of git blame stuff --- base/libgit2/blame.jl | 16 ++++++++++++++++ base/libgit2/consts.jl | 8 ++++++++ base/libgit2/types.jl | 14 ++++++++++++++ doc/src/devdocs/libgit2.md | 3 +++ 4 files changed, 41 insertions(+) diff --git a/base/libgit2/blame.jl b/base/libgit2/blame.jl index a0dee7bb25f02..ee239a0ce0ae0 100644 --- a/base/libgit2/blame.jl +++ b/base/libgit2/blame.jl @@ -1,5 +1,13 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license +""" + GitBlame(repo::GitRepo, path::AbstractString; options::BlameOptions=BlameOptions()) + +Construct a `GitBlame` object for the file at `path`, using change information gleaned +from the history of `repo`. The `GitBlame` object records who changed which chunks of +the file when, and how. `options` controls how to separate the contents of the file and +which commits to probe - see [`BlameOptions`](@ref) for more information. +""" function GitBlame(repo::GitRepo, path::AbstractString; options::BlameOptions=BlameOptions()) blame_ptr_ptr = Ref{Ptr{Void}}(C_NULL) @check ccall((:git_blame_file, :libgit2), Cint, @@ -8,6 +16,14 @@ function GitBlame(repo::GitRepo, path::AbstractString; options::BlameOptions=Bla return GitBlame(repo, blame_ptr_ptr[]) end +""" + counthunks(blame::GitBlame) + +Return the number of distinct "hunks" with a file. A hunk may contain multiple lines. +A hunk is usually a piece of a file that was added/changed/removed together, for example, +a function added to a source file or an inner loop that was optimized out of +that function later. +""" function counthunks(blame::GitBlame) return ccall((:git_blame_get_hunk_count, :libgit2), Int32, (Ptr{Void},), blame.ptr) end diff --git a/base/libgit2/consts.jl b/base/libgit2/consts.jl index 39597dc90bdc9..eb7e31bf71de4 100644 --- a/base/libgit2/consts.jl +++ b/base/libgit2/consts.jl @@ -27,6 +27,14 @@ module Consts const REF_SYMBOLIC = Cint(2) const REF_LISTALL = REF_OID | REF_SYMBOLIC + # blame + const BLAME_NORMAL = Cuint(0) + const BLAME_TRACK_COPIES_SAME_FILE = Cuint(1 << 0) + const BLAME_TRACK_COPIES_SAME_COMMIT_MOVES = Cuint(1 << 1) + const BLAME_TRACK_COPIES_SAME_COMMIT_COPIES = Cuint(1 << 2) + const BLAME_TRACK_COPIES_ANY_COMMIT_COPIES = Cuint(1 << 3) + const BLAME_FIRST_PARENT = Cuint(1 << 4) + # checkout const CHECKOUT_NONE = Cuint(0) const CHECKOUT_SAFE = Cuint(1 << 0) diff --git a/base/libgit2/types.jl b/base/libgit2/types.jl index 1b29c3417d5e3..3f5387dbbd5ee 100644 --- a/base/libgit2/types.jl +++ b/base/libgit2/types.jl @@ -483,6 +483,20 @@ end LibGit2.BlameOptions Matches the [`git_blame_options`](https://libgit2.github.com/libgit2/#HEAD/type/git_blame_options) struct. + +The fields represent: + * `version`: version of the struct in use, in case this changes later. For now, always `1`. + * `flags`: one of `Consts.BLAME_NORMAL` or `Consts.BLAME_FIRST_PARENT` (the other blame flags + are not yet implemented by libgit2). + * `min_match_characters`: the minimum number of *alphanumeric* characters which much change + in a commit in order for the change to be associated with that commit. The default is 20. + Only takes effect if one of the `Consts.BLAME_*_COPIES` flags are used, which libgit2 does + not implement yet. + * `newest_commit`: the [`GitHash`](@ref) of the newest commit from which to look at changes. + * `oldest_commit`: the [`GitHash`](@ref) of the oldest commit from which to look at changes. + * `min_line`: the first line of the file from which to starting blaming. The default is `1`. + * `max_line`: the last line of the file to which to blame. The default is `0`, meaning the + last line of the file. """ @kwdef struct BlameOptions version::Cuint = 1 diff --git a/doc/src/devdocs/libgit2.md b/doc/src/devdocs/libgit2.md index d49f9a7294157..e3e831fc15fc9 100644 --- a/doc/src/devdocs/libgit2.md +++ b/doc/src/devdocs/libgit2.md @@ -22,6 +22,7 @@ Base.LibGit2.DiffFile Base.LibGit2.DiffOptionsStruct Base.LibGit2.FetchHead Base.LibGit2.FetchOptions +Base.LibGit2.GitBlame Base.LibGit2.GitBlob Base.LibGit2.GitCommit Base.LibGit2.GitHash @@ -38,6 +39,7 @@ Base.LibGit2.GitTag Base.LibGit2.GitTree Base.LibGit2.IndexEntry Base.LibGit2.IndexTime +Base.LibGit2.BlameOptions Base.LibGit2.MergeOptions Base.LibGit2.ProxyOptions Base.LibGit2.PushOptions @@ -65,6 +67,7 @@ Base.LibGit2.clone Base.LibGit2.commit Base.LibGit2.committer Base.LibGit2.count(::Function, ::Base.LibGit2.GitRevWalker; ::Base.LibGit2.GitHash, ::Cint, ::Bool) +Base.LibGit2.counthunks Base.LibGit2.create_branch Base.LibGit2.credentials_callback Base.LibGit2.credentials_cb From 7754b61035202a05f925ca29368eef88f2d8ce39 Mon Sep 17 00:00:00 2001 From: kshyatt Date: Fri, 25 Aug 2017 17:42:51 -0700 Subject: [PATCH 163/324] Doc status opts and fix a typo --- base/libgit2/types.jl | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/base/libgit2/types.jl b/base/libgit2/types.jl index 1b29c3417d5e3..73d501a4ed33a 100644 --- a/base/libgit2/types.jl +++ b/base/libgit2/types.jl @@ -128,7 +128,7 @@ The fields represent: * `disable_filters`: if nonzero, do not apply filters like CLRF (to convert file newlines between UNIX and DOS). * `dir_mode`: read/write/access mode for any directories involved in the checkout. Default is `0755`. * `file_mode`: read/write/access mode for any files involved in the checkout. - Default is `0755` or `0644`, depeding on the blob. + Default is `0755` or `0644`, depending on the blob. * `file_open_flags`: bitflags used to open any files during the checkout. * `notify_flags`: Flags for what sort of conflicts the user should be notified about. * `notify_cb`: An optional callback function to notify the user if a checkout conflict occurs. @@ -651,6 +651,14 @@ end Options to control how `git_status_foreach_ext()` will issue callbacks. Matches the [`git_status_opt_t`](https://libgit2.github.com/libgit2/#HEAD/type/git_status_opt_t) struct. + +The fields represent: + * `version`: version of the struct in use, in case this changes later. For now, always `1`. + * `show`: a flag for which files to examine and in which order. + The default is `Consts.STATUS_SHOW_INDEX_AND_WORKDIR`. + * `flags`: flags for controlling any callbacks used in a status call. + * `pathspec`: an array of paths to use for path-matching. The behavior of the path-matching + will vary depending on the values of `show` and `flags`. """ @kwdef struct StatusOptions version::Cuint = 1 From 6347dbd06283c660021e226c552d9f2deefe35f2 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Sat, 26 Aug 2017 05:16:01 -0500 Subject: [PATCH 164/324] Describe how to hack Base more easily with Revise.jl [ci skip] --- CONTRIBUTING.md | 51 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ff4654ebf21a1..340a5b03c765f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -196,7 +196,7 @@ The Julia community uses [GitHub issues](https://github.com/JuliaLang/julia/issu Note: These instructions are for adding to or improving functionality in the base library. Before getting started, it can be helpful to discuss the proposed changes or additions on the [Julia Discourse forum](https://discourse.julialang.org) or in a GitHub issue---it's possible your proposed change belongs in a package rather than the core language. Also, keep in mind that changing stuff in the base can potentially break a lot of things. Finally, because of the time required to build Julia, note that it's usually faster to develop your code in stand-alone files, get it working, and then migrate it into the base libraries. -Add new code to Julia's base libraries as follows: +Add new code to Julia's base libraries as follows (this is the "basic" approach; see a more efficient approach in the next section): 1. Edit the appropriate file in the `base/` directory, or add new files if necessary. Create tests for your functionality and add them to files in the `test/` directory. If you're editing C or Scheme code, most likely it lives in `src/` or one of its subdirectories, although some aspects of Julia's REPL initialization live in `ui/`. @@ -218,6 +218,55 @@ or with the `runtests.jl` script, e.g. to run `test/bitarray.jl` and `test/math. Make sure that [Travis](http://www.travis-ci.org) greenlights the pull request with a [`Good to merge` message](http://blog.travis-ci.com/2012-09-04-pull-requests-just-got-even-more-awesome). +#### Modifying base more efficiently with Revise.jl + +[Revise](https://github.com/timholy/Revise.jl) is a package that +tracks changes in source files and automatically updates function +definitions in your running Julia session. Using it, you can make +extensive changes to Base without needing to rebuild in order to test +your changes. + +Here is the standard procedure: + +1. If you are planning changes to any types or macros, make those + changes, commit them, and build julia using `make`. (This is + necessary because `Revise` cannot handle changes to type + definitions or macros.) By making a git commit, you "shield" these + changes from the `git stash` procedure described below. Unless it's + required to get Julia to build, you do not have to add any + functionality based on the new types, just the type definitions + themselves. + +2. Start a Julia REPL session. Then issue the following commands: + +```julia +using Revise # if you aren't launching it in your .juliarc.jl +Revise.track(Base) +``` + +3. Edit files in `base/`, save your edits, and test the + functionality. Once you are satisfied that things work as desired, + make another commit and rebuild julia. + +Should you for some reason need to quit and restart your REPL session +before finishing your changes, the procedure above will fail because +`Revise.track` +[cannot detect changes in source files that occurred after Julia was built](https://github.com/JuliaLang/julia/issues/23448)---it +will only detect changes to source files that occur after tracking is +initiated. Consequently, any changes made prior to +`Revise.track(Base)` will not be incorporated into your new REPL +session. You can work around this by temporarily reverting all source +files to their original state. From somewhere in the `julia` +directory, start your REPL session and do the following: + +```julia +shell> git stash # ensure that the code in `base/` matches its state when you built julia + +julia> Revise.track(Base) # Revise's source code cache is synchronized with what's running + +shell> git stash pop # restore any in-progress changes (will now be tracked) +``` + ### Code Formatting Guidelines #### General Formatting Guidelines for Julia code contributions From 0d95a1fdab53be3eca0a094eb91b5056d8129a2d Mon Sep 17 00:00:00 2001 From: Martijn Visser Date: Sat, 26 Aug 2017 13:19:39 +0200 Subject: [PATCH 165/324] fix printing expressions like Ref{<:Real} fixes #23457 Previously they were not explicitly handled by show_unquoted, ending up being printed as Expr. This was not parsable back to their original meaning. --- base/show.jl | 5 +++-- test/show.jl | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/base/show.jl b/base/show.jl index db41a94371ab3..ed2f02245d5eb 100644 --- a/base/show.jl +++ b/base/show.jl @@ -535,6 +535,7 @@ show_unquoted(io::IO, ex, ::Int,::Int) = show(io, ex) const indent_width = 4 const quoted_syms = Set{Symbol}([:(:),:(::),:(:=),:(=),:(==),:(!=),:(===),:(!==),:(=>),:(>=),:(<=)]) +const uni_syms = Set{Symbol}([:(::), :(<:), :(>:)]) const uni_ops = Set{Symbol}([:(+), :(-), :(!), :(¬), :(~), :(<:), :(>:), :(√), :(∛), :(∜)]) const expr_infix_wide = Set{Symbol}([ :(=), :(+=), :(-=), :(*=), :(/=), :(\=), :(^=), :(&=), :(|=), :(÷=), :(%=), :(>>>=), :(>>=), :(<<=), @@ -994,8 +995,8 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int) print(io, head) # type annotation (i.e. "::Int") - elseif head === Symbol("::") && nargs == 1 - print(io, "::") + elseif head in uni_syms && nargs == 1 + print(io, head) show_unquoted(io, args[1], indent) # var-arg declaration or expansion diff --git a/test/show.jl b/test/show.jl index 12786b6ecab3e..a9b8d92d74d4a 100644 --- a/test/show.jl +++ b/test/show.jl @@ -415,6 +415,8 @@ end @test_repr "a::b where T" @test_repr "X where (T=1)" @test_repr "X where T = 1" +@test_repr "Array{<:Real}" +@test_repr "Array{>:Real}" let oldout = STDOUT, olderr = STDERR local rdout, wrout, rderr, wrerr, out, err, rd, wr From 119cf4c7c90410b8ed674128679166693498fc54 Mon Sep 17 00:00:00 2001 From: Mus M Date: Sat, 26 Aug 2017 10:33:55 -0400 Subject: [PATCH 166/324] Remove `&x` convention in ccall in gmp (#23289) --- base/gmp.jl | 67 +++++++++++++++++++++++++---------------------------- 1 file changed, 32 insertions(+), 35 deletions(-) diff --git a/base/gmp.jl b/base/gmp.jl index b352f2f4683f1..17a7f6602b27f 100644 --- a/base/gmp.jl +++ b/base/gmp.jl @@ -84,9 +84,9 @@ function __init__() try if version().major != VERSION.major || bits_per_limb() != BITS_PER_LIMB msg = bits_per_limb() != BITS_PER_LIMB ? error : warn - msg(string("The dynamically loaded GMP library (version $(version()) with __gmp_bits_per_limb == $(bits_per_limb()))\n", - "does not correspond to the compile time version (version $VERSION with __gmp_bits_per_limb == $BITS_PER_LIMB).\n", - "Please rebuild Julia.")) + msg("The dynamically loaded GMP library (v\"$(version())\" with __gmp_bits_per_limb == $(bits_per_limb()))\n", + "does not correspond to the compile time version (v\"$VERSION\" with __gmp_bits_per_limb == $BITS_PER_LIMB).\n", + "Please rebuild Julia.") end ccall((:__gmp_set_memory_functions, :libgmp), Void, @@ -98,8 +98,7 @@ function __init__() ZERO.alloc, ZERO.size, ZERO.d = 0, 0, C_NULL ONE.alloc, ONE.size, ONE.d = 1, 1, pointer(_ONE) catch ex - Base.showerror_nostdio(ex, - "WARNING: Error during initialization of module GMP") + Base.showerror_nostdio(ex, "WARNING: Error during initialization of module GMP") end end @@ -113,57 +112,57 @@ module MPZ # and `add!(x, a) = add!(x, x, a)`. using Base.GMP: BigInt, Limb -const mpz_t = Ptr{BigInt} +const mpz_t = Ref{BigInt} const bitcnt_t = Culong gmpz(op::Symbol) = (Symbol(:__gmpz_, op), :libgmp) -init!(x::BigInt) = (ccall((:__gmpz_init, :libgmp), Void, (mpz_t,), &x); x) -init2!(x::BigInt, a) = (ccall((:__gmpz_init2, :libgmp), Void, (mpz_t, bitcnt_t), &x, a); x) +init!(x::BigInt) = (ccall((:__gmpz_init, :libgmp), Void, (mpz_t,), x); x) +init2!(x::BigInt, a) = (ccall((:__gmpz_init2, :libgmp), Void, (mpz_t, bitcnt_t), x, a); x) -realloc2!(x, a) = (ccall((:__gmpz_realloc2, :libgmp), Void, (mpz_t, bitcnt_t), &x, a); x) +realloc2!(x, a) = (ccall((:__gmpz_realloc2, :libgmp), Void, (mpz_t, bitcnt_t), x, a); x) realloc2(a) = realloc2!(BigInt(), a) -sizeinbase(a::BigInt, b) = Int(ccall((:__gmpz_sizeinbase, :libgmp), Csize_t, (mpz_t, Cint), &a, b)) +sizeinbase(a::BigInt, b) = Int(ccall((:__gmpz_sizeinbase, :libgmp), Csize_t, (mpz_t, Cint), a, b)) for op in (:add, :sub, :mul, :fdiv_q, :tdiv_q, :fdiv_r, :tdiv_r, :gcd, :lcm, :and, :ior, :xor) op! = Symbol(op, :!) @eval begin - $op!(x::BigInt, a::BigInt, b::BigInt) = (ccall($(gmpz(op)), Void, (mpz_t, mpz_t, mpz_t), &x, &a, &b); x) + $op!(x::BigInt, a::BigInt, b::BigInt) = (ccall($(gmpz(op)), Void, (mpz_t, mpz_t, mpz_t), x, a, b); x) $op(a::BigInt, b::BigInt) = $op!(BigInt(), a, b) $op!(x::BigInt, b::BigInt) = $op!(x, x, b) end end invert!(x::BigInt, a::BigInt, b::BigInt) = - ccall((:__gmpz_invert, :libgmp), Cint, (mpz_t, mpz_t, mpz_t), &x, &a, &b) + ccall((:__gmpz_invert, :libgmp), Cint, (mpz_t, mpz_t, mpz_t), x, a, b) invert(a::BigInt, b::BigInt) = invert!(BigInt(), a, b) invert!(x::BigInt, b::BigInt) = invert!(x, x, b) for op in (:add_ui, :sub_ui, :mul_ui, :mul_2exp, :fdiv_q_2exp, :pow_ui, :bin_ui) op! = Symbol(op, :!) @eval begin - $op!(x::BigInt, a::BigInt, b) = (ccall($(gmpz(op)), Void, (mpz_t, mpz_t, Culong), &x, &a, b); x) + $op!(x::BigInt, a::BigInt, b) = (ccall($(gmpz(op)), Void, (mpz_t, mpz_t, Culong), x, a, b); x) $op(a::BigInt, b) = $op!(BigInt(), a, b) $op!(x::BigInt, b) = $op!(x, x, b) end end -ui_sub!(x::BigInt, a, b::BigInt) = (ccall((:__gmpz_ui_sub, :libgmp), Void, (mpz_t, Culong, mpz_t), &x, a, &b); x) +ui_sub!(x::BigInt, a, b::BigInt) = (ccall((:__gmpz_ui_sub, :libgmp), Void, (mpz_t, Culong, mpz_t), x, a, b); x) ui_sub(a, b::BigInt) = ui_sub!(BigInt(), a, b) for op in (:scan1, :scan0) - @eval $op(a::BigInt, b) = Int(ccall($(gmpz(op)), Culong, (mpz_t, Culong), &a, b)) + @eval $op(a::BigInt, b) = Int(ccall($(gmpz(op)), Culong, (mpz_t, Culong), a, b)) end -mul_si!(x::BigInt, a::BigInt, b) = (ccall((:__gmpz_mul_si, :libgmp), Void, (mpz_t, mpz_t, Clong), &x, &a, b); x) +mul_si!(x::BigInt, a::BigInt, b) = (ccall((:__gmpz_mul_si, :libgmp), Void, (mpz_t, mpz_t, Clong), x, a, b); x) mul_si(a::BigInt, b) = mul_si!(BigInt(), a, b) mul_si!(x::BigInt, b) = mul_si!(x, x, b) for op in (:neg, :com, :sqrt, :set) op! = Symbol(op, :!) @eval begin - $op!(x::BigInt, a::BigInt) = (ccall($(gmpz(op)), Void, (mpz_t, mpz_t), &x, &a); x) + $op!(x::BigInt, a::BigInt) = (ccall($(gmpz(op)), Void, (mpz_t, mpz_t), x, a); x) $op(a::BigInt) = $op!(BigInt(), a) end op == :set && continue # MPZ.set!(x) would make no sense @@ -173,51 +172,49 @@ end for (op, T) in ((:fac_ui, Culong), (:set_ui, Culong), (:set_si, Clong), (:set_d, Cdouble)) op! = Symbol(op, :!) @eval begin - $op!(x::BigInt, a) = (ccall($(gmpz(op)), Void, (mpz_t, $T), &x, a); x) + $op!(x::BigInt, a) = (ccall($(gmpz(op)), Void, (mpz_t, $T), x, a); x) $op(a) = $op!(BigInt(), a) end end -popcount(a::BigInt) = Int(ccall((:__gmpz_popcount, :libgmp), Culong, (mpz_t,), &a)) +popcount(a::BigInt) = Int(ccall((:__gmpz_popcount, :libgmp), Culong, (mpz_t,), a)) mpn_popcount(d::Ptr{Limb}, s::Integer) = Int(ccall((:__gmpn_popcount, :libgmp), Culong, (Ptr{Limb}, Csize_t), d, s)) mpn_popcount(a::BigInt) = mpn_popcount(a.d, abs(a.size)) function tdiv_qr!(x::BigInt, y::BigInt, a::BigInt, b::BigInt) - ccall((:__gmpz_tdiv_qr, :libgmp), Void, (mpz_t, mpz_t, mpz_t, mpz_t), &x, &y, &a, &b) + ccall((:__gmpz_tdiv_qr, :libgmp), Void, (mpz_t, mpz_t, mpz_t, mpz_t), x, y, a, b) x, y end tdiv_qr(a::BigInt, b::BigInt) = tdiv_qr!(BigInt(), BigInt(), a, b) powm!(x::BigInt, a::BigInt, b::BigInt, c::BigInt) = - (ccall((:__gmpz_powm, :libgmp), Void, (mpz_t, mpz_t, mpz_t, mpz_t), &x, &a, &b, &c); x) + (ccall((:__gmpz_powm, :libgmp), Void, (mpz_t, mpz_t, mpz_t, mpz_t), x, a, b, c); x) powm(a::BigInt, b::BigInt, c::BigInt) = powm!(BigInt(), a, b, c) powm!(x::BigInt, b::BigInt, c::BigInt) = powm!(x, x, b, c) function gcdext!(x::BigInt, y::BigInt, z::BigInt, a::BigInt, b::BigInt) - ccall((:__gmpz_gcdext, :libgmp), Void, (mpz_t, mpz_t, mpz_t, mpz_t, mpz_t), - &x, &y, &z, &a, &b) + ccall((:__gmpz_gcdext, :libgmp), Void, (mpz_t, mpz_t, mpz_t, mpz_t, mpz_t), x, y, z, a, b) x, y, z end gcdext(a::BigInt, b::BigInt) = gcdext!(BigInt(), BigInt(), BigInt(), a, b) -cmp(a::BigInt, b::BigInt) = Int(ccall((:__gmpz_cmp, :libgmp), Cint, (mpz_t, mpz_t), &a, &b)) -cmp_si(a::BigInt, b) = Int(ccall((:__gmpz_cmp_si, :libgmp), Cint, (mpz_t, Clong), &a, b)) -cmp_ui(a::BigInt, b) = Int(ccall((:__gmpz_cmp_ui, :libgmp), Cint, (mpz_t, Culong), &a, b)) -cmp_d(a::BigInt, b) = Int(ccall((:__gmpz_cmp_d, :libgmp), Cint, (mpz_t, Cdouble), &a, b)) +cmp(a::BigInt, b::BigInt) = Int(ccall((:__gmpz_cmp, :libgmp), Cint, (mpz_t, mpz_t), a, b)) +cmp_si(a::BigInt, b) = Int(ccall((:__gmpz_cmp_si, :libgmp), Cint, (mpz_t, Clong), a, b)) +cmp_ui(a::BigInt, b) = Int(ccall((:__gmpz_cmp_ui, :libgmp), Cint, (mpz_t, Culong), a, b)) +cmp_d(a::BigInt, b) = Int(ccall((:__gmpz_cmp_d, :libgmp), Cint, (mpz_t, Cdouble), a, b)) mpn_cmp(a::Ptr{Limb}, b::Ptr{Limb}, c) = ccall((:__gmpn_cmp, :libgmp), Cint, (Ptr{Limb}, Ptr{Limb}, Clong), a, b, c) mpn_cmp(a::BigInt, b::BigInt, c) = mpn_cmp(a.d, b.d, c) -get_str!(x, a, b::BigInt) = (ccall((:__gmpz_get_str,:libgmp), Ptr{Cchar}, (Ptr{Cchar}, Cint, mpz_t), x, a, &b); x) -set_str!(x::BigInt, a, b) = Int(ccall((:__gmpz_set_str, :libgmp), Cint, (mpz_t, Ptr{UInt8}, Cint), &x, a, b)) -get_d(a::BigInt) = ccall((:__gmpz_get_d, :libgmp), Cdouble, (mpz_t,), &a) +get_str!(x, a, b::BigInt) = (ccall((:__gmpz_get_str,:libgmp), Ptr{Cchar}, (Ptr{Cchar}, Cint, mpz_t), x, a, b); x) +set_str!(x::BigInt, a, b) = Int(ccall((:__gmpz_set_str, :libgmp), Cint, (mpz_t, Ptr{UInt8}, Cint), x, a, b)) +get_d(a::BigInt) = ccall((:__gmpz_get_d, :libgmp), Cdouble, (mpz_t,), a) -limbs_write!(x::BigInt, a) = ccall((:__gmpz_limbs_write, :libgmp), Ptr{Limb}, (mpz_t, Clong), &x, a) -limbs_finish!(x::BigInt, a) = ccall((:__gmpz_limbs_finish, :libgmp), Void, (mpz_t, Clong), &x, a) -import!(x::BigInt, a, b, c, d, e, f) = - ccall((:__gmpz_import, :libgmp), Void, (mpz_t, Csize_t, Cint, Csize_t, Cint, Csize_t, Ptr{Void}), - &x, a, b, c, d, e, f) +limbs_write!(x::BigInt, a) = ccall((:__gmpz_limbs_write, :libgmp), Ptr{Limb}, (mpz_t, Clong), x, a) +limbs_finish!(x::BigInt, a) = ccall((:__gmpz_limbs_finish, :libgmp), Void, (mpz_t, Clong), x, a) +import!(x::BigInt, a, b, c, d, e, f) = ccall((:__gmpz_import, :libgmp), Void, + (mpz_t, Csize_t, Cint, Csize_t, Cint, Csize_t, Ptr{Void}), x, a, b, c, d, e, f) end # module MPZ From fc6b0ef0df7efe829c4b64765ce79b6efa69dae1 Mon Sep 17 00:00:00 2001 From: Mus M Date: Sat, 26 Aug 2017 11:56:29 -0400 Subject: [PATCH 167/324] Remove `&x` convention in ccall in mpfr (#23288) --- base/mpfr.jl | 307 ++++++++++++++++++++++++--------------------------- 1 file changed, 145 insertions(+), 162 deletions(-) diff --git a/base/mpfr.jl b/base/mpfr.jl index ceea0f6e92433..b2321675232dd 100644 --- a/base/mpfr.jl +++ b/base/mpfr.jl @@ -7,7 +7,7 @@ export setprecision import - Base: (*), +, -, /, <, <=, ==, >, >=, ^, ceil, cmp, convert, copysign, div, + Base: *, +, -, /, <, <=, ==, >, >=, ^, ceil, cmp, convert, copysign, div, exp, exp2, exponent, factorial, floor, fma, hypot, isinteger, isfinite, isinf, isnan, ldexp, log, log2, log10, max, min, mod, modf, nextfloat, prevfloat, promote_rule, rem, rem2pi, round, show, float, @@ -39,8 +39,7 @@ function __init__() set_emin!(get_emin_min()) set_emax!(get_emax_max()) catch ex - Base.showerror_nostdio(ex, - "WARNING: Error during initialization of module MPFR") + Base.showerror_nostdio(ex, "WARNING: Error during initialization of module MPFR") end end @@ -63,7 +62,7 @@ mutable struct BigFloat <: AbstractFloat function BigFloat() prec = precision(BigFloat) z = new(zero(Clong), zero(Cint), zero(Clong), C_NULL) - ccall((:mpfr_init2,:libmpfr), Void, (Ptr{BigFloat}, Clong), &z, prec) + ccall((:mpfr_init2,:libmpfr), Void, (Ref{BigFloat}, Clong), z, prec) finalizer(z, cglobal((:mpfr_clear, :libmpfr))) return z end @@ -105,7 +104,7 @@ for (fJ, fC) in ((:si,:Clong), (:ui,:Culong), (:d,:Float64)) @eval begin function convert(::Type{BigFloat}, x::($fC)) z = BigFloat() - ccall(($(string(:mpfr_set_,fJ)), :libmpfr), Int32, (Ptr{BigFloat}, ($fC), Int32), &z, x, ROUNDING_MODE[]) + ccall(($(string(:mpfr_set_,fJ)), :libmpfr), Int32, (Ref{BigFloat}, $fC, Int32), z, x, ROUNDING_MODE[]) return z end end @@ -113,21 +112,21 @@ end function convert(::Type{BigFloat}, x::BigInt) z = BigFloat() - ccall((:mpfr_set_z, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigInt}, Int32), &z, &x, ROUNDING_MODE[]) + ccall((:mpfr_set_z, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigInt}, Int32), z, x, ROUNDING_MODE[]) return z end convert(::Type{BigFloat}, x::Integer) = BigFloat(BigInt(x)) -convert(::Type{BigFloat}, x::Union{Bool,Int8,Int16,Int32}) = BigFloat(convert(Clong,x)) -convert(::Type{BigFloat}, x::Union{UInt8,UInt16,UInt32}) = BigFloat(convert(Culong,x)) +convert(::Type{BigFloat}, x::Union{Bool,Int8,Int16,Int32}) = BigFloat(convert(Clong, x)) +convert(::Type{BigFloat}, x::Union{UInt8,UInt16,UInt32}) = BigFloat(convert(Culong, x)) convert(::Type{BigFloat}, x::Union{Float16,Float32}) = BigFloat(Float64(x)) convert(::Type{BigFloat}, x::Rational) = BigFloat(numerator(x)) / BigFloat(denominator(x)) function tryparse(::Type{BigFloat}, s::AbstractString, base::Int=0) z = BigFloat() - err = ccall((:mpfr_set_str, :libmpfr), Int32, (Ptr{BigFloat}, Cstring, Int32, Int32), &z, s, base, ROUNDING_MODE[]) + err = ccall((:mpfr_set_str, :libmpfr), Int32, (Ref{BigFloat}, Cstring, Int32, Int32), z, s, base, ROUNDING_MODE[]) err == 0 ? Nullable(z) : Nullable{BigFloat}() end @@ -180,12 +179,10 @@ BigFloat(x::String) = parse(BigFloat, x) ## BigFloat -> Integer function unsafe_cast(::Type{Int64}, x::BigFloat, ri::Cint) - ccall((:__gmpfr_mpfr_get_sj,:libmpfr), Cintmax_t, - (Ptr{BigFloat}, Cint), &x, ri) + ccall((:__gmpfr_mpfr_get_sj,:libmpfr), Cintmax_t, (Ref{BigFloat}, Cint), x, ri) end function unsafe_cast(::Type{UInt64}, x::BigFloat, ri::Cint) - ccall((:__gmpfr_mpfr_get_uj,:libmpfr), Cuintmax_t, - (Ptr{BigFloat}, Cint), &x, ri) + ccall((:__gmpfr_mpfr_get_uj,:libmpfr), Cuintmax_t, (Ref{BigFloat}, Cint), x, ri) end function unsafe_cast(::Type{T}, x::BigFloat, ri::Cint) where T<:Signed @@ -198,32 +195,31 @@ end function unsafe_cast(::Type{BigInt}, x::BigFloat, ri::Cint) # actually safe, just keep naming consistent z = BigInt() - ccall((:mpfr_get_z, :libmpfr), Int32, (Ptr{BigInt}, Ptr{BigFloat}, Int32), - &z, &x, ri) - z + ccall((:mpfr_get_z, :libmpfr), Int32, (Ref{BigInt}, Ref{BigFloat}, Int32), z, x, ri) + return z end -unsafe_cast(::Type{Int128}, x::BigFloat, ri::Cint) = Int128(unsafe_cast(BigInt,x,ri)) -unsafe_cast(::Type{UInt128}, x::BigFloat, ri::Cint) = UInt128(unsafe_cast(BigInt,x,ri)) -unsafe_cast(::Type{T}, x::BigFloat, r::RoundingMode) where {T<:Integer} = unsafe_cast(T,x,to_mpfr(r)) +unsafe_cast(::Type{Int128}, x::BigFloat, ri::Cint) = Int128(unsafe_cast(BigInt, x, ri)) +unsafe_cast(::Type{UInt128}, x::BigFloat, ri::Cint) = UInt128(unsafe_cast(BigInt, x, ri)) +unsafe_cast(::Type{T}, x::BigFloat, r::RoundingMode) where {T<:Integer} = unsafe_cast(T, x, to_mpfr(r)) -unsafe_trunc(::Type{T}, x::BigFloat) where {T<:Integer} = unsafe_cast(T,x,RoundToZero) +unsafe_trunc(::Type{T}, x::BigFloat) where {T<:Integer} = unsafe_cast(T, x, RoundToZero) function trunc(::Type{T}, x::BigFloat) where T<:Union{Signed,Unsigned} (typemin(T) <= x <= typemax(T)) || throw(InexactError(:trunc, T, x)) - unsafe_cast(T,x,RoundToZero) + unsafe_cast(T, x, RoundToZero) end function floor(::Type{T}, x::BigFloat) where T<:Union{Signed,Unsigned} (typemin(T) <= x <= typemax(T)) || throw(InexactError(:floor, T, x)) - unsafe_cast(T,x,RoundDown) + unsafe_cast(T, x, RoundDown) end function ceil(::Type{T}, x::BigFloat) where T<:Union{Signed,Unsigned} (typemin(T) <= x <= typemax(T)) || throw(InexactError(:ceil, T, x)) - unsafe_cast(T,x,RoundUp) + unsafe_cast(T, x, RoundUp) end function round(::Type{T}, x::BigFloat) where T<:Union{Signed,Unsigned} (typemin(T) <= x <= typemax(T)) || throw(InexactError(:round, T, x)) - unsafe_cast(T,x,ROUNDING_MODE[]) + unsafe_cast(T, x, ROUNDING_MODE[]) end trunc(::Type{BigInt}, x::BigFloat) = unsafe_cast(BigInt, x, RoundToZero) @@ -237,8 +233,11 @@ floor(::Type{Integer}, x::BigFloat) = floor(BigInt, x) ceil(::Type{Integer}, x::BigFloat) = ceil(BigInt, x) round(::Type{Integer}, x::BigFloat) = round(BigInt, x) -convert(::Type{Bool}, x::BigFloat) = iszero(x) ? false : isone(x) ? true : +function convert(::Type{Bool}, x::BigFloat) + iszero(x) && return false + isone(x) && return true throw(InexactError(:convert, Bool, x)) +end function convert(::Type{BigInt},x::BigFloat) isinteger(x) || throw(InexactError(:convert, BigInt, x)) trunc(BigInt,x) @@ -255,19 +254,18 @@ end ## BigFloat -> AbstractFloat convert(::Type{Float64}, x::BigFloat) = - ccall((:mpfr_get_d,:libmpfr), Float64, (Ptr{BigFloat}, Int32), &x, ROUNDING_MODE[]) + ccall((:mpfr_get_d,:libmpfr), Float64, (Ref{BigFloat}, Int32), x, ROUNDING_MODE[]) convert(::Type{Float32}, x::BigFloat) = - ccall((:mpfr_get_flt,:libmpfr), Float32, (Ptr{BigFloat}, Int32), &x, ROUNDING_MODE[]) + ccall((:mpfr_get_flt,:libmpfr), Float32, (Ref{BigFloat}, Int32), x, ROUNDING_MODE[]) # TODO: avoid double rounding convert(::Type{Float16}, x::BigFloat) = convert(Float16, convert(Float32, x)) Float64(x::BigFloat, r::RoundingMode) = - ccall((:mpfr_get_d,:libmpfr), Float64, (Ptr{BigFloat}, Int32), &x, to_mpfr(r)) + ccall((:mpfr_get_d,:libmpfr), Float64, (Ref{BigFloat}, Int32), x, to_mpfr(r)) Float32(x::BigFloat, r::RoundingMode) = - ccall((:mpfr_get_flt,:libmpfr), Float32, (Ptr{BigFloat}, Int32), &x, to_mpfr(r)) + ccall((:mpfr_get_flt,:libmpfr), Float32, (Ref{BigFloat}, Int32), x, to_mpfr(r)) # TODO: avoid double rounding -Float16(x::BigFloat, r::RoundingMode) = - convert(Float16, Float32(x, r)) +Float16(x::BigFloat, r::RoundingMode) = convert(Float16, Float32(x, r)) promote_rule(::Type{BigFloat}, ::Type{<:Real}) = BigFloat promote_rule(::Type{BigInt}, ::Type{<:AbstractFloat}) = BigFloat @@ -276,9 +274,9 @@ promote_rule(::Type{BigFloat}, ::Type{<:AbstractFloat}) = BigFloat big(::Type{<:AbstractFloat}) = BigFloat function convert(::Type{Rational{BigInt}}, x::AbstractFloat) - if isnan(x); return zero(BigInt)//zero(BigInt); end - if isinf(x); return copysign(one(BigInt),x)//zero(BigInt); end - if iszero(x); return zero(BigInt) // one(BigInt); end + isnan(x) && return zero(BigInt) // zero(BigInt) + isinf(x) && return copysign(one(BigInt),x) // zero(BigInt) + iszero(x) && return zero(BigInt) // one(BigInt) s = max(precision(x) - exponent(x), 0) BigInt(ldexp(x,s)) // (BigInt(1) << s) end @@ -289,14 +287,14 @@ for (fJ, fC) in ((:+,:add), (:*,:mul)) # BigFloat function ($fJ)(x::BigFloat, y::BigFloat) z = BigFloat() - ccall(($(string(:mpfr_,fC)),:libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &x, &y, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC)),:libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), z, x, y, ROUNDING_MODE[]) return z end # Unsigned Integer function ($fJ)(x::BigFloat, c::CulongMax) z = BigFloat() - ccall(($(string(:mpfr_,fC,:_ui)), :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Culong, Int32), &z, &x, c, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC,:_ui)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Culong, Int32), z, x, c, ROUNDING_MODE[]) return z end ($fJ)(c::CulongMax, x::BigFloat) = ($fJ)(x,c) @@ -304,7 +302,7 @@ for (fJ, fC) in ((:+,:add), (:*,:mul)) # Signed Integer function ($fJ)(x::BigFloat, c::ClongMax) z = BigFloat() - ccall(($(string(:mpfr_,fC,:_si)), :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Clong, Int32), &z, &x, c, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC,:_si)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Clong, Int32), z, x, c, ROUNDING_MODE[]) return z end ($fJ)(c::ClongMax, x::BigFloat) = ($fJ)(x,c) @@ -312,7 +310,7 @@ for (fJ, fC) in ((:+,:add), (:*,:mul)) # Float32/Float64 function ($fJ)(x::BigFloat, c::CdoubleMax) z = BigFloat() - ccall(($(string(:mpfr_,fC,:_d)), :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Cdouble, Int32), &z, &x, c, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC,:_d)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Cdouble, Int32), z, x, c, ROUNDING_MODE[]) return z end ($fJ)(c::CdoubleMax, x::BigFloat) = ($fJ)(x,c) @@ -320,7 +318,7 @@ for (fJ, fC) in ((:+,:add), (:*,:mul)) # BigInt function ($fJ)(x::BigFloat, c::BigInt) z = BigFloat() - ccall(($(string(:mpfr_,fC,:_z)), :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigInt}, Int32), &z, &x, &c, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC,:_z)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigInt}, Int32), z, x, c, ROUNDING_MODE[]) return z end ($fJ)(c::BigInt, x::BigFloat) = ($fJ)(x,c) @@ -332,50 +330,50 @@ for (fJ, fC) in ((:-,:sub), (:/,:div)) # BigFloat function ($fJ)(x::BigFloat, y::BigFloat) z = BigFloat() - ccall(($(string(:mpfr_,fC)),:libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &x, &y, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC)),:libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), z, x, y, ROUNDING_MODE[]) return z end # Unsigned Int function ($fJ)(x::BigFloat, c::CulongMax) z = BigFloat() - ccall(($(string(:mpfr_,fC,:_ui)), :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Culong, Int32), &z, &x, c, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC,:_ui)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Culong, Int32), z, x, c, ROUNDING_MODE[]) return z end function ($fJ)(c::CulongMax, x::BigFloat) z = BigFloat() - ccall(($(string(:mpfr_,:ui_,fC)), :libmpfr), Int32, (Ptr{BigFloat}, Culong, Ptr{BigFloat}, Int32), &z, c, &x, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,:ui_,fC)), :libmpfr), Int32, (Ref{BigFloat}, Culong, Ref{BigFloat}, Int32), z, c, x, ROUNDING_MODE[]) return z end # Signed Integer function ($fJ)(x::BigFloat, c::ClongMax) z = BigFloat() - ccall(($(string(:mpfr_,fC,:_si)), :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Clong, Int32), &z, &x, c, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC,:_si)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Clong, Int32), z, x, c, ROUNDING_MODE[]) return z end function ($fJ)(c::ClongMax, x::BigFloat) z = BigFloat() - ccall(($(string(:mpfr_,:si_,fC)), :libmpfr), Int32, (Ptr{BigFloat}, Clong, Ptr{BigFloat}, Int32), &z, c, &x, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,:si_,fC)), :libmpfr), Int32, (Ref{BigFloat}, Clong, Ref{BigFloat}, Int32), z, c, x, ROUNDING_MODE[]) return z end # Float32/Float64 function ($fJ)(x::BigFloat, c::CdoubleMax) z = BigFloat() - ccall(($(string(:mpfr_,fC,:_d)), :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Cdouble, Int32), &z, &x, c, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC,:_d)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Cdouble, Int32), z, x, c, ROUNDING_MODE[]) return z end function ($fJ)(c::CdoubleMax, x::BigFloat) z = BigFloat() - ccall(($(string(:mpfr_,:d_,fC)), :libmpfr), Int32, (Ptr{BigFloat}, Cdouble, Ptr{BigFloat}, Int32), &z, c, &x, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,:d_,fC)), :libmpfr), Int32, (Ref{BigFloat}, Cdouble, Ref{BigFloat}, Int32), z, c, x, ROUNDING_MODE[]) return z end # BigInt function ($fJ)(x::BigFloat, c::BigInt) z = BigFloat() - ccall(($(string(:mpfr_,fC,:_z)), :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigInt}, Int32), &z, &x, &c, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC,:_z)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigInt}, Int32), z, x, c, ROUNDING_MODE[]) return z end # no :mpfr_z_div function @@ -384,13 +382,13 @@ end function -(c::BigInt, x::BigFloat) z = BigFloat() - ccall((:mpfr_z_sub, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigInt}, Ptr{BigFloat}, Int32), &z, &c, &x, ROUNDING_MODE[]) + ccall((:mpfr_z_sub, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigInt}, Ref{BigFloat}, Int32), z, c, x, ROUNDING_MODE[]) return z end function fma(x::BigFloat, y::BigFloat, z::BigFloat) r = BigFloat() - ccall(("mpfr_fma",:libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &r, &x, &y, &z, ROUNDING_MODE[]) + ccall(("mpfr_fma",:libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), r, x, y, z, ROUNDING_MODE[]) return r end @@ -398,58 +396,58 @@ end # BigFloat function div(x::BigFloat, y::BigFloat) z = BigFloat() - ccall((:mpfr_div,:libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &x, &y, to_mpfr(RoundToZero)) - ccall((:mpfr_trunc, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}), &z, &z) + ccall((:mpfr_div,:libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), z, x, y, to_mpfr(RoundToZero)) + ccall((:mpfr_trunc, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), z, z) return z end # Unsigned Int function div(x::BigFloat, c::CulongMax) z = BigFloat() - ccall((:mpfr_div_ui, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Culong, Int32), &z, &x, c, to_mpfr(RoundToZero)) - ccall((:mpfr_trunc, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}), &z, &z) + ccall((:mpfr_div_ui, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Culong, Int32), z, x, c, to_mpfr(RoundToZero)) + ccall((:mpfr_trunc, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), z, z) return z end function div(c::CulongMax, x::BigFloat) z = BigFloat() - ccall((:mpfr_ui_div, :libmpfr), Int32, (Ptr{BigFloat}, Culong, Ptr{BigFloat}, Int32), &z, c, &x, to_mpfr(RoundToZero)) - ccall((:mpfr_trunc, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}), &z, &z) + ccall((:mpfr_ui_div, :libmpfr), Int32, (Ref{BigFloat}, Culong, Ref{BigFloat}, Int32), z, c, x, to_mpfr(RoundToZero)) + ccall((:mpfr_trunc, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), z, z) return z end # Signed Integer function div(x::BigFloat, c::ClongMax) z = BigFloat() - ccall((:mpfr_div_si, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Clong, Int32), &z, &x, c, to_mpfr(RoundToZero)) - ccall((:mpfr_trunc, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}), &z, &z) + ccall((:mpfr_div_si, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Clong, Int32), z, x, c, to_mpfr(RoundToZero)) + ccall((:mpfr_trunc, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), z, z) return z end function div(c::ClongMax, x::BigFloat) z = BigFloat() - ccall((:mpfr_si_div, :libmpfr), Int32, (Ptr{BigFloat}, Clong, Ptr{BigFloat}, Int32), &z, c, &x, to_mpfr(RoundToZero)) - ccall((:mpfr_trunc, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}), &z, &z) + ccall((:mpfr_si_div, :libmpfr), Int32, (Ref{BigFloat}, Clong, Ref{BigFloat}, Int32), z, c, x, to_mpfr(RoundToZero)) + ccall((:mpfr_trunc, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), z, z) return z end # Float32/Float64 function div(x::BigFloat, c::CdoubleMax) z = BigFloat() - ccall((:mpfr_div_d, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Cdouble, Int32), &z, &x, c, to_mpfr(RoundToZero)) - ccall((:mpfr_trunc, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}), &z, &z) + ccall((:mpfr_div_d, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Cdouble, Int32), z, x, c, to_mpfr(RoundToZero)) + ccall((:mpfr_trunc, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), z, z) return z end function div(c::CdoubleMax, x::BigFloat) z = BigFloat() - ccall((:mpfr_d_div, :libmpfr), Int32, (Ptr{BigFloat}, Cdouble, Ptr{BigFloat}, Int32), &z, c, &x, to_mpfr(RoundToZero)) - ccall((:mpfr_trunc, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}), &z, &z) + ccall((:mpfr_d_div, :libmpfr), Int32, (Ref{BigFloat}, Cdouble, Ref{BigFloat}, Int32), z, c, x, to_mpfr(RoundToZero)) + ccall((:mpfr_trunc, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), z, z) return z end # BigInt function div(x::BigFloat, c::BigInt) z = BigFloat() - ccall((:mpfr_div_z, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigInt}, Int32), &z, &x, &c, to_mpfr(RoundToZero)) - ccall((:mpfr_trunc, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}), &z, &z) + ccall((:mpfr_div_z, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigInt}, Int32), z, x, c, to_mpfr(RoundToZero)) + ccall((:mpfr_trunc, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), z, z) return z end @@ -459,23 +457,23 @@ for (fJ, fC, fI) in ((:+, :add, 0), (:*, :mul, 1)) @eval begin function ($fJ)(a::BigFloat, b::BigFloat, c::BigFloat) z = BigFloat() - ccall(($(string(:mpfr_,fC)), :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &a, &b, ROUNDING_MODE[]) - ccall(($(string(:mpfr_,fC)), :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &z, &c, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), z, a, b, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), z, z, c, ROUNDING_MODE[]) return z end function ($fJ)(a::BigFloat, b::BigFloat, c::BigFloat, d::BigFloat) z = BigFloat() - ccall(($(string(:mpfr_,fC)), :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &a, &b, ROUNDING_MODE[]) - ccall(($(string(:mpfr_,fC)), :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &z, &c, ROUNDING_MODE[]) - ccall(($(string(:mpfr_,fC)), :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &z, &d, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), z, a, b, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), z, z, c, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), z, z, d, ROUNDING_MODE[]) return z end function ($fJ)(a::BigFloat, b::BigFloat, c::BigFloat, d::BigFloat, e::BigFloat) z = BigFloat() - ccall(($(string(:mpfr_,fC)), :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &a, &b, ROUNDING_MODE[]) - ccall(($(string(:mpfr_,fC)), :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &z, &c, ROUNDING_MODE[]) - ccall(($(string(:mpfr_,fC)), :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &z, &d, ROUNDING_MODE[]) - ccall(($(string(:mpfr_,fC)), :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &z, &e, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), z, a, b, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), z, z, c, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), z, z, d, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), z, z, e, ROUNDING_MODE[]) return z end end @@ -483,17 +481,15 @@ end function -(x::BigFloat) z = BigFloat() - ccall((:mpfr_neg, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &x, ROUNDING_MODE[]) + ccall((:mpfr_neg, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32), z, x, ROUNDING_MODE[]) return z end function sqrt(x::BigFloat) isnan(x) && return x z = BigFloat() - ccall((:mpfr_sqrt, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &x, ROUNDING_MODE[]) - if isnan(z) - throw(DomainError(x, "NaN result for non-NaN input.")) - end + ccall((:mpfr_sqrt, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32), z, x, ROUNDING_MODE[]) + isnan(z) && throw(DomainError(x, "NaN result for non-NaN input.")) return z end @@ -501,25 +497,25 @@ sqrt(x::BigInt) = sqrt(BigFloat(x)) function ^(x::BigFloat, y::BigFloat) z = BigFloat() - ccall((:mpfr_pow, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &x, &y, ROUNDING_MODE[]) + ccall((:mpfr_pow, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), z, x, y, ROUNDING_MODE[]) return z end function ^(x::BigFloat, y::CulongMax) z = BigFloat() - ccall((:mpfr_pow_ui, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Culong, Int32), &z, &x, y, ROUNDING_MODE[]) + ccall((:mpfr_pow_ui, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Culong, Int32), z, x, y, ROUNDING_MODE[]) return z end function ^(x::BigFloat, y::ClongMax) z = BigFloat() - ccall((:mpfr_pow_si, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Clong, Int32), &z, &x, y, ROUNDING_MODE[]) + ccall((:mpfr_pow_si, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Clong, Int32), z, x, y, ROUNDING_MODE[]) return z end function ^(x::BigFloat, y::BigInt) z = BigFloat() - ccall((:mpfr_pow_z, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigInt}, Int32), &z, &x, &y, ROUNDING_MODE[]) + ccall((:mpfr_pow_z, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigInt}, Int32), z, x, y, ROUNDING_MODE[]) return z end @@ -529,7 +525,7 @@ end for f in (:exp, :exp2, :exp10, :expm1, :cosh, :sinh, :tanh, :sech, :csch, :coth, :cbrt) @eval function $f(x::BigFloat) z = BigFloat() - ccall(($(string(:mpfr_,f)), :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &x, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,f)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32), z, x, ROUNDING_MODE[]) return z end end @@ -537,8 +533,7 @@ end function sincos_fast(v::BigFloat) s = BigFloat() c = BigFloat() - ccall((:mpfr_sin_cos, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), - &s, &c, &v, ROUNDING_MODE[]) + ccall((:mpfr_sin_cos, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), s, c, v, ROUNDING_MODE[]) return (s, c) end sincos(v::BigFloat) = sincos_fast(v) @@ -546,24 +541,23 @@ sincos(v::BigFloat) = sincos_fast(v) # return log(2) function big_ln2() c = BigFloat() - ccall((:mpfr_const_log2, :libmpfr), Cint, (Ptr{BigFloat}, Int32), - &c, MPFR.ROUNDING_MODE[]) + ccall((:mpfr_const_log2, :libmpfr), Cint, (Ref{BigFloat}, Int32), c, MPFR.ROUNDING_MODE[]) return c end function ldexp(x::BigFloat, n::Clong) z = BigFloat() - ccall((:mpfr_mul_2si, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Clong, Int32), &z, &x, n, ROUNDING_MODE[]) + ccall((:mpfr_mul_2si, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Clong, Int32), z, x, n, ROUNDING_MODE[]) return z end function ldexp(x::BigFloat, n::Culong) z = BigFloat() - ccall((:mpfr_mul_2ui, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Culong, Int32), &z, &x, n, ROUNDING_MODE[]) + ccall((:mpfr_mul_2ui, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Culong, Int32), z, x, n, ROUNDING_MODE[]) return z end ldexp(x::BigFloat, n::ClongMax) = ldexp(x, convert(Clong, n)) ldexp(x::BigFloat, n::CulongMax) = ldexp(x, convert(Culong, n)) -ldexp(x::BigFloat, n::Integer) = x*exp2(BigFloat(n)) +ldexp(x::BigFloat, n::Integer) = x * exp2(BigFloat(n)) function factorial(x::BigFloat) if x < 0 || !isinteger(x) @@ -571,13 +565,13 @@ function factorial(x::BigFloat) end ui = convert(Culong, x) z = BigFloat() - ccall((:mpfr_fac_ui, :libmpfr), Int32, (Ptr{BigFloat}, Culong, Int32), &z, ui, ROUNDING_MODE[]) + ccall((:mpfr_fac_ui, :libmpfr), Int32, (Ref{BigFloat}, Culong, Int32), z, ui, ROUNDING_MODE[]) return z end function hypot(x::BigFloat, y::BigFloat) z = BigFloat() - ccall((:mpfr_hypot, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &x, &y, ROUNDING_MODE[]) + ccall((:mpfr_hypot, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), z, x, y, ROUNDING_MODE[]) return z end @@ -588,7 +582,7 @@ for f in (:log, :log2, :log10) "with a complex argument. Try ", $f, "(complex(x))."))) end z = BigFloat() - ccall(($(string(:mpfr_,f)), :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &x, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,f)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32), z, x, ROUNDING_MODE[]) return z end end @@ -596,10 +590,10 @@ end function log1p(x::BigFloat) if x < -1 throw(DomainError(x, string("log1p will only return a complex result if called ", - "with a complex argument. Try log1p(complex(x))."))) + "with a complex argument. Try log1p(complex(x))."))) end z = BigFloat() - ccall((:mpfr_log1p, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &x, ROUNDING_MODE[]) + ccall((:mpfr_log1p, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32), z, x, ROUNDING_MODE[]) return z end @@ -607,7 +601,7 @@ function max(x::BigFloat, y::BigFloat) isnan(x) && return x isnan(y) && return y z = BigFloat() - ccall((:mpfr_max, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &x, &y, ROUNDING_MODE[]) + ccall((:mpfr_max, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), z, x, y, ROUNDING_MODE[]) return z end @@ -615,29 +609,27 @@ function min(x::BigFloat, y::BigFloat) isnan(x) && return x isnan(y) && return y z = BigFloat() - ccall((:mpfr_min, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &x, &y, ROUNDING_MODE[]) + ccall((:mpfr_min, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), z, x, y, ROUNDING_MODE[]) return z end function modf(x::BigFloat) - if isinf(x) - return (BigFloat(NaN), x) - end + isinf(x) && return (BigFloat(NaN), x) zint = BigFloat() zfloat = BigFloat() - ccall((:mpfr_modf, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &zint, &zfloat, &x, ROUNDING_MODE[]) + ccall((:mpfr_modf, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), zint, zfloat, x, ROUNDING_MODE[]) return (zfloat, zint) end function rem(x::BigFloat, y::BigFloat) z = BigFloat() - ccall((:mpfr_fmod, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &x, &y, ROUNDING_MODE[]) + ccall((:mpfr_fmod, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), z, x, y, ROUNDING_MODE[]) return z end function rem(x::BigFloat, y::BigFloat, ::RoundingMode{:Nearest}) z = BigFloat() - ccall((:mpfr_remainder, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &x, &y, ROUNDING_MODE[]) + ccall((:mpfr_remainder, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), z, x, y, ROUNDING_MODE[]) return z end @@ -648,25 +640,19 @@ function sum(arr::AbstractArray{BigFloat}) z = BigFloat(0) for i in arr ccall((:mpfr_add, :libmpfr), Int32, - (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Cint), - &z, &z, &i, 0) + (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Cint), z, z, i, 0) end return z end # Functions for which NaN results are converted to DomainError, following Base -for f in (:sin,:cos,:tan,:sec,:csc, - :acos,:asin,:atan,:acosh,:asinh,:atanh, :gamma) +for f in (:sin, :cos, :tan, :sec, :csc, :acos, :asin, :atan, :acosh, :asinh, :atanh, :gamma) @eval begin function ($f)(x::BigFloat) - if isnan(x) - return x - end + isnan(x) && return x z = BigFloat() - ccall(($(string(:mpfr_,f)), :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &x, ROUNDING_MODE[]) - if isnan(z) - throw(DomainError(x, "NaN result for non-NaN input.")) - end + ccall(($(string(:mpfr_,f)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32), z, x, ROUNDING_MODE[]) + isnan(z) && throw(DomainError(x, "NaN result for non-NaN input.")) return z end end @@ -676,7 +662,7 @@ end const lgamma_signp = Ref{Cint}() function lgamma(x::BigFloat) z = BigFloat() - ccall((:mpfr_lgamma,:libmpfr), Cint, (Ptr{BigFloat}, Ptr{Cint}, Ptr{BigFloat}, Int32), &z, lgamma_signp, &x, ROUNDING_MODE[]) + ccall((:mpfr_lgamma,:libmpfr), Cint, (Ref{BigFloat}, Ref{Cint}, Ref{BigFloat}, Int32), z, lgamma_signp, x, ROUNDING_MODE[]) return z end @@ -684,28 +670,28 @@ lgamma_r(x::BigFloat) = (lgamma(x), lgamma_signp[]) function atan2(y::BigFloat, x::BigFloat) z = BigFloat() - ccall((:mpfr_atan2, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &y, &x, ROUNDING_MODE[]) + ccall((:mpfr_atan2, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), z, y, x, ROUNDING_MODE[]) return z end # Utility functions -==(x::BigFloat, y::BigFloat) = ccall((:mpfr_equal_p, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}), &x, &y) != 0 -<=(x::BigFloat, y::BigFloat) = ccall((:mpfr_lessequal_p, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}), &x, &y) != 0 ->=(x::BigFloat, y::BigFloat) = ccall((:mpfr_greaterequal_p, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}), &x, &y) != 0 -<(x::BigFloat, y::BigFloat) = ccall((:mpfr_less_p, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}), &x, &y) != 0 ->(x::BigFloat, y::BigFloat) = ccall((:mpfr_greater_p, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}), &x, &y) != 0 +==(x::BigFloat, y::BigFloat) = ccall((:mpfr_equal_p, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), x, y) != 0 +<=(x::BigFloat, y::BigFloat) = ccall((:mpfr_lessequal_p, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), x, y) != 0 +>=(x::BigFloat, y::BigFloat) = ccall((:mpfr_greaterequal_p, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), x, y) != 0 +<(x::BigFloat, y::BigFloat) = ccall((:mpfr_less_p, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), x, y) != 0 +>(x::BigFloat, y::BigFloat) = ccall((:mpfr_greater_p, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), x, y) != 0 function cmp(x::BigFloat, y::BigInt) isnan(x) && throw(DomainError(x, "`x` cannot be NaN.")) - ccall((:mpfr_cmp_z, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigInt}), &x, &y) + ccall((:mpfr_cmp_z, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigInt}), x, y) end function cmp(x::BigFloat, y::ClongMax) isnan(x) && throw(DomainError(x, "`x` cannot be NaN.")) - ccall((:mpfr_cmp_si, :libmpfr), Int32, (Ptr{BigFloat}, Clong), &x, y) + ccall((:mpfr_cmp_si, :libmpfr), Int32, (Ref{BigFloat}, Clong), x, y) end function cmp(x::BigFloat, y::CulongMax) isnan(x) && throw(DomainError(x, "`x` cannot be NaN.")) - ccall((:mpfr_cmp_ui, :libmpfr), Int32, (Ptr{BigFloat}, Culong), &x, y) + ccall((:mpfr_cmp_ui, :libmpfr), Int32, (Ref{BigFloat}, Culong), x, y) end cmp(x::BigFloat, y::Integer) = cmp(x,big(y)) cmp(x::Integer, y::BigFloat) = -cmp(y,x) @@ -713,29 +699,29 @@ cmp(x::Integer, y::BigFloat) = -cmp(y,x) function cmp(x::BigFloat, y::CdoubleMax) isnan(x) && throw(DomainError(x, "`x` cannot be NaN.")) isnan(y) && throw(DomainError(y, "`y` cannot be NaN.")) - ccall((:mpfr_cmp_d, :libmpfr), Int32, (Ptr{BigFloat}, Cdouble), &x, y) + ccall((:mpfr_cmp_d, :libmpfr), Int32, (Ref{BigFloat}, Cdouble), x, y) end cmp(x::CdoubleMax, y::BigFloat) = -cmp(y,x) -==(x::BigFloat, y::Integer) = !isnan(x) && cmp(x,y) == 0 -==(x::Integer, y::BigFloat) = y == x +==(x::BigFloat, y::Integer) = !isnan(x) && cmp(x,y) == 0 +==(x::Integer, y::BigFloat) = y == x ==(x::BigFloat, y::CdoubleMax) = !isnan(x) && !isnan(y) && cmp(x,y) == 0 ==(x::CdoubleMax, y::BigFloat) = y == x -<(x::BigFloat, y::Integer) = !isnan(x) && cmp(x,y) < 0 -<(x::Integer, y::BigFloat) = !isnan(y) && cmp(y,x) > 0 +<(x::BigFloat, y::Integer) = !isnan(x) && cmp(x,y) < 0 +<(x::Integer, y::BigFloat) = !isnan(y) && cmp(y,x) > 0 <(x::BigFloat, y::CdoubleMax) = !isnan(x) && !isnan(y) && cmp(x,y) < 0 <(x::CdoubleMax, y::BigFloat) = !isnan(x) && !isnan(y) && cmp(y,x) > 0 -<=(x::BigFloat, y::Integer) = !isnan(x) && cmp(x,y) <= 0 -<=(x::Integer, y::BigFloat) = !isnan(y) && cmp(y,x) >= 0 +<=(x::BigFloat, y::Integer) = !isnan(x) && cmp(x,y) <= 0 +<=(x::Integer, y::BigFloat) = !isnan(y) && cmp(y,x) >= 0 <=(x::BigFloat, y::CdoubleMax) = !isnan(x) && !isnan(y) && cmp(x,y) <= 0 <=(x::CdoubleMax, y::BigFloat) = !isnan(x) && !isnan(y) && cmp(y,x) >= 0 -signbit(x::BigFloat) = ccall((:mpfr_signbit, :libmpfr), Int32, (Ptr{BigFloat},), &x) != 0 +signbit(x::BigFloat) = ccall((:mpfr_signbit, :libmpfr), Int32, (Ref{BigFloat},), x) != 0 function precision(x::BigFloat) # precision of an object of type BigFloat - return ccall((:mpfr_get_prec, :libmpfr), Clong, (Ptr{BigFloat},), &x) + return ccall((:mpfr_get_prec, :libmpfr), Clong, (Ref{BigFloat},), x) end """ @@ -793,7 +779,7 @@ setrounding(::Type{BigFloat},r::RoundingMode) = setrounding_raw(BigFloat,to_mpfr function copysign(x::BigFloat, y::BigFloat) z = BigFloat() - ccall((:mpfr_copysign, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &x, &y, ROUNDING_MODE[]) + ccall((:mpfr_copysign, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), z, x, y, ROUNDING_MODE[]) return z end @@ -802,34 +788,34 @@ function exponent(x::BigFloat) throw(DomainError(x, "`x` must be non-zero and finite.")) end # The '- 1' is to make it work as Base.exponent - return ccall((:mpfr_get_exp, :libmpfr), Clong, (Ptr{BigFloat},), &x) - 1 + return ccall((:mpfr_get_exp, :libmpfr), Clong, (Ref{BigFloat},), x) - 1 end function frexp(x::BigFloat) z = BigFloat() c = Ref{Clong}() - ccall((:mpfr_frexp, :libmpfr), Int32, (Ptr{Clong}, Ptr{BigFloat}, Ptr{BigFloat}, Cint), c, &z, &x, ROUNDING_MODE[]) + ccall((:mpfr_frexp, :libmpfr), Int32, (Ptr{Clong}, Ref{BigFloat}, Ref{BigFloat}, Cint), c, z, x, ROUNDING_MODE[]) return (z, c[]) end function significand(x::BigFloat) z = BigFloat() c = Ref{Clong}() - ccall((:mpfr_frexp, :libmpfr), Int32, (Ptr{Clong}, Ptr{BigFloat}, Ptr{BigFloat}, Cint), c, &z, &x, ROUNDING_MODE[]) + ccall((:mpfr_frexp, :libmpfr), Int32, (Ptr{Clong}, Ref{BigFloat}, Ref{BigFloat}, Cint), c, z, x, ROUNDING_MODE[]) # Double the significand to make it work as Base.significand - ccall((:mpfr_mul_si, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Clong, Int32), &z, &z, 2, ROUNDING_MODE[]) + ccall((:mpfr_mul_si, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Clong, Int32), z, z, 2, ROUNDING_MODE[]) return z end function isinteger(x::BigFloat) - return ccall((:mpfr_integer_p, :libmpfr), Int32, (Ptr{BigFloat},), &x) != 0 + return ccall((:mpfr_integer_p, :libmpfr), Int32, (Ref{BigFloat},), x) != 0 end for f in (:ceil, :floor, :trunc) @eval begin function ($f)(x::BigFloat) z = BigFloat() - ccall(($(string(:mpfr_,f)), :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}), &z, &x) + ccall(($(string(:mpfr_,f)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), z, x) return z end end @@ -837,21 +823,21 @@ end function round(x::BigFloat) z = BigFloat() - ccall((:mpfr_rint, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Cint), &z, &x, ROUNDING_MODE[]) + ccall((:mpfr_rint, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Cint), z, x, ROUNDING_MODE[]) return z end function round(x::BigFloat,::RoundingMode{:NearestTiesAway}) z = BigFloat() - ccall((:mpfr_round, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}), &z, &x) + ccall((:mpfr_round, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), z, x) return z end function isinf(x::BigFloat) - return ccall((:mpfr_inf_p, :libmpfr), Int32, (Ptr{BigFloat},), &x) != 0 + return ccall((:mpfr_inf_p, :libmpfr), Int32, (Ref{BigFloat},), x) != 0 end function isnan(x::BigFloat) - return ccall((:mpfr_nan_p, :libmpfr), Int32, (Ptr{BigFloat},), &x) != 0 + return ccall((:mpfr_nan_p, :libmpfr), Int32, (Ref{BigFloat},), x) != 0 end isfinite(x::BigFloat) = !isinf(x) && !isnan(x) @@ -859,22 +845,22 @@ isfinite(x::BigFloat) = !isinf(x) && !isnan(x) iszero(x::BigFloat) = x == Clong(0) isone(x::BigFloat) = x == Clong(1) -@eval typemax(::Type{BigFloat}) = $(BigFloat( Inf)) +@eval typemax(::Type{BigFloat}) = $(BigFloat(Inf)) @eval typemin(::Type{BigFloat}) = $(BigFloat(-Inf)) function nextfloat(x::BigFloat) z = BigFloat() - ccall((:mpfr_set, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Int32), - &z, &x, ROUNDING_MODE[]) - ccall((:mpfr_nextabove, :libmpfr), Int32, (Ptr{BigFloat},), &z) != 0 + ccall((:mpfr_set, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32), + z, x, ROUNDING_MODE[]) + ccall((:mpfr_nextabove, :libmpfr), Int32, (Ref{BigFloat},), z) != 0 return z end function prevfloat(x::BigFloat) z = BigFloat() - ccall((:mpfr_set, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Int32), - &z, &x, ROUNDING_MODE[]) - ccall((:mpfr_nextbelow, :libmpfr), Int32, (Ptr{BigFloat},), &z) != 0 + ccall((:mpfr_set, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32), + z, x, ROUNDING_MODE[]) + ccall((:mpfr_nextbelow, :libmpfr), Int32, (Ref{BigFloat},), z) != 0 return z end @@ -919,12 +905,12 @@ function string(x::BigFloat) lng = k + Int32(8) # Add space for the sign, the most significand digit, the dot and the exponent buf = Base.StringVector(lng + 1) # format strings are guaranteed to contain no NUL, so we don't use Cstring - lng = ccall((:mpfr_snprintf,:libmpfr), Int32, (Ptr{UInt8}, Culong, Ptr{UInt8}, Ptr{BigFloat}...), buf, lng + 1, "%.Re", &x) + lng = ccall((:mpfr_snprintf,:libmpfr), Int32, (Ptr{UInt8}, Culong, Ptr{UInt8}, Ref{BigFloat}...), buf, lng + 1, "%.Re", x) if lng < k + 5 # print at least k decimal places - lng = ccall((:mpfr_sprintf,:libmpfr), Int32, (Ptr{UInt8}, Ptr{UInt8}, Ptr{BigFloat}...), buf, "%.$(k)Re", &x) + lng = ccall((:mpfr_sprintf,:libmpfr), Int32, (Ptr{UInt8}, Ptr{UInt8}, Ref{BigFloat}...), buf, "%.$(k)Re", x) elseif lng > k + 8 buf = Base.StringVector(lng + 1) - lng = ccall((:mpfr_snprintf,:libmpfr), Int32, (Ptr{UInt8}, Culong, Ptr{UInt8}, Ptr{BigFloat}...), buf, lng + 1, "%.Re", &x) + lng = ccall((:mpfr_snprintf,:libmpfr), Int32, (Ptr{UInt8}, Culong, Ptr{UInt8}, Ref{BigFloat}...), buf, lng + 1, "%.Re", x) end n = (1 <= x < 10 || -10 < x <= -1 || iszero(x)) ? lng - 4 : lng return String(resize!(buf,n)) @@ -946,15 +932,12 @@ set_emax!(x) = ccall((:mpfr_set_emax, :libmpfr), Void, (Clong,), x) set_emin!(x) = ccall((:mpfr_set_emin, :libmpfr), Void, (Clong,), x) function Base.deepcopy_internal(x::BigFloat, stackdict::ObjectIdDict) - if haskey(stackdict, x) - return stackdict[x] - end - N = precision(x) + haskey(stackdict, x) && return stackdict[x] + prec = precision(x) y = BigFloat(zero(Clong), zero(Cint), zero(Clong), C_NULL) - ccall((:mpfr_init2,:libmpfr), Void, (Ptr{BigFloat}, Clong), &y, N) + ccall((:mpfr_init2,:libmpfr), Void, (Ref{BigFloat}, Clong), y, prec) finalizer(y, cglobal((:mpfr_clear, :libmpfr))) - ccall((:mpfr_set, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Int32), - &y, &x, ROUNDING_MODE[]) + ccall((:mpfr_set, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32), y, x, ROUNDING_MODE[]) stackdict[x] = y return y end From 9f6187857423e32bda68d244b029fa7cf566a4f2 Mon Sep 17 00:00:00 2001 From: Yichao Yu Date: Sat, 26 Aug 2017 14:14:36 -0400 Subject: [PATCH 168/324] Make sure error from `Expr(:new)` is not optimized away. (#23353) * Fix effect_free for `Expr(:new)` Check if the type being constructed is leaftype and check if all fields are correctly typed. The expression could throw an error otherwise. * Fix functions used by interpreter and codegen to check field types * Fix (remove) assumption of `Expr(:new)` being effect free in lowering --- base/inference.jl | 19 +++++++++++++------ src/datatype.c | 7 +++++-- src/interpreter.c | 8 ++++++-- src/julia-syntax.scm | 1 - test/core.jl | 31 +++++++++++++++++++++++++++++++ 5 files changed, 55 insertions(+), 11 deletions(-) diff --git a/base/inference.jl b/base/inference.jl index a9574ad95301c..3c22f5acd2e98 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -3925,12 +3925,19 @@ function effect_free(@nospecialize(e), src::CodeInfo, mod::Module, allow_volatil return false end elseif head === :new - if !allow_volatile - a = ea[1] - typ = widenconst(exprtype(a, src, mod)) - if !isType(typ) || !isa((typ::Type).parameters[1],DataType) || ((typ::Type).parameters[1]::DataType).mutable - return false - end + a = ea[1] + typ = exprtype(a, src, mod) + # `Expr(:new)` of unknown type could raise arbitrary TypeError. + typ, isexact = instanceof_tfunc(typ) + isexact || return false + (isleaftype(typ) && !iskindtype(typ)) || return false + typ = typ::DataType + if !allow_volatile && typ.mutable + return false + end + fieldcount(typ) >= length(ea) - 1 || return false + for fld_idx in 1:(length(ea) - 1) + exprtype(ea[fld_idx + 1], src, mod) ⊑ fieldtype(typ, fld_idx) || return false end # fall-through elseif head === :return diff --git a/src/datatype.c b/src/datatype.c index c67f55d72080c..147ea375b6023 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -717,7 +717,7 @@ JL_DLLEXPORT jl_value_t *jl_new_struct(jl_datatype_t *type, ...) size_t nf = jl_datatype_nfields(type); va_start(args, type); jl_value_t *jv = jl_gc_alloc(ptls, jl_datatype_size(type), type); - for(size_t i=0; i < nf; i++) { + for (size_t i = 0; i < nf; i++) { jl_set_nth_field(jv, i, va_arg(args, jl_value_t*)); } va_end(args); @@ -731,7 +731,10 @@ JL_DLLEXPORT jl_value_t *jl_new_structv(jl_datatype_t *type, jl_value_t **args, if (type->instance != NULL) return type->instance; size_t nf = jl_datatype_nfields(type); jl_value_t *jv = jl_gc_alloc(ptls, jl_datatype_size(type), type); - for(size_t i=0; i < na; i++) { + for (size_t i = 0; i < na; i++) { + jl_value_t *ft = jl_field_type(type, i); + if (!jl_isa(args[i], ft)) + jl_type_error("new", ft, args[i]); jl_set_nth_field(jv, i, args[i]); } for(size_t i=na; i < nf; i++) { diff --git a/src/interpreter.c b/src/interpreter.c index b995aa4cbe8ec..27419c71b7373 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -270,8 +270,12 @@ static jl_value_t *eval(jl_value_t *e, interpreter_state *s) JL_GC_PUSH2(&thetype, &v); assert(jl_is_structtype(thetype)); v = jl_new_struct_uninit((jl_datatype_t*)thetype); - for(size_t i=1; i < nargs; i++) { - jl_set_nth_field(v, i-1, eval(args[i], s)); + for (size_t i = 1; i < nargs; i++) { + jl_value_t *ft = jl_field_type(thetype, i - 1); + jl_value_t *fldv = eval(args[i], s); + if (!jl_isa(fldv, ft)) + jl_type_error("new", ft, fldv); + jl_set_nth_field(v, i - 1, fldv); } JL_GC_POP(); return v; diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 53cca2c341ec0..05c811be9226c 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -3507,7 +3507,6 @@ f(x) = yt(x) (callex (cons (car e) args))) (cond (tail (emit-return callex)) (value callex) - ((eq? (car e) 'new) #f) (else (emit callex))))) ((=) (let* ((rhs (compile (caddr e) break-labels #t #f)) diff --git a/test/core.jl b/test/core.jl index 08c3986562915..db71df9fcdc68 100644 --- a/test/core.jl +++ b/test/core.jl @@ -3147,6 +3147,37 @@ end @test_throws TypeError MyType8010([3.0;4.0]) @test_throws TypeError MyType8010_ghost([3.0;4.0]) +module TestNewTypeError +using Base.Test + +struct A +end +struct B + a::A +end +@eval function f1() + # Emitting this direction is not recommended but it can come from `convert` that does not + # return the correct type. + $(Expr(:new, B, 1)) +end +@eval function f2() + a = $(Expr(:new, B, 1)) + a = a + return nothing +end +@generated function f3() + quote + $(Expr(:new, B, 1)) + return nothing + end +end +@test_throws TypeError f1() +@test_throws TypeError f2() +@test_throws TypeError f3() +@test_throws TypeError eval(Expr(:new, B, 1)) + +end + # don't allow redefining types if ninitialized changes struct NInitializedTestType a From ab0ac5e31d155d6bb8d987788659cb1ea50d8e62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kenta=20Sato=20=28=E4=BD=90=E8=97=A4=20=E5=BB=BA=E5=A4=AA?= =?UTF-8?q?=29?= Date: Sun, 27 Aug 2017 04:24:17 +0900 Subject: [PATCH 169/324] fix a typo in a docstring (#23466) --- base/distributed/remotecall.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/distributed/remotecall.jl b/base/distributed/remotecall.jl index 4ce1a318a604f..68b143aff2093 100644 --- a/base/distributed/remotecall.jl +++ b/base/distributed/remotecall.jl @@ -551,7 +551,7 @@ end take!(rr::RemoteChannel, args...) Fetch value(s) from a [`RemoteChannel`](@ref) `rr`, -removing the value(s) in the processs. +removing the value(s) in the process. """ take!(rr::RemoteChannel, args...) = call_on_owner(take_ref, rr, myid(), args...) From fd06baa217e1625877d9cba2b11815b5c7371c25 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Sat, 26 Aug 2017 15:38:06 -0400 Subject: [PATCH 170/324] Update conditional documentation example It confuses femtocleaner (https://github.com/JuliaLang/julia/pull/23440) and it's probably better to not reference a specific julia version in the documentation anyway. --- doc/src/manual/documentation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/src/manual/documentation.md b/doc/src/manual/documentation.md index a8176c4f1a59f..289bdbb30f0d2 100644 --- a/doc/src/manual/documentation.md +++ b/doc/src/manual/documentation.md @@ -254,13 +254,13 @@ Documentation written in non-toplevel blocks, such as `begin`, `if`, `for`, and added to the documentation system as blocks are evaluated. For example: ```julia -if VERSION > v"0.5" +if condition() "..." f(x) = x end ``` -will add documentation to `f(x)` when the condition is `true`. Note that even if `f(x)` goes +will add documentation to `f(x)` when `condition()` is `true`. Note that even if `f(x)` goes out of scope at the end of the block, its documentation will remain. ### Dynamic documentation From b510714dbe4f789b7200066b02601e91fa1bbf9d Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Sat, 26 Aug 2017 16:37:37 -0400 Subject: [PATCH 171/324] inference: use TypedSlot to mark intermediate load types previously, we widened the load type too soon, discarding potentially valuable information earlier than necessary --- base/inference.jl | 29 ++++++++++++++++++++--------- test/inference.jl | 10 ++++++++-- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/base/inference.jl b/base/inference.jl index 3c22f5acd2e98..0e8c0ed3e0c1d 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -3563,7 +3563,7 @@ function annotate_slot_load!(e::Expr, vtypes::VarTable, sv::InferenceState, unde elseif isa(subex, Slot) id = slot_id(subex) s = vtypes[id] - vt = widenconst(s.typ) + vt = s.typ if s.undef # find used-undef variables undefs[id] = true @@ -3680,28 +3680,39 @@ function type_annotate!(sv::InferenceState) end # widen all Const elements in type annotations -function _widen_all_consts!(e::Expr, untypedload::Vector{Bool}) +function _widen_all_consts!(e::Expr, untypedload::Vector{Bool}, slottypes::Vector{Any}) e.typ = widenconst(e.typ) for i = 1:length(e.args) x = e.args[i] if isa(x, Expr) - _widen_all_consts!(x, untypedload) - elseif isa(x, Slot) && (i != 1 || e.head !== :(=)) - untypedload[slot_id(x)] = true + _widen_all_consts!(x, untypedload, slottypes) + elseif isa(x, TypedSlot) + vt = widenconst(x.typ) + if !(vt === x.typ) + if slottypes[x.id] <: vt + x = SlotNumber(x.id) + untypedload[x.id] = true + else + x = TypedSlot(x.id, vt) + end + e.args[i] = x + end + elseif isa(x, SlotNumber) && (i != 1 || e.head !== :(=)) + untypedload[x.id] = true end end nothing end + function widen_all_consts!(src::CodeInfo) for i = 1:length(src.ssavaluetypes) src.ssavaluetypes[i] = widenconst(src.ssavaluetypes[i]) end nslots = length(src.slottypes) untypedload = fill(false, nslots) - for i = 1:length(src.code) - x = src.code[i] - isa(x, Expr) && _widen_all_consts!(x, untypedload) - end + e = Expr(:body) + e.args = src.code + _widen_all_consts!(e, untypedload, src.slottypes) for i = 1:nslots src.slottypes[i] = widen_slot_type(src.slottypes[i], untypedload[i]) end diff --git a/test/inference.jl b/test/inference.jl index 0d6458dba76b6..1a6ed74277099 100644 --- a/test/inference.jl +++ b/test/inference.jl @@ -256,6 +256,7 @@ end # make sure none of the slottypes are left as Core.Inference.Const objects function f18679() + local a for i = 1:2 if i == 1 a = ((),) @@ -265,7 +266,7 @@ function f18679() end end g18679(x::Tuple) = () -g18679() = g18679(any_undef_global::Union{Int,Tuple{}}) +g18679() = g18679(any_undef_global::Union{Int, Tuple{}}) for code in Any[ @code_typed(f18679())[1] @code_typed(g18679())[1]] @@ -281,6 +282,8 @@ for code in Any[ for e in code.code notconst(e) end + @test f18679() === () + @test_throws UndefVarError(:any_undef_global) g18679() end # branching based on inferrable conditions @@ -472,7 +475,10 @@ end function g19348(x) a, b = x - return a + b + g = 1 + g = 2 + c = Base.indexed_next(x, g, g) + return a + b + c[1] end test_inferred_static(@code_typed g19348((1, 2.0))) From de7b8935c82c0c63875e698faf7a4ca8537a4d7e Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Sat, 26 Aug 2017 16:46:58 -0400 Subject: [PATCH 172/324] inference: ensure NewvarNode side-effect is handled --- base/inference.jl | 4 +++ test/inference.jl | 82 ++++++++++++++++++++++++++++------------------- 2 files changed, 53 insertions(+), 33 deletions(-) diff --git a/base/inference.jl b/base/inference.jl index 0e8c0ed3e0c1d..d2255af9fc87b 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -3153,6 +3153,10 @@ function typeinf_work(frame::InferenceState) # directly forward changes to an SSAValue to the applicable line record_ssa_assign(changes_var.id + 1, changes.vtype.typ, frame) end + elseif isa(stmt, NewvarNode) + sn = slot_id(stmt.slot) + changes = changes::VarTable + changes[sn] = VarState(Bottom, true) elseif isa(stmt, GotoNode) pc´ = (stmt::GotoNode).label elseif isa(stmt, Expr) diff --git a/test/inference.jl b/test/inference.jl index 1a6ed74277099..6efe8b19b2f21 100644 --- a/test/inference.jl +++ b/test/inference.jl @@ -254,38 +254,6 @@ function foo9222() end @test 0.0 == foo9222() -# make sure none of the slottypes are left as Core.Inference.Const objects -function f18679() - local a - for i = 1:2 - if i == 1 - a = ((),) - else - return a[1] - end - end -end -g18679(x::Tuple) = () -g18679() = g18679(any_undef_global::Union{Int, Tuple{}}) -for code in Any[ - @code_typed(f18679())[1] - @code_typed(g18679())[1]] - @test all(x->isa(x, Type), code.slottypes) - local notconst(@nospecialize(other)) = true - notconst(slot::TypedSlot) = @test isa(slot.typ, Type) - function notconst(expr::Expr) - @test isa(expr.typ, Type) - for a in expr.args - notconst(a) - end - end - for e in code.code - notconst(e) - end - @test f18679() === () - @test_throws UndefVarError(:any_undef_global) g18679() -end - # branching based on inferrable conditions let f(x) = isa(x,Int) ? 1 : "" @test Base.return_types(f, Tuple{Int}) == [Int] @@ -473,6 +441,29 @@ function test_inferred_static(arrow::Pair) end end +function f18679() + local a + for i = 1:2 + if i == 1 + a = ((),) + else + return a[1] + end + end +end +g18679(x::Tuple) = () +g18679() = g18679(any_undef_global::Union{Int, Tuple{}}) +function h18679() + for i = 1:2 + local a + if i == 1 + a = ((),) + else + @isdefined(a) && return "BAD" + end + end +end + function g19348(x) a, b = x g = 1 @@ -480,7 +471,32 @@ function g19348(x) c = Base.indexed_next(x, g, g) return a + b + c[1] end -test_inferred_static(@code_typed g19348((1, 2.0))) + +for codetype in Any[ + @code_typed(f18679()), + @code_typed(g18679()), + @code_typed(h18679()), + @code_typed(g19348((1, 2.0)))] + # make sure none of the slottypes are left as Core.Inference.Const objects + code = codetype[1] + @test all(x->isa(x, Type), code.slottypes) + local notconst(@nospecialize(other)) = true + notconst(slot::TypedSlot) = @test isa(slot.typ, Type) + function notconst(expr::Expr) + @test isa(expr.typ, Type) + for a in expr.args + notconst(a) + end + end + for e in code.code + notconst(e) + end + test_inferred_static(code) +end +@test f18679() === () +@test_throws UndefVarError(:any_undef_global) g18679() +@test h18679() === nothing + # issue #5575 f5575() = zeros(Type[Float64][1], 1) From e89d150c9a1bd45fda40ddba58135aa3ac2f582d Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Sat, 26 Aug 2017 18:16:41 -0400 Subject: [PATCH 173/324] update libuv (#23326) fixes UV_HANDLE_EMULATE_IOCP handling for Windows pipes close #11727 --- .../libuv-52d72a52cc7ccd570929990f010ed16e2ec604c8.tar.gz/md5 | 1 - .../sha512 | 1 - .../libuv-c5a4e584989669ad48c1728b35063291e16f85ee.tar.gz/md5 | 1 + .../sha512 | 1 + deps/libuv.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 deps/checksums/libuv-52d72a52cc7ccd570929990f010ed16e2ec604c8.tar.gz/md5 delete mode 100644 deps/checksums/libuv-52d72a52cc7ccd570929990f010ed16e2ec604c8.tar.gz/sha512 create mode 100644 deps/checksums/libuv-c5a4e584989669ad48c1728b35063291e16f85ee.tar.gz/md5 create mode 100644 deps/checksums/libuv-c5a4e584989669ad48c1728b35063291e16f85ee.tar.gz/sha512 diff --git a/deps/checksums/libuv-52d72a52cc7ccd570929990f010ed16e2ec604c8.tar.gz/md5 b/deps/checksums/libuv-52d72a52cc7ccd570929990f010ed16e2ec604c8.tar.gz/md5 deleted file mode 100644 index 07d3ac158b709..0000000000000 --- a/deps/checksums/libuv-52d72a52cc7ccd570929990f010ed16e2ec604c8.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -0e6ec181c8bfd322a17dbd843b2b8ebb diff --git a/deps/checksums/libuv-52d72a52cc7ccd570929990f010ed16e2ec604c8.tar.gz/sha512 b/deps/checksums/libuv-52d72a52cc7ccd570929990f010ed16e2ec604c8.tar.gz/sha512 deleted file mode 100644 index 704a70e7378a9..0000000000000 --- a/deps/checksums/libuv-52d72a52cc7ccd570929990f010ed16e2ec604c8.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -4bf9c8b16617691b70ea43c667ecc575d2a06b3d0c347b949d10360c012138585a13ecea5e18de81b1585a96cf7734b7cc3d6072490898f1d5531c702cf5afab diff --git a/deps/checksums/libuv-c5a4e584989669ad48c1728b35063291e16f85ee.tar.gz/md5 b/deps/checksums/libuv-c5a4e584989669ad48c1728b35063291e16f85ee.tar.gz/md5 new file mode 100644 index 0000000000000..3399b978883bd --- /dev/null +++ b/deps/checksums/libuv-c5a4e584989669ad48c1728b35063291e16f85ee.tar.gz/md5 @@ -0,0 +1 @@ +8e84e1a3332fd2eb63e6c9d0eaa731b1 diff --git a/deps/checksums/libuv-c5a4e584989669ad48c1728b35063291e16f85ee.tar.gz/sha512 b/deps/checksums/libuv-c5a4e584989669ad48c1728b35063291e16f85ee.tar.gz/sha512 new file mode 100644 index 0000000000000..cdaf96971b71f --- /dev/null +++ b/deps/checksums/libuv-c5a4e584989669ad48c1728b35063291e16f85ee.tar.gz/sha512 @@ -0,0 +1 @@ +53f636422d8ac2c1e738625868e4b000b0e332c1f362564ced7e740ff94019a68017ab8674f85653133a0a9c5c48db78dd043e4d2ab9b9e8451b2cb10a27ddd0 diff --git a/deps/libuv.version b/deps/libuv.version index dd4ea6f5e5760..1a59e7be3fd44 100644 --- a/deps/libuv.version +++ b/deps/libuv.version @@ -1,2 +1,2 @@ LIBUV_BRANCH=julia-uv1.9.0 -LIBUV_SHA1=52d72a52cc7ccd570929990f010ed16e2ec604c8 +LIBUV_SHA1=c5a4e584989669ad48c1728b35063291e16f85ee From 35881021e04edb50b3b69916048066c090460680 Mon Sep 17 00:00:00 2001 From: Milan Bouchet-Valat Date: Mon, 31 Jul 2017 15:55:17 +0200 Subject: [PATCH 174/324] Rename select* functions to partialsort* The new name is more explicit, more consistent with sort and with one of the most commonly used names for this operation (from the C++ stdlib). It also does not conflict with other meanings e.g. for POSIX sockets and SQL. --- NEWS.md | 3 +++ base/deprecated.jl | 6 +++++ base/exports.jl | 8 +++--- base/sort.jl | 59 +++++++++++++++++++++--------------------- base/statistics.jl | 4 +-- doc/src/stdlib/sort.md | 8 +++--- test/core.jl | 6 ++--- test/ranges.jl | 2 +- test/sorting.jl | 24 ++++++++--------- 9 files changed, 65 insertions(+), 55 deletions(-) diff --git a/NEWS.md b/NEWS.md index bbd4f44b6517e..dea80b24dcfaa 100644 --- a/NEWS.md +++ b/NEWS.md @@ -368,6 +368,9 @@ Deprecated or removed respectively. Similarly, `MPFR.get_version()`, has been renamed to `MPFR.version()` ([#23323]). Also, `LinAlg.LAPACK.laver()` has been renamed to `LinAlg.LAPACK.version()` and now returns a `VersionNumber`. + * `select`, `select!`, `selectperm` and `selectperm!` have been renamed respectively to + `partialsort`, `partialsort!`, `partialsortperm` and `partialsortperm!` ([#23051]). + Command-line option changes --------------------------- diff --git a/base/deprecated.jl b/base/deprecated.jl index 34403afcd8121..238fab1ae71b8 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1742,6 +1742,12 @@ end @deprecate IOContext(io::IO, key, value) IOContext(io, key=>value) +# issue #22791 +@deprecate_binding select partialsort +@deprecate_binding select! partialsort! +@deprecate_binding selectperm partialsortperm +@deprecate_binding selectperm! partialsortperm! + # END 0.7 deprecations # BEGIN 1.0 deprecations diff --git a/base/exports.jl b/base/exports.jl index 4563ccdefb455..10133316bbe6d 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -492,6 +492,10 @@ export ones, parent, parentindexes, + partialsort, + partialsort!, + partialsortperm, + partialsortperm!, permute, permute!, permutedims, @@ -517,8 +521,6 @@ export searchsorted, searchsortedfirst, searchsortedlast, - select!, - select, shuffle, shuffle!, size, @@ -526,8 +528,6 @@ export sort!, sort, sortcols, - selectperm, - selectperm!, sortperm, sortperm!, sortrows, diff --git a/base/sort.jl b/base/sort.jl index 090a2f880a957..7930f856f2f92 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -15,18 +15,18 @@ import export # also exported by Base # order-only: issorted, - select, - select!, searchsorted, searchsortedfirst, searchsortedlast, # order & algorithm: sort, sort!, - selectperm, - selectperm!, sortperm, sortperm!, + partialsort, + partialsort!, + partialsortperm, + partialsortperm!, sortrows, sortcols, # algorithms: @@ -82,20 +82,20 @@ issorted(itr; lt=isless, by=identity, rev::Bool=false, order::Ordering=Forward) = issorted(itr, ord(lt,by,rev,order)) -function select!(v::AbstractVector, k::Union{Int,OrdinalRange}, o::Ordering) +function partialsort!(v::AbstractVector, k::Union{Int,OrdinalRange}, o::Ordering) inds = indices(v, 1) sort!(v, first(inds), last(inds), PartialQuickSort(k), o) v[k] end """ - select!(v, k, [by=,] [lt=,] [rev=false]) + partialsort!(v, k, [by=,] [lt=,] [rev=false]) Partially sort the vector `v` in place, according to the order specified by `by`, `lt` and `rev` so that the value at index `k` (or range of adjacent values if `k` is a range) occurs at the position where it would appear if the array were fully sorted via a non-stable algorithm. If `k` is a single index, that value is returned; if `k` is a range, an array of -values at those indices is returned. Note that `select!` does not fully sort the input +values at those indices is returned. Note that `partialsort!` does not fully sort the input array. # Examples @@ -108,7 +108,7 @@ julia> a = [1, 2, 4, 3, 4] 3 4 -julia> select!(a, 4) +julia> partialsort!(a, 4) 4 julia> a @@ -127,7 +127,7 @@ julia> a = [1, 2, 4, 3, 4] 3 4 -julia> select!(a, 4, rev=true) +julia> partialsort!(a, 4, rev=true) 2 julia> a @@ -139,17 +139,18 @@ julia> a 1 ``` """ -select!(v::AbstractVector, k::Union{Int,OrdinalRange}; - lt=isless, by=identity, rev::Bool=false, order::Ordering=Forward) = - select!(v, k, ord(lt,by,rev,order)) +partialsort!(v::AbstractVector, k::Union{Int,OrdinalRange}; + lt=isless, by=identity, rev::Bool=false, order::Ordering=Forward) = + partialsort!(v, k, ord(lt,by,rev,order)) """ - select(v, k, [by=,] [lt=,] [rev=false]) + partialsort(v, k, [by=,] [lt=,] [rev=false]) -Variant of [`select!`](@ref) which copies `v` before partially sorting it, thereby returning the -same thing as `select!` but leaving `v` unmodified. +Variant of [`partialsort!`](@ref) which copies `v` before partially sorting it, thereby returning the +same thing as `partialsort!` but leaving `v` unmodified. """ -select(v::AbstractVector, k::Union{Int,OrdinalRange}; kws...) = select!(copymutable(v), k; kws...) +partialsort(v::AbstractVector, k::Union{Int,OrdinalRange}; kws...) = + partialsort!(copymutable(v), k; kws...) # reference on sorted binary search: @@ -667,10 +668,10 @@ julia> v """ sort(v::AbstractVector; kws...) = sort!(copymutable(v); kws...) -## selectperm: the permutation to sort the first k elements of an array ## +## partialsortperm: the permutation to sort the first k elements of an array ## """ - selectperm(v, k, [alg=,] [by=,] [lt=,] [rev=false]) + partialsortperm(v, k, [alg=,] [by=,] [lt=,] [rev=false]) Return a partial permutation of the vector `v`, according to the order specified by `by`, `lt` and `rev`, so that `v[output]` returns the first `k` (or range of adjacent values @@ -681,22 +682,22 @@ from [`select`](@ref) in that it returns a vector of `k` elements instead of jus element. Also note that this is equivalent to, but more efficient than, calling `sortperm(...)[k]`. """ -selectperm(v::AbstractVector, k::Union{Integer,OrdinalRange}; kwargs...) = - selectperm!(similar(Vector{eltype(k)}, indices(v,1)), v, k; kwargs..., initialized=false) +partialsortperm(v::AbstractVector, k::Union{Integer,OrdinalRange}; kwargs...) = + partialsortperm!(similar(Vector{eltype(k)}, indices(v,1)), v, k; kwargs..., initialized=false) """ - selectperm!(ix, v, k, [alg=,] [by=,] [lt=,] [rev=false,] [initialized=false]) + partialsortperm!(ix, v, k, [alg=,] [by=,] [lt=,] [rev=false,] [initialized=false]) -Like [`selectperm`](@ref), but accepts a preallocated index vector `ix`. If `initialized` is `false` +Like [`partialsortperm`](@ref), but accepts a preallocated index vector `ix`. If `initialized` is `false` (the default), ix is initialized to contain the values `1:length(ix)`. """ -function selectperm!(ix::AbstractVector{<:Integer}, v::AbstractVector, - k::Union{Int, OrdinalRange}; - lt::Function=isless, - by::Function=identity, - rev::Bool=false, - order::Ordering=Forward, - initialized::Bool=false) +function partialsortperm!(ix::AbstractVector{<:Integer}, v::AbstractVector, + k::Union{Int, OrdinalRange}; + lt::Function=isless, + by::Function=identity, + rev::Bool=false, + order::Ordering=Forward, + initialized::Bool=false) if !initialized @inbounds for i = indices(ix,1) ix[i] = i diff --git a/base/statistics.jl b/base/statistics.jl index b15669fc9bf90..226a8661a85f1 100644 --- a/base/statistics.jl +++ b/base/statistics.jl @@ -599,9 +599,9 @@ function median!(v::AbstractVector) n = length(inds) mid = div(first(inds)+last(inds),2) if isodd(n) - return middle(select!(v,mid)) + return middle(partialsort!(v,mid)) else - m = select!(v, mid:mid+1) + m = partialsort!(v, mid:mid+1) return middle(m[1], m[2]) end end diff --git a/doc/src/stdlib/sort.md b/doc/src/stdlib/sort.md index 65bfd0e70bbc9..ffb4725b6bcf6 100644 --- a/doc/src/stdlib/sort.md +++ b/doc/src/stdlib/sort.md @@ -122,10 +122,10 @@ Base.issorted Base.Sort.searchsorted Base.Sort.searchsortedfirst Base.Sort.searchsortedlast -Base.Sort.select! -Base.Sort.select -Base.Sort.selectperm -Base.Sort.selectperm! +Base.Sort.partialsort! +Base.Sort.partialsort +Base.Sort.partialsortperm +Base.Sort.partialsortperm! ``` ## Sorting Algorithms diff --git a/test/core.jl b/test/core.jl index 08c3986562915..d9b80655bd0ae 100644 --- a/test/core.jl +++ b/test/core.jl @@ -3765,11 +3765,11 @@ end module M15455 function rpm_provides(r::T) where T - push!([], select(r,T)) + push!([], partialsort(r,T)) end -select(a,b) = 0 +partialsort(a,b) = 0 end -@test M15455.select(1,2)==0 +@test M15455.partialsort(1,2)==0 # check that medium-sized array is 64-byte aligned (#15139) @test Int(pointer(Vector{Float64}(1024))) % 64 == 0 diff --git a/test/ranges.jl b/test/ranges.jl index 19e082e97819b..e34fbad8628d7 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -314,7 +314,7 @@ end @test sort!(UnitRange(1,2)) == UnitRange(1,2) @test sort(1:10, rev=true) == collect(10:-1:1) @test sort(-3:3, by=abs) == [0,-1,1,-2,2,-3,3] -@test select(1:10, 4) == 4 +@test partialsort(1:10, 4) == 4 @test 0 in UInt(0):100:typemax(UInt) @test last(UInt(0):100:typemax(UInt)) in UInt(0):100:typemax(UInt) diff --git a/test/sorting.jl b/test/sorting.jl index 28d7020d25458..cc5545e353d04 100644 --- a/test/sorting.jl +++ b/test/sorting.jl @@ -17,16 +17,16 @@ end @test !issorted([2,3,1]) @test issorted([1,2,3]) @test reverse([2,3,1]) == [1,3,2] -@test select([3,6,30,1,9],3) == 6 -@test select([3,6,30,1,9],3:4) == [6,9] -@test selectperm([3,6,30,1,9], 3:4) == [2,5] -@test selectperm!(collect(1:5), [3,6,30,1,9], 3:4) == [2,5] +@test partialsort([3,6,30,1,9],3) == 6 +@test partialsort([3,6,30,1,9],3:4) == [6,9] +@test partialsortperm([3,6,30,1,9], 3:4) == [2,5] +@test partialsortperm!(collect(1:5), [3,6,30,1,9], 3:4) == [2,5] let a=[1:10;] for r in Any[2:4, 1:2, 10:10, 4:2, 2:1, 4:-1:2, 2:-1:1, 10:-1:10, 4:1:3, 1:2:8, 10:-3:1] - @test select(a, r) == [r;] - @test selectperm(a, r) == [r;] - @test select(a, r, rev=true) == (11 .- [r;]) - @test selectperm(a, r, rev=true) == (11 .- [r;]) + @test partialsort(a, r) == [r;] + @test partialsortperm(a, r) == [r;] + @test partialsort(a, r, rev=true) == (11 .- [r;]) + @test partialsortperm(a, r, rev=true) == (11 .- [r;]) end end @test sum(randperm(6)) == 21 @@ -204,10 +204,10 @@ let alg = PartialQuickSort(div(length(a), 10)) @test !issorted(d, rev=true) end -@test select([3,6,30,1,9], 2, rev=true) == 9 -@test select([3,6,30,1,9], 2, by=x->1/x) == 9 -@test selectperm([3,6,30,1,9], 2, rev=true) == 5 -@test selectperm([3,6,30,1,9], 2, by=x->1/x) == 5 +@test partialsort([3,6,30,1,9], 2, rev=true) == 9 +@test partialsort([3,6,30,1,9], 2, by=x->1/x) == 9 +@test partialsortperm([3,6,30,1,9], 2, rev=true) == 5 +@test partialsortperm([3,6,30,1,9], 2, by=x->1/x) == 5 ## more advanced sorting tests ## From 8150f666fd76a6098e12f235ea8fbf7246867e9e Mon Sep 17 00:00:00 2001 From: Milan Bouchet-Valat Date: Mon, 31 Jul 2017 16:13:07 +0200 Subject: [PATCH 175/324] Make partialsort!() and partialsortperm!() return a view rather than a copy Returning a copy (partially) defeats the purpose of these functions, which is to avoid allocations. --- base/deprecated.jl | 8 ++++---- base/sort.jl | 14 ++++++++++++-- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/base/deprecated.jl b/base/deprecated.jl index 238fab1ae71b8..b7d6ff7e613f5 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1743,10 +1743,10 @@ end @deprecate IOContext(io::IO, key, value) IOContext(io, key=>value) # issue #22791 -@deprecate_binding select partialsort -@deprecate_binding select! partialsort! -@deprecate_binding selectperm partialsortperm -@deprecate_binding selectperm! partialsortperm! +@deprecate select partialsort +@deprecate select! partialsort! +@deprecate selectperm partialsortperm +@deprecate selectperm! partialsortperm! # END 0.7 deprecations diff --git a/base/sort.jl b/base/sort.jl index 7930f856f2f92..16910be31f2db 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -85,7 +85,12 @@ issorted(itr; function partialsort!(v::AbstractVector, k::Union{Int,OrdinalRange}, o::Ordering) inds = indices(v, 1) sort!(v, first(inds), last(inds), PartialQuickSort(k), o) - v[k] + + if k isa Integer + return v[k] + else + return view(v, k) + end end """ @@ -706,7 +711,12 @@ function partialsortperm!(ix::AbstractVector{<:Integer}, v::AbstractVector, # do partial quicksort sort!(ix, PartialQuickSort(k), Perm(ord(lt, by, rev, order), v)) - return ix[k] + + if k isa Integer + return ix[k] + else + return view(ix, k) + end end ## sortperm: the permutation to sort an array ## From 7a54a2f827f78940ef21324499b4667493222e5e Mon Sep 17 00:00:00 2001 From: Milan Bouchet-Valat Date: Fri, 25 Aug 2017 17:43:27 +0200 Subject: [PATCH 176/324] Fix docstrings for partialsortperm() and partialsortperm!() partialsort(x, k::Integer), partialsortperm(x, k::Integer) and partialsortperm!(x, k::Integer) all return a single value. Also add missing backticks. --- base/sort.jl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/base/sort.jl b/base/sort.jl index 16910be31f2db..cb28e79d7de52 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -680,12 +680,12 @@ sort(v::AbstractVector; kws...) = sort!(copymutable(v); kws...) Return a partial permutation of the vector `v`, according to the order specified by `by`, `lt` and `rev`, so that `v[output]` returns the first `k` (or range of adjacent values -if `k` is a range) values of a fully sorted version of `v`. If `k` is a single index -(Integer), an array of the first `k` indices is returned; if `k` is a range, an array of -those indices is returned. Note that the handling of integer values for `k` is different -from [`select`](@ref) in that it returns a vector of `k` elements instead of just the `k` th -element. Also note that this is equivalent to, but more efficient than, calling -`sortperm(...)[k]`. +if `k` is a range) values of a fully sorted version of `v`. If `k` is a single index, +the index in `v` of the value which would be sorted at position `k` is returned; +if `k` is a range, an array with the indices in `v` of the values which would be sorted in +these positions is returned. + +Note that this is equivalent to, but more efficient than, calling `sortperm(...)[k]`. """ partialsortperm(v::AbstractVector, k::Union{Integer,OrdinalRange}; kwargs...) = partialsortperm!(similar(Vector{eltype(k)}, indices(v,1)), v, k; kwargs..., initialized=false) @@ -694,7 +694,7 @@ partialsortperm(v::AbstractVector, k::Union{Integer,OrdinalRange}; kwargs...) = partialsortperm!(ix, v, k, [alg=,] [by=,] [lt=,] [rev=false,] [initialized=false]) Like [`partialsortperm`](@ref), but accepts a preallocated index vector `ix`. If `initialized` is `false` -(the default), ix is initialized to contain the values `1:length(ix)`. +(the default), `ix` is initialized to contain the values `1:length(ix)`. """ function partialsortperm!(ix::AbstractVector{<:Integer}, v::AbstractVector, k::Union{Int, OrdinalRange}; From 77c24390de0b6cdb864070eb84b850512ce4a88a Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Sun, 27 Aug 2017 13:03:12 -0700 Subject: [PATCH 177/324] Even more docs (#23420) * Even more docs --- base/libgit2/consts.jl | 24 +++++++++++++++++++++++- base/libgit2/types.jl | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/base/libgit2/consts.jl b/base/libgit2/consts.jl index eb7e31bf71de4..9e4c56466184f 100644 --- a/base/libgit2/consts.jl +++ b/base/libgit2/consts.jl @@ -149,6 +149,17 @@ module Consts const INDEX_STAGE_ANY = Cint(-1) # merge + """ Option flags for git merge. + * `MERGE_FIND_RENAMES`: detect if a file has been renamed between the common + ancestor and the "ours" or "theirs" side of the merge. Allows merges where + a file has been renamed. + * `MERGE_FAIL_ON_CONFLICT`: exit immediately if a conflict is found rather + than trying to resolve it. + * `MERGE_SKIP_REUC`: do not write the REUC extension on the index resulting + from the merge. + * `MERGE_NO_RECURSIVE`: if the commits being merged have multiple merge bases, + use the first one, rather than trying to recursively merge the bases. + """ @enum(GIT_MERGE, MERGE_FIND_RENAMES = 1 << 0, MERGE_FAIL_ON_CONFLICT = 1 << 1, MERGE_SKIP_REUC = 1 << 2, @@ -163,7 +174,18 @@ module Consts MERGE_FILE_IGNORE_WHITESPACE_EOL = 1 << 5, # Ignore whitespace at end of line MERGE_FILE_DIFF_PATIENCE = 1 << 6, # Use the "patience diff" algorithm MERGE_FILE_DIFF_MINIMAL = 1 << 7) # Take extra time to find minimal diff - + """ Option flags for git merge file favoritism. + * `MERGE_FILE_FAVOR_NORMAL`: if both sides of the merge have changes to a section, + make a note of the conflict in the index which `git checkout` will use to create + a merge file, which the user can then reference to resolve the conflicts. This is + the default. + * `MERGE_FILE_FAVOR_OURS`: if both sides of the merge have changes to a section, + use the version in the "ours" side of the merge in the index. + * `MERGE_FILE_FAVOR_THEIRS`: if both sides of the merge have changes to a section, + use the version in the "theirs" side of the merge in the index. + * `MERGE_FILE_FAVOR_UNION`: if both sides of the merge have changes to a section, + include each unique line from both sides in the file which is put into the index. + """ @enum(GIT_MERGE_FILE_FAVOR, MERGE_FILE_FAVOR_NORMAL = 0, MERGE_FILE_FAVOR_OURS = 1, MERGE_FILE_FAVOR_THEIRS = 2, diff --git a/base/libgit2/types.jl b/base/libgit2/types.jl index 30066efa64697..874b93f919ecd 100644 --- a/base/libgit2/types.jl +++ b/base/libgit2/types.jl @@ -462,6 +462,44 @@ end LibGit2.MergeOptions Matches the [`git_merge_options`](https://libgit2.github.com/libgit2/#HEAD/type/git_merge_options) struct. + +The fields represent: + * `version`: version of the struct in use, in case this changes later. For now, always `1`. + * `flags`: an `enum` for flags describing merge behavior. + Defined in [`git_merge_flag_t`](https://github.com/libgit2/libgit2/blob/HEAD/include/git2/merge.h#L95). + The corresponding Julia enum is `GIT_MERGE` and has values: + - `MERGE_FIND_RENAMES`: detect if a file has been renamed between the common + ancestor and the "ours" or "theirs" side of the merge. Allows merges where + a file has been renamed. + - `MERGE_FAIL_ON_CONFLICT`: exit immediately if a conflict is found rather + than trying to resolve it. + - `MERGE_SKIP_REUC`: do not write the REUC extension on the index resulting + from the merge. + - `MERGE_NO_RECURSIVE`: if the commits being merged have multiple merge bases, + use the first one, rather than trying to recursively merge the bases. + * `rename_threshold`: how similar two files must to consider one a rename of the other. + This is an integer that sets the percentage similarity. The default is 50. + * `target_limit`: the maximum number of files to compare with to look for renames. + The default is 200. + * `metric`: optional custom function to use to determine the similarity between two + files for rename detection. + * `recursion_limit`: the upper limit on the number of merges of common ancestors to + perform to try to build a new virtual merge base for the merge. The default is no + limit. This field is only present on libgit2 versions newer than 0.24.0. + * `default_driver`: the merge driver to use if both sides have changed. This field + is only present on libgit2 versions newer than 0.25.0. + * `file_favor`: how to handle conflicting file contents for the `text` driver. + - `MERGE_FILE_FAVOR_NORMAL`: if both sides of the merge have changes to a section, + make a note of the conflict in the index which `git checkout` will use to create + a merge file, which the user can then reference to resolve the conflicts. This is + the default. + - `MERGE_FILE_FAVOR_OURS`: if both sides of the merge have changes to a section, + use the version in the "ours" side of the merge in the index. + - `MERGE_FILE_FAVOR_THEIRS`: if both sides of the merge have changes to a section, + use the version in the "theirs" side of the merge in the index. + - `MERGE_FILE_FAVOR_UNION`: if both sides of the merge have changes to a section, + include each unique line from both sides in the file which is put into the index. + * `file_flags`: guidelines for merging files. """ @kwdef struct MergeOptions version::Cuint = 1 From e545c657d5ad88bc77ade254e96499977905e51c Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Sun, 27 Aug 2017 13:06:04 -0700 Subject: [PATCH 178/324] Doc merge_analysis and some enums (#23435) * Doc merge_analysis and some enums --- base/libgit2/consts.jl | 22 ++++++++++++++++++++-- base/libgit2/merge.jl | 32 +++++++++++++++++++++++++++++++- doc/src/devdocs/libgit2.md | 1 + 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/base/libgit2/consts.jl b/base/libgit2/consts.jl index 9e4c56466184f..3528e4e268408 100644 --- a/base/libgit2/consts.jl +++ b/base/libgit2/consts.jl @@ -190,11 +190,29 @@ module Consts MERGE_FILE_FAVOR_OURS = 1, MERGE_FILE_FAVOR_THEIRS = 2, MERGE_FILE_FAVOR_UNION = 3) - + """ The user's instructions for how to perform a possible merge. + * `MERGE_PREFERENCE_NONE`: the user has no preference. + * `MERGE_PREFERENCE_NO_FASTFORWARD`: do not allow any fast-forward merges. + * `MERGE_PREFERENCE_FASTFORWARD_ONLY`: allow only fast-forward merges and no + other type (which may introduce conflicts). + """ @enum(GIT_MERGE_PREFERENCE, MERGE_PREFERENCE_NONE = 0, MERGE_PREFERENCE_NO_FASTFORWARD = 1, MERGE_PREFERENCE_FASTFORWARD_ONLY = 2) - + """ Result of analysis on merge possibilities. + * `MERGE_ANALYSIS_NONE`: it is not possible to merge the elements of the input commits. + * `MERGE_ANALYSIS_NORMAL`: a regular merge, when HEAD and the commits that the + user wishes to merge have all diverged from a common ancestor. In this case the + changes have to be resolved and conflicts may occur. + * `MERGE_ANALYSIS_UP_TO_DATE`: all the input commits the user wishes to merge can + be reached from HEAD, so no merge needs to be performed. + * `MERGE_ANALYSIS_FASTFORWARD`: the input commit is a descendant of HEAD and so no + merge needs to be performed - instead, the user can simply checkout the + input commit(s). + * `MERGE_ANALYSIS_UNBORN`: the HEAD of the repository refers to a commit which does not + exist. It is not possible to merge, but it may be possible to checkout the input + commits. + """ @enum(GIT_MERGE_ANALYSIS, MERGE_ANALYSIS_NONE = 0, MERGE_ANALYSIS_NORMAL = 1 << 0, MERGE_ANALYSIS_UP_TO_DATE = 1 << 1, diff --git a/base/libgit2/merge.jl b/base/libgit2/merge.jl index f0439b9b71e45..42e3283887ca8 100644 --- a/base/libgit2/merge.jl +++ b/base/libgit2/merge.jl @@ -47,6 +47,34 @@ function GitHash(ann::GitAnnotated) unsafe_load(ccall((:git_annotated_commit_id, :libgit2), Ptr{GitHash}, (Ptr{Void},), ann.ptr)) end +""" + merge_analysis(repo::GitRepo, anns::Vector{GitAnnotated}) -> analysis, preference + +Run analysis on the branches pointed to by the annotated branch tips `anns` and +determine under what circumstances they can be merged. For instance, if `anns[1]` +is simply an ancestor of `ann[2]`, then `merge_analysis` will report that a +fast-forward merge is possible. + +`merge_analysis` returns two outputs. `analysis` has several possible values: + * `MERGE_ANALYSIS_NONE`: it is not possible to merge the elements of `anns`. + * `MERGE_ANALYSIS_NORMAL`: a regular merge, when HEAD and the commits that the + user wishes to merge have all diverged from a common ancestor. In this case the + changes have to be resolved and conflicts may occur. + * `MERGE_ANALYSIS_UP_TO_DATE`: all the input commits the user wishes to merge can + be reached from HEAD, so no merge needs to be performed. + * `MERGE_ANALYSIS_FASTFORWARD`: the input commit is a descendant of HEAD and so no + merge needs to be performed - instead, the user can simply checkout the + input commit(s). + * `MERGE_ANALYSIS_UNBORN`: the HEAD of the repository refers to a commit which does not + exist. It is not possible to merge, but it may be possible to checkout the input + commits. +`preference` also has several possible values: + * `MERGE_PREFERENCE_NONE`: the user has no preference. + * `MERGE_PREFERENCE_NO_FASTFORWARD`: do not allow any fast-forward merges. + * `MERGE_PREFERENCE_FASTFORWARD_ONLY`: allow only fast-forward merges and no + other type (which may introduce conflicts). +`preference` can be controlled through the repository or global git configuration. +""" function merge_analysis(repo::GitRepo, anns::Vector{GitAnnotated}) analysis = Ref{Cint}(0) preference = Ref{Cint}(0) @@ -61,7 +89,9 @@ end """ ffmerge!(repo::GitRepo, ann::GitAnnotated) -Fastforward merge changes into current head +Fastforward merge changes into current HEAD. This is only possible if the commit +referred to by `ann` is descended from the current HEAD (e.g. if pulling changes +from a remote branch which is simply ahead of the local branch tip). """ function ffmerge!(repo::GitRepo, ann::GitAnnotated) cmt = GitCommit(repo, GitHash(ann)) diff --git a/doc/src/devdocs/libgit2.md b/doc/src/devdocs/libgit2.md index 2e02a0f7325b9..aed65dbd79cfa 100644 --- a/doc/src/devdocs/libgit2.md +++ b/doc/src/devdocs/libgit2.md @@ -102,6 +102,7 @@ Base.LibGit2.map(::Function, ::Base.LibGit2.GitRevWalker; ::Base.LibGit2.GitHash Base.LibGit2.mirror_callback Base.LibGit2.mirror_cb Base.LibGit2.message +Base.LibGit2.merge_analysis Base.LibGit2.name Base.LibGit2.need_update Base.LibGit2.objtype From 9f00d84f9995240ad13b8c1360d4e34c44b656b6 Mon Sep 17 00:00:00 2001 From: Alex Arslan Date: Sun, 27 Aug 2017 13:28:39 -0700 Subject: [PATCH 179/324] Add docs for git oid methods (#23460) --- base/libgit2/oid.jl | 37 +++++++++++++++++++++++++++++++++++++ doc/src/devdocs/libgit2.md | 3 +++ 2 files changed, 40 insertions(+) diff --git a/base/libgit2/oid.jl b/base/libgit2/oid.jl index 3e369a1358349..ad84253e75ce0 100644 --- a/base/libgit2/oid.jl +++ b/base/libgit2/oid.jl @@ -1,5 +1,12 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license +""" + GitHash(ptr::Ptr{UInt8}) + +Construct a `GitHash` from a pointer to `UInt8` data containing the bytes of the +SHA-1 hash. The constructor throws an error if the pointer is null, i.e. equal to +`C_NULL`. +""" function GitHash(ptr::Ptr{UInt8}) if ptr == C_NULL throw(ArgumentError("NULL pointer passed to GitHash() constructor")) @@ -38,6 +45,11 @@ function GitHash(id::AbstractString) return oid_ptr[] end +""" + GitShortHash(buf::Buffer) + +Construct a `GitShortHash` from the data stored in the given [`Buffer`](@ref). +""" function GitShortHash(buf::Buffer) oid_ptr = Ref{GitHash}() @check ccall((:git_oid_fromstrn, :libgit2), Cint, @@ -59,6 +71,21 @@ function GitShortHash(id::AbstractString) GitShortHash(oid_ptr[], len) end +""" + @githash_str -> AbstractGitHash + +Construct a git hash object from the given string, returning a `GitShortHash` if +the string is shorter than $OID_HEXSZ hexadecimal digits, otherwise a `GitHash`. + +# Examples +```jldoctest +julia> LibGit2.githash"d114feb74ce633" +GitShortHash("d114feb74ce633") + +julia> LibGit2.githash"d114feb74ce63307afe878a5228ad014e0289a85" +GitHash("d114feb74ce63307afe878a5228ad014e0289a85") +``` +""" macro githash_str(id) bstr = String(id) if sizeof(bstr) < OID_HEXSZ @@ -128,6 +155,11 @@ end Base.hex(id::GitHash) = join([hex(i,2) for i in id.val]) Base.hex(id::GitShortHash) = hex(id.hash)[1:id.len] +""" + raw(id::GitHash) -> Vector{UInt8} + +Obtain the raw bytes of the [`GitHash`](@ref) as a vector of length $OID_RAWSZ. +""" raw(id::GitHash) = collect(id.val) Base.string(id::AbstractGitHash) = hex(id) @@ -156,6 +188,11 @@ Base.cmp(id1::GitShortHash, id2::GitHash) = cmp(id1, GitShortHash(id2, OID_HEXSZ ==(id1::GitHash, id2::GitHash) = cmp(id1, id2) == 0 Base.isless(id1::AbstractGitHash, id2::AbstractGitHash) = cmp(id1, id2) < 0 +""" + iszero(id::GitHash) -> Bool + +Determine whether all hexadecimal digits of the given [`GitHash`](@ref) are zero. +""" function iszero(id::GitHash) for i in 1:OID_RAWSZ id.val[i] != zero(UInt8) && return false diff --git a/doc/src/devdocs/libgit2.md b/doc/src/devdocs/libgit2.md index aed65dbd79cfa..b9b279d6a0eba 100644 --- a/doc/src/devdocs/libgit2.md +++ b/doc/src/devdocs/libgit2.md @@ -85,6 +85,7 @@ Base.LibGit2.fullname Base.LibGit2.features Base.LibGit2.get_creds! Base.LibGit2.gitdir +Base.LibGit2.@githash_str Base.LibGit2.head Base.LibGit2.head! Base.LibGit2.head_oid @@ -97,6 +98,7 @@ Base.LibGit2.isdiff Base.LibGit2.isdirty Base.LibGit2.isorphan Base.LibGit2.isset +Base.LibGit2.iszero Base.LibGit2.lookup_branch Base.LibGit2.map(::Function, ::Base.LibGit2.GitRevWalker; ::Base.LibGit2.GitHash, ::Cint, ::Bool) Base.LibGit2.mirror_callback @@ -113,6 +115,7 @@ Base.LibGit2.push Base.LibGit2.push!(::Base.LibGit2.GitRevWalker, ::Base.LibGit2.GitHash) Base.LibGit2.push_head! Base.LibGit2.push_refspecs +Base.LibGit2.raw Base.LibGit2.read_tree! Base.LibGit2.rebase! Base.LibGit2.ref_list From e134a36853be20138dcc8de5ab9fadc4da302b53 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Sun, 27 Aug 2017 22:22:00 -0400 Subject: [PATCH 180/324] fix bug in showing `UnionAll` introduced by #23411 --- base/show.jl | 5 +++++ test/show.jl | 1 + 2 files changed, 6 insertions(+) diff --git a/base/show.jl b/base/show.jl index db41a94371ab3..88ee7974bb20d 100644 --- a/base/show.jl +++ b/base/show.jl @@ -242,6 +242,11 @@ end show(io::IO, x::DataType) = show_datatype(io, x) function show_type_name(io::IO, tn::TypeName) + if tn === UnionAll.name + # by coincidence, `typeof(Type)` is a valid representation of the UnionAll type. + # intercept this case and print `UnionAll` instead. + return print(io, "UnionAll") + end globname = isdefined(tn, :mt) ? tn.mt.name : nothing globfunc = false if globname !== nothing diff --git a/test/show.jl b/test/show.jl index 12786b6ecab3e..8f3b3084fc1a5 100644 --- a/test/show.jl +++ b/test/show.jl @@ -968,6 +968,7 @@ end mkclosure = x->y->x+y clo = mkclosure(10) @test stringmime("text/plain", clo) == "$(typeof(clo).name.mt.name) (generic function with 1 method)" + @test repr(UnionAll) == "UnionAll" end let x = TypeVar(:_), y = TypeVar(:_) From f9527b7ee0cb07384312f32871cbcb960ed72c93 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Sun, 27 Aug 2017 23:45:11 -0400 Subject: [PATCH 181/324] fix bug in printing generator expressions with n-d ranges --- base/show.jl | 12 ++++-------- test/show.jl | 2 ++ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/base/show.jl b/base/show.jl index db41a94371ab3..3469858f75520 100644 --- a/base/show.jl +++ b/base/show.jl @@ -776,23 +776,19 @@ function show_generator(io, ex, indent) fg = ex ranges = Any[] while isa(fg, Expr) && fg.head === :flatten - push!(ranges, fg.args[1].args[2]) + push!(ranges, fg.args[1].args[2:end]) fg = fg.args[1].args[1] end - push!(ranges, fg.args[2]) + push!(ranges, fg.args[2:end]) show_unquoted(io, fg.args[1], indent) for r in ranges print(io, " for ") - show_unquoted(io, r, indent) + show_list(io, r, ", ", indent) end else show_unquoted(io, ex.args[1], indent) print(io, " for ") - show_unquoted(io, ex.args[2], indent) - for i = 3:length(ex.args) - print(io, ", ") - show_unquoted(io, ex.args[i], indent) - end + show_list(io, ex.args[2:end], ", ", indent) end end diff --git a/test/show.jl b/test/show.jl index 12786b6ecab3e..ab74e65688426 100644 --- a/test/show.jl +++ b/test/show.jl @@ -759,6 +759,8 @@ end @test repr(:([x for x = y])) == ":([x for x = y])" @test repr(:([x for x = y if z])) == ":([x for x = y if z])" @test repr(:(z for z = 1:5, y = 1:5)) == ":((z for z = 1:5, y = 1:5))" +@test_repr "(x for i in a, b in c)" +@test_repr "(x for a in b, c in d for e in f)" for op in (:(.=), :(.+=), :(.&=)) @test repr(parse("x $op y")) == ":(x $op y)" From 869112d87a449e5a976bc47920536f9ef796b853 Mon Sep 17 00:00:00 2001 From: "femtocleaner[bot]" Date: Mon, 28 Aug 2017 16:47:30 +0000 Subject: [PATCH 182/324] Fix deprecations --- base/boot.jl | 12 +- base/deprecated.jl | 6 +- base/gmp.jl | 2 +- base/linalg/blas.jl | 254 ++-- base/linalg/lapack.jl | 1230 +++++++++--------- base/random/generation.jl | 8 +- base/sparse/cholmod.jl | 2 +- base/sparse/umfpack.jl | 8 +- doc/src/manual/calling-c-and-fortran-code.md | 4 +- doc/src/manual/methods.md | 10 +- test/TestHelpers.jl | 4 +- test/ccall.jl | 2 +- test/dimensionful.jl | 2 +- test/inference.jl | 2 +- test/perf/array/indexing.jl | 10 +- test/reflection.jl | 2 +- test/replutil.jl | 2 +- 17 files changed, 780 insertions(+), 780 deletions(-) diff --git a/base/boot.jl b/base/boot.jl index 222a9766ab2f4..dd5a425add0a9 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -331,17 +331,17 @@ VecElement(arg::T) where {T} = VecElement{T}(arg) # used by lowering of splicing unquote splicedexpr(hd::Symbol, args::Array{Any,1}) = (e=Expr(hd); e.args=args; e) -_new(typ::Symbol, argty::Symbol) = eval(Core, :((::Type{$typ})(@nospecialize n::$argty) = $(Expr(:new, typ, :n)))) +_new(typ::Symbol, argty::Symbol) = eval(Core, :($typ(@nospecialize n::$argty) = $(Expr(:new, typ, :n)))) _new(:LabelNode, :Int) _new(:GotoNode, :Int) _new(:NewvarNode, :SlotNumber) _new(:QuoteNode, :Any) _new(:SSAValue, :Int) -eval(Core, :((::Type{LineNumberNode})(l::Int) = $(Expr(:new, :LineNumberNode, :l, nothing)))) -eval(Core, :((::Type{LineNumberNode})(l::Int, @nospecialize(f)) = $(Expr(:new, :LineNumberNode, :l, :f)))) -eval(Core, :((::Type{GlobalRef})(m::Module, s::Symbol) = $(Expr(:new, :GlobalRef, :m, :s)))) -eval(Core, :((::Type{SlotNumber})(n::Int) = $(Expr(:new, :SlotNumber, :n)))) -eval(Core, :((::Type{TypedSlot})(n::Int, @nospecialize(t)) = $(Expr(:new, :TypedSlot, :n, :t)))) +eval(Core, :(LineNumberNode(l::Int) = $(Expr(:new, :LineNumberNode, :l, nothing)))) +eval(Core, :(LineNumberNode(l::Int, @nospecialize(f)) = $(Expr(:new, :LineNumberNode, :l, :f)))) +eval(Core, :(GlobalRef(m::Module, s::Symbol) = $(Expr(:new, :GlobalRef, :m, :s)))) +eval(Core, :(SlotNumber(n::Int) = $(Expr(:new, :SlotNumber, :n)))) +eval(Core, :(TypedSlot(n::Int, @nospecialize(t)) = $(Expr(:new, :TypedSlot, :n, :t)))) Module(name::Symbol=:anonymous, std_imports::Bool=true) = ccall(:jl_f_new_module, Ref{Module}, (Any, Bool), name, std_imports) diff --git a/base/deprecated.jl b/base/deprecated.jl index 8338f8a1c65b1..164a577cf3a13 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -556,7 +556,7 @@ function gen_broadcast_function_sparse(genbody::Function, f::Function, is_first_ body = genbody(f, is_first_sparse) @eval let local _F_ - function _F_{Tv,Ti}(B::SparseMatrixCSC{Tv,Ti}, A_1, A_2) + function _F_(B::SparseMatrixCSC{Tv,Ti}, A_1, A_2) where {Tv,Ti} $body end _F_ @@ -1263,11 +1263,11 @@ end @noinline zero_arg_matrix_constructor(prefix::String) = depwarn("$prefix() is deprecated, use $prefix(0, 0) instead.", :zero_arg_matrix_constructor) -function (::Type{Matrix{T}})() where T +function Matrix{T}() where T zero_arg_matrix_constructor("Matrix{T}") return Matrix{T}(0, 0) end -function (::Type{Matrix})() +function Matrix() zero_arg_matrix_constructor("Matrix") return Matrix(0, 0) end diff --git a/base/gmp.jl b/base/gmp.jl index 17a7f6602b27f..9abfc34d44610 100644 --- a/base/gmp.jl +++ b/base/gmp.jl @@ -346,7 +346,7 @@ function convert(::Type{T}, x::BigInt) where T<:Signed end -(::Type{Float64})(n::BigInt, ::RoundingMode{:ToZero}) = MPZ.get_d(n) +Float64(n::BigInt, ::RoundingMode{:ToZero}) = MPZ.get_d(n) function (::Type{T})(n::BigInt, ::RoundingMode{:ToZero}) where T<:Union{Float16,Float32} T(Float64(n,RoundToZero),RoundToZero) diff --git a/base/linalg/blas.jl b/base/linalg/blas.jl index 530413a0edcf2..0ed249b91a9d1 100644 --- a/base/linalg/blas.jl +++ b/base/linalg/blas.jl @@ -172,8 +172,8 @@ for (fname, elty) in ((:dcopy_,:Float64), # SUBROUTINE DCOPY(N,DX,INCX,DY,INCY) function blascopy!(n::Integer, DX::Union{Ptr{$elty},StridedArray{$elty}}, incx::Integer, DY::Union{Ptr{$elty},StridedArray{$elty}}, incy::Integer) ccall((@blasfunc($fname), libblas), Void, - (Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &n, DX, &incx, DY, &incy) + (Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}), + n, DX, incx, DY, incy) DY end end @@ -203,8 +203,8 @@ for (fname, elty) in ((:dscal_,:Float64), # SUBROUTINE DSCAL(N,DA,DX,INCX) function scal!(n::Integer, DA::$elty, DX::Union{Ptr{$elty},DenseArray{$elty}}, incx::Integer) ccall((@blasfunc($fname), libblas), Void, - (Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}), - &n, &DA, DX, &incx) + (Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}), + n, DA, DX, incx) DX end end @@ -267,8 +267,8 @@ for (fname, elty) in ((:ddot_,:Float64), # DOUBLE PRECISION DX(*),DY(*) function dot(n::Integer, DX::Union{Ptr{$elty},DenseArray{$elty}}, incx::Integer, DY::Union{Ptr{$elty},DenseArray{$elty}}, incy::Integer) ccall((@blasfunc($fname), libblas), $elty, - (Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &n, DX, &incx, DY, &incy) + (Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}), + n, DX, incx, DY, incy) end end end @@ -359,8 +359,8 @@ for (fname, elty, ret_type) in ((:dnrm2_,:Float64,:Float64), # SUBROUTINE DNRM2(N,X,INCX) function nrm2(n::Integer, X::Union{Ptr{$elty},DenseArray{$elty}}, incx::Integer) ccall((@blasfunc($fname), libblas), $ret_type, - (Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &n, X, &incx) + (Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}), + n, X, incx) end end end @@ -392,8 +392,8 @@ for (fname, elty, ret_type) in ((:dasum_,:Float64,:Float64), # SUBROUTINE ASUM(N, X, INCX) function asum(n::Integer, X::Union{Ptr{$elty},DenseArray{$elty}}, incx::Integer) ccall((@blasfunc($fname), libblas), $ret_type, - (Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &n, X, &incx) + (Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}), + n, X, incx) end end end @@ -435,8 +435,8 @@ for (fname, elty) in ((:daxpy_,:Float64), # DOUBLE PRECISION DX(*),DY(*) function axpy!(n::Integer, alpha::($elty), dx::Union{Ptr{$elty}, DenseArray{$elty}}, incx::Integer, dy::Union{Ptr{$elty}, DenseArray{$elty}}, incy::Integer) ccall((@blasfunc($fname), libblas), Void, - (Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &n, &alpha, dx, &incx, dy, &incy) + (Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}), + n, alpha, dx, incx, dy, incy) dy end end @@ -521,8 +521,8 @@ for (fname, elty) in ((:idamax_,:Float64), @eval begin function iamax(n::Integer, dx::Union{Ptr{$elty}, DenseArray{$elty}}, incx::Integer) ccall((@blasfunc($fname), libblas),BlasInt, - (Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &n, dx, &incx) + (Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}), + n, dx, incx) end end end @@ -553,12 +553,12 @@ for (fname, elty) in ((:dgemv_,:Float64), throw(DimensionMismatch("A.' has dimensions $n, $m, X has length $(length(X)) and Y has length $(length(Y))")) end ccall((@blasfunc($fname), libblas), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}), - &trans, &size(A,1), &size(A,2), &alpha, - A, &max(1,stride(A,2)), X, &stride(X,1), - &beta, Y, &stride(Y,1)) + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{$elty}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ref{$elty}, Ptr{$elty}, Ref{BlasInt}), + trans, size(A,1), size(A,2), alpha, + A, max(1,stride(A,2)), X, stride(X,1), + beta, Y, stride(Y,1)) Y end function gemv(trans::Char, alpha::($elty), A::StridedMatrix{$elty}, X::StridedVector{$elty}) @@ -628,13 +628,13 @@ for (fname, elty) in ((:dgbmv_,:Float64), # DOUBLE PRECISION A(LDA,*),X(*),Y(*) function gbmv!(trans::Char, m::Integer, kl::Integer, ku::Integer, alpha::($elty), A::StridedMatrix{$elty}, x::StridedVector{$elty}, beta::($elty), y::StridedVector{$elty}) ccall((@blasfunc($fname), libblas), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, - Ptr{BlasInt}), - &trans, &m, &size(A,2), &kl, - &ku, &alpha, A, &max(1,stride(A,2)), - x, &stride(x,1), &beta, y, &stride(y,1)) + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, + Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, + Ref{BlasInt}), + trans, m, size(A,2), kl, + ku, alpha, A, max(1,stride(A,2)), + x, stride(x,1), beta, y, stride(y,1)) y end function gbmv(trans::Char, m::Integer, kl::Integer, ku::Integer, alpha::($elty), A::StridedMatrix{$elty}, x::StridedVector{$elty}) @@ -684,12 +684,12 @@ for (fname, elty, lib) in ((:dsymv_,:Float64,libblas), throw(DimensionMismatch("A has size $(size(A)), and y has length $(length(y))")) end ccall((@blasfunc($fname), $lib), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}), - &uplo, &n, &alpha, A, - &max(1,stride(A,2)), x, &stride(x,1), &beta, - y, &stride(y,1)) + (Ref{UInt8}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, + Ptr{$elty}, Ref{BlasInt}), + uplo, n, alpha, A, + max(1,stride(A,2)), x, stride(x,1), beta, + y, stride(y,1)) y end function symv(uplo::Char, alpha::($elty), A::StridedMatrix{$elty}, x::StridedVector{$elty}) @@ -737,12 +737,12 @@ for (fname, elty) in ((:zhemv_,:Complex128), incx = stride(x, 1) incy = stride(y, 1) ccall((@blasfunc($fname), libblas), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}), - &uplo, &n, &α, A, - &lda, x, &incx, &β, - y, &incy) + (Ref{UInt8}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, + Ptr{$elty}, Ref{BlasInt}), + uplo, n, α, A, + lda, x, incx, β, + y, incy) y end function hemv(uplo::Char, α::($elty), A::StridedMatrix{$elty}, x::StridedVector{$elty}) @@ -767,12 +767,12 @@ for (fname, elty) in ((:dsbmv_,:Float64), # DOUBLE PRECISION A(LDA,*),X(*),Y(*) function sbmv!(uplo::Char, k::Integer, alpha::($elty), A::StridedMatrix{$elty}, x::StridedVector{$elty}, beta::($elty), y::StridedVector{$elty}) ccall((@blasfunc($fname), libblas), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}), - &uplo, &size(A,2), &k, &alpha, - A, &max(1,stride(A,2)), x, &stride(x,1), - &beta, y, &stride(y,1)) + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{$elty}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ref{$elty}, Ptr{$elty}, Ref{BlasInt}), + uplo, size(A,2), k, alpha, + A, max(1,stride(A,2)), x, stride(x,1), + beta, y, stride(y,1)) y end function sbmv(uplo::Char, k::Integer, alpha::($elty), A::StridedMatrix{$elty}, x::StridedVector{$elty}) @@ -829,12 +829,12 @@ for (fname, elty) in ((:zhbmv_,:Complex128), # DOUBLE PRECISION A(LDA,*),X(*),Y(*) function hbmv!(uplo::Char, k::Integer, alpha::($elty), A::StridedMatrix{$elty}, x::StridedVector{$elty}, beta::($elty), y::StridedVector{$elty}) ccall((@blasfunc($fname), libblas), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}), - &uplo, &size(A,2), &k, &alpha, - A, &max(1,stride(A,2)), x, &stride(x,1), - &beta, y, &stride(y,1)) + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{$elty}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ref{$elty}, Ptr{$elty}, Ref{BlasInt}), + uplo, size(A,2), k, alpha, + A, max(1,stride(A,2)), x, stride(x,1), + beta, y, stride(y,1)) y end function hbmv(uplo::Char, k::Integer, alpha::($elty), A::StridedMatrix{$elty}, x::StridedVector{$elty}) @@ -887,10 +887,10 @@ for (fname, elty) in ((:dtrmv_,:Float64), throw(DimensionMismatch("A has size ($n,$n), x has length $(length(x))")) end ccall((@blasfunc($fname), libblas), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &uplo, &trans, &diag, &n, - A, &max(1,stride(A,2)), x, &max(1,stride(x, 1))) + (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}), + uplo, trans, diag, n, + A, max(1,stride(A,2)), x, max(1,stride(x, 1))) x end function trmv(uplo::Char, trans::Char, diag::Char, A::StridedMatrix{$elty}, x::StridedVector{$elty}) @@ -939,10 +939,10 @@ for (fname, elty) in ((:dtrsv_,:Float64), throw(DimensionMismatch("size of A is $n != length(x) = $(length(x))")) end ccall((@blasfunc($fname), libblas), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &uplo, &trans, &diag, &n, - A, &max(1,stride(A,2)), x, &stride(x, 1)) + (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}), + uplo, trans, diag, n, + A, max(1,stride(A,2)), x, stride(x, 1)) x end function trsv(uplo::Char, trans::Char, diag::Char, A::StridedMatrix{$elty}, x::StridedVector{$elty}) @@ -971,12 +971,12 @@ for (fname, elty) in ((:dger_,:Float64), throw(DimensionMismatch("A has size ($m,$n), x has length $(length(x)), y has length $(length(y))")) end ccall((@blasfunc($fname), libblas), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}), - &m, &n, &α, x, - &stride(x, 1), y, &stride(y, 1), A, - &max(1,stride(A,2))) + (Ref{BlasInt}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}), + m, n, α, x, + stride(x, 1), y, stride(y, 1), A, + max(1,stride(A,2))) A end end @@ -1003,10 +1003,10 @@ for (fname, elty, lib) in ((:dsyr_,:Float64,libblas), throw(DimensionMismatch("A has size ($n,$n), x has length $(length(x))")) end ccall((@blasfunc($fname), $lib), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &uplo, &n, &α, x, - &stride(x, 1), A, &max(1,stride(A, 2))) + (Ref{UInt8}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}), + uplo, n, α, x, + stride(x, 1), A, max(1,stride(A, 2))) A end end @@ -1032,10 +1032,10 @@ for (fname, elty, relty) in ((:zher_,:Complex128, :Float64), throw(DimensionMismatch("A has size ($n,$n), x has length $(length(x))")) end ccall((@blasfunc($fname), libblas), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$relty}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &uplo, &n, &α, x, - &stride(x, 1), A, &max(1,stride(A,2))) + (Ref{UInt8}, Ref{BlasInt}, Ref{$relty}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}), + uplo, n, α, x, + stride(x, 1), A, max(1,stride(A,2))) A end end @@ -1077,14 +1077,14 @@ for (gemm, elty) in throw(DimensionMismatch("A has size ($m,$ka), B has size ($kb,$n), C has size $(size(C))")) end ccall((@blasfunc($gemm), libblas), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, - Ptr{BlasInt}), - &transA, &transB, &m, &n, - &ka, &alpha, A, &max(1,stride(A,2)), - B, &max(1,stride(B,2)), &beta, C, - &max(1,stride(C,2))) + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, + Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, + Ref{BlasInt}), + transA, transB, m, n, + ka, alpha, A, max(1,stride(A,2)), + B, max(1,stride(B,2)), beta, C, + max(1,stride(C,2))) C end function gemm(transA::Char, transB::Char, alpha::($elty), A::StridedMatrix{$elty}, B::StridedMatrix{$elty}) @@ -1134,12 +1134,12 @@ for (mfname, elty) in ((:dsymm_,:Float64), throw(DimensionMismatch("B has second dimension $(size(B,2)) but needs to match second dimension of C, $n")) end ccall((@blasfunc($mfname), libblas), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}), - &side, &uplo, &m, &n, - &alpha, A, &max(1,stride(A,2)), B, - &max(1,stride(B,2)), &beta, C, &max(1,stride(C,2))) + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, + Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}), + side, uplo, m, n, + alpha, A, max(1,stride(A,2)), B, + max(1,stride(B,2)), beta, C, max(1,stride(C,2))) C end function symm(side::Char, uplo::Char, alpha::($elty), A::StridedMatrix{$elty}, B::StridedMatrix{$elty}) @@ -1199,12 +1199,12 @@ for (mfname, elty) in ((:zhemm_,:Complex128), throw(DimensionMismatch("B has second dimension $(size(B,2)) but needs to match second dimension of C, $n")) end ccall((@blasfunc($mfname), libblas), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}), - &side, &uplo, &m, &n, - &alpha, A, &max(1,stride(A,2)), B, - &max(1,stride(B,2)), &beta, C, &max(1,stride(C,2))) + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, + Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}), + side, uplo, m, n, + alpha, A, max(1,stride(A,2)), B, + max(1,stride(B,2)), beta, C, max(1,stride(C,2))) C end function hemm(side::Char, uplo::Char, alpha::($elty), A::StridedMatrix{$elty}, B::StridedMatrix{$elty}) @@ -1257,12 +1257,12 @@ for (fname, elty) in ((:dsyrk_,:Float64), if nn != n throw(DimensionMismatch("C has size ($n,$n), corresponding dimension of A is $nn")) end k = size(A, trans == 'N' ? 2 : 1) ccall((@blasfunc($fname), libblas), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}), - &uplo, &trans, &n, &k, - &alpha, A, &max(1,stride(A,2)), &beta, - C, &max(1,stride(C,2))) + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, + Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, + Ptr{$elty}, Ref{BlasInt}), + uplo, trans, n, k, + alpha, A, max(1,stride(A,2)), beta, + C, max(1,stride(C,2))) C end end @@ -1313,12 +1313,12 @@ for (fname, elty, relty) in ((:zherk_, :Complex128, :Float64), end k = size(A, trans == 'N' ? 2 : 1) ccall((@blasfunc($fname), libblas), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$relty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$relty}, - Ptr{$elty}, Ptr{BlasInt}), - &uplo, &trans, &n, &k, - &α, A, &max(1,stride(A,2)), &β, - C, &max(1,stride(C,2))) + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, + Ref{$relty}, Ptr{$elty}, Ref{BlasInt}, Ref{$relty}, + Ptr{$elty}, Ref{BlasInt}), + uplo, trans, n, k, + α, A, max(1,stride(A,2)), β, + C, max(1,stride(C,2))) C end function herk(uplo::Char, trans::Char, α::$relty, A::StridedVecOrMat{$elty}) @@ -1352,12 +1352,12 @@ for (fname, elty) in ((:dsyr2k_,:Float64), if nn != n throw(DimensionMismatch("C has size ($n,$n), corresponding dimension of A is $nn")) end k = size(A, trans == 'N' ? 2 : 1) ccall((@blasfunc($fname), libblas), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}), - &uplo, &trans, &n, &k, - &alpha, A, &max(1,stride(A,2)), B, &max(1,stride(B,2)), &beta, - C, &max(1,stride(C,2))) + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, + Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, + Ptr{$elty}, Ref{BlasInt}), + uplo, trans, n, k, + alpha, A, max(1,stride(A,2)), B, max(1,stride(B,2)), beta, + C, max(1,stride(C,2))) C end end @@ -1389,12 +1389,12 @@ for (fname, elty1, elty2) in ((:zher2k_,:Complex128,:Float64), (:cher2k_,:Comple if nn != n throw(DimensionMismatch("C has size ($n,$n), corresponding dimension of A is $nn")) end k = size(A, trans == 'N' ? 2 : 1) ccall((@blasfunc($fname), libblas), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty1}, Ptr{$elty1}, Ptr{BlasInt}, Ptr{$elty1}, Ptr{BlasInt}, - Ptr{$elty2}, Ptr{$elty1}, Ptr{BlasInt}), - &uplo, &trans, &n, &k, - &alpha, A, &max(1,stride(A,2)), B, &max(1,stride(B,2)), - &beta, C, &max(1,stride(C,2))) + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, + Ref{$elty1}, Ptr{$elty1}, Ref{BlasInt}, Ptr{$elty1}, Ref{BlasInt}, + Ref{$elty2}, Ptr{$elty1}, Ref{BlasInt}), + uplo, trans, n, k, + alpha, A, max(1,stride(A,2)), B, max(1,stride(B,2)), + beta, C, max(1,stride(C,2))) C end function her2k(uplo::Char, trans::Char, alpha::($elty1), A::StridedVecOrMat{$elty1}, B::StridedVecOrMat{$elty1}) @@ -1474,10 +1474,10 @@ for (mmname, smname, elty) in throw(DimensionMismatch("size of A, $(size(A)), doesn't match $side size of B with dims, $(size(B))")) end ccall((@blasfunc($mmname), libblas), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &side, &uplo, &transa, &diag, &m, &n, - &alpha, A, &max(1,stride(A,2)), B, &max(1,stride(B,2))) + (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, + Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}), + side, uplo, transa, diag, m, n, + alpha, A, max(1,stride(A,2)), B, max(1,stride(B,2))) B end function trmm(side::Char, uplo::Char, transa::Char, diag::Char, @@ -1499,12 +1499,12 @@ for (mmname, smname, elty) in throw(DimensionMismatch("size of A is ($k,$k), size of B is ($m,$n), side is $side, and transa='$transa'")) end ccall((@blasfunc($smname), libblas), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &side, &uplo, &transa, &diag, - &m, &n, &alpha, A, - &max(1,stride(A,2)), B, &max(1,stride(B,2))) + (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, + Ref{BlasInt}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}), + side, uplo, transa, diag, + m, n, alpha, A, + max(1,stride(A,2)), B, max(1,stride(B,2))) B end function trsm(side::Char, uplo::Char, transa::Char, diag::Char, alpha::$elty, A::StridedMatrix{$elty}, B::StridedMatrix{$elty}) diff --git a/base/linalg/lapack.jl b/base/linalg/lapack.jl index c73ea95df6b4c..5e5a60982f660 100644 --- a/base/linalg/lapack.jl +++ b/base/linalg/lapack.jl @@ -122,9 +122,9 @@ for (gbtrf, gbtrs, elty) in ipiv = similar(AB, BlasInt, mnmn) info = Ref{BlasInt}() ccall((@blasfunc($gbtrf), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}), - &m, &n, &kl, &ku, AB, &max(1,stride(AB,2)), ipiv, info) + (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}), + m, n, kl, ku, AB, max(1,stride(AB,2)), ipiv, info) chklapackerror(info[]) AB, ipiv end @@ -147,11 +147,11 @@ for (gbtrf, gbtrs, elty) in throw(DimensionMismatch("matrix AB has dimensions $(size(AB)), but right hand side matrix B has dimensions $(size(B))")) end ccall((@blasfunc($gbtrs), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), - &trans, &n, &kl, &ku, &size(B,2), AB, &max(1,stride(AB,2)), ipiv, - B, &max(1,stride(B,2)), info) + trans, n, kl, ku, size(B,2), AB, max(1,stride(AB,2)), ipiv, + B, max(1,stride(B,2)), info) chklapackerror(info[]) B end @@ -201,9 +201,9 @@ for (gebal, gebak, elty, relty) in scale = similar(A, $relty, n) info = Ref{BlasInt}() ccall((@blasfunc($gebal), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$relty}, Ptr{BlasInt}), - &job, &n, A, &max(1,stride(A,2)), ilo, ihi, scale, info) + job, n, A, max(1,stride(A,2)), ilo, ihi, scale, info) chklapackerror(info[]) ilo[], ihi[], scale end @@ -223,9 +223,9 @@ for (gebal, gebak, elty, relty) in n = checksquare(V) info = Ref{BlasInt}() ccall((@blasfunc($gebak), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$relty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &job, &side, &size(V,1), &ilo, &ihi, scale, &n, V, &max(1,stride(V,2)), info) + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, + Ptr{$relty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + job, side, size(V,1), ilo, ihi, scale, n, V, max(1,stride(V,2)), info) chklapackerror(info[]) V end @@ -287,12 +287,12 @@ for (gebrd, gelqf, geqlf, geqrf, geqp3, geqrt, geqrt3, gerqf, getrf, elty, relty info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($gebrd), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &m, &n, A, &max(1,stride(A,2)), + Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + m, n, A, max(1,stride(A,2)), d, e, tauq, taup, - work, &lwork, info) + work, lwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -320,9 +320,9 @@ for (gebrd, gelqf, geqlf, geqrf, geqp3, geqrt, geqrt3, gerqf, getrf, elty, relty info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($gelqf), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &m, &n, A, &lda, tau, work, &lwork, info) + (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + m, n, A, lda, tau, work, lwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -350,9 +350,9 @@ for (gebrd, gelqf, geqlf, geqrf, geqp3, geqrt, geqrt3, gerqf, getrf, elty, relty info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($geqlf), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &m, &n, A, &lda, tau, work, &lwork, info) + (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + m, n, A, lda, tau, work, lwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -391,20 +391,20 @@ for (gebrd, gelqf, geqlf, geqrf, geqp3, geqrt, geqrt3, gerqf, getrf, elty, relty for i = 1:2 # first call returns lwork as work[1] if cmplx ccall((@blasfunc($geqp3), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{BlasInt}), - &m, &n, A, &lda, - jpvt, tau, work, &lwork, + m, n, A, lda, + jpvt, tau, work, lwork, rwork, info) else ccall((@blasfunc($geqp3), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), - &m, &n, A, &lda, + m, n, A, lda, jpvt, tau, work, - &lwork, info) + lwork, info) end chklapackerror(info[]) if i == 1 @@ -428,11 +428,11 @@ for (gebrd, gelqf, geqlf, geqrf, geqp3, geqrt, geqrt3, gerqf, getrf, elty, relty if n > 0 info = Ref{BlasInt}() ccall((@blasfunc($geqrt), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, + (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &m, &n, &nb, A, - &lda, T, &max(1,stride(T,2)), work, + m, n, nb, A, + lda, T, max(1,stride(T,2)), work, info) chklapackerror(info[]) end @@ -453,10 +453,10 @@ for (gebrd, gelqf, geqlf, geqrf, geqp3, geqrt, geqrt3, gerqf, getrf, elty, relty if n > 0 info = Ref{BlasInt}() ccall((@blasfunc($geqrt3), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &m, &n, A, &max(1, stride(A, 2)), - T, &max(1,stride(T,2)), info) + (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + m, n, A, max(1, stride(A, 2)), + T, max(1,stride(T,2)), info) chklapackerror(info[]) end A, T @@ -479,9 +479,9 @@ for (gebrd, gelqf, geqlf, geqrf, geqp3, geqrt, geqrt3, gerqf, getrf, elty, relty info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($geqrf), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &m, &n, A, &max(1,stride(A,2)), tau, work, &lwork, info) + (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + m, n, A, max(1,stride(A,2)), tau, work, lwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -507,9 +507,9 @@ for (gebrd, gelqf, geqlf, geqrf, geqp3, geqrt, geqrt3, gerqf, getrf, elty, relty info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($gerqf), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &m, &n, A, &max(1,stride(A,2)), tau, work, &lwork, info) + (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + m, n, A, max(1,stride(A,2)), tau, work, lwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -532,9 +532,9 @@ for (gebrd, gelqf, geqlf, geqrf, geqp3, geqrt, geqrt3, gerqf, getrf, elty, relty ipiv = similar(A, BlasInt, min(m,n)) info = Ref{BlasInt}() ccall((@blasfunc($getrf), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}), - &m, &n, A, &lda, ipiv, info) + (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}), + m, n, A, lda, ipiv, info) chkargsok(info[]) A, ipiv, info[] #Error code is stored in LU factorization type end @@ -763,10 +763,10 @@ for (tzrzf, ormrz, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($tzrzf), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &m, &n, A, &lda, - tau, work, &lwork, info) + (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + m, n, A, lda, + tau, work, lwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -800,14 +800,14 @@ for (tzrzf, ormrz, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($ormrz), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{BlasInt}), - &side, &trans, &m, &n, - &k, &l, A, &lda, - tau, C, &ldc, work, - &lwork, info) + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, + Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ptr{BlasInt}), + side, trans, m, n, + k, l, A, lda, + tau, C, ldc, work, + lwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -864,11 +864,11 @@ for (gels, gesv, getrs, getri, elty) in lwork = BlasInt(-1) for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($gels), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &(btrn ? 'T' : 'N'), &m, &n, &size(B,2), A, &max(1,stride(A,2)), - B, &max(1,stride(B,2)), work, &lwork, info) + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + (btrn ? 'T' : 'N'), m, n, size(B,2), A, max(1,stride(A,2)), + B, max(1,stride(B,2)), work, lwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -904,9 +904,9 @@ for (gels, gesv, getrs, getri, elty) in ipiv = similar(A, BlasInt, n) info = Ref{BlasInt}() ccall((@blasfunc($gesv), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &n, &size(B,2), A, &max(1,stride(A,2)), ipiv, B, &max(1,stride(B,2)), info) + (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), info) chklapackerror(info[]) B, A, ipiv end @@ -928,9 +928,9 @@ for (gels, gesv, getrs, getri, elty) in nrhs = size(B, 2) info = Ref{BlasInt}() ccall((@blasfunc($getrs), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &trans, &n, &size(B,2), A, &max(1,stride(A,2)), ipiv, B, &max(1,stride(B,2)), info) + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + trans, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), info) chklapackerror(info[]) B end @@ -953,9 +953,9 @@ for (gels, gesv, getrs, getri, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($getri), liblapack), Void, - (Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &n, A, &lda, ipiv, work, &lwork, info) + (Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + n, A, lda, ipiv, work, lwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -1048,13 +1048,13 @@ for (gesvx, elty) in info = Ref{BlasInt}() X = similar(A, $elty, n, nrhs) ccall((@blasfunc($gesvx), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{UInt8}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, + Ref{UInt8}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &fact, &trans, &n, &nrhs, A, &lda, AF, &ldaf, ipiv, &equed, R, C, B, - &ldb, X, &n, rcond, ferr, berr, work, iwork, info) + fact, trans, n, nrhs, A, lda, AF, ldaf, ipiv, equed, R, C, B, + ldb, X, n, rcond, ferr, berr, work, iwork, info) chklapackerror(info[]) if info[] == n + 1 warn("matrix is singular to working precision") @@ -1117,13 +1117,13 @@ for (gesvx, elty, relty) in info = Ref{BlasInt}() X = similar(A, $elty, n, nrhs) ccall((@blasfunc($gesvx), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{UInt8}, Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$relty}, Ptr{$relty}, Ptr{$relty}, + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, + Ref{UInt8}, Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ptr{$relty}, Ptr{BlasInt}), - &fact, &trans, &n, &nrhs, A, &lda, AF, &ldaf, ipiv, &equed, R, C, B, - &ldb, X, &n, rcond, ferr, berr, work, rwork, info) + fact, trans, n, nrhs, A, lda, AF, ldaf, ipiv, equed, R, C, B, + ldb, X, n, rcond, ferr, berr, work, rwork, info) chklapackerror(info[]) if info[] == n + 1 warn("matrix is singular to working precision") @@ -1212,14 +1212,14 @@ for (gelsd, gelsy, elty) in iwork = Vector{BlasInt}(1) for i = 1:2 # first call returns lwork as work[1] and iwork length as iwork[1] ccall((@blasfunc($gelsd), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}), - &m, &n, &size(B,2), - A, &max(1,stride(A,2)), newB, &max(1,stride(B,2),n), + Ref{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}), + m, n, size(B,2), + A, max(1,stride(A,2)), newB, max(1,stride(B,2),n), s, $elty(rcond), rnk, work, - &lwork, iwork, info) + lwork, iwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -1257,13 +1257,13 @@ for (gelsd, gelsy, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($gelsy), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, - Ref{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, + Ref{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), - &m, &n, &nrhs, A, - &lda, newB, &ldb, jpvt, - $elty(rcond), rnk, work, &lwork, + m, n, nrhs, A, + lda, newB, ldb, jpvt, + $elty(rcond), rnk, work, lwork, info) chklapackerror(info[]) if i == 1 @@ -1306,13 +1306,13 @@ for (gelsd, gelsy, elty, relty) in iwork = Vector{BlasInt}(1) for i = 1:2 # first call returns lwork as work[1], rwork length as rwork[1] and iwork length as iwork[1] ccall((@blasfunc($gelsd), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$relty}, - Ref{$relty}, Ref{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, + Ref{$relty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ref{BlasInt}, Ref{BlasInt}), - &m, &n, &size(B,2), A, - &max(1,stride(A,2)), newB, &max(1,stride(B,2),n), s, - $relty(rcond), rnk, work, &lwork, + m, n, size(B,2), A, + max(1,stride(A,2)), newB, max(1,stride(B,2),n), s, + $relty(rcond), rnk, work, lwork, rwork, iwork, info) chklapackerror(info[]) if i == 1 @@ -1353,13 +1353,13 @@ for (gelsd, gelsy, elty, relty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($gelsy), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, - Ref{$relty}, Ref{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, + Ref{$relty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{BlasInt}), - &m, &n, &nrhs, A, - &lda, newB, &ldb, jpvt, - $relty(rcond), rnk, work, &lwork, + m, n, nrhs, A, + lda, newB, ldb, jpvt, + $relty(rcond), rnk, work, lwork, rwork, info) chklapackerror(info[]) if i == 1 @@ -1427,12 +1427,12 @@ for (gglse, elty) in ((:dgglse_, :Float64), lwork = BlasInt(-1) for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($gglse), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, + Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), - &m, &n, &p, A, &max(1,stride(A,2)), B, &max(1,stride(B,2)), c, d, X, - work, &lwork, info) + m, n, p, A, max(1,stride(A,2)), B, max(1,stride(B,2)), c, d, X, + work, lwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -1490,20 +1490,20 @@ for (geev, gesvd, gesdd, ggsvd, elty, relty) in for i = 1:2 # first call returns lwork as work[1] if cmplx ccall((@blasfunc($geev), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{BlasInt}), - &jobvl, &jobvr, &n, A, &max(1,stride(A,2)), W, VL, &n, VR, &n, - work, &lwork, rwork, info) + jobvl, jobvr, n, A, max(1,stride(A,2)), W, VL, n, VR, n, + work, lwork, rwork, info) else ccall((@blasfunc($geev), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{BlasInt}), - &jobvl, &jobvr, &n, A, &max(1,stride(A,2)), WR, WI, VL, &n, - VR, &n, work, &lwork, info) + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ptr{BlasInt}), + jobvl, jobvr, n, A, max(1,stride(A,2)), WR, WI, VL, n, + VR, n, work, lwork, info) end chklapackerror(info[]) if i == 1 @@ -1554,20 +1554,20 @@ for (geev, gesvd, gesdd, ggsvd, elty, relty) in for i = 1:2 # first call returns lwork as work[1] if cmplx ccall((@blasfunc($gesdd), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$relty}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$relty}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{BlasInt}, Ptr{BlasInt}), - &job, &m, &n, A, &max(1,stride(A,2)), S, U, &max(1,stride(U,2)), VT, &max(1,stride(VT,2)), - work, &lwork, rwork, iwork, info) + job, m, n, A, max(1,stride(A,2)), S, U, max(1,stride(U,2)), VT, max(1,stride(VT,2)), + work, lwork, rwork, iwork, info) else ccall((@blasfunc($gesdd), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}), - &job, &m, &n, A, &max(1,stride(A,2)), S, U, &max(1,stride(U,2)), VT, &max(1,stride(VT,2)), - work, &lwork, iwork, info) + job, m, n, A, max(1,stride(A,2)), S, U, max(1,stride(U,2)), VT, max(1,stride(VT,2)), + work, lwork, iwork, info) end chklapackerror(info[]) if i == 1 @@ -1622,20 +1622,20 @@ for (geev, gesvd, gesdd, ggsvd, elty, relty) in for i in 1:2 # first call returns lwork as work[1] if cmplx ccall((@blasfunc($gesvd), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$relty}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$relty}, Ptr{BlasInt}), - &jobu, &jobvt, &m, &n, A, &max(1,stride(A,2)), S, U, &max(1,stride(U,2)), VT, &max(1,stride(VT,2)), - work, &lwork, rwork, info) + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$relty}, Ptr{BlasInt}), + jobu, jobvt, m, n, A, max(1,stride(A,2)), S, U, max(1,stride(U,2)), VT, max(1,stride(VT,2)), + work, lwork, rwork, info) else ccall((@blasfunc($gesvd), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{BlasInt}), - &jobu, &jobvt, &m, &n, A, &max(1,stride(A,2)), S, U, &max(1,stride(U,2)), VT, &max(1,stride(VT,2)), - work, &lwork, info) + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ptr{BlasInt}), + jobu, jobvt, m, n, A, max(1,stride(A,2)), S, U, max(1,stride(U,2)), VT, max(1,stride(VT,2)), + work, lwork, info) end chklapackerror(info[]) if i == 1 @@ -1701,31 +1701,31 @@ for (geev, gesvd, gesdd, ggsvd, elty, relty) in info = Ref{BlasInt}() if cmplx ccall((@blasfunc($ggsvd), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, + Ref{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$relty}, Ptr{BlasInt}, Ptr{BlasInt}), - &jobu, &jobv, &jobq, &m, - &n, &p, k, l, - A, &lda, B, &ldb, - alpha, beta, U, &ldu, - V, &ldv, Q, &ldq, + jobu, jobv, jobq, m, + n, p, k, l, + A, lda, B, ldb, + alpha, beta, U, ldu, + V, ldv, Q, ldq, work, rwork, iwork, info) else ccall((@blasfunc($ggsvd), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, + Ref{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &jobu, &jobv, &jobq, &m, - &n, &p, k, l, - A, &lda, B, &ldb, - alpha, beta, U, &ldu, - V, &ldv, Q, &ldq, + jobu, jobv, jobq, m, + n, p, k, l, + A, lda, B, ldb, + alpha, beta, U, ldu, + V, ldv, Q, ldq, work, iwork, info) end chklapackerror(info[]) @@ -1820,18 +1820,18 @@ for (f, elty) in ((:dggsvd3_, :Float64), info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($f), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}), - &jobu, &jobv, &jobq, &m, - &n, &p, k, l, - A, &lda, B, &ldb, - alpha, beta, U, &ldu, - V, &ldv, Q, &ldq, - work, &lwork, iwork, info) + (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, + Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}), + jobu, jobv, jobq, m, + n, p, k, l, + A, lda, B, ldb, + alpha, beta, U, ldu, + V, ldv, Q, ldq, + work, lwork, iwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(work[1]) @@ -1877,19 +1877,19 @@ for (f, elty, relty) in ((:zggsvd3_, :Complex128, :Float64), info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($f), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$relty}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, + Ref{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{BlasInt}, Ptr{BlasInt}), - &jobu, &jobv, &jobq, &m, - &n, &p, k, l, - A, &lda, B, &ldb, - alpha, beta, U, &ldu, - V, &ldv, Q, &ldq, - work, &lwork, rwork, iwork, + jobu, jobv, jobq, m, + n, p, k, l, + A, lda, B, ldb, + alpha, beta, U, ldu, + V, ldv, Q, ldq, + work, lwork, rwork, iwork, info) chklapackerror(info[]) if i == 1 @@ -1985,18 +1985,18 @@ for (geevx, ggev, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($geevx), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, + (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, + Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}), - &balanc, &jobvl, &jobvr, &sense, - &n, A, &lda, wr, - wi, VL, &max(1,ldvl), VR, - &max(1,ldvr), ilo, ihi, scale, + Ref{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}), + balanc, jobvl, jobvr, sense, + n, A, lda, wr, + wi, VL, max(1,ldvl), VR, + max(1,ldvr), ilo, ihi, scale, abnrm, rconde, rcondv, work, - &lwork, iwork, info) + lwork, iwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(work[1]) @@ -2050,15 +2050,15 @@ for (geevx, ggev, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($ggev), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, + Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), - &jobvl, &jobvr, &n, A, - &lda, B, &ldb, alphar, - alphai, beta, vl, &ldvl, - vr, &ldvr, work, &lwork, + jobvl, jobvr, n, A, + lda, B, ldb, alphar, + alphai, beta, vl, ldvl, + vr, ldvr, work, lwork, info) chklapackerror(info[]) if i == 1 @@ -2130,17 +2130,17 @@ for (geevx, ggev, elty, relty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($geevx), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$relty}, Ptr{$relty}, - Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ptr{BlasInt}, + Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{BlasInt}), - &balanc, &jobvl, &jobvr, &sense, - &n, A, &lda, w, - VL, &max(1,ldvl), VR, &max(1,ldvr), + balanc, jobvl, jobvr, sense, + n, A, lda, w, + VL, max(1,ldvl), VR, max(1,ldvr), ilo, ihi, scale, abnrm, - rconde, rcondv, work, &lwork, + rconde, rcondv, work, lwork, rwork, info) chklapackerror(info[]) if i == 1 @@ -2196,15 +2196,15 @@ for (geevx, ggev, elty, relty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($ggev), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$relty}, + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, + Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{BlasInt}), - &jobvl, &jobvr, &n, A, - &lda, B, &ldb, alpha, - beta, vl, &ldvl, vr, - &ldvr, work, &lwork, rwork, + jobvl, jobvr, n, A, + lda, B, ldb, alpha, + beta, vl, ldvl, vr, + ldvr, work, lwork, rwork, info) chklapackerror(info[]) if i == 1 @@ -2269,11 +2269,11 @@ for (laic1, elty) in s = Vector{$elty}(1) c = Vector{$elty}(1) ccall((@blasfunc($laic1), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, - Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, + (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{$elty}, + Ptr{$elty}, Ref{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}), - &job, &j, x, &sest, - w, &gamma, sestpr, s, + job, j, x, sest, + w, gamma, sestpr, s, c) sestpr[1], s[1], c[1] end @@ -2302,11 +2302,11 @@ for (laic1, elty, relty) in s = Vector{$elty}(1) c = Vector{$elty}(1) ccall((@blasfunc($laic1), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$relty}, - Ptr{$elty}, Ptr{$elty}, Ptr{$relty}, Ptr{$elty}, + (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{$relty}, + Ptr{$elty}, Ref{$elty}, Ptr{$relty}, Ptr{$elty}, Ptr{$elty}), - &job, &j, x, &sest, - w, &gamma, sestpr, s, + job, j, x, sest, + w, gamma, sestpr, s, c) sestpr[1], s[1], c[1] end @@ -2343,9 +2343,9 @@ for (gtsv, gttrf, gttrs, elty) in end info = Ref{BlasInt}() ccall((@blasfunc($gtsv), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &n, &size(B,2), dl, d, du, B, &max(1,stride(B,2)), info) + (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, + Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + n, size(B,2), dl, d, du, B, max(1,stride(B,2)), info) chklapackerror(info[]) B end @@ -2369,9 +2369,9 @@ for (gtsv, gttrf, gttrs, elty) in ipiv = similar(d, BlasInt, n) info = Ref{BlasInt}() ccall((@blasfunc($gttrf), liblapack), Void, - (Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, + (Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &n, dl, d, du, du2, ipiv, info) + n, dl, d, du, du2, ipiv, info) chklapackerror(info[]) dl, d, du, du2, ipiv end @@ -2400,10 +2400,10 @@ for (gtsv, gttrf, gttrs, elty) in end info = Ref{BlasInt}() ccall((@blasfunc($gttrs), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &trans, &n, &size(B,2), dl, d, du, du2, ipiv, B, &max(1,stride(B,2)), info) + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + trans, n, size(B,2), dl, d, du, du2, ipiv, B, max(1,stride(B,2)), info) chklapackerror(info[]) B end @@ -2466,9 +2466,9 @@ for (orglq, orgqr, orgql, orgrq, ormlq, ormqr, ormql, ormrq, gemqrt, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($orglq), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &m, &n, &k, A, &max(1,stride(A,2)), tau, work, &lwork, info) + (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + m, n, k, A, max(1,stride(A,2)), tau, work, lwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -2499,10 +2499,10 @@ for (orglq, orgqr, orgql, orgrq, ormlq, ormqr, ormql, ormrq, gemqrt, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($orgqr), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &m, &n, &k, A, - &max(1,stride(A,2)), tau, work, &lwork, + (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + m, n, k, A, + max(1,stride(A,2)), tau, work, lwork, info) chklapackerror(info[]) if i == 1 @@ -2534,10 +2534,10 @@ for (orglq, orgqr, orgql, orgrq, ormlq, ormqr, ormql, ormrq, gemqrt, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($orgql), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &m, &n, &k, A, - &max(1,stride(A,2)), tau, work, &lwork, + (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + m, n, k, A, + max(1,stride(A,2)), tau, work, lwork, info) chklapackerror(info[]) if i == 1 @@ -2569,10 +2569,10 @@ for (orglq, orgqr, orgql, orgrq, ormlq, ormqr, ormql, ormrq, gemqrt, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($orgrq), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &m, &n, &k, A, - &max(1,stride(A,2)), tau, work, &lwork, + (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + m, n, k, A, + max(1,stride(A,2)), tau, work, lwork, info) chklapackerror(info[]) if i == 1 @@ -2619,11 +2619,11 @@ for (orglq, orgqr, orgql, orgrq, ormlq, ormqr, ormql, ormrq, gemqrt, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($ormlq), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &side, &trans, &m, &n, &k, A, &max(1,stride(A,2)), tau, - C, &max(1,stride(C,2)), work, &lwork, info) + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + side, trans, m, n, k, A, max(1,stride(A,2)), tau, + C, max(1,stride(C,2)), work, lwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -2665,13 +2665,13 @@ for (orglq, orgqr, orgql, orgrq, ormlq, ormqr, ormql, ormrq, gemqrt, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($ormqr), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), - &side, &trans, &m, &n, - &k, A, &max(1,stride(A,2)), tau, - C, &max(1, stride(C,2)), work, &lwork, + side, trans, m, n, + k, A, max(1,stride(A,2)), tau, + C, max(1, stride(C,2)), work, lwork, info) chklapackerror(info[]) if i == 1 @@ -2714,13 +2714,13 @@ for (orglq, orgqr, orgql, orgrq, ormlq, ormqr, ormql, ormrq, gemqrt, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($ormql), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), - &side, &trans, &m, &n, - &k, A, &max(1,stride(A,2)), tau, - C, &max(1, stride(C,2)), work, &lwork, + side, trans, m, n, + k, A, max(1,stride(A,2)), tau, + C, max(1, stride(C,2)), work, lwork, info) chklapackerror(info[]) if i == 1 @@ -2763,11 +2763,11 @@ for (orglq, orgqr, orgql, orgrq, ormlq, ormqr, ormql, ormrq, gemqrt, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($ormrq), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &side, &trans, &m, &n, &k, A, &max(1,stride(A,2)), tau, - C, &max(1,stride(C,2)), work, &lwork, info) + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + side, trans, m, n, k, A, max(1,stride(A,2)), tau, + C, max(1,stride(C,2)), work, lwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -2818,13 +2818,13 @@ for (orglq, orgqr, orgql, orgrq, ormlq, ormqr, ormql, ormrq, gemqrt, elty) in work = Vector{$elty}(wss) info = Ref{BlasInt}() ccall((@blasfunc($gemqrt), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, + Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &side, &trans, &m, &n, - &k, &nb, V, &ldv, - T, &max(1,stride(T,2)), C, &max(1,ldc), + side, trans, m, n, + k, nb, V, ldv, + T, max(1,stride(T,2)), C, max(1,ldc), work, info) chklapackerror(info[]) return C @@ -2936,9 +2936,9 @@ for (posv, potrf, potri, potrs, pstrf, elty, rtyp) in end info = Ref{BlasInt}() ccall((@blasfunc($posv), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &n, &size(B,2), A, &max(1,stride(A,2)), B, &max(1,stride(B,2)), info) + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, n, size(B,2), A, max(1,stride(A,2)), B, max(1,stride(B,2)), info) chkargsok(info[]) chkposdef(info[]) A, B @@ -2960,8 +2960,8 @@ for (posv, potrf, potri, potrs, pstrf, elty, rtyp) in end info = Ref{BlasInt}() ccall((@blasfunc($potrf), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &size(A,1), A, &lda, info) + (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, size(A,1), A, lda, info) chkargsok(info[]) #info[] > 0 means the leading minor of order info[] is not positive definite #ordinarily, throw Exception here, but return error code here @@ -2980,8 +2980,8 @@ for (posv, potrf, potri, potrs, pstrf, elty, rtyp) in chkuplo(uplo) info = Ref{BlasInt}() ccall((@blasfunc($potri), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &size(A,1), A, &max(1,stride(A,2)), info) + (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, size(A,1), A, max(1,stride(A,2)), info) chkargsok(info[]) chknonsingular(info[]) A @@ -3008,10 +3008,10 @@ for (posv, potrf, potri, potrs, pstrf, elty, rtyp) in ldb = max(1,stride(B,2)) info = Ref{BlasInt}() ccall((@blasfunc($potrs), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &n, &nrhs, A, - &lda, B, &ldb, info) + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, n, nrhs, A, + lda, B, ldb, info) chklapackerror(info[]) return B end @@ -3033,9 +3033,9 @@ for (posv, potrf, potri, potrs, pstrf, elty, rtyp) in work = Vector{$rtyp}(2n) info = Ref{BlasInt}() ccall((@blasfunc($pstrf), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$rtyp}, Ptr{$rtyp}, Ptr{BlasInt}), - &uplo, &n, A, &max(1,stride(A,2)), piv, rank, &tol, work, info) + (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, + Ptr{BlasInt}, Ref{$rtyp}, Ptr{$rtyp}, Ptr{BlasInt}), + uplo, n, A, max(1,stride(A,2)), piv, rank, tol, work, info) chkargsok(info[]) A, piv, rank[1], info[] #Stored in PivotedCholesky end @@ -3121,9 +3121,9 @@ for (ptsv, pttrf, elty, relty) in end info = Ref{BlasInt}() ccall((@blasfunc($ptsv), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$relty}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &n, &size(B,2), D, E, B, &max(1,stride(B,2)), info) + (Ref{BlasInt}, Ref{BlasInt}, Ptr{$relty}, Ptr{$elty}, + Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + n, size(B,2), D, E, B, max(1,stride(B,2)), info) chklapackerror(info[]) B end @@ -3141,8 +3141,8 @@ for (ptsv, pttrf, elty, relty) in end info = Ref{BlasInt}() ccall((@blasfunc($pttrf), liblapack), Void, - (Ptr{BlasInt}, Ptr{$relty}, Ptr{$elty}, Ptr{BlasInt}), - &n, D, E, info) + (Ref{BlasInt}, Ptr{$relty}, Ptr{$elty}, Ptr{BlasInt}), + n, D, E, info) chklapackerror(info[]) D, E end @@ -3187,9 +3187,9 @@ for (pttrs, elty, relty) in end info = Ref{BlasInt}() ccall((@blasfunc($pttrs), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$relty}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &n, &size(B,2), D, E, B, &max(1,stride(B,2)), info) + (Ref{BlasInt}, Ref{BlasInt}, Ptr{$relty}, Ptr{$elty}, + Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + n, size(B,2), D, E, B, max(1,stride(B,2)), info) chklapackerror(info[]) B end @@ -3220,9 +3220,9 @@ for (pttrs, elty, relty) in end info = Ref{BlasInt}() ccall((@blasfunc($pttrs), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$relty}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &n, &size(B,2), D, E, B, &max(1,stride(B,2)), info) + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$relty}, Ptr{$elty}, + Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, n, size(B,2), D, E, B, max(1,stride(B,2)), info) chklapackerror(info[]) B end @@ -3259,9 +3259,9 @@ for (trtri, trtrs, elty) in lda = max(1,stride(A, 2)) info = Ref{BlasInt}() ccall((@blasfunc($trtri), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), - &uplo, &diag, &n, A, &lda, info) + uplo, diag, n, A, lda, info) chklapackerror(info[]) A end @@ -3284,10 +3284,10 @@ for (trtri, trtrs, elty) in end info = Ref{BlasInt}() ccall((@blasfunc($trtrs), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &trans, &diag, &n, &size(B,2), A, &max(1,stride(A,2)), - B, &max(1,stride(B,2)), info) + (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, trans, diag, n, size(B,2), A, max(1,stride(A,2)), + B, max(1,stride(B,2)), info) chklapackerror(info[]) B end @@ -3339,10 +3339,10 @@ for (trcon, trevc, trrfs, elty) in iwork = Vector{BlasInt}(n) info = Ref{BlasInt}() ccall((@blasfunc($trcon), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ref{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &norm, &uplo, &diag, &n, - A, &max(1,stride(A,2)), rcond, work, iwork, info) + (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), + norm, uplo, diag, n, + A, max(1,stride(A,2)), rcond, work, iwork, info) chklapackerror(info[]) rcond[] end @@ -3377,13 +3377,13 @@ for (trcon, trevc, trrfs, elty) in info = Ref{BlasInt}() ccall((@blasfunc($trevc), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt},Ptr{BlasInt}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{UInt8}, Ptr{BlasInt}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt},Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &side, &howmny, select, &n, - T, &ldt, VL, &ldvl, - VR, &ldvr, &mm, m, + side, howmny, select, n, + T, ldt, VL, ldvl, + VR, ldvr, mm, m, work, info) chklapackerror(info[]) @@ -3432,11 +3432,11 @@ for (trcon, trevc, trrfs, elty) in iwork = Vector{BlasInt}(n) info = Ref{BlasInt}() ccall((@blasfunc($trrfs), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &trans, &diag, &n, - &nrhs, A, &max(1,stride(A,2)), B, &max(1,stride(B,2)), X, &max(1,stride(X,2)), + uplo, trans, diag, n, + nrhs, A, max(1,stride(A,2)), B, max(1,stride(B,2)), X, max(1,stride(X,2)), Ferr, Berr, work, iwork, info) chklapackerror(info[]) Ferr, Berr @@ -3467,10 +3467,10 @@ for (trcon, trevc, trrfs, elty, relty) in rwork = Vector{$relty}(n) info = Ref{BlasInt}() ccall((@blasfunc($trcon), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ref{$relty}, Ptr{$elty}, Ptr{$relty}, Ptr{BlasInt}), - &norm, &uplo, &diag, &n, - A, &max(1,stride(A,2)), rcond, work, rwork, info) + (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ref{$relty}, Ptr{$elty}, Ptr{$relty}, Ptr{BlasInt}), + norm, uplo, diag, n, + A, max(1,stride(A,2)), rcond, work, rwork, info) chklapackerror(info[]) rcond[] end @@ -3506,13 +3506,13 @@ for (trcon, trevc, trrfs, elty, relty) in rwork = Vector{$relty}(n) info = Ref{BlasInt}() ccall((@blasfunc($trevc), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{UInt8}, Ptr{BlasInt}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$relty}, Ptr{BlasInt}), - &side, &howmny, select, &n, - T, &ldt, VL, &ldvl, - VR, &ldvr, &mm, m, + side, howmny, select, n, + T, ldt, VL, ldvl, + VR, ldvr, mm, m, work, rwork, info) chklapackerror(info[]) @@ -3561,11 +3561,11 @@ for (trcon, trevc, trrfs, elty, relty) in rwork = Vector{$relty}(n) info = Ref{BlasInt}() ccall((@blasfunc($trrfs), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ptr{$relty}, Ptr{BlasInt}), - &uplo, &trans, &diag, &n, - &nrhs, A, &max(1,stride(A,2)), B, &max(1,stride(B,2)), X, &max(1,stride(X,2)), + uplo, trans, diag, n, + nrhs, A, max(1,stride(A,2)), B, max(1,stride(B,2)), X, max(1,stride(X,2)), Ferr, Berr, work, rwork, info) chklapackerror(info[]) Ferr, Berr @@ -3631,9 +3631,9 @@ for (stev, stebz, stegr, stein, elty) in work = Vector{$elty}(max(1, 2n-2)) info = Ref{BlasInt}() ccall((@blasfunc($stev), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &job, &n, dv, ev, Zmat, &n, work, info) + (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), + job, n, dv, ev, Zmat, n, work, info) chklapackerror(info[]) dv, Zmat end @@ -3658,13 +3658,13 @@ for (stev, stebz, stegr, stein, elty) in iwork = Vector{BlasInt}(3*n) info = Ref{BlasInt}() ccall((@blasfunc($stebz), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{$elty}, + Ref{$elty}, Ref{BlasInt}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &range, &order, &n, &vl, - &vu, &il, &iu, &abstol, + range, order, n, vl, + vu, il, iu, abstol, dv, ev, m, nsplit, w, iblock, isplit, work, iwork, info) @@ -3692,16 +3692,16 @@ for (stev, stebz, stegr, stein, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] and liwork as iwork[1] ccall((@blasfunc($stegr), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}), - &jobz, &range, &n, dv, - eev, &vl, &vu, &il, - &iu, abstol, m, w, - Z, &ldz, isuppz, work, - &lwork, iwork, &liwork, info) + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, + Ptr{$elty}, Ref{$elty}, Ref{$elty}, Ref{BlasInt}, + Ref{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, + Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}), + jobz, range, n, dv, + eev, vl, vu, il, + iu, abstol, m, w, + Z, ldz, isuppz, work, + lwork, iwork, liwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(work[1]) @@ -3749,11 +3749,11 @@ for (stev, stebz, stegr, stein, elty) in ifail = Vector{BlasInt}(m) info = Ref{BlasInt}() ccall((@blasfunc($stein), liblapack), Void, - (Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, + Ref{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}), - &n, dv, ev, &m, w, iblock, isplit, z, &ldz, work, iwork, ifail, info) + n, dv, ev, m, w, iblock, isplit, z, ldz, work, iwork, ifail, info) chklapackerror(info[]) if any(ifail .!= 0) # TODO: better error message / type @@ -3837,9 +3837,9 @@ for (syconv, sysv, sytrf, sytri, sytrs, elty) in work = Vector{$elty}(n) info = Ref{BlasInt}() ccall((@blasfunc($syconv), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &uplo, &'C', &n, A, &max(1,stride(A,2)), ipiv, work, info) + uplo, 'C', n, A, max(1,stride(A,2)), ipiv, work, info) chklapackerror(info[]) A, work end @@ -3865,10 +3865,10 @@ for (syconv, sysv, sytrf, sytri, sytrs, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($sysv), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &n, &size(B,2), A, &max(1,stride(A,2)), ipiv, B, &max(1,stride(B,2)), - work, &lwork, info) + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), + work, lwork, info) chkargsok(info[]) chknonsingular(info[]) if i == 1 @@ -3899,9 +3899,9 @@ for (syconv, sysv, sytrf, sytri, sytrs, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($sytrf), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &n, A, &stride(A,2), ipiv, work, &lwork, info) + (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, n, A, stride(A,2), ipiv, work, lwork, info) chkargsok(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -3954,9 +3954,9 @@ for (syconv, sysv, sytrf, sytri, sytrs, elty) in work = Vector{$elty}(n) info = Ref{BlasInt}() ccall((@blasfunc($sytri), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &uplo, &n, A, &max(1,stride(A,2)), ipiv, work, info) + uplo, n, A, max(1,stride(A,2)), ipiv, work, info) chkargsok(info[]) chknonsingular(info[]) A @@ -3980,9 +3980,9 @@ for (syconv, sysv, sytrf, sytri, sytrs, elty) in end info = Ref{BlasInt}() ccall((@blasfunc($sytrs), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &n, &size(B,2), A, &max(1,stride(A,2)), ipiv, B, &max(1,stride(B,2)), info) + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), info) chklapackerror(info[]) B end @@ -4015,10 +4015,10 @@ for (sysv, sytrf, sytri, sytrs, syconvf, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($sysv), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &n, &size(B,2), A, &max(1,stride(A,2)), ipiv, B, &max(1,stride(B,2)), - work, &lwork, info) + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), + work, lwork, info) chkargsok(info[]) chknonsingular(info[]) if i == 1 @@ -4049,9 +4049,9 @@ for (sysv, sytrf, sytri, sytrs, syconvf, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($sytrf), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &n, A, &stride(A,2), ipiv, work, &lwork, info) + (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, n, A, stride(A,2), ipiv, work, lwork, info) chkargsok(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -4075,9 +4075,9 @@ for (sysv, sytrf, sytri, sytrs, syconvf, elty) in work = Vector{$elty}(n) info = Ref{BlasInt}() ccall((@blasfunc($sytri), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &uplo, &n, A, &max(1,stride(A,2)), ipiv, work, info) + uplo, n, A, max(1,stride(A,2)), ipiv, work, info) chkargsok(info[]) chknonsingular(info[]) A @@ -4101,9 +4101,9 @@ for (sysv, sytrf, sytri, sytrs, syconvf, elty) in end info = Ref{BlasInt}() ccall((@blasfunc($sytrs), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &n, &size(B,2), A, &max(1,stride(A,2)), ipiv, B, &max(1,stride(B,2)), info) + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), info) chklapackerror(info[]) B end @@ -4138,10 +4138,10 @@ for (sysv, sytrf, sytri, sytrs, syconvf, elty) in info = Ref{BlasInt}() ccall((@blasfunc($syconvf), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &uplo, &way, &n, A, - &lda, ipiv, e, info) + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), + uplo, way, n, A, + lda, ipiv, e, info) chklapackerror(info[]) return A, e @@ -4171,9 +4171,9 @@ for (syconv, hesv, hetrf, hetri, hetrs, elty, relty) in work = Vector{$elty}(n) info = Ref{BlasInt}() ccall((@blasfunc($syconv), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &uplo, &'C', &n, A, &max(1,stride(A,2)), ipiv, work, info) + uplo, 'C', n, A, max(1,stride(A,2)), ipiv, work, info) chklapackerror(info[]) A, work end @@ -4199,10 +4199,10 @@ for (syconv, hesv, hetrf, hetri, hetrs, elty, relty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($hesv), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &n, &size(B,2), A, &max(1,stride(A,2)), ipiv, B, &max(1,stride(B,2)), - work, &lwork, info) + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), + work, lwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -4230,9 +4230,9 @@ for (syconv, hesv, hetrf, hetri, hetrs, elty, relty) in info = Ref{BlasInt}() for i in 1:2 # first call returns lwork as work[1] ccall((@blasfunc($hetrf), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &n, A, &max(1,stride(A,2)), ipiv, work, &lwork, info) + (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, n, A, max(1,stride(A,2)), ipiv, work, lwork, info) chkargsok(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -4287,9 +4287,9 @@ for (syconv, hesv, hetrf, hetri, hetrs, elty, relty) in work = Vector{$elty}(n) info = Ref{BlasInt}() ccall((@blasfunc($hetri), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &uplo, &n, A, &max(1,stride(A,2)), ipiv, work, info) + uplo, n, A, max(1,stride(A,2)), ipiv, work, info) chklapackerror(info[]) A end @@ -4311,9 +4311,9 @@ for (syconv, hesv, hetrf, hetri, hetrs, elty, relty) in end info = Ref{BlasInt}() ccall((@blasfunc($hetrs), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &n, &size(B,2), A, &max(1,stride(A,2)), ipiv, B, &max(1,stride(B,2)), info) + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), info) chklapackerror(info[]) B end @@ -4345,10 +4345,10 @@ for (hesv, hetrf, hetri, hetrs, elty, relty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($hesv), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &n, &size(B,2), A, &max(1,stride(A,2)), ipiv, B, &max(1,stride(B,2)), - work, &lwork, info) + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), + work, lwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -4376,9 +4376,9 @@ for (hesv, hetrf, hetri, hetrs, elty, relty) in info = Ref{BlasInt}() for i in 1:2 # first call returns lwork as work[1] ccall((@blasfunc($hetrf), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &n, A, &max(1,stride(A,2)), ipiv, work, &lwork, info) + (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, n, A, max(1,stride(A,2)), ipiv, work, lwork, info) chkargsok(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -4403,9 +4403,9 @@ for (hesv, hetrf, hetri, hetrs, elty, relty) in work = Vector{$elty}(n) info = Ref{BlasInt}() ccall((@blasfunc($hetri), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &uplo, &n, A, &max(1,stride(A,2)), ipiv, work, info) + uplo, n, A, max(1,stride(A,2)), ipiv, work, info) chklapackerror(info[]) A end @@ -4427,9 +4427,9 @@ for (hesv, hetrf, hetri, hetrs, elty, relty) in end info = Ref{BlasInt}() ccall((@blasfunc($hetrs), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &n, &size(B,2), A, &max(1,stride(A,2)), ipiv, B, &max(1,stride(B,2)), info) + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), info) chklapackerror(info[]) B end @@ -4462,10 +4462,10 @@ for (sysv, sytrf, sytri, sytrs, elty, relty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($sysv), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &n, &size(B,2), A, &max(1,stride(A,2)), ipiv, B, &max(1,stride(B,2)), - work, &lwork, info) + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), + work, lwork, info) chkargsok(info[]) chknonsingular(info[]) if i == 1 @@ -4497,9 +4497,9 @@ for (sysv, sytrf, sytri, sytrs, elty, relty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($sytrf), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &n, A, &max(1,stride(A,2)), ipiv, work, &lwork, info) + (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, n, A, max(1,stride(A,2)), ipiv, work, lwork, info) chkargsok(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -4553,9 +4553,9 @@ for (sysv, sytrf, sytri, sytrs, elty, relty) in work = Vector{$elty}(n) info = Ref{BlasInt}() ccall((@blasfunc($sytri), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &uplo, &n, A, &max(1,stride(A,2)), ipiv, work, info) + uplo, n, A, max(1,stride(A,2)), ipiv, work, info) chklapackerror(info[]) A end @@ -4578,9 +4578,9 @@ for (sysv, sytrf, sytri, sytrs, elty, relty) in end info = Ref{BlasInt}() ccall((@blasfunc($sytrs), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &n, &size(B,2), A, &max(1,stride(A,2)), ipiv, B, &max(1,stride(B,2)), info) + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), info) chklapackerror(info[]) B end @@ -4613,10 +4613,10 @@ for (sysv, sytrf, sytri, sytrs, syconvf, elty, relty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($sysv), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &n, &size(B,2), A, &max(1,stride(A,2)), ipiv, B, &max(1,stride(B,2)), - work, &lwork, info) + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), + work, lwork, info) chkargsok(info[]) chknonsingular(info[]) if i == 1 @@ -4648,9 +4648,9 @@ for (sysv, sytrf, sytri, sytrs, syconvf, elty, relty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($sytrf), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &n, A, &max(1,stride(A,2)), ipiv, work, &lwork, info) + (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, n, A, max(1,stride(A,2)), ipiv, work, lwork, info) chkargsok(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -4675,9 +4675,9 @@ for (sysv, sytrf, sytri, sytrs, syconvf, elty, relty) in work = Vector{$elty}(n) info = Ref{BlasInt}() ccall((@blasfunc($sytri), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &uplo, &n, A, &max(1,stride(A,2)), ipiv, work, info) + uplo, n, A, max(1,stride(A,2)), ipiv, work, info) chklapackerror(info[]) A end @@ -4700,9 +4700,9 @@ for (sysv, sytrf, sytri, sytrs, syconvf, elty, relty) in end info = Ref{BlasInt}() ccall((@blasfunc($sytrs), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &n, &size(B,2), A, &max(1,stride(A,2)), ipiv, B, &max(1,stride(B,2)), info) + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), info) chklapackerror(info[]) B end @@ -4738,10 +4738,10 @@ for (sysv, sytrf, sytri, sytrs, syconvf, elty, relty) in info = Ref{BlasInt}() ccall((@blasfunc($syconvf), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &uplo, &way, &n, A, - &max(1, lda), ipiv, e, info) + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), + uplo, way, n, A, + max(1, lda), ipiv, e, info) chklapackerror(info[]) return A, e @@ -4869,9 +4869,9 @@ for (syev, syevr, sygvd, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($syev), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &jobz, &uplo, &n, A, &max(1,stride(A,2)), W, work, &lwork, info) + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + jobz, uplo, n, A, max(1,stride(A,2)), W, work, lwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -4919,17 +4919,17 @@ for (syev, syevr, sygvd, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] and liwork as iwork[1] ccall((@blasfunc($syevr), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, Ref{$elty}, + Ref{BlasInt}, Ref{BlasInt}, Ref{$elty}, Ptr{BlasInt}, + Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}), - &jobz, &range, &uplo, &n, - A, &max(1,lda), &vl, &vu, - &il, &iu, &abstol, m, - w, Z, &max(1,ldz), isuppz, - work, &lwork, iwork, &liwork, + jobz, range, uplo, n, + A, max(1,lda), vl, vu, + il, iu, abstol, m, + w, Z, max(1,ldz), isuppz, + work, lwork, iwork, liwork, info) chklapackerror(info[]) if i == 1 @@ -4970,14 +4970,14 @@ for (syev, syevr, sygvd, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] and liwork as iwork[1] ccall((@blasfunc($sygvd), liblapack), Void, - (Ptr{BlasInt}, Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{BlasInt}), - &itype, &jobz, &uplo, &n, - A, &lda, B, &ldb, - w, work, &lwork, iwork, - &liwork, info) + (Ref{BlasInt}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, + Ref{BlasInt}, Ptr{BlasInt}), + itype, jobz, uplo, n, + A, lda, B, ldb, + w, work, lwork, iwork, + liwork, info) chkargsok(info[]) if i == 1 lwork = BlasInt(work[1]) @@ -5014,9 +5014,9 @@ for (syev, syevr, sygvd, elty, relty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($syev), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$relty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$relty}, Ptr{BlasInt}), - &jobz, &uplo, &n, A, &stride(A,2), W, work, &lwork, rwork, info) + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$relty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{BlasInt}), + jobz, uplo, n, A, stride(A,2), W, work, lwork, rwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -5069,18 +5069,18 @@ for (syev, syevr, sygvd, elty, relty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1], lrwork as rwork[1] and liwork as iwork[1] ccall((@blasfunc($syevr), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$relty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$relty}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}), - &jobz, &range, &uplo, &n, - A, &lda, &vl, &vu, - &il, &iu, &abstol, m, - w, Z, &ldz, isuppz, - work, &lwork, rwork, &lrwork, - iwork, &liwork, info) + (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, Ref{$elty}, + Ref{BlasInt}, Ref{BlasInt}, Ref{$elty}, Ptr{BlasInt}, + Ptr{$relty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ref{BlasInt}, + Ptr{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}), + jobz, range, uplo, n, + A, lda, vl, vu, + il, iu, abstol, m, + w, Z, ldz, isuppz, + work, lwork, rwork, lrwork, + iwork, liwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -5124,14 +5124,14 @@ for (syev, syevr, sygvd, elty, relty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1], lrwork as rwork[1] and liwork as iwork[1] ccall((@blasfunc($sygvd), liblapack), Void, - (Ptr{BlasInt}, Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$relty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$relty}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}), - &itype, &jobz, &uplo, &n, - A, &lda, B, &ldb, - w, work, &lwork, rwork, - &lrwork, iwork, &liwork, info) + (Ref{BlasInt}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$relty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, + Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}), + itype, jobz, uplo, n, + A, lda, B, ldb, + w, work, lwork, rwork, + lrwork, iwork, liwork, info) chkargsok(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -5222,14 +5222,14 @@ for (bdsqr, relty, elty) in work = Vector{$relty}(4n) info = Ref{BlasInt}() ccall((@blasfunc($bdsqr), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$relty}, Ptr{BlasInt}), - &uplo, &n, &ncvt, &nru, - &ncc, d, e_, Vt, - &ldvt, U, &ldu, C, - &ldc, work, info) + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, + Ref{BlasInt}, Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$relty}, Ptr{BlasInt}), + uplo, n, ncvt, nru, + ncc, d, e_, Vt, + ldvt, U, ldu, C, + ldc, work, info) chklapackerror(info[]) d, Vt, U, C #singular values in descending order, P**T * VT, U * Q, Q**T * C end @@ -5292,11 +5292,11 @@ for (bdsdc, elty) in iwork = Vector{BlasInt}(8n) info = Ref{BlasInt}() ccall((@blasfunc($bdsdc), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &compq, &n, d, e_, - u, &ldu, vt, &ldvt, + uplo, compq, n, d, e_, + u, ldu, vt, ldvt, q, iq, work, iwork, info) chklapackerror(info[]) d, e, u, vt, q, iq @@ -5342,10 +5342,10 @@ for (gecon, elty) in iwork = Vector{BlasInt}(n) info = Ref{BlasInt}() ccall((@blasfunc($gecon), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ref{$elty}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ref{$elty}, Ref{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &normtype, &n, A, &lda, &anorm, rcond, work, iwork, + normtype, n, A, lda, anorm, rcond, work, iwork, info) chklapackerror(info[]) rcond[] @@ -5376,10 +5376,10 @@ for (gecon, elty, relty) in rwork = Vector{$relty}(2n) info = Ref{BlasInt}() ccall((@blasfunc($gecon), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$relty}, Ref{$relty}, Ptr{$elty}, Ptr{$relty}, + (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ref{$relty}, Ref{$relty}, Ptr{$elty}, Ptr{$relty}, Ptr{BlasInt}), - &normtype, &n, A, &lda, &anorm, rcond, work, rwork, + normtype, n, A, lda, anorm, rcond, work, rwork, info) chklapackerror(info[]) rcond[] @@ -5419,11 +5419,11 @@ for (gehrd, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($gehrd), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), - &n, &ilo, &ihi, A, - &max(1, stride(A, 2)), tau, work, &lwork, + n, ilo, ihi, A, + max(1, stride(A, 2)), tau, work, lwork, info) chklapackerror(info[]) if i == 1 @@ -5469,11 +5469,11 @@ for (orghr, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($orghr), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), - &n, &ilo, &ihi, A, - &max(1, stride(A, 2)), tau, work, &lwork, + n, ilo, ihi, A, + max(1, stride(A, 2)), tau, work, lwork, info) chklapackerror(info[]) if i == 1 @@ -5525,14 +5525,14 @@ for (ormhr, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($ormhr), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{BlasInt}), - &side, &trans, &mC, &nC, - &ilo, &ihi, A, &max(1, stride(A, 2)), - tau, C, &max(1, stride(C, 2)), work, - &lwork, info) + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, + Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ptr{BlasInt}), + side, trans, mC, nC, + ilo, ihi, A, max(1, stride(A, 2)), + tau, C, max(1, stride(C, 2)), work, + lwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -5569,14 +5569,14 @@ for (gees, gges, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($gees), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{Void}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{Void}, Ptr{BlasInt}), - &jobvs, &'N', C_NULL, &n, - A, &max(1, stride(A, 2)), sdim, wr, - wi, vs, &ldvs, work, - &lwork, C_NULL, info) + (Ref{UInt8}, Ref{UInt8}, Ptr{Void}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, + Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ptr{Void}, Ptr{BlasInt}), + jobvs, 'N', C_NULL, n, + A, max(1, stride(A, 2)), sdim, wr, + wi, vs, ldvs, work, + lwork, C_NULL, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -5614,17 +5614,17 @@ for (gees, gges, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($gges), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ptr{Void}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{Void}, + (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ptr{Void}, + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, + Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{Void}, Ptr{BlasInt}), - &jobvsl, &jobvsr, &'N', C_NULL, - &n, A, &max(1,stride(A, 2)), B, - &max(1,stride(B, 2)), &sdim, alphar, alphai, - beta, vsl, &ldvsl, vsr, - &ldvsr, work, &lwork, C_NULL, + jobvsl, jobvsr, 'N', C_NULL, + n, A, max(1,stride(A, 2)), B, + max(1,stride(B, 2)), sdim, alphar, alphai, + beta, vsl, ldvsl, vsr, + ldvsr, work, lwork, C_NULL, info) chklapackerror(info[]) if i == 1 @@ -5663,13 +5663,13 @@ for (gees, gges, elty, relty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($gees), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{Void}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{UInt8}, Ptr{Void}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{Void}, Ptr{BlasInt}), - &jobvs, &sort, C_NULL, &n, - A, &max(1, stride(A, 2)), &sdim, w, - vs, &ldvs, work, &lwork, + jobvs, sort, C_NULL, n, + A, max(1, stride(A, 2)), sdim, w, + vs, ldvs, work, lwork, rwork, C_NULL, info) chklapackerror(info[]) if i == 1 @@ -5709,17 +5709,17 @@ for (gees, gges, elty, relty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($gges), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ptr{Void}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$relty}, Ptr{Void}, + (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ptr{Void}, + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{Void}, Ptr{BlasInt}), - &jobvsl, &jobvsr, &'N', C_NULL, - &n, A, &max(1, stride(A, 2)), B, - &max(1, stride(B, 2)), &sdim, alpha, beta, - vsl, &ldvsl, vsr, &ldvsr, - work, &lwork, rwork, C_NULL, + jobvsl, jobvsr, 'N', C_NULL, + n, A, max(1, stride(A, 2)), B, + max(1, stride(B, 2)), sdim, alpha, beta, + vsl, ldvsl, vsr, ldvsr, + work, lwork, rwork, C_NULL, info) chklapackerror(info[]) if i == 1 @@ -5774,13 +5774,13 @@ for (trexc, trsen, tgsen, elty) in work = Vector{$elty}(n) info = Ref{BlasInt}() ccall((@blasfunc($trexc), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &compq, &n, - T, &ldt, Q, &ldq, - &ifst, &ilst, + compq, n, + T, ldt, Q, ldq, + ifst, ilst, work, info) chklapackerror(info[]) T, Q @@ -5814,15 +5814,15 @@ for (trexc, trsen, tgsen, elty) in select = convert(Array{BlasInt}, select) for i = 1:2 # first call returns lwork as work[1] and liwork as iwork[1] ccall((@blasfunc($trsen), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{Void}, Ptr{Void}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{UInt8}, Ptr{BlasInt}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{Void}, Ptr{Void}, + Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}), - &compq, &job, select, &n, - T, &ldt, Q, &ldq, - wr, wi, &m, C_NULL, C_NULL, - work, &lwork, iwork, &liwork, + compq, job, select, n, + T, ldt, Q, ldq, + wr, wi, m, C_NULL, C_NULL, + work, lwork, iwork, liwork, info) chklapackerror(info[]) if i == 1 # only estimated optimal lwork, liwork @@ -5879,19 +5879,19 @@ for (trexc, trsen, tgsen, elty) in select = convert(Array{BlasInt}, select) for i = 1:2 # first call returns lwork as work[1] and liwork as iwork[1] ccall((@blasfunc($tgsen), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{Void}, Ptr{Void}, Ptr{Void}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, + (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}, + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ref{BlasInt}, Ptr{Void}, Ptr{Void}, Ptr{Void}, + Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}), - &0, &1, &1, select, - &n, S, &lds, T, - &ldt, alphar, alphai, beta, - Q, &ldq, Z, &ldz, - &m, C_NULL, C_NULL, C_NULL, - work, &lwork, iwork, &liwork, + 0, 1, 1, select, + n, S, lds, T, + ldt, alphar, alphai, beta, + Q, ldq, Z, ldz, + m, C_NULL, C_NULL, C_NULL, + work, lwork, iwork, liwork, info) chklapackerror(info[]) if i == 1 # only estimated optimal lwork, liwork @@ -5923,13 +5923,13 @@ for (trexc, trsen, tgsen, elty) in ldq = max(1, stride(Q, 2)) info = Ref{BlasInt}() ccall((@blasfunc($trexc), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ref{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}), - &compq, &n, - T, &ldt, Q, &ldq, - &ifst, &ilst, + compq, n, + T, ldt, Q, ldq, + ifst, ilst, info) chklapackerror(info[]) T, Q @@ -5959,15 +5959,15 @@ for (trexc, trsen, tgsen, elty) in select = convert(Array{BlasInt}, select) for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($trsen), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{Void}, Ptr{Void}, - Ptr{$elty}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{UInt8}, Ptr{BlasInt}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{Void}, Ptr{Void}, + Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), - &compq, &job, select, &n, - T, &ldt, Q, &ldq, - w, &m, C_NULL, C_NULL, - work, &lwork, + compq, job, select, n, + T, ldt, Q, ldq, + w, m, C_NULL, C_NULL, + work, lwork, info) chklapackerror(info[]) if i == 1 # only estimated optimal lwork, liwork @@ -6021,19 +6021,19 @@ for (trexc, trsen, tgsen, elty) in select = convert(Array{BlasInt}, select) for i = 1:2 # first call returns lwork as work[1] and liwork as iwork[1] ccall((@blasfunc($tgsen), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{Void}, Ptr{Void}, Ptr{Void}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, + (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}, + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ref{BlasInt}, Ptr{Void}, Ptr{Void}, Ptr{Void}, + Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}), - &0, &1, &1, select, - &n, S, &lds, T, - &ldt, alpha, beta, - Q, &ldq, Z, &ldz, - &m, C_NULL, C_NULL, C_NULL, - work, &lwork, iwork, &liwork, + 0, 1, 1, select, + n, S, lds, T, + ldt, alpha, beta, + Q, ldq, Z, ldz, + m, C_NULL, C_NULL, C_NULL, + work, lwork, iwork, liwork, info) chklapackerror(info[]) if i == 1 # only estimated optimal lwork, liwork @@ -6100,11 +6100,11 @@ for (fn, elty, relty) in ((:dtrsyl_, :Float64, :Float64), scale = Vector{$relty}(1) info = Ref{BlasInt}() ccall((@blasfunc($fn), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{BlasInt}), - &transa, &transb, &isgn, &m, &n, - A, &lda, B, &ldb, C, &ldc, + transa, transb, isgn, m, n, + A, lda, B, ldb, C, ldc, scale, info) chklapackerror(info[]) C, scale[1] diff --git a/base/random/generation.jl b/base/random/generation.jl index 5c65633216e9e..e6174046fe45b 100644 --- a/base/random/generation.jl +++ b/base/random/generation.jl @@ -73,8 +73,8 @@ function rand(rng::AbstractRNG, gen::BigFloatRandGenerator, ::CloseOpen{BigFloat z.exp = 0 randbool && ccall((:mpfr_sub_d, :libmpfr), Int32, - (Ptr{BigFloat}, Ptr{BigFloat}, Cdouble, Int32), - &z, &z, 0.5, Base.MPFR.ROUNDING_MODE[]) + (Ref{BigFloat}, Ref{BigFloat}, Cdouble, Int32), + z, z, 0.5, Base.MPFR.ROUNDING_MODE[]) z end @@ -82,8 +82,8 @@ end # TODO: make an API for requesting full or not-full precision function rand(rng::AbstractRNG, gen::BigFloatRandGenerator, ::CloseOpen{BigFloat}, ::Void) z = rand(rng, Close1Open2(BigFloat), gen) - ccall((:mpfr_sub_ui, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Culong, Int32), - &z, &z, 1, Base.MPFR.ROUNDING_MODE[]) + ccall((:mpfr_sub_ui, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Culong, Int32), + z, z, 1, Base.MPFR.ROUNDING_MODE[]) z end diff --git a/base/sparse/cholmod.jl b/base/sparse/cholmod.jl index b29f4ed701103..34d1eaad041a1 100644 --- a/base/sparse/cholmod.jl +++ b/base/sparse/cholmod.jl @@ -993,7 +993,7 @@ end convert(::Type{Sparse}, A::Dense) = dense_to_sparse(A, SuiteSparse_long) convert(::Type{Sparse}, L::Factor) = factor_to_sparse!(copy(L)) -function (::Type{Sparse})(filename::String) +function Sparse(filename::String) open(filename) do f return read_sparse(f, SuiteSparse_long) end diff --git a/base/sparse/umfpack.jl b/base/sparse/umfpack.jl index d123ffe04581b..61cc9bf79baa5 100644 --- a/base/sparse/umfpack.jl +++ b/base/sparse/umfpack.jl @@ -337,11 +337,11 @@ for itype in UmfpackIndexTypes (Ptr{$itype},Ptr{$itype},Ptr{Float64}, Ptr{$itype},Ptr{$itype},Ptr{Float64}, Ptr{$itype},Ptr{$itype},Ptr{Void}, - Ptr{$itype},Ptr{Float64},Ptr{Void}), + Ref{$itype},Ptr{Float64},Ptr{Void}), Lp,Lj,Lx, Up,Ui,Ux, P, Q, C_NULL, - &0, Rs, lu.numeric) + 0, Rs, lu.numeric) (transpose(SparseMatrixCSC(min(n_row, n_col), n_row, increment!(Lp), increment!(Lj), Lx)), SparseMatrixCSC(min(n_row, n_col), n_col, increment!(Up), increment!(Ui), Ux), increment!(P), increment!(Q), Rs) @@ -364,11 +364,11 @@ for itype in UmfpackIndexTypes (Ptr{$itype},Ptr{$itype},Ptr{Float64},Ptr{Float64}, Ptr{$itype},Ptr{$itype},Ptr{Float64},Ptr{Float64}, Ptr{$itype},Ptr{$itype},Ptr{Void}, Ptr{Void}, - Ptr{$itype},Ptr{Float64},Ptr{Void}), + Ref{$itype},Ptr{Float64},Ptr{Void}), Lp,Lj,Lx,Lz, Up,Ui,Ux,Uz, P, Q, C_NULL, C_NULL, - &0, Rs, lu.numeric) + 0, Rs, lu.numeric) (transpose(SparseMatrixCSC(min(n_row, n_col), n_row, increment!(Lp), increment!(Lj), complex.(Lx, Lz))), SparseMatrixCSC(min(n_row, n_col), n_col, increment!(Up), increment!(Ui), complex.(Ux, Uz)), increment!(P), increment!(Q), Rs) diff --git a/doc/src/manual/calling-c-and-fortran-code.md b/doc/src/manual/calling-c-and-fortran-code.md index 7b7ec77e641f5..5aae9bc30e090 100644 --- a/doc/src/manual/calling-c-and-fortran-code.md +++ b/doc/src/manual/calling-c-and-fortran-code.md @@ -687,8 +687,8 @@ function compute_dot(DX::Vector{Float64}, DY::Vector{Float64}) incx = incy = 1 product = ccall((:ddot_, "libLAPACK"), Float64, - (Ptr{Int32}, Ptr{Float64}, Ptr{Int32}, Ptr{Float64}, Ptr{Int32}), - &n, DX, &incx, DY, &incy) + (Ref{Int32}, Ptr{Float64}, Ref{Int32}, Ptr{Float64}, Ref{Int32}), + n, DX, incx, DY, incy) return product end ``` diff --git a/doc/src/manual/methods.md b/doc/src/manual/methods.md index 489aa1b45ed8d..862297a52d20b 100644 --- a/doc/src/manual/methods.md +++ b/doc/src/manual/methods.md @@ -529,7 +529,7 @@ Here is the correct code template for returning the element-type `T` of any arbitrary subtype of `AbstractArray`: ```julia -abstract AbstractArray{T, N} +abstract type AbstractArray{T, N} end eltype(::Type{<:AbstractArray{T}}) where {T} = T ``` using so-called triangular dispatch. Note that if `T` is a `UnionAll` @@ -540,11 +540,11 @@ Another way, which used to be the only correct way before the advent of triangular dispatch in Julia v0.6, is: ```julia -abstract AbstractArray{T, N} +abstract type AbstractArray{T, N} end eltype(::Type{AbstractArray}) = Any -eltype{T}(::Type{AbstractArray{T}}) = T -eltype{T, N}(::Type{AbstractArray{T, N}}) = T -eltype{A<:AbstractArray}(::Type{A}) = eltype(supertype(A)) +eltype(::Type{AbstractArray{T}}) where {T} = T +eltype(::Type{AbstractArray{T, N}}) where {T, N} = T +eltype(::Type{A}) where {A<:AbstractArray} = eltype(supertype(A)) ``` Another possibility is the following, which could useful to adapt diff --git a/test/TestHelpers.jl b/test/TestHelpers.jl index 0ee2501159c98..b6513abfec3cb 100644 --- a/test/TestHelpers.jl +++ b/test/TestHelpers.jl @@ -129,8 +129,8 @@ OffsetVector{T,AA<:AbstractArray} = OffsetArray{T,1,AA} OffsetArray(A::AbstractArray{T,N}, offsets::NTuple{N,Int}) where {T,N} = OffsetArray{T,N,typeof(A)}(A, offsets) OffsetArray(A::AbstractArray{T,N}, offsets::Vararg{Int,N}) where {T,N} = OffsetArray(A, offsets) -(::Type{OffsetArray{T,N}})(inds::Indices{N}) where {T,N} = OffsetArray{T,N,Array{T,N}}(Array{T,N}(map(length, inds)), map(indsoffset, inds)) -(::Type{OffsetArray{T}})(inds::Indices{N}) where {T,N} = OffsetArray{T,N}(inds) +OffsetArray{T,N}(inds::Indices{N}) where {T,N} = OffsetArray{T,N,Array{T,N}}(Array{T,N}(map(length, inds)), map(indsoffset, inds)) +OffsetArray{T}(inds::Indices{N}) where {T,N} = OffsetArray{T,N}(inds) Base.IndexStyle(::Type{T}) where {T<:OffsetArray} = Base.IndexStyle(parenttype(T)) parenttype(::Type{OffsetArray{T,N,AA}}) where {T,N,AA} = AA diff --git a/test/ccall.jl b/test/ccall.jl index 9ce6c764de033..1e131f4da296a 100644 --- a/test/ccall.jl +++ b/test/ccall.jl @@ -82,7 +82,7 @@ let a, ci_ary, x @test x == a + 1 - 2im @test a == 20 + 51im - x = ccall((:cptest_static, libccalltest), Ptr{Complex{Int}}, (Ptr{Complex{Int}},), &a) + x = ccall((:cptest_static, libccalltest), Ptr{Complex{Int}}, (Ref{Complex{Int}},), a) @test unsafe_load(x) == a Libc.free(convert(Ptr{Void}, x)) end diff --git a/test/dimensionful.jl b/test/dimensionful.jl index 999f362cc27a7..bb3b5ac2043af 100644 --- a/test/dimensionful.jl +++ b/test/dimensionful.jl @@ -12,7 +12,7 @@ struct Furlong{p,T<:Number} <: Number end Furlong(x::T) where {T<:Number} = Furlong{1,T}(x) (::Type{T})(x::Furlong{p,T}) where {p,T} = x.val -(::Type{Furlong{p}})(v::Number) where {p} = Furlong{p,typeof(v)}(v) +Furlong{p}(v::Number) where {p} = Furlong{p,typeof(v)}(v) Base.convert(::Type{Furlong{p,T}}, x::Furlong{p,S}) where {T,p,S} = Furlong{p,T}(convert(T,x.val)) Base.convert(::Type{Furlong{0,T}}, x::Furlong{0}) where {T} = Furlong{0,T}(convert(T, x.val)) Base.convert(::Type{T}, x::Furlong{0}) where {T<:Number} = convert(T, x.val) diff --git a/test/inference.jl b/test/inference.jl index 6efe8b19b2f21..4bcb3b9c01ff7 100644 --- a/test/inference.jl +++ b/test/inference.jl @@ -817,7 +817,7 @@ end struct NArray_17003{T,N} <: AArray_17003{Nable_17003{T},N} end -(::Type{NArray_17003})(::Array{T,N}) where {T,N} = NArray_17003{T,N}() +NArray_17003(::Array{T,N}) where {T,N} = NArray_17003{T,N}() gl_17003 = [1, 2, 3] diff --git a/test/perf/array/indexing.jl b/test/perf/array/indexing.jl index 36527b0036a8d..eda9b1a71541a 100644 --- a/test/perf/array/indexing.jl +++ b/test/perf/array/indexing.jl @@ -112,7 +112,7 @@ end struct ArrayLSLS{T,N} <: MyArray{T,N} # IndexCartesian with IndexCartesian similar data::Array{T,N} end -Base.similar{T}(A::ArrayLSLS, ::Type{T}, dims::Tuple{Vararg{Int}}) = ArrayLSLS(similar(A.data, T, dims)) +Base.similar(A::ArrayLSLS, ::Type{T}, dims::Tuple{Vararg{Int}}) where {T} = ArrayLSLS(similar(A.data, T, dims)) @inline Base.setindex!(A::ArrayLSLS, v, I::Int...) = A.data[I...] = v @inline Base.unsafe_setindex!(A::ArrayLSLS, v, I::Int...) = Base.unsafe_setindex!(A.data, v, I...) Base.first(A::ArrayLSLS) = first(A.data) @@ -140,15 +140,15 @@ Base.size(A::MyArray) = size(A.data) @inline Base.unsafe_getindex(A::ArrayLF, indx::Int) = unsafe_getindex(A.data, indx) @inline Base.unsafe_getindex(A::Union{ArrayLS, ArrayLSLS}, i::Int, j::Int) = unsafe_getindex(A.data, i, j) -@inline Base.getindex{T}(A::ArrayStrides{T,2}, i::Real, j::Real) = getindex(A.data, 1+A.strides[1]*(i-1)+A.strides[2]*(j-1)) +@inline Base.getindex(A::ArrayStrides{T,2}, i::Real, j::Real) where {T} = getindex(A.data, 1+A.strides[1]*(i-1)+A.strides[2]*(j-1)) @inline Base.getindex(A::ArrayStrides1, i::Real, j::Real) = getindex(A.data, i + A.stride1*(j-1)) -@inline Base.unsafe_getindex{T}(A::ArrayStrides{T,2}, i::Real, j::Real) = unsafe_getindex(A.data, 1+A.strides[1]*(i-1)+A.strides[2]*(j-1)) +@inline Base.unsafe_getindex(A::ArrayStrides{T,2}, i::Real, j::Real) where {T} = unsafe_getindex(A.data, 1+A.strides[1]*(i-1)+A.strides[2]*(j-1)) @inline Base.unsafe_getindex(A::ArrayStrides1, i::Real, j::Real) = unsafe_getindex(A.data, i + A.stride1*(j-1)) # Using the qualified Base.IndexLinear() in the IndexStyle definition # requires looking up the symbol in the module on each call. import Base: IndexLinear -Base.IndexStyle{T<:ArrayLF}(::Type{T}) = IndexLinear() +Base.IndexStyle(::Type{T}) where {T<:ArrayLF} = IndexLinear() if !applicable(unsafe_getindex, [1 2], 1:1, 2) @inline Base.unsafe_getindex(A::Array, I...) = @inbounds return A[I...] @@ -157,7 +157,7 @@ if !applicable(unsafe_getindex, [1 2], 1:1, 2) @inline Base.unsafe_getindex(A::BitArray, I1::BitArray, I2::Int) = unsafe_getindex(A, Base.to_index(I1), I2) end -function makearrays{T}(::Type{T}, sz) +function makearrays(::Type{T}, sz) where T L = prod(sz) A = reshape(convert(Vector{T}, [1:L;]), sz) AS = ArrayLS(A) diff --git a/test/reflection.jl b/test/reflection.jl index 11c23ea08f96d..37a31875d4fbf 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -609,7 +609,7 @@ end mutable struct A18434 end -(::Type{A18434})(x; y=1) = 1 +A18434(x; y=1) = 1 global counter18434 = 0 function get_A18434() diff --git a/test/replutil.jl b/test/replutil.jl index f233b3854f582..37d169f1e1afb 100644 --- a/test/replutil.jl +++ b/test/replutil.jl @@ -356,7 +356,7 @@ Base.Symbol() = throw(ErrorException("1")) EightBitType() = throw(ErrorException("3")) (::EightBitType)() = throw(ErrorException("4")) EightBitTypeT() = throw(ErrorException("5")) -(::Type{EightBitTypeT{T}})() where {T} = throw(ErrorException("6")) +EightBitTypeT{T}() where {T} = throw(ErrorException("6")) (::EightBitTypeT)() = throw(ErrorException("7")) (::FunctionLike)() = throw(ErrorException("8")) From 144c46be0ad98bc0dd40b3e3c5783b1cb12d2f92 Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Mon, 28 Aug 2017 14:22:55 -0700 Subject: [PATCH 183/324] Make describe docs more accurate/informative (#23477) * Make describe docs more accurate/informative --- base/libgit2/repository.jl | 22 ++++++++++++++++++---- doc/src/devdocs/libgit2.md | 2 ++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/base/libgit2/repository.jl b/base/libgit2/repository.jl index 38a326f5b6823..4f169add16115 100644 --- a/base/libgit2/repository.jl +++ b/base/libgit2/repository.jl @@ -251,7 +251,16 @@ contains detailed information about it based on the keyword argument: * `options::DescribeOptions=DescribeOptions()` -Equivalent to `git describe `. +A git decription of a `commitish` object looks for the tag (by default, annotated, +although a search of all tags can be performed) which can be reached from `commitish` +which is most recent. If the tag is pointing to `commitish`, then only the tag is +included in the description. Otherwise, a suffix is included which contains the +number of commits between `commitish` and the most recent tag. If there is no such +tag, the default behavior is for the description to fail, although this can be +changed through `options`. + +Equivalent to `git describe `. See [`DescribeOptions`](@ref) for more +information. """ function GitDescribeResult(commitish::GitObject; options::DescribeOptions=DescribeOptions()) @@ -265,14 +274,19 @@ end """ LibGit2.GitDescribeResult(repo::GitRepo; kwarg...) -Produce a `GitDescribeResult` of the repository `repo`'s working directory, -which can include all the commits and tags (or, for instance, HEAD only). +Produce a `GitDescribeResult` of the repository `repo`'s working directory. The `GitDescribeResult` contains detailed information about the workdir based on the keyword argument: * `options::DescribeOptions=DescribeOptions()` -Equivalent to `git describe`. +In this case, the description is run on HEAD, producing the most recent tag +which is an ancestor of HEAD. Afterwards, a status check on +the [`workdir`](@ref) is performed and if the `workdir` is dirty +(see [`isdirty`](@ref)) the description is also considered dirty. + +Equivalent to `git describe`. See [`DescribeOptions`](@ref) for more +information. """ function GitDescribeResult(repo::GitRepo; options::DescribeOptions=DescribeOptions()) result_ptr_ptr = Ref{Ptr{Void}}(C_NULL) diff --git a/doc/src/devdocs/libgit2.md b/doc/src/devdocs/libgit2.md index b9b279d6a0eba..13709659c3c1f 100644 --- a/doc/src/devdocs/libgit2.md +++ b/doc/src/devdocs/libgit2.md @@ -17,6 +17,8 @@ Base.LibGit2.Buffer Base.LibGit2.CachedCredentials Base.LibGit2.CheckoutOptions Base.LibGit2.CloneOptions +Base.LibGit2.DescribeOptions +Base.LibGit2.DescribeFormatOptions Base.LibGit2.DiffDelta Base.LibGit2.DiffFile Base.LibGit2.DiffOptionsStruct From 6ff8d137fbae840e51d8eb24ed542e92aad67ee5 Mon Sep 17 00:00:00 2001 From: kshyatt Date: Sun, 27 Aug 2017 14:00:02 -0700 Subject: [PATCH 184/324] Doc SigStruct and DescribeOptions --- base/libgit2/types.jl | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/base/libgit2/types.jl b/base/libgit2/types.jl index 874b93f919ecd..f74a1499d1008 100644 --- a/base/libgit2/types.jl +++ b/base/libgit2/types.jl @@ -50,6 +50,12 @@ end An action signature (e.g. for committers, taggers, etc). Matches the [`git_signature`](https://libgit2.github.com/libgit2/#HEAD/type/git_signature) struct. + +The fields represent: + * `name`: The full name of the committer or author of the commit. + * `email`: The email at which the committer/author can be contacted. + * `when`: a [`TimeStruct`](@ref) indicating when the commit was + authored/committed into the repository. """ struct SignatureStruct name::Ptr{UInt8} # full name of the author @@ -359,6 +365,20 @@ end LibGit2.DescribeOptions Matches the [`git_describe_options`](https://libgit2.github.com/libgit2/#HEAD/type/git_describe_options) struct. + +The fields represent: + * `version`: version of the struct in use, in case this changes later. For now, always `1`. + * `max_candidates_tags`: consider this many most recent tags in `refs/tags` to describe a commit. + Defaults to 10 (so that the 10 most recent tags would be examined to see if they describe a commit). + * `describe_strategy`: whether to consider all entries in `refs/tags` (equivalent to `git-describe --tags`) + or all entries in `refs/` (equivalent to `git-describe --all`). The default is to only show annotated tags. + If `Consts.DESCRIBE_TAGS` is passed, all tags, annotated or not, will be considered. + If `Consts.DESCRIBE_ALL` is passed, any ref in `refs/` will be considered. + * `pattern`: only consider tags which match `pattern`. Supports glob expansion. + * `only_follow_first_parent`: when finding the distance from a matching reference to the described + object, only consider the distance from the first parent. + * `show_commit_oid_as_fallback`: if no matching reference can be found which describes a commit, show the + commit's [`GitHash`](@ref) instead of throwing an error (the default behavior). """ @kwdef struct DescribeOptions version::Cuint = 1 From a48a7d2fbba45063c73ec2040f15e57500fbe9b8 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 28 Aug 2017 14:40:10 -0400 Subject: [PATCH 185/324] fix missing propagate_inbounds from ordering comparison --- base/ordering.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/ordering.jl b/base/ordering.jl index 95b86f4be57f0..52d065b4d36ad 100644 --- a/base/ordering.jl +++ b/base/ordering.jl @@ -48,12 +48,12 @@ lt(o::By, a, b) = isless(o.by(a),o.by(b)) lt(o::Lt, a, b) = o.lt(a,b) lt(o::LexicographicOrdering, a, b) = lexcmp(a,b) < 0 -function lt(p::Perm, a::Integer, b::Integer) +Base.@propagate_inbounds function lt(p::Perm, a::Integer, b::Integer) da = p.data[a] db = p.data[b] lt(p.order, da, db) | (!lt(p.order, db, da) & (a < b)) end -function lt(p::Perm{LexicographicOrdering}, a::Integer, b::Integer) +Base.@propagate_inbounds function lt(p::Perm{LexicographicOrdering}, a::Integer, b::Integer) c = lexcmp(p.data[a], p.data[b]) c != 0 ? c < 0 : a < b end From d9dde5d02fa0fa385d2a32910194975f45d67fcd Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 28 Jun 2017 19:19:09 -0400 Subject: [PATCH 186/324] revise boundscheck structure it is much easier if this value gets treated as a normal parameter as that allows all of the normal control flow logic to apply rather than require a complete reimplementation of it --- base/array.jl | 11 +- base/codevalidation.jl | 2 +- base/essentials.jl | 21 +- base/inference.jl | 346 ++++++++++----------------------- base/tuple.jl | 8 +- doc/src/devdocs/ast.md | 4 +- doc/src/devdocs/boundscheck.md | 12 +- src/builtins.c | 44 +++-- src/cgutils.cpp | 48 +++-- src/codegen.cpp | 95 ++++----- src/common_symbols1.inc | 1 - src/common_symbols2.inc | 2 +- src/dump.c | 2 +- src/interpreter.c | 5 +- src/julia-syntax.scm | 7 +- src/julia_internal.h | 4 +- test/boundscheck_exec.jl | 20 +- test/core.jl | 13 +- test/staged.jl | 2 +- 19 files changed, 256 insertions(+), 391 deletions(-) diff --git a/base/array.jl b/base/array.jl index 1141b364e2c0a..63e01f564203e 100644 --- a/base/array.jl +++ b/base/array.jl @@ -760,8 +760,8 @@ julia> getindex(A, "a") function getindex end # This is more complicated than it needs to be in order to get Win64 through bootstrap -getindex(A::Array, i1::Int) = arrayref(A, i1) -getindex(A::Array, i1::Int, i2::Int, I::Int...) = (@_inline_meta; arrayref(A, i1, i2, I...)) # TODO: REMOVE FOR #14770 +@eval getindex(A::Array, i1::Int) = arrayref($(Expr(:boundscheck)), A, i1) +@eval getindex(A::Array, i1::Int, i2::Int, I::Int...) = (@_inline_meta; arrayref($(Expr(:boundscheck)), A, i1, i2, I...)) # TODO: REMOVE FOR #14770 # Faster contiguous indexing using copy! for UnitRange and Colon function getindex(A::Array, I::UnitRange{Int}) @@ -798,8 +798,9 @@ x` is converted by the compiler to `(setindex!(a, x, i, j, ...); x)`. """ function setindex! end -setindex!(A::Array{T}, x, i1::Int) where {T} = arrayset(A, convert(T,x)::T, i1) -setindex!(A::Array{T}, x, i1::Int, i2::Int, I::Int...) where {T} = (@_inline_meta; arrayset(A, convert(T,x)::T, i1, i2, I...)) # TODO: REMOVE FOR #14770 +@eval setindex!(A::Array{T}, x, i1::Int) where {T} = arrayset($(Expr(:boundscheck)), A, convert(T,x)::T, i1) +@eval setindex!(A::Array{T}, x, i1::Int, i2::Int, I::Int...) where {T} = + (@_inline_meta; arrayset($(Expr(:boundscheck)), A, convert(T,x)::T, i1, i2, I...)) # TODO: REMOVE FOR #14770 # These are redundant with the abstract fallbacks but needed for bootstrap function setindex!(A::Array, x, I::AbstractVector{Int}) @@ -905,7 +906,7 @@ end function push!(a::Array{Any,1}, @nospecialize item) _growend!(a, 1) - arrayset(a, item, length(a)) + arrayset(true, a, item, length(a)) return a end diff --git a/base/codevalidation.jl b/base/codevalidation.jl index 10507aea2f386..433eaa7bab5a6 100644 --- a/base/codevalidation.jl +++ b/base/codevalidation.jl @@ -18,7 +18,7 @@ const VALID_EXPR_HEADS = ObjectIdDict( :enter => 1:1, :leave => 1:1, :inbounds => 1:1, - :boundscheck => 1:1, + :boundscheck => 0:0, :copyast => 1:1, :meta => 0:typemax(Int), :global => 1:1, diff --git a/base/essentials.jl b/base/essentials.jl index fe5a1646ed3ef..fcb94f19822f8 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -379,7 +379,7 @@ function append_any(xs...) ccall(:jl_array_grow_end, Void, (Any, UInt), out, 16) l += 16 end - Core.arrayset(out, y, i) + Core.arrayset(true, out, y, i) i += 1 end end @@ -388,7 +388,7 @@ function append_any(xs...) end # simple Array{Any} operations needed for bootstrap -setindex!(A::Array{Any}, @nospecialize(x), i::Int) = Core.arrayset(A, x, i) +@eval setindex!(A::Array{Any}, @nospecialize(x), i::Int) = Core.arrayset($(Expr(:boundscheck)), A, x, i) """ precompile(f, args::Tuple{Vararg{Any}}) @@ -413,10 +413,7 @@ section of the Metaprogramming chapter of the manual for more details and exampl esc(@nospecialize(e)) = Expr(:escape, e) macro boundscheck(blk) - # hack: use this syntax since it avoids introducing line numbers - :($(Expr(:boundscheck,true)); - $(esc(blk)); - $(Expr(:boundscheck,:pop))) + return Expr(:if, Expr(:boundscheck), esc(blk)) end """ @@ -424,7 +421,8 @@ end Eliminates array bounds checking within expressions. -In the example below the bound check of array A is skipped to improve performance. +In the example below the in-range check for referencing +element `i` of array `A` is skipped to improve performance. ```julia function sum(A::AbstractArray) @@ -442,9 +440,10 @@ end for out-of-bounds indices. The user is responsible for checking it manually. """ macro inbounds(blk) - :($(Expr(:inbounds,true)); - $(esc(blk)); - $(Expr(:inbounds,:pop))) + return Expr(:block, + Expr(:inbounds, true), + esc(blk), + Expr(:inbounds, :pop)) end macro label(name::Symbol) @@ -565,7 +564,7 @@ function vector_any(@nospecialize xs...) n = length(xs) a = Vector{Any}(n) @inbounds for i = 1:n - Core.arrayset(a,xs[i],i) + Core.arrayset(false, a, xs[i], i) end a end diff --git a/base/inference.jl b/base/inference.jl index d2255af9fc87b..3e0c25f5ad701 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -1135,6 +1135,8 @@ function const_datatype_getfield_tfunc(sv, fld) return nothing end +getfield_tfunc(@nospecialize(s00), @nospecialize(name), @nospecialize(inbounds)) = + getfield_tfunc(s00, name) function getfield_tfunc(@nospecialize(s00), @nospecialize(name)) if isa(s00, TypeVar) s00 = s00.ub @@ -1255,8 +1257,10 @@ function getfield_tfunc(@nospecialize(s00), @nospecialize(name)) # in the current type system return rewrap_unionall(limit_type_depth(R, MAX_TYPE_DEPTH), s00) end -add_tfunc(getfield, 2, 2, (@nospecialize(s), @nospecialize(name)) -> getfield_tfunc(s, name), 1) +add_tfunc(getfield, 2, 3, getfield_tfunc, 1) add_tfunc(setfield!, 3, 3, (@nospecialize(o), @nospecialize(f), @nospecialize(v)) -> v, 3) +fieldtype_tfunc(@nospecialize(s0), @nospecialize(name), @nospecialize(inbounds)) = + fieldtype_tfunc(s0, name) function fieldtype_tfunc(@nospecialize(s0), @nospecialize(name)) if s0 === Any || s0 === Type || DataType ⊑ s0 || UnionAll ⊑ s0 return Type @@ -1316,7 +1320,7 @@ function fieldtype_tfunc(@nospecialize(s0), @nospecialize(name)) end return Type{<:ft} end -add_tfunc(fieldtype, 2, 2, fieldtype_tfunc, 0) +add_tfunc(fieldtype, 2, 3, fieldtype_tfunc, 0) function valid_tparam(@nospecialize(x)) if isa(x,Tuple) @@ -1510,27 +1514,25 @@ function builtin_tfunction(@nospecialize(f), argtypes::Array{Any,1}, elseif f === svec return SimpleVector elseif f === arrayset - if length(argtypes) < 3 && !isva + if length(argtypes) < 4 + isva && return Any return Bottom end - a1 = argtypes[1] - if isvarargtype(a1) - return unwrap_unionall(a1).parameters[1] - end - return a1 + return argtypes[2] elseif f === arrayref - if length(argtypes) < 2 && !isva + if length(argtypes) < 3 + isva && return Any return Bottom end - a = widenconst(argtypes[1]) + a = widenconst(argtypes[2]) if a <: Array - if isa(a,DataType) && (isa(a.parameters[1],Type) || isa(a.parameters[1],TypeVar)) + if isa(a, DataType) && (isa(a.parameters[1], Type) || isa(a.parameters[1], TypeVar)) # TODO: the TypeVar case should not be needed here a = a.parameters[1] - return isa(a,TypeVar) ? a.ub : a - elseif isa(a,UnionAll) && !has_free_typevars(a) + return isa(a, TypeVar) ? a.ub : a + elseif isa(a, UnionAll) && !has_free_typevars(a) unw = unwrap_unionall(a) - if isa(unw,DataType) + if isa(unw, DataType) return rewrap_unionall(unw.parameters[1], a) end end @@ -2445,6 +2447,8 @@ function abstract_eval(@nospecialize(e), vtypes::VarTable, sv::InferenceState) return abstract_eval_constant(e.args[1]) elseif e.head === :invoke error("type inference data-flow error: tried to double infer a function") + elseif e.head === :boundscheck + return Bool elseif e.head === :isdefined sym = e.args[1] t = Bool @@ -3369,7 +3373,7 @@ function optimize(me::InferenceState) type_annotate!(me) # run optimization passes on fulltree - force_noinline = false + force_noinline = true if me.optimize # This pass is required for the AST to be valid in codegen # if any `SSAValue` is created by type inference. Ref issue #6068 @@ -3377,11 +3381,7 @@ function optimize(me::InferenceState) # if we start to create `SSAValue` in type inference when not # optimizing and use unoptimized IR in codegen. gotoifnot_elim_pass!(me) - inlining_pass!(me) - # probably not an ideal location for most of these steps, - # but boundscheck elimination is not idempotent and needs to run as part of inlining - code = me.src.code::Array{Any,1} - meta_elim_pass!(code, me.src.propagate_inbounds, coverage_enabled()) + inlining_pass!(me, me.src.propagate_inbounds) # Clean up after inlining gotoifnot_elim_pass!(me) basic_dce_pass!(me) @@ -3392,9 +3392,12 @@ function optimize(me::InferenceState) getfield_elim_pass!(me) # Clean up for `alloc_elim_pass!` and `getfield_elim_pass!` void_use_elim_pass!(me) - filter!(x -> x !== nothing, code) # Pop metadata before label reindexing - force_noinline = popmeta!(code, :noinline)[1] + let code = me.src.code::Array{Any,1} + meta_elim_pass!(code, coverage_enabled()) + filter!(x -> x !== nothing, code) + force_noinline = popmeta!(code, :noinline)[1] + end reindex_labels!(me) end @@ -3749,7 +3752,10 @@ end # replace slots 1:na with argexprs, static params with spvals, and increment # other slots by offset. -function substitute!(@nospecialize(e), na::Int, argexprs::Vector{Any}, @nospecialize(spsig), spvals::Vector{Any}, offset::Int) +function substitute!( + @nospecialize(e), na::Int, argexprs::Vector{Any}, + @nospecialize(spsig), spvals::Vector{Any}, + offset::Int, boundscheck::Symbol) if isa(e, Slot) id = slot_id(e) if 1 <= id <= na @@ -3766,7 +3772,7 @@ function substitute!(@nospecialize(e), na::Int, argexprs::Vector{Any}, @nospecia end end if isa(e, NewvarNode) - return NewvarNode(substitute!(e.slot, na, argexprs, spsig, spvals, offset)) + return NewvarNode(substitute!(e.slot, na, argexprs, spsig, spvals, offset, boundscheck)) end if isa(e, Expr) e = e::Expr @@ -3776,7 +3782,7 @@ function substitute!(@nospecialize(e), na::Int, argexprs::Vector{Any}, @nospecia is_self_quoting(sp) && return sp return QuoteNode(sp) elseif head === :foreigncall - @assert !isa(spsig,UnionAll) || !isempty(spvals) + @assert !isa(spsig, UnionAll) || !isempty(spvals) for i = 1:length(e.args) if i == 2 e.args[2] = ccall(:jl_instantiate_type_in_env, Any, (Any, Any, Ptr{Any}), e.args[2], spsig, spvals) @@ -3791,12 +3797,20 @@ function substitute!(@nospecialize(e), na::Int, argexprs::Vector{Any}, @nospecia elseif i == 5 @assert isa(e.args[5], Int) else - e.args[i] = substitute!(e.args[i], na, argexprs, spsig, spvals, offset) + e.args[i] = substitute!(e.args[i], na, argexprs, spsig, spvals, offset, boundscheck) end end + elseif head === :boundscheck + if boundscheck === :propagate + return e + elseif boundscheck === :off + return false + else + return true + end elseif !is_meta_expr_head(head) for i = 1:length(e.args) - e.args[i] = substitute!(e.args[i], na, argexprs, spsig, spvals, offset) + e.args[i] = substitute!(e.args[i], na, argexprs, spsig, spvals, offset, boundscheck) end end end @@ -3912,9 +3926,10 @@ function effect_free(@nospecialize(e), src::CodeInfo, mod::Module, allow_volatil if is_known_call(e, arrayref, src, mod) || is_known_call(e, arraylen, src, mod) return false elseif is_known_call(e, getfield, src, mod) - length(ea) == 3 || return false + nargs = length(ea) + (nargs == 3 || nargs == 4) || return false et = exprtype(e, src, mod) - if !isa(et,Const) && !(isType(et) && isleaftype(et)) + if !isa(et, Const) && !(isType(et) && isleaftype(et)) # first argument must be immutable to ensure e is affect_free a = ea[2] typ = widenconst(exprtype(a, src, mod)) @@ -4184,8 +4199,9 @@ end # `ft` is the type of the function. `f` is the exact function if known, or else `nothing`. # `pending_stmts` is an array of statements from functions inlined so far, so # we can estimate the total size of the enclosing function after inlining. -function inlineable(@nospecialize(f), @nospecialize(ft), e::Expr, atypes::Vector{Any}, sv::InferenceState, - pending_stmts) +function inlineable(@nospecialize(f), @nospecialize(ft), e::Expr, atypes::Vector{Any}, + pending_stmt::Vector{Any}, boundscheck::Symbol, + sv::InferenceState) argexprs = e.args if (f === typeassert || ft ⊑ typeof(typeassert)) && length(atypes)==3 @@ -4487,7 +4503,6 @@ function inlineable(@nospecialize(f), @nospecialize(ft), e::Expr, atypes::Vector body = Expr(:block) body.args = ast - propagate_inbounds = src.propagate_inbounds # see if each argument occurs only once in the body expression stmts = [] @@ -4548,7 +4563,7 @@ function inlineable(@nospecialize(f), @nospecialize(ft), e::Expr, atypes::Vector end # ok, substitute argument expressions for argument names in the body - body = substitute!(body, na, argexprs, method.sig, Any[methsp...], length(sv.src.slotnames) - na) + body = substitute!(body, na, argexprs, method.sig, Any[methsp...], length(sv.src.slotnames) - na, boundscheck) append!(sv.src.slotnames, src.slotnames[(na + 1):end]) append!(sv.src.slottypes, src.slottypes[(na + 1):end]) append!(sv.src.slotflags, src.slotflags[(na + 1):end]) @@ -4669,22 +4684,6 @@ function inlineable(@nospecialize(f), @nospecialize(ft), e::Expr, atypes::Vector push!(stmts, Expr(:meta, :pop_loc)) end end - if !isempty(stmts) && !propagate_inbounds - # avoid redundant inbounds annotations - s_1, s_end = stmts[1], stmts[end] - 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 - else - # inlined statements are out-of-bounds by default - unshift!(stmts, Expr(:inbounds, false)) - push!(stmts, Expr(:inbounds, :pop)) - end - end if isa(expr, Expr) old_t = e.typ @@ -4827,18 +4826,54 @@ function mk_tuplecall(args, sv::InferenceState) return e end -function inlining_pass!(sv::InferenceState) +function inlining_pass!(sv::InferenceState, propagate_inbounds::Bool) + # Also handles bounds check elision: + # + # 1. If check_bounds is always on, set `Expr(:boundscheck)` true + # 2. If check_bounds is always off, set `Expr(:boundscheck)` false + # 3. If check_bounds is default, figure out whether each boundscheck + # is true, false, or propagate based on the enclosing inbounds directives + _opt_check_bounds = JLOptions().check_bounds + opt_check_bounds = (_opt_check_bounds == 0 ? :default : + _opt_check_bounds == 1 ? :on : + :off) + # Number of stacked inbounds + inbounds_depth = 0 + eargs = sv.src.code i = 1 stmtbuf = [] while i <= length(eargs) ei = eargs[i] if isa(ei, Expr) - eargs[i] = inlining_pass(ei, sv, stmtbuf, 1) - if !isempty(stmtbuf) - splice!(eargs, i:i-1, stmtbuf) - i += length(stmtbuf) - empty!(stmtbuf) + if ei.head === :inbounds + eargs[i] = nothing + arg1 = ei.args[1] + if arg1 === true # push + inbounds_depth += 1 + elseif arg1 === false # clear + inbounds_depth = 0 + elseif inbounds_depth > 0 # pop + inbounds_depth -= 1 + end + else + if opt_check_bounds === :off + boundscheck = :off + elseif opt_check_bounds === :on + boundscheck = :on + elseif inbounds_depth > 0 + boundscheck = :off + elseif propagate_inbounds + boundscheck = :propagate + else + boundscheck = :on + end + eargs[i] = inlining_pass(ei, sv, stmtbuf, 1, boundscheck) + if !isempty(stmtbuf) + splice!(eargs, i:(i - 1), stmtbuf) + i += length(stmtbuf) + empty!(stmtbuf) + end end end i += 1 @@ -4849,7 +4884,7 @@ const corenumtype = Union{Int32, Int64, Float32, Float64} # return inlined replacement for `e`, inserting new needed statements # at index `ins` in `stmts`. -function inlining_pass(e::Expr, sv::InferenceState, stmts, ins) +function inlining_pass(e::Expr, sv::InferenceState, stmts::Vector{Any}, ins, boundscheck::Symbol) if e.head === :meta # ignore meta nodes return e @@ -4859,10 +4894,14 @@ function inlining_pass(e::Expr, sv::InferenceState, stmts, ins) return e end # inliners for special expressions + if e.head === :boundscheck + return e + end if e.head === :isdefined isa(e.typ, Const) && return e.typ.val return e end + eargs = e.args if length(eargs) < 1 return e @@ -4905,7 +4944,7 @@ function inlining_pass(e::Expr, sv::InferenceState, stmts, ins) argloc = eargs end sl0 = length(stmts) - res = inlining_pass(ei, sv, stmts, ins) + res = inlining_pass(ei, sv, stmts, ins, boundscheck) ns = length(stmts) - sl0 # number of new statements just added if isccallee restype = exprtype(res, sv.src, sv.mod) @@ -4997,11 +5036,11 @@ function inlining_pass(e::Expr, sv::InferenceState, stmts, ins) exprtype(a1, sv.src, sv.mod) ⊑ basenumtype) if square e.args = Any[GlobalRef(Main.Base,:*), a1, a1] - res = inlining_pass(e, sv, stmts, ins) + res = inlining_pass(e, sv, stmts, ins, boundscheck) else e.args = Any[GlobalRef(Main.Base,:*), Expr(:call, GlobalRef(Main.Base,:*), a1, a1), a1] e.args[2].typ = e.typ - res = inlining_pass(e, sv, stmts, ins) + res = inlining_pass(e, sv, stmts, ins, boundscheck) end return res end @@ -5017,7 +5056,7 @@ function inlining_pass(e::Expr, sv::InferenceState, stmts, ins) (a === Bottom || isvarargtype(a)) && return e ata[i] = a end - res = inlineable(f, ft, e, ata, sv, stmts) + res = inlineable(f, ft, e, ata, stmts, boundscheck, sv) if isa(res,Tuple) if isa(res[2],Array) && !isempty(res[2]) splice!(stmts, ins:ins-1, res[2]) @@ -5416,7 +5455,7 @@ function void_use_elim_pass!(sv::InferenceState) nothing end -function meta_elim_pass!(code::Array{Any,1}, propagate_inbounds::Bool, do_coverage::Bool) +function meta_elim_pass!(code::Array{Any,1}, do_coverage::Bool) # 1. Remove place holders # # 2. If coverage is off, remove line number nodes that don't mark any @@ -5424,54 +5463,6 @@ function meta_elim_pass!(code::Array{Any,1}, propagate_inbounds::Bool, do_covera # # 3. Remove top level SSAValue # - # 4. Handle bounds check elision - # - # 4.1. If check_bounds is always on, delete all `Expr(:boundscheck)` - # 4.2. If check_bounds is always off, delete all boundscheck blocks. - # 4.3. If check_bounds is default, figure out whether each checkbounds - # blocks needs to be eliminated or could be eliminated when inlined - # into another function. Delete the blocks that should be eliminated - # and delete the `Expr(:boundscheck)` for blocks that will never be - # deleted. (i.e. the ones that are not eliminated with - # `length(inbounds_stack) >= 2`) - # - # When deleting IR with boundscheck, keep the label node in order to not - # confuse later passes or codegen. (we could also track if any SSAValue - # is deleted while still having uses that are not but that's a little - # expensive). - # - # 5. Clean up `Expr(:inbounds)` - # - # Delete all `Expr(:inbounds)` that is unnecessary, which is all of them - # for non-default check_bounds. For default check_bounds this includes - # - # * `Expr(:inbounds, true)` in `Expr(:inbounds, true)` - # * `Expr(:inbounds, false)` when - # `!is_inbounds && length(inbounds_stack) >= 2` - # - # Functions without `propagate_inbounds` have an implicit `false` on the - # `inbounds_stack` - # - # There are other cases in which we can eliminate `Expr(:inbounds)` or - # `Expr(:boundscheck)` (e.g. when they don't enclose any non-meta - # expressions). Those are a little harder to detect and are hopefully - # not too common. - check_bounds = JLOptions().check_bounds - - inbounds_stack = propagate_inbounds ? Bool[] : Bool[false] - # Whether the push is deleted (therefore if the pop has to be too) - # Shared for `Expr(:boundscheck)` and `Expr(:inbounds)` - bounds_elim_stack = Bool[] - # The expression index of the push, set to `0` when encountering a - # non-meta expression that might be affect by the push. - # The clearing needs to be propagated up during pop - # This is not pushed to if the push is already eliminated - # Also shared for `Expr(:boundscheck)` and `Expr(:inbounds)` - bounds_push_pos_stack = Int[0] # always non-empty - # Number of boundscheck pushes in a eliminated boundscheck block - void_boundscheck_depth = 0 - is_inbounds = check_bounds == 2 - enabled = true # Position of the last line number node without any non-meta expressions # in between. @@ -5499,140 +5490,16 @@ function meta_elim_pass!(code::Array{Any,1}, propagate_inbounds::Bool, do_covera prev_dbg_stack[end] = i continue elseif !isa(ex, Expr) - if enabled - prev_dbg_stack[end] = 0 - push_loc_pos_stack[end] = 0 - bounds_push_pos_stack[end] = 0 - else - code[i] = nothing - end + prev_dbg_stack[end] = 0 + push_loc_pos_stack[end] = 0 continue end ex = ex::Expr args = ex.args head = ex.head - if head === :boundscheck - if !enabled - # we are in an eliminated boundscheck, simply record the number - # of push/pop - if !(args[1] === :pop) - void_boundscheck_depth += 1 - elseif void_boundscheck_depth == 0 - # There must have been a push - pop!(bounds_elim_stack) - enabled = true - else - void_boundscheck_depth -= 1 - end - code[i] = nothing - elseif args[1] === :pop - # This will also delete pops that don't match - if (isempty(bounds_elim_stack) ? true : - pop!(bounds_elim_stack)) - code[i] = nothing - continue - end - push_idx = bounds_push_pos_stack[end] - if length(bounds_push_pos_stack) > 1 - pop!(bounds_push_pos_stack) - end - if push_idx > 0 - code[push_idx] = nothing - code[i] = nothing - else - bounds_push_pos_stack[end] = 0 - end - elseif is_inbounds - code[i] = nothing - push!(bounds_elim_stack, true) - enabled = false - elseif check_bounds == 1 || length(inbounds_stack) >= 2 - # Not inbounds and at least two levels deep, this will never - # be eliminated when inlined to another function. - code[i] = nothing - push!(bounds_elim_stack, true) - else - push!(bounds_elim_stack, false) - push!(bounds_push_pos_stack, i) - end - continue - end - if !enabled && !(do_coverage && head === :meta) - code[i] = nothing - continue - end - if head === :inbounds - if check_bounds != 0 - code[i] = nothing - continue - end - arg1 = args[1] - if arg1 === true - if !isempty(inbounds_stack) && inbounds_stack[end] - code[i] = nothing - push!(bounds_elim_stack, true) - else - is_inbounds = true - push!(bounds_elim_stack, false) - push!(bounds_push_pos_stack, i) - end - push!(inbounds_stack, true) - elseif arg1 === false - if is_inbounds - # There must have been a `true` on the stack so - # `inbounds_stack` must not be empty - if !inbounds_stack[end] - is_inbounds = false - end - push!(bounds_elim_stack, false) - push!(bounds_push_pos_stack, i) - elseif length(inbounds_stack) >= 2 - code[i] = nothing - push!(bounds_elim_stack, true) - else - push!(bounds_elim_stack, false) - push!(bounds_push_pos_stack, i) - end - push!(inbounds_stack, false) - else - # pop - inbounds_len = length(inbounds_stack) - if inbounds_len != 0 - pop!(inbounds_stack) - inbounds_len -= 1 - end - # This will also delete pops that don't match - if (isempty(bounds_elim_stack) ? true : - pop!(bounds_elim_stack)) - # No need to update `is_inbounds` since the push was a no-op - code[i] = nothing - continue - end - if inbounds_len >= 2 - is_inbounds = (inbounds_stack[inbounds_len] || - inbounds_stack[inbounds_len - 1]) - elseif inbounds_len == 1 - is_inbounds = inbounds_stack[inbounds_len] - else - is_inbounds = false - end - push_idx = bounds_push_pos_stack[end] - if length(bounds_push_pos_stack) > 1 - pop!(bounds_push_pos_stack) - end - if push_idx > 0 - code[push_idx] = nothing - code[i] = nothing - else - bounds_push_pos_stack[end] = 0 - end - end - continue - end if head !== :meta prev_dbg_stack[end] = 0 push_loc_pos_stack[end] = 0 - bounds_push_pos_stack[end] = 0 continue end nargs = length(args) @@ -5683,11 +5550,14 @@ function getfield_elim_pass!(sv::InferenceState) end function _getfield_elim_pass!(e::Expr, sv::InferenceState) - for i = 1:length(e.args) + nargs = length(e.args) + for i = 1:nargs e.args[i] = _getfield_elim_pass!(e.args[i], sv) end - if is_known_call(e, getfield, sv.src, sv.mod) && length(e.args)==3 && - (isa(e.args[3],Int) || isa(e.args[3],QuoteNode)) + if is_known_call(e, getfield, sv.src, sv.mod) && + (nargs == 3 || nargs == 4) && + (isa(e.args[3], Int) || isa(e.args[3], QuoteNode)) && + (nargs == 3 || isa(e.args[4], Bool)) e1 = e.args[2] j = e.args[3] single_use = true diff --git a/base/tuple.jl b/base/tuple.jl index bab87697249e6..961107884466f 100644 --- a/base/tuple.jl +++ b/base/tuple.jl @@ -17,11 +17,11 @@ NTuple length(t::Tuple) = nfields(t) endof(t::Tuple) = length(t) -size(t::Tuple, d) = d==1 ? length(t) : throw(ArgumentError("invalid tuple dimension $d")) -getindex(t::Tuple, i::Int) = getfield(t, i) -getindex(t::Tuple, i::Real) = getfield(t, convert(Int, i)) +size(t::Tuple, d) = (d == 1) ? length(t) : throw(ArgumentError("invalid tuple dimension $d")) +@eval getindex(t::Tuple, i::Int) = getfield(t, i, $(Expr(:boundscheck))) +@eval getindex(t::Tuple, i::Real) = getfield(t, convert(Int, i), $(Expr(:boundscheck))) getindex(t::Tuple, r::AbstractArray{<:Any,1}) = ([t[ri] for ri in r]...) -getindex(t::Tuple, b::AbstractArray{Bool,1}) = length(b) == length(t) ? getindex(t,find(b)) : throw(BoundsError(t, b)) +getindex(t::Tuple, b::AbstractArray{Bool,1}) = length(b) == length(t) ? getindex(t, find(b)) : throw(BoundsError(t, b)) # returns new tuple; N.B.: becomes no-op if i is out-of-bounds setindex(x::Tuple, v, i::Integer) = (@_inline_meta; _setindex(v, i, x...)) diff --git a/doc/src/devdocs/ast.md b/doc/src/devdocs/ast.md index 88df6ae64f613..3e1aad5a394eb 100644 --- a/doc/src/devdocs/ast.md +++ b/doc/src/devdocs/ast.md @@ -170,8 +170,8 @@ These symbols appear in the `head` field of `Expr`s in lowered form. * `boundscheck` - Indicates the beginning or end of a section of code that performs a bounds check. Like `inbounds`, - a stack is maintained, and the second argument can be one of: `true`, `false`, or `:pop`. + Has the value `false` if inlined into a section of code marked with `@inbounds`, + otherwise has the value `true`. * `copyast` diff --git a/doc/src/devdocs/boundscheck.md b/doc/src/devdocs/boundscheck.md index 0647e01a722f1..9eed150ea1497 100644 --- a/doc/src/devdocs/boundscheck.md +++ b/doc/src/devdocs/boundscheck.md @@ -5,17 +5,15 @@ accessing arrays. In tight inner loops or other performance critical situations, to skip these bounds checks to improve runtime performance. For instance, in order to emit vectorized (SIMD) instructions, your loop body cannot contain branches, and thus cannot contain bounds checks. Consequently, Julia includes an `@inbounds(...)` macro to tell the compiler to skip such bounds -checks within the given block. For the built-in `Array` type, the magic happens inside the `arrayref` -and `arrayset` intrinsics. User-defined array types instead use the `@boundscheck(...)` macro +checks within the given block. User-defined array types can use the `@boundscheck(...)` macro to achieve context-sensitive code selection. ## Eliding bounds checks -The `@boundscheck(...)` macro marks blocks of code that perform bounds checking. When such blocks -appear inside of an `@inbounds(...)` block, the compiler removes these blocks. When the `@boundscheck(...)` -is nested inside of a calling function containing an `@inbounds(...)`, the compiler will remove -the `@boundscheck` block *only if it is inlined* into the calling function. For example, you might -write the method `sum` as: +The `@boundscheck(...)` macro marks blocks of code that perform bounds checking. +When such blocks are inlined into an `@inbounds(...)` block, the compiler may remove these blocks. +The compiler removes the `@boundscheck` block *only if it is inlined* into the calling function. +For example, you might write the method `sum` as: ```julia function sum(A::AbstractArray) diff --git a/src/builtins.c b/src/builtins.c index c3f89ab676558..06800206647b0 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -609,6 +609,10 @@ JL_CALLABLE(jl_f_svec) JL_CALLABLE(jl_f_getfield) { + if (nargs == 3) { + JL_TYPECHK(getfield, bool, args[2]); + nargs -= 1; + } JL_NARGS(getfield, 2, 2); jl_value_t *v = args[0]; jl_value_t *vt = (jl_value_t*)jl_typeof(v); @@ -700,6 +704,10 @@ static jl_value_t *get_fieldtype(jl_value_t *t, jl_value_t *f) JL_CALLABLE(jl_f_fieldtype) { + if (nargs == 3) { + JL_TYPECHK(fieldtype, bool, args[2]); + nargs -= 1; + } JL_NARGS(fieldtype, 2, 2); jl_datatype_t *st = (jl_datatype_t*)args[0]; if (st == jl_module_type) @@ -925,20 +933,20 @@ JL_CALLABLE(jl_f_arraysize) static size_t array_nd_index(jl_array_t *a, jl_value_t **args, size_t nidxs, const char *fname) { - size_t i=0; - size_t k, stride=1; + size_t i = 0; + size_t k, stride = 1; size_t nd = jl_array_ndims(a); - for(k=0; k < nidxs; k++) { + for (k = 0; k < nidxs; k++) { if (!jl_is_long(args[k])) jl_type_error(fname, (jl_value_t*)jl_long_type, args[k]); - size_t ii = jl_unbox_long(args[k])-1; + size_t ii = jl_unbox_long(args[k]) - 1; i += ii * stride; - size_t d = k>=nd ? 1 : jl_array_dim(a, k); - if (k < nidxs-1 && ii >= d) + size_t d = (k >= nd) ? 1 : jl_array_dim(a, k); + if (k < nidxs - 1 && ii >= d) jl_bounds_error_v((jl_value_t*)a, args, nidxs); stride *= d; } - for(; k < nd; k++) + for (; k < nd; k++) stride *= jl_array_dim(a, k); if (i >= stride) jl_bounds_error_v((jl_value_t*)a, args, nidxs); @@ -947,21 +955,23 @@ static size_t array_nd_index(jl_array_t *a, jl_value_t **args, size_t nidxs, JL_CALLABLE(jl_f_arrayref) { - JL_NARGSV(arrayref, 2); - JL_TYPECHK(arrayref, array, args[0]); - jl_array_t *a = (jl_array_t*)args[0]; - size_t i = array_nd_index(a, &args[1], nargs-1, "arrayref"); + JL_NARGSV(arrayref, 3); + JL_TYPECHK(arrayref, bool, args[0]); + JL_TYPECHK(arrayref, array, args[1]); + jl_array_t *a = (jl_array_t*)args[1]; + size_t i = array_nd_index(a, &args[2], nargs - 2, "arrayref"); return jl_arrayref(a, i); } JL_CALLABLE(jl_f_arrayset) { - JL_NARGSV(arrayset, 3); - JL_TYPECHK(arrayset, array, args[0]); - jl_array_t *a = (jl_array_t*)args[0]; - size_t i = array_nd_index(a, &args[2], nargs-2, "arrayset"); - jl_arrayset(a, args[1], i); - return args[0]; + JL_NARGSV(arrayset, 4); + JL_TYPECHK(arrayset, bool, args[0]); + JL_TYPECHK(arrayset, array, args[1]); + jl_array_t *a = (jl_array_t*)args[1]; + size_t i = array_nd_index(a, &args[3], nargs - 3, "arrayset"); + jl_arrayset(a, args[2], i); + return args[1]; } // IntrinsicFunctions --------------------------------------------------------- diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 63434b182c0d3..df23794663189 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -1089,24 +1089,29 @@ static void emit_leafcheck(jl_codectx_t &ctx, Value *typ, const std::string &msg } #define CHECK_BOUNDS 1 -static bool bounds_check_enabled(jl_codectx_t &ctx) { +static bool bounds_check_enabled(jl_codectx_t &ctx, jl_value_t *inbounds) { #if CHECK_BOUNDS==1 - return (!ctx.is_inbounds && - jl_options.check_bounds != JL_OPTIONS_CHECK_BOUNDS_OFF) || - jl_options.check_bounds == JL_OPTIONS_CHECK_BOUNDS_ON; + if (jl_options.check_bounds == JL_OPTIONS_CHECK_BOUNDS_ON) + return 1; + if (jl_options.check_bounds == JL_OPTIONS_CHECK_BOUNDS_OFF) + return 0; + if (inbounds == jl_false) + return 0; + return 1; #else return 0; #endif } -static Value *emit_bounds_check(jl_codectx_t &ctx, const jl_cgval_t &ainfo, jl_value_t *ty, Value *i, Value *len) +static Value *emit_bounds_check(jl_codectx_t &ctx, const jl_cgval_t &ainfo, jl_value_t *ty, Value *i, Value *len, jl_value_t *boundscheck) { Value *im1 = ctx.builder.CreateSub(i, ConstantInt::get(T_size, 1)); + jl_cgval_t ib = emit_expr(ctx, boundscheck); #if CHECK_BOUNDS==1 - if (bounds_check_enabled(ctx)) { + if (bounds_check_enabled(ctx, ib.constant)) { Value *ok = ctx.builder.CreateICmpULT(im1, len); - BasicBlock *failBB = BasicBlock::Create(jl_LLVMContext,"fail",ctx.f); - BasicBlock *passBB = BasicBlock::Create(jl_LLVMContext,"pass"); + BasicBlock *failBB = BasicBlock::Create(jl_LLVMContext, "fail", ctx.f); + BasicBlock *passBB = BasicBlock::Create(jl_LLVMContext, "pass"); ctx.builder.CreateCondBr(ok, passBB, failBB); ctx.builder.SetInsertPoint(failBB); if (!ty) { // jl_value_t** tuple (e.g. the vararg) @@ -1260,12 +1265,12 @@ static Value *data_pointer(jl_codectx_t &ctx, const jl_cgval_t &x, Type *astype static bool emit_getfield_unknownidx(jl_codectx_t &ctx, jl_cgval_t *ret, const jl_cgval_t &strct, - Value *idx, jl_datatype_t *stt) + Value *idx, jl_datatype_t *stt, jl_value_t *inbounds) { size_t nfields = jl_datatype_nfields(stt); if (strct.ispointer()) { // boxed or stack if (is_datatype_all_pointers(stt)) { - idx = emit_bounds_check(ctx, strct, (jl_value_t*)stt, idx, ConstantInt::get(T_size, nfields)); + idx = emit_bounds_check(ctx, strct, (jl_value_t*)stt, idx, ConstantInt::get(T_size, nfields), inbounds); bool maybe_null = (unsigned)stt->ninitialized != nfields; size_t minimum_field_size = (size_t)-1; for (size_t i = 0; i < nfields; ++i) { @@ -1288,7 +1293,7 @@ static bool emit_getfield_unknownidx(jl_codectx_t &ctx, else if (is_tupletype_homogeneous(stt->types)) { assert(nfields > 0); // nf == 0 trapped by all_pointers case jl_value_t *jt = jl_field_type(stt, 0); - idx = emit_bounds_check(ctx, strct, (jl_value_t*)stt, idx, ConstantInt::get(T_size, nfields)); + idx = emit_bounds_check(ctx, strct, (jl_value_t*)stt, idx, ConstantInt::get(T_size, nfields), inbounds); Value *ptr = data_pointer(ctx, strct); if (!stt->mutabl) { // just compute the pointer and let user load it when necessary @@ -1312,14 +1317,15 @@ static bool emit_getfield_unknownidx(jl_codectx_t &ctx, else if (is_tupletype_homogeneous(stt->types)) { assert(jl_isbits(stt)); if (nfields == 0) { - idx = emit_bounds_check(ctx, ghostValue(stt), - (jl_value_t*)stt, idx, ConstantInt::get(T_size, nfields)); + idx = emit_bounds_check( + ctx, ghostValue(stt), (jl_value_t*)stt, + idx, ConstantInt::get(T_size, nfields), inbounds); *ret = jl_cgval_t(); return true; } assert(!jl_field_isptr(stt, 0)); jl_value_t *jt = jl_field_type(stt, 0); - Value *idx0 = emit_bounds_check(ctx, strct, (jl_value_t*)stt, idx, ConstantInt::get(T_size, nfields)); + Value *idx0 = emit_bounds_check(ctx, strct, (jl_value_t*)stt, idx, ConstantInt::get(T_size, nfields), inbounds); if (strct.isghost) { *ret = ghostValue(jt); return true; @@ -1628,23 +1634,23 @@ static Value *emit_arraysize_for_unsafe_dim(jl_codectx_t &ctx, } // `nd == -1` means the dimension is unknown. -static Value *emit_array_nd_index(jl_codectx_t &ctx, - const jl_cgval_t &ainfo, jl_value_t *ex, ssize_t nd, const jl_cgval_t *argv, size_t nidxs) +static Value *emit_array_nd_index( + jl_codectx_t &ctx, const jl_cgval_t &ainfo, jl_value_t *ex, ssize_t nd, + const jl_cgval_t *argv, size_t nidxs, jl_value_t *inbounds) { Value *a = boxed(ctx, ainfo); Value *i = ConstantInt::get(T_size, 0); Value *stride = ConstantInt::get(T_size, 1); + jl_cgval_t ib = emit_expr(ctx, inbounds); #if CHECK_BOUNDS==1 - bool bc = (!ctx.is_inbounds && - jl_options.check_bounds != JL_OPTIONS_CHECK_BOUNDS_OFF) || - jl_options.check_bounds == JL_OPTIONS_CHECK_BOUNDS_ON; - BasicBlock *failBB=NULL, *endBB=NULL; + bool bc = bounds_check_enabled(ctx, ib.constant); + BasicBlock *failBB = NULL, *endBB = NULL; if (bc) { failBB = BasicBlock::Create(jl_LLVMContext, "oob"); endBB = BasicBlock::Create(jl_LLVMContext, "idxend"); } #endif - Value **idxs = (Value**)alloca(sizeof(Value*)*nidxs); + Value **idxs = (Value**)alloca(sizeof(Value*) * nidxs); for (size_t k = 0; k < nidxs; k++) { idxs[k] = emit_unbox(ctx, T_size, argv[k], NULL); } diff --git a/src/codegen.cpp b/src/codegen.cpp index ca125ac26d66d..b6dd9091a9d7c 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -560,8 +560,6 @@ class jl_codectx_t { Value *world_age_field = NULL; bool debug_enabled = false; - bool is_inbounds = false; - const jl_cgparams_t *params = NULL; jl_codectx_t(LLVMContext &llvmctx) @@ -1821,7 +1819,7 @@ static jl_value_t *static_eval(jl_codectx_t &ctx, jl_value_t *ex, int sparams=tr if (e->head == call_sym) { jl_value_t *f = static_eval(ctx, jl_exprarg(e, 0), sparams, allow_alloc); if (f) { - if (jl_array_dim0(e->args) == 3 && f==jl_builtin_getfield) { + if (jl_array_dim0(e->args) == 3 && f == jl_builtin_getfield) { m = (jl_module_t*)static_eval(ctx, jl_exprarg(e, 1), sparams, allow_alloc); // Check the tag before evaluating `s` so that a value of random // type won't be corrupted. @@ -2381,10 +2379,10 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, } } - else if (f == jl_builtin_arrayref && nargs >= 2) { - const jl_cgval_t &ary = argv[1]; + else if (f == jl_builtin_arrayref && nargs >= 3) { + const jl_cgval_t &ary = argv[2]; bool indexes_ok = true; - for (size_t i = 2; i <= nargs; i++) { + for (size_t i = 3; i <= nargs; i++) { if (argv[i].typ != (jl_value_t*)jl_long_type) { indexes_ok = false; break; @@ -2394,14 +2392,15 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, if (jl_is_array_type(aty_dt) && indexes_ok) { jl_value_t *ety = jl_tparam0(aty_dt); jl_value_t *ndp = jl_tparam1(aty_dt); - if (!jl_has_free_typevars(ety) && (jl_is_long(ndp) || nargs == 2)) { - jl_value_t *ary_ex = jl_exprarg(ex, 1); + if (!jl_has_free_typevars(ety) && (jl_is_long(ndp) || nargs == 3)) { + jl_value_t *ary_ex = jl_exprarg(ex, 2); size_t elsz = 0, al = 0; bool isboxed = !jl_islayout_inline(ety, &elsz, &al); if (isboxed) ety = (jl_value_t*)jl_any_type; ssize_t nd = jl_is_long(ndp) ? jl_unbox_long(ndp) : -1; - Value *idx = emit_array_nd_index(ctx, ary, ary_ex, nd, &argv[2], nargs - 1); + jl_value_t *boundscheck = argv[1].constant; + Value *idx = emit_array_nd_index(ctx, ary, ary_ex, nd, &argv[3], nargs - 2, boundscheck); if (!isboxed && jl_is_datatype(ety) && jl_datatype_size(ety) == 0) { assert(((jl_datatype_t*)ety)->instance != NULL); *ret = ghostValue(ety); @@ -2423,19 +2422,21 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, *ret = mark_julia_slot(lv, ety, tindex, tbaa_stack); } else { - *ret = typed_load(ctx, emit_arrayptr(ctx, ary, ary_ex), idx, ety, - !isboxed ? tbaa_arraybuf : tbaa_ptrarraybuf); + *ret = typed_load(ctx, + emit_arrayptr(ctx, ary, ary_ex), + idx, ety, + !isboxed ? tbaa_arraybuf : tbaa_ptrarraybuf); } return true; } } } - else if (f == jl_builtin_arrayset && nargs >= 3) { - const jl_cgval_t &ary = argv[1]; - const jl_cgval_t &val = argv[2]; + else if (f == jl_builtin_arrayset && nargs >= 4) { + const jl_cgval_t &ary = argv[2]; + const jl_cgval_t &val = argv[3]; bool indexes_ok = true; - for (size_t i = 3; i <= nargs; i++) { + for (size_t i = 4; i <= nargs; i++) { if (argv[i].typ != (jl_value_t*)jl_long_type) { indexes_ok = false; break; @@ -2445,15 +2446,16 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, if (jl_is_array_type(aty_dt) && indexes_ok) { jl_value_t *ety = jl_tparam0(aty_dt); jl_value_t *ndp = jl_tparam1(aty_dt); - if (!jl_has_free_typevars(ety) && (jl_is_long(ndp) || nargs == 3)) { + if (!jl_has_free_typevars(ety) && (jl_is_long(ndp) || nargs == 4)) { if (jl_subtype(val.typ, ety)) { // TODO: probably should just convert this to a type-assert size_t elsz = 0, al = 0; bool isboxed = !jl_islayout_inline(ety, &elsz, &al); if (isboxed) ety = (jl_value_t*)jl_any_type; - jl_value_t *ary_ex = jl_exprarg(ex, 1); + jl_value_t *ary_ex = jl_exprarg(ex, 2); ssize_t nd = jl_is_long(ndp) ? jl_unbox_long(ndp) : -1; - Value *idx = emit_array_nd_index(ctx, ary, ary_ex, nd, &argv[3], nargs - 2); + jl_value_t *boundscheck = argv[1].constant; + Value *idx = emit_array_nd_index(ctx, ary, ary_ex, nd, &argv[4], nargs - 3, boundscheck); if (!isboxed && jl_is_datatype(ety) && jl_datatype_size(ety) == 0) { // no-op } @@ -2531,7 +2533,7 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, } } - else if (f == jl_builtin_getfield && nargs == 2) { + else if (f == jl_builtin_getfield && (nargs == 2 || nargs == 3)) { const jl_cgval_t &obj = argv[1]; const jl_cgval_t &fld = argv[2]; if (fld.constant && fld.typ == (jl_value_t*)jl_symbol_type) { @@ -2549,7 +2551,8 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, ctx.builder.CreateGEP(ctx.argArray, ConstantInt::get(T_size, ctx.nReqArgs)), NULL, false, NULL, NULL); Value *idx = emit_unbox(ctx, T_size, fld, (jl_value_t*)jl_long_type); - idx = emit_bounds_check(ctx, va_ary, NULL, idx, valen); + jl_value_t *boundscheck = (nargs == 3 ? argv[3].constant : jl_true); + idx = emit_bounds_check(ctx, va_ary, NULL, idx, valen, boundscheck); idx = ctx.builder.CreateAdd(idx, ConstantInt::get(T_size, ctx.nReqArgs)); Value *v = tbaa_decorate(tbaa_value, ctx.builder.CreateLoad(ctx.builder.CreateGEP(ctx.argArray, idx))); *ret = mark_julia_type(ctx, v, /*boxed*/ true, jl_any_type, /*needsgcroot*/ false); @@ -2572,7 +2575,8 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, else { // unknown index Value *vidx = emit_unbox(ctx, T_size, fld, (jl_value_t*)jl_long_type); - if (emit_getfield_unknownidx(ctx, ret, obj, vidx, utt)) { + jl_value_t *boundscheck = (nargs == 3 ? argv[3].constant : jl_true); + if (emit_getfield_unknownidx(ctx, ret, obj, vidx, utt, boundscheck)) { return true; } } @@ -2591,11 +2595,13 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, Value *vidx = emit_unbox(ctx, T_size, fld, (jl_value_t*)jl_long_type); // This is not necessary for correctness, but allows to omit // the extra code for getting the length of the tuple - if (!bounds_check_enabled(ctx)) { + jl_value_t *boundscheck = (nargs == 3 ? argv[3].constant : jl_true); + if (!bounds_check_enabled(ctx, boundscheck)) { vidx = ctx.builder.CreateSub(vidx, ConstantInt::get(T_size, 1)); } else { vidx = emit_bounds_check(ctx, obj, (jl_value_t*)obj.typ, vidx, - emit_datatype_nfields(ctx, emit_typeof_boxed(ctx, obj))); + emit_datatype_nfields(ctx, emit_typeof_boxed(ctx, obj)), + jl_true); } Value *ptr = data_pointer(ctx, obj); *ret = typed_load(ctx, ptr, vidx, jt, obj.tbaa, false); @@ -2672,7 +2678,7 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, } } - else if (f == jl_builtin_fieldtype && nargs == 2) { + else if (f == jl_builtin_fieldtype && (nargs == 2 || nargs == 3)) { const jl_cgval_t &typ = argv[1]; const jl_cgval_t &fld = argv[2]; if ((jl_is_type_type(typ.typ) && jl_is_leaf_type(jl_tparam0(typ.typ))) || @@ -2684,7 +2690,8 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, Value *types_svec = emit_datatype_types(ctx, tyv); Value *types_len = emit_datatype_nfields(ctx, tyv); Value *idx = emit_unbox(ctx, T_size, fld, (jl_value_t*)jl_long_type); - emit_bounds_check(ctx, typ, (jl_value_t*)jl_datatype_type, idx, types_len); + jl_value_t *boundscheck = (nargs == 3 ? argv[3].constant : jl_true); + emit_bounds_check(ctx, typ, (jl_value_t*)jl_datatype_type, idx, types_len, boundscheck); Value *fieldtyp_p = ctx.builder.CreateGEP(decay_derived(emit_bitcast(ctx, types_svec, T_pprjlvalue)), idx); Value *fieldtyp = tbaa_decorate(tbaa_const, ctx.builder.CreateLoad(fieldtyp_p)); *ret = mark_julia_type(ctx, fieldtyp, true, (jl_value_t*)jl_type_type); @@ -3648,8 +3655,7 @@ static void emit_stmtpos(jl_codectx_t &ctx, jl_value_t *expr) jl_expr_t *ex = (jl_expr_t*)expr; jl_value_t **args = (jl_value_t**)jl_array_data(ex->args); jl_sym_t *head = ex->head; - if (head == line_sym || head == meta_sym || head == boundscheck_sym || - head == inbounds_sym) { + if (head == line_sym || head == meta_sym || head == inbounds_sym) { // some expression types are metadata and can be ignored // in statement position return; @@ -3894,7 +3900,7 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr) jl_error("Expr(:inbounds) in value position"); } else if (head == boundscheck_sym) { - jl_error("Expr(:boundscheck) in value position"); + return mark_julia_const(bounds_check_enabled(ctx, jl_true) ? jl_true : jl_false); } else { if (!strcmp(jl_symbol_name(head), "$")) @@ -5323,24 +5329,14 @@ static std::unique_ptr emit_function( DebugLoc loc; StringRef file; ssize_t line; - bool is_inbounds; bool loc_changed; bool is_poploc; bool in_user_code; }; std::vector stmtprops(stmtslen); std::vector DI_stack; - std::vector inbounds_stack{false}; - auto is_inbounds = [&] () { - // inbounds rule is either of top two values on inbounds stack are true - size_t sz = inbounds_stack.size(); - bool inbounds = sz && inbounds_stack.back(); - if (sz > 1) - inbounds |= inbounds_stack[sz - 2]; - return inbounds; - }; StmtProp cur_prop{topdebugloc, filename, toplineno, - false, true, false, false}; + true, false, false}; ctx.line = &cur_prop.line; if (coverage_mode != JL_LOG_NONE || malloc_log_mode) { cur_prop.in_user_code = (!jl_is_submodule(ctx.module, jl_base_module) && @@ -5447,29 +5443,9 @@ static std::unique_ptr emit_function( cur_prop.loc_changed = true; } } - if (expr) { - jl_value_t **args = (jl_value_t**)jl_array_data(expr->args); - if (expr->head == inbounds_sym) { - // manipulate inbounds stack - if (jl_array_len(expr->args) > 0) { - jl_value_t *arg = args[0]; - if (arg == jl_true) { - inbounds_stack.push_back(true); - } - else if (arg == jl_false) { - inbounds_stack.push_back(false); - } - else if (!inbounds_stack.empty()) { - inbounds_stack.pop_back(); - } - } - } - } - cur_prop.is_inbounds = is_inbounds(); stmtprops[i] = cur_prop; } DI_stack.clear(); - inbounds_stack.clear(); // step 12. Do codegen in control flow order std::vector> workstack; @@ -5572,7 +5548,6 @@ static std::unique_ptr emit_function( !props.is_poploc) { coverageVisitLine(ctx, props.file, props.line); } - ctx.is_inbounds = props.is_inbounds; jl_value_t *stmt = jl_array_ptr_ref(stmts, cursor); jl_expr_t *expr = jl_is_expr(stmt) ? (jl_expr_t*)stmt : nullptr; if (jl_is_labelnode(stmt)) { diff --git a/src/common_symbols1.inc b/src/common_symbols1.inc index 013dfdd15dc06..09b1cf5c6adb5 100644 --- a/src/common_symbols1.inc +++ b/src/common_symbols1.inc @@ -14,7 +14,6 @@ jl_symbol("getindex"), jl_symbol("new"), jl_symbol("arrayref"), jl_symbol("static_parameter"), -jl_symbol("abstractarray.jl"), jl_symbol("slt_int"), jl_symbol("convert"), jl_symbol("start"), diff --git a/src/common_symbols2.inc b/src/common_symbols2.inc index 4554dfef79b5b..0a1d0eb73cbea 100644 --- a/src/common_symbols2.inc +++ b/src/common_symbols2.inc @@ -249,4 +249,4 @@ jl_symbol("NF"), jl_symbol("isvarargtype"), jl_symbol("n"), jl_symbol("inferred"), -jl_symbol("eachindex"), +jl_symbol("abstractarray.jl"), diff --git a/src/dump.c b/src/dump.c index f7840eb8042c6..d30a0416628ff 100644 --- a/src/dump.c +++ b/src/dump.c @@ -2694,7 +2694,7 @@ void jl_init_serializer(void) jl_emptysvec, jl_emptytuple, jl_false, jl_true, jl_nothing, jl_any_type, call_sym, invoke_sym, goto_ifnot_sym, return_sym, body_sym, line_sym, - lambda_sym, jl_symbol("tuple"), assign_sym, isdefined_sym, + lambda_sym, jl_symbol("tuple"), assign_sym, isdefined_sym, boundscheck_sym, // empirical list of very common symbols #include "common_symbols1.inc" diff --git a/src/interpreter.c b/src/interpreter.c index 27419c71b7373..b99b7a06a6330 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -499,12 +499,15 @@ static jl_value_t *eval(jl_value_t *e, interpreter_state *s) jl_errorf("syntax: %s", jl_string_data(args[0])); jl_throw(args[0]); } + else if (ex->head == boundscheck_sym) { + return jl_true; + } else if (ex->head == boundscheck_sym || ex->head == inbounds_sym || ex->head == fastmath_sym || ex->head == simdloop_sym || ex->head == meta_sym) { return jl_nothing; } jl_errorf("unsupported or misplaced expression %s", jl_symbol_name(ex->head)); - return (jl_value_t*)jl_nothing; + abort(); } jl_value_t *jl_toplevel_eval_body(jl_module_t *m, jl_array_t *stmts) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 05c811be9226c..ee7b1f812e2ae 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -483,10 +483,10 @@ (block ;; ii = i*2 - 1 (= ,ii (call (top -) (call (top *) ,i 2) 1)) - (= ,elt (call (core arrayref) ,kw ,ii)) + (= ,elt (call (core arrayref) true ,kw ,ii)) ,(foldl (lambda (kn else) (let* ((k (car kn)) - (rval0 `(call (core arrayref) ,kw + (rval0 `(call (core arrayref) true ,kw (call (top +) ,ii 1))) ;; note: if the "declared" type of a KW arg ;; includes something from keyword-sparams @@ -526,7 +526,7 @@ `(foreigncall 'jl_array_ptr_1d_push (core Void) (call (core svec) Any Any) 'ccall 2 ,rkw (tuple ,elt - (call (core arrayref) ,kw + (call (core arrayref) true ,kw (call (top +) ,ii 1))))) (map (lambda (k temp) (cons (if (decl? k) `(,(car k) ,temp ,(caddr k)) temp) @@ -3719,6 +3719,7 @@ f(x) = yt(x) "In the future the variable will be local to the loop instead." #\newline)) (put! deprecated-loop-vars (cadr e) #t)) '(null)) + ((boundscheck) (if tail (emit-return e) e)) ;; top level expressions returning values ((abstract_type primitive_type struct_type thunk toplevel module) diff --git a/src/julia_internal.h b/src/julia_internal.h index f5b4c584dc954..4b6812306bd0d 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -996,7 +996,9 @@ extern jl_sym_t *meta_sym; extern jl_sym_t *list_sym; extern jl_sym_t *inert_sym; extern jl_sym_t *static_parameter_sym; extern jl_sym_t *polly_sym; extern jl_sym_t *inline_sym; extern jl_sym_t *propagate_inbounds_sym; -extern jl_sym_t *isdefined_sym; extern jl_sym_t *nospecialize_sym; +extern jl_sym_t *isdefined_sym; +extern jl_sym_t *nospecialize_sym; +extern jl_sym_t *boundscheck_sym; void jl_register_fptrs(uint64_t sysimage_base, const char *base, const int32_t *offsets, jl_method_instance_t **linfos, size_t n); diff --git a/test/boundscheck_exec.jl b/test/boundscheck_exec.jl index df2deea73cb51..d94cd5e95e915 100644 --- a/test/boundscheck_exec.jl +++ b/test/boundscheck_exec.jl @@ -27,16 +27,20 @@ function A1_inbounds() end return r end +A1_wrap() = @inbounds return A1_inbounds() if bc_opt == bc_default @test A1() == 1 - @test A1_inbounds() == 0 + @test A1_inbounds() == 1 + @test A1_wrap() == 0 elseif bc_opt == bc_on @test A1() == 1 @test A1_inbounds() == 1 + @test A1_wrap() == 1 else @test A1() == 0 @test A1_inbounds() == 0 + @test A1_wrap() == 0 end # test for boundscheck block eliminated one layer deep, if the called method is inlined @@ -124,25 +128,31 @@ end # elide a throw cb(x) = x > 0 || throw(BoundsError()) -function B1() - y = [1,2,3] +@inline function B1() + y = [1, 2, 3] @inbounds begin @boundscheck cb(0) end return 0 end +B1_wrap() = @inbounds return B1() -if bc_opt == bc_default || bc_opt == bc_off +if bc_opt == bc_default + @test_throws BoundsError B1() + @test B1_wrap() == 0 +elseif bc_opt == bc_off @test B1() == 0 + @test B1_wrap() == 0 else @test_throws BoundsError B1() + @test_throws BoundsError B1_wrap() end # elide a simple branch cond(x) = x > 0 ? x : -x function B2() - y = [1,2,3] + y = [1, 2, 3] @inbounds begin @boundscheck cond(0) end diff --git a/test/core.jl b/test/core.jl index 468a1a1afb15f..375ca2de4ae5d 100644 --- a/test/core.jl +++ b/test/core.jl @@ -3810,7 +3810,7 @@ end # `TypeVar`) without crashing let function arrayset_unknown_dim(::Type{T}, n) where T - Base.arrayset(reshape(Vector{T}(1), ones(Int, n)...), 2, 1) + Base.arrayset(true, reshape(Vector{T}(1), ones(Int, n)...), 2, 1) end arrayset_unknown_dim(Any, 1) arrayset_unknown_dim(Any, 2) @@ -4002,16 +4002,13 @@ end function metadata_matches(ast::CodeInfo) inbounds_cnt = Ref(0) - boundscheck_cnt = Ref(0) for ex in ast.code::Array{Any,1} if isa(ex, Expr) ex = ex::Expr count_expr_push(ex, :inbounds, inbounds_cnt) - count_expr_push(ex, :boundscheck, boundscheck_cnt) end end @test inbounds_cnt[] == 0 - @test boundscheck_cnt[] == 0 end function test_metadata_matches(@nospecialize(f), @nospecialize(tt)) @@ -4027,14 +4024,9 @@ function f2() end end # No, don't write code this way... -@eval function f3() - a = $(Expr(:boundscheck, true)) - return 1 - b = $(Expr(:boundscheck, :pop)) -end @noinline function g(a) end -@eval function f4() +@eval function f3() g($(Expr(:inbounds, true))) @goto out g($(Expr(:inbounds, :pop))) @@ -4044,7 +4036,6 @@ end test_metadata_matches(f1, Tuple{}) test_metadata_matches(f2, Tuple{}) test_metadata_matches(f3, Tuple{}) -test_metadata_matches(f4, Tuple{}) end diff --git a/test/staged.jl b/test/staged.jl index 37ed2e7a35338..05cb9f0b39e3e 100644 --- a/test/staged.jl +++ b/test/staged.jl @@ -240,7 +240,7 @@ f22440kernel(::Type{T}) where {T<:AbstractFloat} = zero(T) sig, spvals, method = Base._methods_by_ftype(Tuple{typeof(f22440kernel),y}, -1, typemax(UInt))[1] code_info = Base.uncompressed_ast(method) body = Expr(:block, code_info.code...) - Base.Core.Inference.substitute!(body, 0, Any[], sig, Any[spvals...], 0) + Base.Core.Inference.substitute!(body, 0, Any[], sig, Any[spvals...], 0, :propagate) return code_info end From 9db5eeb2c6f8369092eeae5e4f714fe5a816dffb Mon Sep 17 00:00:00 2001 From: saharjs Date: Tue, 29 Aug 2017 02:18:24 -0400 Subject: [PATCH 187/324] Update README.md (#21326) * Update README.md * updates --- README.md | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 90b248c40542c..0be049d03c126 100644 --- a/README.md +++ b/README.md @@ -87,11 +87,11 @@ Building Julia requires 1.5GiB of disk space and approximately 700MiB of virtual For builds of julia starting with 0.5.0-dev, you can create out-of-tree builds of Julia by specifying `make O= configure` on the command line. This will create a directory mirror, with all of the necessary Makefiles to build Julia, in the specified directory. These builds will share the source files in Julia and `deps/srccache`. Each out-of-tree build directory can have its own `Make.user` file to override the global `Make.user` file in the top-level folder. -If you need to build Julia in an environment that does not allow access to the outside world, use `make -C deps getall` to download all the necessary files. Then, copy the `julia` directory over to the target environment and build with `make`. +If you need to build Julia on a machine without internet access, use `make -C deps getall` to download all the necessary files. Then, copy the `julia` directory over to the target environment and build with `make`. -**Note:** the build process will fail badly if any of the build directory's parent directories have spaces or other shell meta-characters such as `$` or `:` in their names (this is due to a limitation in GNU make). +**Note:** The build process will fail badly if any of the build directory's parent directories have spaces or other shell meta-characters such as `$` or `:` in their names (this is due to a limitation in GNU make). -Once it is built, you can run the `julia` executable using its full path in the directory created above (the `julia` directory), or, to run it from anywhere, either +Once it is built, you can run the `julia` executable using its full path in the directory created above (the `julia` directory). To run julia from anywhere you can: - add an alias (in `bash`: `echo "alias julia='/path/to/install/folder/bin/julia'" >> ~/.bashrc && source ~/.bashrc`), or - add a soft link to the `julia` executable in the `julia` directory to `/usr/local/bin` (or any suitable directory already in your path), or @@ -106,13 +106,11 @@ Now you should be able to run Julia like this: julia -If everything works correctly, you will see a Julia banner and an interactive prompt into which you can enter expressions for evaluation. (Errors related to libraries might be caused by old, incompatible libraries sitting around in your PATH. In that case, try moving the `julia` directory earlier in the PATH). +If everything works correctly, you will see a Julia banner and an interactive prompt into which you can enter expressions for evaluation. (Errors related to libraries might be caused by old, incompatible libraries sitting around in your PATH. In this case, try moving the `julia` directory earlier in the PATH). -Your first test of Julia should be to determine whether your -build is working properly. From the UNIX/Windows command prompt inside -the `julia` source directory, type `make testall`. You should see output -that lists a series of tests being run; if they complete without -error, you should be in good shape to start using Julia. +Your first test of Julia determines whether your build is working properly. From the UNIX/Windows command prompt inside +the `julia` source directory, type `make testall`. You should see output that lists a series of running tests; +if they complete without error, you should be in good shape to start using Julia. You can read about [getting started](https://docs.julialang.org/en/stable/manual/getting-started/) in the manual. From 5645fb212b90910b06c5a06f08c746fdd5b82f80 Mon Sep 17 00:00:00 2001 From: xgdgsc Date: Tue, 29 Aug 2017 18:17:07 +0800 Subject: [PATCH 188/324] rm ubuntu nightly ppa as not actively maintained (#23484) * rm ubuntu nightly ppa as not actively maintained * rm list of outdated repos --- README.md | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 0be049d03c126..149343b24d92d 100644 --- a/README.md +++ b/README.md @@ -401,26 +401,9 @@ On Windows, double-click `usr/bin/julia.exe`. If everything works correctly, you will see a Julia banner and an interactive prompt into which you can enter expressions for evaluation. You can read about [getting started](https://julialang.org/manual/getting-started) in the manual. -The following distributions include julia, but the versions may be out of date due to rapid development: - -* [Alpine Linux](http://pkgs.alpinelinux.org/package/edge/testing/x86_64/julia) -* [Arch Linux](https://www.archlinux.org/packages/community/i686/julia/) -* [Debian GNU/Linux](http://packages.debian.org/sid/julia) -* [Fedora Linux](https://admin.fedoraproject.org/pkgdb/package/julia/), RHEL/CentOS/OEL/Scientific Linux (EPEL) - * [Current stable release for Fedora/EPEL](https://copr.fedoraproject.org/coprs/nalimilan/julia/) - * [Nightly builds for Fedora/EPEL](https://copr.fedoraproject.org/coprs/nalimilan/julia-nightlies/) -* [Gentoo Linux](https://packages.gentoo.org/package/dev-lang/julia) - * Git Package in the [Science overlay](https://wiki.gentoo.org/wiki/Project:Science/Overlay) -* openSUSE - * Stable package for openSUSE: [OBS page](https://build.opensuse.org/package/show/science/julia), [1 Click Install](http://software.opensuse.org/download.html?project=science&package=julia) - * Git package for openSUSE: [OBS page](https://build.opensuse.org/package/show/science/julia-unstable), [1 Click Install](http://software.opensuse.org/download.html?project=science&package=julia-unstable) -* [NixOS](https://github.com/NixOS/nixpkgs/blob/master/pkgs/development/compilers/julia) -* Ubuntu - * [Ubuntu](http://packages.ubuntu.com/search?keywords=julia) - * [Nightly builds PPA](https://launchpad.net/~staticfloat/+archive/julianightlies) (depends on the [julia-deps PPA](https://launchpad.net/~staticfloat/+archive/julia-deps/)) -* [MacPorts](https://trac.macports.org/browser/trunk/dports/lang/julia/Portfile) -* [OS X Homebrew Tap](https://github.com/staticfloat/homebrew-julia/) -* [FreeBSD Ports](https://www.freshports.org/lang/julia/) +**Note**: While some system package managers have Julia installers available, +these are not maintained nor endorsed by the Julia project. They may be outdated +and/or unmaintained. We recommend you use the official Julia binaries instead. ## Editor and Terminal Setup From 3f91250fe6a3e7597b931a44af3c52ebaf92a976 Mon Sep 17 00:00:00 2001 From: Jacob Quinn Date: Tue, 29 Aug 2017 06:53:36 -0600 Subject: [PATCH 189/324] Add missing datatype_module method for UnionAll types --- base/reflection.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/base/reflection.jl b/base/reflection.jl index 50e26e1cc040c..dfce1ba2e0768 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -167,7 +167,7 @@ datatype_name(t::UnionAll) = datatype_name(unwrap_unionall(t)) """ Base.datatype_module(t::DataType) -> Module -Determine the module containing the definition of a `DataType`. +Determine the module containing the definition of a (potentially UnionAll-wrapped) `DataType`. # Examples ```jldoctest @@ -184,6 +184,7 @@ Foo ``` """ datatype_module(t::DataType) = t.name.module +datatype_module(t::UnionAll) = datatype_module(unwrap_unionall(t)) """ isconst(m::Module, s::Symbol) -> Bool From 89772d4d654eae420c2396307a7817dde0da88d6 Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Tue, 29 Aug 2017 15:41:08 +0200 Subject: [PATCH 190/324] fix some convert methods that depwarned (#23494) --- base/linalg/special.jl | 17 +++++++++-------- base/test.jl | 2 ++ test/linalg/special.jl | 1 + 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/base/linalg/special.jl b/base/linalg/special.jl index e7ba87788d40b..d41078fe2a238 100644 --- a/base/linalg/special.jl +++ b/base/linalg/special.jl @@ -3,12 +3,13 @@ # Methods operating on different special matrix types # Interconversion between special matrix types -convert(::Type{Bidiagonal}, A::Diagonal{T}) where {T} = - Bidiagonal(A.diag, zeros(T, size(A.diag,1)-1), :U) -convert(::Type{SymTridiagonal}, A::Diagonal{T}) where {T} = - SymTridiagonal(A.diag, zeros(T, size(A.diag,1)-1)) -convert(::Type{Tridiagonal}, A::Diagonal{T}) where {T} = - Tridiagonal(zeros(T, size(A.diag,1)-1), A.diag, zeros(T, size(A.diag,1)-1)) +convert(::Type{Bidiagonal}, A::Diagonal) = + Bidiagonal(A.diag, fill!(similar(A.diag, length(A.diag)-1), 0), :U) +convert(::Type{SymTridiagonal}, A::Diagonal) = + SymTridiagonal(A.diag, fill!(similar(A.diag, length(A.diag)-1), 0)) +convert(::Type{Tridiagonal}, A::Diagonal) = + Tridiagonal(fill!(similar(A.diag, length(A.diag)-1), 0), A.diag, + fill!(similar(A.diag, length(A.diag)-1), 0)) function convert(::Type{Diagonal}, A::Union{Bidiagonal, SymTridiagonal}) if !iszero(A.ev) @@ -25,8 +26,8 @@ function convert(::Type{SymTridiagonal}, A::Bidiagonal) end convert(::Type{Tridiagonal}, A::Bidiagonal{T}) where {T} = - Tridiagonal(A.uplo == 'U' ? zeros(T, size(A.dv,1)-1) : A.ev, A.dv, - A.uplo == 'U' ? A.ev : zeros(T, size(A.dv,1)-1)) + Tridiagonal(A.uplo == 'U' ? fill!(similar(A.ev), 0) : A.ev, A.dv, + A.uplo == 'U' ? A.ev : fill!(similar(A.ev), 0)) function convert(::Type{Bidiagonal}, A::SymTridiagonal) if !iszero(A.ev) diff --git a/base/test.jl b/base/test.jl index 6f7d4e132c493..19255d6d8e11a 100644 --- a/base/test.jl +++ b/base/test.jl @@ -1422,4 +1422,6 @@ Base.size(a::GenericArray) = size(a.a) Base.getindex(a::GenericArray, i...) = a.a[i...] Base.setindex!(a::GenericArray, x, i...) = a.a[i...] = x +Base.similar(A::GenericArray, s::Integer...) = GenericArray(similar(A.a, s...)) + end # module diff --git a/test/linalg/special.jl b/test/linalg/special.jl index c4f6e180a1f4f..1742a27a138e7 100644 --- a/test/linalg/special.jl +++ b/test/linalg/special.jl @@ -10,6 +10,7 @@ srand(1) A = Diagonal(a) @testset for newtype in [Diagonal, Bidiagonal, SymTridiagonal, Tridiagonal, Matrix] @test full(convert(newtype, A)) == full(A) + @test full(convert(newtype, Diagonal(GenericArray(a)))) == full(A) end @testset for isupper in (true, false) From 6a47db885b8f959e757d2e2ae51ee1ce56fddd69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=B8egh?= Date: Tue, 29 Aug 2017 15:41:46 +0200 Subject: [PATCH 191/324] Fix 22577. Add string macros to REPLCompletion. (#23119) * Fix 22577. Add string macros to REPLCompletion. * Add cmd macros to REPLCompletion. * retrigger ci, update to a testset * unicode tests. * also complete the unicode char --- base/repl/REPLCompletions.jl | 8 ++++++++ test/replcompletions.jl | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/base/repl/REPLCompletions.jl b/base/repl/REPLCompletions.jl index 516846570ffd6..27074fa09bd35 100644 --- a/base/repl/REPLCompletions.jl +++ b/base/repl/REPLCompletions.jl @@ -10,11 +10,19 @@ function completes_global(x, name) return startswith(x, name) && !('#' in x) end +function appendmacro!(syms, macros, needle, endchar) + append!(syms, s[2:end-sizeof(needle)]*endchar for s in filter(x -> endswith(x, needle), macros)) +end + function filtered_mod_names(ffunc::Function, mod::Module, name::AbstractString, all::Bool=false, imported::Bool=false) ssyms = names(mod, all, imported) filter!(ffunc, ssyms) syms = String[string(s) for s in ssyms] + macros = filter(x -> startswith(x, "@" * name), syms) + appendmacro!(syms, macros, "_str", "\"") + appendmacro!(syms, macros, "_cmd", "`") filter!(x->completes_global(x, name), syms) + return syms end # REPL Symbol Completions diff --git a/test/replcompletions.jl b/test/replcompletions.jl index ba9fcf35fabb8..13ada5ae0da63 100644 --- a/test/replcompletions.jl +++ b/test/replcompletions.jl @@ -65,6 +65,12 @@ ex = quote contains=>4, `ls`=>5, 66=>7, 67=>8, ("q",3)=>11, "α"=>12, :α=>13) test_customdict = CustomDict(test_dict) + + macro teststr_str(s) end + macro tϵsτstρ_str(s) end + macro testcmd_cmd(s) end + macro tϵsτcmδ_cmd(s) end + end test_repl_comp_dict = CompletionFoo.test_dict test_repl_comp_customdict = CompletionFoo.test_customdict @@ -788,3 +794,16 @@ test_dict_completion("test_repl_comp_customdict") # Issue #23004: this should not throw: @test REPLCompletions.dict_identifier_key("test_dict_ℂ[\\", :other) isa Tuple + +@testset "completion of string/cmd macros (#22577)" begin + c, r, res = test_complete("ra") + @test "raw\"" in c + c, r, res = test_complete("CompletionFoo.tests") + @test "teststr\"" in c + c, r, res = test_complete("CompletionFoo.tϵsτs") + @test "tϵsτstρ\"" in c + c, r, res = test_complete("CompletionFoo.testc") + @test "testcmd`" in c + c, r, res = test_complete("CompletionFoo.tϵsτc") + @test "tϵsτcmδ`" in c +end From 4d8f6162691f04dcae8b3374dd3c36d5dfb18e44 Mon Sep 17 00:00:00 2001 From: Jan Weidner Date: Tue, 22 Aug 2017 11:09:32 +0200 Subject: [PATCH 192/324] Make printing of big(NaN) consistent (#23382) --- base/mpfr.jl | 4 +--- test/mpfr.jl | 6 ------ test/numbers.jl | 15 ++++++++++----- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/base/mpfr.jl b/base/mpfr.jl index b2321675232dd..17f8a1db964ea 100644 --- a/base/mpfr.jl +++ b/base/mpfr.jl @@ -895,9 +895,7 @@ end setprecision(f::Function, precision::Integer) = setprecision(f, BigFloat, precision) function string(x::BigFloat) - if isnan(x) || isinf(x) - return string("BigFloat(", Float64(x), ", ", precision(x), ")") - end + isfinite(x) || return string(Float64(x)) # In general, the number of decimal places needed to read back the number exactly # is, excluding the most significant, ceil(log(10, 2^precision(x))) diff --git a/test/mpfr.jl b/test/mpfr.jl index 0ec518a80a8bb..8264dec70c323 100644 --- a/test/mpfr.jl +++ b/test/mpfr.jl @@ -880,12 +880,6 @@ for prec in (10, 100, 1000) end end -setprecision(256) do - @test string(big(Inf)) == "BigFloat(Inf, 256)" - @test string(big(-Inf)) == "BigFloat(-Inf, 256)" - @test string(big(NaN)) == "BigFloat(NaN, 256)" -end - # issue #22758 if MPFR.version() > v"3.1.5" || "r11590" in MPFR.version().build setprecision(2_000_000) do diff --git a/test/numbers.jl b/test/numbers.jl index 0f694fa410080..07099655c079c 100644 --- a/test/numbers.jl +++ b/test/numbers.jl @@ -3028,9 +3028,14 @@ end end end -@testset "compact NaN printing" begin - @test sprint(io->show(IOContext(io, :compact => true), NaN16)) == "NaN" - @test sprint(io->show(IOContext(io, :compact => true), NaN32)) == "NaN" - @test sprint(io->show(IOContext(io, :compact => true), NaN64)) == "NaN" - @test_broken sprint(io->show(IOContext(io, :compact => true), big(NaN))) == "NaN" +@testset "printing non finite floats" for T in subtypes(AbstractFloat) + for (x, sx) in [(T(NaN), "NaN"), + (-T(NaN), "NaN"), + (T(Inf), "Inf"), + (-T(Inf), "-Inf")] + @assert x isa T + @test string(x) == sx + @test sprint(io -> show(IOContext(io, :compact => true), x)) == sx + @test sprint(print, x) == sx + end end From 7d60a81c64a485644cdd55d38c03668b9bb64718 Mon Sep 17 00:00:00 2001 From: Jan Weidner Date: Wed, 23 Aug 2017 15:56:42 +0200 Subject: [PATCH 193/324] More compact BigFloat printing (#23382) --- base/mpfr.jl | 71 +++++++++++++++++++++++++++++++++++++++------------- test/mpfr.jl | 37 ++++++++++++++++++++++++--- 2 files changed, 87 insertions(+), 21 deletions(-) diff --git a/base/mpfr.jl b/base/mpfr.jl index 17f8a1db964ea..43064ee9adfab 100644 --- a/base/mpfr.jl +++ b/base/mpfr.jl @@ -894,28 +894,63 @@ end setprecision(f::Function, precision::Integer) = setprecision(f, BigFloat, precision) -function string(x::BigFloat) - isfinite(x) || return string(Float64(x)) - - # In general, the number of decimal places needed to read back the number exactly - # is, excluding the most significant, ceil(log(10, 2^precision(x))) - k = ceil(Int32, precision(x) * 0.3010299956639812) - lng = k + Int32(8) # Add space for the sign, the most significand digit, the dot and the exponent - buf = Base.StringVector(lng + 1) - # format strings are guaranteed to contain no NUL, so we don't use Cstring - lng = ccall((:mpfr_snprintf,:libmpfr), Int32, (Ptr{UInt8}, Culong, Ptr{UInt8}, Ref{BigFloat}...), buf, lng + 1, "%.Re", x) - if lng < k + 5 # print at least k decimal places - lng = ccall((:mpfr_sprintf,:libmpfr), Int32, (Ptr{UInt8}, Ptr{UInt8}, Ref{BigFloat}...), buf, "%.$(k)Re", x) - elseif lng > k + 8 - buf = Base.StringVector(lng + 1) - lng = ccall((:mpfr_snprintf,:libmpfr), Int32, (Ptr{UInt8}, Culong, Ptr{UInt8}, Ref{BigFloat}...), buf, lng + 1, "%.Re", x) +function string_mpfr(x::BigFloat, fmt::String) + buf = Base.StringVector(0) + s = _calculate_buffer_size!(buf, fmt, x) + resize!(buf, s) + _fill_buffer!(buf, fmt, x) + String(buf) +end + +function _calculate_buffer_size!(buf, fmt, x::BigFloat) + ccall((:mpfr_snprintf,:libmpfr), + Int32, (Ptr{UInt8}, Culong, Ptr{UInt8}, Ptr{BigFloat}...), + buf, 0, fmt, &x) +end + +function _fill_buffer!(buf, fmt, x::BigFloat) + s = length(buf) + # we temporarily need one more item in buffer to capture null termination + resize!(buf, s + 1) + n = ccall((:mpfr_sprintf,:libmpfr), Int32, (Ptr{UInt8}, Ptr{UInt8}, Ptr{BigFloat}...), buf, fmt, &x) + @assert n + 1 == length(buf) + @assert last(buf) == 0x00 + resize!(buf, s) +end + +function _prettify_bigfloat(s::String)::String + mantissa, exponent = split(s, 'e') + if !contains(mantissa, '.') + mantissa = string(mantissa, '.') + end + mantissa = rstrip(mantissa, '0') + if endswith(mantissa, '.') + mantissa = string(mantissa, '0') end - n = (1 <= x < 10 || -10 < x <= -1 || iszero(x)) ? lng - 4 : lng - return String(resize!(buf,n)) + if exponent == "+00" + mantissa + else + string(mantissa, 'e', exponent) + end +end + +function _string(x::BigFloat, fmt::String)::String + isfinite(x) || return string(Float64(x)) + _prettify_bigfloat(string_mpfr(x, fmt)) end +_string(x::BigFloat) = _string(x, "%.Re") +_string(x::BigFloat, k::Integer) = _string(x, "%.$(k)Re") + +string(b::BigFloat) = _string(b) print(io::IO, b::BigFloat) = print(io, string(b)) -show(io::IO, b::BigFloat) = print(io, string(b)) +function show(io::IO, b::BigFloat) + if get(io, :compact, false) + print(io, _string(b, 5)) + else + print(io, _string(b)) + end +end # get/set exponent min/max get_emax() = ccall((:mpfr_get_emax, :libmpfr), Clong, ()) diff --git a/test/mpfr.jl b/test/mpfr.jl index 8264dec70c323..6fe67be343b16 100644 --- a/test/mpfr.jl +++ b/test/mpfr.jl @@ -382,17 +382,14 @@ setprecision(406) do @test string(nextfloat(BigFloat(1))) == str end setprecision(21) do - @test string(zero(BigFloat)) == "0.0000000" @test string(parse(BigFloat, "0.1")) == "1.0000002e-01" @test string(parse(BigFloat, "-9.9")) == "-9.9000015" end setprecision(40) do - @test string(zero(BigFloat)) == "0.0000000000000" @test string(parse(BigFloat, "0.1")) == "1.0000000000002e-01" @test string(parse(BigFloat, "-9.9")) == "-9.8999999999942" end setprecision(123) do - @test string(zero(BigFloat)) == "0.00000000000000000000000000000000000000" @test string(parse(BigFloat, "0.1")) == "9.99999999999999999999999999999999999953e-02" @test string(parse(BigFloat, "-9.9")) == "-9.8999999999999999999999999999999999997" end @@ -886,3 +883,37 @@ if MPFR.version() > v"3.1.5" || "r11590" in MPFR.version().build @test abs(sin(big(pi)/6) - 0.5) < ldexp(big(1.0),-1_999_000) end end + +@testset "show BigFloat" begin + function test_show_bigfloat(x::BigFloat; contains_e::Bool=true, + ends::String="", + starts::String="") + sx = sprint(show, x) + scx = sprint(showcompact, x) + strx = string(x) + @test sx == strx + @test length(scx) < 20 + @test length(scx) <= length(sx) + @test x == parse(BigFloat, sx) + @test ≈(x, parse(BigFloat, scx), rtol=1e-4) + for s in (sx, scx) + @test contains(s, 'e') == contains_e + @test startswith(s, starts) + @test endswith(s, ends) + end + end + + test_show_bigfloat(big"1.23456789", contains_e=false, starts="1.23") + test_show_bigfloat(big"-1.23456789", contains_e=false, starts="-1.23") + test_show_bigfloat(big"2.3457645687563543266576889678956787e10000", starts="2.345", ends="e+10000") + test_show_bigfloat(big"-2.3457645687563543266576889678956787e-10000", starts="-2.345", ends="e-10000") + + for to_string in [string, + x->sprint(show, x), + x->sprint(showcompact,x)] + @test to_string(big"0.0") == "0.0" + @test to_string(big"-0.0") == "-0.0" + @test to_string(big"1.0") == "1.0" + @test to_string(big"-1.0") == "-1.0" + end +end From 36990c7da081e8b9ec24778eb72734801ed79028 Mon Sep 17 00:00:00 2001 From: Andreas Noack Date: Tue, 29 Aug 2017 13:14:16 -0400 Subject: [PATCH 194/324] Deprecate countnz in favor of using count(predicate, x) (#23485) Define missing specialization of count(pred, x) for sparse arrays --- base/abstractarray.jl | 2 +- base/array.jl | 30 ++++++++-------- base/bitarray.jl | 19 +++++------ base/deprecated.jl | 7 ++++ base/exports.jl | 1 - base/linalg/bitarray.jl | 2 +- base/multidimensional.jl | 9 +++-- base/reduce.jl | 23 ++----------- base/sparse/sparse.jl | 2 +- base/sparse/sparsematrix.jl | 11 ++---- base/sparse/sparsevector.jl | 10 +++--- doc/src/manual/arrays.md | 2 +- doc/src/stdlib/arrays.md | 1 - test/arrayops.jl | 2 +- test/bitarray.jl | 10 +++--- test/reduce.jl | 10 +++--- test/sparse/sparse.jl | 68 ++++++++++++++++++------------------- test/sparse/sparsevector.jl | 2 +- test/sparse/spqr.jl | 2 +- test/subarray.jl | 2 +- 20 files changed, 99 insertions(+), 116 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index df165c1c49916..23652f9be4c68 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -1275,7 +1275,7 @@ function cat_t(dims, T::Type, X...) catdims = dims2cat(dims) shape = cat_shape(catdims, (), map(cat_size, X)...) A = cat_similar(X[1], T, shape) - if T <: Number && countnz(catdims) > 1 + if T <: Number && count(!iszero, catdims) > 1 fill!(A, zero(T)) end return _cat(A, shape, catdims, X...) diff --git a/base/array.jl b/base/array.jl index 1141b364e2c0a..5db07c626d32d 100644 --- a/base/array.jl +++ b/base/array.jl @@ -1945,14 +1945,14 @@ julia> find(zeros(3)) ``` """ function find(A) - nnzA = countnz(A) + nnzA = count(t -> t != 0, A) I = Vector{Int}(nnzA) - count = 1 + cnt = 1 inds = _index_remapper(A) for (i,a) in enumerate(A) if a != 0 - I[count] = inds[i] - count += 1 + I[cnt] = inds[i] + cnt += 1 end end return I @@ -1991,15 +1991,15 @@ julia> findn(A) ``` """ function findn(A::AbstractMatrix) - nnzA = countnz(A) + nnzA = count(t -> t != 0, A) I = similar(A, Int, nnzA) J = similar(A, Int, nnzA) - count = 1 + cnt = 1 for j=indices(A,2), i=indices(A,1) if A[i,j] != 0 - I[count] = i - J[count] = j - count += 1 + I[cnt] = i + J[cnt] = j + cnt += 1 end end return (I, J) @@ -2024,19 +2024,19 @@ julia> findnz(A) ``` """ function findnz(A::AbstractMatrix{T}) where T - nnzA = countnz(A) + nnzA = count(t -> t != 0, A) I = zeros(Int, nnzA) J = zeros(Int, nnzA) NZs = Array{T,1}(nnzA) - count = 1 + cnt = 1 if nnzA > 0 for j=indices(A,2), i=indices(A,1) Aij = A[i,j] if Aij != 0 - I[count] = i - J[count] = j - NZs[count] = Aij - count += 1 + I[cnt] = i + J[cnt] = j + NZs[cnt] = Aij + cnt += 1 end end end diff --git a/base/bitarray.jl b/base/bitarray.jl index 7d69bcd7e3a63..df58cc4a37cb1 100644 --- a/base/bitarray.jl +++ b/base/bitarray.jl @@ -1614,9 +1614,9 @@ julia> ror(A,5) """ ror(B::BitVector, i::Integer) = ror!(similar(B), B, i) -## countnz & find ## +## count & find ## -function countnz(B::BitArray) +function count(B::BitArray) n = 0 Bc = B.chunks @inbounds for i = 1:length(Bc) @@ -1624,7 +1624,6 @@ function countnz(B::BitArray) end return n end -count(B::BitArray) = countnz(B) # returns the index of the next non-zero element, or 0 if all zeros function findnext(B::BitArray, start::Integer) @@ -1778,7 +1777,7 @@ end function find(B::BitArray) l = length(B) - nnzB = countnz(B) + nnzB = count(B) I = Vector{Int}(nnzB) nnzB == 0 && return I Bc = B.chunks @@ -1812,15 +1811,15 @@ end findn(B::BitVector) = find(B) function findn(B::BitMatrix) - nnzB = countnz(B) + nnzB = count(B) I = Vector{Int}(nnzB) J = Vector{Int}(nnzB) - count = 1 + cnt = 1 for j = 1:size(B,2), i = 1:size(B,1) if B[i,j] - I[count] = i - J[count] = j - count += 1 + I[cnt] = i + J[cnt] = j + cnt += 1 end end return I, J @@ -1834,7 +1833,7 @@ end ## Reductions ## sum(A::BitArray, region) = reducedim(+, A, region) -sum(B::BitArray) = countnz(B) +sum(B::BitArray) = count(B) function all(B::BitArray) isempty(B) && return true diff --git a/base/deprecated.jl b/base/deprecated.jl index 8338f8a1c65b1..75265dcc47ad9 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1739,6 +1739,13 @@ end @deprecate IOContext(io::IO, key, value) IOContext(io, key=>value) +# PR #23485 +export foo +function countnz(x) + depwarn("countnz(x) is deprecated, use either count(!iszero, x) or count(t -> t != 0, x) instead.", :depwarn) + return count(t -> t != 0, x) +end + # issue #22791 @deprecate select partialsort @deprecate select! partialsort! diff --git a/base/exports.jl b/base/exports.jl index 10133316bbe6d..b1c77b26fb2b8 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -488,7 +488,6 @@ export minmax, ndims, nonzeros, - countnz, ones, parent, parentindexes, diff --git a/base/linalg/bitarray.jl b/base/linalg/bitarray.jl index 7b86c75d8e56a..b7dacb300a3f8 100644 --- a/base/linalg/bitarray.jl +++ b/base/linalg/bitarray.jl @@ -133,7 +133,7 @@ end ## Structure query functions -issymmetric(A::BitMatrix) = size(A, 1)==size(A, 2) && countnz(A - A.')==0 +issymmetric(A::BitMatrix) = size(A, 1)==size(A, 2) && count(!iszero, A - A.')==0 ishermitian(A::BitMatrix) = issymmetric(A) function nonzero_chunks(chunks::Vector{UInt64}, pos0::Int, pos1::Int) diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 64cf42b8b44c7..e48394a8c9d37 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -394,7 +394,7 @@ wrapped with `LogicalIndex` upon calling `to_indices`. struct LogicalIndex{T, A<:AbstractArray{Bool}} <: AbstractVector{T} mask::A sum::Int - LogicalIndex{T,A}(mask::A) where {T,A<:AbstractArray{Bool}} = new(mask, countnz(mask)) + LogicalIndex{T,A}(mask::A) where {T,A<:AbstractArray{Bool}} = new(mask, count(mask)) end LogicalIndex(mask::AbstractVector{Bool}) = LogicalIndex{Int, typeof(mask)}(mask) LogicalIndex(mask::AbstractArray{Bool, N}) where {N} = LogicalIndex{CartesianIndex{N}, typeof(mask)}(mask) @@ -574,9 +574,12 @@ end ## +# small helper function since we cannot use a closure in a generated function +_countnz(x) = x != 0 + @generated function findn(A::AbstractArray{T,N}) where {T,N} quote - nnzA = countnz(A) + nnzA = count(_countnz, A) @nexprs $N d->(I_d = Vector{Int}(nnzA)) k = 1 @nloops $N i A begin @@ -1301,7 +1304,7 @@ end @generated function findn(B::BitArray{N}) where N quote - nnzB = countnz(B) + nnzB = count(B) I = ntuple(x->Vector{Int}(nnzB), Val($N)) if nnzB > 0 count = 1 diff --git a/base/reduce.jl b/base/reduce.jl index 3ca6351ebfb99..626809a73721a 100644 --- a/base/reduce.jl +++ b/base/reduce.jl @@ -359,7 +359,7 @@ julia> sum(1:20) ``` """ sum(a) = mapreduce(identity, +, a) -sum(a::AbstractArray{Bool}) = countnz(a) +sum(a::AbstractArray{Bool}) = count(a) # Kahan (compensated) summation: O(1) error growth, at the expense @@ -670,7 +670,7 @@ function contains(eq::Function, itr, x) end -## countnz & count +## count """ count(p, itr) -> Integer @@ -703,22 +703,3 @@ function count(pred, a::AbstractArray) return n end count(itr) = count(identity, itr) - -""" - countnz(A) -> Integer - -Counts the number of nonzero values in array `A` (dense or sparse). Note that this is not a constant-time operation. -For sparse matrices, one should usually use [`nnz`](@ref), which returns the number of stored values. - -```jldoctest -julia> A = [1 2 4; 0 0 1; 1 1 0] -3×3 Array{Int64,2}: - 1 2 4 - 0 0 1 - 1 1 0 - -julia> countnz(A) -6 -``` -""" -countnz(a) = count(x -> x != 0, a) diff --git a/base/sparse/sparse.jl b/base/sparse/sparse.jl index 81cb3f3a95a1a..abe6289b18070 100644 --- a/base/sparse/sparse.jl +++ b/base/sparse/sparse.jl @@ -14,7 +14,7 @@ import Base.LinAlg: At_ldiv_B!, Ac_ldiv_B!, A_rdiv_B!, A_rdiv_Bc! import Base: @get!, acos, acosd, acot, acotd, acsch, asech, asin, asind, asinh, atan, atand, atanh, broadcast!, chol, conj!, cos, cosc, cosd, cosh, cospi, cot, - cotd, coth, countnz, csc, cscd, csch, adjoint!, diag, diff, done, dot, eig, + cotd, coth, count, csc, cscd, csch, adjoint!, diag, diff, done, dot, eig, exp10, exp2, eye, findn, floor, hash, indmin, inv, issymmetric, istril, istriu, log10, log2, lu, next, sec, secd, sech, show, sin, sinc, sind, sinh, sinpi, squeeze, start, sum, summary, tan, diff --git a/base/sparse/sparsematrix.jl b/base/sparse/sparsematrix.jl index e58530b4fb115..c3304d34ad347 100644 --- a/base/sparse/sparsematrix.jl +++ b/base/sparse/sparsematrix.jl @@ -70,9 +70,9 @@ julia> nnz(A) 3 ``` """ -nnz(S::SparseMatrixCSC) = Int(S.colptr[S.n + 1]-1) -countnz(S::SparseMatrixCSC) = countnz(S.nzval) -count(S::SparseMatrixCSC) = count(S.nzval) +nnz(S::SparseMatrixCSC) = Int(S.colptr[S.n + 1] - 1) +count(S::SparseMatrixCSC) = count(S.nzval) +count(pred, S::SparseMatrixCSC) = count(pred, S.nzval) + pred(zero(eltype(S)))*(prod(size(S)) - nnz(S)) """ nonzeros(A) @@ -1911,11 +1911,6 @@ findmax(A::SparseMatrixCSC) = (r=findmax(A,(1,2)); (r[1][1], r[2][1])) indmin(A::SparseMatrixCSC) = findmin(A)[2] indmax(A::SparseMatrixCSC) = findmax(A)[2] -#all(A::SparseMatrixCSC{Bool}, region) = reducedim(all,A,region,true) -#any(A::SparseMatrixCSC{Bool}, region) = reducedim(any,A,region,false) -#sum(A::SparseMatrixCSC{Bool}, region) = reducedim(+,A,region,0,Int) -#sum(A::SparseMatrixCSC{Bool}) = countnz(A) - ## getindex function rangesearch(haystack::Range, needle) (i,rem) = divrem(needle - first(haystack), step(haystack)) diff --git a/base/sparse/sparsevector.jl b/base/sparse/sparsevector.jl index 86784a8bca367..630fea82a0a12 100644 --- a/base/sparse/sparsevector.jl +++ b/base/sparse/sparsevector.jl @@ -37,11 +37,11 @@ const SparseVectorUnion{T} = Union{SparseVector{T}, SparseColumnView{T}} ### Basic properties -length(x::SparseVector) = x.n -size(x::SparseVector) = (x.n,) -nnz(x::SparseVector) = length(x.nzval) -countnz(x::SparseVector) = countnz(x.nzval) -count(x::SparseVector) = count(x.nzval) +length(x::SparseVector) = x.n +size(x::SparseVector) = (x.n,) +nnz(x::SparseVector) = length(x.nzval) +count(x::SparseVector) = count(x.nzval) +count(f, x::SparseVector) = count(f, x.nzval) + f(zero(eltype(x)))*(length(x) - nnz(x)) nonzeros(x::SparseVector) = x.nzval function nonzeros(x::SparseColumnView) diff --git a/doc/src/manual/arrays.md b/doc/src/manual/arrays.md index f91b93f98ec0b..e3dddc0dbe57f 100644 --- a/doc/src/manual/arrays.md +++ b/doc/src/manual/arrays.md @@ -745,7 +745,7 @@ In some applications, it is convenient to store explicit zero values in a `Spars mutating operations). Such explicitly stored zeros are treated as structural nonzeros by many routines. The [`nnz()`](@ref) function returns the number of elements explicitly stored in the sparse data structure, including structural nonzeros. In order to count the exact number of -numerical nonzeros, use [`countnz()`](@ref), which inspects every stored element of a sparse +numerical nonzeros, use [`count(!iszero, x)`](@ref), which inspects every stored element of a sparse matrix. [`dropzeros()`](@ref), and the in-place [`dropzeros!()`](@ref), can be used to remove stored zeros from the sparse matrix. diff --git a/doc/src/stdlib/arrays.md b/doc/src/stdlib/arrays.md index aedb79e7f6a64..9dd0ce3e7053b 100644 --- a/doc/src/stdlib/arrays.md +++ b/doc/src/stdlib/arrays.md @@ -42,7 +42,6 @@ Base.length(::AbstractArray) Base.eachindex Base.linearindices Base.IndexStyle -Base.countnz Base.conj! Base.stride Base.strides diff --git a/test/arrayops.jl b/test/arrayops.jl index e52190f2e7629..98c1908301fa5 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -6,7 +6,7 @@ using TestHelpers.OAs @testset "basics" begin @test length([1, 2, 3]) == 3 - @test countnz([1, 2, 3]) == 3 + @test count(!iszero, [1, 2, 3]) == 3 let a = ones(4), b = a+a, c = a-a @test b[1] === 2. && b[2] === 2. && b[3] === 2. && b[4] === 2. diff --git a/test/bitarray.jl b/test/bitarray.jl index b1704bbf0fd02..aebec67d6f563 100644 --- a/test/bitarray.jl +++ b/test/bitarray.jl @@ -471,20 +471,20 @@ timesofar("constructors") @check_bit_operation setindex!(b1, true, t1) BitMatrix t1 = bitrand(n1, n2) - b2 = bitrand(countnz(t1)) + b2 = bitrand(count(t1)) @check_bit_operation setindex!(b1, b2, t1) BitMatrix m1 = rand(1:n1) m2 = rand(1:n2) t1 = bitrand(n1) - b2 = bitrand(countnz(t1), m2) + b2 = bitrand(count(t1), m2) k2 = randperm(m2) @check_bit_operation setindex!(b1, b2, t1, 1:m2) BitMatrix @check_bit_operation setindex!(b1, b2, t1, n2-m2+1:n2) BitMatrix @check_bit_operation setindex!(b1, b2, t1, k2) BitMatrix t2 = bitrand(n2) - b2 = bitrand(m1, countnz(t2)) + b2 = bitrand(m1, count(t2)) k1 = randperm(m1) @check_bit_operation setindex!(b1, b2, 1:m1, t2) BitMatrix @check_bit_operation setindex!(b1, b2, n1-m1+1:n1, t2) BitMatrix @@ -1056,9 +1056,9 @@ end timesofar("datamove") -@testset "countnz & find" begin +@testset "count & find" begin for m = 0:v1, b1 in Any[bitrand(m), trues(m), falses(m)] - @check_bit_operation countnz(b1) Int + @check_bit_operation count(b1) Int @check_bit_operation findfirst(b1) Int diff --git a/test/reduce.jl b/test/reduce.jl index 0d88fee203f46..9605b9b0a0707 100644 --- a/test/reduce.jl +++ b/test/reduce.jl @@ -318,7 +318,7 @@ struct SomeFunctor end @test contains("quick fox", "fox") == true @test contains("quick fox", "lazy dog") == false -# count & countnz +# count @test count(x->x>0, Int[]) == count(Bool[]) == 0 @test count(x->x>0, -3:5) == count((-3:5) .> 0) == 5 @@ -333,10 +333,10 @@ end @test count(iseven(x) for x in 1:10 if x < 7) == 3 @test count(iseven(x) for x in 1:10 if x < -7) == 0 -@test countnz(Int[]) == 0 -@test countnz(Int[0]) == 0 -@test countnz(Int[1]) == 1 -@test countnz([1, 0, 2, 0, 3, 0, 4]) == 4 +@test count(!iszero, Int[]) == 0 +@test count(!iszero, Int[0]) == 0 +@test count(!iszero, Int[1]) == 1 +@test count(!iszero, [1, 0, 2, 0, 3, 0, 4]) == 4 ## cumsum, cummin, cummax diff --git a/test/sparse/sparse.jl b/test/sparse/sparse.jl index 741ef0de794a8..7a24f2145472a 100644 --- a/test/sparse/sparse.jl +++ b/test/sparse/sparse.jl @@ -689,56 +689,56 @@ end @testset "setindex" begin a = spzeros(Int, 10, 10) - @test countnz(a) == 0 + @test count(!iszero, a) == 0 a[1,:] = 1 - @test countnz(a) == 10 + @test count(!iszero, a) == 10 @test a[1,:] == sparse(ones(Int,10)) a[:,2] = 2 - @test countnz(a) == 19 + @test count(!iszero, a) == 19 @test a[:,2] == 2*sparse(ones(Int,10)) b = copy(a) # Zero-assignment behavior of setindex!(A, v, i, j) a[1,3] = 0 @test nnz(a) == 19 - @test countnz(a) == 18 + @test count(!iszero, a) == 18 a[2,1] = 0 @test nnz(a) == 19 - @test countnz(a) == 18 + @test count(!iszero, a) == 18 # Zero-assignment behavior of setindex!(A, v, I, J) a[1,:] = 0 @test nnz(a) == 19 - @test countnz(a) == 9 + @test count(!iszero, a) == 9 a[2,:] = 0 @test nnz(a) == 19 - @test countnz(a) == 8 + @test count(!iszero, a) == 8 a[:,1] = 0 @test nnz(a) == 19 - @test countnz(a) == 8 + @test count(!iszero, a) == 8 a[:,2] = 0 @test nnz(a) == 19 - @test countnz(a) == 0 + @test count(!iszero, a) == 0 a = copy(b) a[:,:] = 0 @test nnz(a) == 19 - @test countnz(a) == 0 + @test count(!iszero, a) == 0 # Zero-assignment behavior of setindex!(A, B::SparseMatrixCSC, I, J) a = copy(b) a[1:2,:] = spzeros(2, 10) @test nnz(a) == 19 - @test countnz(a) == 8 + @test count(!iszero, a) == 8 a[1:2,1:3] = sparse([1 0 1; 0 0 1]) @test nnz(a) == 20 - @test countnz(a) == 11 + @test count(!iszero, a) == 11 a = copy(b) a[1:2,:] = let c = sparse(ones(2,10)); fill!(c.nzval, 0); c; end @test nnz(a) == 19 - @test countnz(a) == 8 + @test count(!iszero, a) == 8 a[1:2,1:3] = let c = sparse(ones(2,3)); c[1,2] = c[2,1] = c[2,2] = 0; c; end @test nnz(a) == 20 - @test countnz(a) == 11 + @test count(!iszero, a) == 11 a[1,:] = 1:10 @test a[1,:] == sparse([1:10;]) @@ -782,34 +782,34 @@ end A = spzeros(Int, 10, 20) A[1:5,1:10] = 10 A[1:5,1:10] = 10 - @test countnz(A) == 50 + @test count(!iszero, A) == 50 @test A[1:5,1:10] == 10 * ones(Int, 5, 10) A[6:10,11:20] = 0 - @test countnz(A) == 50 + @test count(!iszero, A) == 50 A[6:10,11:20] = 20 - @test countnz(A) == 100 + @test count(!iszero, A) == 100 @test A[6:10,11:20] == 20 * ones(Int, 5, 10) A[4:8,8:16] = 15 - @test countnz(A) == 121 + @test count(!iszero, A) == 121 @test A[4:8,8:16] == 15 * ones(Int, 5, 9) ASZ = 1000 TSZ = 800 A = sprand(ASZ, 2*ASZ, 0.0001) B = copy(A) - nA = countnz(A) + nA = count(!iszero, A) x = A[1:TSZ, 1:(2*TSZ)] - nx = countnz(x) + nx = count(!iszero, x) A[1:TSZ, 1:(2*TSZ)] = 0 - nB = countnz(A) + nB = count(!iszero, A) @test nB == (nA - nx) A[1:TSZ, 1:(2*TSZ)] = x - @test countnz(A) == nA + @test count(!iszero, A) == nA @test A == B A[1:TSZ, 1:(2*TSZ)] = 10 - @test countnz(A) == nB + 2*TSZ*TSZ + @test count(!iszero, A) == nB + 2*TSZ*TSZ A[1:TSZ, 1:(2*TSZ)] = x - @test countnz(A) == nA + @test count(!iszero, A) == nA @test A == B A = speye(Int, 5) @@ -820,17 +820,17 @@ end @test A[I] == A[X] == collect(1:10) A[I] = zeros(Int, 10) @test nnz(A) == 13 - @test countnz(A) == 3 + @test count(!iszero, A) == 3 @test A[I] == A[X] == zeros(Int, 10) c = collect(11:20); c[1] = c[3] = 0 A[I] = c @test nnz(A) == 13 - @test countnz(A) == 11 + @test count(!iszero, A) == 11 @test A[I] == A[X] == c A = speye(Int, 5) A[I] = c @test nnz(A) == 12 - @test countnz(A) == 11 + @test count(!iszero, A) == 11 @test A[I] == A[X] == c S = sprand(50, 30, 0.5, x -> round.(Int, rand(x) * 100)) @@ -839,14 +839,14 @@ end FI = Array(I) @test sparse(FS[FI]) == S[I] == S[FI] @test sum(S[FI]) + sum(S[.!FI]) == sum(S) - @test countnz(I) == count(I) + @test count(!iszero, I) == count(I) sumS1 = sum(S) sumFI = sum(S[FI]) nnzS1 = nnz(S) S[FI] = 0 sumS2 = sum(S) - cnzS2 = countnz(S) + cnzS2 = count(!iszero, S) @test sum(S[FI]) == 0 @test nnz(S) == nnzS1 @test (sum(S) + sumFI) == sumS1 @@ -857,7 +857,7 @@ end S[FI] = 0 @test sum(S) == sumS2 @test nnz(S) == nnzS3 - @test countnz(S) == cnzS2 + @test count(!iszero, S) == cnzS2 S[FI] = [1:sum(FI);] @test sum(S) == sumS2 + sum(1:sum(FI)) @@ -1148,11 +1148,11 @@ end sm = sparse(D) sv = sparsevec(D) - @test countnz(sm) == 10 - @test countnz(sv) == 10 + @test count(!iszero, sm) == 10 + @test count(!iszero, sv) == 10 - @test countnz(sparse(Diagonal(Int[]))) == 0 - @test countnz(sparsevec(Diagonal(Int[]))) == 0 + @test count(!iszero, sparse(Diagonal(Int[]))) == 0 + @test count(!iszero, sparsevec(Diagonal(Int[]))) == 0 end @testset "explicit zeros" begin diff --git a/test/sparse/sparsevector.jl b/test/sparse/sparsevector.jl index 4d849f2080b9c..fefac54f81a47 100644 --- a/test/sparse/sparsevector.jl +++ b/test/sparse/sparsevector.jl @@ -20,7 +20,7 @@ let x = spv_x1 @test size(x,2) == 1 @test !isempty(x) - @test countnz(x) == 3 + @test count(!iszero, x) == 3 @test nnz(x) == 3 @test SparseArrays.nonzeroinds(x) == [2, 5, 6] @test nonzeros(x) == [1.25, -0.75, 3.5] diff --git a/test/sparse/spqr.jl b/test/sparse/spqr.jl index 0e17f0be2a065..071b4a0abe6dc 100644 --- a/test/sparse/spqr.jl +++ b/test/sparse/spqr.jl @@ -73,7 +73,7 @@ end xd = full(A)\b # check that basic solution has more zeros - @test countnz(xs) < countnz(xd) + @test count(!iszero, xs) < count(!iszero, xd) @test A*xs ≈ A*xd end diff --git a/test/subarray.jl b/test/subarray.jl index f80ba3c200c1b..b25ce1a4dfc95 100644 --- a/test/subarray.jl +++ b/test/subarray.jl @@ -421,7 +421,7 @@ msk = ones(Bool, 2, 2) msk[2,1] = false sA = view(A, :, :, 1) sA[msk] = 1.0 -@test sA[msk] == ones(countnz(msk)) +@test sA[msk] == ones(count(msk)) # bounds checking upon construction; see #4044, #10296 @test_throws BoundsError view(1:10, 8:11) From 46bff1bf83f82107369b23c3163a53aed4f9801c Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 29 Aug 2017 14:26:22 -0400 Subject: [PATCH 195/324] test-codegen: make jl_dump_compiles test more reliable (#23486) This was a flaky test, and it's not entirely clear what it was testing. Common failure was: FAILURE Error in testset codegen: Test Failed Expression: tempty == true Evaluated: false == true ERROR: LoadError: Test run finished with errors while loading C:\projects\julia\julia-a661736be5\share\julia\test\runtests.jl, in expression starting on line 29 Command exited with code 1 --- test/codegen.jl | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/test/codegen.jl b/test/codegen.jl index a5009875ef290..6925ef1266ff8 100644 --- a/test/codegen.jl +++ b/test/codegen.jl @@ -51,13 +51,9 @@ end function test_jl_dump_compiles() tfile = tempname() io = open(tfile, "w") + @eval(test_jl_dump_compiles_internal(x) = x) ccall(:jl_dump_compiles, Void, (Ptr{Void},), io.handle) - eval(@noinline function test_jl_dump_compiles_internal(x) - if x > 0 - test_jl_dump_compiles_internal(x-1) - end - end) - test_jl_dump_compiles_internal(1) + @eval test_jl_dump_compiles_internal(1) ccall(:jl_dump_compiles, Void, (Ptr{Void},), C_NULL) close(io) tstats = stat(tfile) @@ -71,8 +67,9 @@ end function test_jl_dump_compiles_toplevel_thunks() tfile = tempname() io = open(tfile, "w") + topthunk = expand(Main, :(for i in 1:10; end)) ccall(:jl_dump_compiles, Void, (Ptr{Void},), io.handle) - eval(expand(Main, :(for i in 1:10 end))) + Core.eval(Main, topthunk) ccall(:jl_dump_compiles, Void, (Ptr{Void},), C_NULL) close(io) tstats = stat(tfile) From 99c84077f59d6c980d9ca340f5ce3e187814efab Mon Sep 17 00:00:00 2001 From: Matt Bauman Date: Tue, 29 Aug 2017 14:54:23 -0400 Subject: [PATCH 196/324] Add propagate_inbounds for LogicalIndex iteration This optimization had been occurring before, but still isn't entirely valid --- base/multidimensional.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 64cf42b8b44c7..de94cb39127a0 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -416,7 +416,7 @@ end r = CartesianRange(indices(L.mask)) return (r, start(r), 1) end -@inline function next(L::LogicalIndex, s) +@propagate_inbounds function next(L::LogicalIndex, s) # We're looking for the n-th true element, using iterator r at state i r, i, n = s while true From 15fb3db773d8c3340a7379f27c72b285b3f2c89c Mon Sep 17 00:00:00 2001 From: Tucker McClure Date: Tue, 29 Aug 2017 12:27:22 -0700 Subject: [PATCH 197/324] Added notes on closing a library. (#23489) * Added notes on closing a library. Fixes 23459 * shorten line lengths * "can"/"could" consistency. --- doc/src/manual/calling-c-and-fortran-code.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/doc/src/manual/calling-c-and-fortran-code.md b/doc/src/manual/calling-c-and-fortran-code.md index 5aae9bc30e090..f8ac137da78f6 100644 --- a/doc/src/manual/calling-c-and-fortran-code.md +++ b/doc/src/manual/calling-c-and-fortran-code.md @@ -873,6 +873,25 @@ mylibvar = Libdl.dlopen("mylib") ccall(@dlsym("myfunc", mylibvar), Void, ()) ``` +## Closing a Library + +It is sometimes useful to close (unload) a library so that it can be reloaded. +For instance, when developing C code for use with Julia, one may need to compile, +call the C code from Julia, then close the library, make an edit, recompile, +and load in the new changes. One can either restart Julia or use the +`Libdl` functions to manage the library explicitly, such as: + +```julia +lib = Libdl.dlopen("./my_lib.so") # Open the library explicitly. +sym = Libdl.dlsym(lib, :my_fcn) # Get a symbol for the function to call. +ccall(sym, ...) # Use the symbol instead of the (symbol, library) tuple (remaining arguments are the same). +Libdl.dlclose(lib) # Close the library explicitly. +``` + +Note that when using `ccall` with the tuple input +(e.g., `ccall((:my_fcn, "./my_lib.so"), ...)`), the library is opened implicitly +and it may not be explicitly closed. + ## Calling Convention The second argument to [`ccall`](@ref) can optionally be a calling convention specifier (immediately From 109b0b4a353240ebf5406864d6cc8d5506b902a6 Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Tue, 29 Aug 2017 12:55:27 -0700 Subject: [PATCH 198/324] Doc warn and warn_with (#23441) * Doc warn and warn_with --- base/libgit2/types.jl | 15 ++++++++++++++- doc/src/devdocs/libgit2.md | 1 + 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/base/libgit2/types.jl b/base/libgit2/types.jl index f74a1499d1008..d713b6f94a772 100644 --- a/base/libgit2/types.jl +++ b/base/libgit2/types.jl @@ -956,8 +956,12 @@ The fields represent: boundary::Char end +""" + with(f::Function, obj) -""" Resource management helper function +Resource management helper function. Applies `f` to `obj`, making sure to call +`close` on `obj` after `f` successfully returns or throws an error. Ensures that +allocated git resources are finalized as soon as they are no longer needed. """ function with(f::Function, obj) try @@ -969,6 +973,15 @@ end with(f::Function, ::Type{T}, args...) where {T} = with(f, T(args...)) +""" + with_warn(f::Function, ::Type{T}, args...) + +Resource management helper function. Apply `f` to `args`, first constructing +an instance of type `T` from `args`. Makes sure to call `close` on the resulting +object after `f` successfully returns or throws an error. Ensures that +allocated git resources are finalized as soon as they are no longer needed. If an +error is thrown by `f`, a warning is shown containing the error. +""" function with_warn(f::Function, ::Type{T}, args...) where T obj = T(args...) try diff --git a/doc/src/devdocs/libgit2.md b/doc/src/devdocs/libgit2.md index 13709659c3c1f..cc5eca7d9b540 100644 --- a/doc/src/devdocs/libgit2.md +++ b/doc/src/devdocs/libgit2.md @@ -145,5 +145,6 @@ Base.LibGit2.update! Base.LibGit2.url Base.LibGit2.version Base.LibGit2.with +Base.LibGit2.with_warn Base.LibGit2.workdir ``` From d8e5b9696f574b024781038db69a6e8a379d4bd4 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Tue, 29 Aug 2017 16:37:23 -0400 Subject: [PATCH 199/324] fix GC rooting of arguments in cfunction `_gfthunk` wrappers --- src/codegen.cpp | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index ca125ac26d66d..9ab787c604734 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3960,21 +3960,16 @@ static void emit_cfunc_invalidate( allocate_gc_frame(ctx, b0); Function::arg_iterator AI = gf_thunk->arg_begin(); - Value *myargs = new AllocaInst(T_prjlvalue, -#if JL_LLVM_VERSION >= 50000 - 0, -#endif - ConstantInt::get(T_int32, nargs), "jlcall", ctx.ptlsStates); + jl_cgval_t *myargs = (jl_cgval_t*)alloca(sizeof(jl_cgval_t) * nargs); if (cc == jl_returninfo_t::SRet || cc == jl_returninfo_t::Union) ++AI; for (size_t i = 0; i < nargs; i++) { jl_value_t *jt = jl_nth_slot_type(lam->specTypes, i); bool isboxed; Type *et = julia_type_to_llvm(jt, &isboxed); - Value *arg_box; if (type_is_ghost(et)) { assert(jl_is_datatype(jt) && ((jl_datatype_t*)jt)->instance); - arg_box = literal_pointer_val(ctx, ((jl_datatype_t*)jt)->instance); + myargs[i] = mark_julia_const(((jl_datatype_t*)jt)->instance); } else { Value *arg_v = &*AI; @@ -3982,23 +3977,20 @@ static void emit_cfunc_invalidate( Type *at = arg_v->getType(); if (isboxed) { assert(at == T_prjlvalue && et == T_pjlvalue); - arg_box = arg_v; + myargs[i] = mark_julia_type(ctx, arg_v, true, jt); } else if (et->isAggregateType()) { - arg_box = boxed(ctx, mark_julia_slot(arg_v, jt, NULL, tbaa_const), false); + myargs[i] = mark_julia_slot(arg_v, jt, NULL, tbaa_const); } else { assert(at == et); - arg_box = boxed(ctx, mark_julia_type(ctx, arg_v, false, jt), false); + myargs[i] = mark_julia_type(ctx, arg_v, false, jt); } (void)at; } - Value *argn = ctx.builder.CreateConstInBoundsGEP1_32(T_prjlvalue, myargs, i); - ctx.builder.CreateStore(maybe_decay_untracked(arg_box), argn); } assert(AI == gf_thunk->arg_end()); - Value *nargs_v = ConstantInt::get(T_int32, nargs); - Value *gf_ret = ctx.builder.CreateCall(prepare_call(jlapplygeneric_func), { myargs, nargs_v }); + Value *gf_ret = emit_jlcall(ctx, jlapplygeneric_func, NULL, myargs, nargs); jl_cgval_t gf_retbox = mark_julia_type(ctx, gf_ret, true, jl_any_type, /*needsroot*/false); jl_value_t *astrt = lam->rettype; if (cc != jl_returninfo_t::Boxed) { From 8522ff0afb90e0cc98e067d77155c334d570ee1a Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Mon, 28 Aug 2017 12:45:03 -0400 Subject: [PATCH 200/324] deprecate `catch `. addresses #19987 --- NEWS.md | 3 +++ base/libgit2/callbacks.jl | 2 +- src/julia-parser.scm | 5 ++++- test/parse.jl | 10 +++++----- test/reflection.jl | 2 +- 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/NEWS.md b/NEWS.md index 0fe173063bc5d..41c50c1bca9fb 100644 --- a/NEWS.md +++ b/NEWS.md @@ -70,6 +70,9 @@ Language changes warning, so that this syntax can be disallowed or given a new meaning in a future version ([#5148]). + * Placing an expression after `catch`, as in `catch f(x)`, is deprecated. + Use `catch; f(x)` instead ([#19987]). + * 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]). diff --git a/base/libgit2/callbacks.jl b/base/libgit2/callbacks.jl index 4923318738b95..1b288b8edd4f1 100644 --- a/base/libgit2/callbacks.jl +++ b/base/libgit2/callbacks.jl @@ -17,7 +17,7 @@ function mirror_callback(remote::Ptr{Ptr{Void}}, repo_ptr::Ptr{Void}, config = GitConfig(GitRepo(repo_ptr,false)) name_str = unsafe_string(name) err= try set!(config, "remote.$name_str.mirror", true) - catch -1 + catch; -1 finally close(config) end err != 0 && return Cint(err) diff --git a/src/julia-parser.scm b/src/julia-parser.scm index 32281cca87b27..3a17ab7346eb8 100644 --- a/src/julia-parser.scm +++ b/src/julia-parser.scm @@ -1464,7 +1464,10 @@ (var (if nl #f (parse-eq* s))) (var? (and (not nl) (or (and (symbol? var) (not (eq? var 'false)) (not (eq? var 'true))) - (and (length= var 2) (eq? (car var) '$))))) + (and (length= var 2) (eq? (car var) '$)) + (and (syntax-deprecation s (string "catch " (deparse var) "") + (string "catch; " (deparse var) "")) + #f)))) (catch-block (if (eq? (require-token s) 'finally) `(block ,(line-number-node s)) (parse-block s)))) diff --git a/test/parse.jl b/test/parse.jl index 2f57e98593752..7d5aaad45964e 100644 --- a/test/parse.jl +++ b/test/parse.jl @@ -565,10 +565,10 @@ add_method_to_glob_fn!() @test expand(Main, :(f(d:Int...) = nothing)) == Expr(:error, "\"d:Int\" is not a valid function argument name") # issue #16517 -@test (try error(); catch 0; end) === 0 -@test (try error(); catch false; end) === false # false and true are Bool literals, not variables -@test (try error(); catch true; end) === true -f16517() = try error(); catch 0; end +@test (try error(); catch; 0; end) === 0 +@test (try error(); catch; false; end) === false # false and true are Bool literals, not variables +@test (try error(); catch; true; end) === true +f16517() = try error(); catch; 0; end @test f16517() === 0 # issue #16671 @@ -592,7 +592,7 @@ end # issue #16686 @test parse("try x - catch test() + catch; test() y end") == Expr(:try, Expr(:block, diff --git a/test/reflection.jl b/test/reflection.jl index 11c23ea08f96d..12683a81d95a0 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -46,7 +46,7 @@ test_code_reflections(test_bin_reflection, code_native) mktemp() do f, io OLDSTDOUT = STDOUT redirect_stdout(io) - @test try @code_native map(abs, rand(3)); true; catch false; end + @test try @code_native map(abs, rand(3)); true; catch; false; end redirect_stdout(OLDSTDOUT) nothing end From 709a639e81ada987ec342d2e9629c2141a07c57b Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Tue, 29 Aug 2017 17:27:45 -0400 Subject: [PATCH 201/324] fix `promote_rule` for `Bidiagonal` and `Tridiagonal` --- base/linalg/bidiag.jl | 4 ++-- test/linalg/bidiag.jl | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/base/linalg/bidiag.jl b/base/linalg/bidiag.jl index 9e3d08ceef2ad..2806e908fc79e 100644 --- a/base/linalg/bidiag.jl +++ b/base/linalg/bidiag.jl @@ -148,7 +148,7 @@ end convert(::Type{Matrix}, A::Bidiagonal{T}) where {T} = convert(Matrix{T}, A) convert(::Type{Array}, A::Bidiagonal) = convert(Matrix, A) full(A::Bidiagonal) = convert(Array, A) -promote_rule(::Type{Matrix{T}}, ::Type{Bidiagonal{S}}) where {T,S} = Matrix{promote_type(T,S)} +promote_rule(::Type{Matrix{T}}, ::Type{<:Bidiagonal{S}}) where {T,S} = Matrix{promote_type(T,S)} #Converting from Bidiagonal to Tridiagonal Tridiagonal(M::Bidiagonal{T}) where {T} = convert(Tridiagonal{T}, M) @@ -158,7 +158,7 @@ function convert(::Type{Tridiagonal{T}}, A::Bidiagonal) where T z = fill!(similar(ev), zero(T)) A.uplo == 'U' ? Tridiagonal(z, dv, ev) : Tridiagonal(ev, dv, z) end -promote_rule(::Type{Tridiagonal{T}}, ::Type{Bidiagonal{S}}) where {T,S} = Tridiagonal{promote_type(T,S)} +promote_rule(::Type{<:Tridiagonal{T}}, ::Type{<:Bidiagonal{S}}) where {T,S} = Tridiagonal{promote_type(T,S)} # No-op for trivial conversion Bidiagonal{T} -> Bidiagonal{T} convert(::Type{Bidiagonal{T}}, A::Bidiagonal{T}) where {T} = A diff --git a/test/linalg/bidiag.jl b/test/linalg/bidiag.jl index 2ecd02da6b572..c72e4f353f228 100644 --- a/test/linalg/bidiag.jl +++ b/test/linalg/bidiag.jl @@ -286,7 +286,9 @@ end C = Tridiagonal(rand(Float64,9),rand(Float64,10),rand(Float64,9)) @test promote_rule(Matrix{Float64}, Bidiagonal{Float64}) == Matrix{Float64} @test promote(B,A) == (B, convert(Matrix{Float64}, A)) + @test promote(B,A) isa Tuple{Matrix{Float64}, Matrix{Float64}} @test promote(C,A) == (C,Tridiagonal(zeros(Float64,9),convert(Vector{Float64},A.dv),convert(Vector{Float64},A.ev))) + @test promote(C,A) isa Tuple{Tridiagonal, Tridiagonal} end import Base.LinAlg: fillslots!, UnitLowerTriangular From 1c75a4d7180729658302b8c41297b64ddad5005f Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Tue, 29 Aug 2017 14:36:40 -0700 Subject: [PATCH 202/324] Finish docs for GitTree (#23451) * Finish docs for GitTree --- base/libgit2/tree.jl | 49 ++++++++++++++++++++++++++++++++++++-- doc/src/devdocs/libgit2.md | 5 ++++ 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/base/libgit2/tree.jl b/base/libgit2/tree.jl index 60da6a821f0be..3d87a42593efc 100644 --- a/base/libgit2/tree.jl +++ b/base/libgit2/tree.jl @@ -1,11 +1,21 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license """ -Traverse the entries in a tree and its subtrees in post or pre order. + treewalk(f::Function, tree::GitTree, payload=Any[], post::Bool=false) -Function parameter should have following signature: +Traverse the entries in `tree` and its subtrees in post or pre order. Preorder +means beginning at the root and then traversing the leftmost subtree (and +recursively on down through that subtree's leftmost subtrees) and moving right +through the subtrees. Postorder means beginning at the bottom of the leftmost +subtree, traversing upwards through it, then traversing the next right subtree +(again beginning at the bottom) and finally visiting the tree root last of all. + +The function parameter `f` should have following signature: (Cstring, Ptr{Void}, Ptr{Void}) -> Cint + +A negative value returned from `f` stops the tree walk. A positive value means +that the entry will be skipped if `post` is `false`. """ function treewalk(f::Function, tree::GitTree, payload=Any[], post::Bool = false) cbf = cfunction(f, Cint, Tuple{Cstring, Ptr{Void}, Ptr{Void}}) @@ -19,21 +29,42 @@ end repository(tree::GitTree) = tree.owner repository(te::GitTreeEntry) = repository(te.owner) +""" + filename(te::GitTreeEntry) + +Return the filename of the object on disk to which `te` refers. +""" function filename(te::GitTreeEntry) str = ccall((:git_tree_entry_name, :libgit2), Cstring, (Ptr{Void},), te.ptr) str != C_NULL && return unsafe_string(str) return nothing end +""" + filemode(te::GitTreeEntry) -> Cint + +Return the UNIX filemode of the object on disk to which `te` refers as an integer. +""" function filemode(te::GitTreeEntry) return ccall((:git_tree_entry_filemode, :libgit2), Cint, (Ptr{Void},), te.ptr) end +""" + entrytype(te::GitTreeEntry) + +Return the type of the object to which `te` refers. The result will be +one of the types which [`objtype`](@ref) returns, e.g. a `GitTree` or `GitBlob`. +""" function entrytype(te::GitTreeEntry) otype = ccall((:git_tree_entry_type, :libgit2), Cint, (Ptr{Void},), te.ptr) return objtype(Consts.OBJECT(otype)) end +""" + entryid(te::GitTreeEntry) + +Return the [`GitHash`](@ref) of the object to which `te` refers. +""" function entryid(te::GitTreeEntry) oid_ptr = ccall((:git_tree_entry_id, :libgit2), Ptr{UInt8}, (Ptr{Void},), te.ptr) return GitHash(oid_ptr) @@ -53,6 +84,20 @@ function Base.getindex(tree::GitTree, i::Integer) return GitTreeEntry(tree, te_ptr, false) end +""" + (::Type{T})(te::GitTreeEntry) where T<:GitObject + +Get the git object to which `te` refers and return it as its actual type (the type +[`entrytype`](@ref) would show), for instance a `GitBlob` or `GitTag`. + +# Examples +```julia +tree = LibGit2.GitTree(repo, "HEAD^{tree}") +tree_entry = tree[1] +blob = LibGit2.GitBlob(tree_entry) +``` +""" +function GitObject(e::GitTreeEntry) end function (::Type{T})(te::GitTreeEntry) where T<:GitObject repo = repository(te) obj_ptr_ptr = Ref{Ptr{Void}}(C_NULL) diff --git a/doc/src/devdocs/libgit2.md b/doc/src/devdocs/libgit2.md index cc5eca7d9b540..7c69b7adac8eb 100644 --- a/doc/src/devdocs/libgit2.md +++ b/doc/src/devdocs/libgit2.md @@ -76,6 +76,8 @@ Base.LibGit2.credentials_cb Base.LibGit2.default_signature Base.LibGit2.delete_branch Base.LibGit2.diff_files +Base.LibGit2.entryid +Base.LibGit2.entrytype Base.LibGit2.fetch Base.LibGit2.fetchheads Base.LibGit2.fetch_refspecs @@ -85,6 +87,8 @@ Base.LibGit2.merge!(::Base.LibGit2.GitRepo; ::Any...) Base.LibGit2.ffmerge! Base.LibGit2.fullname Base.LibGit2.features +Base.LibGit2.filename +Base.LibGit2.filemode Base.LibGit2.get_creds! Base.LibGit2.gitdir Base.LibGit2.@githash_str @@ -147,4 +151,5 @@ Base.LibGit2.version Base.LibGit2.with Base.LibGit2.with_warn Base.LibGit2.workdir +Base.LibGit2.GitObject(::Base.LibGit2.GitTreeEntry) ``` From 9454cd4df0cf4aae7e7385f00582c225a9270e2e Mon Sep 17 00:00:00 2001 From: Mus M Date: Tue, 29 Aug 2017 17:47:28 -0400 Subject: [PATCH 203/324] Remove global const in init in cholmod (#23449) * Remove global const in init in cholmod * Make refs and commonStruct a const * Remove common function * add back removed space [ci skip] --- base/sparse/cholmod.jl | 169 ++++++++++++++++++++--------------------- base/sparse/spqr.jl | 6 +- test/sparse/cholmod.jl | 12 +-- 3 files changed, 91 insertions(+), 96 deletions(-) diff --git a/base/sparse/cholmod.jl b/base/sparse/cholmod.jl index 34d1eaad041a1..062144d3b2be7 100644 --- a/base/sparse/cholmod.jl +++ b/base/sparse/cholmod.jl @@ -26,6 +26,16 @@ include("cholmod_h.jl") const CHOLMOD_MIN_VERSION = v"2.1.1" +const common_struct = Vector{UInt8}() + +const common_supernodal = Ref{Ptr{Cint}}() +const common_final_ll = Ref{Ptr{Cint}}() +const common_print = Ref{Ptr{Cint}}() +const common_itype = Ref{Ptr{Cint}}() +const common_dtype = Ref{Ptr{Cint}}() +const common_nmethods = Ref{Ptr{Cint}}() +const common_postorder = Ref{Ptr{Cint}}() + ### These offsets are defined in SuiteSparse_wrapper.c const common_size = ccall((:jl_cholmod_common_size,:libsuitesparse_wrapper),Int,()) @@ -56,8 +66,6 @@ function defaults(a::Vector{UInt8}) return a end -common() = commonStruct - const build_version_array = Vector{Cint}(3) ccall((:jl_cholmod_version, :libsuitesparse_wrapper), Cint, (Ptr{Cint},), build_version_array) const build_version = VersionNumber(build_version_array...) @@ -129,27 +137,21 @@ function __init__() end ### Initiate CHOLMOD - ### The common struct. Controls the type of factorization and keeps pointers + ### common_struct controls the type of factorization and keeps pointers ### to temporary memory. - global const commonStruct = fill(0xff, common_size) - - global const common_supernodal = - convert(Ptr{Cint}, pointer(commonStruct, cholmod_com_offsets[4] + 1)) - global const common_final_ll = - convert(Ptr{Cint}, pointer(commonStruct, cholmod_com_offsets[7] + 1)) - global const common_print = - convert(Ptr{Cint}, pointer(commonStruct, cholmod_com_offsets[13] + 1)) - global const common_itype = - convert(Ptr{Cint}, pointer(commonStruct, cholmod_com_offsets[18] + 1)) - global const common_dtype = - convert(Ptr{Cint}, pointer(commonStruct, cholmod_com_offsets[19] + 1)) - global const common_nmethods = - convert(Ptr{Cint}, pointer(commonStruct, cholmod_com_offsets[15] + 1)) - global const common_postorder = - convert(Ptr{Cint}, pointer(commonStruct, cholmod_com_offsets[17] + 1)) - - start(commonStruct) # initializes CHOLMOD - set_print_level(commonStruct, 0) # no printing from CHOLMOD by default + resize!(common_struct, common_size) + fill!(common_struct, 0xff) + + common_supernodal[] = pointer(common_struct, cholmod_com_offsets[4] + 1) + common_final_ll[] = pointer(common_struct, cholmod_com_offsets[7] + 1) + common_print[] = pointer(common_struct, cholmod_com_offsets[13] + 1) + common_itype[] = pointer(common_struct, cholmod_com_offsets[18] + 1) + common_dtype[] = pointer(common_struct, cholmod_com_offsets[19] + 1) + common_nmethods[] = pointer(common_struct, cholmod_com_offsets[15] + 1) + common_postorder[] = pointer(common_struct, cholmod_com_offsets[17] + 1) + + start(common_struct) # initializes CHOLMOD + set_print_level(common_struct, 0) # no printing from CHOLMOD by default # Register gc tracked allocator if CHOLMOD is new enough if current_version >= v"3.0.0" @@ -166,9 +168,8 @@ function __init__() end end -function set_print_level(cm::Array{UInt8}, lev::Integer) - global common_print - unsafe_store!(common_print, lev) +function set_print_level(cm::Vector{UInt8}, lev::Integer) + unsafe_store!(common_print[], lev) end #################### @@ -397,35 +398,35 @@ Factor(FC::FactorComponent) = Factor(FC.F) function allocate_dense(nrow::Integer, ncol::Integer, d::Integer, ::Type{Float64}) Dense(ccall((:cholmod_l_allocate_dense, :libcholmod), Ptr{C_Dense{Float64}}, (Csize_t, Csize_t, Csize_t, Cint, Ptr{Void}), - nrow, ncol, d, REAL, common())) + nrow, ncol, d, REAL, common_struct)) end function allocate_dense(nrow::Integer, ncol::Integer, d::Integer, ::Type{Complex{Float64}}) Dense(ccall((:cholmod_l_allocate_dense, :libcholmod), Ptr{C_Dense{Complex{Float64}}}, (Csize_t, Csize_t, Csize_t, Cint, Ptr{Void}), - nrow, ncol, d, COMPLEX, common())) + nrow, ncol, d, COMPLEX, common_struct)) end free_dense!(p::Ptr{C_Dense{T}}) where {T} = ccall((:cholmod_l_free_dense, :libcholmod), - Cint, (Ref{Ptr{C_Dense{T}}}, Ptr{Void}), p, common()) + Cint, (Ref{Ptr{C_Dense{T}}}, Ptr{Void}), p, common_struct) function zeros(m::Integer, n::Integer, ::Type{T}) where T<:VTypes Dense(ccall((:cholmod_l_zeros, :libcholmod), Ptr{C_Dense{T}}, (Csize_t, Csize_t, Cint, Ptr{UInt8}), - m, n, xtyp(T), common())) + m, n, xtyp(T), common_struct)) end zeros(m::Integer, n::Integer) = zeros(m, n, Float64) function ones(m::Integer, n::Integer, ::Type{T}) where T<:VTypes Dense(ccall((:cholmod_l_ones, :libcholmod), Ptr{C_Dense{T}}, (Csize_t, Csize_t, Cint, Ptr{UInt8}), - m, n, xtyp(T), common())) + m, n, xtyp(T), common_struct)) end ones(m::Integer, n::Integer) = ones(m, n, Float64) function eye(m::Integer, n::Integer, ::Type{T}) where T<:VTypes Dense(ccall((:cholmod_l_eye, :libcholmod), Ptr{C_Dense{T}}, (Csize_t, Csize_t, Cint, Ptr{UInt8}), - m, n, xtyp(T), common())) + m, n, xtyp(T), common_struct)) end eye(m::Integer, n::Integer) = eye(m, n, Float64) eye(n::Integer) = eye(n, n, Float64) @@ -433,13 +434,13 @@ eye(n::Integer) = eye(n, n, Float64) function copy_dense(A::Dense{Tv}) where Tv<:VTypes Dense(ccall((:cholmod_l_copy_dense, :libcholmod), Ptr{C_Dense{Tv}}, (Ptr{C_Dense{Tv}}, Ptr{UInt8}), - A, common())) + A, common_struct)) end function sort!(S::Sparse{Tv}) where Tv<:VTypes @isok ccall((:cholmod_l_sort, :libcholmod), SuiteSparse_long, (Ptr{C_Sparse{Tv}}, Ptr{UInt8}), - S, common()) + S, common_struct) return S end @@ -455,14 +456,14 @@ function norm_dense(D::Dense{Tv}, p::Integer) where Tv<:VTypes end ccall((:cholmod_l_norm_dense, :libcholmod), Cdouble, (Ptr{C_Dense{Tv}}, Cint, Ptr{UInt8}), - D, p, common()) + D, p, common_struct) end ### cholmod_check.h ### function check_dense(A::Dense{T}) where T<:VTypes ccall((:cholmod_l_check_dense, :libcholmod), Cint, (Ptr{C_Dense{T}}, Ptr{UInt8}), - A.p, common()) != 0 + A.p, common_struct) != 0 end # Non-Dense wrappers @@ -474,7 +475,7 @@ function allocate_sparse(nrow::Integer, ncol::Integer, nzmax::Integer, (Csize_t, Csize_t, Csize_t, Cint, Cint, Cint, Cint, Ptr{Void}), nrow, ncol, nzmax, sorted, - packed, stype, REAL, common())) + packed, stype, REAL, common_struct)) end function allocate_sparse(nrow::Integer, ncol::Integer, nzmax::Integer, sorted::Bool, packed::Bool, stype::Integer, ::Type{Complex{Float64}}) @@ -483,45 +484,45 @@ function allocate_sparse(nrow::Integer, ncol::Integer, nzmax::Integer, (Csize_t, Csize_t, Csize_t, Cint, Cint, Cint, Cint, Ptr{Void}), nrow, ncol, nzmax, sorted, - packed, stype, COMPLEX, common())) + packed, stype, COMPLEX, common_struct)) end function free_sparse!(ptr::Ptr{C_Sparse{Tv}}) where Tv<:VTypes @isok ccall((@cholmod_name("free_sparse", SuiteSparse_long), :libcholmod), Cint, (Ref{Ptr{C_Sparse{Tv}}}, Ptr{UInt8}), - ptr, common()) + ptr, common_struct) end function free_sparse!(ptr::Ptr{C_SparseVoid}) @isok ccall((@cholmod_name("free_sparse", SuiteSparse_long), :libcholmod), Cint, (Ref{Ptr{C_SparseVoid}}, Ptr{UInt8}), - ptr, common()) + ptr, common_struct) end function free_factor!(ptr::Ptr{C_Factor{Tv}}) where Tv<:VTypes # Warning! Important that finalizer doesn't modify the global Common struct. @isok ccall((@cholmod_name("free_factor", SuiteSparse_long), :libcholmod), Cint, (Ref{Ptr{C_Factor{Tv}}}, Ptr{Void}), - ptr, common()) + ptr, common_struct) end function aat(A::Sparse{Tv}, fset::Vector{SuiteSparse_long}, mode::Integer) where Tv<:VRealTypes Sparse(ccall((@cholmod_name("aat", SuiteSparse_long), :libcholmod), Ptr{C_Sparse{Tv}}, (Ptr{C_Sparse{Tv}}, Ptr{SuiteSparse_long}, Csize_t, Cint, Ptr{UInt8}), - A, fset, length(fset), mode, common())) + A, fset, length(fset), mode, common_struct)) end function sparse_to_dense(A::Sparse{Tv}) where Tv<:VTypes Dense(ccall((@cholmod_name("sparse_to_dense", SuiteSparse_long),:libcholmod), Ptr{C_Dense{Tv}}, (Ptr{C_Sparse{Tv}}, Ptr{UInt8}), - A, common())) + A, common_struct)) end function dense_to_sparse(D::Dense{Tv}, ::Type{SuiteSparse_long}) where Tv<:VTypes Sparse(ccall((@cholmod_name("dense_to_sparse", SuiteSparse_long),:libcholmod), Ptr{C_Sparse{Tv}}, (Ptr{C_Dense{Tv}}, Cint, Ptr{UInt8}), - D, true, common())) + D, true, common_struct)) end function factor_to_sparse!(F::Factor{Tv}) where Tv<:VTypes @@ -530,14 +531,14 @@ function factor_to_sparse!(F::Factor{Tv}) where Tv<:VTypes Sparse(ccall((@cholmod_name("factor_to_sparse", SuiteSparse_long),:libcholmod), Ptr{C_Sparse{Tv}}, (Ptr{C_Factor{Tv}}, Ptr{UInt8}), - F, common())) + F, common_struct)) end function change_factor!(::Type{Float64}, to_ll::Bool, to_super::Bool, to_packed::Bool, to_monotonic::Bool, F::Factor{Tv}) where Tv<:VTypes @isok ccall((@cholmod_name("change_factor", SuiteSparse_long),:libcholmod), Cint, (Cint, Cint, Cint, Cint, Cint, Ptr{C_Factor{Tv}}, Ptr{UInt8}), - REAL, to_ll, to_super, to_packed, to_monotonic, F, common()) + REAL, to_ll, to_super, to_packed, to_monotonic, F, common_struct) # don't register finalizer since we reuse object Factor{Float64}(pointer(F), false) end @@ -546,7 +547,7 @@ function change_factor!(::Type{Complex{Float64}}, to_ll::Bool, to_super::Bool, to_packed::Bool, to_monotonic::Bool, F::Factor{Tv}) where Tv<:VTypes @isok ccall((@cholmod_name("change_factor", SuiteSparse_long),:libcholmod), Cint, (Cint, Cint, Cint, Cint, Cint, Ptr{C_Factor{Tv}}, Ptr{UInt8}), - COMPLEX, to_ll, to_super, to_packed, to_monotonic, F, common()) + COMPLEX, to_ll, to_super, to_packed, to_monotonic, F, common_struct) # don't register finalizer since we reuse object Factor{Complex{Float64}}(pointer(F), false) end @@ -554,77 +555,75 @@ end function check_sparse(A::Sparse{Tv}) where Tv<:VTypes ccall((@cholmod_name("check_sparse", SuiteSparse_long),:libcholmod), Cint, (Ptr{C_Sparse{Tv}}, Ptr{UInt8}), - A, common()) != 0 + A, common_struct) != 0 end function check_factor(F::Factor{Tv}) where Tv<:VTypes ccall((@cholmod_name("check_factor", SuiteSparse_long),:libcholmod), Cint, (Ptr{C_Factor{Tv}}, Ptr{UInt8}), - F, common()) != 0 + F, common_struct) != 0 end function nnz(A::Sparse{Tv}) where Tv<:VTypes ccall((@cholmod_name("nnz", SuiteSparse_long),:libcholmod), Int, (Ptr{C_Sparse{Tv}}, Ptr{UInt8}), - A, common()) + A, common_struct) end function speye(m::Integer, n::Integer, ::Type{Tv}) where Tv<:VTypes Sparse(ccall((@cholmod_name("speye", SuiteSparse_long), :libcholmod), Ptr{C_Sparse{Tv}}, (Csize_t, Csize_t, Cint, Ptr{UInt8}), - m, n, xtyp(Tv), common())) + m, n, xtyp(Tv), common_struct)) end function spzeros(m::Integer, n::Integer, nzmax::Integer, ::Type{Tv}) where Tv<:VTypes Sparse(ccall((@cholmod_name("spzeros", SuiteSparse_long), :libcholmod), Ptr{C_Sparse{Tv}}, (Csize_t, Csize_t, Csize_t, Cint, Ptr{UInt8}), - m, n, nzmax, xtyp(Tv), common())) + m, n, nzmax, xtyp(Tv), common_struct)) end function transpose_(A::Sparse{Tv}, values::Integer) where Tv<:VTypes Sparse(ccall((@cholmod_name("transpose", SuiteSparse_long),:libcholmod), Ptr{C_Sparse{Tv}}, (Ptr{C_Sparse{Tv}}, Cint, Ptr{UInt8}), - A, values, common())) + A, values, common_struct)) end function copy_factor(F::Factor{Tv}) where Tv<:VTypes Factor(ccall((@cholmod_name("copy_factor", SuiteSparse_long),:libcholmod), Ptr{C_Factor{Tv}}, (Ptr{C_Factor{Tv}}, Ptr{UInt8}), - F, common())) + F, common_struct)) end function copy_sparse(A::Sparse{Tv}) where Tv<:VTypes Sparse(ccall((@cholmod_name("copy_sparse", SuiteSparse_long),:libcholmod), Ptr{C_Sparse{Tv}}, (Ptr{C_Sparse{Tv}}, Ptr{UInt8}), - A, common())) + A, common_struct)) end function copy(A::Sparse{Tv}, stype::Integer, mode::Integer) where Tv<:VRealTypes Sparse(ccall((@cholmod_name("copy", SuiteSparse_long),:libcholmod), Ptr{C_Sparse{Tv}}, (Ptr{C_Sparse{Tv}}, Cint, Cint, Ptr{UInt8}), - A, stype, mode, common())) + A, stype, mode, common_struct)) end ### cholmod_check.h ### function print_sparse(A::Sparse{Tv}, name::String) where Tv<:VTypes isascii(name) || error("non-ASCII name: $name") - cm = common() - set_print_level(cm, 3) + set_print_level(common_struct, 3) @isok ccall((@cholmod_name("print_sparse", SuiteSparse_long),:libcholmod), Cint, (Ptr{C_Sparse{Tv}}, Ptr{UInt8}, Ptr{UInt8}), - A, name, cm) + A, name, common_struct) nothing end function print_factor(F::Factor{Tv}, name::String) where Tv<:VTypes - cm = common() - set_print_level(cm, 3) + set_print_level(common_struct, 3) @isok ccall((@cholmod_name("print_factor", SuiteSparse_long),:libcholmod), Cint, (Ptr{C_Factor{Tv}}, Ptr{UInt8}, Ptr{UInt8}), - F, name, cm) + F, name, common_struct) nothing end @@ -641,7 +640,7 @@ function ssmult(A::Sparse{Tv}, B::Sparse{Tv}, stype::Integer, (Ptr{C_Sparse{Tv}}, Ptr{C_Sparse{Tv}}, Cint, Cint, Cint, Ptr{UInt8}), A, B, stype, values, - sorted, common())) + sorted, common_struct)) end function norm_sparse(A::Sparse{Tv}, norm::Integer) where Tv<:VTypes @@ -650,14 +649,14 @@ function norm_sparse(A::Sparse{Tv}, norm::Integer) where Tv<:VTypes end ccall((@cholmod_name("norm_sparse", SuiteSparse_long), :libcholmod), Cdouble, (Ptr{C_Sparse{Tv}}, Cint, Ptr{UInt8}), - A, norm, common()) + A, norm, common_struct) end function horzcat(A::Sparse{Tv}, B::Sparse{Tv}, values::Bool) where Tv<:VRealTypes Sparse(ccall((@cholmod_name("horzcat", SuiteSparse_long), :libcholmod), Ptr{C_Sparse{Tv}}, (Ptr{C_Sparse{Tv}}, Ptr{C_Sparse{Tv}}, Cint, Ptr{UInt8}), - A, B, values, common())) + A, B, values, common_struct)) end function scale!(S::Dense{Tv}, scale::Integer, A::Sparse{Tv}) where Tv<:VRealTypes @@ -686,7 +685,7 @@ function scale!(S::Dense{Tv}, scale::Integer, A::Sparse{Tv}) where Tv<:VRealType sA = unsafe_load(pointer(A)) @isok ccall((@cholmod_name("scale",SuiteSparse_long),:libcholmod), Cint, (Ptr{C_Dense{Tv}}, Cint, Ptr{C_Sparse{Tv}}, Ptr{UInt8}), - S, scale, A, common()) + S, scale, A, common_struct) A end @@ -702,7 +701,7 @@ function sdmult!(A::Sparse{Tv}, transpose::Bool, (Ptr{C_Sparse{Tv}}, Cint, Ref{Complex128}, Ref{Complex128}, Ptr{C_Dense{Tv}}, Ptr{C_Dense{Tv}}, Ptr{UInt8}), - A, transpose, α, β, X, Y, common()) + A, transpose, α, β, X, Y, common_struct) Y end @@ -710,7 +709,7 @@ function vertcat(A::Sparse{Tv}, B::Sparse{Tv}, values::Bool) where Tv<:VRealType Sparse(ccall((@cholmod_name("vertcat", SuiteSparse_long), :libcholmod), Ptr{C_Sparse{Tv}}, (Ptr{C_Sparse{Tv}}, Ptr{C_Sparse{Tv}}, Cint, Ptr{UInt8}), - A, B, values, common())) + A, B, values, common_struct)) end function symmetry(A::Sparse{Tv}, option::Integer) where Tv<:VTypes @@ -722,7 +721,7 @@ function symmetry(A::Sparse{Tv}, option::Integer) where Tv<:VTypes (Ptr{C_Sparse{Tv}}, Cint, Ptr{SuiteSparse_long}, Ptr{SuiteSparse_long}, Ptr{SuiteSparse_long}, Ptr{SuiteSparse_long}, Ptr{UInt8}), A, option, xmatched, pmatched, - nzoffdiag, nzdiag, common()) + nzoffdiag, nzdiag, common_struct) rv, xmatched[], pmatched[], nzoffdiag[], nzdiag[] end @@ -775,7 +774,7 @@ function solve(sys::Integer, F::Factor{Tv}, B::Dense{Tv}) where Tv<:VTypes end Dense(ccall((@cholmod_name("solve", SuiteSparse_long),:libcholmod), Ptr{C_Dense{Tv}}, (Cint, Ptr{C_Factor{Tv}}, Ptr{C_Dense{Tv}}, Ptr{UInt8}), - sys, F, B, common())) + sys, F, B, common_struct)) end function spsolve(sys::Integer, F::Factor{Tv}, B::Sparse{Tv}) where Tv<:VTypes @@ -786,7 +785,7 @@ function spsolve(sys::Integer, F::Factor{Tv}, B::Sparse{Tv}) where Tv<:VTypes Sparse(ccall((@cholmod_name("spsolve", SuiteSparse_long),:libcholmod), Ptr{C_Sparse{Tv}}, (Cint, Ptr{C_Factor{Tv}}, Ptr{C_Sparse{Tv}}, Ptr{UInt8}), - sys, F, B, common())) + sys, F, B, common_struct)) end # Autodetects the types @@ -794,7 +793,7 @@ function read_sparse(file::Libc.FILE, ::Type{SuiteSparse_long}) ptr = ccall((@cholmod_name("read_sparse", SuiteSparse_long), :libcholmod), Ptr{C_SparseVoid}, (Ptr{Void}, Ptr{UInt8}), - file.ptr, common()) + file.ptr, common_struct) if ptr == C_NULL throw(ArgumentError("sparse matrix construction failed. Check that input file is valid.")) end @@ -1267,8 +1266,6 @@ end (*)(A::Sparse, B::VecOrMat) = (*)(A, Dense(B)) function A_mul_Bc(A::Sparse{Tv}, B::Sparse{Tv}) where Tv<:VRealTypes - cm = common() - if A !== B aa1 = transpose_(B, 2) ## result of ssmult will have stype==0, contain numerical values and be sorted @@ -1311,14 +1308,14 @@ function fact_(A::Sparse{<:VTypes}, cm::Array{UInt8}; sA.stype == 0 && throw(ArgumentError("sparse matrix is not symmetric/Hermitian")) if !postorder - unsafe_store!(common_postorder, 0) + unsafe_store!(common_postorder[], 0) end if isempty(perm) F = analyze(A, cm) else # user permutation provided if userperm_only # use perm even if it is worse than AMD - unsafe_store!(common_nmethods, 1) + unsafe_store!(common_nmethods[], 1) end F = analyze_p(A, SuiteSparse_long[p-1 for p in perm], cm) end @@ -1327,13 +1324,11 @@ function fact_(A::Sparse{<:VTypes}, cm::Array{UInt8}; end function cholfact!(F::Factor{Tv}, A::Sparse{Tv}; shift::Real=0.0) where Tv - cm = common() - # Makes it an LLt - unsafe_store!(common_final_ll, 1) + unsafe_store!(common_final_ll[], 1) # Compute the numerical factorization - factorize_p!(A, shift, F, cm) + factorize_p!(A, shift, F, common_struct) return F end @@ -1365,7 +1360,7 @@ cholfact!(F::Factor, A::Union{SparseMatrixCSC{T}, function cholfact(A::Sparse; shift::Real=0.0, perm::AbstractVector{SuiteSparse_long}=SuiteSparse_long[]) - cm = defaults(common()) + cm = defaults(common_struct) set_print_level(cm, 0) # Compute the symbolic factorization @@ -1418,13 +1413,13 @@ cholfact(A::Union{SparseMatrixCSC{T}, SparseMatrixCSC{Complex{T}}, function ldltfact!(F::Factor{Tv}, A::Sparse{Tv}; shift::Real=0.0) where Tv - cm = defaults(common()) + cm = defaults(common_struct) set_print_level(cm, 0) # Makes it an LDLt - unsafe_store!(common_final_ll, 0) + unsafe_store!(common_final_ll[], 0) # Really make sure it's an LDLt by avoiding supernodal factorization - unsafe_store!(common_supernodal, 0) + unsafe_store!(common_supernodal[], 0) # Compute the numerical factorization factorize_p!(A, shift, F, cm) @@ -1459,13 +1454,13 @@ ldltfact!(F::Factor, A::Union{SparseMatrixCSC{T}, function ldltfact(A::Sparse; shift::Real=0.0, perm::AbstractVector{SuiteSparse_long}=SuiteSparse_long[]) - cm = defaults(common()) + cm = defaults(common_struct) set_print_level(cm, 0) # Makes it an LDLt - unsafe_store!(common_final_ll, 0) + unsafe_store!(common_final_ll[], 0) # Really make sure it's an LDLt by avoiding supernodal factorization - unsafe_store!(common_supernodal, 0) + unsafe_store!(common_supernodal[], 0) # Compute the symbolic factorization F = fact_(A, cm; perm = perm) @@ -1536,7 +1531,7 @@ function lowrankupdowndate!(F::Factor{Tv}, C::Sparse{Tv}, update::Cint) where Tv end @isok ccall((:cholmod_l_updown, :libcholmod), Cint, (Cint, Ptr{C_Sparse{Tv}}, Ptr{C_Factor{Tv}}, Ptr{Void}), - update, C, F, common()) + update, C, F, common_struct) F end diff --git a/base/sparse/spqr.jl b/base/sparse/spqr.jl index 6b27d0b25146b..d752712027760 100644 --- a/base/sparse/spqr.jl +++ b/base/sparse/spqr.jl @@ -60,7 +60,7 @@ function _qr!(ordering::Integer, tol::Real, econ::Integer, getCTX::Integer, H, # m-by-nh Householder vectors HPinv, # size m row permutation HTau, # 1-by-nh Householder coefficients - CHOLMOD.common()) # /* workspace and parameters */ + CHOLMOD.common_struct) # /* workspace and parameters */ if rnk < 0 error("Sparse QR factorization failed") @@ -79,7 +79,7 @@ function _qr!(ordering::Integer, tol::Real, econ::Integer, getCTX::Integer, # the common struct is updated ccall((:cholmod_l_free, :libcholmod), Void, (Csize_t, Cint, Ptr{CHOLMOD.SuiteSparse_long}, Ptr{Void}), - n, sizeof(CHOLMOD.SuiteSparse_long), e, CHOLMOD.common()) + n, sizeof(CHOLMOD.SuiteSparse_long), e, CHOLMOD.common_struct) end hpinv = HPinv[] if hpinv == C_NULL @@ -94,7 +94,7 @@ function _qr!(ordering::Integer, tol::Real, econ::Integer, getCTX::Integer, # the common struct is updated ccall((:cholmod_l_free, :libcholmod), Void, (Csize_t, Cint, Ptr{CHOLMOD.SuiteSparse_long}, Ptr{Void}), - m, sizeof(CHOLMOD.SuiteSparse_long), hpinv, CHOLMOD.common()) + m, sizeof(CHOLMOD.SuiteSparse_long), hpinv, CHOLMOD.common_struct) end return rnk, _E, _HPinv diff --git a/test/sparse/cholmod.jl b/test/sparse/cholmod.jl index d4c4a5c211919..0539006a6f52d 100644 --- a/test/sparse/cholmod.jl +++ b/test/sparse/cholmod.jl @@ -216,7 +216,7 @@ end ### illegal dtype (for now but should be supported at some point) p = ccall((:cholmod_l_allocate_sparse, :libcholmod), Ptr{CHOLMOD.C_SparseVoid}, (Csize_t, Csize_t, Csize_t, Cint, Cint, Cint, Cint, Ptr{Void}), - 1, 1, 1, true, true, 0, CHOLMOD.REAL, CHOLMOD.common()) + 1, 1, 1, true, true, 0, CHOLMOD.REAL, CHOLMOD.common_struct) puint = convert(Ptr{UInt32}, p) unsafe_store!(puint, CHOLMOD.SINGLE, 3*div(sizeof(Csize_t), 4) + 5*div(sizeof(Ptr{Void}), 4) + 4) @test_throws CHOLMOD.CHOLMODException CHOLMOD.Sparse(p) @@ -224,7 +224,7 @@ unsafe_store!(puint, CHOLMOD.SINGLE, 3*div(sizeof(Csize_t), 4) + 5*div(sizeof(Pt ### illegal dtype p = ccall((:cholmod_l_allocate_sparse, :libcholmod), Ptr{CHOLMOD.C_SparseVoid}, (Csize_t, Csize_t, Csize_t, Cint, Cint, Cint, Cint, Ptr{Void}), - 1, 1, 1, true, true, 0, CHOLMOD.REAL, CHOLMOD.common()) + 1, 1, 1, true, true, 0, CHOLMOD.REAL, CHOLMOD.common_struct) puint = convert(Ptr{UInt32}, p) unsafe_store!(puint, 5, 3*div(sizeof(Csize_t), 4) + 5*div(sizeof(Ptr{Void}), 4) + 4) @test_throws CHOLMOD.CHOLMODException CHOLMOD.Sparse(p) @@ -232,7 +232,7 @@ unsafe_store!(puint, 5, 3*div(sizeof(Csize_t), 4) + 5*div(sizeof(Ptr{Void}), 4) ### illegal xtype p = ccall((:cholmod_l_allocate_sparse, :libcholmod), Ptr{CHOLMOD.C_SparseVoid}, (Csize_t, Csize_t, Csize_t, Cint, Cint, Cint, Cint, Ptr{Void}), - 1, 1, 1, true, true, 0, CHOLMOD.REAL, CHOLMOD.common()) + 1, 1, 1, true, true, 0, CHOLMOD.REAL, CHOLMOD.common_struct) puint = convert(Ptr{UInt32}, p) unsafe_store!(puint, 3, 3*div(sizeof(Csize_t), 4) + 5*div(sizeof(Ptr{Void}), 4) + 3) @test_throws CHOLMOD.CHOLMODException CHOLMOD.Sparse(p) @@ -240,7 +240,7 @@ unsafe_store!(puint, 3, 3*div(sizeof(Csize_t), 4) + 5*div(sizeof(Ptr{Void}), 4) ### illegal itype p = ccall((:cholmod_l_allocate_sparse, :libcholmod), Ptr{CHOLMOD.C_SparseVoid}, (Csize_t, Csize_t, Csize_t, Cint, Cint, Cint, Cint, Ptr{Void}), - 1, 1, 1, true, true, 0, CHOLMOD.REAL, CHOLMOD.common()) + 1, 1, 1, true, true, 0, CHOLMOD.REAL, CHOLMOD.common_struct) puint = convert(Ptr{UInt32}, p) unsafe_store!(puint, CHOLMOD.INTLONG, 3*div(sizeof(Csize_t), 4) + 5*div(sizeof(Ptr{Void}), 4) + 2) @test_throws CHOLMOD.CHOLMODException CHOLMOD.Sparse(p) @@ -248,7 +248,7 @@ unsafe_store!(puint, CHOLMOD.INTLONG, 3*div(sizeof(Csize_t), 4) + 5*div(sizeof(P ### illegal itype p = ccall((:cholmod_l_allocate_sparse, :libcholmod), Ptr{CHOLMOD.C_SparseVoid}, (Csize_t, Csize_t, Csize_t, Cint, Cint, Cint, Cint, Ptr{Void}), - 1, 1, 1, true, true, 0, CHOLMOD.REAL, CHOLMOD.common()) + 1, 1, 1, true, true, 0, CHOLMOD.REAL, CHOLMOD.common_struct) puint = convert(Ptr{UInt32}, p) unsafe_store!(puint, 5, 3*div(sizeof(Csize_t), 4) + 5*div(sizeof(Ptr{Void}), 4) + 2) @test_throws CHOLMOD.CHOLMODException CHOLMOD.Sparse(p) @@ -297,7 +297,7 @@ end ## test free_sparse! p = ccall((:cholmod_l_allocate_sparse, :libcholmod), Ptr{CHOLMOD.C_Sparse{Float64}}, (Csize_t, Csize_t, Csize_t, Cint, Cint, Cint, Cint, Ptr{Void}), - 1, 1, 1, true, true, 0, CHOLMOD.REAL, CHOLMOD.common()) + 1, 1, 1, true, true, 0, CHOLMOD.REAL, CHOLMOD.common_struct) @test CHOLMOD.free_sparse!(p) for elty in (Float64, Complex{Float64}) From 90c91e3adea5a3d3b1ac1824a167ac8be22be5b9 Mon Sep 17 00:00:00 2001 From: Iblis Lin Date: Wed, 30 Aug 2017 07:04:12 +0800 Subject: [PATCH 204/324] test/file: try to fix freezing of test_22566 (#23001) - wrap the long single test scripts - using `$(repr(fn))` for inserting temp file name - use STDIN to synchronize fifo open calls for each end --- test/file.jl | 43 ++++++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/test/file.jl b/test/file.jl index c758edccd4ce8..da52c1a9d1e28 100644 --- a/test/file.jl +++ b/test/file.jl @@ -1122,22 +1122,35 @@ function test_13559() fn = tempname() run(`mkfifo $fn`) # use subprocess to write 127 bytes to FIFO - writer_cmds = "x=open(\"$fn\", \"w\"); for i=1:127 write(x,0xaa); flush(x); sleep(0.1) end; close(x); quit()" - open(pipeline(`$(Base.julia_cmd()) --startup-file=no -e $writer_cmds`, stderr=STDERR)) - #quickly read FIFO, draining it and blocking but not failing with EOFError yet + writer_cmds = """ + x = open($(repr(fn)), "w") + for i in 1:120 + write(x, 0xaa) + end + flush(x) + Test.@test read(STDIN, Int8) == 31 + for i in 1:7 + write(x, 0xaa) + end + close(x) + """ + p = open(pipeline(`$(Base.julia_cmd()) --startup-file=no -e $writer_cmds`, stderr=STDERR), "w") + # quickly read FIFO, draining it and blocking but not failing with EOFError yet r = open(fn, "r") # 15 proper reads - for i=1:15 - @test read(r, Int64) == -6148914691236517206 + for i in 1:15 + @test read(r, UInt64) === 0xaaaaaaaaaaaaaaaa end - # last read should throw EOFError when FIFO closes, since there are only 7 bytes available. - @test_throws EOFError read(r, Int64) + write(p, 0x1f) + # last read should throw EOFError when FIFO closes, since there are only 7 bytes (or less) available. + @test_throws EOFError read(r, UInt64) close(r) + @test success(p) rm(fn) end test_13559() end -@test_throws ArgumentError mkpath("fakepath",-1) +@test_throws ArgumentError mkpath("fakepath", -1) # issue #22566 if !Sys.iswindows() @@ -1145,13 +1158,21 @@ if !Sys.iswindows() fn = tempname() run(`mkfifo $fn`) - script = "x = open(\"$fn\", \"w\"); close(x)" + script = """ + x = open($(repr(fn)), "w") + write(x, 0x42) + flush(x) + Test.@test read(STDIN, Int8) == 21 + close(x) + """ cmd = `$(Base.julia_cmd()) --startup-file=no -e $script` - open(pipeline(cmd, stderr=STDERR)) + p = open(pipeline(cmd, stderr=STDERR), "w") r = open(fn, "r") + @test read(r, Int8) == 66 + write(p, 0x15) close(r) - + @test success(p) rm(fn) end From 881ab49633f394a507727f03e5af290399d6fac4 Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Tue, 29 Aug 2017 16:34:26 -0700 Subject: [PATCH 205/324] More docs for merge! (#23495) * More docs for merge! --- base/libgit2/merge.jl | 57 ++++++++++++++++++++++++++++++++++++++ doc/src/devdocs/libgit2.md | 3 ++ 2 files changed, 60 insertions(+) diff --git a/base/libgit2/merge.jl b/base/libgit2/merge.jl index 42e3283887ca8..91556476a581a 100644 --- a/base/libgit2/merge.jl +++ b/base/libgit2/merge.jl @@ -111,6 +111,29 @@ function ffmerge!(repo::GitRepo, ann::GitAnnotated) end # Merge changes into current head +""" + merge!(repo::GitRepo, anns::Vector{GitAnnotated}; kwargs...) -> Bool + +Merge changes from the annotated commits (captured as [`GitAnnotated`](@ref) objects) +`anns` into the HEAD of the repository `repo`. The keyword arguments are: + * `merge_opts::MergeOptions = MergeOptions()`: options for how to perform the merge, + including whether fastforwarding is allowed. See [`MergeOptions`](@ref) for more + information. + * `checkout_opts::CheckoutOptions = CheckoutOptions()`: options for how to perform + the checkout. See [`CheckoutOptions`](@ref) for more information. + +`anns` may refer to remote or local branch heads. Return `true` if the merge is +successful, otherwise return `false` (for instance, if no merge is possible +because the branches have no common ancestor). + +# Examples +```julia +upst_ann = LibGit2.GitAnnotated(repo, "branch/a") + +# merge the branch in +LibGit2.merge!(repo, [upst_ann]) +``` +""" function merge!(repo::GitRepo, anns::Vector{GitAnnotated}; merge_opts::MergeOptions = MergeOptions(), checkout_opts::CheckoutOptions = CheckoutOptions()) @@ -126,6 +149,40 @@ end # Internal implementation of merge. # Returns `true` if merge was successful, otherwise `false` +""" + merge!(repo::GitRepo, anns::Vector{GitAnnotated}, fastforward::Bool; kwargs...) -> Bool + +Merge changes from the annotated commits (captured as [`GitAnnotated`](@ref) objects) +`anns` into the HEAD of the repository `repo`. If `fastforward` is `true`, *only* a +fastforward merge is allowed. In this case, if conflicts occur, the merge will fail. +Otherwise, if `fastforward` is `false`, the merge may produce a conflict file which +the user will need to resolve. + +The keyword arguments are: + * `merge_opts::MergeOptions = MergeOptions()`: options for how to perform the merge, + including whether fastforwarding is allowed. See [`MergeOptions`](@ref) for more + information. + * `checkout_opts::CheckoutOptions = CheckoutOptions()`: options for how to perform + the checkout. See [`CheckoutOptions`](@ref) for more information. + +`anns` may refer to remote or local branch heads. Return `true` if the merge is +successful, otherwise return `false` (for instance, if no merge is possible +because the branches have no common ancestor). + +# Examples +```julia +upst_ann_1 = LibGit2.GitAnnotated(repo, "branch/a") + +# merge the branch in, fastforward +LibGit2.merge!(repo, [upst_ann_1], true) + +# merge conflicts! +upst_ann_2 = LibGit2.GitAnnotated(repo, "branch/b") +# merge the branch in, try to fastforward +LibGit2.merge!(repo, [upst_ann_2], true) # will return false +LibGit2.merge!(repo, [upst_ann_2], false) # will return true +``` +""" function merge!(repo::GitRepo, anns::Vector{GitAnnotated}, fastforward::Bool; merge_opts::MergeOptions = MergeOptions(), checkout_opts::CheckoutOptions = CheckoutOptions()) diff --git a/doc/src/devdocs/libgit2.md b/doc/src/devdocs/libgit2.md index 7c69b7adac8eb..f485d77f3e0f6 100644 --- a/doc/src/devdocs/libgit2.md +++ b/doc/src/devdocs/libgit2.md @@ -24,6 +24,7 @@ Base.LibGit2.DiffFile Base.LibGit2.DiffOptionsStruct Base.LibGit2.FetchHead Base.LibGit2.FetchOptions +Base.LibGit2.GitAnnotated Base.LibGit2.GitBlame Base.LibGit2.GitBlob Base.LibGit2.GitCommit @@ -84,6 +85,8 @@ Base.LibGit2.fetch_refspecs Base.LibGit2.fetchhead_foreach_cb Base.LibGit2.merge_base Base.LibGit2.merge!(::Base.LibGit2.GitRepo; ::Any...) +Base.LibGit2.merge!(::Base.LibGit2.GitRepo, ::Vector{Base.LibGit2.GitAnnotated}; ::Base.LibGit2.MergeOptions, ::Base.LibGit2.CheckoutOptions) +Base.LibGit2.merge!(::Base.LibGit2.GitRepo, ::Vector{Base.LibGit2.GitAnnotated}, ::Bool; ::Base.LibGit2.MergeOptions, ::Base.LibGit2.CheckoutOptions) Base.LibGit2.ffmerge! Base.LibGit2.fullname Base.LibGit2.features From e9ab77d218d6ae87aa7427ab1d815ebe2374435b Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Mon, 28 Aug 2017 14:17:34 -0400 Subject: [PATCH 206/324] fix #23107, ensure `Array` constructor always makes new arrays --- base/array.jl | 6 ++++++ base/bitarray.jl | 4 ++++ test/arrayops.jl | 7 +++++++ test/bitarray.jl | 5 +++++ 4 files changed, 22 insertions(+) diff --git a/base/array.jl b/base/array.jl index 5db07c626d32d..cad6c5950d316 100644 --- a/base/array.jl +++ b/base/array.jl @@ -563,6 +563,12 @@ convert(::Type{Array{T,n}}, x::AbstractArray{S,n}) where {T,n,S} = copy!(Array{T promote_rule(a::Type{Array{T,n}}, b::Type{Array{S,n}}) where {T,n,S} = el_same(promote_type(T,S), a, b) +# constructors should make copies + +if module_name(@__MODULE__) === :Base # avoid method overwrite +(::Type{T})(x::T) where {T<:Array} = copy(x) +end + ## copying iterators to containers """ diff --git a/base/bitarray.jl b/base/bitarray.jl index df58cc4a37cb1..509cc70547711 100644 --- a/base/bitarray.jl +++ b/base/bitarray.jl @@ -555,6 +555,10 @@ reinterpret(B::BitArray, dims::NTuple{N,Int}) where {N} = reshape(B, dims) BitArray(A::AbstractArray{<:Any,N}) where {N} = convert(BitArray{N}, A) +if module_name(@__MODULE__) === :Base # avoid method overwrite +(::Type{T})(x::T) where {T<:BitArray} = copy(x) +end + """ BitArray(itr) diff --git a/test/arrayops.jl b/test/arrayops.jl index 98c1908301fa5..f138b68133790 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -90,6 +90,13 @@ using TestHelpers.OAs @test ndims(a) == 5 @test a[2,1,2,2,1] == b[14] @test a[2,2,2,2,2] == b[end] + + # issue #23107 + a = [1,2,3] + @test typeof(a)(a) !== a + @test Array(a) !== a + @test Array{eltype(a)}(a) !== a + @test Vector(a) !== a end @testset "reshaping SubArrays" begin a = collect(reshape(1:5, 1, 5)) diff --git a/test/bitarray.jl b/test/bitarray.jl index aebec67d6f563..e8863fdfdc852 100644 --- a/test/bitarray.jl +++ b/test/bitarray.jl @@ -190,6 +190,11 @@ timesofar("utils") @test Array(one(BitMatrix(2,2))) == eye(2,2) @test_throws DimensionMismatch one(BitMatrix(2,3)) end + + # constructors should copy + a = trues(3) + @test BitArray(a) !== a + @test BitArray{1}(a) !== a end timesofar("constructors") From 7f1623eb28749f835004b7e715e386398b367c8e Mon Sep 17 00:00:00 2001 From: Martin Holters Date: Wed, 30 Aug 2017 16:34:19 +0200 Subject: [PATCH 207/324] Fix `countnz` deprecation (#23509) * `export foo` -> `export countnz` * `depwarn(..., :depwarn)` -> `depwarn(..., :countnz)` --- base/deprecated.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/deprecated.jl b/base/deprecated.jl index 1cd9a22640d3c..d6b2c1e4e498a 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1740,9 +1740,9 @@ end @deprecate IOContext(io::IO, key, value) IOContext(io, key=>value) # PR #23485 -export foo +export countnz function countnz(x) - depwarn("countnz(x) is deprecated, use either count(!iszero, x) or count(t -> t != 0, x) instead.", :depwarn) + depwarn("countnz(x) is deprecated, use either count(!iszero, x) or count(t -> t != 0, x) instead.", :countnz) return count(t -> t != 0, x) end From 00e2075fcb44879b20146c1d7f852ed08123b0c7 Mon Sep 17 00:00:00 2001 From: Yichao Yu Date: Wed, 30 Aug 2017 13:14:07 -0400 Subject: [PATCH 208/324] Make partial_inst a global root. (#23507) Types in this list might not be rooted anywhere else. --- src/gc.c | 4 +++- src/gc.h | 2 +- src/jltypes.c | 2 +- src/julia_internal.h | 2 ++ 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/gc.c b/src/gc.c index 0a5d051dc0f19..6cb84c1147f69 100644 --- a/src/gc.c +++ b/src/gc.c @@ -1977,7 +1977,7 @@ module_binding: { } finlist: { - // Scan a finalizer list. see `gc_mark_finlist_t` + // Scan a finalizer (or format compatible) list. see `gc_mark_finlist_t` gc_mark_finlist_t *finlist = gc_pop_markdata(&sp, gc_mark_finlist_t); jl_value_t **begin = finlist->begin; jl_value_t **end = finlist->end; @@ -2276,6 +2276,8 @@ static void mark_roots(jl_gc_mark_cache_t *gc_cache, gc_mark_sp_t *sp) // constants gc_mark_queue_obj(gc_cache, sp, jl_typetype_type); gc_mark_queue_obj(gc_cache, sp, jl_emptytuple_type); + + gc_mark_queue_finlist(gc_cache, sp, &partial_inst, 0); } // find unmarked objects that need to be finalized from the finalizer list "list". diff --git a/src/gc.h b/src/gc.h index 3817e717d0d34..e50fb8b438854 100644 --- a/src/gc.h +++ b/src/gc.h @@ -188,7 +188,7 @@ typedef struct { uint8_t bits; // GC bits of the module (the bits to mark the binding buffer with) } gc_mark_binding_t; -// Finalizer list +// Finalizer (or object) list typedef struct { jl_value_t **begin; jl_value_t **end; diff --git a/src/jltypes.c b/src/jltypes.c index 3a2bdcd00ffb9..6218abb83a019 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -1080,7 +1080,7 @@ static void check_datatype_parameters(jl_typename_t *tn, jl_value_t **params, si JL_GC_POP(); } -static arraylist_t partial_inst; +arraylist_t partial_inst; int inside_typedef = 0; static jl_value_t *extract_wrapper(jl_value_t *t) diff --git a/src/julia_internal.h b/src/julia_internal.h index f5b4c584dc954..69dba6cb8e991 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -1001,6 +1001,8 @@ extern jl_sym_t *isdefined_sym; extern jl_sym_t *nospecialize_sym; void jl_register_fptrs(uint64_t sysimage_base, const char *base, const int32_t *offsets, jl_method_instance_t **linfos, size_t n); +extern arraylist_t partial_inst; + #ifdef __cplusplus } #endif From b104cee8dc98145735d6cb1d7c5d511fcdbcc06d Mon Sep 17 00:00:00 2001 From: Matt Bauman Date: Wed, 30 Aug 2017 13:22:51 -0400 Subject: [PATCH 209/324] Fix off-by-one error in string reallocation (#23493) and ensure that array to string conversions do not cross GC size boundaries --- src/array.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/array.c b/src/array.c index 8c90de9b85b8d..9bca78b015f71 100644 --- a/src/array.c +++ b/src/array.c @@ -435,7 +435,7 @@ JL_DLLEXPORT jl_value_t *jl_array_to_string(jl_array_t *a) { if (a->flags.how == 3 && a->offset == 0 && a->elsize == 1 && (jl_array_ndims(a) != 1 || - !(a->maxsize+sizeof(void*)+1 > GC_MAX_SZCLASS && jl_array_nrows(a)+sizeof(void*)+1 <= GC_MAX_SZCLASS))) { + ((a->maxsize + sizeof(void*) + 1 <= GC_MAX_SZCLASS) == (jl_array_len(a) + sizeof(void*) + 1 <= GC_MAX_SZCLASS)))) { jl_value_t *o = jl_array_data_owner(a); if (jl_is_string(o)) { a->flags.isshared = 1; @@ -616,7 +616,8 @@ static int NOINLINE array_resize_buffer(jl_array_t *a, size_t newlen) nbytes++; oldnbytes++; } - if (!a->flags.ptrarray && jl_is_uniontype(jl_tparam0(jl_typeof(a)))) { + int is_discriminated_union = !a->flags.ptrarray && jl_is_uniontype(jl_tparam0(jl_typeof(a))); + if (is_discriminated_union) { nbytes += newlen; oldnbytes += oldlen; } @@ -627,15 +628,15 @@ static int NOINLINE array_resize_buffer(jl_array_t *a, size_t newlen) a->data = jl_gc_managed_realloc(olddata, nbytes, oldnbytes, a->flags.isaligned, (jl_value_t*)a); } - else if (a->flags.how == 3 && jl_is_string(jl_array_data_owner(a))) { + else if (a->flags.how == 3 && jl_is_string(jl_array_data_owner(a)) && !is_discriminated_union) { // if data is in a String, keep it that way jl_value_t *s; if (a->flags.isshared) { - s = jl_alloc_string(nbytes); + s = jl_alloc_string(nbytes - (elsz == 1)); newbuf = 1; } else { - s = jl_gc_realloc_string(jl_array_data_owner(a), nbytes); + s = jl_gc_realloc_string(jl_array_data_owner(a), nbytes - (elsz == 1)); } jl_array_data_owner(a) = s; jl_gc_wb(a, s); From 3cb9eb96f99c1c4506caef789af57f531f8dcf2f Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Wed, 30 Aug 2017 13:35:56 -0500 Subject: [PATCH 210/324] Fix expanduser with empty string (#23506) --- base/path.jl | 1 + test/path.jl | 1 + 2 files changed, 2 insertions(+) diff --git a/base/path.jl b/base/path.jl index 323e33eb3dbaf..9934602737e31 100644 --- a/base/path.jl +++ b/base/path.jl @@ -338,6 +338,7 @@ expanduser(path::AbstractString) = path # on windows, ~ means "temporary file" else function expanduser(path::AbstractString) i = start(path) + if done(path,i) return path end c, i = next(path,i) if c != '~' return path end if done(path,i) return homedir() end diff --git a/test/path.jl b/test/path.jl index 2b6ac751660c3..e2a2b34699aaf 100644 --- a/test/path.jl +++ b/test/path.jl @@ -10,6 +10,7 @@ for S in (String, GenericString) @test basename(S("foo$(sep)bar")) == "bar" @test dirname(S("foo$(sep)bar")) == "foo" + @test expanduser(S("")) == "" @test expanduser(S("x")) == "x" @test expanduser(S("~")) == (Sys.iswindows() ? "~" : homedir()) From b9ce52f90e324102365944e28c47066c39dd13a9 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Mon, 14 Aug 2017 17:11:25 -0400 Subject: [PATCH 211/324] make `promote` throw an error if no arguments can be changed. fixes #22801. --- NEWS.md | 5 ++++ base/dates/types.jl | 2 +- base/deprecated.jl | 2 ++ base/int.jl | 2 +- base/promotion.jl | 58 +++++++++++++++++++++--------------------- base/range.jl | 4 +-- base/twiceprecision.jl | 10 ++++---- 7 files changed, 45 insertions(+), 38 deletions(-) diff --git a/NEWS.md b/NEWS.md index 242d0df86f115..80bd7801cb4c2 100644 --- a/NEWS.md +++ b/NEWS.md @@ -178,6 +178,11 @@ This section lists changes that do not have deprecation warnings. of the output was shrunk to fit the union of the type of each element in the input. ([#22696]) + * The `promote` function now raises an error if its arguments are of different types + and if attempting to convert them to a common type fails to change any of their types. + This avoids stack overflows in the common case of definitions like + `f(x, y) = f(promote(x, y)...)` ([#22801]). + Library improvements -------------------- diff --git a/base/dates/types.jl b/base/dates/types.jl index 12e3e62bf25f2..5294b50cbfdd7 100644 --- a/base/dates/types.jl +++ b/base/dates/types.jl @@ -340,7 +340,7 @@ Base.typemin(::Union{Time, Type{Time}}) = Time(0) Base.eltype(::Type{T}) where {T<:Period} = T Base.promote_rule(::Type{Date}, x::Type{DateTime}) = DateTime Base.isless(x::T, y::T) where {T<:TimeType} = isless(value(x), value(y)) -Base.isless(x::TimeType, y::TimeType) = isless(Base.promote_noncircular(x, y)...) +Base.isless(x::TimeType, y::TimeType) = isless(promote(x, y)...) (==)(x::T, y::T) where {T<:TimeType} = (==)(value(x), value(y)) function ==(a::Time, b::Time) return hour(a) == hour(b) && minute(a) == minute(b) && diff --git a/base/deprecated.jl b/base/deprecated.jl index d6b2c1e4e498a..0f137159b3c13 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1752,6 +1752,8 @@ end @deprecate selectperm partialsortperm @deprecate selectperm! partialsortperm! +@deprecate promote_noncircular promote false + # END 0.7 deprecations # BEGIN 1.0 deprecations diff --git a/base/int.jl b/base/int.jl index 7650c6e5a1bb6..df3f4823f9512 100644 --- a/base/int.jl +++ b/base/int.jl @@ -84,7 +84,7 @@ signbit(x::Unsigned) = false flipsign(x::T, y::T) where {T<:BitSigned} = flipsign_int(x, y) flipsign(x::BitSigned, y::BitSigned) = flipsign_int(promote(x, y)...) % typeof(x) -flipsign(x::Signed, y::Signed) = convert(typeof(x), flipsign(promote_noncircular(x, y)...)) +flipsign(x::Signed, y::Signed) = convert(typeof(x), flipsign(promote(x, y)...)) flipsign(x::Signed, y::Float16) = flipsign(x, bitcast(Int16, y)) flipsign(x::Signed, y::Float32) = flipsign(x, bitcast(Int32, y)) flipsign(x::Signed, y::Float64) = flipsign(x, bitcast(Int64, y)) diff --git a/base/promotion.jl b/base/promotion.jl index d51f4811e9d61..57c18a2e00293 100644 --- a/base/promotion.jl +++ b/base/promotion.jl @@ -187,7 +187,8 @@ promote_result(::Type{T},::Type{S},::Type{Bottom},::Type{Bottom}) where {T,S} = """ promote(xs...) -Convert all arguments to their common promotion type (if any), and return them all (as a tuple). +Convert all arguments to a common type, and return them all (as a tuple). +If no arguments can be converted, an error is raised. # Examples ```jldoctest @@ -197,21 +198,19 @@ julia> promote(Int8(1), Float16(4.5), Float32(4.1)) """ function promote end -promote() = () -promote(x) = (x,) -function promote(x::T, y::S) where {T,S} +function _promote(x::T, y::S) where {T,S} @_inline_meta (convert(promote_type(T,S),x), convert(promote_type(T,S),y)) end promote_typeof(x) = typeof(x) promote_typeof(x, xs...) = (@_inline_meta; promote_type(typeof(x), promote_typeof(xs...))) -function promote(x, y, z) +function _promote(x, y, z) @_inline_meta (convert(promote_typeof(x,y,z), x), convert(promote_typeof(x,y,z), y), convert(promote_typeof(x,y,z), z)) end -function promote(x, y, zs...) +function _promote(x, y, zs...) @_inline_meta (convert(promote_typeof(x,y,zs...), x), convert(promote_typeof(x,y,zs...), y), @@ -240,39 +239,38 @@ promote_to_supertype(::Type{T}, ::Type{S}, ::Type{S}) where {T<:Number,S<:Number promote_to_supertype(::Type{T}, ::Type{S}, ::Type) where {T<:Number,S<:Number} = error("no promotion exists for ", T, " and ", S) -# promotion with a check for circularity. Can be used to catch what -# would otherwise become StackOverflowErrors. -function promote_noncircular(x, y) +promote() = () +promote(x) = (x,) + +function promote(x, y) @_inline_meta - px, py = promote(x, y) - not_all_sametype((x,px), (y,py)) + px, py = _promote(x, y) + not_sametype((x,y), (px,py)) px, py end -function promote_noncircular(x, y, z) +function promote(x, y, z) @_inline_meta - px, py, pz = promote(x, y, z) - not_all_sametype((x,px), (y,py), (z,pz)) + px, py, pz = _promote(x, y, z) + not_sametype((x,y,z), (px,py,pz)) px, py, pz end -function promote_noncircular(x, y, z, a...) - p = promote(x, y, z, a...) - not_all_sametype(map(identity, (x, y, z, a...), p)) +function promote(x, y, z, a...) + p = _promote(x, y, z, a...) + not_sametype((x, y, z, a...), p) p end -not_all_sametype(x, y) = nothing -not_all_sametype(x, y, z) = nothing -not_all_sametype(x::Tuple{S,S}, y::Tuple{T,T}) where {S,T} = sametype_error(x[1], y[1]) -not_all_sametype(x::Tuple{R,R}, y::Tuple{S,S}, z::Tuple{T,T}) where {R,S,T} = sametype_error(x[1], y[1], z[1]) -function not_all_sametype(::Tuple{R,R}, y::Tuple{S,S}, z::Tuple{T,T}, args...) where {R,S,T} - @_inline_meta - not_all_sametype(y, z, args...) -end -not_all_sametype() = error("promotion failed to change any input types") -function sametype_error(input...) + +promote(x::T, y::T, zs::T...) where {T} = (x, y, zs...) + +not_sametype(x::T, y::T) where {T} = sametype_error(x) + +not_sametype(x, y) = nothing + +function sametype_error(input) @_noinline_meta - error("circular method definition: promotion of types ", + error("promotion of types ", join(map(x->string(typeof(x)), input), ", ", " and "), - " failed to change any input types") + " failed to change any arguments") end +(x::Number, y::Number) = +(promote(x,y)...) @@ -389,3 +387,5 @@ minmax(x::Real) = (x, x) max(x::T, y::T) where {T<:Real} = select_value(y < x, x, y) min(x::T, y::T) where {T<:Real} = select_value(y < x, y, x) minmax(x::T, y::T) where {T<:Real} = y < x ? (y, x) : (x, y) + +flipsign(x::T, y::T) where {T<:Signed} = no_op_err("flipsign", T) diff --git a/base/range.jl b/base/range.jl index 5bf8a2896767c..ba90a98cbbb26 100644 --- a/base/range.jl +++ b/base/range.jl @@ -241,7 +241,7 @@ julia> linspace(1.3,2.9,9) 1.3:0.2:2.9 ``` """ -linspace(start, stop, len::Real=50) = linspace(promote_noncircular(start, stop)..., Int(len)) +linspace(start, stop, len::Real=50) = linspace(promote(start, stop)..., Int(len)) linspace(start::T, stop::T, len::Real=50) where {T} = linspace(start, stop, Int(len)) linspace(start::Real, stop::Real, len::Integer) = linspace(promote(start, stop)..., len) @@ -947,7 +947,7 @@ function _define_range_op(@nospecialize f) $f(r1::Union{StepRangeLen, OrdinalRange, LinSpace}, r2::Union{StepRangeLen, OrdinalRange, LinSpace}) = - $f(promote_noncircular(r1, r2)...) + $f(promote(r1, r2)...) end end _define_range_op(:+) diff --git a/base/twiceprecision.jl b/base/twiceprecision.jl index 1baf1b9a921bd..a4e8f055377e5 100644 --- a/base/twiceprecision.jl +++ b/base/twiceprecision.jl @@ -84,7 +84,7 @@ function add12(x::T, y::T) where {T} x, y = ifelse(abs(y) > abs(x), (y, x), (x, y)) canonicalize2(x, y) end -add12(x, y) = add12(promote_noncircular(x, y)...) +add12(x, y) = add12(promote(x, y)...) """ zhi, zlo = mul12(x, y) @@ -116,7 +116,7 @@ function mul12(x::T, y::T) where {T<:AbstractFloat} ifelse(iszero(h) | !isfinite(h), (h, h), canonicalize2(h, fma(x, y, -h))) end mul12(x::T, y::T) where {T} = (p = x * y; (p, zero(p))) -mul12(x, y) = mul12(promote_noncircular(x, y)...) +mul12(x, y) = mul12(promote(x, y)...) """ zhi, zlo = div12(x, y) @@ -152,7 +152,7 @@ function div12(x::T, y::T) where {T<:AbstractFloat} ifelse(iszero(r) | !isfinite(r), (r, r), (ldexp(rh, xe-ye), ldexp(rl, xe-ye))) end div12(x::T, y::T) where {T} = (p = x / y; (p, zero(p))) -div12(x, y) = div12(promote_noncircular(x, y)...) +div12(x, y) = div12(promote(x, y)...) ## TwicePrecision @@ -269,7 +269,7 @@ function +(x::TwicePrecision{T}, y::TwicePrecision{T}) where T s = abs(x.hi) > abs(y.hi) ? (((x.hi - r) + y.hi) + y.lo) + x.lo : (((y.hi - r) + x.hi) + x.lo) + y.lo TwicePrecision(canonicalize2(r, s)...) end -+(x::TwicePrecision, y::TwicePrecision) = +(promote_noncircular(x, y)...) ++(x::TwicePrecision, y::TwicePrecision) = +(promote(x, y)...) -(x::TwicePrecision, y::TwicePrecision) = x + (-y) -(x::TwicePrecision, y::Number) = x + (-y) @@ -292,7 +292,7 @@ function *(x::TwicePrecision{T}, y::TwicePrecision{T}) where {T} ret = TwicePrecision{T}(canonicalize2(zh, (x.hi * y.lo + x.lo * y.hi) + zl)...) ifelse(iszero(zh) | !isfinite(zh), TwicePrecision{T}(zh, zh), ret) end -*(x::TwicePrecision, y::TwicePrecision) = *(promote_noncircular(x, y)...) +*(x::TwicePrecision, y::TwicePrecision) = *(promote(x, y)...) function /(x::TwicePrecision, v::Number) x / TwicePrecision{typeof(x.hi/v)}(v) From afa6af122754866fc1fe16e4554d1091340a7a91 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Fri, 18 Aug 2017 16:47:50 -0400 Subject: [PATCH 212/324] implement #6614, destructuring in formal arguments --- NEWS.md | 4 ++++ doc/src/manual/functions.md | 19 +++++++++++++++++++ src/julia-syntax.scm | 29 +++++++++++++++++++++++++++++ test/core.jl | 26 ++++++++++++++++++++++++++ 4 files changed, 78 insertions(+) diff --git a/NEWS.md b/NEWS.md index 242d0df86f115..b2aa3420c9364 100644 --- a/NEWS.md +++ b/NEWS.md @@ -7,6 +7,10 @@ New language features * Local variables can be tested for being defined using the new `@isdefined variable` macro ([#22281]). + * Destructuring in function arguments: when an expression such as `(x, y)` is used as + a function argument name, the argument is unpacked into local variables `x` and `y` + as in the assignment `(x, y) = arg` ([#6614]). + Language changes ---------------- diff --git a/doc/src/manual/functions.md b/doc/src/manual/functions.md index a5d28296daed5..eaee1c912cf23 100644 --- a/doc/src/manual/functions.md +++ b/doc/src/manual/functions.md @@ -267,6 +267,25 @@ end This has the exact same effect as the previous definition of `foo`. +## Argument destructuring + +The destructuring feature can also be used within a function argument. +If a function argument name is written as a tuple (e.g. `(x, y)`) instead of just +a symbol, then an assignment `(x, y) = argument` will be inserted for you: + +```julia +julia> minmax(x, y) = (y < x) ? (y, x) : (x, y) + +julia> range((min, max)) = max - min + +julia> range(minmax(10, 2)) +8 +``` + +Notice the extra set of parentheses in the definition of `range`. +Without those, `range` would be a two-argument function, and this example would +not work. + ## Varargs Functions It is often convenient to be able to write functions taking an arbitrary number of arguments. diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 7b3321c59e8d0..d3f10feb07f28 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -993,6 +993,32 @@ (loop (cadr ex) (append! (reverse (cddr ex)) vars)) `(where ,ex ,.(reverse! vars))))) +(define (lower-destructuring-args argl) + (define (check-lhs a) + (if (expr-contains-p (lambda (e) (or (decl? e) (assignment? e) (kwarg? e))) + a) + (error (string "invalid argument destructuring syntax \"" (deparse a) "\"")) + a)) + (define (transform-arg a) + (cond ((and (pair? a) (eq? (car a) 'tuple)) + (let ((a2 (gensy))) + (cons a2 `(local (= ,(check-lhs a) ,a2))))) + ((or (and (decl? a) (length= a 3)) (kwarg? a)) + (let ((x (transform-arg (cadr a)))) + (cons `(,(car a) ,(car x) ,(caddr a)) (cdr x)))) + ((vararg? a) + (let ((x (transform-arg (cadr a)))) + (cons `(... ,(car x)) (cdr x)))) + (else (cons a #f)))) + (let loop ((argl argl) + (newa '()) + (stmts '())) + (if (null? argl) + (cons (reverse newa) (reverse stmts)) + (let ((a (transform-arg (car argl)))) + (loop (cdr argl) (cons (car a) newa) + (if (cdr a) (cons (cdr a) stmts) stmts)))))) + (define (expand-function-def- e) (let* ((name (cadr e)) (where (if (and (pair? name) (eq? (car name) 'where)) @@ -1040,6 +1066,9 @@ (farg (if (decl? name) (adj-decl name) `(|::| |#self#| (call (core Typeof) ,name)))) + (argl-stmts (lower-destructuring-args argl)) + (argl (car argl-stmts)) + (body (insert-after-meta body (cdr argl-stmts))) (argl (fix-arglist (arglist-unshift argl farg) (and (not (any kwarg? argl)) (not (and (pair? argl) diff --git a/test/core.jl b/test/core.jl index 9f528d6aef7cb..bc0d8c0d6328d 100644 --- a/test/core.jl +++ b/test/core.jl @@ -5508,3 +5508,29 @@ for U in unboxedunions end end # module UnionOptimizations + +# issue #6614, argument destructuring +f6614((x, y)) = [x, y] +@test f6614((4, 3)) == [4, 3] +g6614((x, y), (z,), (a, b)) = (x,y,z,a,b) +@test g6614((1, 2), (3,), (4, 5)) === (1,2,3,4,5) +@test_throws MethodError g6614(1, 2) +@test_throws MethodError g6614((1, 2), (3,)) +@test_throws BoundsError g6614((1, 2), (3,), (1,)) +h6614((x, y) = (5, 6)) = (y, x) +@test h6614() == (6, 5) +@test h6614((4, 5)) == (5, 4) +ff6614((x, y)::Tuple{Int, String}) = (x, y) +@test ff6614((1, "")) == (1, "") +@test_throws MethodError ff6614((1, 1)) +gg6614((x, y)::Tuple{Int, String} = (2, " ")) = (x, y) +@test gg6614() == (2, " ") +function hh6614() + x, y = 1, 2 + function g((x,y)) + # make sure x and y are local + end + g((4,5)) + x, y +end +@test hh6614() == (1, 2) From 488febcebc77d8cdfa71201456f77802af3d8665 Mon Sep 17 00:00:00 2001 From: Matt Bauman Date: Wed, 30 Aug 2017 14:46:58 -0400 Subject: [PATCH 213/324] Completely remove partial linear indexing (#21750) This removes the partial linear indexing deprecation and implents the new behavior: Linear indexing now only takes effect when there is exactly one non-cartesian index. --- base/abstractarray.jl | 49 +++------------------------------------- base/deprecated.jl | 40 -------------------------------- base/indices.jl | 1 + base/multidimensional.jl | 16 ------------- base/subarray.jl | 6 ++--- src/cgutils.cpp | 31 ++++--------------------- src/codegen.cpp | 8 ------- src/julia-syntax.scm | 12 ++++------ src/rtutils.c | 20 ---------------- test/abstractarray.jl | 35 ++++++++++++---------------- test/subarray.jl | 14 ++++-------- 11 files changed, 33 insertions(+), 199 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 23652f9be4c68..e939099865fae 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -418,47 +418,12 @@ function checkbounds_indices(::Type{Bool}, IA::Tuple, I::Tuple) @_inline_meta checkindex(Bool, IA[1], I[1]) & checkbounds_indices(Bool, tail(IA), tail(I)) end -checkbounds_indices(::Type{Bool}, ::Tuple{}, ::Tuple{}) = true -function checkbounds_indices(::Type{Bool}, ::Tuple{}, I::Tuple{Any}) - @_inline_meta - checkindex(Bool, 1:1, I[1]) -end function checkbounds_indices(::Type{Bool}, ::Tuple{}, I::Tuple) @_inline_meta - checkindex(Bool, 1:1, I[1]) & checkbounds_indices(Bool, (), tail(I)) -end -function checkbounds_indices(::Type{Bool}, IA::Tuple{Any}, I::Tuple{Any}) - @_inline_meta - checkindex(Bool, IA[1], I[1]) + checkindex(Bool, OneTo(1), I[1]) & checkbounds_indices(Bool, (), tail(I)) end -function checkbounds_indices(::Type{Bool}, IA::Tuple, I::Tuple{Any}) - @_inline_meta - checkbounds_linear_indices(Bool, IA, I[1]) -end -function checkbounds_linear_indices(::Type{Bool}, IA::Tuple{Vararg{OneTo}}, i) - @_inline_meta - if checkindex(Bool, IA[1], i) - return true - elseif checkindex(Bool, OneTo(trailingsize(IA)), i) # partial linear indexing - partial_linear_indexing_warning_lookup(length(IA)) - return true # TODO: Return false after the above function is removed in deprecated.jl - end - return false -end -function checkbounds_linear_indices(::Type{Bool}, IA::Tuple{AbstractUnitRange,Vararg{AbstractUnitRange}}, i) - @_inline_meta - checkindex(Bool, IA[1], i) -end -function checkbounds_linear_indices(::Type{Bool}, IA::Tuple{Vararg{OneTo}}, i::Union{Slice,Colon}) - partial_linear_indexing_warning_lookup(length(IA)) - true -end -function checkbounds_linear_indices(::Type{Bool}, - IA::Tuple{AbstractUnitRange,Vararg{AbstractUnitRange}}, i::Union{Slice,Colon}) - partial_linear_indexing_warning_lookup(length(IA)) - true -end -checkbounds_indices(::Type{Bool}, ::Tuple, ::Tuple{}) = true +checkbounds_indices(::Type{Bool}, ::Tuple, ::Tuple{}) = true +checkbounds_indices(::Type{Bool}, ::Tuple{}, ::Tuple{}) = true throw_boundserror(A, I) = (@_noinline_meta; throw(BoundsError(A, I))) @@ -1005,14 +970,6 @@ function _to_subscript_indices(A::AbstractArray{T,N}, I::Int...) where {T,N} # T end _to_subscript_indices(A::AbstractArray, J::Tuple, Jrem::Tuple{}) = __to_subscript_indices(A, indices(A), J, Jrem) -# We allow partial linear indexing deprecation for OneTo arrays -function __to_subscript_indices(A::AbstractArray, ::Tuple{Vararg{OneTo}}, J::Tuple, Jrem::Tuple{}) - @_inline_meta - sz = _remaining_size(J, indices(A)) # compute trailing size (overlapping the final index) - (front(J)..., _unsafe_ind2sub(sz, last(J))...) # (maybe) extend the last index -end -# After the partial linear indexing deprecation is removed, this next method can -# become the new normal. For now, it's limited to non-OneTo arrays. function __to_subscript_indices(A::AbstractArray, ::Tuple{AbstractUnitRange,Vararg{AbstractUnitRange}}, J::Tuple, Jrem::Tuple{}) @_inline_meta diff --git a/base/deprecated.jl b/base/deprecated.jl index d6b2c1e4e498a..64b1eed61f769 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1055,46 +1055,6 @@ isempty(::Task) = error("isempty not defined for Tasks") export @test_approx_eq end -# Deprecate partial linear indexing -function partial_linear_indexing_warning_lookup(nidxs_remaining) - # We need to figure out how many indices were passed for a sensible deprecation warning - opts = JLOptions() - if opts.depwarn > 0 - # Find the caller -- this is very expensive so we don't want to do it twice - bt = backtrace() - found = false - call = StackTraces.UNKNOWN - caller = StackTraces.UNKNOWN - for frame in bt - lkups = StackTraces.lookup(frame) - for outer caller in lkups - if caller == StackTraces.UNKNOWN - continue - end - found && @goto found - if caller.func in (:getindex, :setindex!, :view) - found = true - call = caller - end - end - end - @label found - fn = "`reshape`" - if call != StackTraces.UNKNOWN && !isnull(call.linfo) - # Try to grab the number of dimensions in the parent array - mi = get(call.linfo) - args = mi.specTypes.parameters - if length(args) >= 2 && args[2] <: AbstractArray - fn = "`reshape(A, Val{$(ndims(args[2]) - nidxs_remaining + 1)})`" - end - end - _depwarn("Partial linear indexing is deprecated. Use $fn to make the dimensionality of the array match the number of indices.", opts, bt, caller) - end -end -function partial_linear_indexing_warning(n) - depwarn("Partial linear indexing is deprecated. Use `reshape(A, Val{$n})` to make the dimensionality of the array match the number of indices.", (:getindex, :setindex!, :view)) -end - # Deprecate Array(T, dims...) in favor of proper type constructors @deprecate Array(::Type{T}, d::NTuple{N,Int}) where {T,N} Array{T}(d) @deprecate Array(::Type{T}, d::Int...) where {T} Array{T}(d...) diff --git a/base/indices.jl b/base/indices.jl index e55a64dd38597..5b16ec88ae45e 100644 --- a/base/indices.jl +++ b/base/indices.jl @@ -211,6 +211,7 @@ given tuple of indices and the dimensional indices of `A` in tandem. As such, not all index types are guaranteed to propagate to `Base.to_index`. """ to_indices(A, I::Tuple) = (@_inline_meta; to_indices(A, indices(A), I)) +to_indices(A, I::Tuple{Any}) = (@_inline_meta; to_indices(A, (linearindices(A),), I)) to_indices(A, inds, ::Tuple{}) = () to_indices(A, inds, I::Tuple{Any, Vararg{Any}}) = (@_inline_meta; (to_index(A, I[1]), to_indices(A, _maybetail(inds), tail(I))...)) diff --git a/base/multidimensional.jl b/base/multidimensional.jl index e48394a8c9d37..554cec5296e37 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -442,15 +442,6 @@ done(L::LogicalIndex, s) = s[3] > length(L) end @inline done(L::LogicalIndex{Int,<:BitArray}, s) = s[2] > length(L) -# Checking bounds with LogicalIndex{Int} is tricky since we allow linear indexing over trailing dimensions -@inline checkbounds_indices(::Type{Bool},IA::Tuple{},I::Tuple{LogicalIndex{Int,AbstractArray{Bool,N}}}) where {N} = - checkindex(Bool, IA, I[1]) -@inline checkbounds_indices(::Type{Bool},IA::Tuple{Any},I::Tuple{LogicalIndex{Int,AbstractArray{Bool,N}}}) where {N} = - checkindex(Bool, IA[1], I[1]) -@inline function checkbounds_indices(::Type{Bool}, IA::Tuple, I::Tuple{LogicalIndex{Int,AbstractArray{Bool,N}}}) where N - IA1, IArest = IteratorsMD.split(IA, Val(N)) - checkindex(Bool, IA1, I[1]) -end @inline checkbounds(::Type{Bool}, A::AbstractArray, I::LogicalIndex{<:Any,<:AbstractArray{Bool,1}}) = linearindices(A) == linearindices(I.mask) @inline checkbounds(::Type{Bool}, A::AbstractArray, I::LogicalIndex) = indices(A) == indices(I.mask) @@ -490,15 +481,8 @@ _maybe_linear_logical_index(::IndexLinear, A, i) = LogicalIndex{Int}(i) (uncolon(inds, I), to_indices(A, _maybetail(inds), tail(I))...) const CI0 = Union{CartesianIndex{0}, AbstractArray{CartesianIndex{0}}} -uncolon(inds::Tuple{}, I::Tuple{Colon}) = Slice(OneTo(1)) uncolon(inds::Tuple{}, I::Tuple{Colon, Vararg{Any}}) = Slice(OneTo(1)) -uncolon(inds::Tuple{}, I::Tuple{Colon, Vararg{CI0}}) = Slice(OneTo(1)) -uncolon(inds::Tuple{Any}, I::Tuple{Colon}) = Slice(inds[1]) -uncolon(inds::Tuple{Any}, I::Tuple{Colon, Vararg{Any}}) = Slice(inds[1]) -uncolon(inds::Tuple{Any}, I::Tuple{Colon, Vararg{CI0}}) = Slice(inds[1]) uncolon(inds::Tuple, I::Tuple{Colon, Vararg{Any}}) = Slice(inds[1]) -uncolon(inds::Tuple, I::Tuple{Colon}) = Slice(OneTo(trailingsize(inds))) -uncolon(inds::Tuple, I::Tuple{Colon, Vararg{CI0}}) = Slice(OneTo(trailingsize(inds))) ### From abstractarray.jl: Internal multidimensional indexing definitions ### getindex(x::Number, i::CartesianIndex{0}) = x diff --git a/base/subarray.jl b/base/subarray.jl index b2a9c3e258834..b695444563c67 100644 --- a/base/subarray.jl +++ b/base/subarray.jl @@ -370,7 +370,7 @@ end replace_ref_end!(ex) Recursively replace occurrences of the symbol :end in a "ref" expression (i.e. A[...]) `ex` -with the appropriate function calls (`endof`, `size` or `trailingsize`). Replacement uses +with the appropriate function calls (`endof` or `size`). Replacement uses the closest enclosing ref, so A[B[end]] @@ -402,7 +402,7 @@ function replace_ref_end_!(ex, withex) else n = 1 J = endof(ex.args) - for j = 2:J-1 + for j = 2:J exj, used = replace_ref_end_!(ex.args[j],:($size($S,$n))) used_S |= used ex.args[j] = exj @@ -418,8 +418,6 @@ function replace_ref_end_!(ex, withex) n += 1 end end - ex.args[J], used = replace_ref_end_!(ex.args[J],:($trailingsize($S,$n))) - used_S |= used end if used_S && S !== ex.args[1] S0 = ex.args[1] diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 63434b182c0d3..13ecf39ff4bc3 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -1672,33 +1672,10 @@ static Value *emit_array_nd_index(jl_codectx_t &ctx, // We have already emitted a bounds check for each index except for // the last one which we therefore have to do here. bool linear_indexing = nd == -1 || nidxs < (size_t)nd; - if (linear_indexing) { - // Compare the linearized index `i` against the linearized size of - // the accessed array, i.e. `if !(i < alen) goto error`. - if (nidxs > 1) { - // TODO: REMOVE DEPWARN AND RETURN FALSE AFTER 0.6. - // We need to check if this is inside the non-linearized size - BasicBlock *partidx = BasicBlock::Create(jl_LLVMContext, "partlinidx"); - BasicBlock *partidxwarn = BasicBlock::Create(jl_LLVMContext, "partlinidxwarn"); - Value *d = emit_arraysize_for_unsafe_dim(ctx, ainfo, ex, nidxs, nd); - ctx.builder.CreateCondBr(ctx.builder.CreateICmpULT(ii, d), endBB, partidx); - - // We failed the normal bounds check; check to see if we're - // inside the linearized size (partial linear indexing): - ctx.f->getBasicBlockList().push_back(partidx); - ctx.builder.SetInsertPoint(partidx); - Value *alen = emit_arraylen(ctx, ainfo, ex); - ctx.builder.CreateCondBr(ctx.builder.CreateICmpULT(i, alen), partidxwarn, failBB); - - // We passed the linearized bounds check; now throw the depwarn: - ctx.f->getBasicBlockList().push_back(partidxwarn); - ctx.builder.SetInsertPoint(partidxwarn); - ctx.builder.CreateCall(prepare_call(jldepwarnpi_func), ConstantInt::get(T_size, nidxs)); - ctx.builder.CreateBr(endBB); - } else { - Value *alen = emit_arraylen(ctx, ainfo, ex); - ctx.builder.CreateCondBr(ctx.builder.CreateICmpULT(i, alen), endBB, failBB); - } + if (linear_indexing && nidxs == 1) { + // Check against the entire linear span of the array + Value *alen = emit_arraylen(ctx, ainfo, ex); + ctx.builder.CreateCondBr(ctx.builder.CreateICmpULT(i, alen), endBB, failBB); } else { // Compare the last index of the access against the last dimension of // the accessed array, i.e. `if !(last_index < last_dimension) goto error`. diff --git a/src/codegen.cpp b/src/codegen.cpp index 9ab787c604734..96483a28f9dfc 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -342,7 +342,6 @@ static Function *expect_func; static Function *jldlsym_func; static Function *jlnewbits_func; static Function *jltypeassert_func; -static Function *jldepwarnpi_func; //static Function *jlgetnthfield_func; static Function *jlgetnthfieldchecked_func; //static Function *jlsetnthfield_func; @@ -6275,13 +6274,6 @@ static void init_julia_llvm_env(Module *m) jlapply2va_func = jlcall_func_to_llvm("jl_apply_2va", &jl_apply_2va, m); - std::vector argsdepwarnpi(0); - argsdepwarnpi.push_back(T_size); - jldepwarnpi_func = Function::Create(FunctionType::get(T_void, argsdepwarnpi, false), - Function::ExternalLinkage, - "jl_depwarn_partial_indexing", m); - add_named_global(jldepwarnpi_func, &jl_depwarn_partial_indexing); - std::vector args_1ptr(0); args_1ptr.push_back(T_prjlvalue); queuerootfun = Function::Create(FunctionType::get(T_void, args_1ptr, false), diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 7b3321c59e8d0..48d68a2dc437a 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -90,20 +90,16 @@ ;; the array `a` in the `n`th index. ;; `tuples` are a list of the splatted arguments that precede index `n` ;; `last` = is this last index? -;; returns a call to endof(a), trailingsize(a,n), or size(a,n) +;; returns a call to endof(a) or size(a,n) (define (end-val a n tuples last) (if (null? tuples) - (if last - (if (= n 1) - `(call (top endof) ,a) - `(call (top trailingsize) ,a ,n)) + (if (and last (= n 1)) + `(call (top endof) ,a) `(call (top size) ,a ,n)) (let ((dimno `(call (top +) ,(- n (length tuples)) ,.(map (lambda (t) `(call (top length) ,t)) tuples)))) - (if last - `(call (top trailingsize) ,a ,dimno) - `(call (top size) ,a ,dimno))))) + `(call (top size) ,a ,dimno)))) ;; replace `end` for the closest ref expression, so doesn't go inside nested refs (define (replace-end ex a n tuples last) diff --git a/src/rtutils.c b/src/rtutils.c index 75ef9e7b565d3..a2385e24dd364 100644 --- a/src/rtutils.c +++ b/src/rtutils.c @@ -1016,26 +1016,6 @@ void jl_depwarn(const char *msg, jl_value_t *sym) JL_GC_POP(); } -JL_DLLEXPORT void jl_depwarn_partial_indexing(size_t n) -{ - static jl_value_t *depwarn_func = NULL; - if (!depwarn_func && jl_base_module) { - depwarn_func = jl_get_global(jl_base_module, jl_symbol("partial_linear_indexing_warning")); - } - if (!depwarn_func) { - jl_safe_printf("WARNING: Partial linear indexing is deprecated. Use " - "`reshape(A, Val(%zd))` to make the dimensionality of the array match " - "the number of indices\n", n); - return; - } - jl_value_t **depwarn_args; - JL_GC_PUSHARGS(depwarn_args, 2); - depwarn_args[0] = depwarn_func; - depwarn_args[1] = jl_box_long(n); - jl_apply(depwarn_args, 2); - JL_GC_POP(); -} - #ifdef __cplusplus } #endif diff --git a/test/abstractarray.jl b/test/abstractarray.jl index 5a0fc7ff2204e..7baa72f37f378 100644 --- a/test/abstractarray.jl +++ b/test/abstractarray.jl @@ -16,10 +16,10 @@ A = rand(5,4,3) @test checkbounds(Bool, A, 2, 2, 2, 1) == true # extra indices @test checkbounds(Bool, A, 2, 2, 2, 2) == false @test checkbounds(Bool, A, 1, 1) == true # partial linear indexing (PLI) - # @test checkbounds(Bool, A, 1, 12) == false # PLI TODO: Re-enable after partial linear indexing deprecation - # @test checkbounds(Bool, A, 5, 12) == false # PLI TODO: Re-enable after partial linear indexing deprecation - @test checkbounds(Bool, A, 1, 13) == false # PLI - # @test checkbounds(Bool, A, 6, 12) == false # PLI TODO: Re-enable after partial linear indexing deprecation + @test checkbounds(Bool, A, 1, 12) == false + @test checkbounds(Bool, A, 5, 12) == false + @test checkbounds(Bool, A, 1, 13) == false + @test checkbounds(Bool, A, 6, 12) == false end @testset "single CartesianIndex" begin @@ -32,15 +32,15 @@ end @test checkbounds(Bool, A, CartesianIndex((5, 5, 3))) == false @test checkbounds(Bool, A, CartesianIndex((5, 4, 4))) == false @test checkbounds(Bool, A, CartesianIndex((1,))) == true - # @test checkbounds(Bool, A, CartesianIndex((60,))) == false # TODO: Re-enable after partial linear indexing deprecation + @test checkbounds(Bool, A, CartesianIndex((60,))) == false @test checkbounds(Bool, A, CartesianIndex((61,))) == false @test checkbounds(Bool, A, CartesianIndex((2, 2, 2, 1,))) == true @test checkbounds(Bool, A, CartesianIndex((2, 2, 2, 2,))) == false @test checkbounds(Bool, A, CartesianIndex((1, 1,))) == true - # @test checkbounds(Bool, A, CartesianIndex((1, 12,))) == false # TODO: Re-enable after partial linear indexing deprecation - # @test checkbounds(Bool, A, CartesianIndex((5, 12,))) == false # TODO: Re-enable after partial linear indexing deprecation + @test checkbounds(Bool, A, CartesianIndex((1, 12,))) == false + @test checkbounds(Bool, A, CartesianIndex((5, 12,))) == false @test checkbounds(Bool, A, CartesianIndex((1, 13,))) == false - # @test checkbounds(Bool, A, CartesianIndex((6, 12,))) == false # TODO: Re-enable after partial linear indexing deprecation + @test checkbounds(Bool, A, CartesianIndex((6, 12,))) == false end @testset "mix of CartesianIndex and Int" begin @@ -67,9 +67,9 @@ end @test checkbounds(Bool, A, 2, 2, 2, 1:1) == true # extra indices @test checkbounds(Bool, A, 2, 2, 2, 1:2) == false @test checkbounds(Bool, A, 1:5, 1:4) == true - # @test checkbounds(Bool, A, 1:5, 1:12) == false # TODO: Re-enable after partial linear indexing deprecation + @test checkbounds(Bool, A, 1:5, 1:12) == false @test checkbounds(Bool, A, 1:5, 1:13) == false - # @test checkbounds(Bool, A, 1:6, 1:12) == false # TODO: Re-enable after partial linear indexing deprecation + @test checkbounds(Bool, A, 1:6, 1:12) == false end @testset "logical" begin @@ -81,9 +81,9 @@ end @test checkbounds(Bool, A, trues(61)) == false @test checkbounds(Bool, A, 2, 2, 2, trues(1)) == true # extra indices @test checkbounds(Bool, A, 2, 2, 2, trues(2)) == false - # @test checkbounds(Bool, A, trues(5), trues(12)) == false # TODO: Re-enable after partial linear indexing deprecation + @test checkbounds(Bool, A, trues(5), trues(12)) == false @test checkbounds(Bool, A, trues(5), trues(13)) == false - # @test checkbounds(Bool, A, trues(6), trues(12)) == false # TODO: Re-enable after partial linear indexing deprecation + @test checkbounds(Bool, A, trues(6), trues(12)) == false @test checkbounds(Bool, A, trues(5, 4, 3)) == true @test checkbounds(Bool, A, trues(5, 4, 2)) == false @test checkbounds(Bool, A, trues(5, 12)) == false @@ -145,11 +145,6 @@ end @test sub2ind((0:3,3:5), i-1, j+2) == k @test ind2sub((0:3,3:5), k) == (i-1, j+2) end - @testset "Delete when partial linear indexing is deprecated (#14770)" begin - @test sub2ind((4,3), 7) == 7 - @test sub2ind((1:4,1:3), 7) == 7 - @test sub2ind((0:3,3:5), 7) == 8 - end end @testset "3-dimensional" begin @@ -377,8 +372,8 @@ function test_vector_indexing(::Type{T}, shape, ::Type{TestAbstractArray}) where @test B[vec(idxs)] == A[vec(idxs)] == vec(idxs) @test B[:] == A[:] == collect(1:N) @test B[1:end] == A[1:end] == collect(1:N) - # @test B[:,:] == A[:,:] == reshape(1:N, shape[1], prod(shape[2:end])) # TODO: Re-enable after partial linear indexing deprecation - # @test B[1:end,1:end] == A[1:end,1:end] == reshape(1:N, shape[1], prod(shape[2:end])) # TODO: Re-enable after partial linear indexing deprecation + @test B[:,:] == A[:,:] == B[:,:,1] == A[:,:,1] + B[1:end,1:end] == A[1:end,1:end] == B[1:end,1:end,1] == A[1:end,1:end,1] @testset "Test with containers that aren't Int[]" begin @test B[[]] == A[[]] == [] @@ -395,7 +390,7 @@ function test_vector_indexing(::Type{T}, shape, ::Type{TestAbstractArray}) where @testset "test removing dimensions with 0-d arrays" begin idx0 = reshape([rand(1:size(A, 1))]) @test B[idx0, idx2] == A[idx0, idx2] == reshape(A[idx0[], vec(idx2)], 4, 5) == reshape(B[idx0[], vec(idx2)], 4, 5) - # @test B[reshape([end]), reshape([end])] == A[reshape([end]), reshape([end])] == reshape([A[end,end]]) == reshape([B[end,end]]) # TODO: Re-enable after partial linear indexing deprecation + @test B[reshape([end]), reshape([end])] == A[reshape([end]), reshape([end])] == reshape([A[end,end]]) == reshape([B[end,end]]) end mask = bitrand(shape) diff --git a/test/subarray.jl b/test/subarray.jl index b25ce1a4dfc95..3cbbf426929fb 100644 --- a/test/subarray.jl +++ b/test/subarray.jl @@ -34,11 +34,10 @@ _Agen(A, i1, i2, i3, i4) = [A[j1,j2,j3,j4] for j1 in i1, j2 in i2, j3 in i3, j4 function replace_colon(A::AbstractArray, I) Iout = Array{Any}(length(I)) - for d = 1:length(I)-1 + I == (:,) && return (1:length(A),) + for d = 1:length(I) Iout[d] = isa(I[d], Colon) ? (1:size(A,d)) : I[d] end - d = length(I) - Iout[d] = isa(I[d], Colon) ? (1:prod(size(A)[d:end])) : I[d] (Iout...,) end @@ -226,11 +225,9 @@ end function runviews(SB::AbstractArray, indexN, indexNN, indexNNN) @assert ndims(SB) > 2 for i3 in indexN, i2 in indexN, i1 in indexN - ndims(SB) > 3 && i3 isa Colon && continue # TODO: Re-enable once Colon no longer spans partial trailing dimensions runsubarraytests(SB, i1, i2, i3) end for i2 in indexN, i1 in indexN - i2 isa Colon && continue # TODO: Re-enable once Colon no longer spans partial trailing dimensions runsubarraytests(SB, i1, i2) end for i1 in indexNNN @@ -282,9 +279,6 @@ _ndims(x) = 1 if testfull let B = copy(reshape(1:13^3, 13, 13, 13)) for o3 in oindex, o2 in oindex, o1 in oindex - if (o3 isa Colon && (_ndims(o1) + _ndims(o2) != 2)) - continue # TODO: remove once Colon no longer spans partial trailing dimensions - end viewB = view(B, o1, o2, o3) runviews(viewB, index5, index25, index125) end @@ -482,7 +476,7 @@ Y = 4:-1:1 @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] == @view X[1,1:end-2] # TODO: Re-enable after partial linear indexing deprecation +@test X[1,1:end-2] == @view X[1,1:end-2] @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]] @@ -533,7 +527,7 @@ end @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,1:end-2] == @views X[1,1:end-2] # TODO: Re-enable after partial linear indexing deprecation +@test X[1,1:end-2] == @views X[1,1:end-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]] From a969c2a96da4775aa7ef7b6ac389c40892ddcfba Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Wed, 30 Aug 2017 23:43:58 +0200 Subject: [PATCH 214/324] fix promotion of diagonal integer matrix to non-integer power (#23510) --- base/linalg/dense.jl | 3 ++- test/linalg/dense.jl | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/base/linalg/dense.jl b/base/linalg/dense.jl index 42228106ee3e5..5504aa217e868 100644 --- a/base/linalg/dense.jl +++ b/base/linalg/dense.jl @@ -405,7 +405,8 @@ function (^)(A::AbstractMatrix{T}, p::Real) where T # Quicker return if A is diagonal if isdiag(A) - retmat = copy(A) + TT = promote_op(^, T, typeof(p)) + retmat = copy_oftype(A, TT) for i in 1:n retmat[i, i] = retmat[i, i] ^ p end diff --git a/test/linalg/dense.jl b/test/linalg/dense.jl index 30fcf9ace436c..086ecff07452d 100644 --- a/test/linalg/dense.jl +++ b/test/linalg/dense.jl @@ -581,6 +581,11 @@ end end end +@testset "diagonal integer matrix to real power" begin + A = Matrix(Diagonal([1, 2, 3])) + @test A^2.3 ≈ float(A)^2.3 +end + @testset "issue #23366 (Int Matrix to Int power)" begin @testset "Tests for $elty" for elty in (Int128, Int16, Int32, Int64, Int8, UInt128, UInt16, UInt32, UInt64, UInt8, From 104a81cc76622fe1c85f77659e18026716b69494 Mon Sep 17 00:00:00 2001 From: kshyatt Date: Wed, 30 Aug 2017 15:30:07 -0700 Subject: [PATCH 215/324] Doc statusentry and add xrefs --- base/libgit2/status.jl | 2 +- base/libgit2/types.jl | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/base/libgit2/status.jl b/base/libgit2/status.jl index ccfb4c8e19985..568676f8eda82 100644 --- a/base/libgit2/status.jl +++ b/base/libgit2/status.jl @@ -7,7 +7,7 @@ Collect information about the status of each file in the git repository `repo` (e.g. is the file modified, staged, etc.). `status_opts` can be used to set various options, for instance whether or not to look at untracked files or whether to include -submodules or not. +submodules or not. See [`StatusOptions`](@ref) for more information. """ function GitStatus(repo::GitRepo; status_opts=StatusOptions()) stat_ptr_ptr = Ref{Ptr{Void}}(C_NULL) diff --git a/base/libgit2/types.jl b/base/libgit2/types.jl index d713b6f94a772..e7fb21bdd0e47 100644 --- a/base/libgit2/types.jl +++ b/base/libgit2/types.jl @@ -748,6 +748,14 @@ end Providing the differences between the file as it exists in HEAD and the index, and providing the differences between the index and the working directory. Matches the `git_status_entry` struct. + +The fields represent: + * `status`: contains the status flags for the file, indicating if it is current, + or has been changed in some way in the index or work tree. + * `head_to_index`: a pointer to a [`DiffDelta`](@ref) which encapsulates the difference(s) + between the file as it exists in HEAD and in the index. + * `index_to_workdir`: a pointer to a `DiffDelta` which encapsulates the difference(s) + between the file as it exists in the index and in the [`workdir`](@ref). """ struct StatusEntry status::Cuint From f4bb1baa20d2ecca71abae394673c57e80f4a8c1 Mon Sep 17 00:00:00 2001 From: Matt Bauman Date: Wed, 30 Aug 2017 18:26:50 -0400 Subject: [PATCH 216/324] Document boundscheck macro Fix #20469. [ci skip] --- base/essentials.jl | 42 ++++++++++++++++++++++++++++++++++++++++++ doc/src/stdlib/base.md | 1 + 2 files changed, 43 insertions(+) diff --git a/base/essentials.jl b/base/essentials.jl index fcb94f19822f8..b43d3087ed838 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -412,6 +412,46 @@ section of the Metaprogramming chapter of the manual for more details and exampl """ esc(@nospecialize(e)) = Expr(:escape, e) +""" + @boundscheck(blk) + +Annotates the expression `blk` as a bounds checking block, allowing it to be elided by [`@inbounds`](@ref). + +Note that the function in which `@boundscheck` is written must be inlined into +its caller with [`@inline`](@ref) in order for `@inbounds` to have effect. + +```jldoctest +julia> @inline function g(A, i) + @boundscheck checkbounds(A, i) + return "accessing (\$A)[\$i]" + end + f1() = return g(1:2, -1) + f2() = @inbounds return g(1:2, -1) +f2 (generic function with 1 method) + +julia> f1() +ERROR: BoundsError: attempt to access 2-element UnitRange{Int64} at index [-1] +Stacktrace: + [1] throw_boundserror(::UnitRange{Int64}, ::Tuple{Int64}) at ./abstractarray.jl:428 + [2] checkbounds at ./abstractarray.jl:392 [inlined] + [3] g at ./REPL[20]:2 [inlined] + [4] f1() at ./REPL[20]:5 + +julia> f2() +"accessing (1:2)[-1]" +``` + +!!! warning + + The `@boundscheck` annotation allows you, as a library writer, to opt-in to + allowing *other code* to remove your bounds checks with [`@inbounds`](@ref). + As noted there, the caller must verify—using information they can access—that + their accesses are valid before using `@inbounds`. For indexing into your + [`AbstractArray`](@ref) subclasses, for example, this involves checking the + indices against its [`size`](@ref). Therefore, `@boundscheck` annotations + should only be added to a [`getindex`](@ref) or [`setindex!`](@ref) + implementation after you are certain its behavior is correct. +""" macro boundscheck(blk) return Expr(:if, Expr(:boundscheck), esc(blk)) end @@ -438,6 +478,8 @@ end Using `@inbounds` may return incorrect results/crashes/corruption for out-of-bounds indices. The user is responsible for checking it manually. + Only use `@inbounds` when it is certain from the information locally available + that all accesses are in bounds. """ macro inbounds(blk) return Expr(:block, diff --git a/doc/src/stdlib/base.md b/doc/src/stdlib/base.md index cee6d5872db44..15587998cb32d 100644 --- a/doc/src/stdlib/base.md +++ b/doc/src/stdlib/base.md @@ -146,6 +146,7 @@ Base.@eval Base.evalfile Base.esc Base.@inbounds +Base.@boundscheck Base.@inline Base.@noinline Base.@nospecialize From 09f727eeb084c4cc060196b2db2edeace4475d08 Mon Sep 17 00:00:00 2001 From: Matt Bauman Date: Wed, 30 Aug 2017 18:34:57 -0400 Subject: [PATCH 217/324] Add test for issue #20469 --- test/boundscheck_exec.jl | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/boundscheck_exec.jl b/test/boundscheck_exec.jl index d94cd5e95e915..35e61536ddb2d 100644 --- a/test/boundscheck_exec.jl +++ b/test/boundscheck_exec.jl @@ -229,4 +229,14 @@ else @test inbounds_isassigned(Int[], 2) == false end +# Test that @inbounds annotations don't propagate too far for Array; Issue #20469 +struct BadVector20469{T} <: AbstractVector{Int} + data::T +end +Base.size(X::BadVector20469) = size(X.data) +Base.getindex(X::BadVector20469, i::Int) = X.data[i-1] +if bc_opt != bc_off + @test_throws BoundsError BadVector20469([1,2,3])[:] +end + end From 9d6c9bd1e08b76d9a6f7cd580cae31058de8c053 Mon Sep 17 00:00:00 2001 From: Tony Kelman Date: Thu, 31 Aug 2017 12:02:33 -0400 Subject: [PATCH 218/324] Update to latest curl, mbedtls, and pcre (#23498) * Update to latest curl, mbedtls, and pcre * reason for the pcre patch was solved differently upstream --- deps/Versions.make | 6 +++--- deps/checksums/curl-7.54.1.tar.bz2/md5 | 1 - deps/checksums/curl-7.54.1.tar.bz2/sha512 | 1 - deps/checksums/curl-7.55.1.tar.bz2/md5 | 1 + deps/checksums/curl-7.55.1.tar.bz2/sha512 | 1 + deps/checksums/mbedtls-2.5.1-apache.tgz/md5 | 1 - deps/checksums/mbedtls-2.5.1-apache.tgz/sha512 | 1 - deps/checksums/mbedtls-2.5.1-gpl.tgz/md5 | 1 - deps/checksums/mbedtls-2.5.1-gpl.tgz/sha512 | 1 - deps/checksums/mbedtls-2.6.0-apache.tgz/md5 | 1 + deps/checksums/mbedtls-2.6.0-apache.tgz/sha512 | 1 + deps/checksums/mbedtls-2.6.0-gpl.tgz/md5 | 1 + deps/checksums/mbedtls-2.6.0-gpl.tgz/sha512 | 1 + deps/checksums/pcre2-10.23.tar.bz2/md5 | 1 - deps/checksums/pcre2-10.23.tar.bz2/sha512 | 1 - deps/checksums/pcre2-10.30.tar.bz2/md5 | 1 + deps/checksums/pcre2-10.30.tar.bz2/sha512 | 1 + deps/patches/pcre-mingw.patch | 13 ------------- deps/pcre.mk | 7 +------ 19 files changed, 12 insertions(+), 30 deletions(-) delete mode 100644 deps/checksums/curl-7.54.1.tar.bz2/md5 delete mode 100644 deps/checksums/curl-7.54.1.tar.bz2/sha512 create mode 100644 deps/checksums/curl-7.55.1.tar.bz2/md5 create mode 100644 deps/checksums/curl-7.55.1.tar.bz2/sha512 delete mode 100644 deps/checksums/mbedtls-2.5.1-apache.tgz/md5 delete mode 100644 deps/checksums/mbedtls-2.5.1-apache.tgz/sha512 delete mode 100644 deps/checksums/mbedtls-2.5.1-gpl.tgz/md5 delete mode 100644 deps/checksums/mbedtls-2.5.1-gpl.tgz/sha512 create mode 100644 deps/checksums/mbedtls-2.6.0-apache.tgz/md5 create mode 100644 deps/checksums/mbedtls-2.6.0-apache.tgz/sha512 create mode 100644 deps/checksums/mbedtls-2.6.0-gpl.tgz/md5 create mode 100644 deps/checksums/mbedtls-2.6.0-gpl.tgz/sha512 delete mode 100644 deps/checksums/pcre2-10.23.tar.bz2/md5 delete mode 100644 deps/checksums/pcre2-10.23.tar.bz2/sha512 create mode 100644 deps/checksums/pcre2-10.30.tar.bz2/md5 create mode 100644 deps/checksums/pcre2-10.30.tar.bz2/sha512 delete mode 100644 deps/patches/pcre-mingw.patch diff --git a/deps/Versions.make b/deps/Versions.make index f7bdc70bb59a3..48235e4424ca9 100644 --- a/deps/Versions.make +++ b/deps/Versions.make @@ -1,5 +1,5 @@ LLVM_VER = 3.9.1 -PCRE_VER = 10.23 +PCRE_VER = 10.30 DSFMT_VER = 2.2.3 LAPACK_VER = 3.5.0 ARPACK_VER = 3.3.0 @@ -9,5 +9,5 @@ OSXUNWIND_VER = 0.0.3 GMP_VER = 6.1.2 MPFR_VER = 3.1.5 PATCHELF_VER = 0.9 -MBEDTLS_VER = 2.5.1 -CURL_VER = 7.54.1 +MBEDTLS_VER = 2.6.0 +CURL_VER = 7.55.1 diff --git a/deps/checksums/curl-7.54.1.tar.bz2/md5 b/deps/checksums/curl-7.54.1.tar.bz2/md5 deleted file mode 100644 index d06698526d327..0000000000000 --- a/deps/checksums/curl-7.54.1.tar.bz2/md5 +++ /dev/null @@ -1 +0,0 @@ -6b6eb722f512e7a24855ff084f54fe55 diff --git a/deps/checksums/curl-7.54.1.tar.bz2/sha512 b/deps/checksums/curl-7.54.1.tar.bz2/sha512 deleted file mode 100644 index 5a4942693612b..0000000000000 --- a/deps/checksums/curl-7.54.1.tar.bz2/sha512 +++ /dev/null @@ -1 +0,0 @@ -eb9639677f0ca1521ca631c520ab83ad071c52b31690e5e7f31546f6a44b2f11d1bb62282056cffb570eb290bf1e7830e87cb536295ac6a54a904663e795f2da diff --git a/deps/checksums/curl-7.55.1.tar.bz2/md5 b/deps/checksums/curl-7.55.1.tar.bz2/md5 new file mode 100644 index 0000000000000..0aca15d410cc0 --- /dev/null +++ b/deps/checksums/curl-7.55.1.tar.bz2/md5 @@ -0,0 +1 @@ +8c153f282bbe482495214654cdcd4182 diff --git a/deps/checksums/curl-7.55.1.tar.bz2/sha512 b/deps/checksums/curl-7.55.1.tar.bz2/sha512 new file mode 100644 index 0000000000000..c5263aca18f9b --- /dev/null +++ b/deps/checksums/curl-7.55.1.tar.bz2/sha512 @@ -0,0 +1 @@ +bfeb39e94b8378519b2efba0a476636b80dbee3434104b98464ee81ce3871eb134e065f52abe8bedb69681b43576cb30655c8be0be6115386859d0cb426d745b diff --git a/deps/checksums/mbedtls-2.5.1-apache.tgz/md5 b/deps/checksums/mbedtls-2.5.1-apache.tgz/md5 deleted file mode 100644 index 63d6cb36d9fb9..0000000000000 --- a/deps/checksums/mbedtls-2.5.1-apache.tgz/md5 +++ /dev/null @@ -1 +0,0 @@ -1299f38583cb4f93c78c99c14a8c913f diff --git a/deps/checksums/mbedtls-2.5.1-apache.tgz/sha512 b/deps/checksums/mbedtls-2.5.1-apache.tgz/sha512 deleted file mode 100644 index ac443c5cc2d35..0000000000000 --- a/deps/checksums/mbedtls-2.5.1-apache.tgz/sha512 +++ /dev/null @@ -1 +0,0 @@ -8bde8f3d1d025a13c2a20fa8d2a4b9711adf0bb32d2226f1dd898dda3f3337c929b3d60e53684fbcf8094142568dd1d780353a92b94cb15c28c31c6d0542e6fc diff --git a/deps/checksums/mbedtls-2.5.1-gpl.tgz/md5 b/deps/checksums/mbedtls-2.5.1-gpl.tgz/md5 deleted file mode 100644 index f02d4fc23affe..0000000000000 --- a/deps/checksums/mbedtls-2.5.1-gpl.tgz/md5 +++ /dev/null @@ -1 +0,0 @@ -313f637f65b5f6d74d45310482a9c84f diff --git a/deps/checksums/mbedtls-2.5.1-gpl.tgz/sha512 b/deps/checksums/mbedtls-2.5.1-gpl.tgz/sha512 deleted file mode 100644 index 14d290278fb5b..0000000000000 --- a/deps/checksums/mbedtls-2.5.1-gpl.tgz/sha512 +++ /dev/null @@ -1 +0,0 @@ -fa05a5f61d9d79bc74e47badc89f68f8b6fd228537f10706cbdacc03b02455171b88121b3c1c5f56dff648c39ff88936413a9c5eea0dd0e046fb1f889e173c63 diff --git a/deps/checksums/mbedtls-2.6.0-apache.tgz/md5 b/deps/checksums/mbedtls-2.6.0-apache.tgz/md5 new file mode 100644 index 0000000000000..aed0379ec76f0 --- /dev/null +++ b/deps/checksums/mbedtls-2.6.0-apache.tgz/md5 @@ -0,0 +1 @@ +01ede06f7d00dd8a6626494d95a63f6b diff --git a/deps/checksums/mbedtls-2.6.0-apache.tgz/sha512 b/deps/checksums/mbedtls-2.6.0-apache.tgz/sha512 new file mode 100644 index 0000000000000..86ec33c3a2630 --- /dev/null +++ b/deps/checksums/mbedtls-2.6.0-apache.tgz/sha512 @@ -0,0 +1 @@ +5eb47d95a31c63e43074a115d141dedae869c43cbe62d5cf7bde11440e14fb8879ac6ed204d0d741b3501b8ba551019a5d47cbdf6673d18b61296be4463e9ffd diff --git a/deps/checksums/mbedtls-2.6.0-gpl.tgz/md5 b/deps/checksums/mbedtls-2.6.0-gpl.tgz/md5 new file mode 100644 index 0000000000000..9200bb43df37a --- /dev/null +++ b/deps/checksums/mbedtls-2.6.0-gpl.tgz/md5 @@ -0,0 +1 @@ +f03b8cf455f246e70e83662d534e156f diff --git a/deps/checksums/mbedtls-2.6.0-gpl.tgz/sha512 b/deps/checksums/mbedtls-2.6.0-gpl.tgz/sha512 new file mode 100644 index 0000000000000..6a65a69420c01 --- /dev/null +++ b/deps/checksums/mbedtls-2.6.0-gpl.tgz/sha512 @@ -0,0 +1 @@ +ed0765a476bf8a318fac1389b49d2b42e1a4ac5d4df749d241b24b8c8acea59c62fbb17e4b14057203f16ba7c58b8723ececf74d8a6b065a233a87696155076a diff --git a/deps/checksums/pcre2-10.23.tar.bz2/md5 b/deps/checksums/pcre2-10.23.tar.bz2/md5 deleted file mode 100644 index a562ef4a269d3..0000000000000 --- a/deps/checksums/pcre2-10.23.tar.bz2/md5 +++ /dev/null @@ -1 +0,0 @@ -b2cd00ca7e24049040099b0a46bb3649 diff --git a/deps/checksums/pcre2-10.23.tar.bz2/sha512 b/deps/checksums/pcre2-10.23.tar.bz2/sha512 deleted file mode 100644 index 61a5555f9b950..0000000000000 --- a/deps/checksums/pcre2-10.23.tar.bz2/sha512 +++ /dev/null @@ -1 +0,0 @@ -3e5910bd2405cc35934d91e4be760abe4f2e900202a20b6ba74adb7a3acb2b74b3bf9b0e97e8de10f8e8534133e0722e0bf0f5fb40d6c2c4520d1ed61749d456 diff --git a/deps/checksums/pcre2-10.30.tar.bz2/md5 b/deps/checksums/pcre2-10.30.tar.bz2/md5 new file mode 100644 index 0000000000000..ab6a1e91af07a --- /dev/null +++ b/deps/checksums/pcre2-10.30.tar.bz2/md5 @@ -0,0 +1 @@ +d3adf4b130eed854a530390f00020a65 diff --git a/deps/checksums/pcre2-10.30.tar.bz2/sha512 b/deps/checksums/pcre2-10.30.tar.bz2/sha512 new file mode 100644 index 0000000000000..06addeba2e3e9 --- /dev/null +++ b/deps/checksums/pcre2-10.30.tar.bz2/sha512 @@ -0,0 +1 @@ +f247a9f917c75920793b9919a45bb1426d126246e7a5d04e39d9407e44b5781f894a90cd3d232b385436b2f22be391335ab782664dd3a28c79058a2fcc74dc3e diff --git a/deps/patches/pcre-mingw.patch b/deps/patches/pcre-mingw.patch deleted file mode 100644 index 862cd4bf3a0a9..0000000000000 --- a/deps/patches/pcre-mingw.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/src/pcre2grep.c b/src/pcre2grep.c -index e98d743..f00dac2 100644 ---- a/src/pcre2grep.c -+++ b/src/pcre2grep.c -@@ -628,7 +628,7 @@ z/OS, and "no support". */ - - /************* Directory scanning Unix-style and z/OS ***********/ - --#if (defined HAVE_SYS_STAT_H && defined HAVE_DIRENT_H && defined HAVE_SYS_TYPES_H) || defined NATIVE_ZOS -+#if ((defined HAVE_SYS_STAT_H && defined HAVE_DIRENT_H && defined HAVE_SYS_TYPES_H) || defined NATIVE_ZOS) && !defined WIN32 - #include - #include - #include diff --git a/deps/pcre.mk b/deps/pcre.mk index c5e86f338ef30..6375c47bcdb99 100644 --- a/deps/pcre.mk +++ b/deps/pcre.mk @@ -13,12 +13,7 @@ $(SRCCACHE)/pcre2-$(PCRE_VER)/source-extracted: $(SRCCACHE)/pcre2-$(PCRE_VER).ta touch -c $(SRCCACHE)/pcre2-$(PCRE_VER)/configure # old target echo $1 > $@ -# patch for mingw build https://bugs.exim.org/show_bug.cgi?id=2067 -$(SRCCACHE)/pcre2-$(PCRE_VER)/pcre-mingw.patch-applied: $(SRCCACHE)/pcre2-$(PCRE_VER)/source-extracted - cd $(dir $@) && patch -p1 < $(SRCDIR)/patches/pcre-mingw.patch - echo 1 > $@ - -$(BUILDDIR)/pcre2-$(PCRE_VER)/build-configured: $(SRCCACHE)/pcre2-$(PCRE_VER)/source-extracted $(SRCCACHE)/pcre2-$(PCRE_VER)/pcre-mingw.patch-applied +$(BUILDDIR)/pcre2-$(PCRE_VER)/build-configured: $(SRCCACHE)/pcre2-$(PCRE_VER)/source-extracted mkdir -p $(dir $@) cd $(dir $@) && \ $(dir $<)/configure $(CONFIGURE_COMMON) --enable-jit --includedir=$(build_includedir) CFLAGS="$(CFLAGS) $(PCRE_CFLAGS)" LDFLAGS="$(LDFLAGS) $(PCRE_LDFLAGS)" From 8f6f5aae1e8f131a4cb716da10ffe8aa84a71fcc Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Thu, 31 Aug 2017 18:46:27 +0200 Subject: [PATCH 219/324] move mathematical constants to Base.MathConstants (#23427) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - only export pi, π and ℯ from Base - remove eu as alias for ℯ - add \euler as tab completion for ℯ --- NEWS.md | 9 ++++ base/deprecated.jl | 9 ++++ base/exports.jl | 5 +- base/irrationals.jl | 85 ---------------------------------- base/linalg/lapack.jl | 2 +- base/mathconstants.jl | 92 +++++++++++++++++++++++++++++++++++++ base/repl/latex_symbols.jl | 1 + base/sysimg.jl | 2 + contrib/julia.xml | 2 +- doc/src/manual/variables.md | 2 +- doc/src/stdlib/numbers.md | 10 ++-- test/bigfloat.jl | 2 +- test/complex.jl | 4 +- test/core.jl | 2 +- test/floatfuncs.jl | 2 +- test/math.jl | 36 +++++++-------- test/numbers.jl | 15 +++--- test/ranges.jl | 2 +- test/rounding.jl | 4 +- test/sparse/sparse.jl | 2 +- 20 files changed, 157 insertions(+), 131 deletions(-) create mode 100644 base/mathconstants.jl diff --git a/NEWS.md b/NEWS.md index 6767591219dd4..a214aa8ae1a6f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -378,6 +378,15 @@ Deprecated or removed * `diagm(A::BitMatrix)` has been deprecated, use `diagm(vec(A))` instead ([#23373]). + * `ℯ` (written as `\mscre` or `\euler`) is the new default for Euler's + number ([#23427]). + + * The mathematical constants `π`, `pi`, `ℯ`, `e`, `γ`, `eulergamma`, `catalan`, `φ` and + `golden` have been have been moved from `Base` to a new module; `Base.MathConstants`. + Only `π`, `pi` and `ℯ` are now exported by default from `Base` ([#23427]). + + * `eu` (previously an alias for `ℯ`) has been deprecated in favor of `ℯ` (or `MathConstants.e`) ([#23427]). + * `GMP.gmp_version()`, `GMP.GMP_VERSION`, `GMP.gmp_bits_per_limb()`, and `GMP.GMP_BITS_PER_LIBM` have been renamed to `GMP.version()`, `GMP.VERSION`, `GMP.bits_per_libm()`, and `GMP.BITS_PER_LIBM`, respectively. Similarly, `MPFR.get_version()`, has been renamed to `MPFR.version()` ([#23323]). Also, diff --git a/base/deprecated.jl b/base/deprecated.jl index 32d08e7da8e58..e47d6f581ff9d 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1691,6 +1691,15 @@ export hex2num @eval MPFR @deprecate get_version() version() false @eval LinAlg.LAPACK @deprecate laver() version() false +# PR #23427 +@deprecate_binding e ℯ +@deprecate_binding eu ℯ +@deprecate_binding γ MathConstants.γ +@deprecate_binding eulergamma MathConstants.eulergamma +@deprecate_binding catalan MathConstants.catalan +@deprecate_binding φ MathConstants.φ +@deprecate_binding golden MathConstants.golden + # PR #23271 function IOContext(io::IO; kws...) depwarn("IOContext(io, k=v, ...) is deprecated, use IOContext(io, :k => v, ...) instead.", :IOContext) diff --git a/base/exports.jl b/base/exports.jl index b1c77b26fb2b8..62ef78c2c5d38 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -186,10 +186,7 @@ export NaN64, im, π, pi, - e, eu, - γ, eulergamma, - catalan, - φ, golden, + ℯ, I, # Operators diff --git a/base/irrationals.jl b/base/irrationals.jl index 1d53477674423..4e106bc54987b 100644 --- a/base/irrationals.jl +++ b/base/irrationals.jl @@ -141,91 +141,6 @@ end big(x::Irrational) = convert(BigFloat,x) big(::Type{<:Irrational}) = BigFloat -## specific irrational mathematical constants - -@irrational π 3.14159265358979323846 pi -@irrational e 2.71828182845904523536 exp(big(1)) -@irrational γ 0.57721566490153286061 euler -@irrational catalan 0.91596559417721901505 catalan -@irrational φ 1.61803398874989484820 (1+sqrt(big(5)))/2 - -# aliases -""" - pi - π - -The constant pi. - -```jldoctest -julia> pi -π = 3.1415926535897... -``` -""" -π, const pi = π - -""" - e - eu - -The constant e. - -```jldoctest -julia> e -e = 2.7182818284590... -``` -""" -e, const eu = e - -""" - γ - eulergamma - -Euler's constant. - -```jldoctest -julia> eulergamma -γ = 0.5772156649015... -``` -""" -γ, const eulergamma = γ - -""" - φ - golden - -The golden ratio. - -```jldoctest -julia> golden -φ = 1.6180339887498... -``` -""" -φ, const golden = φ - -""" - catalan - -Catalan's constant. - -```jldoctest -julia> catalan -catalan = 0.9159655941772... -``` -""" -catalan - -# special behaviors - -# use exp for e^x or e.^x, as in -# ^(::Irrational{:e}, x::Number) = exp(x) -# but need to loop over types to prevent ambiguity with generic rules for ^(::Number, x) etc. -for T in (Irrational, Rational, Integer, Number) - ^(::Irrational{:e}, x::T) = exp(x) -end - -log(::Irrational{:e}) = 1 # use 1 to correctly promote expressions like log(x)/log(e) -log(::Irrational{:e}, x::Number) = log(x) - # align along = for nice Array printing function alignment(io::IO, x::Irrational) m = match(r"^(.*?)(=.*)$", sprint(0, showcompact, x, env=io)) diff --git a/base/linalg/lapack.jl b/base/linalg/lapack.jl index 5e5a60982f660..c0ed8ca177137 100644 --- a/base/linalg/lapack.jl +++ b/base/linalg/lapack.jl @@ -5299,7 +5299,7 @@ for (bdsdc, elty) in u, ldu, vt, ldvt, q, iq, work, iwork, info) chklapackerror(info[]) - d, e, u, vt, q, iq + d, e_, u, vt, q, iq end end end diff --git a/base/mathconstants.jl b/base/mathconstants.jl new file mode 100644 index 0000000000000..d82d61e5414d1 --- /dev/null +++ b/base/mathconstants.jl @@ -0,0 +1,92 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +""" + Base.MathConstants + +Module containing the mathematical constants. +See [`π`](@ref), [`ℯ`](@ref), [`γ`](@ref), [`φ`](@ref) and [`catalan`](@ref). +""" +module MathConstants + +export π, pi, ℯ, e, γ, eulergamma, catalan, φ, golden + +Base.@irrational π 3.14159265358979323846 pi +Base.@irrational ℯ 2.71828182845904523536 exp(big(1)) +Base.@irrational γ 0.57721566490153286061 euler +Base.@irrational φ 1.61803398874989484820 (1+sqrt(big(5)))/2 +Base.@irrational catalan 0.91596559417721901505 catalan + +# aliases +""" + π + pi + +The constant pi. + +```jldoctest +julia> pi +π = 3.1415926535897... +``` +""" +π, const pi = π + +""" + ℯ + e + +The constant ℯ. + +```jldoctest +julia> ℯ +ℯ = 2.7182818284590... +``` +""" +ℯ, const e = ℯ + +""" + γ + eulergamma + +Euler's constant. + +```jldoctest +julia> MathConstants.eulergamma +γ = 0.5772156649015... +``` +""" +γ, const eulergamma = γ + +""" + φ + golden + +The golden ratio. + +```jldoctest +julia> MathConstants.golden +φ = 1.6180339887498... +``` +""" +φ, const golden = φ + +""" + catalan + +Catalan's constant. + +```jldoctest +julia> MathConstants.catalan +catalan = 0.9159655941772... +``` +""" +catalan + +# loop over types to prevent ambiguities for ^(::Number, x) +for T in (Irrational, Rational, Integer, Number) + Base.:^(::Irrational{:ℯ}, x::T) = exp(x) +end + +Base.log(::Irrational{:ℯ}) = 1 # use 1 to correctly promote expressions like log(x)/log(ℯ) +Base.log(::Irrational{:ℯ}, x::Number) = log(x) + +end # module diff --git a/base/repl/latex_symbols.jl b/base/repl/latex_symbols.jl index 2a546b01259eb..e268b389a644b 100644 --- a/base/repl/latex_symbols.jl +++ b/base/repl/latex_symbols.jl @@ -88,6 +88,7 @@ const latex_symbols = Dict( "\\implies" => "⟹", "\\impliedby" => "⟸", "\\to" => "→", + "\\euler" => "ℯ", # Superscripts "\\^0" => "⁰", diff --git a/base/sysimg.jl b/base/sysimg.jl index 8b180d773cdef..676fb70a6ee53 100644 --- a/base/sysimg.jl +++ b/base/sysimg.jl @@ -323,6 +323,8 @@ include("hashing2.jl") # irrational mathematical constants include("irrationals.jl") +include("mathconstants.jl") +using .MathConstants: ℯ, π, pi # random number generation include("random/dSFMT.jl") diff --git a/contrib/julia.xml b/contrib/julia.xml index 577fc37de5d62..65ebbc95ee351 100644 --- a/contrib/julia.xml +++ b/contrib/julia.xml @@ -306,8 +306,8 @@ im π pi + e - eu γ eulergamma catalan diff --git a/doc/src/manual/variables.md b/doc/src/manual/variables.md index 700adc4b916ef..7e2df62bd8f92 100644 --- a/doc/src/manual/variables.md +++ b/doc/src/manual/variables.md @@ -81,7 +81,7 @@ julia> pi π = 3.1415926535897... julia> pi = 3 -ERROR: cannot assign variable Base.pi from module Main +ERROR: cannot assign variable MathConstants.pi from module Main julia> sqrt(100) 10.0 diff --git a/doc/src/stdlib/numbers.md b/doc/src/stdlib/numbers.md index 76ead7563ee41..977bd50049ac8 100644 --- a/doc/src/stdlib/numbers.md +++ b/doc/src/stdlib/numbers.md @@ -69,12 +69,12 @@ Base.bytes2hex Base.one Base.oneunit Base.zero -Base.pi Base.im -Base.eu -Base.catalan -Base.eulergamma -Base.golden +Base.MathConstants.pi +Base.MathConstants.ℯ +Base.MathConstants.catalan +Base.MathConstants.eulergamma +Base.MathConstants.golden Base.Inf Base.Inf32 Base.Inf16 diff --git a/test/bigfloat.jl b/test/bigfloat.jl index 4966c1b7a1c6e..4fa613ddb3718 100644 --- a/test/bigfloat.jl +++ b/test/bigfloat.jl @@ -7,7 +7,7 @@ for T in [Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt @test big(2.0)^T(3) == 8 end -for x in (2f0, pi, 7.8, big(e)) +for x in (2f0, pi, 7.8, big(ℯ)) @test big(typeof(x)) == typeof(big(x)) @test big(typeof(complex(x, x))) == typeof(big(complex(x, x))) end diff --git a/test/complex.jl b/test/complex.jl index e1428a826573e..bf131ab6b6f93 100644 --- a/test/complex.jl +++ b/test/complex.jl @@ -901,7 +901,7 @@ end @test round.([1:5;] + 0.5im) == [1.0:5.0;] @test float(Complex(1, 2)) == Complex(1.0, 2.0) - @test round(float(Complex(π, e)),3) == Complex(3.142, 2.718) + @test round(float(Complex(π, ℯ)),3) == Complex(3.142, 2.718) end @testset "Complex32 arithmetic, PR #10003" begin @@ -947,7 +947,7 @@ end @test big(1)/(10+10im) ≈ (5-5im)/big(100) ≈ big"0.05" - big"0.05"*im @testset "Complex Irrationals, issue #21204" begin - for x in (pi, e, catalan) # No need to test all of them + for x in (pi, ℯ, Base.MathConstants.catalan) # No need to test all of them z = Complex(x, x) @test typeof(z) == Complex{typeof(x)} @test exp(z) ≈ exp(x) * cis(x) diff --git a/test/core.jl b/test/core.jl index 79db462688b8a..79881a2c747ea 100644 --- a/test/core.jl +++ b/test/core.jl @@ -942,7 +942,7 @@ end @test unsafe_pointer_to_objref(ccall(:jl_call1, Ptr{Void}, (Any,Any), x -> x+1, 314158)) == 314159 -@test unsafe_pointer_to_objref(pointer_from_objref(e+pi)) == e+pi +@test unsafe_pointer_to_objref(pointer_from_objref(ℯ+pi)) == ℯ+pi let local a, aa diff --git a/test/floatfuncs.jl b/test/floatfuncs.jl index c97f077f05438..ed1639b55ef10 100644 --- a/test/floatfuncs.jl +++ b/test/floatfuncs.jl @@ -68,7 +68,7 @@ for elty in (Float32,Float64) end @testset "Types" begin - for x in (Int16(0), 1, 2f0, pi, 3//4, big(5//6), 7.8, big(9), big(e)) + for x in (Int16(0), 1, 2f0, pi, 3//4, big(5//6), 7.8, big(9), big(ℯ)) @test float(typeof(x)) == typeof(float(x)) @test float(typeof(complex(x, x))) == typeof(float(complex(x, x))) end diff --git a/test/math.jl b/test/math.jl index deba71da7dc5b..f3d31b1782939 100644 --- a/test/math.jl +++ b/test/math.jl @@ -23,16 +23,16 @@ end @testset "constants" begin - @test pi != e - @test e != 1//2 - @test 1//2 <= e - @test e <= 15//3 - @test big(1//2) < e - @test e < big(20//6) - @test e^pi == exp(pi) - @test e^2 == exp(2) - @test e^2.4 == exp(2.4) - @test e^(2//3) == exp(2//3) + @test pi != ℯ + @test ℯ != 1//2 + @test 1//2 <= ℯ + @test ℯ <= 15//3 + @test big(1//2) < ℯ + @test ℯ < big(20//6) + @test ℯ^pi == exp(pi) + @test ℯ^2 == exp(2) + @test ℯ^2.4 == exp(2.4) + @test ℯ^(2//3) == exp(2//3) @test Float16(3.0) < pi @test pi < Float16(4.0) @@ -171,19 +171,19 @@ end @test isequal(cos(T(0)), T(1)) @test cos(T(pi)/2) ≈ T(0) atol=eps(T) @test isequal(cos(T(pi)), T(-1)) - @test exp(T(1)) ≈ T(e) atol=10*eps(T) + @test exp(T(1)) ≈ T(ℯ) atol=10*eps(T) @test isequal(exp10(T(1)), T(10)) @test isequal(exp2(T(1)), T(2)) @test isequal(expm1(T(0)), T(0)) - @test expm1(T(1)) ≈ T(e)-1 atol=10*eps(T) + @test expm1(T(1)) ≈ T(ℯ)-1 atol=10*eps(T) @test isequal(hypot(T(3),T(4)), T(5)) @test isequal(log(T(1)), T(0)) - @test isequal(log(e,T(1)), T(0)) - @test log(T(e)) ≈ T(1) atol=eps(T) + @test isequal(log(ℯ,T(1)), T(0)) + @test log(T(ℯ)) ≈ T(1) atol=eps(T) @test isequal(log10(T(1)), T(0)) @test isequal(log10(T(10)), T(1)) @test isequal(log1p(T(0)), T(0)) - @test log1p(T(e)-1) ≈ T(1) atol=eps(T) + @test log1p(T(ℯ)-1) ≈ T(1) atol=eps(T) @test isequal(log2(T(1)), T(0)) @test isequal(log2(T(2)), T(1)) @test isequal(sin(T(0)), T(0)) @@ -402,7 +402,7 @@ end end @testset "Irrational args to sinpi/cospi/sinc/cosc" begin - for x in (pi, e, golden) + for x in (pi, ℯ, Base.MathConstants.golden) @test sinpi(x) ≈ Float64(sinpi(big(x))) @test cospi(x) ≈ Float64(cospi(big(x))) @test sinc(x) ≈ Float64(sinc(big(x))) @@ -662,8 +662,8 @@ end @testset "test fallback definitions" begin @test exp10(5) ≈ exp10(5.0) @test exp10(50//10) ≈ exp10(5.0) - @test log10(exp10(e)) ≈ e - @test log(e) === 1 + @test log10(exp10(ℯ)) ≈ ℯ + @test log(ℯ) === 1 @test exp2(Float16(2.0)) ≈ exp2(2.0) @test exp2(Float16(1.0)) === Float16(exp2(1.0)) @test exp10(Float16(1.0)) === Float16(exp10(1.0)) diff --git a/test/numbers.jl b/test/numbers.jl index 0f694fa410080..84e54e5e3b862 100644 --- a/test/numbers.jl +++ b/test/numbers.jl @@ -1,5 +1,6 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license +using Base.MathConstants const ≣ = isequal # convenient for comparing NaNs # basic booleans @@ -2491,7 +2492,7 @@ z2 = read(zbuf, Complex128) @test bswap(z2) === 3.5 - 4.5im #isreal(x::Real) = true -for x in [1.23, 7, e, 4//5] #[FP, Int, Irrational, Rat] +for x in [1.23, 7, ℯ, 4//5] #[FP, Int, Irrational, Rat] @test isreal(x) == true end @@ -2526,7 +2527,7 @@ let number_types = Set() end #getindex(x::Number) = x -for x in [1.23, 7, e, 4//5] #[FP, Int, Irrational, Rat] +for x in [1.23, 7, ℯ, 4//5] #[FP, Int, Irrational, Rat] @test getindex(x) == x @test getindex(x, 1, 1) == x end @@ -2537,7 +2538,7 @@ end #getindex(x::Array,-1) throws BoundsError #getindex(x::Array,0 throws BoundsError #getindex(x::Array,length(x::Array)+1) throws BoundsError -for x in [1.23, 7, e, 4//5] #[FP, Int, Irrational, Rat] +for x in [1.23, 7, ℯ, 4//5] #[FP, Int, Irrational, Rat] @test_throws BoundsError getindex(x,-1) @test_throws BoundsError getindex(x,0) @test_throws BoundsError getindex(x,2) @@ -2549,8 +2550,8 @@ end # copysign(x::Real, y::Real) = ifelse(signbit(x)!=signbit(y), -x, x) # flipsign(x::Real, y::Real) = ifelse(signbit(y), -x, x) -for x in [1.23, 7, e, 4//5] - for y in [1.23, 7, e, 4//5] +for x in [1.23, 7, ℯ, 4//5] + for y in [1.23, 7, ℯ, 4//5] @test copysign(x, y) == x @test copysign(x, -y) == -x @test copysign(-x, y) == x @@ -2570,7 +2571,7 @@ end #in(x::Number, y::Number) = x == y @test in(3,3) == true #Int @test in(2.0,2.0) == true #FP -@test in(e,e) == true #Const +@test in(ℯ,ℯ) == true #Const @test in(4//5,4//5) == true #Rat @test in(1+2im, 1+2im) == true #Imag @test in(3, 3.0) == true #mixed @@ -2952,7 +2953,7 @@ end end @test !iszero(nextfloat(BigFloat(0))) @test !isone(nextfloat(BigFloat(1))) - for x in (π, e, γ, catalan, φ) + for x in (π, ℯ, γ, catalan, φ) @test !iszero(x) @test !isone(x) end diff --git a/test/ranges.jl b/test/ranges.jl index e34fbad8628d7..a318f04b2c0f0 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -1152,7 +1152,7 @@ end # test default values; n = 50, base = 10 @test logspace(a, b) == logspace(a, b, 50) == 10 .^ linspace(a, b, 50) @test logspace(a, b, n) == 10 .^ linspace(a, b, n) - for base in (10, 2, e) + for base in (10, 2, ℯ) @test logspace(a, b, base=base) == logspace(a, b, 50, base=base) == base.^linspace(a, b, 50) @test logspace(a, b, n, base=base) == base.^linspace(a, b, n) end diff --git a/test/rounding.jl b/test/rounding.jl index 32a19d10b1a1b..06256c8e72efd 100644 --- a/test/rounding.jl +++ b/test/rounding.jl @@ -1,7 +1,7 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license # Small sanity tests to ensure changing the rounding of float functions work -using Base.Test +using Base: Test, MathConstants ## Float64 checks # a + b returns a number exactly between prevfloat(1.) and 1., so its @@ -110,7 +110,7 @@ end for T in [Float32,Float64] for v in [sqrt(big(2.0)),-big(1.0)/big(3.0),nextfloat(big(1.0)), prevfloat(big(1.0)),nextfloat(big(0.0)),prevfloat(big(0.0)), - pi,e,eulergamma,catalan,golden, + pi,ℯ,eulergamma,catalan,golden, typemax(Int64),typemax(UInt64),typemax(Int128),typemax(UInt128),0xa2f30f6001bb2ec6] pn = T(v,RoundNearest) @test pn == convert(T,BigFloat(v)) diff --git a/test/sparse/sparse.jl b/test/sparse/sparse.jl index 7a24f2145472a..600fe519ebc33 100644 --- a/test/sparse/sparse.jl +++ b/test/sparse/sparse.jl @@ -428,7 +428,7 @@ end @testset "exp" begin A = sprandn(5,5,0.2) - @test e.^A ≈ e.^Array(A) + @test ℯ.^A ≈ ℯ.^Array(A) end @testset "reductions" begin From 6210e241f189e3d067a6b1c900360c3f38189a47 Mon Sep 17 00:00:00 2001 From: Klaus Crusius Date: Thu, 31 Aug 2017 19:10:53 +0200 Subject: [PATCH 220/324] make findmin/findmax behavior match min/max (fix #23209) (#23227) --- NEWS.md | 4 + base/array.jl | 36 ++++--- base/reducedim.jl | 131 +++++++++++++++++-------- base/sparse/sparsematrix.jl | 41 ++++---- test/arrayops.jl | 17 ++-- test/reducedim.jl | 185 +++++++++++++++++++++++++++++++++++- test/sparse/sparse.jl | 109 ++++++++++++++++++++- 7 files changed, 428 insertions(+), 95 deletions(-) diff --git a/NEWS.md b/NEWS.md index a214aa8ae1a6f..e2380328156cc 100644 --- a/NEWS.md +++ b/NEWS.md @@ -145,6 +145,10 @@ This section lists changes that do not have deprecation warnings. `Tridiagonal{T,V<:AbstractVector{T}}` and `SymTridiagonal{T,V<:AbstractVector{T}}` respectively ([#22718], [#22925], [#23035], [#23154]). + * When called with an argument that contains `NaN` elements, `findmin` and `findmax` now return the + first `NaN` found and its corresponding index. Previously, `NaN` elements were ignored. + The new behavior matches that of `min`, `max`, `minimum`, and `maximum`. + * `isapprox(x,y)` now tests `norm(x-y) <= max(atol, rtol*max(norm(x), norm(y)))` rather than `norm(x-y) <= atol + ...`, and `rtol` defaults to zero if an `atol > 0` is specified ([#22742]). diff --git a/base/array.jl b/base/array.jl index 5ddef935a2161..61ef1fbd63f29 100644 --- a/base/array.jl +++ b/base/array.jl @@ -332,7 +332,6 @@ function fill!(a::Array{T}, x) where T<:Union{Integer,AbstractFloat} return a end - """ fill(x, dims) @@ -1020,7 +1019,6 @@ function _prepend!(a, ::IteratorSize, iter) a end - """ resize!(a::Vector, n::Integer) -> Vector @@ -1539,7 +1537,6 @@ function reverse!(v::AbstractVector, s=first(linearindices(v)), n=last(linearind return v end - # concatenations of homogeneous combinations of vectors, horizontal and vertical vcat() = Array{Any,1}(0) @@ -1584,7 +1581,6 @@ end cat(n::Integer, x::Integer...) = reshape([x...], (ntuple(x->1, n-1)..., length(x))) - ## find ## """ @@ -2054,8 +2050,9 @@ end findmax(itr) -> (x, index) Returns the maximum element of the collection `itr` and its index. If there are multiple -maximal elements, then the first one will be returned. `NaN` values are ignored, unless -all elements are `NaN`. Other than the treatment of `NaN`, the result is in line with `max`. +maximal elements, then the first one will be returned. +If any data element is `NaN`, this element is returned. +The result is in line with `max`. The collection must not be empty. @@ -2068,7 +2065,7 @@ julia> findmax([1,7,7,6]) (7, 2) julia> findmax([1,7,7,NaN]) -(7.0, 2) +(NaN, 4) ``` """ function findmax(a) @@ -2079,10 +2076,10 @@ function findmax(a) mi = i = 1 m, s = next(a, s) while !done(a, s) + m != m && break ai, s = next(a, s) i += 1 - ai != ai && continue # assume x != x => x is a NaN - if m != m || isless(m, ai) + if ai != ai || isless(m, ai) m = ai mi = i end @@ -2094,8 +2091,9 @@ end findmin(itr) -> (x, index) Returns the minimum element of the collection `itr` and its index. If there are multiple -minimal elements, then the first one will be returned. `NaN` values are ignored, unless -all elements are `NaN`. Other than the treatment of `NaN`, the result is in line with `min`. +minimal elements, then the first one will be returned. +If any data element is `NaN`, this element is returned. +The result is in line with `min`. The collection must not be empty. @@ -2108,7 +2106,7 @@ julia> findmin([7,1,1,6]) (1, 2) julia> findmin([7,1,1,NaN]) -(1.0, 2) +(NaN, 4) ``` """ function findmin(a) @@ -2119,10 +2117,10 @@ function findmin(a) mi = i = 1 m, s = next(a, s) while !done(a, s) + m != m && break ai, s = next(a, s) i += 1 - ai != ai && continue - if m != m || isless(ai, m) + if ai != ai || isless(ai, m) m = ai mi = i end @@ -2134,8 +2132,7 @@ end indmax(itr) -> Integer Returns the index of the maximum element in a collection. If there are multiple maximal -elements, then the first one will be returned. `NaN` values are ignored, unless all -elements are `NaN`. +elements, then the first one will be returned. The collection must not be empty. @@ -2148,7 +2145,7 @@ julia> indmax([1,7,7,6]) 2 julia> indmax([1,7,7,NaN]) -2 +4 ``` """ indmax(a) = findmax(a)[2] @@ -2157,8 +2154,7 @@ indmax(a) = findmax(a)[2] indmin(itr) -> Integer Returns the index of the minimum element in a collection. If there are multiple minimal -elements, then the first one will be returned. `NaN` values are ignored, unless all -elements are `NaN`. +elements, then the first one will be returned. The collection must not be empty. @@ -2171,7 +2167,7 @@ julia> indmin([7,1,1,6]) 2 julia> indmin([7,1,1,NaN]) -2 +4 ``` """ indmin(a) = findmin(a)[2] diff --git a/base/reducedim.jl b/base/reducedim.jl index 84a168e1245df..2bd52371d5529 100644 --- a/base/reducedim.jl +++ b/base/reducedim.jl @@ -23,7 +23,8 @@ reduced_indices(inds::Indices, d::Int) = reduced_indices(inds, d, OneTo(1)) function reduced_indices0(inds::Indices{N}, d::Int) where N d < 1 && throw(ArgumentError("dimension must be ≥ 1, got $d")) if d <= N - return reduced_indices(inds, d, (inds[d] == OneTo(0) ? OneTo(0) : OneTo(1))) + ind = inds[d] + return reduced_indices(inds, d, (isempty(ind) ? ind : OneTo(1))) else return inds end @@ -51,30 +52,50 @@ function reduced_indices0(inds::Indices{N}, region) where N if d < 1 throw(ArgumentError("region dimension(s) must be ≥ 1, got $d")) elseif d <= N - rinds[d] = oftype(rinds[d], (rinds[d] == OneTo(0) ? OneTo(0) : OneTo(1))) + rind = rinds[d] + rinds[d] = oftype(rind, (isempty(rind) ? rind : OneTo(1))) end end tuple(rinds...)::typeof(inds) end - ###### Generic reduction functions ##### ## initialization -for (Op, initfun) in ((:(typeof(+)), :zero), (:(typeof(*)), :one), (:(typeof(scalarmax)), :typemin), (:(typeof(scalarmin)), :typemax), (:(typeof(max)), :typemin), (:(typeof(min)), :typemax)) - @eval initarray!(a::AbstractArray{T}, ::$(Op), init::Bool) where {T} = (init && fill!(a, $(initfun)(T)); a) +for (Op, initfun) in ((:(typeof(+)), :zero), (:(typeof(*)), :one)) + @eval initarray!(a::AbstractArray{T}, ::$(Op), init::Bool, src::AbstractArray) where {T} = (init && fill!(a, $(initfun)(T)); a) +end + +for Op in (:(typeof(scalarmax)), :(typeof(scalarmin)), :(typeof(max)), :(typeof(min))) + @eval initarray!(a::AbstractArray{T}, ::$(Op), init::Bool, src::AbstractArray) where {T} = (init && copyfirst!(a, src); a) end for (Op, initval) in ((:(typeof(&)), true), (:(typeof(|)), false)) - @eval initarray!(a::AbstractArray, ::$(Op), init::Bool) = (init && fill!(a, $initval); a) + @eval initarray!(a::AbstractArray, ::$(Op), init::Bool, src::AbstractArray) = (init && fill!(a, $initval); a) end reducedim_initarray(A::AbstractArray, region, v0, ::Type{R}) where {R} = fill!(similar(A,R,reduced_indices(A,region)), v0) reducedim_initarray(A::AbstractArray, region, v0::T) where {T} = reducedim_initarray(A, region, v0, T) -reducedim_initarray0(A::AbstractArray, region, v0, ::Type{R}) where {R} = fill!(similar(A,R,reduced_indices0(A,region)), v0) -reducedim_initarray0(A::AbstractArray, region, v0::T) where {T} = reducedim_initarray0(A, region, v0, T) +function reducedim_initarray0(A::AbstractArray{T}, region, f, ops) where T + ri = reduced_indices0(A, region) + if isempty(A) + if prod(map(length, reduced_indices(A, region))) != 0 + reducedim_initarray0_empty(A, region, f, ops) # ops over empty slice of A + else + R = f == identity ? T : Core.Inference.return_type(f, (T,)) + similar(A, R, ri) + end + else + R = f == identity ? T : typeof(f(first(A))) + si = similar(A, R, ri) + mapfirst!(f, si, A) + end +end + +reducedim_initarray0_empty(A::AbstractArray, region, f, ops) = mapslices(x->ops(f.(x)), A, region) +reducedim_initarray0_empty(A::AbstractArray, region,::typeof(identity), ops) = mapslices(ops, A, region) # TODO: better way to handle reducedim initialization # @@ -91,7 +112,7 @@ function reducedim_init(f, op::typeof(*), A::AbstractArray, region) end function _reducedim_init(f, op, fv, fop, A, region) T = promote_union(eltype(A)) - if method_exists(zero, Tuple{Type{T}}) + if applicable(zero, T) x = f(zero(T)) z = op(fv(x), fv(x)) Tr = typeof(z) == typeof(x) && !isbits(T) ? T : typeof(z) @@ -106,8 +127,8 @@ reducedim_init(f, op::typeof(max), A::AbstractArray, region) = reducedim_init(f, reducedim_init(f, op::typeof(min), A::AbstractArray, region) = reducedim_init(f, scalarmin, A, region) reducedim_init(f::Union{typeof(abs),typeof(abs2)}, op::typeof(max), A::AbstractArray, region) = reducedim_init(f, scalarmax, A, region) -reducedim_init(f, op::typeof(scalarmax), A::AbstractArray{T}, region) where {T} = reducedim_initarray0(A, region, typemin(f(zero(T)))) -reducedim_init(f, op::typeof(scalarmin), A::AbstractArray{T}, region) where {T} = reducedim_initarray0(A, region, typemax(f(zero(T)))) +reducedim_init(f, op::typeof(scalarmax), A::AbstractArray{T}, region) where {T} = reducedim_initarray0(A, region, f, maximum) +reducedim_init(f, op::typeof(scalarmin), A::AbstractArray{T}, region) where {T} = reducedim_initarray0(A, region, f, minimum) reducedim_init(f::Union{typeof(abs),typeof(abs2)}, op::typeof(scalarmax), A::AbstractArray{T}, region) where {T} = reducedim_initarray(A, region, zero(f(zero(T)))) @@ -132,7 +153,6 @@ end reducedim_init(f::Union{typeof(identity),typeof(abs),typeof(abs2)}, op::typeof(+), A::AbstractArray{Bool}, region) = reducedim_initarray(A, region, 0) - ## generic (map)reduction has_fast_linear_indexing(a::AbstractArray) = false @@ -169,6 +189,23 @@ function check_reducedims(R, A) return lsiz end +""" +Extract first entry of slices of array A into existing array R. +""" +copyfirst!(R::AbstractArray, A::AbstractArray) = mapfirst!(identity, R, A) + +function mapfirst!(f, R::AbstractArray, A::AbstractArray) + lsiz = check_reducedims(R, A) + iA = indices(A) + iR = indices(R) + t = [] + for i in 1:length(iR) + iAi = iA[i] + push!(t, iAi == iR[i] ? iAi : first(iAi)) + end + map!(f, R, view(A, t...)) +end + function _mapreducedim!(f, op, R::AbstractArray, A::AbstractArray) lsiz = check_reducedims(R,A) isempty(A) && return R @@ -211,7 +248,7 @@ mapreducedim!(f, op, R::AbstractArray, A::AbstractArray) = (_mapreducedim!(f, op, R, A); R) reducedim!(op, R::AbstractArray{RT}, A::AbstractArray) where {RT} = - mapreducedim!(identity, op, R, A, zero(RT)) + mapreducedim!(identity, op, R, A) """ mapreducedim(f, op, A, region[, v0]) @@ -277,7 +314,6 @@ julia> reducedim(max, a, 1) reducedim(op, A::AbstractArray, region, v0) = mapreducedim(identity, op, A, region, v0) reducedim(op, A::AbstractArray, region) = mapreducedim(identity, op, A, region) - ##### Specific reduction functions ##### """ sum(A, dims) @@ -577,7 +613,7 @@ for (fname, op) in [(:sum, :+), (:prod, :*), fname! = Symbol(fname, '!') @eval begin $(fname!)(f::Function, r::AbstractArray, A::AbstractArray; init::Bool=true) = - mapreducedim!(f, $(op), initarray!(r, $(op), init), A) + mapreducedim!(f, $(op), initarray!(r, $(op), init, A), A) $(fname!)(r::AbstractArray, A::AbstractArray; init::Bool=true) = $(fname!)(identity, r, A; init=init) $(fname)(f::Function, A::AbstractArray, region) = @@ -586,9 +622,9 @@ for (fname, op) in [(:sum, :+), (:prod, :*), end end - ##### findmin & findmax ##### - +# The initial values of Rval are not used if the correponding indices in Rind are 0. +# function findminmax!(f, Rval, Rind, A::AbstractArray{T,N}) where {T,N} (isempty(Rval) || isempty(A)) && return Rval, Rind lsiz = check_reducedims(Rval, A) @@ -609,7 +645,7 @@ function findminmax!(f, Rval, Rind, A::AbstractArray{T,N}) where {T,N} for i in indices(A,1) k += 1 tmpAv = A[i,IA] - if f(tmpAv, tmpRv) + if tmpRi == 0 || (tmpRv == tmpRv && (tmpAv != tmpAv || f(tmpAv, tmpRv))) tmpRv = tmpAv tmpRi = k end @@ -623,7 +659,9 @@ function findminmax!(f, Rval, Rind, A::AbstractArray{T,N}) where {T,N} for i in indices(A, 1) k += 1 tmpAv = A[i,IA] - if f(tmpAv, Rval[i,IR]) + tmpRv = Rval[i,IR] + tmpRi = Rind[i,IR] + if tmpRi == 0 || (tmpRv == tmpRv && (tmpAv != tmpAv || f(tmpAv, tmpRv))) Rval[i,IR] = tmpAv Rind[i,IR] = k end @@ -633,83 +671,96 @@ function findminmax!(f, Rval, Rind, A::AbstractArray{T,N}) where {T,N} Rval, Rind end - """ findmin!(rval, rind, A, [init=true]) -> (minval, index) Find the minimum of `A` and the corresponding linear index along singleton dimensions of `rval` and `rind`, and store the results in `rval` and `rind`. +`NaN` is treated as less than all other values. """ function findmin!(rval::AbstractArray, rind::AbstractArray, A::AbstractArray; init::Bool=true) - findminmax!(<, initarray!(rval, scalarmin, init), rind, A) + findminmax!(isless, init && !isempty(A) ? fill!(rval, first(A)) : rval, fill!(rind,0), A) end """ findmin(A, region) -> (minval, index) For an array input, returns the value and index of the minimum over the given region. +`NaN` is treated as less than all other values. # Examples ```jldoctest -julia> A = [1 2; 3 4] +julia> A = [1.0 2; 3 4] 2×2 Array{Int64,2}: - 1 2 - 3 4 + 1.0 2.0 + 3.0 4.0 julia> findmin(A, 1) -([1 2], [1 3]) +([1.0 2.0], [1 3]) julia> findmin(A, 2) -([1; 3], [1; 2]) +([1.0; 3.0], [1; 2]) ``` """ function findmin(A::AbstractArray{T}, region) where T + ri = reduced_indices0(A, region) if isempty(A) - return (similar(A, reduced_indices0(A, region)), - similar(dims->zeros(Int, dims), reduced_indices0(A, region))) + if prod(map(length, reduced_indices(A, region))) != 0 + throw(ArgumentError("collection slices must be non-empty")) + end + (similar(A, ri), similar(dims->zeros(Int, dims), ri)) + else + findminmax!(isless, fill!(similar(A, ri), first(A)), + similar(dims->zeros(Int, dims), ri), A) end - return findminmax!(<, reducedim_initarray0(A, region, typemax(T)), - similar(dims->zeros(Int, dims), reduced_indices0(A, region)), A) end +isgreater(a, b) = isless(b,a) + """ findmax!(rval, rind, A, [init=true]) -> (maxval, index) Find the maximum of `A` and the corresponding linear index along singleton dimensions of `rval` and `rind`, and store the results in `rval` and `rind`. +`NaN` is treated as greater than all other values. """ function findmax!(rval::AbstractArray, rind::AbstractArray, A::AbstractArray; init::Bool=true) - findminmax!(>, initarray!(rval, scalarmax, init), rind, A) + findminmax!(isgreater, init && !isempty(A) ? fill!(rval, first(A)) : rval, fill!(rind,0), A) end """ findmax(A, region) -> (maxval, index) For an array input, returns the value and index of the maximum over the given region. +`NaN` is treated as greater than all other values. # Examples ```jldoctest -julia> A = [1 2; 3 4] +julia> A = [1.0 2; 3 4] 2×2 Array{Int64,2}: - 1 2 - 3 4 + 1.0 2.0 + 3.0 4.0 julia> findmax(A,1) -([3 4], [2 4]) +([3.0 4.0], [2 4]) julia> findmax(A,2) -([2; 4], [3; 4]) +([2.0; 4.0], [3; 4]) ``` """ function findmax(A::AbstractArray{T}, region) where T + ri = reduced_indices0(A, region) if isempty(A) - return (similar(A, reduced_indices0(A,region)), - similar(dims->zeros(Int, dims), reduced_indices0(A,region))) + if prod(map(length, reduced_indices(A, region))) != 0 + throw(ArgumentError("collection slices must be non-empty")) + end + similar(A, ri), similar(dims->zeros(Int, dims), ri) + else + findminmax!(isgreater, fill!(similar(A, ri), first(A)), + similar(dims->zeros(Int, dims), ri), A) end - return findminmax!(>, reducedim_initarray0(A, region, typemin(T)), - similar(dims->zeros(Int, dims), reduced_indices0(A, region)), A) end reducedim1(R, A) = length(indices1(R)) == 1 diff --git a/base/sparse/sparsematrix.jl b/base/sparse/sparsematrix.jl index c3304d34ad347..2f071aa586dd0 100644 --- a/base/sparse/sparsematrix.jl +++ b/base/sparse/sparsematrix.jl @@ -52,7 +52,6 @@ getrowval(S::SparseMatrixCSCView) = S.parent.rowval getnzval( S::SparseMatrixCSC) = S.nzval getnzval( S::SparseMatrixCSCView) = S.parent.nzval - """ nnz(A) @@ -1274,7 +1273,6 @@ julia> dropzeros(A) """ dropzeros(A::SparseMatrixCSC, trim::Bool = true) = dropzeros!(copy(A), trim) - ## Find methods function find(S::SparseMatrixCSC) @@ -1332,7 +1330,6 @@ function findnz(S::SparseMatrixCSC{Tv,Ti}) where {Tv,Ti} return (I, J, V) end - import Base.Random.GLOBAL_RNG function sprand_IJ(r::AbstractRNG, m::Integer, n::Integer, density::AbstractFloat) ((m < 0) || (n < 0)) && throw(ArgumentError("invalid Array dimensions")) @@ -1424,7 +1421,6 @@ sprand(r::AbstractRNG, ::Type{T}, m::Integer, n::Integer, density::AbstractFloat sprand(r::AbstractRNG, ::Type{Bool}, m::Integer, n::Integer, density::AbstractFloat) = sprand(r,m,n,density, truebools, Bool) sprand(::Type{T}, m::Integer, n::Integer, density::AbstractFloat) where {T} = sprand(GLOBAL_RNG, T, m, n, density) - """ sprandn([rng], m[,n],p::AbstractFloat) @@ -1447,7 +1443,6 @@ julia> sprandn(rng, 2, 2, 0.75) sprandn(r::AbstractRNG, m::Integer, n::Integer, density::AbstractFloat) = sprand(r,m,n,density,randn,Float64) sprandn(m::Integer, n::Integer, density::AbstractFloat) = sprandn(GLOBAL_RNG,m,n,density) - """ spones(S) @@ -1570,7 +1565,6 @@ end sparse(S::UniformScaling, m::Integer, n::Integer=m) = speye_scaled(S.λ, m, n) - # TODO: More appropriate location? conj!(A::SparseMatrixCSC) = (@inbounds broadcast!(conj, A.nzval, A.nzval); A) (-)(A::SparseMatrixCSC) = SparseMatrixCSC(A.m, A.n, copy(A.colptr), copy(A.rowval), map(-, A.nzval)) @@ -1813,27 +1807,28 @@ function _mapreducecols!(f, op::typeof(+), R::AbstractArray, A::SparseMatrixCSC{ end # findmax/min and indmax/min methods +# find first zero value in sparse matrix - return linear index in full matrix +# non-structural zeros are identified by x == 0 in line with the sparse constructors. function _findz(A::SparseMatrixCSC{Tv,Ti}, rows=1:A.m, cols=1:A.n) where {Tv,Ti} colptr = A.colptr; rowval = A.rowval; nzval = A.nzval - zval = zero(Tv) - col = cols[1]; row = 0 + zval = 0 + row = 0 rowmin = rows[1]; rowmax = rows[end] allrows = (rows == 1:A.m) - @inbounds while col <= cols[end] + @inbounds for col in cols r1::Int = colptr[col] r2::Int = colptr[col+1] - 1 if !allrows && (r1 <= r2) r1 = searchsortedfirst(rowval, rowmin, r1, r2, Forward) - (r1 <= r2 ) && (r2 = searchsortedlast(rowval, rowmax, r1, r2, Forward) + 1) + (r1 <= r2 ) && (r2 = searchsortedlast(rowval, rowmax, r1, r2, Forward)) end row = rowmin - (r1 > r2) && (return sub2ind(size(A),row,col)) + while (r1 <= r2) && (row == rowval[r1]) && (nzval[r1] != zval) r1 += 1 row += 1 end - (row <= rowmax) && (return sub2ind(size(A),row,col)) - col += 1 + (row <= rowmax) && (return sub2ind(size(A), row, col)) end return 0 end @@ -1842,7 +1837,14 @@ macro _findr(op, A, region, Tv, Ti) esc(quote N = nnz($A) L = length($A) - (L == 0) && error("array must be non-empty") + if L == 0 + if prod(map(length, Base.reduced_indices($A, $region))) != 0 + throw(ArgumentError("array slices must be non-empty")) + else + ri = Base.reduced_indices0($A, $region) + return (similar($A, ri), similar(dims->zeros(Int, dims), ri)) + end + end colptr = $A.colptr; rowval = $A.rowval; nzval = $A.nzval; m = $A.m; n = $A.n zval = zero($Tv) @@ -1903,8 +1905,11 @@ macro _findr(op, A, region, Tv, Ti) end) #quote end -findmin(A::SparseMatrixCSC{Tv,Ti}, region) where {Tv,Ti} = @_findr(<, A, region, Tv, Ti) -findmax(A::SparseMatrixCSC{Tv,Ti}, region) where {Tv,Ti} = @_findr(>, A, region, Tv, Ti) +_isless_fm(a, b) = b == b && ( a != a || isless(a, b) ) +_isgreater_fm(a, b) = b == b && ( a != a || isless(b, a) ) + +findmin(A::SparseMatrixCSC{Tv,Ti}, region) where {Tv,Ti} = @_findr(_isless_fm, A, region, Tv, Ti) +findmax(A::SparseMatrixCSC{Tv,Ti}, region) where {Tv,Ti} = @_findr(_isgreater_fm, A, region, Tv, Ti) findmin(A::SparseMatrixCSC) = (r=findmin(A,(1,2)); (r[1][1], r[2][1])) findmax(A::SparseMatrixCSC) = (r=findmax(A,(1,2)); (r[1][1], r[2][1])) @@ -2548,7 +2553,6 @@ setindex!(A::SparseMatrixCSC, v::AbstractVector, i::Integer, J::AbstractVector{< setindex!(A::SparseMatrixCSC, v::AbstractVector, I::AbstractVector{T}, J::AbstractVector{T}) where {T<:Integer} = setindex!(A, reshape(v, length(I), length(J)), I, J) - # A[I,J] = B function setindex!(A::SparseMatrixCSC{Tv,Ti}, B::SparseMatrixCSC{Tv,Ti}, I::AbstractVector{T}, J::AbstractVector{T}) where {Tv,Ti,T<:Integer} if size(B,1) != length(I) || size(B,2) != length(J) @@ -2790,7 +2794,6 @@ function setindex!(A::SparseMatrixCSC, x, I::AbstractMatrix{Bool}) A end - function setindex!(A::SparseMatrixCSC, x, I::AbstractVector{<:Real}) n = length(I) (n == 0) && (return A) @@ -3028,7 +3031,6 @@ dropstored!(A::SparseMatrixCSC, ::Colon) = dropstored!(A, :, :) # TODO: Implement linear indexing methods for dropstored! ? # TODO: Implement logical indexing methods for dropstored! ? - # Sparse concatenation function vcat(X::SparseMatrixCSC...) @@ -3086,7 +3088,6 @@ end end end - function hcat(X::SparseMatrixCSC...) num = length(X) mX = Int[ size(x, 1) for x in X ] diff --git a/test/arrayops.jl b/test/arrayops.jl index f138b68133790..0fe17e2561213 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -42,7 +42,6 @@ using TestHelpers.OAs @test isequal([1,2,5] .<< [1,2,5], [2,8,160]) @test isequal([10,20,50] .>> [1,2,5], [5,5,1]) - a = ones(2,2) a[1,1] = 1 a[1,2] = 2 @@ -486,12 +485,12 @@ end @testset "findmin findmax indmin indmax" begin @test indmax([10,12,9,11]) == 2 @test indmin([10,12,9,11]) == 3 - @test findmin([NaN,3.2,1.8]) == (1.8,3) - @test findmax([NaN,3.2,1.8]) == (3.2,2) - @test findmin([NaN,3.2,1.8,NaN]) == (1.8,3) - @test findmax([NaN,3.2,1.8,NaN]) == (3.2,2) - @test findmin([3.2,1.8,NaN,2.0]) == (1.8,2) - @test findmax([3.2,1.8,NaN,2.0]) == (3.2,1) + @test findmin([NaN,3.2,1.8]) === (NaN,1) + @test findmax([NaN,3.2,1.8]) === (NaN,1) + @test findmin([NaN,3.2,1.8,NaN]) === (NaN,1) + @test findmax([NaN,3.2,1.8,NaN]) === (NaN,1) + @test findmin([3.2,1.8,NaN,2.0]) === (NaN,3) + @test findmax([3.2,1.8,NaN,2.0]) === (NaN,3) #14085 @test findmax(4:9) == (9,6) @@ -639,7 +638,6 @@ end @test_throws MethodError repmat(1, 2, 3) @test_throws MethodError repmat([1, 2], 1, 2, 3) - R = repeat([1, 2]) @test R == [1, 2] R = repeat([1, 2], inner=1) @@ -1161,7 +1159,6 @@ end @test isempty(eoa) end - @testset "deleteat!" begin for idx in Any[1, 2, 5, 9, 10, 1:0, 2:1, 1:1, 2:2, 1:2, 2:4, 9:8, 10:9, 9:9, 10:10, 8:9, 9:10, 6:9, 7:10] @@ -1638,7 +1635,6 @@ R = CartesianRange((3,0)) @test @inferred(eachindex(zeros(3),view(zeros(3,3),1:2,1:2),zeros(2,2,2),zeros(2,2))) == CartesianRange((3,2,2)) @test @inferred(eachindex(zeros(3),zeros(2,2),zeros(2,2,2),zeros(2,2))) == 1:8 - @testset "rotates" begin a = [1 0 0; 0 0 0] @test rotr90(a,1) == [0 1; 0 0; 0 0] @@ -1929,7 +1925,6 @@ let f = OOB_Functor([1,2]) @test_throws BoundsError map(f, [1,2,3,4,5]) end - # issue 15654 @test cumprod([5], 2) == [5] @test cumprod([1 2; 3 4], 3) == [1 2; 3 4] diff --git a/test/reducedim.jl b/test/reducedim.jl index e5d6fb1fcdd54..d5cf35feeb003 100644 --- a/test/reducedim.jl +++ b/test/reducedim.jl @@ -117,22 +117,201 @@ R = reducedim((a,b) -> a+b, [1 2; 3 4], 2, 0.0) rt = Base.return_types(reducedim, Tuple{Function, Array{Float64, 3}, Int, Float64}) @test length(rt) == 1 && rt[1] == Array{Float64, 3} +@testset "empty cases" begin + A = Array{Int}(0,1) + @test sum(A) === 0 + @test prod(A) === 1 + @test_throws ArgumentError minimum(A) + @test_throws ArgumentError maximum(A) + @test var(A) === NaN -## findmin/findmax -A = [1.0 3.0 6.0; + @test isequal(sum(A, 1), zeros(Int, 1, 1)) + @test isequal(sum(A, 2), zeros(Int, 0, 1)) + @test isequal(sum(A, (1, 2)), zeros(Int, 1, 1)) + @test isequal(sum(A, 3), zeros(Int, 0, 1)) + @test isequal(prod(A, 1), ones(Int, 1, 1)) + @test isequal(prod(A, 2), ones(Int, 0, 1)) + @test isequal(prod(A, (1, 2)), ones(Int, 1, 1)) + @test isequal(prod(A, 3), ones(Int, 0, 1)) + @test isequal(var(A, 1), fill(NaN, 1, 1)) + @test isequal(var(A, 2), fill(NaN, 0, 1)) + @test isequal(var(A, (1, 2)), fill(NaN, 1, 1)) + @test isequal(var(A, 3), fill(NaN, 0, 1)) + + for f in (minimum, maximum) + @test_throws ArgumentError f(A, 1) + @test isequal(f(A, 2), zeros(Int, 0, 1)) + @test_throws ArgumentError f(A, (1, 2)) + @test isequal(f(A, 3), zeros(Int, 0, 1)) + end + for f in (findmin, findmax) + @test_throws ArgumentError f(A, 1) + @test isequal(f(A, 2), (zeros(Int, 0, 1), zeros(Int, 0, 1))) + @test_throws ArgumentError f(A, (1, 2)) + @test isequal(f(A, 3), (zeros(Int, 0, 1), zeros(Int, 0, 1))) + end + +end +## findmin/findmax/minumum/maximum + +A = [1.0 5.0 6.0; 5.0 2.0 4.0] for (tup, rval, rind) in [((1,), [1.0 2.0 4.0], [1 4 6]), ((2,), reshape([1.0,2.0], 2, 1), reshape([1,4], 2, 1)), ((1,2), fill(1.0,1,1),fill(1,1,1))] @test findmin(A, tup) == (rval, rind) @test findmin!(similar(rval), similar(rind), A) == (rval, rind) + @test isequal(minimum(A, tup), rval) + @test isequal(minimum!(similar(rval), A), rval) + @test isequal(minimum!(copy(rval), A, init=false), rval) end -for (tup, rval, rind) in [((1,), [5.0 3.0 6.0], [2 3 5]), +for (tup, rval, rind) in [((1,), [5.0 5.0 6.0], [2 3 5]), ((2,), reshape([6.0,5.0], 2, 1), reshape([5,2], 2, 1)), ((1,2), fill(6.0,1,1),fill(5,1,1))] @test findmax(A, tup) == (rval, rind) @test findmax!(similar(rval), similar(rind), A) == (rval, rind) + @test isequal(maximum(A, tup), rval) + @test isequal(maximum!(similar(rval), A), rval) + @test isequal(maximum!(copy(rval), A, init=false), rval) +end + +#issue #23209 + +A = [1.0 3.0 6.0; + NaN 2.0 4.0] +for (tup, rval, rind) in [((1,), [NaN 2.0 4.0], [2 4 6]), + ((2,), reshape([1.0, NaN], 2, 1), reshape([1,2], 2, 1)), + ((1,2), fill(NaN,1,1),fill(2,1,1))] + @test isequal(findmin(A, tup), (rval, rind)) + @test isequal(findmin!(similar(rval), similar(rind), A), (rval, rind)) + @test isequal(minimum(A, tup), rval) + @test isequal(minimum!(similar(rval), A), rval) + @test isequal(minimum!(copy(rval), A, init=false), rval) + @test isequal(Base.reducedim!(min, copy(rval), A), rval) +end + +for (tup, rval, rind) in [((1,), [NaN 3.0 6.0], [2 3 5]), + ((2,), reshape([6.0, NaN], 2, 1), reshape([5,2], 2, 1)), + ((1,2), fill(NaN,1,1),fill(2,1,1))] + @test isequal(findmax(A, tup), (rval, rind)) + @test isequal(findmax!(similar(rval), similar(rind), A), (rval, rind)) + @test isequal(maximum(A, tup), rval) + @test isequal(maximum!(similar(rval), A), rval) + @test isequal(maximum!(copy(rval), A, init=false), rval) + @test isequal(Base.reducedim!(max, copy(rval), A), rval) +end + +A = [1.0 NaN 6.0; + NaN 2.0 4.0] +for (tup, rval, rind) in [((1,), [NaN NaN 4.0], [2 3 6]), + ((2,), reshape([NaN, NaN], 2, 1), reshape([3,2], 2, 1)), + ((1,2), fill(NaN,1,1),fill(2,1,1))] + @test isequal(findmin(A, tup), (rval, rind)) + @test isequal(findmin!(similar(rval), similar(rind), A), (rval, rind)) + @test isequal(minimum(A, tup), rval) + @test isequal(minimum!(similar(rval), A), rval) + @test isequal(minimum!(copy(rval), A, init=false), rval) +end + +for (tup, rval, rind) in [((1,), [NaN NaN 6.0], [2 3 5]), + ((2,), reshape([NaN, NaN], 2, 1), reshape([3,2], 2, 1)), + ((1,2), fill(NaN,1,1),fill(2,1,1))] + @test isequal(findmax(A, tup), (rval, rind)) + @test isequal(findmax!(similar(rval), similar(rind), A), (rval, rind)) + @test isequal(maximum(A, tup), rval) + @test isequal(maximum!(similar(rval), A), rval) + @test isequal(maximum!(copy(rval), A, init=false), rval) +end + +A = [Inf -Inf Inf -Inf; + Inf Inf -Inf -Inf] +for (tup, rval, rind) in [((1,), [Inf -Inf -Inf -Inf], [1 3 6 7]), + ((2,), reshape([-Inf -Inf], 2, 1), reshape([3,6], 2, 1)), + ((1,2), fill(-Inf,1,1),fill(3,1,1))] + @test isequal(findmin(A, tup), (rval, rind)) + @test isequal(findmin!(similar(rval), similar(rind), A), (rval, rind)) + @test isequal(minimum(A, tup), rval) + @test isequal(minimum!(similar(rval), A), rval) + @test isequal(minimum!(copy(rval), A, init=false), rval) +end + +for (tup, rval, rind) in [((1,), [Inf Inf Inf -Inf], [1 4 5 7]), + ((2,), reshape([Inf Inf], 2, 1), reshape([1,2], 2, 1)), + ((1,2), fill(Inf,1,1),fill(1,1,1))] + @test isequal(findmax(A, tup), (rval, rind)) + @test isequal(findmax!(similar(rval), similar(rind), A), (rval, rind)) + @test isequal(maximum(A, tup), rval) + @test isequal(maximum!(similar(rval), A), rval) + @test isequal(maximum!(copy(rval), A, init=false), rval) +end + +A = [BigInt(10)] +for (tup, rval, rind) in [((2,), [BigInt(10)], [1])] + @test isequal(findmin(A, tup), (rval, rind)) + @test isequal(findmin!(similar(rval), similar(rind), A), (rval, rind)) + @test isequal(minimum(A, tup), rval) + @test isequal(minimum!(similar(rval), A), rval) + @test isequal(minimum!(copy(rval), A, init=false), rval) +end + +for (tup, rval, rind) in [((2,), [BigInt(10)], [1])] + @test isequal(findmax(A, tup), (rval, rind)) + @test isequal(findmax!(similar(rval), similar(rind), A), (rval, rind)) + @test isequal(maximum(A, tup), rval) + @test isequal(maximum!(similar(rval), A), rval) + @test isequal(maximum!(copy(rval), A, init=false), rval) +end + +A = [BigInt(-10)] +for (tup, rval, rind) in [((2,), [BigInt(-10)], [1])] + @test isequal(findmin(A, tup), (rval, rind)) + @test isequal(findmin!(similar(rval), similar(rind), A), (rval, rind)) + @test isequal(minimum(A, tup), rval) + @test isequal(minimum!(similar(rval), A), rval) + @test isequal(minimum!(copy(rval), A, init=false), rval) +end + +for (tup, rval, rind) in [((2,), [BigInt(-10)], [1])] + @test isequal(findmax(A, tup), (rval, rind)) + @test isequal(findmax!(similar(rval), similar(rind), A), (rval, rind)) + @test isequal(maximum(A, tup), rval) + @test isequal(maximum!(similar(rval), A), rval) + @test isequal(maximum!(copy(rval), A, init=false), rval) +end + +A = [BigInt(10) BigInt(-10)] +for (tup, rval, rind) in [((2,), reshape([BigInt(-10)], 1, 1), reshape([2], 1,1))] + @test isequal(findmin(A, tup), (rval, rind)) + @test isequal(findmin!(similar(rval), similar(rind), A), (rval, rind)) + @test isequal(minimum(A, tup), rval) + @test isequal(minimum!(similar(rval), A), rval) + @test isequal(minimum!(copy(rval), A, init=false), rval) +end + +for (tup, rval, rind) in [((2,), reshape([BigInt(10)], 1, 1), reshape([1], 1, 1))] + @test isequal(findmax(A, tup), (rval, rind)) + @test isequal(findmax!(similar(rval), similar(rind), A), (rval, rind)) + @test isequal(maximum(A, tup), rval) + @test isequal(maximum!(similar(rval), A), rval) + @test isequal(maximum!(copy(rval), A, init=false), rval) +end + +A = ["a", "b"] +for (tup, rval, rind) in [((1,), ["a"], [1])] + @test isequal(findmin(A, tup), (rval, rind)) + @test isequal(findmin!(similar(rval), similar(rind), A), (rval, rind)) + @test isequal(minimum(A, tup), rval) + @test isequal(minimum!(similar(rval), A), rval) + @test isequal(minimum!(copy(rval), A, init=false), rval) +end + +for (tup, rval, rind) in [((1,), ["b"], [2])] + @test isequal(findmax(A, tup), (rval, rind)) + @test isequal(findmax!(similar(rval), similar(rind), A), (rval, rind)) + @test isequal(maximum(A, tup), rval) + @test isequal(maximum!(similar(rval), A), rval) + @test isequal(maximum!(copy(rval), A, init=false), rval) end # issue #6672 diff --git a/test/sparse/sparse.jl b/test/sparse/sparse.jl index 600fe519ebc33..a52dcf5638973 100644 --- a/test/sparse/sparse.jl +++ b/test/sparse/sparse.jl @@ -466,12 +466,18 @@ end @test_throws ArgumentError maximum(sparse(Int[])) @test var(sparse(Int[])) === NaN - for f in (sum, prod, minimum, maximum, var) + for f in (sum, prod, var) @test isequal(f(spzeros(0, 1), 1), f(Array{Int}(0, 1), 1)) @test isequal(f(spzeros(0, 1), 2), f(Array{Int}(0, 1), 2)) @test isequal(f(spzeros(0, 1), (1, 2)), f(Array{Int}(0, 1), (1, 2))) @test isequal(f(spzeros(0, 1), 3), f(Array{Int}(0, 1), 3)) end + for f in (minimum, maximum, findmin, findmax) + @test_throws ArgumentError f(spzeros(0, 1), 1) + @test isequal(f(spzeros(0, 1), 2), f(Array{Int}(0,1), 2)) + @test_throws ArgumentError f(spzeros(0, 1), (1, 2)) + @test isequal(f(spzeros(0, 1), 3), f(Array{Int}(0,1), 3)) + end end end @@ -1005,6 +1011,107 @@ end @test iA === iS === nothing end +# findmin/findmax/minumum/maximum + +A = sparse([1.0 5.0 6.0; + 5.0 2.0 4.0]) +for (tup, rval, rind) in [((1,), [1.0 2.0 4.0], [1 4 6]), + ((2,), reshape([1.0,2.0], 2, 1), reshape([1,4], 2, 1)), + ((1,2), fill(1.0,1,1),fill(1,1,1))] + @test findmin(A, tup) == (rval, rind) +end + +for (tup, rval, rind) in [((1,), [5.0 5.0 6.0], [2 3 5]), + ((2,), reshape([6.0,5.0], 2, 1), reshape([5,2], 2, 1)), + ((1,2), fill(6.0,1,1),fill(5,1,1))] + @test findmax(A, tup) == (rval, rind) +end + +#issue 23209 + +A = sparse([1.0 5.0 6.0; + NaN 2.0 4.0]) +for (tup, rval, rind) in [((1,), [NaN 2.0 4.0], [2 4 6]), + ((2,), reshape([1.0, NaN], 2, 1), reshape([1,2], 2, 1)), + ((1,2), fill(NaN,1,1),fill(2,1,1))] + @test isequal(findmin(A, tup), (rval, rind)) +end + +for (tup, rval, rind) in [((1,), [NaN 5.0 6.0], [2 3 5]), + ((2,), reshape([6.0, NaN], 2, 1), reshape([5,2], 2, 1)), + ((1,2), fill(NaN,1,1),fill(2,1,1))] + @test isequal(findmax(A, tup), (rval, rind)) +end + +A = sparse([1.0 NaN 6.0; + NaN 2.0 4.0]) +for (tup, rval, rind) in [((1,), [NaN NaN 4.0], [2 3 6]), + ((2,), reshape([NaN, NaN], 2, 1), reshape([3,2], 2, 1)), + ((1,2), fill(NaN,1,1),fill(2,1,1))] + @test isequal(findmin(A, tup), (rval, rind)) +end + +for (tup, rval, rind) in [((1,), [NaN NaN 6.0], [2 3 5]), + ((2,), reshape([NaN, NaN], 2, 1), reshape([3,2], 2, 1)), + ((1,2), fill(NaN,1,1),fill(2,1,1))] + @test isequal(findmax(A, tup), (rval, rind)) +end + +A = sparse([Inf -Inf Inf -Inf; + Inf Inf -Inf -Inf]) +for (tup, rval, rind) in [((1,), [Inf -Inf -Inf -Inf], [1 3 6 7]), + ((2,), reshape([-Inf -Inf], 2, 1), reshape([3,6], 2, 1)), + ((1,2), fill(-Inf,1,1),fill(3,1,1))] + @test isequal(findmin(A, tup), (rval, rind)) +end + +for (tup, rval, rind) in [((1,), [Inf Inf Inf -Inf], [1 4 5 7]), + ((2,), reshape([Inf Inf], 2, 1), reshape([1,2], 2, 1)), + ((1,2), fill(Inf,1,1),fill(1,1,1))] + @test isequal(findmax(A, tup), (rval, rind)) +end + +A = sparse([BigInt(10)]) +for (tup, rval, rind) in [((2,), [BigInt(10)], [1])] + @test isequal(findmin(A, tup), (rval, rind)) +end + +for (tup, rval, rind) in [((2,), [BigInt(10)], [1])] + @test isequal(findmax(A, tup), (rval, rind)) +end + +A = sparse([BigInt(-10)]) +for (tup, rval, rind) in [((2,), [BigInt(-10)], [1])] + @test isequal(findmin(A, tup), (rval, rind)) +end + +for (tup, rval, rind) in [((2,), [BigInt(-10)], [1])] + @test isequal(findmax(A, tup), (rval, rind)) +end + +A = sparse([BigInt(10) BigInt(-10)]) +for (tup, rval, rind) in [((2,), reshape([BigInt(-10)], 1, 1), reshape([2], 1, 1))] + @test isequal(findmin(A, tup), (rval, rind)) +end + +for (tup, rval, rind) in [((2,), reshape([BigInt(10)], 1, 1), reshape([1], 1, 1))] + @test isequal(findmax(A, tup), (rval, rind)) +end + +A = sparse(["a", "b"]) +@test_throws MethodError findmin(A, 1) + +# Support the case, when user defined `zero` and `isless` for non-numerical type +# +Base.zero(::Type{T}) where T<:AbstractString = "" +for (tup, rval, rind) in [((1,), ["a"], [1])] + @test isequal(findmin(A, tup), (rval, rind)) +end + +for (tup, rval, rind) in [((1,), ["b"], [2])] + @test isequal(findmax(A, tup), (rval, rind)) +end + @testset "findn" begin b = findn( speye(4) ) @test (length(b[1]) == 4) From c64a6cc16188cfde28a020703b26a9bc6537855d Mon Sep 17 00:00:00 2001 From: Alex Arslan Date: Thu, 31 Aug 2017 10:16:39 -0700 Subject: [PATCH 221/324] Deprecate rol and ror in favor of circshift methods (#23404) --- NEWS.md | 4 ++ base/abstractarraymath.jl | 29 +++++++- base/bitarray.jl | 141 +++----------------------------------- base/deprecated.jl | 7 ++ base/exports.jl | 4 -- base/multidimensional.jl | 2 +- doc/src/stdlib/arrays.md | 4 -- test/bitarray.jl | 15 ++-- 8 files changed, 56 insertions(+), 150 deletions(-) diff --git a/NEWS.md b/NEWS.md index e2380328156cc..de9af92bb9b4e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -372,6 +372,9 @@ Deprecated or removed * `filter` and `filter!` on dictionaries now pass a single `key=>value` pair to the argument function, instead of two arguments ([#17886]). + * `rol`, `rol!`, `ror`, and `ror!` have been deprecated in favor of specialized methods for + `circshift`/`circshift!` ([#23404]). + * `Base.SparseArrays.SpDiagIterator` has been removed ([#23261]). * The tuple-of-types form of `cfunction`, `cfunction(f, returntype, (types...))`, has been deprecated @@ -1269,3 +1272,4 @@ Command-line option changes [#23207]: https://github.com/JuliaLang/julia/issues/23207 [#23233]: https://github.com/JuliaLang/julia/issues/23233 [#23342]: https://github.com/JuliaLang/julia/issues/23342 +[#23404]: https://github.com/JuliaLang/julia/issues/23404 diff --git a/base/abstractarraymath.jl b/base/abstractarraymath.jl index 437fa54204943..a95c81a807fb9 100644 --- a/base/abstractarraymath.jl +++ b/base/abstractarraymath.jl @@ -178,8 +178,9 @@ circshift(a::AbstractArray, shiftamt::DimsInteger) = circshift!(similar(a), a, s """ circshift(A, shifts) -Circularly shift the data in an array. The second argument is a vector giving the amount to -shift in each dimension. +Circularly shift, i.e. rotate, the data in an array. The second argument is a tuple or +vector giving the amount to shift in each dimension, or an integer to shift only in the +first dimension. # Examples ```jldoctest @@ -203,6 +204,30 @@ julia> circshift(b, (-1,0)) 3 7 11 15 4 8 12 16 1 5 9 13 + +julia> a = BitArray([true, true, false, false, true]) +5-element BitArray{1}: + true + true + false + false + true + +julia> circshift(a, 1) +5-element BitArray{1}: + true + true + true + false + false + +julia> circshift(a, -1) +5-element BitArray{1}: + true + false + false + true + true ``` See also [`circshift!`](@ref). diff --git a/base/bitarray.jl b/base/bitarray.jl index 509cc70547711..71c2702a34d59 100644 --- a/base/bitarray.jl +++ b/base/bitarray.jl @@ -1478,145 +1478,24 @@ details and examples. """ (>>>)(B::BitVector, i::Int) = (i >=0 ? B >> unsigned(i) : B << unsigned(-i)) -""" - rol!(dest::BitVector, src::BitVector, i::Integer) -> BitVector - -Performs a left rotation operation on `src` and puts the result into `dest`. -`i` controls how far to rotate the bits. -""" -function rol!(dest::BitVector, src::BitVector, i::Integer) - length(dest) == length(src) || throw(ArgumentError("destination and source should be of same size")) - n = length(dest) - i %= n - i == 0 && return (src === dest ? src : copy!(dest, src)) - i < 0 && return ror!(dest, src, -i) - Bc = (src === dest ? copy(src.chunks) : src.chunks) - copy_chunks!(dest.chunks, 1, Bc, i+1, n-i) - copy_chunks!(dest.chunks, n-i+1, Bc, 1, i) - return dest -end - -""" - rol!(B::BitVector, i::Integer) -> BitVector - -Performs a left rotation operation in-place on `B`. -`i` controls how far to rotate the bits. -""" -rol!(B::BitVector, i::Integer) = rol!(B, B, i) - -""" - rol(B::BitVector, i::Integer) -> BitVector - -Performs a left rotation operation, returning a new `BitVector`. -`i` controls how far to rotate the bits. -See also [`rol!`](@ref). - -# Examples -```jldoctest -julia> A = BitArray([true, true, false, false, true]) -5-element BitArray{1}: - true - true - false - false - true - -julia> rol(A,1) -5-element BitArray{1}: - true - false - false - true - true - -julia> rol(A,2) -5-element BitArray{1}: - false - false - true - true - true - -julia> rol(A,5) -5-element BitArray{1}: - true - true - false - false - true -``` -""" -rol(B::BitVector, i::Integer) = rol!(similar(B), B, i) - -""" - ror!(dest::BitVector, src::BitVector, i::Integer) -> BitVector - -Performs a right rotation operation on `src` and puts the result into `dest`. -`i` controls how far to rotate the bits. -""" -function ror!(dest::BitVector, src::BitVector, i::Integer) +function circshift!(dest::BitVector, src::BitVector, i::Integer) length(dest) == length(src) || throw(ArgumentError("destination and source should be of same size")) n = length(dest) i %= n i == 0 && return (src === dest ? src : copy!(dest, src)) - i < 0 && return rol!(dest, src, -i) Bc = (src === dest ? copy(src.chunks) : src.chunks) - copy_chunks!(dest.chunks, i+1, Bc, 1, n-i) - copy_chunks!(dest.chunks, 1, Bc, n-i+1, i) + if i > 0 # right + copy_chunks!(dest.chunks, i+1, Bc, 1, n-i) + copy_chunks!(dest.chunks, 1, Bc, n-i+1, i) + else # left + i = -i + copy_chunks!(dest.chunks, 1, Bc, i+1, n-i) + copy_chunks!(dest.chunks, n-i+1, Bc, 1, i) + end return dest end -""" - ror!(B::BitVector, i::Integer) -> BitVector - -Performs a right rotation operation in-place on `B`. -`i` controls how far to rotate the bits. -""" -ror!(B::BitVector, i::Integer) = ror!(B, B, i) - -""" - ror(B::BitVector, i::Integer) -> BitVector - -Performs a right rotation operation on `B`, returning a new `BitVector`. -`i` controls how far to rotate the bits. -See also [`ror!`](@ref). - -# Examples -```jldoctest -julia> A = BitArray([true, true, false, false, true]) -5-element BitArray{1}: - true - true - false - false - true - -julia> ror(A,1) -5-element BitArray{1}: - true - true - true - false - false - -julia> ror(A,2) -5-element BitArray{1}: - false - true - true - true - false - -julia> ror(A,5) -5-element BitArray{1}: - true - true - false - false - true -``` -""" -ror(B::BitVector, i::Integer) = ror!(similar(B), B, i) +circshift!(B::BitVector, i::Integer) = circshift!(B, B, i) ## count & find ## diff --git a/base/deprecated.jl b/base/deprecated.jl index e47d6f581ff9d..853b5890489fd 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1668,6 +1668,13 @@ export hex2num @deprecate convert(::Type{String}, v::Vector{UInt8}) String(v) @deprecate convert(::Type{S}, g::UTF8proc.GraphemeIterator) where {S<:AbstractString} convert(S, g.s) +# Issue #19923 +@deprecate ror circshift +@deprecate ror! circshift! +@deprecate rol(B, i) circshift(B, -i) +@deprecate rol!(dest, src, i) circshift!(dest, src, -i) +@deprecate rol!(B, i) circshift!(B, -i) + # issue #5148, PR #23259 # warning for `const` on locals should be changed to an error in julia-syntax.scm diff --git a/base/exports.jl b/base/exports.jl index 62ef78c2c5d38..775191eeb747f 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -638,10 +638,6 @@ export # bitarrays falses, flipbits!, - rol, - rol!, - ror, - ror!, trues, # dequeues diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 331329c1d2ec5..2988daf2beddc 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -933,7 +933,7 @@ circshift!(dest::AbstractArray, src, ::Tuple{}) = copy!(dest, src) """ circshift!(dest, src, shifts) -Circularly shift the data in `src`, storing the result in +Circularly shift, i.e. rotate, the data in `src`, storing the result in `dest`. `shifts` specifies the amount to shift in each dimension. The `dest` array must be distinct from the `src` array (they cannot diff --git a/doc/src/stdlib/arrays.md b/doc/src/stdlib/arrays.md index 9dd0ce3e7053b..7c46de37df2b4 100644 --- a/doc/src/stdlib/arrays.md +++ b/doc/src/stdlib/arrays.md @@ -178,10 +178,6 @@ and can be converted to/from the latter via `Array(bitarray)` and `BitArray(arra ```@docs Base.flipbits! -Base.rol! -Base.rol -Base.ror! -Base.ror ``` ## [Sparse Vectors and Matrices](@id stdlib-sparse-arrays) diff --git a/test/bitarray.jl b/test/bitarray.jl index e8863fdfdc852..fc349bf888ec0 100644 --- a/test/bitarray.jl +++ b/test/bitarray.jl @@ -1040,21 +1040,20 @@ timesofar("binary comparison") @test isequal(b1 >>> m, [ falses(m); b1[1:end-m] ]) @test isequal(b1 << -m, b1 >> m) @test isequal(b1 >>> -m, b1 << m) - @test isequal(rol(b1, m), [ b1[m+1:end]; b1[1:m] ]) - @test isequal(ror(b1, m), [ b1[end-m+1:end]; b1[1:end-m] ]) - @test isequal(ror(b1, m), rol(b1, -m)) - @test isequal(rol(b1, m), ror(b1, -m)) + @test isequal(circshift(b1, -m), [ b1[m+1:end]; b1[1:m] ]) + @test isequal(circshift(b1, m), [ b1[end-m+1:end]; b1[1:end-m] ]) + @test isequal(circshift(b1, m), circshift(b1, m - length(b1))) end b = bitrand(v1) i = bitrand(v1) for m = [rand(1:v1), 63, 64, 65, 191, 192, 193, v1-1] j = rand(1:m) - b1 = ror!(i, b, j) - i1 = ror!(b, j) + b1 = circshift!(i, b, j) + i1 = circshift!(b, j) @test b1 == i1 - b2 = rol!(i1, b1, j) - i2 = rol!(b1, j) + b2 = circshift!(i1, b1, -j) + i2 = circshift!(b1, -j) @test b2 == i2 end end From 3488679364a6b74c74bc4f7cfcbc9981dd86ef73 Mon Sep 17 00:00:00 2001 From: Sacha Verweij Date: Thu, 31 Aug 2017 10:46:17 -0700 Subject: [PATCH 222/324] Deprecate writecsv in favor of writedlm. --- NEWS.md | 3 +++ base/datafmt.jl | 9 +-------- base/deprecated.jl | 3 +++ base/exports.jl | 1 - doc/src/stdlib/io-network.md | 1 - test/perf/kernel/perf.jl | 2 +- 6 files changed, 8 insertions(+), 11 deletions(-) diff --git a/NEWS.md b/NEWS.md index de9af92bb9b4e..71ec255a09640 100644 --- a/NEWS.md +++ b/NEWS.md @@ -266,6 +266,9 @@ Deprecated or removed * The keyword `immutable` is fully deprecated to `struct`, and `type` is fully deprecated to `mutable struct` ([#19157], [#20418]). + * `writecsv(io, a; opts...)` has been deprecated in favor of + `writedlm(io, a, ','; opts...)` ([#23529]). + * The method `srand(rng, filename, n=4)` has been deprecated ([#21359]). * The `cholfact`/`cholfact!` methods that accepted an `uplo` symbol have been deprecated diff --git a/base/datafmt.jl b/base/datafmt.jl index 573350853bbfd..04525ae9ad158 100644 --- a/base/datafmt.jl +++ b/base/datafmt.jl @@ -6,7 +6,7 @@ module DataFmt import Base: _default_delims, tryparse_internal, show -export countlines, readdlm, readcsv, writedlm, writecsv +export countlines, readdlm, readcsv, writedlm invalid_dlm(::Type{Char}) = reinterpret(Char, 0xfffffffe) invalid_dlm(::Type{UInt8}) = 0xfe @@ -703,13 +703,6 @@ tab-delimited text to `f` by either `writedlm(f, [x y])` or by `writedlm(f, zip( """ writedlm(io, a; opts...) = writedlm(io, a, '\t'; opts...) -""" - writecsv(filename, A; opts) - -Equivalent to [`writedlm`](@ref) with `delim` set to comma. -""" -writecsv(io, a; opts...) = writedlm(io, a, ','; opts...) - show(io::IO, ::MIME"text/csv", a) = writedlm(io, a, ',') show(io::IO, ::MIME"text/tab-separated-values", a) = writedlm(io, a, '\t') diff --git a/base/deprecated.jl b/base/deprecated.jl index 853b5890489fd..81842b65dc337 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1707,6 +1707,9 @@ export hex2num @deprecate_binding φ MathConstants.φ @deprecate_binding golden MathConstants.golden +# deprecate writecsv +@deprecate writecsv(io, a; opts...) writedlm(io, a, ','; opts...) + # PR #23271 function IOContext(io::IO; kws...) depwarn("IOContext(io, k=v, ...) is deprecated, use IOContext(io, :k => v, ...) instead.", :IOContext) diff --git a/base/exports.jl b/base/exports.jl index 775191eeb747f..b13d073b4a135 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -1074,7 +1074,6 @@ export unmark, watch_file, write, - writecsv, writedlm, TCPSocket, UDPSocket, diff --git a/doc/src/stdlib/io-network.md b/doc/src/stdlib/io-network.md index 6b750b7cad0e5..931cb3b55bc0d 100644 --- a/doc/src/stdlib/io-network.md +++ b/doc/src/stdlib/io-network.md @@ -84,7 +84,6 @@ Base.DataFmt.readdlm(::Any, ::Type) Base.DataFmt.readdlm(::Any) Base.DataFmt.writedlm Base.DataFmt.readcsv -Base.DataFmt.writecsv Base.Base64.Base64EncodePipe Base.Base64.Base64DecodePipe Base.Base64.base64encode diff --git a/test/perf/kernel/perf.jl b/test/perf/kernel/perf.jl index cf539d1f628ba..25cb76ae4a961 100644 --- a/test/perf/kernel/perf.jl +++ b/test/perf/kernel/perf.jl @@ -102,7 +102,7 @@ d = randn(len) @timeit (for n in 1:10; a = arith_vectorized(b,c,d); end) "vectorize" "Vectorized arithmetic" -writecsv("random.csv", rand(100000,4)) +writedlm("random.csv", rand(100000, 4), ',') function parsecsv() for line in eachline("random.csv") From 383b91954d70e33bf69f1db1734825ea0e5cc912 Mon Sep 17 00:00:00 2001 From: Sacha Verweij Date: Thu, 31 Aug 2017 10:59:45 -0700 Subject: [PATCH 223/324] Deprecate logm in favor of log. (#23505) --- NEWS.md | 2 ++ base/deprecated.jl | 9 ++++++--- base/exports.jl | 1 - base/linalg/dense.jl | 23 +++++++++-------------- base/linalg/diagonal.jl | 3 +-- base/linalg/linalg.jl | 3 +-- base/linalg/symmetric.jl | 35 ++++++++++++++++++++++++++++++++++- base/linalg/triangular.jl | 8 ++++---- doc/src/stdlib/linalg.md | 1 - test/linalg/dense.jl | 28 +++++++++++++--------------- test/linalg/diagonal.jl | 4 ++-- test/linalg/symmetric.jl | 8 ++++---- test/linalg/triangular.jl | 4 ++-- 13 files changed, 78 insertions(+), 51 deletions(-) diff --git a/NEWS.md b/NEWS.md index de9af92bb9b4e..19dd81a117fed 100644 --- a/NEWS.md +++ b/NEWS.md @@ -357,6 +357,8 @@ Deprecated or removed * `expm` has been deprecated in favor of `exp` ([#23233]). + * `logm` has been deprecated in favor of `log` ([#23505]). + * Calling `union` with no arguments is deprecated; construct an empty set with an appropriate element type using `Set{T}()` instead ([#23144]). diff --git a/base/deprecated.jl b/base/deprecated.jl index 853b5890489fd..d20e243f921ba 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -220,7 +220,7 @@ for f in (:sin, :sinh, :sind, :asin, :asinh, :asind, :tan, :tanh, :tand, :atan, :atanh, :atand, :sinpi, :cosc, :ceil, :floor, :trunc, :round, :log1p, :expm1, :abs, :abs2, - :log, :log2, :log10, :exp2, :exp10, :sinc, :cospi, + :log2, :log10, :exp2, :exp10, :sinc, :cospi, :cos, :cosh, :cosd, :acos, :acosd, :cot, :coth, :cotd, :acot, :acotd, :sec, :sech, :secd, :asech, @@ -247,13 +247,13 @@ for f in ( # base/special/trig.jl :sinpi, :cospi, :sinc, :cosc, # base/special/log.jl - :log, :log1p, + :log1p, # base/special/gamma.jl :gamma, :lfact, # base/math.jl :cbrt, :sinh, :cosh, :tanh, :atan, :asinh, :exp2, :expm1, :exp10, :sin, :cos, :tan, :asin, :acos, :acosh, :atanh, - #=:log,=# :log2, :log10, :lgamma, #=:log1p,=# :sqrt, + :log2, :log10, :lgamma, #=:log1p,=# :sqrt, # base/floatfuncs.jl :abs, :abs2, :angle, :isnan, :isinf, :isfinite, # base/complex.jl @@ -1624,6 +1624,9 @@ end @deprecate expm! exp! @deprecate expm exp +# deprecate logm in favor of log +@deprecate logm log + # PR #23092 @eval LibGit2 begin function prompt(msg::AbstractString; default::AbstractString="", password::Bool=false) diff --git a/base/exports.jl b/base/exports.jl index 775191eeb747f..75607641a48f8 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -586,7 +586,6 @@ export linreg, logabsdet, logdet, - logm, lu, lufact!, lufact, diff --git a/base/linalg/dense.jl b/base/linalg/dense.jl index 5504aa217e868..3298f0c42c737 100644 --- a/base/linalg/dense.jl +++ b/base/linalg/dense.jl @@ -427,7 +427,7 @@ function (^)(A::AbstractMatrix{T}, p::Real) where T # Otherwise, use Schur decomposition return schurpow(A, p) end -(^)(A::AbstractMatrix, p::Number) = exp(p*logm(A)) +(^)(A::AbstractMatrix, p::Number) = exp(p*log(A)) # Matrix exponential @@ -557,7 +557,7 @@ function rcswap!(i::Integer, j::Integer, X::StridedMatrix{<:Number}) end """ - logm(A{T}::StridedMatrix{T}) + log(A{T}::StridedMatrix{T}) If `A` has no negative real eigenvalue, compute the principal matrix logarithm of `A`, i.e. the unique matrix ``X`` such that ``e^X = A`` and ``-\\pi < Im(\\lambda) < \\pi`` for all @@ -581,25 +581,25 @@ julia> A = 2.7182818 * eye(2) 2.71828 0.0 0.0 2.71828 -julia> logm(A) +julia> log(A) 2×2 Symmetric{Float64,Array{Float64,2}}: 1.0 0.0 0.0 1.0 ``` """ -function logm(A::StridedMatrix{T}) where T +function log(A::StridedMatrix{T}) where T # If possible, use diagonalization if issymmetric(A) && T <: Real - return logm(Symmetric(A)) + return log(Symmetric(A)) end if ishermitian(A) - return logm(Hermitian(A)) + return log(Hermitian(A)) end # Use Schur decomposition n = checksquare(A) if istriu(A) - return full(logm(UpperTriangular(complex(A)))) + return full(log(UpperTriangular(complex(A)))) else if isreal(A) SchurF = schurfact(real(A)) @@ -608,19 +608,14 @@ function logm(A::StridedMatrix{T}) where T end if !istriu(SchurF.T) SchurS = schurfact(complex(SchurF.T)) - logT = SchurS.Z * logm(UpperTriangular(SchurS.T)) * SchurS.Z' + logT = SchurS.Z * log(UpperTriangular(SchurS.T)) * SchurS.Z' return SchurF.Z * logT * SchurF.Z' else - R = logm(UpperTriangular(complex(SchurF.T))) + R = log(UpperTriangular(complex(SchurF.T))) return SchurF.Z * R * SchurF.Z' end end end -function logm(a::Number) - b = log(complex(a)) - return imag(b) == 0 ? real(b) : b -end -logm(a::Complex) = log(a) """ sqrtm(A) diff --git a/base/linalg/diagonal.jl b/base/linalg/diagonal.jl index 7161d20450b6c..beac1cf844cbd 100644 --- a/base/linalg/diagonal.jl +++ b/base/linalg/diagonal.jl @@ -327,8 +327,7 @@ eye(::Type{Diagonal{T}}, n::Int) where {T} = Diagonal(ones(T,n)) # Matrix functions exp(D::Diagonal) = Diagonal(exp.(D.diag)) -logm(D::Diagonal) = Diagonal(log.(D.diag)) -logm(D::Diagonal{<:AbstractMatrix}) = Diagonal(logm.(D.diag)) +log(D::Diagonal) = Diagonal(log.(D.diag)) sqrtm(D::Diagonal) = Diagonal(sqrt.(D.diag)) sqrtm(D::Diagonal{<:AbstractMatrix}) = Diagonal(sqrtm.(D.diag)) diff --git a/base/linalg/linalg.jl b/base/linalg/linalg.jl index 6eaea00de1b4d..46025dd8d2999 100644 --- a/base/linalg/linalg.jl +++ b/base/linalg/linalg.jl @@ -7,7 +7,7 @@ import Base: A_mul_Bt, At_ldiv_Bt, A_rdiv_Bc, At_ldiv_B, Ac_mul_Bc, A_mul_Bc, Ac Ac_ldiv_B, Ac_ldiv_Bc, At_mul_Bt, A_rdiv_Bt, At_mul_B import Base: USE_BLAS64, abs, big, broadcast, ceil, conj, convert, copy, copy!, adjoint, eltype, exp, eye, findmax, findmin, fill!, floor, full, getindex, - hcat, imag, indices, inv, isapprox, isone, IndexStyle, kron, length, map, + hcat, imag, indices, inv, isapprox, isone, IndexStyle, kron, length, log, map, ndims, oneunit, parent, power_by_squaring, print_matrix, promote_rule, real, round, setindex!, show, similar, size, transpose, trunc, typed_hcat using Base: hvcat_fill, iszero, IndexLinear, _length, promote_op, promote_typeof, @@ -101,7 +101,6 @@ export linreg, logabsdet, logdet, - logm, lu, lufact, lufact!, diff --git a/base/linalg/symmetric.jl b/base/linalg/symmetric.jl index 92fe036dbf94f..99d95ba112ab6 100644 --- a/base/linalg/symmetric.jl +++ b/base/linalg/symmetric.jl @@ -607,7 +607,7 @@ function exp(A::Hermitian{T}) where T end end -for (funm, func) in ([:logm,:log], [:sqrtm,:sqrt]) +for (funm, func) in ([:sqrtm,:sqrt],) @eval begin function ($funm)(A::Symmetric{T}) where T<:Real F = eigfact(A) @@ -639,3 +639,36 @@ for (funm, func) in ([:logm,:log], [:sqrtm,:sqrt]) end end end + +for func in (:log, #=:sqrtm=#) + @eval begin + function ($func)(A::Symmetric{T}) where T<:Real + F = eigfact(A) + if all(λ -> λ ≥ 0, F.values) + retmat = (F.vectors * Diagonal(($func).(F.values))) * F.vectors' + else + retmat = (F.vectors * Diagonal(($func).(complex.(F.values)))) * F.vectors' + end + return Symmetric(retmat) + end + + function ($func)(A::Hermitian{T}) where T + n = checksquare(A) + F = eigfact(A) + if all(λ -> λ ≥ 0, F.values) + retmat = (F.vectors * Diagonal(($func).(F.values))) * F.vectors' + if T <: Real + return Hermitian(retmat) + else + for i = 1:n + retmat[i,i] = real(retmat[i,i]) + end + return Hermitian(retmat) + end + else + retmat = (F.vectors * Diagonal(($func).(complex(F.values)))) * F.vectors' + return retmat + end + end + end +end diff --git a/base/linalg/triangular.jl b/base/linalg/triangular.jl index ccac60e9cfb7a..eb2af9ab869d5 100644 --- a/base/linalg/triangular.jl +++ b/base/linalg/triangular.jl @@ -1789,7 +1789,7 @@ powm(A::LowerTriangular, p::Real) = powm(A.', p::Real).' # Based on the code available at http://eprints.ma.man.ac.uk/1851/02/logm.zip, # Copyright (c) 2011, Awad H. Al-Mohy and Nicholas J. Higham # Julia version relicensed with permission from original authors -function logm(A0::UpperTriangular{T}) where T<:Union{Float64,Complex{Float64}} +function log(A0::UpperTriangular{T}) where T<:Union{Float64,Complex{Float64}} maxsqrt = 100 theta = [1.586970738772063e-005, 2.313807884242979e-003, @@ -1961,9 +1961,9 @@ function logm(A0::UpperTriangular{T}) where T<:Union{Float64,Complex{Float64}} return UpperTriangular(Y) end -logm(A::LowerTriangular) = logm(A.').' +log(A::LowerTriangular) = log(A.').' -# Auxiliary functions for logm and matrix power +# Auxiliary functions for matrix logarithm and matrix power # Compute accurate diagonal of A = A0^s - I # Al-Mohy, "A more accurate Briggs method for the logarithm", @@ -2117,7 +2117,7 @@ end unw(x::Real) = 0 unw(x::Number) = ceil((imag(x) - pi) / (2 * pi)) -# End of auxiliary functions for logm and matrix power +# End of auxiliary functions for matrix logarithm and matrix power function sqrtm(A::UpperTriangular) realmatrix = false diff --git a/doc/src/stdlib/linalg.md b/doc/src/stdlib/linalg.md index 66dd343d3f195..99c8c2f3d3412 100644 --- a/doc/src/stdlib/linalg.md +++ b/doc/src/stdlib/linalg.md @@ -92,7 +92,6 @@ Base.repmat Base.kron Base.SparseArrays.blkdiag Base.LinAlg.linreg -Base.LinAlg.logm Base.LinAlg.sqrtm Base.LinAlg.lyap Base.LinAlg.sylvester diff --git a/test/linalg/dense.jl b/test/linalg/dense.jl index 086ecff07452d..ba43ee24345b5 100644 --- a/test/linalg/dense.jl +++ b/test/linalg/dense.jl @@ -453,20 +453,20 @@ end 1/3 1/4 1/5 1/6; 1/4 1/5 1/6 1/7; 1/5 1/6 1/7 1/8]) - @test exp(logm(A4)) ≈ A4 + @test exp(log(A4)) ≈ A4 A5 = convert(Matrix{elty}, [1 1 0 1; 0 1 1 0; 0 0 1 1; 1 0 0 1]) - @test exp(logm(A5)) ≈ A5 + @test exp(log(A5)) ≈ A5 A6 = convert(Matrix{elty}, [-5 2 0 0 ; 1/2 -7 3 0; 0 1/3 -9 4; 0 0 1/4 -11]) - @test exp(logm(A6)) ≈ A6 + @test exp(log(A6)) ≈ A6 A7 = convert(Matrix{elty}, [1 0 0 1e-8; 0 1 0 0; 0 0 1 0; 0 0 0 1]) - @test exp(logm(A7)) ≈ A7 + @test exp(log(A7)) ≈ A7 end A8 = 100 * [-1+1im 0 0 1e-8; 0 1 0 0; 0 0 1 0; 0 0 0 1] - @test exp(logm(A8)) ≈ A8 + @test exp(log(A8)) ≈ A8 end @testset "issue 5116" begin @@ -487,28 +487,28 @@ end @testset "Additional matrix logarithm tests" for elty in (Float64, Complex{Float64}) A11 = convert(Matrix{elty}, [3 2; -5 -3]) - @test exp(logm(A11)) ≈ A11 + @test exp(log(A11)) ≈ A11 A12 = convert(Matrix{elty}, [1 -1; 1 -1]) - @test typeof(logm(A12)) == Array{Complex{Float64}, 2} + @test typeof(log(A12)) == Array{Complex{Float64}, 2} A1 = convert(Matrix{elty}, [4 2 0; 1 4 1; 1 1 4]) - logmA1 = convert(Matrix{elty}, [1.329661349 0.5302876358 -0.06818951543; + logA1 = convert(Matrix{elty}, [1.329661349 0.5302876358 -0.06818951543; 0.2310490602 1.295566591 0.2651438179; 0.2310490602 0.1969543025 1.363756107]) - @test logm(A1) ≈ logmA1 - @test exp(logm(A1)) ≈ A1 + @test log(A1) ≈ logA1 + @test exp(log(A1)) ≈ A1 A4 = convert(Matrix{elty}, [1/2 1/3 1/4 1/5+eps(); 1/3 1/4 1/5 1/6; 1/4 1/5 1/6 1/7; 1/5 1/6 1/7 1/8]) - logmA4 = convert(Matrix{elty}, [-1.73297159 1.857349738 0.4462766564 0.2414170219; + logA4 = convert(Matrix{elty}, [-1.73297159 1.857349738 0.4462766564 0.2414170219; 1.857349738 -5.335033737 2.994142974 0.5865285289; 0.4462766564 2.994142974 -7.351095988 3.318413247; 0.2414170219 0.5865285289 3.318413247 -5.444632124]) - @test logm(A4) ≈ logmA4 - @test exp(logm(A4)) ≈ A4 + @test log(A4) ≈ logA4 + @test exp(log(A4)) ≈ A4 end @testset "issue #7181" begin @@ -679,10 +679,8 @@ end @testset "test ops on Numbers for $elty" for elty in [Float32,Float64,Complex64,Complex128] a = rand(elty) - @test exp(a) == exp(a) @test isposdef(one(elty)) @test sqrtm(a) == sqrt(a) - @test logm(a) ≈ log(a) @test lyap(one(elty),a) == -a/2 end diff --git a/test/linalg/diagonal.jl b/test/linalg/diagonal.jl index 3ec850f125591..47b7eaf509194 100644 --- a/test/linalg/diagonal.jl +++ b/test/linalg/diagonal.jl @@ -63,7 +63,7 @@ srand(1) for func in (exp,) @test func(D) ≈ func(DM) atol=n^3*eps(relty) end - @test logm(Diagonal(abs.(D.diag))) ≈ logm(abs.(DM)) atol=n^3*eps(relty) + @test log(Diagonal(abs.(D.diag))) ≈ log(abs.(DM)) atol=n^3*eps(relty) end if elty <: BlasComplex for func in (logdet, sqrtm) @@ -382,7 +382,7 @@ end @test ishermitian(Dsym) == false @test exp(D) == Diagonal([exp([1 2; 3 4]), exp([1 2; 3 4])]) - @test logm(D) == Diagonal([logm([1 2; 3 4]), logm([1 2; 3 4])]) + @test log(D) == Diagonal([log([1 2; 3 4]), log([1 2; 3 4])]) @test sqrtm(D) == Diagonal([sqrtm([1 2; 3 4]), sqrtm([1 2; 3 4])]) end diff --git a/test/linalg/symmetric.jl b/test/linalg/symmetric.jl index 09b4370ed922c..d5d7164f80d63 100644 --- a/test/linalg/symmetric.jl +++ b/test/linalg/symmetric.jl @@ -13,17 +13,17 @@ end A1 = randn(4,4) + im*randn(4,4) A2 = A1 + A1' @test exp(A2) ≈ exp(Hermitian(A2)) - @test logm(A2) ≈ logm(Hermitian(A2)) + @test log(A2) ≈ log(Hermitian(A2)) A3 = A1 * A1' # posdef @test exp(A3) ≈ exp(Hermitian(A3)) - @test logm(A3) ≈ logm(Hermitian(A3)) + @test log(A3) ≈ log(Hermitian(A3)) A1 = randn(4,4) A3 = A1 * A1' A4 = A1 + A1.' @test exp(A4) ≈ exp(Symmetric(A4)) - @test logm(A3) ≈ logm(Symmetric(A3)) - @test logm(A3) ≈ logm(Hermitian(A3)) + @test log(A3) ≈ log(Symmetric(A3)) + @test log(A3) ≈ log(Hermitian(A3)) end @testset "Core functionality" begin diff --git a/test/linalg/triangular.jl b/test/linalg/triangular.jl index eb0fc7cd57717..8589b530160ff 100644 --- a/test/linalg/triangular.jl +++ b/test/linalg/triangular.jl @@ -174,9 +174,9 @@ for elty1 in (Float32, Float64, BigFloat, Complex64, Complex128, Complex{BigFloa @test B == viewA1.' end - #exp/logm + #exp/log if (elty1 == Float64 || elty1 == Complex128) && (t1 == UpperTriangular || t1 == LowerTriangular) - @test exp(full(logm(A1))) ≈ full(A1) + @test exp(full(log(A1))) ≈ full(A1) end # scale From b729e58dff9c9cfa2640b8c4dcc71dbc91b29871 Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Thu, 31 Aug 2017 23:46:01 +0200 Subject: [PATCH 224/324] fix documentation for IOBuffer, close #23398 (#23514) --- base/iobuffer.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/iobuffer.jl b/base/iobuffer.jl index a379adfff0e25..15bcd54b1b788 100644 --- a/base/iobuffer.jl +++ b/base/iobuffer.jl @@ -36,8 +36,8 @@ StringVector(n::Integer) = Vector{UInt8}(_string_n(n)) Create an `IOBuffer`, which may optionally operate on a pre-existing array. If the readable/writable arguments are given, they restrict whether or not the buffer may be read -from or written to respectively. By default the buffer is readable but not writable. The -last argument optionally specifies a size beyond which the buffer may not be grown. +from or written to respectively. The last argument optionally specifies a size beyond which +the buffer may not be grown. """ IOBuffer(data::AbstractVector{UInt8}, readable::Bool=true, writable::Bool=false, maxsize::Int=typemax(Int)) = GenericIOBuffer(data, readable, writable, true, false, maxsize) From 44fe78ceef725270f02b6fea1a81e91cf4fc027f Mon Sep 17 00:00:00 2001 From: Sacha Verweij Date: Thu, 31 Aug 2017 15:07:46 -0700 Subject: [PATCH 225/324] Deprecate sqrtm in favor of sqrt. (#23504) --- NEWS.md | 2 ++ base/deprecated.jl | 5 ++++- base/exports.jl | 1 - base/linalg/dense.jl | 30 +++++++++++++-------------- base/linalg/diagonal.jl | 3 +-- base/linalg/linalg.jl | 3 +-- base/linalg/symmetric.jl | 35 +------------------------------- base/linalg/triangular.jl | 20 +++++++++--------- doc/src/manual/linear-algebra.md | 4 ++-- doc/src/stdlib/linalg.md | 1 - test/linalg/dense.jl | 21 +++++++++---------- test/linalg/diagonal.jl | 4 ++-- test/linalg/triangular.jl | 12 +++++------ 13 files changed, 53 insertions(+), 88 deletions(-) diff --git a/NEWS.md b/NEWS.md index 19dd81a117fed..52a4503ba44d6 100644 --- a/NEWS.md +++ b/NEWS.md @@ -355,6 +355,8 @@ Deprecated or removed full path if you need access to executables or libraries in the `JULIA_HOME` directory, e.g. `joinpath(JULIA_HOME, "7z.exe")` for `7z.exe` ([#21540]). + * `sqrtm` has been deprecated in favor of `sqrt` ([#23504]). + * `expm` has been deprecated in favor of `exp` ([#23233]). * `logm` has been deprecated in favor of `log` ([#23505]). diff --git a/base/deprecated.jl b/base/deprecated.jl index d20e243f921ba..2dc1cec006b6f 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -253,7 +253,7 @@ for f in ( # base/math.jl :cbrt, :sinh, :cosh, :tanh, :atan, :asinh, :exp2, :expm1, :exp10, :sin, :cos, :tan, :asin, :acos, :acosh, :atanh, - :log2, :log10, :lgamma, #=:log1p,=# :sqrt, + :log2, :log10, :lgamma, #=:log1p,=# # base/floatfuncs.jl :abs, :abs2, :angle, :isnan, :isinf, :isfinite, # base/complex.jl @@ -1620,6 +1620,9 @@ function Tridiagonal(dl::AbstractVector{Tl}, d::AbstractVector{Td}, du::Abstract Tridiagonal(map(v->convert(Vector{promote_type(Tl,Td,Tu)}, v), (dl, d, du))...) end +# deprecate sqrtm in favor of sqrt +@deprecate sqrtm sqrt + # deprecate expm in favor of exp @deprecate expm! exp! @deprecate expm exp diff --git a/base/exports.jl b/base/exports.jl index 75607641a48f8..addd53d810f7f 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -609,7 +609,6 @@ export schur, schurfact!, schurfact, - sqrtm, svd, svdfact!, svdfact, diff --git a/base/linalg/dense.jl b/base/linalg/dense.jl index 3298f0c42c737..dcdd94b60ffd6 100644 --- a/base/linalg/dense.jl +++ b/base/linalg/dense.jl @@ -374,8 +374,8 @@ function schurpow(A::AbstractMatrix, p) retmat = A ^ floor(p) # Real part if p - floor(p) == 0.5 - # special case: A^0.5 === sqrtm(A) - retmat = retmat * sqrtm(A) + # special case: A^0.5 === sqrt(A) + retmat = retmat * sqrt(A) else retmat = retmat * powm!(UpperTriangular(float.(A)), real(p - floor(p))) end @@ -385,8 +385,8 @@ function schurpow(A::AbstractMatrix, p) R = S ^ floor(p) # Real part if p - floor(p) == 0.5 - # special case: A^0.5 === sqrtm(A) - R = R * sqrtm(S) + # special case: A^0.5 === sqrt(A) + R = R * sqrt(S) else R = R * powm!(UpperTriangular(float.(S)), real(p - floor(p))) end @@ -618,7 +618,7 @@ function log(A::StridedMatrix{T}) where T end """ - sqrtm(A) + sqrt(A::AbstractMatrix) If `A` has no negative real eigenvalues, compute the principal matrix square root of `A`, that is the unique matrix ``X`` with eigenvalues having positive real part such that @@ -642,40 +642,38 @@ julia> A = [4 0; 0 4] 4 0 0 4 -julia> sqrtm(A) +julia> sqrt(A) 2×2 Array{Float64,2}: 2.0 0.0 0.0 2.0 ``` """ -function sqrtm(A::StridedMatrix{<:Real}) +function sqrt(A::StridedMatrix{<:Real}) if issymmetric(A) - return full(sqrtm(Symmetric(A))) + return full(sqrt(Symmetric(A))) end n = checksquare(A) if istriu(A) - return full(sqrtm(UpperTriangular(A))) + return full(sqrt(UpperTriangular(A))) else SchurF = schurfact(complex(A)) - R = full(sqrtm(UpperTriangular(SchurF[:T]))) + R = full(sqrt(UpperTriangular(SchurF[:T]))) return SchurF[:vectors] * R * SchurF[:vectors]' end end -function sqrtm(A::StridedMatrix{<:Complex}) +function sqrt(A::StridedMatrix{<:Complex}) if ishermitian(A) - return full(sqrtm(Hermitian(A))) + return full(sqrt(Hermitian(A))) end n = checksquare(A) if istriu(A) - return full(sqrtm(UpperTriangular(A))) + return full(sqrt(UpperTriangular(A))) else SchurF = schurfact(A) - R = full(sqrtm(UpperTriangular(SchurF[:T]))) + R = full(sqrt(UpperTriangular(SchurF[:T]))) return SchurF[:vectors] * R * SchurF[:vectors]' end end -sqrtm(a::Number) = (b = sqrt(complex(a)); imag(b) == 0 ? real(b) : b) -sqrtm(a::Complex) = sqrt(a) function inv(A::StridedMatrix{T}) where T checksquare(A) diff --git a/base/linalg/diagonal.jl b/base/linalg/diagonal.jl index beac1cf844cbd..dfa8408594780 100644 --- a/base/linalg/diagonal.jl +++ b/base/linalg/diagonal.jl @@ -328,8 +328,7 @@ eye(::Type{Diagonal{T}}, n::Int) where {T} = Diagonal(ones(T,n)) # Matrix functions exp(D::Diagonal) = Diagonal(exp.(D.diag)) log(D::Diagonal) = Diagonal(log.(D.diag)) -sqrtm(D::Diagonal) = Diagonal(sqrt.(D.diag)) -sqrtm(D::Diagonal{<:AbstractMatrix}) = Diagonal(sqrtm.(D.diag)) +sqrt(D::Diagonal) = Diagonal(sqrt.(D.diag)) #Linear solver function A_ldiv_B!(D::Diagonal, B::StridedVecOrMat) diff --git a/base/linalg/linalg.jl b/base/linalg/linalg.jl index 46025dd8d2999..09816b9e0a73c 100644 --- a/base/linalg/linalg.jl +++ b/base/linalg/linalg.jl @@ -9,7 +9,7 @@ import Base: USE_BLAS64, abs, big, broadcast, ceil, conj, convert, copy, copy!, adjoint, eltype, exp, eye, findmax, findmin, fill!, floor, full, getindex, hcat, imag, indices, inv, isapprox, isone, IndexStyle, kron, length, log, map, ndims, oneunit, parent, power_by_squaring, print_matrix, promote_rule, real, round, - setindex!, show, similar, size, transpose, trunc, typed_hcat + setindex!, show, similar, size, sqrt, transpose, trunc, typed_hcat using Base: hvcat_fill, iszero, IndexLinear, _length, promote_op, promote_typeof, @propagate_inbounds, @pure, reduce, typed_vcat # We use `_length` because of non-1 indices; releases after julia 0.5 @@ -124,7 +124,6 @@ export schur, schurfact!, schurfact, - sqrtm, svd, svdfact!, svdfact, diff --git a/base/linalg/symmetric.jl b/base/linalg/symmetric.jl index 99d95ba112ab6..adf1c0762f71d 100644 --- a/base/linalg/symmetric.jl +++ b/base/linalg/symmetric.jl @@ -607,40 +607,7 @@ function exp(A::Hermitian{T}) where T end end -for (funm, func) in ([:sqrtm,:sqrt],) - @eval begin - function ($funm)(A::Symmetric{T}) where T<:Real - F = eigfact(A) - if all(λ -> λ ≥ 0, F.values) - retmat = (F.vectors * Diagonal(($func).(F.values))) * F.vectors' - else - retmat = (F.vectors * Diagonal(($func).(complex.(F.values)))) * F.vectors' - end - return Symmetric(retmat) - end - - function ($funm)(A::Hermitian{T}) where T - n = checksquare(A) - F = eigfact(A) - if all(λ -> λ ≥ 0, F.values) - retmat = (F.vectors * Diagonal(($func).(F.values))) * F.vectors' - if T <: Real - return Hermitian(retmat) - else - for i = 1:n - retmat[i,i] = real(retmat[i,i]) - end - return Hermitian(retmat) - end - else - retmat = (F.vectors * Diagonal(($func).(complex(F.values)))) * F.vectors' - return retmat - end - end - end -end - -for func in (:log, #=:sqrtm=#) +for func in (:log, :sqrt) @eval begin function ($func)(A::Symmetric{T}) where T<:Real F = eigfact(A) diff --git a/base/linalg/triangular.jl b/base/linalg/triangular.jl index eb2af9ab869d5..df46c9239931d 100644 --- a/base/linalg/triangular.jl +++ b/base/linalg/triangular.jl @@ -1815,7 +1815,7 @@ function log(A0::UpperTriangular{T}) where T<:Union{Float64,Complex{Float64}} end s0 = s for k = 1:min(s, maxsqrt) - A = sqrtm(A) + A = sqrt(A) end AmI = A - I @@ -1871,7 +1871,7 @@ function log(A0::UpperTriangular{T}) where T<:Union{Float64,Complex{Float64}} m = tmax break end - A = sqrtm(A) + A = sqrt(A) AmI = A - I s = s + 1 end @@ -2015,7 +2015,7 @@ function invsquaring(A0::UpperTriangular, theta) end s0 = s for k = 1:min(s, maxsqrt) - A = sqrtm(A) + A = sqrt(A) end AmI = A - I @@ -2073,7 +2073,7 @@ function invsquaring(A0::UpperTriangular, theta) m = tmax break end - A = sqrtm(A) + A = sqrt(A) AmI = A - I s = s + 1 end @@ -2119,7 +2119,7 @@ unw(x::Number) = ceil((imag(x) - pi) / (2 * pi)) # End of auxiliary functions for matrix logarithm and matrix power -function sqrtm(A::UpperTriangular) +function sqrt(A::UpperTriangular) realmatrix = false if isreal(A) realmatrix = true @@ -2131,9 +2131,9 @@ function sqrtm(A::UpperTriangular) end end end - sqrtm(A,Val(realmatrix)) + sqrt(A,Val(realmatrix)) end -function sqrtm(A::UpperTriangular{T},::Val{realmatrix}) where {T,realmatrix} +function sqrt(A::UpperTriangular{T},::Val{realmatrix}) where {T,realmatrix} B = A.data n = checksquare(B) t = realmatrix ? typeof(sqrt(zero(T))) : typeof(sqrt(complex(zero(T)))) @@ -2151,7 +2151,7 @@ function sqrtm(A::UpperTriangular{T},::Val{realmatrix}) where {T,realmatrix} end return UpperTriangular(R) end -function sqrtm(A::UnitUpperTriangular{T}) where T +function sqrt(A::UnitUpperTriangular{T}) where T B = A.data n = checksquare(B) t = typeof(sqrt(zero(T))) @@ -2169,8 +2169,8 @@ function sqrtm(A::UnitUpperTriangular{T}) where T end return UnitUpperTriangular(R) end -sqrtm(A::LowerTriangular) = sqrtm(A.').' -sqrtm(A::UnitLowerTriangular) = sqrtm(A.').' +sqrt(A::LowerTriangular) = sqrt(A.').' +sqrt(A::UnitLowerTriangular) = sqrt(A.').' # Generic eigensystems eigvals(A::AbstractTriangular) = diag(A) diff --git a/doc/src/manual/linear-algebra.md b/doc/src/manual/linear-algebra.md index 3a0fb0fb4e6cc..7c86c389c0cb4 100644 --- a/doc/src/manual/linear-algebra.md +++ b/doc/src/manual/linear-algebra.md @@ -177,8 +177,8 @@ as well as whether hooks to various optimized methods for them in LAPACK are ava | Matrix type | `+` | `-` | `*` | `\` | Other functions with optimized methods | |:------------------------- |:--- |:--- |:--- |:--- |:------------------------------------------------------------------- | -| [`Symmetric`](@ref) | | | | MV | [`inv()`](@ref), [`sqrtm()`](@ref), [`exp()`](@ref) | -| [`Hermitian`](@ref) | | | | MV | [`inv()`](@ref), [`sqrtm()`](@ref), [`exp()`](@ref) | +| [`Symmetric`](@ref) | | | | MV | [`inv()`](@ref), [`sqrt()`](@ref), [`exp()`](@ref) | +| [`Hermitian`](@ref) | | | | MV | [`inv()`](@ref), [`sqrt()`](@ref), [`exp()`](@ref) | | [`UpperTriangular`](@ref) | | | MV | MV | [`inv()`](@ref), [`det()`](@ref) | | [`LowerTriangular`](@ref) | | | MV | MV | [`inv()`](@ref), [`det()`](@ref) | | [`SymTridiagonal`](@ref) | M | M | MS | MV | [`eigmax()`](@ref), [`eigmin()`](@ref) | diff --git a/doc/src/stdlib/linalg.md b/doc/src/stdlib/linalg.md index 99c8c2f3d3412..126d74954d4d2 100644 --- a/doc/src/stdlib/linalg.md +++ b/doc/src/stdlib/linalg.md @@ -92,7 +92,6 @@ Base.repmat Base.kron Base.SparseArrays.blkdiag Base.LinAlg.linreg -Base.LinAlg.sqrtm Base.LinAlg.lyap Base.LinAlg.sylvester Base.LinAlg.issuccess diff --git a/test/linalg/dense.jl b/test/linalg/dense.jl index ba43ee24345b5..ea313bc8f7579 100644 --- a/test/linalg/dense.jl +++ b/test/linalg/dense.jl @@ -115,10 +115,10 @@ bimg = randn(n,2)/2 end @testset "Matrix square root" begin - asq = sqrtm(a) + asq = sqrt(a) @test asq*asq ≈ a asym = a'+a # symmetric indefinite - asymsq = sqrtm(asym) + asymsq = sqrt(asym) @test asymsq*asymsq ≈ asym end @@ -370,10 +370,10 @@ end @testset "issue #2246" begin A = [1 2 0 0; 0 1 0 0; 0 0 0 0; 0 0 0 0] - Asq = sqrtm(A) + Asq = sqrt(A) @test Asq*Asq ≈ A A2 = view(A, 1:2, 1:2) - A2sq = sqrtm(A2) + A2sq = sqrt(A2) @test A2sq*A2sq ≈ A2 N = 3 @@ -569,12 +569,12 @@ end end for A in (Aa, Ab, Ac, Ad, Ah, ADi) - @test A^(1/2) ≈ sqrtm(A) - @test A^(-1/2) ≈ inv(sqrtm(A)) - @test A^(3/4) ≈ sqrtm(A) * sqrtm(sqrtm(A)) - @test A^(-3/4) ≈ inv(A) * sqrtm(sqrtm(A)) - @test A^(17/8) ≈ A^2 * sqrtm(sqrtm(sqrtm(A))) - @test A^(-17/8) ≈ inv(A^2 * sqrtm(sqrtm(sqrtm(A)))) + @test A^(1/2) ≈ sqrt(A) + @test A^(-1/2) ≈ inv(sqrt(A)) + @test A^(3/4) ≈ sqrt(A) * sqrt(sqrt(A)) + @test A^(-3/4) ≈ inv(A) * sqrt(sqrt(A)) + @test A^(17/8) ≈ A^2 * sqrt(sqrt(sqrt(A))) + @test A^(-17/8) ≈ inv(A^2 * sqrt(sqrt(sqrt(A)))) @test (A^0.2)^5 ≈ A @test (A^(2/3))*(A^(1/3)) ≈ A @test (A^im)^(-im) ≈ A @@ -680,7 +680,6 @@ end @testset "test ops on Numbers for $elty" for elty in [Float32,Float64,Complex64,Complex128] a = rand(elty) @test isposdef(one(elty)) - @test sqrtm(a) == sqrt(a) @test lyap(one(elty),a) == -a/2 end diff --git a/test/linalg/diagonal.jl b/test/linalg/diagonal.jl index 47b7eaf509194..4d490c2d6e281 100644 --- a/test/linalg/diagonal.jl +++ b/test/linalg/diagonal.jl @@ -66,7 +66,7 @@ srand(1) @test log(Diagonal(abs.(D.diag))) ≈ log(abs.(DM)) atol=n^3*eps(relty) end if elty <: BlasComplex - for func in (logdet, sqrtm) + for func in (logdet, sqrt) @test func(D) ≈ func(DM) atol=n^2*eps(relty)*2 end end @@ -383,7 +383,7 @@ end @test exp(D) == Diagonal([exp([1 2; 3 4]), exp([1 2; 3 4])]) @test log(D) == Diagonal([log([1 2; 3 4]), log([1 2; 3 4])]) - @test sqrtm(D) == Diagonal([sqrtm([1 2; 3 4]), sqrtm([1 2; 3 4])]) + @test sqrt(D) == Diagonal([sqrt([1 2; 3 4]), sqrt([1 2; 3 4])]) end @testset "multiplication with Symmetric/Hermitian" begin diff --git a/test/linalg/triangular.jl b/test/linalg/triangular.jl index 8589b530160ff..ff03cbcd2f26c 100644 --- a/test/linalg/triangular.jl +++ b/test/linalg/triangular.jl @@ -237,7 +237,7 @@ for elty1 in (Float32, Float64, BigFloat, Complex64, Complex128, Complex{BigFloa @test ladb ≈ fladb atol=sqrt(eps(real(float(one(elty1)))))*n*n # Matrix square root - @test sqrtm(A1) |> t -> t*t ≈ A1 + @test sqrt(A1) |> t -> t*t ≈ A1 # naivesub errors @test_throws DimensionMismatch naivesub!(A1,ones(elty1,n+1)) @@ -391,10 +391,10 @@ end # Matrix square root Atn = UpperTriangular([-1 1 2; 0 -2 2; 0 0 -3]) Atp = UpperTriangular([1 1 2; 0 2 2; 0 0 3]) -@test sqrtm(Atn) |> t->t*t ≈ Atn -@test typeof(sqrtm(Atn)[1,1]) <: Complex -@test sqrtm(Atp) |> t->t*t ≈ Atp -@test typeof(sqrtm(Atp)[1,1]) <: Real +@test sqrt(Atn) |> t->t*t ≈ Atn +@test typeof(sqrt(Atn)[1,1]) <: Complex +@test sqrt(Atp) |> t->t*t ≈ Atp +@test typeof(sqrt(Atp)[1,1]) <: Real Areal = randn(n, n)/2 Aimg = randn(n, n)/2 @@ -511,5 +511,5 @@ end isdefined(Main, :TestHelpers) || @eval Main include("../TestHelpers.jl") using TestHelpers.Furlong let A = UpperTriangular([Furlong(1) Furlong(4); Furlong(0) Furlong(1)]) - @test sqrtm(A) == Furlong{1//2}.(UpperTriangular([1 2; 0 1])) + @test sqrt(A) == Furlong{1//2}.(UpperTriangular([1 2; 0 1])) end From e0ca7d0565179bd6226ed49fca539f9aa776399b Mon Sep 17 00:00:00 2001 From: kshyatt Date: Thu, 31 Aug 2017 18:16:08 -0700 Subject: [PATCH 226/324] Add docs for internal commit method --- base/libgit2/commit.jl | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/base/libgit2/commit.jl b/base/libgit2/commit.jl index 0cf8ca641076d..cd05299168456 100644 --- a/base/libgit2/commit.jl +++ b/base/libgit2/commit.jl @@ -50,7 +50,6 @@ function Base.show(io::IO, c::GitCommit) print(io, "Git Commit:\nCommit Author: $authstr\nCommitter: $cmtrstr\nSHA: $(GitHash(c))\nMessage:\n$(message(c))") end -""" Wrapper around `git_commit_create` """ function commit(repo::GitRepo, refname::AbstractString, msg::AbstractString, @@ -73,7 +72,25 @@ function commit(repo::GitRepo, return commit_id_ptr[] end -"""Commit changes to repository""" +""" + commit(repo::GitRepo, msg::AbstractString; kwargs...) -> GitHash + +Wrapper around [`git_commit_create`](https://libgit2.github.com/libgit2/#HEAD/group/commit/git_commit_create). +Create a commit in the repository `repo`. `msg` is the commit message. Return the OID of the new commit. + +The keyword arguments are: + * `refname::AbstractString=Consts.HEAD_FILE`: if not NULL, the name of the reference to update to point to + the new commit. For example, `"HEAD"` will update the HEAD of the current branch. If the reference does + not yet exist, it will be created. + * `author::Signature = Signature(repo)` is a `Signature` containing information about the person who authored the commit. + * `committer::Signature = Signature(repo)` is a `Signature` containing information about the person who commited the commit to + the repository. Not necessarily the same as `author`, for instance if `author` emailed a patch to + `committer` who committed it. + * `tree_id::GitHash = GitHash()` is a git tree to use to create the commit, showing its ancestry and relationship with + any other history. `tree` must belong to `repo`. + * `parent_ids::Vector{GitHash}=GitHash[]` is a list of commits by [`GitHash`](@ref) to use as parent + commits for the new one, and may be empty. A commit might have multiple parents if it is a merge commit, for example. +""" function commit(repo::GitRepo, msg::AbstractString; refname::AbstractString=Consts.HEAD_FILE, author::Signature = Signature(repo), From 99173bbfcfa16e9cd553b7a867ad041de31e50a8 Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Fri, 1 Sep 2017 16:07:51 +0200 Subject: [PATCH 227/324] =?UTF-8?q?clarification=20about=20=E2=84=AF=20(#2?= =?UTF-8?q?3534)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- NEWS.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index 52a4503ba44d6..3f14cd4f51368 100644 --- a/NEWS.md +++ b/NEWS.md @@ -389,8 +389,9 @@ Deprecated or removed * `diagm(A::BitMatrix)` has been deprecated, use `diagm(vec(A))` instead ([#23373]). - * `ℯ` (written as `\mscre` or `\euler`) is the new default for Euler's - number ([#23427]). + * `ℯ` (written as `\mscre` or `\euler`) is now the only (by default) exported + name for Euler's number, and the type has changed from `Irrational{:e}` to + `Irrational{:ℯ}` ([#23427]). * The mathematical constants `π`, `pi`, `ℯ`, `e`, `γ`, `eulergamma`, `catalan`, `φ` and `golden` have been have been moved from `Base` to a new module; `Base.MathConstants`. From 917e8af281aca137ad57ed5284163e9565cd8779 Mon Sep 17 00:00:00 2001 From: kshyatt Date: Fri, 1 Sep 2017 14:01:42 -0700 Subject: [PATCH 228/324] Add field docs for Diffopts --- base/libgit2/types.jl | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/base/libgit2/types.jl b/base/libgit2/types.jl index e7fb21bdd0e47..e38dacbb3ce6a 100644 --- a/base/libgit2/types.jl +++ b/base/libgit2/types.jl @@ -338,6 +338,32 @@ end LibGit2.DiffOptionsStruct Matches the [`git_diff_options`](https://libgit2.github.com/libgit2/#HEAD/type/git_diff_options) struct. + +The fields represent: + * `version`: version of the struct in use, in case this changes later. For now, always `1`. + * `flags`: flags controlling which files will appear in the diff. Defaults to `DIFF_NORMAL`. + * `ignore_submodules`: whether to look at files in submodules or not. Defaults to + `SUBMODULE_IGNORE_UNSPECIFIED`, which means the submodule's configuration will control + whether it appears in the diff or not. + * `pathspec`: path to files to include in the diff. Default is to use all files in the repository. + * `notify_cb`: optional callback which will notify the user of changes to the diff as file deltas are + added to it. + * `progress_cb`: optional callback which will display diff progress. Only relevant on libgit2 versions + at least as new as 0.24.0. + * `payload`: the payload to pass to `notify_cb` and `progress_cb`. + * `context_lines`: the number of *unchanged* lines used to define the edges of a hunk. + This is also the number of lines which will be shown before/after a hunk to provide + context. Default is 3. + * `interhunk_lines`: the maximum number of *unchanged* lines *between* two separate + hunks allowed before the hunks will be combined. Default is 0. + * `id_abbrev`: sets the length of the abbreviated [`GitHash`](@ref) to print. + Default is `7`. + * `max_size`: the maximum file size of a blob. Above this size, it will be treated + as a binary blob. The default is 512 MB. + * `old_prefix`: the virtual file directory in which to place old files on one side + of the diff. Default is `"a"`. + * `new_prefix`: the virtual file directory in which to place new files on one side + of the diff. Default is `"b"`. """ @kwdef struct DiffOptionsStruct version::Cuint = Consts.DIFF_OPTIONS_VERSION From e56400841abafefe3db7a16a55a7fed3e4ec2e31 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Fri, 1 Sep 2017 17:20:49 -0400 Subject: [PATCH 229/324] deprecate `&x` in ccall. fixes #6080 --- NEWS.md | 3 +++ src/julia-syntax.scm | 7 +++++++ test/ccall.jl | 1 - 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 3f14cd4f51368..a6a589f12e662 100644 --- a/NEWS.md +++ b/NEWS.md @@ -88,6 +88,9 @@ Language changes * Variable bindings local to `while` loop bodies are now freshly allocated on each loop iteration, matching the behavior of `for` loops. + * Prefix `&` for by-reference arguments to `ccall` has been deprecated in favor of + `Ref` argument types ([#6080]). + Breaking changes ---------------- diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index a17184d66ba44..67ec69f9669f9 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -3511,6 +3511,13 @@ f(x) = yt(x) ((call new foreigncall) (let* ((args (cond ((eq? (car e) 'foreigncall) + (for-each (lambda (a) + (if (and (length= a 2) (eq? (car a) '&)) + (deprecation-message + (string "Syntax \"&argument\"" (linenode-string current-loc) + " is deprecated. Remove the \"&\" and use a \"Ref\" argument " + "type instead.")))) + (list-tail e 6)) ;; NOTE: 2nd to 5th arguments of ccall must be left in place ;; the 1st should be compiled if an atom. (append (if (atom? (cadr e)) diff --git a/test/ccall.jl b/test/ccall.jl index 1e131f4da296a..f9c5d53a8d2ba 100644 --- a/test/ccall.jl +++ b/test/ccall.jl @@ -122,7 +122,6 @@ let a a = 2.84 + 5.2im @test_throws MethodError ccall((:cptest, libccalltest), Ptr{Complex{Int}}, (Ptr{Complex{Int}},), a) - @test_throws MethodError ccall((:cptest, libccalltest), Ptr{Complex{Int}}, (Complex{Int},), &a) end From d4a55532a82d126d51c7bfc2d5cba092d49d76c6 Mon Sep 17 00:00:00 2001 From: Christian Kurz Date: Sat, 2 Sep 2017 01:34:08 +0200 Subject: [PATCH 230/324] Cleaned up default descriptions in reflection.jl (#23543) * Cleaned up default descriptions in reflection.jl Redundant default descriptions were removed. In addtion, square brackets to denote optional arguments were replaced by default syntax: code_native([io], f, types, [syntax]) -> code_native(io=STDOUT, f, types, syntax=att) * Use `[io=STDOUT,]` to show default argument --- base/reflection.jl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/base/reflection.jl b/base/reflection.jl index 50e26e1cc040c..be5f2fc38cb95 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -809,10 +809,10 @@ function _dump_function_linfo(linfo::Core.MethodInstance, world::UInt, native::B end """ - code_llvm([io], f, types) + code_llvm([io=STDOUT,], f, types) Prints the LLVM bitcodes generated for running the method matching the given generic -function and type signature to `io` which defaults to `STDOUT`. +function and type signature to `io`. All metadata and dbg.* calls are removed from the printed bitcode. Use code_llvm_raw for the full IR. """ @@ -822,11 +822,11 @@ code_llvm(@nospecialize(f), @nospecialize(types=Tuple)) = code_llvm(STDOUT, f, t code_llvm_raw(@nospecialize(f), @nospecialize(types=Tuple)) = code_llvm(STDOUT, f, types, false) """ - code_native([io], f, types, [syntax]) + code_native([io=STDOUT,], f, types, syntax=:att) Prints the native assembly instructions generated for running the method matching the given -generic function and type signature to `io` which defaults to `STDOUT`. -Switch assembly syntax using `syntax` symbol parameter set to `:att` for AT&T syntax or `:intel` for Intel syntax. Output is AT&T syntax by default. +generic function and type signature to `io`. +Switch assembly syntax using `syntax` symbol parameter set to `:att` for AT&T syntax or `:intel` for Intel syntax. """ code_native(io::IO, @nospecialize(f), @nospecialize(types=Tuple), syntax::Symbol=:att) = print(io, _dump_function(f, types, true, false, false, false, syntax)) From fc54dc4c4f8d26b43ed07f258b5ac2eb4a6e094d Mon Sep 17 00:00:00 2001 From: Christian Kurz Date: Sat, 2 Sep 2017 01:35:58 +0200 Subject: [PATCH 231/324] update rationalize docs (#23541) Removed description of default for T. Was redundant with signature --- base/rational.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/base/rational.jl b/base/rational.jl index 87c0c2cb0cdd9..a7bfdd64d7de9 100644 --- a/base/rational.jl +++ b/base/rational.jl @@ -114,7 +114,6 @@ widen(::Type{Rational{T}}) where {T} = Rational{widen(T)} Approximate floating point number `x` as a [`Rational`](@ref) number with components of the given integer type. The result will differ from `x` by no more than `tol`. -If `T` is not provided, it defaults to `Int`. ```jldoctest julia> rationalize(5.6) From c9be80bd1e93791d47849ff2e86995e1d55d98c4 Mon Sep 17 00:00:00 2001 From: Joe Petviashvili Date: Fri, 1 Sep 2017 21:35:22 -0700 Subject: [PATCH 232/324] Typo: should be elseif instead of else --- base/libgit2/utils.jl | 2 +- base/sparse/sparsematrix.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/base/libgit2/utils.jl b/base/libgit2/utils.jl index 27e531776a9e2..24e640ebc057e 100644 --- a/base/libgit2/utils.jl +++ b/base/libgit2/utils.jl @@ -81,7 +81,7 @@ Standardise the path string `path` to use POSIX separators. function posixpath end if Sys.iswindows() posixpath(path) = replace(path,'\\','/') -else Sys.isunix() +elseif Sys.isunix() posixpath(path) = path end diff --git a/base/sparse/sparsematrix.jl b/base/sparse/sparsematrix.jl index 2f071aa586dd0..14a0d4be07f3e 100644 --- a/base/sparse/sparsematrix.jl +++ b/base/sparse/sparsematrix.jl @@ -2569,7 +2569,7 @@ function setindex!(A::SparseMatrixCSC{Tv,Ti}, B::SparseMatrixCSC{Tv,Ti}, I::Abst elseif !issortedI pI = sortperm(I); @inbounds I = I[pI] B = B[pI,:] - else !issortedJ + elseif !issortedJ pJ = sortperm(J); @inbounds J = J[pJ] B = B[:, pJ] end From a848012db593fdfb7f207ca438eb6a4b5b68c020 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Sat, 2 Sep 2017 09:32:59 +0200 Subject: [PATCH 233/324] fix StackOverflowError in symdiff(::IntSet, ::NotInteger) --- base/intset.jl | 6 ++++-- test/intset.jl | 4 ++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/base/intset.jl b/base/intset.jl index bbd2825256c3b..a83a27175aacc 100644 --- a/base/intset.jl +++ b/base/intset.jl @@ -155,13 +155,15 @@ symdiff(s::IntSet, ns) = symdiff!(copy(s), ns) For each element in `itr`, destructively toggle its inclusion in set `s`. """ -symdiff!(s::IntSet, ns) = (for n in ns; symdiff!(s, n); end; s) +symdiff!(s::IntSet, ns) = (for n in ns; int_symdiff!(s, n); end; s) """ symdiff!(s, n) The set `s` is destructively modified to toggle the inclusion of integer `n`. """ -function symdiff!(s::IntSet, n::Integer) +symdiff!(s::IntSet, n::Integer) = int_symdiff!(s, n) + +function int_symdiff!(s::IntSet, n::Integer) 0 < n < typemax(Int) || _throw_intset_bounds_err() val = !(n in s) _setint!(s, n, val) diff --git a/test/intset.jl b/test/intset.jl index 735f53ca53559..f927ea2cc6271 100644 --- a/test/intset.jl +++ b/test/intset.jl @@ -90,6 +90,10 @@ end # issue #23099 : these tests should not segfault @test_throws ArgumentError symdiff!(IntSet(rand(1:100, 30)), 0) @test_throws ArgumentError symdiff!(IntSet(rand(1:100, 30)), [0, 2, 4]) + + # issue #23557 : + @test_throws MethodError symdiff!(IntSet([1]), ['a']) # should no stack-overflow + @test_throws MethodError symdiff!(IntSet([1, 2]), [[1]]) # should not return IntSet([2]) end @testset "copy, copy!, similar" begin From 43ced35f80b8b424c9e26aedead9edffcf9a1344 Mon Sep 17 00:00:00 2001 From: StephenVavasis Date: Sat, 2 Sep 2017 04:13:24 -0400 Subject: [PATCH 234/324] more about type annotation (#23180) * more about type annotation Type annotation not recommended for types constructed at run-time --- doc/src/manual/performance-tips.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/doc/src/manual/performance-tips.md b/doc/src/manual/performance-tips.md index b26a874215db6..2cefae6e9a867 100644 --- a/doc/src/manual/performance-tips.md +++ b/doc/src/manual/performance-tips.md @@ -485,6 +485,34 @@ annotation in this context in order to achieve type stability. This is because t cannot deduce the type of the return value of a function, even `convert`, unless the types of all the function's arguments are known. +Type annotation will not enhance (and can actually hinder) performance if the type is constructed +at run-time. This is because the compiler cannot use the annotation to specialize the subsequent +code, and the type-check itself takes time. For example, in the code: + +```julia +function nr(a, prec) + ctype = prec == 32 ? Float32 : Float64 + b = Complex{ctype}(a) + c = (b + 1.0f0)::Complex{ctype} + abs(c) +end +``` + +the annotation of `c` harms performance. To write performant code involving types constructed at +run-time, use the [function-barrier technique](@ref kernal-functions) discussed below, and ensure +that the constructed type appears among the argument types of the kernel function so that the kernel +operations are properly specialized by the compiler. For example, in the above snippet, as soon as +`b` is constructed, it can be passed to another function `k`, the kernel. If, for example, function +`k` declares `b` as an argument of type `Complex{T}`, where `T` is a type parameter, then a type annotation +appearing in an assignment statement within `k` of the form: + +```julia +c = (b + 1.0f0)::Complex{T} +``` + +does not hinder performance (but does not help either) since the compiler can determine the type of `c` +at the time `k` is compiled. + ### Declare types of keyword arguments Keyword arguments can have declared types: From 6792ad52e4ad62bb2970a1c7872f85d3d58bc1a6 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Sat, 2 Sep 2017 14:23:33 +0200 Subject: [PATCH 235/324] fix IntSet tests invalid since #23138 Now IntSet have the length of their underlying BitVector be a multiple of 64, so resizing them to something else breaks this invariant. --- test/intset.jl | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/intset.jl b/test/intset.jl index 735f53ca53559..5a4431ff2de02 100644 --- a/test/intset.jl +++ b/test/intset.jl @@ -252,9 +252,7 @@ end @testset "setlike" begin p = IntSet([1,2,5,6]) - resize!(p.bits, 6) q = IntSet([1,3,5,7]) - resize!(q.bits, 8) a = Set(p) b = Set(q) for f in (union, intersect, setdiff, symdiff) From aee1aab70f8521dcebb76ef91173c8c416a8611c Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Sat, 2 Sep 2017 13:14:53 -0400 Subject: [PATCH 236/324] fix #23558, bug in recursive let-bound functions --- src/julia-syntax.scm | 9 +++++---- test/core.jl | 7 +++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index a17184d66ba44..405c048257512 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -1144,7 +1144,7 @@ ;; some kind of assignment (cond ((eventually-call? (cadar binds)) - ;; f()=c + ;; f() = c (let ((asgn (butlast (expand-forms (car binds)))) (name (assigned-name (cadar binds)))) (if (not (symbol? name)) @@ -1152,15 +1152,16 @@ (loop (cdr binds) `(scope-block (block - (local-def ,name) + ,(if (expr-contains-eq name (caddar binds)) + `(local ,name) ;; might need a Box for recursive functions + `(local-def ,name)) ,asgn ,blk))))) ((or (symbol? (cadar binds)) (decl? (cadar binds))) (let ((vname (decl-var (cadar binds)))) (loop (cdr binds) - (if (contains (lambda (x) (eq? x vname)) - (caddar binds)) + (if (expr-contains-eq vname (caddar binds)) (let ((tmp (make-ssavalue))) `(scope-block (block (= ,tmp ,(caddar binds)) diff --git a/test/core.jl b/test/core.jl index 79881a2c747ea..00ddd2b51a656 100644 --- a/test/core.jl +++ b/test/core.jl @@ -328,6 +328,13 @@ let f = i18408() @test_throws UndefRefError f(0) end +# issue #23558 +c23558(n,k) = + let fact(n) = if (n == 0) 1 else n*fact(n-1) end + fact(n)/fact(k)/fact(n-k) + end +@test c23558(10, 5) == 252 + # variable scope, globals glob_x = 23 function glotest() From 92ff781abfce4658cb0f7c447d12a099c6385e81 Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Sat, 2 Sep 2017 21:02:09 +0200 Subject: [PATCH 237/324] faster sparse(::Diagonal) (#23536) --- base/sparse/sparsematrix.jl | 5 +++++ test/sparse/sparse.jl | 3 +++ 2 files changed, 8 insertions(+) diff --git a/base/sparse/sparsematrix.jl b/base/sparse/sparsematrix.jl index 2f071aa586dd0..fbf14eec0ba55 100644 --- a/base/sparse/sparsematrix.jl +++ b/base/sparse/sparsematrix.jl @@ -763,6 +763,11 @@ function sparse(B::Bidiagonal) return sparse([1:m;1:m-1],[1:m;2:m],[B.dv;B.ev], Int(m), Int(m)) # upper bidiagonal end +function sparse(D::Diagonal{T}) where T + m = length(D.diag) + return SparseMatrixCSC(m, m, collect(1:(m+1)), collect(1:m), Vector{T}(D.diag)) +end + ## Transposition and permutation methods """ diff --git a/test/sparse/sparse.jl b/test/sparse/sparse.jl index a52dcf5638973..06cc5a7b803b3 100644 --- a/test/sparse/sparse.jl +++ b/test/sparse/sparse.jl @@ -1305,6 +1305,9 @@ end B = Bidiagonal(randn(5),randn(4),:L) S = sparse(B) @test norm(Array(B) - Array(S)) == 0.0 + D = Diagonal(randn(5)) + S = sparse(D) + @test norm(Array(D) - Array(S)) == 0.0 end @testset "spdiagm promotion" begin From 45b0013d78d1240c39c2771cfd54f4a344144877 Mon Sep 17 00:00:00 2001 From: Yichao Yu Date: Sat, 2 Sep 2017 15:14:41 -0400 Subject: [PATCH 238/324] Makes sure assertion respects JL_NDEBUG in C++ code This generalizes `fix_llvm_assert.h` to `julia_assert.h` and replace (almost) all uses of `cassert` and `assert.h` with `julia_assert.h`. Changes the rule for including `julia_assert.h` so that includes of `assert.h` from external headers (including `libsupport`) won't accidientally disables assertion. Without this patch, the `assert.h` included in `libsupport.h` (in `julia.h`) may disable assertions in our c++ code. --- src/APInt-C.cpp | 3 +-- src/Makefile | 4 ++-- src/array.c | 4 ++-- src/ast.c | 2 +- src/builtins.c | 2 +- src/cgmemmgr.cpp | 2 +- src/codegen.cpp | 3 +-- src/datatype.c | 2 +- src/debuginfo.cpp | 3 +-- src/disasm.cpp | 2 +- src/dlload.c | 2 +- src/dump.c | 7 +++---- src/fix_llvm_assert.h | 16 ---------------- src/gc-debug.c | 2 +- src/gc-pages.c | 1 + src/gc.c | 1 + src/gc.h | 2 +- src/gf.c | 2 +- src/init.c | 2 +- src/interpreter.c | 2 +- src/jitlayers.cpp | 3 ++- src/jitlayers.h | 2 +- src/jl_uv.c | 3 ++- src/jlapi.c | 2 +- src/jloptions.c | 1 + src/jltypes.c | 4 ++-- src/julia.h | 3 ++- src/julia_assert.h | 27 +++++++++++++++++++++++++++ src/llvm-alloc-opt.cpp | 4 ++-- src/llvm-late-gc-lowering.cpp | 1 + src/llvm-lower-handlers.cpp | 2 +- src/llvm-muladd.cpp | 2 +- src/llvm-ptls.cpp | 2 +- src/llvm-simdloop.cpp | 3 ++- src/llvm-version.h | 2 +- src/locks.h | 2 ++ src/method.c | 2 +- src/module.c | 2 +- src/precompile.c | 2 +- src/rtutils.c | 2 +- src/runtime_ccall.cpp | 3 ++- src/safepoint.c | 1 + src/signals-mach.c | 2 ++ src/signals-unix.c | 2 ++ src/simplevector.c | 2 +- src/stackwalk.c | 1 + src/staticdata.c | 6 +++--- src/subtype.c | 2 +- src/support/libsupport.h | 1 - src/symbol.c | 2 +- src/sys.c | 8 +++++--- src/task.c | 2 +- src/threading.c | 1 + src/timing.h | 2 ++ src/toplevel.c | 2 +- src/typemap.c | 2 +- ui/Makefile | 2 +- ui/repl.c | 3 ++- 58 files changed, 104 insertions(+), 75 deletions(-) delete mode 100644 src/fix_llvm_assert.h create mode 100644 src/julia_assert.h diff --git a/src/APInt-C.cpp b/src/APInt-C.cpp index 38bbdbe96a2e8..ccf3a010708b7 100644 --- a/src/APInt-C.cpp +++ b/src/APInt-C.cpp @@ -6,10 +6,9 @@ #include #include -#include "fix_llvm_assert.h" - #include "APInt-C.h" #include "julia.h" +#include "julia_assert.h" using namespace llvm; diff --git a/src/Makefile b/src/Makefile index da00f0f652842..d3ff989009006 100644 --- a/src/Makefile +++ b/src/Makefile @@ -75,8 +75,8 @@ LLVM_LIBS := support endif # headers are used for dependency tracking, while public headers will be part of the dist -HEADERS := $(BUILDDIR)/julia_version.h $(wildcard $(SRCDIR)/support/*.h) $(addprefix $(SRCDIR)/,julia.h julia_threads.h tls.h locks.h atomics.h julia_internal.h options.h timing.h) -PUBLIC_HEADERS := $(BUILDDIR)/julia_version.h $(wildcard $(SRCDIR)/support/*.h) $(addprefix $(SRCDIR)/,julia.h julia_threads.h tls.h locks.h atomics.h) +HEADERS := $(BUILDDIR)/julia_version.h $(wildcard $(SRCDIR)/support/*.h) $(addprefix $(SRCDIR)/,julia.h julia_assert.h julia_threads.h tls.h locks.h atomics.h julia_internal.h options.h timing.h) +PUBLIC_HEADERS := $(BUILDDIR)/julia_version.h $(wildcard $(SRCDIR)/support/*.h) $(addprefix $(SRCDIR)/,julia.h julia_assert.h julia_threads.h tls.h locks.h atomics.h) ifeq ($(USE_SYSTEM_LIBUV),0) ifeq ($(OS),WINNT) HEADERS += $(build_includedir)/tree.h diff --git a/src/array.c b/src/array.c index 9bca78b015f71..2c3deaa1f9bb2 100644 --- a/src/array.c +++ b/src/array.c @@ -5,12 +5,12 @@ */ #include #include -#include #ifdef _OS_WINDOWS_ #include #endif #include "julia.h" #include "julia_internal.h" +#include "julia_assert.h" #ifdef __cplusplus extern "C" { @@ -157,7 +157,7 @@ jl_array_t *jl_new_array_for_deserialization(jl_value_t *atype, uint32_t ndims, return _new_array_(atype, ndims, dims, isunboxed, elsz); } -#ifndef NDEBUG +#ifndef JL_NDEBUG static inline int is_ntuple_long(jl_value_t *v) { if (!jl_is_tuple(v)) diff --git a/src/ast.c b/src/ast.c index 984163f698626..b4e513703f7e8 100644 --- a/src/ast.c +++ b/src/ast.c @@ -7,13 +7,13 @@ #include #include #include -#include #ifdef _OS_WINDOWS_ #include #endif #include "julia.h" #include "julia_internal.h" #include "flisp.h" +#include "julia_assert.h" #ifdef __cplusplus extern "C" { diff --git a/src/builtins.c b/src/builtins.c index 06800206647b0..34b6430709384 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -25,6 +24,7 @@ #include "julia_internal.h" #include "builtin_proto.h" #include "intrinsics.h" +#include "julia_assert.h" #ifdef __cplusplus extern "C" { diff --git a/src/cgmemmgr.cpp b/src/cgmemmgr.cpp index b4f0ff0969b69..595167f718000 100644 --- a/src/cgmemmgr.cpp +++ b/src/cgmemmgr.cpp @@ -5,7 +5,6 @@ #include "options.h" #include -#include "fix_llvm_assert.h" #include "julia.h" #include "julia_internal.h" @@ -25,6 +24,7 @@ #ifdef _OS_FREEBSD_ # include #endif +#include "julia_assert.h" namespace { diff --git a/src/codegen.cpp b/src/codegen.cpp index 24f62d52f04e4..9e3178ce4383a 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -31,7 +31,6 @@ #include #include #include -#include #include #include @@ -85,7 +84,6 @@ #include #endif #include -#include "fix_llvm_assert.h" using namespace llvm; namespace llvm { @@ -100,6 +98,7 @@ namespace llvm { #include "julia_internal.h" #include "jitlayers.h" #include "codegen_shared.h" +#include "julia_assert.h" // LLVM version compatibility macros legacy::PassManager *jl_globalPM; diff --git a/src/datatype.c b/src/datatype.c index 147ea375b6023..ca7f8a361d624 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -8,9 +8,9 @@ #include #include #include -#include #include "julia.h" #include "julia_internal.h" +#include "julia_assert.h" #ifdef __cplusplus extern "C" { diff --git a/src/debuginfo.cpp b/src/debuginfo.cpp index b2a3f070f60ed..3c232af029afd 100644 --- a/src/debuginfo.cpp +++ b/src/debuginfo.cpp @@ -22,7 +22,6 @@ #include #include #include -#include "fix_llvm_assert.h" using namespace llvm; @@ -46,7 +45,7 @@ using llvm_file_magic = sys::fs::file_magic; #include #include #include -#include +#include "julia_assert.h" typedef object::SymbolRef SymRef; diff --git a/src/disasm.cpp b/src/disasm.cpp index 219836e4a59b8..a85d714cf1e25 100644 --- a/src/disasm.cpp +++ b/src/disasm.cpp @@ -69,13 +69,13 @@ #include #include #include "llvm/IR/AssemblyAnnotationWriter.h" -#include "fix_llvm_assert.h" #include "julia.h" #include "julia_internal.h" using namespace llvm; #include "debuginfo.h" +#include "julia_assert.h" // helper class for tracking inlining context while printing debug info class DILineInfoPrinter { diff --git a/src/dlload.c b/src/dlload.c index d7232408f268c..371aa38804fa9 100644 --- a/src/dlload.c +++ b/src/dlload.c @@ -3,7 +3,6 @@ #include #include #include -#include #include #include "platform.h" @@ -16,6 +15,7 @@ #include #include #endif +#include "julia_assert.h" #ifdef __cplusplus extern "C" { diff --git a/src/dump.c b/src/dump.c index d30a0416628ff..ba2df8034321f 100644 --- a/src/dump.c +++ b/src/dump.c @@ -5,7 +5,6 @@ */ #include #include -#include #include "julia.h" #include "julia_internal.h" @@ -20,6 +19,7 @@ #else #define RUNNING_ON_VALGRIND 0 #endif +#include "julia_assert.h" #ifdef __cplusplus extern "C" { @@ -2240,7 +2240,6 @@ JL_DLLEXPORT void jl_fill_argnames(jl_array_t *data, jl_array_t *names) } else { uint8_t *d = (uint8_t*)data->data; -#ifndef NDEBUG assert(jl_typeis(data, jl_array_uint8_type)); int b3 = d[1]; int b2 = d[2]; @@ -2248,7 +2247,7 @@ JL_DLLEXPORT void jl_fill_argnames(jl_array_t *data, jl_array_t *names) int b0 = d[4]; int nslots = b0 | (b1<<8) | (b2<<16) | (b3<<24); assert(nslots >= nargs); -#endif + (void)nslots; char *namestr = (char*)d + 5; for (i = 0; i < nargs; i++) { size_t namelen = strlen(namestr); @@ -2312,7 +2311,7 @@ JL_DLLEXPORT int jl_save_incremental(const char *fname, jl_array_t *worklist) return 0; } -#ifndef NDEBUG +#ifndef JL_NDEBUG // skip the performance optimizations of jl_types_equal and just use subtyping directly // one of these types is invalid - that's why we're doing the recache type operation static int jl_invalid_types_equal(jl_datatype_t *a, jl_datatype_t *b) diff --git a/src/fix_llvm_assert.h b/src/fix_llvm_assert.h deleted file mode 100644 index 776b127c15846..0000000000000 --- a/src/fix_llvm_assert.h +++ /dev/null @@ -1,16 +0,0 @@ -// This file is a part of Julia. License is MIT: https://julialang.org/license - -// Include this file after every blocks of LLVM includes to set the assertion back. - -#ifdef NDEBUG -# ifndef JL_NDEBUG -# undef NDEBUG -# include -// Set NDEBUG back so that we can include another LLVM header right after -# define NDEBUG -# endif -#else -# ifdef JL_NDEBUG -# undef JL_NDEBUG -# endif -#endif diff --git a/src/gc-debug.c b/src/gc-debug.c index 0e59a5594575e..4f40e5d737023 100644 --- a/src/gc-debug.c +++ b/src/gc-debug.c @@ -8,7 +8,7 @@ // so that we can always use the assert macro in this file // for use under their respective enable flags #undef NDEBUG -#include +#include "julia_assert.h" #ifdef __cplusplus extern "C" { diff --git a/src/gc-pages.c b/src/gc-pages.c index b0b3feca52c6e..60c89fd977c5b 100644 --- a/src/gc-pages.c +++ b/src/gc-pages.c @@ -4,6 +4,7 @@ #ifndef _OS_WINDOWS_ # include #endif +#include "julia_assert.h" #ifdef __cplusplus extern "C" { diff --git a/src/gc.c b/src/gc.c index 6cb84c1147f69..0c706061f6f13 100644 --- a/src/gc.c +++ b/src/gc.c @@ -1,6 +1,7 @@ // This file is a part of Julia. License is MIT: https://julialang.org/license #include "gc.h" +#include "julia_assert.h" #ifdef __cplusplus extern "C" { diff --git a/src/gc.h b/src/gc.h index e50fb8b438854..71ae65b3175f7 100644 --- a/src/gc.h +++ b/src/gc.h @@ -14,7 +14,6 @@ #ifndef _MSC_VER #include #endif -#include #include #include "julia.h" #include "julia_internal.h" @@ -25,6 +24,7 @@ #define MAP_ANONYMOUS MAP_ANON #endif #endif +#include "julia_assert.h" #ifdef __cplusplus extern "C" { diff --git a/src/gf.c b/src/gf.c index 210b6cef01ce9..459dcd9d87792 100644 --- a/src/gf.c +++ b/src/gf.c @@ -10,12 +10,12 @@ */ #include #include -#include #include "julia.h" #include "julia_internal.h" #ifndef _OS_WINDOWS_ #include #endif +#include "julia_assert.h" // @nospecialize has no effect if the number of overlapping methods is greater than this #define MAX_UNSPECIALIZED_CONFLICTS 32 diff --git a/src/init.c b/src/init.c index d708b89e158bb..3556f7d8a90d9 100644 --- a/src/init.c +++ b/src/init.c @@ -9,7 +9,6 @@ #include #include #include -#include #include #include @@ -24,6 +23,7 @@ #include "builtin_proto.h" #undef DEFINE_BUILTIN_GLOBALS #include "threading.h" +#include "julia_assert.h" #ifdef __cplusplus extern "C" { diff --git a/src/interpreter.c b/src/interpreter.c index b99b7a06a6330..e79e858eb3b3e 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -2,13 +2,13 @@ #include #include -#include #ifdef _OS_WINDOWS_ #include #endif #include "julia.h" #include "julia_internal.h" #include "builtin_proto.h" +#include "julia_assert.h" #ifdef __cplusplus extern "C" { diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index 656e6fe2398bf..cd986a0336643 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -60,13 +60,14 @@ namespace llvm { #include #include #include "codegen_shared.h" -#include "fix_llvm_assert.h" using namespace llvm; #include "julia.h" #include "julia_internal.h" #include "jitlayers.h" +#include "julia_assert.h" + RTDyldMemoryManager* createRTDyldMemoryManager(void); static Type *T_void; diff --git a/src/jitlayers.h b/src/jitlayers.h index 5ebfad68ad093..9b20c9d704a47 100644 --- a/src/jitlayers.h +++ b/src/jitlayers.h @@ -22,7 +22,7 @@ extern legacy::PassManager *jl_globalPM; #include -#include "fix_llvm_assert.h" +#include "julia_assert.h" extern "C" { extern int globalUnique; diff --git a/src/jl_uv.c b/src/jl_uv.c index c8ad59e7e4152..640c4afd9baad 100644 --- a/src/jl_uv.c +++ b/src/jl_uv.c @@ -7,7 +7,6 @@ #include #include #include -#include #ifdef _OS_WINDOWS_ #include @@ -28,6 +27,8 @@ #define write _write #endif +#include "julia_assert.h" + #ifdef __cplusplus #include extern "C" { diff --git a/src/jlapi.c b/src/jlapi.c index be3e74be8b578..ebd16f11914ba 100644 --- a/src/jlapi.c +++ b/src/jlapi.c @@ -10,9 +10,9 @@ #include #include #include -#include #include "julia.h" #include "options.h" +#include "julia_assert.h" #ifdef __cplusplus #include diff --git a/src/jloptions.c b/src/jloptions.c index 5da1d79159ed4..64b512212c67c 100644 --- a/src/jloptions.c +++ b/src/jloptions.c @@ -10,6 +10,7 @@ #else #include "getopt.h" #endif +#include "julia_assert.h" #ifdef _OS_WINDOWS_ char *shlib_ext = ".dll"; diff --git a/src/jltypes.c b/src/jltypes.c index 6218abb83a019..0cf4b69ff82fb 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -7,13 +7,13 @@ */ #include #include -#include #ifdef _OS_WINDOWS_ #include #endif #include "julia.h" #include "julia_internal.h" #include "builtin_proto.h" +#include "julia_assert.h" #ifdef __cplusplus extern "C" { @@ -270,7 +270,7 @@ JL_DLLEXPORT int (jl_is_leaf_type)(jl_value_t *v) { if (jl_is_datatype(v)) { int isleaf = ((jl_datatype_t*)v)->isleaftype; -#ifdef NDEBUG +#ifdef JL_NDEBUG return isleaf; #else if (((jl_datatype_t*)v)->abstract) { diff --git a/src/julia.h b/src/julia.h index 5fda2e5415f25..e505408312d58 100644 --- a/src/julia.h +++ b/src/julia.h @@ -63,6 +63,7 @@ typedef struct _jl_taggedvalue_t jl_taggedvalue_t; #include "atomics.h" #include "tls.h" #include "julia_threads.h" +#include "julia_assert.h" #ifdef __cplusplus extern "C" { @@ -997,7 +998,7 @@ JL_DLLEXPORT int jl_type_morespecific(jl_value_t *a, jl_value_t *b); jl_value_t *jl_unwrap_unionall(jl_value_t *v); jl_value_t *jl_rewrap_unionall(jl_value_t *t, jl_value_t *u); -#if defined(NDEBUG) && defined(JL_NDEBUG) +#if defined(JL_NDEBUG) STATIC_INLINE int jl_is_leaf_type_(jl_value_t *v) { return jl_is_datatype(v) && ((jl_datatype_t*)v)->isleaftype; diff --git a/src/julia_assert.h b/src/julia_assert.h new file mode 100644 index 0000000000000..6cf89d0e470a5 --- /dev/null +++ b/src/julia_assert.h @@ -0,0 +1,27 @@ +// This file is a part of Julia. License is MIT: https://julialang.org/license + +// Include this file instead of `assert.h` directly. +// This is necessary because LLVM sometimes has bugs that cause runtime assertion if +// the `NDEBUG` setting is different from the one used to compile LLVM. +// For C++ files, we set `NDEBUG` to match what LLVM expects and use `JL_NDEBUG` to +// enable assertions in julia code. After including this file, the definition of `assert` will +// match the setting given in `JL_NDEBUG` and `NDEBUG` will remain unchanged. +// +// Files that need `assert` should include this file after all other includes. +// All files should also check `JL_NDEBUG` instead of `NDEBUG`. + +#ifdef NDEBUG +# ifndef JL_NDEBUG +# undef NDEBUG +# include +// Set NDEBUG back so that we can include another LLVM header right after +# define NDEBUG +# else +# include +# endif +#else +# ifdef JL_NDEBUG +# undef JL_NDEBUG +# endif +# include +#endif diff --git a/src/llvm-alloc-opt.cpp b/src/llvm-alloc-opt.cpp index be872d7f056db..51d9403c96342 100644 --- a/src/llvm-alloc-opt.cpp +++ b/src/llvm-alloc-opt.cpp @@ -16,8 +16,6 @@ #include #include -#include "fix_llvm_assert.h" - #include "codegen_shared.h" #include "julia.h" #include "julia_internal.h" @@ -25,6 +23,8 @@ #include #include +#include "julia_assert.h" + using namespace llvm; extern std::pair tbaa_make_child(const char *name, MDNode *parent=nullptr, bool isConstant=false); diff --git a/src/llvm-late-gc-lowering.cpp b/src/llvm-late-gc-lowering.cpp index dc77551d192c3..c11a6d13029a2 100644 --- a/src/llvm-late-gc-lowering.cpp +++ b/src/llvm-late-gc-lowering.cpp @@ -21,6 +21,7 @@ #include "codegen_shared.h" #include "julia.h" #include "julia_internal.h" +#include "julia_assert.h" #define DEBUG_TYPE "late_lower_gcroot" diff --git a/src/llvm-lower-handlers.cpp b/src/llvm-lower-handlers.cpp index 4ef6f15a02aa5..5caee2b1998bb 100644 --- a/src/llvm-lower-handlers.cpp +++ b/src/llvm-lower-handlers.cpp @@ -1,6 +1,5 @@ // This file is a part of Julia. License is MIT: https://julialang.org/license -#include #include #include #include @@ -14,6 +13,7 @@ #include "llvm-version.h" #include "julia.h" +#include "julia_assert.h" #define DEBUG_TYPE "lower_handlers" #undef DEBUG diff --git a/src/llvm-muladd.cpp b/src/llvm-muladd.cpp index 267efa0749dc7..a8b635f9c34e8 100644 --- a/src/llvm-muladd.cpp +++ b/src/llvm-muladd.cpp @@ -13,9 +13,9 @@ #include #include #include -#include "fix_llvm_assert.h" #include "julia.h" +#include "julia_assert.h" using namespace llvm; diff --git a/src/llvm-ptls.cpp b/src/llvm-ptls.cpp index 391b3096b08e9..5c0e5a8296a71 100644 --- a/src/llvm-ptls.cpp +++ b/src/llvm-ptls.cpp @@ -21,10 +21,10 @@ # include # include #endif -#include "fix_llvm_assert.h" #include "julia.h" #include "julia_internal.h" +#include "julia_assert.h" using namespace llvm; diff --git a/src/llvm-simdloop.cpp b/src/llvm-simdloop.cpp index 72dbe86ee6beb..87f4d953b5fcd 100644 --- a/src/llvm-simdloop.cpp +++ b/src/llvm-simdloop.cpp @@ -14,10 +14,11 @@ #include #include #include -#include "fix_llvm_assert.h" #include +#include "julia_assert.h" + namespace llvm { // simd loop diff --git a/src/llvm-version.h b/src/llvm-version.h index f164da886287e..fa1dbcf433e21 100644 --- a/src/llvm-version.h +++ b/src/llvm-version.h @@ -1,7 +1,7 @@ // This file is a part of Julia. License is MIT: https://julialang.org/license #include -#include "fix_llvm_assert.h" +#include "julia_assert.h" // The LLVM version used, JL_LLVM_VERSION, is represented as a 5-digit integer // of the form ABBCC, where A is the major version, B is minor, and C is patch. diff --git a/src/locks.h b/src/locks.h index 3212eb3377534..21f41f9d72d07 100644 --- a/src/locks.h +++ b/src/locks.h @@ -3,6 +3,8 @@ #ifndef JL_LOCKS_H #define JL_LOCKS_H +#include "julia_assert.h" + #ifdef __cplusplus extern "C" { #endif diff --git a/src/method.c b/src/method.c index d32a6c19d881e..45d39cdbec1f8 100644 --- a/src/method.c +++ b/src/method.c @@ -7,9 +7,9 @@ #include #include #include -#include #include "julia.h" #include "julia_internal.h" +#include "julia_assert.h" #ifdef __cplusplus extern "C" { diff --git a/src/module.c b/src/module.c index c1ca45cc9a9de..2f3ded651bb08 100644 --- a/src/module.c +++ b/src/module.c @@ -3,9 +3,9 @@ /* modules and top-level bindings */ -#include #include "julia.h" #include "julia_internal.h" +#include "julia_assert.h" #ifdef __cplusplus extern "C" { diff --git a/src/precompile.c b/src/precompile.c index 9830bb41744b5..db4c2f57792f8 100644 --- a/src/precompile.c +++ b/src/precompile.c @@ -6,10 +6,10 @@ */ #include -#include #include "julia.h" #include "julia_internal.h" +#include "julia_assert.h" #ifdef __cplusplus extern "C" { diff --git a/src/rtutils.c b/src/rtutils.c index a2385e24dd364..5990561de3cca 100644 --- a/src/rtutils.c +++ b/src/rtutils.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -23,6 +22,7 @@ #include #include "julia.h" #include "julia_internal.h" +#include "julia_assert.h" #ifdef __cplusplus extern "C" { diff --git a/src/runtime_ccall.cpp b/src/runtime_ccall.cpp index 142ecd5b4620e..49a0640943bca 100644 --- a/src/runtime_ccall.cpp +++ b/src/runtime_ccall.cpp @@ -5,9 +5,10 @@ #include #include #include -#include "fix_llvm_assert.h" #include "julia.h" #include "julia_internal.h" +#include "julia_assert.h" + using namespace llvm; // --- library symbol lookup --- diff --git a/src/safepoint.c b/src/safepoint.c index 43dad38f4fad4..1d58c26cad9bd 100644 --- a/src/safepoint.c +++ b/src/safepoint.c @@ -9,6 +9,7 @@ #define MAP_ANONYMOUS MAP_ANON #endif #endif +#include "julia_assert.h" #ifdef __cplusplus extern "C" { diff --git a/src/signals-mach.c b/src/signals-mach.c index 8534eb6e8bafe..ef38b25b01ff3 100644 --- a/src/signals-mach.c +++ b/src/signals-mach.c @@ -15,6 +15,8 @@ #include #endif +#include "julia_assert.h" + static void attach_exception_port(thread_port_t thread, int segv_only); #ifdef JULIA_ENABLE_THREADING diff --git a/src/signals-unix.c b/src/signals-unix.c index 2b69f43d6a972..ed3e52542fb30 100644 --- a/src/signals-unix.c +++ b/src/signals-unix.c @@ -33,6 +33,8 @@ // Should also be enough for parallel GC when we have it =) #define sig_stack_size (8 * 1024 * 1024) +#include "julia_assert.h" + static bt_context_t *jl_to_bt_context(void *sigctx) { #ifdef __APPLE__ diff --git a/src/simplevector.c b/src/simplevector.c index 69247924a6ee9..c3d0dde54e43f 100644 --- a/src/simplevector.c +++ b/src/simplevector.c @@ -3,9 +3,9 @@ #include #include #include -#include #include "julia.h" #include "julia_internal.h" +#include "julia_assert.h" JL_DLLEXPORT jl_svec_t *jl_svec(size_t n, ...) { diff --git a/src/stackwalk.c b/src/stackwalk.c index 1f1cd043c4296..26e400e03d8a4 100644 --- a/src/stackwalk.c +++ b/src/stackwalk.c @@ -8,6 +8,7 @@ #include "julia.h" #include "julia_internal.h" #include "threading.h" +#include "julia_assert.h" // define `jl_unw_get` as a macro, since (like setjmp) // returning from the callee function will invalidate the context diff --git a/src/staticdata.c b/src/staticdata.c index d08f5d0ee6bdb..0d431e05ba992 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -5,7 +5,6 @@ */ #include #include -#include #include "julia.h" #include "julia_internal.h" @@ -20,6 +19,7 @@ #else #define RUNNING_ON_VALGRIND 0 #endif +#include "julia_assert.h" #ifdef __cplusplus extern "C" { @@ -821,7 +821,7 @@ static uintptr_t get_reloc_for_item(uintptr_t reloc_item, size_t reloc_offset) } else { // just write the item reloc_id directly -#ifndef NDEBUG +#ifndef JL_NDEBUG assert(reloc_offset == 0 && "offsets for relocations to builtin objects should be precomposed in the reloc_item"); size_t offset = (reloc_item & (((uintptr_t)1 << RELOC_TAG_OFFSET) - 1)); switch (tag) { @@ -1512,7 +1512,7 @@ static void jl_restore_system_image_from_stream(ios_t *f) // TODO: need to enforce that the alignment of the buffer is suitable for vectors JL_DLLEXPORT void jl_restore_system_image(const char *fname) { -#ifndef NDEBUG +#ifndef JL_NDEBUG char *dot = fname ? (char*)strrchr(fname, '.') : NULL; int is_ji = (dot && !strcmp(dot, ".ji")); assert((is_ji || jl_sysimg_handle) && "System image file not preloaded"); diff --git a/src/subtype.c b/src/subtype.c index d713acdc2e67c..eb94e1463b50d 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -21,12 +21,12 @@ */ #include #include -#include #ifdef _OS_WINDOWS_ #include #endif #include "julia.h" #include "julia_internal.h" +#include "julia_assert.h" #ifdef __cplusplus extern "C" { diff --git a/src/support/libsupport.h b/src/support/libsupport.h index f5267baf28e86..880c8560cd23c 100644 --- a/src/support/libsupport.h +++ b/src/support/libsupport.h @@ -7,7 +7,6 @@ #include #include -#include #include "dtypes.h" #include "utils.h" #include "utf8.h" diff --git a/src/symbol.c b/src/symbol.c index 7be4136fa70d1..da17a426dbc82 100644 --- a/src/symbol.c +++ b/src/symbol.c @@ -7,9 +7,9 @@ #include #include #include -#include #include "julia.h" #include "julia_internal.h" +#include "julia_assert.h" #ifdef __cplusplus extern "C" { diff --git a/src/sys.c b/src/sys.c index ea2a6b1e69a93..c0ea5e7a3638c 100644 --- a/src/sys.c +++ b/src/sys.c @@ -4,16 +4,16 @@ sys.c I/O and operating system utility functions */ -#include "julia.h" -#include "julia_internal.h" #include #include #include -#include #include #include #include +#include "julia.h" +#include "julia_internal.h" + #ifdef _OS_WINDOWS_ #include #else @@ -56,6 +56,8 @@ #include #endif +#include "julia_assert.h" + #ifdef __cplusplus extern "C" { #endif diff --git a/src/task.c b/src/task.c index a9ed17c1c28f1..742f325790a50 100644 --- a/src/task.c +++ b/src/task.c @@ -18,13 +18,13 @@ #include #include -#include #include #include #include #include "julia.h" #include "julia_internal.h" #include "threading.h" +#include "julia_assert.h" #ifdef __cplusplus extern "C" { diff --git a/src/threading.c b/src/threading.c index cce22976db1ad..8dbe9066b9224 100644 --- a/src/threading.c +++ b/src/threading.c @@ -21,6 +21,7 @@ #include "julia.h" #include "julia_internal.h" +#include "julia_assert.h" // Ref https://www.uclibc.org/docs/tls.pdf // For variant 1 JL_ELF_TLS_INIT_SIZE is the size of the thread control block (TCB) diff --git a/src/timing.h b/src/timing.h index dfd676d66e4bd..2eab5e325e312 100644 --- a/src/timing.h +++ b/src/timing.h @@ -7,6 +7,8 @@ #define JL_TIMING(owner) #else +#include "julia_assert.h" + #ifdef __cplusplus extern "C" { #endif diff --git a/src/toplevel.c b/src/toplevel.c index c53b07f402fcf..9a56455b7aed8 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #if defined(_OS_WINDOWS_) @@ -19,6 +18,7 @@ #include "julia.h" #include "julia_internal.h" #include "uv.h" +#include "julia_assert.h" #ifdef __cplusplus extern "C" { diff --git a/src/typemap.c b/src/typemap.c index f86c1c59aede8..62f5e26c8b8bc 100644 --- a/src/typemap.c +++ b/src/typemap.c @@ -2,12 +2,12 @@ #include #include -#include #include "julia.h" #include "julia_internal.h" #ifndef _OS_WINDOWS_ #include #endif +#include "julia_assert.h" #define MAX_METHLIST_COUNT 12 // this can strongly affect the sysimg size and speed! #define INIT_CACHE_SIZE 8 // must be a power-of-two diff --git a/ui/Makefile b/ui/Makefile index 49c4dbac40167..8f69136d1a501 100644 --- a/ui/Makefile +++ b/ui/Makefile @@ -11,7 +11,7 @@ override CPPFLAGS += $(JCPPFLAGS) SRCS := repl -HEADERS := $(addprefix $(JULIAHOME)/src/,julia.h julia_threads.h julia_internal.h options.h) \ +HEADERS := $(addprefix $(JULIAHOME)/src/,julia.h julia_assert.h julia_threads.h julia_internal.h options.h) \ $(BUILDDIR)/../src/julia_version.h $(wildcard $(JULIAHOME)/src/support/*.h) $(LIBUV_INC)/uv.h FLAGS := -I$(BUILDROOT)/src -I$(JULIAHOME)/src -I$(JULIAHOME)/src/support -I$(build_includedir) diff --git a/ui/repl.c b/ui/repl.c index cba286bd1cf9a..357f612a2987e 100644 --- a/ui/repl.c +++ b/ui/repl.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -22,6 +21,8 @@ #include "uv.h" #include "../src/julia.h" +#include "../src/julia_assert.h" + JULIA_DEFINE_FAST_TLS() #ifdef __cplusplus From b8536bfb84c868b2c8444e4b796b47fc8ccc912b Mon Sep 17 00:00:00 2001 From: Andreas Noack Date: Sat, 2 Sep 2017 15:55:54 -0400 Subject: [PATCH 239/324] Use min(size(A)...) instead of max(size(A)...) when computing the numrical rank (#23523) Fixes #23386 --- base/linalg/dense.jl | 2 +- base/linalg/generic.jl | 21 +++++++++------------ base/linalg/qr.jl | 10 ++++++---- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/base/linalg/dense.jl b/base/linalg/dense.jl index dcdd94b60ffd6..d716fc08738b3 100644 --- a/base/linalg/dense.jl +++ b/base/linalg/dense.jl @@ -891,7 +891,7 @@ function pinv(A::StridedMatrix{T}, tol::Real) where T return SVD.Vt' * (Diagonal(Sinv) * SVD.U') end function pinv(A::StridedMatrix{T}) where T - tol = eps(real(float(one(T))))*maximum(size(A)) + tol = eps(real(float(one(T))))*min(size(A)...) return pinv(A, tol) end function pinv(x::Number) diff --git a/base/linalg/generic.jl b/base/linalg/generic.jl index bfb3f33bb6d5a..98b5b4c889fd3 100644 --- a/base/linalg/generic.jl +++ b/base/linalg/generic.jl @@ -705,13 +705,13 @@ dot(x::AbstractVector{<:Number}, y::AbstractVector{<:Number}) = vecdot(x, y) ########################################################################################### """ - rank(M[, tol::Real]) + rank(A[, tol::Real]) Compute the rank of a matrix by counting how many singular -values of `M` have magnitude greater than `tol`. -By default, the value of `tol` is the largest -dimension of `M` multiplied by the [`eps`](@ref) -of the [`eltype`](@ref) of `M`. +values of `A` have magnitude greater than `tol*σ₁` where `σ₁` is +`A`'s largest singular values. By default, the value of `tol` is the smallest +dimension of `A` multiplied by the [`eps`](@ref) +of the [`eltype`](@ref) of `A`. # Examples ```jldoctest @@ -728,14 +728,11 @@ julia> rank(diagm([1, 0.001, 2]), 0.00001) 3 ``` """ -rank(A::AbstractMatrix, tol::Real) = mapreduce(x -> x > tol, +, 0, svdvals(A)) -function rank(A::AbstractMatrix) - m,n = size(A) - (m == 0 || n == 0) && return 0 - sv = svdvals(A) - return sum(sv .> maximum(size(A))*eps(sv[1])) +function rank(A::AbstractMatrix, tol::Real = min(size(A)...)*eps(real(float(one(eltype(A)))))) + s = svdvals(A) + sum(x -> x > tol*s[1], s) end -rank(x::Number) = x==0 ? 0 : 1 +rank(x::Number) = x == 0 ? 0 : 1 """ trace(M) diff --git a/base/linalg/qr.jl b/base/linalg/qr.jl index 46b46d3cb3cf3..c7e266f98b2d9 100644 --- a/base/linalg/qr.jl +++ b/base/linalg/qr.jl @@ -194,7 +194,7 @@ function qrfactPivotedUnblocked!(A::StridedMatrix) end # LAPACK version -qrfact!(A::StridedMatrix{<:BlasFloat}, ::Val{false}) = QRCompactWY(LAPACK.geqrt!(A, min(minimum(size(A)), 36))...) +qrfact!(A::StridedMatrix{<:BlasFloat}, ::Val{false}) = QRCompactWY(LAPACK.geqrt!(A, min(min(size(A)...), 36))...) qrfact!(A::StridedMatrix{<:BlasFloat}, ::Val{true}) = QRPivoted(LAPACK.geqp3!(A)...) qrfact!(A::StridedMatrix{<:BlasFloat}) = qrfact!(A, Val(false)) @@ -458,7 +458,7 @@ convert(::Type{AbstractMatrix{T}}, Q::QRPackedQ) where {T} = convert(QRPackedQ{T convert(::Type{QRCompactWYQ{S}}, Q::QRCompactWYQ) where {S} = QRCompactWYQ(convert(AbstractMatrix{S}, Q.factors), convert(AbstractMatrix{S}, Q.T)) convert(::Type{AbstractMatrix{S}}, Q::QRCompactWYQ{S}) where {S} = Q convert(::Type{AbstractMatrix{S}}, Q::QRCompactWYQ) where {S} = convert(QRCompactWYQ{S}, Q) -convert(::Type{Matrix}, A::AbstractQ{T}) where {T} = A_mul_B!(A, eye(T, size(A.factors, 1), minimum(size(A.factors)))) +convert(::Type{Matrix}, A::AbstractQ{T}) where {T} = A_mul_B!(A, eye(T, size(A.factors, 1), min(size(A.factors)...))) convert(::Type{Array}, A::AbstractQ) = convert(Matrix, A) """ @@ -732,8 +732,10 @@ function A_ldiv_B!(A::QRPivoted{T}, B::StridedMatrix{T}, rcond::Real) where T<:B B[1:nA,:] = view(B, 1:nA, :)[invperm(A[:p]::Vector{BlasInt}),:] return B, rnk end -A_ldiv_B!(A::QRPivoted{T}, B::StridedVector{T}) where {T<:BlasFloat} = vec(A_ldiv_B!(A,reshape(B,length(B),1))) -A_ldiv_B!(A::QRPivoted{T}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = A_ldiv_B!(A, B, maximum(size(A))*eps(real(float(one(eltype(B))))))[1] +A_ldiv_B!(A::QRPivoted{T}, B::StridedVector{T}) where {T<:BlasFloat} = + vec(A_ldiv_B!(A,reshape(B,length(B),1))) +A_ldiv_B!(A::QRPivoted{T}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = + A_ldiv_B!(A, B, min(size(A)...)*eps(real(float(one(eltype(B))))))[1] function A_ldiv_B!(A::QR{T}, B::StridedMatrix{T}) where T m, n = size(A) minmn = min(m,n) From 3c68dcdc98239dbba6334931f8eaf919a71425c2 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Sat, 2 Sep 2017 15:59:34 -0400 Subject: [PATCH 240/324] Don't ignore memcpy in GC root placement --- src/llvm-late-gc-lowering.cpp | 16 ++++++++++++++-- test/llvmpasses/gcroots.ll | 18 +++++++++++++++++- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/llvm-late-gc-lowering.cpp b/src/llvm-late-gc-lowering.cpp index dc77551d192c3..fce07846ed5e5 100644 --- a/src/llvm-late-gc-lowering.cpp +++ b/src/llvm-late-gc-lowering.cpp @@ -728,8 +728,16 @@ State LateLowerGCFrame::LocalScan(Function &F) { Instruction &I = *it; if (CallInst *CI = dyn_cast(&I)) { if (isa(CI)) { - // Intrinsics are never GC uses/defs - continue; + // Most intrinsics are not gc uses/defs, however some have + // memory operands and could thus be GC uses. To be conservative, + // we only skip processing for those that we know we emit often + // and cannot possibly be GC uses. + IntrinsicInst *II = cast(CI); + if (isa(CI) || + II->getIntrinsicID() == Intrinsic::lifetime_start || + II->getIntrinsicID() == Intrinsic::lifetime_end) { + continue; + } } MaybeNoteDef(S, BBS, CI, BBS.Safepoints); NoteOperandUses(S, BBS, I, BBS.UpExposedUses); @@ -743,6 +751,10 @@ State LateLowerGCFrame::LocalScan(Function &F) { if (CI->canReturnTwice()) { S.ReturnsTwice.push_back(CI); } + if (isa(CI)) { + // Intrinsics are never safepoints. + continue; + } if (auto callee = CI->getCalledFunction()) { // Known functions emitted in codegen that are not safepoints if (callee == pointer_from_objref_func || callee->getName() == "memcmp") { diff --git a/test/llvmpasses/gcroots.ll b/test/llvmpasses/gcroots.ll index 835a6e72d71c5..7a92e64a0e434 100644 --- a/test/llvmpasses/gcroots.ll +++ b/test/llvmpasses/gcroots.ll @@ -192,7 +192,7 @@ define %jl_value_t addrspace(10)* @no_redundant_rerooting(i64 %a, i1 %cond) { top: %ptls = call %jl_value_t*** @jl_get_ptls_states() %aboxed = call %jl_value_t addrspace(10)* @jl_box_int64(i64 signext %a) -; CHECK: store %jl_value_t addrspace(10)* %aboxed +; CHECK: store %jl_value_t addrspace(10)* %aboxed ; CHECK-NEXT: call void @jl_safepoint() call void @jl_safepoint() br i1 %cond, label %blocka, label %blockb @@ -207,3 +207,19 @@ blockb: call void @jl_safepoint() ret %jl_value_t addrspace(10)* %aboxed } + +declare void @llvm.memcpy.p064.p10i8.i64(i64*, i8 addrspace(10)*, i64, i32, i1) + +define void @memcpy_use(i64 %a, i64 *%aptr) { +; CHECK-LABEL: @memcpy_use +; CHECK: %gcframe = alloca %jl_value_t addrspace(10)*, i32 3 +top: + %ptls = call %jl_value_t*** @jl_get_ptls_states() + %aboxed = call %jl_value_t addrspace(10)* @jl_box_int64(i64 signext %a) +; CHECK: store %jl_value_t addrspace(10)* %aboxed + call void @jl_safepoint() + %acast = bitcast %jl_value_t addrspace(10)* %aboxed to i8 addrspace(10)* + call void @llvm.memcpy.p064.p10i8.i64(i64* %aptr, i8 addrspace(10)* %acast, i64 8, i32 1, i1 false) + ret void +} + From 797e87bb8b11e947963d3cb7f4f5fea0b63311fa Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Sat, 2 Sep 2017 15:15:57 -0400 Subject: [PATCH 241/324] Add gc use intrinisic Now that our gc root placement pass is significantly more aggressive about dropping roots, we need to be a bit more careful about our use of pointer, etc. This adds a low-level intrinsic for annotating gc uses to keep objects alive even if they would be otherwise unreferenced. As an initial use case, we get rid of a number of uses of `pointer` in string, but creating a new `unsafe_load` that keeps the string alive. --- base/boot.jl | 2 ++ base/strings/string.jl | 33 ++++++++++++++++++--------------- src/ccall.cpp | 7 +++++++ src/codegen.cpp | 7 +++++++ src/julia.h | 1 + src/llvm-late-gc-lowering.cpp | 8 ++++++-- src/rtutils.c | 4 ++++ 7 files changed, 45 insertions(+), 17 deletions(-) diff --git a/base/boot.jl b/base/boot.jl index dd5a425add0a9..fe360e128d31a 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -438,4 +438,6 @@ show(@nospecialize a) = show(STDOUT, a) print(@nospecialize a...) = print(STDOUT, a...) println(@nospecialize a...) = println(STDOUT, a...) +gcuse(@nospecialize a) = ccall(:jl_gc_use, Void, (Any,), a) + ccall(:jl_set_istopmod, Void, (Any, Bool), Core, true) diff --git a/base/strings/string.jl b/base/strings/string.jl index 4b43c2a4d7b44..42296c4fa9ad1 100644 --- a/base/strings/string.jl +++ b/base/strings/string.jl @@ -59,6 +59,13 @@ String(s::Symbol) = unsafe_string(Cstring(s)) pointer(s::String) = unsafe_convert(Ptr{UInt8}, s) pointer(s::String, i::Integer) = pointer(s)+(i-1) +function unsafe_load(s::String, i::Integer=1) + ptr = pointer(s, i) + r = unsafe_load(ptr) + Core.gcuse(s) + r +end + sizeof(s::String) = Core.sizeof(s) """ @@ -73,7 +80,7 @@ codeunit(s::AbstractString, i::Integer) @boundscheck if (i < 1) | (i > sizeof(s)) throw(BoundsError(s,i)) end - unsafe_load(pointer(s),i) + unsafe_load(s, i) end write(io::IO, s::String) = unsafe_write(io, pointer(s), reinterpret(UInt, sizeof(s))) @@ -160,26 +167,24 @@ const utf8_trailing = [ ## required core functionality ## function endof(s::String) - p = pointer(s) i = sizeof(s) - while i > 0 && is_valid_continuation(unsafe_load(p,i)) + while i > 0 && is_valid_continuation(unsafe_load(s, i)) i -= 1 end i end function length(s::String) - p = pointer(s) cnum = 0 for i = 1:sizeof(s) - cnum += !is_valid_continuation(unsafe_load(p,i)) + cnum += !is_valid_continuation(unsafe_load(s, i)) end cnum end -@noinline function slow_utf8_next(p::Ptr{UInt8}, b::UInt8, i::Int, l::Int) +@noinline function slow_utf8_next(s::String, b::UInt8, i::Int, l::Int) if is_valid_continuation(b) - throw(UnicodeError(UTF_ERR_INVALID_INDEX, i, unsafe_load(p,i))) + throw(UnicodeError(UTF_ERR_INVALID_INDEX, i, unsafe_load(s, i))) end trailing = utf8_trailing[b + 1] if l < i + trailing @@ -188,7 +193,7 @@ end c::UInt32 = 0 for j = 1:(trailing + 1) c <<= 6 - c += unsafe_load(p,i) + c += unsafe_load(s, i) i += 1 end c -= utf8_offset[trailing + 1] @@ -206,12 +211,11 @@ done(s::String, state) = state > sizeof(s) @boundscheck if (i < 1) | (i > sizeof(s)) throw(BoundsError(s,i)) end - p = pointer(s) - b = unsafe_load(p, i) + b = unsafe_load(s, i) if b < 0x80 return Char(b), i + 1 end - return slow_utf8_next(p, b, i, sizeof(s)) + return slow_utf8_next(s, b, i, sizeof(s)) end function first_utf8_byte(ch::Char) @@ -225,8 +229,7 @@ end function reverseind(s::String, i::Integer) j = sizeof(s) + 1 - i - p = pointer(s) - while is_valid_continuation(unsafe_load(p,j)) + while is_valid_continuation(unsafe_load(s, j)) j -= 1 end return j @@ -235,7 +238,7 @@ end ## overload methods for efficiency ## isvalid(s::String, i::Integer) = - (1 <= i <= sizeof(s)) && !is_valid_continuation(unsafe_load(pointer(s),i)) + (1 <= i <= sizeof(s)) && !is_valid_continuation(unsafe_load(s, i)) function getindex(s::String, r::UnitRange{Int}) isempty(r) && return "" @@ -438,7 +441,7 @@ function repeat(s::String, r::Integer) n = sizeof(s) out = _string_n(n*r) if n == 1 # common case: repeating a single ASCII char - ccall(:memset, Ptr{Void}, (Ptr{UInt8}, Cint, Csize_t), out, unsafe_load(pointer(s)), r) + ccall(:memset, Ptr{Void}, (Ptr{UInt8}, Cint, Csize_t), out, unsafe_load(s), r) else for i=1:r unsafe_copy!(pointer(out, 1+(i-1)*n), pointer(s), n) diff --git a/src/ccall.cpp b/src/ccall.cpp index 01f622623db34..046827aff214e 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -1687,6 +1687,13 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) emit_signal_fence(ctx); return ghostValue(jl_void_type); } + else if (is_libjulia_func(jl_gc_use)) { + assert(lrt == T_void); + assert(!isVa && !llvmcall && nargt == 1); + ctx.builder.CreateCall(prepare_call(gc_use_func), {decay_derived(boxed(ctx, argv[0]))}); + JL_GC_POP(); + return ghostValue(jl_void_type); + } else if (_is_libjulia_func((uintptr_t)ptls_getter, "jl_get_ptls_states")) { assert(lrt == T_pint8); assert(!isVa && !llvmcall && nargt == 0); diff --git a/src/codegen.cpp b/src/codegen.cpp index 24f62d52f04e4..2f03581ecbfd1 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -357,6 +357,7 @@ static GlobalVariable *jlgetworld_global; // placeholder functions static Function *gcroot_flush_func; +static Function *gc_use_func; static Function *except_enter_func; static Function *pointer_from_objref_func; @@ -6489,6 +6490,12 @@ static void init_julia_llvm_env(Module *m) "julia.gcroot_flush"); add_named_global(gcroot_flush_func, (void*)NULL, /*dllimport*/false); + gc_use_func = Function::Create(FunctionType::get(T_void, + ArrayRef(PointerType::get(T_jlvalue, AddressSpace::Derived)), false), + Function::ExternalLinkage, + "julia.gc_use"); + add_named_global(gc_use_func, (void*)NULL, /*dllimport*/false); + pointer_from_objref_func = Function::Create(FunctionType::get(T_pjlvalue, ArrayRef(PointerType::get(T_jlvalue, AddressSpace::Derived)), false), Function::ExternalLinkage, diff --git a/src/julia.h b/src/julia.h index 5fda2e5415f25..c56d28b5f4f8a 100644 --- a/src/julia.h +++ b/src/julia.h @@ -648,6 +648,7 @@ JL_DLLEXPORT jl_value_t *jl_gc_alloc_1w(void); JL_DLLEXPORT jl_value_t *jl_gc_alloc_2w(void); JL_DLLEXPORT jl_value_t *jl_gc_alloc_3w(void); JL_DLLEXPORT jl_value_t *jl_gc_allocobj(size_t sz); +JL_DLLEXPORT void jl_gc_use(jl_value_t *a); JL_DLLEXPORT void jl_clear_malloc_data(void); diff --git a/src/llvm-late-gc-lowering.cpp b/src/llvm-late-gc-lowering.cpp index dc77551d192c3..431c1a27550ec 100644 --- a/src/llvm-late-gc-lowering.cpp +++ b/src/llvm-late-gc-lowering.cpp @@ -320,6 +320,7 @@ struct LateLowerGCFrame: public FunctionPass { MDNode *tbaa_tag; Function *ptls_getter; Function *gc_flush_func; + Function *gc_use_func; Function *pointer_from_objref_func; Function *alloc_obj_func; Function *pool_alloc_func; @@ -745,7 +746,8 @@ State LateLowerGCFrame::LocalScan(Function &F) { } if (auto callee = CI->getCalledFunction()) { // Known functions emitted in codegen that are not safepoints - if (callee == pointer_from_objref_func || callee->getName() == "memcmp") { + if (callee == pointer_from_objref_func || callee == gc_use_func || + callee->getName() == "memcmp") { continue; } } @@ -1137,7 +1139,8 @@ bool LateLowerGCFrame::CleanupIR(Function &F) { } CallingConv::ID CC = CI->getCallingConv(); auto callee = CI->getCalledValue(); - if (gc_flush_func != nullptr && callee == gc_flush_func) { + if ((gc_flush_func != nullptr && callee == gc_flush_func) || + (gc_use_func != nullptr && callee == gc_use_func)) { /* No replacement */ } else if (pointer_from_objref_func != nullptr && callee == pointer_from_objref_func) { auto *ASCI = new AddrSpaceCastInst(CI->getOperand(0), @@ -1405,6 +1408,7 @@ static void addRetNoAlias(Function *F) bool LateLowerGCFrame::doInitialization(Module &M) { ptls_getter = M.getFunction("jl_get_ptls_states"); gc_flush_func = M.getFunction("julia.gcroot_flush"); + gc_use_func = M.getFunction("julia.gc_use"); pointer_from_objref_func = M.getFunction("julia.pointer_from_objref"); auto &ctx = M.getContext(); T_size = M.getDataLayout().getIntPtrType(ctx); diff --git a/src/rtutils.c b/src/rtutils.c index a2385e24dd364..748c308d9d0a4 100644 --- a/src/rtutils.c +++ b/src/rtutils.c @@ -305,6 +305,10 @@ JL_DLLEXPORT jl_value_t *jl_value_ptr(jl_value_t *a) { return a; } +JL_DLLEXPORT void jl_gc_use(jl_value_t *a) +{ + (void)a; +} // parsing -------------------------------------------------------------------- From 61ee70d533905318814220f0442d047a28817772 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Sat, 2 Sep 2017 21:00:10 -0400 Subject: [PATCH 242/324] Remove some leagcy gcroot codegen code The LLVM pass now figures this out automatically, so we no longer need to put it explicitly into the code. There's more cleanup to be done here, but I want to take this one step at a time, since this legacy code may have been hiding issues in the GC root placement pass. --- Makefile | 4 +-- src/ccall.cpp | 2 +- src/cgutils.cpp | 31 ++++++++------------ src/codegen.cpp | 72 +++++++++++++++++++--------------------------- src/intrinsics.cpp | 2 +- 5 files changed, 45 insertions(+), 66 deletions(-) diff --git a/Makefile b/Makefile index d34d715fcc2cf..fd04142297f50 100644 --- a/Makefile +++ b/Makefile @@ -551,9 +551,9 @@ test: check-whitespace $(JULIA_BUILD_MODE) @$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT)/test default JULIA_BUILD_MODE=$(JULIA_BUILD_MODE) ifeq ($(JULIA_BUILD_MODE),release) -JULIA_SYSIMG=$(build_private_libdir)/sys$(JULIA_LIBSUFFIX).$(SHLIB_EXT) +JULIA_SYSIMG=$(build_private_libdir)/sys$(JULIA_LIBSUFFIX)$(CPUID_TAG).$(SHLIB_EXT) else -JULIA_SYSIMG=$(build_private_libdir)/sys-$(JULIA_BUILD_MODE)$(JULIA_LIBSUFFIX).$(SHLIB_EXT) +JULIA_SYSIMG=$(build_private_libdir)/sys-$(JULIA_BUILD_MODE)$(JULIA_LIBSUFFIX)$(CPUID_TAG).$(SHLIB_EXT) endif testall: check-whitespace $(JULIA_BUILD_MODE) cp $(JULIA_SYSIMG) $(BUILDROOT)/local.$(SHLIB_EXT) && $(JULIA_EXECUTABLE) -J $(call cygpath_w,$(BUILDROOT)/local.$(SHLIB_EXT)) -e 'true' && rm $(BUILDROOT)/local.$(SHLIB_EXT) diff --git a/src/ccall.cpp b/src/ccall.cpp index 01f622623db34..4c00356b10eb4 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -1495,7 +1495,7 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) } else { Value *notany = ctx.builder.CreateICmpNE( - boxed(ctx, runtime_sp, false), + boxed(ctx, runtime_sp), maybe_decay_untracked(literal_pointer_val(ctx, (jl_value_t*)jl_any_type))); error_unless(ctx, notany, "ccall: return type Ref{Any} is invalid. use Ptr{Any} instead."); always_error = false; diff --git a/src/cgutils.cpp b/src/cgutils.cpp index ff02a8b42cdb6..6d350e9ab94a9 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -683,8 +683,7 @@ static Value *emit_typeptr_addr(jl_codectx_t &ctx, Value *p) return emit_nthptr_addr(ctx, p, -offset); } -static Value *boxed(jl_codectx_t &ctx, const jl_cgval_t &v, bool gcooted=true); -static Value *boxed(jl_codectx_t &ctx, const jl_cgval_t &v, jl_value_t* type) = delete; // C++11 (temporary to prevent rebase error) +static Value *boxed(jl_codectx_t &ctx, const jl_cgval_t &v); static Value* mask_gc_bits(jl_codectx_t &ctx, Value *tag) { @@ -713,7 +712,7 @@ static jl_cgval_t emit_typeof(jl_codectx_t &ctx, const jl_cgval_t &p) if (p.constant) return mark_julia_const(jl_typeof(p.constant)); if (p.isboxed && !jl_is_leaf_type(p.typ)) { - return mark_julia_type(ctx, emit_typeof(ctx, p.V), true, jl_datatype_type, /*needsroot*/false); + return mark_julia_type(ctx, emit_typeof(ctx, p.V), true, jl_datatype_type); } if (p.TIndex) { Value *tindex = ctx.builder.CreateAnd(p.TIndex, ConstantInt::get(T_int8, 0x7f)); @@ -746,7 +745,7 @@ static jl_cgval_t emit_typeof(jl_codectx_t &ctx, const jl_cgval_t &p) if (!allunboxed) datatype = mask_gc_bits(ctx, datatype); datatype = maybe_decay_untracked(datatype); - return mark_julia_type(ctx, datatype, true, jl_datatype_type, /*needsroot*/false); + return mark_julia_type(ctx, datatype, true, jl_datatype_type); } jl_value_t *aty = p.typ; if (jl_is_type_type(aty)) { @@ -986,7 +985,7 @@ static void emit_type_error(jl_codectx_t &ctx, const jl_cgval_t &x, Value *type, Value *msg_val = stringConstPtr(ctx.builder, msg); ctx.builder.CreateCall(prepare_call(jltypeerror_func), { fname_val, msg_val, - maybe_decay_untracked(type), mark_callee_rooted(boxed(ctx, x, false))}); + maybe_decay_untracked(type), mark_callee_rooted(boxed(ctx, x))}); } static std::pair emit_isa(jl_codectx_t &ctx, const jl_cgval_t &x, jl_value_t *type, const std::string *msg) @@ -1080,7 +1079,7 @@ static void emit_typecheck(jl_codectx_t &ctx, const jl_cgval_t &x, jl_value_t *t static void emit_leafcheck(jl_codectx_t &ctx, Value *typ, const std::string &msg) { assert(typ->getType() == T_prjlvalue); - emit_typecheck(ctx, mark_julia_type(ctx, typ, true, jl_any_type, false), (jl_value_t*)jl_datatype_type, msg); + emit_typecheck(ctx, mark_julia_type(ctx, typ, true, jl_any_type), (jl_value_t*)jl_datatype_type, msg); Value *isleaf; isleaf = ctx.builder.CreateConstInBoundsGEP1_32(T_int8, emit_bitcast(ctx, decay_derived(typ), T_pint8), offsetof(jl_datatype_t, isleaftype)); isleaf = ctx.builder.CreateLoad(isleaf, tbaa_const); @@ -1205,7 +1204,7 @@ static void typed_store(jl_codectx_t &ctx, Value *ptr, Value *idx_0based, const jl_cgval_t &rhs, jl_value_t *jltype, MDNode *tbaa, Value *parent, // for the write barrier, NULL if no barrier needed - unsigned alignment = 0, bool root_box = true) // if the value to store needs a box, should we root it ? + unsigned alignment = 0) { bool isboxed; Type *elty = julia_type_to_llvm(jltype, &isboxed); @@ -1216,7 +1215,7 @@ static void typed_store(jl_codectx_t &ctx, r = emit_unbox(ctx, elty, rhs, jltype); } else { - r = maybe_decay_untracked(boxed(ctx, rhs, root_box)); + r = maybe_decay_untracked(boxed(ctx, rhs)); if (parent != NULL) emit_write_barrier(ctx, parent, r); } @@ -1287,7 +1286,7 @@ static bool emit_getfield_unknownidx(jl_codectx_t &ctx, maybe_null, minimum_field_size)); if (maybe_null) null_pointer_check(ctx, fld); - *ret = mark_julia_type(ctx, fld, true, jl_any_type, strct.gcroot || !strct.isimmutable); + *ret = mark_julia_type(ctx, fld, true, jl_any_type); return true; } else if (is_tupletype_homogeneous(stt->types)) { @@ -1389,7 +1388,7 @@ static jl_cgval_t emit_getfield_knownidx(jl_codectx_t &ctx, const jl_cgval_t &st Value *fldv = tbaa_decorate(strct.tbaa, Load); if (maybe_null) null_pointer_check(ctx, fldv); - return mark_julia_type(ctx, fldv, true, jfty, strct.gcroot || !strct.isimmutable); + return mark_julia_type(ctx, fldv, true, jfty); } else if (jl_is_uniontype(jfty)) { int fsz = jl_field_size(jt, idx); @@ -1982,7 +1981,7 @@ static Value *box_union(jl_codectx_t &ctx, const jl_cgval_t &vinfo, const SmallB // this is used to wrap values for generic contexts, where a // dynamically-typed value is required (e.g. argument to unknown function). // if it's already a pointer it's left alone. -static Value *boxed(jl_codectx_t &ctx, const jl_cgval_t &vinfo, bool gcrooted) +static Value *boxed(jl_codectx_t &ctx, const jl_cgval_t &vinfo) { jl_value_t *jt = vinfo.typ; if (jt == jl_bottom_type || jt == NULL) @@ -2016,12 +2015,6 @@ static Value *boxed(jl_codectx_t &ctx, const jl_cgval_t &vinfo, bool gcrooted) box = maybe_decay_untracked(box); } } - if (gcrooted) { - // make a gcroot for the new box - // (unless the caller explicitly said this was unnecessary) - Value *froot = emit_local_root(ctx); - ctx.builder.CreateStore(box, froot); - } return box; } @@ -2103,7 +2096,7 @@ static void emit_unionmove(jl_codectx_t &ctx, Value *dest, const jl_cgval_t &src static void emit_cpointercheck(jl_codectx_t &ctx, const jl_cgval_t &x, const std::string &msg) { Value *t = emit_typeof_boxed(ctx, x); - emit_typecheck(ctx, mark_julia_type(ctx, t, true, jl_any_type, false), (jl_value_t*)jl_datatype_type, msg); + emit_typecheck(ctx, mark_julia_type(ctx, t, true, jl_any_type), (jl_value_t*)jl_datatype_type, msg); Value *istype = ctx.builder.CreateICmpEQ(mark_callee_rooted(emit_datatype_name(ctx, t)), @@ -2185,7 +2178,7 @@ static void emit_setfield(jl_codectx_t &ctx, ConstantInt::get(T_size, jl_field_offset(sty, idx0))); jl_value_t *jfty = jl_svecref(sty->types, idx0); if (jl_field_isptr(sty, idx0)) { - Value *r = maybe_decay_untracked(boxed(ctx, rhs, false)); // don't need a temporary gcroot since it'll be rooted by strct + Value *r = maybe_decay_untracked(boxed(ctx, rhs)); // don't need a temporary gcroot since it'll be rooted by strct tbaa_decorate(strct.tbaa, ctx.builder.CreateStore(r, emit_bitcast(ctx, addr, T_pprjlvalue))); if (wb && strct.isboxed) diff --git a/src/codegen.cpp b/src/codegen.cpp index 9e3178ce4383a..c4fac43bd68ab 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -657,7 +657,7 @@ static inline jl_cgval_t mark_julia_slot(Value *v, jl_value_t *typ, Value *tinde return tagval; } -static inline jl_cgval_t mark_julia_type(jl_codectx_t &ctx, Value *v, bool isboxed, jl_value_t *typ, bool needsroot = true) +static inline jl_cgval_t mark_julia_type(jl_codectx_t &ctx, Value *v, bool isboxed, jl_value_t *typ) { if (jl_is_datatype(typ) && jl_is_datatype_singleton((jl_datatype_t*)typ)) { // no need to explicitly load/store a constant/ghost value @@ -687,17 +687,12 @@ static inline jl_cgval_t mark_julia_type(jl_codectx_t &ctx, Value *v, bool isbox } return mark_julia_slot(loc, typ, NULL, tbaa_stack); } - Value *froot = NULL; - if (needsroot && isboxed) { - froot = emit_local_root(ctx); - ctx.builder.CreateStore(v, froot); - } - return jl_cgval_t(v, froot, isboxed, typ, NULL); + return jl_cgval_t(v, NULL, isboxed, typ, NULL); } -static inline jl_cgval_t mark_julia_type(jl_codectx_t &ctx, Value *v, bool isboxed, jl_datatype_t *typ, bool needsroot = true) +static inline jl_cgval_t mark_julia_type(jl_codectx_t &ctx, Value *v, bool isboxed, jl_datatype_t *typ) { - return mark_julia_type(ctx, v, isboxed, (jl_value_t*)typ, needsroot); + return mark_julia_type(ctx, v, isboxed, (jl_value_t*)typ); } // see if it might be profitable (and cheap) to change the type of v to typ @@ -1019,13 +1014,7 @@ static jl_cgval_t convert_julia_type(jl_codectx_t &ctx, const jl_cgval_t &v, jl_ } if (makeboxed) { // convert to a simple isboxed value - Value *boxv = boxed(ctx, v, false); - Value *froot = NULL; - if (needsroot) { - froot = emit_local_root(ctx); - ctx.builder.CreateStore(maybe_decay_untracked(boxv), froot); - } - return jl_cgval_t(boxv, froot, true, typ, NULL); + return jl_cgval_t(boxed(ctx, v), NULL, true, typ, NULL); } } return jl_cgval_t(v, typ, new_tindex); @@ -2069,8 +2058,7 @@ static jl_cgval_t emit_getfield(jl_codectx_t &ctx, const jl_cgval_t &strct, jl_s mark_julia_const((jl_value_t*)name) }; Value *result = emit_jlcall(ctx, jlgetfield_func, maybe_decay_untracked(V_null), myargs_array, 2); - bool needsgcroot = true; // !arg1.isimmutable || !jl_is_leaf_type(arg1.typ) || !is_datatype_all_pointers((jl_datatype_t*)arg1.typ); // TODO: probably want this as a llvm pass - return mark_julia_type(ctx, result, true, jl_any_type, needsgcroot); + return mark_julia_type(ctx, result, true, jl_any_type); } static Value *emit_bits_compare(jl_codectx_t &ctx, const jl_cgval_t &arg1, const jl_cgval_t &arg2) @@ -2165,8 +2153,8 @@ static Value *emit_f_is(jl_codectx_t &ctx, const jl_cgval_t &arg1, const jl_cgva // which is already enough to ensure pointer uniqueness for this test // even if the other pointer managed to get garbage collected return ctx.builder.CreateICmpEQ( - mark_callee_rooted(boxed(ctx, arg1, false)), - mark_callee_rooted(boxed(ctx, arg2, false))); + mark_callee_rooted(boxed(ctx, arg1)), + mark_callee_rooted(boxed(ctx, arg2))); } if (jl_type_intersection(rt1, rt2) == (jl_value_t*)jl_bottom_type) // types are disjoint (exhaustive test) @@ -2218,7 +2206,7 @@ static Value *emit_f_is(jl_codectx_t &ctx, const jl_cgval_t &arg1, const jl_cgva JL_FEAT_REQUIRE(ctx, runtime); Value *varg1 = mark_callee_rooted(boxed(ctx, arg1)); - Value *varg2 = mark_callee_rooted(boxed(ctx, arg2, false)); // potentially unrooted! + Value *varg2 = mark_callee_rooted(boxed(ctx, arg2)); return ctx.builder.CreateTrunc(ctx.builder.CreateCall(prepare_call(jlegal_func), {varg1, varg2}), T_int1); } @@ -2322,7 +2310,7 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, } else if (f == jl_builtin_throw && nargs == 1) { - Value *arg1 = boxed(ctx, argv[1], false); // rooted by throw + Value *arg1 = boxed(ctx, argv[1]); raise_exception(ctx, arg1); *ret = jl_cgval_t(); return true; @@ -2520,8 +2508,7 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, emit_arrayptr(ctx, ary, ary_ex, isboxed), idx, val, ety, !isboxed ? tbaa_arraybuf : tbaa_ptrarraybuf, - data_owner, 0, - false); // don't need to root the box if we had to make one since it's being stored in the array immediatly + data_owner, 0); } *ret = ary; return true; @@ -2553,7 +2540,7 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, idx = emit_bounds_check(ctx, va_ary, NULL, idx, valen, boundscheck); idx = ctx.builder.CreateAdd(idx, ConstantInt::get(T_size, ctx.nReqArgs)); Value *v = tbaa_decorate(tbaa_value, ctx.builder.CreateLoad(ctx.builder.CreateGEP(ctx.argArray, idx))); - *ret = mark_julia_type(ctx, v, /*boxed*/ true, jl_any_type, /*needsgcroot*/ false); + *ret = mark_julia_type(ctx, v, /*boxed*/ true, jl_any_type); return true; } } @@ -2812,7 +2799,7 @@ static Value *emit_jlcall(jl_codectx_t &ctx, Value *theFptr, Value *theF, if (theF) theArgs.push_back(theF); for (size_t i = 0; i < nargs; i++) { - Value *arg = maybe_decay_untracked(boxed(ctx, argv[i], false)); + Value *arg = maybe_decay_untracked(boxed(ctx, argv[i])); theArgs.push_back(arg); } SmallVector argsT; @@ -2975,7 +2962,7 @@ static jl_cgval_t emit_invoke(jl_codectx_t &ctx, jl_expr_t *ex) emit_jlcall( ctx, prepare_call(jlinvoke_func), - boxed(ctx, lival, false), + boxed(ctx, lival), argv, nargs), true, rt); @@ -3136,7 +3123,7 @@ static jl_cgval_t emit_sparam(jl_codectx_t &ctx, size_t i) maybe_decay_untracked(literal_pointer_val(ctx, (jl_value_t*)jl_tvar_type))); jl_sym_t *name = (jl_sym_t*)jl_svecref(ctx.linfo->def.method->sparam_syms, i); undef_var_error_ifnot(ctx, isnull, name); - return mark_julia_type(ctx, sp, true, jl_any_type, false); + return mark_julia_type(ctx, sp, true, jl_any_type); } static jl_cgval_t emit_global(jl_codectx_t &ctx, jl_sym_t *sym) @@ -3304,8 +3291,7 @@ static jl_cgval_t emit_local(jl_codectx_t &ctx, jl_value_t *slotload) v = update_julia_type(ctx, v, typ); } else { - v = mark_julia_type(ctx, boxed, true, typ, - /*gc-root*/!vi.isArgument); // if an argument, doesn't need an additional root + v = mark_julia_type(ctx, boxed, true, typ); if (vi.usedUndef) isnull = box_isnull; } @@ -3446,7 +3432,7 @@ static void emit_assignment(jl_codectx_t &ctx, jl_value_t *l, jl_value_t *r) if (bp != NULL) { // it's a global JL_FEAT_REQUIRE(ctx, runtime); assert(bnd); - Value *rval = mark_callee_rooted(boxed(ctx, emit_expr(ctx, r), false)); // no root needed since this is about to be assigned to a global + Value *rval = mark_callee_rooted(boxed(ctx, emit_expr(ctx, r))); ctx.builder.CreateCall(prepare_call(jlcheckassign_func), { literal_pointer_val(ctx, bnd), rval }); @@ -3520,7 +3506,7 @@ static void emit_assignment(jl_codectx_t &ctx, jl_value_t *l, jl_value_t *r) } else { assert(!vi.pTIndex || rval_info.isboxed || rval_info.constant); - rval = maybe_decay_untracked(boxed(ctx, rval_info, false)); + rval = maybe_decay_untracked(boxed(ctx, rval_info)); } ctx.builder.CreateStore(maybe_decay_untracked(rval), vi.boxroot, vi.isVolatile); } @@ -3995,7 +3981,7 @@ static void emit_cfunc_invalidate( } assert(AI == gf_thunk->arg_end()); Value *gf_ret = emit_jlcall(ctx, jlapplygeneric_func, NULL, myargs, nargs); - jl_cgval_t gf_retbox = mark_julia_type(ctx, gf_ret, true, jl_any_type, /*needsroot*/false); + jl_cgval_t gf_retbox = mark_julia_type(ctx, gf_ret, true, jl_any_type); jl_value_t *astrt = lam->rettype; if (cc != jl_returninfo_t::Boxed) { emit_typecheck(ctx, gf_retbox, astrt, "cfunction"); @@ -4362,7 +4348,7 @@ static Function *gen_cfun_wrapper(jl_function_t *ff, jl_value_t *jlrettype, jl_t if (toboxed) { assert(!sig.sret); // return a jl_value_t* - r = boxed(ctx, retval, false); // no gcroot since this is on the return path + r = boxed(ctx, retval); } else if (sig.sret && jlfunc_sret) { // nothing to do @@ -4574,10 +4560,10 @@ static Function *gen_jlcall_wrapper(jl_method_instance_t *lam, const jl_returnin jl_cgval_t retval; switch (f.cc) { case jl_returninfo_t::Boxed: - retval = mark_julia_type(ctx, call, true, jlretty, /*needsroot*/false); + retval = mark_julia_type(ctx, call, true, jlretty); break; case jl_returninfo_t::Register: - retval = mark_julia_type(ctx, call, false, jlretty, /*needsroot*/false); + retval = mark_julia_type(ctx, call, false, jlretty); break; case jl_returninfo_t::SRet: retval = mark_julia_slot(result, jlretty, NULL, tbaa_stack); @@ -4596,7 +4582,7 @@ static Function *gen_jlcall_wrapper(jl_method_instance_t *lam, const jl_returnin retval = mark_julia_slot(NULL, jlretty, call, tbaa_stack); break; } - ctx.builder.CreateRet(boxed(ctx, retval, false)); // no gcroot needed since this on the return path + ctx.builder.CreateRet(boxed(ctx, retval)); assert(!ctx.roots); return w; } @@ -5219,17 +5205,17 @@ static std::unique_ptr emit_function( Argument *Arg = &*AI++; if (isboxed) maybe_mark_argument_dereferenceable(Arg, argType); - theArg = mark_julia_type(ctx, Arg, isboxed, argType, /*needsgcroot*/false); + theArg = mark_julia_type(ctx, Arg, isboxed, argType); } } else { if (i == 0) { // first (function) arg is separate in jlcall - theArg = mark_julia_type(ctx, fArg, true, vi.value.typ, /*needsgcroot*/false); + theArg = mark_julia_type(ctx, fArg, true, vi.value.typ); } else { Value *argPtr = ctx.builder.CreateGEP(argArray, ConstantInt::get(T_size, i-1)); - theArg = mark_julia_type(ctx, ctx.builder.CreateLoad(argPtr), true, vi.value.typ, /*needsgcroot*/false); + theArg = mark_julia_type(ctx, ctx.builder.CreateLoad(argPtr), true, vi.value.typ); if (ctx.debug_enabled && vi.dinfo && !vi.boxroot && !vi.value.V) { SmallVector addr; addr.push_back(llvm::dwarf::DW_OP_deref); @@ -5267,7 +5253,7 @@ static std::unique_ptr emit_function( } } else { - Value *argp = boxed(ctx, theArg, false); // skip the temporary gcroot since it would be folded to argp anyways + Value *argp = boxed(ctx, theArg); // skip the temporary gcroot since it would be folded to argp anyways ctx.builder.CreateStore(argp, vi.boxroot); if (!theArg.isboxed) emit_local_root(ctx, &vi); // create a root for vi @@ -5563,7 +5549,7 @@ static std::unique_ptr emit_function( Type *retty = f->getReturnType(); switch (returninfo.cc) { case jl_returninfo_t::Boxed: - retval = boxed(ctx, retvalinfo, false); // skip the gcroot on the return path + retval = boxed(ctx, retvalinfo); // skip the gcroot on the return path break; case jl_returninfo_t::Register: if (type_is_ghost(retty)) @@ -5607,7 +5593,7 @@ static std::unique_ptr emit_function( //assert(retvalinfo.isboxed); tindex = compute_tindex_unboxed(ctx, retvalinfo, jlrettype); tindex = ctx.builder.CreateOr(tindex, ConstantInt::get(T_int8, 0x80)); - data = maybe_decay_untracked(boxed(ctx, retvalinfo, false)); // skip the gcroot on the return path + data = maybe_decay_untracked(boxed(ctx, retvalinfo)); sret = NULL; } retval = UndefValue::get(retty); diff --git a/src/intrinsics.cpp b/src/intrinsics.cpp index 0d5e5264685ff..98dcb81ba6738 100644 --- a/src/intrinsics.cpp +++ b/src/intrinsics.cpp @@ -664,7 +664,7 @@ static jl_cgval_t emit_pointerset(jl_codectx_t &ctx, jl_cgval_t *argv) if (ety == (jl_value_t*)jl_any_type) { // unsafe_store to Ptr{Any} is allowed to implicitly drop GC roots. Instruction *store = ctx.builder.CreateAlignedStore( - emit_pointer_from_objref(ctx, boxed(ctx, x, false)), + emit_pointer_from_objref(ctx, boxed(ctx, x)), ctx.builder.CreateGEP(thePtr, im1), align_nb); tbaa_decorate(tbaa_data, store); } else { From dd7feaeac0a0ce68b6b25bc5a026ef2723076e9c Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Sun, 20 Aug 2017 18:17:34 +0200 Subject: [PATCH 243/324] REPL: free-up '\0' as a key (sent by Ctrl-Space) '\0' was used as a sentinel for self-insert (spelled "*" in keymaps), which made it un-available for Ctrl-Space combo. We replace here '\0' with an un-assigned sentinel Char value. --- base/repl/LineEdit.jl | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/base/repl/LineEdit.jl b/base/repl/LineEdit.jl index f29956839b0da..f87117513cee0 100644 --- a/base/repl/LineEdit.jl +++ b/base/repl/LineEdit.jl @@ -731,16 +731,18 @@ end ### Keymap Support +const wildcard = Char(0x0010f7ff) # "Private Use" Char + normalize_key(key::Char) = string(key) normalize_key(key::Integer) = normalize_key(Char(key)) function normalize_key(key::AbstractString) - '\0' in key && error("Matching \\0 not currently supported.") + wildcard in key && error("Matching Char(0x0010f7ff) not supported.") buf = IOBuffer() i = start(key) while !done(key, i) c, i = next(key, i) if c == '*' - write(buf, '\0') + write(buf, wildcard) elseif c == '^' c, i = next(key, i) write(buf, uppercase(c)-64) @@ -810,19 +812,25 @@ struct KeyAlias KeyAlias(seq) = new(normalize_key(seq)) end -match_input(k::Function, s, term, cs, keymap) = (update_key_repeats(s, cs); return keymap_fcn(k, String(cs))) +function match_input(k::Function, s, term, cs, keymap) + update_key_repeats(s, cs) + return keymap_fcn(k, String(cs)) +end + match_input(k::Void, s, term, cs, keymap) = (s,p) -> return :ok -match_input(k::KeyAlias, s, term, cs, keymap) = match_input(keymap, s, IOBuffer(k.seq), Char[], keymap) +match_input(k::KeyAlias, s, term, cs, keymap) = + match_input(keymap, s, IOBuffer(k.seq), Char[], keymap) + function match_input(k::Dict, s, term=terminal(s), cs=Char[], keymap = k) # if we run out of characters to match before resolving an action, # return an empty keymap function eof(term) && return keymap_fcn(nothing, "") c = read(term, Char) - # Ignore any '\0' (eg, CTRL-space in xterm), as this is used as a + # Ignore any `wildcard` as this is used as a # placeholder for the wildcard (see normalize_key("*")) - c != '\0' || return keymap_fcn(nothing, "") + c == wildcard && return keymap_fcn(nothing, "") push!(cs, c) - key = haskey(k, c) ? c : '\0' + key = haskey(k, c) ? c : wildcard # if we don't match on the key, look for a default action then fallback on 'nothing' to ignore return match_input(get(k, key, nothing), s, term, cs, keymap) end @@ -913,12 +921,12 @@ function fixup_keymaps!(dict::Dict, level, s, subkeymap) end function add_specialisations(dict, subdict, level) - default_branch = subdict['\0'] + default_branch = subdict[wildcard] if isa(default_branch, Dict) # Go through all the keymaps in the default branch # and copy them over to dict for s in keys(default_branch) - s == '\0' && add_specialisations(dict, default_branch, level+1) + s == wildcard && add_specialisations(dict, default_branch, level+1) fixup_keymaps!(dict, level, s, default_branch[s]) end end @@ -927,11 +935,11 @@ end postprocess!(others) = nothing function postprocess!(dict::Dict) # needs to be done first for every branch - if haskey(dict, '\0') + if haskey(dict, wildcard) add_specialisations(dict, dict, 1) end for (k,v) in dict - k == '\0' && continue + k == wildcard && continue postprocess!(v) end end @@ -1015,7 +1023,7 @@ function keymap(keymaps::Array{<:Dict}) end const escape_defaults = merge!( - AnyDict(Char(i) => nothing for i=vcat(1:26, 28:31)), # Ignore control characters by default + AnyDict(Char(i) => nothing for i=vcat(0:26, 28:31)), # Ignore control characters by default AnyDict( # And ignore other escape sequences by default "\e*" => nothing, "\e[*" => nothing, From adae8305403c97ee5563a25b5f55ab73616a5a04 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Sun, 20 Aug 2017 10:26:18 +0200 Subject: [PATCH 244/324] REPL: add few "region" operations * add possibility to set the "mark" in the edit area, with C-Space * the mark and current position (the "point") delimit a region * C-x C-x exchanges the mark with the point * M-w is set to copy the region into the kill buffer * M-W is set to kill the region into the kill buffer (in Emacs, the binding for this is C-w, but it's already taken by edit_werase) --- base/repl/LineEdit.jl | 79 +++++++++++++++++++----- doc/src/manual/interacting-with-julia.md | 12 ++-- 2 files changed, 73 insertions(+), 18 deletions(-) diff --git a/base/repl/LineEdit.jl b/base/repl/LineEdit.jl index f87117513cee0..709338dc9d604 100644 --- a/base/repl/LineEdit.jl +++ b/base/repl/LineEdit.jl @@ -67,6 +67,19 @@ mutable struct PromptState <: ModeState indent::Int end +setmark(s) = mark(buffer(s)) + +# the default mark is 0 +getmark(s) = max(0, buffer(s).mark) + +# given two buffer positions delimitating a region +# as an close-open range, return a range of the included +# positions, 0-based (suitable for splice_buffer!) +region(a::Int, b::Int) = ((a, b) = minmax(a, b); a:b-1) +region(s) = region(getmark(s), position(buffer(s))) + +const REGION_ANIMATION_DURATION = Ref(0.2) + input_string(s::PromptState) = String(take!(copy(s.input_buffer))) input_string_newlines(s::PromptState) = count(c->(c == '\n'), input_string(s)) @@ -287,6 +300,17 @@ function reset_key_repeats(f::Function, s::MIState) end end +edit_exchange_point_and_mark(s::MIState) = + edit_exchange_point_and_mark(buffer(s)) && (refresh_line(s); true) + +function edit_exchange_point_and_mark(buf::IOBuffer) + m = getmark(buf) + m == position(buf) && return false + mark(buf) + seek(buf, m) + true +end + char_move_left(s::PromptState) = char_move_left(s.input_buffer) function char_move_left(buf::IOBuffer) while position(buf) > 0 @@ -431,16 +455,23 @@ end # splice! for IOBuffer: convert from 0-indexed positions, update the size, # and keep the cursor position stable with the text +# returns the removed portion as a String function splice_buffer!(buf::IOBuffer, r::UnitRange{<:Integer}, ins::AbstractString = "") pos = position(buf) - if !isempty(r) && pos in r + if pos in r seek(buf, first(r)) elseif pos > last(r) seek(buf, pos - length(r)) end - splice!(buf.data, r + 1, Vector{UInt8}(ins)) # position(), etc, are 0-indexed + if first(r) < buf.mark <= last(r) + unmark(buf) + elseif buf.mark > last(r) && !isempty(r) + buf.mark += sizeof(ins) - length(r) + end + ret = splice!(buf.data, r + 1, Vector{UInt8}(ins)) # position(), etc, are 0-indexed buf.size = buf.size + sizeof(ins) - length(r) seek(buf, position(buf) + sizeof(ins)) + String(ret) end function edit_replace(s, from, to, str) @@ -597,6 +628,26 @@ function edit_kill_line(s::MIState) refresh_line(s) end +function edit_copy_region(s::MIState) + buf = buffer(s) + if edit_exchange_point_and_mark(buf) # region non-empty + s.kill_buffer = String(buf.data[region(buf)+1]) + if REGION_ANIMATION_DURATION[] > 0.0 + refresh_line(s) + sleep(REGION_ANIMATION_DURATION[]) + end + edit_exchange_point_and_mark(s) # includes refresh_line + end +end + +function edit_kill_region(s::MIState) + reg = region(s) + if !isempty(reg) + s.kill_buffer = splice_buffer!(buffer(s), reg) + refresh_line(s) + end +end + edit_transpose_chars(s) = edit_transpose_chars(buffer(s)) && refresh_line(s) function edit_transpose_chars(buf::IOBuffer) @@ -784,20 +835,14 @@ function add_nested_key!(keymap::Dict, key, value; override = false) i = start(key) while !done(key, i) c, i = next(key, i) - if c in keys(keymap) - if done(key, i) && override - # isa(keymap[c], Dict) - In this case we're overriding a prefix of an existing command - keymap[c] = value - break - else - if !isa(keymap[c], Dict) - error("Conflicting definitions for keyseq " * escape_string(key) * " within one keymap") - end - end - elseif done(key, i) + if !override && c in keys(keymap) && (done(key, i) || !isa(keymap[c], Dict)) + error("Conflicting definitions for keyseq " * escape_string(key) * + " within one keymap") + end + if done(key, i) keymap[c] = value break - else + elseif !(c in keys(keymap) && isa(keymap[c], Dict)) keymap[c] = Dict{Char,Any}() end keymap = keymap[c] @@ -1499,6 +1544,9 @@ AnyDict( return :abort end end, + # Ctrl-Space + "\0" => (s,o...)->setmark(s), + "^X^X" => (s,o...)->edit_exchange_point_and_mark(s), "^B" => (s,o...)->edit_move_left(s), "^F" => (s,o...)->edit_move_right(s), # Meta B @@ -1521,6 +1569,8 @@ AnyDict( "^U" => (s,o...)->edit_clear(s), "^K" => (s,o...)->edit_kill_line(s), "^Y" => (s,o...)->edit_yank(s), + "\ew" => (s,o...)->edit_copy_region(s), + "\eW" => (s,o...)->edit_kill_region(s), "^A" => (s,o...)->(move_line_start(s); refresh_line(s)), "^E" => (s,o...)->(move_line_end(s); refresh_line(s)), # Try to catch all Home/End keys @@ -1725,6 +1775,7 @@ end buffer(s::PromptState) = s.input_buffer buffer(s::SearchState) = s.query_buffer buffer(s::PrefixSearchState) = s.response_buffer +buffer(s::IOBuffer) = s keymap(s::PromptState, prompt::Prompt) = prompt.keymap_dict keymap_data(s::PromptState, prompt::Prompt) = prompt.keymap_func_data diff --git a/doc/src/manual/interacting-with-julia.md b/doc/src/manual/interacting-with-julia.md index 03653d2cfeaf8..45b1129881227 100644 --- a/doc/src/manual/interacting-with-julia.md +++ b/doc/src/manual/interacting-with-julia.md @@ -160,17 +160,21 @@ to do so). | Down arrow | Move down one line (or to the next history entry) | | Page-up | Change to the previous history entry that matches the text before the cursor | | Page-down | Change to the next history entry that matches the text before the cursor | -| `meta-F` | Move right one word | -| `meta-B` | Move left one word | +| `meta-f` | Move right one word | +| `meta-b` | Move left one word | | `meta-<` | Change to the first history entry (of the current session if it is before the current position in history) | | `meta->` | Change to the last history entry | +| `^-Space` | Set the "mark" in the editing region | +| `^X^X` | Exchange the current position with the mark | | **Editing** |   | | Backspace, `^H` | Delete the previous character | | Delete, `^D` | Forward delete one character (when buffer has text) | | meta-Backspace | Delete the previous word | -| `meta-D` | Forward delete the next word | +| `meta-d` | Forward delete the next word | | `^W` | Delete previous text up to the nearest whitespace | -| `^K` | "Kill" to end of line, placing the text in a buffer | +| `meta-w` | Copy the current region in the kill buffer | +| `meta-W` | "Kill" the current region, placed the text in the kill buffer | +| `^K` | "Kill" to end of line, placing the text in the kill buffer | | `^Y` | "Yank" insert the text from the kill buffer | | `^T` | Transpose the characters about the cursor | | `meta-u` | Change the next word to uppercase | From 9cf7505c8b972da25c1b554225db2214255a6589 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Sat, 19 Aug 2017 16:12:28 +0200 Subject: [PATCH 245/324] REPL: register previous run action as symbol --- base/repl/LineEdit.jl | 109 ++++++++++++++++++++++++++++-------------- 1 file changed, 74 insertions(+), 35 deletions(-) diff --git a/base/repl/LineEdit.jl b/base/repl/LineEdit.jl index 709338dc9d604..97ea953148a88 100644 --- a/base/repl/LineEdit.jl +++ b/base/repl/LineEdit.jl @@ -43,10 +43,11 @@ mutable struct MIState aborted::Bool mode_state::Dict kill_buffer::String - previous_key::Array{Char,1} + previous_key::Vector{Char} key_repeats::Int + last_action::Symbol end -MIState(i, c, a, m) = MIState(i, c, a, m, "", Char[], 0) +MIState(i, c, a, m) = MIState(i, c, a, m, "", Char[], 0, :begin) function show(io::IO, s::MIState) print(io, "MI State (", s.current_mode, " active)") @@ -103,13 +104,20 @@ complete_line(c::EmptyCompletionProvider, s) = [], true, true terminal(s::IO) = s terminal(s::PromptState) = s.terminal -for f in [:terminal, :edit_insert, :edit_insert_newline, :on_enter, :add_history, - :buffer, :edit_backspace, :(Base.isempty), :replace_line, :refresh_multi_line, - :input_string, :edit_move_left, :edit_move_right, - :edit_move_word_left, :edit_move_word_right, :update_display_buffer] +for f in [:terminal, :on_enter, :add_history, :buffer, :(Base.isempty), + :replace_line, :refresh_multi_line, :input_string, :update_display_buffer] @eval ($f)(s::MIState, args...) = $(f)(s.mode_state[s.current_mode], args...) end +for f in [:edit_insert, :edit_insert_newline, :edit_backspace, :edit_move_left, + :edit_move_right, :edit_move_word_left, :edit_move_word_right] + @eval function ($f)(s::MIState, args...) + $(f)(s.mode_state[s.current_mode], args...) + return $(Expr(:quote, f)) + end +end + + function common_prefix(completions) ret = "" c1 = completions[1] @@ -153,7 +161,12 @@ function show_completions(s::PromptState, completions) end # Prompt Completions -complete_line(s::MIState) = complete_line(s.mode_state[s.current_mode], s.key_repeats) +function complete_line(s::MIState) + complete_line(s.mode_state[s.current_mode], s.key_repeats) + refresh_line(s) + :complete_line +end + function complete_line(s::PromptState, repeats) completions, partial, should_complete = complete_line(s.p.complete, s) if isempty(completions) @@ -564,7 +577,9 @@ function edit_backspace(buf::IOBuffer, align::Bool=false, adjust::Bool=align) return true end -edit_delete(s) = edit_delete(buffer(s)) ? refresh_line(s) : beep(terminal(s)) +edit_delete(s::MIState) = (edit_delete(buffer(s)) ? refresh_line(s) : beep(terminal(s)); + :edit_delete) + function edit_delete(buf::IOBuffer) eof(buf) && return false oldpos = position(buf) @@ -581,9 +596,7 @@ function edit_werase(buf::IOBuffer) splice_buffer!(buf, pos0:pos1-1) true end -function edit_werase(s) - edit_werase(buffer(s)) && refresh_line(s) -end +edit_werase(s::MIState) = (edit_werase(buffer(s)) && refresh_line(s); :edit_werase) function edit_delete_prev_word(buf::IOBuffer) pos1 = position(buf) @@ -593,8 +606,9 @@ function edit_delete_prev_word(buf::IOBuffer) splice_buffer!(buf, pos0:pos1-1) true end -function edit_delete_prev_word(s) +function edit_delete_prev_word(s::MIState) edit_delete_prev_word(buffer(s)) && refresh_line(s) + :edit_delete_prev_word end function edit_delete_next_word(buf::IOBuffer) @@ -605,13 +619,16 @@ function edit_delete_next_word(buf::IOBuffer) splice_buffer!(buf, pos0:pos1-1) true end + function edit_delete_next_word(s) edit_delete_next_word(buffer(s)) && refresh_line(s) + :edit_delete_next_word end function edit_yank(s::MIState) edit_insert(buffer(s), s.kill_buffer) refresh_line(s) + :edit_yank end function edit_kill_line(s::MIState) @@ -622,10 +639,14 @@ function edit_kill_line(s::MIState) killbuf = killbuf[1:end-1] char_move_left(buf) end - s.kill_buffer = s.key_repeats > 0 ? s.kill_buffer * killbuf : killbuf - - splice_buffer!(buf, pos:position(buf)-1) - refresh_line(s) + if !isempty(killbuf) + s.kill_buffer = s.key_repeats > 0 ? s.kill_buffer * killbuf : killbuf + splice_buffer!(buf, pos:position(buf)-1) + refresh_line(s) + :edit_kill_line + else + :ignore + end end function edit_copy_region(s::MIState) @@ -637,6 +658,9 @@ function edit_copy_region(s::MIState) sleep(REGION_ANIMATION_DURATION[]) end edit_exchange_point_and_mark(s) # includes refresh_line + :edit_copy_region + else + :ignore end end @@ -645,10 +669,16 @@ function edit_kill_region(s::MIState) if !isempty(reg) s.kill_buffer = splice_buffer!(buffer(s), reg) refresh_line(s) + :edit_kill_region + else + :ignore end end -edit_transpose_chars(s) = edit_transpose_chars(buffer(s)) && refresh_line(s) +function edit_transpose_chars(s::MIState) + edit_transpose_chars(buffer(s)) && refresh_line(s) + :edit_transpose +end function edit_transpose_chars(buf::IOBuffer) position(buf) == 0 && return false @@ -661,7 +691,10 @@ function edit_transpose_chars(buf::IOBuffer) return true end -edit_transpose_words(s) = edit_transpose_words(buffer(s)) && refresh_line(s) +function edit_transpose_words(s) + edit_transpose_words(buffer(s)) && refresh_line(s) + :edit_transpose_words +end function edit_transpose_words(buf::IOBuffer, mode=:emacs) mode in [:readline, :emacs] || @@ -687,9 +720,9 @@ function edit_transpose_words(buf::IOBuffer, mode=:emacs) end -edit_upper_case(s) = edit_replace_word_right(s, uppercase) -edit_lower_case(s) = edit_replace_word_right(s, lowercase) -edit_title_case(s) = edit_replace_word_right(s, ucfirst) +edit_upper_case(s) = (edit_replace_word_right(s, uppercase); :edit_upper_case) +edit_lower_case(s) = (edit_replace_word_right(s, lowercase); :edit_lower_case) +edit_title_case(s) = (edit_replace_word_right(s, ucfirst); :edit_title_case) edit_replace_word_right(s, replace::Function) = edit_replace_word_right(buffer(s), replace) && refresh_line(s) @@ -711,6 +744,7 @@ edit_clear(buf::IOBuffer) = truncate(buf, 0) function edit_clear(s::MIState) edit_clear(buffer(s)) refresh_line(s) + :edit_clear end function replace_line(s::PromptState, l::IOBuffer) @@ -1429,11 +1463,13 @@ function move_line_start(s::MIState) else seek(buf, rsearch(buf.data, '\n', curpos)) end + :move_line_start end function move_line_end(s::MIState) s.key_repeats > 0 ? move_input_end(s) : move_line_end(buffer(s)) + :move_line_end end function move_line_end(buf::IOBuffer) eof(buf) && return @@ -1491,14 +1527,17 @@ end # jump_spaces: if cursor is on a ' ', move it to the first non-' ' char on the right # if `delete_trailing`, ignore trailing ' ' by deleting them -function edit_tab(s, jump_spaces=false, delete_trailing=jump_spaces) - tab_should_complete(s) ? - complete_line(s) : - edit_tab(buffer(s), jump_spaces, delete_trailing) - refresh_line(s) +function edit_tab(s::MIState, jump_spaces=false, delete_trailing=jump_spaces) + if tab_should_complete(s) + complete_line(s) + else + edit_insert_tab(buffer(s), jump_spaces, delete_trailing) + refresh_line(s) + :edit_insert_tab + end end -function edit_tab(buf::IOBuffer, jump_spaces=false, delete_trailing=jump_spaces) +function edit_insert_tab(buf::IOBuffer, jump_spaces=false, delete_trailing=jump_spaces) i = position(buf) if jump_spaces && i < buf.size && buf.data[i+1] == _space spaces = findnext(_notspace, buf.data[i+1:buf.size], 1) @@ -1793,29 +1832,28 @@ function prompt!(term, prompt, s = init_state(term, prompt)) kmap = keymap(s, prompt) fcn = match_input(kmap, s) kdata = keymap_data(s, prompt) + local action # errors in keymaps shouldn't cause the REPL to fail, so wrap in a # try/catch block - local state try - state = fcn(s, kdata) + action = fcn(s, kdata) catch e bt = catch_backtrace() warn(e, bt = bt, prefix = "ERROR (in the keymap): ") # try to cleanup and get `s` back to its original state before returning transition(s, :reset) transition(s, old_state) - state = :done + action = :done end - if state === :abort + action != :ignore && (s.last_action = action) + if action === :abort return buffer(s), false, false - elseif state === :done + elseif action === :done return buffer(s), true, false - elseif state === :suspend + elseif action === :suspend if Sys.isunix() return buffer(s), true, true end - else - @assert state === :ok end end finally @@ -1823,4 +1861,5 @@ function prompt!(term, prompt, s = init_state(term, prompt)) end end + end # module From c2cbef268f65729d81e1bd97bde07650a5a1474c Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Sun, 20 Aug 2017 19:33:10 +0200 Subject: [PATCH 246/324] REPL: implement a kill ring (fix part of #8447) After a yank ("^Y"), it's possible to "yank-pop" with the "M-y" binding, which replaces the just-yanked text with an older entry from the "kill-ring". --- base/repl/LineEdit.jl | 82 +++++++++++++++--------- doc/src/manual/interacting-with-julia.md | 9 +-- 2 files changed, 57 insertions(+), 34 deletions(-) diff --git a/base/repl/LineEdit.jl b/base/repl/LineEdit.jl index 97ea953148a88..9045d2cf67cd6 100644 --- a/base/repl/LineEdit.jl +++ b/base/repl/LineEdit.jl @@ -37,17 +37,22 @@ end show(io::IO, x::Prompt) = show(io, string("Prompt(\"", prompt_string(x.prompt), "\",...)")) +"Maximum number of entries in the kill ring queue. +Beyond this number, oldest entries are discarded first." +const KILL_RING_MAX = Ref(100) + mutable struct MIState interface::ModalInterface current_mode::TextInterface aborted::Bool mode_state::Dict - kill_buffer::String + kill_ring::Vector{String} + kill_idx::Int previous_key::Vector{Char} key_repeats::Int last_action::Symbol end -MIState(i, c, a, m) = MIState(i, c, a, m, "", Char[], 0, :begin) +MIState(i, c, a, m) = MIState(i, c, a, m, String[], 0, Char[], 0, :begin) function show(io::IO, s::MIState) print(io, "MI State (", s.current_mode, " active)") @@ -477,7 +482,7 @@ function splice_buffer!(buf::IOBuffer, r::UnitRange{<:Integer}, ins::AbstractStr seek(buf, pos - length(r)) end if first(r) < buf.mark <= last(r) - unmark(buf) + buf.mark = first(r) elseif buf.mark > last(r) && !isempty(r) buf.mark += sizeof(ins) - length(r) end @@ -626,11 +631,40 @@ function edit_delete_next_word(s) end function edit_yank(s::MIState) - edit_insert(buffer(s), s.kill_buffer) + if isempty(s.kill_ring) + beep(terminal(s)) + return :ignore + end + setmark(s) # necessary for edit_yank_pop + edit_insert(buffer(s), s.kill_ring[mod1(s.kill_idx, end)]) refresh_line(s) :edit_yank end +function edit_yank_pop(s::MIState, require_previous_yank=true) + if require_previous_yank && !(s.last_action in [:edit_yank, :edit_yank_pop]) || + isempty(s.kill_ring) + beep(terminal(s)) + :ignore + else + splice_buffer!(buffer(s), region(s), s.kill_ring[mod1(s.kill_idx-=1, end)]) + refresh_line(s) + :edit_yank_pop + end +end + +function push_kill!(s::MIState, killed::String, concat=false) + isempty(killed) && return false + if concat + s.kill_ring[end] *= killed + else + push!(s.kill_ring, killed) + length(s.kill_ring) > KILL_RING_MAX[] && shift!(s.kill_ring) + end + s.kill_idx = endof(s.kill_ring) + true +end + function edit_kill_line(s::MIState) buf = buffer(s) pos = position(buf) @@ -639,40 +673,27 @@ function edit_kill_line(s::MIState) killbuf = killbuf[1:end-1] char_move_left(buf) end - if !isempty(killbuf) - s.kill_buffer = s.key_repeats > 0 ? s.kill_buffer * killbuf : killbuf - splice_buffer!(buf, pos:position(buf)-1) - refresh_line(s) - :edit_kill_line - else - :ignore - end + push_kill!(s, killbuf, s.key_repeats > 0) || return :ignore + splice_buffer!(buf, pos:position(buf)-1) + refresh_line(s) + :edit_kill_line end function edit_copy_region(s::MIState) buf = buffer(s) - if edit_exchange_point_and_mark(buf) # region non-empty - s.kill_buffer = String(buf.data[region(buf)+1]) - if REGION_ANIMATION_DURATION[] > 0.0 - refresh_line(s) - sleep(REGION_ANIMATION_DURATION[]) - end - edit_exchange_point_and_mark(s) # includes refresh_line - :edit_copy_region - else - :ignore + push_kill!(s, String(buf.data[region(buf)+1])) || return :ignore + if REGION_ANIMATION_DURATION[] > 0.0 + edit_exchange_point_and_mark(s) + sleep(REGION_ANIMATION_DURATION[]) + edit_exchange_point_and_mark(s) end + :edit_copy_region end function edit_kill_region(s::MIState) - reg = region(s) - if !isempty(reg) - s.kill_buffer = splice_buffer!(buffer(s), reg) - refresh_line(s) - :edit_kill_region - else - :ignore - end + push_kill!(s, splice_buffer!(buffer(s), region(s))) || return :ignore + refresh_line(s) + :edit_kill_region end function edit_transpose_chars(s::MIState) @@ -1608,6 +1629,7 @@ AnyDict( "^U" => (s,o...)->edit_clear(s), "^K" => (s,o...)->edit_kill_line(s), "^Y" => (s,o...)->edit_yank(s), + "\ey" => (s,o...)->edit_yank_pop(s), "\ew" => (s,o...)->edit_copy_region(s), "\eW" => (s,o...)->edit_kill_region(s), "^A" => (s,o...)->(move_line_start(s); refresh_line(s)), diff --git a/doc/src/manual/interacting-with-julia.md b/doc/src/manual/interacting-with-julia.md index 45b1129881227..caeb91f2e0e2e 100644 --- a/doc/src/manual/interacting-with-julia.md +++ b/doc/src/manual/interacting-with-julia.md @@ -172,10 +172,11 @@ to do so). | meta-Backspace | Delete the previous word | | `meta-d` | Forward delete the next word | | `^W` | Delete previous text up to the nearest whitespace | -| `meta-w` | Copy the current region in the kill buffer | -| `meta-W` | "Kill" the current region, placed the text in the kill buffer | -| `^K` | "Kill" to end of line, placing the text in the kill buffer | -| `^Y` | "Yank" insert the text from the kill buffer | +| `meta-w` | Copy the current region in the kill ring | +| `meta-W` | "Kill" the current region, placing the text in the kill ring | +| `^K` | "Kill" to end of line, placing the text in the kill ring | +| `^Y` | "Yank" insert the text from the kill ring | +| `meta-y` | Replace a previously yanked text with an older entry from the kill ring | | `^T` | Transpose the characters about the cursor | | `meta-u` | Change the next word to uppercase | | `meta-c` | Change the next word to titlecase | From 2d962c1ee62c9bead4b3e4b619cb72893900543d Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Sun, 20 Aug 2017 22:18:53 +0200 Subject: [PATCH 247/324] REPL: specify regions with open-close ranges using Pairs All call sites of `splice_buffer!` were manually substracting 1 to the last position of the range, indicating that it's more natural to specify a region with an open-close range. Therefore, `splice_buffer!` and `edit_replace` (thin wrapper of `splice_buffer!`) are merged into `edit_splice!`, which takes a `Region` (a pair of integers) object instead of a range. --- base/precompile.jl | 2 +- base/repl/LineEdit.jl | 103 ++++++++++++++++++++++-------------------- 2 files changed, 56 insertions(+), 49 deletions(-) diff --git a/base/precompile.jl b/base/precompile.jl index 03a87add90607..1f41808501d51 100644 --- a/base/precompile.jl +++ b/base/precompile.jl @@ -492,7 +492,7 @@ precompile(Tuple{typeof(Base.read), Base.TTY, Type{UInt8}}) precompile(Tuple{typeof(Base.throw_boundserror), Array{UInt8, 1}, Tuple{Base.UnitRange{Int64}}}) precompile(Tuple{typeof(Base.deleteat!), Array{UInt8, 1}, Base.UnitRange{Int64}}) precompile(Tuple{typeof(Base.splice!), Array{UInt8, 1}, Base.UnitRange{Int64}, Array{UInt8, 1}}) -precompile(Tuple{typeof(Base.LineEdit.splice_buffer!), Base.GenericIOBuffer{Array{UInt8, 1}}, Base.UnitRange{Int64}, String}) +precompile(Tuple{typeof(Base.LineEdit.edit_splice!), Base.GenericIOBuffer{Array{UInt8, 1}}, Base.Pair{Int,Int}, String}) precompile(Tuple{typeof(Base.LineEdit.refresh_multi_line), Base.Terminals.TerminalBuffer, Base.LineEdit.PromptState}) precompile(Tuple{typeof(Base.LineEdit.edit_insert), Base.GenericIOBuffer{Array{UInt8, 1}}, String}) precompile(Tuple{typeof(Base.LineEdit.edit_insert), Base.LineEdit.PromptState, String}) diff --git a/base/repl/LineEdit.jl b/base/repl/LineEdit.jl index 9045d2cf67cd6..8a448187f6fd7 100644 --- a/base/repl/LineEdit.jl +++ b/base/repl/LineEdit.jl @@ -7,7 +7,7 @@ using ..Terminals import ..Terminals: raw!, width, height, cmove, getX, getY, clear_line, beep -import Base: ensureroom, peek, show, AnyDict +import Base: ensureroom, peek, show, AnyDict, position abstract type TextInterface end abstract type ModeState end @@ -78,11 +78,14 @@ setmark(s) = mark(buffer(s)) # the default mark is 0 getmark(s) = max(0, buffer(s).mark) -# given two buffer positions delimitating a region -# as an close-open range, return a range of the included -# positions, 0-based (suitable for splice_buffer!) -region(a::Int, b::Int) = ((a, b) = minmax(a, b); a:b-1) -region(s) = region(getmark(s), position(buffer(s))) +const Region = Pair{<:Integer,<:Integer} + +_region(s) = getmark(s) => position(s) +region(s) = Pair(extrema(_region(s))...) + +indexes(reg::Region) = first(reg)+1:last(reg) + +content(s, reg::Region = 0=>buffer(s).size) = String(buffer(s).data[indexes(reg)]) const REGION_ANIMATION_DURATION = Ref(0.2) @@ -92,7 +95,7 @@ input_string_newlines(s::PromptState) = count(c->(c == '\n'), input_string(s)) function input_string_newlines_aftercursor(s::PromptState) str = input_string(s) isempty(str) && return 0 - rest = str[nextind(str, position(s.input_buffer)):end] + rest = str[nextind(str, position(s)):end] return count(c->(c == '\n'), rest) end @@ -182,17 +185,17 @@ function complete_line(s::PromptState, repeats) show_completions(s, completions) elseif length(completions) == 1 # Replace word by completion - prev_pos = position(s.input_buffer) + prev_pos = position(s) seek(s.input_buffer, prev_pos-sizeof(partial)) - edit_replace(s, position(s.input_buffer), prev_pos, completions[1]) + edit_splice!(s, position(s) => prev_pos, completions[1]) else p = common_prefix(completions) if !isempty(p) && p != partial # All possible completions share the same prefix, so we might as # well complete that - prev_pos = position(s.input_buffer) + prev_pos = position(s) seek(s.input_buffer, prev_pos-sizeof(partial)) - edit_replace(s, position(s.input_buffer), prev_pos, p) + edit_splice!(s, position(s) => prev_pos, p) elseif repeats > 0 show_completions(s, completions) end @@ -358,7 +361,7 @@ end edit_move_left(s::PromptState) = edit_move_left(s.input_buffer) && refresh_line(s) function edit_move_word_left(s) - if position(s.input_buffer) > 0 + if position(s) > 0 char_move_word_left(s.input_buffer) refresh_line(s) end @@ -429,7 +432,7 @@ function edit_move_up(buf::IOBuffer) npos = rsearch(buf.data, '\n', position(buf)) npos == 0 && return false # we're in the first line # We're interested in character count, not byte count - offset = length(String(buf.data[(npos+1):(position(buf))])) + offset = length(content(buf, npos => position(buf))) npos2 = rsearch(buf.data, '\n', npos-1) seek(buf, npos2) for _ = 1:offset @@ -471,30 +474,31 @@ function edit_move_down(s) changed end -# splice! for IOBuffer: convert from 0-indexed positions, update the size, -# and keep the cursor position stable with the text +# splice! for IOBuffer: convert from close-open region to index, update the size, +# and keep the cursor position and mark stable with the text # returns the removed portion as a String -function splice_buffer!(buf::IOBuffer, r::UnitRange{<:Integer}, ins::AbstractString = "") +function edit_splice!(s, r::Region=region(s), ins::AbstractString = "") + A, B = first(r), last(r) + A >= B && isempty(ins) && return String(ins) + buf = buffer(s) pos = position(buf) - if pos in r - seek(buf, first(r)) - elseif pos > last(r) - seek(buf, pos - length(r)) - end - if first(r) < buf.mark <= last(r) - buf.mark = first(r) - elseif buf.mark > last(r) && !isempty(r) - buf.mark += sizeof(ins) - length(r) - end - ret = splice!(buf.data, r + 1, Vector{UInt8}(ins)) # position(), etc, are 0-indexed - buf.size = buf.size + sizeof(ins) - length(r) + if A <= pos < B + seek(buf, A) + elseif B <= pos + seek(buf, pos - B + A) + end + if A < buf.mark < B + buf.mark = A + elseif A < B <= buf.mark + buf.mark += sizeof(ins) - B + A + end + ret = splice!(buf.data, A+1:B, Vector{UInt8}(ins)) # position(), etc, are 0-indexed + buf.size = buf.size + sizeof(ins) - B + A seek(buf, position(buf) + sizeof(ins)) String(ret) end -function edit_replace(s, from, to, str) - splice_buffer!(buffer(s), from:to-1, str) -end +edit_splice!(s, ins::AbstractString) = edit_splice!(s, region(s), ins) function edit_insert(s::PromptState, c) buf = s.input_buffer @@ -518,7 +522,7 @@ function edit_insert(buf::IOBuffer, c) return write(buf, c) else s = string(c) - splice_buffer!(buf, position(buf):position(buf)-1, s) + edit_splice!(buf, position(buf) => position(buf), s) return sizeof(s) end end @@ -578,7 +582,7 @@ function edit_backspace(buf::IOBuffer, align::Bool=false, adjust::Bool=align) end end end - splice_buffer!(buf, newpos:oldpos-1) + edit_splice!(buf, newpos => oldpos) return true end @@ -589,7 +593,7 @@ function edit_delete(buf::IOBuffer) eof(buf) && return false oldpos = position(buf) char_move_right(buf) - splice_buffer!(buf, oldpos:position(buf)-1) + edit_splice!(buf, oldpos => position(buf)) true end @@ -598,9 +602,10 @@ function edit_werase(buf::IOBuffer) char_move_word_left(buf, isspace) pos0 = position(buf) pos0 < pos1 || return false - splice_buffer!(buf, pos0:pos1-1) + edit_splice!(buf, pos0 => pos1) true end + edit_werase(s::MIState) = (edit_werase(buffer(s)) && refresh_line(s); :edit_werase) function edit_delete_prev_word(buf::IOBuffer) @@ -608,9 +613,10 @@ function edit_delete_prev_word(buf::IOBuffer) char_move_word_left(buf) pos0 = position(buf) pos0 < pos1 || return false - splice_buffer!(buf, pos0:pos1-1) + edit_splice!(buf, pos0 => pos1) true end + function edit_delete_prev_word(s::MIState) edit_delete_prev_word(buffer(s)) && refresh_line(s) :edit_delete_prev_word @@ -621,7 +627,7 @@ function edit_delete_next_word(buf::IOBuffer) char_move_word_right(buf) pos1 = position(buf) pos0 < pos1 || return false - splice_buffer!(buf, pos0:pos1-1) + edit_splice!(buf, pos0 => pos1) true end @@ -647,7 +653,7 @@ function edit_yank_pop(s::MIState, require_previous_yank=true) beep(terminal(s)) :ignore else - splice_buffer!(buffer(s), region(s), s.kill_ring[mod1(s.kill_idx-=1, end)]) + edit_splice!(s, s.kill_ring[mod1(s.kill_idx-=1, end)]) refresh_line(s) :edit_yank_pop end @@ -674,14 +680,14 @@ function edit_kill_line(s::MIState) char_move_left(buf) end push_kill!(s, killbuf, s.key_repeats > 0) || return :ignore - splice_buffer!(buf, pos:position(buf)-1) + edit_splice!(buf, pos => position(buf)) refresh_line(s) :edit_kill_line end function edit_copy_region(s::MIState) buf = buffer(s) - push_kill!(s, String(buf.data[region(buf)+1])) || return :ignore + push_kill!(s, content(buf, region(buf))) || return :ignore if REGION_ANIMATION_DURATION[] > 0.0 edit_exchange_point_and_mark(s) sleep(REGION_ANIMATION_DURATION[]) @@ -691,7 +697,7 @@ function edit_copy_region(s::MIState) end function edit_kill_region(s::MIState) - push_kill!(s, splice_buffer!(buffer(s), region(s))) || return :ignore + push_kill!(s, edit_splice!(s)) || return :ignore refresh_line(s) :edit_kill_region end @@ -734,8 +740,8 @@ function edit_transpose_words(buf::IOBuffer, mode=:emacs) char_move_word_right(buf) e1 = position(buf) e1 >= b2 && (seek(buf, pos); return false) - word2 = splice!(buf.data, b2+1:e2, buf.data[b1+1:e1]) - splice!(buf.data, b1+1:e1, word2) + word2 = edit_splice!(buf, b2 => e2, content(buf, b1 => e1)) + edit_splice!(buf, b1 => e1, word2) seek(buf, e2) true end @@ -755,8 +761,7 @@ function edit_replace_word_right(buf::IOBuffer, replace::Function) char_move_word_right(buf) e = position(buf) e == b && return false - newstr = replace(String(buf.data[b+1:e])) - splice_buffer!(buf, b:e-1, newstr) + edit_splice!(buf, b => e, replace(content(buf, b => e))) true end @@ -1323,9 +1328,9 @@ function complete_line(s::SearchState, repeats) completions, partial, should_complete = complete_line(s.histprompt.complete, s) # For now only allow exact completions in search mode if length(completions) == 1 - prev_pos = position(s.query_buffer) + prev_pos = position(s) seek(s.query_buffer, prev_pos-sizeof(partial)) - edit_replace(s, position(s.query_buffer), prev_pos, completions[1]) + edit_splice!(s, position(s) => prev_pos, completions[1]) end end @@ -1563,7 +1568,7 @@ function edit_insert_tab(buf::IOBuffer, jump_spaces=false, delete_trailing=jump_ if jump_spaces && i < buf.size && buf.data[i+1] == _space spaces = findnext(_notspace, buf.data[i+1:buf.size], 1) if delete_trailing && (spaces == 0 || buf.data[i+spaces] == _newline) - splice_buffer!(buf, i:(spaces == 0 ? buf.size-1 : i+spaces-2)) + edit_splice!(buf, i => (spaces == 0 ? buf.size : i+spaces-1)) else jump = spaces == 0 ? buf.size : i+spaces-1 return seek(buf, jump) @@ -1838,6 +1843,8 @@ buffer(s::SearchState) = s.query_buffer buffer(s::PrefixSearchState) = s.response_buffer buffer(s::IOBuffer) = s +position(s::Union{MIState,ModeState}) = position(buffer(s)) + keymap(s::PromptState, prompt::Prompt) = prompt.keymap_dict keymap_data(s::PromptState, prompt::Prompt) = prompt.keymap_func_data keymap(ms::MIState, m::ModalInterface) = keymap(ms.mode_state[ms.current_mode], ms.current_mode) From 8422d505da28db963bb2bd891223391638e1ff9c Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Thu, 24 Aug 2017 17:53:41 +0200 Subject: [PATCH 248/324] REPL: add tests for the kill ring --- test/lineedit.jl | 196 +++++++++++++++++++++++++++-------------------- 1 file changed, 112 insertions(+), 84 deletions(-) diff --git a/test/lineedit.jl b/test/lineedit.jl index 8bbf881d7adb4..cb275d37d4da1 100644 --- a/test/lineedit.jl +++ b/test/lineedit.jl @@ -1,9 +1,32 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license using Base.LineEdit +using Base.LineEdit: edit_insert, buffer, content, setmark, getmark + isdefined(Main, :TestHelpers) || @eval Main include(joinpath(dirname(@__FILE__), "TestHelpers.jl")) using TestHelpers +# no need to have animation in tests +LineEdit.REGION_ANIMATION_DURATION[] = 0.001 + +## helper functions + +function new_state() + term = TestHelpers.FakeTerminal(IOBuffer(), IOBuffer(), IOBuffer()) + LineEdit.init_state(term, ModalInterface([Prompt("test> ")])) +end + +charseek(buf, i) = seek(buf, Base.unsafe_chr2ind(content(buf), i+1)-1) +charpos(buf, pos=position(buf)) = Base.unsafe_ind2chr(content(buf), pos+1)-1 + +function transform!(f, s, i = -1) # i is char-based (not bytes) buffer position + buf = buffer(s) + i >= 0 && charseek(buf, i) + f(s) + content(buf), charpos(buf), charpos(buf, getmark(buf)) +end + + function run_test(d,buf) global a_foo, b_foo, a_bar, b_bar a_foo = b_foo = a_bar = b_bar = 0 @@ -277,25 +300,25 @@ seekend(buf) @test LineEdit.edit_delete_prev_word(buf) @test position(buf) == 5 @test buf.size == 5 -@test String(buf.data[1:buf.size]) == "type " +@test content(buf) == "type " buf = IOBuffer("4 +aaa+ x") seek(buf,8) @test LineEdit.edit_delete_prev_word(buf) @test position(buf) == 3 @test buf.size == 4 -@test String(buf.data[1:buf.size]) == "4 +x" +@test content(buf) == "4 +x" buf = IOBuffer("x = func(arg1,arg2 , arg3)") seekend(buf) LineEdit.char_move_word_left(buf) @test position(buf) == 21 @test LineEdit.edit_delete_prev_word(buf) -@test String(buf.data[1:buf.size]) == "x = func(arg1,arg3)" +@test content(buf) == "x = func(arg1,arg3)" @test LineEdit.edit_delete_prev_word(buf) -@test String(buf.data[1:buf.size]) == "x = func(arg3)" +@test content(buf) == "x = func(arg3)" @test LineEdit.edit_delete_prev_word(buf) -@test String(buf.data[1:buf.size]) == "x = arg3)" +@test content(buf) == "x = arg3)" # Unicode combining characters let buf = IOBuffer() @@ -305,7 +328,7 @@ let buf = IOBuffer() LineEdit.edit_move_right(buf) @test nb_available(buf) == 0 LineEdit.edit_backspace(buf) - @test String(buf.data[1:buf.size]) == "a" + @test content(buf) == "a" end ## edit_transpose_chars ## @@ -313,45 +336,41 @@ let buf = IOBuffer() LineEdit.edit_insert(buf, "abcde") seek(buf,0) LineEdit.edit_transpose_chars(buf) - @test String(buf.data[1:buf.size]) == "abcde" + @test content(buf) == "abcde" LineEdit.char_move_right(buf) LineEdit.edit_transpose_chars(buf) - @test String(buf.data[1:buf.size]) == "bacde" + @test content(buf) == "bacde" LineEdit.edit_transpose_chars(buf) - @test String(buf.data[1:buf.size]) == "bcade" + @test content(buf) == "bcade" seekend(buf) LineEdit.edit_transpose_chars(buf) - @test String(buf.data[1:buf.size]) == "bcaed" + @test content(buf) == "bcaed" LineEdit.edit_transpose_chars(buf) - @test String(buf.data[1:buf.size]) == "bcade" + @test content(buf) == "bcade" seek(buf, 0) LineEdit.edit_clear(buf) LineEdit.edit_insert(buf, "αβγδε") seek(buf,0) LineEdit.edit_transpose_chars(buf) - @test String(buf.data[1:buf.size]) == "αβγδε" + @test content(buf) == "αβγδε" LineEdit.char_move_right(buf) LineEdit.edit_transpose_chars(buf) - @test String(buf.data[1:buf.size]) == "βαγδε" + @test content(buf) == "βαγδε" LineEdit.edit_transpose_chars(buf) - @test String(buf.data[1:buf.size]) == "βγαδε" + @test content(buf) == "βγαδε" seekend(buf) LineEdit.edit_transpose_chars(buf) - @test String(buf.data[1:buf.size]) == "βγαεδ" + @test content(buf) == "βγαεδ" LineEdit.edit_transpose_chars(buf) - @test String(buf.data[1:buf.size]) == "βγαδε" + @test content(buf) == "βγαδε" end @testset "edit_word_transpose" begin buf = IOBuffer() mode = Ref{Symbol}() - function transpose!(i) # i: char indice - seek(buf, Base.unsafe_chr2ind(String(take!(copy(buf))), i+1)-1) - LineEdit.edit_transpose_words(buf, mode[]) - str = String(take!(copy(buf))) - str, Base.unsafe_ind2chr(str, position(buf)+1)-1 - end + transpose!(i) = transform!(buf -> LineEdit.edit_transpose_words(buf, mode[]), + buf, i)[1:2] mode[] = :readline LineEdit.edit_insert(buf, "àbç def gh ") @@ -379,13 +398,11 @@ end @test transpose!(13) == ("àbç gh def", 13) end -let - term = TestHelpers.FakeTerminal(IOBuffer(), IOBuffer(), IOBuffer()) - s = LineEdit.init_state(term, ModalInterface([Prompt("test> ")])) - buf = LineEdit.buffer(s) +let s = new_state() + buf = buffer(s) LineEdit.edit_insert(s,"first line\nsecond line\nthird line") - @test String(buf.data[1:buf.size]) == "first line\nsecond line\nthird line" + @test content(buf) == "first line\nsecond line\nthird line" ## edit_move_line_start/end ## seek(buf, 0) @@ -414,11 +431,11 @@ let s.key_repeats = 1 # Manually flag a repeated keypress LineEdit.edit_kill_line(s) s.key_repeats = 0 - @test String(buf.data[1:buf.size]) == "second line\nthird line" + @test content(buf) == "second line\nthird line" LineEdit.move_line_end(s) LineEdit.edit_move_right(s) LineEdit.edit_yank(s) - @test String(buf.data[1:buf.size]) == "second line\nfirst line\nthird line" + @test content(buf) == "second line\nfirst line\nthird line" end # Issue 7845 @@ -439,16 +456,16 @@ let end @testset "function prompt indentation" begin - term = TestHelpers.FakeTerminal(IOBuffer(), IOBuffer(), IOBuffer(), false) + s = new_state() + term = Base.LineEdit.terminal(s) # default prompt: PromptState.indent should not be set to a final fixed value - s = LineEdit.init_state(term, ModalInterface([Prompt("julia> ")])) ps::LineEdit.PromptState = s.mode_state[s.current_mode] @test ps.indent == -1 # the prompt is modified afterwards to a function ps.p.prompt = let i = 0 () -> ["Julia is Fun! > ", "> "][mod1(i+=1, 2)] # lengths are 16 and 2 end - buf = LineEdit.buffer(ps) + buf = buffer(ps) write(buf, "begin\n julia = :fun\nend") outbuf = IOBuffer() termbuf = Base.Terminals.TerminalBuffer(outbuf) @@ -458,120 +475,107 @@ end "\r\e[16C julia = :fun\n" * "\r\e[16Cend\r\e[19C" LineEdit.refresh_multi_line(termbuf, term, ps) - @test String(take!(copy(outbuf))) == + @test String(take!(outbuf)) == "\r\e[0K\e[1A\r\e[0K\e[1A\r\e[0K\e[1m> \e[0m\r\e[2Cbegin\n" * "\r\e[2C julia = :fun\n" * "\r\e[2Cend\r\e[5C" end @testset "tab/backspace alignment feature" begin - term = TestHelpers.FakeTerminal(IOBuffer(), IOBuffer(), IOBuffer()) - s = LineEdit.init_state(term, ModalInterface([Prompt("test> ")])) - function bufferdata(s) - buf = LineEdit.buffer(s) - String(buf.data[1:buf.size]) - end + s = new_state() move_left(s, n) = for x = 1:n LineEdit.edit_move_left(s) end - bufpos(s::Base.LineEdit.MIState) = position(LineEdit.buffer(s)) - LineEdit.edit_insert(s, "for x=1:10\n") LineEdit.edit_tab(s) - @test bufferdata(s) == "for x=1:10\n " + @test content(s) == "for x=1:10\n " LineEdit.edit_backspace(s, true, false) - @test bufferdata(s) == "for x=1:10\n" + @test content(s) == "for x=1:10\n" LineEdit.edit_insert(s, " ") - @test bufpos(s) == 13 + @test position(s) == 13 LineEdit.edit_tab(s) - @test bufferdata(s) == "for x=1:10\n " + @test content(s) == "for x=1:10\n " LineEdit.edit_insert(s, " ") LineEdit.edit_backspace(s, true, false) - @test bufferdata(s) == "for x=1:10\n " + @test content(s) == "for x=1:10\n " LineEdit.edit_insert(s, "éé=3 ") LineEdit.edit_tab(s) - @test bufferdata(s) == "for x=1:10\n éé=3 " + @test content(s) == "for x=1:10\n éé=3 " LineEdit.edit_backspace(s, true, false) - @test bufferdata(s) == "for x=1:10\n éé=3" + @test content(s) == "for x=1:10\n éé=3" LineEdit.edit_insert(s, "\n 1∉x ") LineEdit.edit_tab(s) - @test bufferdata(s) == "for x=1:10\n éé=3\n 1∉x " + @test content(s) == "for x=1:10\n éé=3\n 1∉x " LineEdit.edit_backspace(s, false, false) - @test bufferdata(s) == "for x=1:10\n éé=3\n 1∉x " + @test content(s) == "for x=1:10\n éé=3\n 1∉x " LineEdit.edit_backspace(s, true, false) - @test bufferdata(s) == "for x=1:10\n éé=3\n 1∉x " + @test content(s) == "for x=1:10\n éé=3\n 1∉x " LineEdit.edit_move_word_left(s) LineEdit.edit_tab(s) - @test bufferdata(s) == "for x=1:10\n éé=3\n 1∉x " + @test content(s) == "for x=1:10\n éé=3\n 1∉x " LineEdit.move_line_start(s) - @test bufpos(s) == 22 + @test position(s) == 22 LineEdit.edit_tab(s, true) - @test bufferdata(s) == "for x=1:10\n éé=3\n 1∉x " - @test bufpos(s) == 30 + @test content(s) == "for x=1:10\n éé=3\n 1∉x " + @test position(s) == 30 LineEdit.edit_move_left(s) - @test bufpos(s) == 29 + @test position(s) == 29 LineEdit.edit_backspace(s, true, true) - @test bufferdata(s) == "for x=1:10\n éé=3\n 1∉x " - @test bufpos(s) == 26 + @test content(s) == "for x=1:10\n éé=3\n 1∉x " + @test position(s) == 26 LineEdit.edit_tab(s, false) # same as edit_tab(s, true) here - @test bufpos(s) == 30 + @test position(s) == 30 move_left(s, 6) - @test bufpos(s) == 24 + @test position(s) == 24 LineEdit.edit_backspace(s, true, true) - @test bufferdata(s) == "for x=1:10\n éé=3\n 1∉x " - @test bufpos(s) == 22 + @test content(s) == "for x=1:10\n éé=3\n 1∉x " + @test position(s) == 22 LineEdit.edit_kill_line(s) LineEdit.edit_insert(s, ' '^10) move_left(s, 7) - @test bufferdata(s) == "for x=1:10\n éé=3\n " - @test bufpos(s) == 25 + @test content(s) == "for x=1:10\n éé=3\n " + @test position(s) == 25 LineEdit.edit_tab(s, true, false) - @test bufpos(s) == 32 + @test position(s) == 32 move_left(s, 7) LineEdit.edit_tab(s, true, true) - @test bufpos(s) == 26 - @test bufferdata(s) == "for x=1:10\n éé=3\n " + @test position(s) == 26 + @test content(s) == "for x=1:10\n éé=3\n " # test again the same, when there is a next line LineEdit.edit_insert(s, " \nend") move_left(s, 11) - @test bufpos(s) == 25 + @test position(s) == 25 LineEdit.edit_tab(s, true, false) - @test bufpos(s) == 32 + @test position(s) == 32 move_left(s, 7) LineEdit.edit_tab(s, true, true) - @test bufpos(s) == 26 - @test bufferdata(s) == "for x=1:10\n éé=3\n \nend" + @test position(s) == 26 + @test content(s) == "for x=1:10\n éé=3\n \nend" end @testset "newline alignment feature" begin - term = TestHelpers.FakeTerminal(IOBuffer(), IOBuffer(), IOBuffer()) - s = LineEdit.init_state(term, ModalInterface([Prompt("test> ")])) - function bufferdata(s) - buf = LineEdit.buffer(s) - String(buf.data[1:buf.size]) - end - + s = new_state() LineEdit.edit_insert(s, "for x=1:10\n é = 1") LineEdit.edit_insert_newline(s) - @test bufferdata(s) == "for x=1:10\n é = 1\n " + @test content(s) == "for x=1:10\n é = 1\n " LineEdit.edit_insert(s, " b = 2") LineEdit.edit_insert_newline(s) - @test bufferdata(s) == "for x=1:10\n é = 1\n b = 2\n " + @test content(s) == "for x=1:10\n é = 1\n b = 2\n " # after an empty line, should still insert the expected number of spaces LineEdit.edit_insert_newline(s) - @test bufferdata(s) == "for x=1:10\n é = 1\n b = 2\n \n " + @test content(s) == "for x=1:10\n é = 1\n b = 2\n \n " LineEdit.edit_insert_newline(s, 0) - @test bufferdata(s) == "for x=1:10\n é = 1\n b = 2\n \n \n" + @test content(s) == "for x=1:10\n é = 1\n b = 2\n \n \n" LineEdit.edit_insert_newline(s, 2) - @test bufferdata(s) == "for x=1:10\n é = 1\n b = 2\n \n \n\n " + @test content(s) == "for x=1:10\n é = 1\n b = 2\n \n \n\n " # test when point before first letter of the line for i=6:10 LineEdit.edit_clear(s) LineEdit.edit_insert(s, "begin\n x") seek(LineEdit.buffer(s), i) LineEdit.edit_insert_newline(s) - @test bufferdata(s) == "begin\n" * ' '^(i-6) * "\n x" + @test content(s) == "begin\n" * ' '^(i-6) * "\n x" end end @@ -586,3 +590,27 @@ end LineEdit.edit_lower_case(buf) @test String(take!(copy(buf))) == "AA Bb cc" end + +@testset "kill ring" begin + s = new_state() + buf = buffer(s) + edit_insert(s, "ça ≡ nothing") + @test transform!(LineEdit.edit_copy_region, s) == ("ça ≡ nothing", 12, 0) + @test s.kill_ring[end] == "ça ≡ nothing" + @test transform!(LineEdit.edit_exchange_point_and_mark, s)[2:3] == (0, 12) + charseek(buf, 8); setmark(s) + charseek(buf, 1) + @test transform!(LineEdit.edit_kill_region, s) == ("çhing", 1, 1) + @test s.kill_ring[end] == "a ≡ not" + charseek(buf, 0) + @test transform!(LineEdit.edit_yank, s) == ("a ≡ notçhing", 7, 0) + # next action will fail, as yank-pop doesn't know a yank was just issued + @test transform!(LineEdit.edit_yank_pop, s) == ("a ≡ notçhing", 7, 0) + s.last_action = :edit_yank + # not this should work: + @test transform!(LineEdit.edit_yank_pop, s) == ("ça ≡ nothingçhing", 12, 0) + @test s.kill_idx == 1 + LineEdit.edit_kill_line(s) + @test s.kill_ring[end] == "çhing" + @test s.kill_idx == 3 +end From 81b839d2cfc6218c0434e30e7a26bd4c4186e7cc Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Sun, 14 May 2017 12:25:03 +0200 Subject: [PATCH 249/324] re-enable the 0-arg MersenneTwister() constructor The arguments to `srand(rng, ...)` and `typeof(rng)(...)` should mirror each-other, in particular `srand(MersenneTwister(0))` and `MersenneTwister()` should produce an equivalent object. Also, `srand(::RandomDevice)` has been added for consistency, which could be useful in generic code. --- base/deprecated.jl | 3 --- base/random/RNGs.jl | 14 +++++++++++++- base/random/random.jl | 28 ++++++++++++++++++++++------ test/random.jl | 34 +++++++++++++++++++++++++++++++--- 4 files changed, 66 insertions(+), 13 deletions(-) diff --git a/base/deprecated.jl b/base/deprecated.jl index 2dc1cec006b6f..8aaeeaa5c852f 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1267,9 +1267,6 @@ end end end -# PR #16984 -@deprecate MersenneTwister() MersenneTwister(0) - # #19635 for fname in (:ones, :zeros) @eval @deprecate ($fname)(T::Type, arr) ($fname)(T, size(arr)) diff --git a/base/random/RNGs.jl b/base/random/RNGs.jl index 6a12e3877f814..ae5c4d32d9ce5 100644 --- a/base/random/RNGs.jl +++ b/base/random/RNGs.jl @@ -40,9 +40,13 @@ end # os-test Create a `RandomDevice` RNG object. Two such objects will always generate different streams of random numbers. +The entropy is obtained from the operating system. """ RandomDevice +RandomDevice(::Void) = RandomDevice() +srand(rng::RandomDevice) = rng + ### generation of floats @inline rand(r::RandomDevice, I::FloatInterval) = rand_generic(r, I) @@ -71,10 +75,17 @@ MersenneTwister(seed::Vector{UInt32}, state::DSFMT_state) = """ MersenneTwister(seed) + MersenneTwister() Create a `MersenneTwister` RNG object. Different RNG objects can have their own seeds, which may be useful for generating different streams of random numbers. +The `seed` may be a non-negative integer or a vector of +`UInt32` integers. If no seed is provided, a randomly generated one +is created (using entropy from the system). +See the [`srand`](@ref) function for reseeding an already existing +`MersenneTwister` object. + # Examples ```jldoctest @@ -96,7 +107,8 @@ julia> x1 == x2 true ``` """ -MersenneTwister(seed) = srand(MersenneTwister(Vector{UInt32}(), DSFMT_state()), seed) +MersenneTwister(seed=nothing) = + srand(MersenneTwister(Vector{UInt32}(), DSFMT_state()), seed) function copy!(dst::MersenneTwister, src::MersenneTwister) copy!(resize!(dst.seed, length(src.seed)), src.seed) diff --git a/base/random/random.jl b/base/random/random.jl index 2535443f9a8d4..9d3cc99bc69fe 100644 --- a/base/random/random.jl +++ b/base/random/random.jl @@ -117,13 +117,14 @@ rand! srand([rng=GLOBAL_RNG], seed) -> rng srand([rng=GLOBAL_RNG]) -> rng -Reseed the random number generator. If a `seed` is provided, the RNG will give a -reproducible sequence of numbers, otherwise Julia will get entropy from the system. For -`MersenneTwister`, the `seed` may be a non-negative integer or a vector of [`UInt32`](@ref) -integers. `RandomDevice` does not support seeding. +Reseed the random number generator: `rng` will give a reproducible +sequence of numbers if and only if a `seed` is provided. Some RNGs +don't accept a seed, like `RandomDevice`. +After the call to `srand`, `rng` is equivalent to a newly created +object initialized with the same seed. # Examples -```jldoctest +```julia-repl julia> srand(1234); julia> x1 = rand(2) @@ -140,8 +141,23 @@ julia> x2 = rand(2) julia> x1 == x2 true + +julia> rng = MersenneTwister(1234); rand(rng, 2) == x1 +true + +julia> MersenneTwister(1) == srand(rng, 1) +true + +julia> rand(srand(rng), Bool) # not reproducible +true + +julia> rand(srand(rng), Bool) +false + +julia> rand(MersenneTwister(), Bool) # not reproducible either +true ``` """ -srand +srand(rng::AbstractRNG, ::Void) = srand(rng) end # module diff --git a/test/random.jl b/test/random.jl index 07923ac39131c..87382784c2ce9 100644 --- a/test/random.jl +++ b/test/random.jl @@ -38,8 +38,7 @@ A = zeros(UInt128, 2, 2) @test_throws BoundsError rand!(MersenneTwister(0), A, 5) # rand from AbstractArray -let mt = MersenneTwister(0) - srand(mt) +let mt = MersenneTwister() @test rand(mt, 0:3:1000) in 0:3:1000 @test issubset(rand!(mt, Array{Int}(100), 0:3:1000), 0:3:1000) coll = Any[2, UInt128(128), big(619), "string"] @@ -424,7 +423,7 @@ function hist(X, n) end # test uniform distribution of floats -for rng in [srand(MersenneTwister(0)), RandomDevice()], +for rng in [MersenneTwister(), RandomDevice()], T in [Float16, Float32, Float64, BigFloat], prec in (T == BigFloat ? [3, 53, 64, 100, 256, 1000] : [256]) setprecision(BigFloat, prec) do @@ -589,3 +588,32 @@ end # this shouldn't crash (#22403) @test_throws MethodError rand!(Union{UInt,Int}[1, 2, 3]) + +@testset "$RNG() & srand(rng::$RNG) initializes randomly" for RNG in (MersenneTwister, RandomDevice) + m = RNG() + a = rand(m, Int) + m = RNG() + @test rand(m, Int) != a + # passing `nothing` is equivalent to passing nothing + m = RNG(nothing) + b = rand(m, Int) + @test b != a + srand(m) + c = rand(m, Int) + @test c ∉ (a, b) + srand(m) + @test rand(m, Int) ∉ (a, b, c) + srand(m, nothing) + d = rand(m, Int) + @test d ∉ (a, b, c) + srand(m, nothing) + @test rand(m, Int) ∉ (a, b, c, d) +end + +@testset "MersenneTwister($seed_) & srand(m::MersenneTwister, $seed_) produce the same stream" for seed_ in [0:5; 10000:10005] + # "seed_" instead of "seed" because `seed` is a global variable in this file, and there is an "overwriting" warning + m = MersenneTwister(seed_) + a = [rand(m) for _=1:100] + srand(m, seed_) + @test a == [rand(m) for _=1:100] +end From ec601e95c3c7557753207fea7157bdcaa3d33c27 Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Sun, 3 Sep 2017 18:29:47 -0500 Subject: [PATCH 250/324] Correct indentation for LibGit2.Consts --- base/libgit2/consts.jl | 762 ++++++++++++++++++++--------------------- 1 file changed, 381 insertions(+), 381 deletions(-) diff --git a/base/libgit2/consts.jl b/base/libgit2/consts.jl index 3528e4e268408..83feeeb9ed0cb 100644 --- a/base/libgit2/consts.jl +++ b/base/libgit2/consts.jl @@ -2,364 +2,364 @@ module Consts - const HEAD_FILE = "HEAD" - const FETCH_HEAD = "FETCH_HEAD" - const REMOTE_ORIGIN = "origin" - - # objs - @enum(OBJECT, - OBJ_ANY = -2, - OBJ_BAD = -1, - OBJ_COMMIT = 1, - OBJ_TREE = 2, - OBJ_BLOB = 3, - OBJ_TAG = 4) - - #revwalk - const SORT_NONE = Cint(0) - const SORT_TOPOLOGICAL = Cint(1 << 0) - const SORT_TIME = Cint(1 << 1) - const SORT_REVERSE = Cint(1 << 2) - - # refs - const REF_INVALID = Cint(0) - const REF_OID = Cint(1) - const REF_SYMBOLIC = Cint(2) - const REF_LISTALL = REF_OID | REF_SYMBOLIC - - # blame - const BLAME_NORMAL = Cuint(0) - const BLAME_TRACK_COPIES_SAME_FILE = Cuint(1 << 0) - const BLAME_TRACK_COPIES_SAME_COMMIT_MOVES = Cuint(1 << 1) - const BLAME_TRACK_COPIES_SAME_COMMIT_COPIES = Cuint(1 << 2) - const BLAME_TRACK_COPIES_ANY_COMMIT_COPIES = Cuint(1 << 3) - const BLAME_FIRST_PARENT = Cuint(1 << 4) - - # checkout - const CHECKOUT_NONE = Cuint(0) - const CHECKOUT_SAFE = Cuint(1 << 0) - const CHECKOUT_FORCE = Cuint(1 << 1) - const CHECKOUT_RECREATE_MISSING = Cuint(1 << 2) - const CHECKOUT_ALLOW_CONFLICTS = Cuint(1 << 4) - const CHECKOUT_REMOVE_UNTRACKED = Cuint(1 << 5) - const CHECKOUT_REMOVE_IGNORED = Cuint(1 << 6) - const CHECKOUT_UPDATE_ONLY = Cuint(1 << 7) - const CHECKOUT_DONT_UPDATE_INDEX = Cuint(1 << 8) - const CHECKOUT_NO_REFRESH = Cuint(1 << 9) - const CHECKOUT_SKIP_UNMERGED = Cuint(1 << 10) - const CHECKOUT_USE_OURS = Cuint(1 << 11) - const CHECKOUT_USE_THEIRS = Cuint(1 << 12) - const CHECKOUT_DISABLE_PATHSPEC_MATCH = Cuint(1 << 13) - const CHECKOUT_SKIP_LOCKED_DIRECTORIES = Cuint(1 << 18) - const CHECKOUT_DONT_OVERWRITE_IGNORED = Cuint(1 << 19) - const CHECKOUT_CONFLICT_STYLE_MERGE = Cuint(1 << 20) - const CHECKOUT_CONFLICT_STYLE_DIFF3 = Cuint(1 << 21) - const CHECKOUT_DONT_REMOVE_EXISTING = Cuint(1 << 22) - - const CHECKOUT_UPDATE_SUBMODULES = Cuint(1 << 16) - const CHECKOUT_UPDATE_SUBMODULES_IF_CHANGED = Cuint(1 << 17) - - const CHECKOUT_NOTIFY_NONE = Cuint(0) - const CHECKOUT_NOTIFY_CONFLICT = Cuint(1 << 0) - const CHECKOUT_NOTIFY_DIRTY = Cuint(1 << 1) - const CHECKOUT_NOTIFY_UPDATED = Cuint(1 << 2) - const CHECKOUT_NOTIFY_UNTRACKED = Cuint(1 << 3) - const CHECKOUT_NOTIFY_IGNORED = Cuint(1 << 4) - const CHECKOUT_NOTIFY_ALL = 0x0FFFF - - # diff - const DIFF_OPTIONS_VERSION = Cuint(1) - - const DIFF_NORMAL = Cuint(0) - const DIFF_REVERSE = Cuint(1 << 0) - const DIFF_INCLUDE_IGNORED = Cuint(1 << 1) - const DIFF_RECURSE_IGNORED_DIRS = Cuint(1 << 2) - const DIFF_INCLUDE_UNTRACKED = Cuint(1 << 3) - const DIFF_RECURSE_UNTRACKED_DIRS = Cuint(1 << 4) - const DIFF_INCLUDE_UNMODIFIED = Cuint(1 << 5) - const DIFF_INCLUDE_TYPECHANGE = Cuint(1 << 6) - const DIFF_INCLUDE_TYPECHANGE_TREES = Cuint(1 << 7) - const DIFF_IGNORE_FILEMODE = Cuint(1 << 8) - const DIFF_IGNORE_SUBMODULES = Cuint(1 << 9) - const DIFF_IGNORE_CASE = Cuint(1 << 10) - const DIFF_DISABLE_PATHSPEC_MATCH = Cuint(1 << 12) - const DIFF_SKIP_BINARY_CHECK = Cuint(1 << 13) - const DIFF_ENABLE_FAST_UNTRACKED_DIRS = Cuint(1 << 14) - - const DIFF_FORCE_TEXT = Cuint(1 << 20) - const DIFF_FORCE_BINARY = Cuint(1 << 21) - const DIFF_IGNORE_WHITESPACE = Cuint(1 << 22) - const DIFF_IGNORE_WHITESPACE_CHANGE = Cuint(1 << 23) - const DIFF_IGNORE_WHITESPACE_EOL = Cuint(1 << 24) - const DIFF_SHOW_UNTRACKED_CONTENT = Cuint(1 << 25) - const DIFF_SHOW_UNMODIFIED = Cuint(1 << 26) - const DIFF_PATIENCE = Cuint(1 << 28) - const DIFF_MINIMAL = Cuint(1 << 29) - - const DIFF_FLAG_BINARY = Cuint(1 << 0) - const DIFF_FLAG_NOT_BINARY = Cuint(1 << 1) - const DIFF_FLAG_VALID_OID = Cuint(1 << 2) - - const DIFF_FORMAT_PATCH = Cuint(1) - const DIFF_FORMAT_PATCH_HEADER = Cuint(2) - const DIFF_FORMAT_RAW = Cuint(3) - const DIFF_FORMAT_NAME_ONLY = Cuint(4) - const DIFF_FORMAT_NAME_STATUS = Cuint(5) - - @enum(DELTA_STATUS, DELTA_UNMODIFIED = Cint(0), - DELTA_ADDED = Cint(1), - DELTA_DELETED = Cint(2), - DELTA_MODIFIED = Cint(3), - DELTA_RENAMED = Cint(4), - DELTA_COPIED = Cint(5), - DELTA_IGNORED = Cint(6), - DELTA_UNTRACKED = Cint(7), - DELTA_TYPECHANGE = Cint(8)) - - # index - const IDXENTRY_NAMEMASK = (0x0fff) - const IDXENTRY_STAGEMASK = (0x3000) - const IDXENTRY_EXTENDED = (0x4000) - const IDXENTRY_VALID = (0x8000) - const IDXENTRY_STAGESHIFT = Cint(12) - - const IDXENTRY_UPDATE = Cint(1 << 0) - const IDXENTRY_REMOVE = Cint(1 << 1) - const IDXENTRY_UPTODATE = Cint(1 << 2) - const IDXENTRY_ADDED = Cint(1 << 3) - - const IDXENTRY_HASHED = Cint(1 << 4) - const IDXENTRY_UNHASHED = Cint(1 << 5) - const IDXENTRY_WT_REMOVE = Cint(1 << 6) - const IDXENTRY_CONFLICTED = Cint(1 << 7) - - const IDXENTRY_UNPACKED = Cint(1 << 8) - const IDXENTRY_NEW_SKIP_WORKTREE = Cint(1 << 9) - - const INDEXCAP_IGNORE_CASE = Cuint(1) - const INDEXCAP_NO_FILEMODE = Cuint(2) - const INDEXCAP_NO_SYMLINKS = Cuint(4) - const INDEXCAP_FROM_OWNER = ~Cuint(0) - - const INDEX_ADD_DEFAULT = Cuint(0) - const INDEX_ADD_FORCE = Cuint(1 << 0) - const INDEX_ADD_DISABLE_PATHSPEC_MATCH = Cuint(1 << 1) - const INDEX_ADD_CHECK_PATHSPEC = Cuint(1 << 2) - - const INDEX_STAGE_ANY = Cint(-1) - - # merge - """ Option flags for git merge. - * `MERGE_FIND_RENAMES`: detect if a file has been renamed between the common - ancestor and the "ours" or "theirs" side of the merge. Allows merges where - a file has been renamed. - * `MERGE_FAIL_ON_CONFLICT`: exit immediately if a conflict is found rather - than trying to resolve it. - * `MERGE_SKIP_REUC`: do not write the REUC extension on the index resulting - from the merge. - * `MERGE_NO_RECURSIVE`: if the commits being merged have multiple merge bases, - use the first one, rather than trying to recursively merge the bases. - """ - @enum(GIT_MERGE, MERGE_FIND_RENAMES = 1 << 0, - MERGE_FAIL_ON_CONFLICT = 1 << 1, - MERGE_SKIP_REUC = 1 << 2, - MERGE_NO_RECURSIVE = 1 << 3) - - @enum(GIT_MERGE_FILE, MERGE_FILE_DEFAULT = 0, # Defaults - MERGE_FILE_STYLE_MERGE = 1 << 0, # Create standard conflicted merge files - MERGE_FILE_STYLE_DIFF3 = 1 << 1, # Create diff3-style files - MERGE_FILE_SIMPLIFY_ALNUM = 1 << 2, # Condense non-alphanumeric regions for simplified diff file - MERGE_FILE_IGNORE_WHITESPACE = 1 << 3, # Ignore all whitespace - MERGE_FILE_IGNORE_WHITESPACE_CHANGE = 1 << 4, # Ignore changes in amount of whitespace - MERGE_FILE_IGNORE_WHITESPACE_EOL = 1 << 5, # Ignore whitespace at end of line - MERGE_FILE_DIFF_PATIENCE = 1 << 6, # Use the "patience diff" algorithm - MERGE_FILE_DIFF_MINIMAL = 1 << 7) # Take extra time to find minimal diff - """ Option flags for git merge file favoritism. - * `MERGE_FILE_FAVOR_NORMAL`: if both sides of the merge have changes to a section, - make a note of the conflict in the index which `git checkout` will use to create - a merge file, which the user can then reference to resolve the conflicts. This is - the default. - * `MERGE_FILE_FAVOR_OURS`: if both sides of the merge have changes to a section, - use the version in the "ours" side of the merge in the index. - * `MERGE_FILE_FAVOR_THEIRS`: if both sides of the merge have changes to a section, - use the version in the "theirs" side of the merge in the index. - * `MERGE_FILE_FAVOR_UNION`: if both sides of the merge have changes to a section, - include each unique line from both sides in the file which is put into the index. - """ - @enum(GIT_MERGE_FILE_FAVOR, MERGE_FILE_FAVOR_NORMAL = 0, - MERGE_FILE_FAVOR_OURS = 1, - MERGE_FILE_FAVOR_THEIRS = 2, - MERGE_FILE_FAVOR_UNION = 3) - """ The user's instructions for how to perform a possible merge. - * `MERGE_PREFERENCE_NONE`: the user has no preference. - * `MERGE_PREFERENCE_NO_FASTFORWARD`: do not allow any fast-forward merges. - * `MERGE_PREFERENCE_FASTFORWARD_ONLY`: allow only fast-forward merges and no - other type (which may introduce conflicts). - """ - @enum(GIT_MERGE_PREFERENCE, MERGE_PREFERENCE_NONE = 0, - MERGE_PREFERENCE_NO_FASTFORWARD = 1, - MERGE_PREFERENCE_FASTFORWARD_ONLY = 2) - """ Result of analysis on merge possibilities. - * `MERGE_ANALYSIS_NONE`: it is not possible to merge the elements of the input commits. - * `MERGE_ANALYSIS_NORMAL`: a regular merge, when HEAD and the commits that the - user wishes to merge have all diverged from a common ancestor. In this case the - changes have to be resolved and conflicts may occur. - * `MERGE_ANALYSIS_UP_TO_DATE`: all the input commits the user wishes to merge can - be reached from HEAD, so no merge needs to be performed. - * `MERGE_ANALYSIS_FASTFORWARD`: the input commit is a descendant of HEAD and so no - merge needs to be performed - instead, the user can simply checkout the - input commit(s). - * `MERGE_ANALYSIS_UNBORN`: the HEAD of the repository refers to a commit which does not - exist. It is not possible to merge, but it may be possible to checkout the input - commits. - """ - @enum(GIT_MERGE_ANALYSIS, MERGE_ANALYSIS_NONE = 0, - MERGE_ANALYSIS_NORMAL = 1 << 0, - MERGE_ANALYSIS_UP_TO_DATE = 1 << 1, - MERGE_ANALYSIS_FASTFORWARD = 1 << 2, - MERGE_ANALYSIS_UNBORN = 1 << 3) - - # reset - const RESET_SOFT = Cint(1) # Move the head to the given commit - const RESET_MIXED = Cint(2) # SOFT plus reset index to the commit - const RESET_HARD = Cint(3) # MIXED plus changes in working tree discarded - - # rebase - """ Options for what rebase operation is currently being performed on a commit. - * `REBASE_OPERATION_PICK`: cherry-pick the commit in question. - * `REBASE_OPERATION_REWORD`: cherry-pick the commit in question, but rewrite its - message using the prompt. - * `REBASE_OPERATION_EDIT`: cherry-pick the commit in question, but allow the user - to edit the commit's contents and its message. - * `REBASE_OPERATION_SQUASH`: squash the commit in question into the previous commit. - The commit messages of the two commits will be merged. - * `REBASE_OPERATION_FIXUP`: squash the commit in question into the previous commit. - Only the commit message of the previous commit will be used. - * `REBASE_OPERATION_EXEC`: do not cherry-pick a commit. Run a command and continue if - the command exits successfully. - """ - @enum(GIT_REBASE_OPERATION, REBASE_OPERATION_PICK = Cint(0), - REBASE_OPERATION_REWORD = Cint(1), - REBASE_OPERATION_EDIT = Cint(2), - REBASE_OPERATION_SQUASH = Cint(3), - REBASE_OPERATION_FIXUP = Cint(4), - REBASE_OPERATION_EXEC = Cint(5)) - - # fetch_prune - const FETCH_PRUNE_UNSPECIFIED = Cint(0) - const FETCH_PRUNE = Cint(1) - const FETCH_NO_PRUNE = Cint(2) - - # remote_autotag - const REMOTE_DOWNLOAD_TAGS_UNSPECIFIED = Cint(0) - const REMOTE_DOWNLOAD_TAGS_AUTO = Cint(1) - const REMOTE_DOWNLOAD_TAGS_NONE = Cint(2) - const REMOTE_DOWNLOAD_TAGS_ALL = Cint(3) - - # clone - const CLONE_LOCAL_AUTO = Cint(0) - const CLONE_LOCAL = Cint(1) - const CLONE_NO_LOCAL = Cint(2) - const CLONE_LOCAL_NO_LINKS = Cint(3) - - # describe - const DESCRIBE_DEFAULT = Cuint(0) - const DESCRIBE_TAGS = Cuint(1 << 0) - const DESCRIBE_ALL = Cuint(1 << 1) - - # status - const STATUS_CURRENT = Cuint(0) - const STATUS_INDEX_NEW = Cuint(1 << 0) - const STATUS_INDEX_MODIFIED = Cuint(1 << 1) - const STATUS_INDEX_DELETED = Cuint(1 << 2) - const STATUS_INDEX_RENAMED = Cuint(1 << 3) - const STATUS_INDEX_TYPECHANGE = Cuint(1 << 4) - const STATUS_WT_NEW = Cuint(1 << 7) - const STATUS_WT_MODIFIED = Cuint(1 << 8) - const STATUS_WT_DELETED = Cuint(1 << 9) - const STATUS_WT_TYPECHANGE = Cuint(1 << 10) - const STATUS_WT_RENAMED = Cuint(1 << 11) - const STATUS_WT_UNREADABLE = Cuint(1 << 12) - const STATUS_IGNORED = Cuint(1 << 14) - const STATUS_CONFLICTED = Cuint(1 << 15) - - # status show - const STATUS_SHOW_INDEX_AND_WORKDIR = Cint(0) - const STATUS_SHOW_INDEX_ONLY = Cint(1) - const STATUS_SHOW_WORKDIR_ONLY = Cint(2) - - # status options - const STATUS_OPT_INCLUDE_UNTRACKED = Cuint(1 << 0) - const STATUS_OPT_INCLUDE_IGNORED = Cuint(1 << 1) - const STATUS_OPT_INCLUDE_UNMODIFIED = Cuint(1 << 2) - const STATUS_OPT_EXCLUDE_SUBMODULES = Cuint(1 << 3) - const STATUS_OPT_RECURSE_UNTRACKED_DIRS = Cuint(1 << 4) - const STATUS_OPT_DISABLE_PATHSPEC_MATCH = Cuint(1 << 5) - const STATUS_OPT_RECURSE_IGNORED_DIRS = Cuint(1 << 6) - const STATUS_OPT_RENAMES_HEAD_TO_INDEX = Cuint(1 << 7) - const STATUS_OPT_RENAMES_INDEX_TO_WORKDIR = Cuint(1 << 8) - const STATUS_OPT_SORT_CASE_SENSITIVELY = Cuint(1 << 9) - const STATUS_OPT_SORT_CASE_INSENSITIVELY = Cuint(1 << 10) - const STATUS_OPT_RENAMES_FROM_REWRITES = Cuint(1 << 11) - const STATUS_OPT_NO_REFRESH = Cuint(1 << 12) - const STATUS_OPT_UPDATE_INDEX = Cuint(1 << 13) - const STATUS_OPT_INCLUDE_UNREADABLE = Cuint(1 << 14) - const STATUS_OPT_INCLUDE_UNREADABLE_AS_UNTRACKED = Cuint(1 << 15) - - @enum(GIT_SUBMODULE_IGNORE, SUBMODULE_IGNORE_UNSPECIFIED = -1, # use the submodule's configuration - SUBMODULE_IGNORE_NONE = 1, # any change or untracked == dirty - SUBMODULE_IGNORE_UNTRACKED = 2, # dirty if tracked files change - SUBMODULE_IGNORE_DIRTY = 3, # only dirty if HEAD moved - SUBMODULE_IGNORE_ALL = 4) # never dirty - - """ +const HEAD_FILE = "HEAD" +const FETCH_HEAD = "FETCH_HEAD" +const REMOTE_ORIGIN = "origin" + +# objs +@enum(OBJECT, + OBJ_ANY = -2, + OBJ_BAD = -1, + OBJ_COMMIT = 1, + OBJ_TREE = 2, + OBJ_BLOB = 3, + OBJ_TAG = 4) + +#revwalk +const SORT_NONE = Cint(0) +const SORT_TOPOLOGICAL = Cint(1 << 0) +const SORT_TIME = Cint(1 << 1) +const SORT_REVERSE = Cint(1 << 2) + +# refs +const REF_INVALID = Cint(0) +const REF_OID = Cint(1) +const REF_SYMBOLIC = Cint(2) +const REF_LISTALL = REF_OID | REF_SYMBOLIC + +# blame +const BLAME_NORMAL = Cuint(0) +const BLAME_TRACK_COPIES_SAME_FILE = Cuint(1 << 0) +const BLAME_TRACK_COPIES_SAME_COMMIT_MOVES = Cuint(1 << 1) +const BLAME_TRACK_COPIES_SAME_COMMIT_COPIES = Cuint(1 << 2) +const BLAME_TRACK_COPIES_ANY_COMMIT_COPIES = Cuint(1 << 3) +const BLAME_FIRST_PARENT = Cuint(1 << 4) + +# checkout +const CHECKOUT_NONE = Cuint(0) +const CHECKOUT_SAFE = Cuint(1 << 0) +const CHECKOUT_FORCE = Cuint(1 << 1) +const CHECKOUT_RECREATE_MISSING = Cuint(1 << 2) +const CHECKOUT_ALLOW_CONFLICTS = Cuint(1 << 4) +const CHECKOUT_REMOVE_UNTRACKED = Cuint(1 << 5) +const CHECKOUT_REMOVE_IGNORED = Cuint(1 << 6) +const CHECKOUT_UPDATE_ONLY = Cuint(1 << 7) +const CHECKOUT_DONT_UPDATE_INDEX = Cuint(1 << 8) +const CHECKOUT_NO_REFRESH = Cuint(1 << 9) +const CHECKOUT_SKIP_UNMERGED = Cuint(1 << 10) +const CHECKOUT_USE_OURS = Cuint(1 << 11) +const CHECKOUT_USE_THEIRS = Cuint(1 << 12) +const CHECKOUT_DISABLE_PATHSPEC_MATCH = Cuint(1 << 13) +const CHECKOUT_SKIP_LOCKED_DIRECTORIES = Cuint(1 << 18) +const CHECKOUT_DONT_OVERWRITE_IGNORED = Cuint(1 << 19) +const CHECKOUT_CONFLICT_STYLE_MERGE = Cuint(1 << 20) +const CHECKOUT_CONFLICT_STYLE_DIFF3 = Cuint(1 << 21) +const CHECKOUT_DONT_REMOVE_EXISTING = Cuint(1 << 22) + +const CHECKOUT_UPDATE_SUBMODULES = Cuint(1 << 16) +const CHECKOUT_UPDATE_SUBMODULES_IF_CHANGED = Cuint(1 << 17) + +const CHECKOUT_NOTIFY_NONE = Cuint(0) +const CHECKOUT_NOTIFY_CONFLICT = Cuint(1 << 0) +const CHECKOUT_NOTIFY_DIRTY = Cuint(1 << 1) +const CHECKOUT_NOTIFY_UPDATED = Cuint(1 << 2) +const CHECKOUT_NOTIFY_UNTRACKED = Cuint(1 << 3) +const CHECKOUT_NOTIFY_IGNORED = Cuint(1 << 4) +const CHECKOUT_NOTIFY_ALL = 0x0FFFF + +# diff +const DIFF_OPTIONS_VERSION = Cuint(1) + +const DIFF_NORMAL = Cuint(0) +const DIFF_REVERSE = Cuint(1 << 0) +const DIFF_INCLUDE_IGNORED = Cuint(1 << 1) +const DIFF_RECURSE_IGNORED_DIRS = Cuint(1 << 2) +const DIFF_INCLUDE_UNTRACKED = Cuint(1 << 3) +const DIFF_RECURSE_UNTRACKED_DIRS = Cuint(1 << 4) +const DIFF_INCLUDE_UNMODIFIED = Cuint(1 << 5) +const DIFF_INCLUDE_TYPECHANGE = Cuint(1 << 6) +const DIFF_INCLUDE_TYPECHANGE_TREES = Cuint(1 << 7) +const DIFF_IGNORE_FILEMODE = Cuint(1 << 8) +const DIFF_IGNORE_SUBMODULES = Cuint(1 << 9) +const DIFF_IGNORE_CASE = Cuint(1 << 10) +const DIFF_DISABLE_PATHSPEC_MATCH = Cuint(1 << 12) +const DIFF_SKIP_BINARY_CHECK = Cuint(1 << 13) +const DIFF_ENABLE_FAST_UNTRACKED_DIRS = Cuint(1 << 14) + +const DIFF_FORCE_TEXT = Cuint(1 << 20) +const DIFF_FORCE_BINARY = Cuint(1 << 21) +const DIFF_IGNORE_WHITESPACE = Cuint(1 << 22) +const DIFF_IGNORE_WHITESPACE_CHANGE = Cuint(1 << 23) +const DIFF_IGNORE_WHITESPACE_EOL = Cuint(1 << 24) +const DIFF_SHOW_UNTRACKED_CONTENT = Cuint(1 << 25) +const DIFF_SHOW_UNMODIFIED = Cuint(1 << 26) +const DIFF_PATIENCE = Cuint(1 << 28) +const DIFF_MINIMAL = Cuint(1 << 29) + +const DIFF_FLAG_BINARY = Cuint(1 << 0) +const DIFF_FLAG_NOT_BINARY = Cuint(1 << 1) +const DIFF_FLAG_VALID_OID = Cuint(1 << 2) + +const DIFF_FORMAT_PATCH = Cuint(1) +const DIFF_FORMAT_PATCH_HEADER = Cuint(2) +const DIFF_FORMAT_RAW = Cuint(3) +const DIFF_FORMAT_NAME_ONLY = Cuint(4) +const DIFF_FORMAT_NAME_STATUS = Cuint(5) + +@enum(DELTA_STATUS, DELTA_UNMODIFIED = Cint(0), + DELTA_ADDED = Cint(1), + DELTA_DELETED = Cint(2), + DELTA_MODIFIED = Cint(3), + DELTA_RENAMED = Cint(4), + DELTA_COPIED = Cint(5), + DELTA_IGNORED = Cint(6), + DELTA_UNTRACKED = Cint(7), + DELTA_TYPECHANGE = Cint(8)) + +# index +const IDXENTRY_NAMEMASK = (0x0fff) +const IDXENTRY_STAGEMASK = (0x3000) +const IDXENTRY_EXTENDED = (0x4000) +const IDXENTRY_VALID = (0x8000) +const IDXENTRY_STAGESHIFT = Cint(12) + +const IDXENTRY_UPDATE = Cint(1 << 0) +const IDXENTRY_REMOVE = Cint(1 << 1) +const IDXENTRY_UPTODATE = Cint(1 << 2) +const IDXENTRY_ADDED = Cint(1 << 3) + +const IDXENTRY_HASHED = Cint(1 << 4) +const IDXENTRY_UNHASHED = Cint(1 << 5) +const IDXENTRY_WT_REMOVE = Cint(1 << 6) +const IDXENTRY_CONFLICTED = Cint(1 << 7) + +const IDXENTRY_UNPACKED = Cint(1 << 8) +const IDXENTRY_NEW_SKIP_WORKTREE = Cint(1 << 9) + +const INDEXCAP_IGNORE_CASE = Cuint(1) +const INDEXCAP_NO_FILEMODE = Cuint(2) +const INDEXCAP_NO_SYMLINKS = Cuint(4) +const INDEXCAP_FROM_OWNER = ~Cuint(0) + +const INDEX_ADD_DEFAULT = Cuint(0) +const INDEX_ADD_FORCE = Cuint(1 << 0) +const INDEX_ADD_DISABLE_PATHSPEC_MATCH = Cuint(1 << 1) +const INDEX_ADD_CHECK_PATHSPEC = Cuint(1 << 2) + +const INDEX_STAGE_ANY = Cint(-1) + +# merge +""" Option flags for git merge. +* `MERGE_FIND_RENAMES`: detect if a file has been renamed between the common + ancestor and the "ours" or "theirs" side of the merge. Allows merges where + a file has been renamed. +* `MERGE_FAIL_ON_CONFLICT`: exit immediately if a conflict is found rather + than trying to resolve it. +* `MERGE_SKIP_REUC`: do not write the REUC extension on the index resulting + from the merge. +* `MERGE_NO_RECURSIVE`: if the commits being merged have multiple merge bases, + use the first one, rather than trying to recursively merge the bases. +""" +@enum(GIT_MERGE, MERGE_FIND_RENAMES = 1 << 0, + MERGE_FAIL_ON_CONFLICT = 1 << 1, + MERGE_SKIP_REUC = 1 << 2, + MERGE_NO_RECURSIVE = 1 << 3) + +@enum(GIT_MERGE_FILE, MERGE_FILE_DEFAULT = 0, # Defaults + MERGE_FILE_STYLE_MERGE = 1 << 0, # Create standard conflicted merge files + MERGE_FILE_STYLE_DIFF3 = 1 << 1, # Create diff3-style files + MERGE_FILE_SIMPLIFY_ALNUM = 1 << 2, # Condense non-alphanumeric regions for simplified diff file + MERGE_FILE_IGNORE_WHITESPACE = 1 << 3, # Ignore all whitespace + MERGE_FILE_IGNORE_WHITESPACE_CHANGE = 1 << 4, # Ignore changes in amount of whitespace + MERGE_FILE_IGNORE_WHITESPACE_EOL = 1 << 5, # Ignore whitespace at end of line + MERGE_FILE_DIFF_PATIENCE = 1 << 6, # Use the "patience diff" algorithm + MERGE_FILE_DIFF_MINIMAL = 1 << 7) # Take extra time to find minimal diff +""" Option flags for git merge file favoritism. + * `MERGE_FILE_FAVOR_NORMAL`: if both sides of the merge have changes to a section, + make a note of the conflict in the index which `git checkout` will use to create + a merge file, which the user can then reference to resolve the conflicts. This is + the default. + * `MERGE_FILE_FAVOR_OURS`: if both sides of the merge have changes to a section, + use the version in the "ours" side of the merge in the index. + * `MERGE_FILE_FAVOR_THEIRS`: if both sides of the merge have changes to a section, + use the version in the "theirs" side of the merge in the index. + * `MERGE_FILE_FAVOR_UNION`: if both sides of the merge have changes to a section, + include each unique line from both sides in the file which is put into the index. +""" +@enum(GIT_MERGE_FILE_FAVOR, MERGE_FILE_FAVOR_NORMAL = 0, + MERGE_FILE_FAVOR_OURS = 1, + MERGE_FILE_FAVOR_THEIRS = 2, + MERGE_FILE_FAVOR_UNION = 3) +""" The user's instructions for how to perform a possible merge. +* `MERGE_PREFERENCE_NONE`: the user has no preference. +* `MERGE_PREFERENCE_NO_FASTFORWARD`: do not allow any fast-forward merges. +* `MERGE_PREFERENCE_FASTFORWARD_ONLY`: allow only fast-forward merges and no + other type (which may introduce conflicts). +""" +@enum(GIT_MERGE_PREFERENCE, MERGE_PREFERENCE_NONE = 0, + MERGE_PREFERENCE_NO_FASTFORWARD = 1, + MERGE_PREFERENCE_FASTFORWARD_ONLY = 2) +""" Result of analysis on merge possibilities. +* `MERGE_ANALYSIS_NONE`: it is not possible to merge the elements of the input commits. +* `MERGE_ANALYSIS_NORMAL`: a regular merge, when HEAD and the commits that the + user wishes to merge have all diverged from a common ancestor. In this case the + changes have to be resolved and conflicts may occur. +* `MERGE_ANALYSIS_UP_TO_DATE`: all the input commits the user wishes to merge can + be reached from HEAD, so no merge needs to be performed. +* `MERGE_ANALYSIS_FASTFORWARD`: the input commit is a descendant of HEAD and so no + merge needs to be performed - instead, the user can simply checkout the + input commit(s). +* `MERGE_ANALYSIS_UNBORN`: the HEAD of the repository refers to a commit which does not + exist. It is not possible to merge, but it may be possible to checkout the input + commits. +""" +@enum(GIT_MERGE_ANALYSIS, MERGE_ANALYSIS_NONE = 0, + MERGE_ANALYSIS_NORMAL = 1 << 0, + MERGE_ANALYSIS_UP_TO_DATE = 1 << 1, + MERGE_ANALYSIS_FASTFORWARD = 1 << 2, + MERGE_ANALYSIS_UNBORN = 1 << 3) + +# reset +const RESET_SOFT = Cint(1) # Move the head to the given commit +const RESET_MIXED = Cint(2) # SOFT plus reset index to the commit +const RESET_HARD = Cint(3) # MIXED plus changes in working tree discarded + +# rebase +""" Options for what rebase operation is currently being performed on a commit. +* `REBASE_OPERATION_PICK`: cherry-pick the commit in question. +* `REBASE_OPERATION_REWORD`: cherry-pick the commit in question, but rewrite its + message using the prompt. +* `REBASE_OPERATION_EDIT`: cherry-pick the commit in question, but allow the user + to edit the commit's contents and its message. +* `REBASE_OPERATION_SQUASH`: squash the commit in question into the previous commit. + The commit messages of the two commits will be merged. +* `REBASE_OPERATION_FIXUP`: squash the commit in question into the previous commit. + Only the commit message of the previous commit will be used. +* `REBASE_OPERATION_EXEC`: do not cherry-pick a commit. Run a command and continue if + the command exits successfully. +""" +@enum(GIT_REBASE_OPERATION, REBASE_OPERATION_PICK = Cint(0), + REBASE_OPERATION_REWORD = Cint(1), + REBASE_OPERATION_EDIT = Cint(2), + REBASE_OPERATION_SQUASH = Cint(3), + REBASE_OPERATION_FIXUP = Cint(4), + REBASE_OPERATION_EXEC = Cint(5)) + +# fetch_prune +const FETCH_PRUNE_UNSPECIFIED = Cint(0) +const FETCH_PRUNE = Cint(1) +const FETCH_NO_PRUNE = Cint(2) + +# remote_autotag +const REMOTE_DOWNLOAD_TAGS_UNSPECIFIED = Cint(0) +const REMOTE_DOWNLOAD_TAGS_AUTO = Cint(1) +const REMOTE_DOWNLOAD_TAGS_NONE = Cint(2) +const REMOTE_DOWNLOAD_TAGS_ALL = Cint(3) + +# clone +const CLONE_LOCAL_AUTO = Cint(0) +const CLONE_LOCAL = Cint(1) +const CLONE_NO_LOCAL = Cint(2) +const CLONE_LOCAL_NO_LINKS = Cint(3) + +# describe +const DESCRIBE_DEFAULT = Cuint(0) +const DESCRIBE_TAGS = Cuint(1 << 0) +const DESCRIBE_ALL = Cuint(1 << 1) + +# status +const STATUS_CURRENT = Cuint(0) +const STATUS_INDEX_NEW = Cuint(1 << 0) +const STATUS_INDEX_MODIFIED = Cuint(1 << 1) +const STATUS_INDEX_DELETED = Cuint(1 << 2) +const STATUS_INDEX_RENAMED = Cuint(1 << 3) +const STATUS_INDEX_TYPECHANGE = Cuint(1 << 4) +const STATUS_WT_NEW = Cuint(1 << 7) +const STATUS_WT_MODIFIED = Cuint(1 << 8) +const STATUS_WT_DELETED = Cuint(1 << 9) +const STATUS_WT_TYPECHANGE = Cuint(1 << 10) +const STATUS_WT_RENAMED = Cuint(1 << 11) +const STATUS_WT_UNREADABLE = Cuint(1 << 12) +const STATUS_IGNORED = Cuint(1 << 14) +const STATUS_CONFLICTED = Cuint(1 << 15) + +# status show +const STATUS_SHOW_INDEX_AND_WORKDIR = Cint(0) +const STATUS_SHOW_INDEX_ONLY = Cint(1) +const STATUS_SHOW_WORKDIR_ONLY = Cint(2) + +# status options +const STATUS_OPT_INCLUDE_UNTRACKED = Cuint(1 << 0) +const STATUS_OPT_INCLUDE_IGNORED = Cuint(1 << 1) +const STATUS_OPT_INCLUDE_UNMODIFIED = Cuint(1 << 2) +const STATUS_OPT_EXCLUDE_SUBMODULES = Cuint(1 << 3) +const STATUS_OPT_RECURSE_UNTRACKED_DIRS = Cuint(1 << 4) +const STATUS_OPT_DISABLE_PATHSPEC_MATCH = Cuint(1 << 5) +const STATUS_OPT_RECURSE_IGNORED_DIRS = Cuint(1 << 6) +const STATUS_OPT_RENAMES_HEAD_TO_INDEX = Cuint(1 << 7) +const STATUS_OPT_RENAMES_INDEX_TO_WORKDIR = Cuint(1 << 8) +const STATUS_OPT_SORT_CASE_SENSITIVELY = Cuint(1 << 9) +const STATUS_OPT_SORT_CASE_INSENSITIVELY = Cuint(1 << 10) +const STATUS_OPT_RENAMES_FROM_REWRITES = Cuint(1 << 11) +const STATUS_OPT_NO_REFRESH = Cuint(1 << 12) +const STATUS_OPT_UPDATE_INDEX = Cuint(1 << 13) +const STATUS_OPT_INCLUDE_UNREADABLE = Cuint(1 << 14) +const STATUS_OPT_INCLUDE_UNREADABLE_AS_UNTRACKED = Cuint(1 << 15) + +@enum(GIT_SUBMODULE_IGNORE, SUBMODULE_IGNORE_UNSPECIFIED = -1, # use the submodule's configuration + SUBMODULE_IGNORE_NONE = 1, # any change or untracked == dirty + SUBMODULE_IGNORE_UNTRACKED = 2, # dirty if tracked files change + SUBMODULE_IGNORE_DIRTY = 3, # only dirty if HEAD moved + SUBMODULE_IGNORE_ALL = 4) # never dirty + +""" Option flags for `GitRepo`. * `REPOSITORY_OPEN_NO_SEARCH` - Only open the repository if it can be immediately found in the `path`. Do not walk up from the `path` looking at parent directories. * `REPOSITORY_OPEN_CROSS_FS` - Unless this flag is set, open will not continue searching across filesystem boundaries. (E.g. Searching in a user's home directory `/home/user/source/` will not return `/.git/` as the found repo if `/` is a different filesystem than `/home`.) * `REPOSITORY_OPEN_BARE` - Open repository as a bare repo regardless of core.bare config, and defer loading config file for faster setup. - """ - @enum(GIT_REPOSITORY_OPEN, REPOSITORY_OPEN_DEFAULT = 0, - REPOSITORY_OPEN_NO_SEARCH = 1<<0, - REPOSITORY_OPEN_CROSS_FS = 1<<1, - REPOSITORY_OPEN_BARE = 1<<2) - - @enum(GIT_BRANCH, BRANCH_LOCAL = 1, BRANCH_REMOTE = 2) - - @enum(GIT_FILEMODE, FILEMODE_UNREADABLE = 0o000000, - FILEMODE_TREE = 0o040000, - FILEMODE_BLOB = 0o100644, - FILEMODE_BLOB_EXECUTABLE = 0o100755, - FILEMODE_LINK = 0o120000, - FILEMODE_COMMIT = 0o160000) - - @enum(GIT_CREDTYPE, CREDTYPE_USERPASS_PLAINTEXT = Cuint(1 << 0), - CREDTYPE_SSH_KEY = Cuint(1 << 1), - CREDTYPE_SSH_CUSTOM = Cuint(1 << 2), - CREDTYPE_DEFAULT = Cuint(1 << 3), - CREDTYPE_SSH_INTERACTIVE = Cuint(1 << 4), - CREDTYPE_USERNAME = Cuint(1 << 5), - CREDTYPE_SSH_MEMORY = Cuint(1 << 6)) - - @enum(GIT_FEATURE, FEATURE_THREADS = Cuint(1 << 0), - FEATURE_HTTPS = Cuint(1 << 1), - FEATURE_SSH = Cuint(1 << 2), - FEATURE_NSEC = Cuint(1 << 3)) +""" +@enum(GIT_REPOSITORY_OPEN, REPOSITORY_OPEN_DEFAULT = 0, + REPOSITORY_OPEN_NO_SEARCH = 1<<0, + REPOSITORY_OPEN_CROSS_FS = 1<<1, + REPOSITORY_OPEN_BARE = 1<<2) + +@enum(GIT_BRANCH, BRANCH_LOCAL = 1, BRANCH_REMOTE = 2) + +@enum(GIT_FILEMODE, FILEMODE_UNREADABLE = 0o000000, + FILEMODE_TREE = 0o040000, + FILEMODE_BLOB = 0o100644, + FILEMODE_BLOB_EXECUTABLE = 0o100755, + FILEMODE_LINK = 0o120000, + FILEMODE_COMMIT = 0o160000) + +@enum(GIT_CREDTYPE, CREDTYPE_USERPASS_PLAINTEXT = Cuint(1 << 0), + CREDTYPE_SSH_KEY = Cuint(1 << 1), + CREDTYPE_SSH_CUSTOM = Cuint(1 << 2), + CREDTYPE_DEFAULT = Cuint(1 << 3), + CREDTYPE_SSH_INTERACTIVE = Cuint(1 << 4), + CREDTYPE_USERNAME = Cuint(1 << 5), + CREDTYPE_SSH_MEMORY = Cuint(1 << 6)) + +@enum(GIT_FEATURE, FEATURE_THREADS = Cuint(1 << 0), + FEATURE_HTTPS = Cuint(1 << 1), + FEATURE_SSH = Cuint(1 << 2), + FEATURE_NSEC = Cuint(1 << 3)) if LibGit2.version() >= v"0.24.0" """ -Priority level of a config file. - -These priority levels correspond to the natural escalation logic (from higher to lower) when searching for config entries in git. - -* `CONFIG_LEVEL_DEFAULT` - Open the global, XDG and system configuration files if any available. -* `CONFIG_LEVEL_PROGRAMDATA` - System-wide on Windows, for compatibility with portable git -* `CONFIG_LEVEL_SYSTEM` - System-wide configuration file; `/etc/gitconfig` on Linux systems -* `CONFIG_LEVEL_XDG` - XDG compatible configuration file; typically `~/.config/git/config` -* `CONFIG_LEVEL_GLOBAL` - User-specific configuration file (also called Global configuration file); typically `~/.gitconfig` -* `CONFIG_LEVEL_LOCAL` - Repository specific configuration file; `\$WORK_DIR/.git/config` on non-bare repos -* `CONFIG_LEVEL_APP` - Application specific configuration file; freely defined by applications -* `CONFIG_HIGHEST_LEVEL` - Represents the highest level available config file (i.e. the most specific config file available that actually is loaded) + Priority level of a config file. + + These priority levels correspond to the natural escalation logic (from higher to lower) when searching for config entries in git. + + * `CONFIG_LEVEL_DEFAULT` - Open the global, XDG and system configuration files if any available. + * `CONFIG_LEVEL_PROGRAMDATA` - System-wide on Windows, for compatibility with portable git + * `CONFIG_LEVEL_SYSTEM` - System-wide configuration file; `/etc/gitconfig` on Linux systems + * `CONFIG_LEVEL_XDG` - XDG compatible configuration file; typically `~/.config/git/config` + * `CONFIG_LEVEL_GLOBAL` - User-specific configuration file (also called Global configuration file); typically `~/.gitconfig` + * `CONFIG_LEVEL_LOCAL` - Repository specific configuration file; `\$WORK_DIR/.git/config` on non-bare repos + * `CONFIG_LEVEL_APP` - Application specific configuration file; freely defined by applications + * `CONFIG_HIGHEST_LEVEL` - Represents the highest level available config file (i.e. the most specific config file available that actually is loaded) """ @enum(GIT_CONFIG, CONFIG_LEVEL_DEFAULT = 0, CONFIG_LEVEL_PROGRAMDATA = 1, @@ -371,17 +371,17 @@ These priority levels correspond to the natural escalation logic (from higher to CONFIG_HIGHEST_LEVEL =-1) else """ -Priority level of a config file. + Priority level of a config file. -These priority levels correspond to the natural escalation logic (from higher to lower) when searching for config entries in git. + These priority levels correspond to the natural escalation logic (from higher to lower) when searching for config entries in git. -* `CONFIG_LEVEL_DEFAULT` - Open the global, XDG and system configuration files if any available. -* `CONFIG_LEVEL_SYSTEM` - System-wide configuration file; `/etc/gitconfig` on Linux systems -* `CONFIG_LEVEL_XDG` - XDG compatible configuration file; typically `~/.config/git/config` -* `CONFIG_LEVEL_GLOBAL` - User-specific configuration file (also called Global configuration file); typically `~/.gitconfig` -* `CONFIG_LEVEL_LOCAL` - Repository specific configuration file; `\$WORK_DIR/.git/config` on non-bare repos -* `CONFIG_LEVEL_APP` - Application specific configuration file; freely defined by applications -* `CONFIG_HIGHEST_LEVEL` - Represents the highest level available config file (i.e. the most specific config file available that actually is loaded) + * `CONFIG_LEVEL_DEFAULT` - Open the global, XDG and system configuration files if any available. + * `CONFIG_LEVEL_SYSTEM` - System-wide configuration file; `/etc/gitconfig` on Linux systems + * `CONFIG_LEVEL_XDG` - XDG compatible configuration file; typically `~/.config/git/config` + * `CONFIG_LEVEL_GLOBAL` - User-specific configuration file (also called Global configuration file); typically `~/.gitconfig` + * `CONFIG_LEVEL_LOCAL` - Repository specific configuration file; `\$WORK_DIR/.git/config` on non-bare repos + * `CONFIG_LEVEL_APP` - Application specific configuration file; freely defined by applications + * `CONFIG_HIGHEST_LEVEL` - Represents the highest level available config file (i.e. the most specific config file available that actually is loaded) """ @enum(GIT_CONFIG, CONFIG_LEVEL_DEFAULT = 0, CONFIG_LEVEL_SYSTEM = 1, @@ -392,35 +392,35 @@ These priority levels correspond to the natural escalation logic (from higher to CONFIG_HIGHEST_LEVEL =-1) end - """ +""" Global library options. These are used to select which global option to set or get and are used in `git_libgit2_opts()`. - """ - @enum(GIT_OPT, GET_MWINDOW_SIZE = 0, - SET_MWINDOW_SIZE = 1, - GET_MWINDOW_MAPPED_LIMIT = 2, - SET_MWINDOW_MAPPED_LIMIT = 3, - GET_SEARCH_PATH = 4, - SET_SEARCH_PATH = 5, - SET_CACHE_OBJECT_LIMIT = 6, - SET_CACHE_MAX_SIZE = 7, - ENABLE_CACHING = 8, - GET_CACHED_MEMORY = 9, - GET_TEMPLATE_PATH = 10, - SET_TEMPLATE_PATH = 11, - SET_SSL_CERT_LOCATIONS = 12) - - - """ +""" +@enum(GIT_OPT, GET_MWINDOW_SIZE = 0, + SET_MWINDOW_SIZE = 1, + GET_MWINDOW_MAPPED_LIMIT = 2, + SET_MWINDOW_MAPPED_LIMIT = 3, + GET_SEARCH_PATH = 4, + SET_SEARCH_PATH = 5, + SET_CACHE_OBJECT_LIMIT = 6, + SET_CACHE_MAX_SIZE = 7, + ENABLE_CACHING = 8, + GET_CACHED_MEMORY = 9, + GET_TEMPLATE_PATH = 10, + SET_TEMPLATE_PATH = 11, + SET_SSL_CERT_LOCATIONS = 12) + + +""" Option flags for `GitProxy`. * `PROXY_NONE`: do not attempt the connection through a proxy. * `PROXY_AUTO`: attempt to figure out the proxy configuration from the git configuration. * `PROXY_SPECIFIED`: connect using the URL given in the `url` field of this struct. - """ - @enum(GIT_PROXY, PROXY_NONE, - PROXY_AUTO, - PROXY_SPECIFIED) +""" +@enum(GIT_PROXY, PROXY_NONE, + PROXY_AUTO, + PROXY_SPECIFIED) end From e2a4c7235ad9d064198e6f4af22b563666f33355 Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Mon, 28 Aug 2017 15:53:17 -0500 Subject: [PATCH 251/324] Fix GIT_CONFIG docstring --- base/libgit2/consts.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/base/libgit2/consts.jl b/base/libgit2/consts.jl index 83feeeb9ed0cb..c16cb651c0378 100644 --- a/base/libgit2/consts.jl +++ b/base/libgit2/consts.jl @@ -347,7 +347,7 @@ Option flags for `GitRepo`. FEATURE_NSEC = Cuint(1 << 3)) if LibGit2.version() >= v"0.24.0" - """ + @doc """ Priority level of a config file. These priority levels correspond to the natural escalation logic (from higher to lower) when searching for config entries in git. @@ -360,7 +360,7 @@ if LibGit2.version() >= v"0.24.0" * `CONFIG_LEVEL_LOCAL` - Repository specific configuration file; `\$WORK_DIR/.git/config` on non-bare repos * `CONFIG_LEVEL_APP` - Application specific configuration file; freely defined by applications * `CONFIG_HIGHEST_LEVEL` - Represents the highest level available config file (i.e. the most specific config file available that actually is loaded) - """ + """ -> @enum(GIT_CONFIG, CONFIG_LEVEL_DEFAULT = 0, CONFIG_LEVEL_PROGRAMDATA = 1, CONFIG_LEVEL_SYSTEM = 2, @@ -370,7 +370,7 @@ if LibGit2.version() >= v"0.24.0" CONFIG_LEVEL_APP = 6, CONFIG_HIGHEST_LEVEL =-1) else - """ + @doc """ Priority level of a config file. These priority levels correspond to the natural escalation logic (from higher to lower) when searching for config entries in git. @@ -382,7 +382,7 @@ else * `CONFIG_LEVEL_LOCAL` - Repository specific configuration file; `\$WORK_DIR/.git/config` on non-bare repos * `CONFIG_LEVEL_APP` - Application specific configuration file; freely defined by applications * `CONFIG_HIGHEST_LEVEL` - Represents the highest level available config file (i.e. the most specific config file available that actually is loaded) - """ + """ -> @enum(GIT_CONFIG, CONFIG_LEVEL_DEFAULT = 0, CONFIG_LEVEL_SYSTEM = 1, CONFIG_LEVEL_XDG = 2, From e235f65ee228660fb0e969d788a1c1ddae33de14 Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Sun, 3 Sep 2017 18:47:26 -0500 Subject: [PATCH 252/324] Fix GIT_OPT indentation --- base/libgit2/consts.jl | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/base/libgit2/consts.jl b/base/libgit2/consts.jl index c16cb651c0378..07f6f75c6df44 100644 --- a/base/libgit2/consts.jl +++ b/base/libgit2/consts.jl @@ -397,19 +397,19 @@ Global library options. These are used to select which global option to set or get and are used in `git_libgit2_opts()`. """ -@enum(GIT_OPT, GET_MWINDOW_SIZE = 0, - SET_MWINDOW_SIZE = 1, - GET_MWINDOW_MAPPED_LIMIT = 2, - SET_MWINDOW_MAPPED_LIMIT = 3, - GET_SEARCH_PATH = 4, - SET_SEARCH_PATH = 5, - SET_CACHE_OBJECT_LIMIT = 6, - SET_CACHE_MAX_SIZE = 7, - ENABLE_CACHING = 8, - GET_CACHED_MEMORY = 9, - GET_TEMPLATE_PATH = 10, - SET_TEMPLATE_PATH = 11, - SET_SSL_CERT_LOCATIONS = 12) +@enum(GIT_OPT, GET_MWINDOW_SIZE = 0, + SET_MWINDOW_SIZE = 1, + GET_MWINDOW_MAPPED_LIMIT = 2, + SET_MWINDOW_MAPPED_LIMIT = 3, + GET_SEARCH_PATH = 4, + SET_SEARCH_PATH = 5, + SET_CACHE_OBJECT_LIMIT = 6, + SET_CACHE_MAX_SIZE = 7, + ENABLE_CACHING = 8, + GET_CACHED_MEMORY = 9, + GET_TEMPLATE_PATH = 10, + SET_TEMPLATE_PATH = 11, + SET_SSL_CERT_LOCATIONS = 12) """ From a3de583f4b9685aa2a14b10103686126b763f310 Mon Sep 17 00:00:00 2001 From: quinnj Date: Mon, 4 Sep 2017 08:23:59 -0600 Subject: [PATCH 253/324] Fix #23567. In general, we need to be careful when we start branching on isbits(eltype(::Array)) because of Union isbits optimizations because isbits Unions don't return isbits == true. --- base/array.jl | 18 ++++++++++++++++++ test/core.jl | 17 +++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/base/array.jl b/base/array.jl index 61ef1fbd63f29..5a8840f133af6 100644 --- a/base/array.jl +++ b/base/array.jl @@ -181,6 +181,14 @@ the same manner as C. function unsafe_copy!(dest::Array{T}, doffs, src::Array{T}, soffs, n) where T if isbits(T) unsafe_copy!(pointer(dest, doffs), pointer(src, soffs), n) + elseif isbitsunion(T) + ccall(:memmove, Ptr{Void}, (Ptr{Void}, Ptr{Void}, UInt), + pointer(dest, doffs), pointer(src, soffs), n * Base.bitsunionsize(T)) + # copy selector bytes + ccall(:memmove, Ptr{Void}, (Ptr{Void}, Ptr{Void}, UInt), + convert(Ptr{UInt8}, pointer(dest)) + length(dest) * Base.bitsunionsize(T) + doffs - 1, + convert(Ptr{UInt8}, pointer(src)) + length(src) * Base.bitsunionsize(T) + soffs - 1, + n) else ccall(:jl_array_ptr_copy, Void, (Any, Ptr{Void}, Any, Ptr{Void}, Int), dest, pointer(dest, doffs), src, pointer(src, soffs), n) @@ -1561,6 +1569,9 @@ function vcat(arrays::Vector{T}...) where T ptr = pointer(arr) if isbits(T) elsz = Core.sizeof(T) + elseif isbitsunion(T) + elsz = bitsunionsize(T) + selptr = convert(Ptr{UInt8}, ptr) + n * elsz else elsz = Core.sizeof(Ptr{Void}) end @@ -1570,6 +1581,13 @@ function vcat(arrays::Vector{T}...) where T if isbits(T) ccall(:memcpy, Ptr{Void}, (Ptr{Void}, Ptr{Void}, UInt), ptr, a, nba) + elseif isbitsunion(T) + ccall(:memcpy, Ptr{Void}, (Ptr{Void}, Ptr{Void}, UInt), + ptr, a, nba) + # copy selector bytes + ccall(:memcpy, Ptr{Void}, (Ptr{Void}, Ptr{Void}, UInt), + selptr, convert(Ptr{UInt8}, pointer(a)) + nba, na) + selptr += na else ccall(:jl_array_ptr_copy, Void, (Any, Ptr{Void}, Any, Ptr{Void}, Int), arr, ptr, a, pointer(a), na) diff --git a/test/core.jl b/test/core.jl index 00ddd2b51a656..dfdbfb13513f0 100644 --- a/test/core.jl +++ b/test/core.jl @@ -5336,6 +5336,23 @@ A4 = [1, 2, 3] A5 = [1 2 3; 4 5 6] @test_throws ArgumentError unsafe_wrap(Array, convert(Ptr{Union{Int, Void}}, pointer(A5)), 6) +# copy! +A23567 = Vector{Union{Float64, Void}}(5) +B23567 = collect(Union{Float64, Void}, 1.0:3.0) +copy!(A23567, 2, B23567) +@test A23567[1] === nothing +@test A23567[2] === 1.0 +@test A23567[3] === 2.0 +@test A23567[4] === 3.0 + +# vcat +t2 = deepcopy(A23567) +t3 = deepcopy(A23567) +t4 = vcat(A23567, t2, t3) +@test t4[1:5] == A23567 +@test t4[6:10] == A23567 +@test t4[11:15] == A23567 + for U in unboxedunions local U for N in (1, 2, 3, 4) From c73da152f1018d904aeff886e4bfeec90ce10130 Mon Sep 17 00:00:00 2001 From: quinnj Date: Mon, 4 Sep 2017 09:01:15 -0600 Subject: [PATCH 254/324] Remove incorrect code from jl_array_ptr_copy for isbits Union arrays --- src/array.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/array.c b/src/array.c index 2c3deaa1f9bb2..0810ab9348958 100644 --- a/src/array.c +++ b/src/array.c @@ -1092,14 +1092,6 @@ static NOINLINE ssize_t jl_array_ptr_copy_backward(jl_value_t *owner, JL_DLLEXPORT void jl_array_ptr_copy(jl_array_t *dest, void **dest_p, jl_array_t *src, void **src_p, ssize_t n) { - // need to intercept union isbits arrays here since they're unboxed - if (!src->flags.ptrarray && jl_is_uniontype(jl_tparam0(jl_typeof(src))) && - !dest->flags.ptrarray && jl_is_uniontype(jl_tparam0(jl_typeof(dest)))) { - memcpy(dest_p, src_p, n * src->elsize); - memcpy((char*)dest->data + jl_array_len(dest) * dest->elsize, - (char*)src->data + jl_array_len(src) * src->elsize, n); - return; - } assert(dest->flags.ptrarray && src->flags.ptrarray); jl_value_t *owner = jl_array_owner(dest); // Destination is old and doesn't refer to any young object From 063f0f8d1a3dd5cad3a3e3106d59f54ba6c71aee Mon Sep 17 00:00:00 2001 From: quinnj Date: Mon, 4 Sep 2017 10:17:06 -0600 Subject: [PATCH 255/324] Update unsafe_convert for isbits Unions according to recommendation from yuyichao. --- base/refpointer.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/refpointer.jl b/base/refpointer.jl index 44a625c76b6cf..9ad3be1d89dbf 100644 --- a/base/refpointer.jl +++ b/base/refpointer.jl @@ -53,7 +53,7 @@ Ref{T}(x) where {T} = RefValue{T}(x) # Ref{T}(x) convert(::Type{Ref{T}}, x) where {T} = RefValue{T}(x) function unsafe_convert(P::Type{Ptr{T}}, b::RefValue{T}) where T - if isbits(T) + if isbits(T) || isbitsunion(T) return convert(P, pointer_from_objref(b)) elseif isleaftype(T) return convert(P, pointer_from_objref(b.x)) From 54a2b012f103c45a4078592c0de62e14ebb12bed Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Mon, 4 Sep 2017 21:36:30 +0200 Subject: [PATCH 256/324] Unify function references in docs (#23535) * Unify function references by removing misleading () * review fix --- NEWS.md | 4 +- base/asyncmap.jl | 8 +- base/distributed/remotecall.jl | 2 +- base/distributed/workerpool.jl | 2 +- base/docs/basedocs.jl | 4 +- base/env.jl | 2 +- base/indices.jl | 2 +- base/printf.jl | 2 +- doc/src/devdocs/ast.md | 2 +- doc/src/devdocs/reflection.md | 18 +-- doc/src/devdocs/stdio.md | 2 +- doc/src/devdocs/sysimg.md | 2 +- doc/src/manual/arrays.md | 62 +++++----- doc/src/manual/calling-c-and-fortran-code.md | 46 ++++---- .../manual/complex-and-rational-numbers.md | 12 +- doc/src/manual/constructors.md | 4 +- doc/src/manual/control-flow.md | 70 +++++------ doc/src/manual/conversion-and-promotion.md | 4 +- doc/src/manual/dates.md | 18 +-- doc/src/manual/faq.md | 10 +- doc/src/manual/functions.md | 38 +++--- .../integers-and-floating-point-numbers.md | 22 ++-- doc/src/manual/interfaces.md | 72 ++++++------ doc/src/manual/linear-algebra.md | 42 +++---- doc/src/manual/mathematical-operations.md | 14 +-- doc/src/manual/metaprogramming.md | 46 ++++---- doc/src/manual/methods.md | 4 +- doc/src/manual/modules.md | 4 +- doc/src/manual/networking-and-streams.md | 42 +++---- doc/src/manual/noteworthy-differences.md | 42 +++---- doc/src/manual/parallel-computing.md | 110 +++++++++--------- doc/src/manual/performance-tips.md | 18 +-- doc/src/manual/profile.md | 2 +- doc/src/manual/running-external-programs.md | 4 +- doc/src/manual/stacktraces.md | 26 ++--- doc/src/manual/strings.md | 38 +++--- doc/src/manual/style-guide.md | 18 +-- doc/src/manual/types.md | 40 +++---- doc/src/manual/variables-and-scoping.md | 4 +- doc/src/stdlib/collections.md | 8 +- doc/src/stdlib/libdl.md | 2 +- doc/src/stdlib/test.md | 14 +-- 42 files changed, 443 insertions(+), 443 deletions(-) diff --git a/NEWS.md b/NEWS.md index 3f14cd4f51368..c951c5ea92cc5 100644 --- a/NEWS.md +++ b/NEWS.md @@ -217,7 +217,7 @@ Library improvements * The `crc32c` function for CRC-32c checksums is now exported ([#22274]). - * The output of `versioninfo()` is now controlled with keyword arguments ([#21974]). + * The output of `versioninfo` is now controlled with keyword arguments ([#21974]). * The function `LibGit2.set_remote_url` now always sets both the fetch and push URLs for a git repo. Additionally, the argument order was changed to be consistent with the git @@ -889,7 +889,7 @@ Deprecated or removed `pop!(ENV, k, def)`. Be aware that `pop!` returns `k` or `def`, whereas `delete!` returns `ENV` or `def` ([#18012]). - * infix operator `$` has been deprecated in favor of infix `⊻` or function `xor()` ([#18977]). + * infix operator `$` has been deprecated in favor of infix `⊻` or function `xor` ([#18977]). * The single-argument form of `write` (`write(x)`, with implicit `STDOUT` output stream), has been deprecated in favor of the explicit equivalent `write(STDOUT, x)` ([#17654]). diff --git a/base/asyncmap.jl b/base/asyncmap.jl index 19846ab7a0ced..7394182bb48b8 100644 --- a/base/asyncmap.jl +++ b/base/asyncmap.jl @@ -15,7 +15,7 @@ up to 100 tasks will be used for concurrent mapping. `ntasks` can also be specified as a zero-arg function. In this case, the number of tasks to run in parallel is checked before processing every element and a new -task started if the value of `ntasks_func()` is less than the current number +task started if the value of `ntasks_func` is less than the current number of tasks. If `batch_size` is specified, the collection is processed in batch mode. `f` must @@ -285,7 +285,7 @@ Returns an iterator which applies `f` to each element of `c` asynchronously and collects output into `results`. Keyword args `ntasks` and `batch_size` have the same behavior as in -[`asyncmap()`](@ref). If `batch_size` is specified, `f` must +[`asyncmap`](@ref). If `batch_size` is specified, `f` must be a function which operates on an array of argument tuples. !!! note @@ -360,7 +360,7 @@ end Apply `f` to each element of `c` using at most `ntasks` asynchronous tasks. Keyword args `ntasks` and `batch_size` have the same behavior as in -[`asyncmap()`](@ref). If `batch_size` is specified, `f` must +[`asyncmap`](@ref). If `batch_size` is specified, `f` must be a function which operates on an array of argument tuples. !!! note @@ -415,7 +415,7 @@ length(itr::AsyncGenerator) = length(itr.collector.enumerator) """ asyncmap!(f, results, c...; ntasks=0, batch_size=nothing) -Like [`asyncmap()`](@ref), but stores output in `results` rather than +Like [`asyncmap`](@ref), but stores output in `results` rather than returning a collection. """ function asyncmap!(f, r, c1, c...; ntasks=0, batch_size=nothing) diff --git a/base/distributed/remotecall.jl b/base/distributed/remotecall.jl index 68b143aff2093..1e87ba22b6698 100644 --- a/base/distributed/remotecall.jl +++ b/base/distributed/remotecall.jl @@ -94,7 +94,7 @@ RemoteChannel(pid::Integer=myid()) = RemoteChannel{Channel{Any}}(pid, RRID()) """ RemoteChannel(f::Function, pid::Integer=myid()) -Create references to remote channels of a specific size and type. `f()` is a function that +Create references to remote channels of a specific size and type. `f` is a function that when executed on `pid` must return an implementation of an `AbstractChannel`. For example, `RemoteChannel(()->Channel{Int}(10), pid)`, will return a reference to a diff --git a/base/distributed/workerpool.jl b/base/distributed/workerpool.jl index 0406f80bbe3b4..7aedd63c844c4 100644 --- a/base/distributed/workerpool.jl +++ b/base/distributed/workerpool.jl @@ -190,7 +190,7 @@ const _default_worker_pool = Ref{Nullable}(Nullable{WorkerPool}()) """ default_worker_pool() -`WorkerPool` containing idle `workers()` - used by `remote(f)` and [`pmap`](@ref) (by default). +`WorkerPool` containing idle `workers` - used by `remote(f)` and [`pmap`](@ref) (by default). """ function default_worker_pool() # On workers retrieve the default worker pool from the master when accessed diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index 366c1db1bd01e..6dec34c97e38a 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -132,7 +132,7 @@ kw"primitive type" """ `macro` defines a method to include generated code in the final body of a program. A macro maps a tuple of arguments to a returned expression, and the resulting expression -is compiled directly rather than requiring a runtime `eval()` call. Macro arguments may +is compiled directly rather than requiring a runtime `eval` call. Macro arguments may include expressions, literal values, and symbols. For example: macro sayhello(name) @@ -189,7 +189,7 @@ modify the global variable `z`: julia> z 6 -Without the `global` declaration in `foo()`, a new local variable would have been +Without the `global` declaration in `foo`, a new local variable would have been created inside foo(), and the `z` in the global scope would have remained equal to `3`. """ kw"global" diff --git a/base/env.jl b/base/env.jl index 582f0d823436c..fb2d306e11f62 100644 --- a/base/env.jl +++ b/base/env.jl @@ -143,7 +143,7 @@ end """ withenv(f::Function, kv::Pair...) -Execute `f()` in an environment that is temporarily modified (not replaced as in `setenv`) +Execute `f` in an environment that is temporarily modified (not replaced as in `setenv`) by zero or more `"var"=>val` arguments `kv`. `withenv` is generally used via the `withenv(kv...) do ... end` syntax. A value of `nothing` can be used to temporarily unset an environment variable (if it is set). When `withenv` returns, the original environment has diff --git a/base/indices.jl b/base/indices.jl index 5b16ec88ae45e..a5f705d2ac201 100644 --- a/base/indices.jl +++ b/base/indices.jl @@ -224,7 +224,7 @@ _maybetail(t::Tuple) = tail(t) Represent an AbstractUnitRange of indices as a vector of the indices themselves. -Upon calling `to_indices()`, Colons are converted to Slice objects to represent +Upon calling `to_indices`, Colons are converted to Slice objects to represent the indices over which the Colon spans. Slice objects are themselves unit ranges with the same indices as those they wrap. This means that indexing into Slice objects with an integer always returns that exact integer, and they diff --git a/base/printf.jl b/base/printf.jl index 4fcde4a41f4dd..c2ca443ba8bc0 100644 --- a/base/printf.jl +++ b/base/printf.jl @@ -1194,7 +1194,7 @@ end """ @printf([io::IOStream], "%Fmt", args...) -Print `args` using C `printf()` style format specification string, with some caveats: +Print `args` using C `printf` style format specification string, with some caveats: `Inf` and `NaN` are printed consistently as `Inf` and `NaN` for flags `%a`, `%A`, `%e`, `%E`, `%f`, `%F`, `%g`, and `%G`. Furthermore, if a floating point number is equally close to the numeric values of two possible output strings, the output diff --git a/doc/src/devdocs/ast.md b/doc/src/devdocs/ast.md index 3e1aad5a394eb..ae9622fbb7866 100644 --- a/doc/src/devdocs/ast.md +++ b/doc/src/devdocs/ast.md @@ -1,7 +1,7 @@ # Julia ASTs Julia has two representations of code. First there is a surface syntax AST returned by the parser -(e.g. the [`parse()`](@ref) function), and manipulated by macros. It is a structured representation +(e.g. the [`parse`](@ref) function), and manipulated by macros. It is a structured representation of code as it is written, constructed by `julia-parser.scm` from a character stream. Next there is a lowered form, or IR (intermediate representation), which is used by type inference and code generation. In the lowered form there are fewer types of nodes, all macros are expanded, and all diff --git a/doc/src/devdocs/reflection.md b/doc/src/devdocs/reflection.md index 09d2bba65abec..f835bdac7b1dc 100644 --- a/doc/src/devdocs/reflection.md +++ b/doc/src/devdocs/reflection.md @@ -10,7 +10,7 @@ returns symbols for all bindings in `m`, regardless of export status. ## DataType fields -The names of `DataType` fields may be interrogated using [`fieldnames()`](@ref). For example, +The names of `DataType` fields may be interrogated using [`fieldnames`](@ref). For example, given the following type, `fieldnames(Point)` returns an arrays of [`Symbol`](@ref) elements representing the field names: @@ -49,7 +49,7 @@ of these fields is the `types` field observed in the example above. ## Subtypes -The *direct* subtypes of any `DataType` may be listed using [`subtypes()`](@ref). For example, +The *direct* subtypes of any `DataType` may be listed using [`subtypes`](@ref). For example, the abstract `DataType` [`AbstractFloat`](@ref) has four (concrete) subtypes: ```jldoctest @@ -62,7 +62,7 @@ julia> subtypes(AbstractFloat) ``` Any abstract subtype will also be included in this list, but further subtypes thereof will not; -recursive application of [`subtypes()`](@ref) may be used to inspect the full type tree. +recursive application of [`subtypes`](@ref) may be used to inspect the full type tree. ## DataType layout @@ -73,12 +73,12 @@ returns the (byte) offset for field *i* relative to the start of the type. ## Function methods -The methods of any generic function may be listed using [`methods()`](@ref). The method dispatch -table may be searched for methods accepting a given type using [`methodswith()`](@ref). +The methods of any generic function may be listed using [`methods`](@ref). The method dispatch +table may be searched for methods accepting a given type using [`methodswith`](@ref). ## Expansion and lowering -As discussed in the [Metaprogramming](@ref) section, the [`macroexpand()`](@ref) function gives +As discussed in the [Metaprogramming](@ref) section, the [`macroexpand`](@ref) function gives the unquoted and interpolated expression (`Expr`) form for a given macro. To use `macroexpand`, `quote` the expression block itself (otherwise, the macro will be evaluated and the result will be passed instead!). For example: @@ -88,10 +88,10 @@ julia> macroexpand(@__MODULE__, :(@edit println("")) ) :((Base.edit)(println, (Base.typesof)(""))) ``` -The functions `Base.Meta.show_sexpr()` and [`dump()`](@ref) are used to display S-expr style views +The functions `Base.Meta.show_sexpr` and [`dump`](@ref) are used to display S-expr style views and depth-nested detail views for any expression. -Finally, the [`expand()`](@ref) function gives the `lowered` form of any expression and is of +Finally, the [`expand`](@ref) function gives the `lowered` form of any expression and is of particular interest for understanding both macros and top-level statements such as function declarations and variable assignments: @@ -113,7 +113,7 @@ Inspecting the lowered form for functions requires selection of the specific met because generic functions may have many methods with different type signatures. For this purpose, method-specific code-lowering is available using [`code_lowered(f::Function, (Argtypes...))`](@ref), and the type-inferred form is available using [`code_typed(f::Function, (Argtypes...))`](@ref). -[`code_warntype(f::Function, (Argtypes...))`](@ref) adds highlighting to the output of [`code_typed()`](@ref) +[`code_warntype(f::Function, (Argtypes...))`](@ref) adds highlighting to the output of [`code_typed`](@ref) (see [`@code_warntype`](@ref)). Closer to the machine, the LLVM intermediate representation of a function may be printed using diff --git a/doc/src/devdocs/stdio.md b/doc/src/devdocs/stdio.md index f78d7c623e62b..2adc259e03640 100644 --- a/doc/src/devdocs/stdio.md +++ b/doc/src/devdocs/stdio.md @@ -50,7 +50,7 @@ $ echo hello | julia -e 'println(typeof((STDIN, STDOUT, STDERR)))' | cat Tuple{Base.PipeEndpoint,Base.PipeEndpoint,Base.TTY} ``` -The [`Base.read()`](@ref) and [`Base.write()`](@ref) methods for these streams use [`ccall`](@ref) +The [`Base.read`](@ref) and [`Base.write`](@ref) methods for these streams use [`ccall`](@ref) to call libuv wrappers in `src/jl_uv.c`, e.g.: ``` diff --git a/doc/src/devdocs/sysimg.md b/doc/src/devdocs/sysimg.md index dbda7567e1076..2248911710e90 100644 --- a/doc/src/devdocs/sysimg.md +++ b/doc/src/devdocs/sysimg.md @@ -24,7 +24,7 @@ Julia session, type: include(joinpath(JULIA_HOME, Base.DATAROOTDIR, "julia", "build_sysimg.jl")) ``` -This will include a `build_sysimg()` function: +This will include a `build_sysimg` function: ```@docs BuildSysImg.build_sysimg diff --git a/doc/src/manual/arrays.md b/doc/src/manual/arrays.md index e3dddc0dbe57f..e9b5d50d8580d 100644 --- a/doc/src/manual/arrays.md +++ b/doc/src/manual/arrays.md @@ -76,7 +76,7 @@ omitted it will default to [`Float64`](@ref). The syntax `[A, B, C, ...]` constructs a 1-d array (vector) of its arguments. If all arguments have a common [promotion type](@ref conversion-and-promotion) then they get -converted to that type using `convert()`. +converted to that type using `convert`. ### Concatenation @@ -94,11 +94,11 @@ The concatenation functions are used so often that they have special syntax: | Expression | Calls | |:----------------- |:----------------- | -| `[A; B; C; ...]` | [`vcat()`](@ref) | -| `[A B C ...]` | [`hcat()`](@ref) | -| `[A B; C D; ...]` | [`hvcat()`](@ref) | +| `[A; B; C; ...]` | [`vcat`](@ref) | +| `[A B C ...]` | [`hcat`](@ref) | +| `[A B; C D; ...]` | [`hvcat`](@ref) | -[`hvcat()`](@ref) concatenates in both dimension 1 (with semicolons) and dimension 2 (with spaces). +[`hvcat`](@ref) concatenates in both dimension 1 (with semicolons) and dimension 2 (with spaces). ### Typed array initializers @@ -278,7 +278,7 @@ julia> x[1, [2 3; 4 1]] ``` Empty ranges of the form `n:n-1` are sometimes used to indicate the inter-index location between -`n-1` and `n`. For example, the [`searchsorted()`](@ref) function uses this convention to indicate +`n-1` and `n`. For example, the [`searchsorted`](@ref) function uses this convention to indicate the insertion point of a value not found in a sorted array: ```jldoctest @@ -311,7 +311,7 @@ Just as in [Indexing](@ref man-array-indexing), the `end` keyword may be used to represent the last index of each dimension within the indexing brackets, as determined by the size of the array being assigned into. Indexed assignment syntax without the `end` keyword is equivalent to a call to -[`setindex!()`](@ref): +[`setindex!`](@ref): ``` setindex!(A, X, I_1, I_2, ..., I_n) @@ -440,7 +440,7 @@ vector of `CartesianIndex{N}`s where its values are `true`. A logical index must be a vector of the same length as the dimension it indexes into, or it must be the only index provided and match the size and dimensionality of the array it indexes into. It is generally more efficient to use boolean arrays as -indices directly instead of first calling [`find()`](@ref). +indices directly instead of first calling [`find`](@ref). ```jldoctest julia> x = reshape(1:16, 4, 4) @@ -546,7 +546,7 @@ Note that comparisons such as `==` operate on whole arrays, giving a single bool answer. Use dot operators like `.==` for elementwise comparisons. (For comparison operations like `<`, *only* the elementwise `.<` version is applicable to arrays.) -Also notice the difference between `max.(a,b)`, which `broadcast`s [`max()`](@ref) +Also notice the difference between `max.(a,b)`, which `broadcast`s [`max`](@ref) elementwise over `a` and `b`, and `maximum(a)`, which finds the largest value within `a`. The same relationship holds for `min.(a,b)` and `minimum(a)`. @@ -565,7 +565,7 @@ julia> repmat(a,1,3)+A 1.56851 1.86401 1.67846 ``` -This is wasteful when dimensions get large, so Julia offers [`broadcast()`](@ref), which expands +This is wasteful when dimensions get large, so Julia offers [`broadcast`](@ref), which expands singleton dimensions in array arguments to match the corresponding dimension in the other array without using extra memory, and applies the given function elementwise: @@ -587,14 +587,14 @@ julia> broadcast(+, a, b) [Dotted operators](@ref man-dot-operators) such as `.+` and `.*` are equivalent to `broadcast` calls (except that they fuse, as described below). There is also a -[`broadcast!()`](@ref) function to specify an explicit destination (which can also -be accessed in a fusing fashion by `.=` assignment), and functions [`broadcast_getindex()`](@ref) -and [`broadcast_setindex!()`](@ref) that broadcast the indices before indexing. Moreover, `f.(args...)` +[`broadcast!`](@ref) function to specify an explicit destination (which can also +be accessed in a fusing fashion by `.=` assignment), and functions [`broadcast_getindex`](@ref) +and [`broadcast_setindex!`](@ref) that broadcast the indices before indexing. Moreover, `f.(args...)` is equivalent to `broadcast(f, args...)`, providing a convenient syntax to broadcast any function ([dot syntax](@ref man-vectorized)). Nested "dot calls" `f.(...)` (including calls to `.+` etcetera) [automatically fuse](@ref man-dot-operators) into a single `broadcast` call. -Additionally, [`broadcast()`](@ref) is not limited to arrays (see the function documentation), +Additionally, [`broadcast`](@ref) is not limited to arrays (see the function documentation), it also handles tuples and treats any argument that is not an array, tuple or `Ref` (except for `Ptr`) as a "scalar". ```jldoctest @@ -627,13 +627,13 @@ The `AbstractArray` type includes anything vaguely array-like, and implementatio be quite different from conventional arrays. For example, elements might be computed on request rather than stored. However, any concrete `AbstractArray{T,N}` type should generally implement at least [`size(A)`](@ref) (returning an `Int` tuple), [`getindex(A,i)`](@ref) and [`getindex(A,i1,...,iN)`](@ref getindex); -mutable arrays should also implement [`setindex!()`](@ref). It is recommended that these operations +mutable arrays should also implement [`setindex!`](@ref). It is recommended that these operations have nearly constant time complexity, or technically Õ(1) complexity, as otherwise some array functions may be unexpectedly slow. Concrete types should also typically provide a [`similar(A,T=eltype(A),dims=size(A))`](@ref) -method, which is used to allocate a similar array for [`copy()`](@ref) and other out-of-place +method, which is used to allocate a similar array for [`copy`](@ref) and other out-of-place operations. No matter how an `AbstractArray{T,N}` is represented internally, `T` is the type of object returned by *integer* indexing (`A[1, ..., 1]`, when `A` is not empty) and `N` should be -the length of the tuple returned by [`size()`](@ref). +the length of the tuple returned by [`size`](@ref). `DenseArray` is an abstract subtype of `AbstractArray` intended to include all arrays that are laid out at regular offsets in memory, and which can therefore be passed to external C and Fortran @@ -650,9 +650,9 @@ basic storage-specific operations are all that have to be implemented for [`Arra that the rest of the array library can be implemented in a generic manner. `SubArray` is a specialization of `AbstractArray` that performs indexing by reference rather than -by copying. A `SubArray` is created with the [`view()`](@ref) function, which is called the same -way as [`getindex()`](@ref) (with an array and a series of index arguments). The result of [`view()`](@ref) -looks the same as the result of [`getindex()`](@ref), except the data is left in place. [`view()`](@ref) +by copying. A `SubArray` is created with the [`view`](@ref) function, which is called the same +way as [`getindex`](@ref) (with an array and a series of index arguments). The result of [`view`](@ref) +looks the same as the result of [`getindex`](@ref), except the data is left in place. [`view`](@ref) stores the input index vectors in a `SubArray` object, which can later be used to index the original array indirectly. By putting the [`@views`](@ref) macro in front of an expression or block of code, any `array[...]` slice in that expression will be converted to @@ -743,10 +743,10 @@ them is by doing a double transpose. In some applications, it is convenient to store explicit zero values in a `SparseMatrixCSC`. These *are* accepted by functions in `Base` (but there is no guarantee that they will be preserved in mutating operations). Such explicitly stored zeros are treated as structural nonzeros by many -routines. The [`nnz()`](@ref) function returns the number of elements explicitly stored in the +routines. The [`nnz`](@ref) function returns the number of elements explicitly stored in the sparse data structure, including structural nonzeros. In order to count the exact number of numerical nonzeros, use [`count(!iszero, x)`](@ref), which inspects every stored element of a sparse -matrix. [`dropzeros()`](@ref), and the in-place [`dropzeros!()`](@ref), can be used to +matrix. [`dropzeros`](@ref), and the in-place [`dropzeros!`](@ref), can be used to remove stored zeros from the sparse matrix. ```jldoctest @@ -781,8 +781,8 @@ stored zeros. (See [Sparse Matrix Storage](@ref man-csc).). ### Sparse Vector and Matrix Constructors -The simplest way to create sparse arrays is to use functions equivalent to the [`zeros()`](@ref) -and [`eye()`](@ref) functions that Julia provides for working with dense arrays. To produce +The simplest way to create sparse arrays is to use functions equivalent to the [`zeros`](@ref) +and [`eye`](@ref) functions that Julia provides for working with dense arrays. To produce sparse arrays instead, you can use the same names with an `sp` prefix: ```jldoctest @@ -796,7 +796,7 @@ julia> speye(3,5) [3, 3] = 1.0 ``` -The [`sparse()`](@ref) function is often a handy way to construct sparse arrays. For +The [`sparse`](@ref) function is often a handy way to construct sparse arrays. For example, to construct a sparse matrix we can input a vector `I` of row indices, a vector `J` of column indices, and a vector `V` of stored values (this is also known as the [COO (coordinate) format](https://en.wikipedia.org/wiki/Sparse_matrix#Coordinate_list_.28COO.29)). @@ -823,8 +823,8 @@ julia> R = sparsevec(I,V) [5] = 3 ``` -The inverse of the [`sparse()`](@ref) and [`sparsevec`](@ref) functions is -[`findnz()`](@ref), which retrieves the inputs used to create the sparse array. +The inverse of the [`sparse`](@ref) and [`sparsevec`](@ref) functions is +[`findnz`](@ref), which retrieves the inputs used to create the sparse array. There is also a [`findn`](@ref) function which only returns the index vectors. ```jldoctest sparse_function @@ -846,7 +846,7 @@ julia> findn(R) ``` Another way to create a sparse array is to convert a dense array into a sparse array using -the [`sparse()`](@ref) function: +the [`sparse`](@ref) function: ```jldoctest julia> sparse(eye(5)) @@ -863,7 +863,7 @@ julia> sparse([1.0, 0.0, 1.0]) [3] = 1.0 ``` -You can go in the other direction using the [`Array`](@ref) constructor. The [`issparse()`](@ref) +You can go in the other direction using the [`Array`](@ref) constructor. The [`issparse`](@ref) function can be used to query if a matrix is sparse. ```jldoctest @@ -876,7 +876,7 @@ true Arithmetic operations on sparse matrices also work as they do on dense matrices. Indexing of, assignment into, and concatenation of sparse matrices work in the same way as dense matrices. Indexing operations, especially assignment, are expensive, when carried out one element at a time. -In many cases it may be better to convert the sparse matrix into `(I,J,V)` format using [`findnz()`](@ref), +In many cases it may be better to convert the sparse matrix into `(I,J,V)` format using [`findnz`](@ref), manipulate the values or the structure in the dense vectors `(I,J,V)`, and then reconstruct the sparse matrix. @@ -894,7 +894,7 @@ section of the standard library reference. | Sparse | Dense | Description | |:-------------------------- |:---------------------- |:--------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [`spzeros(m,n)`](@ref) | [`zeros(m,n)`](@ref) | Creates a *m*-by-*n* matrix of zeros. ([`spzeros(m,n)`](@ref) is empty.) | -| [`spones(S)`](@ref) | [`ones(m,n)`](@ref) | Creates a matrix filled with ones. Unlike the dense version, [`spones()`](@ref) has the same sparsity pattern as *S*. | +| [`spones(S)`](@ref) | [`ones(m,n)`](@ref) | Creates a matrix filled with ones. Unlike the dense version, [`spones`](@ref) has the same sparsity pattern as *S*. | | [`speye(n)`](@ref) | [`eye(n)`](@ref) | Creates a *n*-by-*n* identity matrix. | | [`full(S)`](@ref) | [`sparse(A)`](@ref) | Interconverts between dense and sparse formats. | | [`sprand(m,n,d)`](@ref) | [`rand(m,n)`](@ref) | Creates a *m*-by-*n* random matrix (of density *d*) with iid non-zero elements distributed uniformly on the half-open interval ``[0, 1)``. | diff --git a/doc/src/manual/calling-c-and-fortran-code.md b/doc/src/manual/calling-c-and-fortran-code.md index f8ac137da78f6..a0ef7744c4b06 100644 --- a/doc/src/manual/calling-c-and-fortran-code.md +++ b/doc/src/manual/calling-c-and-fortran-code.md @@ -153,8 +153,8 @@ For example, to match C prototypes of the form: typedef returntype (*functiontype)(argumenttype,...) ``` -The function [`cfunction()`](@ref) generates the C-compatible function pointer for a call to a -Julia function. Arguments to [`cfunction()`](@ref) are as follows: +The function [`cfunction`](@ref) generates the C-compatible function pointer for a call to a +Julia function. Arguments to [`cfunction`](@ref) are as follows: 1. A Julia Function 2. Return type @@ -195,7 +195,7 @@ In order to pass this function to C, we obtain its address using the function `c julia> const mycompare_c = cfunction(mycompare, Cint, Tuple{Ref{Cdouble}, Ref{Cdouble}}); ``` -[`cfunction()`](@ref) accepts three arguments: the Julia function (`mycompare`), the return type +[`cfunction`](@ref) accepts three arguments: the Julia function (`mycompare`), the return type (`Cint`), and a tuple type of the input argument types, in this case to sort an array of `Cdouble` ([`Float64`](@ref)) elements. @@ -239,7 +239,7 @@ Julia code from a C header file.) ### Auto-conversion: -Julia automatically inserts calls to the [`Base.cconvert()`](@ref) function to convert each argument +Julia automatically inserts calls to the [`Base.cconvert`](@ref) function to convert each argument to the specified type. For example, the following call: ```julia @@ -254,12 +254,12 @@ ccall((:foo, "libfoo"), Void, (Int32, Float64), Base.unsafe_convert(Float64, Base.cconvert(Float64, y))) ``` -[`Base.cconvert()`](@ref) normally just calls [`convert()`](@ref), but can be defined to return an +[`Base.cconvert`](@ref) normally just calls [`convert`](@ref), but can be defined to return an arbitrary new object more appropriate for passing to C. This should be used to perform all allocations of memory that will be accessed by the C code. For example, this is used to convert an `Array` of objects (e.g. strings) to an array of pointers. -[`Base.unsafe_convert()`](@ref) handles conversion to `Ptr` types. It is considered unsafe because +[`Base.unsafe_convert`](@ref) handles conversion to `Ptr` types. It is considered unsafe because converting an object to a native pointer can hide the object from the garbage collector, causing it to be freed prematurely. @@ -323,9 +323,9 @@ same: (for example, to pass a `Float64` array to a function that operates on uninterpreted bytes), you can declare the argument as `Ptr{Void}`. - If an array of eltype `Ptr{T}` is passed as a `Ptr{Ptr{T}}` argument, [`Base.cconvert()`](@ref) + If an array of eltype `Ptr{T}` is passed as a `Ptr{Ptr{T}}` argument, [`Base.cconvert`](@ref) will attempt to first make a null-terminated copy of the array with each element replaced by its - [`Base.cconvert()`](@ref) version. This allows, for example, passing an `argv` pointer array of type + [`Base.cconvert`](@ref) version. This allows, for example, passing an `argv` pointer array of type `Vector{String}` to an argument of type `Ptr{Ptr{Cchar}}`. On all systems we currently support, basic C/C++ value types may be translated to Julia types @@ -555,7 +555,7 @@ allocated in Julia to be freed by an external library) is equally invalid. In Julia code wrapping calls to external C routines, ordinary (non-pointer) data should be declared to be of type `T` inside the [`ccall`](@ref), as they are passed by value. For C code accepting pointers, `Ref{T}` should generally be used for the types of input arguments, allowing the use -of pointers to memory managed by either Julia or C through the implicit call to [`Base.cconvert()`](@ref). +of pointers to memory managed by either Julia or C through the implicit call to [`Base.cconvert`](@ref). In contrast, pointers returned by the C function called should be declared to be of output type `Ptr{T}`, reflecting that the memory pointed to is managed by C only. Pointers contained in C structs should be represented as fields of type `Ptr{T}` within the corresponding Julia struct @@ -590,12 +590,12 @@ For translating a C argument list to Julia: * `Any` * argument value must be a valid Julia object - * currently unsupported by [`cfunction()`](@ref) + * currently unsupported by [`cfunction`](@ref) * `jl_value_t**` * `Ref{Any}` * argument value must be a valid Julia object (or `C_NULL`) - * currently unsupported by [`cfunction()`](@ref) + * currently unsupported by [`cfunction`](@ref) * `T*` * `Ref{T}`, where `T` is the Julia type corresponding to `T` @@ -603,7 +603,7 @@ For translating a C argument list to Julia: object * `(T*)(...)` (e.g. a pointer to a function) - * `Ptr{Void}` (you may need to use [`cfunction()`](@ref) explicitly to create this pointer) + * `Ptr{Void}` (you may need to use [`cfunction`](@ref) explicitly to create this pointer) * `...` (e.g. a vararg) * `T...`, where `T` is the Julia type @@ -654,7 +654,7 @@ For translating a C return type to Julia: * `Ptr{T}`, where `T` is the Julia type corresponding to `T` * `(T*)(...)` (e.g. a pointer to a function) - * `Ptr{Void}` (you may need to use [`cfunction()`](@ref) explicitly to create this pointer) + * `Ptr{Void}` (you may need to use [`cfunction`](@ref) explicitly to create this pointer) ### Passing Pointers for Modifying Inputs @@ -728,7 +728,7 @@ end The [GNU Scientific Library](https://www.gnu.org/software/gsl/) (here assumed to be accessible through `:libgsl`) defines an opaque pointer, `gsl_permutation *`, as the return type of the C -function `gsl_permutation_alloc()`. As user code never has to look inside the `gsl_permutation` +function `gsl_permutation_alloc`. As user code never has to look inside the `gsl_permutation` struct, the corresponding Julia wrapper simply needs a new type declaration, `gsl_permutation`, that has no internal fields and whose sole purpose is to be placed in the type parameter of a `Ptr` type. The return type of the [`ccall`](@ref) is declared as `Ptr{gsl_permutation}`, since @@ -757,7 +757,7 @@ end Here, the input `p` is declared to be of type `Ref{gsl_permutation}`, meaning that the memory that `p` points to may be managed by Julia or by C. A pointer to memory allocated by C should -be of type `Ptr{gsl_permutation}`, but it is convertable using [`Base.cconvert()`](@ref) and therefore +be of type `Ptr{gsl_permutation}`, but it is convertable using [`Base.cconvert`](@ref) and therefore can be used in the same (covariant) context of the input argument to a [`ccall`](@ref). A pointer to memory allocated by Julia must be of type `Ref{gsl_permutation}`, to ensure that the memory address pointed to is valid and that Julia's garbage collector manages the chunk of memory pointed @@ -809,7 +809,7 @@ the C function may end up throwing an invalid memory access exception. ## Garbage Collection Safety -When passing data to a [`ccall`](@ref), it is best to avoid using the [`pointer()`](@ref) function. +When passing data to a [`ccall`](@ref), it is best to avoid using the [`pointer`](@ref) function. Instead define a convert method and pass the variables directly to the [`ccall`](@ref). [`ccall`](@ref) automatically arranges that all of its arguments will be preserved from garbage collection until the call returns. If a C API will store a reference to memory allocated by Julia, after the [`ccall`](@ref) @@ -818,9 +818,9 @@ way to handle this is to make a global variable of type `Array{Ref,1}` to hold t the C library notifies you that it is finished with them. Whenever you have created a pointer to Julia data, you must ensure the original data exists until -you are done with using the pointer. Many methods in Julia such as [`unsafe_load()`](@ref) and -[`String()`](@ref) make copies of data instead of taking ownership of the buffer, so that it is -safe to free (or alter) the original data without affecting Julia. A notable exception is [`unsafe_wrap()`](@ref) +you are done with using the pointer. Many methods in Julia such as [`unsafe_load`](@ref) and +[`String`](@ref) make copies of data instead of taking ownership of the buffer, so that it is +safe to free (or alter) the original data without affecting Julia. A notable exception is [`unsafe_wrap`](@ref) which, for performance reasons, shares (or can be told to take ownership of) the underlying buffer. The garbage collector does not guarantee any order of finalization. That is, if `a` contained @@ -923,8 +923,8 @@ unlike the equivalent Julia functions exposed by `Core.Intrinsics`. ## Accessing Global Variables -Global variables exported by native libraries can be accessed by name using the [`cglobal()`](@ref) -function. The arguments to [`cglobal()`](@ref) are a symbol specification identical to that used +Global variables exported by native libraries can be accessed by name using the [`cglobal`](@ref) +function. The arguments to [`cglobal`](@ref) are a symbol specification identical to that used by [`ccall`](@ref), and a type describing the value stored in the variable: ```julia-repl @@ -933,7 +933,7 @@ Ptr{Int32} @0x00007f418d0816b8 ``` The result is a pointer giving the address of the value. The value can be manipulated through -this pointer using [`unsafe_load()`](@ref) and [`unsafe_store!()`](@ref). +this pointer using [`unsafe_load`](@ref) and [`unsafe_store!`](@ref). ## Accessing Data through a Pointer @@ -943,7 +943,7 @@ cause Julia to terminate abruptly. Given a `Ptr{T}`, the contents of type `T` can generally be copied from the referenced memory into a Julia object using `unsafe_load(ptr, [index])`. The index argument is optional (default is 1), and follows the Julia-convention of 1-based indexing. This function is intentionally similar -to the behavior of [`getindex()`](@ref) and [`setindex!()`](@ref) (e.g. `[]` access syntax). +to the behavior of [`getindex`](@ref) and [`setindex!`](@ref) (e.g. `[]` access syntax). The return value will be a new object initialized to contain a copy of the contents of the referenced memory. The referenced memory can safely be freed or released. diff --git a/doc/src/manual/complex-and-rational-numbers.md b/doc/src/manual/complex-and-rational-numbers.md index b76a128bb72ab..d656f37c376f4 100644 --- a/doc/src/manual/complex-and-rational-numbers.md +++ b/doc/src/manual/complex-and-rational-numbers.md @@ -111,9 +111,9 @@ julia> angle(1 + 2im) # phase angle in radians 1.1071487177940904 ``` -As usual, the absolute value ([`abs()`](@ref)) of a complex number is its distance from zero. -[`abs2()`](@ref) gives the square of the absolute value, and is of particular use for complex -numbers where it avoids taking a square root. [`angle()`](@ref) returns the phase angle in radians +As usual, the absolute value ([`abs`](@ref)) of a complex number is its distance from zero. +[`abs2`](@ref) gives the square of the absolute value, and is of particular use for complex +numbers where it avoids taking a square root. [`angle`](@ref) returns the phase angle in radians (also known as the *argument* or *arg* function). The full gamut of other [Elementary Functions](@ref) is also defined for complex numbers: @@ -135,7 +135,7 @@ julia> sinh(1 + 2im) ``` Note that mathematical functions typically return real values when applied to real numbers and -complex values when applied to complex numbers. For example, [`sqrt()`](@ref) behaves differently +complex values when applied to complex numbers. For example, [`sqrt`](@ref) behaves differently when applied to `-1` versus `-1 + 0im` even though `-1 == -1 + 0im`: ```jldoctest @@ -159,7 +159,7 @@ julia> a = 1; b = 2; a + b*im 1 + 2im ``` -However, this is *not* recommended; Use the [`complex()`](@ref) function instead to construct +However, this is *not* recommended; Use the [`complex`](@ref) function instead to construct a complex value directly from its real and imaginary parts: ```jldoctest @@ -209,7 +209,7 @@ julia> -4//-12 This normalized form for a ratio of integers is unique, so equality of rational values can be tested by checking for equality of the numerator and denominator. The standardized numerator and -denominator of a rational value can be extracted using the [`numerator()`](@ref) and [`denominator()`](@ref) +denominator of a rational value can be extracted using the [`numerator`](@ref) and [`denominator`](@ref) functions: ```jldoctest diff --git a/doc/src/manual/constructors.md b/doc/src/manual/constructors.md index 0d77cdcb56373..66f6283f4b999 100644 --- a/doc/src/manual/constructors.md +++ b/doc/src/manual/constructors.md @@ -354,7 +354,7 @@ following additional outer constructor method: julia> Point(x::Int64, y::Float64) = Point(convert(Float64,x),y); ``` -This method uses the [`convert()`](@ref) function to explicitly convert `x` to [`Float64`](@ref) +This method uses the [`convert`](@ref) function to explicitly convert `x` to [`Float64`](@ref) and then delegates construction to the general constructor for the case where both arguments are [`Float64`](@ref). With this method definition what was previously a [`MethodError`](@ref) now successfully creates a point of type `Point{Float64}`: @@ -513,7 +513,7 @@ table for the `Type` type. This means that you can declare more flexible constru for abstract types, by explicitly defining methods for the appropriate types. However, in some cases you could consider adding methods to `Base.convert` *instead* of defining -a constructor, because Julia falls back to calling [`convert()`](@ref) if no matching constructor +a constructor, because Julia falls back to calling [`convert`](@ref) if no matching constructor is found. For example, if no constructor `T(args...) = ...` exists `Base.convert(::Type{T}, args...) = ...` is called. diff --git a/doc/src/manual/control-flow.md b/doc/src/manual/control-flow.md index 4d751a836aceb..37f44ee167343 100644 --- a/doc/src/manual/control-flow.md +++ b/doc/src/manual/control-flow.md @@ -6,8 +6,8 @@ Julia provides a variety of control flow constructs: * [Conditional Evaluation](@ref man-conditional-evaluation): `if`-`elseif`-`else` and `?:` (ternary operator). * [Short-Circuit Evaluation](@ref): `&&`, `||` and chained comparisons. * [Repeated Evaluation: Loops](@ref man-loops): `while` and `for`. - * [Exception Handling](@ref): `try`-`catch`, [`error()`](@ref) and [`throw()`](@ref). - * [Tasks (aka Coroutines)](@ref man-tasks): [`yieldto()`](@ref). + * [Exception Handling](@ref): `try`-`catch`, [`error`](@ref) and [`throw`](@ref). + * [Tasks (aka Coroutines)](@ref man-tasks): [`yieldto`](@ref). The first five control flow mechanisms are standard to high-level programming languages. [`Task`](@ref)s are not so standard: they provide non-local control flow, making it possible to switch between @@ -565,7 +565,7 @@ below all interrupt the normal flow of control. | [`UndefVarError`](@ref) | | `UnicodeError` | -For example, the [`sqrt()`](@ref) function throws a [`DomainError`](@ref) if applied to a negative +For example, the [`sqrt`](@ref) function throws a [`DomainError`](@ref) if applied to a negative real value: ```jldoctest @@ -584,10 +584,10 @@ You may define your own exceptions in the following way: julia> struct MyCustomException <: Exception end ``` -### The [`throw()`](@ref) function +### The [`throw`](@ref) function -Exceptions can be created explicitly with [`throw()`](@ref). For example, a function defined only -for nonnegative numbers could be written to [`throw()`](@ref) a [`DomainError`](@ref) if the argument +Exceptions can be created explicitly with [`throw`](@ref). For example, a function defined only +for nonnegative numbers could be written to [`throw`](@ref) a [`DomainError`](@ref) if the argument is negative: ```jldoctest @@ -646,11 +646,11 @@ julia> Base.showerror(io::IO, e::MyUndefVarError) = print(io, e.var, " not defin ### Errors -The [`error()`](@ref) function is used to produce an [`ErrorException`](@ref) that interrupts +The [`error`](@ref) function is used to produce an [`ErrorException`](@ref) that interrupts the normal flow of control. Suppose we want to stop execution immediately if the square root of a negative number is taken. -To do this, we can define a fussy version of the [`sqrt()`](@ref) function that raises an error +To do this, we can define a fussy version of the [`sqrt`](@ref) function that raises an error if its argument is negative: ```jldoctest fussy_sqrt @@ -799,7 +799,7 @@ julia> try error() end # Returns nothing The power of the `try/catch` construct lies in the ability to unwind a deeply nested computation immediately to a much higher level in the stack of calling functions. There are situations where no error has occurred, but the ability to unwind the stack and pass a value to a higher level -is desirable. Julia provides the [`rethrow()`](@ref), [`backtrace()`](@ref) and [`catch_backtrace()`](@ref) +is desirable. Julia provides the [`rethrow`](@ref), [`backtrace`](@ref) and [`catch_backtrace`](@ref) functions for more advanced error handling. ### `finally` Clauses @@ -855,7 +855,7 @@ multiple tasks reading from and writing to it. Let's define a producer task, which produces values via the [`put!`](@ref) call. To consume values, we need to schedule the producer to run in a new task. A special [`Channel`](@ref) constructor which accepts a 1-arg function as an argument can be used to run a task bound to a channel. -We can then [`take!()`](@ref) values repeatedly from the channel object: +We can then [`take!`](@ref) values repeatedly from the channel object: ```jldoctest producer julia> function producer(c::Channel) @@ -888,7 +888,7 @@ julia> take!(chnl) ``` One way to think of this behavior is that `producer` was able to return multiple times. Between -calls to [`put!()`](@ref), the producer's execution is suspended and the consumer has control. +calls to [`put!`](@ref), the producer's execution is suspended and the consumer has control. The returned [`Channel`](@ref) can be used as an iterable object in a `for` loop, in which case the loop variable takes on all the produced values. The loop is terminated when the channel is closed. @@ -906,16 +906,16 @@ stop ``` Note that we did not have to explicitly close the channel in the producer. This is because -the act of binding a [`Channel`](@ref) to a [`Task()`](@ref) associates the open lifetime of +the act of binding a [`Channel`](@ref) to a [`Task`](@ref) associates the open lifetime of a channel with that of the bound task. The channel object is closed automatically when the task terminates. Multiple channels can be bound to a task, and vice-versa. -While the [`Task()`](@ref) constructor expects a 0-argument function, the [`Channel()`](@ref) +While the [`Task`](@ref) constructor expects a 0-argument function, the [`Channel`](@ref) method which creates a channel bound task expects a function that accepts a single argument of type [`Channel`](@ref). A common pattern is for the producer to be parameterized, in which case a partial function application is needed to create a 0 or 1 argument [anonymous function](@ref man-anonymous-functions). -For [`Task()`](@ref) objects this can be done either directly or by use of a convenience macro: +For [`Task`](@ref) objects this can be done either directly or by use of a convenience macro: ```julia function mytask(myarg) @@ -927,8 +927,8 @@ taskHdl = Task(() -> mytask(7)) taskHdl = @task mytask(7) ``` -To orchestrate more advanced work distribution patterns, [`bind()`](@ref) and [`schedule()`](@ref) -can be used in conjunction with [`Task()`](@ref) and [`Channel()`](@ref) +To orchestrate more advanced work distribution patterns, [`bind`](@ref) and [`schedule`](@ref) +can be used in conjunction with [`Task`](@ref) and [`Channel`](@ref) constructors to explicitly link a set of channels with a set of producer/consumer tasks. Note that currently Julia tasks are not scheduled to run on separate CPU cores. @@ -936,27 +936,27 @@ True kernel threads are discussed under the topic of [Parallel Computing](@ref). ### Core task operations -Let us explore the low level construct [`yieldto()`](@ref) to underestand how task switching works. +Let us explore the low level construct [`yieldto`](@ref) to underestand how task switching works. `yieldto(task,value)` suspends the current task, switches to the specified `task`, and causes -that task's last [`yieldto()`](@ref) call to return the specified `value`. Notice that [`yieldto()`](@ref) +that task's last [`yieldto`](@ref) call to return the specified `value`. Notice that [`yieldto`](@ref) is the only operation required to use task-style control flow; instead of calling and returning we are always just switching to a different task. This is why this feature is also called "symmetric coroutines"; each task is switched to and from using the same mechanism. -[`yieldto()`](@ref) is powerful, but most uses of tasks do not invoke it directly. Consider why +[`yieldto`](@ref) is powerful, but most uses of tasks do not invoke it directly. Consider why this might be. If you switch away from the current task, you will probably want to switch back to it at some point, but knowing when to switch back, and knowing which task has the responsibility -of switching back, can require considerable coordination. For example, [`put!()`](@ref) and [`take!()`](@ref) +of switching back, can require considerable coordination. For example, [`put!`](@ref) and [`take!`](@ref) are blocking operations, which, when used in the context of channels maintain state to remember -who the consumers are. Not needing to manually keep track of the consuming task is what makes [`put!()`](@ref) -easier to use than the low-level [`yieldto()`](@ref). +who the consumers are. Not needing to manually keep track of the consuming task is what makes [`put!`](@ref) +easier to use than the low-level [`yieldto`](@ref). -In addition to [`yieldto()`](@ref), a few other basic functions are needed to use tasks effectively. +In addition to [`yieldto`](@ref), a few other basic functions are needed to use tasks effectively. - * [`current_task()`](@ref) gets a reference to the currently-running task. - * [`istaskdone()`](@ref) queries whether a task has exited. - * [`istaskstarted()`](@ref) queries whether a task has run yet. - * [`task_local_storage()`](@ref) manipulates a key-value store specific to the current task. + * [`current_task`](@ref) gets a reference to the currently-running task. + * [`istaskdone`](@ref) queries whether a task has exited. + * [`istaskstarted`](@ref) queries whether a task has run yet. + * [`task_local_storage`](@ref) manipulates a key-value store specific to the current task. ### Tasks and events @@ -964,23 +964,23 @@ Most task switches occur as a result of waiting for events such as I/O requests, by a scheduler included in the standard library. The scheduler maintains a queue of runnable tasks, and executes an event loop that restarts tasks based on external events such as message arrival. -The basic function for waiting for an event is [`wait()`](@ref). Several objects implement [`wait()`](@ref); -for example, given a `Process` object, [`wait()`](@ref) will wait for it to exit. [`wait()`](@ref) -is often implicit; for example, a [`wait()`](@ref) can happen inside a call to [`read()`](@ref) +The basic function for waiting for an event is [`wait`](@ref). Several objects implement [`wait`](@ref); +for example, given a `Process` object, [`wait`](@ref) will wait for it to exit. [`wait`](@ref) +is often implicit; for example, a [`wait`](@ref) can happen inside a call to [`read`](@ref) to wait for data to be available. -In all of these cases, [`wait()`](@ref) ultimately operates on a [`Condition`](@ref) object, which -is in charge of queueing and restarting tasks. When a task calls [`wait()`](@ref) on a [`Condition`](@ref), +In all of these cases, [`wait`](@ref) ultimately operates on a [`Condition`](@ref) object, which +is in charge of queueing and restarting tasks. When a task calls [`wait`](@ref) on a [`Condition`](@ref), the task is marked as non-runnable, added to the condition's queue, and switches to the scheduler. The scheduler will then pick another task to run, or block waiting for external events. If all -goes well, eventually an event handler will call [`notify()`](@ref) on the condition, which causes +goes well, eventually an event handler will call [`notify`](@ref) on the condition, which causes tasks waiting for that condition to become runnable again. A task created explicitly by calling [`Task`](@ref) is initially not known to the scheduler. This -allows you to manage tasks manually using [`yieldto()`](@ref) if you wish. However, when such +allows you to manage tasks manually using [`yieldto`](@ref) if you wish. However, when such a task waits for an event, it still gets restarted automatically when the event happens, as you would expect. It is also possible to make the scheduler run a task whenever it can, without necessarily -waiting for any events. This is done by calling [`schedule()`](@ref), or using the [`@schedule`](@ref) +waiting for any events. This is done by calling [`schedule`](@ref), or using the [`@schedule`](@ref) or [`@async`](@ref) macros (see [Parallel Computing](@ref) for more details). ### Task states diff --git a/doc/src/manual/conversion-and-promotion.md b/doc/src/manual/conversion-and-promotion.md index 3ce8b87830ef8..59f8679972078 100644 --- a/doc/src/manual/conversion-and-promotion.md +++ b/doc/src/manual/conversion-and-promotion.md @@ -89,12 +89,12 @@ since type constructors fall back to convert methods. Some languages consider parsing strings as numbers or formatting numbers as strings to be conversions (many dynamic languages will even perform conversion for you automatically), however Julia does not: even though some strings can be parsed as numbers, most strings are not valid representations -of numbers, and only a very limited subset of them are. Therefore in Julia the dedicated `parse()` +of numbers, and only a very limited subset of them are. Therefore in Julia the dedicated `parse` function must be used to perform this operation, making it more explicit. ### Defining New Conversions -To define a new conversion, simply provide a new method for `convert()`. That's really all there +To define a new conversion, simply provide a new method for `convert`. That's really all there is to it. For example, the method to convert a real number to a boolean is this: ```julia diff --git a/doc/src/manual/dates.md b/doc/src/manual/dates.md index e9b34ac5d0e01..7c8ebda32bbfe 100644 --- a/doc/src/manual/dates.md +++ b/doc/src/manual/dates.md @@ -98,7 +98,7 @@ noting the transition `"yyyymm"` from one period character to the next. Support for text-form month parsing is also supported through the `u` and `U` characters, for abbreviated and full-length month names, respectively. By default, only English month names are supported, so `u` corresponds to "Jan", "Feb", "Mar", etc. And `U` corresponds to "January", "February", -"March", etc. Similar to other name=>value mapping functions [`dayname()`](@ref) and [`monthname()`](@ref), +"March", etc. Similar to other name=>value mapping functions [`dayname`](@ref) and [`monthname`](@ref), custom locales can be loaded by passing in the `locale=>Dict{String,Int}` mapping to the `MONTHTOVALUEABBR` and `MONTHTOVALUE` dicts for abbreviated and full-name month names, respectively. @@ -296,10 +296,10 @@ julia> Dates.dayofquarter(t) 31 ``` -The [`dayname()`](@ref) and [`monthname()`](@ref) methods can also take an optional `locale` keyword +The [`dayname`](@ref) and [`monthname`](@ref) methods can also take an optional `locale` keyword that can be used to return the name of the day or month of the year for other languages/locales. There are also versions of these functions returning the abbreviated names, namely -[`dayabbr()`](@ref) and [`monthabbr()`](@ref). +[`dayabbr`](@ref) and [`monthabbr`](@ref). First the mapping is loaded into the `LOCALES` variable: ```jldoctest tdate2 @@ -328,7 +328,7 @@ julia> Dates.monthabbr(t;locale="french") ``` Since the abbreviated versions of the days are not loaded, trying to use the -function `dayabbr()` will error. +function `dayabbr` will error. ```jldoctest tdate2 julia> Dates.dayabbr(t;locale="french") @@ -451,7 +451,7 @@ julia> Dates.lastdayofquarter(Date(2014,7,16)) # Adjusts to the last day of the 2014-09-30 ``` -The next two higher-order methods, [`tonext()`](@ref), and [`toprev()`](@ref), generalize working +The next two higher-order methods, [`tonext`](@ref), and [`toprev`](@ref), generalize working with temporal expressions by taking a `DateFunction` as first argument, along with a starting [`TimeType`](@ref). A `DateFunction` is just a function, usually anonymous, that takes a single [`TimeType`](@ref) as input and returns a [`Bool`](@ref), `true` indicating a satisfied @@ -481,7 +481,7 @@ julia> Dates.tonext(Date(2014,7,13)) do x 2014-11-27 ``` -The [`Base.filter()`](@ref) method can be used to obtain all valid dates/moments in a specified +The [`Base.filter`](@ref) method can be used to obtain all valid dates/moments in a specified range: ```jldoctest @@ -545,7 +545,7 @@ julia> div(y3,3) # mirrors integer division ## Rounding [`Date`](@ref) and [`DateTime`](@ref) values can be rounded to a specified resolution (e.g., 1 -month or 15 minutes) with [`floor()`](@ref), [`ceil()`](@ref), or [`round()`](@ref): +month or 15 minutes) with [`floor`](@ref), [`ceil`](@ref), or [`round`](@ref): ```jldoctest julia> floor(Date(1985, 8, 16), Dates.Month) @@ -558,8 +558,8 @@ julia> round(DateTime(2016, 8, 6, 20, 15), Dates.Day) 2016-08-07T00:00:00 ``` -Unlike the numeric [`round()`](@ref) method, which breaks ties toward the even number by default, -the [`TimeType`](@ref)[`round()`](@ref) method uses the `RoundNearestTiesUp` rounding mode. (It's +Unlike the numeric [`round`](@ref) method, which breaks ties toward the even number by default, +the [`TimeType`](@ref)[`round`](@ref) method uses the `RoundNearestTiesUp` rounding mode. (It's difficult to guess what breaking ties to nearest "even" [`TimeType`](@ref) would entail.) Further details on the available `RoundingMode` s can be found in the [API reference](@ref stdlib-dates). diff --git a/doc/src/manual/faq.md b/doc/src/manual/faq.md index 776f26ff04e86..5ccf9d250acf7 100644 --- a/doc/src/manual/faq.md +++ b/doc/src/manual/faq.md @@ -94,7 +94,7 @@ julia> x 3 ``` -Here we created a function `change_array!()`, that assigns `5` to the first element of the passed +Here we created a function `change_array!`, that assigns `5` to the first element of the passed array (bound to `x` at the call site, and bound to `A` within the function). Notice that, after the function call, `x` is still bound to the same array, but the content of that array changed: the variables `A` and `x` were distinct bindings refering to the same mutable `Array` object. @@ -242,11 +242,11 @@ Stacktrace: ``` This behavior is an inconvenient consequence of the requirement for type-stability. In the case -of [`sqrt()`](@ref), most users want `sqrt(2.0)` to give a real number, and would be unhappy if -it produced the complex number `1.4142135623730951 + 0.0im`. One could write the [`sqrt()`](@ref) +of [`sqrt`](@ref), most users want `sqrt(2.0)` to give a real number, and would be unhappy if +it produced the complex number `1.4142135623730951 + 0.0im`. One could write the [`sqrt`](@ref) function to switch to a complex-valued output only when passed a negative number (which is what -[`sqrt()`](@ref) does in some other languages), but then the result would not be [type-stable](@ref man-type-stability) -and the [`sqrt()`](@ref) function would have poor performance. +[`sqrt`](@ref) does in some other languages), but then the result would not be [type-stable](@ref man-type-stability) +and the [`sqrt`](@ref) function would have poor performance. In these and other cases, you can get the result you want by choosing an *input type* that conveys your willingness to accept an *output type* in which the result can be represented: diff --git a/doc/src/manual/functions.md b/doc/src/manual/functions.md index eaee1c912cf23..89d8387d945ac 100644 --- a/doc/src/manual/functions.md +++ b/doc/src/manual/functions.md @@ -143,7 +143,7 @@ julia> +(1,2,3) The infix form is exactly equivalent to the function application form -- in fact the former is parsed to produce the function call internally. This also means that you can assign and pass around -operators such as [`+()`](@ref) and [`*()`](@ref) just like you would with other function values: +operators such as [`+`](@ref) and [`*`](@ref) just like you would with other function values: ```jldoctest julia> f = +; @@ -160,14 +160,14 @@ A few special expressions correspond to calls to functions with non-obvious name | Expression | Calls | |:----------------- |:---------------------- | -| `[A B C ...]` | [`hcat()`](@ref) | -| `[A; B; C; ...]` | [`vcat()`](@ref) | -| `[A B; C D; ...]` | [`hvcat()`](@ref) | -| `A'` | [`adjoint()`](@ref) | -| `A.'` | [`transpose()`](@ref) | -| `1:n` | [`colon()`](@ref) | -| `A[i]` | [`getindex()`](@ref) | -| `A[i]=x` | [`setindex!()`](@ref) | +| `[A B C ...]` | [`hcat`](@ref) | +| `[A; B; C; ...]` | [`vcat`](@ref) | +| `[A B; C D; ...]` | [`hvcat`](@ref) | +| `A'` | [`adjoint`](@ref) | +| `A.'` | [`transpose`](@ref) | +| `1:n` | [`colon`](@ref) | +| `A[i]` | [`getindex`](@ref) | +| `A[i]=x` | [`setindex!`](@ref) | ## [Anonymous Functions](@id man-anonymous-functions) @@ -192,7 +192,7 @@ This creates a function taking one argument `x` and returning the value of the p name based on consecutive numbering. The primary use for anonymous functions is passing them to functions which take other functions -as arguments. A classic example is [`map()`](@ref), which applies a function to each value of +as arguments. A classic example is [`map`](@ref), which applies a function to each value of an array and returns a new array containing the resulting values: ```jldoctest @@ -204,7 +204,7 @@ julia> map(round, [1.2,3.5,1.7]) ``` This is fine if a named function effecting the transform already exists to pass as the first argument -to [`map()`](@ref). Often, however, a ready-to-use, named function does not exist. In these +to [`map`](@ref). Often, however, a ready-to-use, named function does not exist. In these situations, the anonymous function construct allows easy creation of a single-use function object without needing a name: @@ -219,7 +219,7 @@ julia> map(x -> x^2 + 2x - 1, [1,3,-1]) An anonymous function accepting multiple arguments can be written using the syntax `(x,y,z)->2x+y-z`. A zero-argument anonymous function is written as `()->3`. The idea of a function with no arguments may seem strange, but is useful for "delaying" a computation. In this usage, a block of code is -wrapped in a zero-argument function, which is later invoked by calling it as `f()`. +wrapped in a zero-argument function, which is later invoked by calling it as `f`. ## Multiple Return Values @@ -507,7 +507,7 @@ the `b` in `a=b` refers to a `b` in an outer scope, not the subsequent argument Passing functions as arguments to other functions is a powerful technique, but the syntax for it is not always convenient. Such calls are especially awkward to write when the function argument -requires multiple lines. As an example, consider calling [`map()`](@ref) on a function with several +requires multiple lines. As an example, consider calling [`map`](@ref) on a function with several cases: ```julia @@ -538,16 +538,16 @@ end ``` The `do x` syntax creates an anonymous function with argument `x` and passes it as the first argument -to [`map()`](@ref). Similarly, `do a,b` would create a two-argument anonymous function, and a +to [`map`](@ref). Similarly, `do a,b` would create a two-argument anonymous function, and a plain `do` would declare that what follows is an anonymous function of the form `() -> ...`. -How these arguments are initialized depends on the "outer" function; here, [`map()`](@ref) will +How these arguments are initialized depends on the "outer" function; here, [`map`](@ref) will sequentially set `x` to `A`, `B`, `C`, calling the anonymous function on each, just as would happen in the syntax `map(func, [A, B, C])`. This syntax makes it easier to use functions to effectively extend the language, since calls look -like normal code blocks. There are many possible uses quite different from [`map()`](@ref), such -as managing system state. For example, there is a version of [`open()`](@ref) that runs code ensuring +like normal code blocks. There are many possible uses quite different from [`map`](@ref), such +as managing system state. For example, there is a version of [`open`](@ref) that runs code ensuring that the opened file is eventually closed: ```julia @@ -569,8 +569,8 @@ function open(f::Function, args...) end ``` -Here, [`open()`](@ref) first opens the file for writing and then passes the resulting output stream -to the anonymous function you defined in the `do ... end` block. After your function exits, [`open()`](@ref) +Here, [`open`](@ref) first opens the file for writing and then passes the resulting output stream +to the anonymous function you defined in the `do ... end` block. After your function exits, [`open`](@ref) will make sure that the stream is properly closed, regardless of whether your function exited normally or threw an exception. (The `try/finally` construct will be described in [Control Flow](@ref).) diff --git a/doc/src/manual/integers-and-floating-point-numbers.md b/doc/src/manual/integers-and-floating-point-numbers.md index 1b819938cc625..59a3961ce837e 100644 --- a/doc/src/manual/integers-and-floating-point-numbers.md +++ b/doc/src/manual/integers-and-floating-point-numbers.md @@ -162,7 +162,7 @@ UInt8 ``` The minimum and maximum representable values of primitive numeric types such as integers are given -by the [`typemin()`](@ref) and [`typemax()`](@ref) functions: +by the [`typemin`](@ref) and [`typemax`](@ref) functions: ```jldoctest julia> (typemin(Int32), typemax(Int32)) @@ -183,7 +183,7 @@ julia> for T in [Int8,Int16,Int32,Int64,Int128,UInt8,UInt16,UInt32,UInt64,UInt12 UInt128: [0,340282366920938463463374607431768211455] ``` -The values returned by [`typemin()`](@ref) and [`typemax()`](@ref) are always of the given argument +The values returned by [`typemin`](@ref) and [`typemax`](@ref) are always of the given argument type. (The above expression uses several features we have yet to introduce, including [for loops](@ref man-loops), [Strings](@ref man-strings), and [Interpolation](@ref), but should be easy enough to understand for users with some existing programming experience.) @@ -212,7 +212,7 @@ is recommended instead. ### Division errors Integer division (the `div` function) has two exceptional cases: dividing by zero, and dividing -the lowest negative number ([`typemin()`](@ref)) by -1. Both of these cases throw a [`DivideError`](@ref). +the lowest negative number ([`typemin`](@ref)) by -1. Both of these cases throw a [`DivideError`](@ref). The remainder and modulus functions (`rem` and `mod`) throw a [`DivideError`](@ref) when their second argument is zero. @@ -371,7 +371,7 @@ julia> 0 * Inf NaN ``` -The [`typemin()`](@ref) and [`typemax()`](@ref) functions also apply to floating-point types: +The [`typemin`](@ref) and [`typemax`](@ref) functions also apply to floating-point types: ```jldoctest julia> (typemin(Float16),typemax(Float16)) @@ -390,7 +390,7 @@ Most real numbers cannot be represented exactly with floating-point numbers, and it is important to know the distance between two adjacent representable floating-point numbers, which is often known as [machine epsilon](https://en.wikipedia.org/wiki/Machine_epsilon). -Julia provides [`eps()`](@ref), which gives the distance between `1.0` and the next larger representable +Julia provides [`eps`](@ref), which gives the distance between `1.0` and the next larger representable floating-point value: ```jldoctest @@ -405,7 +405,7 @@ julia> eps() # same as eps(Float64) ``` These values are `2.0^-23` and `2.0^-52` as [`Float32`](@ref) and [`Float64`](@ref) values, -respectively. The [`eps()`](@ref) function can also take a floating-point value as an +respectively. The [`eps`](@ref) function can also take a floating-point value as an argument, and gives the absolute difference between that value and the next representable floating point value. That is, `eps(x)` yields a value of the same type as `x` such that `x + eps(x)` is the next representable floating-point value larger than `x`: @@ -430,7 +430,7 @@ numbers are densest in the real number line near zero, and grow sparser exponent farther away from zero. By definition, `eps(1.0)` is the same as `eps(Float64)` since `1.0` is a 64-bit floating-point value. -Julia also provides the [`nextfloat()`](@ref) and [`prevfloat()`](@ref) functions which return +Julia also provides the [`nextfloat`](@ref) and [`prevfloat`](@ref) functions which return the next largest or smallest representable floating-point number to the argument respectively: ```jldoctest @@ -478,8 +478,8 @@ The default mode used is always [`RoundNearest`](@ref), which rounds to the near value, with ties rounded towards the nearest value with an even least significant bit. !!! warning - Rounding is generally only correct for basic arithmetic functions ([`+()`](@ref), [`-()`](@ref), - [`*()`](@ref), [`/()`](@ref) and [`sqrt()`](@ref)) and type conversion operations. Many other + Rounding is generally only correct for basic arithmetic functions ([`+`](@ref), [`-`](@ref), + [`*`](@ref), [`/`](@ref) and [`sqrt`](@ref)) and type conversion operations. Many other functions assume the default [`RoundNearest`](@ref) mode is set, and can give erroneous results when operating under other rounding modes. @@ -511,7 +511,7 @@ the [GNU Multiple Precision Arithmetic Library (GMP)](https://gmplib.org) and th respectively. The [`BigInt`](@ref) and [`BigFloat`](@ref) types are available in Julia for arbitrary precision integer and floating point numbers respectively. -Constructors exist to create these types from primitive numerical types, and [`parse()`](@ref) +Constructors exist to create these types from primitive numerical types, and [`parse`](@ref) can be used to construct them from `AbstractString`s. Once created, they participate in arithmetic with all other numeric types thanks to Julia's [type promotion and conversion mechanism](@ref conversion-and-promotion): @@ -556,7 +556,7 @@ BigInt ``` The default precision (in number of bits of the significand) and rounding mode of [`BigFloat`](@ref) -operations can be changed globally by calling [`setprecision()`](@ref) and [`setrounding()`](@ref), +operations can be changed globally by calling [`setprecision`](@ref) and [`setrounding`](@ref), and all further calculations will take these changes in account. Alternatively, the precision or the rounding can be changed only within the execution of a particular block of code by using the same functions with a `do` block: diff --git a/doc/src/manual/interfaces.md b/doc/src/manual/interfaces.md index 3b679db3d8aa8..f321efb204aac 100644 --- a/doc/src/manual/interfaces.md +++ b/doc/src/manual/interfaces.md @@ -31,7 +31,7 @@ to generically build upon those behaviors. | `HasEltype()` | `eltype(IterType)` | | `EltypeUnknown()` | (*none*) | -Sequential iteration is implemented by the methods [`start()`](@ref), [`done()`](@ref), and [`next()`](@ref). Instead +Sequential iteration is implemented by the methods [`start`](@ref), [`done`](@ref), and [`next`](@ref). Instead of mutating objects as they are iterated over, Julia provides these three methods to keep track of the iteration state externally from the object. The `start(iter)` method returns the initial state for the iterable object `iter`. That state gets passed along to `done(iter, state)`, which @@ -92,7 +92,7 @@ julia> for i in Squares(7) 49 ``` -We can use many of the builtin methods that work with iterables, like [`in()`](@ref), [`mean()`](@ref) and [`std()`](@ref): +We can use many of the builtin methods that work with iterables, like [`in`](@ref), [`mean`](@ref) and [`std`](@ref): ```jldoctest squaretype julia> 25 in Squares(10) @@ -107,11 +107,11 @@ julia> std(Squares(100)) There are a few more methods we can extend to give Julia more information about this iterable collection. We know that the elements in a `Squares` sequence will always be `Int`. By extending -the [`eltype()`](@ref) method, we can give that information to Julia and help it make more specialized +the [`eltype`](@ref) method, we can give that information to Julia and help it make more specialized code in the more complicated methods. We also know the number of elements in our sequence, so -we can extend [`length()`](@ref), too. +we can extend [`length`](@ref), too. -Now, when we ask Julia to [`collect()`](@ref) all the elements into an array it can preallocate a `Vector{Int}` +Now, when we ask Julia to [`collect`](@ref) all the elements into an array it can preallocate a `Vector{Int}` of the right size instead of blindly [`push!`](@ref)ing each element into a `Vector{Any}`: ```jldoctest squaretype @@ -146,7 +146,7 @@ be used in their specific case. For the `Squares` iterable above, we can easily compute the `i`th element of the sequence by squaring it. We can expose this as an indexing expression `S[i]`. To opt into this behavior, `Squares` -simply needs to define [`getindex()`](@ref): +simply needs to define [`getindex`](@ref): ```jldoctest squaretype julia> function Base.getindex(S::Squares, i::Int) @@ -158,7 +158,7 @@ julia> Squares(100)[23] 529 ``` -Additionally, to support the syntax `S[end]`, we must define [`endof()`](@ref) to specify the last valid +Additionally, to support the syntax `S[end]`, we must define [`endof`](@ref) to specify the last valid index: ```jldoctest squaretype @@ -168,7 +168,7 @@ julia> Squares(23)[end] 529 ``` -Note, though, that the above *only* defines [`getindex()`](@ref) with one integer index. Indexing with +Note, though, that the above *only* defines [`getindex`](@ref) with one integer index. Indexing with anything other than an `Int` will throw a [`MethodError`](@ref) saying that there was no matching method. In order to support indexing with ranges or vectors of `Int`s, separate methods must be written: @@ -191,27 +191,27 @@ ourselves, we can officially define it as a subtype of an [`AbstractArray`](@ref ## [Abstract Arrays](@id man-interface-array) -| Methods to implement |   | Brief description | -|:----------------------------------------------- |:---------------------------------------- |:------------------------------------------------------------------------------------- | -| `size(A)` |   | Returns a tuple containing the dimensions of `A` | -| `getindex(A, i::Int)` |   | (if `IndexLinear`) Linear scalar indexing | -| `getindex(A, I::Vararg{Int, N})` |   | (if `IndexCartesian`, where `N = ndims(A)`) N-dimensional scalar indexing | -| `setindex!(A, v, i::Int)` |   | (if `IndexLinear`) Scalar indexed assignment | -| `setindex!(A, v, I::Vararg{Int, N})` |   | (if `IndexCartesian`, where `N = ndims(A)`) N-dimensional scalar indexed assignment | -| **Optional methods** | **Default definition** | **Brief description** | -| `IndexStyle(::Type)` | `IndexCartesian()` | Returns either `IndexLinear()` or `IndexCartesian()`. See the description below. | -| `getindex(A, I...)` | defined in terms of scalar `getindex()` | [Multidimensional and nonscalar indexing](@ref man-array-indexing) | -| `setindex!(A, I...)` | defined in terms of scalar `setindex!()` | [Multidimensional and nonscalar indexed assignment](@ref man-array-indexing) | -| `start()`/`next()`/`done()` | defined in terms of scalar `getindex()` | Iteration | -| `length(A)` | `prod(size(A))` | Number of elements | -| `similar(A)` | `similar(A, eltype(A), size(A))` | Return a mutable array with the same shape and element type | -| `similar(A, ::Type{S})` | `similar(A, S, size(A))` | Return a mutable array with the same shape and the specified element type | -| `similar(A, dims::NTuple{Int})` | `similar(A, eltype(A), dims)` | Return a mutable array with the same element type and size *dims* | -| `similar(A, ::Type{S}, dims::NTuple{Int})` | `Array{S}(dims)` | Return a mutable array with the specified element type and size | -| **Non-traditional indices** | **Default definition** | **Brief description** | -| `indices(A)` | `map(OneTo, size(A))` | Return the `AbstractUnitRange` of valid indices | -| `Base.similar(A, ::Type{S}, inds::NTuple{Ind})` | `similar(A, S, Base.to_shape(inds))` | Return a mutable array with the specified indices `inds` (see below) | -| `Base.similar(T::Union{Type,Function}, inds)` | `T(Base.to_shape(inds))` | Return an array similar to `T` with the specified indices `inds` (see below) | +| Methods to implement |   | Brief description | +|:----------------------------------------------- |:-------------------------------------- |:------------------------------------------------------------------------------------- | +| `size(A)` |   | Returns a tuple containing the dimensions of `A` | +| `getindex(A, i::Int)` |   | (if `IndexLinear`) Linear scalar indexing | +| `getindex(A, I::Vararg{Int, N})` |   | (if `IndexCartesian`, where `N = ndims(A)`) N-dimensional scalar indexing | +| `setindex!(A, v, i::Int)` |   | (if `IndexLinear`) Scalar indexed assignment | +| `setindex!(A, v, I::Vararg{Int, N})` |   | (if `IndexCartesian`, where `N = ndims(A)`) N-dimensional scalar indexed assignment | +| **Optional methods** | **Default definition** | **Brief description** | +| `IndexStyle(::Type)` | `IndexCartesian()` | Returns either `IndexLinear()` or `IndexCartesian()`. See the description below. | +| `getindex(A, I...)` | defined in terms of scalar `getindex` | [Multidimensional and nonscalar indexing](@ref man-array-indexing) | +| `setindex!(A, I...)` | defined in terms of scalar `setindex!` | [Multidimensional and nonscalar indexed assignment](@ref man-array-indexing) | +| `start`/`next`/`done` | defined in terms of scalar `getindex` | Iteration | +| `length(A)` | `prod(size(A))` | Number of elements | +| `similar(A)` | `similar(A, eltype(A), size(A))` | Return a mutable array with the same shape and element type | +| `similar(A, ::Type{S})` | `similar(A, S, size(A))` | Return a mutable array with the same shape and the specified element type | +| `similar(A, dims::NTuple{Int})` | `similar(A, eltype(A), dims)` | Return a mutable array with the same element type and size *dims* | +| `similar(A, ::Type{S}, dims::NTuple{Int})` | `Array{S}(dims)` | Return a mutable array with the specified element type and size | +| **Non-traditional indices** | **Default definition** | **Brief description** | +| `indices(A)` | `map(OneTo, size(A))` | Return the `AbstractUnitRange` of valid indices | +| `Base.similar(A, ::Type{S}, inds::NTuple{Ind})` | `similar(A, S, Base.to_shape(inds))` | Return a mutable array with the specified indices `inds` (see below) | +| `Base.similar(T::Union{Type,Function}, inds)` | `T(Base.to_shape(inds))` | Return an array similar to `T` with the specified indices `inds` (see below) | If a type is defined as a subtype of `AbstractArray`, it inherits a very large set of rich behaviors including iteration and multidimensional indexing built on top of single-element access. See @@ -233,7 +233,7 @@ efficiently converts the indices into one linear index and then calls the above arrays, on the other hand, require methods to be defined for each supported dimensionality with `ndims(A)` `Int` indices. For example, the built-in [`SparseMatrixCSC`](@ref) type only supports two dimensions, so it just defines -`getindex(A::SparseMatrixCSC, i::Int, j::Int)`. The same holds for `setindex!()`. +`getindex(A::SparseMatrixCSC, i::Int, j::Int)`. The same holds for `setindex!`. Returning to the sequence of squares from above, we could instead define it as a subtype of an `AbstractArray{Int, 1}`: @@ -251,7 +251,7 @@ julia> Base.getindex(S::SquaresVector, i::Int) = i*i ``` Note that it's very important to specify the two parameters of the `AbstractArray`; the first -defines the [`eltype()`](@ref), and the second defines the [`ndims()`](@ref). That supertype and those three +defines the [`eltype`](@ref), and the second defines the [`ndims`](@ref). That supertype and those three methods are all it takes for `SquaresVector` to be an iterable, indexable, and completely functional array: @@ -302,8 +302,8 @@ julia> Base.getindex(A::SparseArray{T,N}, I::Vararg{Int,N}) where {T,N} = get(A. julia> Base.setindex!(A::SparseArray{T,N}, v, I::Vararg{Int,N}) where {T,N} = (A.data[I] = v) ``` -Notice that this is an `IndexCartesian` array, so we must manually define [`getindex()`](@ref) and [`setindex!()`](@ref) -at the dimensionality of the array. Unlike the `SquaresVector`, we are able to define [`setindex!()`](@ref), +Notice that this is an `IndexCartesian` array, so we must manually define [`getindex`](@ref) and [`setindex!`](@ref) +at the dimensionality of the array. Unlike the `SquaresVector`, we are able to define [`setindex!`](@ref), and so we can mutate the array: ```jldoctest squarevectype @@ -327,7 +327,7 @@ julia> A[:] = 1:length(A); A ``` The result of indexing an `AbstractArray` can itself be an array (for instance when indexing by -a `Range`). The `AbstractArray` fallback methods use [`similar()`](@ref) to allocate an `Array` of the +a `Range`). The `AbstractArray` fallback methods use [`similar`](@ref) to allocate an `Array` of the appropriate size and element type, which is filled in using the basic indexing method described above. However, when implementing an array wrapper you often want the result to be wrapped as well: @@ -342,8 +342,8 @@ julia> A[1:2,:] In this example it is accomplished by defining `Base.similar{T}(A::SparseArray, ::Type{T}, dims::Dims)` to create the appropriate wrapped array. (Note that while `similar` supports 1- and 2-argument forms, in most case you only need to specialize the 3-argument form.) For this to work it's important -that `SparseArray` is mutable (supports `setindex!`). Defining `similar()`, `getindex()` and -`setindex!()` for `SparseArray` also makes it possible to [`copy()`](@ref) the array: +that `SparseArray` is mutable (supports `setindex!`). Defining `similar`, `getindex` and +`setindex!` for `SparseArray` also makes it possible to [`copy`](@ref) the array: ```jldoctest squarevectype julia> copy(A) diff --git a/doc/src/manual/linear-algebra.md b/doc/src/manual/linear-algebra.md index 7c86c389c0cb4..8d895eac02873 100644 --- a/doc/src/manual/linear-algebra.md +++ b/doc/src/manual/linear-algebra.md @@ -175,17 +175,17 @@ as well as whether hooks to various optimized methods for them in LAPACK are ava ### Elementary operations -| Matrix type | `+` | `-` | `*` | `\` | Other functions with optimized methods | -|:------------------------- |:--- |:--- |:--- |:--- |:------------------------------------------------------------------- | -| [`Symmetric`](@ref) | | | | MV | [`inv()`](@ref), [`sqrt()`](@ref), [`exp()`](@ref) | -| [`Hermitian`](@ref) | | | | MV | [`inv()`](@ref), [`sqrt()`](@ref), [`exp()`](@ref) | -| [`UpperTriangular`](@ref) | | | MV | MV | [`inv()`](@ref), [`det()`](@ref) | -| [`LowerTriangular`](@ref) | | | MV | MV | [`inv()`](@ref), [`det()`](@ref) | -| [`SymTridiagonal`](@ref) | M | M | MS | MV | [`eigmax()`](@ref), [`eigmin()`](@ref) | -| [`Tridiagonal`](@ref) | M | M | MS | MV | | -| [`Bidiagonal`](@ref) | M | M | MS | MV | | -| [`Diagonal`](@ref) | M | M | MV | MV | [`inv()`](@ref), [`det()`](@ref), [`logdet()`](@ref), [`/()`](@ref) | -| [`UniformScaling`](@ref) | M | M | MVS | MVS | [`/()`](@ref) | +| Matrix type | `+` | `-` | `*` | `\` | Other functions with optimized methods | +|:------------------------- |:--- |:--- |:--- |:--- |:----------------------------------------------------------- | +| [`Symmetric`](@ref) | | | | MV | [`inv`](@ref), [`sqrt`](@ref), [`exp`](@ref) | +| [`Hermitian`](@ref) | | | | MV | [`inv`](@ref), [`sqrt`](@ref), [`exp`](@ref) | +| [`UpperTriangular`](@ref) | | | MV | MV | [`inv`](@ref), [`det`](@ref) | +| [`LowerTriangular`](@ref) | | | MV | MV | [`inv`](@ref), [`det`](@ref) | +| [`SymTridiagonal`](@ref) | M | M | MS | MV | [`eigmax`](@ref), [`eigmin`](@ref) | +| [`Tridiagonal`](@ref) | M | M | MS | MV | | +| [`Bidiagonal`](@ref) | M | M | MS | MV | | +| [`Diagonal`](@ref) | M | M | MV | MV | [`inv`](@ref), [`det`](@ref), [`logdet`](@ref), [`/`](@ref) | +| [`UniformScaling`](@ref) | M | M | MVS | MVS | [`/`](@ref) | Legend: @@ -197,16 +197,16 @@ Legend: ### Matrix factorizations -| Matrix type | LAPACK | [`eig()`](@ref) | [`eigvals()`](@ref) | [`eigvecs()`](@ref) | [`svd()`](@ref) | [`svdvals()`](@ref) | -|:------------------------- |:------ |:--------------- |:------------------- |:------------------- |:--------------- |:------------------- | -| [`Symmetric`](@ref) | SY |   | ARI |   |   |   | -| [`Hermitian`](@ref) | HE |   | ARI |   |   |   | -| [`UpperTriangular`](@ref) | TR | A | A | A |   |   | -| [`LowerTriangular`](@ref) | TR | A | A | A |   |   | -| [`SymTridiagonal`](@ref) | ST | A | ARI | AV |   |   | -| [`Tridiagonal`](@ref) | GT |   |   |   |   |   | -| [`Bidiagonal`](@ref) | BD |   |   |   | A | A | -| [`Diagonal`](@ref) | DI |   | A |   |   |   | +| Matrix type | LAPACK | [`eig`](@ref) | [`eigvals`](@ref) | [`eigvecs`](@ref) | [`svd`](@ref) | [`svdvals`](@ref) | +|:------------------------- |:------ |:------------- |:----------------- |:----------------- |:------------- |:----------------- | +| [`Symmetric`](@ref) | SY | | ARI | | | | +| [`Hermitian`](@ref) | HE | | ARI | | | | +| [`UpperTriangular`](@ref) | TR | A | A | A | | | +| [`LowerTriangular`](@ref) | TR | A | A | A | | | +| [`SymTridiagonal`](@ref) | ST | A | ARI | AV | | | +| [`Tridiagonal`](@ref) | GT | | | | | | +| [`Bidiagonal`](@ref) | BD | | | | A | A | +| [`Diagonal`](@ref) | DI | | A | | | | Legend: diff --git a/doc/src/manual/mathematical-operations.md b/doc/src/manual/mathematical-operations.md index 0311108131697..1f4fc3a3dddd2 100644 --- a/doc/src/manual/mathematical-operations.md +++ b/doc/src/manual/mathematical-operations.md @@ -266,7 +266,7 @@ situations like hash key comparisons: | [`isinf(x)`](@ref) | `x` is infinite | | [`isnan(x)`](@ref) | `x` is not a number | -[`isequal()`](@ref) considers `NaN`s equal to each other: +[`isequal`](@ref) considers `NaN`s equal to each other: ```jldoctest julia> isequal(NaN, NaN) @@ -279,7 +279,7 @@ julia> isequal(NaN, NaN32) true ``` -`isequal()` can also be used to distinguish signed zeros: +`isequal` can also be used to distinguish signed zeros: ```jldoctest julia> -0.0 == 0.0 @@ -292,9 +292,9 @@ false Mixed-type comparisons between signed integers, unsigned integers, and floats can be tricky. A great deal of care has been taken to ensure that Julia does them correctly. -For other types, `isequal()` defaults to calling [`==()`](@ref), so if you want to define -equality for your own types then you only need to add a [`==()`](@ref) method. If you define -your own equality function, you should probably define a corresponding [`hash()`](@ref) method +For other types, `isequal` defaults to calling [`==`](@ref), so if you want to define +equality for your own types then you only need to add a [`==`](@ref) method. If you define +your own equality function, you should probably define a corresponding [`hash`](@ref) method to ensure that `isequal(x,y)` implies `hash(x) == hash(y)`. ### Chaining comparisons @@ -462,7 +462,7 @@ See [Conversion and Promotion](@ref conversion-and-promotion) for how to define | [`cld(x,y)`](@ref) | ceiling division; quotient rounded towards `+Inf` | | [`rem(x,y)`](@ref) | remainder; satisfies `x == div(x,y)*y + rem(x,y)`; sign matches `x` | | [`mod(x,y)`](@ref) | modulus; satisfies `x == fld(x,y)*y + mod(x,y)`; sign matches `y` | -| [`mod1(x,y)`](@ref) | `mod()` with offset 1; returns `r∈(0,y]` for `y>0` or `r∈[y,0)` for `y<0`, where `mod(r, y) == mod(x, y)` | +| [`mod1(x,y)`](@ref) | `mod` with offset 1; returns `r∈(0,y]` for `y>0` or `r∈[y,0)` for `y<0`, where `mod(r, y) == mod(x, y)` | | [`mod2pi(x)`](@ref) | modulus with respect to 2pi; `0 <= mod2pi(x)   < 2pi` | | [`divrem(x,y)`](@ref) | returns `(div(x,y),rem(x,y))` | | [`fldmod(x,y)`](@ref) | returns `(fld(x,y),mod(x,y))` | @@ -498,7 +498,7 @@ See [Conversion and Promotion](@ref conversion-and-promotion) for how to define | [`exponent(x)`](@ref) | binary exponent of `x` | | [`significand(x)`](@ref) | binary significand (a.k.a. mantissa) of a floating-point number `x` | -For an overview of why functions like [`hypot()`](@ref), [`expm1()`](@ref), and [`log1p()`](@ref) +For an overview of why functions like [`hypot`](@ref), [`expm1`](@ref), and [`log1p`](@ref) are necessary and useful, see John D. Cook's excellent pair of blog posts on the subject: [expm1, log1p, erfc](https://www.johndcook.com/blog/2010/06/07/math-library-functions-that-seem-unnecessary/), and [hypot](https://www.johndcook.com/blog/2010/06/02/whats-so-hard-about-finding-a-hypotenuse/). diff --git a/doc/src/manual/metaprogramming.md b/doc/src/manual/metaprogramming.md index 8bb806c546cd7..9f38f38f8ee29 100644 --- a/doc/src/manual/metaprogramming.md +++ b/doc/src/manual/metaprogramming.md @@ -70,7 +70,7 @@ true **The key point here is that Julia code is internally represented as a data structure that is accessible from the language itself.** -The [`dump()`](@ref) function provides indented and annotated display of `Expr` objects: +The [`dump`](@ref) function provides indented and annotated display of `Expr` objects: ```jldoctest prog julia> dump(ex2) @@ -157,10 +157,10 @@ julia> typeof(ex) Expr ``` -(to view the structure of this expression, try `ex.head` and `ex.args`, or use [`dump()`](@ref) +(to view the structure of this expression, try `ex.head` and `ex.args`, or use [`dump`](@ref) as above or [`Meta.@dump`](@ref)) -Note that equivalent expressions may be constructed using [`parse()`](@ref) or the direct `Expr` +Note that equivalent expressions may be constructed using [`parse`](@ref) or the direct `Expr` form: ```jldoctest @@ -243,10 +243,10 @@ The use of `$` for expression interpolation is intentionally reminiscent of [str and [command interpolation](@ref command-interpolation). Expression interpolation allows convenient, readable programmatic construction of complex Julia expressions. -### [`eval()`](@ref) and effects +### [`eval`](@ref) and effects Given an expression object, one can cause Julia to evaluate (execute) it at global scope using -[`eval()`](@ref): +[`eval`](@ref): ```jldoctest interp1 julia> :(1 + 2) @@ -268,8 +268,8 @@ julia> eval(ex) 3 ``` -Every [module](@ref modules) has its own [`eval()`](@ref) function that evaluates expressions in its global -scope. Expressions passed to [`eval()`](@ref) are not limited to returning values -- they can +Every [module](@ref modules) has its own [`eval`](@ref) function that evaluates expressions in its global +scope. Expressions passed to [`eval`](@ref) are not limited to returning values -- they can also have side-effects that alter the state of the enclosing module's environment: ```jldoctest @@ -290,7 +290,7 @@ Here, the evaluation of an expression object causes a value to be assigned to th `x`. Since expressions are just `Expr` objects which can be constructed programmatically and then evaluated, -it is possible to dynamically generate arbitrary code which can then be run using [`eval()`](@ref). +it is possible to dynamically generate arbitrary code which can then be run using [`eval`](@ref). Here is a simple example: ```julia-repl @@ -320,7 +320,7 @@ value 1 and the variable `b`. Note the important distinction between the way `a` As hinted above, one extremely useful feature of Julia is the capability to generate and manipulate Julia code within Julia itself. We have already seen one example of a function returning `Expr` -objects: the [`parse()`](@ref) function, which takes a string of Julia code and returns the corresponding +objects: the [`parse`](@ref) function, which takes a string of Julia code and returns the corresponding `Expr`. A function can also take one or more `Expr` objects as arguments, and return another `Expr`. Here is a simple, motivating example: @@ -363,7 +363,7 @@ julia> eval(ex) Macros provide a method to include generated code in the final body of a program. A macro maps a tuple of arguments to a returned *expression*, and the resulting expression is compiled directly -rather than requiring a runtime [`eval()`](@ref) call. Macro arguments may include expressions, +rather than requiring a runtime [`eval`](@ref) call. Macro arguments may include expressions, literal values, and symbols. ### Basics @@ -410,7 +410,7 @@ julia> @sayhello("human") Hello, human ``` -We can view the quoted return expression using the function [`macroexpand()`](@ref) (**important note:** +We can view the quoted return expression using the function [`macroexpand`](@ref) (**important note:** this is an extremely useful tool for debugging macros): ```julia-repl sayhello2 @@ -433,7 +433,7 @@ julia> @macroexpand @sayhello "human" ### Hold up: why macros? -We have already seen a function `f(::Expr...) -> Expr` in a previous section. In fact, [`macroexpand()`](@ref) +We have already seen a function `f(::Expr...) -> Expr` in a previous section. In fact, [`macroexpand`](@ref) is also such a function. So, why do macros exist? Macros are necessary because they execute when code is parsed, therefore, macros allow the programmer @@ -451,7 +451,7 @@ julia> ex = macroexpand(Main, :(@twostep :(1, 2, 3)) ); I execute at parse time. The argument is: $(Expr(:quote, :((1, 2, 3)))) ``` -The first call to [`println()`](@ref) is executed when [`macroexpand()`](@ref) is called. The +The first call to [`println`](@ref) is executed when [`macroexpand`](@ref) is called. The resulting expression contains *only* the second `println`: ```julia-repl whymacros @@ -484,7 +484,7 @@ above; it passes the tuple `(expr1, expr2, ...)` as one argument to the macro: ``` It is important to emphasize that macros receive their arguments as expressions, literals, or -symbols. One way to explore macro arguments is to call the [`show()`](@ref) function within the +symbols. One way to explore macro arguments is to call the [`show`](@ref) function within the macro body: ```jldoctest @@ -610,7 +610,7 @@ There is yet another case that the actual `@assert` macro handles: what if, in a "a should equal b," we wanted to print their values? One might naively try to use string interpolation in the custom message, e.g., `@assert a==b "a ($a) should equal b ($b)!"`, but this won't work as expected with the above macro. Can you see why? Recall from [string interpolation](@ref string-interpolation) that -an interpolated string is rewritten to a call to [`string()`](@ref). Compare: +an interpolated string is rewritten to a call to [`string`](@ref). Compare: ```jldoctest julia> typeof(:("a should equal b")) @@ -633,7 +633,7 @@ Expr So now instead of getting a plain string in `msg_body`, the macro is receiving a full expression that will need to be evaluated in order to display as expected. This can be spliced directly into -the returned expression as an argument to the [`string()`](@ref) call; see [`error.jl`](https://github.com/JuliaLang/julia/blob/master/base/error.jl) +the returned expression as an argument to the [`string`](@ref) call; see [`error.jl`](https://github.com/JuliaLang/julia/blob/master/base/error.jl) for the complete implementation. The `@assert` macro makes great use of splicing into quoted expressions to simplify the manipulation @@ -670,7 +670,7 @@ end ``` Here, we want `t0`, `t1`, and `val` to be private temporary variables, and we want `time` to refer -to the [`time()`](@ref) function in the standard library, not to any `time` variable the user +to the [`time`](@ref) function in the standard library, not to any `time` variable the user might have (the same applies to `println`). Imagine the problems that could occur if the user expression `ex` also contained assignments to a variable called `t0`, or defined its own `time` variable. We might get errors, or mysteriously incorrect behavior. @@ -678,7 +678,7 @@ variable. We might get errors, or mysteriously incorrect behavior. Julia's macro expander solves these problems in the following way. First, variables within a macro result are classified as either local or global. A variable is considered local if it is assigned to (and not declared global), declared local, or used as a function argument name. Otherwise, -it is considered global. Local variables are then renamed to be unique (using the [`gensym()`](@ref) +it is considered global. Local variables are then renamed to be unique (using the [`gensym`](@ref) function, which generates new symbols), and global variables are resolved within the macro definition environment. Therefore both of the above concerns are handled; the macro's locals will not conflict with any user variables, and `time` and `println` will refer to the standard library definitions. @@ -697,7 +697,7 @@ end Here the user expression `ex` is a call to `time`, but not the same `time` function that the macro uses. It clearly refers to `MyModule.time`. Therefore we must arrange for the code in `ex` to -be resolved in the macro call environment. This is done by "escaping" the expression with [`esc()`](@ref): +be resolved in the macro call environment. This is done by "escaping" the expression with [`esc`](@ref): ```julia macro time(ex) @@ -763,7 +763,7 @@ while we want `@time` to be usable with minimum impact on the wrapped code. When a significant amount of repetitive boilerplate code is required, it is common to generate it programmatically to avoid redundancy. In most languages, this requires an extra build step, and a separate program to generate the repetitive code. In Julia, expression interpolation and -[`eval()`](@ref) allow such code generation to take place in the normal course of program execution. +[`eval`](@ref) allow such code generation to take place in the normal course of program execution. For example, the following code defines a series of operators on three arguments in terms of their 2-argument forms: @@ -984,9 +984,9 @@ performance optimization, so it is invalid to depend too closely on this behavio The number of times a generated function is generated *might* be only once, but it *might* also be more often, or appear to not happen at all. As a consequence, you should *never* write a generated function with side effects - when, and how often, the side effects occur is undefined. (This is -true for macros too - and just like for macros, the use of [`eval()`](@ref) in a generated function +true for macros too - and just like for macros, the use of [`eval`](@ref) in a generated function is a sign that you're doing something the wrong way.) However, unlike macros, the runtime system -cannot correctly handle a call to [`eval()`](@ref), so it is disallowed. +cannot correctly handle a call to [`eval`](@ref), so it is disallowed. It is also important to see how `@generated` functions interact with method redefinition. Following the principle that a correct `@generated` function must not observe any @@ -1119,7 +1119,7 @@ to build some more advanced (and valid) functionality... ### An advanced example -Julia's base library has a [`sub2ind()`](@ref) function to calculate a linear index into an n-dimensional +Julia's base library has a [`sub2ind`](@ref) function to calculate a linear index into an n-dimensional array, based on a set of n multilinear indices - in other words, to calculate the index `i` that can be used to index into an array `A` using `A[i]`, instead of `A[x,y,z,...]`. One possible implementation is the following: diff --git a/doc/src/manual/methods.md b/doc/src/manual/methods.md index 862297a52d20b..3b1524c84c5fd 100644 --- a/doc/src/manual/methods.md +++ b/doc/src/manual/methods.md @@ -163,7 +163,7 @@ f (generic function with 2 methods) ``` This output tells us that `f` is a function object with two methods. To find out what the signatures -of those methods are, use the [`methods()`](@ref) function: +of those methods are, use the [`methods`](@ref) function: ```julia-repl julia> methods(f) @@ -453,7 +453,7 @@ This monotonically increasing value tracks each method definition operation. This allows describing "the set of method definitions visible to a given runtime environment" as a single number, or "world age". It also allows comparing the methods available in two worlds just by comparing their ordinal value. -In the example above, we see that the "current world" (in which the method `newfun()` exists), +In the example above, we see that the "current world" (in which the method `newfun` exists), is one greater than the task-local "runtime world" that was fixed when the execution of `tryeval` started. Sometimes it is necessary to get around this (for example, if you are implementing the above REPL). diff --git a/doc/src/manual/modules.md b/doc/src/manual/modules.md index 921f195c7a317..ee15f40288e50 100644 --- a/doc/src/manual/modules.md +++ b/doc/src/manual/modules.md @@ -384,7 +384,7 @@ A few other points to be aware of: It is sometimes helpful during module development to turn off incremental precompilation. The command line flag `--compilecache={yes|no}` enables you to toggle module precompilation on and off. When Julia is started with `--compilecache=no` the serialized modules in the compile cache -are ignored when loading modules and module dependencies. `Base.compilecache()` can still be called +are ignored when loading modules and module dependencies. `Base.compilecache` can still be called manually and it will respect `__precompile__()` directives for the module. The state of this command -line flag is passed to [`Pkg.build()`](@ref) to disable automatic precompilation triggering when installing, +line flag is passed to [`Pkg.build`](@ref) to disable automatic precompilation triggering when installing, updating, and explicitly building packages. diff --git a/doc/src/manual/networking-and-streams.md b/doc/src/manual/networking-and-streams.md index 00ee502a61639..2e5916c8f176f 100644 --- a/doc/src/manual/networking-and-streams.md +++ b/doc/src/manual/networking-and-streams.md @@ -8,7 +8,7 @@ functionality. ## Basic Stream I/O -All Julia streams expose at least a [`read()`](@ref) and a [`write()`](@ref) method, taking the +All Julia streams expose at least a [`read`](@ref) and a [`write`](@ref) method, taking the stream as their first argument, e.g.: ```julia-repl @@ -19,11 +19,11 @@ julia> read(STDIN,Char) '\n': ASCII/Unicode U+000a (category Cc: Other, control) ``` -Note that [`write()`](@ref) returns 11, the number of bytes (in `"Hello World"`) written to [`STDOUT`](@ref), +Note that [`write`](@ref) returns 11, the number of bytes (in `"Hello World"`) written to [`STDOUT`](@ref), but this return value is suppressed with the `;`. Here Enter was pressed again so that Julia would read the newline. Now, as you can see from this -example, [`write()`](@ref) takes the data to write as its second argument, while [`read()`](@ref) +example, [`write`](@ref) takes the data to write as its second argument, while [`read`](@ref) takes the type of the data to be read as the second argument. For example, to read a simple byte array, we could do: @@ -69,7 +69,7 @@ abcd Note that depending on your terminal settings, your TTY may be line buffered and might thus require an additional enter before the data is sent to Julia. -To read every line from [`STDIN`](@ref) you can use [`eachline()`](@ref): +To read every line from [`STDIN`](@ref) you can use [`eachline`](@ref): ```julia for line in eachline(STDIN) @@ -77,7 +77,7 @@ for line in eachline(STDIN) end ``` -or [`read()`](@ref) if you wanted to read by character instead: +or [`read`](@ref) if you wanted to read by character instead: ```julia while !eof(STDIN) @@ -88,7 +88,7 @@ end ## Text I/O -Note that the [`write()`](@ref) method mentioned above operates on binary streams. In particular, +Note that the [`write`](@ref) method mentioned above operates on binary streams. In particular, values do not get converted to any canonical text representation but are written out as is: ```jldoctest @@ -96,10 +96,10 @@ julia> write(STDOUT,0x61); # suppress return value 1 with ; a ``` -Note that `a` is written to [`STDOUT`](@ref) by the [`write()`](@ref) function and that the returned +Note that `a` is written to [`STDOUT`](@ref) by the [`write`](@ref) function and that the returned value is `1` (since `0x61` is one byte). -For text I/O, use the [`print()`](@ref) or [`show()`](@ref) methods, depending on your needs (see +For text I/O, use the [`print`](@ref) or [`show`](@ref) methods, depending on your needs (see the standard library reference for a detailed discussion of the difference between the two): ```jldoctest @@ -116,7 +116,7 @@ should print a shorter output (if applicable). ## Working with Files -Like many other environments, Julia has an [`open()`](@ref) function, which takes a filename and +Like many other environments, Julia has an [`open`](@ref) function, which takes a filename and returns an `IOStream` object that you can use to read and write things from the file. For example if we have a file, `hello.txt`, whose contents are `Hello, World!`: @@ -150,7 +150,7 @@ julia> close(f) Examining `hello.txt` again will show its contents have been changed. Opening a file, doing something to its contents, and closing it again is a very common pattern. -To make this easier, there exists another invocation of [`open()`](@ref) which takes a function +To make this easier, there exists another invocation of [`open`](@ref) which takes a function as its first argument and filename as its second, opens the file, calls the function with the file as an argument, and then closes it again. For example, given a function: @@ -196,7 +196,7 @@ Task (runnable) @0x00007fd31dc11ae0 ``` To those familiar with the Unix socket API, the method names will feel familiar, though their -usage is somewhat simpler than the raw Unix socket API. The first call to [`listen()`](@ref) will +usage is somewhat simpler than the raw Unix socket API. The first call to [`listen`](@ref) will create a server waiting for incoming connections on the specified port (2000) in this case. The same function may also be used to create various other kinds of servers: @@ -228,12 +228,12 @@ listen on TCP, but rather on a named pipe (Windows) or UNIX domain socket. Also named pipe format has to be a specific pattern such that the name prefix (`\\.\pipe\`) uniquely identifies the [file type](https://msdn.microsoft.com/en-us/library/windows/desktop/aa365783(v=vs.85).aspx). The difference between TCP and named pipes or -UNIX domain sockets is subtle and has to do with the [`accept()`](@ref) and [`connect()`](@ref) -methods. The [`accept()`](@ref) method retrieves a connection to the client that is connecting on -the server we just created, while the [`connect()`](@ref) function connects to a server using the -specified method. The [`connect()`](@ref) function takes the same arguments as [`listen()`](@ref), +UNIX domain sockets is subtle and has to do with the [`accept`](@ref) and [`connect`](@ref) +methods. The [`accept`](@ref) method retrieves a connection to the client that is connecting on +the server we just created, while the [`connect`](@ref) function connects to a server using the +specified method. The [`connect`](@ref) function takes the same arguments as [`listen`](@ref), so, assuming the environment (i.e. host, cwd, etc.) is the same you should be able to pass the same -arguments to [`connect()`](@ref) as you did to listen to establish the connection. So let's try that +arguments to [`connect`](@ref) as you did to listen to establish the connection. So let's try that out (after having created the server above): ```julia-repl @@ -244,13 +244,13 @@ julia> Hello World ``` As expected we saw "Hello World" printed. So, let's actually analyze what happened behind the -scenes. When we called [`connect()`](@ref), we connect to the server we had just created. Meanwhile, +scenes. When we called [`connect`](@ref), we connect to the server we had just created. Meanwhile, the accept function returns a server-side connection to the newly created socket and prints "Hello World" to indicate that the connection was successful. A great strength of Julia is that since the API is exposed synchronously even though the I/O is actually happening asynchronously, we didn't have to worry callbacks or even making sure that -the server gets to run. When we called [`connect()`](@ref) the current task waited for the connection +the server gets to run. When we called [`connect`](@ref) the current task waited for the connection to be established and only continued executing after that was done. In this pause, the server task resumed execution (because a connection request was now available), accepted the connection, printed the message and waited for the next client. Reading and writing works in the same way. @@ -280,7 +280,7 @@ julia> println(clientside,"Hello World from the Echo Server") Hello World from the Echo Server ``` -As with other streams, use [`close()`](@ref) to disconnect the socket: +As with other streams, use [`close`](@ref) to disconnect the socket: ```julia-repl julia> close(clientside) @@ -288,7 +288,7 @@ julia> close(clientside) ## Resolving IP Addresses -One of the [`connect()`](@ref) methods that does not follow the [`listen()`](@ref) methods is +One of the [`connect`](@ref) methods that does not follow the [`listen`](@ref) methods is `connect(host::String,port)`, which will attempt to connect to the host given by the `host` parameter on the port given by the port parameter. It allows you to do things like: @@ -297,7 +297,7 @@ julia> connect("google.com",80) TCPSocket(RawFD(30) open, 0 bytes waiting) ``` -At the base of this functionality is [`getaddrinfo()`](@ref), which will do the appropriate address +At the base of this functionality is [`getaddrinfo`](@ref), which will do the appropriate address resolution: ```julia-repl diff --git a/doc/src/manual/noteworthy-differences.md b/doc/src/manual/noteworthy-differences.md index 72a3ea4467fa4..202dbf12224cd 100644 --- a/doc/src/manual/noteworthy-differences.md +++ b/doc/src/manual/noteworthy-differences.md @@ -14,7 +14,7 @@ may trip up Julia users accustomed to MATLAB: * Julia does not automatically grow arrays in an assignment statement. Whereas in MATLAB `a(4) = 3.2` can create the array `a = [0 0 0 3.2]` and `a(5) = 7` can grow it into `a = [0 0 0 3.2 7]`, the corresponding Julia statement `a[5] = 7` throws an error if the length of `a` is less than 5 or - if this statement is the first use of the identifier `a`. Julia has [`push!()`](@ref) and [`append!()`](@ref), + if this statement is the first use of the identifier `a`. Julia has [`push!`](@ref) and [`append!`](@ref), which grow `Vector`s much more efficiently than MATLAB's `a(end+1) = val`. * The imaginary unit `sqrt(-1)` is represented in Julia as [`im`](@ref), not `i` or `j` as in MATLAB. * In Julia, literal numbers without a decimal point (such as `42`) create integers instead of floating @@ -32,7 +32,7 @@ may trip up Julia users accustomed to MATLAB: with semicolons (`[x; y; z]`). - To concatenate in the second ("horizontal") dimension use either [`hcat(x,y,z)`](@ref) or separate with spaces (`[x y z]`). - - To construct block matrices (concatenating in the first two dimensions), use either [`hvcat()`](@ref) + - To construct block matrices (concatenating in the first two dimensions), use either [`hvcat`](@ref) or combine spaces and semicolons (`[a b; c d]`). * In Julia, `a:b` and `a:b:c` construct `Range` objects. To construct a full vector like in MATLAB, use [`collect(a:b)`](@ref). Generally, there is no need to call `collect` though. `Range` will @@ -46,17 +46,17 @@ may trip up Julia users accustomed to MATLAB: * A Julia script may contain any number of functions, and all definitions will be externally visible when the file is loaded. Function definitions can be loaded from files outside the current working directory. - * In Julia, reductions such as [`sum()`](@ref), [`prod()`](@ref), and [`max()`](@ref) are performed + * In Julia, reductions such as [`sum`](@ref), [`prod`](@ref), and [`max`](@ref) are performed over every element of an array when called with a single argument, as in `sum(A)`, even if `A` has more than one dimension. - * In Julia, functions such as [`sort()`](@ref) that operate column-wise by default (`sort(A)` is + * In Julia, functions such as [`sort`](@ref) that operate column-wise by default (`sort(A)` is equivalent to `sort(A,1)`) do not have special behavior for `1xN` arrays; the argument is returned unmodified since it still performs `sort(A,1)`. To sort a `1xN` matrix like a vector, use `sort(A,2)`. * In Julia, parentheses must be used to call a function with zero arguments, like in [`tic()`](@ref) and [`toc()`](@ref). * Julia discourages the used of semicolons to end statements. The results of statements are not automatically printed (except at the interactive prompt), and lines of code do not need to end - with semicolons. [`println()`](@ref) or [`@printf()`](@ref) can be used to print specific output. + with semicolons. [`println`](@ref) or [`@printf`](@ref) can be used to print specific output. * In Julia, if `A` and `B` are arrays, logical comparison operations like `A == B` do not return an array of booleans. Instead, use `A .== B`, and similarly for the other boolean operators like [`<`](@ref), [`>`](@ref) and `=`. @@ -67,7 +67,7 @@ may trip up Julia users accustomed to MATLAB: parentheses may be required (e.g., to select elements of `A` equal to 1 or 2 use `(A .== 1) | (A .== 2)`). * In Julia, the elements of a collection can be passed as arguments to a function using the splat operator `...`, as in `xs=[1,2]; f(xs...)`. - * Julia's [`svd()`](@ref) returns singular values as a vector instead of as a dense diagonal matrix. + * Julia's [`svd`](@ref) returns singular values as a vector instead of as a dense diagonal matrix. * In Julia, `...` is not used to continue lines of code. Instead, incomplete expressions automatically continue onto the next line. * In both Julia and MATLAB, the variable `ans` is set to the value of the last expression issued @@ -79,9 +79,9 @@ may trip up Julia users accustomed to MATLAB: scope. * In MATLAB, an idiomatic way to remove unwanted values is to use logical indexing, like in the expression `x(x>3)` or in the statement `x(x>3) = []` to modify `x` in-place. In contrast, Julia - provides the higher order functions [`filter()`](@ref) and [`filter!()`](@ref), allowing users + provides the higher order functions [`filter`](@ref) and [`filter!`](@ref), allowing users to write `filter(z->z>3, x)` and `filter!(z->z>3, x)` as alternatives to the corresponding transliterations - `x[x.>3]` and `x = x[x.>3]`. Using [`filter!()`](@ref) reduces the use of temporary arrays. + `x[x.>3]` and `x = x[x.>3]`. Using [`filter!`](@ref) reduces the use of temporary arrays. * The analogue of extracting (or "dereferencing") all elements of a cell array, e.g. in `vertcat(A{:})` in MATLAB, is written using the splat operator in Julia, e.g. as `vcat(A...)`. @@ -108,8 +108,8 @@ For users coming to Julia from R, these are some noteworthy differences: * Like many languages, Julia does not always allow operations on vectors of different lengths, unlike R where the vectors only need to share a common index range. For example, `c(1, 2, 3, 4) + c(1, 2)` is valid R but the equivalent `[1, 2, 3, 4] + [1, 2]` will throw an error in Julia. - * Julia's [`map()`](@ref) takes the function first, then its arguments, unlike `lapply(, function, ...)` - in R. Similarly Julia's equivalent of `apply(X, MARGIN, FUN, ...)` in R is [`mapslices()`](@ref) + * Julia's [`map`](@ref) takes the function first, then its arguments, unlike `lapply(, function, ...)` + in R. Similarly Julia's equivalent of `apply(X, MARGIN, FUN, ...)` in R is [`mapslices`](@ref) where the function is the first argument. * Multivariate apply in R, e.g. `mapply(choose, 11:13, 1:3)`, can be written as `broadcast(binomial, 11:13, 1:3)` in Julia. Equivalently Julia offers a shorter dot syntax for vectorizing functions `binomial.(11:13, 1:3)`. @@ -136,7 +136,7 @@ For users coming to Julia from R, these are some noteworthy differences: * Julia is careful to distinguish scalars, vectors and matrices. In R, `1` and `c(1)` are the same. In Julia, they can not be used interchangeably. One potentially confusing result of this is that `x' * y` for vectors `x` and `y` is a 1-element vector, not a scalar. To get a scalar, use [`dot(x, y)`](@ref). - * Julia's [`diag()`](@ref) and [`diagm()`](@ref) are not like R's. + * Julia's [`diag`](@ref) and [`diagm`](@ref) are not like R's. * Julia cannot assign to the results of function calls on the left hand side of an assignment operation: you cannot write `diag(M) = ones(n)`. * Julia discourages populating the main namespace with functions. Most statistical functionality @@ -154,15 +154,15 @@ For users coming to Julia from R, these are some noteworthy differences: * In Julia, values are passed and assigned by reference. If a function modifies an array, the changes will be visible in the caller. This is very different from R and allows new functions to operate on large data structures much more efficiently. - * In Julia, vectors and matrices are concatenated using [`hcat()`](@ref), [`vcat()`](@ref) and - [`hvcat()`](@ref), not `c`, `rbind` and `cbind` like in R. + * In Julia, vectors and matrices are concatenated using [`hcat`](@ref), [`vcat`](@ref) and + [`hvcat`](@ref), not `c`, `rbind` and `cbind` like in R. * In Julia, a range like `a:b` is not shorthand for a vector like in R, but is a specialized `Range` that is used for iteration without high memory overhead. To convert a range into a vector, use [`collect(a:b)`](@ref). - * Julia's [`max()`](@ref) and [`min()`](@ref) are the equivalent of `pmax` and `pmin` respectively - in R, but both arguments need to have the same dimensions. While [`maximum()`](@ref) and [`minimum()`](@ref) + * Julia's [`max`](@ref) and [`min`](@ref) are the equivalent of `pmax` and `pmin` respectively + in R, but both arguments need to have the same dimensions. While [`maximum`](@ref) and [`minimum`](@ref) replace `max` and `min` in R, there are important differences. - * Julia's [`sum()`](@ref), [`prod()`](@ref), [`maximum()`](@ref), and [`minimum()`](@ref) are different + * Julia's [`sum`](@ref), [`prod`](@ref), [`maximum`](@ref), and [`minimum`](@ref) are different from their counterparts in R. They all accept one or two arguments. The first argument is an iterable collection such as an array. If there is a second argument, then this argument indicates the dimensions, over which the operation is carried out. For instance, let `A=[[1 2],[3 4]]` in Julia @@ -172,8 +172,8 @@ For users coming to Julia from R, these are some noteworthy differences: `sum(B,1)=11` and `sum(B,2)=12`. If the second argument is a vector, then it specifies all the dimensions over which the sum is performed, e.g., `sum(A,[1,2])=10`. It should be noted that there is no error checking regarding the second argument. - * Julia has several functions that can mutate their arguments. For example, it has both [`sort()`](@ref) - and [`sort!()`](@ref). + * Julia has several functions that can mutate their arguments. For example, it has both [`sort`](@ref) + and [`sort!`](@ref). * In R, performance requires vectorization. In Julia, almost the opposite is true: the best performing code is often achieved by using devectorized loops. * Julia is eagerly evaluated and does not support R-style lazy evaluation. For most users, this @@ -183,9 +183,9 @@ For users coming to Julia from R, these are some noteworthy differences: * In Julia, `return` does not require parentheses. * In R, an idiomatic way to remove unwanted values is to use logical indexing, like in the expression `x[x>3]` or in the statement `x = x[x>3]` to modify `x` in-place. In contrast, Julia provides - the higher order functions [`filter()`](@ref) and [`filter!()`](@ref), allowing users to write + the higher order functions [`filter`](@ref) and [`filter!`](@ref), allowing users to write `filter(z->z>3, x)` and `filter!(z->z>3, x)` as alternatives to the corresponding transliterations - `x[x.>3]` and `x = x[x.>3]`. Using [`filter!()`](@ref) reduces the use of temporary arrays. + `x[x.>3]` and `x = x[x.>3]`. Using [`filter!`](@ref) reduces the use of temporary arrays. ## Noteworthy differences from Python @@ -257,7 +257,7 @@ For users coming to Julia from R, these are some noteworthy differences: C/C++ (i.e. `a = myfunction(&b)`. * Julia does not require the use of semicolons to end statements. The results of expressions are not automatically printed (except at the interactive prompt, i.e. the REPL), and lines of code - do not need to end with semicolons. [`println()`](@ref) or [`@printf()`](@ref) can be used to + do not need to end with semicolons. [`println`](@ref) or [`@printf`](@ref) can be used to print specific output. In the REPL, `;` can be used to suppress output. `;` also has a different meaning within `[ ]`, something to watch out for. `;` can be used to separate expressions on a single line, but are not strictly necessary in many cases, and are more an aid to readability. diff --git a/doc/src/manual/parallel-computing.md b/doc/src/manual/parallel-computing.md index 0d31a561f149e..90e8c0b465b91 100644 --- a/doc/src/manual/parallel-computing.md +++ b/doc/src/manual/parallel-computing.md @@ -26,8 +26,8 @@ Remote references come in two flavors: [`Future`](@ref) and [`RemoteChannel`](@r A remote call returns a [`Future`](@ref) to its result. Remote calls return immediately; the process that made the call proceeds to its next operation while the remote call happens somewhere else. -You can wait for a remote call to finish by calling [`wait()`](@ref) on the returned [`Future`](@ref), -and you can obtain the full value of the result using [`fetch()`](@ref). +You can wait for a remote call to finish by calling [`wait`](@ref) on the returned [`Future`](@ref), +and you can obtain the full value of the result using [`fetch`](@ref). On the other hand, [`RemoteChannel`](@ref) s are rewritable. For example, multiple processes can co-ordinate their processing by referencing the same remote `Channel`. @@ -55,9 +55,9 @@ julia> fetch(s) 1.16296 1.60607 ``` -The first argument to [`remotecall()`](@ref) is the function to call. Most parallel programming -in Julia does not reference specific processes or the number of processes available, but [`remotecall()`](@ref) -is considered a low-level interface providing finer control. The second argument to [`remotecall()`](@ref) +The first argument to [`remotecall`](@ref) is the function to call. Most parallel programming +in Julia does not reference specific processes or the number of processes available, but [`remotecall`](@ref) +is considered a low-level interface providing finer control. The second argument to [`remotecall`](@ref) is the `id` of the process that will do the work, and the remaining arguments will be passed to the function being called. @@ -68,7 +68,7 @@ argument on the process specified by the first argument. Occasionally you might want a remotely-computed value immediately. This typically happens when you read from a remote object to obtain data needed by the next local operation. The function -[`remotecall_fetch()`](@ref) exists for this purpose. It is equivalent to `fetch(remotecall(...))` +[`remotecall_fetch`](@ref) exists for this purpose. It is equivalent to `fetch(remotecall(...))` but is more efficient. ```julia-repl @@ -79,7 +79,7 @@ julia> remotecall_fetch(getindex, 2, r, 1, 1) Remember that [`getindex(r,1,1)`](@ref) is [equivalent](@ref man-array-indexing) to `r[1,1]`, so this call fetches the first element of the future `r`. -The syntax of [`remotecall()`](@ref) is not especially convenient. The macro [`@spawn`](@ref) +The syntax of [`remotecall`](@ref) is not especially convenient. The macro [`@spawn`](@ref) makes things easier. It operates on an expression rather than a function, and picks where to do the operation for you: @@ -97,15 +97,15 @@ julia> fetch(s) ``` Note that we used `1 .+ fetch(r)` instead of `1 .+ r`. This is because we do not know where the -code will run, so in general a [`fetch()`](@ref) might be required to move `r` to the process +code will run, so in general a [`fetch`](@ref) might be required to move `r` to the process doing the addition. In this case, [`@spawn`](@ref) is smart enough to perform the computation -on the process that owns `r`, so the [`fetch()`](@ref) will be a no-op (no work is done). +on the process that owns `r`, so the [`fetch`](@ref) will be a no-op (no work is done). (It is worth noting that [`@spawn`](@ref) is not built-in but defined in Julia as a [macro](@ref man-macros). It is possible to define your own such constructs.) An important thing to remember is that, once fetched, a [`Future`](@ref) will cache its value -locally. Further [`fetch()`](@ref) calls do not entail a network hop. Once all referencing [`Future`](@ref)s +locally. Further [`fetch`](@ref) calls do not entail a network hop. Once all referencing [`Future`](@ref)s have fetched, the remote stored value is deleted. ## Code Availability and Loading Packages @@ -192,7 +192,7 @@ The base Julia installation has in-built support for two types of clusters: * A cluster spanning machines using the `--machinefile` option. This uses a passwordless `ssh` login to start Julia worker processes (from the same path as the current host) on the specified machines. -Functions [`addprocs()`](@ref), [`rmprocs()`](@ref), [`workers()`](@ref), and others are available +Functions [`addprocs`](@ref), [`rmprocs`](@ref), [`workers`](@ref), and others are available as a programmatic means of adding, removing and querying the processes in a cluster. Note that workers do not run a `.juliarc.jl` startup script, nor do they synchronize their global @@ -209,7 +209,7 @@ the number of messages and the amount of data sent is critical to achieving perf To this end, it is important to understand the data movement performed by Julia's various parallel programming constructs. -[`fetch()`](@ref) can be considered an explicit data movement operation, since it directly asks +[`fetch`](@ref) can be considered an explicit data movement operation, since it directly asks that an object be moved to the local machine. [`@spawn`](@ref) (and a few related constructs) also moves data, but this is not as obvious, hence it can be called an implicit data movement operation. Consider these two approaches to constructing and squaring a random matrix: @@ -420,12 +420,12 @@ Here each iteration applies `f` to a randomly-chosen sample from a vector `a` sh As you could see, the reduction operator can be omitted if it is not needed. In that case, the loop executes asynchronously, i.e. it spawns independent tasks on all available workers and returns an array of [`Future`](@ref) immediately without waiting for completion. The caller can wait for -the [`Future`](@ref) completions at a later point by calling [`fetch()`](@ref) on them, or wait +the [`Future`](@ref) completions at a later point by calling [`fetch`](@ref) on them, or wait for completion at the end of the loop by prefixing it with [`@sync`](@ref), like `@sync @parallel for`. In some cases no reduction operator is needed, and we merely wish to apply a function to all integers in some range (or, more generally, to all elements in some collection). This is another useful -operation called *parallel map*, implemented in Julia as the [`pmap()`](@ref) function. For example, +operation called *parallel map*, implemented in Julia as the [`pmap`](@ref) function. For example, we could compute the singular values of several large random matrices in parallel as follows: ```julia-repl @@ -434,9 +434,9 @@ julia> M = Matrix{Float64}[rand(1000,1000) for i = 1:10]; julia> pmap(svd, M); ``` -Julia's [`pmap()`](@ref) is designed for the case where each function call does a large amount +Julia's [`pmap`](@ref) is designed for the case where each function call does a large amount of work. In contrast, `@parallel for` can handle situations where each iteration is tiny, perhaps -merely summing two numbers. Only worker processes are used by both [`pmap()`](@ref) and `@parallel for` +merely summing two numbers. Only worker processes are used by both [`pmap`](@ref) and `@parallel for` for the parallel computation. In case of `@parallel for`, the final reduction is done on the calling process. @@ -445,7 +445,7 @@ process. ## Scheduling Julia's parallel programming platform uses [Tasks (aka Coroutines)](@ref man-tasks) to switch among multiple -computations. Whenever code performs a communication operation like [`fetch()`](@ref) or [`wait()`](@ref), +computations. Whenever code performs a communication operation like [`fetch`](@ref) or [`wait`](@ref), the current task is suspended and a scheduler picks another task to run. A task is restarted when the event it is waiting for completes. @@ -465,7 +465,7 @@ julia> pmap(svd, M); If one process handles both 800×800 matrices and another handles both 600×600 matrices, we will not get as much scalability as we could. The solution is to make a local task to "feed" work to -each process when it completes its current task. For example, consider a simple [`pmap()`](@ref) +each process when it completes its current task. For example, consider a simple [`pmap`](@ref) implementation: ```julia @@ -501,10 +501,10 @@ use it to create a "feeder" task for each process. Each task picks the next inde be computed, then waits for its process to finish, then repeats until we run out of indexes. Note that the feeder tasks do not begin to execute until the main task reaches the end of the [`@sync`](@ref) block, at which point it surrenders control and waits for all the local tasks to complete before -returning from the function. The feeder tasks are able to share state via `nextidx()` because +returning from the function. The feeder tasks are able to share state via `nextidx` because they all run on the same process. No locking is required, since the threads are scheduled cooperatively and not preemptively. This means context switches only occur at well-defined points: in this case, -when [`remotecall_fetch()`](@ref) is called. +when [`remotecall_fetch`](@ref) is called. ## Channels @@ -519,9 +519,9 @@ to complete. A channel can be visualized as a pipe, i.e., it has a write end and read end. - * Multiple writers in different tasks can write to the same channel concurrently via [`put!()`](@ref) + * Multiple writers in different tasks can write to the same channel concurrently via [`put!`](@ref) calls. - * Multiple readers in different tasks can read data concurrently via [`take!()`](@ref) calls. + * Multiple readers in different tasks can read data concurrently via [`take!`](@ref) calls. * As an example: ```julia @@ -529,7 +529,7 @@ A channel can be visualized as a pipe, i.e., it has a write end and read end. c1 = Channel(32) c2 = Channel(32) - # and a function `foo()` which reads items from from c1, processes the item read + # and a function `foo` which reads items from from c1, processes the item read # and writes a result to c2, function foo() while true @@ -539,7 +539,7 @@ A channel can be visualized as a pipe, i.e., it has a write end and read end. end end - # we can schedule `n` instances of `foo()` to be active concurrently. + # we can schedule `n` instances of `foo` to be active concurrently. for _ in 1:n @schedule foo() end @@ -549,13 +549,13 @@ A channel can be visualized as a pipe, i.e., it has a write end and read end. to the maximum number of elements that can be held in the channel at any time. For example, `Channel(32)` creates a channel that can hold a maximum of 32 objects of any type. A `Channel{MyType}(64)` can hold up to 64 objects of `MyType` at any time. - * If a [`Channel`](@ref) is empty, readers (on a [`take!()`](@ref) call) will block until data is available. - * If a [`Channel`](@ref) is full, writers (on a [`put!()`](@ref) call) will block until space becomes available. - * [`isready()`](@ref) tests for the presence of any object in the channel, while [`wait()`](@ref) + * If a [`Channel`](@ref) is empty, readers (on a [`take!`](@ref) call) will block until data is available. + * If a [`Channel`](@ref) is full, writers (on a [`put!`](@ref) call) will block until space becomes available. + * [`isready`](@ref) tests for the presence of any object in the channel, while [`wait`](@ref) waits for an object to become available. * A [`Channel`](@ref) is in an open state initially. This means that it can be read from and written to - freely via [`take!()`](@ref) and [`put!()`](@ref) calls. [`close()`](@ref) closes a [`Channel`](@ref). - On a closed [`Channel`](@ref), [`put!()`](@ref) will fail. For example: + freely via [`take!`](@ref) and [`put!`](@ref) calls. [`close`](@ref) closes a [`Channel`](@ref). + On a closed [`Channel`](@ref), [`put!`](@ref) will fail. For example: ```julia-repl julia> c = Channel(2); @@ -570,7 +570,7 @@ ERROR: InvalidStateException("Channel is closed.",:closed) [...] ``` - * [`take!()`](@ref) and [`fetch()`](@ref) (which retrieves but does not remove the value) on a closed + * [`take!`](@ref) and [`fetch`](@ref) (which retrieves but does not remove the value) on a closed channel successfully return any existing values until it is emptied. Continuing the above example: ```julia-repl @@ -683,7 +683,7 @@ too. Remote references always refer to an implementation of an `AbstractChannel`. A concrete implementation of an `AbstractChannel` (like `Channel`), is required to implement -[`put!()`](@ref), [`take!()`](@ref), [`fetch()`](@ref), [`isready()`](@ref) and [`wait()`](@ref). +[`put!`](@ref), [`take!`](@ref), [`fetch`](@ref), [`isready`](@ref) and [`wait`](@ref). The remote object referred to by a [`Future`](@ref) is stored in a `Channel{Any}(1)`, i.e., a `Channel` of size 1 capable of holding objects of `Any` type. @@ -691,13 +691,13 @@ The remote object referred to by a [`Future`](@ref) is stored in a `Channel{Any} other implementation of an `AbstractChannel`. The constructor `RemoteChannel(f::Function, pid)()` allows us to construct references to channels -holding more than one value of a specific type. `f()` is a function executed on `pid` and it must +holding more than one value of a specific type. `f` is a function executed on `pid` and it must return an `AbstractChannel`. For example, `RemoteChannel(()->Channel{Int}(10), pid)`, will return a reference to a channel of type `Int` and size 10. The channel exists on worker `pid`. -Methods [`put!()`](@ref), [`take!()`](@ref), [`fetch()`](@ref), [`isready()`](@ref) and [`wait()`](@ref) +Methods [`put!`](@ref), [`take!`](@ref), [`fetch`](@ref), [`isready`](@ref) and [`wait`](@ref) on a [`RemoteChannel`](@ref) are proxied onto the backing store on the remote process. [`RemoteChannel`](@ref) can thus be used to refer to user implemented `AbstractChannel` objects. @@ -796,7 +796,7 @@ The notifications are done via sending of "tracking" messages--an "add reference a reference is serialized to a different process and a "delete reference" message when a reference is locally garbage collected. -Since [`Future`](@ref)s are write-once and cached locally, the act of [`fetch()`](@ref)ing a +Since [`Future`](@ref)s are write-once and cached locally, the act of [`fetch`](@ref)ing a [`Future`](@ref) also updates reference tracking information on the node owning the value. The node which owns the value frees it once all references to it are cleared. @@ -809,10 +809,10 @@ of the object and the current memory pressure in the system. In case of remote references, the size of the local reference object is quite small, while the value stored on the remote node may be quite large. Since the local object may not be collected -immediately, it is a good practice to explicitly call [`finalize()`](@ref) on local instances -of a [`RemoteChannel`](@ref), or on unfetched [`Future`](@ref)s. Since calling [`fetch()`](@ref) +immediately, it is a good practice to explicitly call [`finalize`](@ref) on local instances +of a [`RemoteChannel`](@ref), or on unfetched [`Future`](@ref)s. Since calling [`fetch`](@ref) on a [`Future`](@ref) also removes its reference from the remote store, this is not required on -fetched [`Future`](@ref)s. Explicitly calling [`finalize()`](@ref) results in an immediate message +fetched [`Future`](@ref)s. Explicitly calling [`finalize`](@ref) results in an immediate message sent to the remote node to go ahead and remove its reference to the value. Once finalized, a reference becomes invalid and cannot be used in any further calls. @@ -831,8 +831,8 @@ data jointly accessible to two or more processes on the same machine. and is efficient because the underlying memory is available to the local process. Therefore, most algorithms work naturally on [`SharedArray`](@ref)s, albeit in single-process mode. In cases where an algorithm insists on an [`Array`](@ref) input, the underlying array can be retrieved -from a [`SharedArray`](@ref) by calling [`sdata()`](@ref). For other `AbstractArray` types, [`sdata()`](@ref) -just returns the object itself, so it's safe to use [`sdata()`](@ref) on any `Array`-type object. +from a [`SharedArray`](@ref) by calling [`sdata`](@ref). For other `AbstractArray` types, [`sdata`](@ref) +just returns the object itself, so it's safe to use [`sdata`](@ref) on any `Array`-type object. The constructor for a shared array is of the form: @@ -874,7 +874,7 @@ julia> S 2 7 4 4 ``` -[`Base.localindexes()`](@ref) provides disjoint one-dimensional ranges of indexes, and is sometimes +[`Base.localindexes`](@ref) provides disjoint one-dimensional ranges of indexes, and is sometimes convenient for splitting up tasks among processes. You can, of course, divide the work any way you wish: @@ -1035,8 +1035,8 @@ A Julia cluster has the following characteristics: Connections between workers (using the in-built TCP/IP transport) is established in the following manner: - * [`addprocs()`](@ref) is called on the master process with a `ClusterManager` object. - * [`addprocs()`](@ref) calls the appropriate [`launch()`](@ref) method which spawns required number + * [`addprocs`](@ref) is called on the master process with a `ClusterManager` object. + * [`addprocs`](@ref) calls the appropriate [`launch`](@ref) method which spawns required number of worker processes on appropriate machines. * Each worker starts listening on a free port and writes out its host and port information to [`STDOUT`](@ref). * The cluster manager captures the [`STDOUT`](@ref) of each worker and makes it available to the @@ -1061,8 +1061,8 @@ and multi-processor hardware. Thus, a minimal cluster manager would need to: * be a subtype of the abstract `ClusterManager` - * implement [`launch()`](@ref), a method responsible for launching new workers - * implement [`manage()`](@ref), which is called at various events during a worker's lifetime (for + * implement [`launch`](@ref), a method responsible for launching new workers + * implement [`manage`](@ref), which is called at various events during a worker's lifetime (for example, sending an interrupt signal) [`addprocs(manager::FooManager)`](@ref addprocs) requires `FooManager` to implement: @@ -1094,15 +1094,15 @@ function manage(manager::LocalManager, id::Integer, config::WorkerConfig, op::Sy end ``` -The [`launch()`](@ref) method takes the following arguments: +The [`launch`](@ref) method takes the following arguments: - * `manager::ClusterManager`: the cluster manager that [`addprocs()`](@ref) is called with - * `params::Dict`: all the keyword arguments passed to [`addprocs()`](@ref) + * `manager::ClusterManager`: the cluster manager that [`addprocs`](@ref) is called with + * `params::Dict`: all the keyword arguments passed to [`addprocs`](@ref) * `launched::Array`: the array to append one or more `WorkerConfig` objects to * `c::Condition`: the condition variable to be notified as and when workers are launched -The [`launch()`](@ref) method is called asynchronously in a separate task. The termination of -this task signals that all requested workers have been launched. Hence the [`launch()`](@ref) +The [`launch`](@ref) method is called asynchronously in a separate task. The termination of +this task signals that all requested workers have been launched. Hence the [`launch`](@ref) function MUST exit as soon as all the requested workers have been launched. Newly launched workers are connected to each other and the master process in an all-to-all manner. @@ -1122,7 +1122,7 @@ As an example of a non-TCP/IP transport, an implementation may choose to use MPI `--worker` must NOT be specified. Instead, newly launched workers should call `init_worker(cookie)` before using any of the parallel constructs. -For every worker launched, the [`launch()`](@ref) method must add a `WorkerConfig` object (with +For every worker launched, the [`launch`](@ref) method must add a `WorkerConfig` object (with appropriate fields initialized) to `launched` ```julia @@ -1225,8 +1225,8 @@ When using custom transports: must be called. This launches a new task that handles reading and writing of messages from/to the worker represented by the `IO` objects. * `init_worker(cookie, manager::FooManager)` MUST be called as part of worker process initialization. - * Field `connect_at::Any` in `WorkerConfig` can be set by the cluster manager when [`launch()`](@ref) - is called. The value of this field is passed in in all [`connect()`](@ref) callbacks. Typically, + * Field `connect_at::Any` in `WorkerConfig` can be set by the cluster manager when [`launch`](@ref) + is called. The value of this field is passed in in all [`connect`](@ref) callbacks. Typically, it carries information on *how to connect* to a worker. For example, the TCP/IP socket transport uses this field to specify the `(host, port)` tuple at which to connect to a worker. @@ -1344,7 +1344,7 @@ julia> Threads.nthreads() 4 ``` -But we are currently on the master thread. To check, we use the command [`Threads.threadid()`](@ref) +But we are currently on the master thread. To check, we use the function [`Threads.threadid`](@ref) ```julia-repl julia> Threads.threadid() @@ -1408,12 +1408,12 @@ All I/O tasks, timers, REPL commands, etc are multiplexed onto a single OS threa loop. A patched version of libuv ([http://docs.libuv.org/en/v1.x/](http://docs.libuv.org/en/v1.x/)) provides this functionality. Yield points provide for co-operatively scheduling multiple tasks onto the same OS thread. I/O tasks and timers yield implicitly while waiting for the event to -occur. Calling [`yield()`](@ref) explicitly allows for other tasks to be scheduled. +occur. Calling [`yield`](@ref) explicitly allows for other tasks to be scheduled. Thus, a task executing a [`ccall`](@ref) effectively prevents the Julia scheduler from executing any other tasks till the call returns. This is true for all calls into external libraries. Exceptions are calls into custom C code that call back into Julia (which may then yield) or C code that calls -`jl_yield()` (C equivalent of [`yield()`](@ref)). +`jl_yield()` (C equivalent of [`yield`](@ref)). Note that while Julia code runs on a single thread (by default), libraries used by Julia may launch their own internal threads. For example, the BLAS library may start as many threads as there are diff --git a/doc/src/manual/performance-tips.md b/doc/src/manual/performance-tips.md index 2cefae6e9a867..6fa1c9a509ee2 100644 --- a/doc/src/manual/performance-tips.md +++ b/doc/src/manual/performance-tips.md @@ -74,7 +74,7 @@ On the first call (`@time f(1)`), `f` gets compiled. (If you've not yet used [` in this session, it will also compile functions needed for timing.) You should not take the results of this run seriously. For the second run, note that in addition to reporting the time, it also indicated that a large amount of memory was allocated. This is the single biggest advantage of -[`@time`](@ref) vs. functions like [`tic()`](@ref) and [`toc()`](@ref), which only report time. +[`@time`](@ref) vs. functions like [`tic`](@ref) and [`toc`](@ref), which only report time. Unexpected memory allocation is almost always a sign of some problem with your code, usually a problem with type-stability. Consequently, in addition to the allocation itself, it's very likely @@ -577,7 +577,7 @@ easily be fixed as follows: pos(x) = x < 0 ? zero(x) : x ``` -There is also a [`one()`](@ref) function, and a more general [`oftype(x, y)`](@ref) function, which +There is also a [`one`](@ref) function, and a more general [`oftype(x, y)`](@ref) function, which returns `y` converted to the type of `x`. ## Avoid changing the type of a variable @@ -838,7 +838,7 @@ Consider the following contrived example. Imagine we wanted to write a function [`Vector`](@ref) and returns a square [`Matrix`](@ref) with either the rows or the columns filled with copies of the input vector. Assume that it is not important whether rows or columns are filled with these copies (perhaps the rest of the code can be easily adapted accordingly). We could conceivably -do this in at least four ways (in addition to the recommended call to the built-in [`repmat()`](@ref)): +do this in at least four ways (in addition to the recommended call to the built-in [`repmat`](@ref)): ```julia function copy_cols(x::Vector{T}) where T @@ -1025,7 +1025,7 @@ An alternative is to create a "view" of the array, which is an array object (a `SubArray`) that actually references the data of the original array in-place, without making a copy. (If you write to a view, it modifies the original array's data as well.) -This can be done for individual slices by calling [`view()`](@ref), +This can be done for individual slices by calling [`view`](@ref), or more simply for a whole expression or block of code by putting [`@views`](@ref) in front of that expression. For example: @@ -1153,7 +1153,7 @@ These are some minor points that might help in tight inner loops. * Avoid unnecessary arrays. For example, instead of [`sum([x,y,z])`](@ref) use `x+y+z`. * Use [`abs2(z)`](@ref) instead of [`abs(z)^2`](@ref) for complex `z`. In general, try to rewrite - code to use [`abs2()`](@ref) instead of [`abs()`](@ref) for complex arguments. + code to use [`abs2`](@ref) instead of [`abs`](@ref) for complex arguments. * Use [`div(x,y)`](@ref) for truncating division of integers instead of [`trunc(x/y)`](@ref), [`fld(x,y)`](@ref) instead of [`floor(x/y)`](@ref), and [`cld(x,y)`](@ref) instead of [`ceil(x/y)`](@ref). @@ -1236,8 +1236,8 @@ following additional properties: * The loop must be an innermost loop. * The loop body must be straight-line code. This is why `@inbounds` is currently needed for all array accesses. The compiler can sometimes turn short `&&`, `||`, and `?:` expressions into straight-line - code, if it is safe to evaluate all operands unconditionally. Consider using [`ifelse()`](@ref) - instead of `?:` in the loop if it is safe to do so. + code, if it is safe to evaluate all operands unconditionally. Consider using the [`ifelse`](@ref) + function instead of `?:` in the loop if it is safe to do so. * Accesses must have a stride pattern and cannot be "gathers" (random-index reads) or "scatters" (random-index writes). * The stride should be unit stride. @@ -1323,7 +1323,7 @@ on this particular computer), the main difference is that the expression `1 / (2 `idx = 1 / (2*dx)`. In the loop, the expression `... / (2*dx)` then becomes `... * idx`, which is much faster to evaluate. Of course, both the actual optimization that is applied by the compiler as well as the resulting speedup depend very much on the hardware. You can examine the change -in generated code by using Julia's [`code_native()`](@ref) function. +in generated code by using Julia's [`code_native`](@ref) function. ## Treat Subnormal Numbers as Zeros @@ -1387,7 +1387,7 @@ a = rand(Float32,1000) * 1.f-9 ## [[`@code_warntype`](@ref)](@id man-code-warntype) -The macro [`@code_warntype`](@ref) (or its function variant [`code_warntype()`](@ref)) can sometimes +The macro [`@code_warntype`](@ref) (or its function variant [`code_warntype`](@ref)) can sometimes be helpful in diagnosing type-related problems. Here's an example: ```julia diff --git a/doc/src/manual/profile.md b/doc/src/manual/profile.md index 863c41de36c9c..8e9d530156a35 100644 --- a/doc/src/manual/profile.md +++ b/doc/src/manual/profile.md @@ -205,7 +205,7 @@ be very useful, but sometimes you want to start fresh; you can do so with [`Prof ## Options for controlling the display of profile results -[`Profile.print()`](@ref) has more options than we've described so far. Let's see the full declaration: +[`Profile.print`](@ref) has more options than we've described so far. Let's see the full declaration: ```julia function print(io::IO = STDOUT, data = fetch(); kwargs...) diff --git a/doc/src/manual/running-external-programs.md b/doc/src/manual/running-external-programs.md index e1f96a9bad903..82ca983b729cc 100644 --- a/doc/src/manual/running-external-programs.md +++ b/doc/src/manual/running-external-programs.md @@ -47,7 +47,7 @@ julia> chomp(a) == "hello" true ``` -More generally, you can use [`open()`](@ref) to read from or write to an external command. +More generally, you can use [`open`](@ref) to read from or write to an external command. ```jldoctest julia> open(`less`, "w", STDOUT) do io @@ -245,7 +245,7 @@ hello | sort This expression invokes the `echo` command with three words as arguments: `hello`, `|`, and `sort`. The result is that a single line is printed: `hello | sort`. How, then, does one construct a -pipeline? Instead of using `'|'` inside of backticks, one uses [`pipeline()`](@ref): +pipeline? Instead of using `'|'` inside of backticks, one uses [`pipeline`](@ref): ```jldoctest julia> run(pipeline(`echo hello`, `sort`)) diff --git a/doc/src/manual/stacktraces.md b/doc/src/manual/stacktraces.md index 82f0ad70f5070..6d2a06b06ccda 100644 --- a/doc/src/manual/stacktraces.md +++ b/doc/src/manual/stacktraces.md @@ -5,7 +5,7 @@ easy to use programmatically. ## Viewing a stack trace -The primary function used to obtain a stack trace is [`stacktrace()`](@ref): +The primary function used to obtain a stack trace is [`stacktrace`](@ref): ```julia-repl julia> stacktrace() @@ -69,7 +69,7 @@ julia> example() Each [`StackFrame`](@ref) contains the function name, file name, line number, lambda info, a flag indicating whether the frame has been inlined, a flag indicating whether it is a C function (by default C functions do not appear in the stack trace), and an integer representation of the pointer -returned by [`backtrace()`](@ref): +returned by [`backtrace`](@ref): ```julia-repl julia> top_frame = stacktrace()[1] @@ -126,13 +126,13 @@ julia> example() ``` You may notice that in the example above the first stack frame points points at line 4, where -[`stacktrace()`](@ref) is called, rather than line 2, where *bad_function* is called, and `bad_function`'s -frame is missing entirely. This is understandable, given that [`stacktrace()`](@ref) is called +[`stacktrace`](@ref) is called, rather than line 2, where *bad_function* is called, and `bad_function`'s +frame is missing entirely. This is understandable, given that [`stacktrace`](@ref) is called from the context of the *catch*. While in this example it's fairly easy to find the actual source of the error, in complex cases tracking down the source of the error becomes nontrivial. -This can be remedied by calling [`catch_stacktrace()`](@ref) instead of [`stacktrace()`](@ref). -Instead of returning callstack information for the current context, [`catch_stacktrace()`](@ref) +This can be remedied by calling [`catch_stacktrace`](@ref) instead of [`stacktrace`](@ref). +Instead of returning callstack information for the current context, [`catch_stacktrace`](@ref) returns stack information for the context of the most recent exception: ```julia-repl @@ -181,10 +181,10 @@ ERROR: Whoops! [...] ``` -## Comparison with [`backtrace()`](@ref) +## Comparison with [`backtrace`](@ref) -A call to [`backtrace()`](@ref) returns a vector of `Ptr{Void}`, which may then be passed into -[`stacktrace()`](@ref) for translation: +A call to [`backtrace`](@ref) returns a vector of `Ptr{Void}`, which may then be passed into +[`stacktrace`](@ref) for translation: ```julia-repl julia> trace = backtrace() @@ -220,8 +220,8 @@ julia> stacktrace(trace) (::Base.REPL.##1#2{Base.REPL.REPLBackend})() at event.jl:73 ``` -Notice that the vector returned by [`backtrace()`](@ref) had 21 pointers, while the vector returned -by [`stacktrace()`](@ref) only has 5. This is because, by default, [`stacktrace()`](@ref) removes +Notice that the vector returned by [`backtrace`](@ref) had 21 pointers, while the vector returned +by [`stacktrace`](@ref) only has 5. This is because, by default, [`stacktrace`](@ref) removes any lower-level C functions from the stack. If you want to include stack frames from C calls, you can do it like this: @@ -257,8 +257,8 @@ julia> stacktrace(trace, true) ip:0xffffffffffffffff ``` -Individual pointers returned by [`backtrace()`](@ref) can be translated into [`StackFrame`](@ref) -s by passing them into [`StackTraces.lookup()`](@ref): +Individual pointers returned by [`backtrace`](@ref) can be translated into [`StackFrame`](@ref) +s by passing them into [`StackTraces.lookup`](@ref): ```julia-repl julia> pointer = backtrace()[1]; diff --git a/doc/src/manual/strings.md b/doc/src/manual/strings.md index 8f7957c7250ed..2508e28b61177 100644 --- a/doc/src/manual/strings.md +++ b/doc/src/manual/strings.md @@ -22,7 +22,7 @@ There are a few noteworthy high-level features about Julia's strings: * The built-in concrete type used for strings (and string literals) in Julia is [`String`](@ref). This supports the full range of [Unicode](https://en.wikipedia.org/wiki/Unicode) characters via - the [UTF-8](https://en.wikipedia.org/wiki/UTF-8) encoding. (A [`transcode()`](@ref) function is + the [UTF-8](https://en.wikipedia.org/wiki/UTF-8) encoding. (A [`transcode`](@ref) function is provided to convert to/from other Unicode encodings.) * All string types are subtypes of the abstract type `AbstractString`, and external packages define additional `AbstractString` subtypes (e.g. for other encodings). If you define a function expecting @@ -72,9 +72,9 @@ julia> Char(120) 'x': ASCII/Unicode U+0078 (category Ll: Letter, lowercase) ``` -Not all integer values are valid Unicode code points, but for performance, the `Char()` conversion +Not all integer values are valid Unicode code points, but for performance, the `Char` conversion does not check that every character value is valid. If you want to check that each converted value -is a valid code point, use the [`isvalid()`](@ref) function: +is a valid code point, use the [`isvalid`](@ref) function: ```jldoctest julia> Char(0x110000) @@ -314,7 +314,7 @@ For example, the [LegacyStrings.jl](https://github.com/JuliaArchive/LegacyString implements `UTF16String` and `UTF32String` types. Additional discussion of other encodings and how to implement support for them is beyond the scope of this document for the time being. For further discussion of UTF-8 encoding issues, see the section below on [byte array literals](@ref man-byte-array-literals). -The [`transcode()`](@ref) function is provided to convert data between the various UTF-xx encodings, +The [`transcode`](@ref) function is provided to convert data between the various UTF-xx encodings, primarily for working with external data and libraries. ## Concatenation @@ -359,7 +359,7 @@ implies commutativity. ## [Interpolation](@id string-interpolation) Constructing strings using concatenation can become a bit cumbersome, however. To reduce the need for these -verbose calls to [`string()`](@ref) or repeated multiplications, Julia allows interpolation into string literals +verbose calls to [`string`](@ref) or repeated multiplications, Julia allows interpolation into string literals using `$`, as in Perl: ```jldoctest stringconcat @@ -378,7 +378,7 @@ julia> "1 + 2 = $(1 + 2)" "1 + 2 = 3" ``` -Both concatenation and string interpolation call [`string()`](@ref) to convert objects into string +Both concatenation and string interpolation call [`string`](@ref) to convert objects into string form. Most non-`AbstractString` objects are converted to strings closely corresponding to how they are entered as literal expressions: @@ -393,7 +393,7 @@ julia> "v: $v" "v: [1, 2, 3]" ``` -[`string()`](@ref) is the identity for `AbstractString` and `Char` values, so these are interpolated +[`string`](@ref) is the identity for `AbstractString` and `Char` values, so these are interpolated into strings as themselves, unquoted and unescaped: ```jldoctest @@ -474,7 +474,7 @@ julia> "1 + 2 = 3" == "1 + 2 = $(1 + 2)" true ``` -You can search for the index of a particular character using the [`search()`](@ref) function: +You can search for the index of a particular character using the [`search`](@ref) function: ```jldoctest julia> search("xylophone", 'x') @@ -500,7 +500,7 @@ julia> search("xylophone", 'o', 8) 0 ``` -You can use the [`contains()`](@ref) function to check if a substring is contained in a string: +You can use the [`contains`](@ref) function to check if a substring is contained in a string: ```jldoctest julia> contains("Hello, world.", "world") @@ -516,9 +516,9 @@ julia> contains("Xylophon", 'o') true ``` -The last example shows that [`contains()`](@ref) can also look for a character literal. +The last example shows that [`contains`](@ref) can also look for a character literal. -Two other handy string functions are [`repeat()`](@ref) and [`join()`](@ref): +Two other handy string functions are [`repeat`](@ref) and [`join`](@ref): ```jldoctest julia> repeat(".:Z:.", 10) @@ -535,7 +535,7 @@ Some other useful functions include: * [`i = start(str)`](@ref start) gives the first valid index at which a character can be found in `str` (typically 1). * [`c, j = next(str,i)`](@ref next) returns next character at or after the index `i` and the next valid - character index following that. With [`start()`](@ref) and [`endof()`](@ref), can be used to iterate + character index following that. With [`start`](@ref) and [`endof`](@ref), can be used to iterate through the characters in `str`. * [`ind2chr(str,i)`](@ref) gives the number of characters in `str` up to and including any at index `i`. @@ -569,7 +569,7 @@ julia> typeof(ans) Regex ``` -To check if a regex matches a string, use [`ismatch()`](@ref): +To check if a regex matches a string, use [`ismatch`](@ref): ```jldoctest julia> ismatch(r"^\s*(?:#|$)", "not a comment") @@ -579,10 +579,10 @@ julia> ismatch(r"^\s*(?:#|$)", "# a comment") true ``` -As one can see here, [`ismatch()`](@ref) simply returns true or false, indicating whether the +As one can see here, [`ismatch`](@ref) simply returns true or false, indicating whether the given regex matches the string or not. Commonly, however, one wants to know not just whether a string matched, but also *how* it matched. To capture this information about a match, use the -[`match()`](@ref) function instead: +[`match`](@ref) function instead: ```jldoctest julia> match(r"^\s*(?:#|$)", "not a comment") @@ -591,7 +591,7 @@ julia> match(r"^\s*(?:#|$)", "# a comment") RegexMatch("#") ``` -If the regular expression does not match the given string, [`match()`](@ref) returns `nothing` +If the regular expression does not match the given string, [`match`](@ref) returns `nothing` -- a special value that does not print anything at the interactive prompt. Other than not printing, it is a completely normal value and you can test for it programmatically: @@ -604,7 +604,7 @@ else end ``` -If a regular expression does match, the value returned by [`match()`](@ref) is a `RegexMatch` +If a regular expression does match, the value returned by [`match`](@ref) is a `RegexMatch` object. These objects record how the expression matches, including the substring that the pattern matches and any captured substrings, if there are any. This example only captures the portion of the substring that matches, but perhaps we want to capture any non-blank text after the comment @@ -615,7 +615,7 @@ julia> m = match(r"^\s*(?:#\s*(.*?)\s*$|$)", "# a comment ") RegexMatch("# a comment ", 1="a comment") ``` -When calling [`match()`](@ref), you have the option to specify an index at which to start the +When calling [`match`](@ref), you have the option to specify an index at which to start the search. For example: ```jldoctest @@ -706,7 +706,7 @@ julia> m[2] "45" ``` -Captures can be referenced in a substitution string when using [`replace()`](@ref) by using `\n` +Captures can be referenced in a substitution string when using [`replace`](@ref) by using `\n` to refer to the nth capture group and prefixing the substitution string with `s`. Capture group 0 refers to the entire match object. Named capture groups can be referenced in the substitution with `g`. For example: diff --git a/doc/src/manual/style-guide.md b/doc/src/manual/style-guide.md index 458cd5c37b5d0..07defaea4df67 100644 --- a/doc/src/manual/style-guide.md +++ b/doc/src/manual/style-guide.md @@ -111,8 +111,8 @@ end ``` The Julia standard library uses this convention throughout and contains examples of functions -with both copying and modifying forms (e.g., [`sort()`](@ref) and [`sort!()`](@ref)), and others -which are just modifying (e.g., [`push!()`](@ref), [`pop!()`](@ref), [`splice!()`](@ref)). It +with both copying and modifying forms (e.g., [`sort`](@ref) and [`sort!`](@ref)), and others +which are just modifying (e.g., [`push!`](@ref), [`pop!`](@ref), [`splice!`](@ref)). It is typical for such functions to also return the modified array for convenience. ## Avoid strange type `Union`s @@ -156,11 +156,11 @@ uses (e.g. `a[i]::Int`) than to try to pack many alternatives into one type. ## Use naming conventions consistent with Julia's `base/` * modules and type names use capitalization and camel case: `module SparseArrays`, `struct UnitRange`. - * functions are lowercase ([`maximum()`](@ref), [`convert()`](@ref)) and, when readable, with multiple - words squashed together ([`isequal()`](@ref), [`haskey()`](@ref)). When necessary, use underscores - as word separators. Underscores are also used to indicate a combination of concepts ([`remotecall_fetch()`](@ref) - as a more efficient implementation of `fetch(remotecall(...))`) or as modifiers ([`sum_kbn()`](@ref)). - * conciseness is valued, but avoid abbreviation ([`indexin()`](@ref) rather than `indxin()`) as + * functions are lowercase ([`maximum`](@ref), [`convert`](@ref)) and, when readable, with multiple + words squashed together ([`isequal`](@ref), [`haskey`](@ref)). When necessary, use underscores + as word separators. Underscores are also used to indicate a combination of concepts ([`remotecall_fetch`](@ref) + as a more efficient implementation of `fetch(remotecall(...))`) or as modifiers ([`sum_kbn`](@ref)). + * conciseness is valued, but avoid abbreviation ([`indexin`](@ref) rather than `indxin`) as it becomes difficult to remember whether and how particular words are abbreviated. If a function name requires multiple words, consider whether it might represent more than one @@ -235,7 +235,7 @@ with the "values" as subtypes. Be aware of when a macro could really be a function instead. -Calling [`eval()`](@ref) inside a macro is a particularly dangerous warning sign; it means the +Calling [`eval`](@ref) inside a macro is a particularly dangerous warning sign; it means the macro will only work when called at the top level. If such a macro is written as a function instead, it will naturally have access to the run-time values it needs. @@ -307,7 +307,7 @@ higher-level, Julia-friendly API. ## Be careful with type equality -You generally want to use [`isa()`](@ref) and [`<:`](@ref) for testing types, +You generally want to use [`isa`](@ref) and [`<:`](@ref) for testing types, not `==`. Checking types for exact equality typically only makes sense when comparing to a known concrete type (e.g. `T == Float64`), or if you *really, really* know what you're doing. diff --git a/doc/src/manual/types.md b/doc/src/manual/types.md index 5825d121ff81f..893c53a1b54b7 100644 --- a/doc/src/manual/types.md +++ b/doc/src/manual/types.md @@ -41,7 +41,7 @@ up front are: where the combination of static compilation with polymorphism makes this distinction significant. * Only values, not variables, have types -- variables are simply names bound to values. * Both abstract and concrete types can be parameterized by other types. They can also be parameterized - by symbols, by values of any type for which [`isbits()`](@ref) returns true (essentially, things + by symbols, by values of any type for which [`isbits`](@ref) returns true (essentially, things like numbers and bools that are stored like C types or structs with no pointers to other objects), and also by tuples thereof. Type parameters may be omitted when they do not need to be referenced or restricted. @@ -80,7 +80,7 @@ This allows a type assertion to be attached to any expression in-place. When appended to a variable on the left-hand side of an assignment, or as part of a `local` declaration, the `::` operator means something a bit different: it declares the variable to always have the specified type, like a type declaration in a statically-typed language such as C. Every value -assigned to the variable will be converted to the declared type using [`convert()`](@ref): +assigned to the variable will be converted to the declared type using [`convert`](@ref): ```jldoctest julia> function foo() @@ -334,7 +334,7 @@ Foo When a type is applied like a function it is called a *constructor*. Two constructors are generated automatically (these are called *default constructors*). One accepts any arguments and calls -[`convert()`](@ref) to convert them to the types of the fields, and the other accepts arguments +[`convert`](@ref) to convert them to the types of the fields, and the other accepts arguments that match the field types exactly. The reason both of these are generated is that this makes it easier to add new definitions without inadvertently replacing a default constructor. @@ -1111,7 +1111,7 @@ julia> isa(1, AbstractFloat) false ``` -The [`typeof()`](@ref) function, already used throughout the manual in examples, returns the type +The [`typeof`](@ref) function, already used throughout the manual in examples, returns the type of its argument. Since, as noted above, types are objects, they also have types, and we can ask what their types are: @@ -1139,7 +1139,7 @@ DataType `DataType` is its own type. -Another operation that applies to some types is [`supertype()`](@ref), which reveals a type's +Another operation that applies to some types is [`supertype`](@ref), which reveals a type's supertype. Only declared types (`DataType`) have unambiguous supertypes: ```jldoctest @@ -1156,7 +1156,7 @@ julia> supertype(Any) Any ``` -If you apply [`supertype()`](@ref) to other type objects (or non-type objects), a [`MethodError`](@ref) +If you apply [`supertype`](@ref) to other type objects (or non-type objects), a [`MethodError`](@ref) is raised: ```jldoctest @@ -1170,7 +1170,7 @@ Closest candidates are: ## Custom pretty-printing Often, one wants to customize how instances of a type are displayed. This is accomplished by -overloading the [`show()`](@ref) function. For example, suppose we define a type to represent +overloading the [`show`](@ref) function. For example, suppose we define a type to represent complex numbers in polar form: ```jldoctest polartype @@ -1202,7 +1202,7 @@ julia> Base.show(io::IO, z::Polar) = print(io, z.r, " * exp(", z.Θ, "im)") More fine-grained control over display of `Polar` objects is possible. In particular, sometimes one wants both a verbose multi-line printing format, used for displaying a single object in the REPL and other interactive environments, and also a more compact single-line format used for -[`print()`](@ref) or for displaying the object as part of another object (e.g. in an array). Although +[`print`](@ref) or for displaying the object as part of another object (e.g. in an array). Although by default the `show(io, z)` function is called in both cases, you can define a *different* multi-line format for displaying an object by overloading a three-argument form of `show` that takes the `text/plain` MIME type as its second argument (see [Multimedia I/O](@ref)), for example: @@ -1227,7 +1227,7 @@ julia> [Polar(3, 4.0), Polar(4.0,5.3)] where the single-line `show(io, z)` form is still used for an array of `Polar` values. Technically, the REPL calls `display(z)` to display the result of executing a line, which defaults to `show(STDOUT, MIME("text/plain"), z)`, -which in turn defaults to `show(STDOUT, z)`, but you should *not* define new [`display()`](@ref) +which in turn defaults to `show(STDOUT, z)`, but you should *not* define new [`display`](@ref) methods unless you are defining a new multimedia display handler (see [Multimedia I/O](@ref)). Moreover, you can also define `show` methods for other MIME types in order to enable richer display @@ -1405,7 +1405,7 @@ a single value of type `T` as an argument. ### Checking if a `Nullable` object has a value -You can check if a `Nullable` object has any value using [`isnull()`](@ref): +You can check if a `Nullable` object has any value using [`isnull`](@ref): ```jldoctest julia> isnull(Nullable{Float64}()) @@ -1417,7 +1417,7 @@ false ### Safely accessing the value of a `Nullable` object -You can safely access the value of a `Nullable` object using [`get()`](@ref): +You can safely access the value of a `Nullable` object using [`get`](@ref): ```jldoctest julia> get(Nullable{Float64}()) @@ -1430,12 +1430,12 @@ julia> get(Nullable(1.0)) ``` If the value is not present, as it would be for `Nullable{Float64}`, a [`NullException`](@ref) -error will be thrown. The error-throwing nature of the `get()` function ensures that any +error will be thrown. The error-throwing nature of the `get` function ensures that any attempt to access a missing value immediately fails. In cases for which a reasonable default value exists that could be used when a `Nullable` object's value turns out to be missing, you can provide this default value as a second argument -to `get()`: +to `get`: ```jldoctest julia> get(Nullable{Float64}(), 0.0) @@ -1446,15 +1446,15 @@ julia> get(Nullable(1.0), 0.0) ``` !!! tip - Make sure the type of the default value passed to `get()` and that of the `Nullable` - object match to avoid type instability, which could hurt performance. Use [`convert()`](@ref) + Make sure the type of the default value passed to `get` and that of the `Nullable` + object match to avoid type instability, which could hurt performance. Use [`convert`](@ref) manually if needed. ### Performing operations on `Nullable` objects `Nullable` objects represent values that are possibly missing, and it is possible to write all code using these objects by first testing to see if -the value is missing with [`isnull()`](@ref), and then doing an appropriate +the value is missing with [`isnull`](@ref), and then doing an appropriate action. However, there are some common use cases where the code could be more concise or clear by using a higher-order function. @@ -1503,7 +1503,7 @@ example, we will assume that the best solution is to propagate the missing values forward; that is, if any input is missing, we simply produce a missing output. -The `broadcast()` function makes this task easy; we can simply pass the +The `broadcast` function makes this task easy; we can simply pass the `root` function we wrote to `broadcast`: ```jldoctest nullableroot @@ -1518,9 +1518,9 @@ Nullable{Float64}() ``` If one or more of the inputs is missing, then the output of -`broadcast()` will be missing. +`broadcast` will be missing. -There exists special syntactic sugar for the `broadcast()` function +There exists special syntactic sugar for the `broadcast` function using a dot notation: ```jldoctest nullableroot @@ -1528,7 +1528,7 @@ julia> root.(Nullable(1), Nullable(-9), Nullable(20)) Nullable{Float64}(5.0) ``` -In particular, the regular arithmetic operators can be `broadcast()` +In particular, the regular arithmetic operators can be `broadcast` conveniently using `.`-prefixed operators: ```jldoctest diff --git a/doc/src/manual/variables-and-scoping.md b/doc/src/manual/variables-and-scoping.md index d2f511a0dd965..f56cd1c49d51d 100644 --- a/doc/src/manual/variables-and-scoping.md +++ b/doc/src/manual/variables-and-scoping.md @@ -304,8 +304,8 @@ julia> odd(3) true ``` -Julia provides built-in, efficient functions to test for oddness and evenness called [`iseven()`](@ref) -and [`isodd()`](@ref) so the above definitions should only be taken as examples. +Julia provides built-in, efficient functions to test for oddness and evenness called [`iseven`](@ref) +and [`isodd`](@ref) so the above definitions should only be taken as examples. ### Hard vs. Soft Local Scope diff --git a/doc/src/stdlib/collections.md b/doc/src/stdlib/collections.md index 9b611ed598174..fd6bbf38e9171 100644 --- a/doc/src/stdlib/collections.md +++ b/doc/src/stdlib/collections.md @@ -2,7 +2,7 @@ ## [Iteration](@id lib-collections-iteration) -Sequential iteration is implemented by the methods [`start()`](@ref), [`done()`](@ref), and [`next()`](@ref). +Sequential iteration is implemented by the methods [`start`](@ref), [`done`](@ref), and [`next`](@ref). The general `for` loop: ```julia @@ -161,8 +161,8 @@ Partially implemented by: ## Associative Collections -[`Dict`](@ref) is the standard associative collection. Its implementation uses [`hash()`](@ref) -as the hashing function for the key, and [`isequal()`](@ref) to determine equality. Define these +[`Dict`](@ref) is the standard associative collection. Its implementation uses [`hash`](@ref) +as the hashing function for the key, and [`isequal`](@ref) to determine equality. Define these two functions for custom types to override how they are stored in a hash table. [`ObjectIdDict`](@ref) is a special hash table where the keys are always object identities. @@ -170,7 +170,7 @@ two functions for custom types to override how they are stored in a hash table. [`WeakKeyDict`](@ref) is a hash table implementation where the keys are weak references to objects, and thus may be garbage collected even when referenced in a hash table. -[`Dict`](@ref)s can be created by passing pair objects constructed with `=>()` to a [`Dict`](@ref) +[`Dict`](@ref)s can be created by passing pair objects constructed with `=>` to a [`Dict`](@ref) constructor: `Dict("A"=>1, "B"=>2)`. This call will attempt to infer type information from the keys and values (i.e. this example creates a `Dict{String, Int64}`). To explicitly specify types use the syntax `Dict{KeyType,ValueType}(...)`. For example, `Dict{String,Int32}("A"=>1, "B"=>2)`. diff --git a/doc/src/stdlib/libdl.md b/doc/src/stdlib/libdl.md index 621eaaeaa793d..9956624ad7f95 100644 --- a/doc/src/stdlib/libdl.md +++ b/doc/src/stdlib/libdl.md @@ -1,6 +1,6 @@ # Dynamic Linker -The names in `Base.Libdl` are not exported and need to be called e.g. as `Libdl.dlopen()`. +The names in `Base.Libdl` are not exported and need to be called e.g. as `Libdl.dlopen`. ```@docs Base.Libdl.dlopen diff --git a/doc/src/stdlib/test.md b/doc/src/stdlib/test.md index baf78ae1c16b6..5f8415ee79b09 100644 --- a/doc/src/stdlib/test.md +++ b/doc/src/stdlib/test.md @@ -23,7 +23,7 @@ see if your code is correct by checking that the results are what you expect. It to ensure your code still works after you make changes, and can be used when developing as a way of specifying the behaviors your code should have when complete. -Simple unit testing can be performed with the `@test()` and `@test_throws()` macros: +Simple unit testing can be performed with the `@test` and `@test_throws` macros: ```@docs Base.Test.@test @@ -60,7 +60,7 @@ ERROR: There was an error during testing ``` If the condition could not be evaluated because an exception was thrown, which occurs in this -case because `length()` is not defined for symbols, an `Error` object is returned and an exception +case because `length` is not defined for symbols, an `Error` object is returned and an exception is thrown: ```julia-repl @@ -79,7 +79,7 @@ Error During Test ERROR: There was an error during testing ``` -If we expect that evaluating an expression *should* throw an exception, then we can use `@test_throws()` +If we expect that evaluating an expression *should* throw an exception, then we can use `@test_throws` to check that this occurs: ```jldoctest testfoo @@ -95,7 +95,7 @@ of inputs. In the event a test fails, the default behavior is to throw an except However, it is normally preferable to run the rest of the tests first to get a better picture of how many errors there are in the code being tested. -The `@testset()` macro can be used to group tests into *sets*. All the tests in a test set will +The `@testset` macro can be used to group tests into *sets*. All the tests in a test set will be run, and at the end of the test set a summary will be printed. If any of the tests failed, or could not be evaluated due to an error, the test set will then throw a `TestSetException`. @@ -167,7 +167,7 @@ ERROR: Some tests did not pass: 3 passed, 1 failed, 0 errored, 0 broken. As calculations on floating-point values can be imprecise, you can perform approximate equality checks using either `@test a ≈ b` (where `≈`, typed via tab completion of `\approx`, is the -[`isapprox()`](@ref) function) or use [`isapprox()`](@ref) directly. +[`isapprox`](@ref) function) or use [`isapprox`](@ref) directly. ```jldoctest julia> @test 1 ≈ 0.999999999 @@ -188,7 +188,7 @@ Base.Test.@test_nowarn ## Broken Tests -If a test fails consistently it can be changed to use the `@test_broken()` macro. This will denote +If a test fails consistently it can be changed to use the `@test_broken` macro. This will denote the test as `Broken` if the test continues to fail and alerts the user via an `Error` if the test succeeds. @@ -196,7 +196,7 @@ succeeds. Base.Test.@test_broken ``` -`@test_skip()` is also available to skip a test without evaluation, but counting the skipped test +`@test_skip` is also available to skip a test without evaluation, but counting the skipped test in the test set reporting. The test will not run but gives a `Broken` `Result`. ```@docs From e2ea8951543522f5276bc7e198f51e71661f5a35 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Fri, 21 Jul 2017 16:12:35 -0400 Subject: [PATCH 257/324] add `pairs`, and `keys`/`values` for arrays This allows more uniform treatment of indexed collections. --- base/abstractarray.jl | 20 ++++++++++++++----- base/associative.jl | 19 ++++++++++++++++-- base/deprecated.jl | 5 +++++ base/essentials.jl | 10 ++++++++++ base/exports.jl | 1 + base/iterators.jl | 37 ++++++++++++++++++----------------- base/number.jl | 1 + base/process.jl | 2 +- base/strings/basic.jl | 2 +- base/test.jl | 2 +- base/tuple.jl | 4 ++-- doc/src/stdlib/collections.md | 1 + test/abstractarray.jl | 9 +++++++++ test/arrayops.jl | 8 ++++---- 14 files changed, 87 insertions(+), 34 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index e939099865fae..551cebd3b90ae 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -69,9 +69,9 @@ function indices(A) map(OneTo, size(A)) end -# Performance optimization: get rid of a branch on `d` in `indices(A, -# d)` for d=1. 1d arrays are heavily used, and the first dimension -# comes up in other applications. +# Performance optimization: get rid of a branch on `d` in `indices(A, d)` +# for d=1. 1d arrays are heavily used, and the first dimension comes up +# in other applications. indices1(A::AbstractArray{<:Any,0}) = OneTo(1) indices1(A::AbstractArray) = (@_inline_meta; indices(A)[1]) indices1(iter) = OneTo(length(iter)) @@ -103,6 +103,10 @@ julia> extrema(b) """ linearindices(A) = (@_inline_meta; OneTo(_length(A))) linearindices(A::AbstractVector) = (@_inline_meta; indices1(A)) + +keys(a::AbstractArray) = CartesianRange(indices(a)) +keys(a::AbstractVector) = linearindices(a) + eltype(::Type{<:AbstractArray{E}}) where {E} = E elsize(::AbstractArray{T}) where {T} = sizeof(T) @@ -756,8 +760,11 @@ start(A::AbstractArray) = (@_inline_meta; itr = eachindex(A); (itr, start(itr))) next(A::AbstractArray, i) = (@_propagate_inbounds_meta; (idx, s) = next(i[1], i[2]); (A[idx], (i[1], s))) done(A::AbstractArray, i) = (@_propagate_inbounds_meta; done(i[1], i[2])) +# `eachindex` is mostly an optimization of `keys` +eachindex(itrs...) = keys(itrs...) + # eachindex iterates over all indices. IndexCartesian definitions are later. -eachindex(A::Union{Number,AbstractVector}) = (@_inline_meta(); indices1(A)) +eachindex(A::AbstractVector) = (@_inline_meta(); indices1(A)) """ eachindex(A...) @@ -826,6 +833,9 @@ end isempty(a::AbstractArray) = (_length(a) == 0) +# keys with an IndexStyle +keys(s::IndexStyle, A::AbstractArray, B::AbstractArray...) = eachindex(s, A, B...) + ## Conversions ## convert(::Type{AbstractArray{T,N}}, A::AbstractArray{T,N}) where {T,N } = A @@ -1739,7 +1749,7 @@ _sub2ind_vec(i) = () function ind2sub(inds::Union{DimsInteger{N},Indices{N}}, ind::AbstractVector{<:Integer}) where N M = length(ind) t = ntuple(n->similar(ind),Val(N)) - for (i,idx) in enumerate(IndexLinear(), ind) + for (i,idx) in pairs(IndexLinear(), ind) sub = ind2sub(inds, idx) for j = 1:N t[j][i] = sub[j] diff --git a/base/associative.jl b/base/associative.jl index 44ecd6faed3d5..18c4de2ec7915 100644 --- a/base/associative.jl +++ b/base/associative.jl @@ -69,11 +69,18 @@ end in(k, v::KeyIterator) = get(v.dict, k, secret_table_token) !== secret_table_token +""" + keys(iterator) + +For an iterator or collection that has keys and values (e.g. arrays and dictionaries), +return an iterator over the keys. +""" +function keys end """ keys(a::Associative) -Return an iterator over all keys in a collection. +Return an iterator over all keys in an associative collection. `collect(keys(a))` returns an array of keys. Since the keys are stored internally in a hash table, the order in which they are returned may vary. @@ -94,7 +101,6 @@ julia> collect(keys(a)) ``` """ keys(a::Associative) = KeyIterator(a) -eachindex(a::Associative) = KeyIterator(a) """ values(a::Associative) @@ -121,6 +127,15 @@ julia> collect(values(a)) """ values(a::Associative) = ValueIterator(a) +""" + pairs(collection) + +Return an iterator over `key => value` pairs for any +collection that maps a set of keys to a set of values. +This includes arrays, where the keys are the array indices. +""" +pairs(collection) = Generator(=>, keys(collection), values(collection)) + function copy(a::Associative) b = similar(a) for (k,v) in a diff --git a/base/deprecated.jl b/base/deprecated.jl index 8aaeeaa5c852f..26ed3243c4ee0 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1733,6 +1733,11 @@ end @deprecate promote_noncircular promote false +import .Iterators.enumerate + +@deprecate enumerate(i::IndexLinear, A::AbstractArray) pairs(i, A) +@deprecate enumerate(i::IndexCartesian, A::AbstractArray) pairs(i, A) + # END 0.7 deprecations # BEGIN 1.0 deprecations diff --git a/base/essentials.jl b/base/essentials.jl index fcb94f19822f8..bef327f36afc6 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -676,3 +676,13 @@ false ``` """ isempty(itr) = done(itr, start(itr)) + +""" + values(iterator) + +For an iterator or collection that has keys and values, return an iterator +over the values. +This function simply returns its argument by default, since the elements +of a general iterator are normally considered its "values". +""" +values(itr) = itr diff --git a/base/exports.jl b/base/exports.jl index addd53d810f7f..608c5f46d0d8f 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -688,6 +688,7 @@ export mapreducedim, merge!, merge, + pairs, #pop!, #push!, reduce, diff --git a/base/iterators.jl b/base/iterators.jl index d3671af58bb25..9a401adfb2e00 100644 --- a/base/iterators.jl +++ b/base/iterators.jl @@ -2,7 +2,7 @@ module Iterators -import Base: start, done, next, isempty, length, size, eltype, iteratorsize, iteratoreltype, indices, ndims +import Base: start, done, next, isempty, length, size, eltype, iteratorsize, iteratoreltype, indices, ndims, pairs using Base: tail, tuple_type_head, tuple_type_tail, tuple_type_cons, SizeUnknown, HasLength, HasShape, IsInfinite, EltypeUnknown, HasEltype, OneTo, @propagate_inbounds @@ -78,14 +78,16 @@ struct IndexValue{I,A<:AbstractArray} end """ - enumerate(IndexLinear(), A) - enumerate(IndexCartesian(), A) - enumerate(IndexStyle(A), A) + pairs(IndexLinear(), A) + pairs(IndexCartesian(), A) + pairs(IndexStyle(A), A) An iterator that accesses each element of the array `A`, returning -`(i, x)`, where `i` is the index for the element and `x = A[i]`. This -is similar to `enumerate(A)`, except `i` will always be a valid index -for `A`. +`i => x`, where `i` is the index for the element and `x = A[i]`. +Identical to `pairs(A)`, except that the style of index can be selected. +Also similar to `enumerate(A)`, except `i` will be a valid index +for `A`, while `enumerate` always counts from 1 regardless of the indices +of `A`. Specifying `IndexLinear()` ensures that `i` will be an integer; specifying `IndexCartesian()` ensures that `i` will be a @@ -96,7 +98,7 @@ been defined as the native indexing style for array `A`. ```jldoctest julia> A = ["a" "d"; "b" "e"; "c" "f"]; -julia> for (index, value) in enumerate(IndexStyle(A), A) +julia> for (index, value) in pairs(IndexStyle(A), A) println("\$index \$value") end 1 a @@ -108,7 +110,7 @@ julia> for (index, value) in enumerate(IndexStyle(A), A) julia> S = view(A, 1:2, :); -julia> for (index, value) in enumerate(IndexStyle(S), S) +julia> for (index, value) in pairs(IndexStyle(S), S) println("\$index \$value") end CartesianIndex{2}((1, 1)) a @@ -117,15 +119,14 @@ CartesianIndex{2}((1, 2)) d CartesianIndex{2}((2, 2)) e ``` -Note that `enumerate(A)` returns `i` as a *counter* (always starting -at 1), whereas `enumerate(IndexLinear(), A)` returns `i` as an *index* -(starting at the first linear index of `A`, which may or may not be -1). - See also: [`IndexStyle`](@ref), [`indices`](@ref). """ -enumerate(::IndexLinear, A::AbstractArray) = IndexValue(A, linearindices(A)) -enumerate(::IndexCartesian, A::AbstractArray) = IndexValue(A, CartesianRange(indices(A))) +pairs(::IndexLinear, A::AbstractArray) = IndexValue(A, linearindices(A)) +pairs(::IndexCartesian, A::AbstractArray) = IndexValue(A, CartesianRange(indices(A))) + +# faster than zip(keys(a), values(a)) for arrays +pairs(A::AbstractArray) = pairs(IndexCartesian(), A) +pairs(A::AbstractVector) = pairs(IndexLinear(), A) length(v::IndexValue) = length(v.itr) indices(v::IndexValue) = indices(v.itr) @@ -134,11 +135,11 @@ size(v::IndexValue) = size(v.itr) @propagate_inbounds function next(v::IndexValue, state) indx, n = next(v.itr, state) item = v.data[indx] - (indx, item), n + (indx => item), n end @inline done(v::IndexValue, state) = done(v.itr, state) -eltype(::Type{IndexValue{I,A}}) where {I,A} = Tuple{eltype(I), eltype(A)} +eltype(::Type{IndexValue{I,A}}) where {I,A} = Pair{eltype(I), eltype(A)} iteratorsize(::Type{IndexValue{I}}) where {I} = iteratorsize(I) iteratoreltype(::Type{IndexValue{I}}) where {I} = iteratoreltype(I) diff --git a/base/number.jl b/base/number.jl index 4fb47f8c54cf3..c1c22a5866423 100644 --- a/base/number.jl +++ b/base/number.jl @@ -49,6 +49,7 @@ ndims(::Type{<:Number}) = 0 length(x::Number) = 1 endof(x::Number) = 1 iteratorsize(::Type{<:Number}) = HasShape() +keys(::Number) = OneTo(1) getindex(x::Number) = x function getindex(x::Number, i::Integer) diff --git a/base/process.jl b/base/process.jl index 781e1d3ea1f94..cc188a084e675 100644 --- a/base/process.jl +++ b/base/process.jl @@ -822,7 +822,7 @@ wait(x::ProcessChain) = for p in x.processes; wait(p); end show(io::IO, p::Process) = print(io, "Process(", p.cmd, ", ", process_status(p), ")") # allow the elements of the Cmd to be accessed as an array or iterator -for f in (:length, :endof, :start, :eachindex, :eltype, :first, :last) +for f in (:length, :endof, :start, :keys, :eltype, :first, :last) @eval $f(cmd::Cmd) = $f(cmd.exec) end for f in (:next, :done, :getindex) diff --git a/base/strings/basic.jl b/base/strings/basic.jl index e397e782c349a..408d07504dc2c 100644 --- a/base/strings/basic.jl +++ b/base/strings/basic.jl @@ -316,7 +316,7 @@ unsafe_chr2ind(s::AbstractString, i::Integer) = map_chr_ind(s, i, first, last) struct EachStringIndex{T<:AbstractString} s::T end -eachindex(s::AbstractString) = EachStringIndex(s) +keys(s::AbstractString) = EachStringIndex(s) length(e::EachStringIndex) = length(e.s) start(e::EachStringIndex) = start(e.s) diff --git a/base/test.jl b/base/test.jl index 19255d6d8e11a..f118d105cebbc 100644 --- a/base/test.jl +++ b/base/test.jl @@ -1415,7 +1415,7 @@ end GenericArray{T}(args...) where {T} = GenericArray(Array{T}(args...)) GenericArray{T,N}(args...) where {T,N} = GenericArray(Array{T,N}(args...)) -Base.eachindex(a::GenericArray) = eachindex(a.a) +Base.keys(a::GenericArray) = keys(a.a) Base.indices(a::GenericArray) = indices(a.a) Base.length(a::GenericArray) = length(a.a) Base.size(a::GenericArray) = size(a.a) diff --git a/base/tuple.jl b/base/tuple.jl index 961107884466f..0f4ed589fe19d 100644 --- a/base/tuple.jl +++ b/base/tuple.jl @@ -38,9 +38,9 @@ start(t::Tuple) = 1 done(t::Tuple, i::Int) = (length(t) < i) next(t::Tuple, i::Int) = (t[i], i+1) -eachindex(t::Tuple) = 1:length(t) +keys(t::Tuple) = 1:length(t) -function eachindex(t::Tuple, t2::Tuple...) +function keys(t::Tuple, t2::Tuple...) @_inline_meta 1:_maxlength(t, t2...) end diff --git a/doc/src/stdlib/collections.md b/doc/src/stdlib/collections.md index fd6bbf38e9171..e83303f7ab89a 100644 --- a/doc/src/stdlib/collections.md +++ b/doc/src/stdlib/collections.md @@ -196,6 +196,7 @@ Base.delete! Base.pop!(::Any, ::Any, ::Any) Base.keys Base.values +Base.pairs Base.merge Base.merge!(::Associative, ::Associative...) Base.merge!(::Function, ::Associative, ::Associative...) diff --git a/test/abstractarray.jl b/test/abstractarray.jl index 7baa72f37f378..057b115a164ff 100644 --- a/test/abstractarray.jl +++ b/test/abstractarray.jl @@ -835,3 +835,12 @@ end @testset "checkbounds_indices method ambiguities #20989" begin @test Base.checkbounds_indices(Bool, (1:1,), ([CartesianIndex(1)],)) end + +# keys, values, pairs +for A in (rand(2), rand(2,3)) + local A + for (i, v) in pairs(A) + @test A[i] == v + end + @test collect(values(A)) == collect(A) +end diff --git a/test/arrayops.jl b/test/arrayops.jl index 0fe17e2561213..39b914a6a0e55 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -1214,10 +1214,10 @@ end @testset "eachindexvalue" begin A14 = [11 13; 12 14] R = CartesianRange(indices(A14)) - @test [a for (a,b) in enumerate(IndexLinear(), A14)] == [1,2,3,4] - @test [a for (a,b) in enumerate(IndexCartesian(), A14)] == vec(collect(R)) - @test [b for (a,b) in enumerate(IndexLinear(), A14)] == [11,12,13,14] - @test [b for (a,b) in enumerate(IndexCartesian(), A14)] == [11,12,13,14] + @test [a for (a,b) in pairs(IndexLinear(), A14)] == [1,2,3,4] + @test [a for (a,b) in pairs(IndexCartesian(), A14)] == vec(collect(R)) + @test [b for (a,b) in pairs(IndexLinear(), A14)] == [11,12,13,14] + @test [b for (a,b) in pairs(IndexCartesian(), A14)] == [11,12,13,14] end @testset "reverse" begin From ef2fd5fdb7ad0da25fc327c62f24d049015f6dea Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Fri, 21 Jul 2017 16:46:43 -0400 Subject: [PATCH 258/324] use `pairs` in `findmin` and `findmax`, supporting all indexable collections return `CartesianIndex` for n-d arrays in findmin, findmax, indmin, indmax more compact printing of `CartesianIndex` change sparse `_findr` macro to a function --- NEWS.md | 4 ++ base/array.jl | 24 +++++------ base/multidimensional.jl | 3 +- base/reducedim.jl | 24 +++++------ base/sparse/sparsematrix.jl | 79 ++++++++++++++++++------------------- test/arrayops.jl | 7 +++- test/reducedim.jl | 52 ++++++++++++------------ test/sparse/sparse.jl | 56 +++++++++++++------------- 8 files changed, 130 insertions(+), 119 deletions(-) diff --git a/NEWS.md b/NEWS.md index 97deb4df01f46..e68b1719e960a 100644 --- a/NEWS.md +++ b/NEWS.md @@ -194,6 +194,10 @@ This section lists changes that do not have deprecation warnings. This avoids stack overflows in the common case of definitions like `f(x, y) = f(promote(x, y)...)` ([#22801]). + * `findmin`, `findmax`, `indmin`, and `indmax` used to always return linear indices. + They now return `CartesianIndex`es for all but 1-d arrays, and in general return + the `keys` of indexed collections (e.g. dictionaries) ([#22907]). + Library improvements -------------------- diff --git a/base/array.jl b/base/array.jl index 61ef1fbd63f29..9f43321ac8a38 100644 --- a/base/array.jl +++ b/base/array.jl @@ -2072,13 +2072,13 @@ function findmax(a) if isempty(a) throw(ArgumentError("collection must be non-empty")) end - s = start(a) - mi = i = 1 - m, s = next(a, s) - while !done(a, s) + p = pairs(a) + s = start(p) + (mi, m), s = next(p, s) + i = mi + while !done(p, s) m != m && break - ai, s = next(a, s) - i += 1 + (i, ai), s = next(p, s) if ai != ai || isless(m, ai) m = ai mi = i @@ -2113,13 +2113,13 @@ function findmin(a) if isempty(a) throw(ArgumentError("collection must be non-empty")) end - s = start(a) - mi = i = 1 - m, s = next(a, s) - while !done(a, s) + p = pairs(a) + s = start(p) + (mi, m), s = next(p, s) + i = mi + while !done(p, s) m != m && break - ai, s = next(a, s) - i += 1 + (i, ai), s = next(p, s) if ai != ai || isless(ai, m) m = ai mi = i diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 2988daf2beddc..2693333f063b7 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -4,7 +4,7 @@ module IteratorsMD import Base: eltype, length, size, start, done, next, first, last, in, getindex, setindex!, IndexStyle, min, max, zero, one, isless, eachindex, - ndims, iteratorsize, convert + ndims, iteratorsize, convert, show import Base: +, -, * import Base: simd_outer_range, simd_inner_length, simd_index @@ -80,6 +80,7 @@ module IteratorsMD @inline _flatten(i, I...) = (i, _flatten(I...)...) @inline _flatten(i::CartesianIndex, I...) = (i.I..., _flatten(I...)...) CartesianIndex(index::Tuple{Vararg{Union{Integer, CartesianIndex}}}) = CartesianIndex(index...) + show(io::IO, i::CartesianIndex) = (print(io, "CartesianIndex"); show(io, i.I)) # length length(::CartesianIndex{N}) where {N} = N diff --git a/base/reducedim.jl b/base/reducedim.jl index 2bd52371d5529..b2bee574032e7 100644 --- a/base/reducedim.jl +++ b/base/reducedim.jl @@ -635,7 +635,9 @@ function findminmax!(f, Rval, Rind, A::AbstractArray{T,N}) where {T,N} # Otherwise, keep the result in Rval/Rind so that we traverse A in storage order. indsAt, indsRt = safe_tail(indices(A)), safe_tail(indices(Rval)) keep, Idefault = Broadcast.shapeindexer(indsAt, indsRt) - k = 0 + ks = keys(A) + k, kss = next(ks, start(ks)) + zi = zero(eltype(ks)) if reducedim1(Rval, A) i1 = first(indices1(Rval)) @inbounds for IA in CartesianRange(indsAt) @@ -643,12 +645,12 @@ function findminmax!(f, Rval, Rind, A::AbstractArray{T,N}) where {T,N} tmpRv = Rval[i1,IR] tmpRi = Rind[i1,IR] for i in indices(A,1) - k += 1 tmpAv = A[i,IA] - if tmpRi == 0 || (tmpRv == tmpRv && (tmpAv != tmpAv || f(tmpAv, tmpRv))) + if tmpRi == zi || (tmpRv == tmpRv && (tmpAv != tmpAv || f(tmpAv, tmpRv))) tmpRv = tmpAv tmpRi = k end + k, kss = next(ks, kss) end Rval[i1,IR] = tmpRv Rind[i1,IR] = tmpRi @@ -657,14 +659,14 @@ function findminmax!(f, Rval, Rind, A::AbstractArray{T,N}) where {T,N} @inbounds for IA in CartesianRange(indsAt) IR = Broadcast.newindex(IA, keep, Idefault) for i in indices(A, 1) - k += 1 tmpAv = A[i,IA] tmpRv = Rval[i,IR] tmpRi = Rind[i,IR] - if tmpRi == 0 || (tmpRv == tmpRv && (tmpAv != tmpAv || f(tmpAv, tmpRv))) + if tmpRi == zi || (tmpRv == tmpRv && (tmpAv != tmpAv || f(tmpAv, tmpRv))) Rval[i,IR] = tmpAv Rind[i,IR] = k end + k, kss = next(ks, kss) end end end @@ -680,7 +682,7 @@ dimensions of `rval` and `rind`, and store the results in `rval` and `rind`. """ function findmin!(rval::AbstractArray, rind::AbstractArray, A::AbstractArray; init::Bool=true) - findminmax!(isless, init && !isempty(A) ? fill!(rval, first(A)) : rval, fill!(rind,0), A) + findminmax!(isless, init && !isempty(A) ? fill!(rval, first(A)) : rval, fill!(rind,zero(eltype(keys(A)))), A) end """ @@ -709,10 +711,10 @@ function findmin(A::AbstractArray{T}, region) where T if prod(map(length, reduced_indices(A, region))) != 0 throw(ArgumentError("collection slices must be non-empty")) end - (similar(A, ri), similar(dims->zeros(Int, dims), ri)) + (similar(A, ri), similar(dims->zeros(eltype(keys(A)), dims), ri)) else findminmax!(isless, fill!(similar(A, ri), first(A)), - similar(dims->zeros(Int, dims), ri), A) + similar(dims->zeros(eltype(keys(A)), dims), ri), A) end end @@ -727,7 +729,7 @@ dimensions of `rval` and `rind`, and store the results in `rval` and `rind`. """ function findmax!(rval::AbstractArray, rind::AbstractArray, A::AbstractArray; init::Bool=true) - findminmax!(isgreater, init && !isempty(A) ? fill!(rval, first(A)) : rval, fill!(rind,0), A) + findminmax!(isgreater, init && !isempty(A) ? fill!(rval, first(A)) : rval, fill!(rind,zero(eltype(keys(A)))), A) end """ @@ -756,10 +758,10 @@ function findmax(A::AbstractArray{T}, region) where T if prod(map(length, reduced_indices(A, region))) != 0 throw(ArgumentError("collection slices must be non-empty")) end - similar(A, ri), similar(dims->zeros(Int, dims), ri) + similar(A, ri), similar(dims->zeros(eltype(keys(A)), dims), ri) else findminmax!(isgreater, fill!(similar(A, ri), first(A)), - similar(dims->zeros(Int, dims), ri), A) + similar(dims->zeros(eltype(keys(A)), dims), ri), A) end end diff --git a/base/sparse/sparsematrix.jl b/base/sparse/sparsematrix.jl index fbf14eec0ba55..5d27cc316c31e 100644 --- a/base/sparse/sparsematrix.jl +++ b/base/sparse/sparsematrix.jl @@ -1820,7 +1820,7 @@ function _findz(A::SparseMatrixCSC{Tv,Ti}, rows=1:A.m, cols=1:A.n) where {Tv,Ti} row = 0 rowmin = rows[1]; rowmax = rows[end] allrows = (rows == 1:A.m) - @inbounds for col in cols + @inbounds for col in cols r1::Int = colptr[col] r2::Int = colptr[col+1] - 1 if !allrows && (r1 <= r2) @@ -1828,93 +1828,92 @@ function _findz(A::SparseMatrixCSC{Tv,Ti}, rows=1:A.m, cols=1:A.n) where {Tv,Ti} (r1 <= r2 ) && (r2 = searchsortedlast(rowval, rowmax, r1, r2, Forward)) end row = rowmin - while (r1 <= r2) && (row == rowval[r1]) && (nzval[r1] != zval) r1 += 1 row += 1 end - (row <= rowmax) && (return sub2ind(size(A), row, col)) + (row <= rowmax) && (return CartesianIndex(row, col)) end - return 0 + return CartesianIndex(0, 0) end -macro _findr(op, A, region, Tv, Ti) - esc(quote - N = nnz($A) - L = length($A) +function _findr(op, A, region, Tv) + Ti = eltype(keys(A)) + i1 = first(keys(A)) + N = nnz(A) + L = length(A) if L == 0 - if prod(map(length, Base.reduced_indices($A, $region))) != 0 + if prod(map(length, Base.reduced_indices(A, region))) != 0 throw(ArgumentError("array slices must be non-empty")) else - ri = Base.reduced_indices0($A, $region) - return (similar($A, ri), similar(dims->zeros(Int, dims), ri)) + ri = Base.reduced_indices0(A, region) + return (similar(A, ri), similar(dims->zeros(Ti, dims), ri)) end end - colptr = $A.colptr; rowval = $A.rowval; nzval = $A.nzval; m = $A.m; n = $A.n - zval = zero($Tv) - szA = size($A) + colptr = A.colptr; rowval = A.rowval; nzval = A.nzval; m = A.m; n = A.n + zval = zero(Tv) + szA = size(A) - if $region == 1 || $region == (1,) - (N == 0) && (return (fill(zval,1,n), fill(convert($Ti,1),1,n))) - S = Vector{$Tv}(n); I = Vector{$Ti}(n) + if region == 1 || region == (1,) + (N == 0) && (return (fill(zval,1,n), fill(i1,1,n))) + S = Vector{Tv}(n); I = Vector{Ti}(n) @inbounds for i = 1 : n - Sc = zval; Ic = _findz($A, 1:m, i:i) - if Ic == 0 + Sc = zval; Ic = _findz(A, 1:m, i:i) + if Ic == CartesianIndex(0, 0) j = colptr[i] - Ic = sub2ind(szA, rowval[j], i) + Ic = CartesianIndex(rowval[j], i) Sc = nzval[j] end for j = colptr[i] : colptr[i+1]-1 - if ($op)(nzval[j], Sc) + if op(nzval[j], Sc) Sc = nzval[j] - Ic = sub2ind(szA, rowval[j], i) + Ic = CartesianIndex(rowval[j], i) end end S[i] = Sc; I[i] = Ic end return(reshape(S,1,n), reshape(I,1,n)) - elseif $region == 2 || $region == (2,) - (N == 0) && (return (fill(zval,m,1), fill(convert($Ti,1),m,1))) - S = Vector{$Tv}(m); I = Vector{$Ti}(m) + elseif region == 2 || region == (2,) + (N == 0) && (return (fill(zval,m,1), fill(i1,m,1))) + S = Vector{Tv}(m); I = Vector{Ti}(m) @inbounds for row in 1:m - S[row] = zval; I[row] = _findz($A, row:row, 1:n) - if I[row] == 0 - I[row] = sub2ind(szA, row, 1) + S[row] = zval; I[row] = _findz(A, row:row, 1:n) + if I[row] == CartesianIndex(0, 0) + I[row] = CartesianIndex(row, 1) S[row] = A[row,1] end end @inbounds for i = 1 : n, j = colptr[i] : colptr[i+1]-1 row = rowval[j] - if ($op)(nzval[j], S[row]) + if op(nzval[j], S[row]) S[row] = nzval[j] - I[row] = sub2ind(szA, row, i) + I[row] = CartesianIndex(row, i) end end return (reshape(S,m,1), reshape(I,m,1)) - elseif $region == (1,2) - (N == 0) && (return (fill(zval,1,1), fill(convert($Ti,1),1,1))) - hasz = nnz($A) != length($A) + elseif region == (1,2) + (N == 0) && (return (fill(zval,1,1), fill(i1,1,1))) + hasz = nnz(A) != length(A) Sv = hasz ? zval : nzval[1] - Iv::($Ti) = hasz ? _findz($A) : 1 - @inbounds for i = 1 : $A.n, j = colptr[i] : (colptr[i+1]-1) - if ($op)(nzval[j], Sv) + Iv::(Ti) = hasz ? _findz(A) : i1 + @inbounds for i = 1 : A.n, j = colptr[i] : (colptr[i+1]-1) + if op(nzval[j], Sv) Sv = nzval[j] - Iv = sub2ind(szA, rowval[j], i) + Iv = CartesianIndex(rowval[j], i) end end return (fill(Sv,1,1), fill(Iv,1,1)) else throw(ArgumentError("invalid value for region; must be 1, 2, or (1,2)")) end - end) #quote end _isless_fm(a, b) = b == b && ( a != a || isless(a, b) ) _isgreater_fm(a, b) = b == b && ( a != a || isless(b, a) ) -findmin(A::SparseMatrixCSC{Tv,Ti}, region) where {Tv,Ti} = @_findr(_isless_fm, A, region, Tv, Ti) -findmax(A::SparseMatrixCSC{Tv,Ti}, region) where {Tv,Ti} = @_findr(_isgreater_fm, A, region, Tv, Ti) +findmin(A::SparseMatrixCSC{Tv,Ti}, region) where {Tv,Ti} = _findr(_isless_fm, A, region, Tv) +findmax(A::SparseMatrixCSC{Tv,Ti}, region) where {Tv,Ti} = _findr(_isgreater_fm, A, region, Tv) findmin(A::SparseMatrixCSC) = (r=findmin(A,(1,2)); (r[1][1], r[2][1])) findmax(A::SparseMatrixCSC) = (r=findmax(A,(1,2)); (r[1][1], r[2][1])) diff --git a/test/arrayops.jl b/test/arrayops.jl index 39b914a6a0e55..3848aa14e61cc 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -503,7 +503,7 @@ end @test indmin(5:-2:1) == 3 #23094 - @test findmax(Set(["abc"])) === ("abc", 1) + @test_throws MethodError findmax(Set(["abc"])) @test findmin(["abc", "a"]) === ("a", 2) @test_throws MethodError findmax([Set([1]), Set([2])]) @test findmin([0.0, -0.0]) === (-0.0, 2) @@ -1814,6 +1814,11 @@ s, si = findmax(S) @test a == b == s @test ai == bi == si +for X in (A, B, S) + @test findmin(X) == findmin(Dict(pairs(X))) + @test findmax(X) == findmax(Dict(pairs(X))) +end + fill!(B, 2) @test all(x->x==2, B) diff --git a/test/reducedim.jl b/test/reducedim.jl index d5cf35feeb003..63cc41775f125 100644 --- a/test/reducedim.jl +++ b/test/reducedim.jl @@ -156,9 +156,9 @@ end A = [1.0 5.0 6.0; 5.0 2.0 4.0] -for (tup, rval, rind) in [((1,), [1.0 2.0 4.0], [1 4 6]), - ((2,), reshape([1.0,2.0], 2, 1), reshape([1,4], 2, 1)), - ((1,2), fill(1.0,1,1),fill(1,1,1))] +for (tup, rval, rind) in [((1,), [1.0 2.0 4.0], [CartesianIndex(1,1) CartesianIndex(2,2) CartesianIndex(2,3)]), + ((2,), reshape([1.0,2.0], 2, 1), reshape([CartesianIndex(1,1),CartesianIndex(2,2)], 2, 1)), + ((1,2), fill(1.0,1,1),fill(CartesianIndex(1,1),1,1))] @test findmin(A, tup) == (rval, rind) @test findmin!(similar(rval), similar(rind), A) == (rval, rind) @test isequal(minimum(A, tup), rval) @@ -166,9 +166,9 @@ for (tup, rval, rind) in [((1,), [1.0 2.0 4.0], [1 4 6]), @test isequal(minimum!(copy(rval), A, init=false), rval) end -for (tup, rval, rind) in [((1,), [5.0 5.0 6.0], [2 3 5]), - ((2,), reshape([6.0,5.0], 2, 1), reshape([5,2], 2, 1)), - ((1,2), fill(6.0,1,1),fill(5,1,1))] +for (tup, rval, rind) in [((1,), [5.0 5.0 6.0], [CartesianIndex(2,1) CartesianIndex(1,2) CartesianIndex(1,3)]), + ((2,), reshape([6.0,5.0], 2, 1), reshape([CartesianIndex(1,3),CartesianIndex(2,1)], 2, 1)), + ((1,2), fill(6.0,1,1),fill(CartesianIndex(1,3),1,1))] @test findmax(A, tup) == (rval, rind) @test findmax!(similar(rval), similar(rind), A) == (rval, rind) @test isequal(maximum(A, tup), rval) @@ -180,9 +180,9 @@ end A = [1.0 3.0 6.0; NaN 2.0 4.0] -for (tup, rval, rind) in [((1,), [NaN 2.0 4.0], [2 4 6]), - ((2,), reshape([1.0, NaN], 2, 1), reshape([1,2], 2, 1)), - ((1,2), fill(NaN,1,1),fill(2,1,1))] +for (tup, rval, rind) in [((1,), [NaN 2.0 4.0], [CartesianIndex(2,1) CartesianIndex(2,2) CartesianIndex(2,3)]), + ((2,), reshape([1.0, NaN], 2, 1), reshape([CartesianIndex(1,1),CartesianIndex(2,1)], 2, 1)), + ((1,2), fill(NaN,1,1),fill(CartesianIndex(2,1),1,1))] @test isequal(findmin(A, tup), (rval, rind)) @test isequal(findmin!(similar(rval), similar(rind), A), (rval, rind)) @test isequal(minimum(A, tup), rval) @@ -191,9 +191,9 @@ for (tup, rval, rind) in [((1,), [NaN 2.0 4.0], [2 4 6]), @test isequal(Base.reducedim!(min, copy(rval), A), rval) end -for (tup, rval, rind) in [((1,), [NaN 3.0 6.0], [2 3 5]), - ((2,), reshape([6.0, NaN], 2, 1), reshape([5,2], 2, 1)), - ((1,2), fill(NaN,1,1),fill(2,1,1))] +for (tup, rval, rind) in [((1,), [NaN 3.0 6.0], [CartesianIndex(2,1) CartesianIndex(1,2) CartesianIndex(1,3)]), + ((2,), reshape([6.0, NaN], 2, 1), reshape([CartesianIndex(1,3),CartesianIndex(2,1)], 2, 1)), + ((1,2), fill(NaN,1,1),fill(CartesianIndex(2,1),1,1))] @test isequal(findmax(A, tup), (rval, rind)) @test isequal(findmax!(similar(rval), similar(rind), A), (rval, rind)) @test isequal(maximum(A, tup), rval) @@ -204,9 +204,9 @@ end A = [1.0 NaN 6.0; NaN 2.0 4.0] -for (tup, rval, rind) in [((1,), [NaN NaN 4.0], [2 3 6]), - ((2,), reshape([NaN, NaN], 2, 1), reshape([3,2], 2, 1)), - ((1,2), fill(NaN,1,1),fill(2,1,1))] +for (tup, rval, rind) in [((1,), [NaN NaN 4.0], [CartesianIndex(2,1) CartesianIndex(1,2) CartesianIndex(2,3)]), + ((2,), reshape([NaN, NaN], 2, 1), reshape([CartesianIndex(1,2),CartesianIndex(2,1)], 2, 1)), + ((1,2), fill(NaN,1,1),fill(CartesianIndex(2,1),1,1))] @test isequal(findmin(A, tup), (rval, rind)) @test isequal(findmin!(similar(rval), similar(rind), A), (rval, rind)) @test isequal(minimum(A, tup), rval) @@ -214,9 +214,9 @@ for (tup, rval, rind) in [((1,), [NaN NaN 4.0], [2 3 6]), @test isequal(minimum!(copy(rval), A, init=false), rval) end -for (tup, rval, rind) in [((1,), [NaN NaN 6.0], [2 3 5]), - ((2,), reshape([NaN, NaN], 2, 1), reshape([3,2], 2, 1)), - ((1,2), fill(NaN,1,1),fill(2,1,1))] +for (tup, rval, rind) in [((1,), [NaN NaN 6.0], [CartesianIndex(2,1) CartesianIndex(1,2) CartesianIndex(1,3)]), + ((2,), reshape([NaN, NaN], 2, 1), reshape([CartesianIndex(1,2),CartesianIndex(2,1)], 2, 1)), + ((1,2), fill(NaN,1,1),fill(CartesianIndex(2,1),1,1))] @test isequal(findmax(A, tup), (rval, rind)) @test isequal(findmax!(similar(rval), similar(rind), A), (rval, rind)) @test isequal(maximum(A, tup), rval) @@ -226,9 +226,9 @@ end A = [Inf -Inf Inf -Inf; Inf Inf -Inf -Inf] -for (tup, rval, rind) in [((1,), [Inf -Inf -Inf -Inf], [1 3 6 7]), - ((2,), reshape([-Inf -Inf], 2, 1), reshape([3,6], 2, 1)), - ((1,2), fill(-Inf,1,1),fill(3,1,1))] +for (tup, rval, rind) in [((1,), [Inf -Inf -Inf -Inf], [CartesianIndex(1,1) CartesianIndex(1,2) CartesianIndex(2,3) CartesianIndex(1,4)]), + ((2,), reshape([-Inf -Inf], 2, 1), reshape([CartesianIndex(1,2),CartesianIndex(2,3)], 2, 1)), + ((1,2), fill(-Inf,1,1),fill(CartesianIndex(1,2),1,1))] @test isequal(findmin(A, tup), (rval, rind)) @test isequal(findmin!(similar(rval), similar(rind), A), (rval, rind)) @test isequal(minimum(A, tup), rval) @@ -236,9 +236,9 @@ for (tup, rval, rind) in [((1,), [Inf -Inf -Inf -Inf], [1 3 6 7]), @test isequal(minimum!(copy(rval), A, init=false), rval) end -for (tup, rval, rind) in [((1,), [Inf Inf Inf -Inf], [1 4 5 7]), - ((2,), reshape([Inf Inf], 2, 1), reshape([1,2], 2, 1)), - ((1,2), fill(Inf,1,1),fill(1,1,1))] +for (tup, rval, rind) in [((1,), [Inf Inf Inf -Inf], [CartesianIndex(1,1) CartesianIndex(2,2) CartesianIndex(1,3) CartesianIndex(1,4)]), + ((2,), reshape([Inf Inf], 2, 1), reshape([CartesianIndex(1,1),CartesianIndex(2,1)], 2, 1)), + ((1,2), fill(Inf,1,1),fill(CartesianIndex(1,1),1,1))] @test isequal(findmax(A, tup), (rval, rind)) @test isequal(findmax!(similar(rval), similar(rind), A), (rval, rind)) @test isequal(maximum(A, tup), rval) @@ -281,7 +281,7 @@ for (tup, rval, rind) in [((2,), [BigInt(-10)], [1])] end A = [BigInt(10) BigInt(-10)] -for (tup, rval, rind) in [((2,), reshape([BigInt(-10)], 1, 1), reshape([2], 1,1))] +for (tup, rval, rind) in [((2,), reshape([BigInt(-10)], 1, 1), reshape([CartesianIndex(1,2)], 1, 1))] @test isequal(findmin(A, tup), (rval, rind)) @test isequal(findmin!(similar(rval), similar(rind), A), (rval, rind)) @test isequal(minimum(A, tup), rval) @@ -289,7 +289,7 @@ for (tup, rval, rind) in [((2,), reshape([BigInt(-10)], 1, 1), reshape([2], 1,1) @test isequal(minimum!(copy(rval), A, init=false), rval) end -for (tup, rval, rind) in [((2,), reshape([BigInt(10)], 1, 1), reshape([1], 1, 1))] +for (tup, rval, rind) in [((2,), reshape([BigInt(10)], 1, 1), reshape([CartesianIndex(1,1)], 1, 1))] @test isequal(findmax(A, tup), (rval, rind)) @test isequal(findmax!(similar(rval), similar(rind), A), (rval, rind)) @test isequal(maximum(A, tup), rval) diff --git a/test/sparse/sparse.jl b/test/sparse/sparse.jl index 06cc5a7b803b3..ebdd7c40c3087 100644 --- a/test/sparse/sparse.jl +++ b/test/sparse/sparse.jl @@ -998,8 +998,8 @@ end S = spzeros(10,8) A = Array(S) - @test indmax(S) == indmax(A) == 1 - @test indmin(S) == indmin(A) == 1 + @test indmax(S) == indmax(A) == CartesianIndex(1,1) + @test indmin(S) == indmin(A) == CartesianIndex(1,1) A = Array{Int}(0,0) S = sparse(A) @@ -1015,15 +1015,15 @@ end A = sparse([1.0 5.0 6.0; 5.0 2.0 4.0]) -for (tup, rval, rind) in [((1,), [1.0 2.0 4.0], [1 4 6]), - ((2,), reshape([1.0,2.0], 2, 1), reshape([1,4], 2, 1)), - ((1,2), fill(1.0,1,1),fill(1,1,1))] +for (tup, rval, rind) in [((1,), [1.0 2.0 4.0], [CartesianIndex(1,1) CartesianIndex(2,2) CartesianIndex(2,3)]), + ((2,), reshape([1.0,2.0], 2, 1), reshape([CartesianIndex(1,1),CartesianIndex(2,2)], 2, 1)), + ((1,2), fill(1.0,1,1),fill(CartesianIndex(1,1),1,1))] @test findmin(A, tup) == (rval, rind) end -for (tup, rval, rind) in [((1,), [5.0 5.0 6.0], [2 3 5]), - ((2,), reshape([6.0,5.0], 2, 1), reshape([5,2], 2, 1)), - ((1,2), fill(6.0,1,1),fill(5,1,1))] +for (tup, rval, rind) in [((1,), [5.0 5.0 6.0], [CartesianIndex(2,1) CartesianIndex(1,2) CartesianIndex(1,3)]), + ((2,), reshape([6.0,5.0], 2, 1), reshape([CartesianIndex(1,3),CartesianIndex(2,1)], 2, 1)), + ((1,2), fill(6.0,1,1),fill(CartesianIndex(1,3),1,1))] @test findmax(A, tup) == (rval, rind) end @@ -1031,43 +1031,43 @@ end A = sparse([1.0 5.0 6.0; NaN 2.0 4.0]) -for (tup, rval, rind) in [((1,), [NaN 2.0 4.0], [2 4 6]), - ((2,), reshape([1.0, NaN], 2, 1), reshape([1,2], 2, 1)), - ((1,2), fill(NaN,1,1),fill(2,1,1))] +for (tup, rval, rind) in [((1,), [NaN 2.0 4.0], [CartesianIndex(2,1) CartesianIndex(2,2) CartesianIndex(2,3)]), + ((2,), reshape([1.0, NaN], 2, 1), reshape([CartesianIndex(1,1),CartesianIndex(2,1)], 2, 1)), + ((1,2), fill(NaN,1,1),fill(CartesianIndex(2,1),1,1))] @test isequal(findmin(A, tup), (rval, rind)) end -for (tup, rval, rind) in [((1,), [NaN 5.0 6.0], [2 3 5]), - ((2,), reshape([6.0, NaN], 2, 1), reshape([5,2], 2, 1)), - ((1,2), fill(NaN,1,1),fill(2,1,1))] +for (tup, rval, rind) in [((1,), [NaN 5.0 6.0], [CartesianIndex(2,1) CartesianIndex(1,2) CartesianIndex(1,3)]), + ((2,), reshape([6.0, NaN], 2, 1), reshape([CartesianIndex(1,3),CartesianIndex(2,1)], 2, 1)), + ((1,2), fill(NaN,1,1),fill(CartesianIndex(2,1),1,1))] @test isequal(findmax(A, tup), (rval, rind)) end A = sparse([1.0 NaN 6.0; NaN 2.0 4.0]) -for (tup, rval, rind) in [((1,), [NaN NaN 4.0], [2 3 6]), - ((2,), reshape([NaN, NaN], 2, 1), reshape([3,2], 2, 1)), - ((1,2), fill(NaN,1,1),fill(2,1,1))] +for (tup, rval, rind) in [((1,), [NaN NaN 4.0], [CartesianIndex(2,1) CartesianIndex(1,2) CartesianIndex(2,3)]), + ((2,), reshape([NaN, NaN], 2, 1), reshape([CartesianIndex(1,2),CartesianIndex(2,1)], 2, 1)), + ((1,2), fill(NaN,1,1),fill(CartesianIndex(2,1),1,1))] @test isequal(findmin(A, tup), (rval, rind)) end -for (tup, rval, rind) in [((1,), [NaN NaN 6.0], [2 3 5]), - ((2,), reshape([NaN, NaN], 2, 1), reshape([3,2], 2, 1)), - ((1,2), fill(NaN,1,1),fill(2,1,1))] +for (tup, rval, rind) in [((1,), [NaN NaN 6.0], [CartesianIndex(2,1) CartesianIndex(1,2) CartesianIndex(1,3)]), + ((2,), reshape([NaN, NaN], 2, 1), reshape([CartesianIndex(1,2),CartesianIndex(2,1)], 2, 1)), + ((1,2), fill(NaN,1,1),fill(CartesianIndex(2,1),1,1))] @test isequal(findmax(A, tup), (rval, rind)) end A = sparse([Inf -Inf Inf -Inf; Inf Inf -Inf -Inf]) -for (tup, rval, rind) in [((1,), [Inf -Inf -Inf -Inf], [1 3 6 7]), - ((2,), reshape([-Inf -Inf], 2, 1), reshape([3,6], 2, 1)), - ((1,2), fill(-Inf,1,1),fill(3,1,1))] +for (tup, rval, rind) in [((1,), [Inf -Inf -Inf -Inf], [CartesianIndex(1,1) CartesianIndex(1,2) CartesianIndex(2,3) CartesianIndex(1,4)]), + ((2,), reshape([-Inf -Inf], 2, 1), reshape([CartesianIndex(1,2),CartesianIndex(2,3)], 2, 1)), + ((1,2), fill(-Inf,1,1),fill(CartesianIndex(1,2),1,1))] @test isequal(findmin(A, tup), (rval, rind)) end -for (tup, rval, rind) in [((1,), [Inf Inf Inf -Inf], [1 4 5 7]), - ((2,), reshape([Inf Inf], 2, 1), reshape([1,2], 2, 1)), - ((1,2), fill(Inf,1,1),fill(1,1,1))] +for (tup, rval, rind) in [((1,), [Inf Inf Inf -Inf], [CartesianIndex(1,1) CartesianIndex(2,2) CartesianIndex(1,3) CartesianIndex(1,4)]), + ((2,), reshape([Inf Inf], 2, 1), reshape([CartesianIndex(1,1),CartesianIndex(2,1)], 2, 1)), + ((1,2), fill(Inf,1,1),fill(CartesianIndex(1,1),1,1))] @test isequal(findmax(A, tup), (rval, rind)) end @@ -1090,11 +1090,11 @@ for (tup, rval, rind) in [((2,), [BigInt(-10)], [1])] end A = sparse([BigInt(10) BigInt(-10)]) -for (tup, rval, rind) in [((2,), reshape([BigInt(-10)], 1, 1), reshape([2], 1, 1))] +for (tup, rval, rind) in [((2,), reshape([BigInt(-10)], 1, 1), reshape([CartesianIndex(1,2)], 1, 1))] @test isequal(findmin(A, tup), (rval, rind)) end -for (tup, rval, rind) in [((2,), reshape([BigInt(10)], 1, 1), reshape([1], 1, 1))] +for (tup, rval, rind) in [((2,), reshape([BigInt(10)], 1, 1), reshape([CartesianIndex(1,1)], 1, 1))] @test isequal(findmax(A, tup), (rval, rind)) end From 447ca303357f464d4c63c464012b0b06ef43060c Mon Sep 17 00:00:00 2001 From: Alex Arslan Date: Mon, 4 Sep 2017 14:55:38 -0700 Subject: [PATCH 259/324] Add a trailing newline to the & deprecation message --- src/julia-syntax.scm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index c74c0c1a0aeff..41a333b773644 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -3517,7 +3517,7 @@ f(x) = yt(x) (deprecation-message (string "Syntax \"&argument\"" (linenode-string current-loc) " is deprecated. Remove the \"&\" and use a \"Ref\" argument " - "type instead.")))) + "type instead." #\newline)))) (list-tail e 6)) ;; NOTE: 2nd to 5th arguments of ccall must be left in place ;; the 1st should be compiled if an atom. From dbd1574aa0f8f8de3840dedb859a3ee984e4ccb0 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Fri, 1 Sep 2017 16:54:57 -0400 Subject: [PATCH 260/324] parse `let` the same as `for`. part of #21774 --- NEWS.md | 4 ++++ base/broadcast.jl | 4 ++-- base/distributed/macros.jl | 2 +- base/printf.jl | 4 ++-- base/show.jl | 5 +---- base/subarray.jl | 13 +++++++------ doc/src/devdocs/ast.md | 3 ++- src/julia-parser.scm | 28 +++++++++++++++------------- src/julia-syntax.scm | 30 ++++++++++++++++++++++-------- src/macroexpand.scm | 32 +++++++++++++++++--------------- test/parse.jl | 2 +- 11 files changed, 74 insertions(+), 53 deletions(-) diff --git a/NEWS.md b/NEWS.md index 97deb4df01f46..f434689a8d2c1 100644 --- a/NEWS.md +++ b/NEWS.md @@ -42,6 +42,10 @@ Language changes * Nested `if` expressions that arise from the keyword `elseif` now use `elseif` as their expression head instead of `if` ([#21774]). + * `let` blocks now parse the same as `for` loops; the first argument is either an + assignment or `block` of assignments, and the second argument is a block of + statements ([#21774]). + * Parsed and lowered forms of type definitions have been synchronized with their new keywords ([#23157]). Expression heads are renamed as follows: diff --git a/base/broadcast.jl b/base/broadcast.jl index 1b847aa7ddb6d..2ec5d451512bd 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -564,8 +564,8 @@ function __dot__(x::Expr) Expr(:., dotargs[1], Expr(:tuple, dotargs[2:end]...)) elseif x.head == :$ x.args[1] - elseif x.head == :let # don't add dots to "let x=... assignments - Expr(:let, dotargs[1], map(undot, dotargs[2:end])...) + elseif x.head == :let # don't add dots to `let x=...` assignments + Expr(:let, undot(dotargs[1]), dotargs[2]) elseif x.head == :for # don't add dots to for x=... assignments Expr(:for, undot(dotargs[1]), dotargs[2]) elseif (x.head == :(=) || x.head == :function || x.head == :macro) && diff --git a/base/distributed/macros.jl b/base/distributed/macros.jl index aa3f52cedc2d4..d3b72cb791682 100644 --- a/base/distributed/macros.jl +++ b/base/distributed/macros.jl @@ -123,7 +123,7 @@ function extract_imports!(imports, ex::Expr) if Meta.isexpr(ex, (:import, :using)) return push!(imports, ex.args[1]) elseif Meta.isexpr(ex, :let) - return extract_imports!(imports, ex.args[1]) + return extract_imports!(imports, ex.args[2]) elseif Meta.isexpr(ex, (:toplevel, :block)) for i in eachindex(ex.args) extract_imports!(imports, ex.args[i]) diff --git a/base/printf.jl b/base/printf.jl index c2ca443ba8bc0..dff3d28412df7 100644 --- a/base/printf.jl +++ b/base/printf.jl @@ -1188,7 +1188,7 @@ function _printf(macroname, io, fmt, args) end unshift!(blk.args, :(out = $io)) - Expr(:let, blk) + Expr(:let, Expr(:block), blk) end """ @@ -1241,7 +1241,7 @@ macro sprintf(args...) isa(args[1], AbstractString) || is_str_expr(args[1]) || throw(ArgumentError("@sprintf: first argument must be a format string")) letexpr = _printf("@sprintf", :(IOBuffer()), args[1], args[2:end]) - push!(letexpr.args[1].args, :(String(take!(out)))) + push!(letexpr.args[2].args, :(String(take!(out)))) letexpr end diff --git a/base/show.jl b/base/show.jl index ab517f9c44795..801d6d8d1bc6f 100644 --- a/base/show.jl +++ b/base/show.jl @@ -959,7 +959,7 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int) print(io, "function ", args[1], " end") # block with argument - elseif head in (:for,:while,:function,:if,:elseif) && nargs==2 + elseif head in (:for,:while,:function,:if,:elseif,:let) && nargs==2 show_block(io, head, args[1], args[2], indent) print(io, "end") @@ -1043,9 +1043,6 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int) end print(io, "end") - elseif head === :let && nargs >= 1 - show_block(io, "let", args[2:end], args[1], indent); print(io, "end") - elseif head === :block || head === :body show_block(io, "begin", ex, indent); print(io, "end") diff --git a/base/subarray.jl b/base/subarray.jl index b695444563c67..5db883f964c06 100644 --- a/base/subarray.jl +++ b/base/subarray.jl @@ -422,7 +422,7 @@ function replace_ref_end_!(ex, withex) if used_S && S !== ex.args[1] S0 = ex.args[1] ex.args[1] = S - ex = Expr(:let, ex, :($S = $S0)) + ex = Expr(:let, :($S = $S0), ex) end else # recursive search @@ -471,8 +471,8 @@ macro view(ex) if Meta.isexpr(ex, :ref) ex = Expr(:call, view, ex.args...) else # ex replaced by let ...; foo[...]; end - assert(Meta.isexpr(ex, :let) && Meta.isexpr(ex.args[1], :ref)) - ex.args[1] = Expr(:call, view, ex.args[1].args...) + assert(Meta.isexpr(ex, :let) && Meta.isexpr(ex.args[2], :ref)) + ex.args[2] = Expr(:call, view, ex.args[2].args...) end Expr(:&&, true, esc(ex)) else @@ -532,12 +532,13 @@ function _views(ex::Expr) end Expr(:let, + Expr(:block, + :($a = $(_views(lhs.args[1]))), + [:($(i[k]) = $(_views(lhs.args[k+1]))) for k=1:length(i)]...), Expr(first(h) == '.' ? :(.=) : :(=), :($a[$(I...)]), Expr(:call, Symbol(h[1:end-1]), :($maybeview($a, $(I...))), - _views.(ex.args[2:end])...)), - :($a = $(_views(lhs.args[1]))), - [:($(i[k]) = $(_views(lhs.args[k+1]))) for k=1:length(i)]...) + _views.(ex.args[2:end])...))) else Expr(ex.head, _views.(ex.args)...) end diff --git a/doc/src/devdocs/ast.md b/doc/src/devdocs/ast.md index ae9622fbb7866..a13e6f4bf2660 100644 --- a/doc/src/devdocs/ast.md +++ b/doc/src/devdocs/ast.md @@ -486,7 +486,8 @@ they are parsed as a block: `(for (block (= v1 iter1) (= v2 iter2)) body)`. `break` and `continue` are parsed as 0-argument expressions `(break)` and `(continue)`. -`let` is parsed as `(let body (= var1 val1) (= var2 val2) ...)`. +`let` is parsed as `(let (= var val) body)` or `(let (block (= var1 val1) (= var2 val2) ...) body)`, +like `for` loops. A basic function definition is parsed as `(function (call f x) body)`. A more complex example: diff --git a/src/julia-parser.scm b/src/julia-parser.scm index 3a17ab7346eb8..53e36ec03eeaf 100644 --- a/src/julia-parser.scm +++ b/src/julia-parser.scm @@ -1303,6 +1303,21 @@ `(for ,(if (length= ranges 1) (car ranges) (cons 'block ranges)) ,body))) + ((let) + (let ((binds (if (memv (peek-token s) '(#\newline #\;)) + '() + (parse-comma-separated-assignments s)))) + (if (not (or (eof-object? (peek-token s)) + (memv (peek-token s) '(#\newline #\; end)))) + (error "let variables should end in \";\" or newline")) + (let* ((ex (begin0 (parse-block s) + (expect-end s word))) + (ex (if (and (length= ex 2) (pair? (cadr ex)) (eq? (caadr ex) 'line)) + `(block) ;; don't need line info in an empty let block + ex))) + `(let ,(if (length= binds 1) (car binds) (cons 'block binds)) + ,ex)))) + ((if elseif) (if (newline? (peek-token s)) (error (string "missing condition in \"if\" at " current-filename @@ -1331,19 +1346,6 @@ (begin0 (list word test then (parse-block s)) (expect-end s 'if))) (else (error (string "unexpected \"" nxt "\"")))))) - ((let) - (let ((binds (if (memv (peek-token s) '(#\newline #\;)) - '() - (parse-comma-separated-assignments s)))) - (if (not (or (eof-object? (peek-token s)) - (memv (peek-token s) '(#\newline #\; end)))) - (error "let variables should end in \";\" or newline")) - (let ((ex (parse-block s))) - (expect-end s word) - ;; don't need line info in an empty let block - (if (and (length= ex 2) (pair? (cadr ex)) (eq? (caadr ex) 'line)) - `(let (block) ,@binds) - `(let ,ex ,@binds))))) ((global local) (let* ((const (and (eq? (peek-token s) 'const) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index c74c0c1a0aeff..c096f27fa9d84 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -359,9 +359,9 @@ (define (scopenest names vals expr) (if (null? names) expr - `(let (block - ,(scopenest (cdr names) (cdr vals) expr)) - (= ,(car names) ,(car vals))))) + `(let (= ,(car names) ,(car vals)) + (block + ,(scopenest (cdr names) (cdr vals) expr))))) (define empty-vector-any '(call (core AnyVector) 0)) @@ -1120,9 +1120,23 @@ `(call ,name ,@argl)) ,body))))) +(define (let-binds e) + (if (and (pair? (cadr e)) + (eq? (car (cadr e)) 'block)) + (cdr (cadr e)) + (list (cadr e)))) + (define (expand-let e) - (let ((ex (cadr e)) - (binds (cddr e))) + (if (length= e 2) + (begin (deprecation-message (string "The form `Expr(:let, ex)` is deprecated. " + "Use `Expr(:let, Expr(:block), ex)` instead." #\newline)) + (return (expand-let `(let (block) ,(cadr e)))))) + (if (length> e 3) + (begin (deprecation-message (string "The form `Expr(:let, ex, binds...)` is deprecated. " + "Use `Expr(:let, Expr(:block, binds...), ex)` instead." #\newline)) + (return (expand-let `(let (block ,@(cddr e)) ,(cadr e)))))) + (let ((ex (caddr e)) + (binds (let-binds e))) (expand-forms (if (null? binds) @@ -1803,7 +1817,7 @@ `(fuse _ ,(cdadr (cadr arg))) oldarg)) fargs args))) - (let ,fbody ,@(reverse (fuse-lets fargs args '())))))) + (let (block ,@(reverse (fuse-lets fargs args '()))) ,fbody)))) (define (dot-to-fuse e) ; convert e == (. f (tuple args)) to (fuse f args) (define (make-fuse f args) ; check for nested (fuse f args) exprs and combine (define (split-kwargs args) ; return (cons keyword-args positional-args) extracted from args @@ -1891,8 +1905,8 @@ (define (expand-where body var) (let* ((bounds (analyze-typevar var)) (v (car bounds))) - `(let (call (core UnionAll) ,v ,body) - (= ,v ,(bounds-to-TypeVar bounds))))) + `(let (= ,v ,(bounds-to-TypeVar bounds)) + (call (core UnionAll) ,v ,body)))) (define (expand-wheres body vars) (if (null? vars) diff --git a/src/macroexpand.scm b/src/macroexpand.scm index 8161be6fff2ae..a7535920d24b0 100644 --- a/src/macroexpand.scm +++ b/src/macroexpand.scm @@ -92,8 +92,8 @@ (cons 'varlist (typevar-names vars))) ;; let - (pattern-lambda (let ex . binds) - (let loop ((binds binds) + (pattern-lambda (let binds ex) + (let loop ((binds (let-binds __)) (vars '())) (if (null? binds) (cons 'varlist vars) @@ -360,19 +360,21 @@ ((let) (let* ((newenv (new-expansion-env-for e env)) - (body (resolve-expansion-vars- (cadr e) newenv m parent-scope inarg))) - `(let ,body - ,@(map - (lambda (bind) - (if (assignment? bind) - (make-assignment - ;; expand binds in old env with dummy RHS - (cadr (resolve-expansion-vars- (make-assignment (cadr bind) 0) - newenv m parent-scope inarg)) - ;; expand initial values in old env - (resolve-expansion-vars- (caddr bind) env m parent-scope inarg)) - bind)) - (cddr e))))) + (body (resolve-expansion-vars- (caddr e) newenv m parent-scope inarg)) + (binds (let-binds e))) + `(let (block + ,@(map + (lambda (bind) + (if (assignment? bind) + (make-assignment + ;; expand binds in old env with dummy RHS + (cadr (resolve-expansion-vars- (make-assignment (cadr bind) 0) + newenv m parent-scope inarg)) + ;; expand initial values in old env + (resolve-expansion-vars- (caddr bind) env m parent-scope inarg)) + bind)) + binds)) + ,body))) ((hygienic-scope) ; TODO: move this lowering to resolve-scopes, instead of reimplementing it here badly (let ((parent-scope (cons (list env m) parent-scope)) (body (cadr e)) diff --git a/test/parse.jl b/test/parse.jl index 7d5aaad45964e..19bac9550cae3 100644 --- a/test/parse.jl +++ b/test/parse.jl @@ -514,7 +514,7 @@ let b = IOBuffer(""" end f() """) - @test Base.parse_input_line(b) == Expr(:let, Expr(:block, LineNumberNode(2, :none), :x), Expr(:(=), :x, :x)) + @test Base.parse_input_line(b) == Expr(:let, Expr(:(=), :x, :x), Expr(:block, LineNumberNode(2, :none), :x)) @test Base.parse_input_line(b) == Expr(:call, :f) @test Base.parse_input_line(b) === nothing end From e0658f2c9164614faaff7ad5ba0364e86ed2776c Mon Sep 17 00:00:00 2001 From: Alex Arslan Date: Mon, 4 Sep 2017 15:05:47 -0700 Subject: [PATCH 261/324] Fix deprecated & syntax in MPFR ccalls --- base/mpfr.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/base/mpfr.jl b/base/mpfr.jl index 43064ee9adfab..864c6b6f650c5 100644 --- a/base/mpfr.jl +++ b/base/mpfr.jl @@ -904,15 +904,15 @@ end function _calculate_buffer_size!(buf, fmt, x::BigFloat) ccall((:mpfr_snprintf,:libmpfr), - Int32, (Ptr{UInt8}, Culong, Ptr{UInt8}, Ptr{BigFloat}...), - buf, 0, fmt, &x) + Int32, (Ptr{UInt8}, Culong, Ptr{UInt8}, Ref{BigFloat}...), + buf, 0, fmt, x) end function _fill_buffer!(buf, fmt, x::BigFloat) s = length(buf) # we temporarily need one more item in buffer to capture null termination resize!(buf, s + 1) - n = ccall((:mpfr_sprintf,:libmpfr), Int32, (Ptr{UInt8}, Ptr{UInt8}, Ptr{BigFloat}...), buf, fmt, &x) + n = ccall((:mpfr_sprintf,:libmpfr), Int32, (Ptr{UInt8}, Ptr{UInt8}, Ref{BigFloat}...), buf, fmt, x) @assert n + 1 == length(buf) @assert last(buf) == 0x00 resize!(buf, s) From 47986fadf6d70dfecc856fc8c2fe14902fcacfaf Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Sun, 3 Sep 2017 15:02:07 -0400 Subject: [PATCH 262/324] Add a codegen param for preferring specsig. --- base/reflection.jl | 5 +++-- src/cgutils.cpp | 1 + src/codegen.cpp | 6 ++++-- src/jltypes.c | 2 +- src/julia.h | 1 + 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/base/reflection.jl b/base/reflection.jl index 50e26e1cc040c..f5f1979ebb72c 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -745,6 +745,7 @@ struct CodegenParams code_coverage::Cint static_alloc::Cint dynamic_alloc::Cint + prefer_specsig::Cint hooks::CodegenHooks @@ -752,12 +753,12 @@ struct CodegenParams runtime::Bool=true, exceptions::Bool=true, track_allocations::Bool=true, code_coverage::Bool=true, static_alloc::Bool=true, dynamic_alloc::Bool=true, - hooks::CodegenHooks=CodegenHooks()) = + prefer_specsig::Bool=false, hooks::CodegenHooks=CodegenHooks()) = new(Cint(cached), Cint(runtime), Cint(exceptions), Cint(track_allocations), Cint(code_coverage), Cint(static_alloc), Cint(dynamic_alloc), - hooks) + Cint(prefer_specsig), hooks) end # Printing code representations in IR and assembly diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 13ecf39ff4bc3..e0ca56dee5066 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -2355,6 +2355,7 @@ static int compare_cgparams(const jl_cgparams_t *a, const jl_cgparams_t *b) (a->code_coverage == b->code_coverage) && (a->static_alloc == b->static_alloc) && (a->dynamic_alloc == b->dynamic_alloc) && + (a->prefer_specsig == b->prefer_specsig) && // hooks (a->hooks.module_setup == b->hooks.module_setup) && (a->hooks.module_activation == b->hooks.module_activation) && diff --git a/src/codegen.cpp b/src/codegen.cpp index 96483a28f9dfc..f30113dfb4312 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4596,7 +4596,7 @@ static Function *gen_jlcall_wrapper(jl_method_instance_t *lam, const jl_returnin return w; } -static bool uses_specsig(jl_value_t *sig, jl_value_t *rettype, bool needsparam, bool va, jl_code_info_t *src) +static bool uses_specsig(jl_value_t *sig, jl_value_t *rettype, bool needsparam, bool va, jl_code_info_t *src, bool prefer_specsig) { if (va || needsparam) return false; @@ -4609,6 +4609,8 @@ static bool uses_specsig(jl_value_t *sig, jl_value_t *rettype, bool needsparam, if (jl_nparams(sig) == 0) return false; // not invalid, consider if specialized signature is worthwhile + if (prefer_specsig) + return true; if (isbits_spec(rettype, false)) return true; if (jl_is_uniontype(rettype)) { @@ -4862,7 +4864,7 @@ static std::unique_ptr emit_function( } jl_value_t *jlrettype = lam->rettype; - bool specsig = uses_specsig(lam->specTypes, jlrettype, needsparams, va, src); + bool specsig = uses_specsig(lam->specTypes, jlrettype, needsparams, va, src, params->prefer_specsig); if (!specsig) ctx.nReqArgs--; // function not part of argArray in jlcall diff --git a/src/jltypes.c b/src/jltypes.c index 6218abb83a019..4b0eaedcb6b7e 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -127,7 +127,7 @@ jl_value_t *jl_memory_exception; jl_value_t *jl_readonlymemory_exception; union jl_typemap_t jl_cfunction_list; -jl_cgparams_t jl_default_cgparams = {1, 1, 1, 1, 1, 1, 1, {NULL, NULL, NULL}}; +jl_cgparams_t jl_default_cgparams = {1, 1, 1, 1, 1, 1, 1, 0, {NULL, NULL, NULL}}; // --- type properties and predicates --- diff --git a/src/julia.h b/src/julia.h index 5fda2e5415f25..af899ff85d758 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1832,6 +1832,7 @@ typedef struct { int code_coverage; // can we measure coverage (don't if disallowed)? int static_alloc; // is the compiler allowed to allocate statically? int dynamic_alloc; // is the compiler allowed to allocate dynamically (requires runtime)? + int prefer_specsig; // are specialized function signatures preferred? jl_cghooks_t hooks; } jl_cgparams_t; From 5c6f42316c84f1e59fcb830a02dece8a8610486e Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Mon, 4 Sep 2017 18:06:57 -0400 Subject: [PATCH 263/324] Embed codegen hooks in the params object. Using pointer_objref is unsafe for the GC. --- base/reflection.jl | 26 ++++++++------------------ src/cgutils.cpp | 10 +++++----- src/jltypes.c | 8 ++++---- src/julia.h | 28 ++++++++++++---------------- 4 files changed, 29 insertions(+), 43 deletions(-) diff --git a/base/reflection.jl b/base/reflection.jl index f5f1979ebb72c..f24aae7d20d87 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -723,18 +723,6 @@ function method_instances(@nospecialize(f), @nospecialize(t), world::UInt = type return results end -# this type mirrors jl_cghooks_t (documented in julia.h) -struct CodegenHooks - module_setup::Ptr{Void} - module_activation::Ptr{Void} - raise_exception::Ptr{Void} - - CodegenHooks(;module_setup=nothing, module_activation=nothing, raise_exception=nothing) = - new(pointer_from_objref(module_setup), - pointer_from_objref(module_activation), - pointer_from_objref(raise_exception)) -end - # this type mirrors jl_cgparams_t (documented in julia.h) struct CodegenParams cached::Cint @@ -747,18 +735,20 @@ struct CodegenParams dynamic_alloc::Cint prefer_specsig::Cint - hooks::CodegenHooks + module_setup::Any + module_activation::Any + raise_exception::Any CodegenParams(;cached::Bool=true, runtime::Bool=true, exceptions::Bool=true, track_allocations::Bool=true, code_coverage::Bool=true, static_alloc::Bool=true, dynamic_alloc::Bool=true, - prefer_specsig::Bool=false, hooks::CodegenHooks=CodegenHooks()) = + prefer_specsig::Bool=false, + module_setup=nothing, module_activation=nothing, raise_exception=nothing) = new(Cint(cached), - Cint(runtime), Cint(exceptions), - Cint(track_allocations), Cint(code_coverage), - Cint(static_alloc), Cint(dynamic_alloc), - Cint(prefer_specsig), hooks) + Cint(runtime), Cint(exceptions), Cint(track_allocations), Cint(code_coverage), + Cint(static_alloc), Cint(dynamic_alloc), Cint(prefer_specsig), + module_setup, module_activation, raise_exception) end # Printing code representations in IR and assembly diff --git a/src/cgutils.cpp b/src/cgutils.cpp index e0ca56dee5066..7e7d3fa5c7a64 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -109,10 +109,10 @@ static Value *mark_callee_rooted(IRBuilder<> &irbuilder, Value *V) // --- hook checks --- -#define JL_HOOK_TEST(params,hook) ((params)->hooks.hook != jl_nothing) +#define JL_HOOK_TEST(params,hook) ((params)->hook != jl_nothing) #define JL_HOOK_CALL(params,hook,argc,...) \ - _hook_call((params)->hooks.hook, {{__VA_ARGS__}}); + _hook_call((params)->hook, {{__VA_ARGS__}}); template static inline void _hook_call(jl_value_t *hook, std::array args) { jl_value_t **argv; @@ -2357,7 +2357,7 @@ static int compare_cgparams(const jl_cgparams_t *a, const jl_cgparams_t *b) (a->dynamic_alloc == b->dynamic_alloc) && (a->prefer_specsig == b->prefer_specsig) && // hooks - (a->hooks.module_setup == b->hooks.module_setup) && - (a->hooks.module_activation == b->hooks.module_activation) && - (a->hooks.raise_exception == b->hooks.raise_exception); + (a->module_setup == b->module_setup) && + (a->module_activation == b->module_activation) && + (a->raise_exception == b->raise_exception); } diff --git a/src/jltypes.c b/src/jltypes.c index 4b0eaedcb6b7e..7a7e2e4a6fe4a 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -127,7 +127,7 @@ jl_value_t *jl_memory_exception; jl_value_t *jl_readonlymemory_exception; union jl_typemap_t jl_cfunction_list; -jl_cgparams_t jl_default_cgparams = {1, 1, 1, 1, 1, 1, 1, 0, {NULL, NULL, NULL}}; +jl_cgparams_t jl_default_cgparams = {1, 1, 1, 1, 1, 1, 1, 0, NULL, NULL, NULL}; // --- type properties and predicates --- @@ -1663,9 +1663,9 @@ void jl_init_types(void) jl_methtable_type = jl_new_uninitialized_datatype(); jl_nothing = jl_gc_permobj(0, NULL); - jl_default_cgparams.hooks.module_setup = jl_nothing; - jl_default_cgparams.hooks.module_activation = jl_nothing; - jl_default_cgparams.hooks.raise_exception = jl_nothing; + jl_default_cgparams.module_setup = jl_nothing; + jl_default_cgparams.module_activation = jl_nothing; + jl_default_cgparams.raise_exception = jl_nothing; jl_emptysvec = (jl_svec_t*)jl_gc_permobj(sizeof(void*), jl_simplevector_type); jl_svec_set_len_unsafe(jl_emptysvec, 0); diff --git a/src/julia.h b/src/julia.h index af899ff85d758..871c2cfa43ba8 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1804,7 +1804,18 @@ typedef struct { // codegen interface ---------------------------------------------------------- typedef struct { - // to disable a hook: set to NULL or nothing + int cached; // can the compiler use/populate the compilation cache? + + int runtime; // can we call into the runtime? + int exceptions; // are exceptions supported (requires runtime)? + int track_allocations; // can we track allocations (don't if disallowed)? + int code_coverage; // can we measure coverage (don't if disallowed)? + int static_alloc; // is the compiler allowed to allocate statically? + int dynamic_alloc; // is the compiler allowed to allocate dynamically (requires runtime)? + int prefer_specsig; // are specialized function signatures preferred? + + + // hooks // module setup: prepare a module for code emission (data layout, DWARF version, ...) // parameters: LLVMModuleRef as Ptr{Void} @@ -1820,21 +1831,6 @@ typedef struct { // parameters: LLVMBasicBlockRef as Ptr{Void}, LLVMValueRef as Ptr{Void} // return value: none jl_value_t *raise_exception; -} jl_cghooks_t; - -typedef struct { - int cached; // can the compiler use/populate the compilation cache? - - // language features (C-style integer booleans) - int runtime; // can we call into the runtime? - int exceptions; // are exceptions supported (requires runtime)? - int track_allocations; // can we track allocations (don't if disallowed)? - int code_coverage; // can we measure coverage (don't if disallowed)? - int static_alloc; // is the compiler allowed to allocate statically? - int dynamic_alloc; // is the compiler allowed to allocate dynamically (requires runtime)? - int prefer_specsig; // are specialized function signatures preferred? - - jl_cghooks_t hooks; } jl_cgparams_t; extern JL_DLLEXPORT jl_cgparams_t jl_default_cgparams; From dc4f140ad40d77bc5f6c35ca396d94fbd4b949ca Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Sun, 3 Sep 2017 16:38:18 -0400 Subject: [PATCH 264/324] Untangle legacy .gcroot field When the new gc root placement pass was introduced, the .gcroot field lost its original meaning and became mostly superfluous. However, the codegen code for unions still used it to get a referenced to the boxed part of the split union. Clean that up by introducing an explicit field for that purpose and removing the .gcroot field. --- src/ccall.cpp | 5 +- src/cgutils.cpp | 38 ++--- src/codegen.cpp | 391 ++++++++++++++++++++++-------------------------- 3 files changed, 199 insertions(+), 235 deletions(-) diff --git a/src/ccall.cpp b/src/ccall.cpp index 118e4ae3b2b84..7512f24f0648b 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -1583,9 +1583,8 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) if (jl_is_long(argi_root)) continue; jl_cgval_t arg_root = emit_expr(ctx, argi_root); - Value *gcuse = arg_root.gcroot ? ctx.builder.CreateLoad(arg_root.gcroot) : arg_root.V; - if (gcuse) { - gc_uses.push_back(gcuse); + if (arg_root.Vboxed || arg_root.V) { + gc_uses.push_back(arg_root.Vboxed ? arg_root.Vboxed : arg_root.V); } } diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 6d350e9ab94a9..6dbb982e7223e 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -728,7 +728,7 @@ static jl_cgval_t emit_typeof(jl_codectx_t &ctx, const jl_cgval_t &p) else { // See note above in emit_typeof(Value*), we can't tell the system // about this until we've cleared the GC bits. - pdatatype = emit_bitcast(ctx, emit_typeptr_addr(ctx, ctx.builder.CreateLoad(p.gcroot)), T_ppjlvalue); + pdatatype = emit_bitcast(ctx, emit_typeptr_addr(ctx, p.Vboxed), T_ppjlvalue); } counter = 0; for_each_uniontype_small( @@ -1028,7 +1028,7 @@ static std::pair emit_isa(jl_codectx_t &ctx, const jl_cgval_t &x, Value *xtindex = ctx.builder.CreateAnd(x.TIndex, ConstantInt::get(T_int8, 0x7f)); return std::make_pair(ctx.builder.CreateICmpEQ(xtindex, ConstantInt::get(T_int8, tindex)), false); } - else { + else if (x.Vboxed) { // test for (x.TIndex == 0x80 && typeof(x.V) == type) Value *isboxed = ctx.builder.CreateICmpEQ(x.TIndex, ConstantInt::get(T_int8, 0x80)); BasicBlock *currBB = ctx.builder.GetInsertBlock(); @@ -1036,7 +1036,7 @@ static std::pair emit_isa(jl_codectx_t &ctx, const jl_cgval_t &x, BasicBlock *postBB = BasicBlock::Create(jl_LLVMContext, "post_isa", ctx.f); ctx.builder.CreateCondBr(isboxed, isaBB, postBB); ctx.builder.SetInsertPoint(isaBB); - Value *istype_boxed = ctx.builder.CreateICmpEQ(emit_typeof(ctx, x.V), + Value *istype_boxed = ctx.builder.CreateICmpEQ(emit_typeof(ctx, x.Vboxed), maybe_decay_untracked(literal_pointer_val(ctx, type))); ctx.builder.CreateBr(postBB); ctx.builder.SetInsertPoint(postBB); @@ -1044,6 +1044,9 @@ static std::pair emit_isa(jl_codectx_t &ctx, const jl_cgval_t &x, istype->addIncoming(ConstantInt::get(T_int1, 0), currBB); istype->addIncoming(istype_boxed, isaBB); return std::make_pair(istype, false); + } else { + // handle the case where we know that `x` is unboxed (but of unknown type), but that leaf type `type` cannot be unboxed + return std::make_pair(ConstantInt::get(T_int1, 0), false); } } return std::make_pair(ctx.builder.CreateICmpEQ(emit_typeof_boxed(ctx, x), @@ -1299,7 +1302,6 @@ static bool emit_getfield_unknownidx(jl_codectx_t &ctx, Type *fty = julia_type_to_llvm(jt); Value *addr = ctx.builder.CreateGEP(emit_bitcast(ctx, decay_derived(ptr), PointerType::get(fty,0)), idx); *ret = mark_julia_slot(addr, jt, NULL, strct.tbaa); - ret->gcroot = strct.gcroot; ret->isimmutable = strct.isimmutable; return true; } @@ -1395,7 +1397,6 @@ static jl_cgval_t emit_getfield_knownidx(jl_codectx_t &ctx, const jl_cgval_t &st Value *ptindex = ctx.builder.CreateGEP(T_int8, emit_bitcast(ctx, addr, T_pint8), ConstantInt::get(T_size, fsz - 1)); Value *tindex = ctx.builder.CreateNUWAdd(ConstantInt::get(T_int8, 1), ctx.builder.CreateLoad(ptindex)); bool isimmutable = strct.isimmutable; - Value *gcroot = strct.gcroot; if (jt->mutabl) { // move value to an immutable stack slot Type *AT = ArrayType::get(IntegerType::get(jl_LLVMContext, 8 * align), (fsz + align - 2) / align); @@ -1406,18 +1407,15 @@ static jl_cgval_t emit_getfield_knownidx(jl_codectx_t &ctx, const jl_cgval_t &st ctx.builder.CreateMemCpy(lv, addr, nbytes, align); addr = lv; isimmutable = true; - gcroot = NULL; } jl_cgval_t fieldval = mark_julia_slot(addr, jfty, tindex, strct.tbaa); fieldval.isimmutable = isimmutable; - fieldval.gcroot = gcroot; return fieldval; } else if (!jt->mutabl) { // just compute the pointer and let user load it when necessary jl_cgval_t fieldval = mark_julia_slot(addr, jfty, NULL, strct.tbaa); fieldval.isimmutable = strct.isimmutable; - fieldval.gcroot = strct.gcroot; return fieldval; } return typed_load(ctx, addr, ConstantInt::get(T_size, 0), jfty, strct.tbaa, true, align); @@ -1905,6 +1903,13 @@ static Value *compute_tindex_unboxed(jl_codectx_t &ctx, const jl_cgval_t &val, j return ctx.builder.CreateAnd(val.TIndex, ConstantInt::get(T_int8, 0x7f)); } +/* + * Box unboxed values in a union. Optionally, skip certain unboxed values, + * returning `V_null` in one of the skipped cases. If `skip` is not empty, + * skip[0] (corresponding to unknown boxed) must always be set. In that + * case, the calling code must separately deal with the case where + * `vinfo` is already an unkown boxed union (union tag 0x80). + */ static Value *box_union(jl_codectx_t &ctx, const jl_cgval_t &vinfo, const SmallBitVector &skip) { // given vinfo::Union{T, S}, emit IR of the form: @@ -1955,13 +1960,12 @@ static Value *box_union(jl_codectx_t &ctx, const jl_cgval_t &vinfo, const SmallB vinfo.typ, counter); ctx.builder.SetInsertPoint(defaultBB); - if (skip.size() > 0 && skip[0]) { - // skip[0] specifies where to return NULL or the original pointer - // if the value was not handled above + if (skip.size() > 0) { + assert(skip[0]); box_merge->addIncoming(maybe_decay_untracked(V_null), defaultBB); ctx.builder.CreateBr(postBB); } - else if ((vinfo.V == NULL || isa(vinfo.V)) && !vinfo.gcroot) { + else if (!vinfo.Vboxed) { Function *trap_func = Intrinsic::getDeclaration( ctx.f->getParent(), Intrinsic::trap); @@ -1969,9 +1973,7 @@ static Value *box_union(jl_codectx_t &ctx, const jl_cgval_t &vinfo, const SmallB ctx.builder.CreateUnreachable(); } else { - // We're guaranteed here that Load(.gcroot) == .V, because we have determined - // that this union is a boxed value, rather than an interior pointer of some sort - box_merge->addIncoming(ctx.builder.CreateLoad(vinfo.gcroot), defaultBB); + box_merge->addIncoming(vinfo.Vboxed, defaultBB); ctx.builder.CreateBr(postBB); } ctx.builder.SetInsertPoint(postBB); @@ -1990,10 +1992,8 @@ static Value *boxed(jl_codectx_t &ctx, const jl_cgval_t &vinfo) if (vinfo.constant) return maybe_decay_untracked(literal_pointer_val(ctx, vinfo.constant)); if (vinfo.isboxed) { - assert(vinfo.V && "Missing value for box."); - // We're guaranteed here that Load(.gcroot) == .V, because we have determined - // that this value is a box, so if it has a gcroot, that's where the value is. - return vinfo.gcroot ? ctx.builder.CreateLoad(vinfo.gcroot) : vinfo.V; + assert(vinfo.V == vinfo.Vboxed); + return vinfo.V; } Value *box; diff --git a/src/codegen.cpp b/src/codegen.cpp index 9aee554c1e84f..ba9e7aa12b192 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -391,9 +391,14 @@ static MDNode *best_tbaa(jl_value_t *jt) { // metadata tracking for a llvm Value* during codegen struct jl_cgval_t { Value *V; // may be of type T* or T, or set to NULL if ghost (or if the value has not been initialized yet, for a variable definition) + // For unions, we may need to keep a reference to the boxed part individually. + // If this is non-NULL, then, at runtime, we satisfy the invariant that (for the corresponding + // runtime values) if `(TIndex | 0x80) != 0`, then `Vboxed == V` (by value). + // For conenience, we also set this value of isboxed values, in which case + // it is equal (at compile time) to V. + Value *Vboxed; Value *TIndex; // if `V` is an unboxed (tagged) Union described by `typ`, this gives the DataType index (1-based, small int) as an i8 jl_value_t *constant; // constant value (rooted in linfo.def.roots) - Value *gcroot; // the gcroot associated with V (if it has one) jl_value_t *typ; // the original type of V, never NULL bool isboxed; // whether this value is a jl_value_t* allocated on the heap with the right type tag bool isghost; // whether this value is "ghost" @@ -411,23 +416,24 @@ struct jl_cgval_t { //} jl_cgval_t(Value *V, Value *gcroot, bool isboxed, jl_value_t *typ, Value *tindex) : // general constructor (with pointer type auto-detect) V(V), // V is allowed to be NULL in a jl_varinfo_t context, but not during codegen contexts + Vboxed(isboxed ? V : nullptr), TIndex(tindex), constant(NULL), - gcroot(gcroot), typ(typ), isboxed(isboxed), isghost(false), isimmutable(isboxed && jl_is_immutable_datatype(typ)), tbaa(isboxed ? best_tbaa(typ) : nullptr) { + assert(gcroot == nullptr); assert(!(isboxed && TIndex != NULL)); assert(TIndex == NULL || TIndex->getType() == T_int8); } jl_cgval_t(jl_value_t *typ) : // ghost value constructor V(NULL), + Vboxed(NULL), TIndex(NULL), constant(((jl_datatype_t*)typ)->instance), - gcroot(NULL), typ(typ), isboxed(false), isghost(true), @@ -439,9 +445,9 @@ struct jl_cgval_t { } jl_cgval_t(const jl_cgval_t &v, jl_value_t *typ, Value *tindex) : // copy constructor with new type V(v.V), + Vboxed(v.Vboxed), TIndex(tindex), constant(v.constant), - gcroot(v.gcroot), typ(typ), isboxed(v.isboxed), isghost(v.isghost), @@ -459,9 +465,9 @@ struct jl_cgval_t { } jl_cgval_t() : // undef / unreachable / default constructor V(UndefValue::get(T_void)), + Vboxed(NULL), TIndex(NULL), constant(NULL), - gcroot(NULL), typ(jl_bottom_type), isboxed(false), isghost(true), @@ -570,7 +576,6 @@ class jl_codectx_t { }; static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr); -static Value *emit_local_root(jl_codectx_t &ctx, jl_varinfo_t *vi = NULL); static Value *global_binding_pointer(jl_codectx_t &ctx, jl_module_t *m, jl_sym_t *s, jl_binding_t **pbnd, bool assign); static jl_cgval_t emit_checked_var(jl_codectx_t &ctx, Value *bp, jl_sym_t *name, bool isvol, MDNode *tbaa); @@ -714,8 +719,8 @@ static inline jl_cgval_t update_julia_type(jl_codectx_t &ctx, const jl_cgval_t & return v; // not worth trying to improve type info if (!isbits_spec(typ)) { // discovered that this union-split type must actually be isboxed - if (v.V) { - return jl_cgval_t(v.V, v.gcroot, true, typ, NULL); + if (v.Vboxed) { + return jl_cgval_t(v.Vboxed, nullptr, true, typ, NULL); } else { // type mismatch (there weren't any boxed values in the union) @@ -730,7 +735,7 @@ static inline jl_cgval_t update_julia_type(jl_codectx_t &ctx, const jl_cgval_t & return jl_cgval_t(v, typ, NULL); } -static jl_cgval_t convert_julia_type(jl_codectx_t &ctx, const jl_cgval_t &v, jl_value_t *typ, bool needsroot = true); +static jl_cgval_t convert_julia_type(jl_codectx_t &ctx, const jl_cgval_t &v, jl_value_t *typ); // --- allocating local variables --- @@ -811,8 +816,154 @@ static void jl_rethrow_with_add(const char *fmt, ...) jl_rethrow(); } +static jl_cgval_t convert_julia_type_union(jl_codectx_t &ctx, const jl_cgval_t &v, jl_value_t *typ) +{ + // previous value was a split union, compute new index, or box + Value *new_tindex = ConstantInt::get(T_int8, 0x80); + SmallBitVector skip_box(1, true); + Value *tindex = ctx.builder.CreateAnd(v.TIndex, ConstantInt::get(T_int8, 0x7f)); + if (jl_is_uniontype(typ)) { + // compute the TIndex mapping from v.typ -> typ + unsigned counter = 0; + for_each_uniontype_small( + // for each old union-split value + [&](unsigned idx, jl_datatype_t *jt) { + unsigned new_idx = get_box_tindex(jt, typ); + bool t; + if (new_idx) { + // found a matching element, + // match it against either the unboxed index + Value *cmp = ctx.builder.CreateICmpEQ(tindex, ConstantInt::get(T_int8, idx)); + new_tindex = ctx.builder.CreateSelect(cmp, ConstantInt::get(T_int8, new_idx), new_tindex); + t = true; + } + else if (!jl_subtype((jl_value_t*)jt, typ)) { + // new value doesn't need to be boxed + // since it isn't part of the new union + t = true; + } + else { + // will actually need to box this element + // since it appeared as a leaftype in the original type + // but not in the remark type + t = false; + } + skip_box.resize(idx + 1, t); + }, + v.typ, + counter); + } + + // some of the values are still unboxed + if (!isa(new_tindex)) { + Value *wasboxed = NULL; + // If the old value was boxed and unknown (type tag 0x80), + // it is possible that the tag was actually one of the types + // that are now explicitly represented. To find out, we need + // to compare typeof(v.Vboxed) (i.e. the type of the unknown + // value) against all the types that are now explicitly + // selected and select the appropriate one as our new tindex. + if (v.Vboxed) { + wasboxed = ctx.builder.CreateAnd(v.TIndex, ConstantInt::get(T_int8, 0x80)); + new_tindex = ctx.builder.CreateOr(wasboxed, new_tindex); + wasboxed = ctx.builder.CreateICmpNE(wasboxed, ConstantInt::get(T_int8, 0)); + + BasicBlock *currBB = ctx.builder.GetInsertBlock(); + + // We lazily create a BB for this, once we decide that we + // actually need it. + Value *union_box_dt = NULL; + BasicBlock *union_isaBB = NULL; + auto maybe_setup_union_isa = [&]() { + union_isaBB = BasicBlock::Create(jl_LLVMContext, "union_isa", ctx.f); + ctx.builder.SetInsertPoint(union_isaBB); + union_box_dt = emit_typeof(ctx, v.Vboxed); + }; + + // If we don't find a match. The type remains unknown + // (0x80). We could use `v.Tindex`, here, since we know + // it has to be 0x80, but it seems likely the backend + // will like the explicit constant better. + Value *union_box_tindex = ConstantInt::get(T_int8, 0x80); + unsigned counter = 0; + for_each_uniontype_small( + // for each new union-split value + [&](unsigned idx, jl_datatype_t *jt) { + unsigned old_idx = get_box_tindex(jt, v.typ); + if (old_idx == 0) { + // didn't handle this item before, select its new union index + maybe_setup_union_isa(); + Value *cmp = ctx.builder.CreateICmpEQ(maybe_decay_untracked(literal_pointer_val(ctx, (jl_value_t*)jt)), union_box_dt); + union_box_tindex = ctx.builder.CreateSelect(cmp, ConstantInt::get(T_int8, 0x80 | idx), union_box_tindex); + } + }, + typ, + counter); + if (union_box_dt) { + BasicBlock *postBB = BasicBlock::Create(jl_LLVMContext, "post_union_isa", ctx.f); + ctx.builder.CreateBr(postBB); + ctx.builder.SetInsertPoint(currBB); + Value *wasunknown = ctx.builder.CreateICmpEQ(v.TIndex, ConstantInt::get(T_int8, 0x80)); + ctx.builder.CreateCondBr(wasunknown, union_isaBB, postBB); + ctx.builder.SetInsertPoint(postBB); + PHINode *tindex_phi = ctx.builder.CreatePHI(T_int8, 2); + tindex_phi->addIncoming(new_tindex, currBB); + tindex_phi->addIncoming(union_box_tindex, union_isaBB); + new_tindex = tindex_phi; + } + } + if (!skip_box.all()) { + // some values weren't unboxed in the new union + // box them now (tindex above already selected 0x80 = box for them) + Value *boxv = box_union(ctx, v, skip_box); + if (v.Vboxed) { + // If the value is boxed both before and after, we don't need + // to touch it at all. Otherwise we're either transitioning + // unboxed->boxed, or leaving an unboxed value in place. + Value *isboxed = ctx.builder.CreateICmpNE( + ctx.builder.CreateAnd(new_tindex, ConstantInt::get(T_int8, 0x80)), + ConstantInt::get(T_int8, 0)); + boxv = ctx.builder.CreateSelect( + ctx.builder.CreateAnd(wasboxed, isboxed), v.Vboxed, boxv); + } + if (v.V == NULL) { + // v.V might be NULL if it was all ghost objects before + return jl_cgval_t(boxv, NULL, false, typ, new_tindex); + } else { + Value *isboxv = ctx.builder.CreateIsNotNull(boxv); + Value *slotv; + MDNode *tbaa; + bool isimmutable; + if (v.ispointer()) { + slotv = v.V; + tbaa = v.tbaa; + isimmutable = v.isimmutable; + } + else { + slotv = emit_static_alloca(ctx, v.V->getType()); + ctx.builder.CreateStore(v.V, slotv); + tbaa = tbaa_stack; + isimmutable = true; + } + slotv = ctx.builder.CreateSelect(isboxv, + decay_derived(boxv), + decay_derived(emit_bitcast(ctx, slotv, boxv->getType()))); + jl_cgval_t newv = jl_cgval_t(slotv, NULL, false, typ, new_tindex); + newv.Vboxed = boxv; + newv.tbaa = tbaa; + newv.isimmutable = isimmutable; + return newv; + } + } + } + else { + return jl_cgval_t(boxed(ctx, v), NULL, true, typ, NULL); + } + return jl_cgval_t(v, typ, new_tindex); +} + // given a value marked with type `v.typ`, compute the mapping and/or boxing to return a value of type `typ` -static jl_cgval_t convert_julia_type(jl_codectx_t &ctx, const jl_cgval_t &v, jl_value_t *typ, bool needsroot) +static jl_cgval_t convert_julia_type(jl_codectx_t &ctx, const jl_cgval_t &v, jl_value_t *typ) { if (typ == (jl_value_t*)jl_typeofbottom_type) return ghostValue(typ); // normalize TypeofBottom to Type{Union{}} @@ -825,8 +976,8 @@ static jl_cgval_t convert_julia_type(jl_codectx_t &ctx, const jl_cgval_t &v, jl_ if (jl_is_leaf_type(typ)) { if (v.TIndex && !isbits_spec(typ)) { // discovered that this union-split type must actually be isboxed - if (v.V) { - return jl_cgval_t(v.V, v.gcroot, true, typ, NULL); + if (v.Vboxed) { + return jl_cgval_t(v.Vboxed, nullptr, true, typ, NULL); } else { // type mismatch: there weren't any boxed values in the union @@ -845,145 +996,7 @@ static jl_cgval_t convert_julia_type(jl_codectx_t &ctx, const jl_cgval_t &v, jl_ else { bool makeboxed = false; if (v.TIndex) { - // previous value was a split union, compute new index, or box - new_tindex = ConstantInt::get(T_int8, 0x80); - SmallBitVector skip_box(1, true); - Value *tindex = ctx.builder.CreateAnd(v.TIndex, ConstantInt::get(T_int8, 0x7f)); - if (jl_is_uniontype(typ)) { - // compute the TIndex mapping from v.typ -> typ - unsigned counter = 0; - for_each_uniontype_small( - // for each old union-split value - [&](unsigned idx, jl_datatype_t *jt) { - unsigned new_idx = get_box_tindex(jt, typ); - bool t; - if (new_idx) { - // found a matching element, - // match it against either the unboxed index - Value *cmp = ctx.builder.CreateICmpEQ(tindex, ConstantInt::get(T_int8, idx)); - new_tindex = ctx.builder.CreateSelect(cmp, ConstantInt::get(T_int8, new_idx), new_tindex); - t = true; - } - else if (!jl_subtype((jl_value_t*)jt, typ)) { - // new value doesn't need to be boxed - // since it isn't part of the new union - t = true; - } - else { - // will actually need to box this element - // since it appeared as a leaftype in the original type - // but not in the remark type - t = false; - } - skip_box.resize(idx + 1, t); - }, - v.typ, - counter); - } - - // some of the values are still unboxed - if (!isa(new_tindex)) { - Value *wasboxed = NULL; - // check if some of the old values might have been boxed - // and copy that information over into the new tindex - if (v.ispointer() && v.V && !isa(v.V)) { - wasboxed = ctx.builder.CreateAnd(v.TIndex, ConstantInt::get(T_int8, 0x80)); - new_tindex = ctx.builder.CreateOr(wasboxed, new_tindex); - wasboxed = ctx.builder.CreateICmpNE(wasboxed, ConstantInt::get(T_int8, 0)); - - // may need to handle compute_box_tindex for some of the values - BasicBlock *currBB = ctx.builder.GetInsertBlock(); - Value *union_box_dt = NULL; - Value *union_box_tindex = ConstantInt::get(T_int8, 0x80); - unsigned counter = 0; - for_each_uniontype_small( - // for each new union-split value - [&](unsigned idx, jl_datatype_t *jt) { - unsigned old_idx = get_box_tindex(jt, v.typ); - if (old_idx == 0) { - if (!union_box_dt) { - BasicBlock *isaBB = BasicBlock::Create(jl_LLVMContext, "union_isa", ctx.f); - ctx.builder.SetInsertPoint(isaBB); - union_box_dt = emit_typeof(ctx, v.V); - } - // didn't handle this item before, select its new union index - Value *cmp = ctx.builder.CreateICmpEQ(maybe_decay_untracked(literal_pointer_val(ctx, (jl_value_t*)jt)), union_box_dt); - union_box_tindex = ctx.builder.CreateSelect(cmp, ConstantInt::get(T_int8, 0x80 | idx), union_box_tindex); - } - }, - typ, - counter); - if (union_box_dt) { - BasicBlock *isaBB = ctx.builder.GetInsertBlock(); - BasicBlock *postBB = BasicBlock::Create(jl_LLVMContext, "post_union_isa", ctx.f); - ctx.builder.CreateBr(postBB); - ctx.builder.SetInsertPoint(currBB); - Value *wasunknown = ctx.builder.CreateICmpEQ(v.TIndex, ConstantInt::get(T_int8, 0x80)); - ctx.builder.CreateCondBr(wasunknown, isaBB, postBB); - ctx.builder.SetInsertPoint(postBB); - PHINode *tindex_phi = ctx.builder.CreatePHI(T_int8, 2); - tindex_phi->addIncoming(new_tindex, currBB); - tindex_phi->addIncoming(union_box_tindex, isaBB); - new_tindex = tindex_phi; - } - - } - - if (!skip_box.all()) { - // some values weren't unboxed in the new union - // box them now (tindex above already selected 0x80 = box for them) - // root the result, and return a new mark_julia_slot over the result - Value *boxv = box_union(ctx, v, skip_box); - Value *froot = NULL; - if (needsroot) { - // build a new gc-root, as needed - froot = emit_local_root(ctx); - Value *newroot = boxv; - if (wasboxed || v.gcroot) { // oldbox might be all ghost values (which don't need roots) - // store either the old box or the new box into the gc-root (skip_box ensures these are mutually-exclusive) - // need to clone the value from `v.gcroot` if this isn't a new box - Value *oldroot; - if (v.gcroot) - oldroot = ctx.builder.CreateLoad(v.gcroot); - else - oldroot = v.V; - newroot = ctx.builder.CreateSelect(wasboxed, emit_bitcast(ctx, oldroot, boxv->getType()), newroot); - } - ctx.builder.CreateStore(newroot, froot); - } - if (v.V == NULL) { - // v.V might be NULL if it was all ghost objects before - return jl_cgval_t(boxv, froot, false, typ, new_tindex); - } - else { - Value *isboxv = ctx.builder.CreateIsNotNull(boxv); - Value *slotv; - MDNode *tbaa; - bool isimmutable; - if (v.ispointer()) { - slotv = v.V; - tbaa = v.tbaa; - isimmutable = v.isimmutable; - } - else { - slotv = emit_static_alloca(ctx, v.V->getType()); - ctx.builder.CreateStore(v.V, slotv); - tbaa = tbaa_stack; - isimmutable = true; - } - slotv = ctx.builder.CreateSelect(isboxv, - decay_derived(boxv), emit_bitcast(ctx, slotv, boxv->getType())); - jl_cgval_t newv = jl_cgval_t(slotv, froot, false, typ, new_tindex); - newv.tbaa = tbaa; - newv.isimmutable = isimmutable; - return newv; - } - } - } - else { - new_tindex = NULL; - makeboxed = true; - } + return convert_julia_type_union(ctx, v, typ); } else if (!v.isboxed && jl_is_uniontype(typ)) { // previous value was unboxed (leaftype), statically compute union tindex @@ -1981,20 +1994,6 @@ static void simple_use_analysis(jl_codectx_t &ctx, jl_value_t *expr) // ---- Get Element Pointer (GEP) instructions within the GC frame ---- -// Emit a gc-root slot indicator -static Value *emit_local_root(jl_codectx_t &ctx, jl_varinfo_t *vi) -{ - Instruction *newroot = new AllocaInst(T_prjlvalue, 0, "gcroot", /*InsertBefore*/ctx.ptlsStates); - if (vi) { - vi->boxroot->replaceAllUsesWith(newroot); - newroot->takeName(vi->boxroot); - vi->boxroot->eraseFromParent(); - vi->boxroot = newroot; - } - - return newroot; -} - static void jl_add_method_root(jl_codectx_t &ctx, jl_value_t *val) { if (jl_is_leaf_type(val) || jl_is_bool(val) || jl_is_symbol(val) || @@ -2905,9 +2904,7 @@ static jl_cgval_t emit_call_function_object(jl_method_instance_t *li, jl_llvm_fu jlretty, tindex, tbaa_stack); - // root this, if the return value was a box (tindex & 0x80) != 0 - retval.gcroot = emit_local_root(ctx); - ctx.builder.CreateStore(box, retval.gcroot); + retval.Vboxed = box; break; } case jl_returninfo_t::Ghosts: @@ -3272,7 +3269,6 @@ static jl_cgval_t emit_local(jl_codectx_t &ctx, jl_value_t *slotload) if (vi.boxroot != NULL) { Value *boxed = ctx.builder.CreateLoad(vi.boxroot, vi.isVolatile); Value *box_isnull; - v.gcroot = vi.boxroot; if (vi.usedUndef) box_isnull = ctx.builder.CreateICmpNE(boxed, maybe_decay_untracked(V_null)); if (vi.pTIndex) { @@ -3284,11 +3280,11 @@ static jl_cgval_t emit_local(jl_codectx_t &ctx, jl_value_t *slotload) if (vi.usedUndef) isnull = ctx.builder.CreateSelect(load_unbox, isnull, box_isnull); if (v.V) { // v.V will be null if it is a union of all ghost values - boxed = decay_derived(boxed); v.V = ctx.builder.CreateSelect(load_unbox, emit_bitcast(ctx, - decay_derived(v.V), boxed->getType()), boxed); + decay_derived(v.V), boxed->getType()), decay_derived(boxed)); } else v.V = boxed; + v.Vboxed = boxed; v = update_julia_type(ctx, v, typ); } else { @@ -3372,26 +3368,15 @@ static void emit_assignment(jl_codectx_t &ctx, jl_value_t *l, jl_value_t *r) size_t min_align; dest = try_emit_union_alloca(ctx, ((jl_uniontype_t*)jt), allunbox, min_align); Value *isboxed = NULL; - if (slot.ispointer() && slot.V != NULL && !isa(slot.V)) { + if (slot.isboxed || slot.Vboxed != nullptr) { isboxed = ctx.builder.CreateICmpNE( ctx.builder.CreateAnd(slot.TIndex, ConstantInt::get(T_int8, 0x80)), ConstantInt::get(T_int8, 0)); } if (dest) emit_unionmove(ctx, dest, slot, isboxed, false, NULL); - Value *gcroot = NULL; if (isboxed) { - Value *box; - if (slot.gcroot) { - gcroot = emit_local_root(ctx); - // This might load the wrong object in general, but if it gets selected, below, - // we know that it was in fact the one we wanted. - box = ctx.builder.CreateLoad(slot.gcroot); - } else { - gcroot = emit_static_alloca(ctx, T_pjlvalue); - box = V_null; - } - ctx.builder.CreateStore(box, gcroot); + Value *box = slot.Vboxed ? slot.Vboxed : V_null; if (dest) // might be all ghost values dest = ctx.builder.CreateSelect(isboxed, decay_derived(box), @@ -3402,8 +3387,9 @@ static void emit_assignment(jl_codectx_t &ctx, jl_value_t *l, jl_value_t *r) else { assert(allunbox && "Failed to allocate correct union-type storage."); } + Value *box = slot.Vboxed; slot = mark_julia_slot(dest, slot.typ, slot.TIndex, tbaa_stack); - slot.gcroot = gcroot; + slot.Vboxed = box; } else { bool isboxed; @@ -3449,14 +3435,9 @@ static void emit_assignment(jl_codectx_t &ctx, jl_value_t *l, jl_value_t *r) if (!vi.used) return; - bool needs_root = false; - if ((!vi.isSA && rval_info.gcroot) || !rval_info.isboxed) - // rval needed a gcroot, so lval will need one too - needs_root = true; - // convert rval-type to lval-type jl_value_t *slot_type = vi.value.typ; - rval_info = convert_julia_type(ctx, rval_info, slot_type, /*needs-root*/true); + rval_info = convert_julia_type(ctx, rval_info, slot_type); if (rval_info.typ == jl_bottom_type) return; @@ -3491,18 +3472,13 @@ static void emit_assignment(jl_codectx_t &ctx, jl_value_t *l, jl_value_t *r) // store boxed variables Value *isboxed = NULL; if (vi.boxroot) { - if (isa(vi.boxroot) && needs_root) - emit_local_root(ctx, &vi); // promote variable slot to a gcroot Value *rval; if (vi.pTIndex && rval_info.TIndex) { ctx.builder.CreateStore(rval_info.TIndex, vi.pTIndex, vi.isVolatile); isboxed = ctx.builder.CreateICmpNE( ctx.builder.CreateAnd(rval_info.TIndex, ConstantInt::get(T_int8, 0x80)), ConstantInt::get(T_int8, 0)); - rval = maybe_decay_untracked(V_null); - if (rval_info.ispointer() && rval_info.V != NULL && !isa(rval_info.V) && - !(isa(isboxed) && cast(isboxed)->isZero())) // might be all ghost values or otherwise definitely not boxed - rval = ctx.builder.CreateLoad(rval_info.gcroot); + rval = maybe_decay_untracked(rval_info.Vboxed ? rval_info.Vboxed : V_null); assert(!vi.value.constant); } else { @@ -4576,8 +4552,7 @@ static Function *gen_jlcall_wrapper(jl_method_instance_t *lam, const jl_returnin jlretty, ctx.builder.CreateExtractValue(call, 1), tbaa_stack); - retval.gcroot = emit_local_root(ctx); - ctx.builder.CreateStore(ctx.builder.CreateExtractValue(call, 0), retval.gcroot); + retval.Vboxed = ctx.builder.CreateExtractValue(call, 0); break; case jl_returninfo_t::Ghosts: retval = mark_julia_slot(NULL, jlretty, call, tbaa_stack); @@ -5254,10 +5229,8 @@ static std::unique_ptr emit_function( } } else { - Value *argp = boxed(ctx, theArg); // skip the temporary gcroot since it would be folded to argp anyways + Value *argp = boxed(ctx, theArg); ctx.builder.CreateStore(argp, vi.boxroot); - if (!theArg.isboxed) - emit_local_root(ctx, &vi); // create a root for vi } // get arrayvar data if applicable if (arrayvars.find(i) != arrayvars.end()) { @@ -5284,7 +5257,6 @@ static std::unique_ptr emit_function( ConstantInt::get(T_int32, nreq - 1)) }); restTuple->setAttributes(jltuple_func->getAttributes()); ctx.builder.CreateStore(restTuple, vi.boxroot); - emit_local_root(ctx, &vi); // create a root for vi } } @@ -5537,7 +5509,7 @@ static std::unique_ptr emit_function( // this is basically a copy of emit_assignment, // but where the assignment slot is the retval jl_cgval_t retvalinfo = emit_expr(ctx, jl_exprarg(expr, 0)); - retvalinfo = convert_julia_type(ctx, retvalinfo, jlrettype, /*needs-root*/false); + retvalinfo = convert_julia_type(ctx, retvalinfo, jlrettype); if (retvalinfo.typ == jl_bottom_type) { ctx.builder.CreateUnreachable(); find_next_stmt(-1); @@ -5572,20 +5544,13 @@ static std::unique_ptr emit_function( } else { data = maybe_decay_untracked(V_null); - if (retvalinfo.ispointer() && !isa(retvalinfo.V)) { + if (retvalinfo.Vboxed) { // also need to account for the possibility the return object is boxed // and avoid / skip copying it to the stack isboxed_union = ctx.builder.CreateICmpNE( ctx.builder.CreateAnd(tindex, ConstantInt::get(T_int8, 0x80)), ConstantInt::get(T_int8, 0)); - // Lift the select, because gcroot may be NULL if - // there's no boxed value. - if (isa(isboxed_union)) - data = cast(isboxed_union)->isZero() ? data : ctx.builder.CreateLoad(retvalinfo.gcroot); - else - data = ctx.builder.CreateSelect(isboxed_union, - ctx.builder.CreateLoad(retvalinfo.gcroot), - data); + data = ctx.builder.CreateSelect(isboxed_union, retvalinfo.Vboxed, data); } } } From 4f8a6128ea8a4bef8a096d20d777752e6442b83e Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Mon, 4 Sep 2017 19:49:13 -0400 Subject: [PATCH 265/324] `unsafe_load` -> `@inbounds codeunit` Addresses post-commit review in #23562. --- base/strings/string.jl | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/base/strings/string.jl b/base/strings/string.jl index 42296c4fa9ad1..d81fe940fc8aa 100644 --- a/base/strings/string.jl +++ b/base/strings/string.jl @@ -59,13 +59,6 @@ String(s::Symbol) = unsafe_string(Cstring(s)) pointer(s::String) = unsafe_convert(Ptr{UInt8}, s) pointer(s::String, i::Integer) = pointer(s)+(i-1) -function unsafe_load(s::String, i::Integer=1) - ptr = pointer(s, i) - r = unsafe_load(ptr) - Core.gcuse(s) - r -end - sizeof(s::String) = Core.sizeof(s) """ @@ -80,7 +73,10 @@ codeunit(s::AbstractString, i::Integer) @boundscheck if (i < 1) | (i > sizeof(s)) throw(BoundsError(s,i)) end - unsafe_load(s, i) + ptr = pointer(s, i) + r = unsafe_load(ptr) + Core.gcuse(s) + r end write(io::IO, s::String) = unsafe_write(io, pointer(s), reinterpret(UInt, sizeof(s))) @@ -168,7 +164,7 @@ const utf8_trailing = [ function endof(s::String) i = sizeof(s) - while i > 0 && is_valid_continuation(unsafe_load(s, i)) + @inbounds while i > 0 && is_valid_continuation(codeunit(s, i)) i -= 1 end i @@ -176,24 +172,24 @@ end function length(s::String) cnum = 0 - for i = 1:sizeof(s) - cnum += !is_valid_continuation(unsafe_load(s, i)) + @inbounds for i = 1:sizeof(s) + cnum += !is_valid_continuation(codeunit(s, i)) end cnum end @noinline function slow_utf8_next(s::String, b::UInt8, i::Int, l::Int) - if is_valid_continuation(b) - throw(UnicodeError(UTF_ERR_INVALID_INDEX, i, unsafe_load(s, i))) + @inbounds if is_valid_continuation(b) + throw(UnicodeError(UTF_ERR_INVALID_INDEX, i, codeunit(s, i))) end trailing = utf8_trailing[b + 1] if l < i + trailing return '\ufffd', i+1 end c::UInt32 = 0 - for j = 1:(trailing + 1) + @inbounds for j = 1:(trailing + 1) c <<= 6 - c += unsafe_load(s, i) + c += codeunit(s, i) i += 1 end c -= utf8_offset[trailing + 1] @@ -211,7 +207,7 @@ done(s::String, state) = state > sizeof(s) @boundscheck if (i < 1) | (i > sizeof(s)) throw(BoundsError(s,i)) end - b = unsafe_load(s, i) + @inbounds b = codeunit(s, i) if b < 0x80 return Char(b), i + 1 end @@ -229,7 +225,7 @@ end function reverseind(s::String, i::Integer) j = sizeof(s) + 1 - i - while is_valid_continuation(unsafe_load(s, j)) + @inbounds while is_valid_continuation(codeunit(s, j)) j -= 1 end return j @@ -238,7 +234,7 @@ end ## overload methods for efficiency ## isvalid(s::String, i::Integer) = - (1 <= i <= sizeof(s)) && !is_valid_continuation(unsafe_load(s, i)) + (1 <= i <= sizeof(s)) && ((@inbounds b = codeunit(s, i)); !is_valid_continuation(b)) function getindex(s::String, r::UnitRange{Int}) isempty(r) && return "" @@ -263,7 +259,7 @@ function search(s::String, c::Char, i::Integer = 1) i == sizeof(s) + 1 && return 0 throw(BoundsError(s, i)) end - if is_valid_continuation(codeunit(s,i)) + @inbounds if is_valid_continuation(codeunit(s,i)) throw(UnicodeError(UTF_ERR_INVALID_INDEX, i, codeunit(s,i))) end c < Char(0x80) && return search(s, c%UInt8, i) @@ -441,7 +437,7 @@ function repeat(s::String, r::Integer) n = sizeof(s) out = _string_n(n*r) if n == 1 # common case: repeating a single ASCII char - ccall(:memset, Ptr{Void}, (Ptr{UInt8}, Cint, Csize_t), out, unsafe_load(s), r) + @inbounds ccall(:memset, Ptr{Void}, (Ptr{UInt8}, Cint, Csize_t), out, codeunit(s, 1), r) else for i=1:r unsafe_copy!(pointer(out, 1+(i-1)*n), pointer(s), n) From 4c0a3e64fe7d71b4dbeb4c802d0ecda26d27c8c8 Mon Sep 17 00:00:00 2001 From: quinnj Date: Mon, 4 Sep 2017 19:46:47 -0600 Subject: [PATCH 266/324] Probably due to #23397; ensure the istart variable is declared outside the while loop --- base/cartesian.jl | 1 + test/cartesian.jl | 2 ++ 2 files changed, 3 insertions(+) diff --git a/base/cartesian.jl b/base/cartesian.jl index 6762b4db15461..b944617726345 100644 --- a/base/cartesian.jl +++ b/base/cartesian.jl @@ -302,6 +302,7 @@ function lreplace!(str::AbstractString, r::LReplace) pat = r.pat_str j = start(pat) matching = false + local istart::Int while !done(str, i) cstr, i = next(str, i) if !matching diff --git a/test/cartesian.jl b/test/cartesian.jl index be2605d200143..cf8ed426468c5 100644 --- a/test/cartesian.jl +++ b/test/cartesian.jl @@ -3,3 +3,5 @@ @test Base.Cartesian.exprresolve(:(1 + 3)) == 4 ex = Base.Cartesian.exprresolve(:(if 5 > 4; :x; else :y; end)) @test ex.args[2] == QuoteNode(:x) + +@test Base.Cartesian.lreplace!("val_col", Base.Cartesian.LReplace{String}(:col, "col", 1)) == "val_1" \ No newline at end of file From 7b6a55410734795771c650f25f4256af6ca114b7 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Sun, 3 Sep 2017 16:38:18 -0400 Subject: [PATCH 267/324] Untangle legacy .gcroot field When the new gc root placement pass was introduced, the .gcroot field lost its original meaning and became mostly superfluous. However, the codegen code for unions still used it to get a referenced to the boxed part of the split union. Clean that up by introducing an explicit field for that purpose and removing the .gcroot field. --- src/ccall.cpp | 5 +- src/cgutils.cpp | 38 ++--- src/codegen.cpp | 391 ++++++++++++++++++++++-------------------------- 3 files changed, 199 insertions(+), 235 deletions(-) diff --git a/src/ccall.cpp b/src/ccall.cpp index 118e4ae3b2b84..7512f24f0648b 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -1583,9 +1583,8 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) if (jl_is_long(argi_root)) continue; jl_cgval_t arg_root = emit_expr(ctx, argi_root); - Value *gcuse = arg_root.gcroot ? ctx.builder.CreateLoad(arg_root.gcroot) : arg_root.V; - if (gcuse) { - gc_uses.push_back(gcuse); + if (arg_root.Vboxed || arg_root.V) { + gc_uses.push_back(arg_root.Vboxed ? arg_root.Vboxed : arg_root.V); } } diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 6d350e9ab94a9..6dbb982e7223e 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -728,7 +728,7 @@ static jl_cgval_t emit_typeof(jl_codectx_t &ctx, const jl_cgval_t &p) else { // See note above in emit_typeof(Value*), we can't tell the system // about this until we've cleared the GC bits. - pdatatype = emit_bitcast(ctx, emit_typeptr_addr(ctx, ctx.builder.CreateLoad(p.gcroot)), T_ppjlvalue); + pdatatype = emit_bitcast(ctx, emit_typeptr_addr(ctx, p.Vboxed), T_ppjlvalue); } counter = 0; for_each_uniontype_small( @@ -1028,7 +1028,7 @@ static std::pair emit_isa(jl_codectx_t &ctx, const jl_cgval_t &x, Value *xtindex = ctx.builder.CreateAnd(x.TIndex, ConstantInt::get(T_int8, 0x7f)); return std::make_pair(ctx.builder.CreateICmpEQ(xtindex, ConstantInt::get(T_int8, tindex)), false); } - else { + else if (x.Vboxed) { // test for (x.TIndex == 0x80 && typeof(x.V) == type) Value *isboxed = ctx.builder.CreateICmpEQ(x.TIndex, ConstantInt::get(T_int8, 0x80)); BasicBlock *currBB = ctx.builder.GetInsertBlock(); @@ -1036,7 +1036,7 @@ static std::pair emit_isa(jl_codectx_t &ctx, const jl_cgval_t &x, BasicBlock *postBB = BasicBlock::Create(jl_LLVMContext, "post_isa", ctx.f); ctx.builder.CreateCondBr(isboxed, isaBB, postBB); ctx.builder.SetInsertPoint(isaBB); - Value *istype_boxed = ctx.builder.CreateICmpEQ(emit_typeof(ctx, x.V), + Value *istype_boxed = ctx.builder.CreateICmpEQ(emit_typeof(ctx, x.Vboxed), maybe_decay_untracked(literal_pointer_val(ctx, type))); ctx.builder.CreateBr(postBB); ctx.builder.SetInsertPoint(postBB); @@ -1044,6 +1044,9 @@ static std::pair emit_isa(jl_codectx_t &ctx, const jl_cgval_t &x, istype->addIncoming(ConstantInt::get(T_int1, 0), currBB); istype->addIncoming(istype_boxed, isaBB); return std::make_pair(istype, false); + } else { + // handle the case where we know that `x` is unboxed (but of unknown type), but that leaf type `type` cannot be unboxed + return std::make_pair(ConstantInt::get(T_int1, 0), false); } } return std::make_pair(ctx.builder.CreateICmpEQ(emit_typeof_boxed(ctx, x), @@ -1299,7 +1302,6 @@ static bool emit_getfield_unknownidx(jl_codectx_t &ctx, Type *fty = julia_type_to_llvm(jt); Value *addr = ctx.builder.CreateGEP(emit_bitcast(ctx, decay_derived(ptr), PointerType::get(fty,0)), idx); *ret = mark_julia_slot(addr, jt, NULL, strct.tbaa); - ret->gcroot = strct.gcroot; ret->isimmutable = strct.isimmutable; return true; } @@ -1395,7 +1397,6 @@ static jl_cgval_t emit_getfield_knownidx(jl_codectx_t &ctx, const jl_cgval_t &st Value *ptindex = ctx.builder.CreateGEP(T_int8, emit_bitcast(ctx, addr, T_pint8), ConstantInt::get(T_size, fsz - 1)); Value *tindex = ctx.builder.CreateNUWAdd(ConstantInt::get(T_int8, 1), ctx.builder.CreateLoad(ptindex)); bool isimmutable = strct.isimmutable; - Value *gcroot = strct.gcroot; if (jt->mutabl) { // move value to an immutable stack slot Type *AT = ArrayType::get(IntegerType::get(jl_LLVMContext, 8 * align), (fsz + align - 2) / align); @@ -1406,18 +1407,15 @@ static jl_cgval_t emit_getfield_knownidx(jl_codectx_t &ctx, const jl_cgval_t &st ctx.builder.CreateMemCpy(lv, addr, nbytes, align); addr = lv; isimmutable = true; - gcroot = NULL; } jl_cgval_t fieldval = mark_julia_slot(addr, jfty, tindex, strct.tbaa); fieldval.isimmutable = isimmutable; - fieldval.gcroot = gcroot; return fieldval; } else if (!jt->mutabl) { // just compute the pointer and let user load it when necessary jl_cgval_t fieldval = mark_julia_slot(addr, jfty, NULL, strct.tbaa); fieldval.isimmutable = strct.isimmutable; - fieldval.gcroot = strct.gcroot; return fieldval; } return typed_load(ctx, addr, ConstantInt::get(T_size, 0), jfty, strct.tbaa, true, align); @@ -1905,6 +1903,13 @@ static Value *compute_tindex_unboxed(jl_codectx_t &ctx, const jl_cgval_t &val, j return ctx.builder.CreateAnd(val.TIndex, ConstantInt::get(T_int8, 0x7f)); } +/* + * Box unboxed values in a union. Optionally, skip certain unboxed values, + * returning `V_null` in one of the skipped cases. If `skip` is not empty, + * skip[0] (corresponding to unknown boxed) must always be set. In that + * case, the calling code must separately deal with the case where + * `vinfo` is already an unkown boxed union (union tag 0x80). + */ static Value *box_union(jl_codectx_t &ctx, const jl_cgval_t &vinfo, const SmallBitVector &skip) { // given vinfo::Union{T, S}, emit IR of the form: @@ -1955,13 +1960,12 @@ static Value *box_union(jl_codectx_t &ctx, const jl_cgval_t &vinfo, const SmallB vinfo.typ, counter); ctx.builder.SetInsertPoint(defaultBB); - if (skip.size() > 0 && skip[0]) { - // skip[0] specifies where to return NULL or the original pointer - // if the value was not handled above + if (skip.size() > 0) { + assert(skip[0]); box_merge->addIncoming(maybe_decay_untracked(V_null), defaultBB); ctx.builder.CreateBr(postBB); } - else if ((vinfo.V == NULL || isa(vinfo.V)) && !vinfo.gcroot) { + else if (!vinfo.Vboxed) { Function *trap_func = Intrinsic::getDeclaration( ctx.f->getParent(), Intrinsic::trap); @@ -1969,9 +1973,7 @@ static Value *box_union(jl_codectx_t &ctx, const jl_cgval_t &vinfo, const SmallB ctx.builder.CreateUnreachable(); } else { - // We're guaranteed here that Load(.gcroot) == .V, because we have determined - // that this union is a boxed value, rather than an interior pointer of some sort - box_merge->addIncoming(ctx.builder.CreateLoad(vinfo.gcroot), defaultBB); + box_merge->addIncoming(vinfo.Vboxed, defaultBB); ctx.builder.CreateBr(postBB); } ctx.builder.SetInsertPoint(postBB); @@ -1990,10 +1992,8 @@ static Value *boxed(jl_codectx_t &ctx, const jl_cgval_t &vinfo) if (vinfo.constant) return maybe_decay_untracked(literal_pointer_val(ctx, vinfo.constant)); if (vinfo.isboxed) { - assert(vinfo.V && "Missing value for box."); - // We're guaranteed here that Load(.gcroot) == .V, because we have determined - // that this value is a box, so if it has a gcroot, that's where the value is. - return vinfo.gcroot ? ctx.builder.CreateLoad(vinfo.gcroot) : vinfo.V; + assert(vinfo.V == vinfo.Vboxed); + return vinfo.V; } Value *box; diff --git a/src/codegen.cpp b/src/codegen.cpp index 9aee554c1e84f..ba9e7aa12b192 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -391,9 +391,14 @@ static MDNode *best_tbaa(jl_value_t *jt) { // metadata tracking for a llvm Value* during codegen struct jl_cgval_t { Value *V; // may be of type T* or T, or set to NULL if ghost (or if the value has not been initialized yet, for a variable definition) + // For unions, we may need to keep a reference to the boxed part individually. + // If this is non-NULL, then, at runtime, we satisfy the invariant that (for the corresponding + // runtime values) if `(TIndex | 0x80) != 0`, then `Vboxed == V` (by value). + // For conenience, we also set this value of isboxed values, in which case + // it is equal (at compile time) to V. + Value *Vboxed; Value *TIndex; // if `V` is an unboxed (tagged) Union described by `typ`, this gives the DataType index (1-based, small int) as an i8 jl_value_t *constant; // constant value (rooted in linfo.def.roots) - Value *gcroot; // the gcroot associated with V (if it has one) jl_value_t *typ; // the original type of V, never NULL bool isboxed; // whether this value is a jl_value_t* allocated on the heap with the right type tag bool isghost; // whether this value is "ghost" @@ -411,23 +416,24 @@ struct jl_cgval_t { //} jl_cgval_t(Value *V, Value *gcroot, bool isboxed, jl_value_t *typ, Value *tindex) : // general constructor (with pointer type auto-detect) V(V), // V is allowed to be NULL in a jl_varinfo_t context, but not during codegen contexts + Vboxed(isboxed ? V : nullptr), TIndex(tindex), constant(NULL), - gcroot(gcroot), typ(typ), isboxed(isboxed), isghost(false), isimmutable(isboxed && jl_is_immutable_datatype(typ)), tbaa(isboxed ? best_tbaa(typ) : nullptr) { + assert(gcroot == nullptr); assert(!(isboxed && TIndex != NULL)); assert(TIndex == NULL || TIndex->getType() == T_int8); } jl_cgval_t(jl_value_t *typ) : // ghost value constructor V(NULL), + Vboxed(NULL), TIndex(NULL), constant(((jl_datatype_t*)typ)->instance), - gcroot(NULL), typ(typ), isboxed(false), isghost(true), @@ -439,9 +445,9 @@ struct jl_cgval_t { } jl_cgval_t(const jl_cgval_t &v, jl_value_t *typ, Value *tindex) : // copy constructor with new type V(v.V), + Vboxed(v.Vboxed), TIndex(tindex), constant(v.constant), - gcroot(v.gcroot), typ(typ), isboxed(v.isboxed), isghost(v.isghost), @@ -459,9 +465,9 @@ struct jl_cgval_t { } jl_cgval_t() : // undef / unreachable / default constructor V(UndefValue::get(T_void)), + Vboxed(NULL), TIndex(NULL), constant(NULL), - gcroot(NULL), typ(jl_bottom_type), isboxed(false), isghost(true), @@ -570,7 +576,6 @@ class jl_codectx_t { }; static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr); -static Value *emit_local_root(jl_codectx_t &ctx, jl_varinfo_t *vi = NULL); static Value *global_binding_pointer(jl_codectx_t &ctx, jl_module_t *m, jl_sym_t *s, jl_binding_t **pbnd, bool assign); static jl_cgval_t emit_checked_var(jl_codectx_t &ctx, Value *bp, jl_sym_t *name, bool isvol, MDNode *tbaa); @@ -714,8 +719,8 @@ static inline jl_cgval_t update_julia_type(jl_codectx_t &ctx, const jl_cgval_t & return v; // not worth trying to improve type info if (!isbits_spec(typ)) { // discovered that this union-split type must actually be isboxed - if (v.V) { - return jl_cgval_t(v.V, v.gcroot, true, typ, NULL); + if (v.Vboxed) { + return jl_cgval_t(v.Vboxed, nullptr, true, typ, NULL); } else { // type mismatch (there weren't any boxed values in the union) @@ -730,7 +735,7 @@ static inline jl_cgval_t update_julia_type(jl_codectx_t &ctx, const jl_cgval_t & return jl_cgval_t(v, typ, NULL); } -static jl_cgval_t convert_julia_type(jl_codectx_t &ctx, const jl_cgval_t &v, jl_value_t *typ, bool needsroot = true); +static jl_cgval_t convert_julia_type(jl_codectx_t &ctx, const jl_cgval_t &v, jl_value_t *typ); // --- allocating local variables --- @@ -811,8 +816,154 @@ static void jl_rethrow_with_add(const char *fmt, ...) jl_rethrow(); } +static jl_cgval_t convert_julia_type_union(jl_codectx_t &ctx, const jl_cgval_t &v, jl_value_t *typ) +{ + // previous value was a split union, compute new index, or box + Value *new_tindex = ConstantInt::get(T_int8, 0x80); + SmallBitVector skip_box(1, true); + Value *tindex = ctx.builder.CreateAnd(v.TIndex, ConstantInt::get(T_int8, 0x7f)); + if (jl_is_uniontype(typ)) { + // compute the TIndex mapping from v.typ -> typ + unsigned counter = 0; + for_each_uniontype_small( + // for each old union-split value + [&](unsigned idx, jl_datatype_t *jt) { + unsigned new_idx = get_box_tindex(jt, typ); + bool t; + if (new_idx) { + // found a matching element, + // match it against either the unboxed index + Value *cmp = ctx.builder.CreateICmpEQ(tindex, ConstantInt::get(T_int8, idx)); + new_tindex = ctx.builder.CreateSelect(cmp, ConstantInt::get(T_int8, new_idx), new_tindex); + t = true; + } + else if (!jl_subtype((jl_value_t*)jt, typ)) { + // new value doesn't need to be boxed + // since it isn't part of the new union + t = true; + } + else { + // will actually need to box this element + // since it appeared as a leaftype in the original type + // but not in the remark type + t = false; + } + skip_box.resize(idx + 1, t); + }, + v.typ, + counter); + } + + // some of the values are still unboxed + if (!isa(new_tindex)) { + Value *wasboxed = NULL; + // If the old value was boxed and unknown (type tag 0x80), + // it is possible that the tag was actually one of the types + // that are now explicitly represented. To find out, we need + // to compare typeof(v.Vboxed) (i.e. the type of the unknown + // value) against all the types that are now explicitly + // selected and select the appropriate one as our new tindex. + if (v.Vboxed) { + wasboxed = ctx.builder.CreateAnd(v.TIndex, ConstantInt::get(T_int8, 0x80)); + new_tindex = ctx.builder.CreateOr(wasboxed, new_tindex); + wasboxed = ctx.builder.CreateICmpNE(wasboxed, ConstantInt::get(T_int8, 0)); + + BasicBlock *currBB = ctx.builder.GetInsertBlock(); + + // We lazily create a BB for this, once we decide that we + // actually need it. + Value *union_box_dt = NULL; + BasicBlock *union_isaBB = NULL; + auto maybe_setup_union_isa = [&]() { + union_isaBB = BasicBlock::Create(jl_LLVMContext, "union_isa", ctx.f); + ctx.builder.SetInsertPoint(union_isaBB); + union_box_dt = emit_typeof(ctx, v.Vboxed); + }; + + // If we don't find a match. The type remains unknown + // (0x80). We could use `v.Tindex`, here, since we know + // it has to be 0x80, but it seems likely the backend + // will like the explicit constant better. + Value *union_box_tindex = ConstantInt::get(T_int8, 0x80); + unsigned counter = 0; + for_each_uniontype_small( + // for each new union-split value + [&](unsigned idx, jl_datatype_t *jt) { + unsigned old_idx = get_box_tindex(jt, v.typ); + if (old_idx == 0) { + // didn't handle this item before, select its new union index + maybe_setup_union_isa(); + Value *cmp = ctx.builder.CreateICmpEQ(maybe_decay_untracked(literal_pointer_val(ctx, (jl_value_t*)jt)), union_box_dt); + union_box_tindex = ctx.builder.CreateSelect(cmp, ConstantInt::get(T_int8, 0x80 | idx), union_box_tindex); + } + }, + typ, + counter); + if (union_box_dt) { + BasicBlock *postBB = BasicBlock::Create(jl_LLVMContext, "post_union_isa", ctx.f); + ctx.builder.CreateBr(postBB); + ctx.builder.SetInsertPoint(currBB); + Value *wasunknown = ctx.builder.CreateICmpEQ(v.TIndex, ConstantInt::get(T_int8, 0x80)); + ctx.builder.CreateCondBr(wasunknown, union_isaBB, postBB); + ctx.builder.SetInsertPoint(postBB); + PHINode *tindex_phi = ctx.builder.CreatePHI(T_int8, 2); + tindex_phi->addIncoming(new_tindex, currBB); + tindex_phi->addIncoming(union_box_tindex, union_isaBB); + new_tindex = tindex_phi; + } + } + if (!skip_box.all()) { + // some values weren't unboxed in the new union + // box them now (tindex above already selected 0x80 = box for them) + Value *boxv = box_union(ctx, v, skip_box); + if (v.Vboxed) { + // If the value is boxed both before and after, we don't need + // to touch it at all. Otherwise we're either transitioning + // unboxed->boxed, or leaving an unboxed value in place. + Value *isboxed = ctx.builder.CreateICmpNE( + ctx.builder.CreateAnd(new_tindex, ConstantInt::get(T_int8, 0x80)), + ConstantInt::get(T_int8, 0)); + boxv = ctx.builder.CreateSelect( + ctx.builder.CreateAnd(wasboxed, isboxed), v.Vboxed, boxv); + } + if (v.V == NULL) { + // v.V might be NULL if it was all ghost objects before + return jl_cgval_t(boxv, NULL, false, typ, new_tindex); + } else { + Value *isboxv = ctx.builder.CreateIsNotNull(boxv); + Value *slotv; + MDNode *tbaa; + bool isimmutable; + if (v.ispointer()) { + slotv = v.V; + tbaa = v.tbaa; + isimmutable = v.isimmutable; + } + else { + slotv = emit_static_alloca(ctx, v.V->getType()); + ctx.builder.CreateStore(v.V, slotv); + tbaa = tbaa_stack; + isimmutable = true; + } + slotv = ctx.builder.CreateSelect(isboxv, + decay_derived(boxv), + decay_derived(emit_bitcast(ctx, slotv, boxv->getType()))); + jl_cgval_t newv = jl_cgval_t(slotv, NULL, false, typ, new_tindex); + newv.Vboxed = boxv; + newv.tbaa = tbaa; + newv.isimmutable = isimmutable; + return newv; + } + } + } + else { + return jl_cgval_t(boxed(ctx, v), NULL, true, typ, NULL); + } + return jl_cgval_t(v, typ, new_tindex); +} + // given a value marked with type `v.typ`, compute the mapping and/or boxing to return a value of type `typ` -static jl_cgval_t convert_julia_type(jl_codectx_t &ctx, const jl_cgval_t &v, jl_value_t *typ, bool needsroot) +static jl_cgval_t convert_julia_type(jl_codectx_t &ctx, const jl_cgval_t &v, jl_value_t *typ) { if (typ == (jl_value_t*)jl_typeofbottom_type) return ghostValue(typ); // normalize TypeofBottom to Type{Union{}} @@ -825,8 +976,8 @@ static jl_cgval_t convert_julia_type(jl_codectx_t &ctx, const jl_cgval_t &v, jl_ if (jl_is_leaf_type(typ)) { if (v.TIndex && !isbits_spec(typ)) { // discovered that this union-split type must actually be isboxed - if (v.V) { - return jl_cgval_t(v.V, v.gcroot, true, typ, NULL); + if (v.Vboxed) { + return jl_cgval_t(v.Vboxed, nullptr, true, typ, NULL); } else { // type mismatch: there weren't any boxed values in the union @@ -845,145 +996,7 @@ static jl_cgval_t convert_julia_type(jl_codectx_t &ctx, const jl_cgval_t &v, jl_ else { bool makeboxed = false; if (v.TIndex) { - // previous value was a split union, compute new index, or box - new_tindex = ConstantInt::get(T_int8, 0x80); - SmallBitVector skip_box(1, true); - Value *tindex = ctx.builder.CreateAnd(v.TIndex, ConstantInt::get(T_int8, 0x7f)); - if (jl_is_uniontype(typ)) { - // compute the TIndex mapping from v.typ -> typ - unsigned counter = 0; - for_each_uniontype_small( - // for each old union-split value - [&](unsigned idx, jl_datatype_t *jt) { - unsigned new_idx = get_box_tindex(jt, typ); - bool t; - if (new_idx) { - // found a matching element, - // match it against either the unboxed index - Value *cmp = ctx.builder.CreateICmpEQ(tindex, ConstantInt::get(T_int8, idx)); - new_tindex = ctx.builder.CreateSelect(cmp, ConstantInt::get(T_int8, new_idx), new_tindex); - t = true; - } - else if (!jl_subtype((jl_value_t*)jt, typ)) { - // new value doesn't need to be boxed - // since it isn't part of the new union - t = true; - } - else { - // will actually need to box this element - // since it appeared as a leaftype in the original type - // but not in the remark type - t = false; - } - skip_box.resize(idx + 1, t); - }, - v.typ, - counter); - } - - // some of the values are still unboxed - if (!isa(new_tindex)) { - Value *wasboxed = NULL; - // check if some of the old values might have been boxed - // and copy that information over into the new tindex - if (v.ispointer() && v.V && !isa(v.V)) { - wasboxed = ctx.builder.CreateAnd(v.TIndex, ConstantInt::get(T_int8, 0x80)); - new_tindex = ctx.builder.CreateOr(wasboxed, new_tindex); - wasboxed = ctx.builder.CreateICmpNE(wasboxed, ConstantInt::get(T_int8, 0)); - - // may need to handle compute_box_tindex for some of the values - BasicBlock *currBB = ctx.builder.GetInsertBlock(); - Value *union_box_dt = NULL; - Value *union_box_tindex = ConstantInt::get(T_int8, 0x80); - unsigned counter = 0; - for_each_uniontype_small( - // for each new union-split value - [&](unsigned idx, jl_datatype_t *jt) { - unsigned old_idx = get_box_tindex(jt, v.typ); - if (old_idx == 0) { - if (!union_box_dt) { - BasicBlock *isaBB = BasicBlock::Create(jl_LLVMContext, "union_isa", ctx.f); - ctx.builder.SetInsertPoint(isaBB); - union_box_dt = emit_typeof(ctx, v.V); - } - // didn't handle this item before, select its new union index - Value *cmp = ctx.builder.CreateICmpEQ(maybe_decay_untracked(literal_pointer_val(ctx, (jl_value_t*)jt)), union_box_dt); - union_box_tindex = ctx.builder.CreateSelect(cmp, ConstantInt::get(T_int8, 0x80 | idx), union_box_tindex); - } - }, - typ, - counter); - if (union_box_dt) { - BasicBlock *isaBB = ctx.builder.GetInsertBlock(); - BasicBlock *postBB = BasicBlock::Create(jl_LLVMContext, "post_union_isa", ctx.f); - ctx.builder.CreateBr(postBB); - ctx.builder.SetInsertPoint(currBB); - Value *wasunknown = ctx.builder.CreateICmpEQ(v.TIndex, ConstantInt::get(T_int8, 0x80)); - ctx.builder.CreateCondBr(wasunknown, isaBB, postBB); - ctx.builder.SetInsertPoint(postBB); - PHINode *tindex_phi = ctx.builder.CreatePHI(T_int8, 2); - tindex_phi->addIncoming(new_tindex, currBB); - tindex_phi->addIncoming(union_box_tindex, isaBB); - new_tindex = tindex_phi; - } - - } - - if (!skip_box.all()) { - // some values weren't unboxed in the new union - // box them now (tindex above already selected 0x80 = box for them) - // root the result, and return a new mark_julia_slot over the result - Value *boxv = box_union(ctx, v, skip_box); - Value *froot = NULL; - if (needsroot) { - // build a new gc-root, as needed - froot = emit_local_root(ctx); - Value *newroot = boxv; - if (wasboxed || v.gcroot) { // oldbox might be all ghost values (which don't need roots) - // store either the old box or the new box into the gc-root (skip_box ensures these are mutually-exclusive) - // need to clone the value from `v.gcroot` if this isn't a new box - Value *oldroot; - if (v.gcroot) - oldroot = ctx.builder.CreateLoad(v.gcroot); - else - oldroot = v.V; - newroot = ctx.builder.CreateSelect(wasboxed, emit_bitcast(ctx, oldroot, boxv->getType()), newroot); - } - ctx.builder.CreateStore(newroot, froot); - } - if (v.V == NULL) { - // v.V might be NULL if it was all ghost objects before - return jl_cgval_t(boxv, froot, false, typ, new_tindex); - } - else { - Value *isboxv = ctx.builder.CreateIsNotNull(boxv); - Value *slotv; - MDNode *tbaa; - bool isimmutable; - if (v.ispointer()) { - slotv = v.V; - tbaa = v.tbaa; - isimmutable = v.isimmutable; - } - else { - slotv = emit_static_alloca(ctx, v.V->getType()); - ctx.builder.CreateStore(v.V, slotv); - tbaa = tbaa_stack; - isimmutable = true; - } - slotv = ctx.builder.CreateSelect(isboxv, - decay_derived(boxv), emit_bitcast(ctx, slotv, boxv->getType())); - jl_cgval_t newv = jl_cgval_t(slotv, froot, false, typ, new_tindex); - newv.tbaa = tbaa; - newv.isimmutable = isimmutable; - return newv; - } - } - } - else { - new_tindex = NULL; - makeboxed = true; - } + return convert_julia_type_union(ctx, v, typ); } else if (!v.isboxed && jl_is_uniontype(typ)) { // previous value was unboxed (leaftype), statically compute union tindex @@ -1981,20 +1994,6 @@ static void simple_use_analysis(jl_codectx_t &ctx, jl_value_t *expr) // ---- Get Element Pointer (GEP) instructions within the GC frame ---- -// Emit a gc-root slot indicator -static Value *emit_local_root(jl_codectx_t &ctx, jl_varinfo_t *vi) -{ - Instruction *newroot = new AllocaInst(T_prjlvalue, 0, "gcroot", /*InsertBefore*/ctx.ptlsStates); - if (vi) { - vi->boxroot->replaceAllUsesWith(newroot); - newroot->takeName(vi->boxroot); - vi->boxroot->eraseFromParent(); - vi->boxroot = newroot; - } - - return newroot; -} - static void jl_add_method_root(jl_codectx_t &ctx, jl_value_t *val) { if (jl_is_leaf_type(val) || jl_is_bool(val) || jl_is_symbol(val) || @@ -2905,9 +2904,7 @@ static jl_cgval_t emit_call_function_object(jl_method_instance_t *li, jl_llvm_fu jlretty, tindex, tbaa_stack); - // root this, if the return value was a box (tindex & 0x80) != 0 - retval.gcroot = emit_local_root(ctx); - ctx.builder.CreateStore(box, retval.gcroot); + retval.Vboxed = box; break; } case jl_returninfo_t::Ghosts: @@ -3272,7 +3269,6 @@ static jl_cgval_t emit_local(jl_codectx_t &ctx, jl_value_t *slotload) if (vi.boxroot != NULL) { Value *boxed = ctx.builder.CreateLoad(vi.boxroot, vi.isVolatile); Value *box_isnull; - v.gcroot = vi.boxroot; if (vi.usedUndef) box_isnull = ctx.builder.CreateICmpNE(boxed, maybe_decay_untracked(V_null)); if (vi.pTIndex) { @@ -3284,11 +3280,11 @@ static jl_cgval_t emit_local(jl_codectx_t &ctx, jl_value_t *slotload) if (vi.usedUndef) isnull = ctx.builder.CreateSelect(load_unbox, isnull, box_isnull); if (v.V) { // v.V will be null if it is a union of all ghost values - boxed = decay_derived(boxed); v.V = ctx.builder.CreateSelect(load_unbox, emit_bitcast(ctx, - decay_derived(v.V), boxed->getType()), boxed); + decay_derived(v.V), boxed->getType()), decay_derived(boxed)); } else v.V = boxed; + v.Vboxed = boxed; v = update_julia_type(ctx, v, typ); } else { @@ -3372,26 +3368,15 @@ static void emit_assignment(jl_codectx_t &ctx, jl_value_t *l, jl_value_t *r) size_t min_align; dest = try_emit_union_alloca(ctx, ((jl_uniontype_t*)jt), allunbox, min_align); Value *isboxed = NULL; - if (slot.ispointer() && slot.V != NULL && !isa(slot.V)) { + if (slot.isboxed || slot.Vboxed != nullptr) { isboxed = ctx.builder.CreateICmpNE( ctx.builder.CreateAnd(slot.TIndex, ConstantInt::get(T_int8, 0x80)), ConstantInt::get(T_int8, 0)); } if (dest) emit_unionmove(ctx, dest, slot, isboxed, false, NULL); - Value *gcroot = NULL; if (isboxed) { - Value *box; - if (slot.gcroot) { - gcroot = emit_local_root(ctx); - // This might load the wrong object in general, but if it gets selected, below, - // we know that it was in fact the one we wanted. - box = ctx.builder.CreateLoad(slot.gcroot); - } else { - gcroot = emit_static_alloca(ctx, T_pjlvalue); - box = V_null; - } - ctx.builder.CreateStore(box, gcroot); + Value *box = slot.Vboxed ? slot.Vboxed : V_null; if (dest) // might be all ghost values dest = ctx.builder.CreateSelect(isboxed, decay_derived(box), @@ -3402,8 +3387,9 @@ static void emit_assignment(jl_codectx_t &ctx, jl_value_t *l, jl_value_t *r) else { assert(allunbox && "Failed to allocate correct union-type storage."); } + Value *box = slot.Vboxed; slot = mark_julia_slot(dest, slot.typ, slot.TIndex, tbaa_stack); - slot.gcroot = gcroot; + slot.Vboxed = box; } else { bool isboxed; @@ -3449,14 +3435,9 @@ static void emit_assignment(jl_codectx_t &ctx, jl_value_t *l, jl_value_t *r) if (!vi.used) return; - bool needs_root = false; - if ((!vi.isSA && rval_info.gcroot) || !rval_info.isboxed) - // rval needed a gcroot, so lval will need one too - needs_root = true; - // convert rval-type to lval-type jl_value_t *slot_type = vi.value.typ; - rval_info = convert_julia_type(ctx, rval_info, slot_type, /*needs-root*/true); + rval_info = convert_julia_type(ctx, rval_info, slot_type); if (rval_info.typ == jl_bottom_type) return; @@ -3491,18 +3472,13 @@ static void emit_assignment(jl_codectx_t &ctx, jl_value_t *l, jl_value_t *r) // store boxed variables Value *isboxed = NULL; if (vi.boxroot) { - if (isa(vi.boxroot) && needs_root) - emit_local_root(ctx, &vi); // promote variable slot to a gcroot Value *rval; if (vi.pTIndex && rval_info.TIndex) { ctx.builder.CreateStore(rval_info.TIndex, vi.pTIndex, vi.isVolatile); isboxed = ctx.builder.CreateICmpNE( ctx.builder.CreateAnd(rval_info.TIndex, ConstantInt::get(T_int8, 0x80)), ConstantInt::get(T_int8, 0)); - rval = maybe_decay_untracked(V_null); - if (rval_info.ispointer() && rval_info.V != NULL && !isa(rval_info.V) && - !(isa(isboxed) && cast(isboxed)->isZero())) // might be all ghost values or otherwise definitely not boxed - rval = ctx.builder.CreateLoad(rval_info.gcroot); + rval = maybe_decay_untracked(rval_info.Vboxed ? rval_info.Vboxed : V_null); assert(!vi.value.constant); } else { @@ -4576,8 +4552,7 @@ static Function *gen_jlcall_wrapper(jl_method_instance_t *lam, const jl_returnin jlretty, ctx.builder.CreateExtractValue(call, 1), tbaa_stack); - retval.gcroot = emit_local_root(ctx); - ctx.builder.CreateStore(ctx.builder.CreateExtractValue(call, 0), retval.gcroot); + retval.Vboxed = ctx.builder.CreateExtractValue(call, 0); break; case jl_returninfo_t::Ghosts: retval = mark_julia_slot(NULL, jlretty, call, tbaa_stack); @@ -5254,10 +5229,8 @@ static std::unique_ptr emit_function( } } else { - Value *argp = boxed(ctx, theArg); // skip the temporary gcroot since it would be folded to argp anyways + Value *argp = boxed(ctx, theArg); ctx.builder.CreateStore(argp, vi.boxroot); - if (!theArg.isboxed) - emit_local_root(ctx, &vi); // create a root for vi } // get arrayvar data if applicable if (arrayvars.find(i) != arrayvars.end()) { @@ -5284,7 +5257,6 @@ static std::unique_ptr emit_function( ConstantInt::get(T_int32, nreq - 1)) }); restTuple->setAttributes(jltuple_func->getAttributes()); ctx.builder.CreateStore(restTuple, vi.boxroot); - emit_local_root(ctx, &vi); // create a root for vi } } @@ -5537,7 +5509,7 @@ static std::unique_ptr emit_function( // this is basically a copy of emit_assignment, // but where the assignment slot is the retval jl_cgval_t retvalinfo = emit_expr(ctx, jl_exprarg(expr, 0)); - retvalinfo = convert_julia_type(ctx, retvalinfo, jlrettype, /*needs-root*/false); + retvalinfo = convert_julia_type(ctx, retvalinfo, jlrettype); if (retvalinfo.typ == jl_bottom_type) { ctx.builder.CreateUnreachable(); find_next_stmt(-1); @@ -5572,20 +5544,13 @@ static std::unique_ptr emit_function( } else { data = maybe_decay_untracked(V_null); - if (retvalinfo.ispointer() && !isa(retvalinfo.V)) { + if (retvalinfo.Vboxed) { // also need to account for the possibility the return object is boxed // and avoid / skip copying it to the stack isboxed_union = ctx.builder.CreateICmpNE( ctx.builder.CreateAnd(tindex, ConstantInt::get(T_int8, 0x80)), ConstantInt::get(T_int8, 0)); - // Lift the select, because gcroot may be NULL if - // there's no boxed value. - if (isa(isboxed_union)) - data = cast(isboxed_union)->isZero() ? data : ctx.builder.CreateLoad(retvalinfo.gcroot); - else - data = ctx.builder.CreateSelect(isboxed_union, - ctx.builder.CreateLoad(retvalinfo.gcroot), - data); + data = ctx.builder.CreateSelect(isboxed_union, retvalinfo.Vboxed, data); } } } From b83b7e92ba9312fc07a581f08b8ddc8d5c08bb0f Mon Sep 17 00:00:00 2001 From: quinnj Date: Sun, 20 Aug 2017 07:53:05 -0600 Subject: [PATCH 268/324] Work to ensure immutable isbits Union fields get set correctly. #23351 --- src/cgutils.cpp | 20 ++++++++++++++++---- src/datatype.c | 2 ++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 6dbb982e7223e..6bfe19db711d0 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -2244,15 +2244,27 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg Type *fty = julia_type_to_llvm(jtype); const jl_cgval_t &fval_info = argv[i + 1]; emit_typecheck(ctx, fval_info, jtype, "new"); - if (!type_is_ghost(fty)) { - Value *fval = NULL, *dest = NULL; - if (!init_as_value) { + Value *dest = NULL; + if (!init_as_value) { + bool isunion = jl_is_uniontype(jtype); + if (!type_is_ghost(fty) || isunion) { // avoid unboxing the argument explicitly // and use memcpy instead dest = ctx.builder.CreateConstInBoundsGEP2_32(lt, strct, 0, i); } + if (isunion) { + int fsz = jl_field_size(sty, i); + // compute tindex from rhs + // jl_cgval_t rhs_union = convert_julia_type(ctx, rhs, jtype); + Value *tindex = compute_tindex_unboxed(ctx, fval_info, jtype); + tindex = ctx.builder.CreateNUWSub(tindex, ConstantInt::get(T_int8, 1)); + Value *ptindex = ctx.builder.CreateGEP(T_int8, emit_bitcast(ctx, dest, T_pint8), ConstantInt::get(T_size, fsz - 1)); + ctx.builder.CreateStore(tindex, ptindex); + } + } + if (!type_is_ghost(fty)) { + Value *fval = NULL; fval = emit_unbox(ctx, fty, fval_info, jtype, dest); - if (init_as_value) { if (lt->isVectorTy()) strct = ctx.builder.CreateInsertElement(strct, fval, ConstantInt::get(T_int32, idx)); diff --git a/src/datatype.c b/src/datatype.c index ca7f8a361d624..1212fc91b6d09 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -784,6 +784,8 @@ JL_DLLEXPORT jl_value_t *jl_get_nth_field(jl_value_t *v, size_t i) if (jl_is_uniontype(ty)) { uint8_t sel = ((uint8_t*)v)[offs + jl_field_size(st, i) - 1]; ty = jl_nth_union_component(ty, sel); + if (jl_is_datatype_singleton((jl_datatype_t*)ty)) + return ((jl_datatype_t*)ty)->instance; } return jl_new_bits(ty, (char*)v + offs); } From 560aca9e7d24baf6b39fa1c5e9da1a11c99341d1 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 22 Aug 2017 13:35:42 -0400 Subject: [PATCH 269/324] correctly handle union-splitting in codegen type allocation --- src/cgutils.cpp | 92 +++++++++++++++++++++++++++++++++++-------------- src/datatype.c | 18 +++------- 2 files changed, 71 insertions(+), 39 deletions(-) diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 6bfe19db711d0..20403ba6c54f7 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -461,6 +461,14 @@ static bool julia_struct_has_layout(jl_datatype_t *dt, jl_unionall_t *ua) return true; } +static unsigned jl_field_align(jl_datatype_t *dt, size_t i) +{ + unsigned al = jl_field_offset(dt, i); + al |= 16; + al &= -al; + return std::min(al, jl_datatype_align(dt)); +} + static Type *julia_struct_to_llvm(jl_value_t *jt, jl_unionall_t *ua, bool *isboxed) { // this function converts a Julia Type into the equivalent LLVM struct @@ -497,15 +505,34 @@ static Type *julia_struct_to_llvm(jl_value_t *jt, jl_unionall_t *ua, bool *isbox isvector = false; jlasttype = ty; bool isptr; - if (jst->layout) + size_t fsz = 0, al = 0; + if (jst->layout) { isptr = jl_field_isptr(jst, i); - else // compute what jl_compute_field_offsets would say - isptr = jl_isbits(ty) && jl_is_leaf_type(ty) && ((jl_datatype_t*)ty)->layout; + fsz = jl_field_size(jst, i); + al = jl_field_align(jst, i); + } + else { // compute what jl_compute_field_offsets would say + isptr = !jl_islayout_inline(ty, &fsz, &al); + if (!isptr && jl_is_uniontype(jst)) + fsz += 1; + } Type *lty; if (isptr) lty = T_pjlvalue; else if (ty == (jl_value_t*)jl_bool_type) lty = T_int8; + else if (jl_is_uniontype(ty)) { + // pick an Integer type size such that alignment will be correct + // and always end with an Int8 (selector byte) + lty = ArrayType::get(IntegerType::get(jl_LLVMContext, 8 * al), (fsz - 1) / al); + std::vector Elements(2); + Elements[0] = lty; + Elements[1] = T_int8; + unsigned remainder = (fsz - 1) % al; + while (remainder--) + Elements.push_back(T_int8); + lty = StructType::get(jl_LLVMContext, makeArrayRef(Elements)); + } else lty = julia_type_to_llvm(ty); if (lasttype != NULL && lasttype != lty) @@ -560,6 +587,16 @@ static Type *julia_struct_to_llvm(jl_value_t *jt, jl_unionall_t *ua, bool *isbox } return (Type*)jst->struct_decl; } + // TODO: enable this (with tests): + // if (jl_is_uniontype(ty)) { + // size_t fsz = 0, al = 0; + // bool isptr = !jl_islayout_inline(ty, &fsz, &al); + // // pick an Integer type size such that alignment will be correct + // return StructType::get(jl_LLVMContext, makeArrayRef({ + // ArrayType::get(IntegerType::get(jl_LLVMContext, 8 * al), + // (fsz + al - 1) / al), + // T_int8 })); + // } if (isboxed) *isboxed = true; return T_pjlvalue; } @@ -1378,9 +1415,7 @@ static jl_cgval_t emit_getfield_knownidx(jl_codectx_t &ctx, const jl_cgval_t &st addr = ctx.builder.CreateStructGEP(lt, ptr, idx); } } - int align = jl_field_offset(jt, idx); - align |= 16; - align &= -align; + int align = jl_field_align(jt, idx); if (jl_field_isptr(jt, idx)) { bool maybe_null = idx >= (unsigned)jt->ninitialized; Instruction *Load = maybe_mark_load_dereferenceable( @@ -1895,6 +1930,8 @@ static Value *compute_box_tindex(jl_codectx_t &ctx, Value *datatype, jl_value_t // get the runtime tindex value static Value *compute_tindex_unboxed(jl_codectx_t &ctx, const jl_cgval_t &val, jl_value_t *typ) { + if (val.typ == jl_bottom_type) + return UndefValue::get(T_int8); if (val.constant) return ConstantInt::get(T_int8, get_box_tindex((jl_datatype_t*)jl_typeof(val.constant), typ)); if (val.isboxed) @@ -2189,6 +2226,8 @@ static void emit_setfield(jl_codectx_t &ctx, int fsz = jl_field_size(sty, idx0); // compute tindex from rhs jl_cgval_t rhs_union = convert_julia_type(ctx, rhs, jfty); + if (rhs_union.typ == jl_bottom_type) + return; Value *tindex = compute_tindex_unboxed(ctx, rhs_union, jfty); tindex = ctx.builder.CreateNUWSub(tindex, ConstantInt::get(T_int8, 1)); Value *ptindex = ctx.builder.CreateGEP(T_int8, emit_bitcast(ctx, addr, T_pint8), ConstantInt::get(T_size, fsz - 1)); @@ -2199,9 +2238,7 @@ static void emit_setfield(jl_codectx_t &ctx, } } else { - int align = jl_field_offset(sty, idx0); - align |= 16; - align &= -align; + int align = jl_field_align(sty, idx0); typed_store(ctx, addr, ConstantInt::get(T_size, 0), rhs, jfty, strct.tbaa, data_pointer(ctx, strct, T_pjlvalue), align); } @@ -2231,17 +2268,16 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg type_is_ghost(lt)) // maybe also check the size ? init_as_value = true; - size_t na = nargs-1 < nf ? nargs-1 : nf; + unsigned na = (nargs - 1 < nf) ? (nargs - 1) : nf; Value *strct; if (init_as_value) strct = UndefValue::get(lt == T_void ? NoopType : lt); else strct = emit_static_alloca(ctx, lt); - unsigned idx = 0; - for (size_t i = 0; i < na; i++) { + for (unsigned i = 0; i < na; i++) { jl_value_t *jtype = jl_svecref(sty->types, i); - Type *fty = julia_type_to_llvm(jtype); + Type *fty = julia_struct_to_llvm(jtype, NULL, NULL); const jl_cgval_t &fval_info = argv[i + 1]; emit_typecheck(ctx, fval_info, jtype, "new"); Value *dest = NULL; @@ -2251,15 +2287,22 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg // avoid unboxing the argument explicitly // and use memcpy instead dest = ctx.builder.CreateConstInBoundsGEP2_32(lt, strct, 0, i); - } - if (isunion) { - int fsz = jl_field_size(sty, i); - // compute tindex from rhs - // jl_cgval_t rhs_union = convert_julia_type(ctx, rhs, jtype); - Value *tindex = compute_tindex_unboxed(ctx, fval_info, jtype); - tindex = ctx.builder.CreateNUWSub(tindex, ConstantInt::get(T_int8, 1)); - Value *ptindex = ctx.builder.CreateGEP(T_int8, emit_bitcast(ctx, dest, T_pint8), ConstantInt::get(T_size, fsz - 1)); - ctx.builder.CreateStore(tindex, ptindex); + if (isunion) { + // compute tindex from rhs + jl_cgval_t rhs_union = convert_julia_type(ctx, fval_info, jtype); + if (rhs_union.typ == jl_bottom_type) + return jl_cgval_t(); + Value *tindex = compute_tindex_unboxed(ctx, rhs_union, jtype); + tindex = ctx.builder.CreateNUWSub(tindex, ConstantInt::get(T_int8, 1)); + StructType *lt_i = cast(cast(dest)->getResultElementType()); + Value *ptindex = ctx.builder.CreateConstInBoundsGEP2_32( + lt_i, dest, 0, lt_i->getNumElements() - 1); + ctx.builder.CreateStore(tindex, ptindex); + if (!rhs_union.isghost) { + emit_unionmove(ctx, dest, fval_info, NULL, false, NULL); + } + continue; + } } } if (!type_is_ghost(fty)) { @@ -2267,16 +2310,15 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg fval = emit_unbox(ctx, fty, fval_info, jtype, dest); if (init_as_value) { if (lt->isVectorTy()) - strct = ctx.builder.CreateInsertElement(strct, fval, ConstantInt::get(T_int32, idx)); + strct = ctx.builder.CreateInsertElement(strct, fval, ConstantInt::get(T_int32, i)); else if (jl_is_vecelement_type(ty)) strct = fval; // VecElement type comes unwrapped in LLVM. else if (lt->isAggregateType()) - strct = ctx.builder.CreateInsertValue(strct, fval, ArrayRef(&idx, 1)); + strct = ctx.builder.CreateInsertValue(strct, fval, ArrayRef(&i, 1)); else assert(false); } } - idx++; } if (init_as_value) return mark_julia_type(ctx, strct, false, ty); diff --git a/src/datatype.c b/src/datatype.c index 1212fc91b6d09..9fb6b19d5591f 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -490,21 +490,17 @@ typedef struct { int64_t b; } bits128_t; -// Note that this function updates len -static jl_value_t *jl_new_bits_internal(jl_value_t *dt, void *data, size_t *len) +// TODO: do we care that this has invalid alignment assumptions? +JL_DLLEXPORT jl_value_t *jl_new_bits(jl_value_t *dt, void *data) { jl_ptls_t ptls = jl_get_ptls_states(); assert(jl_is_datatype(dt)); jl_datatype_t *bt = (jl_datatype_t*)dt; size_t nb = jl_datatype_size(bt); - if (nb == 0) - return jl_new_struct_uninit(bt); - *len = LLT_ALIGN(*len, jl_datatype_align(bt)); - data = (char*)data + (*len); - *len += nb; + if (nb == 0) return jl_new_struct_uninit(bt); // returns bt->instance if (bt == jl_uint8_type) return jl_box_uint8(*(uint8_t*)data); if (bt == jl_int64_type) return jl_box_int64(*(int64_t*)data); - if (bt == jl_bool_type) return (*(int8_t*)data) ? jl_true:jl_false; + if (bt == jl_bool_type) return (*(int8_t*)data) ? jl_true : jl_false; if (bt == jl_int32_type) return jl_box_int32(*(int32_t*)data); if (bt == jl_float64_type) return jl_box_float64(*(double*)data); @@ -520,12 +516,6 @@ static jl_value_t *jl_new_bits_internal(jl_value_t *dt, void *data, size_t *len) return v; } -JL_DLLEXPORT jl_value_t *jl_new_bits(jl_value_t *bt, void *data) -{ - size_t len = 0; - return jl_new_bits_internal(bt, data, &len); -} - // used by boot.jl JL_DLLEXPORT jl_value_t *jl_typemax_uint(jl_value_t *bt) { From a562e3330e4a6148b9af46cfdb64ad11567452b2 Mon Sep 17 00:00:00 2001 From: quinnj Date: Wed, 23 Aug 2017 00:34:20 -0600 Subject: [PATCH 270/324] Add tests for structs with isbits Union fields. --- test/core.jl | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/core.jl b/test/core.jl index dfdbfb13513f0..b0a812a88f23b 100644 --- a/test/core.jl +++ b/test/core.jl @@ -5316,6 +5316,26 @@ x.u = initvalue2(Base.uniontypes(U)[1]) x.u = initvalue(Base.uniontypes(U)[2]) @test x.u === initvalue(Base.uniontypes(U)[2]) +struct AA + x::Union{Int8, Int16, NTuple{7, Int8}, Void} +end +struct B + x::Int8 + y::AA + z::Int8 +end +b = B(91, AA(ntuple(i -> Int8(i), Val(7))), 23) + +@test b.x === Int8(91) +@test b.z === Int8(23) +@test b.y === AA(ntuple(i -> Int8(i), Val(7))) +@test sizeof(b) == 12 +@test AA(Int8(1)).x === Int8(1) +@test AA(Int8(0)).x === Int8(0) +@test AA(Int16(1)).x === Int16(1) +@test AA(nothing).x === nothing +@test sizeof(b.y) == 8 + for U in boxedunions local U for N in (1, 2, 3, 4) From f61db81e9eae0c2b94ce28a19769abd03c61ff4e Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 23 Aug 2017 15:31:58 -0400 Subject: [PATCH 271/324] correctly handle union-store-splitting in "is" and "object_id" --- src/builtins.c | 69 +++++++++++++++++++++++++++++++------------------ src/cgutils.cpp | 38 +++++++++++++-------------- src/codegen.cpp | 69 +++++++++++++++++++++++++++++++++++++++++++++---- src/rtutils.c | 9 ++++--- test/core.jl | 35 +++++++++++++++++-------- 5 files changed, 156 insertions(+), 64 deletions(-) diff --git a/src/builtins.c b/src/builtins.c index 34b6430709384..0a5950e930d25 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -77,30 +77,40 @@ static int NOINLINE compare_svec(jl_svec_t *a, jl_svec_t *b) // See comment above for an explanation of NOINLINE. static int NOINLINE compare_fields(jl_value_t *a, jl_value_t *b, jl_datatype_t *dt) { - size_t nf = jl_datatype_nfields(dt); - for (size_t f=0; f < nf; f++) { + size_t f, nf = jl_datatype_nfields(dt); + for (f = 0; f < nf; f++) { size_t offs = jl_field_offset(dt, f); char *ao = (char*)jl_data_ptr(a) + offs; char *bo = (char*)jl_data_ptr(b) + offs; - int eq; if (jl_field_isptr(dt, f)) { jl_value_t *af = *(jl_value_t**)ao; jl_value_t *bf = *(jl_value_t**)bo; - if (af == bf) eq = 1; - else if (af==NULL || bf==NULL) eq = 0; - else eq = jl_egal(af, bf); + if (af != bf) { + if (af == NULL || bf == NULL) + return 0; + if (!jl_egal(af, bf)) + return 0; + } } else { jl_datatype_t *ft = (jl_datatype_t*)jl_field_type(dt, f); + if (jl_is_uniontype(ft)) { + uint8_t asel = ((uint8_t*)ao)[jl_field_size(dt, f) - 1]; + uint8_t bsel = ((uint8_t*)bo)[jl_field_size(dt, f) - 1]; + if (asel != bsel) + return 0; + ft = (jl_datatype_t*)jl_nth_union_component((jl_value_t*)ft, asel); + } if (!ft->layout->haspadding) { - eq = bits_equal(ao, bo, jl_field_size(dt, f)); + if (!bits_equal(ao, bo, jl_field_size(dt, f))) + return 0; } else { assert(jl_datatype_nfields(ft) > 0); - eq = compare_fields((jl_value_t*)ao, (jl_value_t*)bo, ft); + if (!compare_fields((jl_value_t*)ao, (jl_value_t*)bo, ft)) + return 0; } } - if (!eq) return 0; } return 1; } @@ -127,9 +137,11 @@ JL_DLLEXPORT int jl_egal(jl_value_t *a, jl_value_t *b) return 0; return !memcmp(jl_string_data(a), jl_string_data(b), l); } - if (dt->mutabl) return 0; + if (dt->mutabl) + return 0; size_t sz = jl_datatype_size(dt); - if (sz == 0) return 1; + if (sz == 0) + return 1; size_t nf = jl_datatype_nfields(dt); if (nf == 0) return bits_equal(jl_data_ptr(a), jl_data_ptr(b), sz); @@ -161,10 +173,10 @@ static uintptr_t bits_hash(void *b, size_t sz) static uintptr_t NOINLINE hash_svec(jl_svec_t *v) { uintptr_t h = 0; - size_t l = jl_svec_len(v); - for(size_t i = 0; i < l; i++) { - jl_value_t *x = jl_svecref(v,i); - uintptr_t u = x==NULL ? 0 : jl_object_id(x); + size_t i, l = jl_svec_len(v); + for (i = 0; i < l; i++) { + jl_value_t *x = jl_svecref(v, i); + uintptr_t u = (x == NULL) ? 0 : jl_object_id(x); h = bitmix(h, u); } return h; @@ -188,9 +200,11 @@ static uintptr_t jl_object_id_(jl_value_t *tv, jl_value_t *v) if (dt == jl_typename_type) return ((jl_typename_t*)v)->hash; #ifdef _P64 - if (v == jl_ANY_flag) return 0x31c472f68ee30bddULL; + if (v == jl_ANY_flag) + return 0x31c472f68ee30bddULL; #else - if (v == jl_ANY_flag) return 0x8ee30bdd; + if (v == jl_ANY_flag) + return 0x8ee30bdd; #endif if (dt == jl_string_type) { #ifdef _P64 @@ -199,24 +213,29 @@ static uintptr_t jl_object_id_(jl_value_t *tv, jl_value_t *v) return memhash32_seed(jl_string_data(v), jl_string_len(v), 0xedc3b677); #endif } - if (dt->mutabl) return inthash((uintptr_t)v); + if (dt->mutabl) + return inthash((uintptr_t)v); size_t sz = jl_datatype_size(tv); uintptr_t h = jl_object_id(tv); - if (sz == 0) return ~h; - size_t nf = jl_datatype_nfields(dt); - if (nf == 0) { + if (sz == 0) + return ~h; + size_t f, nf = jl_datatype_nfields(dt); + if (nf == 0) return bits_hash(jl_data_ptr(v), sz) ^ h; - } - for (size_t f=0; f < nf; f++) { + for (f = 0; f < nf; f++) { size_t offs = jl_field_offset(dt, f); char *vo = (char*)jl_data_ptr(v) + offs; uintptr_t u; if (jl_field_isptr(dt, f)) { jl_value_t *f = *(jl_value_t**)vo; - u = f==NULL ? 0 : jl_object_id(f); + u = (f == NULL) ? 0 : jl_object_id(f); } else { jl_datatype_t *fieldtype = (jl_datatype_t*)jl_field_type(dt, f); + if (jl_is_uniontype(fieldtype)) { + uint8_t sel = ((uint8_t*)vo)[jl_field_size(dt, f) - 1]; + fieldtype = (jl_datatype_t*)jl_nth_union_component((jl_value_t*)fieldtype, sel); + } assert(jl_is_datatype(fieldtype) && !fieldtype->abstract && !fieldtype->mutabl); if (fieldtype->layout->haspadding) u = jl_object_id_((jl_value_t*)fieldtype, (jl_value_t*)vo); @@ -244,7 +263,7 @@ JL_CALLABLE(jl_f_is) JL_NARGS(===, 2, 2); if (args[0] == args[1]) return jl_true; - return jl_egal(args[0],args[1]) ? jl_true : jl_false; + return jl_egal(args[0], args[1]) ? jl_true : jl_false; } JL_CALLABLE(jl_f_typeof) diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 20403ba6c54f7..961f321a9450e 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -2221,28 +2221,26 @@ static void emit_setfield(jl_codectx_t &ctx, if (wb && strct.isboxed) emit_checked_write_barrier(ctx, boxed(ctx, strct), r); } - else { - if (jl_is_uniontype(jfty)) { - int fsz = jl_field_size(sty, idx0); - // compute tindex from rhs - jl_cgval_t rhs_union = convert_julia_type(ctx, rhs, jfty); - if (rhs_union.typ == jl_bottom_type) - return; - Value *tindex = compute_tindex_unboxed(ctx, rhs_union, jfty); - tindex = ctx.builder.CreateNUWSub(tindex, ConstantInt::get(T_int8, 1)); - Value *ptindex = ctx.builder.CreateGEP(T_int8, emit_bitcast(ctx, addr, T_pint8), ConstantInt::get(T_size, fsz - 1)); - ctx.builder.CreateStore(tindex, ptindex); - // copy data - if (!rhs.isghost) { - emit_unionmove(ctx, addr, rhs, NULL, false, NULL); - } - } - else { - int align = jl_field_align(sty, idx0); - typed_store(ctx, addr, ConstantInt::get(T_size, 0), rhs, jfty, - strct.tbaa, data_pointer(ctx, strct, T_pjlvalue), align); + else if (jl_is_uniontype(jfty)) { + int fsz = jl_field_size(sty, idx0); + // compute tindex from rhs + jl_cgval_t rhs_union = convert_julia_type(ctx, rhs, jfty); + if (rhs_union.typ == jl_bottom_type) + return; + Value *tindex = compute_tindex_unboxed(ctx, rhs_union, jfty); + tindex = ctx.builder.CreateNUWSub(tindex, ConstantInt::get(T_int8, 1)); + Value *ptindex = ctx.builder.CreateGEP(T_int8, emit_bitcast(ctx, addr, T_pint8), ConstantInt::get(T_size, fsz - 1)); + ctx.builder.CreateStore(tindex, ptindex); + // copy data + if (!rhs.isghost) { + emit_unionmove(ctx, addr, rhs, NULL, false, NULL); } } + else { + int align = jl_field_align(sty, idx0); + typed_store(ctx, addr, ConstantInt::get(T_size, 0), rhs, jfty, + strct.tbaa, data_pointer(ctx, strct, T_pjlvalue), align); + } } else { // TODO: better error diff --git a/src/codegen.cpp b/src/codegen.cpp index ba9e7aa12b192..d85b087a89883 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2061,11 +2061,48 @@ static jl_cgval_t emit_getfield(jl_codectx_t &ctx, const jl_cgval_t &strct, jl_s return mark_julia_type(ctx, result, true, jl_any_type); } +static Value *emit_bits_compare(jl_codectx_t &ctx, const jl_cgval_t &arg1, const jl_cgval_t &arg2); + +static Value *emit_bitsunion_compare(jl_codectx_t &ctx, const jl_cgval_t &arg1, const jl_cgval_t &arg2) +{ + assert(arg1.typ == arg2.typ && arg1.TIndex && arg2.TIndex && jl_is_uniontype(arg1.typ) && "unimplemented"); + Value *tindex = arg1.TIndex; + BasicBlock *defaultBB = BasicBlock::Create(jl_LLVMContext, "unionbits_is_boxed", ctx.f); + SwitchInst *switchInst = ctx.builder.CreateSwitch(tindex, defaultBB); + BasicBlock *postBB = BasicBlock::Create(jl_LLVMContext, "post_unionbits_is", ctx.f); + ctx.builder.SetInsertPoint(postBB); + PHINode *phi = ctx.builder.CreatePHI(T_int1, 2); + unsigned counter = 0; + for_each_uniontype_small( + [&](unsigned idx, jl_datatype_t *jt) { + BasicBlock *tempBB = BasicBlock::Create(jl_LLVMContext, "unionbits_is", ctx.f); + ctx.builder.SetInsertPoint(tempBB); + switchInst->addCase(ConstantInt::get(T_int8, idx), tempBB); + jl_cgval_t sel_arg1(arg1, (jl_value_t*)jt, NULL); + jl_cgval_t sel_arg2(arg2, (jl_value_t*)jt, NULL); + phi->addIncoming(emit_bits_compare(ctx, sel_arg1, sel_arg2), tempBB); + ctx.builder.CreateBr(postBB); + }, + arg1.typ, + counter); + ctx.builder.SetInsertPoint(defaultBB); + Function *trap_func = Intrinsic::getDeclaration( + ctx.f->getParent(), + Intrinsic::trap); + ctx.builder.CreateCall(trap_func); + ctx.builder.CreateUnreachable(); + ctx.builder.SetInsertPoint(postBB); + return ctx.builder.CreateAnd(phi, ctx.builder.CreateICmpEQ(arg1.TIndex, arg2.TIndex)); +} + static Value *emit_bits_compare(jl_codectx_t &ctx, const jl_cgval_t &arg1, const jl_cgval_t &arg2) { assert(jl_is_datatype(arg1.typ) && arg1.typ == arg2.typ); Type *at = julia_type_to_llvm(arg1.typ); + if (type_is_ghost(at)) + return ConstantInt::get(T_int1, 1); + if (at->isIntegerTy() || at->isPointerTy() || at->isFloatingPointTy()) { Type *at_int = INTT(at); Value *varg1 = emit_unbox(ctx, at_int, arg1, arg1.typ); @@ -2114,11 +2151,29 @@ static Value *emit_bits_compare(jl_codectx_t &ctx, const jl_cgval_t &arg1, const Value *subAns, *fld1, *fld2; fld1 = ctx.builder.CreateConstGEP2_32(at, varg1, 0, i); fld2 = ctx.builder.CreateConstGEP2_32(at, varg2, 0, i); - if (type_is_ghost(fld1->getType()->getPointerElementType())) + Type *at_i = cast(fld1)->getResultElementType(); + if (type_is_ghost(at_i)) continue; - subAns = emit_bits_compare(ctx, - mark_julia_slot(fld1, fldty, NULL, arg1.tbaa), - mark_julia_slot(fld2, fldty, NULL, arg2.tbaa)); + if (jl_is_uniontype(fldty)) { + unsigned tindex_offset = cast(at_i)->getNumElements() - 1; + Value *ptindex1 = ctx.builder.CreateConstInBoundsGEP2_32( + at_i, fld1, 0, tindex_offset); + Value *ptindex2 = ctx.builder.CreateConstInBoundsGEP2_32( + at_i, fld2, 0, tindex_offset); + Value *tindex1 = ctx.builder.CreateNUWAdd(ConstantInt::get(T_int8, 1), + ctx.builder.CreateLoad(T_int8, ptindex1)); + Value *tindex2 = ctx.builder.CreateNUWAdd(ConstantInt::get(T_int8, 1), + ctx.builder.CreateLoad(T_int8, ptindex2)); + subAns = emit_bitsunion_compare(ctx, + mark_julia_slot(fld1, fldty, tindex1, arg1.tbaa), + mark_julia_slot(fld2, fldty, tindex2, arg2.tbaa)); + } + else { + assert(jl_is_leaf_type(fldty)); + subAns = emit_bits_compare(ctx, + mark_julia_slot(fld1, fldty, NULL, arg1.tbaa), + mark_julia_slot(fld2, fldty, NULL, arg2.tbaa)); + } answer = ctx.builder.CreateAnd(answer, subAns); } return answer; @@ -2182,6 +2237,9 @@ static Value *emit_f_is(jl_codectx_t &ctx, const jl_cgval_t &arg1, const jl_cgva return cmp; } + // if (arg1.tindex || arg2.tindex) + // TODO: handle with emit_bitsunion_compare + int ptr_comparable = 0; // whether this type is unique'd by pointer if (rt1 == (jl_value_t*)jl_sym_type || rt2 == (jl_value_t*)jl_sym_type) ptr_comparable = 1; @@ -2398,7 +2456,8 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, Value *selidx = ctx.builder.CreateMul(emit_arraylen_prim(ctx, ary), nbytes); selidx = ctx.builder.CreateAdd(selidx, idx); Value *ptindex = ctx.builder.CreateGEP(T_int8, data, selidx); - Value *tindex = ctx.builder.CreateNUWAdd(ConstantInt::get(T_int8, 1), tbaa_decorate(tbaa_arrayselbyte, ctx.builder.CreateLoad(T_int8, ptindex))); + Value *tindex = ctx.builder.CreateNUWAdd(ConstantInt::get(T_int8, 1), + tbaa_decorate(tbaa_arrayselbyte, ctx.builder.CreateLoad(T_int8, ptindex))); Type *AT = ArrayType::get(IntegerType::get(jl_LLVMContext, 8 * al), (elsz + al - 1) / al); AllocaInst *lv = emit_static_alloca(ctx, AT); if (al > 1) diff --git a/src/rtutils.c b/src/rtutils.c index e5b4cc4f645a8..1d0e5417163b5 100644 --- a/src/rtutils.c +++ b/src/rtutils.c @@ -887,9 +887,12 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt n += jl_static_show_x(out, *(jl_value_t**)fld_ptr, depth); } else { - n += jl_static_show_x_(out, (jl_value_t*)fld_ptr, - (jl_datatype_t*)jl_field_type(vt, i), - depth); + jl_datatype_t *ft = (jl_datatype_t*)jl_field_type(vt, i); + if (jl_is_uniontype(ft)) { + uint8_t sel = ((uint8_t*)fld_ptr)[jl_field_size(vt, i) - 1]; + ft = (jl_datatype_t*)jl_nth_union_component((jl_value_t*)ft, sel); + } + n += jl_static_show_x_(out, (jl_value_t*)fld_ptr, ft, depth); } if (istuple && tlen == 1) n += jl_printf(out, ","); diff --git a/test/core.jl b/test/core.jl index b0a812a88f23b..291030e8fd249 100644 --- a/test/core.jl +++ b/test/core.jl @@ -5324,17 +5324,30 @@ struct B y::AA z::Int8 end -b = B(91, AA(ntuple(i -> Int8(i), Val(7))), 23) - -@test b.x === Int8(91) -@test b.z === Int8(23) -@test b.y === AA(ntuple(i -> Int8(i), Val(7))) -@test sizeof(b) == 12 -@test AA(Int8(1)).x === Int8(1) -@test AA(Int8(0)).x === Int8(0) -@test AA(Int16(1)).x === Int16(1) -@test AA(nothing).x === nothing -@test sizeof(b.y) == 8 +@noinline compare(a, b) = (a === b) # test code-generation of `is` +let + b = B(91, AA(ntuple(i -> Int8(i), Val(7))), 23) + b2 = Ref(b)[] # copy b via field assignment + b3 = B[b][1] # copy b via array assignment + @test pointer_from_objref(b) == pointer_from_objref(b) + @test pointer_from_objref(b) != pointer_from_objref(b2) + @test pointer_from_objref(b) != pointer_from_objref(b3) + @test pointer_from_objref(b2) != pointer_from_objref(b3) + + @test b === b2 === b3 + @test compare(b, b2) + @test compare(b, b3) + @test object_id(b) === object_id(b2) == object_id(b3) + @test b.x === Int8(91) + @test b.z === Int8(23) + @test b.y === AA((Int8(1), Int8(2), Int8(3), Int8(4), Int8(5), Int8(6), Int8(7))) + @test sizeof(b) == 12 + @test AA(Int8(1)).x === Int8(1) + @test AA(Int8(0)).x === Int8(0) + @test AA(Int16(1)).x === Int16(1) + @test AA(nothing).x === nothing + @test sizeof(b.y) == 8 +end for U in boxedunions local U From 2bb430e383809dcf5f73605a65f408c7e5bd22d6 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 24 Aug 2017 13:28:30 -0400 Subject: [PATCH 272/324] fix codegen bugs, simplify type representations anonymize all types, remove special representation for Ptr, fix Constant generation for types containing split-union fields --- base/atomics.jl | 18 +- base/fastmath.jl | 32 +-- src/ccall.cpp | 70 ++++--- src/cgutils.cpp | 360 +++++++++++++++++----------------- src/codegen.cpp | 2 +- src/intrinsics.cpp | 223 +++++++++++---------- src/llvm-alloc-opt.cpp | 1 + src/llvm-late-gc-lowering.cpp | 7 +- test/core.jl | 40 ++-- test/libgit2.jl | 2 + 10 files changed, 388 insertions(+), 367 deletions(-) diff --git a/base/atomics.jl b/base/atomics.jl index 38f8a0e243178..51c6964a13e16 100644 --- a/base/atomics.jl +++ b/base/atomics.jl @@ -345,20 +345,23 @@ for typ in atomictypes irt = Base.libllvm_version >= v"3.6" ? "$ilt, $ilt*" : "$ilt*" @eval getindex(x::Atomic{$typ}) = llvmcall($""" - %rv = load atomic $rt %0 acquire, align $(alignment(typ)) + %ptr = inttoptr i$WORD_SIZE %0 to $lt* + %rv = load atomic $rt %ptr acquire, align $(alignment(typ)) ret $lt %rv """, $typ, Tuple{Ptr{$typ}}, unsafe_convert(Ptr{$typ}, x)) @eval setindex!(x::Atomic{$typ}, v::$typ) = llvmcall($""" - store atomic $lt %1, $lt* %0 release, align $(alignment(typ)) + %ptr = inttoptr i$WORD_SIZE %0 to $lt* + store atomic $lt %1, $lt* %ptr release, align $(alignment(typ)) ret void - """, Void, Tuple{Ptr{$typ},$typ}, unsafe_convert(Ptr{$typ}, x), v) + """, Void, Tuple{Ptr{$typ}, $typ}, unsafe_convert(Ptr{$typ}, x), v) # Note: atomic_cas! succeeded (i.e. it stored "new") if and only if the result is "cmp" if typ <: Integer @eval atomic_cas!(x::Atomic{$typ}, cmp::$typ, new::$typ) = llvmcall($""" - %rs = cmpxchg $lt* %0, $lt %1, $lt %2 acq_rel acquire + %ptr = inttoptr i$WORD_SIZE %0 to $lt* + %rs = cmpxchg $lt* %ptr, $lt %1, $lt %2 acq_rel acquire %rv = extractvalue { $lt, i1 } %rs, 0 ret $lt %rv """, $typ, Tuple{Ptr{$typ},$typ,$typ}, @@ -366,7 +369,7 @@ for typ in atomictypes else @eval atomic_cas!(x::Atomic{$typ}, cmp::$typ, new::$typ) = llvmcall($""" - %iptr = bitcast $lt* %0 to $ilt* + %iptr = inttoptr i$WORD_SIZE %0 to $ilt* %icmp = bitcast $lt %1 to $ilt %inew = bitcast $lt %2 to $ilt %irs = cmpxchg $ilt* %iptr, $ilt %icmp, $ilt %inew acq_rel acquire @@ -387,14 +390,15 @@ for typ in atomictypes if typ <: Integer @eval $fn(x::Atomic{$typ}, v::$typ) = llvmcall($""" - %rv = atomicrmw $rmw $lt* %0, $lt %1 acq_rel + %ptr = inttoptr i$WORD_SIZE %0 to $lt* + %rv = atomicrmw $rmw $lt* %ptr, $lt %1 acq_rel ret $lt %rv """, $typ, Tuple{Ptr{$typ}, $typ}, unsafe_convert(Ptr{$typ}, x), v) else rmwop == :xchg || continue @eval $fn(x::Atomic{$typ}, v::$typ) = llvmcall($""" - %iptr = bitcast $lt* %0 to $ilt* + %iptr = inttoptr i$WORD_SIZE %0 to $ilt* %ival = bitcast $lt %1 to $ilt %irv = atomicrmw $rmw $ilt* %iptr, $ilt %ival acq_rel %rv = bitcast $ilt %irv to $lt diff --git a/base/fastmath.jl b/base/fastmath.jl index b9c4f7db748b7..69b5cb7f758e5 100644 --- a/base/fastmath.jl +++ b/base/fastmath.jl @@ -296,34 +296,18 @@ asin_fast(x::FloatTypes) = asin(x) # explicit implementations -# FIXME: Change to `ccall((:sincos, libm))` when `Ref` calling convention can be -# stack allocated. @inline function sincos_fast(v::Float64) - return Base.llvmcall(""" - %f = bitcast i8 *%1 to void (double, double *, double *)* - %ps = alloca double - %pc = alloca double - call void %f(double %0, double *%ps, double *%pc) - %s = load double, double* %ps - %c = load double, double* %pc - %res0 = insertvalue [2 x double] undef, double %s, 0 - %res = insertvalue [2 x double] %res0, double %c, 1 - ret [2 x double] %res - """, Tuple{Float64,Float64}, Tuple{Float64,Ptr{Void}}, v, cglobal((:sincos, libm))) + s = Ref{Cdouble}() + c = Ref{Cdouble}() + ccall((:sincos, libm), Void, (Cdouble, Ptr{Cdouble}, Ptr{Cdouble}), v, s, c) + return (s[], c[]) end @inline function sincos_fast(v::Float32) - return Base.llvmcall(""" - %f = bitcast i8 *%1 to void (float, float *, float *)* - %ps = alloca float - %pc = alloca float - call void %f(float %0, float *%ps, float *%pc) - %s = load float, float* %ps - %c = load float, float* %pc - %res0 = insertvalue [2 x float] undef, float %s, 0 - %res = insertvalue [2 x float] %res0, float %c, 1 - ret [2 x float] %res - """, Tuple{Float32,Float32}, Tuple{Float32,Ptr{Void}}, v, cglobal((:sincosf, libm))) + s = Ref{Cfloat}() + c = Ref{Cfloat}() + ccall((:sincosf, libm), Void, (Cfloat, Ptr{Cfloat}, Ptr{Cfloat}), v, s, c) + return (s[], c[]) end @inline function sincos_fast(v::Float16) diff --git a/src/ccall.cpp b/src/ccall.cpp index 7512f24f0648b..d6dc4121114a5 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -182,7 +182,7 @@ static Value *runtime_sym_lookup( PHINode *p = irbuilder.CreatePHI(T_pvoidfunc, 2); p->addIncoming(llvmf_orig, enter_bb); p->addIncoming(llvmf, dlsym_lookup); - return irbuilder.CreatePointerCast(p, funcptype); + return irbuilder.CreateBitCast(p, funcptype); } static Value *runtime_sym_lookup( @@ -534,7 +534,7 @@ static Value *julia_to_address( { assert(jl_is_datatype(jlto) && julia_struct_has_layout((jl_datatype_t*)jlto, jlto_env)); - if (!jl_is_cpointer_type(jlto) || !to->isPointerTy()) { + if (!jl_is_cpointer_type(jlto) || to != T_size) { emit_error(ctx, "ccall: & on argument was not matched by Ptr{T} argument type"); return UndefValue::get(to); } @@ -547,19 +547,18 @@ static Value *julia_to_address( ety = jl_tparam0(jlto); typeassert_input(ctx, jvinfo, ety, jlto_env, argn, true); } - assert(to->isPointerTy()); if (jvinfo.isboxed) { if (!jl_is_abstracttype(ety)) { if (jl_is_mutable_datatype(ety)) { // no copy, just reference the data field - return data_pointer(ctx, jvinfo, to); + return ctx.builder.CreateBitCast(emit_pointer_from_objref(ctx, data_pointer(ctx, jvinfo)), to); } - else if (jl_is_immutable_datatype(ety) && jlto != (jl_value_t*)jl_voidpointer_type) { + else if (jl_is_immutable_datatype(ety) && jlto != (jl_value_t*)jl_voidpointer_type) { // anything declared `struct`, except Ptr{Void} // yes copy Value *nbytes; AllocaInst *ai; - if (jl_is_leaf_type(ety) || jl_is_primitivetype(ety)) { + if (((jl_datatype_t*)ety)->layout) { int nb = jl_datatype_size(ety); nbytes = ConstantInt::get(T_int32, nb); ai = emit_static_alloca(ctx, T_int8, nb); @@ -571,7 +570,7 @@ static Value *julia_to_address( } ai->setAlignment(16); ctx.builder.CreateMemCpy(ai, data_pointer(ctx, jvinfo, T_pint8), nbytes, sizeof(void*)); // minimum gc-alignment in julia is pointer size - return emit_bitcast(ctx, ai, to); + return ctx.builder.CreatePtrToInt(ai, to); } } // emit maybe copy @@ -583,14 +582,14 @@ static Value *julia_to_address( Value *ismutable = emit_datatype_mutabl(ctx, jvt); ctx.builder.CreateCondBr(ismutable, mutableBB, immutableBB); ctx.builder.SetInsertPoint(mutableBB); - Value *p1 = data_pointer(ctx, jvinfo, to); + Value *p1 = ctx.builder.CreateBitCast(emit_pointer_from_objref(ctx, data_pointer(ctx, jvinfo)), to); ctx.builder.CreateBr(afterBB); ctx.builder.SetInsertPoint(immutableBB); Value *nbytes = emit_datatype_size(ctx, jvt); AllocaInst *ai = ctx.builder.CreateAlloca(T_int8, nbytes); ai->setAlignment(16); ctx.builder.CreateMemCpy(ai, data_pointer(ctx, jvinfo, T_pint8), nbytes, sizeof(void*)); // minimum gc-alignment in julia is pointer size - Value *p2 = emit_bitcast(ctx, ai, to); + Value *p2 = ctx.builder.CreatePtrToInt(ai, to); ctx.builder.CreateBr(afterBB); ctx.builder.SetInsertPoint(afterBB); PHINode *p = ctx.builder.CreatePHI(to, 2); @@ -612,9 +611,7 @@ static Value *julia_to_address( (uint64_t)jl_datatype_size(ety), (uint64_t)jl_datatype_align(ety)); } - if (slot->getType() != to) - slot = emit_bitcast(ctx, slot, to); - return slot; + return ctx.builder.CreatePtrToInt(slot, to); } @@ -781,22 +778,21 @@ static jl_cgval_t emit_cglobal(jl_codectx_t &ctx, jl_value_t **args, size_t narg rt = (jl_value_t*)jl_voidpointer_type; } Type *lrt = julia_type_to_llvm(rt); - if (lrt == NULL) - lrt = T_pint8; interpret_symbol_arg(ctx, sym, args[1], "cglobal", false); if (sym.jl_ptr != NULL) { - res = ctx.builder.CreateIntToPtr(sym.jl_ptr, lrt); + res = ctx.builder.CreateBitCast(sym.jl_ptr, lrt); } else if (sym.fptr != NULL) { - res = literal_static_pointer_val(ctx, (void*)(uintptr_t)sym.fptr, lrt); + res = ConstantInt::get(lrt, (uint64_t)sym.fptr); if (imaging_mode) jl_printf(JL_STDERR,"WARNING: literal address used in cglobal for %s; code cannot be statically compiled\n", sym.f_name); } else { if (imaging_mode) { - res = runtime_sym_lookup(ctx, (PointerType*)lrt, sym.f_lib, sym.f_name, ctx.f); + res = runtime_sym_lookup(ctx, cast(T_pint8), sym.f_lib, sym.f_name, ctx.f); + res = ctx.builder.CreatePtrToInt(res, lrt); } else { void *symaddr = jl_dlsym_e(jl_get_library(sym.f_lib), sym.f_name); @@ -815,7 +811,7 @@ static jl_cgval_t emit_cglobal(jl_codectx_t &ctx, jl_value_t **args, size_t narg } // since we aren't saving this code, there's no sense in // putting anything complicated here: just JIT the address of the cglobal - res = literal_static_pointer_val(ctx, symaddr, lrt); + res = ConstantInt::get(lrt, (uint64_t)symaddr); } } @@ -1590,17 +1586,17 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) // some special functions if (is_libjulia_func(jl_array_ptr)) { - assert(lrt->isPointerTy()); + assert(lrt == T_size); assert(!isVa && !llvmcall && nargt == 1); assert(!addressOf.at(0)); const jl_cgval_t &ary = argv[0]; jl_value_t *aryex = ccallarg(0); JL_GC_POP(); - return mark_or_box_ccall_result(ctx, emit_bitcast(ctx, emit_arrayptr(ctx, ary, aryex), lrt), + return mark_or_box_ccall_result(ctx, ctx.builder.CreatePtrToInt(emit_arrayptr(ctx, ary, aryex), lrt), retboxed, rt, unionall, static_rt); } else if (is_libjulia_func(jl_value_ptr)) { - assert(lrt->isPointerTy()); + assert(retboxed ? lrt == T_prjlvalue : lrt == T_size); assert(!isVa && !llvmcall && nargt == 1); jl_value_t *tti = jl_svecref(at, 0); Value *ary; @@ -1612,7 +1608,7 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) } else if (jl_is_abstract_ref_type(tti)) { tti = (jl_value_t*)jl_voidpointer_type; - largty = T_pint8; + largty = T_size; isboxed = false; } else { @@ -1628,13 +1624,16 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) if (!retboxed) { return mark_or_box_ccall_result( ctx, - emit_bitcast(ctx, emit_pointer_from_objref(ctx, - emit_bitcast(ctx, ary, T_prjlvalue)), lrt), + emit_pointer_from_objref(ctx, + emit_bitcast(ctx, ary, T_prjlvalue)), retboxed, rt, unionall, static_rt); - } else { + } + else { return mark_or_box_ccall_result( ctx, - maybe_decay_untracked(emit_bitcast(ctx, ary, lrt)), + ctx.builder.CreateAddrSpaceCast( + ctx.builder.CreateIntToPtr(ary, T_pjlvalue), + T_prjlvalue), // TODO: this addrspace cast is invalid (implies that the value is rooted elsewhere) retboxed, rt, unionall, static_rt); } } @@ -1694,11 +1693,11 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) return ghostValue(jl_void_type); } else if (_is_libjulia_func((uintptr_t)ptls_getter, "jl_get_ptls_states")) { - assert(lrt == T_pint8); + assert(lrt == T_size); assert(!isVa && !llvmcall && nargt == 0); JL_GC_POP(); return mark_or_box_ccall_result(ctx, - emit_bitcast(ctx, ctx.ptlsStates, lrt), + ctx.builder.CreatePtrToInt(ctx.ptlsStates, lrt), retboxed, rt, unionall, static_rt); } else if (is_libjulia_func(jl_threadid)) { @@ -1771,6 +1770,7 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) } else if (is_libjulia_func(jl_function_ptr)) { assert(!isVa && !llvmcall && nargt == 3); + assert(lrt == T_size); jl_value_t *f = argv[0].constant; jl_value_t *frt = argv[1].constant; if (!frt) { @@ -1797,11 +1797,10 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) llvmf = NULL; } if (llvmf) { - llvmf = prepare_call(llvmf); JL_GC_POP(); JL_GC_POP(); - return mark_or_box_ccall_result(ctx, emit_bitcast(ctx, llvmf, lrt), - retboxed, rt, unionall, static_rt); + Value *fptr = ctx.builder.CreatePtrToInt(prepare_call(llvmf), lrt); + return mark_or_box_ccall_result(ctx, fptr, retboxed, rt, unionall, static_rt); } } JL_GC_POP(); @@ -1833,10 +1832,10 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) } } else if (is_libjulia_func(jl_string_ptr)) { - assert(lrt == T_pint8); + assert(lrt == T_size); assert(!isVa && !llvmcall && nargt == 1 && !addressOf.at(0)); - auto obj = emit_pointer_from_objref(ctx, boxed(ctx, argv[0])); - auto strp = ctx.builder.CreateConstGEP1_32(emit_bitcast(ctx, obj, T_pint8), sizeof(void*)); + Value *obj = emit_pointer_from_objref(ctx, boxed(ctx, argv[0])); + Value *strp = ctx.builder.CreateAdd(obj, ConstantInt::get(T_size, sizeof(void*))); JL_GC_POP(); return mark_or_box_ccall_result(ctx, strp, retboxed, rt, unionall, static_rt); } @@ -1939,9 +1938,6 @@ jl_cgval_t function_sig_t::emit_a_ccall( if (isa(v)) { return jl_cgval_t(); } - // A bit of a hack, but we're trying to get rid of this feature - // anyway. - v = emit_bitcast(ctx, emit_pointer_from_objref(ctx, v), pargty); assert((!toboxed && !byRef) || isa(v)); } diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 961f321a9450e..73b54e3ffb2c1 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -170,15 +170,8 @@ static DIType *julia_type_to_di(jl_value_t *jt, DIBuilder *dbuilder, bool isboxe return jl_pvalue_dillvmt; jl_datatype_t *jdt = (jl_datatype_t*)jt; // always return the boxed representation for types with hidden content - if (jl_is_abstracttype(jt) || jl_is_array_type(jt) || - jt == (jl_value_t*)jl_sym_type || jt == (jl_value_t*)jl_module_type || - jt == (jl_value_t*)jl_simplevector_type || jt == (jl_value_t*)jl_datatype_type || - jt == (jl_value_t*)jl_method_instance_type) - return jl_pvalue_dillvmt; - if (jdt->ditype != NULL) { - DIType* t = (DIType*)jdt->ditype; - return t; - } + if (jdt->ditype != NULL) + return (DIType*)jdt->ditype; if (jl_is_primitivetype(jt)) { uint64_t SizeInBits = jl_datatype_nbits(jdt); #if JL_LLVM_VERSION >= 40000 @@ -198,11 +191,11 @@ static DIType *julia_type_to_di(jl_value_t *jt, DIBuilder *dbuilder, bool isboxe return t; #endif } - if (jl_is_structtype(jt) && jdt->layout) { + if (jl_is_structtype(jt) && jdt->uid && jdt->layout && !jl_is_layout_opaque(jdt->layout)) { size_t ntypes = jl_datatype_nfields(jdt); const char *tname = jl_symbol_name(jdt->name->name); std::stringstream unique_name; - unique_name << tname << "_" << globalUnique++; + unique_name << jdt->uid; llvm::DICompositeType *ct = dbuilder->createStructType( NULL, // Scope tname, // Name @@ -219,8 +212,15 @@ static DIType *julia_type_to_di(jl_value_t *jt, DIBuilder *dbuilder, bool isboxe ); jdt->ditype = ct; std::vector Elements; - for (unsigned i = 0; i < ntypes; i++) - Elements.push_back(julia_type_to_di(jl_svecref(jdt->types, i), dbuilder, false)); + for (unsigned i = 0; i < ntypes; i++) { + jl_value_t *el = jl_svecref(jdt->types, i); + DIType *di; + if (jl_field_isptr(jdt, i)) + di = jl_pvalue_dillvmt; + else + di = julia_type_to_di(el, dbuilder, false); + Elements.push_back(di); + } dbuilder->replaceArrays(ct, dbuilder->getOrCreateArray(ArrayRef(Elements))); return ct; } @@ -233,7 +233,7 @@ static Value *emit_pointer_from_objref(jl_codectx_t &ctx, Value *V) { unsigned AS = cast(V->getType())->getAddressSpace(); if (AS != AddressSpace::Tracked && AS != AddressSpace::Derived) - return ctx.builder.CreateBitCast(V, T_pjlvalue); + return ctx.builder.CreatePtrToInt(V, T_size); V = ctx.builder.CreateBitCast(decay_derived(V), PointerType::get(T_jlvalue, AddressSpace::Derived)); CallInst *Call = ctx.builder.CreateCall(prepare_call(pointer_from_objref_func), V); @@ -427,18 +427,14 @@ static Type *bitstype_to_llvm(jl_value_t *bt) assert(jl_is_primitivetype(bt)); if (bt == (jl_value_t*)jl_bool_type) return T_int8; - if (bt == (jl_value_t*)jl_long_type) - return T_size; + if (bt == (jl_value_t*)jl_int32_type) + return T_int32; + if (bt == (jl_value_t*)jl_int64_type) + return T_int64; if (bt == (jl_value_t*)jl_float32_type) return T_float32; if (bt == (jl_value_t*)jl_float64_type) return T_float64; - if (jl_is_cpointer_type(bt)) { - Type *lt = julia_type_to_llvm(jl_tparam0(bt)); - if (lt == T_void) - return T_pint8; - return PointerType::get(lt, 0); - } int nb = jl_datatype_size(bt); return Type::getIntNTy(jl_LLVMContext, nb * 8); } @@ -481,121 +477,116 @@ static Type *julia_struct_to_llvm(jl_value_t *jt, jl_unionall_t *ua, bool *isbox return bitstype_to_llvm(jt); bool isTuple = jl_is_tuple_type(jt); jl_datatype_t *jst = (jl_datatype_t*)jt; + if (jst->struct_decl != NULL) + return (Type*)jst->struct_decl; if (jl_is_structtype(jt) && !(jst->layout && jl_is_layout_opaque(jst->layout))) { - if (jst->struct_decl == NULL) { - size_t i, ntypes = jl_svec_len(jst->types); - if (ntypes == 0 || (jst->layout && jl_datatype_nbits(jst) == 0)) - return T_void; - if (!julia_struct_has_layout(jst, ua)) - return NULL; - StructType *structdecl; - if (!isTuple) { - structdecl = StructType::create(jl_LLVMContext, jl_symbol_name(jst->name->name)); - jst->struct_decl = structdecl; - } - std::vector latypes(0); - bool isarray = true; - bool isvector = true; - jl_value_t *jlasttype = NULL; - Type *lasttype = NULL; - bool allghost = true; - for (i = 0; i < ntypes; i++) { - jl_value_t *ty = jl_svecref(jst->types, i); - if (jlasttype != NULL && ty != jlasttype) - isvector = false; - jlasttype = ty; - bool isptr; - size_t fsz = 0, al = 0; - if (jst->layout) { - isptr = jl_field_isptr(jst, i); - fsz = jl_field_size(jst, i); - al = jl_field_align(jst, i); - } - else { // compute what jl_compute_field_offsets would say - isptr = !jl_islayout_inline(ty, &fsz, &al); - if (!isptr && jl_is_uniontype(jst)) - fsz += 1; - } - Type *lty; - if (isptr) - lty = T_pjlvalue; - else if (ty == (jl_value_t*)jl_bool_type) - lty = T_int8; - else if (jl_is_uniontype(ty)) { - // pick an Integer type size such that alignment will be correct - // and always end with an Int8 (selector byte) - lty = ArrayType::get(IntegerType::get(jl_LLVMContext, 8 * al), (fsz - 1) / al); - std::vector Elements(2); - Elements[0] = lty; - Elements[1] = T_int8; - unsigned remainder = (fsz - 1) % al; - while (remainder--) - Elements.push_back(T_int8); - lty = StructType::get(jl_LLVMContext, makeArrayRef(Elements)); - } - else - lty = julia_type_to_llvm(ty); - if (lasttype != NULL && lasttype != lty) - isarray = false; - lasttype = lty; - if (type_is_ghost(lty)) - lty = NoopType; - else - allghost = false; - latypes.push_back(lty); + size_t i, ntypes = jl_svec_len(jst->types); + if (ntypes == 0 || (jst->layout && jl_datatype_nbits(jst) == 0)) + return T_void; + if (!julia_struct_has_layout(jst, ua)) + return NULL; + std::vector latypes(ntypes); + bool isarray = true; + bool isvector = true; + jl_value_t *jlasttype = NULL; + Type *lasttype = NULL; + bool allghost = true; + for (i = 0; i < ntypes; i++) { + jl_value_t *ty = jl_svecref(jst->types, i); + if (jlasttype != NULL && ty != jlasttype) + isvector = false; + jlasttype = ty; + bool isptr; + size_t fsz = 0, al = 0; + if (jst->layout) { + isptr = jl_field_isptr(jst, i); + fsz = jl_field_size(jst, i); + al = jl_field_align(jst, i); } - if (allghost) { - assert(jst->layout == NULL); // otherwise should have been caught above - jst->struct_decl = T_void; + else { // compute what jl_compute_field_offsets would say + isptr = !jl_islayout_inline(ty, &fsz, &al); + if (!isptr && jl_is_uniontype(jst)) + fsz += 1; } - else if (!isTuple) { - if (jl_is_vecelement_type(jt)) - // VecElement type is unwrapped in LLVM - jst->struct_decl = latypes[0]; - else - structdecl->setBody(latypes); - } - else { - if (isarray && lasttype != T_int1 && !type_is_ghost(lasttype)) { - if (isvector && jl_special_vector_alignment(ntypes, jlasttype) != 0) - jst->struct_decl = VectorType::get(lasttype, ntypes); - else - jst->struct_decl = ArrayType::get(lasttype, ntypes); - } - else { - jst->struct_decl = StructType::get(jl_LLVMContext, ArrayRef(&latypes[0], ntypes)); - } + Type *lty; + if (isptr) + lty = T_pjlvalue; + else if (ty == (jl_value_t*)jl_bool_type) + lty = T_int8; + else if (jl_is_uniontype(ty)) { + // pick an Integer type size such that alignment will be correct + // and always end with an Int8 (selector byte) + lty = ArrayType::get(IntegerType::get(jl_LLVMContext, 8 * al), (fsz - 1) / al); + std::vector Elements(2); + Elements[0] = lty; + Elements[1] = T_int8; + unsigned remainder = (fsz - 1) % al; + while (remainder--) + Elements.push_back(T_int8); + lty = StructType::get(jl_LLVMContext, Elements); } + else + lty = julia_type_to_llvm(ty); + if (lasttype != NULL && lasttype != lty) + isarray = false; + lasttype = lty; + if (type_is_ghost(lty)) + lty = NoopType; + else + allghost = false; + latypes[i] = lty; + } + Type *decl; + if (allghost) { + assert(jst->layout == NULL); // otherwise should have been caught above + decl = T_void; + } + else if (jl_is_vecelement_type(jt)) { + // VecElement type is unwrapped in LLVM + decl = latypes[0]; + } + else if (isTuple && isarray && lasttype != T_int1 && !type_is_ghost(lasttype)) { + if (isvector && jl_special_vector_alignment(ntypes, jlasttype) != 0) + decl = VectorType::get(lasttype, ntypes); + else + decl = ArrayType::get(lasttype, ntypes); + } + else { + decl = StructType::get(jl_LLVMContext, latypes); + } + jst->struct_decl = decl; #ifndef JL_NDEBUG - // If LLVM and Julia disagree about alignment, much trouble ensues, so check it! - if (jst->layout) { - const DataLayout &DL = + // If LLVM and Julia disagree about alignment, much trouble ensues, so check it! + if (jst->layout) { + const DataLayout &DL = #if JL_LLVM_VERSION >= 40000 - jl_data_layout; + jl_data_layout; #else - jl_ExecutionEngine->getDataLayout(); -#endif - unsigned llvm_alignment = DL.getABITypeAlignment((Type*)jst->struct_decl); - unsigned julia_alignment = jl_datatype_align(jst); - // Check that the alignment adheres to the heap alignment. - assert(julia_alignment <= JL_HEAP_ALIGNMENT); - // TODO: Fix alignment calculation in LLVM, as well as in the GC and the struct declaration - if (llvm_alignment <= JL_HEAP_ALIGNMENT) - assert(julia_alignment == llvm_alignment); - } + jl_ExecutionEngine->getDataLayout(); #endif + unsigned llvm_alignment = DL.getABITypeAlignment((Type*)jst->struct_decl); + unsigned julia_alignment = jl_datatype_align(jst); + // Check that the alignment adheres to the heap alignment. + assert(julia_alignment <= JL_HEAP_ALIGNMENT); + // TODO: Fix alignment calculation in LLVM, as well as in the GC and the struct declaration + if (llvm_alignment <= JL_HEAP_ALIGNMENT) + assert(julia_alignment == llvm_alignment); } - return (Type*)jst->struct_decl; +#endif + return decl; } // TODO: enable this (with tests): // if (jl_is_uniontype(ty)) { - // size_t fsz = 0, al = 0; - // bool isptr = !jl_islayout_inline(ty, &fsz, &al); - // // pick an Integer type size such that alignment will be correct - // return StructType::get(jl_LLVMContext, makeArrayRef({ - // ArrayType::get(IntegerType::get(jl_LLVMContext, 8 * al), - // (fsz + al - 1) / al), - // T_int8 })); + // // pick an Integer type size such that alignment will be correct + // // and always end with an Int8 (selector byte) + // lty = ArrayType::get(IntegerType::get(jl_LLVMContext, 8 * al), (fsz - 1) / al); + // std::vector Elements(2); + // Elements[0] = lty; + // Elements[1] = T_int8; + // unsigned remainder = (fsz - 1) % al; + // while (remainder--) + // Elements.push_back(T_int8); + // lty = StructType::get(jl_LLVMContext, makeArrayRef(Elements)); // } if (isboxed) *isboxed = true; return T_pjlvalue; @@ -604,7 +595,7 @@ static Type *julia_struct_to_llvm(jl_value_t *jt, jl_unionall_t *ua, bool *isbox static bool is_datatype_all_pointers(jl_datatype_t *dt) { size_t i, l = jl_datatype_nfields(dt); - for(i=0; i < l; i++) { + for (i = 0; i < l; i++) { if (!jl_field_isptr(dt, i)) { return false; } @@ -623,9 +614,9 @@ static bool is_tupletype_homogeneous(jl_svec_t *t, bool allow_va = false) return true; return false; } - for(i=1; i < l; i++) { - if (allow_va && i == l - 1 && jl_is_vararg_type(jl_svecref(t,i))) { - if (t0 != jl_unwrap_vararg(jl_svecref(t,i))) + for (i = 1; i < l; i++) { + if (allow_va && i == l - 1 && jl_is_vararg_type(jl_svecref(t, i))) { + if (t0 != jl_unwrap_vararg(jl_svecref(t, i))) return false; continue; } @@ -1210,9 +1201,10 @@ static jl_cgval_t typed_load(jl_codectx_t &ctx, Value *ptr, Value *idx_0based, j return ghostValue(jltype); if (isboxed) elty = T_prjlvalue; - Value *data = ptr; - if (ptr->getType()->getContainedType(0) != elty) - data = emit_bitcast(ctx, ptr, PointerType::get(elty, 0)); + Type *ptrty = PointerType::get(elty, ptr->getType()->getPointerAddressSpace()); + Value *data; + if (ptr->getType() != ptrty) + data = emit_bitcast(ctx, ptr, ptrty); else data = ptr; if (idx_0based) @@ -1260,14 +1252,16 @@ static void typed_store(jl_codectx_t &ctx, emit_write_barrier(ctx, parent, r); } Value *data; - if (ptr->getType()->getContainedType(0) != elty) { + Type *ptrty = PointerType::get(elty, ptr->getType()->getPointerAddressSpace()); + if (ptr->getType() != ptrty) { if (isboxed) { data = emit_bitcast(ctx, ptr, T_pprjlvalue); } else { - data = emit_bitcast(ctx, ptr, PointerType::get(elty, cast(ptr->getType())->getAddressSpace())); + data = emit_bitcast(ctx, ptr, ptrty); } - } else + } else { data = ptr; + } Instruction *store = ctx.builder.CreateAlignedStore(r, ctx.builder.CreateGEP(data, idx_0based), isboxed ? alignment : julia_alignment(r, jltype, alignment)); if (tbaa) @@ -2258,67 +2252,81 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg if (nf > 0) { if (jl_isbits(sty)) { Type *lt = julia_type_to_llvm(ty); + unsigned na = (nargs - 1 < nf) ? (nargs - 1) : nf; + // whether we should perform the initialization with the struct as a IR value // or instead initialize the stack buffer with stores bool init_as_value = false; if (lt->isVectorTy() || - jl_is_vecelement_type(ty) || - type_is_ghost(lt)) // maybe also check the size ? + jl_is_vecelement_type(ty)) { // maybe also check the size ? init_as_value = true; + } - unsigned na = (nargs - 1 < nf) ? (nargs - 1) : nf; Value *strct; - if (init_as_value) - strct = UndefValue::get(lt == T_void ? NoopType : lt); + if (type_is_ghost(lt)) + strct = NULL; + else if (init_as_value) + strct = UndefValue::get(lt); else strct = emit_static_alloca(ctx, lt); for (unsigned i = 0; i < na; i++) { jl_value_t *jtype = jl_svecref(sty->types, i); - Type *fty = julia_struct_to_llvm(jtype, NULL, NULL); const jl_cgval_t &fval_info = argv[i + 1]; emit_typecheck(ctx, fval_info, jtype, "new"); + Type *fty; + if (type_is_ghost(lt)) + continue; + else if (jl_is_vecelement_type(ty)) + fty = lt; + else + fty = cast(lt)->getTypeAtIndex(i); + if (type_is_ghost(fty)) + continue; Value *dest = NULL; if (!init_as_value) { - bool isunion = jl_is_uniontype(jtype); - if (!type_is_ghost(fty) || isunion) { - // avoid unboxing the argument explicitly - // and use memcpy instead - dest = ctx.builder.CreateConstInBoundsGEP2_32(lt, strct, 0, i); - if (isunion) { - // compute tindex from rhs - jl_cgval_t rhs_union = convert_julia_type(ctx, fval_info, jtype); - if (rhs_union.typ == jl_bottom_type) - return jl_cgval_t(); - Value *tindex = compute_tindex_unboxed(ctx, rhs_union, jtype); - tindex = ctx.builder.CreateNUWSub(tindex, ConstantInt::get(T_int8, 1)); - StructType *lt_i = cast(cast(dest)->getResultElementType()); - Value *ptindex = ctx.builder.CreateConstInBoundsGEP2_32( - lt_i, dest, 0, lt_i->getNumElements() - 1); - ctx.builder.CreateStore(tindex, ptindex); - if (!rhs_union.isghost) { - emit_unionmove(ctx, dest, fval_info, NULL, false, NULL); - } - continue; - } - } + // avoid unboxing the argument explicitly + // and use memcpy instead + dest = ctx.builder.CreateConstInBoundsGEP2_32(lt, strct, 0, i); + } + Value *fval = NULL; + if (jl_is_uniontype(jtype)) { + assert(!init_as_value && "unimplemented"); + StructType *lt_i = cast(fty); + // compute tindex from rhs + jl_cgval_t rhs_union = convert_julia_type(ctx, fval_info, jtype); + if (rhs_union.typ == jl_bottom_type) + return jl_cgval_t(); + Value *tindex = compute_tindex_unboxed(ctx, rhs_union, jtype); + tindex = ctx.builder.CreateNUWSub(tindex, ConstantInt::get(T_int8, 1)); + Value *ptindex = ctx.builder.CreateConstInBoundsGEP2_32( + lt_i, dest, 0, lt_i->getNumElements() - 1); + ctx.builder.CreateStore(tindex, ptindex); + if (!rhs_union.isghost) + emit_unionmove(ctx, dest, fval_info, NULL, false, NULL); + // If you wanted to implement init_as_value, + // would need to emit the union-move into temporary memory, + // then load it and combine with the tindex. + // But more efficient to just store it directly. } - if (!type_is_ghost(fty)) { - Value *fval = NULL; + else { fval = emit_unbox(ctx, fty, fval_info, jtype, dest); - if (init_as_value) { - if (lt->isVectorTy()) - strct = ctx.builder.CreateInsertElement(strct, fval, ConstantInt::get(T_int32, i)); - else if (jl_is_vecelement_type(ty)) - strct = fval; // VecElement type comes unwrapped in LLVM. - else if (lt->isAggregateType()) - strct = ctx.builder.CreateInsertValue(strct, fval, ArrayRef(&i, 1)); - else - assert(false); - } + } + if (init_as_value) { + assert(fval); + if (lt->isVectorTy()) + strct = ctx.builder.CreateInsertElement(strct, fval, ConstantInt::get(T_int32, i)); + else if (jl_is_vecelement_type(ty)) + strct = fval; // VecElement type comes unwrapped in LLVM. + else if (lt->isAggregateType()) + strct = ctx.builder.CreateInsertValue(strct, fval, ArrayRef(&i, 1)); + else + assert(false); } } - if (init_as_value) + if (type_is_ghost(lt)) + return mark_julia_const(sty->instance); + else if (init_as_value) return mark_julia_type(ctx, strct, false, ty); else return mark_julia_slot(strct, ty, NULL, tbaa_stack); diff --git a/src/codegen.cpp b/src/codegen.cpp index d85b087a89883..52a8e2583d1fc 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6505,7 +6505,7 @@ static void init_julia_llvm_env(Module *m) "julia.gc_use"); add_named_global(gc_use_func, (void*)NULL, /*dllimport*/false); - pointer_from_objref_func = Function::Create(FunctionType::get(T_pjlvalue, + pointer_from_objref_func = Function::Create(FunctionType::get(T_size, ArrayRef(PointerType::get(T_jlvalue, AddressSpace::Derived)), false), Function::ExternalLinkage, "julia.pointer_from_objref"); diff --git a/src/intrinsics.cpp b/src/intrinsics.cpp index 98dcb81ba6738..f59169287a0e0 100644 --- a/src/intrinsics.cpp +++ b/src/intrinsics.cpp @@ -146,99 +146,113 @@ static Value *uint_cnvt(jl_codectx_t &ctx, Type *to, Value *x) return ctx.builder.CreateZExt(x, to); } -#if JL_LLVM_VERSION >= 40000 -#define LLVM_FP(a,b) APFloat(a(),b) -#else -#define LLVM_FP(a,b) APFloat(a,b) -#endif -static Constant *julia_const_to_llvm(void *ptr, jl_value_t *bt) +static Constant *julia_const_to_llvm(const void *ptr, jl_datatype_t *bt) { // assumes `jl_isbits(bt)`. // `ptr` can point to a inline field, do not read the tag from it. // make sure to return exactly the type specified by // julia_type_to_llvm as this will be assumed by the callee. - if (bt == (jl_value_t*)jl_bool_type) - return ConstantInt::get(T_int8, (*(uint8_t*)ptr) ? 1 : 0); + if (bt == jl_bool_type) + return ConstantInt::get(T_int8, (*(const uint8_t*)ptr) ? 1 : 0); - if (bt == (jl_value_t*)jl_ssavalue_type) - return NULL; + if (jl_is_vecelement_type((jl_value_t*)bt)) + bt = (jl_datatype_t*)jl_tparam0(bt); + + Type *lt = julia_struct_to_llvm((jl_value_t*)bt, NULL, NULL); - if (jl_is_vecelement_type(bt)) - bt = jl_tparam0(bt); + if (type_is_ghost(lt)) + return UndefValue::get(NoopType); - if (jl_is_cpointer_type(bt)) - return ConstantExpr::getIntToPtr(ConstantInt::get(T_size, *(uintptr_t*)ptr), julia_type_to_llvm(bt)); if (jl_is_primitivetype(bt)) { - int nb = jl_datatype_size(bt); - // TODO: non-power-of-2 size datatypes may not be interpreted correctly on big-endian systems - switch (nb) { - case 1: { - uint8_t data8 = *(uint8_t*)ptr; - return ConstantInt::get(T_int8, data8); - } - case 2: { - uint16_t data16 = *(uint16_t*)ptr; - return ConstantInt::get(T_int16, data16); - } - case 4: { - uint32_t data32 = *(uint32_t*)ptr; - if (bt == (jl_value_t*)jl_float32_type) - return ConstantFP::get(jl_LLVMContext, - LLVM_FP(APFloat::IEEEsingle, - APInt(32, data32))); - return ConstantInt::get(T_int32, data32); + if (lt->isFloatTy()) { + uint32_t data32 = *(const uint32_t*)ptr; + return ConstantFP::get(jl_LLVMContext, + APFloat(lt->getFltSemantics(), APInt(32, data32))); } - case 8: { - uint64_t data64 = *(uint64_t*)ptr; - if (bt == (jl_value_t*)jl_float64_type) - return ConstantFP::get(jl_LLVMContext, - LLVM_FP(APFloat::IEEEdouble, - APInt(64, data64))); - return ConstantInt::get(T_int64, data64); + if (lt->isDoubleTy()) { + uint64_t data64 = *(const uint64_t*)ptr; + return ConstantFP::get(jl_LLVMContext, + APFloat(lt->getFltSemantics(), APInt(64, data64))); } - default: - size_t nw = (nb+sizeof(uint64_t)-1)/sizeof(uint64_t); - uint64_t *data = (uint64_t*)ptr; - APInt val; -#if !defined(_P64) - // malloc may not be 16-byte aligned on P32, - // but we must ensure that llvm's uint64_t reads don't fall - // off the end of a page - // where 16-byte alignment requirement == (8-byte typetag) % (uint64_t ArrayRef access) - if (nb % 16 != 0) { - uint64_t *data_a64 = (uint64_t*)alloca(sizeof(uint64_t)*nw); - memcpy(data_a64, data, nb); - val = APInt(8*nb, ArrayRef(data_a64, nw)); - } - else -#endif - val = APInt(8*nb, ArrayRef(data, nw)); - return ConstantInt::get(IntegerType::get(jl_LLVMContext,8*nb),val); + int nb = jl_datatype_size(bt); + APInt val(8 * nb, 0); + void *bits = const_cast(val.getRawData()); + assert(sys::IsLittleEndianHost); + memcpy(bits, ptr, nb); + if (lt->isFloatingPointTy()) { + return ConstantFP::get(jl_LLVMContext, + APFloat(lt->getFltSemantics(), val)); } + assert(cast(lt)->getBitWidth() == 8u * nb); + return ConstantInt::get(lt, val); } + + CompositeType *lct = cast(lt); size_t nf = jl_datatype_nfields(bt); - Constant **fields = (Constant**)alloca(nf * sizeof(Constant*)); + std::vector fields(nf); for (size_t i = 0; i < nf; i++) { - size_t offs = jl_field_offset((jl_datatype_t*)bt, i); + size_t offs = jl_field_offset(bt, i); + assert(!jl_field_isptr(bt, i)); jl_value_t *ft = jl_field_type(bt, i); - Constant *val = julia_const_to_llvm((char*)ptr + offs, ft); - if (val == NULL) - return NULL; + Type *lft = lct->getTypeAtIndex(i); + const uint8_t *ov = (const uint8_t*)ptr + offs; + Constant *val; + if (jl_is_uniontype(ft)) { + // compute the same type layout as julia_struct_to_llvm + size_t fsz = jl_field_size(bt, i); + size_t al = jl_field_align(bt, i); + uint8_t sel = ((const uint8_t*)ptr)[offs + fsz - 1]; + jl_value_t *active_ty = jl_nth_union_component(ft, sel); + size_t active_sz = jl_datatype_size(active_ty); + ArrayType *aty = cast(cast(lft)->getTypeAtIndex(0u)); + assert(aty->getElementType() == IntegerType::get(jl_LLVMContext, 8 * al) && + aty->getNumElements() == (fsz - 1) / al); + std::vector ArrayElements(0); + for (unsigned j = 0; j < aty->getNumElements(); j++) { + APInt Elem(8 * al, 0); + void *bits = const_cast(Elem.getRawData()); + if (active_sz > al) { + memcpy(bits, ov, al); + active_sz -= al; + } + else if (active_sz > 0) { + memcpy(bits, ov, active_sz); + active_sz = 0; + } + ov += al; + ArrayElements.push_back(ConstantInt::get(aty->getElementType(), Elem)); + } + std::vector Elements(0); + Elements.push_back(ConstantArray::get(aty, ArrayElements)); + unsigned remainder = (fsz - 1) % al; + while (remainder--) { + uint8_t byte; + if (active_sz > 0) { + byte = *ov; + active_sz -= 1; + } + else { + byte = 0; + } + ov += 1; + APInt Elem(8, byte); + Elements.push_back(ConstantInt::get(T_int8, Elem)); + } + Elements.push_back(ConstantInt::get(T_int8, sel)); + val = ConstantStruct::get(cast(lft), Elements); + } + else { + val = julia_const_to_llvm(ov, (jl_datatype_t*)ft); + } fields[i] = val; } - Type *t = julia_struct_to_llvm(bt, NULL, NULL); - if (type_is_ghost(t)) - return UndefValue::get(NoopType); - if (t->isVectorTy()) - return ConstantVector::get(ArrayRef(fields, nf)); - if (StructType *st = dyn_cast(t)) { - return ConstantStruct::get(st, ArrayRef(fields, nf)); - } - else { - ArrayType *at = cast(t); - return ConstantArray::get(at, ArrayRef(fields, nf)); - } + if (lct->isVectorTy()) + return ConstantVector::get(fields); + if (StructType *st = dyn_cast(lct)) + return ConstantStruct::get(st, fields); + ArrayType *at = cast(lct); + return ConstantArray::get(at, fields); } static Constant *julia_const_to_llvm(jl_value_t *e) @@ -250,7 +264,7 @@ static Constant *julia_const_to_llvm(jl_value_t *e) jl_value_t *bt = jl_typeof(e); if (!jl_isbits(bt)) return NULL; - return julia_const_to_llvm(e, bt); + return julia_const_to_llvm(e, (jl_datatype_t*)bt); } static jl_cgval_t ghostValue(jl_value_t *ty); @@ -577,15 +591,15 @@ static jl_cgval_t emit_pointerref(jl_codectx_t &ctx, jl_cgval_t *argv) Value *idx = emit_unbox(ctx, T_size, i, (jl_value_t*)jl_long_type); Value *im1 = ctx.builder.CreateSub(idx, ConstantInt::get(T_size, 1)); - if (!jl_isbits(ety)) { - if (ety == (jl_value_t*)jl_any_type) { - Value *thePtr = emit_unbox(ctx, T_pprjlvalue, e, e.typ); - return mark_julia_type( - ctx, - ctx.builder.CreateAlignedLoad(ctx.builder.CreateGEP(thePtr, im1), align_nb), - true, - ety); - } + if (ety == (jl_value_t*)jl_any_type) { + Value *thePtr = emit_unbox(ctx, T_pprjlvalue, e, e.typ); + return mark_julia_type( + ctx, + ctx.builder.CreateAlignedLoad(ctx.builder.CreateGEP(T_prjlvalue, thePtr, im1), align_nb), + true, + ety); + } + else if (!jl_isbits(ety)) { if (!jl_is_structtype(ety) || jl_is_array_type(ety) || !jl_is_leaf_type(ety)) { emit_error(ctx, "pointerref: invalid pointer type"); return jl_cgval_t(); @@ -597,16 +611,17 @@ static jl_cgval_t emit_pointerref(jl_codectx_t &ctx, jl_cgval_t *argv) im1 = ctx.builder.CreateMul(im1, ConstantInt::get(T_size, LLT_ALIGN(size, jl_datatype_align(ety)))); Value *thePtr = emit_unbox(ctx, T_pint8, e, e.typ); - thePtr = ctx.builder.CreateGEP(emit_bitcast(ctx, thePtr, T_pint8), im1); + thePtr = ctx.builder.CreateGEP(T_int8, emit_bitcast(ctx, thePtr, T_pint8), im1); ctx.builder.CreateMemCpy(emit_bitcast(ctx, strct, T_pint8), thePtr, size, 1); return mark_julia_type(ctx, strct, true, ety); } - - bool isboxed; - Type *ptrty = julia_type_to_llvm(e.typ, &isboxed); - assert(!isboxed); - Value *thePtr = emit_unbox(ctx, ptrty, e, e.typ); - return typed_load(ctx, thePtr, im1, ety, tbaa_data, true, align_nb); + else { + bool isboxed; + Type *ptrty = julia_type_to_llvm(ety, &isboxed); + assert(!isboxed); + Value *thePtr = emit_unbox(ctx, ptrty->getPointerTo(), e, e.typ); + return typed_load(ctx, thePtr, im1, ety, tbaa_data, true, align_nb); + } } static jl_cgval_t emit_runtime_pointerset(jl_codectx_t &ctx, jl_cgval_t *argv) @@ -644,7 +659,15 @@ static jl_cgval_t emit_pointerset(jl_codectx_t &ctx, jl_cgval_t *argv) Value *im1 = ctx.builder.CreateSub(idx, ConstantInt::get(T_size, 1)); Value *thePtr; - if (!jl_isbits(ety) && ety != (jl_value_t*)jl_any_type) { + if (ety == (jl_value_t*)jl_any_type) { + // unsafe_store to Ptr{Any} is allowed to implicitly drop GC roots. + thePtr = emit_unbox(ctx, T_psize, e, e.typ); + Instruction *store = ctx.builder.CreateAlignedStore( + emit_pointer_from_objref(ctx, boxed(ctx, x)), + ctx.builder.CreateGEP(T_size, thePtr, im1), align_nb); + tbaa_decorate(tbaa_data, store); + } + else if (!jl_isbits(ety)) { if (!jl_is_structtype(ety) || jl_is_array_type(ety) || !jl_is_leaf_type(ety)) { emit_error(ctx, "pointerset: invalid pointer type"); return jl_cgval_t(); @@ -653,23 +676,15 @@ static jl_cgval_t emit_pointerset(jl_codectx_t &ctx, jl_cgval_t *argv) uint64_t size = jl_datatype_size(ety); im1 = ctx.builder.CreateMul(im1, ConstantInt::get(T_size, LLT_ALIGN(size, jl_datatype_align(ety)))); - ctx.builder.CreateMemCpy(ctx.builder.CreateGEP(thePtr, im1), + ctx.builder.CreateMemCpy(ctx.builder.CreateGEP(T_int8, thePtr, im1), data_pointer(ctx, x, T_pint8), size, align_nb); } else { bool isboxed; - Type *ptrty = julia_type_to_llvm(e.typ, &isboxed); + Type *ptrty = julia_type_to_llvm(ety, &isboxed); assert(!isboxed); - thePtr = emit_unbox(ctx, ptrty, e, e.typ); - if (ety == (jl_value_t*)jl_any_type) { - // unsafe_store to Ptr{Any} is allowed to implicitly drop GC roots. - Instruction *store = ctx.builder.CreateAlignedStore( - emit_pointer_from_objref(ctx, boxed(ctx, x)), - ctx.builder.CreateGEP(thePtr, im1), align_nb); - tbaa_decorate(tbaa_data, store); - } else { - typed_store(ctx, thePtr, im1, x, ety, tbaa_data, NULL, align_nb); - } + thePtr = emit_unbox(ctx, ptrty->getPointerTo(), e, e.typ); + typed_store(ctx, thePtr, im1, x, ety, tbaa_data, NULL, align_nb); } return mark_julia_type(ctx, thePtr, false, aty); } diff --git a/src/llvm-alloc-opt.cpp b/src/llvm-alloc-opt.cpp index 51d9403c96342..d559de07de7eb 100644 --- a/src/llvm-alloc-opt.cpp +++ b/src/llvm-alloc-opt.cpp @@ -491,6 +491,7 @@ void AllocOpt::replaceUsesWith(Instruction *orig_inst, Instruction *new_inst, } else if (auto call = dyn_cast(user)) { if (ptr_from_objref && ptr_from_objref == call->getCalledFunction()) { + new_i = new PtrToIntInst(new_i, T_size, "", call); call->replaceAllUsesWith(new_i); call->eraseFromParent(); return; diff --git a/src/llvm-late-gc-lowering.cpp b/src/llvm-late-gc-lowering.cpp index 4e15b2955f4f5..6c62c73809bc6 100644 --- a/src/llvm-late-gc-lowering.cpp +++ b/src/llvm-late-gc-lowering.cpp @@ -1156,10 +1156,9 @@ bool LateLowerGCFrame::CleanupIR(Function &F) { (gc_use_func != nullptr && callee == gc_use_func)) { /* No replacement */ } else if (pointer_from_objref_func != nullptr && callee == pointer_from_objref_func) { - auto *ASCI = new AddrSpaceCastInst(CI->getOperand(0), - CI->getType(), "", CI); - ASCI->takeName(CI); - CI->replaceAllUsesWith(ASCI); + auto *ptr = new PtrToIntInst(CI->getOperand(0), CI->getType(), "", CI); + ptr->takeName(CI); + CI->replaceAllUsesWith(ptr); } else if (alloc_obj_func && callee == alloc_obj_func) { assert(CI->getNumArgOperands() == 3); auto sz = (size_t)cast(CI->getArgOperand(1))->getZExtValue(); diff --git a/test/core.jl b/test/core.jl index 291030e8fd249..dca835dfc0a49 100644 --- a/test/core.jl +++ b/test/core.jl @@ -5316,19 +5316,23 @@ x.u = initvalue2(Base.uniontypes(U)[1]) x.u = initvalue(Base.uniontypes(U)[2]) @test x.u === initvalue(Base.uniontypes(U)[2]) -struct AA +# PR #23367 +struct A23367 x::Union{Int8, Int16, NTuple{7, Int8}, Void} end -struct B +struct B23367 x::Int8 - y::AA + y::A23367 z::Int8 end @noinline compare(a, b) = (a === b) # test code-generation of `is` +@noinline get_x(a::A23367) = a.x +function constant23367 end let - b = B(91, AA(ntuple(i -> Int8(i), Val(7))), 23) + b = B23367(91, A23367(ntuple(i -> Int8(i), Val(7))), 23) + @eval @noinline constant23367(a, b) = (a ? b : $b) b2 = Ref(b)[] # copy b via field assignment - b3 = B[b][1] # copy b via array assignment + b3 = B23367[b][1] # copy b via array assignment @test pointer_from_objref(b) == pointer_from_objref(b) @test pointer_from_objref(b) != pointer_from_objref(b2) @test pointer_from_objref(b) != pointer_from_objref(b3) @@ -5340,13 +5344,19 @@ let @test object_id(b) === object_id(b2) == object_id(b3) @test b.x === Int8(91) @test b.z === Int8(23) - @test b.y === AA((Int8(1), Int8(2), Int8(3), Int8(4), Int8(5), Int8(6), Int8(7))) + @test b.y === A23367((Int8(1), Int8(2), Int8(3), Int8(4), Int8(5), Int8(6), Int8(7))) @test sizeof(b) == 12 - @test AA(Int8(1)).x === Int8(1) - @test AA(Int8(0)).x === Int8(0) - @test AA(Int16(1)).x === Int16(1) - @test AA(nothing).x === nothing + @test A23367(Int8(1)).x === Int8(1) + @test A23367(Int8(0)).x === Int8(0) + @test A23367(Int16(1)).x === Int16(1) + @test A23367(nothing).x === nothing @test sizeof(b.y) == 8 + @test get_x(A23367(Int8(1))) === Int8(1) + + # test code-generation of constants + other = B23367(91, A23367(nothing), 23) + @test constant23367(true, other) === other + @test constant23367(false, other) === b end for U in boxedunions @@ -5364,10 +5374,12 @@ for U in boxedunions end # unsafe_wrap -A4 = [1, 2, 3] -@test_throws ArgumentError unsafe_wrap(Array, convert(Ptr{Union{Int, Void}}, pointer(A4)), 3) -A5 = [1 2 3; 4 5 6] -@test_throws ArgumentError unsafe_wrap(Array, convert(Ptr{Union{Int, Void}}, pointer(A5)), 6) +let + A4 = [1, 2, 3] + @test_throws ArgumentError unsafe_wrap(Array, convert(Ptr{Union{Int, Void}}, pointer(A4)), 3) + A5 = [1 2 3; 4 5 6] + @test_throws ArgumentError unsafe_wrap(Array, convert(Ptr{Union{Int, Void}}, pointer(A5)), 6) +end # copy! A23567 = Vector{Union{Float64, Void}}(5) diff --git a/test/libgit2.jl b/test/libgit2.jl index 72c964cb9be0a..d93bb3615c177 100644 --- a/test/libgit2.jl +++ b/test/libgit2.jl @@ -76,6 +76,8 @@ end a = Base.cconvert(Ptr{LibGit2.StrArrayStruct}, p) b = Base.unsafe_convert(Ptr{LibGit2.StrArrayStruct}, a) @test p == convert(Vector{String}, unsafe_load(b)) + @noinline gcuse(a) = a + gcuse(a) end @testset "Signature" begin From 503a091586be2f4ddd76707803e0fd9c3f1c9210 Mon Sep 17 00:00:00 2001 From: Allen Hill Date: Tue, 5 Sep 2017 01:39:12 -0400 Subject: [PATCH 273/324] Switch Cygwin download links to https (#23578) --- README.windows.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.windows.md b/README.windows.md index 05a31ee1f3104..92c2443b83914 100644 --- a/README.windows.md +++ b/README.windows.md @@ -80,8 +80,8 @@ The recommended way of compiling Julia from source on Windows is by cross compiling from [Cygwin](http://www.cygwin.com), using versions of the MinGW-w64 compilers available through Cygwin's package manager. - 1. Download and run Cygwin setup for [32 bit](http://cygwin.com/setup-x86.exe) - or [64 bit](http://cygwin.com/setup-x86_64.exe). Note, that you can compile + 1. Download and run Cygwin setup for [32 bit](https://cygwin.com/setup-x86.exe) + or [64 bit](https://cygwin.com/setup-x86_64.exe). Note, that you can compile either 32 or 64 bit Julia from either 32 or 64 bit Cygwin. 64 bit Cygwin has a slightly smaller but often more up-to-date selection of packages. From 582ba828820c80b9b50818f6f59c9875501a591b Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Tue, 5 Sep 2017 09:24:36 +0200 Subject: [PATCH 274/324] remove mention of helpdb/Base.jl in CONTRIBUTING.md (#23587) --- CONTRIBUTING.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 340a5b03c765f..14e7fb37f1e78 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -141,10 +141,6 @@ All docstrings are written inline above the methods or types they are associated 4. check the output in `doc/_build/html/` to make sure the changes are correct; 5. commit your changes and open a pull request. -> **Note** -> -> Currently there are a large number of docstrings found in `base/docs/helpdb/Base.jl`. When any of these docstrings are modified please move them out of this file and place them above the most appropriate definition in one of the `base/` source files. - #### Adding a new docstring to `base/` The steps required to add a new docstring are listed below: From eb05225db1f5ae6b472fb2c3ab2137ddb68ebbac Mon Sep 17 00:00:00 2001 From: Magnus Lie Hetland Date: Tue, 5 Sep 2017 10:24:17 +0200 Subject: [PATCH 275/324] Correct default of writable in doc for IOBuffer Changed default for writable to false in the docs. Also removed superfluous comma in doc, and adjusted brackets for optionals. --- base/iobuffer.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/iobuffer.jl b/base/iobuffer.jl index 15bcd54b1b788..9da496d729a16 100644 --- a/base/iobuffer.jl +++ b/base/iobuffer.jl @@ -32,7 +32,7 @@ StringVector(n::Integer) = Vector{UInt8}(_string_n(n)) # IOBuffers behave like Files. They are typically readable and writable. They are seekable. (They can be appendable). """ - IOBuffer([data,],[readable::Bool=true, writable::Bool=true, [maxsize::Int=typemax(Int)]]) + IOBuffer([data, ][readable::Bool=true, writable::Bool=false[, maxsize::Int=typemax(Int)]]) Create an `IOBuffer`, which may optionally operate on a pre-existing array. If the readable/writable arguments are given, they restrict whether or not the buffer may be read From be83cac845c8f2817bd77fd2e88701e74323a36e Mon Sep 17 00:00:00 2001 From: Evey Date: Tue, 5 Sep 2017 17:15:30 +0800 Subject: [PATCH 276/324] REPL: Add ^P and ^N to modal prefix searching (#23319) * Add ^P and ^N to modal prefix searching * Add meta-P, meta-N as synonyms for page up/page down in REPL --- base/repl/LineEdit.jl | 12 ++++++++++-- doc/src/manual/interacting-with-julia.md | 16 +++++++--------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/base/repl/LineEdit.jl b/base/repl/LineEdit.jl index 8a448187f6fd7..c1f0708b94a55 100644 --- a/base/repl/LineEdit.jl +++ b/base/repl/LineEdit.jl @@ -1614,6 +1614,8 @@ AnyDict( "^X^X" => (s,o...)->edit_exchange_point_and_mark(s), "^B" => (s,o...)->edit_move_left(s), "^F" => (s,o...)->edit_move_right(s), + "^P" => (s,o...)->edit_move_up(s), + "^N" => (s,o...)->edit_move_down(s), # Meta B "\eb" => (s,o...)->edit_move_word_left(s), # Meta F @@ -1680,8 +1682,10 @@ AnyDict( ) const history_keymap = AnyDict( - "^P" => (s,o...)->(history_prev(s, mode(s).hist)), - "^N" => (s,o...)->(history_next(s, mode(s).hist)), + "^P" => (s,o...)->(edit_move_up(s) || history_prev(s, mode(s).hist)), + "^N" => (s,o...)->(edit_move_down(s) || history_next(s, mode(s).hist)), + "\ep" => (s,o...)->(history_prev(s, mode(s).hist)), + "\en" => (s,o...)->(history_next(s, mode(s).hist)), # Up Arrow "\e[A" => (s,o...)->(edit_move_up(s) || history_prev(s, mode(s).hist)), # Down Arrow @@ -1696,6 +1700,8 @@ const history_keymap = AnyDict( const prefix_history_keymap = merge!( AnyDict( + "^P" => (s,data,c)->history_prev_prefix(data, data.histprompt.hp, data.prefix), + "^N" => (s,data,c)->history_next_prefix(data, data.histprompt.hp, data.prefix), # Up Arrow "\e[A" => (s,data,c)->history_prev_prefix(data, data.histprompt.hp, data.prefix), # Down Arrow @@ -1726,6 +1732,8 @@ function setup_prefix_keymap(hp, parent_prompt) p = PrefixHistoryPrompt(hp, parent_prompt) p.keymap_dict = keymap([prefix_history_keymap]) pkeymap = AnyDict( + "^P" => (s,o...)->(edit_move_up(s) || enter_prefix_search(s, p, true)), + "^N" => (s,o...)->(edit_move_down(s) || enter_prefix_search(s, p, false)), # Up Arrow "\e[A" => (s,o...)->(edit_move_up(s) || enter_prefix_search(s, p, true)), # Down Arrow diff --git a/doc/src/manual/interacting-with-julia.md b/doc/src/manual/interacting-with-julia.md index caeb91f2e0e2e..d951164bc8ab0 100644 --- a/doc/src/manual/interacting-with-julia.md +++ b/doc/src/manual/interacting-with-julia.md @@ -152,16 +152,14 @@ to do so). | **Cursor movement** |   | | Right arrow, `^F` | Move right one character | | Left arrow, `^B` | Move left one character | +| ctrl-Right, `meta-F`| Move right one word | +| ctrl-Left, `meta-B` | Move left one word | | Home, `^A` | Move to beginning of line | | End, `^E` | Move to end of line | -| `^P` | Change to the previous or next history entry | -| `^N` | Change to the next history entry | -| Up arrow | Move up one line (or to the previous history entry) | -| Down arrow | Move down one line (or to the next history entry) | -| Page-up | Change to the previous history entry that matches the text before the cursor | -| Page-down | Change to the next history entry that matches the text before the cursor | -| `meta-f` | Move right one word | -| `meta-b` | Move left one word | +| Up arrow, `^P` | Move up one line (or change to the previous history entry that matches the text before the cursor) | +| Down arrow, `^N` | Move down one line (or change to the next history entry that matches the text before the cursor) | +| Page-up, `meta-P` | Change to the previous history entry | +| Page-down, `meta-N` | Change to the next history entry | | `meta-<` | Change to the first history entry (of the current session if it is before the current position in history) | | `meta->` | Change to the last history entry | | `^-Space` | Set the "mark" in the editing region | @@ -173,7 +171,7 @@ to do so). | `meta-d` | Forward delete the next word | | `^W` | Delete previous text up to the nearest whitespace | | `meta-w` | Copy the current region in the kill ring | -| `meta-W` | "Kill" the current region, placing the text in the kill ring | +| `meta-W` | "Kill" the current region, placing the text in the kill ring | | `^K` | "Kill" to end of line, placing the text in the kill ring | | `^Y` | "Yank" insert the text from the kill ring | | `meta-y` | Replace a previously yanked text with an older entry from the kill ring | From 4502ee42eff407ec640ae808985d3a3591760a5f Mon Sep 17 00:00:00 2001 From: Harrison Grodin Date: Tue, 5 Sep 2017 05:21:29 -0400 Subject: [PATCH 277/324] Add REPL completion for tilde paths (#23475) Overrides path completions if a user expansion is available. For example, `~/Docu` will autocomplete to `/home/user/Docu` after one , and then to `/home/user/Documents` after a second . --- base/repl/REPLCompletions.jl | 11 +++++++++++ test/replcompletions.jl | 17 +++++++++++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/base/repl/REPLCompletions.jl b/base/repl/REPLCompletions.jl index 27074fa09bd35..402b86279e2ec 100644 --- a/base/repl/REPLCompletions.jl +++ b/base/repl/REPLCompletions.jl @@ -194,6 +194,11 @@ function complete_path(path::AbstractString, pos; use_envpath=false) return matchList, startpos:pos, !isempty(matchList) end +function complete_expanduser(path::AbstractString, r) + expanded = expanduser(path) + return String[expanded], r, path != expanded +end + # Determines whether method_complete should be tried. It should only be done if # the string endswiths ',' or '(' when disregarding whitespace_chars function should_method_complete(s::AbstractString) @@ -470,13 +475,19 @@ function completions(string, pos) m = match(r"[\t\n\r\"><=*?|]| (?!\\)", reverse(partial)) startpos = nextind(partial, reverseind(partial, m.offset)) r = startpos:pos + + expanded = complete_expanduser(replace(string[r], r"\\ ", " "), r) + expanded[3] && return expanded # If user expansion available, return it + paths, r, success = complete_path(replace(string[r], r"\\ ", " "), pos) + if inc_tag == :string && length(paths) == 1 && # Only close if there's a single choice, !isdir(expanduser(replace(string[startpos:start(r)-1] * paths[1], r"\\ ", " "))) && # except if it's a directory (length(string) <= pos || string[pos+1] != '"') # or there's already a " at the cursor. paths[1] *= "\"" end + #Latex symbols can be completed for strings (success || inc_tag==:cmd) && return sort!(paths), r, success end diff --git a/test/replcompletions.jl b/test/replcompletions.jl index 13ada5ae0da63..d79d6fdcd3de5 100644 --- a/test/replcompletions.jl +++ b/test/replcompletions.jl @@ -574,10 +574,11 @@ if Sys.isunix() path = homedir() dir = joinpath(path, "tmpfoobar") mkdir(dir) - s = "\"~/tmpfoob" + s = "\"" * path * "/tmpfoob" c,r = test_complete(s) @test "tmpfoobar/" in c - @test r == 4:10 + l = 3 + length(path) + @test r == l:l+6 @test s[r] == "tmpfoob" s = "\"~" @test "tmpfoobar/" in c @@ -684,6 +685,18 @@ let #test that it can auto complete with spaces in file/path rm(dir, recursive=true) end +let # Test tilde path completion + c, r, res = test_complete("\"~/julia") + if !Sys.iswindows() + @test res && c == String[homedir() * "/julia"] + else + @test !res + end + + c, r, res = test_complete("\"foo~bar") + @test !res +end + # Test the completion returns nothing when the folder do not exist c,r = test_complete("cd(\"folder_do_not_exist_77/file") @test length(c) == 0 From 30530e01b8a73ac023512a14228fda1c35eb507b Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Tue, 5 Sep 2017 11:32:14 +0200 Subject: [PATCH 278/324] remove vestigial Char in rand(::UnitRange{<:Union{..., Char}) (#23545) Cf. #13600 and https://github.com/JuliaLang/julia/pull/22752/files#r128026174. Also, changing `Union{Signed,Unsigned,BigInt,Bool}` to `Integer`, as this is equivalent in practice. --- base/random/generation.jl | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/base/random/generation.jl b/base/random/generation.jl index e6174046fe45b..45b2329d30176 100644 --- a/base/random/generation.jl +++ b/base/random/generation.jl @@ -290,13 +290,10 @@ end ### random values from UnitRange -rand(rng::AbstractRNG, r::UnitRange{<:Union{Signed,Unsigned,BigInt,Bool}}) = - rand(rng, RangeGenerator(r)) - -rand!(rng::AbstractRNG, A::AbstractArray, - r::UnitRange{<:Union{Signed,Unsigned,BigInt,Bool,Char}}) = - rand!(rng, A, RangeGenerator(r)) +rand(rng::AbstractRNG, r::UnitRange{<:Integer}) = rand(rng, RangeGenerator(r)) +rand!(rng::AbstractRNG, A::AbstractArray, r::UnitRange{<:Integer}) = + rand!(rng, A, RangeGenerator(r)) ## random values from AbstractArray From a61285107eb471b116c4cd3d45b86b2b74e432f7 Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Sat, 26 Aug 2017 00:08:52 -0500 Subject: [PATCH 279/324] Return GitError from credential_loop --- test/libgit2-helpers.jl | 9 +++- test/libgit2.jl | 91 +++++++++++++++++++---------------------- 2 files changed, 50 insertions(+), 50 deletions(-) diff --git a/test/libgit2-helpers.jl b/test/libgit2-helpers.jl index 13e5f163569a1..09fa7d8207f43 100644 --- a/test/libgit2-helpers.jl +++ b/test/libgit2-helpers.jl @@ -39,7 +39,14 @@ function credential_loop( end end - return err, num_authentications + # Note: LibGit2.GitError(0) will not work if an error message has been set. + git_error = if err == 0 + LibGit2.GitError(LibGit2.Error.None, LibGit2.Error.GIT_OK, "No errors") + else + LibGit2.GitError(err) + end + + return git_error, num_authentications end function credential_loop( diff --git a/test/libgit2.jl b/test/libgit2.jl index 72c964cb9be0a..2ace4cdf543fc 100644 --- a/test/libgit2.jl +++ b/test/libgit2.jl @@ -1609,6 +1609,10 @@ mktempdir() do dir # which use the `getpass` function. At the moment we can only fake this on UNIX based # systems. if Sys.isunix() + git_ok = LibGit2.GitError( + LibGit2.Error.None, LibGit2.Error.GIT_OK, + "No errors") + abort_prompt = LibGit2.GitError( LibGit2.Error.Callback, LibGit2.Error.EAUTH, "Aborting, user cancelled credential request.") @@ -1634,23 +1638,20 @@ mktempdir() do dir ssh_cmd = """ include("$LIBGIT2_HELPER_PATH") valid_cred = LibGit2.SSHCredentials("$username", "", "$valid_key", "$valid_key.pub") - err, auth_attempts = credential_loop(valid_cred, "$url", "$username") - (err < 0 ? LibGit2.GitError(err) : err, auth_attempts) + credential_loop(valid_cred, "$url", "$username") """ ssh_p_cmd = """ include("$LIBGIT2_HELPER_PATH") valid_cred = LibGit2.SSHCredentials("$username", "$passphrase", "$valid_p_key", "$valid_p_key.pub") - err, auth_attempts = credential_loop(valid_cred, "$url", "$username") - (err < 0 ? LibGit2.GitError(err) : err, auth_attempts) + credential_loop(valid_cred, "$url", "$username") """ # SSH requires username ssh_u_cmd = """ include("$LIBGIT2_HELPER_PATH") valid_cred = LibGit2.SSHCredentials("$username", "", "$valid_key", "$valid_key.pub") - err, auth_attempts = credential_loop(valid_cred, "$url") - (err < 0 ? LibGit2.GitError(err) : err, auth_attempts) + credential_loop(valid_cred, "$url") """ # Note: We cannot use the default ~/.ssh/id_rsa for tests since we cannot be @@ -1660,7 +1661,7 @@ mktempdir() do dir # Default credentials are valid withenv("SSH_KEY_PATH" => valid_key) do err, auth_attempts = challenge_prompt(ssh_cmd, []) - @test err == 0 + @test err == git_ok @test auth_attempts == 1 end @@ -1670,7 +1671,7 @@ mktempdir() do dir "Passphrase for $valid_p_key:" => "$passphrase\n", ] err, auth_attempts = challenge_prompt(ssh_p_cmd, challenges) - @test err == 0 + @test err == git_ok @test auth_attempts == 1 # User mistypes passphrase. @@ -1683,7 +1684,7 @@ mktempdir() do dir "Passphrase for $valid_p_key:" => "$passphrase\n", ] err, auth_attempts = challenge_prompt(ssh_p_cmd, challenges) - @test err == 0 + @test err == git_ok @test auth_attempts == 5 # User sends EOF in passphrase prompt which aborts the credential request @@ -1705,7 +1706,7 @@ mktempdir() do dir withenv("SSH_KEY_PATH" => valid_p_key, "SSH_KEY_PASS" => passphrase) do err, auth_attempts = challenge_prompt(ssh_p_cmd, []) - @test err == 0 + @test err == git_ok @test auth_attempts == 1 end @@ -1716,7 +1717,7 @@ mktempdir() do dir "Username for 'github.com':" => "$username\n", ] err, auth_attempts = challenge_prompt(ssh_u_cmd, challenges) - @test err == 0 + @test err == git_ok @test auth_attempts == 1 # User sends EOF in username prompt which aborts the credential request @@ -1751,15 +1752,14 @@ mktempdir() do dir ssh_user_empty_cmd = """ include("$LIBGIT2_HELPER_PATH") valid_cred = LibGit2.SSHCredentials("$username", "", "$valid_key", "$valid_key.pub") - err, auth_attempts = credential_loop(valid_cred, "$url", "") - (err < 0 ? LibGit2.GitError(err) : err, auth_attempts) + credential_loop(valid_cred, "$url", "") """ challenges = [ "Username for 'github.com':" => "$username\n", ] err, auth_attempts = challenge_prompt(ssh_user_empty_cmd, challenges) - @test err == 0 + @test err == git_ok @test auth_attempts == 1 end @@ -1780,7 +1780,7 @@ mktempdir() do dir "Private key location for 'git@github.com':" => "$valid_key\n", ] err, auth_attempts = challenge_prompt(ssh_cmd, challenges) - @test err == 0 + @test err == git_ok @test auth_attempts == 1 # User provides valid credentials that requires a passphrase @@ -1789,7 +1789,7 @@ mktempdir() do dir "Passphrase for $valid_p_key:" => "$passphrase\n", ] err, auth_attempts = challenge_prompt(ssh_p_cmd, challenges) - @test err == 0 + @test err == git_ok @test auth_attempts == 1 # User sends EOF in private key prompt which aborts the credential request @@ -1821,7 +1821,7 @@ mktempdir() do dir "Private key location for 'git@github.com' [$invalid_key]:" => "$valid_key\n", ] err, auth_attempts = challenge_prompt(ssh_cmd, challenges) - @test err == 0 + @test err == git_ok @test auth_attempts == 2 end =# @@ -1838,7 +1838,7 @@ mktempdir() do dir "Public key location for 'git@github.com':" => "$valid_key.pub\n" ] err, auth_attempts = challenge_prompt(ssh_cmd, challenges) - @test err == 0 + @test err == git_ok @test auth_attempts == 2 end =# @@ -1853,8 +1853,7 @@ mktempdir() do dir https_cmd = """ include("$LIBGIT2_HELPER_PATH") valid_cred = LibGit2.UserPasswordCredentials("$valid_username", "$valid_password") - err, auth_attempts = credential_loop(valid_cred, "$url") - (err < 0 ? LibGit2.GitError(err) : err, auth_attempts) + credential_loop(valid_cred, "$url") """ # User provides a valid username and password @@ -1863,7 +1862,7 @@ mktempdir() do dir "Password for 'https://$valid_username@github.com':" => "$valid_password\n", ] err, auth_attempts = challenge_prompt(https_cmd, challenges) - @test err == 0 + @test err == git_ok @test auth_attempts == 1 # User sends EOF in username prompt which aborts the credential request @@ -1902,7 +1901,7 @@ mktempdir() do dir "Password for 'https://$valid_username@github.com':" => "$valid_password\n", ] err, auth_attempts = challenge_prompt(https_cmd, challenges) - @test err == 0 + @test err == git_ok @test auth_attempts == 5 end @@ -1918,8 +1917,7 @@ mktempdir() do dir include("$LIBGIT2_HELPER_PATH") valid_cred = LibGit2.SSHCredentials("$username", "$passphrase", "$valid_p_key", "$valid_p_key.pub") payload = CredentialPayload(Nullable(valid_cred)) - err, auth_attempts = credential_loop(valid_cred, "$url", "$username", payload) - (err < 0 ? LibGit2.GitError(err) : err, auth_attempts) + credential_loop(valid_cred, "$url", "$username", payload) """ invalid_cmd = """ @@ -1928,13 +1926,12 @@ mktempdir() do dir invalid_cred = LibGit2.SSHCredentials("$username", "", "$invalid_key", "$invalid_key.pub") invalid_cred.usesshagent = "N" # Disable SSH agent use payload = CredentialPayload(Nullable(invalid_cred)) - err, auth_attempts = credential_loop(valid_cred, "$url", "$username", payload) - (err < 0 ? LibGit2.GitError(err) : err, auth_attempts) + credential_loop(valid_cred, "$url", "$username", payload) """ # Explicitly provided credential is correct err, auth_attempts = challenge_prompt(valid_cmd, []) - @test err == 0 + @test err == git_ok @test auth_attempts == 1 # Explicitly provided credential is incorrect @@ -1956,8 +1953,7 @@ mktempdir() do dir include("$LIBGIT2_HELPER_PATH") valid_cred = LibGit2.UserPasswordCredentials("$valid_username", "$valid_password") payload = CredentialPayload(Nullable(valid_cred)) - err, auth_attempts = credential_loop(valid_cred, "$url", "", payload) - (err < 0 ? LibGit2.GitError(err) : err, auth_attempts) + credential_loop(valid_cred, "$url", "", payload) """ invalid_cmd = """ @@ -1965,13 +1961,12 @@ mktempdir() do dir valid_cred = LibGit2.UserPasswordCredentials("$valid_username", "$valid_password") invalid_cred = LibGit2.UserPasswordCredentials("$invalid_username", "$invalid_password") payload = CredentialPayload(Nullable(invalid_cred)) - err, auth_attempts = credential_loop(valid_cred, "$url", "", payload) - (err < 0 ? LibGit2.GitError(err) : err, auth_attempts) + credential_loop(valid_cred, "$url", "", payload) """ # Explicitly provided credential is correct err, auth_attempts = challenge_prompt(valid_cmd, []) - @test err == 0 + @test err == git_ok @test auth_attempts == 1 # Explicitly provided credential is incorrect @@ -1989,14 +1984,15 @@ mktempdir() do dir invalid_username = "alice" invalid_password = randstring(15) + + valid_cmd = """ include("$LIBGIT2_HELPER_PATH") valid_cred = LibGit2.UserPasswordCredentials("$valid_username", "$valid_password") cache = CachedCredentials() LibGit2.get_creds!(cache, "$cred_id", valid_cred) payload = CredentialPayload(Nullable(cache)) - err, auth_attempts = credential_loop(valid_cred, "$url", "", payload) - (err < 0 ? LibGit2.GitError(err) : err, auth_attempts) + credential_loop(valid_cred, "$url", "", payload) """ add_cmd = """ @@ -2005,7 +2001,7 @@ mktempdir() do dir cache = CachedCredentials() payload = CredentialPayload(Nullable(cache)) err, auth_attempts = credential_loop(valid_cred, "$url", "", payload) - (err < 0 ? LibGit2.GitError(err) : err, auth_attempts, cache) + (err, auth_attempts, cache) """ replace_cmd = """ @@ -2016,12 +2012,12 @@ mktempdir() do dir LibGit2.get_creds!(cache, "$cred_id", invalid_cred) payload = CredentialPayload(Nullable(cache)) err, auth_attempts = credential_loop(valid_cred, "$url", "", payload) - (err < 0 ? LibGit2.GitError(err) : err, auth_attempts, cache) + (err, auth_attempts, cache) """ # Cache contains a correct credential err, auth_attempts = challenge_prompt(valid_cmd, []) - @test err == 0 + @test err == git_ok @test auth_attempts == 1 # Add a credential into the cache @@ -2031,7 +2027,7 @@ mktempdir() do dir ] expected_cred = LibGit2.UserPasswordCredentials(valid_username, valid_password) err, auth_attempts, cache = challenge_prompt(add_cmd, challenges) - @test err == 0 + @test err == git_ok @test auth_attempts == 1 @test typeof(cache) == LibGit2.CachedCredentials @test cache.cred == Dict(cred_id => expected_cred) @@ -2043,7 +2039,7 @@ mktempdir() do dir ] expected_cred = LibGit2.UserPasswordCredentials(valid_username, valid_password) err, auth_attempts, cache = challenge_prompt(replace_cmd, challenges) - @test err == 0 + @test err == git_ok @test auth_attempts == 4 @test typeof(cache) == LibGit2.CachedCredentials @test cache.cred == Dict(cred_id => expected_cred) @@ -2055,9 +2051,8 @@ mktempdir() do dir include("$LIBGIT2_HELPER_PATH") valid_cred = LibGit2.UserPasswordCredentials("foo", "bar") payload = CredentialPayload(Nullable(valid_cred)) - err, auth_attempts = credential_loop(valid_cred, "ssh://github.com/repo", - Nullable(""), Cuint(LibGit2.Consts.CREDTYPE_SSH_KEY), payload) - (err < 0 ? LibGit2.GitError(err) : err, auth_attempts) + credential_loop(valid_cred, "ssh://github.com/repo", Nullable(""), + Cuint(LibGit2.Consts.CREDTYPE_SSH_KEY), payload) """ # User provides a SSH credential where a user/password credential is required. @@ -2065,9 +2060,8 @@ mktempdir() do dir include("$LIBGIT2_HELPER_PATH") valid_cred = LibGit2.SSHCredentials("foo", "", "", "") payload = CredentialPayload(Nullable(valid_cred)) - err, auth_attempts = credential_loop(valid_cred, "https://github.com/repo", - Nullable(""), Cuint(LibGit2.Consts.CREDTYPE_USERPASS_PLAINTEXT), payload) - (err < 0 ? LibGit2.GitError(err) : err, auth_attempts) + credential_loop(valid_cred, "https://github.com/repo", Nullable(""), + Cuint(LibGit2.Consts.CREDTYPE_USERPASS_PLAINTEXT), payload) """ err, auth_attempts = challenge_prompt(expect_ssh_cmd, []) @@ -2090,13 +2084,12 @@ mktempdir() do dir include("$LIBGIT2_HELPER_PATH") valid_cred = LibGit2.UserPasswordCredentials("foo", "bar") payload = CredentialPayload(Nullable(valid_cred)) - err, auth_attempts = credential_loop(valid_cred, "foo://github.com/repo", - Nullable(""), Cuint($allowed_types), payload) - (err < 0 ? LibGit2.GitError(err) : err, auth_attempts) + credential_loop(valid_cred, "foo://github.com/repo", Nullable(""), + Cuint($allowed_types), payload) """ err, auth_attempts = challenge_prompt(cmd, []) - @test err == 0 + @test err == git_ok @test auth_attempts == 1 end end From 10cf8a2b15dec99b3e7815cf652fd28002212bb4 Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Sat, 26 Aug 2017 12:13:51 -0500 Subject: [PATCH 280/324] challenge_prompt now takes an expression Switch from strings to expressions for readability and to improve support for generating tests. --- test/TestHelpers.jl | 2 +- test/libgit2.jl | 274 ++++++++++++++++++++++---------------------- 2 files changed, 137 insertions(+), 139 deletions(-) diff --git a/test/TestHelpers.jl b/test/TestHelpers.jl index b6513abfec3cb..84f8edc7fc7cb 100644 --- a/test/TestHelpers.jl +++ b/test/TestHelpers.jl @@ -53,7 +53,7 @@ function with_fake_pty(f) end end -function challenge_prompt(code::AbstractString, challenges; timeout::Integer=10, debug::Bool=true) +function challenge_prompt(code::Expr, challenges; timeout::Integer=10, debug::Bool=true) output_file = tempname() wrapped_code = """ result = let diff --git a/test/libgit2.jl b/test/libgit2.jl index 2ace4cdf543fc..a45217c318d81 100644 --- a/test/libgit2.jl +++ b/test/libgit2.jl @@ -1635,24 +1635,24 @@ mktempdir() do dir username = "git" passphrase = "secret" - ssh_cmd = """ - include("$LIBGIT2_HELPER_PATH") - valid_cred = LibGit2.SSHCredentials("$username", "", "$valid_key", "$valid_key.pub") - credential_loop(valid_cred, "$url", "$username") - """ - - ssh_p_cmd = """ - include("$LIBGIT2_HELPER_PATH") - valid_cred = LibGit2.SSHCredentials("$username", "$passphrase", "$valid_p_key", "$valid_p_key.pub") - credential_loop(valid_cred, "$url", "$username") - """ + ssh_ex = quote + include($LIBGIT2_HELPER_PATH) + valid_cred = SSHCredentials($username, "", $valid_key, $(valid_key * ".pub")) + credential_loop(valid_cred, $url, $username) + end + + ssh_p_ex = quote + include($LIBGIT2_HELPER_PATH) + valid_cred = SSHCredentials($username, $passphrase, $valid_p_key, $(valid_p_key * ".pub")) + credential_loop(valid_cred, $url, $username) + end # SSH requires username - ssh_u_cmd = """ - include("$LIBGIT2_HELPER_PATH") - valid_cred = LibGit2.SSHCredentials("$username", "", "$valid_key", "$valid_key.pub") - credential_loop(valid_cred, "$url") - """ + ssh_u_ex = quote + include($LIBGIT2_HELPER_PATH) + valid_cred = SSHCredentials($username, "", $valid_key, $(valid_key * ".pub")) + credential_loop(valid_cred, $url) + end # Note: We cannot use the default ~/.ssh/id_rsa for tests since we cannot be # sure a users will actually have these files. Instead we will use the ENV @@ -1660,7 +1660,7 @@ mktempdir() do dir # Default credentials are valid withenv("SSH_KEY_PATH" => valid_key) do - err, auth_attempts = challenge_prompt(ssh_cmd, []) + err, auth_attempts = challenge_prompt(ssh_ex, []) @test err == git_ok @test auth_attempts == 1 end @@ -1670,7 +1670,7 @@ mktempdir() do dir challenges = [ "Passphrase for $valid_p_key:" => "$passphrase\n", ] - err, auth_attempts = challenge_prompt(ssh_p_cmd, challenges) + err, auth_attempts = challenge_prompt(ssh_p_ex, challenges) @test err == git_ok @test auth_attempts == 1 @@ -1683,7 +1683,7 @@ mktempdir() do dir # "Private key location for 'git@github.com' [$valid_p_key]:" => "\n", "Passphrase for $valid_p_key:" => "$passphrase\n", ] - err, auth_attempts = challenge_prompt(ssh_p_cmd, challenges) + err, auth_attempts = challenge_prompt(ssh_p_ex, challenges) @test err == git_ok @test auth_attempts == 5 @@ -1691,7 +1691,7 @@ mktempdir() do dir challenges = [ "Passphrase for $valid_p_key:" => "\x04", ] - err, auth_attempts = challenge_prompt(ssh_p_cmd, challenges) + err, auth_attempts = challenge_prompt(ssh_p_ex, challenges) @test err == abort_prompt @test auth_attempts == 1 @@ -1699,13 +1699,13 @@ mktempdir() do dir challenges = [ "Passphrase for $valid_p_key:" => "\n", ] - err, auth_attempts = challenge_prompt(ssh_p_cmd, challenges) + err, auth_attempts = challenge_prompt(ssh_p_ex, challenges) @test err == abort_prompt @test auth_attempts == 1 end withenv("SSH_KEY_PATH" => valid_p_key, "SSH_KEY_PASS" => passphrase) do - err, auth_attempts = challenge_prompt(ssh_p_cmd, []) + err, auth_attempts = challenge_prompt(ssh_p_ex, []) @test err == git_ok @test auth_attempts == 1 end @@ -1716,7 +1716,7 @@ mktempdir() do dir challenges = [ "Username for 'github.com':" => "$username\n", ] - err, auth_attempts = challenge_prompt(ssh_u_cmd, challenges) + err, auth_attempts = challenge_prompt(ssh_u_ex, challenges) @test err == git_ok @test auth_attempts == 1 @@ -1724,7 +1724,7 @@ mktempdir() do dir challenges = [ "Username for 'github.com':" => "\x04", ] - err, auth_attempts = challenge_prompt(ssh_u_cmd, challenges) + err, auth_attempts = challenge_prompt(ssh_u_ex, challenges) @test err == abort_prompt @test auth_attempts == 1 @@ -1733,7 +1733,7 @@ mktempdir() do dir "Username for 'github.com':" => "\n", "Username for 'github.com':" => "\x04", ] - err, auth_attempts = challenge_prompt(ssh_u_cmd, challenges) + err, auth_attempts = challenge_prompt(ssh_u_ex, challenges) @test err == abort_prompt @test auth_attempts == 5 # Should ideally be <= 2 @@ -1743,22 +1743,22 @@ mktempdir() do dir "Username for 'github.com' [foo]:" => "\n", "Username for 'github.com' [foo]:" => "\x04", # Need to manually abort ] - err, auth_attempts = challenge_prompt(ssh_u_cmd, challenges) + err, auth_attempts = challenge_prompt(ssh_u_ex, challenges) @test err == abort_prompt @test auth_attempts == 6 # Credential callback is given an empty string in the `username_ptr` # instead of the typical C_NULL. - ssh_user_empty_cmd = """ - include("$LIBGIT2_HELPER_PATH") - valid_cred = LibGit2.SSHCredentials("$username", "", "$valid_key", "$valid_key.pub") - credential_loop(valid_cred, "$url", "") - """ + ssh_user_empty_ex = quote + include($LIBGIT2_HELPER_PATH) + valid_cred = LibGit2.SSHCredentials($username, "", $valid_key, $(valid_key * ".pub")) + credential_loop(valid_cred, $url, "") + end challenges = [ "Username for 'github.com':" => "$username\n", ] - err, auth_attempts = challenge_prompt(ssh_user_empty_cmd, challenges) + err, auth_attempts = challenge_prompt(ssh_user_empty_ex, challenges) @test err == git_ok @test auth_attempts == 1 end @@ -1779,7 +1779,7 @@ mktempdir() do dir challenges = [ "Private key location for 'git@github.com':" => "$valid_key\n", ] - err, auth_attempts = challenge_prompt(ssh_cmd, challenges) + err, auth_attempts = challenge_prompt(ssh_ex, challenges) @test err == git_ok @test auth_attempts == 1 @@ -1788,7 +1788,7 @@ mktempdir() do dir "Private key location for 'git@github.com':" => "$valid_p_key\n", "Passphrase for $valid_p_key:" => "$passphrase\n", ] - err, auth_attempts = challenge_prompt(ssh_p_cmd, challenges) + err, auth_attempts = challenge_prompt(ssh_p_ex, challenges) @test err == git_ok @test auth_attempts == 1 @@ -1796,7 +1796,7 @@ mktempdir() do dir challenges = [ "Private key location for 'git@github.com':" => "\x04", ] - err, auth_attempts = challenge_prompt(ssh_cmd, challenges) + err, auth_attempts = challenge_prompt(ssh_ex, challenges) @test err == abort_prompt @test auth_attempts == 1 @@ -1806,7 +1806,7 @@ mktempdir() do dir "Public key location for 'git@github.com' [.pub]:" => "\n", "Private key location for 'git@github.com':" => "\x04", ] - err, auth_attempts = challenge_prompt(ssh_cmd, challenges) + err, auth_attempts = challenge_prompt(ssh_ex, challenges) @test err == abort_prompt @test auth_attempts == 2 end @@ -1820,7 +1820,7 @@ mktempdir() do dir challenges = [ "Private key location for 'git@github.com' [$invalid_key]:" => "$valid_key\n", ] - err, auth_attempts = challenge_prompt(ssh_cmd, challenges) + err, auth_attempts = challenge_prompt(ssh_ex, challenges) @test err == git_ok @test auth_attempts == 2 end @@ -1837,7 +1837,7 @@ mktempdir() do dir "Private key location for 'git@github.com' [$valid_key]:" => "\n" "Public key location for 'git@github.com':" => "$valid_key.pub\n" ] - err, auth_attempts = challenge_prompt(ssh_cmd, challenges) + err, auth_attempts = challenge_prompt(ssh_ex, challenges) @test err == git_ok @test auth_attempts == 2 end @@ -1850,18 +1850,18 @@ mktempdir() do dir valid_username = "julia" valid_password = randstring(16) - https_cmd = """ - include("$LIBGIT2_HELPER_PATH") - valid_cred = LibGit2.UserPasswordCredentials("$valid_username", "$valid_password") - credential_loop(valid_cred, "$url") - """ + https_ex = quote + include($LIBGIT2_HELPER_PATH) + valid_cred = LibGit2.UserPasswordCredentials($valid_username, $valid_password) + credential_loop(valid_cred, $url) + end # User provides a valid username and password challenges = [ "Username for 'https://github.com':" => "$valid_username\n", "Password for 'https://$valid_username@github.com':" => "$valid_password\n", ] - err, auth_attempts = challenge_prompt(https_cmd, challenges) + err, auth_attempts = challenge_prompt(https_ex, challenges) @test err == git_ok @test auth_attempts == 1 @@ -1869,7 +1869,7 @@ mktempdir() do dir challenges = [ "Username for 'https://github.com':" => "\x04", ] - err, auth_attempts = challenge_prompt(https_cmd, challenges) + err, auth_attempts = challenge_prompt(https_ex, challenges) @test err == abort_prompt @test auth_attempts == 1 @@ -1878,7 +1878,7 @@ mktempdir() do dir "Username for 'https://github.com':" => "foo\n", "Password for 'https://foo@github.com':" => "\x04", ] - err, auth_attempts = challenge_prompt(https_cmd, challenges) + err, auth_attempts = challenge_prompt(https_ex, challenges) @test err == abort_prompt @test auth_attempts == 1 @@ -1888,7 +1888,7 @@ mktempdir() do dir "Username for 'https://github.com':" => "foo\n", "Password for 'https://foo@github.com':" => "\n", ] - err, auth_attempts = challenge_prompt(https_cmd, challenges) + err, auth_attempts = challenge_prompt(https_ex, challenges) @test err == abort_prompt @test auth_attempts == 1 @@ -1900,7 +1900,7 @@ mktempdir() do dir "Username for 'https://github.com' [foo]:" => "$valid_username\n", "Password for 'https://$valid_username@github.com':" => "$valid_password\n", ] - err, auth_attempts = challenge_prompt(https_cmd, challenges) + err, auth_attempts = challenge_prompt(https_ex, challenges) @test err == git_ok @test auth_attempts == 5 end @@ -1913,30 +1913,30 @@ mktempdir() do dir username = "git" passphrase = "secret" - valid_cmd = """ - include("$LIBGIT2_HELPER_PATH") - valid_cred = LibGit2.SSHCredentials("$username", "$passphrase", "$valid_p_key", "$valid_p_key.pub") - payload = CredentialPayload(Nullable(valid_cred)) - credential_loop(valid_cred, "$url", "$username", payload) - """ - - invalid_cmd = """ - include("$LIBGIT2_HELPER_PATH") - valid_cred = LibGit2.SSHCredentials("$username", "$passphrase", "$valid_p_key", "$valid_p_key.pub") - invalid_cred = LibGit2.SSHCredentials("$username", "", "$invalid_key", "$invalid_key.pub") - invalid_cred.usesshagent = "N" # Disable SSH agent use - payload = CredentialPayload(Nullable(invalid_cred)) - credential_loop(valid_cred, "$url", "$username", payload) - """ + valid_ex = quote + include($LIBGIT2_HELPER_PATH) + valid_cred = LibGit2.SSHCredentials($username, $passphrase, $valid_p_key, $(valid_p_key * ".pub")) + payload = CredentialPayload(Nullable(valid_cred)) + credential_loop(valid_cred, $url, $username, payload) + end + + invalid_ex = quote + include($LIBGIT2_HELPER_PATH) + valid_cred = LibGit2.SSHCredentials($username, $passphrase, $valid_p_key, $(valid_p_key * ".pub")) + invalid_cred = LibGit2.SSHCredentials($username, "", $invalid_key, $(invalid_key * ".pub")) + invalid_cred.usesshagent = "N" # Disable SSH agent use + payload = CredentialPayload(Nullable(invalid_cred)) + credential_loop(valid_cred, $url, $username, payload) + end # Explicitly provided credential is correct - err, auth_attempts = challenge_prompt(valid_cmd, []) + err, auth_attempts = challenge_prompt(valid_ex, []) @test err == git_ok @test auth_attempts == 1 # Explicitly provided credential is incorrect # TODO: Unless the SSH agent is disabled we may get caught in an infinite loop - err, auth_attempts = challenge_prompt(invalid_cmd, []) + err, auth_attempts = challenge_prompt(invalid_ex, []) @test err == eauth_error @test auth_attempts == 4 end @@ -1949,28 +1949,28 @@ mktempdir() do dir invalid_username = "alice" invalid_password = randstring(15) - valid_cmd = """ - include("$LIBGIT2_HELPER_PATH") - valid_cred = LibGit2.UserPasswordCredentials("$valid_username", "$valid_password") - payload = CredentialPayload(Nullable(valid_cred)) - credential_loop(valid_cred, "$url", "", payload) - """ - - invalid_cmd = """ - include("$LIBGIT2_HELPER_PATH") - valid_cred = LibGit2.UserPasswordCredentials("$valid_username", "$valid_password") - invalid_cred = LibGit2.UserPasswordCredentials("$invalid_username", "$invalid_password") - payload = CredentialPayload(Nullable(invalid_cred)) - credential_loop(valid_cred, "$url", "", payload) - """ + valid_ex = quote + include($LIBGIT2_HELPER_PATH) + valid_cred = LibGit2.UserPasswordCredentials($valid_username, $valid_password) + payload = CredentialPayload(Nullable(valid_cred)) + credential_loop(valid_cred, $url, "", payload) + end + + invalid_ex = quote + include($LIBGIT2_HELPER_PATH) + valid_cred = LibGit2.UserPasswordCredentials($valid_username, $valid_password) + invalid_cred = LibGit2.UserPasswordCredentials($invalid_username, $invalid_password) + payload = CredentialPayload(Nullable(invalid_cred)) + credential_loop(valid_cred, $url, "", payload) + end # Explicitly provided credential is correct - err, auth_attempts = challenge_prompt(valid_cmd, []) + err, auth_attempts = challenge_prompt(valid_ex, []) @test err == git_ok @test auth_attempts == 1 # Explicitly provided credential is incorrect - err, auth_attempts = challenge_prompt(invalid_cmd, []) + err, auth_attempts = challenge_prompt(invalid_ex, []) @test err == eauth_error @test auth_attempts == 4 end @@ -1984,39 +1984,37 @@ mktempdir() do dir invalid_username = "alice" invalid_password = randstring(15) + valid_ex = quote + include($LIBGIT2_HELPER_PATH) + valid_cred = LibGit2.UserPasswordCredentials($valid_username, $valid_password) + cache = CachedCredentials() + LibGit2.get_creds!(cache, $cred_id, valid_cred) + payload = CredentialPayload(Nullable(cache)) + credential_loop(valid_cred, $url, "", payload) + end + add_ex = quote + include($LIBGIT2_HELPER_PATH) + valid_cred = LibGit2.UserPasswordCredentials($valid_username, $valid_password) + cache = CachedCredentials() + payload = CredentialPayload(Nullable(cache)) + err, auth_attempts = credential_loop(valid_cred, $url, "", payload) + (err, auth_attempts, cache) + end - valid_cmd = """ - include("$LIBGIT2_HELPER_PATH") - valid_cred = LibGit2.UserPasswordCredentials("$valid_username", "$valid_password") - cache = CachedCredentials() - LibGit2.get_creds!(cache, "$cred_id", valid_cred) - payload = CredentialPayload(Nullable(cache)) - credential_loop(valid_cred, "$url", "", payload) - """ - - add_cmd = """ - include("$LIBGIT2_HELPER_PATH") - valid_cred = LibGit2.UserPasswordCredentials("$valid_username", "$valid_password") - cache = CachedCredentials() - payload = CredentialPayload(Nullable(cache)) - err, auth_attempts = credential_loop(valid_cred, "$url", "", payload) - (err, auth_attempts, cache) - """ - - replace_cmd = """ - include("$LIBGIT2_HELPER_PATH") - valid_cred = LibGit2.UserPasswordCredentials("$valid_username", "$valid_password") - invalid_cred = LibGit2.UserPasswordCredentials("$invalid_username", "$invalid_password", true) - cache = CachedCredentials() - LibGit2.get_creds!(cache, "$cred_id", invalid_cred) - payload = CredentialPayload(Nullable(cache)) - err, auth_attempts = credential_loop(valid_cred, "$url", "", payload) - (err, auth_attempts, cache) - """ + replace_ex = quote + include($LIBGIT2_HELPER_PATH) + valid_cred = LibGit2.UserPasswordCredentials($valid_username, $valid_password) + invalid_cred = LibGit2.UserPasswordCredentials($invalid_username, $invalid_password, true) + cache = CachedCredentials() + LibGit2.get_creds!(cache, $cred_id, invalid_cred) + payload = CredentialPayload(Nullable(cache)) + err, auth_attempts = credential_loop(valid_cred, $url, "", payload) + (err, auth_attempts, cache) + end # Cache contains a correct credential - err, auth_attempts = challenge_prompt(valid_cmd, []) + err, auth_attempts = challenge_prompt(valid_ex, []) @test err == git_ok @test auth_attempts == 1 @@ -2026,7 +2024,7 @@ mktempdir() do dir "Password for 'https://$valid_username@github.com':" => "$valid_password\n", ] expected_cred = LibGit2.UserPasswordCredentials(valid_username, valid_password) - err, auth_attempts, cache = challenge_prompt(add_cmd, challenges) + err, auth_attempts, cache = challenge_prompt(add_ex, challenges) @test err == git_ok @test auth_attempts == 1 @test typeof(cache) == LibGit2.CachedCredentials @@ -2038,7 +2036,7 @@ mktempdir() do dir "Password for 'https://$valid_username@github.com':" => "$valid_password\n", ] expected_cred = LibGit2.UserPasswordCredentials(valid_username, valid_password) - err, auth_attempts, cache = challenge_prompt(replace_cmd, challenges) + err, auth_attempts, cache = challenge_prompt(replace_ex, challenges) @test err == git_ok @test auth_attempts == 4 @test typeof(cache) == LibGit2.CachedCredentials @@ -2047,28 +2045,28 @@ mktempdir() do dir @testset "Incompatible explicit credentials" begin # User provides a user/password credential where a SSH credential is required. - expect_ssh_cmd = """ - include("$LIBGIT2_HELPER_PATH") - valid_cred = LibGit2.UserPasswordCredentials("foo", "bar") - payload = CredentialPayload(Nullable(valid_cred)) - credential_loop(valid_cred, "ssh://github.com/repo", Nullable(""), - Cuint(LibGit2.Consts.CREDTYPE_SSH_KEY), payload) - """ + expect_ssh_ex = quote + include($LIBGIT2_HELPER_PATH) + valid_cred = LibGit2.UserPasswordCredentials("foo", "bar") + payload = CredentialPayload(Nullable(valid_cred)) + credential_loop(valid_cred, "ssh://github.com/repo", Nullable(""), + Cuint(LibGit2.Consts.CREDTYPE_SSH_KEY), payload) + end # User provides a SSH credential where a user/password credential is required. - expect_https_cmd = """ - include("$LIBGIT2_HELPER_PATH") - valid_cred = LibGit2.SSHCredentials("foo", "", "", "") - payload = CredentialPayload(Nullable(valid_cred)) - credential_loop(valid_cred, "https://github.com/repo", Nullable(""), - Cuint(LibGit2.Consts.CREDTYPE_USERPASS_PLAINTEXT), payload) - """ - - err, auth_attempts = challenge_prompt(expect_ssh_cmd, []) + expect_https_ex = quote + include($LIBGIT2_HELPER_PATH) + valid_cred = LibGit2.SSHCredentials("foo", "", "", "") + payload = CredentialPayload(Nullable(valid_cred)) + credential_loop(valid_cred, "https://github.com/repo", Nullable(""), + Cuint(LibGit2.Consts.CREDTYPE_USERPASS_PLAINTEXT), payload) + end + + err, auth_attempts = challenge_prompt(expect_ssh_ex, []) @test err == incompatible_error @test auth_attempts == 1 - err, auth_attempts = challenge_prompt(expect_https_cmd, []) + err, auth_attempts = challenge_prompt(expect_https_ex, []) @test err == incompatible_error @test auth_attempts == 1 end @@ -2080,15 +2078,15 @@ mktempdir() do dir Cuint(LibGit2.Consts.CREDTYPE_USERPASS_PLAINTEXT) # User provides a user/password credential where a SSH credential is required. - cmd = """ - include("$LIBGIT2_HELPER_PATH") - valid_cred = LibGit2.UserPasswordCredentials("foo", "bar") - payload = CredentialPayload(Nullable(valid_cred)) - credential_loop(valid_cred, "foo://github.com/repo", Nullable(""), - Cuint($allowed_types), payload) - """ - - err, auth_attempts = challenge_prompt(cmd, []) + ex = quote + include($LIBGIT2_HELPER_PATH) + valid_cred = LibGit2.UserPasswordCredentials("foo", "bar") + payload = CredentialPayload(Nullable(valid_cred)) + credential_loop(valid_cred, "foo://github.com/repo", Nullable(""), + $allowed_types, payload) + end + + err, auth_attempts = challenge_prompt(ex, []) @test err == git_ok @test auth_attempts == 1 end From c31fdea516d186aa475e3d307543146a62367e4e Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Tue, 5 Sep 2017 00:19:58 +0200 Subject: [PATCH 281/324] Remove cgparams that disallow language features. We realistically rely on LLVM optimization passes anyway. --- base/reflection.jl | 11 +++-------- src/ccall.cpp | 6 ------ src/cgutils.cpp | 13 ------------- src/codegen.cpp | 21 --------------------- src/jltypes.c | 2 +- src/julia.h | 7 ++----- 6 files changed, 6 insertions(+), 54 deletions(-) diff --git a/base/reflection.jl b/base/reflection.jl index f24aae7d20d87..bbbbe5a43eaae 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -727,12 +727,9 @@ end struct CodegenParams cached::Cint - runtime::Cint - exceptions::Cint track_allocations::Cint code_coverage::Cint static_alloc::Cint - dynamic_alloc::Cint prefer_specsig::Cint module_setup::Any @@ -740,14 +737,12 @@ struct CodegenParams raise_exception::Any CodegenParams(;cached::Bool=true, - runtime::Bool=true, exceptions::Bool=true, track_allocations::Bool=true, code_coverage::Bool=true, - static_alloc::Bool=true, dynamic_alloc::Bool=true, - prefer_specsig::Bool=false, + static_alloc::Bool=true, prefer_specsig::Bool=false, module_setup=nothing, module_activation=nothing, raise_exception=nothing) = new(Cint(cached), - Cint(runtime), Cint(exceptions), Cint(track_allocations), Cint(code_coverage), - Cint(static_alloc), Cint(dynamic_alloc), Cint(prefer_specsig), + Cint(track_allocations), Cint(code_coverage), + Cint(static_alloc), Cint(prefer_specsig), module_setup, module_activation, raise_exception) end diff --git a/src/ccall.cpp b/src/ccall.cpp index 01f622623db34..8ca42dfdec546 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -1513,12 +1513,6 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) rt = (jl_value_t*)jl_any_type; // convert return type to jl_value_t* } - // check if we require the runtime - // TODO: could be more fine-grained, - // respecting special functions below that don't require the runtime - if (!llvmcall && (!f_lib || f_lib == JL_DL_LIBNAME)) - JL_FEAT_REQUIRE(ctx, runtime); - // some sanity checking and check whether there's a vararg bool isVa; size_t nargt; diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 7e7d3fa5c7a64..d881f9e623175 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -97,15 +97,8 @@ static Value *mark_callee_rooted(IRBuilder<> &irbuilder, Value *V) // --- language feature checks --- -// branch on whether a language feature is enabled or not #define JL_FEAT_TEST(ctx, feature) ((ctx).params->feature) -// require a language feature to be enabled -#define JL_FEAT_REQUIRE(ctx, feature) \ - if (!JL_FEAT_TEST(ctx, feature)) \ - jl_errorf("%s for %s:%d requires the " #feature " language feature, which is disabled", \ - __FUNCTION__, (ctx).file.str().c_str(), *(ctx).line); - // --- hook checks --- @@ -908,7 +901,6 @@ static void raise_exception(jl_codectx_t &ctx, Value *exc, jl_box_voidpointer(wrap(ctx.builder.GetInsertBlock())), jl_box_voidpointer(wrap(exc))); } else { - JL_FEAT_REQUIRE(ctx, runtime); ctx.builder.CreateCall(prepare_call(jlthrow_func), { mark_callee_rooted(exc) }); } ctx.builder.CreateUnreachable(); @@ -2117,8 +2109,6 @@ static void emit_cpointercheck(jl_codectx_t &ctx, const jl_cgval_t &x, const std // allocation for known size object static Value *emit_allocobj(jl_codectx_t &ctx, size_t static_size, Value *jt) { - JL_FEAT_REQUIRE(ctx, dynamic_alloc); - JL_FEAT_REQUIRE(ctx, runtime); Value *ptls_ptr = emit_bitcast(ctx, ctx.ptlsStates, T_pint8); auto call = ctx.builder.CreateCall(prepare_call(jl_alloc_obj_func), {ptls_ptr, ConstantInt::get(T_size, static_size), @@ -2349,12 +2339,9 @@ static int compare_cgparams(const jl_cgparams_t *a, const jl_cgparams_t *b) { return (a->cached == b->cached) && // language features - (a->runtime == b->runtime) && - (a->exceptions == b->exceptions) && (a->track_allocations == b->track_allocations) && (a->code_coverage == b->code_coverage) && (a->static_alloc == b->static_alloc) && - (a->dynamic_alloc == b->dynamic_alloc) && (a->prefer_specsig == b->prefer_specsig) && // hooks (a->module_setup == b->module_setup) && diff --git a/src/codegen.cpp b/src/codegen.cpp index f30113dfb4312..65d44fb75d3bc 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2219,7 +2219,6 @@ static Value *emit_f_is(jl_codectx_t &ctx, const jl_cgval_t &arg1, const jl_cgva decay_derived(varg2)); } - JL_FEAT_REQUIRE(ctx, runtime); Value *varg1 = mark_callee_rooted(boxed(ctx, arg1)); Value *varg2 = mark_callee_rooted(boxed(ctx, arg2, false)); // potentially unrooted! return ctx.builder.CreateTrunc(ctx.builder.CreateCall(prepare_call(jlegal_func), {varg1, varg2}), T_int1); @@ -2264,7 +2263,6 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, if (jl_subtype(ty.typ, (jl_value_t*)jl_type_type)) { Value *rt_arg = boxed(ctx, arg); Value *rt_ty = boxed(ctx, ty); - JL_FEAT_REQUIRE(ctx, runtime); ctx.builder.CreateCall(prepare_call(jltypeassert_func), {rt_arg, rt_ty}); *ret = arg; return true; @@ -2305,7 +2303,6 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, nva = ctx.builder.CreateTrunc(nva, T_int32); #endif Value *theArgs = ctx.builder.CreateGEP(ctx.argArray, ConstantInt::get(T_size, ctx.nReqArgs)); - JL_FEAT_REQUIRE(ctx, runtime); Value *r = ctx.builder.CreateCall(prepare_call(jlapply2va_func), { theF, theArgs, nva }); *ret = mark_julia_type(ctx, r, true, jl_any_type); return true; @@ -2964,7 +2961,6 @@ static jl_cgval_t emit_invoke(jl_codectx_t &ctx, jl_expr_t *ex) } } } - JL_FEAT_REQUIRE(ctx, runtime); jl_cgval_t result = mark_julia_type(ctx, emit_jlcall( ctx, @@ -3016,16 +3012,6 @@ static jl_cgval_t emit_call(jl_codectx_t &ctx, jl_expr_t *ex) } } - if (!JL_FEAT_TEST(ctx, runtime)) { - char* name = NULL; - if (jl_is_symbol(args[0])) - name = jl_symbol_name((jl_sym_t*)args[0]); - if (jl_is_globalref(args[0])) - name = jl_symbol_name(jl_globalref_name(args[0])); - jl_errorf("generic call to %s requires the runtime language feature", - name ? name : ""); - } - // emit function and arguments Value *callval = emit_jlcall(ctx, jlapplygeneric_func, nullptr, argv, nargs); return mark_julia_type(ctx, callval, true, rt); @@ -3068,7 +3054,6 @@ static Value *global_binding_pointer(jl_codectx_t &ctx, jl_module_t *m, jl_sym_t b = jl_get_binding(m, s); if (b == NULL) { // var not found. switch to delayed lookup. - JL_FEAT_REQUIRE(ctx, runtime); std::stringstream name; name << "delayedvar" << globalUnique++; Constant *initnul = V_null; @@ -3438,7 +3423,6 @@ static void emit_assignment(jl_codectx_t &ctx, jl_value_t *l, jl_value_t *r) if (bp == NULL && s != NULL) bp = global_binding_pointer(ctx, ctx.module, s, &bnd, true); if (bp != NULL) { // it's a global - JL_FEAT_REQUIRE(ctx, runtime); assert(bnd); Value *rval = mark_callee_rooted(boxed(ctx, emit_expr(ctx, r), false)); // no root needed since this is about to be assigned to a global ctx.builder.CreateCall(prepare_call(jlcheckassign_func), @@ -3655,7 +3639,6 @@ static void emit_stmtpos(jl_codectx_t &ctx, jl_value_t *expr) } else if (head == leave_sym) { assert(jl_is_long(args[0])); - JL_FEAT_REQUIRE(ctx, runtime); ctx.builder.CreateCall(prepare_call(jlleave_func), ConstantInt::get(T_int32, jl_unbox_long(args[0]))); } @@ -3765,7 +3748,6 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr) else if (head == method_sym) { jl_value_t *mn = args[0]; assert(jl_expr_nargs(ex) != 1 || jl_is_symbol(mn) || jl_is_slot(mn)); - JL_FEAT_REQUIRE(ctx, runtime); Value *bp = NULL, *name, *bp_owner = V_null; jl_binding_t *bnd = NULL; @@ -3832,7 +3814,6 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr) sym = jl_globalref_name(sym); } if (jl_is_symbol(sym)) { - JL_FEAT_REQUIRE(ctx, runtime); jl_binding_t *bnd = NULL; (void)global_binding_pointer(ctx, mod, sym, &bnd, true); assert(bnd); ctx.builder.CreateCall(prepare_call(jldeclareconst_func), @@ -3862,7 +3843,6 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr) true, jl_any_type); } else if (head == copyast_sym) { - JL_FEAT_REQUIRE(ctx, runtime); jl_value_t *arg = args[0]; if (jl_is_quotenode(arg)) { jl_value_t *arg1 = jl_fieldref(arg, 0); @@ -3900,7 +3880,6 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr) jl_error("syntax: prefix \"$\" in non-quoted expression"); if (jl_is_toplevel_only_expr(expr) && !jl_is_method(ctx.linfo->def.method)) { - JL_FEAT_REQUIRE(ctx, runtime); // call interpreter to run a toplevel expr from inside a // compiled toplevel thunk. Value *args[2] = { diff --git a/src/jltypes.c b/src/jltypes.c index 7a7e2e4a6fe4a..f7e88e05a6ad6 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -127,7 +127,7 @@ jl_value_t *jl_memory_exception; jl_value_t *jl_readonlymemory_exception; union jl_typemap_t jl_cfunction_list; -jl_cgparams_t jl_default_cgparams = {1, 1, 1, 1, 1, 1, 1, 0, NULL, NULL, NULL}; +jl_cgparams_t jl_default_cgparams = {1, 1, 1, 1, 0, NULL, NULL, NULL}; // --- type properties and predicates --- diff --git a/src/julia.h b/src/julia.h index 871c2cfa43ba8..eba6b8007a9bc 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1806,12 +1806,9 @@ typedef struct { typedef struct { int cached; // can the compiler use/populate the compilation cache? - int runtime; // can we call into the runtime? - int exceptions; // are exceptions supported (requires runtime)? - int track_allocations; // can we track allocations (don't if disallowed)? - int code_coverage; // can we measure coverage (don't if disallowed)? + int track_allocations; // can we track allocations? + int code_coverage; // can we measure coverage? int static_alloc; // is the compiler allowed to allocate statically? - int dynamic_alloc; // is the compiler allowed to allocate dynamically (requires runtime)? int prefer_specsig; // are specialized function signatures preferred? From ac37603db5df1025bf80e3d47d34813708045861 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Mon, 7 Aug 2017 18:43:53 +0200 Subject: [PATCH 282/324] REPL: enable undo in bracketed paste mode --- base/repl/LineEdit.jl | 4 ++-- base/repl/REPL.jl | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/base/repl/LineEdit.jl b/base/repl/LineEdit.jl index 06ee7d0e53266..a2fa7e245e796 100644 --- a/base/repl/LineEdit.jl +++ b/base/repl/LineEdit.jl @@ -812,8 +812,8 @@ function replace_line(s::PromptState, l::IOBuffer) s.input_buffer = copy(l) end -function replace_line(s::PromptState, l) - empty_undo(s) +function replace_line(s::PromptState, l, keep_undo=false) + keep_undo || empty_undo(s) s.input_buffer.ptr = 1 s.input_buffer.size = 0 write(s.input_buffer, l) diff --git a/base/repl/REPL.jl b/base/repl/REPL.jl index 589a1e1ebfd2d..c8fc5a3fb42ba 100644 --- a/base/repl/REPL.jl +++ b/base/repl/REPL.jl @@ -849,6 +849,7 @@ function setup_interface( edit_insert(s, input) return end + LineEdit.push_undo(s) edit_insert(sbuffer, input) input = String(take!(sbuffer)) oldpos = start(input) @@ -892,7 +893,7 @@ function setup_interface( if isprompt_paste # remove indentation spaces corresponding to the prompt tail = replace(tail, r"^ {7}"m, "") # 7: jl_prompt_len end - LineEdit.replace_line(s, tail) + LineEdit.replace_line(s, tail, true) LineEdit.refresh_line(s) break end @@ -910,6 +911,7 @@ function setup_interface( raw!(terminal, false) && disable_bracketed_paste(terminal) LineEdit.mode(s).on_done(s, LineEdit.buffer(s), true) raw!(terminal, true) && enable_bracketed_paste(terminal) + LineEdit.push_undo(s) # when the last line is incomplete end oldpos = pos firstline = false From 0f7ca717051e2e651ed09ce8ed0f2160095130be Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 5 Sep 2017 11:59:03 -0400 Subject: [PATCH 283/324] add error message for attempting to capture a local variable as a closure type signature (#23527) fix #18730 --- src/julia-syntax.scm | 13 +++++++++---- test/parse.jl | 8 ++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 41a333b773644..9023b7dc3f571 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -3261,13 +3261,18 @@ f(x) = yt(x) (capt-vars (diff all-capt-vars capt-sp)) ; remove capt-sp from capt-vars (find-locals-in-method-sig (lambda (methdef) (expr-find-all - (lambda (e) (and (pair? e) (eq? (car e) 'outerref) - (let ((s (cadr e))) + (lambda (e) (and (or (symbol? e) (and (pair? e) (eq? (car e) 'outerref))) + (let ((s (if (symbol? e) e (cadr e)))) (and (symbol? s) (not (eq? name s)) (not (memq s capt-sp)) - (or ;(local? s) ; TODO: error for local variables - (memq s (lam:sp lam))))))) + (if (and (local? s) (length> (lam:args lam) 0)) + ; error for local variables except in toplevel thunks + (error (string "local variable " s + " cannot be used in closure declaration")) + #t) + ; allow captured variables + (memq s (lam:sp lam)))))) (caddr methdef) (lambda (e) (cadr e))))) (sig-locals (simple-sort diff --git a/test/parse.jl b/test/parse.jl index 7d5aaad45964e..32a19ac158a05 100644 --- a/test/parse.jl +++ b/test/parse.jl @@ -1319,3 +1319,11 @@ let @test f() == 0 @test f(2) == 2 end + +# issue #18730 +@test expand(Main, quote + function f() + local Int + x::Int -> 2 + end + end) == Expr(:error, "local variable Int cannot be used in closure declaration") From 61e4a2672128e063cfba82ecf2bca09902052b0c Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Tue, 5 Sep 2017 11:57:14 -0400 Subject: [PATCH 284/324] fix #15723, `findfirst` on strings Makes findfirst, findlast, findnext, and findprev more generic by using endof, nextind, and prevind. Also adds a `nextind` method for arrays and `CartesianIndex`. --- base/abstractarray.jl | 3 +++ base/array.jl | 33 ++++++++++++++++++++++++--------- base/multidimensional.jl | 6 ++++++ base/strings/basic.jl | 2 -- base/tuple.jl | 3 +++ test/abstractarray.jl | 4 ++++ test/strings/search.jl | 6 ++++++ 7 files changed, 46 insertions(+), 11 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 551cebd3b90ae..c5847e584038c 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -107,6 +107,9 @@ linearindices(A::AbstractVector) = (@_inline_meta; indices1(A)) keys(a::AbstractArray) = CartesianRange(indices(a)) keys(a::AbstractVector) = linearindices(a) +prevind(::AbstractArray, i::Integer) = Int(i)-1 +nextind(::AbstractArray, i::Integer) = Int(i)+1 + eltype(::Type{<:AbstractArray{E}}) where {E} = E elsize(::AbstractArray{T}) where {T} = sizeof(T) diff --git a/base/array.jl b/base/array.jl index 44b504753b5d3..2f4aff71802b5 100644 --- a/base/array.jl +++ b/base/array.jl @@ -1621,10 +1621,13 @@ julia> findnext(A,3) ``` """ function findnext(A, start::Integer) - for i = start:length(A) + l = endof(A) + i = start + while i <= l if A[i] != 0 return i end + i = nextind(A, i) end return 0 end @@ -1671,10 +1674,13 @@ julia> findnext(A,4,3) ``` """ function findnext(A, v, start::Integer) - for i = start:length(A) + l = endof(A) + i = start + while i <= l if A[i] == v return i end + i = nextind(A, i) end return 0 end @@ -1720,10 +1726,13 @@ julia> findnext(isodd, A, 2) ``` """ function findnext(testf::Function, A, start::Integer) - for i = start:length(A) + l = endof(A) + i = start + while i <= l if testf(A[i]) return i end + i = nextind(A, i) end return 0 end @@ -1770,8 +1779,10 @@ julia> findprev(A,1) ``` """ function findprev(A, start::Integer) - for i = start:-1:1 + i = start + while i >= 1 A[i] != 0 && return i + i = prevind(A, i) end return 0 end @@ -1801,7 +1812,7 @@ julia> findlast(A) 0 ``` """ -findlast(A) = findprev(A, length(A)) +findlast(A) = findprev(A, endof(A)) """ findprev(A, v, i::Integer) @@ -1823,8 +1834,10 @@ julia> findprev(A, 1, 1) ``` """ function findprev(A, v, start::Integer) - for i = start:-1:1 + i = start + while i >= 1 A[i] == v && return i + i = prevind(A, i) end return 0 end @@ -1852,7 +1865,7 @@ julia> findlast(A,3) 0 ``` """ -findlast(A, v) = findprev(A, v, length(A)) +findlast(A, v) = findprev(A, v, endof(A)) """ findprev(predicate::Function, A, i::Integer) @@ -1875,8 +1888,10 @@ julia> findprev(isodd, A, 3) ``` """ function findprev(testf::Function, A, start::Integer) - for i = start:-1:1 + i = start + while i >= 1 testf(A[i]) && return i + i = prevind(A, i) end return 0 end @@ -1901,7 +1916,7 @@ julia> findlast(x -> x > 5, A) 0 ``` """ -findlast(testf::Function, A) = findprev(testf, A, length(A)) +findlast(testf::Function, A) = findprev(testf, A, endof(A)) """ find(f::Function, A) diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 2693333f063b7..b07572a5e98e5 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -133,6 +133,12 @@ module IteratorsMD return h end + # nextind with CartesianIndex + function Base.nextind(a::AbstractArray{<:Any,N}, i::CartesianIndex{N}) where {N} + _, ni = next(CartesianRange(indices(a)), i) + return ni + end + # Iteration """ CartesianRange(sz::Dims) -> R diff --git a/base/strings/basic.jl b/base/strings/basic.jl index 408d07504dc2c..5fd209255eb0c 100644 --- a/base/strings/basic.jl +++ b/base/strings/basic.jl @@ -170,9 +170,7 @@ end ## Generic indexing functions ## prevind(s::DirectIndexString, i::Integer) = Int(i)-1 -prevind(s::AbstractArray , i::Integer) = Int(i)-1 nextind(s::DirectIndexString, i::Integer) = Int(i)+1 -nextind(s::AbstractArray , i::Integer) = Int(i)+1 """ prevind(str::AbstractString, i::Integer) diff --git a/base/tuple.jl b/base/tuple.jl index 0f4ed589fe19d..1ffc85f9c47c2 100644 --- a/base/tuple.jl +++ b/base/tuple.jl @@ -40,6 +40,9 @@ next(t::Tuple, i::Int) = (t[i], i+1) keys(t::Tuple) = 1:length(t) +prevind(t::Tuple, i::Integer) = Int(i)-1 +nextind(t::Tuple, i::Integer) = Int(i)+1 + function keys(t::Tuple, t2::Tuple...) @_inline_meta 1:_maxlength(t, t2...) diff --git a/test/abstractarray.jl b/test/abstractarray.jl index 057b115a164ff..394d698e6c9ff 100644 --- a/test/abstractarray.jl +++ b/test/abstractarray.jl @@ -844,3 +844,7 @@ for A in (rand(2), rand(2,3)) end @test collect(values(A)) == collect(A) end + +# nextind +@test nextind(zeros(4), 2) == 3 +@test nextind(zeros(2,3), CartesianIndex(2,1)) == CartesianIndex(1, 2) diff --git a/test/strings/search.jl b/test/strings/search.jl index 34472914557b9..cb0c342844db0 100644 --- a/test/strings/search.jl +++ b/test/strings/search.jl @@ -379,3 +379,9 @@ end @test rsearchindex("\U1f596\U1f596", "\U1f596\U1f596", endof("\U1f596\U1f596\U1f596")) == 1 @test_throws ErrorException "ab" ∈ "abc" + +# issue #15723 +@test findfirst("⨳(", '(') == 4 +@test findnext("(⨳(", '(', 2) == 5 +@test findlast("(⨳(", '(') == 5 +@test findprev("(⨳(", '(', 2) == 1 From 7bb0d53a8e06b8caa14883c9768fead57c9aafda Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Wed, 6 Sep 2017 09:30:19 -0500 Subject: [PATCH 285/324] Exclude starting slash from path in URL_REGEX (#23574) Keeps URL parsing consistent between the scp-like syntax and the scheme based parsing. --- base/libgit2/utils.jl | 7 +++++-- test/libgit2.jl | 16 ++++++++-------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/base/libgit2/utils.jl b/base/libgit2/utils.jl index 27e531776a9e2..1bbfc978e39c1 100644 --- a/base/libgit2/utils.jl +++ b/base/libgit2/utils.jl @@ -10,12 +10,15 @@ const URL_REGEX = r""" )? (?[A-Za-z0-9\-\.]+) (?() - (?:\:(?\d+))? # only parse port when not using scp-like syntax + # Only parse port when not using scp-like syntax + (?:\:(?\d+))? + /? | :? ) (? - (?()/|(?<=:)) # scp-like syntax must be preceeded by a colon + # Require path to be preceeded by '/'. Alternatively, ':' when using scp-like syntax. + (?<=(?()/|:)) .* )? $ diff --git a/test/libgit2.jl b/test/libgit2.jl index 72c964cb9be0a..b781168013980 100644 --- a/test/libgit2.jl +++ b/test/libgit2.jl @@ -123,7 +123,7 @@ end @test m[:password] == "pass" @test m[:host] == "server.com" @test m[:port] == "80" - @test m[:path] == "/org/project.git" + @test m[:path] == "org/project.git" end @testset "SSH URL" begin @@ -133,7 +133,7 @@ end @test m[:password] == "pass" @test m[:host] == "server" @test m[:port] == "22" - @test m[:path] == "/project.git" + @test m[:path] == "project.git" end @testset "SSH URL, scp-like syntax" begin @@ -165,7 +165,7 @@ end @test m[:password] === nothing @test m[:host] == "github.com" @test m[:port] === nothing - @test m[:path] == "/JuliaLang/Example.jl.git" + @test m[:path] == "JuliaLang/Example.jl.git" end @testset "SSH URL, realistic" begin @@ -216,7 +216,7 @@ end password="pass", host="server.com", port=80, - path="/org/project.git") + path="org/project.git") @test url == "https://user:pass@server.com:80/org/project.git" end @@ -227,7 +227,7 @@ end password="pass", host="server", port="22", - path="/project.git") + path="project.git") @test url == "ssh://user:pass@server:22/project.git" end @@ -244,7 +244,7 @@ end url = LibGit2.git_url( scheme="https", host="github.com", - path="/JuliaLang/Example.jl.git") + path="JuliaLang/Example.jl.git") @test url == "https://github.com/JuliaLang/Example.jl.git" end @@ -273,11 +273,11 @@ end @test url == "user@server.com" end - @testset "HTTP URL, path missing slash prefix" begin + @testset "HTTP URL, path includes slash prefix" begin url = LibGit2.git_url( scheme="http", host="server.com", - path="path") + path="/path") @test url == "http://server.com/path" end From 1ca3510baa14e0371fb53764276090f9108b1e9a Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Wed, 6 Sep 2017 09:31:28 -0500 Subject: [PATCH 286/324] Display STDOUT from Pkg.build --- base/pkg/entry.jl | 2 +- test/pkg.jl | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/base/pkg/entry.jl b/base/pkg/entry.jl index 6dc47473cd17b..2ffbef9eb89c7 100644 --- a/base/pkg/entry.jl +++ b/base/pkg/entry.jl @@ -615,7 +615,7 @@ function build(pkg::AbstractString, build_file::AbstractString, errfile::Abstrac --eval $code ``` - success(pipeline(cmd, stderr=STDERR)) + success(pipeline(cmd, stdout=STDOUT, stderr=STDERR)) end function build!(pkgs::Vector, seen::Set, errfile::AbstractString) diff --git a/test/pkg.jl b/test/pkg.jl index 288cc657c0146..4d2548f253d87 100644 --- a/test/pkg.jl +++ b/test/pkg.jl @@ -572,6 +572,23 @@ temp_pkg_dir() do @test contains(msg, "INFO: Main.JULIA_RC_LOADED defined true") end end + + let package = "Output" + stdout_file = Pkg.dir(package, "stdout.txt") + stderr_file = Pkg.dir(package, "stderr.txt") + content = """ + println(STDOUT, "stdout") + println(STDERR, "stderr") + """ + write_build(package, content) + + code = "Pkg.build(\"$package\")" + msg = run(pipeline( + `$(Base.julia_cmd()) --startup-file=no -e $code`, + stdout=stdout_file, stderr=stderr_file)) + @test last(readlines(stdout_file)) == "stdout" + @test last(readlines(stderr_file)) == "stderr" + end end @testset "Pkg functions with .jl extension" begin From 508c9f536c2c55c0cb8e7a290ebaa170243ed477 Mon Sep 17 00:00:00 2001 From: Jarrett Revels Date: Wed, 6 Sep 2017 11:48:00 -0400 Subject: [PATCH 287/324] fix boundscheck value propagation codegen bug (#23595) --- src/cgutils.cpp | 6 ++---- test/codegen.jl | 5 +++++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/cgutils.cpp b/src/cgutils.cpp index a5d8e4c58e518..922c204895de5 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -1100,9 +1100,8 @@ static bool bounds_check_enabled(jl_codectx_t &ctx, jl_value_t *inbounds) { static Value *emit_bounds_check(jl_codectx_t &ctx, const jl_cgval_t &ainfo, jl_value_t *ty, Value *i, Value *len, jl_value_t *boundscheck) { Value *im1 = ctx.builder.CreateSub(i, ConstantInt::get(T_size, 1)); - jl_cgval_t ib = emit_expr(ctx, boundscheck); #if CHECK_BOUNDS==1 - if (bounds_check_enabled(ctx, ib.constant)) { + if (bounds_check_enabled(ctx, boundscheck)) { Value *ok = ctx.builder.CreateICmpULT(im1, len); BasicBlock *failBB = BasicBlock::Create(jl_LLVMContext, "fail", ctx.f); BasicBlock *passBB = BasicBlock::Create(jl_LLVMContext, "pass"); @@ -1630,9 +1629,8 @@ static Value *emit_array_nd_index( Value *a = boxed(ctx, ainfo); Value *i = ConstantInt::get(T_size, 0); Value *stride = ConstantInt::get(T_size, 1); - jl_cgval_t ib = emit_expr(ctx, inbounds); #if CHECK_BOUNDS==1 - bool bc = bounds_check_enabled(ctx, ib.constant); + bool bc = bounds_check_enabled(ctx, inbounds); BasicBlock *failBB = NULL, *endBB = NULL; if (bc) { failBB = BasicBlock::Create(jl_LLVMContext, "oob"); diff --git a/test/codegen.jl b/test/codegen.jl index 6925ef1266ff8..35ac3b09e365e 100644 --- a/test/codegen.jl +++ b/test/codegen.jl @@ -245,3 +245,8 @@ let c = [1,2,3] len2 = issue22582!(c, true) @test len1 == len2 end + +# PR #23595 +@generated f23595(g, args...) = Expr(:call, :g, Expr(:(...), :args)) +x23595 = rand(1) +@test f23595(Core.arrayref, true, x23595, 1) == x23595[] From d6c990291ad8e13160ce17ac3537249b3d60a169 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 6 Sep 2017 11:56:17 -0400 Subject: [PATCH 288/324] inference: leaftype Type{typeof(Union{})} is ambiguous fix #23413 --- base/inference.jl | 21 +++++++++++++-------- test/inference.jl | 3 +++ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/base/inference.jl b/base/inference.jl index 3e0c25f5ad701..198a5aaaca788 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -403,7 +403,16 @@ const _Type_name = Type.body.name isType(@nospecialize t) = isa(t, DataType) && (t::DataType).name === _Type_name # true if Type is inlineable as constant (is a singleton) -isconstType(@nospecialize t) = isType(t) && (isleaftype(t.parameters[1]) || t.parameters[1] === Union{}) +function isconstType(@nospecialize t) + isType(t) || return false + p1 = t.parameters[1] + # typeof(Bottom) is special since even though it is as leaftype, + # at runtime, it might be Type{Union{}} instead, so don't attempt inference of it + p1 === typeof(Union{}) && return false + p1 === Union{} && return true + isleaftype(p1) && return true + return false +end iskindtype(@nospecialize t) = (t === DataType || t === UnionAll || t === Union || t === typeof(Bottom)) @@ -1147,13 +1156,9 @@ function getfield_tfunc(@nospecialize(s00), @nospecialize(name)) rewrap(getfield_tfunc(s.b, name),s00)) elseif isa(s, Conditional) return Bottom # Bool has no fields - elseif isa(s, Const) || isType(s) + elseif isa(s, Const) || isconstType(s) if !isa(s, Const) - p1 = s.parameters[1] - if !isleaftype(p1) - return Any - end - sv = p1 + sv = s.parameters[1] else sv = s.val end @@ -1189,7 +1194,7 @@ function getfield_tfunc(@nospecialize(s00), @nospecialize(name)) end s = typeof(sv) end - if !isa(s,DataType) || s.abstract + if isType(s) || !isa(s, DataType) || s.abstract return Any end if s <: Tuple && name ⊑ Symbol diff --git a/test/inference.jl b/test/inference.jl index 4bcb3b9c01ff7..8f11ffb85b138 100644 --- a/test/inference.jl +++ b/test/inference.jl @@ -1198,3 +1198,6 @@ g23024(TT::Tuple{DataType}) = f23024(TT[1], v23024) @test Base.return_types(f23024, (DataType, Any)) == Any[Int] @test Base.return_types(g23024, (Tuple{DataType},)) == Any[Int] @test g23024((UInt8,)) === 2 + +@test !Core.Inference.isconstType(Type{typeof(Union{})}) # could be Core.TypeofBottom or Type{Union{}} at runtime +@test Base.return_types(supertype, (Type{typeof(Union{})},)) == Any[Any] From db80d86a5a26a129a88b579844902ab99f244e5b Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 6 Sep 2017 12:44:03 -0400 Subject: [PATCH 289/324] invoke should raise MethodError for ambiguous cases fix #18095 fix #22988 (by improving error message) --- base/reflection.jl | 21 +++++---------------- src/gf.c | 22 ++++++++++++++++++++-- test/ambiguous.jl | 32 ++++++++++++++++++++------------ test/core.jl | 13 ++++++++++--- 4 files changed, 55 insertions(+), 33 deletions(-) diff --git a/base/reflection.jl b/base/reflection.jl index 6ab945b66a619..41b67dff32964 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -884,23 +884,12 @@ function which(@nospecialize(f), @nospecialize(t)) throw(ArgumentError("argument is not a generic function")) end t = to_tuple_type(t) - if isleaftype(t) - ms = methods(f, t) - isempty(ms) && error("no method found for the specified argument types") - length(ms)!=1 && error("no unique matching method for the specified argument types") - return first(ms) - else - tt = signature_type(f, t) - m = ccall(:jl_gf_invoke_lookup, Any, (Any, UInt), tt, typemax(UInt)) - if m === nothing - error("no method found for the specified argument types") - end - meth = m.func::Method - if ccall(:jl_has_call_ambiguities, Cint, (Any, Any), tt, meth) != 0 - error("method match is ambiguous for the specified argument types") - end - return meth + tt = signature_type(f, t) + m = ccall(:jl_gf_invoke_lookup, Any, (Any, UInt), tt, typemax(UInt)) + if m === nothing + error("no unique matching method found for the specified argument types") end + return m.func::Method end """ diff --git a/src/gf.c b/src/gf.c index 459dcd9d87792..6e01d6de2669a 100644 --- a/src/gf.c +++ b/src/gf.c @@ -1552,7 +1552,7 @@ jl_method_instance_t *jl_method_lookup_by_type(jl_methtable_t *mt, jl_tupletype_ JL_UNLOCK(&mt->writelock); return linfo; } - if (jl_is_leaf_type((jl_value_t*)types)) + if (jl_is_leaf_type((jl_value_t*)types)) // FIXME: this is the wrong predicate cache = 1; jl_method_instance_t *sf = jl_mt_assoc_by_type(mt, types, cache, allow_exec, world); if (cache) { @@ -1745,9 +1745,25 @@ JL_DLLEXPORT jl_value_t *jl_get_spec_lambda(jl_tupletype_t *types, size_t world) return li ? (jl_value_t*)li : jl_nothing; } +// see if a call to m with computed from `types` is ambiguous +JL_DLLEXPORT int jl_is_call_ambiguous(jl_tupletype_t *types, jl_method_t *m) +{ + if (m->ambig == jl_nothing) + return 0; + for (size_t i = 0; i < jl_array_len(m->ambig); i++) { + jl_method_t *mambig = (jl_method_t*)jl_array_ptr_ref(m->ambig, i); + if (jl_subtype((jl_value_t*)types, (jl_value_t*)mambig->sig)) + return 1; + } + return 0; +} + +// see if a call to m with a subtype of `types` might be ambiguous +// if types is from a call signature (approximated by isleaftype), this is the same as jl_is_call_ambiguous above JL_DLLEXPORT int jl_has_call_ambiguities(jl_tupletype_t *types, jl_method_t *m) { - if (m->ambig == jl_nothing) return 0; + if (m->ambig == jl_nothing) + return 0; for (size_t i = 0; i < jl_array_len(m->ambig); i++) { jl_method_t *mambig = (jl_method_t*)jl_array_ptr_ref(m->ambig, i); if (!jl_has_empty_intersection((jl_value_t*)mambig->sig, (jl_value_t*)types)) @@ -1951,6 +1967,8 @@ JL_DLLEXPORT jl_value_t *jl_gf_invoke_lookup(jl_datatype_t *types, size_t world) JL_GC_POP(); if (!entry) return jl_nothing; + if (jl_is_call_ambiguous(types, entry->func.method)) + return jl_nothing; return (jl_value_t*)entry; } diff --git a/test/ambiguous.jl b/test/ambiguous.jl index 1f4f63296369c..9b7d010721d32 100644 --- a/test/ambiguous.jl +++ b/test/ambiguous.jl @@ -65,20 +65,28 @@ end ## Other ways of accessing functions # Test that non-ambiguous cases work -io = IOBuffer() -@test precompile(ambig, (Int, Int)) == true -cfunction(ambig, Int, Tuple{Int, Int}) -@test length(code_lowered(ambig, (Int, Int))) == 1 -@test length(code_typed(ambig, (Int, Int))) == 1 -code_llvm(io, ambig, (Int, Int)) -code_native(io, ambig, (Int, Int)) +let io = IOBuffer() + @test precompile(ambig, (Int, Int)) == true + cf = cfunction(ambig, Int, Tuple{Int, Int}) + @test ccall(cf, Int, (Int, Int), 1, 2) == 4 + @test length(code_lowered(ambig, (Int, Int))) == 1 + @test length(code_typed(ambig, (Int, Int))) == 1 + code_llvm(io, ambig, (Int, Int)) + code_native(io, ambig, (Int, Int)) +end # Test that ambiguous cases fail appropriately -@test precompile(ambig, (UInt8, Int)) == false -cfunction(ambig, Int, Tuple{UInt8, Int}) # test for a crash (doesn't throw an error) -@test_throws ErrorException which(ambig, (UInt8, Int)) -@test_throws ErrorException code_llvm(io, ambig, (UInt8, Int)) -@test_throws ErrorException code_native(io, ambig, (UInt8, Int)) +let io = IOBuffer() + @test precompile(ambig, (UInt8, Int)) == false + cf = cfunction(ambig, Int, Tuple{UInt8, Int}) # test for a crash (doesn't throw an error) + @test_throws MethodError ccall(cf, Int, (UInt8, Int), 1, 2) + @test_throws(ErrorException("no unique matching method found for the specified argument types"), + which(ambig, (UInt8, Int))) + @test_throws(ErrorException("no unique matching method found for the specified argument types"), + code_llvm(io, ambig, (UInt8, Int))) + @test_throws(ErrorException("no unique matching method found for the specified argument types"), + code_native(io, ambig, (UInt8, Int))) +end # Method overwriting doesn't destroy ambiguities @test_throws MethodError ambig(2, 0x03) diff --git a/test/core.jl b/test/core.jl index dfdbfb13513f0..9999634c7e438 100644 --- a/test/core.jl +++ b/test/core.jl @@ -1107,7 +1107,7 @@ end # issue #2169 let i2169(a::Array{T}) where {T} = typemin(T) - @test invoke(i2169, Tuple{Array} ,Int8[1]) === Int8(-128) + @test invoke(i2169, Tuple{Array}, Int8[1]) === Int8(-128) end # issue #2365 @@ -4541,9 +4541,9 @@ let x = 1 @noinline g18444(a) = (x += 1; a[]) f18444_1(a) = invoke(sin, Tuple{Int}, g18444(a)) f18444_2(a) = invoke(sin, Tuple{Integer}, g18444(a)) - @test_throws ErrorException f18444_1(Ref{Any}(1.0)) + @test_throws ErrorException("invoke: argument type error") f18444_1(Ref{Any}(1.0)) @test x == 2 - @test_throws ErrorException f18444_2(Ref{Any}(1.0)) + @test_throws ErrorException("invoke: argument type error") f18444_2(Ref{Any}(1.0)) @test x == 3 @test f18444_1(Ref{Any}(1)) === sin(1) @test x == 4 @@ -4551,6 +4551,13 @@ let x = 1 @test x == 5 end +f18095(::Int, ::Number) = 0x21 +f18095(::Number, ::Int) = 0x12 +@test_throws MethodError f18095(1, 2) +@test_throws MethodError invoke(f18095, Tuple{Int, Int}, 1, 2) +@test_throws MethodError invoke(f18095, Tuple{Int, Any}, 1, 2) +@test invoke(f18095, Tuple{Int, Real}, 1, 2) === 0x21 + # issue #10981, long argument lists let a = fill(["sdf"], 2*10^6), temp_vcat(x...) = vcat(x...) # we introduce a new function `temp_vcat` to make sure there is no existing From e21f35a174908ecbb023d53b4a4287de7749dd89 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 6 Sep 2017 13:09:04 -0400 Subject: [PATCH 290/324] fieldtype validation: disallow `Vararg` as a field type fix #17951 --- src/interpreter.c | 15 ++++++++------- test/core.jl | 19 ++++++++++++------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/interpreter.c b/src/interpreter.c index e79e858eb3b3e..3892df26f90ac 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -380,7 +380,7 @@ static jl_value_t *eval(jl_value_t *e, interpreter_state *s) } else if (ex->head == primtype_sym) { if (inside_typedef) - jl_error("cannot eval a new bits type definition while defining another type"); + jl_error("cannot eval a new primitive type definition while defining another type"); jl_value_t *name = args[0]; jl_value_t *super = NULL, *para = NULL, *vnb = NULL, *temp = NULL; jl_datatype_t *dt = NULL; @@ -395,11 +395,11 @@ static jl_value_t *eval(jl_value_t *e, interpreter_state *s) assert(jl_is_svec(para)); vnb = eval(args[2], s); if (!jl_is_long(vnb)) - jl_errorf("invalid declaration of bits type %s", + jl_errorf("invalid declaration of primitive type %s", jl_symbol_name((jl_sym_t*)name)); ssize_t nb = jl_unbox_long(vnb); - if (nb < 1 || nb>=(1<<23) || (nb&7) != 0) - jl_errorf("invalid number of bits in type %s", + if (nb < 1 || nb >= (1 << 23) || (nb & 7) != 0) + jl_errorf("invalid number of bits in primitive type %s", jl_symbol_name((jl_sym_t*)name)); dt = jl_new_primitivetype(name, modu, NULL, (jl_svec_t*)para, nb); w = dt->name->wrapper; @@ -428,7 +428,7 @@ static jl_value_t *eval(jl_value_t *e, interpreter_state *s) } else if (ex->head == structtype_sym) { if (inside_typedef) - jl_error("cannot eval a new data type definition while defining another type"); + jl_error("cannot eval a new struct type definition while defining another type"); jl_value_t *name = args[0]; jl_value_t *para = eval(args[1], s); jl_value_t *temp = NULL; @@ -462,12 +462,13 @@ static jl_value_t *eval(jl_value_t *e, interpreter_state *s) jl_set_datatype_super(dt, super); dt->types = (jl_svec_t*)eval(args[4], s); jl_gc_wb(dt, dt->types); - for(size_t i=0; i < jl_svec_len(dt->types); i++) { + for (size_t i = 0; i < jl_svec_len(dt->types); i++) { jl_value_t *elt = jl_svecref(dt->types, i); - if (!jl_is_type(elt) && !jl_is_typevar(elt)) + if ((!jl_is_type(elt) && !jl_is_typevar(elt)) || jl_is_vararg_type(elt)) { jl_type_error_rt(jl_symbol_name(dt->name->name), "type definition", (jl_value_t*)jl_type_type, elt); + } } jl_reinstantiate_inner_types(dt); } diff --git a/test/core.jl b/test/core.jl index dfdbfb13513f0..18218ad7f48fa 100644 --- a/test/core.jl +++ b/test/core.jl @@ -1276,14 +1276,13 @@ let @test foo(x) == [1.0, 2.0, 3.0] end -# TODO!! # issue #4115 -#mutable struct Foo4115 -#end -#const Foo4115s = NTuple{3,Union{Foo4115,Type{Foo4115}}} -#baz4115(x::Foo4115s) = x -#@test baz4115(convert(Tuple{Type{Foo4115},Type{Foo4115},Foo4115}, -# (Foo4115,Foo4115,Foo4115()))) == (Foo4115,Foo4115,Foo4115()) +mutable struct Foo4115 end +const Foo4115s = NTuple{3, Union{Foo4115, Type{Foo4115}}} +baz4115(x::Foo4115s) = x +let t = (Foo4115, Foo4115, Foo4115()) + @test_throws MethodError baz4115(t) +end # issue #4129 mutable struct Foo4129; end @@ -4859,6 +4858,12 @@ let a = Array{Core.TypeofBottom, 1}(2) @test a == [Union{}, Union{}] end +@test_throws TypeError(:T17951, "type definition", Type, Vararg) @eval begin + struct T17951 + x::Vararg + end +end + # issue #21178 struct F21178{A,B} end b21178(::F1,::F2) where {B1,B2,F1<:F21178{B1,<:Any},F2<:F21178{B2}} = F1,F2,B1,B2 From d7169aea98954d81b9fb4929b777bc19d20023c4 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 10 Aug 2017 13:53:35 -0400 Subject: [PATCH 291/324] move macroexpand to C cuts down on complexity a bit (it now never needs to transition from Julia -> flisp -> Julia) and fixes backtraces from macros! (fix #15643) --- src/ast.c | 414 +++++++++++++++++++++++-------------------- src/codegen.cpp | 14 +- src/jlfrontend.scm | 13 +- src/julia-syntax.scm | 4 +- src/macroexpand.scm | 78 ++++---- test/core.jl | 21 ++- test/parse.jl | 14 +- test/replutil.jl | 10 +- 8 files changed, 297 insertions(+), 271 deletions(-) diff --git a/src/ast.c b/src/ast.c index b4e513703f7e8..847a19df05451 100644 --- a/src/ast.c +++ b/src/ast.c @@ -57,6 +57,9 @@ jl_sym_t *unused_sym; jl_sym_t *static_parameter_sym; jl_sym_t *polly_sym; jl_sym_t *inline_sym; jl_sym_t *propagate_inbounds_sym; jl_sym_t *isdefined_sym; jl_sym_t *nospecialize_sym; +jl_sym_t *macrocall_sym; +jl_sym_t *hygienicscope_sym; +jl_sym_t *escape_sym; static uint8_t flisp_system_image[] = { #include @@ -97,12 +100,8 @@ typedef struct _jl_ast_context_t { value_t slot_sym; jl_ast_context_list_t list; int ref; - jl_task_t *task; - jl_module_t *module; - // use a pointer to a stack slot so that we can detect if - // `jl_ast_preserve` is called in the wrong context. - // If `roots` is not NULL, it always points to a rooted stack slot. - jl_array_t **roots; + jl_task_t *task; // the current owner (user) of this jl_ast_context_t + jl_module_t *module; // context module for "defined-julia-global" and "current-julia-module-counter" } jl_ast_context_t; static jl_ast_context_t jl_ast_main_ctx; @@ -111,36 +110,20 @@ static jl_ast_context_t jl_ast_main_ctx; #define jl_ast_context_list_item(node) \ container_of(node, jl_ast_context_t, list) -#define JL_AST_PRESERVE_PUSH(ctx, _roots, old, inmodule) \ - jl_array_t *(_roots) = NULL; \ - struct { jl_array_t **roots; \ - jl_module_t *module; } (old); \ - old.roots = ctx->roots; \ - old.module = ctx->module; \ - ctx->roots = &(_roots); \ - ctx->module = (inmodule); \ - JL_GC_PUSH2(&(_roots), &(inmodule)) -#define JL_AST_PRESERVE_POP(ctx, old) \ - JL_GC_POP(); \ - ctx->roots = (old).roots; \ - ctx->module = (old).module +struct macroctx_stack { + jl_module_t *m; + struct macroctx_stack *parent; +}; -static void jl_ast_preserve(fl_context_t *fl_ctx, jl_value_t *obj) -{ - jl_ast_context_t *ctx = jl_ast_ctx(fl_ctx); - assert(ctx->roots); - jl_array_t *roots = *ctx->roots; - if (!roots) { - roots = *ctx->roots = jl_alloc_vec_any(1); - jl_array_ptr_set(roots, 0, obj); - } - else { - jl_array_ptr_1d_push(roots, obj); - } -} +#define JL_AST_PRESERVE_PUSH(ctx, old, inmodule) \ + jl_module_t *(old) = ctx->module; \ + ctx->module = (inmodule) +#define JL_AST_PRESERVE_POP(ctx, old) \ + ctx->module = (old) -static jl_value_t *scm_to_julia(fl_context_t *fl_ctx, value_t e, int expronly); +static jl_value_t *scm_to_julia(fl_context_t *fl_ctx, value_t e, int expronly, jl_module_t *mod); static value_t julia_to_scm(fl_context_t *fl_ctx, jl_value_t *v); +static jl_value_t *jl_expand_macros(jl_value_t *expr, jl_module_t *inmodule, struct macroctx_stack *macroctx, int onelevel); value_t fl_defined_julia_global(fl_context_t *fl_ctx, value_t *args, uint32_t nargs) { @@ -148,7 +131,7 @@ value_t fl_defined_julia_global(fl_context_t *fl_ctx, value_t *args, uint32_t na // tells whether a var is defined in and *by* the current module argcount(fl_ctx, "defined-julia-global", nargs, 1); jl_module_t *mod = ctx->module; - jl_sym_t *sym = (jl_sym_t*)scm_to_julia(fl_ctx, args[0], 0); + jl_sym_t *sym = (jl_sym_t*)scm_to_julia(fl_ctx, args[0], 0, mod); if (jl_is_globalref(sym)) { mod = jl_globalref_mod(sym); sym = jl_globalref_name(sym); @@ -168,86 +151,6 @@ value_t fl_current_module_counter(fl_context_t *fl_ctx, value_t *args, uint32_t return fixnum(jl_module_next_counter(ctx->module)); } -value_t fl_invoke_julia_macro(fl_context_t *fl_ctx, value_t *args, uint32_t nargs) -{ - JL_TIMING(MACRO_INVOCATION); - jl_ptls_t ptls = jl_get_ptls_states(); - jl_ast_context_t *ctx = jl_ast_ctx(fl_ctx); - if (nargs < 3) // macro name and location - argcount(fl_ctx, "invoke-julia-macro", nargs, 3); - jl_method_instance_t *mfunc = NULL; - jl_value_t **margs; - // Reserve one more slot for the result - JL_GC_PUSHARGS(margs, nargs + 1); - int i; - margs[0] = scm_to_julia(fl_ctx, args[1], 1); - // __source__ argument - jl_value_t *lno = scm_to_julia(fl_ctx, args[2], 1); - margs[1] = lno; - if (jl_is_expr(lno) && ((jl_expr_t*)lno)->head == line_sym) { - jl_value_t *file = jl_nothing; - jl_value_t *line = NULL; - switch (jl_expr_nargs(lno)) { - case 2: - file = jl_exprarg(lno, 1); // file - JL_FALLTHROUGH; - case 1: - line = jl_exprarg(lno, 0); // line - JL_FALLTHROUGH; - default: ; - } - if (line == NULL) - line = jl_box_long(0); - margs[1] = jl_new_struct(jl_linenumbernode_type, line, file); - } - else if (!jl_typeis(lno, jl_linenumbernode_type)) { - margs[1] = jl_new_struct(jl_linenumbernode_type, jl_box_long(0), jl_nothing); - } - margs[2] = (jl_value_t*)ctx->module; - for (i = 3; i < nargs; i++) - margs[i] = scm_to_julia(fl_ctx, args[i], 1); - margs[nargs] = scm_to_julia(fl_ctx, args[0], 1); // module context for @macrocall argument - jl_value_t *result = NULL; - size_t world = jl_get_ptls_states()->world_age; - - JL_TRY { - jl_module_t *m = (jl_module_t*)margs[nargs]; - if (!jl_is_module(m)) - m = ctx->module; - margs[0] = jl_toplevel_eval(m, margs[0]); - mfunc = jl_method_lookup(jl_gf_mtable(margs[0]), margs, nargs, 1, world); - if (mfunc == NULL) { - jl_method_error((jl_function_t*)margs[0], margs, nargs, world); - // unreachable - } - margs[nargs] = result = jl_call_method_internal(mfunc, margs, nargs); - } - JL_CATCH { - JL_GC_POP(); - value_t opaque = cvalue(fl_ctx, jl_ast_ctx(fl_ctx)->jvtype, sizeof(void*)); - *(jl_value_t**)cv_data((cvalue_t*)ptr(opaque)) = ptls->exception_in_transit; - return fl_list2(fl_ctx, jl_ast_ctx(fl_ctx)->error_sym, opaque); - } - // protect result from GC, otherwise it could be freed during future - // macro expansions, since it will be referenced only from scheme and - // not julia. - // all calls to invoke-julia-macro happen under `jl_macroexpand`, - // `jl_expand` or `jl_parse_eval_all` so the preserved array is rooted there. - assert(result != NULL); - jl_ast_preserve(fl_ctx, result); - value_t scm = julia_to_scm(fl_ctx, result); - fl_gc_handle(fl_ctx, &scm); - value_t scmresult; - jl_module_t *defmod = mfunc->def.method->module; - value_t opaque = cvalue(fl_ctx, jl_ast_ctx(fl_ctx)->jvtype, sizeof(void*)); - *(jl_value_t**)cv_data((cvalue_t*)ptr(opaque)) = (jl_value_t*)defmod; - scmresult = fl_cons(fl_ctx, scm, opaque); - fl_free_gc_handles(fl_ctx, 1); - - JL_GC_POP(); - return scmresult; -} - // Check whether v is a scalar for purposes of inlining fused-broadcast // arguments when lowering; should agree with broadcast.jl on what is a // scalar. When in doubt, return false, since this is only an optimization. @@ -266,7 +169,6 @@ value_t fl_julia_scalar(fl_context_t *fl_ctx, value_t *args, uint32_t nargs) static const builtinspec_t julia_flisp_ast_ext[] = { { "defined-julia-global", fl_defined_julia_global }, - { "invoke-julia-macro", fl_invoke_julia_macro }, { "current-julia-module-counter", fl_current_module_counter }, { "julia-scalar?", fl_julia_scalar }, { NULL, NULL } @@ -334,14 +236,12 @@ static jl_ast_context_t *jl_ast_ctx_enter(void) ctx = jl_ast_context_list_item(node); ctx->ref = 1; ctx->task = ptls->current_task; - ctx->roots = NULL; ctx->module = NULL; JL_UNLOCK_NOGC(&flisp_lock); return ctx; } // Construct a new one if we can't find any ctx = (jl_ast_context_t*)calloc(1, sizeof(jl_ast_context_t)); - // ctx->roots is NULL already due to calloc. ctx->ref = 1; ctx->task = ptls->current_task; node = &ctx->list; @@ -437,6 +337,9 @@ void jl_init_frontend(void) propagate_inbounds_sym = jl_symbol("propagate_inbounds"); isdefined_sym = jl_symbol("isdefined"); nospecialize_sym = jl_symbol("nospecialize"); + macrocall_sym = jl_symbol("macrocall"); + escape_sym = jl_symbol("escape"); + hygienicscope_sym = jl_symbol("hygienic-scope"); } JL_DLLEXPORT void jl_lisp_prompt(void) @@ -446,7 +349,7 @@ JL_DLLEXPORT void jl_lisp_prompt(void) JL_SIGATOMIC_BEGIN(); jl_init_frontend(); jl_ast_context_t *ctx = jl_ast_ctx_enter(); - JL_AST_PRESERVE_PUSH(ctx, roots, old_roots, jl_main_module); + JL_AST_PRESERVE_PUSH(ctx, old_roots, jl_main_module); fl_context_t *fl_ctx = &ctx->fl; fl_applyn(fl_ctx, 1, symbol_value(symbol(fl_ctx, "__start")), fl_cons(fl_ctx, fl_ctx->NIL,fl_ctx->NIL)); JL_AST_PRESERVE_POP(ctx, old_roots); @@ -491,14 +394,14 @@ static jl_sym_t *scmsym_to_julia(fl_context_t *fl_ctx, value_t s) return jl_symbol(symbol_name(fl_ctx, s)); } -static jl_value_t *scm_to_julia_(fl_context_t *fl_ctx, value_t e, int expronly); +static jl_value_t *scm_to_julia_(fl_context_t *fl_ctx, value_t e, int expronly, jl_module_t *mod); -static jl_value_t *scm_to_julia(fl_context_t *fl_ctx, value_t e, int expronly) +static jl_value_t *scm_to_julia(fl_context_t *fl_ctx, value_t e, int expronly, jl_module_t *mod) { jl_value_t *v = NULL; JL_GC_PUSH1(&v); JL_TRY { - v = scm_to_julia_(fl_ctx, e, expronly); + v = scm_to_julia_(fl_ctx, e, expronly, mod); } JL_CATCH { // if expression cannot be converted, replace with error expr @@ -512,9 +415,8 @@ static jl_value_t *scm_to_julia(fl_context_t *fl_ctx, value_t e, int expronly) extern int64_t conv_to_int64(void *data, numerictype_t tag); -static jl_value_t *scm_to_julia_(fl_context_t *fl_ctx, value_t e, int eo) +static jl_value_t *scm_to_julia_(fl_context_t *fl_ctx, value_t e, int eo, jl_module_t *mod) { - jl_ast_context_t *ctx = jl_ast_ctx(fl_ctx); if (fl_isnumber(fl_ctx, e)) { int64_t i64; if (isfixnum(e)) { @@ -586,11 +488,11 @@ static jl_value_t *scm_to_julia_(fl_context_t *fl_ctx, value_t e, int eo) n++; if (!eo) { if (sym == line_sym && (n == 1 || n == 2)) { - jl_value_t *linenum = scm_to_julia_(fl_ctx, car_(e), 0); + jl_value_t *linenum = scm_to_julia_(fl_ctx, car_(e), 0, mod); jl_value_t *file = jl_nothing; JL_GC_PUSH2(&linenum, &file); if (n == 2) - file = scm_to_julia_(fl_ctx, car_(cdr_(e)), 0); + file = scm_to_julia_(fl_ctx, car_(cdr_(e)), 0, mod); jl_value_t *temp = jl_new_struct(jl_linenumbernode_type, linenum, file); JL_GC_POP(); return temp; @@ -598,40 +500,41 @@ static jl_value_t *scm_to_julia_(fl_context_t *fl_ctx, value_t e, int eo) jl_value_t *scmv = NULL, *temp = NULL; JL_GC_PUSH1(&scmv); if (sym == label_sym) { - scmv = scm_to_julia_(fl_ctx,car_(e),0); + scmv = scm_to_julia_(fl_ctx, car_(e), 0, mod); temp = jl_new_struct(jl_labelnode_type, scmv); JL_GC_POP(); return temp; } if (sym == goto_sym) { - scmv = scm_to_julia_(fl_ctx,car_(e),0); + scmv = scm_to_julia_(fl_ctx, car_(e), 0, mod); temp = jl_new_struct(jl_gotonode_type, scmv); JL_GC_POP(); return temp; } if (sym == inert_sym || (sym == quote_sym && (!iscons(car_(e))))) { - scmv = scm_to_julia_(fl_ctx,car_(e),0); + scmv = scm_to_julia_(fl_ctx, car_(e), 0, mod); temp = jl_new_struct(jl_quotenode_type, scmv); JL_GC_POP(); return temp; } if (sym == top_sym) { - scmv = scm_to_julia_(fl_ctx,car_(e),0); + assert(mod && "top should not be generated by the parser"); + scmv = scm_to_julia_(fl_ctx, car_(e), 0, mod); assert(jl_is_symbol(scmv)); - temp = jl_module_globalref(jl_base_relative_to(ctx->module), (jl_sym_t*)scmv); + temp = jl_module_globalref(jl_base_relative_to(mod), (jl_sym_t*)scmv); JL_GC_POP(); return temp; } if (sym == core_sym) { - scmv = scm_to_julia_(fl_ctx,car_(e),0); + scmv = scm_to_julia_(fl_ctx, car_(e), 0, mod); assert(jl_is_symbol(scmv)); temp = jl_module_globalref(jl_core_module, (jl_sym_t*)scmv); JL_GC_POP(); return temp; } if (sym == globalref_sym) { - scmv = scm_to_julia_(fl_ctx,car_(e),0); - temp = scm_to_julia_(fl_ctx,car_(cdr_(e)),0); + scmv = scm_to_julia_(fl_ctx, car_(e), 0, mod); + temp = scm_to_julia_(fl_ctx, car_(cdr_(e)), 0, mod); assert(jl_is_module(scmv)); assert(jl_is_symbol(temp)); temp = jl_module_globalref((jl_module_t*)scmv, (jl_sym_t*)temp); @@ -639,7 +542,7 @@ static jl_value_t *scm_to_julia_(fl_context_t *fl_ctx, value_t e, int eo) return temp; } if (sym == newvar_sym) { - scmv = scm_to_julia_(fl_ctx,car_(e),0); + scmv = scm_to_julia_(fl_ctx, car_(e), 0, mod); temp = jl_new_struct(jl_newvarnode_type, scmv); JL_GC_POP(); return temp; @@ -657,9 +560,9 @@ static jl_value_t *scm_to_julia_(fl_context_t *fl_ctx, value_t e, int eo) jl_gc_wb(ex, ((jl_expr_t*)ex)->args); } size_t i; - for(i=0; i < n; i++) { + for (i = 0; i < n; i++) { assert(iscons(e)); - jl_array_ptr_set(((jl_expr_t*)ex)->args, i, scm_to_julia_(fl_ctx, car_(e), eo)); + jl_array_ptr_set(((jl_expr_t*)ex)->args, i, scm_to_julia_(fl_ctx, car_(e), eo, mod)); e = cdr_(e); } if (sym == lambda_sym) @@ -676,8 +579,6 @@ static jl_value_t *scm_to_julia_(fl_context_t *fl_ctx, value_t e, int eo) return *(jl_value_t**)cv_data((cvalue_t*)ptr(e)); } jl_error("malformed tree"); - - return jl_nothing; } static value_t julia_to_scm_(fl_context_t *fl_ctx, jl_value_t *v); @@ -801,7 +702,7 @@ JL_DLLEXPORT jl_value_t *jl_parse_input_line(const char *str, size_t len, const value_t s = cvalue_static_cstrn(fl_ctx, str, len); value_t files = cvalue_static_cstrn(fl_ctx, filename, filename_len); value_t e = fl_applyn(fl_ctx, 2, symbol_value(symbol(fl_ctx, "jl-parse-string")), s, files); - jl_value_t *res = e == fl_ctx->FL_EOF ? jl_nothing : scm_to_julia(fl_ctx, e, 0); + jl_value_t *res = e == fl_ctx->FL_EOF ? jl_nothing : scm_to_julia(fl_ctx, e, 0, NULL); jl_ast_ctx_leave(ctx); return res; } @@ -830,9 +731,9 @@ JL_DLLEXPORT jl_value_t *jl_parse_string(const char *str, size_t len, if (e == fl_ctx->FL_EOF) expr = jl_nothing; else - expr = scm_to_julia(fl_ctx, e, 0); + expr = scm_to_julia(fl_ctx, e, 0, NULL); - pos1 = jl_box_long(tosize(fl_ctx, cdr_(p),"parse")); + pos1 = jl_box_long(tosize(fl_ctx, cdr_(p), "parse")); jl_ast_ctx_leave(ctx); jl_value_t *result = (jl_value_t*)jl_svec2(expr, pos1); JL_GC_POP(); @@ -849,7 +750,7 @@ jl_value_t *jl_parse_eval_all(const char *fname, jl_error("cannot use include inside a generated function"); jl_ast_context_t *ctx = jl_ast_ctx_enter(); fl_context_t *fl_ctx = &ctx->fl; - value_t f, ast; + value_t f, ast, expression; size_t len = strlen(fname); f = cvalue_static_cstrn(fl_ctx, fname, len); fl_gc_handle(fl_ctx, &f); @@ -871,31 +772,35 @@ jl_value_t *jl_parse_eval_all(const char *fname, jl_errorf("could not open file %s", fname); } fl_gc_handle(fl_ctx, &ast); + fl_gc_handle(fl_ctx, &expression); int last_lineno = jl_lineno; const char *last_filename = jl_filename; size_t last_age = jl_get_ptls_states()->world_age; jl_lineno = 0; jl_filename = fname; - jl_array_t *roots = NULL; - jl_array_t **old_roots = ctx->roots; jl_module_t *old_module = ctx->module; - ctx->roots = &roots; ctx->module = inmodule; - jl_value_t *form=NULL, *result=jl_nothing; + jl_value_t *form = NULL; + jl_value_t *result = jl_nothing; int err = 0; - JL_GC_PUSH4(&roots, &form, &result, &inmodule); + JL_GC_PUSH2(&form, &result); JL_TRY { - assert(iscons(ast) && car_(ast) == symbol(fl_ctx,"toplevel")); + assert(iscons(ast) && car_(ast) == symbol(fl_ctx, "toplevel")); ast = cdr_(ast); while (iscons(ast)) { - value_t expansion; + expression = car_(ast); { JL_TIMING(LOWERING); - expansion = fl_applyn(fl_ctx, 1, symbol_value(symbol(fl_ctx, "jl-expand-to-thunk")), car_(ast)); + if (fl_ctx->T == fl_applyn(fl_ctx, 1, symbol_value(symbol(fl_ctx, "contains-macrocall")), expression)) { + form = scm_to_julia(fl_ctx, expression, 0, inmodule); + form = jl_expand_macros(form, inmodule, NULL, 0); + expression = julia_to_scm(fl_ctx, form); + } + expression = fl_applyn(fl_ctx, 1, symbol_value(symbol(fl_ctx, "jl-expand-to-thunk")), expression); } jl_get_ptls_states()->world_age = jl_world_counter; - form = scm_to_julia(fl_ctx, expansion, 0); + form = scm_to_julia(fl_ctx, expression, 0, inmodule); jl_sym_t *head = NULL; if (jl_is_expr(form)) head = ((jl_expr_t*)form)->head; @@ -923,8 +828,7 @@ jl_value_t *jl_parse_eval_all(const char *fname, jl_get_ptls_states()->world_age = last_age; jl_lineno = last_lineno; jl_filename = last_filename; - fl_free_gc_handles(fl_ctx, 1); - ctx->roots = old_roots; + fl_free_gc_handles(fl_ctx, 2); ctx->module = old_module; jl_ast_ctx_leave(ctx); if (err) { @@ -971,33 +875,15 @@ jl_value_t *jl_call_scm_on_ast(const char *funcname, jl_value_t *expr, jl_module { jl_ast_context_t *ctx = jl_ast_ctx_enter(); fl_context_t *fl_ctx = &ctx->fl; - JL_AST_PRESERVE_PUSH(ctx, roots, old_roots, inmodule); + JL_AST_PRESERVE_PUSH(ctx, old_roots, inmodule); value_t arg = julia_to_scm(fl_ctx, expr); value_t e = fl_applyn(fl_ctx, 1, symbol_value(symbol(fl_ctx, funcname)), arg); - jl_value_t *result = scm_to_julia(fl_ctx, e, 0); + jl_value_t *result = scm_to_julia(fl_ctx, e, 0, inmodule); JL_AST_PRESERVE_POP(ctx, old_roots); jl_ast_ctx_leave(ctx); return result; } -JL_DLLEXPORT jl_value_t *jl_expand(jl_value_t *expr, jl_module_t *inmodule) -{ - JL_TIMING(LOWERING); - return jl_call_scm_on_ast("jl-expand-to-thunk", expr, inmodule); -} - -JL_DLLEXPORT jl_value_t *jl_macroexpand(jl_value_t *expr, jl_module_t *inmodule) -{ - JL_TIMING(LOWERING); - return jl_call_scm_on_ast("jl-macroexpand", expr, inmodule); -} - -JL_DLLEXPORT jl_value_t *jl_macroexpand1(jl_value_t *expr, jl_module_t *inmodule) -{ - JL_TIMING(LOWERING); - return jl_call_scm_on_ast("jl-macroexpand-1", expr, inmodule); -} - // wrap expr in a thunk AST jl_code_info_t *jl_wrap_expr(jl_value_t *expr) { @@ -1031,38 +917,24 @@ jl_code_info_t *jl_wrap_expr(jl_value_t *expr) JL_DLLEXPORT jl_value_t *jl_copy_ast(jl_value_t *expr) { - if (expr == NULL) { - return NULL; - } - else if (jl_is_expr(expr)) { + if (expr && jl_is_expr(expr)) { jl_expr_t *e = (jl_expr_t*)expr; size_t i, l = jl_array_len(e->args); - jl_expr_t *ne = NULL; + jl_expr_t *ne = jl_exprn(e->head, l); JL_GC_PUSH2(&ne, &expr); - ne = jl_exprn(e->head, l); if (l == 0) { ne->args = jl_alloc_vec_any(0); jl_gc_wb(ne, ne->args); } else { - for(i=0; i < l; i++) { - jl_exprargset(ne, i, jl_copy_ast(jl_exprarg(e,i))); + for (i = 0; i < l; i++) { + jl_value_t *a = jl_exprarg(e, i); + jl_exprargset(ne, i, jl_copy_ast(a)); } } JL_GC_POP(); return (jl_value_t*)ne; } - else if (jl_typeis(expr,jl_array_any_type)) { - jl_array_t *a = (jl_array_t*)expr; - size_t i, l = jl_array_len(a); - jl_array_t *na = NULL; - JL_GC_PUSH2(&na, &expr); - na = jl_alloc_vec_any(l); - for(i=0; i < l; i++) - jl_array_ptr_set(na, i, jl_copy_ast(jl_array_ptr_ref(a,i))); - JL_GC_POP(); - return (jl_value_t*)na; - } return expr; } @@ -1099,6 +971,164 @@ int jl_has_meta(jl_array_t *body, jl_sym_t *sym) return 0; } +static jl_value_t *jl_invoke_julia_macro(jl_array_t *args, jl_module_t *inmodule, jl_module_t **ctx) +{ + jl_ptls_t ptls = jl_get_ptls_states(); + JL_TIMING(MACRO_INVOCATION); + size_t nargs = jl_array_len(args) + 1; + JL_NARGSV("macrocall", 3); // macro name, location, and module + jl_value_t **margs; + JL_GC_PUSHARGS(margs, nargs); + int i; + margs[0] = jl_array_ptr_ref(args, 0); + margs[0] = jl_toplevel_eval(*ctx, margs[0]); + // __source__ argument + jl_value_t *lno = jl_array_ptr_ref(args, 1); + margs[1] = lno; + if (jl_is_expr(lno) && ((jl_expr_t*)lno)->head == line_sym) { + jl_value_t *file = jl_nothing; + jl_value_t *line = NULL; + switch (jl_expr_nargs(lno)) { + case 2: + file = jl_exprarg(lno, 1); // file + JL_FALLTHROUGH; + case 1: + line = jl_exprarg(lno, 0); // line + JL_FALLTHROUGH; + default: ; + } + if (line == NULL) + line = jl_box_long(0); + margs[1] = jl_new_struct(jl_linenumbernode_type, line, file); + } + else if (!jl_typeis(lno, jl_linenumbernode_type)) { + margs[1] = jl_new_struct(jl_linenumbernode_type, jl_box_long(0), jl_nothing); + } + margs[2] = (jl_value_t*)inmodule; + for (i = 3; i < nargs; i++) + margs[i] = jl_array_ptr_ref(args, i - 1); + + size_t last_age = ptls->world_age; + size_t world = jl_world_counter; + ptls->world_age = world; + jl_method_instance_t *mfunc = jl_method_lookup(jl_gf_mtable(margs[0]), margs, nargs, 1, world); + if (mfunc == NULL) { + jl_method_error((jl_function_t*)margs[0], margs, nargs, world); + // unreachable + } + *ctx = mfunc->def.method->module; + jl_value_t *result = jl_call_method_internal(mfunc, margs, nargs); + ptls->world_age = last_age; + JL_GC_POP(); + return result; +} + +static jl_value_t *jl_expand_macros(jl_value_t *expr, jl_module_t *inmodule, struct macroctx_stack *macroctx, int onelevel) +{ + if (!expr || !jl_is_expr(expr)) + return expr; + jl_expr_t *e = (jl_expr_t*)expr; + if (e->head == inert_sym || + e->head == module_sym || + //e->head == toplevel_sym || // TODO: enable this once julia-expand-macroscope is fixed / removed + e->head == meta_sym) { + return expr; + } + if (e->head == quote_sym && jl_expr_nargs(e) == 1) { + expr = jl_call_scm_on_ast("julia-bq-macro", jl_exprarg(e, 0), inmodule); + JL_GC_PUSH1(&expr); + if (macroctx) { + // in a macro, `quote` also implies `escape` + jl_expr_t *e2 = jl_exprn(escape_sym, 1); + jl_array_ptr_set(e2->args, 0, expr); + expr = (jl_value_t*)e2; + } + expr = jl_expand_macros(expr, inmodule, macroctx, onelevel); + JL_GC_POP(); + return expr; + } + if (e->head == hygienicscope_sym && jl_expr_nargs(e) == 2) { + struct macroctx_stack newctx; + newctx.m = (jl_module_t*)jl_exprarg(e, 1); + JL_TYPECHK(hygienic-scope, module, (jl_value_t*)newctx.m); + newctx.parent = macroctx; + jl_value_t *a = jl_exprarg(e, 0); + jl_value_t *a2 = jl_expand_macros(a, inmodule, &newctx, onelevel); + if (a != a2) + jl_array_ptr_set(e->args, 0, a2); + return expr; + } + if (e->head == macrocall_sym) { + struct macroctx_stack newctx; + newctx.m = macroctx ? macroctx->m : inmodule; + newctx.parent = macroctx; + jl_value_t *result = jl_invoke_julia_macro(e->args, inmodule, &newctx.m); + jl_value_t *wrap = NULL; + JL_GC_PUSH3(&result, &wrap, &newctx.m); + // copy and wrap the result in `(hygienic-scope ,result ,newctx) + if (jl_is_expr(result) && ((jl_expr_t*)result)->head == escape_sym) + result = jl_exprarg(result, 0); + else + wrap = (jl_value_t*)jl_exprn(hygienicscope_sym, 2); + result = jl_copy_ast(result); + if (!onelevel) + result = jl_expand_macros(result, inmodule, wrap ? &newctx : macroctx, onelevel); + if (wrap) { + jl_exprargset(wrap, 0, result); + jl_exprargset(wrap, 1, newctx.m); + result = wrap; + } + JL_GC_POP(); + return result; + } + if (e->head == escape_sym && macroctx) { + macroctx = macroctx->parent; + } + + size_t i; + for (i = 0; i < jl_array_len(e->args); i++) { + jl_value_t *a = jl_array_ptr_ref(e->args, i); + jl_value_t *a2 = jl_expand_macros(a, inmodule, macroctx, onelevel); + if (a != a2) + jl_array_ptr_set(e->args, i, a2); + } + return expr; +} + +JL_DLLEXPORT jl_value_t *jl_macroexpand(jl_value_t *expr, jl_module_t *inmodule) +{ + JL_TIMING(LOWERING); + JL_GC_PUSH1(&expr); + expr = jl_copy_ast(expr); + expr = jl_expand_macros(expr, inmodule, NULL, 0); + expr = jl_call_scm_on_ast("julia-expand-macroscope", expr, inmodule); + JL_GC_POP(); + return expr; +} + +JL_DLLEXPORT jl_value_t *jl_macroexpand1(jl_value_t *expr, jl_module_t *inmodule) +{ + JL_TIMING(LOWERING); + JL_GC_PUSH1(&expr); + expr = jl_copy_ast(expr); + expr = jl_expand_macros(expr, inmodule, NULL, 1); + expr = jl_call_scm_on_ast("julia-expand-macroscope", expr, inmodule); + JL_GC_POP(); + return expr; +} + +JL_DLLEXPORT jl_value_t *jl_expand(jl_value_t *expr, jl_module_t *inmodule) +{ + JL_TIMING(LOWERING); + JL_GC_PUSH1(&expr); + expr = jl_copy_ast(expr); + expr = jl_expand_macros(expr, inmodule, NULL, 0); + expr = jl_call_scm_on_ast("jl-expand-to-thunk", expr, inmodule); + JL_GC_POP(); + return expr; +} + + #ifdef __cplusplus } #endif diff --git a/src/codegen.cpp b/src/codegen.cpp index f4b9e6a5fe164..d85ceccba8b08 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3811,18 +3811,14 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr) true, jl_any_type); } else if (head == copyast_sym) { - jl_value_t *arg = args[0]; - if (jl_is_quotenode(arg)) { - jl_value_t *arg1 = jl_fieldref(arg, 0); - if (!(jl_is_expr(arg1) || jl_typeis(arg1, jl_array_any_type) || jl_is_quotenode(arg1))) { - // elide call to jl_copy_ast when possible - return emit_expr(ctx, arg); - } + jl_cgval_t ast = emit_expr(ctx, args[0]); + if (ast.typ != (jl_value_t*)jl_expr_type && ast.typ != (jl_value_t*)jl_any_type) { + // elide call to jl_copy_ast when possible + return ast; } - jl_cgval_t ast = emit_expr(ctx, arg); return mark_julia_type(ctx, ctx.builder.CreateCall(prepare_call(jlcopyast_func), - maybe_decay_untracked(boxed(ctx, ast))), true, ast.typ); + maybe_decay_untracked(boxed(ctx, ast))), true, jl_expr_type); } else if (head == simdloop_sym) { llvm::annotateSimdLoop(ctx.builder.GetInsertBlock()); diff --git a/src/jlfrontend.scm b/src/jlfrontend.scm index a556310a881d0..e704f540b1500 100644 --- a/src/jlfrontend.scm +++ b/src/jlfrontend.scm @@ -65,7 +65,7 @@ ;; note: expansion of stuff inside module is delayed, so the contents obey ;; toplevel expansion order (don't expand until stuff before is evaluated). (define (expand-toplevel-expr-- e) - (let ((ex0 (julia-expand-macros e))) + (let ((ex0 (julia-expand-macroscope e))) (if (and (pair? ex0) (eq? (car ex0) 'toplevel)) ex0 (let* ((ex (julia-expand0 ex0)) @@ -219,17 +219,6 @@ (parser-wrap (lambda () (expand-toplevel-expr expr)))) -; macroexpand only -(define (jl-macroexpand expr) - (reset-gensyms) - (parser-wrap (lambda () - (julia-expand-macros expr)))) - -(define (jl-macroexpand-1 expr) - (reset-gensyms) - (parser-wrap (lambda () - (julia-expand-macros expr 1)))) - ; run whole frontend on a string. useful for testing. (define (fe str) (expand-toplevel-expr (julia-parse str))) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 2385ab724bcfe..602498fa7c3ee 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -3245,7 +3245,7 @@ f(x) = yt(x) ,@top-stmts (block ,@sp-inits (method ,name ,(cl-convert sig fname lam namemap toplevel interp) - ,(julia-expand-macros `(quote ,newlam)) + ,(julia-bq-macro newlam) ,(last e))))))) ;; local case - lift to a new type at top level (let* ((exists (get namemap name #f)) @@ -4004,4 +4004,4 @@ f(x) = yt(x) (define (julia-expand ex) (julia-expand1 (julia-expand0 - (julia-expand-macros ex)))) + julia-expand-macroscope ex))) diff --git a/src/macroexpand.scm b/src/macroexpand.scm index a7535920d24b0..6cd91ffdda26f 100644 --- a/src/macroexpand.scm +++ b/src/macroexpand.scm @@ -46,10 +46,6 @@ (call (top append_any) ,@forms))) (loop (cdr p) (cons (julia-bq-bracket (car p) d) q))))))) -(define (julia-bq-expand-hygienic x unhygienic) - (let ((expanded (julia-bq-expand x 0))) - (if unhygienic expanded `(escape ,expanded)))) - ;; hygiene ;; return the names of vars introduced by forms, instead of their transformations. @@ -297,7 +293,7 @@ (case (car e) ((ssavalue) e) ((escape) (if (null? parent-scope) - (julia-expand-macroscopes (cadr e)) + (julia-expand-macroscopes- (cadr e)) (let* ((scope (car parent-scope)) (env (car scope)) (m (cadr scope)) @@ -476,6 +472,27 @@ ;; and wrap globals in (globalref module var) for macro's home module (resolve-expansion-vars-with-new-env e '() m '() #f #t)) +(define (julia-expand-quotes e) + (cond ((not (pair? e)) e) + ((eq? (car e) 'inert) e) + ((eq? (car e) 'module) e) + ((eq? (car e) 'quote) + (julia-expand-quotes (julia-bq-macro (cadr e)))) + ((not (contains (lambda (e) (and (pair? e) (eq? (car e) 'quote))) (cdr e))) e) + (else + (cons (car e) (map julia-expand-quotes (cdr e)))))) + +(define (julia-expand-macroscopes- e) + (cond ((not (pair? e)) e) + ((eq? (car e) 'inert) e) + ((eq? (car e) 'module) e) + ((eq? (car e) 'hygienic-scope) + (let ((form (cadr e)) ;; form is the expression returned from expand-macros + (modu (caddr e))) ;; m is the macro's def module + (resolve-expansion-vars form modu))) + (else + (map julia-expand-macroscopes- e)))) + (define (rename-symbolic-labels- e relabels parent-scope) (cond ((or (not (pair? e)) (quoted? e)) e) @@ -502,48 +519,15 @@ ;; macro expander entry point -(define (julia-expand-macros e (max-depth -1)) - (julia-expand-macroscopes +;; TODO: delete this file and fold this operation into resolve-scopes +(define (julia-expand-macroscope e) + (julia-expand-macroscopes- (rename-symbolic-labels - (julia-expand-macros- '() e max-depth)))) + (julia-expand-quotes e)))) -(define (julia-expand-macros- m e max-depth) - (cond ((= max-depth 0) e) - ((not (pair? e)) e) - ((eq? (car e) 'quote) - ;; backquote is essentially a built-in unhygienic macro at the moment - (julia-expand-macros- m (julia-bq-expand-hygienic (cadr e) (null? m)) max-depth)) - ((eq? (car e) 'inert) e) - ((eq? (car e) 'macrocall) - ;; expand macro - (let ((form (apply invoke-julia-macro (if (null? m) 'false (car m)) (cdr e)))) - (if (not form) - (error (string "macro \"" (cadr e) "\" not defined"))) - (if (and (pair? form) (eq? (car form) 'error)) - (error (cadr form))) - (let* ((modu (cdr form)) ;; modu is the macro's def module - (form (car form)) ;; form is the expression returned from expand-macros - (form (julia-expand-macros- (cons modu m) form (- max-depth 1)))) - (if (and (pair? form) (eq? (car form) 'escape)) - (cadr form) ; immediately fold away (hygienic-scope (escape ...)) - `(hygienic-scope ,form ,modu))))) - ((eq? (car e) 'module) e) - ((eq? (car e) 'escape) - (let ((m (if (null? m) m (cdr m)))) - `(escape ,(julia-expand-macros- m (cadr e) max-depth)))) - (else - (map (lambda (ex) - (julia-expand-macros- m ex max-depth)) - e)))) +(define (contains-macrocall e) + (and (pair? e) + (contains (lambda (e) (and (pair? e) (eq? (car e) 'macrocall))) e))) -;; TODO: delete this file and fold this operation into resolve-scopes -(define (julia-expand-macroscopes e) - (cond ((not (pair? e)) e) - ((eq? (car e) 'inert) e) - ((eq? (car e) 'module) e) - ((eq? (car e) 'hygienic-scope) - (let ((form (cadr e)) ;; form is the expression returned from expand-macros - (modu (caddr e))) ;; m is the macro's def module - (resolve-expansion-vars form modu))) - (else - (map julia-expand-macroscopes e)))) +(define (julia-bq-macro x) + (julia-bq-expand x 0)) diff --git a/test/core.jl b/test/core.jl index dfdbfb13513f0..08fcb4584c6ed 100644 --- a/test/core.jl +++ b/test/core.jl @@ -3491,8 +3491,16 @@ macro m8846(a, b=0) a, b end @test @m8846(a) === (:a, 0) -@test @m8846(a,1) === (:a, 1) -@test_throws MethodError @eval @m8846(a,b,c) +@test @m8846(a, 1) === (:a, 1) +let nometh = try; @eval @m8846(a, b, c); false; catch ex; ex; end + __source__ = LineNumberNode(@__LINE__() - 1, Symbol(@__FILE__)) + nometh::LoadError + @test nometh.file === string(__source__.file) + @test nometh.line === __source__.line + e = nometh.error::MethodError + @test e.f === getfield(@__MODULE__, Symbol("@m8846")) + @test e.args === (__source__, @__MODULE__, :a, :b, :c) + end # a simple case of parametric dispatch with unions let foo(x::Union{T, Void}, y::Union{T, Void}) where {T} = 1 @@ -5130,8 +5138,13 @@ f_isdefined_unionvar(y, t) = (t > 0 && (x = (t == 1 ? 1 : y)); @isdefined x) @test !f_isdefined_unionvar(1, 0) f_isdefined_splat(x...) = @isdefined x @test f_isdefined_splat(1, 2, 3) -@test let err = @macroexpand @isdefined :x - isa(err, Expr) && err.head === :error && isa(err.args[1], MethodError) +let err = try; @macroexpand @isdefined :x; false; catch ex; ex; end, + __source__ = LineNumberNode(@__LINE__() - 1, Symbol(@__FILE__)) + @test err.file === string(__source__.file) + @test err.line === __source__.line + e = err.error::MethodError + @test e.f === getfield(@__MODULE__, Symbol("@isdefined")) + @test e.args === (__source__, @__MODULE__, :(:x)) end f_isdefined_cl_1(y) = (local x; for i = 1:y; x = 2; end; () -> x; @isdefined x) f_isdefined_cl_2(y) = (local x; for i = 1:y; x = 2; end; () -> @isdefined x) diff --git a/test/parse.jl b/test/parse.jl index 15b37e3d341b1..7fc93ce4bab37 100644 --- a/test/parse.jl +++ b/test/parse.jl @@ -823,10 +823,16 @@ module B15838 end @test A15838.@f() === nothing @test A15838.@f(1) === :b -let nometh = expand(@__MODULE__, :(A15838.@f(1, 2))), __source__ = LineNumberNode(@__LINE__, Symbol(@__FILE__)) - @test (nometh::Expr).head === :error - @test length(nometh.args) == 1 - e = nometh.args[1]::MethodError +let ex = :(A15838.@f(1, 2)), __source__ = LineNumberNode(@__LINE__, Symbol(@__FILE__)) + nometh = try + macroexpand(@__MODULE__, ex) + false + catch ex + ex + end::LoadError + @test nometh.file === string(__source__.file) + @test nometh.line === __source__.line + e = nometh.error::MethodError @test e.f === getfield(A15838, Symbol("@f")) @test e.args === (__source__, @__MODULE__, 1, 2) end diff --git a/test/replutil.jl b/test/replutil.jl index 37d169f1e1afb..a3d54dc5e61c0 100644 --- a/test/replutil.jl +++ b/test/replutil.jl @@ -486,7 +486,15 @@ let @test (@macroexpand @fastmath 1+2 ) == :(Base.FastMath.add_fast(1,2)) @test (@macroexpand @fastmath + ) == :(Base.FastMath.add_fast) @test (@macroexpand @fastmath min(1) ) == :(Base.FastMath.min_fast(1)) - @test (@macroexpand @doc "" f() = @x) == Expr(:error, UndefVarError(Symbol("@x"))) + let err = try; @macroexpand @doc "" f() = @x; catch ex; ex; end + file, line = @__FILE__, @__LINE__() - 1 + err = err::LoadError + @test err.file == file && err.line == line + err = err.error::LoadError + @test err.file == file && err.line == line + err = err.error::UndefVarError + @test err == UndefVarError(Symbol("@x")) + end @test (@macroexpand @seven_dollar $bar) == 7 x = 2 @test (@macroexpand @seven_dollar 1+$x) == :(1 + $(Expr(:$, :x))) From 0b08a7eeeec1a3b4a4bbe4843f985cf52dab5061 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 10 Aug 2017 14:36:04 -0400 Subject: [PATCH 292/324] add macro linenumber-node to invoke-macro error --- base/interactiveutil.jl | 15 +++++++-------- src/ast.c | 33 ++++++++++++++++++++++++++------- test/docs.jl | 13 ++++++++++++- test/enums.jl | 34 ++++++++++++++++++++++++---------- test/printf.jl | 25 +++++++++++++++++++------ test/reflection.jl | 22 ++++++++++++---------- test/simdloop.jl | 25 +++++++++++++++++++------ 7 files changed, 119 insertions(+), 48 deletions(-) diff --git a/base/interactiveutil.jl b/base/interactiveutil.jl index 748f075b4b81b..8a780d3304102 100644 --- a/base/interactiveutil.jl +++ b/base/interactiveutil.jl @@ -415,13 +415,12 @@ function gen_call_with_extracted_types(__module__, fcn, ex0) Expr(:call, typesof, map(esc, ex0.args[2:end])...)) end end - exret = Expr(:none) - is_macro = false - ex = expand(__module__, ex0) if isa(ex0, Expr) && ex0.head == :macrocall # Make @edit @time 1+2 edit the macro by using the types of the *expressions* - is_macro = true - exret = Expr(:call, fcn, esc(ex0.args[1]), Tuple{#=__source__=#LineNumberNode, #=__module__=#Module, Any[ Core.Typeof(a) for a in ex0.args[3:end] ]...}) - elseif !isa(ex, Expr) + return Expr(:call, fcn, esc(ex0.args[1]), Tuple{#=__source__=#LineNumberNode, #=__module__=#Module, Any[ Core.Typeof(a) for a in ex0.args[3:end] ]...}) + end + ex = expand(__module__, ex0) + exret = Expr(:none) + if !isa(ex, Expr) exret = Expr(:call, :error, "expression is not a function call or symbol") elseif ex.head == :call if any(e->(isa(e, Expr) && e.head==:(...)), ex0.args) && @@ -445,12 +444,12 @@ function gen_call_with_extracted_types(__module__, fcn, ex0) end end end - if (!is_macro && ex.head == :thunk) || exret.head == :none + if ex.head == :thunk || exret.head == :none exret = Expr(:call, :error, "expression is not a function call, " * "or is too complex for @$fcn to analyze; " * "break it down to simpler parts if possible") end - exret + return exret end for fname in [:which, :less, :edit, :functionloc, :code_warntype, diff --git a/src/ast.c b/src/ast.c index 847a19df05451..27378832f664b 100644 --- a/src/ast.c +++ b/src/ast.c @@ -981,7 +981,6 @@ static jl_value_t *jl_invoke_julia_macro(jl_array_t *args, jl_module_t *inmodule JL_GC_PUSHARGS(margs, nargs); int i; margs[0] = jl_array_ptr_ref(args, 0); - margs[0] = jl_toplevel_eval(*ctx, margs[0]); // __source__ argument jl_value_t *lno = jl_array_ptr_ref(args, 1); margs[1] = lno; @@ -1011,13 +1010,33 @@ static jl_value_t *jl_invoke_julia_macro(jl_array_t *args, jl_module_t *inmodule size_t last_age = ptls->world_age; size_t world = jl_world_counter; ptls->world_age = world; - jl_method_instance_t *mfunc = jl_method_lookup(jl_gf_mtable(margs[0]), margs, nargs, 1, world); - if (mfunc == NULL) { - jl_method_error((jl_function_t*)margs[0], margs, nargs, world); - // unreachable + jl_value_t *result; + JL_TRY { + margs[0] = jl_toplevel_eval(*ctx, margs[0]); + jl_method_instance_t *mfunc = jl_method_lookup(jl_gf_mtable(margs[0]), margs, nargs, 1, world); + if (mfunc == NULL) { + jl_method_error((jl_function_t*)margs[0], margs, nargs, world); + // unreachable + } + *ctx = mfunc->def.method->module; + result = jl_call_method_internal(mfunc, margs, nargs); + } + JL_CATCH { + if (jl_loaderror_type == NULL) { + jl_rethrow(); + } + else { + jl_value_t *lno = margs[1]; + jl_value_t *file = jl_fieldref(lno, 1); + if (jl_is_symbol(file)) + margs[0] = jl_cstr_to_string(jl_symbol_name((jl_sym_t*)file)); + else + margs[0] = jl_cstr_to_string(""); + margs[1] = jl_fieldref(lno, 0); // extract and allocate line number + jl_rethrow_other(jl_new_struct(jl_loaderror_type, margs[0], margs[1], + ptls->exception_in_transit)); + } } - *ctx = mfunc->def.method->module; - jl_value_t *result = jl_call_method_internal(mfunc, margs, nargs); ptls->world_age = last_age; JL_GC_POP(); return result; diff --git a/test/docs.jl b/test/docs.jl index 635aa36dc205e..6b6ab27a489e1 100644 --- a/test/docs.jl +++ b/test/docs.jl @@ -706,7 +706,18 @@ end ) # Issue #13905. -@test @macroexpand(@doc "" f() = @x) == Expr(:error, UndefVarError(Symbol("@x"))) +let err = try; @macroexpand(@doc "" f() = @x); false; catch ex; ex; end + __source__ = LineNumberNode(@__LINE__() - 1, Symbol(@__FILE__)) + err::LoadError + @test err.file === string(__source__.file) + @test err.line === __source__.line + err = err.error::LoadError + @test err.file === string(__source__.file) + @test err.line === __source__.line + err = err.error::UndefVarError + @test err.var == Symbol("@x") + end + # Undocumented DataType Summaries. diff --git a/test/enums.jl b/test/enums.jl index ea4169af7b8aa..6ded32a9a19eb 100644 --- a/test/enums.jl +++ b/test/enums.jl @@ -7,7 +7,17 @@ using Base.Test @test_throws MethodError convert(Enum, 1.0) -@test_throws ArgumentError eval(:(@enum Foo)) +macro macrocall(ex) + @assert Meta.isexpr(ex, :macrocall) + ex.head = :call + for i in 2:length(ex.args) + ex.args[i] = QuoteNode(ex.args[i]) + end + insert!(ex.args, 3, __module__) + return esc(ex) +end + +@test_throws ArgumentError("no arguments given for Enum Foo") @macrocall(@enum Foo) @enum Fruit apple orange kiwi @test typeof(Fruit) == DataType @@ -72,14 +82,18 @@ end @test Int(_neg4) === -4 @test Int(_neg3) === -3 -@test_throws ArgumentError eval(:(@enum Test1 _zerofp=0.0)) -@test_throws ArgumentError eval(:(@enum Test11 _zerofp2=0.5)) +@test_throws ArgumentError("invalid value for Enum Test1, _zerofp = 0.0=0.0; values must be integers") @macrocall(@enum Test1 _zerofp=0.0) +@test_throws ArgumentError("invalid value for Enum Test11, _zerofp2 = 0.5=0.5; values must be integers") @macrocall(@enum Test11 _zerofp2=0.5) @enum Test111 _zerobi=BigInt(1) @test Integer(_zerobi) == 1 # can't use non-identifiers as enum members -@test_throws ArgumentError eval(:(@enum Test2 x ? 1 : 2)) -@test_throws ArgumentError eval(:(@enum Test22 1=2)) +@test_throws ArgumentError("""invalid argument for Enum Test2: if x + 1 + else + 2 + end""") @macrocall(@enum Test2 x ? 1 : 2) +@test_throws ArgumentError("invalid argument for Enum Test22: 1 = 2") @macrocall(@enum Test22 1=2) # other Integer types of enum members @enum Test3::UInt8 _one_Test3=0x01 _two_Test3=0x02 _three_Test3=0x03 @@ -98,9 +112,9 @@ end @test typeof(convert(Integer, _one_Test6)) == UInt128 # enum values must be integers -@test_throws ArgumentError eval(:(@enum Test7 _zero="zero")) -@test_throws ArgumentError eval(:(@enum Test8 _zero='0')) -@test_throws ArgumentError eval(:(@enum Test9 _zero=0.5)) +@test_throws ArgumentError("invalid value for Enum Test7, _zero = \"zero\"=zero; values must be integers") @macrocall(@enum Test7 _zero="zero") +@test_throws ArgumentError("invalid value for Enum Test8, _zero = '0'=0; values must be integers") @macrocall(@enum Test8 _zero='0') +@test_throws ArgumentError("invalid value for Enum Test9, _zero = 0.5=0.5; values must be integers") @macrocall(@enum Test9 _zero=0.5) # test macro handles keyword arguments @enum(Test11, _zero_Test11=2, @@ -114,10 +128,10 @@ end @test Int(_three_Test11) == 6 # don't allow enum value to overflow -@test_throws ArgumentError @eval(@enum EnumOvf x=typemax(Int32) y) +@test_throws ArgumentError("overflow in value \"y\" of Enum EnumOvf") @macrocall(@enum EnumOvf x=typemax(Int32) y) # test for unique Enum values -@test_throws ArgumentError eval(:(@enum(Test14, _zero_Test14, _one_Test14, _two_Test14=0))) +@test_throws ArgumentError("values for Enum Test14 are not unique") @macrocall(@enum(Test14, _zero_Test14, _one_Test14, _two_Test14=0)) @test repr(apple) == "apple::$(string(Fruit)) = 0" @test string(apple) == "apple" diff --git a/test/printf.jl b/test/printf.jl index 2885ddc8df3bd..90c0e10769621 100644 --- a/test/printf.jl +++ b/test/printf.jl @@ -1,5 +1,18 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license +macro test_throws(ty, ex) + return quote + Test.@test_throws $(esc(ty)) try + $(esc(ex)) + catch err + @test err isa LoadError + @test err.file === $(string(__source__.file)) + @test err.line === $(__source__.line) + rethrow(err.error) + end + end +end + # printf # int @test (@sprintf "%d" typemax(Int64)) == "9223372036854775807" @@ -189,14 +202,14 @@ end # escape % @test (@sprintf "%%") == "%" @test (@sprintf "%%s") == "%s" -@test_throws ArgumentError eval(:(@sprintf "%")) +@test_throws ArgumentError("invalid printf format string: \"%\"") @macroexpand(@sprintf "%") #" (fixes syntax highlighting) # argument count -@test_throws ArgumentError eval(:(@sprintf "%s")) -@test_throws ArgumentError eval(:(@sprintf "%s" "1" "2")) +@test_throws ArgumentError("@sprintf: wrong number of arguments (0) should be (1)") @macroexpand(@sprintf "%s") +@test_throws ArgumentError("@sprintf: wrong number of arguments (2) should be (1)") @macroexpand(@sprintf "%s" "1" "2") # no interpolation -@test_throws ArgumentError eval(:(@sprintf "$n")) +@test_throws ArgumentError("@sprintf: format must be a plain static string (no interpolation or prefix)") @macroexpand(@sprintf "$n") # type width specifier parsing (ignored) @test (@sprintf "%llf" 1.2) == "1.200000" @@ -242,7 +255,7 @@ end # invalid format specifiers, not "diouxXDOUeEfFgGaAcCsSpn" for c in "bBhHIjJkKlLmMNPqQrRtTvVwWyYzZ" fmt_str = string("%", c) - @test_throws ArgumentError eval(:(@sprintf $fmt_str 1)) + @test_throws ArgumentError("@sprintf: first argument must be a format string") @macroexpand(@sprintf $fmt_str 1) end # combo @@ -255,7 +268,7 @@ end @test (@sprintf "%s %s %s %d %d %d %f %f %f" Any[10^x+y for x=1:3,y=1:3 ]...) == "11 101 1001 12 102 1002 13.000000 103.000000 1003.000000" # @printf -@test_throws ArgumentError eval(:(@printf 1)) +@test_throws ArgumentError("@printf: first or second argument must be a format string") @macroexpand(@printf 1) # Check bug with trailing nul printing BigFloat @test (@sprintf("%.330f", BigFloat(1)))[end] != '\0' diff --git a/test/reflection.jl b/test/reflection.jl index f7564fb0b99ae..dc0af73e23987 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -246,26 +246,27 @@ let @test TestMod7648.TestModSub9475 == @which a9475 end -@test_throws ArgumentError which(===, Tuple{Int, Int}) -@test_throws ArgumentError code_typed(===, Tuple{Int, Int}) -@test_throws ArgumentError code_llvm(===, Tuple{Int, Int}) -@test_throws ArgumentError code_native(===, Tuple{Int, Int}) -@test_throws ArgumentError Base.return_types(===, Tuple{Int, Int}) +@test_throws ArgumentError("argument is not a generic function") which(===, Tuple{Int, Int}) +@test_throws ArgumentError("argument is not a generic function") code_typed(===, Tuple{Int, Int}) +@test_throws ArgumentError("argument is not a generic function") code_llvm(===, Tuple{Int, Int}) +@test_throws ArgumentError("argument is not a generic function") code_native(===, Tuple{Int, Int}) +@test_throws ArgumentError("argument is not a generic function") Base.return_types(===, Tuple{Int, Int}) module TestingExported using Base.Test +include("testenv.jl") # for curmod_str import Base.isexported global this_is_not_defined export this_is_not_defined -@test_throws ErrorException which(:this_is_not_defined) -@test_throws ErrorException @which this_is_not_defined -@test_throws ErrorException which(:this_is_not_exported) +@test_throws ErrorException("\"this_is_not_defined\" is not defined in module Main") which(:this_is_not_defined) +@test_throws ErrorException("\"this_is_not_defined\" is not defined in module $curmod_str") @which this_is_not_defined +@test_throws ErrorException("\"this_is_not_exported\" is not defined in module Main") which(:this_is_not_exported) @test isexported(@__MODULE__, :this_is_not_defined) @test !isexported(@__MODULE__, :this_is_not_exported) const a_value = 1 @test Base.which_module(@__MODULE__, :a_value) === @__MODULE__ @test @which(a_value) === @__MODULE__ -@test_throws ErrorException which(:a_value) +@test_throws ErrorException("\"a_value\" is not defined in module Main") which(:a_value) @test which(:Core) === Main @test !isexported(@__MODULE__, :a_value) end @@ -668,7 +669,8 @@ let @test @inferred wrapperT(ReflectionExample{T, Int64} where T) == ReflectionExample @test @inferred wrapperT(ReflectionExample) == ReflectionExample @test @inferred wrapperT(Union{ReflectionExample{Union{},1},ReflectionExample{Float64,1}}) == ReflectionExample - @test_throws ErrorException Base.typename(Union{Int, Float64}) + @test_throws(ErrorException("typename does not apply to unions whose components have different typenames"), + Base.typename(Union{Int, Float64})) end # Issue #20086 diff --git a/test/simdloop.jl b/test/simdloop.jl index bbac5f1dfa82a..31b28a9eec51a 100644 --- a/test/simdloop.jl +++ b/test/simdloop.jl @@ -74,24 +74,37 @@ import Base.SimdLoop.SimdError # Test that @simd rejects inner loop body with invalid control flow statements # issue #8613 -@test_throws SimdError eval(:(begin +macro test_throws(ty, ex) + return quote + Test.@test_throws $(esc(ty)) try + $(esc(ex)) + catch err + @test err isa LoadError + @test err.file === $(string(__source__.file)) + @test err.line === $(__source__.line + 1) + rethrow(err.error) + end + end +end + +@test_throws SimdError("break is not allowed inside a @simd loop body") @macroexpand begin @simd for x = 1:10 x == 1 && break end -end)) +end -@test_throws SimdError eval(:(begin +@test_throws SimdError("continue is not allowed inside a @simd loop body") @macroexpand begin @simd for x = 1:10 x < 5 && continue end -end)) +end -@test_throws SimdError eval(:(begin +@test_throws SimdError("@goto is not allowed inside a @simd loop body") @macroexpand begin @simd for x = 1:10 x == 1 || @goto exit_loop end @label exit_loop -end)) +end # @simd with cartesian iteration function simd_cartesian_range!(indexes, crng) From fe33fa7d9e97a894631daec79a539e3f948f1432 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Thu, 7 Sep 2017 04:08:05 +0200 Subject: [PATCH 293/324] Patch the NVPTX back-end to ignore nonrelevant address spaces. --- deps/llvm.mk | 1 + deps/patches/llvm-NVPTX-addrspaces.patch | 30 ++++++++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 deps/patches/llvm-NVPTX-addrspaces.patch diff --git a/deps/llvm.mk b/deps/llvm.mk index 0b24f95abaa75..f69810ff68a20 100644 --- a/deps/llvm.mk +++ b/deps/llvm.mk @@ -440,6 +440,7 @@ $(eval $(call LLVM_PATCH,llvm-3.9.0_D27296-libssp)) $(eval $(call LLVM_PATCH,llvm-D27609-AArch64-UABS_G3)) # Remove for 4.0 $(eval $(call LLVM_PATCH,llvm-D27629-AArch64-large_model)) # patches for NVPTX +$(eval $(call LLVM_PATCH,llvm-NVPTX-addrspaces)) $(eval $(call LLVM_PATCH,llvm-D9168_argument_alignment)) # Remove for 4.0 $(eval $(call LLVM_PATCH,llvm-D23597_sdag_names)) # Dep for D24300, remove for 4.0 $(eval $(call LLVM_PATCH,llvm-D24300_ptx_intrinsics)) # Remove for 4.0 diff --git a/deps/patches/llvm-NVPTX-addrspaces.patch b/deps/patches/llvm-NVPTX-addrspaces.patch new file mode 100644 index 0000000000000..006868d61ace0 --- /dev/null +++ b/deps/patches/llvm-NVPTX-addrspaces.patch @@ -0,0 +1,30 @@ +diff -u llvm-3.9.1.orig/lib/Target/NVPTX/NVPTXISelLowering.cpp llvm-3.9.1/lib/Target/NVPTX/NVPTXISelLowering.cpp +--- llvm-3.9.1.orig/lib/Target/NVPTX/NVPTXISelLowering.cpp 2016-07-15 20:27:10.000000000 +0200 ++++ llvm-3.9.1/lib/Target/NVPTX/NVPTXISelLowering.cpp 2017-09-07 04:04:25.540721694 +0200 +@@ -888,6 +910,14 @@ + return TargetLoweringBase::getPreferredVectorAction(VT); + } + ++bool NVPTXTargetLowering::isNoopAddrSpaceCast(unsigned SrcAS, ++ unsigned DestAS) const { ++ assert(SrcAS != DestAS && "Expected different address spaces!"); ++ ++ return (SrcAS == ADDRESS_SPACE_GENERIC || SrcAS > ADDRESS_SPACE_LOCAL) && ++ (DestAS == ADDRESS_SPACE_GENERIC || DestAS > ADDRESS_SPACE_LOCAL); ++} ++ + SDValue + NVPTXTargetLowering::LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const { + SDLoc dl(Op); +diff -u llvm-3.9.1.orig/lib/Target/NVPTX/NVPTXISelLowering.h llvm-3.9.1/lib/Target/NVPTX/NVPTXISelLowering.h +--- llvm-3.9.1.orig/lib/Target/NVPTX/NVPTXISelLowering.h 2016-06-12 17:39:02.000000000 +0200 ++++ llvm-3.9.1/lib/Target/NVPTX/NVPTXISelLowering.h 2017-09-07 04:03:39.591373952 +0200 +@@ -442,6 +442,8 @@ + const NVPTXSubtarget &STI); + SDValue LowerOperation(SDValue Op, SelectionDAG &DAG) const override; + ++ bool isNoopAddrSpaceCast(unsigned SrcAS, unsigned DestAS) const override; ++ + SDValue LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const; + + const char *getTargetNodeName(unsigned Opcode) const override; From e05bd6b454028a4449bc4d0f409f263e5194f970 Mon Sep 17 00:00:00 2001 From: quinnj Date: Wed, 6 Sep 2017 20:48:29 -0600 Subject: [PATCH 294/324] Speed up isbitsunion and bitsunionsize functions --- base/array.jl | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/base/array.jl b/base/array.jl index 5a8840f133af6..97742ef79c959 100644 --- a/base/array.jl +++ b/base/array.jl @@ -114,27 +114,19 @@ asize_from(a::Array, n) = n > ndims(a) ? () : (arraysize(a,n), asize_from(a, n+1 Return whether a type is an "is-bits" Union type, meaning each type included in a Union is `isbits`. """ -function isbitsunion end - -function isbitsunion(U::Union) - for u in Base.uniontypes(U) - isbits(u) || return false - end - return true -end -isbitsunion(T) = false +isbitsunion(u::Union) = ccall(:jl_array_store_unboxed, Cint, (Any,), u) == Cint(1) +isbitsunion(x) = false """ Base.bitsunionsize(U::Union) -For a Union of `isbits` types, return the size of the largest type. +For a Union of `isbits` types, return the size of the largest type; assumes `Base.isbitsunion(U) == true` """ -function bitsunionsize(U::Union) - sz = 0 - for u in Base.uniontypes(U) - sz = max(sz, sizeof(u)) - end - return sz +function bitsunionsize(u::Union) + sz = Ref{Csize_t}(0) + algn = Ref{Csize_t}(0) + @assert ccall(:jl_islayout_inline, Cint, (Any, Ptr{Csize_t}, Ptr{Csize_t}), u, sz, algn) == Cint(1) + return sz[] end length(a::Array) = arraylen(a) From 10ed407203a826f2a3df7731eb4b476ff407391b Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Tue, 5 Sep 2017 23:10:46 +0200 Subject: [PATCH 295/324] LineEdit: add convenient default mode in `state` function --- base/repl/LineEdit.jl | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/base/repl/LineEdit.jl b/base/repl/LineEdit.jl index a2fa7e245e796..8efd54896c798 100644 --- a/base/repl/LineEdit.jl +++ b/base/repl/LineEdit.jl @@ -55,7 +55,7 @@ end MIState(i, c, a, m) = MIState(i, c, a, m, String[], 0, Char[], 0, :begin) function show(io::IO, s::MIState) - print(io, "MI State (", s.current_mode, " active)") + print(io, "MI State (", mode(s), " active)") end struct InputAreaState @@ -116,13 +116,13 @@ terminal(s::PromptState) = s.terminal for f in [:terminal, :on_enter, :add_history, :buffer, :(Base.isempty), :replace_line, :refresh_multi_line, :input_string, :update_display_buffer, :empty_undo, :push_undo, :pop_undo] - @eval ($f)(s::MIState, args...) = $(f)(s.mode_state[s.current_mode], args...) + @eval ($f)(s::MIState, args...) = $(f)(state(s), args...) end for f in [:edit_insert, :edit_insert_newline, :edit_backspace, :edit_move_left, :edit_move_right, :edit_move_word_left, :edit_move_word_right] @eval function ($f)(s::MIState, args...) - $(f)(s.mode_state[s.current_mode], args...) + $(f)(state(s), args...) return $(Expr(:quote, f)) end end @@ -172,7 +172,7 @@ end # Prompt Completions function complete_line(s::MIState) - complete_line(s.mode_state[s.current_mode], s.key_repeats) + complete_line(state(s), s.key_repeats) refresh_line(s) :complete_line end @@ -1352,8 +1352,8 @@ function refresh_multi_line(termbuf::TerminalBuffer, s::SearchState) s.ias = refresh_multi_line(termbuf, s.terminal, buf, s.ias, s.backward ? "(reverse-i-search)`" : "(forward-i-search)`") end -state(s::MIState, p) = s.mode_state[p] -state(s::PromptState, p) = (@assert s.p == p; s) +state(s::MIState, p=mode(s)) = s.mode_state[p] +state(s::PromptState, p=mode(s)) = (@assert s.p == p; s) mode(s::MIState) = s.current_mode mode(s::PromptState) = s.p mode(s::SearchState) = @assert false @@ -1791,31 +1791,32 @@ function activate(p::TextInterface, s::ModeState, termbuf, term::TextTerminal) end function activate(p::TextInterface, s::MIState, termbuf, term::TextTerminal) - @assert p == s.current_mode - activate(p, s.mode_state[s.current_mode], termbuf, term) + @assert p == mode(s) + activate(p, state(s), termbuf, term) end activate(m::ModalInterface, s::MIState, termbuf, term::TextTerminal) = - activate(s.current_mode, s, termbuf, term) + activate(mode(s), s, termbuf, term) commit_changes(t::UnixTerminal, termbuf) = write(t, take!(termbuf.out_stream)) -function transition(f::Function, s::MIState, mode) - if mode === :abort + +function transition(f::Function, s::MIState, newmode) + if newmode === :abort s.aborted = true return end - if mode === :reset + if newmode === :reset reset_state(s) return end - if !haskey(s.mode_state,mode) - s.mode_state[mode] = init_state(terminal(s), mode) + if !haskey(s.mode_state, newmode) + s.mode_state[newmode] = init_state(terminal(s), newmode) end termbuf = TerminalBuffer(IOBuffer()) t = terminal(s) - s.mode_state[s.current_mode] = deactivate(s.current_mode, s.mode_state[s.current_mode], termbuf, t) - s.current_mode = mode + s.mode_state[mode(s)] = deactivate(mode(s), state(s), termbuf, t) + s.current_mode = newmode f() - activate(mode, s.mode_state[mode], termbuf, t) + activate(newmode, state(s, newmode), termbuf, t) commit_changes(t, termbuf) end transition(s::MIState, mode) = transition((args...)->nothing, s, mode) @@ -1830,7 +1831,7 @@ function reset_state(s::PromptState) end function reset_state(s::MIState) - for (mode,state) in s.mode_state + for (mode, state) in s.mode_state reset_state(state) end end @@ -1878,7 +1879,7 @@ function run_interface(terminal, m::ModalInterface) Expr(:body, Expr(:return, Expr(:call, - QuoteNode(mode(state(s, s.current_mode)).on_done), + QuoteNode(mode(state(s)).on_done), QuoteNode(s), QuoteNode(buf), QuoteNode(ok))))) @@ -1911,8 +1912,8 @@ pop_undo(s) = nothing keymap(s::PromptState, prompt::Prompt) = prompt.keymap_dict keymap_data(s::PromptState, prompt::Prompt) = prompt.keymap_func_data -keymap(ms::MIState, m::ModalInterface) = keymap(ms.mode_state[ms.current_mode], ms.current_mode) -keymap_data(ms::MIState, m::ModalInterface) = keymap_data(ms.mode_state[ms.current_mode], ms.current_mode) +keymap(ms::MIState, m::ModalInterface) = keymap(state(ms), mode(ms)) +keymap_data(ms::MIState, m::ModalInterface) = keymap_data(state(ms), mode(ms)) function prompt!(term, prompt, s = init_state(term, prompt)) Base.reseteof(term) From 5f708e8788c09fecae955c446792f82fc10bc502 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Sun, 3 Sep 2017 23:15:38 +0200 Subject: [PATCH 296/324] REPL: enable "redo" after "undo" --- base/repl/LineEdit.jl | 55 +++++++++++++++++++--- test/lineedit.jl | 106 ++++++++++++++++++++---------------------- 2 files changed, 98 insertions(+), 63 deletions(-) diff --git a/base/repl/LineEdit.jl b/base/repl/LineEdit.jl index 8efd54896c798..1e1a8a9fcde47 100644 --- a/base/repl/LineEdit.jl +++ b/base/repl/LineEdit.jl @@ -68,6 +68,7 @@ mutable struct PromptState <: ModeState p::Prompt input_buffer::IOBuffer undo_buffers::Vector{IOBuffer} + undo_idx::Int ias::InputAreaState # indentation of lines which do not include the prompt # if negative, the width of the prompt is used @@ -1668,7 +1669,8 @@ AnyDict( # Meta Enter "\e\r" => (s,o...)->edit_insert_newline(s), "\e\n" => "\e\r", - "^_" => (s,o...)->(pop_undo(s) ? refresh_line(s) : beep(terminal(s))), + "^_" => (s,o...)->edit_undo!(s), + "\e_" => (s,o...)->edit_redo!(s), # Simply insert it into the buffer by default "*" => (s,data,c)->(edit_insert(s, c)), "^U" => (s,o...)->edit_clear(s), @@ -1856,7 +1858,7 @@ end run_interface(::Prompt) = nothing init_state(terminal, prompt::Prompt) = - PromptState(terminal, prompt, IOBuffer(), IOBuffer[], InputAreaState(1, 1), + PromptState(terminal, prompt, IOBuffer(), IOBuffer[], 1, InputAreaState(1, 1), #=indent(spaces)=# -1) function init_state(terminal, m::ModalInterface) @@ -1895,20 +1897,59 @@ position(s::Union{MIState,ModeState}) = position(buffer(s)) function empty_undo(s::PromptState) empty!(s.undo_buffers) + s.undo_idx = 1 end + empty_undo(s) = nothing -function push_undo(s::PromptState) - push!(s.undo_buffers, copy(s.input_buffer)) +function push_undo(s::PromptState, advance=true) + resize!(s.undo_buffers, s.undo_idx) + s.undo_buffers[end] = copy(s.input_buffer) + advance && (s.undo_idx += 1) end + push_undo(s) = nothing +# must be called after a push_undo function pop_undo(s::PromptState) - length(s.undo_buffers) > 0 || return false - s.input_buffer = pop!(s.undo_buffers) + pop!(s.undo_buffers) + s.undo_idx -= 1 +end + +function edit_undo!(s::MIState) + s.last_action ∉ (:edit_redo!, :edit_undo!) && push_undo(s, false) + if edit_undo!(state(s)) + :edit_undo! + else + beep(terminal(s)) + :ignore + end +end + +function edit_undo!(s::PromptState) + s.undo_idx > 1 || return false + s.input_buffer = s.undo_buffers[s.undo_idx -=1] + refresh_line(s) + true +end +edit_undo!(s) = nothing + +function edit_redo!(s::MIState) + if s.last_action ∈ (:edit_redo!, :edit_undo!) && edit_redo!(state(s)) + :edit_redo! + else + beep(terminal(s)) + :ignore + end +end + +function edit_redo!(s::PromptState) + s.undo_idx < length(s.undo_buffers) || return false + s.input_buffer = s.undo_buffers[s.undo_idx += 1] + refresh_line(s) true end -pop_undo(s) = nothing +edit_redo!(s) = nothing keymap(s::PromptState, prompt::Prompt) = prompt.keymap_dict keymap_data(s::PromptState, prompt::Prompt) = prompt.keymap_func_data diff --git a/test/lineedit.jl b/test/lineedit.jl index 194a6b4d676c8..8dc8d5aad2a53 100644 --- a/test/lineedit.jl +++ b/test/lineedit.jl @@ -22,8 +22,11 @@ charpos(buf, pos=position(buf)) = Base.unsafe_ind2chr(content(buf), pos+1)-1 function transform!(f, s, i = -1) # i is char-based (not bytes) buffer position buf = buffer(s) i >= 0 && charseek(buf, i) - f(s) - content(buf), charpos(buf), charpos(buf, getmark(buf)) + action = f(s) + if s isa LineEdit.MIState && action isa Symbol + s.last_action = action # simulate what happens in LineEdit.prompt! + end + content(s), charpos(buf), charpos(buf, getmark(buf)) end @@ -604,10 +607,11 @@ end @test s.kill_ring[end] == "a ≡ not" charseek(buf, 0) @test transform!(LineEdit.edit_yank, s) == ("a ≡ notçhing", 7, 0) + s.last_action = :unknown # next action will fail, as yank-pop doesn't know a yank was just issued @test transform!(LineEdit.edit_yank_pop, s) == ("a ≡ notçhing", 7, 0) s.last_action = :edit_yank - # not this should work: + # now this should work: @test transform!(LineEdit.edit_yank_pop, s) == ("ça ≡ nothingçhing", 12, 0) @test s.kill_idx == 1 LineEdit.edit_kill_line(s) @@ -617,88 +621,78 @@ end @testset "undo" begin s = new_state() + edit!(f) = transform!(f, s)[1] + edit_undo! = LineEdit.edit_undo! + edit_redo! = LineEdit.edit_redo! edit_insert(s, "one two three") - LineEdit.edit_delete_prev_word(s) - @test content(s) == "one two " - LineEdit.pop_undo(s) - @test content(s) == "one two three" + @test edit!(LineEdit.edit_delete_prev_word) == "one two " + @test edit!(edit_undo!) == "one two three" + @test edit!(edit_redo!) == "one two " + @test edit!(edit_undo!) == "one two three" edit_insert(s, " four") - edit_insert(s, " five") - @test content(s) == "one two three four five" - LineEdit.pop_undo(s) - @test content(s) == "one two three four" - LineEdit.pop_undo(s) - @test content(s) == "one two three" - - LineEdit.edit_clear(s) - @test content(s) == "" - LineEdit.pop_undo(s) - @test content(s) == "one two three" + @test edit!(s->edit_insert(s, " five")) == "one two three four five" + @test edit!(edit_undo!) == "one two three four" + @test edit!(edit_undo!) == "one two three" + @test edit!(edit_redo!) == "one two three four" + @test edit!(edit_redo!) == "one two three four five" + @test edit!(edit_undo!) == "one two three four" + @test edit!(edit_undo!) == "one two three" + + @test edit!(LineEdit.edit_clear) == "" + @test edit!(edit_undo!) == "one two three" LineEdit.edit_move_left(s) LineEdit.edit_move_left(s) - LineEdit.edit_transpose_chars(s) - @test content(s) == "one two there" - LineEdit.pop_undo(s) - @test content(s) == "one two three" + @test edit!(LineEdit.edit_transpose_chars) == "one two there" + @test edit!(edit_undo!) == "one two three" LineEdit.move_line_start(s) - LineEdit.edit_kill_line(s) - @test content(s) == "" - LineEdit.pop_undo(s) - @test content(s) == "one two three" + @test edit!(LineEdit.edit_kill_line) == "" + @test edit!(edit_undo!) == "one two three" LineEdit.move_line_start(s) LineEdit.edit_kill_line(s) LineEdit.edit_yank(s) - LineEdit.edit_yank(s) - @test content(s) == "one two threeone two three" - LineEdit.pop_undo(s) - @test content(s) == "one two three" - LineEdit.pop_undo(s) - @test content(s) == "" - LineEdit.pop_undo(s) - @test content(s) == "one two three" + @test edit!(LineEdit.edit_yank) == "one two threeone two three" + @test edit!(edit_undo!) == "one two three" + @test edit!(edit_undo!) == "" + @test edit!(edit_undo!) == "one two three" LineEdit.move_line_end(s) LineEdit.edit_backspace(s) LineEdit.edit_backspace(s) - LineEdit.edit_backspace(s) - @test content(s) == "one two th" - LineEdit.pop_undo(s) - @test content(s) == "one two thr" - LineEdit.pop_undo(s) - @test content(s) == "one two thre" - LineEdit.pop_undo(s) - @test content(s) == "one two three" + @test edit!(LineEdit.edit_backspace) == "one two th" + @test edit!(edit_undo!) == "one two thr" + @test edit!(edit_undo!) == "one two thre" + @test edit!(edit_undo!) == "one two three" LineEdit.push_undo(s) # TODO: incorporate push_undo into edit_splice! ? LineEdit.edit_splice!(s, 4 => 7, "stott") @test content(s) == "one stott three" - LineEdit.pop_undo(s) - @test content(s) == "one two three" + s.last_action = :not_undo + @test edit!(edit_undo!) == "one two three" LineEdit.edit_move_left(s) LineEdit.edit_move_left(s) LineEdit.edit_move_left(s) - LineEdit.edit_delete(s) - @test content(s) == "one two thee" - LineEdit.pop_undo(s) - @test content(s) == "one two three" + @test edit!(LineEdit.edit_delete) == "one two thee" + @test edit!(edit_undo!) == "one two three" LineEdit.edit_move_word_left(s) LineEdit.edit_werase(s) - LineEdit.edit_delete_next_word(s) - @test content(s) == "one " - LineEdit.pop_undo(s) - @test content(s) == "one three" - LineEdit.pop_undo(s) - @test content(s) == "one two three" + @test edit!(LineEdit.edit_delete_next_word) == "one " + @test edit!(edit_undo!) == "one three" + @test edit!(edit_undo!) == "one two three" + @test edit!(edit_redo!) == "one three" + @test edit!(edit_redo!) == "one " + @test edit!(edit_redo!) == "one " # nothing more to redo (this "beeps") + @test edit!(edit_undo!) == "one three" + @test edit!(edit_undo!) == "one two three" # pop initial insert of "one two three" - LineEdit.pop_undo(s) - @test content(s) == "" + @test edit!(edit_undo!) == "" + @test edit!(edit_undo!) == "" # nothing more to undo (this "beeps") end From 34df23e8abf91f82cec717494dcb5929d5124459 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Thu, 7 Sep 2017 10:00:57 +0200 Subject: [PATCH 297/324] REPL undo: fix edit_kill_region and add more tests --- base/repl/LineEdit.jl | 10 +++++++--- test/lineedit.jl | 30 ++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/base/repl/LineEdit.jl b/base/repl/LineEdit.jl index 1e1a8a9fcde47..95a05868bce22 100644 --- a/base/repl/LineEdit.jl +++ b/base/repl/LineEdit.jl @@ -726,10 +726,14 @@ function edit_copy_region(s::MIState) end function edit_kill_region(s::MIState) - push_kill!(s, edit_splice!(s)) || return :ignore push_undo(s) - refresh_line(s) - :edit_kill_region + if push_kill!(s, edit_splice!(s)) + refresh_line(s) + :edit_kill_region + else + pop_undo(s) + :ignore + end end function edit_transpose_chars(s::MIState) diff --git a/test/lineedit.jl b/test/lineedit.jl index 8dc8d5aad2a53..e6b50cb14e402 100644 --- a/test/lineedit.jl +++ b/test/lineedit.jl @@ -644,10 +644,15 @@ end @test edit!(LineEdit.edit_clear) == "" @test edit!(edit_undo!) == "one two three" + @test edit!(LineEdit.edit_insert_newline) == "one two three\n" + @test edit!(edit_undo!) == "one two three" + LineEdit.edit_move_left(s) LineEdit.edit_move_left(s) @test edit!(LineEdit.edit_transpose_chars) == "one two there" @test edit!(edit_undo!) == "one two three" + @test edit!(LineEdit.edit_transpose_words) == "one three two" + @test edit!(edit_undo!) == "one two three" LineEdit.move_line_start(s) @test edit!(LineEdit.edit_kill_line) == "" @@ -661,6 +666,15 @@ end @test edit!(edit_undo!) == "" @test edit!(edit_undo!) == "one two three" + LineEdit.setmark(s) + LineEdit.edit_move_word_right(s) + @test edit!(LineEdit.edit_kill_region) == " two three" + @test edit!(LineEdit.edit_yank) == "one two three" + @test edit!(LineEdit.edit_yank_pop) == "one two three two three" + @test edit!(edit_undo!) == "one two three" + @test edit!(edit_undo!) == " two three" + @test edit!(edit_undo!) == "one two three" + LineEdit.move_line_end(s) LineEdit.edit_backspace(s) LineEdit.edit_backspace(s) @@ -692,6 +706,22 @@ end @test edit!(edit_undo!) == "one three" @test edit!(edit_undo!) == "one two three" + LineEdit.move_line_start(s) + @test edit!(LineEdit.edit_upper_case) == "ONE two three" + LineEdit.move_line_start(s) + @test edit!(LineEdit.edit_lower_case) == "one two three" + @test edit!(LineEdit.edit_title_case) == "one Two three" + @test edit!(edit_undo!) == "one two three" + @test edit!(edit_undo!) == "ONE two three" + @test edit!(edit_undo!) == "one two three" + + LineEdit.move_line_end(s) + edit_insert(s, " ") + @test edit!(LineEdit.edit_tab) == "one two three " + @test edit!(edit_undo!) == "one two three " + @test edit!(edit_undo!) == "one two three" + # TODO: add tests for complete_line, which don't work directly + # pop initial insert of "one two three" @test edit!(edit_undo!) == "" @test edit!(edit_undo!) == "" # nothing more to undo (this "beeps") From b83c228a6de9a3d1a22afa5d019da770ff355427 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Thu, 7 Sep 2017 16:27:30 +0200 Subject: [PATCH 298/324] make BigInt <: Signed (#23473) --- base/float.jl | 2 +- base/gmp.jl | 8 +++++--- base/int.jl | 23 +++++++++++------------ base/intfuncs.jl | 2 +- base/rational.jl | 2 +- test/bigint.jl | 3 +++ test/parse.jl | 1 + 7 files changed, 23 insertions(+), 18 deletions(-) diff --git a/base/float.jl b/base/float.jl index 4d65e56be4ded..8be322ba4ea6f 100644 --- a/base/float.jl +++ b/base/float.jl @@ -598,7 +598,7 @@ signed integer, so that `abs(typemin(x)) == typemin(x) < 0`, in which case the r `uabs(x)` will be an unsigned integer of the same size. """ uabs(x::Integer) = abs(x) -uabs(x::Signed) = unsigned(abs(x)) +uabs(x::BitSigned) = unsigned(abs(x)) """ diff --git a/base/gmp.jl b/base/gmp.jl index 9abfc34d44610..2b2afc9fcc558 100644 --- a/base/gmp.jl +++ b/base/gmp.jl @@ -43,11 +43,11 @@ else end """ - BigInt <: Integer + BigInt <: Signed Arbitrary precision integer type. """ -mutable struct BigInt <: Integer +mutable struct BigInt <: Signed alloc::Cint size::Cint d::Ptr{Limb} @@ -313,7 +313,7 @@ rem(x::BigInt, ::Type{Bool}) = !iszero(x) & unsafe_load(x.d) % Bool # never unsa rem(x::BigInt, ::Type{T}) where T<:Union{SLimbMax,ULimbMax} = iszero(x) ? zero(T) : flipsign(unsafe_load(x.d) % T, x.size) -function rem(x::BigInt, ::Type{T}) where T<:Union{Unsigned,Signed} +function rem(x::BigInt, ::Type{T}) where T<:Union{Base.BitUnsigned,Base.BitSigned} u = zero(T) for l = 1:min(abs(x.size), cld(sizeof(T), sizeof(Limb))) u += (unsafe_load(x.d, l) % T) << ((sizeof(Limb)<<3)*(l-1)) @@ -588,6 +588,8 @@ ispos(x::BigInt) = x.size > 0 signbit(x::BigInt) = isneg(x) flipsign!(x::BigInt, y::Integer) = (signbit(y) && (x.size = -x.size); x) flipsign( x::BigInt, y::Integer) = signbit(y) ? -x : x +flipsign( x::BigInt, y::BigInt) = signbit(y) ? -x : x +# above method to resolving ambiguities with flipsign(::T, ::T) where T<:Signed string(x::BigInt) = dec(x) show(io::IO, x::BigInt) = print(io, string(x)) diff --git a/base/int.jl b/base/int.jl index df3f4823f9512..e563dcc066df8 100644 --- a/base/int.jl +++ b/base/int.jl @@ -84,7 +84,6 @@ signbit(x::Unsigned) = false flipsign(x::T, y::T) where {T<:BitSigned} = flipsign_int(x, y) flipsign(x::BitSigned, y::BitSigned) = flipsign_int(promote(x, y)...) % typeof(x) -flipsign(x::Signed, y::Signed) = convert(typeof(x), flipsign(promote(x, y)...)) flipsign(x::Signed, y::Float16) = flipsign(x, bitcast(Int16, y)) flipsign(x::Signed, y::Float32) = flipsign(x, bitcast(Int32, y)) flipsign(x::Signed, y::Float64) = flipsign(x, bitcast(Int64, y)) @@ -125,7 +124,7 @@ abs(x::Signed) = flipsign(x,x) ~(n::Integer) = -n-1 -unsigned(x::Signed) = reinterpret(typeof(convert(Unsigned, zero(x))), x) +unsigned(x::BitSigned) = reinterpret(typeof(convert(Unsigned, zero(x))), x) unsigned(x::Bool) = convert(Unsigned, x) """ @@ -157,11 +156,11 @@ signed without checking for overflow. """ signed(x) = convert(Signed, x) -div(x::Signed, y::Unsigned) = flipsign(signed(div(unsigned(abs(x)), y)), x) -div(x::Unsigned, y::Signed) = unsigned(flipsign(signed(div(x, unsigned(abs(y)))), y)) +div(x::BitSigned, y::Unsigned) = flipsign(signed(div(unsigned(abs(x)), y)), x) +div(x::Unsigned, y::BitSigned) = unsigned(flipsign(signed(div(x, unsigned(abs(y)))), y)) -rem(x::Signed, y::Unsigned) = flipsign(signed(rem(unsigned(abs(x)), y)), x) -rem(x::Unsigned, y::Signed) = rem(x, unsigned(abs(y))) +rem(x::BitSigned, y::Unsigned) = flipsign(signed(rem(unsigned(abs(x)), y)), x) +rem(x::Unsigned, y::BitSigned) = rem(x, unsigned(abs(y))) fld(x::Signed, y::Unsigned) = div(x, y) - (signbit(x) & (rem(x, y) != 0)) fld(x::Unsigned, y::Signed) = div(x, y) - (signbit(y) & (rem(x, y) != 0)) @@ -396,12 +395,12 @@ trailing_ones(x::Integer) = trailing_zeros(~x) (<=)(x::T, y::T) where {T<:BitSigned} = sle_int(x, y) (<=)(x::T, y::T) where {T<:BitUnsigned} = ule_int(x, y) -==(x::Signed, y::Unsigned) = (x >= 0) & (unsigned(x) == y) -==(x::Unsigned, y::Signed ) = (y >= 0) & (x == unsigned(y)) -<( x::Signed, y::Unsigned) = (x < 0) | (unsigned(x) < y) -<( x::Unsigned, y::Signed ) = (y >= 0) & (x < unsigned(y)) -<=(x::Signed, y::Unsigned) = (x < 0) | (unsigned(x) <= y) -<=(x::Unsigned, y::Signed ) = (y >= 0) & (x <= unsigned(y)) +==(x::BitSigned, y::BitUnsigned) = (x >= 0) & (unsigned(x) == y) +==(x::BitUnsigned, y::BitSigned ) = (y >= 0) & (x == unsigned(y)) +<( x::BitSigned, y::BitUnsigned) = (x < 0) | (unsigned(x) < y) +<( x::BitUnsigned, y::BitSigned ) = (y >= 0) & (x < unsigned(y)) +<=(x::BitSigned, y::BitUnsigned) = (x < 0) | (unsigned(x) <= y) +<=(x::BitUnsigned, y::BitSigned ) = (y >= 0) & (x <= unsigned(y)) ## integer shifts ## diff --git a/base/intfuncs.jl b/base/intfuncs.jl index e34986037b5fa..e60408dce1f33 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -424,7 +424,7 @@ function ndigits0z(x::UInt128) return n + ndigits0z(UInt64(x)) end -ndigits0z(x::Signed) = ndigits0z(unsigned(abs(x))) +ndigits0z(x::BitSigned) = ndigits0z(unsigned(abs(x))) ndigits0z(x::Integer) = ndigits0zpb(x, 10) diff --git a/base/rational.jl b/base/rational.jl index a7bfdd64d7de9..a23c38dc2a59c 100644 --- a/base/rational.jl +++ b/base/rational.jl @@ -231,7 +231,7 @@ typemax(::Type{Rational{T}}) where {T<:Integer} = one(T)//zero(T) isinteger(x::Rational) = x.den == 1 -(x::Rational) = (-x.num) // x.den -function -(x::Rational{T}) where T<:Signed +function -(x::Rational{T}) where T<:BitSigned x.num == typemin(T) && throw(OverflowError("rational numerator is typemin(T)")) (-x.num) // x.den end diff --git a/test/bigint.jl b/test/bigint.jl index 71a2fd16e8cec..6dadc6556cece 100644 --- a/test/bigint.jl +++ b/test/bigint.jl @@ -371,3 +371,6 @@ end @test typeof(tan(a)) == BigFloat @test typeof(cos(a)) == BigFloat @test typeof(sin(a)) == BigFloat + +@test BigInt <: Signed +@test big(1) isa Signed diff --git a/test/parse.jl b/test/parse.jl index 15b37e3d341b1..a2a623c50d123 100644 --- a/test/parse.jl +++ b/test/parse.jl @@ -201,6 +201,7 @@ macro test999_str(args...); args; end # Issue 20587 for T in vcat(subtypes(Signed), subtypes(Unsigned)) + T === BigInt && continue # TODO: make BigInt pass this test for s in ["", " ", " "] # Without a base (handles things like "0x00001111", etc) result = @test_throws ArgumentError parse(T, s) From 8052341fa12e76f57d6c6a0fa223182842611323 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Wed, 6 Sep 2017 16:28:29 -0400 Subject: [PATCH 299/324] remove uses of `importall` in Base --- base/deprecated.jl | 22 +++++++++++++++--- base/pkg/entry.jl | 3 +-- base/pkg/write.jl | 4 ++-- base/sharedarray.jl | 4 ++-- base/sparse/cholmod.jl | 5 +++-- base/sparse/umfpack.jl | 2 +- base/strings/strings.jl | 2 +- base/sysimg.jl | 49 ++++++++++++++++++++++------------------- 8 files changed, 55 insertions(+), 36 deletions(-) diff --git a/base/deprecated.jl b/base/deprecated.jl index 26ed3243c4ee0..d1a3d05b7820e 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -225,6 +225,7 @@ for f in (:sin, :sinh, :sind, :asin, :asinh, :asind, :cot, :coth, :cotd, :acot, :acotd, :sec, :sech, :secd, :asech, :csc, :csch, :cscd, :acsch) + @eval import .Math: $f @eval @deprecate $f(A::SparseMatrixCSC) $f.(A) end @@ -259,6 +260,7 @@ for f in ( # base/complex.jl :cis, ) + @eval import .Math: $f @eval @dep_vectorize_1arg Number $f end # base/fastmath.jl @@ -267,13 +269,15 @@ for f in ( :acos_fast, :acosh_fast, :angle_fast, :asin_fast, :asinh_fast, :cosh_fast, :exp10_fast, :exp2_fast, :exp_fast, :expm1_fast, :lgamma_fast, :log10_fast, :log1p_fast, :log2_fast, :log_fast, :sin_fast, :sinh_fast, :sqrt_fast, :tan_fast, :tanh_fast ) - @eval FastMath Base.@dep_vectorize_1arg Number $f + @eval import .FastMath: $f + @eval @dep_vectorize_1arg Number $f end for f in ( :trunc, :floor, :ceil, :round, # base/floatfuncs.jl :rad2deg, :deg2rad, :exponent, :significand, # base/math.jl :sind, :cosd, :tand, :asind, :acosd, :atand, :asecd, :acscd, :acotd, # base/special/trig.jl ) + @eval import .Math: $f @eval @dep_vectorize_1arg Real $f end # base/complex.jl @@ -312,11 +316,13 @@ for f in ( # base/math.jl :log, :hypot, :atan2, ) + @eval import .Math: $f @eval @dep_vectorize_2arg Number $f end # base/fastmath.jl for f in (:pow_fast, :atan2_fast, :hypot_fast, :max_fast, :min_fast, :minmax_fast) - @eval FastMath Base.@dep_vectorize_2arg Number $f + @eval import .FastMath: $f + @eval @dep_vectorize_2arg Number $f end for f in ( :max, :min, # base/math.jl @@ -737,6 +743,7 @@ end for f in (:sec, :sech, :secd, :asec, :asech, :csc, :csch, :cscd, :acsc, :acsch, :cot, :coth, :cotd, :acot, :acoth) + @eval import .Math: $f @eval @deprecate $f(A::AbstractArray{<:Number}) $f.(A) end @@ -746,6 +753,7 @@ end @deprecate complex(A::AbstractArray, B::AbstractArray) complex.(A, B) # Deprecate manually vectorized clamp methods in favor of compact broadcast syntax +import .Math: clamp @deprecate clamp(A::AbstractArray, lo, hi) clamp.(A, lo, hi) # Deprecate manually vectorized round methods in favor of compact broadcast syntax @@ -845,7 +853,7 @@ end @deprecate ~(A::AbstractArray) .~A @deprecate ~(B::BitArray) .~B -function frexp(A::Array{<:AbstractFloat}) +function Math.frexp(A::Array{<:AbstractFloat}) depwarn(string("`frexp(x::Array)` is discontinued. Though not a direct replacement, ", "consider using dot-syntax to `broadcast` scalar `frexp` over `Array`s ", "instead, for example `frexp.(rand(4))`."), :frexp) @@ -1235,6 +1243,7 @@ end for name in ("alnum", "alpha", "cntrl", "digit", "number", "graph", "lower", "print", "punct", "space", "upper", "xdigit") f = Symbol("is",name) + @eval import .UTF8proc: $f @eval @deprecate ($f)(s::AbstractString) all($f, s) end @@ -1296,9 +1305,11 @@ next(p::Union{Process, ProcessChain}, i::Int) = (getindex(p, i), i + 1) return i == 1 ? getfield(p, p.openstream) : p end +import .LinAlg: cond @deprecate cond(F::LinAlg.LU, p::Integer) cond(full(F), p) # PR #21359 +import .Random: srand @deprecate srand(r::MersenneTwister, filename::AbstractString, n::Integer=4) srand(r, read!(filename, Array{UInt32}(Int(n)))) @deprecate srand(filename::AbstractString, n::Integer=4) srand(read!(filename, Array{UInt32}(Int(n)))) @deprecate MersenneTwister(filename::AbstractString) srand(MersenneTwister(0), read!(filename, Array{UInt32}(Int(4)))) @@ -1308,6 +1319,7 @@ end @deprecate versioninfo(io::IO, verbose::Bool) versioninfo(io, verbose=verbose) # PR #22188 +import .LinAlg: cholfact, cholfact! @deprecate cholfact!(A::StridedMatrix, uplo::Symbol, ::Type{Val{false}}) cholfact!(Hermitian(A, uplo), Val(false)) @deprecate cholfact!(A::StridedMatrix, uplo::Symbol) cholfact!(Hermitian(A, uplo)) @deprecate cholfact(A::StridedMatrix, uplo::Symbol, ::Type{Val{false}}) cholfact(Hermitian(A, uplo), Val(false)) @@ -1316,6 +1328,7 @@ end @deprecate cholfact(A::StridedMatrix, uplo::Symbol, ::Type{Val{true}}; tol = 0.0) cholfact(Hermitian(A, uplo), Val(true), tol = tol) # PR #22245 +import .LinAlg: isposdef, isposdef! @deprecate isposdef(A::AbstractMatrix, UL::Symbol) isposdef(Hermitian(A, UL)) @deprecate isposdef!(A::StridedMatrix, UL::Symbol) isposdef!(Hermitian(A, UL)) @@ -1425,6 +1438,7 @@ export conv, conv2, deconv, filt, filt!, xcorr @deprecate cov(X::AbstractVecOrMat, Y::AbstractVecOrMat, vardim::Int, corrected::Bool) cov(X, Y, vardim, corrected=corrected) # bkfact +import .LinAlg: bkfact, bkfact! function bkfact(A::StridedMatrix, uplo::Symbol, symmetric::Bool = issymmetric(A), rook::Bool = false) depwarn("bkfact with uplo and symmetric arguments deprecated. Please use bkfact($(symmetric ? "Symmetric(" : "Hermitian(")A, :$uplo))", :bkfact) @@ -1458,6 +1472,7 @@ end @deprecate literal_pow(a, b, ::Type{Val{N}}) where {N} literal_pow(a, b, Val(N)) false @eval IteratorsMD @deprecate split(t, V::Type{Val{n}}) where {n} split(t, Val(n)) false @deprecate sqrtm(A::UpperTriangular{T},::Type{Val{realmatrix}}) where {T,realmatrix} sqrtm(A, Val(realmatrix)) +import .LinAlg: lufact, lufact!, qrfact, qrfact!, cholfact, cholfact! @deprecate lufact(A::AbstractMatrix, ::Type{Val{false}}) lufact(A, Val(false)) @deprecate lufact(A::AbstractMatrix, ::Type{Val{true}}) lufact(A, Val(true)) @deprecate lufact!(A::AbstractMatrix, ::Type{Val{false}}) lufact!(A, Val(false)) @@ -1688,6 +1703,7 @@ export hex2num @deprecate cfunction(f, r, a::Tuple) cfunction(f, r, Tuple{a...}) # PR 23341 +import .LinAlg: diagm @deprecate diagm(A::SparseMatrixCSC) spdiagm(sparsevec(A)) # PR #23373 diff --git a/base/pkg/entry.jl b/base/pkg/entry.jl index 6dc47473cd17b..b480645e41f64 100644 --- a/base/pkg/entry.jl +++ b/base/pkg/entry.jl @@ -4,8 +4,7 @@ module Entry import Base: thispatch, nextpatch, nextminor, nextmajor, check_new_version import ..Reqs, ..Read, ..Query, ..Resolve, ..Cache, ..Write, ..Dir -import ...LibGit2 -importall ...LibGit2 +using ...LibGit2 import ...Pkg.PkgError using ..Types diff --git a/base/pkg/write.jl b/base/pkg/write.jl index ffc2c6f84fb50..bf5078a05c0ea 100644 --- a/base/pkg/write.jl +++ b/base/pkg/write.jl @@ -2,8 +2,8 @@ module Write -import ...LibGit2, ..Cache, ..Read, ...Pkg.PkgError -importall ...LibGit2 +import ..Cache, ..Read, ...Pkg.PkgError +using ...LibGit2 function prefetch(pkg::AbstractString, sha1::AbstractString) isempty(Cache.prefetch(pkg, Read.url(pkg), sha1)) && return diff --git a/base/sharedarray.jl b/base/sharedarray.jl index 479eee1bcc898..3cdeb3bcd88a1 100644 --- a/base/sharedarray.jl +++ b/base/sharedarray.jl @@ -1,7 +1,7 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license import .Serializer: serialize_cycle_header, serialize_type, writetag, UNDEFREF_TAG -import .Distributed: RRID +import .Distributed: RRID, procs mutable struct SharedArray{T,N} <: DenseArray{T,N} id::RRID @@ -499,7 +499,7 @@ function rand!(S::SharedArray{T}) where T return S end -function randn!(S::SharedArray) +function Random.randn!(S::SharedArray) f = S->map!(x -> randn(), S.loc_subarr_1d, S.loc_subarr_1d) @sync for p in procs(S) @async remotecall_wait(f, p, S) diff --git a/base/sparse/cholmod.jl b/base/sparse/cholmod.jl index 062144d3b2be7..4d5e7188eb5ae 100644 --- a/base/sparse/cholmod.jl +++ b/base/sparse/cholmod.jl @@ -9,14 +9,15 @@ import Base.LinAlg: (\), A_mul_Bc, A_mul_Bt, Ac_ldiv_B, Ac_mul_B, At_ldiv_B, At_ cholfact, cholfact!, det, diag, ishermitian, isposdef, issuccess, issymmetric, ldltfact, ldltfact!, logdet -importall ..SparseArrays +using ..SparseArrays export Dense, Factor, Sparse -import ..SparseArrays: AbstractSparseMatrix, SparseMatrixCSC, increment, indtype +import ..SparseArrays: AbstractSparseMatrix, SparseMatrixCSC, increment, indtype, sparse, speye, + spzeros, nnz ######### # Setup # diff --git a/base/sparse/umfpack.jl b/base/sparse/umfpack.jl index 61cc9bf79baa5..7f7177c55c37c 100644 --- a/base/sparse/umfpack.jl +++ b/base/sparse/umfpack.jl @@ -7,7 +7,7 @@ export UmfpackLU import Base: (\), Ac_ldiv_B, At_ldiv_B, findnz, getindex, show, size import Base.LinAlg: A_ldiv_B!, Ac_ldiv_B!, At_ldiv_B!, Factorization, det, lufact -importall ..SparseArrays +using ..SparseArrays import ..SparseArrays: increment, increment!, decrement, decrement!, nnz include("umfpack_h.jl") diff --git a/base/strings/strings.jl b/base/strings/strings.jl index bc1f302b7a6ad..0acad00f79ca7 100644 --- a/base/strings/strings.jl +++ b/base/strings/strings.jl @@ -7,4 +7,4 @@ include("strings/search.jl") include("strings/util.jl") include("strings/io.jl") include("strings/utf8proc.jl") -importall .UTF8proc +using .UTF8proc diff --git a/base/sysimg.jl b/base/sysimg.jl index 676fb70a6ee53..6c6a3213ba60c 100644 --- a/base/sysimg.jl +++ b/base/sysimg.jl @@ -95,7 +95,7 @@ include("operators.jl") include("pointer.jl") include("refpointer.jl") include("checked.jl") -importall .Checked +using .Checked # buggy handling of ispure in type-inference means this should be # after re-defining the basic operations that they might try to call @@ -132,7 +132,7 @@ Matrix(m::Integer, n::Integer) = Matrix{Any}(Int(m), Int(n)) # numeric operations include("hashing.jl") include("rounding.jl") -importall .Rounding +using .Rounding include("float.jl") include("twiceprecision.jl") include("complex.jl") @@ -153,7 +153,7 @@ include("strings/string.jl") # SIMD loops include("simdloop.jl") -importall .SimdLoop +using .SimdLoop # map-reduce operators include("reduce.jl") @@ -219,7 +219,7 @@ using .PermutedDimsArrays include("nullable.jl") include("broadcast.jl") -importall .Broadcast +using .Broadcast # define the real ntuple functions @generated function ntuple(f::F, ::Val{N}) where {F,N} @@ -242,7 +242,7 @@ end # base64 conversions (need broadcast) include("base64.jl") -importall .Base64 +using .Base64 # version include("version.jl") @@ -267,10 +267,10 @@ include("weakkeydict.jl") include("stream.jl") include("socket.jl") include("filesystem.jl") -importall .Filesystem +using .Filesystem include("process.jl") include("multimedia.jl") -importall .Multimedia +using .Multimedia include("grisu/grisu.jl") import .Grisu.print_shortest include("methodshow.jl") @@ -278,7 +278,8 @@ include("methodshow.jl") # core math functions include("floatfuncs.jl") include("math.jl") -importall .Math +using .Math +import .Math: gamma const (√)=sqrt const (∛)=cbrt @@ -289,21 +290,21 @@ include("reducedim.jl") # macros in this file relies on string.jl # basic data structures include("ordering.jl") -importall .Order +using .Order # Combinatorics include("sort.jl") -importall .Sort +using .Sort # Fast math include("fastmath.jl") -importall .FastMath +using .FastMath function deepcopy_internal end # BigInts and BigFloats include("gmp.jl") -importall .GMP +using .GMP for T in [Signed, Integer, BigInt, Float32, Float64, Real, Complex, Rational] @eval flipsign(x::$T, ::Unsigned) = +x @@ -311,7 +312,7 @@ for T in [Signed, Integer, BigInt, Float32, Float64, Real, Complex, Rational] end include("mpfr.jl") -importall .MPFR +using .MPFR big(n::Integer) = convert(BigInt,n) big(x::AbstractFloat) = convert(BigFloat,x) big(q::Rational) = big(numerator(q))//big(denominator(q)) @@ -329,22 +330,24 @@ using .MathConstants: ℯ, π, pi # random number generation include("random/dSFMT.jl") include("random/random.jl") -importall .Random +using .Random +import .Random: rand, rand! # (s)printf macros include("printf.jl") -importall .Printf +using .Printf # metaprogramming include("meta.jl") # enums include("Enums.jl") -importall .Enums +using .Enums # concurrency and parallelism include("serialize.jl") -importall .Serializer +using .Serializer +import .Serializer: serialize, deserialize include("channels.jl") # memory-mapped and shared arrays @@ -353,7 +356,7 @@ import .Mmap # utilities - timing, help, edit include("datafmt.jl") -importall .DataFmt +using .DataFmt include("deepcopy.jl") include("interactiveutil.jl") include("summarysize.jl") @@ -372,14 +375,14 @@ include("client.jl") # Stack frames and traces include("stacktraces.jl") -importall .StackTraces +using .StackTraces # misc useful functions & macros include("util.jl") # dense linear algebra include("linalg/linalg.jl") -importall .LinAlg +using .LinAlg const ⋅ = dot const × = cross @@ -394,7 +397,7 @@ include("pkg/pkg.jl") # profiler include("profile.jl") -importall .Profile +using .Profile # dates include("dates/Dates.jl") @@ -402,12 +405,12 @@ import .Dates: Date, DateTime, DateFormat, @dateformat_str, now # sparse matrices, vectors, and sparse linear algebra include("sparse/sparse.jl") -importall .SparseArrays +using .SparseArrays include("asyncmap.jl") include("distributed/Distributed.jl") -importall .Distributed +using .Distributed include("sharedarray.jl") # code loading From caad8b2208ec046bae4e32c1b4b877093c5e1bcf Mon Sep 17 00:00:00 2001 From: Alex Arslan Date: Thu, 7 Sep 2017 10:42:01 -0700 Subject: [PATCH 300/324] Add a README note referencing FreeBSD Pkg segfaults (#23613) --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 149343b24d92d..c5c41ae8179c0 100644 --- a/README.md +++ b/README.md @@ -256,6 +256,9 @@ Some known issues on FreeBSD are: * The x86 architecture does not support threading due to lack of compiler runtime library support, so you may need to set `JULIA_THREADS=0` in your `Make.user` if you're on a 32-bit system. +* The `Pkg` test suite segfaults on FreeBSD 11.1, likely due to a change in FreeBSD's default handling of stack guarding. + See [issue #23328](https://github.com/JuliaLang/julia/issues/23328) for more information. + ### Windows In order to build Julia on Windows, see [README.windows](https://github.com/JuliaLang/julia/blob/master/README.windows.md). From cadbe233a67674d5fa406c930a5aad3a5bcc6765 Mon Sep 17 00:00:00 2001 From: Yichao Yu Date: Thu, 7 Sep 2017 13:43:47 -0400 Subject: [PATCH 301/324] Fix invalid `ptrtoint` on non-integral pointers This broke LLVM 4.0+. --- src/llvm-late-gc-lowering.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/llvm-late-gc-lowering.cpp b/src/llvm-late-gc-lowering.cpp index 6c62c73809bc6..860985beab2f0 100644 --- a/src/llvm-late-gc-lowering.cpp +++ b/src/llvm-late-gc-lowering.cpp @@ -315,6 +315,7 @@ struct LateLowerGCFrame: public FunctionPass { Type *T_int8; Type *T_int32; Type *T_pint8; + Type *T_pjlvalue; Type *T_pjlvalue_der; Type *T_ppjlvalue_der; MDNode *tbaa_gcframe; @@ -1156,7 +1157,9 @@ bool LateLowerGCFrame::CleanupIR(Function &F) { (gc_use_func != nullptr && callee == gc_use_func)) { /* No replacement */ } else if (pointer_from_objref_func != nullptr && callee == pointer_from_objref_func) { - auto *ptr = new PtrToIntInst(CI->getOperand(0), CI->getType(), "", CI); + auto *obj = CI->getOperand(0); + auto *ASCI = new AddrSpaceCastInst(obj, T_pjlvalue, "", CI); + auto *ptr = new PtrToIntInst(ASCI, CI->getType(), "", CI); ptr->takeName(CI); CI->replaceAllUsesWith(ptr); } else if (alloc_obj_func && callee == alloc_obj_func) { @@ -1447,7 +1450,7 @@ bool LateLowerGCFrame::doInitialization(Module &M) { addRetNoAlias(big_alloc_func); } auto T_jlvalue = cast(T_prjlvalue)->getElementType(); - auto T_pjlvalue = PointerType::get(T_jlvalue, 0); + T_pjlvalue = PointerType::get(T_jlvalue, 0); T_ppjlvalue = PointerType::get(T_pjlvalue, 0); T_pjlvalue_der = PointerType::get(T_jlvalue, AddressSpace::Derived); T_ppjlvalue_der = PointerType::get(T_prjlvalue, AddressSpace::Derived); @@ -1455,7 +1458,7 @@ bool LateLowerGCFrame::doInitialization(Module &M) { else if (ptls_getter) { auto functype = ptls_getter->getFunctionType(); T_ppjlvalue = cast(functype->getReturnType())->getElementType(); - auto T_pjlvalue = cast(T_ppjlvalue)->getElementType(); + T_pjlvalue = cast(T_ppjlvalue)->getElementType(); auto T_jlvalue = cast(T_pjlvalue)->getElementType(); T_prjlvalue = PointerType::get(T_jlvalue, AddressSpace::Tracked); T_pjlvalue_der = PointerType::get(T_jlvalue, AddressSpace::Derived); @@ -1464,6 +1467,7 @@ bool LateLowerGCFrame::doInitialization(Module &M) { else { T_ppjlvalue = nullptr; T_prjlvalue = nullptr; + T_pjlvalue = nullptr; T_pjlvalue_der = nullptr; T_ppjlvalue_der = nullptr; } From 523cdcf5521556011bd1f3db4046e7f2a68438c7 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Thu, 7 Sep 2017 20:32:29 +0200 Subject: [PATCH 302/324] Make sure we have all required function prototypes before lowering GC intrinsics. According to the LLVM docs this should be done in doInitialization, but eg. in the case of stripDeadPrototypes (module pass) -> LowerGCFrame (function pass) the pass manager initializes all module passes, including the function pass manager which in turn initializes all function passes, before running any other pass. This may result in cached prototypes pointing to invalid functions. Alternatively, we could populate llvm.used with the prototypes and clean it afterwards. --- src/llvm-late-gc-lowering.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/llvm-late-gc-lowering.cpp b/src/llvm-late-gc-lowering.cpp index 4e15b2955f4f5..7756424534b55 100644 --- a/src/llvm-late-gc-lowering.cpp +++ b/src/llvm-late-gc-lowering.cpp @@ -349,7 +349,7 @@ struct LateLowerGCFrame: public FunctionPass { void PlaceGCFrameStore(State &S, unsigned R, unsigned MinColorRoot, const std::vector &Colors, Value *GCFrame, Instruction *InsertionPoint); void PlaceGCFrameStores(Function &F, State &S, unsigned MinColorRoot, const std::vector &Colors, Value *GCFrame); void PlaceRootsAndUpdateCalls(Function &F, std::vector &Colors, State &S, std::map>); - bool doInitialization(Module &M) override; + bool DefineFunctions(Module &M); bool runOnFunction(Function &F) override; Instruction *get_pgcstack(Instruction *ptlsStates); bool CleanupIR(Function &F); @@ -1418,7 +1418,7 @@ static void addRetNoAlias(Function *F) #endif } -bool LateLowerGCFrame::doInitialization(Module &M) { +bool LateLowerGCFrame::DefineFunctions(Module &M) { ptls_getter = M.getFunction("jl_get_ptls_states"); gc_flush_func = M.getFunction("julia.gcroot_flush"); gc_use_func = M.getFunction("julia.gc_use"); @@ -1452,6 +1452,7 @@ bool LateLowerGCFrame::doInitialization(Module &M) { T_ppjlvalue = PointerType::get(T_pjlvalue, 0); T_pjlvalue_der = PointerType::get(T_jlvalue, AddressSpace::Derived); T_ppjlvalue_der = PointerType::get(T_prjlvalue, AddressSpace::Derived); + return true; } else if (ptls_getter) { auto functype = ptls_getter->getFunctionType(); @@ -1473,6 +1474,7 @@ bool LateLowerGCFrame::doInitialization(Module &M) { bool LateLowerGCFrame::runOnFunction(Function &F) { DEBUG(dbgs() << "GC ROOT PLACEMENT: Processing function " << F.getName() << "\n"); + DefineFunctions(*F.getParent()); if (!ptls_getter) return CleanupIR(F); ptlsStates = nullptr; From 26014f9f49e8b6201f72d399b4db495103385c13 Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Wed, 22 Feb 2017 19:33:46 -0600 Subject: [PATCH 303/324] Remove authentication failure protection counter State between multiple credential_callback calls is now being maintained within the `CredentialPayload`. This change allows credential types to just contain credential information. --- base/libgit2/callbacks.jl | 83 +++++++++++++++++++-------------------- base/libgit2/types.jl | 25 +++--------- base/pkg/entry.jl | 1 - test/libgit2-helpers.jl | 2 +- test/libgit2.jl | 20 ++++------ 5 files changed, 55 insertions(+), 76 deletions(-) diff --git a/base/libgit2/callbacks.jl b/base/libgit2/callbacks.jl index 1b288b8edd4f1..2994c97a93be0 100644 --- a/base/libgit2/callbacks.jl +++ b/base/libgit2/callbacks.jl @@ -50,7 +50,11 @@ end function authenticate_ssh(libgit2credptr::Ptr{Ptr{Void}}, p::CredentialPayload, username_ptr) creds = Base.get(p.credential)::SSHCredentials - isusedcreds = checkused!(creds) + + # Reset password on sucessive calls + if !p.first_pass + creds.pass = "" + end # Note: The same SSHCredentials can be used to authenticate separate requests using the # same credential cache. e.g. using Pkg.update when there are two private packages. @@ -74,15 +78,10 @@ function authenticate_ssh(libgit2credptr::Ptr{Ptr{Void}}, p::CredentialPayload, # if username is not provided or empty, then prompt for it username = username_ptr != Cstring(C_NULL) ? unsafe_string(username_ptr) : "" if isempty(username) - uname = creds.user # check if credentials were already used prompt_url = git_url(scheme=p.scheme, host=p.host) - if !isusedcreds - username = uname - else - response = Base.prompt("Username for '$prompt_url'", default=uname) - isnull(response) && return user_abort() - username = unsafe_get(response) - end + response = Base.prompt("Username for '$prompt_url'", default=creds.user) + isnull(response) && return user_abort() + username = unsafe_get(response) end prompt_url = git_url(scheme=p.scheme, host=p.host, username=username) @@ -92,7 +91,7 @@ function authenticate_ssh(libgit2credptr::Ptr{Ptr{Void}}, p::CredentialPayload, ENV["SSH_KEY_PATH"] else keydefpath = creds.prvkey # check if credentials were already used - if isempty(keydefpath) || isusedcreds + if isempty(keydefpath) defaultkeydefpath = joinpath(homedir(),".ssh","id_rsa") if isempty(keydefpath) && isfile(defaultkeydefpath) keydefpath = defaultkeydefpath @@ -117,7 +116,7 @@ function authenticate_ssh(libgit2credptr::Ptr{Ptr{Void}}, p::CredentialPayload, ENV["SSH_PUB_KEY_PATH"] else keydefpath = creds.pubkey # check if credentials were already used - if isempty(keydefpath) || isusedcreds + if isempty(keydefpath) if isempty(keydefpath) keydefpath = privatekey*".pub" end @@ -135,7 +134,7 @@ function authenticate_ssh(libgit2credptr::Ptr{Ptr{Void}}, p::CredentialPayload, ENV["SSH_KEY_PASS"] else passdef = creds.pass # check if credentials were already used - if (isempty(passdef) || isusedcreds) && is_passphrase_required(privatekey) + if isempty(passdef) && is_passphrase_required(privatekey) if Sys.iswindows() response = Base.winprompt( "Your SSH Key requires a password, please enter it now:", @@ -151,15 +150,13 @@ function authenticate_ssh(libgit2credptr::Ptr{Ptr{Void}}, p::CredentialPayload, end passdef end - ((creds.user != username) || (creds.pass != passphrase) || - (creds.prvkey != privatekey) || (creds.pubkey != publickey)) && reset!(creds) creds.user = username # save credentials creds.prvkey = privatekey # save credentials creds.pubkey = publickey # save credentials creds.pass = passphrase - else - isusedcreds && return Cint(Error.EAUTH) + elseif !p.first_pass + return Cint(Error.EAUTH) end return ccall((:git_cred_ssh_key_new, :libgit2), Cint, @@ -169,37 +166,39 @@ end function authenticate_userpass(libgit2credptr::Ptr{Ptr{Void}}, p::CredentialPayload) creds = Base.get(p.credential)::UserPasswordCredentials - isusedcreds = checkused!(creds) + + # Reset password on sucessive calls + if !p.first_pass + creds.pass = "" + end if creds.prompt_if_incorrect username = creds.user userpass = creds.pass - prompt_url = git_url(scheme=p.scheme, host=p.host) - if Sys.iswindows() - if isempty(username) || isempty(userpass) || isusedcreds + if isempty(username) || isempty(userpass) + prompt_url = git_url(scheme=p.scheme, host=p.host) + if Sys.iswindows() response = Base.winprompt("Please enter your credentials for '$prompt_url'", "Credentials required", isempty(username) ? p.username : username; prompt_username = true) isnull(response) && return user_abort() username, userpass = unsafe_get(response) - end - elseif isusedcreds - response = Base.prompt("Username for '$prompt_url'", - default=isempty(username) ? p.username : username) - isnull(response) && return user_abort() - username = unsafe_get(response) + else + response = Base.prompt("Username for '$prompt_url'", + default=isempty(username) ? p.username : username) + isnull(response) && return user_abort() + username = unsafe_get(response) - prompt_url = git_url(scheme=p.scheme, host=p.host, username=username) - response = Base.prompt("Password for '$prompt_url'", password=true) - isnull(response) && return user_abort() - userpass = unsafe_get(response) - isempty(userpass) && return user_abort() # Ambiguous if EOF or newline + prompt_url = git_url(scheme=p.scheme, host=p.host, username=username) + response = Base.prompt("Password for '$prompt_url'", password=true) + isnull(response) && return user_abort() + userpass = unsafe_get(response) + isempty(userpass) && return user_abort() # Ambiguous if EOF or newline + end end - - ((creds.user != username) || (creds.pass != userpass)) && reset!(creds) creds.user = username # save credentials creds.pass = userpass # save credentials - else - isusedcreds && return Cint(Error.EAUTH) + elseif !p.first_pass + return Cint(Error.EAUTH) end return ccall((:git_cred_userpass_plaintext_new, :libgit2), Cint, @@ -228,11 +227,7 @@ Credentials are checked in the following order (if supported): **Note**: Due to the specifics of the `libgit2` authentication procedure, when authentication fails, this function is called again without any indication whether authentication was successful or not. To avoid an infinite loop from repeatedly -using the same faulty credentials, the `checkused!` function can be called. This -function returns `true` if the credentials were used. -Using credentials triggers a user prompt for (re)entering required information. -`UserPasswordCredentials` and `CachedCredentials` are implemented using a call -counting strategy that prevents repeated usage of faulty credentials. +using the same faulty credentials, we will keep track of state using the payload. """ function credentials_callback(libgit2credptr::Ptr{Ptr{Void}}, url_ptr::Cstring, username_ptr::Cstring, @@ -269,12 +264,16 @@ function credentials_callback(libgit2credptr::Ptr{Ptr{Void}}, url_ptr::Cstring, allowed_types &= Cuint(0) # Unhandled credential type end end + + p.first_pass = true + else + p.first_pass = false end # use ssh key or ssh-agent if isset(allowed_types, Cuint(Consts.CREDTYPE_SSH_KEY)) if isnull(p.credential) || !isa(unsafe_get(p.credential), SSHCredentials) - creds = reset!(SSHCredentials(p.username, "", true), -1) + creds = SSHCredentials(p.username, "", true) if !isnull(p.cache) credid = "ssh://$(p.host)" creds = get_creds!(unsafe_get(p.cache), credid, creds) @@ -287,7 +286,7 @@ function credentials_callback(libgit2credptr::Ptr{Ptr{Void}}, url_ptr::Cstring, if isset(allowed_types, Cuint(Consts.CREDTYPE_USERPASS_PLAINTEXT)) if isnull(p.credential) || !isa(unsafe_get(p.credential), UserPasswordCredentials) - creds = reset!(UserPasswordCredentials(p.username, "", true), -1) + creds = UserPasswordCredentials(p.username, "", true) if !isnull(p.cache) credid = "$(isempty(p.scheme) ? "ssh" : p.scheme)://$(p.host)" creds = get_creds!(unsafe_get(p.cache), credid, creds) diff --git a/base/libgit2/types.jl b/base/libgit2/types.jl index e38dacbb3ce6a..38dcb0b8177c4 100644 --- a/base/libgit2/types.jl +++ b/base/libgit2/types.jl @@ -1076,9 +1076,8 @@ mutable struct UserPasswordCredentials <: AbstractCredentials user::String pass::String prompt_if_incorrect::Bool # Whether to allow interactive prompting if the credentials are incorrect - count::Int # authentication failure protection count function UserPasswordCredentials(u::AbstractString,p::AbstractString,prompt_if_incorrect::Bool=false) - c = new(u,p,prompt_if_incorrect,3) + c = new(u,p,prompt_if_incorrect) finalizer(c, securezero!) return c end @@ -1088,7 +1087,6 @@ end function securezero!(cred::UserPasswordCredentials) securezero!(cred.user) securezero!(cred.pass) - cred.count = 0 return cred end @@ -1104,9 +1102,8 @@ mutable struct SSHCredentials <: AbstractCredentials pubkey::String usesshagent::String # used for ssh-agent authentication prompt_if_incorrect::Bool # Whether to allow interactive prompting if the credentials are incorrect - count::Int function SSHCredentials(u::AbstractString,p::AbstractString,prvkey::AbstractString,pubkey::AbstractString,prompt_if_incorrect::Bool=false) - c = new(u,p,prvkey,pubkey,"Y",prompt_if_incorrect,3) + c = new(u,p,prvkey,pubkey,"Y",prompt_if_incorrect) finalizer(c, securezero!) return c end @@ -1119,7 +1116,6 @@ function securezero!(cred::SSHCredentials) securezero!(cred.pass) securezero!(cred.prvkey) securezero!(cred.pubkey) - cred.count = 0 return cred end @@ -1128,21 +1124,11 @@ function Base.:(==)(a::SSHCredentials, b::SSHCredentials) end "Credentials that support caching" -mutable struct CachedCredentials <: AbstractCredentials +struct CachedCredentials <: AbstractCredentials cred::Dict{String,AbstractCredentials} - count::Int # authentication failure protection count - CachedCredentials() = new(Dict{String,AbstractCredentials}(),3) + CachedCredentials() = new(Dict{String,AbstractCredentials}()) end -"Checks if credentials were used or failed authentication, see `LibGit2.credentials_callback`" -function checkused!(p::Union{UserPasswordCredentials, SSHCredentials}) - p.count <= 0 && return true - p.count -= 1 - return false -end -reset!(p::Union{UserPasswordCredentials, SSHCredentials}, cnt::Int=3) = (p.count = cnt; p) -reset!(p::CachedCredentials) = (foreach(reset!, values(p.cred)); p) - "Obtain the cached credentials for the given host+protocol (credid), or return and store the default if not found" get_creds!(collection::CachedCredentials, credid, default) = get!(collection.cred, credid, default) @@ -1161,13 +1147,14 @@ instances will be used when the URL has changed. mutable struct CredentialPayload <: Payload credential::Nullable{AbstractCredentials} cache::Nullable{CachedCredentials} + first_pass::Bool scheme::String username::String host::String path::String function CredentialPayload(credential::Nullable{<:AbstractCredentials}, cache::Nullable{CachedCredentials}) - new(credential, cache, "", "", "", "") + new(credential, cache, true, "", "", "", "") end end diff --git a/base/pkg/entry.jl b/base/pkg/entry.jl index 2ffbef9eb89c7..9ba4c6cc8a3cb 100644 --- a/base/pkg/entry.jl +++ b/base/pkg/entry.jl @@ -427,7 +427,6 @@ function update(branch::AbstractString, upkgs::Set{String}) success = true try LibGit2.fetch(repo, payload = Nullable(creds)) - LibGit2.reset!(creds) LibGit2.merge!(repo, fastforward=true) catch err cex = CapturedException(err, catch_backtrace()) diff --git a/test/libgit2-helpers.jl b/test/libgit2-helpers.jl index 09fa7d8207f43..1953793b45817 100644 --- a/test/libgit2-helpers.jl +++ b/test/libgit2-helpers.jl @@ -71,7 +71,7 @@ function credential_loop( cache = get(payload.cache) m = match(LibGit2.URL_REGEX, url) - default_cred = LibGit2.reset!(SSHCredentials(true), -1) + default_cred = SSHCredentials(true) default_cred.usesshagent = "N" LibGit2.get_creds!(cache, "ssh://$(m[:host])", default_cred) end diff --git a/test/libgit2.jl b/test/libgit2.jl index fa306afc4b4c7..46359368eb899 100644 --- a/test/libgit2.jl +++ b/test/libgit2.jl @@ -1588,16 +1588,10 @@ mktempdir() do dir creds_user = "USER" creds_pass = "PASS" creds = LibGit2.UserPasswordCredentials(creds_user, creds_pass) - @test !LibGit2.checkused!(creds) - @test !LibGit2.checkused!(creds) - @test !LibGit2.checkused!(creds) - @test LibGit2.checkused!(creds) @test creds.user == creds_user @test creds.pass == creds_pass creds2 = LibGit2.UserPasswordCredentials(creds_user, creds_pass) @test creds == creds2 - LibGit2.reset!(creds) - @test !LibGit2.checkused!(creds) sshcreds = LibGit2.SSHCredentials(creds_user, creds_pass) @test sshcreds.user == creds_user @test sshcreds.pass == creds_pass @@ -1687,7 +1681,7 @@ mktempdir() do dir ] err, auth_attempts = challenge_prompt(ssh_p_ex, challenges) @test err == git_ok - @test auth_attempts == 5 + @test auth_attempts == 2 # User sends EOF in passphrase prompt which aborts the credential request challenges = [ @@ -1737,7 +1731,7 @@ mktempdir() do dir ] err, auth_attempts = challenge_prompt(ssh_u_ex, challenges) @test err == abort_prompt - @test auth_attempts == 5 # Should ideally be <= 2 + @test auth_attempts == 2 # User repeatedly chooses an invalid username challenges = [ @@ -1747,7 +1741,7 @@ mktempdir() do dir ] err, auth_attempts = challenge_prompt(ssh_u_ex, challenges) @test err == abort_prompt - @test auth_attempts == 6 + @test auth_attempts == 3 # Credential callback is given an empty string in the `username_ptr` # instead of the typical C_NULL. @@ -1904,7 +1898,7 @@ mktempdir() do dir ] err, auth_attempts = challenge_prompt(https_ex, challenges) @test err == git_ok - @test auth_attempts == 5 + @test auth_attempts == 2 end @testset "SSH explicit credentials" begin @@ -1940,7 +1934,7 @@ mktempdir() do dir # TODO: Unless the SSH agent is disabled we may get caught in an infinite loop err, auth_attempts = challenge_prompt(invalid_ex, []) @test err == eauth_error - @test auth_attempts == 4 + @test auth_attempts == 2 end @testset "HTTPS explicit credentials" begin @@ -1974,7 +1968,7 @@ mktempdir() do dir # Explicitly provided credential is incorrect err, auth_attempts = challenge_prompt(invalid_ex, []) @test err == eauth_error - @test auth_attempts == 4 + @test auth_attempts == 2 end @testset "Cached credentials" begin @@ -2040,7 +2034,7 @@ mktempdir() do dir expected_cred = LibGit2.UserPasswordCredentials(valid_username, valid_password) err, auth_attempts, cache = challenge_prompt(replace_ex, challenges) @test err == git_ok - @test auth_attempts == 4 + @test auth_attempts == 2 @test typeof(cache) == LibGit2.CachedCredentials @test cache.cred == Dict(cred_id => expected_cred) end From a06dbefeda6be84e72e861394b9502f25837df2e Mon Sep 17 00:00:00 2001 From: "Viral B. Shah" Date: Thu, 7 Sep 2017 17:39:37 -0400 Subject: [PATCH 304/324] Add GPU support to the README. (#23623) * Add GPU support to the README. * Add PTX backend note. * External is a better word for the PTX support, I think. --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c5c41ae8179c0..cf9e3413bf7e0 100644 --- a/README.md +++ b/README.md @@ -52,13 +52,14 @@ Julia is built and tested regularly on the following platforms: | | ARM v7 (32-bit) | | ✓ | Official | | | ARM v8 (64-bit) | | ✓ | Official | | | PowerPC (64-bit) | | | Community | +| | PTX (64-bit) | [✓](http://ci.maleadt.net:8010/) | | [External](https://github.com/JuliaGPU/CUDAnative.jl) | | macOS 10.8+ | x86-64 (64-bit) | ✓ | ✓ | Official | | Windows 7+ | x86-64 (64-bit) | ✓ | ✓ | Official | | | i686 (32-bit) | ✓ | ✓ | Official | | FreeBSD 11.0+ | x86-64 (64-bit) | ✓ | | Community | All systems marked with ✓ for CI are tested using continuous integration for every commit. -Systems with ✓ for binaries have official binaries available on the [downloads](https://julialang.org/downloads) page and are tested regularly. +Systems with ✓ for binaries have official binaries available on the [downloads](https://julialang.org/downloads) page and are tested regularly. The PTX backend needs a source build and the [CUDAnative.jl](https://github.com/JuliaGPU/CUDAnative.jl) package. The systems listed here with neither CI nor official binaries are known to build and work, but ongoing support for those platforms is dependent on community efforts. It's possible that Julia will build and work on other platforms too, and we're always looking to better our platform coverage. If you're using Julia on a platform not listed here, let us know! From 6d1680a564fd693c7aacb607958af9d57d7984e1 Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Thu, 7 Sep 2017 14:48:48 -0700 Subject: [PATCH 305/324] [RFC] Clean up test/tridiag (#23609) * Clean up test/tridiag --- test/linalg/tridiag.jl | 626 ++++++++++++++++------------------------- 1 file changed, 238 insertions(+), 388 deletions(-) diff --git a/test/linalg/tridiag.jl b/test/linalg/tridiag.jl index ba52628695c50..28d6e00655a97 100644 --- a/test/linalg/tridiag.jl +++ b/test/linalg/tridiag.jl @@ -1,217 +1,5 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -# basic tridiagonal operations -n = 5 - -srand(123) - -d = 1 .+ rand(n) -dl = -rand(n-1) -du = -rand(n-1) -v = randn(n) -B = randn(n,2) - -@testset for elty in (Float32, Float64, Complex64, Complex128, Int) - if elty == Int - srand(61516384) - d = rand(1:100, n) - dl = -rand(0:10, n-1) - du = -rand(0:10, n-1) - v = rand(1:100, n) - B = rand(1:100, n, 2) - else - d = convert(Vector{elty}, d) - dl = convert(Vector{elty}, dl) - du = convert(Vector{elty}, du) - v = convert(Vector{elty}, v) - B = convert(Matrix{elty}, B) - end - ε = eps(abs2(float(one(elty)))) - T = Tridiagonal(dl, d, du) - Ts = SymTridiagonal(d, dl) - F = diagm(d) - for i = 1:n-1 - F[i,i+1] = du[i] - F[i+1,i] = dl[i] - end - - @testset "constructor" begin - for (x, y) in ((d, dl), (GenericArray(d), GenericArray(dl))) - ST = (SymTridiagonal(x, y))::SymTridiagonal{elty, typeof(x)} - @test ST == Matrix(ST) - @test ST.dv === x - @test ST.ev === y - TT = (Tridiagonal(y, x, y))::Tridiagonal{elty, typeof(x)} - @test TT == Matrix(TT) - @test TT.dl === y - @test TT.d === x - @test TT.du === y - end - # enable when deprecations for 0.7 are dropped - # @test_throws MethodError SymTridiagonal(dv, GenericArray(ev)) - # @test_throws MethodError SymTridiagonal(GenericArray(dv), ev) - # @test_throws MethodError Tridiagonal(GenericArray(ev), dv, GenericArray(ev)) - # @test_throws MethodError Tridiagonal(ev, GenericArray(dv), ev) - end - - @testset "size and Array" begin - @test_throws ArgumentError size(Ts,0) - @test size(Ts,3) == 1 - @test size(T, 1) == n - @test size(T) == (n, n) - @test Array(T) == F - end - - @testset "elementary operations" begin - @test conj(T) == Tridiagonal(conj(dl), conj(d), conj(du)) - @test transpose(T) == Tridiagonal(du, d, dl) - @test adjoint(T) == Tridiagonal(conj(du), conj(d), conj(dl)) - - @test abs.(T) == Tridiagonal(abs.(dl),abs.(d),abs.(du)) - if elty <: Real - @test round.(T) == Tridiagonal(round.(dl),round.(d),round.(du)) - @test isa(round.(T), Tridiagonal) - @test trunc.(T) == Tridiagonal(trunc.(dl),trunc.(d),trunc.(du)) - @test isa(trunc.(T), Tridiagonal) - @test floor.(T) == Tridiagonal(floor.(dl),floor.(d),floor.(du)) - @test isa(floor.(T), Tridiagonal) - @test ceil.(T) == Tridiagonal(ceil.(dl),ceil.(d),ceil.(du)) - @test isa(ceil.(T), Tridiagonal) - end - @test real(T) == Tridiagonal(real(dl),real(d),real(du)) - @test imag(T) == Tridiagonal(imag(dl),imag(d),imag(du)) - @test abs.(Ts) == SymTridiagonal(abs.(d),abs.(dl)) - if elty <: Real - @test round.(Ts) == SymTridiagonal(round.(d),round.(dl)) - @test isa(round.(Ts), SymTridiagonal) - @test trunc.(Ts) == SymTridiagonal(trunc.(d),trunc.(dl)) - @test isa(trunc.(Ts), SymTridiagonal) - @test floor.(Ts) == SymTridiagonal(floor.(d),floor.(dl)) - @test isa(floor.(Ts), SymTridiagonal) - @test ceil.(Ts) == SymTridiagonal(ceil.(d),ceil.(dl)) - @test isa(ceil.(Ts), SymTridiagonal) - end - @test real(Ts) == SymTridiagonal(real(d),real(dl)) - @test imag(Ts) == SymTridiagonal(imag(d),imag(dl)) - end - - @testset "interconversion of Tridiagonal and SymTridiagonal" begin - @test Tridiagonal(dl, d, dl) == SymTridiagonal(d, dl) - @test SymTridiagonal(d, dl) == Tridiagonal(dl, d, dl) - @test Tridiagonal(dl, d, du) + Tridiagonal(du, d, dl) == SymTridiagonal(2d, dl+du) - @test SymTridiagonal(d, dl) + Tridiagonal(dl, d, du) == Tridiagonal(dl + dl, d+d, dl+du) - @test convert(SymTridiagonal,Tridiagonal(Ts)) == Ts - @test Array(convert(SymTridiagonal{Complex64},Tridiagonal(Ts))) == convert(Matrix{Complex64}, Ts) - end - if elty == Int - vs = rand(1:100, n) - Bs = rand(1:100, n, 2) - else - vs = convert(Vector{elty}, v) - Bs = convert(Matrix{elty}, B) - end - - @testset "tridiagonal linear algebra" begin - for (BB, vv) in ((Bs, vs), (view(Bs, 1:n, 1), view(vs, 1:n))) - @test T*vv ≈ F*vv - invFv = F\vv - @test T\vv ≈ invFv - # @test Base.solve(T,v) ≈ invFv - # @test Base.solve(T, B) ≈ F\B - Tlu = factorize(T) - x = Tlu\vv - @test x ≈ invFv - end - end - @test det(T) ≈ det(F) - - @testset "Matmul with Triangular types" begin - @test T*Base.LinAlg.UnitUpperTriangular(eye(n)) ≈ F*eye(n) - @test T*Base.LinAlg.UnitLowerTriangular(eye(n)) ≈ F*eye(n) - @test T*UpperTriangular(eye(n)) ≈ F*eye(n) - @test T*LowerTriangular(eye(n)) ≈ F*eye(n) - end - if elty <: Real - Ts = SymTridiagonal(d, dl) - Fs = Array(Ts) - Tldlt = factorize(Ts) - @testset "symmetric tridiagonal" begin - @test_throws DimensionMismatch Tldlt\rand(elty,n+1) - @test size(Tldlt) == size(Ts) - if elty <: AbstractFloat - @test typeof(convert(Base.LinAlg.LDLt{Float32},Tldlt)) == - Base.LinAlg.LDLt{Float32,SymTridiagonal{elty,Vector{elty}}} - end - for vv in (vs, view(vs, 1:n)) - invFsv = Fs\vv - x = Ts\vv - @test x ≈ invFsv - @test Array(AbstractArray(Tldlt)) ≈ Fs - end - - @testset "similar" begin - @test isa(similar(Ts), SymTridiagonal{elty}) - @test isa(similar(Ts, Int), SymTridiagonal{Int}) - @test isa(similar(Ts, Int, (3,2)), Matrix{Int}) - end - end - end - - @testset "eigenvalues/eigenvectors of symmetric tridiagonal" begin - if elty === Float32 || elty === Float64 - DT, VT = @inferred eig(Ts) - @inferred eig(Ts, 2:4) - @inferred eig(Ts, 1.0, 2.0) - D, Vecs = eig(Fs) - @test DT ≈ D - @test abs.(VT'Vecs) ≈ eye(elty, n) - @test eigvecs(Ts) == eigvecs(Fs) - #call to LAPACK.stein here - Test.test_approx_eq_modphase(eigvecs(Ts,eigvals(Ts)),eigvecs(Fs)) - elseif elty != Int - # check that undef is determined accurately even if type inference - # bails out due to the number of try/catch blocks in this code. - @test_throws UndefVarError Fs - end - end - - if elty != Int - @testset "issue #1490" begin - @test det(ones(elty,3,3)) ≈ zero(elty) atol=3*eps(real(one(elty))) - - @test det(SymTridiagonal(elty[],elty[])) == one(elty) - end - end - @testset "tril/triu" begin - @test_throws ArgumentError tril!(SymTridiagonal(d,dl),n+1) - @test_throws ArgumentError tril!(Tridiagonal(dl,d,du),n+1) - @test tril(SymTridiagonal(d,dl)) == Tridiagonal(dl,d,zeros(dl)) - @test tril(SymTridiagonal(d,dl),1) == Tridiagonal(dl,d,dl) - @test tril(SymTridiagonal(d,dl),-1) == Tridiagonal(dl,zeros(d),zeros(dl)) - @test tril(SymTridiagonal(d,dl),-2) == Tridiagonal(zeros(dl),zeros(d),zeros(dl)) - @test tril(Tridiagonal(dl,d,du)) == Tridiagonal(dl,d,zeros(du)) - @test tril(Tridiagonal(dl,d,du),1) == Tridiagonal(dl,d,du) - @test tril(Tridiagonal(dl,d,du),-1) == Tridiagonal(dl,zeros(d),zeros(du)) - @test tril(Tridiagonal(dl,d,du),-2) == Tridiagonal(zeros(dl),zeros(d),zeros(du)) - - @test_throws ArgumentError triu!(SymTridiagonal(d,dl),n+1) - @test_throws ArgumentError triu!(Tridiagonal(dl,d,du),n+1) - @test triu(SymTridiagonal(d,dl)) == Tridiagonal(zeros(dl),d,dl) - @test triu(SymTridiagonal(d,dl),-1) == Tridiagonal(dl,d,dl) - @test triu(SymTridiagonal(d,dl),1) == Tridiagonal(zeros(dl),zeros(d),dl) - @test triu(SymTridiagonal(d,dl),2) == Tridiagonal(zeros(dl),zeros(d),zeros(dl)) - @test triu(Tridiagonal(dl,d,du)) == Tridiagonal(zeros(dl),d,du) - @test triu(Tridiagonal(dl,d,du),-1) == Tridiagonal(dl,d,du) - @test triu(Tridiagonal(dl,d,du),1) == Tridiagonal(zeros(dl),zeros(d),du) - @test triu(Tridiagonal(dl,d,du),2) == Tridiagonal(zeros(dl),zeros(d),zeros(du)) - - @test !istril(SymTridiagonal(d,dl)) - @test !istriu(SymTridiagonal(d,dl)) - @test istriu(Tridiagonal(zeros(dl),d,du)) - @test istril(Tridiagonal(dl,d,zeros(du))) - end -end - #Test equivalence of eigenvectors/singular vectors taking into account possible phase (sign) differences function test_approx_eq_vecs(a::StridedVecOrMat{S}, b::StridedVecOrMat{T}, error=nothing) where {S<:Real,T<:Real} n = size(a, 1) @@ -228,118 +16,177 @@ end let n = 12 #Size of matrix problem to test srand(123) - @testset "SymTridiagonal (symmetric tridiagonal) matrices" begin - for relty in (Float32, Float64), elty in (relty, Complex{relty}) - a = convert(Vector{elty}, randn(n)) - b = convert(Vector{elty}, randn(n-1)) + @testset for elty in (Float32, Float64, Complex64, Complex128, Int) + if elty == Int + srand(61516384) + d = rand(1:100, n) + dl = -rand(0:10, n-1) + du = -rand(0:10, n-1) + v = rand(1:100, n) + B = rand(1:100, n, 2) + a = rand(1:100, n-1) + b = rand(1:100, n) + c = rand(1:100, n-1) + else + d = convert(Vector{elty}, 1. + randn(n)) + dl = convert(Vector{elty}, randn(n - 1)) + du = convert(Vector{elty}, randn(n - 1)) + v = convert(Vector{elty}, randn(n)) + B = convert(Matrix{elty}, randn(n, 2)) + a = convert(Vector{elty}, randn(n - 1)) + b = convert(Vector{elty}, randn(n)) + c = convert(Vector{elty}, randn(n - 1)) if elty <: Complex - a += im*convert(Vector{elty}, randn(n)) - b += im*convert(Vector{elty}, randn(n-1)) + a += im*convert(Vector{elty}, randn(n - 1)) + b += im*convert(Vector{elty}, randn(n)) + c += im*convert(Vector{elty}, randn(n - 1)) end + end + @test_throws DimensionMismatch SymTridiagonal(dl, ones(elty, n + 1)) + @test_throws ArgumentError SymTridiagonal(rand(n, n)) + @test_throws ArgumentError Tridiagonal(dl, dl, dl) + @test_throws ArgumentError convert(SymTridiagonal{elty}, Tridiagonal(dl, d, du)) + + if elty != Int + @testset "issue #1490" begin + @test det(ones(elty,3,3)) ≈ zero(elty) atol=3*eps(real(one(elty))) + @test det(SymTridiagonal(elty[],elty[])) == one(elty) + end + end - @test_throws DimensionMismatch SymTridiagonal(a, ones(elty, n+1)) - @test_throws ArgumentError SymTridiagonal(rand(n,n)) + @testset "constructor" begin + for (x, y) in ((d, dl), (GenericArray(d), GenericArray(dl))) + ST = (SymTridiagonal(x, y))::SymTridiagonal{elty, typeof(x)} + @test ST == Matrix(ST) + @test ST.dv === x + @test ST.ev === y + TT = (Tridiagonal(y, x, y))::Tridiagonal{elty, typeof(x)} + @test TT == Matrix(TT) + @test TT.dl === y + @test TT.d === x + @test TT.du === y + end + # enable when deprecations for 0.7 are dropped + # @test_throws MethodError SymTridiagonal(dv, GenericArray(ev)) + # @test_throws MethodError SymTridiagonal(GenericArray(dv), ev) + # @test_throws MethodError Tridiagonal(GenericArray(ev), dv, GenericArray(ev)) + # @test_throws MethodError Tridiagonal(ev, GenericArray(dv), ev) + end + @testset "interconversion of Tridiagonal and SymTridiagonal" begin + @test Tridiagonal(dl, d, dl) == SymTridiagonal(d, dl) + @test Tridiagonal(dl, d, du) + Tridiagonal(du, d, dl) == SymTridiagonal(2d, dl+du) + @test SymTridiagonal(d, dl) + Tridiagonal(dl, d, du) == Tridiagonal(dl + dl, d+d, dl+du) + @test convert(SymTridiagonal,Tridiagonal(SymTridiagonal(d, dl))) == SymTridiagonal(d, dl) + @test Array(convert(SymTridiagonal{Complex64},Tridiagonal(SymTridiagonal(d, dl)))) == convert(Matrix{Complex64}, SymTridiagonal(d, dl)) + end + @testset "tril/triu" begin + @test_throws ArgumentError tril!(SymTridiagonal(d,dl),n+1) + @test_throws ArgumentError tril!(Tridiagonal(dl,d,du),n+1) + @test tril(SymTridiagonal(d,dl)) == Tridiagonal(dl,d,zeros(dl)) + @test tril(SymTridiagonal(d,dl),1) == Tridiagonal(dl,d,dl) + @test tril(SymTridiagonal(d,dl),-1) == Tridiagonal(dl,zeros(d),zeros(dl)) + @test tril(SymTridiagonal(d,dl),-2) == Tridiagonal(zeros(dl),zeros(d),zeros(dl)) + @test tril(Tridiagonal(dl,d,du)) == Tridiagonal(dl,d,zeros(du)) + @test tril(Tridiagonal(dl,d,du),1) == Tridiagonal(dl,d,du) + @test tril(Tridiagonal(dl,d,du),-1) == Tridiagonal(dl,zeros(d),zeros(du)) + @test tril(Tridiagonal(dl,d,du),-2) == Tridiagonal(zeros(dl),zeros(d),zeros(du)) + + @test_throws ArgumentError triu!(SymTridiagonal(d,dl),n+1) + @test_throws ArgumentError triu!(Tridiagonal(dl,d,du),n+1) + @test triu(SymTridiagonal(d,dl)) == Tridiagonal(zeros(dl),d,dl) + @test triu(SymTridiagonal(d,dl),-1) == Tridiagonal(dl,d,dl) + @test triu(SymTridiagonal(d,dl),1) == Tridiagonal(zeros(dl),zeros(d),dl) + @test triu(SymTridiagonal(d,dl),2) == Tridiagonal(zeros(dl),zeros(d),zeros(dl)) + @test triu(Tridiagonal(dl,d,du)) == Tridiagonal(zeros(dl),d,du) + @test triu(Tridiagonal(dl,d,du),-1) == Tridiagonal(dl,d,du) + @test triu(Tridiagonal(dl,d,du),1) == Tridiagonal(zeros(dl),zeros(d),du) + @test triu(Tridiagonal(dl,d,du),2) == Tridiagonal(zeros(dl),zeros(d),zeros(du)) + + @test !istril(SymTridiagonal(d,dl)) + @test !istriu(SymTridiagonal(d,dl)) + @test istriu(Tridiagonal(zeros(dl),d,du)) + @test istril(Tridiagonal(dl,d,zeros(du))) + end - A = SymTridiagonal(a, b) + @testset for mat_type in (Tridiagonal, SymTridiagonal) + A = mat_type == Tridiagonal ? mat_type(dl, d, du) : mat_type(d, dl) fA = map(elty <: Complex ? Complex128 : Float64, Array(A)) - + @testset "similar, size, and copy!" begin + B = similar(A) + @test size(B) == size(A) + if mat_type == Tridiagonal # doesn't work for SymTridiagonal yet + copy!(B, A) + @test B == A + end + @test isa(similar(A), mat_type{elty}) + @test isa(similar(A, Int), mat_type{Int}) + @test isa(similar(A, Int, (3, 2)), Matrix{Int}) + @test size(A, 3) == 1 + @test size(A, 1) == n + @test size(A) == (n, n) + @test_throws ArgumentError size(A, 0) + end @testset "getindex" begin - @test_throws BoundsError A[n+1,1] - @test_throws BoundsError A[1,n+1] - @test A[1,n] == convert(elty,0.0) - @test A[1,1] == a[1] + @test_throws BoundsError A[n + 1, 1] + @test_throws BoundsError A[1, n + 1] + @test A[1, n] == convert(elty, 0.0) + @test A[1, 1] == d[1] end @testset "setindex!" begin @test_throws BoundsError A[n + 1, 1] = 0 # test bounds check @test_throws BoundsError A[1, n + 1] = 0 # test bounds check - @test ((A[3, 3] = A[3, 3]) == A[3, 3]; A == fA) # test assignment on the main diagonal - @test_throws ArgumentError A[3, 2] = 1 # test assignment on the subdiagonal - @test_throws ArgumentError A[2, 3] = 1 # test assignment on the superdiagonal - @test_throws ArgumentError A[1, 3] = 1 # test assignment off the main/sub/super diagonal + @test_throws ArgumentError A[1, 3] = 1 # test assignment off the main/sub/super diagonal + if mat_type == Tridiagonal + @test (A[3, 3] = A[3, 3]; A == fA) # test assignment on the main diagonal + @test (A[3, 2] = A[3, 2]; A == fA) # test assignment on the subdiagonal + @test (A[2, 3] = A[2, 3]; A == fA) # test assignment on the superdiagonal + @test ((A[1, 3] = 0) == 0; A == fA) # test zero assignment off the main/sub/super diagonal + else # mat_type is SymTridiagonal + @test ((A[3, 3] = A[3, 3]) == A[3, 3]; A == fA) # test assignment on the main diagonal + @test_throws ArgumentError A[3, 2] = 1 # test assignment on the subdiagonal + @test_throws ArgumentError A[2, 3] = 1 # test assignment on the superdiagonal + end end @testset "Diagonal extraction" begin - @test diag(A,1) == b - @test diag(A,-1) == b - @test diag(A,0) == a - @test diag(A,n-1) == zeros(elty,1) - @test_throws ArgumentError diag(A,n+1) + @test diag(A, 1) === (mat_type == Tridiagonal ? du : dl) + @test diag(A, -1) === dl + @test diag(A, 0) === d + @test diag(A) === d + @test diag(A, n - 1) == zeros(elty, 1) + @test_throws ArgumentError diag(A, n + 1) end @testset "Idempotent tests" begin for func in (conj, transpose, adjoint) @test func(func(A)) == A end end - @testset "Simple unary functions" begin - for func in (det, inv) - @test func(A) ≈ func(fA) atol=n^2*sqrt(eps(relty)) + if elty != Int + @testset "Simple unary functions" begin + for func in (det, inv) + @test func(A) ≈ func(fA) atol=n^2*sqrt(eps(real(one(elty)))) + end end end - + ds = mat_type == Tridiagonal ? (dl, d, du) : (d, dl) + for f in (real, imag) + @test f(A)::mat_type == mat_type(map(f, ds)...) + end if elty <: Real - @testset "Rounding to Ints" begin - @test round.(Int,A) == round.(Int,fA) - @test isa(round.(Int,A), SymTridiagonal) - @test trunc.(Int,A) == trunc.(Int,fA) - @test isa(trunc.(Int,A), SymTridiagonal) - @test ceil.(Int,A) == ceil.(Int,fA) - @test isa(ceil.(Int,A), SymTridiagonal) - @test floor.(Int,A) == floor.(Int,fA) - @test isa(floor.(Int,A), SymTridiagonal) + for f in (round, trunc, floor, ceil) + fds = [f.(d) for d in ds] + @test f.(A)::mat_type == mat_type(fds...) + @test f.(Int, A)::mat_type == f.(Int, fA) end end - - @testset "Tridiagonal/SymTridiagonal mixing ops" begin - B = convert(Tridiagonal{elty},A) - @test B == A - @test B + A == A + B - @test B - A == A - B - end + fds = [abs.(d) for d in ds] + @test abs.(A)::mat_type == mat_type(fds...) @testset "Multiplication with strided matrix/vector" begin @test A*ones(n) ≈ Array(A)*ones(n) @test A*ones(n, 2) ≈ Array(A)*ones(n, 2) end - if elty <: Real - @testset "Eigensystems" begin - zero, infinity = convert(elty, 0), convert(elty, Inf) - @testset "stebz! and stein!" begin - w, iblock, isplit = LAPACK.stebz!('V', 'B', -infinity, infinity, 0, 0, zero, a, b) - evecs = LAPACK.stein!(a, b, w) - - (e, v) = eig(SymTridiagonal(a, b)) - @test e ≈ w - test_approx_eq_vecs(v, evecs) - end - @testset "stein! call using iblock and isplit" begin - w, iblock, isplit = LAPACK.stebz!('V', 'B', -infinity, infinity, 0, 0, zero, a, b) - evecs = LAPACK.stein!(a, b, w, iblock, isplit) - test_approx_eq_vecs(v, evecs) - end - @testset "stegr! call with index range" begin - F = eigfact(SymTridiagonal(a, b),1:2) - fF = eigfact(Symmetric(Array(SymTridiagonal(a, b))),1:2) - Test.test_approx_eq_modphase(F[:vectors], fF[:vectors]) - @test F[:values] ≈ fF[:values] - end - @testset "stegr! call with value range" begin - F = eigfact(SymTridiagonal(a, b),0.0,1.0) - fF = eigfact(Symmetric(Array(SymTridiagonal(a, b))),0.0,1.0) - Test.test_approx_eq_modphase(F[:vectors], fF[:vectors]) - @test F[:values] ≈ fF[:values] - end - end - end - @testset "Binary operations" begin - a = convert(Vector{elty}, randn(n)) - b = convert(Vector{elty}, randn(n - 1)) - if elty <: Complex - a += im*convert(Vector{elty}, randn(n)) - b += im*convert(Vector{elty}, randn(n - 1)) - end - - B = SymTridiagonal(a, b) + B = mat_type == Tridiagonal ? mat_type(a, b, c) : mat_type(b, a) fB = map(elty <: Complex ? Complex128 : Float64, Array(B)) - for op in (+, -, *) @test Array(op(A, B)) ≈ op(fA, fB) end @@ -348,109 +195,112 @@ let n = 12 #Size of matrix problem to test @test Array(A*α) ≈ Array(A)*α @test Array(A/α) ≈ Array(A)/α + @testset "Matmul with Triangular types" begin + @test A*Base.LinAlg.UnitUpperTriangular(eye(n)) ≈ fA*eye(n) + @test A*Base.LinAlg.UnitLowerTriangular(eye(n)) ≈ fA*eye(n) + @test A*UpperTriangular(eye(n)) ≈ fA*eye(n) + @test A*LowerTriangular(eye(n)) ≈ fA*eye(n) + end @testset "A_mul_B! errors" begin + @test_throws DimensionMismatch Base.LinAlg.A_mul_B!(zeros(fA),A,ones(elty,n,n+1)) + @test_throws DimensionMismatch Base.LinAlg.A_mul_B!(zeros(fA),A,ones(elty,n+1,n)) @test_throws DimensionMismatch A_mul_B!(zeros(elty,n,n),B,ones(elty,n+1,n)) @test_throws DimensionMismatch A_mul_B!(zeros(elty,n+1,n),B,ones(elty,n,n)) @test_throws DimensionMismatch A_mul_B!(zeros(elty,n,n+1),B,ones(elty,n,n)) end end - end - end - @testset "Tridiagonal matrices" begin - for relty in (Float32, Float64), elty in (relty, Complex{relty}) - a = convert(Vector{elty}, randn(n - 1)) - b = convert(Vector{elty}, randn(n)) - c = convert(Vector{elty}, randn(n - 1)) - if elty <: Complex - a += im*convert(Vector{elty}, randn(n - 1)) - b += im*convert(Vector{elty}, randn(n)) - c += im*convert(Vector{elty}, randn(n - 1)) - end - - @test_throws ArgumentError Tridiagonal(a,a,a) - A = Tridiagonal(a, b, c) - fA = map(elty <: Complex ? Complex128 : Float64, Array(A)) - - @testset "similar, size, and copy!" begin - B = similar(A) - @test size(B) == size(A) - copy!(B,A) - @test B == A - @test isa(similar(A), Tridiagonal{elty}) - @test isa(similar(A, Int), Tridiagonal{Int}) - @test isa(similar(A, Int, (3,2)), Matrix{Int}) - @test size(A,3) == 1 - @test_throws ArgumentError size(A,0) - end - @testset "Diagonal extraction" begin - @test diag(A,-1) == a - @test diag(A,0) == b - @test diag(A,1) == c - @test diag(A,n-1) == zeros(elty,1) - @test_throws ArgumentError diag(A,n+1) - end - @testset "Simple unary functions" begin - for func in (det, inv) - @test func(A) ≈ func(fA) atol=n^2*sqrt(eps(relty)) + if mat_type == SymTridiagonal + @testset "Tridiagonal/SymTridiagonal mixing ops" begin + B = convert(Tridiagonal{elty}, A) + @test B == A + @test B + A == A + B + @test B - A == A - B end - end - if elty <: Real - @testset "Rounding to Ints" begin - @test round.(Int,A) == round.(Int,fA) - @test isa(round.(Int,A), Tridiagonal) - @test trunc.(Int,A) == trunc.(Int,fA) - @test isa(trunc.(Int,A), Tridiagonal) - @test ceil.(Int,A) == ceil.(Int,fA) - @test isa(ceil.(Int,A), Tridiagonal) - @test floor.(Int,A) == floor.(Int,fA) - @test isa(floor.(Int,A), Tridiagonal) + if elty <: Base.LinAlg.BlasReal + @testset "Eigensystems" begin + zero, infinity = convert(elty, 0), convert(elty, Inf) + @testset "stebz! and stein!" begin + w, iblock, isplit = LAPACK.stebz!('V', 'B', -infinity, infinity, 0, 0, zero, b, a) + evecs = LAPACK.stein!(b, a, w) + + (e, v) = eig(SymTridiagonal(b, a)) + @test e ≈ w + test_approx_eq_vecs(v, evecs) + end + @testset "stein! call using iblock and isplit" begin + w, iblock, isplit = LAPACK.stebz!('V', 'B', -infinity, infinity, 0, 0, zero, b, a) + evecs = LAPACK.stein!(b, a, w, iblock, isplit) + test_approx_eq_vecs(v, evecs) + end + @testset "stegr! call with index range" begin + F = eigfact(SymTridiagonal(b, a),1:2) + fF = eigfact(Symmetric(Array(SymTridiagonal(b, a))),1:2) + Test.test_approx_eq_modphase(F[:vectors], fF[:vectors]) + @test F[:values] ≈ fF[:values] + end + @testset "stegr! call with value range" begin + F = eigfact(SymTridiagonal(b, a),0.0,1.0) + fF = eigfact(Symmetric(Array(SymTridiagonal(b, a))),0.0,1.0) + Test.test_approx_eq_modphase(F[:vectors], fF[:vectors]) + @test F[:values] ≈ fF[:values] + end + @testset "eigenvalues/eigenvectors of symmetric tridiagonal" begin + if elty === Float32 || elty === Float64 + DT, VT = @inferred eig(A) + @inferred eig(A, 2:4) + @inferred eig(A, 1.0, 2.0) + D, Vecs = eig(fA) + @test DT ≈ D + @test abs.(VT'Vecs) ≈ eye(elty, n) + @test eigvecs(A) ≈ eigvecs(fA) + #call to LAPACK.stein here + Test.test_approx_eq_modphase(eigvecs(A,eigvals(A)),eigvecs(A)) + elseif elty != Int + # check that undef is determined accurately even if type inference + # bails out due to the number of try/catch blocks in this code. + @test_throws UndefVarError fA + end + end + end end - end - @testset "Binary operations" begin - a = convert(Vector{elty}, randn(n - 1)) - b = convert(Vector{elty}, randn(n)) - c = convert(Vector{elty}, randn(n - 1)) - if elty <: Complex - a += im*convert(Vector{elty}, randn(n - 1)) - b += im*convert(Vector{elty}, randn(n)) - c += im*convert(Vector{elty}, randn(n - 1)) + if elty <: Real + Ts = SymTridiagonal(d, dl) + Fs = Array(Ts) + Tldlt = factorize(Ts) + @testset "symmetric tridiagonal" begin + @test_throws DimensionMismatch Tldlt\rand(elty,n+1) + @test size(Tldlt) == size(Ts) + if elty <: AbstractFloat + @test typeof(convert(Base.LinAlg.LDLt{Float32},Tldlt)) == + Base.LinAlg.LDLt{Float32,SymTridiagonal{elty,Vector{elty}}} + end + for vv in (copy(v), view(v, 1:n)) + invFsv = Fs\vv + x = Ts\vv + @test x ≈ invFsv + @test Array(AbstractArray(Tldlt)) ≈ Fs + end + + @testset "similar" begin + @test isa(similar(Ts), SymTridiagonal{elty}) + @test isa(similar(Ts, Int), SymTridiagonal{Int}) + @test isa(similar(Ts, Int, (3,2)), Matrix{Int}) + end + end end - end - - @testset "Multiplication with strided matrix/vector" begin - @test A*ones(n) ≈ Array(A)*ones(n) - @test A*ones(n, 2) ≈ Array(A)*ones(n, 2) - end - - B = Tridiagonal(a, b, c) - fB = map(elty <: Complex ? Complex128 : Float64, Array(B)) - @testset "Binary ops" begin - for op in (+, -, *) - @test Array(op(A, B)) ≈ op(fA, fB) + else # mat_type is Tridiagonal + @testset "tridiagonal linear algebra" begin + for (BB, vv) in ((copy(B), copy(v)), (view(B, 1:n, 1), view(v, 1:n))) + @test A*vv ≈ fA*vv + invFv = fA\vv + @test A\vv ≈ invFv + # @test Base.solve(T,v) ≈ invFv + # @test Base.solve(T, B) ≈ F\B + Tlu = factorize(A) + x = Tlu\vv + @test x ≈ invFv + end end - α = rand(elty) - @test Array(α*A) ≈ α*Array(A) - @test Array(A*α) ≈ Array(A)*α - @test Array(A/α) ≈ Array(A)/α - end - @test_throws ArgumentError convert(SymTridiagonal{elty},A) - - @testset "A_mul_B! errors" begin - @test_throws DimensionMismatch Base.LinAlg.A_mul_B!(zeros(fA),A,ones(elty,n,n+1)) - @test_throws DimensionMismatch Base.LinAlg.A_mul_B!(zeros(fA),A,ones(elty,n+1,n)) - end - @testset "getindex" begin - @test_throws BoundsError A[n+1,1] - @test_throws BoundsError A[1,n+1] - end - @testset "setindex!" begin - @test_throws BoundsError A[n + 1, 1] = 0 # test bounds check - @test_throws BoundsError A[1, n + 1] = 0 # test bounds check - @test (A[3, 3] = A[3, 3]; A == fA) # test assignment on the main diagonal - @test (A[3, 2] = A[3, 2]; A == fA) # test assignment on the subdiagonal - @test (A[2, 3] = A[2, 3]; A == fA) # test assignment on the superdiagonal - @test ((A[1, 3] = 0) == 0; A == fA) # test zero assignment off the main/sub/super diagonal - @test_throws ArgumentError A[1, 3] = 1 # test non-zero assignment off the main/sub/super diagonal end end end From c5a642d34939f9cf9ca7a84c230900c8f732f600 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Thu, 7 Sep 2017 11:17:19 -0400 Subject: [PATCH 306/324] error if `for outer` is used with no existing outer var. fixes #23532 --- src/julia-syntax.scm | 11 ++++++++--- test/core.jl | 16 ++++++++++++++++ test/topology.jl | 12 +++++++----- 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 2385ab724bcfe..0a88232038d49 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -1725,6 +1725,7 @@ (= ,state (call (top start) ,coll)) ;; TODO avoid `local declared twice` error from this ;;,@(if outer? `((local ,lhs)) '()) + ,@(if outer? `((require-existing-local ,lhs)) '()) ,(expand-forms `(,while (call (top !) (call (top done) ,coll ,state)) @@ -2599,10 +2600,14 @@ ((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) 'require-existing-local) + (if (not (memq (cadr e) env)) + (error "no outer variable declaration exists for \"for outer\"")) + '(null)) ((eq? (car e) 'warn-if-existing) - (if (or (memq (cadr e) outerglobals) (memq (cadr e) implicitglobals)) - `(warn-loop-var ,(cadr e)) - '(null))) + (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)) diff --git a/test/core.jl b/test/core.jl index 14c0d983ef040..cd541dc302c55 100644 --- a/test/core.jl +++ b/test/core.jl @@ -2934,6 +2934,22 @@ let @test a == 1:2 end +# `for outer` +let + function forouter() + i = 1 + for outer i = 2:3 + end + return i + end + @test forouter() == 3 +end + +@test_throws ErrorException("syntax: no outer variable declaration exists for \"for outer\"") @eval function f() + for outer i = 1:2 + end +end + # issue #11295 function f11295(x...) call = Expr(x...) diff --git a/test/topology.jl b/test/topology.jl index 75a4c3ae52322..5b79cf58cb083 100644 --- a/test/topology.jl +++ b/test/topology.jl @@ -1,10 +1,10 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license pids = addprocs_with_testenv(4; topology="master_slave") -p1 = pids[1] -p2 = pids[2] -@test_throws RemoteException remotecall_fetch(()->remotecall_fetch(myid, p2), p1) +let p1 = pids[1], p2 = pids[2] + @test_throws RemoteException remotecall_fetch(()->remotecall_fetch(myid, p2), p1) +end function test_worker_counts() # check if the nprocs/nworkers/workers are the same on the remaining workers @@ -77,8 +77,9 @@ while true end end -for outer p1 in workers() - for outer p2 in workers() +let p1, p2 +for p1 in workers() + for p2 in workers() i1 = map_pid_ident[p1] i2 = map_pid_ident[p2] if (iseven(i1) && iseven(i2)) || (isodd(i1) && isodd(i2)) @@ -88,6 +89,7 @@ for outer p1 in workers() end end end +end remove_workers_and_test() From d2041509ad94759c0fa61426f6b194473a6c8669 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Thu, 7 Sep 2017 12:19:01 -0400 Subject: [PATCH 307/324] rename `--precompiled` and `--compilecache` command line options also rearrange help text a bit part of #23054 --- NEWS.md | 4 +++ base/loading.jl | 6 ++-- base/options.jl | 4 +-- base/pkg/entry.jl | 4 +-- doc/src/manual/getting-started.md | 12 ++++---- doc/src/manual/modules.md | 4 +-- src/jloptions.c | 48 +++++++++++++++++++------------ src/julia.h | 12 ++++---- src/staticdata.c | 2 +- test/cmdlineargs.jl | 34 +++++++++++----------- test/compile.jl | 12 ++++---- test/loading.jl | 2 +- test/workspace.jl | 2 +- 13 files changed, 82 insertions(+), 64 deletions(-) diff --git a/NEWS.md b/NEWS.md index 8b7a481d5bb5a..6f4c2bb5faef5 100644 --- a/NEWS.md +++ b/NEWS.md @@ -431,6 +431,10 @@ Command-line option changes The `--quiet` option implies `--banner=no` even in REPL mode but can be overridden by passing `--quiet` together with `--banner=yes` ([#23342]). + * The option `--precompiled` has been renamed to `--sysimage-native-code` ([#23054]). + + * The option `--compilecache` has been renamed to `--compiled-modules` ([#23054]). + Julia v0.6.0 Release Notes ========================== diff --git a/base/loading.jl b/base/loading.jl index c3be02d771fb7..bdc38471890fe 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -269,7 +269,7 @@ order to throw an error if Julia attempts to precompile it. using `__precompile__()`. Failure to do so can result in a runtime error when loading the module. """ function __precompile__(isprecompilable::Bool=true) - if (JLOptions().use_compilecache != 0 && + if (JLOptions().use_compiled_modules != 0 && isprecompilable != (0 != ccall(:jl_generating_output, Cint, ())) && !(isprecompilable && toplevel_load[])) throw(PrecompilableError(isprecompilable)) @@ -361,7 +361,7 @@ function _require(mod::Symbol) # attempt to load the module file via the precompile cache locations doneprecompile = false - if JLOptions().use_compilecache != 0 + if JLOptions().use_compiled_modules != 0 doneprecompile = _require_search_from_serialized(mod, path) if !isa(doneprecompile, Bool) return # success @@ -400,7 +400,7 @@ function _require(mod::Symbol) try Base.include_relative(Main, path) catch ex - if doneprecompile === true || JLOptions().use_compilecache == 0 || !precompilableerror(ex, true) + if doneprecompile === true || JLOptions().use_compiled_modules == 0 || !precompilableerror(ex, true) rethrow() # rethrow non-precompilable=true errors end # the file requested `__precompile__`, so try to build a cache file and use that diff --git a/base/options.jl b/base/options.jl index b4a4583aabfab..aec3ae1862237 100644 --- a/base/options.jl +++ b/base/options.jl @@ -31,8 +31,8 @@ struct JLOptions worker::Int8 cookie::Ptr{UInt8} handle_signals::Int8 - use_precompiled::Int8 - use_compilecache::Int8 + use_sysimage_native_code::Int8 + use_compiled_modules::Int8 bindto::Ptr{UInt8} outputbc::Ptr{UInt8} outputunoptbc::Ptr{UInt8} diff --git a/base/pkg/entry.jl b/base/pkg/entry.jl index 2ffbef9eb89c7..c8506bd8a22a2 100644 --- a/base/pkg/entry.jl +++ b/base/pkg/entry.jl @@ -609,7 +609,7 @@ function build(pkg::AbstractString, build_file::AbstractString, errfile::Abstrac cmd = ``` $(Base.julia_cmd()) -O0 --color=$(Base.have_color ? "yes" : "no") - --compilecache=$(Bool(Base.JLOptions().use_compilecache) ? "yes" : "no") + --compiled-modules=$(Bool(Base.JLOptions().use_compiled_modules) ? "yes" : "no") --history-file=no --startup-file=$(Base.JLOptions().startupfile != 2 ? "yes" : "no") --eval $code @@ -717,7 +717,7 @@ function test!(pkg::AbstractString, $(Base.julia_cmd()) --code-coverage=$(coverage ? "user" : "none") --color=$(Base.have_color ? "yes" : "no") - --compilecache=$(Bool(Base.JLOptions().use_compilecache) ? "yes" : "no") + --compiled-modules=$(Bool(Base.JLOptions().use_compiled_modules) ? "yes" : "no") --check-bounds=yes --warn-overwrite=yes --startup-file=$(Base.JLOptions().startupfile != 2 ? "yes" : "no") diff --git a/doc/src/manual/getting-started.md b/doc/src/manual/getting-started.md index aaafc82a2b769..acae790309ad7 100644 --- a/doc/src/manual/getting-started.md +++ b/doc/src/manual/getting-started.md @@ -99,11 +99,13 @@ julia [switches] -- [programfile] [args...] -h, --help Print this message -J, --sysimage Start up with the given system image file - --precompiled={yes|no} Use precompiled code from system image if available - --compilecache={yes|no} Enable/disable incremental precompilation of modules -H, --home Set location of `julia` executable --startup-file={yes|no} Load ~/.juliarc.jl --handle-signals={yes|no} Enable or disable Julia's default signal handlers + --sysimage-native-code={yes|no} + Use native code from system image if available + --compiled-modules={yes|no} + Enable or disable incremental precompilation of modules -e, --eval Evaluate -E, --print Evaluate and show @@ -118,6 +120,9 @@ julia [switches] -- [programfile] [args...] --color={yes|no} Enable or disable color text --history-file={yes|no} Load or save history + --depwarn={yes|no|error} Enable or disable syntax and method deprecation warnings ("error" turns warnings into errors) + --warn-overwrite={yes|no} Enable or disable method overwrite warnings + --compile={yes|no|all|min}Enable or disable JIT compiler, or request exhaustive compilation -C, --cpu-target Limit usage of cpu features up to -O, --optimize={0,1,2,3} Set the optimization level (default is 2 if unspecified or 3 if specified as -O) @@ -126,9 +131,6 @@ julia [switches] -- [programfile] [args...] --check-bounds={yes|no} Emit bounds checks always or never (ignoring declarations) --math-mode={ieee,fast} Disallow or enable unsafe floating point optimizations (overrides @fastmath declaration) - --depwarn={yes|no|error} Enable or disable syntax and method deprecation warnings ("error" turns warnings into errors) - --warn-overwrite={yes|no} Enable or disable method overwrite warnings - --output-o name Generate an object file (including system image data) --output-ji name Generate a system image data file (.ji) --output-bc name Generate LLVM bitcode (.bc) diff --git a/doc/src/manual/modules.md b/doc/src/manual/modules.md index ee15f40288e50..0921880fa87e3 100644 --- a/doc/src/manual/modules.md +++ b/doc/src/manual/modules.md @@ -382,8 +382,8 @@ A few other points to be aware of: of these and to create a single unique instance of others. It is sometimes helpful during module development to turn off incremental precompilation. The -command line flag `--compilecache={yes|no}` enables you to toggle module precompilation on and -off. When Julia is started with `--compilecache=no` the serialized modules in the compile cache +command line flag `--compiled-modules={yes|no}` enables you to toggle module precompilation on and +off. When Julia is started with `--compiled-modules=no` the serialized modules in the compile cache are ignored when loading modules and module dependencies. `Base.compilecache` can still be called manually and it will respect `__precompile__()` directives for the module. The state of this command line flag is passed to [`Pkg.build`](@ref) to disable automatic precompilation triggering when installing, diff --git a/src/jloptions.c b/src/jloptions.c index 64b512212c67c..7fe1adb84913d 100644 --- a/src/jloptions.c +++ b/src/jloptions.c @@ -67,8 +67,8 @@ jl_options_t jl_options = { 0, // quiet 0, // worker NULL, // cookie JL_OPTIONS_HANDLE_SIGNALS_ON, - JL_OPTIONS_USE_PRECOMPILED_YES, - JL_OPTIONS_USE_COMPILECACHE_YES, + JL_OPTIONS_USE_SYSIMAGE_NATIVE_CODE_YES, + JL_OPTIONS_USE_COMPILED_MODULES_YES, NULL, // bind-to NULL, // output-bc NULL, // output-unopt-bc @@ -86,11 +86,13 @@ static const char opts[] = // startup options " -J, --sysimage Start up with the given system image file\n" - " --precompiled={yes|no} Use precompiled code from system image if available\n" - " --compilecache={yes|no} Enable/disable incremental precompilation of modules\n" " -H, --home Set location of `julia` executable\n" " --startup-file={yes|no} Load ~/.juliarc.jl\n" - " --handle-signals={yes|no} Enable or disable Julia's default signal handlers\n\n" + " --handle-signals={yes|no} Enable or disable Julia's default signal handlers\n" + " --sysimage-native-code={yes|no}\n" + " Use native code from system image if available\n" + " --compiled-modules={yes|no}\n" + " Enable or disable incremental precompilation of modules\n\n" // actions " -e, --eval Evaluate \n" @@ -109,6 +111,10 @@ static const char opts[] = " --color={yes|no} Enable or disable color text\n" " --history-file={yes|no} Load or save history\n\n" + // error and warning options + " --depwarn={yes|no|error} Enable or disable syntax and method deprecation warnings (\"error\" turns warnings into errors)\n" + " --warn-overwrite={yes|no} Enable or disable method overwrite warnings\n\n" + // code generation options " --compile={yes|no|all|min}Enable or disable JIT compiler, or request exhaustive compilation\n" " -C, --cpu-target Limit usage of cpu features up to ; set to \"help\" to see the available options\n" @@ -126,10 +132,6 @@ static const char opts[] = #endif " --math-mode={ieee,fast} Disallow or enable unsafe floating point optimizations (overrides @fastmath declaration)\n\n" - // error and warning options - " --depwarn={yes|no|error} Enable or disable syntax and method deprecation warnings (\"error\" turns warnings into errors)\n\n" - " --warn-overwrite={yes|no} Enable or disable method overwrite warnings" - // compiler output options " --output-o name Generate an object file (including system image data)\n" " --output-ji name Generate a system image data file (.ji)\n" @@ -174,7 +176,9 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) opt_use_precompiled, opt_use_compilecache, opt_incremental, - opt_banner + opt_banner, + opt_sysimage_native_code, + opt_compiled_modules }; static const char* const shortopts = "+vhqH:e:E:L:J:C:ip:O:g:"; static const struct option longopts[] = { @@ -190,8 +194,10 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) { "print", required_argument, 0, 'E' }, { "load", required_argument, 0, 'L' }, { "sysimage", required_argument, 0, 'J' }, - { "precompiled", required_argument, 0, opt_use_precompiled }, - { "compilecache", required_argument, 0, opt_use_compilecache }, + { "precompiled", required_argument, 0, opt_use_precompiled }, // deprecated + { "sysimage-native-code", required_argument, 0, opt_sysimage_native_code }, + { "compilecache", required_argument, 0, opt_use_compilecache }, // deprecated + { "compiled-modules", required_argument, 0, opt_compiled_modules }, { "cpu-target", required_argument, 0, 'C' }, { "procs", required_argument, 0, 'p' }, { "machinefile", required_argument, 0, opt_machinefile }, @@ -331,20 +337,26 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) jl_errorf("julia: invalid argument to --banner={yes|no} (%s)", optarg); break; case opt_use_precompiled: + jl_printf(JL_STDOUT, "WARNING: julia --precompiled option is deprecated, use --sysimage-native-code instead.\n"); + // fall through + case opt_sysimage_native_code: if (!strcmp(optarg,"yes")) - jl_options.use_precompiled = JL_OPTIONS_USE_PRECOMPILED_YES; + jl_options.use_sysimage_native_code = JL_OPTIONS_USE_SYSIMAGE_NATIVE_CODE_YES; else if (!strcmp(optarg,"no")) - jl_options.use_precompiled = JL_OPTIONS_USE_PRECOMPILED_NO; + jl_options.use_sysimage_native_code = JL_OPTIONS_USE_SYSIMAGE_NATIVE_CODE_NO; else - jl_errorf("julia: invalid argument to --precompiled={yes|no} (%s)", optarg); + jl_errorf("julia: invalid argument to --sysimage-native-code={yes|no} (%s)", optarg); break; case opt_use_compilecache: + jl_printf(JL_STDOUT, "WARNING: julia --compilecache option is deprecated, use --compiled-modules instead.\n"); + // fall through + case opt_compiled_modules: if (!strcmp(optarg,"yes")) - jl_options.use_compilecache = JL_OPTIONS_USE_COMPILECACHE_YES; + jl_options.use_compiled_modules = JL_OPTIONS_USE_COMPILED_MODULES_YES; else if (!strcmp(optarg,"no")) - jl_options.use_compilecache = JL_OPTIONS_USE_COMPILECACHE_NO; + jl_options.use_compiled_modules = JL_OPTIONS_USE_COMPILED_MODULES_NO; else - jl_errorf("julia: invalid argument to --compilecache={yes|no} (%s)", optarg); + jl_errorf("julia: invalid argument to --compiled-modules={yes|no} (%s)", optarg); break; case 'C': // cpu-target jl_options.cpu_target = strdup(optarg); diff --git a/src/julia.h b/src/julia.h index 23e00ceb9c1af..398d7eeeba9ca 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1702,8 +1702,8 @@ typedef struct { int8_t worker; const char *cookie; int8_t handle_signals; - int8_t use_precompiled; - int8_t use_compilecache; + int8_t use_sysimage_native_code; + int8_t use_compiled_modules; const char *bindto; const char *outputbc; const char *outputunoptbc; @@ -1769,11 +1769,11 @@ JL_DLLEXPORT int jl_generating_output(void); #define JL_OPTIONS_HANDLE_SIGNALS_ON 1 #define JL_OPTIONS_HANDLE_SIGNALS_OFF 0 -#define JL_OPTIONS_USE_PRECOMPILED_YES 1 -#define JL_OPTIONS_USE_PRECOMPILED_NO 0 +#define JL_OPTIONS_USE_SYSIMAGE_NATIVE_CODE_YES 1 +#define JL_OPTIONS_USE_SYSIMAGE_NATIVE_CODE_NO 0 -#define JL_OPTIONS_USE_COMPILECACHE_YES 1 -#define JL_OPTIONS_USE_COMPILECACHE_NO 0 +#define JL_OPTIONS_USE_COMPILED_MODULES_YES 1 +#define JL_OPTIONS_USE_COMPILED_MODULES_NO 0 // Version information #include "julia_version.h" diff --git a/src/staticdata.c b/src/staticdata.c index 0d431e05ba992..a5d8146fca578 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -163,7 +163,7 @@ static void jl_load_sysimg_so(void) #endif int imaging_mode = jl_generating_output() && !jl_options.incremental; // in --build mode only use sysimg data, not precompiled native code - if (!imaging_mode && jl_options.use_precompiled==JL_OPTIONS_USE_PRECOMPILED_YES) { + if (!imaging_mode && jl_options.use_sysimage_native_code==JL_OPTIONS_USE_SYSIMAGE_NATIVE_CODE_YES) { sysimg_gvars_base = (uintptr_t*)jl_dlsym(jl_sysimg_handle, "jl_sysimg_gvars_base"); sysimg_gvars_offsets = (const int32_t*)jl_dlsym(jl_sysimg_handle, "jl_sysimg_gvars_offsets"); diff --git a/test/cmdlineargs.jl b/test/cmdlineargs.jl index b7160b2da8a46..ba68561a67c90 100644 --- a/test/cmdlineargs.jl +++ b/test/cmdlineargs.jl @@ -9,7 +9,7 @@ if Sys.iswindows() end end -let exename = `$(Base.julia_cmd()) --precompiled=yes --startup-file=no` +let exename = `$(Base.julia_cmd()) --sysimage-native-code=yes --startup-file=no` # --version let v = split(read(`$exename -v`, String), "julia version ")[end] @test Base.VERSION_STRING == chomp(v) @@ -75,8 +75,8 @@ let exename = `$(Base.julia_cmd()) --precompiled=yes --startup-file=no` # --cpu-target # NOTE: this test only holds true if image_file is a shared library. if Libdl.dlopen_e(unsafe_string(Base.JLOptions().image_file)) != C_NULL - @test !success(`$exename -C invalidtarget --precompiled=yes`) - @test !success(`$exename --cpu-target=invalidtarget --precompiled=yes`) + @test !success(`$exename -C invalidtarget --sysimage-native-code=yes`) + @test !success(`$exename --cpu-target=invalidtarget --sysimage-native-code=yes`) else warn("--cpu-target test not runnable") end @@ -342,13 +342,13 @@ let exename = `$(Base.julia_cmd()) --precompiled=yes --startup-file=no` @test readchomp(pipeline(ignorestatus(`$exename --startup-file=no -e "@show ARGS" -now -- julia RUN.jl`), stderr=catcmd)) == "ERROR: unknown option `-n`" - # --compilecache={yes|no} - @test readchomp(`$exename -E "Bool(Base.JLOptions().use_compilecache)"`) == "true" - @test readchomp(`$exename --compilecache=yes -E - "Bool(Base.JLOptions().use_compilecache)"`) == "true" - @test readchomp(`$exename --compilecache=no -E - "Bool(Base.JLOptions().use_compilecache)"`) == "false" - @test !success(`$exename --compilecache=foo -e "exit(0)"`) + # --compiled-modules={yes|no} + @test readchomp(`$exename -E "Bool(Base.JLOptions().use_compiled_modules)"`) == "true" + @test readchomp(`$exename --compiled-modules=yes -E + "Bool(Base.JLOptions().use_compiled_modules)"`) == "true" + @test readchomp(`$exename --compiled-modules=no -E + "Bool(Base.JLOptions().use_compiled_modules)"`) == "false" + @test !success(`$exename --compiled-modules=foo -e "exit(0)"`) # issue #12671, starting from a non-directory # rm(dir) fails on windows with Permission denied @@ -399,7 +399,7 @@ let exename = joinpath(JULIA_HOME, Base.julia_exename()), end end -let exename = `$(Base.julia_cmd()) --precompiled=yes` +let exename = `$(Base.julia_cmd()) --sysimage-native-code=yes` # --startup-file let JL_OPTIONS_STARTUPFILE_ON = 1, JL_OPTIONS_STARTUPFILE_OFF = 2 @@ -422,17 +422,17 @@ run(pipeline(DevNull, `$(joinpath(JULIA_HOME, Base.julia_exename())) --lisp`, De @test_throws ErrorException run(pipeline(DevNull, pipeline(`$(joinpath(JULIA_HOME, Base.julia_exename())) -Cnative --lisp`, stderr=DevNull), DevNull)) -# --precompiled={yes|no} +# --sysimage-native-code={yes|no} let exename = `$(Base.julia_cmd()) --startup-file=no` - @test readchomp(`$exename --precompiled=yes -E - "Bool(Base.JLOptions().use_precompiled)"`) == "true" - @test readchomp(`$exename --precompiled=no -E - "Bool(Base.JLOptions().use_precompiled)"`) == "false" + @test readchomp(`$exename --sysimage-native-code=yes -E + "Bool(Base.JLOptions().use_sysimage_native_code)"`) == "true" + @test readchomp(`$exename --sysimage-native-code=no -E + "Bool(Base.JLOptions().use_sysimage_native_code)"`) == "false" end # backtrace contains type and line number info (esp. on windows #17179) for precomp in ("yes", "no") - bt = read(pipeline(ignorestatus(`$(Base.julia_cmd()) --startup-file=no --precompiled=$precomp + bt = read(pipeline(ignorestatus(`$(Base.julia_cmd()) --startup-file=no --sysimage-native-code=$precomp -E 'include("____nonexistent_file")'`), stderr=catcmd), String) @test contains(bt, "include_relative(::Module, ::String) at $(joinpath(".", "loading.jl"))") lno = match(r"at \.[\/\\]loading\.jl:(\d+)", bt) diff --git a/test/compile.jl b/test/compile.jl index e47f1128de02a..18aef37377dbc 100644 --- a/test/compile.jl +++ b/test/compile.jl @@ -387,7 +387,7 @@ finally rm(dir2, recursive=true) end -# test --compilecache=no command line option +# test --compiled-modules=no command line option let dir = mktempdir(), Time_module = :Time4b3a94a1a081a8cb @@ -406,7 +406,7 @@ let dir = mktempdir(), Base.compilecache(:Time4b3a94a1a081a8cb) end) - exename = `$(Base.julia_cmd()) --precompiled=yes --startup-file=no` + exename = `$(Base.julia_cmd()) --compiled-modules=yes --startup-file=no` testcode = """ insert!(LOAD_PATH, 1, $(repr(dir))) @@ -415,12 +415,12 @@ let dir = mktempdir(), getfield($Time_module, :time) """ - t1_yes = readchomp(`$exename --compilecache=yes -E $(testcode)`) - t2_yes = readchomp(`$exename --compilecache=yes -E $(testcode)`) + t1_yes = readchomp(`$exename --compiled-modules=yes -E $(testcode)`) + t2_yes = readchomp(`$exename --compiled-modules=yes -E $(testcode)`) @test t1_yes == t2_yes - t1_no = readchomp(`$exename --compilecache=no -E $(testcode)`) - t2_no = readchomp(`$exename --compilecache=no -E $(testcode)`) + t1_no = readchomp(`$exename --compiled-modules=no -E $(testcode)`) + t2_no = readchomp(`$exename --compiled-modules=no -E $(testcode)`) @test t1_no != t2_no @test parse(Float64, t1_no) < parse(Float64, t2_no) diff --git a/test/loading.jl b/test/loading.jl index a7d29f31cabc1..fc405732f5dc5 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -41,7 +41,7 @@ include_string_test_func = include_string(@__MODULE__, "include_string_test() = @test isdir(@__DIR__) @test @__DIR__() == dirname(@__FILE__) -let exename = `$(Base.julia_cmd()) --precompiled=yes --startup-file=no`, +let exename = `$(Base.julia_cmd()) --compiled-modules=yes --startup-file=no`, wd = sprint(show, abspath(pwd(), "")), s_dir = sprint(show, joinpath(realpath(tempdir()), "")) @test wd != s_dir diff --git a/test/workspace.jl b/test/workspace.jl index 16c48357ec219..b65f09df63a0e 100644 --- a/test/workspace.jl +++ b/test/workspace.jl @@ -67,7 +67,7 @@ mktempdir() do dir nothing """) # Ensure that STDIO doesn't get swallowed (helps with debugging) - cmd = `$(Base.julia_cmd()) --startup-file=no --precompiled=yes --compilecache=yes $(joinpath(dir, "testdriver.jl"))` + cmd = `$(Base.julia_cmd()) --startup-file=no --sysimage-native-code=yes --compiled-modules=yes $(joinpath(dir, "testdriver.jl"))` @test success(pipeline(cmd, stdout=STDOUT, stderr=STDERR)) end end From 22987e393241514ee16a7d04e7db053ee2833953 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Fri, 8 Sep 2017 08:05:11 +0200 Subject: [PATCH 308/324] fix #19182: convert([Un]Signed, x::BigInt) (#23474) --- base/gmp.jl | 5 +++-- test/bigint.jl | 7 +++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/base/gmp.jl b/base/gmp.jl index 2b2afc9fcc558..04e627881c8b9 100644 --- a/base/gmp.jl +++ b/base/gmp.jl @@ -229,6 +229,7 @@ widen(::Type{BigInt}) = BigInt signed(x::BigInt) = x convert(::Type{BigInt}, x::BigInt) = x +convert(::Type{Signed}, x::BigInt) = x hastypemax(::Type{BigInt}) = false @@ -323,7 +324,7 @@ end rem(x::Integer, ::Type{BigInt}) = convert(BigInt, x) -function convert(::Type{T}, x::BigInt) where T<:Unsigned +function convert(::Type{T}, x::BigInt) where T<:Base.BitUnsigned if sizeof(T) < sizeof(Limb) convert(T, convert(Limb,x)) else @@ -332,7 +333,7 @@ function convert(::Type{T}, x::BigInt) where T<:Unsigned end end -function convert(::Type{T}, x::BigInt) where T<:Signed +function convert(::Type{T}, x::BigInt) where T<:Base.BitSigned n = abs(x.size) if sizeof(T) < sizeof(Limb) SLimb = typeof(Signed(one(Limb))) diff --git a/test/bigint.jl b/test/bigint.jl index 6dadc6556cece..180fca5ee4d33 100644 --- a/test/bigint.jl +++ b/test/bigint.jl @@ -374,3 +374,10 @@ end @test BigInt <: Signed @test big(1) isa Signed + +let x = big(1) + @test signed(x) === x + @test convert(Signed, x) === x + @test Signed(x) === x + @test_throws MethodError convert(Unsigned, x) # could change in the future +end From 172f36e002a662dd51e080eb24a818abaf7cea72 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Fri, 8 Sep 2017 08:13:54 +0200 Subject: [PATCH 309/324] REPL: more deleted words pushed into the kill ring (#23593) The two ways of deleting the previous word, deleting the next word and clearing the space now hook into the kill ring. Implements item 2 of #8447. --- base/repl/LineEdit.jl | 48 +++++++++++++++++++++++++++---------------- test/lineedit.jl | 33 ++++++++++++++++++++++++----- 2 files changed, 58 insertions(+), 23 deletions(-) diff --git a/base/repl/LineEdit.jl b/base/repl/LineEdit.jl index 95a05868bce22..ebf27b7a7bad9 100644 --- a/base/repl/LineEdit.jl +++ b/base/repl/LineEdit.jl @@ -621,45 +621,54 @@ function edit_werase(buf::IOBuffer) pos1 = position(buf) char_move_word_left(buf, isspace) pos0 = position(buf) - pos0 < pos1 || return false edit_splice!(buf, pos0 => pos1) - true end function edit_werase(s::MIState) push_undo(s) - edit_werase(buffer(s)) ? refresh_line(s) : pop_undo(s) - :edit_werase + if push_kill!(s, edit_werase(buffer(s)), rev=true) + refresh_line(s) + :edit_werase + else + pop_undo(s) + :ignore + end end function edit_delete_prev_word(buf::IOBuffer) pos1 = position(buf) char_move_word_left(buf) pos0 = position(buf) - pos0 < pos1 || return false edit_splice!(buf, pos0 => pos1) - true end function edit_delete_prev_word(s::MIState) push_undo(s) - edit_delete_prev_word(buffer(s)) ? refresh_line(s) : pop_undo(s) - :edit_delete_prev_word + if push_kill!(s, edit_delete_prev_word(buffer(s)), rev=true) + refresh_line(s) + :edit_delete_prev_word + else + pop_undo(s) + :ignore + end end function edit_delete_next_word(buf::IOBuffer) pos0 = position(buf) char_move_word_right(buf) pos1 = position(buf) - pos0 < pos1 || return false edit_splice!(buf, pos0 => pos1) - true end function edit_delete_next_word(s) push_undo(s) - edit_delete_next_word(buffer(s)) ? refresh_line(s) : pop_undo(s) - :edit_delete_next_word + if push_kill!(s, edit_delete_next_word(buffer(s))) + refresh_line(s) + :edit_delete_next_word + else + pop_undo(s) + :ignore + end end function edit_yank(s::MIState) @@ -687,10 +696,12 @@ function edit_yank_pop(s::MIState, require_previous_yank=true) end end -function push_kill!(s::MIState, killed::String, concat=false) +function push_kill!(s::MIState, killed::String, concat = s.key_repeats > 0; rev=false) isempty(killed) && return false - if concat - s.kill_ring[end] *= killed + if concat && !isempty(s.kill_ring) + s.kill_ring[end] = rev ? + killed * s.kill_ring[end] : # keep expected order for backward deletion + s.kill_ring[end] * killed else push!(s.kill_ring, killed) length(s.kill_ring) > KILL_RING_MAX[] && shift!(s.kill_ring) @@ -708,7 +719,7 @@ function edit_kill_line(s::MIState) killbuf = killbuf[1:end-1] char_move_left(buf) end - push_kill!(s, killbuf, s.key_repeats > 0) || return :ignore + push_kill!(s, killbuf) || return :ignore edit_splice!(buf, pos => position(buf)) refresh_line(s) :edit_kill_line @@ -716,7 +727,7 @@ end function edit_copy_region(s::MIState) buf = buffer(s) - push_kill!(s, content(buf, region(buf))) || return :ignore + push_kill!(s, content(buf, region(buf)), false) || return :ignore if REGION_ANIMATION_DURATION[] > 0.0 edit_exchange_point_and_mark(s) sleep(REGION_ANIMATION_DURATION[]) @@ -727,7 +738,7 @@ end function edit_kill_region(s::MIState) push_undo(s) - if push_kill!(s, edit_splice!(s)) + if push_kill!(s, edit_splice!(s), false) refresh_line(s) :edit_kill_region else @@ -807,6 +818,7 @@ edit_clear(buf::IOBuffer) = truncate(buf, 0) function edit_clear(s::MIState) push_undo(s) + push_kill!(s, content(s), false) || return :ignore edit_clear(buffer(s)) refresh_line(s) :edit_clear diff --git a/test/lineedit.jl b/test/lineedit.jl index e6b50cb14e402..db08ac64c2bc4 100644 --- a/test/lineedit.jl +++ b/test/lineedit.jl @@ -300,14 +300,14 @@ seek(buf,0) buf = IOBuffer("type X\n ") seekend(buf) -@test LineEdit.edit_delete_prev_word(buf) +@test !isempty(LineEdit.edit_delete_prev_word(buf)) @test position(buf) == 5 @test buf.size == 5 @test content(buf) == "type " buf = IOBuffer("4 +aaa+ x") seek(buf,8) -@test LineEdit.edit_delete_prev_word(buf) +@test !isempty(LineEdit.edit_delete_prev_word(buf)) @test position(buf) == 3 @test buf.size == 4 @test content(buf) == "4 +x" @@ -316,11 +316,11 @@ buf = IOBuffer("x = func(arg1,arg2 , arg3)") seekend(buf) LineEdit.char_move_word_left(buf) @test position(buf) == 21 -@test LineEdit.edit_delete_prev_word(buf) +@test !isempty(LineEdit.edit_delete_prev_word(buf)) @test content(buf) == "x = func(arg1,arg3)" -@test LineEdit.edit_delete_prev_word(buf) +@test !isempty(LineEdit.edit_delete_prev_word(buf)) @test content(buf) == "x = func(arg3)" -@test LineEdit.edit_delete_prev_word(buf) +@test !isempty(LineEdit.edit_delete_prev_word(buf)) @test content(buf) == "x = arg3)" # Unicode combining characters @@ -617,6 +617,29 @@ end LineEdit.edit_kill_line(s) @test s.kill_ring[end] == "çhing" @test s.kill_idx == 3 + # repetition (concatenation of killed strings + edit_insert(s, "A B C") + LineEdit.edit_delete_prev_word(s) + s.key_repeats = 1 + LineEdit.edit_delete_prev_word(s) + s.key_repeats = 0 + @test s.kill_ring[end] == "B C" + LineEdit.edit_yank(s) + LineEdit.edit_werase(s) + @test s.kill_ring[end] == "C" + s.key_repeats = 1 + LineEdit.edit_werase(s) + s.key_repeats = 0 + @test s.kill_ring[end] == "B C" + LineEdit.edit_yank(s) + LineEdit.edit_move_word_left(s) + LineEdit.edit_move_word_left(s) + LineEdit.edit_delete_next_word(s) + @test s.kill_ring[end] == "B" + s.key_repeats = 1 + LineEdit.edit_delete_next_word(s) + s.key_repeats = 0 + @test s.kill_ring[end] == "B C" end @testset "undo" begin From 15b29bc17d3ea0434e9e3167407ccdbbdfc36e51 Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Fri, 8 Sep 2017 00:43:59 -0700 Subject: [PATCH 310/324] Remove string "Array" funnybusiness (#23583) * Remove string "Array" funnybusiness * Remove last string things and fix comments * Re-add missing test --- test/linalg/bunchkaufman.jl | 37 +++------ test/linalg/dense.jl | 57 ++------------ test/linalg/diagonal.jl | 142 ++++++++++++++++------------------ test/linalg/eigen.jl | 23 ++---- test/linalg/lu.jl | 48 ++++-------- test/linalg/matmul.jl | 76 +++++------------- test/linalg/schur.jl | 14 ++-- test/linalg/svd.jl | 9 +-- test/linalg/uniformscaling.jl | 18 ++--- 9 files changed, 135 insertions(+), 289 deletions(-) diff --git a/test/linalg/bunchkaufman.jl b/test/linalg/bunchkaufman.jl index d6879e68e5543..30847c6705f31 100644 --- a/test/linalg/bunchkaufman.jl +++ b/test/linalg/bunchkaufman.jl @@ -22,19 +22,14 @@ bimg = randn(n,2)/2 @testset for eltya in (Float32, Float64, Complex64, Complex128, Int) a = eltya == Int ? rand(1:7, n, n) : convert(Matrix{eltya}, eltya <: Complex ? complex.(areal, aimg) : areal) a2 = eltya == Int ? rand(1:7, n, n) : convert(Matrix{eltya}, eltya <: Complex ? complex.(a2real, a2img) : a2real) - @testset for atype in ("Array", "SubArray") - asym = a.'+ a # symmetric indefinite - aher = a' + a # Hermitian indefinite - apd = a' * a # Positive-definite - if atype == "Array" - a = a - a2 = a2 - else - a = view(a , 1:n, 1:n) - a2 = view(a2 , 1:n, 1:n) - aher = view(aher, 1:n, 1:n) - apd = view(apd , 1:n, 1:n) - end + asym = a.'+ a # symmetric indefinite + aher = a' + a # Hermitian indefinite + apd = a' * a # Positive-definite + for (a, a2, aher, apd) in ((a, a2, aher, apd), + (view(a, 1:n, 1:n), + view(a2, 1:n, 1:n), + view(aher, 1:n, 1:n), + view(apd , 1:n, 1:n))) ε = εa = eps(abs(float(one(eltya)))) @testset for eltyb in (Float32, Float64, Complex64, Complex128, Int) @@ -44,13 +39,7 @@ bimg = randn(n,2)/2 @test isa(factorize(asym), LinAlg.BunchKaufman) @test isa(factorize(aher), LinAlg.BunchKaufman) - @testset for btype in ("Array", "SubArray") - if btype == "Array" - b = b - else - b = view(b, 1:n, 1:2) - end - + for b in (b, view(b, 1:n, 1:2)) εb = eps(abs(float(one(eltyb)))) ε = max(εa,εb) @@ -113,13 +102,7 @@ end As3[1, end] -= im for As = (As1, As2, As3) - @testset for Astype in ("Array", "SubArray") - if Astype == "Array" - As = As - else - As = view(As, 1:n, 1:n) - end - + for As in (As, view(As, 1:n, 1:n)) @testset for rook in (false, true) @testset for uplo in (:L, :U) F = bkfact(issymmetric(As) ? Symmetric(As, uplo) : Hermitian(As, uplo), rook) diff --git a/test/linalg/dense.jl b/test/linalg/dense.jl index ea313bc8f7579..ad719ee0fb3cc 100644 --- a/test/linalg/dense.jl +++ b/test/linalg/dense.jl @@ -20,12 +20,7 @@ srand(1234321) ainit = rand(n,n) @testset "for $elty" for elty in (Float32, Float64, Complex64, Complex128) ainit = convert(Matrix{elty}, ainit) - for arraytype in ("Array", "SubArray") - if arraytype == "Array" - a = ainit - else - a = view(ainit, 1:n, 1:n) - end + for a in (copy(ainit), view(ainit, 1:n, 1:n)) @test cond(a,1) ≈ 4.837320054554436e+02 atol=0.01 @test cond(a,2) ≈ 1.960057871514615e+02 atol=0.01 @test cond(a,Inf) ≈ 3.757017682707787e+02 atol=0.01 @@ -60,16 +55,7 @@ bimg = randn(n,2)/2 binit = eltyb == Int ? rand(1:5, n, 2) : convert(Matrix{eltyb}, eltyb <: Complex ? complex.(breal, bimg) : breal) εb = eps(abs(float(one(eltyb)))) ε = max(εa,εb) - - for arraytype in ("Array", "SubArray") - if arraytype == "Array" - a = ainit - b = binit - else - a = view(ainit, 1:n, 1:n) - b = view(binit, 1:n, 1:2) - end - + for (a, b) in ((copy(ainit), copy(binit)), (view(ainit, 1:n, 1:n), view(binit, 1:n, 1:2))) @testset "Solve square general system of equations" begin κ = cond(a,1) x = a \ b @@ -90,15 +76,7 @@ bimg = randn(n,2)/2 end end # for eltyb - for arraytype in ("Array", "SubArray") - if arraytype == "Array" - a = ainit - a2 = ainit2 - else - a = view(ainit, 1:n, 1:n) - a2 = view(ainit2, 1:n, 1:n) - end - + for (a, a2) in ((copy(ainit), copy(ainit2)), (view(ainit, 1:n, 1:n), view(ainit2, 1:n, 1:n))) @testset "Test pinv" begin pinva15 = pinv(a[:,1:n1]) @test a[:,1:n1]*pinva15*a[:,1:n1] ≈ a[:,1:n1] @@ -169,14 +147,9 @@ end # for eltya @testset "test triu/tril bounds checking" begin ainit = rand(5,7) - for arraytype in ("Array", "SubArray") - if arraytype == "Array" - a = ainit - else - a = view(ainit, 1:size(ainit, 1), 1:size(ainit, 2)) - end - @test_throws(ArgumentError,triu(a,8)) + for a in (copy(ainit), view(ainit, 1:size(ainit, 1), 1:size(ainit, 2))) @test_throws(ArgumentError,triu(a,-6)) + @test_throws(ArgumentError,triu(a,8)) @test_throws(ArgumentError,tril(a,8)) @test_throws(ArgumentError,tril(a,-6)) end @@ -253,14 +226,7 @@ end α = elty <: Integer ? randn() : elty <: Complex ? convert(elty, complex(randn(),randn())) : convert(elty, randn()) - for arraytype in ("Array", "SubArray") - if arraytype == "Array" - x = xinit - y = yinit - else - x = view(xinit,1:2:nnorm) - y = view(yinit,1:2:nnorm) - end + for (x, y) in ((copy(xinit), copy(yinit)), (view(xinit,1:2:nnorm), view(yinit,1:2:nnorm))) # Absolute homogeneity @test norm(α*x,-Inf) ≈ abs(α)*norm(x,-Inf) @test norm(α*x,-1) ≈ abs(α)*norm(x,-1) @@ -309,16 +275,7 @@ end α = elty <: Integer ? randn() : elty <: Complex ? convert(elty, complex(randn(),randn())) : convert(elty, randn()) - - for arraytype in ("Array", "SubArray") - if arraytype == "Array" - A = Ainit - B = Binit - else - A = view(Ainit,1:nmat,1:nmat) - B = view(Binit,1:nmat,1:nmat) - end - + for (A, B) in ((copy(Ainit), copy(Binit)), (view(Ainit,1:nmat,1:nmat), view(Binit,1:nmat,1:nmat))) # Absolute homogeneity @test norm(α*A,1) ≈ abs(α)*norm(A,1) elty <: Union{BigFloat,Complex{BigFloat},BigInt} || @test norm(α*A) ≈ abs(α)*norm(A) # two is default diff --git a/test/linalg/diagonal.jl b/test/linalg/diagonal.jl index 4d490c2d6e281..ec7b4f9338a8e 100644 --- a/test/linalg/diagonal.jl +++ b/test/linalg/diagonal.jl @@ -8,19 +8,19 @@ n=12 #Size of matrix problem to test srand(1) @testset for relty in (Float32, Float64, BigFloat), elty in (relty, Complex{relty}) - d=convert(Vector{elty}, randn(n)) - v=convert(Vector{elty}, randn(n)) - U=convert(Matrix{elty}, randn(n,n)) + dd=convert(Vector{elty}, randn(n)) + vv=convert(Vector{elty}, randn(n)) + UU=convert(Matrix{elty}, randn(n,n)) if elty <: Complex - d+=im*convert(Vector{elty}, randn(n)) - v+=im*convert(Vector{elty}, randn(n)) - U+=im*convert(Matrix{elty}, randn(n,n)) + dd+=im*convert(Vector{elty}, randn(n)) + vv+=im*convert(Vector{elty}, randn(n)) + UU+=im*convert(Matrix{elty}, randn(n,n)) end - D = Diagonal(d) - DM = diagm(d) + D = Diagonal(dd) + DM = diagm(dd) @testset "constructor" begin - for x in (d, GenericArray(d)) + for x in (dd, GenericArray(dd)) @test Diagonal(x)::Diagonal{elty,typeof(x)} == DM @test Diagonal(x).diag === x @test Diagonal{elty}(x)::Diagonal{elty,typeof(x)} == DM @@ -38,9 +38,9 @@ srand(1) @test Array(abs.(D)) == abs.(DM) @test Array(imag(D)) == imag(DM) - @test parent(D) == d - @test diag(D) == d - @test D[1,1] == d[1] + @test parent(D) == dd + @test diag(D) == dd + @test D[1,1] == dd[1] @test D[1,2] == 0 @test issymmetric(D) @@ -73,61 +73,49 @@ srand(1) end @testset "Linear solve" begin - let vv = v, UU = U - @testset for atype in ("Array", "SubArray") - if atype == "Array" - v = vv - U = UU - else - v = view(vv, 1:n) - U = view(UU, 1:n, 1:2) - end - - @test D*v ≈ DM*v atol=n*eps(relty)*(1+(elty<:Complex)) - @test D*U ≈ DM*U atol=n^2*eps(relty)*(1+(elty<:Complex)) - - @test U.'*D ≈ U.'*Array(D) - @test U'*D ≈ U'*Array(D) - - if relty != BigFloat - atol_two = 2n^2 * eps(relty) * (1 + (elty <: Complex)) - atol_three = 2n^3 * eps(relty) * (1 + (elty <: Complex)) - @test D\v ≈ DM\v atol=atol_two - @test D\U ≈ DM\U atol=atol_three - @test A_ldiv_B!(D, copy(v)) ≈ DM\v atol=atol_two - @test At_ldiv_B!(D, copy(v)) ≈ DM\v atol=atol_two - @test Ac_ldiv_B!(conj(D), copy(v)) ≈ DM\v atol=atol_two - @test A_ldiv_B!(D, copy(U)) ≈ DM\U atol=atol_three - @test At_ldiv_B!(D, copy(U)) ≈ DM\U atol=atol_three - @test Ac_ldiv_B!(conj(D), copy(U)) ≈ DM\U atol=atol_three - Uc = adjoint(U) - target = scale!(Uc, inv.(D.diag)) - @test A_rdiv_B!(Uc, D) ≈ target atol=atol_three - @test_throws DimensionMismatch A_rdiv_B!(eye(elty, n-1), D) - @test_throws SingularException A_rdiv_B!(Uc, zeros(D)) - @test A_rdiv_Bt!(Uc, D) ≈ target atol=atol_three - @test A_rdiv_Bc!(Uc, conj(D)) ≈ target atol=atol_three - @test A_ldiv_B!(D, eye(D)) ≈ D\eye(D) atol=atol_three - @test_throws DimensionMismatch A_ldiv_B!(D, ones(elty, n + 1)) - @test_throws SingularException A_ldiv_B!(Diagonal(zeros(relty, n)), copy(v)) - b = rand(elty, n, n) - b = sparse(b) - @test A_ldiv_B!(D, copy(b)) ≈ Array(D)\Array(b) - @test_throws SingularException A_ldiv_B!(Diagonal(zeros(elty, n)), copy(b)) - b = view(rand(elty, n), collect(1:n)) - b2 = copy(b) - c = A_ldiv_B!(D, b) - d = Array(D)\b2 - for i in 1:n - @test c[i] ≈ d[i] - end - @test_throws SingularException A_ldiv_B!(Diagonal(zeros(elty, n)), b) - b = rand(elty, n+1, n+1) - b = sparse(b) - @test_throws DimensionMismatch A_ldiv_B!(D, copy(b)) - b = view(rand(elty, n+1), collect(1:n+1)) - @test_throws DimensionMismatch A_ldiv_B!(D, b) - end + for (v, U) in ((vv, UU), (view(vv, 1:n), view(UU, 1:n, 1:2))) + @test D*v ≈ DM*v atol=n*eps(relty)*(1+(elty<:Complex)) + @test D*U ≈ DM*U atol=n^2*eps(relty)*(1+(elty<:Complex)) + + @test U.'*D ≈ U.'*Array(D) + @test U'*D ≈ U'*Array(D) + + if relty != BigFloat + atol_two = 2n^2 * eps(relty) * (1 + (elty <: Complex)) + atol_three = 2n^3 * eps(relty) * (1 + (elty <: Complex)) + @test D\v ≈ DM\v atol=atol_two + @test D\U ≈ DM\U atol=atol_three + @test A_ldiv_B!(D, copy(v)) ≈ DM\v atol=atol_two + @test At_ldiv_B!(D, copy(v)) ≈ DM\v atol=atol_two + @test Ac_ldiv_B!(conj(D), copy(v)) ≈ DM\v atol=atol_two + @test A_ldiv_B!(D, copy(U)) ≈ DM\U atol=atol_three + @test At_ldiv_B!(D, copy(U)) ≈ DM\U atol=atol_three + @test Ac_ldiv_B!(conj(D), copy(U)) ≈ DM\U atol=atol_three + Uc = adjoint(U) + target = scale!(Uc, inv.(D.diag)) + @test A_rdiv_B!(Uc, D) ≈ target atol=atol_three + @test_throws DimensionMismatch A_rdiv_B!(eye(elty, n-1), D) + @test_throws SingularException A_rdiv_B!(Uc, zeros(D)) + @test A_rdiv_Bt!(Uc, D) ≈ target atol=atol_three + @test A_rdiv_Bc!(Uc, conj(D)) ≈ target atol=atol_three + @test A_ldiv_B!(D, eye(D)) ≈ D\eye(D) atol=atol_three + @test_throws DimensionMismatch A_ldiv_B!(D, ones(elty, n + 1)) + @test_throws SingularException A_ldiv_B!(Diagonal(zeros(relty, n)), copy(v)) + b = rand(elty, n, n) + b = sparse(b) + @test A_ldiv_B!(D, copy(b)) ≈ Array(D)\Array(b) + @test_throws SingularException A_ldiv_B!(Diagonal(zeros(elty, n)), copy(b)) + b = view(rand(elty, n), collect(1:n)) + b2 = copy(b) + c = A_ldiv_B!(D, b) + d = Array(D)\b2 + @test c ≈ d + @test_throws SingularException A_ldiv_B!(Diagonal(zeros(elty, n)), b) + b = rand(elty, n+1, n+1) + b = sparse(b) + @test_throws DimensionMismatch A_ldiv_B!(D, copy(b)) + b = view(rand(elty, n+1), collect(1:n+1)) + @test_throws DimensionMismatch A_ldiv_B!(D, b) end end end @@ -163,15 +151,15 @@ srand(1) @test D\D2 ≈ Diagonal(D2.diag./D.diag) # Performance specialisations for A*_mul_B! - vv = similar(v) - @test (r = full(D) * v ; A_mul_B!(vv, D, v) ≈ r ≈ vv) - @test (r = full(D)' * v ; Ac_mul_B!(vv, D, v) ≈ r ≈ vv) - @test (r = full(D).' * v ; At_mul_B!(vv, D, v) ≈ r ≈ vv) + vvv = similar(vv) + @test (r = full(D) * vv ; A_mul_B!(vvv, D, vv) ≈ r ≈ vvv) + @test (r = full(D)' * vv ; Ac_mul_B!(vvv, D, vv) ≈ r ≈ vvv) + @test (r = full(D).' * vv ; At_mul_B!(vvv, D, vv) ≈ r ≈ vvv) - UU = similar(U) - @test (r = full(D) * U ; A_mul_B!(UU, D, U) ≈ r ≈ UU) - @test (r = full(D)' * U ; Ac_mul_B!(UU, D, U) ≈ r ≈ UU) - @test (r = full(D).' * U ; At_mul_B!(UU, D, U) ≈ r ≈ UU) + UUU = similar(UU) + @test (r = full(D) * UU ; A_mul_B!(UUU, D, UU) ≈ r ≈ UUU) + @test (r = full(D)' * UU ; Ac_mul_B!(UUU, D, UU) ≈ r ≈ UUU) + @test (r = full(D).' * UU ; At_mul_B!(UUU, D, UU) ≈ r ≈ UUU) # make sure that A_mul_B{c,t}! works with B as a Diagonal VV = Array(D) @@ -225,8 +213,8 @@ srand(1) @test adjoint(D) == conj(D) end # Translates to Ac/t_mul_B, which is specialized after issue 21286 - @test(D' * v == conj(D) * v) - @test(D.' * v == D * v) + @test(D' * vv == conj(D) * vv) + @test(D.' * vv == D * vv) end #logdet diff --git a/test/linalg/eigen.jl b/test/linalg/eigen.jl index 5e6ec48db16f2..edfa42c7e7991 100644 --- a/test/linalg/eigen.jl +++ b/test/linalg/eigen.jl @@ -19,14 +19,10 @@ aimg = randn(n,n)/2 aa = eltya == Int ? rand(1:7, n, n) : convert(Matrix{eltya}, eltya <: Complex ? complex.(areal, aimg) : areal) asym = aa'+aa # symmetric indefinite apd = aa'*aa # symmetric positive-definite - @testset for atype in ("Array", "SubArray") - if atype == "Array" - a = aa - else - a = view(aa, 1:n, 1:n) - asym = view(asym, 1:n, 1:n) - apd = view(apd, 1:n, 1:n) - end + for (a, asym, apd) in ((aa, asym, apd), + (view(aa, 1:n, 1:n), + view(asym, 1:n, 1:n), + view(apd, 1:n, 1:n))) ε = εa = eps(abs(float(one(eltya)))) α = rand(eltya) @@ -56,7 +52,7 @@ aimg = randn(n,n)/2 @test_throws DomainError eigmax(a - a') end @testset "symmetric generalized eigenproblem" begin - if atype == "Array" + if isa(a, Array) asym_sg = asym[1:n1, 1:n1] a_sg = a[:,n1+1:n2] else @@ -77,7 +73,7 @@ aimg = randn(n,n)/2 @test v == f[:vectors] end @testset "Non-symmetric generalized eigenproblem" begin - if atype == "Array" + if isa(a, Array) a1_nsg = a[1:n1, 1:n1] a2_nsg = a[n1+1:n2, n1+1:n2] else @@ -110,12 +106,7 @@ end # test a matrix larger than 140-by-140 for #14174 let aa = rand(200, 200) - for atype in ("Array", "SubArray") - if atype == "Array" - a = aa - else - a = view(aa, 1:n, 1:n) - end + for a in (aa, view(aa, 1:n, 1:n)) f = eigfact(a) @test a ≈ f[:vectors] * Diagonal(f[:values]) / f[:vectors] end diff --git a/test/linalg/lu.jl b/test/linalg/lu.jl index dd5a84b7904d1..bcb09e2bf9ead 100644 --- a/test/linalg/lu.jl +++ b/test/linalg/lu.jl @@ -69,23 +69,16 @@ dimg = randn(n)/2 lstring = sprint(show,l) ustring = sprint(show,u) @test sprint(show,lua) == "$(typeof(lua)) with factors L and U:\n$lstring\n$ustring\nsuccessful: true" - let Bs = b, Cs = c - @testset for atype in ("Array", "SubArray") - if atype == "Array" - b = Bs - c = Cs - else - b = view(Bs, 1:n, 1) - c = view(Cs, 1:n) - end - @test norm(a*(lua\b) - b, 1) < ε*κ*n*2 # Two because the right hand side has two columns - @test norm(a'*(lua'\b) - b, 1) < ε*κ*n*2 # Two because the right hand side has two columns + let Bs = copy(b), Cs = copy(c) + for (bb, cc) in ((Bs, Cs), (view(Bs, 1:n, 1), view(Cs, 1:n))) + @test norm(a*(lua\bb) - bb, 1) < ε*κ*n*2 # Two because the right hand side has two columns + @test norm(a'*(lua'\bb) - bb, 1) < ε*κ*n*2 # Two because the right hand side has two columns @test norm(a'*(lua'\a') - a', 1) < ε*κ*n^2 - @test norm(a*(lua\c) - c, 1) < ε*κ*n # c is a vector - @test norm(a'*(lua'\c) - c, 1) < ε*κ*n # c is a vector + @test norm(a*(lua\cc) - cc, 1) < ε*κ*n # cc is a vector + @test norm(a'*(lua'\cc) - cc, 1) < ε*κ*n # cc is a vector @test AbstractArray(lua) ≈ a - @test norm(a.'*(lua.'\b) - b,1) < ε*κ*n*2 # Two because the right hand side has two columns - @test norm(a.'*(lua.'\c) - c,1) < ε*κ*n + @test norm(a.'*(lua.'\bb) - bb,1) < ε*κ*n*2 # Two because the right hand side has two columns + @test norm(a.'*(lua.'\cc) - cc,1) < ε*κ*n end # Test whether Ax_ldiv_B!(y, LU, x) indeed overwrites y @@ -128,23 +121,17 @@ dimg = randn(n)/2 @test_throws DimensionMismatch lud.'\f @test_throws DimensionMismatch lud'\f @test_throws DimensionMismatch Base.LinAlg.At_ldiv_B!(lud, f) - let Bs = b - @testset for atype in ("Array", "SubArray") - if atype == "Array" - b = Bs - else - b = view(Bs, 1:n, 1) - end - - @test norm(d*(lud\b) - b, 1) < ε*κd*n*2 # Two because the right hand side has two columns + let Bs = copy(b) + for bb in (Bs, view(Bs, 1:n, 1)) + @test norm(d*(lud\bb) - bb, 1) < ε*κd*n*2 # Two because the right hand side has two columns if eltya <: Real - @test norm((lud.'\b) - Array(d.')\b, 1) < ε*κd*n*2 # Two because the right hand side has two columns + @test norm((lud.'\bb) - Array(d.')\bb, 1) < ε*κd*n*2 # Two because the right hand side has two columns if eltya != Int && eltyb != Int - @test norm(Base.LinAlg.At_ldiv_B!(lud, copy(b)) - Array(d.')\b, 1) < ε*κd*n*2 + @test norm(Base.LinAlg.At_ldiv_B!(lud, copy(bb)) - Array(d.')\bb, 1) < ε*κd*n*2 end end if eltya <: Complex - @test norm((lud'\b) - Array(d')\b, 1) < ε*κd*n*2 # Two because the right hand side has two columns + @test norm((lud'\bb) - Array(d')\bb, 1) < ε*κd*n*2 # Two because the right hand side has two columns end end end @@ -201,12 +188,7 @@ end @test l[invperm(p),:]*u ≈ a @test a*inv(lua) ≈ eye(n) let Bs = b - for atype in ("Array", "SubArray") - if atype == "Array" - b = Bs - else - b = view(Bs, 1:n, 1) - end + for b in (Bs, view(Bs, 1:n, 1)) @test a*(lua\b) ≈ b end end diff --git a/test/linalg/matmul.jl b/test/linalg/matmul.jl index 5e27672df6a0d..3b8f6d18257e9 100644 --- a/test/linalg/matmul.jl +++ b/test/linalg/matmul.jl @@ -23,16 +23,13 @@ let BB = [5 6; 7 8] AAi = AA+(0.5*im).*BB BBi = BB+(2.5*im).*AA[[2,1],[2,1]] - for Atype = ["Array", "SubArray"], Btype = ["Array", "SubArray"] - A = Atype == "Array" ? AA : view(AA, 1:2, 1:2) - B = Btype == "Array" ? BB : view(BB, 1:2, 1:2) + for A in (copy(AA), view(AA, 1:2, 1:2)), B in (copy(BB), view(BB, 1:2, 1:2)) @test A*B == [19 22; 43 50] @test At_mul_B(A, B) == [26 30; 38 44] @test A_mul_Bt(A, B) == [17 23; 39 53] @test At_mul_Bt(A, B) == [23 31; 34 46] - - Ai = Atype == "Array" ? AAi : view(AAi, 1:2, 1:2) - Bi = Btype == "Array" ? BBi : view(BBi, 1:2, 1:2) + end + for Ai in (copy(AAi), view(AAi, 1:2, 1:2)), Bi in (copy(BBi), view(BBi, 1:2, 1:2)) @test Ai*Bi == [-21+53.5im -4.25+51.5im; -12+95.5im 13.75+85.5im] @test Ac_mul_B(Ai, Bi) == [68.5-12im 57.5-28im; 88-3im 76.5-25im] @test A_mul_Bc(Ai, Bi) == [64.5+5.5im 43+31.5im; 104-18.5im 80.5+31.5im] @@ -48,16 +45,13 @@ let BB = [1 0 5; 6 -10 3; 2 -4 -1] AAi = AA+(0.5*im).*BB BBi = BB+(2.5*im).*AA[[2,1,3],[2,3,1]] - for Atype = ["Array", "SubArray"], Btype = ["Array", "SubArray"] - A = Atype == "Array" ? AA : view(AA, 1:3, 1:3) - B = Btype == "Array" ? BB : view(BB, 1:3, 1:3) + for A in (copy(AA), view(AA, 1:3, 1:3)), B in (copy(BB), view(BB, 1:3, 1:3)) @test A*B == [-26 38 -27; 1 -4 -6; 28 -46 15] @test Ac_mul_B(A, B) == [-6 2 -25; 3 -12 -18; 12 -26 -11] @test A_mul_Bc(A, B) == [-14 0 6; 4 -3 -3; 22 -6 -12] @test Ac_mul_Bc(A, B) == [6 -8 -6; 12 -9 -9; 18 -10 -12] - - Ai = Atype == "Array" ? AAi : view(AAi, 1:3, 1:3) - Bi = Btype == "Array" ? BBi : view(BBi, 1:3, 1:3) + end + for Ai in (copy(AAi), view(AAi, 1:3, 1:3)), Bi in (copy(BBi), view(BBi, 1:3, 1:3)) @test Ai*Bi == [-44.75+13im 11.75-25im -38.25+30im; -47.75-16.5im -51.5+51.5im -56+6im; 16.75-4.5im -53.5+52im -15.5im] @test Ac_mul_B(Ai, Bi) == [-21+2im -1.75+49im -51.25+19.5im; 25.5+56.5im -7-35.5im 22+35.5im; -3+12im -32.25+43im -34.75-2.5im] @test A_mul_Bc(Ai, Bi) == [-20.25+15.5im -28.75-54.5im 22.25+68.5im; -12.25+13im -15.5+75im -23+27im; 18.25+im 1.5+94.5im -27-54.5im] @@ -86,26 +80,19 @@ end let AA = [1 2 3; 4 5 6] .- 3 BB = [2 -2; 3 -5; -4 7] - for Atype = ["Array", "SubArray"], Btype = ["Array", "SubArray"] - A = Atype == "Array" ? AA : view(AA, 1:2, 1:3) - B = Btype == "Array" ? BB : view(BB, 1:3, 1:2) + for A in (copy(AA), view(AA, 1:2, 1:3)), B in (copy(BB), view(BB, 1:3, 1:2)) @test A*B == [-7 9; -4 9] @test At_mul_Bt(A, B) == [-6 -11 15; -6 -13 18; -6 -15 21] end AA = ones(Int, 2, 100) BB = ones(Int, 100, 3) - for Atype = ["Array", "SubArray"], Btype = ["Array", "SubArray"] - A = Atype == "Array" ? AA : view(AA, 1:2, 1:100) - B = Btype == "Array" ? BB : view(BB, 1:100, 1:3) + for A in (copy(AA), view(AA, 1:2, 1:100)), B in (copy(BB), view(BB, 1:100, 1:3)) @test A*B == [100 100 100; 100 100 100] end AA = rand(1:20, 5, 5) .- 10 BB = rand(1:20, 5, 5) .- 10 CC = Array{Int}(size(AA, 1), size(BB, 2)) - for Atype = ["Array", "SubArray"], Btype = ["Array", "SubArray"], Ctype = ["Array", "SubArray"] - A = Atype == "Array" ? AA : view(AA, 1:5, 1:5) - B = Btype == "Array" ? BB : view(BB, 1:5, 1:5) - C = Btype == "Array" ? CC : view(CC, 1:5, 1:5) + for A in (copy(AA), view(AA, 1:5, 1:5)), B in (copy(BB), view(BB, 1:5, 1:5)), C in (copy(CC), view(CC, 1:5, 1:5)) @test At_mul_B(A, B) == A'*B @test A_mul_Bt(A, B) == A*B' # Preallocated @@ -121,9 +108,7 @@ let end vv = [1,2] CC = Array{Int}(2, 2) - for vtype = ["Array", "SubArray"], Ctype = ["Array", "SubArray"] - v = vtype == "Array" ? vv : view(vv, 1:2) - C = Ctype == "Array" ? CC : view(CC, 1:2, 1:2) + for v in (copy(vv), view(vv, 1:2)), C in (copy(CC), view(CC, 1:2, 1:2)) @test @inferred(A_mul_Bc!(C, v, v)) == [1 2; 2 4] end end @@ -132,24 +117,18 @@ end let AA = rand(5,5) BB = rand(5) - for Atype = ["Array", "SubArray"], Btype = ["Array", "SubArray"] - A = Atype == "Array" ? AA : view(AA, 1:5, 1:5) - B = Btype == "Array" ? BB : view(BB, 1:5) + for A in (copy(AA), view(AA, 1:5, 1:5)), B in (copy(BB), view(BB, 1:5)) @test_throws DimensionMismatch Base.LinAlg.generic_matvecmul!(zeros(6),'N',A,B) @test_throws DimensionMismatch Base.LinAlg.generic_matvecmul!(B,'N',A,zeros(6)) end vv = [1,2,3] CC = Array{Int}(3, 3) - for vtype = ["Array", "SubArray"], Ctype = ["Array", "SubArray"] - v = vtype == "Array" ? vv : view(vv, 1:3) - C = Ctype == "Array" ? CC : view(CC, 1:3, 1:3) + for v in (copy(vv), view(vv, 1:3)), C in (copy(CC), view(CC, 1:3, 1:3)) @test A_mul_Bt!(C, v, v) == v*v' end vvf = map(Float64,vv) CC = Array{Float64}(3, 3) - for vtype = ["Array", "SubArray"], Ctype = ["Array", "SubArray"] - vf = vtype == "Array" ? vvf : view(vvf, 1:3) - C = Ctype == "Array" ? CC : view(CC, 1:3, 1:3) + for vf in (copy(vvf), view(vvf, 1:3)), C in (copy(CC), view(CC, 1:3, 1:3)) @test A_mul_Bt!(C, vf, vf) == vf*vf' end end @@ -159,10 +138,7 @@ let AA = rand(Float64,6,6) BB = rand(Float64,6,6) CC = zeros(Float64,6,6) - for Atype = ["Array", "SubArray"], Btype = ["Array", "SubArray"], Ctype = ["Array", "SubArray"] - A = Atype == "Array" ? AA : view(AA, 1:6, 1:6) - B = Btype == "Array" ? BB : view(BB, 1:6, 1:6) - C = Ctype == "Array" ? CC : view(CC, 1:6, 1:6) + for A in (copy(AA), view(AA, 1:6, 1:6)), B in (copy(BB), view(BB, 1:6, 1:6)), C in (copy(CC), view(CC, 1:6, 1:6)) @test Base.LinAlg.At_mul_Bt!(C,A,B) == A.'*B.' @test Base.LinAlg.A_mul_Bc!(C,A,B) == A*B.' @test Base.LinAlg.Ac_mul_B!(C,A,B) == A.'*B @@ -202,8 +178,7 @@ end let AA = reshape(1:1503, 501, 3).-750.0 res = Float64[135228751 9979252 -115270247; 9979252 10481254 10983256; -115270247 10983256 137236759] - for Atype = ["Array", "SubArray"] - A = Atype == "Array" ? AA : view(AA, 1:501, 1:3) + for A in (copy(AA), view(AA, 1:501, 1:3)) @test At_mul_B(A, A) == res @test A_mul_Bt(A',A') == res end @@ -224,8 +199,7 @@ end # matmul for types w/o sizeof (issue #1282) let AA = fill(complex(1,1), 10, 10) - for Atype = ["Array", "SubArray"] - A = Atype == "Array" ? AA : view(AA, 1:10, 1:10) + for A in (copy(AA), view(AA, 1:10, 1:10)) A2 = A^2 @test A2[1,1] == 20im end @@ -235,14 +209,8 @@ let AA = zeros(5, 5) BB = ones(5) CC = rand(5, 6) - for Atype = ["Array", "SubArray"], Btype = ["Array", "SubArray"] - for Ctype = ["Array", "SubArray"] - A = Atype == "Array" ? AA : view(AA, 1:5, 1:5) - B = Btype == "Array" ? BB : view(BB, 1:5) - C = Ctype == "Array" ? CC : view(CC, 1:5, 1:6) - - @test_throws DimensionMismatch scale!(A, B, C) - end + for A in (copy(AA), view(AA, 1:5, 1:5)), B in (copy(BB), view(BB, 1:5)), C in (copy(CC), view(CC, 1:5, 1:6)) + @test_throws DimensionMismatch scale!(A, B, C) end end @@ -265,9 +233,7 @@ end vecdot_(x,y) = invoke(vecdot, Tuple{Any,Any}, x,y) # generic vecdot let AA = [1+2im 3+4im; 5+6im 7+8im], BB = [2+7im 4+1im; 3+8im 6+5im] - for Atype = ["Array", "SubArray"], Btype = ["Array", "SubArray"] - A = Atype == "Array" ? AA : view(AA, 1:2, 1:2) - B = Btype == "Array" ? BB : view(BB, 1:2, 1:2) + for A in (copy(AA), view(AA, 1:2, 1:2)), B in (copy(BB), view(BB, 1:2, 1:2)) @test vecdot(A,B) == dot(vec(A),vec(B)) == vecdot_(A,B) == vecdot(float.(A),float.(B)) @test vecdot(Int[], Int[]) == 0 == vecdot_(Int[], Int[]) @test_throws MethodError vecdot(Any[], Any[]) @@ -315,9 +281,7 @@ end let aa = rand(3,3) bb = rand(3,3) - for atype = ["Array", "SubArray"], btype = ["Array", "SubArray"] - a = atype == "Array" ? aa : view(aa, 1:3, 1:3) - b = btype == "Array" ? bb : view(bb, 1:3, 1:3) + for a in (copy(aa), view(aa, 1:3, 1:3)), b in (copy(bb), view(bb, 1:3, 1:3)) @test_throws ArgumentError A_mul_B!(a, a, b) @test_throws ArgumentError A_mul_B!(a, b, a) @test_throws ArgumentError A_mul_B!(a, a, a) diff --git a/test/linalg/schur.jl b/test/linalg/schur.jl index dd88bda0c0554..53350074e2245 100644 --- a/test/linalg/schur.jl +++ b/test/linalg/schur.jl @@ -19,14 +19,10 @@ aimg = randn(n,n)/2 a = eltya == Int ? rand(1:7, n, n) : convert(Matrix{eltya}, eltya <: Complex ? complex.(areal, aimg) : areal) asym = a'+a # symmetric indefinite apd = a'*a # symmetric positive-definite - @testset for atype in ("Array", "SubArray") - if atype == "Array" - a = a - else - a = view(a, 1:n, 1:n) - asym = view(asym, 1:n, 1:n) - apd = view(apd, 1:n, 1:n) - end + for (a, asym, apd) in ((a, asym, apd), + (view(a, 1:n, 1:n), + view(asym, 1:n, 1:n), + view(apd, 1:n, 1:n))) ε = εa = eps(abs(float(one(eltya)))) d,v = eig(a) @@ -67,7 +63,7 @@ aimg = randn(n,n)/2 @test O[:Schur] ≈ SchurNew[:Schur] end - if atype == "Array" + if isa(a, Array) a1_sf = a[1:n1, 1:n1] a2_sf = a[n1+1:n2, n1+1:n2] else diff --git a/test/linalg/svd.jl b/test/linalg/svd.jl index d85d4eae26f6e..aa135c8448793 100644 --- a/test/linalg/svd.jl +++ b/test/linalg/svd.jl @@ -47,14 +47,7 @@ a2img = randn(n,n)/2 aa2 = eltya == Int ? rand(1:7, n, n) : convert(Matrix{eltya}, eltya <: Complex ? complex.(a2real, a2img) : a2real) asym = aa'+aa # symmetric indefinite apd = aa'*aa # symmetric positive-definite - @testset for atype in ("Array", "SubArray") - if atype == "Array" - a = aa - a2 = aa2 - else - a = view(aa, 1:n, 1:n) - a2 = view(aa2, 1:n, 1:n) - end + for (a, a2) in ((aa, aa2), (view(aa, 1:n, 1:n), view(aa2, 1:n, 1:n))) ε = εa = eps(abs(float(one(eltya)))) usv = svdfact(a) diff --git a/test/linalg/uniformscaling.jl b/test/linalg/uniformscaling.jl index e745e3ac1f441..88fb277b7b201 100644 --- a/test/linalg/uniformscaling.jl +++ b/test/linalg/uniformscaling.jl @@ -76,15 +76,7 @@ B = bitrand(2,2) @testset "binary ops with matrices" begin let AA = randn(2, 2) for SS in (sprandn(3,3, 0.5), speye(Int, 3)) - @testset for atype in ("Array", "SubArray") - if atype == "Array" - A = AA - S = SS - else - A = view(AA, 1:2, 1:2) - S = view(SS, 1:3, 1:3) - end - + for (A, S) in ((AA, SS), (view(AA, 1:2, 1:2), view(SS, 1:3, 1:3))) @test @inferred(A + I) == A + eye(A) @test @inferred(I + A) == A + eye(A) @test @inferred(I - I) === UniformScaling(0) @@ -113,7 +105,7 @@ B = bitrand(2,2) @test @inferred(I/λ) === UniformScaling(1/λ) @test @inferred(I\J) === J - if atype == "Array" + if isa(A, Array) T = LowerTriangular(randn(3,3)) else T = LowerTriangular(view(randn(3,3), 1:3, 1:3)) @@ -124,7 +116,7 @@ B = bitrand(2,2) @test @inferred(J - T) == J - full(T) @test @inferred(T\I) == inv(T) - if atype == "Array" + if isa(A, Array) T = LinAlg.UnitLowerTriangular(randn(3,3)) else T = LinAlg.UnitLowerTriangular(view(randn(3,3), 1:3, 1:3)) @@ -135,7 +127,7 @@ B = bitrand(2,2) @test @inferred(J - T) == J - full(T) @test @inferred(T\I) == inv(T) - if atype == "Array" + if isa(A, Array) T = UpperTriangular(randn(3,3)) else T = UpperTriangular(view(randn(3,3), 1:3, 1:3)) @@ -146,7 +138,7 @@ B = bitrand(2,2) @test @inferred(J - T) == J - full(T) @test @inferred(T\I) == inv(T) - if atype == "Array" + if isa(A, Array) T = LinAlg.UnitUpperTriangular(randn(3,3)) else T = LinAlg.UnitUpperTriangular(view(randn(3,3), 1:3, 1:3)) From fc2e1e1b7f238338694b35dde6e030e3f39dcf6d Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Fri, 8 Sep 2017 03:53:50 -0700 Subject: [PATCH 311/324] Consolidate pinv tests (#23627) --- test/linalg/pinv.jl | 50 ++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/test/linalg/pinv.jl b/test/linalg/pinv.jl index 0dd73997c441a..0cb26aa7f5648 100644 --- a/test/linalg/pinv.jl +++ b/test/linalg/pinv.jl @@ -142,10 +142,8 @@ end @test apinv isa Vector{eltya} end end -end -@testset "zero valued numbers/vectors/matrices" begin - @testset for eltya in (Float32, Float64, Complex64, Complex128) + @testset "zero valued numbers/vectors/matrices" begin a = pinv(zero(eltya)) @test a ≈ 0.0 @@ -161,29 +159,29 @@ end @test a.diag[1] ≈ 0.0 @test a.diag[2] ≈ 0.0 end -end -@testset "sub-normal numbers/vectors/matrices" begin - @testset for eltya in (Float32, Float64) - a = pinv(realmin(eltya)/100) - @test a ≈ 0.0 - # Complex subnormal - a = pinv(realmin(eltya)/100*(1+1im)) - @test a ≈ 0.0 - - a = pinv([realmin(eltya); realmin(eltya)]/100) - @test a[1] ≈ 0.0 - @test a[2] ≈ 0.0 - # Complex subnormal - a = pinv([realmin(eltya); realmin(eltya)]/100*(1+1im)) - @test a[1] ≈ 0.0 - @test a[2] ≈ 0.0 - a = pinv(Diagonal([realmin(eltya); realmin(eltya)]/100)) - @test a.diag[1] ≈ 0.0 - @test a.diag[2] ≈ 0.0 - # Complex subnormal - a = pinv(Diagonal([realmin(eltya); realmin(eltya)]/100*(1+1im))) - @test a.diag[1] ≈ 0.0 - @test a.diag[2] ≈ 0.0 + if eltya <: Base.LinAlg.BlasReal + @testset "sub-normal numbers/vectors/matrices" begin + a = pinv(realmin(eltya)/100) + @test a ≈ 0.0 + # Complex subnormal + a = pinv(realmin(eltya)/100*(1+1im)) + @test a ≈ 0.0 + + a = pinv([realmin(eltya); realmin(eltya)]/100) + @test a[1] ≈ 0.0 + @test a[2] ≈ 0.0 + # Complex subnormal + a = pinv([realmin(eltya); realmin(eltya)]/100*(1+1im)) + @test a[1] ≈ 0.0 + @test a[2] ≈ 0.0 + a = pinv(Diagonal([realmin(eltya); realmin(eltya)]/100)) + @test a.diag[1] ≈ 0.0 + @test a.diag[2] ≈ 0.0 + # Complex subnormal + a = pinv(Diagonal([realmin(eltya); realmin(eltya)]/100*(1+1im))) + @test a.diag[1] ≈ 0.0 + @test a.diag[2] ≈ 0.0 + end end end From 02fba855595f5f4ea1dc12ec3fe1cff4926ef959 Mon Sep 17 00:00:00 2001 From: Milan Bouchet-Valat Date: Fri, 8 Sep 2017 16:44:52 +0200 Subject: [PATCH 312/324] Rename Range to AbstractRange (#23570) * Rename Range to AbstractRange * Change "a" to "an" before AbstractRange * Update syntax files under contrib/ Rename Range to AbstractRange, and Range1 to UnitRange (which should have been done before). * Add deprecation and NEWS entry * Fix incorrect replacements * Address review comments --- NEWS.md | 2 + base/abstractarray.jl | 32 +++---- base/array.jl | 4 +- base/complex.jl | 2 +- base/dates/arithmetic.jl | 2 +- base/dates/ranges.jl | 6 +- base/deprecated.jl | 6 +- base/exports.jl | 2 +- base/hashing.jl | 2 +- base/linalg/blas.jl | 8 +- base/linalg/dense.jl | 4 +- base/linalg/matmul.jl | 4 +- base/linalg/transpose.jl | 4 +- base/range.jl | 108 +++++++++++------------ base/reduce.jl | 2 +- base/replutil.jl | 4 +- base/reshapedarray.jl | 2 +- base/set.jl | 2 +- base/sort.jl | 14 +-- base/sparse/sparsematrix.jl | 10 +-- base/sparse/sparsevector.jl | 2 +- base/statistics.jl | 6 +- base/strings/basic.jl | 2 +- base/subarray.jl | 8 +- base/twiceprecision.jl | 10 +-- contrib/Julia_Notepad++.xml | 2 +- contrib/julia.hrc | 4 +- contrib/julia.lang | 5 +- contrib/julia.xml | 5 +- doc/src/manual/arrays.md | 2 +- doc/src/manual/control-flow.md | 2 +- doc/src/manual/interfaces.md | 4 +- doc/src/manual/noteworthy-differences.md | 8 +- doc/src/stdlib/collections.md | 6 +- test/TestHelpers.jl | 2 +- test/core.jl | 6 +- test/ranges.jl | 16 ++-- test/sorting.jl | 2 +- test/specificity.jl | 2 +- test/subtype.jl | 4 +- 40 files changed, 161 insertions(+), 157 deletions(-) diff --git a/NEWS.md b/NEWS.md index 8b7a481d5bb5a..66d7885f5f539 100644 --- a/NEWS.md +++ b/NEWS.md @@ -420,6 +420,8 @@ Deprecated or removed * `select`, `select!`, `selectperm` and `selectperm!` have been renamed respectively to `partialsort`, `partialsort!`, `partialsortperm` and `partialsortperm!` ([#23051]). + * The `Range` abstract type has been renamed to `AbstractRange` ([#23570]). + Command-line option changes --------------------------- diff --git a/base/abstractarray.jl b/base/abstractarray.jl index c5847e584038c..bf84de4251aad 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -77,7 +77,7 @@ indices1(A::AbstractArray) = (@_inline_meta; indices(A)[1]) indices1(iter) = OneTo(length(iter)) unsafe_indices(A) = indices(A) -unsafe_indices(r::Range) = (OneTo(unsafe_length(r)),) # Ranges use checked_sub for size +unsafe_indices(r::AbstractRange) = (OneTo(unsafe_length(r)),) # Ranges use checked_sub for size """ linearindices(A) @@ -171,8 +171,8 @@ first(a::AbstractArray) = a[first(eachindex(a))] """ first(coll) -Get the first element of an iterable collection. Returns the start point of a -`Range` even if it is empty. +Get the first element of an iterable collection. Returns the start point of an +`AbstractRange` even if it is empty. # Examples ```jldoctest @@ -194,7 +194,7 @@ end Get the last element of an ordered collection, if it can be computed in O(1) time. This is accomplished by calling [`endof`](@ref) to get the last index. Returns the end -point of a `Range` even if it is empty. +point of an `AbstractRange` even if it is empty. # Examples ```jldoctest @@ -324,7 +324,7 @@ IndexStyle(A::AbstractArray) = IndexStyle(typeof(A)) IndexStyle(::Type{Union{}}) = IndexLinear() IndexStyle(::Type{<:AbstractArray}) = IndexCartesian() IndexStyle(::Type{<:Array}) = IndexLinear() -IndexStyle(::Type{<:Range}) = IndexLinear() +IndexStyle(::Type{<:AbstractRange}) = IndexLinear() IndexStyle(A::AbstractArray, B::AbstractArray) = IndexStyle(IndexStyle(A), IndexStyle(B)) IndexStyle(A::AbstractArray, B::AbstractArray...) = IndexStyle(IndexStyle(A), IndexStyle(B...)) @@ -457,7 +457,7 @@ checkindex(::Type{Bool}, inds::AbstractUnitRange, i) = checkindex(::Type{Bool}, inds::AbstractUnitRange, i::Real) = (first(inds) <= i) & (i <= last(inds)) checkindex(::Type{Bool}, inds::AbstractUnitRange, ::Colon) = true checkindex(::Type{Bool}, inds::AbstractUnitRange, ::Slice) = true -function checkindex(::Type{Bool}, inds::AbstractUnitRange, r::Range) +function checkindex(::Type{Bool}, inds::AbstractUnitRange, r::AbstractRange) @_propagate_inbounds_meta isempty(r) | (checkindex(Bool, inds, first(r)) & checkindex(Bool, inds, last(r))) end @@ -700,8 +700,8 @@ function copy(a::AbstractArray) copymutable(a) end -function copy!(B::AbstractVecOrMat{R}, ir_dest::Range{Int}, jr_dest::Range{Int}, - A::AbstractVecOrMat{S}, ir_src::Range{Int}, jr_src::Range{Int}) where {R,S} +function copy!(B::AbstractVecOrMat{R}, ir_dest::AbstractRange{Int}, jr_dest::AbstractRange{Int}, + A::AbstractVecOrMat{S}, ir_src::AbstractRange{Int}, jr_src::AbstractRange{Int}) where {R,S} if length(ir_dest) != length(ir_src) throw(ArgumentError(string("source and destination must have same size (got ", length(ir_src)," and ",length(ir_dest),")"))) @@ -895,7 +895,7 @@ end getindex(A, inds...) Return a subset of array `A` as specified by `inds`, where each `ind` may be an -`Int`, a `Range`, or a [`Vector`](@ref). See the manual section on +`Int`, an `AbstractRange`, or a [`Vector`](@ref). See the manual section on [array indexing](@ref man-array-indexing) for details. # Examples @@ -1045,14 +1045,14 @@ end ## get (getindex with a default value) ## -RangeVecIntList{A<:AbstractVector{Int}} = Union{Tuple{Vararg{Union{Range, AbstractVector{Int}}}}, - AbstractVector{UnitRange{Int}}, AbstractVector{Range{Int}}, AbstractVector{A}} +RangeVecIntList{A<:AbstractVector{Int}} = Union{Tuple{Vararg{Union{AbstractRange, AbstractVector{Int}}}}, + AbstractVector{UnitRange{Int}}, AbstractVector{AbstractRange{Int}}, AbstractVector{A}} get(A::AbstractArray, i::Integer, default) = checkbounds(Bool, A, i) ? A[i] : default get(A::AbstractArray, I::Tuple{}, default) = similar(A, typeof(default), 0) get(A::AbstractArray, I::Dims, default) = checkbounds(Bool, A, I...) ? A[I...] : default -function get!(X::AbstractVector{T}, A::AbstractVector, I::Union{Range,AbstractVector{Int}}, default::T) where T +function get!(X::AbstractVector{T}, A::AbstractVector, I::Union{AbstractRange,AbstractVector{Int}}, default::T) where T # 1d is not linear indexing ind = findin(I, indices1(A)) X[ind] = A[I[ind]] @@ -1061,7 +1061,7 @@ function get!(X::AbstractVector{T}, A::AbstractVector, I::Union{Range,AbstractVe X[last(ind)+1:last(Xind)] = default X end -function get!(X::AbstractArray{T}, A::AbstractArray, I::Union{Range,AbstractVector{Int}}, default::T) where T +function get!(X::AbstractArray{T}, A::AbstractArray, I::Union{AbstractRange,AbstractVector{Int}}, default::T) where T # Linear indexing ind = findin(I, 1:length(A)) X[ind] = A[I[ind]] @@ -1070,7 +1070,7 @@ function get!(X::AbstractArray{T}, A::AbstractArray, I::Union{Range,AbstractVect X end -get(A::AbstractArray, I::Range, default) = get!(similar(A, typeof(default), index_shape(I)), A, I, default) +get(A::AbstractArray, I::AbstractRange, default) = get!(similar(A, typeof(default), index_shape(I)), A, I, default) # TODO: DEPRECATE FOR #14770 (just the partial linear indexing part) function get!(X::AbstractArray{T}, A::AbstractArray, I::RangeVecIntList, default::T) where T @@ -1559,7 +1559,7 @@ function isequal(A::AbstractArray, B::AbstractArray) if indices(A) != indices(B) return false end - if isa(A,Range) != isa(B,Range) + if isa(A,AbstractRange) != isa(B,AbstractRange) return false end for (a, b) in zip(A, B) @@ -1582,7 +1582,7 @@ function (==)(A::AbstractArray, B::AbstractArray) if indices(A) != indices(B) return false end - if isa(A,Range) != isa(B,Range) + if isa(A,AbstractRange) != isa(B,AbstractRange) return false end for (a, b) in zip(A, B) diff --git a/base/array.jl b/base/array.jl index 194f6f48da2f4..28683a9fe19ce 100644 --- a/base/array.jl +++ b/base/array.jl @@ -30,7 +30,7 @@ elements of type `T`. Alias for [`AbstractArray{T,2}`](@ref). """ const AbstractMatrix{T} = AbstractArray{T,2} const AbstractVecOrMat{T} = Union{AbstractVector{T}, AbstractMatrix{T}} -const RangeIndex = Union{Int, Range{Int}, AbstractUnitRange{Int}} +const RangeIndex = Union{Int, AbstractRange{Int}, AbstractUnitRange{Int}} const DimOrInd = Union{Integer, AbstractUnitRange} const IntOrInd = Union{Int, AbstractUnitRange} const DimsOrInds{N} = NTuple{N,DimOrInd} @@ -789,7 +789,7 @@ function getindex(A::Array, c::Colon) end # This is redundant with the abstract fallbacks, but needed for bootstrap -function getindex(A::Array{S}, I::Range{Int}) where S +function getindex(A::Array{S}, I::AbstractRange{Int}) where S return S[ A[i] for i in I ] end diff --git a/base/complex.jl b/base/complex.jl index 0664f9679c248..07a7cbe317423 100644 --- a/base/complex.jl +++ b/base/complex.jl @@ -211,7 +211,7 @@ bswap(z::Complex) = Complex(bswap(real(z)), bswap(imag(z))) isequal(z::Complex, w::Complex) = isequal(real(z),real(w)) & isequal(imag(z),imag(w)) -in(x::Complex, r::Range{<:Real}) = isreal(x) && real(x) in r +in(x::Complex, r::AbstractRange{<:Real}) = isreal(x) && real(x) in r if UInt === UInt64 const h_imag = 0x32a7a07f3e7cd1f9 diff --git a/base/dates/arithmetic.jl b/base/dates/arithmetic.jl index 93d22c5f72119..028a5fa6ff20a 100644 --- a/base/dates/arithmetic.jl +++ b/base/dates/arithmetic.jl @@ -95,4 +95,4 @@ end # AbstractArray{TimeType}, AbstractArray{TimeType} (-)(x::OrdinalRange{T}, y::OrdinalRange{T}) where {T<:TimeType} = collect(x) - collect(y) -(-)(x::Range{T}, y::Range{T}) where {T<:TimeType} = collect(x) - collect(y) +(-)(x::AbstractRange{T}, y::AbstractRange{T}) where {T<:TimeType} = collect(x) - collect(y) diff --git a/base/dates/ranges.jl b/base/dates/ranges.jl index 21b646bd062fe..2a2795102757b 100644 --- a/base/dates/ranges.jl +++ b/base/dates/ranges.jl @@ -43,6 +43,6 @@ Base.start(r::StepRange{<:TimeType}) = 0 Base.next(r::StepRange{<:TimeType}, i::Int) = (r.start + r.step*i, i + 1) Base.done(r::StepRange{<:TimeType,<:Period}, i::Integer) = length(r) <= i -+(x::Period, r::Range{<:TimeType}) = (x + first(r)):step(r):(x + last(r)) -+(r::Range{<:TimeType}, x::Period) = x + r --(r::Range{<:TimeType}, x::Period) = (first(r)-x):step(r):(last(r)-x) ++(x::Period, r::AbstractRange{<:TimeType}) = (x + first(r)):step(r):(x + last(r)) ++(r::AbstractRange{<:TimeType}, x::Period) = x + r +-(r::AbstractRange{<:TimeType}, x::Period) = (first(r)-x):step(r):(last(r)-x) diff --git a/base/deprecated.jl b/base/deprecated.jl index d1a3d05b7820e..51bed516e347a 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -395,7 +395,7 @@ export $ @deprecate is (===) # midpoints of intervals -@deprecate midpoints(r::Range) r[1:length(r)-1] + 0.5*step(r) +@deprecate midpoints(r::AbstractRange) r[1:length(r)-1] + 0.5*step(r) @deprecate midpoints(v::AbstractVector) [0.5*(v[i] + v[i+1]) for i in 1:length(v)-1] @deprecate_binding Filter Iterators.Filter @@ -1149,7 +1149,7 @@ end ## the replacement StepRangeLen also has 4 real-valued fields, which ## makes deprecation tricky. See #20506. -struct Use_StepRangeLen_Instead{T<:AbstractFloat} <: Range{T} +struct Use_StepRangeLen_Instead{T<:AbstractFloat} <: AbstractRange{T} start::T step::T len::T @@ -1754,6 +1754,8 @@ import .Iterators.enumerate @deprecate enumerate(i::IndexLinear, A::AbstractArray) pairs(i, A) @deprecate enumerate(i::IndexCartesian, A::AbstractArray) pairs(i, A) +@deprecate_binding Range AbstractRange + # END 0.7 deprecations # BEGIN 1.0 deprecations diff --git a/base/exports.jl b/base/exports.jl index 608c5f46d0d8f..5bec9d90274bf 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -26,6 +26,7 @@ export # Types AbstractChannel, AbstractMatrix, + AbstractRange, AbstractSet, AbstractUnitRange, AbstractVector, @@ -87,7 +88,6 @@ export PermutedDimsArray, PollingFileWatcher, QuickSort, - Range, RangeIndex, Rational, Regex, diff --git a/base/hashing.jl b/base/hashing.jl index ee4be4d384941..e2847342a8c57 100644 --- a/base/hashing.jl +++ b/base/hashing.jl @@ -76,7 +76,7 @@ hash(x::QuoteNode, h::UInt) = hash(x.value, hash(QuoteNode, h)) # hashing ranges by component at worst leads to collisions for very similar ranges const hashr_seed = UInt === UInt64 ? 0x80707b6821b70087 : 0x21b70087 -function hash(r::Range, h::UInt) +function hash(r::AbstractRange, h::UInt) h += hashr_seed h = hash(first(r), h) h = hash(step(r), h) diff --git a/base/linalg/blas.jl b/base/linalg/blas.jl index 0ed249b91a9d1..6cf36f09b04e1 100644 --- a/base/linalg/blas.jl +++ b/base/linalg/blas.jl @@ -449,8 +449,8 @@ function axpy!(alpha::Number, x::Union{DenseArray{T},StridedVector{T}}, y::Union y end -function axpy!(alpha::Number, x::Array{T}, rx::Union{UnitRange{Ti},Range{Ti}}, - y::Array{T}, ry::Union{UnitRange{Ti},Range{Ti}}) where {T<:BlasFloat,Ti<:Integer} +function axpy!(alpha::Number, x::Array{T}, rx::Union{UnitRange{Ti},AbstractRange{Ti}}, + y::Array{T}, ry::Union{UnitRange{Ti},AbstractRange{Ti}}) where {T<:BlasFloat,Ti<:Integer} if length(rx) != length(ry) throw(DimensionMismatch("ranges of differing lengths")) end @@ -1515,8 +1515,8 @@ end end # module -function copy!(dest::Array{T}, rdest::Union{UnitRange{Ti},Range{Ti}}, - src::Array{T}, rsrc::Union{UnitRange{Ti},Range{Ti}}) where {T<:BlasFloat,Ti<:Integer} +function copy!(dest::Array{T}, rdest::Union{UnitRange{Ti},AbstractRange{Ti}}, + src::Array{T}, rsrc::Union{UnitRange{Ti},AbstractRange{Ti}}) where {T<:BlasFloat,Ti<:Integer} if minimum(rdest) < 1 || maximum(rdest) > length(dest) throw(ArgumentError("range out of bounds for dest, of length $(length(dest))")) end diff --git a/base/linalg/dense.jl b/base/linalg/dense.jl index d716fc08738b3..9c4741769c74a 100644 --- a/base/linalg/dense.jl +++ b/base/linalg/dense.jl @@ -115,7 +115,7 @@ isposdef(x::Number) = imag(x)==0 && real(x) > 0 stride1(x::Array) = 1 stride1(x::StridedVector) = stride(x, 1)::Int -function norm(x::StridedVector{T}, rx::Union{UnitRange{TI},Range{TI}}) where {T<:BlasFloat,TI<:Integer} +function norm(x::StridedVector{T}, rx::Union{UnitRange{TI},AbstractRange{TI}}) where {T<:BlasFloat,TI<:Integer} if minimum(rx) < 1 || maximum(rx) > length(x) throw(BoundsError(x, rx)) end @@ -240,7 +240,7 @@ end """ diagind(M, k::Integer=0) -A `Range` giving the indices of the `k`th diagonal of the matrix `M`. +An `AbstractRange` giving the indices of the `k`th diagonal of the matrix `M`. # Examples ```jldoctest diff --git a/base/linalg/matmul.jl b/base/linalg/matmul.jl index 7badc4be6176b..fe1b9b10194ab 100644 --- a/base/linalg/matmul.jl +++ b/base/linalg/matmul.jl @@ -41,7 +41,7 @@ end vecdot(x::Union{DenseArray{T},StridedVector{T}}, y::Union{DenseArray{T},StridedVector{T}}) where {T<:BlasReal} = BLAS.dot(x, y) vecdot(x::Union{DenseArray{T},StridedVector{T}}, y::Union{DenseArray{T},StridedVector{T}}) where {T<:BlasComplex} = BLAS.dotc(x, y) -function dot(x::Vector{T}, rx::Union{UnitRange{TI},Range{TI}}, y::Vector{T}, ry::Union{UnitRange{TI},Range{TI}}) where {T<:BlasReal,TI<:Integer} +function dot(x::Vector{T}, rx::Union{UnitRange{TI},AbstractRange{TI}}, y::Vector{T}, ry::Union{UnitRange{TI},AbstractRange{TI}}) where {T<:BlasReal,TI<:Integer} if length(rx) != length(ry) throw(DimensionMismatch("length of rx, $(length(rx)), does not equal length of ry, $(length(ry))")) end @@ -54,7 +54,7 @@ function dot(x::Vector{T}, rx::Union{UnitRange{TI},Range{TI}}, y::Vector{T}, ry: BLAS.dot(length(rx), pointer(x)+(first(rx)-1)*sizeof(T), step(rx), pointer(y)+(first(ry)-1)*sizeof(T), step(ry)) end -function dot(x::Vector{T}, rx::Union{UnitRange{TI},Range{TI}}, y::Vector{T}, ry::Union{UnitRange{TI},Range{TI}}) where {T<:BlasComplex,TI<:Integer} +function dot(x::Vector{T}, rx::Union{UnitRange{TI},AbstractRange{TI}}, y::Vector{T}, ry::Union{UnitRange{TI},AbstractRange{TI}}) where {T<:BlasComplex,TI<:Integer} if length(rx) != length(ry) throw(DimensionMismatch("length of rx, $(length(rx)), does not equal length of ry, $(length(ry))")) end diff --git a/base/linalg/transpose.jl b/base/linalg/transpose.jl index 0baf0f9393f6e..a97fe96318ac8 100644 --- a/base/linalg/transpose.jl +++ b/base/linalg/transpose.jl @@ -128,8 +128,8 @@ end @inline adjoint(A::AbstractVector{<:Real}) = transpose(A) @inline adjoint(A::AbstractMatrix{<:Real}) = transpose(A) -function copy_transpose!(B::AbstractVecOrMat, ir_dest::Range{Int}, jr_dest::Range{Int}, - A::AbstractVecOrMat, ir_src::Range{Int}, jr_src::Range{Int}) +function copy_transpose!(B::AbstractVecOrMat, ir_dest::AbstractRange{Int}, jr_dest::AbstractRange{Int}, + A::AbstractVecOrMat, ir_src::AbstractRange{Int}, jr_src::AbstractRange{Int}) if length(ir_dest) != length(jr_src) throw(ArgumentError(string("source and destination must have same size (got ", length(jr_src)," and ",length(ir_dest),")"))) diff --git a/base/range.jl b/base/range.jl index ba90a98cbbb26..08a7eb941f276 100644 --- a/base/range.jl +++ b/base/range.jl @@ -77,11 +77,11 @@ range(a::AbstractFloat, st::Real, len::Integer) = range(a, float(st), len) ## 1-dimensional ranges ## -abstract type Range{T} <: AbstractArray{T,1} end +abstract type AbstractRange{T} <: AbstractArray{T,1} end ## ordinal ranges -abstract type OrdinalRange{T,S} <: Range{T} end +abstract type OrdinalRange{T,S} <: AbstractRange{T} end abstract type AbstractUnitRange{T} <: OrdinalRange{T,Int} end struct StepRange{T,S} <: OrdinalRange{T,S} @@ -190,7 +190,7 @@ value `r[1]`, but alternatively you can supply it as the value of with `TwicePrecision` this can be used to implement ranges that are free of roundoff error. """ -struct StepRangeLen{T,R,S} <: Range{T} +struct StepRangeLen{T,R,S} <: AbstractRange{T} ref::R # reference value (might be smallest-magnitude value in the range) step::S # step value len::Int # length of the range @@ -210,7 +210,7 @@ StepRangeLen{T}(ref::R, step::S, len::Integer, offset::Integer = 1) where {T,R,S ## linspace and logspace -struct LinSpace{T} <: Range{T} +struct LinSpace{T} <: AbstractRange{T} start::T stop::T len::Int @@ -273,7 +273,7 @@ parameters `pre` and `post` characters for each printed row, `sep` separator string between printed elements, `hdots` string for the horizontal ellipsis. """ -function print_range(io::IO, r::Range, +function print_range(io::IO, r::AbstractRange, pre::AbstractString = " ", sep::AbstractString = ",", post::AbstractString = "", @@ -344,7 +344,7 @@ logspace(start::Real, stop::Real, n::Integer=50; base=10) = base.^linspace(start ## interface implementations -size(r::Range) = (length(r),) +size(r::AbstractRange) = (length(r),) isempty(r::StepRange) = (r.start != r.stop) & ((r.step > zero(r.step)) != (r.stop > r.start)) @@ -355,7 +355,7 @@ isempty(r::LinSpace) = length(r) == 0 """ step(r) -Get the step size of a `Range` object. +Get the step size of an `AbstractRange` object. ```jldoctest julia> step(1:10) 1 @@ -376,9 +376,9 @@ step(r::StepRangeLen{T}) where {T} = T(r.step) step(r::LinSpace) = (last(r)-first(r))/r.lendiv step_hp(r::StepRangeLen) = r.step -step_hp(r::Range) = step(r) +step_hp(r::AbstractRange) = step(r) -unsafe_length(r::Range) = length(r) # generic fallback +unsafe_length(r::AbstractRange) = length(r) # generic fallback function unsafe_length(r::StepRange) n = Integer(div(r.stop+r.step - r.start, r.step)) @@ -438,11 +438,11 @@ last(r::LinSpace) = r.stop minimum(r::AbstractUnitRange) = isempty(r) ? throw(ArgumentError("range must be non-empty")) : first(r) maximum(r::AbstractUnitRange) = isempty(r) ? throw(ArgumentError("range must be non-empty")) : last(r) -minimum(r::Range) = isempty(r) ? throw(ArgumentError("range must be non-empty")) : min(first(r), last(r)) -maximum(r::Range) = isempty(r) ? throw(ArgumentError("range must be non-empty")) : max(first(r), last(r)) +minimum(r::AbstractRange) = isempty(r) ? throw(ArgumentError("range must be non-empty")) : min(first(r), last(r)) +maximum(r::AbstractRange) = isempty(r) ? throw(ArgumentError("range must be non-empty")) : max(first(r), last(r)) # Ranges are immutable -copy(r::Range) = r +copy(r::AbstractRange) = r ## iteration @@ -498,7 +498,7 @@ function getindex(v::OneTo{T}, i::Integer) where T convert(T, i) end -function getindex(v::Range{T}, i::Integer) where T +function getindex(v::AbstractRange{T}, i::Integer) where T @_inline_meta ret = convert(T, first(v) + (i - 1)*step_hp(v)) ok = ifelse(step(v) > zero(step(v)), @@ -535,7 +535,7 @@ function lerpi(j::Integer, d::Integer, a::T, b::T) where T T((1-t)*a + t*b) end -getindex(r::Range, ::Colon) = copy(r) +getindex(r::AbstractRange, ::Colon) = copy(r) function getindex(r::AbstractUnitRange, s::AbstractUnitRange{<:Integer}) @_inline_meta @@ -558,7 +558,7 @@ function getindex(r::AbstractUnitRange, s::StepRange{<:Integer}) range(st, step(s), length(s)) end -function getindex(r::StepRange, s::Range{<:Integer}) +function getindex(r::StepRange, s::AbstractRange{<:Integer}) @_inline_meta @boundscheck checkbounds(r, s) st = oftype(r.start, r.start + (first(s)-1)*step(r)) @@ -583,11 +583,11 @@ function getindex(r::LinSpace, s::OrdinalRange{<:Integer}) return LinSpace(vfirst, vlast, length(s)) end -show(io::IO, r::Range) = print(io, repr(first(r)), ':', repr(step(r)), ':', repr(last(r))) +show(io::IO, r::AbstractRange) = print(io, repr(first(r)), ':', repr(step(r)), ':', repr(last(r))) show(io::IO, r::UnitRange) = print(io, repr(first(r)), ':', repr(last(r))) show(io::IO, r::OneTo) = print(io, "Base.OneTo(", r.stop, ")") -==(r::T, s::T) where {T<:Range} = +==(r::T, s::T) where {T<:AbstractRange} = (first(r) == first(s)) & (step(r) == step(s)) & (last(r) == last(s)) ==(r::OrdinalRange, s::OrdinalRange) = (first(r) == first(s)) & (step(r) == step(s)) & (last(r) == last(s)) @@ -596,7 +596,7 @@ show(io::IO, r::OneTo) = print(io, "Base.OneTo(", r.stop, ")") ==(r::Union{StepRange{T},StepRangeLen{T,T}}, s::Union{StepRange{T},StepRangeLen{T,T}}) where {T} = (first(r) == first(s)) & (last(r) == last(s)) & (step(r) == step(s)) -function ==(r::Range, s::Range) +function ==(r::AbstractRange, s::AbstractRange) lr = length(r) if lr != length(s) return false @@ -669,11 +669,11 @@ function intersect(r::StepRange, s::StepRange) # if a == 0 # # One or both ranges have step 0. # if step1 == 0 && step2 == 0 - # return start1 == start2 ? r : Range(start1, 0, 0) + # return start1 == start2 ? r : AbstractRange(start1, 0, 0) # elseif step1 == 0 - # return start2 <= start1 <= stop2 && rem(start1 - start2, step2) == 0 ? r : Range(start1, 0, 0) + # return start2 <= start1 <= stop2 && rem(start1 - start2, step2) == 0 ? r : AbstractRange(start1, 0, 0) # else - # return start1 <= start2 <= stop1 && rem(start2 - start1, step1) == 0 ? (start2:step1:start2) : Range(start1, step1, 0) + # return start1 <= start2 <= stop1 && rem(start2 - start1, step1) == 0 ? (start2:step1:start2) : AbstractRange(start1, step1, 0) # end # end @@ -694,7 +694,7 @@ function intersect(r::StepRange, s::StepRange) m:a:n end -function intersect(r1::Range, r2::Range, r3::Range, r::Range...) +function intersect(r1::AbstractRange, r2::AbstractRange, r3::AbstractRange, r::AbstractRange...) i = intersect(intersect(r1, r2), r3) for t in r i = intersect(i, t) @@ -703,7 +703,7 @@ function intersect(r1::Range, r2::Range, r3::Range, r::Range...) end # findin (the index of intersection) -function _findin(r::Range{<:Integer}, span::AbstractUnitRange{<:Integer}) +function _findin(r::AbstractRange{<:Integer}, span::AbstractUnitRange{<:Integer}) local ifirst local ilast fspan = first(span) @@ -729,7 +729,7 @@ function findin(r::AbstractUnitRange{<:Integer}, span::AbstractUnitRange{<:Integ ifirst:ilast end -function findin(r::Range{<:Integer}, span::AbstractUnitRange{<:Integer}) +function findin(r::AbstractRange{<:Integer}, span::AbstractUnitRange{<:Integer}) ifirst, ilast = _findin(r, span) ifirst:1:ilast end @@ -744,7 +744,7 @@ end +(x::Real, r::AbstractUnitRange) = range(x + first(r), length(r)) # For #18336 we need to prevent promotion of the step type: +(x::Number, r::AbstractUnitRange) = range(x + first(r), step(r), length(r)) -+(x::Number, r::Range) = (x+first(r)):step(r):(x+last(r)) ++(x::Number, r::AbstractRange) = (x+first(r)):step(r):(x+last(r)) function +(x::Number, r::StepRangeLen{T}) where T newref = x + r.ref StepRangeLen{typeof(T(r.ref) + x)}(newref, r.step, length(r), r.offset) @@ -752,32 +752,32 @@ end function +(x::Number, r::LinSpace) LinSpace(x + r.start, x + r.stop, r.len) end -+(r::Range, x::Number) = x + r # assumes addition is commutative ++(r::AbstractRange, x::Number) = x + r # assumes addition is commutative --(x::Number, r::Range) = (x-first(r)):-step(r):(x-last(r)) +-(x::Number, r::AbstractRange) = (x-first(r)):-step(r):(x-last(r)) -(x::Number, r::StepRangeLen) = +(x, -r) function -(x::Number, r::LinSpace) LinSpace(x - r.start, x - r.stop, r.len) end --(r::Range, x::Number) = +(-x, r) +-(r::AbstractRange, x::Number) = +(-x, r) -*(x::Number, r::Range) = range(x*first(r), x*step(r), length(r)) +*(x::Number, r::AbstractRange) = range(x*first(r), x*step(r), length(r)) *(x::Number, r::StepRangeLen{T}) where {T} = StepRangeLen{typeof(x*T(r.ref))}(x*r.ref, x*r.step, length(r), r.offset) *(x::Number, r::LinSpace) = LinSpace(x * r.start, x * r.stop, r.len) # separate in case of noncommutative multiplication -*(r::Range, x::Number) = range(first(r)*x, step(r)*x, length(r)) +*(r::AbstractRange, x::Number) = range(first(r)*x, step(r)*x, length(r)) *(r::StepRangeLen{T}, x::Number) where {T} = StepRangeLen{typeof(T(r.ref)*x)}(r.ref*x, r.step*x, length(r), r.offset) *(r::LinSpace, x::Number) = LinSpace(r.start * x, r.stop * x, r.len) -/(r::Range, x::Number) = range(first(r)/x, step(r)/x, length(r)) +/(r::AbstractRange, x::Number) = range(first(r)/x, step(r)/x, length(r)) /(r::StepRangeLen{T}, x::Number) where {T} = StepRangeLen{typeof(T(r.ref)/x)}(r.ref/x, r.step/x, length(r), r.offset) /(r::LinSpace, x::Number) = LinSpace(r.start / x, r.stop / x, r.len) -/(x::Number, r::Range) = [ x/y for y=r ] +/(x::Number, r::AbstractRange) = [ x/y for y=r ] # promote eltype if at least one container wouldn't change, otherwise join container types. el_same(::Type{T}, a::Type{<:AbstractArray{T,n}}, b::Type{<:AbstractArray{T,n}}) where {T,n} = a @@ -813,11 +813,11 @@ convert(::Type{StepRange{T1,T2}}, r::StepRange{T1,T2}) where {T1,T2} = r promote_rule(a::Type{StepRange{T1a,T1b}}, ::Type{UR}) where {T1a,T1b,UR<:AbstractUnitRange} = promote_rule(a, StepRange{eltype(UR), eltype(UR)}) -convert(::Type{StepRange{T1,T2}}, r::Range) where {T1,T2} = +convert(::Type{StepRange{T1,T2}}, r::AbstractRange) where {T1,T2} = StepRange{T1,T2}(convert(T1, first(r)), convert(T2, step(r)), convert(T1, last(r))) convert(::Type{StepRange}, r::AbstractUnitRange{T}) where {T} = StepRange{T,T}(first(r), step(r), last(r)) -convert(::Type{StepRange{T1,T2} where T1}, r::Range) where {T2} = +convert(::Type{StepRange{T1,T2} where T1}, r::AbstractRange) where {T2} = convert(StepRange{eltype(r),T2}, r) promote_rule(::Type{StepRangeLen{T1,R1,S1}},::Type{StepRangeLen{T2,R2,S2}}) where {T1,T2,R1,R2,S1,S2} = @@ -830,20 +830,20 @@ convert(::Type{StepRangeLen{T,R,S}}, r::StepRangeLen) where {T,R,S} = convert(::Type{StepRangeLen{T}}, r::StepRangeLen) where {T} = StepRangeLen(convert(T, r.ref), convert(T, r.step), length(r), r.offset) -promote_rule(a::Type{StepRangeLen{T,R,S}}, ::Type{OR}) where {T,R,S,OR<:Range} = +promote_rule(a::Type{StepRangeLen{T,R,S}}, ::Type{OR}) where {T,R,S,OR<:AbstractRange} = promote_rule(a, StepRangeLen{eltype(OR), eltype(OR), eltype(OR)}) -convert(::Type{StepRangeLen{T,R,S}}, r::Range) where {T,R,S} = +convert(::Type{StepRangeLen{T,R,S}}, r::AbstractRange) where {T,R,S} = StepRangeLen{T,R,S}(R(first(r)), S(step(r)), length(r)) -convert(::Type{StepRangeLen{T}}, r::Range) where {T} = +convert(::Type{StepRangeLen{T}}, r::AbstractRange) where {T} = StepRangeLen(T(first(r)), T(step(r)), length(r)) -convert(::Type{StepRangeLen}, r::Range) = convert(StepRangeLen{eltype(r)}, r) +convert(::Type{StepRangeLen}, r::AbstractRange) = convert(StepRangeLen{eltype(r)}, r) promote_rule(a::Type{LinSpace{T1}}, b::Type{LinSpace{T2}}) where {T1,T2} = el_same(promote_type(T1,T2), a, b) convert(::Type{LinSpace{T}}, r::LinSpace{T}) where {T} = r -convert(::Type{LinSpace{T}}, r::Range) where {T} = +convert(::Type{LinSpace{T}}, r::AbstractRange) where {T} = LinSpace{T}(first(r), last(r), length(r)) -convert(::Type{LinSpace}, r::Range{T}) where {T} = +convert(::Type{LinSpace}, r::AbstractRange{T}) where {T} = convert(LinSpace{T}, r) promote_rule(a::Type{LinSpace{T}}, ::Type{OR}) where {T,OR<:OrdinalRange} = @@ -856,7 +856,7 @@ promote_rule(::Type{LinSpace{L}}, b::Type{StepRangeLen{T,R,S}}) where {L,T,R,S} ## concatenation ## -function vcat(rs::Range{T}...) where T +function vcat(rs::AbstractRange{T}...) where T n::Int = 0 for ra in rs n += length(ra) @@ -870,8 +870,8 @@ function vcat(rs::Range{T}...) where T return a end -convert(::Type{Array{T,1}}, r::Range{T}) where {T} = vcat(r) -collect(r::Range) = vcat(r) +convert(::Type{Array{T,1}}, r::AbstractRange{T}) where {T} = vcat(r) +collect(r::AbstractRange) = vcat(r) reverse(r::OrdinalRange) = colon(last(r), -step(r), first(r)) reverse(r::StepRangeLen) = StepRangeLen(r.ref, -r.step, length(r), length(r)-r.offset+1) @@ -880,31 +880,31 @@ reverse(r::LinSpace) = LinSpace(r.stop, r.start, length(r)) ## sorting ## issorted(r::AbstractUnitRange) = true -issorted(r::Range) = length(r) <= 1 || step(r) >= zero(step(r)) +issorted(r::AbstractRange) = length(r) <= 1 || step(r) >= zero(step(r)) sort(r::AbstractUnitRange) = r sort!(r::AbstractUnitRange) = r -sort(r::Range) = issorted(r) ? r : reverse(r) +sort(r::AbstractRange) = issorted(r) ? r : reverse(r) sortperm(r::AbstractUnitRange) = 1:length(r) -sortperm(r::Range) = issorted(r) ? (1:1:length(r)) : (length(r):-1:1) +sortperm(r::AbstractRange) = issorted(r) ? (1:1:length(r)) : (length(r):-1:1) -function sum(r::Range{<:Real}) +function sum(r::AbstractRange{<:Real}) l = length(r) # note that a little care is required to avoid overflow in l*(l-1)/2 return l * first(r) + (iseven(l) ? (step(r) * (l-1)) * (l>>1) : (step(r) * l) * ((l-1)>>1)) end -function mean(r::Range{<:Real}) +function mean(r::AbstractRange{<:Real}) isempty(r) && throw(ArgumentError("mean of an empty range is undefined")) (first(r) + last(r)) / 2 end -median(r::Range{<:Real}) = mean(r) +median(r::AbstractRange{<:Real}) = mean(r) -function _in_range(x, r::Range) +function _in_range(x, r::AbstractRange) if step(r) == 0 return !isempty(r) && first(r) == x else @@ -912,17 +912,17 @@ function _in_range(x, r::Range) return n >= 1 && n <= length(r) && r[n] == x end end -in(x::Real, r::Range{<:Real}) = _in_range(x, r) +in(x::Real, r::AbstractRange{<:Real}) = _in_range(x, r) # This method needs to be defined separately since -(::T, ::T) can be implemented # even if -(::T, ::Real) is not -in(x::T, r::Range{T}) where {T} = _in_range(x, r) +in(x::T, r::AbstractRange{T}) where {T} = _in_range(x, r) in(x::Integer, r::AbstractUnitRange{<:Integer}) = (first(r) <= x) & (x <= last(r)) -in(x::Real, r::Range{T}) where {T<:Integer} = +in(x::Real, r::AbstractRange{T}) where {T<:Integer} = isinteger(x) && !isempty(r) && x >= minimum(r) && x <= maximum(r) && (mod(convert(T,x),step(r))-mod(first(r),step(r)) == 0) -in(x::Char, r::Range{Char}) = +in(x::Char, r::AbstractRange{Char}) = !isempty(r) && x >= minimum(r) && x <= maximum(r) && (mod(Int(x) - Int(first(r)), step(r)) == 0) diff --git a/base/reduce.jl b/base/reduce.jl index 626809a73721a..6b0415fbe266d 100644 --- a/base/reduce.jl +++ b/base/reduce.jl @@ -472,7 +472,7 @@ minimum(a) = mapreduce(identity, scalarmin, a) ## extrema -extrema(r::Range) = (minimum(r), maximum(r)) +extrema(r::AbstractRange) = (minimum(r), maximum(r)) extrema(x::Real) = (x, x) """ diff --git a/base/replutil.jl b/base/replutil.jl index 2e2031b506e3e..a59905d86af45 100644 --- a/base/replutil.jl +++ b/base/replutil.jl @@ -137,7 +137,7 @@ function show(io::IO, ::MIME"text/plain", t::Task) end show(io::IO, ::MIME"text/plain", X::AbstractArray) = showarray(io, X, false) -show(io::IO, ::MIME"text/plain", r::Range) = show(io, r) # always use the compact form for printing ranges +show(io::IO, ::MIME"text/plain", r::AbstractRange) = show(io, r) # always use the compact form for printing ranges # display something useful even for strings containing arbitrary # (non-UTF8) binary data: @@ -186,7 +186,7 @@ function showerror(io::IO, ex::BoundsError) if isdefined(ex, :i) !isa(ex.a, AbstractArray) && print(io, "\n ") print(io, " at index [") - if isa(ex.i, Range) + if isa(ex.i, AbstractRange) print(io, ex.i) else join(io, ex.i, ", ") diff --git a/base/reshapedarray.jl b/base/reshapedarray.jl index 1c63d5b5349c2..fef634061165b 100644 --- a/base/reshapedarray.jl +++ b/base/reshapedarray.jl @@ -232,7 +232,7 @@ end end # helpful error message for a common failure case -const ReshapedRange{T,N,A<:Range} = ReshapedArray{T,N,A,Tuple{}} +const ReshapedRange{T,N,A<:AbstractRange} = ReshapedArray{T,N,A,Tuple{}} setindex!(A::ReshapedRange, val, index::Int) = _rs_setindex!_err() setindex!(A::ReshapedRange{T,N}, val, indexes::Vararg{Int,N}) where {T,N} = _rs_setindex!_err() setindex!(A::ReshapedRange, val, index::ReshapedIndex) = _rs_setindex!_err() diff --git a/base/set.jl b/base/set.jl index 86f1e93b889b4..916d9746a1435 100644 --- a/base/set.jl +++ b/base/set.jl @@ -446,7 +446,7 @@ end allunique(::Set) = true -allunique(r::Range{T}) where {T} = (step(r) != zero(T)) || (length(r) <= 1) +allunique(r::AbstractRange{T}) where {T} = (step(r) != zero(T)) || (length(r) <= 1) function filter(f, s::Set) u = similar(s) diff --git a/base/sort.jl b/base/sort.jl index cb28e79d7de52..d965217814487 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -214,7 +214,7 @@ function searchsorted(v::AbstractVector, x, ilo::Int, ihi::Int, o::Ordering) return (lo + 1) : (hi - 1) end -function searchsortedlast(a::Range{<:Real}, x::Real, o::DirectOrdering) +function searchsortedlast(a::AbstractRange{<:Real}, x::Real, o::DirectOrdering) if step(a) == 0 lt(o, x, first(a)) ? 0 : length(a) else @@ -223,7 +223,7 @@ function searchsortedlast(a::Range{<:Real}, x::Real, o::DirectOrdering) end end -function searchsortedfirst(a::Range{<:Real}, x::Real, o::DirectOrdering) +function searchsortedfirst(a::AbstractRange{<:Real}, x::Real, o::DirectOrdering) if step(a) == 0 lt(o, first(a), x) ? length(a) + 1 : 1 else @@ -232,7 +232,7 @@ function searchsortedfirst(a::Range{<:Real}, x::Real, o::DirectOrdering) end end -function searchsortedlast(a::Range{<:Integer}, x::Real, o::DirectOrdering) +function searchsortedlast(a::AbstractRange{<:Integer}, x::Real, o::DirectOrdering) if step(a) == 0 lt(o, x, first(a)) ? 0 : length(a) else @@ -240,7 +240,7 @@ function searchsortedlast(a::Range{<:Integer}, x::Real, o::DirectOrdering) end end -function searchsortedfirst(a::Range{<:Integer}, x::Real, o::DirectOrdering) +function searchsortedfirst(a::AbstractRange{<:Integer}, x::Real, o::DirectOrdering) if step(a) == 0 lt(o, first(a), x) ? length(a)+1 : 1 else @@ -248,7 +248,7 @@ function searchsortedfirst(a::Range{<:Integer}, x::Real, o::DirectOrdering) end end -function searchsortedfirst(a::Range{<:Integer}, x::Unsigned, o::DirectOrdering) +function searchsortedfirst(a::AbstractRange{<:Integer}, x::Unsigned, o::DirectOrdering) if lt(o, first(a), x) if step(a) == 0 length(a) + 1 @@ -260,7 +260,7 @@ function searchsortedfirst(a::Range{<:Integer}, x::Unsigned, o::DirectOrdering) end end -function searchsortedlast(a::Range{<:Integer}, x::Unsigned, o::DirectOrdering) +function searchsortedlast(a::AbstractRange{<:Integer}, x::Unsigned, o::DirectOrdering) if lt(o, x, first(a)) 0 elseif step(a) == 0 @@ -270,7 +270,7 @@ function searchsortedlast(a::Range{<:Integer}, x::Unsigned, o::DirectOrdering) end end -searchsorted(a::Range{<:Real}, x::Real, o::DirectOrdering) = +searchsorted(a::AbstractRange{<:Real}, x::Real, o::DirectOrdering) = searchsortedfirst(a, x, o) : searchsortedlast(a, x, o) for s in [:searchsortedfirst, :searchsortedlast, :searchsorted] diff --git a/base/sparse/sparsematrix.jl b/base/sparse/sparsematrix.jl index 5d27cc316c31e..15daf86f9a0ca 100644 --- a/base/sparse/sparsematrix.jl +++ b/base/sparse/sparsematrix.jl @@ -1921,7 +1921,7 @@ indmin(A::SparseMatrixCSC) = findmin(A)[2] indmax(A::SparseMatrixCSC) = findmax(A)[2] ## getindex -function rangesearch(haystack::Range, needle) +function rangesearch(haystack::AbstractRange, needle) (i,rem) = divrem(needle - first(haystack), step(haystack)) (rem==0 && 1<=i+1<=length(haystack)) ? i+1 : 0 end @@ -1978,7 +1978,7 @@ end getindex_traverse_col(::AbstractUnitRange, lo::Int, hi::Int) = lo:hi getindex_traverse_col(I::StepRange, lo::Int, hi::Int) = step(I) > 0 ? (lo:1:hi) : (hi:-1:lo) -function getindex(A::SparseMatrixCSC{Tv,Ti}, I::Range, J::AbstractVector) where {Tv,Ti<:Integer} +function getindex(A::SparseMatrixCSC{Tv,Ti}, I::AbstractRange, J::AbstractVector) where {Tv,Ti<:Integer} # Ranges for indexing rows (m, n) = size(A) # whole columns: @@ -2338,10 +2338,10 @@ function getindex(A::SparseMatrixCSC{Tv}, I::AbstractArray) where Tv end # logical getindex -getindex(A::SparseMatrixCSC{<:Any,<:Integer}, I::Range{Bool}, J::AbstractVector{Bool}) = error("Cannot index with Range{Bool}") -getindex(A::SparseMatrixCSC{<:Any,<:Integer}, I::Range{Bool}, J::AbstractVector{<:Integer}) = error("Cannot index with Range{Bool}") +getindex(A::SparseMatrixCSC{<:Any,<:Integer}, I::AbstractRange{Bool}, J::AbstractVector{Bool}) = error("Cannot index with AbstractRange{Bool}") +getindex(A::SparseMatrixCSC{<:Any,<:Integer}, I::AbstractRange{Bool}, J::AbstractVector{<:Integer}) = error("Cannot index with AbstractRange{Bool}") -getindex(A::SparseMatrixCSC, I::Range{<:Integer}, J::AbstractVector{Bool}) = A[I,find(J)] +getindex(A::SparseMatrixCSC, I::AbstractRange{<:Integer}, J::AbstractVector{Bool}) = A[I,find(J)] getindex(A::SparseMatrixCSC, I::Integer, J::AbstractVector{Bool}) = A[I,find(J)] getindex(A::SparseMatrixCSC, I::AbstractVector{Bool}, J::Integer) = A[find(I),J] getindex(A::SparseMatrixCSC, I::AbstractVector{Bool}, J::AbstractVector{Bool}) = A[find(I),find(J)] diff --git a/base/sparse/sparsevector.jl b/base/sparse/sparsevector.jl index 630fea82a0a12..acaac624548ea 100644 --- a/base/sparse/sparsevector.jl +++ b/base/sparse/sparsevector.jl @@ -573,7 +573,7 @@ function _logical_index(A::SparseMatrixCSC{Tv}, I::AbstractArray{Bool}) where Tv SparseVector(n, rowvalB, nzvalB) end -# TODO: further optimizations are available for ::Colon and other types of Range +# TODO: further optimizations are available for ::Colon and other types of AbstractRange getindex(A::SparseMatrixCSC, ::Colon) = A[1:end] function getindex(A::SparseMatrixCSC{Tv}, I::AbstractUnitRange) where Tv diff --git a/base/statistics.jl b/base/statistics.jl index 226a8661a85f1..5094e43762ad0 100644 --- a/base/statistics.jl +++ b/base/statistics.jl @@ -224,7 +224,7 @@ varm(iterable, m::Number; corrected::Bool=true) = ## variances over ranges -function varm(v::Range, m::Number) +function varm(v::AbstractRange, m::Number) f = first(v) - m s = step(v) l = length(v) @@ -235,7 +235,7 @@ function varm(v::Range, m::Number) return vv end -function var(v::Range) +function var(v::AbstractRange) s = step(v) l = length(v) vv = abs2(s) * (l + 1) * l / 12 @@ -561,7 +561,7 @@ julia> middle(1:10) 5.5 ``` """ -middle(a::Range) = middle(a[1], a[end]) +middle(a::AbstractRange) = middle(a[1], a[end]) """ middle(a) diff --git a/base/strings/basic.jl b/base/strings/basic.jl index 5fd209255eb0c..e7cea9e8da9c4 100644 --- a/base/strings/basic.jl +++ b/base/strings/basic.jl @@ -239,7 +239,7 @@ function nextind(s::AbstractString, i::Integer) end checkbounds(s::AbstractString, i::Integer) = start(s) <= i <= endof(s) || throw(BoundsError(s, i)) -checkbounds(s::AbstractString, r::Range{<:Integer}) = isempty(r) || (minimum(r) >= start(s) && maximum(r) <= endof(s)) || throw(BoundsError(s, r)) +checkbounds(s::AbstractString, r::AbstractRange{<:Integer}) = isempty(r) || (minimum(r) >= start(s) && maximum(r) <= endof(s)) || throw(BoundsError(s, r)) # The following will end up using a deprecated checkbounds, when the covariant parameter is not Integer checkbounds(s::AbstractString, I::AbstractArray{<:Real}) = all(i -> checkbounds(s, i), I) checkbounds(s::AbstractString, I::AbstractArray{<:Integer}) = all(i -> checkbounds(s, i), I) diff --git a/base/subarray.jl b/base/subarray.jl index 5db883f964c06..4929635729dc0 100644 --- a/base/subarray.jl +++ b/base/subarray.jl @@ -46,7 +46,7 @@ viewindexing(I::Tuple{Slice, Slice, Vararg{Any}}) = (@_inline_meta; viewindexing # A UnitRange can follow Slices, but only if all other indices are scalar viewindexing(I::Tuple{Slice, UnitRange, Vararg{ScalarIndex}}) = IndexLinear() # In general, ranges are only fast if all other indices are scalar -viewindexing(I::Tuple{Union{Range, Slice}, Vararg{ScalarIndex}}) = IndexLinear() +viewindexing(I::Tuple{Union{AbstractRange, Slice}, Vararg{ScalarIndex}}) = IndexLinear() # All other index combinations are slow viewindexing(I::Tuple{Vararg{Any}}) = IndexCartesian() # Of course, all other array types are slow @@ -257,7 +257,7 @@ substrides(parent, I::Tuple) = substrides(1, parent, 1, I) substrides(s, parent, dim, ::Tuple{}) = () substrides(s, parent, dim, I::Tuple{ScalarIndex, Vararg{Any}}) = (substrides(s*size(parent, dim), parent, dim+1, tail(I))...) substrides(s, parent, dim, I::Tuple{Slice, Vararg{Any}}) = (s, substrides(s*size(parent, dim), parent, dim+1, tail(I))...) -substrides(s, parent, dim, I::Tuple{Range, Vararg{Any}}) = (s*step(I[1]), substrides(s*size(parent, dim), parent, dim+1, tail(I))...) +substrides(s, parent, dim, I::Tuple{AbstractRange, Vararg{Any}}) = (s*step(I[1]), substrides(s*size(parent, dim), parent, dim+1, tail(I))...) substrides(s, parent, dim, I::Tuple{Any, Vararg{Any}}) = throw(ArgumentError("strides is invalid for SubArrays with indices of type $(typeof(I[1]))")) stride(V::SubArray, d::Integer) = d <= ndims(V) ? strides(V)[d] : strides(V)[end] * size(V)[end] @@ -267,7 +267,7 @@ compute_stride1(parent::AbstractArray, I::NTuple{N,Any}) where {N} = compute_stride1(s, inds, I::Tuple{}) = s compute_stride1(s, inds, I::Tuple{ScalarIndex, Vararg{Any}}) = (@_inline_meta; compute_stride1(s*unsafe_length(inds[1]), tail(inds), tail(I))) -compute_stride1(s, inds, I::Tuple{Range, Vararg{Any}}) = s*step(I[1]) +compute_stride1(s, inds, I::Tuple{AbstractRange, Vararg{Any}}) = s*step(I[1]) compute_stride1(s, inds, I::Tuple{Slice, Vararg{Any}}) = s compute_stride1(s, inds, I::Tuple{Any, Vararg{Any}}) = throw(ArgumentError("invalid strided index type $(typeof(I[1]))")) @@ -358,7 +358,7 @@ function parentdims(s::SubArray) j = 1 for i = 1:ndims(s.parent) r = s.indexes[i] - if j <= nd && (isa(r,Union{Slice,Range}) ? sp[i]*step(r) : sp[i]) == sv[j] + if j <= nd && (isa(r,Union{Slice,AbstractRange}) ? sp[i]*step(r) : sp[i]) == sv[j] dimindex[j] = i j += 1 end diff --git a/base/twiceprecision.jl b/base/twiceprecision.jl index a4e8f055377e5..0e9a32f12a9ff 100644 --- a/base/twiceprecision.jl +++ b/base/twiceprecision.jl @@ -485,20 +485,20 @@ convert(::Type{StepRangeLen{Float64}}, r::StepRangeLen) = convert(::Type{StepRangeLen{T}}, r::StepRangeLen) where {T<:IEEEFloat} = _convertSRL(StepRangeLen{T,Float64,Float64}, r) -convert(::Type{StepRangeLen{Float64}}, r::Range) = +convert(::Type{StepRangeLen{Float64}}, r::AbstractRange) = _convertSRL(StepRangeLen{Float64,TwicePrecision{Float64},TwicePrecision{Float64}}, r) -convert(::Type{StepRangeLen{T}}, r::Range) where {T<:IEEEFloat} = +convert(::Type{StepRangeLen{T}}, r::AbstractRange) where {T<:IEEEFloat} = _convertSRL(StepRangeLen{T,Float64,Float64}, r) function _convertSRL(::Type{StepRangeLen{T,R,S}}, r::StepRangeLen{<:Integer}) where {T,R,S} StepRangeLen{T,R,S}(R(r.ref), S(r.step), length(r), r.offset) end -function _convertSRL(::Type{StepRangeLen{T,R,S}}, r::Range{<:Integer}) where {T,R,S} +function _convertSRL(::Type{StepRangeLen{T,R,S}}, r::AbstractRange{<:Integer}) where {T,R,S} StepRangeLen{T,R,S}(R(first(r)), S(step(r)), length(r)) end -function _convertSRL(::Type{StepRangeLen{T,R,S}}, r::Range{U}) where {T,R,S,U} +function _convertSRL(::Type{StepRangeLen{T,R,S}}, r::AbstractRange{U}) where {T,R,S,U} # if start and step have a rational approximation in the old type, # then we transfer that rational approximation to the new type f, s = first(r), step(r) @@ -521,7 +521,7 @@ end function __convertSRL(::Type{StepRangeLen{T,R,S}}, r::StepRangeLen{U}) where {T,R,S,U} StepRangeLen{T,R,S}(R(r.ref), S(r.step), length(r), r.offset) end -function __convertSRL(::Type{StepRangeLen{T,R,S}}, r::Range{U}) where {T,R,S,U} +function __convertSRL(::Type{StepRangeLen{T,R,S}}, r::AbstractRange{U}) where {T,R,S,U} StepRangeLen{T,R,S}(R(first(r)), S(step(r)), length(r)) end diff --git a/contrib/Julia_Notepad++.xml b/contrib/Julia_Notepad++.xml index 2d548197bd9de..e43a1ab4865ee 100644 --- a/contrib/Julia_Notepad++.xml +++ b/contrib/Julia_Notepad++.xml @@ -25,7 +25,7 @@ true false C_NULL Inf NaN Inf32 NaN32 nothing - AbstractArray AbstractMatrix AbstractRemoteRef AbstractSparseMatrix AbstractString AbstractVector Any ArgumentError Array Associative BigFloat BigInt BitArray BitMatrix BitVector Bool BunchKaufman Cchar Cdouble Cfloat Char CharString CholeskyDense CholeskyPivotedDense Cint Cintmax_t Clong Clonglong Colon Complex Complex128 Complex64 ComplexPair Cptrdiff_t Cshort Csize_t Cuchar Cuint Cuintmax_t Culong Culonglong Cushort DArray Dict Dims DisconnectException EOFError EachLine EnvHash ErrorException Exception Expr Factorization Filter Float Float32 Float64 Function GSVDDense IO IOBuffer IOStream ImaginaryUnit InsertionSort Int Int128 Int16 Int32 Int64 Int8 IntSet Integer KeyError LDLTTridiagonal LUDense LUTridiagonal LoadError LocalProcess Matrix MergeSort MethodError NTuple Number ObjectIdDict ObjectIdDict OrdinalRange ParseError PipeBuffer ProcessGroup Ptr QRDense QRPivotedDense QuickSort Range Range1 RangeIndex Ranges Rational Real Regex RegexMatch RegexMatchIterator RepString RevString Reverse SVDDense Set Signed SparseMatrixCSC SpawnNullStream Stat StridedArray StridedMatrix StridedVecOrMat StridedVector String SubArray SubDArray SubOrDArray SubString SymTridiagonal Symbol SystemError Task TCPSocket TimSort Tridiagonal Tuple Type TypeError UInt UInt128 UInt16 UInt32 UInt64 UInt8 UVError Union Unsigned VecOrMat Vector VersionNumber Void WeakKeyDict WeakRef Zip + AbstractArray AbstractMatrix AbstractRange AbstractRemoteRef AbstractSparseMatrix AbstractString AbstractVector Any ArgumentError Array Associative BigFloat BigInt BitArray BitMatrix BitVector Bool BunchKaufman Cchar Cdouble Cfloat Char CharString CholeskyDense CholeskyPivotedDense Cint Cintmax_t Clong Clonglong Colon Complex Complex128 Complex64 ComplexPair Cptrdiff_t Cshort Csize_t Cuchar Cuint Cuintmax_t Culong Culonglong Cushort DArray Dict Dims DisconnectException EOFError EachLine EnvHash ErrorException Exception Expr Factorization Filter Float Float32 Float64 Function GSVDDense IO IOBuffer IOStream ImaginaryUnit InsertionSort Int Int128 Int16 Int32 Int64 Int8 IntSet Integer KeyError LDLTTridiagonal LUDense LUTridiagonal LoadError LocalProcess Matrix MergeSort MethodError NTuple Number ObjectIdDict ObjectIdDict OrdinalRange ParseError PipeBuffer ProcessGroup Ptr QRDense QRPivotedDense QuickSort RangeIndex Rational Real Regex RegexMatch RegexMatchIterator RepString RevString Reverse SVDDense Set Signed SparseMatrixCSC SpawnNullStream Stat StridedArray StridedMatrix StridedVecOrMat StridedVector String SubArray SubDArray SubOrDArray SubString SymTridiagonal Symbol SystemError Task TCPSocket TimSort Tridiagonal Tuple Type TypeError UInt UInt128 UInt16 UInt32 UInt64 UInt8 UVError Union UnitRange Unsigned VecOrMat Vector VersionNumber Void WeakKeyDict WeakRef Zip abstract begin baremodule primitive break catch ccall const continue do else elseif end export finally for function global if struct import importall let local macro module quote return try mutable typealias using while close enumerate error info open print println read write warn print println diff --git a/contrib/julia.hrc b/contrib/julia.hrc index 5162fc0447b13..b175639b84f3e 100644 --- a/contrib/julia.hrc +++ b/contrib/julia.hrc @@ -146,8 +146,8 @@ - - + + diff --git a/contrib/julia.lang b/contrib/julia.lang index 566685a1ebb3a..30705ae7eb3d4 100644 --- a/contrib/julia.lang +++ b/contrib/julia.lang @@ -231,7 +231,7 @@ Any|Void Type(Constructor|Name|Var|_Array)?|(Union|Data|NonTuple)Type (Abstract|Strided|Bit)?(Array|Matrix|Vector) - Abstract(Cmd|RNG|SparseMatrix) + Abstract(Cmd|Range|RNG|SparseMatrix) (Abstract|Strided)?VecOrMat SparseMatrixCSC (D|Sub((Or)?D)?)Array @@ -301,7 +301,7 @@ Process(Chain(OrNot)?|Group)? Ptr QR(Pivoted)? - Range(s|1|Index|VecIntList)? + Range(Index|VecIntList)? RawOrBoxedHandle Redirectable Regex(Match(Iterator)?)? @@ -325,6 +325,7 @@ UV(Handle|PollingWatcher|Stream) UDPSocket Undef(RefTag)? + UnitRange VarTable Vararg VersionNumber diff --git a/contrib/julia.xml b/contrib/julia.xml index 65ebbc95ee351..a0bb9c33b7984 100644 --- a/contrib/julia.xml +++ b/contrib/julia.xml @@ -77,6 +77,7 @@ AbstractArray AbstractMatrix + AbstractRange AbstractRemoteRef AbstractSparseMatrix AbstractVector @@ -148,10 +149,7 @@ PipeBuffer ProcessGroup Ptr - Range - Range1 RangeIndex - Ranges Rational Real Regex @@ -193,6 +191,7 @@ UInt64 UInt128 Union + UnitRange Unsigned UVError VecOrMat diff --git a/doc/src/manual/arrays.md b/doc/src/manual/arrays.md index e9b5d50d8580d..c7691100cc1cc 100644 --- a/doc/src/manual/arrays.md +++ b/doc/src/manual/arrays.md @@ -348,7 +348,7 @@ indices and can be converted to such by [`to_indices`](@ref): 2. An array of scalar indices. This includes: * Vectors and multidimensional arrays of integers * Empty arrays like `[]`, which select no elements - * `Range`s of the form `a:c` or `a:b:c`, which select contiguous or strided subsections from `a` to `c` (inclusive) + * Ranges like `a:c` or `a:b:c`, which select contiguous or strided subsections from `a` to `c` (inclusive) * Any custom array of scalar indices that is a subtype of `AbstractArray` * Arrays of `CartesianIndex{N}` (see below for more details) 3. An object that represents an array of scalar indices and can be converted to such by [`to_indices`](@ref). By default this includes: diff --git a/doc/src/manual/control-flow.md b/doc/src/manual/control-flow.md index 37f44ee167343..a48b619d237a6 100644 --- a/doc/src/manual/control-flow.md +++ b/doc/src/manual/control-flow.md @@ -409,7 +409,7 @@ julia> for i = 1:5 5 ``` -Here the `1:5` is a `Range` object, representing the sequence of numbers 1, 2, 3, 4, 5. The `for` +Here the `1:5` is a range object, representing the sequence of numbers 1, 2, 3, 4, 5. The `for` loop iterates through these values, assigning each one in turn to the variable `i`. One rather important distinction between the previous `while` loop form and the `for` loop form is the scope during which the variable is visible. If the variable `i` has not been introduced in an other diff --git a/doc/src/manual/interfaces.md b/doc/src/manual/interfaces.md index f321efb204aac..8576bffa4d9b7 100644 --- a/doc/src/manual/interfaces.md +++ b/doc/src/manual/interfaces.md @@ -327,8 +327,8 @@ julia> A[:] = 1:length(A); A ``` The result of indexing an `AbstractArray` can itself be an array (for instance when indexing by -a `Range`). The `AbstractArray` fallback methods use [`similar`](@ref) to allocate an `Array` of the -appropriate size and element type, which is filled in using the basic indexing method described +an `AbstractRange`). The `AbstractArray` fallback methods use [`similar`](@ref) to allocate an `Array` +of the appropriate size and element type, which is filled in using the basic indexing method described above. However, when implementing an array wrapper you often want the result to be wrapped as well: diff --git a/doc/src/manual/noteworthy-differences.md b/doc/src/manual/noteworthy-differences.md index 202dbf12224cd..65bc7e37e76ae 100644 --- a/doc/src/manual/noteworthy-differences.md +++ b/doc/src/manual/noteworthy-differences.md @@ -34,8 +34,8 @@ may trip up Julia users accustomed to MATLAB: with spaces (`[x y z]`). - To construct block matrices (concatenating in the first two dimensions), use either [`hvcat`](@ref) or combine spaces and semicolons (`[a b; c d]`). - * In Julia, `a:b` and `a:b:c` construct `Range` objects. To construct a full vector like in MATLAB, - use [`collect(a:b)`](@ref). Generally, there is no need to call `collect` though. `Range` will + * In Julia, `a:b` and `a:b:c` construct `AbstractRange` objects. To construct a full vector like in MATLAB, + use [`collect(a:b)`](@ref). Generally, there is no need to call `collect` though. An `AbstractRange` object will act like a normal array in most cases but is more efficient because it lazily computes its values. This pattern of creating specialized objects instead of full arrays is used frequently, and is also seen in functions such as [`linspace`](@ref), or with iterators such as `enumerate`, and @@ -156,8 +156,8 @@ For users coming to Julia from R, these are some noteworthy differences: on large data structures much more efficiently. * In Julia, vectors and matrices are concatenated using [`hcat`](@ref), [`vcat`](@ref) and [`hvcat`](@ref), not `c`, `rbind` and `cbind` like in R. - * In Julia, a range like `a:b` is not shorthand for a vector like in R, but is a specialized `Range` - that is used for iteration without high memory overhead. To convert a range into a vector, use + * In Julia, a range like `a:b` is not shorthand for a vector like in R, but is a specialized `AbstractRange` + object that is used for iteration without high memory overhead. To convert a range into a vector, use [`collect(a:b)`](@ref). * Julia's [`max`](@ref) and [`min`](@ref) are the equivalent of `pmax` and `pmin` respectively in R, but both arguments need to have the same dimensions. While [`maximum`](@ref) and [`minimum`](@ref) diff --git a/doc/src/stdlib/collections.md b/doc/src/stdlib/collections.md index e83303f7ab89a..5c5ecc11ca317 100644 --- a/doc/src/stdlib/collections.md +++ b/doc/src/stdlib/collections.md @@ -35,7 +35,7 @@ Base.iteratoreltype Fully implemented by: - * `Range` + * `AbstractRange` * `UnitRange` * `Tuple` * `Number` @@ -58,7 +58,7 @@ Base.length Fully implemented by: - * `Range` + * `AbstractRange` * `UnitRange` * `Tuple` * `Number` @@ -151,7 +151,7 @@ Fully implemented by: Partially implemented by: - * `Range` + * `AbstractRange` * `UnitRange` * `Tuple` * `AbstractString` diff --git a/test/TestHelpers.jl b/test/TestHelpers.jl index 84f8edc7fc7cb..6a2761997cb27 100644 --- a/test/TestHelpers.jl +++ b/test/TestHelpers.jl @@ -221,7 +221,7 @@ offset(offsets::NTuple{N,Int}, inds::NTuple{N,Int}) where {N} = _offset((), offs _offset(out, ::Tuple{}, ::Tuple{}) = out @inline _offset(out, offsets, inds) = _offset((out..., inds[1]-offsets[1]), Base.tail(offsets), Base.tail(inds)) -indsoffset(r::Range) = first(r) - 1 +indsoffset(r::AbstractRange) = first(r) - 1 indsoffset(i::Integer) = 0 Base.resize!(A::OffsetVector, nl::Integer) = (resize!(A.parent, nl); A) diff --git a/test/core.jl b/test/core.jl index 9c5d8e37da9d6..822be8dae02d4 100644 --- a/test/core.jl +++ b/test/core.jl @@ -1737,7 +1737,7 @@ mutable struct A6142 <: AbstractMatrix{Float64}; end +(x::A6142, y::UniformScaling{TJ}) where {TJ} = "UniformScaling method called" +(x::A6142, y::AbstractArray) = "AbstractArray method called" @test A6142() + I == "UniformScaling method called" -+(x::A6142, y::Range) = "Range method called" #16324 ambiguity ++(x::A6142, y::AbstractRange) = "AbstractRange method called" #16324 ambiguity # issue #6175 function g6175(); print(""); (); end @@ -1850,7 +1850,7 @@ end # issue #6387 primitive type Date6387{C} 64 end -mutable struct DateRange6387{C} <: Range{Date6387{C}} +mutable struct DateRange6387{C} <: AbstractRange{Date6387{C}} end mutable struct ObjMember @@ -1859,7 +1859,7 @@ end obj6387 = ObjMember(DateRange6387{Int64}()) -function v6387(r::Range{T}) where T +function v6387(r::AbstractRange{T}) where T a = Array{T}(1) a[1] = Core.Intrinsics.bitcast(Date6387{Int64}, Int64(1)) return a diff --git a/test/ranges.jl b/test/ranges.jl index a318f04b2c0f0..b9e1696f7590b 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -294,7 +294,7 @@ end @test intersect(-51:5:100, -33:7:125) == -26:35:79 @test intersect(-51:5:100, -32:7:125) == -11:35:94 #@test intersect(0:6:24, 6+0*(0:4:24)) == 6:6:6 -#@test intersect(12+0*(0:6:24), 0:4:24) == Range(12, 0, 5) +#@test intersect(12+0*(0:6:24), 0:4:24) == AbstractRange(12, 0, 5) #@test isempty(intersect(6+0*(0:6:24), 0:4:24)) @test intersect(-10:3:24, -10:3:24) == -10:3:23 @test isempty(intersect(-11:3:24, -10:3:24)) @@ -372,7 +372,7 @@ r = (-4*Int64(maxintfloat(Int === Int32 ? Float32 : Float64))):5 @test_throws BoundsError (0:2:10)[7:7] # indexing with negative ranges (#8351) -for a=Range[3:6, 0:2:10], b=Range[0:1, 2:-1:0] +for a=AbstractRange[3:6, 0:2:10], b=AbstractRange[0:1, 2:-1:0] @test_throws BoundsError a[b] end @@ -649,9 +649,9 @@ r = LinSpace(1,4,4) # comparing and hashing ranges let - Rs = Range[1:2, map(Int32,1:3:17), map(Int64,1:3:17), 1:0, 17:-3:0, - 0.0:0.1:1.0, map(Float32,0.0:0.1:1.0), - linspace(0, 1, 20), map(Float32, linspace(0, 1, 20))] + Rs = AbstractRange[1:2, map(Int32,1:3:17), map(Int64,1:3:17), 1:0, 17:-3:0, + 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) @@ -713,7 +713,7 @@ end # issue #7114 r = -0.004532318104333742:1.2597349521122731e-5:0.008065031416788989 @test length(r[1:end-1]) == length(r) - 1 -@test isa(r[1:2:end],Range) && length(r[1:2:end]) == div(length(r)+1, 2) +@test isa(r[1:2:end],AbstractRange) && length(r[1:2:end]) == div(length(r)+1, 2) @test r[3:5][2] ≈ r[4] @test r[5:-2:1][2] ≈ r[3] @test_throws BoundsError r[0:10] @@ -896,7 +896,7 @@ test_range_index(linspace(1.0, 1.0, 1), 1:1) test_range_index(linspace(1.0, 1.0, 1), 1:0) test_range_index(linspace(1.0, 2.0, 0), 1:0) -function test_linspace_identity(r::Range{T}, mr) where T +function test_linspace_identity(r::AbstractRange{T}, mr) where T @test -r == mr @test -collect(r) == collect(mr) @test isa(-r, typeof(r)) @@ -945,7 +945,7 @@ for _r in (1:2:100, 1:100, 1f0:2f0:100f0, 1.0:2.0:100.0, @test isa(float_r, typeof(_r)) @test eltype(big_r) === BigFloat else - @test isa(float_r, Range) + @test isa(float_r, AbstractRange) @test eltype(float_r) <: AbstractFloat @test eltype(big_r) === BigInt end diff --git a/test/sorting.jl b/test/sorting.jl index cc5545e353d04..192b739ef9665 100644 --- a/test/sorting.jl +++ b/test/sorting.jl @@ -95,7 +95,7 @@ end @test searchsortedlast(500:1.0:600, 1.0e20) == 101 # exercise the codepath in searchsorted* methods for ranges that check for zero step range -struct ConstantRange{T} <: Range{T} +struct ConstantRange{T} <: AbstractRange{T} val::T len::Int end diff --git a/test/specificity.jl b/test/specificity.jl index 417af2ed4a43d..67a815cee07ef 100644 --- a/test/specificity.jl +++ b/test/specificity.jl @@ -96,7 +96,7 @@ end # a method specificity issue c99991(::Type{T},x::T) where {T} = 0 c99991(::Type{UnitRange{T}},x::StepRangeLen{T}) where {T} = 1 -c99991(::Type{UnitRange{T}},x::Range{T}) where {T} = 2 +c99991(::Type{UnitRange{T}},x::AbstractRange{T}) where {T} = 2 @test c99991(UnitRange{Float64}, 1.0:2.0) == 1 @test c99991(UnitRange{Int}, 1:2) == 2 diff --git a/test/subtype.jl b/test/subtype.jl index 050dc8af0a6fb..3e68f9ce4d97a 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -646,8 +646,8 @@ function test_intersection() @testintersect(Tuple{Type{Ptr{UInt8}}, Ptr{Bottom}}, (@UnionAll T Tuple{Type{Ptr{T}},Ptr{T}}), Bottom) - @testintersect(Tuple{Range{Int},Tuple{Int,Int}}, (@UnionAll T Tuple{AbstractArray{T},Dims}), - Tuple{Range{Int},Tuple{Int,Int}}) + @testintersect(Tuple{AbstractRange{Int},Tuple{Int,Int}}, (@UnionAll T Tuple{AbstractArray{T},Dims}), + Tuple{AbstractRange{Int},Tuple{Int,Int}}) @testintersect((@UnionAll Integer<:T<:Number Array{T}), (@UnionAll T<:Number Array{T}), (@UnionAll Integer<:T<:Number Array{T})) From f58b7ff4147761f6cfa5dae2ef7df080b3c84353 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Fri, 8 Sep 2017 17:28:07 +0200 Subject: [PATCH 313/324] REPL: edit_backspace: fix bad passed option --- base/repl/LineEdit.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/repl/LineEdit.jl b/base/repl/LineEdit.jl index ebf27b7a7bad9..bcfc57cda6f76 100644 --- a/base/repl/LineEdit.jl +++ b/base/repl/LineEdit.jl @@ -551,7 +551,7 @@ end # on the right function edit_backspace(s::PromptState, align::Bool=false, adjust=align) push_undo(s) - if edit_backspace(buffer(s), align) + if edit_backspace(buffer(s), align, adjust) refresh_line(s) else pop_undo(s) From 07f132fe719088f783206e46a2e527823aab9472 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 8 Sep 2017 14:14:27 -0400 Subject: [PATCH 314/324] set JULIA_HOME to try point to the julia binary (#23525) fix #14577 --- examples/embedding/LocalModule.jl | 13 ++++++++ examples/embedding/Makefile | 9 +++++- examples/embedding/embedding-test.jl | 3 +- examples/embedding/embedding.c | 19 ++++++++++++ src/jlapi.c | 46 +++++++++++++++++++--------- 5 files changed, 74 insertions(+), 16 deletions(-) create mode 100644 examples/embedding/LocalModule.jl diff --git a/examples/embedding/LocalModule.jl b/examples/embedding/LocalModule.jl new file mode 100644 index 0000000000000..340bd4bc7f666 --- /dev/null +++ b/examples/embedding/LocalModule.jl @@ -0,0 +1,13 @@ +__precompile__() +module LocalModule + +export myapp + +function myapp() + p = addprocs(1) + @everywhere p println("Taking over the world...") + rmprocs(p) + nothing +end + +end diff --git a/examples/embedding/Makefile b/examples/embedding/Makefile index 38e9bc33b2cae..acfd9baf545ed 100644 --- a/examples/embedding/Makefile +++ b/examples/embedding/Makefile @@ -38,7 +38,14 @@ $(BIN)/embedding$(EXE): $(SRCDIR)/embedding.c $(BIN)/embedding-debug$(EXE): $(SRCDIR)/embedding.c $(CC) $^ -o $@ $(CPPFLAGS_ADD) $(CPPFLAGS) $(CFLAGS_ADD) $(CFLAGS) $(LDFLAGS_ADD) $(LDFLAGS) $(DEBUGFLAGS) -check: $(BIN)/embedding$(EXE) +ifneq ($(abspath $(BIN)),$(abspath $(SRCDIR))) +# for demonstration purposes, our demo code is also installed +# in $BIN, although this would likely not be typical +$(BIN)/LocalModule.jl: $(SRCDIR)/LocalModule.jl + cp $< $@ +endif + +check: $(BIN)/embedding$(EXE) $(BIN)/LocalModule.jl $(JULIA) $(SRCDIR)/embedding-test.jl $< @echo SUCCESS diff --git a/examples/embedding/embedding-test.jl b/examples/embedding/embedding-test.jl index fcdbf7fbf2999..11aaca5b5d258 100644 --- a/examples/embedding/embedding-test.jl +++ b/examples/embedding/embedding-test.jl @@ -20,8 +20,9 @@ end @test stderr == "MethodError: no method matching this_function_has_no_methods()\n" @test success(p) lines = wait(stdout_task) - @test length(lines) == 9 + @test length(lines) == 10 @test parse(Float64, lines[1]) ≈ sqrt(2) @test lines[8] == "called bar" @test lines[9] == "calling new bar" + @test lines[10] == "\tFrom worker 2:\tTaking over the world..." end diff --git a/examples/embedding/embedding.c b/examples/embedding/embedding.c index 712e46dd8b6b4..a1fc6c676620b 100644 --- a/examples/embedding/embedding.c +++ b/examples/embedding/embedding.c @@ -149,6 +149,25 @@ int main() bar(); } + { + // Importing a Julia package + + checked_eval_string("let d = dirname(unsafe_string(Base.JLOptions().julia_bin))\n" + // disable the package manager + " ENV[\"JULIA_PKGDIR\"] = joinpath(d, \"disabled\")\n" + // locate files relative to the "embedding" executable + " empty!(LOAD_PATH)\n" + " push!(LOAD_PATH, d)\n" + // this directory needs to be writable for the example, + // although in real code it usually wouldn't necessarily be used that way + " empty!(Base.LOAD_CACHE_PATH)\n" + " push!(Base.LOAD_CACHE_PATH, tempdir())\n" + "end"); + checked_eval_string("import LocalModule"); + checked_eval_string("LocalModule.myapp()"); + } + + int ret = 0; jl_atexit_hook(ret); return ret; diff --git a/src/jlapi.c b/src/jlapi.c index ebd16f11914ba..eadf3eafc7f27 100644 --- a/src/jlapi.c +++ b/src/jlapi.c @@ -26,19 +26,24 @@ JL_DLLEXPORT char * __cdecl dirname(char *); #else #include #endif +#ifndef _OS_WINDOWS_ +#include +#endif -JL_DLLEXPORT int jl_is_initialized(void) { return jl_main_module!=NULL; } +JL_DLLEXPORT int jl_is_initialized(void) +{ + return jl_main_module != NULL; +} -// First argument is the usr/lib directory where libjulia is, or NULL to guess. -// if that doesn't work, try the full path to the "lib" directory that -// contains lib/julia/sys.ji +// First argument is the usr/bin directory where the julia binary is, or NULL to guess. // Second argument is the path of a system image file (*.ji) relative to the -// first argument path, or relative to the default julia home dir. The default -// is something like ../lib/julia/sys.ji +// first argument path, or relative to the default julia home dir. +// The default is something like ../lib/julia/sys.ji JL_DLLEXPORT void jl_init_with_image(const char *julia_home_dir, const char *image_relative_path) { - if (jl_is_initialized()) return; + if (jl_is_initialized()) + return; libsupport_init(); jl_options.julia_home = julia_home_dir; if (image_relative_path != NULL) @@ -51,17 +56,30 @@ JL_DLLEXPORT void jl_init_with_image(const char *julia_home_dir, JL_DLLEXPORT void jl_init(void) { - char *libjldir = NULL; - + char *libbindir = NULL; +#ifdef _OS_WINDOWS_ void *hdl = (void*)jl_load_dynamic_library_e(NULL, JL_RTLD_DEFAULT); - if (hdl) - libjldir = dirname((char*)jl_pathname_for_handle(hdl)); - if (libjldir) - jl_init_with_image(libjldir, jl_get_default_sysimg_path()); - else { + if (hdl) { + char *to_free = (char*)jl_pathname_for_handle(hdl); + if (to_free) { + libbindir = strdup(dirname(to_free)); + free(to_free); + } + } +#else + Dl_info dlinfo; + if (dladdr((void*)jl_init, &dlinfo) != 0 && dlinfo.dli_fname) { + char *to_free = strdup(dlinfo.dli_fname); + (void)asprintf(&libbindir, "%s" PATHSEPSTRING ".." PATHSEPSTRING "%s", dirname(to_free), "bin"); + free(to_free); + } +#endif + if (!libbindir) { printf("jl_init unable to find libjulia!\n"); abort(); } + jl_init_with_image(libbindir, jl_get_default_sysimg_path()); + free(libbindir); } JL_DLLEXPORT jl_value_t *jl_eval_string(const char *str) From b9ba7c499be85652b0603eb1d8fdbe7a46dff9b7 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Fri, 8 Sep 2017 17:34:42 +0200 Subject: [PATCH 315/324] REPL: add an Options type to handle REPL options It was previously not practical to disable the "align" feature of backspace: overwriting the '\b' binding with: `'\b' => (s,o...) -> Base.LineEdit.edit_backspace(s, false)` worked to a certain extent, but for example it was disabling the feature that '\b' must get you out of shell/help mode. A new experimental Options type is introduced here, which is meant to conveniently allow users to pass options controlling the REPL behavior. For example: atreplinit() do repl repl.options = Base.REPL.Options(backspace_align = false) end --- base/repl/LineEdit.jl | 19 +++++++++++-------- base/repl/REPL.jl | 19 +++++++++++++++---- test/lineedit.jl | 8 ++++---- 3 files changed, 30 insertions(+), 16 deletions(-) diff --git a/base/repl/LineEdit.jl b/base/repl/LineEdit.jl index bcfc57cda6f76..72cf884858464 100644 --- a/base/repl/LineEdit.jl +++ b/base/repl/LineEdit.jl @@ -27,7 +27,7 @@ mutable struct Prompt <: TextInterface # Same as prefix except after the prompt prompt_suffix::Union{String,Function} keymap_dict::Dict{Char} - keymap_func_data # ::AbstractREPL + repl # ::AbstractREPL complete # ::REPLCompletionProvider on_enter::Function on_done::Function @@ -75,6 +75,8 @@ mutable struct PromptState <: ModeState indent::Int end +options(s::PromptState) = isdefined(s.p, :repl) ? s.p.repl.options : Base.REPL.Options() + setmark(s) = mark(buffer(s)) # the default mark is 0 @@ -116,7 +118,7 @@ terminal(s::PromptState) = s.terminal for f in [:terminal, :on_enter, :add_history, :buffer, :(Base.isempty), :replace_line, :refresh_multi_line, :input_string, :update_display_buffer, - :empty_undo, :push_undo, :pop_undo] + :empty_undo, :push_undo, :pop_undo, :options] @eval ($f)(s::MIState, args...) = $(f)(state(s), args...) end @@ -549,7 +551,8 @@ end # align: delete up to 4 spaces to align to a multiple of 4 chars # adjust: also delete spaces on the right of the cursor to try to keep aligned what is # on the right -function edit_backspace(s::PromptState, align::Bool=false, adjust=align) +function edit_backspace(s::PromptState, align::Bool=options(s).backspace_align, + adjust=options(s).backspace_adjust) push_undo(s) if edit_backspace(buffer(s), align, adjust) refresh_line(s) @@ -571,7 +574,7 @@ function endofline(buf, pos=position(buf)) eol == 0 ? buf.size : pos + eol - 1 end -function edit_backspace(buf::IOBuffer, align::Bool=false, adjust::Bool=align) +function edit_backspace(buf::IOBuffer, align::Bool=false, adjust::Bool=false) !align && adjust && throw(DomainError((align, adjust), "if `adjust` is `true`, `align` must be `true`")) @@ -1649,7 +1652,7 @@ AnyDict( end, '\n' => KeyAlias('\r'), # Backspace/^H - '\b' => (s,o...)->edit_backspace(s, true), + '\b' => (s,o...)->edit_backspace(s), 127 => KeyAlias('\b'), # Meta Backspace "\e\b" => (s,o...)->edit_delete_prev_word(s), @@ -1860,14 +1863,14 @@ function Prompt(prompt; prompt_prefix = "", prompt_suffix = "", keymap_dict = default_keymap_dict, - keymap_func_data = nothing, + repl = nothing, complete = EmptyCompletionProvider(), on_enter = default_enter_cb, on_done = ()->nothing, hist = EmptyHistoryProvider(), sticky = false) - Prompt(prompt, prompt_prefix, prompt_suffix, keymap_dict, keymap_func_data, + Prompt(prompt, prompt_prefix, prompt_suffix, keymap_dict, repl, complete, on_enter, on_done, hist, sticky) end @@ -1968,7 +1971,7 @@ end edit_redo!(s) = nothing keymap(s::PromptState, prompt::Prompt) = prompt.keymap_dict -keymap_data(s::PromptState, prompt::Prompt) = prompt.keymap_func_data +keymap_data(s::PromptState, prompt::Prompt) = prompt.repl keymap(ms::MIState, m::ModalInterface) = keymap(state(ms), mode(ms)) keymap_data(ms::MIState, m::ModalInterface) = keymap_data(state(ms), mode(ms)) diff --git a/base/repl/REPL.jl b/base/repl/REPL.jl index c8fc5a3fb42ba..c975b1b6329f5 100644 --- a/base/repl/REPL.jl +++ b/base/repl/REPL.jl @@ -241,6 +241,16 @@ function run_frontend(repl::BasicREPL, backend::REPLBackendRef) dopushdisplay && popdisplay(d) end +## Custom Options + +mutable struct Options + backspace_align::Bool + backspace_adjust::Bool +end + +Options(; backspace_align=true, backspace_adjust=backspace_align) = + Options(backspace_align, backspace_adjust) + ## LineEditREPL ## mutable struct LineEditREPL <: AbstractREPL @@ -257,11 +267,12 @@ mutable struct LineEditREPL <: AbstractREPL envcolors::Bool waserror::Bool specialdisplay::Union{Void,Display} + options::Options interface::ModalInterface backendref::REPLBackendRef LineEditREPL(t,hascolor,prompt_color,input_color,answer_color,shell_color,help_color,history_file,in_shell,in_help,envcolors) = new(t,true,prompt_color,input_color,answer_color,shell_color,help_color,history_file,in_shell, - in_help,envcolors,false,nothing) + in_help,envcolors,false,nothing, Options()) end outstream(r::LineEditREPL) = r.t specialdisplay(r::LineEditREPL) = r.specialdisplay @@ -741,7 +752,7 @@ function setup_interface( prompt_prefix = hascolor ? repl.prompt_color : "", prompt_suffix = hascolor ? (repl.envcolors ? Base.input_color : repl.input_color) : "", - keymap_func_data = repl, + repl = repl, complete = replc, on_enter = return_callback) @@ -750,7 +761,7 @@ function setup_interface( prompt_prefix = hascolor ? repl.help_color : "", prompt_suffix = hascolor ? (repl.envcolors ? Base.input_color : repl.input_color) : "", - keymap_func_data = repl, + repl = repl, complete = replc, # When we're done transform the entered line into a call to help("$line") on_done = respond(Docs.helpmode, repl, julia_prompt)) @@ -760,7 +771,7 @@ function setup_interface( prompt_prefix = hascolor ? repl.shell_color : "", prompt_suffix = hascolor ? (repl.envcolors ? Base.input_color : repl.input_color) : "", - keymap_func_data = repl, + repl = repl, complete = ShellCompletionProvider(), # Transform "foo bar baz" into `foo bar baz` (shell quoting) # and pass into Base.repl_cmd for processing (handles `ls` and `cd` diff --git a/test/lineedit.jl b/test/lineedit.jl index db08ac64c2bc4..ac03ee24229b5 100644 --- a/test/lineedit.jl +++ b/test/lineedit.jl @@ -330,7 +330,7 @@ let buf = IOBuffer() @test position(buf) == 0 LineEdit.edit_move_right(buf) @test nb_available(buf) == 0 - LineEdit.edit_backspace(buf) + LineEdit.edit_backspace(buf, false, false) @test content(buf) == "a" end @@ -699,9 +699,9 @@ end @test edit!(edit_undo!) == "one two three" LineEdit.move_line_end(s) - LineEdit.edit_backspace(s) - LineEdit.edit_backspace(s) - @test edit!(LineEdit.edit_backspace) == "one two th" + LineEdit.edit_backspace(s, false, false) + LineEdit.edit_backspace(s, false, false) + @test edit!(s->LineEdit.edit_backspace(s, false, false)) == "one two th" @test edit!(edit_undo!) == "one two thr" @test edit!(edit_undo!) == "one two thre" @test edit!(edit_undo!) == "one two three" From 0245e2e49e88a33c324e6458a4c196edf9c1cb17 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Fri, 8 Sep 2017 18:22:09 +0200 Subject: [PATCH 316/324] REPL: add hascolor & extra_repl_keymap to Options --- base/repl/REPL.jl | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/base/repl/REPL.jl b/base/repl/REPL.jl index c975b1b6329f5..d13faef285281 100644 --- a/base/repl/REPL.jl +++ b/base/repl/REPL.jl @@ -241,15 +241,20 @@ function run_frontend(repl::BasicREPL, backend::REPLBackendRef) dopushdisplay && popdisplay(d) end -## Custom Options +## User Options mutable struct Options + hascolor::Bool + extra_keymap::Union{Dict,Vector{<:Dict}} backspace_align::Bool backspace_adjust::Bool end -Options(; backspace_align=true, backspace_adjust=backspace_align) = - Options(backspace_align, backspace_adjust) +Options(; + hascolor = true, + extra_keymap = AnyDict[], + backspace_align = true, backspace_adjust = backspace_align) = + Options(hascolor, extra_keymap, backspace_align, backspace_adjust) ## LineEditREPL ## @@ -714,8 +719,9 @@ enable_promptpaste(v::Bool) = JL_PROMPT_PASTE[] = v function setup_interface( repl::LineEditREPL; - hascolor::Bool = repl.hascolor, - extra_repl_keymap::Union{Dict,Vector{<:Dict}} = Dict{Any,Any}[] + # those keyword arguments may be deprecated eventually in favor of the Options mechanism + hascolor::Bool = repl.options.hascolor, + extra_repl_keymap::Union{Dict,Vector{<:Dict}} = repl.options.extra_keymap ) ### # From e16f4dce0fb707214fe84ab2bf4c8cba71d135c6 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Sat, 9 Sep 2017 14:46:39 +0200 Subject: [PATCH 317/324] REPL: fix edit_yank_pop when require_previous_yank=false (#23635) --- base/repl/LineEdit.jl | 5 +++-- test/lineedit.jl | 4 ++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/base/repl/LineEdit.jl b/base/repl/LineEdit.jl index ebf27b7a7bad9..f3499f01209c5 100644 --- a/base/repl/LineEdit.jl +++ b/base/repl/LineEdit.jl @@ -684,11 +684,12 @@ function edit_yank(s::MIState) end function edit_yank_pop(s::MIState, require_previous_yank=true) - if require_previous_yank && !(s.last_action in [:edit_yank, :edit_yank_pop]) || - isempty(s.kill_ring) + repeat = s.last_action ∈ (:edit_yank, :edit_yank_pop) + if require_previous_yank && !repeat || isempty(s.kill_ring) beep(terminal(s)) :ignore else + require_previous_yank || repeat || setmark(s) push_undo(s) edit_splice!(s, s.kill_ring[mod1(s.kill_idx-=1, end)]) refresh_line(s) diff --git a/test/lineedit.jl b/test/lineedit.jl index db08ac64c2bc4..f3794860c3ec7 100644 --- a/test/lineedit.jl +++ b/test/lineedit.jl @@ -617,6 +617,10 @@ end LineEdit.edit_kill_line(s) @test s.kill_ring[end] == "çhing" @test s.kill_idx == 3 + # check that edit_yank_pop works when passing require_previous_yank=false (#23635) + s.last_action = :unknown + @test transform!(s->LineEdit.edit_yank_pop(s, false), s) == ("ça ≡ nothinga ≡ not", 19, 12) + # repetition (concatenation of killed strings edit_insert(s, "A B C") LineEdit.edit_delete_prev_word(s) From 36b7565ff0c706169354514a0c3794dc3931653b Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Sat, 9 Sep 2017 15:59:32 +0200 Subject: [PATCH 318/324] runtests: don't stop if exception is thrown with 1 worker (#23576) And fix typo. --- test/runtests.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/runtests.jl b/test/runtests.jl index 262ca6a46ecca..c2b8b4bfdc39c 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -59,14 +59,14 @@ cd(dirname(@__FILE__)) do resp = [e] end push!(results, (test, resp)) - if (isa(resp[end], Integer) && (resp[end] > max_worker_rss)) || isa(resp, Exception) + if resp[1] isa Exception || resp[end] > max_worker_rss if n > 1 rmprocs(wrkr, waitfor=30) p = addprocs_with_testenv(1)[1] remotecall_fetch(include, p, "testdefs.jl") else - # single process testing, bail if mem limit reached, or, on an exception. - isa(resp, Exception) ? rethrow(resp) : error("Halting tests. Memory limit reached : $resp > $max_worker_rss") + # single process testing, bail if mem limit reached + resp[1] isa Exception || error("Halting tests. Memory limit reached : $resp > $max_worker_rss") end end if !isa(resp[1], Exception) From 368bb49efb7a8fd2c7372c2a405bcee5f415c35b Mon Sep 17 00:00:00 2001 From: Sacha Verweij Date: Sat, 9 Sep 2017 08:00:57 -0700 Subject: [PATCH 319/324] Deprecate readcsv in favor of readdlm. (#23530) --- NEWS.md | 3 ++ base/datafmt.jl | 10 +----- base/deprecated.jl | 4 +++ base/exports.jl | 1 - doc/src/manual/packages.md | 2 +- doc/src/stdlib/io-network.md | 1 - test/datafmt.jl | 66 ++++++++++++++++++------------------ test/read.jl | 6 ++-- 8 files changed, 45 insertions(+), 48 deletions(-) diff --git a/NEWS.md b/NEWS.md index b635e507fd78e..e838a3ed0e800 100644 --- a/NEWS.md +++ b/NEWS.md @@ -284,6 +284,9 @@ Deprecated or removed * The method `srand(rng, filename, n=4)` has been deprecated ([#21359]). + * `readcsv(io[, T::Type]; opts...)` has been deprecated in favor of + `readdlm(io, ','[, T]; opts...)` ([#23530]). + * The `cholfact`/`cholfact!` methods that accepted an `uplo` symbol have been deprecated in favor of using `Hermitian` (or `Symmetric`) views ([#22187], [#22188]). diff --git a/base/datafmt.jl b/base/datafmt.jl index 04525ae9ad158..6ceecd14acf40 100644 --- a/base/datafmt.jl +++ b/base/datafmt.jl @@ -6,7 +6,7 @@ module DataFmt import Base: _default_delims, tryparse_internal, show -export countlines, readdlm, readcsv, writedlm +export countlines, readdlm, writedlm invalid_dlm(::Type{Char}) = reinterpret(Char, 0xfffffffe) invalid_dlm(::Type{UInt8}) = 0xfe @@ -616,14 +616,6 @@ function dlm_parse(dbuff::String, eol::D, dlm::D, qchar::D, cchar::D, return (nrows, ncols) end -""" - readcsv(source, [T::Type]; options...) - -Equivalent to [`readdlm`](@ref) with `delim` set to comma, and type optionally defined by `T`. -""" -readcsv(io; opts...) = readdlm(io, ','; opts...) -readcsv(io, T::Type; opts...) = readdlm(io, ',', T; opts...) - # todo: keyword argument for # of digits to print writedlm_cell(io::IO, elt::AbstractFloat, dlm, quotes) = print_shortest(io, elt) function writedlm_cell(io::IO, elt::AbstractString, dlm::T, quotes::Bool) where T diff --git a/base/deprecated.jl b/base/deprecated.jl index 661276c56569c..65d99d763ac0f 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1735,6 +1735,10 @@ function IOContext(io::IO; kws...) IOContext(io, (k=>v for (k, v) in kws)...) end +# deprecate readcsv +@deprecate readcsv(io; opts...) readdlm(io, ','; opts...) +@deprecate readcsv(io, T::Type; opts...) readdlm(io, ',', T; opts...) + @deprecate IOContext(io::IO, key, value) IOContext(io, key=>value) # PR #23485 diff --git a/base/exports.jl b/base/exports.jl index 24548821f7c92..0b359c200e64a 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -1049,7 +1049,6 @@ export readavailable, readbytes!, readchomp, - readcsv, readdir, readdlm, readline, diff --git a/doc/src/manual/packages.md b/doc/src/manual/packages.md index ea024571f6f32..19a6f714a1cc5 100644 --- a/doc/src/manual/packages.md +++ b/doc/src/manual/packages.md @@ -811,7 +811,7 @@ the directory of the current source file. For example if `FooBar/src/FooBar.jl` ```julia datapath = joinpath(@__DIR__, "..", "data") -foo = readcsv(joinpath(datapath, "foo.csv")) +foo = readdlm(joinpath(datapath, "foo.csv"), ',') ``` ### Making Your Package Available diff --git a/doc/src/stdlib/io-network.md b/doc/src/stdlib/io-network.md index 931cb3b55bc0d..22dc207996886 100644 --- a/doc/src/stdlib/io-network.md +++ b/doc/src/stdlib/io-network.md @@ -83,7 +83,6 @@ Base.DataFmt.readdlm(::Any, ::Char) Base.DataFmt.readdlm(::Any, ::Type) Base.DataFmt.readdlm(::Any) Base.DataFmt.writedlm -Base.DataFmt.readcsv Base.Base64.Base64EncodePipe Base.Base64.Base64DecodePipe Base.Base64.base64encode diff --git a/test/datafmt.jl b/test/datafmt.jl index 95f0959e7b8ea..d5bdc45495466 100644 --- a/test/datafmt.jl +++ b/test/datafmt.jl @@ -18,18 +18,18 @@ isequaldlm(m1, m2, t) = isequal(m1, m2) && (eltype(m1) == eltype(m2) == t) @test isequaldlm(readdlm(IOBuffer("1\t2\n3\t4\n5\t6\n")), [1. 2; 3 4; 5 6], Float64) @test isequaldlm(readdlm(IOBuffer("1\t2\n3\t4\n5\t6\n"), Int), [1 2; 3 4; 5 6], Int) -@test size(readcsv(IOBuffer("1,2,3,4"))) == (1,4) -@test size(readcsv(IOBuffer("1,2,3,"))) == (1,4) -@test size(readcsv(IOBuffer("1,2,3,4\n"))) == (1,4) -@test size(readcsv(IOBuffer("1,2,3,\n"))) == (1,4) -@test size(readcsv(IOBuffer("1,2,3,4\n1,2,3,4"))) == (2,4) -@test size(readcsv(IOBuffer("1,2,3,4\n1,2,3,"))) == (2,4) -@test size(readcsv(IOBuffer("1,2,3,4\n1,2,3"))) == (2,4) - -@test size(readcsv(IOBuffer("1,2,3,4\r\n"))) == (1,4) -@test size(readcsv(IOBuffer("1,2,3,4\r\n1,2,3\r\n"))) == (2,4) -@test size(readcsv(IOBuffer("1,2,3,4\r\n1,2,3,4\r\n"))) == (2,4) -@test size(readcsv(IOBuffer("1,2,3,\"4\"\r\n1,2,3,4\r\n"))) == (2,4) +@test size(readdlm(IOBuffer("1,2,3,4"), ',')) == (1,4) +@test size(readdlm(IOBuffer("1,2,3,"), ',')) == (1,4) +@test size(readdlm(IOBuffer("1,2,3,4\n"), ',')) == (1,4) +@test size(readdlm(IOBuffer("1,2,3,\n"), ',')) == (1,4) +@test size(readdlm(IOBuffer("1,2,3,4\n1,2,3,4"), ',')) == (2,4) +@test size(readdlm(IOBuffer("1,2,3,4\n1,2,3,"), ',')) == (2,4) +@test size(readdlm(IOBuffer("1,2,3,4\n1,2,3"), ',')) == (2,4) + +@test size(readdlm(IOBuffer("1,2,3,4\r\n"), ',')) == (1,4) +@test size(readdlm(IOBuffer("1,2,3,4\r\n1,2,3\r\n"), ',')) == (2,4) +@test size(readdlm(IOBuffer("1,2,3,4\r\n1,2,3,4\r\n"), ',')) == (2,4) +@test size(readdlm(IOBuffer("1,2,3,\"4\"\r\n1,2,3,4\r\n"), ',')) == (2,4) @test size(readdlm(IOBuffer("1 2 3 4\n1 2 3"))) == (2,4) @test size(readdlm(IOBuffer("1\t2 3 4\n1 2 3"))) == (2,4) @@ -73,11 +73,11 @@ let result1 = reshape(Any["t", "c", "", "c"], 2, 2), @test isequaldlm(readdlm(IOBuffer("t t \n\"\"\"c\" c")), result2, Any) end -@test isequaldlm(readcsv(IOBuffer("\n1,2,3\n4,5,6\n\n\n"), skipblanks=false), +@test isequaldlm(readdlm(IOBuffer("\n1,2,3\n4,5,6\n\n\n"), ',', skipblanks=false), reshape(Any["",1.0,4.0,"","","",2.0,5.0,"","","",3.0,6.0,"",""], 5, 3), Any) -@test isequaldlm(readcsv(IOBuffer("\n1,2,3\n4,5,6\n\n\n"), skipblanks=true), reshape([1.0,4.0,2.0,5.0,3.0,6.0], 2, 3), Float64) -@test isequaldlm(readcsv(IOBuffer("1,2\n\n4,5"), skipblanks=false), reshape(Any[1.0,"",4.0,2.0,"",5.0], 3, 2), Any) -@test isequaldlm(readcsv(IOBuffer("1,2\n\n4,5"), skipblanks=true), reshape([1.0,4.0,2.0,5.0], 2, 2), Float64) +@test isequaldlm(readdlm(IOBuffer("\n1,2,3\n4,5,6\n\n\n"), ',', skipblanks=true), reshape([1.0,4.0,2.0,5.0,3.0,6.0], 2, 3), Float64) +@test isequaldlm(readdlm(IOBuffer("1,2\n\n4,5"), ',', skipblanks=false), reshape(Any[1.0,"",4.0,2.0,"",5.0], 3, 2), Any) +@test isequaldlm(readdlm(IOBuffer("1,2\n\n4,5"), ',', skipblanks=true), reshape([1.0,4.0,2.0,5.0], 2, 2), Float64) let x = bitrand(5, 10), io = IOBuffer() writedlm(io, x) @@ -88,7 +88,7 @@ end let x = [1,2,3], y = [4,5,6], io = IOBuffer() writedlm(io, zip(x,y), ", ") seek(io, 0) - @test readcsv(io) == [x y] + @test readdlm(io, ',') == [x y] end let x = [0.1 0.3 0.5], io = IOBuffer() @@ -100,13 +100,13 @@ end let x = [0.1 0.3 0.5], io = IOBuffer() writedlm(io, x, ", ") seek(io, 0) - @test readcsv(io) == [0.1 0.3 0.5] + @test readdlm(io, ',') == [0.1 0.3 0.5] end let x = ["abc", "def\"ghi", "jk\nl"], y = [1, ",", "\"quoted\""], io = IOBuffer() writedlm(io, zip(x,y), ',') seek(io, 0) - @test readcsv(io) == [x y] + @test readdlm(io, ',') == [x y] end let x = ["a" "b"; "d" ""], io = IOBuffer() @@ -124,12 +124,12 @@ let x = ["\"hello\"", "world\""], io = IOBuffer() end # test comments -@test isequaldlm(readcsv(IOBuffer("#this is comment\n1,2,3\n#one more comment\n4,5,6")), [1. 2. 3.;4. 5. 6.], Float64) -@test isequaldlm(readcsv(IOBuffer("#this is \n#comment\n1,2,3\n#one more \n#comment\n4,5,6")), [1. 2. 3.;4. 5. 6.], Float64) -@test isequaldlm(readcsv(IOBuffer("1,2,#3\n4,5,6")), [1. 2. "";4. 5. 6.], Any) -@test isequaldlm(readcsv(IOBuffer("1#,2,3\n4,5,6")), [1. "" "";4. 5. 6.], Any) -@test isequaldlm(readcsv(IOBuffer("1,2,\"#3\"\n4,5,6")), [1. 2. "#3";4. 5. 6.], Any) -@test isequaldlm(readcsv(IOBuffer("1,2,3\n #with leading whitespace\n4,5,6")), [1. 2. 3.;" " "" "";4. 5. 6.], Any) +@test isequaldlm(readdlm(IOBuffer("#this is comment\n1,2,3\n#one more comment\n4,5,6"), ','), [1. 2. 3.;4. 5. 6.], Float64) +@test isequaldlm(readdlm(IOBuffer("#this is \n#comment\n1,2,3\n#one more \n#comment\n4,5,6"), ','), [1. 2. 3.;4. 5. 6.], Float64) +@test isequaldlm(readdlm(IOBuffer("1,2,#3\n4,5,6"), ','), [1. 2. "";4. 5. 6.], Any) +@test isequaldlm(readdlm(IOBuffer("1#,2,3\n4,5,6"), ','), [1. "" "";4. 5. 6.], Any) +@test isequaldlm(readdlm(IOBuffer("1,2,\"#3\"\n4,5,6"), ','), [1. 2. "#3";4. 5. 6.], Any) +@test isequaldlm(readdlm(IOBuffer("1,2,3\n #with leading whitespace\n4,5,6"), ','), [1. 2. 3.;" " "" "";4. 5. 6.], Any) # test skipstart let x = ["a" "b" "c"; "d" "e" "f"; "g" "h" "i"; "A" "B" "C"; 1 2 3; 4 5 6; 7 8 9], io = IOBuffer() @@ -213,21 +213,21 @@ let i18n_data = ["Origin (English)", "Name (English)", "Origin (Native)", "Name i18n_arr = permutedims(reshape(i18n_data, 4, Int(floor(length(i18n_data)/4))), [2, 1]) i18n_buff = PipeBuffer() writedlm(i18n_buff, i18n_arr, ',') - @test i18n_arr == readcsv(i18n_buff) + @test i18n_arr == readdlm(i18n_buff, ',') hdr = i18n_arr[1:1, :] data = i18n_arr[2:end, :] writedlm(i18n_buff, i18n_arr, ',') - @test (data, hdr) == readcsv(i18n_buff, header=true) + @test (data, hdr) == readdlm(i18n_buff, ',', header=true) writedlm(i18n_buff, i18n_arr, '\t') @test (data, hdr) == readdlm(i18n_buff, '\t', header=true) end -@test isequaldlm(readcsv(IOBuffer("1,22222222222222222222222222222222222222,0x3,10e6\n2000.1,true,false,-10.34"), Any), +@test isequaldlm(readdlm(IOBuffer("1,22222222222222222222222222222222222222,0x3,10e6\n2000.1,true,false,-10.34"), ',', Any), reshape(Any[1,2000.1,Float64(22222222222222222222222222222222222222),true,0x3,false,10e6,-10.34], 2, 4), Any) -@test isequaldlm(readcsv(IOBuffer("-9223355253176920979,9223355253176920979"), Int64), Int64[-9223355253176920979 9223355253176920979], Int64) +@test isequaldlm(readdlm(IOBuffer("-9223355253176920979,9223355253176920979"), ',', Int64), Int64[-9223355253176920979 9223355253176920979], Int64) # fix #13028 for data in ["A B C", "A B C\n"] @@ -253,13 +253,13 @@ for writefunc in ((io,x) -> show(io, "text/csv", x), let x = [(1,2), (3,4)], io = IOBuffer() writefunc(io, x) seek(io, 0) - @test readcsv(io) == [1 2; 3 4] + @test readdlm(io, ',') == [1 2; 3 4] end # vectors of strings: let x = ["foo", "bar"], io = IOBuffer() writefunc(io, x) seek(io, 0) - @test vec(readcsv(io)) == x + @test vec(readdlm(io, ',')) == x end end @@ -275,12 +275,12 @@ end # issue #21180 let data = "\"721\",\"1438\",\"1439\",\"…\",\"1\"" - @test readcsv(IOBuffer(data)) == Any[721 1438 1439 "…" 1] + @test readdlm(IOBuffer(data), ',') == Any[721 1438 1439 "…" 1] end # issue #21207 let data = "\"1\",\"灣\"\"灣灣灣灣\",\"3\"" - @test readcsv(IOBuffer(data)) == Any[1 "灣\"灣灣灣灣" 3] + @test readdlm(IOBuffer(data), ',') == Any[1 "灣\"灣灣灣灣" 3] end # issue #11484: useful error message for invalid readdlm filepath arguments diff --git a/test/read.jl b/test/read.jl index e3b8cf002887e..ba82a26aaaef6 100644 --- a/test/read.jl +++ b/test/read.jl @@ -261,9 +261,9 @@ for (name, f) in l verbose && println("$name countlines...") @test countlines(io()) == countlines(IOBuffer(text)) - verbose && println("$name readcsv...") - @test readcsv(io()) == readcsv(IOBuffer(text)) - @test readcsv(io()) == readcsv(filename) + verbose && println("$name readdlm...") + @test readdlm(io(), ',') == readdlm(IOBuffer(text), ',') + @test readdlm(io(), ',') == readdlm(filename, ',') cleanup() end From 357b052d8f4036518de781c3a47cabe82dcce00a Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Sat, 9 Sep 2017 15:44:54 -0400 Subject: [PATCH 320/324] fix #23327, subtyping bug in bounds containing free variables (#23643) --- src/subtype.c | 28 ++++++++++++++++++---------- test/subtype.jl | 22 ++++++++++++++++++++++ 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index eb94e1463b50d..810d4c33ace8a 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -64,6 +64,9 @@ typedef struct _varbinding { int8_t occurs_inv; // occurs in invariant position int8_t occurs_cov; // # of occurrences in covariant position int8_t concrete; // 1 if another variable has a constraint forcing this one to be concrete + // set if this variable's bounds contain a free variable that's been removed from + // the environment. + int8_t hasfree; // in covariant position, we need to try constraining a variable in different ways: // 0 - unconstrained // 1 - less than @@ -139,7 +142,7 @@ static void save_env(jl_stenv_t *e, jl_value_t **root, jl_savedenv_t *se) v = v->prev; } *root = (jl_value_t*)jl_alloc_svec(len*3); - se->buf = (int8_t*)(len ? malloc(len*2) : NULL); + se->buf = (int8_t*)(len ? malloc(len*3) : NULL); int i=0, j=0; v = e->vars; while (v != NULL) { jl_svecset(*root, i++, v->lb); @@ -147,6 +150,7 @@ static void save_env(jl_stenv_t *e, jl_value_t **root, jl_savedenv_t *se) jl_svecset(*root, i++, (jl_value_t*)v->innervars); se->buf[j++] = v->occurs_inv; se->buf[j++] = v->occurs_cov; + se->buf[j++] = v->hasfree; v = v->prev; } se->rdepth = e->Runions.depth; @@ -165,6 +169,7 @@ static void restore_env(jl_stenv_t *e, jl_value_t *root, jl_savedenv_t *se) i++; v->occurs_inv = se->buf[j++]; v->occurs_cov = se->buf[j++]; + v->hasfree = se->buf[j++]; v = v->prev; } e->Runions.depth = se->rdepth; @@ -562,7 +567,7 @@ static int subtype_unionall(jl_value_t *t, jl_unionall_t *u, jl_stenv_t *e, int8 } btemp = btemp->prev; } - jl_varbinding_t vb = { u->var, u->var->lb, u->var->ub, R, NULL, 0, 0, 0, 0, e->invdepth, 0, NULL, e->vars }; + jl_varbinding_t vb = { u->var, u->var->lb, u->var->ub, R, NULL, 0, 0, 0, 0, 0, e->invdepth, 0, NULL, e->vars }; JL_GC_PUSH3(&u, &vb.lb, &vb.ub); e->vars = &vb; int ans; @@ -599,6 +604,7 @@ static int subtype_unionall(jl_value_t *t, jl_unionall_t *u, jl_stenv_t *e, int8 else { ans = subtype(u->body, t, e, param); } + if (vb.hasfree) ans = 0; // handle the "diagonal dispatch" rule, which says that a type var occurring more // than once, and only in covariant position, is constrained to concrete types. E.g. @@ -638,14 +644,16 @@ static int subtype_unionall(jl_value_t *t, jl_unionall_t *u, jl_stenv_t *e, int8 e->vars = vb.prev; btemp = e->vars; - while (btemp != NULL) { - jl_value_t *vi = btemp->ub; - // TODO: this takes a significant amount of time - if (vi != (jl_value_t*)vb.var && btemp->var->ub != vi && jl_has_typevar(vi, vb.var)) { - btemp->ub = jl_new_struct(jl_unionall_type, vb.var, vi); - btemp->lb = jl_bottom_type; + if (vb.lb != vb.ub) { + while (btemp != NULL) { + jl_value_t *vu = btemp->ub; + jl_value_t *vl = btemp->lb; + // TODO: this takes a significant amount of time + if ((vu != (jl_value_t*)vb.var && btemp->var->ub != vu && jl_has_typevar(vu, vb.var)) || + (vl != (jl_value_t*)vb.var && btemp->var->lb != vl && jl_has_typevar(vl, vb.var))) + btemp->hasfree = 1; + btemp = btemp->prev; } - btemp = btemp->prev; } JL_GC_POP(); @@ -1548,7 +1556,7 @@ static jl_value_t *intersect_unionall(jl_value_t *t, jl_unionall_t *u, jl_stenv_ { jl_value_t *res=NULL, *res2=NULL, *save=NULL, *save2=NULL; jl_savedenv_t se, se2; - jl_varbinding_t vb = { u->var, u->var->lb, u->var->ub, R, NULL, 0, 0, 0, 0, e->invdepth, 0, NULL, e->vars }; + jl_varbinding_t vb = { u->var, u->var->lb, u->var->ub, R, NULL, 0, 0, 0, 0, 0, e->invdepth, 0, NULL, e->vars }; JL_GC_PUSH6(&res, &save2, &vb.lb, &vb.ub, &save, &vb.innervars); save_env(e, &save, &se); res = intersect_unionall_(t, u, e, R, param, &vb); diff --git a/test/subtype.jl b/test/subtype.jl index 3e68f9ce4d97a..b0907706a4ec1 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -256,6 +256,21 @@ function test_3() @test !issub((@UnionAll T>:Integer @UnionAll S>:Ptr Tuple{Ptr{T},Ptr{S}}), B) @test issub((@UnionAll T>:Ptr @UnionAll S>:Integer Tuple{Ptr{T},Ptr{S}}), B) + + # issue #23327 + @test !issub((Type{AbstractArray{Array{T}} where T}), Type{AbstractArray{S}} where S) + @test !issub((Val{AbstractArray{Array{T}} where T}), Val{AbstractArray{T}} where T) + @test !issub((Array{Array{Array{T}} where T}), Array{Array{T}} where T) + @test !issub((Array{Array{T, 1}, 1} where T), AbstractArray{Vector}) + + @test !issub((Ref{Pair{Pair{T, R}, R} where R} where T), + (Ref{Pair{A, B} where B} where A)) + @test !issub((Ref{Pair{Pair{A, B}, B} where B} where A), + (Ref{Pair{A, B2} where B2 <: B} where A where B)) + + @test !issub(Tuple{Type{Vector{T}} where T, Vector{Float64}}, Tuple{Type{T}, T} where T) + @test !issub(Tuple{Vector{Float64}, Type{Vector{T}} where T}, Tuple{T, Type{T}} where T) + @test !issub(Tuple{Type{Ref{T}} where T, Vector{Float64}}, Tuple{Ref{T}, T} where T) end # level 4: Union @@ -1149,3 +1164,10 @@ end Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64, Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64, Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64} <: (Tuple{Vararg{T}} where T<:Number)) + +# part of issue #23327 +let + triangular(::Type{<:AbstractArray{T}}) where {T} = T + triangular(::Type{<:AbstractArray}) = Any + @test triangular(Array{Array{T, 1}, 1} where T) === Any +end From 8c2fc37ac939a3f30de4744ab0fa6b6803a7a4b3 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Sun, 10 Sep 2017 10:51:00 +0200 Subject: [PATCH 321/324] re-enable terminal's beep and use... something else! (#23201) In the REPL, when an action cannot be done, the user can be notified by animating the prompt - no need to use built-in terminal's beep ('\a'), which is anyway often disabled. --- base/repl/LineEdit.jl | 112 ++++++++++++++++++++++++++++++----------- base/repl/REPL.jl | 8 +-- base/repl/Terminals.jl | 2 +- 3 files changed, 89 insertions(+), 33 deletions(-) diff --git a/base/repl/LineEdit.jl b/base/repl/LineEdit.jl index 9f0e251915c5b..255177c77f7c6 100644 --- a/base/repl/LineEdit.jl +++ b/base/repl/LineEdit.jl @@ -73,6 +73,9 @@ mutable struct PromptState <: ModeState # indentation of lines which do not include the prompt # if negative, the width of the prompt is used indent::Int + refresh_lock::Threads.AbstractLock + # this would better be Threads.Atomic{Float64}, but not supported on some platforms + beeping::Float64 end options(s::PromptState) = isdefined(s.p, :repl) ? s.p.repl.options : Base.REPL.Options() @@ -116,9 +119,54 @@ complete_line(c::EmptyCompletionProvider, s) = [], true, true terminal(s::IO) = s terminal(s::PromptState) = s.terminal + +# these may be better stored in Prompt or LineEditREPL +const BEEP_DURATION = Ref(0.2) +const BEEP_BLINK = Ref(0.2) +const BEEP_MAXDURATION = Ref(1.0) +const BEEP_COLORS = ["\e[90m"] # gray (text_colors not yet available) +const BEEP_USE_CURRENT = Ref(true) + +function beep(s::PromptState, duration::Real=BEEP_DURATION[], blink::Real=BEEP_BLINK[], + maxduration::Real=BEEP_MAXDURATION[]; + colors=BEEP_COLORS, use_current::Bool=BEEP_USE_CURRENT[]) + isinteractive() || return # some tests fail on some platforms + s.beeping = min(s.beeping + duration, maxduration) + @async begin + trylock(s.refresh_lock) || return + orig_prefix = s.p.prompt_prefix + colors = Base.copymutable(colors) + use_current && push!(colors, orig_prefix) + i = 0 + while s.beeping > 0.0 + prefix = colors[mod1(i+=1, end)] + s.p.prompt_prefix = prefix + refresh_multi_line(s, beeping=true) + sleep(blink) + s.beeping -= blink + end + s.p.prompt_prefix = orig_prefix + refresh_multi_line(s, beeping=true) + s.beeping = 0.0 + unlock(s.refresh_lock) + end +end + +function cancel_beep(s::PromptState) + # wait till beeping finishes + while !trylock(s.refresh_lock) + s.beeping = 0.0 + sleep(.05) + end + unlock(s.refresh_lock) +end + +beep(::ModeState) = nothing +cancel_beep(::ModeState) = nothing + for f in [:terminal, :on_enter, :add_history, :buffer, :(Base.isempty), :replace_line, :refresh_multi_line, :input_string, :update_display_buffer, - :empty_undo, :push_undo, :pop_undo, :options] + :empty_undo, :push_undo, :pop_undo, :options, :cancel_beep, :beep] @eval ($f)(s::MIState, args...) = $(f)(state(s), args...) end @@ -175,16 +223,19 @@ end # Prompt Completions function complete_line(s::MIState) - complete_line(state(s), s.key_repeats) - refresh_line(s) - :complete_line + if complete_line(state(s), s.key_repeats) + refresh_line(s) + :complete_line + else + beep(s) + :ignore + end end function complete_line(s::PromptState, repeats) completions, partial, should_complete = complete_line(s.p.complete, s) - if isempty(completions) - beep(terminal(s)) - elseif !should_complete + isempty(completions) && return false + if !should_complete # should_complete is false for cases where we only want to show # a list of possible completions but not complete, e.g. foo(\t show_completions(s, completions) @@ -205,6 +256,7 @@ function complete_line(s::PromptState, repeats) show_completions(s, completions) end end + true end clear_input_area(terminal, s) = (_clear_input_area(terminal, s.ias); s.ias = InputAreaState(0, 0)) @@ -230,9 +282,9 @@ prompt_string(p::Prompt) = prompt_string(p.prompt) prompt_string(s::AbstractString) = s prompt_string(f::Function) = Base.invokelatest(f) -refresh_multi_line(s::ModeState) = refresh_multi_line(terminal(s), s) -refresh_multi_line(termbuf::TerminalBuffer, s::ModeState) = refresh_multi_line(termbuf, terminal(s), s) -refresh_multi_line(termbuf::TerminalBuffer, term, s::ModeState) = (@assert term == terminal(s); refresh_multi_line(termbuf,s)) +refresh_multi_line(s::ModeState; kw...) = refresh_multi_line(terminal(s), s; kw...) +refresh_multi_line(termbuf::TerminalBuffer, s::ModeState; kw...) = refresh_multi_line(termbuf, terminal(s), s; kw...) +refresh_multi_line(termbuf::TerminalBuffer, term, s::ModeState; kw...) = (@assert term == terminal(s); refresh_multi_line(termbuf,s; kw...)) function refresh_multi_line(termbuf::TerminalBuffer, terminal::UnixTerminal, buf, state::InputAreaState, prompt = ""; indent = 0) _clear_input_area(termbuf, state) @@ -297,7 +349,6 @@ function refresh_multi_line(termbuf::TerminalBuffer, terminal::UnixTerminal, buf #columns are 1 based cmove_col(termbuf, curs_pos + 1) - # Updated cur_row,curs_row return InputAreaState(cur_row, curs_row) end @@ -558,7 +609,7 @@ function edit_backspace(s::PromptState, align::Bool=options(s).backspace_align, refresh_line(s) else pop_undo(s) - beep(terminal(s)) + beep(s) end end @@ -607,7 +658,7 @@ function edit_delete(s) refresh_line(s) else pop_undo(s) - beep(terminal(s)) + beep(s) end :edit_delete end @@ -676,7 +727,7 @@ end function edit_yank(s::MIState) if isempty(s.kill_ring) - beep(terminal(s)) + beep(s) return :ignore end setmark(s) # necessary for edit_yank_pop @@ -689,7 +740,7 @@ end function edit_yank_pop(s::MIState, require_previous_yank=true) repeat = s.last_action ∈ (:edit_yank, :edit_yank_pop) if require_previous_yank && !repeat || isempty(s.kill_ring) - beep(terminal(s)) + beep(s) :ignore else require_previous_yank || repeat || setmark(s) @@ -857,7 +908,7 @@ function history_prev(s, hist) move_input_start(s) refresh_line(s) else - beep(terminal(s)) + beep(s) end end function history_next(s, hist) @@ -867,7 +918,7 @@ function history_next(s, hist) move_input_end(s) refresh_line(s) else - beep(terminal(s)) + beep(s) end end @@ -1249,12 +1300,12 @@ end terminal(s::SearchState) = s.terminal function update_display_buffer(s::SearchState, data) - history_search(data.histprompt.hp, data.query_buffer, data.response_buffer, data.backward, false) || beep(terminal(s)) + history_search(data.histprompt.hp, data.query_buffer, data.response_buffer, data.backward, false) || beep(s) refresh_line(s) end function history_next_result(s::MIState, data::SearchState) - history_search(data.histprompt.hp, data.query_buffer, data.response_buffer, data.backward, true) || beep(terminal(s)) + history_search(data.histprompt.hp, data.query_buffer, data.response_buffer, data.backward, true) || beep(s) refresh_line(data) end @@ -1307,9 +1358,11 @@ function show(io::IO, s::PrefixSearchState) isdefined(s,:mi) ? s.mi : "no MI") end -refresh_multi_line(termbuf::TerminalBuffer, terminal::UnixTerminal, - s::Union{PromptState,PrefixSearchState}) = s.ias = - refresh_multi_line(termbuf, terminal, buffer(s), s.ias, s, indent = s.indent) +function refresh_multi_line(termbuf::TerminalBuffer, terminal::UnixTerminal, + s::Union{PromptState,PrefixSearchState}; beeping=false) + beeping || cancel_beep(s) + s.ias = refresh_multi_line(termbuf, terminal, buffer(s), s.ias, s, indent = s.indent) +end input_string(s::PrefixSearchState) = String(take!(copy(s.response_buffer))) @@ -1456,15 +1509,15 @@ function setup_search_keymap(hp) # Backspace/^H '\b' => (s,data,c)->(edit_backspace(data.query_buffer) ? - update_display_buffer(s, data) : beep(terminal(s))), + update_display_buffer(s, data) : beep(s)), 127 => KeyAlias('\b'), # Meta Backspace "\e\b" => (s,data,c)->(edit_delete_prev_word(data.query_buffer) ? - update_display_buffer(s, data) : beep(terminal(s))), + update_display_buffer(s, data) : beep(s)), "\e\x7f" => "\e\b", # Word erase to whitespace "^W" => (s,data,c)->(edit_werase(data.query_buffer) ? - update_display_buffer(s, data) : beep(terminal(s))), + update_display_buffer(s, data) : beep(s)), # ^C and ^D "^C" => (s,data,c)->(edit_clear(data.query_buffer); edit_clear(data.response_buffer); @@ -1565,6 +1618,7 @@ function move_line_end(buf::IOBuffer) end function commit_line(s) + cancel_beep(s) move_input_end(s) refresh_line(s) println(terminal(s)) @@ -1712,6 +1766,7 @@ AnyDict( try # raise the debugger if present ccall(:jl_raise_debugger, Int, ()) end + cancel_beep(s) move_input_end(s) refresh_line(s) print(terminal(s), "^C\n\n") @@ -1822,6 +1877,7 @@ activate(m::ModalInterface, s::MIState, termbuf, term::TextTerminal) = commit_changes(t::UnixTerminal, termbuf) = write(t, take!(termbuf.out_stream)) function transition(f::Function, s::MIState, newmode) + cancel_beep(s) if newmode === :abort s.aborted = true return @@ -1879,7 +1935,7 @@ run_interface(::Prompt) = nothing init_state(terminal, prompt::Prompt) = PromptState(terminal, prompt, IOBuffer(), IOBuffer[], 1, InputAreaState(1, 1), - #=indent(spaces)=# -1) + #=indent(spaces)=# -1, Threads.SpinLock(), 0.0) function init_state(terminal, m::ModalInterface) s = MIState(m, m.modes[1], false, Dict{Any,Any}()) @@ -1941,7 +1997,7 @@ function edit_undo!(s::MIState) if edit_undo!(state(s)) :edit_undo! else - beep(terminal(s)) + beep(s) :ignore end end @@ -1958,7 +2014,7 @@ function edit_redo!(s::MIState) if s.last_action ∈ (:edit_redo!, :edit_undo!) && edit_redo!(state(s)) :edit_redo! else - beep(terminal(s)) + beep(s) :ignore end end diff --git a/base/repl/REPL.jl b/base/repl/REPL.jl index d13faef285281..ed793924fa0c8 100644 --- a/base/repl/REPL.jl +++ b/base/repl/REPL.jl @@ -493,14 +493,14 @@ function history_prev(s::LineEdit.MIState, hist::REPLHistoryProvider, elseif m === :skip history_prev(s, hist, num+1, save_idx) else - Terminals.beep(LineEdit.terminal(s)) + Terminals.beep(s) end end function history_next(s::LineEdit.MIState, hist::REPLHistoryProvider, num::Int=1, save_idx::Int = hist.cur_idx) if num == 0 - Terminals.beep(LineEdit.terminal(s)) + Terminals.beep(s) return end num < 0 && return history_prev(s, hist, -num, save_idx) @@ -518,7 +518,7 @@ function history_next(s::LineEdit.MIState, hist::REPLHistoryProvider, elseif m === :skip history_next(s, hist, num+1, save_idx) else - Terminals.beep(LineEdit.terminal(s)) + Terminals.beep(s) end end @@ -562,7 +562,7 @@ function history_move_prefix(s::LineEdit.PrefixSearchState, end end end - Terminals.beep(LineEdit.terminal(s)) + Terminals.beep(s) end history_next_prefix(s::LineEdit.PrefixSearchState, hist::REPLHistoryProvider, prefix::AbstractString) = history_move_prefix(s, hist, prefix, false) diff --git a/base/repl/Terminals.jl b/base/repl/Terminals.jl index ad7382009d832..1941043a5e65b 100644 --- a/base/repl/Terminals.jl +++ b/base/repl/Terminals.jl @@ -146,7 +146,7 @@ end @eval clear(t::UnixTerminal) = write(t.out_stream, $"$(CSI)H$(CSI)2J") @eval clear_line(t::UnixTerminal) = write(t.out_stream, $"\r$(CSI)0K") -#beep(t::UnixTerminal) = write(t.err_stream,"\x7") +beep(t::UnixTerminal) = write(t.err_stream,"\x7") Base.displaysize(t::UnixTerminal) = displaysize(t.out_stream) From 96f64cef62605742036a2f437eb656c70abedc72 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Sun, 10 Sep 2017 14:08:19 -0400 Subject: [PATCH 322/324] deprecate behavior of `map` on dicts. part of #5794 (#23648) --- NEWS.md | 3 +++ base/abstractarray.jl | 2 +- base/deprecated.jl | 3 +++ test/dict.jl | 5 ++--- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/NEWS.md b/NEWS.md index e838a3ed0e800..7b0d6dc390109 100644 --- a/NEWS.md +++ b/NEWS.md @@ -428,6 +428,9 @@ Deprecated or removed * The `Range` abstract type has been renamed to `AbstractRange` ([#23570]). + * `map` on dictionaries previously operated on `key=>value` pairs. This behavior is deprecated, + and in the future `map` will operate only on values ([#5794]). + Command-line option changes --------------------------- diff --git a/base/abstractarray.jl b/base/abstractarray.jl index bf84de4251aad..686ab40ef379e 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -1936,7 +1936,7 @@ function map!(f::F, dest::AbstractArray, A::AbstractArray) where F end # map on collections -map(f, A::Union{AbstractArray,AbstractSet,Associative}) = collect_similar(A, Generator(f,A)) +map(f, A::Union{AbstractArray,AbstractSet}) = collect_similar(A, Generator(f,A)) # default to returning an Array for `map` on general iterators """ diff --git a/base/deprecated.jl b/base/deprecated.jl index 65d99d763ac0f..246a06367ab66 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1763,6 +1763,9 @@ import .Iterators.enumerate @deprecate_binding Range AbstractRange +# issue #5794 +@deprecate map(f, d::T) where {T<:Associative} T( f(p) for p in pairs(d) ) + # END 0.7 deprecations # BEGIN 1.0 deprecations diff --git a/test/dict.jl b/test/dict.jl index dbd4771b2dfe1..a37ca0e8142b9 100644 --- a/test/dict.jl +++ b/test/dict.jl @@ -429,9 +429,8 @@ d = Dict('a'=>1, 'b'=>1, 'c'=> 3) # generators, similar d = Dict(:a=>"a") -@test @inferred(map(identity, d)) == d -@test @inferred(map(p->p.first=>p.second[1], d)) == Dict(:a=>'a') -@test_throws ArgumentError map(p->p.second, d) +# TODO: restore when 0.7 deprecation is removed +#@test @inferred(map(identity, d)) == d # Issue 12451 @test_throws ArgumentError Dict(0) From fce0a3c9ebf05d38d62c821ba83d461051fbfd7b Mon Sep 17 00:00:00 2001 From: Eric Davies Date: Sun, 10 Sep 2017 17:37:30 -0500 Subject: [PATCH 323/324] Don't print type prefix for String array (#23644) --- base/show.jl | 3 ++- test/cmdlineargs.jl | 6 +++--- test/show.jl | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/base/show.jl b/base/show.jl index 801d6d8d1bc6f..59e17ab638e89 100644 --- a/base/show.jl +++ b/base/show.jl @@ -1913,7 +1913,8 @@ function array_eltype_show_how(X) str = string(e) end # Types hard-coded here are those which are created by default for a given syntax - isleaftype(e), (!isempty(X) && (e===Float64 || e===Int || e===Char) ? "" : str) + (isleaftype(e), + (!isempty(X) && (e===Float64 || e===Int || e===Char || e===String) ? "" : str)) end function show_vector(io::IO, v, opn, cls) diff --git a/test/cmdlineargs.jl b/test/cmdlineargs.jl index ba68561a67c90..6ca4b5b71bd15 100644 --- a/test/cmdlineargs.jl +++ b/test/cmdlineargs.jl @@ -269,7 +269,7 @@ let exename = `$(Base.julia_cmd()) --sysimage-native-code=yes --startup-file=no` cp(testfile, joinpath(dir, ".juliarc.jl")) withenv((Sys.iswindows() ? "USERPROFILE" : "HOME") => dir) do - output = "String[\"foo\", \"-bar\", \"--baz\"]" + output = "[\"foo\", \"-bar\", \"--baz\"]" @test readchomp(`$exename $testfile foo -bar --baz`) == output @test readchomp(`$exename $testfile -- foo -bar --baz`) == output @test readchomp(`$exename -L $testfile -e 'exit(0)' -- foo -bar --baz`) == @@ -283,7 +283,7 @@ let exename = `$(Base.julia_cmd()) --sysimage-native-code=yes --startup-file=no` @test !success(`$exename --foo $testfile`) @test readchomp(`$exename -L $testfile -e 'exit(0)' -- foo -bar -- baz`) == - "String[\"foo\", \"-bar\", \"--\", \"baz\"]" + "[\"foo\", \"-bar\", \"--\", \"baz\"]" end end @@ -330,7 +330,7 @@ let exename = `$(Base.julia_cmd()) --sysimage-native-code=yes --startup-file=no` end # issue #10562 - @test readchomp(`$exename -e 'println(ARGS);' ''`) == "String[\"\"]" + @test readchomp(`$exename -e 'println(ARGS);' ''`) == "[\"\"]" # issue #12679 @test readchomp(pipeline(ignorestatus(`$exename --startup-file=no --compile=yes -ioo`), diff --git a/test/show.jl b/test/show.jl index bf9a491e55c00..8c9875967c1f2 100644 --- a/test/show.jl +++ b/test/show.jl @@ -13,7 +13,7 @@ replstr(x) = sprint((io,x) -> show(IOContext(io, :limit => true, :displaysize => struct T5589 names::Vector{String} end -@test replstr(T5589(Array{String,1}(100))) == "$(curmod_prefix)T5589(String[#undef, #undef, #undef, #undef, #undef, #undef, #undef, #undef, #undef, #undef … #undef, #undef, #undef, #undef, #undef, #undef, #undef, #undef, #undef, #undef])" +@test replstr(T5589(Array{String,1}(100))) == "$(curmod_prefix)T5589([#undef, #undef, #undef, #undef, #undef, #undef, #undef, #undef, #undef, #undef … #undef, #undef, #undef, #undef, #undef, #undef, #undef, #undef, #undef, #undef])" @test replstr(parse("mutable struct X end")) == ":(mutable struct X\n #= none:1 =#\n end)" @test replstr(parse("struct X end")) == ":(struct X\n #= none:1 =#\n end)" From 9ebcd00eb1601a613581deb7d754054761b1abe0 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Mon, 7 Aug 2017 16:12:11 -0400 Subject: [PATCH 324/324] WIP: allow `@generated begin ... end` inside a function to provide an optional optimizer use meta nodes instead of `stagedfunction` expression head --- base/docs/Docs.jl | 2 +- base/expr.jl | 15 ++++-- base/methodshow.jl | 13 ++++- base/reflection.jl | 3 +- src/ast.c | 5 +- src/ast.scm | 6 +++ src/codegen.cpp | 8 +-- src/dump.c | 2 +- src/interpreter.c | 2 +- src/jltypes.c | 3 +- src/julia-syntax.scm | 105 ++++++++++++++++++++++------------------ src/julia.h | 4 +- src/julia_internal.h | 2 + src/method.c | 113 ++++++++++++++++++++++++------------------- src/utils.scm | 5 ++ 15 files changed, 173 insertions(+), 115 deletions(-) diff --git a/base/docs/Docs.jl b/base/docs/Docs.jl index 90899bbc91254..044b0a5ad981f 100644 --- a/base/docs/Docs.jl +++ b/base/docs/Docs.jl @@ -642,7 +642,7 @@ finddoc(λ, def) = false # Predicates and helpers for `docm` expression selection: -const FUNC_HEADS = [:function, :stagedfunction, :macro, :(=)] +const FUNC_HEADS = [:function, :macro, :(=)] const BINDING_HEADS = [:typealias, :const, :global, :(=)] # deprecation: remove `typealias` post-0.6 # For the special `:@mac` / `:(Base.@mac)` syntax for documenting a macro after definition. isquotedmacrocall(x) = diff --git a/base/expr.jl b/base/expr.jl index d3138e1a0ac23..86d237cebea13 100644 --- a/base/expr.jl +++ b/base/expr.jl @@ -333,9 +333,18 @@ function remove_linenums!(ex::Expr) end macro generated(f) - if isa(f, Expr) && (f.head === :function || is_short_function_def(f)) - f.head = :stagedfunction - return Expr(:escape, f) + if isa(f, Expr) && (f.head === :function || is_short_function_def(f)) + body = f.args[2] + lno = body.args[1] + return Expr(:escape, + Expr(f.head, f.args[1], + Expr(:block, + lno, + Expr(:meta, :generator, body), + Expr(:meta, :generated_only), + Expr(:return, nothing)))) + elseif isa(f, Expr) && f.head === :block + return Expr(:escape, Expr(:meta, :generator, f)) else error("invalid syntax; @generated must be used with a function definition") end diff --git a/base/methodshow.jl b/base/methodshow.jl index 286980c197be0..e8862cb1cdf73 100644 --- a/base/methodshow.jl +++ b/base/methodshow.jl @@ -42,6 +42,16 @@ function argtype_decl(env, n, sig::DataType, i::Int, nargs, isva::Bool) # -> (ar return s, string_with_env(env, t) end +function method_argnames(m::Method) + if !isdefined(m, :source) + gm = first(methods(m.generator)) + return method_argnames(gm)[length(m.sparam_syms)+2 : end] + end + argnames = Vector{Any}(m.nargs) + ccall(:jl_fill_argnames, Void, (Any, Any), m.source, argnames) + return argnames +end + function arg_decl_parts(m::Method) tv = Any[] sig = m.sig @@ -52,8 +62,7 @@ function arg_decl_parts(m::Method) file = m.file line = m.line if isdefined(m, :source) || isdefined(m, :generator) - argnames = Vector{Any}(m.nargs) - ccall(:jl_fill_argnames, Void, (Any, Any), isdefined(m, :source) ? m.source : m.generator.inferred, argnames) + argnames = method_argnames(m) show_env = ImmutableDict{Symbol, Any}() for t in tv show_env = ImmutableDict(show_env, :unionall_env => t) diff --git a/base/reflection.jl b/base/reflection.jl index 41b67dff32964..1a5429673c9a6 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -708,7 +708,8 @@ function length(mt::MethodTable) end isempty(mt::MethodTable) = (mt.defs === nothing) -uncompressed_ast(m::Method) = uncompressed_ast(m, isdefined(m, :source) ? m.source : m.generator.inferred) +uncompressed_ast(m::Method) = isdefined(m,:source) ? uncompressed_ast(m, m.source) : + uncompressed_ast(first(methods(m.generator))) uncompressed_ast(m::Method, s::CodeInfo) = s uncompressed_ast(m::Method, s::Array{UInt8,1}) = ccall(:jl_uncompress_ast, Any, (Any, Any), m, s)::CodeInfo uncompressed_ast(m::Core.MethodInstance) = uncompressed_ast(m.def) diff --git a/src/ast.c b/src/ast.c index 27378832f664b..bb2e9f09feca8 100644 --- a/src/ast.c +++ b/src/ast.c @@ -55,7 +55,8 @@ jl_sym_t *meta_sym; jl_sym_t *compiler_temp_sym; jl_sym_t *inert_sym; jl_sym_t *vararg_sym; jl_sym_t *unused_sym; jl_sym_t *static_parameter_sym; jl_sym_t *polly_sym; jl_sym_t *inline_sym; -jl_sym_t *propagate_inbounds_sym; +jl_sym_t *propagate_inbounds_sym; jl_sym_t *generator_sym; +jl_sym_t *generated_only_sym; jl_sym_t *isdefined_sym; jl_sym_t *nospecialize_sym; jl_sym_t *macrocall_sym; jl_sym_t *hygienicscope_sym; @@ -340,6 +341,8 @@ void jl_init_frontend(void) macrocall_sym = jl_symbol("macrocall"); escape_sym = jl_symbol("escape"); hygienicscope_sym = jl_symbol("hygienic-scope"); + generator_sym = jl_symbol("generator"); + generated_only_sym = jl_symbol("generated_only"); } JL_DLLEXPORT void jl_lisp_prompt(void) diff --git a/src/ast.scm b/src/ast.scm index b4c46ab4a3509..87832bfe5d39c 100644 --- a/src/ast.scm +++ b/src/ast.scm @@ -352,6 +352,12 @@ (and (if one (length= e 3) (length> e 2)) (eq? (car e) 'meta) (eq? (cadr e) 'nospecialize))) +(define (generator-meta? e) + (and (length= e 3) (eq? (car e) 'meta) (eq? (cadr e) 'generator))) + +(define (generated_only-meta? e) + (and (length= e 2) (eq? (car e) 'meta) (eq? (cadr e) 'generated_only))) + ;; flatten nested expressions with the given head ;; (op (op a b) c) => (op a b c) (define (flatten-ex head e) diff --git a/src/codegen.cpp b/src/codegen.cpp index f6589cfdd5355..ca4f7be84486e 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1198,8 +1198,6 @@ jl_llvm_functions_t jl_compile_linfo(jl_method_instance_t **pli, jl_code_info_t li->inferred && // and there is something to delete (test this before calling jl_ast_flag_inlineable) li->inferred != jl_nothing && - // don't delete the code for the generator - li != li->def.method->generator && // don't delete inlineable code, unless it is constant (li->jlcall_api == 2 || !jl_ast_flag_inlineable((jl_array_t*)li->inferred)) && // don't delete code when generating a precompile file @@ -3824,11 +3822,10 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr) } Value *a1 = boxed(ctx, emit_expr(ctx, args[1])); Value *a2 = boxed(ctx, emit_expr(ctx, args[2])); - Value *mdargs[4] = { + Value *mdargs[3] = { /*argdata*/a1, /*code*/a2, - /*module*/literal_pointer_val(ctx, (jl_value_t*)ctx.module), - /*isstaged*/literal_pointer_val(ctx, args[3]) + /*module*/literal_pointer_val(ctx, (jl_value_t*)ctx.module) }; ctx.builder.CreateCall(prepare_call(jlmethod_func), makeArrayRef(mdargs)); return ghostValue(jl_void_type); @@ -6294,7 +6291,6 @@ static void init_julia_llvm_env(Module *m) mdargs.push_back(T_prjlvalue); mdargs.push_back(T_prjlvalue); mdargs.push_back(T_pjlvalue); - mdargs.push_back(T_pjlvalue); jlmethod_func = Function::Create(FunctionType::get(T_void, mdargs, false), Function::ExternalLinkage, diff --git a/src/dump.c b/src/dump.c index ba2df8034321f..c7307efd15096 100644 --- a/src/dump.c +++ b/src/dump.c @@ -1431,7 +1431,7 @@ static jl_value_t *jl_deserialize_value_method(jl_serializer_state *s, jl_value_ m->unspecialized = (jl_method_instance_t*)jl_deserialize_value(s, (jl_value_t**)&m->unspecialized); if (m->unspecialized) jl_gc_wb(m, m->unspecialized); - m->generator = (jl_method_instance_t*)jl_deserialize_value(s, (jl_value_t**)&m->generator); + m->generator = jl_deserialize_value(s, (jl_value_t**)&m->generator); if (m->generator) jl_gc_wb(m, m->generator); m->invokes.unknown = jl_deserialize_value(s, (jl_value_t**)&m->invokes); diff --git a/src/interpreter.c b/src/interpreter.c index 3892df26f90ac..34a51b3570e4d 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -322,7 +322,7 @@ static jl_value_t *eval(jl_value_t *e, interpreter_state *s) JL_GC_PUSH2(&atypes, &meth); atypes = eval(args[1], s); meth = eval(args[2], s); - jl_method_def((jl_svec_t*)atypes, (jl_code_info_t*)meth, s->module, args[3]); + jl_method_def((jl_svec_t*)atypes, (jl_code_info_t*)meth, s->module); JL_GC_POP(); return jl_nothing; } diff --git a/src/jltypes.c b/src/jltypes.c index 95b215913711e..38c8101716807 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -2056,7 +2056,7 @@ void jl_init_types(void) jl_simplevector_type, jl_any_type, jl_any_type, // jl_method_instance_type - jl_any_type, // jl_method_instance_type + jl_any_type, jl_array_any_type, jl_any_type, jl_int32_type, @@ -2169,7 +2169,6 @@ void jl_init_types(void) #endif jl_svecset(jl_methtable_type->types, 8, jl_int32_type); // uint32_t jl_svecset(jl_method_type->types, 10, jl_method_instance_type); - jl_svecset(jl_method_type->types, 11, jl_method_instance_type); jl_svecset(jl_method_instance_type->types, 12, jl_voidpointer_type); jl_svecset(jl_method_instance_type->types, 13, jl_voidpointer_type); jl_svecset(jl_method_instance_type->types, 14, jl_voidpointer_type); diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index b78e091082434..906ba80b3b535 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -286,9 +286,20 @@ (map (lambda (x) (replace-outer-vars x renames)) (cdr e)))))) +(define (make-generator-function name sp-names arg-names body) + (let ((arg-names (append sp-names + (map (lambda (n) + (if (eq? n '|#self#|) (gensy) n)) + arg-names)))) + (let ((body (insert-after-meta body ;; don't specialize on generator arguments + `((meta nospecialize ,@arg-names))))) + `(block + (global ,name) + (function (call ,name ,@arg-names) ,body))))) + ;; construct the (method ...) expression for one primitive method definition, ;; assuming optional and keyword args are already handled -(define (method-def-expr- name sparams argl body isstaged (rett '(core Any))) +(define (method-def-expr- name sparams argl body (rett '(core Any))) (if (any kwarg? argl) ;; has optional positional args @@ -307,7 +318,7 @@ (dfl (map caddr kws))) (receive (vararg req) (separate vararg? argl) - (optional-positional-defs name sparams req opt dfl body isstaged + (optional-positional-defs name sparams req opt dfl body (append req opt vararg) rett))))) ;; no optional positional args (let ((names (map car sparams))) @@ -320,7 +331,14 @@ (error "function argument and static parameter names must be distinct"))) (if (or (and name (not (sym-ref? name))) (eq? name 'true) (eq? name 'false)) (error (string "invalid function name \"" (deparse name) "\""))) - (let* ((types (llist-types argl)) + (let* ((generator (let ((found (find generator-meta? body))) + (if found + (let* ((gname (symbol (string (gensy) "#" (current-julia-module-counter)))) + (gf (make-generator-function gname names (llist-vars argl) (caddr (car found))))) + (set-car! (cddar found) gname) + (list gf)) + '()))) + (types (llist-types argl)) (body (method-lambda-expr argl body rett)) ;; HACK: the typevars need to be bound to ssavalues, since this code ;; might be moved to a different scope by closure-convert. @@ -329,7 +347,7 @@ (mdef (if (null? sparams) `(method ,name (call (core svec) (call (core svec) ,@(dots->vararg types)) (call (core svec))) - ,body ,isstaged) + ,body) `(method ,name (block ,@(let loop ((n names) @@ -350,10 +368,12 @@ (replace-vars ty renames)) types))) (call (core svec) ,@temps))) - ,body ,isstaged)))) + ,body)))) (if (or (symbol? name) (globalref? name)) - `(block (method ,name) ,mdef (unnecessary ,name)) ;; return the function - mdef))))) + `(block ,@generator (method ,name) ,mdef (unnecessary ,name)) ;; return the function + (if (not (null? generator)) + `(block ,@generator ,mdef) + mdef)))))) ;; wrap expr in nested scopes assigning names to vals (define (scopenest names vals expr) @@ -365,10 +385,8 @@ (define empty-vector-any '(call (core AnyVector) 0)) -(define (keywords-method-def-expr name sparams argl body isstaged rett) +(define (keywords-method-def-expr name sparams argl body rett) (let* ((kargl (cdar argl)) ;; keyword expressions (= k v) - (annotations (map (lambda (a) `(meta nospecialize ,(arg-name (cadr (caddr a))))) - (filter nospecialize-meta? kargl))) (kargl (map (lambda (a) (if (nospecialize-meta? a) (caddr a) a)) kargl)) @@ -403,6 +421,8 @@ keynames)) ;; list of function's initial line number and meta nodes (empty if none) (prologue (extract-method-prologue body)) + (annotations (map (lambda (a) `(meta nospecialize ,(arg-name (cadr (caddr a))))) + (filter nospecialize-meta? kargl))) ;; body statements (stmts (cdr body)) (positional-sparams @@ -427,7 +447,7 @@ ,(method-def-expr- name positional-sparams (append pargl vararg) `(block - ,@prologue + ,@(without-generated prologue) ,(let (;; call mangled(vals..., [rest_kw,] pargs..., [vararg]...) (ret `(return (call ,mangled ,@(if ordered-defaults keynames vals) @@ -437,8 +457,7 @@ (list `(... ,(arg-name (car vararg))))))))) (if ordered-defaults (scopenest keynames vals ret) - ret))) - #f) + ret)))) ;; call with keyword args pre-sorted - original method code goes here ,(method-def-expr- @@ -457,7 +476,7 @@ (insert-after-meta `(block ,@stmts) annotations) - isstaged rett) + rett) ;; call with unsorted keyword args. this sorts and re-dispatches. ,(method-def-expr- @@ -539,8 +558,7 @@ ,@(if (null? restkw) '() (list rkw)) ,@(map arg-name pargl) ,@(if (null? vararg) '() - (list `(... ,(arg-name (car vararg))))))))) - #f) + (list `(... ,(arg-name (car vararg)))))))))) ;; return primary function ,(if (not (symbol? name)) '(null) name))))) @@ -553,6 +571,11 @@ (cdr body)) '())) +(define (without-generated stmts) + (filter (lambda (x) (not (or (generator-meta? x) + (generated_only-meta? x)))) + stmts)) + ;; keep only sparams used by `expr` or other sparams (define (filter-sparams expr sparams) (let loop ((filtered '()) @@ -566,8 +589,8 @@ (else (loop filtered (cdr params)))))) -(define (optional-positional-defs name sparams req opt dfl body isstaged overall-argl rett) - (let ((prologue (extract-method-prologue body))) +(define (optional-positional-defs name sparams req opt dfl body overall-argl rett) + (let ((prologue (without-generated (extract-method-prologue body)))) `(block ,@(map (lambda (n) (let* ((passed (append req (list-head opt n))) @@ -596,9 +619,9 @@ `(block ,@prologue (call ,(arg-name (car req)) ,@(map arg-name (cdr passed)) ,@vals))))) - (method-def-expr- name sp passed body #f))) + (method-def-expr- name sp passed body))) (iota (length opt))) - ,(method-def-expr- name sparams overall-argl body isstaged rett)))) + ,(method-def-expr- name sparams overall-argl body rett)))) ;; strip empty (parameters ...), normalizing `f(x;)` to `f(x)`. (define (remove-empty-parameters argl) @@ -627,14 +650,14 @@ ;; definitions without keyword arguments are passed to method-def-expr-, ;; which handles optional positional arguments by adding the needed small ;; boilerplate definitions. -(define (method-def-expr name sparams argl body isstaged rett) +(define (method-def-expr name sparams argl body rett) (let ((argl (remove-empty-parameters argl))) (if (has-parameters? argl) ;; has keywords (begin (check-kw-args (cdar argl)) - (keywords-method-def-expr name sparams argl body isstaged rett)) + (keywords-method-def-expr name sparams argl body rett)) ;; no keywords - (method-def-expr- name sparams argl body isstaged rett)))) + (method-def-expr- name sparams argl body rett)))) (define (struct-def-expr name params super fields mut) (receive @@ -763,12 +786,12 @@ ,@sig) new-params))))) -(define (ctor-def keyword name Tname params bounds sig ctor-body body wheres) +(define (ctor-def name Tname params bounds sig ctor-body body wheres) (let* ((curly? (and (pair? name) (eq? (car name) 'curly))) (curlyargs (if curly? (cddr name) '())) (name (if curly? (cadr name) name))) (cond ((not (eq? name Tname)) - `(,keyword ,(with-wheres `(call ,(if curly? + `(function ,(with-wheres `(call ,(if curly? `(curly ,name ,@curlyargs) name) ,@sig) @@ -777,7 +800,7 @@ ;; new{...} inside a non-ctor inner definition. ,(ctor-body body '()))) (wheres - `(,keyword ,(with-wheres `(call ,(if curly? + `(function ,(with-wheres `(call ,(if curly? `(curly ,name ,@curlyargs) name) ,@sig) @@ -791,7 +814,7 @@ (syntax-deprecation #f (string "inner constructor " name "(...)" (linenode-string (function-body-lineno body))) (deparse `(where (call (curly ,name ,@params) ...) ,@params)))) - `(,keyword ,sig ,(ctor-body body params))))))) + `(function ,sig ,(ctor-body body params))))))) (define (function-body-lineno body) (let ((lnos (filter (lambda (e) (and (pair? e) (eq? (car e) 'line))) @@ -818,18 +841,14 @@ (pattern-set ;; definitions without `where` (pattern-lambda (function (-$ (call name . sig) (|::| (call name . sig) _t)) body) - (ctor-def (car __) name Tname params bounds sig ctor-body body #f)) - (pattern-lambda (stagedfunction (-$ (call name . sig) (|::| (call name . sig) _t)) body) - (ctor-def (car __) name Tname params bounds sig ctor-body body #f)) + (ctor-def name Tname params bounds sig ctor-body body #f)) (pattern-lambda (= (-$ (call name . sig) (|::| (call name . sig) _t)) body) - (ctor-def 'function name Tname params bounds sig ctor-body body #f)) + (ctor-def name Tname params bounds sig ctor-body body #f)) ;; definitions with `where` (pattern-lambda (function (where (-$ (call name . sig) (|::| (call name . sig) _t)) . wheres) body) - (ctor-def (car __) name Tname params bounds sig ctor-body body wheres)) - (pattern-lambda (stagedfunction (where (-$ (call name . sig) (|::| (call name . sig) _t)) . wheres) body) - (ctor-def (car __) name Tname params bounds sig ctor-body body wheres)) + (ctor-def name Tname params bounds sig ctor-body body wheres)) (pattern-lambda (= (where (-$ (call name . sig) (|::| (call name . sig) _t)) . wheres) body) - (ctor-def 'function name Tname params bounds sig ctor-body body wheres))) + (ctor-def name Tname params bounds sig ctor-body body wheres))) ;; flatten `where`s first (pattern-replace @@ -970,7 +989,7 @@ (loop (if isseq F (cdr F)) (cdr A) stmts (list* (if isamp `(& ,ca) ca) C) (list* g GC)))))))) -(define (expand-function-def e) ;; handle function or stagedfunction +(define (expand-function-def e) ;; handle function definitions (define (just-arglist? ex) (and (pair? ex) (or (memq (car ex) '(tuple block)) @@ -1054,7 +1073,6 @@ (where where) (else '()))) (sparams (map analyze-typevar raw-typevars)) - (isstaged (eq? (car e) 'stagedfunction)) (adj-decl (lambda (n) (if (and (decl? n) (length= n 2)) `(|::| |#self#| ,(cadr n)) n))) @@ -1081,7 +1099,7 @@ (cdr argl))) ,@raw-typevars)))) (expand-forms - (method-def-expr name sparams argl body isstaged rett)))) + (method-def-expr name sparams argl body rett)))) (else (error (string "invalid assignment location \"" (deparse name) "\"")))))) @@ -1273,7 +1291,6 @@ (cond ((or (atom? e) (quoted? e)) e) ((or (eq? (car e) 'lambda) (eq? (car e) 'function) - (eq? (car e) 'stagedfunction) (eq? (car e) '->)) e) ((eq? (car e) 'return) `(block ,@(if ret `((= ,ret true)) '()) @@ -1941,7 +1958,6 @@ (define expand-table (table 'function expand-function-def - 'stagedfunction expand-function-def '-> expand-arrow 'let expand-let 'macro expand-macro-def @@ -3250,8 +3266,7 @@ f(x) = yt(x) ,@top-stmts (block ,@sp-inits (method ,name ,(cl-convert sig fname lam namemap toplevel interp) - ,(julia-bq-macro newlam) - ,(last e))))))) + ,(julia-bq-macro newlam))))))) ;; local case - lift to a new type at top level (let* ((exists (get namemap name #f)) (type-name (or exists @@ -3329,8 +3344,7 @@ f(x) = yt(x) (if iskw (caddr (lam:args lam2)) (car (lam:args lam2))) - #f closure-param-names) - ,(last e))))))) + #f closure-param-names))))))) (mk-closure ;; expression to make the closure (let* ((var-exprs (map (lambda (v) (let ((cv (assq v (cadr (lam:vinfo lam))))) @@ -3723,8 +3737,7 @@ f(x) = yt(x) (if (length> e 2) (begin (emit `(method ,(or (cadr e) 'false) ,(compile (caddr e) break-labels #t #f) - ,(linearize (cadddr e)) - ,(if (car (cddddr e)) 'true 'false))) + ,(linearize (cadddr e)))) (if value (compile '(null) break-labels value tail))) (cond (tail (emit-return e)) (value e) diff --git a/src/julia.h b/src/julia.h index 398d7eeeba9ca..bf8bbc979ff27 100644 --- a/src/julia.h +++ b/src/julia.h @@ -248,7 +248,7 @@ typedef struct _jl_method_t { jl_svec_t *sparam_syms; // symbols giving static parameter names jl_value_t *source; // original code template (jl_code_info_t, but may be compressed), null for builtins struct _jl_method_instance_t *unspecialized; // unspecialized executable method instance, or null - struct _jl_method_instance_t *generator; // executable code-generating function if available + jl_value_t *generator; // executable code-generating function if available jl_array_t *roots; // pointers in generated code (shared to reduce memory), or null // cache of specializations of this method for invoke(), i.e. @@ -1055,7 +1055,7 @@ JL_DLLEXPORT jl_value_t *jl_generic_function_def(jl_sym_t *name, jl_module_t *module, jl_value_t **bp, jl_value_t *bp_owner, jl_binding_t *bnd); -JL_DLLEXPORT void jl_method_def(jl_svec_t *argdata, jl_code_info_t *f, jl_module_t *module, jl_value_t *isstaged); +JL_DLLEXPORT void jl_method_def(jl_svec_t *argdata, jl_code_info_t *f, jl_module_t *module); JL_DLLEXPORT jl_code_info_t *jl_code_for_staged(jl_method_instance_t *linfo); JL_DLLEXPORT jl_code_info_t *jl_copy_code_info(jl_code_info_t *src); JL_DLLEXPORT size_t jl_get_world_counter(void); diff --git a/src/julia_internal.h b/src/julia_internal.h index 94297906a56b3..515d8f7121cbb 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -999,6 +999,8 @@ extern jl_sym_t *propagate_inbounds_sym; extern jl_sym_t *isdefined_sym; extern jl_sym_t *nospecialize_sym; extern jl_sym_t *boundscheck_sym; +extern jl_sym_t *generator_sym; +extern jl_sym_t *generated_only_sym; void jl_register_fptrs(uint64_t sysimage_base, const char *base, const int32_t *offsets, jl_method_instance_t **linfos, size_t n); diff --git a/src/method.c b/src/method.c index 45d39cdbec1f8..0d34f80cf68bb 100644 --- a/src/method.c +++ b/src/method.c @@ -247,24 +247,23 @@ jl_code_info_t *jl_new_code_info_from_ast(jl_expr_t *ast) } // invoke (compiling if necessary) the jlcall function pointer for a method template -STATIC_INLINE jl_value_t *jl_call_staged(jl_svec_t *sparam_vals, jl_method_instance_t *generator, +STATIC_INLINE jl_value_t *jl_call_staged(jl_method_t *def, jl_value_t *generator, jl_svec_t *sparam_vals, jl_value_t **args, uint32_t nargs) { - jl_generic_fptr_t fptr; - fptr.fptr = generator->fptr; - fptr.jlcall_api = generator->jlcall_api; - if (__unlikely(fptr.fptr == NULL || fptr.jlcall_api == 0)) { - size_t world = generator->def.method->min_world; - const char *F = jl_compile_linfo(&generator, (jl_code_info_t*)generator->inferred, world, &jl_default_cgparams).functionObject; - fptr = jl_generate_fptr(generator, F, world); + size_t n_sparams = jl_svec_len(sparam_vals); + jl_value_t **gargs; + size_t totargs = 1 + n_sparams + nargs + def->isva; + JL_GC_PUSHARGS(gargs, totargs); + gargs[0] = generator; + memcpy(&gargs[1], jl_svec_data(sparam_vals), n_sparams * sizeof(void*)); + memcpy(&gargs[1 + n_sparams], args, nargs * sizeof(void*)); + if (def->isva) { + gargs[totargs-1] = jl_f_tuple(NULL, &gargs[1 + n_sparams + def->nargs - 1], nargs - (def->nargs - 1)); + gargs[1 + n_sparams + def->nargs - 1] = gargs[totargs - 1]; } - assert(jl_svec_len(generator->def.method->sparam_syms) == jl_svec_len(sparam_vals)); - if (fptr.jlcall_api == 1) - return fptr.fptr1(args[0], &args[1], nargs-1); - else if (fptr.jlcall_api == 3) - return fptr.fptr3(sparam_vals, args[0], &args[1], nargs-1); - else - abort(); // shouldn't have inferred any other calling convention + jl_value_t *code = jl_apply(gargs, 1 + n_sparams + def->nargs); + JL_GC_POP(); + return code; } // return a newly allocated CodeInfo for the function signature @@ -277,9 +276,11 @@ JL_DLLEXPORT jl_code_info_t *jl_code_for_staged(jl_method_instance_t *linfo) jl_expr_t *ex = NULL; jl_value_t *linenum = NULL; jl_svec_t *sparam_vals = env; - jl_method_instance_t *generator = linfo->def.method->generator; + jl_value_t *generator = linfo->def.method->generator; + jl_method_t *gen_meth = jl_gf_mtable(generator)->defs.leaf->func.method; assert(generator != NULL); assert(linfo != generator); + assert(jl_is_method(gen_meth)); jl_code_info_t *func = NULL; JL_GC_PUSH4(&ex, &linenum, &sparam_vals, &func); jl_ptls_t ptls = jl_get_ptls_states(); @@ -294,13 +295,14 @@ JL_DLLEXPORT jl_code_info_t *jl_code_for_staged(jl_method_instance_t *linfo) // need to eval macros in the right module ptls->current_task->current_module = ptls->current_module = linfo->def.method->module; // and the right world - ptls->world_age = generator->def.method->min_world; + ptls->world_age = gen_meth->min_world; ex = jl_exprn(lambda_sym, 2); - jl_array_t *argnames = jl_alloc_vec_any(linfo->def.method->nargs); + jl_array_t *argnames = jl_alloc_vec_any(linfo->def.method->nargs + jl_svec_len(sparam_vals) + 1); jl_array_ptr_set(ex->args, 0, argnames); - jl_fill_argnames((jl_array_t*)generator->inferred, argnames); + jl_fill_argnames((jl_array_t*)gen_meth->source, argnames); + jl_array_del_beg(argnames, jl_svec_len(sparam_vals) + 1); // build the rest of the body to pass to expand jl_expr_t *scopeblock = jl_exprn(jl_symbol("scope-block"), 1); @@ -321,7 +323,7 @@ JL_DLLEXPORT jl_code_info_t *jl_code_for_staged(jl_method_instance_t *linfo) // invoke code generator assert(jl_nparams(tt) == jl_array_len(argnames) || (linfo->def.method->isva && (jl_nparams(tt) >= jl_array_len(argnames) - 1))); - jl_value_t *generated_body = jl_call_staged(sparam_vals, generator, jl_svec_data(tt->parameters), jl_nparams(tt)); + jl_value_t *generated_body = jl_call_staged(linfo->def.method, generator, sparam_vals, jl_svec_data(tt->parameters), jl_nparams(tt)); jl_array_ptr_set(body->args, 2, generated_body); if (jl_is_code_info(generated_body)) { @@ -400,7 +402,7 @@ jl_method_instance_t *jl_get_specialized(jl_method_t *m, jl_value_t *types, jl_s return new_linfo; } -static void jl_method_set_source(jl_method_t *m, jl_code_info_t *src) +static void jl_method_set_source(jl_method_t *m, jl_code_info_t *src, jl_value_t **generator, int *gen_only) { uint8_t j; uint8_t called = 0; @@ -449,28 +451,41 @@ static void jl_method_set_source(jl_method_t *m, jl_code_info_t *src) set_lineno = 1; } } - else if (jl_is_expr(st) && ((jl_expr_t*)st)->head == meta_sym && - jl_expr_nargs(st) > 1 && jl_exprarg(st, 0) == (jl_value_t*)nospecialize_sym) { - for (size_t j=1; j < jl_expr_nargs(st); j++) { - jl_value_t *aj = jl_exprarg(st, j); - if (jl_is_slot(aj)) { - int sn = (int)jl_slot_number(aj) - 2; - if (sn >= 0) { // @nospecialize on self is valid but currently ignored - if (sn > (m->nargs - 2)) { - jl_error("@nospecialize annotation applied to a non-argument"); - } - else if (sn >= sizeof(m->nospecialize) * 8) { - jl_printf(JL_STDERR, - "WARNING: @nospecialize annotation only supported on the first %d arguments.\n", - (int)(sizeof(m->nospecialize) * 8)); - } - else { - m->nospecialize |= (1 << sn); + else if (jl_is_expr(st) && ((jl_expr_t*)st)->head == meta_sym) { + if (jl_expr_nargs(st) > 1 && jl_exprarg(st, 0) == (jl_value_t*)nospecialize_sym) { + for (size_t j=1; j < jl_expr_nargs(st); j++) { + jl_value_t *aj = jl_exprarg(st, j); + if (jl_is_slot(aj)) { + int sn = (int)jl_slot_number(aj) - 2; + if (sn >= 0) { // @nospecialize on self is valid but currently ignored + if (sn > (m->nargs - 2)) { + jl_error("@nospecialize annotation applied to a non-argument"); + } + else if (sn >= sizeof(m->nospecialize) * 8) { + jl_printf(JL_STDERR, + "WARNING: @nospecialize annotation only supported on the first %d arguments.\n", + (int)(sizeof(m->nospecialize) * 8)); + } + else { + m->nospecialize |= (1 << sn); + } } } } + st = jl_nothing; + } + else if (jl_expr_nargs(st) == 2 && jl_exprarg(st, 0) == (jl_value_t*)generator_sym) { + jl_value_t *gname = jl_exprarg(st, 1); + *generator = jl_get_global(m->module, (jl_sym_t*)gname); + if (*generator == NULL) { + jl_error("invalid @generated function; try placing it in global scope"); + } + st = jl_nothing; + } + else if (jl_expr_nargs(st) == 1 && jl_exprarg(st, 0) == (jl_value_t*)generated_only_sym) { + *gen_only = 1; + st = jl_nothing; } - st = jl_nothing; } else { st = jl_resolve_globals(st, m->module, sparam_vars); @@ -521,8 +536,7 @@ static jl_method_t *jl_new_method( jl_tupletype_t *sig, size_t nargs, int isva, - jl_svec_t *tvars, - int isstaged) + jl_svec_t *tvars) { size_t i, l = jl_svec_len(tvars); jl_svec_t *sparam_syms = jl_alloc_svec_uninit(l); @@ -541,12 +555,14 @@ static jl_method_t *jl_new_method( m->sig = (jl_value_t*)sig; m->isva = isva; m->nargs = nargs; - jl_method_set_source(m, definition); - if (isstaged) { - // create and store generator for generated functions - m->generator = jl_get_specialized(m, (jl_value_t*)jl_anytuple_type, jl_emptysvec); + jl_value_t *gen = NULL; int gen_only = 0; + jl_method_set_source(m, definition, &gen, &gen_only); + if (gen) { + m->generator = gen; jl_gc_wb(m, m->generator); - m->generator->inferred = (jl_value_t*)m->source; + } + if (gen_only) { + assert(gen); m->source = NULL; } @@ -668,8 +684,7 @@ extern tracer_cb jl_newmeth_tracer; JL_DLLEXPORT void jl_method_def(jl_svec_t *argdata, jl_code_info_t *f, - jl_module_t *module, - jl_value_t *isstaged) + jl_module_t *module) { // argdata is svec(svec(types...), svec(typevars...)) jl_svec_t *atypes = (jl_svec_t*)jl_svecref(argdata, 0); @@ -726,7 +741,7 @@ JL_DLLEXPORT void jl_method_def(jl_svec_t *argdata, // the result is that the closure variables get interpolated directly into the AST f = jl_new_code_info_from_ast((jl_expr_t*)f); } - m = jl_new_method(f, name, module, (jl_tupletype_t*)argtype, nargs, isva, tvars, isstaged == jl_true); + m = jl_new_method(f, name, module, (jl_tupletype_t*)argtype, nargs, isva, tvars); m->nospecialize |= nospec; if (jl_has_free_typevars(argtype)) { diff --git a/src/utils.scm b/src/utils.scm index 97842a387b544..9e9fc02fb8dc3 100644 --- a/src/utils.scm +++ b/src/utils.scm @@ -84,3 +84,8 @@ (without (cdr alst) remove))))) (define (caddddr x) (car (cdr (cdr (cdr (cdr x)))))) + +(define (find p lst) + (cond ((atom? lst) #f) + ((p (car lst)) lst) + (else (find p (cdr lst)))))