diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index f1112510af0f0..e6c8e5a4e3923 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -1194,9 +1194,10 @@ pub(crate) struct UnknownCTargetFeature<'a> { #[derive(Diagnostic)] #[diag("unstable feature specified for `-Ctarget-feature`: `{$feature}`")] -#[note("this feature is not stably supported; its behavior can change in the future")] +#[note("{$note}; its behavior can change in the future")] pub(crate) struct UnstableCTargetFeature<'a> { pub feature: &'a str, + pub note: &'a str, } #[derive(Diagnostic)] diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index 24f731c01996d..b8adad9e71777 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -62,16 +62,15 @@ pub(crate) fn from_target_feature_attr( feature: feature_str, reason, }); - } else if let Some(nightly_feature) = stability.requires_nightly() + } else if let Some(nightly_feature) = stability.requires_nightly(/* in_cfg */ false) && !rust_features.enabled(nightly_feature) { - feature_err( - &tcx.sess, - nightly_feature, - feature_span, - format!("the target feature `{feature}` is currently unstable"), - ) - .emit(); + let explain = if stability.is_cfg_stable_toggle_unstable() { + format!("the target feature `{feature}` is allowed in cfg but unstable otherwise") + } else { + format!("the target feature `{feature}` is currently unstable") + }; + feature_err(&tcx.sess, nightly_feature, feature_span, explain).emit(); } else { // Add this and the implied features. for &name in tcx.implied_target_features(feature) { @@ -315,12 +314,19 @@ pub fn cfg_target_feature<'a, const N: usize>( enabled: if enable { "enabled" } else { "disabled" }, reason, }); - } else if stability.requires_nightly().is_some() { + } else if stability.requires_nightly(/* in_cfg */ false).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 }); + let note = if stability.is_cfg_stable_toggle_unstable() { + "this feature is allowed in cfg but unstable otherwise" + } else { + "this feature is not stably supported" + }; + sess.dcx().emit_warn(errors::UnstableCTargetFeature { + feature: base_feature, + note, + }); } } } @@ -346,7 +352,8 @@ pub fn cfg_target_feature<'a, const N: usize>( // "forbidden" features. if allow_unstable || (gate.in_cfg() - && (sess.is_nightly_build() || gate.requires_nightly().is_none())) + && (sess.is_nightly_build() + || gate.requires_nightly(/* in_cfg */ true).is_none())) { Some(Symbol::intern(feature)) } else { diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 963b167be424d..b1e365e2c821b 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -17,6 +17,13 @@ pub enum Stability { /// This target feature is stable, it can be used in `#[target_feature]` and /// `#[cfg(target_feature)]`. Stable, + /// This target feature is cfg-stable. It can be used for `#[cfg(target_feature)]` on stable, + /// but using it in `#[target_feature]` requires the given nightly feature. + CfgStableToggleUnstable( + /// This must be a *language* feature, or else rustc will ICE when reporting a missing + /// feature gate! + Symbol, + ), /// This target feature is unstable. It is only present in `#[cfg(target_feature)]` on /// nightly and using it in `#[target_feature]` requires enabling the given nightly feature. Unstable( @@ -37,7 +44,12 @@ 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::Stable | Stability::Unstable { .. }) + matches!( + self, + Stability::Stable + | Stability::CfgStableToggleUnstable { .. } + | Stability::Unstable { .. } + ) } /// Returns the nightly feature that is required to toggle this target feature via @@ -48,20 +60,38 @@ impl Stability { /// Before calling this, ensure the feature is even permitted for this use: /// - for `#[target_feature]`/`-Ctarget-feature`, check `toggle_allowed()` /// - for `cfg(target_feature)`, check `in_cfg()` - pub fn requires_nightly(&self) -> Option { + /// + /// The `in_cfg` parameter is used to determine whether it will be used in + /// `cfg(target_feature)` (true) or `#[target_feature]`/`-Ctarget-feature` (false) + pub fn requires_nightly(&self, in_cfg: bool) -> Option { match *self { Stability::Unstable(nightly_feature) => Some(nightly_feature), + Stability::CfgStableToggleUnstable(nightly_feature) => { + if in_cfg { + None + } else { + Some(nightly_feature) + } + } Stability::Stable { .. } => None, Stability::Forbidden { .. } => panic!("forbidden features should not reach this far"), } } + /// Returns whether the feature is cfg-stable but still requires a nightly feature gate to + /// be used in `#[target_feature]`/`-Ctarget-feature`. + pub fn is_cfg_stable_toggle_unstable(&self) -> bool { + matches!(self, Stability::CfgStableToggleUnstable { .. }) + } + /// 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::Unstable(_) + | Stability::CfgStableToggleUnstable(_) + | Stability::Stable { .. } => Ok(()), Stability::Forbidden { reason } => Err(reason), } }