diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs index cdc01dc6c91ad..65061059a02e2 100644 --- a/compiler/rustc_attr_data_structures/src/attributes.rs +++ b/compiler/rustc_attr_data_structures/src/attributes.rs @@ -38,7 +38,8 @@ pub enum InstructionSetAttr { ArmT32, } -#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq, HashStable_Generic, Default)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Default, PrintAttribute)] +#[derive(Encodable, Decodable, HashStable_Generic)] pub enum OptimizeAttr { /// No `#[optimize(..)]` attribute #[default] @@ -229,7 +230,8 @@ pub enum AttributeKind { /// Represents `#[rustc_macro_transparency]`. MacroTransparency(Transparency), - + /// Represents `#[optimize(size|speed)]` + Optimize(OptimizeAttr, Span), /// Represents [`#[repr]`](https://doc.rust-lang.org/stable/reference/type-layout.html#representations). Repr(ThinVec<(ReprAttr, Span)>), diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs new file mode 100644 index 0000000000000..ddcf82cbf7cd5 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -0,0 +1,40 @@ +use rustc_attr_data_structures::{AttributeKind, OptimizeAttr}; +use rustc_feature::{AttributeTemplate, template}; +use rustc_span::sym; + +use super::{AttributeOrder, OnDuplicate, SingleAttributeParser}; +use crate::context::{AcceptContext, Stage}; +use crate::parser::ArgParser; + +pub(crate) struct OptimizeParser; + +impl SingleAttributeParser for OptimizeParser { + const PATH: &[rustc_span::Symbol] = &[sym::optimize]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; + const TEMPLATE: AttributeTemplate = template!(List: "size|speed|none"); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + let Some(list) = args.list() else { + cx.expected_list(cx.attr_span); + return None; + }; + + let Some(single) = list.single() else { + cx.expected_single_argument(list.span); + return None; + }; + + let res = match single.meta_item().and_then(|i| i.path().word().map(|i| i.name)) { + Some(sym::size) => OptimizeAttr::Size, + Some(sym::speed) => OptimizeAttr::Speed, + Some(sym::none) => OptimizeAttr::DoNotOptimize, + _ => { + cx.expected_specific_argument(single.span(), vec!["size", "speed", "none"]); + OptimizeAttr::Default + } + }; + + Some(AttributeKind::Optimize(res, cx.attr_span)) + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index fa2a6087506c1..3bb4c163d3268 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -28,6 +28,7 @@ use crate::session_diagnostics::UnusedMultiple; pub(crate) mod allow_unstable; pub(crate) mod cfg; +pub(crate) mod codegen_attrs; pub(crate) mod confusables; pub(crate) mod deprecation; pub(crate) mod inline; diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index d7570634c1f7e..1708fd72e5888 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -15,6 +15,7 @@ use rustc_session::Session; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym}; use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInternalUnstableParser}; +use crate::attributes::codegen_attrs::OptimizeParser; use crate::attributes::confusables::ConfusablesParser; use crate::attributes::deprecation::DeprecationParser; use crate::attributes::inline::{InlineParser, RustcForceInlineParser}; @@ -108,6 +109,7 @@ attribute_parsers!( Single, Single, Single, + Single, Single, Single, // tidy-alphabetical-end diff --git a/compiler/rustc_codegen_gcc/messages.ftl b/compiler/rustc_codegen_gcc/messages.ftl index 18a8a5a1e048a..55a28bc9493e7 100644 --- a/compiler/rustc_codegen_gcc/messages.ftl +++ b/compiler/rustc_codegen_gcc/messages.ftl @@ -1,7 +1,3 @@ -codegen_gcc_unknown_ctarget_feature_prefix = - unknown feature specified for `-Ctarget-feature`: `{$feature}` - .note = features must begin with a `+` to enable or `-` to disable it - codegen_gcc_unwinding_inline_asm = GCC backend does not support unwinding from inline asm @@ -16,15 +12,3 @@ codegen_gcc_lto_disallowed = lto can only be run for executables, cdylibs and st codegen_gcc_lto_dylib = lto cannot be used for `dylib` crate type without `-Zdylib-lto` codegen_gcc_lto_bitcode_from_rlib = failed to get bitcode from object file for LTO ({$gcc_err}) - -codegen_gcc_unknown_ctarget_feature = - unknown and unstable feature specified for `-Ctarget-feature`: `{$feature}` - .note = it is still passed through to the codegen backend, but use of this feature might be unsound and the behavior of this feature can change in the future - .possible_feature = you might have meant: `{$rust_feature}` - .consider_filing_feature_request = consider filing a feature request - -codegen_gcc_missing_features = - add the missing features in a `target_feature` attribute - -codegen_gcc_target_feature_disable_or_enable = - the target features {$features} must all be either enabled or disabled together diff --git a/compiler/rustc_codegen_gcc/src/errors.rs b/compiler/rustc_codegen_gcc/src/errors.rs index 7786be9ae5de9..b7e7343460fbf 100644 --- a/compiler/rustc_codegen_gcc/src/errors.rs +++ b/compiler/rustc_codegen_gcc/src/errors.rs @@ -1,30 +1,6 @@ -use rustc_macros::{Diagnostic, Subdiagnostic}; +use rustc_macros::Diagnostic; use rustc_span::Span; -#[derive(Diagnostic)] -#[diag(codegen_gcc_unknown_ctarget_feature_prefix)] -#[note] -pub(crate) struct UnknownCTargetFeaturePrefix<'a> { - pub feature: &'a str, -} - -#[derive(Diagnostic)] -#[diag(codegen_gcc_unknown_ctarget_feature)] -#[note] -pub(crate) struct UnknownCTargetFeature<'a> { - pub feature: &'a str, - #[subdiagnostic] - pub rust_feature: PossibleFeature<'a>, -} - -#[derive(Subdiagnostic)] -pub(crate) enum PossibleFeature<'a> { - #[help(codegen_gcc_possible_feature)] - Some { rust_feature: &'a str }, - #[help(codegen_gcc_consider_filing_feature_request)] - None, -} - #[derive(Diagnostic)] #[diag(codegen_gcc_unwinding_inline_asm)] pub(crate) struct UnwindingInlineAsm { diff --git a/compiler/rustc_codegen_gcc/src/gcc_util.rs b/compiler/rustc_codegen_gcc/src/gcc_util.rs index 2e00d5fcb612f..42ba40692b75c 100644 --- a/compiler/rustc_codegen_gcc/src/gcc_util.rs +++ b/compiler/rustc_codegen_gcc/src/gcc_util.rs @@ -1,20 +1,12 @@ #[cfg(feature = "master")] use gccjit::Context; -use rustc_codegen_ssa::codegen_attrs::check_tied_features; -use rustc_codegen_ssa::errors::TargetFeatureDisableOrEnable; -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::unord::UnordSet; +use rustc_codegen_ssa::target_features; use rustc_session::Session; -use rustc_session::features::{StabilityExt, retpoline_features_by_flags}; -use rustc_target::target_features::RUSTC_SPECIFIC_FEATURES; use smallvec::{SmallVec, smallvec}; -use crate::errors::{PossibleFeature, UnknownCTargetFeature, UnknownCTargetFeaturePrefix}; - -fn gcc_features_by_flags(sess: &Session) -> Vec<&str> { - let mut features: Vec<&str> = Vec::new(); - retpoline_features_by_flags(sess, &mut features); - features +fn gcc_features_by_flags(sess: &Session, features: &mut Vec) { + target_features::retpoline_features_by_flags(sess, features); + // FIXME: LLVM also sets +reserve-x18 here under some conditions. } /// The list of GCC features computed from CLI flags (`-Ctarget-cpu`, `-Ctarget-feature`, @@ -44,98 +36,29 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec. - all_rust_features.push((false, feature)); - } else if !feature.is_empty() && diagnostics { - sess.dcx().emit_warn(UnknownCTargetFeaturePrefix { feature }); - } - } - // Remove features that are meant for rustc, not codegen. - all_rust_features.retain(|&(_, feature)| { - // Retain if it is not a rustc feature - !RUSTC_SPECIFIC_FEATURES.contains(&feature) - }); - - // Check feature validity. - if diagnostics { - for &(enable, feature) in &all_rust_features { - let feature_state = known_features.iter().find(|&&(v, _, _)| v == feature); - match feature_state { - None => { - let rust_feature = known_features.iter().find_map(|&(rust_feature, _, _)| { - let gcc_features = to_gcc_features(sess, rust_feature); - if gcc_features.contains(&feature) && !gcc_features.contains(&rust_feature) - { - Some(rust_feature) - } else { - None - } - }); - let unknown_feature = if let Some(rust_feature) = rust_feature { - UnknownCTargetFeature { - feature, - rust_feature: PossibleFeature::Some { rust_feature }, - } - } else { - UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None } - }; - sess.dcx().emit_warn(unknown_feature); - } - Some(&(_, stability, _)) => { - stability.verify_feature_enabled_by_flag(sess, enable, feature); - } - } - - // FIXME(nagisa): figure out how to not allocate a full hashset here. - featsmap.insert(feature, enable); - } - } - - // Translate this into GCC features. - let feats = - all_rust_features.iter().flat_map(|&(enable, feature)| { - let enable_disable = if enable { '+' } else { '-' }; + target_features::flag_to_backend_features( + sess, + diagnostics, + |feature| to_gcc_features(sess, feature), + |feature, enable| { // We run through `to_gcc_features` when // passing requests down to GCC. This means that all in-language // features also work on the command line instead of having two // different names when the GCC name and the Rust name differ. - to_gcc_features(sess, feature) - .iter() - .flat_map(|feat| to_gcc_features(sess, feat).into_iter()) - .map(|feature| { - if enable_disable == '-' { - format!("-{}", feature) - } else { - feature.to_string() - } - }) - .collect::>() - }); - features.extend(feats); - - if diagnostics && let Some(f) = check_tied_features(sess, &featsmap) { - sess.dcx().emit_err(TargetFeatureDisableOrEnable { - features: f, - span: None, - missing_features: None, - }); - } + features.extend( + to_gcc_features(sess, feature) + .iter() + .flat_map(|feat| to_gcc_features(sess, feat).into_iter()) + .map( + |feature| { + if !enable { format!("-{}", feature) } else { feature.to_string() } + }, + ), + ); + }, + ); + + gcc_features_by_flags(sess, &mut features); features } diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index aa57655921d44..a912678ef2a10 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -102,6 +102,7 @@ use rustc_codegen_ssa::back::write::{ CodegenContext, FatLtoInput, ModuleConfig, TargetMachineFactoryFn, }; use rustc_codegen_ssa::base::codegen_crate; +use rustc_codegen_ssa::target_features::cfg_target_feature; use rustc_codegen_ssa::traits::{CodegenBackend, ExtraBackendMethods, WriteBackendMethods}; use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleCodegen, TargetConfig}; use rustc_data_structures::fx::FxIndexMap; @@ -476,42 +477,21 @@ fn to_gcc_opt_level(optlevel: Option) -> OptimizationLevel { /// Returns the features that should be set in `cfg(target_feature)`. fn target_config(sess: &Session, target_info: &LockedTargetInfo) -> TargetConfig { - // TODO(antoyo): use global_gcc_features. - let f = |allow_unstable| { - sess.target - .rust_target_features() - .iter() - .filter_map(|&(feature, gate, _)| { - if allow_unstable - || (gate.in_cfg() - && (sess.is_nightly_build() || gate.requires_nightly().is_none())) - { - Some(feature) - } else { - None - } - }) - .filter(|feature| { - // TODO: we disable Neon for now since we don't support the LLVM intrinsics for it. - if *feature == "neon" { - return false; - } - target_info.cpu_supports(feature) - // cSpell:disable - /* - adx, aes, avx, avx2, avx512bf16, avx512bitalg, avx512bw, avx512cd, avx512dq, avx512er, avx512f, avx512fp16, avx512ifma, - avx512pf, avx512vbmi, avx512vbmi2, avx512vl, avx512vnni, avx512vp2intersect, avx512vpopcntdq, - bmi1, bmi2, cmpxchg16b, ermsb, f16c, fma, fxsr, gfni, lzcnt, movbe, pclmulqdq, popcnt, rdrand, rdseed, rtm, - sha, sse, sse2, sse3, sse4.1, sse4.2, sse4a, ssse3, tbm, vaes, vpclmulqdq, xsave, xsavec, xsaveopt, xsaves - */ - // cSpell:enable - }) - .map(Symbol::intern) - .collect() - }; - - let target_features = f(false); - let unstable_target_features = f(true); + let (unstable_target_features, target_features) = cfg_target_feature(sess, |feature| { + // TODO: we disable Neon for now since we don't support the LLVM intrinsics for it. + if feature == "neon" { + return false; + } + target_info.cpu_supports(feature) + // cSpell:disable + /* + adx, aes, avx, avx2, avx512bf16, avx512bitalg, avx512bw, avx512cd, avx512dq, avx512er, avx512f, avx512fp16, avx512ifma, + avx512pf, avx512vbmi, avx512vbmi2, avx512vl, avx512vnni, avx512vp2intersect, avx512vpopcntdq, + bmi1, bmi2, cmpxchg16b, ermsb, f16c, fma, fxsr, gfni, lzcnt, movbe, pclmulqdq, popcnt, rdrand, rdseed, rtm, + sha, sse, sse2, sse3, sse4.1, sse4.2, sse4a, ssse3, tbm, vaes, vpclmulqdq, xsave, xsavec, xsaveopt, xsaves + */ + // cSpell:enable + }); let has_reliable_f16 = target_info.supports_target_dependent_type(CType::Float16); let has_reliable_f128 = target_info.supports_target_dependent_type(CType::Float128); diff --git a/compiler/rustc_codegen_llvm/messages.ftl b/compiler/rustc_codegen_llvm/messages.ftl index 3faeb9b3b221b..3885f18271f1b 100644 --- a/compiler/rustc_codegen_llvm/messages.ftl +++ b/compiler/rustc_codegen_llvm/messages.ftl @@ -59,16 +59,6 @@ codegen_llvm_symbol_already_defined = codegen_llvm_target_machine = could not create LLVM TargetMachine for triple: {$triple} codegen_llvm_target_machine_with_llvm_err = could not create LLVM TargetMachine for triple: {$triple}: {$llvm_err} -codegen_llvm_unknown_ctarget_feature = - unknown and unstable feature specified for `-Ctarget-feature`: `{$feature}` - .note = it is still passed through to the codegen backend, but use of this feature might be unsound and the behavior of this feature can change in the future - .possible_feature = you might have meant: `{$rust_feature}` - .consider_filing_feature_request = consider filing a feature request - -codegen_llvm_unknown_ctarget_feature_prefix = - unknown feature specified for `-Ctarget-feature`: `{$feature}` - .note = features must begin with a `+` to enable or `-` to disable it - codegen_llvm_unknown_debuginfo_compression = unknown debuginfo compression algorithm {$algorithm} - will fall back to uncompressed debuginfo codegen_llvm_write_bytecode = failed to write bytecode to {$path}: {$err} diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs index 8bc74fbec7ece..d50ad8a1a9cb4 100644 --- a/compiler/rustc_codegen_llvm/src/errors.rs +++ b/compiler/rustc_codegen_llvm/src/errors.rs @@ -3,35 +3,11 @@ use std::path::Path; use rustc_data_structures::small_c_str::SmallCStr; use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level}; -use rustc_macros::{Diagnostic, Subdiagnostic}; +use rustc_macros::Diagnostic; use rustc_span::Span; use crate::fluent_generated as fluent; -#[derive(Diagnostic)] -#[diag(codegen_llvm_unknown_ctarget_feature_prefix)] -#[note] -pub(crate) struct UnknownCTargetFeaturePrefix<'a> { - pub feature: &'a str, -} - -#[derive(Diagnostic)] -#[diag(codegen_llvm_unknown_ctarget_feature)] -#[note] -pub(crate) struct UnknownCTargetFeature<'a> { - pub feature: &'a str, - #[subdiagnostic] - pub rust_feature: PossibleFeature<'a>, -} - -#[derive(Subdiagnostic)] -pub(crate) enum PossibleFeature<'a> { - #[help(codegen_llvm_possible_feature)] - Some { rust_feature: &'a str }, - #[help(codegen_llvm_consider_filing_feature_request)] - None, -} - #[derive(Diagnostic)] #[diag(codegen_llvm_symbol_already_defined)] pub(crate) struct SymbolAlreadyDefined<'a> { diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 0e77bc43df805..6fd07d562afd8 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -6,27 +6,20 @@ use std::sync::Once; use std::{ptr, slice, str}; use libc::c_int; -use rustc_codegen_ssa::TargetConfig; use rustc_codegen_ssa::base::wants_wasm_eh; -use rustc_codegen_ssa::codegen_attrs::check_tied_features; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_codegen_ssa::target_features::cfg_target_feature; +use rustc_codegen_ssa::{TargetConfig, target_features}; +use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::small_c_str::SmallCStr; -use rustc_data_structures::unord::UnordSet; use rustc_fs_util::path_to_c_string; use rustc_middle::bug; use rustc_session::Session; use rustc_session::config::{PrintKind, PrintRequest}; -use rustc_session::features::{StabilityExt, retpoline_features_by_flags}; -use rustc_span::Symbol; use rustc_target::spec::{MergeFunctions, PanicStrategy, SmallDataThresholdSupport}; -use rustc_target::target_features::{RUSTC_SPECIAL_FEATURES, RUSTC_SPECIFIC_FEATURES}; use smallvec::{SmallVec, smallvec}; use crate::back::write::create_informational_target_machine; -use crate::errors::{ - FixedX18InvalidArch, PossibleFeature, UnknownCTargetFeature, UnknownCTargetFeaturePrefix, -}; -use crate::llvm; +use crate::{errors, llvm}; static INIT: Once = Once::new(); @@ -195,15 +188,6 @@ impl<'a> LLVMFeature<'a> { ) -> Self { Self { llvm_feature_name, dependencies } } - - fn contains(&'a self, feat: &str) -> bool { - self.iter().any(|dep| dep == feat) - } - - fn iter(&'a self) -> impl Iterator { - let dependencies = self.dependencies.iter().map(|feat| feat.as_str()); - std::iter::once(self.llvm_feature_name).chain(dependencies) - } } impl<'a> IntoIterator for LLVMFeature<'a> { @@ -216,18 +200,22 @@ impl<'a> IntoIterator for LLVMFeature<'a> { } } -// WARNING: the features after applying `to_llvm_features` must be known -// to LLVM or the feature detection code will walk past the end of the feature -// array, leading to crashes. -// -// To find a list of LLVM's names, see llvm-project/llvm/lib/Target/{ARCH}/*.td -// where `{ARCH}` is the architecture name. Look for instances of `SubtargetFeature`. -// -// Check the current rustc fork of LLVM in the repo at https://github.com/rust-lang/llvm-project/. -// The commit in use can be found via the `llvm-project` submodule in -// https://github.com/rust-lang/rust/tree/master/src Though note that Rust can also be build with -// an external precompiled version of LLVM which might lead to failures if the oldest tested / -// supported LLVM version doesn't yet support the relevant intrinsics. +/// Convert a Rust feature name to an LLVM feature name. Returning `None` means the +/// feature should be skipped, usually because it is not supported by the current +/// LLVM version. +/// +/// WARNING: the features after applying `to_llvm_features` must be known +/// to LLVM or the feature detection code will walk past the end of the feature +/// array, leading to crashes. +/// +/// To find a list of LLVM's names, see llvm-project/llvm/lib/Target/{ARCH}/*.td +/// where `{ARCH}` is the architecture name. Look for instances of `SubtargetFeature`. +/// +/// Check the current rustc fork of LLVM in the repo at +/// . The commit in use can be found via the +/// `llvm-project` submodule in Though note that +/// Rust can also be build with an external precompiled version of LLVM which might lead to failures +/// if the oldest tested / supported LLVM version doesn't yet support the relevant intrinsics. pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option> { let arch = if sess.target.arch == "x86_64" { "x86" @@ -343,98 +331,25 @@ pub(crate) fn target_config(sess: &Session) -> TargetConfig { // the target CPU, that is still expanded to target features (with all their implied features) // by LLVM. let target_machine = create_informational_target_machine(sess, true); - // Compute which of the known target features are enabled in the 'base' target machine. We only - // consider "supported" features; "forbidden" features are not reflected in `cfg` as of now. - let mut features: FxHashSet = sess - .target - .rust_target_features() - .iter() - .filter(|(feature, _, _)| { - // skip checking special features, as LLVM may not understand them - if RUSTC_SPECIAL_FEATURES.contains(feature) { - return true; - } - if let Some(feat) = to_llvm_features(sess, feature) { - for llvm_feature in feat { - let cstr = SmallCStr::new(llvm_feature); - // `LLVMRustHasFeature` is moderately expensive. On targets with many - // features (e.g. x86) these calls take a non-trivial fraction of runtime - // when compiling very small programs. - if !unsafe { llvm::LLVMRustHasFeature(target_machine.raw(), cstr.as_ptr()) } { - return false; - } + + let (unstable_target_features, target_features) = cfg_target_feature(sess, |feature| { + if let Some(feat) = to_llvm_features(sess, feature) { + // All the LLVM features this expands to must be enabled. + for llvm_feature in feat { + let cstr = SmallCStr::new(llvm_feature); + // `LLVMRustHasFeature` is moderately expensive. On targets with many + // features (e.g. x86) these calls take a non-trivial fraction of runtime + // when compiling very small programs. + if !unsafe { llvm::LLVMRustHasFeature(target_machine.raw(), cstr.as_ptr()) } { + return false; } - true - } else { - false } - }) - .map(|(feature, _, _)| Symbol::intern(feature)) - .collect(); - - // Add enabled and remove disabled features. - for (enabled, feature) in - sess.opts.cg.target_feature.split(',').filter_map(|s| match s.chars().next() { - Some('+') => Some((true, Symbol::intern(&s[1..]))), - Some('-') => Some((false, Symbol::intern(&s[1..]))), - _ => None, - }) - { - if enabled { - // Also add all transitively implied features. - - // We don't care about the order in `features` since the only thing we use it for is the - // `features.contains` below. - #[allow(rustc::potential_query_instability)] - features.extend( - sess.target - .implied_target_features(feature.as_str()) - .iter() - .map(|s| Symbol::intern(s)), - ); + true } else { - // Remove transitively reverse-implied features. - - // We don't care about the order in `features` since the only thing we use it for is the - // `features.contains` below. - #[allow(rustc::potential_query_instability)] - features.retain(|f| { - if sess.target.implied_target_features(f.as_str()).contains(&feature.as_str()) { - // If `f` if implies `feature`, then `!feature` implies `!f`, so we have to - // remove `f`. (This is the standard logical contraposition principle.) - false - } else { - // We can keep `f`. - true - } - }); + false } - } - - // Filter enabled features based on feature gates. - let f = |allow_unstable| { - sess.target - .rust_target_features() - .iter() - .filter_map(|(feature, gate, _)| { - // The `allow_unstable` set is used by rustc internally to determined which target - // features are truly available, so we want to return even perma-unstable - // "forbidden" features. - if allow_unstable - || (gate.in_cfg() - && (sess.is_nightly_build() || gate.requires_nightly().is_none())) - { - Some(Symbol::intern(feature)) - } else { - None - } - }) - .filter(|feature| features.contains(&feature)) - .collect() - }; + }); - let target_features = f(false); - let unstable_target_features = f(true); let mut cfg = TargetConfig { target_features, unstable_target_features, @@ -707,10 +622,18 @@ pub(crate) fn target_cpu(sess: &Session) -> &str { handle_native(cpu_name) } -fn llvm_features_by_flags(sess: &Session) -> Vec<&str> { - let mut features: Vec<&str> = Vec::new(); - retpoline_features_by_flags(sess, &mut features); - features +/// The target features for compiler flags other than `-Ctarget-features`. +fn llvm_features_by_flags(sess: &Session, features: &mut Vec) { + target_features::retpoline_features_by_flags(sess, features); + + // -Zfixed-x18 + if sess.opts.unstable_opts.fixed_x18 { + if sess.target.arch != "aarch64" { + sess.dcx().emit_fatal(errors::FixedX18InvalidArch { arch: &sess.target.arch }); + } else { + features.push("+reserve-x18".into()); + } + } } /// The list of LLVM features computed from CLI flags (`-Ctarget-cpu`, `-Ctarget-feature`, @@ -777,6 +700,8 @@ pub(crate) fn global_llvm_features( .split(',') .filter(|v| !v.is_empty()) // Drop +v8plus feature introduced in LLVM 20. + // (Hard-coded target features do not go through `to_llvm_feature` since they already + // are LLVM feature names, hence we need a special case here.) .filter(|v| *v != "+v8plus" || get_version() >= (20, 0, 0)) .map(String::from), ); @@ -787,86 +712,23 @@ pub(crate) fn global_llvm_features( // -Ctarget-features if !only_base_features { - let known_features = sess.target.rust_target_features(); - // Will only be filled when `diagnostics` is set! - let mut featsmap = FxHashMap::default(); - - // Compute implied features - let mut all_rust_features = vec![]; - for feature in sess.opts.cg.target_feature.split(',').chain(llvm_features_by_flags(sess)) { - if let Some(feature) = feature.strip_prefix('+') { - all_rust_features.extend( - UnordSet::from(sess.target.implied_target_features(feature)) - .to_sorted_stable_ord() - .iter() - .map(|&&s| (true, s)), - ) - } else if let Some(feature) = feature.strip_prefix('-') { - // FIXME: Why do we not remove implied features on "-" here? - // We do the equivalent above in `target_config`. - // See . - all_rust_features.push((false, feature)); - } else if !feature.is_empty() { - if diagnostics { - sess.dcx().emit_warn(UnknownCTargetFeaturePrefix { feature }); - } - } - } - // Remove features that are meant for rustc, not LLVM. - all_rust_features.retain(|(_, feature)| { - // Retain if it is not a rustc feature - !RUSTC_SPECIFIC_FEATURES.contains(feature) - }); - - // Check feature validity. - if diagnostics { - for &(enable, feature) in &all_rust_features { - let feature_state = known_features.iter().find(|&&(v, _, _)| v == feature); - match feature_state { - None => { - let rust_feature = - known_features.iter().find_map(|&(rust_feature, _, _)| { - let llvm_features = to_llvm_features(sess, rust_feature)?; - if llvm_features.contains(feature) - && !llvm_features.contains(rust_feature) - { - Some(rust_feature) - } else { - None - } - }); - let unknown_feature = if let Some(rust_feature) = rust_feature { - UnknownCTargetFeature { - feature, - rust_feature: PossibleFeature::Some { rust_feature }, - } - } else { - UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None } - }; - sess.dcx().emit_warn(unknown_feature); - } - Some((_, stability, _)) => { - stability.verify_feature_enabled_by_flag(sess, enable, feature); - } - } - - // FIXME(nagisa): figure out how to not allocate a full hashset here. - featsmap.insert(feature, enable); - } - } - - // Translate this into LLVM features. - let feats = all_rust_features - .iter() - .filter_map(|&(enable, feature)| { + target_features::flag_to_backend_features( + sess, + diagnostics, + |feature| { + to_llvm_features(sess, feature) + .map(|f| SmallVec::<[&str; 2]>::from_iter(f.into_iter())) + .unwrap_or_default() + }, + |feature, enable| { let enable_disable = if enable { '+' } else { '-' }; // We run through `to_llvm_features` when // passing requests down to LLVM. This means that all in-language // features also work on the command line instead of having two // different names when the LLVM name and the Rust name differ. - let llvm_feature = to_llvm_features(sess, feature)?; + let Some(llvm_feature) = to_llvm_features(sess, feature) else { return }; - Some( + features.extend( std::iter::once(format!( "{}{}", enable_disable, llvm_feature.llvm_feature_name @@ -881,27 +743,12 @@ pub(crate) fn global_llvm_features( }, )), ) - }) - .flatten(); - features.extend(feats); - - if diagnostics && let Some(f) = check_tied_features(sess, &featsmap) { - sess.dcx().emit_err(rustc_codegen_ssa::errors::TargetFeatureDisableOrEnable { - features: f, - span: None, - missing_features: None, - }); - } + }, + ); } - // -Zfixed-x18 - if sess.opts.unstable_opts.fixed_x18 { - if sess.target.arch != "aarch64" { - sess.dcx().emit_fatal(FixedX18InvalidArch { arch: &sess.target.arch }); - } else { - features.push("+reserve-x18".into()); - } - } + // We add this in the "base target" so that these show up in `sess.unstable_target_features`. + llvm_features_by_flags(sess, &mut features); features } diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 5322fe58cf337..2bd8644e0d7fc 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -48,8 +48,6 @@ codegen_ssa_error_writing_def_file = codegen_ssa_expected_name_value_pair = expected name value pair -codegen_ssa_expected_one_argument = expected one argument - codegen_ssa_expected_used_symbol = expected `used`, `used(compiler)` or `used(linker)` codegen_ssa_extern_funcs_not_found = some `extern` functions couldn't be found; some native libraries may need to be installed or have their path specified @@ -68,6 +66,11 @@ codegen_ssa_failed_to_write = failed to write {$path}: {$error} codegen_ssa_field_associated_value_expected = associated value expected for `{$name}` +codegen_ssa_forbidden_ctarget_feature = + target feature `{$feature}` cannot be {$enabled} with `-Ctarget-feature`: {$reason} + .note = this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +codegen_ssa_forbidden_ctarget_feature_issue = for more information, see issue #116344 + codegen_ssa_forbidden_target_feature_attr = target feature `{$feature}` cannot be enabled with `#[target_feature]`: {$reason} @@ -86,9 +89,6 @@ codegen_ssa_incorrect_cgu_reuse_type = codegen_ssa_insufficient_vs_code_product = VS Code is a different product, and is not sufficient. -codegen_ssa_invalid_argument = invalid argument - .help = valid inline arguments are `always` and `never` - codegen_ssa_invalid_instruction_set = invalid instruction set specified codegen_ssa_invalid_link_ordinal_nargs = incorrect number of arguments to `#[link_ordinal]` @@ -368,8 +368,22 @@ codegen_ssa_unexpected_parameter_name = unexpected parameter name codegen_ssa_unknown_archive_kind = Don't know how to build archive of type: {$kind} +codegen_ssa_unknown_ctarget_feature = + unknown and unstable feature specified for `-Ctarget-feature`: `{$feature}` + .note = it is still passed through to the codegen backend, but use of this feature might be unsound and the behavior of this feature can change in the future + .possible_feature = you might have meant: `{$rust_feature}` + .consider_filing_feature_request = consider filing a feature request + +codegen_ssa_unknown_ctarget_feature_prefix = + unknown feature specified for `-Ctarget-feature`: `{$feature}` + .note = features must begin with a `+` to enable or `-` to disable it + codegen_ssa_unknown_reuse_kind = unknown cgu-reuse-kind `{$kind}` specified +codegen_ssa_unstable_ctarget_feature = + unstable feature specified for `-Ctarget-feature`: `{$feature}` + .note = this feature is not stably supported; its behavior can change in the future + codegen_ssa_unsupported_instruction_set = target does not support `#[instruction_set]` codegen_ssa_unsupported_link_self_contained = option `-C link-self-contained` is not supported on this target diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 98742255063f7..e855f1d355892 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -6,7 +6,6 @@ use rustc_ast::{LitKind, MetaItem, MetaItemInner, attr}; use rustc_attr_data_structures::{ AttributeKind, InlineAttr, InstructionSetAttr, OptimizeAttr, find_attr, }; -use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::weak_lang_items::WEAK_LANG_ITEMS; @@ -18,13 +17,15 @@ use rustc_middle::mir::mono::Linkage; use rustc_middle::query::Providers; use rustc_middle::span_bug; use rustc_middle::ty::{self as ty, TyCtxt}; +use rustc_session::lint; use rustc_session::parse::feature_err; -use rustc_session::{Session, lint}; use rustc_span::{Ident, Span, sym}; use rustc_target::spec::SanitizerSet; use crate::errors; -use crate::target_features::{check_target_feature_trait_unsafe, from_target_feature_attr}; +use crate::target_features::{ + check_target_feature_trait_unsafe, check_tied_features, from_target_feature_attr, +}; fn linkage_by_name(tcx: TyCtxt<'_>, def_id: LocalDefId, name: &str) -> Linkage { use rustc_middle::mir::mono::Linkage::*; @@ -455,33 +456,8 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { codegen_fn_attrs.inline = InlineAttr::Never; } - codegen_fn_attrs.optimize = attrs.iter().fold(OptimizeAttr::Default, |ia, attr| { - if !attr.has_name(sym::optimize) { - return ia; - } - if attr.is_word() { - tcx.dcx().emit_err(errors::ExpectedOneArgumentOptimize { span: attr.span() }); - return ia; - } - let Some(ref items) = attr.meta_item_list() else { - return OptimizeAttr::Default; - }; - - let [item] = &items[..] else { - tcx.dcx().emit_err(errors::ExpectedOneArgumentOptimize { span: attr.span() }); - return OptimizeAttr::Default; - }; - if item.has_name(sym::size) { - OptimizeAttr::Size - } else if item.has_name(sym::speed) { - OptimizeAttr::Speed - } else if item.has_name(sym::none) { - OptimizeAttr::DoNotOptimize - } else { - tcx.dcx().emit_err(errors::InvalidArgumentOptimize { span: item.span() }); - OptimizeAttr::Default - } - }); + codegen_fn_attrs.optimize = + find_attr!(attrs, AttributeKind::Optimize(i, _) => *i).unwrap_or(OptimizeAttr::Default); // #73631: closures inherit `#[target_feature]` annotations // @@ -615,25 +591,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { codegen_fn_attrs } -/// Given a map from target_features to whether they are enabled or disabled, ensure only valid -/// combinations are allowed. -pub fn check_tied_features( - sess: &Session, - features: &FxHashMap<&str, bool>, -) -> Option<&'static [&'static str]> { - if !features.is_empty() { - for tied in sess.target.tied_target_features() { - // Tied features must be set to the same value, or not set at all - let mut tied_iter = tied.iter(); - let enabled = features.get(tied_iter.next().unwrap()); - if tied_iter.any(|f| enabled != features.get(f)) { - return Some(tied); - } - } - } - None -} - /// Checks if the provided DefId is a method in a trait impl for a trait which has track_caller /// applied to the method prototype. fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool { diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index 5387b2a7f8183..72e71b97a1743 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -208,20 +208,6 @@ pub(crate) struct OutOfRangeInteger { pub span: Span, } -#[derive(Diagnostic)] -#[diag(codegen_ssa_expected_one_argument, code = E0722)] -pub(crate) struct ExpectedOneArgumentOptimize { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(codegen_ssa_invalid_argument, code = E0722)] -pub(crate) struct InvalidArgumentOptimize { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] #[diag(codegen_ssa_copy_path_buf)] pub(crate) struct CopyPathBuf { @@ -1217,30 +1203,6 @@ pub(crate) struct ErrorCreatingImportLibrary<'a> { pub error: String, } -pub struct TargetFeatureDisableOrEnable<'a> { - pub features: &'a [&'a str], - pub span: Option, - pub missing_features: Option, -} - -#[derive(Subdiagnostic)] -#[help(codegen_ssa_missing_features)] -pub struct MissingFeatures; - -impl Diagnostic<'_, G> for TargetFeatureDisableOrEnable<'_> { - fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { - let mut diag = Diag::new(dcx, level, fluent::codegen_ssa_target_feature_disable_or_enable); - if let Some(span) = self.span { - diag.span(span); - }; - if let Some(missing_features) = self.missing_features { - diag.subdiagnostic(missing_features); - } - diag.arg("features", self.features.join(", ")); - diag - } -} - #[derive(Diagnostic)] #[diag(codegen_ssa_aix_strip_not_used)] pub(crate) struct AixStripNotUsed; @@ -1283,3 +1245,68 @@ pub(crate) struct XcrunSdkPathWarning { #[derive(LintDiagnostic)] #[diag(codegen_ssa_aarch64_softfloat_neon)] pub(crate) struct Aarch64SoftfloatNeon; + +#[derive(Diagnostic)] +#[diag(codegen_ssa_unknown_ctarget_feature_prefix)] +#[note] +pub(crate) struct UnknownCTargetFeaturePrefix<'a> { + pub feature: &'a str, +} + +#[derive(Subdiagnostic)] +pub(crate) enum PossibleFeature<'a> { + #[help(codegen_ssa_possible_feature)] + Some { rust_feature: &'a str }, + #[help(codegen_ssa_consider_filing_feature_request)] + None, +} + +#[derive(Diagnostic)] +#[diag(codegen_ssa_unknown_ctarget_feature)] +#[note] +pub(crate) struct UnknownCTargetFeature<'a> { + pub feature: &'a str, + #[subdiagnostic] + pub rust_feature: PossibleFeature<'a>, +} + +#[derive(Diagnostic)] +#[diag(codegen_ssa_unstable_ctarget_feature)] +#[note] +pub(crate) struct UnstableCTargetFeature<'a> { + pub feature: &'a str, +} + +#[derive(Diagnostic)] +#[diag(codegen_ssa_forbidden_ctarget_feature)] +#[note] +#[note(codegen_ssa_forbidden_ctarget_feature_issue)] +pub(crate) struct ForbiddenCTargetFeature<'a> { + pub feature: &'a str, + pub enabled: &'a str, + pub reason: &'a str, +} + +pub struct TargetFeatureDisableOrEnable<'a> { + pub features: &'a [&'a str], + pub span: Option, + pub missing_features: Option, +} + +#[derive(Subdiagnostic)] +#[help(codegen_ssa_missing_features)] +pub struct MissingFeatures; + +impl Diagnostic<'_, G> for TargetFeatureDisableOrEnable<'_> { + fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { + let mut diag = Diag::new(dcx, level, fluent::codegen_ssa_target_feature_disable_or_enable); + if let Some(span) = self.span { + diag.span(span); + }; + if let Some(missing_features) = self.missing_features { + diag.subdiagnostic(missing_features); + } + diag.arg("features", self.features.join(", ")); + diag + } +} diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index 640d197c219a8..67ac619091bea 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -1,5 +1,5 @@ use rustc_attr_data_structures::InstructionSetAttr; -use rustc_data_structures::fx::FxIndexSet; +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -8,11 +8,12 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; use rustc_middle::middle::codegen_fn_attrs::TargetFeature; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; -use rustc_session::features::StabilityExt; +use rustc_session::Session; use rustc_session::lint::builtin::AARCH64_SOFTFLOAT_NEON; use rustc_session::parse::feature_err; use rustc_span::{Span, Symbol, sym}; -use rustc_target::target_features::{self, Stability}; +use rustc_target::target_features::{self, RUSTC_SPECIFIC_FEATURES, Stability}; +use smallvec::SmallVec; use crate::errors; @@ -67,7 +68,7 @@ pub(crate) fn from_target_feature_attr( // Only allow target features whose feature gates have been enabled // and which are permitted to be toggled. - if let Err(reason) = stability.is_toggle_permitted(tcx.sess) { + if let Err(reason) = stability.toggle_allowed() { tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr { span: item.span(), feature, @@ -88,7 +89,7 @@ pub(crate) fn from_target_feature_attr( let feature_sym = Symbol::intern(feature); for &name in tcx.implied_target_features(feature_sym) { // But ensure the ABI does not forbid enabling this. - // Here we do assume that LLVM doesn't add even more implied features + // Here we do assume that the backend doesn't add even more implied features // we don't know about, at least no features that would have ABI effects! // We skip this logic in rustdoc, where we want to allow all target features of // all targets, so we can't check their ABI compatibility and anyway we are not @@ -156,6 +157,276 @@ pub(crate) fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId, } } +/// Parse the value of `-Ctarget-feature`, also expanding implied features, +/// and call the closure for each (expanded) Rust feature. If the list contains +/// a syntactically invalid item (not starting with `+`/`-`), the error callback is invoked. +fn parse_rust_feature_flag<'a>( + sess: &'a Session, + err_callback: impl Fn(&'a str), + mut callback: impl FnMut( + /* base_feature */ &'a str, + /* with_implied */ FxHashSet<&'a str>, + /* enable */ bool, + ), +) { + // A cache for the backwards implication map. + let mut inverse_implied_features: Option>> = None; + + for feature in sess.opts.cg.target_feature.split(',') { + if let Some(base_feature) = feature.strip_prefix('+') { + // Skip features that are not target features, but rustc features. + if RUSTC_SPECIFIC_FEATURES.contains(&base_feature) { + return; + } + + callback(base_feature, sess.target.implied_target_features(base_feature), true) + } else if let Some(base_feature) = feature.strip_prefix('-') { + // Skip features that are not target features, but rustc features. + if RUSTC_SPECIFIC_FEATURES.contains(&base_feature) { + return; + } + + // If `f1` implies `f2`, then `!f2` implies `!f1` -- this is standard logical + // contraposition. So we have to find all the reverse implications of `base_feature` and + // disable them, too. + + let inverse_implied_features = inverse_implied_features.get_or_insert_with(|| { + let mut set: FxHashMap<&str, FxHashSet<&str>> = FxHashMap::default(); + for (f, _, is) in sess.target.rust_target_features() { + for i in is.iter() { + set.entry(i).or_default().insert(f); + } + } + set + }); + + // Inverse implied target features have their own inverse implied target features, so we + // traverse the map until there are no more features to add. + let mut features = FxHashSet::default(); + let mut new_features = vec![base_feature]; + while let Some(new_feature) = new_features.pop() { + if features.insert(new_feature) { + if let Some(implied_features) = inverse_implied_features.get(&new_feature) { + new_features.extend(implied_features) + } + } + } + + callback(base_feature, features, false) + } else if !feature.is_empty() { + err_callback(feature) + } + } +} + +/// Utility function for a codegen backend to compute `cfg(target_feature)`, or more specifically, +/// to populate `sess.unstable_target_features` and `sess.target_features` (these are the first and +/// 2nd component of the return value, respectively). +/// +/// `target_base_has_feature` should check whether the given feature (a Rust feature name!) is +/// enabled in the "base" target machine, i.e., without applying `-Ctarget-feature`. +/// +/// We do not have to worry about RUSTC_SPECIFIC_FEATURES here, those are handled elsewhere. +pub fn cfg_target_feature( + sess: &Session, + mut target_base_has_feature: impl FnMut(&str) -> bool, +) -> (Vec, Vec) { + // Compute which of the known target features are enabled in the 'base' target machine. We only + // consider "supported" features; "forbidden" features are not reflected in `cfg` as of now. + let mut features: UnordSet = sess + .target + .rust_target_features() + .iter() + .filter(|(feature, _, _)| target_base_has_feature(feature)) + .map(|(feature, _, _)| Symbol::intern(feature)) + .collect(); + + // Add enabled and remove disabled features. + parse_rust_feature_flag( + sess, + /* err_callback */ + |_| { + // Errors are already emitted in `flag_to_backend_features`; avoid duplicates. + }, + |_base_feature, new_features, enabled| { + // Iteration order is irrelevant since this only influences an `UnordSet`. + #[allow(rustc::potential_query_instability)] + if enabled { + features.extend(new_features.into_iter().map(|f| Symbol::intern(f))); + } else { + // Remove `new_features` from `features`. + for new in new_features { + features.remove(&Symbol::intern(new)); + } + } + }, + ); + + // Filter enabled features based on feature gates. + let f = |allow_unstable| { + sess.target + .rust_target_features() + .iter() + .filter_map(|(feature, gate, _)| { + // The `allow_unstable` set is used by rustc internally to determine which target + // features are truly available, so we want to return even perma-unstable + // "forbidden" features. + if allow_unstable + || (gate.in_cfg() + && (sess.is_nightly_build() || gate.requires_nightly().is_none())) + { + Some(Symbol::intern(feature)) + } else { + None + } + }) + .filter(|feature| features.contains(&feature)) + .collect() + }; + + (f(true), f(false)) +} + +/// Given a map from target_features to whether they are enabled or disabled, ensure only valid +/// combinations are allowed. +pub fn check_tied_features( + sess: &Session, + features: &FxHashMap<&str, bool>, +) -> Option<&'static [&'static str]> { + if !features.is_empty() { + for tied in sess.target.tied_target_features() { + // Tied features must be set to the same value, or not set at all + let mut tied_iter = tied.iter(); + let enabled = features.get(tied_iter.next().unwrap()); + if tied_iter.any(|f| enabled != features.get(f)) { + return Some(tied); + } + } + } + None +} + +/// Translates the `-Ctarget-feature` flag into a backend target feature list. +/// +/// `to_backend_features` converts a Rust feature name into a list of backend feature names; this is +/// used for diagnostic purposes only. +/// +/// `extend_backend_features` extends the set of backend features (assumed to be in mutable state +/// accessible by that closure) to enable/disable the given Rust feature name. +pub fn flag_to_backend_features<'a, const N: usize>( + sess: &'a Session, + diagnostics: bool, + to_backend_features: impl Fn(&'a str) -> SmallVec<[&'a str; N]>, + mut extend_backend_features: impl FnMut(&'a str, /* enable */ bool), +) { + let known_features = sess.target.rust_target_features(); + + // Compute implied features + let mut rust_features = vec![]; + parse_rust_feature_flag( + sess, + /* err_callback */ + |feature| { + if diagnostics { + sess.dcx().emit_warn(errors::UnknownCTargetFeaturePrefix { feature }); + } + }, + |base_feature, new_features, enable| { + rust_features.extend( + UnordSet::from(new_features).to_sorted_stable_ord().iter().map(|&&s| (enable, s)), + ); + // Check feature validity. + if diagnostics { + let feature_state = known_features.iter().find(|&&(v, _, _)| v == base_feature); + match feature_state { + None => { + // This is definitely not a valid Rust feature name. Maybe it is a backend + // feature name? If so, give a better error message. + let rust_feature = + known_features.iter().find_map(|&(rust_feature, _, _)| { + let backend_features = to_backend_features(rust_feature); + if backend_features.contains(&base_feature) + && !backend_features.contains(&rust_feature) + { + Some(rust_feature) + } else { + None + } + }); + let unknown_feature = if let Some(rust_feature) = rust_feature { + errors::UnknownCTargetFeature { + feature: base_feature, + rust_feature: errors::PossibleFeature::Some { rust_feature }, + } + } else { + errors::UnknownCTargetFeature { + feature: base_feature, + rust_feature: errors::PossibleFeature::None, + } + }; + sess.dcx().emit_warn(unknown_feature); + } + Some((_, stability, _)) => { + if let Err(reason) = stability.toggle_allowed() { + sess.dcx().emit_warn(errors::ForbiddenCTargetFeature { + feature: base_feature, + enabled: if enable { "enabled" } else { "disabled" }, + reason, + }); + } else if stability.requires_nightly().is_some() { + // An unstable feature. Warn about using it. It makes little sense + // to hard-error here since we just warn about fully unknown + // features above. + sess.dcx().emit_warn(errors::UnstableCTargetFeature { + feature: base_feature, + }); + } + } + } + } + }, + ); + + if diagnostics { + // FIXME(nagisa): figure out how to not allocate a full hashmap here. + if let Some(f) = check_tied_features( + sess, + &FxHashMap::from_iter(rust_features.iter().map(|&(enable, feature)| (feature, enable))), + ) { + sess.dcx().emit_err(errors::TargetFeatureDisableOrEnable { + features: f, + span: None, + missing_features: None, + }); + } + } + + // Add this to the backend features. + for (enable, feature) in rust_features { + extend_backend_features(feature, enable); + } +} + +/// Computes the backend target features to be added to account for retpoline flags. +/// Used by both LLVM and GCC since their target features are, conveniently, the same. +pub fn retpoline_features_by_flags(sess: &Session, features: &mut Vec) { + // -Zretpoline without -Zretpoline-external-thunk enables + // retpoline-indirect-branches and retpoline-indirect-calls target features + let unstable_opts = &sess.opts.unstable_opts; + if unstable_opts.retpoline && !unstable_opts.retpoline_external_thunk { + features.push("+retpoline-indirect-branches".into()); + features.push("+retpoline-indirect-calls".into()); + } + // -Zretpoline-external-thunk (maybe, with -Zretpoline too) enables + // retpoline-external-thunk, retpoline-indirect-branches and + // retpoline-indirect-calls target features + if unstable_opts.retpoline_external_thunk { + features.push("+retpoline-external-thunk".into()); + features.push("+retpoline-indirect-branches".into()); + features.push("+retpoline-indirect-calls".into()); + } +} + pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { rust_target_features: |tcx, cnum| { @@ -182,7 +453,8 @@ pub(crate) fn provide(providers: &mut Providers) { Stability::Unstable { .. } | Stability::Forbidden { .. }, ) | (Stability::Forbidden { .. }, Stability::Forbidden { .. }) => { - // The stability in the entry is at least as good as the new one, just keep it. + // The stability in the entry is at least as good as the new + // one, just keep it. } _ => { // Overwrite stabilite. diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 4f252f3ccd489..576b174369d46 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -463,12 +463,6 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { ); } - fn crate_inject_span(&self) -> Option { - self.tcx.hir_crate_items(()).definitions().next().and_then(|id| { - self.tcx.crate_level_attribute_injection_span(self.tcx.local_def_id_to_hir_id(id)) - }) - } - /// Check the const stability of the given item (fn or trait). fn check_callee_stability(&mut self, def_id: DefId) { match self.tcx.lookup_const_stability(def_id) { @@ -543,7 +537,6 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { feature, feature_enabled, safe_to_expose_on_stable: callee_safe_to_expose_on_stable, - suggestion_span: self.crate_inject_span(), is_function_call: self.tcx.def_kind(def_id) != DefKind::Trait, }); } @@ -919,7 +912,6 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { name: intrinsic.name, feature, const_stable_indirect: is_const_stable, - suggestion: self.crate_inject_span(), }); } Some(attrs::ConstStability { diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs index 9c30dbff99eb3..887275e7294d2 100644 --- a/compiler/rustc_const_eval/src/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/check_consts/ops.rs @@ -1,8 +1,8 @@ //! Concrete error types for all operations which may be invalid in a certain const context. use hir::{ConstContext, LangItem}; +use rustc_errors::Diag; use rustc_errors::codes::*; -use rustc_errors::{Applicability, Diag}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_infer::infer::TyCtxtInferExt; @@ -384,7 +384,6 @@ pub(crate) struct CallUnstable { /// expose on stable. pub feature_enabled: bool, pub safe_to_expose_on_stable: bool, - pub suggestion_span: Option, /// true if `def_id` is the function we are calling, false if `def_id` is an unstable trait. pub is_function_call: bool, } @@ -412,20 +411,7 @@ impl<'tcx> NonConstOp<'tcx> for CallUnstable { def_path: ccx.tcx.def_path_str(self.def_id), }) }; - // FIXME: make this translatable - let msg = format!("add `#![feature({})]` to the crate attributes to enable", self.feature); - #[allow(rustc::untranslatable_diagnostic)] - if let Some(span) = self.suggestion_span { - err.span_suggestion_verbose( - span, - msg, - format!("#![feature({})]\n", self.feature), - Applicability::MachineApplicable, - ); - } else { - err.help(msg); - } - + ccx.tcx.disabled_nightly_features(&mut err, [(String::new(), self.feature)]); err } } @@ -452,7 +438,6 @@ pub(crate) struct IntrinsicUnstable { pub name: Symbol, pub feature: Symbol, pub const_stable_indirect: bool, - pub suggestion: Option, } impl<'tcx> NonConstOp<'tcx> for IntrinsicUnstable { @@ -472,8 +457,7 @@ impl<'tcx> NonConstOp<'tcx> for IntrinsicUnstable { span, name: self.name, feature: self.feature, - suggestion: self.suggestion, - help: self.suggestion.is_none(), + suggestion: ccx.tcx.crate_level_attribute_injection_span(), }) } } diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index 037cbf777e70b..69c71aef9f337 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -136,9 +136,7 @@ pub(crate) struct UnstableIntrinsic { code = "#![feature({feature})]\n", applicability = "machine-applicable" )] - pub suggestion: Option, - #[help(const_eval_unstable_intrinsic_suggestion)] - pub help: bool, + pub suggestion: Span, } #[derive(Diagnostic)] diff --git a/compiler/rustc_driver_impl/src/pretty.rs b/compiler/rustc_driver_impl/src/pretty.rs index ec77043cd1287..688307a941f30 100644 --- a/compiler/rustc_driver_impl/src/pretty.rs +++ b/compiler/rustc_driver_impl/src/pretty.rs @@ -292,7 +292,11 @@ pub fn print<'tcx>(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) { } HirTree => { debug!("pretty printing HIR tree"); - format!("{:#?}", ex.tcx().hir_crate(())) + ex.tcx() + .hir_crate_items(()) + .owners() + .map(|owner| format!("{:#?} => {:#?}\n", owner, ex.tcx().hir_owner_nodes(owner))) + .collect() } Mir => { let mut out = Vec::new(); diff --git a/compiler/rustc_error_codes/src/error_codes/E0722.md b/compiler/rustc_error_codes/src/error_codes/E0722.md index 570717a92bd79..1799458d46cb0 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0722.md +++ b/compiler/rustc_error_codes/src/error_codes/E0722.md @@ -1,8 +1,14 @@ +#### Note: this error code is no longer emitted by the compiler + +This is because it was too specific to the `optimize` attribute. +Similar diagnostics occur for other attributes too. +The example here will now emit `E0539` + The `optimize` attribute was malformed. Erroneous code example: -```compile_fail,E0722 +```compile_fail,E0539 #![feature(optimize_attribute)] #[optimize(something)] // error: invalid argument diff --git a/compiler/rustc_error_codes/src/lib.rs b/compiler/rustc_error_codes/src/lib.rs index 6f5e4829802e9..22cc1e894da90 100644 --- a/compiler/rustc_error_codes/src/lib.rs +++ b/compiler/rustc_error_codes/src/lib.rs @@ -686,6 +686,7 @@ E0805: 0805, // E0707, // multiple elided lifetimes used in arguments of `async fn` // E0709, // multiple different lifetimes used in arguments of `async fn` // E0721, // `await` keyword +// E0722, // replaced with a generic attribute input check // E0723, // unstable feature in `const` context // E0738, // Removed; errored on `#[track_caller] fn`s in `extern "Rust" { ... }`. // E0744, // merged into E0728 diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index ca24d5a6424d2..bf2d4f662efea 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -311,9 +311,7 @@ fn default_body_is_unstable( reason: reason_str, }); - let inject_span = item_did - .as_local() - .and_then(|id| tcx.crate_level_attribute_injection_span(tcx.local_def_id_to_hir_id(id))); + let inject_span = item_did.is_local().then(|| tcx.crate_level_attribute_injection_span()); rustc_session::parse::add_feature_diagnostics_for_issue( &mut err, &tcx.sess, diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 20d0e87b7a763..13f95024e5a67 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1064,7 +1064,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(), Ok(..) => Some(vec![(adt_const_params_feature_string, sym::adt_const_params)]), }; if let Some(features) = may_suggest_feature { - tcx.disabled_nightly_features(&mut diag, Some(param.hir_id), features); + tcx.disabled_nightly_features(&mut diag, features); } Err(diag.emit()) diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index abb8cdc1cdf3c..5f59b3ad96e04 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -30,8 +30,6 @@ pub(crate) struct BaseExpressionDoubleDot { )] pub default_field_values_suggestion: Option, #[subdiagnostic] - pub default_field_values_help: Option, - #[subdiagnostic] pub add_expr: Option, #[subdiagnostic] pub remove_dots: Option, @@ -61,10 +59,6 @@ pub(crate) struct BaseExpressionDoubleDotAddExpr { pub span: Span, } -#[derive(Subdiagnostic)] -#[help(hir_typeck_base_expression_double_dot_enable_default_field_values)] -pub(crate) struct BaseExpressionDoubleDotEnableDefaultFieldValues; - #[derive(Diagnostic)] #[diag(hir_typeck_field_multiply_specified_in_initializer, code = E0062)] pub(crate) struct FieldMultiplySpecifiedInInitializer { diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 55c39d960e7c2..eca9c5faa3229 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -43,10 +43,9 @@ use crate::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExpectatio use crate::coercion::{CoerceMany, DynamicCoerceMany}; use crate::errors::{ AddressOfTemporaryTaken, BaseExpressionDoubleDot, BaseExpressionDoubleDotAddExpr, - BaseExpressionDoubleDotEnableDefaultFieldValues, BaseExpressionDoubleDotRemove, - CantDereference, FieldMultiplySpecifiedInInitializer, FunctionalRecordUpdateOnNonStruct, - HelpUseLatestEdition, NakedAsmOutsideNakedFn, NoFieldOnType, NoFieldOnVariant, - ReturnLikeStatementKind, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive, + BaseExpressionDoubleDotRemove, CantDereference, FieldMultiplySpecifiedInInitializer, + FunctionalRecordUpdateOnNonStruct, HelpUseLatestEdition, NakedAsmOutsideNakedFn, NoFieldOnType, + NoFieldOnVariant, ReturnLikeStatementKind, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive, TypeMismatchFruTypo, YieldExprOutsideOfCoroutine, }; use crate::{ @@ -2158,7 +2157,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } if !self.tcx.features().default_field_values() { - let sugg = self.tcx.crate_level_attribute_injection_span(expr.hir_id); + let sugg = self.tcx.crate_level_attribute_injection_span(); self.dcx().emit_err(BaseExpressionDoubleDot { span: span.shrink_to_hi(), // We only mention enabling the feature if this is a nightly rustc *and* the @@ -2166,18 +2165,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { default_field_values_suggestion: if self.tcx.sess.is_nightly_build() && missing_mandatory_fields.is_empty() && !missing_optional_fields.is_empty() - && sugg.is_some() { - sugg - } else { - None - }, - default_field_values_help: if self.tcx.sess.is_nightly_build() - && missing_mandatory_fields.is_empty() - && !missing_optional_fields.is_empty() - && sugg.is_none() - { - Some(BaseExpressionDoubleDotEnableDefaultFieldValues) + Some(sugg) } else { None }, diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index a3fdf200c8ea0..589dbb531168b 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -1727,7 +1727,6 @@ impl<'tcx> Pick<'tcx> { } tcx.disabled_nightly_features( lint, - Some(scope_expr_id), self.unstable_candidates.iter().map(|(candidate, feature)| { (format!(" `{}`", tcx.def_path_str(candidate.item.def_id)), *feature) }), diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 02d1ebdb31a9a..70ae9147fb1de 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -1011,8 +1011,8 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { // Prefetch this to prevent multiple threads from blocking on it later. // This is needed since the `hir_id_validator::check_crate` call above is not guaranteed - // to use `hir_crate`. - tcx.ensure_done().hir_crate(()); + // to use `hir_crate_items`. + tcx.ensure_done().hir_crate_items(()); let sess = tcx.sess; sess.time("misc_checking_1", || { diff --git a/compiler/rustc_lint/src/expect.rs b/compiler/rustc_lint/src/expect.rs index 4c2b82a9a23a6..481e116d06e01 100644 --- a/compiler/rustc_lint/src/expect.rs +++ b/compiler/rustc_lint/src/expect.rs @@ -1,5 +1,4 @@ use rustc_data_structures::fx::FxHashSet; -use rustc_hir::CRATE_OWNER_ID; use rustc_middle::lint::LintExpectation; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; @@ -18,7 +17,7 @@ fn lint_expectations(tcx: TyCtxt<'_>, (): ()) -> Vec<(LintExpectationId, LintExp let mut expectations = Vec::new(); - for owner in std::iter::once(CRATE_OWNER_ID).chain(krate.owners()) { + for owner in krate.owners() { let lints = tcx.shallow_lint_levels_on(owner); expectations.extend_from_slice(&lints.expectations); } diff --git a/compiler/rustc_middle/src/hir/map.rs b/compiler/rustc_middle/src/hir/map.rs index e5e1ae508edc0..03bb97095a4bf 100644 --- a/compiler/rustc_middle/src/hir/map.rs +++ b/compiler/rustc_middle/src/hir/map.rs @@ -328,8 +328,7 @@ impl<'tcx> TyCtxt<'tcx> { } /// Returns an iterator of the `DefId`s for all body-owners in this - /// crate. If you would prefer to iterate over the bodies - /// themselves, you can do `self.hir_crate(()).body_ids.iter()`. + /// crate. #[inline] pub fn hir_body_owners(self) -> impl Iterator { self.hir_crate_items(()).body_owners.iter().copied() @@ -396,12 +395,11 @@ impl<'tcx> TyCtxt<'tcx> { where V: Visitor<'tcx>, { - let krate = self.hir_crate(()); - for info in krate.owners.iter() { - if let MaybeOwner::Owner(info) = info { - for attrs in info.attrs.map.values() { - walk_list!(visitor, visit_attribute, *attrs); - } + let krate = self.hir_crate_items(()); + for owner in krate.owners() { + let attrs = self.hir_attr_map(owner); + for attrs in attrs.map.values() { + walk_list!(visitor, visit_attribute, *attrs); } } V::Result::output() @@ -1225,6 +1223,7 @@ pub(super) fn hir_module_items(tcx: TyCtxt<'_>, module_id: LocalModDefId) -> Mod .. } = collector; ModuleItems { + add_root: false, submodules: submodules.into_boxed_slice(), free_items: items.into_boxed_slice(), trait_items: trait_items.into_boxed_slice(), @@ -1260,6 +1259,7 @@ pub(crate) fn hir_crate_items(tcx: TyCtxt<'_>, _: ()) -> ModuleItems { } = collector; ModuleItems { + add_root: true, submodules: submodules.into_boxed_slice(), free_items: items.into_boxed_slice(), trait_items: trait_items.into_boxed_slice(), diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index 9f79ed4b5a52b..d7a8dce0536b2 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -24,6 +24,9 @@ use crate::ty::{EarlyBinder, ImplSubject, TyCtxt}; /// bodies. The Ids are in visitor order. This is used to partition a pass between modules. #[derive(Debug, HashStable, Encodable, Decodable)] pub struct ModuleItems { + /// Whether this represents the whole crate, in which case we need to add `CRATE_OWNER_ID` to + /// the iterators if we want to account for the crate root. + add_root: bool, submodules: Box<[OwnerId]>, free_items: Box<[ItemId]>, trait_items: Box<[TraitItemId]>, @@ -66,9 +69,10 @@ impl ModuleItems { } pub fn owners(&self) -> impl Iterator { - self.free_items - .iter() - .map(|id| id.owner_id) + self.add_root + .then_some(CRATE_OWNER_ID) + .into_iter() + .chain(self.free_items.iter().map(|id| id.owner_id)) .chain(self.trait_items.iter().map(|id| id.owner_id)) .chain(self.impl_items.iter().map(|id| id.owner_id)) .chain(self.foreign_items.iter().map(|id| id.owner_id)) diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index f1395c242f271..82825b9b38ee3 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -2113,7 +2113,7 @@ impl<'tcx> TyCtxt<'tcx> { ) -> &'tcx rustc_hir::def_path_hash_map::DefPathHashMap { // Create a dependency to the crate to be sure we re-execute this when the amount of // definitions change. - self.ensure_ok().hir_crate(()); + self.ensure_ok().hir_crate_items(()); // Freeze definitions once we start iterating on them, to prevent adding new ones // while iterating. If some query needs to add definitions, it should be `ensure`d above. self.untracked.definitions.freeze().def_path_hash_to_def_index_map() @@ -3160,42 +3160,33 @@ impl<'tcx> TyCtxt<'tcx> { lint_level(self.sess, lint, level, Some(span.into()), decorate); } - /// Find the crate root and the appropriate span where `use` and outer attributes can be - /// inserted at. - pub fn crate_level_attribute_injection_span(self, hir_id: HirId) -> Option { - for (_hir_id, node) in self.hir_parent_iter(hir_id) { - if let hir::Node::Crate(m) = node { - return Some(m.spans.inject_use_span.shrink_to_lo()); - } - } - None + /// Find the appropriate span where `use` and outer attributes can be inserted at. + pub fn crate_level_attribute_injection_span(self) -> Span { + let node = self.hir_node(hir::CRATE_HIR_ID); + let hir::Node::Crate(m) = node else { bug!() }; + m.spans.inject_use_span.shrink_to_lo() } pub fn disabled_nightly_features( self, diag: &mut Diag<'_, E>, - hir_id: Option, features: impl IntoIterator, ) { if !self.sess.is_nightly_build() { return; } - let span = hir_id.and_then(|id| self.crate_level_attribute_injection_span(id)); + let span = self.crate_level_attribute_injection_span(); for (desc, feature) in features { // FIXME: make this string translatable let msg = format!("add `#![feature({feature})]` to the crate attributes to enable{desc}"); - if let Some(span) = span { - diag.span_suggestion_verbose( - span, - msg, - format!("#![feature({feature})]\n"), - Applicability::MaybeIncorrect, - ); - } else { - diag.help(msg); - } + diag.span_suggestion_verbose( + span, + msg, + format!("#![feature({feature})]\n"), + Applicability::MaybeIncorrect, + ); } } diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index a12215a44f95d..646e996ff3030 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -291,6 +291,7 @@ fn emit_malformed_attribute( | sym::repr | sym::align | sym::deprecated + | sym::optimize ) { return; } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 50d6c5d9764a5..a5d2ad4dc205b 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -128,6 +128,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { Attribute::Parsed(AttributeKind::Inline(kind, attr_span)) => { self.check_inline(hir_id, *attr_span, span, kind, target) } + Attribute::Parsed(AttributeKind::Optimize(_, attr_span)) => { + self.check_optimize(hir_id, *attr_span, span, target) + } Attribute::Parsed(AttributeKind::AllowInternalUnstable(syms)) => self .check_allow_internal_unstable( hir_id, @@ -167,7 +170,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.check_diagnostic_on_unimplemented(attr.span(), hir_id, target) } [sym::coverage, ..] => self.check_coverage(attr, span, target), - [sym::optimize, ..] => self.check_optimize(hir_id, attr, span, target), [sym::no_sanitize, ..] => { self.check_no_sanitize(attr, span, target) } @@ -529,7 +531,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { /// Checks that `#[optimize(..)]` is applied to a function/closure/method, /// or to an impl block or module. - fn check_optimize(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) { + fn check_optimize(&self, hir_id: HirId, attr_span: Span, span: Span, target: Target) { let is_valid = matches!( target, Target::Fn @@ -538,7 +540,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { ); if !is_valid { self.dcx().emit_err(errors::OptimizeInvalidTarget { - attr_span: attr.span(), + attr_span, defn_span: span, on_crate: hir_id == CRATE_HIR_ID, }); diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl index 61953614c77e1..528c52eace7cb 100644 --- a/compiler/rustc_session/messages.ftl +++ b/compiler/rustc_session/messages.ftl @@ -40,11 +40,6 @@ session_file_is_not_writeable = output file {$file} is not writeable -- check it session_file_write_fail = failed to write `{$path}` due to error `{$err}` -session_forbidden_ctarget_feature = - target feature `{$feature}` cannot be {$enabled} with `-Ctarget-feature`: {$reason} - .note = this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -session_forbidden_ctarget_feature_issue = for more information, see issue #116344 - session_function_return_requires_x86_or_x86_64 = `-Zfunction-return` (except `keep`) is only supported on x86 and x86_64 session_function_return_thunk_extern_requires_non_large_code_model = `-Zfunction-return=thunk-extern` is only supported on non-large code models @@ -137,9 +132,6 @@ session_target_stack_protector_not_supported = `-Z stack-protector={$stack_prote session_unleashed_feature_help_named = skipping check for `{$gate}` feature session_unleashed_feature_help_unnamed = skipping check that does not even have a feature gate -session_unstable_ctarget_feature = - unstable feature specified for `-Ctarget-feature`: `{$feature}` - .note = this feature is not stably supported; its behavior can change in the future session_unstable_virtual_function_elimination = `-Zvirtual-function-elimination` requires `-Clto` session_unsupported_crate_type_for_target = diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index 9c591dcf619a5..bf95014843d23 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -501,20 +501,3 @@ pub(crate) struct SoftFloatIgnored; #[note] #[note(session_soft_float_deprecated_issue)] pub(crate) struct SoftFloatDeprecated; - -#[derive(Diagnostic)] -#[diag(session_forbidden_ctarget_feature)] -#[note] -#[note(session_forbidden_ctarget_feature_issue)] -pub(crate) struct ForbiddenCTargetFeature<'a> { - pub feature: &'a str, - pub enabled: &'a str, - pub reason: &'a str, -} - -#[derive(Diagnostic)] -#[diag(session_unstable_ctarget_feature)] -#[note] -pub(crate) struct UnstableCTargetFeature<'a> { - pub feature: &'a str, -} diff --git a/compiler/rustc_session/src/features.rs b/compiler/rustc_session/src/features.rs deleted file mode 100644 index 70a088a236f7e..0000000000000 --- a/compiler/rustc_session/src/features.rs +++ /dev/null @@ -1,59 +0,0 @@ -use rustc_target::target_features::Stability; - -use crate::Session; -use crate::errors::{ForbiddenCTargetFeature, UnstableCTargetFeature}; - -pub trait StabilityExt { - /// Returns whether the feature may be toggled via `#[target_feature]` or `-Ctarget-feature`. - /// Otherwise, some features also may only be enabled by flag (target modifier). - /// (It might still be nightly-only even if this returns `true`, so make sure to also check - /// `requires_nightly`.) - fn is_toggle_permitted(&self, sess: &Session) -> Result<(), &'static str>; - - /// Check that feature is correctly enabled/disabled by command line flag (emits warnings) - fn verify_feature_enabled_by_flag(&self, sess: &Session, enable: bool, feature: &str); -} - -impl StabilityExt for Stability { - fn is_toggle_permitted(&self, sess: &Session) -> Result<(), &'static str> { - match self { - Stability::Forbidden { reason } => Err(reason), - Stability::TargetModifierOnly { reason, flag } => { - if !sess.opts.target_feature_flag_enabled(*flag) { Err(reason) } else { Ok(()) } - } - _ => Ok(()), - } - } - fn verify_feature_enabled_by_flag(&self, sess: &Session, enable: bool, feature: &str) { - if let Err(reason) = self.is_toggle_permitted(sess) { - sess.dcx().emit_warn(ForbiddenCTargetFeature { - feature, - enabled: if enable { "enabled" } else { "disabled" }, - reason, - }); - } else if self.requires_nightly().is_some() { - // An unstable feature. Warn about using it. It makes little sense - // to hard-error here since we just warn about fully unknown - // features above. - sess.dcx().emit_warn(UnstableCTargetFeature { feature }); - } - } -} - -pub fn retpoline_features_by_flags(sess: &Session, features: &mut Vec<&str>) { - // -Zretpoline without -Zretpoline-external-thunk enables - // retpoline-indirect-branches and retpoline-indirect-calls target features - let unstable_opts = &sess.opts.unstable_opts; - if unstable_opts.retpoline && !unstable_opts.retpoline_external_thunk { - features.push("+retpoline-indirect-branches"); - features.push("+retpoline-indirect-calls"); - } - // -Zretpoline-external-thunk (maybe, with -Zretpoline too) enables - // retpoline-external-thunk, retpoline-indirect-branches and - // retpoline-indirect-calls target features - if unstable_opts.retpoline_external_thunk { - features.push("+retpoline-external-thunk"); - features.push("+retpoline-indirect-branches"); - features.push("+retpoline-indirect-calls"); - } -} diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs index 4added19e56e7..5e5872ee06815 100644 --- a/compiler/rustc_session/src/lib.rs +++ b/compiler/rustc_session/src/lib.rs @@ -29,7 +29,6 @@ pub use session::*; pub mod output; pub use getopts; -pub mod features; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 31b4237d3b2ed..7fef942525b96 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -290,14 +290,6 @@ macro_rules! top_level_options { mods.sort_by(|a, b| a.opt.cmp(&b.opt)); mods } - - pub fn target_feature_flag_enabled(&self, flag: &str) -> bool { - match flag { - "retpoline" => self.unstable_opts.retpoline, - "retpoline-external-thunk" => self.unstable_opts.retpoline_external_thunk, - _ => false, - } - } } ); } diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index a1eac1fba25da..3eea1e070a669 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -11,11 +11,6 @@ use crate::spec::{FloatAbi, RustcAbi, Target}; /// These exist globally and are not in the target-specific lists below. pub const RUSTC_SPECIFIC_FEATURES: &[&str] = &["crt-static"]; -/// Features that require special handling when passing to LLVM: -/// these are target-specific (i.e., must also be listed in the target-specific list below) -/// but do not correspond to an LLVM target feature. -pub const RUSTC_SPECIAL_FEATURES: &[&str] = &["backchain"]; - /// Stability information for target features. #[derive(Debug, Copy, Clone)] pub enum Stability { @@ -34,9 +29,6 @@ pub enum Stability { /// particular for features are actually ABI configuration flags (not all targets are as nice as /// RISC-V and have an explicit way to set the ABI separate from target features). Forbidden { reason: &'static str }, - /// This feature can not be set via `-Ctarget-feature` or `#[target_feature]`, it can only be set - /// by target modifier flag. Target modifier flags are tracked to be consistent in linked modules. - TargetModifierOnly { reason: &'static str, flag: &'static str }, } use Stability::*; @@ -52,7 +44,6 @@ impl HashStable for Stability { Stability::Forbidden { reason } => { reason.hash_stable(hcx, hasher); } - Stability::TargetModifierOnly { .. } => {} } } } @@ -62,7 +53,7 @@ impl Stability { /// (It might still be nightly-only even if this returns `true`, so make sure to also check /// `requires_nightly`.) pub fn in_cfg(&self) -> bool { - !matches!(self, Stability::Forbidden { .. }) + matches!(self, Stability::Stable | Stability::Unstable { .. }) } /// Returns the nightly feature that is required to toggle this target feature via @@ -78,7 +69,16 @@ impl Stability { Stability::Unstable(nightly_feature) => Some(nightly_feature), Stability::Stable { .. } => None, Stability::Forbidden { .. } => panic!("forbidden features should not reach this far"), - Stability::TargetModifierOnly { .. } => None, + } + } + + /// Returns whether the feature may be toggled via `#[target_feature]` or `-Ctarget-feature`. + /// (It might still be nightly-only even if this returns `true`, so make sure to also check + /// `requires_nightly`.) + pub fn toggle_allowed(&self) -> Result<(), &'static str> { + match self { + Stability::Unstable(_) | Stability::Stable { .. } => Ok(()), + Stability::Forbidden { reason } => Err(reason), } } } @@ -270,12 +270,7 @@ static AARCH64_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("rcpc3", Unstable(sym::aarch64_unstable_target_feature), &["rcpc2"]), // FEAT_RDM ("rdm", Stable, &["neon"]), - // This is needed for inline assembly, but shouldn't be stabilized as-is - // since it should be enabled globally using -Zfixed-x18, not - // #[target_feature]. - // Note that cfg(target_feature = "reserve-x18") is currently not set for - // targets that reserve x18 by default. - ("reserve-x18", Unstable(sym::aarch64_unstable_target_feature), &[]), + ("reserve-x18", Forbidden { reason: "use `-Zfixed-x18` compiler flag instead" }, &[]), // FEAT_SB ("sb", Stable, &[]), // FEAT_SHA1 & FEAT_SHA256 @@ -450,26 +445,17 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("rdseed", Stable, &[]), ( "retpoline-external-thunk", - Stability::TargetModifierOnly { - reason: "use `retpoline-external-thunk` target modifier flag instead", - flag: "retpoline-external-thunk", - }, + Stability::Forbidden { reason: "use `-Zretpoline-external-thunk` compiler flag instead" }, &[], ), ( "retpoline-indirect-branches", - Stability::TargetModifierOnly { - reason: "use `retpoline` target modifier flag instead", - flag: "retpoline", - }, + Stability::Forbidden { reason: "use `-Zretpoline` compiler flag instead" }, &[], ), ( "retpoline-indirect-calls", - Stability::TargetModifierOnly { - reason: "use `retpoline` target modifier flag instead", - flag: "retpoline", - }, + Stability::Forbidden { reason: "use `-Zretpoline` compiler flag instead" }, &[], ), ("rtm", Unstable(sym::rtm_target_feature), &[]), @@ -732,6 +718,7 @@ static LOONGARCH_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ #[rustfmt::skip] const IBMZ_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start + // For "backchain", https://github.com/rust-lang/rust/issues/142412 is a stabilization blocker ("backchain", Unstable(sym::s390x_target_feature), &[]), ("concurrent-functions", Unstable(sym::s390x_target_feature), &[]), ("deflate-conversion", Unstable(sym::s390x_target_feature), &[]), diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index ee5a5b247ce8c..6d07ae021ae9d 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -3575,11 +3575,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } ObligationCauseCode::TrivialBound => { err.help("see issue #48214"); - tcx.disabled_nightly_features( - err, - Some(tcx.local_def_id_to_hir_id(body_id)), - [(String::new(), sym::trivial_bounds)], - ); + tcx.disabled_nightly_features(err, [(String::new(), sym::trivial_bounds)]); } ObligationCauseCode::OpaqueReturnType(expr_info) => { let (expr_ty, expr) = if let Some((expr_ty, hir_id)) = expr_info { diff --git a/library/Cargo.lock b/library/Cargo.lock index 34012d6943b79..4f0e7aed217c0 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -219,21 +219,19 @@ dependencies = [ [[package]] name = "r-efi" -version = "5.2.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" dependencies = [ - "compiler_builtins", "rustc-std-workspace-core", ] [[package]] name = "r-efi-alloc" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e43c53ff1a01d423d1cb762fd991de07d32965ff0ca2e4f80444ac7804198203" +checksum = "dc2f58ef3ca9bb0f9c44d9aa8537601bcd3df94cc9314a40178cadf7d4466354" dependencies = [ - "compiler_builtins", "r-efi", "rustc-std-workspace-core", ] diff --git a/library/alloc/src/collections/linked_list/tests.rs b/library/alloc/src/collections/linked_list/tests.rs index 410e67d3fdb08..3d6c740e80b3b 100644 --- a/library/alloc/src/collections/linked_list/tests.rs +++ b/library/alloc/src/collections/linked_list/tests.rs @@ -1,4 +1,3 @@ -use std::cell::Cell; use std::panic::{AssertUnwindSafe, catch_unwind}; use std::thread; @@ -6,6 +5,7 @@ use rand::RngCore; use super::*; use crate::testing::crash_test::{CrashTestDummy, Panic}; +use crate::testing::macros::struct_with_counted_drop; use crate::vec::Vec; #[test] @@ -1010,22 +1010,6 @@ fn extract_if_drop_panic_leak() { assert_eq!(d7.dropped(), 1); } -macro_rules! struct_with_counted_drop { - ($struct_name:ident$(($elt_ty:ty))?, $drop_counter:ident $(=> $drop_stmt:expr)?) => { - thread_local! {static $drop_counter: Cell = Cell::new(0);} - - struct $struct_name$(($elt_ty))?; - - impl Drop for $struct_name { - fn drop(&mut self) { - $drop_counter.set($drop_counter.get() + 1); - - $($drop_stmt(self))? - } - } - }; -} - #[test] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn extract_if_pred_panic_leak() { diff --git a/library/alloc/src/collections/vec_deque/tests.rs b/library/alloc/src/collections/vec_deque/tests.rs index c90679f179775..ad76cb14deb86 100644 --- a/library/alloc/src/collections/vec_deque/tests.rs +++ b/library/alloc/src/collections/vec_deque/tests.rs @@ -1,9 +1,7 @@ -// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint -#![allow(static_mut_refs)] - use core::iter::TrustedLen; use super::*; +use crate::testing::macros::struct_with_counted_drop; #[bench] fn bench_push_back_100(b: &mut test::Bencher) { @@ -1086,36 +1084,24 @@ fn test_clone_from() { #[test] fn test_vec_deque_truncate_drop() { - static mut DROPS: u32 = 0; - #[derive(Clone)] - struct Elem(#[allow(dead_code)] i32); - impl Drop for Elem { - fn drop(&mut self) { - unsafe { - DROPS += 1; - } - } - } + struct_with_counted_drop!(Elem, DROPS); - let v = vec![Elem(1), Elem(2), Elem(3), Elem(4), Elem(5)]; - for push_front in 0..=v.len() { - let v = v.clone(); - let mut tester = VecDeque::with_capacity(5); - for (index, elem) in v.into_iter().enumerate() { + const LEN: usize = 5; + for push_front in 0..=LEN { + let mut tester = VecDeque::with_capacity(LEN); + for index in 0..LEN { if index < push_front { - tester.push_front(elem); + tester.push_front(Elem); } else { - tester.push_back(elem); + tester.push_back(Elem); } } - assert_eq!(unsafe { DROPS }, 0); + assert_eq!(DROPS.get(), 0); tester.truncate(3); - assert_eq!(unsafe { DROPS }, 2); + assert_eq!(DROPS.get(), 2); tester.truncate(0); - assert_eq!(unsafe { DROPS }, 5); - unsafe { - DROPS = 0; - } + assert_eq!(DROPS.get(), 5); + DROPS.set(0); } } diff --git a/library/alloctests/testing/macros.rs b/library/alloctests/testing/macros.rs new file mode 100644 index 0000000000000..2433e53ca8954 --- /dev/null +++ b/library/alloctests/testing/macros.rs @@ -0,0 +1,37 @@ +macro_rules! struct_with_counted_drop { + ($struct_name:ident $(( $( $elt_ty:ty ),+ ))?, $drop_counter:ident $( => $drop_stmt:expr )? ) => { + thread_local! {static $drop_counter: ::core::cell::Cell = ::core::cell::Cell::new(0);} + + #[derive(Clone, Debug, PartialEq)] + struct $struct_name $(( $( $elt_ty ),+ ))?; + + impl ::std::ops::Drop for $struct_name { + fn drop(&mut self) { + $drop_counter.set($drop_counter.get() + 1); + + $($drop_stmt(self))? + } + } + }; + ($struct_name:ident $(( $( $elt_ty:ty ),+ ))?, $drop_counter:ident[ $drop_key:expr,$key_ty:ty ] $( => $drop_stmt:expr )? ) => { + thread_local! { + static $drop_counter: ::core::cell::RefCell<::std::collections::HashMap<$key_ty, u32>> = + ::core::cell::RefCell::new(::std::collections::HashMap::new()); + } + + #[derive(Clone, Debug, PartialEq)] + struct $struct_name $(( $( $elt_ty ),+ ))?; + + impl ::std::ops::Drop for $struct_name { + fn drop(&mut self) { + $drop_counter.with_borrow_mut(|counter| { + *counter.entry($drop_key(self)).or_default() += 1; + }); + + $($drop_stmt(self))? + } + } + }; +} + +pub(crate) use struct_with_counted_drop; diff --git a/library/alloctests/testing/mod.rs b/library/alloctests/testing/mod.rs index c8457daf93e5a..66a4f6682b9f0 100644 --- a/library/alloctests/testing/mod.rs +++ b/library/alloctests/testing/mod.rs @@ -1,3 +1,4 @@ pub(crate) mod crash_test; +pub(crate) mod macros; pub(crate) mod ord_chaos; pub(crate) mod rng; diff --git a/library/alloctests/tests/fmt.rs b/library/alloctests/tests/fmt.rs index dbcf0c3a11410..0989a56b5543b 100644 --- a/library/alloctests/tests/fmt.rs +++ b/library/alloctests/tests/fmt.rs @@ -1,6 +1,4 @@ #![deny(warnings)] -// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint -#![allow(static_mut_refs)] #![allow(unnecessary_transmutes)] use std::cell::RefCell; @@ -285,19 +283,32 @@ fn test_format_args() { t!(s, "args were: hello world"); } +macro_rules! counter_fn { + ($name:ident) => { + fn $name() -> u32 { + thread_local! {static COUNTER: ::core::cell::Cell = ::core::cell::Cell::new(0);} + + COUNTER.set(COUNTER.get() + 1); + COUNTER.get() + } + }; +} + #[test] fn test_order() { - // Make sure format!() arguments are always evaluated in a left-to-right - // ordering - fn foo() -> isize { - static mut FOO: isize = 0; - unsafe { - FOO += 1; - FOO - } - } + // Make sure format!() arguments are always evaluated in a left-to-right ordering + counter_fn!(count); + assert_eq!( - format!("{} {} {a} {b} {} {c}", foo(), foo(), foo(), a = foo(), b = foo(), c = foo()), + format!( + "{} {} {a} {b} {} {c}", + count(), + count(), + count(), + a = count(), + b = count(), + c = count() + ), "1 2 4 5 3 6".to_string() ); } @@ -306,14 +317,9 @@ fn test_order() { fn test_once() { // Make sure each argument are evaluated only once even though it may be // formatted multiple times - fn foo() -> isize { - static mut FOO: isize = 0; - unsafe { - FOO += 1; - FOO - } - } - assert_eq!(format!("{0} {0} {0} {a} {a} {a}", foo(), a = foo()), "1 1 1 2 2 2".to_string()); + counter_fn!(count); + + assert_eq!(format!("{0} {0} {0} {a} {a} {a}", count(), a = count()), "1 1 1 2 2 2".to_string()); } #[test] diff --git a/library/alloctests/tests/testing/mod.rs b/library/alloctests/tests/testing/mod.rs index 30275a5125e8a..08856e39ded2b 100644 --- a/library/alloctests/tests/testing/mod.rs +++ b/library/alloctests/tests/testing/mod.rs @@ -1,2 +1,4 @@ #[path = "../../testing/crash_test.rs"] pub mod crash_test; +#[path = "../../testing/macros.rs"] +pub mod macros; diff --git a/library/alloctests/tests/vec.rs b/library/alloctests/tests/vec.rs index 51b49b8edb3f0..00f640cd17ea4 100644 --- a/library/alloctests/tests/vec.rs +++ b/library/alloctests/tests/vec.rs @@ -1,6 +1,3 @@ -// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint -#![allow(static_mut_refs)] - use core::alloc::{Allocator, Layout}; use core::num::NonZero; use core::ptr::NonNull; @@ -20,6 +17,8 @@ use std::rc::Rc; use std::sync::atomic::{AtomicU32, Ordering}; use std::vec::{Drain, IntoIter}; +use crate::testing::macros::struct_with_counted_drop; + struct DropCounter<'a> { count: &'a mut u32, } @@ -548,32 +547,25 @@ fn test_cmp() { #[test] fn test_vec_truncate_drop() { - static mut DROPS: u32 = 0; - struct Elem(#[allow(dead_code)] i32); - impl Drop for Elem { - fn drop(&mut self) { - unsafe { - DROPS += 1; - } - } - } + struct_with_counted_drop!(Elem(i32), DROPS); let mut v = vec![Elem(1), Elem(2), Elem(3), Elem(4), Elem(5)]; - assert_eq!(unsafe { DROPS }, 0); + + assert_eq!(DROPS.get(), 0); v.truncate(3); - assert_eq!(unsafe { DROPS }, 2); + assert_eq!(DROPS.get(), 2); v.truncate(0); - assert_eq!(unsafe { DROPS }, 5); + assert_eq!(DROPS.get(), 5); } #[test] #[should_panic] fn test_vec_truncate_fail() { struct BadElem(i32); + impl Drop for BadElem { fn drop(&mut self) { - let BadElem(ref mut x) = *self; - if *x == 0xbadbeef { + if let BadElem(0xbadbeef) = self { panic!("BadElem panic: 0xbadbeef") } } @@ -812,22 +804,7 @@ fn test_drain_end_overflow() { #[test] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_drain_leak() { - static mut DROPS: i32 = 0; - - #[derive(Debug, PartialEq)] - struct D(u32, bool); - - impl Drop for D { - fn drop(&mut self) { - unsafe { - DROPS += 1; - } - - if self.1 { - panic!("panic in `drop`"); - } - } - } + struct_with_counted_drop!(D(u32, bool), DROPS => |this: &D| if this.1 { panic!("panic in `drop`"); }); let mut v = vec![ D(0, false), @@ -844,7 +821,7 @@ fn test_drain_leak() { })) .ok(); - assert_eq!(unsafe { DROPS }, 4); + assert_eq!(DROPS.get(), 4); assert_eq!(v, vec![D(0, false), D(1, false), D(6, false),]); } @@ -1057,27 +1034,13 @@ fn test_into_iter_clone() { #[test] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_into_iter_leak() { - static mut DROPS: i32 = 0; - - struct D(bool); - - impl Drop for D { - fn drop(&mut self) { - unsafe { - DROPS += 1; - } - - if self.0 { - panic!("panic in `drop`"); - } - } - } + struct_with_counted_drop!(D(bool), DROPS => |this: &D| if this.0 { panic!("panic in `drop`"); }); let v = vec![D(false), D(true), D(false)]; catch_unwind(move || drop(v.into_iter())).ok(); - assert_eq!(unsafe { DROPS }, 3); + assert_eq!(DROPS.get(), 3); } #[test] @@ -1274,55 +1237,31 @@ fn test_from_iter_specialization_panic_during_iteration_drops() { #[test] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] -// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint -#[allow(static_mut_refs)] fn test_from_iter_specialization_panic_during_drop_doesnt_leak() { - static mut DROP_COUNTER_OLD: [usize; 5] = [0; 5]; - static mut DROP_COUNTER_NEW: [usize; 2] = [0; 2]; - - #[derive(Debug)] - struct Old(usize); - - impl Drop for Old { - fn drop(&mut self) { - unsafe { - DROP_COUNTER_OLD[self.0] += 1; - } - - if self.0 == 3 { - panic!(); - } - - println!("Dropped Old: {}", self.0); - } - } - - #[derive(Debug)] - struct New(usize); - - impl Drop for New { - fn drop(&mut self) { - unsafe { - DROP_COUNTER_NEW[self.0] += 1; + struct_with_counted_drop!( + Old(usize), DROP_COUNTER_OLD[|this: &Old| this.0, usize] => + |this: &Old| { + if this.0 == 3 { panic!(); } println!("Dropped Old: {}", this.0) } - - println!("Dropped New: {}", self.0); - } - } + ); + struct_with_counted_drop!( + New(usize), DROP_COUNTER_NEW[|this: &New| this.0, usize] => + |this: &New| println!("Dropped New: {}", this.0) + ); let _ = std::panic::catch_unwind(AssertUnwindSafe(|| { let v = vec![Old(0), Old(1), Old(2), Old(3), Old(4)]; let _ = v.into_iter().map(|x| New(x.0)).take(2).collect::>(); })); - assert_eq!(unsafe { DROP_COUNTER_OLD[0] }, 1); - assert_eq!(unsafe { DROP_COUNTER_OLD[1] }, 1); - assert_eq!(unsafe { DROP_COUNTER_OLD[2] }, 1); - assert_eq!(unsafe { DROP_COUNTER_OLD[3] }, 1); - assert_eq!(unsafe { DROP_COUNTER_OLD[4] }, 1); + DROP_COUNTER_OLD.with_borrow(|c| assert_eq!(c.get(&0), Some(&1))); + DROP_COUNTER_OLD.with_borrow(|c| assert_eq!(c.get(&1), Some(&1))); + DROP_COUNTER_OLD.with_borrow(|c| assert_eq!(c.get(&2), Some(&1))); + DROP_COUNTER_OLD.with_borrow(|c| assert_eq!(c.get(&3), Some(&1))); + DROP_COUNTER_OLD.with_borrow(|c| assert_eq!(c.get(&4), Some(&1))); - assert_eq!(unsafe { DROP_COUNTER_NEW[0] }, 1); - assert_eq!(unsafe { DROP_COUNTER_NEW[1] }, 1); + DROP_COUNTER_NEW.with_borrow(|c| assert_eq!(c.get(&0), Some(&1))); + DROP_COUNTER_NEW.with_borrow(|c| assert_eq!(c.get(&1), Some(&1))); } // regression test for issue #85322. Peekable previously implemented InPlaceIterable, diff --git a/library/alloctests/tests/vec_deque.rs b/library/alloctests/tests/vec_deque.rs index b77ea3a312bef..a82906d55e5d0 100644 --- a/library/alloctests/tests/vec_deque.rs +++ b/library/alloctests/tests/vec_deque.rs @@ -1,6 +1,3 @@ -// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint -#![allow(static_mut_refs)] - use core::num::NonZero; use std::assert_matches::assert_matches; use std::collections::TryReserveErrorKind::*; @@ -14,6 +11,7 @@ use Taggy::*; use Taggypar::*; use crate::hash; +use crate::testing::macros::struct_with_counted_drop; #[test] fn test_simple() { @@ -719,15 +717,7 @@ fn test_show() { #[test] fn test_drop() { - static mut DROPS: i32 = 0; - struct Elem; - impl Drop for Elem { - fn drop(&mut self) { - unsafe { - DROPS += 1; - } - } - } + struct_with_counted_drop!(Elem, DROPS); let mut ring = VecDeque::new(); ring.push_back(Elem); @@ -736,20 +726,12 @@ fn test_drop() { ring.push_front(Elem); drop(ring); - assert_eq!(unsafe { DROPS }, 4); + assert_eq!(DROPS.get(), 4); } #[test] fn test_drop_with_pop() { - static mut DROPS: i32 = 0; - struct Elem; - impl Drop for Elem { - fn drop(&mut self) { - unsafe { - DROPS += 1; - } - } - } + struct_with_counted_drop!(Elem, DROPS); let mut ring = VecDeque::new(); ring.push_back(Elem); @@ -759,23 +741,15 @@ fn test_drop_with_pop() { drop(ring.pop_back()); drop(ring.pop_front()); - assert_eq!(unsafe { DROPS }, 2); + assert_eq!(DROPS.get(), 2); drop(ring); - assert_eq!(unsafe { DROPS }, 4); + assert_eq!(DROPS.get(), 4); } #[test] fn test_drop_clear() { - static mut DROPS: i32 = 0; - struct Elem; - impl Drop for Elem { - fn drop(&mut self) { - unsafe { - DROPS += 1; - } - } - } + struct_with_counted_drop!(Elem, DROPS); let mut ring = VecDeque::new(); ring.push_back(Elem); @@ -783,30 +757,16 @@ fn test_drop_clear() { ring.push_back(Elem); ring.push_front(Elem); ring.clear(); - assert_eq!(unsafe { DROPS }, 4); + assert_eq!(DROPS.get(), 4); drop(ring); - assert_eq!(unsafe { DROPS }, 4); + assert_eq!(DROPS.get(), 4); } #[test] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_drop_panic() { - static mut DROPS: i32 = 0; - - struct D(bool); - - impl Drop for D { - fn drop(&mut self) { - unsafe { - DROPS += 1; - } - - if self.0 { - panic!("panic in `drop`"); - } - } - } + struct_with_counted_drop!(D(bool), DROPS => |this: &D| if this.0 { panic!("panic in `drop`"); } ); let mut q = VecDeque::new(); q.push_back(D(false)); @@ -820,7 +780,7 @@ fn test_drop_panic() { catch_unwind(move || drop(q)).ok(); - assert_eq!(unsafe { DROPS }, 8); + assert_eq!(DROPS.get(), 8); } #[test] @@ -1655,21 +1615,7 @@ fn test_try_rfold_moves_iter() { #[test] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn truncate_leak() { - static mut DROPS: i32 = 0; - - struct D(bool); - - impl Drop for D { - fn drop(&mut self) { - unsafe { - DROPS += 1; - } - - if self.0 { - panic!("panic in `drop`"); - } - } - } + struct_with_counted_drop!(D(bool), DROPS => |this: &D| if this.0 { panic!("panic in `drop`"); } ); let mut q = VecDeque::new(); q.push_back(D(false)); @@ -1683,27 +1629,13 @@ fn truncate_leak() { catch_unwind(AssertUnwindSafe(|| q.truncate(1))).ok(); - assert_eq!(unsafe { DROPS }, 7); + assert_eq!(DROPS.get(), 7); } #[test] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn truncate_front_leak() { - static mut DROPS: i32 = 0; - - struct D(bool); - - impl Drop for D { - fn drop(&mut self) { - unsafe { - DROPS += 1; - } - - if self.0 { - panic!("panic in `drop`"); - } - } - } + struct_with_counted_drop!(D(bool), DROPS => |this: &D| if this.0 { panic!("panic in `drop`"); } ); let mut q = VecDeque::new(); q.push_back(D(false)); @@ -1717,28 +1649,13 @@ fn truncate_front_leak() { catch_unwind(AssertUnwindSafe(|| q.truncate_front(1))).ok(); - assert_eq!(unsafe { DROPS }, 7); + assert_eq!(DROPS.get(), 7); } #[test] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_drain_leak() { - static mut DROPS: i32 = 0; - - #[derive(Debug, PartialEq)] - struct D(u32, bool); - - impl Drop for D { - fn drop(&mut self) { - unsafe { - DROPS += 1; - } - - if self.1 { - panic!("panic in `drop`"); - } - } - } + struct_with_counted_drop!(D(u32, bool), DROPS => |this: &D| if this.1 { panic!("panic in `drop`"); } ); let mut v = VecDeque::new(); v.push_back(D(4, false)); @@ -1754,10 +1671,10 @@ fn test_drain_leak() { })) .ok(); - assert_eq!(unsafe { DROPS }, 4); + assert_eq!(DROPS.get(), 4); assert_eq!(v.len(), 3); drop(v); - assert_eq!(unsafe { DROPS }, 7); + assert_eq!(DROPS.get(), 7); } #[test] diff --git a/library/coretests/tests/atomic.rs b/library/coretests/tests/atomic.rs index e0c0fe4790c04..b1ab443aa6e5e 100644 --- a/library/coretests/tests/atomic.rs +++ b/library/coretests/tests/atomic.rs @@ -228,24 +228,20 @@ fn static_init() { } #[test] -// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint -#[allow(static_mut_refs)] fn atomic_access_bool() { - static mut ATOMIC: AtomicBool = AtomicBool::new(false); - - unsafe { - assert_eq!(*ATOMIC.get_mut(), false); - ATOMIC.store(true, SeqCst); - assert_eq!(*ATOMIC.get_mut(), true); - ATOMIC.fetch_or(false, SeqCst); - assert_eq!(*ATOMIC.get_mut(), true); - ATOMIC.fetch_and(false, SeqCst); - assert_eq!(*ATOMIC.get_mut(), false); - ATOMIC.fetch_nand(true, SeqCst); - assert_eq!(*ATOMIC.get_mut(), true); - ATOMIC.fetch_xor(true, SeqCst); - assert_eq!(*ATOMIC.get_mut(), false); - } + let mut atom = AtomicBool::new(false); + + assert_eq!(*atom.get_mut(), false); + atom.store(true, SeqCst); + assert_eq!(*atom.get_mut(), true); + atom.fetch_or(false, SeqCst); + assert_eq!(*atom.get_mut(), true); + atom.fetch_and(false, SeqCst); + assert_eq!(*atom.get_mut(), false); + atom.fetch_nand(true, SeqCst); + assert_eq!(*atom.get_mut(), true); + atom.fetch_xor(true, SeqCst); + assert_eq!(*atom.get_mut(), false); } #[test] diff --git a/library/std/src/os/unix/mod.rs b/library/std/src/os/unix/mod.rs index 5802b6539651c..78c957270c451 100644 --- a/library/std/src/os/unix/mod.rs +++ b/library/std/src/os/unix/mod.rs @@ -116,6 +116,9 @@ pub mod prelude { #[stable(feature = "rust1", since = "1.0.0")] pub use super::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; #[doc(no_inline)] + #[unstable(feature = "unix_send_signal", issue = "141975")] + pub use super::process::ChildExt; + #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] pub use super::process::{CommandExt, ExitStatusExt}; #[doc(no_inline)] diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index 57ce3c5a4bf4a..3f4fe2e56ec31 100644 --- a/library/std/src/os/unix/process.rs +++ b/library/std/src/os/unix/process.rs @@ -378,6 +378,41 @@ impl ExitStatusExt for process::ExitStatusError { } } +#[unstable(feature = "unix_send_signal", issue = "141975")] +pub trait ChildExt: Sealed { + /// Sends a signal to a child process. + /// + /// # Errors + /// + /// This function will return an error if the signal is invalid. The integer values associated + /// with signals are implemenation-specific, so it's encouraged to use a crate that provides + /// posix bindings. + /// + /// # Examples + /// + /// ```rust + /// #![feature(unix_send_signal)] + /// + /// use std::{io, os::unix::process::ChildExt, process::{Command, Stdio}}; + /// + /// use libc::SIGTERM; + /// + /// fn main() -> io::Result<()> { + /// let child = Command::new("cat").stdin(Stdio::piped()).spawn()?; + /// child.send_signal(SIGTERM)?; + /// Ok(()) + /// } + /// ``` + fn send_signal(&self, signal: i32) -> io::Result<()>; +} + +#[unstable(feature = "unix_send_signal", issue = "141975")] +impl ChildExt for process::Child { + fn send_signal(&self, signal: i32) -> io::Result<()> { + self.handle.send_signal(signal) + } +} + #[stable(feature = "process_extensions", since = "1.2.0")] impl FromRawFd for process::Stdio { #[inline] diff --git a/library/std/src/sys/pal/unix/linux/pidfd.rs b/library/std/src/sys/pal/unix/linux/pidfd.rs index 2d949ec9e91f7..47e9a616bfdaf 100644 --- a/library/std/src/sys/pal/unix/linux/pidfd.rs +++ b/library/std/src/sys/pal/unix/linux/pidfd.rs @@ -13,11 +13,15 @@ pub(crate) struct PidFd(FileDesc); impl PidFd { pub fn kill(&self) -> io::Result<()> { + self.send_signal(libc::SIGKILL) + } + + pub(crate) fn send_signal(&self, signal: i32) -> io::Result<()> { cvt(unsafe { libc::syscall( libc::SYS_pidfd_send_signal, self.0.as_raw_fd(), - libc::SIGKILL, + signal, crate::ptr::null::<()>(), 0, ) diff --git a/library/std/src/sys/process/unix/fuchsia.rs b/library/std/src/sys/process/unix/fuchsia.rs index 017ab91797ce6..d71be510b6afe 100644 --- a/library/std/src/sys/process/unix/fuchsia.rs +++ b/library/std/src/sys/process/unix/fuchsia.rs @@ -152,6 +152,11 @@ impl Process { Ok(()) } + pub fn send_signal(&self, _signal: i32) -> io::Result<()> { + // Fuchsia doesn't have a direct equivalent for signals + unimplemented!() + } + pub fn wait(&mut self) -> io::Result { let mut proc_info: zx_info_process_t = Default::default(); let mut actual: size_t = 0; diff --git a/library/std/src/sys/process/unix/unix.rs b/library/std/src/sys/process/unix/unix.rs index 4f595ac9a1c5f..1fe80c13b81c8 100644 --- a/library/std/src/sys/process/unix/unix.rs +++ b/library/std/src/sys/process/unix/unix.rs @@ -963,9 +963,13 @@ impl Process { self.pid as u32 } - pub fn kill(&mut self) -> io::Result<()> { + pub fn kill(&self) -> io::Result<()> { + self.send_signal(libc::SIGKILL) + } + + pub(crate) fn send_signal(&self, signal: i32) -> io::Result<()> { // If we've already waited on this process then the pid can be recycled - // and used for another process, and we probably shouldn't be killing + // and used for another process, and we probably shouldn't be signaling // random processes, so return Ok because the process has exited already. if self.status.is_some() { return Ok(()); @@ -973,9 +977,9 @@ impl Process { #[cfg(target_os = "linux")] if let Some(pid_fd) = self.pidfd.as_ref() { // pidfd_send_signal predates pidfd_open. so if we were able to get an fd then sending signals will work too - return pid_fd.kill(); + return pid_fd.send_signal(signal); } - cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop) + cvt(unsafe { libc::kill(self.pid, signal) }).map(drop) } pub fn wait(&mut self) -> io::Result { diff --git a/library/std/src/sys/process/unix/unsupported.rs b/library/std/src/sys/process/unix/unsupported.rs index e86561a5c5c4f..87403cd50f822 100644 --- a/library/std/src/sys/process/unix/unsupported.rs +++ b/library/std/src/sys/process/unix/unsupported.rs @@ -40,7 +40,11 @@ impl Process { 0 } - pub fn kill(&mut self) -> io::Result<()> { + pub fn kill(&self) -> io::Result<()> { + unsupported() + } + + pub fn send_signal(&self, _signal: i32) -> io::Result<()> { unsupported() } diff --git a/library/std/src/sys/process/unix/vxworks.rs b/library/std/src/sys/process/unix/vxworks.rs index f33b4a375da83..51ae8c56bdb9b 100644 --- a/library/std/src/sys/process/unix/vxworks.rs +++ b/library/std/src/sys/process/unix/vxworks.rs @@ -146,14 +146,18 @@ impl Process { self.pid as u32 } - pub fn kill(&mut self) -> io::Result<()> { + pub fn kill(&self) -> io::Result<()> { + self.send_signal(libc::SIGKILL) + } + + pub fn send_signal(&self, signal: i32) -> io::Result<()> { // If we've already waited on this process then the pid can be recycled // and used for another process, and we probably shouldn't be killing // random processes, so return Ok because the process has exited already. if self.status.is_some() { Ok(()) } else { - cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop) + cvt(unsafe { libc::kill(self.pid, signal) }).map(drop) } } diff --git a/src/bootstrap/build.rs b/src/bootstrap/build.rs index e0e32d3135354..d9810e899a043 100644 --- a/src/bootstrap/build.rs +++ b/src/bootstrap/build.rs @@ -1,6 +1,7 @@ use std::env; fn main() { + // this is needed because `HOST` is only available to build scripts. let host = env::var("HOST").unwrap(); println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rustc-env=BUILD_TRIPLE={host}"); diff --git a/src/doc/book b/src/doc/book index 4433c9f0cad84..8a6d44e45b7b5 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit 4433c9f0cad8460bee05ede040587f8a1fa3f1de +Subproject commit 8a6d44e45b7b564eeb6bae30507e1fbac439d72d diff --git a/src/doc/reference b/src/doc/reference index d4c66b346f4b7..50fc1628f3656 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit d4c66b346f4b72d29e70390a3fa3ea7d4e064db1 +Subproject commit 50fc1628f36563958399123829c73755fa7a8421 diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example index 9baa9e863116c..05c7d8bae65f2 160000 --- a/src/doc/rust-by-example +++ b/src/doc/rust-by-example @@ -1 +1 @@ -Subproject commit 9baa9e863116cb9524a177d5a5c475baac18928a +Subproject commit 05c7d8bae65f23a1837430c5a19be129d414f5ec diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 204f8decffcc0..d73600a463631 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -387,8 +387,6 @@ pub(crate) fn run_global_ctxt( ctxt.external_traits.insert(sized_trait_did, sized_trait); } - debug!("crate: {:?}", tcx.hir_crate(())); - let mut krate = tcx.sess.time("clean_crate", || clean::krate(&mut ctxt)); if krate.module.doc_value().is_empty() { diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs index 2feadce26d09f..29c63a391e215 100644 --- a/src/librustdoc/json/mod.rs +++ b/src/librustdoc/json/mod.rs @@ -18,7 +18,6 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::{DefId, DefIdSet}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; -use rustc_session::features::StabilityExt; use rustc_span::def_id::LOCAL_CRATE; use rustdoc_json_types as types; // It's important to use the FxHashMap from rustdoc_json_types here, instead of @@ -148,7 +147,7 @@ fn target(sess: &rustc_session::Session) -> types::Target { .copied() .filter(|(_, stability, _)| { // Describe only target features which the user can toggle - stability.is_toggle_permitted(sess).is_ok() + stability.toggle_allowed().is_ok() }) .map(|(name, stability, implied_features)| { types::TargetFeature { @@ -164,7 +163,7 @@ fn target(sess: &rustc_session::Session) -> types::Target { // Imply only target features which the user can toggle feature_stability .get(name) - .map(|stability| stability.is_toggle_permitted(sess).is_ok()) + .map(|stability| stability.toggle_allowed().is_ok()) .unwrap_or(false) }) .map(String::from) diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs index 1f93895ae0762..d5de43feb588a 100644 --- a/src/rustdoc-json-types/lib.rs +++ b/src/rustdoc-json-types/lib.rs @@ -37,8 +37,8 @@ pub type FxHashMap = HashMap; // re-export for use in src/librustdoc // will instead cause conflicts. See #94591 for more. (This paragraph and the "Latest feature" line // are deliberately not in a doc comment, because they need not be in public docs.) // -// Latest feature: Pretty printing of inline attributes changed -pub const FORMAT_VERSION: u32 = 48; +// Latest feature: Pretty printing of optimize attributes changed +pub const FORMAT_VERSION: u32 = 49; /// The root of the emitted JSON blob. /// diff --git a/tests/assembly/s390x-backchain-toggle.rs b/tests/assembly/s390x-backchain-toggle.rs index 83c7b82d0d4b3..9bae15b7d11bb 100644 --- a/tests/assembly/s390x-backchain-toggle.rs +++ b/tests/assembly/s390x-backchain-toggle.rs @@ -1,5 +1,5 @@ //@ add-core-stubs -//@ revisions: enable-backchain disable-backchain +//@ revisions: enable-backchain disable-backchain default-backchain //@ assembly-output: emit-asm //@ compile-flags: -Copt-level=3 --crate-type=lib --target=s390x-unknown-linux-gnu //@ needs-llvm-components: systemz @@ -26,6 +26,8 @@ extern "C" fn test_backchain() -> i32 { // enable-backchain: stg [[REG1]], 0(%r15) // disable-backchain: aghi %r15, -160 // disable-backchain-NOT: stg %r{{.*}}, 0(%r15) + // default-backchain: aghi %r15, -160 + // default-backchain-NOT: stg %r{{.*}}, 0(%r15) unsafe { extern_func(); } @@ -35,6 +37,7 @@ extern "C" fn test_backchain() -> i32 { // Make sure that the expected return value is written into %r2 (return register): // enable-backchain-NEXT: lghi %r2, 1 // disable-backchain: lghi %r2, 0 + // default-backchain: lghi %r2, 0 #[cfg(target_feature = "backchain")] { 1 diff --git a/tests/codegen/target-feature-negative-implication.rs b/tests/codegen/target-feature-negative-implication.rs new file mode 100644 index 0000000000000..36cd82dd8cf5a --- /dev/null +++ b/tests/codegen/target-feature-negative-implication.rs @@ -0,0 +1,20 @@ +//@ add-core-stubs +//@ needs-llvm-components: x86 +//@ compile-flags: --target=x86_64-unknown-linux-gnu +//@ compile-flags: -Ctarget-feature=-avx2 + +#![feature(no_core, lang_items)] +#![crate_type = "lib"] +#![no_core] + +extern crate minicore; +use minicore::*; + +#[no_mangle] +pub unsafe fn banana() { + // CHECK-LABEL: @banana() + // CHECK-SAME: [[BANANAATTRS:#[0-9]+]] { +} + +// CHECK: attributes [[BANANAATTRS]] +// CHECK-SAME: -avx512 diff --git a/tests/codegen/target-feature-overrides.rs b/tests/codegen/target-feature-overrides.rs index 0fc1e0136b3f3..eb19b0de2fa8a 100644 --- a/tests/codegen/target-feature-overrides.rs +++ b/tests/codegen/target-feature-overrides.rs @@ -1,3 +1,4 @@ +// ignore-tidy-linelength //@ add-core-stubs //@ revisions: COMPAT INCOMPAT //@ needs-llvm-components: x86 @@ -39,7 +40,7 @@ pub unsafe fn banana() -> u32 { // CHECK: attributes [[APPLEATTRS]] // COMPAT-SAME: "target-features"="+avx,+avx2,{{.*}}" -// INCOMPAT-SAME: "target-features"="-avx2,-avx,+avx,{{.*}}" +// INCOMPAT-SAME: "target-features"="{{(-[^,]+,)*}}-avx2{{(,-[^,]+)*}},-avx{{(,-[^,]+)*}},+avx{{(,\+[^,]+)*}}" // CHECK: attributes [[BANANAATTRS]] // COMPAT-SAME: "target-features"="+avx,+avx2,{{.*}}" -// INCOMPAT-SAME: "target-features"="-avx2,-avx" +// INCOMPAT-SAME: "target-features"="{{(-[^,]+,)*}}-avx2{{(,-[^,]+)*}},-avx{{(,-[^,]+)*}}" diff --git a/tests/codegen/tied-features-strength.rs b/tests/codegen/tied-features-strength.rs index 6be0e21e0ef3a..81499c070d19a 100644 --- a/tests/codegen/tied-features-strength.rs +++ b/tests/codegen/tied-features-strength.rs @@ -4,14 +4,23 @@ //@ compile-flags: --crate-type=rlib --target=aarch64-unknown-linux-gnu //@ needs-llvm-components: aarch64 +// Rust made SVE require neon. //@ [ENABLE_SVE] compile-flags: -C target-feature=+sve -Copt-level=0 -// ENABLE_SVE: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)|(\+sve,?)|(\+neon,?)|(\+fp-armv8,?))*}}" } +// ENABLE_SVE: attributes #0 +// ENABLE_SVE-SAME: +neon +// ENABLE_SVE-SAME: +sve +// However, disabling SVE does not disable neon. //@ [DISABLE_SVE] compile-flags: -C target-feature=-sve -Copt-level=0 -// DISABLE_SVE: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)|(-sve,?)|(\+neon,?))*}}" } +// DISABLE_SVE: attributes #0 +// DISABLE_SVE-NOT: -neon +// DISABLE_SVE-SAME: -sve +// OTOH, neon fn `fp-armv8` are fully tied; toggling neon must toggle `fp-armv8` the same way. //@ [DISABLE_NEON] compile-flags: -C target-feature=-neon -Copt-level=0 -// DISABLE_NEON: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)|(-fp-armv8,?)|(-neon,?))*}}" } +// DISABLE_NEON: attributes #0 +// DISABLE_NEON-SAME: -neon +// DISABLE_NEON-SAME: -fp-armv8 //@ [ENABLE_NEON] compile-flags: -C target-feature=+neon -Copt-level=0 // ENABLE_NEON: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)|(\+fp-armv8,?)|(\+neon,?))*}}" } diff --git a/tests/ui/check-cfg/target_feature.stderr b/tests/ui/check-cfg/target_feature.stderr index f29a41d6a8e28..f422919983b75 100644 --- a/tests/ui/check-cfg/target_feature.stderr +++ b/tests/ui/check-cfg/target_feature.stderr @@ -211,10 +211,6 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `reference-types` `relax` `relaxed-simd` -`reserve-x18` -`retpoline-external-thunk` -`retpoline-indirect-branches` -`retpoline-indirect-calls` `rtm` `sb` `scq` diff --git a/tests/ui/feature-gates/feature-gate-optimize_attribute.rs b/tests/ui/feature-gates/feature-gate-optimize_attribute.rs index 77cc307c9f453..ed5a11270f83e 100644 --- a/tests/ui/feature-gates/feature-gate-optimize_attribute.rs +++ b/tests/ui/feature-gates/feature-gate-optimize_attribute.rs @@ -11,5 +11,5 @@ fn none() {} #[optimize(banana)] //~^ ERROR the `#[optimize]` attribute is an experimental feature -//~| ERROR E0722 +//~| ERROR malformed `optimize` attribute input [E0539] fn not_known() {} diff --git a/tests/ui/feature-gates/feature-gate-optimize_attribute.stderr b/tests/ui/feature-gates/feature-gate-optimize_attribute.stderr index 4e6e4ac2703a8..e7e62b4f98993 100644 --- a/tests/ui/feature-gates/feature-gate-optimize_attribute.stderr +++ b/tests/ui/feature-gates/feature-gate-optimize_attribute.stderr @@ -38,13 +38,16 @@ LL | #[optimize(banana)] = help: add `#![feature(optimize_attribute)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0722]: invalid argument - --> $DIR/feature-gate-optimize_attribute.rs:12:12 +error[E0539]: malformed `optimize` attribute input + --> $DIR/feature-gate-optimize_attribute.rs:12:1 | LL | #[optimize(banana)] - | ^^^^^^ + | ^^^^^^^^^^^------^^ + | | | + | | valid arguments are `size`, `speed` or `none` + | help: must be of the form: `#[optimize(size|speed|none)]` error: aborting due to 5 previous errors -Some errors have detailed explanations: E0658, E0722. -For more information about an error, try `rustc --explain E0658`. +Some errors have detailed explanations: E0539, E0658. +For more information about an error, try `rustc --explain E0539`. diff --git a/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.riscv.stderr b/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.riscv.stderr index 2dca0c220332b..0b2d71f97d0d4 100644 --- a/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.riscv.stderr +++ b/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.riscv.stderr @@ -7,13 +7,5 @@ warning: unstable feature specified for `-Ctarget-feature`: `d` | = note: this feature is not stably supported; its behavior can change in the future -warning: unstable feature specified for `-Ctarget-feature`: `f` - | - = note: this feature is not stably supported; its behavior can change in the future - -warning: unstable feature specified for `-Ctarget-feature`: `zicsr` - | - = note: this feature is not stably supported; its behavior can change in the future - -warning: 4 warnings emitted +warning: 2 warnings emitted diff --git a/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.rs b/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.rs index 302cceccf6939..1006b078bab36 100644 --- a/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.rs +++ b/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.rs @@ -24,5 +24,3 @@ pub trait Freeze {} //~? WARN must be disabled to ensure that the ABI of the current target can be implemented correctly //~? WARN unstable feature specified for `-Ctarget-feature` -//[riscv]~? WARN unstable feature specified for `-Ctarget-feature` -//[riscv]~? WARN unstable feature specified for `-Ctarget-feature` diff --git a/tests/ui/target-feature/retpoline-target-feature-flag.by_feature1.stderr b/tests/ui/target-feature/retpoline-target-feature-flag.by_feature1.stderr index 2a0f5f01aef6f..79e89823c5170 100644 --- a/tests/ui/target-feature/retpoline-target-feature-flag.by_feature1.stderr +++ b/tests/ui/target-feature/retpoline-target-feature-flag.by_feature1.stderr @@ -1,4 +1,4 @@ -warning: target feature `retpoline-external-thunk` cannot be enabled with `-Ctarget-feature`: use `retpoline-external-thunk` target modifier flag instead +warning: target feature `retpoline-external-thunk` cannot be enabled with `-Ctarget-feature`: use `-Zretpoline-external-thunk` compiler flag instead | = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #116344 diff --git a/tests/ui/target-feature/retpoline-target-feature-flag.by_feature2.stderr b/tests/ui/target-feature/retpoline-target-feature-flag.by_feature2.stderr index f7b6cb1644778..f5ff15df63299 100644 --- a/tests/ui/target-feature/retpoline-target-feature-flag.by_feature2.stderr +++ b/tests/ui/target-feature/retpoline-target-feature-flag.by_feature2.stderr @@ -1,4 +1,4 @@ -warning: target feature `retpoline-indirect-branches` cannot be enabled with `-Ctarget-feature`: use `retpoline` target modifier flag instead +warning: target feature `retpoline-indirect-branches` cannot be enabled with `-Ctarget-feature`: use `-Zretpoline` compiler flag instead | = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #116344 diff --git a/tests/ui/target-feature/retpoline-target-feature-flag.by_feature3.stderr b/tests/ui/target-feature/retpoline-target-feature-flag.by_feature3.stderr index 4f2cd1d1a522f..158cca08a7621 100644 --- a/tests/ui/target-feature/retpoline-target-feature-flag.by_feature3.stderr +++ b/tests/ui/target-feature/retpoline-target-feature-flag.by_feature3.stderr @@ -1,4 +1,4 @@ -warning: target feature `retpoline-indirect-calls` cannot be enabled with `-Ctarget-feature`: use `retpoline` target modifier flag instead +warning: target feature `retpoline-indirect-calls` cannot be enabled with `-Ctarget-feature`: use `-Zretpoline` compiler flag instead | = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #116344 diff --git a/tests/ui/target-feature/retpoline-target-feature-flag.rs b/tests/ui/target-feature/retpoline-target-feature-flag.rs index de3c44c3ed0eb..05c85860385a7 100644 --- a/tests/ui/target-feature/retpoline-target-feature-flag.rs +++ b/tests/ui/target-feature/retpoline-target-feature-flag.rs @@ -16,6 +16,6 @@ #![no_core] extern crate minicore; -//[by_feature1]~? WARN target feature `retpoline-external-thunk` cannot be enabled with `-Ctarget-feature`: use `retpoline-external-thunk` target modifier flag instead -//[by_feature2]~? WARN target feature `retpoline-indirect-branches` cannot be enabled with `-Ctarget-feature`: use `retpoline` target modifier flag instead -//[by_feature3]~? WARN target feature `retpoline-indirect-calls` cannot be enabled with `-Ctarget-feature`: use `retpoline` target modifier flag instead +//[by_feature1]~? WARN target feature `retpoline-external-thunk` cannot be enabled with `-Ctarget-feature` +//[by_feature2]~? WARN target feature `retpoline-indirect-branches` cannot be enabled with `-Ctarget-feature` +//[by_feature3]~? WARN target feature `retpoline-indirect-calls` cannot be enabled with `-Ctarget-feature`