1use derive_generic_visitor::{Drive, DriveMut, DriveTwo};
5use macros::{EnumAsGetters, EnumIsA, VariantName};
6use rustc_hash::FxHashMap as HashMap;
7use serde_state::{DeserializeState, SerializeState};
8use smallvec::{SmallVec, smallvec};
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(Debug, Clone, PartialEq, Eq)]
27#[derive(SerializeState, DeserializeState, Drive, DriveMut, DriveTwo)]
28#[cfg_attr(feature = "charon_on_charon", charon::rename("Block"))]
29pub struct BlockData {
30 pub statements: Vec<Statement>,
31 pub terminator: Terminator,
32 pub kind: UnwindKind,
33}
34
35#[derive(Debug, Copy, Clone, PartialEq, Eq)]
37#[derive(SerializeState, DeserializeState, Drive, DriveMut, DriveTwo)]
38pub enum UnwindKind {
39 Regular,
41 Cleanup,
43 Terminate,
46}
47
48#[derive(Debug, Clone, PartialEq, Eq)]
50#[derive(SerializeState, DeserializeState, Drive, DriveMut, DriveTwo)]
51pub struct Statement {
52 pub span: Span,
53 pub kind: StatementKind,
54 pub comments_before: Vec<String>,
57}
58
59#[derive(Debug, Clone, PartialEq, Eq)]
60#[derive(EnumIsA, EnumAsGetters, VariantName)]
61#[derive(SerializeState, DeserializeState, Drive, DriveMut, DriveTwo)]
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(Debug, Clone, PartialEq, Eq)]
97#[derive(EnumIsA, EnumAsGetters)]
98#[derive(SerializeState, DeserializeState, Drive, DriveMut, DriveTwo)]
99pub enum TerminatorKind {
100 Goto {
101 target: BlockId,
102 },
103 Switch {
104 data: SwitchData,
105 branches: IndexVec<BranchId, BlockId>,
106 },
107 Call {
108 call: Call,
109 target: BlockId,
110 on_unwind: BlockId,
111 },
112 Drop {
118 kind: DropKind,
119 place: Place,
120 fn_ptr: FnPtr,
122 target: BlockId,
123 on_unwind: BlockId,
124 },
125 InlineAsm {
127 asm: InlineAsm,
128 fallthrough: Option<BlockId>,
130 labels: IndexVec<BranchId, BlockId>,
132 on_unwind: BlockId,
134 },
135
136 #[cfg_attr(feature = "charon_on_charon", charon::rename("TAssert"))]
139 Assert {
140 assert: Assert,
141 target: BlockId,
142 on_unwind: BlockId,
143 },
144 Panic {
146 name: Name,
148 on_unwind: BlockId,
149 },
150 UnwindTerminate,
152 UnwindResume,
154
155 Return,
156 UndefinedBehavior,
158}
159
160#[derive(Debug, Clone, PartialEq, Eq)]
162#[derive(SerializeState, DeserializeState, Drive, DriveMut, DriveTwo)]
163pub struct Terminator {
164 pub span: Span,
165 pub kind: TerminatorKind,
166 pub comments_before: Vec<String>,
169}
170
171impl ExprBody {
172 pub fn as_abort_map(&self) -> HashMap<BlockId, AbortKind> {
175 self.body
176 .iter_enumerated()
177 .filter_map(|(bid, block)| block.as_abort().map(|abort| (bid, abort)))
178 .collect()
179 }
180
181 pub fn transform_sequences_fwd<F>(&mut self, mut f: F)
182 where
183 F: FnMut(BlockId, &mut Locals, &mut [Statement]) -> Vec<(usize, Vec<Statement>)>,
184 {
185 for (id, block) in &mut self.body.iter_mut_enumerated() {
186 block.transform_sequences_fwd(|seq| f(id, &mut self.locals, seq));
187 }
188 }
189
190 pub fn transform_sequences_bwd<F>(&mut self, mut f: F)
191 where
192 F: FnMut(&mut Locals, &mut [Statement]) -> Vec<(usize, Vec<Statement>)>,
193 {
194 for block in &mut self.body {
195 block.transform_sequences_bwd(|seq| f(&mut self.locals, seq));
196 }
197 }
198
199 pub fn visit_block_ids_mut<F: FnMut(&mut BlockId)>(&mut self, mut f: F) {
201 for block in &mut self.body {
202 block.terminator.targets_mut().into_iter().for_each(&mut f);
203 }
204 }
205
206 pub fn visit_statements<F: FnMut(&mut Statement)>(&mut self, mut f: F) {
208 for block in self.body.iter_mut().rev() {
209 for st in block.statements.iter_mut().rev() {
210 f(st);
211 }
212 }
213 }
214}
215
216impl BlockData {
217 pub fn new_goto(span: Span, target: BlockId, kind: UnwindKind) -> Self {
219 BlockData {
220 statements: vec![],
221 terminator: Terminator::goto(span, target),
222 kind,
223 }
224 }
225 pub fn as_goto(&self) -> Option<BlockId> {
226 if let TerminatorKind::Goto { target } = self.terminator.kind {
227 Some(target)
228 } else {
229 None
230 }
231 }
232 pub fn as_trivial_goto(&self) -> Option<BlockId> {
233 self.as_goto().filter(|_| {
234 self.statements
235 .iter()
236 .all(|st| matches!(st.kind, StatementKind::Nop))
237 })
238 }
239
240 pub fn as_abort(&self) -> Option<AbortKind> {
241 if self.statements.iter().all(|st| {
242 matches!(
243 st.kind,
244 StatementKind::Nop | StatementKind::StorageLive(_) | StatementKind::StorageDead(_)
245 )
246 }) {
247 match &self.terminator.kind {
248 TerminatorKind::Panic { name, .. } => Some(AbortKind::Panic(Some(name.clone()))),
249 TerminatorKind::UndefinedBehavior => Some(AbortKind::UndefinedBehavior),
250 TerminatorKind::UnwindTerminate => Some(AbortKind::UnwindTerminate),
251 _ => None,
252 }
253 } else {
254 None
255 }
256 }
257
258 pub fn new_unreachable(kind: UnwindKind) -> Self {
260 Terminator::new(Span::dummy(), TerminatorKind::UndefinedBehavior).into_block(kind)
261 }
262 pub fn take(&mut self) -> Self {
264 mem::replace(self, BlockData::new_unreachable(self.kind))
265 }
266
267 pub fn targets(&self) -> SmallVec<[BlockId; 2]> {
268 self.terminator.targets()
269 }
270 pub fn targets_ignoring_unwind(&self) -> SmallVec<[BlockId; 2]> {
271 self.terminator.targets_ignoring_unwind()
272 }
273
274 pub fn transform<F: FnMut(&mut Statement) -> Vec<Statement>>(&mut self, mut f: F) {
280 self.transform_sequences_fwd(|slice| {
281 let new_statements = f(&mut slice[0]);
282 if new_statements.is_empty() {
283 vec![]
284 } else {
285 vec![(0, new_statements)]
286 }
287 });
288 }
289
290 fn transform_sequences<F>(&mut self, mut f: F, forward: bool)
292 where
293 F: FnMut(&mut [Statement]) -> Vec<(usize, Vec<Statement>)>,
294 {
295 let mut to_insert = vec![];
296 let mut final_len = self.statements.len();
297 if forward {
298 for i in 0..self.statements.len() {
299 let new_to_insert = f(&mut self.statements[i..]);
300 to_insert.extend(new_to_insert.into_iter().map(|(j, stmts)| {
301 final_len += stmts.len();
302 (i + j, stmts)
303 }));
304 }
305 } else {
306 for i in (0..self.statements.len()).rev() {
307 let new_to_insert = f(&mut self.statements[i..]);
308 to_insert.extend(new_to_insert.into_iter().map(|(j, stmts)| {
309 final_len += stmts.len();
310 (i + j, stmts)
311 }));
312 }
313 }
314 if !to_insert.is_empty() {
315 to_insert.sort_by_key(|(i, _)| *i);
316 to_insert.reverse();
318 let old_statements = mem::replace(&mut self.statements, Vec::with_capacity(final_len));
320 for (i, stmt) in old_statements.into_iter().enumerate() {
321 while let Some((j, _)) = to_insert.last()
322 && *j == i
323 {
324 let (_, mut stmts) = to_insert.pop().unwrap();
325 self.statements.append(&mut stmts);
326 }
327 self.statements.push(stmt);
328 }
329 }
330 }
331
332 pub fn transform_sequences_fwd<F>(&mut self, f: F)
338 where
339 F: FnMut(&mut [Statement]) -> Vec<(usize, Vec<Statement>)>,
340 {
341 self.transform_sequences(f, true);
342 }
343
344 pub fn transform_sequences_bwd<F>(&mut self, f: F)
350 where
351 F: FnMut(&mut [Statement]) -> Vec<(usize, Vec<Statement>)>,
352 {
353 self.transform_sequences(f, false);
354 }
355}
356
357impl UnwindKind {
358 pub fn further_unwind_kind(self) -> Self {
360 match self {
361 Self::Regular => Self::Cleanup,
362 Self::Cleanup | Self::Terminate => Self::Terminate,
364 }
365 }
366}
367
368impl Statement {
369 pub fn new(span: Span, kind: StatementKind) -> Self {
370 Statement {
371 span,
372 kind,
373 comments_before: vec![],
374 }
375 }
376}
377
378impl Terminator {
379 pub fn new(span: Span, kind: TerminatorKind) -> Self {
380 Terminator {
381 span,
382 kind,
383 comments_before: vec![],
384 }
385 }
386 pub fn goto(span: Span, target: BlockId) -> Self {
387 Self::new(span, TerminatorKind::Goto { target })
388 }
389 pub fn is_error(&self) -> bool {
391 use TerminatorKind::*;
392 match &self.kind {
393 Panic { .. } | UndefinedBehavior | UnwindTerminate => true,
394 Goto { .. }
395 | Switch { .. }
396 | InlineAsm { .. }
397 | Return
398 | Call { .. }
399 | Drop { .. }
400 | UnwindResume
401 | Assert { .. } => false,
402 }
403 }
404
405 pub fn into_block(self, kind: UnwindKind) -> BlockData {
406 BlockData {
407 statements: vec![],
408 terminator: self,
409 kind,
410 }
411 }
412
413 pub fn targets(&self) -> SmallVec<[BlockId; 2]> {
414 match &self.kind {
415 TerminatorKind::Goto { target } => {
416 smallvec![*target]
417 }
418 TerminatorKind::Switch { branches, .. } => branches.iter().copied().collect(),
419 TerminatorKind::InlineAsm {
420 fallthrough,
421 labels,
422 on_unwind,
423 ..
424 } => fallthrough
425 .iter()
426 .copied()
427 .chain(labels.iter().copied())
428 .chain([*on_unwind])
429 .collect(),
430 TerminatorKind::Call {
431 target, on_unwind, ..
432 }
433 | TerminatorKind::Drop {
434 target, on_unwind, ..
435 }
436 | TerminatorKind::Assert {
437 target, on_unwind, ..
438 } => smallvec![*target, *on_unwind],
439 TerminatorKind::Panic { on_unwind, .. } => smallvec![*on_unwind],
440 TerminatorKind::UndefinedBehavior
441 | TerminatorKind::UnwindTerminate
442 | TerminatorKind::Return
443 | TerminatorKind::UnwindResume => {
444 smallvec![]
445 }
446 }
447 }
448 pub fn targets_mut(&mut self) -> SmallVec<[&mut BlockId; 2]> {
449 match &mut self.kind {
450 TerminatorKind::Goto { target } => {
451 smallvec![target]
452 }
453 TerminatorKind::Switch { branches, .. } => branches.iter_mut().collect(),
454 TerminatorKind::InlineAsm {
455 fallthrough,
456 labels,
457 on_unwind,
458 ..
459 } => fallthrough
460 .iter_mut()
461 .chain(labels.iter_mut())
462 .chain([on_unwind])
463 .collect(),
464 TerminatorKind::Call {
465 target, on_unwind, ..
466 }
467 | TerminatorKind::Drop {
468 target, on_unwind, ..
469 }
470 | TerminatorKind::Assert {
471 target, on_unwind, ..
472 } => smallvec![target, on_unwind],
473 TerminatorKind::Panic { on_unwind, .. } => smallvec![on_unwind],
474 TerminatorKind::UndefinedBehavior
475 | TerminatorKind::UnwindTerminate
476 | TerminatorKind::Return
477 | TerminatorKind::UnwindResume => {
478 smallvec![]
479 }
480 }
481 }
482
483 pub fn targets_ignoring_unwind(&self) -> SmallVec<[BlockId; 2]> {
484 match &self.kind {
485 TerminatorKind::Goto { target } => {
486 smallvec![*target]
487 }
488 TerminatorKind::Switch { branches, .. } => branches.iter().copied().collect(),
489 TerminatorKind::InlineAsm {
490 fallthrough,
491 labels,
492 ..
493 } => fallthrough
494 .iter()
495 .copied()
496 .chain(labels.iter().copied())
497 .collect(),
498 TerminatorKind::Call { target, .. }
499 | TerminatorKind::Drop { target, .. }
500 | TerminatorKind::Assert { target, .. } => {
501 smallvec![*target]
502 }
503 TerminatorKind::Panic { .. }
504 | TerminatorKind::UndefinedBehavior
505 | TerminatorKind::UnwindTerminate
506 | TerminatorKind::Return
507 | TerminatorKind::UnwindResume => {
508 smallvec![]
509 }
510 }
511 }
512}
513
514impl TerminatorKind {
515 pub fn take(&mut self) -> Self {
517 std::mem::replace(self, TerminatorKind::UndefinedBehavior)
518 }
519}
520
521#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
523pub struct StmtLoc {
524 pub block: BlockId,
525 pub statement: usize,
526}
527
528impl StmtLoc {
529 pub fn new(block: BlockId, statement: usize) -> Self {
530 StmtLoc { block, statement }
531 }
532
533 pub fn block_start(block: BlockId) -> Self {
534 StmtLoc {
535 block,
536 statement: 0,
537 }
538 }
539
540 pub fn after(self) -> Self {
541 StmtLoc {
542 block: self.block,
543 statement: self.statement + 1,
544 }
545 }
546}
547
548impl Index<StmtLoc> for ExprBody {
549 type Output = Statement;
550 fn index(&self, loc: StmtLoc) -> &Self::Output {
551 &self.body[loc.block].statements[loc.statement]
552 }
553}
554
555impl IndexMut<StmtLoc> for ExprBody {
556 fn index_mut(&mut self, loc: StmtLoc) -> &mut Self::Output {
557 &mut self.body[loc.block].statements[loc.statement]
558 }
559}
560
561pub struct BodyBuilder {
563 pub span: Span,
565 pub body: ExprBody,
567 pub current_block: BlockId,
569 pub unwind_block: Option<BlockId>,
571}
572
573fn mk_block(span: Span, term: TerminatorKind, kind: UnwindKind) -> BlockData {
574 BlockData {
575 statements: vec![],
576 terminator: Terminator::new(span, term),
577 kind,
578 }
579}
580
581impl BodyBuilder {
582 pub fn new(span: Span, arg_count: usize) -> Self {
583 let mut body: ExprBody = GExprBody {
584 span,
585 locals: Locals::new(arg_count),
586 bound_body_regions: 0,
587 body: IndexVec::new(),
588 comments: vec![],
589 };
590 let current_block = body.body.push(BlockData {
591 statements: Default::default(),
592 terminator: Terminator::new(span, TerminatorKind::Return),
593 kind: UnwindKind::Regular,
594 });
595 Self {
596 span,
597 body,
598 current_block,
599 unwind_block: None,
600 }
601 }
602
603 pub fn build(mut self) -> ExprBody {
605 let mut freshener: IndexMap<RegionId, ()> = IndexMap::new();
607 self.body.dyn_visit_mut(|r: &mut Region| {
608 if r.is_erased() || r.is_body() {
609 *r = Region::Body(freshener.push(()));
610 }
611 });
612 self.body.bound_body_regions = freshener.slot_count();
613 self.body
615 }
616
617 pub fn new_var(&mut self, name: Option<String>, ty: Ty) -> Place {
620 let place = self.body.locals.new_var(name, ty);
621 let local_id = place.as_local().unwrap();
622 if !self.body.locals.is_return_or_arg(local_id) {
623 self.push_statement(StatementKind::StorageLive(local_id));
624 }
625 place
626 }
627
628 fn current_block(&mut self) -> &mut BlockData {
630 &mut self.body.body[self.current_block]
631 }
632
633 pub fn push_statement(&mut self, kind: StatementKind) {
634 let st = Statement::new(self.span, kind);
635 self.current_block().statements.push(st);
636 }
637
638 fn unwind_block(&mut self) -> BlockId {
639 *self.unwind_block.get_or_insert_with(|| {
640 self.body.body.push(mk_block(
641 self.span,
642 TerminatorKind::UnwindResume,
643 UnwindKind::Cleanup,
644 ))
645 })
646 }
647
648 pub fn call(&mut self, call: Call) {
649 let next_block = self.body.body.push(mk_block(
650 self.span,
651 TerminatorKind::Return,
652 UnwindKind::Regular,
653 ));
654 let term = TerminatorKind::Call {
655 target: next_block,
656 call,
657 on_unwind: self.unwind_block(),
658 };
659 self.current_block().terminator.kind = term;
660 self.current_block = next_block;
661 }
662
663 pub fn insert_drop(&mut self, place: Place, fn_ptr: FnPtr) {
664 let next_block = self.body.body.push(mk_block(
665 self.span,
666 TerminatorKind::Return,
667 UnwindKind::Regular,
668 ));
669 let term = TerminatorKind::Drop {
670 kind: DropKind::Precise,
671 place,
672 fn_ptr,
673 target: next_block,
674 on_unwind: self.unwind_block(),
675 };
676 self.current_block().terminator.kind = term;
677 self.current_block = next_block;
678 }
679}