Skip to content

Add check for 0-dimensional array arguments in hvncat and produce an error #41101

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 10 commits into from
10 changes: 7 additions & 3 deletions base/abstractarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2196,8 +2196,11 @@ function _typed_hvncat(::Type{T}, ::Val{N}, as...) where {T, N}
# into the destination
nd = N
Ndim = 0
for a ∈ as
for i ∈ eachindex(as)
a = as[i]
if a isa AbstractArray
length(a) > 0 ||
throw(ArgumentError("element $i has no elements"))
cat_size(a, N) == length(a) ||
throw(ArgumentError("all dimensions of elements other than $N must be of length 1"))
nd = max(nd, cat_ndims(a))
Expand Down Expand Up @@ -2299,8 +2302,9 @@ function _typed_hvncat(::Type{T}, shape::Tuple{Vararg{Tuple, N}}, row_first::Boo
shapepos = ones(Int, nd)

for i ∈ eachindex(as)
length(as[i]) > 0 || throw(ArgumentError("argument $i has no elements"))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see why this is a special case, i.e. why isn't it covered by the other check that the number of elements matches?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For example [zeros(1,2,0);;;7 8] doesn't need to be an error.

Copy link
Contributor Author

@BioTurboNick BioTurboNick Jun 10, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The former check is the length of as; this is the length of each element. Though I should probably be using cat_length here in case any of them are strings.

But you're right. This is a crude check. The dimension calculating algorithm right now just can't handle arrays with a zero dimension. e.g. [zeros(1, 0);;;7 8] creates an underfilled array. Seems better to make them error for now until I can address that.

wasstartblock = false
for d ∈ 1:N
for d ∈ 1:nd
ad = (d < 3 && row_first) ? (d == 1 ? 2 : 1) : d
dsize = cat_size(as[i], ad)
blockcounts[d] += 1
Expand All @@ -2313,7 +2317,7 @@ function _typed_hvncat(::Type{T}, shape::Tuple{Vararg{Tuple, N}}, row_first::Boo

wasstartblock = blockcounts[d] == 1 # remember for next dimension

isendblock = blockcounts[d] == shape[d][shapepos[d]]
isendblock = blockcounts[d] == shape[min(end, d)][shapepos[d]]
if isendblock
if outdims[d] == 0
outdims[d] = currentdims[d]
Expand Down
31 changes: 30 additions & 1 deletion test/abstractarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1389,7 +1389,36 @@ end
@test [v v;;; fill(v, 1, 2)] == fill(v, 1, 2, 2)
end

@test_throws BoundsError hvncat(((1, 2), (3,)), false, zeros(Int, 0, 0, 0), 7, 8)
# ensure that a shape with greater dimensionality than the concatenation expression is supported #41047
x = [1 2 ; 3 4 ;;;; 5 6 ; 7 8]
y = [x x]
@test [x x ;;; y] == [y ;;; x x]

# zero-length arrays as arguments aren't supported #41047
@test_throws ArgumentError hvncat(((1, 2), (3,)), false, zeros(Int, 0, 0, 0), 7, 8)
@test_throws ArgumentError hvncat(((1, 2), (3,)), false, zeros(Int, 0), 7, 8)

#41047 - ensure zero-length arrays do not cause a segfault
for v in (fill(1, 0), fill(1, 0, 0), fill(1, 0, 0, 0), fill(1, 0, 0, 0))
@test_throws ArgumentError [v ;; 1]
@test_throws ArgumentError [v ;;; 1]
@test_throws ArgumentError [v ;;;; 1]
@test_throws ArgumentError [1 ;; v]
@test_throws ArgumentError [1 ;;; v]
@test_throws ArgumentError [1 ;;;; v]
@test_throws ArgumentError [v ;; 1; 2]
@test_throws ArgumentError [v ;;; 1 2]
@test_throws ArgumentError [v ;;; 1;; 2]
@test_throws ArgumentError [v ;;;; 1;; 2]
@test_throws ArgumentError [1; 2 ;; v]
@test_throws ArgumentError [1 2 ;;; v]
@test_throws ArgumentError [1;; 2 ;;; v]
@test_throws ArgumentError [1;; 2 ;;;; v]
@test_throws ArgumentError [v; v;; 1; 2]
@test_throws ArgumentError [v v;;; 1 2]
@test_throws ArgumentError [v;; v;;; 1;; 2]
@test_throws ArgumentError [v;; v ;;;; 1;; 2]
end
end

@testset "keepat!" begin
Expand Down