fix bounds checking and unaliasing of copyto! for PermutedDimsArray#61554
fix bounds checking and unaliasing of copyto! for PermutedDimsArray#61554adienes wants to merge 3 commits into
copyto! for PermutedDimsArray#61554Conversation
|
bump |
|
found and fixed another bug on master I think these are quite "serious" bugs (relative to the frequency with with people directly call |
|
there is another inconsistency on master that this PR does not fix but I think this is best left for another PR. it also touches on #45430 . I am experimenting with a more general refactor to |
| end | ||
|
|
||
| function Base.copyto!(dest::PermutedDimsArray{T,N}, src::AbstractArray{T,N}) where {T,N} | ||
| function Base.copyto!(dest::PermutedDimsArray{<:Any,N}, src::AbstractArray{<:Any,N}) where {N} |
There was a problem hiding this comment.
| function Base.copyto!(dest::PermutedDimsArray{<:Any,N}, src::AbstractArray{<:Any,N}) where {N} | |
| function Base.copyto!(dest::PermutedDimsArray, src::AbstractArray) |
This is the signature you deleted; I think checkbounds should behave as we want in the dim-mismatch case, yeah?
There was a problem hiding this comment.
the problem is that checkbounds will accept just same-length when there is a dimension mismatch
julia> P = PermutedDimsArray(zeros(2, 2), (2, 1));
julia> checkbounds(Bool, P, 1:4)
truebut _copy! will write OOB on this, see the behavior on master
julia> copyto!(P, [1.0, 2.0, 3.0, 4.0])
2×2 PermutedDimsArray(::Matrix{Float64}, (2, 1)) with eltype Float64:
1.0 0.0
2.0 0.0There was a problem hiding this comment.
Oh I see. And that's complicated to fix, looks like?
Could this use a generic Will this just fall back to the generic copyto! in the meantime? It seems not great to just delete a method, even if it is buggy in some situations.copyto! in the meantime?
There was a problem hiding this comment.
the vector src case will hit copyto!(dest::AbstractArray, src::AbstractArray) which forwards to copyto_unaliased!, so on this PR we have (correctly)
julia> P = PermutedDimsArray(zeros(2, 2), (2, 1));
julia> copyto!(P, [1, 2, 3, 4])
2×2 PermutedDimsArray(::Matrix{Float64}, (2, 1)) with eltype Float64:
1.0 3.0
2.0 4.0
so I don't think there are any cases where we hit a new MethodError. however there are some new BoundsError
julia> copyto!(P, [1 2 3 4])
ERROR: BoundsError: attempt to access 2×2 PermutedDimsArray(::Matrix{Float64}, (2, 1)) with eltype Float64 at index [1:1, 1:4]
Stacktrace:
[1] _throw_boundserror_indices(A::PermutedDimsArray{Float64, 2, (2, 1), (2, 1), Matrix{Float64}},however note that the behavior on master is incorrect, so the error is an improvement IMO
julia> copyto!(P, [1 2 3 4])
2×2 PermutedDimsArray(::Matrix{Float64}, (2, 1)) with eltype Float64:
1.0 2.0
3.0 4.0note the reverse is true too. some things error on master that this PR fixes
julia> P = PermutedDimsArray(zeros(2, 2), (1, 2));
# PR
julia> copyto!(P, reshape(1:4, 2, 2, 1))
2×2 PermutedDimsArray(::Matrix{Float64}, (1, 2)) with eltype Float64:
1.0 3.0
2.0 4.0
# master
julia> copyto!(P, reshape(1:4, 2, 2, 1))
ERROR: BoundsError: attempt to access Tuple{Int64, Int64} at index [3]... but master actually works, as long as P is NOT the identity permutation. so it's pretty inconsistent
julia> P = PermutedDimsArray(zeros(2, 2), (2, 1));
julia> copyto!(P, reshape(1:4, 2, 2, 1))
2×2 PermutedDimsArray(::Matrix{Float64}, (2, 1)) with eltype Float64:
1.0 3.0
2.0 4.0I can only think of one case where master was "accidentally" correct, but we introduce a BoundsError, namely when the dimensionalities match and are > 1, the axes don't match, the eltypes don't match, and perm is the identity. example on master, but note that if we use the same shape checks as in #59025 this should error in the end
julia> P = PermutedDimsArray(zeros(2, 2), (1, 2));
# master
julia> copyto!(P, [1 2 3 4])
2×2 PermutedDimsArray(::Matrix{Float64}, (1, 2)) with eltype Float64:
1.0 3.0
2.0 4.0
# PR
julia> copyto!(P, Float64[1 2 3 4])
ERROR: BoundsError: attempt to access 2×2 PermutedDimsArray(::Matrix{Float64}, (1, 2)) with eltype Float64 at index [1:1, 1:4]There was a problem hiding this comment.
I think it will be a bit complicated to fix _copy!, or at least I'd rather not do it in this PR, until I can couple it with broader cleanups & a firmer contract about exactly which bounds / axes need to be checked and when, make things more consistent across copyto!(P, _) vs copyto!(collect(P), _), when the linear fallback is acceptable, etc.
there were two issues with the old method(s):
destandsrc. I know there are not specific contracts that either ofPermutedDimsArraynorcopyto!promise w.r.t. aliasing, but by convention it seemscopyto!intends to unalias its arguments. see also: Somecopyto!methods don’t check for aliasing #39460the new method matches the pattern of
copyto!fallback forAbstractArray, only replacingcopyto_unaliased!with its specific_copy!