Auto merge of #129884 - RalfJung:forbidden-target-features, r=workingjubilee

mark some target features as 'forbidden' so they cannot be (un)set with -Ctarget-feature

The context for this is https://github.com/rust-lang/rust/issues/116344: some target features change the way floats are passed between functions. Changing those target features is unsound as code compiled for the same target may now use different ABIs.

So this introduces a new concept of "forbidden" target features (on top of the existing "stable " and "unstable" categories), and makes it a hard error to (un)set such a target feature. For now, the x86 and ARM feature `soft-float` is on that list. We'll have to make some effort to collect more relevant features, and similar features from other targets, but that can happen after the basic infrastructure for this landed. (These features are being collected in https://github.com/rust-lang/rust/issues/131799.)

I've made this a warning for now to give people some time to speak up if this would break something.

MCP: https://github.com/rust-lang/compiler-team/issues/780
This commit is contained in:
bors 2024-11-05 16:25:45 +00:00
commit e8c698bb3b
23 changed files with 372 additions and 158 deletions

View File

@ -8,6 +8,9 @@ codegen_gcc_invalid_minimum_alignment =
codegen_gcc_lto_not_supported = codegen_gcc_lto_not_supported =
LTO is not supported. You may get a linker error. LTO is not supported. You may get a linker error.
codegen_gcc_forbidden_ctarget_feature =
target feature `{$feature}` cannot be toggled with `-Ctarget-feature`
codegen_gcc_unwinding_inline_asm = codegen_gcc_unwinding_inline_asm =
GCC backend does not support unwinding from inline asm GCC backend does not support unwinding from inline asm
@ -24,11 +27,15 @@ codegen_gcc_lto_dylib = lto cannot be used for `dylib` crate type without `-Zdyl
codegen_gcc_lto_bitcode_from_rlib = failed to get bitcode from object file for LTO ({$gcc_err}) codegen_gcc_lto_bitcode_from_rlib = failed to get bitcode from object file for LTO ({$gcc_err})
codegen_gcc_unknown_ctarget_feature = codegen_gcc_unknown_ctarget_feature =
unknown feature specified for `-Ctarget-feature`: `{$feature}` unknown and unstable feature specified for `-Ctarget-feature`: `{$feature}`
.note = it is still passed through to the codegen backend .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}` .possible_feature = you might have meant: `{$rust_feature}`
.consider_filing_feature_request = consider filing a feature request .consider_filing_feature_request = consider filing a feature request
codegen_gcc_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_gcc_missing_features = codegen_gcc_missing_features =
add the missing features in a `target_feature` attribute add the missing features in a `target_feature` attribute

View File

@ -17,6 +17,19 @@ pub(crate) struct UnknownCTargetFeature<'a> {
pub rust_feature: PossibleFeature<'a>, pub rust_feature: PossibleFeature<'a>,
} }
#[derive(Diagnostic)]
#[diag(codegen_gcc_unstable_ctarget_feature)]
#[note]
pub(crate) struct UnstableCTargetFeature<'a> {
pub feature: &'a str,
}
#[derive(Diagnostic)]
#[diag(codegen_gcc_forbidden_ctarget_feature)]
pub(crate) struct ForbiddenCTargetFeature<'a> {
pub feature: &'a str,
}
#[derive(Subdiagnostic)] #[derive(Subdiagnostic)]
pub(crate) enum PossibleFeature<'a> { pub(crate) enum PossibleFeature<'a> {
#[help(codegen_gcc_possible_feature)] #[help(codegen_gcc_possible_feature)]

View File

@ -5,10 +5,13 @@ use rustc_codegen_ssa::errors::TargetFeatureDisableOrEnable;
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_middle::bug; use rustc_middle::bug;
use rustc_session::Session; use rustc_session::Session;
use rustc_target::target_features::RUSTC_SPECIFIC_FEATURES; use rustc_target::target_features::{RUSTC_SPECIFIC_FEATURES, Stability};
use smallvec::{SmallVec, smallvec}; use smallvec::{SmallVec, smallvec};
use crate::errors::{PossibleFeature, UnknownCTargetFeature, UnknownCTargetFeaturePrefix}; use crate::errors::{
ForbiddenCTargetFeature, PossibleFeature, UnknownCTargetFeature, UnknownCTargetFeaturePrefix,
UnstableCTargetFeature,
};
/// The list of GCC features computed from CLI flags (`-Ctarget-cpu`, `-Ctarget-feature`, /// The list of GCC features computed from CLI flags (`-Ctarget-cpu`, `-Ctarget-feature`,
/// `--target` and similar). /// `--target` and similar).
@ -43,7 +46,7 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec<Stri
); );
// -Ctarget-features // -Ctarget-features
let supported_features = sess.target.supported_target_features(); let known_features = sess.target.rust_target_features();
let mut featsmap = FxHashMap::default(); let mut featsmap = FxHashMap::default();
let feats = sess let feats = sess
.opts .opts
@ -62,12 +65,20 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec<Stri
} }
}; };
// Get the backend feature name, if any.
// This excludes rustc-specific features, that do not get passed down to GCC.
let feature = backend_feature_name(s)?; let feature = backend_feature_name(s)?;
// Warn against use of GCC specific feature names on the CLI. // Warn against use of GCC specific feature names on the CLI.
if diagnostics && !supported_features.iter().any(|&(v, _, _)| v == feature) { if diagnostics {
let rust_feature = supported_features.iter().find_map(|&(rust_feature, _, _)| { 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); let gcc_features = to_gcc_features(sess, rust_feature);
if gcc_features.contains(&feature) && !gcc_features.contains(&rust_feature) { if gcc_features.contains(&feature)
&& !gcc_features.contains(&rust_feature)
{
Some(rust_feature) Some(rust_feature)
} else { } else {
None None
@ -83,16 +94,20 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec<Stri
}; };
sess.dcx().emit_warn(unknown_feature); sess.dcx().emit_warn(unknown_feature);
} }
Some((_, Stability::Stable, _)) => {}
Some((_, Stability::Unstable(_), _)) => {
// An unstable feature. Warn about using it.
sess.dcx().emit_warn(UnstableCTargetFeature { feature });
}
Some((_, Stability::Forbidden { .. }, _)) => {
sess.dcx().emit_err(ForbiddenCTargetFeature { feature });
}
}
if diagnostics {
// FIXME(nagisa): figure out how to not allocate a full hashset here. // FIXME(nagisa): figure out how to not allocate a full hashset here.
featsmap.insert(feature, enable_disable == '+'); featsmap.insert(feature, enable_disable == '+');
} }
// rustc-specific features do not get passed down to GCC…
if RUSTC_SPECIFIC_FEATURES.contains(&feature) {
return None;
}
// ... otherwise though we run through `to_gcc_features` when // ... otherwise though we run through `to_gcc_features` when
// passing requests down to GCC. This means that all in-language // passing requests down to GCC. This means that all in-language
// features also work on the command line instead of having two // features also work on the command line instead of having two

