]>
Commit | Line | Data |
---|---|---|
dfeec247 | 1 | use crate::util; |
532ac7d7 | 2 | |
74b04a01 | 3 | use rustc_ast::token; |
4b012472 | 4 | use rustc_ast::{LitKind, MetaItemKind}; |
ba9703b0 | 5 | use rustc_codegen_ssa::traits::CodegenBackend; |
49aad941 | 6 | use rustc_data_structures::defer; |
dfeec247 | 7 | use rustc_data_structures::fx::{FxHashMap, FxHashSet}; |
ed00b5ec | 8 | use rustc_data_structures::stable_hasher::StableHasher; |
532ac7d7 | 9 | use rustc_data_structures::sync::Lrc; |
60c5eb7d | 10 | use rustc_errors::registry::Registry; |
4b012472 | 11 | use rustc_errors::{DiagCtxt, ErrorGuaranteed}; |
dfeec247 | 12 | use rustc_lint::LintStore; |
4b012472 | 13 | use rustc_middle::ty; |
781aab86 | 14 | use rustc_middle::util::Providers; |
a2a8927a | 15 | use rustc_parse::maybe_new_parser_from_source_str; |
136023e0 | 16 | use rustc_query_impl::QueryCtxt; |
49aad941 | 17 | use rustc_query_system::query::print_query_stack; |
4b012472 | 18 | use rustc_session::config::{self, Cfg, CheckCfg, ExpectedValues, Input, OutFileName}; |
ed00b5ec FG |
19 | use rustc_session::filesearch::sysroot_candidates; |
20 | use rustc_session::parse::ParseSess; | |
4b012472 | 21 | use rustc_session::{lint, CompilerIO, EarlyDiagCtxt, Session}; |
ed00b5ec | 22 | use rustc_span::source_map::FileLoader; |
5099ac24 | 23 | use rustc_span::symbol::sym; |
ed00b5ec | 24 | use rustc_span::FileName; |
532ac7d7 XL |
25 | use std::path::PathBuf; |
26 | use std::result; | |
ed00b5ec | 27 | use std::sync::Arc; |
532ac7d7 | 28 | |
5e7ed085 | 29 | pub 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 | 38 | pub 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 | 45 | pub(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 | 103 | pub(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 |
252 | pub 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 | 311 | pub 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 | 433 | pub 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 | } |