1use crate::translate::translate_crate_to_ullbc;
3use crate::CharonFailure;
4use charon_lib::options::CliOpts;
5use charon_lib::transform::TransformCtx;
6use rustc_driver::{Callbacks, Compilation};
7use rustc_interface::interface::Compiler;
8use rustc_interface::Config;
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 let disabled_mir_passes = ["CheckAlignment"];
31 for pass in disabled_mir_passes {
32 config
33 .opts
34 .unstable_opts
35 .mir_enable_passes
36 .push((pass.to_owned(), false));
37 }
38}
39
40fn set_no_codegen(config: &mut Config) {
43 config.opts.unstable_opts.no_codegen = true;
44 config.opts.output_types = OutputTypes::new(&[(OutputType::Metadata, None)]);
46}
47
48static SKIP_BORROWCK: AtomicBool = AtomicBool::new(false);
50fn set_skip_borrowck() {
51 SKIP_BORROWCK.store(true, Ordering::SeqCst);
52}
53fn skip_borrowck_if_set(providers: &mut Providers) {
54 if SKIP_BORROWCK.load(Ordering::SeqCst) {
55 providers.mir_borrowck = |tcx, def_id| {
56 let (input_body, _promoted) = tcx.mir_promoted(def_id);
57 let input_body = &input_body.borrow();
58 let result = rustc_middle::mir::BorrowCheckResult {
60 concrete_opaque_types: Default::default(),
61 closure_requirements: None,
62 used_mut_upvars: Default::default(),
63 tainted_by_errors: input_body.tainted_by_errors,
64 };
65 tcx.arena.alloc(result)
66 }
67 }
68}
69
70fn setup_compiler(config: &mut Config, options: &CliOpts, do_translate: bool) {
71 if do_translate {
72 if options.skip_borrowck {
73 set_skip_borrowck();
75 }
76
77 config.override_queries = Some(|_sess, providers| {
78 skip_borrowck_if_set(providers);
79
80 });
88
89 set_no_codegen(config);
90 if options.use_polonius {
91 config.opts.unstable_opts.polonius = Polonius::Legacy;
92 }
93 }
94 set_mir_options(config);
95}
96
97pub fn run_rustc_driver(options: &CliOpts) -> Result<Option<TransformCtx>, CharonFailure> {
101 let mut compiler_args: Vec<String> = env::args().skip(1).collect();
104
105 let is_workspace_dependency =
110 env::var("CHARON_USING_CARGO").is_ok() && !env::var("CARGO_PRIMARY_PACKAGE").is_ok();
111 let is_target = arg_values(&compiler_args, "--target").next().is_some();
119 let is_selected_crate = !is_workspace_dependency && is_target;
121
122 let output = if !is_selected_crate {
123 trace!("Skipping charon; running compiler normally instead.");
124 run_compiler_with_callbacks(compiler_args, &mut RunCompilerNormallyCallbacks { options })?;
126 None
127 } else {
128 for extra_flag in options.rustc_args.iter().cloned() {
129 compiler_args.push(extra_flag);
130 }
131
132 let mut callback = CharonCallbacks {
134 options,
135 transform_ctx: None,
136 };
137 run_compiler_with_callbacks(compiler_args, &mut callback)?;
138 let ctx = callback.transform_ctx.ok_or(CharonFailure::RustcError)?;
140 Some(ctx)
141 };
142 Ok(output)
143}
144
145pub struct CharonCallbacks<'a> {
147 options: &'a CliOpts,
148 transform_ctx: Option<TransformCtx>,
150}
151impl<'a> Callbacks for CharonCallbacks<'a> {
152 fn config(&mut self, config: &mut Config) {
153 setup_compiler(config, self.options, true);
154 }
155
156 fn after_expansion<'tcx>(&mut self, compiler: &Compiler, tcx: TyCtxt<'tcx>) -> Compilation {
161 rustc_hir::def_id::DEF_ID_DEBUG
163 .swap(&(def_id_debug as fn(_, &mut fmt::Formatter<'_>) -> _));
164
165 let transform_ctx =
166 translate_crate_to_ullbc::translate(&self.options, tcx, compiler.sess.sysroot.clone());
167 self.transform_ctx = Some(transform_ctx);
168 Compilation::Continue
169 }
170 fn after_analysis<'tcx>(&mut self, _: &Compiler, _: TyCtxt<'tcx>) -> Compilation {
171 Compilation::Stop
173 }
174}
175
176pub struct RunCompilerNormallyCallbacks<'a> {
178 options: &'a CliOpts,
179}
180impl<'a> Callbacks for RunCompilerNormallyCallbacks<'a> {
181 fn config(&mut self, config: &mut Config) {
182 setup_compiler(config, self.options, false);
183 }
184}
185
186fn arg_values<'a, T: Deref<Target = str>>(
189 args: &'a [T],
190 needle: &'a str,
191) -> impl Iterator<Item = &'a str> {
192 struct ArgFilter<'a, T> {
193 args: std::slice::Iter<'a, T>,
194 needle: &'a str,
195 }
196 impl<'a, T: Deref<Target = str>> Iterator for ArgFilter<'a, T> {
197 type Item = &'a str;
198 fn next(&mut self) -> Option<Self::Item> {
199 while let Some(arg) = self.args.next() {
200 let mut split_arg = arg.splitn(2, '=');
201 if split_arg.next() == Some(self.needle) {
202 return match split_arg.next() {
203 arg @ Some(_) => arg,
205 None => self.args.next().map(|x| x.deref()),
207 };
208 }
209 }
210 None
211 }
212 }
213 ArgFilter {
214 args: args.iter(),
215 needle,
216 }
217}
218
219fn def_id_debug(def_id: rustc_hir::def_id::DefId, f: &mut fmt::Formatter<'_>) -> fmt::Result {
221 rustc_middle::ty::tls::with_opt(|opt_tcx| {
222 if let Some(tcx) = opt_tcx {
223 let crate_name = if def_id.is_local() {
224 tcx.crate_name(rustc_hir::def_id::LOCAL_CRATE)
225 } else {
226 tcx.cstore_untracked().crate_name(def_id.krate)
227 };
228 write!(
229 f,
230 "{}{}",
231 crate_name,
232 tcx.def_path(def_id).to_string_no_crate_verbose()
233 )?;
234 } else {
235 write!(f, "<can't access `tcx` to print `DefId` path>")?;
236 }
237 Ok(())
238 })
239}