(* TEST flags = "-dlambda -dno-unique-ids"; expect; *) (* Check that the code produced by TMC reads reasonably well. *) let[@tail_mod_cons] rec map f = function | [] -> [] | x :: xs -> f x :: map f xs ;; [%%expect{| (letrec (map (function f param tail_mod_cons (if param (let (block = (makemutable 0 (apply f (field_imm 0 param)) 24029)) (seq (apply map_dps block 1 f (field_imm 1 param)) block)) 0)) map_dps (function dst offset[int] f param tail_mod_cons (if param (let (block0_arg0 = (apply f (field_imm 0 param)) block = (makemutable 0 block0_arg0 24029)) (seq (setfield_ptr(heap-init)_computed dst offset block) (apply map_dps block 1 f (field_imm 1 param) tailcall))) (setfield_ptr(heap-init)_computed dst offset 0)))) (apply (field_mut 1 (global Toploop!)) "map" map)) val map : ('a -> 'b) -> 'a list -> 'b list = |}] (* check that TMC works for records as well *) type 'a cell = { hd : 'a; tl : 'a rec_list } and 'a rec_list = 'a cell option [%%expect{| 0 type 'a cell = { hd : 'a; tl : 'a rec_list; } and 'a rec_list = 'a cell option |}] let[@tail_mod_cons] rec rec_map f = function | None -> None | Some {hd; tl} -> Some { hd = f hd; tl = rec_map f tl } ;; [%%expect{| (letrec (rec_map (function f param tail_mod_cons (if param (let (*match* =a (field_imm 0 param)) (makeblock 0 (let (block = (makemutable 0 (apply f (field_imm 0 *match*)) 24029)) (seq (apply rec_map_dps block 1 f (field_imm 1 *match*)) block)))) 0)) rec_map_dps (function dst offset[int] f param tail_mod_cons (if param (let (*match* =a (field_imm 0 param) block1_arg0 = (apply f (field_imm 0 *match*)) block = (makemutable 0 block1_arg0 24029)) (seq (setfield_ptr(heap-init)_computed dst offset (makeblock 0 block)) (apply rec_map_dps block 1 f (field_imm 1 *match*) tailcall))) (setfield_ptr(heap-init)_computed dst offset 0)))) (apply (field_mut 1 (global Toploop!)) "rec_map" rec_map)) val rec_map : ('a -> 'b) -> 'a rec_list -> 'b rec_list = |}] (* check the case where several constructors are nested; we want to avoid creating an intermediate destination for each constructor. *) let[@tail_mod_cons] rec trip = function | [] -> [] | x :: xs -> (x, 0) :: (x, 1) :: (x, 2) :: trip xs ;; [%%expect{| (letrec (trip (function param tail_mod_cons (if param (let (x =a (field_imm 0 param)) (makeblock 0 (makeblock 0 (*,int) x 0) (makeblock 0 (makeblock 0 (*,int) x 1) (let (block = (makemutable 0 (makeblock 0 (*,int) x 2) 24029)) (seq (apply trip_dps block 1 (field_imm 1 param)) block))))) 0)) trip_dps (function dst offset[int] param tail_mod_cons (if param (let (x =a (field_imm 0 param) block0_arg0 = (makeblock 0 (*,int) x 0) block1_arg0 = (makeblock 0 (*,int) x 1) block2_arg0 = (makeblock 0 (*,int) x 2) block = (makemutable 0 block2_arg0 24029)) (seq (setfield_ptr(heap-init)_computed dst offset (makeblock 0 block0_arg0 (makeblock 0 block1_arg0 block))) (apply trip_dps block 1 (field_imm 1 param) tailcall))) (setfield_ptr(heap-init)_computed dst offset 0)))) (apply (field_mut 1 (global Toploop!)) "trip" trip)) val trip : 'a list -> ('a * int) list = |}] (* check nested-constructors whose arguments are effectful: they need to be let-bound appropriately (ideally, only in the DPS version) *) let[@tail_mod_cons] rec effects f = function | [] -> [] | (x, y) :: xs -> f x :: f y :: effects f xs ;; [%%expect{| (letrec (effects (function f param tail_mod_cons (if param (let (*match* =a (field_imm 0 param)) (makeblock 0 (apply f (field_imm 0 *match*)) (let (block = (makemutable 0 (apply f (field_imm 1 *match*)) 24029)) (seq (apply effects_dps block 1 f (field_imm 1 param)) block)))) 0)) effects_dps (function dst offset[int] f param tail_mod_cons (if param (let (*match* =a (field_imm 0 param) block0_arg0 = (apply f (field_imm 0 *match*)) block1_arg0 = (apply f (field_imm 1 *match*)) block = (makemutable 0 block1_arg0 24029)) (seq (setfield_ptr(heap-init)_computed dst offset (makeblock 0 block0_arg0 block)) (apply effects_dps block 1 f (field_imm 1 param) tailcall))) (setfield_ptr(heap-init)_computed dst offset 0)))) (apply (field_mut 1 (global Toploop!)) "effects" effects)) val effects : ('a -> 'b) -> ('a * 'a) list -> 'b list = |}] (* Check the case where several constructors are nested across a duplicating context: the [f None ::] part should not be duplicated in each branch. *) let[@tail_mod_cons] rec map_stutter f xs = f None :: ( match xs with | [] -> [] | x :: xs -> f (Some x) :: map_stutter f xs ) ;; [%%expect{| (letrec (map_stutter (function f xs tail_mod_cons (makeblock 0 (apply f 0) (if xs (let (block = (makemutable 0 (apply f (makeblock 0 (field_imm 0 xs))) 24029)) (seq (apply map_stutter_dps block 1 f (field_imm 1 xs)) block)) 0))) map_stutter_dps (function dst offset[int] f xs tail_mod_cons (let (block0_arg0 = (apply f 0) block = (makemutable 0 block0_arg0 24029)) (seq (setfield_ptr(heap-init)_computed dst offset block) (if xs (let (block0_arg0 = (apply f (makeblock 0 (field_imm 0 xs))) block = (makemutable 0 block0_arg0 24029)) (seq (setfield_ptr(heap-init)_computed block 1 block) (apply map_stutter_dps block 1 f (field_imm 1 xs) tailcall))) (setfield_ptr(heap-init)_computed block 1 0)))))) (apply (field_mut 1 (global Toploop!)) "map_stutter" map_stutter)) val map_stutter : ('a option -> 'b) -> 'a list -> 'b list = |}] (* Check the case where several constructors are nested across a non-duplicating context; the [f None :: .] part can be delayed below the let..in, buts it expression argument must be let-bound before the let..in is evaluated. *) type 'a stream = { hd : 'a; tl : unit -> 'a stream } let[@tail_mod_cons] rec smap_stutter f xs n = if n = 0 then [] else f None :: ( let v = f (Some xs.hd) in v :: smap_stutter f (xs.tl ()) (n - 1) ) ;; [%%expect{| 0 type 'a stream = { hd : 'a; tl : unit -> 'a stream; } (letrec (smap_stutter (function f xs n[int] tail_mod_cons (if (== n 0) 0 (makeblock 0 (apply f 0) (let (v = (apply f (makeblock 0 (field_imm 0 xs))) block = (makemutable 0 v 24029)) (seq (apply smap_stutter_dps block 1 f (apply (field_imm 1 xs) 0) (- n 1)) block))))) smap_stutter_dps (function dst offset[int] f xs n[int] tail_mod_cons (if (== n 0) (setfield_ptr(heap-init)_computed dst offset 0) (let (block0_arg0 = (apply f 0) v = (apply f (makeblock 0 (field_imm 0 xs))) block = (makemutable 0 v 24029)) (seq (setfield_ptr(heap-init)_computed dst offset (makeblock 0 block0_arg0 block)) (apply smap_stutter_dps block 1 f (apply (field_imm 1 xs) 0) (- n 1) tailcall)))))) (apply (field_mut 1 (global Toploop!)) "smap_stutter" smap_stutter)) val smap_stutter : ('a option -> 'b) -> 'a stream -> int -> 'b list = |}]