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::transform::CowBox;
8use crate::ullbc_ast;
9use std::cell::RefCell;
10use std::{fmt, mem};
11
12pub struct TransformCtx {
15 pub options: TranslateOptions,
17 pub translated: TranslatedCrate,
19 pub errors: RefCell<ErrorCtx>,
21}
22
23pub trait UllbcPass: Sync {
26 fn should_run(&self, _options: &TranslateOptions) -> bool {
28 true
29 }
30
31 fn transform_body(&self, _ctx: &mut TransformCtx, _body: &mut ullbc_ast::ExprBody) {}
33
34 fn transform_function(&self, ctx: &mut TransformCtx, decl: &mut FunDecl) {
36 if let Some(body) = decl.body.as_unstructured_mut() {
37 self.transform_body(ctx, body)
38 }
39 }
40
41 fn transform_item(&self, ctx: &mut TransformCtx, item: ItemRefMut<'_>) {
43 if let ItemRefMut::Fun(decl) = item {
44 self.transform_function(ctx, decl);
45 }
46 }
47
48 fn apply_preceding_passes(
52 &mut self,
53 _ctx: &mut TransformCtx,
54 _passes: &[CowBox<dyn UllbcPass>],
55 ) {
56 }
57
58 fn finalize(&self, _ctx: &mut TransformCtx) {}
60
61 fn name(&self) -> &str {
64 std::any::type_name::<Self>()
65 }
66}
67
68pub trait LlbcPass: Sync {
70 fn should_run(&self, _options: &TranslateOptions) -> bool {
72 true
73 }
74
75 fn transform_body(&self, _ctx: &mut TransformCtx, _body: &mut llbc_ast::ExprBody) {}
77
78 fn transform_function(&self, ctx: &mut TransformCtx, decl: &mut FunDecl) {
80 if let Some(body) = decl.body.as_structured_mut() {
81 self.transform_body(ctx, body)
82 }
83 }
84
85 fn name(&self) -> &str {
88 std::any::type_name::<Self>()
89 }
90}
91
92pub trait TransformPass: Sync {
94 fn should_run(&self, _options: &TranslateOptions) -> bool {
96 true
97 }
98
99 fn transform_ctx(&self, ctx: &mut TransformCtx);
100
101 fn name(&self) -> &str {
104 std::any::type_name::<Self>()
105 }
106}
107
108impl TransformCtx {
109 pub(crate) fn has_errors(&self) -> bool {
110 self.errors.borrow().has_errors()
111 }
112
113 pub(crate) fn span_err(&self, span: Span, msg: &str, level: Level) -> Error {
115 self.errors
116 .borrow_mut()
117 .span_err(&self.translated, span, msg, level)
118 }
119
120 pub(crate) fn opacity_for_name(&self, name: &Name) -> ItemOpacity {
121 self.options.opacity_for_name(&self.translated, name)
122 }
123
124 pub(crate) fn with_def_id<F, T>(
125 &mut self,
126 def_id: impl Into<ItemId>,
127 def_id_is_local: bool,
128 f: F,
129 ) -> T
130 where
131 F: FnOnce(&mut Self) -> T,
132 {
133 let mut errors = self.errors.borrow_mut();
134 let current_def_id = errors.def_id.replace(def_id.into());
135 let current_def_id_is_local = mem::replace(&mut errors.def_id_is_local, def_id_is_local);
136 drop(errors); let ret = f(self);
138 let mut errors = self.errors.borrow_mut();
139 errors.def_id = current_def_id;
140 errors.def_id_is_local = current_def_id_is_local;
141 ret
142 }
143
144 pub(crate) fn for_each_body(&mut self, mut f: impl FnMut(&mut Self, &mut Body)) {
148 let fn_ids = self.translated.fun_decls.all_indices();
149 for id in fn_ids {
150 if let Some(decl) = self.translated.fun_decls.get_mut(id)
151 && decl.body.has_contents()
152 {
153 let mut body = mem::replace(&mut decl.body, Body::Opaque);
154 let fun_decl_id = decl.def_id;
155 let is_local = decl.item_meta.is_local;
156 self.with_def_id(fun_decl_id, is_local, |ctx| f(ctx, &mut body));
157 self.translated.fun_decls[id].body = body;
158 }
159 }
160 }
161
162 pub(crate) fn for_each_fun_decl(&mut self, mut f: impl FnMut(&mut Self, &mut FunDecl)) {
165 let fn_ids = self.translated.fun_decls.all_indices();
166 for id in fn_ids {
167 if let Some(mut decl) = self.translated.fun_decls.remove(id) {
168 let fun_decl_id = decl.def_id;
169 let is_local = decl.item_meta.is_local;
170 self.with_def_id(fun_decl_id, is_local, |ctx| f(ctx, &mut decl));
171 self.translated.fun_decls.set_slot(id, decl);
172 }
173 }
174 }
175
176 pub fn for_each_item_mut(&mut self, mut f: impl for<'a> FnMut(&'a mut Self, ItemRefMut<'a>)) {
179 for id in self.translated.all_ids() {
180 if let Some(mut decl) = self.translated.remove_item_temporarily(id) {
181 f(self, decl.as_mut());
182 self.translated.set_item_slot(id, decl);
183 }
184 }
185 }
186}
187
188impl<'a> IntoFormatter for &'a TransformCtx {
189 type C = FmtCtx<'a>;
190
191 fn into_fmt(self) -> Self::C {
192 self.translated.into_fmt()
193 }
194}
195
196impl fmt::Display for TransformCtx {
197 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
198 self.translated.fmt(f)
199 }
200}
201
202pub trait BodyTransformCtx: Sized {
204 fn get_crate(&self) -> &TranslatedCrate;
205 fn get_options(&self) -> &TranslateOptions;
206 fn get_params(&self) -> &GenericParams;
207 fn get_locals_mut(&mut self) -> &mut Locals;
208
209 fn insert_storage_live_stmt(&mut self, local: LocalId);
210 fn insert_storage_dead_stmt(&mut self, local: LocalId);
211 fn insert_assn_stmt(&mut self, place: Place, rvalue: Rvalue);
212
213 fn to_fmt(&self) -> FmtCtx<'_> {
214 self.get_crate().into_fmt()
215 }
216
217 fn fresh_var(&mut self, name: Option<String>, ty: Ty) -> Place {
219 let var = self.get_locals_mut().new_var(name, ty);
220 self.insert_storage_live_stmt(var.local_id().unwrap());
221 var
222 }
223
224 fn rval_to_place(&mut self, rvalue: Rvalue, ty: Ty) -> Place {
227 if let Rvalue::Use(Operand::Move(place), WithRetag::No) = rvalue {
228 place
229 } else {
230 let var = self.fresh_var(None, ty);
231 self.insert_assn_stmt(var.clone(), rvalue);
232 var
233 }
234 }
235
236 fn compute_subslice_end_idx(
242 &mut self,
243 len_place: &Place,
244 last_arg: Operand,
245 from_end: bool,
246 ) -> Operand {
247 if from_end {
248 let len_var = self.fresh_var(None, Ty::mk_usize());
251 let len = match len_place.ty().kind() {
252 TyKind::Array(_, len) => Some(len.clone()),
253 TyKind::Slice(_) => None,
254 _ => panic!(
255 "called `compute_subslice_end_idx` on something that isn't an array or slice: {:?}",
256 len_place.ty()
257 ),
258 };
259 self.insert_assn_stmt(
260 len_var.clone(),
261 Rvalue::Len(len_place.clone(), len_place.ty().clone(), len),
262 );
263
264 let index_var = self.fresh_var(None, Ty::mk_usize());
268 self.insert_assn_stmt(
269 index_var.clone(),
270 Rvalue::BinaryOp(
271 BinOp::Sub(OverflowMode::UB),
272 Operand::Copy(len_var.clone()),
273 last_arg,
274 ),
275 );
276 self.insert_storage_dead_stmt(len_var.local_id().unwrap());
277 Operand::Copy(index_var)
278 } else {
279 last_arg
280 }
281 }
282
283 fn is_sized_type_var(&mut self, ty: &Ty) -> bool {
284 match ty.kind() {
285 TyKind::TypeVar(..) => {
286 if self.get_options().hide_marker_traits {
287 return true;
289 }
290 let params = self.get_params();
291 for clause in ¶ms.trait_clauses {
292 let tref = clause.trait_.clone().erase();
293 if tref.generics.types[0] == *ty
295 && self
296 .get_crate()
297 .trait_decls
298 .get(tref.id)
299 .and_then(|decl| decl.item_meta.lang_item.clone())
300 == Some("sized".into())
301 {
302 return true;
303 }
304 }
305 false
306 }
307 _ => false,
308 }
309 }
310
311 fn compute_place_metadata(&mut self, place: &Place) -> Operand {
326 fn compute_place_metadata_inner<T: BodyTransformCtx>(
328 ctx: &mut T,
329 place: &Place,
330 metadata_ty: &Ty,
331 ) -> Option<Operand> {
332 let (subplace, proj) = place.as_projection()?;
333 match proj {
334 ProjectionElem::Deref => {
336 let metadata_place = subplace
337 .clone()
338 .project(ProjectionElem::PtrMetadata, metadata_ty.clone());
339 Some(Operand::Copy(metadata_place))
340 }
341 ProjectionElem::Field { .. } => {
342 compute_place_metadata_inner(ctx, subplace, metadata_ty)
343 }
344 ProjectionElem::Index { .. } => None,
346 ProjectionElem::PtrMetadata => None,
348 ProjectionElem::Subslice { from, to, from_end } => {
350 let to_idx = ctx.compute_subslice_end_idx(subplace, *to.clone(), *from_end);
351 let diff_place = ctx.fresh_var(None, Ty::mk_usize());
352 ctx.insert_assn_stmt(
353 diff_place.clone(),
354 Rvalue::BinaryOp(BinOp::Sub(OverflowMode::UB), to_idx, *from.clone()),
356 );
357 Some(Operand::Copy(diff_place))
358 }
359 }
360 }
361 trace!(
362 "getting ptr metadata for place: {}",
363 place.with_ctx(&self.to_fmt())
364 );
365 let metadata_ty = place.ty().get_ptr_metadata(self.get_crate()).into_type();
366 if metadata_ty.is_unit()
367 || matches!(metadata_ty.kind(), TyKind::PtrMetadata(ty) if self.is_sized_type_var(ty))
368 {
369 return Operand::mk_const_unit();
371 }
372 trace!(
373 "computed metadata type: {}",
374 metadata_ty.with_ctx(&self.to_fmt())
375 );
376 compute_place_metadata_inner(self, place, &metadata_ty)
377 .unwrap_or_else(Operand::mk_const_unit)
378 }
379
380 fn borrow(&mut self, place: Place, kind: BorrowKind) -> Rvalue {
382 let ptr_metadata = self.compute_place_metadata(&place);
383 Rvalue::Ref {
384 place,
385 kind,
386 ptr_metadata,
387 }
388 }
389 fn raw_borrow(&mut self, place: Place, kind: RefKind) -> Rvalue {
391 let ptr_metadata = self.compute_place_metadata(&place);
392 Rvalue::RawPtr {
393 place,
394 kind,
395 ptr_metadata,
396 }
397 }
398
399 fn borrow_to_new_var(&mut self, place: Place, kind: BorrowKind, name: Option<String>) -> Place {
401 let ref_ty = TyKind::Ref(Region::Erased, place.ty().clone(), kind.into()).into_ty();
402 let target_place = self.fresh_var(name, ref_ty);
403 let rvalue = self.borrow(place, kind);
404 self.insert_assn_stmt(target_place.clone(), rvalue);
405 target_place
406 }
407 fn raw_borrow_to_new_var(
409 &mut self,
410 place: Place,
411 kind: RefKind,
412 name: Option<String>,
413 ) -> Place {
414 let ref_ty = TyKind::RawPtr(place.ty().clone(), kind).into_ty();
415 let target_place = self.fresh_var(name, ref_ty);
416 let rvalue = self.raw_borrow(place, kind);
417 self.insert_assn_stmt(target_place.clone(), rvalue);
418 target_place
419 }
420}
421
422pub struct UllbcStatementTransformCtx<'a> {
423 pub ctx: &'a mut TransformCtx,
424 pub params: &'a GenericParams,
425 pub locals: &'a mut Locals,
426 pub span: Span,
428 pub statements: Vec<ullbc_ast::Statement>,
430}
431
432impl BodyTransformCtx for UllbcStatementTransformCtx<'_> {
433 fn get_crate(&self) -> &TranslatedCrate {
434 &self.ctx.translated
435 }
436 fn get_options(&self) -> &TranslateOptions {
437 &self.ctx.options
438 }
439 fn get_params(&self) -> &GenericParams {
440 self.params
441 }
442 fn get_locals_mut(&mut self) -> &mut Locals {
443 self.locals
444 }
445
446 fn insert_storage_live_stmt(&mut self, local: LocalId) {
447 self.statements.push(ullbc_ast::Statement::new(
448 self.span,
449 ullbc_ast::StatementKind::StorageLive(local),
450 ));
451 }
452
453 fn insert_assn_stmt(&mut self, place: Place, rvalue: Rvalue) {
454 self.statements.push(ullbc_ast::Statement::new(
455 self.span,
456 ullbc_ast::StatementKind::Assign(place, rvalue),
457 ));
458 }
459
460 fn insert_storage_dead_stmt(&mut self, local: LocalId) {
461 self.statements.push(ullbc_ast::Statement::new(
462 self.span,
463 ullbc_ast::StatementKind::StorageDead(local),
464 ));
465 }
466}
467
468pub struct LlbcStatementTransformCtx<'a> {
469 pub ctx: &'a mut TransformCtx,
470 pub params: &'a GenericParams,
471 pub locals: &'a mut Locals,
472 pub span: Span,
474 pub statements: Vec<llbc_ast::Statement>,
476}
477
478impl BodyTransformCtx for LlbcStatementTransformCtx<'_> {
479 fn get_crate(&self) -> &TranslatedCrate {
480 &self.ctx.translated
481 }
482 fn get_options(&self) -> &TranslateOptions {
483 &self.ctx.options
484 }
485 fn get_params(&self) -> &GenericParams {
486 self.params
487 }
488 fn get_locals_mut(&mut self) -> &mut Locals {
489 self.locals
490 }
491
492 fn insert_storage_live_stmt(&mut self, local: LocalId) {
493 self.statements.push(llbc_ast::Statement::new(
494 self.span,
495 llbc_ast::StatementKind::StorageLive(local),
496 ));
497 }
498
499 fn insert_assn_stmt(&mut self, place: Place, rvalue: Rvalue) {
500 self.statements.push(llbc_ast::Statement::new(
501 self.span,
502 llbc_ast::StatementKind::Assign(place, rvalue),
503 ));
504 }
505
506 fn insert_storage_dead_stmt(&mut self, local: LocalId) {
507 self.statements.push(llbc_ast::Statement::new(
508 self.span,
509 llbc_ast::StatementKind::StorageDead(local),
510 ));
511 }
512}
513
514impl FunDecl {
515 pub fn transform_ullbc_statements(
516 &mut self,
517 ctx: &mut TransformCtx,
518 mut f: impl FnMut(&mut UllbcStatementTransformCtx, &mut ullbc_ast::Statement),
519 ) {
520 if let Some(body) = self.body.as_unstructured_mut() {
521 let mut ctx = UllbcStatementTransformCtx {
522 ctx,
523 params: &self.generics,
524 locals: &mut body.locals,
525 span: self.item_meta.span,
526 statements: Vec::new(),
527 };
528 body.body.iter_mut().for_each(|block| {
529 ctx.statements = Vec::with_capacity(block.statements.len());
530 for mut st in mem::take(&mut block.statements) {
531 ctx.span = st.span;
532 f(&mut ctx, &mut st);
533 ctx.statements.push(st);
534 }
535 block.statements = mem::take(&mut ctx.statements);
536 });
537 }
538 }
539
540 pub fn transform_ullbc_terminators(
541 &mut self,
542 ctx: &mut TransformCtx,
543 mut f: impl FnMut(&mut UllbcStatementTransformCtx, &mut ullbc_ast::Terminator),
544 ) {
545 if let Some(body) = self.body.as_unstructured_mut() {
546 let mut ctx = UllbcStatementTransformCtx {
547 ctx,
548 params: &self.generics,
549 locals: &mut body.locals,
550 span: self.item_meta.span,
551 statements: Vec::new(),
552 };
553 body.body.iter_mut().for_each(|block| {
554 ctx.span = block.terminator.span;
555 ctx.statements = mem::take(&mut block.statements);
556 f(&mut ctx, &mut block.terminator);
557 block.statements = mem::take(&mut ctx.statements);
558 });
559 }
560 }
561
562 pub fn transform_ullbc_operands(
563 &mut self,
564 ctx: &mut TransformCtx,
565 mut f: impl FnMut(&mut UllbcStatementTransformCtx, &mut Operand),
566 ) {
567 self.transform_ullbc_statements(ctx, |ctx, st| {
568 st.kind.dyn_visit_in_body_mut(|op: &mut Operand| f(ctx, op));
569 });
570 self.transform_ullbc_terminators(ctx, |ctx, st| {
571 st.kind.dyn_visit_in_body_mut(|op: &mut Operand| f(ctx, op));
572 });
573 }
574
575 pub fn transform_llbc_statements(
576 &mut self,
577 ctx: &mut TransformCtx,
578 mut f: impl FnMut(&mut LlbcStatementTransformCtx, &mut llbc_ast::Statement),
579 ) {
580 if let Some(body) = self.body.as_structured_mut() {
581 let mut ctx = LlbcStatementTransformCtx {
582 ctx,
583 locals: &mut body.locals,
584 statements: Vec::new(),
585 span: self.item_meta.span,
586 params: &self.generics,
587 };
588 body.body.visit_blocks_bwd(|block: &mut llbc_ast::Block| {
589 ctx.statements = Vec::with_capacity(block.statements.len());
590 for mut st in mem::take(&mut block.statements) {
591 ctx.span = st.span;
592 f(&mut ctx, &mut st);
593 ctx.statements.push(st);
594 }
595 block.statements = mem::take(&mut ctx.statements)
596 })
597 }
598 }
599}