target_features: control separately whether enabling and disabling a target feature is allowed
This commit is contained in:
parent
a1740a9c35
commit
eb2e928250
5 changed files with 64 additions and 31 deletions
|
@ -96,7 +96,7 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec<Stri
|
||||||
}
|
}
|
||||||
Some((_, stability, _)) => {
|
Some((_, stability, _)) => {
|
||||||
if let Err(reason) =
|
if let Err(reason) =
|
||||||
stability.compute_toggleability(&sess.target).allow_toggle()
|
stability.toggle_allowed(&sess.target, enable_disable == '+')
|
||||||
{
|
{
|
||||||
sess.dcx().emit_warn(ForbiddenCTargetFeature { feature, reason });
|
sess.dcx().emit_warn(ForbiddenCTargetFeature { feature, reason });
|
||||||
} else if stability.requires_nightly().is_some() {
|
} else if stability.requires_nightly().is_some() {
|
||||||
|
|
|
@ -483,9 +483,9 @@ fn target_features_cfg(
|
||||||
.rust_target_features()
|
.rust_target_features()
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(_, gate, _)| gate.in_cfg())
|
.filter(|(_, gate, _)| gate.in_cfg())
|
||||||
.filter_map(|&(feature, gate, _)| {
|
.filter_map(|(feature, gate, _)| {
|
||||||
if sess.is_nightly_build() || allow_unstable || gate.requires_nightly().is_none() {
|
if sess.is_nightly_build() || allow_unstable || gate.requires_nightly().is_none() {
|
||||||
Some(feature)
|
Some(*feature)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
|
@ -373,9 +373,9 @@ pub fn target_features_cfg(sess: &Session, allow_unstable: bool) -> Vec<Symbol>
|
||||||
.rust_target_features()
|
.rust_target_features()
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(_, gate, _)| gate.in_cfg())
|
.filter(|(_, gate, _)| gate.in_cfg())
|
||||||
.filter_map(|&(feature, gate, _)| {
|
.filter_map(|(feature, gate, _)| {
|
||||||
if sess.is_nightly_build() || allow_unstable || gate.requires_nightly().is_none() {
|
if sess.is_nightly_build() || allow_unstable || gate.requires_nightly().is_none() {
|
||||||
Some(feature)
|
Some(*feature)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -718,7 +718,7 @@ pub(crate) fn global_llvm_features(
|
||||||
}
|
}
|
||||||
Some((_, stability, _)) => {
|
Some((_, stability, _)) => {
|
||||||
if let Err(reason) =
|
if let Err(reason) =
|
||||||
stability.compute_toggleability(&sess.target).allow_toggle()
|
stability.toggle_allowed(&sess.target, enable_disable == '+')
|
||||||
{
|
{
|
||||||
sess.dcx().emit_warn(ForbiddenCTargetFeature { feature, reason });
|
sess.dcx().emit_warn(ForbiddenCTargetFeature { feature, reason });
|
||||||
} else if stability.requires_nightly().is_some() {
|
} else if stability.requires_nightly().is_some() {
|
||||||
|
|
|
@ -65,7 +65,7 @@ pub(crate) fn from_target_feature_attr(
|
||||||
|
|
||||||
// Only allow target features whose feature gates have been enabled
|
// Only allow target features whose feature gates have been enabled
|
||||||
// and which are permitted to be toggled.
|
// and which are permitted to be toggled.
|
||||||
if let Err(reason) = stability.allow_toggle() {
|
if let Err(reason) = stability.toggle_allowed(/*enable*/ true) {
|
||||||
tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr {
|
tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr {
|
||||||
span: item.span(),
|
span: item.span(),
|
||||||
feature,
|
feature,
|
||||||
|
@ -160,7 +160,7 @@ pub(crate) fn provide(providers: &mut Providers) {
|
||||||
.target
|
.target
|
||||||
.rust_target_features()
|
.rust_target_features()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|&(a, b, _)| (a.to_string(), b.compute_toggleability(target)))
|
.map(|(a, b, _)| (a.to_string(), b.compute_toggleability(target)))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -20,7 +20,7 @@ pub const RUSTC_SPECIAL_FEATURES: &[&str] = &["backchain"];
|
||||||
/// `Toggleability` is the type storing whether (un)stable features can be toggled:
|
/// `Toggleability` is the type storing whether (un)stable features can be toggled:
|
||||||
/// this is initially a function since it can depend on `Target`, but for stable hashing
|
/// this is initially a function since it can depend on `Target`, but for stable hashing
|
||||||
/// it needs to be something hashable to we have to make the type generic.
|
/// it needs to be something hashable to we have to make the type generic.
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Stability<Toggleability> {
|
pub enum Stability<Toggleability> {
|
||||||
/// This target feature is stable, it can be used in `#[target_feature]` and
|
/// This target feature is stable, it can be used in `#[target_feature]` and
|
||||||
/// `#[cfg(target_feature)]`.
|
/// `#[cfg(target_feature)]`.
|
||||||
|
@ -44,11 +44,21 @@ pub enum Stability<Toggleability> {
|
||||||
Forbidden { reason: &'static str },
|
Forbidden { reason: &'static str },
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `Stability` where `allow_toggle` has not been computed yet.
|
|
||||||
/// Returns `Ok` if the toggle is allowed, `Err` with an explanation of not.
|
/// Returns `Ok` if the toggle is allowed, `Err` with an explanation of not.
|
||||||
pub type StabilityUncomputed = Stability<fn(&Target) -> Result<(), &'static str>>;
|
/// The `bool` indicates whether the feature is being enabled (`true`) or disabled.
|
||||||
|
pub type AllowToggleUncomputed = fn(&Target, bool) -> Result<(), &'static str>;
|
||||||
|
|
||||||
|
/// The computed result of whether a feature can be enabled/disabled on the current target.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct AllowToggleComputed {
|
||||||
|
enable: Result<(), &'static str>,
|
||||||
|
disable: Result<(), &'static str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Stability` where `allow_toggle` has not been computed yet.
|
||||||
|
pub type StabilityUncomputed = Stability<AllowToggleUncomputed>;
|
||||||
/// `Stability` where `allow_toggle` has already been computed.
|
/// `Stability` where `allow_toggle` has already been computed.
|
||||||
pub type StabilityComputed = Stability<Result<(), &'static str>>;
|
pub type StabilityComputed = Stability<AllowToggleComputed>;
|
||||||
|
|
||||||
impl<CTX, Toggleability: HashStable<CTX>> HashStable<CTX> for Stability<Toggleability> {
|
impl<CTX, Toggleability: HashStable<CTX>> HashStable<CTX> for Stability<Toggleability> {
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -69,11 +79,20 @@ impl<CTX, Toggleability: HashStable<CTX>> HashStable<CTX> for Stability<Toggleab
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<CTX> HashStable<CTX> for AllowToggleComputed {
|
||||||
|
#[inline]
|
||||||
|
fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
|
||||||
|
let AllowToggleComputed { enable, disable } = self;
|
||||||
|
enable.hash_stable(hcx, hasher);
|
||||||
|
disable.hash_stable(hcx, hasher);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<Toggleability> Stability<Toggleability> {
|
impl<Toggleability> Stability<Toggleability> {
|
||||||
/// Returns whether the feature can be used in `cfg(target_feature)` ever.
|
/// Returns whether the feature can be used in `cfg(target_feature)` ever.
|
||||||
/// (It might still be nightly-only even if this returns `true`, so make sure to also check
|
/// (It might still be nightly-only even if this returns `true`, so make sure to also check
|
||||||
/// `requires_nightly`.)
|
/// `requires_nightly`.)
|
||||||
pub fn in_cfg(self) -> bool {
|
pub fn in_cfg(&self) -> bool {
|
||||||
!matches!(self, Stability::Forbidden { .. })
|
!matches!(self, Stability::Forbidden { .. })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,24 +104,37 @@ impl<Toggleability> Stability<Toggleability> {
|
||||||
/// Before calling this, ensure the feature is even permitted for this use:
|
/// Before calling this, ensure the feature is even permitted for this use:
|
||||||
/// - for `#[target_feature]`/`-Ctarget-feature`, check `allow_toggle()`
|
/// - for `#[target_feature]`/`-Ctarget-feature`, check `allow_toggle()`
|
||||||
/// - for `cfg(target_feature)`, check `in_cfg`
|
/// - for `cfg(target_feature)`, check `in_cfg`
|
||||||
pub fn requires_nightly(self) -> Option<Symbol> {
|
pub fn requires_nightly(&self) -> Option<Symbol> {
|
||||||
match self {
|
match self {
|
||||||
Stability::Unstable { nightly_feature, .. } => Some(nightly_feature),
|
&Stability::Unstable { nightly_feature, .. } => Some(nightly_feature),
|
||||||
Stability::Stable { .. } => None,
|
&Stability::Stable { .. } => None,
|
||||||
Stability::Forbidden { .. } => panic!("forbidden features should not reach this far"),
|
&Stability::Forbidden { .. } => panic!("forbidden features should not reach this far"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StabilityUncomputed {
|
impl StabilityUncomputed {
|
||||||
pub fn compute_toggleability(self, target: &Target) -> StabilityComputed {
|
pub fn compute_toggleability(&self, target: &Target) -> StabilityComputed {
|
||||||
|
use Stability::*;
|
||||||
|
let compute = |f: AllowToggleUncomputed| AllowToggleComputed {
|
||||||
|
enable: f(target, true),
|
||||||
|
disable: f(target, false),
|
||||||
|
};
|
||||||
|
match self {
|
||||||
|
&Stable { allow_toggle } => Stable { allow_toggle: compute(allow_toggle) },
|
||||||
|
&Unstable { nightly_feature, allow_toggle } => {
|
||||||
|
Unstable { nightly_feature, allow_toggle: compute(allow_toggle) }
|
||||||
|
}
|
||||||
|
&Forbidden { reason } => Forbidden { reason },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn toggle_allowed(&self, target: &Target, enable: bool) -> Result<(), &'static str> {
|
||||||
use Stability::*;
|
use Stability::*;
|
||||||
match self {
|
match self {
|
||||||
Stable { allow_toggle } => Stable { allow_toggle: allow_toggle(target) },
|
&Stable { allow_toggle } => allow_toggle(target, enable),
|
||||||
Unstable { nightly_feature, allow_toggle } => {
|
&Unstable { allow_toggle, .. } => allow_toggle(target, enable),
|
||||||
Unstable { nightly_feature, allow_toggle: allow_toggle(target) }
|
&Forbidden { reason } => Err(reason),
|
||||||
}
|
|
||||||
Forbidden { reason } => Forbidden { reason },
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -111,19 +143,20 @@ impl StabilityComputed {
|
||||||
/// Returns whether the feature may be toggled via `#[target_feature]` or `-Ctarget-feature`.
|
/// 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
|
/// (It might still be nightly-only even if this returns `true`, so make sure to also check
|
||||||
/// `requires_nightly`.)
|
/// `requires_nightly`.)
|
||||||
pub fn allow_toggle(self) -> Result<(), &'static str> {
|
pub fn toggle_allowed(&self, enable: bool) -> Result<(), &'static str> {
|
||||||
match self {
|
let allow_toggle = match self {
|
||||||
Stability::Stable { allow_toggle } => allow_toggle,
|
Stability::Stable { allow_toggle } => allow_toggle,
|
||||||
Stability::Unstable { allow_toggle, .. } => allow_toggle,
|
Stability::Unstable { allow_toggle, .. } => allow_toggle,
|
||||||
Stability::Forbidden { reason } => Err(reason),
|
Stability::Forbidden { reason } => return Err(reason),
|
||||||
}
|
};
|
||||||
|
if enable { allow_toggle.enable } else { allow_toggle.disable }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Constructors for the list below, defaulting to "always allow toggle".
|
// Constructors for the list below, defaulting to "always allow toggle".
|
||||||
const STABLE: StabilityUncomputed = Stability::Stable { allow_toggle: |_target| Ok(()) };
|
const STABLE: StabilityUncomputed = Stability::Stable { allow_toggle: |_target, _enable| Ok(()) };
|
||||||
const fn unstable(nightly_feature: Symbol) -> StabilityUncomputed {
|
const fn unstable(nightly_feature: Symbol) -> StabilityUncomputed {
|
||||||
Stability::Unstable { nightly_feature, allow_toggle: |_target| Ok(()) }
|
Stability::Unstable { nightly_feature, allow_toggle: |_target, _enable| Ok(()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Here we list target features that rustc "understands": they can be used in `#[target_feature]`
|
// Here we list target features that rustc "understands": they can be used in `#[target_feature]`
|
||||||
|
@ -184,7 +217,7 @@ const ARM_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[
|
||||||
"fpregs",
|
"fpregs",
|
||||||
Stability::Unstable {
|
Stability::Unstable {
|
||||||
nightly_feature: sym::arm_target_feature,
|
nightly_feature: sym::arm_target_feature,
|
||||||
allow_toggle: |target: &Target| {
|
allow_toggle: |target: &Target, _enable| {
|
||||||
// Only allow toggling this if the target has `soft-float` set. With `soft-float`,
|
// Only allow toggling this if the target has `soft-float` set. With `soft-float`,
|
||||||
// `fpregs` isn't needed so changing it cannot affect the ABI.
|
// `fpregs` isn't needed so changing it cannot affect the ABI.
|
||||||
if target.has_feature("soft-float") {
|
if target.has_feature("soft-float") {
|
||||||
|
@ -481,7 +514,7 @@ const X86_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[
|
||||||
"x87",
|
"x87",
|
||||||
Stability::Unstable {
|
Stability::Unstable {
|
||||||
nightly_feature: sym::x87_target_feature,
|
nightly_feature: sym::x87_target_feature,
|
||||||
allow_toggle: |target: &Target| {
|
allow_toggle: |target: &Target, _enable| {
|
||||||
// Only allow toggling this if the target has `soft-float` set. With `soft-float`,
|
// Only allow toggling this if the target has `soft-float` set. With `soft-float`,
|
||||||
// `fpregs` isn't needed so changing it cannot affect the ABI.
|
// `fpregs` isn't needed so changing it cannot affect the ABI.
|
||||||
if target.has_feature("soft-float") {
|
if target.has_feature("soft-float") {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue