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)) {
166 let fn_ids = self.translated.fun_decls.all_indices();
167 for id in fn_ids {
168 if let Some(mut decl) = self.translated.fun_decls.remove(id) {
169 let fun_decl_id = decl.def_id;
170 let is_local = decl.item_meta.is_local;
171 self.with_def_id(fun_decl_id, is_local, |ctx| f(ctx, &mut decl));
172 self.translated.fun_decls.set_slot(id, decl);
173 }
174 }
175 }
176
177 pub(crate) fn for_each_type_decl(&mut self, mut f: impl FnMut(&mut Self, &mut TypeDecl)) {
181 let type_ids = self.translated.type_decls.all_indices();
182 for id in type_ids {
183 if let Some(mut decl) = self.translated.type_decls.remove(id) {
184 let type_decl_id = decl.def_id;
185 let is_local = decl.item_meta.is_local;
186 self.with_def_id(type_decl_id, is_local, |ctx| f(ctx, &mut decl));
187 self.translated.type_decls.set_slot(id, decl);
188 }
189 }
190 }
191
192 pub fn for_each_item_mut(&mut self, mut f: impl for<'a> FnMut(&'a mut Self, ItemRefMut<'a>)) {
195 for id in self.translated.all_ids() {
196 if let Some(mut decl) = self.translated.remove_item_temporarily(id) {
197 f(self, decl.as_mut());
198 self.translated.put_item_back(id, decl);
199 }
200 }
201 }
202}
203
204impl<'a> IntoFormatter for &'a TransformCtx {
205 type C = FmtCtx<'a>;
206
207 fn into_fmt(self) -> Self::C {
208 self.translated.into_fmt()
209 }
210}
211
212impl fmt::Display for TransformCtx {
213 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
214 self.translated.fmt(f)
215 }
216}
217
218pub trait BodyTransformCtx: Sized {
220 fn get_crate(&self) -> &TranslatedCrate;
221 fn get_options(&self) -> &TranslateOptions;
222 fn get_params(&self) -> &GenericParams;
223 fn get_locals_mut(&mut self) -> &mut Locals;
224
225 fn insert_storage_live_stmt(&mut self, local: LocalId);
226 fn insert_storage_dead_stmt(&mut self, local: LocalId);
227 fn insert_assn_stmt(&mut self, place: Place, rvalue: Rvalue);
228
229 fn to_fmt(&self) -> FmtCtx<'_> {
230 self.get_crate().into_fmt()
231 }
232
233 fn fresh_var(&mut self, name: Option<String>, ty: Ty) -> Place {
235 let var = self.get_locals_mut().new_var(name, ty);
236 self.insert_storage_live_stmt(var.local_id().unwrap());
237 var
238 }
239
240 fn rval_to_place(&mut self, rvalue: Rvalue, ty: Ty) -> Place {
243 if let Rvalue::Use(Operand::Move(place), WithRetag::No) = rvalue {
244 place
245 } else {
246 let var = self.fresh_var(None, ty);
247 self.insert_assn_stmt(var.clone(), rvalue);
248 var
249 }
250 }
251
252 fn compute_subslice_end_idx(
258 &mut self,
259 len_place: &Place,
260 last_arg: Operand,
261 from_end: bool,
262 ) -> Operand {
263 if from_end {
264 let len_var = self.fresh_var(None, Ty::mk_usize());
267 let len = match len_place.ty().kind() {
268 TyKind::Array(_, len, _) => Some(len.clone()),
269 TyKind::Slice(..) => None,
270 _ => panic!(
271 "called `compute_subslice_end_idx` on something that isn't an array or slice: {:?}",
272 len_place.ty()
273 ),
274 };
275 self.insert_assn_stmt(
276 len_var.clone(),
277 Rvalue::Len(len_place.clone(), len_place.ty().clone(), len),
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_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_crate()
313 .trait_decls
314 .get(tref.id)
315 .and_then(|decl| decl.item_meta.lang_item.as_ref())
316 == Some(&from_rustc::LangItem::Sized)
317 {
318 return true;
319 }
320 }
321 false
322 }
323 _ => false,
324 }
325 }
326
327 fn compute_place_metadata(&mut self, place: &Place) -> Operand {
342 fn compute_place_metadata_inner<T: BodyTransformCtx>(
344 ctx: &mut T,
345 place: &Place,
346 metadata_ty: &Ty,
347 ) -> Option<Operand> {
348 let (subplace, proj) = place.as_projection()?;
349 match proj {
350 ProjectionElem::Deref => {
352 let metadata_place = subplace
353 .clone()
354 .project(ProjectionElem::PtrMetadata, metadata_ty.clone());
355 Some(Operand::Copy(metadata_place))
356 }
357 ProjectionElem::Field { .. } => {
358 compute_place_metadata_inner(ctx, subplace, metadata_ty)
359 }
360 ProjectionElem::Index { .. } => None,
362 ProjectionElem::PtrMetadata => None,
364 ProjectionElem::Subslice { from, to, from_end } => {
366 let to_idx = ctx.compute_subslice_end_idx(subplace, *to.clone(), *from_end);
367 let diff_place = ctx.fresh_var(None, Ty::mk_usize());
368 ctx.insert_assn_stmt(
369 diff_place.clone(),
370 Rvalue::BinaryOp(BinOp::Sub(OverflowMode::UB), to_idx, *from.clone()),
372 );
373 Some(Operand::Copy(diff_place))
374 }
375 }
376 }
377 trace!(
378 "getting ptr metadata for place: {}",
379 place.with_ctx(&self.to_fmt())
380 );
381 let metadata_ty = place.ty().get_ptr_metadata(self.get_crate()).into_type();
382 if metadata_ty.is_unit()
383 || matches!(metadata_ty.kind(), TyKind::PtrMetadata(ty) if self.is_sized_type_var(ty))
384 {
385 return Operand::mk_const_unit();
387 }
388 trace!(
389 "computed metadata type: {}",
390 metadata_ty.with_ctx(&self.to_fmt())
391 );
392 compute_place_metadata_inner(self, place, &metadata_ty)
393 .unwrap_or_else(Operand::mk_const_unit)
394 }
395
396 fn borrow(&mut self, place: Place, kind: BorrowKind) -> Rvalue {
398 let ptr_metadata = self.compute_place_metadata(&place);
399 Rvalue::Ref {
400 place,
401 kind,
402 ptr_metadata,
403 }
404 }
405 fn raw_borrow(&mut self, place: Place, kind: RefKind) -> Rvalue {
407 let ptr_metadata = self.compute_place_metadata(&place);
408 Rvalue::RawPtr {
409 place,
410 kind,
411 ptr_metadata,
412 }
413 }
414
415 fn borrow_to_new_var(&mut self, place: Place, kind: BorrowKind, name: Option<String>) -> Place {
417 let ref_ty = TyKind::Ref(Region::Erased, place.ty().clone(), kind.into()).into_ty();
418 let target_place = self.fresh_var(name, ref_ty);
419 let rvalue = self.borrow(place, kind);
420 self.insert_assn_stmt(target_place.clone(), rvalue);
421 target_place
422 }
423 fn raw_borrow_to_new_var(
425 &mut self,
426 place: Place,
427 kind: RefKind,
428 name: Option<String>,
429 ) -> Place {
430 let ref_ty = TyKind::RawPtr(place.ty().clone(), kind).into_ty();
431 let target_place = self.fresh_var(name, ref_ty);
432 let rvalue = self.raw_borrow(place, kind);
433 self.insert_assn_stmt(target_place.clone(), rvalue);
434 target_place
435 }
436}
437
438pub struct UllbcStatementTransformCtx<'a> {
439 pub ctx: &'a mut TransformCtx,
440 pub params: &'a GenericParams,
441 pub locals: &'a mut Locals,
442 pub span: Span,
444 pub statements: Vec<ullbc_ast::Statement>,
446}
447
448impl BodyTransformCtx for UllbcStatementTransformCtx<'_> {
449 fn get_crate(&self) -> &TranslatedCrate {
450 &self.ctx.translated
451 }
452 fn get_options(&self) -> &TranslateOptions {
453 &self.ctx.options
454 }
455 fn get_params(&self) -> &GenericParams {
456 self.params
457 }
458 fn get_locals_mut(&mut self) -> &mut Locals {
459 self.locals
460 }
461
462 fn insert_storage_live_stmt(&mut self, local: LocalId) {
463 self.statements.push(ullbc_ast::Statement::new(
464 self.span,
465 ullbc_ast::StatementKind::StorageLive(local),
466 ));
467 }
468
469 fn insert_assn_stmt(&mut self, place: Place, rvalue: Rvalue) {
470 self.statements.push(ullbc_ast::Statement::new(
471 self.span,
472 ullbc_ast::StatementKind::Assign(place, rvalue),
473 ));
474 }
475
476 fn insert_storage_dead_stmt(&mut self, local: LocalId) {
477 self.statements.push(ullbc_ast::Statement::new(
478 self.span,
479 ullbc_ast::StatementKind::StorageDead(local),
480 ));
481 }
482}
483
484pub struct LlbcStatementTransformCtx<'a> {
485 pub ctx: &'a mut TransformCtx,
486 pub params: &'a GenericParams,
487 pub locals: &'a mut Locals,
488 pub span: Span,
490 pub statements: Vec<llbc_ast::Statement>,
492}
493
494impl BodyTransformCtx for LlbcStatementTransformCtx<'_> {
495 fn get_crate(&self) -> &TranslatedCrate {
496 &self.ctx.translated
497 }
498 fn get_options(&self) -> &TranslateOptions {
499 &self.ctx.options
500 }
501 fn get_params(&self) -> &GenericParams {
502 self.params
503 }
504 fn get_locals_mut(&mut self) -> &mut Locals {
505 self.locals
506 }
507
508 fn insert_storage_live_stmt(&mut self, local: LocalId) {
509 self.statements.push(llbc_ast::Statement::new(
510 self.span,
511 llbc_ast::StatementKind::StorageLive(local),
512 ));
513 }
514
515 fn insert_assn_stmt(&mut self, place: Place, rvalue: Rvalue) {
516 self.statements.push(llbc_ast::Statement::new(
517 self.span,
518 llbc_ast::StatementKind::Assign(place, rvalue),
519 ));
520 }
521
522 fn insert_storage_dead_stmt(&mut self, local: LocalId) {
523 self.statements.push(llbc_ast::Statement::new(
524 self.span,
525 llbc_ast::StatementKind::StorageDead(local),
526 ));
527 }
528}
529
530impl FunDecl {
531 pub fn transform_ullbc_statements(
532 &mut self,
533 ctx: &mut TransformCtx,
534 mut f: impl FnMut(&mut UllbcStatementTransformCtx, &mut ullbc_ast::Statement),
535 ) {
536 if let Some(body) = self.body.as_unstructured_mut() {
537 let mut ctx = UllbcStatementTransformCtx {
538 ctx,
539 params: &self.generics,
540 locals: &mut body.locals,
541 span: self.item_meta.span,
542 statements: Vec::new(),
543 };
544 body.body.iter_mut().for_each(|block| {
545 ctx.statements = Vec::with_capacity(block.statements.len());
546 for mut st in mem::take(&mut block.statements) {
547 ctx.span = st.span;
548 f(&mut ctx, &mut st);
549 ctx.statements.push(st);
550 }
551 block.statements = mem::take(&mut ctx.statements);
552 });
553 }
554 }
555
556 pub fn transform_ullbc_terminators(
557 &mut self,
558 ctx: &mut TransformCtx,
559 mut f: impl FnMut(&mut UllbcStatementTransformCtx, &mut ullbc_ast::Terminator),
560 ) {
561 if let Some(body) = self.body.as_unstructured_mut() {
562 let mut ctx = UllbcStatementTransformCtx {
563 ctx,
564 params: &self.generics,
565 locals: &mut body.locals,
566 span: self.item_meta.span,
567 statements: Vec::new(),
568 };
569 body.body.iter_mut().for_each(|block| {
570 ctx.span = block.terminator.span;
571 ctx.statements = mem::take(&mut block.statements);
572 f(&mut ctx, &mut block.terminator);
573 block.statements = mem::take(&mut ctx.statements);
574 });
575 }
576 }
577
578 pub fn transform_ullbc_operands(
579 &mut self,
580 ctx: &mut TransformCtx,
581 mut f: impl FnMut(&mut UllbcStatementTransformCtx, &mut Operand),
582 ) {
583 self.transform_ullbc_statements(ctx, |ctx, st| {
584 st.kind.dyn_visit_in_body_mut(|op: &mut Operand| f(ctx, op));
585 });
586 self.transform_ullbc_terminators(ctx, |ctx, st| {
587 st.kind.dyn_visit_in_body_mut(|op: &mut Operand| f(ctx, op));
588 });
589 }
590
591 pub fn transform_llbc_statements(
592 &mut self,
593 ctx: &mut TransformCtx,
594 mut f: impl FnMut(&mut LlbcStatementTransformCtx, &mut llbc_ast::Statement),
595 ) {
596 if let Some(body) = self.body.as_structured_mut() {
597 let mut ctx = LlbcStatementTransformCtx {
598 ctx,
599 locals: &mut body.locals,
600 statements: Vec::new(),
601 span: self.item_meta.span,
602 params: &self.generics,
603 };
604 body.body.visit_blocks_bwd(|block: &mut llbc_ast::Block| {
605 ctx.statements = Vec::with_capacity(block.statements.len());
606 for mut st in mem::take(&mut block.statements) {
607 ctx.span = st.span;
608 f(&mut ctx, &mut st);
609 ctx.statements.push(st);
610 }
611 block.statements = mem::take(&mut ctx.statements)
612 })
613 }
614 }
615}