charon_lib/transform/resugar/
reconstruct_box_derefs.rs1use std::ops::ControlFlow;
6
7use crate::transform::TransformCtx;
8use crate::transform::ctx::UllbcPass;
9use crate::ullbc_ast::*;
10
11pub struct Transform;
12
13fn box_pointee_pointer_assignment(rvalue: &Rvalue) -> Option<Place> {
20 let Rvalue::UnaryOp(UnOp::Cast(CastKind::Transmute(raw_ptr_ty)), Operand::Copy(hidden_pointer)) =
21 &rvalue
22 else {
23 return None;
24 };
25 let unique_place = match hidden_pointer.as_projection()? {
26 (field_base, ProjectionElem::Field(_, FieldId::ZERO)) => field_base,
27 _ => hidden_pointer,
28 };
29 let (box_place, ProjectionElem::Deref) = unique_place.as_projection()? else {
30 return None;
31 };
32 let TyKind::Adt(tref) = box_place.ty().kind() else {
33 return None;
34 };
35 if !tref.is_box() {
36 return None;
37 }
38 let box_generics = &tref.generics;
39 if &box_generics.types[0] != raw_ptr_ty.as_raw_ptr()?.0 {
40 return None;
41 }
42 Some(box_place.clone().deref())
43}
44
45#[derive(Default)]
46struct LocalStatus {
47 box_pointee_assignment: Option<(StmtLoc, Place)>,
50 ambiguous: bool,
52 used_outside_derefs: bool,
55}
56
57impl LocalStatus {
58 fn rewritable_box_pointee(&self) -> Option<&(StmtLoc, Place)> {
59 if self.ambiguous || self.used_outside_derefs {
60 return None;
61 }
62 self.box_pointee_assignment.as_ref()
63 }
64}
65
66struct LocalStatusCollector {
67 local_status: IndexVec<LocalId, LocalStatus>,
68 current_statement: Option<StmtLoc>,
69}
70
71impl Visitor for LocalStatusCollector {
72 type Break = std::convert::Infallible;
73}
74
75impl VisitBody for LocalStatusCollector {
76 fn visit_place(&mut self, place: &Place) -> ControlFlow<Self::Break> {
77 match &place.kind {
78 PlaceKind::Projection(subplace, pj) if subplace.is_local() && pj.is_deref() => {
80 return ControlFlow::Continue(());
81 }
82 PlaceKind::Local(local) => self.local_status[*local].used_outside_derefs = true,
84 _ => {}
85 }
86 self.visit_inner(place)
87 }
88
89 fn visit_ullbc_statement(&mut self, st: &ullbc_ast::Statement) -> ControlFlow<Self::Break> {
90 if let StatementKind::Assign(dst, rvalue) = &st.kind
91 && let Some(local) = dst.as_local()
92 && let Some(box_pointee) = box_pointee_pointer_assignment(rvalue)
93 {
94 let status = &mut self.local_status[local];
95 if status.box_pointee_assignment.is_some() {
96 status.ambiguous = true;
97 } else {
98 let loc = self.current_statement.unwrap();
99 status.box_pointee_assignment = Some((loc, box_pointee));
100 }
101 self.visit(rvalue)
104 } else {
105 self.visit_inner(st)
106 }
107 }
108}
109
110fn collect_local_status(body: &ExprBody) -> IndexVec<LocalId, LocalStatus> {
111 let mut collector = LocalStatusCollector {
112 local_status: body.locals.locals.map_ref(|_| Default::default()),
113 current_statement: None,
114 };
115 for (block, block_data) in body.body.iter_enumerated() {
116 for (statement, st) in block_data.statements.iter().enumerate() {
117 collector.current_statement = Some(StmtLoc::new(block, statement));
118 let _ = collector.visit(st);
119 }
120 collector.current_statement = None;
121 let _ = collector.visit(&block_data.terminator);
122 }
123 collector.local_status
124}
125
126fn rewrite_place(place: &mut Place, local_status: &IndexVec<LocalId, LocalStatus>) {
127 let Some((subplace, projection)) = place.as_projection() else {
128 return;
129 };
130 if projection == &ProjectionElem::Deref
131 && let Some(local) = subplace.as_local()
132 && let Some((_, box_pointee)) = local_status[local].rewritable_box_pointee()
133 {
134 *place = box_pointee.clone();
135 return;
136 }
137
138 let PlaceKind::Projection(subplace, _) = &mut place.kind else {
139 unreachable!();
140 };
141 rewrite_place(subplace, local_status);
142}
143
144impl UllbcPass for Transform {
145 fn should_run(&self, options: &crate::options::TranslateOptions) -> bool {
146 options.treat_box_as_builtin
147 }
148
149 fn transform_body(&self, _ctx: &mut TransformCtx, body: &mut ExprBody) {
150 let local_status = collect_local_status(body);
151
152 body.body.dyn_visit_in_body_mut(|place: &mut Place| {
153 rewrite_place(place, &local_status);
154 });
155
156 for status in local_status {
157 if let Some((assign_loc, _)) = status.rewritable_box_pointee() {
158 body[*assign_loc].kind = StatementKind::Nop;
159 }
160 }
161 }
162}