1use super::types::{
6 L4AnalysisCache, L4ConstantFoldingHelper, L4DepGraph, L4DominatorTree, L4ExtCache,
7 L4ExtConstFolder, L4ExtDepGraph, L4ExtDomTree, L4ExtLiveness, L4ExtPassConfig, L4ExtPassPhase,
8 L4ExtPassRegistry, L4ExtPassStats, L4ExtWorklist, L4LivenessInfo, L4PassConfig, L4PassPhase,
9 L4PassRegistry, L4PassStats, L4Worklist, Lean4Abbrev, Lean4Axiom, Lean4Backend, Lean4CalcStep,
10 Lean4Constructor, Lean4Decl, Lean4Def, Lean4DoStmt, Lean4Expr, Lean4File, Lean4Inductive,
11 Lean4Instance, Lean4Pattern, Lean4Structure, Lean4Theorem, Lean4Type,
12};
13
14pub(super) fn paren_type(ty: &Lean4Type) -> std::string::String {
16 match ty {
17 Lean4Type::App(_, _)
18 | Lean4Type::Fun(_, _)
19 | Lean4Type::ForAll(_, _, _)
20 | Lean4Type::List(_)
21 | Lean4Type::Option(_)
22 | Lean4Type::Prod(_, _)
23 | Lean4Type::Sum(_, _)
24 | Lean4Type::IO(_)
25 | Lean4Type::Array(_)
26 | Lean4Type::Fin(_) => format!("({})", ty),
27 _ => ty.to_string(),
28 }
29}
30pub(super) fn paren_complex_type(ty: &Lean4Type) -> std::string::String {
32 match ty {
33 Lean4Type::Fun(_, _) | Lean4Type::ForAll(_, _, _) => format!("({})", ty),
34 _ => ty.to_string(),
35 }
36}
37pub(super) fn paren_fun_type(ty: &Lean4Type) -> std::string::String {
39 match ty {
40 Lean4Type::Fun(_, _) | Lean4Type::ForAll(_, _, _) => format!("({})", ty),
41 _ => ty.to_string(),
42 }
43}
44pub(super) fn paren_expr(e: &Lean4Expr) -> std::string::String {
46 match e {
47 Lean4Expr::App(_, _)
48 | Lean4Expr::Lambda(_, _, _)
49 | Lean4Expr::If(_, _, _)
50 | Lean4Expr::Let(_, _, _, _)
51 | Lean4Expr::LetRec(_, _, _)
52 | Lean4Expr::Match(_, _)
53 | Lean4Expr::Have(_, _, _, _)
54 | Lean4Expr::Show(_, _)
55 | Lean4Expr::Do(_)
56 | Lean4Expr::Calc(_)
57 | Lean4Expr::ByTactic(_) => format!("({})", e),
58 _ => e.to_string(),
59 }
60}
61pub(super) fn paren_pattern(p: &Lean4Pattern) -> std::string::String {
63 match p {
64 Lean4Pattern::Ctor(_, args) if !args.is_empty() => format!("({})", p),
65 Lean4Pattern::Or(_, _) => format!("({})", p),
66 _ => p.to_string(),
67 }
68}
69#[cfg(test)]
70mod tests {
71 use super::*;
72 #[test]
73 pub(super) fn test_type_nat() {
74 assert_eq!(Lean4Type::Nat.to_string(), "Nat");
75 }
76 #[test]
77 pub(super) fn test_type_bool() {
78 assert_eq!(Lean4Type::Bool.to_string(), "Bool");
79 }
80 #[test]
81 pub(super) fn test_type_prop() {
82 assert_eq!(Lean4Type::Prop.to_string(), "Prop");
83 }
84 #[test]
85 pub(super) fn test_type_type_zero() {
86 assert_eq!(Lean4Type::Type(0).to_string(), "Type");
87 }
88 #[test]
89 pub(super) fn test_type_type_nonzero() {
90 assert_eq!(Lean4Type::Type(1).to_string(), "Type 1");
91 }
92 #[test]
93 pub(super) fn test_type_list() {
94 let ty = Lean4Type::List(Box::new(Lean4Type::Nat));
95 assert_eq!(ty.to_string(), "List Nat");
96 }
97 #[test]
98 pub(super) fn test_type_option() {
99 let ty = Lean4Type::Option(Box::new(Lean4Type::Int));
100 assert_eq!(ty.to_string(), "Option Int");
101 }
102 #[test]
103 pub(super) fn test_type_prod() {
104 let ty = Lean4Type::Prod(Box::new(Lean4Type::Nat), Box::new(Lean4Type::Bool));
105 assert_eq!(ty.to_string(), "Nat × Bool");
106 }
107 #[test]
108 pub(super) fn test_type_fun() {
109 let ty = Lean4Type::Fun(Box::new(Lean4Type::Nat), Box::new(Lean4Type::Bool));
110 assert_eq!(ty.to_string(), "Nat → Bool");
111 }
112 #[test]
113 pub(super) fn test_type_forall() {
114 let ty = Lean4Type::ForAll(
115 "n".to_string(),
116 Box::new(Lean4Type::Nat),
117 Box::new(Lean4Type::Prop),
118 );
119 assert_eq!(ty.to_string(), "∀ (n : Nat), Prop");
120 }
121 #[test]
122 pub(super) fn test_type_io() {
123 let ty = Lean4Type::IO(Box::new(Lean4Type::Unit));
124 assert_eq!(ty.to_string(), "IO Unit");
125 }
126 #[test]
127 pub(super) fn test_type_array() {
128 let ty = Lean4Type::Array(Box::new(Lean4Type::Float));
129 assert_eq!(ty.to_string(), "Array Float");
130 }
131 #[test]
132 pub(super) fn test_expr_var() {
133 let e = Lean4Expr::Var("x".to_string());
134 assert_eq!(e.to_string(), "x");
135 }
136 #[test]
137 pub(super) fn test_expr_nat_lit() {
138 let e = Lean4Expr::NatLit(42);
139 assert_eq!(e.to_string(), "42");
140 }
141 #[test]
142 pub(super) fn test_expr_bool_lit() {
143 let e = Lean4Expr::BoolLit(true);
144 assert_eq!(e.to_string(), "true");
145 }
146 #[test]
147 pub(super) fn test_expr_str_lit() {
148 let e = Lean4Expr::StrLit("hello".to_string());
149 assert_eq!(e.to_string(), "\"hello\"");
150 }
151 #[test]
152 pub(super) fn test_expr_str_lit_escaping() {
153 let e = Lean4Expr::StrLit("say \"hi\"".to_string());
154 assert!(e.to_string().contains("\\\""));
155 }
156 #[test]
157 pub(super) fn test_expr_sorry() {
158 let e = Lean4Expr::Sorry;
159 assert_eq!(e.to_string(), "sorry");
160 }
161 #[test]
162 pub(super) fn test_expr_hole() {
163 let e = Lean4Expr::Hole;
164 assert_eq!(e.to_string(), "_");
165 }
166 #[test]
167 pub(super) fn test_expr_app() {
168 let e = Lean4Expr::App(
169 Box::new(Lean4Expr::Var("f".to_string())),
170 Box::new(Lean4Expr::Var("x".to_string())),
171 );
172 assert_eq!(e.to_string(), "f x");
173 }
174 #[test]
175 pub(super) fn test_expr_lambda_untyped() {
176 let e = Lean4Expr::Lambda(
177 "x".to_string(),
178 None,
179 Box::new(Lean4Expr::Var("x".to_string())),
180 );
181 assert_eq!(e.to_string(), "fun x => x");
182 }
183 #[test]
184 pub(super) fn test_expr_lambda_typed() {
185 let e = Lean4Expr::Lambda(
186 "x".to_string(),
187 Some(Box::new(Lean4Type::Nat)),
188 Box::new(Lean4Expr::Var("x".to_string())),
189 );
190 assert_eq!(e.to_string(), "fun (x : Nat) => x");
191 }
192 #[test]
193 pub(super) fn test_expr_if() {
194 let e = Lean4Expr::If(
195 Box::new(Lean4Expr::BoolLit(true)),
196 Box::new(Lean4Expr::NatLit(1)),
197 Box::new(Lean4Expr::NatLit(0)),
198 );
199 assert_eq!(e.to_string(), "if true then 1 else 0");
200 }
201 #[test]
202 pub(super) fn test_expr_tuple() {
203 let e = Lean4Expr::Tuple(vec![Lean4Expr::NatLit(1), Lean4Expr::NatLit(2)]);
204 assert_eq!(e.to_string(), "(1, 2)");
205 }
206 #[test]
207 pub(super) fn test_expr_anonymous_ctor() {
208 let e = Lean4Expr::AnonymousCtor(vec![Lean4Expr::NatLit(1), Lean4Expr::NatLit(2)]);
209 assert_eq!(e.to_string(), "⟨1, 2⟩");
210 }
211 #[test]
212 pub(super) fn test_expr_by_tactic() {
213 let e = Lean4Expr::ByTactic(vec!["simp".to_string(), "exact h".to_string()]);
214 assert_eq!(e.to_string(), "by simp; exact h");
215 }
216 #[test]
217 pub(super) fn test_pattern_wildcard() {
218 assert_eq!(Lean4Pattern::Wildcard.to_string(), "_");
219 }
220 #[test]
221 pub(super) fn test_pattern_var() {
222 assert_eq!(Lean4Pattern::Var("x".to_string()).to_string(), "x");
223 }
224 #[test]
225 pub(super) fn test_pattern_ctor_noargs() {
226 let p = Lean4Pattern::Ctor("none".to_string(), vec![]);
227 assert_eq!(p.to_string(), "none");
228 }
229 #[test]
230 pub(super) fn test_pattern_ctor_with_args() {
231 let p = Lean4Pattern::Ctor("some".to_string(), vec![Lean4Pattern::Var("x".to_string())]);
232 assert_eq!(p.to_string(), "some x");
233 }
234 #[test]
235 pub(super) fn test_pattern_tuple() {
236 let p = Lean4Pattern::Tuple(vec![
237 Lean4Pattern::Var("a".to_string()),
238 Lean4Pattern::Var("b".to_string()),
239 ]);
240 assert_eq!(p.to_string(), "(a, b)");
241 }
242 #[test]
243 pub(super) fn test_pattern_or() {
244 let p = Lean4Pattern::Or(
245 Box::new(Lean4Pattern::Lit("0".to_string())),
246 Box::new(Lean4Pattern::Lit("1".to_string())),
247 );
248 assert_eq!(p.to_string(), "0 | 1");
249 }
250 #[test]
251 pub(super) fn test_def_emit_simple() {
252 let def = Lean4Def::simple(
253 "id",
254 vec![("x".to_string(), Lean4Type::Nat)],
255 Lean4Type::Nat,
256 Lean4Expr::Var("x".to_string()),
257 );
258 let out = def.emit();
259 assert!(out.contains("def id"));
260 assert!(out.contains("(x : Nat)"));
261 assert!(out.contains(": Nat"));
262 assert!(out.contains(":="));
263 assert!(out.contains("x"));
264 }
265 #[test]
266 pub(super) fn test_def_emit_with_attribute() {
267 let mut def = Lean4Def::simple("myFn", vec![], Lean4Type::Nat, Lean4Expr::NatLit(0));
268 def.attributes.push("simp".to_string());
269 let out = def.emit();
270 assert!(out.contains("@[simp]"));
271 }
272 #[test]
273 pub(super) fn test_def_emit_noncomputable() {
274 let mut def = Lean4Def::simple(
275 "realVal",
276 vec![],
277 Lean4Type::Float,
278 Lean4Expr::FloatLit(3.14),
279 );
280 def.is_noncomputable = true;
281 let out = def.emit();
282 assert!(out.contains("noncomputable"));
283 }
284 #[test]
285 pub(super) fn test_def_emit_private() {
286 let mut def = Lean4Def::simple("helper", vec![], Lean4Type::Nat, Lean4Expr::NatLit(0));
287 def.is_private = true;
288 let out = def.emit();
289 assert!(out.contains("private"));
290 }
291 #[test]
292 pub(super) fn test_theorem_emit_tactic() {
293 let thm = Lean4Theorem::tactic(
294 "add_zero",
295 vec![("n".to_string(), Lean4Type::Nat)],
296 Lean4Type::Custom("n + 0 = n".to_string()),
297 vec!["simp".to_string()],
298 );
299 let out = thm.emit();
300 assert!(out.contains("theorem add_zero"));
301 assert!(out.contains("(n : Nat)"));
302 assert!(out.contains("by simp"));
303 }
304 #[test]
305 pub(super) fn test_theorem_emit_term_mode() {
306 let thm = Lean4Theorem::term_mode(
307 "trivial_thm",
308 vec![],
309 Lean4Type::Custom("True".to_string()),
310 Lean4Expr::Var("trivial".to_string()),
311 );
312 let out = thm.emit();
313 assert!(out.contains("theorem trivial_thm"));
314 assert!(out.contains("trivial"));
315 }
316 #[test]
317 pub(super) fn test_inductive_emit_simple() {
318 let ind = Lean4Inductive::simple(
319 "Color",
320 vec![
321 Lean4Constructor::positional("red", vec![]),
322 Lean4Constructor::positional("green", vec![]),
323 Lean4Constructor::positional("blue", vec![]),
324 ],
325 );
326 let out = ind.emit();
327 assert!(out.contains("inductive Color"));
328 assert!(out.contains("| red"));
329 assert!(out.contains("| green"));
330 assert!(out.contains("| blue"));
331 }
332 #[test]
333 pub(super) fn test_inductive_emit_with_fields() {
334 let ind = Lean4Inductive::simple(
335 "Expr",
336 vec![
337 Lean4Constructor::positional("lit", vec![Lean4Type::Nat]),
338 Lean4Constructor::positional(
339 "add",
340 vec![
341 Lean4Type::Custom("Expr".to_string()),
342 Lean4Type::Custom("Expr".to_string()),
343 ],
344 ),
345 ],
346 );
347 let out = ind.emit();
348 assert!(out.contains("inductive Expr"));
349 assert!(out.contains("| lit"));
350 assert!(out.contains("Nat"));
351 }
352 #[test]
353 pub(super) fn test_structure_emit() {
354 let s = Lean4Structure::simple(
355 "Point",
356 vec![
357 ("x".to_string(), Lean4Type::Float),
358 ("y".to_string(), Lean4Type::Float),
359 ],
360 );
361 let out = s.emit();
362 assert!(out.contains("structure Point"));
363 assert!(out.contains("x : Float"));
364 assert!(out.contains("y : Float"));
365 }
366 #[test]
367 pub(super) fn test_file_emit_imports() {
368 let file = Lean4File::new()
369 .with_import("Mathlib.Data.Nat.Basic")
370 .with_open("Nat");
371 let out = file.emit();
372 assert!(out.contains("import Mathlib.Data.Nat.Basic"));
373 assert!(out.contains("open Nat"));
374 }
375 #[test]
376 pub(super) fn test_file_emit_with_def() {
377 let mut file = Lean4File::new();
378 let def = Lean4Def::simple(
379 "double",
380 vec![("n".to_string(), Lean4Type::Nat)],
381 Lean4Type::Nat,
382 Lean4Expr::App(
383 Box::new(Lean4Expr::App(
384 Box::new(Lean4Expr::Var("HAdd.hAdd".to_string())),
385 Box::new(Lean4Expr::Var("n".to_string())),
386 )),
387 Box::new(Lean4Expr::Var("n".to_string())),
388 ),
389 );
390 file.add_decl(Lean4Decl::Def(def));
391 let out = file.emit();
392 assert!(out.contains("def double"));
393 assert!(out.contains("(n : Nat)"));
394 }
395 #[test]
396 pub(super) fn test_backend_compile_kernel_decl() {
397 let mut backend = Lean4Backend::new();
398 backend.compile_kernel_decl(
399 "identity",
400 vec![("x".to_string(), Lean4Type::Nat)],
401 Lean4Type::Nat,
402 Lean4Expr::Var("x".to_string()),
403 );
404 let out = backend.emit_file();
405 assert!(out.contains("def identity"));
406 }
407 #[test]
408 pub(super) fn test_backend_add_theorem() {
409 let mut backend = Lean4Backend::new();
410 backend.add_theorem(
411 "nat_eq_refl",
412 vec![("n".to_string(), Lean4Type::Nat)],
413 Lean4Type::Custom("n = n".to_string()),
414 vec!["rfl".to_string()],
415 );
416 let out = backend.emit_file();
417 assert!(out.contains("theorem nat_eq_refl"));
418 assert!(out.contains("by rfl"));
419 }
420 #[test]
421 pub(super) fn test_backend_add_inductive() {
422 let mut backend = Lean4Backend::new();
423 let ind = Lean4Inductive::simple(
424 "Tree",
425 vec![
426 Lean4Constructor::positional("leaf", vec![]),
427 Lean4Constructor::positional(
428 "node",
429 vec![
430 Lean4Type::Custom("Tree".to_string()),
431 Lean4Type::Nat,
432 Lean4Type::Custom("Tree".to_string()),
433 ],
434 ),
435 ],
436 );
437 backend.add_inductive(ind);
438 let out = backend.emit_file();
439 assert!(out.contains("inductive Tree"));
440 assert!(out.contains("leaf"));
441 assert!(out.contains("node"));
442 }
443 #[test]
444 pub(super) fn test_namespace_emit() {
445 let decl = Lean4Decl::Namespace(
446 "MyNS".to_string(),
447 vec![Lean4Decl::Def(Lean4Def::simple(
448 "foo",
449 vec![],
450 Lean4Type::Nat,
451 Lean4Expr::NatLit(0),
452 ))],
453 );
454 let out = decl.emit();
455 assert!(out.contains("namespace MyNS"));
456 assert!(out.contains("end MyNS"));
457 assert!(out.contains("def foo"));
458 }
459 #[test]
460 pub(super) fn test_section_emit() {
461 let decl = Lean4Decl::Section(
462 "Aux".to_string(),
463 vec![Lean4Decl::Check(Lean4Expr::NatLit(42))],
464 );
465 let out = decl.emit();
466 assert!(out.contains("section Aux"));
467 assert!(out.contains("end Aux"));
468 assert!(out.contains("#check"));
469 }
470 #[test]
471 pub(super) fn test_axiom_emit() {
472 let ax = Lean4Axiom {
473 name: "classical".to_string(),
474 args: vec![("p".to_string(), Lean4Type::Prop)],
475 ty: Lean4Type::Custom("p ∨ ¬p".to_string()),
476 doc_comment: Some("Law of excluded middle".to_string()),
477 };
478 let out = ax.emit();
479 assert!(out.contains("axiom classical"));
480 assert!(out.contains("(p : Prop)"));
481 assert!(out.contains("Law of excluded middle"));
482 }
483 #[test]
484 pub(super) fn test_abbrev_emit() {
485 let a = Lean4Abbrev {
486 name: "MyNat".to_string(),
487 args: vec![],
488 ty: Some(Lean4Type::Type(0)),
489 body: Lean4Expr::Var("Nat".to_string()),
490 };
491 let out = a.emit();
492 assert!(out.contains("abbrev MyNat"));
493 assert!(out.contains("Nat"));
494 }
495 #[test]
496 pub(super) fn test_instance_emit() {
497 let inst = Lean4Instance {
498 name: Some("instAddNat".to_string()),
499 ty: Lean4Type::Custom("Add Nat".to_string()),
500 args: vec![],
501 body: Lean4Expr::StructLit(
502 "".to_string(),
503 vec![("add".to_string(), Lean4Expr::Var("Nat.add".to_string()))],
504 ),
505 };
506 let out = inst.emit();
507 assert!(out.contains("instance instAddNat"));
508 assert!(out.contains("Add Nat"));
509 }
510 #[test]
511 pub(super) fn test_do_notation() {
512 let stmt = Lean4DoStmt::Bind(
513 "line".to_string(),
514 None,
515 Box::new(Lean4Expr::Var("IO.getLine".to_string())),
516 );
517 let s = stmt.to_string();
518 assert!(s.contains("let line ←"));
519 assert!(s.contains("IO.getLine"));
520 }
521 #[test]
522 pub(super) fn test_calc_step() {
523 let step = Lean4CalcStep {
524 lhs: Lean4Expr::Var("a".to_string()),
525 relation: "=".to_string(),
526 rhs: Lean4Expr::Var("b".to_string()),
527 justification: Lean4Expr::Var("h".to_string()),
528 };
529 let s = step.to_string();
530 assert!(s.contains("a = b := h"));
531 }
532 #[test]
533 pub(super) fn test_module_doc_emit() {
534 let mut file = Lean4File::new();
535 file.module_doc = Some("This is a module.".to_string());
536 let out = file.emit();
537 assert!(out.contains("/-!"));
538 assert!(out.contains("This is a module."));
539 assert!(out.contains("-/"));
540 }
541 #[test]
542 pub(super) fn test_structure_with_extends() {
543 let mut s = Lean4Structure::simple("Point3D", vec![("z".to_string(), Lean4Type::Float)]);
544 s.extends.push("Point".to_string());
545 let out = s.emit();
546 assert!(out.contains("extends Point"));
547 }
548 #[test]
549 pub(super) fn test_inductive_with_derives() {
550 let mut ind = Lean4Inductive::simple(
551 "MyType",
552 vec![Lean4Constructor::positional("mk", vec![Lean4Type::Nat])],
553 );
554 ind.derives.push("Repr".to_string());
555 ind.derives.push("DecidableEq".to_string());
556 let out = ind.emit();
557 assert!(out.contains("deriving Repr, DecidableEq"));
558 }
559}
560#[cfg(test)]
561mod L4_infra_tests {
562 use super::*;
563 #[test]
564 pub(super) fn test_pass_config() {
565 let config = L4PassConfig::new("test_pass", L4PassPhase::Transformation);
566 assert!(config.enabled);
567 assert!(config.phase.is_modifying());
568 assert_eq!(config.phase.name(), "transformation");
569 }
570 #[test]
571 pub(super) fn test_pass_stats() {
572 let mut stats = L4PassStats::new();
573 stats.record_run(10, 100, 3);
574 stats.record_run(20, 200, 5);
575 assert_eq!(stats.total_runs, 2);
576 assert!((stats.average_changes_per_run() - 15.0).abs() < 0.01);
577 assert!((stats.success_rate() - 1.0).abs() < 0.01);
578 let s = stats.format_summary();
579 assert!(s.contains("Runs: 2/2"));
580 }
581 #[test]
582 pub(super) fn test_pass_registry() {
583 let mut reg = L4PassRegistry::new();
584 reg.register(L4PassConfig::new("pass_a", L4PassPhase::Analysis));
585 reg.register(L4PassConfig::new("pass_b", L4PassPhase::Transformation).disabled());
586 assert_eq!(reg.total_passes(), 2);
587 assert_eq!(reg.enabled_count(), 1);
588 reg.update_stats("pass_a", 5, 50, 2);
589 let stats = reg.get_stats("pass_a").expect("stats should exist");
590 assert_eq!(stats.total_changes, 5);
591 }
592 #[test]
593 pub(super) fn test_analysis_cache() {
594 let mut cache = L4AnalysisCache::new(10);
595 cache.insert("key1".to_string(), vec![1, 2, 3]);
596 assert!(cache.get("key1").is_some());
597 assert!(cache.get("key2").is_none());
598 assert!((cache.hit_rate() - 0.5).abs() < 0.01);
599 cache.invalidate("key1");
600 assert!(!cache.entries["key1"].valid);
601 assert_eq!(cache.size(), 1);
602 }
603 #[test]
604 pub(super) fn test_worklist() {
605 let mut wl = L4Worklist::new();
606 assert!(wl.push(1));
607 assert!(wl.push(2));
608 assert!(!wl.push(1));
609 assert_eq!(wl.len(), 2);
610 assert_eq!(wl.pop(), Some(1));
611 assert!(!wl.contains(1));
612 assert!(wl.contains(2));
613 }
614 #[test]
615 pub(super) fn test_dominator_tree() {
616 let mut dt = L4DominatorTree::new(5);
617 dt.set_idom(1, 0);
618 dt.set_idom(2, 0);
619 dt.set_idom(3, 1);
620 assert!(dt.dominates(0, 3));
621 assert!(dt.dominates(1, 3));
622 assert!(!dt.dominates(2, 3));
623 assert!(dt.dominates(3, 3));
624 }
625 #[test]
626 pub(super) fn test_liveness() {
627 let mut liveness = L4LivenessInfo::new(3);
628 liveness.add_def(0, 1);
629 liveness.add_use(1, 1);
630 assert!(liveness.defs[0].contains(&1));
631 assert!(liveness.uses[1].contains(&1));
632 }
633 #[test]
634 pub(super) fn test_constant_folding() {
635 assert_eq!(L4ConstantFoldingHelper::fold_add_i64(3, 4), Some(7));
636 assert_eq!(L4ConstantFoldingHelper::fold_div_i64(10, 0), None);
637 assert_eq!(L4ConstantFoldingHelper::fold_div_i64(10, 2), Some(5));
638 assert_eq!(
639 L4ConstantFoldingHelper::fold_bitand_i64(0b1100, 0b1010),
640 0b1000
641 );
642 assert_eq!(L4ConstantFoldingHelper::fold_bitnot_i64(0), -1);
643 }
644 #[test]
645 pub(super) fn test_dep_graph() {
646 let mut g = L4DepGraph::new();
647 g.add_dep(1, 2);
648 g.add_dep(2, 3);
649 g.add_dep(1, 3);
650 assert_eq!(g.dependencies_of(2), vec![1]);
651 let topo = g.topological_sort();
652 assert_eq!(topo.len(), 3);
653 assert!(!g.has_cycle());
654 let pos: std::collections::HashMap<u32, usize> =
655 topo.iter().enumerate().map(|(i, &n)| (n, i)).collect();
656 assert!(pos[&1] < pos[&2]);
657 assert!(pos[&1] < pos[&3]);
658 assert!(pos[&2] < pos[&3]);
659 }
660}
661#[cfg(test)]
662mod l4ext_pass_tests {
663 use super::*;
664 #[test]
665 pub(super) fn test_l4ext_phase_order() {
666 assert_eq!(L4ExtPassPhase::Early.order(), 0);
667 assert_eq!(L4ExtPassPhase::Middle.order(), 1);
668 assert_eq!(L4ExtPassPhase::Late.order(), 2);
669 assert_eq!(L4ExtPassPhase::Finalize.order(), 3);
670 assert!(L4ExtPassPhase::Early.is_early());
671 assert!(!L4ExtPassPhase::Early.is_late());
672 }
673 #[test]
674 pub(super) fn test_l4ext_config_builder() {
675 let c = L4ExtPassConfig::new("p")
676 .with_phase(L4ExtPassPhase::Late)
677 .with_max_iter(50)
678 .with_debug(1);
679 assert_eq!(c.name, "p");
680 assert_eq!(c.max_iterations, 50);
681 assert!(c.is_debug_enabled());
682 assert!(c.enabled);
683 let c2 = c.disabled();
684 assert!(!c2.enabled);
685 }
686 #[test]
687 pub(super) fn test_l4ext_stats() {
688 let mut s = L4ExtPassStats::new();
689 s.visit();
690 s.visit();
691 s.modify();
692 s.iterate();
693 assert_eq!(s.nodes_visited, 2);
694 assert_eq!(s.nodes_modified, 1);
695 assert!(s.changed);
696 assert_eq!(s.iterations, 1);
697 let e = s.efficiency();
698 assert!((e - 0.5).abs() < 1e-9);
699 }
700 #[test]
701 pub(super) fn test_l4ext_registry() {
702 let mut r = L4ExtPassRegistry::new();
703 r.register(L4ExtPassConfig::new("a").with_phase(L4ExtPassPhase::Early));
704 r.register(L4ExtPassConfig::new("b").disabled());
705 assert_eq!(r.len(), 2);
706 assert_eq!(r.enabled_passes().len(), 1);
707 assert_eq!(r.passes_in_phase(&L4ExtPassPhase::Early).len(), 1);
708 }
709 #[test]
710 pub(super) fn test_l4ext_cache() {
711 let mut c = L4ExtCache::new(4);
712 assert!(c.get(99).is_none());
713 c.put(99, vec![1, 2, 3]);
714 let v = c.get(99).expect("v should be present in map");
715 assert_eq!(v, &[1u8, 2, 3]);
716 assert!(c.hit_rate() > 0.0);
717 assert_eq!(c.live_count(), 1);
718 }
719 #[test]
720 pub(super) fn test_l4ext_worklist() {
721 let mut w = L4ExtWorklist::new(10);
722 w.push(5);
723 w.push(3);
724 w.push(5);
725 assert_eq!(w.len(), 2);
726 assert!(w.contains(5));
727 let first = w.pop().expect("first should be available to pop");
728 assert!(!w.contains(first));
729 }
730 #[test]
731 pub(super) fn test_l4ext_dom_tree() {
732 let mut dt = L4ExtDomTree::new(5);
733 dt.set_idom(1, 0);
734 dt.set_idom(2, 0);
735 dt.set_idom(3, 1);
736 dt.set_idom(4, 1);
737 assert!(dt.dominates(0, 3));
738 assert!(dt.dominates(1, 4));
739 assert!(!dt.dominates(2, 3));
740 assert_eq!(dt.depth_of(3), 2);
741 }
742 #[test]
743 pub(super) fn test_l4ext_liveness() {
744 let mut lv = L4ExtLiveness::new(3);
745 lv.add_def(0, 1);
746 lv.add_use(1, 1);
747 assert!(lv.var_is_def_in_block(0, 1));
748 assert!(lv.var_is_used_in_block(1, 1));
749 assert!(!lv.var_is_def_in_block(1, 1));
750 }
751 #[test]
752 pub(super) fn test_l4ext_const_folder() {
753 let mut cf = L4ExtConstFolder::new();
754 assert_eq!(cf.add_i64(3, 4), Some(7));
755 assert_eq!(cf.div_i64(10, 0), None);
756 assert_eq!(cf.mul_i64(6, 7), Some(42));
757 assert_eq!(cf.and_i64(0b1100, 0b1010), 0b1000);
758 assert_eq!(cf.fold_count(), 3);
759 assert_eq!(cf.failure_count(), 1);
760 }
761 #[test]
762 pub(super) fn test_l4ext_dep_graph() {
763 let mut g = L4ExtDepGraph::new(4);
764 g.add_edge(0, 1);
765 g.add_edge(1, 2);
766 g.add_edge(2, 3);
767 assert!(!g.has_cycle());
768 assert_eq!(g.topo_sort(), Some(vec![0, 1, 2, 3]));
769 assert_eq!(g.reachable(0).len(), 4);
770 let sccs = g.scc();
771 assert_eq!(sccs.len(), 4);
772 }
773}