1use crate::CharonFailure;
3use crate::toolchain::CHARON_SYSROOT_VAR;
4use crate::translate::translate_crate;
5use charon_lib::errors::ErrorCtx;
6use charon_lib::options::{self, CliOpts, selected_crate_var};
7use charon_lib::transform::TransformCtx;
8use charon_lib::utils::arg_value;
9use itertools::Itertools;
10use rustc_driver::{Callbacks, Compilation};
11use rustc_interface::Config;
12use rustc_interface::interface::Compiler;
13use rustc_middle::ty::{InstanceKind, TyCtxt};
14use rustc_middle::util::Providers;
15use rustc_session::config::{OutputType, OutputTypes};
16use rustc_span::{ErrorGuaranteed, Symbol};
17use std::num::NonZero;
18use std::sync::atomic::{AtomicBool, Ordering};
19use std::{env, fmt};
20
21fn run_compiler_with_callbacks(
23 args: Vec<String>,
24 callbacks: &mut (dyn Callbacks + Send),
25) -> Result<(), CharonFailure> {
26 rustc_driver::catch_fatal_errors(|| rustc_driver::run_compiler(&args, callbacks))
27 .map_err(|_| CharonFailure::RustcError)
28}
29
30fn set_mir_options(config: &mut Config) {
32 config.opts.unstable_opts.always_encode_mir = true;
33 config.opts.unstable_opts.mir_opt_level = Some(0);
34 config.opts.unstable_opts.mir_preserve_ub = true;
35 let disabled_mir_passes = ["CheckAlignment", "CheckNull"];
36 for pass in disabled_mir_passes {
37 config
38 .opts
39 .unstable_opts
40 .mir_enable_passes
41 .push((pass.to_owned(), false));
42 }
43}
44
45fn set_parallel_frontend(config: &mut Config) {
47 if config.opts.jobs.frontend.is_none() {
48 const RUSTC_MAX_THREADS_CAP: usize = u8::MAX as usize;
50 let threads = std::thread::available_parallelism()
51 .map_or(1, |n| n.get())
52 .min(RUSTC_MAX_THREADS_CAP);
53 config.opts.jobs.frontend = NonZero::new(threads).filter(|n| n.get() > 1);
54 }
55}
56
57static SKIP_BORROWCK: AtomicBool = AtomicBool::new(false);
59fn set_skip_borrowck() {
60 SKIP_BORROWCK.store(true, Ordering::SeqCst);
61}
62fn skip_borrowck_if_set(providers: &mut Providers) {
63 if SKIP_BORROWCK.load(Ordering::SeqCst) {
64 providers.queries.mir_borrowck = |tcx, _def_id| {
65 Ok(tcx.arena.alloc(Default::default()))
67 }
68 }
69}
70
71fn setup_compiler(
72 config: &mut Config,
73 options: &CliOpts,
74 is_target_crate: bool,
75 do_translate: bool,
76 emit_artifacts: bool,
77 codegen: bool,
78) {
79 if is_target_crate && let Some(crate_name) = &config.opts.crate_name {
80 let var = selected_crate_var(crate_name);
83 config.track_state = Some(Box::new(move |sess| {
84 let value = env::var(&var).ok().map(|value| Symbol::intern(&value));
85 let var = Symbol::intern(&var);
86 sess.env_depinfo.borrow_mut().insert((var, value));
87 }));
88 }
89
90 if do_translate {
91 if options.skip_borrowck {
92 set_skip_borrowck();
94 }
95
96 config.override_queries = Some(|_sess, providers| {
97 skip_borrowck_if_set(providers);
98
99 });
107
108 config.opts.unstable_opts.no_codegen = !codegen;
109 if !emit_artifacts {
110 config.opts.output_types = OutputTypes::new(&[(OutputType::Object, None)]);
111 }
112 set_parallel_frontend(config);
113 }
114 set_mir_options(config);
115}
116
117fn precheck_rustc_errors(tcx: TyCtxt<'_>) -> bool {
121 type QueryResult = Result<(), ErrorGuaranteed>;
122
123 tcx.par_hir_for_each_module(|module| {
124 tcx.ensure_ok().check_mod_attrs(module);
125 tcx.ensure_ok().check_mod_unstable_api_usage(module);
126 });
127
128 let _: QueryResult = tcx.ensure_result().check_type_wf(());
129 for &trait_def_id in tcx.all_local_trait_impls(()).keys() {
130 let _: QueryResult = tcx.ensure_result().coherent_trait(trait_def_id);
131 }
132 let _: QueryResult = tcx.ensure_result().crate_inherent_impls_validity_check(());
133 let _: QueryResult = tcx.ensure_result().crate_inherent_impls_overlap_check(());
134
135 tcx.par_hir_body_owners(|def_id| {
136 let def_kind = tcx.def_kind(def_id);
137 if !matches!(def_kind, rustc_hir::def::DefKind::AnonConst)
138 && !tcx.is_typeck_child(def_id.to_def_id())
139 {
140 tcx.ensure_ok().typeck(def_id);
141 }
142 });
143
144 tcx.dcx().has_errors().is_some()
145}
146
147fn check_late_rustc_errors(tcx: TyCtxt<'_>) {
150 tcx.par_hir_body_owners(|def_id| {
151 let _ = tcx.instance_mir(InstanceKind::Item(def_id.to_def_id()));
152 });
153
154 if tcx.dcx().err_count() == 0 {
155 let _ = tcx.collect_and_partition_mono_items(());
156 }
157}
158
159pub fn run_rustc_driver() -> Result<Option<(TransformCtx, CliOpts)>, CharonFailure> {
163 let mut compiler_args: Vec<String> = env::args().skip(1).collect();
166 trace!(
167 "charon-driver called with args: {}",
168 compiler_args.iter().format(" ")
169 );
170
171 let is_workspace_dependency =
176 env::var("CHARON_USING_CARGO").is_ok() && env::var("CARGO_PRIMARY_PACKAGE").is_err();
177 let emit_artifacts =
180 env::var("CHARON_USING_CARGO").is_ok() || env::var("CHARON_EMIT_ARTIFACTS").is_ok();
181 let mut codegen = emit_artifacts;
183 let is_target_crate = arg_value(&compiler_args, "--target").is_some();
191 let is_selected_crate = !is_workspace_dependency && is_target_crate;
193
194 let mut error_ctx = ErrorCtx::new();
195
196 let options = match env::var(options::CHARON_ARGS)
200 .ok()
201 .map(|opts| serde_json::from_str::<options::CliOpts>(&opts).unwrap())
202 {
203 Some(options) => options,
204 None if !is_selected_crate => Default::default(),
205 None => {
206 register_error!(
207 error_ctx,
208 no_crate,
209 "environment variable `CHARON_ARGS` not set; \
210 don't call `charon-driver` directly, call `charon rustc` instead"
211 );
212 return Err(CharonFailure::CharonError(1));
213 }
214 };
215
216 if options.sysroot.as_deref() == Some("default") {
217 } else if let Some(sysroot) = options.sysroot.as_ref()
219 && sysroot != "miri"
220 {
221 compiler_args.push(format!("--sysroot={sysroot}"));
222 } else if is_target_crate && let Ok(sysroot) = env::var(CHARON_SYSROOT_VAR) {
223 compiler_args.push(format!("--sysroot={sysroot}"));
225 codegen = false;
227 }
228
229 let output = if !is_selected_crate {
230 trace!("Skipping charon; running compiler normally instead.");
231 let mut callback = RunCompilerNormallyCallbacks { is_target_crate };
233 run_compiler_with_callbacks(compiler_args, &mut callback)?;
234 None
235 } else {
236 error_ctx.continue_on_failure = !options.abort_on_error;
237 error_ctx.error_on_warnings = options.error_on_warnings;
238
239 for extra_flag in options.rustc_args.iter().cloned() {
240 compiler_args.push(extra_flag);
241 }
242
243 let mut callback = CharonCallbacks {
245 options: &options,
246 emit_artifacts,
247 codegen,
248 error_ctx: Some(error_ctx),
249 transform_ctx: None,
250 };
251 charon_lib::timing::time("rustc-driver", || {
252 run_compiler_with_callbacks(compiler_args, &mut callback)
253 })?;
254 let ctx = callback.transform_ctx.ok_or(CharonFailure::RustcError)?;
256 Some((ctx, options))
257 };
258 Ok(output)
259}
260
261pub struct CharonCallbacks<'a> {
263 options: &'a CliOpts,
264 emit_artifacts: bool,
267 codegen: bool,
269 error_ctx: Option<ErrorCtx>,
271 transform_ctx: Option<TransformCtx>,
274}
275impl<'a> Callbacks for CharonCallbacks<'a> {
276 fn config(&mut self, config: &mut Config) {
277 setup_compiler(
278 config,
279 self.options,
280 true,
281 true,
282 self.emit_artifacts,
283 self.codegen,
284 );
285 }
286
287 fn after_expansion<'tcx>(&mut self, compiler: &Compiler, tcx: TyCtxt<'tcx>) -> Compilation {
292 rustc_hir::def_id::DEF_ID_DEBUG
294 .swap(&(def_id_debug as fn(_, &mut fmt::Formatter<'_>) -> _));
295
296 if charon_lib::timing::time("rustc-precheck-errors", || precheck_rustc_errors(tcx)) {
297 return Compilation::Continue;
298 }
299
300 self.transform_ctx = charon_lib::timing::time("translate-crate", || {
301 translate_crate::translate(
302 tcx,
303 self.options,
304 self.error_ctx.take().unwrap(),
305 compiler.sess.opts.sysroot.path().to_owned(),
306 )
307 })
308 .ok();
309
310 Compilation::Continue
311 }
312 fn after_analysis<'tcx>(&mut self, _compiler: &Compiler, tcx: TyCtxt<'tcx>) -> Compilation {
313 if !self.emit_artifacts {
314 charon_lib::timing::time("rustc-late-checks", || check_late_rustc_errors(tcx));
315 }
316 Compilation::Continue
317 }
318}
319
320pub struct RunCompilerNormallyCallbacks {
322 is_target_crate: bool,
323}
324
325impl Callbacks for RunCompilerNormallyCallbacks {
326 fn config(&mut self, config: &mut Config) {
327 setup_compiler(
328 config,
329 &Default::default(),
330 self.is_target_crate,
331 false,
332 true,
333 true,
334 );
335 }
336}
337
338fn def_id_debug(def_id: rustc_hir::def_id::DefId, f: &mut fmt::Formatter<'_>) -> fmt::Result {
340 rustc_middle::ty::tls::with_opt(|opt_tcx| {
341 if let Some(tcx) = opt_tcx {
342 let crate_name = if def_id.is_local() {
343 tcx.crate_name(rustc_hir::def_id::LOCAL_CRATE)
344 } else {
345 tcx.cstore_untracked().crate_name(def_id.krate)
346 };
347 write!(
348 f,
349 "{}{}",
350 crate_name,
351 tcx.def_path(def_id).to_string_no_crate_verbose()
352 )?;
353 } else {
354 write!(f, "<can't access `tcx` to print `DefId` path>")?;
355 }
356 Ok(())
357 })
358}