|
| 1 | +open OUnit |
| 2 | + |
| 3 | +let loc = Location.none |
| 4 | +let x = Ident.create "x" |
| 5 | +let y = Ident.create "y" |
| 6 | +let debugger = Lambda.prim ~primitive:Pdebugger ~args:[] loc |
| 7 | +let var = Lambda.var x |
| 8 | + |
| 9 | +(* One node per constructor that has children. A leaf shares trivially, so it |
| 10 | + would pass either check below without exercising anything. *) |
| 11 | +let nodes : (string * Lambda.t) list = |
| 12 | + [ |
| 13 | + ("apply", Lambda.apply var [var] {ap_loc = loc; ap_inlined = Default_inline}); |
| 14 | + ( "function", |
| 15 | + Lambda.function_ ~loc ~attr:Lambda.default_function_attribute ~params:[x] |
| 16 | + ~body:debugger ); |
| 17 | + ("let", Lambda.let_ Strict y debugger var); |
| 18 | + ("letrec", Lambda.letrec [(y, debugger)] var); |
| 19 | + ("prim", Lambda.prim ~primitive:Pdebugger ~args:[var] loc); |
| 20 | + ( "switch", |
| 21 | + Lambda.switch var |
| 22 | + { |
| 23 | + sw_consts_full = false; |
| 24 | + sw_consts = [(Switch_int 0, debugger)]; |
| 25 | + sw_blocks_full = false; |
| 26 | + sw_blocks = []; |
| 27 | + sw_failaction = Some debugger; |
| 28 | + sw_dispatch = Switch_direct; |
| 29 | + } ); |
| 30 | + ("stringswitch", Lambda.stringswitch var [("a", debugger)] (Some debugger)); |
| 31 | + ("staticraise", Lambda.staticraise 1 [var]); |
| 32 | + ( "staticcatch", |
| 33 | + Lambda.staticcatch (Lambda.staticraise 1 []) (1, []) debugger ); |
| 34 | + ("trywith", Lambda.try_ debugger y var); |
| 35 | + ("ifthenelse", Lambda.if_ var debugger debugger); |
| 36 | + ("sequence", Lambda.seq debugger var); |
| 37 | + ("while", Lambda.while_ var debugger); |
| 38 | + ("for", Lambda.for_ y var var Upto debugger); |
| 39 | + ("for_of", Lambda.for_of y var debugger); |
| 40 | + ("for_await_of", Lambda.for_await_of y var debugger); |
| 41 | + ("assign", Lambda.assign x debugger); |
| 42 | + ] |
| 43 | + |
| 44 | +(* Every optimization pass routes its "nothing to do here" case through |
| 45 | + [shallow_map_sharing], so an arm of it that stops sharing silently costs the |
| 46 | + property in all of them. That is invisible to generated output: breaking the |
| 47 | + [Lapply] and [Lswitch] arms leaves every fixture in the repository byte for |
| 48 | + byte identical. Add a node above when adding a Lambda constructor. *) |
| 49 | +let suites = |
| 50 | + __FILE__ |
| 51 | + >::: [ |
| 52 | + ( "an unchanged child is not rebuilt" >:: fun _ -> |
| 53 | + List.iter |
| 54 | + (fun (name, node) -> |
| 55 | + assert_bool |
| 56 | + (name ^ " should be handed back when nothing changed") |
| 57 | + (Lambda_traverse.shallow_map_sharing (fun lam -> lam) node |
| 58 | + == node)) |
| 59 | + nodes ); |
| 60 | + ( "a changed child is rebuilt" >:: fun _ -> |
| 61 | + (* Without this, a node whose children were never visited would pass |
| 62 | + the check above by doing nothing at all. *) |
| 63 | + List.iter |
| 64 | + (fun (name, node) -> |
| 65 | + assert_bool |
| 66 | + (name ^ " should be rebuilt when a child changed") |
| 67 | + (Lambda_traverse.shallow_map_sharing |
| 68 | + (fun _ -> Lambda.const Lambda.const_unit) |
| 69 | + node |
| 70 | + != node)) |
| 71 | + nodes ); |
| 72 | + ] |
0 commit comments