diff --git a/.travis.yml b/.travis.yml index 322ff5c8fd21e..9787360f69f4e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -80,7 +80,7 @@ before_install: BUILDOPTS="$BUILDOPTS USE_BINARYBUILDER_LLVM=1 LLVM_CONFIG=$TRAVIS_BUILD_DIR/usr/tools/llvm-config LLVM_SIZE=$TRAVIS_BUILD_DIR/usr/tools/llvm-size"; BUILDOPTS="$BUILDOPTS VERBOSE=1 USE_BLAS64=0 SUITESPARSE_INC=-I$(brew --prefix suite-sparse-julia)/include FORCE_ASSERTIONS=1"; BUILDOPTS="$BUILDOPTS LIBBLAS=-lopenblas LIBBLASNAME=libopenblas LIBLAPACK=-lopenblas LIBLAPACKNAME=libopenblas"; - for lib in SUITESPARSE BLAS LAPACK GMP MPFR PCRE LIBUNWIND; do + for lib in SUITESPARSE BLAS LAPACK GMP MPFR LIBUNWIND; do BUILDOPTS="$BUILDOPTS USE_SYSTEM_$lib=1"; done; export LDFLAGS="-L$(brew --prefix openblas-julia)/lib -L$(brew --prefix suite-sparse-julia)/lib"; diff --git a/README.windows.md b/README.windows.md index 76056b69959ed..a13c1ba5a86b0 100644 --- a/README.windows.md +++ b/README.windows.md @@ -23,7 +23,7 @@ Instructions for adding fonts to the terminal are available at Additionally, rather than sticking with the default command prompt, you may want to use a different terminal emulator program, such as -[Conemu](https://code.google.com/p/conemu-maximus5/) or [Mintty]( +[Conemu](https://conemu.github.io/) or [Mintty]( https://github.com/mintty/mintty) (note that running Julia on Mintty needs a copy of `stty.exe` in your `%PATH%` to work properly). Alternatively, you may prefer the features of a more full-function IDE, such as [Juno](http://junolab.org), diff --git a/base/Enums.jl b/base/Enums.jl index 021c8c4bea561..234fc42d28afa 100644 --- a/base/Enums.jl +++ b/base/Enums.jl @@ -44,6 +44,9 @@ f (generic function with 1 method) julia> f(apple) "I'm a Fruit with value: 1" + +julia> Fruit(1) +apple::Fruit = 1 ``` Values can also be specified inside a `begin` block, e.g. @@ -100,7 +103,7 @@ macro enum(T, syms...) length(s.args) == 2 && isa(s.args[1], Symbol) i = Core.eval(__module__, s.args[2]) # allow exprs, e.g. uint128"1" if !isa(i, Integer) - throw(ArgumentError("invalid value for Enum $typename, $s=$i; values must be integers")) + throw(ArgumentError("invalid value for Enum $typename, $s; values must be integers")) end i = convert(basetype, i) s = s.args[1] diff --git a/base/abstractset.jl b/base/abstractset.jl index 2c2ec47c0c041..100383a126a1d 100644 --- a/base/abstractset.jl +++ b/base/abstractset.jl @@ -226,14 +226,15 @@ end <=(l::AbstractSet, r::AbstractSet) = l ⊆ r function issubset(l, r) - - rlen = length(r) - #This threshold was empirically determined by repeatedly - #sampling using these two methods. - lenthresh = 70 - - if rlen > lenthresh && !isa(r, AbstractSet) - return issubset(l, Set(r)) + if haslength(r) + rlen = length(r) + #This threshold was empirically determined by repeatedly + #sampling using these two methods (see #26198) + lenthresh = 70 + + if rlen > lenthresh && !isa(r, AbstractSet) + return issubset(l, Set(r)) + end end for elt in l diff --git a/base/accumulate.jl b/base/accumulate.jl index 1f30250f44ee5..c5c4e83b3b0e3 100644 --- a/base/accumulate.jl +++ b/base/accumulate.jl @@ -191,7 +191,7 @@ cumprod(x::AbstractVector) = cumprod(x, dims=1) accumulate(op, A; dims::Integer, [init]) Cumulative operation `op` along the dimension `dims` of `A` (providing `dims` is optional -for vectors). An inital value `init` may optionally be privided by a keyword argument. See +for vectors). An initial value `init` may optionally be provided by a keyword argument. See also [`accumulate!`](@ref) to use a preallocated output array, both for performance and to control the precision of the output (e.g. to avoid overflow). For common operations there are specialized variants of `accumulate`, see: [`cumsum`](@ref), [`cumprod`](@ref) diff --git a/base/array.jl b/base/array.jl index 7559c29ab1d9d..a6364188b8977 100644 --- a/base/array.jl +++ b/base/array.jl @@ -888,7 +888,7 @@ julia> append!([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 +themselves in another collection. The result of the preceding example is equivalent to `push!([1, 2, 3], 4, 5, 6)`. """ function append!(a::Array{<:Any,1}, items::AbstractVector) diff --git a/base/broadcast.jl b/base/broadcast.jl index 88d83d2377e19..a95cc8bbfed29 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -713,6 +713,30 @@ Like [`broadcast`](@ref), but store the result of Note that `dest` is only used to store the result, and does not supply arguments to `f` unless it is also listed in the `As`, as in `broadcast!(f, A, A, B)` to perform `A[:] = broadcast(f, A, B)`. + +# Examples +```jldoctest +julia> A = [1.0; 0.0]; B = [0.0; 0.0]; + +julia> broadcast!(+, B, A, (0, -2.0)); + +julia> B +2-element Array{Float64,1}: + 1.0 + -2.0 + +julia> A +2-element Array{Float64,1}: + 1.0 + 0.0 + +julia> broadcast!(+, A, A, (0, -2.0)); + +julia> A +2-element Array{Float64,1}: + 1.0 + -2.0 +``` """ broadcast!(f::Tf, dest, As::Vararg{Any,N}) where {Tf,N} = (materialize!(dest, broadcasted(f, As...)); dest) diff --git a/base/cartesian.jl b/base/cartesian.jl index 526cff67bd706..865b766664708 100644 --- a/base/cartesian.jl +++ b/base/cartesian.jl @@ -32,7 +32,7 @@ would generate: end end -If you want just a post-expression, supply `nothing` for the pre-expression. Using +If you want just a post-expression, supply [`nothing`](@ref) for the pre-expression. Using parentheses and semicolons, you can supply multi-statement expressions. """ macro nloops(N, itersym, rangeexpr, args...) diff --git a/base/client.jl b/base/client.jl index 7f568d39d143d..5c5894fc7c8a3 100644 --- a/base/client.jl +++ b/base/client.jl @@ -56,7 +56,7 @@ function repl_cmd(cmd, out) # If it's intended to simulate `cd`, it should instead be doing # more nearly `cd $dir && printf %s \$PWD` (with appropriate quoting), # since shell `cd` does more than just `echo` the result. - dir = read(`$shell -c "printf %s $(shell_escape_posixly(dir))"`, String) + dir = read(`$shell -c "printf '%s' $(shell_escape_posixly(dir))"`, String) end cd(dir) end diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 9ca7db1f669a6..17f9eba24044e 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -55,12 +55,15 @@ function abstract_call_gf_by_type(@nospecialize(f), argtypes::Vector{Any}, @nosp rettype = Bottom edgecycle = false edges = Any[] + nonbot = 0 # the index of the only non-Bottom inference result if > 0 + seen = 0 # number of signatures actually inferred for i in 1:napplicable match = applicable[i]::SimpleVector method = match[3]::Method sig = match[1] sigtuple = unwrap_unionall(sig)::DataType splitunions = false + this_rt = Bottom # TODO: splitunions = 1 < countunionsplit(sigtuple.parameters) * napplicable <= sv.params.MAX_UNION_SPLITTING # currently this triggers a bug in inference recursion detection if splitunions @@ -71,24 +74,32 @@ function abstract_call_gf_by_type(@nospecialize(f), argtypes::Vector{Any}, @nosp push!(edges, edge) end edgecycle |= edgecycle1::Bool - rettype = tmerge(rettype, rt) - rettype === Any && break + this_rt = tmerge(this_rt, rt) + this_rt === Any && break end - rettype === Any && break else - rt, edgecycle, edge = abstract_call_method(method, sig, match[2]::SimpleVector, sv) + this_rt, edgecycle, edge = abstract_call_method(method, sig, match[2]::SimpleVector, sv) if edge !== nothing push!(edges, edge) end - rettype = tmerge(rettype, rt) - rettype === Any && break end + if this_rt !== Bottom + if nonbot === 0 + nonbot = i + else + nonbot = -1 + end + end + seen += 1 + rettype = tmerge(rettype, this_rt) + rettype === Any && break end - if napplicable == 1 && !edgecycle && isa(rettype, Type) && sv.params.ipo_constant_propagation + # try constant propagation if only 1 method is inferred to non-Bottom + if nonbot > 0 && seen == napplicable && !edgecycle && isa(rettype, Type) && sv.params.ipo_constant_propagation # if there's a possibility we could constant-propagate a better result # (hopefully without doing too much work), try to do that now # TODO: it feels like this could be better integrated into abstract_call_method / typeinf_edge - const_rettype = abstract_call_method_with_const_args(f, argtypes, applicable[1]::SimpleVector, sv) + const_rettype = abstract_call_method_with_const_args(f, argtypes, applicable[nonbot]::SimpleVector, sv) if const_rettype ⊑ rettype # use the better result, if it's a refinement of rettype rettype = const_rettype diff --git a/base/compiler/ssair/show.jl b/base/compiler/ssair/show.jl index c1b396f22a6ab..97d74be275702 100644 --- a/base/compiler/ssair/show.jl +++ b/base/compiler/ssair/show.jl @@ -327,7 +327,7 @@ function show_ir(io::IO, code::IRCode, expr_type_printer=default_expr_type_print bb_idx = 1 new_nodes = code.new_nodes if any(i -> !isassigned(code.new_nodes, i), 1:length(code.new_nodes)) - printstyled(io, :red, "ERROR: New node array has unset entry\n") + printstyled(io, "ERROR: New node array has unset entry\n", color=:red) new_nodes = new_nodes[filter(i -> isassigned(code.new_nodes, i), 1:length(code.new_nodes))] end for nn in new_nodes @@ -354,7 +354,7 @@ function show_ir(io::IO, code::IRCode, expr_type_printer=default_expr_type_print if !isassigned(stmts, idx) # This is invalid, but do something useful rather # than erroring, to make debugging easier - printstyled(io, :red, "#UNDEF\n") + printstyled(io, "#UNDEF\n", color=:red) continue end stmt = stmts[idx] @@ -495,7 +495,7 @@ function show_ir(io::IO, code::CodeInfo, expr_type_printer=default_expr_type_pri if !isassigned(stmts, idx) # This is invalid, but do something useful rather # than erroring, to make debugging easier - printstyled(io, :red, "#UNDEF\n") + printstyled(io, "#UNDEF\n", color=:red) continue end stmt = stmts[idx] diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index d4bc7253da23d..8e9a33b34f00b 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -537,8 +537,10 @@ function getfield_nothrow(@nospecialize(s00), @nospecialize(name), @nospecialize sv = s00.val end if isa(name, Const) - (isa(sv, Module) && isa(name.val, Symbol)) || return false - (isa(name.val, Symbol) || isa(name.val, Int)) || return false + if !isa(name.val, Symbol) + isa(sv, Module) && return false + isa(name.val, Int) || return false + end return isdefined(sv, name.val) end if bounds_check_disabled && !isa(sv, Module) @@ -1056,7 +1058,7 @@ function _builtin_nothrow(@nospecialize(f), argtypes::Array{Any,1}, @nospecializ length(argtypes) == 1 || return false return sizeof_nothrow(argtypes[1]) elseif f === Core.kwfunc - length(argtypes) == 2 || return false + length(argtypes) == 1 || return false return isa(rt, Const) end return false diff --git a/base/compiler/typelimits.jl b/base/compiler/typelimits.jl index 83ca2a386b119..f8220375e4518 100644 --- a/base/compiler/typelimits.jl +++ b/base/compiler/typelimits.jl @@ -412,6 +412,9 @@ function tuplemerge(a::DataType, b::DataType) for loop_b = (false, true) for i = (lt + 1):(loop_b ? lbr : lar) ti = unwrapva(loop_b ? bp[i] : ap[i]) + while ti isa TypeVar + ti = ti.ub + end # compare (ti <-> tail), (wrapper ti <-> tail), (ti <-> wrapper tail), then (wrapper ti <-> wrapper tail) # until we find the first element that contains the other in the pair # TODO: this result would be more stable (and more associative and more commutative) diff --git a/base/compiler/utilities.jl b/base/compiler/utilities.jl index 0edcec83bd038..89f5c0b05fb52 100644 --- a/base/compiler/utilities.jl +++ b/base/compiler/utilities.jl @@ -122,7 +122,7 @@ function retrieve_code_info(linfo::MethodInstance) end function code_for_method(method::Method, @nospecialize(atypes), sparams::SimpleVector, world::UInt, preexisting::Bool=false) - if world < min_world(method) + if world < min_world(method) || world > max_world(method) return nothing end if isdefined(method, :generator) && !isdispatchtuple(atypes) diff --git a/base/docs/Docs.jl b/base/docs/Docs.jl index c9de490bcb67f..d409eea9a6ca8 100644 --- a/base/docs/Docs.jl +++ b/base/docs/Docs.jl @@ -281,7 +281,7 @@ function astname(x::Expr, ismacro::Bool) ismacro ? macroname(x) : x # Call overloading, e.g. `(a::A)(b) = b` or `function (a::A)(b) b end` should document `A(b)` elseif (isexpr(x, :function) || isexpr(x, :(=))) && isexpr(x.args[1], :call) && isexpr(x.args[1].args[1], :(::)) - return astname(x.args[1].args[1].args[2], ismacro) + return astname(x.args[1].args[1].args[end], ismacro) else n = isexpr(x, (:module, :struct)) ? 2 : 1 astname(x.args[n], ismacro) diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index 80100bfb6b3e4..f1cf823457985 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -260,7 +260,7 @@ kw"'" """ const -`const` is used to declare global variables which are also constant. In almost all code +`const` is used to declare global variables whose values will not change. In almost all code (and particularly performance sensitive code) global variables should be declared constant in this way. @@ -275,15 +275,17 @@ const y, z = 7, 11 Note that `const` only applies to one `=` operation, therefore `const x = y = 1` declares `x` to be constant but not `y`. On the other hand, `const x = const y = 1` -declares both `x` and `y` as constants. +declares both `x` and `y` constant. -Note that "constant-ness" is not enforced inside containers, so if `x` is an array or -dictionary (for example) you can still add and remove elements. +Note that "constant-ness" does not extend into mutable containers; only the +association between a variable and its value is constant. +If `x` is an array or dictionary (for example) you can still modify, add, or remove elements. -Technically, you can even redefine `const` variables, although this will generate a -warning from the compiler. The only strict requirement is that the *type* of the -variable does not change, which is why `const` variables are much faster than regular -globals. +In some cases changing the value of a `const` variable gives a warning instead of +an error. +However, this can produce unpredictable behavior or corrupt the state of your program, +and so should be avoided. +This feature is intended only for convenience during interactive use. """ kw"const" @@ -855,7 +857,7 @@ ErrorException WrappedException(msg) Generic type for `Exception`s wrapping another `Exception`, such as `LoadError` and -`InitError`. Those exceptions contain information about the the root cause of an +`InitError`. Those exceptions contain information about the root cause of an exception. Subtypes define a field `error` containing the causing `Exception`. """ Core.WrappedException @@ -1083,6 +1085,8 @@ InterruptException Determine whether the given generic function has a method applicable to the given arguments. +See also [`hasmethod`](@ref). + # Examples ```jldoctest julia> function f(x, y) diff --git a/base/download.jl b/base/download.jl index e5ed437a94a7f..798a9930b7d42 100644 --- a/base/download.jl +++ b/base/download.jl @@ -2,9 +2,7 @@ # file downloading -downloadcmd = nothing if Sys.iswindows() - downloadcmd = "powershell" function download(url::AbstractString, filename::AbstractString) ps = "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe" tls12 = "[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12" @@ -25,28 +23,16 @@ if Sys.iswindows() end else function download(url::AbstractString, filename::AbstractString) - global downloadcmd - if downloadcmd === nothing - for checkcmd in ("curl", "wget", "fetch") - try - # Sys.which() will throw() if it can't find `checkcmd` - Sys.which(checkcmd) - downloadcmd = checkcmd - break - catch - end - end - end - if downloadcmd == "wget" + if Sys.which("curl") !== nothing + run(`curl -g -L -f -o $filename $url`) + elseif Sys.which("wget") !== nothing try run(`wget -O $filename $url`) catch - rm(filename) # wget always creates a file + rm(filename, force=true) # wget always creates a file rethrow() end - elseif downloadcmd == "curl" - run(`curl -g -L -f -o $filename $url`) - elseif downloadcmd == "fetch" + elseif Sys.which("fetch") !== nothing run(`fetch -f $filename $url`) else error("no download agent available; install curl, wget, or fetch") diff --git a/base/errorshow.jl b/base/errorshow.jl index c6c81a02081ba..f9de780cf0446 100644 --- a/base/errorshow.jl +++ b/base/errorshow.jl @@ -432,6 +432,8 @@ function show_method_candidates(io::IO, ex::MethodError, @nospecialize kwargs=() end if ex.world < min_world(method) print(iob, " (method too new to be called from this world context.)") + elseif ex.world > max_world(method) + print(iob, " (method deleted before this world age.)") end # TODO: indicate if it's in the wrong world push!(lines, (buf, right_matches)) diff --git a/base/essentials.jl b/base/essentials.jl index 3bf9ecfad81d3..575d3aac0e9d0 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -79,7 +79,7 @@ end @specialize Reset the specialization hint for an argument back to the default. -For details, see [`@specialize`](@ref). +For details, see [`@nospecialize`](@ref). """ macro specialize(vars...) if nfields(vars) === 1 @@ -264,11 +264,22 @@ function typename(a::Union) end typename(union::UnionAll) = typename(union.body) -convert(::Type{T}, x::T) where {T<:Tuple{Any, Vararg{Any}}} = x -convert(::Type{Tuple{}}, x::Tuple{Any, Vararg{Any}}) = throw(MethodError(convert, (Tuple{}, x))) -convert(::Type{T}, x::Tuple{Any, Vararg{Any}}) where {T<:Tuple} = +const AtLeast1 = Tuple{Any, Vararg{Any}} + +# converting to empty tuple type +convert(::Type{Tuple{}}, ::Tuple{}) = () +convert(::Type{Tuple{}}, x::AtLeast1) = throw(MethodError(convert, (Tuple{}, x))) + +# converting to tuple types with at least one element +convert(::Type{T}, x::T) where {T<:AtLeast1} = x +convert(::Type{T}, x::AtLeast1) where {T<:AtLeast1} = (convert(tuple_type_head(T), x[1]), convert(tuple_type_tail(T), tail(x))...) +# converting to Vararg tuple types +convert(::Type{Tuple{Vararg{V}}}, x::Tuple{Vararg{V}}) where {V} = x +convert(T::Type{Tuple{Vararg{V}}}, x::Tuple) where {V} = + (convert(tuple_type_head(T), x[1]), convert(T, tail(x))...) + # TODO: the following definitions are equivalent (behaviorally) to the above method # I think they may be faster / more efficient for inference, # if we could enable them, but are they? @@ -795,7 +806,7 @@ isdone(itr, state...) = missing iterate(iter [, state]) -> Union{Nothing, Tuple{Any, Any}} Advance the iterator to obtain the next element. If no elements -remain, nothing should be returned. Otherwise, a 2-tuple of the +remain, `nothing` should be returned. Otherwise, a 2-tuple of the next element and the new iteration state should be returned. """ function iterate end diff --git a/base/file.jl b/base/file.jl index 1fab5238bc745..da966d37f0e49 100644 --- a/base/file.jl +++ b/base/file.jl @@ -317,7 +317,7 @@ end """ cp(src::AbstractString, dst::AbstractString; force::Bool=false, follow_symlinks::Bool=false) -Copy the file, link, or directory from `src` to `dest`. +Copy the file, link, or directory from `src` to `dst`. `force=true` will first remove an existing `dst`. If `follow_symlinks=false`, and `src` is a symbolic link, `dst` will be created as a diff --git a/base/generator.jl b/base/generator.jl index b0f7e32d0b22f..73d0e8a2e3ee2 100644 --- a/base/generator.jl +++ b/base/generator.jl @@ -126,3 +126,4 @@ IteratorEltype(::Type) = HasEltype() # HasEltype is the default IteratorEltype(::Type{Generator{I,T}}) where {I,T} = EltypeUnknown() IteratorEltype(::Type{Any}) = EltypeUnknown() +IteratorEltype(::Type{Union{}}) = EltypeUnknown() diff --git a/base/gmp.jl b/base/gmp.jl index d80182646732e..4a7f6945e050d 100644 --- a/base/gmp.jl +++ b/base/gmp.jl @@ -487,13 +487,13 @@ count_ones_abs(x::BigInt) = iszero(x) ? 0 : MPZ.mpn_popcount(x) divrem(x::BigInt, y::BigInt) = MPZ.tdiv_qr(x, y) -cmp(x::BigInt, y::BigInt) = MPZ.cmp(x, y) -cmp(x::BigInt, y::ClongMax) = MPZ.cmp_si(x, y) -cmp(x::BigInt, y::CulongMax) = MPZ.cmp_ui(x, y) +cmp(x::BigInt, y::BigInt) = sign(MPZ.cmp(x, y)) +cmp(x::BigInt, y::ClongMax) = sign(MPZ.cmp_si(x, y)) +cmp(x::BigInt, y::CulongMax) = sign(MPZ.cmp_ui(x, y)) cmp(x::BigInt, y::Integer) = cmp(x, big(y)) cmp(x::Integer, y::BigInt) = -cmp(y, x) -cmp(x::BigInt, y::CdoubleMax) = isnan(y) ? -1 : MPZ.cmp_d(x, y) +cmp(x::BigInt, y::CdoubleMax) = isnan(y) ? -1 : sign(MPZ.cmp_d(x, y)) cmp(x::CdoubleMax, y::BigInt) = -cmp(y, x) isqrt(x::BigInt) = MPZ.sqrt(x) diff --git a/base/indices.jl b/base/indices.jl index adec164f080b5..dce053872bd1a 100644 --- a/base/indices.jl +++ b/base/indices.jl @@ -13,7 +13,32 @@ Indices{N} = NTuple{N,AbstractUnitRange} ## Traits for array types ## abstract type IndexStyle end +""" + IndexLinear() + +Subtype of [`IndexStyle`](@ref) used to describe arrays which +are optimally indexed by one linear index. + +A linear indexing style uses one integer to describe the position in the array +(even if it's a multidimensional array) and column-major +ordering is used to access the elements. For example, +if `A` were a `(2, 3)` custom matrix type with linear indexing, +and we referenced `A[5]` (using linear style), this would +be equivalent to referencing `A[1, 3]` (since `2*1 + 3 = 5`). +See also [`IndexCartesian`](@ref). +""" struct IndexLinear <: IndexStyle end +""" + IndexCartesian() + +Subtype of [`IndexStyle`](@ref) used to describe arrays which +are optimally indexed by a Cartesian index. + +A cartesian indexing style uses multiple integers/indices to describe the position in the array. +For example, if `A` were a `(2, 3, 4)` custom matrix type with cartesian indexing, +we could reference `A[2, 1, 3]` and Julia would automatically convert this into the +correct location in the underlying memory. See also [`IndexLinear`](@ref). +""" struct IndexCartesian <: IndexStyle end """ @@ -21,14 +46,14 @@ struct IndexCartesian <: IndexStyle end IndexStyle(typeof(A)) `IndexStyle` specifies the "native indexing style" for array `A`. When -you define a new `AbstractArray` type, you can choose to implement -either linear indexing or cartesian indexing. If you decide to -implement linear indexing, then you must set this trait for your array +you define a new [`AbstractArray`](@ref) type, you can choose to implement +either linear indexing (with [`IndexLinear`](@ref)) or cartesian indexing. +If you decide to implement linear indexing, then you must set this trait for your array type: Base.IndexStyle(::Type{<:MyArray}) = IndexLinear() -The default is `IndexCartesian()`. +The default is [`IndexCartesian()`](@ref). Julia's internal indexing machinery will automatically (and invisibly) convert all indexing operations into the preferred style. This allows users @@ -330,7 +355,7 @@ julia> extrema(b) LinearIndices(inds::CartesianIndices) -> R LinearIndices(sz::Dims) -> R - LinearIndices(istart:istop, jstart:jstop, ...) -> R + LinearIndices((istart:istop, jstart:jstop, ...)) -> R Return a `LinearIndices` array with the specified shape or [`axes`](@ref). diff --git a/base/initdefs.jl b/base/initdefs.jl index 0846a47534886..d93590af4d63c 100644 --- a/base/initdefs.jl +++ b/base/initdefs.jl @@ -138,8 +138,9 @@ function init_load_path() unsafe_string(Base.JLOptions().project) : get(ENV, "JULIA_PROJECT", nothing)) HOME_PROJECT[] = + project == nothing ? nothing : project == "" ? nothing : - project == "@." ? current_project() : project + project == "@." ? current_project() : abspath(project) append!(empty!(LOAD_PATH), paths) end diff --git a/base/int.jl b/base/int.jl index e6610e9af4774..718542e0e13ea 100644 --- a/base/int.jl +++ b/base/int.jl @@ -579,9 +579,18 @@ end @big_str str @big_str(str) -`@big_str` parses a string into a BigInt -Throws an `ArgumentError` if the string is not a valid integer -Removes all underscores `_` from the string +Parse a string into a [`BigInt`](@ref) or [`BigFloat`](@ref), +and throw an `ArgumentError` if the string is not a valid number. +For integers `_` is allowed in the string as a separator. + +# Examples +```jldoctest +julia> big"123_456" +123456 + +julia> big"7891.5" +7.8915e+03 +``` """ macro big_str(s) if '_' in s diff --git a/base/intfuncs.jl b/base/intfuncs.jl index c3ec78eeb31e9..a6f6ea05efe41 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -440,7 +440,8 @@ function ndigits0znb(x::Integer, b::Integer) return d end -ndigits0znb(x::Unsigned, b::Integer) = ndigits0znb(signed(x), b) +# do first division before conversion with signed here, which can otherwise overflow +ndigits0znb(x::Unsigned, b::Integer) = ndigits0znb(-signed(fld(x, -b)), b) + (x != 0) ndigits0znb(x::Bool, b::Integer) = x % Int # The suffix "pb" stands for "positive base" @@ -543,11 +544,11 @@ function bin(x::Unsigned, pad::Int, neg::Bool) i = neg + max(pad,sizeof(x)<<3-leading_zeros(x)) a = StringVector(i) while i > neg - a[i] = '0'+(x&0x1) + @inbounds a[i] = 48+(x&0x1) x >>= 1 i -= 1 end - if neg; a[1]='-'; end + if neg; @inbounds a[1]=0x2d; end String(a) end @@ -555,11 +556,11 @@ function oct(x::Unsigned, pad::Int, neg::Bool) i = neg + max(pad,div((sizeof(x)<<3)-leading_zeros(x)+2,3)) a = StringVector(i) while i > neg - a[i] = '0'+(x&0x7) + @inbounds a[i] = 48+(x&0x7) x >>= 3 i -= 1 end - if neg; a[1]='-'; end + if neg; @inbounds a[1]=0x2d; end String(a) end @@ -567,11 +568,11 @@ function dec(x::Unsigned, pad::Int, neg::Bool) i = neg + ndigits(x, base=10, pad=pad) a = StringVector(i) while i > neg - a[i] = '0'+rem(x,10) + @inbounds a[i] = 48+rem(x,10) x = oftype(x,div(x,10)) i -= 1 end - if neg; a[1]='-'; end + if neg; @inbounds a[1]=0x2d; end String(a) end @@ -580,11 +581,11 @@ function hex(x::Unsigned, pad::Int, neg::Bool) a = StringVector(i) while i > neg d = x & 0xf - a[i] = '0'+d+39*(d>9) + @inbounds a[i] = 48+d+39*(d>9) x >>= 4 i -= 1 end - if neg; a[1]='-'; end + if neg; @inbounds a[1]=0x2d; end String(a) end diff --git a/base/iobuffer.jl b/base/iobuffer.jl index b3efe5076c1ac..dec5735d2ed3b 100644 --- a/base/iobuffer.jl +++ b/base/iobuffer.jl @@ -167,6 +167,21 @@ function unsafe_read(from::GenericIOBuffer, p::Ptr{UInt8}, nb::UInt) nothing end +function read(from::GenericIOBuffer, T::Union{Type{Int16},Type{UInt16},Type{Int32},Type{UInt32},Type{Int64},Type{UInt64},Type{Int128},Type{UInt128},Type{Float16},Type{Float32},Type{Float64}}) + from.readable || throw(ArgumentError("read failed, IOBuffer is not readable")) + avail = bytesavailable(from) + nb = sizeof(T) + if nb > avail + throw(EOFError()) + end + GC.@preserve from begin + ptr::Ptr{T} = pointer(from.data, from.ptr) + x = unsafe_load(ptr) + end + from.ptr += nb + return x +end + function read_sub(from::GenericIOBuffer, a::AbstractArray{T}, offs, nel) where T @assert !has_offset_axes(a) from.readable || throw(ArgumentError("read failed, IOBuffer is not readable")) diff --git a/base/iostream.jl b/base/iostream.jl index 8454a1e330175..0f9ef99b146e1 100644 --- a/base/iostream.jl +++ b/base/iostream.jl @@ -401,6 +401,10 @@ if ENDIAN_BOM == 0x04030201 function read(s::IOStream, T::Union{Type{Int16},Type{UInt16},Type{Int32},Type{UInt32},Type{Int64},Type{UInt64}}) return ccall(:jl_ios_get_nbyte_int, UInt64, (Ptr{Cvoid}, Csize_t), s.ios, sizeof(T)) % T end + +read(s::IOStream, ::Type{Float16}) = reinterpret(Float16, read(s, Int16)) +read(s::IOStream, ::Type{Float32}) = reinterpret(Float32, read(s, Int32)) +read(s::IOStream, ::Type{Float64}) = reinterpret(Float64, read(s, Int64)) end function unsafe_read(s::IOStream, p::Ptr{UInt8}, nb::UInt) diff --git a/base/iterators.jl b/base/iterators.jl index 396b1744482f7..ac3c3ad88d9a3 100644 --- a/base/iterators.jl +++ b/base/iterators.jl @@ -181,9 +181,9 @@ 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 -`CartesianIndex`; specifying `IndexStyle(A)` chooses whichever has +Specifying [`IndexLinear()`](@ref) ensures that `i` will be an integer; +specifying [`IndexCartesian()`](@ref) ensures that `i` will be a +[`CartesianIndex`](@ref); specifying `IndexStyle(A)` chooses whichever has been defined as the native indexing style for array `A`. Mutation of the bounds of the underlying array will invalidate this iterator. @@ -880,6 +880,7 @@ _flatteneltype(I, et) = EltypeUnknown() flatten_iteratorsize(::Union{HasShape, HasLength}, ::Type{<:NTuple{N,Any}}) where {N} = HasLength() flatten_iteratorsize(::Union{HasShape, HasLength}, ::Type{<:Tuple}) = SizeUnknown() flatten_iteratorsize(::Union{HasShape, HasLength}, ::Type{<:Number}) = HasLength() +flatten_iteratorsize(::Union{HasShape, HasLength}, ::Type{Union{}}) = SizeUnknown() flatten_iteratorsize(a, b) = SizeUnknown() _flatten_iteratorsize(sz, ::EltypeUnknown, I) = SizeUnknown() diff --git a/base/loading.jl b/base/loading.jl index 7feb1cdd26d85..18a8765d68480 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -254,6 +254,9 @@ locate_package(::Nothing) = nothing Return the path of `m.jl` file that was used to `import` module `m`, or `nothing` if `m` was not imported from a package. + +Use [`dirname`](@ref) to get the directory part and [`basename`](@ref) +to get the file name part of the path. """ function pathof(m::Module) pkgid = get(Base.module_keys, m, nothing) @@ -816,7 +819,7 @@ function require(into::Module, mod::Symbol) if where.uuid === nothing throw(ArgumentError(""" Package $mod not found in current path: - - Run `Pkg.add($(repr(String(mod))))` to install the $mod package. + - Run `import Pkg; Pkg.add($(repr(String(mod))))` to install the $mod package. """)) else s = """ diff --git a/base/logging.jl b/base/logging.jl index c3570469a90fb..9034460a4dfe2 100644 --- a/base/logging.jl +++ b/base/logging.jl @@ -496,7 +496,7 @@ with_logger(f::Function, logger::AbstractLogger) = with_logstate(f, LogState(log current_logger() Return the logger for the current task, or the global logger if none is -is attached to the task. +attached to the task. """ current_logger() = current_logstate().logger diff --git a/base/math.jl b/base/math.jl index 6e758cf7b83e2..0793e4996792f 100644 --- a/base/math.jl +++ b/base/math.jl @@ -27,7 +27,7 @@ using Core.Intrinsics: sqrt_llvm using .Base: IEEEFloat -@noinline function throw_complex_domainerror(f, x) +@noinline function throw_complex_domainerror(f::Symbol, x) throw(DomainError(x, string("$f will only return a complex result if called with a ", "complex argument. Try $f(Complex(x))."))) end @@ -198,17 +198,17 @@ julia> log(4,2) 0.5 julia> log(-2, 3) -ERROR: DomainError with log: --2.0 will only return a complex result if called with a complex argument. Try -2.0(Complex(x)). +ERROR: DomainError with -2.0: +log will only return a complex result if called with a complex argument. Try log(Complex(x)). Stacktrace: - [1] throw_complex_domainerror(::Float64, ::Symbol) at ./math.jl:31 + [1] throw_complex_domainerror(::Symbol, ::Float64) at ./math.jl:31 [...] julia> log(2, -3) -ERROR: DomainError with log: --3.0 will only return a complex result if called with a complex argument. Try -3.0(Complex(x)). +ERROR: DomainError with -3.0: +log will only return a complex result if called with a complex argument. Try log(Complex(x)). Stacktrace: - [1] throw_complex_domainerror(::Float64, ::Symbol) at ./math.jl:31 + [1] throw_complex_domainerror(::Symbol, ::Float64) at ./math.jl:31 [...] ``` @@ -393,6 +393,19 @@ atanh(x::Number) Compute the natural logarithm of `x`. Throws [`DomainError`](@ref) for negative [`Real`](@ref) arguments. Use complex negative arguments to obtain complex results. + +# Examples +```jldoctest; filter = r"Stacktrace:(\\n \\[[0-9]+\\].*)*" +julia> log(2) +0.6931471805599453 + +julia> log(-3) +ERROR: DomainError with -3.0: +log will only return a complex result if called with a complex argument. Try log(Complex(x)). +Stacktrace: + [1] throw_complex_domainerror(::Symbol, ::Float64) at ./math.jl:31 +[...] +``` """ log(x::Number) @@ -459,10 +472,10 @@ julia> log1p(0) 0.0 julia> log1p(-2) -ERROR: DomainError with log1p: --2.0 will only return a complex result if called with a complex argument. Try -2.0(Complex(x)). +ERROR: DomainError with -2.0: +log1p will only return a complex result if called with a complex argument. Try log1p(Complex(x)). Stacktrace: - [1] throw_complex_domainerror(::Float64, ::Symbol) at ./math.jl:31 + [1] throw_complex_domainerror(::Symbol, ::Float64) at ./math.jl:31 [...] ``` """ @@ -709,18 +722,18 @@ according to the rounding mode `r`. In other words, the quantity without any intermediate rounding. - if `r == RoundNearest`, then the result is exact, and in the interval - ``[-|y|/2, |y|/2]``. + ``[-|y|/2, |y|/2]``. See also [`RoundNearest`](@ref). - if `r == RoundToZero` (default), then the result is exact, and in the interval - ``[0, |y|)`` if `x` is positive, or ``(-|y|, 0]`` otherwise. + ``[0, |y|)`` if `x` is positive, or ``(-|y|, 0]`` otherwise. See also [`RoundToZero`](@ref). - if `r == RoundDown`, then the result is in the interval ``[0, y)`` if `y` is positive, or ``(y, 0]`` otherwise. The result may not be exact if `x` and `y` have different signs, and - `abs(x) < abs(y)`. + `abs(x) < abs(y)`. See also[`RoundDown`](@ref). - if `r == RoundUp`, then the result is in the interval `(-y,0]` if `y` is positive, or `[0,-y)` otherwise. The result may not be exact if `x` and `y` have the same sign, and - `abs(x) < abs(y)`. + `abs(x) < abs(y)`. See also [`RoundUp`](@ref). """ rem(x, y, ::RoundingMode{:ToZero}) = rem(x,y) @@ -820,14 +833,15 @@ without any intermediate rounding. This internally uses a high precision approxi 2π, and so will give a more accurate result than `rem(x,2π,r)` - if `r == RoundNearest`, then the result is in the interval ``[-π, π]``. This will generally - be the most accurate result. + be the most accurate result. See also [`RoundNearest`](@ref). - if `r == RoundToZero`, then the result is in the interval ``[0, 2π]`` if `x` is positive,. - or ``[-2π, 0]`` otherwise. + or ``[-2π, 0]`` otherwise. See also [`RoundToZero`](@ref). - if `r == RoundDown`, then the result is in the interval ``[0, 2π]``. - + See also [`RoundDown`](@ref). - if `r == RoundUp`, then the result is in the interval ``[-2π, 0]``. + See also [`RoundUp`](@ref). # Examples ```jldoctest diff --git a/base/methodshow.jl b/base/methodshow.jl index e02101ba533d1..2015a564ac1f8 100644 --- a/base/methodshow.jl +++ b/base/methodshow.jl @@ -76,7 +76,7 @@ end function kwarg_decl(m::Method, kwtype::DataType) sig = rewrap_unionall(Tuple{kwtype, Any, unwrap_unionall(m.sig).parameters...}, m.sig) - kwli = ccall(:jl_methtable_lookup, Any, (Any, Any, UInt), kwtype.name.mt, sig, typemax(UInt)) + kwli = ccall(:jl_methtable_lookup, Any, (Any, Any, UInt), kwtype.name.mt, sig, max_world(m)) if kwli !== nothing kwli = kwli::Method src = uncompressed_ast(kwli) diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 4677e8649a680..1a78b84c1e6a3 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -156,7 +156,7 @@ module IteratorsMD # Iteration """ CartesianIndices(sz::Dims) -> R - CartesianIndices(istart:istop, jstart:jstop, ...) -> R + CartesianIndices((istart:istop, jstart:jstop, ...)) -> R Define a region `R` spanning a multidimensional rectangular range of integer indices. These are most commonly encountered in the diff --git a/base/multimedia.jl b/base/multimedia.jl index 3ec70cc12b663..9e17f05d7312b 100644 --- a/base/multimedia.jl +++ b/base/multimedia.jl @@ -43,8 +43,8 @@ julia> showable("img/png", rand(5)) false ``` """ -showable(::MIME{mime}, x) where {mime} = hasmethod(show, Tuple{IO, MIME{mime}, typeof(x)}) -showable(m::AbstractString, x) = showable(MIME(m), x) +showable(::MIME{mime}, @nospecialize x) where {mime} = hasmethod(show, Tuple{IO, MIME{mime}, typeof(x)}) +showable(m::AbstractString, @nospecialize x) = showable(MIME(m), x) """ show(io, mime, x) @@ -175,8 +175,8 @@ end abstract type AbstractDisplay end # it is convenient to accept strings instead of ::MIME -display(d::AbstractDisplay, mime::AbstractString, x) = display(d, MIME(mime), x) -display(mime::AbstractString, x) = display(MIME(mime), x) +display(d::AbstractDisplay, mime::AbstractString, @nospecialize x) = display(d, MIME(mime), x) +display(mime::AbstractString, @nospecialize x) = display(MIME(mime), x) """ displayable(mime) -> Bool @@ -201,12 +201,12 @@ objects are printed in the Julia REPL.) struct TextDisplay <: AbstractDisplay io::IO end -display(d::TextDisplay, M::MIME"text/plain", x) = show(d.io, M, x) -display(d::TextDisplay, x) = display(d, MIME"text/plain"(), x) +display(d::TextDisplay, M::MIME"text/plain", @nospecialize x) = show(d.io, M, x) +display(d::TextDisplay, @nospecialize x) = display(d, MIME"text/plain"(), x) # if you explicitly call display("text/foo", x), it should work on a TextDisplay: displayable(d::TextDisplay, M::MIME) = istextmime(M) -function display(d::TextDisplay, M::MIME, x) +function display(d::TextDisplay, M::MIME, @nospecialize x) displayable(d, M) || throw(MethodError(display, (d, M, x))) show(d.io, M, x) end @@ -254,7 +254,7 @@ function reinit_displays() pushdisplay(TextDisplay(stdout)) end -xdisplayable(D::AbstractDisplay, args...) = applicable(display, D, args...) +xdisplayable(D::AbstractDisplay, @nospecialize args...) = applicable(display, D, args...) """ display(x) @@ -280,7 +280,7 @@ variants, one can also supply the "raw" data in the requested MIME type by passi `x::AbstractString` (for MIME types with text-based storage, such as text/html or application/postscript) or `x::Vector{UInt8}` (for binary MIME types). """ -function display(x) +function display(@nospecialize x) for i = length(displays):-1:1 if xdisplayable(displays[i], x) try @@ -294,7 +294,7 @@ function display(x) throw(MethodError(display, (x,))) end -function display(m::MIME, x) +function display(m::MIME, @nospecialize x) for i = length(displays):-1:1 if xdisplayable(displays[i], m, x) try @@ -339,7 +339,7 @@ Using `redisplay` is also a hint to the backend that `x` may be redisplayed several times, and the backend may choose to defer the display until (for example) the next interactive prompt. """ -function redisplay(x) +function redisplay(@nospecialize x) for i = length(displays):-1:1 if xdisplayable(displays[i], x) try @@ -353,7 +353,7 @@ function redisplay(x) throw(MethodError(redisplay, (x,))) end -function redisplay(m::Union{MIME,AbstractString}, x) +function redisplay(m::Union{MIME,AbstractString}, @nospecialize x) for i = length(displays):-1:1 if xdisplayable(displays[i], m, x) try @@ -368,8 +368,8 @@ function redisplay(m::Union{MIME,AbstractString}, x) end # default redisplay is simply to call display -redisplay(d::AbstractDisplay, x) = display(d, x) -redisplay(d::AbstractDisplay, m::Union{MIME,AbstractString}, x) = display(d, m, x) +redisplay(d::AbstractDisplay, @nospecialize x) = display(d, x) +redisplay(d::AbstractDisplay, m::Union{MIME,AbstractString}, @nospecialize x) = display(d, m, x) ########################################################################### diff --git a/base/parse.jl b/base/parse.jl index d0037ca6a6574..50b76e0577eb1 100644 --- a/base/parse.jl +++ b/base/parse.jl @@ -106,14 +106,21 @@ function tryparse_internal(::Type{T}, s::AbstractString, startpos::Int, endpos:: return nothing end - base = convert(T,base) - m::T = div(typemax(T)-base+1,base) + base = convert(T, base) + m::T = div(typemax(T) - base + 1, base) n::T = 0 a::Int = base <= 36 ? 10 : 36 + _0 = UInt32('0') + _9 = UInt32('9') + _A = UInt32('A') + _a = UInt32('a') + _Z = UInt32('Z') + _z = UInt32('z') while n <= m - d::T = '0' <= c <= '9' ? c-'0' : - 'A' <= c <= 'Z' ? c-'A'+10 : - 'a' <= c <= 'z' ? c-'a'+a : base + _c = UInt32(c) + d::T = _0 <= _c <= _9 ? _c-_0 : + _A <= _c <= _Z ? _c-_A+ UInt32(10) : + _a <= _c <= _z ? _c-_a+a : base if d >= base raise && throw(ArgumentError("invalid base $base digit $(repr(c)) in $(repr(SubString(s,startpos,endpos)))")) return nothing @@ -129,9 +136,10 @@ function tryparse_internal(::Type{T}, s::AbstractString, startpos::Int, endpos:: end (T <: Signed) && (n *= sgn) while !isspace(c) - d::T = '0' <= c <= '9' ? c-'0' : - 'A' <= c <= 'Z' ? c-'A'+10 : - 'a' <= c <= 'z' ? c-'a'+a : base + _c = UInt32(c) + d::T = _0 <= _c <= _9 ? _c-_0 : + _A <= _c <= _Z ? _c-_A+ UInt32(10) : + _a <= _c <= _z ? _c-_a+a : base if d >= base raise && throw(ArgumentError("invalid base $base digit $(repr(c)) in $(repr(SubString(s,startpos,endpos)))")) return nothing diff --git a/base/printf.jl b/base/printf.jl index 5010503949819..85a066be43bca 100644 --- a/base/printf.jl +++ b/base/printf.jl @@ -863,7 +863,7 @@ function decode_oct(d::Integer) @handle_zero x digits pt = i = div((sizeof(x)<<3)-leading_zeros(x)+2,3) while i > 0 - digits[i] = '0'+(x&0x7) + digits[i] = 48+(x&0x7) x >>= 3 i -= 1 end @@ -876,7 +876,7 @@ function decode_0ct(d::Integer) pt = i = div((sizeof(x)<<3)-leading_zeros(x)+5,3) digits = DIGITSs[Threads.threadid()] while i > 0 - digits[i] = '0'+(x&0x7) + digits[i] = 48+(x&0x7) x >>= 3 i -= 1 end @@ -889,7 +889,7 @@ function decode_dec(d::Integer) @handle_zero x digits pt = i = Base.ndigits0z(x) while i > 0 - digits[i] = '0'+rem(x,10) + digits[i] = 48+rem(x,10) x = div(x,10) i -= 1 end diff --git a/base/process.jl b/base/process.jl index d9b15b2e5bfe3..ef690c454d263 100644 --- a/base/process.jl +++ b/base/process.jl @@ -625,6 +625,11 @@ function open(f::Function, cmds::AbstractCmd, args...) return ret end +""" + read(command::Cmd) + +Run `command` and return the resulting output as an array of bytes. +""" function read(cmd::AbstractCmd) procs = open(cmd, "r", devnull) bytes = read(procs.out) @@ -632,6 +637,11 @@ function read(cmd::AbstractCmd) return bytes end +""" + read(command::Cmd, String) + +Run `command` and return the resulting output as a `String`. +""" read(cmd::AbstractCmd, ::Type{String}) = String(read(cmd)) """ diff --git a/base/promotion.jl b/base/promotion.jl index 78a1954da55eb..319284beaaaf9 100644 --- a/base/promotion.jl +++ b/base/promotion.jl @@ -14,7 +14,11 @@ typejoin(@nospecialize(t)) = (@_pure_meta; t) typejoin(@nospecialize(t), ts...) = (@_pure_meta; typejoin(t, typejoin(ts...))) function typejoin(@nospecialize(a), @nospecialize(b)) @_pure_meta - if a <: b + if isa(a, TypeVar) + return typejoin(a.ub, b) + elseif isa(b, TypeVar) + return typejoin(a, b.ub) + elseif a <: b return b elseif b <: a return a @@ -22,10 +26,6 @@ function typejoin(@nospecialize(a), @nospecialize(b)) return UnionAll(a.var, typejoin(a.body, b)) elseif isa(b, UnionAll) return UnionAll(b.var, typejoin(a, b.body)) - elseif isa(a, TypeVar) - return typejoin(a.ub, b) - elseif isa(b, TypeVar) - return typejoin(a, b.ub) elseif isa(a, Union) return typejoin(typejoin(a.a, a.b), b) elseif isa(b, Union) diff --git a/base/reduce.jl b/base/reduce.jl index caeadc55b4017..a1732a87f860f 100644 --- a/base/reduce.jl +++ b/base/reduce.jl @@ -180,7 +180,7 @@ mapreduce_impl(f, op, A::AbstractArray, ifirst::Integer, ilast::Integer) = mapreduce(f, op, itr; [init]) Apply function `f` to each element in `itr`, and then reduce the result using the binary -function `op`. If provided, `init` must be a neutral element for `op` that will be returne +function `op`. If provided, `init` must be a neutral element for `op` that will be returned for empty collections. It is unspecified whether `init` is used for non-empty collections. In general, it will be necessary to provide `init` to work with empty collections. diff --git a/base/reflection.jl b/base/reflection.jl index 25de43a3270dd..da407a73c82eb 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -1008,6 +1008,8 @@ end Determine whether the given generic function has a method matching the given `Tuple` of argument types with the upper bound of world age given by `world`. +See also [`applicable`](@ref). + # Examples ```jldoctest julia> hasmethod(length, Tuple{Array}) @@ -1101,7 +1103,7 @@ has_bottom_parameter(t::TypeVar) = t.ub == Bottom || has_bottom_parameter(t.ub) has_bottom_parameter(::Any) = false min_world(m::Method) = reinterpret(UInt, m.min_world) -max_world(m::Method) = typemax(UInt) +max_world(m::Method) = reinterpret(UInt, m.max_world) min_world(m::Core.MethodInstance) = reinterpret(UInt, m.min_world) max_world(m::Core.MethodInstance) = reinterpret(UInt, m.max_world) diff --git a/base/regex.jl b/base/regex.jl index 41745eddd2a21..384d6ca3dfa21 100644 --- a/base/regex.jl +++ b/base/regex.jl @@ -7,6 +7,16 @@ include("pcre.jl") const DEFAULT_COMPILER_OPTS = PCRE.UTF | PCRE.NO_UTF_CHECK | PCRE.ALT_BSUX | PCRE.UCP const DEFAULT_MATCH_OPTS = PCRE.NO_UTF_CHECK +""" + Regex(pattern[, flags]) + +A type representing a regular expression. `Regex` objects can be used to match strings +with [`match`](@ref). + +`Regex` objects can be created using the [`@r_str`](@ref) string macro. The +`Regex(pattern[, flags])` constructor is usually used if the `pattern` string needs +to be interpolated. See the documentation of the string macro for details on flags. +""" mutable struct Regex pattern::String compile_options::UInt32 @@ -81,6 +91,8 @@ listed after the ending quote, to change its behaviour: `\\s`, `\\W`, `\\w`, etc. match based on Unicode character properties. With this option, these sequences only match ASCII characters. +See `Regex` if interpolation is needed. + # Examples ```jldoctest julia> match(r"a+.*b+.*?d\$"ism, "Goodbye,\\nOh, angry,\\nBad world\\n") diff --git a/base/reinterpretarray.jl b/base/reinterpretarray.jl index 2cde44e9a386d..60b268465c14a 100644 --- a/base/reinterpretarray.jl +++ b/base/reinterpretarray.jl @@ -104,6 +104,8 @@ end _getindex_ra(a, inds[1], tail(inds)) end +@inline _memcpy!(dst, src, n) = ccall(:memcpy, Cvoid, (Ptr{UInt8}, Ptr{UInt8}, Csize_t), dst, src, n) + @inline @propagate_inbounds function _getindex_ra(a::ReinterpretArray{T,N,S}, i1::Int, tailinds::TT) where {T,N,S,TT} # Make sure to match the scalar reinterpret if that is applicable if sizeof(T) == sizeof(S) && (fieldcount(T) + fieldcount(S)) == 0 @@ -123,11 +125,9 @@ end # once it knows the data layout while nbytes_copied < sizeof(T) s[] = a.parent[ind_start + i, tailinds...] - while nbytes_copied < sizeof(T) && sidx < sizeof(S) - unsafe_store!(tptr, unsafe_load(sptr, sidx + 1), nbytes_copied + 1) - sidx += 1 - nbytes_copied += 1 - end + nb = min(sizeof(S) - sidx, sizeof(T)-nbytes_copied) + _memcpy!(tptr + nbytes_copied, sptr + sidx, nb) + nbytes_copied += nb sidx = 0 i += 1 end @@ -173,34 +173,26 @@ end # element from the original array and overwrite the relevant parts if sidx != 0 s[] = a.parent[ind_start + i, tailinds...] - while nbytes_copied < sizeof(T) && sidx < sizeof(S) - unsafe_store!(sptr, unsafe_load(tptr, nbytes_copied + 1), sidx + 1) - sidx += 1 - nbytes_copied += 1 - end + nb = min(sizeof(S) - sidx, sizeof(T)) + _memcpy!(sptr + sidx, tptr, nb) + nbytes_copied += nb a.parent[ind_start + i, tailinds...] = s[] i += 1 sidx = 0 end # Deal with the main body of elements while nbytes_copied < sizeof(T) && (sizeof(T) - nbytes_copied) > sizeof(S) - while nbytes_copied < sizeof(T) && sidx < sizeof(S) - unsafe_store!(sptr, unsafe_load(tptr, nbytes_copied + 1), sidx + 1) - sidx += 1 - nbytes_copied += 1 - end + nb = min(sizeof(S), sizeof(T) - nbytes_copied) + _memcpy!(sptr, tptr + nbytes_copied, nb) + nbytes_copied += nb a.parent[ind_start + i, tailinds...] = s[] i += 1 - sidx = 0 end # Deal with trailing partial elements if nbytes_copied < sizeof(T) s[] = a.parent[ind_start + i, tailinds...] - while nbytes_copied < sizeof(T) && sidx < sizeof(S) - unsafe_store!(sptr, unsafe_load(tptr, nbytes_copied + 1), sidx + 1) - sidx += 1 - nbytes_copied += 1 - end + nb = min(sizeof(S), sizeof(T) - nbytes_copied) + _memcpy!(sptr, tptr + nbytes_copied, nb) a.parent[ind_start + i, tailinds...] = s[] end end diff --git a/base/reshapedarray.jl b/base/reshapedarray.jl index ebd2efba68b36..18b2008f2c4d1 100644 --- a/base/reshapedarray.jl +++ b/base/reshapedarray.jl @@ -222,7 +222,7 @@ end I = ind2sub_rs(axes(A.parent), A.mi, i) _unsafe_getindex_rs(parent(A), I) end -_unsafe_getindex_rs(A, i::Integer) = (@inbounds ret = A[i]; ret) +@inline _unsafe_getindex_rs(A, i::Integer) = (@inbounds ret = A[i]; ret) @inline _unsafe_getindex_rs(A, I) = (@inbounds ret = A[I...]; ret) @inline function setindex!(A::ReshapedArrayLF, val, index::Int) diff --git a/base/set.jl b/base/set.jl index a3e9954d21477..ab64837aa9ce1 100644 --- a/base/set.jl +++ b/base/set.jl @@ -67,6 +67,26 @@ rehash!(s::Set) = (rehash!(s.dict); s) iterate(s::Set, i...) = iterate(KeySet(s.dict), i...) +# In case the size(s) is smaller than size(t) its more efficient to iterate through +# elements of s instead and only delete the ones also contained in t. +# The threshold for this decision boils down to a tradeoff between +# size(s) * cost(in() + delete!()) ≶ size(t) * cost(delete!()) +# Empirical observations on Ints point towards a threshold of 0.8. +# To be on the safe side (e.g. cost(in) >>> cost(delete!) ) a +# conservative threshold of 0.5 was chosen. +function setdiff!(s::Set, t::Set) + if 2 * length(s) < length(t) + for x in s + x in t && delete!(s, x) + end + else + for x in t + delete!(s, x) + end + end + return s +end + """ unique(itr) diff --git a/base/sort.jl b/base/sort.jl index c5cd7232e65c4..e04829a3191c4 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -370,6 +370,20 @@ struct InsertionSortAlg <: Algorithm end struct QuickSortAlg <: Algorithm end struct MergeSortAlg <: Algorithm end +""" + PartialQuickSort{T <: Union{Int,OrdinalRange}} + +Indicate that a sorting function should use the partial quick sort +algorithm. Partial quick sort returns the smallest `k` elements sorted from smallest +to largest, finding them and sorting them using [`QuickSort`](@ref). + +Characteristics: + * *not stable*: does not preserve the ordering of elements which + compare equal (e.g. "a" and "A" in a sort of letters which + ignores case). + * *in-place* in memory. + * *divide-and-conquer*: sort strategy similar to [`MergeSort`](@ref). +""" struct PartialQuickSort{T <: Union{Int,OrdinalRange}} <: Algorithm k::T end @@ -379,8 +393,54 @@ Base.last(a::PartialQuickSort{Int}) = a.k Base.first(a::PartialQuickSort) = first(a.k) Base.last(a::PartialQuickSort) = last(a.k) +""" + InsertionSort + +Indicate that a sorting function should use the insertion sort +algorithm. Insertion sort traverses the collection one element +at a time, inserting each element into its correct, sorted position in +the output list. + +Characteristics: + * *stable*: preserves the ordering of elements which + compare equal (e.g. "a" and "A" in a sort of letters + which ignores case). + * *in-place* in memory. + * *quadratic performance* in the number of elements to be sorted: + it is well-suited to small collections but should not be used for large ones. +""" const InsertionSort = InsertionSortAlg() +""" + QuickSort + +Indicate that a sorting function should use the quick sort +algorithm, which is *not* stable. + +Characteristics: + * *not stable*: does not preserve the ordering of elements which + compare equal (e.g. "a" and "A" in a sort of letters which + ignores case). + * *in-place* in memory. + * *divide-and-conquer*: sort strategy similar to [`MergeSort`](@ref). + * *good performance* for large collections. +""" const QuickSort = QuickSortAlg() +""" + MergeSort + +Indicate that a sorting function should use the merge sort +algorithm. Merge sort divides the collection into +subcollections and repeatedly merges them, sorting each +subcollection at each step, until the entire +collection has been recombined in sorted form. + +Characteristics: + * *stable*: preserves the ordering of elements which compare + equal (e.g. "a" and "A" in a sort of letters which ignores + case). + * *not in-place* in memory. + * *divide-and-conquer* sort strategy. +""" const MergeSort = MergeSortAlg() const DEFAULT_UNSTABLE = QuickSort diff --git a/base/special/log.jl b/base/special/log.jl index 7e52c39c2a5d9..4b6429adf7c2d 100644 --- a/base/special/log.jl +++ b/base/special/log.jl @@ -282,7 +282,7 @@ function log(x::Float64) elseif isnan(x) NaN else - throw_complex_domainerror(x, :log) + throw_complex_domainerror(:log, x) end end @@ -318,7 +318,7 @@ function log(x::Float32) elseif isnan(x) NaN32 else - throw_complex_domainerror(x, :log) + throw_complex_domainerror(:log, x) end end @@ -353,7 +353,7 @@ function log1p(x::Float64) elseif isnan(x) NaN else - throw_complex_domainerror(x, :log1p) + throw_complex_domainerror(:log1p, x) end end @@ -386,7 +386,7 @@ function log1p(x::Float32) elseif isnan(x) NaN32 else - throw_complex_domainerror(x, :log1p) + throw_complex_domainerror(:log1p, x) end end diff --git a/base/special/rem_pio2.jl b/base/special/rem_pio2.jl index 67cb067955f7a..c8d1018cd7881 100644 --- a/base/special/rem_pio2.jl +++ b/base/special/rem_pio2.jl @@ -208,7 +208,7 @@ function paynehanek(x::Float64) end """ - rem_pio2_kernel(x, xhp) + rem_pio2_kernel(x) Return the remainder of `x` modulo π/2 as a double-double pair, along with a `k` such that ``k \\mod 3 == K \\mod 3`` where ``K*π/2 = x - rem``. Note, that it is diff --git a/base/strings/io.jl b/base/strings/io.jl index bd82fd37494b3..e2a759c473189 100644 --- a/base/strings/io.jl +++ b/base/strings/io.jl @@ -11,7 +11,7 @@ of values `xs` if there is one, otherwise call [`show`](@ref). The representation used by `print` includes minimal formatting and tries to avoid Julia-specific details. -Printing `nothing` is deprecated and will throw an error in the future. +Printing `nothing` is not allowed and throws an error. # Examples ```jldoctest @@ -81,7 +81,7 @@ of the buffer (in bytes). The optional keyword argument `context` can be set to `:key=>value` pair or an `IO` or [`IOContext`](@ref) object whose attributes are used for the I/O -stream passed to `f`. The optional `sizehint` is a suggersted (in bytes) +stream passed to `f`. The optional `sizehint` is a suggested size (in bytes) to allocate for the buffer used to write the string. # Examples @@ -103,36 +103,48 @@ function sprint(f::Function, args...; context=nothing, sizehint::Integer=0) String(resize!(s.data, s.size)) end -tostr_sizehint(x) = 0 +tostr_sizehint(x) = 8 tostr_sizehint(x::AbstractString) = lastindex(x) tostr_sizehint(x::Float64) = 20 tostr_sizehint(x::Float32) = 12 -function print_to_string(xs...; env=nothing) +function print_to_string(xs...) if isempty(xs) return "" end + siz = 0 + for x in xs + siz += tostr_sizehint(x) + end # specialized for performance reasons - s = IOBuffer(sizehint=tostr_sizehint(xs[1])) - if env !== nothing - env_io = IOContext(s, env) - for x in xs - print(env_io, x) - end - else - for x in xs - print(s, x) - end + s = IOBuffer(sizehint=siz) + for x in xs + print(s, x) end String(resize!(s.data, s.size)) end -string_with_env(env, xs...) = print_to_string(xs...; env=env) +function string_with_env(env, xs...) + if isempty(xs) + return "" + end + siz = 0 + for x in xs + siz += tostr_sizehint(x) + end + # specialized for performance reasons + s = IOBuffer(sizehint=siz) + env_io = IOContext(s, env) + for x in xs + print(env_io, x) + end + String(resize!(s.data, s.size)) +end """ string(xs...) -Create a string from any values using the [`print`](@ref) function. +Create a string from any values, except `nothing`, using the [`print`](@ref) function. # Examples ```jldoctest @@ -223,7 +235,7 @@ IOBuffer(s::SubString{String}) = IOBuffer(view(unsafe_wrap(Vector{UInt8}, s.stri Join an array of `strings` into a single string, inserting the given delimiter between adjacent strings. If `last` is given, it will be used instead of `delim` between the last two strings. If `io` is given, the result is written to `io` rather than returned as -as a `String`. For example, +as a `String`. # Examples ```jldoctest diff --git a/base/strings/substring.jl b/base/strings/substring.jl index d6fba1b41c9bf..9b9e0d1e7424b 100644 --- a/base/strings/substring.jl +++ b/base/strings/substring.jl @@ -142,19 +142,32 @@ function reverse(s::Union{String,SubString{String}})::String end end -function string(a::Union{String, SubString{String}}...) - if length(a) == 1 - return String(a[1]) - end +string(a::String) = String(a) +string(a::SubString{String}) = String(a) + +function string(a::Union{Char, String, SubString{String}}...) n = 0 - for str in a - n += sizeof(str) + for v in a + if v isa Char + n += codelen(v) + else + n += sizeof(v) + end end out = _string_n(n) offs = 1 - for str in a - unsafe_copyto!(pointer(out,offs), pointer(str), sizeof(str)) - offs += sizeof(str) + for v in a + if v isa Char + x = bswap(reinterpret(UInt32, v)) + for j in 1:codelen(v) + unsafe_store!(pointer(out, offs), x % UInt8) + offs += 1 + x >>= 8 + end + else + unsafe_copyto!(pointer(out,offs), pointer(v), sizeof(v)) + offs += sizeof(v) + end end return out end diff --git a/base/subarray.jl b/base/subarray.jl index b74290ba26e1e..ff640b3e73db4 100644 --- a/base/subarray.jl +++ b/base/subarray.jl @@ -66,7 +66,20 @@ parentindices(V::SubArray) = V.indices """ parentindices(A) -From an array view `A`, returns the corresponding indices in the parent. +Return the indices in the [`parent`](@ref) which correspond to the array view `A`. + +# Examples +```jldoctest +julia> A = [1 2; 3 4]; + +julia> V = view(A, 1, :) +2-element view(::Array{Int64,2}, 1, :) with eltype Int64: + 1 + 2 + +julia> parentindices(V) +(1, Base.Slice(Base.OneTo(2))) +``` """ parentindices(a::AbstractArray) = map(OneTo, size(a)) diff --git a/base/tuple.jl b/base/tuple.jl index 987b6764f1401..a1889b79b6faa 100644 --- a/base/tuple.jl +++ b/base/tuple.jl @@ -38,7 +38,7 @@ _setindex(v, i::Integer) = () ## iterating ## -iterate(t::Tuple, i::Int=1) = length(t) < i ? nothing : (t[i], i+1) +iterate(t::Tuple, i::Int=1) = 1 <= i <= length(t) ? (@inbounds t[i], i+1) : nothing keys(@nospecialize t::Tuple) = OneTo(length(t)) @@ -57,7 +57,7 @@ end # this allows partial evaluation of bounded sequences of next() calls on tuples, # while reducing to plain next() for arbitrary iterables. -indexed_iterate(t::Tuple, i::Int, state=1) = (@_inline_meta; (t[i], i+1)) +indexed_iterate(t::Tuple, i::Int, state=1) = (@_inline_meta; (getfield(t, i), i+1)) indexed_iterate(a::Array, i::Int, state=1) = (@_inline_meta; (a[i], i+1)) function indexed_iterate(I, i) x = iterate(I) diff --git a/base/twiceprecision.jl b/base/twiceprecision.jl index cf61823ecc0f6..c2be8f53dfa24 100644 --- a/base/twiceprecision.jl +++ b/base/twiceprecision.jl @@ -332,11 +332,14 @@ 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]) +# Defined to prevent splatting in the function below which here has a performance impact +_TP(x) = TwicePrecision{Float64}(x) +_TP(x::Tuple{Any, Any}) = TwicePrecision{Float64}(x[1], 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) + StepRangeLen(_TP(ref), + twiceprecision(_TP(step), nb), Int(len), offset) end function steprangelen_hp(::Type{T}, ref::F_or_FF, @@ -354,7 +357,7 @@ StepRangeLen(ref::TwicePrecision{T}, step::TwicePrecision{T}, # 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 + if len < 2 || step_n == 0 return steprangelen_hp(T, (start_n, den), (step_n, den), 0, Int(len), 1) end # index of smallest-magnitude value diff --git a/base/util.jl b/base/util.jl index e3423491aa84d..80c790770af74 100644 --- a/base/util.jl +++ b/base/util.jl @@ -476,7 +476,7 @@ function getpass(input::TTY, output::IO, prompt::AbstractString) write(s, c) end end - return s + return seekstart(s) end else function getpass(input::TTY, output::IO, prompt::AbstractString) diff --git a/base/uuid.jl b/base/uuid.jl index cf422724eb928..75bf21d4697cb 100644 --- a/base/uuid.jl +++ b/base/uuid.jl @@ -1,5 +1,10 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license +""" + Represents a Universally Unique Identifier (UUID). + Can be built from one `UInt128` (all byte values), two `UInt64`, or four `UInt32`. + Conversion from a string will check the UUID validity. +""" struct UUID value::UInt128 end @@ -8,18 +13,18 @@ UUID(u::NTuple{4, UInt32}) = UUID((UInt128(u[1]) << 96) | (UInt128(u[2]) << 64) (UInt128(u[3]) << 32) | UInt128(u[4])) function convert(::Type{NTuple{2, UInt64}}, uuid::UUID) - uuid = uuid.value - hi = UInt64((uuid >> 64) & 0xffffffffffffffff) - lo = UInt64(uuid & 0xffffffffffffffff) + bytes = uuid.value + hi = UInt64((bytes >> 64) & 0xffffffffffffffff) + lo = UInt64(bytes & 0xffffffffffffffff) return (hi, lo) end function convert(::Type{NTuple{4, UInt32}}, uuid::UUID) - uuid = uuid.value - hh = UInt32((uuid >> 96) & 0xffffffff) - hl = UInt32((uuid >> 64) & 0xffffffff) - lh = UInt32((uuid >> 32) & 0xffffffff) - ll = UInt32(uuid & 0xffffffff) + bytes = uuid.value + hh = UInt32((bytes >> 96) & 0xffffffff) + hl = UInt32((bytes >> 64) & 0xffffffff) + lh = UInt32((bytes >> 32) & 0xffffffff) + ll = UInt32(bytes & 0xffffffff) return (hh, hl, lh, ll) end diff --git a/deps/llvm.mk b/deps/llvm.mk index 4f350aa443b25..f3ebb3a1f3454 100644 --- a/deps/llvm.mk +++ b/deps/llvm.mk @@ -507,6 +507,7 @@ $(eval $(call LLVM_PATCH,llvm-rL323946-LSRTy)) # Remove for 7.0 $(eval $(call LLVM_PATCH,llvm-D50010-VNCoercion-ni)) $(eval $(call LLVM_PATCH,llvm-D50167-scev-umin)) $(eval $(call LLVM_PATCH,llvm-windows-race)) +$(eval $(call LLVM_PATCH,llvm-rL326967-aligned-load)) # remove for 7.0 endif # LLVM_VER # Remove hardcoded OS X requirements in compilter-rt cmake build diff --git a/deps/patches/llvm-rL326967-aligned-load.patch b/deps/patches/llvm-rL326967-aligned-load.patch new file mode 100644 index 0000000000000..62c112306a292 --- /dev/null +++ b/deps/patches/llvm-rL326967-aligned-load.patch @@ -0,0 +1,301 @@ +commit b398d8e1fa5a5a914957fa22d0a64db97f6c265e +Author: Craig Topper +Date: Thu Mar 8 00:21:17 2018 +0000 + + [X86] Fix some isel patterns that used aligned vector load instructions with unaligned predicates. + + These patterns weren't checking the alignment of the load, but were using the aligned instructions. This will cause a GP fault if the data isn't aligned. + + I believe these were introduced in r312450. + + git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@326967 91177308-0d34-0410-b5e6-96231b3b80d8 + +diff --git a/lib/Target/X86/X86InstrVecCompiler.td b/lib/Target/X86/X86InstrVecCompiler.td +index db3dfe56531..50c7763a2c3 100644 +--- a/lib/Target/X86/X86InstrVecCompiler.td ++++ b/lib/Target/X86/X86InstrVecCompiler.td +@@ -261,10 +261,10 @@ let Predicates = [HasVLX] in { + // will zero the upper bits. + // TODO: Is there a safe way to detect whether the producing instruction + // already zeroed the upper bits? +-multiclass subvector_zero_lowering { ++multiclass subvector_zero_lowering { + def : Pat<(DstTy (insert_subvector (bitconvert (ZeroTy immAllZerosV)), + (SrcTy RC:$src), (iPTR 0))), + (SUBREG_TO_REG (i64 0), +@@ -274,91 +274,91 @@ multiclass subvector_zero_lowering("VMOV"#MoveStr#"rm") addr:$src), SubIdx)>; ++ (!cast("VMOV"#LoadStr#"rm") addr:$src), SubIdx)>; + } + + let Predicates = [HasAVX, NoVLX] in { +- defm : subvector_zero_lowering<"APD", VR128, v4f64, v2f64, v8i32, loadv2f64, +- sub_xmm>; +- defm : subvector_zero_lowering<"APS", VR128, v8f32, v4f32, v8i32, loadv4f32, +- sub_xmm>; +- defm : subvector_zero_lowering<"DQA", VR128, v4i64, v2i64, v8i32, loadv2i64, +- sub_xmm>; +- defm : subvector_zero_lowering<"DQA", VR128, v8i32, v4i32, v8i32, loadv2i64, +- sub_xmm>; +- defm : subvector_zero_lowering<"DQA", VR128, v16i16, v8i16, v8i32, loadv2i64, +- sub_xmm>; +- defm : subvector_zero_lowering<"DQA", VR128, v32i8, v16i8, v8i32, loadv2i64, +- sub_xmm>; +-} +- +-let Predicates = [HasVLX] in { +- defm : subvector_zero_lowering<"APDZ128", VR128X, v4f64, v2f64, v8i32, ++ defm : subvector_zero_lowering<"APD", "UPD", VR128, v4f64, v2f64, v8i32, + loadv2f64, sub_xmm>; +- defm : subvector_zero_lowering<"APSZ128", VR128X, v8f32, v4f32, v8i32, ++ defm : subvector_zero_lowering<"APS", "UPS", VR128, v8f32, v4f32, v8i32, + loadv4f32, sub_xmm>; +- defm : subvector_zero_lowering<"DQA64Z128", VR128X, v4i64, v2i64, v8i32, ++ defm : subvector_zero_lowering<"DQA", "DQU", VR128, v4i64, v2i64, v8i32, + loadv2i64, sub_xmm>; +- defm : subvector_zero_lowering<"DQA64Z128", VR128X, v8i32, v4i32, v8i32, ++ defm : subvector_zero_lowering<"DQA", "DQU", VR128, v8i32, v4i32, v8i32, + loadv2i64, sub_xmm>; +- defm : subvector_zero_lowering<"DQA64Z128", VR128X, v16i16, v8i16, v8i32, ++ defm : subvector_zero_lowering<"DQA", "DQU", VR128, v16i16, v8i16, v8i32, + loadv2i64, sub_xmm>; +- defm : subvector_zero_lowering<"DQA64Z128", VR128X, v32i8, v16i8, v8i32, +- loadv2i64, sub_xmm>; +- +- defm : subvector_zero_lowering<"APDZ128", VR128X, v8f64, v2f64, v16i32, +- loadv2f64, sub_xmm>; +- defm : subvector_zero_lowering<"APSZ128", VR128X, v16f32, v4f32, v16i32, +- loadv4f32, sub_xmm>; +- defm : subvector_zero_lowering<"DQA64Z128", VR128X, v8i64, v2i64, v16i32, +- loadv2i64, sub_xmm>; +- defm : subvector_zero_lowering<"DQA64Z128", VR128X, v16i32, v4i32, v16i32, +- loadv2i64, sub_xmm>; +- defm : subvector_zero_lowering<"DQA64Z128", VR128X, v32i16, v8i16, v16i32, +- loadv2i64, sub_xmm>; +- defm : subvector_zero_lowering<"DQA64Z128", VR128X, v64i8, v16i8, v16i32, ++ defm : subvector_zero_lowering<"DQA", "DQU", VR128, v32i8, v16i8, v8i32, + loadv2i64, sub_xmm>; ++} + +- defm : subvector_zero_lowering<"APDZ256", VR256X, v8f64, v4f64, v16i32, +- loadv4f64, sub_ymm>; +- defm : subvector_zero_lowering<"APSZ256", VR256X, v16f32, v8f32, v16i32, +- loadv8f32, sub_ymm>; +- defm : subvector_zero_lowering<"DQA64Z256", VR256X, v8i64, v4i64, v16i32, +- loadv4i64, sub_ymm>; +- defm : subvector_zero_lowering<"DQA64Z256", VR256X, v16i32, v8i32, v16i32, +- loadv4i64, sub_ymm>; +- defm : subvector_zero_lowering<"DQA64Z256", VR256X, v32i16, v16i16, v16i32, +- loadv4i64, sub_ymm>; +- defm : subvector_zero_lowering<"DQA64Z256", VR256X, v64i8, v32i8, v16i32, +- loadv4i64, sub_ymm>; ++let Predicates = [HasVLX] in { ++ defm : subvector_zero_lowering<"APDZ128", "UPDZ128", VR128X, v4f64, ++ v2f64, v8i32, loadv2f64, sub_xmm>; ++ defm : subvector_zero_lowering<"APSZ128", "UPSZ128", VR128X, v8f32, ++ v4f32, v8i32, loadv4f32, sub_xmm>; ++ defm : subvector_zero_lowering<"DQA64Z128", "DQU64Z128", VR128X, v4i64, ++ v2i64, v8i32, loadv2i64, sub_xmm>; ++ defm : subvector_zero_lowering<"DQA64Z128", "DQU64Z128", VR128X, v8i32, ++ v4i32, v8i32, loadv2i64, sub_xmm>; ++ defm : subvector_zero_lowering<"DQA64Z128", "DQU64Z128", VR128X, v16i16, ++ v8i16, v8i32, loadv2i64, sub_xmm>; ++ defm : subvector_zero_lowering<"DQA64Z128", "DQU64Z128", VR128X, v32i8, ++ v16i8, v8i32, loadv2i64, sub_xmm>; ++ ++ defm : subvector_zero_lowering<"APDZ128", "UPDZ128", VR128X, v8f64, ++ v2f64, v16i32, loadv2f64, sub_xmm>; ++ defm : subvector_zero_lowering<"APSZ128", "UPSZ128", VR128X, v16f32, ++ v4f32, v16i32, loadv4f32, sub_xmm>; ++ defm : subvector_zero_lowering<"DQA64Z128", "DQU64Z128", VR128X, v8i64, ++ v2i64, v16i32, loadv2i64, sub_xmm>; ++ defm : subvector_zero_lowering<"DQA64Z128", "DQU64Z128", VR128X, v16i32, ++ v4i32, v16i32, loadv2i64, sub_xmm>; ++ defm : subvector_zero_lowering<"DQA64Z128", "DQU64Z128", VR128X, v32i16, ++ v8i16, v16i32, loadv2i64, sub_xmm>; ++ defm : subvector_zero_lowering<"DQA64Z128", "DQU64Z128", VR128X, v64i8, ++ v16i8, v16i32, loadv2i64, sub_xmm>; ++ ++ defm : subvector_zero_lowering<"APDZ256", "UPDZ256", VR256X, v8f64, ++ v4f64, v16i32, loadv4f64, sub_ymm>; ++ defm : subvector_zero_lowering<"APSZ256", "UPDZ256", VR256X, v16f32, ++ v8f32, v16i32, loadv8f32, sub_ymm>; ++ defm : subvector_zero_lowering<"DQA64Z256", "DQU64Z256", VR256X, v8i64, ++ v4i64, v16i32, loadv4i64, sub_ymm>; ++ defm : subvector_zero_lowering<"DQA64Z256", "DQU64Z256", VR256X, v16i32, ++ v8i32, v16i32, loadv4i64, sub_ymm>; ++ defm : subvector_zero_lowering<"DQA64Z256", "DQU64Z256", VR256X, v32i16, ++ v16i16, v16i32, loadv4i64, sub_ymm>; ++ defm : subvector_zero_lowering<"DQA64Z256", "DQU64Z256", VR256X, v64i8, ++ v32i8, v16i32, loadv4i64, sub_ymm>; + } + + let Predicates = [HasAVX512, NoVLX] in { +- defm : subvector_zero_lowering<"APD", VR128, v8f64, v2f64, v16i32, loadv2f64, +- sub_xmm>; +- defm : subvector_zero_lowering<"APS", VR128, v16f32, v4f32, v16i32, loadv4f32, +- sub_xmm>; +- defm : subvector_zero_lowering<"DQA", VR128, v8i64, v2i64, v16i32, loadv2i64, +- sub_xmm>; +- defm : subvector_zero_lowering<"DQA", VR128, v16i32, v4i32, v16i32, loadv2i64, +- sub_xmm>; +- defm : subvector_zero_lowering<"DQA", VR128, v32i16, v8i16, v16i32, loadv2i64, +- sub_xmm>; +- defm : subvector_zero_lowering<"DQA", VR128, v64i8, v16i8, v16i32, loadv2i64, +- sub_xmm>; +- +- defm : subvector_zero_lowering<"APDY", VR256, v8f64, v4f64, v16i32, +- loadv4f64, sub_ymm>; +- defm : subvector_zero_lowering<"APSY", VR256, v16f32, v8f32, v16i32, +- loadv8f32, sub_ymm>; +- defm : subvector_zero_lowering<"DQAY", VR256, v8i64, v4i64, v16i32, +- loadv4i64, sub_ymm>; +- defm : subvector_zero_lowering<"DQAY", VR256, v16i32, v8i32, v16i32, +- loadv4i64, sub_ymm>; +- defm : subvector_zero_lowering<"DQAY", VR256, v32i16, v16i16, v16i32, +- loadv4i64, sub_ymm>; +- defm : subvector_zero_lowering<"DQAY", VR256, v64i8, v32i8, v16i32, +- loadv4i64, sub_ymm>; ++ defm : subvector_zero_lowering<"APD", "UPD", VR128, v8f64, v2f64, ++ v16i32,loadv2f64, sub_xmm>; ++ defm : subvector_zero_lowering<"APS", "UPS", VR128, v16f32, v4f32, ++ v16i32, loadv4f32, sub_xmm>; ++ defm : subvector_zero_lowering<"DQA", "DQU", VR128, v8i64, v2i64, ++ v16i32, loadv2i64, sub_xmm>; ++ defm : subvector_zero_lowering<"DQA", "DQU", VR128, v16i32, v4i32, ++ v16i32, loadv2i64, sub_xmm>; ++ defm : subvector_zero_lowering<"DQA", "DQU", VR128, v32i16, v8i16, ++ v16i32, loadv2i64, sub_xmm>; ++ defm : subvector_zero_lowering<"DQA", "DQU", VR128, v64i8, v16i8, ++ v16i32, loadv2i64, sub_xmm>; ++ ++ defm : subvector_zero_lowering<"APDY", "UPDY", VR256, v8f64, v4f64, ++ v16i32, loadv4f64, sub_ymm>; ++ defm : subvector_zero_lowering<"APSY", "UPSY", VR256, v16f32, v8f32, ++ v16i32, loadv8f32, sub_ymm>; ++ defm : subvector_zero_lowering<"DQAY", "DQUY", VR256, v8i64, v4i64, ++ v16i32, loadv4i64, sub_ymm>; ++ defm : subvector_zero_lowering<"DQAY", "DQUY", VR256, v16i32, v8i32, ++ v16i32, loadv4i64, sub_ymm>; ++ defm : subvector_zero_lowering<"DQAY", "DQUY", VR256, v32i16, v16i16, ++ v16i32, loadv4i64, sub_ymm>; ++ defm : subvector_zero_lowering<"DQAY", "DQUY", VR256, v64i8, v32i8, ++ v16i32, loadv4i64, sub_ymm>; + } + + // List of opcodes that guaranteed to zero the upper elements of vector regs. +diff --git a/test/CodeGen/X86/merge-consecutive-loads-256.ll b/test/CodeGen/X86/merge-consecutive-loads-256.ll +index 6ecd8116443..0f2cf594b1c 100644 +--- a/test/CodeGen/X86/merge-consecutive-loads-256.ll ++++ b/test/CodeGen/X86/merge-consecutive-loads-256.ll +@@ -28,13 +28,13 @@ define <4 x double> @merge_4f64_2f64_23(<2 x double>* %ptr) nounwind uwtable noi + define <4 x double> @merge_4f64_2f64_2z(<2 x double>* %ptr) nounwind uwtable noinline ssp { + ; AVX-LABEL: merge_4f64_2f64_2z: + ; AVX: # %bb.0: +-; AVX-NEXT: vmovaps 32(%rdi), %xmm0 ++; AVX-NEXT: vmovups 32(%rdi), %xmm0 + ; AVX-NEXT: retq + ; + ; X32-AVX-LABEL: merge_4f64_2f64_2z: + ; X32-AVX: # %bb.0: + ; X32-AVX-NEXT: movl {{[0-9]+}}(%esp), %eax +-; X32-AVX-NEXT: vmovaps 32(%eax), %xmm0 ++; X32-AVX-NEXT: vmovups 32(%eax), %xmm0 + ; X32-AVX-NEXT: retl + %ptr0 = getelementptr inbounds <2 x double>, <2 x double>* %ptr, i64 2 + %val0 = load <2 x double>, <2 x double>* %ptr0 +@@ -109,13 +109,13 @@ define <4 x double> @merge_4f64_f64_34uu(double* %ptr) nounwind uwtable noinline + define <4 x double> @merge_4f64_f64_45zz(double* %ptr) nounwind uwtable noinline ssp { + ; AVX-LABEL: merge_4f64_f64_45zz: + ; AVX: # %bb.0: +-; AVX-NEXT: vmovaps 32(%rdi), %xmm0 ++; AVX-NEXT: vmovups 32(%rdi), %xmm0 + ; AVX-NEXT: retq + ; + ; X32-AVX-LABEL: merge_4f64_f64_45zz: + ; X32-AVX: # %bb.0: + ; X32-AVX-NEXT: movl {{[0-9]+}}(%esp), %eax +-; X32-AVX-NEXT: vmovaps 32(%eax), %xmm0 ++; X32-AVX-NEXT: vmovups 32(%eax), %xmm0 + ; X32-AVX-NEXT: retl + %ptr0 = getelementptr inbounds double, double* %ptr, i64 4 + %ptr1 = getelementptr inbounds double, double* %ptr, i64 5 +@@ -155,13 +155,13 @@ define <4 x double> @merge_4f64_f64_34z6(double* %ptr) nounwind uwtable noinline + define <4 x i64> @merge_4i64_2i64_3z(<2 x i64>* %ptr) nounwind uwtable noinline ssp { + ; AVX-LABEL: merge_4i64_2i64_3z: + ; AVX: # %bb.0: +-; AVX-NEXT: vmovaps 48(%rdi), %xmm0 ++; AVX-NEXT: vmovups 48(%rdi), %xmm0 + ; AVX-NEXT: retq + ; + ; X32-AVX-LABEL: merge_4i64_2i64_3z: + ; X32-AVX: # %bb.0: + ; X32-AVX-NEXT: movl {{[0-9]+}}(%esp), %eax +-; X32-AVX-NEXT: vmovaps 48(%eax), %xmm0 ++; X32-AVX-NEXT: vmovups 48(%eax), %xmm0 + ; X32-AVX-NEXT: retl + %ptr0 = getelementptr inbounds <2 x i64>, <2 x i64>* %ptr, i64 3 + %val0 = load <2 x i64>, <2 x i64>* %ptr0 +@@ -217,13 +217,13 @@ define <4 x i64> @merge_4i64_i64_1zzu(i64* %ptr) nounwind uwtable noinline ssp { + define <4 x i64> @merge_4i64_i64_23zz(i64* %ptr) nounwind uwtable noinline ssp { + ; AVX-LABEL: merge_4i64_i64_23zz: + ; AVX: # %bb.0: +-; AVX-NEXT: vmovaps 16(%rdi), %xmm0 ++; AVX-NEXT: vmovups 16(%rdi), %xmm0 + ; AVX-NEXT: retq + ; + ; X32-AVX-LABEL: merge_4i64_i64_23zz: + ; X32-AVX: # %bb.0: + ; X32-AVX-NEXT: movl {{[0-9]+}}(%esp), %eax +-; X32-AVX-NEXT: vmovaps 16(%eax), %xmm0 ++; X32-AVX-NEXT: vmovups 16(%eax), %xmm0 + ; X32-AVX-NEXT: retl + %ptr0 = getelementptr inbounds i64, i64* %ptr, i64 2 + %ptr1 = getelementptr inbounds i64, i64* %ptr, i64 3 +diff --git a/test/CodeGen/X86/merge-consecutive-loads-512.ll b/test/CodeGen/X86/merge-consecutive-loads-512.ll +index 62102eb382c..3c6eaf65292 100644 +--- a/test/CodeGen/X86/merge-consecutive-loads-512.ll ++++ b/test/CodeGen/X86/merge-consecutive-loads-512.ll +@@ -106,13 +106,13 @@ define <8 x double> @merge_8f64_f64_23uuuuu9(double* %ptr) nounwind uwtable noin + define <8 x double> @merge_8f64_f64_12zzuuzz(double* %ptr) nounwind uwtable noinline ssp { + ; ALL-LABEL: merge_8f64_f64_12zzuuzz: + ; ALL: # %bb.0: +-; ALL-NEXT: vmovaps 8(%rdi), %xmm0 ++; ALL-NEXT: vmovups 8(%rdi), %xmm0 + ; ALL-NEXT: retq + ; + ; X32-AVX512F-LABEL: merge_8f64_f64_12zzuuzz: + ; X32-AVX512F: # %bb.0: + ; X32-AVX512F-NEXT: movl {{[0-9]+}}(%esp), %eax +-; X32-AVX512F-NEXT: vmovaps 8(%eax), %xmm0 ++; X32-AVX512F-NEXT: vmovups 8(%eax), %xmm0 + ; X32-AVX512F-NEXT: retl + %ptr0 = getelementptr inbounds double, double* %ptr, i64 1 + %ptr1 = getelementptr inbounds double, double* %ptr, i64 2 +@@ -190,7 +190,7 @@ define <8 x i64> @merge_8i64_4i64_z3(<4 x i64>* %ptr) nounwind uwtable noinline + define <8 x i64> @merge_8i64_i64_56zz9uzz(i64* %ptr) nounwind uwtable noinline ssp { + ; ALL-LABEL: merge_8i64_i64_56zz9uzz: + ; ALL: # %bb.0: +-; ALL-NEXT: vmovaps 40(%rdi), %xmm0 ++; ALL-NEXT: vmovups 40(%rdi), %xmm0 + ; ALL-NEXT: vmovsd {{.*#+}} xmm1 = mem[0],zero + ; ALL-NEXT: vinsertf64x4 $1, %ymm1, %zmm0, %zmm0 + ; ALL-NEXT: retq +@@ -198,7 +198,7 @@ define <8 x i64> @merge_8i64_i64_56zz9uzz(i64* %ptr) nounwind uwtable noinline s + ; X32-AVX512F-LABEL: merge_8i64_i64_56zz9uzz: + ; X32-AVX512F: # %bb.0: + ; X32-AVX512F-NEXT: movl {{[0-9]+}}(%esp), %eax +-; X32-AVX512F-NEXT: vmovaps 40(%eax), %xmm0 ++; X32-AVX512F-NEXT: vmovups 40(%eax), %xmm0 + ; X32-AVX512F-NEXT: vmovsd {{.*#+}} xmm1 = mem[0],zero + ; X32-AVX512F-NEXT: vinsertf64x4 $1, %ymm1, %zmm0, %zmm0 + ; X32-AVX512F-NEXT: retl diff --git a/doc/make.jl b/doc/make.jl index b2b3107949103..e4d849ec167bf 100644 --- a/doc/make.jl +++ b/doc/make.jl @@ -40,16 +40,16 @@ cd(joinpath(@__DIR__, "src")) do end end -# Generate a suitable markdown file from NEWS.md and put it in src -str = read(joinpath(@__DIR__, "..", "NEWS.md"), String) -splitted = split(str, "") -@assert length(splitted) == 2 -replaced_links = replace(splitted[1], r"\[\#([0-9]*?)\]" => s"[#\g<1>](https://github.com/JuliaLang/julia/issues/\g<1>)") -write(joinpath(@__DIR__, "src", "NEWS.md"), replaced_links) +# # Generate a suitable markdown file from NEWS.md and put it in src +# str = read(joinpath(@__DIR__, "..", "NEWS.md"), String) +# splitted = split(str, "") +# @assert length(splitted) == 2 +# replaced_links = replace(splitted[1], r"\[\#([0-9]*?)\]" => s"[#\g<1>](https://github.com/JuliaLang/julia/issues/\g<1>)") +# write(joinpath(@__DIR__, "src", "NEWS.md"), replaced_links) const PAGES = [ "Home" => "index.md", - hide("NEWS.md"), + # hide("NEWS.md"), "Manual" => [ "manual/getting-started.md", "manual/variables.md", @@ -162,10 +162,40 @@ makedocs( analytics = "UA-28835595-6", pages = PAGES, html_prettyurls = ("deploy" in ARGS), - html_canonical = ("deploy" in ARGS) ? "https://docs.julialang.org/en/stable/" : nothing, + html_canonical = ("deploy" in ARGS) ? "https://docs.julialang.org/en/v1/" : nothing, assets = ["assets/julia-manual.css", ] ) +# This overloads the function in Documenter that generates versions.js, to include +# v1/ in the version selector, instead of stable/. +# +# The function is identical to the version found in Documenter v0.19.6, except that +# it includes "v1" instead of "stable". +# +# Original: +# https://github.com/JuliaDocs/Documenter.jl/blob/v0.19.6/src/Writers/HTMLWriter.jl#L481-L506 +# +import Documenter.Writers.HTMLWriter: generate_version_file +function generate_version_file(dir::AbstractString) + named_folders = ["v1", "latest"] + tag_folders = [] + for each in readdir(dir) + each == "v1" && continue # skip the v1 symlink + occursin(Base.VERSION_REGEX, each) && push!(tag_folders, each) + end + # sort tags by version number + sort!(tag_folders, lt = (x, y) -> VersionNumber(x) < VersionNumber(y), rev = true) + open(joinpath(dir, "versions.js"), "w") do buf + println(buf, "var DOC_VERSIONS = [") + for group in (named_folders, tag_folders) + for folder in group + println(buf, " \"", folder, "\",") + end + end + println(buf, "];") + end +end + # Only deploy docs from 64bit Linux to avoid committing multiple versions of the same # docs from different workers. if "deploy" in ARGS && Sys.ARCH === :x86_64 && Sys.KERNEL === :Linux diff --git a/doc/src/base/arrays.md b/doc/src/base/arrays.md index 521cbc2d3dea8..260649a3a36cf 100644 --- a/doc/src/base/arrays.md +++ b/doc/src/base/arrays.md @@ -53,6 +53,8 @@ Base.axes(::AbstractArray, ::Any) Base.length(::AbstractArray) Base.eachindex Base.IndexStyle +Base.IndexLinear +Base.IndexCartesian Base.conj! Base.stride Base.strides diff --git a/doc/src/base/numbers.md b/doc/src/base/numbers.md index 190584bb0f8f4..50e6d2b7134da 100644 --- a/doc/src/base/numbers.md +++ b/doc/src/base/numbers.md @@ -89,8 +89,6 @@ Base.isinteger Base.isreal Core.Float32(::Any) Core.Float64(::Any) -Base.GMP.BigInt(::Any) -Base.MPFR.BigFloat(::Any) Base.Rounding.rounding Base.Rounding.setrounding(::Type, ::Any) Base.Rounding.setrounding(::Function, ::Type, ::RoundingMode) @@ -113,18 +111,22 @@ Base.@int128_str Base.@uint128_str ``` -## BigFloats +## BigFloats and BigInts -The [`BigFloat`](@ref) type implements arbitrary-precision floating-point arithmetic using -the [GNU MPFR library](http://www.mpfr.org/). +The [`BigFloat`](@ref) and [`BigInt`](@ref) types implements +arbitrary-precision floating point and integer arithmetic, respectively. For +[`BigFloat`](@ref) the [GNU MPFR library](http://www.mpfr.org/) is used, +and for [`BigInt`](@ref) the [GNU Multiple Precision Arithmetic Library (GMP)] +(https://gmplib.org) is used. ```@docs +Base.MPFR.BigFloat(::Any) Base.precision Base.MPFR.precision(::Type{BigFloat}) Base.MPFR.setprecision Base.MPFR.BigFloat(x, prec::Int) -BigFloat(x::Union{Integer, AbstractFloat, String}, rounding::RoundingMode) +Base.MPFR.BigFloat(x::Union{Integer, AbstractFloat, String}, rounding::RoundingMode) Base.MPFR.BigFloat(x, prec::Int, rounding::RoundingMode) -Base.MPFR.BigFloat(x::String) +Base.GMP.BigInt(::Any) Base.@big_str ``` diff --git a/doc/src/base/sort.md b/doc/src/base/sort.md index a3c3b7c3df338..17a9b8c5ed97d 100644 --- a/doc/src/base/sort.md +++ b/doc/src/base/sort.md @@ -110,6 +110,10 @@ can be specified via the `lt` keyword. Base.sort! Base.sort Base.sortperm +Base.InsertionSort +Base.MergeSort +Base.QuickSort +Base.PartialQuickSort Base.Sort.sortperm! Base.Sort.sortslices ``` @@ -131,10 +135,10 @@ Base.Sort.partialsortperm! There are currently four sorting algorithms available in base Julia: - * `InsertionSort` - * `QuickSort` - * `PartialQuickSort(k)` - * `MergeSort` + * [`InsertionSort`](@ref) + * [`QuickSort`](@ref) + * [`PartialQuickSort(k)`](@ref) + * [`MergeSort`](@ref) `InsertionSort` is an O(n^2) stable sorting algorithm. It is efficient for very small `n`, and is used internally by `QuickSort`. diff --git a/doc/src/devdocs/backtraces.md b/doc/src/devdocs/backtraces.md index ca750b79a320d..a65a494bbc3a4 100644 --- a/doc/src/devdocs/backtraces.md +++ b/doc/src/devdocs/backtraces.md @@ -15,10 +15,10 @@ and follow the instructions to generate the debugging information requested. Ta ## [Version/Environment info](@id dev-version-info) No matter the error, we will always need to know what version of Julia you are running. When Julia -first starts up, a header is printed out with a version number and date. Please also include the -output of `versioninfo()` in any report you create: +first starts up, a header is printed out with a version number and date. Please also include the output of `versioninfo()` (exported from the [`InteractiveUtils`](@ref InteractiveUtils.versioninfo) standard library) in any report you create: ```@repl +using InteractiveUtils versioninfo() ``` diff --git a/doc/src/devdocs/object.md b/doc/src/devdocs/object.md index cf9223f768d96..49d1e46724301 100644 --- a/doc/src/devdocs/object.md +++ b/doc/src/devdocs/object.md @@ -1,6 +1,6 @@ # Memory layout of Julia Objects -## Object layout (jl_value_t) +## Object layout (`jl_value_t`) The `jl_value_t` struct is the name for a block of memory owned by the Julia Garbage Collector, representing the data associated with a Julia object in memory. Absent any type information, it diff --git a/doc/src/index.md b/doc/src/index.md index c7420bce1656a..855e45db671f1 100644 --- a/doc/src/index.md +++ b/doc/src/index.md @@ -1,8 +1,12 @@ -# Julia 0.7 Documentation +# Julia 1.0 Documentation -Welcome to the documentation for Julia 0.7. +Welcome to the documentation for Julia 1.0. -Please read the [release notes](NEWS.md) to see what has changed since the last release. +Please read the [release blog post](https://julialang.org/blog/2018/08/one-point-zero) for a general overview of the language and +many of the changes since Julia v0.6. Note that version 0.7 was released alongside +1.0 to provide an upgrade path for packages and code that predates the 1.0 release. +The only difference between 0.7 and 1.0 is the removal of deprecation warnings. +For a complete list of all the changes since 0.6, see the [release notes for version 0.7](https://docs.julialang.org/en/v0.7.0/NEWS/) ### [Introduction](@id man-introduction) diff --git a/doc/src/manual/arrays.md b/doc/src/manual/arrays.md index de54a61617717..d209686d1532c 100644 --- a/doc/src/manual/arrays.md +++ b/doc/src/manual/arrays.md @@ -692,12 +692,7 @@ The following operators are supported for arrays: 2. Binary arithmetic -- `-`, `+`, `*`, `/`, `\`, `^` 3. Comparison -- `==`, `!=`, `≈` ([`isapprox`](@ref)), `≉` -Most of the binary arithmetic operators listed above also operate elementwise -when one argument is scalar: `-`, `+`, and `*` when either argument is scalar, -and `/` and `\` when the denominator is scalar. For example, `[1, 2] + 3 == [4, 5]` -and `[6, 4] / 2 == [3, 2]`. - -Additionally, to enable convenient vectorization of mathematical and other operations, +To enable convenient vectorization of mathematical and other operations, Julia [provides the dot syntax](@ref man-vectorized) `f.(args...)`, e.g. `sin.(x)` or `min.(x,y)`, for elementwise operations over arrays or mixtures of arrays and scalars (a [Broadcasting](@ref) operation); these have the additional advantage of @@ -782,7 +777,7 @@ julia> string.(1:3, ". ", ["First", "Second", "Third"]) ## Implementation -The base array type in Julia is the abstract type [`AbstractArray{T,N}`](@ref). It is parametrized by +The base array type in Julia is the abstract type [`AbstractArray{T,N}`](@ref). It is parameterized by the number of dimensions `N` and the element type `T`. [`AbstractVector`](@ref) and [`AbstractMatrix`](@ref) are aliases for the 1-d and 2-d cases. Operations on `AbstractArray` objects are defined using higher level operators and functions, in a way that is independent of the underlying storage. These operations @@ -833,7 +828,7 @@ index of dimension `k` by `1` should increase the index `i` of [`getindex(A,i)`] [`stride(A,k)`](@ref). If a pointer conversion method [`Base.unsafe_convert(Ptr{T}, A)`](@ref) is provided, the memory layout must correspond in the same way to these strides. `DenseArray` is a very specific example of a strided array where the elements are arranged contiguously, thus it -provides its subtypes with the approporiate definition of `strides`. More concrete examples +provides its subtypes with the appropriate definition of `strides`. More concrete examples can be found within the [interface guide for strided arrays](@ref man-interface-strided-arrays). [`StridedVector`](@ref) and [`StridedMatrix`](@ref) are convenient aliases for many of the builtin array types that are considered strided arrays, allowing them to dispatch to select specialized implementations that diff --git a/doc/src/manual/calling-c-and-fortran-code.md b/doc/src/manual/calling-c-and-fortran-code.md index dfd82b475fb42..6155952a813a4 100644 --- a/doc/src/manual/calling-c-and-fortran-code.md +++ b/doc/src/manual/calling-c-and-fortran-code.md @@ -130,7 +130,7 @@ Here is a slightly more complex example that discovers the local machine's hostn ```julia function gethostname() - hostname = Vector{UInt8}(128) + hostname = Vector{UInt8}(undef, 128) ccall((:gethostname, "libc"), Int32, (Ptr{UInt8}, Csize_t), hostname, sizeof(hostname)) @@ -820,7 +820,7 @@ function sf_bessel_Jn_array(nmin::Integer, nmax::Integer, x::Real) if nmax < nmin throw(DomainError()) end - result_array = Vector{Cdouble}(nmax - nmin + 1) + result_array = Vector{Cdouble}(undef, nmax - nmin + 1) errorcode = ccall( (:gsl_sf_bessel_Jn_array, :libgsl), # name of C function and library Cint, # output type @@ -973,7 +973,7 @@ Other supported conventions are: `stdcall`, `cdecl`, `fastcall`, and `thiscall` signature for Windows: ```julia -hn = Vector{UInt8}(256) +hn = Vector{UInt8}(undef, 256) err = ccall(:gethostname, stdcall, Int32, (Ptr{UInt8}, UInt32), hn, length(hn)) ``` diff --git a/doc/src/manual/documentation.md b/doc/src/manual/documentation.md index 0eae97de6a4d9..41ac5e44f4951 100644 --- a/doc/src/manual/documentation.md +++ b/doc/src/manual/documentation.md @@ -181,7 +181,7 @@ As in the example above, we recommend following some simple conventions when wri 9. Respect the line length limit used in the surrounding code. Docstrings are edited using the same tools as code. Therefore, the same conventions should apply. - It it advised to add line breaks after 92 characters. + It is advised to add line breaks after 92 characters. 6. Provide information allowing custom types to implement the function in an `# Implementation` section. These implementation details intended for developers rather than users, explaining e.g. which functions should be overridden and which functions @@ -568,7 +568,7 @@ A paragraph containing a **bold** word. Surround words with one asterisk, `*`, to display the enclosed text in italics. ``` -A paragraph containing an *emphasised* word. +A paragraph containing an *emphasized* word. ``` #### Literals @@ -621,16 +621,17 @@ the Julia documentation itself. For example: ```julia """ - accumulate!(op, y, x) + tryparse(type, str; base) -Cumulative operation `op` on a vector `x`, storing the result in `y`. See also [`accumulate`](@ref). +Like [`parse`](@ref), but returns either a value of the requested type, +or [`nothing`](@ref) if the string does not contain a valid number. """ ``` -This will create a link in the generated docs to the `accumulate` documentation -(which has more information about what this function actually does). It's good to include -cross references to mutating/non-mutating versions of a function, or to highlight a difference -between two similar-seeming functions. +This will create a link in the generated docs to the [`parse`](@ref) documentation +(which has more information about what this function actually does), and to the +[`nothing`](@ref) documentation. It's good to include cross references to mutating/non-mutating +versions of a function, or to highlight a difference between two similar-seeming functions. !!! note The above cross referencing is *not* a Markdown feature, and relies on @@ -664,7 +665,7 @@ in the [Inline elements](@ref) section above, with one or more blank lines above ``` This is a paragraph. -And this is *another* one containing some emphasised text. +And this is *another* one containing some emphasized text. A new line, but still part of the same paragraph. ``` diff --git a/doc/src/manual/environment-variables.md b/doc/src/manual/environment-variables.md index b27ff27729afc..d38aa0f21a067 100644 --- a/doc/src/manual/environment-variables.md +++ b/doc/src/manual/environment-variables.md @@ -65,6 +65,15 @@ and a global configuration search path of /etc/julia/startup.jl ``` +### `JULIA_PROJECT` + +A directory path that points to the current Julia project. Setting this +environment variable has the same effect as specifying the `--project` start-up +option, but `--project` has higher precedence. If the variable is set to `@.`, +Julia tries to find a project directory that contains `Project.toml` or +`JuliaProject.toml` file from the current directory and its parents. See also +the chapter on [Code Loading](@ref). + ### `JULIA_LOAD_PATH` A separated list of absolute paths that are to be appended to the variable diff --git a/doc/src/manual/faq.md b/doc/src/manual/faq.md index 192aa287689fd..63011925ad498 100644 --- a/doc/src/manual/faq.md +++ b/doc/src/manual/faq.md @@ -41,6 +41,12 @@ obj3 = MyModule.someotherfunction(obj2, c) ... ``` +### How do I check if the current file is being run as the main script? + +When a file is run as the main script using `julia file.jl` one might want to activate extra +functionality like command line argument handling. A way to determine that a file is run in +this fashion is to check if `abspath(PROGRAM_FILE) == @__FILE__` is `true`. + ## Functions ### I passed an argument `x` to a function, modified it inside that function, but on the outside, the variable `x` is still unchanged. Why? @@ -645,13 +651,6 @@ as nothing but rather a tuple of zero values. The empty (or "bottom") type, written as `Union{}` (an empty union type), is a type with no values and no subtypes (except itself). You will generally not need to use this type. - -### How do I check if the current file is being run as the main script? - -When a file is run as the main script using `julia file.jl` one might want to activate extra -functionality like command line argument handling. A way to determine that a file is run in -this fashion is to check if `abspath(PROGRAM_FILE) == @__FILE__` is `true`. - ## Memory ### Why does `x += y` allocate memory when `x` and `y` are arrays? diff --git a/doc/src/manual/functions.md b/doc/src/manual/functions.md index 45c170bf9b1a6..5b57f6a2a435d 100644 --- a/doc/src/manual/functions.md +++ b/doc/src/manual/functions.md @@ -541,6 +541,10 @@ function f(x; y=0, kwargs...) end ``` +Inside `f`, `kwargs` will be a key-value iterator over a named tuple. Named +tuples (as well as dictionaries with keys of `Symbol`) can be passed as keyword +arguments using a semicolon in a call, e.g. `f(x, z=1; kwargs...)`. + If a keyword argument is not assigned a default value in the method definition, then it is *required*: an [`UndefKeywordError`](@ref) exception will be thrown if the caller does not assign it a value: @@ -552,9 +556,6 @@ f(3, y=5) # ok, y is assigned f(3) # throws UndefKeywordError(:y) ``` -Inside `f`, `kwargs` will be a named tuple. Named tuples (as well as dictionaries) can be passed as -keyword arguments using a semicolon in a call, e.g. `f(x, z=1; kwargs...)`. - One can also pass `key => value` expressions after a semicolon. For example, `plot(x, y; :width => 2)` is equivalent to `plot(x, y, width=2)`. This is useful in situations where the keyword name is computed at runtime. diff --git a/doc/src/manual/getting-started.md b/doc/src/manual/getting-started.md index b41eab692e6e1..b648cf8699470 100644 --- a/doc/src/manual/getting-started.md +++ b/doc/src/manual/getting-started.md @@ -111,7 +111,7 @@ julia [switches] -- [programfile] [args...] |`--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| -|`-C`, `--cpu-target ` |Limit usage of cpu features up to ; set to `help` to see the available options| +|`-C`, `--cpu-target ` |Limit usage of CPU features up to ``; set to `help` to see the available options| |`-O`, `--optimize={0,1,2,3}` |Set the optimization level (default level is 2 if unspecified or 3 if used without a level)| |`-g`, `-g ` |Enable / Set the level of debug info generation (default level is 1 if unspecified or 2 if used without a level)| |`--inline={yes\|no}` |Control whether inlining is permitted, including overriding `@inline` declarations| diff --git a/doc/src/manual/index.md b/doc/src/manual/index.md deleted file mode 100644 index 3ae00cff847e6..0000000000000 --- a/doc/src/manual/index.md +++ /dev/null @@ -1,39 +0,0 @@ -# The Julia Manual - - * [Introduction](@ref man-introduction) - * [Getting Started](@ref man-getting-started) - * [Variables](@ref) - * [Integers and Floating-Point Numbers](@ref) - * [Mathematical Operations and Elementary Functions](@ref) - * [Complex and Rational Numbers](@ref) - * [Strings](@ref) - * [Functions](@ref) - * [Control Flow](@ref) - * [Scope of Variables](@ref scope-of-variables) - * [Types](@ref man-types) - * [Methods](@ref) - * [Constructors](@ref man-constructors) - * [Conversion and Promotion](@ref conversion-and-promotion) - * [Interfaces](@ref) - * [Modules](@ref) - * [Documentation](@ref) - * [Metaprogramming](@ref) - * [Multi-dimensional Arrays](@ref man-multi-dim-arrays) - * [Missing Values](@ref missing) - * [Networking and Streams](@ref) - * [Parallel Computing](@ref) - * [Dates](@ref) - * [Running External Programs](@ref) - * [Calling C and Fortran Code](@ref) - * [Handling Operating System Variation](@ref) - * [Environment Variables](@ref) - * [Embedding Julia](@ref) - * [Profiling](@ref) - * [Memory allocation analysis](@ref) - * [Stack Traces](@ref) - * [Performance Tips](@ref man-performance-tips) - * [Workflow Tips](@ref man-workflow-tips) - * [Style Guide](@ref) - * [Frequently Asked Questions](@ref) - * [Noteworthy Differences from other Languages](@ref) - * [Unicode Input](@ref) diff --git a/doc/src/manual/integers-and-floating-point-numbers.md b/doc/src/manual/integers-and-floating-point-numbers.md index b218ab8d582e4..b4d82c39fe44c 100644 --- a/doc/src/manual/integers-and-floating-point-numbers.md +++ b/doc/src/manual/integers-and-floating-point-numbers.md @@ -178,7 +178,7 @@ of the binary data item is the minimal needed size, if the leading digit of the `0`. In the case of leading zeros, the size is determined by the minimal needed size for a literal, which has the same length but leading digit `1`. That allows the user to control the size. -Values, which cannot be stored in `UInt128` cannot be written as such literals. +Values which cannot be stored in `UInt128` cannot be written as such literals. Binary, octal, and hexadecimal literals may be signed by a `-` immediately preceding the unsigned literal. They produce an unsigned integer of the same size as the unsigned literal @@ -215,7 +215,7 @@ UInt128: [0,340282366920938463463374607431768211455] ``` 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), +type. (The above expression uses several features that have yet to be introduced, 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.) @@ -678,7 +678,7 @@ where syntactic conflicts arise: * The 32-bit floating-point literal expression `1.5f22` could be interpreted as the numeric literal `1.5` multiplied by the variable `f22`. -In all cases, we resolve the ambiguity in favor of interpretation as numeric literals: +In all cases the ambiguity is resolved in favor of interpretation as numeric literals: * Expressions starting with `0x` are always hexadecimal literals. * Expressions starting with a numeric literal followed by `e` or `E` are always floating-point literals. diff --git a/doc/src/manual/interfaces.md b/doc/src/manual/interfaces.md index d21c493bea36f..db69514a9f0bf 100644 --- a/doc/src/manual/interfaces.md +++ b/doc/src/manual/interfaces.md @@ -16,12 +16,12 @@ to generically build upon those behaviors. | `IteratorEltype(IterType)` | `HasEltype()` | Either `EltypeUnknown()` or `HasEltype()` as appropriate | | `eltype(IterType)` | `Any` | The type of the first entry of the tuple returned by `iterate()` | | `length(iter)` | (*undefined*) | The number of items, if known | -| `size(iter, [dim...])` | (*undefined*) | The number of items in each dimension, if known | +| `size(iter, [dim])` | (*undefined*) | The number of items in each dimension, if known | | Value returned by `IteratorSize(IterType)` | Required Methods | |:------------------------------------------ |:------------------------------------------ | | `HasLength()` | [`length(iter)`](@ref) | -| `HasShape{N}()` | `length(iter)` and `size(iter, [dim...])` | +| `HasShape{N}()` | `length(iter)` and `size(iter, [dim])` | | `IsInfinite()` | (*none*) | | `SizeUnknown()` | (*none*) | @@ -231,11 +231,11 @@ ourselves, we can officially define it as a subtype of an [`AbstractArray`](@ref | `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}(undef, dims)` | Return a mutable array with the specified element type and size | +| `similar(A, dims::Dims)` | `similar(A, eltype(A), dims)` | Return a mutable array with the same element type and size *dims* | +| `similar(A, ::Type{S}, dims::Dims)` | `Array{S}(undef, dims)` | Return a mutable array with the specified element type and size | | **Non-traditional indices** | **Default definition** | **Brief description** | | `axes(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(A, ::Type{S}, inds)` | `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 diff --git a/doc/src/manual/modules.md b/doc/src/manual/modules.md index 74065606cc4e4..ef073cbe6db23 100644 --- a/doc/src/manual/modules.md +++ b/doc/src/manual/modules.md @@ -232,13 +232,10 @@ For file dependencies, a change is determined by examining whether the modificat of each file loaded by `include` or added explicitly by `include_dependency` is unchanged, or equal to the modification time truncated to the nearest second (to accommodate systems that can't copy mtime with sub-second accuracy). It also takes into account whether the path to the file chosen -by the search logic in `require` matches the path that had created the precompile file. - -It also takes into account the set of dependencies already loaded into the current process and -won't recompile those modules, even if their files change or disappear, in order to avoid creating -incompatibilities between the running system and the precompile cache. If you want to have changes -to the source reflected in the running system, you should call `reload("Module")` on the module -you changed, and any module that depended on it in which you want to see the change reflected. +by the search logic in `require` matches the path that had created the precompile file. It also takes +into account the set of dependencies already loaded into the current process and won't recompile those +modules, even if their files change or disappear, in order to avoid creating incompatibilities between +the running system and the precompile cache. If you know that a module is *not* safe to precompile your module (for example, for one of the reasons described below), you should diff --git a/doc/src/manual/noteworthy-differences.md b/doc/src/manual/noteworthy-differences.md index 9aff0df12fe68..c45d7b1ef8e44 100644 --- a/doc/src/manual/noteworthy-differences.md +++ b/doc/src/manual/noteworthy-differences.md @@ -253,7 +253,7 @@ For users coming to Julia from R, these are some noteworthy differences: Floating point literals are closer in behavior to C/C++. Octal (prefixed with `0o`) and binary (prefixed with `0b`) literals are also treated as unsigned. * String literals can be delimited with either `"` or `"""`, `"""` delimited literals can contain - `"` characters without quoting it like `"\""` String literals can have values of other variables + `"` characters without quoting it like `"\""`. String literals can have values of other variables or expressions interpolated into them, indicated by `$variablename` or `$(expression)`, which evaluates the variable name or the expression in the context of the function. * `//` indicates a [`Rational`](@ref) number, and not a single-line comment (which is `#` in Julia) diff --git a/doc/src/manual/parallel-computing.md b/doc/src/manual/parallel-computing.md index 5f18d97e75aa4..11bf6669b26ce 100644 --- a/doc/src/manual/parallel-computing.md +++ b/doc/src/manual/parallel-computing.md @@ -7,22 +7,22 @@ the different levels of parallelism offered by Julia. We can divide them in thre 2. Multi-Threading 3. Multi-Core or Distributed Processing -We will first consider Julia [Tasks (aka Coroutines)](@ref man-tasks) and other modules that rely on the Julia runtime library, that allow to suspend and resume computations with full control of inter-`Tasks` communication without having to manually interface with the operative system's scheduler. -Julia also allows to communicate between `Tasks` through operations like [`wait`](@ref) and [`fetch`](@ref). -Communication and data synchronization is managed through [`Channel`](@ref)s, which are the conduit -that allows inter-`Tasks` communication. +We will first consider Julia [Tasks (aka Coroutines)](@ref man-tasks) and other modules that rely on the Julia runtime library, that allow us to suspend and resume computations with full control of inter-`Tasks` communication without having to manually interface with the operating system's scheduler. +Julia also supports communication between `Tasks` through operations like [`wait`](@ref) and [`fetch`](@ref). +Communication and data synchronization is managed through [`Channel`](@ref)s, which are the conduits +that provide inter-`Tasks` communication. Julia also supports experimental multi-threading, where execution is forked and an anonymous function is run across all threads. -Described as a fork-join approach, parallel threads are branched off and they all have to join the Julia main thread to make serial execution continue. +Known as the fork-join approach, parallel threads execute independently, and must ultimately be joined in Julia's main thread to allow serial execution to continue. Multi-threading is supported using the `Base.Threads` module that is still considered experimental, as Julia is -not fully thread-safe yet. In particular segfaults seem to emerge for I\O operations and task switching. -As an un up-to-date reference, keep an eye on [the issue tracker](https://github.com/JuliaLang/julia/issues?q=is%3Aopen+is%3Aissue+label%3Amultithreading). +not yet fully thread-safe. In particular segfaults seem to occur during I\O operations and task switching. +As an up-to-date reference, keep an eye on [the issue tracker](https://github.com/JuliaLang/julia/issues?q=is%3Aopen+is%3Aissue+label%3Amultithreading). Multi-Threading should only be used if you take into consideration global variables, locks and -atomics, so we will explain it later. +atomics, all of which are explained later. -In the end we will present Julia's way to distributed and parallel computing. With scientific computing -in mind, Julia natively implements interfaces to distribute a process through multiple cores or machines. +In the end we will present Julia's approach to distributed and parallel computing. With scientific computing +in mind, Julia natively implements interfaces to distribute a process across multiple cores or machines. Also we will mention useful external packages for distributed programming like `MPI.jl` and `DistributedArrays.jl`. # Coroutines @@ -77,7 +77,7 @@ A channel can be visualized as a pipe, i.e., it has a write end and a read end : # we can schedule `n` instances of `foo` to be active concurrently. for _ in 1:n - @schedule foo() + @async foo() end ``` * Channels are created via the `Channel{T}(sz)` constructor. The channel will only hold objects @@ -184,16 +184,16 @@ julia> function make_jobs(n) julia> n = 12; -julia> @schedule make_jobs(n); # feed the jobs channel with "n" jobs +julia> @async make_jobs(n); # feed the jobs channel with "n" jobs julia> for i in 1:4 # start 4 tasks to process requests in parallel - @schedule do_work() + @async do_work() end julia> @elapsed while n > 0 # print out results job_id, exec_time = take!(results) - println("$job_id finished in $(round(exec_time,2)) seconds") - n = n - 1 + println("$job_id finished in $(round(exec_time; digits=2)) seconds") + global n = n - 1 end 4 finished in 0.22 seconds 3 finished in 0.45 seconds @@ -465,7 +465,7 @@ julia> function g_fix(r) g_fix (generic function with 1 method) julia> r = let m = MersenneTwister(1) - [m; accumulate(Future.randjump, m, fill(big(10)^20, nthreads()-1))] + [m; accumulate(Future.randjump, fill(big(10)^20, nthreads()-1), init=m)] end; julia> g_fix(r) @@ -1086,7 +1086,7 @@ julia> for p in workers() # start tasks on the workers to process requests in pa julia> @elapsed while n > 0 # print out results job_id, exec_time, where = take!(results) - println("$job_id finished in $(round(exec_time,2)) seconds on worker $where") + println("$job_id finished in $(round(exec_time; digits=2)) seconds on worker $where") n = n - 1 end 1 finished in 0.18 seconds on worker 4 @@ -1595,7 +1595,7 @@ requirements for the inbuilt `LocalManager` and `SSHManager`: running the Julia REPL (i.e., the master) with the rest of the cluster on the cloud, say on Amazon EC2. In this case only port 22 needs to be opened at the remote cluster coupled with SSH client authenticated via public key infrastructure (PKI). Authentication credentials can be supplied - via `sshflags`, for example ```sshflags=`-e ` ```. + via `sshflags`, for example ```sshflags=`-i ` ```. In an all-to-all topology (the default), all workers connect to each other via plain TCP sockets. The security policy on the cluster nodes must thus ensure free connectivity between workers for diff --git a/doc/src/manual/performance-tips.md b/doc/src/manual/performance-tips.md index cd81e9eaf0c59..ce86bc9ffdf9a 100644 --- a/doc/src/manual/performance-tips.md +++ b/doc/src/manual/performance-tips.md @@ -467,7 +467,7 @@ 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 +run-time, use the [function-barrier technique](@ref kernel-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 @@ -576,7 +576,7 @@ optimize the body of the loop. There are several possible fixes: * Use an explicit conversion: `x = oneunit(Float64)` * Initialize with the first loop iteration, to `x = 1 / rand()`, then loop `for i = 2:10` -## [Separate kernel functions (aka, function barriers)](@id kernal-functions) +## [Separate kernel functions (aka, function barriers)](@id kernel-functions) Many functions follow a pattern of performing some set-up work, and then running many iterations to perform a core computation. Where possible, it is a good idea to put these core computations @@ -675,7 +675,7 @@ does not (and cannot) predict its value in advance. This means that code using t function has to be conservative, checking the type on each access of `A`; such code will be very slow. -Now, one very good way to solve such problems is by using the [function-barrier technique](@ref kernal-functions). +Now, one very good way to solve such problems is by using the [function-barrier technique](@ref kernel-functions). However, in some cases you might want to eliminate the type-instability altogether. In such cases, one approach is to pass the dimensionality as a parameter, for example through `Val{T}()` (see ["Value types"](@ref)): @@ -1151,7 +1151,7 @@ The common idiom of using 1:n to index into an AbstractArray is not safe if the and may cause a segmentation fault if bounds checking is turned off. Use `LinearIndices(x)` or `eachindex(x)` instead (see also [offset-arrays](https://docs.julialang.org/en/latest/devdocs/offset-arrays)). -!!!note +!!! note While `@simd` needs to be placed directly in front of an innermost `for` loop, both `@inbounds` and `@fastmath` can be applied to either single expressions or all the expressions that appear within nested blocks of code, e.g., using `@inbounds begin` or `@inbounds for ...`. @@ -1231,7 +1231,7 @@ function mynorm(u::Vector) @fastmath @inbounds @simd for i in 1:n s += u[i]^2 end - @fastmath @inbounds return sqrt(s/n) + @fastmath @inbounds return sqrt(s) end function main() diff --git a/doc/src/manual/style-guide.md b/doc/src/manual/style-guide.md index 624297d8a10fa..a39697d124456 100644 --- a/doc/src/manual/style-guide.md +++ b/doc/src/manual/style-guide.md @@ -130,7 +130,7 @@ a = Vector{Union{Int,AbstractString,Tuple,Array}}(undef, n) In this case `Vector{Any}(undef, n)` is better. It is also more helpful to the compiler to annotate specific uses (e.g. `a[i]::Int`) than to try to pack many alternatives into one type. -## Use naming conventions consistent with Julia's `base/` +## Use naming conventions consistent with Julia `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 @@ -143,7 +143,7 @@ uses (e.g. `a[i]::Int`) than to try to pack many alternatives into one type. If a function name requires multiple words, consider whether it might represent more than one concept and might be better split into pieces. -## Write functions with argument ordering similar to Julia's Base +## Write functions with argument ordering similar to Julia Base As a general rule, the Base library uses the following order of arguments to functions, as applicable: diff --git a/doc/src/manual/variables-and-scoping.md b/doc/src/manual/variables-and-scoping.md index 496730a4376fa..82d84f289ad4d 100644 --- a/doc/src/manual/variables-and-scoping.md +++ b/doc/src/manual/variables-and-scoping.md @@ -449,7 +449,7 @@ julia> f() 0 ``` -However, it is occasionally useful to reuse an existing variable as the iteration variable. +However, it is occasionally useful to reuse an existing local variable as the iteration variable. This can be done conveniently by adding the keyword `outer`: ```jldoctest @@ -495,7 +495,7 @@ are constant by default. Note that `const` only affects the variable binding; the variable may be bound to a mutable object (such as an array), and that object may still be modified. Additionally when one tries -to assign a value a variable that is declared constant the following scenarios are possible: +to assign a value to a variable that is declared constant the following scenarios are possible: * if a new value has a different type than the type of the constant then an error is thrown: ```jldoctest @@ -522,7 +522,7 @@ julia> const z = 100 julia> z = 100 100 ``` -The last rule applies for immutable objects even if the vairable binding would change, e.g.: +The last rule applies for immutable objects even if the variable binding would change, e.g.: ```julia-repl julia> const s1 = "1" "1" @@ -555,8 +555,11 @@ WARNING: redefining constant a 1 ``` -Note that although possible, changing the value of a variable that is declared as constant -is strongly discouraged. For instance, if a method references a constant and is already +Note that although sometimes possible, changing the value of a `const` variable +is strongly discouraged, and is intended only for convenience during +interactive use. +Changing constants can cause various problems or unexpected behaviors. +For instance, if a method references a constant and is already compiled before the constant is changed then it might keep using the old value: ```jldoctest julia> const x = 1 diff --git a/src/array.c b/src/array.c index 746d474b97b18..fd4de0d24412d 100644 --- a/src/array.c +++ b/src/array.c @@ -53,7 +53,7 @@ size_t jl_arr_xtralloc_limit = 0; #define MAXINTVAL (((size_t)-1)>>1) static jl_array_t *_new_array_(jl_value_t *atype, uint32_t ndims, size_t *dims, - int isunboxed, int elsz) + int isunboxed, int isunion, int elsz) { jl_ptls_t ptls = jl_get_ptls_states(); size_t i, tot, nel=1; @@ -61,12 +61,13 @@ static jl_array_t *_new_array_(jl_value_t *atype, uint32_t ndims, size_t *dims, jl_array_t *a; for(i=0; i < ndims; i++) { - wideint_t prod = (wideint_t)nel * (wideint_t)dims[i]; - if (prod > (wideint_t) MAXINTVAL) + size_t di = dims[i]; + wideint_t prod = (wideint_t)nel * (wideint_t)di; + if (prod > (wideint_t) MAXINTVAL || di > MAXINTVAL) jl_error("invalid Array dimensions"); nel = prod; } - int isunion = atype != NULL && jl_is_uniontype(jl_tparam0(atype)); + assert(atype == NULL || isunion == jl_is_uniontype(jl_tparam0(atype))); if (isunboxed) { wideint_t prod = (wideint_t)elsz * (wideint_t)nel; if (prod > (wideint_t) MAXINTVAL) @@ -148,18 +149,19 @@ static inline jl_array_t *_new_array(jl_value_t *atype, uint32_t ndims, size_t * jl_value_t *eltype = jl_tparam0(atype); size_t elsz = 0, al = 0; int isunboxed = jl_islayout_inline(eltype, &elsz, &al); + int isunion = jl_is_uniontype(eltype); if (!isunboxed) { elsz = sizeof(void*); al = elsz; } - return _new_array_(atype, ndims, dims, isunboxed, elsz); + return _new_array_(atype, ndims, dims, isunboxed, isunion, elsz); } jl_array_t *jl_new_array_for_deserialization(jl_value_t *atype, uint32_t ndims, size_t *dims, - int isunboxed, int elsz) + int isunboxed, int isunion, int elsz) { - return _new_array_(atype, ndims, dims, isunboxed, elsz); + return _new_array_(atype, ndims, dims, isunboxed, isunion, elsz); } #ifndef JL_NDEBUG @@ -1112,8 +1114,10 @@ JL_DLLEXPORT jl_array_t *jl_array_copy(jl_array_t *ary) { size_t elsz = ary->elsize; size_t len = jl_array_len(ary); + int isunion = jl_is_uniontype(jl_tparam0(jl_typeof(ary))); jl_array_t *new_ary = _new_array_(jl_typeof(ary), jl_array_ndims(ary), - &ary->nrows, !ary->flags.ptrarray, elsz); + &ary->nrows, !ary->flags.ptrarray, + isunion, elsz); memcpy(new_ary->data, ary->data, len * elsz); // ensure isbits union arrays copy their selector bytes correctly if (jl_array_isbitsunion(ary)) diff --git a/src/ccall.cpp b/src/ccall.cpp index 23e8d1c3ad980..30a919e027427 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -1820,6 +1820,20 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) JL_GC_POP(); return mark_or_box_ccall_result(ctx, strp, retboxed, rt, unionall, static_rt); } + else if (is_libjulia_func(memcpy)) { + const jl_cgval_t &dst = argv[0]; + const jl_cgval_t &src = argv[1]; + const jl_cgval_t &n = argv[2]; + ctx.builder.CreateMemCpy( + ctx.builder.CreateIntToPtr( + emit_unbox(ctx, T_size, dst, (jl_value_t*)jl_voidpointer_type), T_pint8), + ctx.builder.CreateIntToPtr( + emit_unbox(ctx, T_size, src, (jl_value_t*)jl_voidpointer_type), T_pint8), + emit_unbox(ctx, T_size, n, (jl_value_t*)jl_ulong_type), 1, + false); + JL_GC_POP(); + return ghostValue(jl_void_type); + } jl_cgval_t retval = sig.emit_a_ccall( ctx, diff --git a/src/datatype.c b/src/datatype.c index 63069f3675854..908a9a457d05b 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -770,7 +770,16 @@ JL_DLLEXPORT jl_value_t *jl_new_structv(jl_datatype_t *type, jl_value_t **args, uint32_t na) { jl_ptls_t ptls = jl_get_ptls_states(); - if (type->instance != NULL) return type->instance; + if (type->instance != NULL) { + 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]); + } + return type->instance; + } + if (type->layout == NULL) + jl_type_error("new", (jl_value_t*)jl_datatype_type, (jl_value_t*)type); size_t nf = jl_datatype_nfields(type); jl_value_t *jv = jl_gc_alloc(ptls, jl_datatype_size(type), type); JL_GC_PUSH1(&jv); @@ -783,8 +792,8 @@ JL_DLLEXPORT jl_value_t *jl_new_structv(jl_datatype_t *type, jl_value_t **args, for(size_t i=na; i < nf; i++) { if (jl_field_isptr(type, i)) { *(jl_value_t**)((char*)jl_data_ptr(jv)+jl_field_offset(type,i)) = NULL; - - } else { + } + else { jl_value_t *ft = jl_field_type(type, i); if (jl_is_uniontype(ft)) { uint8_t *psel = &((uint8_t *)jv)[jl_field_offset(type, i) + jl_field_size(type, i) - 1]; diff --git a/src/dump.c b/src/dump.c index c18865125e6c8..0e355b87a4c5c 100644 --- a/src/dump.c +++ b/src/dump.c @@ -638,14 +638,15 @@ static void jl_serialize_value_(jl_serializer_state *s, jl_value_t *v, int as_li } else if (jl_is_array(v)) { jl_array_t *ar = (jl_array_t*)v; - if (ar->flags.ndims == 1 && ar->elsize < 128) { + int isunion = jl_is_uniontype(jl_tparam0(jl_typeof(ar))); + if (ar->flags.ndims == 1 && ar->elsize <= 0x3f) { write_uint8(s->s, TAG_ARRAY1D); - write_uint8(s->s, (ar->flags.ptrarray<<7) | (ar->elsize & 0x7f)); + write_uint8(s->s, (ar->flags.ptrarray<<7) | (isunion << 6) | (ar->elsize & 0x3f)); } else { write_uint8(s->s, TAG_ARRAY); write_uint16(s->s, ar->flags.ndims); - write_uint16(s->s, (ar->flags.ptrarray<<15) | (ar->elsize & 0x7fff)); + write_uint16(s->s, (ar->flags.ptrarray << 15) | (isunion << 14) | (ar->elsize & 0x3fff)); } for (i = 0; i < ar->flags.ndims; i++) jl_serialize_value(s, jl_box_long(jl_array_dim(ar,i))); @@ -1210,7 +1211,7 @@ static void write_mod_list(ios_t *s, jl_array_t *a) } // "magic" string and version header of .ji file -static const int JI_FORMAT_VERSION = 6; +static const int JI_FORMAT_VERSION = 7; static const char JI_MAGIC[] = "\373jli\r\n\032\n"; // based on PNG signature static const uint16_t BOM = 0xFEFF; // byte-order marker static void write_header(ios_t *s) @@ -1495,18 +1496,20 @@ static jl_value_t *jl_deserialize_value_array(jl_serializer_state *s, uint8_t ta { int usetable = (s->mode != MODE_IR); int16_t i, ndims; - int isunboxed, elsize; + int isunboxed, isunion, elsize; if (tag == TAG_ARRAY1D) { ndims = 1; elsize = read_uint8(s->s); isunboxed = !(elsize >> 7); - elsize = elsize & 0x7f; + isunion = elsize >> 6; + elsize = elsize & 0x3f; } else { ndims = read_uint16(s->s); elsize = read_uint16(s->s); isunboxed = !(elsize >> 15); - elsize = elsize & 0x7fff; + isunion = elsize >> 14; + elsize = elsize & 0x3fff; } uintptr_t pos = backref_list.len; if (usetable) @@ -1515,7 +1518,8 @@ static jl_value_t *jl_deserialize_value_array(jl_serializer_state *s, uint8_t ta for (i = 0; i < ndims; i++) { dims[i] = jl_unbox_long(jl_deserialize_value(s, NULL)); } - jl_array_t *a = jl_new_array_for_deserialization((jl_value_t*)NULL, ndims, dims, isunboxed, elsize); + jl_array_t *a = jl_new_array_for_deserialization( + (jl_value_t*)NULL, ndims, dims, isunboxed, isunion, elsize); if (usetable) backref_list.items[pos] = a; jl_value_t *aty = jl_deserialize_value(s, &jl_astaggedvalue(a)->type); @@ -1643,6 +1647,7 @@ static jl_value_t *jl_deserialize_value_method(jl_serializer_state *s, jl_value_ m->file = (jl_sym_t*)jl_deserialize_value(s, NULL); m->line = read_int32(s->s); m->min_world = jl_world_counter; + m->max_world = ~(size_t)0; m->ambig = jl_deserialize_value(s, (jl_value_t**)&m->ambig); jl_gc_wb(m, m->ambig); m->called = read_int32(s->s); @@ -2988,7 +2993,7 @@ static jl_method_instance_t *jl_recache_method_instance(jl_method_instance_t *li //assert(ti != jl_bottom_type); (void)ti; if (ti == jl_bottom_type) env = jl_emptysvec; // the intersection may fail now if the type system had made an incorrect subtype env in the past - jl_method_instance_t *_new = jl_specializations_get_linfo(m, (jl_value_t*)argtypes, env, jl_world_counter); + jl_method_instance_t *_new = jl_specializations_get_linfo(m, (jl_value_t*)argtypes, env, jl_world_counter < max_world ? jl_world_counter : max_world); _new->max_world = max_world; jl_update_backref_list((jl_value_t*)li, (jl_value_t*)_new, start); return _new; diff --git a/src/gf.c b/src/gf.c index c179563422013..194526ef7c7e7 100644 --- a/src/gf.c +++ b/src/gf.c @@ -148,7 +148,7 @@ static int8_t jl_cachearg_offset(jl_methtable_t *mt) // get or create the MethodInstance for a specialization JL_DLLEXPORT jl_method_instance_t *jl_specializations_get_linfo(jl_method_t *m, jl_value_t *type, jl_svec_t *sparams, size_t world) { - assert(world >= m->min_world && "typemap lookup is corrupted"); + assert(world >= m->min_world && world <= m->max_world && "typemap lookup is corrupted"); JL_LOCK(&m->writelock); jl_typemap_entry_t *sf = jl_typemap_assoc_by_type(m->specializations, type, NULL, /*subtype*/0, /*offs*/0, world, /*max_world_mask*/0); @@ -169,7 +169,7 @@ JL_DLLEXPORT jl_method_instance_t *jl_specializations_get_linfo(jl_method_t *m, li->min_world = world; } if (world == jl_world_counter) { - li->max_world = ~(size_t)0; + li->max_world = m->max_world; } else { li->max_world = world; @@ -358,6 +358,7 @@ JL_DLLEXPORT jl_method_instance_t* jl_set_method_inferred( else { JL_LOCK(&li->def.method->writelock); assert(min_world >= li->def.method->min_world); + assert(max_world <= li->def.method->max_world); int isinferred = jl_is_rettype_inferred(li); if (!isinferred && li->min_world >= min_world && li->max_world <= max_world) { // expand the current (uninferred) entry to cover the full inferred range @@ -955,7 +956,7 @@ static jl_method_instance_t *cache_method( jl_tupletype_t *cachett = tt; jl_svec_t* guardsigs = jl_emptysvec; size_t min_valid = definition->min_world; - size_t max_valid = ~(size_t)0; + size_t max_valid = definition->max_world; if (!cache_with_orig) { // now examine what will happen if we chose to use this sig in the cache temp = ml_matches(mt->defs, 0, compilationsig, -1, 0, world, &min_valid, &max_valid); // TODO: use MAX_UNSPECIALIZED_CONFLICTS? @@ -1501,6 +1502,7 @@ JL_DLLEXPORT void jl_method_table_disable(jl_methtable_t *mt, jl_method_t *metho jl_error("method not in method table"); JL_LOCK(&mt->writelock); // Narrow the world age on the method to make it uncallable + method->max_world = jl_world_counter; methodentry->max_world = jl_world_counter++; // Recompute ambiguities (deleting a more specific method might reveal ambiguities that it previously resolved) check_ambiguous_matches(mt->defs, methodentry, check_disabled_ambiguous_visitor); // TODO: decrease repeated work? @@ -1525,7 +1527,7 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method JL_LOCK(&mt->writelock); jl_typemap_entry_t *newentry = jl_typemap_insert(&mt->defs, (jl_value_t*)mt, (jl_tupletype_t*)type, simpletype, jl_emptysvec, (jl_value_t*)method, 0, &method_defs, - method->min_world, ~(size_t)0, &oldvalue); + method->min_world, method->max_world, &oldvalue); if (oldvalue) { if (oldvalue == (jl_value_t*)method) { // redundant add of same method; no need to do anything @@ -1982,7 +1984,7 @@ JL_DLLEXPORT int jl_is_call_ambiguous(jl_value_t *types, jl_method_t *m) 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)) + if (mambig->min_world <= jl_world_counter && jl_world_counter <= mambig->max_world && jl_subtype((jl_value_t*)types, (jl_value_t*)mambig->sig)) return 1; } return 0; @@ -1996,7 +1998,7 @@ JL_DLLEXPORT int jl_has_call_ambiguities(jl_value_t *types, jl_method_t *m) 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(mambig->sig, types)) + if (mambig->min_world <= jl_world_counter && jl_world_counter <= mambig->max_world && !jl_has_empty_intersection(mambig->sig, types)) return 1; } return 0; diff --git a/src/init.c b/src/init.c index b293ab1a44b88..64a82792b685f 100644 --- a/src/init.c +++ b/src/init.c @@ -592,6 +592,8 @@ static void jl_resolve_sysimg_location(JL_IMAGE_SEARCH rel) jl_options.outputbc = abspath(jl_options.outputbc, 0); if (jl_options.machine_file) jl_options.machine_file = abspath(jl_options.machine_file, 0); + if (jl_options.project && strncmp(jl_options.project, "@.", strlen(jl_options.project)) != 0) + jl_options.project = abspath(jl_options.project, 0); const char **cmdp = jl_options.cmds; if (cmdp) { diff --git a/src/interpreter.c b/src/interpreter.c index 030fb4a709912..feb0bbbe168a4 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -397,7 +397,9 @@ SECT_INTERP static jl_value_t *eval_value(jl_value_t *e, interpreter_state *s) if (jl_is_pinode(e)) { jl_value_t *val = eval_value(jl_fieldref_noalloc(e, 0), s); #ifndef JL_NDEBUG + JL_GC_PUSH1(&val); jl_typeassert(val, jl_fieldref_noalloc(e, 1)); + JL_GC_POP(); #endif return val; } @@ -475,18 +477,12 @@ SECT_INTERP static jl_value_t *eval_value(jl_value_t *e, interpreter_state *s) return jl_nothing; } else if (head == new_sym) { - jl_value_t *thetype = eval_value(args[0], s); - jl_value_t *v=NULL, *fldv=NULL; - JL_GC_PUSH3(&thetype, &v, &fldv); - assert(jl_is_structtype(thetype)); - v = jl_new_struct_uninit((jl_datatype_t*)thetype); - for (size_t i = 1; i < nargs; i++) { - jl_value_t *ft = jl_field_type(thetype, i - 1); - fldv = eval_value(args[i], s); - if (!jl_isa(fldv, ft)) - jl_type_error("new", ft, fldv); - jl_set_nth_field(v, i - 1, fldv); - } + jl_value_t **argv; + JL_GC_PUSHARGS(argv, nargs); + for (size_t i = 0; i < nargs; i++) + argv[i] = eval_value(args[i], s); + assert(jl_is_structtype(argv[0])); + jl_value_t *v = jl_new_structv((jl_datatype_t*)argv[0], &argv[1], nargs - 1); JL_GC_POP(); return v; } diff --git a/src/jlfrontend.scm b/src/jlfrontend.scm index 8233a47feb3aa..77557c14ab682 100644 --- a/src/jlfrontend.scm +++ b/src/jlfrontend.scm @@ -133,16 +133,19 @@ (jl-expand-to-thunk (let* ((name (caddr e)) (body (cadddr e)) - (loc (cadr body)) - (x (if (eq? name 'x) 'y 'x))) + (loc (cadr body)) + (loc (if (and (pair? loc) (eq? (car loc) 'line)) + (list loc) + '())) + (x (if (eq? name 'x) 'y 'x))) `(block (= (call eval ,x) (block - ,loc + ,@loc (call (core eval) ,name ,x))) (= (call include ,x) (block - ,loc + ,@loc (call (top include) ,name ,x))))))) ;; parse only, returning end position, no expansion. diff --git a/src/jloptions.c b/src/jloptions.c index 8f885179601f3..83c5121a5bff5 100644 --- a/src/jloptions.c +++ b/src/jloptions.c @@ -110,7 +110,7 @@ static const char opts[] = // 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" + " -C, --cpu-target Limit usage of CPU features up to ; set to \"help\" to see the available options\n" " -O, --optimize={0,1,2,3} Set the optimization level (default level is 2 if unspecified or 3 if used without a level)\n" " -g, -g Enable / Set the level of debug info generation" #ifdef JL_DEBUG_BUILD diff --git a/src/jltypes.c b/src/jltypes.c index 9a1f6a42b6449..2748cb4467dd9 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -2059,13 +2059,14 @@ void jl_init_types(void) jl_method_type = jl_new_datatype(jl_symbol("Method"), core, jl_any_type, jl_emptysvec, - jl_perm_symsvec(19, + jl_perm_symsvec(20, "name", "module", "file", "line", "sig", "min_world", + "max_world", "ambig", "specializations", "sparam_syms", @@ -2079,13 +2080,14 @@ void jl_init_types(void) "nospecialize", "isva", "pure"), - jl_svec(19, + jl_svec(20, jl_sym_type, jl_module_type, jl_sym_type, jl_int32_type, jl_type_type, jl_long_type, + jl_long_type, jl_any_type, // Union{Array, Nothing} jl_any_type, // TypeMap jl_simplevector_type, @@ -2099,7 +2101,7 @@ void jl_init_types(void) jl_int32_type, jl_bool_type, jl_bool_type), - 0, 1, 9); + 0, 1, 10); jl_method_instance_type = jl_new_datatype(jl_symbol("MethodInstance"), core, @@ -2211,7 +2213,7 @@ void jl_init_types(void) jl_svecset(jl_methtable_type->types, 7, jl_int32_type); // DWORD #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, 11, jl_voidpointer_type); jl_svecset(jl_method_instance_type->types, 12, jl_voidpointer_type); jl_svecset(jl_method_instance_type->types, 13, jl_voidpointer_type); diff --git a/src/julia-parser.scm b/src/julia-parser.scm index e319d9cc3ecca..9d832894f9bb1 100644 --- a/src/julia-parser.scm +++ b/src/julia-parser.scm @@ -779,7 +779,7 @@ (begin (if (not (ts:space? s)) (error "space required before \"?\" operator")) (take-token s) ; take the ? - (let ((t (with-whitespace-newline (without-range-colon (peek-token s))))) + (let ((t (with-whitespace-newline (without-range-colon (require-token s))))) (if (not (ts:space? s)) (error "space required after \"?\" operator"))) (let ((then (without-range-colon (parse-eq* s)))) @@ -788,7 +788,7 @@ (if (not (ts:space? s)) (error "space required before colon in \"?\" expression")) (take-token s) ; take the : - (let ((t (with-whitespace-newline (peek-token s)))) + (let ((t (with-whitespace-newline (require-token s)))) (if (not (ts:space? s)) (error "space required after colon in \"?\" expression"))) (list 'if ex then (parse-eq* s))))) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index abce5e47b6c20..be21b395c0338 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -200,9 +200,17 @@ (let ((bounds (map analyze-typevar params))) (values (map car bounds) bounds))) +(define (unmangled-name v) + (if (eq? v '||) + v + (let ((s (string v))) + (if (eqv? (string.char s 0) #\#) + (symbol (last (string-split s "#"))) + v)))) + ;; construct expression to allocate a TypeVar -(define (bounds-to-TypeVar v) - (let ((v (car v)) +(define (bounds-to-TypeVar v (unmangle #f)) + (let ((v ((if unmangle unmangled-name identity) (car v))) (lb (cadr v)) (ub (caddr v))) `(call (core TypeVar) ',v @@ -684,7 +692,7 @@ ,@(map make-decl field-names field-types)) (block ,@locs - (new ,name ,@field-names)))) + (new (outerref ,name) ,@field-names)))) any-ctor) (list any-ctor)))) @@ -836,7 +844,7 @@ (block (global ,name) (const ,name) ,@(map (lambda (v) `(local ,v)) params) - ,@(map (lambda (n v) (make-assignment n (bounds-to-TypeVar v))) params bounds) + ,@(map (lambda (n v) (make-assignment n (bounds-to-TypeVar v #t))) params bounds) (struct_type ,name (call (core svec) ,@params) (call (core svec) ,@(map quotify field-names)) ,super (call (core svec) ,@field-types) ,mut ,min-initialized))) @@ -877,7 +885,7 @@ (scope-block (block ,@(map (lambda (v) `(local ,v)) params) - ,@(map (lambda (n v) (make-assignment n (bounds-to-TypeVar v))) params bounds) + ,@(map (lambda (n v) (make-assignment n (bounds-to-TypeVar v #t))) params bounds) (abstract_type ,name (call (core svec) ,@params) ,super)))))) (define (primitive-type-def-expr n name params super) @@ -888,7 +896,7 @@ (scope-block (block ,@(map (lambda (v) `(local ,v)) params) - ,@(map (lambda (n v) (make-assignment n (bounds-to-TypeVar v))) params bounds) + ,@(map (lambda (n v) (make-assignment n (bounds-to-TypeVar v #t))) params bounds) (primitive_type ,name (call (core svec) ,@params) ,n ,super)))))) ;; take apart a type signature, e.g. T{X} <: S{Y} @@ -1908,16 +1916,19 @@ (ssavalue? x)) x (make-ssavalue))) (ini (if (eq? x xx) '() `((= ,xx ,(expand-forms x))))) + (n (length lhss)) (st (gensy))) `(block ,@ini ,.(map (lambda (i lhs) (expand-forms (lower-tuple-assignment - (list lhs st) + (if (= i (- n 1)) + (list lhs) + (list lhs st)) `(call (top indexed_iterate) ,xx ,(+ i 1) ,.(if (eq? i 0) '() `(,st)))))) - (iota (length lhss)) + (iota n) lhss) (unnecessary ,xx)))))) ((typed_hcat) @@ -2422,7 +2433,7 @@ '(null)) ((eq? (car e) 'require-existing-local) (if (not (memq (cadr e) env)) - (error "no outer variable declaration exists for \"for outer\"")) + (error "no outer local variable declaration exists for \"for outer\"")) '(null)) ((eq? (car e) 'lambda) (let* ((lv (lam:vars e)) @@ -2739,12 +2750,37 @@ f(x) = yt(x) ,(delete-duplicates (append (lam:sp lam) capt-sp))) ,body))) +;; renumber ssavalues assigned in an expr, allowing it to be repeated +(define (renumber-assigned-ssavalues e) + (let ((vals (expr-find-all (lambda (x) (and (assignment? x) (ssavalue? (cadr x)))) + e + cadadr))) + (if (null? vals) + e + (let ((repl (table))) + (for-each (lambda (id) (put! repl id (make-ssavalue))) + vals) + (let do-replace ((x e)) + (if (or (atom? x) (quoted? x)) + x + (if (eq? (car x) 'ssavalue) + (or (get repl (cadr x) #f) x) + (cons (car x) + (map do-replace (cdr x)))))))))) + (define (convert-for-type-decl rhs t) (if (equal? t '(core Any)) rhs - `(call (core typeassert) - (call (top convert) ,t ,rhs) - ,t))) + (let* ((temp (if (or (atom? t) (ssavalue? t) (quoted? t)) + #f + (make-ssavalue))) + (ty (or temp t)) + (ex `(call (core typeassert) + (call (top convert) ,ty ,rhs) + ,ty))) + (if temp + `(block (= ,temp ,(renumber-assigned-ssavalues t)) ,ex) + ex)))) ;; convert assignment to a closed variable to a setfield! call. ;; while we're at it, generate `convert` calls for variables with diff --git a/src/julia.h b/src/julia.h index 8916a655ba1a7..48563ac331f33 100644 --- a/src/julia.h +++ b/src/julia.h @@ -264,6 +264,7 @@ typedef struct _jl_method_t { // method's type signature. redundant with TypeMapEntry->specTypes jl_value_t *sig; size_t min_world; + size_t max_world; // list of potentially-ambiguous methods (nothing = none, Vector{Any} of Methods otherwise) jl_value_t *ambig; diff --git a/src/julia_internal.h b/src/julia_internal.h index 186b5d7b2b985..26835e25cc5e1 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -463,7 +463,7 @@ JL_DLLEXPORT jl_value_t *jl_argument_datatype(jl_value_t *argt JL_PROPAGATES_ROO jl_value_t *jl_nth_slot_type(jl_value_t *sig JL_PROPAGATES_ROOT, size_t i) JL_NOTSAFEPOINT; void jl_compute_field_offsets(jl_datatype_t *st); jl_array_t *jl_new_array_for_deserialization(jl_value_t *atype, uint32_t ndims, size_t *dims, - int isunboxed, int elsz); + int isunboxed, int isunion, int elsz); void jl_module_run_initializer(jl_module_t *m); extern jl_array_t *jl_module_init_order JL_GLOBALLY_ROOTED; extern jl_array_t *jl_cfunction_list JL_GLOBALLY_ROOTED; diff --git a/src/llvm-late-gc-lowering.cpp b/src/llvm-late-gc-lowering.cpp index d83f5785f9cb1..902e110cf05cf 100644 --- a/src/llvm-late-gc-lowering.cpp +++ b/src/llvm-late-gc-lowering.cpp @@ -349,8 +349,8 @@ struct LateLowerGCFrame: public FunctionPass { NoteUse(S, BBS, V, BBS.UpExposedUses); } Value *MaybeExtractUnion(std::pair Val, Instruction *InsertBefore); - int LiftPhi(State &S, PHINode *Phi); - int LiftSelect(State &S, SelectInst *SI); + void LiftPhi(State &S, PHINode *Phi, SmallVector &PHINumbers); + bool LiftSelect(State &S, SelectInst *SI); int Number(State &S, Value *V); std::vector NumberVector(State &S, Value *Vec); int NumberBase(State &S, Value *V, Value *Base); @@ -383,7 +383,10 @@ struct LateLowerGCFrame: public FunctionPass { }; static unsigned getValueAddrSpace(Value *V) { - return cast(V->getType())->getAddressSpace(); + Type *Ty = V->getType(); + if (isa(Ty)) + Ty = cast(V->getType())->getElementType(); + return cast(Ty)->getAddressSpace(); } static bool isSpecialPtr(Type *Ty) { @@ -508,42 +511,108 @@ Value *LateLowerGCFrame::MaybeExtractUnion(std::pair Val, Instructio return Val.first; } -int LateLowerGCFrame::LiftSelect(State &S, SelectInst *SI) { - Value *TrueBase = MaybeExtractUnion(FindBaseValue(S, SI->getTrueValue(), false), SI); - Value *FalseBase = MaybeExtractUnion(FindBaseValue(S, SI->getFalseValue(), false), SI); - if (getValueAddrSpace(TrueBase) != AddressSpace::Tracked) - TrueBase = ConstantPointerNull::get(cast(FalseBase->getType())); - if (getValueAddrSpace(FalseBase) != AddressSpace::Tracked) - FalseBase = ConstantPointerNull::get(cast(TrueBase->getType())); - if (getValueAddrSpace(TrueBase) != AddressSpace::Tracked) - return -1; - Value *SelectBase = SelectInst::Create(SI->getCondition(), - TrueBase, FalseBase, "gclift", SI); - int Number = ++S.MaxPtrNumber; - S.PtrNumbering[SelectBase] = S.AllPtrNumbering[SelectBase] = - S.AllPtrNumbering[SI] = Number; - S.ReversePtrNumbering[Number] = SelectBase; - return Number; +static Value *GetPtrForNumber(State &S, unsigned Num, Instruction *InsertionPoint) +{ + Value *Val = S.ReversePtrNumbering[Num]; + if (isSpecialPtrVec(Val->getType())) { + const std::vector &AllNums = S.AllVectorNumbering[Val]; + unsigned Idx = 0; + for (; Idx < AllNums.size(); ++Idx) { + if ((unsigned)AllNums[Idx] == Num) + break; + } + Val = ExtractElementInst::Create(Val, ConstantInt::get( + Type::getInt32Ty(Val->getContext()), Idx), "", InsertionPoint); + } + return Val; +} + +bool LateLowerGCFrame::LiftSelect(State &S, SelectInst *SI) { + if (isSpecialPtrVec(SI->getType())) { + VectorType *VT = cast(SI->getType()); + std::vector TrueNumbers = NumberVector(S, SI->getTrueValue()); + std::vector FalseNumbers = NumberVector(S, SI->getFalseValue()); + std::vector Numbers; + for (unsigned i = 0; i < VT->getNumElements(); ++i) { + SelectInst *LSI = SelectInst::Create(SI->getCondition(), + TrueNumbers[i] < 0 ? + ConstantPointerNull::get(cast(T_prjlvalue)) : + GetPtrForNumber(S, TrueNumbers[i], SI), + FalseNumbers[i] < 0 ? + ConstantPointerNull::get(cast(T_prjlvalue)) : + GetPtrForNumber(S, FalseNumbers[i], SI), + "gclift", SI); + int Number = ++S.MaxPtrNumber; + Numbers.push_back(Number); + S.PtrNumbering[LSI] = S.AllPtrNumbering[LSI] = Number; + S.ReversePtrNumbering[Number] = LSI; + } + S.AllVectorNumbering[SI] = Numbers; + } else { + Value *TrueBase = MaybeExtractUnion(FindBaseValue(S, SI->getTrueValue(), false), SI); + Value *FalseBase = MaybeExtractUnion(FindBaseValue(S, SI->getFalseValue(), false), SI); + if (getValueAddrSpace(TrueBase) != AddressSpace::Tracked) + TrueBase = ConstantPointerNull::get(cast(FalseBase->getType())); + if (getValueAddrSpace(FalseBase) != AddressSpace::Tracked) + FalseBase = ConstantPointerNull::get(cast(TrueBase->getType())); + if (getValueAddrSpace(TrueBase) != AddressSpace::Tracked) + return false; + Value *SelectBase = SelectInst::Create(SI->getCondition(), + TrueBase, FalseBase, "gclift", SI); + int Number = ++S.MaxPtrNumber; + S.PtrNumbering[SelectBase] = S.AllPtrNumbering[SelectBase] = + S.AllPtrNumbering[SI] = Number; + S.ReversePtrNumbering[Number] = SelectBase; + } + return true; } -int LateLowerGCFrame::LiftPhi(State &S, PHINode *Phi) +void LateLowerGCFrame::LiftPhi(State &S, PHINode *Phi, SmallVector &PHINumbers) { - PHINode *lift = PHINode::Create(T_prjlvalue, Phi->getNumIncomingValues(), "gclift", Phi); - for (unsigned i = 0; i < Phi->getNumIncomingValues(); ++i) { - Value *Incoming = Phi->getIncomingValue(i); - Value *Base = MaybeExtractUnion(FindBaseValue(S, Incoming, false), - Phi->getIncomingBlock(i)->getTerminator()); - if (getValueAddrSpace(Base) != AddressSpace::Tracked) - Base = ConstantPointerNull::get(cast(T_prjlvalue)); - if (Base->getType() != T_prjlvalue) - Base = new BitCastInst(Base, T_prjlvalue, "", Phi->getIncomingBlock(i)->getTerminator()); - lift->addIncoming(Base, Phi->getIncomingBlock(i)); + if (isSpecialPtrVec(Phi->getType())) { + VectorType *VT = cast(Phi->getType()); + std::vector lifted; + for (unsigned i = 0; i < VT->getNumElements(); ++i) { + lifted.push_back(PHINode::Create(T_prjlvalue, Phi->getNumIncomingValues(), "gclift", Phi)); + } + for (unsigned i = 0; i < Phi->getNumIncomingValues(); ++i) { + std::vector Numbers = NumberVector(S, Phi->getIncomingValue(i)); + BasicBlock *IncomingBB = Phi->getIncomingBlock(i); + Instruction *Terminator = IncomingBB->getTerminator(); + for (unsigned i = 0; i < VT->getNumElements(); ++i) { + if (Numbers[i] < 0) + lifted[i]->addIncoming(ConstantPointerNull::get(cast(T_prjlvalue)), IncomingBB); + else + lifted[i]->addIncoming(GetPtrForNumber(S, Numbers[i], Terminator), IncomingBB); + } + } + std::vector Numbers; + for (unsigned i = 0; i < VT->getNumElements(); ++i) { + int Number = ++S.MaxPtrNumber; + PHINumbers.push_back(Number); + Numbers.push_back(Number); + S.PtrNumbering[lifted[i]] = S.AllPtrNumbering[lifted[i]] = Number; + S.ReversePtrNumbering[Number] = lifted[i]; + } + S.AllVectorNumbering[Phi] = Numbers; + } else { + PHINode *lift = PHINode::Create(T_prjlvalue, Phi->getNumIncomingValues(), "gclift", Phi); + for (unsigned i = 0; i < Phi->getNumIncomingValues(); ++i) { + Value *Incoming = Phi->getIncomingValue(i); + Value *Base = MaybeExtractUnion(FindBaseValue(S, Incoming, false), + Phi->getIncomingBlock(i)->getTerminator()); + if (getValueAddrSpace(Base) != AddressSpace::Tracked) + Base = ConstantPointerNull::get(cast(T_prjlvalue)); + if (Base->getType() != T_prjlvalue) + Base = new BitCastInst(Base, T_prjlvalue, "", Phi->getIncomingBlock(i)->getTerminator()); + lift->addIncoming(Base, Phi->getIncomingBlock(i)); + } + int Number = ++S.MaxPtrNumber; + PHINumbers.push_back(Number); + S.PtrNumbering[lift] = S.AllPtrNumbering[lift] = + S.AllPtrNumbering[Phi] = Number; + S.ReversePtrNumbering[Number] = lift; } - int Number = ++S.MaxPtrNumber; - S.PtrNumbering[lift] = S.AllPtrNumbering[lift] = - S.AllPtrNumbering[Phi] = Number; - S.ReversePtrNumbering[Number] = lift; - return Number; } int LateLowerGCFrame::NumberBase(State &S, Value *V, Value *CurrentV) @@ -566,12 +635,14 @@ int LateLowerGCFrame::NumberBase(State &S, Value *V, Value *CurrentV) // input IR) Number = -1; } else if (isa(CurrentV) && !isUnion && getValueAddrSpace(CurrentV) != AddressSpace::Tracked) { - int Number = LiftSelect(S, cast(CurrentV)); - S.AllPtrNumbering[V] = Number; + Number = -1; + if (LiftSelect(S, cast(CurrentV))) + Number = S.AllPtrNumbering[V] = S.AllPtrNumbering.at(CurrentV); return Number; } else if (isa(CurrentV) && !isUnion && getValueAddrSpace(CurrentV) != AddressSpace::Tracked) { - int Number = LiftPhi(S, cast(CurrentV)); - S.AllPtrNumbering[V] = Number; + SmallVector PHINumbers; + LiftPhi(S, cast(CurrentV), PHINumbers); + Number = S.AllPtrNumbering[V] = S.AllPtrNumbering.at(CurrentV); return Number; } else if (isa(CurrentV) && !isUnion) { assert(false && "TODO: Extract"); @@ -630,7 +701,15 @@ std::vector LateLowerGCFrame::NumberVectorBase(State &S, Value *CurrentV) { Numbers = NumberVectorBase(S, IEI->getOperand(0)); int ElNumber = Number(S, IEI->getOperand(1)); Numbers[idx] = ElNumber; - } else if (isa(CurrentV) || isa(CurrentV) || isa(CurrentV)) { + } else if (isa(CurrentV) && getValueAddrSpace(CurrentV) != AddressSpace::Tracked) { + LiftSelect(S, cast(CurrentV)); + Numbers = S.AllVectorNumbering[CurrentV]; + } else if (isa(CurrentV) && getValueAddrSpace(CurrentV) != AddressSpace::Tracked) { + SmallVector PHINumbers; + LiftPhi(S, cast(CurrentV), PHINumbers); + Numbers = S.AllVectorNumbering[CurrentV]; + } else if (isa(CurrentV) || isa(CurrentV) || isa(CurrentV) || + isa(CurrentV)) { // This is simple, we can just number them sequentially for (unsigned i = 0; i < cast(CurrentV->getType())->getNumElements(); ++i) { int Num = ++S.MaxPtrNumber; @@ -638,7 +717,7 @@ std::vector LateLowerGCFrame::NumberVectorBase(State &S, Value *CurrentV) { S.ReversePtrNumbering[Num] = CurrentV; } } else { - assert(false && "Unexpected vector generating operating"); + assert(false && "Unexpected vector generating operation"); } S.AllVectorNumbering[CurrentV] = Numbers; return Numbers; @@ -1148,40 +1227,63 @@ State LateLowerGCFrame::LocalScan(Function &F) { NoteOperandUses(S, BBS, I, BBS.UpExposedUsesUnrooted); } else if (SelectInst *SI = dyn_cast(&I)) { // We need to insert an extra select for the GC root - if (!isSpecialPtr(SI->getType()) && !isUnionRep(SI->getType())) + if (!isSpecialPtr(SI->getType()) && !isSpecialPtrVec(SI->getType()) && + !isUnionRep(SI->getType())) continue; if (!isUnionRep(SI->getType()) && getValueAddrSpace(SI) != AddressSpace::Tracked) { - if (S.AllPtrNumbering.find(SI) != S.AllPtrNumbering.end()) + if (isSpecialPtrVec(SI->getType()) ? + S.AllVectorNumbering.find(SI) != S.AllVectorNumbering.end() : + S.AllPtrNumbering.find(SI) != S.AllPtrNumbering.end()) continue; - auto Num = LiftSelect(S, SI); - if (Num < 0) + if (!LiftSelect(S, SI)) continue; - auto SelectBase = cast(S.ReversePtrNumbering[Num]); - SmallVector RefinedPtr{Number(S, SelectBase->getTrueValue()), - Number(S, SelectBase->getFalseValue())}; - S.Refinements[Num] = std::move(RefinedPtr); + if (!isSpecialPtrVec(SI->getType())) { + // TODO: Refinements for vector select + int Num = S.AllPtrNumbering[SI]; + if (Num < 0) + continue; + auto SelectBase = cast(S.ReversePtrNumbering[Num]); + SmallVector RefinedPtr{Number(S, SelectBase->getTrueValue()), + Number(S, SelectBase->getFalseValue())}; + S.Refinements[Num] = std::move(RefinedPtr); + } } else { - SmallVector RefinedPtr{Number(S, SI->getTrueValue()), - Number(S, SI->getFalseValue())}; + SmallVector RefinedPtr; + if (!isSpecialPtrVec(SI->getType())) { + RefinedPtr = { + Number(S, SI->getTrueValue()), + Number(S, SI->getFalseValue()) + }; + } MaybeNoteDef(S, BBS, SI, BBS.Safepoints, std::move(RefinedPtr)); NoteOperandUses(S, BBS, I, BBS.UpExposedUsesUnrooted); } } else if (PHINode *Phi = dyn_cast(&I)) { - if (!isSpecialPtr(Phi->getType()) && !isUnionRep(Phi->getType())) { + if (!isSpecialPtr(Phi->getType()) && !isSpecialPtrVec(Phi->getType()) && + !isUnionRep(Phi->getType())) { continue; } auto nIncoming = Phi->getNumIncomingValues(); // We need to insert an extra phi for the GC root if (!isUnionRep(Phi->getType()) && getValueAddrSpace(Phi) != AddressSpace::Tracked) { - if (S.AllPtrNumbering.find(Phi) != S.AllPtrNumbering.end()) + if (isSpecialPtrVec(Phi->getType()) ? + S.AllVectorNumbering.find(Phi) != S.AllVectorNumbering.end() : + S.AllPtrNumbering.find(Phi) != S.AllPtrNumbering.end()) continue; - auto Num = LiftPhi(S, Phi); - auto lift = cast(S.ReversePtrNumbering[Num]); - S.Refinements[Num] = GetPHIRefinements(lift, S); - PHINumbers.push_back(Num); + LiftPhi(S, Phi, PHINumbers); } else { - MaybeNoteDef(S, BBS, Phi, BBS.Safepoints, GetPHIRefinements(Phi, S)); - PHINumbers.push_back(Number(S, Phi)); + SmallVector PHIRefinements; + if (!isSpecialPtrVec(Phi->getType())) + PHIRefinements = GetPHIRefinements(Phi, S); + MaybeNoteDef(S, BBS, Phi, BBS.Safepoints, std::move(PHIRefinements)); + if (isSpecialPtrVec(Phi->getType())) { + // TODO: Vector refinements + std::vector Nums = NumberVector(S, Phi); + for (int Num : Nums) + PHINumbers.push_back(Num); + } else { + PHINumbers.push_back(Number(S, Phi)); + } for (unsigned i = 0; i < nIncoming; ++i) { BBState &IncomingBBS = S.BBStates[Phi->getIncomingBlock(i)]; NoteUse(S, IncomingBBS, Phi->getIncomingValue(i), IncomingBBS.PhiOuts); @@ -1776,22 +1878,6 @@ bool LateLowerGCFrame::CleanupIR(Function &F, State *S) { return ChangesMade; } -static Value *GetPtrForNumber(State &S, unsigned Num, Instruction *InsertionPoint) -{ - Value *Val = S.ReversePtrNumbering[Num]; - if (isSpecialPtrVec(Val->getType())) { - const std::vector &AllNums = S.AllVectorNumbering[Val]; - unsigned Idx = 0; - for (; Idx < AllNums.size(); ++Idx) { - if ((unsigned)AllNums[Idx] == Num) - break; - } - Val = ExtractElementInst::Create(Val, ConstantInt::get( - Type::getInt32Ty(Val->getContext()), Idx), "", InsertionPoint); - } - return Val; -} - static void AddInPredLiveOuts(BasicBlock *BB, BitVector &LiveIn, State &S) { bool First = true; diff --git a/src/macroexpand.scm b/src/macroexpand.scm index 2c7a040e39f53..4349e3a5b67d6 100644 --- a/src/macroexpand.scm +++ b/src/macroexpand.scm @@ -60,7 +60,7 @@ ;; function definition (pattern-lambda (function (-$ (call name . argl) (|::| (call name . argl) _t)) body) - (cons 'varlist (safe-llist-positional-args (fix-arglist argl)))) + (cons 'varlist (safe-llist-positional-args (fix-arglist (append (self-argname name) argl))))) (pattern-lambda (function (where callspec . wheres) body) (let ((others (pattern-expand1 vars-introduced-by-patterns `(function ,callspec ,body)))) (cons 'varlist (append (if (and (pair? others) (eq? (car others) 'varlist)) @@ -75,7 +75,7 @@ (pattern-lambda (= (call (curly name . sparams) . argl) body) `(function (call (curly ,name . ,sparams) . ,argl) ,body)) (pattern-lambda (= (-$ (call name . argl) (|::| (call name . argl) _t)) body) - `(function (call ,name ,@argl) ,body)) + `(function ,(cadr __) ,body)) (pattern-lambda (= (where callspec . wheres) body) (cons 'function (cdr __))) @@ -135,16 +135,21 @@ (if var (list 'varlist var) '())) ;; type definition - (pattern-lambda (struct mut (<: (curly tn . tvars) super) body) - (list* 'varlist (cons (unescape tn) (unescape tn)) '(new . new) - (typevar-names tvars))) - (pattern-lambda (struct mut (curly tn . tvars) body) - (list* 'varlist (cons (unescape tn) (unescape tn)) '(new . new) - (typevar-names tvars))) - (pattern-lambda (struct mut (<: tn super) body) - (list 'varlist (cons (unescape tn) (unescape tn)) '(new . new))) - (pattern-lambda (struct mut tn body) - (list 'varlist (cons (unescape tn) (unescape tn)) '(new . new))) + (pattern-lambda (struct mut spec body) + (let ((tn (typedef-expr-name spec)) + (tv (typedef-expr-tvars spec))) + (list* 'varlist (cons (unescape tn) (unescape tn)) '(new . new) + (typevar-names tv)))) + (pattern-lambda (abstract spec) + (let ((tn (typedef-expr-name spec)) + (tv (typedef-expr-tvars spec))) + (list* 'varlist (cons (unescape tn) (unescape tn)) + (typevar-names tv)))) + (pattern-lambda (primitive spec nb) + (let ((tn (typedef-expr-name spec)) + (tv (typedef-expr-tvars spec))) + (list* 'varlist (cons (unescape tn) (unescape tn)) + (typevar-names tv)))) )) ; vars-introduced-by-patterns @@ -178,6 +183,17 @@ (cadr e) e)) +(define (typedef-expr-name e) + (cond ((atom? e) e) + ((or (eq? (car e) 'curly) (eq? (car e) '<:)) (typedef-expr-name (cadr e))) + (else e))) + +(define (typedef-expr-tvars e) + (cond ((atom? e) '()) + ((eq? (car e) '<:) (typedef-expr-tvars (cadr e))) + ((eq? (car e) 'curly) (cddr e)) + (else '()))) + (define (typevar-expr-name e) (car (analyze-typevar e))) ;; get the list of names from a list of `where` variable expressions @@ -241,6 +257,12 @@ ;; count escaped argument names as "keywords" to prevent renaming (safe-llist-positional-args lst #t)))) +;; argument name for the function itself given `function (f::T)(...)`, otherwise () +(define (self-argname name) + (if (and (length= name 3) (eq? (car name) '|::|)) + (list (cadr name)) + '())) + ;; resolve-expansion-vars-with-new-env, but turn on `inarg` once we get inside ;; the formal argument list. `e` in general might be e.g. `(f{T}(x)::T) where T`, ;; and we want `inarg` to be true for the `(x)` part. @@ -363,12 +385,12 @@ ;; in keyword arg A=B, don't transform "A" (unescape (cadr (cadr e)))) ,(resolve-expansion-vars- (caddr (cadr e)) env m parent-scope inarg)) - ,(resolve-expansion-vars- (caddr e) env m parent-scope inarg))) + ,(resolve-expansion-vars-with-new-env (caddr e) env m parent-scope inarg))) (else `(kw ,(if inarg (resolve-expansion-vars- (cadr e) env m parent-scope inarg) (unescape (cadr e))) - ,(resolve-expansion-vars- (caddr e) env m parent-scope inarg))))) + ,(resolve-expansion-vars-with-new-env (caddr e) env m parent-scope inarg))))) ((let) (let* ((newenv (new-expansion-env-for e env)) diff --git a/src/method.c b/src/method.c index 981f917831fa3..223fa4eb4b8e1 100644 --- a/src/method.c +++ b/src/method.c @@ -460,7 +460,7 @@ jl_method_instance_t *jl_get_specialized(jl_method_t *m, jl_value_t *types, jl_s new_linfo->specTypes = types; new_linfo->sparam_vals = sp; new_linfo->min_world = m->min_world; - new_linfo->max_world = ~(size_t)0; + new_linfo->max_world = m->max_world; return new_linfo; } @@ -595,6 +595,7 @@ JL_DLLEXPORT jl_method_t *jl_new_method_uninit(jl_module_t *module) m->nargs = 0; m->traced = 0; m->min_world = 1; + m->max_world = ~(size_t)0; JL_MUTEX_INIT(&m->writelock); return m; } @@ -639,6 +640,7 @@ static jl_method_t *jl_new_method( JL_GC_POP(); m->min_world = ++jl_world_counter; + m->max_world = ~(size_t)0; return m; } diff --git a/src/runtime_intrinsics.c b/src/runtime_intrinsics.c index 5caa7bf6f137f..92c19c8fe5898 100644 --- a/src/runtime_intrinsics.c +++ b/src/runtime_intrinsics.c @@ -78,8 +78,9 @@ JL_DLLEXPORT jl_value_t *jl_pointerset(jl_value_t *p, jl_value_t *x, jl_value_t JL_DLLEXPORT jl_value_t *jl_cglobal(jl_value_t *v, jl_value_t *ty) { JL_TYPECHK(cglobal, type, ty); + JL_GC_PUSH1(&v); jl_value_t *rt = - v == (jl_value_t*)jl_void_type ? (jl_value_t*)jl_voidpointer_type : // a common case + ty == (jl_value_t*)jl_void_type ? (jl_value_t*)jl_voidpointer_type : // a common case (jl_value_t*)jl_apply_type1((jl_value_t*)jl_pointer_type, ty); if (!jl_is_concrete_type(rt)) @@ -88,8 +89,11 @@ JL_DLLEXPORT jl_value_t *jl_cglobal(jl_value_t *v, jl_value_t *ty) if (jl_is_tuple(v) && jl_nfields(v) == 1) v = jl_fieldref(v, 0); - if (jl_is_pointer(v)) - return jl_bitcast(rt, v); + if (jl_is_pointer(v)) { + v = jl_bitcast(rt, v); + JL_GC_POP(); + return v; + } char *f_lib = NULL; if (jl_is_tuple(v) && jl_nfields(v) > 1) { @@ -120,6 +124,7 @@ JL_DLLEXPORT jl_value_t *jl_cglobal(jl_value_t *v, jl_value_t *ty) jl_value_t *jv = jl_gc_alloc_1w(); jl_set_typeof(jv, rt); *(void**)jl_data_ptr(jv) = ptr; + JL_GC_POP(); return jv; } diff --git a/src/subtype.c b/src/subtype.c index 33baf50bac0ee..0575a3c830dbf 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -598,8 +598,9 @@ static jl_value_t *fix_inferred_var_bound(jl_tvar_t *var, jl_value_t *ty) { if (!jl_is_typevar(ty) && jl_has_free_typevars(ty)) { jl_value_t *ans = ty; - jl_array_t *vs = jl_find_free_typevars(ty); + jl_array_t *vs = NULL; JL_GC_PUSH2(&ans, &vs); + vs = jl_find_free_typevars(ty); int i; for (i = 0; i < jl_array_len(vs); i++) { ans = jl_type_unionall((jl_tvar_t*)jl_array_ptr_ref(vs, i), ans); @@ -630,7 +631,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_GC_PUSH3(&u, &vb.lb, &vb.ub); + JL_GC_PUSH4(&u, &vb.lb, &vb.ub, &vb.innervars); e->vars = &vb; int ans; if (R) { @@ -1188,6 +1189,13 @@ JL_DLLEXPORT int jl_types_equal(jl_value_t *a, jl_value_t *b) { if (obviously_egal(a, b)) return 1; if (obviously_unequal(a, b)) return 0; + if (jl_is_datatype(a) && !jl_is_concrete_type(b)) { + // if one type looks more likely to be abstract, check it on the left + // first in order to reject more quickly. + jl_value_t *temp = a; + a = b; + b = temp; + } return jl_subtype(a, b) && jl_subtype(b, a); } @@ -1724,7 +1732,9 @@ static int intersect_vararg_length(jl_value_t *v, ssize_t n, jl_stenv_t *e, int8 // only do the check if N is free in the tuple type's last parameter if (jl_is_typevar(N) && N != (jl_value_t*)va_p1 && N != (jl_value_t*)va_p2) { jl_value_t *len = jl_box_long(n); + JL_GC_PUSH1(&len); jl_value_t *il = R ? intersect(len, N, e, 2) : intersect(N, len, e, 2); + JL_GC_POP(); if (il == jl_bottom_type) return 0; } @@ -2609,18 +2619,6 @@ static int args_morespecific_fix1(jl_value_t *a, jl_value_t *b, int swap, jl_typ return ret; } -static int partially_morespecific(jl_value_t *a, jl_value_t *b, int invariant, jl_typeenv_t *env) -{ - if (jl_is_uniontype(b)) { - jl_uniontype_t *u = (jl_uniontype_t*)b; - if (type_morespecific_(a, u->a, invariant, env) || - type_morespecific_(a, u->b, invariant, env)) - return 1; - return 0; - } - return type_morespecific_(a, b, invariant, env); -} - static int count_occurs(jl_value_t *t, jl_tvar_t *v) { if (t == (jl_value_t*)v) @@ -2692,9 +2690,18 @@ static int type_morespecific_(jl_value_t *a, jl_value_t *b, int invariant, jl_ty if (jl_is_uniontype(a)) { // Union a is more specific than b if some element of a is more specific than b, but // not vice-versa. + if (sub_msp(b, a, env)) + return 0; jl_uniontype_t *u = (jl_uniontype_t*)a; - return ((partially_morespecific(u->a, b, invariant, env) || partially_morespecific(u->b, b, invariant, env)) && - !partially_morespecific(b, a, invariant, env)); + if (type_morespecific_(u->a, b, invariant, env) || type_morespecific_(u->b, b, invariant, env)) { + if (jl_is_uniontype(b)) { + jl_uniontype_t *v = (jl_uniontype_t*)b; + if (type_morespecific_(v->a, a, invariant, env) || type_morespecific_(v->b, a, invariant, env)) + return 0; + } + return 1; + } + return 0; } if (jl_is_type_type(a) && !invariant) { diff --git a/src/toplevel.c b/src/toplevel.c index 6da0ab47b98f5..a57ad3b16ddfb 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -523,6 +523,29 @@ int jl_is_toplevel_only_expr(jl_value_t *e) ((jl_expr_t*)e)->head == jl_incomplete_sym); } +int jl_needs_lowering(jl_value_t *e) JL_NOTSAFEPOINT +{ + if (!jl_is_expr(e)) + return 0; + jl_expr_t *ex = (jl_expr_t*)e; + jl_sym_t *head = ex->head; + if (head == module_sym || head == import_sym || head == using_sym || + head == export_sym || head == thunk_sym || head == toplevel_sym || + head == error_sym || head == jl_incomplete_sym || head == method_sym) { + return 0; + } + if (head == global_sym) { + size_t i, l = jl_array_len(ex->args); + for (i = 0; i < l; i++) { + jl_value_t *a = jl_exprarg(ex, i); + if (!jl_is_symbol(a) && !jl_is_globalref(a)) + return 1; + } + return 0; + } + return 1; +} + void jl_resolve_globals_in_ir(jl_array_t *stmts, jl_module_t *m, jl_svec_t *sparam_vals, int binding_effects); @@ -600,22 +623,36 @@ jl_value_t *jl_toplevel_eval_flex(jl_module_t *m, jl_value_t *e, int fast, int e } jl_expr_t *ex = (jl_expr_t*)e; + if (ex->head == dot_sym) { if (jl_expr_nargs(ex) != 2) jl_error("syntax: malformed \".\" expression"); jl_value_t *lhs = jl_exprarg(ex, 0); jl_value_t *rhs = jl_exprarg(ex, 1); - // only handle `a.b` syntax here + // only handle `a.b` syntax here, so qualified names can be eval'd in pure contexts if (jl_is_quotenode(rhs) && jl_is_symbol(jl_fieldref(rhs,0))) return jl_eval_dot_expr(m, lhs, rhs, fast); } + if (ptls->in_pure_callback) { jl_error("eval cannot be used in a generated function"); } - else if (ex->head == module_sym) { - return jl_eval_module_expr(m, ex); + + jl_method_instance_t *li = NULL; + jl_code_info_t *thk = NULL; + JL_GC_PUSH3(&li, &thk, &ex); + + if (!expanded && jl_needs_lowering(e)) { + ex = (jl_expr_t*)jl_expand(e, m); + } + jl_sym_t *head = jl_is_expr(ex) ? ex->head : NULL; + + if (head == module_sym) { + jl_value_t *val = jl_eval_module_expr(m, ex); + JL_GC_POP(); + return val; } - else if (ex->head == using_sym) { + else if (head == using_sym) { size_t last_age = ptls->world_age; ptls->world_age = jl_world_counter; jl_sym_t *name = NULL; @@ -652,9 +689,10 @@ jl_value_t *jl_toplevel_eval_flex(jl_module_t *m, jl_value_t *e, int fast, int e } } ptls->world_age = last_age; + JL_GC_POP(); return jl_nothing; } - else if (ex->head == import_sym) { + else if (head == import_sym) { size_t last_age = ptls->world_age; ptls->world_age = jl_world_counter; jl_sym_t *name = NULL; @@ -679,53 +717,41 @@ jl_value_t *jl_toplevel_eval_flex(jl_module_t *m, jl_value_t *e, int fast, int e } } ptls->world_age = last_age; + JL_GC_POP(); return jl_nothing; } - else if (ex->head == export_sym) { + else if (head == export_sym) { for (size_t i = 0; i < jl_array_len(ex->args); i++) { jl_sym_t *name = (jl_sym_t*)jl_array_ptr_ref(ex->args, i); if (!jl_is_symbol(name)) jl_error("syntax: malformed \"export\" statement"); jl_module_export(m, name); } + JL_GC_POP(); return jl_nothing; } - else if (ex->head == global_sym) { + else if (head == global_sym) { // create uninitialized mutable binding for "global x" decl size_t i, l = jl_array_len(ex->args); for (i = 0; i < l; i++) { - jl_value_t *a = jl_exprarg(ex, i); - if (!jl_is_symbol(a) && !jl_is_globalref(a)) - break; - } - if (i == l) { - for (i = 0; i < l; i++) { - jl_sym_t *gs = (jl_sym_t*)jl_exprarg(ex, i); - jl_module_t *gm = m; - if (jl_is_globalref(gs)) { - gm = jl_globalref_mod(gs); - gs = jl_globalref_name(gs); - } - assert(jl_is_symbol(gs)); - jl_get_binding_wr(gm, gs, 0); + jl_value_t *arg = jl_exprarg(ex, i); + jl_module_t *gm; + jl_sym_t *gs; + if (jl_is_globalref(arg)) { + gm = jl_globalref_mod(arg); + gs = jl_globalref_name(arg); } - return jl_nothing; + else { + assert(jl_is_symbol(arg)); + gm = m; + gs = (jl_sym_t*)arg; + } + jl_get_binding_wr(gm, gs, 0); } - // fall-through to expand to normalize the syntax - } - - jl_method_instance_t *li = NULL; - jl_code_info_t *thk = NULL; - JL_GC_PUSH3(&li, &thk, &ex); - - if (!expanded && ex->head != thunk_sym && ex->head != method_sym && ex->head != toplevel_sym && - ex->head != error_sym && ex->head != jl_incomplete_sym) { - // not yet expanded - ex = (jl_expr_t*)jl_expand(e, m); + JL_GC_POP(); + return jl_nothing; } - jl_sym_t *head = jl_is_expr(ex) ? ex->head : NULL; - - if (head == toplevel_sym) { + else if (head == toplevel_sym) { size_t last_age = ptls->world_age; jl_value_t *res = jl_nothing; int i; diff --git a/src/typemap.c b/src/typemap.c index 03f9ec7564c58..222bf7136490c 100644 --- a/src/typemap.c +++ b/src/typemap.c @@ -743,7 +743,7 @@ jl_typemap_entry_t *jl_typemap_entry_assoc_exact(jl_typemap_entry_t *ml, jl_valu { // some manually-unrolled common special cases while (ml->simplesig == (void*)jl_nothing && ml->guardsigs == jl_emptysvec && ml->isleafsig) { - // use a tight loop for a long as possible + // use a tight loop for as long as possible if (world >= ml->min_world && world <= ml->max_world) { if (n == jl_field_count(ml->sig) && jl_typeof(args[0]) == jl_tparam(ml->sig, 0)) { if (n == 1) diff --git a/stdlib/Dates/src/query.jl b/stdlib/Dates/src/query.jl index 88806673cc157..cbad78689e62c 100644 --- a/stdlib/Dates/src/query.jl +++ b/stdlib/Dates/src/query.jl @@ -187,7 +187,7 @@ julia> Dates.dayofweekofmonth(Date("2000-02-08")) julia> Dates.dayofweekofmonth(Date("2000-02-15")) 3 -```` +``` """ function dayofweekofmonth(dt::TimeType) d = day(dt) diff --git a/stdlib/Distributed/src/cluster.jl b/stdlib/Distributed/src/cluster.jl index 0d1614b7e972a..e1d4bae511d91 100644 --- a/stdlib/Distributed/src/cluster.jl +++ b/stdlib/Distributed/src/cluster.jl @@ -644,7 +644,7 @@ end Return the cluster cookie. """ -cluster_cookie() = LPROC.cookie +cluster_cookie() = (init_multi(); LPROC.cookie) """ cluster_cookie(cookie) -> cookie @@ -652,6 +652,7 @@ cluster_cookie() = LPROC.cookie Set the passed cookie as the cluster cookie, then returns it. """ function cluster_cookie(cookie) + init_multi() # The cookie must be an ASCII string with length <= HDR_COOKIE_LEN @assert isascii(cookie) @assert length(cookie) <= HDR_COOKIE_LEN @@ -719,6 +720,15 @@ const map_del_wrkr = Set{Int}() myid() Get the id of the current process. + +# Examples +```julia-repl +julia> myid() +1 + +julia> remotecall_fetch(() -> myid(), 4) +4 +``` """ myid() = LPROC.id @@ -726,6 +736,17 @@ myid() = LPROC.id nprocs() Get the number of available processes. + +# Examples +```julia-repl +julia> nprocs() +3 + +julia> workers() +5-element Array{Int64,1}: + 2 + 3 +``` """ function nprocs() if myid() == 1 || (PGRP.topology == :all_to_all && !isclusterlazy()) @@ -745,8 +766,19 @@ end """ nworkers() -Get the number of available worker processes. This is one less than `nprocs()`. Equal to +Get the number of available worker processes. This is one less than [`nprocs()`](@ref). Equal to `nprocs()` if `nprocs() == 1`. + +# Examples +```julia-repl +\$ julia -p 5 + +julia> nprocs() +6 + +julia> nworkers() +5 +``` """ function nworkers() n = nprocs() @@ -756,7 +788,18 @@ end """ procs() -Return a list of all process identifiers. +Return a list of all process identifiers, including pid 1 (which is not included by [`workers()`](@ref)). + +# Examples +```julia-repl +\$ julia -p 5 + +julia> procs() +3-element Array{Int64,1}: + 1 + 2 + 3 +``` """ function procs() if myid() == 1 || (PGRP.topology == :all_to_all && !isclusterlazy()) @@ -808,6 +851,16 @@ end workers() Return a list of all worker process identifiers. + +# Examples +```julia-repl +\$ julia -p 5 + +julia> workers() +2-element Array{Int64,1}: + 2 + 3 +``` """ function workers() allp = procs() @@ -831,13 +884,29 @@ Remove the specified workers. Note that only process 1 can add or remove workers. Argument `waitfor` specifies how long to wait for the workers to shut down: - - If unspecified, `rmprocs` will wait until all requested `pids` are removed. - - An `ErrorException` is raised if all workers cannot be terminated before - the requested `waitfor` seconds. - - With a `waitfor` value of 0, the call returns immediately with the workers - scheduled for removal in a different task. The scheduled `Task` object is - returned. The user should call `wait` on the task before invoking any other - parallel calls. + - If unspecified, `rmprocs` will wait until all requested `pids` are removed. + - An [`ErrorException`](@ref) is raised if all workers cannot be terminated before + the requested `waitfor` seconds. + - With a `waitfor` value of 0, the call returns immediately with the workers + scheduled for removal in a different task. The scheduled [`Task`](@ref) object is + returned. The user should call [`wait`](@ref) on the task before invoking any other + parallel calls. + +# Examples +```julia-repl +\$ julia -p 5 + +julia> t = rmprocs(2, 3, waitfor=0) +Task (runnable) @0x0000000107c718d0 + +julia> wait(t) + +julia> workers() +3-element Array{Int64,1}: + 4 + 5 + 6 +``` """ function rmprocs(pids...; waitfor=typemax(Int)) cluster_mgmt_from_master_check() diff --git a/stdlib/Distributed/src/macros.jl b/stdlib/Distributed/src/macros.jl index f04ad345f10bc..15faadb3c31fa 100644 --- a/stdlib/Distributed/src/macros.jl +++ b/stdlib/Distributed/src/macros.jl @@ -251,7 +251,7 @@ end function preduce(reducer, f, R) N = length(R) - chunks = splitrange(N, nworkers()) + chunks = splitrange(Int(N), nworkers()) all_w = workers()[1:length(chunks)] w_exec = Task[] diff --git a/stdlib/Distributed/src/remotecall.jl b/stdlib/Distributed/src/remotecall.jl index 052e4416efe4c..2ee8c5437820c 100644 --- a/stdlib/Distributed/src/remotecall.jl +++ b/stdlib/Distributed/src/remotecall.jl @@ -388,6 +388,20 @@ Any remote exceptions are captured in a [`RemoteException`](@ref) and thrown. See also [`fetch`](@ref) and [`remotecall`](@ref). + +# Examples +```julia-repl +\$ julia -p 2 + +julia> remotecall_fetch(sqrt, 2, 4) +2.0 + +julia> remotecall_fetch(sqrt, 2, -4) +ERROR: On worker 2: +DomainError with -4.0: +sqrt will only return a complex result if called with a complex argument. Try sqrt(Complex(x)). +... +``` """ remotecall_fetch(f, id::Integer, args...; kwargs...) = remotecall_fetch(f, worker_from_id(id), args...; kwargs...) @@ -481,14 +495,14 @@ end """ wait(r::Future) -Wait for a value to become available for the specified future. +Wait for a value to become available for the specified [`Future`](@ref). """ wait(r::Future) = (r.v !== nothing && return r; call_on_owner(wait_ref, r, myid()); r) """ wait(r::RemoteChannel, args...) -Wait for a value to become available on the specified remote channel. +Wait for a value to become available on the specified [`RemoteChannel`](@ref). """ wait(r::RemoteChannel, args...) = (call_on_owner(wait_ref, r, myid(), args...); r) diff --git a/stdlib/Distributed/src/workerpool.jl b/stdlib/Distributed/src/workerpool.jl index e52f546e308a0..c8e3f111a5daa 100644 --- a/stdlib/Distributed/src/workerpool.jl +++ b/stdlib/Distributed/src/workerpool.jl @@ -32,7 +32,15 @@ end """ WorkerPool(workers::Vector{Int}) -Create a WorkerPool from a vector of worker ids. +Create a `WorkerPool` from a vector of worker ids. + +# Examples +```julia-repl +\$ julia -p 3 + +julia> WorkerPool([2, 3]) +WorkerPool(Channel{Int64}(sz_max:9223372036854775807,sz_curr:2), Set([2, 3]), RemoteChannel{Channel{Any}}(1, 1, 6)) +``` """ function WorkerPool(workers::Vector{Int}) pool = WorkerPool() @@ -155,7 +163,20 @@ end """ remotecall(f, pool::AbstractWorkerPool, args...; kwargs...) -> Future -`WorkerPool` variant of `remotecall(f, pid, ....)`. Waits for and takes a free worker from `pool` and performs a `remotecall` on it. +[`WorkerPool`](@ref) variant of `remotecall(f, pid, ....)`. Wait for and take a free worker from `pool` and perform a `remotecall` on it. + +# Examples +```julia-repl +\$ julia -p 3 + +julia> wp = WorkerPool([2, 3]); + +julia> A = rand(3000); + +julia> f = remotecall(maximum, wp, A) +Future(2, 1, 6, nothing) +``` +In this example, the task ran on pid 2, called from pid 1. """ remotecall(f, pool::AbstractWorkerPool, args...; kwargs...) = remotecall_pool(remotecall, f, pool, args...; kwargs...) @@ -163,8 +184,23 @@ remotecall(f, pool::AbstractWorkerPool, args...; kwargs...) = remotecall_pool(re """ remotecall_wait(f, pool::AbstractWorkerPool, args...; kwargs...) -> Future -`WorkerPool` variant of `remotecall_wait(f, pid, ....)`. Waits for and takes a free worker from `pool` and -performs a `remotecall_wait` on it. +[`WorkerPool`](@ref) variant of `remotecall_wait(f, pid, ....)`. Wait for and take a free worker from `pool` and +perform a `remotecall_wait` on it. + +# Examples +```julia-repl +\$ julia -p 3 + +julia> wp = WorkerPool([2, 3]); + +julia> A = rand(3000); + +julia> f = remotecall_wait(maximum, wp, A) +Future(3, 1, 9, nothing) + +julia> fetch(f) +0.9995177101692958 +``` """ remotecall_wait(f, pool::AbstractWorkerPool, args...; kwargs...) = remotecall_pool(remotecall_wait, f, pool, args...; kwargs...) @@ -172,16 +208,28 @@ remotecall_wait(f, pool::AbstractWorkerPool, args...; kwargs...) = remotecall_po """ remotecall_fetch(f, pool::AbstractWorkerPool, args...; kwargs...) -> result -`WorkerPool` variant of `remotecall_fetch(f, pid, ....)`. Waits for and takes a free worker from `pool` and +[`WorkerPool`](@ref) variant of `remotecall_fetch(f, pid, ....)`. Waits for and takes a free worker from `pool` and performs a `remotecall_fetch` on it. + +# Examples +```julia-repl +\$ julia -p 3 + +julia> wp = WorkerPool([2, 3]); + +julia> A = rand(3000); + +julia> remotecall_fetch(maximum, wp, A) +0.9995177101692958 +``` """ remotecall_fetch(f, pool::AbstractWorkerPool, args...; kwargs...) = remotecall_pool(remotecall_fetch, f, pool, args...; kwargs...) """ remote_do(f, pool::AbstractWorkerPool, args...; kwargs...) -> nothing -`WorkerPool` variant of `remote_do(f, pid, ....)`. Waits for and takes a free worker from `pool` and -performs a `remote_do` on it. +[`WorkerPool`](@ref) variant of `remote_do(f, pid, ....)`. Wait for and take a free worker from `pool` and +perform a `remote_do` on it. """ remote_do(f, pool::AbstractWorkerPool, args...; kwargs...) = remotecall_pool(remote_do, f, pool, args...; kwargs...) @@ -190,7 +238,15 @@ const _default_worker_pool = Ref{Union{WorkerPool, Nothing}}(nothing) """ default_worker_pool() -`WorkerPool` containing idle `workers` - used by `remote(f)` and [`pmap`](@ref) (by default). +[`WorkerPool`](@ref) containing idle [`workers`](@ref) - used by `remote(f)` and [`pmap`](@ref) (by default). + +# Examples +```julia-repl +\$ julia -p 3 + +julia> default_worker_pool() +WorkerPool(Channel{Int64}(sz_max:9223372036854775807,sz_curr:3), Set([4, 2, 3]), RemoteChannel{Channel{Any}}(1, 1, 4)) +``` """ function default_worker_pool() # On workers retrieve the default worker pool from the master when accessed @@ -206,10 +262,10 @@ function default_worker_pool() end """ - remote([::AbstractWorkerPool], f) -> Function + remote([p::AbstractWorkerPool], f) -> Function Return an anonymous function that executes function `f` on an available worker -using [`remotecall_fetch`](@ref). +(drawn from [`WorkerPool`](@ref) `p` if provided) using [`remotecall_fetch`](@ref). """ remote(f) = (args...; kwargs...)->remotecall_fetch(f, default_worker_pool(), args...; kwargs...) remote(p::AbstractWorkerPool, f) = (args...; kwargs...)->remotecall_fetch(f, p, args...; kwargs...) diff --git a/stdlib/Distributed/test/distributed_exec.jl b/stdlib/Distributed/test/distributed_exec.jl index 04ef09d495a2d..383a6783a311d 100644 --- a/stdlib/Distributed/test/distributed_exec.jl +++ b/stdlib/Distributed/test/distributed_exec.jl @@ -3,6 +3,8 @@ using Test, Distributed, Random, Serialization, Sockets import Distributed: launch, manage +@test cluster_cookie() isa String + include(joinpath(Sys.BINDIR, "..", "share", "julia", "test", "testenv.jl")) @test Distributed.extract_imports(:(begin; import Foo, Bar; let; using Baz; end; end)) == @@ -1525,6 +1527,14 @@ end a27933 = :_not_defined_27933 @test remotecall_fetch(()->a27933, first(workers())) === a27933 +# PR #28651 +for T in (UInt8, Int8, UInt16, Int16, UInt32, Int32, UInt64) + n = @distributed (+) for i in Base.OneTo(T(10)) + i + end + @test n == 55 +end + # Run topology tests last after removing all workers, since a given # cluster at any time only supports a single topology. rmprocs(workers()) diff --git a/stdlib/InteractiveUtils/src/macros.jl b/stdlib/InteractiveUtils/src/macros.jl index 1d13e7020a3e1..6415bb68c64af 100644 --- a/stdlib/InteractiveUtils/src/macros.jl +++ b/stdlib/InteractiveUtils/src/macros.jl @@ -15,22 +15,50 @@ function gen_call_with_extracted_types(__module__, fcn, ex0) $(fcn)(Core.kwfunc(arg1), Tuple{typeof(kwargs), Core.Typeof(arg1), map(Core.Typeof, args)...}) end - elseif ex0.head == :call + elseif ex0.head === :call return Expr(:call, fcn, esc(ex0.args[1]), Expr(:call, typesof, map(esc, ex0.args[2:end])...)) - elseif ex0.head == :(=) && length(ex0.args) == 2 && ex0.args[1].head == :(.) - return Expr(:call, fcn, Base.setproperty!, - Expr(:call, typesof, map(esc, [ex0.args[1].args..., ex0.args[2]])...)) + elseif ex0.head === :(=) && length(ex0.args) == 2 + lhs, rhs = ex0.args + if isa(lhs, Expr) + if lhs.head === :(.) + return Expr(:call, fcn, Base.setproperty!, + Expr(:call, typesof, map(esc, lhs.args)..., esc(rhs))) + elseif lhs.head === :ref + return Expr(:call, fcn, Base.setindex!, + Expr(:call, typesof, esc(lhs.args[1]), esc(rhs), map(esc, lhs.args[2:end])...)) + end + end + elseif ex0.head === :vcat || ex0.head === :typed_vcat + if ex0.head === :vcat + f, hf = Base.vcat, Base.hvcat + args = ex0.args + else + f, hf = Base.typed_vcat, Base.typed_hvcat + args = ex0.args[2:end] + end + if any(a->isa(a,Expr) && a.head === :row, args) + rows = Any[ (isa(x,Expr) && x.head === :row ? x.args : Any[x]) for x in args ] + lens = map(length, rows) + return Expr(:call, fcn, hf, + Expr(:call, typesof, + (ex0.head === :vcat ? [] : Any[esc(ex0.args[1])])..., + Expr(:tuple, lens...), + map(esc, vcat(rows...))...)) + else + return Expr(:call, fcn, f, + Expr(:call, typesof, map(esc, ex0.args)...)) + end else - for (head, f) in (:ref => Base.getindex, :vcat => Base.vcat, :hcat => Base.hcat, :(.) => Base.getproperty, :vect => Base.vect) - if ex0.head == head + for (head, f) in (:ref => Base.getindex, :hcat => Base.hcat, :(.) => Base.getproperty, :vect => Base.vect, Symbol("'") => Base.adjoint, :typed_hcat => Base.typed_hcat, :string => string) + if ex0.head === head return Expr(:call, fcn, f, Expr(:call, typesof, map(esc, ex0.args)...)) end end end end - if isa(ex0, Expr) && ex0.head == :macrocall # Make @edit @time 1+2 edit the macro by using the types of the *expressions* + if isa(ex0, Expr) && ex0.head === :macrocall # Make @edit @time 1+2 edit the macro by using the types of the *expressions* 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 @@ -40,8 +68,8 @@ function gen_call_with_extracted_types(__module__, fcn, ex0) end exret = Expr(:none) - if ex.head == :call - if any(e->(isa(e, Expr) && e.head==:(...)), ex0.args) && + if ex.head === :call + if any(e->(isa(e, Expr) && e.head === :(...)), ex0.args) && (ex.args[1] === GlobalRef(Core,:_apply) || ex.args[1] === GlobalRef(Base,:_apply)) # check for splatting @@ -53,7 +81,7 @@ function gen_call_with_extracted_types(__module__, fcn, ex0) Expr(:call, typesof, map(esc, ex.args[2:end])...)) end end - if 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") diff --git a/stdlib/InteractiveUtils/test/runtests.jl b/stdlib/InteractiveUtils/test/runtests.jl index 14a5068c36c6d..3e522ce24a54e 100644 --- a/stdlib/InteractiveUtils/test/runtests.jl +++ b/stdlib/InteractiveUtils/test/runtests.jl @@ -198,20 +198,23 @@ const curmod_str = curmod === Main ? "Main" : join(curmod_name, ".") # issue #13264 @test (@which vcat(1...)).name == :vcat -# PR #28122 +# PR #28122, issue #25474 @test (@which [1][1]).name === :getindex -@test (@which [1 2]).name == :hcat -@test (@which [1; 2]).name == :vcat -@test (@which [1]).name == :vect +@test (@which [1][1] = 2).name === :setindex! +@test (@which [1]).name === :vect +@test (@which [1 2]).name === :hcat +@test (@which [1; 2]).name === :vcat +@test (@which Int[1 2]).name === :typed_hcat +@test (@which Int[1; 2]).name === :typed_vcat +@test (@which [1 2;3 4]).name === :hvcat +@test (@which Int[1 2;3 4]).name === :typed_hvcat # issue #13464 -let t13464 = "hey there sailor" - try - @which t13464[1,1] = (1.0,true) - error("unexpected") - catch err13464 - @test startswith(err13464.msg, "expression is not a function call, or is too complex") - end +try + @which x = 1 + error("unexpected") +catch err13464 + @test startswith(err13464.msg, "expression is not a function call, or is too complex") end module MacroTest diff --git a/stdlib/LibGit2/src/LibGit2.jl b/stdlib/LibGit2/src/LibGit2.jl index 133407b9a2ac4..38175f1c3eb7b 100644 --- a/stdlib/LibGit2/src/LibGit2.jl +++ b/stdlib/LibGit2/src/LibGit2.jl @@ -978,7 +978,7 @@ end atexit() do # refcount zero, no objects to be finalized - if Threads.atomic_sub!(REFCOUNT, 1) >= 1 + if Threads.atomic_sub!(REFCOUNT, 1) == 1 ccall((:git_libgit2_shutdown, :libgit2), Cint, ()) end end diff --git a/stdlib/LinearAlgebra/src/LinearAlgebra.jl b/stdlib/LinearAlgebra/src/LinearAlgebra.jl index 48f5b34ca81f7..f84e56883b436 100644 --- a/stdlib/LinearAlgebra/src/LinearAlgebra.jl +++ b/stdlib/LinearAlgebra/src/LinearAlgebra.jl @@ -60,7 +60,6 @@ export axpby!, bunchkaufman, bunchkaufman!, - chol, cholesky, cholesky!, cond, @@ -134,7 +133,6 @@ export tr, transpose, transpose!, - transpose_type, tril, triu, tril!, diff --git a/stdlib/LinearAlgebra/src/adjtrans.jl b/stdlib/LinearAlgebra/src/adjtrans.jl index f75c42117ec8e..208c2f884c563 100644 --- a/stdlib/LinearAlgebra/src/adjtrans.jl +++ b/stdlib/LinearAlgebra/src/adjtrans.jl @@ -150,7 +150,7 @@ similar(A::AdjOrTrans, ::Type{T}, dims::Dims{N}) where {T,N} = similar(A.parent, # sundry basic definitions parent(A::AdjOrTrans) = A.parent -vec(v::AdjOrTransAbsVec) = v.parent +vec(v::TransposeAbsVec) = parent(v) cmp(A::AdjOrTransAbsVec, B::AdjOrTransAbsVec) = cmp(parent(A), parent(B)) isless(A::AdjOrTransAbsVec, B::AdjOrTransAbsVec) = isless(parent(A), parent(B)) diff --git a/stdlib/LinearAlgebra/src/cholesky.jl b/stdlib/LinearAlgebra/src/cholesky.jl index 3e95a92d3a0d4..5db318fabf999 100644 --- a/stdlib/LinearAlgebra/src/cholesky.jl +++ b/stdlib/LinearAlgebra/src/cholesky.jl @@ -340,11 +340,11 @@ function getproperty(C::Cholesky, d::Symbol) Cuplo = getfield(C, :uplo) info = getfield(C, :info) if d == :U - return UpperTriangular(Symbol(Cuplo) == d ? Cfactors : copy(Cfactors')) + return UpperTriangular(Cuplo === char_uplo(d) ? Cfactors : copy(Cfactors')) elseif d == :L - return LowerTriangular(Symbol(Cuplo) == d ? Cfactors : copy(Cfactors')) + return LowerTriangular(Cuplo === char_uplo(d) ? Cfactors : copy(Cfactors')) elseif d == :UL - return (Symbol(Cuplo) == :U ? UpperTriangular(Cfactors) : LowerTriangular(Cfactors)) + return (Cuplo === 'U' ? UpperTriangular(Cfactors) : LowerTriangular(Cfactors)) else return getfield(C, d) end diff --git a/stdlib/LinearAlgebra/src/dense.jl b/stdlib/LinearAlgebra/src/dense.jl index 8a4c9a857f514..c15a1bd0dcfbd 100644 --- a/stdlib/LinearAlgebra/src/dense.jl +++ b/stdlib/LinearAlgebra/src/dense.jl @@ -541,10 +541,10 @@ function exp!(A::StridedMatrix{T}) where T<:BlasFloat A2 = A * A A4 = A2 * A2 A6 = A2 * A4 - U = A * (A6 * (CC[14]*A6 + CC[12]*A4 + CC[10]*A2) + - CC[8]*A6 + CC[6]*A4 + CC[4]*A2 + CC[2]*Inn) - V = A6 * (CC[13]*A6 + CC[11]*A4 + CC[9]*A2) + - CC[7]*A6 + CC[5]*A4 + CC[3]*A2 + CC[1]*Inn + U = A * (A6 * (CC[14].*A6 .+ CC[12].*A4 .+ CC[10].*A2) .+ + CC[8].*A6 .+ CC[6].*A4 .+ CC[4].*A2 .+ CC[2].*Inn) + V = A6 * (CC[13].*A6 .+ CC[11].*A4 .+ CC[9].*A2) .+ + CC[7].*A6 .+ CC[5].*A4 .+ CC[3].*A2 .+ CC[1].*Inn X = V + U LAPACK.gesv!(V-U, X) diff --git a/stdlib/LinearAlgebra/src/diagonal.jl b/stdlib/LinearAlgebra/src/diagonal.jl index 2d2557510cca0..1a5eca3c0ce05 100644 --- a/stdlib/LinearAlgebra/src/diagonal.jl +++ b/stdlib/LinearAlgebra/src/diagonal.jl @@ -277,9 +277,9 @@ mul!(out::AbstractVector, A::Diagonal, in::AbstractVector) = out .= A.diag .* in mul!(out::AbstractVector, A::Adjoint{<:Any,<:Diagonal}, in::AbstractVector) = out .= adjoint.(A.parent.diag) .* in mul!(out::AbstractVector, A::Transpose{<:Any,<:Diagonal}, in::AbstractVector) = out .= transpose.(A.parent.diag) .* in -mul!(out::AbstractMatrix, A::Diagonal, in::AbstractMatrix) = out .= A.diag .* in -mul!(out::AbstractMatrix, A::Adjoint{<:Any,<:Diagonal}, in::AbstractMatrix) = out .= adjoint.(A.parent.diag) .* in -mul!(out::AbstractMatrix, A::Transpose{<:Any,<:Diagonal}, in::AbstractMatrix) = out .= transpose.(A.parent.diag) .* in +mul!(out::AbstractMatrix, A::Diagonal, in::StridedMatrix) = out .= A.diag .* in +mul!(out::AbstractMatrix, A::Adjoint{<:Any,<:Diagonal}, in::StridedMatrix) = out .= adjoint.(A.parent.diag) .* in +mul!(out::AbstractMatrix, A::Transpose{<:Any,<:Diagonal}, in::StridedMatrix) = out .= transpose.(A.parent.diag) .* in # ambiguities with Symmetric/Hermitian # RealHermSymComplex[Sym]/[Herm] only include Number; invariant to [c]transpose diff --git a/stdlib/LinearAlgebra/src/generic.jl b/stdlib/LinearAlgebra/src/generic.jl index ac55b9ed696ae..ca747db604fc8 100644 --- a/stdlib/LinearAlgebra/src/generic.jl +++ b/stdlib/LinearAlgebra/src/generic.jl @@ -1274,7 +1274,7 @@ julia> logabsdet(B) (0.6931471805599453, 1.0) ``` """ -logabsdet(A::AbstractMatrix) = logabsdet(lu(A)) +logabsdet(A::AbstractMatrix) = logabsdet(lu(A, check=false)) """ logdet(M) @@ -1324,8 +1324,8 @@ julia> promote_leaf_eltypes(a) Complex{Float64} ``` """ -promote_leaf_eltypes(x::Union{AbstractArray{T},Tuple{Vararg{T}}}) where {T<:Number} = T -promote_leaf_eltypes(x::Union{AbstractArray{T},Tuple{Vararg{T}}}) where {T<:NumberArray} = eltype(T) +promote_leaf_eltypes(x::Union{AbstractArray{T},Tuple{T,Vararg{T}}}) where {T<:Number} = T +promote_leaf_eltypes(x::Union{AbstractArray{T},Tuple{T,Vararg{T}}}) where {T<:NumberArray} = eltype(T) promote_leaf_eltypes(x::T) where {T} = T promote_leaf_eltypes(x::Union{AbstractArray,Tuple}) = mapreduce(promote_leaf_eltypes, promote_type, x; init=Bool) diff --git a/stdlib/LinearAlgebra/src/triangular.jl b/stdlib/LinearAlgebra/src/triangular.jl index ca5f4d2e0e014..93f54f7ee5a2f 100644 --- a/stdlib/LinearAlgebra/src/triangular.jl +++ b/stdlib/LinearAlgebra/src/triangular.jl @@ -58,7 +58,7 @@ UpperTriangular(U::LowerTriangular) = throw(ArgumentError( """ LowerTriangular(A::AbstractMatrix) -Construct a `LowerTriangular` view of the the matrix `A`. +Construct a `LowerTriangular` view of the matrix `A`. # Examples ```jldoctest @@ -79,7 +79,7 @@ LowerTriangular """ UpperTriangular(A::AbstractMatrix) -Construct an `UpperTriangular` view of the the matrix `A`. +Construct an `UpperTriangular` view of the matrix `A`. # Examples ```jldoctest diff --git a/stdlib/LinearAlgebra/test/adjtrans.jl b/stdlib/LinearAlgebra/test/adjtrans.jl index 5abba308fcec0..ba3faac50374d 100644 --- a/stdlib/LinearAlgebra/test/adjtrans.jl +++ b/stdlib/LinearAlgebra/test/adjtrans.jl @@ -271,8 +271,10 @@ end @testset "Adjoint and Transpose vector vec methods" begin intvec = [1, 2] - @test vec(Adjoint(intvec)) === intvec + @test vec(Adjoint(intvec)) == intvec @test vec(Transpose(intvec)) === intvec + cvec = [1 + 1im] + @test vec(cvec')[1] == cvec[1]' end @testset "horizontal concatenation of Adjoint/Transpose-wrapped vectors and Numbers" begin diff --git a/stdlib/LinearAlgebra/test/ambiguous_exec.jl b/stdlib/LinearAlgebra/test/ambiguous_exec.jl new file mode 100644 index 0000000000000..6dce4926f4610 --- /dev/null +++ b/stdlib/LinearAlgebra/test/ambiguous_exec.jl @@ -0,0 +1,4 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +using Test, LinearAlgebra +@test detect_ambiguities(LinearAlgebra; imported=true, recursive=true) == [] diff --git a/stdlib/LinearAlgebra/test/generic.jl b/stdlib/LinearAlgebra/test/generic.jl index 1c38883356efb..13d18e1d29b23 100644 --- a/stdlib/LinearAlgebra/test/generic.jl +++ b/stdlib/LinearAlgebra/test/generic.jl @@ -80,6 +80,8 @@ n = 5 # should be odd @test logdet(A) ≈ log(det(A)) @test logabsdet(A)[1] ≈ log(abs(det(A))) @test logabsdet(Matrix{elty}(-I, n, n))[2] == -1 + infinity = convert(float(elty), Inf) + @test logabsdet(zeros(elty, n, n)) == (-infinity, zero(elty)) if elty <: Real @test logabsdet(A)[2] == sign(det(A)) @test_throws DomainError logdet(Matrix{elty}(-I, n, n)) diff --git a/stdlib/LinearAlgebra/test/matmul.jl b/stdlib/LinearAlgebra/test/matmul.jl index 7ac8924e63447..bd1cc3072bc79 100644 --- a/stdlib/LinearAlgebra/test/matmul.jl +++ b/stdlib/LinearAlgebra/test/matmul.jl @@ -445,4 +445,12 @@ end @test Xv1'*Xv3' ≈ XcXc end +@testset "method ambiguity" begin + # Ambiguity test is run inside a clean process. + # https://github.com/JuliaLang/julia/issues/28804 + script = joinpath(@__DIR__, "ambiguous_exec.jl") + cmd = `$(Base.julia_cmd()) --startup-file=no $script` + @test success(pipeline(cmd; stdout=stdout, stderr=stderr)) +end + end # module TestMatmul diff --git a/stdlib/Logging/docs/src/index.md b/stdlib/Logging/docs/src/index.md index ba07f6602f3ae..bed0b1b0d2e98 100644 --- a/stdlib/Logging/docs/src/index.md +++ b/stdlib/Logging/docs/src/index.md @@ -193,6 +193,43 @@ Similarly, the environment variable can be used to enable debug logging of modules, such as `Pkg`, or module roots (see [`Base.moduleroot`](@ref)). To enable all debug logging, use the special value `all`. +## Writing log events to a file + +Sometimes it can be useful to write log events to a file. Here is an example +of how to use a task-local and global logger to write information to a text +file: + +```julia-repl +# Load the logging module +julia> using Logging + +# Open a textfile for writing +julia> io = open("log.txt", "w+") +IOStream() + +# Create a simple logger +julia> logger = SimpleLogger(io) +SimpleLogger(IOStream(), Info, Dict{Any,Int64}()) + +# Log a task-specific message +julia> with_logger(logger) do + @info("a context specific log message") + end + +# Write all buffered messages to the file +julia> flush(io) + +# Set the global logger to logger +julia> global_logger(logger) +SimpleLogger(IOStream(), Info, Dict{Any,Int64}()) + +# This message will now also be written to the file +julia> @info("a global log message") + +# Close the file +julia> close(io) +``` + ## Reference diff --git a/stdlib/Pkg/docs/src/index.md b/stdlib/Pkg/docs/src/index.md index 7e01e44a08584..66ed06686add9 100644 --- a/stdlib/Pkg/docs/src/index.md +++ b/stdlib/Pkg/docs/src/index.md @@ -81,7 +81,7 @@ identify the package in projects that depend on it. **Application:** a project which provides standalone functionality not intended to be reused by other Julia projects. For example a web application or a -commmand-line utility, or simulation/analytics code accompanying a scientific paper. +command-line utility, or simulation/analytics code accompanying a scientific paper. An application may have a UUID but does not need one. An application may also provide global configuration options for packages it depends on. Packages, on the other hand, may not provide global configuration @@ -146,7 +146,7 @@ format using ranges of package versions. **Depot:** a directory on a system where various package-related resources live, including: -- `environments`: shared named environments (e.g. `v0.7`, `devtools`) +- `environments`: shared named environments (e.g. `v1.0`, `devtools`) - `clones`: bare clones of package repositories - `compiled`: cached compiled package images (`.ji` files) - `config`: global configuration files (e.g. `startup.jl`) @@ -181,11 +181,11 @@ managed by system administrators. The Pkg REPL-mode is entered from the Julia REPL using the key `]`. ``` -(v0.7) pkg> +(v1.0) pkg> ``` The part inside the parenthesis of the prompt shows the name of the current project. -Since we haven't created our own project yet, we are in the default project, located at `~/.julia/environments/v0.7` +Since we haven't created our own project yet, we are in the default project, located at `~/.julia/environments/v1.0` (or whatever version of Julia you happen to run). To return to the `julia>` prompt, either press backspace when the input line is empty or press Ctrl+C. @@ -207,15 +207,15 @@ The most frequently used one is `add` and its usage is described first. In the Pkg REPL packages can be added with the `add` command followed by the name of the package, for example: ``` -(v0.7) pkg> add Example +(v1.0) pkg> add Example Cloning default registries into /Users/kristoffer/.julia/registries Cloning registry General from "https://github.com/JuliaRegistries/General.git" Updating registry at `~/.julia/registries/General` Updating git-repo `https://github.com/JuliaRegistries/General.git` Resolving package versions... - Updating `~/.julia/environments/v0.7/Project.toml` + Updating `~/.julia/environments/v1.0/Project.toml` [7876af07] + Example v0.5.1 - Updating `~/.julia/environments/v0.7/Manifest.toml` + Updating `~/.julia/environments/v1.0/Manifest.toml` [7876af07] + Example v0.5.1 [8dfed614] + Test ``` @@ -228,7 +228,7 @@ Since standard libraries (e.g. `Test`) are shipped with Julia, they do not have you have added yourself, in this case, `Example`: ``` -(v0.7) pkg> st +(v1.0) pkg> st Status `Project.toml` [7876af07] Example v0.5.1 ``` @@ -236,7 +236,7 @@ you have added yourself, in this case, `Example`: The manifest status, in addition, includes the dependencies of explicitly added packages. ``` -(v0.7) pkg> st --manifest +(v1.0) pkg> st --manifest Status `Manifest.toml` [7876af07] Example v0.5.1 [8dfed614] Test @@ -256,11 +256,11 @@ julia> Example.hello("User") A specific version can be installed by appending a version after a `@` symbol, e.g. `@v0.4`, to the package name: ``` -(v0.7) pkg> add Example@0.4 +(v1.0) pkg> add Example@0.4 Resolving package versions... - Updating `~/.julia/environments/v0.7/Project.toml` + Updating `~/.julia/environments/v1.0/Project.toml` [7876af07] + Example v0.4.1 - Updating `~/.julia/environments/v0.7/Manifest.toml` + Updating `~/.julia/environments/v1.0/Manifest.toml` [7876af07] + Example v0.4.1 ``` @@ -268,12 +268,12 @@ If the master branch (or a certain commit SHA) of `Example` has a hotfix that ha we can explicitly track a branch (or commit) by appending `#branch` (or `#commit`) to the package name: ``` -(v0.7) pkg> add Example#master +(v1.0) pkg> add Example#master Updating git-repo `https://github.com/JuliaLang/Example.jl.git` Resolving package versions... - Updating `~/.julia/environments/v0.7/Project.toml` + Updating `~/.julia/environments/v1.0/Project.toml` [7876af07] ~ Example v0.5.1 ⇒ v0.5.1+ #master (https://github.com/JuliaLang/Example.jl.git) - Updating `~/.julia/environments/v0.7/Manifest.toml` + Updating `~/.julia/environments/v1.0/Manifest.toml` [7876af07] ~ Example v0.5.1 ⇒ v0.5.1+ #master (https://github.com/JuliaLang/Example.jl.git) ``` @@ -283,11 +283,11 @@ When updating packages, we will pull updates from that branch. To go back to tracking the registry version of `Example`, the command `free` is used: ``` -(v0.7) pkg> free Example +(v1.0) pkg> free Example Resolving package versions... - Updating `~/.julia/environments/v0.7/Project.toml` + Updating `~/.julia/environments/v1.0/Project.toml` [7876af07] ~ Example v0.5.1+ #master (https://github.com/JuliaLang/Example.jl.git) ⇒ v0.5.1 - Updating `~/.julia/environments/v0.7/Manifest.toml` + Updating `~/.julia/environments/v1.0/Manifest.toml` [7876af07] ~ Example v0.5.1+ #master )https://github.com/JuliaLang/Example.jl.git) ⇒ v0.5.1 ``` @@ -297,13 +297,13 @@ To go back to tracking the registry version of `Example`, the command `free` is If a package is not in a registry, it can still be added by instead of the package name giving the URL to the repository to `add`. ``` -(v0.7) pkg> add https://github.com/fredrikekre/ImportMacros.jl +(v1.0) pkg> add https://github.com/fredrikekre/ImportMacros.jl Updating git-repo `https://github.com/fredrikekre/ImportMacros.jl` Resolving package versions... Downloaded MacroTools ─ v0.4.1 - Updating `~/.julia/environments/v0.7/Project.toml` + Updating `~/.julia/environments/v1.0/Project.toml` [e6797606] + ImportMacros v0.0.0 # (https://github.com/fredrikekre/ImportMacros.jl) - Updating `~/.julia/environments/v0.7/Manifest.toml` + Updating `~/.julia/environments/v1.0/Manifest.toml` [e6797606] + ImportMacros v0.0.0 # (https://github.com/fredrikekre/ImportMacros.jl) [1914dd2f] + MacroTools v0.4.1 ``` @@ -330,12 +330,12 @@ However, when you are developing a package, it is more convenient to load packag Let's try to `dev` a registered package: ``` -(v0.7) pkg> dev Example +(v1.0) pkg> dev Example Updating git-repo `https://github.com/JuliaLang/Example.jl.git` Resolving package versions... - Updating `~/.julia/environments/v0.7/Project.toml` + Updating `~/.julia/environments/v1.0/Project.toml` [7876af07] + Example v0.5.1+ [`~/.julia/dev/Example`] - Updating `~/.julia/environments/v0.7/Manifest.toml` + Updating `~/.julia/environments/v1.0/Manifest.toml` [7876af07] + Example v0.5.1+ [`~/.julia/dev/Example`] ``` @@ -347,7 +347,7 @@ If we try to `dev` a package at some branch that already exists at `~/.julia/dev For example: ``` -(v0.7) pkg> dev Example +(v1.0) pkg> dev Example Updating git-repo `https://github.com/JuliaLang/Example.jl.git` [ Info: Path `/Users/kristoffer/.julia/dev/Example` exists and looks like the correct package, using existing path instead of cloning ``` @@ -361,11 +361,11 @@ The path will be recorded relative to the project file, unless it is given as an To stop tracking a path and use the registered version again, use `free` ``` -(v0.7) pkg> free Example +(v1.0) pkg> free Example Resolving package versions... - Updating `~/.julia/environments/v0.7/Project.toml` + Updating `~/.julia/environments/v1.0/Project.toml` [7876af07] ↓ Example v0.5.1+ [`~/.julia/dev/Example`] ⇒ v0.5.1 - Updating `~/.julia/environments/v0.7/Manifest.toml` + Updating `~/.julia/environments/v1.0/Manifest.toml` [7876af07] ↓ Example v0.5.1+ [`~/.julia/dev/Example`] ⇒ v0.5.1 ``` @@ -390,13 +390,13 @@ When new versions of packages the project is using are released, it is a good id to the latest compatible version. Sometimes this is not what you want. You can specify a subset of the dependencies to upgrade by giving them as arguments to `up`, e.g: ``` -(v0.7) pkg> up Example +(v1.0) pkg> up Example ``` The version of all other packages direct dependencies will stay the same. If you only want to update the minor version of packages, to reduce the risk that your project breaks, you can give the `--minor` flag, e.g: ``` -(v0.7) pkg> up --minor Example +(v1.0) pkg> up --minor Example ``` Packages that track a repository are not updated when a minor upgrade is done. @@ -407,30 +407,30 @@ Packages that track a path are never touched by the package manager. A pinned package will never be updated. A package can be pinned using `pin` as for example ``` -(v0.7) pkg> pin Example +(v1.0) pkg> pin Example Resolving package versions... - Updating `~/.julia/environments/v0.7/Project.toml` + Updating `~/.julia/environments/v1.0/Project.toml` [7876af07] ~ Example v0.5.1 ⇒ v0.5.1 ⚲ - Updating `~/.julia/environments/v0.7/Manifest.toml` + Updating `~/.julia/environments/v1.0/Manifest.toml` [7876af07] ~ Example v0.5.1 ⇒ v0.5.1 ⚲ ``` Note the pin symbol `⚲` showing that the package is pinned. Removing the pin is done using `free` ``` -(v0.7) pkg> free Example - Updating `~/.julia/environments/v0.7/Project.toml` +(v1.0) pkg> free Example + Updating `~/.julia/environments/v1.0/Project.toml` [7876af07] ~ Example v0.5.1 ⚲ ⇒ v0.5.1 - Updating `~/.julia/environments/v0.7/Manifest.toml` + Updating `~/.julia/environments/v1.0/Manifest.toml` [7876af07] ~ Example v0.5.1 ⚲ ⇒ v0.5.1 ``` ### Testing packages -The tests for a package can be run using `test`command: +The tests for a package can be run using `test` command: ``` -(v0.7) pkg> test Example +(v1.0) pkg> test Example Testing Example Testing Example tests passed ``` @@ -442,7 +442,7 @@ The output of the build process is directed to a file. To explicitly run the build step for a package the `build` command is used: ``` -(v0.7) pkg> build MbedTLS +(v1.0) pkg> build MbedTLS Building MbedTLS → `~/.julia/packages/MbedTLS/h1Vu/deps/build.log` shell> cat ~/.julia/packages/MbedTLS/h1Vu/deps/build.log @@ -455,7 +455,7 @@ shell> cat ~/.julia/packages/MbedTLS/h1Vu/deps/build.log ## Creating your own projects -So far we have added packages to the default project at `~/.julia/environments/v0.7`, it is, however, easy to create other, independent, projects. +So far we have added packages to the default project at `~/.julia/environments/v1.0`, it is, however, easy to create other, independent, projects. It should be pointed out if two projects uses the same package at the same version, the content of this package is not duplicated. In order to create a new project, create a directory for it and then activate that directory to make it the "active project" which package operations manipulate: @@ -465,7 +465,7 @@ shell> mkdir MyProject shell> cd MyProject /Users/kristoffer/MyProject -(v0.7) pkg> activate . +(v1.0) pkg> activate . (MyProject) pkg> st Status `Project.toml` @@ -518,7 +518,7 @@ and what packages those projects used. The rest can be deleted. This is done with the `gc` command: ``` -(v0.7) pkg> gc +(v1.0) pkg> gc Active manifests at: `/Users/kristoffer/BinaryProvider/Manifest.toml` ... @@ -542,7 +542,7 @@ This file is executed when the package is loaded. To generate files for a new package, use `pkg> generate`. ``` -(v0.7) pkg> generate HelloWorld +(v1.0) pkg> generate HelloWorld ``` This creates a new project `HelloWorld` with the following files (visualized with the external [`tree` command](https://linux.die.net/man/1/tree)): @@ -731,6 +731,7 @@ Compatibility for a dependency is entered in the `Project.toml` file as for exam ```toml [compat] +julia = "1.0" Example = "0.4.3" ``` @@ -825,7 +826,7 @@ However, nothing would be installed and your `Project.toml` and `Manifest.toml` Simply clone their project using e.g. `git clone`, `cd` to the project directory and call ``` -(v0.7) pkg> activate . +(v1.0) pkg> activate . (SomeProject) pkg> instantiate ``` diff --git a/stdlib/Pkg/src/API.jl b/stdlib/Pkg/src/API.jl index a4cc2a363f682..62b5f67de8fad 100644 --- a/stdlib/Pkg/src/API.jl +++ b/stdlib/Pkg/src/API.jl @@ -29,6 +29,7 @@ add_or_develop(pkgs::Vector{String}; kwargs...) = add_or_develop([che add_or_develop(pkgs::Vector{PackageSpec}; kwargs...) = add_or_develop(Context(), pkgs; kwargs...) function add_or_develop(ctx::Context, pkgs::Vector{PackageSpec}; mode::Symbol, shared::Bool=true, kwargs...) + pkgs = deepcopy(pkgs) # deepcopy for avoid mutating PackageSpec members Context!(ctx; kwargs...) # All developed packages should go through handle_repos_develop so just give them an empty repo @@ -48,8 +49,7 @@ function add_or_develop(ctx::Context, pkgs::Vector{PackageSpec}; mode::Symbol, s ctx.preview && preview_info() if mode == :develop - devdir = shared ? Pkg.devdir() : joinpath(dirname(ctx.env.project_file), "dev") - new_git = handle_repos_develop!(ctx, pkgs, devdir) + new_git = handle_repos_develop!(ctx, pkgs, shared = shared) else new_git = handle_repos_add!(ctx, pkgs; upgrade_or_add=true) end @@ -74,8 +74,9 @@ rm(pkgs::Vector{String}; kwargs...) = rm([PackageSpec(pkg) for pkg in rm(pkgs::Vector{PackageSpec}; kwargs...) = rm(Context(), pkgs; kwargs...) function rm(ctx::Context, pkgs::Vector{PackageSpec}; mode=PKGMODE_PROJECT, kwargs...) + pkgs = deepcopy(pkgs) # deepcopy for avoid mutating PackageSpec members for pkg in pkgs - #TODO only overwrite pkg.mode is default value ? + # TODO only overwrite pkg.mode if default value ? pkg.mode = mode end @@ -97,16 +98,25 @@ function update_registry(ctx) else for reg in registries() if isdir(joinpath(reg, ".git")) - regpath = pathrepr(ctx, reg) + regpath = pathrepr(reg) printpkgstyle(ctx, :Updating, "registry at " * regpath) - LibGit2.with(LibGit2.GitRepo, reg) do repo + # Using LibGit2.with here crashes julia when running the + # tests for PkgDev wiht "Unreachable reached". + # This seems to work around it. + local repo + try + repo = LibGit2.GitRepo(reg) if LibGit2.isdirty(repo) push!(errors, (regpath, "registry dirty")) - return + @goto done end if !LibGit2.isattached(repo) push!(errors, (regpath, "registry detached")) - return + @goto done + end + if !("origin" in LibGit2.remotes(repo)) + push!(errors, (regpath, "origin not in the list of remotes")) + @goto done end branch = LibGit2.headname(repo) try @@ -114,14 +124,14 @@ function update_registry(ctx) catch e e isa PkgError || rethrow(e) push!(errors, (reg, "failed to fetch from repo")) - return + @goto done end ff_succeeded = try LibGit2.merge!(repo; branch="refs/remotes/origin/$branch", fastforward=true) catch e e isa LibGit2.GitError && e.code == LibGit2.Error.ENOTFOUND || rethrow(e) push!(errors, (reg, "branch origin/$branch not found")) - return + @goto done end if !ff_succeeded @@ -129,9 +139,12 @@ function update_registry(ctx) catch e e isa LibGit2.GitError || rethrow(e) push!(errors, (reg, "registry failed to rebase on origin/$branch")) - return + @goto done end end + @label done + finally + close(repo) end end end @@ -155,6 +168,7 @@ up(pkgs::Vector{PackageSpec}; kwargs...) = up(Context(), pkgs; kwargs...) function up(ctx::Context, pkgs::Vector{PackageSpec}; level::UpgradeLevel=UPLEVEL_MAJOR, mode::PackageMode=PKGMODE_PROJECT, do_update_registry=true, kwargs...) + pkgs = deepcopy(pkgs) # deepcopy for avoid mutating PackageSpec members for pkg in pkgs # TODO only override if they are not already set pkg.mode = mode @@ -194,6 +208,7 @@ pin(pkgs::Vector{String}; kwargs...) = pin([PackageSpec(pkg) for pkg pin(pkgs::Vector{PackageSpec}; kwargs...) = pin(Context(), pkgs; kwargs...) function pin(ctx::Context, pkgs::Vector{PackageSpec}; kwargs...) + pkgs = deepcopy(pkgs) # deepcopy for avoid mutating PackageSpec members Context!(ctx; kwargs...) ctx.preview && preview_info() project_deps_resolve!(ctx.env, pkgs) @@ -208,6 +223,7 @@ free(pkgs::Vector{String}; kwargs...) = free([PackageSpec(pkg) for pk free(pkgs::Vector{PackageSpec}; kwargs...) = free(Context(), pkgs; kwargs...) function free(ctx::Context, pkgs::Vector{PackageSpec}; kwargs...) + pkgs = deepcopy(pkgs) # deepcopy for avoid mutating PackageSpec members Context!(ctx; kwargs...) ctx.preview && preview_info() registry_resolve!(ctx.env, pkgs) @@ -239,6 +255,7 @@ test(pkgs::Vector{String}; kwargs...) = test([PackageSpec(pkg) for p test(pkgs::Vector{PackageSpec}; kwargs...) = test(Context(), pkgs; kwargs...) function test(ctx::Context, pkgs::Vector{PackageSpec}; coverage=false, kwargs...) + pkgs = deepcopy(pkgs) # deepcopy for avoid mutating PackageSpec members Context!(ctx; kwargs...) ctx.preview && preview_info() if isempty(pkgs) @@ -408,6 +425,7 @@ build(pkg::Array{Union{}, 1}) = build(PackageSpec[]) build(pkg::PackageSpec) = build([pkg]) build(pkgs::Vector{PackageSpec}) = build(Context(), pkgs) function build(ctx::Context, pkgs::Vector{PackageSpec}; kwargs...) + pkgs = deepcopy(pkgs) # deepcopy for avoid mutating PackageSpec members Context!(ctx; kwargs...) ctx.preview && preview_info() @@ -472,10 +490,9 @@ function precompile(ctx::Context) for pkg in pkgids paths = Base.find_all_in_cache_path(pkg) sourcepath = Base.locate_package(pkg) - if sourcepath == nothing - # XXX: this isn't supposed to be fatal - pkgerror("couldn't find path to $(pkg.name) when trying to precompilie project") - end + sourcepath == nothing && continue + # Heuristic for when precompilation is disabled + occursin(r"\b__precompile__\(\s*false\s*\)", read(sourcepath, String)) && continue stale = true for path_to_try in paths::Vector{String} staledeps = Base.stale_cachefile(sourcepath, path_to_try) @@ -572,7 +589,7 @@ function activate(path::String; shared::Bool=false) fullpath = abspath(devpath) else fullpath = abspath(path) - isdir(fullpath) || @info("new environment will be placed at $fullpath") + isdir(fullpath) || @info("activating new environment at $(Base.contractuser(fullpath)).") end else # initialize `fullpath` in case of empty `Pkg.depots()` @@ -589,7 +606,7 @@ function activate(path::String; shared::Bool=false) # unless the shared environment already exists, place it in the first depots if !isdir(fullpath) fullpath = joinpath(Pkg.envdir(Pkg.depots1()), path) - @info("new shared environment \"$path\" will be placed at $fullpath") + @info("activating new environment at $(Base.contractuser(fullpath)).") end end Base.ACTIVE_PROJECT[] = Base.load_path_expand(fullpath) diff --git a/stdlib/Pkg/src/Display.jl b/stdlib/Pkg/src/Display.jl index 415e0ba5d57c9..030d206db1069 100644 --- a/stdlib/Pkg/src/Display.jl +++ b/stdlib/Pkg/src/Display.jl @@ -56,14 +56,14 @@ function status(ctx::Context, mode::PackageMode, use_as_api=false) m₁ = filter_manifest(in_project(project₁["deps"]), manifest₁) diff = manifest_diff(ctx, m₀, m₁) if !use_as_api - printpkgstyle(ctx, :Status, pathrepr(ctx, env.project_file), #=ignore_indent=# true) + printpkgstyle(ctx, :Status, pathrepr(env.project_file), #=ignore_indent=# true) print_diff(ctx, diff, #=status=# true) end end if mode == PKGMODE_MANIFEST diff = manifest_diff(ctx, manifest₀, manifest₁) if !use_as_api - printpkgstyle(ctx, :Status, pathrepr(ctx, env.manifest_file), #=ignore_indent=# true) + printpkgstyle(ctx, :Status, pathrepr(env.manifest_file), #=ignore_indent=# true) print_diff(ctx, diff, #=status=# true) end elseif mode == PKGMODE_COMBINED @@ -73,7 +73,7 @@ function status(ctx::Context, mode::PackageMode, use_as_api=false) c_diff = filter!(x->x.old != x.new, manifest_diff(ctx, m₀, m₁)) if !isempty(c_diff) if !use_as_api - printpkgstyle(ctx, :Status, pathrepr(ctx, env.manifest_file), #=ignore_indent=# true) + printpkgstyle(ctx, :Status, pathrepr(env.manifest_file), #=ignore_indent=# true) print_diff(ctx, c_diff, #=status=# true) end diff = Base.vcat(c_diff, diff) @@ -116,7 +116,7 @@ revstring(str::String) = occursin(r"\b([a-f0-9]{40})\b", str) ? str[1:7] : str vstring(ctx::Context, a::VerInfo) = string((a.ver == nothing && a.hash != nothing) ? "[$(string(a.hash)[1:16])]" : "", a.ver != nothing ? "v$(a.ver)" : "", - a.path != nothing ? " [$(pathrepr(ctx, a.path))]" : "", + a.path != nothing ? " [$(pathrepr(a.path))]" : "", a.repo != nothing ? " #$(revstring(a.repo.rev)) ($(a.repo.url))" : "", a.pinned == true ? " ⚲" : "", ) diff --git a/stdlib/Pkg/src/GitTools.jl b/stdlib/Pkg/src/GitTools.jl index acd988ead9bd9..d480f46b02f03 100644 --- a/stdlib/Pkg/src/GitTools.jl +++ b/stdlib/Pkg/src/GitTools.jl @@ -75,8 +75,13 @@ setprotocol!(proto::Union{Nothing, AbstractString}=nothing) = GIT_PROTOCOL[] = p # TODO: extend this to more urls function normalize_url(url::AbstractString) m = match(GITHUB_REGEX, url) - (m === nothing || GIT_PROTOCOL[] === nothing) ? - url : "$(GIT_PROTOCOL[])://github.com/$(m.captures[1]).git" + if m === nothing || GIT_PROTOCOL[] === nothing + url + elseif GIT_PROTOCOL[] == "ssh" + "ssh://git@github.com/$(m.captures[1]).git" + else + "$(GIT_PROTOCOL[])://github.com/$(m.captures[1]).git" + end end function clone(url, source_path; header=nothing, kwargs...) diff --git a/stdlib/Pkg/src/Operations.jl b/stdlib/Pkg/src/Operations.jl index f03de979c85dc..a2198ae261d98 100644 --- a/stdlib/Pkg/src/Operations.jl +++ b/stdlib/Pkg/src/Operations.jl @@ -98,7 +98,6 @@ function collect_fixed!(ctx::Context, pkgs::Vector{PackageSpec}, uuid_to_name::D path = pkg.path elseif info !== nothing && haskey(info, "repo-url") path = find_installed(pkg.name, pkg.uuid, SHA1(info["git-tree-sha1"])) - pkg.version = VersionNumber(info["version"]) pkg.repo = Types.GitRepo(info["repo-url"], info["repo-rev"], SHA1(info["git-tree-sha1"])) else continue @@ -310,38 +309,49 @@ function resolve_versions!(ctx::Context, pkgs::Vector{PackageSpec})::Dict{UUID,V uuid_to_name[uuid] = name uuid_idx = findfirst(isequal(uuid), uuids) - ver = VersionSpec() + info = manifest_info(ctx.env, uuid) + if info !== nothing && haskey(info, "version") # stdlibs might not have a version + ver = VersionSpec(VersionNumber(info["version"])) + else + ver = VersionSpec() + end if uuid_idx != nothing pkg = pkgs[uuid_idx] - info = manifest_info(ctx.env, uuid) - if info !== nothing && haskey(info, "version") # stdlibs might not have a version - ver = VersionNumber(info["version"]) - if pkg.special_action != PKGSPEC_FREED && get(info, "pinned", false) - # This is a pinned package, fix its version - pkg.version = ver - end + if info !== nothing && pkg.special_action != PKGSPEC_FREED && get(info, "pinned", false) + # This is a pinned package, fix its version + pkg.version = ver end else pkg = PackageSpec(name, uuid, ver) push!(pkgs, pkg) end - proj_compat = Types.project_compatibility(ctx, name) - v = intersect(pkg.version, proj_compat) - if isempty(v) - pkgerror(string("for package $(pkg.name) intersection between project compatibility $(proj_compat) ", - "and package version $(pkg.version) is empty")) - end - pkg.version = v end + + # construct data structures for resolver and call it + # this also sets pkg.version for fixed packages + fixed = collect_fixed!(ctx, pkgs, uuid_to_name) + + # compatibility proj_compat = Types.project_compatibility(ctx, "julia") v = intersect(VERSION, proj_compat) if isempty(v) - @warn("julia version requirement for project not satisfied") + @warn "julia version requirement for project not satisfied" _module=nothing _file=nothing + end + + for pkg in pkgs + proj_compat = Types.project_compatibility(ctx, pkg.name) + v = intersect(pkg.version, proj_compat) + if isempty(v) + pkgerror(string("empty intersection between $(pkg.name)@$(pkg.version) and project ", + "compatibility $(proj_compat)")) + end + # Work around not clobbering 0.x.y+ for checked out old type of packages + if !(pkg.version isa VersionNumber) + pkg.version = v + end end - # construct data structures for resolver and call it reqs = Requires(pkg.uuid => VersionSpec(pkg.version) for pkg in pkgs if pkg.uuid ≠ uuid_julia) - fixed = collect_fixed!(ctx, pkgs, uuid_to_name) fixed[uuid_julia] = Fixed(VERSION) graph = deps_graph(ctx, uuid_to_name, reqs, fixed) @@ -647,17 +657,8 @@ function find_stdlib_deps(ctx::Context, path::String) return stdlib_deps end -function relative_project_path_if_in_project(ctx::Context, path::String) - # Check if path is in project => relative - project_path = dirname(ctx.env.project_file) - if startswith(normpath(path), project_path) - return relpath(path, project_path) - else - return abspath(path) - end -end - -project_rel_path(ctx::Context, path::String) = joinpath(dirname(ctx.env.project_file), path) +project_rel_path(ctx::Context, path::String) = + normpath(joinpath(dirname(ctx.env.project_file), path)) function update_manifest(ctx::Context, pkg::PackageSpec, hash::Union{SHA1, Nothing}) env = ctx.env @@ -1037,7 +1038,7 @@ function build_versions(ctx::Context, uuids::Vector{UUID}; might_need_to_resolve for (uuid, name, hash_or_path, build_file, version) in builds log_file = splitext(build_file)[1] * ".log" printpkgstyle(ctx, :Building, - rpad(name * " ", max_name + 1, "─") * "→ " * Types.pathrepr(ctx, log_file)) + rpad(name * " ", max_name + 1, "─") * "→ " * Types.pathrepr(log_file)) code = """ $(Base.load_path_setup_code(false)) cd($(repr(dirname(build_file)))) @@ -1045,7 +1046,7 @@ function build_versions(ctx::Context, uuids::Vector{UUID}; might_need_to_resolve """ cmd = ``` $(Base.julia_cmd()) -O0 --color=no --history-file=no - --startup-file=$(Base.JLOptions().startupfile != 2 ? "yes" : "no") + --startup-file=$(Base.JLOptions().startupfile == 1 ? "yes" : "no") --compiled-modules=$(Bool(Base.JLOptions().use_compiled_modules) ? "yes" : "no") --eval $code ``` diff --git a/stdlib/Pkg/src/Pkg.jl b/stdlib/Pkg/src/Pkg.jl index 940375c94af54..0e2ffc0859cac 100644 --- a/stdlib/Pkg/src/Pkg.jl +++ b/stdlib/Pkg/src/Pkg.jl @@ -74,7 +74,7 @@ const UpgradeLevel = Types.UpgradeLevel # Define new variables so tab comleting Pkg. works. """ - Pkg.add(pkg::Union{String, Vector{String}) + Pkg.add(pkg::Union{String, Vector{String}}) Pkg.add(pkg::Union{PackageSpec, Vector{PackageSpec}}) Add a package to the current project. This package will be available using the @@ -94,7 +94,7 @@ See also [`PackageSpec`](@ref). const add = API.add """ - Pkg.rm(pkg::Union{String, Vector{String}) + Pkg.rm(pkg::Union{String, Vector{String}}) Pkg.rm(pkg::Union{PackageSpec, Vector{PackageSpec}}) Remove a package from the current project. If the `mode` of `pkg` is @@ -107,7 +107,7 @@ const rm = API.rm """ Pkg.update(; level::UpgradeLevel=UPLEVEL_MAJOR, mode::PackageMode = PKGMODE_PROJECT) - Pkg.update(pkg::Union{String, Vector{String}) + Pkg.update(pkg::Union{String, Vector{String}}) Pkg.update(pkg::Union{PackageSpec, Vector{PackageSpec}}) Update a package `pkg`. If no posistional argument is given, update all packages in the manifest if `mode` is `PKGMODE_MANIFEST` and packages in both manifest and project if `mode` is `PKGMODE_PROJECT`. @@ -162,7 +162,7 @@ const gc = API.gc """ Pkg.build() - Pkg.build(pkg::Union{String, Vector{String}) + Pkg.build(pkg::Union{String, Vector{String}}) Pkg.build(pkgs::Union{PackageSpec, Vector{PackageSpec}}) Run the build script in `deps/build.jl` for `pkg` and all of the dependencies in @@ -178,7 +178,7 @@ const build = API.build const installed = API.installed """ - Pkg.pin(pkg::Union{String, Vector{String}) + Pkg.pin(pkg::Union{String, Vector{String}}) Pkg.pin(pkgs::Union{Packagespec, Vector{Packagespec}}) Pin a package to the current version (or the one given in the `packagespec` or a certain @@ -187,7 +187,7 @@ git revision. A pinned package is never updated. const pin = API.pin """ - Pkg.free(pkg::Union{String, Vector{String}) + Pkg.free(pkg::Union{String, Vector{String}}) Pkg.free(pkgs::Union{Packagespec, Vector{Packagespec}}) Free a package which removes a `pin` if it exists, or if the package is tracking a path, @@ -203,7 +203,7 @@ const free = API.free """ - Pkg.develop(pkg::Union{String, Vector{String}) + Pkg.develop(pkg::Union{String, Vector{String}}) Pkg.develop(pkgs::Union{Packagespec, Vector{Packagespec}}) Make a package available for development by tracking it by path. @@ -219,10 +219,10 @@ If `pkg` is given as a local path, the package at that path will be tracked. Pkg.develop("Example") # By url -Pkg.develop(PackageSpec(url="https://github.com/JuliaLang/Compat.jl", rev="master")) +Pkg.develop(PackageSpec(url="https://github.com/JuliaLang/Compat.jl")) -# By path (also uses url keyword to PackageSpec) -Pkg.develop(PackageSpec(url="MyJuliaPackages/Package.jl") +# By path +Pkg.develop(PackageSpec(path="MyJuliaPackages/Package.jl") ``` See also [`PackageSpec`](@ref) @@ -270,16 +270,16 @@ that is modified by executing package commands. The logic for what path is activated is as follows: * If `shared` is `true`, the first existing environment named `s` from the depots - in the depot stack will be activated. If no such environment exists yet, - activate it in the first depot. - * If `s` is a path that exist, that environment will be activated. - * If `s` is a package name in the current project activate that is tracking a path, - activate the environment at that path. - * If `s` is a non-existing path, activate that path. - -If no argument is given to `activate`, activate the home project, -which is the one specified by either `--project` command line when starting julia, -or `JULIA_PROJECT` environment variable. + in the depot stack will be activated. If no such environment exists, + create and activate that environment in the first depot. + * If `s` is an existing path, then activate the environment at that path. + * If `s` is a package in the current project and `s` is tracking a path, then + activate the environment at the tracked path. + * Else, `s` is interpreted as a non-existing path, activate that path. + +If no argument is given to `activate`, then activate the home project. +The home project is specified by either the `--project` command line option to +the julia executable, or the `JULIA_PROJECT` environment variable. # Examples ``` diff --git a/stdlib/Pkg/src/PlatformEngines.jl b/stdlib/Pkg/src/PlatformEngines.jl index 8fb430d146be8..736ccccb0ea67 100644 --- a/stdlib/Pkg/src/PlatformEngines.jl +++ b/stdlib/Pkg/src/PlatformEngines.jl @@ -199,6 +199,8 @@ function probe_platform_engines!(;verbose::Bool = false) [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12; \$webclient = (New-Object System.Net.Webclient); + \$webclient.UseDefaultCredentials = \$true; + \$webclient.Proxy.Credentials = \$webclient.Credentials; \$webclient.Headers.Add("user-agent", \"Pkg.jl (https://github.com/JuliaLang/Pkg.jl)\"); \$webclient.DownloadFile(\"$url\", \"$path\") """ diff --git a/stdlib/Pkg/src/REPLMode.jl b/stdlib/Pkg/src/REPLMode.jl index a9c9822863938..0ed813bae5063 100644 --- a/stdlib/Pkg/src/REPLMode.jl +++ b/stdlib/Pkg/src/REPLMode.jl @@ -82,7 +82,7 @@ function parse_option(word::AbstractString)::Option end meta_option_declarations = OptionDeclaration[ - ("env", OPT_ARG, :env => arg->EnvCache(Base.parse_env(arg))) + ("preview", OPT_SWITCH, :preview => true) ] meta_option_specs = OptionSpecs(meta_option_declarations) @@ -97,22 +97,29 @@ meta_option_specs = OptionSpecs(meta_option_declarations) ) @enum(ArgClass, ARG_RAW, ARG_PKG, ARG_VERSION, ARG_REV, ARG_ALL) struct ArgSpec - class::ArgClass - count::Vector{Int} + count::Pair + parser::Function + parser_keys::Vector{Pair{Symbol, Any}} end const CommandDeclaration = Tuple{CommandKind, Vector{String}, # names Union{Nothing,Function}, # handler - Tuple{ArgClass, Vector{Int}}, # argument count + Tuple{Pair, # count + Function, # parser + Vector{Pair{Symbol, Any}}, # parser keys + }, # arguments Vector{OptionDeclaration}, # options + String, #description Union{Nothing, Markdown.MD}, #help } struct CommandSpec kind::CommandKind - names::Vector{String} + canonical_name::String + short_name::Union{Nothing,String} handler::Union{Nothing,Function} - argument_spec::ArgSpec # note: just use range operator for max/min + argument_spec::ArgSpec option_specs::Dict{String, OptionSpec} + description::String help::Union{Nothing, Markdown.MD} end command_specs = Dict{String,CommandSpec}() # TODO remove this ? @@ -135,10 +142,12 @@ function CommandSpecs(declarations::Vector{CommandDeclaration})::Dict{String,Com for dec in declarations names = dec[2] spec = CommandSpec(dec[1], - names, + names[1], + length(names) == 2 ? names[2] : nothing, dec[3], ArgSpec(dec[4]...), OptionSpecs(dec[5]), + dec[6], dec[end]) for name in names # TODO regex check name @@ -196,7 +205,72 @@ struct QuotedWord isquoted::Bool end -function parse(cmd::String)::Vector{Statement} +function unwrap_option(option::String) + if startswith(option, "--") + return length(option) == 2 ? "" : option[3:end] + elseif length(option) == 2 + return option[end] + end +end + +wrap_option(option::String) = + length(option) == 1 ? "-$option" : "--$option" + +function _statement(words) + is_option(word) = first(word) == '-' + + word = popfirst!(words) + # meta options + while is_option(word) + if isempty(words) + if unwrap_option(word) in keys(meta_option_specs) + return :cmd, "", nothing, true + else + return :meta, word, nothing, true + end + end + word = popfirst!(words) + end + # command + if word == "preview" + if isempty(words) + return :cmd, "", nothing, true + end + word = popfirst!(words) + end + if word in keys(super_specs) # have a super command + super_name = word + super = super_specs[word] + if isempty(words) + return :sub, "", super_name, true + end + word = popfirst!(words) + command = get(super, word, nothing) + if command === nothing + if isempty(words) + return :sub, word, super_name, true + else + return nothing + end + end + elseif get(super_specs["package"], word, nothing) !== nothing # given a "package" command + command = get(super_specs["package"], word, nothing) + elseif isempty(words) # try to complete the super command + return :cmd, word, nothing, true + else + return nothing + end + if isempty(words) + return :arg, "", command, true + end + word = words[end] + manifest = any(x->x in ["--manifest", "-m"], filter(is_option, words)) + return is_option(word) ? + (:opt, word, command, true) : + (:arg, word, command, !manifest) +end + +function parse(cmd::String; for_completions=false) # replace new lines with ; to support multiline commands cmd = replace(replace(cmd, "\r\n" => "; "), "\n" => "; ") # tokenize accoring to whitespace / quotes @@ -206,8 +280,10 @@ function parse(cmd::String)::Vector{Statement} # break up words according to ";"(doing this early makes subsequent processing easier) word_groups = group_words(words) # create statements - statements = map(Statement, word_groups) - return statements + if for_completions + return _statement(word_groups[end]) + end + return map(Statement, word_groups) end # vector of words -> structured statement @@ -224,8 +300,17 @@ function Statement(words)::Statement word = popfirst!(words) end # command + # special handling for `preview`, just convert it to a meta option under the hood + if word == "preview" + if !("--preview" in statement.meta_options) + push!(statement.meta_options, "--preview") + end + isempty(words) && pkgerror("preview requires a command") + word = popfirst!(words) + end if word in keys(super_specs) super = super_specs[word] + isempty(words) && pkgerror("no subcommand specified") word = popfirst!(words) else super = super_specs["package"] @@ -315,6 +400,8 @@ end # PkgCommand # ############## const Token = Union{String, VersionRange, Rev} +const ArgToken = Union{VersionRange, Rev} +const PkgToken = Union{String, VersionRange, Rev} const PkgArguments = Union{Vector{String}, Vector{PackageSpec}} struct PkgCommand meta_options::Vector{Option} @@ -344,22 +431,35 @@ function APIOptions(options::Vector{Option}, return Dict(keyword_vec) end -function enforce_argument_order(args::Vector{Token}) - prev_arg = nothing - function check_prev_arg(valid_type::DataType, error_message::AbstractString) - prev_arg isa valid_type || pkgerror(error_message) - end +function enforce_argument_count(spec::Pair, args::PkgArguments) + count = length(args) + spec.first <= count <= spec.second || + pkgerror("Wrong number of arguments") +end +# Only for PkgSpec +function package_args(args::Vector{Token}; add_or_dev=false)::Vector{PackageSpec} + pkgs = PackageSpec[] for arg in args - if arg isa VersionRange - check_prev_arg(String, "package name/uuid must precede version spec `@$arg`") + if arg isa String + push!(pkgs, parse_package(arg; add_or_develop=add_or_dev)) + elseif arg isa VersionRange + pkgs[end].version = VersionSpec(arg) elseif arg isa Rev - check_prev_arg(String, "package name/uuid must precede rev spec `#$(arg.rev)`") + pkg = pkgs[end] + if pkg.repo == nothing + pkg.repo = Types.GitRepo("", arg.rev) + else + pkgs[end].repo.rev = arg.rev + end + else + assert(false) end - prev_arg = arg end + return pkgs end +# Only for PkgSpec function word2token(word::AbstractString)::Token if first(word) == '@' return VersionRange(word[2:end]) @@ -370,68 +470,39 @@ function word2token(word::AbstractString)::Token end end -function enforce_arg_spec(raw_args::Vector{String}, class::ArgClass) - # TODO is there a more idiomatic way to do this? - function has_types(arguments::Vector{Token}, types::Vector{DataType}) - return !isempty(filter(x->typeof(x) in types, arguments)) - end - - class == ARG_RAW && return raw_args - args::Vector{Token} = map(word2token, raw_args) - class == ARG_ALL && return args - - if class == ARG_PKG && has_types(args, [VersionRange, Rev]) - pkgerror("no versioned packages allowed") - elseif class == ARG_REV && has_types(args, [VersionRange]) - pkgerror("no versioned packages allowed") - elseif class == ARG_VERSION && has_types(args, [Rev]) - pkgerror("no reved packages allowed") +# Only for PkgSpec +function enforce_argument_order(args::Vector{Token}) + prev_arg = nothing + function check_prev_arg(valid_type::DataType, error_message::AbstractString) + prev_arg isa valid_type || pkgerror(error_message) end - return args -end -function package_args(args::Vector{Token}, spec::CommandSpec)::Vector{PackageSpec} - pkgs = PackageSpec[] for arg in args - if arg isa String - is_add_or_develop = spec.kind in (CMD_ADD, CMD_DEVELOP) - push!(pkgs, parse_package(arg; add_or_develop=is_add_or_develop)) - elseif arg isa VersionRange - pkgs[end].version = VersionSpec(arg) + if arg isa VersionRange + check_prev_arg(String, "package name/uuid must precede version spec `@$arg`") elseif arg isa Rev - if spec.kind == CMD_DEVELOP - pkgerror("a git revision cannot be given to `develop`") - end - pkg = pkgs[end] - if pkg.repo == nothing - pkg.repo = Types.GitRepo("", arg.rev) - else - pkgs[end].repo.rev = arg.rev - end - else - assert(false) + check_prev_arg(String, "package name/uuid must precede rev spec `#$(arg.rev)`") end + prev_arg = arg end - return pkgs -end - -function enforce_arg_count(count::Vector{Int}, args::PkgArguments) - isempty(count) && return - length(args) in count || - pkgerror("Wrong number of arguments") end -function enforce_args(raw_args::Vector{String}, spec::ArgSpec, cmd_spec::CommandSpec)::PkgArguments - if spec.class == ARG_RAW - enforce_arg_count(spec.count, raw_args) - return raw_args +function parse_pkg(raw_args::Vector{String}; valid=[], add_or_dev=false) + args::Vector{PkgToken} = map(word2token, raw_args) + enforce_argument_order(args) + # enforce spec + push!(valid, String) # always want at least PkgSpec identifiers + if !all(x->typeof(x) in valid, args) + pkgerror("invalid token") end + # convert to final arguments + return package_args(args; add_or_dev=add_or_dev) +end - args = enforce_arg_spec(raw_args, spec.class) - enforce_argument_order(args) - pkgs = package_args(args, cmd_spec) - enforce_arg_count(spec.count, pkgs) - return pkgs +function enforce_argument(raw_args::Vector{String}, spec::ArgSpec)::PkgArguments + args = spec.parser(raw_args; spec.parser_keys...) + enforce_argument_count(spec.count, args) + return args end function enforce_option(option::String, specs::Dict{String,OptionSpec})::Option @@ -487,9 +558,8 @@ end function PkgCommand(statement::Statement)::PkgCommand meta_opts = enforce_meta_options(statement.meta_options, meta_option_specs) - args = enforce_args(statement.arguments, - statement.command.argument_spec, - statement.command) + args = enforce_argument(statement.arguments, + statement.command.argument_spec) opts = enforce_opts(statement.options, statement.command.option_specs) return PkgCommand(meta_opts, statement.command, opts, args) end @@ -520,31 +590,36 @@ end function do_cmd!(command::PkgCommand, repl) context = APIOptions(command.meta_options, meta_option_specs) - spec = command.spec # REPL specific commands - if spec.kind == CMD_HELP + if command.spec.kind == CMD_HELP return Base.invokelatest(do_help!, command, repl) - elseif spec.kind == CMD_PREVIEW - context[:preview] = true - cmd = command.arguments[1] - cmd_spec = get(command_specs, cmd, nothing) - cmd_spec === nothing && - pkgerror("'$cmd' is not a valid command") - spec = cmd_spec - command = PkgCommand([], cmd, [], PackageSpec[]) end # API commands # TODO is invokelatest still needed? api_opts = APIOptions(command) - if applicable(spec.handler, context, command.arguments, api_opts) - Base.invokelatest(spec.handler, context, command.arguments, api_opts) + if applicable(command.spec.handler, context, command.arguments, api_opts) + Base.invokelatest(command.spec.handler, context, command.arguments, api_opts) else - Base.invokelatest(spec.handler, command.arguments, api_opts) + Base.invokelatest(command.spec.handler, command.arguments, api_opts) end end +function CommandSpec(command_name::String)::Union{Nothing,CommandSpec} + # maybe a "package" command + spec = get(super_specs["package"], command_name, nothing) + if spec !== nothing + return spec + end + # maybe a "compound command" + m = match(r"(\w+)-(\w+)", command_name) + m !== nothing || (return nothing) + super = get(super_specs, m.captures[1], nothing) + super !== nothing || (return nothing) + return get(super, m.captures[2], nothing) +end + function do_help!(command::PkgCommand, repl::REPL.AbstractREPL) disp = REPL.REPLDisplay(repl) if isempty(command.arguments) @@ -553,9 +628,10 @@ function do_help!(command::PkgCommand, repl::REPL.AbstractREPL) end help_md = md"" for arg in command.arguments - spec = get(command_specs, arg, nothing) - spec === nothing && + spec = CommandSpec(arg) + if spec === nothing pkgerror("'$arg' does not name a command") + end spec.help === nothing && pkgerror("Sorry, I don't have any help for the `$arg` command.") isempty(help_md.content) || @@ -666,10 +742,16 @@ end pkgstr(str::String) = do_cmd(minirepl[], str; do_rethrow=true) # handle completions -all_commands_sorted = [] -long_commands = [] -all_options_sorted = [] -long_options = [] +mutable struct CompletionCache + commands::Vector{String} + canonical_names::Vector{String} + meta_options::Vector{String} + options::Dict{CommandKind, Vector{String}} + subcommands::Dict{String, Vector{String}} + CompletionCache() = new([],[],[],Dict(),Dict()) +end + +completion_cache = CompletionCache() struct PkgCompletionProvider <: LineEdit.CompletionProvider end @@ -680,27 +762,10 @@ function LineEdit.complete_line(c::PkgCompletionProvider, s) return ret, partial[range], should_complete end -function complete_command(s, i1, i2) - # only show short form commands when no input is given at all - cmp = filter(cmd -> startswith(cmd, s), isempty(s) ? all_commands_sorted : long_commands) - return cmp, i1:i2, !isempty(cmp) -end - -function complete_option(s, i1, i2) - # only show short form options if only a dash is given - cmp = filter(cmd -> startswith(cmd, s), length(s) == 1 && first(s) == '-' ? - all_options_sorted : - long_options) - return cmp, i1:i2, !isempty(cmp) -end - -function complete_package(s, i1, i2, lastcommand, project_opt) - if lastcommand in [CMD_STATUS, CMD_RM, CMD_UP, CMD_TEST, CMD_BUILD, CMD_FREE, CMD_PIN] - return complete_installed_package(s, i1, i2, project_opt) - elseif lastcommand in [CMD_ADD, CMD_DEVELOP] - return complete_remote_package(s, i1, i2) - end - return String[], 0:-1, false +function complete_local_path(s, i1, i2) + cmp = REPL.REPLCompletions.complete_path(s, i2) + completions = filter!(isdir, [REPL.REPLCompletions.completion_text(p) for p in cmp[1]]) + return completions, cmp[2], !isempty(completions) end function complete_installed_package(s, i1, i2, project_opt) @@ -737,46 +802,51 @@ function complete_remote_package(s, i1, i2) return cmp, i1:i2, !isempty(cmp) end -function completions(full, index) - pre = full[1:index] - - pre_words = split(pre, ' ', keepempty=true) - - # first word should always be a command - if isempty(pre_words) - return complete_command("", 1:1) - else - to_complete = pre_words[end] - offset = isempty(to_complete) ? index+1 : to_complete.offset+1 - - if length(pre_words) == 1 - return complete_command(to_complete, offset, index) - end - - # tokenize input, don't offer any completions for invalid commands - statement = try - parse(join(pre_words[1:end-1], ' '))[end] - catch - return String[], 0:-1, false - end - - lastcommand = statement.command.kind - project_opt = true - for opt in statement.options - if opt in ["--manifest", "--project", "-m", "-p"] - project_opt = opt in ["--project", "-p"] - break - end - end - - if lastcommand in [CMD_HELP, CMD_PREVIEW] - return complete_command(to_complete, offset, index) - elseif !isempty(to_complete) && first(to_complete) == '-' - return complete_option(to_complete, offset, index) +function complete_argument(to_complete, i1, i2, lastcommand, project_opt + )::Tuple{Vector{String},UnitRange{Int},Bool} + if lastcommand == CMD_HELP + completions = filter(x->startswith(x,to_complete), completion_cache.canonical_names) + return completions, i1:i2, !isempty(completions) + elseif lastcommand in [CMD_STATUS, CMD_RM, CMD_UP, CMD_TEST, CMD_BUILD, CMD_FREE, CMD_PIN] + return complete_installed_package(to_complete, i1, i2, project_opt) + elseif lastcommand in [CMD_ADD, CMD_DEVELOP] + if occursin(Base.Filesystem.path_separator_re, to_complete) + return complete_local_path(to_complete, i1, i2) else - return complete_package(to_complete, offset, index, lastcommand, project_opt) + rps = complete_remote_package(to_complete, i1, i2) + lps = complete_local_path(to_complete, i1, i2) + return vcat(rps[1], lps[1]), isempty(rps[1]) ? lps[2] : i1:i2, length(rps[1]) + length(lps[1]) > 0 end end + return String[], 0:-1, false +end + +function completions(full, index)::Tuple{Vector{String},UnitRange{Int},Bool} + pre = full[1:index] + if isempty(pre) + return completion_cache.commands, 0:-1, false + end + x = parse(pre; for_completions=true) + if x === nothing # failed parse (invalid command name) + return String[], 0:-1, false + end + (key::Symbol, to_complete::String, spec, proj::Bool) = x + last = split(pre, ' ', keepempty=true)[end] + offset = isempty(last) ? index+1 : last.offset+1 + if last != to_complete # require a space before completing next field + return String[], 0:-1, false + end + if key == :arg + return complete_argument(to_complete, offset, index, spec.kind, proj) + end + possible::Vector{String} = + key == :meta ? completion_cache.meta_options : + key == :cmd ? completion_cache.commands : + key == :sub ? completion_cache.subcommands[spec] : + key == :opt ? completion_cache.options[spec.kind] : + String[] + completions = filter(x->startswith(x,to_complete), possible) + return completions, offset:index, !isempty(completions) end prev_project_file = nothing @@ -900,25 +970,29 @@ end # SPEC # ######## command_declarations = [ +#= ["registry"] => CommandDeclaration[ ( CMD_REGISTRY_ADD, ["add"], do_registry_add!, - (ARG_PKG, []), + (1=>Inf, identity, []), [], + "Currently just a placeholder for a future command", nothing, ), ], #registry +=# ["package"] => CommandDeclaration[ ( CMD_TEST, ["test"], do_test!, - (ARG_PKG, []), + (0=>Inf, parse_pkg, []), [ ("coverage", OPT_SWITCH, :coverage => true), ], + "run tests for packages", md""" test [opts] pkg[=uuid] ... @@ -933,8 +1007,9 @@ julia is started with `--startup-file=yes`. ),( CMD_HELP, ["help", "?"], nothing, - (ARG_RAW, []), + (0=>Inf, identity, []), [], + "show this message", md""" help @@ -950,11 +1025,12 @@ Available commands: `help`, `status`, `add`, `rm`, `up`, `preview`, `gc`, `test` ),( CMD_INSTANTIATE, ["instantiate"], do_instantiate!, - (ARG_RAW, [0]), + (0=>0, identity, []), [ (["project", "p"], OPT_SWITCH, :manifest => false), (["manifest", "m"], OPT_SWITCH, :manifest => true), ], + "downloads all the dependencies for the project", md""" instantiate instantiate [-m|--manifest] @@ -966,11 +1042,12 @@ If no manifest exists or the `--project` option is given, resolve and download t ),( CMD_RM, ["remove", "rm"], do_rm!, - (ARG_PKG, []), + (1=>Inf, parse_pkg, []), [ (["project", "p"], OPT_SWITCH, :mode => PKGMODE_PROJECT), (["manifest", "m"], OPT_SWITCH, :mode => PKGMODE_MANIFEST), ], + "remove packages from project or manifest", md""" rm [-p|--project] pkg[=uuid] ... @@ -994,8 +1071,9 @@ as any no-longer-necessary manifest packages due to project package removals. ),( CMD_ADD, ["add"], do_add!, - (ARG_ALL, []), + (1=>Inf, parse_pkg, [:add_or_dev => true, :valid => [VersionRange, Rev]]), [], + "add packages to project", md""" add pkg[=uuid] [@version] [#rev] ... @@ -1024,11 +1102,12 @@ pkg> add Example=7876af07-990d-54b4-ab0e-23690620f79a ),( CMD_DEVELOP, ["develop", "dev"], do_develop!, - (ARG_ALL, []), + (1=>Inf, parse_pkg, [:add_or_dev => true, :valid => [VersionRange]]), [ ("local", OPT_SWITCH, :shared => false), ("shared", OPT_SWITCH, :shared => true), ], + "clone the full package repo locally for development", md""" develop [--shared|--local] pkg[=uuid] ... @@ -1042,17 +1121,17 @@ This operation is undone by `free`. *Example* ```jl pkg> develop Example -pkg> develop Example#master -pkg> develop Example#c37b675 -pkg> develop https://github.com/JuliaLang/Example.jl#master +pkg> develop https://github.com/JuliaLang/Example.jl +pkg> develop ~/mypackages/Example pkg> develop --local Example ``` """, ),( CMD_FREE, ["free"], do_free!, - (ARG_PKG, []), + (1=>Inf, parse_pkg, []), [], + "undoes a `pin`, `develop`, or stops tracking a repo", md""" free pkg[=uuid] ... @@ -1062,8 +1141,9 @@ makes the package no longer being checked out. ),( CMD_PIN, ["pin"], do_pin!, - (ARG_VERSION, []), + (1=>Inf, parse_pkg, [:valid => [VersionRange]]), [], + "pins the version of packages", md""" pin pkg[=uuid] ... @@ -1074,8 +1154,9 @@ A pinned package has the symbol `⚲` next to its version in the status list. ),( CMD_BUILD, ["build"], do_build!, - (ARG_PKG, []), + (0=>Inf, parse_pkg, []), [], + "run the build script for packages", md""" build pkg[=uuid] ... @@ -1087,21 +1168,23 @@ The `startup.jl` file is disabled during building unless julia is started with ` ),( CMD_RESOLVE, ["resolve"], do_resolve!, - (ARG_RAW, [0]), + (0=>0, identity, []), [], + "resolves to update the manifest from changes in dependencies of developed packages", md""" resolve Resolve the project i.e. run package resolution and update the Manifest. This is useful in case the dependencies of developed -packages have changed causing the current Manifest to_indices be out of sync. +packages have changed causing the current Manifest to be out of sync. """, ),( CMD_ACTIVATE, ["activate"], do_activate!, - (ARG_RAW, [0,1]), + (0=>1, identity, []), [ ("shared", OPT_SWITCH, :shared => true), ], + "set the primary environment the package manager manipulates", md""" activate activate [--shared] path @@ -1115,7 +1198,7 @@ it will be placed in the first depot of the stack. ),( CMD_UP, ["update", "up"], do_up!, - (ARG_VERSION, []), + (0=>Inf, parse_pkg, [:valid => [VersionRange]]), [ (["project", "p"], OPT_SWITCH, :mode => PKGMODE_PROJECT), (["manifest", "m"], OPT_SWITCH, :mode => PKGMODE_MANIFEST), @@ -1124,6 +1207,7 @@ it will be placed in the first depot of the stack. ("patch", OPT_SWITCH, :level => UPLEVEL_PATCH), ("fixed", OPT_SWITCH, :level => UPLEVEL_FIXED), ], + "update packages in manifest", md""" up [-p|project] [opts] pkg[=uuid] [@version] ... @@ -1143,8 +1227,9 @@ packages will not be upgraded at all. ),( CMD_GENERATE, ["generate"], do_generate!, - (ARG_RAW, [1]), + (1=>1, identity, []), [], + "generate files for a new project", md""" generate pkgname @@ -1154,22 +1239,23 @@ Create a project called `pkgname` in the current folder. ),( CMD_PRECOMPILE, ["precompile"], do_precompile!, - (ARG_RAW, [0]), + (0=>0, identity, []), [], + "precompile all the project dependencies", md""" precompile Precompile all the dependencies of the project by running `import` on all of them in a new process. -The `startup.jl` file is disabled during precompilation unless julia is started with `--startup-file=yes`. """, ),( CMD_STATUS, ["status", "st"], do_status!, - (ARG_RAW, [0]), + (0=>0, identity, []), [ (["project", "p"], OPT_SWITCH, :mode => PKGMODE_PROJECT), (["manifest", "m"], OPT_SWITCH, :mode => PKGMODE_MANIFEST), ], + "summarize contents of and changes to environment", md""" status @@ -1186,17 +1272,21 @@ includes the dependencies of explicitly added packages. ),( CMD_GC, ["gc"], do_gc!, - (ARG_RAW, [0]), + (0=>0, identity, []), [], + "garbage collect packages not used for a significant time", md""" Deletes packages that cannot be reached from any existing environment. """, -),( CMD_PREVIEW, +),( # preview is not a regular command. + # this is here so that preview appears as a registered command to users + CMD_PREVIEW, ["preview"], nothing, - (ARG_RAW, [1]), + (1=>Inf, identity, []), [], + "previews a subsequent command without affecting the current state", md""" preview cmd @@ -1210,22 +1300,32 @@ is modified. ] #command_declarations super_specs = SuperSpecs(command_declarations) -command_specs = super_specs["package"] -all_commands_sorted = sort(collect(String,keys(command_specs))) -long_commands = filter(c -> length(c) > 2, all_commands_sorted) -function all_options() - all_opts = [] - for command in values(command_specs) - for opt_spec in values(command.option_specs) - push!(all_opts, opt_spec.name) - opt_spec.short_name !== nothing && push!(all_opts, opt_spec.short_name) +# cache things you need for completions +completion_cache.meta_options = sort(map(wrap_option, collect(keys(meta_option_specs)))) +completion_cache.commands = sort(append!(collect(keys(super_specs)), + collect(keys(super_specs["package"])))) +let names = String[] + for (super, specs) in pairs(super_specs) + super == "package" && continue # skip "package" + for spec in unique(values(specs)) + push!(names, join([super, spec.canonical_name], "-")) end end - unique!(all_opts) - return all_opts + for spec in unique(values(super_specs["package"])) + push!(names, spec.canonical_name) + end + completion_cache.canonical_names = names + sort!(completion_cache.canonical_names) +end +for (k, v) in pairs(super_specs) + completion_cache.subcommands[k] = sort(collect(keys(v))) + for spec in values(v) + completion_cache.options[spec.kind] = + sort(map(wrap_option, collect(keys(spec.option_specs)))) + end end -all_options_sorted = [length(opt) > 1 ? "--$opt" : "-$opt" for opt in sort!(all_options())] -long_options = filter(c -> length(c) > 2, all_options_sorted) +# TODO remove this +command_specs = super_specs["package"] const help = md""" @@ -1235,57 +1335,16 @@ backspace when the input line is empty or press Ctrl+C. **Synopsis** - pkg> [--env=...] cmd [opts] [args] + pkg> cmd [opts] [args] Multiple commands can be given on the same line by interleaving a `;` between the commands. -**Environment** - -The `--env` meta option determines which project environment to manipulate. By -default, this looks for a git repo in the parents directories of the current -working directory, and if it finds one, it uses that as an environment. Otherwise, -it uses a named environment (typically found in `~/.julia/environments`) looking -for environments named `v$(VERSION.major).$(VERSION.minor).$(VERSION.patch)`, -`v$(VERSION.major).$(VERSION.minor)`, `v$(VERSION.major)` or `default` in order. - **Commands** - -What action you want the package manager to take: - -`help`: show this message - -`status`: summarize contents of and changes to environment - -`add`: add packages to project - -`develop`: clone the full package repo locally for development - -`rm`: remove packages from project or manifest - -`up`: update packages in manifest - -`test`: run tests for packages - -`build`: run the build script for packages - -`pin`: pins the version of packages - -`free`: undoes a `pin`, `develop`, or stops tracking a repo. - -`instantiate`: downloads all the dependencies for the project - -`resolve`: resolves to update the manifest from changes in dependencies of -developed packages - -`generate`: generate files for a new project - -`preview`: previews a subsequent command without affecting the current state - -`precompile`: precompile all the project dependencies - -`gc`: garbage collect packages not used for a significant time - -`activate`: set the primary environment the package manager manipulates """ +for command in completion_cache.canonical_names + spec = CommandSpec(command) + push!(help.content, Markdown.parse("`$command`: $(spec.description)")) +end + end #module diff --git a/stdlib/Pkg/src/Types.jl b/stdlib/Pkg/src/Types.jl index 1cd15177f05f6..8376a93711332 100644 --- a/stdlib/Pkg/src/Types.jl +++ b/stdlib/Pkg/src/Types.jl @@ -240,13 +240,13 @@ function find_project_file(env::Union{Nothing,String}=nothing) project_file = nothing if env isa Nothing project_file = Base.active_project() - project_file == nothing && error("no active project") + project_file == nothing && pkgerror("no active project") elseif startswith(env, '@') project_file = Base.load_path_expand(env) - project_file === nothing && error("package environment does not exist: $env") + project_file === nothing && pkgerror("package environment does not exist: $env") elseif env isa String if isdir(env) - isempty(readdir(env)) || error("environment is a package directory: $env") + isempty(readdir(env)) || pkgerror("environment is a package directory: $env") project_file = joinpath(env, Base.project_names[end]) else project_file = endswith(env, ".toml") ? abspath(env) : @@ -333,7 +333,7 @@ is_project_uuid(env::EnvCache, uuid::UUID) = ########### # Context # ########### -stdlib_dir() = joinpath(Sys.BINDIR, "..", "share", "julia", "stdlib", "v$(VERSION.major).$(VERSION.minor)") +stdlib_dir() = normpath(joinpath(Sys.BINDIR, "..", "share", "julia", "stdlib", "v$(VERSION.major).$(VERSION.minor)")) stdlib_path(stdlib::String) = joinpath(stdlib_dir(), stdlib) function gather_stdlib_uuids() stdlibs = Dict{UUID,String}() @@ -492,9 +492,22 @@ function isdir_windows_workaround(path::String) end end +# try to call realpath on as much as possible +function safe_realpath(path) + ispath(path) && return realpath(path) + a, b = splitdir(path) + return joinpath(safe_realpath(a), b) +end +function relative_project_path(ctx::Context, path::String) + # compute path relative the project + # realpath needed to expand symlinks before taking the relative path + return relpath(safe_realpath(abspath(path)), + safe_realpath(dirname(ctx.env.project_file))) +end + casesensitive_isdir(dir::String) = isdir_windows_workaround(dir) && dir in readdir(joinpath(dir, "..")) -function handle_repos_develop!(ctx::Context, pkgs::AbstractVector{PackageSpec}, devdir::String) +function handle_repos_develop!(ctx::Context, pkgs::AbstractVector{PackageSpec}; shared::Bool) Base.shred!(LibGit2.CachedCredentials()) do creds env = ctx.env new_uuids = UUID[] @@ -512,8 +525,7 @@ function handle_repos_develop!(ctx::Context, pkgs::AbstractVector{PackageSpec}, else # Relative paths are given relative pwd() so we # translate that to be relative the project instead. - # `realpath` is needed to expand symlinks before taking the relative path. - pkg.path = relpath(realpath(abspath(pkg.repo.url)), realpath(dirname(ctx.env.project_file))) + pkg.path = relative_project_path(ctx, pkg.repo.url) end folder_already_downloaded = true project_path = pkg.repo.url @@ -550,7 +562,7 @@ function handle_repos_develop!(ctx::Context, pkgs::AbstractVector{PackageSpec}, else rev = string(LibGit2.GitHash(LibGit2.head(repo))) end - gitobject, isbranch = get_object_branch(repo, rev) + gitobject, isbranch = get_object_branch(repo, rev, creds) try LibGit2.transact(repo) do r if isbranch @@ -565,6 +577,7 @@ function handle_repos_develop!(ctx::Context, pkgs::AbstractVector{PackageSpec}, end parse_package!(ctx, pkg, project_path) + devdir = shared ? Pkg.devdir() : joinpath(dirname(ctx.env.project_file), "dev") dev_pkg_path = joinpath(devdir, pkg.name) if isdir(dev_pkg_path) if !isfile(joinpath(dev_pkg_path, "src", pkg.name * ".jl")) @@ -577,11 +590,12 @@ function handle_repos_develop!(ctx::Context, pkgs::AbstractVector{PackageSpec}, mv(project_path, dev_pkg_path; force=true) push!(new_uuids, pkg.uuid) end - # Save the path as relative if the location is inside the project - # (e.g. from `dev --local`), otherwise put in the absolute path. - pkg.path = Pkg.Operations.relative_project_path_if_in_project(ctx, dev_pkg_path) + # Save the path as relative if it is a --local dev, + # otherwise put in the absolute path. + pkg.path = shared ? dev_pkg_path : relative_project_path(ctx, dev_pkg_path) end @assert pkg.path != nothing + @assert has_uuid(pkg) end return new_uuids end @@ -607,35 +621,32 @@ function handle_repos_add!(ctx::Context, pkgs::AbstractVector{PackageSpec}; project_path = nothing folder_already_downloaded = false try - repo, just_cloned = ispath(repo_path) ? (LibGit2.GitRepo(repo_path), false) : begin - r = GitTools.clone(pkg.repo.url, repo_path, isbare=true, credentials=creds) - GitTools.fetch(r, pkg.repo.url; refspecs=refspecs, credentials=creds) - r, true + repo = if ispath(repo_path) + LibGit2.GitRepo(repo_path) + else + GitTools.clone(pkg.repo.url, repo_path, isbare=true, credentials=creds) end info = manifest_info(env, pkg.uuid) pinned = (info != nothing && get(info, "pinned", false)) - if upgrade_or_add && !pinned && !just_cloned - rev = pkg.repo.rev - GitTools.fetch(repo, pkg.repo.url; refspecs=refspecs, credentials=creds) - end upgrading = upgrade_or_add && !pinned if upgrading + GitTools.fetch(repo; refspecs=refspecs, credentials=creds) rev = pkg.repo.rev + # see if we can get rev as a branch + if isempty(rev) + if LibGit2.isattached(repo) + rev = LibGit2.branch(repo) + else + rev = string(LibGit2.GitHash(LibGit2.head(repo))) + end + end else # Not upgrading so the rev should be the current git-tree-sha rev = info["git-tree-sha1"] pkg.version = VersionNumber(info["version"]) end - # see if we can get rev as a branch - if isempty(rev) - if LibGit2.isattached(repo) - rev = LibGit2.branch(repo) - else - rev = string(LibGit2.GitHash(LibGit2.head(repo))) - end - end - gitobject, isbranch = get_object_branch(repo, rev) + gitobject, isbranch = get_object_branch(repo, rev, creds) # If the user gave a shortened commit SHA, might as well update it to the full one try if upgrading @@ -652,7 +663,6 @@ function handle_repos_add!(ctx::Context, pkgs::AbstractVector{PackageSpec}; info = manifest_info(env, pkg.uuid) if info != nothing && get(info, "git-tree-sha1", "") == string(pkg.repo.git_tree_sha1) && folder_already_downloaded # Same tree sha and this version already downloaded, nothing left to do - pkg.version = VersionNumber(info["version"]) do_nothing_more = true end end @@ -679,7 +689,7 @@ function handle_repos_add!(ctx::Context, pkgs::AbstractVector{PackageSpec}; mv(project_path, version_path; force=true) push!(new_uuids, pkg.uuid) end - @assert pkg.version isa VersionNumber + @assert has_uuid(pkg) end return new_uuids finally @@ -694,25 +704,20 @@ function parse_package!(ctx, pkg, project_path) project_data = read_package(project_file) pkg.uuid = UUID(project_data["uuid"]) pkg.name = project_data["name"] - if haskey(project_data, "version") - pkg.version = VersionNumber(project_data["version"]) - else - @warn "project file for $(pkg.name) at $(project_path) is missing a `version` entry" - Pkg.Operations.set_maximum_version_registry!(env, pkg) - end else - # @warn "package $(pkg.name) at $(project_path) will need to have a [Julia]Project.toml file in the future" if !isempty(ctx.old_pkg2_clone_name) # remove when legacy CI script support is removed pkg.name = ctx.old_pkg2_clone_name else - # This is an old style package, get the name from src/PackageName - if isdir_windows_workaround(pkg.repo.url) - m = match(reg_pkg, abspath(pkg.repo.url)) - else - m = match(reg_pkg, pkg.repo.url) + # This is an old style package, if not set, get the name from src/PackageName + if !has_name(pkg) + if isdir_windows_workaround(pkg.repo.url) + m = match(reg_pkg, abspath(pkg.repo.url)) + else + m = match(reg_pkg, pkg.repo.url) + end + m === nothing && pkgerror("cannot determine package name from URL or path: $(pkg.repo.url), provide a name argument to `PackageSpec`") + pkg.name = m.captures[1] end - m === nothing && pkgerror("cannot determine package name from URL or path: $(pkg.repo.url)") - pkg.name = m.captures[1] end reg_uuids = registered_uuids(env, pkg.name) is_registered = !isempty(reg_uuids) @@ -723,14 +728,9 @@ function parse_package!(ctx, pkg, project_path) pkg.uuid = uuid5(uuid_unreg_pkg, pkg.name) @info "Assigning UUID $(pkg.uuid) to $(pkg.name)" end - pkg.version = v"0.0" else - # TODO: Fix @assert length(reg_uuids) == 1 pkg.uuid = reg_uuids[1] - # Old style registered package - # What version does this package have? We have no idea... let's give it the latest one with a `+`... - Pkg.Operations.set_maximum_version_registry!(env, pkg) end end end @@ -744,7 +744,7 @@ function set_repo_for_pkg!(env, pkg) _, pkg.repo.url = Types.registered_info(env, pkg.uuid, "repo")[1] end -function get_object_branch(repo, rev) +function get_object_branch(repo, rev, creds) gitobject = nothing isbranch = false try @@ -758,7 +758,13 @@ function get_object_branch(repo, rev) gitobject = LibGit2.GitObject(repo, rev) catch err err isa LibGit2.GitError && err.code == LibGit2.Error.ENOTFOUND || rethrow(err) - pkgerror("git object $(rev) could not be found") + GitTools.fetch(repo; refspecs=refspecs, credentials=creds) + try + gitobject = LibGit2.GitObject(repo, rev) + catch err + err isa LibGit2.GitError && err.code == LibGit2.Error.ENOTFOUND || rethrow(err) + pkgerror("git object $(rev) could not be found") + end end end return gitobject, isbranch @@ -782,7 +788,7 @@ end # Disambiguate name/uuid package specifications using project info. function project_deps_resolve!(env::EnvCache, pkgs::AbstractVector{PackageSpec}) uuids = env.project["deps"] - names = Dict(uuid => name for (uuid, name) in uuids) + names = Dict(uuid => name for (name, uuid) in uuids) length(uuids) < length(names) && # TODO: handle this somehow? pkgerror("duplicate UUID found in project file's [deps] section") for pkg in pkgs @@ -1050,12 +1056,16 @@ function registered_uuid(env::EnvCache, name::String)::UUID end end length(choices_cache) == 1 && return choices_cache[1][1] - # prompt for which UUID was intended: - menu = RadioMenu(choices) - choice = request("There are multiple registered `$name` packages, choose one:", menu) - choice == -1 && return UUID(zero(UInt128)) - env.paths[choices_cache[choice][1]] = [choices_cache[choice][2]] - return choices_cache[choice][1] + if isinteractive() + # prompt for which UUID was intended: + menu = RadioMenu(choices) + choice = request("There are multiple registered `$name` packages, choose one:", menu) + choice == -1 && return UUID(zero(UInt128)) + env.paths[choices_cache[choice][1]] = [choices_cache[choice][2]] + return choices_cache[choice][1] + else + pkgerror("there are multiple registered `$name` packages, explicitly set the uuid") + end end # Determine current name for a given package UUID @@ -1109,20 +1119,12 @@ function printpkgstyle(ctx::Context, cmd::Symbol, text::String, ignore_indent::B end -function pathrepr(ctx::Union{Nothing, Context}, path::String, base::String=pwd()) - project_path = dirname(ctx.env.project_file) - path = joinpath(project_path, path) - if startswith(path, project_path) && startswith(base, project_path) - # We are in project and path is in project - path = relpath(path, base) - end - if !Sys.iswindows() && isabspath(path) - home = joinpath(homedir(), "") - if startswith(path, home) - path = joinpath("~", path[nextind(path, lastindex(home)):end]) - end +function pathrepr(path::String) + # print stdlib paths as @stdlib/Name + if startswith(path, stdlib_dir()) + path = "@stdlib/" * basename(path) end - return "`" * path * "`" + return "`" * Base.contractuser(path) * "`" end function project_key_order(key::String) @@ -1145,7 +1147,7 @@ function write_env(ctx::Context; display_diff=true) isempty(project["deps"]) && delete!(project, "deps") if !isempty(project) || ispath(env.project_file) if display_diff && !(ctx.currently_running_target) - printpkgstyle(ctx, :Updating, pathrepr(ctx, env.project_file)) + printpkgstyle(ctx, :Updating, pathrepr(env.project_file)) Pkg.Display.print_project_diff(ctx, old_env, env) end if !ctx.preview @@ -1158,7 +1160,7 @@ function write_env(ctx::Context; display_diff=true) # update the manifest file if !isempty(env.manifest) || ispath(env.manifest_file) if display_diff && !(ctx.currently_running_target) - printpkgstyle(ctx, :Updating, pathrepr(ctx, env.manifest_file)) + printpkgstyle(ctx, :Updating, pathrepr(env.manifest_file)) Pkg.Display.print_manifest_diff(ctx, old_env, env) end manifest = deepcopy(env.manifest) diff --git a/stdlib/Pkg/src/versions.jl b/stdlib/Pkg/src/versions.jl index c088c7de2c1a1..3ef108678654b 100644 --- a/stdlib/Pkg/src/versions.jl +++ b/stdlib/Pkg/src/versions.jl @@ -66,7 +66,7 @@ stricterupper(a::VersionBound, b::VersionBound) = isless_uu(a, b) ? a : b # `2.3.4` can be joined with `2.3.5` etc. function isjoinable(up::VersionBound, lo::VersionBound) - up.n == 0 && up.lo == 0 && return true + up.n == 0 && lo.n == 0 && return true if up.n == lo.n n = up.n for i = 1:(n - 1) diff --git a/stdlib/Pkg/test/pkg.jl b/stdlib/Pkg/test/pkg.jl index 32e1f8d18bb5c..d8af07ebbbee5 100644 --- a/stdlib/Pkg/test/pkg.jl +++ b/stdlib/Pkg/test/pkg.jl @@ -88,6 +88,9 @@ import Pkg.Types: semver_spec, VersionSpec @test_throws ErrorException semver_spec("^^0.2.3") @test_throws ErrorException semver_spec("^^0.2.3.4") @test_throws ErrorException semver_spec("0.0.0") + + @test Pkg.Types.isjoinable(Pkg.Types.VersionBound((1,5)), Pkg.Types.VersionBound((1,6))) + @test !(Pkg.Types.isjoinable(Pkg.Types.VersionBound((1,5)), Pkg.Types.VersionBound((1,6,0)))) end # TODO: Should rewrite these tests not to rely on internals like field names @@ -144,6 +147,9 @@ temp_pkg_dir() do project_path # VersionRange Pkg.add(PackageSpec(TEST_PKG.name, VersionSpec(VersionRange("0.3.0-0.3.2")))) @test Pkg.API.__installed()[TEST_PKG.name] == v"0.3.2" + # Check that adding another packages doesn't upgrade other packages + Pkg.add("Test") + @test Pkg.API.__installed()[TEST_PKG.name] == v"0.3.2" Pkg.update(; level = UPLEVEL_PATCH) @test Pkg.API.__installed()[TEST_PKG.name] == v"0.3.3" Pkg.update(; level = UPLEVEL_MINOR) @@ -153,7 +159,6 @@ temp_pkg_dir() do project_path @testset "testing" begin # TODO: Check that preview = true doesn't actually execute the test - # TODO: Test-only dependencies Pkg.add(TEST_PKG.name) Pkg.test(TEST_PKG.name; coverage=true) pkgdir = Base.locate_package(Base.PkgId(TEST_PKG.uuid, TEST_PKG.name)) @@ -251,6 +256,23 @@ temp_pkg_dir() do project_path end end end + mktempdir() do devdir + withenv("JULIA_PKG_DEVDIR" => devdir) do + try + https_url = "https://github.com/JuliaLang/Example.jl.git" + ssh_url = "ssh://git@github.com/JuliaLang/Example.jl.git" + @test Pkg.GitTools.normalize_url(https_url) == https_url + Pkg.setprotocol!("ssh") + @test Pkg.GitTools.normalize_url(https_url) == ssh_url + # TODO: figure out how to test this without + # having to deploy a ssh key on github + #Pkg.develop("Example") + #@test isinstalled(TEST_PKG) + finally + Pkg.setprotocol!() + end + end + end end @testset "check logging" begin @@ -414,6 +436,51 @@ temp_pkg_dir() do project_path end end +temp_pkg_dir() do project_path; cd(project_path) do + @testset "instantiating updated repo" begin + tmp = mktempdir() + cd(tmp) + depo1 = mktempdir() + depo2 = mktempdir() + + empty!(DEPOT_PATH) + pushfirst!(DEPOT_PATH, depo1) + LibGit2.close(LibGit2.clone("https://github.com/JuliaLang/Example.jl", "Example.jl")) + mkdir("machine1") + cd("machine1") + Pkg.activate(".") + Pkg.add(PackageSpec(path="../Example.jl")) + cd("..") + cp("machine1", "machine2") + empty!(DEPOT_PATH) + pushfirst!(DEPOT_PATH, depo2) + cd("machine2") + Pkg.activate(".") + Pkg.instantiate() + cd("..") + cd("Example.jl") + open("README.md", "a") do io + print(io, "Hello") + end + LibGit2.with(LibGit2.GitRepo(".")) do repo + LibGit2.add!(repo, "*") + LibGit2.commit(repo, "changes"; author=TEST_SIG, committer=TEST_SIG) + end + cd("../machine1") + empty!(DEPOT_PATH) + pushfirst!(DEPOT_PATH, depo1) + Pkg.activate(".") + Pkg.update() + cd("..") + cp("machine1/Manifest.toml", "machine2/Manifest.toml"; force=true) + cd("machine2") + empty!(DEPOT_PATH) + pushfirst!(DEPOT_PATH, depo2) + Pkg.activate(".") + Pkg.instantiate() + end +end end + temp_pkg_dir() do project_path cd(project_path) do project = """ @@ -448,6 +515,21 @@ end end end +@testset "printing of stdlib paths, issue #605" begin + path = Pkg.Types.stdlib_path("Test") + @test Pkg.Types.pathrepr(path) == "`@stdlib/Test`" +end + + +temp_pkg_dir() do project_path + @testset "Pkg.add should not mutate" begin + package_names = ["JSON"] + packages = PackageSpec.(package_names) + Pkg.add(packages) + @test [p.name for p in packages] == package_names + end +end + include("repl.jl") include("api.jl") diff --git a/stdlib/Pkg/test/repl.jl b/stdlib/Pkg/test/repl.jl index 3c80a9b7d904a..9c9651ce24887 100644 --- a/stdlib/Pkg/test/repl.jl +++ b/stdlib/Pkg/test/repl.jl @@ -3,6 +3,7 @@ module REPLTests using Pkg +using Pkg.Types: manifest_info, EnvCache import Pkg.Types.PkgError using UUIDs using Test @@ -10,9 +11,6 @@ import LibGit2 include("utils.jl") -const TEST_SIG = LibGit2.Signature("TEST", "TEST@TEST.COM", round(time()), 0) -const TEST_PKG = (name = "Example", uuid = UUID("7876af07-990d-54b4-ab0e-23690620f79a")) - function git_init_package(tmp, path) base = basename(path) pkgpath = joinpath(tmp, base) @@ -68,15 +66,15 @@ end @test length(statement.arguments) == 1 @test statement.arguments[1] == "dev" statement = Pkg.REPLMode.parse("add git@github.com:JuliaLang/Example.jl.git")[1] - @test "add" in statement.command.names + @test "add" == statement.command.canonical_name @test statement.arguments[1] == "git@github.com:JuliaLang/Example.jl.git" statement = Pkg.REPLMode.parse("add git@github.com:JuliaLang/Example.jl.git#master")[1] - @test "add" in statement.command.names + @test "add" == statement.command.canonical_name @test length(statement.arguments) == 2 @test statement.arguments[1] == "git@github.com:JuliaLang/Example.jl.git" @test statement.arguments[2] == "#master" statement = Pkg.REPLMode.parse("add git@github.com:JuliaLang/Example.jl.git#c37b675")[1] - @test "add" in statement.command.names + @test "add" == statement.command.canonical_name @test length(statement.arguments) == 2 @test statement.arguments[1] == "git@github.com:JuliaLang/Example.jl.git" @test statement.arguments[2] == "#c37b675" @@ -84,7 +82,7 @@ end @test statement.arguments[1] == "git@github.com:JuliaLang/Example.jl.git" @test statement.arguments[2] == "@v0.5.0" statement = Pkg.REPLMode.parse("add git@gitlab-fsl.jsc.näsan.guvv:drats/URGA2010.jl.git@0.5.0")[1] - @test "add" in statement.command.names + @test "add" == statement.command.canonical_name @test length(statement.arguments) == 2 @test statement.arguments[1] == "git@gitlab-fsl.jsc.näsan.guvv:drats/URGA2010.jl.git" @test statement.arguments[2] == "@0.5.0" @@ -264,21 +262,21 @@ temp_pkg_dir() do project_path pkg"activate Foo" # activate path Foo over deps Foo @test Base.active_project() == joinpath(path, "Foo", "Project.toml") pkg"activate ." - @test_logs (:info, r"new shared environment") pkg"activate --shared Foo" # activate shared Foo + @test_logs (:info, r"activating new environment at ") pkg"activate --shared Foo" # activate shared Foo @test Base.active_project() == joinpath(Pkg.envdir(), "Foo", "Project.toml") pkg"activate ." rm("Foo"; force=true, recursive=true) pkg"activate Foo" # activate path from developed Foo @test Base.active_project() == joinpath(path, "modules", "Foo", "Project.toml") pkg"activate ." - @test_logs (:info, r"new environment") pkg"activate ./Foo" # activate empty directory Foo (sidestep the developed Foo) + @test_logs (:info, r"activating new environment at ") pkg"activate ./Foo" # activate empty directory Foo (sidestep the developed Foo) @test Base.active_project() == joinpath(path, "Foo", "Project.toml") pkg"activate ." - @test_logs (:info, r"new environment") pkg"activate Bar" # activate empty directory Bar + @test_logs (:info, r"activating new environment at ") pkg"activate Bar" # activate empty directory Bar @test Base.active_project() == joinpath(path, "Bar", "Project.toml") pkg"activate ." pkg"add Example" # non-deved deps should not be activated - @test_logs (:info, r"new environment") pkg"activate Example" + @test_logs (:info, r"activating new environment at ") pkg"activate Example" @test Base.active_project() == joinpath(path, "Example", "Project.toml") pkg"activate ." cd(mkdir("tests")) @@ -311,8 +309,39 @@ cd(mktempdir()) do @test manifest["SubModule"][1]["path"] == joinpath("..", "SubModule") end +# path should not be relative when devdir() happens to be in project +# unless user used dev --local. +temp_pkg_dir() do depot + cd(mktempdir()) do + uuid = UUID("7876af07-990d-54b4-ab0e-23690620f79a") # Example + pkg"activate ." + withenv("JULIA_PKG_DEVDIR" => joinpath(pwd(), "dev")) do + pkg"dev Example" + @test manifest_info(EnvCache(), uuid)["path"] == joinpath(pwd(), "dev", "Example") + pkg"dev --shared Example" + @test manifest_info(EnvCache(), uuid)["path"] == joinpath(pwd(), "dev", "Example") + pkg"dev --local Example" + @test manifest_info(EnvCache(), uuid)["path"] == joinpath("dev", "Example") + end + end +end + +# test relative dev paths (#490) without existing Project.toml +temp_pkg_dir() do depot + cd(mktempdir()) do + pkg"activate NonExistent" + withenv("USER" => "Test User") do + pkg"generate Foo" + end + # this dev should not error even if NonExistent/Project.toml file is non-existent + @test !isdir("NonExistent") + pkg"dev Foo" + manifest = Pkg.Types.Context().env.manifest + @test manifest["Foo"][1]["path"] == joinpath("..", "Foo") + end +end + # develop with --shared and --local -using Pkg.Types: manifest_info, EnvCache cd(mktempdir()) do uuid = UUID("7876af07-990d-54b4-ab0e-23690620f79a") # Example pkg"activate ." @@ -324,72 +353,120 @@ cd(mktempdir()) do @test manifest_info(EnvCache(), uuid)["path"] == joinpath("dev", "Example") end +@testset "parse completions" begin + # meta options + @test Pkg.REPLMode.parse("--pre"; for_completions=true) == (:meta, "--pre", nothing, true) + @test Pkg.REPLMode.parse("--meta --pre"; for_completions=true) == (:meta, "--pre", nothing, true) + @test Pkg.REPLMode.parse("--meta -"; for_completions=true) == (:meta, "-", nothing, true) + @test Pkg.REPLMode.parse("--meta --"; for_completions=true) == (:meta, "--", nothing, true) + # commands + @test Pkg.REPLMode.parse("--preview"; for_completions=true) == (:cmd, "", nothing, true) + @test Pkg.REPLMode.parse("--preview ad"; for_completions=true) == (:cmd, "ad", nothing, true) + @test Pkg.REPLMode.parse("--meta --preview r"; for_completions=true) == (:cmd, "r", nothing, true) + @test Pkg.REPLMode.parse("--preview reg"; for_completions=true) == (:cmd, "reg", nothing, true) + # sub commands + @test Pkg.REPLMode.parse("--preview package"; for_completions=true) == + (:sub, "", "package", true) + @test Pkg.REPLMode.parse("--preview package a"; for_completions=true) == + (:sub, "a", "package", true) + # options + @test Pkg.REPLMode.parse("add -"; for_completions=true) == + (:opt, "-", Pkg.REPLMode.super_specs["package"]["add"], true) + @test Pkg.REPLMode.parse("up --m"; for_completions=true) == + (:opt, "--m", Pkg.REPLMode.super_specs["package"]["up"], true) + @test Pkg.REPLMode.parse("up --major --pro"; for_completions=true) == + (:opt, "--pro", Pkg.REPLMode.super_specs["package"]["up"], true) + @test Pkg.REPLMode.parse("foo --maj"; for_completions=true) === + nothing + # arguments + @test Pkg.REPLMode.parse("up --major Ex"; for_completions=true) == + (:arg, "Ex", Pkg.REPLMode.super_specs["package"]["up"], true) + @test Pkg.REPLMode.parse("--preview up --major foo Ex"; for_completions=true) == + (:arg, "Ex", Pkg.REPLMode.super_specs["package"]["up"], true) + @test Pkg.REPLMode.parse("remove --manifest Ex"; for_completions=true) == + (:arg, "Ex", Pkg.REPLMode.super_specs["package"]["remove"], false) +end + test_complete(s) = Pkg.REPLMode.completions(s,lastindex(s)) apply_completion(str) = begin c, r, s = test_complete(str) - @test s == true str[1:prevind(str, first(r))]*first(c) end # Autocompletions temp_pkg_dir() do project_path; cd(project_path) do - Pkg.Types.registries() - pkg"activate ." - c, r = test_complete("add Exam") - @test "Example" in c - c, r = test_complete("rm Exam") - @test isempty(c) - Pkg.REPLMode.pkgstr("develop $(joinpath(@__DIR__, "test_packages", "RequireDependency"))") - - c, r = test_complete("rm RequireDep") - @test "RequireDependency" in c - c, r = test_complete("rm -p RequireDep") - @test "RequireDependency" in c - c, r = test_complete("rm --project RequireDep") - @test "RequireDependency" in c - c, r = test_complete("rm Exam") - @test isempty(c) - c, r = test_complete("rm -p Exam") - @test isempty(c) - c, r = test_complete("rm --project Exam") - @test isempty(c) - - c, r = test_complete("rm -m RequireDep") - @test "RequireDependency" in c - c, r = test_complete("rm --manifest RequireDep") - @test "RequireDependency" in c - c, r = test_complete("rm -m Exam") - @test "Example" in c - c, r = test_complete("rm --manifest Exam") - @test "Example" in c - - c, r = test_complete("rm RequireDep") - @test "RequireDependency" in c - c, r = test_complete("rm Exam") - @test isempty(c) - c, r = test_complete("rm -m Exam") - c, r = test_complete("rm -m Exam") - @test "Example" in c - - pkg"add Example" - c, r = test_complete("rm Exam") - @test "Example" in c - c, r = test_complete("add --man") - @test "--manifest" in c - c, r = test_complete("rem") - @test "remove" in c - @test apply_completion("rm E") == "rm Example" - @test apply_completion("add Exampl") == "add Example" - - c, r = test_complete("preview r") - @test "remove" in c - c, r = test_complete("help r") - @test "remove" in c - @test !("rm" in c) - - c, r = test_complete("add REPL") - # Filtered by version - @test !("REPL" in c) + @testset "tab completion" begin + Pkg.Types.registries() + pkg"activate ." + c, r = test_complete("add Exam") + @test "Example" in c + c, r = test_complete("rm Exam") + @test isempty(c) + Pkg.REPLMode.pkgstr("develop $(joinpath(@__DIR__, "test_packages", "RequireDependency"))") + + c, r = test_complete("rm RequireDep") + @test "RequireDependency" in c + c, r = test_complete("rm -p RequireDep") + @test "RequireDependency" in c + c, r = test_complete("rm --project RequireDep") + @test "RequireDependency" in c + c, r = test_complete("rm Exam") + @test isempty(c) + c, r = test_complete("rm -p Exam") + @test isempty(c) + c, r = test_complete("rm --project Exam") + @test isempty(c) + + c, r = test_complete("rm -m RequireDep") + @test "RequireDependency" in c + c, r = test_complete("rm --manifest RequireDep") + @test "RequireDependency" in c + c, r = test_complete("rm -m Exam") + @test "Example" in c + c, r = test_complete("rm --manifest Exam") + @test "Example" in c + + c, r = test_complete("rm RequireDep") + @test "RequireDependency" in c + c, r = test_complete("rm Exam") + @test isempty(c) + c, r = test_complete("rm -m Exam") + c, r = test_complete("rm -m Exam") + @test "Example" in c + + pkg"add Example" + c, r = test_complete("rm Exam") + @test "Example" in c + c, r = test_complete("up --man") + @test "--manifest" in c + c, r = test_complete("rem") + @test "remove" in c + @test apply_completion("rm E") == "rm Example" + @test apply_completion("add Exampl") == "add Example" + + c, r = test_complete("preview r") + @test "remove" in c + c, r = test_complete("help r") + @test "remove" in c + @test !("rm" in c) + + c, r = test_complete("add REPL") + # Filtered by version + @test !("REPL" in c) + + mkdir("testdir") + c, r = test_complete("add ") + @test Sys.iswindows() ? ("testdir\\\\" in c) : ("testdir/" in c) + @test "Example" in c + @test apply_completion("add tes") == (Sys.iswindows() ? "add testdir\\\\" : "add testdir/") + @test apply_completion("add ./tes") == (Sys.iswindows() ? "add ./testdir\\\\" : "add ./testdir/") + c, r = test_complete("dev ./") + @test (Sys.iswindows() ? ("testdir\\\\" in c) : ("testdir/" in c)) + # dont complete files + touch("README.md") + c, r = test_complete("add RE") + @test !("README.md" in c) + end # testset end end temp_pkg_dir() do project_path; cd(project_path) do @@ -421,12 +498,12 @@ temp_pkg_dir() do project_path; cd(project_path) do print(io, """ [compat] - JSON = "0.16.0" + JSON = "0.18.0" """ ) end pkg"up" - @test Pkg.API.installed()["JSON"].minor == 16 + @test Pkg.API.installed()["JSON"].minor == 18 write("Project.toml", old_project) pkg"up" @test Pkg.API.installed()["JSON"] == current_json @@ -820,7 +897,6 @@ end @test_throws PkgError Pkg.REPLMode.pkgstr("--foo=foo add Example") @test_throws PkgError Pkg.REPLMode.pkgstr("--bar add Example") @test_throws PkgError Pkg.REPLMode.pkgstr("-x add Example") - # malformed, but registered meta option @test_throws PkgError Pkg.REPLMode.pkgstr("--env Example") end end end end @@ -846,6 +922,27 @@ end end end end end +@testset "preview" begin + temp_pkg_dir() do project_path; cd_tempdir() do tmpdir; with_temp_env() do; + pkg"add Example" + pkg"preview rm Example" + @test isinstalled(TEST_PKG) + pkg"rm Example" + pkg"preview add Example" + @test !isinstalled(TEST_PKG) + # as a meta option + pkg"add Example" + pkg"--preview rm Example" + @test isinstalled(TEST_PKG) + pkg"rm Example" + pkg"--preview add Example" + @test !isinstalled(TEST_PKG) + # both + pkg"--preview preview add Example" + @test !isinstalled(TEST_PKG) + end end end +end + @testset "`parse_quotes` unit tests" begin qwords = Pkg.REPLMode.parse_quotes("\"Don't\" forget to '\"test\"'") @test qwords[1].isquoted diff --git a/stdlib/Pkg/test/utils.jl b/stdlib/Pkg/test/utils.jl index a93ca4c92872a..54f1a3982a877 100644 --- a/stdlib/Pkg/test/utils.jl +++ b/stdlib/Pkg/test/utils.jl @@ -15,11 +15,13 @@ function temp_pkg_dir(fn::Function) empty!(DEPOT_PATH) Base.HOME_PROJECT[] = nothing Base.ACTIVE_PROJECT[] = nothing - mktempdir() do env_dir - mktempdir() do depot_dir - push!(LOAD_PATH, "@", "@v#.#", "@stdlib") - push!(DEPOT_PATH, depot_dir) - fn(env_dir) + withenv("JULIA_PROJECT" => nothing, "JULIA_LOAD_PATH" => nothing) do + mktempdir() do env_dir + mktempdir() do depot_dir + push!(LOAD_PATH, "@", "@v#.#", "@stdlib") + push!(DEPOT_PATH, depot_dir) + fn(env_dir) + end end end finally @@ -80,3 +82,8 @@ function with_pkg_env(fn::Function, path::AbstractString="."; change_dir=false) Pkg.activate() end end + +import LibGit2 +using UUIDs +const TEST_SIG = LibGit2.Signature("TEST", "TEST@TEST.COM", round(time()), 0) +const TEST_PKG = (name = "Example", uuid = UUID("7876af07-990d-54b4-ab0e-23690620f79a")) diff --git a/stdlib/REPL/docs/src/index.md b/stdlib/REPL/docs/src/index.md index 42348a1eae06a..2afa93dfb32ea 100644 --- a/stdlib/REPL/docs/src/index.md +++ b/stdlib/REPL/docs/src/index.md @@ -6,22 +6,16 @@ it has a searchable history, tab-completion, many helpful keybindings, and dedic shell modes. The REPL can be started by simply calling `julia` with no arguments or double-clicking on the executable: -``` -$ julia - _ - _ _ _(_)_ | A fresh approach to technical computing - (_) | (_) (_) | Documentation: https://docs.julialang.org - _ _ _| |_ __ _ | Type "?help" for help. - | | | | | | |/ _` | | - | | |_| | | | (_| | | Version 0.6.0-dev.2493 (2017-01-31 18:53 UTC) - _/ |\__'_|_|_|\__'_| | Commit c99e12c* (0 days old master) -|__/ | x86_64-linux-gnu - -julia> +```@eval +io = IOBuffer() +Base.banner(io) +banner = String(take!(io)) +import Markdown +Markdown.parse("```\n\$ julia\n\n$(banner)\njulia>\n```") ``` To exit the interactive session, type `^D` -- the control key together with the `d` key on a blank -line -- or type `quit()` followed by the return or enter key. The REPL greets you with a banner +line -- or type `exit()` followed by the return or enter key. The REPL greets you with a banner and a `julia>` prompt. ## The different prompt modes diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index 2ce39a860bc51..977c4af6edb92 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -346,7 +346,7 @@ function complete_line(c::REPLCompletionProvider, s) partial = beforecursor(s.input_buffer) full = LineEdit.input_string(s) ret, range, should_complete = completions(full, lastindex(partial)) - return map(completion_text, ret), partial[range], should_complete + return unique!(map(completion_text, ret)), partial[range], should_complete end function complete_line(c::ShellCompletionProvider, s) @@ -354,14 +354,14 @@ function complete_line(c::ShellCompletionProvider, s) partial = beforecursor(s.input_buffer) full = LineEdit.input_string(s) ret, range, should_complete = shell_completions(full, lastindex(partial)) - return map(completion_text, ret), partial[range], should_complete + return unique!(map(completion_text, ret)), partial[range], should_complete end function complete_line(c::LatexCompletions, s) partial = beforecursor(LineEdit.buffer(s)) full = LineEdit.input_string(s) ret, range, should_complete = bslash_completions(full, lastindex(partial))[2] - return map(completion_text, ret), partial[range], should_complete + return unique!(map(completion_text, ret)), partial[range], should_complete end mutable struct REPLHistoryProvider <: HistoryProvider diff --git a/stdlib/REPL/test/replcompletions.jl b/stdlib/REPL/test/replcompletions.jl index 2d03dbf925d32..2e8ae033ee0d0 100644 --- a/stdlib/REPL/test/replcompletions.jl +++ b/stdlib/REPL/test/replcompletions.jl @@ -102,6 +102,11 @@ let s = "" @test s[r] == "" end +let s = "using REP" + c, r = test_complete(s) + @test count(isequal("REPL"), c) == 1 +end + let s = "Comp" c, r = test_complete(s) @test "CompletionFoo" in c diff --git a/stdlib/Random/src/Random.jl b/stdlib/Random/src/Random.jl index 42aebc1344d42..1ac74ad5e64fd 100644 --- a/stdlib/Random/src/Random.jl +++ b/stdlib/Random/src/Random.jl @@ -23,9 +23,7 @@ export rand!, randn!, shuffle, shuffle!, randperm, randperm!, randcycle, randcycle!, - AbstractRNG, MersenneTwister, RandomDevice, - randjump - + AbstractRNG, MersenneTwister, RandomDevice ## general definitions diff --git a/stdlib/Random/src/misc.jl b/stdlib/Random/src/misc.jl index 0e7bb9782e500..d24fe9c457ccf 100644 --- a/stdlib/Random/src/misc.jl +++ b/stdlib/Random/src/misc.jl @@ -121,10 +121,24 @@ function randsubseq!(r::AbstractRNG, S::AbstractArray, A::AbstractArray, p::Real end """ - randsubseq!(S, A, p) + randsubseq!([rng=GLOBAL_RNG,] S, A, p) Like [`randsubseq`](@ref), but the results are stored in `S` (which is resized as needed). + +# Examples +```jldoctest +julia> rng = MersenneTwister(1234); + +julia> S = Int64[]; + +julia> randsubseq!(rng, S, collect(1:8), 0.3); + +julia> S +2-element Array{Int64,1}: + 7 + 8 +``` """ randsubseq!(S::AbstractArray, A::AbstractArray, p::Real) = randsubseq!(GLOBAL_RNG, S, A, p) @@ -132,12 +146,22 @@ randsubseq(r::AbstractRNG, A::AbstractArray{T}, p::Real) where {T} = randsubseq!(r, T[], A, p) """ - randsubseq(A, p) -> Vector + randsubseq([rng=GLOBAL_RNG,] 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`. + +# Examples +```jldoctest +julia> rng = MersenneTwister(1234); + +julia> randsubseq(rng, collect(1:8), 0.3) +2-element Array{Int64,1}: + 7 + 8 +``` """ randsubseq(A::AbstractArray, p::Real) = randsubseq(GLOBAL_RNG, A, p) @@ -182,6 +206,7 @@ julia> shuffle!(rng, Vector(1:16)) function shuffle!(r::AbstractRNG, a::AbstractArray) @assert !has_offset_axes(a) n = length(a) + n <= 1 && return a # nextpow below won't work with n == 0 @assert n <= Int64(2)^52 mask = nextpow(2, n) - 1 for i = n:-1:2 diff --git a/stdlib/Random/test/runtests.jl b/stdlib/Random/test/runtests.jl index 91d3db9eb994a..83e4b44e83697 100644 --- a/stdlib/Random/test/runtests.jl +++ b/stdlib/Random/test/runtests.jl @@ -686,3 +686,11 @@ end @test Random.gentype(Random.UInt52(UInt128)) == UInt128 @test Random.gentype(Random.UInt104()) == UInt128 end + +@testset "shuffle[!]" begin + a = [] + @test shuffle(a) == a # issue #28727 + @test shuffle!(a) === a + a = rand(Int, 1) + @test shuffle(a) == a +end diff --git a/stdlib/SparseArrays/Project.toml b/stdlib/SparseArrays/Project.toml index c3d35186e1b8e..bffdfc775fdc6 100644 --- a/stdlib/SparseArrays/Project.toml +++ b/stdlib/SparseArrays/Project.toml @@ -7,6 +7,7 @@ Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" [targets] -test = ["Test"] +test = ["Test", "InteractiveUtils"] diff --git a/stdlib/SparseArrays/src/linalg.jl b/stdlib/SparseArrays/src/linalg.jl index 6e9a751dd4306..ad8ffb2b14f63 100644 --- a/stdlib/SparseArrays/src/linalg.jl +++ b/stdlib/SparseArrays/src/linalg.jl @@ -968,7 +968,7 @@ function copyinds!(C::SparseMatrixCSC, A::SparseMatrixCSC) end # multiply by diagonal matrix as vector -function mul!(C::SparseMatrixCSC, A::SparseMatrixCSC, D::Diagonal{<:Vector}) +function mul!(C::SparseMatrixCSC, A::SparseMatrixCSC, D::Diagonal{T, <:Vector}) where T m, n = size(A) b = D.diag (n==length(b) && size(A)==size(C)) || throw(DimensionMismatch()) @@ -982,7 +982,7 @@ function mul!(C::SparseMatrixCSC, A::SparseMatrixCSC, D::Diagonal{<:Vector}) C end -function mul!(C::SparseMatrixCSC, D::Diagonal{<:Vector}, A::SparseMatrixCSC) +function mul!(C::SparseMatrixCSC, D::Diagonal{T, <:Vector}, A::SparseMatrixCSC) where T m, n = size(A) b = D.diag (m==length(b) && size(A)==size(C)) || throw(DimensionMismatch()) diff --git a/stdlib/SparseArrays/src/sparsematrix.jl b/stdlib/SparseArrays/src/sparsematrix.jl index a88f27edd90d1..5b107ff86d258 100644 --- a/stdlib/SparseArrays/src/sparsematrix.jl +++ b/stdlib/SparseArrays/src/sparsematrix.jl @@ -356,6 +356,7 @@ similar(S::SparseMatrixCSC, ::Type{TvNew}, ::Type{TiNew}, m::Integer, n::Integer # converting between SparseMatrixCSC types +SparseMatrixCSC(S::SparseMatrixCSC) = copy(S) AbstractMatrix{Tv}(A::SparseMatrixCSC) where {Tv} = SparseMatrixCSC{Tv}(A) SparseMatrixCSC{Tv}(S::SparseMatrixCSC{Tv}) where {Tv} = copy(S) SparseMatrixCSC{Tv}(S::SparseMatrixCSC) where {Tv} = SparseMatrixCSC{Tv,eltype(S.colptr)}(S) @@ -400,6 +401,16 @@ function SparseMatrixCSC{Tv,Ti}(M::StridedMatrix) where {Tv,Ti} end SparseMatrixCSC(M::Adjoint{<:Any,<:SparseMatrixCSC}) = copy(M) SparseMatrixCSC(M::Transpose{<:Any,<:SparseMatrixCSC}) = copy(M) +SparseMatrixCSC{Tv}(M::Adjoint{Tv,SparseMatrixCSC{Tv}}) where {Tv} = copy(M) +SparseMatrixCSC{Tv}(M::Transpose{Tv,SparseMatrixCSC{Tv}}) where {Tv} = copy(M) +SparseMatrixCSC{Tv,Ti}(M::Adjoint{Tv,SparseMatrixCSC{Tv,Ti}}) where {Tv,Ti} = copy(M) +SparseMatrixCSC{Tv,Ti}(M::Transpose{Tv,SparseMatrixCSC{Tv,Ti}}) where {Tv,Ti} = copy(M) + +# converting from adjoint or transpose sparse matrices to sparse matrices with different eltype +SparseMatrixCSC{Tv}(M::Adjoint{<:Any,SparseMatrixCSC}) where {Tv} = SparseMatrixCSC{Tv}(copy(M)) +SparseMatrixCSC{Tv}(M::Transpose{<:Any,SparseMatrixCSC}) where {Tv} = SparseMatrixCSC{Tv}(copy(M)) +SparseMatrixCSC{Tv,Ti}(M::Adjoint{<:Any,SparseMatrixCSC}) where {Tv,Ti} = SparseMatrixCSC{Tv,Ti}(copy(M)) +SparseMatrixCSC{Tv,Ti}(M::Transpose{<:Any,SparseMatrixCSC}) where {Tv,Ti} = SparseMatrixCSC{Tv,Ti}(copy(M)) # converting from SparseMatrixCSC to other matrix types function Matrix(S::SparseMatrixCSC{Tv}) where Tv diff --git a/stdlib/SparseArrays/test/sparse.jl b/stdlib/SparseArrays/test/sparse.jl index f88c6dde06493..416cbffac27cf 100644 --- a/stdlib/SparseArrays/test/sparse.jl +++ b/stdlib/SparseArrays/test/sparse.jl @@ -8,6 +8,7 @@ using LinearAlgebra using Base.Printf: @printf using Random using Test: guardseed +using InteractiveUtils: @which @testset "issparse" begin @test issparse(sparse(fill(1,5,5))) @@ -2289,4 +2290,21 @@ end @test adjoint(MC) == copy(adjoint(SC)) end +@testset "Issue #28634" begin + a = SparseMatrixCSC{Int8, Int16}([1 2; 3 4]) + na = SparseMatrixCSC(a) + @test typeof(a) === typeof(na) +end + +#PR #29045 +@testset "Issue #28934" begin + A = sprand(5,5,0.5) + D = Diagonal(rand(5)) + C = copy(A) + m1 = @which mul!(C,A,D) + m2 = @which mul!(C,D,A) + @test m1.module == SparseArrays + @test m2.module == SparseArrays +end + end # module diff --git a/stdlib/Statistics/src/Statistics.jl b/stdlib/Statistics/src/Statistics.jl index 28d2cae7837e3..bc7a857079e88 100644 --- a/stdlib/Statistics/src/Statistics.jl +++ b/stdlib/Statistics/src/Statistics.jl @@ -248,7 +248,7 @@ end Compute the sample variance of a collection `v` with known mean(s) `m`, optionally over the given dimensions. `m` may contain means for each dimension of `v`. If `corrected` is `true`, then the sum is scaled with `n-1`, -whereas the sum is scaled with `n` if `corrected` is `false` where `n = length(x)`. +whereas the sum is scaled with `n` if `corrected` is `false` where `n = length(v)`. !!! note If array contains `NaN` or [`missing`](@ref) values, the result is also @@ -278,7 +278,7 @@ The algorithm will return an estimator of the generative distribution's variance under the assumption that each entry of `v` is an IID drawn from that generative distribution. This computation is equivalent to calculating `sum(abs2, v - mean(v)) / (length(v) - 1)`. If `corrected` is `true`, then the sum is scaled with `n-1`, -whereas the sum is scaled with `n` if `corrected` is `false` where `n = length(x)`. +whereas the sum is scaled with `n` if `corrected` is `false` where `n = length(v)`. The mean `mean` over the region may be provided. !!! note @@ -345,7 +345,7 @@ deviation under the assumption that each entry of `v` is an IID drawn from that distribution. This computation is equivalent to calculating `sqrt(sum((v - mean(v)).^2) / (length(v) - 1))`. A pre-computed `mean` may be provided. If `corrected` is `true`, then the sum is scaled with `n-1`, whereas the sum is scaled with `n` if `corrected` is -`false` where `n = length(x)`. +`false` where `n = length(v)`. !!! note If array contains `NaN` or [`missing`](@ref) values, the result is also @@ -376,7 +376,7 @@ std(iterable; corrected::Bool=true, mean=nothing) = Compute the sample standard deviation of a vector `v` with known mean `m`. If `corrected` is `true`, then the sum is scaled with `n-1`, whereas the sum is -scaled with `n` if `corrected` is `false` where `n = length(x)`. +scaled with `n` if `corrected` is `false` where `n = length(v)`. !!! note If array contains `NaN` or [`missing`](@ref) values, the result is also @@ -882,7 +882,7 @@ probabilities `p` on the interval [0,1]. The keyword argument `sorted` indicates `itr` can be assumed to be sorted. Quantiles are computed via linear interpolation between the points `((k-1)/(n-1), v[k])`, -for `k = 1:n` where `n = length(v)`. This corresponds to Definition 7 of Hyndman and Fan +for `k = 1:n` where `n = length(itr)`. This corresponds to Definition 7 of Hyndman and Fan (1996), and is the same as the R default. !!! note diff --git a/test/ambiguous.jl b/test/ambiguous.jl index 6835a2c1977f7..02d076d5eb8ba 100644 --- a/test/ambiguous.jl +++ b/test/ambiguous.jl @@ -273,6 +273,8 @@ end pop!(need_to_handle_undef_sparam, first(methods(Core.Compiler.same_names))) pop!(need_to_handle_undef_sparam, which(Core.Compiler.convert, (Type{Union{Core.Compiler.Some{T}, Nothing}} where T, Core.Compiler.Some))) pop!(need_to_handle_undef_sparam, which(Core.Compiler.convert, (Type{Union{T, Nothing}} where T, Core.Compiler.Some))) + pop!(need_to_handle_undef_sparam, which(Core.Compiler.convert, Tuple{Type{Tuple{Vararg{Int}}}, Tuple{}})) + pop!(need_to_handle_undef_sparam, which(Core.Compiler.convert, Tuple{Type{Tuple{Vararg{Int}}}, Tuple{Int8}})) @test need_to_handle_undef_sparam == Set() end let need_to_handle_undef_sparam = @@ -295,6 +297,8 @@ end pop!(need_to_handle_undef_sparam, which(Base.nonmissingtype, Tuple{Type{Union{Missing, T}} where T})) pop!(need_to_handle_undef_sparam, which(Base.convert, (Type{Union{Some{T}, Nothing}} where T, Some))) pop!(need_to_handle_undef_sparam, which(Base.convert, (Type{Union{T, Nothing}} where T, Some))) + pop!(need_to_handle_undef_sparam, which(Base.convert, Tuple{Type{Tuple{Vararg{Int}}}, Tuple{}})) + pop!(need_to_handle_undef_sparam, which(Base.convert, Tuple{Type{Tuple{Vararg{Int}}}, Tuple{Int8}})) @test need_to_handle_undef_sparam == Set() end end diff --git a/test/bigint.jl b/test/bigint.jl index 01acd5449b8c3..cd4c48f44873d 100644 --- a/test/bigint.jl +++ b/test/bigint.jl @@ -401,3 +401,27 @@ end # Issue #24298 @test mod(BigInt(6), UInt(5)) == mod(6, 5) + +@testset "cmp has values in [-1, 0, 1], issue #28780" begin + # _rand produces values whose log2 is better distributed than rand + _rand(::Type{BigInt}, n=1000) = let x = big(2)^rand(1:rand(1:n)) + rand(-x:x) + end + _rand(F::Type{<:AbstractFloat}) = F(_rand(BigInt, round(Int, log2(floatmax(F))))) + rand(F) + _rand(T) = rand(T) + for T in (Base.BitInteger_types..., BigInt, Float64, Float32, Float16) + @test cmp(big(2)^130, one(T)) === 1 + @test cmp(-big(2)^130, one(T)) === -1 + c = cmp(_rand(BigInt), _rand(T)) + @test c ∈ (-1, 0, 1) + @test c isa Int + (T <: Integer && T !== BigInt) || continue + x = rand(T) + @test cmp(big(2)^130, x) === cmp(x, -big(2)^130) === 1 + @test cmp(-big(2)^130, x) === cmp(x, big(2)^130) === -1 + @test cmp(big(x), x) === cmp(x, big(x)) === 0 + end + c = cmp(_rand(BigInt), _rand(BigInt)) + @test c ∈ (-1, 0, 1) + @test c isa Int +end diff --git a/test/compiler/compiler.jl b/test/compiler/compiler.jl index 904695982e030..008b05d1de56c 100644 --- a/test/compiler/compiler.jl +++ b/test/compiler/compiler.jl @@ -1977,3 +1977,23 @@ function bar28444() e[1] end @test bar28444() == 1 + +# issue #28641 +struct VoxelIndices{T <: Integer} + voxCrnrPos::NTuple{8,NTuple{3,T}} + voxEdgeCrnrs::NTuple{19, NTuple{2,T}} + voxEdgeDir::NTuple{19,T} + voxEdgeIx::NTuple{8,NTuple{8,T}} + subTets::NTuple{6,NTuple{4,T}} + tetEdgeCrnrs::NTuple{6,NTuple{2,T}} + tetTri::NTuple{16,NTuple{6,T}} +end +f28641(x::VoxelIndices, f) = getfield(x, f) +@test Base.return_types(f28641, (Any,Symbol)) == Any[Tuple] + +# issue #29036 +function f29036(s, i) + val, i = iterate(s, i) + val +end +@test Base.return_types(f29036, (String, Int)) == Any[Char] diff --git a/test/core.jl b/test/core.jl index 7dd27fefe03fc..be7876b9b2bb9 100644 --- a/test/core.jl +++ b/test/core.jl @@ -141,6 +141,8 @@ end @test typejoin(Tuple{Vararg{Int,2}}, Tuple{Int,Int,Int}) === Tuple{Int,Int,Vararg{Int}} @test typejoin(Tuple{Vararg{Int,2}}, Tuple{Vararg{Int}}) === Tuple{Vararg{Int}} +@test typejoin(NTuple{3,Tuple}, NTuple{2,T} where T) == Tuple{Any,Any,Vararg{Tuple}} + # issue #26321 struct T26321{N,S<:NTuple{N}} t::S @@ -3254,7 +3256,7 @@ let @test forouter() == 3 end -@test_throws ErrorException("syntax: no outer variable declaration exists for \"for outer\"") @eval function f() +@test_throws ErrorException("syntax: no outer local variable declaration exists for \"for outer\"") @eval function f() for outer i = 1:2 end end @@ -6695,3 +6697,25 @@ function repackage28445() true end @test repackage28445() + +# issue #28597 +@test_throws ErrorException Array{Int, 2}(undef, 0, -10) +@test_throws ErrorException Array{Int, 2}(undef, -10, 0) +@test_throws ErrorException Array{Int, 2}(undef, -1, -1) + +# issue #29145 +struct T29145{A,B} + function T29145() + new{S,Ref{S}}() where S + end +end +@test_throws TypeError T29145() + +# issue #29175 +function f29175(tuple::T) where {T<:Tuple} + prefix::Tuple{T.parameters[1:end-1]...} = tuple[1:length(T.parameters)-1] + x = prefix + prefix = x # force another conversion to declared type + return prefix +end +@test f29175((1,2,3)) === (1,2) diff --git a/test/docs.jl b/test/docs.jl index 3a925d65a4334..c6cbb3847ae28 100644 --- a/test/docs.jl +++ b/test/docs.jl @@ -1121,6 +1121,13 @@ struct A_20087 end @test docstrings_equal(@doc(A_20087()), doc"a") +struct B_20087 end + +"""b""" +(::B_20087)() = a + +@test docstrings_equal(@doc(B_20087()), doc"b") + # issue #27832 _last_atdoc = Core.atdoc diff --git a/test/enums.jl b/test/enums.jl index aeccadb44e502..c85ef556dd58f 100644 --- a/test/enums.jl +++ b/test/enums.jl @@ -77,8 +77,8 @@ end @test Int(_neg4) === -4 @test Int(_neg3) === -3 -@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) +@test_throws ArgumentError("invalid value for Enum Test1, _zerofp = 0.0; values must be integers") @macrocall(@enum Test1 _zerofp=0.0) +@test_throws ArgumentError("invalid value for Enum Test11, _zerofp2 = 0.5; values must be integers") @macrocall(@enum Test11 _zerofp2=0.5) @enum Test111 _zerobi=BigInt(1) @test Integer(_zerobi) == 1 @@ -107,9 +107,9 @@ end @test typeof(Integer(_one_Test6)) == UInt128 # enum values must be integers -@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_throws ArgumentError("invalid value for Enum Test7, _zero = \"zero\"; values must be integers") @macrocall(@enum Test7 _zero="zero") +@test_throws ArgumentError("invalid value for Enum Test8, _zero = '0'; values must be integers") @macrocall(@enum Test8 _zero='0') +@test_throws ArgumentError("invalid value for Enum Test9, _zero = 0.5; values must be integers") @macrocall(@enum Test9 _zero=0.5) # test macro handles keyword arguments @enum(Test11, _zero_Test11=2, diff --git a/test/inline.jl b/test/inline.jl index 1d5554b5b362c..447bcdee51a22 100644 --- a/test/inline.jl +++ b/test/inline.jl @@ -111,6 +111,16 @@ let a = read21311() @test a[] == 1 end +# issue #29083 +f29083(;μ,σ) = μ + σ*randn() +g29083() = f29083(μ=2.0,σ=0.1) +let c = code_typed(g29083, ())[1][1].code + # make sure no call to kwfunc remains + @test !any(e->(isa(e,Expr) && ((e.head === :invoke && e.args[1].def.name === :kwfunc) || + (e.head === :foreigncall && e.args[1] === QuoteNode(:jl_get_keyword_sorter)))), + c) +end + @testset "issue #19122: [no]inline of short func. def. with return type annotation" begin exf19122 = @macroexpand(@inline f19122()::Bool = true) exg19122 = @macroexpand(@noinline g19122()::Bool = true) @@ -137,3 +147,7 @@ end (src, _) = code_typed(sum27403, Tuple{Vector{Int}})[1] @test !any(x -> x isa Expr && x.head === :invoke, src.code) end + +# check that type.mutable can be fully eliminated +f_mutable_nothrow(s::String) = Val{typeof(s).mutable} +@test length(code_typed(f_mutable_nothrow, (String,))[1][1].code) == 1 diff --git a/test/intfuncs.jl b/test/intfuncs.jl index 4ead5b224fad0..b060bc69686b2 100644 --- a/test/intfuncs.jl +++ b/test/intfuncs.jl @@ -139,6 +139,15 @@ end @test iszero([Base.ndigits0z(false, b) for b in [-20:-2;2:20]]) @test all(n -> n == 1, Base.ndigits0z(true, b) for b in [-20:-2;2:20]) @test all(n -> n == 1, ndigits(x, base=b) for b in [-20:-2;2:20] for x in [true, false]) + + # issue #29148 + @test ndigits(typemax(UInt64), base=-2) == ndigits(big(typemax(UInt64)), base=-2) + for T in Base.BitInteger_types + n = rand(T) + b = -rand(2:100) + @test ndigits(n, base=b) == ndigits(big(n), base=b) + end + end @testset "bin/oct/dec/hex/bits" begin @test string(UInt32('3'), base = 2) == "110011" diff --git a/test/iterators.jl b/test/iterators.jl index 5fca4f11b91ad..6e5f88a72cd0d 100644 --- a/test/iterators.jl +++ b/test/iterators.jl @@ -365,6 +365,7 @@ end @test length(flatten(zip(1:3, 4:6))) == 6 @test length(flatten(1:6)) == 6 @test collect(flatten(Any[])) == Any[] +@test collect(flatten(())) == Union{}[] @test_throws ArgumentError length(flatten(NTuple[(1,), ()])) # #16680 @test_throws ArgumentError length(flatten([[1], [1]])) diff --git a/test/llvmpasses/gcroots.ll b/test/llvmpasses/gcroots.ll index 958a735b8efca..eb72e2e580d91 100644 --- a/test/llvmpasses/gcroots.ll +++ b/test/llvmpasses/gcroots.ll @@ -414,6 +414,111 @@ top: ret %jl_value_t addrspace(10)* %obj } +define void @vecphi(i1 %cond, <2 x %jl_value_t addrspace(10)*> *%arg) { +; CHECK-LABEL: @vecphi +; CHECK: %gcframe = alloca %jl_value_t addrspace(10)*, i32 4 +top: + %ptls = call %jl_value_t*** @julia.ptls_states() + br i1 %cond, label %A, label %B + +A: + br label %common + +B: + %loaded = load <2 x %jl_value_t addrspace(10)*>, <2 x %jl_value_t addrspace(10)*> *%arg + call void @jl_safepoint() + br label %common + +common: + %phi = phi <2 x %jl_value_t addrspace(10)*> [ zeroinitializer, %A ], [ %loaded, %B ] + call void @jl_safepoint() + %el1 = extractelement <2 x %jl_value_t addrspace(10)*> %phi, i32 0 + %el2 = extractelement <2 x %jl_value_t addrspace(10)*> %phi, i32 1 + call void @one_arg_boxed(%jl_value_t addrspace(10)* %el1) + call void @one_arg_boxed(%jl_value_t addrspace(10)* %el2) + unreachable +} + +define i8 @phi_arrayptr(i1 %cond) { +; CHECK-LABEL: @phi_arrayptr +; CHECK: %gcframe = alloca %jl_value_t addrspace(10)*, i32 4 +top: + %ptls = call %jl_value_t*** @julia.ptls_states() + br i1 %cond, label %A, label %B + +A: + %obj1 = call %jl_value_t addrspace(10) *@alloc() + %obj2 = call %jl_value_t addrspace(10) *@alloc() + %decayed1 = addrspacecast %jl_value_t addrspace(10) *%obj1 to %jl_value_t addrspace(11) * + %arrayptrptr1 = bitcast %jl_value_t addrspace(11) *%decayed1 to i8 addrspace(13)* addrspace(11)* + %arrayptr1 = load i8 addrspace(13)*, i8 addrspace(13)* addrspace(11)* %arrayptrptr1 + %decayed2 = addrspacecast %jl_value_t addrspace(10) *%obj2 to %jl_value_t addrspace(11) * + %arrayptrptr2 = bitcast %jl_value_t addrspace(11) *%decayed2 to i8 addrspace(13)* addrspace(11)* + %arrayptr2 = load i8 addrspace(13)*, i8 addrspace(13)* addrspace(11)* %arrayptrptr2 + %insert1 = insertelement <2 x i8 addrspace(13)*> undef, i8 addrspace(13)* %arrayptr1, i32 0 + %insert2 = insertelement <2 x i8 addrspace(13)*> %insert1, i8 addrspace(13)* %arrayptr2, i32 1 + call void @jl_safepoint() + br label %common + +B: + br label %common + +common: +; CHECK: %gclift +; CHECK: %gclift1 +; CHECK-NOT: %gclift2 + %phi = phi <2 x i8 addrspace(13)*> [ %insert2, %A ], [ zeroinitializer, %B ] + call void @jl_safepoint() + %el1 = extractelement <2 x i8 addrspace(13)*> %phi, i32 0 + %el2 = extractelement <2 x i8 addrspace(13)*> %phi, i32 1 + %l1 = load i8, i8 addrspace(13)* %el1 + %l2 = load i8, i8 addrspace(13)* %el2 + %add = add i8 %l1, %l2 + ret i8 %add +} + +define void @vecselect(i1 %cond, <2 x %jl_value_t addrspace(10)*> *%arg) { +; CHECK-LABEL: @vecselect +; CHECK: %gcframe = alloca %jl_value_t addrspace(10)*, i32 4 +top: + %ptls = call %jl_value_t*** @julia.ptls_states() + %loaded = load <2 x %jl_value_t addrspace(10)*>, <2 x %jl_value_t addrspace(10)*> *%arg + call void @jl_safepoint() + %select = select i1 %cond, <2 x %jl_value_t addrspace(10)*> zeroinitializer, <2 x %jl_value_t addrspace(10)*> %loaded + call void @jl_safepoint() + %el1 = extractelement <2 x %jl_value_t addrspace(10)*> %select, i32 0 + %el2 = extractelement <2 x %jl_value_t addrspace(10)*> %select, i32 1 + call void @one_arg_boxed(%jl_value_t addrspace(10)* %el1) + call void @one_arg_boxed(%jl_value_t addrspace(10)* %el2) + unreachable +} + +define i8 @select_arrayptr(i1 %cond) { +; CHECK-LABEL: @select_arrayptr +; CHECK: %gcframe = alloca %jl_value_t addrspace(10)*, i32 4 +top: + %ptls = call %jl_value_t*** @julia.ptls_states() + %obj1 = call %jl_value_t addrspace(10) *@alloc() + %obj2 = call %jl_value_t addrspace(10) *@alloc() + %decayed1 = addrspacecast %jl_value_t addrspace(10) *%obj1 to %jl_value_t addrspace(11) * + %arrayptrptr1 = bitcast %jl_value_t addrspace(11) *%decayed1 to i8 addrspace(13)* addrspace(11)* + %arrayptr1 = load i8 addrspace(13)*, i8 addrspace(13)* addrspace(11)* %arrayptrptr1 + %decayed2 = addrspacecast %jl_value_t addrspace(10) *%obj2 to %jl_value_t addrspace(11) * + %arrayptrptr2 = bitcast %jl_value_t addrspace(11) *%decayed2 to i8 addrspace(13)* addrspace(11)* + %arrayptr2 = load i8 addrspace(13)*, i8 addrspace(13)* addrspace(11)* %arrayptrptr2 + %insert1 = insertelement <2 x i8 addrspace(13)*> undef, i8 addrspace(13)* %arrayptr1, i32 0 + %insert2 = insertelement <2 x i8 addrspace(13)*> %insert1, i8 addrspace(13)* %arrayptr2, i32 1 + call void @jl_safepoint() + %select = select i1 %cond, <2 x i8 addrspace(13)*> %insert2, <2 x i8 addrspace(13)*> zeroinitializer + call void @jl_safepoint() + %el1 = extractelement <2 x i8 addrspace(13)*> %select, i32 0 + %el2 = extractelement <2 x i8 addrspace(13)*> %select, i32 1 + %l1 = load i8, i8 addrspace(13)* %el1 + %l2 = load i8, i8 addrspace(13)* %el2 + %add = add i8 %l1, %l2 + ret i8 %add +} + !0 = !{!"jtbaa"} !1 = !{!"jtbaa_const", !0, i64 0} !2 = !{!1, !1, i64 0, i64 1} diff --git a/test/loading.jl b/test/loading.jl index 289c25e582b0a..259f55a87c999 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -547,6 +547,22 @@ finally popfirst!(LOAD_PATH) end +@testset "--project and JULIA_PROJECT paths should be absolutified" begin + mktempdir() do dir; cd(dir) do + mkdir("foo") + script = """ + using Test + old = Base.active_project() + cd("foo") + @test Base.active_project() == old + """ + @test success(`$(Base.julia_cmd()) --project=foo -e $(script)`) + withenv("JULIA_PROJECT" => "foo") do + @test success(`$(Base.julia_cmd()) -e $(script)`) + end + end; end +end + ## cleanup after tests ## for env in keys(envs) diff --git a/test/misc.jl b/test/misc.jl index beec7c1033a31..c280fc79d145e 100644 --- a/test/misc.jl +++ b/test/misc.jl @@ -678,3 +678,11 @@ end # Just checking that this doesn't stack overflow on construction @test Test27970Empty() == Test27970Empty() end + +@testset "exports of modules" begin + for (_, mod) in Base.loaded_modules + for v in names(mod) + @test isdefined(mod, v) + end + end +end diff --git a/test/precompile.jl b/test/precompile.jl index df0883dfa5fa6..61e5e59512b5e 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -136,6 +136,12 @@ try const x28297 = Result(missing) + # issue #28998 + const x28998 = [missing, 2, missing, 6, missing, + missing, missing, missing, + missing, missing, missing, + missing, missing, 6] + let some_method = which(Base.include, (String,)) # global const some_method // FIXME: support for serializing a direct reference to an external Method not implemented global const some_linfo = @@ -180,6 +186,8 @@ try @test Foo.abigint_x::BigInt + 1 == big"125" @test Foo.x28297.result === missing + + @test Foo.x28998[end] == 6 end cachedir = joinpath(dir, "compiled", "v$(VERSION.major).$(VERSION.minor)") diff --git a/test/ranges.jl b/test/ranges.jl index 5817871b04050..cfb41a477ebef 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -1371,3 +1371,44 @@ end # module NonStandardIntegerRangeTest end end end + +@testset "constant-valued ranges (issues #10391 and #29052)" begin + for r in ((1:4), (1:1:4), (1.0:4.0)) + if eltype(r) === Int + @test_broken @inferred(0 * r) == [0.0, 0.0, 0.0, 0.0] + @test_broken @inferred(0 .* r) == [0.0, 0.0, 0.0, 0.0] + @test_broken @inferred(r + (4:-1:1)) == [5.0, 5.0, 5.0, 5.0] + @test_broken @inferred(r .+ (4:-1:1)) == [5.0, 5.0, 5.0, 5.0] + else + @test @inferred(0 * r) == [0.0, 0.0, 0.0, 0.0] + @test @inferred(0 .* r) == [0.0, 0.0, 0.0, 0.0] + @test @inferred(r + (4:-1:1)) == [5.0, 5.0, 5.0, 5.0] + @test @inferred(r .+ (4:-1:1)) == [5.0, 5.0, 5.0, 5.0] + end + @test @inferred(r .+ (4.0:-1:1)) == [5.0, 5.0, 5.0, 5.0] + @test @inferred(0.0 * r) == [0.0, 0.0, 0.0, 0.0] + @test @inferred(0.0 .* r) == [0.0, 0.0, 0.0, 0.0] + @test @inferred(r / Inf) == [0.0, 0.0, 0.0, 0.0] + @test @inferred(r ./ Inf) == [0.0, 0.0, 0.0, 0.0] + end + + @test_broken @inferred(range(0, step=0, length=4)) == [0, 0, 0, 0] + @test @inferred(range(0, stop=0, length=4)) == [0, 0, 0, 0] + @test @inferred(range(0.0, step=0.0, length=4)) == [0.0, 0.0, 0.0, 0.0] + @test @inferred(range(0.0, stop=0.0, length=4)) == [0.0, 0.0, 0.0, 0.0] + @test @inferred(range(0, step=0.0, length=4)) == [0.0, 0.0, 0.0, 0.0] + @test @inferred(range(0.0, step=0, length=4)) == [0.0, 0.0, 0.0, 0.0] + @test @inferred(range(0, stop=0.0, length=4)) == [0.0, 0.0, 0.0, 0.0] + @test @inferred(range(0.0, stop=0, length=4)) == [0.0, 0.0, 0.0, 0.0] + + z4 = 0.0 * (1:4) + @test @inferred(z4 .+ (1:4)) === 1.0:1.0:4.0 + @test @inferred(z4 .+ z4) === z4 +end + +@testset "allocation of TwicePrecision call" begin + 0:286.493442:360 + 0:286:360 + @test @allocated(0:286.493442:360) == 0 + @test @allocated(0:286:360) == 0 +end diff --git a/test/reflection.jl b/test/reflection.jl index 7c992072fbf79..b1f5fecc9178f 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -775,6 +775,18 @@ typeparam(::Type{T}, a::AbstractArray{T}) where T = 2 @test typeparam(Float64, rand(2)) == 1 @test typeparam(Int, rand(Int, 2)) == 2 +# prior ambiguities (issue #28899) +uambig(::Union{Int,Nothing}) = 1 +uambig(::Union{Float64,Nothing}) = 2 +@test uambig(1) == 1 +@test uambig(1.0) == 2 +@test_throws MethodError uambig(nothing) +m = which(uambig, Tuple{Int}) +Base.delete_method(m) +@test_throws MethodError uambig(1) +@test uambig(1.0) == 2 +@test uambig(nothing) == 2 + end # issue #26267 diff --git a/test/sets.jl b/test/sets.jl index ea523d43afb72..daddf0bc7bb8b 100644 --- a/test/sets.jl +++ b/test/sets.jl @@ -264,6 +264,14 @@ end s = Set([1,2,3,4]) setdiff!(s, Set([2,4,5,6])) @test isequal(s,Set([1,3])) + + # setdiff iterates the shorter set - make sure this algorithm works + sa, sb = Set([1,2,3,4,5,6,7]), Set([2,3,9]) + @test Set([1,4,5,6,7]) == setdiff(sa, sb) !== sa + @test Set([1,4,5,6,7]) == setdiff!(sa, sb) === sa + sa, sb = Set([1,2,3,4,5,6,7]), Set([2,3,9]) + @test Set([9]) == setdiff(sb, sa) !== sb + @test Set([9]) == setdiff!(sb, sa) === sb end @testset "ordering" begin @@ -604,3 +612,16 @@ end end end end + +struct OpenInterval{T} + lower::T + upper::T +end +Base.in(x, i::OpenInterval) = i.lower < x < i.upper +Base.IteratorSize(::Type{<:OpenInterval}) = Base.SizeUnknown() + +@testset "Continuous sets" begin + i = OpenInterval(2, 4) + @test 3 ∈ i + @test issubset(3, i) +end diff --git a/test/specificity.jl b/test/specificity.jl index f65776a3c4466..bcfd1e5284ee1 100644 --- a/test/specificity.jl +++ b/test/specificity.jl @@ -214,3 +214,14 @@ f27361(::M) where M <: Tuple{3} = nothing @test args_morespecific(Tuple{Type{Any}, Type}, Tuple{Type{T}, Type{T}} where T) @test !args_morespecific(Tuple{Type{Any}, Type}, Tuple{Type{T}, Type{T}} where T<:Union{}) + +# issue #22592 +abstract type Colorant22592{T,N} end +abstract type Color22592{T, N} <: Colorant22592{T,N} end +abstract type AbstractRGB22592{T} <: Color22592{T,3} end +AbstractGray22592{T} = Color22592{T,1} +MathTypes22592{T,C} = Union{AbstractRGB22592{T},AbstractGray22592{T}} +@test !args_morespecific(Tuple{MathTypes22592}, Tuple{AbstractGray22592}) +@test !args_morespecific(Tuple{MathTypes22592, MathTypes22592}, Tuple{AbstractGray22592}) + +@test args_morespecific(Union{Set,Dict,Vector}, Union{Vector,AbstractSet}) diff --git a/test/strings/basic.jl b/test/strings/basic.jl index 034d45bee9596..521dfa6d52b99 100644 --- a/test/strings/basic.jl +++ b/test/strings/basic.jl @@ -34,6 +34,7 @@ using Random @test string("∀∃", "1∀∃") === "∀∃1∀∃" @test string(SubString("∀∃"), SubString("1∀∃", 2)) === "∀∃∀∃" @test string(s"123") === s"123" + @test string("123", 'α', SubString("1∀∃", 2), 'a', "foo") === "123α∀∃afoo" codegen_egal_of_strings(x, y) = (x===y, x!==y) @test codegen_egal_of_strings(string("ab", 'c'), "abc") === (true, false) let strs = ["", "a", "a b c", "до свидания"] diff --git a/test/syntax.jl b/test/syntax.jl index 935c7ce58add5..6c6fc089b7203 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -1502,6 +1502,12 @@ function test27710() end @test test27710() === Int64 +# issue #29064 +struct X29064 + X29064::Int +end +@test X29064(1) isa X29064 + # issue #27268 function f27268() g(col::AbstractArray{<:Real}) = col @@ -1642,3 +1648,73 @@ end for ex in [:([x=1]), :(T{x=1})] @test Meta.lower(@__MODULE__, ex) == Expr(:error, string("misplaced assignment statement in \"", ex, "\"")) end + +# issue #28576 +@test Meta.isexpr(Meta.parse("1 == 2 ?"), :incomplete) +@test Meta.isexpr(Meta.parse("1 == 2 ? 3 :"), :incomplete) + +# issue #28991 +eval(Expr(:toplevel, + Expr(:module, true, :Mod28991, + Expr(:block, + Expr(:export, :Inner), + Expr(:abstract, :Inner))))) +@test names(Mod28991) == Symbol[:Inner, :Mod28991] + +# issue #28593 +macro a28593() + quote + abstract type A28593{S<:Real, V<:AbstractVector{S}} end + end +end + +macro b28593() + quote + struct B28593{S<:Real, V<:AbstractVector{S}} end + end +end + +macro c28593() + quote + primitive type C28593{S<:Real, V<:AbstractVector{S}} 32 end + end +end + +@a28593 +@b28593 +@c28593 + +@test A28593.var.name === :S +@test B28593.var.name === :S +@test C28593.var.name === :S + +# issue #25955 +macro noeffect25955(e) + return e +end + +struct foo25955 +end + +@noeffect25955 function (f::foo25955)() + 42 +end + +@test foo25955()() == 42 + +# issue #28833 +macro m28833(expr) + esc(:(global a28833)) +end +@m28833 1+1 + +# issue #28900 +macro foo28900(x) + quote + $x + end +end +f28900(; kwarg) = kwarg +let g = @foo28900 f28900(kwarg = x->2x) + @test g(10) == 20 +end diff --git a/test/tuple.jl b/test/tuple.jl index 11dc498086226..3a22f3b089557 100644 --- a/test/tuple.jl +++ b/test/tuple.jl @@ -86,6 +86,9 @@ end end @test empty((1, 2.0, "c")) === () + + # issue #28915 + @test convert(Union{Tuple{}, Tuple{Int}}, (1,)) === (1,) end @testset "size" begin