Skip to content

Commit

Permalink
Add setindex! for mutable MArray eltypes (inspired by JuliaArrays#1150)
Browse files Browse the repository at this point in the history
  • Loading branch information
serenity4 committed Nov 18, 2023
1 parent d419e21 commit b4e24e3
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 4 deletions.
23 changes: 20 additions & 3 deletions src/MArray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,33 @@ end
getfield(v,:data)[i]
end

# copied from `jl_is_layout_opaque`,
# which is not available for use becaused marked as static inline.
function is_layout_opaque(@nospecialize(T::DataType))
layout = unsafe_load(convert(Ptr{Base.DataTypeLayout}, T.layout))
layout.nfields == 0 && layout.npointers > 0
end
is_layout_opaque(T) = true

@propagate_inbounds function setindex!(v::MArray, val, i::Int)
@boundscheck checkbounds(v,i)
T = eltype(v)

if isbitstype(T)
GC.@preserve v unsafe_store!(Base.unsafe_convert(Ptr{T}, pointer_from_objref(v)), convert(T, val), i)
elseif isconcretetype(T) && ismutabletype(T) && !is_layout_opaque(T)
# The tuple contains object pointers.
# Replace the pointer at `i` by that of the new mutable value.
GC.@preserve v begin
data_ptr = Ptr{UInt}(pointer_from_objref(v))
value_ptr = Ptr{UInt}(pointer_from_objref(val))
unsafe_store!(data_ptr, value_ptr, i)
end
else
# This one is unsafe (#27)
# unsafe_store!(Base.unsafe_convert(Ptr{Ptr{Nothing}}, pointer_from_objref(v.data)), pointer_from_objref(val), i)
error("setindex!() with non-isbitstype eltype is not supported by StaticArrays. Consider using SizedArray.")
# For non-isbitstype immutable data, it is safer to replace the whole `.data` field directly after update.
# For more context see #27.
updated = Base.setindex(v.data, convert(T, val), i)
v.data = updated
end

return v
Expand Down
24 changes: 23 additions & 1 deletion test/MArray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -209,8 +209,30 @@
@test_throws BoundsError setindex!(mm, 4, 82)

# setindex with non-elbits type

m = MArray{Tuple{2,2,2}, String}(undef)
@test_throws ErrorException setindex!(m, "a", 1, 1, 1)
@test_throws UndefRefError setindex!(m, "a", 1, 1, 1)

m = @MArray ["a" "b"; "c" "d"]
setindex!(m, "z", 1, 1)
@test m[1, 1] == "z"
setindex!(m, "y", 1, 2)
@test m[1, 2] == "y"

m = @MArray [[], []]
setindex!(m, [2], 2)
@test m[2] == [2]

m = @MArray [Ref([1, 2]), Ref([3, 4])]
ref = Ref([5, 6])
setindex!(m, ref, 2)
@test m[2] === ref
ref[] = [7, 8]
@test m[2][] == [7, 8]

m = @MArray Any["a", "b"]
setindex!(m, "c", 2)
@test m[2] == "c"
end

@testset "promotion" begin
Expand Down

0 comments on commit b4e24e3

Please sign in to comment.