11(* Representation-level tests for object-field mutability state: the
2- linkable [field_mutability] cells in [Tfield] (doc §7). These properties
2+ linkable [field_mutability] cells in [Tfield] (doc §6. 7). These properties
33 are not observable from generated JavaScript, so they are tested here
44 directly against [Ctype]/[Btype].
55
@@ -149,9 +149,7 @@ let test_structure_generalized_occurrences_share _ =
149149 assert_bool " promotion is visible through the annotation itself"
150150 (flag_of annotated = Asttypes. Mutable )
151151
152- (* ---- §7.4 Q1: every path that shares a mutability class between two
153- owners also shares the row terminator node, so terminator genericity is
154- a property of the sharing class and the copy policy is well defined. *)
152+ (* Copy-policy coverage for ordinary typing and substitution paths. *)
155153
156154let terminator_of ty =
157155 let _, rest = Ctype. flatten_fields (Ctype. object_fields ty) in
@@ -173,22 +171,22 @@ let abstract_type_decl type_manifest : Types.type_declaration =
173171 type_inlined_types = [] ;
174172 }
175173
176- let test_q1_unified_owners_share_terminator _ =
174+ let test_unified_owners_share_terminator _ =
177175 let a = obj_with_cell (immutable_cell () ) in
178176 let b = obj_with_cell ~closed: true (immutable_cell () ) in
179177 Ctype. unify Env. empty a b;
180178 assert_bool " unification makes both rows end at the same terminator node"
181179 (terminator_of a == terminator_of b)
182180
183- let test_q1_shared_copy_shares_terminator _ =
181+ let test_shared_copy_shares_terminator _ =
184182 let annotated = obj_with_cell (immutable_cell () ) in
185183 Ctype. generalize_structure annotated;
186184 let occurrence = Ctype. instance Env. empty annotated in
187185 assert_bool " class shared" (cell_of occurrence == cell_of annotated);
188186 assert_bool " terminator shared"
189187 (terminator_of occurrence == terminator_of annotated)
190188
191- let test_q1_generalized_instance_fresh_cell_fresh_terminator _ =
189+ let test_generalized_instance_fresh_cell_fresh_terminator _ =
192190 Ctype. begin_def () ;
193191 let scheme = obj_with_cell (immutable_cell () ) in
194192 Ctype. end_def () ;
@@ -197,7 +195,7 @@ let test_q1_generalized_instance_fresh_cell_fresh_terminator _ =
197195 assert_bool " class fresh" (cell_of inst != cell_of scheme);
198196 assert_bool " terminator fresh" (terminator_of inst != terminator_of scheme)
199197
200- let test_q1_subst_generic_copy_gets_fresh_cell _ =
198+ let test_subst_generic_copy_gets_fresh_cell _ =
201199 Ctype. begin_def () ;
202200 let scheme = obj_with_cell (immutable_cell () ) in
203201 Ctype. end_def () ;
@@ -283,12 +281,10 @@ let test_nondep_failure_ends_copy_session _ =
283281 | Types. Mutability_value Asttypes. Immutable -> true
284282 | Types. Mutability_value Asttypes. Mutable | Types. Mutability_link _ -> false )
285283
286- let test_saving_closed_row_resolves_links _ =
287- (* Closed rows have no generic terminator, so saving shares the source's
288- cell rather than duplicating it (safe: marshalling deep-copies). The
289- shared cell must be the resolved representative — saved graphs never
290- contain [Mutability_link], even when the source field's own ref is a
291- link left by an earlier class merge. *)
284+ let test_saving_closed_row_gets_fresh_resolved_cell _ =
285+ (* A saved graph owns fresh cells even for closed rows. The copied cell must
286+ contain the resolved value: saved graphs never contain [Mutability_link],
287+ even when the source field's own ref is a merged-class link. *)
292288 let rep = immutable_cell () in
293289 let a = obj_with_cell ~closed: true (ref (Types. Mutability_link rep)) in
294290 assert_bool " the source field holds a link (a merged class member)"
@@ -301,7 +297,9 @@ let test_saving_closed_row_resolves_links _ =
301297 | Types. Mutability_value _ -> true
302298 | Types. Mutability_link _ -> false );
303299 assert_bool " the saved flag is the class value"
304- (flag_of saved = Asttypes. Immutable )
300+ (flag_of saved = Asttypes. Immutable );
301+ assert_bool " the saved graph does not retain the source cell"
302+ (cell_of saved != cell_of a)
305303
306304let test_saving_marshal_round_trip _ =
307305 (* The real persistence claim: after [for_saving] the graph marshals, and
@@ -350,6 +348,56 @@ let test_saving_preserves_class_sharing _ =
350348 | Types. Mutability_value _ -> true
351349 | Types. Mutability_link _ -> false )
352350
351+ let test_for_saving_copy_order_is_irrelevant _ =
352+ (* [generalize_structure] makes the field spine generic while leaving its
353+ open-row terminator non-generic. [for_saving] makes the copied terminator
354+ generic. If the two graphs still share one mutability cell, copying them
355+ in one session can then choose different policies for that cell. *)
356+ Ctype. begin_def () ;
357+ let source = obj_with_cell (immutable_cell () ) in
358+ Ctype. end_def () ;
359+ Ctype. generalize_structure source;
360+ let saved = Subst. type_expr (Subst. for_saving Subst. identity) source in
361+ assert_bool " the fixture has different row-copy classifications"
362+ ((terminator_of source).level <> Btype. generic_level
363+ && (terminator_of saved).level = Btype. generic_level);
364+ assert_bool " for_saving gives the copied graph an independent class"
365+ (cell_of source != cell_of saved);
366+ let copy_pair first second =
367+ match Ctype. instance_list Env. empty [first; second] with
368+ | [first'; second'] -> (first', second')
369+ | _ -> OUnit. assert_failure " expected two copied object types"
370+ in
371+ let source_first, saved_second = copy_pair source saved in
372+ let saved_first, source_second = copy_pair saved source in
373+ let source_first_shares = cell_of source_first == cell_of saved_second in
374+ let saved_first_shares = cell_of saved_first == cell_of source_second in
375+ assert_bool " source-first copies are separate" (not source_first_shares);
376+ assert_bool " saved-first copies are separate" (not saved_first_shares);
377+ assert_bool
378+ " copying owners of one class must not depend on their order in the session"
379+ (source_first_shares = saved_first_shares)
380+
381+ let test_for_saving_fresh_copy_preserves_internal_aliasing _ =
382+ Ctype. begin_def () ;
383+ let cell = immutable_cell () in
384+ let source =
385+ Ctype. newty (Types. Ttuple [obj_with_cell cell; obj_with_cell cell])
386+ in
387+ Ctype. end_def () ;
388+ Ctype. generalize_structure source;
389+ let saved = Subst. type_expr (Subst. for_saving Subst. identity) source in
390+ let source_a, source_b, saved_a, saved_b =
391+ match ((Btype. repr source).desc, (Btype. repr saved).desc) with
392+ | Types. Ttuple [source_a; source_b], Types. Ttuple [saved_a; saved_b] ->
393+ (source_a, source_b, saved_a, saved_b)
394+ | _ -> OUnit. assert_failure " expected source and saved object pairs"
395+ in
396+ assert_bool " the source aliases share one class"
397+ (cell_of source_a == cell_of source_b);
398+ assert_bool " the saved aliases share one fresh class"
399+ (cell_of saved_a == cell_of saved_b && cell_of saved_a != cell_of source_a)
400+
353401let suites =
354402 __FILE__
355403 > ::: [
@@ -365,23 +413,26 @@ let suites =
365413 > :: test_generalized_instance_preserves_internal_aliasing;
366414 " structure_generalized_occurrences_share"
367415 > :: test_structure_generalized_occurrences_share;
368- " q1_unified_owners_share_terminator"
369- > :: test_q1_unified_owners_share_terminator;
370- " q1_shared_copy_shares_terminator"
371- > :: test_q1_shared_copy_shares_terminator;
372- " q1_generalized_instance_fresh_cell_fresh_terminator"
373- > :: test_q1_generalized_instance_fresh_cell_fresh_terminator;
374- " q1_subst_generic_copy_gets_fresh_cell"
375- > :: test_q1_subst_generic_copy_gets_fresh_cell;
416+ " unified_owners_share_terminator"
417+ > :: test_unified_owners_share_terminator;
418+ " shared_copy_shares_terminator" > :: test_shared_copy_shares_terminator;
419+ " generalized_instance_fresh_cell_fresh_terminator"
420+ > :: test_generalized_instance_fresh_cell_fresh_terminator;
421+ " subst_generic_copy_gets_fresh_cell"
422+ > :: test_subst_generic_copy_gets_fresh_cell;
376423 " nondep_type_ends_its_copy_session"
377424 > :: test_nondep_type_ends_its_copy_session;
378425 " nondep_nested_copy_preserves_class_sharing"
379426 > :: test_nondep_nested_copy_preserves_class_sharing;
380427 " nondep_failure_ends_copy_session"
381428 > :: test_nondep_failure_ends_copy_session;
382- " saving_closed_row_resolves_links "
383- > :: test_saving_closed_row_resolves_links ;
429+ " saving_closed_row_gets_fresh_resolved_cell "
430+ > :: test_saving_closed_row_gets_fresh_resolved_cell ;
384431 " saving_marshal_round_trip" > :: test_saving_marshal_round_trip;
385432 " saving_preserves_class_sharing"
386433 > :: test_saving_preserves_class_sharing;
434+ " for_saving_copy_order_is_irrelevant"
435+ > :: test_for_saving_copy_order_is_irrelevant;
436+ " for_saving_fresh_copy_preserves_internal_aliasing"
437+ > :: test_for_saving_fresh_copy_preserves_internal_aliasing;
387438 ]
0 commit comments