1use std::collections::HashMap;
18
19use crate::arena::Arena;
20use crate::ast::stmt::{BinaryOpKind, Expr, Literal, Stmt};
21use crate::intern::{Interner, Symbol};
22use crate::optimize::OracleFacts;
23
24use super::extract::{best_tree, best_tree_filtered, ExtractTree};
25use super::{rules, CompilerEGraph, CompilerENode, NodeId};
26
27pub struct Converter<'a> {
28 pub eg: CompilerEGraph,
29 opaques: Vec<&'a Expr<'a>>,
30 symbols: HashMap<u32, Symbol>,
31 pending_len_facts: Vec<(NodeId, (i64, i64))>,
36 saw_opaque: bool,
37 versions: HashMap<u32, u32>,
41 coll_read_vars: std::collections::HashSet<u32>,
46 suppressed: std::collections::HashSet<u32>,
54}
55
56impl<'a> Converter<'a> {
57 pub fn new() -> Self {
58 Converter {
59 eg: CompilerEGraph::new(),
60 opaques: Vec::new(),
61 symbols: HashMap::new(),
62 pending_len_facts: Vec::new(),
63 saw_opaque: false,
64 versions: HashMap::new(),
65 coll_read_vars: std::collections::HashSet::new(),
66 suppressed: std::collections::HashSet::new(),
67 }
68 }
69
70 fn version_of(&self, ix: u32) -> u32 {
71 self.versions.get(&ix).copied().unwrap_or(0)
72 }
73
74 fn bump(&mut self, sym: Symbol) {
76 *self.versions.entry(sym.index() as u32).or_insert(0) += 1;
77 }
78
79 fn bump_collection_reads(&mut self) {
83 let vars: Vec<u32> = self.coll_read_vars.iter().copied().collect();
84 for ix in vars {
85 *self.versions.entry(ix).or_insert(0) += 1;
86 }
87 }
88
89 fn note_collection_read(&mut self, collection: &Expr) {
90 if let Expr::Identifier(sym) = collection {
91 self.coll_read_vars.insert(sym.index() as u32);
92 }
93 }
94
95 fn mentions_suppressed(&self, expr: &Expr) -> bool {
100 if self.suppressed.is_empty() {
101 return false;
102 }
103 match expr {
104 Expr::Identifier(sym) => self.suppressed.contains(&(sym.index() as u32)),
105 Expr::BinaryOp { left, right, .. } => {
106 self.mentions_suppressed(left) || self.mentions_suppressed(right)
107 }
108 Expr::Not { operand } => self.mentions_suppressed(operand),
109 Expr::Index { collection, index } => {
110 self.mentions_suppressed(collection) || self.mentions_suppressed(index)
111 }
112 Expr::Length { collection } => self.mentions_suppressed(collection),
113 Expr::Copy { expr } => self.mentions_suppressed(expr),
114 Expr::Slice { collection, start, end } => {
115 self.mentions_suppressed(collection)
116 || self.mentions_suppressed(start)
117 || self.mentions_suppressed(end)
118 }
119 Expr::Contains { collection, value } => {
120 self.mentions_suppressed(collection) || self.mentions_suppressed(value)
121 }
122 _ => false,
123 }
124 }
125
126 pub fn finish_facts(&mut self) {
128 if !self.saw_opaque {
129 for (id, (lo, hi)) in std::mem::take(&mut self.pending_len_facts) {
130 self.eg.set_int_range(id, lo, hi);
131 }
132 }
133 }
134
135 fn opaque(&mut self, expr: &'a Expr<'a>) -> (NodeId, bool) {
136 self.saw_opaque = true;
137 let ix = self.opaques.len() as u32;
138 self.opaques.push(expr);
139 let id = self.eg.add(CompilerENode::Opaque(ix));
140 if matches!(expr, Expr::List(_)) {
143 self.eg.set_list(id);
144 }
145 (id, false)
146 }
147
148 pub fn to_node(&mut self, expr: &'a Expr<'a>, facts: Option<&OracleFacts>) -> (NodeId, bool) {
151 let (id, stable) = match expr {
152 Expr::Literal(Literal::Number(n)) => (self.eg.add(CompilerENode::Int(*n)), true),
153 Expr::Literal(Literal::Boolean(b)) => (self.eg.add(CompilerENode::Bool(*b)), true),
154 Expr::Literal(Literal::Float(f)) => {
155 (self.eg.add(CompilerENode::Float(f.to_bits())), true)
156 }
157 Expr::Identifier(sym) => {
158 let ix = sym.index() as u32;
159 self.symbols.insert(ix, *sym);
160 let v = self.version_of(ix);
161 let id = self.eg.add(CompilerENode::Var(ix, v));
162 if let Some(f) = facts {
165 if f.expr_is_tracked_collection(expr) {
166 self.eg.set_collection(id);
167 }
168 }
169 (id, true)
170 }
171 Expr::BinaryOp { op: BinaryOpKind::ExactDivide, .. } => self.opaque(expr),
175 Expr::BinaryOp { op: BinaryOpKind::SeqConcat, .. } => self.opaque(expr),
178 Expr::BinaryOp { op: BinaryOpKind::ApproxEq, .. } => self.opaque(expr),
180 Expr::BinaryOp { op: BinaryOpKind::BitAnd | BinaryOpKind::BitOr, .. } => self.opaque(expr),
183 Expr::BinaryOp { op: BinaryOpKind::Pow, .. } => self.opaque(expr),
185 Expr::BinaryOp { op: BinaryOpKind::FloorDivide, .. } => self.opaque(expr),
188 Expr::BinaryOp { op, left, right } => {
189 let (l, ls) = self.to_node(left, facts);
190 let (r, rs) = self.to_node(right, facts);
191 let node = match op {
192 BinaryOpKind::Add => CompilerENode::Add(l, r),
193 BinaryOpKind::Subtract => CompilerENode::Sub(l, r),
194 BinaryOpKind::Multiply => CompilerENode::Mul(l, r),
195 BinaryOpKind::Divide => CompilerENode::Div(l, r),
196 BinaryOpKind::ExactDivide => unreachable!("ExactDivide handled by the opaque arm above"),
197 BinaryOpKind::Pow => unreachable!("Pow handled by the opaque arm above"),
198 BinaryOpKind::FloorDivide => unreachable!("FloorDivide handled by the opaque arm above"),
199 BinaryOpKind::ApproxEq => unreachable!("ApproxEq handled by the opaque arm above"),
200 BinaryOpKind::BitAnd | BinaryOpKind::BitOr => unreachable!("BitAnd/BitOr handled by the opaque arm above"),
201 BinaryOpKind::Modulo => CompilerENode::Mod(l, r),
202 BinaryOpKind::Eq => CompilerENode::Eq(l, r),
203 BinaryOpKind::NotEq => CompilerENode::Ne(l, r),
204 BinaryOpKind::Lt => CompilerENode::Lt(l, r),
205 BinaryOpKind::Gt => CompilerENode::Gt(l, r),
206 BinaryOpKind::LtEq => CompilerENode::Le(l, r),
207 BinaryOpKind::GtEq => CompilerENode::Ge(l, r),
208 BinaryOpKind::And => CompilerENode::And(l, r),
209 BinaryOpKind::Or => CompilerENode::Or(l, r),
210 BinaryOpKind::Concat => CompilerENode::Concat(l, r),
211 BinaryOpKind::SeqConcat => unreachable!("SeqConcat handled by the opaque arm above"),
212 BinaryOpKind::BitXor => CompilerENode::BitXor(l, r),
213 BinaryOpKind::Shl => CompilerENode::Shl(l, r),
214 BinaryOpKind::Shr => CompilerENode::Shr(l, r),
215 };
216 (self.eg.add(node), ls && rs)
217 }
218 Expr::Not { operand } => {
219 let (o, os) = self.to_node(operand, facts);
220 (self.eg.add(CompilerENode::Not(o)), os)
221 }
222 Expr::Index { collection, index } => {
223 self.note_collection_read(collection);
224 let (c, _) = self.to_node(collection, facts);
225 let (i, _) = self.to_node(index, facts);
226 (self.eg.add(CompilerENode::Index(c, i)), false)
228 }
229 Expr::Length { collection } => {
230 self.note_collection_read(collection);
231 let (c, _) = self.to_node(collection, facts);
232 let id = self.eg.add(CompilerENode::Len(c));
233 if let Some(f) = facts {
237 if !self.mentions_suppressed(collection) {
238 if let Some((lo, hi)) = f.expr_len_range(collection) {
239 self.pending_len_facts.push((id, (lo, hi)));
240 }
241 }
242 }
243 (id, false)
244 }
245 Expr::Copy { expr: inner } => {
246 self.note_collection_read(inner);
247 let (c, _) = self.to_node(inner, facts);
248 let id = self.eg.add(CompilerENode::Copy(c));
249 if self.eg.proven_list(c) {
251 self.eg.set_list(id);
252 } else if self.eg.proven_collection(c) {
253 self.eg.set_collection(id);
254 }
255 (id, false)
256 }
257 Expr::Slice { collection, start, end } => {
258 self.note_collection_read(collection);
259 let (c, _) = self.to_node(collection, facts);
260 let (s, _) = self.to_node(start, facts);
261 let (e, _) = self.to_node(end, facts);
262 (self.eg.add(CompilerENode::Slice(c, s, e)), false)
263 }
264 Expr::Contains { collection, value } => {
265 self.note_collection_read(collection);
266 let (c, _) = self.to_node(collection, facts);
267 let (v, _) = self.to_node(value, facts);
268 (self.eg.add(CompilerENode::Contains(c, v)), false)
269 }
270 other => self.opaque(other),
271 };
272 if stable {
273 if let Some(f) = facts {
274 if !self.mentions_suppressed(expr) {
275 if let Some(k) = f.expr_scalar(expr) {
276 self.eg.set_scalar(id, k);
277 }
278 if let Some((lo, hi)) = f.expr_int_range(expr) {
279 self.eg.set_int_range(id, lo, hi);
280 }
281 }
282 }
283 }
284 (id, stable)
285 }
286
287 pub fn tree_to_expr(
288 &self,
289 t: &ExtractTree,
290 arena: &'a Arena<Expr<'a>>,
291 ) -> &'a Expr<'a> {
292 let bin = |op: BinaryOpKind, s: &Self| -> &'a Expr<'a> {
293 arena.alloc(Expr::BinaryOp {
294 op,
295 left: s.tree_to_expr(&t.children[0], arena),
296 right: s.tree_to_expr(&t.children[1], arena),
297 })
298 };
299 match t.node {
300 CompilerENode::Int(n) => arena.alloc(Expr::Literal(Literal::Number(n))),
301 CompilerENode::Bool(b) => arena.alloc(Expr::Literal(Literal::Boolean(b))),
302 CompilerENode::Float(bits) => {
303 arena.alloc(Expr::Literal(Literal::Float(f64::from_bits(bits))))
304 }
305 CompilerENode::Var(ix, _) => arena.alloc(Expr::Identifier(self.symbols[&ix])),
306 CompilerENode::Opaque(ix) => self.opaques[ix as usize],
307 CompilerENode::Not(_) => arena.alloc(Expr::Not {
308 operand: self.tree_to_expr(&t.children[0], arena),
309 }),
310 CompilerENode::Len(_) => arena.alloc(Expr::Length {
311 collection: self.tree_to_expr(&t.children[0], arena),
312 }),
313 CompilerENode::Index(..) => arena.alloc(Expr::Index {
314 collection: self.tree_to_expr(&t.children[0], arena),
315 index: self.tree_to_expr(&t.children[1], arena),
316 }),
317 CompilerENode::Copy(_) => arena.alloc(Expr::Copy {
318 expr: self.tree_to_expr(&t.children[0], arena),
319 }),
320 CompilerENode::Slice(..) => arena.alloc(Expr::Slice {
321 collection: self.tree_to_expr(&t.children[0], arena),
322 start: self.tree_to_expr(&t.children[1], arena),
323 end: self.tree_to_expr(&t.children[2], arena),
324 }),
325 CompilerENode::Contains(..) => arena.alloc(Expr::Contains {
326 collection: self.tree_to_expr(&t.children[0], arena),
327 value: self.tree_to_expr(&t.children[1], arena),
328 }),
329 CompilerENode::Add(..) => bin(BinaryOpKind::Add, self),
330 CompilerENode::Sub(..) => bin(BinaryOpKind::Subtract, self),
331 CompilerENode::Mul(..) => bin(BinaryOpKind::Multiply, self),
332 CompilerENode::Div(..) => bin(BinaryOpKind::Divide, self),
333 CompilerENode::Mod(..) => bin(BinaryOpKind::Modulo, self),
334 CompilerENode::Shl(..) => bin(BinaryOpKind::Shl, self),
335 CompilerENode::Shr(..) => bin(BinaryOpKind::Shr, self),
336 CompilerENode::BitXor(..) => bin(BinaryOpKind::BitXor, self),
337 CompilerENode::BitAnd(..) => bin(BinaryOpKind::BitAnd, self),
338 CompilerENode::BitOr(..) => bin(BinaryOpKind::BitOr, self),
339 CompilerENode::And(..) => bin(BinaryOpKind::And, self),
340 CompilerENode::Or(..) => bin(BinaryOpKind::Or, self),
341 CompilerENode::Eq(..) => bin(BinaryOpKind::Eq, self),
342 CompilerENode::Ne(..) => bin(BinaryOpKind::NotEq, self),
343 CompilerENode::Lt(..) => bin(BinaryOpKind::Lt, self),
344 CompilerENode::Le(..) => bin(BinaryOpKind::LtEq, self),
345 CompilerENode::Gt(..) => bin(BinaryOpKind::Gt, self),
346 CompilerENode::Ge(..) => bin(BinaryOpKind::GtEq, self),
347 CompilerENode::Concat(..) => bin(BinaryOpKind::Concat, self),
348 }
349 }
350}
351
352fn expr_struct_eq(a: &Expr, b: &Expr) -> bool {
355 if std::ptr::eq(a, b) {
356 return true;
357 }
358 match (a, b) {
359 (Expr::Literal(Literal::Number(x)), Expr::Literal(Literal::Number(y))) => x == y,
360 (Expr::Literal(Literal::Boolean(x)), Expr::Literal(Literal::Boolean(y))) => x == y,
361 (Expr::Literal(Literal::Float(x)), Expr::Literal(Literal::Float(y))) => {
362 x.to_bits() == y.to_bits()
363 }
364 (Expr::Identifier(x), Expr::Identifier(y)) => x == y,
365 (
366 Expr::BinaryOp { op: oa, left: la, right: ra },
367 Expr::BinaryOp { op: ob, left: lb, right: rb },
368 ) => oa == ob && expr_struct_eq(la, lb) && expr_struct_eq(ra, rb),
369 (Expr::Not { operand: x }, Expr::Not { operand: y }) => expr_struct_eq(x, y),
370 (Expr::Length { collection: x }, Expr::Length { collection: y }) => expr_struct_eq(x, y),
371 (
372 Expr::Index { collection: ca, index: ia },
373 Expr::Index { collection: cb, index: ib },
374 ) => expr_struct_eq(ca, cb) && expr_struct_eq(ia, ib),
375 (Expr::Copy { expr: a }, Expr::Copy { expr: b }) => expr_struct_eq(a, b),
376 (
377 Expr::Slice { collection: ca, start: sa, end: ea },
378 Expr::Slice { collection: cb, start: sb, end: eb },
379 ) => expr_struct_eq(ca, cb) && expr_struct_eq(sa, sb) && expr_struct_eq(ea, eb),
380 (
381 Expr::Contains { collection: ca, value: va },
382 Expr::Contains { collection: cb, value: vb },
383 ) => expr_struct_eq(ca, cb) && expr_struct_eq(va, vb),
384 _ => false,
385 }
386}
387
388fn worth_modeling(expr: &Expr) -> bool {
390 match expr {
391 Expr::BinaryOp { .. } | Expr::Not { .. } => true,
392 Expr::Index { collection, index } => {
395 fusable(collection) || worth_modeling(collection) || worth_modeling(index)
396 }
397 Expr::Length { collection } => fusable(collection) || worth_modeling(collection),
398 Expr::Copy { expr } => fusable(expr) || worth_modeling(expr),
399 Expr::Slice { collection, start, end } => {
400 worth_modeling(collection) || worth_modeling(start) || worth_modeling(end)
401 }
402 Expr::Contains { collection, value } => {
403 worth_modeling(collection) || worth_modeling(value)
404 }
405 _ => false,
406 }
407}
408
409fn fusable(expr: &Expr) -> bool {
411 matches!(expr, Expr::Copy { .. } | Expr::Slice { .. })
412}
413
414pub fn simplify_expr<'a>(
417 expr: &'a Expr<'a>,
418 facts: &OracleFacts,
419 rule_set: &[rules::Rewrite],
420 arena: &'a Arena<Expr<'a>>,
421) -> &'a Expr<'a> {
422 simplify_expr_in(expr, facts, rule_set, arena, &std::collections::HashSet::new())
423}
424
425fn simplify_expr_in<'a>(
429 expr: &'a Expr<'a>,
430 facts: &OracleFacts,
431 rule_set: &[rules::Rewrite],
432 arena: &'a Arena<Expr<'a>>,
433 suppressed: &std::collections::HashSet<u32>,
434) -> &'a Expr<'a> {
435 if !worth_modeling(expr) {
436 return expr;
437 }
438 let mut cv = Converter::new();
439 cv.suppressed = suppressed.clone();
440 let (root, _) = cv.to_node(expr, Some(facts));
441 cv.finish_facts();
442 cv.eg.saturate(rule_set);
443 let tree = best_tree(&mut cv.eg, root);
444 let rebuilt = cv.tree_to_expr(&tree, arena);
445 if expr_struct_eq(rebuilt, expr) {
446 expr
447 } else {
448 rebuilt
449 }
450}
451
452pub fn egraph_stmts<'a>(
459 stmts: Vec<Stmt<'a>>,
460 expr_arena: &'a Arena<Expr<'a>>,
461 stmt_arena: &'a Arena<Stmt<'a>>,
462 interner: &mut Interner,
463) -> Vec<Stmt<'a>> {
464 let facts = crate::optimize::oracle_analyze_with(&stmts, interner);
465 let rule_set = rules::all();
466 walk_stmts(stmts, &facts, &rule_set, expr_arena, stmt_arena, &std::collections::HashSet::new())
467}
468
469fn run_modeled(s: &Stmt) -> bool {
472 matches!(
473 s,
474 Stmt::Let { .. }
475 | Stmt::Set { .. }
476 | Stmt::Show { .. }
477 | Stmt::RuntimeAssert { .. }
478 | Stmt::Return { .. }
479 | Stmt::Push { .. }
480 | Stmt::SetIndex { .. }
481 | Stmt::Call { .. }
482 )
483}
484
485struct SiteRec<'a> {
489 root: NodeId,
490 ctx: HashMap<u32, u32>,
491 opq: (usize, usize),
492 original: &'a Expr<'a>,
493}
494
495fn walk_stmts<'a>(
496 stmts: Vec<Stmt<'a>>,
497 facts: &OracleFacts,
498 rule_set: &[rules::Rewrite],
499 expr_arena: &'a Arena<Expr<'a>>,
500 stmt_arena: &'a Arena<Stmt<'a>>,
501 suppressed: &std::collections::HashSet<u32>,
502) -> Vec<Stmt<'a>> {
503 let mut out: Vec<Stmt<'a>> = Vec::new();
504 let mut run: Vec<Stmt<'a>> = Vec::new();
505 for s in stmts {
506 if run_modeled(&s) {
507 run.push(s);
508 } else {
509 flush_run(&mut run, &mut out, facts, rule_set, expr_arena, suppressed);
510 out.push(walk_stmt(s, facts, rule_set, expr_arena, stmt_arena, suppressed));
511 }
512 }
513 flush_run(&mut run, &mut out, facts, rule_set, expr_arena, suppressed);
514 out
515}
516
517fn flush_run<'a>(
521 run: &mut Vec<Stmt<'a>>,
522 out: &mut Vec<Stmt<'a>>,
523 facts: &OracleFacts,
524 rule_set: &[rules::Rewrite],
525 expr_arena: &'a Arena<Expr<'a>>,
526 suppressed: &std::collections::HashSet<u32>,
527) {
528 if run.is_empty() {
529 return;
530 }
531 let stmts = std::mem::take(run);
532 let mut cv = Converter::new();
533 cv.suppressed = suppressed.clone();
534 let mut sites: Vec<SiteRec<'a>> = Vec::new();
535 let mut plans: Vec<Vec<usize>> = Vec::with_capacity(stmts.len());
537
538 let mut convert_site = |cv: &mut Converter<'a>,
539 sites: &mut Vec<SiteRec<'a>>,
540 e: &'a Expr<'a>|
541 -> usize {
542 let opq_start = cv.opaques.len();
543 let (root, _) = cv.to_node(e, Some(facts));
544 let opq_end = cv.opaques.len();
545 sites.push(SiteRec {
546 root,
547 ctx: cv.versions.clone(),
548 opq: (opq_start, opq_end),
549 original: e,
550 });
551 sites.len() - 1
552 };
553
554 for s in &stmts {
555 let mut plan: Vec<usize> = Vec::new();
556 match s {
557 Stmt::Let { var, value, .. } => {
558 let site = convert_site(&mut cv, &mut sites, value);
559 plan.push(site);
560 let had_opaques = sites[site].opq.0 != sites[site].opq.1;
561 cv.bump(*var);
562 if !had_opaques {
563 let ix = var.index() as u32;
564 cv.symbols.insert(ix, *var);
565 let v = cv.version_of(ix);
566 let vnode = cv.eg.add(CompilerENode::Var(ix, v));
567 let root = sites[site].root;
568 cv.eg.union(vnode, root);
569 } else {
570 cv.bump_collection_reads();
573 }
574 }
575 Stmt::Set { target, value } => {
576 let site = convert_site(&mut cv, &mut sites, value);
577 plan.push(site);
578 let had_opaques = sites[site].opq.0 != sites[site].opq.1;
579 cv.bump(*target);
580 if !had_opaques {
581 let ix = target.index() as u32;
582 cv.symbols.insert(ix, *target);
583 let v = cv.version_of(ix);
584 let vnode = cv.eg.add(CompilerENode::Var(ix, v));
585 let root = sites[site].root;
586 cv.eg.union(vnode, root);
587 } else {
588 cv.bump_collection_reads();
589 }
590 }
591 Stmt::Show { object, .. } => {
592 plan.push(convert_site(&mut cv, &mut sites, object));
593 }
596 Stmt::RuntimeAssert { condition, .. } => {
597 plan.push(convert_site(&mut cv, &mut sites, condition));
598 }
599 Stmt::Return { value } => {
600 if let Some(v) = value {
601 plan.push(convert_site(&mut cv, &mut sites, v));
602 }
603 }
604 Stmt::Push { value, collection } => {
605 plan.push(convert_site(&mut cv, &mut sites, value));
606 if let Expr::Identifier(sym) = collection {
608 cv.bump(*sym);
609 }
610 cv.bump_collection_reads();
611 }
612 Stmt::SetIndex { collection, index, value } => {
613 plan.push(convert_site(&mut cv, &mut sites, index));
614 plan.push(convert_site(&mut cv, &mut sites, value));
615 if let Expr::Identifier(sym) = collection {
616 cv.bump(*sym);
617 }
618 cv.bump_collection_reads();
619 }
620 Stmt::Call { args, .. } => {
621 for a in args {
622 plan.push(convert_site(&mut cv, &mut sites, a));
623 }
624 cv.bump_collection_reads();
626 }
627 _ => unreachable!("run_modeled admitted an unhandled statement"),
628 }
629 plans.push(plan);
630 }
631
632 cv.finish_facts();
633 cv.eg.saturate(rule_set);
634
635 let mut extract = |cv: &mut Converter<'a>, site: usize| -> &'a Expr<'a> {
636 let rec = &sites[site];
637 if !worth_modeling(rec.original) {
638 return rec.original;
639 }
640 let ctx = rec.ctx.clone();
641 let (lo, hi) = rec.opq;
642 let admissible = move |n: &CompilerENode| match *n {
643 CompilerENode::Var(ix, v) => ctx.get(&ix).copied().unwrap_or(0) == v,
644 CompilerENode::Opaque(j) => (lo..hi).contains(&(j as usize)),
645 _ => true,
646 };
647 match best_tree_filtered(&mut cv.eg, rec.root, &admissible) {
648 Some(tree) => {
649 let rebuilt = cv.tree_to_expr(&tree, expr_arena);
650 if expr_struct_eq(rebuilt, rec.original) {
651 rec.original
652 } else {
653 rebuilt
654 }
655 }
656 None => rec.original,
657 }
658 };
659
660 for (s, plan) in stmts.into_iter().zip(plans) {
661 let rebuilt = match s {
662 Stmt::Let { var, ty, value: _, mutable } => Stmt::Let {
663 var,
664 ty,
665 value: extract(&mut cv, plan[0]),
666 mutable,
667 },
668 Stmt::Set { target, value: _ } => {
669 Stmt::Set { target, value: extract(&mut cv, plan[0]) }
670 }
671 Stmt::Show { object: _, recipient } => {
672 Stmt::Show { object: extract(&mut cv, plan[0]), recipient }
673 }
674 Stmt::RuntimeAssert { condition: _ , hard } => {
675 Stmt::RuntimeAssert { condition: extract(&mut cv, plan[0]) , hard }
676 }
677 Stmt::Return { value } => Stmt::Return {
678 value: value.map(|_| extract(&mut cv, plan[0])),
679 },
680 Stmt::Push { value: _, collection } => {
681 Stmt::Push { value: extract(&mut cv, plan[0]), collection }
682 }
683 Stmt::SetIndex { collection, index: _, value: _ } => Stmt::SetIndex {
684 collection,
685 index: extract(&mut cv, plan[0]),
686 value: extract(&mut cv, plan[1]),
687 },
688 Stmt::Call { function, args } => Stmt::Call {
689 function,
690 args: (0..args.len()).map(|k| extract(&mut cv, plan[k])).collect(),
691 },
692 other => other,
693 };
694 out.push(rebuilt);
695 }
696}
697
698fn walk_block<'a>(
699 block: &'a [Stmt<'a>],
700 facts: &OracleFacts,
701 rule_set: &[rules::Rewrite],
702 expr_arena: &'a Arena<Expr<'a>>,
703 stmt_arena: &'a Arena<Stmt<'a>>,
704 suppressed: &std::collections::HashSet<u32>,
705) -> &'a [Stmt<'a>] {
706 let walked = walk_stmts(
707 block.to_vec(),
708 facts,
709 rule_set,
710 expr_arena,
711 stmt_arena,
712 suppressed,
713 );
714 stmt_arena.alloc_slice(walked)
715}
716
717fn collect_mutated(stmts: &[Stmt], out: &mut std::collections::HashSet<u32>) {
723 for s in stmts {
724 match s {
725 Stmt::Set { target, .. } => {
726 out.insert(target.index() as u32);
727 }
728 Stmt::Let { var, .. } => {
729 out.insert(var.index() as u32);
730 }
731 Stmt::If { then_block, else_block, .. } => {
732 collect_mutated(then_block, out);
733 if let Some(e) = else_block {
734 collect_mutated(e, out);
735 }
736 }
737 Stmt::While { body, .. }
738 | Stmt::Repeat { body, .. }
739 | Stmt::Zone { body, .. } => collect_mutated(body, out),
740 Stmt::Concurrent { tasks } | Stmt::Parallel { tasks } => collect_mutated(tasks, out),
741 Stmt::Inspect { arms, .. } => {
742 for a in arms {
743 collect_mutated(a.body, out);
744 }
745 }
746 Stmt::Push { collection, .. }
747 | Stmt::Add { collection, .. }
748 | Stmt::Remove { collection, .. }
749 | Stmt::SetIndex { collection, .. } => {
750 if let Expr::Identifier(sym) = collection {
751 out.insert(sym.index() as u32);
752 }
753 }
754 Stmt::Pop { collection, into } => {
755 if let Expr::Identifier(sym) = collection {
756 out.insert(sym.index() as u32);
757 }
758 if let Some(v) = into {
759 out.insert(v.index() as u32);
760 }
761 }
762 Stmt::SetField { object, .. } => {
763 if let Expr::Identifier(sym) = object {
764 out.insert(sym.index() as u32);
765 }
766 }
767 Stmt::ReadFrom { var, .. } => {
768 out.insert(var.index() as u32);
769 }
770 _ => {}
771 }
772 }
773}
774
775fn walk_stmt<'a>(
776 stmt: Stmt<'a>,
777 facts: &OracleFacts,
778 rule_set: &[rules::Rewrite],
779 expr_arena: &'a Arena<Expr<'a>>,
780 stmt_arena: &'a Arena<Stmt<'a>>,
781 suppressed: &std::collections::HashSet<u32>,
782) -> Stmt<'a> {
783 let se = |e: &'a Expr<'a>| simplify_expr_in(e, facts, rule_set, expr_arena, suppressed);
784 match stmt {
785 Stmt::Let { var, ty, value, mutable } => {
786 Stmt::Let { var, ty, value: se(value), mutable }
787 }
788 Stmt::Set { target, value } => Stmt::Set { target, value: se(value) },
789 Stmt::If { cond, then_block, else_block } => Stmt::If {
790 cond: se(cond),
791 then_block: walk_block(then_block, facts, rule_set, expr_arena, stmt_arena, suppressed),
792 else_block: else_block
793 .map(|b| walk_block(b, facts, rule_set, expr_arena, stmt_arena, suppressed)),
794 },
795 Stmt::While { cond, body, decreasing } => {
796 let mut inner = suppressed.clone();
800 collect_mutated(body, &mut inner);
801 Stmt::While {
802 cond: simplify_expr_in(cond, facts, rule_set, expr_arena, &inner),
803 body: walk_block(body, facts, rule_set, expr_arena, stmt_arena, &inner),
804 decreasing,
805 }
806 }
807 Stmt::Repeat { pattern, iterable, body } => {
808 let mut inner = suppressed.clone();
811 collect_mutated(body, &mut inner);
812 Stmt::Repeat {
813 pattern,
814 iterable: se(iterable),
815 body: walk_block(body, facts, rule_set, expr_arena, stmt_arena, &inner),
816 }
817 }
818 Stmt::FunctionDef {
819 name,
820 params,
821 generics,
822 body,
823 return_type,
824 is_native,
825 native_path,
826 is_exported,
827 export_target,
828 opt_flags,
829 } => Stmt::FunctionDef {
830 name,
831 params,
832 generics,
833 body: walk_block(body, facts, rule_set, expr_arena, stmt_arena, suppressed),
834 return_type,
835 is_native,
836 native_path,
837 is_exported,
838 export_target,
839 opt_flags,
840 },
841 Stmt::Show { object, recipient } => Stmt::Show { object: se(object), recipient },
842 Stmt::Return { value } => Stmt::Return { value: value.map(se) },
843 Stmt::RuntimeAssert { condition, hard } => Stmt::RuntimeAssert { condition: se(condition) , hard },
844 Stmt::Push { value, collection } => Stmt::Push { value: se(value), collection },
845 Stmt::SetField { object, field, value } => {
846 Stmt::SetField { object, field, value: se(value) }
847 }
848 Stmt::SetIndex { collection, index, value } => {
849 Stmt::SetIndex { collection, index: se(index), value: se(value) }
850 }
851 Stmt::Call { function, args } => Stmt::Call {
852 function,
853 args: args.into_iter().map(se).collect(),
854 },
855 Stmt::Give { object, recipient } => {
856 Stmt::Give { object: se(object), recipient: se(recipient) }
857 }
858 Stmt::Inspect { target, arms, has_otherwise } => Stmt::Inspect {
859 target: se(target),
860 arms: arms
861 .into_iter()
862 .map(|arm| crate::ast::stmt::MatchArm {
863 enum_name: arm.enum_name,
864 variant: arm.variant,
865 bindings: arm.bindings,
866 body: walk_block(arm.body, facts, rule_set, expr_arena, stmt_arena, suppressed),
867 })
868 .collect(),
869 has_otherwise,
870 },
871 Stmt::Pop { collection, into } => Stmt::Pop { collection: se(collection), into },
872 Stmt::Add { value, collection } => {
873 Stmt::Add { value: se(value), collection: se(collection) }
874 }
875 Stmt::Remove { value, collection } => {
876 Stmt::Remove { value: se(value), collection: se(collection) }
877 }
878 Stmt::Zone { name, capacity, source_file, body } => Stmt::Zone {
879 name,
880 capacity,
881 source_file,
882 body: walk_block(body, facts, rule_set, expr_arena, stmt_arena, suppressed),
883 },
884 Stmt::Concurrent { tasks } => Stmt::Concurrent {
885 tasks: walk_block(tasks, facts, rule_set, expr_arena, stmt_arena, suppressed),
886 },
887 Stmt::Parallel { tasks } => Stmt::Parallel {
888 tasks: walk_block(tasks, facts, rule_set, expr_arena, stmt_arena, suppressed),
889 },
890 Stmt::WriteFile { content, path } => {
891 Stmt::WriteFile { content: se(content), path: se(path) }
892 }
893 Stmt::SendMessage { message, destination, compression, cached, unchecked, layout, shared, computed, indexed, deduped } => {
894 Stmt::SendMessage { message: se(message), destination: se(destination), compression, cached, unchecked, layout, shared, computed, indexed, deduped }
895 }
896 Stmt::StreamMessage { values, destination } => {
897 Stmt::StreamMessage { values: se(values), destination: se(destination) }
898 }
899 Stmt::IncreaseCrdt { object, field, amount } => {
900 Stmt::IncreaseCrdt { object: se(object), field, amount: se(amount) }
901 }
902 Stmt::DecreaseCrdt { object, field, amount } => {
903 Stmt::DecreaseCrdt { object: se(object), field, amount: se(amount) }
904 }
905 Stmt::Sleep { milliseconds } => Stmt::Sleep { milliseconds: se(milliseconds) },
906 Stmt::MergeCrdt { source, target } => {
907 Stmt::MergeCrdt { source: se(source), target: se(target) }
908 }
909 other => other,
910 }
911}