diff --git a/src/MArray.jl b/src/MArray.jl index 9d8e87f3..e1592ee3 100644 --- a/src/MArray.jl +++ b/src/MArray.jl @@ -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 diff --git a/test/MArray.jl b/test/MArray.jl index 79022dd9..675c0530 100644 --- a/test/MArray.jl +++ b/test/MArray.jl @@ -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