Rollup merge of #142876 - JonathanBrouwer:target_feature_parser, r=oli-obk
Port `#[target_feature]` to new attribute parsing infrastructure Ports `target_feature` to the new attribute parsing infrastructure for https://github.com/rust-lang/rust/issues/131229#issuecomment-2971353197 r? ``@jdonszelmann``
This commit is contained in:
@@ -1,8 +1,6 @@
|
||||
use rustc_attr_data_structures::InstructionSetAttr;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
|
||||
use rustc_data_structures::unord::{UnordMap, UnordSet};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
|
||||
use rustc_middle::middle::codegen_fn_attrs::TargetFeature;
|
||||
@@ -12,110 +10,85 @@ use rustc_session::Session;
|
||||
use rustc_session::lint::builtin::AARCH64_SOFTFLOAT_NEON;
|
||||
use rustc_session::parse::feature_err;
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
use rustc_target::target_features::{self, RUSTC_SPECIFIC_FEATURES, Stability};
|
||||
use rustc_target::target_features::{RUSTC_SPECIFIC_FEATURES, Stability};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::errors;
|
||||
use crate::errors::FeatureNotValid;
|
||||
use crate::{errors, target_features};
|
||||
|
||||
/// 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<'_>,
|
||||
did: LocalDefId,
|
||||
attr: &hir::Attribute,
|
||||
features: &[(Symbol, Span)],
|
||||
rust_target_features: &UnordMap<String, target_features::Stability>,
|
||||
target_features: &mut Vec<TargetFeature>,
|
||||
) {
|
||||
let Some(list) = attr.meta_item_list() else { return };
|
||||
let bad_item = |span| {
|
||||
let msg = "malformed `target_feature` attribute input";
|
||||
let code = "enable = \"..\"";
|
||||
tcx.dcx()
|
||||
.struct_span_err(span, msg)
|
||||
.with_span_suggestion(span, "must be of the form", code, Applicability::HasPlaceholders)
|
||||
.emit();
|
||||
};
|
||||
let rust_features = tcx.features();
|
||||
let abi_feature_constraints = tcx.sess.target.abi_required_features();
|
||||
for item in list {
|
||||
// Only `enable = ...` is accepted in the meta-item list.
|
||||
if !item.has_name(sym::enable) {
|
||||
bad_item(item.span());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Must be of the form `enable = "..."` (a string).
|
||||
let Some(value) = item.value_str() else {
|
||||
bad_item(item.span());
|
||||
for &(feature, feature_span) in features {
|
||||
let feature_str = feature.as_str();
|
||||
let Some(stability) = rust_target_features.get(feature_str) else {
|
||||
let plus_hint = feature_str
|
||||
.strip_prefix('+')
|
||||
.is_some_and(|stripped| rust_target_features.contains_key(stripped));
|
||||
tcx.dcx().emit_err(FeatureNotValid {
|
||||
feature: feature_str,
|
||||
span: feature_span,
|
||||
plus_hint,
|
||||
});
|
||||
continue;
|
||||
};
|
||||
|
||||
// We allow comma separation to enable multiple features.
|
||||
for feature in value.as_str().split(',') {
|
||||
let Some(stability) = rust_target_features.get(feature) else {
|
||||
let msg = format!("the feature named `{feature}` is not valid for this target");
|
||||
let mut err = tcx.dcx().struct_span_err(item.span(), msg);
|
||||
err.span_label(item.span(), format!("`{feature}` is not valid for this target"));
|
||||
if let Some(stripped) = feature.strip_prefix('+') {
|
||||
let valid = rust_target_features.contains_key(stripped);
|
||||
if valid {
|
||||
err.help("consider removing the leading `+` in the feature name");
|
||||
}
|
||||
}
|
||||
err.emit();
|
||||
continue;
|
||||
};
|
||||
|
||||
// Only allow target features whose feature gates have been enabled
|
||||
// and which are permitted to be toggled.
|
||||
if let Err(reason) = stability.toggle_allowed() {
|
||||
tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr {
|
||||
span: item.span(),
|
||||
feature,
|
||||
reason,
|
||||
});
|
||||
} else if let Some(nightly_feature) = stability.requires_nightly()
|
||||
&& !rust_features.enabled(nightly_feature)
|
||||
{
|
||||
feature_err(
|
||||
&tcx.sess,
|
||||
nightly_feature,
|
||||
item.span(),
|
||||
format!("the target feature `{feature}` is currently unstable"),
|
||||
)
|
||||
.emit();
|
||||
} else {
|
||||
// Add this and the implied features.
|
||||
let feature_sym = Symbol::intern(feature);
|
||||
for &name in tcx.implied_target_features(feature_sym) {
|
||||
// But ensure the ABI does not forbid enabling this.
|
||||
// Here we do assume that the backend doesn't add even more implied features
|
||||
// we don't know about, at least no features that would have ABI effects!
|
||||
// We skip this logic in rustdoc, where we want to allow all target features of
|
||||
// all targets, so we can't check their ABI compatibility and anyway we are not
|
||||
// generating code so "it's fine".
|
||||
if !tcx.sess.opts.actually_rustdoc {
|
||||
if abi_feature_constraints.incompatible.contains(&name.as_str()) {
|
||||
// For "neon" specifically, we emit an FCW instead of a hard error.
|
||||
// See <https://github.com/rust-lang/rust/issues/134375>.
|
||||
if tcx.sess.target.arch == "aarch64" && name.as_str() == "neon" {
|
||||
tcx.emit_node_span_lint(
|
||||
AARCH64_SOFTFLOAT_NEON,
|
||||
tcx.local_def_id_to_hir_id(did),
|
||||
item.span(),
|
||||
errors::Aarch64SoftfloatNeon,
|
||||
);
|
||||
} else {
|
||||
tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr {
|
||||
span: item.span(),
|
||||
feature: name.as_str(),
|
||||
reason: "this feature is incompatible with the target ABI",
|
||||
});
|
||||
}
|
||||
// Only allow target features whose feature gates have been enabled
|
||||
// and which are permitted to be toggled.
|
||||
if let Err(reason) = stability.toggle_allowed() {
|
||||
tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr {
|
||||
span: feature_span,
|
||||
feature: feature_str,
|
||||
reason,
|
||||
});
|
||||
} else if let Some(nightly_feature) = stability.requires_nightly()
|
||||
&& !rust_features.enabled(nightly_feature)
|
||||
{
|
||||
feature_err(
|
||||
&tcx.sess,
|
||||
nightly_feature,
|
||||
feature_span,
|
||||
format!("the target feature `{feature}` is currently unstable"),
|
||||
)
|
||||
.emit();
|
||||
} else {
|
||||
// Add this and the implied features.
|
||||
for &name in tcx.implied_target_features(feature) {
|
||||
// But ensure the ABI does not forbid enabling this.
|
||||
// Here we do assume that the backend doesn't add even more implied features
|
||||
// we don't know about, at least no features that would have ABI effects!
|
||||
// We skip this logic in rustdoc, where we want to allow all target features of
|
||||
// all targets, so we can't check their ABI compatibility and anyway we are not
|
||||
// generating code so "it's fine".
|
||||
if !tcx.sess.opts.actually_rustdoc {
|
||||
if abi_feature_constraints.incompatible.contains(&name.as_str()) {
|
||||
// For "neon" specifically, we emit an FCW instead of a hard error.
|
||||
// See <https://github.com/rust-lang/rust/issues/134375>.
|
||||
if tcx.sess.target.arch == "aarch64" && name.as_str() == "neon" {
|
||||
tcx.emit_node_span_lint(
|
||||
AARCH64_SOFTFLOAT_NEON,
|
||||
tcx.local_def_id_to_hir_id(did),
|
||||
feature_span,
|
||||
errors::Aarch64SoftfloatNeon,
|
||||
);
|
||||
} else {
|
||||
tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr {
|
||||
span: feature_span,
|
||||
feature: name.as_str(),
|
||||
reason: "this feature is incompatible with the target ABI",
|
||||
});
|
||||
}
|
||||
}
|
||||
target_features.push(TargetFeature { name, implied: name != feature_sym })
|
||||
}
|
||||
target_features.push(TargetFeature { name, implied: name != feature })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user