From 5b893dd60c895ab4e2d5be0ff6781a20e7c5ad74 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Thu, 27 Jan 2022 07:10:44 -0500 Subject: [PATCH] deleteat!: Handle unassigned array elements (#43941) This function was erroring on unassigned array elements. This is the minimal fix to resolve that issue. I tried something more generic, but it turns out all of our Array code basically assumes that all elements are assigned. Even basic things like `==`, `hash`, etc. error for arrays with unassigned elements. I think we may want to strongly consider removing the ability to unassign elements in arrays in favor of Union{Nothing, T} arrays, which our existing code handles fine and should be able to be made to have equivalent performance. --- base/array.jl | 27 ++++++++++++++++++++++----- test/arrayops.jl | 5 +++++ 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/base/array.jl b/base/array.jl index 2fc1ccfdf7dda..807f99342e25f 100644 --- a/base/array.jl +++ b/base/array.jl @@ -1531,6 +1531,23 @@ deleteat!(a::Vector, inds::AbstractVector) = _deleteat!(a, to_indices(a, (inds,) struct Nowhere; end push!(::Nowhere, _) = nothing +_growend!(::Nowhere, _) = nothing + +@inline function _push_deleted!(dltd, a::Vector, ind) + if @inbounds isassigned(a, ind) + push!(dltd, @inbounds a[ind]) + else + _growend!(dltd, 1) + end +end + +@inline function _copy_item!(a::Vector, p, q) + if @inbounds isassigned(a, q) + @inbounds a[p] = a[q] + else + _unsetindex!(a, p) + end +end function _deleteat!(a::Vector, inds, dltd=Nowhere()) n = length(a) @@ -1538,7 +1555,7 @@ function _deleteat!(a::Vector, inds, dltd=Nowhere()) y === nothing && return a (p, s) = y checkbounds(a, p) - push!(dltd, @inbounds a[p]) + _push_deleted!(dltd, a, p) q = p+1 while true y = iterate(inds, s) @@ -1552,14 +1569,14 @@ function _deleteat!(a::Vector, inds, dltd=Nowhere()) end end while q < i - @inbounds a[p] = a[q] + _copy_item!(a, p, q) p += 1; q += 1 end - push!(dltd, @inbounds a[i]) + _push_deleted!(dltd, a, i) q = i+1 end while q <= n - @inbounds a[p] = a[q] + _copy_item!(a, p, q) p += 1; q += 1 end _deleteend!(a, n-p+1) @@ -1572,7 +1589,7 @@ function deleteat!(a::Vector, inds::AbstractVector{Bool}) length(inds) == n || throw(BoundsError(a, inds)) p = 1 for (q, i) in enumerate(inds) - @inbounds a[p] = a[q] + _copy_item!(a, p, q) p += !i end _deleteend!(a, n-p+1) diff --git a/test/arrayops.jl b/test/arrayops.jl index 1cb8d667bef36..84b0e7d259f45 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -1516,6 +1516,11 @@ end @test_throws BoundsError deleteat!([], [2]) @test deleteat!([], []) == [] @test deleteat!([], Bool[]) == [] + let a = Vector{Any}(undef, 2) + a[1] = 1 + @test isassigned(deleteat!(copy(a), [2]), 1) + @test !isassigned(deleteat!(copy(a), [1]), 1) + end end @testset "comprehensions" begin