charon_lib/transform/
ctx.rs

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
11/// Simpler context used for rustc-independent code transformation. This only depends on rustc for
12/// its error reporting machinery.
13pub struct TransformCtx {
14    /// The options that control transformation.
15    pub options: TranslateOptions,
16    /// The translated data.
17    pub translated: TranslatedCrate,
18    /// Context for tracking and reporting errors.
19    pub errors: RefCell<ErrorCtx>,
20}
21
22/// A pass that modifies ullbc bodies.
23pub trait UllbcPass: Sync {
24    /// Transform a body.
25    fn transform_body(&self, _ctx: &mut TransformCtx, _body: &mut ullbc_ast::ExprBody) {}
26
27    /// Transform a function declaration. This forwards to `transform_body` by default.
28    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    /// Transform the given context. This forwards to the other methods by default.
35    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    /// The name of the pass, used for debug logging. The default implementation uses the type
48    /// name.
49    fn name(&self) -> &str {
50        std::any::type_name::<Self>()
51    }
52
53    /// Log that the pass is about to be run on this body.
54    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.fmt_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
75/// A pass that modifies llbc bodies.
76pub trait LlbcPass: Sync {
77    /// Transform a body.
78    fn transform_body(&self, _ctx: &mut TransformCtx, _body: &mut llbc_ast::ExprBody) {}
79
80    /// Transform a function declaration. This forwards to `transform_body` by default.
81    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    /// Transform the given context. This forwards to the other methods by default.
88    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    /// The name of the pass, used for debug logging. The default implementation uses the type
101    /// name.
102    fn name(&self) -> &str {
103        std::any::type_name::<Self>()
104    }
105
106    /// Log that the pass is about to be run on this body.
107    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.fmt_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
128/// A pass that transforms the crate data.
129pub trait TransformPass: Sync {
130    fn transform_ctx(&self, ctx: &mut TransformCtx);
131
132    /// The name of the pass, used for debug logging. The default implementation uses the type
133    /// name.
134    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    /// Span an error and register the error.
145    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<AnyTransId>,
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); // important: release the refcell "lock"
168        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    /// Mutably iterate over the bodies.
176    /// Warning: we replace each body with `Err(Opaque)` while inspecting it so we can keep access
177    /// to the rest of the crate.
178    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    /// Mutably iterate over the function declarations.
193    /// Warning: each inspected fundecl becomes inaccessible from `ctx` during the course of this function.
194    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    /// Iterate mutably over all items, keeping access to `self`. To make this work, we move out
207    /// each item before iterating over it.
208    pub fn for_each_item_mut(
209        &mut self,
210        mut f: impl for<'a> FnMut(&'a mut Self, AnyTransItemMut<'a>),
211    ) {
212        macro_rules! for_each {
213            ($vector:ident, $kind:ident) => {
214                for id in self.translated.$vector.all_indices() {
215                    if let Some(mut decl) = self.translated.$vector.remove(id) {
216                        f(self, AnyTransItemMut::$kind(&mut decl));
217                        self.translated.$vector.set_slot(id, decl);
218                    }
219                }
220            };
221        }
222        for_each!(type_decls, Type);
223        for_each!(fun_decls, Fun);
224        for_each!(global_decls, Global);
225        for_each!(trait_decls, TraitDecl);
226        for_each!(trait_impls, TraitImpl);
227    }
228}
229
230impl<'a> IntoFormatter for &'a TransformCtx {
231    type C = FmtCtx<'a>;
232
233    fn into_fmt(self) -> Self::C {
234        self.translated.into_fmt()
235    }
236}
237
238impl fmt::Display for TransformCtx {
239    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
240        self.translated.fmt(f)
241    }
242}