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::register_error;
8use crate::transform::CowBox;
9use crate::ullbc_ast;
10use std::cell::RefCell;
11use std::{fmt, mem};
12
13pub struct TransformCtx {
16 pub options: TranslateOptions,
18 pub translated: TranslatedCrate,
20 pub errors: RefCell<ErrorCtx>,
22}
23
24pub trait UllbcPass: Sync {
27 fn should_run(&self, _options: &TranslateOptions) -> bool {
29 true
30 }
31
32 fn transform_body(&self, _ctx: &mut TransformCtx, _body: &mut ullbc_ast::ExprBody) {}
34
35 fn transform_function(&self, ctx: &mut TransformCtx, decl: &mut FunDecl) {
37 if let Some(body) = decl.body.as_unstructured_mut() {
38 self.transform_body(ctx, body)
39 }
40 }
41
42 fn transform_item(&self, ctx: &mut TransformCtx, item: ItemRefMut<'_>) {
44 if let ItemRefMut::Fun(decl) = item {
45 self.transform_function(ctx, decl);
46 }
47 }
48
49 fn apply_preceding_passes(
53 &mut self,
54 _ctx: &mut TransformCtx,
55 _passes: &[CowBox<dyn UllbcPass>],
56 ) {
57 }
58
59 fn finalize(&self, _ctx: &mut TransformCtx) {}
61
62 fn name(&self) -> &str {
65 std::any::type_name::<Self>()
66 }
67}
68
69pub trait LlbcPass: Sync {
71 fn should_run(&self, _options: &TranslateOptions) -> bool {
73 true
74 }
75
76 fn transform_body(&self, _ctx: &mut TransformCtx, _body: &mut llbc_ast::ExprBody) {}
78
79 fn transform_function(&self, ctx: &mut TransformCtx, decl: &mut FunDecl) {
81 if let Some(body) = decl.body.as_structured_mut() {
82 self.transform_body(ctx, body)
83 }
84 }
85
86 fn name(&self) -> &str {
89 std::any::type_name::<Self>()
90 }
91}
92
93pub trait TransformPass: Sync {
95 fn should_run(&self, _options: &TranslateOptions) -> bool {
97 true
98 }
99
100 fn transform_ctx(&self, ctx: &mut TransformCtx);
101
102 fn name(&self) -> &str {
105 std::any::type_name::<Self>()
106 }
107}
108
109impl TransformCtx {
110 pub(crate) fn has_errors(&self) -> bool {
111 self.errors.borrow().has_errors()
112 }
113
114 pub(crate) fn span_err(&self, span: Span, msg: &str, level: Level) -> Error {
116 self.errors
117 .borrow_mut()
118 .span_err(&self.translated, span, msg, level)
119 }
120
121 pub(crate) fn opacity_for_name(&self, name: &Name) -> ItemOpacity {
122 self.options.opacity_for_name(&self.translated, name)
123 }
124
125 pub(crate) fn with_def_id(
127 &mut self,
128 def_id: impl Into<ItemId>,
129 def_id_is_local: bool,
130 span: Span,
131 f: impl FnOnce(&mut Self),
132 ) {
133 let def_id = def_id.into();
134 let mut errors = self.errors.borrow_mut();
135 let current_def_id = errors.def_id.replace(def_id);
136 let current_def_id_is_local = mem::replace(&mut errors.def_id_is_local, def_id_is_local);
137 drop(errors); if std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| f(self))).is_err() {
139 register_error!(
140 self,
141 span,
142 "Thread panicked while transforming item `{def_id:?}`."
143 );
144 }
145 let mut errors = self.errors.borrow_mut();
146 errors.def_id = current_def_id;
147 errors.def_id_is_local = current_def_id_is_local;
148 }
149
150 pub(crate) fn for_each_body(&mut self, mut f: impl FnMut(&mut Self, &mut Body)) {
154 let fn_ids = self.translated.fun_decls.all_indices();
155 for id in fn_ids {
156 if let Some(decl) = self.translated.fun_decls.get_mut(id)
157 && decl.body.has_contents()
158 {
159 let mut body = mem::replace(&mut decl.body, Body::Opaque);
160 let fun_decl_id = decl.def_id;
161 let is_local = decl.item_meta.is_local;
162 let span = decl.item_meta.span;
163 self.with_def_id(fun_decl_id, is_local, span, |ctx| f(ctx, &mut body));
164 let decl = &mut self.translated.fun_decls[id];
165 decl.body = body;
166 decl.item_meta.has_errors |= self.errors.borrow().item_has_errors(fun_decl_id);
167 }
168 }
169 }
170
171 pub(crate) fn for_each_fun_decl(&mut self, mut f: impl FnMut(&mut Self, &mut FunDecl)) {
175 let fn_ids = self.translated.fun_decls.all_indices();
176 for id in fn_ids {
177 if let Some(mut decl) = self.translated.fun_decls.remove(id) {
178 let fun_decl_id = decl.def_id;
179 let is_local = decl.item_meta.is_local;
180 let span = decl.item_meta.span;
181 self.with_def_id(fun_decl_id, is_local, span, |ctx| f(ctx, &mut decl));
182 decl.item_meta.has_errors |= self.errors.borrow().item_has_errors(fun_decl_id);
183 self.translated.fun_decls.set_slot(id, decl);
184 }
185 }
186 }
187
188 pub(crate) fn for_each_type_decl(&mut self, mut f: impl FnMut(&mut Self, &mut TypeDecl)) {
192 let type_ids = self.translated.type_decls.all_indices();
193 for id in type_ids {
194 if let Some(mut decl) = self.translated.type_decls.remove(id) {
195 let type_decl_id = decl.def_id;
196 let is_local = decl.item_meta.is_local;
197 let span = decl.item_meta.span;
198 self.with_def_id(type_decl_id, is_local, span, |ctx| f(ctx, &mut decl));
199 decl.item_meta.has_errors |= self.errors.borrow().item_has_errors(type_decl_id);
200 self.translated.type_decls.set_slot(id, decl);
201 }
202 }
203 }
204
205 pub fn for_each_item_mut(&mut self, mut f: impl for<'a> FnMut(&'a mut Self, ItemRefMut<'a>)) {
208 for id in self.translated.all_ids() {
209 if let Some(mut decl) = self.translated.remove_item_temporarily(id) {
210 let is_local = decl.as_ref().item_meta().is_local;
211 let span = decl.as_ref().item_meta().span;
212 self.with_def_id(id, is_local, span, |ctx| f(ctx, decl.as_mut()));
213 decl.as_mut().item_meta().has_errors |= self.errors.borrow().item_has_errors(id);
214 self.translated.put_item_back(id, decl);
215 }
216 }
217 }
218}
219
220impl<'a> IntoFormatter for &'a TransformCtx {
221 type C = FmtCtx<'a>;
222
223 fn into_fmt(self) -> Self::C {
224 self.translated.into_fmt()
225 }
226}
227
228impl fmt::Display for TransformCtx {
229 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
230 self.translated.fmt(f)
231 }
232}
233
234pub trait BodyTransformCtx: Sized {
236 fn get_crate(&self) -> &TranslatedCrate;
237 fn get_options(&self) -> &TranslateOptions;
238 fn get_params(&self) -> &GenericParams;
239 fn get_locals_mut(&mut self) -> &mut Locals;
240
241 fn insert_storage_live_stmt(&mut self, local: LocalId);
242 fn insert_storage_dead_stmt(&mut self, local: LocalId);
243 fn insert_assn_stmt(&mut self, place: Place, rvalue: Rvalue);
244
245 fn to_fmt(&self) -> FmtCtx<'_> {
246 self.get_crate().into_fmt()
247 }
248
249 fn fresh_var(&mut self, name: Option<String>, ty: Ty) -> Place {
251 let var = self.get_locals_mut().new_var(name, ty);
252 self.insert_storage_live_stmt(var.local_id().unwrap());
253 var
254 }
255
256 fn rval_to_place(&mut self, rvalue: Rvalue, ty: Ty) -> Place {
259 if let Rvalue::Use(Operand::Move(place), WithRetag::No) = rvalue {
260 place
261 } else {
262 let var = self.fresh_var(None, ty);
263 self.insert_assn_stmt(var.clone(), rvalue);
264 var
265 }
266 }
267
268 fn compute_subslice_end_idx(
274 &mut self,
275 len_place: &Place,
276 last_arg: Operand,
277 from_end: bool,
278 ) -> Operand {
279 if from_end {
280 let len = match len_place.ty().kind() {
281 TyKind::Array(_, len, _) => Operand::Const(len.clone()),
282 TyKind::Slice(..) => self.compute_place_metadata(len_place),
283 _ => panic!(
284 "called `compute_subslice_end_idx` on something that isn't an array or slice: {:?}",
285 len_place.ty()
286 ),
287 };
288 let index_var = self.fresh_var(None, Ty::mk_usize());
291 self.insert_assn_stmt(
292 index_var.clone(),
293 Rvalue::BinaryOp(BinOp::Sub(OverflowMode::UB), len, last_arg),
294 );
295 Operand::Read(index_var)
296 } else {
297 last_arg
298 }
299 }
300
301 fn is_sized_type_var(&mut self, ty: &Ty) -> bool {
302 match ty.kind() {
303 TyKind::TypeVar(..) => {
304 if self.get_options().hide_marker_traits {
305 return true;
307 }
308 let params = self.get_params();
309 for clause in ¶ms.trait_clauses {
310 let tref = clause.trait_.clone().erase();
311 if tref.generics.types[0] == *ty
313 && self
314 .get_crate()
315 .trait_decls
316 .get(tref.id)
317 .and_then(|decl| decl.item_meta.lang_item.as_ref())
318 == Some(&from_rustc::LangItem::Sized)
319 {
320 return true;
321 }
322 }
323 false
324 }
325 _ => false,
326 }
327 }
328
329 fn compute_place_metadata(&mut self, place: &Place) -> Operand {
344 fn compute_place_metadata_inner<T: BodyTransformCtx>(
346 ctx: &mut T,
347 place: &Place,
348 metadata_ty: &Ty,
349 ) -> Option<Operand> {
350 let (subplace, proj) = match &place.kind {
351 PlaceKind::Local(_) => return None,
352 PlaceKind::Global(gref) => {
353 let global = ctx.get_crate().global_decls.get(gref.id)?;
354 return Some(Operand::Const(global.ptr_metadata.clone()));
355 }
356 PlaceKind::Projection(subplace, proj) => (subplace, proj),
357 };
358 match proj {
359 ProjectionElem::Deref => {
361 let metadata_place = subplace
362 .clone()
363 .project(ProjectionElem::PtrMetadata, metadata_ty.clone());
364 Some(Operand::Read(metadata_place))
365 }
366 ProjectionElem::Field { .. } => {
367 compute_place_metadata_inner(ctx, subplace, metadata_ty)
368 }
369 ProjectionElem::Offset { .. } => None,
371 ProjectionElem::Index { .. } => None,
373 ProjectionElem::PtrMetadata => None,
375 ProjectionElem::Subslice { .. } if place.ty.kind().is_array() => None,
378 ProjectionElem::Subslice { from, to, from_end } => {
380 let to_idx = ctx.compute_subslice_end_idx(subplace, *to.clone(), *from_end);
381 let diff_place = ctx.fresh_var(None, Ty::mk_usize());
382 ctx.insert_assn_stmt(
383 diff_place.clone(),
384 Rvalue::BinaryOp(BinOp::Sub(OverflowMode::UB), to_idx, *from.clone()),
386 );
387 Some(Operand::Read(diff_place))
388 }
389 }
390 }
391 trace!(
392 "getting ptr metadata for place: {}",
393 place.with_ctx(&self.to_fmt())
394 );
395 let metadata_ty = place.ty().get_ptr_metadata(self.get_crate()).into_type();
396 if metadata_ty.is_unit()
397 || matches!(metadata_ty.kind(), TyKind::PtrMetadata(ty) if self.is_sized_type_var(ty))
398 {
399 return Operand::mk_const_unit();
401 }
402 trace!(
403 "computed metadata type: {}",
404 metadata_ty.with_ctx(&self.to_fmt())
405 );
406 compute_place_metadata_inner(self, place, &metadata_ty)
407 .unwrap_or_else(Operand::mk_const_unit)
408 }
409
410 fn offset_projection(
414 &mut self,
415 count: Operand,
416 ty: Option<&Ty>,
417 in_bounds: bool,
418 output_ty: Ty,
419 ty_is_sized: Option<TraitRef>,
420 ) -> ProjectionElem {
421 let bytes = match ty {
422 None => count,
423 Some(ty) => {
424 let count_ty = count.ty().clone();
425 let size =
426 ConstantExpr::new(ConstantExprKind::SizeOf(ty.clone()), count_ty.clone());
427 let mode = if in_bounds {
428 OverflowMode::UB
429 } else {
430 OverflowMode::Wrap
431 };
432 let rval = Rvalue::BinaryOp(BinOp::Mul(mode), count, Operand::Const(size));
433 Operand::Read(self.rval_to_place(rval, count_ty))
434 }
435 };
436 ProjectionElem::Offset {
437 bytes: Box::new(bytes),
438 in_bounds,
439 ty: output_ty,
440 ty_is_sized,
441 }
442 }
443
444 fn borrow(&mut self, place: Place, kind: BorrowKind) -> Rvalue {
446 let ptr_metadata = self.compute_place_metadata(&place);
447 Rvalue::Ref {
448 place,
449 kind,
450 ptr_metadata,
451 }
452 }
453 fn raw_borrow(&mut self, place: Place, kind: RefKind) -> Rvalue {
455 let ptr_metadata = self.compute_place_metadata(&place);
456 Rvalue::RawPtr {
457 place,
458 kind,
459 ptr_metadata,
460 }
461 }
462
463 fn borrow_to_new_var(&mut self, place: Place, kind: BorrowKind, name: Option<String>) -> Place {
465 let ref_ty = TyKind::Ref(Region::Erased, place.ty().clone(), kind.into()).into_ty();
466 let target_place = self.fresh_var(name, ref_ty);
467 let rvalue = self.borrow(place, kind);
468 self.insert_assn_stmt(target_place.clone(), rvalue);
469 target_place
470 }
471 fn raw_borrow_to_new_var(
473 &mut self,
474 place: Place,
475 kind: RefKind,
476 name: Option<String>,
477 ) -> Place {
478 let ref_ty = TyKind::RawPtr(place.ty().clone(), kind).into_ty();
479 let target_place = self.fresh_var(name, ref_ty);
480 let rvalue = self.raw_borrow(place, kind);
481 self.insert_assn_stmt(target_place.clone(), rvalue);
482 target_place
483 }
484}
485
486pub struct UllbcStatementTransformCtx<'a> {
487 pub ctx: &'a mut TransformCtx,
488 pub params: &'a GenericParams,
489 pub def_id: &'a FunDeclId,
490 pub locals: &'a mut Locals,
491 pub span: Span,
493 pub statements: Vec<ullbc_ast::Statement>,
495}
496
497impl BodyTransformCtx for UllbcStatementTransformCtx<'_> {
498 fn get_crate(&self) -> &TranslatedCrate {
499 &self.ctx.translated
500 }
501 fn get_options(&self) -> &TranslateOptions {
502 &self.ctx.options
503 }
504 fn get_params(&self) -> &GenericParams {
505 self.params
506 }
507 fn get_locals_mut(&mut self) -> &mut Locals {
508 self.locals
509 }
510
511 fn insert_storage_live_stmt(&mut self, local: LocalId) {
512 self.statements.push(ullbc_ast::Statement::new(
513 self.span,
514 ullbc_ast::StatementKind::StorageLive(local),
515 ));
516 }
517
518 fn insert_assn_stmt(&mut self, place: Place, rvalue: Rvalue) {
519 self.statements.push(ullbc_ast::Statement::new(
520 self.span,
521 ullbc_ast::StatementKind::Assign(place, rvalue),
522 ));
523 }
524
525 fn insert_storage_dead_stmt(&mut self, local: LocalId) {
526 self.statements.push(ullbc_ast::Statement::new(
527 self.span,
528 ullbc_ast::StatementKind::StorageDead(local),
529 ));
530 }
531}
532
533pub struct LlbcStatementTransformCtx<'a> {
534 pub ctx: &'a mut TransformCtx,
535 pub params: &'a GenericParams,
536 pub locals: &'a mut Locals,
537 pub span: Span,
539 pub statements: Vec<llbc_ast::Statement>,
541}
542
543impl BodyTransformCtx for LlbcStatementTransformCtx<'_> {
544 fn get_crate(&self) -> &TranslatedCrate {
545 &self.ctx.translated
546 }
547 fn get_options(&self) -> &TranslateOptions {
548 &self.ctx.options
549 }
550 fn get_params(&self) -> &GenericParams {
551 self.params
552 }
553 fn get_locals_mut(&mut self) -> &mut Locals {
554 self.locals
555 }
556
557 fn insert_storage_live_stmt(&mut self, local: LocalId) {
558 self.statements.push(llbc_ast::Statement::new(
559 self.span,
560 llbc_ast::StatementKind::StorageLive(local),
561 ));
562 }
563
564 fn insert_assn_stmt(&mut self, place: Place, rvalue: Rvalue) {
565 self.statements.push(llbc_ast::Statement::new(
566 self.span,
567 llbc_ast::StatementKind::Assign(place, rvalue),
568 ));
569 }
570
571 fn insert_storage_dead_stmt(&mut self, local: LocalId) {
572 self.statements.push(llbc_ast::Statement::new(
573 self.span,
574 llbc_ast::StatementKind::StorageDead(local),
575 ));
576 }
577}
578
579impl FunDecl {
580 pub fn transform_ullbc_statements(
581 &mut self,
582 ctx: &mut TransformCtx,
583 mut f: impl FnMut(&mut UllbcStatementTransformCtx, &mut ullbc_ast::Statement),
584 ) {
585 if let Some(body) = self.body.as_unstructured_mut() {
586 let mut ctx = UllbcStatementTransformCtx {
587 ctx,
588 params: &self.generics,
589 def_id: &self.def_id,
590 locals: &mut body.locals,
591 span: self.item_meta.span,
592 statements: Vec::new(),
593 };
594 body.body.iter_mut().for_each(|block| {
595 ctx.statements = Vec::with_capacity(block.statements.len());
596 for mut st in mem::take(&mut block.statements) {
597 ctx.span = st.span;
598 f(&mut ctx, &mut st);
599 ctx.statements.push(st);
600 }
601 block.statements = mem::take(&mut ctx.statements);
602 });
603 }
604 }
605
606 pub fn transform_ullbc_terminators(
607 &mut self,
608 ctx: &mut TransformCtx,
609 mut f: impl FnMut(&mut UllbcStatementTransformCtx, &mut ullbc_ast::Terminator),
610 ) {
611 if let Some(body) = self.body.as_unstructured_mut() {
612 let mut ctx = UllbcStatementTransformCtx {
613 ctx,
614 params: &self.generics,
615 def_id: &self.def_id,
616 locals: &mut body.locals,
617 span: self.item_meta.span,
618 statements: Vec::new(),
619 };
620 body.body.iter_mut().for_each(|block| {
621 ctx.span = block.terminator.span;
622 ctx.statements = mem::take(&mut block.statements);
623 f(&mut ctx, &mut block.terminator);
624 block.statements = mem::take(&mut ctx.statements);
625 });
626 }
627 }
628
629 pub fn transform_ullbc_operands(
630 &mut self,
631 ctx: &mut TransformCtx,
632 mut f: impl FnMut(&mut UllbcStatementTransformCtx, &mut Operand),
633 ) {
634 self.transform_ullbc_statements(ctx, |ctx, st| {
635 st.kind.dyn_visit_in_body_mut(|op: &mut Operand| f(ctx, op));
636 });
637 self.transform_ullbc_terminators(ctx, |ctx, st| {
638 st.kind.dyn_visit_in_body_mut(|op: &mut Operand| f(ctx, op));
639 });
640 }
641
642 pub fn transform_llbc_statements(
643 &mut self,
644 ctx: &mut TransformCtx,
645 mut f: impl FnMut(&mut LlbcStatementTransformCtx, &mut llbc_ast::Statement),
646 ) {
647 if let Some(body) = self.body.as_structured_mut() {
648 let mut ctx = LlbcStatementTransformCtx {
649 ctx,
650 locals: &mut body.locals,
651 statements: Vec::new(),
652 span: self.item_meta.span,
653 params: &self.generics,
654 };
655 body.body.visit_blocks_bwd(|block: &mut llbc_ast::Block| {
656 ctx.statements = Vec::with_capacity(block.statements.len());
657 for mut st in mem::take(&mut block.statements) {
658 ctx.span = st.span;
659 f(&mut ctx, &mut st);
660 ctx.statements.push(st);
661 }
662 block.statements = mem::take(&mut ctx.statements)
663 })
664 }
665 }
666}