• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

JuliaLang / julia / #37477

pending completion
#37477

push

local

web-flow
Allow external lattice elements to properly union split (#49030)

Currently `MustAlias` is the only lattice element that is allowed
to widen to union types. However, there are others in external
packages. Expand the support we have for this in order to allow
union splitting of lattice elements.

Co-authored-by: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com>

26 of 26 new or added lines in 5 files covered. (100.0%)

71476 of 82705 relevant lines covered (86.42%)

34756248.54 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

81.18
/base/reshapedarray.jl
1
# This file is a part of Julia. License is MIT: https://julialang.org/license
2

3
using  Base.MultiplicativeInverses: SignedMultiplicativeInverse
4

5
struct ReshapedArray{T,N,P<:AbstractArray,MI<:Tuple{Vararg{SignedMultiplicativeInverse{Int}}}} <: AbstractArray{T,N}
6
    parent::P
125,137✔
7
    dims::NTuple{N,Int}
8
    mi::MI
9
end
10
ReshapedArray(parent::AbstractArray{T}, dims::NTuple{N,Int}, mi) where {T,N} = ReshapedArray{T,N,typeof(parent),typeof(mi)}(parent, dims, mi)
125,133✔
11

12
# IndexLinear ReshapedArray
13
const ReshapedArrayLF{T,N,P<:AbstractArray} = ReshapedArray{T,N,P,Tuple{}}
14

15
# Fast iteration on ReshapedArrays: use the parent iterator
16
struct ReshapedArrayIterator{I,M}
17
    iter::I
18
    mi::NTuple{M,SignedMultiplicativeInverse{Int}}
19
end
20
ReshapedArrayIterator(A::ReshapedArray) = _rs_iterator(parent(A), A.mi)
×
21
function _rs_iterator(P, mi::NTuple{M}) where M
×
22
    iter = eachindex(P)
×
23
    ReshapedArrayIterator{typeof(iter),M}(iter, mi)
×
24
end
25

26
struct ReshapedIndex{T}
27
    parentindex::T
28
end
29

30
# eachindex(A::ReshapedArray) = ReshapedArrayIterator(A)  # TODO: uncomment this line
31
@inline function iterate(R::ReshapedArrayIterator, i...)
×
32
    item, inext = iterate(R.iter, i...)
×
33
    ReshapedIndex(item), inext
×
34
end
35
length(R::ReshapedArrayIterator) = length(R.iter)
×
36
eltype(::Type{<:ReshapedArrayIterator{I}}) where {I} = @isdefined(I) ? ReshapedIndex{eltype(I)} : Any
×
37

38
## reshape(::Array, ::Dims) returns an Array, except for isbitsunion eltypes (issue #28611)
39
# reshaping to same # of dimensions
40
function reshape(a::Array{T,M}, dims::NTuple{N,Int}) where {T,N,M}
17,247✔
41
    throw_dmrsa(dims, len) =
17,247✔
42
        throw(DimensionMismatch("new dimensions $(dims) must be consistent with array size $len"))
43

44
    if prod(dims) != length(a)
17,247✔
45
        throw_dmrsa(dims, length(a))
×
46
    end
47
    isbitsunion(T) && return ReshapedArray(a, dims, ())
17,247✔
48
    if N == M && dims == size(a)
21,441✔
49
        return a
4,291✔
50
    end
51
    ccall(:jl_reshape_array, Array{T,N}, (Any, Any, Any), Array{T,N}, a, dims)
12,916✔
52
end
53

54
"""
55
    reshape(A, dims...) -> AbstractArray
56
    reshape(A, dims) -> AbstractArray
57

58
Return an array with the same data as `A`, but with different
59
dimension sizes or number of dimensions. The two arrays share the same
60
underlying data, so that the result is mutable if and only if `A` is
61
mutable, and setting elements of one alters the values of the other.
62

63
The new dimensions may be specified either as a list of arguments or
64
as a shape tuple. At most one dimension may be specified with a `:`,
65
in which case its length is computed such that its product with all
66
the specified dimensions is equal to the length of the original array
67
`A`. The total number of elements must not change.
68

69
# Examples
70
```jldoctest
71
julia> A = Vector(1:16)
72
16-element Vector{Int64}:
73
  1
74
  2
75
  3
76
  4
77
  5
78
  6
79
  7
80
  8
81
  9
82
 10
83
 11
84
 12
85
 13
86
 14
87
 15
88
 16
89

90
julia> reshape(A, (4, 4))
91
4×4 Matrix{Int64}:
92
 1  5   9  13
93
 2  6  10  14
94
 3  7  11  15
95
 4  8  12  16
96

97
julia> reshape(A, 2, :)
98
2×8 Matrix{Int64}:
99
 1  3  5  7   9  11  13  15
100
 2  4  6  8  10  12  14  16
101

102
julia> reshape(1:6, 2, 3)
103
2×3 reshape(::UnitRange{Int64}, 2, 3) with eltype Int64:
104
 1  3  5
105
 2  4  6
106
```
107
"""
108
reshape
109

110
reshape(parent::AbstractArray, dims::IntOrInd...) = reshape(parent, dims)
1✔
111
reshape(parent::AbstractArray, shp::Tuple{Union{Integer,OneTo}, Vararg{Union{Integer,OneTo}}}) = reshape(parent, to_shape(shp))
2,159✔
112
reshape(parent::AbstractArray, dims::Dims)        = _reshape(parent, dims)
6,981✔
113

114
# Allow missing dimensions with Colon():
115
reshape(parent::AbstractVector, ::Colon) = parent
2✔
116
reshape(parent::AbstractVector, ::Tuple{Colon}) = parent
2✔
117
reshape(parent::AbstractArray, dims::Int...) = reshape(parent, dims)
14,202✔
118
reshape(parent::AbstractArray, dims::Union{Int,Colon}...) = reshape(parent, dims)
52✔
119
reshape(parent::AbstractArray, dims::Tuple{Vararg{Union{Int,Colon}}}) = reshape(parent, _reshape_uncolon(parent, dims))
55✔
120
@inline function _reshape_uncolon(A, dims)
54✔
121
    @noinline throw1(dims) = throw(DimensionMismatch(string("new dimensions $(dims) ",
54✔
122
        "may have at most one omitted dimension specified by `Colon()`")))
123
    @noinline throw2(A, dims) = throw(DimensionMismatch(string("array size $(length(A)) ",
54✔
124
        "must be divisible by the product of the new dimensions $dims")))
125
    pre = _before_colon(dims...)
54✔
126
    post = _after_colon(dims...)
54✔
127
    _any_colon(post...) && throw1(dims)
54✔
128
    sz, remainder = divrem(length(A), prod(pre)*prod(post))
54✔
129
    remainder == 0 || throw2(A, dims)
54✔
130
    (pre..., Int(sz), post...)
54✔
131
end
132
@inline _any_colon() = false
54✔
133
@inline _any_colon(dim::Colon, tail...) = true
×
134
@inline _any_colon(dim::Any, tail...) = _any_colon(tail...)
25✔
135
@inline _before_colon(dim::Any, tail...) = (dim, _before_colon(tail...)...)
8✔
136
@inline _before_colon(dim::Colon, tail...) = ()
54✔
137
@inline _after_colon(dim::Any, tail...) =  _after_colon(tail...)
8✔
138
@inline _after_colon(dim::Colon, tail...) = tail
54✔
139

140
reshape(parent::AbstractArray{T,N}, ndims::Val{N}) where {T,N} = parent
325,249✔
141
function reshape(parent::AbstractArray, ndims::Val{N}) where N
2,093✔
142
    reshape(parent, rdims(Val(N), axes(parent)))
2,093✔
143
end
144

145
# Move elements from inds to out until out reaches the desired
146
# dimensionality N, either filling with OneTo(1) or collapsing the
147
# product of trailing dims into the last element
148
rdims_trailing(l, inds...) = length(l) * rdims_trailing(inds...)
2,100✔
149
rdims_trailing(l) = length(l)
1,697✔
150
rdims(out::Val{N}, inds::Tuple) where {N} = rdims(ntuple(Returns(OneTo(1)), Val(N)), inds)
2,093✔
151
rdims(out::Tuple{}, inds::Tuple{}) = () # N == 0, M == 0
×
152
rdims(out::Tuple{}, inds::Tuple{Any}) = ()
×
153
rdims(out::Tuple{}, inds::NTuple{M,Any}) where {M} = ()
×
154
rdims(out::Tuple{Any}, inds::Tuple{}) = out # N == 1, M == 0
388✔
155
rdims(out::NTuple{N,Any}, inds::Tuple{}) where {N} = out # N > 1, M == 0
8✔
156
rdims(out::Tuple{Any}, inds::Tuple{Any}) = inds # N == 1, M == 1
×
157
rdims(out::Tuple{Any}, inds::NTuple{M,Any}) where {M} = (oneto(rdims_trailing(inds...)),) # N == 1, M > 1
1,697✔
158
rdims(out::NTuple{N,Any}, inds::NTuple{N,Any}) where {N} = inds # N > 1, M == N
×
159
rdims(out::NTuple{N,Any}, inds::NTuple{M,Any}) where {N,M} = (first(inds), rdims(tail(out), tail(inds))...) # N > 1, M > 1, M != N
432✔
160

161

162
# _reshape on Array returns an Array
163
_reshape(parent::Vector, dims::Dims{1}) = parent
×
164
_reshape(parent::Array, dims::Dims{1}) = reshape(parent, dims)
×
165
_reshape(parent::Array, dims::Dims) = reshape(parent, dims)
×
166

167
# When reshaping Vector->Vector, don't wrap with a ReshapedArray
168
function _reshape(v::AbstractVector, dims::Dims{1})
27✔
169
    require_one_based_indexing(v)
27✔
170
    len = dims[1]
27✔
171
    len == length(v) || _throw_dmrs(length(v), "length", len)
27✔
172
    v
27✔
173
end
174
# General reshape
175
function _reshape(parent::AbstractArray, dims::Dims)
6,949✔
176
    n = length(parent)
6,949✔
177
    prod(dims) == n || _throw_dmrs(n, "size", dims)
6,960✔
178
    __reshape((parent, IndexStyle(parent)), dims)
6,938✔
179
end
180

181
@noinline function _throw_dmrs(n, str, dims)
11✔
182
    throw(DimensionMismatch("parent has $n elements, which is incompatible with $str $dims"))
11✔
183
end
184

185
# Reshaping a ReshapedArray
186
_reshape(v::ReshapedArray{<:Any,1}, dims::Dims{1}) = _reshape(v.parent, dims)
×
187
_reshape(R::ReshapedArray, dims::Dims) = _reshape(R.parent, dims)
21✔
188

189
function __reshape(p::Tuple{AbstractArray,IndexStyle}, dims::Dims)
2,663✔
190
    parent = p[1]
2,663✔
191
    strds = front(size_to_strides(map(length, axes(parent))..., 1))
2,663✔
192
    strds1 = map(s->max(1,Int(s)), strds)  # for resizing empty arrays
5,295✔
193
    mi = map(SignedMultiplicativeInverse, strds1)
2,663✔
194
    ReshapedArray(parent, dims, reverse(mi))
2,663✔
195
end
196

197
function __reshape(p::Tuple{AbstractArray{<:Any,0},IndexCartesian}, dims::Dims)
1✔
198
    parent = p[1]
1✔
199
    ReshapedArray(parent, dims, ())
1✔
200
end
201

202
function __reshape(p::Tuple{AbstractArray,IndexLinear}, dims::Dims)
4,274✔
203
    parent = p[1]
4,274✔
204
    ReshapedArray(parent, dims, ())
4,274✔
205
end
206

207
size(A::ReshapedArray) = A.dims
1,932,554✔
208
length(A::ReshapedArray) = length(parent(A))
4,044,250✔
209
similar(A::ReshapedArray, eltype::Type, dims::Dims) = similar(parent(A), eltype, dims)
1,010✔
210
IndexStyle(::Type{<:ReshapedArrayLF}) = IndexLinear()
22,197✔
211
parent(A::ReshapedArray) = A.parent
8,718,637✔
212
parentindices(A::ReshapedArray) = map(oneto, size(parent(A)))
×
213
reinterpret(::Type{T}, A::ReshapedArray, dims::Dims) where {T} = reinterpret(T, parent(A), dims)
×
214
elsize(::Type{<:ReshapedArray{<:Any,<:Any,P}}) where {P} = elsize(P)
6,610✔
215

216
unaliascopy(A::ReshapedArray) = typeof(A)(unaliascopy(A.parent), A.dims, A.mi)
4✔
217
dataids(A::ReshapedArray) = dataids(A.parent)
5,093✔
218

219
@inline ind2sub_rs(ax, ::Tuple{}, i::Int) = (i,)
72,310✔
220
@inline ind2sub_rs(ax, strds, i) = _ind2sub_rs(ax, strds, i - 1)
697,729✔
221
@inline _ind2sub_rs(ax, ::Tuple{}, ind) = (ind + first(ax[end]),)
697,729✔
222
@inline function _ind2sub_rs(ax, strds, ind)
1,261,755✔
223
    d, r = divrem(ind, strds[1])
1,261,755✔
224
    (_ind2sub_rs(front(ax), tail(strds), r)..., d + first(ax[end]))
1,261,755✔
225
end
226
offset_if_vec(i::Integer, axs::Tuple{<:AbstractUnitRange}) = i + first(axs[1]) - 1
62,805✔
227
offset_if_vec(i::Integer, axs::Tuple) = i
576,732✔
228

229
@inline function getindex(A::ReshapedArrayLF, index::Int)
4,025,399✔
230
    @boundscheck checkbounds(A, index)
4,025,399✔
231
    @inbounds ret = parent(A)[index]
4,025,999✔
232
    ret
4,025,399✔
233
end
234
@inline function getindex(A::ReshapedArray{T,N}, indices::Vararg{Int,N}) where {T,N}
638,077✔
235
    @boundscheck checkbounds(A, indices...)
638,079✔
236
    _unsafe_getindex(A, indices...)
639,764✔
237
end
238
@inline function getindex(A::ReshapedArray, index::ReshapedIndex)
×
239
    @boundscheck checkbounds(parent(A), index.parentindex)
×
240
    @inbounds ret = parent(A)[index.parentindex]
×
241
    ret
×
242
end
243

244
@inline function _unsafe_getindex(A::ReshapedArray{T,N}, indices::Vararg{Int,N}) where {T,N}
638,075✔
245
    axp = axes(A.parent)
638,075✔
246
    i = offset_if_vec(_sub2ind(size(A), indices...), axp)
638,075✔
247
    I = ind2sub_rs(axp, A.mi, i)
638,075✔
248
    _unsafe_getindex_rs(parent(A), I)
639,764✔
249
end
250
@inline _unsafe_getindex_rs(A, i::Integer) = (@inbounds ret = A[i]; ret)
×
251
@inline _unsafe_getindex_rs(A, I) = (@inbounds ret = A[I...]; ret)
1,277,839✔
252

253
@inline function setindex!(A::ReshapedArrayLF, val, index::Int)
240✔
254
    @boundscheck checkbounds(A, index)
240✔
255
    @inbounds parent(A)[index] = val
240✔
256
    val
240✔
257
end
258
@inline function setindex!(A::ReshapedArray{T,N}, val, indices::Vararg{Int,N}) where {T,N}
1,462✔
259
    @boundscheck checkbounds(A, indices...)
1,462✔
260
    _unsafe_setindex!(A, val, indices...)
1,462✔
261
end
262
@inline function setindex!(A::ReshapedArray, val, index::ReshapedIndex)
×
263
    @boundscheck checkbounds(parent(A), index.parentindex)
×
264
    @inbounds parent(A)[index.parentindex] = val
×
265
    val
×
266
end
267

268
@inline function _unsafe_setindex!(A::ReshapedArray{T,N}, val, indices::Vararg{Int,N}) where {T,N}
1,462✔
269
    axp = axes(A.parent)
1,462✔
270
    i = offset_if_vec(_sub2ind(size(A), indices...), axp)
1,462✔
271
    @inbounds parent(A)[ind2sub_rs(axes(A.parent), A.mi, i)...] = val
1,462✔
272
    val
1,462✔
273
end
274

275
# helpful error message for a common failure case
276
const ReshapedRange{T,N,A<:AbstractRange} = ReshapedArray{T,N,A,Tuple{}}
277
setindex!(A::ReshapedRange, val, index::Int) = _rs_setindex!_err()
×
278
setindex!(A::ReshapedRange{T,N}, val, indices::Vararg{Int,N}) where {T,N} = _rs_setindex!_err()
×
279
setindex!(A::ReshapedRange, val, index::ReshapedIndex) = _rs_setindex!_err()
×
280

281
@noinline _rs_setindex!_err() = error("indexed assignment fails for a reshaped range; consider calling collect")
×
282

283
unsafe_convert(::Type{Ptr{T}}, a::ReshapedArray{T}) where {T} = unsafe_convert(Ptr{T}, parent(a))
6,642✔
284

285
# Add a few handy specializations to further speed up views of reshaped ranges
286
const ReshapedUnitRange{T,N,A<:AbstractUnitRange} = ReshapedArray{T,N,A,Tuple{}}
287
viewindexing(I::Tuple{Slice, ReshapedUnitRange, Vararg{ScalarIndex}}) = IndexLinear()
2✔
288
viewindexing(I::Tuple{ReshapedRange, Vararg{ScalarIndex}}) = IndexLinear()
5✔
289
compute_stride1(s, inds, I::Tuple{ReshapedRange, Vararg{Any}}) = s*step(I[1].parent)
5✔
290
compute_offset1(parent::AbstractVector, stride1::Integer, I::Tuple{ReshapedRange}) =
1✔
291
    (@inline; first(I[1]) - first(axes1(I[1]))*stride1)
1✔
292
substrides(strds::NTuple{N,Int}, I::Tuple{ReshapedUnitRange, Vararg{Any}}) where N =
144✔
293
    (size_to_strides(strds[1], size(I[1])...)..., substrides(tail(strds), tail(I))...)
294
unsafe_convert(::Type{Ptr{T}}, V::SubArray{T,N,P,<:Tuple{Vararg{Union{RangeIndex,ReshapedUnitRange}}}}) where {T,N,P} =
1✔
295
    unsafe_convert(Ptr{T}, V.parent) + (first_index(V)-1)*sizeof(T)
296

297

298
_checkcontiguous(::Type{Bool}, A::AbstractArray) = false
7,157✔
299
# `strides(A::DenseArray)` calls `size_to_strides` by default.
300
# Thus it's OK to assume all `DenseArray`s are contiguously stored.
301
_checkcontiguous(::Type{Bool}, A::DenseArray) = true
56,446✔
302
_checkcontiguous(::Type{Bool}, A::ReshapedArray) = _checkcontiguous(Bool, parent(A))
1,551✔
303
_checkcontiguous(::Type{Bool}, A::FastContiguousSubArray) = _checkcontiguous(Bool, parent(A))
3,022✔
304

305
function strides(a::ReshapedArray)
1,487✔
306
    _checkcontiguous(Bool, a) && return size_to_strides(1, size(a)...)
1,487✔
307
    apsz::Dims = size(a.parent)
1,465✔
308
    apst::Dims = strides(a.parent)
1,465✔
309
    msz, mst, n = merge_adjacent_dim(apsz, apst) # Try to perform "lazy" reshape
1,465✔
310
    n == ndims(a.parent) && return size_to_strides(mst, size(a)...) # Parent is stridevector like
1,465✔
311
    return _reshaped_strides(size(a), 1, msz, mst, n, apsz, apst)
581✔
312
end
313

314
function _reshaped_strides(::Dims{0}, reshaped::Int, msz::Int, ::Int, ::Int, ::Dims, ::Dims)
581✔
315
    reshaped == msz && return ()
581✔
316
    throw(ArgumentError("Input is not strided."))
5✔
317
end
318
function _reshaped_strides(sz::Dims, reshaped::Int, msz::Int, mst::Int, n::Int, apsz::Dims, apst::Dims)
2,924✔
319
    st = reshaped * mst
2,924✔
320
    reshaped = reshaped * sz[1]
2,924✔
321
    if length(sz) > 1 && reshaped == msz && sz[2] != 1
2,924✔
322
        msz, mst, n = merge_adjacent_dim(apsz, apst, n + 1)
576✔
323
        reshaped = 1
576✔
324
    end
325
    sts = _reshaped_strides(tail(sz), reshaped, msz, mst, n, apsz, apst)
2,937✔
326
    return (st, sts...)
2,908✔
327
end
328

329
merge_adjacent_dim(::Dims{0}, ::Dims{0}) = 1, 1, 0
1✔
330
merge_adjacent_dim(apsz::Dims{1}, apst::Dims{1}) = apsz[1], apst[1], 1
3,348✔
331
function merge_adjacent_dim(apsz::Dims{N}, apst::Dims{N}, n::Int = 1) where {N}
1,997✔
332
    sz, st = apsz[n], apst[n]
1,980✔
333
    while n < N
1,415✔
334
        szₙ, stₙ = apsz[n+1], apst[n+1]
720✔
335
        if sz == 1
720✔
336
            sz, st = szₙ, stₙ
3✔
337
        elseif stₙ == st * sz || szₙ == 1
1,317✔
338
            sz *= szₙ
134✔
339
        else
340
            break
583✔
341
        end
342
        n += 1
137✔
343
    end
137✔
344
    return sz, st, n
1,278✔
345
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc