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 macro_rules! for_each {
210 ($vector:ident, $kind:ident) => {
211 for id in self.translated.$vector.all_indices() {
212 if let Some(mut decl) = self.translated.$vector.remove(id) {
213 f(self, ItemRefMut::$kind(&mut decl));
214 self.translated.$vector.set_slot(id, decl);
215 }
216 }
217 };
218 }
219 for_each!(type_decls, Type);
220 for_each!(fun_decls, Fun);
221 for_each!(global_decls, Global);
222 for_each!(trait_decls, TraitDecl);
223 for_each!(trait_impls, TraitImpl);
224 }
225}
226
227impl<'a> IntoFormatter for &'a TransformCtx {
228 type C = FmtCtx<'a>;
229
230 fn into_fmt(self) -> Self::C {
231 self.translated.into_fmt()
232 }
233}
234
235impl fmt::Display for TransformCtx {
236 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
237 self.translated.fmt(f)
238 }
239}
240
241pub trait BodyTransformCtx: Sized {
243 fn get_ctx(&self) -> &TransformCtx;
244 fn get_params(&self) -> &GenericParams;
245 fn get_locals_mut(&mut self) -> &mut Locals;
246
247 fn insert_storage_live_stmt(&mut self, local: LocalId);
248 fn insert_storage_dead_stmt(&mut self, local: LocalId);
249 fn insert_assn_stmt(&mut self, place: Place, rvalue: Rvalue);
250
251 fn fresh_var(&mut self, name: Option<String>, ty: Ty) -> Place {
253 let var = self.get_locals_mut().new_var(name, ty);
254 self.insert_storage_live_stmt(var.local_id().unwrap());
255 var
256 }
257
258 fn compute_subslice_end_idx(
264 &mut self,
265 len_place: &Place,
266 last_arg: Operand,
267 from_end: bool,
268 ) -> Operand {
269 if from_end {
270 let len_var = self.fresh_var(None, Ty::mk_usize());
273 self.insert_assn_stmt(
274 len_var.clone(),
275 Rvalue::Len(
276 len_place.clone(),
277 len_place.ty().clone(),
278 len_place
279 .ty()
280 .as_adt()
281 .unwrap()
282 .generics
283 .const_generics
284 .get(0.into())
285 .cloned(),
286 ),
287 );
288
289 let index_var = self.fresh_var(None, Ty::mk_usize());
293 self.insert_assn_stmt(
294 index_var.clone(),
295 Rvalue::BinaryOp(
296 BinOp::Sub(OverflowMode::UB),
297 Operand::Copy(len_var.clone()),
298 last_arg,
299 ),
300 );
301 self.insert_storage_dead_stmt(len_var.local_id().unwrap());
302 Operand::Copy(index_var)
303 } else {
304 last_arg
305 }
306 }
307
308 fn is_sized_type_var(&mut self, ty: &Ty) -> bool {
309 match ty.kind() {
310 TyKind::TypeVar(..) => {
311 if self.get_ctx().options.hide_marker_traits {
312 return true;
314 }
315 let params = self.get_params();
316 for clause in ¶ms.trait_clauses {
317 let tref = clause.trait_.clone().erase();
318 if tref.generics.types[0] == *ty
320 && self
321 .get_ctx()
322 .translated
323 .trait_decls
324 .get(tref.id)
325 .and_then(|decl| decl.item_meta.lang_item.clone())
326 == Some("sized".into())
327 {
328 return true;
329 }
330 }
331 false
332 }
333 _ => false,
334 }
335 }
336
337 fn compute_place_metadata(&mut self, place: &Place) -> Operand {
340 fn no_metadata<T: BodyTransformCtx>(ctx: &T) -> Operand {
343 let unit_meta = ctx.get_ctx().translated.unit_metadata.clone().unwrap();
344 Operand::Copy(Place::new_global(unit_meta, Ty::mk_unit()))
345 }
346
347 fn compute_place_metadata_inner<T: BodyTransformCtx>(
349 ctx: &mut T,
350 place: &Place,
351 metadata_ty: &Ty,
352 ) -> Option<Operand> {
353 let (subplace, proj) = place.as_projection()?;
354 match proj {
355 ProjectionElem::Deref => {
357 let metadata_place = subplace
358 .clone()
359 .project(ProjectionElem::PtrMetadata, metadata_ty.clone());
360 Some(Operand::Copy(metadata_place))
361 }
362 ProjectionElem::Field { .. } => {
363 compute_place_metadata_inner(ctx, subplace, metadata_ty)
364 }
365 ProjectionElem::Index { .. } => None,
367 ProjectionElem::PtrMetadata { .. } => None,
369 ProjectionElem::Subslice { from, to, from_end } => {
371 let to_idx = ctx.compute_subslice_end_idx(subplace, *to.clone(), *from_end);
372 let diff_place = ctx.fresh_var(None, Ty::mk_usize());
373 ctx.insert_assn_stmt(
374 diff_place.clone(),
375 Rvalue::BinaryOp(BinOp::Sub(OverflowMode::UB), to_idx, *from.clone()),
377 );
378 Some(Operand::Copy(diff_place))
379 }
380 }
381 }
382 trace!(
383 "getting ptr metadata for place: {}",
384 place.with_ctx(&self.get_ctx().into_fmt())
385 );
386 let metadata_ty = place
387 .ty()
388 .get_ptr_metadata(&self.get_ctx().translated)
389 .into_type();
390 if metadata_ty.is_unit()
391 || matches!(metadata_ty.kind(), TyKind::PtrMetadata(ty) if self.is_sized_type_var(ty))
392 {
393 return no_metadata(self);
395 }
396 trace!(
397 "computed metadata type: {}",
398 metadata_ty.with_ctx(&self.get_ctx().into_fmt())
399 );
400 compute_place_metadata_inner(self, place, &metadata_ty).unwrap_or_else(|| no_metadata(self))
401 }
402}
403
404pub struct UllbcStatementTransformCtx<'a> {
405 pub ctx: &'a mut TransformCtx,
406 pub params: &'a GenericParams,
407 pub locals: &'a mut Locals,
408 pub span: Span,
410 pub statements: Vec<ullbc_ast::Statement>,
412}
413
414impl BodyTransformCtx for UllbcStatementTransformCtx<'_> {
415 fn get_ctx(&self) -> &TransformCtx {
416 self.ctx
417 }
418 fn get_params(&self) -> &GenericParams {
419 self.params
420 }
421 fn get_locals_mut(&mut self) -> &mut Locals {
422 self.locals
423 }
424
425 fn insert_storage_live_stmt(&mut self, local: LocalId) {
426 self.statements.push(ullbc_ast::Statement::new(
427 self.span,
428 ullbc_ast::StatementKind::StorageLive(local),
429 ));
430 }
431
432 fn insert_assn_stmt(&mut self, place: Place, rvalue: Rvalue) {
433 self.statements.push(ullbc_ast::Statement::new(
434 self.span,
435 ullbc_ast::StatementKind::Assign(place, rvalue),
436 ));
437 }
438
439 fn insert_storage_dead_stmt(&mut self, local: LocalId) {
440 self.statements.push(ullbc_ast::Statement::new(
441 self.span,
442 ullbc_ast::StatementKind::StorageDead(local),
443 ));
444 }
445}
446
447impl FunDecl {
448 pub fn transform_ullbc_terminators(
449 &mut self,
450 ctx: &mut TransformCtx,
451 mut f: impl FnMut(&mut UllbcStatementTransformCtx, &mut ullbc_ast::Terminator),
452 ) {
453 if let Ok(body) = &mut self.body {
454 let params = &self.signature.generics;
455 let body = body.as_unstructured_mut().unwrap();
456 body.body.iter_mut().for_each(|block| {
457 let span = block.terminator.span;
458 let mut ctx = UllbcStatementTransformCtx {
459 ctx,
460 params,
461 locals: &mut body.locals,
462 span,
463 statements: std::mem::take(&mut block.statements),
464 };
465 f(&mut ctx, &mut block.terminator);
466 block.statements = ctx.statements;
467 });
468 }
469 }
470
471 pub fn transform_ullbc_statements(
472 &mut self,
473 ctx: &mut TransformCtx,
474 mut f: impl FnMut(&mut UllbcStatementTransformCtx, &mut ullbc_ast::Statement),
475 ) {
476 if let Ok(body) = &mut self.body {
477 let params = &self.signature.generics;
478 let body = body.as_unstructured_mut().unwrap();
479 body.body.iter_mut().for_each(|block| {
480 block.transform(|st: &mut ullbc_ast::Statement| {
481 let mut ctx = UllbcStatementTransformCtx {
482 ctx,
483 params,
484 locals: &mut body.locals,
485 span: st.span,
486 statements: Vec::new(),
487 };
488 f(&mut ctx, st);
489 ctx.statements
490 });
491 });
492 }
493 }
494}