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 Ok(body) = &mut decl.body {
30 self.transform_body(ctx, body.as_unstructured_mut().unwrap())
31 }
32 }
33
34 fn transform_ctx(&self, ctx: &mut TransformCtx) {
36 ctx.for_each_fun_decl(|ctx, decl| {
37 let body = decl
38 .body
39 .as_mut()
40 .map(|body| body.as_unstructured_mut().unwrap())
41 .map_err(|opaque| *opaque);
42 self.log_before_body(ctx, &decl.item_meta.name, body.as_deref());
43 self.transform_function(ctx, decl);
44 });
45 }
46
47 fn name(&self) -> &str {
50 std::any::type_name::<Self>()
51 }
52
53 fn log_before_body(
55 &self,
56 ctx: &TransformCtx,
57 name: &Name,
58 body: Result<&ullbc_ast::ExprBody, &Opaque>,
59 ) {
60 let fmt_ctx = &ctx.into_fmt();
61 let body_str = if let Ok(body) = body {
62 body.to_string_with_ctx(fmt_ctx)
63 } else {
64 "<opaque>".to_owned()
65 };
66 trace!(
67 "# About to run pass [{}] on `{}`:\n{}",
68 self.name(),
69 name.with_ctx(fmt_ctx),
70 body_str,
71 );
72 }
73}
74
75pub trait LlbcPass: Sync {
77 fn transform_body(&self, _ctx: &mut TransformCtx, _body: &mut llbc_ast::ExprBody) {}
79
80 fn transform_function(&self, ctx: &mut TransformCtx, decl: &mut FunDecl) {
82 if let Ok(body) = &mut decl.body {
83 self.transform_body(ctx, body.as_structured_mut().unwrap())
84 }
85 }
86
87 fn transform_ctx(&self, ctx: &mut TransformCtx) {
89 ctx.for_each_fun_decl(|ctx, decl| {
90 let body = decl
91 .body
92 .as_mut()
93 .map(|body| body.as_structured_mut().unwrap())
94 .map_err(|opaque| *opaque);
95 self.log_before_body(ctx, &decl.item_meta.name, body.as_deref());
96 self.transform_function(ctx, decl);
97 });
98 }
99
100 fn name(&self) -> &str {
103 std::any::type_name::<Self>()
104 }
105
106 fn log_before_body(
108 &self,
109 ctx: &TransformCtx,
110 name: &Name,
111 body: Result<&llbc_ast::ExprBody, &Opaque>,
112 ) {
113 let fmt_ctx = &ctx.into_fmt();
114 let body_str = if let Ok(body) = body {
115 body.to_string_with_ctx(fmt_ctx)
116 } else {
117 "<opaque>".to_owned()
118 };
119 trace!(
120 "# About to run pass [{}] on `{}`:\n{}",
121 self.name(),
122 name.with_ctx(fmt_ctx),
123 body_str,
124 );
125 }
126}
127
128pub trait TransformPass: Sync {
130 fn transform_ctx(&self, ctx: &mut TransformCtx);
131
132 fn name(&self) -> &str {
135 std::any::type_name::<Self>()
136 }
137}
138
139impl<'ctx> TransformCtx {
140 pub(crate) fn has_errors(&self) -> bool {
141 self.errors.borrow().has_errors()
142 }
143
144 pub(crate) fn span_err(&self, span: Span, msg: &str, level: Level) -> Error {
146 self.errors
147 .borrow_mut()
148 .span_err(&self.translated, span, msg, level)
149 }
150
151 pub(crate) fn opacity_for_name(&self, name: &Name) -> ItemOpacity {
152 self.options.opacity_for_name(&self.translated, name)
153 }
154
155 pub(crate) fn with_def_id<F, T>(
156 &mut self,
157 def_id: impl Into<ItemId>,
158 def_id_is_local: bool,
159 f: F,
160 ) -> T
161 where
162 F: FnOnce(&mut Self) -> T,
163 {
164 let mut errors = self.errors.borrow_mut();
165 let current_def_id = mem::replace(&mut errors.def_id, Some(def_id.into()));
166 let current_def_id_is_local = mem::replace(&mut errors.def_id_is_local, def_id_is_local);
167 drop(errors); let ret = f(self);
169 let mut errors = self.errors.borrow_mut();
170 errors.def_id = current_def_id;
171 errors.def_id_is_local = current_def_id_is_local;
172 ret
173 }
174
175 pub(crate) fn for_each_body(&mut self, mut f: impl FnMut(&mut Self, &mut Body)) {
179 let fn_ids = self.translated.fun_decls.all_indices();
180 for id in fn_ids {
181 if let Some(decl) = self.translated.fun_decls.get_mut(id) {
182 if let Ok(mut body) = mem::replace(&mut decl.body, Err(Opaque)) {
183 let fun_decl_id = decl.def_id;
184 let is_local = decl.item_meta.is_local;
185 self.with_def_id(fun_decl_id, is_local, |ctx| f(ctx, &mut body));
186 self.translated.fun_decls[id].body = Ok(body);
187 }
188 }
189 }
190 }
191
192 pub(crate) fn for_each_fun_decl(&mut self, mut f: impl FnMut(&mut Self, &mut FunDecl)) {
195 let fn_ids = self.translated.fun_decls.all_indices();
196 for id in fn_ids {
197 if let Some(mut decl) = self.translated.fun_decls.remove(id) {
198 let fun_decl_id = decl.def_id;
199 let is_local = decl.item_meta.is_local;
200 self.with_def_id(fun_decl_id, is_local, |ctx| f(ctx, &mut decl));
201 self.translated.fun_decls.set_slot(id, decl);
202 }
203 }
204 }
205
206 pub fn for_each_item_mut(&mut self, mut f: impl for<'a> FnMut(&'a mut Self, ItemRefMut<'a>)) {
209 for id in self.translated.all_ids() {
210 if let Some(mut decl) = self.translated.remove_item(id) {
211 f(self, decl.as_mut());
212 self.translated.set_item_slot(id, decl);
213 }
214 }
215 }
216}
217
218impl<'a> IntoFormatter for &'a TransformCtx {
219 type C = FmtCtx<'a>;
220
221 fn into_fmt(self) -> Self::C {
222 self.translated.into_fmt()
223 }
224}
225
226impl fmt::Display for TransformCtx {
227 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
228 self.translated.fmt(f)
229 }
230}
231
232pub trait BodyTransformCtx: Sized {
234 fn get_ctx(&self) -> &TransformCtx;
235 fn get_params(&self) -> &GenericParams;
236 fn get_locals_mut(&mut self) -> &mut Locals;
237
238 fn insert_storage_live_stmt(&mut self, local: LocalId);
239 fn insert_storage_dead_stmt(&mut self, local: LocalId);
240 fn insert_assn_stmt(&mut self, place: Place, rvalue: Rvalue);
241
242 fn fresh_var(&mut self, name: Option<String>, ty: Ty) -> Place {
244 let var = self.get_locals_mut().new_var(name, ty);
245 self.insert_storage_live_stmt(var.local_id().unwrap());
246 var
247 }
248
249 fn compute_subslice_end_idx(
255 &mut self,
256 len_place: &Place,
257 last_arg: Operand,
258 from_end: bool,
259 ) -> Operand {
260 if from_end {
261 let len_var = self.fresh_var(None, Ty::mk_usize());
264 self.insert_assn_stmt(
265 len_var.clone(),
266 Rvalue::Len(
267 len_place.clone(),
268 len_place.ty().clone(),
269 len_place
270 .ty()
271 .as_adt()
272 .unwrap()
273 .generics
274 .const_generics
275 .get(0.into())
276 .cloned(),
277 ),
278 );
279
280 let index_var = self.fresh_var(None, Ty::mk_usize());
284 self.insert_assn_stmt(
285 index_var.clone(),
286 Rvalue::BinaryOp(
287 BinOp::Sub(OverflowMode::UB),
288 Operand::Copy(len_var.clone()),
289 last_arg,
290 ),
291 );
292 self.insert_storage_dead_stmt(len_var.local_id().unwrap());
293 Operand::Copy(index_var)
294 } else {
295 last_arg
296 }
297 }
298
299 fn is_sized_type_var(&mut self, ty: &Ty) -> bool {
300 match ty.kind() {
301 TyKind::TypeVar(..) => {
302 if self.get_ctx().options.hide_marker_traits {
303 return true;
305 }
306 let params = self.get_params();
307 for clause in ¶ms.trait_clauses {
308 let tref = clause.trait_.clone().erase();
309 if tref.generics.types[0] == *ty
311 && self
312 .get_ctx()
313 .translated
314 .trait_decls
315 .get(tref.id)
316 .and_then(|decl| decl.item_meta.lang_item.clone())
317 == Some("sized".into())
318 {
319 return true;
320 }
321 }
322 false
323 }
324 _ => false,
325 }
326 }
327
328 fn compute_place_metadata(&mut self, place: &Place) -> Operand {
331 fn no_metadata<T: BodyTransformCtx>(ctx: &T) -> Operand {
334 let unit_meta = ctx.get_ctx().translated.unit_metadata.clone().unwrap();
335 Operand::Copy(Place::new_global(unit_meta, Ty::mk_unit()))
336 }
337
338 fn compute_place_metadata_inner<T: BodyTransformCtx>(
340 ctx: &mut T,
341 place: &Place,
342 metadata_ty: &Ty,
343 ) -> Option<Operand> {
344 let (subplace, proj) = place.as_projection()?;
345 match proj {
346 ProjectionElem::Deref => {
348 let metadata_place = subplace
349 .clone()
350 .project(ProjectionElem::PtrMetadata, metadata_ty.clone());
351 Some(Operand::Copy(metadata_place))
352 }
353 ProjectionElem::Field { .. } => {
354 compute_place_metadata_inner(ctx, subplace, metadata_ty)
355 }
356 ProjectionElem::Index { .. } => None,
358 ProjectionElem::PtrMetadata { .. } => None,
360 ProjectionElem::Subslice { from, to, from_end } => {
362 let to_idx = ctx.compute_subslice_end_idx(subplace, *to.clone(), *from_end);
363 let diff_place = ctx.fresh_var(None, Ty::mk_usize());
364 ctx.insert_assn_stmt(
365 diff_place.clone(),
366 Rvalue::BinaryOp(BinOp::Sub(OverflowMode::UB), to_idx, *from.clone()),
368 );
369 Some(Operand::Copy(diff_place))
370 }
371 }
372 }
373 trace!(
374 "getting ptr metadata for place: {}",
375 place.with_ctx(&self.get_ctx().into_fmt())
376 );
377 let metadata_ty = place
378 .ty()
379 .get_ptr_metadata(&self.get_ctx().translated)
380 .into_type();
381 if metadata_ty.is_unit()
382 || matches!(metadata_ty.kind(), TyKind::PtrMetadata(ty) if self.is_sized_type_var(ty))
383 {
384 return no_metadata(self);
386 }
387 trace!(
388 "computed metadata type: {}",
389 metadata_ty.with_ctx(&self.get_ctx().into_fmt())
390 );
391 compute_place_metadata_inner(self, place, &metadata_ty).unwrap_or_else(|| no_metadata(self))
392 }
393}
394
395pub struct UllbcStatementTransformCtx<'a> {
396 pub ctx: &'a mut TransformCtx,
397 pub params: &'a GenericParams,
398 pub locals: &'a mut Locals,
399 pub span: Span,
401 pub statements: Vec<ullbc_ast::Statement>,
403}
404
405impl BodyTransformCtx for UllbcStatementTransformCtx<'_> {
406 fn get_ctx(&self) -> &TransformCtx {
407 self.ctx
408 }
409 fn get_params(&self) -> &GenericParams {
410 self.params
411 }
412 fn get_locals_mut(&mut self) -> &mut Locals {
413 self.locals
414 }
415
416 fn insert_storage_live_stmt(&mut self, local: LocalId) {
417 self.statements.push(ullbc_ast::Statement::new(
418 self.span,
419 ullbc_ast::StatementKind::StorageLive(local),
420 ));
421 }
422
423 fn insert_assn_stmt(&mut self, place: Place, rvalue: Rvalue) {
424 self.statements.push(ullbc_ast::Statement::new(
425 self.span,
426 ullbc_ast::StatementKind::Assign(place, rvalue),
427 ));
428 }
429
430 fn insert_storage_dead_stmt(&mut self, local: LocalId) {
431 self.statements.push(ullbc_ast::Statement::new(
432 self.span,
433 ullbc_ast::StatementKind::StorageDead(local),
434 ));
435 }
436}
437
438impl FunDecl {
439 pub fn transform_ullbc_terminators(
440 &mut self,
441 ctx: &mut TransformCtx,
442 mut f: impl FnMut(&mut UllbcStatementTransformCtx, &mut ullbc_ast::Terminator),
443 ) {
444 if let Ok(body) = &mut self.body {
445 let params = &self.signature.generics;
446 let body = body.as_unstructured_mut().unwrap();
447 body.body.iter_mut().for_each(|block| {
448 let span = block.terminator.span;
449 let mut ctx = UllbcStatementTransformCtx {
450 ctx,
451 params,
452 locals: &mut body.locals,
453 span,
454 statements: std::mem::take(&mut block.statements),
455 };
456 f(&mut ctx, &mut block.terminator);
457 block.statements = ctx.statements;
458 });
459 }
460 }
461
462 pub fn transform_ullbc_statements(
463 &mut self,
464 ctx: &mut TransformCtx,
465 mut f: impl FnMut(&mut UllbcStatementTransformCtx, &mut ullbc_ast::Statement),
466 ) {
467 if let Ok(body) = &mut self.body {
468 let params = &self.signature.generics;
469 let body = body.as_unstructured_mut().unwrap();
470 body.body.iter_mut().for_each(|block| {
471 block.transform(|st: &mut ullbc_ast::Statement| {
472 let mut ctx = UllbcStatementTransformCtx {
473 ctx,
474 params,
475 locals: &mut body.locals,
476 span: st.span,
477 statements: Vec::new(),
478 };
479 f(&mut ctx, st);
480 ctx.statements
481 });
482 });
483 }
484 }
485}