Skip to main content

oxilean_codegen/haskell_backend/
functions.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use crate::lcnf::*;
6
7use super::types::{
8    HaskellBackend, HaskellDataDecl, HaskellDecl, HaskellDoStmt, HaskellEquation, HaskellExpr,
9    HaskellFunction, HaskellGuard, HaskellImport, HaskellLit, HaskellModule, HaskellNewtype,
10    HaskellPattern, HaskellType, HaskellTypeClass, HsExtConfig, HsExtDiagCollector, HsExtDiagMsg,
11    HsExtEmitStats, HsExtEventLog, HsExtFeatures, HsExtIdGen, HsExtIncrKey, HsExtNameScope,
12    HsExtPassTiming, HsExtProfiler, HsExtSourceBuffer, HsExtVersion, HsListQual, HskAnalysisCache,
13    HskConstantFoldingHelper, HskDepGraph, HskDominatorTree, HskLivenessInfo, HskPassConfig,
14    HskPassPhase, HskPassRegistry, HskPassStats, HskWorklist,
15};
16
17/// Wrap a type in parentheses if it is a compound type (for applications).
18pub(super) fn paren_type(ty: &HaskellType) -> String {
19    match ty {
20        HaskellType::Fun(_, _)
21        | HaskellType::IO(_)
22        | HaskellType::Maybe(_)
23        | HaskellType::Either(_, _) => format!("({})", ty),
24        _ => format!("{}", ty),
25    }
26}
27/// Wrap a function type in parentheses only when used as left side of `->`
28pub(super) fn paren_fun_type(ty: &HaskellType) -> String {
29    match ty {
30        HaskellType::Fun(_, _) => format!("({})", ty),
31        _ => paren_type(ty),
32    }
33}
34pub(super) fn paren_pattern(pat: &HaskellPattern) -> String {
35    match pat {
36        HaskellPattern::Constructor(_, args) if !args.is_empty() => format!("({})", pat),
37        HaskellPattern::Cons(_, _) => format!("({})", pat),
38        HaskellPattern::As(_, _) => format!("({})", pat),
39        HaskellPattern::LazyPat(_) => format!("({})", pat),
40        _ => format!("{}", pat),
41    }
42}
43/// Map an LCNF type to a Haskell type.
44pub fn lcnf_type_to_haskell(ty: &LcnfType) -> HaskellType {
45    match ty {
46        LcnfType::Nat => HaskellType::Integer,
47        LcnfType::LcnfString => HaskellType::HsString,
48        LcnfType::Unit | LcnfType::Erased | LcnfType::Irrelevant => HaskellType::Unit,
49        LcnfType::Object => HaskellType::Polymorphic("a".to_string()),
50        LcnfType::Var(name) => HaskellType::Custom(name.clone()),
51        LcnfType::Fun(params, ret) => {
52            let hs_ret = lcnf_type_to_haskell(ret);
53            params.iter().rev().fold(hs_ret, |acc, p| {
54                HaskellType::Fun(Box::new(lcnf_type_to_haskell(p)), Box::new(acc))
55            })
56        }
57        LcnfType::Ctor(name, _args) => HaskellType::Custom(name.clone()),
58    }
59}
60/// Sanitize an identifier to be a valid Haskell identifier.
61pub(super) fn sanitize_hs_ident(name: &str) -> String {
62    let s: String = name
63        .chars()
64        .map(|c| {
65            if c.is_alphanumeric() || c == '_' || c == '\'' {
66                c
67            } else {
68                '_'
69            }
70        })
71        .collect();
72    if s.starts_with(|c: char| c.is_uppercase()) {
73        format!("fn_{}", s)
74    } else if s.is_empty() {
75        "fn_".to_string()
76    } else {
77        s
78    }
79}
80#[cfg(test)]
81mod tests {
82    use super::*;
83    #[test]
84    pub(super) fn test_type_primitives() {
85        assert_eq!(HaskellType::Int.to_string(), "Int");
86        assert_eq!(HaskellType::Integer.to_string(), "Integer");
87        assert_eq!(HaskellType::Double.to_string(), "Double");
88        assert_eq!(HaskellType::Float.to_string(), "Float");
89        assert_eq!(HaskellType::Bool.to_string(), "Bool");
90        assert_eq!(HaskellType::Char.to_string(), "Char");
91        assert_eq!(HaskellType::HsString.to_string(), "String");
92        assert_eq!(HaskellType::Unit.to_string(), "()");
93    }
94    #[test]
95    pub(super) fn test_type_io() {
96        let io_int = HaskellType::IO(Box::new(HaskellType::Int));
97        assert_eq!(io_int.to_string(), "IO Int");
98    }
99    #[test]
100    pub(super) fn test_type_list() {
101        let list_int = HaskellType::List(Box::new(HaskellType::Int));
102        assert_eq!(list_int.to_string(), "[Int]");
103    }
104    #[test]
105    pub(super) fn test_type_maybe() {
106        let maybe_str = HaskellType::Maybe(Box::new(HaskellType::HsString));
107        assert_eq!(maybe_str.to_string(), "Maybe String");
108    }
109    #[test]
110    pub(super) fn test_type_either() {
111        let either =
112            HaskellType::Either(Box::new(HaskellType::HsString), Box::new(HaskellType::Int));
113        assert_eq!(either.to_string(), "Either String Int");
114    }
115    #[test]
116    pub(super) fn test_type_tuple() {
117        let tup = HaskellType::Tuple(vec![
118            HaskellType::Int,
119            HaskellType::Bool,
120            HaskellType::HsString,
121        ]);
122        assert_eq!(tup.to_string(), "(Int, Bool, String)");
123    }
124    #[test]
125    pub(super) fn test_type_fun() {
126        let fun = HaskellType::Fun(Box::new(HaskellType::Int), Box::new(HaskellType::Bool));
127        assert_eq!(fun.to_string(), "Int -> Bool");
128    }
129    #[test]
130    pub(super) fn test_type_fun_nested() {
131        let inner = HaskellType::Fun(Box::new(HaskellType::Int), Box::new(HaskellType::Int));
132        let outer = HaskellType::Fun(Box::new(inner), Box::new(HaskellType::Bool));
133        assert_eq!(outer.to_string(), "(Int -> Int) -> Bool");
134    }
135    #[test]
136    pub(super) fn test_type_polymorphic() {
137        let p = HaskellType::Polymorphic("a".to_string());
138        assert_eq!(p.to_string(), "a");
139    }
140    #[test]
141    pub(super) fn test_type_constraint() {
142        let c = HaskellType::Constraint(
143            "Eq".to_string(),
144            vec![HaskellType::Polymorphic("a".to_string())],
145        );
146        assert_eq!(c.to_string(), "Eq a");
147    }
148    #[test]
149    pub(super) fn test_lit_int_positive() {
150        assert_eq!(HaskellLit::Int(42).to_string(), "42");
151    }
152    #[test]
153    pub(super) fn test_lit_int_negative() {
154        assert_eq!(HaskellLit::Int(-7).to_string(), "(-7)");
155    }
156    #[test]
157    pub(super) fn test_lit_bool() {
158        assert_eq!(HaskellLit::Bool(true).to_string(), "True");
159        assert_eq!(HaskellLit::Bool(false).to_string(), "False");
160    }
161    #[test]
162    pub(super) fn test_lit_char() {
163        assert_eq!(HaskellLit::Char('a').to_string(), "'a'");
164    }
165    #[test]
166    pub(super) fn test_lit_str_with_escapes() {
167        let s = HaskellLit::Str("hello\nworld".to_string());
168        assert_eq!(s.to_string(), "\"hello\\nworld\"");
169    }
170    #[test]
171    pub(super) fn test_lit_unit() {
172        assert_eq!(HaskellLit::Unit.to_string(), "()");
173    }
174    #[test]
175    pub(super) fn test_pattern_wildcard() {
176        assert_eq!(HaskellPattern::Wildcard.to_string(), "_");
177    }
178    #[test]
179    pub(super) fn test_pattern_var() {
180        assert_eq!(HaskellPattern::Var("xs".to_string()).to_string(), "xs");
181    }
182    #[test]
183    pub(super) fn test_pattern_constructor() {
184        let p = HaskellPattern::Constructor(
185            "Just".to_string(),
186            vec![HaskellPattern::Var("x".to_string())],
187        );
188        assert_eq!(p.to_string(), "Just x");
189    }
190    #[test]
191    pub(super) fn test_pattern_cons() {
192        let p = HaskellPattern::Cons(
193            Box::new(HaskellPattern::Var("x".to_string())),
194            Box::new(HaskellPattern::Var("xs".to_string())),
195        );
196        assert_eq!(p.to_string(), "(x : xs)");
197    }
198    #[test]
199    pub(super) fn test_pattern_tuple() {
200        let p = HaskellPattern::Tuple(vec![
201            HaskellPattern::Var("a".to_string()),
202            HaskellPattern::Var("b".to_string()),
203        ]);
204        assert_eq!(p.to_string(), "(a, b)");
205    }
206    #[test]
207    pub(super) fn test_pattern_as() {
208        let inner = Box::new(HaskellPattern::Constructor(
209            "Just".to_string(),
210            vec![HaskellPattern::Var("x".to_string())],
211        ));
212        let p = HaskellPattern::As("v".to_string(), inner);
213        assert_eq!(p.to_string(), "v@(Just x)");
214    }
215    #[test]
216    pub(super) fn test_pattern_lazy() {
217        let p = HaskellPattern::LazyPat(Box::new(HaskellPattern::Var("x".to_string())));
218        assert_eq!(p.to_string(), "~x");
219    }
220    #[test]
221    pub(super) fn test_expr_lambda() {
222        let e = HaskellExpr::Lambda(
223            vec![HaskellPattern::Var("x".to_string())],
224            Box::new(HaskellExpr::Var("x".to_string())),
225        );
226        assert_eq!(e.to_string(), "(\\x -> x)");
227    }
228    #[test]
229    pub(super) fn test_expr_infix() {
230        let e = HaskellExpr::InfixApp(
231            Box::new(HaskellExpr::Lit(HaskellLit::Int(1))),
232            "+".to_string(),
233            Box::new(HaskellExpr::Lit(HaskellLit::Int(2))),
234        );
235        assert_eq!(e.to_string(), "(1 + 2)");
236    }
237    #[test]
238    pub(super) fn test_expr_list_comp() {
239        let e = HaskellExpr::ListComp(
240            Box::new(HaskellExpr::InfixApp(
241                Box::new(HaskellExpr::Var("x".to_string())),
242                "*".to_string(),
243                Box::new(HaskellExpr::Var("x".to_string())),
244            )),
245            vec![HsListQual::Generator(
246                "x".to_string(),
247                HaskellExpr::App(
248                    Box::new(HaskellExpr::Var("enumFromTo".to_string())),
249                    vec![
250                        HaskellExpr::Lit(HaskellLit::Int(1)),
251                        HaskellExpr::Lit(HaskellLit::Int(10)),
252                    ],
253                ),
254            )],
255        );
256        assert!(e.to_string().contains("x <- "));
257        assert!(e.to_string().contains("x * x"));
258    }
259    #[test]
260    pub(super) fn test_expr_type_annotation() {
261        let e = HaskellExpr::TypeAnnotation(
262            Box::new(HaskellExpr::Lit(HaskellLit::Int(42))),
263            HaskellType::Int,
264        );
265        assert_eq!(e.to_string(), "(42 :: Int)");
266    }
267    #[test]
268    pub(super) fn test_data_decl_simple() {
269        let d = HaskellDataDecl {
270            name: "Color".to_string(),
271            type_params: Vec::new(),
272            constructors: vec![
273                ("Red".to_string(), Vec::new()),
274                ("Green".to_string(), Vec::new()),
275                ("Blue".to_string(), Vec::new()),
276            ],
277            deriving_clauses: vec!["Show".to_string(), "Eq".to_string()],
278        };
279        let s = d.to_string();
280        assert!(s.contains("data Color"));
281        assert!(s.contains("= Red"));
282        assert!(s.contains("| Green"));
283        assert!(s.contains("| Blue"));
284        assert!(s.contains("deriving (Show, Eq)"));
285    }
286    #[test]
287    pub(super) fn test_data_decl_with_fields() {
288        let d = HaskellDataDecl {
289            name: "Expr".to_string(),
290            type_params: Vec::new(),
291            constructors: vec![
292                ("Lit".to_string(), vec![HaskellType::Int]),
293                (
294                    "Add".to_string(),
295                    vec![
296                        HaskellType::Custom("Expr".to_string()),
297                        HaskellType::Custom("Expr".to_string()),
298                    ],
299                ),
300            ],
301            deriving_clauses: vec!["Show".to_string()],
302        };
303        let s = d.to_string();
304        assert!(s.contains("Lit Int"));
305        assert!(s.contains("Add Expr Expr"));
306    }
307    #[test]
308    pub(super) fn test_newtype() {
309        let n = HaskellNewtype {
310            name: "Name".to_string(),
311            type_param: None,
312            constructor: "Name".to_string(),
313            field: ("unName".to_string(), HaskellType::HsString),
314            deriving_clauses: vec!["Show".to_string(), "Eq".to_string()],
315        };
316        let s = n.to_string();
317        assert!(s.contains("newtype Name"));
318        assert!(s.contains("{ unName :: String }"));
319        assert!(s.contains("deriving (Show, Eq)"));
320    }
321    #[test]
322    pub(super) fn test_typeclass() {
323        let c = HaskellTypeClass {
324            name: "Container".to_string(),
325            type_params: vec!["f".to_string()],
326            superclasses: Vec::new(),
327            methods: vec![(
328                "empty".to_string(),
329                HaskellType::Custom("f a".to_string()),
330                None,
331            )],
332        };
333        let s = c.to_string();
334        assert!(s.contains("class Container f where"));
335        assert!(s.contains("empty :: f a"));
336    }
337    #[test]
338    pub(super) fn test_function_single_equation() {
339        let f = HaskellFunction {
340            name: "double".to_string(),
341            type_annotation: Some(HaskellType::Fun(
342                Box::new(HaskellType::Int),
343                Box::new(HaskellType::Int),
344            )),
345            equations: vec![HaskellEquation {
346                patterns: vec![HaskellPattern::Var("n".to_string())],
347                guards: Vec::new(),
348                body: Some(HaskellExpr::InfixApp(
349                    Box::new(HaskellExpr::Lit(HaskellLit::Int(2))),
350                    "*".to_string(),
351                    Box::new(HaskellExpr::Var("n".to_string())),
352                )),
353                where_clause: Vec::new(),
354            }],
355        };
356        let s = f.to_string();
357        assert!(s.contains("double :: Int -> Int"));
358        assert!(s.contains("double n = (2 * n)"));
359    }
360    #[test]
361    pub(super) fn test_function_with_guards() {
362        let f = HaskellFunction {
363            name: "signum'".to_string(),
364            type_annotation: None,
365            equations: vec![HaskellEquation {
366                patterns: vec![HaskellPattern::Var("x".to_string())],
367                guards: vec![
368                    HaskellGuard {
369                        condition: HaskellExpr::InfixApp(
370                            Box::new(HaskellExpr::Var("x".to_string())),
371                            ">".to_string(),
372                            Box::new(HaskellExpr::Lit(HaskellLit::Int(0))),
373                        ),
374                        body: HaskellExpr::Lit(HaskellLit::Int(1)),
375                    },
376                    HaskellGuard {
377                        condition: HaskellExpr::Var("otherwise".to_string()),
378                        body: HaskellExpr::Lit(HaskellLit::Int(-1)),
379                    },
380                ],
381                body: None,
382                where_clause: Vec::new(),
383            }],
384        };
385        let s = f.to_string();
386        assert!(s.contains("| (x > 0) = 1"));
387        assert!(s.contains("| otherwise = (-1)"));
388    }
389    #[test]
390    pub(super) fn test_module_emit() {
391        let mut m = HaskellModule::new("MyModule");
392        m.add_import(HaskellImport {
393            module: "Data.List".to_string(),
394            qualified: false,
395            alias: None,
396            items: vec!["sort".to_string()],
397            hiding: Vec::new(),
398        });
399        m.add_decl(HaskellDecl::Comment("Example module".to_string()));
400        let s = m.emit();
401        assert!(s.contains("module MyModule where"));
402        assert!(s.contains("import Data.List (sort)"));
403        assert!(s.contains("-- Example module"));
404    }
405    #[test]
406    pub(super) fn test_module_with_exports() {
407        let mut m = HaskellModule::new("Lib");
408        m.exports = vec!["foo".to_string(), "bar".to_string()];
409        let s = m.emit();
410        assert!(s.contains("module Lib ("));
411        assert!(s.contains("  foo"));
412        assert!(s.contains(") where"));
413    }
414    #[test]
415    pub(super) fn test_import_qualified() {
416        let imp = HaskellImport {
417            module: "Data.Map.Strict".to_string(),
418            qualified: true,
419            alias: Some("Map".to_string()),
420            items: Vec::new(),
421            hiding: Vec::new(),
422        };
423        assert_eq!(imp.to_string(), "import qualified Data.Map.Strict as Map");
424    }
425    #[test]
426    pub(super) fn test_import_hiding() {
427        let imp = HaskellImport {
428            module: "Prelude".to_string(),
429            qualified: false,
430            alias: None,
431            items: Vec::new(),
432            hiding: vec!["lookup".to_string()],
433        };
434        assert_eq!(imp.to_string(), "import Prelude hiding (lookup)");
435    }
436    #[test]
437    pub(super) fn test_lcnf_type_to_haskell_nat() {
438        assert_eq!(lcnf_type_to_haskell(&LcnfType::Nat), HaskellType::Integer);
439    }
440    #[test]
441    pub(super) fn test_lcnf_type_to_haskell_string() {
442        assert_eq!(
443            lcnf_type_to_haskell(&LcnfType::LcnfString),
444            HaskellType::HsString
445        );
446    }
447    #[test]
448    pub(super) fn test_lcnf_type_to_haskell_fun() {
449        let ty = LcnfType::Fun(vec![LcnfType::Nat], Box::new(LcnfType::LcnfString));
450        let hs = lcnf_type_to_haskell(&ty);
451        assert_eq!(
452            hs,
453            HaskellType::Fun(
454                Box::new(HaskellType::Integer),
455                Box::new(HaskellType::HsString)
456            )
457        );
458    }
459    #[test]
460    pub(super) fn test_sanitize_ident() {
461        assert_eq!(sanitize_hs_ident("foo"), "foo");
462        assert_eq!(sanitize_hs_ident("Foo"), "fn_Foo");
463        assert_eq!(sanitize_hs_ident("foo.bar"), "foo_bar");
464    }
465    #[test]
466    pub(super) fn test_backend_emit_module() {
467        let backend = HaskellBackend::new("Generated");
468        let src = backend.emit_module();
469        assert!(src.contains("module Generated where"));
470        assert!(src.contains("import Prelude"));
471    }
472    #[test]
473    pub(super) fn test_do_notation_display() {
474        let e = HaskellExpr::Do(vec![
475            HaskellDoStmt::Bind("x".to_string(), HaskellExpr::Var("getLine".to_string())),
476            HaskellDoStmt::Stmt(HaskellExpr::App(
477                Box::new(HaskellExpr::Var("putStrLn".to_string())),
478                vec![HaskellExpr::Var("x".to_string())],
479            )),
480        ]);
481        let s = e.to_string();
482        assert!(s.contains("x <- getLine"));
483        assert!(s.contains("putStrLn"));
484    }
485    #[test]
486    pub(super) fn test_type_synonym() {
487        let d = HaskellDecl::TypeSynonym("Name".to_string(), Vec::new(), HaskellType::HsString);
488        assert_eq!(d.to_string(), "type Name = String");
489    }
490}
491#[cfg(test)]
492mod tests_hs_ext_extra {
493    use super::*;
494    #[test]
495    pub(super) fn test_hs_ext_config() {
496        let mut cfg = HsExtConfig::new();
497        cfg.set("mode", "release");
498        cfg.set("verbose", "true");
499        assert_eq!(cfg.get("mode"), Some("release"));
500        assert!(cfg.get_bool("verbose"));
501        assert!(cfg.get_int("mode").is_none());
502        assert_eq!(cfg.len(), 2);
503    }
504    #[test]
505    pub(super) fn test_hs_ext_source_buffer() {
506        let mut buf = HsExtSourceBuffer::new();
507        buf.push_line("fn main() {");
508        buf.indent();
509        buf.push_line("println!(\"hello\");");
510        buf.dedent();
511        buf.push_line("}");
512        assert!(buf.as_str().contains("fn main()"));
513        assert!(buf.as_str().contains("    println!"));
514        assert_eq!(buf.line_count(), 3);
515        buf.reset();
516        assert!(buf.is_empty());
517    }
518    #[test]
519    pub(super) fn test_hs_ext_name_scope() {
520        let mut scope = HsExtNameScope::new();
521        assert!(scope.declare("x"));
522        assert!(!scope.declare("x"));
523        assert!(scope.is_declared("x"));
524        let scope = scope.push_scope();
525        assert_eq!(scope.depth(), 1);
526        let mut scope = scope.pop_scope();
527        assert_eq!(scope.depth(), 0);
528        scope.declare("y");
529        assert_eq!(scope.len(), 2);
530    }
531    #[test]
532    pub(super) fn test_hs_ext_diag_collector() {
533        let mut col = HsExtDiagCollector::new();
534        col.emit(HsExtDiagMsg::warning("pass_a", "slow"));
535        col.emit(HsExtDiagMsg::error("pass_b", "fatal"));
536        assert!(col.has_errors());
537        assert_eq!(col.errors().len(), 1);
538        assert_eq!(col.warnings().len(), 1);
539        col.clear();
540        assert!(col.is_empty());
541    }
542    #[test]
543    pub(super) fn test_hs_ext_id_gen() {
544        let mut gen = HsExtIdGen::new();
545        assert_eq!(gen.next_id(), 0);
546        assert_eq!(gen.next_id(), 1);
547        gen.skip(10);
548        assert_eq!(gen.next_id(), 12);
549        gen.reset();
550        assert_eq!(gen.peek_next(), 0);
551    }
552    #[test]
553    pub(super) fn test_hs_ext_incr_key() {
554        let k1 = HsExtIncrKey::new(100, 200);
555        let k2 = HsExtIncrKey::new(100, 200);
556        let k3 = HsExtIncrKey::new(999, 200);
557        assert!(k1.matches(&k2));
558        assert!(!k1.matches(&k3));
559    }
560    #[test]
561    pub(super) fn test_hs_ext_profiler() {
562        let mut p = HsExtProfiler::new();
563        p.record(HsExtPassTiming::new("pass_a", 1000, 50, 200, 100));
564        p.record(HsExtPassTiming::new("pass_b", 500, 30, 100, 200));
565        assert_eq!(p.total_elapsed_us(), 1500);
566        assert_eq!(
567            p.slowest_pass()
568                .expect("slowest pass should exist")
569                .pass_name,
570            "pass_a"
571        );
572        assert_eq!(p.profitable_passes().len(), 1);
573    }
574    #[test]
575    pub(super) fn test_hs_ext_event_log() {
576        let mut log = HsExtEventLog::new(3);
577        log.push("event1");
578        log.push("event2");
579        log.push("event3");
580        assert_eq!(log.len(), 3);
581        log.push("event4");
582        assert_eq!(log.len(), 3);
583        assert_eq!(
584            log.iter()
585                .next()
586                .expect("iterator should have next element"),
587            "event2"
588        );
589    }
590    #[test]
591    pub(super) fn test_hs_ext_version() {
592        let v = HsExtVersion::new(1, 2, 3).with_pre("alpha");
593        assert!(!v.is_stable());
594        assert_eq!(format!("{}", v), "1.2.3-alpha");
595        let stable = HsExtVersion::new(2, 0, 0);
596        assert!(stable.is_stable());
597        assert!(stable.is_compatible_with(&HsExtVersion::new(2, 0, 0)));
598        assert!(!stable.is_compatible_with(&HsExtVersion::new(3, 0, 0)));
599    }
600    #[test]
601    pub(super) fn test_hs_ext_features() {
602        let mut f = HsExtFeatures::new();
603        f.enable("sse2");
604        f.enable("avx2");
605        assert!(f.is_enabled("sse2"));
606        assert!(!f.is_enabled("avx512"));
607        f.disable("avx2");
608        assert!(!f.is_enabled("avx2"));
609        let mut g = HsExtFeatures::new();
610        g.enable("sse2");
611        g.enable("neon");
612        let union = f.union(&g);
613        assert!(union.is_enabled("sse2") && union.is_enabled("neon"));
614        let inter = f.intersection(&g);
615        assert!(inter.is_enabled("sse2"));
616    }
617    #[test]
618    pub(super) fn test_hs_ext_emit_stats() {
619        let mut s = HsExtEmitStats::new();
620        s.bytes_emitted = 50_000;
621        s.items_emitted = 500;
622        s.elapsed_ms = 100;
623        assert!(s.is_clean());
624        assert!((s.throughput_bps() - 500_000.0).abs() < 1.0);
625        let disp = format!("{}", s);
626        assert!(disp.contains("bytes=50000"));
627    }
628}
629#[cfg(test)]
630mod Hsk_infra_tests {
631    use super::*;
632    #[test]
633    pub(super) fn test_pass_config() {
634        let config = HskPassConfig::new("test_pass", HskPassPhase::Transformation);
635        assert!(config.enabled);
636        assert!(config.phase.is_modifying());
637        assert_eq!(config.phase.name(), "transformation");
638    }
639    #[test]
640    pub(super) fn test_pass_stats() {
641        let mut stats = HskPassStats::new();
642        stats.record_run(10, 100, 3);
643        stats.record_run(20, 200, 5);
644        assert_eq!(stats.total_runs, 2);
645        assert!((stats.average_changes_per_run() - 15.0).abs() < 0.01);
646        assert!((stats.success_rate() - 1.0).abs() < 0.01);
647        let s = stats.format_summary();
648        assert!(s.contains("Runs: 2/2"));
649    }
650    #[test]
651    pub(super) fn test_pass_registry() {
652        let mut reg = HskPassRegistry::new();
653        reg.register(HskPassConfig::new("pass_a", HskPassPhase::Analysis));
654        reg.register(HskPassConfig::new("pass_b", HskPassPhase::Transformation).disabled());
655        assert_eq!(reg.total_passes(), 2);
656        assert_eq!(reg.enabled_count(), 1);
657        reg.update_stats("pass_a", 5, 50, 2);
658        let stats = reg.get_stats("pass_a").expect("stats should exist");
659        assert_eq!(stats.total_changes, 5);
660    }
661    #[test]
662    pub(super) fn test_analysis_cache() {
663        let mut cache = HskAnalysisCache::new(10);
664        cache.insert("key1".to_string(), vec![1, 2, 3]);
665        assert!(cache.get("key1").is_some());
666        assert!(cache.get("key2").is_none());
667        assert!((cache.hit_rate() - 0.5).abs() < 0.01);
668        cache.invalidate("key1");
669        assert!(!cache.entries["key1"].valid);
670        assert_eq!(cache.size(), 1);
671    }
672    #[test]
673    pub(super) fn test_worklist() {
674        let mut wl = HskWorklist::new();
675        assert!(wl.push(1));
676        assert!(wl.push(2));
677        assert!(!wl.push(1));
678        assert_eq!(wl.len(), 2);
679        assert_eq!(wl.pop(), Some(1));
680        assert!(!wl.contains(1));
681        assert!(wl.contains(2));
682    }
683    #[test]
684    pub(super) fn test_dominator_tree() {
685        let mut dt = HskDominatorTree::new(5);
686        dt.set_idom(1, 0);
687        dt.set_idom(2, 0);
688        dt.set_idom(3, 1);
689        assert!(dt.dominates(0, 3));
690        assert!(dt.dominates(1, 3));
691        assert!(!dt.dominates(2, 3));
692        assert!(dt.dominates(3, 3));
693    }
694    #[test]
695    pub(super) fn test_liveness() {
696        let mut liveness = HskLivenessInfo::new(3);
697        liveness.add_def(0, 1);
698        liveness.add_use(1, 1);
699        assert!(liveness.defs[0].contains(&1));
700        assert!(liveness.uses[1].contains(&1));
701    }
702    #[test]
703    pub(super) fn test_constant_folding() {
704        assert_eq!(HskConstantFoldingHelper::fold_add_i64(3, 4), Some(7));
705        assert_eq!(HskConstantFoldingHelper::fold_div_i64(10, 0), None);
706        assert_eq!(HskConstantFoldingHelper::fold_div_i64(10, 2), Some(5));
707        assert_eq!(
708            HskConstantFoldingHelper::fold_bitand_i64(0b1100, 0b1010),
709            0b1000
710        );
711        assert_eq!(HskConstantFoldingHelper::fold_bitnot_i64(0), -1);
712    }
713    #[test]
714    pub(super) fn test_dep_graph() {
715        let mut g = HskDepGraph::new();
716        g.add_dep(1, 2);
717        g.add_dep(2, 3);
718        g.add_dep(1, 3);
719        assert_eq!(g.dependencies_of(2), vec![1]);
720        let topo = g.topological_sort();
721        assert_eq!(topo.len(), 3);
722        assert!(!g.has_cycle());
723        let pos: std::collections::HashMap<u32, usize> =
724            topo.iter().enumerate().map(|(i, &n)| (n, i)).collect();
725        assert!(pos[&1] < pos[&2]);
726        assert!(pos[&1] < pos[&3]);
727        assert!(pos[&2] < pos[&3]);
728    }
729}