diff --git a/NEWS.md b/NEWS.md index e4885c4c34d51..445f3b84ad178 100644 --- a/NEWS.md +++ b/NEWS.md @@ -39,6 +39,11 @@ New library features Standard library changes ------------------------ +* The `length` function on certain ranges of certain specific element types no longer checks for integer + overflow in most cases. The new function `checked_length` is now available, which will try to use checked + arithmetic to error if the result may be wrapping. Or use a package such as SaferIntegers.jl when + constructing the range. ([#40382]) + #### Package Manager #### LinearAlgebra diff --git a/README.md b/README.md index 3f5990cf4c76c..1021f58331087 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,16 @@ Documentation: [![version 1][docs-img]](https://docs.julialang.org) +Code coverage: +[![coveralls][coveralls-img]](https://coveralls.io/r/JuliaLang/julia?branch=master) +[![codecov][codecov-img]](https://codecov.io/github/JuliaLang/julia?branch=master) + +Continuous integration: [![Build status](https://badge.buildkite.com/f28e0d28b345f9fad5856ce6a8d64fffc7c70df8f4f2685cd8.svg?branch=master)](https://buildkite.com/julialang/julia) + [docs-img]: https://img.shields.io/badge/docs-v1-blue.svg +[coveralls-img]: https://img.shields.io/coveralls/github/JuliaLang/julia/master.svg?label=coveralls +[codecov-img]: https://img.shields.io/codecov/c/github/JuliaLang/julia/master.svg?label=codecov + ## The Julia Language @@ -141,19 +150,3 @@ Supported IDEs include: [julia-vscode](https://github.com/JuliaEditorSupport/jul Code plugin), [Juno](http://junolab.org/) (Atom plugin). [Jupyter](https://jupyter.org/) notebooks are available through the [IJulia](https://github.com/JuliaLang/IJulia.jl) package, and [Pluto](https://github.com/fonsp/Pluto.jl) notebooks through the Pluto.jl package. - -## Continuous Integration (CI) Builders - -Code coverage: -[![coveralls][coveralls-img]](https://coveralls.io/r/JuliaLang/julia?branch=master) -[![codecov][codecov-img]](https://codecov.io/github/JuliaLang/julia?branch=master) - -| Builder | Status | -| ---------- | ------ | -| Overall | [![Build status](https://badge.buildkite.com/f28e0d28b345f9fad5856ce6a8d64fffc7c70df8f4f2685cd8.svg?branch=master)](https://buildkite.com/julialang/julia) | -| analyzegc | [![Build status](https://badge.buildkite.com/f28e0d28b345f9fad5856ce6a8d64fffc7c70df8f4f2685cd8.svg?branch=master&step=analyzegc)](https://buildkite.com/julialang/julia) | -| llvmpasses | [![Build status](https://badge.buildkite.com/f28e0d28b345f9fad5856ce6a8d64fffc7c70df8f4f2685cd8.svg?branch=master&step=llvmpasses)](https://buildkite.com/julialang/julia) | -| coverage | [![Build status](https://badge.buildkite.com/d5ae34dbbf6fefe615300c4f3118bf63cb4a5ae7fd962263c1.svg?branch=master)](https://buildkite.com/julialang/julia-coverage-linux64) | - -[coveralls-img]: https://img.shields.io/coveralls/github/JuliaLang/julia/master.svg?label=coveralls -[codecov-img]: https://img.shields.io/codecov/c/github/JuliaLang/julia/master.svg?label=codecov diff --git a/base/Base.jl b/base/Base.jl index 9a4091447395b..0bfd3b6e7d7b7 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -124,11 +124,6 @@ include("refpointer.jl") include("checked.jl") using .Checked -# SIMD loops -@pure sizeof(s::String) = Core.sizeof(s) # needed by gensym as called from simdloop -include("simdloop.jl") -using .SimdLoop - # array structures include("indices.jl") include("array.jl") @@ -177,6 +172,14 @@ using .MultiplicativeInverses include("abstractarraymath.jl") include("arraymath.jl") +# SIMD loops +@pure sizeof(s::String) = Core.sizeof(s) # needed by gensym as called from simdloop +include("simdloop.jl") +using .SimdLoop + +# functions for AbstractArray with @simd +include("abstractarraypatch.jl") + # map-reduce operators include("reduce.jl") diff --git a/base/abstractarray.jl b/base/abstractarray.jl index eeda52ea22e4c..bb49521756829 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -116,9 +116,6 @@ axes1(A::AbstractArray{<:Any,0}) = OneTo(1) axes1(A::AbstractArray) = (@_inline_meta; axes(A)[1]) axes1(iter) = oneto(length(iter)) -unsafe_indices(A) = axes(A) -unsafe_indices(r::AbstractRange) = (oneto(unsafe_length(r)),) # Ranges use checked_sub for size - """ keys(a::AbstractArray) @@ -580,14 +577,14 @@ end function trailingsize(inds::Indices, n) s = 1 for i=n:length(inds) - s *= unsafe_length(inds[i]) + s *= length(inds[i]) end return s end # This version is type-stable even if inds is heterogeneous function trailingsize(inds::Indices) @_inline_meta - prod(map(unsafe_length, inds)) + prod(map(length, inds)) end ## Bounds checking ## @@ -688,7 +685,7 @@ function checkbounds_indices(::Type{Bool}, ::Tuple{}, I::Tuple) @_inline_meta checkindex(Bool, OneTo(1), I[1])::Bool & checkbounds_indices(Bool, (), tail(I)) end -checkbounds_indices(::Type{Bool}, IA::Tuple, ::Tuple{}) = (@_inline_meta; all(x->unsafe_length(x)==1, IA)) +checkbounds_indices(::Type{Bool}, IA::Tuple, ::Tuple{}) = (@_inline_meta; all(x->length(x)==1, IA)) checkbounds_indices(::Type{Bool}, ::Tuple{}, ::Tuple{}) = true throw_boundserror(A, I) = (@_noinline_meta; throw(BoundsError(A, I))) @@ -886,7 +883,35 @@ function copy!(dst::AbstractArray, src::AbstractArray) end ## from general iterable to any array + +""" + copyto!(dest::AbstractArray, src) -> dest + +Copy all elements from collection `src` to array `dest`, whose length must be greater than +or equal to the length `n` of `src`. The first `n` elements of `dest` are overwritten, +the other elements are left untouched. +See also [`copy!`](@ref Base.copy!), [`copy`](@ref). + +# Examples +```jldoctest +julia> x = [1., 0., 3., 0., 5.]; + +julia> y = zeros(7); + +julia> copyto!(y, x); + +julia> y +7-element Vector{Float64}: + 1.0 + 0.0 + 3.0 + 0.0 + 5.0 + 0.0 + 0.0 +``` +""" function copyto!(dest::AbstractArray, src) destiter = eachindex(dest) y = iterate(destiter) @@ -964,87 +989,6 @@ function copyto!(dest::AbstractArray, dstart::Integer, src, sstart::Integer, n:: return dest end -## copy between abstract arrays - generally more efficient -## since a single index variable can be used. - -""" - copyto!(dest::AbstractArray, src) -> dest - -Copy all elements from collection `src` to array `dest`, whose length must be greater than -or equal to the length `n` of `src`. The first `n` elements of `dest` are overwritten, -the other elements are left untouched. - -See also [`copy!`](@ref Base.copy!), [`copy`](@ref). - -# Examples -```jldoctest -julia> x = [1., 0., 3., 0., 5.]; - -julia> y = zeros(7); - -julia> copyto!(y, x); - -julia> y -7-element Vector{Float64}: - 1.0 - 0.0 - 3.0 - 0.0 - 5.0 - 0.0 - 0.0 -``` -""" -function copyto!(dest::AbstractArray, src::AbstractArray) - isempty(src) && return dest - src′ = unalias(dest, src) - copyto_unaliased!(IndexStyle(dest), dest, IndexStyle(src′), src′) -end - -function copyto!(deststyle::IndexStyle, dest::AbstractArray, srcstyle::IndexStyle, src::AbstractArray) - isempty(src) && return dest - src′ = unalias(dest, src) - copyto_unaliased!(deststyle, dest, srcstyle, src′) -end - -function copyto_unaliased!(deststyle::IndexStyle, dest::AbstractArray, srcstyle::IndexStyle, src::AbstractArray) - isempty(src) && return dest - length(dest) >= length(src) || throw(BoundsError(dest, LinearIndices(src))) - if deststyle isa IndexLinear - if srcstyle isa IndexLinear - Δi = firstindex(dest) - firstindex(src) - for i in eachindex(src) - @inbounds dest[i + Δi] = src[i] - end - else - j = firstindex(dest) - 1 - @inbounds @simd for I in eachindex(src) - dest[j+=1] = src[I] - end - end - else - if srcstyle isa IndexLinear - i = firstindex(src) - 1 - @inbounds @simd for J in eachindex(dest) - dest[J] = src[i+=1] - end - else - iterdest, itersrc = eachindex(dest), eachindex(src) - if iterdest == itersrc - # Shared-iterator implementation - @inbounds @simd for I in itersrc - dest[I] = src[I] - end - else - for (I,J) in zip(itersrc, iterdest) - @inbounds dest[J] = src[I] - end - end - end - end - return dest -end - function copyto!(dest::AbstractArray, dstart::Integer, src::AbstractArray) copyto!(dest, dstart, src, first(LinearIndices(src)), length(src)) end @@ -2497,8 +2441,8 @@ function _sub2ind_recurse(inds, L, ind, i::Integer, I::Integer...) end nextL(L, l::Integer) = L*l -nextL(L, r::AbstractUnitRange) = L*unsafe_length(r) -nextL(L, r::Slice) = L*unsafe_length(r.indices) +nextL(L, r::AbstractUnitRange) = L*length(r) +nextL(L, r::Slice) = L*length(r.indices) offsetin(i, l::Integer) = i-1 offsetin(i, r::AbstractUnitRange) = i-first(r) @@ -2524,7 +2468,7 @@ end _lookup(ind, d::Integer) = ind+1 _lookup(ind, r::AbstractUnitRange) = ind+first(r) _div(ind, d::Integer) = div(ind, d), 1, d -_div(ind, r::AbstractUnitRange) = (d = unsafe_length(r); (div(ind, d), first(r), d)) +_div(ind, r::AbstractUnitRange) = (d = length(r); (div(ind, d), first(r), d)) # Vectorized forms function _sub2ind(inds::Indices{1}, I1::AbstractVector{T}, I::AbstractVector{T}...) where T<:Integer diff --git a/base/abstractarraypatch.jl b/base/abstractarraypatch.jl new file mode 100644 index 0000000000000..eee678fccecaf --- /dev/null +++ b/base/abstractarraypatch.jl @@ -0,0 +1,55 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +## copy between abstract arrays - generally more efficient +## since a single index variable can be used. +## copyto_unaliased! use @simd to speed up, so these definition is seperated from abstractarray.jl + +function copyto!(dest::AbstractArray, src::AbstractArray) + isempty(src) && return dest + src′ = unalias(dest, src) + copyto_unaliased!(IndexStyle(dest), dest, IndexStyle(src′), src′) +end + +function copyto!(deststyle::IndexStyle, dest::AbstractArray, srcstyle::IndexStyle, src::AbstractArray) + isempty(src) && return dest + src′ = unalias(dest, src) + copyto_unaliased!(deststyle, dest, srcstyle, src′) +end + +function copyto_unaliased!(deststyle::IndexStyle, dest::AbstractArray, srcstyle::IndexStyle, src::AbstractArray) + isempty(src) && return dest + length(dest) >= length(src) || throw(BoundsError(dest, LinearIndices(src))) + if deststyle isa IndexLinear + if srcstyle isa IndexLinear + Δi = firstindex(dest) - firstindex(src) + for i in eachindex(src) + @inbounds dest[i + Δi] = src[i] + end + else + j = firstindex(dest) - 1 + @inbounds @simd for I in eachindex(src) + dest[j+=1] = src[I] + end + end + else + if srcstyle isa IndexLinear + i = firstindex(src) - 1 + @inbounds @simd for J in eachindex(dest) + dest[J] = src[i+=1] + end + else + iterdest, itersrc = eachindex(dest), eachindex(src) + if iterdest == itersrc + # Shared-iterator implementation + @inbounds @simd for I in itersrc + dest[I] = src[I] + end + else + for (I,J) in zip(itersrc, iterdest) + @inbounds dest[J] = src[I] + end + end + end + end + return dest +end diff --git a/base/broadcast.jl b/base/broadcast.jl index 7c87fef3f19e6..c6651e28489a3 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -566,7 +566,7 @@ an `Int`. """ Base.@propagate_inbounds newindex(arg, I::CartesianIndex) = CartesianIndex(_newindex(axes(arg), I.I)) Base.@propagate_inbounds newindex(arg, I::Integer) = CartesianIndex(_newindex(axes(arg), (I,))) -Base.@propagate_inbounds _newindex(ax::Tuple, I::Tuple) = (ifelse(Base.unsafe_length(ax[1])==1, ax[1][1], I[1]), _newindex(tail(ax), tail(I))...) +Base.@propagate_inbounds _newindex(ax::Tuple, I::Tuple) = (ifelse(length(ax[1]) == 1, ax[1][1], I[1]), _newindex(tail(ax), tail(I))...) Base.@propagate_inbounds _newindex(ax::Tuple{}, I::Tuple) = () Base.@propagate_inbounds _newindex(ax::Tuple, I::Tuple{}) = (ax[1][1], _newindex(tail(ax), ())...) Base.@propagate_inbounds _newindex(ax::Tuple{}, I::Tuple{}) = () diff --git a/base/checked.jl b/base/checked.jl index 840015861923f..ba23d4c5acd2b 100644 --- a/base/checked.jl +++ b/base/checked.jl @@ -6,14 +6,14 @@ module Checked export checked_neg, checked_abs, checked_add, checked_sub, checked_mul, checked_div, checked_rem, checked_fld, checked_mod, checked_cld, - add_with_overflow, sub_with_overflow, mul_with_overflow + checked_length, add_with_overflow, sub_with_overflow, mul_with_overflow import Core.Intrinsics: checked_sadd_int, checked_ssub_int, checked_smul_int, checked_sdiv_int, checked_srem_int, checked_uadd_int, checked_usub_int, checked_umul_int, checked_udiv_int, checked_urem_int -import ..no_op_err, ..@_inline_meta, ..@_noinline_meta +import ..no_op_err, ..@_inline_meta, ..@_noinline_meta, ..checked_length # define promotion behavior for checked operations checked_add(x::Integer, y::Integer) = checked_add(promote(x,y)...) @@ -349,4 +349,12 @@ The overflow protection may impose a perceptible performance penalty. """ checked_cld(x::T, y::T) where {T<:Integer} = cld(x, y) # Base.cld already checks +""" + Base.checked_length(r) + +Calculates `length(r)`, but may check for overflow errors where applicable when +the result doesn't fit into `Union{Integer(eltype(r)),Int}`. +""" +checked_length(r) = length(r) # for most things, length doesn't error + end diff --git a/base/compiler/compiler.jl b/base/compiler/compiler.jl index a3e05b3040f9e..6a0362b78f759 100644 --- a/base/compiler/compiler.jl +++ b/base/compiler/compiler.jl @@ -70,11 +70,6 @@ add_with_overflow(x::T, y::T) where {T<:SignedInt} = checked_sadd_int(x, y) add_with_overflow(x::T, y::T) where {T<:UnsignedInt} = checked_uadd_int(x, y) add_with_overflow(x::Bool, y::Bool) = (x+y, false) -# SIMD loops (The new copyto_unalias! use @simd) -@pure sizeof(s::String) = Core.sizeof(s) # needed by gensym as called from simdloop -include("simdloop.jl") -using .SimdLoop - # core array operations include("indices.jl") include("array.jl") diff --git a/base/deprecated.jl b/base/deprecated.jl index d60cc3393662c..3ed9ffa9c34e8 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -240,6 +240,9 @@ end @deprecate cat_shape(dims, shape::Tuple{}, shapes::Tuple...) cat_shape(dims, shapes) false cat_shape(dims, shape::Tuple{}) = () # make sure `cat_shape(dims, ())` do not recursively calls itself +@deprecate unsafe_indices(A) axes(A) false +@deprecate unsafe_length(r) length(r) false + # END 1.6 deprecations # BEGIN 1.7 deprecations diff --git a/base/indices.jl b/base/indices.jl index ef9e2c52ca6a3..817d9d435522b 100644 --- a/base/indices.jl +++ b/base/indices.jl @@ -352,17 +352,14 @@ struct Slice{T<:AbstractUnitRange} <: AbstractUnitRange{Int} end Slice(S::Slice) = S axes(S::Slice) = (IdentityUnitRange(S.indices),) -unsafe_indices(S::Slice) = (IdentityUnitRange(S.indices),) axes1(S::Slice) = IdentityUnitRange(S.indices) axes(S::Slice{<:OneTo}) = (S.indices,) -unsafe_indices(S::Slice{<:OneTo}) = (S.indices,) axes1(S::Slice{<:OneTo}) = S.indices first(S::Slice) = first(S.indices) last(S::Slice) = last(S.indices) size(S::Slice) = (length(S.indices),) length(S::Slice) = length(S.indices) -unsafe_length(S::Slice) = unsafe_length(S.indices) getindex(S::Slice, i::Int) = (@_inline_meta; @boundscheck checkbounds(S, i); i) getindex(S::Slice, i::AbstractUnitRange{<:Integer}) = (@_inline_meta; @boundscheck checkbounds(S, i); i) getindex(S::Slice, i::StepRange{<:Integer}) = (@_inline_meta; @boundscheck checkbounds(S, i); i) @@ -383,17 +380,14 @@ end IdentityUnitRange(S::IdentityUnitRange) = S # IdentityUnitRanges are offset and thus have offset axes, so they are their own axes axes(S::IdentityUnitRange) = (S,) -unsafe_indices(S::IdentityUnitRange) = (S,) axes1(S::IdentityUnitRange) = S axes(S::IdentityUnitRange{<:OneTo}) = (S.indices,) -unsafe_indices(S::IdentityUnitRange{<:OneTo}) = (S.indices,) axes1(S::IdentityUnitRange{<:OneTo}) = S.indices first(S::IdentityUnitRange) = first(S.indices) last(S::IdentityUnitRange) = last(S.indices) size(S::IdentityUnitRange) = (length(S.indices),) length(S::IdentityUnitRange) = length(S.indices) -unsafe_length(S::IdentityUnitRange) = unsafe_length(S.indices) getindex(S::IdentityUnitRange, i::Int) = (@_inline_meta; @boundscheck checkbounds(S, i); i) getindex(S::IdentityUnitRange, i::AbstractUnitRange{<:Integer}) = (@_inline_meta; @boundscheck checkbounds(S, i); i) getindex(S::IdentityUnitRange, i::StepRange{<:Integer}) = (@_inline_meta; @boundscheck checkbounds(S, i); i) @@ -479,7 +473,7 @@ convert(::Type{LinearIndices{N,R}}, inds::LinearIndices{N}) where {N,R} = # AbstractArray implementation IndexStyle(::Type{<:LinearIndices}) = IndexLinear() axes(iter::LinearIndices) = map(axes1, iter.indices) -size(iter::LinearIndices) = map(unsafe_length, iter.indices) +size(iter::LinearIndices) = map(length, iter.indices) function getindex(iter::LinearIndices, i::Int) @_inline_meta @boundscheck checkbounds(iter, i) diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 2f277af36cdc1..8104cddb34387 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -849,7 +849,7 @@ function _unsafe_getindex(::IndexStyle, A::AbstractArray, I::Vararg{Union{Real, # This is specifically not inlined to prevent excessive allocations in type unstable code shape = index_shape(I...) dest = similar(A, shape) - map(unsafe_length, axes(dest)) == map(unsafe_length, shape) || throw_checksize_error(dest, shape) + map(length, axes(dest)) == map(length, shape) || throw_checksize_error(dest, shape) _unsafe_getindex!(dest, A, I...) # usually a generated function, don't allow it to impact inference result return dest end diff --git a/base/operators.jl b/base/operators.jl index 3292ab8f7abe6..00211ea56e76b 100644 --- a/base/operators.jl +++ b/base/operators.jl @@ -875,15 +875,24 @@ const ÷ = div Modulus after flooring division, returning a value `r` such that `mod(r, y) == mod(x, y)` in the range ``(0, y]`` for positive `y` and in the range ``[y,0)`` for negative `y`. -See also [`fld1`](@ref), [`fldmod1`](@ref). +With integer arguments and positive `y`, this is equal to `mod(x, 1:y)`, and hence natural +for 1-based indexing. By comparison, `mod(x, y) == mod(x, 0:y-1)` is natural for computations with +offsets or strides. + +See also [`mod`](@ref), [`fld1`](@ref), [`fldmod1`](@ref). # Examples ```jldoctest julia> mod1(4, 2) 2 -julia> mod1(4, 3) -1 +julia> mod1.(-5:5, 3)' +1×11 adjoint(::Vector{Int64}) with eltype Int64: + 1 2 3 1 2 3 1 2 3 1 2 + +julia> mod1.([-0.1, 0, 0.1, 1, 2, 2.9, 3, 3.1]', 3) +1×8 Matrix{Float64}: + 2.9 3.0 0.1 1.0 2.0 2.9 3.0 0.1 ``` """ mod1(x::T, y::T) where {T<:Real} = (m = mod(x, y); ifelse(m == 0, y, m)) diff --git a/base/range.jl b/base/range.jl index 37b2da7a3bb4c..8845f363cd38c 100644 --- a/base/range.jl +++ b/base/range.jl @@ -585,9 +585,11 @@ end ## interface implementations +length(r::AbstractRange) = error("length implementation missing") # catch mistakes size(r::AbstractRange) = (length(r),) isempty(r::StepRange) = + # steprange_last_empty(r.start, r.step, r.stop) == r.stop (r.start != r.stop) & ((r.step > zero(r.step)) != (r.stop > r.start)) isempty(r::AbstractUnitRange) = first(r) > last(r) isempty(r::StepRangeLen) = length(r) == 0 @@ -614,7 +616,7 @@ julia> step(range(2.5, stop=10.9, length=85)) ``` """ step(r::StepRange) = r.step -step(r::AbstractUnitRange{T}) where{T} = oneunit(T) - zero(T) +step(r::AbstractUnitRange{T}) where {T} = oneunit(T) - zero(T) step(r::StepRangeLen) = r.step step(r::StepRangeLen{T}) where {T<:AbstractFloat} = T(r.step) step(r::LinRange) = (last(r)-first(r))/r.lendiv @@ -622,60 +624,127 @@ step(r::LinRange) = (last(r)-first(r))/r.lendiv step_hp(r::StepRangeLen) = r.step step_hp(r::AbstractRange) = step(r) -unsafe_length(r::AbstractRange) = length(r) # generic fallback - -function unsafe_length(r::StepRange) - n = Integer(div((r.stop - r.start) + r.step, r.step)) - isempty(r) ? zero(n) : n -end -length(r::StepRange) = unsafe_length(r) -unsafe_length(r::AbstractUnitRange) = Integer(last(r) - first(r) + step(r)) -unsafe_length(r::OneTo) = Integer(r.stop - zero(r.stop)) -length(r::AbstractUnitRange) = unsafe_length(r) -length(r::OneTo) = unsafe_length(r) -length(r::StepRangeLen) = r.len -length(r::LinRange) = r.len +axes(r::AbstractRange) = (oneto(length(r)),) # Needed to fold the `firstindex` call in SimdLoop.simd_index firstindex(::UnitRange) = 1 firstindex(::StepRange) = 1 firstindex(::LinRange) = 1 -function length(r::StepRange{T}) where T<:Union{Int,UInt,Int64,UInt64,Int128,UInt128} - isempty(r) && return zero(T) - if r.step > 1 - return checked_add(convert(T, div(unsigned(r.stop - r.start), r.step)), one(T)) - elseif r.step < -1 - return checked_add(convert(T, div(unsigned(r.start - r.stop), -r.step)), one(T)) - elseif r.step > 0 - return checked_add(div(checked_sub(r.stop, r.start), r.step), one(T)) +# n.b. checked_length for these is defined iff checked_add and checked_sub are +# defined between the relevant types +function checked_length(r::OrdinalRange{T}) where T + s = step(r) + # s != 0, by construction, but avoids the division error later + start = first(r) + if s == zero(s) || isempty(r) + return Integer(start - start + zero(s)) + end + stop = last(r) + if isless(s, zero(s)) + diff = checked_sub(start, stop) + s = -s else - return checked_add(div(checked_sub(r.start, r.stop), -r.step), one(T)) + diff = checked_sub(stop, start) end + a = Integer(div(diff, s)) + return checked_add(a, one(a)) end -function length(r::AbstractUnitRange{T}) where T<:Union{Int,Int64,Int128} +function checked_length(r::AbstractUnitRange{T}) where T + # compiler optimization: remove dead cases from above + if isempty(r) + return Integer(first(r) - first(r)) + end + a = Integer(checked_add(checked_sub(last(r), first(r)))) + return checked_add(a, one(a)) +end + +function length(r::OrdinalRange{T}) where T + s = step(r) + # s != 0, by construction, but avoids the division error later + start = first(r) + if s == zero(s) || isempty(r) + return Integer(start - start + zero(s)) + end + stop = last(r) + if isless(s, zero(s)) + diff = start - stop + s = -s + else + diff = stop - start + end + a = Integer(div(diff, s)) + return a + one(a) +end + + +function length(r::AbstractUnitRange{T}) where T @_inline_meta - checked_add(checked_sub(last(r), first(r)), one(T)) + a = Integer(last(r) - first(r)) # even when isempty, by construction (with overflow) + return a + one(a) end -length(r::OneTo{T}) where {T<:Union{Int,Int64}} = T(r.stop) -length(r::AbstractUnitRange{T}) where {T<:Union{UInt,UInt64,UInt128}} = - r.stop < r.start ? zero(T) : checked_add(last(r) - first(r), one(T)) +length(r::OneTo) = Integer(r.stop - zero(r.stop)) +length(r::StepRangeLen) = r.len +length(r::LinRange) = r.len -# some special cases to favor default Int type -let smallint = (Int === Int64 ? - Union{Int8,UInt8,Int16,UInt16,Int32,UInt32} : - Union{Int8,UInt8,Int16,UInt16}) - global length - - function length(r::StepRange{<:smallint}) - isempty(r) && return Int(0) - div(Int(r.stop)+Int(r.step) - Int(r.start), Int(r.step)) +let bigints = Union{Int, UInt, Int64, UInt64, Int128, UInt128} + global length, checked_length + # compile optimization for which promote_type(T, Int) == T + length(r::OneTo{T}) where {T<:bigints} = r.stop + # slightly more accurate length and checked_length in extreme cases + # (near typemax) for types with known `unsigned` functions + function length(r::OrdinalRange{T}) where T<:bigints + s = step(r) + s == zero(s) && return zero(T) # unreachable, by construction, but avoids the error case here later + isempty(r) && return zero(T) + diff = last(r) - first(r) + # if |s| > 1, diff might have overflowed, but unsigned(diff)÷s should + # therefore still be valid (if the result is representable at all) + # n.b. !(s isa T) + if s isa Unsigned || -1 <= s <= 1 || s == -s + a = div(diff, s) + elseif s < 0 + a = div(unsigned(-diff), -s) % typeof(diff) + else + a = div(unsigned(diff), s) % typeof(diff) + end + return Integer(a) + one(a) + end + function checked_length(r::OrdinalRange{T}) where T<:bigints + s = step(r) + s == zero(s) && return zero(T) # unreachable, by construction, but avoids the error case here later + isempty(r) && return zero(T) + stop, start = last(r), first(r) + # n.b. !(s isa T) + if s > 1 + diff = stop - start + a = convert(T, div(unsigned(diff), s)) + elseif s < -1 + diff = start - stop + a = convert(T, div(unsigned(diff), -s)) + elseif s > 0 + a = div(checked_sub(stop, start), s) + else + a = div(checked_sub(start, stop), -s) + end + return checked_add(a, one(a)) end +end - length(r::AbstractUnitRange{<:smallint}) = Int(last(r)) - Int(first(r)) + 1 - length(r::OneTo{<:smallint}) = Int(r.stop) +# some special cases to favor default Int type +let smallints = (Int === Int64 ? + Union{Int8, UInt8, Int16, UInt16, Int32, UInt32} : + Union{Int8, UInt8, Int16, UInt16}) + global length, checked_length + # n.b. !(step isa T) + length(r::OrdinalRange{<:smallints}) = div(Int(last(r)) - Int(first(r)), step(r)) + 1 + length(r::AbstractUnitRange{<:smallints}) = Int(last(r)) - Int(first(r)) + 1 + length(r::OneTo{<:smallints}) = Int(r.stop) + checked_length(r::OrdinalRange{<:smallints}) = length(r) + checked_length(r::AbstractUnitRange{<:smallints}) = length(r) + checked_length(r::OneTo{<:smallints}) = length(r) end first(r::OrdinalRange{T}) where {T} = convert(T, r.start) @@ -1288,10 +1357,10 @@ See also [`mod1`](@ref). # Examples ```jldoctest -julia> mod(0, Base.OneTo(3)) +julia> mod(0, Base.OneTo(3)) # mod1(0, 3) 3 -julia> mod(3, 0:2) +julia> mod(3, 0:2) # mod(3, 3) 0 ``` diff --git a/base/subarray.jl b/base/subarray.jl index 32262058cb55b..89a4db4d65790 100644 --- a/base/subarray.jl +++ b/base/subarray.jl @@ -60,7 +60,7 @@ viewindexing(I::Tuple{Vararg{Any}}) = IndexCartesian() viewindexing(I::Tuple{AbstractArray, Vararg{Any}}) = IndexCartesian() # Simple utilities -size(V::SubArray) = (@_inline_meta; map(unsafe_length, axes(V))) +size(V::SubArray) = (@_inline_meta; map(length, axes(V))) similar(V::SubArray, T::Type, dims::Dims) = similar(V.parent, T, dims) @@ -362,7 +362,7 @@ compute_stride1(parent::AbstractArray, I::NTuple{N,Any}) where {N} = compute_stride1(s, inds, I::Tuple{}) = s compute_stride1(s, inds, I::Tuple{Vararg{ScalarIndex}}) = s compute_stride1(s, inds, I::Tuple{ScalarIndex, Vararg{Any}}) = - (@_inline_meta; compute_stride1(s*unsafe_length(inds[1]), tail(inds), tail(I))) + (@_inline_meta; compute_stride1(s*length(inds[1]), tail(inds), tail(I))) compute_stride1(s, inds, I::Tuple{AbstractRange, Vararg{Any}}) = s*step(I[1]) compute_stride1(s, inds, I::Tuple{Slice, Vararg{Any}}) = s compute_stride1(s, inds, I::Tuple{Any, Vararg{Any}}) = throw(ArgumentError("invalid strided index type $(typeof(I[1]))")) @@ -407,12 +407,12 @@ end function compute_linindex(f, s, IP::Tuple, I::Tuple{ScalarIndex, Vararg{Any}}) @_inline_meta Δi = I[1]-first(IP[1]) - compute_linindex(f + Δi*s, s*unsafe_length(IP[1]), tail(IP), tail(I)) + compute_linindex(f + Δi*s, s*length(IP[1]), tail(IP), tail(I)) end function compute_linindex(f, s, IP::Tuple, I::Tuple{Any, Vararg{Any}}) @_inline_meta Δi = first(I[1])-first(IP[1]) - compute_linindex(f + Δi*s, s*unsafe_length(IP[1]), tail(IP), tail(I)) + compute_linindex(f + Δi*s, s*length(IP[1]), tail(IP), tail(I)) end compute_linindex(f, s, IP::Tuple, I::Tuple{}) = f @@ -447,5 +447,5 @@ _indices_sub(::Real, I...) = (@_inline_meta; _indices_sub(I...)) _indices_sub() = () function _indices_sub(i1::AbstractArray, I...) @_inline_meta - (unsafe_indices(i1)..., _indices_sub(I...)...) + (axes(i1)..., _indices_sub(I...)...) end diff --git a/deps/checksums/Pkg-9914dabfd66813197e0c67e28b78cfe0b393fe88.tar.gz/md5 b/deps/checksums/Pkg-9914dabfd66813197e0c67e28b78cfe0b393fe88.tar.gz/md5 deleted file mode 100644 index 7e270d33db3d1..0000000000000 --- a/deps/checksums/Pkg-9914dabfd66813197e0c67e28b78cfe0b393fe88.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -91de1078abb41513423dfab4e0e7d406 diff --git a/deps/checksums/Pkg-9914dabfd66813197e0c67e28b78cfe0b393fe88.tar.gz/sha512 b/deps/checksums/Pkg-9914dabfd66813197e0c67e28b78cfe0b393fe88.tar.gz/sha512 deleted file mode 100644 index 3f6952477519a..0000000000000 --- a/deps/checksums/Pkg-9914dabfd66813197e0c67e28b78cfe0b393fe88.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -1a6a87e27839910829a322326c7aa52a66a3467850de9a4a17c19003c3b4a4de8507bbe19fddc5cc3bc40be8e3a8902af1c0458107b7f5d55213cf2c9cafad21 diff --git a/deps/checksums/Pkg-e476cd0c61e19b645cc0e32bb30f8e44f60001f7.tar.gz/md5 b/deps/checksums/Pkg-e476cd0c61e19b645cc0e32bb30f8e44f60001f7.tar.gz/md5 new file mode 100644 index 0000000000000..de586e869f6db --- /dev/null +++ b/deps/checksums/Pkg-e476cd0c61e19b645cc0e32bb30f8e44f60001f7.tar.gz/md5 @@ -0,0 +1 @@ +af31db7882d62388fcc3fccf81fe699c diff --git a/deps/checksums/Pkg-e476cd0c61e19b645cc0e32bb30f8e44f60001f7.tar.gz/sha512 b/deps/checksums/Pkg-e476cd0c61e19b645cc0e32bb30f8e44f60001f7.tar.gz/sha512 new file mode 100644 index 0000000000000..e6703d6365171 --- /dev/null +++ b/deps/checksums/Pkg-e476cd0c61e19b645cc0e32bb30f8e44f60001f7.tar.gz/sha512 @@ -0,0 +1 @@ +6982d5a2eebf2cd2a69b43a9ee8c54c3833f494f89aef9fab852d3b9b48a0724fa58b4f59fbc8dfd27406ae9c8e8c4beedba7f7a8dc8d128d435f6d9a8a03147 diff --git a/doc/src/base/collections.md b/doc/src/base/collections.md index f8ef12071171a..84e5702e0e396 100644 --- a/doc/src/base/collections.md +++ b/doc/src/base/collections.md @@ -66,6 +66,7 @@ Base.LinRange Base.isempty Base.empty! Base.length +Base.checked_length ``` Fully implemented by: diff --git a/doc/src/manual/functions.md b/doc/src/manual/functions.md index d8e68d535088f..5fbca52bbfaad 100644 --- a/doc/src/manual/functions.md +++ b/doc/src/manual/functions.md @@ -180,7 +180,7 @@ end ``` This is a *convention* in the sense that `nothing` is not a Julia keyword -but a only singleton object of type `Nothing`. +but only a singleton object of type `Nothing`. Also, you may notice that the `printx` function example above is contrived, because `println` already returns `nothing`, so that the `return` line is redundant. diff --git a/doc/src/manual/mathematical-operations.md b/doc/src/manual/mathematical-operations.md index 4d1ebc726fa6d..c285ed9abbabd 100644 --- a/doc/src/manual/mathematical-operations.md +++ b/doc/src/manual/mathematical-operations.md @@ -69,7 +69,7 @@ The following [Boolean operators](https://en.wikipedia.org/wiki/Boolean_algebra# | `x && y` | [short-circuiting and](@ref man-conditional-evaluation) | | `x \|\| y` | [short-circuiting or](@ref man-conditional-evaluation) | -Negation changes `true` to `false` and vice versa. The short-circuiting opeations are explained on the linked page. +Negation changes `true` to `false` and vice versa. The short-circuiting operations are explained on the linked page. Note that `Bool` is an integer type and all the usual promotion rules and numeric operators are also defined on it. diff --git a/src/ccall.cpp b/src/ccall.cpp index 15f51cf9d6897..c7853ccc2a8d8 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -493,7 +493,7 @@ static Value *julia_to_native( assert(!byRef); // don't expect any ABI to pass pointers by pointer return boxed(ctx, jvinfo); } - assert(jl_is_datatype(jlto) && julia_struct_has_layout((jl_datatype_t*)jlto)); + assert(jl_is_datatype(jlto) && jl_struct_try_layout((jl_datatype_t*)jlto)); typeassert_input(ctx, jvinfo, jlto, jlto_env, argn); if (!byRef) diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 2303ddb286971..f715abb3b490b 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -321,7 +321,7 @@ static size_t dereferenceable_size(jl_value_t *jt) // Array has at least this much data return sizeof(jl_array_t); } - else if (jl_is_datatype(jt) && ((jl_datatype_t*)jt)->layout) { + else if (jl_is_datatype(jt) && jl_struct_try_layout((jl_datatype_t*)jt)) { return jl_datatype_size(jt); } return 0; @@ -339,7 +339,7 @@ static unsigned julia_alignment(jl_value_t *jt) // and this is the guarantee we have for the GC bits return 16; } - assert(jl_is_datatype(jt) && ((jl_datatype_t*)jt)->layout); + assert(jl_is_datatype(jt) && jl_struct_try_layout((jl_datatype_t*)jt)); unsigned alignment = jl_datatype_align(jt); if (alignment > JL_HEAP_ALIGNMENT) return JL_HEAP_ALIGNMENT; @@ -555,16 +555,10 @@ static Type *bitstype_to_llvm(jl_value_t *bt, bool llvmcall = false) } static bool jl_type_hasptr(jl_value_t* typ) -{ // assumes that jl_stored_inline(typ) is true +{ // assumes that jl_stored_inline(typ) is true (and therefore that layout is defined) return jl_is_datatype(typ) && ((jl_datatype_t*)typ)->layout->npointers > 0; } -// return whether all concrete subtypes of this type have the same layout -static bool julia_struct_has_layout(jl_datatype_t *dt) -{ - return dt->layout || jl_has_fixed_layout(dt); -} - static unsigned jl_field_align(jl_datatype_t *dt, size_t i) { unsigned al = jl_field_offset(dt, i); @@ -588,11 +582,8 @@ static Type *_julia_struct_to_llvm(jl_codegen_params_t *ctx, jl_value_t *jt, boo bool isTuple = jl_is_tuple_type(jt); jl_svec_t *ftypes = jl_get_fieldtypes(jst); size_t i, ntypes = jl_svec_len(ftypes); - if (!julia_struct_has_layout(jst)) + if (!jl_struct_try_layout(jst)) return NULL; // caller should have checked jl_type_mappable_to_c already, but we'll be nice - if (jst->layout == NULL) - jl_compute_field_offsets(jst); - assert(jst->layout); if (ntypes == 0 || jl_datatype_nbits(jst) == 0) return T_void; Type *_struct_decl = NULL; @@ -1904,6 +1895,9 @@ static bool emit_getfield_unknownidx(jl_codectx_t &ctx, return true; } if (nfields == 1) { + if (jl_has_free_typevars(jl_field_type(stt, 0))) { + return false; + } (void)idx0(); *ret = emit_getfield_knownidx(ctx, strct, 0, stt, order); return true; diff --git a/src/codegen.cpp b/src/codegen.cpp index dc03d453d4578..3d8ad33516080 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -933,12 +933,12 @@ static MDNode *best_tbaa(jl_value_t *jt) { // note that this includes jl_isbits, although codegen should work regardless static bool jl_is_concrete_immutable(jl_value_t* t) { - return jl_is_immutable_datatype(t) && ((jl_datatype_t*)t)->layout; + return jl_is_immutable_datatype(t) && ((jl_datatype_t*)t)->isconcretetype; } static bool jl_is_pointerfree(jl_value_t* t) { - if (!jl_is_immutable_datatype(t)) + if (!jl_is_concrete_immutable(t)) return 0; const jl_datatype_layout_t *layout = ((jl_datatype_t*)t)->layout; return layout && layout->npointers == 0; @@ -3033,9 +3033,9 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, return true; } - if (jl_is_datatype(utt) && utt->layout) { + if (jl_is_datatype(utt) && jl_struct_try_layout(utt)) { ssize_t idx = jl_field_index(utt, name, 0); - if (idx != -1) { + if (idx != -1 && !jl_has_free_typevars(jl_field_type(utt, idx))) { *ret = emit_getfield_knownidx(ctx, obj, idx, utt, order); return true; } @@ -3063,14 +3063,16 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, } if (jl_is_datatype(utt)) { - if (jl_is_structtype(utt) && utt->layout) { + if (jl_struct_try_layout(utt)) { size_t nfields = jl_datatype_nfields(utt); // integer index size_t idx; if (fld.constant && (idx = jl_unbox_long(fld.constant) - 1) < nfields) { - // known index - *ret = emit_getfield_knownidx(ctx, obj, idx, utt, order); - return true; + if (!jl_has_free_typevars(jl_field_type(utt, idx))) { + // known index + *ret = emit_getfield_knownidx(ctx, obj, idx, utt, order); + return true; + } } else { // unknown index @@ -3115,8 +3117,6 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, } } } - // TODO: attempt better codegen for approximate types, if the types - // and offsets of some fields are independent of parameters. // TODO: generic getfield func with more efficient calling convention return false; } @@ -3155,7 +3155,7 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, } jl_datatype_t *uty = (jl_datatype_t*)jl_unwrap_unionall(obj.typ); - if (jl_is_structtype(uty) && uty->layout) { + if (jl_is_datatype(uty) && jl_struct_try_layout(uty)) { ssize_t idx = -1; if (fld.constant && fld.typ == (jl_value_t*)jl_symbol_type) { idx = jl_field_index(uty, (jl_sym_t*)fld.constant, 0); diff --git a/src/datatype.c b/src/datatype.c index 9bad2bc3d3bd8..a321b417d91c0 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -220,27 +220,36 @@ unsigned jl_special_vector_alignment(size_t nfields, jl_value_t *t) return next_power_of_two(size); } -STATIC_INLINE int jl_is_datatype_make_singleton(jl_datatype_t *d) +STATIC_INLINE int jl_is_datatype_make_singleton(jl_datatype_t *d) JL_NOTSAFEPOINT { return (!d->name->abstract && jl_datatype_size(d) == 0 && d != jl_symbol_type && d->name != jl_array_typename && d->isconcretetype && !d->name->mutabl); } -STATIC_INLINE void jl_maybe_allocate_singleton_instance(jl_datatype_t *st) +STATIC_INLINE void jl_maybe_allocate_singleton_instance(jl_datatype_t *st) JL_NOTSAFEPOINT { if (jl_is_datatype_make_singleton(st)) { // It's possible for st to already have an ->instance if it was redefined - if (!st->instance) { - jl_task_t *ct = jl_current_task; - st->instance = jl_gc_alloc(ct->ptls, 0, st); - jl_gc_wb(st, st->instance); - } + if (!st->instance) + st->instance = jl_gc_permobj(0, st); } } +// return whether all concrete subtypes of this type have the same layout +int jl_struct_try_layout(jl_datatype_t *dt) +{ + if (dt->layout) + return 1; + else if (!jl_has_fixed_layout(dt)) + return 0; + jl_compute_field_offsets(dt); + assert(dt->layout); + return 1; +} + int jl_datatype_isinlinealloc(jl_datatype_t *ty, int pointerfree) JL_NOTSAFEPOINT { - if (ty->name->mayinlinealloc && ty->layout) { + if (ty->name->mayinlinealloc && (ty->isconcretetype || ((jl_datatype_t*)jl_unwrap_unionall(ty->name->wrapper))->layout)) { // TODO: use jl_struct_try_layout(dt) (but it is a safepoint) if (ty->layout->npointers > 0) { if (pointerfree) return 0; @@ -348,9 +357,6 @@ void jl_compute_field_offsets(jl_datatype_t *st) if (st->name->wrapper == NULL) return; // we got called too early--we'll be back jl_datatype_t *w = (jl_datatype_t*)jl_unwrap_unionall(st->name->wrapper); - assert(st->types && w->types); - size_t i, nfields = jl_svec_len(st->types); - assert(st->name->n_uninitialized <= nfields); if (st == w && st->layout) { // this check allows us to force re-computation of the layout for some types during init st->layout = NULL; @@ -358,6 +364,7 @@ void jl_compute_field_offsets(jl_datatype_t *st) st->zeroinit = 0; st->has_concrete_subtype = 1; } + int isbitstype = st->isconcretetype && st->name->mayinlinealloc; // If layout doesn't depend on type parameters, it's stored in st->name->wrapper // and reused by all subtypes. if (w->layout) { @@ -365,11 +372,16 @@ void jl_compute_field_offsets(jl_datatype_t *st) st->size = w->size; st->zeroinit = w->zeroinit; st->has_concrete_subtype = w->has_concrete_subtype; - if (jl_is_layout_opaque(st->layout)) { // e.g. jl_array_typename - return; + if (!jl_is_layout_opaque(st->layout)) { // e.g. jl_array_typename + st->isbitstype = isbitstype && st->layout->npointers == 0; + jl_maybe_allocate_singleton_instance(st); } + return; } - else if (nfields == 0) { + assert(st->types && w->types); + size_t i, nfields = jl_svec_len(st->types); + assert(st->name->n_uninitialized <= nfields); + if (nfields == 0) { // if we have no fields, we can trivially skip the rest if (st == jl_symbol_type || st == jl_string_type) { // opaque layout - heap-allocated blob @@ -404,7 +416,6 @@ void jl_compute_field_offsets(jl_datatype_t *st) } } - int isbitstype = st->isconcretetype && st->name->mayinlinealloc; for (i = 0; isbitstype && i < nfields; i++) { jl_value_t *fld = jl_field_type(st, i); isbitstype = jl_isbits(fld); diff --git a/src/jltypes.c b/src/jltypes.c index f9f60f1227b9a..886abadb046e0 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -223,7 +223,9 @@ int jl_has_fixed_layout(jl_datatype_t *dt) { if (dt->layout || dt->isconcretetype) return 1; - if (jl_is_tuple_type(dt)) + if (dt->name->abstract) + return 0; + if (jl_is_tuple_type(dt) || jl_is_namedtuple_type(dt)) return 0; // TODO: relax more? jl_svec_t *types = jl_get_fieldtypes(dt); size_t i, l = jl_svec_len(types); @@ -242,9 +244,10 @@ int jl_type_mappable_to_c(jl_value_t *ty) assert(!jl_is_typevar(ty) && jl_is_type(ty)); if (jl_is_structtype(ty)) { jl_datatype_t *jst = (jl_datatype_t*)ty; - return jst->layout || jl_has_fixed_layout(jst); + return jl_has_fixed_layout(jst); } - if (jl_is_tuple_type(jl_unwrap_unionall(ty))) + ty = jl_unwrap_unionall(ty); + if (jl_is_tuple_type(ty) || jl_is_namedtuple_type(ty)) return 0; // TODO: relax some? return 1; // as boxed or primitive } @@ -1437,7 +1440,7 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value jl_gc_wb(ndt, ndt->parameters); ndt->types = NULL; // to be filled in below if (istuple) { - ndt->types = p; + ndt->types = p; // TODO: this may need to filter out certain types } else if (isnamedtuple) { jl_value_t *names_tup = jl_svecref(p, 0); @@ -1463,19 +1466,16 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value jl_gc_wb(ndt, ndt->types); } else { - ndt->types = jl_emptysvec; + ndt->types = jl_emptysvec; // XXX: this is essentially always false } } - ndt->size = 0; - jl_precompute_memoized_dt(ndt, cacheable); - - if (jl_is_primitivetype(dt)) { - ndt->size = dt->size; - ndt->layout = dt->layout; - ndt->isbitstype = ndt->isconcretetype; - } jl_datatype_t *primarydt = ((jl_datatype_t*)jl_unwrap_unionall(tn->wrapper)); + jl_precompute_memoized_dt(ndt, cacheable); + ndt->size = 0; + if (primarydt->layout) + jl_compute_field_offsets(ndt); + if (istuple || isnamedtuple) { ndt->super = jl_any_type; } @@ -1516,7 +1516,7 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value // leading to incorrect layouts and data races (#40050: the A{T} should be // an isbitstype singleton of size 0) if (cacheable) { - if (!jl_is_primitivetype(dt) && ndt->types != NULL && ndt->isconcretetype) { + if (dt->layout == NULL && !jl_is_primitivetype(dt) && ndt->types != NULL && ndt->isconcretetype) { jl_compute_field_offsets(ndt); } jl_cache_type_(ndt); diff --git a/src/julia_internal.h b/src/julia_internal.h index c22f8846de52a..e044ecc1bce61 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -388,7 +388,7 @@ STATIC_INLINE jl_gc_tracked_buffer_t *jl_gc_alloc_buf(jl_ptls_t ptls, size_t sz) return jl_gc_alloc(ptls, sz, (void*)jl_buff_tag); } -STATIC_INLINE jl_value_t *jl_gc_permobj(size_t sz, void *ty) +STATIC_INLINE jl_value_t *jl_gc_permobj(size_t sz, void *ty) JL_NOTSAFEPOINT { const size_t allocsz = sz + sizeof(jl_taggedvalue_t); unsigned align = (sz == 0 ? sizeof(void*) : (allocsz <= sizeof(void*) * 2 ? @@ -539,6 +539,7 @@ jl_datatype_t *jl_mk_builtin_func(jl_datatype_t *dt, const char *name, jl_fptr_a int jl_obviously_unequal(jl_value_t *a, jl_value_t *b); JL_DLLEXPORT jl_array_t *jl_find_free_typevars(jl_value_t *v); int jl_has_fixed_layout(jl_datatype_t *t); +int jl_struct_try_layout(jl_datatype_t *dt); int jl_type_mappable_to_c(jl_value_t *ty); jl_svec_t *jl_outer_unionall_vars(jl_value_t *u); jl_value_t *jl_type_intersection_env_s(jl_value_t *a, jl_value_t *b, jl_svec_t **penv, int *issubty); diff --git a/stdlib/Dates/test/ranges.jl b/stdlib/Dates/test/ranges.jl index 6eb6371376867..52416fc95ec0c 100644 --- a/stdlib/Dates/test/ranges.jl +++ b/stdlib/Dates/test/ranges.jl @@ -515,7 +515,7 @@ end @test length(Dates.Year(1):Dates.Year(1):Dates.Year(10)) == 10 @test length(Dates.Year(10):Dates.Year(-1):Dates.Year(1)) == 10 @test length(Dates.Year(10):Dates.Year(-2):Dates.Year(1)) == 5 -@test_throws OverflowError length(typemin(Dates.Year):Dates.Year(1):typemax(Dates.Year)) +@test length(typemin(Dates.Year):Dates.Year(1):typemax(Dates.Year)) == 0 # overflow @test_throws MethodError Dates.Date(0):Dates.DateTime(2000) @test_throws MethodError Dates.Date(0):Dates.Year(10) @test length(range(Dates.Date(2000), step=Dates.Day(1), length=366)) == 366 diff --git a/stdlib/Distributed/src/remotecall.jl b/stdlib/Distributed/src/remotecall.jl index 91e5de36736bd..6be7be2c8a91b 100644 --- a/stdlib/Distributed/src/remotecall.jl +++ b/stdlib/Distributed/src/remotecall.jl @@ -544,7 +544,7 @@ fetch_ref(rid, args...) = fetch(lookup_ref(rid).c, args...) Wait for and get a value from a [`RemoteChannel`](@ref). Exceptions raised are the same as for a [`Future`](@ref). Does not remove the item fetched. """ -fetch(r::RemoteChannel, args...) = call_on_owner(fetch_ref, r, args...) +fetch(r::RemoteChannel, args...) = call_on_owner(fetch_ref, r, args...)::eltype(r) isready(rv::RemoteValue, args...) = isready(rv.c, args...) @@ -623,7 +623,7 @@ end Fetch value(s) from a [`RemoteChannel`](@ref) `rr`, removing the value(s) in the process. """ -take!(rr::RemoteChannel, args...) = call_on_owner(take_ref, rr, myid(), args...) +take!(rr::RemoteChannel, args...) = call_on_owner(take_ref, rr, myid(), args...)::eltype(rr) # close and isopen are not supported on Future diff --git a/stdlib/LinearAlgebra/src/LinearAlgebra.jl b/stdlib/LinearAlgebra/src/LinearAlgebra.jl index 55caad1f82bee..f0f13776146d1 100644 --- a/stdlib/LinearAlgebra/src/LinearAlgebra.jl +++ b/stdlib/LinearAlgebra/src/LinearAlgebra.jl @@ -15,8 +15,8 @@ import Base: USE_BLAS64, abs, acos, acosh, acot, acoth, acsc, acsch, adjoint, as oneunit, parent, power_by_squaring, print_matrix, promote_rule, real, round, sec, sech, setindex!, show, similar, sin, sincos, sinh, size, sqrt, strides, stride, tan, tanh, transpose, trunc, typed_hcat, vec -using Base: IndexLinear, promote_op, promote_typeof, - @propagate_inbounds, @pure, reduce, typed_vcat, require_one_based_indexing, +using Base: IndexLinear, promote_eltype, promote_op, promote_typeof, + @propagate_inbounds, @pure, reduce, typed_hvcat, typed_vcat, require_one_based_indexing, splat using Base.Broadcast: Broadcasted, broadcasted import Libdl diff --git a/stdlib/LinearAlgebra/src/uniformscaling.jl b/stdlib/LinearAlgebra/src/uniformscaling.jl index 44ad5d98e99a4..bbcade43c5569 100644 --- a/stdlib/LinearAlgebra/src/uniformscaling.jl +++ b/stdlib/LinearAlgebra/src/uniformscaling.jl @@ -391,6 +391,7 @@ end # in A to matrices of type T and sizes given by n[k:end]. n is an array # so that the same promotion code can be used for hvcat. We pass the type T # so that we can re-use this code for sparse-matrix hcat etcetera. +promote_to_arrays_(n::Int, ::Type, a::Number) = a promote_to_arrays_(n::Int, ::Type{Matrix}, J::UniformScaling{T}) where {T} = copyto!(Matrix{T}(undef, n,n), J) promote_to_arrays_(n::Int, ::Type, A::AbstractVecOrMat) = A promote_to_arrays(n,k, ::Type) = () @@ -401,11 +402,11 @@ promote_to_arrays(n,k, ::Type{T}, A, B, C) where {T} = (promote_to_arrays_(n[k], T, A), promote_to_arrays_(n[k+1], T, B), promote_to_arrays_(n[k+2], T, C)) promote_to_arrays(n,k, ::Type{T}, A, B, Cs...) where {T} = (promote_to_arrays_(n[k], T, A), promote_to_arrays_(n[k+1], T, B), promote_to_arrays(n,k+2, T, Cs...)...) -promote_to_array_type(A::Tuple{Vararg{Union{AbstractVecOrMat,UniformScaling}}}) = Matrix +promote_to_array_type(A::Tuple{Vararg{Union{AbstractVecOrMat,UniformScaling,Number}}}) = Matrix for (f,dim,name) in ((:hcat,1,"rows"), (:vcat,2,"cols")) @eval begin - function $f(A::Union{AbstractVecOrMat,UniformScaling}...) + function $f(A::Union{AbstractVecOrMat,UniformScaling,Number}...) n = -1 for a in A if !isa(a, UniformScaling) @@ -418,13 +419,13 @@ for (f,dim,name) in ((:hcat,1,"rows"), (:vcat,2,"cols")) end end n == -1 && throw(ArgumentError($("$f of only UniformScaling objects cannot determine the matrix size"))) - return $f(promote_to_arrays(fill(n,length(A)),1, promote_to_array_type(A), A...)...) + return cat(promote_to_arrays(fill(n, length(A)), 1, promote_to_array_type(A), A...)..., dims=Val(3-$dim)) end end end -function hvcat(rows::Tuple{Vararg{Int}}, A::Union{AbstractVecOrMat,UniformScaling}...) +function hvcat(rows::Tuple{Vararg{Int}}, A::Union{AbstractVecOrMat,UniformScaling,Number}...) require_one_based_indexing(A...) nr = length(rows) sum(rows) == length(A) || throw(ArgumentError("mismatch between row sizes and number of arguments")) @@ -467,8 +468,8 @@ function hvcat(rows::Tuple{Vararg{Int}}, A::Union{AbstractVecOrMat,UniformScalin j = 0 for i = 1:nr if rows[i] > 0 && n[j+1] == -1 # this row consists entirely of UniformScalings - nci = nc ÷ rows[i] - nci * rows[i] != nc && throw(DimensionMismatch("indivisible UniformScaling sizes")) + nci, r = divrem(nc, rows[i]) + r != 0 && throw(DimensionMismatch("indivisible UniformScaling sizes")) for k = 1:rows[i] n[j+k] = nci end @@ -476,7 +477,18 @@ function hvcat(rows::Tuple{Vararg{Int}}, A::Union{AbstractVecOrMat,UniformScalin j += rows[i] end end - return hvcat(rows, promote_to_arrays(n,1, promote_to_array_type(A), A...)...) + Atyp = promote_to_array_type(A) + Amat = promote_to_arrays(n, 1, Atyp, A...) + # We have two methods for promote_to_array_type, one returning Matrix and + # another one returning SparseMatrixCSC (in SparseArrays.jl). In the dense + # case, we cannot call hvcat for the promoted UniformScalings because this + # causes a stack overflow. In the sparse case, however, we cannot call + # typed_hvcat because we need a sparse output. + if Atyp == Matrix + return typed_hvcat(promote_eltype(Amat...), rows, Amat...) + else + return hvcat(rows, Amat...) + end end ## Matrix construction from UniformScaling diff --git a/stdlib/LinearAlgebra/test/uniformscaling.jl b/stdlib/LinearAlgebra/test/uniformscaling.jl index 04d4d7d7e0c9e..edbb1789a366b 100644 --- a/stdlib/LinearAlgebra/test/uniformscaling.jl +++ b/stdlib/LinearAlgebra/test/uniformscaling.jl @@ -335,10 +335,19 @@ end B = T(rand(3,3)) C = T(rand(0,3)) D = T(rand(2,0)) + E = T(rand(1,3)) + F = T(rand(3,1)) + α = rand() @test (hcat(A, 2I))::T == hcat(A, Matrix(2I, 3, 3)) + @test (hcat(E, α))::T == hcat(E, [α]) + @test (hcat(E, α, 2I))::T == hcat(E, [α], fill(2, 1, 1)) @test (vcat(A, 2I))::T == vcat(A, Matrix(2I, 4, 4)) + @test (vcat(F, α))::T == vcat(F, [α]) + @test (vcat(F, α, 2I))::T == vcat(F, [α], fill(2, 1, 1)) @test (hcat(C, 2I))::T == C + @test_throws DimensionMismatch hcat(C, α) @test (vcat(D, 2I))::T == D + @test_throws DimensionMismatch vcat(D, α) @test (hcat(I, 3I, A, 2I))::T == hcat(Matrix(I, 3, 3), Matrix(3I, 3, 3), A, Matrix(2I, 3, 3)) @test (vcat(I, 3I, A, 2I))::T == vcat(Matrix(I, 4, 4), Matrix(3I, 4, 4), A, Matrix(2I, 4, 4)) @test (hvcat((2,1,2), B, 2I, I, 3I, 4I))::T == @@ -353,6 +362,9 @@ end hvcat((2,2,2), B, Matrix(2I, 3, 3), C, C, Matrix(3I, 3, 3), Matrix(4I, 3, 3)) @test hvcat((3,2,1), C, C, I, B ,3I, 2I)::T == hvcat((2,2,1), C, C, B, Matrix(3I,3,3), Matrix(2I,6,6)) + @test (hvcat((1,2), A, E, α))::T == hvcat((1,2), A, E, [α]) == hvcat((1,2), A, E, α*I) + @test (hvcat((2,2), α, E, F, 3I))::T == hvcat((2,2), [α], E, F, Matrix(3I, 3, 3)) + @test (hvcat((2,2), 3I, F, E, α))::T == hvcat((2,2), Matrix(3I, 3, 3), F, E, [α]) end end diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index ed4959ee4756e..296410c62e300 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,2 +1,2 @@ PKG_BRANCH = master -PKG_SHA1 = 9914dabfd66813197e0c67e28b78cfe0b393fe88 +PKG_SHA1 = e476cd0c61e19b645cc0e32bb30f8e44f60001f7 diff --git a/stdlib/SparseArrays/src/sparsevector.jl b/stdlib/SparseArrays/src/sparsevector.jl index dae26a77626b4..55ad738a7eb77 100644 --- a/stdlib/SparseArrays/src/sparsevector.jl +++ b/stdlib/SparseArrays/src/sparsevector.jl @@ -1083,23 +1083,26 @@ const _Triangular_DenseArrays{T,A<:Matrix} = LinearAlgebra.AbstractTriangular{T, const _Annotated_DenseArrays = Union{_Triangular_DenseArrays, _Symmetric_DenseArrays, _Hermitian_DenseArrays} const _Annotated_Typed_DenseArrays{T} = Union{_Triangular_DenseArrays{T}, _Symmetric_DenseArrays{T}, _Hermitian_DenseArrays{T}} -const _SparseConcatGroup = Union{Vector, Adjoint{<:Any,<:Vector}, Transpose{<:Any,<:Vector}, Matrix, _SparseConcatArrays, _Annotated_SparseConcatArrays, _Annotated_DenseArrays} -const _DenseConcatGroup = Union{Vector, Adjoint{<:Any,<:Vector}, Transpose{<:Any,<:Vector}, Matrix, _Annotated_DenseArrays} +const _SparseConcatGroup = Union{Number, Vector, Adjoint{<:Any,<:Vector}, Transpose{<:Any,<:Vector}, Matrix, _SparseConcatArrays, _Annotated_SparseConcatArrays, _Annotated_DenseArrays} +const _DenseConcatGroup = Union{Number, Vector, Adjoint{<:Any,<:Vector}, Transpose{<:Any,<:Vector}, Matrix, _Annotated_DenseArrays} const _TypedDenseConcatGroup{T} = Union{Vector{T}, Adjoint{T,Vector{T}}, Transpose{T,Vector{T}}, Matrix{T}, _Annotated_Typed_DenseArrays{T}} # Concatenations involving un/annotated sparse/special matrices/vectors should yield sparse arrays +_makesparse(x::Number) = x +_makesparse(x::AbstractArray) = SparseMatrixCSC(issparse(x) ? x : sparse(x)) + function Base._cat(dims, Xin::_SparseConcatGroup...) - X = map(x -> SparseMatrixCSC(issparse(x) ? x : sparse(x)), Xin) + X = map(_makesparse, Xin) T = promote_eltype(Xin...) Base.cat_t(T, X...; dims=dims) end function hcat(Xin::_SparseConcatGroup...) - X = map(x -> SparseMatrixCSC(issparse(x) ? x : sparse(x)), Xin) - hcat(X...) + X = map(_makesparse, Xin) + return cat(X..., dims=Val(2)) end function vcat(Xin::_SparseConcatGroup...) - X = map(x -> SparseMatrixCSC(issparse(x) ? x : sparse(x)), Xin) - vcat(X...) + X = map(_makesparse, Xin) + return cat(X..., dims=Val(1)) end hvcat(rows::Tuple{Vararg{Int}}, X::_SparseConcatGroup...) = vcat(_hvcat_rows(rows, X...)...) diff --git a/stdlib/libblastrampoline_jll/Project.toml b/stdlib/libblastrampoline_jll/Project.toml index 64f88602b862f..644107b9dac00 100644 --- a/stdlib/libblastrampoline_jll/Project.toml +++ b/stdlib/libblastrampoline_jll/Project.toml @@ -3,7 +3,6 @@ uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" version = "3.0.4+0" [deps] -Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" Artifacts = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" OpenBLAS_jll = "4536629a-c528-5b80-bd46-f80d51c5b363" diff --git a/test/compiler/codegen.jl b/test/compiler/codegen.jl index b8fa5536865bb..df29a339d5a85 100644 --- a/test/compiler/codegen.jl +++ b/test/compiler/codegen.jl @@ -580,3 +580,16 @@ g40612(a, b) = a[]|a[] === b[]|b[] @test g40612(Union{Bool,Missing}[missing], Union{Bool,Missing}[missing]) @test g40612(Union{Bool,Missing}[true], Union{Bool,Missing}[true]) @test g40612(Union{Bool,Missing}[false], Union{Bool,Missing}[false]) + +# issue #41438 +struct A41438{T} + x::Ptr{T} +end +struct B41438{T} + x::T +end +f41438(y) = y[].x +@test A41438.body.layout != C_NULL +@test B41438.body.layout === C_NULL +@test f41438(Ref{A41438}(A41438(C_NULL))) === C_NULL +@test f41438(Ref{B41438}(B41438(C_NULL))) === C_NULL diff --git a/test/ranges.jl b/test/ranges.jl index 783120c361bcd..b622fc05b3ca6 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -1,5 +1,7 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license +using Base.Checked: checked_length + @testset "range construction" begin @test_throws ArgumentError range(start=1, step=1, stop=2, length=10) @test_throws ArgumentError range(start=1, step=1, stop=10, length=11) @@ -267,22 +269,28 @@ end end end @testset "length" begin - @test length(.1:.1:.3) == 3 - @test length(1.1:1.1:3.3) == 3 - @test length(1.1:1.3:3) == 2 - @test length(1:1:1.8) == 1 - @test length(1:.2:2) == 6 - @test length(1.:.2:2.) == 6 - @test length(2:-.2:1) == 6 - @test length(2.:-.2:1.) == 6 - @test length(2:.2:1) == 0 + @test length(.1:.1:.3) == checked_length(.1:.1:.3) == 3 + @test length(1.1:1.1:3.3) == checked_length(1.1:1.1:3.3) == 3 + @test length(1.1:1.3:3) == checked_length(1.1:1.3:3) == 2 + @test length(1:1:1.8) == checked_length(1:1:1.8) == 1 + @test length(1:.2:2) == checked_length(1:.2:2) == 6 + @test length(1.:.2:2.) == checked_length(1.:.2:2.) == 6 + @test length(2:-.2:1) == checked_length(2:-.2:1) == 6 + @test length(2.:-.2:1.) == checked_length(2.:-.2:1.) == 6 + @test length(2:.2:1) == checked_length(2:.2:1) == 0 @test length(2.:.2:1.) == 0 - @test length(1:0) == 0 - @test length(0.0:-0.5) == 0 - @test length(1:2:0) == 0 - @test length(Char(0):Char(0x001fffff)) == 2097152 - @test length(typemax(UInt64)//one(UInt64):1:typemax(UInt64)//one(UInt64)) == 1 + @test length(1:0) == checked_length(1:0) == 0 + @test length(0.0:-0.5) == checked_length(0.0:-0.5) == 0 + @test length(1:2:0) == checked_length(1:2:0) == 0 + let r = Char(0):Char(0x001fffff) + @test length(r) == 2097152 + @test_throws MethodError checked_length(r) == 2097152 # this would work if checked_sub is defined on Char + end + let r = typemax(UInt64)//one(UInt64):1:typemax(UInt64)//one(UInt64) + @test length(r) == 1 + @test_throws MethodError checked_length(r) == 1 # this would work if checked_sub is defined on Rational + end end @testset "keys/values" begin keytype_is_correct(r) = keytype(r) == eltype(keys(r)) @@ -501,22 +509,41 @@ for a=AbstractRange[3:6, 0:2:10], b=AbstractRange[0:1, 2:-1:0] end # avoiding intermediate overflow (#5065) -@test length(1:4:typemax(Int)) == div(typemax(Int),4) + 1 +@test length(1:4:typemax(Int)) == div(typemax(Int), 4) + 1 +@test checked_length(1:4:typemax(Int)) == div(typemax(Int), 4) + 1 # computed exactly in modulo arithmetic @testset "overflow in length" begin - Tset = Int === Int64 ? (Int,UInt,Int128,UInt128) : - (Int,UInt,Int64,UInt64,Int128, UInt128) + Tset = Int === Int64 ? (Int, UInt, Int128, UInt128) : + (Int, UInt, Int64, UInt64, Int128, UInt128) for T in Tset - @test_throws OverflowError length(zero(T):typemax(T)) - @test_throws OverflowError length(typemin(T):typemax(T)) - @test_throws OverflowError length(zero(T):one(T):typemax(T)) - @test_throws OverflowError length(typemin(T):one(T):typemax(T)) + @test length(zero(T):typemax(T)) == typemin(T) + @test length(typemin(T):typemax(T)) == T(0) + @test length(zero(T):one(T):typemax(T)) == typemin(T) + @test length(typemin(T):one(T):typemax(T)) == T(0) + @test_throws OverflowError checked_length(zero(T):typemax(T)) + @test_throws OverflowError checked_length(typemin(T):typemax(T)) + @test_throws OverflowError checked_length(zero(T):one(T):typemax(T)) + @test_throws OverflowError checked_length(typemin(T):one(T):typemax(T)) + @test length(one(T):typemax(T)) == checked_length(one(T):typemax(T)) == typemax(T) if T <: Signed - @test_throws OverflowError length(-one(T):typemax(T)-one(T)) - @test_throws OverflowError length(-one(T):one(T):typemax(T)-one(T)) + @test length(-one(T):typemax(T)-one(T)) == typemin(T) + @test length(-one(T):one(T):typemax(T)-one(T)) == typemin(T) + @test length(-one(T):typemax(T)) == typemin(T) + T(1) + @test length(zero(T):typemin(T):typemin(T)) == 2 + @test length(one(T):typemin(T):typemin(T)) == 2 + @test length(typemax(T):typemin(T):typemin(T)) == 2 + @test length(-one(T):typemin(T):typemin(T)) == 1 + @test length(zero(T):typemin(T):zero(T)) == 1 + @test length(zero(T):typemin(T):one(T)) == 0 + @test_throws OverflowError checked_length(-one(T):typemax(T)-one(T)) + @test_throws OverflowError checked_length(-one(T):one(T):typemax(T)-one(T)) + @test_throws InexactError checked_length(zero(T):typemin(T):typemin(T)) == 2 # this can be improved + @test_throws InexactError checked_length(one(T):typemin(T):typemin(T)) == 2 # this can be improved + @test_throws InexactError checked_length(typemax(T):typemin(T):typemin(T)) == 2 # this can be improved end end end + @testset "loops involving typemin/typemax" begin n = 0 s = 0 @@ -871,32 +898,45 @@ end end # issue #2959 @test 1.0:1.5 == 1.0:1.0:1.5 == 1.0:1.0 -#@test 1.0:(.3-.1)/.1 == 1.0:2.0 +@test_broken 1.0:(.3-.1)/.1 == 1.0:2.0 # (this is just shy of 2.0) @testset "length with typemin/typemax" begin - let r = typemin(Int64):2:typemax(Int64), s = typemax(Int64):-2:typemin(Int64) + let r = typemin(Int64):2:typemax(Int64) @test first(r) == typemin(Int64) - @test last(r) == (typemax(Int64)-1) - @test_throws OverflowError length(r) - - @test first(s) == typemax(Int64) - @test last(s) == (typemin(Int64)+1) - @test_throws OverflowError length(s) + @test last(r) == typemax(Int64) - 1 + @test length(r) == typemin(Int64) + @test_throws OverflowError checked_length(r) + end + let r = typemax(Int64):-2:typemin(Int64) + @test first(r) == typemax(Int64) + @test last(r) == typemin(Int64) + 1 + @test length(r) == typemin(Int64) + @test_throws OverflowError checked_length(r) end - @test length(typemin(Int64):3:typemax(Int64)) == 6148914691236517206 - @test length(typemax(Int64):-3:typemin(Int64)) == 6148914691236517206 + let r = typemin(Int64):3:typemax(Int64) + @test length(r) == checked_length(r) == 6148914691236517206 + end + let r = typemax(Int64):-3:typemin(Int64) + @test length(r) == checked_length(r) == 6148914691236517206 + end for s in 3:100 - @test length(typemin(Int):s:typemax(Int)) == length(big(typemin(Int)):big(s):big(typemax(Int))) - @test length(typemax(Int):-s:typemin(Int)) == length(big(typemax(Int)):big(-s):big(typemin(Int))) + r = typemin(Int):s:typemax(Int) + br = big(typemin(Int)):big(s):big(typemax(Int)) + @test length(r) == checked_length(r) == length(br) + + r = typemax(Int):-s:typemin(Int) + br = big(typemax(Int)):big(-s):big(typemin(Int)) + @test length(r) == checked_length(r) == length(br) end - @test length(UInt(1):UInt(1):UInt(0)) == 0 - @test length(typemax(UInt):UInt(1):(typemax(UInt)-1)) == 0 - @test length(typemax(UInt):UInt(2):(typemax(UInt)-1)) == 0 - @test length((typemin(Int)+3):5:(typemin(Int)+1)) == 0 + @test length(UInt(1):UInt(1):UInt(0)) == checked_length(UInt(1):UInt(1):UInt(0)) == 0 + @test length(typemax(UInt):UInt(1):(typemax(UInt)-1)) == checked_length(typemax(UInt):UInt(1):(typemax(UInt)-1)) == 0 + @test length(typemax(UInt):UInt(2):(typemax(UInt)-1)) == checked_length(typemax(UInt):UInt(2):(typemax(UInt)-1)) == 0 + @test length((typemin(Int)+3):5:(typemin(Int)+1)) == checked_length((typemin(Int)+3):5:(typemin(Int)+1)) == 0 end + # issue #6364 @test length((1:64)*(pi/5)) == 64 @@ -966,7 +1006,8 @@ end (Int8,UInt8,Int16,UInt16,Int32,UInt32) : (Int8,UInt8,Int16,UInt16)) for T in smallint - @test length(typemin(T):typemax(T)) == 2^(8*sizeof(T)) + s = typemin(T):typemax(T) + @test length(s) == checked_length(s) == 2^(8*sizeof(T)) end end @@ -974,7 +1015,7 @@ end @test (0:1//2:2)[1:2:3] == 0:1//1:1 # issue #12278 -@test length(1:UInt(0)) == 0 +@test length(1:UInt(0)) == checked_length(1:UInt(0)) == 0 @testset "zip" begin i = 0 @@ -1047,17 +1088,14 @@ end @test reverse(LinRange{Int}(0,3,4)) === LinRange{Int}(3,0,4) @test reverse(LinRange{Float64}(0.,3.,4)) === LinRange{Float64}(3.,0.,4) end -@testset "Issue #11245" begin - io = IOBuffer() - show(io, range(1, stop=2, length=3)) - str = String(take!(io)) -# @test str == "range(1.0, stop=2.0, length=3)" - @test str == "1.0:0.5:2.0" -end + +# issue #11245 +@test repr(range(1, stop=2, length=3)) == "1.0:0.5:2.0" @testset "issue 10950" begin r = 1//2:3 @test length(r) == 3 + @test_throws MethodError checked_length(r) == 3 # this would work if checked_sub is defined on Rational i = 1 for x in r @test x == i//2 @@ -1268,19 +1306,22 @@ end end r = 1f8-10:1f8 - @test_broken argmin(f) == argmin(collect(r)) - @test_broken argmax(f) == argmax(collect(r)) + rv = collect(r) + @test argmin(r) == argmin(rv) == 1 + @test r[argmax(r)] == r[argmax(rv)] == 1f8 + @test argmax(r) == lastindex(r) + @test argmax(rv) != lastindex(r) end @testset "OneTo" begin let r = Base.OneTo(-5) @test isempty(r) - @test length(r) == 0 + @test length(r) == checked_length(r) == 0 @test size(r) == (0,) end let r = Base.OneTo(3) @test !isempty(r) - @test length(r) == 3 + @test length(r) == checked_length(r) == 3 @test size(r) == (3,) @test step(r) == 1 @test first(r) == 1 @@ -1377,7 +1418,7 @@ end @testset "issue #20520" begin r = range(1.3173739f0, stop=1.3173739f0, length=3) - @test length(r) == 3 + @test length(r) == checked_length(r) == 3 @test first(r) === 1.3173739f0 @test last(r) === 1.3173739f0 @test r[2] === 1.3173739f0 @@ -1401,7 +1442,7 @@ using .Main.Furlongs @testset "dimensional correctness" begin @test length(Vector(Furlong(2):Furlong(10))) == 9 - @test length(range(Furlong(2), length=9)) == 9 + @test length(range(Furlong(2), length=9)) == checked_length(range(Furlong(2), length=9)) == 9 @test Vector(Furlong(2):Furlong(1):Furlong(10)) == Vector(range(Furlong(2), step=Furlong(1), length=9)) == Furlong.(2:10) @test Vector(Furlong(1.0):Furlong(0.5):Furlong(10.0)) == Vector(Furlong(1):Furlong(0.5):Furlong(10)) == Furlong.(1:0.5:10) @@ -1496,15 +1537,18 @@ module NonStandardIntegerRangeTest using Test +using Base.Checked: checked_length +import Base.Checked: checked_add, checked_sub + struct Position <: Integer val::Int end -Position(x::Position) = x # to resolve ambiguity with boot.jl:728 +Position(x::Position) = x # to resolve ambiguity with boot.jl:770 struct Displacement <: Integer val::Int end -Displacement(x::Displacement) = x # to resolve ambiguity with boot.jl:728 +Displacement(x::Displacement) = x # to resolve ambiguity with boot.jl:770 Base.:-(x::Displacement) = Displacement(-x.val) Base.:-(x::Position, y::Position) = Displacement(x.val - y.val) @@ -1521,14 +1565,67 @@ Base.Unsigned(x::Displacement) = Unsigned(x.val) Base.rem(x::Displacement, y::Displacement) = Displacement(rem(x.val, y.val)) Base.div(x::Displacement, y::Displacement) = Displacement(div(x.val, y.val)) -# required for collect (summing lengths); alternatively, should unsafe_length return Int by default? +# required for collect (summing lengths); alternatively, should length return Int by default? Base.promote_rule(::Type{Displacement}, ::Type{Int}) = Int Base.convert(::Type{Int}, x::Displacement) = x.val +# Unsigned complement, for testing checked_length +struct UPosition <: Unsigned + val::UInt +end +UPosition(x::UPosition) = x # to resolve ambiguity with boot.jl:770 + +struct UDisplacement <: Unsigned + val::UInt +end +UDisplacement(x::UDisplacement) = x # to resolve ambiguity with boot.jl:770 + +Base.show(io::IO, x::Union{Position, UPosition, Displacement, UDisplacement}) = + # should use show if we were to do this properly (instead of just a test-helper) + print(io, typeof(x).name.name, "(", x.val, ")") + +Base.:-(x::UPosition, y::UPosition) = UDisplacement(x.val - y.val) +Base.:-(x::UPosition, y::UDisplacement) = UPosition(x.val - y.val) +Base.:+(x::UPosition, y::UDisplacement) = UPosition(x.val + y.val) +Base.:+(x::UDisplacement, y::Displacement) = UDisplacement(x.val + y.val) +Base.:+(x::UDisplacement, y::UDisplacement) = UDisplacement(x.val + y.val) +checked_sub(x::UPosition, y::UPosition) = UDisplacement(checked_sub(x.val, y.val)) +checked_sub(x::UPosition, y::UDisplacement) = UPosition(checked_sub(x.val, y.val)) +checked_sub(x::UDisplacement, y::UDisplacement) = UDisplacement(checked_sub(x.val, y.val)) +checked_add(x::UPosition, y::UDisplacement) = UPosition(checked_add(x.val, y.val)) +checked_add(x::UDisplacement, y::UDisplacement) = UDisplacement(checked_add(x.val, y.val)) +Base.:+(x::UPosition, y::Displacement) = UPosition(x.val + y.val) +Base.:(<=)(x::UPosition, y::UPosition) = x.val <= y.val +Base.:(<)(x::UPosition, y::UPosition) = x.val < y.val +Base.:(<)(x::UDisplacement, y::UDisplacement) = x.val < y.val + +# for StepRange computation: +Base.rem(x::UDisplacement, y::Displacement) = UDisplacement(rem(x.val, y.val)) +Base.div(x::UDisplacement, y::Displacement) = UDisplacement(div(x.val, y.val)) +Base.rem(x::UDisplacement, y::UDisplacement) = UDisplacement(rem(x.val, y.val)) +Base.div(x::UDisplacement, y::UDisplacement) = UDisplacement(div(x.val, y.val)) + +#Base.promote_rule(::Type{UDisplacement}, ::Type{Int}) = Int +#Base.convert(::Type{Int}, x::UDisplacement) = Int(x.val) + @testset "Ranges with nonstandard Integers" begin for (start, stop) in [(2, 4), (3, 3), (3, -2)] - @test collect(Position(start) : Position(stop)) == Position.(start : stop) - end + r = Position(start) : Position(stop) + @test length(r) === Displacement(stop >= start ? stop - start + 1 : 0) + start >= 0 && stop >= 0 && @test UDisplacement(length(r).val) === + checked_length(UPosition(start) : UPosition(stop)) === + checked_length(UPosition(start) : Displacement(1) : UPosition(stop)) === + checked_length(UPosition(start) : UDisplacement(1) : UPosition(stop)) + @test collect(r) == Position.(start : stop) + end + + @test length(UPosition(3):Displacement(7):UPosition(100)) === checked_length(UPosition(3):Displacement(7):UPosition(100)) === UDisplacement(14) + @test length(UPosition(100):Displacement(7):UPosition(3)) === checked_length(UPosition(100):Displacement(7):UPosition(3)) === UDisplacement(0) + @test length(UPosition(100):Displacement(-7):UPosition(3)) === checked_length(UPosition(100):Displacement(-7):UPosition(3)) === UDisplacement(14) + @test length(UPosition(3):Displacement(-7):UPosition(100)) === checked_length(UPosition(3):Displacement(-7):UPosition(100)) === UDisplacement(0) + @test_throws OverflowError checked_length(zero(UPosition):UPosition(typemax(UInt))) + @test_throws OverflowError checked_length(zero(UPosition):Displacement(1):UPosition(typemax(UInt))) + @test_throws OverflowError checked_length(UPosition(typemax(UInt)):Displacement(-1):zero(UPosition)) for start in [3, 0, -2] @test collect(Base.OneTo(Position(start))) == Position.(Base.OneTo(start)) @@ -1550,7 +1647,7 @@ end end # module NonStandardIntegerRangeTest @testset "Issue #26619" begin - @test length(UInt(100) : -1 : 1) === UInt(100) + @test length(UInt(100) : -1 : 1) == checked_length(UInt(100) : -1 : 1) === UInt(100) @test collect(UInt(5) : -1 : 3) == [UInt(5), UInt(4), UInt(3)] let r = UInt(5) : -2 : 2 diff --git a/test/testhelpers/Furlongs.jl b/test/testhelpers/Furlongs.jl index 73d23a39d2d7b..f3583a532215a 100644 --- a/test/testhelpers/Furlongs.jl +++ b/test/testhelpers/Furlongs.jl @@ -20,7 +20,7 @@ Furlong{p}(x::Furlong{q}) where {p,q} = (@assert(p==q); Furlong{p,typeof(x.val)} Furlong{p,T}(x::Furlong{q}) where {T,p,q} = (@assert(p==q); Furlong{p,T}(T(x.val))) Base.promote_type(::Type{Furlong{p,T}}, ::Type{Furlong{p,S}}) where {p,T,S} = - (Base.@_pure_meta; Furlong{p,promote_type(T,S)}) + Furlong{p,promote_type(T,S)} Base.one(x::Furlong{p,T}) where {p,T} = one(T) Base.one(::Type{Furlong{p,T}}) where {p,T} = one(T) @@ -38,14 +38,12 @@ Base.floatmax(::Type{Furlong{p,T}}) where {p,T<:AbstractFloat} = Furlong{p}(floa Base.floatmax(::Furlong{p,T}) where {p,T<:AbstractFloat} = floatmax(Furlong{p,T}) Base.conj(x::Furlong{p,T}) where {p,T} = Furlong{p,T}(conj(x.val)) -# convert Furlong exponent p to a canonical form. This -# is not type stable, but it doesn't matter since it is used -# at compile time (in generated functions), not runtime +# convert Furlong exponent p to a canonical form canonical_p(p) = isinteger(p) ? Int(p) : Rational{Int}(p) Base.abs(x::Furlong{p}) where {p} = Furlong{p}(abs(x.val)) -@generated Base.abs2(x::Furlong{p}) where {p} = :(Furlong{$(canonical_p(2p))}(abs2(x.val))) -@generated Base.inv(x::Furlong{p}) where {p} = :(Furlong{$(canonical_p(-p))}(inv(x.val))) +Base.abs2(x::Furlong{p}) where {p} = Furlong{canonical_p(2p)}(abs2(x.val)) +Base.inv(x::Furlong{p}) where {p} = Furlong{canonical_p(-p)}(inv(x.val)) for f in (:isfinite, :isnan, :isreal, :isinf) @eval Base.$f(x::Furlong) = $f(x.val) @@ -64,11 +62,10 @@ end for op in (:(==), :(!=), :<, :<=, :isless, :isequal) @eval $op(x::Furlong{p}, y::Furlong{p}) where {p} = $op(x.val, y.val) end -# generated functions to allow type inference of the value of the exponent: for (f,op) in ((:_plus,:+),(:_minus,:-),(:_times,:*),(:_div,://)) - @eval @generated function $f(v::T, ::Furlong{p}, ::Union{Furlong{q},Val{q}}) where {T,p,q} + @eval function $f(v::T, ::Furlong{p}, ::Union{Furlong{q},Val{q}}) where {T,p,q} s = $op(p, q) - :(Furlong{$(canonical_p(s)),$T}(v)) + Furlong{canonical_p(s),T}(v) end end for (op,eop) in ((:*, :_plus), (:/, :_minus), (://, :_minus), (:div, :_minus)) diff --git a/test/testhelpers/InfiniteArrays.jl b/test/testhelpers/InfiniteArrays.jl index bc6de1afc5503..d69130f4d726a 100644 --- a/test/testhelpers/InfiniteArrays.jl +++ b/test/testhelpers/InfiniteArrays.jl @@ -39,13 +39,11 @@ struct OneToInf{T<:Integer} <: AbstractUnitRange{T} end OneToInf() = OneToInf{Int}() Base.axes(r::OneToInf) = (r,) -Base.unsafe_indices(r::OneToInf) = (r,) -Base.unsafe_length(r::OneToInf) = Infinity() Base.size(r::OneToInf) = (Infinity(),) Base.first(r::OneToInf{T}) where {T} = oneunit(T) -Base.length(r::OneToInf{T}) where {T} = Infinity() -Base.last(r::OneToInf{T}) where {T} = Infinity() +Base.length(r::OneToInf) = Infinity() +Base.last(r::OneToInf) = Infinity() Base.unitrange(r::OneToInf) = r Base.oneto(::Infinity) = OneToInf() -end \ No newline at end of file +end diff --git a/test/testhelpers/OffsetArrays.jl b/test/testhelpers/OffsetArrays.jl index 67de3ef476652..2f650808a12f2 100644 --- a/test/testhelpers/OffsetArrays.jl +++ b/test/testhelpers/OffsetArrays.jl @@ -68,7 +68,6 @@ offset_coerce(::Type{I}, r::AbstractUnitRange) where I<:AbstractUnitRange{T} whe @inline Base.parent(r::IdOffsetRange) = r.parent @inline Base.axes(r::IdOffsetRange) = (Base.axes1(r),) @inline Base.axes1(r::IdOffsetRange) = IdOffsetRange(Base.axes1(r.parent), r.offset) -@inline Base.unsafe_indices(r::IdOffsetRange) = (r,) @inline Base.length(r::IdOffsetRange) = length(r.parent) Base.reduced_index(i::IdOffsetRange) = typeof(i)(first(i):first(i)) # Workaround for #92 on Julia < 1.4