1use crate::ast::*;
2use crate::errors::{ErrorCtx, Level};
3use crate::formatter::{FmtCtx, IntoFormatter};
4use crate::llbc_ast;
5use crate::options::TranslateOptions;
6use crate::pretty::FmtWithCtx;
7use crate::ullbc_ast;
8use std::cell::RefCell;
9use std::{fmt, mem};
10
11pub struct TransformCtx {
14 pub options: TranslateOptions,
16 pub translated: TranslatedCrate,
18 pub errors: RefCell<ErrorCtx>,
20}
21
22pub trait UllbcPass: Sync {
24 fn transform_body(&self, _ctx: &mut TransformCtx, _body: &mut ullbc_ast::ExprBody) {}
26
27 fn transform_function(&self, ctx: &mut TransformCtx, decl: &mut FunDecl) {
29 if let Some(body) = decl.body.as_unstructured_mut() {
30 self.transform_body(ctx, body)
31 }
32 }
33
34 fn transform_ctx(&self, ctx: &mut TransformCtx) {
36 ctx.for_each_fun_decl(|ctx, decl| {
37 self.log_before_body(ctx, &decl.item_meta.name, &decl.body);
38 self.transform_function(ctx, decl);
39 });
40 }
41
42 fn name(&self) -> &str {
45 std::any::type_name::<Self>()
46 }
47
48 fn log_before_body(&self, ctx: &TransformCtx, name: &Name, body: &Body) {
50 let fmt_ctx = &ctx.into_fmt();
51 let body_str = body.to_string_with_ctx(fmt_ctx);
52 trace!(
53 "# About to run pass [{}] on `{}`:\n{}",
54 self.name(),
55 name.with_ctx(fmt_ctx),
56 body_str,
57 );
58 }
59}
60
61pub trait LlbcPass: Sync {
63 fn transform_body(&self, _ctx: &mut TransformCtx, _body: &mut llbc_ast::ExprBody) {}
65
66 fn transform_function(&self, ctx: &mut TransformCtx, decl: &mut FunDecl) {
68 if let Some(body) = decl.body.as_structured_mut() {
69 self.transform_body(ctx, body)
70 }
71 }
72
73 fn transform_ctx(&self, ctx: &mut TransformCtx) {
75 ctx.for_each_fun_decl(|ctx, decl| {
76 self.log_before_body(ctx, &decl.item_meta.name, &decl.body);
77 self.transform_function(ctx, decl);
78 });
79 }
80
81 fn name(&self) -> &str {
84 std::any::type_name::<Self>()
85 }
86
87 fn log_before_body(&self, ctx: &TransformCtx, name: &Name, body: &Body) {
89 let fmt_ctx = &ctx.into_fmt();
90 let body_str = body.to_string_with_ctx(fmt_ctx);
91 trace!(
92 "# About to run pass [{}] on `{}`:\n{}",
93 self.name(),
94 name.with_ctx(fmt_ctx),
95 body_str,
96 );
97 }
98}
99
100pub trait TransformPass: Sync {
102 fn transform_ctx(&self, ctx: &mut TransformCtx);
103
104 fn name(&self) -> &str {
107 std::any::type_name::<Self>()
108 }
109}
110
111impl<'ctx> TransformCtx {
112 pub(crate) fn has_errors(&self) -> bool {
113 self.errors.borrow().has_errors()
114 }
115
116 pub(crate) fn span_err(&self, span: Span, msg: &str, level: Level) -> Error {
118 self.errors
119 .borrow_mut()
120 .span_err(&self.translated, span, msg, level)
121 }
122
123 pub(crate) fn opacity_for_name(&self, name: &Name) -> ItemOpacity {
124 self.options.opacity_for_name(&self.translated, name)
125 }
126
127 pub(crate) fn with_def_id<F, T>(
128 &mut self,
129 def_id: impl Into<ItemId>,
130 def_id_is_local: bool,
131 f: F,
132 ) -> T
133 where
134 F: FnOnce(&mut Self) -> T,
135 {
136 let mut errors = self.errors.borrow_mut();
137 let current_def_id = mem::replace(&mut errors.def_id, Some(def_id.into()));
138 let current_def_id_is_local = mem::replace(&mut errors.def_id_is_local, def_id_is_local);
139 drop(errors); let ret = f(self);
141 let mut errors = self.errors.borrow_mut();
142 errors.def_id = current_def_id;
143 errors.def_id_is_local = current_def_id_is_local;
144 ret
145 }
146
147 pub(crate) fn for_each_body(&mut self, mut f: impl FnMut(&mut Self, &mut Body)) {
151 let fn_ids = self.translated.fun_decls.all_indices();
152 for id in fn_ids {
153 if let Some(decl) = self.translated.fun_decls.get_mut(id) {
154 if decl.body.has_contents() {
155 let mut body = mem::replace(&mut decl.body, Body::Opaque);
156 let fun_decl_id = decl.def_id;
157 let is_local = decl.item_meta.is_local;
158 self.with_def_id(fun_decl_id, is_local, |ctx| f(ctx, &mut body));
159 self.translated.fun_decls[id].body = body;
160 }
161 }
162 }
163 }
164
165 pub(crate) fn for_each_fun_decl(&mut self, mut f: impl FnMut(&mut Self, &mut FunDecl)) {
168 let fn_ids = self.translated.fun_decls.all_indices();
169 for id in fn_ids {
170 if let Some(mut decl) = self.translated.fun_decls.remove(id) {
171 let fun_decl_id = decl.def_id;
172 let is_local = decl.item_meta.is_local;
173 self.with_def_id(fun_decl_id, is_local, |ctx| f(ctx, &mut decl));
174 self.translated.fun_decls.set_slot(id, decl);
175 }
176 }
177 }
178
179 pub fn for_each_item_mut(&mut self, mut f: impl for<'a> FnMut(&'a mut Self, ItemRefMut<'a>)) {
182 for id in self.translated.all_ids() {
183 if let Some(mut decl) = self.translated.remove_item(id) {
184 f(self, decl.as_mut());
185 self.translated.set_item_slot(id, decl);
186 }
187 }
188 }
189}
190
191impl<'a> IntoFormatter for &'a TransformCtx {
192 type C = FmtCtx<'a>;
193
194 fn into_fmt(self) -> Self::C {
195 self.translated.into_fmt()
196 }
197}
198
199impl fmt::Display for TransformCtx {
200 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
201 self.translated.fmt(f)
202 }
203}
204
205pub trait BodyTransformCtx: Sized {
207 fn get_ctx(&self) -> &TransformCtx;
208 fn get_params(&self) -> &GenericParams;
209 fn get_locals_mut(&mut self) -> &mut Locals;
210
211 fn insert_storage_live_stmt(&mut self, local: LocalId);
212 fn insert_storage_dead_stmt(&mut self, local: LocalId);
213 fn insert_assn_stmt(&mut self, place: Place, rvalue: Rvalue);
214
215 fn fresh_var(&mut self, name: Option<String>, ty: Ty) -> Place {
217 let var = self.get_locals_mut().new_var(name, ty);
218 self.insert_storage_live_stmt(var.local_id().unwrap());
219 var
220 }
221
222 fn compute_subslice_end_idx(
228 &mut self,
229 len_place: &Place,
230 last_arg: Operand,
231 from_end: bool,
232 ) -> Operand {
233 if from_end {
234 let len_var = self.fresh_var(None, Ty::mk_usize());
237 self.insert_assn_stmt(
238 len_var.clone(),
239 Rvalue::Len(
240 len_place.clone(),
241 len_place.ty().clone(),
242 len_place
243 .ty()
244 .as_adt()
245 .unwrap()
246 .generics
247 .const_generics
248 .get(0.into())
249 .cloned(),
250 ),
251 );
252
253 let index_var = self.fresh_var(None, Ty::mk_usize());
257 self.insert_assn_stmt(
258 index_var.clone(),
259 Rvalue::BinaryOp(
260 BinOp::Sub(OverflowMode::UB),
261 Operand::Copy(len_var.clone()),
262 last_arg,
263 ),
264 );
265 self.insert_storage_dead_stmt(len_var.local_id().unwrap());
266 Operand::Copy(index_var)
267 } else {
268 last_arg
269 }
270 }
271
272 fn is_sized_type_var(&mut self, ty: &Ty) -> bool {
273 match ty.kind() {
274 TyKind::TypeVar(..) => {
275 if self.get_ctx().options.hide_marker_traits {
276 return true;
278 }
279 let params = self.get_params();
280 for clause in ¶ms.trait_clauses {
281 let tref = clause.trait_.clone().erase();
282 if tref.generics.types[0] == *ty
284 && self
285 .get_ctx()
286 .translated
287 .trait_decls
288 .get(tref.id)
289 .and_then(|decl| decl.item_meta.lang_item.clone())
290 == Some("sized".into())
291 {
292 return true;
293 }
294 }
295 false
296 }
297 _ => false,
298 }
299 }
300
301 fn compute_place_metadata(&mut self, place: &Place) -> Operand {
304 fn no_metadata<T: BodyTransformCtx>(ctx: &T) -> Operand {
307 let unit_meta = ctx.get_ctx().translated.unit_metadata.clone().unwrap();
308 Operand::Copy(Place::new_global(unit_meta, Ty::mk_unit()))
309 }
310
311 fn compute_place_metadata_inner<T: BodyTransformCtx>(
313 ctx: &mut T,
314 place: &Place,
315 metadata_ty: &Ty,
316 ) -> Option<Operand> {
317 let (subplace, proj) = place.as_projection()?;
318 match proj {
319 ProjectionElem::Deref => {
321 let metadata_place = subplace
322 .clone()
323 .project(ProjectionElem::PtrMetadata, metadata_ty.clone());
324 Some(Operand::Copy(metadata_place))
325 }
326 ProjectionElem::Field { .. } => {
327 compute_place_metadata_inner(ctx, subplace, metadata_ty)
328 }
329 ProjectionElem::Index { .. } => None,
331 ProjectionElem::PtrMetadata { .. } => None,
333 ProjectionElem::Subslice { from, to, from_end } => {
335 let to_idx = ctx.compute_subslice_end_idx(subplace, *to.clone(), *from_end);
336 let diff_place = ctx.fresh_var(None, Ty::mk_usize());
337 ctx.insert_assn_stmt(
338 diff_place.clone(),
339 Rvalue::BinaryOp(BinOp::Sub(OverflowMode::UB), to_idx, *from.clone()),
341 );
342 Some(Operand::Copy(diff_place))
343 }
344 }
345 }
346 trace!(
347 "getting ptr metadata for place: {}",
348 place.with_ctx(&self.get_ctx().into_fmt())
349 );
350 let metadata_ty = place
351 .ty()
352 .get_ptr_metadata(&self.get_ctx().translated)
353 .into_type();
354 if metadata_ty.is_unit()
355 || matches!(metadata_ty.kind(), TyKind::PtrMetadata(ty) if self.is_sized_type_var(ty))
356 {
357 return no_metadata(self);
359 }
360 trace!(
361 "computed metadata type: {}",
362 metadata_ty.with_ctx(&self.get_ctx().into_fmt())
363 );
364 compute_place_metadata_inner(self, place, &metadata_ty).unwrap_or_else(|| no_metadata(self))
365 }
366}
367
368pub struct UllbcStatementTransformCtx<'a> {
369 pub ctx: &'a mut TransformCtx,
370 pub params: &'a GenericParams,
371 pub locals: &'a mut Locals,
372 pub span: Span,
374 pub statements: Vec<ullbc_ast::Statement>,
376}
377
378impl BodyTransformCtx for UllbcStatementTransformCtx<'_> {
379 fn get_ctx(&self) -> &TransformCtx {
380 self.ctx
381 }
382 fn get_params(&self) -> &GenericParams {
383 self.params
384 }
385 fn get_locals_mut(&mut self) -> &mut Locals {
386 self.locals
387 }
388
389 fn insert_storage_live_stmt(&mut self, local: LocalId) {
390 self.statements.push(ullbc_ast::Statement::new(
391 self.span,
392 ullbc_ast::StatementKind::StorageLive(local),
393 ));
394 }
395
396 fn insert_assn_stmt(&mut self, place: Place, rvalue: Rvalue) {
397 self.statements.push(ullbc_ast::Statement::new(
398 self.span,
399 ullbc_ast::StatementKind::Assign(place, rvalue),
400 ));
401 }
402
403 fn insert_storage_dead_stmt(&mut self, local: LocalId) {
404 self.statements.push(ullbc_ast::Statement::new(
405 self.span,
406 ullbc_ast::StatementKind::StorageDead(local),
407 ));
408 }
409}
410
411impl FunDecl {
412 pub fn transform_ullbc_terminators(
413 &mut self,
414 ctx: &mut TransformCtx,
415 mut f: impl FnMut(&mut UllbcStatementTransformCtx, &mut ullbc_ast::Terminator),
416 ) {
417 if let Some(body) = self.body.as_unstructured_mut() {
418 let params = &self.signature.generics;
419 body.body.iter_mut().for_each(|block| {
420 let span = block.terminator.span;
421 let mut ctx = UllbcStatementTransformCtx {
422 ctx,
423 params,
424 locals: &mut body.locals,
425 span,
426 statements: std::mem::take(&mut block.statements),
427 };
428 f(&mut ctx, &mut block.terminator);
429 block.statements = ctx.statements;
430 });
431 }
432 }
433
434 pub fn transform_ullbc_statements(
435 &mut self,
436 ctx: &mut TransformCtx,
437 mut f: impl FnMut(&mut UllbcStatementTransformCtx, &mut ullbc_ast::Statement),
438 ) {
439 if let Some(body) = self.body.as_unstructured_mut() {
440 let params = &self.signature.generics;
441 body.body.iter_mut().for_each(|block| {
442 block.transform(|st: &mut ullbc_ast::Statement| {
443 let mut ctx = UllbcStatementTransformCtx {
444 ctx,
445 params,
446 locals: &mut body.locals,
447 span: st.span,
448 statements: Vec::new(),
449 };
450 f(&mut ctx, st);
451 ctx.statements
452 });
453 });
454 }
455 }
456}