View File

@ -491,8 +491,9 @@ pub fn target_features(
) -> Vec<Symbol> { ) -> Vec<Symbol> {
// TODO(antoyo): use global_gcc_features. // TODO(antoyo): use global_gcc_features.
sess.target sess.target
.supported_target_features() .rust_target_features()
.iter() .iter()
.filter(|(_, gate, _)| gate.is_supported())
.filter_map(|&(feature, gate, _)| { .filter_map(|&(feature, gate, _)| {
if sess.is_nightly_build() || allow_unstable || gate.is_stable() { if sess.is_nightly_build() || allow_unstable || gate.is_stable() {
Some(feature) Some(feature)

View File

@ -7,6 +7,11 @@ codegen_llvm_dynamic_linking_with_lto =
codegen_llvm_fixed_x18_invalid_arch = the `-Zfixed-x18` flag is not supported on the `{$arch}` architecture codegen_llvm_fixed_x18_invalid_arch = the `-Zfixed-x18` flag is not supported on the `{$arch}` architecture
codegen_llvm_forbidden_ctarget_feature =
target feature `{$feature}` cannot be toggled 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_llvm_forbidden_ctarget_feature_issue = for more information, see issue #116344 <https://github.com/rust-lang/rust/issues/116344>
codegen_llvm_from_llvm_diag = {$message} codegen_llvm_from_llvm_diag = {$message}
codegen_llvm_from_llvm_optimization_diag = {$filename}:{$line}:{$column} {$pass_name} ({$kind}): {$message} codegen_llvm_from_llvm_optimization_diag = {$filename}:{$line}:{$column} {$pass_name} ({$kind}): {$message}

View File

@ -31,6 +31,15 @@ pub(crate) struct UnstableCTargetFeature<'a> {
pub feature: &'a str, pub feature: &'a str,
} }
#[derive(Diagnostic)]
#[diag(codegen_llvm_forbidden_ctarget_feature)]
#[note]
#[note(codegen_llvm_forbidden_ctarget_feature_issue)]
pub(crate) struct ForbiddenCTargetFeature<'a> {
pub feature: &'a str,
pub reason: &'a str,
}
#[derive(Subdiagnostic)] #[derive(Subdiagnostic)]
pub(crate) enum PossibleFeature<'a> { pub(crate) enum PossibleFeature<'a> {
#[help(codegen_llvm_possible_feature)] #[help(codegen_llvm_possible_feature)]

View File

@ -17,12 +17,12 @@ use rustc_session::Session;
use rustc_session::config::{PrintKind, PrintRequest}; use rustc_session::config::{PrintKind, PrintRequest};
use rustc_span::symbol::Symbol; use rustc_span::symbol::Symbol;
use rustc_target::spec::{MergeFunctions, PanicStrategy, SmallDataThresholdSupport}; use rustc_target::spec::{MergeFunctions, PanicStrategy, SmallDataThresholdSupport};
use rustc_target::target_features::{RUSTC_SPECIAL_FEATURES, RUSTC_SPECIFIC_FEATURES}; use rustc_target::target_features::{RUSTC_SPECIAL_FEATURES, RUSTC_SPECIFIC_FEATURES, Stability};
use crate::back::write::create_informational_target_machine; use crate::back::write::create_informational_target_machine;
use crate::errors::{ use crate::errors::{
FixedX18InvalidArch, InvalidTargetFeaturePrefix, PossibleFeature, UnknownCTargetFeature, FixedX18InvalidArch, ForbiddenCTargetFeature, InvalidTargetFeaturePrefix, PossibleFeature,
UnknownCTargetFeaturePrefix, UnstableCTargetFeature, UnknownCTargetFeature, UnknownCTargetFeaturePrefix, UnstableCTargetFeature,
}; };
use crate::llvm; use crate::llvm;
@ -281,19 +281,29 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFea
} }
} }
/// Used to generate cfg variables and apply features /// Used to generate cfg variables and apply features.
/// Must express features in the way Rust understands them /// Must express features in the way Rust understands them.
///
/// We do not have to worry about RUSTC_SPECIFIC_FEATURES here, those are handled outside codegen.
pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec<Symbol> { pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec<Symbol> {
let mut features = vec![]; let mut features: FxHashSet<Symbol> = Default::default();
// Add base features for the target // Add base features for the target.
// We do *not* add the -Ctarget-features there, and instead duplicate the logic for that below.
// The reason is that if LLVM considers a feature implied but we do not, we don't want that to
// show up in `cfg`. That way, `cfg` is entirely under our control -- except for the handling of
// 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); 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.
features.extend( features.extend(
sess.target sess.target
.supported_target_features() .rust_target_features()
.iter() .iter()
.filter(|(_, gate, _)| gate.is_supported())
.filter(|(feature, _, _)| { .filter(|(feature, _, _)| {
// skip checking special features, as LLVM may not understands them // skip checking special features, as LLVM may not understand them
if RUSTC_SPECIAL_FEATURES.contains(feature) { if RUSTC_SPECIAL_FEATURES.contains(feature) {
return true; return true;
} }
@ -324,7 +334,12 @@ pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec<Symbol> {
if enabled { if enabled {
features.extend(sess.target.implied_target_features(std::iter::once(feature))); features.extend(sess.target.implied_target_features(std::iter::once(feature)));
} else { } else {
// 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| { features.retain(|f| {
// Keep a feature if it does not imply `feature`. Or, equivalently,
// remove the reverse-dependencies of `feature`.
!sess.target.implied_target_features(std::iter::once(*f)).contains(&feature) !sess.target.implied_target_features(std::iter::once(*f)).contains(&feature)
}); });
} }
@ -332,8 +347,9 @@ pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec<Symbol> {
// Filter enabled features based on feature gates // Filter enabled features based on feature gates
sess.target sess.target
.supported_target_features() .rust_target_features()
.iter() .iter()
.filter(|(_, gate, _)| gate.is_supported())
.filter_map(|&(feature, gate, _)| { .filter_map(|&(feature, gate, _)| {
if sess.is_nightly_build() || allow_unstable || gate.is_stable() { if sess.is_nightly_build() || allow_unstable || gate.is_stable() {
Some(feature) Some(feature)
@ -451,9 +467,13 @@ fn print_target_features(sess: &Session, tm: &llvm::TargetMachine, out: &mut Str
let mut known_llvm_target_features = FxHashSet::<&'static str>::default(); let mut known_llvm_target_features = FxHashSet::<&'static str>::default();
let mut rustc_target_features = sess let mut rustc_target_features = sess
.target .target
.supported_target_features() .rust_target_features()
.iter() .iter()
.filter_map(|(feature, _gate, _implied)| { .filter_map(|(feature, gate, _implied)| {
if !gate.is_supported() {
// Only list (experimentally) supported features.
return None;
}
// LLVM asserts that these are sorted. LLVM and Rust both use byte comparison for these // LLVM asserts that these are sorted. LLVM and Rust both use byte comparison for these
// strings. // strings.
let llvm_feature = to_llvm_features(sess, *feature)?.llvm_feature_name; let llvm_feature = to_llvm_features(sess, *feature)?.llvm_feature_name;
@ -605,7 +625,7 @@ pub(crate) fn global_llvm_features(
// -Ctarget-features // -Ctarget-features
if !only_base_features { if !only_base_features {
let supported_features = sess.target.supported_target_features(); let known_features = sess.target.rust_target_features();
let mut featsmap = FxHashMap::default(); let mut featsmap = FxHashMap::default();
// insert implied features // insert implied features
@ -639,13 +659,16 @@ pub(crate) fn global_llvm_features(
} }
}; };
// Get the backend feature name, if any.
// This excludes rustc-specific features, which do not get passed to LLVM.
let feature = backend_feature_name(sess, s)?; let feature = backend_feature_name(sess, s)?;
// Warn against use of LLVM specific feature names and unstable features on the CLI. // Warn against use of LLVM specific feature names and unstable features on the CLI.
if diagnostics { if diagnostics {
let feature_state = supported_features.iter().find(|&&(v, _, _)| v == feature); let feature_state = known_features.iter().find(|&&(v, _, _)| v == feature);
if feature_state.is_none() { match feature_state {
None => {
let rust_feature = let rust_feature =
supported_features.iter().find_map(|&(rust_feature, _, _)| { known_features.iter().find_map(|&(rust_feature, _, _)| {
let llvm_features = to_llvm_features(sess, rust_feature)?; let llvm_features = to_llvm_features(sess, rust_feature)?;
if llvm_features.contains(feature) if llvm_features.contains(feature)
&& !llvm_features.contains(rust_feature) && !llvm_features.contains(rust_feature)
@ -661,28 +684,28 @@ pub(crate) fn global_llvm_features(
rust_feature: PossibleFeature::Some { rust_feature }, rust_feature: PossibleFeature::Some { rust_feature },
} }
} else { } else {
UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None } UnknownCTargetFeature {
feature,
rust_feature: PossibleFeature::None,
}
}; };
sess.dcx().emit_warn(unknown_feature); sess.dcx().emit_warn(unknown_feature);
} else if feature_state }
.is_some_and(|(_name, feature_gate, _implied)| !feature_gate.is_stable()) Some((_, Stability::Stable, _)) => {}
{ Some((_, Stability::Unstable(_), _)) => {
// An unstable feature. Warn about using it. // An unstable feature. Warn about using it.
sess.dcx().emit_warn(UnstableCTargetFeature { feature }); sess.dcx().emit_warn(UnstableCTargetFeature { feature });
} }
Some((_, Stability::Forbidden { reason }, _)) => {
sess.dcx().emit_warn(ForbiddenCTargetFeature { feature, reason });
}
} }
if diagnostics {
// FIXME(nagisa): figure out how to not allocate a full hashset here. // FIXME(nagisa): figure out how to not allocate a full hashset here.
featsmap.insert(feature, enable_disable == '+'); featsmap.insert(feature, enable_disable == '+');
} }
// rustc-specific features do not get passed down to LLVM… // We run through `to_llvm_features` when
if RUSTC_SPECIFIC_FEATURES.contains(&feature) {
return None;
}
// ... otherwise though we run through `to_llvm_features` when
// passing requests down to LLVM. This means that all in-language // passing requests down to LLVM. This means that all in-language
// features also work on the command line instead of having two // features also work on the command line instead of having two
// different names when the LLVM name and the Rust name differ. // different names when the LLVM name and the Rust name differ.

View File

@ -64,6 +64,9 @@ codegen_ssa_failed_to_write = failed to write {$path}: {$error}
codegen_ssa_field_associated_value_expected = associated value expected for `{$name}` codegen_ssa_field_associated_value_expected = associated value expected for `{$name}`
codegen_ssa_forbidden_target_feature_attr =
target feature `{$feature}` cannot be toggled with `#[target_feature]`: {$reason}
codegen_ssa_ignoring_emit_path = ignoring emit path because multiple .{$extension} files were produced codegen_ssa_ignoring_emit_path = ignoring emit path because multiple .{$extension} files were produced
codegen_ssa_ignoring_output = ignoring -o because multiple .{$extension} files were produced codegen_ssa_ignoring_output = ignoring -o because multiple .{$extension} files were produced

View File

@ -20,8 +20,8 @@ use rustc_span::symbol::Ident;
use rustc_span::{Span, sym}; use rustc_span::{Span, sym};
use rustc_target::spec::{SanitizerSet, abi}; use rustc_target::spec::{SanitizerSet, abi};
use crate::errors::{self, MissingFeatures, TargetFeatureDisableOrEnable}; use crate::errors;
use crate::target_features::{check_target_feature_trait_unsafe, from_target_feature}; use crate::target_features::{check_target_feature_trait_unsafe, from_target_feature_attr};
fn linkage_by_name(tcx: TyCtxt<'_>, def_id: LocalDefId, name: &str) -> Linkage { fn linkage_by_name(tcx: TyCtxt<'_>, def_id: LocalDefId, name: &str) -> Linkage {
use rustc_middle::mir::mono::Linkage::*; use rustc_middle::mir::mono::Linkage::*;
@ -73,7 +73,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_BUILTINS; codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_BUILTINS;
} }
let supported_target_features = tcx.supported_target_features(LOCAL_CRATE); let rust_target_features = tcx.rust_target_features(LOCAL_CRATE);
let mut inline_span = None; let mut inline_span = None;
let mut link_ordinal_span = None; let mut link_ordinal_span = None;
@ -281,10 +281,10 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
check_target_feature_trait_unsafe(tcx, did, attr.span); check_target_feature_trait_unsafe(tcx, did, attr.span);
} }
} }
from_target_feature( from_target_feature_attr(
tcx, tcx,
attr, attr,
supported_target_features, rust_target_features,
&mut codegen_fn_attrs.target_features, &mut codegen_fn_attrs.target_features,
); );
} }
@ -676,10 +676,10 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
.next() .next()
.map_or_else(|| tcx.def_span(did), |a| a.span); .map_or_else(|| tcx.def_span(did), |a| a.span);
tcx.dcx() tcx.dcx()
.create_err(TargetFeatureDisableOrEnable { .create_err(errors::TargetFeatureDisableOrEnable {
features, features,
span: Some(span), span: Some(span),
missing_features: Some(MissingFeatures), missing_features: Some(errors::MissingFeatures),
}) })
.emit(); .emit();
} }

View File

@ -1027,6 +1027,15 @@ pub(crate) struct TargetFeatureSafeTrait {
pub def: Span, pub def: Span,
} }
#[derive(Diagnostic)]
#[diag(codegen_ssa_forbidden_target_feature_attr)]
pub struct ForbiddenTargetFeatureAttr<'a> {
#[primary_span]
pub span: Span,
pub feature: &'a str,
pub reason: &'a str,
}
#[derive(Diagnostic)] #[derive(Diagnostic)]
#[diag(codegen_ssa_failed_to_get_layout)] #[diag(codegen_ssa_failed_to_get_layout)]
pub struct FailedToGetLayout<'tcx> { pub struct FailedToGetLayout<'tcx> {

View File

@ -11,13 +11,16 @@ use rustc_middle::ty::TyCtxt;
use rustc_session::parse::feature_err; use rustc_session::parse::feature_err;
use rustc_span::Span; use rustc_span::Span;
use rustc_span::symbol::{Symbol, sym}; use rustc_span::symbol::{Symbol, sym};
use rustc_target::target_features::{self, Stability};
use crate::errors; use crate::errors;
pub(crate) fn from_target_feature( /// Compute the enabled target features from the `#[target_feature]` function attribute.
/// Enabled target features are added to `target_features`.
pub(crate) fn from_target_feature_attr(
tcx: TyCtxt<'_>, tcx: TyCtxt<'_>,
attr: &ast::Attribute, attr: &ast::Attribute,
supported_target_features: &UnordMap<String, Option<Symbol>>, rust_target_features: &UnordMap<String, target_features::Stability>,
target_features: &mut Vec<TargetFeature>, target_features: &mut Vec<TargetFeature>,
) { ) {
let Some(list) = attr.meta_item_list() else { return }; let Some(list) = attr.meta_item_list() else { return };
@ -46,12 +49,12 @@ pub(crate) fn from_target_feature(
// We allow comma separation to enable multiple features. // We allow comma separation to enable multiple features.
added_target_features.extend(value.as_str().split(',').filter_map(|feature| { added_target_features.extend(value.as_str().split(',').filter_map(|feature| {
let Some(feature_gate) = supported_target_features.get(feature) else { let Some(stability) = rust_target_features.get(feature) else {
let msg = format!("the feature named `{feature}` is not valid for this target"); let msg = format!("the feature named `{feature}` is not valid for this target");
let mut err = tcx.dcx().struct_span_err(item.span(), msg); let mut err = tcx.dcx().struct_span_err(item.span(), msg);
err.span_label(item.span(), format!("`{feature}` is not valid for this target")); err.span_label(item.span(), format!("`{feature}` is not valid for this target"));
if let Some(stripped) = feature.strip_prefix('+') { if let Some(stripped) = feature.strip_prefix('+') {
let valid = supported_target_features.contains_key(stripped); let valid = rust_target_features.contains_key(stripped);
if valid { if valid {
err.help("consider removing the leading `+` in the feature name"); err.help("consider removing the leading `+` in the feature name");
} }
@ -61,19 +64,32 @@ pub(crate) fn from_target_feature(
}; };
// Only allow target features whose feature gates have been enabled. // Only allow target features whose feature gates have been enabled.
let allowed = match feature_gate.as_ref().copied() { let allowed = match stability {
Some(name) => rust_features.enabled(name), Stability::Forbidden { .. } => false,
None => true, Stability::Stable => true,
Stability::Unstable(name) => rust_features.enabled(*name),
}; };
if !allowed { if !allowed {
match stability {
Stability::Stable => unreachable!(),
&Stability::Unstable(lang_feature_name) => {
feature_err( feature_err(
&tcx.sess, &tcx.sess,
feature_gate.unwrap(), lang_feature_name,
item.span(), item.span(),
format!("the target feature `{feature}` is currently unstable"), format!("the target feature `{feature}` is currently unstable"),
) )
.emit(); .emit();
} }
Stability::Forbidden { reason } => {
tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr {
span: item.span(),
feature,
reason,
});
}
}
}
Some(Symbol::intern(feature)) Some(Symbol::intern(feature))
})); }));
} }
@ -138,20 +154,20 @@ pub(crate) fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId,
pub(crate) fn provide(providers: &mut Providers) { pub(crate) fn provide(providers: &mut Providers) {
*providers = Providers { *providers = Providers {
supported_target_features: |tcx, cnum| { rust_target_features: |tcx, cnum| {
assert_eq!(cnum, LOCAL_CRATE); assert_eq!(cnum, LOCAL_CRATE);
if tcx.sess.opts.actually_rustdoc { if tcx.sess.opts.actually_rustdoc {
// rustdoc needs to be able to document functions that use all the features, so // rustdoc needs to be able to document functions that use all the features, so
// whitelist them all // whitelist them all
rustc_target::target_features::all_known_features() rustc_target::target_features::all_rust_features()
.map(|(a, b)| (a.to_string(), b.as_feature_name())) .map(|(a, b)| (a.to_string(), b))
.collect() .collect()
} else { } else {
tcx.sess tcx.sess
.target .target
.supported_target_features() .rust_target_features()
.iter() .iter()
.map(|&(a, b, _)| (a.to_string(), b.as_feature_name())) .map(|&(a, b, _)| (a.to_string(), b))
.collect() .collect()
} }
}, },

View File

@ -2200,10 +2200,11 @@ rustc_queries! {
desc { "computing autoderef types for `{}`", goal.canonical.value.value } desc { "computing autoderef types for `{}`", goal.canonical.value.value }
} }
query supported_target_features(_: CrateNum) -> &'tcx UnordMap<String, Option<Symbol>> { /// Returns the Rust target features for the current target. These are not always the same as LLVM target features!
query rust_target_features(_: CrateNum) -> &'tcx UnordMap<String, rustc_target::target_features::Stability> {
arena_cache arena_cache
eval_always eval_always
desc { "looking up supported target features" } desc { "looking up Rust target features" }
} }
query implied_target_features(feature: Symbol) -> &'tcx Vec<Symbol> { query implied_target_features(feature: Symbol) -> &'tcx Vec<Symbol> {

View File

@ -370,8 +370,9 @@ impl CheckCfg {
ins!(sym::sanitizer_cfi_normalize_integers, no_values); ins!(sym::sanitizer_cfi_normalize_integers, no_values);
ins!(sym::target_feature, empty_values).extend( ins!(sym::target_feature, empty_values).extend(
rustc_target::target_features::all_known_features() rustc_target::target_features::all_rust_features()
.map(|(f, _sb)| f) .filter(|(_, s)| s.is_supported())
.map(|(f, _s)| f)
.chain(rustc_target::target_features::RUSTC_SPECIFIC_FEATURES.iter().cloned()) .chain(rustc_target::target_features::RUSTC_SPECIFIC_FEATURES.iter().cloned())
.map(Symbol::intern), .map(Symbol::intern),
); );

View File

@ -1,10 +1,17 @@
//! Declares Rust's target feature names for each target.
//! Note that these are similar to but not always identical to LLVM's feature names,
//! and Rust adds some features that do not correspond to LLVM features at all.
use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_span::symbol::{Symbol, sym}; use rustc_span::symbol::{Symbol, sym};
/// Features that control behaviour of rustc, rather than the codegen. /// Features that control behaviour of rustc, rather than the codegen.
/// These exist globally and are not in the target-specific lists below.
pub const RUSTC_SPECIFIC_FEATURES: &[&str] = &["crt-static"]; pub const RUSTC_SPECIFIC_FEATURES: &[&str] = &["crt-static"];
/// Features that require special handling when passing to LLVM. /// 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"]; pub const RUSTC_SPECIAL_FEATURES: &[&str] = &["backchain"];
/// Stability information for target features. /// Stability information for target features.
@ -16,26 +23,47 @@ pub enum Stability {
/// This target feature is unstable; using it in `#[target_feature]` or `#[cfg(target_feature)]` /// This target feature is unstable; using it in `#[target_feature]` or `#[cfg(target_feature)]`
/// requires enabling the given nightly feature. /// requires enabling the given nightly feature.
Unstable(Symbol), Unstable(Symbol),
/// This feature can not be set via `-Ctarget-feature` or `#[target_feature]`, it can only be set in the basic
/// target definition. Used in particular for features that change the floating-point ABI.
Forbidden { reason: &'static str },
} }
use Stability::*; use Stability::*;
impl Stability { impl<CTX> HashStable<CTX> for Stability {
pub fn as_feature_name(self) -> Option<Symbol> { #[inline]
fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
std::mem::discriminant(self).hash_stable(hcx, hasher);
match self { match self {
Stable => None, Stable => {}
Unstable(s) => Some(s), Unstable(sym) => {
sym.hash_stable(hcx, hasher);
}
Forbidden { .. } => {}
} }
} }
}
impl Stability {
pub fn is_stable(self) -> bool { pub fn is_stable(self) -> bool {
matches!(self, Stable) matches!(self, Stable)
} }
/// Forbidden features are not supported.
pub fn is_supported(self) -> bool {
!matches!(self, Forbidden { .. })
}
} }
// 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]`
// and `#[cfg(target_feature)]`. They also do not trigger any warnings when used with // and `#[cfg(target_feature)]`. They also do not trigger any warnings when used with
// `-Ctarget-feature`. // `-Ctarget-feature`.
// //
// Note that even unstable (and even entirely unlisted) features can be used with `-Ctarget-feature`
// on stable. Using a feature not on the list of Rust target features only emits a warning.
// Only `cfg(target_feature)` and `#[target_feature]` actually do any stability gating.
// `cfg(target_feature)` for unstable features just works on nightly without any feature gate.
// `#[target_feature]` requires a feature gate.
//
// When adding features to the below lists // When adding features to the below lists
// check whether they're named already elsewhere in rust // check whether they're named already elsewhere in rust
// e.g. in stdarch and whether the given name matches LLVM's // e.g. in stdarch and whether the given name matches LLVM's
@ -46,17 +74,27 @@ impl Stability {
// per-function level, since we would then allow safe calls from functions with `+soft-float` to // per-function level, since we would then allow safe calls from functions with `+soft-float` to
// functions without that feature! // functions without that feature!
// //
// When adding a new feature, be particularly mindful of features that affect function ABIs. Those // It is important for soundness that features allowed here do *not* change the function call ABI.
// need to be treated very carefully to avoid introducing unsoundness! This often affects features // For example, disabling the `x87` feature on x86 changes how scalar floats are passed as
// that enable/disable hardfloat support (see https://github.com/rust-lang/rust/issues/116344 for an // arguments, so enabling toggling that feature would be unsound. In fact, since `-Ctarget-feature`
// example of this going wrong), but features enabling new SIMD registers are also a concern (see // will just allow unknown features (with a warning), we have to explicitly list features that change
// https://github.com/rust-lang/rust/issues/116558 for an example of this going wrong). // the ABI as `Forbidden` to ensure using them causes an error. Note that this is only effective if
// such features can never be toggled via `-Ctarget-cpu`! If that is ever a possibility, we will need
// extra checks ensuring that the LLVM-computed target features for a CPU did not (un)set a
// `Forbidden` feature. See https://github.com/rust-lang/rust/issues/116344 for some more context.
// FIXME: add such "forbidden" features for non-x86 targets.
//
// The one exception to features that change the ABI is features that enable larger vector
// registers. Those are permitted to be listed here. This is currently unsound (see
// https://github.com/rust-lang/rust/issues/116558); in the future we will have to ensure that
// functions can only use such vectors as arguments/return types if the corresponding target feature
// is enabled.
// //
// Stabilizing a target feature requires t-lang approval. // Stabilizing a target feature requires t-lang approval.
type ImpliedFeatures = &'static [&'static str]; type ImpliedFeatures = &'static [&'static str];
const ARM_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ const ARM_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
// tidy-alphabetical-start // tidy-alphabetical-start
("aclass", Unstable(sym::arm_target_feature), &[]), ("aclass", Unstable(sym::arm_target_feature), &[]),
("aes", Unstable(sym::arm_target_feature), &["neon"]), ("aes", Unstable(sym::arm_target_feature), &["neon"]),
@ -70,6 +108,7 @@ const ARM_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
("neon", Unstable(sym::arm_target_feature), &["vfp3"]), ("neon", Unstable(sym::arm_target_feature), &["vfp3"]),
("rclass", Unstable(sym::arm_target_feature), &[]), ("rclass", Unstable(sym::arm_target_feature), &[]),
("sha2", Unstable(sym::arm_target_feature), &["neon"]), ("sha2", Unstable(sym::arm_target_feature), &["neon"]),
("soft-float", Forbidden { reason: "unsound because it changes float ABI" }, &[]),
// This is needed for inline assembly, but shouldn't be stabilized as-is // This is needed for inline assembly, but shouldn't be stabilized as-is
// since it should be enabled per-function using #[instruction_set], not // since it should be enabled per-function using #[instruction_set], not
// #[target_feature]. // #[target_feature].
@ -87,9 +126,10 @@ const ARM_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
("vfp4", Unstable(sym::arm_target_feature), &["vfp3"]), ("vfp4", Unstable(sym::arm_target_feature), &["vfp3"]),
("virtualization", Unstable(sym::arm_target_feature), &[]), ("virtualization", Unstable(sym::arm_target_feature), &[]),
// tidy-alphabetical-end // tidy-alphabetical-end
// FIXME: need to also forbid turning off `fpregs` on hardfloat targets
]; ];
const AARCH64_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ const AARCH64_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
// tidy-alphabetical-start // tidy-alphabetical-start
// FEAT_AES & FEAT_PMULL // FEAT_AES & FEAT_PMULL
("aes", Stable, &["neon"]), ("aes", Stable, &["neon"]),
@ -277,7 +317,7 @@ const AARCH64_TIED_FEATURES: &[&[&str]] = &[
&["paca", "pacg"], // Together these represent `pauth` in LLVM &["paca", "pacg"], // Together these represent `pauth` in LLVM
]; ];
const X86_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ const X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
// tidy-alphabetical-start // tidy-alphabetical-start
("adx", Stable, &[]), ("adx", Stable, &[]),
("aes", Stable, &["sse2"]), ("aes", Stable, &["sse2"]),
@ -328,6 +368,7 @@ const X86_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
("sha512", Unstable(sym::sha512_sm_x86), &["avx2"]), ("sha512", Unstable(sym::sha512_sm_x86), &["avx2"]),
("sm3", Unstable(sym::sha512_sm_x86), &["avx"]), ("sm3", Unstable(sym::sha512_sm_x86), &["avx"]),
("sm4", Unstable(sym::sha512_sm_x86), &["avx2"]), ("sm4", Unstable(sym::sha512_sm_x86), &["avx2"]),
("soft-float", Forbidden { reason: "unsound because it changes float ABI" }, &[]),
("sse", Stable, &[]), ("sse", Stable, &[]),
("sse2", Stable, &["sse"]), ("sse2", Stable, &["sse"]),
("sse3", Stable, &["sse2"]), ("sse3", Stable, &["sse2"]),
@ -344,16 +385,17 @@ const X86_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
("xsaveopt", Stable, &["xsave"]), ("xsaveopt", Stable, &["xsave"]),
("xsaves", Stable, &["xsave"]), ("xsaves", Stable, &["xsave"]),
// tidy-alphabetical-end // tidy-alphabetical-end
// FIXME: need to also forbid turning off `x87` on hardfloat targets
]; ];
const HEXAGON_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ const HEXAGON_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
// tidy-alphabetical-start // tidy-alphabetical-start
("hvx", Unstable(sym::hexagon_target_feature), &[]), ("hvx", Unstable(sym::hexagon_target_feature), &[]),
("hvx-length128b", Unstable(sym::hexagon_target_feature), &["hvx"]), ("hvx-length128b", Unstable(sym::hexagon_target_feature), &["hvx"]),
// tidy-alphabetical-end // tidy-alphabetical-end
]; ];
const POWERPC_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ const POWERPC_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
// tidy-alphabetical-start // tidy-alphabetical-start
("altivec", Unstable(sym::powerpc_target_feature), &[]), ("altivec", Unstable(sym::powerpc_target_feature), &[]),
("partword-atomics", Unstable(sym::powerpc_target_feature), &[]), ("partword-atomics", Unstable(sym::powerpc_target_feature), &[]),
@ -367,7 +409,7 @@ const POWERPC_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
// tidy-alphabetical-end // tidy-alphabetical-end
]; ];
const MIPS_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ const MIPS_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
// tidy-alphabetical-start // tidy-alphabetical-start
("fp64", Unstable(sym::mips_target_feature), &[]), ("fp64", Unstable(sym::mips_target_feature), &[]),
("msa", Unstable(sym::mips_target_feature), &[]), ("msa", Unstable(sym::mips_target_feature), &[]),
@ -375,7 +417,7 @@ const MIPS_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
// tidy-alphabetical-end // tidy-alphabetical-end
]; ];
const RISCV_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ const RISCV_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
// tidy-alphabetical-start // tidy-alphabetical-start
("a", Stable, &["zaamo", "zalrsc"]), ("a", Stable, &["zaamo", "zalrsc"]),
("c", Stable, &[]), ("c", Stable, &[]),
@ -415,7 +457,7 @@ const RISCV_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
// tidy-alphabetical-end // tidy-alphabetical-end
]; ];
const WASM_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ const WASM_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
// tidy-alphabetical-start // tidy-alphabetical-start
("atomics", Unstable(sym::wasm_target_feature), &[]), ("atomics", Unstable(sym::wasm_target_feature), &[]),
("bulk-memory", Stable, &[]), ("bulk-memory", Stable, &[]),
@ -431,10 +473,10 @@ const WASM_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
// tidy-alphabetical-end // tidy-alphabetical-end
]; ];
const BPF_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = const BPF_FEATURES: &[(&str, Stability, ImpliedFeatures)] =
&[("alu32", Unstable(sym::bpf_target_feature), &[])]; &[("alu32", Unstable(sym::bpf_target_feature), &[])];
const CSKY_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ const CSKY_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
// tidy-alphabetical-start // tidy-alphabetical-start
("10e60", Unstable(sym::csky_target_feature), &["7e10"]), ("10e60", Unstable(sym::csky_target_feature), &["7e10"]),
("2e3", Unstable(sym::csky_target_feature), &["e2"]), ("2e3", Unstable(sym::csky_target_feature), &["e2"]),
@ -481,7 +523,7 @@ const CSKY_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
// tidy-alphabetical-end // tidy-alphabetical-end
]; ];
const LOONGARCH_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ const LOONGARCH_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
// tidy-alphabetical-start // tidy-alphabetical-start
("d", Unstable(sym::loongarch_target_feature), &["f"]), ("d", Unstable(sym::loongarch_target_feature), &["f"]),
("f", Unstable(sym::loongarch_target_feature), &[]), ("f", Unstable(sym::loongarch_target_feature), &[]),
@ -495,7 +537,7 @@ const LOONGARCH_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
// tidy-alphabetical-end // tidy-alphabetical-end
]; ];
const IBMZ_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ const IBMZ_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
// tidy-alphabetical-start // tidy-alphabetical-start
("backchain", Unstable(sym::s390x_target_feature), &[]), ("backchain", Unstable(sym::s390x_target_feature), &[]),
("vector", Unstable(sym::s390x_target_feature), &[]), ("vector", Unstable(sym::s390x_target_feature), &[]),
@ -506,41 +548,39 @@ const IBMZ_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
/// primitives may be documented. /// primitives may be documented.
/// ///
/// IMPORTANT: If you're adding another feature list above, make sure to add it to this iterator! /// IMPORTANT: If you're adding another feature list above, make sure to add it to this iterator!
pub fn all_known_features() -> impl Iterator<Item = (&'static str, Stability)> { pub fn all_rust_features() -> impl Iterator<Item = (&'static str, Stability)> {
std::iter::empty() std::iter::empty()
.chain(ARM_ALLOWED_FEATURES.iter()) .chain(ARM_FEATURES.iter())
.chain(AARCH64_ALLOWED_FEATURES.iter()) .chain(AARCH64_FEATURES.iter())
.chain(X86_ALLOWED_FEATURES.iter()) .chain(X86_FEATURES.iter())
.chain(HEXAGON_ALLOWED_FEATURES.iter()) .chain(HEXAGON_FEATURES.iter())
.chain(POWERPC_ALLOWED_FEATURES.iter()) .chain(POWERPC_FEATURES.iter())
.chain(MIPS_ALLOWED_FEATURES.iter()) .chain(MIPS_FEATURES.iter())
.chain(RISCV_ALLOWED_FEATURES.iter()) .chain(RISCV_FEATURES.iter())
.chain(WASM_ALLOWED_FEATURES.iter()) .chain(WASM_FEATURES.iter())
.chain(BPF_ALLOWED_FEATURES.iter()) .chain(BPF_FEATURES.iter())
.chain(CSKY_ALLOWED_FEATURES) .chain(CSKY_FEATURES)
.chain(LOONGARCH_ALLOWED_FEATURES) .chain(LOONGARCH_FEATURES)
.chain(IBMZ_ALLOWED_FEATURES) .chain(IBMZ_FEATURES)
.cloned() .cloned()
.map(|(f, s, _)| (f, s)) .map(|(f, s, _)| (f, s))
} }
impl super::spec::Target { impl super::spec::Target {
pub fn supported_target_features( pub fn rust_target_features(&self) -> &'static [(&'static str, Stability, ImpliedFeatures)] {
&self,
) -> &'static [(&'static str, Stability, ImpliedFeatures)] {
match &*self.arch { match &*self.arch {
"arm" => ARM_ALLOWED_FEATURES, "arm" => ARM_FEATURES,
"aarch64" | "arm64ec" => AARCH64_ALLOWED_FEATURES, "aarch64" | "arm64ec" => AARCH64_FEATURES,
"x86" | "x86_64" => X86_ALLOWED_FEATURES, "x86" | "x86_64" => X86_FEATURES,
"hexagon" => HEXAGON_ALLOWED_FEATURES, "hexagon" => HEXAGON_FEATURES,
"mips" | "mips32r6" | "mips64" | "mips64r6" => MIPS_ALLOWED_FEATURES, "mips" | "mips32r6" | "mips64" | "mips64r6" => MIPS_FEATURES,
"powerpc" | "powerpc64" => POWERPC_ALLOWED_FEATURES, "powerpc" | "powerpc64" => POWERPC_FEATURES,
"riscv32" | "riscv64" => RISCV_ALLOWED_FEATURES, "riscv32" | "riscv64" => RISCV_FEATURES,
"wasm32" | "wasm64" => WASM_ALLOWED_FEATURES, "wasm32" | "wasm64" => WASM_FEATURES,
"bpf" => BPF_ALLOWED_FEATURES, "bpf" => BPF_FEATURES,
"csky" => CSKY_ALLOWED_FEATURES, "csky" => CSKY_FEATURES,
"loongarch64" => LOONGARCH_ALLOWED_FEATURES, "loongarch64" => LOONGARCH_FEATURES,
"s390x" => IBMZ_ALLOWED_FEATURES, "s390x" => IBMZ_FEATURES,
_ => &[], _ => &[],
} }
} }
@ -557,7 +597,7 @@ impl super::spec::Target {
base_features: impl Iterator<Item = Symbol>, base_features: impl Iterator<Item = Symbol>,
) -> FxHashSet<Symbol> { ) -> FxHashSet<Symbol> {
let implied_features = self let implied_features = self
.supported_target_features() .rust_target_features()
.iter() .iter()
.map(|(f, _, i)| (Symbol::intern(f), i)) .map(|(f, _, i)| (Symbol::intern(f), i))
.collect::<FxHashMap<_, _>>(); .collect::<FxHashMap<_, _>>();

View File

@ -0,0 +1,12 @@
//@ compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=lib
//@ needs-llvm-components: x86
#![feature(no_core, lang_items)]
#![no_std]
#![no_core]
#[lang = "sized"]
pub trait Sized {}
#[target_feature(enable = "soft-float")]
//~^ERROR: cannot be toggled with
pub unsafe fn my_fun() {}

View File

@ -0,0 +1,8 @@
error: target feature `soft-float` cannot be toggled with `#[target_feature]`: unsound because it changes float ABI
--> $DIR/forbidden-target-feature-attribute.rs:10:18
|
LL | #[target_feature(enable = "soft-float")]
| ^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 1 previous error

View File

@ -0,0 +1,15 @@
//@ compile-flags: --target=x86_64-unknown-none --crate-type=lib
//@ needs-llvm-components: x86
//@ check-pass
#![feature(no_core, lang_items)]
#![no_std]
#![no_core]
#![allow(unexpected_cfgs)]
#[lang = "sized"]
pub trait Sized {}
// The compile_error macro does not exist, so if the `cfg` evaluates to `true` this
// complains about the missing macro rather than showing the error... but that's good enough.
#[cfg(target_feature = "soft-float")]
compile_error!("the soft-float feature should not be exposed in `cfg`");

View File

@ -0,0 +1,11 @@
//@ compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=lib
//@ needs-llvm-components: x86
//@ compile-flags: -Ctarget-feature=-soft-float
// For now this is just a warning.
//@ build-pass
#![feature(no_core, lang_items)]
#![no_std]
#![no_core]
#[lang = "sized"]
pub trait Sized {}

View File

@ -0,0 +1,7 @@
warning: target feature `soft-float` cannot be toggled with `-Ctarget-feature`: unsound because it changes float ABI
|
= 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 <https://github.com/rust-lang/rust/issues/116344>
warning: 1 warning emitted

View File

@ -0,0 +1,11 @@
//@ compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=lib
//@ needs-llvm-components: x86
//@ compile-flags: -Ctarget-feature=+soft-float
// For now this is just a warning.
//@ build-pass
#![feature(no_core, lang_items)]
#![no_std]
#![no_core]
#[lang = "sized"]
pub trait Sized {}

View File

@ -0,0 +1,7 @@
warning: target feature `soft-float` cannot be toggled with `-Ctarget-feature`: unsound because it changes float ABI
|
= 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 <https://github.com/rust-lang/rust/issues/116344>
warning: 1 warning emitted