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_var = self.fresh_var(None, Ty::mk_usize());
283 let len = match len_place.ty().kind() {
284 TyKind::Array(_, len, _) => Some(len.clone()),
285 TyKind::Slice(..) => None,
286 _ => panic!(
287 "called `compute_subslice_end_idx` on something that isn't an array or slice: {:?}",
288 len_place.ty()
289 ),
290 };
291 self.insert_assn_stmt(
292 len_var.clone(),
293 Rvalue::Len(len_place.clone(), len_place.ty().clone(), len),
294 );
295
296 let index_var = self.fresh_var(None, Ty::mk_usize());
300 self.insert_assn_stmt(
301 index_var.clone(),
302 Rvalue::BinaryOp(
303 BinOp::Sub(OverflowMode::UB),
304 Operand::Copy(len_var.clone()),
305 last_arg,
306 ),
307 );
308 self.insert_storage_dead_stmt(len_var.local_id().unwrap());
309 Operand::Copy(index_var)
310 } else {
311 last_arg
312 }
313 }
314
315 fn is_sized_type_var(&mut self, ty: &Ty) -> bool {
316 match ty.kind() {
317 TyKind::TypeVar(..) => {
318 if self.get_options().hide_marker_traits {
319 return true;
321 }
322 let params = self.get_params();
323 for clause in ¶ms.trait_clauses {
324 let tref = clause.trait_.clone().erase();
325 if tref.generics.types[0] == *ty
327 && self
328 .get_crate()
329 .trait_decls
330 .get(tref.id)
331 .and_then(|decl| decl.item_meta.lang_item.as_ref())
332 == Some(&from_rustc::LangItem::Sized)
333 {
334 return true;
335 }
336 }
337 false
338 }
339 _ => false,
340 }
341 }
342
343 fn compute_place_metadata(&mut self, place: &Place) -> Operand {
358 fn compute_place_metadata_inner<T: BodyTransformCtx>(
360 ctx: &mut T,
361 place: &Place,
362 metadata_ty: &Ty,
363 ) -> Option<Operand> {
364 let (subplace, proj) = match &place.kind {
365 PlaceKind::Local(_) => return None,
366 PlaceKind::Global(gref) => {
367 let global = ctx.get_crate().global_decls.get(gref.id)?;
368 return Some(Operand::Const(global.ptr_metadata.clone()));
369 }
370 PlaceKind::Projection(subplace, proj) => (subplace, proj),
371 };
372 match proj {
373 ProjectionElem::Deref => {
375 let metadata_place = subplace
376 .clone()
377 .project(ProjectionElem::PtrMetadata, metadata_ty.clone());
378 Some(Operand::Copy(metadata_place))
379 }
380 ProjectionElem::Field { .. } => {
381 compute_place_metadata_inner(ctx, subplace, metadata_ty)
382 }
383 ProjectionElem::Offset { .. } => None,
385 ProjectionElem::Index { .. } => None,
387 ProjectionElem::PtrMetadata => None,
389 ProjectionElem::Subslice { .. } if place.ty.kind().is_array() => None,
392 ProjectionElem::Subslice { from, to, from_end } => {
394 let to_idx = ctx.compute_subslice_end_idx(subplace, *to.clone(), *from_end);
395 let diff_place = ctx.fresh_var(None, Ty::mk_usize());
396 ctx.insert_assn_stmt(
397 diff_place.clone(),
398 Rvalue::BinaryOp(BinOp::Sub(OverflowMode::UB), to_idx, *from.clone()),
400 );
401 Some(Operand::Copy(diff_place))
402 }
403 }
404 }
405 trace!(
406 "getting ptr metadata for place: {}",
407 place.with_ctx(&self.to_fmt())
408 );
409 let metadata_ty = place.ty().get_ptr_metadata(self.get_crate()).into_type();
410 if metadata_ty.is_unit()
411 || matches!(metadata_ty.kind(), TyKind::PtrMetadata(ty) if self.is_sized_type_var(ty))
412 {
413 return Operand::mk_const_unit();
415 }
416 trace!(
417 "computed metadata type: {}",
418 metadata_ty.with_ctx(&self.to_fmt())
419 );
420 compute_place_metadata_inner(self, place, &metadata_ty)
421 .unwrap_or_else(Operand::mk_const_unit)
422 }
423
424 fn offset_projection(
428 &mut self,
429 count: Operand,
430 ty: Option<&Ty>,
431 in_bounds: bool,
432 output_ty: Ty,
433 ty_is_sized: Option<TraitRef>,
434 ) -> ProjectionElem {
435 let bytes = match ty {
436 None => count,
437 Some(ty) => {
438 let count_ty = count.ty().clone();
439 let size =
440 ConstantExpr::new(ConstantExprKind::SizeOf(ty.clone()), count_ty.clone());
441 let mode = if in_bounds {
442 OverflowMode::UB
443 } else {
444 OverflowMode::Wrap
445 };
446 let rval = Rvalue::BinaryOp(BinOp::Mul(mode), count, Operand::Const(size));
447 Operand::Copy(self.rval_to_place(rval, count_ty))
448 }
449 };
450 ProjectionElem::Offset {
451 bytes: Box::new(bytes),
452 in_bounds,
453 ty: output_ty,
454 ty_is_sized,
455 }
456 }
457
458 fn borrow(&mut self, place: Place, kind: BorrowKind) -> Rvalue {
460 let ptr_metadata = self.compute_place_metadata(&place);
461 Rvalue::Ref {
462 place,
463 kind,
464 ptr_metadata,
465 }
466 }
467 fn raw_borrow(&mut self, place: Place, kind: RefKind) -> Rvalue {
469 let ptr_metadata = self.compute_place_metadata(&place);
470 Rvalue::RawPtr {
471 place,
472 kind,
473 ptr_metadata,
474 }
475 }
476
477 fn borrow_to_new_var(&mut self, place: Place, kind: BorrowKind, name: Option<String>) -> Place {
479 let ref_ty = TyKind::Ref(Region::Erased, place.ty().clone(), kind.into()).into_ty();
480 let target_place = self.fresh_var(name, ref_ty);
481 let rvalue = self.borrow(place, kind);
482 self.insert_assn_stmt(target_place.clone(), rvalue);
483 target_place
484 }
485 fn raw_borrow_to_new_var(
487 &mut self,
488 place: Place,
489 kind: RefKind,
490 name: Option<String>,
491 ) -> Place {
492 let ref_ty = TyKind::RawPtr(place.ty().clone(), kind).into_ty();
493 let target_place = self.fresh_var(name, ref_ty);
494 let rvalue = self.raw_borrow(place, kind);
495 self.insert_assn_stmt(target_place.clone(), rvalue);
496 target_place
497 }
498}
499
500pub struct UllbcStatementTransformCtx<'a> {
501 pub ctx: &'a mut TransformCtx,
502 pub params: &'a GenericParams,
503 pub def_id: &'a FunDeclId,
504 pub locals: &'a mut Locals,
505 pub span: Span,
507 pub statements: Vec<ullbc_ast::Statement>,
509}
510
511impl BodyTransformCtx for UllbcStatementTransformCtx<'_> {
512 fn get_crate(&self) -> &TranslatedCrate {
513 &self.ctx.translated
514 }
515 fn get_options(&self) -> &TranslateOptions {
516 &self.ctx.options
517 }
518 fn get_params(&self) -> &GenericParams {
519 self.params
520 }
521 fn get_locals_mut(&mut self) -> &mut Locals {
522 self.locals
523 }
524
525 fn insert_storage_live_stmt(&mut self, local: LocalId) {
526 self.statements.push(ullbc_ast::Statement::new(
527 self.span,
528 ullbc_ast::StatementKind::StorageLive(local),
529 ));
530 }
531
532 fn insert_assn_stmt(&mut self, place: Place, rvalue: Rvalue) {
533 self.statements.push(ullbc_ast::Statement::new(
534 self.span,
535 ullbc_ast::StatementKind::Assign(place, rvalue),
536 ));
537 }
538
539 fn insert_storage_dead_stmt(&mut self, local: LocalId) {
540 self.statements.push(ullbc_ast::Statement::new(
541 self.span,
542 ullbc_ast::StatementKind::StorageDead(local),
543 ));
544 }
545}
546
547pub struct LlbcStatementTransformCtx<'a> {
548 pub ctx: &'a mut TransformCtx,
549 pub params: &'a GenericParams,
550 pub locals: &'a mut Locals,
551 pub span: Span,
553 pub statements: Vec<llbc_ast::Statement>,
555}
556
557impl BodyTransformCtx for LlbcStatementTransformCtx<'_> {
558 fn get_crate(&self) -> &TranslatedCrate {
559 &self.ctx.translated
560 }
561 fn get_options(&self) -> &TranslateOptions {
562 &self.ctx.options
563 }
564 fn get_params(&self) -> &GenericParams {
565 self.params
566 }
567 fn get_locals_mut(&mut self) -> &mut Locals {
568 self.locals
569 }
570
571 fn insert_storage_live_stmt(&mut self, local: LocalId) {
572 self.statements.push(llbc_ast::Statement::new(
573 self.span,
574 llbc_ast::StatementKind::StorageLive(local),
575 ));
576 }
577
578 fn insert_assn_stmt(&mut self, place: Place, rvalue: Rvalue) {
579 self.statements.push(llbc_ast::Statement::new(
580 self.span,
581 llbc_ast::StatementKind::Assign(place, rvalue),
582 ));
583 }
584
585 fn insert_storage_dead_stmt(&mut self, local: LocalId) {
586 self.statements.push(llbc_ast::Statement::new(
587 self.span,
588 llbc_ast::StatementKind::StorageDead(local),
589 ));
590 }
591}
592
593impl FunDecl {
594 pub fn transform_ullbc_statements(
595 &mut self,
596 ctx: &mut TransformCtx,
597 mut f: impl FnMut(&mut UllbcStatementTransformCtx, &mut ullbc_ast::Statement),
598 ) {
599 if let Some(body) = self.body.as_unstructured_mut() {
600 let mut ctx = UllbcStatementTransformCtx {
601 ctx,
602 params: &self.generics,
603 def_id: &self.def_id,
604 locals: &mut body.locals,
605 span: self.item_meta.span,
606 statements: Vec::new(),
607 };
608 body.body.iter_mut().for_each(|block| {
609 ctx.statements = Vec::with_capacity(block.statements.len());
610 for mut st in mem::take(&mut block.statements) {
611 ctx.span = st.span;
612 f(&mut ctx, &mut st);
613 ctx.statements.push(st);
614 }
615 block.statements = mem::take(&mut ctx.statements);
616 });
617 }
618 }
619
620 pub fn transform_ullbc_terminators(
621 &mut self,
622 ctx: &mut TransformCtx,
623 mut f: impl FnMut(&mut UllbcStatementTransformCtx, &mut ullbc_ast::Terminator),
624 ) {
625 if let Some(body) = self.body.as_unstructured_mut() {
626 let mut ctx = UllbcStatementTransformCtx {
627 ctx,
628 params: &self.generics,
629 def_id: &self.def_id,
630 locals: &mut body.locals,
631 span: self.item_meta.span,
632 statements: Vec::new(),
633 };
634 body.body.iter_mut().for_each(|block| {
635 ctx.span = block.terminator.span;
636 ctx.statements = mem::take(&mut block.statements);
637 f(&mut ctx, &mut block.terminator);
638 block.statements = mem::take(&mut ctx.statements);
639 });
640 }
641 }
642
643 pub fn transform_ullbc_operands(
644 &mut self,
645 ctx: &mut TransformCtx,
646 mut f: impl FnMut(&mut UllbcStatementTransformCtx, &mut Operand),
647 ) {
648 self.transform_ullbc_statements(ctx, |ctx, st| {
649 st.kind.dyn_visit_in_body_mut(|op: &mut Operand| f(ctx, op));
650 });
651 self.transform_ullbc_terminators(ctx, |ctx, st| {
652 st.kind.dyn_visit_in_body_mut(|op: &mut Operand| f(ctx, op));
653 });
654 }
655
656 pub fn transform_llbc_statements(
657 &mut self,
658 ctx: &mut TransformCtx,
659 mut f: impl FnMut(&mut LlbcStatementTransformCtx, &mut llbc_ast::Statement),
660 ) {
661 if let Some(body) = self.body.as_structured_mut() {
662 let mut ctx = LlbcStatementTransformCtx {
663 ctx,
664 locals: &mut body.locals,
665 statements: Vec::new(),
666 span: self.item_meta.span,
667 params: &self.generics,
668 };
669 body.body.visit_blocks_bwd(|block: &mut llbc_ast::Block| {
670 ctx.statements = Vec::with_capacity(block.statements.len());
671 for mut st in mem::take(&mut block.statements) {
672 ctx.span = st.span;
673 f(&mut ctx, &mut st);
674 ctx.statements.push(st);
675 }
676 block.statements = mem::take(&mut ctx.statements)
677 })
678 }
679 }
680}