Skip to content

Commit 8f4fb37

Browse files
committed
Add setindex! for non-isbits MArray eltypes (inspired by JuliaArrays#1150)
1 parent d419e21 commit 8f4fb37

File tree

2 files changed

+43
-4
lines changed

2 files changed

+43
-4
lines changed

src/MArray.jl

+20-3
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,33 @@ end
2727
getfield(v,:data)[i]
2828
end
2929

30+
# copied from `jl_is_layout_opaque`,
31+
# which is not available for use becaused marked as static inline.
32+
function is_layout_opaque(@nospecialize(T::DataType))
33+
layout = unsafe_load(convert(Ptr{Base.DataTypeLayout}, T.layout))
34+
layout.nfields == 0 && layout.npointers > 0
35+
end
36+
is_layout_opaque(T) = true
37+
3038
@propagate_inbounds function setindex!(v::MArray, val, i::Int)
3139
@boundscheck checkbounds(v,i)
3240
T = eltype(v)
3341

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

4259
return v

test/MArray.jl

+23-1
Original file line numberDiff line numberDiff line change
@@ -209,8 +209,30 @@
209209
@test_throws BoundsError setindex!(mm, 4, 82)
210210

211211
# setindex with non-elbits type
212+
212213
m = MArray{Tuple{2,2,2}, String}(undef)
213-
@test_throws ErrorException setindex!(m, "a", 1, 1, 1)
214+
@test_throws UndefRefError setindex!(m, "a", 1, 1, 1)
215+
216+
m = @MArray ["a" "b"; "c" "d"]
217+
setindex!(m, "z", 1, 1)
218+
@test m[1, 1] == "z"
219+
setindex!(m, "y", 1, 2)
220+
@test m[1, 2] == "y"
221+
222+
m = @MArray [[], []]
223+
setindex!(m, [2], 2)
224+
@test m[2] == [2]
225+
226+
m = @MArray [Ref([1, 2]), Ref([3, 4])]
227+
ref = Ref([5, 6])
228+
setindex!(m, ref, 2)
229+
@test m[2] === ref
230+
ref[] = [7, 8]
231+
@test m[2][] == [7, 8]
232+
233+
m = @MArray Any["a", "b"]
234+
setindex!(m, "c", 2)
235+
@test m[2] == "c"
214236
end
215237

216238
@testset "promotion" begin

0 commit comments

Comments
 (0)