1use derive_generic_visitor::{Drive, DriveMut, DriveTwo};
5use macros::{EnumAsGetters, EnumIsA, 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 pub comments_before: Vec<String>,
45}
46
47#[derive(
48 Debug,
49 PartialEq,
50 Eq,
51 Clone,
52 EnumIsA,
53 EnumAsGetters,
54 VariantName,
55 SerializeState,
56 DeserializeState,
57 Drive,
58 DriveMut,
59 DriveTwo,
60)]
61pub enum StatementKind {
62 Assign(Place, Rvalue),
63 SetDiscriminant(Place, VariantId),
65 StorageLive(LocalId),
70 StorageDead(LocalId),
75 PlaceMention(Place),
79 Borrowck(BorrowckStatement),
81 Assert {
88 assert: Assert,
89 on_failure: AbortKind,
90 },
91 Nop,
93}
94
95#[derive(
96 Debug,
97 PartialEq,
98 Eq,
99 Clone,
100 EnumIsA,
101 EnumAsGetters,
102 SerializeState,
103 DeserializeState,
104 Drive,
105 DriveMut,
106 DriveTwo,
107)]
108pub enum TerminatorKind {
109 Goto {
110 target: BlockId,
111 },
112 Switch {
113 data: SwitchData,
114 branches: IndexVec<BranchId, BlockId>,
115 },
116 Call {
117 call: Call,
118 target: BlockId,
119 on_unwind: BlockId,
120 },
121 Drop {
127 kind: DropKind,
128 place: Place,
129 fn_ptr: FnPtr,
131 target: BlockId,
132 on_unwind: BlockId,
133 },
134 #[cfg_attr(feature = "charon_on_charon", charon::rename("TAssert"))]
137 Assert {
138 assert: Assert,
139 target: BlockId,
140 on_unwind: BlockId,
141 },
142 InlineAsm {
144 asm: String,
145 targets: Vec<BlockId>,
146 on_unwind: BlockId,
147 },
148 Abort(AbortKind),
150 Return,
151 UnwindResume,
153}
154
155#[derive(
157 Debug, PartialEq, Eq, Clone, SerializeState, DeserializeState, Drive, DriveMut, DriveTwo,
158)]
159pub struct Terminator {
160 pub span: Span,
161 pub kind: TerminatorKind,
162 pub comments_before: Vec<String>,
165}
166
167impl ExprBody {
168 pub fn as_abort_map(&self) -> HashMap<BlockId, AbortKind> {
172 self.body
173 .iter_enumerated()
174 .filter_map(|(bid, block)| block.as_abort().map(|abort| (bid, abort)))
175 .collect()
176 }
177
178 pub fn transform_sequences_fwd<F>(&mut self, mut f: F)
179 where
180 F: FnMut(BlockId, &mut Locals, &mut [Statement]) -> Vec<(usize, Vec<Statement>)>,
181 {
182 for (id, block) in &mut self.body.iter_mut_enumerated() {
183 block.transform_sequences_fwd(|seq| f(id, &mut self.locals, seq));
184 }
185 }
186
187 pub fn transform_sequences_bwd<F>(&mut self, mut f: F)
188 where
189 F: FnMut(&mut Locals, &mut [Statement]) -> Vec<(usize, Vec<Statement>)>,
190 {
191 for block in &mut self.body {
192 block.transform_sequences_bwd(|seq| f(&mut self.locals, seq));
193 }
194 }
195
196 pub fn visit_statements<F: FnMut(&mut Statement)>(&mut self, mut f: F) {
198 for block in self.body.iter_mut().rev() {
199 for st in block.statements.iter_mut().rev() {
200 f(st);
201 }
202 }
203 }
204}
205
206impl BlockData {
207 pub fn new_goto(span: Span, target: BlockId) -> Self {
209 BlockData {
210 statements: vec![],
211 terminator: Terminator::goto(span, target),
212 }
213 }
214 pub fn as_goto(&self) -> Option<BlockId> {
215 if let TerminatorKind::Goto { target } = self.terminator.kind {
216 Some(target)
217 } else {
218 None
219 }
220 }
221 pub fn as_trivial_goto(&self) -> Option<BlockId> {
222 self.as_goto().filter(|_| {
223 self.statements
224 .iter()
225 .all(|st| matches!(st.kind, StatementKind::Nop))
226 })
227 }
228
229 pub fn as_abort(&self) -> Option<AbortKind> {
230 if self.statements.iter().all(|st| {
231 matches!(
232 st.kind,
233 StatementKind::Nop | StatementKind::StorageLive(_) | StatementKind::StorageDead(_)
234 )
235 }) && let TerminatorKind::Abort(abort) = &self.terminator.kind
236 {
237 Some(abort.clone())
238 } else {
239 None
240 }
241 }
242
243 pub fn new_unreachable() -> Self {
245 Terminator::new(
246 Span::dummy(),
247 TerminatorKind::Abort(AbortKind::UndefinedBehavior),
248 )
249 .into_block()
250 }
251
252 pub fn targets(&self) -> SmallVec<[BlockId; 2]> {
253 self.terminator.targets()
254 }
255 pub fn targets_ignoring_unwind(&self) -> SmallVec<[BlockId; 2]> {
256 self.terminator.targets_ignoring_unwind()
257 }
258
259 pub fn transform<F: FnMut(&mut Statement) -> Vec<Statement>>(&mut self, mut f: F) {
265 self.transform_sequences_fwd(|slice| {
266 let new_statements = f(&mut slice[0]);
267 if new_statements.is_empty() {
268 vec![]
269 } else {
270 vec![(0, new_statements)]
271 }
272 });
273 }
274
275 fn transform_sequences<F>(&mut self, mut f: F, forward: bool)
277 where
278 F: FnMut(&mut [Statement]) -> Vec<(usize, Vec<Statement>)>,
279 {
280 let mut to_insert = vec![];
281 let mut final_len = self.statements.len();
282 if forward {
283 for i in 0..self.statements.len() {
284 let new_to_insert = f(&mut self.statements[i..]);
285 to_insert.extend(new_to_insert.into_iter().map(|(j, stmts)| {
286 final_len += stmts.len();
287 (i + j, stmts)
288 }));
289 }
290 } else {
291 for i in (0..self.statements.len()).rev() {
292 let new_to_insert = f(&mut self.statements[i..]);
293 to_insert.extend(new_to_insert.into_iter().map(|(j, stmts)| {
294 final_len += stmts.len();
295 (i + j, stmts)
296 }));
297 }
298 }
299 if !to_insert.is_empty() {
300 to_insert.sort_by_key(|(i, _)| *i);
301 to_insert.reverse();
303 let old_statements = mem::replace(&mut self.statements, Vec::with_capacity(final_len));
305 for (i, stmt) in old_statements.into_iter().enumerate() {
306 while let Some((j, _)) = to_insert.last()
307 && *j == i
308 {
309 let (_, mut stmts) = to_insert.pop().unwrap();
310 self.statements.append(&mut stmts);
311 }
312 self.statements.push(stmt);
313 }
314 }
315 }
316
317 pub fn transform_sequences_fwd<F>(&mut self, f: F)
323 where
324 F: FnMut(&mut [Statement]) -> Vec<(usize, Vec<Statement>)>,
325 {
326 self.transform_sequences(f, true);
327 }
328
329 pub fn transform_sequences_bwd<F>(&mut self, f: F)
335 where
336 F: FnMut(&mut [Statement]) -> Vec<(usize, Vec<Statement>)>,
337 {
338 self.transform_sequences(f, false);
339 }
340}
341
342impl Statement {
343 pub fn new(span: Span, kind: StatementKind) -> Self {
344 Statement {
345 span,
346 kind,
347 comments_before: vec![],
348 }
349 }
350}
351
352impl Terminator {
353 pub fn new(span: Span, kind: TerminatorKind) -> Self {
354 Terminator {
355 span,
356 kind,
357 comments_before: vec![],
358 }
359 }
360 pub fn goto(span: Span, target: BlockId) -> Self {
361 Self::new(span, TerminatorKind::Goto { target })
362 }
363 pub fn is_error(&self) -> bool {
365 use TerminatorKind::*;
366 match &self.kind {
367 Abort(..) => true,
368 Goto { .. }
369 | Switch { .. }
370 | InlineAsm { .. }
371 | Return
372 | Call { .. }
373 | Drop { .. }
374 | UnwindResume
375 | Assert { .. } => false,
376 }
377 }
378
379 pub fn into_block(self) -> BlockData {
380 BlockData {
381 statements: vec![],
382 terminator: self,
383 }
384 }
385
386 pub fn targets(&self) -> SmallVec<[BlockId; 2]> {
387 match &self.kind {
388 TerminatorKind::Goto { target } => {
389 smallvec![*target]
390 }
391 TerminatorKind::Switch { branches, .. } => branches.iter().copied().collect(),
392 TerminatorKind::InlineAsm {
393 targets, on_unwind, ..
394 } => targets.iter().copied().chain([*on_unwind]).collect(),
395 TerminatorKind::Call {
396 target, on_unwind, ..
397 }
398 | TerminatorKind::Drop {
399 target, on_unwind, ..
400 }
401 | TerminatorKind::Assert {
402 target, on_unwind, ..
403 } => smallvec![*target, *on_unwind],
404 TerminatorKind::Abort(..) | TerminatorKind::Return | TerminatorKind::UnwindResume => {
405 smallvec![]
406 }
407 }
408 }
409 pub fn targets_mut(&mut self) -> SmallVec<[&mut BlockId; 2]> {
410 match &mut self.kind {
411 TerminatorKind::Goto { target } => {
412 smallvec![target]
413 }
414 TerminatorKind::Switch { branches, .. } => branches.iter_mut().collect(),
415 TerminatorKind::InlineAsm {
416 targets, on_unwind, ..
417 } => targets.iter_mut().chain([on_unwind]).collect(),
418 TerminatorKind::Call {
419 target, on_unwind, ..
420 }
421 | TerminatorKind::Drop {
422 target, on_unwind, ..
423 }
424 | TerminatorKind::Assert {
425 target, on_unwind, ..
426 } => smallvec![target, on_unwind],
427 TerminatorKind::Abort(..) | TerminatorKind::Return | TerminatorKind::UnwindResume => {
428 smallvec![]
429 }
430 }
431 }
432
433 pub fn targets_ignoring_unwind(&self) -> SmallVec<[BlockId; 2]> {
434 match &self.kind {
435 TerminatorKind::Goto { target } => {
436 smallvec![*target]
437 }
438 TerminatorKind::Switch { branches, .. } => branches.iter().copied().collect(),
439 TerminatorKind::InlineAsm { targets, .. } => targets.iter().copied().collect(),
440 TerminatorKind::Call { target, .. }
441 | TerminatorKind::Drop { target, .. }
442 | TerminatorKind::Assert { target, .. } => {
443 smallvec![*target]
444 }
445 TerminatorKind::Abort(..) | TerminatorKind::Return | TerminatorKind::UnwindResume => {
446 smallvec![]
447 }
448 }
449 }
450}
451
452#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
454pub struct StmtLoc {
455 pub block: BlockId,
456 pub statement: usize,
457}
458
459impl StmtLoc {
460 pub fn new(block: BlockId, statement: usize) -> Self {
461 StmtLoc { block, statement }
462 }
463
464 pub fn block_start(block: BlockId) -> Self {
465 StmtLoc {
466 block,
467 statement: 0,
468 }
469 }
470
471 pub fn after(self) -> Self {
472 StmtLoc {
473 block: self.block,
474 statement: self.statement + 1,
475 }
476 }
477}
478
479impl Index<StmtLoc> for ExprBody {
480 type Output = Statement;
481 fn index(&self, loc: StmtLoc) -> &Self::Output {
482 &self.body[loc.block].statements[loc.statement]
483 }
484}
485
486impl IndexMut<StmtLoc> for ExprBody {
487 fn index_mut(&mut self, loc: StmtLoc) -> &mut Self::Output {
488 &mut self.body[loc.block].statements[loc.statement]
489 }
490}
491
492pub struct BodyBuilder {
494 pub span: Span,
496 pub body: ExprBody,
498 pub current_block: BlockId,
500 pub unwind_block: Option<BlockId>,
502}
503
504fn mk_block(span: Span, term: TerminatorKind) -> BlockData {
505 BlockData {
506 statements: vec![],
507 terminator: Terminator::new(span, term),
508 }
509}
510
511impl BodyBuilder {
512 pub fn new(span: Span, arg_count: usize) -> Self {
513 let mut body: ExprBody = GExprBody {
514 span,
515 locals: Locals::new(arg_count),
516 bound_body_regions: 0,
517 body: IndexVec::new(),
518 comments: vec![],
519 };
520 let current_block = body.body.push(BlockData {
521 statements: Default::default(),
522 terminator: Terminator::new(span, TerminatorKind::Return),
523 });
524 Self {
525 span,
526 body,
527 current_block,
528 unwind_block: None,
529 }
530 }
531
532 pub fn build(mut self) -> ExprBody {
534 let mut freshener: IndexMap<RegionId, ()> = IndexMap::new();
536 self.body.dyn_visit_mut(|r: &mut Region| {
537 if r.is_erased() || r.is_body() {
538 *r = Region::Body(freshener.push(()));
539 }
540 });
541 self.body.bound_body_regions = freshener.slot_count();
542 self.body
544 }
545
546 pub fn new_var(&mut self, name: Option<String>, ty: Ty) -> Place {
549 let place = self.body.locals.new_var(name, ty);
550 let local_id = place.as_local().unwrap();
551 if !self.body.locals.is_return_or_arg(local_id) {
552 self.push_statement(StatementKind::StorageLive(local_id));
553 }
554 place
555 }
556
557 fn current_block(&mut self) -> &mut BlockData {
559 &mut self.body.body[self.current_block]
560 }
561
562 pub fn push_statement(&mut self, kind: StatementKind) {
563 let st = Statement::new(self.span, kind);
564 self.current_block().statements.push(st);
565 }
566
567 fn unwind_block(&mut self) -> BlockId {
568 *self.unwind_block.get_or_insert_with(|| {
569 self.body
570 .body
571 .push(mk_block(self.span, TerminatorKind::UnwindResume))
572 })
573 }
574
575 pub fn call(&mut self, call: Call) {
576 let next_block = self
577 .body
578 .body
579 .push(mk_block(self.span, TerminatorKind::Return));
580 let term = TerminatorKind::Call {
581 target: next_block,
582 call,
583 on_unwind: self.unwind_block(),
584 };
585 self.current_block().terminator.kind = term;
586 self.current_block = next_block;
587 }
588
589 pub fn insert_drop(&mut self, place: Place, fn_ptr: FnPtr) {
590 let next_block = self
591 .body
592 .body
593 .push(mk_block(self.span, TerminatorKind::Return));
594 let term = TerminatorKind::Drop {
595 kind: DropKind::Precise,
596 place,
597 fn_ptr,
598 target: next_block,
599 on_unwind: self.unwind_block(),
600 };
601 self.current_block().terminator.kind = term;
602 self.current_block = next_block;
603 }
604}