1use std::borrow::Cow;
16use std::collections::HashSet;
17
18mod settle;
19
20use super::Span;
21use super::aborts::{cast_message, overflow_message, zero_divisor_message};
22use super::diagnostics::{Result, unsupported};
23use super::ir::{
24 Case, Comparison, Ctor, DataDecl, Decl, Effect, Expr, Field, FnDecl, LitValue, Main, Node,
25 Param, Pattern, Program, Prop, TheoremDecl,
26};
27use super::surface::BinaryOp;
28use super::types::{BOOL, INT, NAT, STRING, Type, data};
29use settle::{aborts, division, fixed_max, fixed_min, representation, settle_program, total};
30fn effect_operand_any(effect: &Effect, test: &dyn Fn(&Expr) -> bool) -> bool {
32 match effect {
33 Effect::Let { value, .. } => test(value),
34 Effect::Assert { prop, .. } => prop_any(prop, test),
35 Effect::Print { expr, .. } | Effect::Output { expr, .. } => test(expr),
36 Effect::Unwrap { pair, .. } => test(pair),
37 }
38}
39
40pub fn thread_output(source: &Program) -> Result<Cow<'_, Program>> {
47 let program = settle_program(source);
48 let mut effectful = HashSet::new();
49 let mut grew = true;
50 while grew {
51 grew = false;
52 for entry in &program.declarations {
53 if let Decl::Fn(function) = entry
54 && !effectful.contains(&function.full_name)
55 && effects(&function.body, &effectful)
56 {
57 effectful.insert(function.full_name.clone());
58 grew = true;
59 }
60 }
61 }
62 let main_effects = program.main.as_ref().is_some_and(|main| {
63 main.effects
64 .iter()
65 .any(|effect| effect_operand_any(effect, &|e| effects(e, &effectful)))
66 });
67 if effectful.is_empty() && !main_effects {
68 return Ok(Cow::Owned(program));
69 }
70 let can_abort = program.declarations.iter().any(|entry| match entry {
71 Decl::Fn(function) => aborting(&function.body),
72 Decl::Theorem(theorem) => prop_any(&theorem.prop, &aborting),
73 Decl::Data(_) => false,
74 }) || program.main.as_ref().is_some_and(|main| {
75 main.effects
76 .iter()
77 .any(|effect| effect_operand_any(effect, &aborting))
78 });
79 Threader {
80 source: &program,
81 effectful,
82 can_abort,
83 pairs: Vec::new(),
84 count: 0,
85 }
86 .program()
87 .map(Cow::Owned)
88}
89
90fn effects(expr: &Expr, effectful: &HashSet<String>) -> bool {
91 match &expr.node {
92 Node::Print { .. } => true,
93 Node::Call { func, .. } if effectful.contains(func) => true,
94 _ if aborts(expr) => true,
95 _ => expr
96 .children()
97 .into_iter()
98 .any(|child| effects(child, effectful)),
99 }
100}
101
102fn aborting(expr: &Expr) -> bool {
103 aborts(expr) || expr.children().into_iter().any(aborting)
104}
105
106fn prop_any(prop: &Prop, test: &dyn Fn(&Expr) -> bool) -> bool {
107 match prop {
108 Prop::Forall { body, .. } => prop_any(body, test),
109 Prop::And { left, right } | Prop::Or { left, right } | Prop::Implies { left, right } => {
110 prop_any(left, test) || prop_any(right, test)
111 }
112 Prop::Not { arg } => prop_any(arg, test),
113 Prop::Bool { expr } => test(expr),
114 other => other
115 .comparison()
116 .is_some_and(|(_, comparison)| test(&comparison.left) || test(&comparison.right)),
117 }
118}
119
120fn literal(ty: Type, value: &impl ToString) -> Expr {
121 Expr::lit(ty, LitValue::Text(value.to_string()))
122}
123
124fn compare(op: BinaryOp, left: Expr, right: Expr) -> Expr {
125 let domain = representation(&left.ty);
126 Expr::new(
127 Node::Binary {
128 op,
129 left: Box::new(left),
130 right: Box::new(right),
131 domain: Some(domain),
132 semantics: None,
133 rounding: None,
134 by_zero: None,
135 },
136 BOOL,
137 )
138}
139
140fn logical(op: BinaryOp, left: Expr, right: Expr) -> Expr {
141 Expr::new(
142 Node::Binary {
143 op,
144 left: Box::new(left),
145 right: Box::new(right),
146 domain: None,
147 semantics: None,
148 rounding: None,
149 by_zero: None,
150 },
151 BOOL,
152 )
153}
154
155fn let_in(name: String, value: Expr, body: Expr) -> Expr {
156 let ty = body.ty.clone();
157 Expr::new(
158 Node::Let {
159 name,
160 value: Box::new(value),
161 body: Box::new(body),
162 },
163 ty,
164 )
165}
166
167const fn out_nil() -> Expr {
168 Expr::new(Node::OutNil, Type::Output)
169}
170
171struct Frame {
173 scrutinee: Expr,
174 data: String,
175 out: String,
176 value: String,
177}
178
179struct Threader<'p> {
180 source: &'p Program,
181 effectful: HashSet<String>,
182 can_abort: bool,
183 pairs: Vec<(String, DataDecl)>,
185 count: usize,
186}
187
188impl Threader<'_> {
189 fn program(mut self) -> Result<Program> {
190 let mut declarations = Vec::new();
191 for entry in &self.source.declarations {
192 let entry = match entry {
193 Decl::Fn(function) if self.effectful.contains(&function.full_name) => {
194 let out = "ml_out";
195 let mut params = function.params.clone();
196 params.push(Param {
197 name: out.to_owned(),
198 ty: Type::Output,
199 guard: None,
200 default_value: None,
201 });
202 let ret = self.pair_type(&function.ret);
203 let body = self.io(&function.body, Expr::var(out, Type::Output))?;
204 Decl::Fn(FnDecl {
205 params,
206 ret,
207 body,
208 ..function.clone()
209 })
210 }
211 Decl::Theorem(theorem) if self.prop_effects(&theorem.prop) => {
212 Decl::Theorem(TheoremDecl {
213 prop: self.pure_prop(&theorem.prop)?,
214 ..theorem.clone()
215 })
216 }
217 other => other.clone(),
218 };
219 declarations.push(entry);
220 }
221 let main = match &self.source.main {
222 Some(main) => {
223 let mut effects = Vec::new();
224 for effect in &main.effects {
225 effects.extend(self.effect(effect)?);
226 }
227 Some(Main {
228 effects,
229 ..main.clone()
230 })
231 }
232 None => None,
233 };
234 let mut all: Vec<Decl> = self
236 .pairs
237 .into_iter()
238 .map(|(_, entry)| Decl::Data(entry))
239 .collect();
240 all.extend(declarations);
241 Ok(Program {
242 declarations: all,
243 main,
244 output_threaded: true,
245 aborts_threaded: self.can_abort,
246 ..self.source.clone()
247 })
248 }
249
250 fn effects(&self, expr: &Expr) -> bool {
251 effects(expr, &self.effectful)
252 }
253
254 fn prop_effects(&self, prop: &Prop) -> bool {
255 prop_any(prop, &|e| effects(e, &self.effectful))
256 }
257
258 fn fresh(&mut self, prefix: &str) -> String {
259 self.count += 1;
260 format!("ml_{prefix}{}", self.count)
261 }
262
263 fn pair(&mut self, source: &Type) -> String {
268 let ty = representation(source);
269 let key = ty.key();
270 if let Some((_, entry)) = self.pairs.iter().find(|(candidate, _)| *candidate == key) {
271 return entry.full_name.clone();
272 }
273 let name = format!("ml_io{}", self.pairs.len() + 1);
274 let mut ctors = vec![Ctor {
275 name: format!("{name}_mk"),
276 fields: vec![
277 Field {
278 name: "output".to_owned(),
279 ty: Type::Output,
280 },
281 Field {
282 name: "value".to_owned(),
283 ty,
284 },
285 ],
286 }];
287 if self.can_abort {
288 ctors.push(Ctor {
289 name: format!("{name}_abort"),
290 fields: vec![
291 Field {
292 name: "output".to_owned(),
293 ty: Type::Output,
294 },
295 Field {
296 name: "message".to_owned(),
297 ty: STRING,
298 },
299 ],
300 });
301 }
302 self.pairs.push((
303 key,
304 DataDecl {
305 name: name.clone(),
306 ctors,
307 span: None,
308 full_name: name.clone(),
309 module_path: Vec::new(),
310 generated: true,
311 output: true,
312 },
313 ));
314 name
315 }
316
317 fn pair_type(&mut self, ty: &Type) -> Type {
318 data(self.pair(ty))
319 }
320
321 fn make_as(&mut self, out: Expr, value: Expr, ty: &Type) -> Expr {
324 let name = self.pair(ty);
325 Expr::new(
326 Node::Ctor {
327 ctor: format!("{name}_mk"),
328 data: name.clone(),
329 args: vec![out, value],
330 },
331 data(name),
332 )
333 }
334
335 fn make(&mut self, out: Expr, value: Expr) -> Expr {
336 let ty = value.ty.clone();
337 self.make_as(out, value, &ty)
338 }
339
340 fn fail(&mut self, out: Expr, message: Expr, ty: &Type) -> Expr {
342 let name = self.pair(ty);
343 Expr::new(
344 Node::Ctor {
345 ctor: format!("{name}_abort"),
346 data: name.clone(),
347 args: vec![out, message],
348 },
349 data(name),
350 )
351 }
352
353 fn reraise(&self, out: Expr, message: Expr, pair_type: &Type) -> Expr {
354 let entry = match pair_type {
355 Type::Data { name } => self
356 .pairs
357 .iter()
358 .find(|(_, entry)| entry.full_name == *name)
359 .map(|(_, entry)| entry),
360 _ => None,
361 };
362 let entry = entry.expect("an abort passes on only as a pair");
363 Expr::new(
364 Node::Ctor {
365 data: entry.full_name.clone(),
366 ctor: format!("{}_abort", entry.full_name),
367 args: vec![out, message],
368 },
369 pair_type.clone(),
370 )
371 }
372
373 fn open(&mut self, scrutinee: Expr, ty: &Type) -> Frame {
375 let data = self.pair(ty);
376 let out = self.fresh("o");
377 let value = self.fresh("v");
378 Frame {
379 scrutinee,
380 data,
381 out,
382 value,
383 }
384 }
385
386 fn close_one(
390 &mut self,
391 frame: Frame,
392 inner: Expr,
393 failure: Option<&dyn Fn(Expr, Expr) -> Expr>,
394 ) -> Expr {
395 let ty = inner.ty.clone();
396 let mut cases = vec![Case {
397 pattern: Pattern::Ctor {
398 data: frame.data.clone(),
399 ctor: format!("{}_mk", frame.data),
400 binds: vec![Some(frame.out), Some(frame.value)],
401 },
402 body: inner,
403 }];
404 if self.can_abort {
405 let lines = self.fresh("o");
406 let message = self.fresh("m");
407 let out = Expr::var(lines.clone(), Type::Output);
408 let text = Expr::var(message.clone(), STRING);
409 let handled = match failure {
410 Some(failure) => failure(out, text),
411 None => self.reraise(out, text, &ty),
412 };
413 cases.push(Case {
414 pattern: Pattern::Ctor {
415 data: frame.data.clone(),
416 ctor: format!("{}_abort", frame.data),
417 binds: vec![Some(lines), Some(message)],
418 },
419 body: handled,
420 });
421 }
422 Expr::new(
423 Node::Match {
424 scrutinee: Box::new(frame.scrutinee),
425 cases,
426 },
427 ty,
428 )
429 }
430
431 fn close(&mut self, frames: Vec<Frame>, inner: Expr) -> Expr {
433 frames
434 .into_iter()
435 .rev()
436 .fold(inner, |inner, frame| self.close_one(frame, inner, None))
437 }
438
439 fn bind_all(&mut self, list: &[&Expr], mut out: Expr) -> Result<(Vec<Frame>, Expr, Vec<Expr>)> {
444 let mut frames = Vec::new();
445 let mut values = Vec::new();
446 for &operand in list {
447 if self.effects(operand) {
448 let pair = self.io(operand, out)?;
449 let frame = self.open(pair, &operand.ty);
450 out = Expr::var(frame.out.clone(), Type::Output);
451 values.push(Expr::var(frame.value.clone(), operand.ty.clone()));
452 frames.push(frame);
453 } else {
454 values.push(operand.clone());
455 }
456 }
457 Ok((frames, out, values))
458 }
459
460 fn bind(&mut self, operand: &Expr, out: Expr) -> Result<(Vec<Frame>, Expr, Expr)> {
461 let (frames, out, mut values) = self.bind_all(&[operand], out)?;
462 let value = values.pop().expect("one operand");
463 Ok((frames, out, value))
464 }
465
466 fn io(&mut self, e: &Expr, out: Expr) -> Result<Expr> {
469 if !self.effects(e) {
470 return Ok(self.make(out, e.clone()));
471 }
472 let ty = self.pair_type(&e.ty);
473 let rebuilt = |node: Node| Expr {
474 node,
475 ty: ty.clone(),
476 span: e.span,
477 };
478 match &e.node {
479 Node::Print { text, body } => {
480 let (frames, before, text) = self.bind(text, out)?;
481 let after = self.fresh("o");
482 let value = Expr::new(
483 Node::OutCons {
484 head: Box::new(text),
485 tail: Box::new(before),
486 },
487 Type::Output,
488 );
489 let body = self.io(body, Expr::var(after.clone(), Type::Output))?;
490 let inner = Expr::new(
491 Node::Let {
492 name: after,
493 value: Box::new(value),
494 body: Box::new(body),
495 },
496 ty,
497 );
498 Ok(self.close(frames, inner))
499 }
500 Node::Abort { message } => Ok(self.fail(out, literal(STRING, &message), &e.ty)),
501 Node::Call { func, args } => {
502 let operands: Vec<&Expr> = args.iter().collect();
503 let (frames, after, mut args) = self.bind_all(&operands, out)?;
504 let inner = if self.effectful.contains(func) {
505 args.push(after);
506 rebuilt(Node::Call {
507 func: func.clone(),
508 args,
509 })
510 } else {
511 let call = Expr {
512 node: Node::Call {
513 func: func.clone(),
514 args,
515 },
516 ty: e.ty.clone(),
517 span: e.span,
518 };
519 self.make(after, call)
520 };
521 Ok(self.close(frames, inner))
522 }
523 Node::If {
524 cond,
525 then,
526 otherwise,
527 } => {
528 let (frames, after, cond) = self.bind(cond, out)?;
529 let then = self.io(then, after.clone())?;
530 let otherwise = self.io(otherwise, after)?;
531 let inner = rebuilt(Node::If {
532 cond: Box::new(cond),
533 then: Box::new(then),
534 otherwise: Box::new(otherwise),
535 });
536 Ok(self.close(frames, inner))
537 }
538 Node::Let { name, value, body } => {
539 let (frames, after, value) = self.bind(value, out)?;
540 let body = self.io(body, after)?;
541 let inner = rebuilt(Node::Let {
542 name: name.clone(),
543 value: Box::new(value),
544 body: Box::new(body),
545 });
546 Ok(self.close(frames, inner))
547 }
548 Node::Match { scrutinee, cases } => {
549 let (frames, after, scrutinee) = self.bind(scrutinee, out)?;
550 let mut threaded = Vec::new();
551 for kase in cases {
552 threaded.push(Case {
553 pattern: kase.pattern.clone(),
554 body: self.io(&kase.body, after.clone())?,
555 });
556 }
557 let inner = rebuilt(Node::Match {
558 scrutinee: Box::new(scrutinee),
559 cases: threaded,
560 });
561 Ok(self.close(frames, inner))
562 }
563 Node::Binary {
565 op: op @ (BinaryOp::And | BinaryOp::Or),
566 left,
567 right,
568 ..
569 } => {
570 let decided = Expr::lit(BOOL, LitValue::Bool(*op == BinaryOp::Or));
571 let (then, otherwise) = if *op == BinaryOp::And {
572 ((**right).clone(), decided)
573 } else {
574 (decided, (**right).clone())
575 };
576 let test = Expr::new(
577 Node::If {
578 cond: left.clone(),
579 then: Box::new(then),
580 otherwise: Box::new(otherwise),
581 },
582 BOOL,
583 );
584 self.io(&test, out)
585 }
586 _ => {
588 let operands = e.children();
589 let (frames, after, values) = self.bind_all(&operands, out)?;
590 let mut values = values.into_iter();
591 let copy = e.map_children(&mut |_| values.next().expect("an operand value"));
592 let inner = self.complete(after, copy);
593 Ok(self.close(frames, inner))
594 }
595 }
596 }
597
598 fn complete(&mut self, out: Expr, e: Expr) -> Expr {
602 if !aborts(&e) {
603 return self.make(out, e);
604 }
605 let mut operands: Vec<(String, Expr)> = Vec::new();
606 let mut name = |this: &mut Self, value: Expr| -> Expr {
607 if matches!(value.node, Node::Var { .. } | Node::Lit { .. }) {
608 return value;
609 }
610 let bound = this.fresh("a");
611 let ty = value.ty.clone();
612 operands.push((bound.clone(), value));
613 Expr::var(bound, ty)
614 };
615 let source = e.ty.clone();
616 let guard = |this: &mut Self, cond: Expr, message: &str, rest: Expr| -> Expr {
617 let ty = rest.ty.clone();
618 let then = this.fail(out.clone(), literal(STRING, &message), &source);
619 Expr::new(
620 Node::If {
621 cond: Box::new(cond),
622 then: Box::new(then),
623 otherwise: Box::new(rest),
624 },
625 ty,
626 )
627 };
628 let Expr { node, ty, span } = e;
629 let result = match node {
630 Node::Cast {
631 arg,
632 from,
633 to,
634 flavor,
635 message,
636 order,
637 } => {
638 let arg = name(self, *arg);
639 let zero = literal(representation(&arg.ty), &0);
640 let cond = compare(BinaryOp::Lt, arg.clone(), zero);
641 let text = cast_message(message.as_deref()).to_owned();
642 let node = total(Expr {
643 node: Node::Cast {
644 arg: Box::new(arg),
645 from,
646 to,
647 flavor,
648 message,
649 order,
650 },
651 ty: ty.clone(),
652 span,
653 });
654 let rest = self.make_as(out.clone(), node, &ty);
655 guard(self, cond, &text, rest)
656 }
657 Node::Unary { op, arg, semantics } => {
658 let arg = name(self, *arg);
659 let cond = compare(BinaryOp::Eq, arg.clone(), literal(INT, &fixed_min(&ty)));
660 let node = total(Expr {
661 node: Node::Unary {
662 op,
663 arg: Box::new(arg),
664 semantics,
665 },
666 ty: ty.clone(),
667 span,
668 });
669 let rest = self.make_as(out.clone(), node, &ty);
670 guard(self, cond, overflow_message("neg"), rest)
671 }
672 Node::Binary {
673 op,
674 left,
675 right,
676 domain,
677 semantics,
678 rounding,
679 by_zero,
680 } => {
681 let left = name(self, *left);
682 let right = name(self, *right);
683 let node = total(Expr {
684 node: Node::Binary {
685 op,
686 left: Box::new(left.clone()),
687 right: Box::new(right.clone()),
688 domain,
689 semantics,
690 rounding,
691 by_zero,
692 },
693 ty: ty.clone(),
694 span,
695 });
696 let rep = representation(&ty);
697 if division(op) {
698 let mut rest = self.make_as(out.clone(), node, &ty);
699 if matches!(ty, Type::Fixed { signed: true, .. }) {
700 let overflow = logical(
701 BinaryOp::And,
702 compare(BinaryOp::Eq, left, literal(rep.clone(), &fixed_min(&ty))),
703 compare(BinaryOp::Eq, right.clone(), literal(rep.clone(), &-1)),
704 );
705 rest = guard(self, overflow, overflow_message(op.name()), rest);
706 }
707 let zero = compare(BinaryOp::Eq, right, literal(rep, &0));
708 guard(self, zero, zero_divisor_message(op.name(), &ty), rest)
709 } else if op == BinaryOp::Sub && rep == NAT {
710 let cond = compare(BinaryOp::Lt, left, right);
711 let rest = self.make_as(out.clone(), node, &ty);
712 guard(self, cond, overflow_message("sub"), rest)
713 } else {
714 let wide = self.fresh("w");
716 let value = Expr::var(wide.clone(), rep.clone());
717 let above = compare(
718 BinaryOp::Gt,
719 value.clone(),
720 literal(rep.clone(), &fixed_max(&ty)),
721 );
722 let outside = if rep == NAT {
723 above
724 } else {
725 let below =
726 compare(BinaryOp::Lt, value.clone(), literal(rep, &fixed_min(&ty)));
727 logical(BinaryOp::Or, below, above)
728 };
729 let rest = self.make_as(out.clone(), value, &ty);
730 let body = guard(self, outside, overflow_message(op.name()), rest);
731 let pair = self.pair_type(&ty);
732 Expr::new(
733 Node::Let {
734 name: wide,
735 value: Box::new(node),
736 body: Box::new(body),
737 },
738 pair,
739 )
740 }
741 }
742 Node::Abort { .. } => unreachable!("an abort runs as an effect"),
743 other => unreachable!("{} does not abort", other.kind()),
744 };
745 operands
746 .into_iter()
747 .rev()
748 .fold(result, |body, (bound, value)| let_in(bound, value, body))
749 }
750
751 fn value(&mut self, e: &Expr, span: Option<Span>) -> Result<Expr> {
753 if self.can_abort {
754 return Err(unsupported(
755 "a theorem over a computation that may abort",
756 "a theorem states a fact of a value, which an abort does not have",
757 span,
758 ));
759 }
760 let pair = self.io(e, out_nil())?;
761 let frame = self.open(pair, &e.ty);
762 let value = Expr::var(frame.value.clone(), e.ty.clone());
763 Ok(self.close(vec![frame], value))
764 }
765
766 fn pure_expr(&mut self, e: &Expr) -> Result<Expr> {
767 if self.effects(e) {
768 self.value(e, None)
769 } else {
770 Ok(e.clone())
771 }
772 }
773
774 fn pure_prop(&mut self, prop: &Prop) -> Result<Prop> {
775 Ok(match prop {
776 Prop::Forall { binders, body } => Prop::Forall {
777 binders: binders.clone(),
778 body: Box::new(self.pure_prop(body)?),
779 },
780 Prop::And { left, right } => Prop::And {
781 left: Box::new(self.pure_prop(left)?),
782 right: Box::new(self.pure_prop(right)?),
783 },
784 Prop::Or { left, right } => Prop::Or {
785 left: Box::new(self.pure_prop(left)?),
786 right: Box::new(self.pure_prop(right)?),
787 },
788 Prop::Implies { left, right } => Prop::Implies {
789 left: Box::new(self.pure_prop(left)?),
790 right: Box::new(self.pure_prop(right)?),
791 },
792 Prop::Not { arg } => Prop::Not {
793 arg: Box::new(self.pure_prop(arg)?),
794 },
795 Prop::Bool { expr } => Prop::Bool {
796 expr: self.pure_expr(expr)?,
797 },
798 other => {
799 let (op, comparison) = other.comparison().expect("a comparison");
800 let left = self.pure_expr(&comparison.left)?;
801 let right = self.pure_expr(&comparison.right)?;
802 Prop::compare(
803 op,
804 Comparison {
805 left,
806 right,
807 ..comparison.clone()
808 },
809 )
810 }
811 })
812 }
813
814 fn effect(&mut self, effect: &Effect) -> Result<Vec<Effect>> {
818 Ok(match effect {
819 Effect::Let {
820 name,
821 value,
822 constant,
823 span,
824 } if self.effects(value) => {
825 let (mut steps, value) = self.run(value)?;
826 steps.push(Effect::Let {
827 name: name.clone(),
828 value,
829 constant: *constant,
830 span: *span,
831 });
832 steps
833 }
834 Effect::Print { expr, span } if self.effects(expr) => {
835 let (mut steps, expr) = self.run(expr)?;
836 steps.push(Effect::Print { expr, span: *span });
837 steps
838 }
839 Effect::Assert { prop, span } if self.prop_effects(prop) => {
840 let mut steps = Vec::new();
841 let prop = self.hoist(prop, &mut steps, *span)?;
842 steps.push(Effect::Assert { prop, span: *span });
843 steps
844 }
845 other => vec![other.clone()],
846 })
847 }
848
849 fn run(&mut self, e: &Expr) -> Result<(Vec<Effect>, Expr)> {
850 let name = self.fresh("run");
851 let pair = Expr::var(name.clone(), self.pair_type(&e.ty));
852 let value = self.io(e, out_nil())?;
853 let lines = self.open(pair.clone(), &e.ty);
854 let lines_var = Expr::var(lines.out.clone(), Type::Output);
855 let lines = self.close_one(lines, lines_var, Some(&|out, _| out));
856 let mut steps = vec![
857 Effect::Let {
858 name,
859 value,
860 constant: false,
861 span: e.span,
862 },
863 Effect::Output {
864 expr: lines,
865 span: e.span,
866 },
867 ];
868 if !self.can_abort {
869 let result = self.open(pair, &e.ty);
870 let result_var = Expr::var(result.value.clone(), e.ty.clone());
871 let result = self.close(vec![result], result_var);
872 return Ok((steps, result));
873 }
874 let value = self.fresh("val");
876 let data = self.pair(&e.ty);
877 steps.push(Effect::Unwrap {
878 name: value.clone(),
879 pair,
880 ctors: vec![format!("{data}_mk"), format!("{data}_abort")],
881 data,
882 ty: e.ty.clone(),
883 span: e.span,
884 });
885 Ok((steps, Expr::var(value, e.ty.clone())))
886 }
887
888 fn lift(&mut self, e: &Expr, steps: &mut Vec<Effect>) -> Result<Expr> {
889 if !self.effects(e) {
890 return Ok(e.clone());
891 }
892 let (run, value) = self.run(e)?;
893 steps.extend(run);
894 Ok(value)
895 }
896
897 fn hoist(&mut self, prop: &Prop, steps: &mut Vec<Effect>, span: Option<Span>) -> Result<Prop> {
899 Ok(match prop {
900 Prop::Bool { expr } => Prop::Bool {
901 expr: self.lift(expr, steps)?,
902 },
903 Prop::Not { arg } => Prop::Not {
904 arg: Box::new(self.hoist(arg, steps, span)?),
905 },
906 Prop::And { left, right } if !self.prop_effects(right) => Prop::And {
908 left: Box::new(self.hoist(left, steps, span)?),
909 right: right.clone(),
910 },
911 Prop::Or { left, right } if !self.prop_effects(right) => Prop::Or {
912 left: Box::new(self.hoist(left, steps, span)?),
913 right: right.clone(),
914 },
915 Prop::And { .. } | Prop::Or { .. } => {
916 return Err(unsupported(
917 "output or an abort in a compound assertion",
918 "an operand that prints or aborts after the first runs only when the assertion evaluates it; assert on a value computed before",
919 span,
920 ));
921 }
922 Prop::Implies { .. } | Prop::Forall { .. } => {
923 return Err(unsupported(
924 "output or an abort in a compound assertion",
925 "an operand that prints or aborts runs only when the assertion evaluates it; assert on a value computed before",
926 span,
927 ));
928 }
929 other => {
930 let (op, comparison) = other.comparison().expect("a comparison");
931 let left = self.lift(&comparison.left, steps)?;
932 let right = self.lift(&comparison.right, steps)?;
933 Prop::compare(
934 op,
935 Comparison {
936 left,
937 right,
938 ..comparison.clone()
939 },
940 )
941 }
942 })
943 }
944}