|  | //! cfg and check-cfg configuration | 
|  | //! | 
|  | //! This module contains the definition of [`Cfg`] and [`CheckCfg`] | 
|  | //! as well as the logic for creating the default configuration for a | 
|  | //! given [`Session`]. | 
|  | //! | 
|  | //! It also contains the filling of the well known configs, which should | 
|  | //! ALWAYS be in sync with the default_configuration. | 
|  | //! | 
|  | //! ## Adding a new cfg | 
|  | //! | 
|  | //! Adding a new feature requires two new symbols one for the cfg itself | 
|  | //! and the second one for the unstable feature gate, those are defined in | 
|  | //! `rustc_span::symbol`. | 
|  | //! | 
|  | //! As well as the following points, | 
|  | //!  - Add the activation logic in [`default_configuration`] | 
|  | //!  - Add the cfg to [`CheckCfg::fill_well_known`] (and related files), | 
|  | //!    so that the compiler can know the cfg is expected | 
|  | //!  - Add the cfg in [`disallow_cfgs`] to disallow users from setting it via `--cfg` | 
|  | //!  - Add the feature gating in `compiler/rustc_feature/src/builtin_attrs.rs` | 
|  |  | 
|  | use std::hash::Hash; | 
|  | use std::iter; | 
|  |  | 
|  | use rustc_abi::Align; | 
|  | use rustc_ast::ast; | 
|  | use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; | 
|  | use rustc_lint_defs::BuiltinLintDiag; | 
|  | use rustc_lint_defs::builtin::EXPLICIT_BUILTIN_CFGS_IN_FLAGS; | 
|  | use rustc_span::{Symbol, sym}; | 
|  | use rustc_target::spec::{PanicStrategy, RelocModel, SanitizerSet, Target}; | 
|  |  | 
|  | use crate::Session; | 
|  | use crate::config::{CrateType, FmtDebug}; | 
|  |  | 
|  | /// The parsed `--cfg` options that define the compilation environment of the | 
|  | /// crate, used to drive conditional compilation. | 
|  | /// | 
|  | /// An `FxIndexSet` is used to ensure deterministic ordering of error messages | 
|  | /// relating to `--cfg`. | 
|  | pub type Cfg = FxIndexSet<(Symbol, Option<Symbol>)>; | 
|  |  | 
|  | /// The parsed `--check-cfg` options. | 
|  | #[derive(Default)] | 
|  | pub struct CheckCfg { | 
|  | /// Is well known names activated | 
|  | pub exhaustive_names: bool, | 
|  | /// Is well known values activated | 
|  | pub exhaustive_values: bool, | 
|  | /// All the expected values for a config name | 
|  | pub expecteds: FxHashMap<Symbol, ExpectedValues<Symbol>>, | 
|  | /// Well known names (only used for diagnostics purposes) | 
|  | pub well_known_names: FxHashSet<Symbol>, | 
|  | } | 
|  |  | 
|  | pub enum ExpectedValues<T> { | 
|  | Some(FxHashSet<Option<T>>), | 
|  | Any, | 
|  | } | 
|  |  | 
|  | impl<T: Eq + Hash> ExpectedValues<T> { | 
|  | fn insert(&mut self, value: T) -> bool { | 
|  | match self { | 
|  | ExpectedValues::Some(expecteds) => expecteds.insert(Some(value)), | 
|  | ExpectedValues::Any => false, | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | impl<T: Eq + Hash> Extend<T> for ExpectedValues<T> { | 
|  | fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) { | 
|  | match self { | 
|  | ExpectedValues::Some(expecteds) => expecteds.extend(iter.into_iter().map(Some)), | 
|  | ExpectedValues::Any => {} | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | impl<'a, T: Eq + Hash + Copy + 'a> Extend<&'a T> for ExpectedValues<T> { | 
|  | fn extend<I: IntoIterator<Item = &'a T>>(&mut self, iter: I) { | 
|  | match self { | 
|  | ExpectedValues::Some(expecteds) => expecteds.extend(iter.into_iter().map(|a| Some(*a))), | 
|  | ExpectedValues::Any => {} | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Disallow builtin cfgs from the CLI. | 
|  | pub(crate) fn disallow_cfgs(sess: &Session, user_cfgs: &Cfg) { | 
|  | let disallow = |cfg: &(Symbol, Option<Symbol>), controlled_by| { | 
|  | let cfg_name = cfg.0; | 
|  | let cfg = if let Some(value) = cfg.1 { | 
|  | format!(r#"{}="{}""#, cfg_name, value) | 
|  | } else { | 
|  | format!("{}", cfg_name) | 
|  | }; | 
|  | sess.psess.opt_span_buffer_lint( | 
|  | EXPLICIT_BUILTIN_CFGS_IN_FLAGS, | 
|  | None, | 
|  | ast::CRATE_NODE_ID, | 
|  | BuiltinLintDiag::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by }, | 
|  | ) | 
|  | }; | 
|  |  | 
|  | // We want to restrict setting builtin cfgs that will produce incoherent behavior | 
|  | // between the cfg and the rustc cli flag that sets it. | 
|  | // | 
|  | // The tests are in tests/ui/cfg/disallowed-cli-cfgs.rs. | 
|  |  | 
|  | // By-default all builtin cfgs are disallowed, only those are allowed: | 
|  | //  - test: as it makes sense to the have the `test` cfg active without the builtin | 
|  | //          test harness. See Cargo `harness = false` config. | 
|  | // | 
|  | // Cargo `--cfg test`: https://github.com/rust-lang/cargo/blob/bc89bffa5987d4af8f71011c7557119b39e44a65/src/cargo/core/compiler/mod.rs#L1124 | 
|  |  | 
|  | for cfg in user_cfgs { | 
|  | match cfg { | 
|  | (sym::overflow_checks, None) => disallow(cfg, "-C overflow-checks"), | 
|  | (sym::debug_assertions, None) => disallow(cfg, "-C debug-assertions"), | 
|  | (sym::ub_checks, None) => disallow(cfg, "-Z ub-checks"), | 
|  | (sym::contract_checks, None) => disallow(cfg, "-Z contract-checks"), | 
|  | (sym::sanitize, None | Some(_)) => disallow(cfg, "-Z sanitizer"), | 
|  | ( | 
|  | sym::sanitizer_cfi_generalize_pointers | sym::sanitizer_cfi_normalize_integers, | 
|  | None | Some(_), | 
|  | ) => disallow(cfg, "-Z sanitizer=cfi"), | 
|  | (sym::proc_macro, None) => disallow(cfg, "--crate-type proc-macro"), | 
|  | (sym::panic, Some(sym::abort | sym::unwind)) => disallow(cfg, "-C panic"), | 
|  | (sym::target_feature, Some(_)) => disallow(cfg, "-C target-feature"), | 
|  | (sym::unix, None) | 
|  | | (sym::windows, None) | 
|  | | (sym::relocation_model, Some(_)) | 
|  | | (sym::target_abi, None | Some(_)) | 
|  | | (sym::target_arch, Some(_)) | 
|  | | (sym::target_endian, Some(_)) | 
|  | | (sym::target_env, None | Some(_)) | 
|  | | (sym::target_family, Some(_)) | 
|  | | (sym::target_os, Some(_)) | 
|  | | (sym::target_pointer_width, Some(_)) | 
|  | | (sym::target_vendor, None | Some(_)) | 
|  | | (sym::target_has_atomic, Some(_)) | 
|  | | (sym::target_has_atomic_equal_alignment, Some(_)) | 
|  | | (sym::target_has_atomic_load_store, Some(_)) | 
|  | | (sym::target_has_reliable_f16, None | Some(_)) | 
|  | | (sym::target_has_reliable_f16_math, None | Some(_)) | 
|  | | (sym::target_has_reliable_f128, None | Some(_)) | 
|  | | (sym::target_has_reliable_f128_math, None | Some(_)) | 
|  | | (sym::target_thread_local, None) => disallow(cfg, "--target"), | 
|  | (sym::fmt_debug, None | Some(_)) => disallow(cfg, "-Z fmt-debug"), | 
|  | (sym::emscripten_wasm_eh, None | Some(_)) => disallow(cfg, "-Z emscripten_wasm_eh"), | 
|  | _ => {} | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Generate the default configs for a given session | 
|  | pub(crate) fn default_configuration(sess: &Session) -> Cfg { | 
|  | let mut ret = Cfg::default(); | 
|  |  | 
|  | macro_rules! ins_none { | 
|  | ($key:expr) => { | 
|  | ret.insert(($key, None)); | 
|  | }; | 
|  | } | 
|  | macro_rules! ins_str { | 
|  | ($key:expr, $val_str:expr) => { | 
|  | ret.insert(($key, Some(Symbol::intern($val_str)))); | 
|  | }; | 
|  | } | 
|  | macro_rules! ins_sym { | 
|  | ($key:expr, $val_sym:expr) => { | 
|  | ret.insert(($key, Some($val_sym))); | 
|  | }; | 
|  | } | 
|  |  | 
|  | // Symbols are inserted in alphabetical order as much as possible. | 
|  | // The exceptions are where control flow forces things out of order. | 
|  | // | 
|  | // Run `rustc --print cfg` to see the configuration in practice. | 
|  | // | 
|  | // NOTE: These insertions should be kept in sync with | 
|  | // `CheckCfg::fill_well_known` below. | 
|  |  | 
|  | if sess.opts.debug_assertions { | 
|  | ins_none!(sym::debug_assertions); | 
|  | } | 
|  |  | 
|  | if sess.is_nightly_build() { | 
|  | match sess.opts.unstable_opts.fmt_debug { | 
|  | FmtDebug::Full => { | 
|  | ins_sym!(sym::fmt_debug, sym::full); | 
|  | } | 
|  | FmtDebug::Shallow => { | 
|  | ins_sym!(sym::fmt_debug, sym::shallow); | 
|  | } | 
|  | FmtDebug::None => { | 
|  | ins_sym!(sym::fmt_debug, sym::none); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if sess.overflow_checks() { | 
|  | ins_none!(sym::overflow_checks); | 
|  | } | 
|  |  | 
|  | ins_sym!(sym::panic, sess.panic_strategy().desc_symbol()); | 
|  |  | 
|  | // JUSTIFICATION: before wrapper fn is available | 
|  | #[allow(rustc::bad_opt_access)] | 
|  | if sess.opts.crate_types.contains(&CrateType::ProcMacro) { | 
|  | ins_none!(sym::proc_macro); | 
|  | } | 
|  |  | 
|  | if sess.is_nightly_build() { | 
|  | ins_sym!(sym::relocation_model, sess.target.relocation_model.desc_symbol()); | 
|  | } | 
|  |  | 
|  | for mut s in sess.opts.unstable_opts.sanitizer { | 
|  | // KASAN is still ASAN under the hood, so it uses the same attribute. | 
|  | if s == SanitizerSet::KERNELADDRESS { | 
|  | s = SanitizerSet::ADDRESS; | 
|  | } | 
|  | ins_str!(sym::sanitize, &s.to_string()); | 
|  | } | 
|  |  | 
|  | if sess.is_sanitizer_cfi_generalize_pointers_enabled() { | 
|  | ins_none!(sym::sanitizer_cfi_generalize_pointers); | 
|  | } | 
|  | if sess.is_sanitizer_cfi_normalize_integers_enabled() { | 
|  | ins_none!(sym::sanitizer_cfi_normalize_integers); | 
|  | } | 
|  |  | 
|  | ins_str!(sym::target_abi, &sess.target.abi); | 
|  | ins_str!(sym::target_arch, &sess.target.arch); | 
|  | ins_str!(sym::target_endian, sess.target.endian.as_str()); | 
|  | ins_str!(sym::target_env, &sess.target.env); | 
|  |  | 
|  | for family in sess.target.families.as_ref() { | 
|  | ins_str!(sym::target_family, family); | 
|  | if family == "windows" { | 
|  | ins_none!(sym::windows); | 
|  | } else if family == "unix" { | 
|  | ins_none!(sym::unix); | 
|  | } | 
|  | } | 
|  |  | 
|  | // `target_has_atomic*` | 
|  | let layout = sess.target.parse_data_layout().unwrap_or_else(|err| { | 
|  | sess.dcx().emit_fatal(err); | 
|  | }); | 
|  | let mut has_atomic = false; | 
|  | for (i, align) in [ | 
|  | (8, layout.i8_align.abi), | 
|  | (16, layout.i16_align.abi), | 
|  | (32, layout.i32_align.abi), | 
|  | (64, layout.i64_align.abi), | 
|  | (128, layout.i128_align.abi), | 
|  | ] { | 
|  | if i >= sess.target.min_atomic_width() && i <= sess.target.max_atomic_width() { | 
|  | if !has_atomic { | 
|  | has_atomic = true; | 
|  | if sess.is_nightly_build() { | 
|  | if sess.target.atomic_cas { | 
|  | ins_none!(sym::target_has_atomic); | 
|  | } | 
|  | ins_none!(sym::target_has_atomic_load_store); | 
|  | } | 
|  | } | 
|  | let mut insert_atomic = |sym, align: Align| { | 
|  | if sess.target.atomic_cas { | 
|  | ins_sym!(sym::target_has_atomic, sym); | 
|  | } | 
|  | if align.bits() == i { | 
|  | ins_sym!(sym::target_has_atomic_equal_alignment, sym); | 
|  | } | 
|  | ins_sym!(sym::target_has_atomic_load_store, sym); | 
|  | }; | 
|  | insert_atomic(sym::integer(i), align); | 
|  | if sess.target.pointer_width as u64 == i { | 
|  | insert_atomic(sym::ptr, layout.pointer_align().abi); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | ins_str!(sym::target_os, &sess.target.os); | 
|  | ins_sym!(sym::target_pointer_width, sym::integer(sess.target.pointer_width)); | 
|  |  | 
|  | if sess.opts.unstable_opts.has_thread_local.unwrap_or(sess.target.has_thread_local) { | 
|  | ins_none!(sym::target_thread_local); | 
|  | } | 
|  |  | 
|  | ins_str!(sym::target_vendor, &sess.target.vendor); | 
|  |  | 
|  | // If the user wants a test runner, then add the test cfg. | 
|  | if sess.is_test_crate() { | 
|  | ins_none!(sym::test); | 
|  | } | 
|  |  | 
|  | if sess.ub_checks() { | 
|  | ins_none!(sym::ub_checks); | 
|  | } | 
|  |  | 
|  | // Nightly-only implementation detail for the `panic_unwind` and `unwind` crates. | 
|  | if sess.is_nightly_build() && sess.opts.unstable_opts.emscripten_wasm_eh { | 
|  | ins_none!(sym::emscripten_wasm_eh); | 
|  | } | 
|  |  | 
|  | if sess.contract_checks() { | 
|  | ins_none!(sym::contract_checks); | 
|  | } | 
|  |  | 
|  | ret | 
|  | } | 
|  |  | 
|  | impl CheckCfg { | 
|  | /// Fill the current [`CheckCfg`] with all the well known cfgs | 
|  | pub fn fill_well_known(&mut self, current_target: &Target) { | 
|  | if !self.exhaustive_values && !self.exhaustive_names { | 
|  | return; | 
|  | } | 
|  |  | 
|  | // for `#[cfg(foo)]` (ie. cfg value is none) | 
|  | let no_values = || { | 
|  | let mut values = FxHashSet::default(); | 
|  | values.insert(None); | 
|  | ExpectedValues::Some(values) | 
|  | }; | 
|  |  | 
|  | // preparation for inserting some values | 
|  | let empty_values = || { | 
|  | let values = FxHashSet::default(); | 
|  | ExpectedValues::Some(values) | 
|  | }; | 
|  |  | 
|  | macro_rules! ins { | 
|  | ($name:expr, $values:expr) => {{ | 
|  | self.well_known_names.insert($name); | 
|  | self.expecteds.entry($name).or_insert_with($values) | 
|  | }}; | 
|  | } | 
|  |  | 
|  | // Symbols are inserted in alphabetical order as much as possible. | 
|  | // The exceptions are where control flow forces things out of order. | 
|  | // | 
|  | // NOTE: This should be kept in sync with `default_configuration`. | 
|  | // Note that symbols inserted conditionally in `default_configuration` | 
|  | // are inserted unconditionally here. | 
|  | // | 
|  | // One exception is the `test` cfg which is consider to be a "user-space" | 
|  | // cfg, despite being also set by in `default_configuration` above. | 
|  | // It allows the build system to "deny" using the config by not marking it | 
|  | // as expected (e.g. `lib.test = false` for Cargo). | 
|  | // | 
|  | // When adding a new config here you should also update | 
|  | // `tests/ui/check-cfg/well-known-values.rs` (in order to test the | 
|  | // expected values of the new config) and bless the all directory. | 
|  | // | 
|  | // Don't forget to update `src/doc/rustc/src/check-cfg.md` | 
|  | // in the unstable book as well! | 
|  |  | 
|  | ins!(sym::debug_assertions, no_values); | 
|  |  | 
|  | ins!(sym::fmt_debug, empty_values).extend(FmtDebug::all()); | 
|  |  | 
|  | // These four are never set by rustc, but we set them anyway; they | 
|  | // should not trigger the lint because `cargo clippy`, `cargo doc`, | 
|  | // `cargo test`, `cargo miri run` and `cargo fmt` (respectively) | 
|  | // can set them. | 
|  | ins!(sym::clippy, no_values); | 
|  | ins!(sym::doc, no_values); | 
|  | ins!(sym::doctest, no_values); | 
|  | ins!(sym::miri, no_values); | 
|  | ins!(sym::rustfmt, no_values); | 
|  |  | 
|  | ins!(sym::overflow_checks, no_values); | 
|  |  | 
|  | ins!(sym::panic, empty_values).extend(&PanicStrategy::all()); | 
|  |  | 
|  | ins!(sym::proc_macro, no_values); | 
|  |  | 
|  | ins!(sym::relocation_model, empty_values).extend(RelocModel::all()); | 
|  |  | 
|  | let sanitize_values = SanitizerSet::all() | 
|  | .into_iter() | 
|  | .map(|sanitizer| Symbol::intern(sanitizer.as_str().unwrap())); | 
|  | ins!(sym::sanitize, empty_values).extend(sanitize_values); | 
|  |  | 
|  | ins!(sym::sanitizer_cfi_generalize_pointers, no_values); | 
|  | ins!(sym::sanitizer_cfi_normalize_integers, no_values); | 
|  |  | 
|  | ins!(sym::target_feature, empty_values).extend( | 
|  | rustc_target::target_features::all_rust_features() | 
|  | .filter(|(_, s)| s.in_cfg()) | 
|  | .map(|(f, _s)| f) | 
|  | .chain(rustc_target::target_features::RUSTC_SPECIFIC_FEATURES.iter().cloned()) | 
|  | .map(Symbol::intern), | 
|  | ); | 
|  |  | 
|  | // sym::target_* | 
|  | { | 
|  | const VALUES: [&Symbol; 8] = [ | 
|  | &sym::target_abi, | 
|  | &sym::target_arch, | 
|  | &sym::target_endian, | 
|  | &sym::target_env, | 
|  | &sym::target_family, | 
|  | &sym::target_os, | 
|  | &sym::target_pointer_width, | 
|  | &sym::target_vendor, | 
|  | ]; | 
|  |  | 
|  | // Initialize (if not already initialized) | 
|  | for &e in VALUES { | 
|  | if !self.exhaustive_values { | 
|  | ins!(e, || ExpectedValues::Any); | 
|  | } else { | 
|  | ins!(e, empty_values); | 
|  | } | 
|  | } | 
|  |  | 
|  | if self.exhaustive_values { | 
|  | // Get all values map at once otherwise it would be costly. | 
|  | // (8 values * 220 targets ~= 1760 times, at the time of writing this comment). | 
|  | let [ | 
|  | Some(values_target_abi), | 
|  | Some(values_target_arch), | 
|  | Some(values_target_endian), | 
|  | Some(values_target_env), | 
|  | Some(values_target_family), | 
|  | Some(values_target_os), | 
|  | Some(values_target_pointer_width), | 
|  | Some(values_target_vendor), | 
|  | ] = self.expecteds.get_disjoint_mut(VALUES) | 
|  | else { | 
|  | panic!("unable to get all the check-cfg values buckets"); | 
|  | }; | 
|  |  | 
|  | for target in Target::builtins().chain(iter::once(current_target.clone())) { | 
|  | values_target_abi.insert(Symbol::intern(&target.options.abi)); | 
|  | values_target_arch.insert(Symbol::intern(&target.arch)); | 
|  | values_target_endian.insert(Symbol::intern(target.options.endian.as_str())); | 
|  | values_target_env.insert(Symbol::intern(&target.options.env)); | 
|  | values_target_family.extend( | 
|  | target.options.families.iter().map(|family| Symbol::intern(family)), | 
|  | ); | 
|  | values_target_os.insert(Symbol::intern(&target.options.os)); | 
|  | values_target_pointer_width.insert(sym::integer(target.pointer_width)); | 
|  | values_target_vendor.insert(Symbol::intern(&target.options.vendor)); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | let atomic_values = &[ | 
|  | sym::ptr, | 
|  | sym::integer(8usize), | 
|  | sym::integer(16usize), | 
|  | sym::integer(32usize), | 
|  | sym::integer(64usize), | 
|  | sym::integer(128usize), | 
|  | ]; | 
|  | for sym in [ | 
|  | sym::target_has_atomic, | 
|  | sym::target_has_atomic_equal_alignment, | 
|  | sym::target_has_atomic_load_store, | 
|  | ] { | 
|  | ins!(sym, no_values).extend(atomic_values); | 
|  | } | 
|  |  | 
|  | ins!(sym::target_thread_local, no_values); | 
|  |  | 
|  | ins!(sym::ub_checks, no_values); | 
|  | ins!(sym::contract_checks, no_values); | 
|  |  | 
|  | ins!(sym::unix, no_values); | 
|  | ins!(sym::windows, no_values); | 
|  | } | 
|  | } |