1use 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
17pub(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}
27pub(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}
43pub 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}
60pub(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}