Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion compiler/rustc_codegen_ssa/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
31 changes: 19 additions & 12 deletions compiler/rustc_codegen_ssa/src/target_features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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,
});
}
}
}
Expand All @@ -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 {
Expand Down
36 changes: 33 additions & 3 deletions compiler/rustc_target/src/target_features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Comment thread
RalfJung marked this conversation as resolved.
),
/// 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(
Expand All @@ -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
Expand All @@ -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),
}
}
Expand Down
Loading