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 is_cleanup: bool,
34}
35
36#[derive(Debug, Clone, PartialEq, Eq)]
38#[derive(SerializeState, DeserializeState, Drive, DriveMut, DriveTwo)]
39pub struct Statement {
40 pub span: Span,
41 pub kind: StatementKind,
42 pub comments_before: Vec<String>,
45}
46
47#[derive(Debug, Clone, PartialEq, Eq)]
48#[derive(EnumIsA, EnumAsGetters, VariantName)]
49#[derive(SerializeState, DeserializeState, Drive, DriveMut, DriveTwo)]
50pub enum StatementKind {
51 Assign(Place, Rvalue),
52 SetDiscriminant(Place, VariantId),
54 StorageLive(LocalId),
59 StorageDead(LocalId),
64 PlaceMention(Place),
68 Borrowck(BorrowckStatement),
70 Assert {
77 assert: Assert,
78 on_failure: AbortKind,
79 },
80 Nop,
82}
83
84#[derive(Debug, Clone, PartialEq, Eq)]
85#[derive(EnumIsA, EnumAsGetters)]
86#[derive(SerializeState, DeserializeState, Drive, DriveMut, DriveTwo)]
87pub enum TerminatorKind {
88 Goto {
89 target: BlockId,
90 },
91 Switch {
92 data: SwitchData,
93 branches: IndexVec<BranchId, BlockId>,
94 },
95 Call {
96 call: Call,
97 target: BlockId,
98 on_unwind: BlockId,
99 },
100 Drop {
106 kind: DropKind,
107 place: Place,
108 fn_ptr: FnPtr,
110 target: BlockId,
111 on_unwind: BlockId,
112 },
113 InlineAsm {
115 asm: InlineAsm,
116 fallthrough: Option<BlockId>,
118 labels: IndexVec<BranchId, BlockId>,
120 on_unwind: BlockId,
122 },
123
124 #[cfg_attr(feature = "charon_on_charon", charon::rename("TAssert"))]
127 Assert {
128 assert: Assert,
129 target: BlockId,
130 on_unwind: BlockId,
131 },
132 Panic {
134 name: Name,
136 on_unwind: BlockId,
137 },
138 UnwindTerminate,
140 UnwindResume,
142
143 Return,
144 UndefinedBehavior,
146}
147
148#[derive(Debug, Clone, PartialEq, Eq)]
150#[derive(SerializeState, DeserializeState, Drive, DriveMut, DriveTwo)]
151pub struct Terminator {
152 pub span: Span,
153 pub kind: TerminatorKind,
154 pub comments_before: Vec<String>,
157}
158
159impl ExprBody {
160 pub fn as_abort_map(&self) -> HashMap<BlockId, AbortKind> {
163 self.body
164 .iter_enumerated()
165 .filter_map(|(bid, block)| block.as_abort().map(|abort| (bid, abort)))
166 .collect()
167 }
168
169 pub fn transform_sequences_fwd<F>(&mut self, mut f: F)
170 where
171 F: FnMut(BlockId, &mut Locals, &mut [Statement]) -> Vec<(usize, Vec<Statement>)>,
172 {
173 for (id, block) in &mut self.body.iter_mut_enumerated() {
174 block.transform_sequences_fwd(|seq| f(id, &mut self.locals, seq));
175 }
176 }
177
178 pub fn transform_sequences_bwd<F>(&mut self, mut f: F)
179 where
180 F: FnMut(&mut Locals, &mut [Statement]) -> Vec<(usize, Vec<Statement>)>,
181 {
182 for block in &mut self.body {
183 block.transform_sequences_bwd(|seq| f(&mut self.locals, seq));
184 }
185 }
186
187 pub fn visit_block_ids_mut<F: FnMut(&mut BlockId)>(&mut self, mut f: F) {
189 for block in &mut self.body {
190 block.terminator.targets_mut().into_iter().for_each(&mut f);
191 }
192 }
193
194 pub fn visit_statements<F: FnMut(&mut Statement)>(&mut self, mut f: F) {
196 for block in self.body.iter_mut().rev() {
197 for st in block.statements.iter_mut().rev() {
198 f(st);
199 }
200 }
201 }
202}
203
204impl BlockData {
205 pub fn new_goto(span: Span, target: BlockId, is_cleanup: bool) -> Self {
207 BlockData {
208 statements: vec![],
209 terminator: Terminator::goto(span, target),
210 is_cleanup,
211 }
212 }
213 pub fn as_goto(&self) -> Option<BlockId> {
214 if let TerminatorKind::Goto { target } = self.terminator.kind {
215 Some(target)
216 } else {
217 None
218 }
219 }
220 pub fn as_trivial_goto(&self) -> Option<BlockId> {
221 self.as_goto().filter(|_| {
222 self.statements
223 .iter()
224 .all(|st| matches!(st.kind, StatementKind::Nop))
225 })
226 }
227
228 pub fn as_abort(&self) -> Option<AbortKind> {
229 if self.statements.iter().all(|st| {
230 matches!(
231 st.kind,
232 StatementKind::Nop | StatementKind::StorageLive(_) | StatementKind::StorageDead(_)
233 )
234 }) {
235 match &self.terminator.kind {
236 TerminatorKind::Panic { name, .. } => Some(AbortKind::Panic(Some(name.clone()))),
237 TerminatorKind::UndefinedBehavior => Some(AbortKind::UndefinedBehavior),
238 TerminatorKind::UnwindTerminate => Some(AbortKind::UnwindTerminate),
239 _ => None,
240 }
241 } else {
242 None
243 }
244 }
245
246 pub fn new_unreachable(is_cleanup: bool) -> Self {
248 Terminator::new(Span::dummy(), TerminatorKind::UndefinedBehavior).into_block(is_cleanup)
249 }
250 pub fn take(&mut self) -> Self {
252 mem::replace(self, BlockData::new_unreachable(self.is_cleanup))
253 }
254
255 pub fn targets(&self) -> SmallVec<[BlockId; 2]> {
256 self.terminator.targets()
257 }
258 pub fn targets_ignoring_unwind(&self) -> SmallVec<[BlockId; 2]> {
259 self.terminator.targets_ignoring_unwind()
260 }
261
262 pub fn transform<F: FnMut(&mut Statement) -> Vec<Statement>>(&mut self, mut f: F) {
268 self.transform_sequences_fwd(|slice| {
269 let new_statements = f(&mut slice[0]);
270 if new_statements.is_empty() {
271 vec![]
272 } else {
273 vec![(0, new_statements)]
274 }
275 });
276 }
277
278 fn transform_sequences<F>(&mut self, mut f: F, forward: bool)
280 where
281 F: FnMut(&mut [Statement]) -> Vec<(usize, Vec<Statement>)>,
282 {
283 let mut to_insert = vec![];
284 let mut final_len = self.statements.len();
285 if forward {
286 for i in 0..self.statements.len() {
287 let new_to_insert = f(&mut self.statements[i..]);
288 to_insert.extend(new_to_insert.into_iter().map(|(j, stmts)| {
289 final_len += stmts.len();
290 (i + j, stmts)
291 }));
292 }
293 } else {
294 for i in (0..self.statements.len()).rev() {
295 let new_to_insert = f(&mut self.statements[i..]);
296 to_insert.extend(new_to_insert.into_iter().map(|(j, stmts)| {
297 final_len += stmts.len();
298 (i + j, stmts)
299 }));
300 }
301 }
302 if !to_insert.is_empty() {
303 to_insert.sort_by_key(|(i, _)| *i);
304 to_insert.reverse();
306 let old_statements = mem::replace(&mut self.statements, Vec::with_capacity(final_len));
308 for (i, stmt) in old_statements.into_iter().enumerate() {
309 while let Some((j, _)) = to_insert.last()
310 && *j == i
311 {
312 let (_, mut stmts) = to_insert.pop().unwrap();
313 self.statements.append(&mut stmts);
314 }
315 self.statements.push(stmt);
316 }
317 }
318 }
319
320 pub fn transform_sequences_fwd<F>(&mut self, f: F)
326 where
327 F: FnMut(&mut [Statement]) -> Vec<(usize, Vec<Statement>)>,
328 {
329 self.transform_sequences(f, true);
330 }
331
332 pub fn transform_sequences_bwd<F>(&mut self, f: F)
338 where
339 F: FnMut(&mut [Statement]) -> Vec<(usize, Vec<Statement>)>,
340 {
341 self.transform_sequences(f, false);
342 }
343}
344
345impl Statement {
346 pub fn new(span: Span, kind: StatementKind) -> Self {
347 Statement {
348 span,
349 kind,
350 comments_before: vec![],
351 }
352 }
353}
354
355impl Terminator {
356 pub fn new(span: Span, kind: TerminatorKind) -> Self {
357 Terminator {
358 span,
359 kind,
360 comments_before: vec![],
361 }
362 }
363 pub fn goto(span: Span, target: BlockId) -> Self {
364 Self::new(span, TerminatorKind::Goto { target })
365 }
366 pub fn is_error(&self) -> bool {
368 use TerminatorKind::*;
369 match &self.kind {
370 Panic { .. } | UndefinedBehavior | UnwindTerminate => true,
371 Goto { .. }
372 | Switch { .. }
373 | InlineAsm { .. }
374 | Return
375 | Call { .. }
376 | Drop { .. }
377 | UnwindResume
378 | Assert { .. } => false,
379 }
380 }
381
382 pub fn into_block(self, is_cleanup: bool) -> BlockData {
383 BlockData {
384 statements: vec![],
385 terminator: self,
386 is_cleanup,
387 }
388 }
389
390 pub fn targets(&self) -> SmallVec<[BlockId; 2]> {
391 match &self.kind {
392 TerminatorKind::Goto { target } => {
393 smallvec![*target]
394 }
395 TerminatorKind::Switch { branches, .. } => branches.iter().copied().collect(),
396 TerminatorKind::InlineAsm {
397 fallthrough,
398 labels,
399 on_unwind,
400 ..
401 } => fallthrough
402 .iter()
403 .copied()
404 .chain(labels.iter().copied())
405 .chain([*on_unwind])
406 .collect(),
407 TerminatorKind::Call {
408 target, on_unwind, ..
409 }
410 | TerminatorKind::Drop {
411 target, on_unwind, ..
412 }
413 | TerminatorKind::Assert {
414 target, on_unwind, ..
415 } => smallvec![*target, *on_unwind],
416 TerminatorKind::Panic { on_unwind, .. } => smallvec![*on_unwind],
417 TerminatorKind::UndefinedBehavior
418 | TerminatorKind::UnwindTerminate
419 | TerminatorKind::Return
420 | TerminatorKind::UnwindResume => {
421 smallvec![]
422 }
423 }
424 }
425 pub fn targets_mut(&mut self) -> SmallVec<[&mut BlockId; 2]> {
426 match &mut self.kind {
427 TerminatorKind::Goto { target } => {
428 smallvec![target]
429 }
430 TerminatorKind::Switch { branches, .. } => branches.iter_mut().collect(),
431 TerminatorKind::InlineAsm {
432 fallthrough,
433 labels,
434 on_unwind,
435 ..
436 } => fallthrough
437 .iter_mut()
438 .chain(labels.iter_mut())
439 .chain([on_unwind])
440 .collect(),
441 TerminatorKind::Call {
442 target, on_unwind, ..
443 }
444 | TerminatorKind::Drop {
445 target, on_unwind, ..
446 }
447 | TerminatorKind::Assert {
448 target, on_unwind, ..
449 } => smallvec![target, on_unwind],
450 TerminatorKind::Panic { on_unwind, .. } => smallvec![on_unwind],
451 TerminatorKind::UndefinedBehavior
452 | TerminatorKind::UnwindTerminate
453 | TerminatorKind::Return
454 | TerminatorKind::UnwindResume => {
455 smallvec![]
456 }
457 }
458 }
459
460 pub fn targets_ignoring_unwind(&self) -> SmallVec<[BlockId; 2]> {
461 match &self.kind {
462 TerminatorKind::Goto { target } => {
463 smallvec![*target]
464 }
465 TerminatorKind::Switch { branches, .. } => branches.iter().copied().collect(),
466 TerminatorKind::InlineAsm {
467 fallthrough,
468 labels,
469 ..
470 } => fallthrough
471 .iter()
472 .copied()
473 .chain(labels.iter().copied())
474 .collect(),
475 TerminatorKind::Call { target, .. }
476 | TerminatorKind::Drop { target, .. }
477 | TerminatorKind::Assert { target, .. } => {
478 smallvec![*target]
479 }
480 TerminatorKind::Panic { .. }
481 | TerminatorKind::UndefinedBehavior
482 | TerminatorKind::UnwindTerminate
483 | TerminatorKind::Return
484 | TerminatorKind::UnwindResume => {
485 smallvec![]
486 }
487 }
488 }
489}
490
491impl TerminatorKind {
492 pub fn take(&mut self) -> Self {
494 std::mem::replace(self, TerminatorKind::UndefinedBehavior)
495 }
496}
497
498#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
500pub struct StmtLoc {
501 pub block: BlockId,
502 pub statement: usize,
503}
504
505impl StmtLoc {
506 pub fn new(block: BlockId, statement: usize) -> Self {
507 StmtLoc { block, statement }
508 }
509
510 pub fn block_start(block: BlockId) -> Self {
511 StmtLoc {
512 block,
513 statement: 0,
514 }
515 }
516
517 pub fn after(self) -> Self {
518 StmtLoc {
519 block: self.block,
520 statement: self.statement + 1,
521 }
522 }
523}
524
525impl Index<StmtLoc> for ExprBody {
526 type Output = Statement;
527 fn index(&self, loc: StmtLoc) -> &Self::Output {
528 &self.body[loc.block].statements[loc.statement]
529 }
530}
531
532impl IndexMut<StmtLoc> for ExprBody {
533 fn index_mut(&mut self, loc: StmtLoc) -> &mut Self::Output {
534 &mut self.body[loc.block].statements[loc.statement]
535 }
536}
537
538pub struct BodyBuilder {
540 pub span: Span,
542 pub body: ExprBody,
544 pub current_block: BlockId,
546 pub unwind_block: Option<BlockId>,
548}
549
550fn mk_block(span: Span, term: TerminatorKind, is_cleanup: bool) -> BlockData {
551 BlockData {
552 statements: vec![],
553 terminator: Terminator::new(span, term),
554 is_cleanup,
555 }
556}
557
558impl BodyBuilder {
559 pub fn new(span: Span, arg_count: usize) -> Self {
560 let mut body: ExprBody = GExprBody {
561 span,
562 locals: Locals::new(arg_count),
563 bound_body_regions: 0,
564 body: IndexVec::new(),
565 comments: vec![],
566 };
567 let current_block = body.body.push(BlockData {
568 statements: Default::default(),
569 terminator: Terminator::new(span, TerminatorKind::Return),
570 is_cleanup: false,
571 });
572 Self {
573 span,
574 body,
575 current_block,
576 unwind_block: None,
577 }
578 }
579
580 pub fn build(mut self) -> ExprBody {
582 let mut freshener: IndexMap<RegionId, ()> = IndexMap::new();
584 self.body.dyn_visit_mut(|r: &mut Region| {
585 if r.is_erased() || r.is_body() {
586 *r = Region::Body(freshener.push(()));
587 }
588 });
589 self.body.bound_body_regions = freshener.slot_count();
590 self.body
592 }
593
594 pub fn new_var(&mut self, name: Option<String>, ty: Ty) -> Place {
597 let place = self.body.locals.new_var(name, ty);
598 let local_id = place.as_local().unwrap();
599 if !self.body.locals.is_return_or_arg(local_id) {
600 self.push_statement(StatementKind::StorageLive(local_id));
601 }
602 place
603 }
604
605 fn current_block(&mut self) -> &mut BlockData {
607 &mut self.body.body[self.current_block]
608 }
609
610 pub fn push_statement(&mut self, kind: StatementKind) {
611 let st = Statement::new(self.span, kind);
612 self.current_block().statements.push(st);
613 }
614
615 fn unwind_block(&mut self) -> BlockId {
616 *self.unwind_block.get_or_insert_with(|| {
617 self.body
618 .body
619 .push(mk_block(self.span, TerminatorKind::UnwindResume, true))
620 })
621 }
622
623 pub fn call(&mut self, call: Call) {
624 let next_block = self
625 .body
626 .body
627 .push(mk_block(self.span, TerminatorKind::Return, false));
628 let term = TerminatorKind::Call {
629 target: next_block,
630 call,
631 on_unwind: self.unwind_block(),
632 };
633 self.current_block().terminator.kind = term;
634 self.current_block = next_block;
635 }
636
637 pub fn insert_drop(&mut self, place: Place, fn_ptr: FnPtr) {
638 let next_block = self
639 .body
640 .body
641 .push(mk_block(self.span, TerminatorKind::Return, false));
642 let term = TerminatorKind::Drop {
643 kind: DropKind::Precise,
644 place,
645 fn_ptr,
646 target: next_block,
647 on_unwind: self.unwind_block(),
648 };
649 self.current_block().terminator.kind = term;
650 self.current_block = next_block;
651 }
652}