diff --git a/NEWS.md b/NEWS.md index d9b4267353803..246afc1efe852 100644 --- a/NEWS.md +++ b/NEWS.md @@ -468,6 +468,12 @@ This section lists changes that do not have deprecation warnings. * `parse(::Type, ::Char)` now uses a default base of 10, like other number parsing methods, instead of 36 ([#26576]). + * `isequal` for `Ptr`s now compares element types; `==` still compares only addresses + ([#26858]). + + * `widen` on 8- and 16-bit integer types now widens to the platform word size (`Int`) + instead of to a 32-bit type ([#26859]). + Library improvements -------------------- @@ -777,6 +783,9 @@ Deprecated or removed `AbstractUnitRange`s are accepted as arguments. Instead, convert the arguments before calling `ones` or `zeros` ([#26733]). + * The variadic `size(A, dim1, dim2, dims...)` method to return a tuple of multiple + dimension lengths of `A` has been deprecated ([#26862]). + * The `Operators` module is deprecated. Instead, import required operators explicitly from `Base`, e.g. `import Base: +, -, *, /` ([#22251]). diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 9f4741d5e3ed4..5755ed8e7d999 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -24,11 +24,10 @@ end end """ - size(A::AbstractArray, [dim...]) + size(A::AbstractArray, [dim]) -Return a tuple containing the dimensions of `A`. Optionally you can specify the -dimension(s) you want the length of, and get the length of that dimension, or a tuple of the -lengths of dimensions you asked for. +Return a tuple containing the dimensions of `A`. Optionally you can specify a +dimension to just get the length of that dimension. Note that `size` may not be defined for arrays with non-standard indices, in which case [`axes`](@ref) may be useful. See the manual chapter on [arrays with custom indices](@ref man-custom-indices). @@ -45,8 +44,6 @@ julia> size(A, 3, 2) ``` """ size(t::AbstractArray{T,N}, d) where {T,N} = d <= N ? size(t)[d] : 1 -size(x, d1::Integer, d2::Integer, dx::Vararg{Integer, N}) where {N} = - (size(x, d1), size(x, d2), ntuple(k->size(x, dx[k]), Val(N))...) """ axes(A, d) diff --git a/base/accumulate.jl b/base/accumulate.jl new file mode 100644 index 0000000000000..6bc0f27892727 --- /dev/null +++ b/base/accumulate.jl @@ -0,0 +1,393 @@ +# accumulate_pairwise slightly slower then accumulate, but more numerically +# stable in certain situations (e.g. sums). +# it does double the number of operations compared to accumulate, +# though for cheap operations like + this does not have much impact (20%) +function _accumulate_pairwise!(op::Op, c::AbstractVector{T}, v::AbstractVector, s, i1, n)::T where {T,Op} + @inbounds if n < 128 + s_ = v[i1] + c[i1] = op(s, s_) + for i = i1+1:i1+n-1 + s_ = op(s_, v[i]) + c[i] = op(s, s_) + end + else + n2 = n >> 1 + s_ = _accumulate_pairwise!(op, c, v, s, i1, n2) + s_ = op(s_, _accumulate_pairwise!(op, c, v, op(s, s_), i1+n2, n-n2)) + end + return s_ +end + +function accumulate_pairwise!(op::Op, result::AbstractVector, v::AbstractVector) where Op + li = linearindices(v) + li != linearindices(result) && throw(DimensionMismatch("input and output array sizes and indices must match")) + n = length(li) + n == 0 && return result + i1 = first(li) + @inbounds result[i1] = v1 = reduce_first(op,v[i1]) + n == 1 && return result + _accumulate_pairwise!(op, result, v, v1, i1+1, n-1) + return result +end + +function accumulate_pairwise(op, v::AbstractVector{T}) where T + out = similar(v, promote_op(op, T, T)) + return accumulate_pairwise!(op, out, v) +end + + +""" + cumsum!(B, A; dims::Integer) + +Cumulative sum of `A` along the dimension `dims`, storing the result in `B`. See also [`cumsum`](@ref). +""" +cumsum!(B::AbstractArray{T}, A; dims::Integer) where {T} = + accumulate!(add_sum, B, A, dims=dims) + +function cumsum!(out::AbstractArray, v::AbstractVector; dims::Integer=1) + # we dispatch on the possibility of numerical stability issues + _cumsum!(out, v, dims, ArithmeticStyle(eltype(out))) +end + +function _cumsum!(out::AbstractArray{T}, v, dim, ::ArithmeticRounds) where {T} + dim == 1 ? accumulate_pairwise!(add_sum, out, v) : copyto!(out, v) +end +function _cumsum!(out::AbstractArray, v, dim, ::ArithmeticUnknown) + _cumsum!(out, v, dim, ArithmeticRounds()) +end +function _cumsum!(out::AbstractArray{T}, v, dim, ::ArithmeticStyle) where {T} + dim == 1 ? accumulate!(add_sum, out, v) : copyto!(out, v) +end + +""" + cumsum(A; dims::Integer) + +Cumulative sum along the dimension `dims`. See also [`cumsum!`](@ref) +to use a preallocated output array, both for performance and to control the precision of the +output (e.g. to avoid overflow). + +# Examples +```jldoctest +julia> a = [1 2 3; 4 5 6] +2×3 Array{Int64,2}: + 1 2 3 + 4 5 6 + +julia> cumsum(a, dims=1) +2×3 Array{Int64,2}: + 1 2 3 + 5 7 9 + +julia> cumsum(a, dims=2) +2×3 Array{Int64,2}: + 1 3 6 + 4 9 15 +``` +""" +function cumsum(A::AbstractArray{T}; dims::Union{Nothing,Integer}=nothing) where T + if dims === nothing + depwarn("`cumsum(A::AbstractArray)` is deprecated, use `cumsum(A, dims=1)` instead.", :cumsum) + dims = 1 + end + out = similar(A, promote_op(add_sum, T, T)) + cumsum!(out, A, dims=dims) +end + +""" + cumsum(x::AbstractVector) + +Cumulative sum a vector. See also [`cumsum!`](@ref) +to use a preallocated output array, both for performance and to control the precision of the +output (e.g. to avoid overflow). + +# Examples +```jldoctest +julia> cumsum([1, 1, 1]) +3-element Array{Int64,1}: + 1 + 2 + 3 + +julia> cumsum([fill(1, 2) for i in 1:3]) +3-element Array{Array{Int64,1},1}: + [1, 1] + [2, 2] + [3, 3] +``` +""" +cumsum(x::AbstractVector) = cumsum(x, dims=1) + + +""" + cumprod!(B, A; dims::Integer) + +Cumulative product of `A` along the dimension `dims`, storing the result in `B`. +See also [`cumprod`](@ref). +""" +cumprod!(B::AbstractArray{T}, A; dims::Integer) where {T} = + accumulate!(add_sum, B, A, dims=dims) + +""" + cumprod!(y::AbstractVector, x::AbstractVector) + +Cumulative product of a vector `x`, storing the result in `y`. +See also [`cumprod`](@ref). +""" +cumprod!(y::AbstractVector, x::AbstractVector) = cumprod!(y, x, dims=1) + +""" + cumprod(A; dims::Integer) + +Cumulative product along the dimension `dim`. See also +[`cumprod!`](@ref) to use a preallocated output array, both for performance and +to control the precision of the output (e.g. to avoid overflow). + +# Examples +```jldoctest +julia> a = [1 2 3; 4 5 6] +2×3 Array{Int64,2}: + 1 2 3 + 4 5 6 + +julia> cumprod(a, dims=1) +2×3 Array{Int64,2}: + 1 2 3 + 4 10 18 + +julia> cumprod(a, dims=2) +2×3 Array{Int64,2}: + 1 2 6 + 4 20 120 +``` +""" +function cumprod(A::AbstractArray; dims::Union{Nothing,Integer}=nothing) + if dims === nothing + depwarn("`cumprod(A::AbstractArray)` is deprecated, use `cumprod(A, dims=1)` instead.", :cumprod) + dims = 1 + end + return accumulate(mul_prod, A, dims=dims) +end + +""" + cumprod(x::AbstractVector) + +Cumulative product of a vector. See also +[`cumprod!`](@ref) to use a preallocated output array, both for performance and +to control the precision of the output (e.g. to avoid overflow). + +# Examples +```jldoctest +julia> cumprod(fill(1//2, 3)) +3-element Array{Rational{Int64},1}: + 1//2 + 1//4 + 1//8 + +julia> cumprod([fill(1//3, 2, 2) for i in 1:3]) +3-element Array{Array{Rational{Int64},2},1}: + [1//3 1//3; 1//3 1//3] + [2//9 2//9; 2//9 2//9] + [4//27 4//27; 4//27 4//27] +``` +""" +cumprod(x::AbstractVector) = cumprod(x, dims=1) + + +""" + accumulate(op, A; dims::Integer) + +Cumulative operation `op` along the dimension `dims`. 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) + +# Examples +```jldoctest +julia> accumulate(+, fill(1, 3, 3), dims=1) +3×3 Array{Int64,2}: + 1 1 1 + 2 2 2 + 3 3 3 + +julia> accumulate(+, fill(1, 3, 3), dims=2) +3×3 Array{Int64,2}: + 1 2 3 + 1 2 3 + 1 2 3 +``` +""" +function accumulate(op, A; dims::Integer) + out = similar(A, promote_op(op, eltype(A), eltype(A))) + accumulate!(op, out, A, dims=dims) +end + +""" + accumulate(op, x::AbstractVector) + +Cumulative operation `op` on a vector. 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) + +# Examples +```jldoctest +julia> accumulate(+, [1,2,3]) +3-element Array{Int64,1}: + 1 + 3 + 6 + +julia> accumulate(*, [1,2,3]) +3-element Array{Int64,1}: + 1 + 2 + 6 +``` +""" +accumulate(op, x::AbstractVector) = accumulate(op, x, dims=1) + +""" + accumulate!(op, B, A; dims::Integer) + +Cumulative operation `op` on `A` along the dimension `dims`, storing the result in `B`. +See also [`accumulate`](@ref). + +# Examples +```jldoctest +julia> A = [1 2; 3 4]; + +julia> B = [0 0; 0 0]; + +julia> accumulate!(-, B, A, dims=1); + +julia> B +2×2 Array{Int64,2}: + 1 2 + -2 -2 + +julia> accumulate!(-, B, A, dims=2); + +julia> B +2×2 Array{Int64,2}: + 1 -1 + 3 -1 +``` +""" +function accumulate!(op, B, A; dims::Integer) + dim = dims + dim > 0 || throw(ArgumentError("dim must be a positive integer")) + inds_t = axes(A) + axes(B) == inds_t || throw(DimensionMismatch("shape of B must match A")) + dim > ndims(A) && return copyto!(B, A) + isempty(inds_t[dim]) && return B + if dim == 1 + # We can accumulate to a temporary variable, which allows + # register usage and will be slightly faster + ind1 = inds_t[1] + @inbounds for I in CartesianIndices(tail(inds_t)) + tmp = reduce_first(op, A[first(ind1), I]) + B[first(ind1), I] = tmp + for i_1 = first(ind1)+1:last(ind1) + tmp = op(tmp, A[i_1, I]) + B[i_1, I] = tmp + end + end + else + R1 = CartesianIndices(axes(A)[1:dim-1]) # not type-stable + R2 = CartesianIndices(axes(A)[dim+1:end]) + _accumulate!(op, B, A, R1, inds_t[dim], R2) # use function barrier + end + return B +end + +""" + accumulate!(op, y, x::AbstractVector) + +Cumulative operation `op` on a vector `x`, storing the result in `y`. +See also [`accumulate`](@ref). + +# Examples +``jldoctest +julia> x = [1, 0, 2, 0, 3]; + +julia> y = [0, 0, 0, 0, 0]; + +julia> accumulate!(+, y, x); + +julia> y +5-element Array{Int64,1}: + 1 + 1 + 3 + 3 + 6 +``` +""" +function accumulate!(op::Op, y, x::AbstractVector) where Op + isempty(x) && return y + v1 = first(x) + _accumulate1!(op, y, v1, x, 1) +end + +@noinline function _accumulate!(op, B, A, R1, ind, R2) + # Copy the initial element in each 1d vector along dimension `dim` + ii = first(ind) + @inbounds for J in R2, I in R1 + B[I, ii, J] = reduce_first(op, A[I, ii, J]) + end + # Accumulate + @inbounds for J in R2, i in first(ind)+1:last(ind), I in R1 + B[I, i, J] = op(B[I, i-1, J], A[I, i, J]) + end + B +end + +""" + accumulate(op, v0, x::AbstractVector) + +Like `accumulate`, but using a starting element `v0`. The first entry of the result will be +`op(v0, first(A))`. + +# Examples +```jldoctest +julia> accumulate(+, 100, [1,2,3]) +3-element Array{Int64,1}: + 101 + 103 + 106 + +julia> accumulate(min, 0, [1,2,-1]) +3-element Array{Int64,1}: + 0 + 0 + -1 +``` +""" +function accumulate(op, v0, x::AbstractVector) + T = promote_op(op, typeof(v0), eltype(x)) + out = similar(x, T) + accumulate!(op, out, v0, x) +end + +function accumulate!(op, y, v0, x::AbstractVector) + isempty(x) && return y + v1 = op(v0, first(x)) + _accumulate1!(op, y, v1, x, 1) +end + +function _accumulate1!(op, B, v1, A::AbstractVector, dim::Integer) + dim > 0 || throw(ArgumentError("dim must be a positive integer")) + inds = linearindices(A) + inds == linearindices(B) || throw(DimensionMismatch("linearindices of A and B don't match")) + dim > 1 && return copyto!(B, A) + i1 = inds[1] + cur_val = reduce_first(op, v1) + B[i1] = cur_val + @inbounds for i in inds[2:end] + cur_val = op(cur_val, A[i]) + B[i] = cur_val + end + return B +end diff --git a/base/broadcast.jl b/base/broadcast.jl index 84e1e1dc0f8ec..5f6b0e9c2feec 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -1143,6 +1143,11 @@ function __dot__(x::Expr) Meta.isexpr(x.args[1], :call) # function or macro definition Expr(x.head, x.args[1], dotargs[2]) else + if x.head == :&& || x.head == :|| + Base.depwarn(""" + using $(x.head) expressions in @. is deprecated; in the future it will + become elementwise. Break the expression into smaller parts instead.""", nothing) + end head = string(x.head) if last(head) == '=' && first(head) != '.' Expr(Symbol('.',head), dotargs...) diff --git a/base/deprecated.jl b/base/deprecated.jl index ce554e4d611ed..8b3fc0613f7a5 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -82,11 +82,12 @@ function depwarn(msg, funcsym) _id=(frame,funcsym), _group=:depwarn, caller=caller, - maxlog=1 + maxlog=funcsym === nothing ? nothing : 1 ) nothing end +firstcaller(bt::Vector, ::Nothing) = Ptr{Cvoid}(0), StackTraces.UNKNOWN firstcaller(bt::Vector, funcsym::Symbol) = firstcaller(bt, (funcsym,)) function firstcaller(bt::Vector, funcsyms) # Identify the calling line @@ -1590,6 +1591,9 @@ end @deprecate ones(::Type{T}, dims::NTuple{N, Any}) where {T, N} ones(T, convert(Dims, dims)) @deprecate ones(dims::Tuple) ones(convert(Dims, dims)) +# Deprecate varargs size: PR #26862 +@deprecate size(x, d1::Integer, d2::Integer) (size(x, d1), size(x, d2)) +@deprecate size(x, d1::Integer, d2::Integer, dx::Integer...) map(dim->size(x, dim), (d1, d2, dx...)) @deprecate showcompact(x) show(IOContext(stdout, :compact => true), x) @deprecate showcompact(io, x) show(IOContext(io, :compact => true), x) diff --git a/base/int.jl b/base/int.jl index 80140c6d65191..aa6070a6d1299 100644 --- a/base/int.jl +++ b/base/int.jl @@ -640,10 +640,10 @@ typemax(::Type{UInt64}) = 0xffffffffffffffff @eval typemin(::Type{Int128} ) = $(convert(Int128, 1) << 127) @eval typemax(::Type{Int128} ) = $(bitcast(Int128, typemax(UInt128) >> 1)) -widen(::Type{<:Union{Int8, Int16}}) = Int32 +widen(::Type{<:Union{Int8, Int16}}) = Int widen(::Type{Int32}) = Int64 widen(::Type{Int64}) = Int128 -widen(::Type{<:Union{UInt8, UInt16}}) = UInt32 +widen(::Type{<:Union{UInt8, UInt16}}) = UInt widen(::Type{UInt32}) = UInt64 widen(::Type{UInt64}) = UInt128 diff --git a/base/multidimensional.jl b/base/multidimensional.jl index ce9db4a86226c..7e157ccc9a21f 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -680,405 +680,6 @@ _iterable(v) = Iterators.repeated(v) end end -# see discussion in #18364 ... we try not to widen type of the resulting array -# from cumsum or cumprod, but in some cases (+, Bool) we may not have a choice. -rcum_promote_type(op, ::Type{T}, ::Type{S}) where {T,S<:Number} = promote_op(op, T, S) -rcum_promote_type(op, ::Type{T}) where {T<:Number} = rcum_promote_type(op, T,T) -rcum_promote_type(op, ::Type{T}) where {T} = T - -# handle sums of Vector{Bool} and similar. it would be nice to handle -# any AbstractArray here, but it's not clear how that would be possible -rcum_promote_type(op, ::Type{Array{T,N}}) where {T,N} = Array{rcum_promote_type(op,T), N} - -# accumulate_pairwise slightly slower then accumulate, but more numerically -# stable in certain situations (e.g. sums). -# it does double the number of operations compared to accumulate, -# though for cheap operations like + this does not have much impact (20%) -function _accumulate_pairwise!(op::Op, c::AbstractVector{T}, v::AbstractVector, s, i1, n)::T where {T,Op} - @inbounds if n < 128 - s_ = v[i1] - c[i1] = op(s, s_) - for i = i1+1:i1+n-1 - s_ = op(s_, v[i]) - c[i] = op(s, s_) - end - else - n2 = n >> 1 - s_ = _accumulate_pairwise!(op, c, v, s, i1, n2) - s_ = op(s_, _accumulate_pairwise!(op, c, v, op(s, s_), i1+n2, n-n2)) - end - return s_ -end - -function accumulate_pairwise!(op::Op, result::AbstractVector, v::AbstractVector) where Op - li = linearindices(v) - li != linearindices(result) && throw(DimensionMismatch("input and output array sizes and indices must match")) - n = length(li) - n == 0 && return result - i1 = first(li) - @inbounds result[i1] = v1 = v[i1] - n == 1 && return result - _accumulate_pairwise!(op, result, v, v1, i1+1, n-1) - return result -end - -function accumulate_pairwise(op, v::AbstractVector{T}) where T - out = similar(v, rcum_promote_type(op, T)) - return accumulate_pairwise!(op, out, v) -end - -function cumsum!(out, v::AbstractVector; dims::Integer=1) - # we dispatch on the possibility of numerical stability issues - _cumsum!(out, v, dims, ArithmeticStyle(eltype(out))) -end - -function _cumsum!(out, v, dim, ::ArithmeticRounds) - dim == 1 ? accumulate_pairwise!(+, out, v) : copyto!(out, v) -end -function _cumsum!(out, v, dim, ::ArithmeticUnknown) - _cumsum!(out, v, dim, ArithmeticRounds()) -end -function _cumsum!(out, v, dim, ::ArithmeticStyle) - dim == 1 ? accumulate!(+, out, v) : copyto!(out, v) -end - -""" - cumsum(A; dims::Integer) - -Cumulative sum along the dimension `dims`. See also [`cumsum!`](@ref) -to use a preallocated output array, both for performance and to control the precision of the -output (e.g. to avoid overflow). - -# Examples -```jldoctest -julia> a = [1 2 3; 4 5 6] -2×3 Array{Int64,2}: - 1 2 3 - 4 5 6 - -julia> cumsum(a, dims=1) -2×3 Array{Int64,2}: - 1 2 3 - 5 7 9 - -julia> cumsum(a, dims=2) -2×3 Array{Int64,2}: - 1 3 6 - 4 9 15 -``` -""" -function cumsum(A::AbstractArray{T}; dims::Union{Nothing,Integer}=nothing) where T - if dims === nothing - depwarn("`cumsum(A::AbstractArray)` is deprecated, use `cumsum(A, dims=1)` instead.", :cumsum) - dims = 1 - end - out = similar(A, rcum_promote_type(+, T)) - cumsum!(out, A, dims=dims) -end - -""" - cumsum(x::AbstractVector) - -Cumulative sum a vector. See also [`cumsum!`](@ref) -to use a preallocated output array, both for performance and to control the precision of the -output (e.g. to avoid overflow). - -# Examples -```jldoctest -julia> cumsum([1, 1, 1]) -3-element Array{Int64,1}: - 1 - 2 - 3 - -julia> cumsum([fill(1, 2) for i in 1:3]) -3-element Array{Array{Int64,1},1}: - [1, 1] - [2, 2] - [3, 3] -``` -""" -cumsum(x::AbstractVector) = cumsum(x, dims=1) - -""" - cumsum!(B, A; dims::Integer) - -Cumulative sum of `A` along the dimension `dims`, storing the result in `B`. See also [`cumsum`](@ref). -""" -cumsum!(B, A; dims::Integer) = accumulate!(+, B, A, dims=dims) - -""" - cumprod(A; dims::Integer) - -Cumulative product along the dimension `dim`. See also -[`cumprod!`](@ref) to use a preallocated output array, both for performance and -to control the precision of the output (e.g. to avoid overflow). - -# Examples -```jldoctest -julia> a = [1 2 3; 4 5 6] -2×3 Array{Int64,2}: - 1 2 3 - 4 5 6 - -julia> cumprod(a, dims=1) -2×3 Array{Int64,2}: - 1 2 3 - 4 10 18 - -julia> cumprod(a, dims=2) -2×3 Array{Int64,2}: - 1 2 6 - 4 20 120 -``` -""" -function cumprod(A::AbstractArray; dims::Union{Nothing,Integer}=nothing) - if dims === nothing - depwarn("`cumprod(A::AbstractArray)` is deprecated, use `cumprod(A, dims=1)` instead.", :cumprod) - dims = 1 - end - return accumulate(*, A, dims=dims) -end - -""" - cumprod(x::AbstractVector) - -Cumulative product of a vector. See also -[`cumprod!`](@ref) to use a preallocated output array, both for performance and -to control the precision of the output (e.g. to avoid overflow). - -# Examples -```jldoctest -julia> cumprod(fill(1//2, 3)) -3-element Array{Rational{Int64},1}: - 1//2 - 1//4 - 1//8 - -julia> cumprod([fill(1//3, 2, 2) for i in 1:3]) -3-element Array{Array{Rational{Int64},2},1}: - [1//3 1//3; 1//3 1//3] - [2//9 2//9; 2//9 2//9] - [4//27 4//27; 4//27 4//27] -``` -""" -cumprod(x::AbstractVector) = cumprod(x, dims=1) - -""" - cumprod!(B, A; dims::Integer) - -Cumulative product of `A` along the dimension `dims`, storing the result in `B`. -See also [`cumprod`](@ref). -""" -cumprod!(B, A; dims::Integer) = accumulate!(*, B, A, dims=dims) - -""" - cumprod!(y::AbstractVector, x::AbstractVector) - -Cumulative product of a vector `x`, storing the result in `y`. -See also [`cumprod`](@ref). -""" -cumprod!(y::AbstractVector, x::AbstractVector) = cumprod!(y, x, dims=1) - -""" - accumulate(op, A; dims::Integer) - -Cumulative operation `op` along the dimension `dims`. 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) - -# Examples -```jldoctest -julia> accumulate(+, fill(1, 3, 3), dims=1) -3×3 Array{Int64,2}: - 1 1 1 - 2 2 2 - 3 3 3 - -julia> accumulate(+, fill(1, 3, 3), dims=2) -3×3 Array{Int64,2}: - 1 2 3 - 1 2 3 - 1 2 3 -``` -""" -function accumulate(op, A; dims::Integer) - out = similar(A, rcum_promote_type(op, eltype(A))) - accumulate!(op, out, A, dims=dims) -end - -""" - accumulate(op, x::AbstractVector) - -Cumulative operation `op` on a vector. 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) - -# Examples -```jldoctest -julia> accumulate(+, [1,2,3]) -3-element Array{Int64,1}: - 1 - 3 - 6 - -julia> accumulate(*, [1,2,3]) -3-element Array{Int64,1}: - 1 - 2 - 6 -``` -""" -accumulate(op, x::AbstractVector) = accumulate(op, x, dims=1) - -""" - accumulate!(op, B, A; dims::Integer) - -Cumulative operation `op` on `A` along the dimension `dims`, storing the result in `B`. -See also [`accumulate`](@ref). - -# Examples -```jldoctest -julia> A = [1 2; 3 4]; - -julia> B = [0 0; 0 0]; - -julia> accumulate!(-, B, A, dims=1); - -julia> B -2×2 Array{Int64,2}: - 1 2 - -2 -2 - -julia> accumulate!(-, B, A, dims=2); - -julia> B -2×2 Array{Int64,2}: - 1 -1 - 3 -1 -``` -""" -function accumulate!(op, B, A; dims::Integer) - dim = dims - dim > 0 || throw(ArgumentError("dim must be a positive integer")) - inds_t = axes(A) - axes(B) == inds_t || throw(DimensionMismatch("shape of B must match A")) - dim > ndims(A) && return copyto!(B, A) - isempty(inds_t[dim]) && return B - if dim == 1 - # We can accumulate to a temporary variable, which allows - # register usage and will be slightly faster - ind1 = inds_t[1] - @inbounds for I in CartesianIndices(tail(inds_t)) - tmp = convert(eltype(B), A[first(ind1), I]) - B[first(ind1), I] = tmp - for i_1 = first(ind1)+1:last(ind1) - tmp = op(tmp, A[i_1, I]) - B[i_1, I] = tmp - end - end - else - R1 = CartesianIndices(axes(A)[1:dim-1]) # not type-stable - R2 = CartesianIndices(axes(A)[dim+1:end]) - _accumulate!(op, B, A, R1, inds_t[dim], R2) # use function barrier - end - return B -end - -""" - accumulate!(op, y, x::AbstractVector) - -Cumulative operation `op` on a vector `x`, storing the result in `y`. -See also [`accumulate`](@ref). - -# Examples -``jldoctest -julia> x = [1, 0, 2, 0, 3]; - -julia> y = [0, 0, 0, 0, 0]; - -julia> accumulate!(+, y, x); - -julia> y -5-element Array{Int64,1}: - 1 - 1 - 3 - 3 - 6 -``` -""" -function accumulate!(op::Op, y, x::AbstractVector) where Op - isempty(x) && return y - v1 = first(x) - _accumulate1!(op, y, v1, x, 1) -end - -@noinline function _accumulate!(op, B, A, R1, ind, R2) - # Copy the initial element in each 1d vector along dimension `dim` - ii = first(ind) - @inbounds for J in R2, I in R1 - B[I, ii, J] = A[I, ii, J] - end - # Accumulate - @inbounds for J in R2, i in first(ind)+1:last(ind), I in R1 - B[I, i, J] = op(B[I, i-1, J], A[I, i, J]) - end - B -end - -""" - accumulate(op, v0, x::AbstractVector) - -Like `accumulate`, but using a starting element `v0`. The first entry of the result will be -`op(v0, first(A))`. - -# Examples -```jldoctest -julia> accumulate(+, 100, [1,2,3]) -3-element Array{Int64,1}: - 101 - 103 - 106 - -julia> accumulate(min, 0, [1,2,-1]) -3-element Array{Int64,1}: - 0 - 0 - -1 -``` -""" -function accumulate(op, v0, x::AbstractVector) - T = rcum_promote_type(op, typeof(v0), eltype(x)) - out = similar(x, T) - accumulate!(op, out, v0, x) -end - -function accumulate!(op, y, v0, x::AbstractVector) - isempty(x) && return y - v1 = op(v0, first(x)) - _accumulate1!(op, y, v1, x, 1) -end - -function _accumulate1!(op, B, v1, A::AbstractVector, dim::Integer) - dim > 0 || throw(ArgumentError("dim must be a positive integer")) - inds = linearindices(A) - inds == linearindices(B) || throw(DimensionMismatch("linearindices of A and B don't match")) - dim > 1 && return copyto!(B, A) - i1 = inds[1] - cur_val = v1 - B[i1] = cur_val - @inbounds for i in inds[2:end] - cur_val = op(cur_val, A[i]) - B[i] = cur_val - end - return B -end - diff(a::AbstractVector) = [ a[i+1] - a[i] for i=1:length(a)-1 ] """ diff --git a/base/pointer.jl b/base/pointer.jl index bbc5c215d38f2..9cc74577b9a01 100644 --- a/base/pointer.jl +++ b/base/pointer.jl @@ -145,9 +145,12 @@ end ## limited pointer arithmetic & comparison ## +isequal(x::Ptr, y::Ptr) = (x === y) +isless(x::Ptr{T}, y::Ptr{T}) where {T} = x < y + ==(x::Ptr, y::Ptr) = UInt(x) == UInt(y) -isless(x::Ptr, y::Ptr) = isless(UInt(x), UInt(y)) --(x::Ptr, y::Ptr) = UInt(x) - UInt(y) +<(x::Ptr, y::Ptr) = UInt(x) < UInt(y) +-(x::Ptr, y::Ptr) = UInt(x) - UInt(y) +(x::Ptr, y::Integer) = oftype(x, Intrinsics.add_ptr(UInt(x), (y % UInt) % UInt)) -(x::Ptr, y::Integer) = oftype(x, Intrinsics.sub_ptr(UInt(x), (y % UInt) % UInt)) diff --git a/base/reshapedarray.jl b/base/reshapedarray.jl index 3ac1c7cac616f..902200eb0d3d3 100644 --- a/base/reshapedarray.jl +++ b/base/reshapedarray.jl @@ -168,6 +168,11 @@ function __reshape(p::Tuple{AbstractArray,IndexCartesian}, dims::Dims) ReshapedArray(parent, dims, reverse(mi)) end +function __reshape(p::Tuple{AbstractArray{<:Any,0},IndexCartesian}, dims::Dims) + parent = p[1] + ReshapedArray(parent, dims, ()) +end + function __reshape(p::Tuple{AbstractArray,IndexLinear}, dims::Dims) parent = p[1] ReshapedArray(parent, dims, ()) diff --git a/base/sysimg.jl b/base/sysimg.jl index be31a12f2d890..f5ad1d0ce42ee 100644 --- a/base/sysimg.jl +++ b/base/sysimg.jl @@ -354,6 +354,7 @@ INCLUDE_STATE = 2 # include = _include (from lines above) # reduction along dims include("reducedim.jl") # macros in this file relies on string.jl +include("accumulate.jl") # basic data structures include("ordering.jl") @@ -796,8 +797,8 @@ end @deprecate_stdlib triu LinearAlgebra true @deprecate_stdlib vecdot LinearAlgebra true @deprecate_stdlib vecnorm LinearAlgebra true - # @deprecate_stdlib ⋅ LinearAlgebra true - # @deprecate_stdlib × LinearAlgebra true + @deprecate_stdlib $(:⋅) LinearAlgebra true + @deprecate_stdlib $(:×) LinearAlgebra true ## types that were re-exported from Base @deprecate_stdlib Diagonal LinearAlgebra true diff --git a/deps/llvm.mk b/deps/llvm.mk index 44737ca3be220..a917d132736ea 100644 --- a/deps/llvm.mk +++ b/deps/llvm.mk @@ -481,6 +481,7 @@ $(eval $(call LLVM_PATCH,llvm-6.0.0_D27296-libssp)) # remove for 7.0 $(eval $(call LLVM_PATCH,llvm-6.0-D44650)) # mingw32 build fix $(eval $(call LLVM_PATCH,llvm-D45008)) # remove for 7.0 $(eval $(call LLVM_PATCH,llvm-D45070)) # remove for 7.0 +$(eval $(call LLVM_PATCH,llvm-6.0.0-ifconv-D45819)) # remove for 7.0 endif # LLVM_VER # Remove hardcoded OS X requirements in compilter-rt cmake build diff --git a/deps/patches/llvm-6.0.0-ifconv-D45819.patch b/deps/patches/llvm-6.0.0-ifconv-D45819.patch new file mode 100644 index 0000000000000..0fcd5a906d325 --- /dev/null +++ b/deps/patches/llvm-6.0.0-ifconv-D45819.patch @@ -0,0 +1,158 @@ +Index: lib/CodeGen/IfConversion.cpp +=================================================================== +--- a/lib/CodeGen/IfConversion.cpp ++++ b/lib/CodeGen/IfConversion.cpp +@@ -1714,20 +1714,25 @@ + } + + // Remove the duplicated instructions at the beginnings of both paths. +- // Skip dbg_value instructions ++ // Skip dbg_value instructions. + MachineBasicBlock::iterator DI1 = MBB1.getFirstNonDebugInstr(); + MachineBasicBlock::iterator DI2 = MBB2.getFirstNonDebugInstr(); + BBI1->NonPredSize -= NumDups1; + BBI2->NonPredSize -= NumDups1; + + // Skip past the dups on each side separately since there may be +- // differing dbg_value entries. ++ // differing dbg_value entries. NumDups1 can include a "return" ++ // instruction, if it's not marked as "branch". + for (unsigned i = 0; i < NumDups1; ++DI1) { ++ if (DI1 == MBB1.end()) ++ break; + if (!DI1->isDebugValue()) + ++i; + } + while (NumDups1 != 0) { + ++DI2; ++ if (DI2 == MBB2.end()) ++ break; + if (!DI2->isDebugValue()) + --NumDups1; + } +@@ -1738,11 +1743,16 @@ + Redefs.stepForward(MI, Dummy); + } + } ++ + BBI.BB->splice(BBI.BB->end(), &MBB1, MBB1.begin(), DI1); + MBB2.erase(MBB2.begin(), DI2); + +- // The branches have been checked to match, so it is safe to remove the branch +- // in BB1 and rely on the copy in BB2 ++ // The branches have been checked to match, so it is safe to remove the ++ // branch in BB1 and rely on the copy in BB2. The complication is that ++ // the blocks may end with a return instruction, which may or may not ++ // be marked as "branch". If it's not, then it could be included in ++ // "dups1", leaving the blocks potentially empty after moving the common ++ // duplicates. + #ifndef NDEBUG + // Unanalyzable branches must match exactly. Check that now. + if (!BBI1->IsBrAnalyzable) +@@ -1768,11 +1778,14 @@ + if (RemoveBranch) + BBI2->NonPredSize -= TII->removeBranch(*BBI2->BB); + else { +- do { +- assert(DI2 != MBB2.begin()); +- DI2--; +- } while (DI2->isBranch() || DI2->isDebugValue()); +- DI2++; ++ // Make DI2 point to the end of the range where the common "tail" ++ // instructions could be found. ++ while (DI2 != MBB2.begin()) { ++ MachineBasicBlock::iterator Prev = std::prev(DI2); ++ if (!Prev->isBranch() && !Prev->isDebugValue()) ++ break; ++ DI2 = Prev; ++ } + } + while (NumDups2 != 0) { + // NumDups2 only counted non-dbg_value instructions, so this won't +@@ -1833,11 +1846,15 @@ + // a non-predicated in BBI2, then we don't want to predicate the one from + // BBI2. The reason is that if we merged these blocks, we would end up with + // two predicated terminators in the same block. ++ // Also, if the branches in MBB1 and MBB2 were non-analyzable, then don't ++ // predicate them either. They were checked to be identical, and so the ++ // same branch would happen regardless of which path was taken. + if (!MBB2.empty() && (DI2 == MBB2.end())) { + MachineBasicBlock::iterator BBI1T = MBB1.getFirstTerminator(); + MachineBasicBlock::iterator BBI2T = MBB2.getFirstTerminator(); +- if (BBI1T != MBB1.end() && TII->isPredicated(*BBI1T) && +- BBI2T != MBB2.end() && !TII->isPredicated(*BBI2T)) ++ bool BB1Predicated = BBI1T != MBB1.end() && TII->isPredicated(*BBI1T); ++ bool BB2NonPredicated = BBI2T != MBB2.end() && !TII->isPredicated(*BBI2T); ++ if (BB2NonPredicated && (BB1Predicated || !BBI2->IsBrAnalyzable)) + --DI2; + } + +Index: test/CodeGen/Hexagon/ifcvt-diamond-ret.mir +=================================================================== +--- /dev/null ++++ test/CodeGen/Hexagon/ifcvt-diamond-ret.mir +@@ -0,0 +1,25 @@ ++# RUN: llc -march=hexagon -run-pass if-converter %s -o - | FileCheck %s ++ ++# Make sure this gets if-converted and it doesn't crash. ++# CHECK-LABEL: bb.0 ++# CHECK: PS_jmpret $r31 ++# CHECK-NOT: bb.{{[1-9]+}}: ++ ++--- ++name: fred ++tracksRegLiveness: true ++body: | ++ bb.0: ++ successors: %bb.1, %bb.2 ++ liveins: $r0 ++ renamable $p0 = C2_cmpeqi killed renamable $r0, 0 ++ J2_jumpf killed renamable $p0, %bb.2, implicit-def dead $pc ++ ++ bb.1: ++ S4_storeiri_io undef renamable $r0, 0, 32768 :: (store 4 into `i32* undef`) ++ PS_jmpret $r31, implicit-def dead $pc ++ ++ bb.2: ++ S4_storeiri_io undef renamable $r0, 0, 32768 :: (store 4 into `i32* undef`) ++ PS_jmpret $r31, implicit-def dead $pc ++... +Index: test/CodeGen/MIR/PowerPC/ifcvt-diamond-ret.mir +=================================================================== +--- /dev/null ++++ test/CodeGen/MIR/PowerPC/ifcvt-diamond-ret.mir +@@ -0,0 +1,34 @@ ++# RUN: llc -mtriple=powerpc64le-unknown-linux-gnu -run-pass=if-converter %s -o - | FileCheck %s ++--- ++name: foo ++body: | ++ bb.0: ++ liveins: $x0, $x3 ++ successors: %bb.1(0x40000000), %bb.2(0x40000000) ++ ++ dead renamable $x3 = ANDIo8 killed renamable $x3, 1, implicit-def dead $cr0, implicit-def $cr0gt ++ $cr2lt = CROR $cr0gt, $cr0gt ++ BCn killed renamable $cr2lt, %bb.2 ++ B %bb.1 ++ ++ bb.1: ++ renamable $x3 = LIS8 4096 ++ MTLR8 $x0, implicit-def $lr8 ++ BLR8 implicit $lr8, implicit $rm, implicit $x3 ++ ++ bb.2: ++ renamable $x3 = LIS8 4096 ++ MTLR8 $x0, implicit-def $lr8 ++ BLR8 implicit $lr8, implicit $rm, implicit $x3 ++... ++ ++# Diamond testcase with equivalent branches terminating in returns. ++ ++# CHECK: body: | ++# CHECK: bb.0: ++# CHECK: dead renamable $x3 = ANDIo8 killed renamable $x3, 1, implicit-def dead $cr0, implicit-def $cr0gt ++# CHECK: $cr2lt = CROR $cr0gt, $cr0gt ++# CHECK: renamable $x3 = LIS8 4096 ++# CHECK: MTLR8 $x0, implicit-def $lr8 ++# CHECK: BLR8 implicit $lr8, implicit $rm, implicit $x3 ++ diff --git a/doc/src/manual/code-loading.md b/doc/src/manual/code-loading.md index 216e05e9ed10b..cb371579894e1 100644 --- a/doc/src/manual/code-loading.md +++ b/doc/src/manual/code-loading.md @@ -126,6 +126,7 @@ A materialized representation of this dependency `graph` looks like this: graph = Dict{UUID,Dict{Symbol,UUID}}( # Priv – the private one: UUID("ba13f791-ae1d-465a-978b-69c3ad90f72b") => Dict{Symbol,UUID}( + :Pub => UUID("ba13f791-ae1d-465a-978b-69c3ad90f72b"), :Zebra => UUID("f7a24cb4-21fc-4002-ac70-f0e3a0dd3f62"), ), # Priv – the public one: @@ -262,12 +263,12 @@ Here is the corresponding `graph` structure, materialized as a dictionary: graph = Dict{UUID,Dict{Symbol,UUID}}( # Bobcat: UUID("85ad11c7-31f6-5d08-84db-0a4914d4cadf") => Dict{Symbol,UUID}( - :Cobra => UUID("4725e24d-f727-424b-bca0-c4307a3456fa"), - :Dingo => UUID("7a7925be-828c-4418-bbeb-bac8dfc843bc"), + :Cobra => UUID("4725e24d-f727-424b-bca0-c4307a3456fa"), + :Dingo => UUID("7a7925be-828c-4418-bbeb-bac8dfc843bc"), ), # Cobra: UUID("4725e24d-f727-424b-bca0-c4307a3456fa") => Dict{Symbol,UUID}( - :Dingo => UUID("7a7925be-828c-4418-bbeb-bac8dfc843bc"), + :Dingo => UUID("7a7925be-828c-4418-bbeb-bac8dfc843bc"), ), # Dingo: UUID("7a7925be-828c-4418-bbeb-bac8dfc843bc") => Dict{Symbol,UUID}(), diff --git a/doc/src/manual/parallel-computing.md b/doc/src/manual/parallel-computing.md index 58bdaf9f72de0..76bb41af49448 100644 --- a/doc/src/manual/parallel-computing.md +++ b/doc/src/manual/parallel-computing.md @@ -1580,6 +1580,8 @@ by using [`randjump`](@ref) function: ```julia-repl +julia> using Random + julia> function g_fix(r) a = zeros(1000) @threads for i in 1:1000 diff --git a/src/interpreter-stacktrace.c b/src/interpreter-stacktrace.c index 754c225c4b41c..c717624ffc1ae 100644 --- a/src/interpreter-stacktrace.c +++ b/src/interpreter-stacktrace.c @@ -298,6 +298,77 @@ asm( #define CALLBACK_ABI +#elif defined(_CPU_PPC64_) +/** + * Implementation notes: + * + * This needs to follow the PPC ELFv2 ABI. Which means that there is a localentry + * and a global entry. The local entry expects r2/TOC to be set correctly, while + * the global entry expects r12 to be set to the function address, and from there + * restores r2/TOC. The function pointer we are getting passed point to the global + * entry and thus we need to set r12 correctly. + * + * - LR is stored in the caller + * - r1/SP is a back-chain that needs to be atomically updated + */ + +#define MAX_INTERP_STATE_SIZE 64 +#define MIN_STACK 32 +#define STACK_PADDING 0 +#define STACK_SIZE (MIN_STACK + MAX_INTERP_STATE_SIZE + STACK_PADDING) + +size_t TOTAL_STACK_PADDING = MIN_STACK; + +// Check that the interpreter state can fit +static_assert(sizeof(interpreter_state) <= MAX_INTERP_STATE_SIZE, + "Stack layout invariants violated."); +// Check that the alignment of the type is satisfied +static_assert(alignof(interpreter_state) <= 16, "Stack layout invariants violated"); +// Check that ABI stack alignment requirement is maintained. +static_assert(STACK_SIZE % 16 == 0, "Stack layout invariants violated"); +static_assert(MIN_STACK % 16 == 0, "Stack layout invariants violated"); + +asm( + ASM_ENTRY + MANGLE("enter_interpreter_frame") ":\n" + "\taddis 2, 12, .TOC.-enter_interpreter_frame@ha\n" + "\taddi 2, 2, .TOC.-enter_interpreter_frame@l\n" + "\t.localentry enter_interpreter_frame, .-enter_interpreter_frame\n" + ".cfi_startproc\n" + // store LR + "\tmflr 0\n" + "\tstd 0, 16(1)\n" + ".cfi_offset lr, 16\n" + // set up stack frame + "\tstdu 1, -" XSTR(STACK_SIZE) "(1)\n" + ".cfi_adjust_cfa_offset " XSTR(STACK_SIZE) "\n" + "\tmtctr 3\n" // move arg1 (func pointer) to ctr + "\tmr 12, 3\n" // move func pointer to r12 if we jump to global entry point + "\tcal 3, " XSTR(MIN_STACK) "(1)\n" // move pointer to INTERP_STATE to arg1 + // zero out src and mi field + "\tli 6, 0\n" + "\tstd 6, 0(3)\n" + "\tstd 6, 8(3)\n" + // store TOC + "\tstd 2, 24(1)\n" + "Lenter_interpreter_frame_start_val:\n" + "\tbctrl\n" + "Lenter_interpreter_frame_end_val:\n" + // restore TOC + "\tld 2, 24(1)\n" + // restore stack frame + "\tld 1, 0(1)\n" + // restore LR + "\tld 0, 16(1)\n" + "\tmtlr 0\n" + ".cfi_same_value lr\n" + "\tblr\n" + ".cfi_endproc\n" + ASM_END + ); + +#define CALLBACK_ABI + #else #warning "Interpreter backtraces not implemented for this platform" #define NO_INTERP_BT diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 6b9a06863c795..ea7956bb1f369 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -862,16 +862,6 @@ (if (not (symbol? v)) (error (string "field name \"" (deparse v) "\" is not a symbol")))) field-names) - (for-each (lambda (t) - (if (expr-contains-p (lambda (e) - (and (pair? e) (eq? (car e) 'call) - (expr-contains-p (lambda (a) (memq a params)) - e))) - t) - (error (string "unsupported field type expression \"" - (deparse t) - "\"")))) - field-types) `(block (scope-block (block @@ -1705,7 +1695,7 @@ (if top (cons 'fuse make) make))) (if (and (pair? e) (eq? (car e) '|.|)) (let ((f (cadr e)) (x (caddr e))) - (cond ((or (eq? (car x) 'quote) (eq? (car x) 'inert) (eq? (car x) '$)) + (cond ((or (atom? x) (eq? (car x) 'quote) (eq? (car x) 'inert) (eq? (car x) '$)) `(call (top getproperty) ,f ,x)) ((eq? (car x) 'tuple) (if (and (eq? f '^) (length= x 3) (integer? (caddr x))) @@ -1713,7 +1703,7 @@ (list '^ (cadr x) (expand-forms `(call (call (core apply_type) (top Val) ,(caddr x)))))) (make-fuse f (cdr x)))) (else - (error (string "invalid syntax " (deparse e)))))) + (error (string "invalid syntax \"" (deparse e) "\""))))) (if (and (pair? e) (eq? (car e) 'call) (dotop? (cadr e))) (let ((f (undotop (cadr e))) (x (cddr e))) (if (and (eq? f '^) (length= x 2) (integer? (cadr x))) diff --git a/src/llvm-late-gc-lowering.cpp b/src/llvm-late-gc-lowering.cpp index d429a7fbaef17..bbf8426c96dc7 100644 --- a/src/llvm-late-gc-lowering.cpp +++ b/src/llvm-late-gc-lowering.cpp @@ -1750,7 +1750,7 @@ bool LateLowerGCFrame::CleanupIR(Function &F, State *S) { NewCall->setAttributes(attr); #endif #if JL_LLVM_VERSION >= 40000 - NewCall->copyMetadata(CI); + NewCall->copyMetadata(*CI); #else SmallVector, 1> MDs; CI->getAllMetadata(MDs); @@ -1767,7 +1767,7 @@ bool LateLowerGCFrame::CleanupIR(Function &F, State *S) { CallInst *NewCall = CallInst::Create(CI, None, CI); NewCall->takeName(CI); #if JL_LLVM_VERSION >= 40000 - NewCall->copyMetadata(CI); + NewCall->copyMetadata(*CI); #else SmallVector, 1> MDs; CI->getAllMetadata(MDs); diff --git a/src/toplevel.c b/src/toplevel.c index ea3e26992234e..427fb79144ead 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -641,9 +641,8 @@ jl_value_t *jl_toplevel_eval_flex(jl_module_t *m, jl_value_t *e, int fast, int e jl_error("syntax: malformed \".\" expression"); jl_value_t *lhs = jl_exprarg(ex, 0); jl_value_t *rhs = jl_exprarg(ex, 1); - // ('.' f (tuple args...)) is a broadcast instead, which doesn't - // go through this fast path. - if (!jl_is_expr(rhs) || ((jl_expr_t*)rhs)->head != tuple_sym) + // only handle `a.b` syntax here + 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) { diff --git a/stdlib/Pkg3/.travis.yml b/stdlib/Pkg3/.travis.yml index 92e21fc5c42a6..d1dfc96794d90 100644 --- a/stdlib/Pkg3/.travis.yml +++ b/stdlib/Pkg3/.travis.yml @@ -19,8 +19,9 @@ branches: script: - if [[ -a .git/shallow ]]; then git fetch --unshallow; fi - - julia --check-bounds=yes -e 'Pkg.clone(pwd()); Pkg.build("Pkg3"); Pkg.test("Pkg3"; coverage=true)' + - julia --check-bounds=yes -e 'using UUIDs; write("Project.toml", replace(read("Project.toml", String), r"uuid = .*?\n" =>"uuid = \"$(uuid4())\"")); import Pkg3; Pkg3.build(); Pkg3.test(; coverage=true)' after_success: - - julia -e 'cd(Pkg.dir("Pkg3")); Pkg.add("Coverage"); using Coverage; Codecov.submit(Codecov.process_folder())' + - julia -e 'import Pkg3; Pkg3.add("Documenter"); include("docs/make.jl")' + - julia -e 'import Pkg3; Pkg3.add("Coverage"); using Coverage; Codecov.submit(Codecov.process_folder())' diff --git a/stdlib/Pkg3/REQUIRE b/stdlib/Pkg3/REQUIRE deleted file mode 100644 index 4aa321c1e26fe..0000000000000 --- a/stdlib/Pkg3/REQUIRE +++ /dev/null @@ -1 +0,0 @@ -julia 0.7- diff --git a/stdlib/Pkg3/appveyor.yml b/stdlib/Pkg3/appveyor.yml index 3a2777692c3f8..56eb35ae291e9 100644 --- a/stdlib/Pkg3/appveyor.yml +++ b/stdlib/Pkg3/appveyor.yml @@ -32,8 +32,8 @@ build_script: # Need to convert from shallow to complete for Pkg.clone to work - IF EXIST .git\shallow (git fetch --unshallow) - C:\projects\julia\bin\julia -e "versioninfo(); - Pkg.clone(pwd(), \"Pkg3\"); Pkg.build(\"Pkg3\")" + using UUIDs; write(\"Project.toml\", replace(read(\"Project.toml\", String), r\"uuid = .*?\\n\" => \"uuid = \\\"\$(uuid4())\\\"\")); import Pkg3; Pkg3.build()" test_script: - - C:\projects\julia\bin\julia -e "Pkg.test(\"Pkg3\")" + - C:\projects\julia\bin\julia -e "import Pkg3; Pkg3.test(; coverage=true)" diff --git a/stdlib/Pkg3/docs/src/index.md b/stdlib/Pkg3/docs/src/index.md index 289511fd25906..06787670b6ed2 100644 --- a/stdlib/Pkg3/docs/src/index.md +++ b/stdlib/Pkg3/docs/src/index.md @@ -322,6 +322,7 @@ Downloaded MacroTools ─ v0.4.0 The dependencies of the unregistered package (here `MacroTools`) got installed. For unregistered packages we could have given a branch (or commit SHA) to track using `#`, just like for registered packages. + ## Developing packages Let’s say we found a bug in one of our dependencies, e.g. `JSON` that we want to fix. We can get the full git-repo using the `develop` command @@ -352,7 +353,7 @@ It is also possible to give a local path as the argument to `develop` which will Overriding the dependency path for a non registered package is done by giving the git-repo url as an argument to `develop`. -### Updating dependencies +## Updating dependencies When new versions of packages the project is using are released, it is a good idea to update. Simply calling `up` will try to update *all* the dependencies of the project. 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: @@ -375,7 +376,17 @@ If you just want install the packages that are given by the current `Manifest.to (HelloWorld) pkg> up --manifest --fixed ``` -### Preview mode +## Precompiling the project + +The REPL command `precompile` can be used to precompile all the dependencies in the project. You can for example do + +``` +(HelloWorld) pkg> update; precompile +``` + +do update the dependencies and then precompile them. + +## Preview mode If you just want to see the effects of running a command, but not change your state you can `preview` a command. For example: @@ -393,7 +404,7 @@ or will show you the effects adding `Plots`, or doing a full upgrade, respectively, would have on your project. However, nothing would be installed and your `Project.toml` and `Manfiest.toml` are untouched. -### Using someone elses project +## Using someone elses project Simple clone their project using e.g. `git clone`, `cd` to the project directory and call @@ -402,3 +413,5 @@ Simple clone their project using e.g. `git clone`, `cd` to the project directory ``` This will install the packages at the same state that the project you cloned was using. + + diff --git a/stdlib/Pkg3/src/API.jl b/stdlib/Pkg3/src/API.jl index bc4d651a05bfc..0947e812106f1 100644 --- a/stdlib/Pkg3/src/API.jl +++ b/stdlib/Pkg3/src/API.jl @@ -7,7 +7,7 @@ import Dates import LibGit2 import ..depots, ..logdir, ..devdir, ..print_first_command_header -import ..Operations, ..Display, ..GitTools +import ..Operations, ..Display, ..GitTools, ..Pkg3 using ..Types, ..TOML @@ -15,8 +15,10 @@ preview_info() = printstyled("───── Preview mode ─────\n"; c include("generate.jl") +parse_package(pkg) = Pkg3.REPLMode.parse_package(pkg; context=Pkg3.REPLMode.CMD_ADD) + add_or_develop(pkg::Union{String, PackageSpec}; kwargs...) = add_or_develop([pkg]; kwargs...) -add_or_develop(pkgs::Vector{String}; kwargs...) = add_or_develop([PackageSpec(pkg) for pkg in pkgs]; kwargs...) +add_or_develop(pkgs::Vector{String}; kwargs...) = add_or_develop([parse_package(pkg) for pkg in pkgs]; kwargs...) add_or_develop(pkgs::Vector{PackageSpec}; kwargs...) = add_or_develop(Context(), pkgs; kwargs...) function add_or_develop(ctx::Context, pkgs::Vector{PackageSpec}; mode::Symbol, kwargs...) @@ -27,6 +29,7 @@ function add_or_develop(ctx::Context, pkgs::Vector{PackageSpec}; mode::Symbol, k new_git = handle_repos_develop!(ctx, pkgs) else new_git = handle_repos_add!(ctx, pkgs; upgrade_or_add=true) + update_registry(ctx) end project_deps_resolve!(ctx.env, pkgs) registry_resolve!(ctx.env, pkgs) @@ -34,6 +37,7 @@ function add_or_develop(ctx::Context, pkgs::Vector{PackageSpec}; mode::Symbol, k ensure_resolved(ctx.env, pkgs, registry=true) Operations.add_or_develop(ctx, pkgs; new_git=new_git) ctx.preview && preview_info() + return end add(args...; kwargs...) = add_or_develop(args...; mode = :add, kwargs...) @@ -53,24 +57,15 @@ function rm(ctx::Context, pkgs::Vector{PackageSpec}; kwargs...) manifest_resolve!(ctx.env, pkgs) Operations.rm(ctx, pkgs) ctx.preview && preview_info() + return end -up(;kwargs...) = up(PackageSpec[]; kwargs...) -up(pkg::Union{String, PackageSpec}; kwargs...) = up([pkg]; kwargs...) -up(pkgs::Vector{String}; kwargs...) = up([PackageSpec(pkg) for pkg in pkgs]; kwargs...) -up(pkgs::Vector{PackageSpec}; kwargs...) = up(Context(), pkgs; kwargs...) - -function up(ctx::Context, pkgs::Vector{PackageSpec}; - level::UpgradeLevel=UPLEVEL_MAJOR, mode::PackageMode=PKGMODE_PROJECT, kwargs...) - print_first_command_header() - Context!(ctx; kwargs...) - ctx.preview && preview_info() - +function update_registry(ctx) # Update the registry errors = Tuple{String, String}[] if ctx.preview - info("Skipping updating registry in preview mode") + @info("Skipping updating registry in preview mode") else for reg in registries() if isdir(joinpath(reg, ".git")) @@ -86,7 +81,13 @@ function up(ctx::Context, pkgs::Vector{PackageSpec}; return end branch = LibGit2.headname(repo) - GitTools.fetch(repo) + try + GitTools.fetch(repo) + catch e + e isa LibGit2.GitError || rethrow(e) + push!(errors, (reg, "failed to fetch from repo")) + return + end ff_succeeded = try LibGit2.merge!(repo; branch="refs/remotes/origin/$branch", fastforward=true) catch e @@ -107,7 +108,6 @@ function up(ctx::Context, pkgs::Vector{PackageSpec}; end end end - if !isempty(errors) warn_str = "Some registries failed to update:" for (reg, err) in errors @@ -115,7 +115,20 @@ function up(ctx::Context, pkgs::Vector{PackageSpec}; end @warn warn_str end + return +end +up(;kwargs...) = up(PackageSpec[]; kwargs...) +up(pkg::Union{String, PackageSpec}; kwargs...) = up([pkg]; kwargs...) +up(pkgs::Vector{String}; kwargs...) = up([PackageSpec(pkg) for pkg in pkgs]; kwargs...) +up(pkgs::Vector{PackageSpec}; kwargs...) = up(Context(), pkgs; kwargs...) + +function up(ctx::Context, pkgs::Vector{PackageSpec}; + level::UpgradeLevel=UPLEVEL_MAJOR, mode::PackageMode=PKGMODE_PROJECT, kwargs...) + print_first_command_header() + Context!(ctx; kwargs...) + ctx.preview && preview_info() + update_registry(ctx) if isempty(pkgs) if mode == PKGMODE_PROJECT for (name::String, uuidstr::String) in ctx.env.project["deps"] @@ -135,6 +148,7 @@ function up(ctx::Context, pkgs::Vector{PackageSpec}; end Operations.up(ctx, pkgs) ctx.preview && preview_info() + return end @@ -149,6 +163,7 @@ function pin(ctx::Context, pkgs::Vector{PackageSpec}; kwargs...) project_deps_resolve!(ctx.env, pkgs) ensure_resolved(ctx.env, pkgs) Operations.pin(ctx, pkgs) + return end @@ -163,11 +178,12 @@ function free(ctx::Context, pkgs::Vector{PackageSpec}; kwargs...) registry_resolve!(ctx.env, pkgs) ensure_resolved(ctx.env, pkgs; registry=true) Operations.free(ctx, pkgs) + return end -test(;kwargs...) = test(PackageSpec[], kwargs...) +test(;kwargs...) = test(PackageSpec[]; kwargs...) test(pkg::Union{String, PackageSpec}; kwargs...) = test([pkg]; kwargs...) test(pkgs::Vector{String}; kwargs...) = test([PackageSpec(pkg) for pkg in pkgs]; kwargs...) test(pkgs::Vector{PackageSpec}; kwargs...) = test(Context(), pkgs; kwargs...) @@ -186,6 +202,7 @@ function test(ctx::Context, pkgs::Vector{PackageSpec}; coverage=false, kwargs... manifest_resolve!(ctx.env, pkgs) ensure_resolved(ctx.env, pkgs) Operations.test(ctx, pkgs; coverage=coverage) + return end @@ -299,6 +316,7 @@ function gc(ctx::Context=Context(); period = Dates.Week(6), kwargs...) byte_save_str = length(paths_to_delete) == 0 ? "" : ("saving " * @sprintf("%.3f %s", bytes, Base._mem_units[mb])) @info("Deleted $(length(paths_to_delete)) package installations $byte_save_str") ctx.preview && preview_info() + return end @@ -317,6 +335,7 @@ function _get_deps!(ctx::Context, pkgs::Vector{PackageSpec}, uuids::Vector{UUID} end _get_deps!(ctx, pkgs, uuids) end + return end build(pkgs...) = build([PackageSpec(pkg) for pkg in pkgs]) @@ -349,6 +368,7 @@ function build(ctx::Context, pkgs::Vector{PackageSpec}; kwargs...) length(uuids) == 0 && (@info("no packages to build"); return) Operations.build_versions(ctx, uuids; might_need_to_resolve=true) ctx.preview && preview_info() + return end init() = init(Context()) @@ -357,6 +377,82 @@ function init(ctx::Context, path::String=pwd()) print_first_command_header() Context!(ctx; env = EnvCache(joinpath(path, "Project.toml"))) Operations.init(ctx) + return +end + +##################################### +# Backwards compatibility with Pkg2 # +##################################### + +function clone(pkg::String...) + @warn "Pkg.clone is only kept for legacy CI script reasons, please use `add`" maxlog=1 + add(joinpath(pkg...)) +end + +function dir(pkg::String, paths::String...) + @warn "Pkg.dir is only kept for legacy CI script reasons" maxlog=1 + pkgid = Base.identify_package(pkg) + pkgid == nothing && return nothing + path = Base.locate_package(pkgid) + pkgid == nothing && return nothing + return joinpath(abspath(path, "..", "..", paths...)) +end + + +function precompile(ctx::Context) + printpkgstyle(ctx, :Precompiling, "project...") + + pkgids = [Base.PkgId(UUID(uuid), name) for (name, uuid) in ctx.env.project["deps"] if !(UUID(uuid) in keys(ctx.stdlibs))] + if ctx.env.pkg !== nothing && isfile( joinpath( dirname(ctx.env.project_file), "src", ctx.env.pkg.name * ".jl")) + push!(pkgids, Base.PkgId(ctx.env.pkg.uuid, ctx.env.pkg.name)) + end + + needs_to_be_precompiled = String[] + for pkg in pkgids + paths = Base.find_all_in_cache_path(pkg) + sourcepath = Base.locate_package(pkg) + if sourcepath == nothing + cmderror("couldn't find path to $(pkg.name) when trying to precompilie project") + end + found_matching_precompile = false + for path_to_try in paths::Vector{String} + staledeps = Base.stale_cachefile(sourcepath, path_to_try) + if !(staledeps isa Bool) + found_matching_precompile = true + end + end + if !found_matching_precompile + # Only precompile packages that has contains `__precompile__` or `__precompile__(true)` + source = read(sourcepath, String) + if occursin(r"__precompile__\(\)|__precompile__\(true\)", source) + push!(needs_to_be_precompiled, pkg.name) + end + end + end + + # Perhaps running all the imports in the same process would avoid some overheda. + # Julia starts pretty fast though (0.3 seconds) + code = join(["import " * pkg for pkg in needs_to_be_precompiled], '\n') * "\nexit(0)" + for (i, pkg) in enumerate(needs_to_be_precompiled) + code = """ + empty!(Base.DEPOT_PATH) + append!(Base.DEPOT_PATH, $(repr(map(abspath, DEPOT_PATH)))) + empty!(Base.DL_LOAD_PATH) + append!(Base.DL_LOAD_PATH, $(repr(map(abspath, Base.DL_LOAD_PATH)))) + empty!(Base.LOAD_PATH) + append!(Base.LOAD_PATH, $(repr(Base.LOAD_PATH))) + import $pkg + """ + printpkgstyle(ctx, :Precompiling, pkg, " [$i of $(length(needs_to_be_precompiled))]") + run(pipeline(ignorestatus(``` + $(Base.julia_cmd()) -O$(Base.JLOptions().opt_level) --color=no --history-file=no + --startup-file=$(Base.JLOptions().startupfile != 2 ? "yes" : "no") + --compiled-modules="yes" + --depwarn=no + --eval $code + ```))) + end + return nothing end end # module diff --git a/stdlib/Pkg3/src/Operations.jl b/stdlib/Pkg3/src/Operations.jl index b7c879c9a2496..acfc6d4caed1f 100644 --- a/stdlib/Pkg3/src/Operations.jl +++ b/stdlib/Pkg3/src/Operations.jl @@ -106,7 +106,7 @@ function collect_fixed!(ctx::Context, pkgs::Vector{PackageSpec}, uuid_to_name::D uuid_to_pkg[pkg.uuid] = pkg uuid_to_name[pkg.uuid] = pkg.name - found_project = collect_project!(pkg, path, fix_deps_map) + found_project = collect_project!(ctx, pkg, path, fix_deps_map) if !found_project collect_require!(ctx, pkg, path, fix_deps_map) end @@ -127,7 +127,7 @@ function collect_fixed!(ctx::Context, pkgs::Vector{PackageSpec}, uuid_to_name::D return fixed end -function collect_project!(pkg::PackageSpec, path::String, fix_deps_map::Dict{UUID,Vector{PackageSpec}}) +function collect_project!(ctx::Context, pkg::PackageSpec, path::String, fix_deps_map::Dict{UUID,Vector{PackageSpec}}) project_file = joinpath(path, "Project.toml") fix_deps_map[pkg.uuid] = valtype(fix_deps_map)() !isfile(project_file) && return false @@ -137,7 +137,12 @@ function collect_project!(pkg::PackageSpec, path::String, fix_deps_map::Dict{UUI deppkg = PackageSpec(deppkg_name, UUID(uuid), vspec) push!(fix_deps_map[pkg.uuid], deppkg) end - pkg.version = VersionNumber(get(project, "version", "0.0")) + if haskey(project, "version") + pkg.version = VersionNumber(project["version"]) + else + @warn "project file for $(pkg.name) is missing a `version` entry" + set_maximum_version_registry!(ctx.env, pkg) + end return true end diff --git a/stdlib/Pkg3/src/Pkg3.jl b/stdlib/Pkg3/src/Pkg3.jl index a4f4ecd111a40..fcbeea74b731b 100644 --- a/stdlib/Pkg3/src/Pkg3.jl +++ b/stdlib/Pkg3/src/Pkg3.jl @@ -40,6 +40,10 @@ include("REPLMode.jl") import .API: add, rm, up, test, gc, init, build, installed, pin, free, checkout, develop, generate const update = up +# legacy CI script support +import .API: clone, dir + + import .REPLMode: @pkg_str export @pkg_str diff --git a/stdlib/Pkg3/src/REPLMode.jl b/stdlib/Pkg3/src/REPLMode.jl index 6a6371b490278..7278b2c613766 100644 --- a/stdlib/Pkg3/src/REPLMode.jl +++ b/stdlib/Pkg3/src/REPLMode.jl @@ -14,7 +14,7 @@ using ..Types, ..Display, ..Operations ############ @enum(CommandKind, CMD_HELP, CMD_STATUS, CMD_SEARCH, CMD_ADD, CMD_RM, CMD_UP, CMD_TEST, CMD_GC, CMD_PREVIEW, CMD_INIT, CMD_BUILD, CMD_FREE, - CMD_PIN, CMD_CHECKOUT, CMD_DEVELOP, CMD_GENERATE) + CMD_PIN, CMD_CHECKOUT, CMD_DEVELOP, CMD_GENERATE, CMD_PRECOMPILE) struct Command kind::CommandKind @@ -52,6 +52,7 @@ const cmds = Dict( "develop" => CMD_DEVELOP, "dev" => CMD_DEVELOP, "generate" => CMD_GENERATE, + "precompile" => CMD_PRECOMPILE, ) ################# @@ -135,11 +136,11 @@ let uuid = raw"(?i)[0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12}( global const name_uuid_re = Regex("^$name\\s*=\\s*($uuid)\$") end -function parse_package(word::AbstractString; context=nothing)# ::PackageSpec +function parse_package(word::AbstractString; context=nothing)::PackageSpec word = replace(word, "~" => homedir()) if context in (CMD_ADD, CMD_DEVELOP) && isdir(word) pkg = PackageSpec() - pkg.repo = Types.GitRepo(word) + pkg.repo = Types.GitRepo(abspath(word)) return pkg elseif occursin(uuid_re, word) return PackageSpec(UUID(word)) @@ -163,14 +164,22 @@ end ################ # REPL parsing # ################ -const lex_re = r"^[\?\./\+\-](?!\-) | [^@\#\s]+\s*=\s*[^@\#\s]+ | \#\s*[^@\#\s]* | @\s*[^@\#\s]* | [^@\#\s]+"x +const lex_re = r"^[\?\./\+\-](?!\-) | [^@\#\s;]+\s*=\s*[^@\#\s;]+ | \#\s*[^@\#\s;]* | @\s*[^@\#\s;]* | [^@\#\s;]+|;"x const Token = Union{Command, Option, VersionRange, String, Rev} -function tokenize(cmd::String)::Vector{Token} +function tokenize(cmd::String)::Vector{Vector{Token}} + words = map(m->m.match, eachmatch(lex_re, cmd)) + commands = Vector{Token}[] + while !isempty(words) + push!(commands, tokenize!(words)) + end + return commands +end + +function tokenize!(words::Vector{<:AbstractString})::Vector{Token} print_first_command_header() tokens = Token[] - words = map(m->m.match, eachmatch(lex_re, cmd)) help_mode = false preview_mode = false # First parse a Command or a modifier (help / preview) + Command @@ -196,7 +205,9 @@ function tokenize(cmd::String)::Vector{Token} # Now parse the arguments / options to the command while !isempty(words) word = popfirst!(words) - if first(word) == '-' + if word == ";" + return tokens + elseif first(word) == '-' push!(tokens, parse_option(word)) elseif first(word) == '@' push!(tokens, VersionRange(strip(word[2:end]))) @@ -209,14 +220,17 @@ function tokenize(cmd::String)::Vector{Token} return tokens end + ############# # Execution # ############# function do_cmd(repl::REPL.AbstractREPL, input::String; do_rethrow=false) try - tokens = tokenize(input) - do_cmd!(tokens, repl) + commands = tokenize(input) + for command in commands + do_cmd!(command, repl) + end catch err if do_rethrow rethrow(err) @@ -269,6 +283,7 @@ function do_cmd!(tokens::Vector{Token}, repl) cmd.kind == CMD_PIN ? Base.invokelatest( do_pin!, ctx, tokens) : cmd.kind == CMD_FREE ? Base.invokelatest( do_free!, ctx, tokens) : cmd.kind == CMD_GENERATE ? Base.invokelatest( do_generate!, ctx, tokens) : + cmd.kind == CMD_PRECOMPILE ? Base.invokelatest( do_precompile!, ctx, tokens) : cmderror("`$cmd` command not yet implemented") return end @@ -283,6 +298,8 @@ backspace when the input line is empty or press Ctrl+C. pkg> [--env=...] 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 @@ -323,6 +340,8 @@ What action you want the package manager to take: `develop`: clone the full package repo locally for development `free`: undos a `pin` or `develop` + +`precompile`: precompile all the project dependencies """ const helps = Dict( @@ -466,6 +485,10 @@ const helps = Dict( pkg> develop Example#c37b675 pkg> develop https://github.com/JuliaLang/Example.jl#master ``` + """, CMD_PRECOMPILE => md""" + precompile + + Precompile all the dependencies of the project by running `import` on all of them in a new process. """ ) @@ -712,6 +735,13 @@ function do_generate!(ctx::Context, tokens::Vector{Token}) API.generate(pkg) end +function do_precompile!(ctx::Context, tokens::Vector{Token}) + if !isempty(tokens) + cmderror("`precompile` does not take any arguments") + end + API.precompile(ctx) +end + ###################### # REPL mode creation # @@ -817,7 +847,7 @@ function completions(full, index) # tokenize input, don't offer any completions for invalid commands tokens = try - tokenize(join(pre_words[1:end-1], ' ')) + tokenize(join(pre_words[1:end-1], ' '))[end] catch return String[], 0:-1, false end diff --git a/stdlib/Pkg3/src/Types.jl b/stdlib/Pkg3/src/Types.jl index 186e544d4fe98..667853b9be6d4 100644 --- a/stdlib/Pkg3/src/Types.jl +++ b/stdlib/Pkg3/src/Types.jl @@ -628,7 +628,7 @@ end const refspecs = ["+refs/*:refs/remotes/cache/*"] -const reg_pkg = r"(?:^|[/\\])(\w+?)(?:\.jl)?(?:\.git)?$" +const reg_pkg = r"(?:^|[/\\])(\w+?)(?:\.jl)?(?:\.git)?(?:\/)?$" # Windows sometimes throw on `isdir`... function isdir_windows_workaround(path::String) @@ -775,12 +775,17 @@ end function parse_package!(env, pkg, project_path) found_project_file = false for projname in project_names - if isfile(joinpath(project_path, "Project.toml")) + if isfile(joinpath(project_path, projname)) found_project_file = true project_data = parse_toml(project_path, "Project.toml") pkg.uuid = UUID(project_data["uuid"]) pkg.name = project_data["name"] - pkg.version = VersionNumber(get(project_data, "version", "0.0")) + if haskey(project_data, "version") + pkg.version = VersionNumber(project_data["version"]) + else + @warn "project file for $(pkg.name) is missing a `version` entry" + Pkg3.Operations.set_maximum_version_registry!(env, pkg) + end break end end diff --git a/stdlib/Pkg3/test/pkg.jl b/stdlib/Pkg3/test/pkg.jl index 801d8b3d8334c..4603572ceb513 100644 --- a/stdlib/Pkg3/test/pkg.jl +++ b/stdlib/Pkg3/test/pkg.jl @@ -143,6 +143,21 @@ temp_pkg_dir() do project_path end Pkg3.rm(TEST_PKG.name) + + @testset "legacy CI script" begin + mktempdir() do dir + LibGit2.with(LibGit2.clone("https://github.com/JuliaLang/Example.jl", joinpath(dir, "Example.jl"))) do r + cd(joinpath(dir, "Example.jl")) do + let Pkg = Pkg3 + Pkg.clone(pwd()) + Pkg.build("Example") + Pkg.test("Example"; coverage=true) + @test isfile(Pkg.dir("Example", "src", "Example.jl")) + end + end + end + end + end end temp_pkg_dir() do project_path diff --git a/stdlib/Pkg3/test/repl.jl b/stdlib/Pkg3/test/repl.jl index b0b6965ee0f72..1bb1ac78737ff 100644 --- a/stdlib/Pkg3/test/repl.jl +++ b/stdlib/Pkg3/test/repl.jl @@ -56,7 +56,7 @@ temp_pkg_dir() do project_path; cd(project_path) do; mktempdir() do tmp_pkg_path @test Pkg3.installed()[TEST_PKG.name] > v pkg = "UnregisteredWithoutProject" p = git_init_package(tmp_pkg_path, joinpath(@__DIR__, "test_packages/$pkg")) - Pkg3.REPLMode.pkgstr("add $p") + Pkg3.REPLMode.pkgstr("add $p; precompile") @eval import $(Symbol(pkg)) @test Pkg3.installed()[pkg] == v"0.0" Pkg3.test("UnregisteredWithoutProject") @@ -138,7 +138,7 @@ temp_pkg_dir() do project_path; cd(project_path) do cp(p2_path, p2_new_path) Pkg3.REPLMode.pkgstr("develop $(p1_new_path)") Pkg3.REPLMode.pkgstr("develop $(p2_new_path)") - Pkg3.REPLMode.pkgstr("build") + Pkg3.REPLMode.pkgstr("build; precompile") @test locate_name("UnregisteredWithProject") == joinpath(p1_new_path, "src", "UnregisteredWithProject.jl") @test locate_name("UnregisteredWithoutProject") == joinpath(p2_new_path, "src", "UnregisteredWithoutProject.jl") @test Pkg3.installed()["UnregisteredWithProject"] == v"0.1.0" diff --git a/stdlib/Pkg3/test/test_packages/UnregisteredWithoutProject/src/UnregisteredWithoutProject.jl b/stdlib/Pkg3/test/test_packages/UnregisteredWithoutProject/src/UnregisteredWithoutProject.jl index 661908156cb47..09aa3d0968990 100644 --- a/stdlib/Pkg3/test/test_packages/UnregisteredWithoutProject/src/UnregisteredWithoutProject.jl +++ b/stdlib/Pkg3/test/test_packages/UnregisteredWithoutProject/src/UnregisteredWithoutProject.jl @@ -1,3 +1,4 @@ +__precompile__() module UnregisteredWithoutProject if !isfile(joinpath(@__DIR__, "..", "deps", "deps.jl")) diff --git a/stdlib/SparseArrays/src/sparsematrix.jl b/stdlib/SparseArrays/src/sparsematrix.jl index 821e4c396ff93..134897631050c 100644 --- a/stdlib/SparseArrays/src/sparsematrix.jl +++ b/stdlib/SparseArrays/src/sparsematrix.jl @@ -2306,7 +2306,7 @@ function getindex(A::SparseMatrixCSC{Tv,Ti}, I::AbstractArray) where {Tv,Ti} end end end - colptrB = cumsum(colptrB) + cumsum!(colptrB,colptrB) if n > (idxB-1) deleteat!(nzvalB, idxB:n) deleteat!(rowvalB, idxB:n) diff --git a/test/abstractarray.jl b/test/abstractarray.jl index c99fe4c9ba5e2..f5da393a70777 100644 --- a/test/abstractarray.jl +++ b/test/abstractarray.jl @@ -732,8 +732,8 @@ A = TSlowNIndexes(rand(2,2)) @test first(A) == A.data[1] @testset "#16381" begin - @inferred size(rand(3,2,1), 2, 1) - @inferred size(rand(3,2,1), 2, 1, 3) + @inferred size(rand(3,2,1)) + @inferred size(rand(3,2,1), 2) @test @inferred(axes(rand(3,2))) == (1:3,1:2) @test @inferred(axes(rand(3,2,1))) == (1:3,1:2,1:1) diff --git a/test/arrayops.jl b/test/arrayops.jl index 2b9b7a592adaf..788aa899510ec 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -189,6 +189,22 @@ end end end +struct Z26163 <: AbstractArray{Int,0}; end +Base.size(::Z26163) = () +Base.getindex(::Z26163) = 0 +struct V26163 <: AbstractArray{Int,1}; end +Base.size(::V26163) = (1,) +Base.getindex(::V26163, ::Int) = 0 +@testset "reshape of custom zero- and one-dimensional arrays" begin + z = Z26163() + v = V26163() + @test z == reshape(v, ()) == fill(0, ()) + @test reshape(z, 1) == v == [0] + @test reshape(z, 1, 1) == reshape(v, 1, 1) == fill(0, 1, 1) + @test occursin("1-element reshape", summary(reshape(z, 1))) + @test_broken occursin("0-dimensional reshape", summary(reshape(v, ()))) +end + @test reshape(1:5, (5,)) === 1:5 @test reshape(1:5, 5) === 1:5 @@ -1478,15 +1494,14 @@ end let a = Array{Float64}(undef, 10) @test size(a) == (10,) @test size(a, 1) == 10 - @test size(a,2,1) == (1,10) + @test (size(a,2), size(a,1)) == (1,10) aa = Array{Float64}(undef, 2,3) @test size(aa) == (2,3) - @test size(aa,4,3,2,1) == (1,1,3,2) - @test size(aa,1,2) == (2,3) + @test (size(aa,4), size(aa,3), size(aa,2), size(aa,1)) == (1,1,3,2) aaa = Array{Float64}(undef, 9,8,7,6,5,4,3,2,1) - @test size(aaa,1,1) == (9,9) + @test size(aaa,1) == 9 @test size(aaa,4) == 6 - @test size(aaa,9,8,7,6,5,4,3,2,19,8,7,6,5,4,3,2,1) == (1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,9) + @test size(aaa) == (9,8,7,6,5,4,3,2,1) #18459 Test Array{T, N} constructor b = Vector{Float64}(undef, 10) @@ -2059,9 +2074,12 @@ end # issue #18363 @test_throws DimensionMismatch cumsum!([0,0], 1:4) @test cumsum(Any[])::Vector{Any} == Any[] -@test cumsum(Any[1, 2.3])::Vector{Any} == [1, 3.3] == cumsum(Real[1, 2.3])::Vector{Real} +@test cumsum(Any[1, 2.3]) == [1, 3.3] == cumsum(Real[1, 2.3])::Vector{Real} @test cumsum([true,true,true]) == [1,2,3] -@test cumsum(0x00:0xff)[end] === 0x80 # overflow +@test cumsum(0x00:0xff)[end] === UInt(255*(255+1)÷2) # no overflow +@test accumulate(+, 0x00:0xff)[end] === 0x80 # overflow +@test_throws InexactError cumsum!(similar(0x00:0xff), 0x00:0xff) # overflow + @test cumsum([[true], [true], [false]])::Vector{Vector{Int}} == [[1], [2], [2]] #issue #18336 @@ -2176,19 +2194,16 @@ end b = Vector{Float64}(undef, 10) @test size(a) == (10,) @test size(a, 1) == 10 - @test size(a,2,1) == (1,10) @test size(a) == size(b) a = Array{Float64}(undef, 2,3) b = Matrix{Float64}(undef, 2,3) @test size(a) == (2,3) - @test size(a,4,3,2,1) == (1,1,3,2) - @test size(a,1,2) == (2,3) + @test (size(a, 1), size(a, 2), size(a, 3)) == (2,3,1) @test size(a) == size(b) a = Array{Float64}(undef, 9,8,7,6,5,4,3,2,1) b = Array{Float64,9}(undef, 9,8,7,6,5,4,3,2,1) - @test size(a,1,1) == (9,9) @test size(a,4) == 6 - @test size(a,9,8,7,6,5,4,3,2,19,8,7,6,5,4,3,2,1) == (1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,9) + @test size(a) == (9,8,7,6,5,4,3,2,1) @test size(a) == size(b) end @@ -2278,6 +2293,13 @@ end op(x,y) = 2x+y @test accumulate(op, [10,20, 30]) == [10, op(10, 20), op(op(10, 20), 30)] == [10, 40, 110] @test accumulate(op, [10 20 30], dims=2) == [10 op(10, 20) op(op(10, 20), 30)] == [10 40 110] + + #25506 + @test accumulate((acc, x) -> acc+x[1], 0, [(1,2), (3,4), (5,6)]) == [1, 4, 9] + @test accumulate(*, ['a', 'b']) == ["a", "ab"] + @inferred accumulate(*, String[]) + @test accumulate(*, ['a' 'b'; 'c' 'd'], dims=1) == ["a" "b"; "ac" "bd"] + @test accumulate(*, ['a' 'b'; 'c' 'd'], dims=2) == ["a" "ab"; "c" "cd"] end struct F21666{T <: Base.ArithmeticStyle} diff --git a/test/hashing.jl b/test/hashing.jl index 90c9eafe01e4f..b9675d9ce8a05 100644 --- a/test/hashing.jl +++ b/test/hashing.jl @@ -223,3 +223,18 @@ let vals_expr = :(Any[Vector, (Array{T,1} where T), 1, 2, Union{Int, String}, Un @test (a === b) == (objectid(a) == objectid(b)) end end + +# issue #26038 +let p1 = Ptr{Int8}(1), p2 = Ptr{Int32}(1), p3 = Ptr{Int8}(2) + @test p1 == p2 + @test !isequal(p1, p2) + @test p1 != p3 + @test hash(p1) != hash(p2) + @test hash(p1) != hash(p3) + @test hash(p1) == hash(Ptr{Int8}(1)) + + @test p1 < p3 + @test !(p1 < p2) + @test isless(p1, p3) + @test_throws MethodError isless(p1, p2) +end diff --git a/test/int.jl b/test/int.jl index 9ed384e561b1c..9d645256051be 100644 --- a/test/int.jl +++ b/test/int.jl @@ -188,15 +188,15 @@ end end end @testset "widen/widemul" begin - @test widen(UInt8(3)) === UInt32(3) - @test widen(UInt16(3)) === UInt32(3) + @test widen(UInt8(3)) === UInt(3) + @test widen(UInt16(3)) === UInt(3) @test widen(UInt32(3)) === UInt64(3) @test widen(UInt64(3)) === UInt128(3) @test widen(UInt128(3)) == 3 @test typeof(widen(UInt128(3))) == BigInt - @test widen(Int8(-3)) === Int32(-3) - @test widen(Int16(-3)) === Int32(-3) + @test widen(Int8(-3)) === Int(-3) + @test widen(Int16(-3)) === Int(-3) @test widen(Int32(-3)) === Int64(-3) @test widen(Int64(-3)) === Int128(-3) @test widen(Int128(-3)) == -3 diff --git a/test/numbers.jl b/test/numbers.jl index 6ec22f739b2ee..b4a8910d365d5 100644 --- a/test/numbers.jl +++ b/test/numbers.jl @@ -2013,7 +2013,7 @@ end @testset "widen and widemul" begin @test widen(1.5f0) === 1.5 @test widen(Int32(42)) === Int64(42) - @test widen(Int8) === Int32 + @test widen(Int8) === Int @test widen(Int64) === Int128 @test widen(Float32) === Float64 @test widen(Float16) === Float32 diff --git a/test/strings/io.jl b/test/strings/io.jl index cb928f65f001c..9ba490b5f3310 100644 --- a/test/strings/io.jl +++ b/test/strings/io.jl @@ -144,7 +144,8 @@ end end @testset "join()" begin - @test join([]) == "" + @test join([]) == join([],",") == "" + @test_broken join(()) == join((),",") == "" @test join(["a"],"?") == "a" @test join("HELLO",'-') == "H-E-L-L-O" @test join(1:5, ", ", " and ") == "1, 2, 3, 4 and 5" diff --git a/test/syntax.jl b/test/syntax.jl index 9f9f0d9687dde..4a1a64a8cc1d1 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -939,8 +939,8 @@ g21054(>:) = >:2 @test g21054(-) == -2 # issue #21168 -@test Meta.lower(Main, :(a.[1])) == Expr(:error, "invalid syntax a.[1]") -@test Meta.lower(Main, :(a.{1})) == Expr(:error, "invalid syntax a.{1}") +@test Meta.lower(Main, :(a.[1])) == Expr(:error, "invalid syntax \"a.[1]\"") +@test Meta.lower(Main, :(a.{1})) == Expr(:error, "invalid syntax \"a.{1}\"") # Issue #21225 let abstr = Meta.parse("abstract type X end") @@ -1406,3 +1406,16 @@ invalid assignment location "function (s, o...) end end\"""" end + +# issue #26739 +@test_throws ErrorException("syntax: invalid syntax \"sin.[1]\"") eval(@__MODULE__, :(sin.[1])) + +# issue #26873 +f26873 = 0 +try + include_string(@__MODULE__, """f26873."a" """) + @test false +catch e + @test e isa LoadError + @test e.error isa MethodError +end