charon_driver/translate/
translate_ctx.rs

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