1use crate::CharonFailure;
3use crate::translate::translate_crate;
4use charon_lib::options::CliOpts;
5use charon_lib::transform::TransformCtx;
6use rustc_driver::{Callbacks, Compilation};
7use rustc_interface::Config;
8use rustc_interface::interface::Compiler;
9use rustc_middle::ty::TyCtxt;
10use rustc_middle::util::Providers;
11use rustc_session::config::{OutputType, OutputTypes, Polonius};
12use std::ops::Deref;
13use std::sync::atomic::{AtomicBool, Ordering};
14use std::{env, fmt};
15
16fn run_compiler_with_callbacks(
18 args: Vec<String>,
19 callbacks: &mut (dyn Callbacks + Send),
20) -> Result<(), CharonFailure> {
21 rustc_driver::catch_fatal_errors(|| rustc_driver::run_compiler(&args, callbacks))
22 .map_err(|_| CharonFailure::RustcError)
23}
24
25fn set_mir_options(config: &mut Config) {
27 config.opts.unstable_opts.always_encode_mir = true;
28 config.opts.unstable_opts.mir_opt_level = Some(0);
29 config.opts.unstable_opts.mir_emit_retag = true;
30 config.opts.unstable_opts.mir_preserve_ub = true;
31 let disabled_mir_passes = ["CheckAlignment"];
32 for pass in disabled_mir_passes {
33 config
34 .opts
35 .unstable_opts
36 .mir_enable_passes
37 .push((pass.to_owned(), false));
38 }
39}
40
41fn set_no_codegen(config: &mut Config) {
44 config.opts.unstable_opts.no_codegen = true;
45 config.opts.output_types = OutputTypes::new(&[(OutputType::Metadata, None)]);
47}
48
49static SKIP_BORROWCK: AtomicBool = AtomicBool::new(false);
51fn set_skip_borrowck() {
52 SKIP_BORROWCK.store(true, Ordering::SeqCst);
53}
54fn skip_borrowck_if_set(providers: &mut Providers) {
55 if SKIP_BORROWCK.load(Ordering::SeqCst) {
56 providers.mir_borrowck = |tcx, _def_id| {
57 Ok(tcx.arena.alloc(Default::default()))
59 }
60 }
61}
62
63fn setup_compiler(config: &mut Config, options: &CliOpts, do_translate: bool) {
64 if do_translate {
65 if options.skip_borrowck {
66 set_skip_borrowck();
68 }
69
70 config.override_queries = Some(|_sess, providers| {
71 skip_borrowck_if_set(providers);
72
73 });
81
82 set_no_codegen(config);
83 if options.use_polonius {
84 config.opts.unstable_opts.polonius = Polonius::Legacy;
85 }
86 }
87 set_mir_options(config);
88}
89
90pub fn run_rustc_driver(options: &CliOpts) -> Result<Option<TransformCtx>, CharonFailure> {
94 let mut compiler_args: Vec<String> = env::args().skip(1).collect();
97
98 let is_workspace_dependency =
103 env::var("CHARON_USING_CARGO").is_ok() && !env::var("CARGO_PRIMARY_PACKAGE").is_ok();
104 let is_target = arg_values(&compiler_args, "--target").next().is_some();
112 let is_selected_crate = !is_workspace_dependency && is_target;
114
115 let output = if !is_selected_crate {
116 trace!("Skipping charon; running compiler normally instead.");
117 run_compiler_with_callbacks(compiler_args, &mut RunCompilerNormallyCallbacks { options })?;
119 None
120 } else {
121 for extra_flag in options.rustc_args.iter().cloned() {
122 compiler_args.push(extra_flag);
123 }
124
125 let mut callback = CharonCallbacks {
127 options,
128 transform_ctx: None,
129 };
130 run_compiler_with_callbacks(compiler_args, &mut callback)?;
131 let ctx = callback.transform_ctx.ok_or(CharonFailure::RustcError)?;
133 Some(ctx)
134 };
135 Ok(output)
136}
137
138pub struct CharonCallbacks<'a> {
140 options: &'a CliOpts,
141 transform_ctx: Option<TransformCtx>,
143}
144impl<'a> Callbacks for CharonCallbacks<'a> {
145 fn config(&mut self, config: &mut Config) {
146 setup_compiler(config, self.options, true);
147 }
148
149 fn after_expansion<'tcx>(&mut self, compiler: &Compiler, tcx: TyCtxt<'tcx>) -> Compilation {
154 rustc_hir::def_id::DEF_ID_DEBUG
156 .swap(&(def_id_debug as fn(_, &mut fmt::Formatter<'_>) -> _));
157
158 let transform_ctx = translate_crate::translate(
159 &self.options,
160 tcx,
161 compiler.sess.opts.sysroot.path().to_owned(),
162 );
163 self.transform_ctx = Some(transform_ctx);
164 Compilation::Continue
165 }
166 fn after_analysis<'tcx>(&mut self, _: &Compiler, _: TyCtxt<'tcx>) -> Compilation {
167 Compilation::Stop
169 }
170}
171
172pub struct RunCompilerNormallyCallbacks<'a> {
174 options: &'a CliOpts,
175}
176impl<'a> Callbacks for RunCompilerNormallyCallbacks<'a> {
177 fn config(&mut self, config: &mut Config) {
178 setup_compiler(config, self.options, false);
179 }
180}
181
182fn arg_values<'a, T: Deref<Target = str>>(
185 args: &'a [T],
186 needle: &'a str,
187) -> impl Iterator<Item = &'a str> {
188 struct ArgFilter<'a, T> {
189 args: std::slice::Iter<'a, T>,
190 needle: &'a str,
191 }
192 impl<'a, T: Deref<Target = str>> Iterator for ArgFilter<'a, T> {
193 type Item = &'a str;
194 fn next(&mut self) -> Option<Self::Item> {
195 while let Some(arg) = self.args.next() {
196 let mut split_arg = arg.splitn(2, '=');
197 if split_arg.next() == Some(self.needle) {
198 return match split_arg.next() {
199 arg @ Some(_) => arg,
201 None => self.args.next().map(|x| x.deref()),
203 };
204 }
205 }
206 None
207 }
208 }
209 ArgFilter {
210 args: args.iter(),
211 needle,
212 }
213}
214
215fn def_id_debug(def_id: rustc_hir::def_id::DefId, f: &mut fmt::Formatter<'_>) -> fmt::Result {
217 rustc_middle::ty::tls::with_opt(|opt_tcx| {
218 if let Some(tcx) = opt_tcx {
219 let crate_name = if def_id.is_local() {
220 tcx.crate_name(rustc_hir::def_id::LOCAL_CRATE)
221 } else {
222 tcx.cstore_untracked().crate_name(def_id.krate)
223 };
224 write!(
225 f,
226 "{}{}",
227 crate_name,
228 tcx.def_path(def_id).to_string_no_crate_verbose()
229 )?;
230 } else {
231 write!(f, "<can't access `tcx` to print `DefId` path>")?;
232 }
233 Ok(())
234 })
235}