Rollup merge of #155962 - romancardenas:cfg-only-stable-target-feature, r=RalfJung

`rustc`: `target_features`: allow for `cfg`-only stable `target_features`

This PR introduces a new stabilization level for `target_features`: `CfgOnlyStable`. The motivation is allowing the Rust compiler to expose `target_features` of targets so users can use `cfg(target_feature = "feature")` for conditional blocks depending on target features. However, `CfgOnlyStable` cannot be used for `#[target_feature(enable = "feature")]`, as this is still considered unstable. Accordingly, the compiler will still raise an error if these expressions are used on stable.

This PR relates partially to rust-lang/rust#150257. As discussed, for RISC-V targets, having the `"d"`, `"e"`, and `"f"` target features exposed will allow baremetal developers to adapt the code depending on the target's properties.

r? @RalfJung
This commit is contained in:
Guillaume Gomez
2026-05-05 02:50:08 +02:00
committed by GitHub
3 changed files with 54 additions and 16 deletions
+2 -1
View File
@@ -1212,9 +1212,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)]
@@ -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 {
+33 -3
View File
@@ -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<Symbol> {
///
/// 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<Symbol> {
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),
}
}