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::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}
61pub(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}