charon_lib/transform/finish_translation/
insert_storage_statements.rs1use derive_generic_visitor::Visitor;
7
8use crate::ast::*;
9use crate::ids::IndexVec;
10use crate::transform::TransformCtx;
11use crate::transform::ctx::{TransformPass, UllbcPass};
12use crate::ullbc_ast::{BlockId, TerminatorKind};
13
14#[derive(Visitor)]
15struct StorageVisitor {
16 local_status: IndexVec<LocalId, LocalStatus>,
17}
18
19struct LocalStatus {
20 used: bool,
21 has_storage_live: bool,
22 has_storage_dead: bool,
23}
24
25impl StorageVisitor {
26 fn new(locals: &Locals) -> Self {
27 let local_status = locals.locals.map_ref(|local| {
28 let is_return = local.index == LocalId::ZERO;
29 let is_argument = !is_return && locals.is_return_or_arg(local.index);
30 LocalStatus {
31 used: is_return || is_argument,
32 has_storage_live: is_argument,
33 has_storage_dead: is_return,
34 }
35 });
36 Self { local_status }
37 }
38
39 fn locals_missing_storage_lives(&self) -> Vec<LocalId> {
40 self.local_status
41 .iter_enumerated()
42 .filter(|(_, status)| status.used && !status.has_storage_live)
43 .map(|(local, _)| local)
44 .collect()
45 }
46
47 fn locals_missing_storage_deads(&self) -> Vec<LocalId> {
48 self.local_status
49 .iter_enumerated()
50 .filter(|(_, status)| status.used && !status.has_storage_dead)
51 .map(|(local, _)| local)
52 .collect()
53 }
54}
55
56impl VisitBody for StorageVisitor {
57 fn visit_locals(&mut self, _: &Locals) -> ::std::ops::ControlFlow<Self::Break> {
58 ControlFlow::Continue(())
60 }
61 fn enter_local_id(&mut self, lid: &LocalId) {
62 self.local_status[*lid].used = true;
63 }
64 fn enter_llbc_statement(&mut self, st: &llbc_ast::Statement) {
65 match &st.kind {
66 llbc_ast::StatementKind::StorageLive(lid) => {
67 self.local_status[*lid].has_storage_live = true;
68 }
69 llbc_ast::StatementKind::StorageDead(lid) => {
70 self.local_status[*lid].has_storage_dead = true;
71 }
72 _ => {}
73 }
74 }
75 fn enter_ullbc_statement(&mut self, st: &ullbc_ast::Statement) {
76 match &st.kind {
77 ullbc_ast::StatementKind::StorageLive(lid) => {
78 self.local_status[*lid].has_storage_live = true;
79 }
80 ullbc_ast::StatementKind::StorageDead(lid) => {
81 self.local_status[*lid].has_storage_dead = true;
82 }
83 _ => {}
84 }
85 }
86}
87
88pub struct Transform;
89impl Transform {
90 fn transform_ullbc_body(
91 &self,
92 body: &mut ullbc_ast::ExprBody,
93 insert_missing_storage_deads: bool,
94 ) {
95 let mut storage_visitor = StorageVisitor::new(&body.locals);
96 let _ = storage_visitor.visit(body);
97
98 let locals_with_missing_storage_lives = storage_visitor.locals_missing_storage_lives();
100 let first_block = body.body.get_mut(BlockId::ZERO).unwrap();
101 let first_span = if let Some(fst) = first_block.statements.first() {
102 fst.span
103 } else {
104 first_block.terminator.span
105 };
106 let new_statements = locals_with_missing_storage_lives.iter().map(|local| {
107 ullbc_ast::Statement::new(first_span, ullbc_ast::StatementKind::StorageLive(*local))
108 });
109 first_block.statements.splice(0..0, new_statements);
110
111 if insert_missing_storage_deads {
113 let locals_with_missing_storage_deads = storage_visitor.locals_missing_storage_deads();
114 for block in &mut body.body {
115 if matches!(
116 block.terminator.kind,
117 TerminatorKind::Abort(AbortKind::Panic(..) | AbortKind::UnwindTerminate)
118 | TerminatorKind::Return
119 | TerminatorKind::UnwindResume
120 ) {
121 let span = block.terminator.span;
122 block
123 .statements
124 .extend(locals_with_missing_storage_deads.iter().rev().map(|local| {
125 ullbc_ast::Statement::new(
126 span,
127 ullbc_ast::StatementKind::StorageDead(*local),
128 )
129 }));
130 }
131 }
132 }
133 }
134
135 fn transform_llbc_body(
136 &self,
137 body: &mut llbc_ast::ExprBody,
138 insert_missing_storage_deads: bool,
139 ) {
140 let mut storage_visitor = StorageVisitor::new(&body.locals);
141 let _ = storage_visitor.visit(body);
142
143 let locals_with_missing_storage_lives = storage_visitor.locals_missing_storage_lives();
144 let first_span = if let Some(fst) = body.body.statements.first() {
145 fst.span
146 } else {
147 body.span
148 };
149 let new_statements = locals_with_missing_storage_lives.iter().map(|local| {
150 llbc_ast::Statement::new(first_span, llbc_ast::StatementKind::StorageLive(*local))
151 });
152 body.body.statements.splice(0..0, new_statements);
153
154 if insert_missing_storage_deads {
155 let locals_with_missing_storage_deads = storage_visitor.locals_missing_storage_deads();
156 body.body.transform_sequences(|statements| {
157 if !matches!(
158 &statements[0].kind,
159 llbc_ast::StatementKind::Abort(
160 AbortKind::Panic(..) | AbortKind::UnwindTerminate
161 ) | llbc_ast::StatementKind::Return
162 ) {
163 return Vec::new();
164 }
165 let span = statements[0].span;
166 locals_with_missing_storage_deads
167 .iter()
168 .rev()
169 .map(|local| {
170 llbc_ast::Statement::new(span, llbc_ast::StatementKind::StorageDead(*local))
171 })
172 .collect()
173 });
174 }
175 }
176
177 fn transform_function(&self, fun: &mut FunDecl) {
178 let insert_missing_storage_deads = fun.is_global_initializer.is_none();
181 match &mut fun.body {
182 Body::Unstructured(body) => {
183 self.transform_ullbc_body(body, insert_missing_storage_deads)
184 }
185 Body::Structured(body) => self.transform_llbc_body(body, insert_missing_storage_deads),
186 _ => {}
187 }
188 }
189}
190
191impl UllbcPass for Transform {
192 fn transform_function(&self, _ctx: &mut TransformCtx, fun: &mut FunDecl) {
193 self.transform_function(fun)
194 }
195}
196
197impl TransformPass for Transform {
198 fn transform_ctx(&self, ctx: &mut TransformCtx) {
199 ctx.for_each_fun_decl(|_ctx, fun| self.transform_function(fun));
200 }
201}