Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve cfg and check-cfg configuration #123519

Merged
merged 2 commits into from
Apr 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
348 changes: 6 additions & 342 deletions compiler/rustc_session/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,16 @@ use crate::search_paths::SearchPath;
use crate::utils::{CanonicalizedPath, NativeLib, NativeLibKind};
use crate::{filesearch, lint, HashStableContext};
use crate::{EarlyDiagCtxt, Session};
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet};
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
use rustc_data_structures::stable_hasher::{StableOrd, ToStableHashKey};
use rustc_errors::emitter::HumanReadableErrorType;
use rustc_errors::{ColorConfig, DiagArgValue, DiagCtxtFlags, IntoDiagArg};
use rustc_feature::UnstableFeatures;
use rustc_span::edition::{Edition, DEFAULT_EDITION, EDITION_NAME_LIST, LATEST_STABLE_EDITION};
use rustc_span::source_map::FilePathMapping;
use rustc_span::symbol::{sym, Symbol};
use rustc_span::{FileName, FileNameDisplayPreference, RealFileName, SourceFileHashAlgorithm};
use rustc_target::abi::Align;
use rustc_target::spec::LinkSelfContainedComponents;
use rustc_target::spec::{PanicStrategy, RelocModel, SanitizerSet, SplitDebuginfo};
use rustc_target::spec::{Target, TargetTriple, TARGETS};
use rustc_target::spec::{SplitDebuginfo, Target, TargetTriple};
use std::collections::btree_map::{
Iter as BTreeMapIter, Keys as BTreeMapKeysIter, Values as BTreeMapValuesIter,
};
Expand All @@ -36,8 +33,11 @@ use std::path::{Path, PathBuf};
use std::str::{self, FromStr};
use std::sync::LazyLock;

mod cfg;
pub mod sigpipe;

pub use cfg::{Cfg, CheckCfg, ExpectedValues};

/// The different settings that the `-C strip` flag can have.
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
pub enum Strip {
Expand Down Expand Up @@ -1201,346 +1201,10 @@ pub(crate) const fn default_lib_output() -> CrateType {
CrateType::Rlib
}

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.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);
}

ret
}

/// 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 => {}
}
}
}

impl CheckCfg {
pub fn fill_well_known(&mut self, current_target: &Target) {
if !self.exhaustive_values && !self.exhaustive_names {
return;
}

let no_values = || {
let mut values = FxHashSet::default();
values.insert(None);
ExpectedValues::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.
//
// When adding a new config here you should also update
// `tests/ui/check-cfg/well-known-values.rs`.
//
// Don't forget to update `src/doc/unstable-book/src/compiler-flags/check-cfg.md`
// in the unstable book as well!

ins!(sym::debug_assertions, no_values);

// These four are never set by rustc, but we set them anyway: they
// should not trigger a lint because `cargo clippy`, `cargo doc`,
// `cargo test` and `cargo miri run` (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::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_known_features()
.map(|(f, _sb)| 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 [
values_target_abi,
values_target_arch,
values_target_endian,
values_target_env,
values_target_family,
values_target_os,
values_target_pointer_width,
values_target_vendor,
] = self
.expecteds
.get_many_mut(VALUES)
.expect("unable to get all the check-cfg values buckets");

for target in TARGETS
.iter()
.map(|target| Target::expect_builtin(&TargetTriple::from_triple(target)))
.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::test, no_values);

ins!(sym::unix, no_values);
ins!(sym::windows, no_values);
}
}

pub fn build_configuration(sess: &Session, mut user_cfg: Cfg) -> Cfg {
// Combine the configuration requested by the session (command line) with
// some default and generated configuration items.
user_cfg.extend(default_configuration(sess));
user_cfg.extend(cfg::default_configuration(sess));
user_cfg
}

Expand Down