1use derive_generic_visitor::{Drive, DriveMut, DriveTwo};
5use macros::{EnumAsGetters, EnumIsA, VariantIndexArity, VariantName};
6use serde_state::{DeserializeState, SerializeState};
7use smallvec::{SmallVec, smallvec};
8use std::collections::HashMap;
9use std::mem;
10use std::ops::{Index, IndexMut};
11
12use crate::ast::*;
13
14generate_index_type!(BlockId, "Block");
16
17pub static START_BLOCK_ID: BlockId = BlockId::ZERO;
19
20#[cfg_attr(feature = "charon_on_charon", charon::rename("Blocks"))]
21pub type BodyContents = IndexVec<BlockId, BlockData>;
22pub type ExprBody = GExprBody<BodyContents>;
23
24#[derive(
27 Debug, PartialEq, Eq, Clone, SerializeState, DeserializeState, Drive, DriveMut, DriveTwo,
28)]
29#[cfg_attr(feature = "charon_on_charon", charon::rename("Block"))]
30pub struct BlockData {
31 pub statements: Vec<Statement>,
32 pub terminator: Terminator,
33}
34
35#[derive(
37 Debug, PartialEq, Eq, Clone, SerializeState, DeserializeState, Drive, DriveMut, DriveTwo,
38)]
39pub struct Statement {
40 pub span: Span,
41 pub kind: StatementKind,
42 #[drive(skip)]
45 pub comments_before: Vec<String>,
46}
47
48#[derive(
49 Debug,
50 PartialEq,
51 Eq,
52 Clone,
53 EnumIsA,
54 EnumAsGetters,
55 VariantName,
56 SerializeState,
57 DeserializeState,
58 Drive,
59 DriveMut,
60 DriveTwo,
61)]
62pub enum StatementKind {
63 Assign(Place, Rvalue),
64 SetDiscriminant(Place, VariantId),
66 StorageLive(LocalId),
71 StorageDead(LocalId),
76 PlaceMention(Place),
80 Borrowck(BorrowckStatement),
82 Assert {
89 assert: Assert,
90 on_failure: AbortKind,
91 },
92 Nop,
94}
95
96#[derive(
97 Debug,
98 PartialEq,
99 Eq,
100 Clone,
101 EnumIsA,
102 EnumAsGetters,
103 VariantName,
104 VariantIndexArity,
105 SerializeState,
106 DeserializeState,
107 Drive,
108 DriveMut,
109 DriveTwo,
110)]
111#[cfg_attr(feature = "charon_on_charon", charon::rename("Switch"))]
112pub enum SwitchTargets {
113 If(BlockId, BlockId),
115 SwitchInt(LiteralTy, Vec<(Literal, BlockId)>, BlockId),
119}
120
121#[derive(
122 Debug,
123 PartialEq,
124 Eq,
125 Clone,
126 EnumIsA,
127 EnumAsGetters,
128 SerializeState,
129 DeserializeState,
130 Drive,
131 DriveMut,
132 DriveTwo,
133)]
134pub enum TerminatorKind {
135 Goto {
136 target: BlockId,
137 },
138 Switch {
139 discr: Operand,
140 targets: SwitchTargets,
141 },
142 Call {
143 call: Call,
144 target: BlockId,
145 on_unwind: BlockId,
146 },
147 Drop {
153 #[drive(skip)]
154 kind: DropKind,
155 place: Place,
156 fn_ptr: FnPtr,
158 target: BlockId,
159 on_unwind: BlockId,
160 },
161 #[cfg_attr(feature = "charon_on_charon", charon::rename("TAssert"))]
164 Assert {
165 assert: Assert,
166 target: BlockId,
167 on_unwind: BlockId,
168 },
169 InlineAsm {
171 asm: String,
172 targets: Vec<BlockId>,
173 on_unwind: BlockId,
174 },
175 Abort(AbortKind),
177 Return,
178 UnwindResume,
180}
181
182#[derive(
184 Debug, PartialEq, Eq, Clone, SerializeState, DeserializeState, Drive, DriveMut, DriveTwo,
185)]
186pub struct Terminator {
187 pub span: Span,
188 pub kind: TerminatorKind,
189 #[drive(skip)]
192 pub comments_before: Vec<String>,
193}
194
195impl ExprBody {
196 pub fn as_abort_map(&self) -> HashMap<BlockId, AbortKind> {
200 self.body
201 .iter_enumerated()
202 .filter_map(|(bid, block)| block.as_abort().map(|abort| (bid, abort)))
203 .collect()
204 }
205
206 pub fn transform_sequences_fwd<F>(&mut self, mut f: F)
207 where
208 F: FnMut(BlockId, &mut Locals, &mut [Statement]) -> Vec<(usize, Vec<Statement>)>,
209 {
210 for (id, block) in &mut self.body.iter_mut_enumerated() {
211 block.transform_sequences_fwd(|seq| f(id, &mut self.locals, seq));
212 }
213 }
214
215 pub fn transform_sequences_bwd<F>(&mut self, mut f: F)
216 where
217 F: FnMut(&mut Locals, &mut [Statement]) -> Vec<(usize, Vec<Statement>)>,
218 {
219 for block in &mut self.body {
220 block.transform_sequences_bwd(|seq| f(&mut self.locals, seq));
221 }
222 }
223
224 pub fn visit_statements<F: FnMut(&mut Statement)>(&mut self, mut f: F) {
226 for block in self.body.iter_mut().rev() {
227 for st in block.statements.iter_mut().rev() {
228 f(st);
229 }
230 }
231 }
232}
233
234impl BlockData {
235 pub fn new_goto(span: Span, target: BlockId) -> Self {
237 BlockData {
238 statements: vec![],
239 terminator: Terminator::goto(span, target),
240 }
241 }
242 pub fn as_goto(&self) -> Option<BlockId> {
243 if let TerminatorKind::Goto { target } = self.terminator.kind {
244 Some(target)
245 } else {
246 None
247 }
248 }
249 pub fn as_trivial_goto(&self) -> Option<BlockId> {
250 self.as_goto().filter(|_| {
251 self.statements
252 .iter()
253 .all(|st| matches!(st.kind, StatementKind::Nop))
254 })
255 }
256
257 pub fn as_abort(&self) -> Option<AbortKind> {
258 if self.statements.iter().all(|st| {
259 matches!(
260 st.kind,
261 StatementKind::Nop | StatementKind::StorageLive(_) | StatementKind::StorageDead(_)
262 )
263 }) && let TerminatorKind::Abort(abort) = &self.terminator.kind
264 {
265 Some(abort.clone())
266 } else {
267 None
268 }
269 }
270
271 pub fn new_unreachable() -> Self {
273 Terminator::new(
274 Span::dummy(),
275 TerminatorKind::Abort(AbortKind::UndefinedBehavior),
276 )
277 .into_block()
278 }
279
280 pub fn targets(&self) -> SmallVec<[BlockId; 2]> {
281 self.terminator.targets()
282 }
283 pub fn targets_ignoring_unwind(&self) -> SmallVec<[BlockId; 2]> {
284 self.terminator.targets_ignoring_unwind()
285 }
286
287 pub fn transform<F: FnMut(&mut Statement) -> Vec<Statement>>(&mut self, mut f: F) {
293 self.transform_sequences_fwd(|slice| {
294 let new_statements = f(&mut slice[0]);
295 if new_statements.is_empty() {
296 vec![]
297 } else {
298 vec![(0, new_statements)]
299 }
300 });
301 }
302
303 fn transform_sequences<F>(&mut self, mut f: F, forward: bool)
305 where
306 F: FnMut(&mut [Statement]) -> Vec<(usize, Vec<Statement>)>,
307 {
308 let mut to_insert = vec![];
309 let mut final_len = self.statements.len();
310 if forward {
311 for i in 0..self.statements.len() {
312 let new_to_insert = f(&mut self.statements[i..]);
313 to_insert.extend(new_to_insert.into_iter().map(|(j, stmts)| {
314 final_len += stmts.len();
315 (i + j, stmts)
316 }));
317 }
318 } else {
319 for i in (0..self.statements.len()).rev() {
320 let new_to_insert = f(&mut self.statements[i..]);
321 to_insert.extend(new_to_insert.into_iter().map(|(j, stmts)| {
322 final_len += stmts.len();
323 (i + j, stmts)
324 }));
325 }
326 }
327 if !to_insert.is_empty() {
328 to_insert.sort_by_key(|(i, _)| *i);
329 to_insert.reverse();
331 let old_statements = mem::replace(&mut self.statements, Vec::with_capacity(final_len));
333 for (i, stmt) in old_statements.into_iter().enumerate() {
334 while let Some((j, _)) = to_insert.last()
335 && *j == i
336 {
337 let (_, mut stmts) = to_insert.pop().unwrap();
338 self.statements.append(&mut stmts);
339 }
340 self.statements.push(stmt);
341 }
342 }
343 }
344
345 pub fn transform_sequences_fwd<F>(&mut self, f: F)
351 where
352 F: FnMut(&mut [Statement]) -> Vec<(usize, Vec<Statement>)>,
353 {
354 self.transform_sequences(f, true);
355 }
356
357 pub fn transform_sequences_bwd<F>(&mut self, f: F)
363 where
364 F: FnMut(&mut [Statement]) -> Vec<(usize, Vec<Statement>)>,
365 {
366 self.transform_sequences(f, false);
367 }
368}
369
370impl Statement {
371 pub fn new(span: Span, kind: StatementKind) -> Self {
372 Statement {
373 span,
374 kind,
375 comments_before: vec![],
376 }
377 }
378}
379
380impl Terminator {
381 pub fn new(span: Span, kind: TerminatorKind) -> Self {
382 Terminator {
383 span,
384 kind,
385 comments_before: vec![],
386 }
387 }
388 pub fn goto(span: Span, target: BlockId) -> Self {
389 Self::new(span, TerminatorKind::Goto { target })
390 }
391 pub fn is_error(&self) -> bool {
393 use TerminatorKind::*;
394 match &self.kind {
395 Abort(..) => true,
396 Goto { .. }
397 | Switch { .. }
398 | InlineAsm { .. }
399 | Return
400 | Call { .. }
401 | Drop { .. }
402 | UnwindResume
403 | Assert { .. } => false,
404 }
405 }
406
407 pub fn into_block(self) -> BlockData {
408 BlockData {
409 statements: vec![],
410 terminator: self,
411 }
412 }
413
414 pub fn targets(&self) -> SmallVec<[BlockId; 2]> {
415 match &self.kind {
416 TerminatorKind::Goto { target } => {
417 smallvec![*target]
418 }
419 TerminatorKind::Switch { targets, .. } => targets.targets(),
420 TerminatorKind::InlineAsm {
421 targets, on_unwind, ..
422 } => targets.iter().copied().chain([*on_unwind]).collect(),
423 TerminatorKind::Call {
424 target, on_unwind, ..
425 }
426 | TerminatorKind::Drop {
427 target, on_unwind, ..
428 }
429 | TerminatorKind::Assert {
430 target, on_unwind, ..
431 } => smallvec![*target, *on_unwind],
432 TerminatorKind::Abort(..) | TerminatorKind::Return | TerminatorKind::UnwindResume => {
433 smallvec![]
434 }
435 }
436 }
437 pub fn targets_mut(&mut self) -> SmallVec<[&mut BlockId; 2]> {
438 match &mut self.kind {
439 TerminatorKind::Goto { target } => {
440 smallvec![target]
441 }
442 TerminatorKind::Switch { targets, .. } => targets.targets_mut(),
443 TerminatorKind::InlineAsm {
444 targets, on_unwind, ..
445 } => targets.iter_mut().chain([on_unwind]).collect(),
446 TerminatorKind::Call {
447 target, on_unwind, ..
448 }
449 | TerminatorKind::Drop {
450 target, on_unwind, ..
451 }
452 | TerminatorKind::Assert {
453 target, on_unwind, ..
454 } => smallvec![target, on_unwind],
455 TerminatorKind::Abort(..) | TerminatorKind::Return | TerminatorKind::UnwindResume => {
456 smallvec![]
457 }
458 }
459 }
460
461 pub fn targets_ignoring_unwind(&self) -> SmallVec<[BlockId; 2]> {
462 match &self.kind {
463 TerminatorKind::Goto { target } => {
464 smallvec![*target]
465 }
466 TerminatorKind::Switch { targets, .. } => targets.targets(),
467 TerminatorKind::InlineAsm { targets, .. } => targets.iter().copied().collect(),
468 TerminatorKind::Call { target, .. }
469 | TerminatorKind::Drop { target, .. }
470 | TerminatorKind::Assert { target, .. } => {
471 smallvec![*target]
472 }
473 TerminatorKind::Abort(..) | TerminatorKind::Return | TerminatorKind::UnwindResume => {
474 smallvec![]
475 }
476 }
477 }
478}
479
480impl SwitchTargets {
481 pub fn targets(&self) -> SmallVec<[BlockId; 2]> {
482 match self {
483 SwitchTargets::If(then_tgt, else_tgt) => {
484 smallvec![*then_tgt, *else_tgt]
485 }
486 SwitchTargets::SwitchInt(_, targets, otherwise) => targets
487 .iter()
488 .map(|(_, t)| t)
489 .chain([otherwise])
490 .copied()
491 .collect(),
492 }
493 }
494 pub fn targets_mut(&mut self) -> SmallVec<[&mut BlockId; 2]> {
495 match self {
496 SwitchTargets::If(then_tgt, else_tgt) => {
497 smallvec![then_tgt, else_tgt]
498 }
499 SwitchTargets::SwitchInt(_, targets, otherwise) => targets
500 .iter_mut()
501 .map(|(_, t)| t)
502 .chain([otherwise])
503 .collect(),
504 }
505 }
506}
507
508#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
510pub struct StmtLoc {
511 pub block: BlockId,
512 pub statement: usize,
513}
514
515impl StmtLoc {
516 pub fn new(block: BlockId, statement: usize) -> Self {
517 StmtLoc { block, statement }
518 }
519
520 pub fn block_start(block: BlockId) -> Self {
521 StmtLoc {
522 block,
523 statement: 0,
524 }
525 }
526
527 pub fn after(self) -> Self {
528 StmtLoc {
529 block: self.block,
530 statement: self.statement + 1,
531 }
532 }
533}
534
535impl Index<StmtLoc> for ExprBody {
536 type Output = Statement;
537 fn index(&self, loc: StmtLoc) -> &Self::Output {
538 &self.body[loc.block].statements[loc.statement]
539 }
540}
541
542impl IndexMut<StmtLoc> for ExprBody {
543 fn index_mut(&mut self, loc: StmtLoc) -> &mut Self::Output {
544 &mut self.body[loc.block].statements[loc.statement]
545 }
546}
547
548pub struct BodyBuilder {
550 pub span: Span,
552 pub body: ExprBody,
554 pub current_block: BlockId,
556 pub unwind_block: Option<BlockId>,
558}
559
560fn mk_block(span: Span, term: TerminatorKind) -> BlockData {
561 BlockData {
562 statements: vec![],
563 terminator: Terminator::new(span, term),
564 }
565}
566
567impl BodyBuilder {
568 pub fn new(span: Span, arg_count: usize) -> Self {
569 let mut body: ExprBody = GExprBody {
570 span,
571 locals: Locals::new(arg_count),
572 bound_body_regions: 0,
573 body: IndexVec::new(),
574 comments: vec![],
575 };
576 let current_block = body.body.push(BlockData {
577 statements: Default::default(),
578 terminator: Terminator::new(span, TerminatorKind::Return),
579 });
580 Self {
581 span,
582 body,
583 current_block,
584 unwind_block: None,
585 }
586 }
587
588 pub fn build(mut self) -> ExprBody {
590 let mut freshener: IndexMap<RegionId, ()> = IndexMap::new();
592 self.body.dyn_visit_mut(|r: &mut Region| {
593 if r.is_erased() || r.is_body() {
594 *r = Region::Body(freshener.push(()));
595 }
596 });
597 self.body.bound_body_regions = freshener.slot_count();
598 self.body
600 }
601
602 pub fn new_var(&mut self, name: Option<String>, ty: Ty) -> Place {
605 let place = self.body.locals.new_var(name, ty);
606 let local_id = place.as_local().unwrap();
607 if !self.body.locals.is_return_or_arg(local_id) {
608 self.push_statement(StatementKind::StorageLive(local_id));
609 }
610 place
611 }
612
613 fn current_block(&mut self) -> &mut BlockData {
615 &mut self.body.body[self.current_block]
616 }
617
618 pub fn push_statement(&mut self, kind: StatementKind) {
619 let st = Statement::new(self.span, kind);
620 self.current_block().statements.push(st);
621 }
622
623 fn unwind_block(&mut self) -> BlockId {
624 *self.unwind_block.get_or_insert_with(|| {
625 self.body
626 .body
627 .push(mk_block(self.span, TerminatorKind::UnwindResume))
628 })
629 }
630
631 pub fn call(&mut self, call: Call) {
632 let next_block = self
633 .body
634 .body
635 .push(mk_block(self.span, TerminatorKind::Return));
636 let term = TerminatorKind::Call {
637 target: next_block,
638 call,
639 on_unwind: self.unwind_block(),
640 };
641 self.current_block().terminator.kind = term;
642 self.current_block = next_block;
643 }
644
645 pub fn insert_drop(&mut self, place: Place, fn_ptr: FnPtr) {
646 let next_block = self
647 .body
648 .body
649 .push(mk_block(self.span, TerminatorKind::Return));
650 let term = TerminatorKind::Drop {
651 kind: DropKind::Precise,
652 place,
653 fn_ptr,
654 target: next_block,
655 on_unwind: self.unwind_block(),
656 };
657 self.current_block().terminator.kind = term;
658 self.current_block = next_block;
659 }
660}