Skip to main content

hax_rust_engine/
backends.rs

1//! Code generation backends.
2//!
3//! A backend is consititued of:
4//!  - a list of AST transformations to apply, those are called phases.
5//!  - and a printer.
6//!
7//! This top-level module is mostly an index of available backends and a
8//! small prelude to make backend modules concise.
9//!
10//! # Adding a new backend
11//! 1. Create a submodule under `src/backends/`, e.g. `foo.rs`.
12//! 2. Put your printer and backend there.
13//! 3. Re-export it here with `pub mod foo;`.
14//!
15//! See [`rust`] for an example implementation.
16
17pub mod fstar;
18pub mod lean;
19pub mod rust;
20
21use std::{collections::HashMap, rc::Rc};
22
23use crate::{
24    ast::{Item, Metadata, Module, span::Span},
25    attributes::LinkedItemGraph,
26    phase::legacy::group_consecutive_ocaml_phases,
27    printer::{HasLinkedItemGraph, Print, Printer},
28};
29use camino::Utf8PathBuf;
30use hax_types::engine_api::File;
31
32/// A hax backend.
33///
34/// A backend is responsible for turning the hax AST into sources of a target language.
35/// It combines:
36/// - a sequence of AST transformation phases, and
37/// - a printer that generates textual output.
38///
39/// For example, we have F\*, Coq, and Lean backends.
40/// Some are still in the old OCaml engine.
41pub trait Backend {
42    /// The printer type used by this backend.
43    type Printer: Printer;
44
45    /// Construct a new printer instance.
46    ///
47    /// By default this calls `Default::default` on the printer type.
48    fn printer(&self, linked_item_graph: Rc<LinkedItemGraph>) -> Self::Printer {
49        Self::Printer::default().with_linked_item_graph(linked_item_graph)
50    }
51
52    /// A short name identifying the backend.
53    ///
54    /// By default, this is delegated to the associated printer's [`Printer::NAME`].
55    const NAME: &'static str = Self::Printer::NAME;
56
57    /// The AST phases to apply before printing.
58    ///
59    /// Backends can override this to add transformations.
60    /// The default is an empty list (no transformations).
61    fn phases(&self) -> Vec<crate::phase::PhaseKind> {
62        vec![]
63    }
64
65    /// A list of resugaring phases.
66    fn resugaring_phases() -> Vec<Box<dyn prelude::Resugaring>> {
67        vec![]
68    }
69
70    /// Group a flat list of items into modules.
71    fn items_to_module(&self, items: Vec<Item>) -> Vec<Module> {
72        let mut modules: HashMap<_, Vec<_>> = HashMap::new();
73        for item in items {
74            let module_ident = item.ident.mod_only_closest_parent();
75            modules.entry(module_ident).or_default().push(item);
76        }
77        modules
78            .into_iter()
79            .map(|(ident, items)| Module {
80                ident,
81                items,
82                meta: Metadata {
83                    span: Span::dummy(),
84                    attributes: vec![],
85                },
86            })
87            .collect()
88    }
89
90    /// Print a list of modules into files
91    fn modules_to_files(&self, modules: Vec<Module>, mut printer: Self::Printer) -> Vec<File> {
92        modules
93            .into_iter()
94            .map(|module: Module| {
95                let path = self.module_path(&module).into_string();
96                let (contents, _) = printer.print(module);
97                File {
98                    path,
99                    contents,
100                    sourcemap: None,
101                }
102            })
103            .collect()
104    }
105
106    /// Compute the relative filesystem path where a given module should be written.
107    fn module_path(&self, module: &Module) -> Utf8PathBuf;
108}
109
110/// A backend can be interpreted as a phase
111impl<B: Backend> crate::phase::Phase for B {
112    fn apply(&self, items: &mut Vec<Item>) {
113        for phase in group_consecutive_ocaml_phases(self.phases()) {
114            phase.apply(items);
115        }
116    }
117}
118
119/// Apply a backend to a collection of AST items, producing output files.
120///
121/// This runs all of the backend's [`Backend::phases`], groups the items into
122/// modules via [`Backend::items_to_module`], and then uses the backend's printer
123/// to generate source files with paths determined by [`Backend::module_path`].
124pub fn apply_backend<B: Backend + 'static>(backend: B, mut items: Vec<Item>) -> Vec<File> {
125    crate::phase::Phase::apply(&backend, &mut items);
126
127    for mut resugaring_phase in B::resugaring_phases() {
128        for item in &mut items {
129            resugaring_phase.visit(item)
130        }
131    }
132
133    let linked_items_graph = Rc::new(LinkedItemGraph::new(
134        &items,
135        prelude::diagnostics::Context::Printer(B::NAME.into()),
136    ));
137
138    /// Drop any item marked with a hax attribute whose payload deserializes to
139    /// `AttrPayload::ItemStatus(ItemStatus::Included { late_skip: true })`.
140    ///
141    /// Items with such a "late-skip" attribute are typically generated by hax
142    /// attributes.
143    fn drop_skip_late_items(items: &mut Vec<Item>) {
144        items.retain_mut(|item| {
145            use hax_lib_macros_types::{AttrPayload, ItemStatus};
146            !item.meta.hax_attributes().any(|attr| {
147                matches!(
148                    attr,
149                    AttrPayload::ItemStatus(ItemStatus::Included { late_skip: true })
150                )
151            })
152        });
153    }
154
155    drop_skip_late_items(&mut items);
156
157    let modules = backend.items_to_module(items);
158    let printer = backend.printer(linked_items_graph.clone());
159    backend.modules_to_files(modules, printer)
160}
161
162mod prelude {
163    //! Small "bring-into-scope" set used by backend modules.
164    //!
165    //! Importing this prelude saves repetitive `use` lists in per-backend
166    //! modules without forcing these names on downstream users.
167    pub use super::Backend;
168    pub use crate::ast::{identifiers::global_id::view::AnyKind, literals::*, resugared::*, *};
169    pub use crate::printer::{
170        pretty_ast::{DocBuilder, PrettyAst, ToDocument, install_pretty_helpers},
171        render_view::*,
172        *,
173    };
174    pub use crate::resugarings::*;
175    pub use crate::symbol::Symbol;
176    pub use hax_rust_engine_macros::{prepend_associated_functions_with, setup_printer_struct};
177}