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