1use super::bug_records::*;
2use super::types::*;
3use crate::analysis::{
4 core::heap_analysis::HAResult,
5 utils::intrinsic_id::*
6};
7use rustc_data_structures::fx::{FxHashMap, FxHashSet};
8use rustc_middle::mir::{
9 BasicBlock, Body, Const, Operand, Place, Rvalue, StatementKind, Terminator, TerminatorKind,
10 UnwindAction,
11};
12use rustc_middle::ty::{self, TyCtxt, TypingEnv};
13use rustc_span::{
14 Span,
15 def_id::DefId
16};
17use std::{
18 cell::RefCell,
19 cmp::min,
20 vec::Vec
21};
22
23#[derive(PartialEq, Debug, Copy, Clone)]
24pub enum AssignType {
25 Copy,
26 Move,
27 InitBox,
28 Variant,
29}
30
31#[derive(Debug, Clone)]
33pub struct Assignment<'tcx> {
34 pub lv: Place<'tcx>,
35 pub rv: Place<'tcx>,
36 pub atype: AssignType,
37 pub span: Span,
38}
39
40impl<'tcx> Assignment<'tcx> {
41 pub fn new(
42 lv: Place<'tcx>,
43 rv: Place<'tcx>,
44 atype: AssignType,
45 span: Span,
46 ) -> Assignment<'tcx> {
47 Assignment {
48 lv,
49 rv,
50 atype,
51 span,
52 }
53 }
54}
55
56#[derive(Debug, Clone)]
62pub struct BlockNode<'tcx> {
63 pub index: usize,
64 pub is_cleanup: bool,
65 pub next: FxHashSet<usize>,
66 pub assignments: Vec<Assignment<'tcx>>,
67 pub calls: Vec<Terminator<'tcx>>,
68 pub drops: Vec<Terminator<'tcx>>,
69 pub scc_sub_blocks: Vec<usize>,
71 pub const_value: Vec<(usize, usize)>,
73 pub switch_stmts: Vec<Terminator<'tcx>>,
75 pub modified_value: FxHashSet<usize>,
76 pub scc_outer: SccOuter,
78}
79
80pub type SccOuter = RefCell<Option<FxHashMap<(usize, usize), Vec<usize>>>>;
81
82impl<'tcx> BlockNode<'tcx> {
83 pub fn new(index: usize, is_cleanup: bool) -> BlockNode<'tcx> {
84 BlockNode {
85 index,
86 is_cleanup,
87 next: FxHashSet::<usize>::default(),
88 assignments: Vec::<Assignment<'tcx>>::new(),
89 calls: Vec::<Terminator<'tcx>>::new(),
90 drops: Vec::<Terminator<'tcx>>::new(),
91 scc_sub_blocks: Vec::<usize>::new(),
92 const_value: Vec::<(usize, usize)>::new(),
93 switch_stmts: Vec::<Terminator<'tcx>>::new(),
94 modified_value: FxHashSet::<usize>::default(),
95 scc_outer: RefCell::new(None),
96 }
97 }
98
99 pub fn add_next(&mut self, index: usize) {
100 self.next.insert(index);
101 }
102}
103
104#[derive(Debug, Clone)]
105pub struct ValueNode {
106 pub index: usize, pub local: usize, pub need_drop: bool,
109 pub may_drop: bool,
110 pub kind: TyKind,
111 pub father: usize,
112 pub field_id: usize, pub birth: isize,
114 pub fields: FxHashMap<usize, usize>,
115}
116
117impl ValueNode {
118 pub fn new(index: usize, local: usize, need_drop: bool, may_drop: bool) -> Self {
119 ValueNode {
120 index,
121 local,
122 need_drop,
123 father: local,
124 field_id: usize::MAX,
125 birth: 0,
126 may_drop,
127 kind: TyKind::Adt,
128 fields: FxHashMap::default(),
129 }
130 }
131
132 pub fn dead(&mut self) {
133 self.birth = -1;
134 }
135
136 pub fn is_alive(&self) -> bool {
137 self.birth > -1
138 }
139
140 pub fn is_tuple(&self) -> bool {
141 self.kind == TyKind::Tuple
142 }
143
144 pub fn is_ptr(&self) -> bool {
145 self.kind == TyKind::RawPtr || self.kind == TyKind::Ref
146 }
147
148 pub fn is_ref(&self) -> bool {
149 self.kind == TyKind::Ref
150 }
151
152 pub fn is_corner_case(&self) -> bool {
153 self.kind == TyKind::CornerCase
154 }
155}
156
157pub struct SafeDropGraph<'tcx> {
158 pub def_id: DefId,
159 pub tcx: TyCtxt<'tcx>,
160 pub span: Span,
161 pub values: Vec<ValueNode>,
163 pub blocks: Vec<BlockNode<'tcx>>,
165 pub arg_size: usize,
166 pub scc_indices: Vec<usize>,
168 pub constant: FxHashMap<usize, usize>,
170 pub return_set: FxHashSet<(usize, usize)>,
172 pub bug_records: BugRecords,
174 pub visit_times: usize,
176 pub alias_set: Vec<usize>,
177 pub dead_record: Vec<bool>,
178 pub adt_owner: HAResult,
180 pub child_scc: FxHashMap<
181 usize,
182 (
183 BlockNode<'tcx>,
184 rustc_middle::mir::SwitchTargets,
185 FxHashSet<usize>,
186 ),
187 >,
188 pub disc_map: FxHashMap<usize, usize>,
189 pub terms: Vec<TerminatorKind<'tcx>>,
190}
191
192impl<'tcx> SafeDropGraph<'tcx> {
193 pub fn new(
194 body: &Body<'tcx>,
195 tcx: TyCtxt<'tcx>,
196 def_id: DefId,
197 adt_owner: HAResult,
198 ) -> SafeDropGraph<'tcx> {
199 let locals = &body.local_decls;
201 let arg_size = body.arg_count;
202 let mut values = Vec::<ValueNode>::new();
203 let mut alias = Vec::<usize>::new();
204 let mut dead = Vec::<bool>::new();
205 let ty_env = TypingEnv::post_analysis(tcx, def_id);
206 for (local, local_decl) in locals.iter_enumerated() {
207 let need_drop = local_decl.ty.needs_drop(tcx, ty_env); let may_drop = !is_not_drop(tcx, local_decl.ty);
209 let mut node = ValueNode::new(
210 local.as_usize(),
211 local.as_usize(),
212 need_drop,
213 need_drop || may_drop,
214 );
215 node.kind = kind(local_decl.ty);
216 alias.push(alias.len());
217 dead.push(false);
218 values.push(node);
219 }
220
221 let basicblocks = &body.basic_blocks;
222 let mut blocks = Vec::<BlockNode<'tcx>>::new();
223 let mut scc_indices = Vec::<usize>::new();
224 let mut disc_map = FxHashMap::default();
225 let mut terms = Vec::new();
226
227 for i in 0..basicblocks.len() {
229 scc_indices.push(i);
230 let iter = BasicBlock::from(i);
231 let terminator = basicblocks[iter].terminator.clone().unwrap();
232 let mut cur_bb = BlockNode::new(i, basicblocks[iter].is_cleanup);
233
234 for stmt in &basicblocks[iter].statements {
236 let span = stmt.source_info.span;
238 if let StatementKind::Assign(ref assign) = stmt.kind {
239 let lv_local = assign.0.local.as_usize(); let lv = assign.0;
241 cur_bb.modified_value.insert(lv_local);
242 match assign.1.clone() {
244 Rvalue::Use(x) => {
245 match x {
246 Operand::Copy(rv) => {
247 let rv_local = rv.local.as_usize();
248 if values[lv_local].may_drop && values[rv_local].may_drop {
249 let assign =
250 Assignment::new(lv, rv, AssignType::Copy, span);
251 cur_bb.assignments.push(assign);
252 }
253 }
254 Operand::Move(rv) => {
255 let rv_local = rv.local.as_usize();
256 if values[lv_local].may_drop && values[rv_local].may_drop {
257 let assign =
258 Assignment::new(lv, rv, AssignType::Move, span);
259 cur_bb.assignments.push(assign);
260 }
261 }
262 Operand::Constant(constant) => {
263 match constant.const_ {
265 Const::Ty(_ty, const_value) => {
266 if let Some(val) = const_value.try_to_target_usize(tcx)
267 {
268 cur_bb.const_value.push((lv_local, val as usize));
269 }
270 }
271 Const::Unevaluated(_unevaluated, _ty) => {}
272 Const::Val(const_value, _ty) => {
273 if let Some(scalar) = const_value.try_to_scalar_int() {
275 let val = scalar.to_uint(scalar.size());
276 cur_bb.const_value.push((lv_local, val as usize));
277 }
278 }
279 }
280 }
281 }
282 }
283 Rvalue::Ref(_, _, rv) | Rvalue::RawPtr(_, rv) => {
284 let rv_local = rv.local.as_usize();
285 if values[lv_local].may_drop && values[rv_local].may_drop {
286 let assign = Assignment::new(lv, rv, AssignType::Copy, span);
287 cur_bb.assignments.push(assign);
288 }
289 }
290 Rvalue::ShallowInitBox(x, _) => {
291 #[allow(clippy::map_entry)]
297 if !values[lv_local].fields.contains_key(&0) {
298 let mut lvl0 = ValueNode::new(values.len(), lv_local, false, true);
299 lvl0.birth = values[lv_local].birth;
300 lvl0.field_id = 0;
301 values[lv_local].fields.insert(0, lvl0.index);
302 alias.push(alias.len());
303 dead.push(false);
304 values.push(lvl0);
305 }
306 match x {
307 Operand::Copy(rv) | Operand::Move(rv) => {
308 let rv_local = rv.local.as_usize();
309 if values[lv_local].may_drop && values[rv_local].may_drop {
310 let assign =
311 Assignment::new(lv, rv, AssignType::InitBox, span);
312 cur_bb.assignments.push(assign);
313 }
314 }
315 Operand::Constant(_) => {}
316 }
317 }
318 Rvalue::Cast(_, x, _) => match x {
319 Operand::Copy(rv) => {
320 let rv_local = rv.local.as_usize();
321 if values[lv_local].may_drop && values[rv_local].may_drop {
322 let assign = Assignment::new(lv, rv, AssignType::Copy, span);
323 cur_bb.assignments.push(assign);
324 }
325 }
326 Operand::Move(rv) => {
327 let rv_local = rv.local.as_usize();
328 if values[lv_local].may_drop && values[rv_local].may_drop {
329 let assign = Assignment::new(lv, rv, AssignType::Move, span);
330 cur_bb.assignments.push(assign);
331 }
332 }
333 Operand::Constant(_) => {}
334 },
335 Rvalue::Aggregate(_, x) => {
336 for each_x in x {
337 match each_x {
338 Operand::Copy(rv) | Operand::Move(rv) => {
339 let rv_local = rv.local.as_usize();
340 if values[lv_local].may_drop && values[rv_local].may_drop {
341 let assign =
342 Assignment::new(lv, rv, AssignType::Copy, span);
343 cur_bb.assignments.push(assign);
344 }
345 }
346 Operand::Constant(_) => {}
347 }
348 }
349 }
350 Rvalue::Discriminant(rv) => {
351 let assign = Assignment::new(lv, rv, AssignType::Variant, span);
352 cur_bb.assignments.push(assign);
353 disc_map.insert(lv_local, rv.local.as_usize());
354 }
355 _ => {}
356 }
357 }
358 }
359
360 terms.push(terminator.kind.clone());
361 match terminator.kind {
363 TerminatorKind::Goto { target } => {
364 cur_bb.add_next(target.as_usize());
365 }
366 TerminatorKind::SwitchInt {
367 discr: _,
368 ref targets,
369 } => {
370 cur_bb.switch_stmts.push(terminator.clone());
371 for (_, target) in targets.iter() {
372 cur_bb.add_next(target.as_usize());
373 }
374 cur_bb.add_next(targets.otherwise().as_usize());
375 }
376 TerminatorKind::UnwindResume
377 | TerminatorKind::Return
378 | TerminatorKind::UnwindTerminate(_)
379 | TerminatorKind::Unreachable => {}
380 TerminatorKind::Drop {
381 place: _,
382 target,
383 unwind,
384 replace: _,
385 drop: _,
386 async_fut: _,
387 } => {
388 cur_bb.add_next(target.as_usize());
389 cur_bb.drops.push(terminator.clone());
390 if let UnwindAction::Cleanup(target) = unwind {
391 cur_bb.add_next(target.as_usize());
392 }
393 }
394 TerminatorKind::Call {
395 ref func,
396 args: _,
397 destination: _,
398 ref target,
399 ref unwind,
400 call_source: _,
401 fn_span: _,
402 } => {
403 if let Operand::Constant(c) = func {
404 if let ty::FnDef(id, ..) = c.ty().kind() {
405 if id.index.as_usize() == DROP
406 || id.index.as_usize() == DROP_IN_PLACE
407 || id.index.as_usize() == MANUALLYDROP
408 || id.index.as_usize() == DEALLOC
409 {
410 cur_bb.drops.push(terminator.clone());
411 }
412 }
413 }
414
415 if let Some(tt) = target {
416 cur_bb.add_next(tt.as_usize());
417 }
418 if let UnwindAction::Cleanup(tt) = unwind {
419 cur_bb.add_next(tt.as_usize());
420 }
421 cur_bb.calls.push(terminator.clone());
422 }
423 TerminatorKind::TailCall { .. } => todo!(),
424 TerminatorKind::Assert {
425 cond: _,
426 expected: _,
427 msg: _,
428 ref target,
429 ref unwind,
430 } => {
431 cur_bb.add_next(target.as_usize());
432 if let UnwindAction::Cleanup(target) = unwind {
433 cur_bb.add_next(target.as_usize());
434 }
435 }
436 TerminatorKind::Yield {
437 value: _,
438 ref resume,
439 resume_arg: _,
440 ref drop,
441 } => {
442 cur_bb.add_next(resume.as_usize());
443 if let Some(target) = drop {
444 cur_bb.add_next(target.as_usize());
445 }
446 }
447 TerminatorKind::FalseEdge {
448 ref real_target,
449 imaginary_target: _,
450 } => {
451 cur_bb.add_next(real_target.as_usize());
452 }
453 TerminatorKind::FalseUnwind {
454 ref real_target,
455 unwind: _,
456 } => {
457 cur_bb.add_next(real_target.as_usize());
458 }
459 TerminatorKind::CoroutineDrop {} => {
460 }
462 TerminatorKind::InlineAsm {
463 template: _,
464 operands: _,
465 options: _,
466 line_spans: _,
467 ref unwind,
468 targets,
469 asm_macro: _,
470 } => {
471 for target in targets {
472 cur_bb.add_next(target.as_usize());
473 }
474 if let UnwindAction::Cleanup(target) = unwind {
475 cur_bb.add_next(target.as_usize());
476 }
477 }
478 }
479 blocks.push(cur_bb);
480 }
481
482 SafeDropGraph {
483 def_id,
484 tcx,
485 span: body.span,
486 blocks,
487 values,
488 arg_size,
489 scc_indices,
490 constant: FxHashMap::default(),
491 return_set: FxHashSet::default(),
492 bug_records: BugRecords::new(),
493 visit_times: 0,
494 alias_set: alias,
495 dead_record: dead,
496 adt_owner,
497 child_scc: FxHashMap::default(),
498 disc_map,
499 terms,
500 }
501 }
502
503 pub fn tarjan(
504 &mut self,
505 index: usize,
506 stack: &mut Vec<usize>,
507 instack: &mut FxHashSet<usize>,
508 dfn: &mut Vec<usize>,
509 low: &mut Vec<usize>,
510 time: &mut usize,
511 ) {
512 dfn[index] = *time;
513 low[index] = *time;
514 *time += 1;
515 instack.insert(index);
516 stack.push(index);
517 let out_set = self.blocks[index].next.clone();
518 for target in out_set {
519 if dfn[target] == 0 {
520 self.tarjan(target, stack, instack, dfn, low, time);
521 low[index] = min(low[index], low[target]);
522 } else if instack.contains(&target) {
523 low[index] = min(low[index], dfn[target]);
524 }
525 }
526
527 if dfn[index] == low[index] {
529 let mut modified_set = FxHashSet::<usize>::default();
530 let mut switch_target = Vec::new();
531 let mut scc_block_set = FxHashSet::<usize>::default();
532 let init_block = self.blocks[index].clone();
533 loop {
534 let node = stack.pop().unwrap();
535 self.scc_indices[node] = index;
536 instack.remove(&node);
537 if index == node {
538 break;
540 }
541 self.blocks[index].scc_sub_blocks.push(node);
542 scc_block_set.insert(node);
543
544 for value in &self.blocks[index].modified_value {
545 modified_set.insert(*value);
546 }
547 if let Some(target) = self.switch_target(self.tcx, node) {
548 if !self.blocks[index].switch_stmts.is_empty() {
549 switch_target.push((target, self.blocks[index].switch_stmts[0].clone()));
550 }
551 }
552 let nexts = self.blocks[node].next.clone();
553 for i in nexts {
554 self.blocks[index].next.insert(i);
555 }
556 }
557 switch_target.retain(|v| !modified_set.contains(&(v.0)));
558
559 if !switch_target.is_empty() && switch_target.len() == 1 {
560 let target_terminator = switch_target[0].1.clone();
562
563 let TerminatorKind::SwitchInt { discr: _, targets } = target_terminator.kind else {
564 unreachable!();
565 };
566
567 self.child_scc
568 .insert(index, (init_block, targets, scc_block_set));
569 }
570
571 let mut to_remove = Vec::new();
573 for i in self.blocks[index].next.iter() {
574 if self.scc_indices[*i] == index {
575 to_remove.push(*i);
576 }
577 }
578 for i in to_remove {
579 self.blocks[index].next.remove(&i);
580 }
581 self.blocks[index].scc_sub_blocks.reverse();
586 }
587 }
588
589 pub fn solve_scc(&mut self) {
591 let mut stack = Vec::<usize>::new();
592 let mut instack = FxHashSet::<usize>::default();
593 let mut dfn = vec![0; self.blocks.len()];
594 let mut low = vec![0; self.blocks.len()];
595 let mut time = 0;
596 self.tarjan(0, &mut stack, &mut instack, &mut dfn, &mut low, &mut time);
597 }
598
599 pub fn dfs_on_spanning_tree(
600 &self,
601 index: usize,
602 stack: &mut Vec<usize>,
603 paths: &mut Vec<Vec<usize>>,
604 ) {
605 let curr_scc_index = self.scc_indices[index];
606 if self.blocks[curr_scc_index].next.is_empty() {
607 paths.push(stack.to_vec());
608 } else {
609 for child in self.blocks[curr_scc_index].next.iter() {
610 stack.push(*child);
611 self.dfs_on_spanning_tree(*child, stack, paths);
612 }
613 }
614 stack.pop();
615 }
616
617 pub fn get_paths(&self) -> Vec<Vec<usize>> {
618 let mut paths: Vec<Vec<usize>> = Vec::new();
619 let mut stack: Vec<usize> = vec![0];
620 self.dfs_on_spanning_tree(0, &mut stack, &mut paths);
621 paths
622 }
623
624 pub fn switch_target(&mut self, tcx: TyCtxt<'tcx>, block_index: usize) -> Option<usize> {
625 let block = &self.blocks[block_index];
626 if block.switch_stmts.is_empty() {
627 return None;
628 }
629
630 if let TerminatorKind::SwitchInt { discr, .. } = &block.switch_stmts[0].kind {
631 match discr {
632 Operand::Copy(p) | Operand::Move(p) => {
633 let place = self.projection(tcx, false, *p);
634 Some(place)
635 }
636 _ => None,
637 }
638 } else {
639 None
640 }
641 }
642}