driver_hax_frontend_exporter/
features.rs

1use std::collections::HashSet;
2
3use rustc_driver::{Callbacks, Compilation};
4use rustc_interface::interface;
5use rustc_middle::ty::TyCtxt;
6use rustc_span::symbol::Symbol;
7
8use crate::callbacks_wrapper::CallbacksWrapper;
9
10use serde::{Deserialize, Serialize};
11
12/// A subset of `rustc_feature::Features` that is relevant to us
13#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct Features {
15    pub adt_const_params: bool,
16    pub generic_const_exprs: bool,
17    pub register_tool: bool,
18    pub auto_traits: bool,
19    pub negative_impls: bool,
20    pub registered_tools: HashSet<String>,
21}
22
23impl From<&rustc_feature::Features> for Features {
24    fn from(rfeatures: &rustc_feature::Features) -> Self {
25        Features {
26            adt_const_params: rfeatures.adt_const_params(),
27            generic_const_exprs: rfeatures.generic_const_exprs(),
28            register_tool: rfeatures.register_tool(),
29            auto_traits: rfeatures.auto_traits(),
30            negative_impls: rfeatures.negative_impls(),
31            registered_tools: HashSet::new(),
32        }
33    }
34}
35
36impl core::ops::Sub for Features {
37    type Output = Self;
38    fn sub(self, rhs: Self) -> Self {
39        fn sub(x: bool, y: bool) -> bool {
40            x & !y
41        }
42        Features {
43            adt_const_params: sub(self.adt_const_params, rhs.adt_const_params),
44            generic_const_exprs: sub(self.generic_const_exprs, rhs.generic_const_exprs),
45            register_tool: sub(self.register_tool, rhs.register_tool),
46            auto_traits: sub(self.auto_traits, rhs.auto_traits),
47            negative_impls: sub(self.negative_impls, rhs.negative_impls),
48            registered_tools: self
49                .registered_tools
50                .difference(&rhs.registered_tools)
51                .cloned()
52                .collect(),
53        }
54    }
55}
56
57impl Default for Features {
58    fn default() -> Self {
59        (&rustc_feature::Features::default()).into()
60    }
61}
62
63impl Features {
64    pub fn into_iter(&self) -> impl Iterator<Item = String> {
65        [
66            self.adt_const_params.then_some("adt_const_params"),
67            self.generic_const_exprs.then_some("generic_const_exprs"),
68            self.register_tool.then_some("register_tool"),
69        ]
70        .into_iter()
71        .flatten()
72        .map(|s| format!("feature({})", s))
73        .chain(
74            self.registered_tools
75                .clone()
76                .into_iter()
77                .map(|tool| format!("register_tool({})", tool)),
78        )
79    }
80    /// Runs Rustc with a driver that only collects which unstable
81    /// Rustc features are enabled
82    pub fn detect(options: &hax_types::cli_options::Options, rustc_args: &Vec<String>) -> Self {
83        struct CollectFeatures {
84            features: Features,
85        }
86        impl Callbacks for CollectFeatures {
87            fn after_expansion<'tcx>(
88                &mut self,
89                compiler: &interface::Compiler,
90                tcx: TyCtxt<'tcx>,
91            ) -> Compilation {
92                self.features = tcx.features().into();
93                self.features.registered_tools = tcx
94                    .registered_tools(())
95                    .iter()
96                    .map(|x| x.name.to_ident_string())
97                    .collect();
98                rustc_driver::Compilation::Stop
99            }
100        }
101        let mut callbacks = CollectFeatures {
102            features: Features::default(),
103        };
104        let exit_code = rustc_driver::catch_with_exit_code(|| {
105            rustc_driver::run_compiler(
106                rustc_args,
107                &mut CallbacksWrapper {
108                    sub: &mut callbacks,
109                    options: options.clone(),
110                },
111            )
112        });
113        if exit_code != 0 {
114            std::process::exit(exit_code);
115        }
116        callbacks.features.clone()
117    }
118
119    /// Just like `detect`, but wraps the call in a subprocess so that
120    /// we can capture `stdout` and `stderr`: we don't want the use to
121    /// see error message from Rustc twice, or Cargo to have to parse
122    /// Rustc messages twice.
123    pub fn detect_forking() -> Self {
124        use std::process::{Command, Stdio};
125        let output = Command::new(std::env::args().next().unwrap())
126            .args(std::env::args().skip(1))
127            .env("HAX_FEATURES_DETECTION_MODE", "1")
128            .stdout(Stdio::piped())
129            .stderr(Stdio::piped())
130            .spawn()
131            .unwrap()
132            .wait_with_output()
133            .unwrap();
134        let stderr = &std::str::from_utf8(&output.stderr).unwrap();
135        serde_json::from_str(stderr).unwrap_or_else(|e| {
136            eprintln!("{}", stderr);
137            tracing::error!("rustc emitted an error, aborting hax custom driver.");
138            std::process::exit(1);
139        })
140    }
141}