Add push! implementation for AbstractArray depending only on resize! (#55470)
Fix #55459
In Julia 1.10, `push!` and `append!` would be functional for
`AbstractVector` implementations
if `resize!` and `setindex!` were defined.
As of #51903 by @vtjnash as in Julia 1.11.0-rc2, `append!` now depends
on an implementation of
`sizehint!` and `push!`. Since `push!` also depends on `append!`, a
stack
overflow situation can easily be created.
To avoid this, this pull request defines the following
* Add generic versions of `push!(a::AbstractVector, x)` which do not
depend on `append!`
* Add default implementation of `sizehint!` that is a no-op
The implementation of `push!(a::AbstractVector, x)` is a generic version
based on the implementation
of `push!(a::Vector, x)` without depending on internals.
# Example for SimpleArray
Consider the `SimpleArray` example from test/abstractarray.jl:
```julia
mutable struct SimpleArray{T} <: AbstractVector{T}
els::Vector{T}
end
Base.size(sa::SimpleArray) = size(sa.els)
Base.getindex(sa::SimpleArray, idx...) = getindex(sa.els, idx...)
Base.setindex!(sa::SimpleArray, v, idx...) = setindex!(sa.els, v, idx...)
Base.resize!(sa::SimpleArray, n) = resize!(sa.els, n)
Base.copy(sa::SimpleArray) = SimpleArray(copy(sa.els))
```
Note that `setindex!` and `resize!` are implemented for `SimpleArray`.
## Julia 1.10.4: push! is functional
On Julia 1.10.4, `push!` has a functional implementation for
`SimpleArray`
```julia-repl
julia> push!(SimpleArray{Int}(zeros(Int,5)), 6)
6-element SimpleArray{Int64}:
0
0
0
0
0
6
```
## Julia 1.11.0-rc2 and nightly: push! requires sizehint! and is prone
to stack overflow
Before this pull request, on Julia 1.11.0-rc2 and nightly, `push!` fails
for want of `sizehint!`.
```julia-repl
julia> push!(SimpleArray{Int}(zeros(Int,5)), 6)
ERROR: MethodError: no method matching sizehint!(::SimpleArray{Int64}, ::Int64)
The function `sizehint!` exists, but no method is defined for this combination of argument types.
...
```
After implementing `sizehint!`, `push!` still fails with a stack
overflow.
```julia-repl
julia> Base.sizehint!(a::SimpleArray, x) = a
julia> push!(SimpleArray{Int}(zeros(Int, 5)), 6)
Warning: detected a stack overflow; program state may be corrupted, so further execution might be unreliable.
ERROR: StackOverflowError:
Stacktrace:
[1] _append!
@ ./array.jl:1344 [inlined]
[2] append!
@ ./array.jl:1335 [inlined]
[3] push!(a::SimpleArray{Int64}, iter::Int64)
@ Base ./array.jl:1336
--- the above 3 lines are repeated 79982 more times ---
[239950] _append!
@ ./array.jl:1344 [inlined]
[239951] append!
@ ./array.jl:1335 [inlined]
```
This is because the new implementation of `append!` depends on `push!`.
## After this pull request, push! is functional.
After this pull request, there is a functional `push!` for `SimpleArray`
again as in Julia 1.10.4:
```julia-repl
julia> push!(SimpleArray{Int}(zeros(Int, 5), 6)
6-element SimpleArray{Int64}:
0
0
0
0
0
6
```