···187187 (Émile Trotignon, review by Nicolás Ojeda Bär, Jan Midtgaard and
188188 Damien Doligez)
189189190190+- #14084: Future-proof Dynarray implementation against a smarter compiler
191191+ (Basile Clément, review by Gabriel Scherer)
192192+190193### Type system:
191194192195- #13781: Set scope of internal type nodes during abbreviation expansion
+85-36
stdlib/dynarray.ml
···183183 ('a, 'stamp2) with_dummy array -> 'stamp2 dummy -> int ->
184184 len:int ->
185185 unit
186186+ (** Raises [Dummy_found i] if there is a dummy at any index [i] in
187187+ the source region. *)
186188187189 val prefix :
188190 ('a, 'stamp) with_dummy array ->
···226228 r := Some dummy;
227229 Fresh dummy
228230229229- type ('a, 'stamp) with_dummy = 'a
231231+ (* Use an abstract type to prevent the compiler from assuming anything about
232232+ the representation of [with_dummy] values.
233233+234234+ Representation: We explicitly use "%opaque" primitives when converting
235235+ to/from [with_dummy] types and/or arrays of [with_dummy] types, because
236236+ using "transparent" identity (e.g. `Obj.magic`) might break assumptions
237237+ that the compiler makes about value representations (for instance, a value
238238+ of type [(int, 'stamp) with_dummy array] could contain blocks, while a
239239+ value of type [int array] certainly does not).
240240+241241+ While it would be possible to use transparent identity in {b some} places,
242242+ it would require careful reasoning to make sure it is safe to do so
243243+ (especially in a forward-compatible way) and it is not clear the benefit
244244+ is worth the effort. *)
245245+ type ('a, 'stamp) with_dummy
230246231231- let of_val v = v
247247+ external of_val : 'a -> ('a, 'stamp) with_dummy = "%opaque"
232248233233- let of_dummy (type a stamp) (dummy : stamp dummy) =
234234- (Obj.magic dummy : (a, stamp) with_dummy)
249249+ external of_dummy : 'stamp dummy -> ('a, 'stamp) with_dummy = "%opaque"
235250236251 let is_dummy v dummy =
237252 v == of_dummy dummy
238253239239- let unsafe_get v =
240240- v
254254+ (* Safety: the argument must not be the ['stamp dummy]. *)
255255+ external unsafe_get : ('a, 'stamp) with_dummy -> 'a = "%opaque"
241256242257 module Array = struct
243258 let make n x ~dummy =
···249264 arr
250265 end
251266267267+ (* Safety: must not be called on float arrays. *)
268268+ external unsafe_nocopy_from_non_float_array :
269269+ 'a array -> ('a, 'stamp) with_dummy array
270270+ = "%opaque"
271271+252272 let copy_from_array a ~dummy =
253273 if Obj.(tag (repr a) <> double_array_tag) then
254254- Array.copy a
274274+ unsafe_nocopy_from_non_float_array (Array.copy a)
255275 else begin
256276 let n = Array.length a in
257277 let arr = Array.make n (of_dummy dummy) in
···264284265285 let unsafe_nocopy_from_array a ~dummy =
266286 if Obj.(tag (repr a) <> double_array_tag) then
267267- a
287287+ unsafe_nocopy_from_non_float_array a
268288 else copy_from_array a ~dummy
269289270290 exception Dummy_found of int
271291292292+ (* Safety: the argument must not contain any dummies, and must not contain
293293+ floats. *)
294294+ external unsafe_nocopy_to_non_float_array :
295295+ ('a, 'stamp) with_dummy array -> 'a array
296296+ = "%opaque"
297297+272298 let unsafe_nocopy_to_array a ~dummy =
273273- let arr =
274274- if Array.length a = 0 || Obj.(tag (repr a.(0)) <> double_tag) then
275275- a
276276- else begin
277277- let n = Array.length a in
278278- let a' = Array.make n a.(0) in
279279- for i = 1 to n - 1 do
280280- Array.unsafe_set a' i (unsafe_get (Array.unsafe_get a i))
281281- done;
282282- a'
283283- end
284284- in
285285- Array.iteri
286286- (fun i v -> if is_dummy v dummy then raise (Dummy_found i))
287287- arr;
288288- arr
299299+ let n = Array.length a in
300300+ if n = 0 || Obj.(tag (repr a.(0)) <> double_tag) then begin
301301+ for i = 0 to n - 1 do
302302+ if is_dummy (Array.unsafe_get a i) dummy then raise (Dummy_found i)
303303+ done;
304304+ unsafe_nocopy_to_non_float_array a
305305+ end else begin
306306+ let a' = Array.make n (unsafe_get a.(0)) in
307307+ for i = 1 to n - 1 do
308308+ let v = Array.unsafe_get a i in
309309+ if is_dummy v dummy then raise (Dummy_found i);
310310+ Array.unsafe_set a' i (unsafe_get v)
311311+ done;
312312+ a'
313313+ end
289314290315 let init n f ~dummy =
291316 let arr = Array.make n (of_dummy dummy) in
···296321297322 let blit_array src src_pos dst dst_pos ~len =
298323 if Obj.(tag (repr src) <> double_array_tag) then
299299- Array.blit src src_pos dst dst_pos len
324324+ Array.blit
325325+ (unsafe_nocopy_from_non_float_array src)
326326+ src_pos dst
327327+ dst_pos len
300328 else begin
301329 for i = 0 to len - 1 do
302330 dst.(dst_pos + i) <- of_val src.(src_pos + i)
303331 done;
304332 end
305333334334+ (* Safety: both arrays must have the same dummy, i.e. the ['stamp1 dummy]
335335+ and the ['stamp2 dummy] must be physically equal. *)
336336+ external unsafe_cast_stamp_array :
337337+ ('a, 'stamp1) with_dummy array -> ('a, 'stamp2) with_dummy array
338338+ = "%opaque"
339339+306340 let blit src src_dummy src_pos dst dst_dummy dst_pos ~len =
307341 if src_dummy == dst_dummy then
308308- Array.blit src src_pos dst dst_pos len
342342+ Array.blit (unsafe_cast_stamp_array src) src_pos dst dst_pos len
309343 else begin
310344 if len < 0
311345 || src_pos < 0
···323357 end;
324358 (* We failed the check [src_dummy == dst_dummy] above, so we
325359 know that in fact [src != dst] -- two dynarrays with
326326- distinct dummies cannot share the same backing arrays. *)
327327- assert (src != dst);
360360+ distinct dummies cannot share the same backing arrays.
361361+362362+ We use [Obj.repr] for the comparison since [src] and [dst] have
363363+ different dummies. *)
364364+ assert (Obj.repr src != Obj.repr dst);
328365 (* In particular, the source and destination arrays cannot
329366 overlap, so we can always copy in ascending order without
330330- risking overwriting an element needed later. *)
367367+ risking overwriting an element needed later.
368368+369369+ We also must check for dummies (invalid state) in the source
370370+ array: having two different dummies in the same array would be
371371+ memory unsafe. *)
331372 for i = 0 to len - 1 do
373373+ let v = Array.unsafe_get src (src_pos + i) in
374374+ (* The combination of [of_val] and [unsafe_get] below allows to change
375375+ the stamp mark, which is only safe on a non-dummy value. *)
376376+ if is_dummy v src_dummy then
377377+ raise (Dummy_found (src_pos + i));
332378 Array.unsafe_set dst (dst_pos + i)
333333- (Array.unsafe_get src (src_pos + i));
379379+ (of_val (unsafe_get v));
334380 done
335381 end
336382···738784 if dst_pos + blit_length > dst_length then begin
739785 dst.length <- dst_pos + blit_length;
740786 end;
741741- (* note: [src] and [dst] may be equal when self-blitting, so
742742- [src.length] may have been mutated here. *)
743743- Dummy.Array.blit
744744- src_arr src.dummy src_pos
745745- dst_arr dst.dummy dst_pos
746746- ~len:blit_length
787787+ try
788788+ (* note: [src] and [dst] may be equal when self-blitting, so
789789+ [src.length] may have been mutated here. *)
790790+ Dummy.Array.blit
791791+ src_arr src.dummy src_pos
792792+ dst_arr dst.dummy dst_pos
793793+ ~len:blit_length
794794+ with Dummy.Array.Dummy_found i ->
795795+ Error.missing_element ~i ~length:src_length
747796748797let blit ~src ~src_pos ~dst ~dst_pos ~len =
749798 let src_length = length src in