1use super::types::{
6 ZigAllocatorKind, ZigAllocatorUsage, ZigAnalysisCache, ZigAsyncFn, ZigBackend,
7 ZigBuildConfiguration, ZigComptime, ZigConstantFoldingHelper, ZigDepGraph, ZigDominatorTree,
8 ZigErrorSet, ZigExpr, ZigExtCache, ZigExtConstFolder, ZigExtDepGraph, ZigExtDomTree,
9 ZigExtLiveness, ZigExtPassConfig, ZigExtPassPhase, ZigExtPassRegistry, ZigExtPassStats,
10 ZigExtWorklist, ZigFn, ZigGenericFn, ZigImport, ZigLivenessInfo, ZigModule, ZigOptimizeMode,
11 ZigPackedStruct, ZigPassConfig, ZigPassPhase, ZigPassRegistry, ZigPassStats, ZigSliceOps,
12 ZigStmt, ZigStruct, ZigTaggedUnion, ZigTestBlock, ZigType, ZigWorklist,
13};
14
15#[cfg(test)]
16mod tests {
17 use super::*;
18 #[test]
19 pub(super) fn test_zig_type_codegen_primitives() {
20 assert_eq!(ZigType::Void.codegen(), "void");
21 assert_eq!(ZigType::Bool.codegen(), "bool");
22 assert_eq!(ZigType::U8.codegen(), "u8");
23 assert_eq!(ZigType::U64.codegen(), "u64");
24 assert_eq!(ZigType::I64.codegen(), "i64");
25 assert_eq!(ZigType::F64.codegen(), "f64");
26 assert_eq!(ZigType::Anyopaque.codegen(), "anyopaque");
27 }
28 #[test]
29 pub(super) fn test_zig_type_codegen_composite() {
30 let ptr = ZigType::Ptr(Box::new(ZigType::U8));
31 assert_eq!(ptr.codegen(), "*u8");
32 let slice = ZigType::Slice(Box::new(ZigType::I64));
33 assert_eq!(slice.codegen(), "[]i64");
34 let opt = ZigType::Optional(Box::new(ZigType::Bool));
35 assert_eq!(opt.codegen(), "?bool");
36 let eu = ZigType::ErrorUnion(Box::new(ZigType::Void));
37 assert_eq!(eu.codegen(), "!void");
38 }
39 #[test]
40 pub(super) fn test_zig_type_fn() {
41 let fn_ty = ZigType::Fn(vec![ZigType::U64, ZigType::Bool], Box::new(ZigType::I64));
42 let out = fn_ty.codegen();
43 assert!(out.contains("fn("));
44 assert!(out.contains("u64"));
45 assert!(out.contains("bool"));
46 assert!(out.contains("i64"));
47 }
48 #[test]
49 pub(super) fn test_zig_expr_literals() {
50 assert_eq!(ZigExpr::IntLit(42).codegen(), "42");
51 assert_eq!(ZigExpr::BoolLit(true).codegen(), "true");
52 assert_eq!(ZigExpr::BoolLit(false).codegen(), "false");
53 assert_eq!(ZigExpr::NullLit.codegen(), "null");
54 assert_eq!(
55 ZigExpr::StringLit("hello".to_string()).codegen(),
56 "\"hello\""
57 );
58 assert_eq!(ZigExpr::Ident("foo".to_string()).codegen(), "foo");
59 }
60 #[test]
61 pub(super) fn test_zig_expr_binop() {
62 let expr = ZigExpr::BinOp {
63 op: "+".to_string(),
64 lhs: Box::new(ZigExpr::IntLit(1)),
65 rhs: Box::new(ZigExpr::IntLit(2)),
66 };
67 assert_eq!(expr.codegen(), "(1 + 2)");
68 }
69 #[test]
70 pub(super) fn test_zig_expr_call() {
71 let expr = ZigExpr::Call {
72 callee: Box::new(ZigExpr::Ident("foo".to_string())),
73 args: vec![ZigExpr::IntLit(1), ZigExpr::IntLit(2)],
74 };
75 assert_eq!(expr.codegen(), "foo(1, 2)");
76 }
77 #[test]
78 pub(super) fn test_zig_fn_codegen() {
79 let mut f = ZigFn::new("add", ZigType::U64);
80 f.add_param("a", ZigType::U64);
81 f.add_param("b", ZigType::U64);
82 f.add_stmt(ZigStmt::Return(Some(ZigExpr::BinOp {
83 op: "+".to_string(),
84 lhs: Box::new(ZigExpr::Ident("a".to_string())),
85 rhs: Box::new(ZigExpr::Ident("b".to_string())),
86 })));
87 let out = f.codegen();
88 assert!(out.contains("fn add("));
89 assert!(out.contains("a: u64"));
90 assert!(out.contains("b: u64"));
91 assert!(out.contains("u64"));
92 assert!(out.contains("return"));
93 }
94 #[test]
95 pub(super) fn test_zig_struct_codegen() {
96 let mut s = ZigStruct::new("Point");
97 s.add_field("x", ZigType::F64);
98 s.add_field("y", ZigType::F64);
99 let out = s.codegen();
100 assert!(out.contains("const Point = struct"));
101 assert!(out.contains("x: f64"));
102 assert!(out.contains("y: f64"));
103 }
104 #[test]
105 pub(super) fn test_zig_module_codegen() {
106 let mut m = ZigModule::new("mymod");
107 m.add_import("std");
108 let mut s = ZigStruct::new("Foo");
109 s.add_field("val", ZigType::U64);
110 m.add_struct(s);
111 let mut f = ZigFn::new("bar", ZigType::Void);
112 f.add_stmt(ZigStmt::Return(None));
113 m.add_fn(f);
114 let out = m.codegen();
115 assert!(out.contains("@import(\"std\")"));
116 assert!(out.contains("const Foo = struct"));
117 assert!(out.contains("fn bar()"));
118 }
119 #[test]
120 pub(super) fn test_compile_name_sanitize() {
121 assert_eq!(ZigBackend::compile_name("Foo.bar"), "Foo_bar");
122 assert_eq!(ZigBackend::compile_name("123abc"), "ox_123abc");
123 assert_eq!(ZigBackend::compile_name(""), "ox_empty");
124 assert_eq!(ZigBackend::compile_name("fn"), "ox_fn");
125 assert_eq!(ZigBackend::compile_name("const"), "ox_const");
126 assert_eq!(ZigBackend::compile_name("my_var"), "my_var");
127 }
128}
129#[allow(dead_code)]
130pub fn zig_builtin_type_of(expr: &str) -> String {
131 format!("@TypeOf({})", expr)
132}
133#[allow(dead_code)]
134pub fn zig_builtin_int_cast(ty: &str, val: &str) -> String {
135 format!("@intCast({}, {})", ty, val)
136}
137#[allow(dead_code)]
138pub fn zig_builtin_float_cast(ty: &str, val: &str) -> String {
139 format!("@floatCast({}, {})", ty, val)
140}
141#[allow(dead_code)]
142pub fn zig_builtin_ptrcast(ty: &str, val: &str) -> String {
143 format!("@ptrCast({}, {})", ty, val)
144}
145#[allow(dead_code)]
146pub fn zig_builtin_size_of(ty: &str) -> String {
147 format!("@sizeOf({})", ty)
148}
149#[allow(dead_code)]
150pub fn zig_builtin_align_of(ty: &str) -> String {
151 format!("@alignOf({})", ty)
152}
153#[allow(dead_code)]
154pub fn zig_builtin_has_decl(ty: &str, name: &str) -> String {
155 format!("@hasDecl({}, \"{}\")", ty, name)
156}
157#[allow(dead_code)]
158pub fn zig_builtin_field(val: &str, field: &str) -> String {
159 format!("@field({}, \"{}\")", val, field)
160}
161#[cfg(test)]
162mod zig_extended_tests {
163 use super::*;
164 #[test]
165 pub(super) fn test_error_set() {
166 let mut es = ZigErrorSet::new("FileError");
167 es.add_error("NotFound");
168 es.add_error("AccessDenied");
169 let s = es.emit();
170 assert!(s.contains("const FileError = error{"));
171 assert!(s.contains("NotFound"));
172 assert!(s.contains("AccessDenied"));
173 }
174 #[test]
175 pub(super) fn test_tagged_union() {
176 let mut u = ZigTaggedUnion::new("Shape");
177 u.add_field("circle", None);
178 u.add_field("rectangle", None);
179 let s = u.emit();
180 assert!(s.contains("const Shape = union"));
181 assert!(s.contains("circle"));
182 }
183 #[test]
184 pub(super) fn test_packed_struct() {
185 let mut ps = ZigPackedStruct::new("Flags");
186 ps.add_field("enabled", ZigType::Bool, Some(1));
187 ps.add_field("value", ZigType::Int, Some(7));
188 assert_eq!(ps.total_bits(), 8);
189 let s = ps.emit();
190 assert!(s.contains("const Flags = packed struct"));
191 assert!(s.contains("u1"));
192 assert!(s.contains("u7"));
193 }
194 #[test]
195 pub(super) fn test_generic_fn() {
196 let f = ZigGenericFn::new("max")
197 .type_param("T")
198 .param("a", "T")
199 .param("b", "T")
200 .returns("T");
201 let s = f.emit();
202 assert!(s.contains("fn max("));
203 assert!(s.contains("comptime T: type"));
204 }
205 #[test]
206 pub(super) fn test_builtins() {
207 assert_eq!(zig_builtin_size_of("u32"), "@sizeOf(u32)");
208 assert_eq!(zig_builtin_type_of("x"), "@TypeOf(x)");
209 assert_eq!(zig_builtin_int_cast("u8", "val"), "@intCast(u8, val)");
210 }
211 #[test]
212 pub(super) fn test_test_block() {
213 let t = ZigTestBlock::new("basic add").add_expect("1 + 1", "2");
214 let s = t.emit();
215 assert!(s.contains("test \"basic add\""));
216 assert!(s.contains("expectEqual"));
217 }
218 #[test]
219 pub(super) fn test_allocator() {
220 let alloc = ZigAllocatorUsage::new(ZigAllocatorKind::GeneralPurpose, "gpa");
221 let init = alloc.emit_init();
222 assert!(init.contains("GeneralPurposeAllocator"));
223 assert!(init.contains("gpa"));
224 let iface = alloc.emit_interface_call();
225 assert!(iface.contains("allocator()"));
226 }
227 #[test]
228 pub(super) fn test_slice_ops() {
229 assert_eq!(ZigSliceOps::len_expr("items"), "items.len");
230 assert_eq!(ZigSliceOps::index_expr("arr", "i"), "arr[i]");
231 assert_eq!(ZigSliceOps::slice_expr("buf", "0", "n"), "buf[0..n]");
232 assert_eq!(ZigSliceOps::eql("a", "b"), "std.mem.eql(u8, a, b)");
233 }
234 #[test]
235 pub(super) fn test_import() {
236 let imp = ZigImport::std();
237 assert_eq!(imp.emit(), "const std = @import(\"std\");");
238 }
239 #[test]
240 pub(super) fn test_build_configuration() {
241 let cfg = ZigBuildConfiguration::new("src/main.zig").optimize(ZigOptimizeMode::ReleaseFast);
242 let build_zig = cfg.emit_build_zig();
243 assert!(build_zig.contains("pub fn build"));
244 assert!(build_zig.contains("src/main.zig"));
245 }
246 #[test]
247 pub(super) fn test_async_fn() {
248 let f = ZigAsyncFn::new("fetchData").param("url", "[]const u8");
249 let s = f.emit();
250 assert!(s.contains("async fn fetchData"));
251 assert!(s.contains("url: []const u8"));
252 let await_call = f.emit_await_call(&["\"https://example.com\""]);
253 assert!(await_call.contains("await async fetchData"));
254 }
255 #[test]
256 pub(super) fn test_comptime_block() {
257 let ct = ZigComptime::new();
258 let s = ct.emit();
259 assert!(s.starts_with("comptime {"));
260 }
261}
262#[cfg(test)]
263mod Zig_infra_tests {
264 use super::*;
265 #[test]
266 pub(super) fn test_pass_config() {
267 let config = ZigPassConfig::new("test_pass", ZigPassPhase::Transformation);
268 assert!(config.enabled);
269 assert!(config.phase.is_modifying());
270 assert_eq!(config.phase.name(), "transformation");
271 }
272 #[test]
273 pub(super) fn test_pass_stats() {
274 let mut stats = ZigPassStats::new();
275 stats.record_run(10, 100, 3);
276 stats.record_run(20, 200, 5);
277 assert_eq!(stats.total_runs, 2);
278 assert!((stats.average_changes_per_run() - 15.0).abs() < 0.01);
279 assert!((stats.success_rate() - 1.0).abs() < 0.01);
280 let s = stats.format_summary();
281 assert!(s.contains("Runs: 2/2"));
282 }
283 #[test]
284 pub(super) fn test_pass_registry() {
285 let mut reg = ZigPassRegistry::new();
286 reg.register(ZigPassConfig::new("pass_a", ZigPassPhase::Analysis));
287 reg.register(ZigPassConfig::new("pass_b", ZigPassPhase::Transformation).disabled());
288 assert_eq!(reg.total_passes(), 2);
289 assert_eq!(reg.enabled_count(), 1);
290 reg.update_stats("pass_a", 5, 50, 2);
291 let stats = reg.get_stats("pass_a").expect("stats should exist");
292 assert_eq!(stats.total_changes, 5);
293 }
294 #[test]
295 pub(super) fn test_analysis_cache() {
296 let mut cache = ZigAnalysisCache::new(10);
297 cache.insert("key1".to_string(), vec![1, 2, 3]);
298 assert!(cache.get("key1").is_some());
299 assert!(cache.get("key2").is_none());
300 assert!((cache.hit_rate() - 0.5).abs() < 0.01);
301 cache.invalidate("key1");
302 assert!(!cache.entries["key1"].valid);
303 assert_eq!(cache.size(), 1);
304 }
305 #[test]
306 pub(super) fn test_worklist() {
307 let mut wl = ZigWorklist::new();
308 assert!(wl.push(1));
309 assert!(wl.push(2));
310 assert!(!wl.push(1));
311 assert_eq!(wl.len(), 2);
312 assert_eq!(wl.pop(), Some(1));
313 assert!(!wl.contains(1));
314 assert!(wl.contains(2));
315 }
316 #[test]
317 pub(super) fn test_dominator_tree() {
318 let mut dt = ZigDominatorTree::new(5);
319 dt.set_idom(1, 0);
320 dt.set_idom(2, 0);
321 dt.set_idom(3, 1);
322 assert!(dt.dominates(0, 3));
323 assert!(dt.dominates(1, 3));
324 assert!(!dt.dominates(2, 3));
325 assert!(dt.dominates(3, 3));
326 }
327 #[test]
328 pub(super) fn test_liveness() {
329 let mut liveness = ZigLivenessInfo::new(3);
330 liveness.add_def(0, 1);
331 liveness.add_use(1, 1);
332 assert!(liveness.defs[0].contains(&1));
333 assert!(liveness.uses[1].contains(&1));
334 }
335 #[test]
336 pub(super) fn test_constant_folding() {
337 assert_eq!(ZigConstantFoldingHelper::fold_add_i64(3, 4), Some(7));
338 assert_eq!(ZigConstantFoldingHelper::fold_div_i64(10, 0), None);
339 assert_eq!(ZigConstantFoldingHelper::fold_div_i64(10, 2), Some(5));
340 assert_eq!(
341 ZigConstantFoldingHelper::fold_bitand_i64(0b1100, 0b1010),
342 0b1000
343 );
344 assert_eq!(ZigConstantFoldingHelper::fold_bitnot_i64(0), -1);
345 }
346 #[test]
347 pub(super) fn test_dep_graph() {
348 let mut g = ZigDepGraph::new();
349 g.add_dep(1, 2);
350 g.add_dep(2, 3);
351 g.add_dep(1, 3);
352 assert_eq!(g.dependencies_of(2), vec![1]);
353 let topo = g.topological_sort();
354 assert_eq!(topo.len(), 3);
355 assert!(!g.has_cycle());
356 let pos: std::collections::HashMap<u32, usize> =
357 topo.iter().enumerate().map(|(i, &n)| (n, i)).collect();
358 assert!(pos[&1] < pos[&2]);
359 assert!(pos[&1] < pos[&3]);
360 assert!(pos[&2] < pos[&3]);
361 }
362}
363#[cfg(test)]
364mod zigext_pass_tests {
365 use super::*;
366 #[test]
367 pub(super) fn test_zigext_phase_order() {
368 assert_eq!(ZigExtPassPhase::Early.order(), 0);
369 assert_eq!(ZigExtPassPhase::Middle.order(), 1);
370 assert_eq!(ZigExtPassPhase::Late.order(), 2);
371 assert_eq!(ZigExtPassPhase::Finalize.order(), 3);
372 assert!(ZigExtPassPhase::Early.is_early());
373 assert!(!ZigExtPassPhase::Early.is_late());
374 }
375 #[test]
376 pub(super) fn test_zigext_config_builder() {
377 let c = ZigExtPassConfig::new("p")
378 .with_phase(ZigExtPassPhase::Late)
379 .with_max_iter(50)
380 .with_debug(1);
381 assert_eq!(c.name, "p");
382 assert_eq!(c.max_iterations, 50);
383 assert!(c.is_debug_enabled());
384 assert!(c.enabled);
385 let c2 = c.disabled();
386 assert!(!c2.enabled);
387 }
388 #[test]
389 pub(super) fn test_zigext_stats() {
390 let mut s = ZigExtPassStats::new();
391 s.visit();
392 s.visit();
393 s.modify();
394 s.iterate();
395 assert_eq!(s.nodes_visited, 2);
396 assert_eq!(s.nodes_modified, 1);
397 assert!(s.changed);
398 assert_eq!(s.iterations, 1);
399 let e = s.efficiency();
400 assert!((e - 0.5).abs() < 1e-9);
401 }
402 #[test]
403 pub(super) fn test_zigext_registry() {
404 let mut r = ZigExtPassRegistry::new();
405 r.register(ZigExtPassConfig::new("a").with_phase(ZigExtPassPhase::Early));
406 r.register(ZigExtPassConfig::new("b").disabled());
407 assert_eq!(r.len(), 2);
408 assert_eq!(r.enabled_passes().len(), 1);
409 assert_eq!(r.passes_in_phase(&ZigExtPassPhase::Early).len(), 1);
410 }
411 #[test]
412 pub(super) fn test_zigext_cache() {
413 let mut c = ZigExtCache::new(4);
414 assert!(c.get(99).is_none());
415 c.put(99, vec![1, 2, 3]);
416 let v = c.get(99).expect("v should be present in map");
417 assert_eq!(v, &[1u8, 2, 3]);
418 assert!(c.hit_rate() > 0.0);
419 assert_eq!(c.live_count(), 1);
420 }
421 #[test]
422 pub(super) fn test_zigext_worklist() {
423 let mut w = ZigExtWorklist::new(10);
424 w.push(5);
425 w.push(3);
426 w.push(5);
427 assert_eq!(w.len(), 2);
428 assert!(w.contains(5));
429 let first = w.pop().expect("first should be available to pop");
430 assert!(!w.contains(first));
431 }
432 #[test]
433 pub(super) fn test_zigext_dom_tree() {
434 let mut dt = ZigExtDomTree::new(5);
435 dt.set_idom(1, 0);
436 dt.set_idom(2, 0);
437 dt.set_idom(3, 1);
438 dt.set_idom(4, 1);
439 assert!(dt.dominates(0, 3));
440 assert!(dt.dominates(1, 4));
441 assert!(!dt.dominates(2, 3));
442 assert_eq!(dt.depth_of(3), 2);
443 }
444 #[test]
445 pub(super) fn test_zigext_liveness() {
446 let mut lv = ZigExtLiveness::new(3);
447 lv.add_def(0, 1);
448 lv.add_use(1, 1);
449 assert!(lv.var_is_def_in_block(0, 1));
450 assert!(lv.var_is_used_in_block(1, 1));
451 assert!(!lv.var_is_def_in_block(1, 1));
452 }
453 #[test]
454 pub(super) fn test_zigext_const_folder() {
455 let mut cf = ZigExtConstFolder::new();
456 assert_eq!(cf.add_i64(3, 4), Some(7));
457 assert_eq!(cf.div_i64(10, 0), None);
458 assert_eq!(cf.mul_i64(6, 7), Some(42));
459 assert_eq!(cf.and_i64(0b1100, 0b1010), 0b1000);
460 assert_eq!(cf.fold_count(), 3);
461 assert_eq!(cf.failure_count(), 1);
462 }
463 #[test]
464 pub(super) fn test_zigext_dep_graph() {
465 let mut g = ZigExtDepGraph::new(4);
466 g.add_edge(0, 1);
467 g.add_edge(1, 2);
468 g.add_edge(2, 3);
469 assert!(!g.has_cycle());
470 assert_eq!(g.topo_sort(), Some(vec![0, 1, 2, 3]));
471 assert_eq!(g.reachable(0).len(), 4);
472 let sccs = g.scc();
473 assert_eq!(sccs.len(), 4);
474 }
475}