rustc_session/config/
cfg.rs

1//! cfg and check-cfg configuration
2//!
3//! This module contains the definition of [`Cfg`] and [`CheckCfg`]
4//! as well as the logic for creating the default configuration for a
5//! given [`Session`].
6//!
7//! It also contains the filling of the well known configs, which should
8//! ALWAYS be in sync with the default_configuration.
9//!
10//! ## Adding a new cfg
11//!
12//! Adding a new feature requires two new symbols one for the cfg itself
13//! and the second one for the unstable feature gate, those are defined in
14//! `rustc_span::symbol`.
15//!
16//! As well as the following points,
17//!  - Add the activation logic in [`default_configuration`]
18//!  - Add the cfg to [`CheckCfg::fill_well_known`] (and related files),
19//!    so that the compiler can know the cfg is expected
20//!  - Add the cfg in [`disallow_cfgs`] to disallow users from setting it via `--cfg`
21//!  - Add the feature gating in `compiler/rustc_feature/src/builtin_attrs.rs`
22
23use std::hash::Hash;
24use std::iter;
25
26use rustc_abi::Align;
27use rustc_ast::ast;
28use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
29use rustc_lint_defs::BuiltinLintDiag;
30use rustc_lint_defs::builtin::EXPLICIT_BUILTIN_CFGS_IN_FLAGS;
31use rustc_span::{Symbol, sym};
32use rustc_target::spec::{PanicStrategy, RelocModel, SanitizerSet, Target};
33
34use crate::Session;
35use crate::config::{CrateType, FmtDebug};
36
37/// The parsed `--cfg` options that define the compilation environment of the
38/// crate, used to drive conditional compilation.
39///
40/// An `FxIndexSet` is used to ensure deterministic ordering of error messages
41/// relating to `--cfg`.
42pub type Cfg = FxIndexSet<(Symbol, Option<Symbol>)>;
43
44/// The parsed `--check-cfg` options.
45#[derive(Default)]
46pub struct CheckCfg {
47    /// Is well known names activated
48    pub exhaustive_names: bool,
49    /// Is well known values activated
50    pub exhaustive_values: bool,
51    /// All the expected values for a config name
52    pub expecteds: FxHashMap<Symbol, ExpectedValues<Symbol>>,
53    /// Well known names (only used for diagnostics purposes)
54    pub well_known_names: FxHashSet<Symbol>,
55}
56
57pub enum ExpectedValues<T> {
58    Some(FxHashSet<Option<T>>),
59    Any,
60}
61
62impl<T: Eq + Hash> ExpectedValues<T> {
63    fn insert(&mut self, value: T) -> bool {
64        match self {
65            ExpectedValues::Some(expecteds) => expecteds.insert(Some(value)),
66            ExpectedValues::Any => false,
67        }
68    }
69}
70
71impl<T: Eq + Hash> Extend<T> for ExpectedValues<T> {
72    fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
73        match self {
74            ExpectedValues::Some(expecteds) => expecteds.extend(iter.into_iter().map(Some)),
75            ExpectedValues::Any => {}
76        }
77    }
78}
79
80impl<'a, T: Eq + Hash + Copy + 'a> Extend<&'a T> for ExpectedValues<T> {
81    fn extend<I: IntoIterator<Item = &'a T>>(&mut self, iter: I) {
82        match self {
83            ExpectedValues::Some(expecteds) => expecteds.extend(iter.into_iter().map(|a| Some(*a))),
84            ExpectedValues::Any => {}
85        }
86    }
87}
88
89/// Disallow builtin cfgs from the CLI.
90pub(crate) fn disallow_cfgs(sess: &Session, user_cfgs: &Cfg) {
91    let disallow = |cfg: &(Symbol, Option<Symbol>), controlled_by| {
92        let cfg_name = cfg.0;
93        let cfg = if let Some(value) = cfg.1 {
94            format!(r#"{}="{}""#, cfg_name, value)
95        } else {
96            format!("{}", cfg_name)
97        };
98        sess.psess.opt_span_buffer_lint(
99            EXPLICIT_BUILTIN_CFGS_IN_FLAGS,
100            None,
101            ast::CRATE_NODE_ID,
102            BuiltinLintDiag::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by },
103        )
104    };
105
106    // We want to restrict setting builtin cfgs that will produce incoherent behavior
107    // between the cfg and the rustc cli flag that sets it.
108    //
109    // The tests are in tests/ui/cfg/disallowed-cli-cfgs.rs.
110
111    // By-default all builtin cfgs are disallowed, only those are allowed:
112    //  - test: as it makes sense to the have the `test` cfg active without the builtin
113    //          test harness. See Cargo `harness = false` config.
114    //
115    // Cargo `--cfg test`: https://github.com/rust-lang/cargo/blob/bc89bffa5987d4af8f71011c7557119b39e44a65/src/cargo/core/compiler/mod.rs#L1124
116
117    for cfg in user_cfgs {
118        match cfg {
119            (sym::overflow_checks, None) => disallow(cfg, "-C overflow-checks"),
120            (sym::debug_assertions, None) => disallow(cfg, "-C debug-assertions"),
121            (sym::ub_checks, None) => disallow(cfg, "-Z ub-checks"),
122            (sym::contract_checks, None) => disallow(cfg, "-Z contract-checks"),
123            (sym::sanitize, None | Some(_)) => disallow(cfg, "-Z sanitizer"),
124            (
125                sym::sanitizer_cfi_generalize_pointers | sym::sanitizer_cfi_normalize_integers,
126                None | Some(_),
127            ) => disallow(cfg, "-Z sanitizer=cfi"),
128            (sym::proc_macro, None) => disallow(cfg, "--crate-type proc-macro"),
129            (sym::panic, Some(sym::abort | sym::unwind)) => disallow(cfg, "-C panic"),
130            (sym::target_feature, Some(_)) => disallow(cfg, "-C target-feature"),
131            (sym::unix, None)
132            | (sym::windows, None)
133            | (sym::relocation_model, Some(_))
134            | (sym::target_abi, None | Some(_))
135            | (sym::target_arch, Some(_))
136            | (sym::target_endian, Some(_))
137            | (sym::target_env, None | Some(_))
138            | (sym::target_family, Some(_))
139            | (sym::target_os, Some(_))
140            | (sym::target_pointer_width, Some(_))
141            | (sym::target_vendor, None | Some(_))
142            | (sym::target_has_atomic, Some(_))
143            | (sym::target_has_atomic_equal_alignment, Some(_))
144            | (sym::target_has_atomic_load_store, Some(_))
145            | (sym::target_thread_local, None) => disallow(cfg, "--target"),
146            (sym::fmt_debug, None | Some(_)) => disallow(cfg, "-Z fmt-debug"),
147            (sym::emscripten_wasm_eh, None | Some(_)) => disallow(cfg, "-Z emscripten_wasm_eh"),
148            _ => {}
149        }
150    }
151}
152
153/// Generate the default configs for a given session
154pub(crate) fn default_configuration(sess: &Session) -> Cfg {
155    let mut ret = Cfg::default();
156
157    macro_rules! ins_none {
158        ($key:expr) => {
159            ret.insert(($key, None));
160        };
161    }
162    macro_rules! ins_str {
163        ($key:expr, $val_str:expr) => {
164            ret.insert(($key, Some(Symbol::intern($val_str))));
165        };
166    }
167    macro_rules! ins_sym {
168        ($key:expr, $val_sym:expr) => {
169            ret.insert(($key, Some($val_sym)));
170        };
171    }
172
173    // Symbols are inserted in alphabetical order as much as possible.
174    // The exceptions are where control flow forces things out of order.
175    //
176    // Run `rustc --print cfg` to see the configuration in practice.
177    //
178    // NOTE: These insertions should be kept in sync with
179    // `CheckCfg::fill_well_known` below.
180
181    if sess.opts.debug_assertions {
182        ins_none!(sym::debug_assertions);
183    }
184
185    if sess.is_nightly_build() {
186        match sess.opts.unstable_opts.fmt_debug {
187            FmtDebug::Full => {
188                ins_sym!(sym::fmt_debug, sym::full);
189            }
190            FmtDebug::Shallow => {
191                ins_sym!(sym::fmt_debug, sym::shallow);
192            }
193            FmtDebug::None => {
194                ins_sym!(sym::fmt_debug, sym::none);
195            }
196        }
197    }
198
199    if sess.overflow_checks() {
200        ins_none!(sym::overflow_checks);
201    }
202
203    ins_sym!(sym::panic, sess.panic_strategy().desc_symbol());
204
205    // JUSTIFICATION: before wrapper fn is available
206    #[allow(rustc::bad_opt_access)]
207    if sess.opts.crate_types.contains(&CrateType::ProcMacro) {
208        ins_none!(sym::proc_macro);
209    }
210
211    if sess.is_nightly_build() {
212        ins_sym!(sym::relocation_model, sess.target.relocation_model.desc_symbol());
213    }
214
215    for mut s in sess.opts.unstable_opts.sanitizer {
216        // KASAN is still ASAN under the hood, so it uses the same attribute.
217        if s == SanitizerSet::KERNELADDRESS {
218            s = SanitizerSet::ADDRESS;
219        }
220        ins_str!(sym::sanitize, &s.to_string());
221    }
222
223    if sess.is_sanitizer_cfi_generalize_pointers_enabled() {
224        ins_none!(sym::sanitizer_cfi_generalize_pointers);
225    }
226    if sess.is_sanitizer_cfi_normalize_integers_enabled() {
227        ins_none!(sym::sanitizer_cfi_normalize_integers);
228    }
229
230    ins_str!(sym::target_abi, &sess.target.abi);
231    ins_str!(sym::target_arch, &sess.target.arch);
232    ins_str!(sym::target_endian, sess.target.endian.as_str());
233    ins_str!(sym::target_env, &sess.target.env);
234
235    for family in sess.target.families.as_ref() {
236        ins_str!(sym::target_family, family);
237        if family == "windows" {
238            ins_none!(sym::windows);
239        } else if family == "unix" {
240            ins_none!(sym::unix);
241        }
242    }
243
244    // `target_has_atomic*`
245    let layout = sess.target.parse_data_layout().unwrap_or_else(|err| {
246        sess.dcx().emit_fatal(err);
247    });
248    let mut has_atomic = false;
249    for (i, align) in [
250        (8, layout.i8_align.abi),
251        (16, layout.i16_align.abi),
252        (32, layout.i32_align.abi),
253        (64, layout.i64_align.abi),
254        (128, layout.i128_align.abi),
255    ] {
256        if i >= sess.target.min_atomic_width() && i <= sess.target.max_atomic_width() {
257            if !has_atomic {
258                has_atomic = true;
259                if sess.is_nightly_build() {
260                    if sess.target.atomic_cas {
261                        ins_none!(sym::target_has_atomic);
262                    }
263                    ins_none!(sym::target_has_atomic_load_store);
264                }
265            }
266            let mut insert_atomic = |sym, align: Align| {
267                if sess.target.atomic_cas {
268                    ins_sym!(sym::target_has_atomic, sym);
269                }
270                if align.bits() == i {
271                    ins_sym!(sym::target_has_atomic_equal_alignment, sym);
272                }
273                ins_sym!(sym::target_has_atomic_load_store, sym);
274            };
275            insert_atomic(sym::integer(i), align);
276            if sess.target.pointer_width as u64 == i {
277                insert_atomic(sym::ptr, layout.pointer_align.abi);
278            }
279        }
280    }
281
282    ins_str!(sym::target_os, &sess.target.os);
283    ins_sym!(sym::target_pointer_width, sym::integer(sess.target.pointer_width));
284
285    if sess.opts.unstable_opts.has_thread_local.unwrap_or(sess.target.has_thread_local) {
286        ins_none!(sym::target_thread_local);
287    }
288
289    ins_str!(sym::target_vendor, &sess.target.vendor);
290
291    // If the user wants a test runner, then add the test cfg.
292    if sess.is_test_crate() {
293        ins_none!(sym::test);
294    }
295
296    if sess.ub_checks() {
297        ins_none!(sym::ub_checks);
298    }
299
300    // Nightly-only implementation detail for the `panic_unwind` and `unwind` crates.
301    if sess.is_nightly_build() && sess.opts.unstable_opts.emscripten_wasm_eh {
302        ins_none!(sym::emscripten_wasm_eh);
303    }
304
305    if sess.contract_checks() {
306        ins_none!(sym::contract_checks);
307    }
308
309    ret
310}
311
312impl CheckCfg {
313    /// Fill the current [`CheckCfg`] with all the well known cfgs
314    pub fn fill_well_known(&mut self, current_target: &Target) {
315        if !self.exhaustive_values && !self.exhaustive_names {
316            return;
317        }
318
319        // for `#[cfg(foo)]` (ie. cfg value is none)
320        let no_values = || {
321            let mut values = FxHashSet::default();
322            values.insert(None);
323            ExpectedValues::Some(values)
324        };
325
326        // preparation for inserting some values
327        let empty_values = || {
328            let values = FxHashSet::default();
329            ExpectedValues::Some(values)
330        };
331
332        macro_rules! ins {
333            ($name:expr, $values:expr) => {{
334                self.well_known_names.insert($name);
335                self.expecteds.entry($name).or_insert_with($values)
336            }};
337        }
338
339        // Symbols are inserted in alphabetical order as much as possible.
340        // The exceptions are where control flow forces things out of order.
341        //
342        // NOTE: This should be kept in sync with `default_configuration`.
343        // Note that symbols inserted conditionally in `default_configuration`
344        // are inserted unconditionally here.
345        //
346        // One exception is the `test` cfg which is consider to be a "user-space"
347        // cfg, despite being also set by in `default_configuration` above.
348        // It allows the build system to "deny" using the config by not marking it
349        // as expected (e.g. `lib.test = false` for Cargo).
350        //
351        // When adding a new config here you should also update
352        // `tests/ui/check-cfg/well-known-values.rs` (in order to test the
353        // expected values of the new config) and bless the all directory.
354        //
355        // Don't forget to update `src/doc/rustc/src/check-cfg.md`
356        // in the unstable book as well!
357
358        ins!(sym::debug_assertions, no_values);
359
360        ins!(sym::fmt_debug, empty_values).extend(FmtDebug::all());
361
362        // These four are never set by rustc, but we set them anyway; they
363        // should not trigger the lint because `cargo clippy`, `cargo doc`,
364        // `cargo test`, `cargo miri run` and `cargo fmt` (respectively)
365        // can set them.
366        ins!(sym::clippy, no_values);
367        ins!(sym::doc, no_values);
368        ins!(sym::doctest, no_values);
369        ins!(sym::miri, no_values);
370        ins!(sym::rustfmt, no_values);
371
372        ins!(sym::overflow_checks, no_values);
373
374        ins!(sym::panic, empty_values).extend(&PanicStrategy::all());
375
376        ins!(sym::proc_macro, no_values);
377
378        ins!(sym::relocation_model, empty_values).extend(RelocModel::all());
379
380        let sanitize_values = SanitizerSet::all()
381            .into_iter()
382            .map(|sanitizer| Symbol::intern(sanitizer.as_str().unwrap()));
383        ins!(sym::sanitize, empty_values).extend(sanitize_values);
384
385        ins!(sym::sanitizer_cfi_generalize_pointers, no_values);
386        ins!(sym::sanitizer_cfi_normalize_integers, no_values);
387
388        ins!(sym::target_feature, empty_values).extend(
389            rustc_target::target_features::all_rust_features()
390                .filter(|(_, s)| s.in_cfg())
391                .map(|(f, _s)| f)
392                .chain(rustc_target::target_features::RUSTC_SPECIFIC_FEATURES.iter().cloned())
393                .map(Symbol::intern),
394        );
395
396        // sym::target_*
397        {
398            const VALUES: [&Symbol; 8] = [
399                &sym::target_abi,
400                &sym::target_arch,
401                &sym::target_endian,
402                &sym::target_env,
403                &sym::target_family,
404                &sym::target_os,
405                &sym::target_pointer_width,
406                &sym::target_vendor,
407            ];
408
409            // Initialize (if not already initialized)
410            for &e in VALUES {
411                if !self.exhaustive_values {
412                    ins!(e, || ExpectedValues::Any);
413                } else {
414                    ins!(e, empty_values);
415                }
416            }
417
418            if self.exhaustive_values {
419                // Get all values map at once otherwise it would be costly.
420                // (8 values * 220 targets ~= 1760 times, at the time of writing this comment).
421                let [
422                    Some(values_target_abi),
423                    Some(values_target_arch),
424                    Some(values_target_endian),
425                    Some(values_target_env),
426                    Some(values_target_family),
427                    Some(values_target_os),
428                    Some(values_target_pointer_width),
429                    Some(values_target_vendor),
430                ] = self.expecteds.get_disjoint_mut(VALUES)
431                else {
432                    panic!("unable to get all the check-cfg values buckets");
433                };
434
435                for target in Target::builtins().chain(iter::once(current_target.clone())) {
436                    values_target_abi.insert(Symbol::intern(&target.options.abi));
437                    values_target_arch.insert(Symbol::intern(&target.arch));
438                    values_target_endian.insert(Symbol::intern(target.options.endian.as_str()));
439                    values_target_env.insert(Symbol::intern(&target.options.env));
440                    values_target_family.extend(
441                        target.options.families.iter().map(|family| Symbol::intern(family)),
442                    );
443                    values_target_os.insert(Symbol::intern(&target.options.os));
444                    values_target_pointer_width.insert(sym::integer(target.pointer_width));
445                    values_target_vendor.insert(Symbol::intern(&target.options.vendor));
446                }
447            }
448        }
449
450        let atomic_values = &[
451            sym::ptr,
452            sym::integer(8usize),
453            sym::integer(16usize),
454            sym::integer(32usize),
455            sym::integer(64usize),
456            sym::integer(128usize),
457        ];
458        for sym in [
459            sym::target_has_atomic,
460            sym::target_has_atomic_equal_alignment,
461            sym::target_has_atomic_load_store,
462        ] {
463            ins!(sym, no_values).extend(atomic_values);
464        }
465
466        ins!(sym::target_thread_local, no_values);
467
468        ins!(sym::ub_checks, no_values);
469        ins!(sym::contract_checks, no_values);
470
471        ins!(sym::unix, no_values);
472        ins!(sym::windows, no_values);
473    }
474}