]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_interface/src/interface.rs
New upstream version 1.76.0+dfsg1
[rustc.git] / compiler / rustc_interface / src / interface.rs
CommitLineData
dfeec247 1use crate::util;
532ac7d7 2
74b04a01 3use rustc_ast::token;
4b012472 4use rustc_ast::{LitKind, MetaItemKind};
ba9703b0 5use rustc_codegen_ssa::traits::CodegenBackend;
49aad941 6use rustc_data_structures::defer;
dfeec247 7use rustc_data_structures::fx::{FxHashMap, FxHashSet};
ed00b5ec 8use rustc_data_structures::stable_hasher::StableHasher;
532ac7d7 9use rustc_data_structures::sync::Lrc;
60c5eb7d 10use rustc_errors::registry::Registry;
4b012472 11use rustc_errors::{DiagCtxt, ErrorGuaranteed};
dfeec247 12use rustc_lint::LintStore;
4b012472 13use rustc_middle::ty;
781aab86 14use rustc_middle::util::Providers;
a2a8927a 15use rustc_parse::maybe_new_parser_from_source_str;
136023e0 16use rustc_query_impl::QueryCtxt;
49aad941 17use rustc_query_system::query::print_query_stack;
4b012472 18use rustc_session::config::{self, Cfg, CheckCfg, ExpectedValues, Input, OutFileName};
ed00b5ec
FG
19use rustc_session::filesearch::sysroot_candidates;
20use rustc_session::parse::ParseSess;
4b012472 21use rustc_session::{lint, CompilerIO, EarlyDiagCtxt, Session};
ed00b5ec 22use rustc_span::source_map::FileLoader;
5099ac24 23use rustc_span::symbol::sym;
ed00b5ec 24use rustc_span::FileName;
532ac7d7
XL
25use std::path::PathBuf;
26use std::result;
ed00b5ec 27use std::sync::Arc;
532ac7d7 28
5e7ed085 29pub type Result<T> = result::Result<T, ErrorGuaranteed>;
532ac7d7 30
2b03887a
FG
31/// Represents a compiler session. Note that every `Compiler` contains a
32/// `Session`, but `Compiler` also contains some things that cannot be in
33/// `Session`, due to `Session` being in a crate that has many fewer
34/// dependencies than this crate.
fc512014 35///
dfeec247 36/// Can be used to run `rustc_interface` queries.
fc512014 37/// Created by passing [`Config`] to [`run_compiler`].
532ac7d7 38pub struct Compiler {
4b012472
FG
39 pub sess: Session,
40 pub codegen_backend: Box<dyn CodegenBackend>,
781aab86 41 pub(crate) override_queries: Option<fn(&Session, &mut Providers)>,
532ac7d7
XL
42}
43
ed00b5ec 44/// Converts strings provided as `--cfg [cfgspec]` into a `Cfg`.
4b012472 45pub(crate) fn parse_cfg(dcx: &DiagCtxt, cfgs: Vec<String>) -> Cfg {
ed00b5ec
FG
46 cfgs.into_iter()
47 .map(|s| {
5099ac24 48 let sess = ParseSess::with_silent_emitter(Some(format!(
ed00b5ec 49 "this error occurred on the command line: `--cfg={s}`"
5099ac24
FG
50 )));
51 let filename = FileName::cfg_spec_source_code(&s);
52
53 macro_rules! error {
54 ($reason: expr) => {
4b012472
FG
55 #[allow(rustc::untranslatable_diagnostic)]
56 #[allow(rustc::diagnostic_outside_of_impl)]
57 dcx.struct_fatal(format!(
ed00b5ec 58 concat!("invalid `--cfg` argument: `{}` (", $reason, ")"),
fe692bf9 59 s
4b012472
FG
60 ))
61 .emit();
5099ac24
FG
62 };
63 }
64
65 match maybe_new_parser_from_source_str(&sess, filename, s.to_string()) {
5e7ed085 66 Ok(mut parser) => match parser.parse_meta_item() {
5099ac24 67 Ok(meta_item) if parser.token == token::Eof => {
ed00b5ec
FG
68 if meta_item.path.segments.len() != 1 {
69 error!("argument key must be an identifier");
70 }
71 match &meta_item.kind {
72 MetaItemKind::List(..) => {}
73 MetaItemKind::NameValue(lit) if !lit.kind.is_str() => {
74 error!("argument value must be a string");
75 }
76 MetaItemKind::NameValue(..) | MetaItemKind::Word => {
77 let ident = meta_item.ident().expect("multi-segment cfg key");
78 return (ident.name, meta_item.value_str());
79 }
80 }
81 }
82 Ok(..) => {}
83 Err(err) => err.cancel(),
84 },
85 Err(errs) => drop(errs),
86 }
87
88 // If the user tried to use a key="value" flag, but is missing the quotes, provide
89 // a hint about how to resolve this.
90 if s.contains('=') && !s.contains("=\"") && !s.ends_with('"') {
91 error!(concat!(
92 r#"expected `key` or `key="value"`, ensure escaping is appropriate"#,
93 r#" for your shell, try 'key="value"' or key=\"value\""#
94 ));
95 } else {
96 error!(r#"expected `key` or `key="value"`"#);
97 }
98 })
99 .collect::<Cfg>()
100}
101
102/// Converts strings provided as `--check-cfg [specs]` into a `CheckCfg`.
4b012472 103pub(crate) fn parse_check_cfg(dcx: &DiagCtxt, specs: Vec<String>) -> CheckCfg {
ed00b5ec
FG
104 // If any --check-cfg is passed then exhaustive_values and exhaustive_names
105 // are enabled by default.
106 let exhaustive_names = !specs.is_empty();
107 let exhaustive_values = !specs.is_empty();
108 let mut check_cfg = CheckCfg { exhaustive_names, exhaustive_values, ..CheckCfg::default() };
109
ed00b5ec
FG
110 for s in specs {
111 let sess = ParseSess::with_silent_emitter(Some(format!(
112 "this error occurred on the command line: `--check-cfg={s}`"
113 )));
114 let filename = FileName::cfg_spec_source_code(&s);
115
116 macro_rules! error {
117 ($reason:expr) => {
4b012472
FG
118 #[allow(rustc::untranslatable_diagnostic)]
119 #[allow(rustc::diagnostic_outside_of_impl)]
120 dcx.struct_fatal(format!(
ed00b5ec
FG
121 concat!("invalid `--check-cfg` argument: `{}` (", $reason, ")"),
122 s
123 ))
4b012472 124 .emit()
ed00b5ec
FG
125 };
126 }
127
128 let expected_error = || -> ! {
129 error!("expected `cfg(name, values(\"value1\", \"value2\", ... \"valueN\"))`")
130 };
131
132 let Ok(mut parser) = maybe_new_parser_from_source_str(&sess, filename, s.to_string())
133 else {
134 expected_error();
135 };
136
137 let meta_item = match parser.parse_meta_item() {
138 Ok(meta_item) if parser.token == token::Eof => meta_item,
139 Ok(..) => expected_error(),
140 Err(err) => {
141 err.cancel();
142 expected_error();
143 }
144 };
145
146 let Some(args) = meta_item.meta_item_list() else {
147 expected_error();
148 };
149
4b012472
FG
150 if !meta_item.has_name(sym::cfg) {
151 expected_error();
152 }
ed00b5ec 153
4b012472
FG
154 let mut names = Vec::new();
155 let mut values: FxHashSet<_> = Default::default();
ed00b5ec 156
4b012472
FG
157 let mut any_specified = false;
158 let mut values_specified = false;
159 let mut values_any_specified = false;
ed00b5ec 160
4b012472
FG
161 for arg in args {
162 if arg.is_word()
163 && let Some(ident) = arg.ident()
164 {
165 if values_specified {
166 error!("`cfg()` names cannot be after values");
49aad941 167 }
4b012472
FG
168 names.push(ident);
169 } else if arg.has_name(sym::any)
170 && let Some(args) = arg.meta_item_list()
171 {
172 if any_specified {
173 error!("`any()` cannot be specified multiple times");
174 }
175 any_specified = true;
176 if !args.is_empty() {
177 error!("`any()` must be empty");
178 }
179 } else if arg.has_name(sym::values)
180 && let Some(args) = arg.meta_item_list()
181 {
182 if names.is_empty() {
183 error!("`values()` cannot be specified before the names");
184 } else if values_specified {
185 error!("`values()` cannot be specified multiple times");
186 }
187 values_specified = true;
188
189 for arg in args {
190 if let Some(LitKind::Str(s, _)) = arg.lit().map(|lit| &lit.kind) {
191 values.insert(Some(*s));
192 } else if arg.has_name(sym::any)
193 && let Some(args) = arg.meta_item_list()
194 {
195 if values_any_specified {
196 error!("`any()` in `values()` cannot be specified multiple times");
ed00b5ec 197 }
4b012472
FG
198 values_any_specified = true;
199 if !args.is_empty() {
200 error!("`any()` must be empty");
201 }
202 } else {
203 error!("`values()` arguments must be string literals or `any()`");
ed00b5ec 204 }
ed00b5ec 205 }
4b012472
FG
206 } else {
207 error!("`cfg()` arguments must be simple identifiers, `any()` or `values(...)`");
ed00b5ec 208 }
4b012472 209 }
ed00b5ec 210
4b012472
FG
211 if values.is_empty() && !values_any_specified && !any_specified {
212 values.insert(None);
213 } else if !values.is_empty() && values_any_specified {
214 error!(
215 "`values()` arguments cannot specify string literals and `any()` at the same time"
216 );
217 }
ed00b5ec 218
4b012472
FG
219 if any_specified {
220 if names.is_empty() && values.is_empty() && !values_specified && !values_any_specified {
221 check_cfg.exhaustive_names = false;
ed00b5ec 222 } else {
4b012472 223 error!("`cfg(any())` can only be provided in isolation");
ed00b5ec
FG
224 }
225 } else {
4b012472
FG
226 for name in names {
227 check_cfg
228 .expecteds
229 .entry(name.name)
230 .and_modify(|v| match v {
231 ExpectedValues::Some(v) if !values_any_specified => {
232 v.extend(values.clone())
233 }
234 ExpectedValues::Some(_) => *v = ExpectedValues::Any,
235 ExpectedValues::Any => {}
236 })
237 .or_insert_with(|| {
238 if values_any_specified {
239 ExpectedValues::Any
240 } else {
241 ExpectedValues::Some(values.clone())
242 }
243 });
244 }
5099ac24 245 }
ed00b5ec 246 }
5099ac24 247
ed00b5ec 248 check_cfg
5099ac24
FG
249}
250
532ac7d7
XL
251/// The compiler configuration
252pub struct Config {
253 /// Command line options
254 pub opts: config::Options,
255
ed00b5ec
FG
256 /// Unparsed cfg! configuration in addition to the default ones.
257 pub crate_cfg: Vec<String>,
258 pub crate_check_cfg: Vec<String>,
532ac7d7
XL
259
260 pub input: Input,
532ac7d7 261 pub output_dir: Option<PathBuf>,
fe692bf9 262 pub output_file: Option<OutFileName>,
add651ee 263 pub ice_file: Option<PathBuf>,
532ac7d7 264 pub file_loader: Option<Box<dyn FileLoader + Send + Sync>>,
9ffffee4 265 pub locale_resources: &'static [&'static str],
532ac7d7 266
532ac7d7 267 pub lint_caps: FxHashMap<lint::LintId, lint::Level>,
e74abb32 268
6a06907d
XL
269 /// This is a callback from the driver that is called when [`ParseSess`] is created.
270 pub parse_sess_created: Option<Box<dyn FnOnce(&mut ParseSess) + Send>>,
271
ed00b5ec
FG
272 /// This is a callback to hash otherwise untracked state used by the caller, if the
273 /// hash changes between runs the incremental cache will be cleared.
274 ///
275 /// e.g. used by Clippy to hash its config file
276 pub hash_untracked_state: Option<Box<dyn FnOnce(&Session, &mut StableHasher) + Send>>,
277
e74abb32 278 /// This is a callback from the driver that is called when we're registering lints;
ed00b5ec 279 /// it is called during lint loading when we have the LintStore in a non-shared state.
e74abb32
XL
280 ///
281 /// Note that if you find a Some here you probably want to call that function in the new
282 /// function being registered.
dfeec247 283 pub register_lints: Option<Box<dyn Fn(&Session, &mut LintStore) + Send + Sync>>,
60c5eb7d
XL
284
285 /// This is a callback from the driver that is called just after we have populated
286 /// the list of queries.
781aab86 287 pub override_queries: Option<fn(&Session, &mut Providers)>,
60c5eb7d 288
1b1a35ee
XL
289 /// This is a callback from the driver that is called to create a codegen backend.
290 pub make_codegen_backend:
291 Option<Box<dyn FnOnce(&config::Options) -> Box<dyn CodegenBackend> + Send>>,
292
60c5eb7d
XL
293 /// Registry of diagnostics codes.
294 pub registry: Registry,
781aab86 295
ed00b5ec
FG
296 /// The inner atomic value is set to true when a feature marked as `internal` is
297 /// enabled. Makes it so that "please report a bug" is hidden, as ICEs with
298 /// internal features are wontfix, and they are usually the cause of the ICEs.
299 /// None signifies that this is not tracked.
300 pub using_internal_features: Arc<std::sync::atomic::AtomicBool>,
301
781aab86
FG
302 /// All commandline args used to invoke the compiler, with @file args fully expanded.
303 /// This will only be used within debug info, e.g. in the pdb file on windows
304 /// This is mainly useful for other tools that reads that debuginfo to figure out
305 /// how to call the compiler with the same arguments.
306 pub expanded_args: Vec<String>,
532ac7d7
XL
307}
308
064997fb 309// JUSTIFICATION: before session exists, only config
f2b60f7d 310#[allow(rustc::bad_opt_access)]
5099ac24 311pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Send) -> R {
f2b60f7d 312 trace!("run_compiler");
add651ee
FG
313
314 // Set parallel mode before thread pool creation, which will create `Lock`s.
315 rustc_data_structures::sync::set_dyn_thread_safe_mode(config.opts.unstable_opts.threads > 1);
316
4b012472
FG
317 // Check jobserver before run_in_thread_pool_with_globals, which call jobserver::acquire_thread
318 let early_dcx = EarlyDiagCtxt::new(config.opts.error_format);
319 early_dcx.initialize_checked_jobserver();
320
5099ac24 321 util::run_in_thread_pool_with_globals(
dc9dc135 322 config.opts.edition,
064997fb 323 config.opts.unstable_opts.threads,
2b03887a
FG
324 || {
325 crate::callbacks::setup_callbacks();
326
4b012472 327 let early_dcx = EarlyDiagCtxt::new(config.opts.error_format);
fe692bf9 328
ed00b5ec
FG
329 let codegen_backend = if let Some(make_codegen_backend) = config.make_codegen_backend {
330 make_codegen_backend(&config.opts)
331 } else {
332 util::get_codegen_backend(
4b012472 333 &early_dcx,
ed00b5ec
FG
334 &config.opts.maybe_sysroot,
335 config.opts.unstable_opts.codegen_backend.as_deref(),
336 )
337 };
338
9c376795 339 let temps_dir = config.opts.unstable_opts.temps_dir.as_deref().map(PathBuf::from);
ed00b5ec
FG
340
341 let bundle = match rustc_errors::fluent_bundle(
342 config.opts.maybe_sysroot.clone(),
343 sysroot_candidates().to_vec(),
344 config.opts.unstable_opts.translate_lang.clone(),
345 config.opts.unstable_opts.translate_additional_ftl.as_deref(),
346 config.opts.unstable_opts.translate_directionality_markers,
347 ) {
348 Ok(bundle) => bundle,
349 Err(e) => {
4b012472 350 early_dcx.early_error(format!("failed to load fluent bundle: {e}"));
ed00b5ec
FG
351 }
352 };
353
354 let mut locale_resources = Vec::from(config.locale_resources);
355 locale_resources.push(codegen_backend.locale_resource());
356
357 // target_override is documented to be called before init(), so this is okay
358 let target_override = codegen_backend.target_override(&config.opts);
359
360 let mut sess = rustc_session::build_session(
4b012472 361 early_dcx,
2b03887a 362 config.opts,
9c376795
FG
363 CompilerIO {
364 input: config.input,
365 output_dir: config.output_dir,
366 output_file: config.output_file,
367 temps_dir,
368 },
ed00b5ec
FG
369 bundle,
370 config.registry.clone(),
371 locale_resources,
2b03887a 372 config.lint_caps,
ed00b5ec
FG
373 config.file_loader,
374 target_override,
375 util::rustc_version_str().unwrap_or("unknown"),
add651ee 376 config.ice_file,
ed00b5ec 377 config.using_internal_features,
781aab86 378 config.expanded_args,
2b03887a
FG
379 );
380
ed00b5ec
FG
381 codegen_backend.init(&sess);
382
4b012472 383 let cfg = parse_cfg(&sess.dcx(), config.crate_cfg);
ed00b5ec
FG
384 let mut cfg = config::build_configuration(&sess, cfg);
385 util::add_configuration(&mut cfg, &mut sess, &*codegen_backend);
386 sess.parse_sess.config = cfg;
387
4b012472 388 let mut check_cfg = parse_check_cfg(&sess.dcx(), config.crate_check_cfg);
ed00b5ec
FG
389 check_cfg.fill_well_known(&sess.target);
390 sess.parse_sess.check_config = check_cfg;
391
2b03887a
FG
392 if let Some(parse_sess_created) = config.parse_sess_created {
393 parse_sess_created(&mut sess.parse_sess);
394 }
395
ed00b5ec
FG
396 if let Some(hash_untracked_state) = config.hash_untracked_state {
397 let mut hasher = StableHasher::new();
398 hash_untracked_state(&sess, &mut hasher);
399 sess.opts.untracked_state_hash = hasher.finish()
400 }
401
4b012472
FG
402 // Even though the session holds the lint store, we can't build the
403 // lint store until after the session exists. And we wait until now
404 // so that `register_lints` sees the fully initialized session.
405 let mut lint_store = rustc_lint::new_lint_store(sess.enable_internal_lints());
406 if let Some(register_lints) = config.register_lints.as_deref() {
407 register_lints(&sess, &mut lint_store);
408 sess.registered_lints = true;
409 }
410 sess.lint_store = Some(Lrc::new(lint_store));
411
412 let compiler =
413 Compiler { sess, codegen_backend, override_queries: config.override_queries };
2b03887a 414
353b0b11 415 rustc_span::set_source_map(compiler.sess.parse_sess.clone_source_map(), move || {
2b03887a 416 let r = {
49aad941 417 let _sess_abort_error = defer(|| {
ed00b5ec 418 compiler.sess.finish_diagnostics(&config.registry);
2b03887a
FG
419 });
420
421 f(&compiler)
422 };
423
424 let prof = compiler.sess.prof.clone();
fe692bf9 425
2b03887a
FG
426 prof.generic_activity("drop_compiler").run(move || drop(compiler));
427 r
428 })
429 },
532ac7d7
XL
430 )
431}
6a06907d 432
add651ee 433pub fn try_print_query_stack(
4b012472 434 dcx: &DiagCtxt,
add651ee
FG
435 num_frames: Option<usize>,
436 file: Option<std::fs::File>,
437) {
6a06907d
XL
438 eprintln!("query stack during panic:");
439
440 // Be careful relying on global state here: this code is called from
4b012472 441 // a panic hook, which means that the global `DiagCtxt` may be in a weird
6a06907d
XL
442 // state if it was responsible for triggering the panic.
443 let i = ty::tls::with_context_opt(|icx| {
444 if let Some(icx) = icx {
fe692bf9
FG
445 ty::print::with_no_queries!(print_query_stack(
446 QueryCtxt::new(icx.tcx),
447 icx.query,
4b012472 448 dcx,
add651ee
FG
449 num_frames,
450 file,
fe692bf9 451 ))
6a06907d
XL
452 } else {
453 0
454 }
455 });
456
457 if num_frames == None || num_frames >= Some(i) {
458 eprintln!("end of query stack");
459 } else {
460 eprintln!("we're just showing a limited slice of the query stack");
461 }
462}