charon_driver/translate/
translate_ctx.rs

1//! The translation contexts.
2use super::translate_crate::RustcItem;
3pub use super::translate_crate::{TraitImplSource, TransItemSource, TransItemSourceKind};
4use super::translate_generics::BindingLevel;
5use charon_lib::ast::*;
6use charon_lib::formatter::{FmtCtx, IntoFormatter};
7use charon_lib::ids::Vector;
8use charon_lib::options::TranslateOptions;
9use hax_frontend_exporter::{self as hax, SInto};
10use rustc_middle::ty::TyCtxt;
11use std::borrow::Cow;
12use std::cell::RefCell;
13use std::collections::{BTreeSet, HashMap, HashSet};
14use std::fmt::Debug;
15use std::path::PathBuf;
16use std::sync::Arc;
17use std::{fmt, mem};
18
19// Re-export to avoid having to fix imports.
20pub(crate) use charon_lib::errors::{
21    DepSource, ErrorCtx, Level, error_assert, raise_error, register_error,
22};
23
24/// Translation context used while translating the crate data into our representation.
25pub struct TranslateCtx<'tcx> {
26    /// The Rust compiler type context
27    pub tcx: TyCtxt<'tcx>,
28    /// Path to the toolchain root.
29    pub sysroot: PathBuf,
30    /// The Hax context
31    pub hax_state: hax::StateWithBase<'tcx>,
32
33    /// The options that control translation.
34    pub options: TranslateOptions,
35    /// The translated data.
36    pub translated: TranslatedCrate,
37
38    /// The map from rustc id to translated id.
39    pub id_map: HashMap<TransItemSource, AnyTransId>,
40    /// The reverse map of ids.
41    pub reverse_id_map: HashMap<AnyTransId, TransItemSource>,
42    /// The reverse filename map.
43    pub file_to_id: HashMap<FileName, FileId>,
44
45    /// Context for tracking and reporting errors.
46    pub errors: RefCell<ErrorCtx>,
47    /// The declarations we came accross and which we haven't translated yet. We keep them sorted
48    /// to make the output order a bit more stable.
49    pub items_to_translate: BTreeSet<TransItemSource>,
50    /// The declaration we've already processed (successfully or not).
51    pub processed: HashSet<TransItemSource>,
52    /// Stack of the translations currently happening. Used to avoid accidental cycles.
53    pub translate_stack: Vec<AnyTransId>,
54    /// Cache the names to compute them only once each.
55    pub cached_names: HashMap<RustcItem, Name>,
56    /// Cache the `ItemMeta`s to compute them only once each.
57    pub cached_item_metas: HashMap<TransItemSource, ItemMeta>,
58}
59
60/// A translation context for items.
61/// Augments the [TranslateCtx] with type-level variables.
62pub(crate) struct ItemTransCtx<'tcx, 'ctx> {
63    /// The definition we are currently extracting.
64    pub item_src: TransItemSource,
65    /// The id of the definition we are currently extracting, if there is one.
66    pub item_id: Option<AnyTransId>,
67    /// The translation context containing the top-level definitions/ids.
68    pub t_ctx: &'ctx mut TranslateCtx<'tcx>,
69    /// Whether to consider a `ImplExprAtom::Error` as an error for us. True except inside type
70    /// aliases, because rust does not enforce correct trait bounds on type aliases.
71    pub error_on_impl_expr_error: bool,
72
73    /// The stack of generic parameter binders for the current context. Each binder introduces an
74    /// entry in this stack, with the entry as index `0` being the innermost binder. These
75    /// parameters are referenced using [`DeBruijnVar`]; see there for details.
76    pub binding_levels: BindingStack<BindingLevel>,
77    /// (For traits only) accumulated implied trait clauses.
78    pub parent_trait_clauses: Vector<TraitClauseId, TraitClause>,
79    /// (For traits only) accumulated trait clauses on associated types.
80    pub item_trait_clauses: HashMap<TraitItemName, Vector<TraitClauseId, TraitClause>>,
81}
82
83/// Translates `T` into `U` using `hax`'s `SInto` trait, catching any hax panics.
84pub fn catch_sinto<S, T, U>(
85    s: &S,
86    err: &mut ErrorCtx,
87    krate: &TranslatedCrate,
88    span: Span,
89    x: &T,
90) -> Result<U, Error>
91where
92    T: Debug + SInto<S, U>,
93{
94    let unwind_safe_s = std::panic::AssertUnwindSafe(s);
95    let unwind_safe_x = std::panic::AssertUnwindSafe(x);
96    std::panic::catch_unwind(move || unwind_safe_x.sinto(*unwind_safe_s)).or_else(|_| {
97        raise_error!(
98            err,
99            crate(krate),
100            span,
101            "Hax panicked when translating `{x:?}`."
102        )
103    })
104}
105
106impl<'tcx, 'ctx> TranslateCtx<'tcx> {
107    /// Span an error and register the error.
108    pub fn span_err(&self, span: Span, msg: &str, level: Level) -> Error {
109        self.errors
110            .borrow_mut()
111            .span_err(&self.translated, span, msg, level)
112    }
113
114    /// Translates `T` into `U` using `hax`'s `SInto` trait, catching any hax panics.
115    pub fn catch_sinto<S, T, U>(&mut self, s: &S, span: Span, x: &T) -> Result<U, Error>
116    where
117        T: Debug + SInto<S, U>,
118    {
119        catch_sinto(s, &mut *self.errors.borrow_mut(), &self.translated, span, x)
120    }
121
122    /// Return the polymorphic definition for this item. Use with care, prefer `hax_def` whenever
123    /// possible.
124    ///
125    /// Used for computing names, for associated items, and for various checks.
126    pub fn poly_hax_def(&mut self, def_id: &hax::DefId) -> Result<Arc<hax::FullDef>, Error> {
127        self.hax_def_for_item(&RustcItem::Poly(def_id.clone()))
128    }
129
130    /// Return the definition for this item. This uses the polymorphic or monomorphic definition
131    /// depending on user choice.
132    pub fn hax_def_for_item(&mut self, item: &RustcItem) -> Result<Arc<hax::FullDef>, Error> {
133        let def_id = item.def_id();
134        let span = self.def_span(def_id);
135        if let RustcItem::Mono(item_ref) = item
136            && item_ref.has_param
137        {
138            raise_error!(self, span, "Item is not monomorphic: {item:?}")
139        }
140        // Hax takes care of caching the translation.
141        let unwind_safe_s = std::panic::AssertUnwindSafe(&self.hax_state);
142        std::panic::catch_unwind(move || match item {
143            RustcItem::Poly(def_id) => def_id.full_def(*unwind_safe_s),
144            RustcItem::Mono(item_ref) => item_ref.instantiated_full_def(*unwind_safe_s),
145        })
146        .or_else(|_| raise_error!(self, span, "Hax panicked when translating `{def_id:?}`."))
147    }
148
149    pub(crate) fn with_def_id<F, T>(
150        &mut self,
151        def_id: &hax::DefId,
152        item_id: Option<AnyTransId>,
153        f: F,
154    ) -> T
155    where
156        F: FnOnce(&mut Self) -> T,
157    {
158        let mut errors = self.errors.borrow_mut();
159        let current_def_id = mem::replace(&mut errors.def_id, item_id);
160        let current_def_id_is_local = mem::replace(&mut errors.def_id_is_local, def_id.is_local);
161        drop(errors); // important: release the refcell "lock"
162        let ret = f(self);
163        let mut errors = self.errors.borrow_mut();
164        errors.def_id = current_def_id;
165        errors.def_id_is_local = current_def_id_is_local;
166        ret
167    }
168}
169
170impl<'tcx, 'ctx> ItemTransCtx<'tcx, 'ctx> {
171    /// Create a new `ExecContext`.
172    pub(crate) fn new(
173        item_src: TransItemSource,
174        item_id: Option<AnyTransId>,
175        t_ctx: &'ctx mut TranslateCtx<'tcx>,
176    ) -> Self {
177        ItemTransCtx {
178            item_src,
179            item_id,
180            t_ctx,
181            error_on_impl_expr_error: true,
182            binding_levels: Default::default(),
183            parent_trait_clauses: Default::default(),
184            item_trait_clauses: Default::default(),
185        }
186    }
187
188    /// Whether to monomorphize items we encounter.
189    pub fn monomorphize(&self) -> bool {
190        matches!(self.item_src.item, RustcItem::Mono(..))
191    }
192
193    pub fn span_err(&self, span: Span, msg: &str, level: Level) -> Error {
194        self.t_ctx.span_err(span, msg, level)
195    }
196
197    pub fn hax_state(&self) -> &hax::StateWithBase<'tcx> {
198        &self.t_ctx.hax_state
199    }
200
201    pub fn hax_state_with_id(&self) -> hax::StateWithOwner<'tcx> {
202        use hax::BaseState;
203        let def_id = self.item_src.def_id().underlying_rust_def_id();
204        self.t_ctx.hax_state.clone().with_owner_id(def_id)
205    }
206
207    /// Return the definition for this item. This uses the polymorphic or monomorphic definition
208    /// depending on user choice.
209    pub fn hax_def(&mut self, item: &hax::ItemRef) -> Result<Arc<hax::FullDef>, Error> {
210        let item = if self.monomorphize() {
211            RustcItem::Mono(item.clone())
212        } else {
213            RustcItem::Poly(item.def_id.clone())
214        };
215        self.t_ctx.hax_def_for_item(&item)
216    }
217
218    pub(crate) fn poly_hax_def(&mut self, def_id: &hax::DefId) -> Result<Arc<hax::FullDef>, Error> {
219        self.t_ctx.poly_hax_def(def_id)
220    }
221}
222
223impl<'a> IntoFormatter for &'a TranslateCtx<'_> {
224    type C = FmtCtx<'a>;
225    fn into_fmt(self) -> Self::C {
226        self.translated.into_fmt()
227    }
228}
229
230impl<'a> IntoFormatter for &'a ItemTransCtx<'_, '_> {
231    type C = FmtCtx<'a>;
232    fn into_fmt(self) -> Self::C {
233        FmtCtx {
234            translated: Some(&self.t_ctx.translated),
235            generics: self.binding_levels.map_ref(|bl| Cow::Borrowed(&bl.params)),
236            locals: None,
237            indent_level: 0,
238        }
239    }
240}
241
242impl<'tcx, 'ctx> fmt::Display for TranslateCtx<'tcx> {
243    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
244        self.translated.fmt(f)
245    }
246}