Add an experimental unsafe(force_target_feature) attribute.
This uses the feature gate for https://github.com/rust-lang/rust/issues/143352, but is described in https://github.com/rust-lang/rfcs/pull/3820 which is strongly tied to the experiment.
This commit is contained in:
@@ -1596,7 +1596,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||||||
let safety = self.lower_safety(h.safety, default_safety);
|
let safety = self.lower_safety(h.safety, default_safety);
|
||||||
|
|
||||||
// Treat safe `#[target_feature]` functions as unsafe, but also remember that we did so.
|
// Treat safe `#[target_feature]` functions as unsafe, but also remember that we did so.
|
||||||
let safety = if find_attr!(attrs, AttributeKind::TargetFeature { .. })
|
let safety = if find_attr!(attrs, AttributeKind::TargetFeature { was_forced: false, .. })
|
||||||
&& safety.is_safe()
|
&& safety.is_safe()
|
||||||
&& !self.tcx.sess.target.is_like_wasm
|
&& !self.tcx.sess.target.is_like_wasm
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -385,57 +385,68 @@ impl<S: Stage> AttributeParser<S> for UsedParser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_tf_attribute<'c, S: Stage>(
|
||||||
|
cx: &'c mut AcceptContext<'_, '_, S>,
|
||||||
|
args: &'c ArgParser<'_>,
|
||||||
|
) -> impl IntoIterator<Item = (Symbol, Span)> + 'c {
|
||||||
|
let mut features = Vec::new();
|
||||||
|
let ArgParser::List(list) = args else {
|
||||||
|
cx.expected_list(cx.attr_span);
|
||||||
|
return features;
|
||||||
|
};
|
||||||
|
if list.is_empty() {
|
||||||
|
cx.warn_empty_attribute(cx.attr_span);
|
||||||
|
return features;
|
||||||
|
}
|
||||||
|
for item in list.mixed() {
|
||||||
|
let Some(name_value) = item.meta_item() else {
|
||||||
|
cx.expected_name_value(item.span(), Some(sym::enable));
|
||||||
|
return features;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Validate name
|
||||||
|
let Some(name) = name_value.path().word_sym() else {
|
||||||
|
cx.expected_name_value(name_value.path().span(), Some(sym::enable));
|
||||||
|
return features;
|
||||||
|
};
|
||||||
|
if name != sym::enable {
|
||||||
|
cx.expected_name_value(name_value.path().span(), Some(sym::enable));
|
||||||
|
return features;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use value
|
||||||
|
let Some(name_value) = name_value.args().name_value() else {
|
||||||
|
cx.expected_name_value(item.span(), Some(sym::enable));
|
||||||
|
return features;
|
||||||
|
};
|
||||||
|
let Some(value_str) = name_value.value_as_str() else {
|
||||||
|
cx.expected_string_literal(name_value.value_span, Some(name_value.value_as_lit()));
|
||||||
|
return features;
|
||||||
|
};
|
||||||
|
for feature in value_str.as_str().split(",") {
|
||||||
|
features.push((Symbol::intern(feature), item.span()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
features
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) struct TargetFeatureParser;
|
pub(crate) struct TargetFeatureParser;
|
||||||
|
|
||||||
impl<S: Stage> CombineAttributeParser<S> for TargetFeatureParser {
|
impl<S: Stage> CombineAttributeParser<S> for TargetFeatureParser {
|
||||||
type Item = (Symbol, Span);
|
type Item = (Symbol, Span);
|
||||||
const PATH: &[Symbol] = &[sym::target_feature];
|
const PATH: &[Symbol] = &[sym::target_feature];
|
||||||
const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature(items, span);
|
const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {
|
||||||
|
features: items,
|
||||||
|
attr_span: span,
|
||||||
|
was_forced: false,
|
||||||
|
};
|
||||||
const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]);
|
const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]);
|
||||||
|
|
||||||
fn extend<'c>(
|
fn extend<'c>(
|
||||||
cx: &'c mut AcceptContext<'_, '_, S>,
|
cx: &'c mut AcceptContext<'_, '_, S>,
|
||||||
args: &'c ArgParser<'_>,
|
args: &'c ArgParser<'_>,
|
||||||
) -> impl IntoIterator<Item = Self::Item> + 'c {
|
) -> impl IntoIterator<Item = Self::Item> + 'c {
|
||||||
let mut features = Vec::new();
|
parse_tf_attribute(cx, args)
|
||||||
let ArgParser::List(list) = args else {
|
|
||||||
cx.expected_list(cx.attr_span);
|
|
||||||
return features;
|
|
||||||
};
|
|
||||||
if list.is_empty() {
|
|
||||||
cx.warn_empty_attribute(cx.attr_span);
|
|
||||||
return features;
|
|
||||||
}
|
|
||||||
for item in list.mixed() {
|
|
||||||
let Some(name_value) = item.meta_item() else {
|
|
||||||
cx.expected_name_value(item.span(), Some(sym::enable));
|
|
||||||
return features;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Validate name
|
|
||||||
let Some(name) = name_value.path().word_sym() else {
|
|
||||||
cx.expected_name_value(name_value.path().span(), Some(sym::enable));
|
|
||||||
return features;
|
|
||||||
};
|
|
||||||
if name != sym::enable {
|
|
||||||
cx.expected_name_value(name_value.path().span(), Some(sym::enable));
|
|
||||||
return features;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use value
|
|
||||||
let Some(name_value) = name_value.args().name_value() else {
|
|
||||||
cx.expected_name_value(item.span(), Some(sym::enable));
|
|
||||||
return features;
|
|
||||||
};
|
|
||||||
let Some(value_str) = name_value.value_as_str() else {
|
|
||||||
cx.expected_string_literal(name_value.value_span, Some(name_value.value_as_lit()));
|
|
||||||
return features;
|
|
||||||
};
|
|
||||||
for feature in value_str.as_str().split(",") {
|
|
||||||
features.push((Symbol::intern(feature), item.span()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
features
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||||
@@ -449,3 +460,30 @@ impl<S: Stage> CombineAttributeParser<S> for TargetFeatureParser {
|
|||||||
Warn(Target::MacroDef),
|
Warn(Target::MacroDef),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) struct ForceTargetFeatureParser;
|
||||||
|
|
||||||
|
impl<S: Stage> CombineAttributeParser<S> for ForceTargetFeatureParser {
|
||||||
|
type Item = (Symbol, Span);
|
||||||
|
const PATH: &[Symbol] = &[sym::force_target_feature];
|
||||||
|
const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {
|
||||||
|
features: items,
|
||||||
|
attr_span: span,
|
||||||
|
was_forced: true,
|
||||||
|
};
|
||||||
|
const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]);
|
||||||
|
|
||||||
|
fn extend<'c>(
|
||||||
|
cx: &'c mut AcceptContext<'_, '_, S>,
|
||||||
|
args: &'c ArgParser<'_>,
|
||||||
|
) -> impl IntoIterator<Item = Self::Item> + 'c {
|
||||||
|
parse_tf_attribute(cx, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||||
|
Allow(Target::Fn),
|
||||||
|
Allow(Target::Method(MethodKind::Inherent)),
|
||||||
|
Allow(Target::Method(MethodKind::Trait { body: true })),
|
||||||
|
Allow(Target::Method(MethodKind::TraitImpl)),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ use crate::attributes::allow_unstable::{
|
|||||||
};
|
};
|
||||||
use crate::attributes::body::CoroutineParser;
|
use crate::attributes::body::CoroutineParser;
|
||||||
use crate::attributes::codegen_attrs::{
|
use crate::attributes::codegen_attrs::{
|
||||||
ColdParser, CoverageParser, ExportNameParser, NakedParser, NoMangleParser, OptimizeParser,
|
ColdParser, CoverageParser, ExportNameParser, ForceTargetFeatureParser, NakedParser,
|
||||||
TargetFeatureParser, TrackCallerParser, UsedParser,
|
NoMangleParser, OptimizeParser, TargetFeatureParser, TrackCallerParser, UsedParser,
|
||||||
};
|
};
|
||||||
use crate::attributes::confusables::ConfusablesParser;
|
use crate::attributes::confusables::ConfusablesParser;
|
||||||
use crate::attributes::deprecation::DeprecationParser;
|
use crate::attributes::deprecation::DeprecationParser;
|
||||||
@@ -161,6 +161,7 @@ attribute_parsers!(
|
|||||||
// tidy-alphabetical-start
|
// tidy-alphabetical-start
|
||||||
Combine<AllowConstFnUnstableParser>,
|
Combine<AllowConstFnUnstableParser>,
|
||||||
Combine<AllowInternalUnstableParser>,
|
Combine<AllowInternalUnstableParser>,
|
||||||
|
Combine<ForceTargetFeatureParser>,
|
||||||
Combine<ReprParser>,
|
Combine<ReprParser>,
|
||||||
Combine<TargetFeatureParser>,
|
Combine<TargetFeatureParser>,
|
||||||
Combine<UnstableFeatureBoundParser>,
|
Combine<UnstableFeatureBoundParser>,
|
||||||
|
|||||||
@@ -193,7 +193,7 @@ fn process_builtin_attrs(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
AttributeKind::Optimize(optimize, _) => codegen_fn_attrs.optimize = *optimize,
|
AttributeKind::Optimize(optimize, _) => codegen_fn_attrs.optimize = *optimize,
|
||||||
AttributeKind::TargetFeature(features, attr_span) => {
|
AttributeKind::TargetFeature { features, attr_span, was_forced } => {
|
||||||
let Some(sig) = tcx.hir_node_by_def_id(did).fn_sig() else {
|
let Some(sig) = tcx.hir_node_by_def_id(did).fn_sig() else {
|
||||||
tcx.dcx().span_delayed_bug(*attr_span, "target_feature applied to non-fn");
|
tcx.dcx().span_delayed_bug(*attr_span, "target_feature applied to non-fn");
|
||||||
continue;
|
continue;
|
||||||
@@ -201,7 +201,7 @@ fn process_builtin_attrs(
|
|||||||
let safe_target_features =
|
let safe_target_features =
|
||||||
matches!(sig.header.safety, hir::HeaderSafety::SafeTargetFeatures);
|
matches!(sig.header.safety, hir::HeaderSafety::SafeTargetFeatures);
|
||||||
codegen_fn_attrs.safe_target_features = safe_target_features;
|
codegen_fn_attrs.safe_target_features = safe_target_features;
|
||||||
if safe_target_features {
|
if safe_target_features && !was_forced {
|
||||||
if tcx.sess.target.is_like_wasm || tcx.sess.opts.actually_rustdoc {
|
if tcx.sess.target.is_like_wasm || tcx.sess.opts.actually_rustdoc {
|
||||||
// The `#[target_feature]` attribute is allowed on
|
// The `#[target_feature]` attribute is allowed on
|
||||||
// WebAssembly targets on all functions. Prior to stabilizing
|
// WebAssembly targets on all functions. Prior to stabilizing
|
||||||
@@ -232,6 +232,7 @@ fn process_builtin_attrs(
|
|||||||
tcx,
|
tcx,
|
||||||
did,
|
did,
|
||||||
features,
|
features,
|
||||||
|
*was_forced,
|
||||||
rust_target_features,
|
rust_target_features,
|
||||||
&mut codegen_fn_attrs.target_features,
|
&mut codegen_fn_attrs.target_features,
|
||||||
);
|
);
|
||||||
@@ -462,7 +463,7 @@ fn check_result(
|
|||||||
.collect(),
|
.collect(),
|
||||||
) {
|
) {
|
||||||
let span =
|
let span =
|
||||||
find_attr!(tcx.get_all_attrs(did), AttributeKind::TargetFeature(_, span) => *span)
|
find_attr!(tcx.get_all_attrs(did), AttributeKind::TargetFeature{attr_span: span, ..} => *span)
|
||||||
.unwrap_or_else(|| tcx.def_span(did));
|
.unwrap_or_else(|| tcx.def_span(did));
|
||||||
|
|
||||||
tcx.dcx()
|
tcx.dcx()
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use rustc_data_structures::unord::{UnordMap, UnordSet};
|
|||||||
use rustc_hir::attrs::InstructionSetAttr;
|
use rustc_hir::attrs::InstructionSetAttr;
|
||||||
use rustc_hir::def::DefKind;
|
use rustc_hir::def::DefKind;
|
||||||
use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
|
use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
|
||||||
use rustc_middle::middle::codegen_fn_attrs::TargetFeature;
|
use rustc_middle::middle::codegen_fn_attrs::{TargetFeature, TargetFeatureKind};
|
||||||
use rustc_middle::query::Providers;
|
use rustc_middle::query::Providers;
|
||||||
use rustc_middle::ty::TyCtxt;
|
use rustc_middle::ty::TyCtxt;
|
||||||
use rustc_session::Session;
|
use rustc_session::Session;
|
||||||
@@ -22,6 +22,7 @@ pub(crate) fn from_target_feature_attr(
|
|||||||
tcx: TyCtxt<'_>,
|
tcx: TyCtxt<'_>,
|
||||||
did: LocalDefId,
|
did: LocalDefId,
|
||||||
features: &[(Symbol, Span)],
|
features: &[(Symbol, Span)],
|
||||||
|
was_forced: bool,
|
||||||
rust_target_features: &UnordMap<String, target_features::Stability>,
|
rust_target_features: &UnordMap<String, target_features::Stability>,
|
||||||
target_features: &mut Vec<TargetFeature>,
|
target_features: &mut Vec<TargetFeature>,
|
||||||
) {
|
) {
|
||||||
@@ -88,7 +89,14 @@ pub(crate) fn from_target_feature_attr(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
target_features.push(TargetFeature { name, implied: name != feature })
|
let kind = if name != feature {
|
||||||
|
TargetFeatureKind::Implied
|
||||||
|
} else if was_forced {
|
||||||
|
TargetFeatureKind::Forced
|
||||||
|
} else {
|
||||||
|
TargetFeatureKind::Enabled
|
||||||
|
};
|
||||||
|
target_features.push(TargetFeature { name, kind })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -744,6 +744,10 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||||||
template!(List: &["set"], "https://doc.rust-lang.org/reference/attributes/codegen.html#the-instruction_set-attribute"),
|
template!(List: &["set"], "https://doc.rust-lang.org/reference/attributes/codegen.html#the-instruction_set-attribute"),
|
||||||
ErrorPreceding, EncodeCrossCrate::No
|
ErrorPreceding, EncodeCrossCrate::No
|
||||||
),
|
),
|
||||||
|
gated!(
|
||||||
|
unsafe force_target_feature, Normal, template!(List: &[r#"enable = "name""#]),
|
||||||
|
DuplicatesOk, EncodeCrossCrate::No, effective_target_features, experimental!(force_target_feature)
|
||||||
|
),
|
||||||
gated!(
|
gated!(
|
||||||
sanitize, Normal, template!(List: &[r#"address = "on|off""#, r#"kernel_address = "on|off""#, r#"cfi = "on|off""#, r#"hwaddress = "on|off""#, r#"kcfi = "on|off""#, r#"memory = "on|off""#, r#"memtag = "on|off""#, r#"shadow_call_stack = "on|off""#, r#"thread = "on|off""#]), ErrorPreceding,
|
sanitize, Normal, template!(List: &[r#"address = "on|off""#, r#"kernel_address = "on|off""#, r#"cfi = "on|off""#, r#"hwaddress = "on|off""#, r#"kcfi = "on|off""#, r#"memory = "on|off""#, r#"memtag = "on|off""#, r#"shadow_call_stack = "on|off""#, r#"thread = "on|off""#]), ErrorPreceding,
|
||||||
EncodeCrossCrate::No, sanitize, experimental!(sanitize),
|
EncodeCrossCrate::No, sanitize, experimental!(sanitize),
|
||||||
|
|||||||
@@ -480,6 +480,8 @@ declare_features! (
|
|||||||
(unstable, doc_cfg_hide, "1.57.0", Some(43781)),
|
(unstable, doc_cfg_hide, "1.57.0", Some(43781)),
|
||||||
/// Allows `#[doc(masked)]`.
|
/// Allows `#[doc(masked)]`.
|
||||||
(unstable, doc_masked, "1.21.0", Some(44027)),
|
(unstable, doc_masked, "1.21.0", Some(44027)),
|
||||||
|
/// Allows features to allow target_feature to better interact with traits.
|
||||||
|
(incomplete, effective_target_features, "CURRENT_RUSTC_VERSION", Some(143352)),
|
||||||
/// Allows the .use postfix syntax `x.use` and use closures `use |x| { ... }`
|
/// Allows the .use postfix syntax `x.use` and use closures `use |x| { ... }`
|
||||||
(incomplete, ergonomic_clones, "1.87.0", Some(132290)),
|
(incomplete, ergonomic_clones, "1.87.0", Some(132290)),
|
||||||
/// Allows exhaustive pattern matching on types that contain uninhabited types.
|
/// Allows exhaustive pattern matching on types that contain uninhabited types.
|
||||||
|
|||||||
@@ -524,8 +524,9 @@ pub enum AttributeKind {
|
|||||||
/// Represents `#[rustc_std_internal_symbol]`.
|
/// Represents `#[rustc_std_internal_symbol]`.
|
||||||
StdInternalSymbol(Span),
|
StdInternalSymbol(Span),
|
||||||
|
|
||||||
/// Represents `#[target_feature(enable = "...")]`
|
/// Represents `#[target_feature(enable = "...")]` and
|
||||||
TargetFeature(ThinVec<(Symbol, Span)>, Span),
|
/// `#[unsafe(force_target_feature(enable = "...")]`.
|
||||||
|
TargetFeature { features: ThinVec<(Symbol, Span)>, attr_span: Span, was_forced: bool },
|
||||||
|
|
||||||
/// Represents `#[track_caller]`
|
/// Represents `#[track_caller]`
|
||||||
TrackCaller(Span),
|
TrackCaller(Span),
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ impl AttributeKind {
|
|||||||
SpecializationTrait(..) => No,
|
SpecializationTrait(..) => No,
|
||||||
Stability { .. } => Yes,
|
Stability { .. } => Yes,
|
||||||
StdInternalSymbol(..) => No,
|
StdInternalSymbol(..) => No,
|
||||||
TargetFeature(..) => No,
|
TargetFeature { .. } => No,
|
||||||
TrackCaller(..) => Yes,
|
TrackCaller(..) => Yes,
|
||||||
TypeConst(..) => Yes,
|
TypeConst(..) => Yes,
|
||||||
UnsafeSpecializationMarker(..) => No,
|
UnsafeSpecializationMarker(..) => No,
|
||||||
|
|||||||
@@ -72,13 +72,23 @@ pub struct CodegenFnAttrs {
|
|||||||
pub patchable_function_entry: Option<PatchableFunctionEntry>,
|
pub patchable_function_entry: Option<PatchableFunctionEntry>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable, PartialEq, Eq)]
|
||||||
|
pub enum TargetFeatureKind {
|
||||||
|
/// The feature is implied by another feature, rather than explicitly added by the
|
||||||
|
/// `#[target_feature]` attribute
|
||||||
|
Implied,
|
||||||
|
/// The feature is added by the regular `target_feature` attribute.
|
||||||
|
Enabled,
|
||||||
|
/// The feature is added by the unsafe `force_target_feature` attribute.
|
||||||
|
Forced,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)]
|
#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)]
|
||||||
pub struct TargetFeature {
|
pub struct TargetFeature {
|
||||||
/// The name of the target feature (e.g. "avx")
|
/// The name of the target feature (e.g. "avx")
|
||||||
pub name: Symbol,
|
pub name: Symbol,
|
||||||
/// The feature is implied by another feature, rather than explicitly added by the
|
/// The way this feature was enabled.
|
||||||
/// `#[target_feature]` attribute
|
pub kind: TargetFeatureKind,
|
||||||
pub implied: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)]
|
#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)]
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use rustc_errors::DiagArgValue;
|
|||||||
use rustc_hir::attrs::AttributeKind;
|
use rustc_hir::attrs::AttributeKind;
|
||||||
use rustc_hir::def::DefKind;
|
use rustc_hir::def::DefKind;
|
||||||
use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Mutability, find_attr};
|
use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Mutability, find_attr};
|
||||||
use rustc_middle::middle::codegen_fn_attrs::TargetFeature;
|
use rustc_middle::middle::codegen_fn_attrs::{TargetFeature, TargetFeatureKind};
|
||||||
use rustc_middle::mir::BorrowKind;
|
use rustc_middle::mir::BorrowKind;
|
||||||
use rustc_middle::span_bug;
|
use rustc_middle::span_bug;
|
||||||
use rustc_middle::thir::visit::Visitor;
|
use rustc_middle::thir::visit::Visitor;
|
||||||
@@ -522,7 +522,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
|
|||||||
.iter()
|
.iter()
|
||||||
.copied()
|
.copied()
|
||||||
.filter(|feature| {
|
.filter(|feature| {
|
||||||
!feature.implied
|
feature.kind == TargetFeatureKind::Enabled
|
||||||
&& !self
|
&& !self
|
||||||
.body_target_features
|
.body_target_features
|
||||||
.iter()
|
.iter()
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||||||
Attribute::Parsed(AttributeKind::Deprecation { .. }) => {
|
Attribute::Parsed(AttributeKind::Deprecation { .. }) => {
|
||||||
self.check_deprecated(hir_id, attr, span, target)
|
self.check_deprecated(hir_id, attr, span, target)
|
||||||
}
|
}
|
||||||
Attribute::Parsed(AttributeKind::TargetFeature(_, attr_span)) => {
|
Attribute::Parsed(AttributeKind::TargetFeature{ attr_span, ..}) => {
|
||||||
self.check_target_feature(hir_id, *attr_span, target, attrs)
|
self.check_target_feature(hir_id, *attr_span, target, attrs)
|
||||||
}
|
}
|
||||||
Attribute::Parsed(AttributeKind::RustcObjectLifetimeDefault) => {
|
Attribute::Parsed(AttributeKind::RustcObjectLifetimeDefault) => {
|
||||||
|
|||||||
@@ -901,6 +901,7 @@ symbols! {
|
|||||||
dynamic_no_pic: "dynamic-no-pic",
|
dynamic_no_pic: "dynamic-no-pic",
|
||||||
e,
|
e,
|
||||||
edition_panic,
|
edition_panic,
|
||||||
|
effective_target_features,
|
||||||
effects,
|
effects,
|
||||||
eh_catch_typeinfo,
|
eh_catch_typeinfo,
|
||||||
eh_personality,
|
eh_personality,
|
||||||
@@ -1061,6 +1062,7 @@ symbols! {
|
|||||||
fn_ptr_addr,
|
fn_ptr_addr,
|
||||||
fn_ptr_trait,
|
fn_ptr_trait,
|
||||||
forbid,
|
forbid,
|
||||||
|
force_target_feature,
|
||||||
forget,
|
forget,
|
||||||
format,
|
format,
|
||||||
format_args,
|
format_args,
|
||||||
|
|||||||
@@ -537,7 +537,7 @@ impl<T> Trait<T> for X {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
TypeError::TargetFeatureCast(def_id) => {
|
TypeError::TargetFeatureCast(def_id) => {
|
||||||
let target_spans = find_attr!(tcx.get_all_attrs(def_id), AttributeKind::TargetFeature(.., span) => *span);
|
let target_spans = find_attr!(tcx.get_all_attrs(def_id), AttributeKind::TargetFeature{attr_span: span, was_forced: false, ..} => *span);
|
||||||
diag.note(
|
diag.note(
|
||||||
"functions with `#[target_feature]` can only be coerced to `unsafe` function pointers"
|
"functions with `#[target_feature]` can only be coerced to `unsafe` function pointers"
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1087,7 +1087,8 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator<Item = &'a hir::Attribute>
|
|||||||
|
|
||||||
// treat #[target_feature(enable = "feat")] attributes as if they were
|
// treat #[target_feature(enable = "feat")] attributes as if they were
|
||||||
// #[doc(cfg(target_feature = "feat"))] attributes as well
|
// #[doc(cfg(target_feature = "feat"))] attributes as well
|
||||||
if let Some(features) = find_attr!(attrs, AttributeKind::TargetFeature(features, _) => features)
|
if let Some(features) =
|
||||||
|
find_attr!(attrs, AttributeKind::TargetFeature { features, .. } => features)
|
||||||
{
|
{
|
||||||
for (feature, _) in features {
|
for (feature, _) in features {
|
||||||
cfg &= Cfg::Cfg(sym::target_feature, Some(*feature));
|
cfg &= Cfg::Cfg(sym::target_feature, Some(*feature));
|
||||||
|
|||||||
@@ -930,7 +930,7 @@ fn maybe_from_hir_attr(
|
|||||||
),
|
),
|
||||||
AK::ExportName { name, span: _ } => Attribute::ExportName(name.to_string()),
|
AK::ExportName { name, span: _ } => Attribute::ExportName(name.to_string()),
|
||||||
AK::LinkSection { name, span: _ } => Attribute::LinkSection(name.to_string()),
|
AK::LinkSection { name, span: _ } => Attribute::LinkSection(name.to_string()),
|
||||||
AK::TargetFeature(features, _span) => Attribute::TargetFeature {
|
AK::TargetFeature { features, .. } => Attribute::TargetFeature {
|
||||||
enable: features.iter().map(|(feat, _span)| feat.to_string()).collect(),
|
enable: features.iter().map(|(feat, _span)| feat.to_string()).collect(),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
|||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
use rustc_data_structures::static_assert_size;
|
use rustc_data_structures::static_assert_size;
|
||||||
use rustc_hir::attrs::InlineAttr;
|
use rustc_hir::attrs::InlineAttr;
|
||||||
|
use rustc_middle::middle::codegen_fn_attrs::TargetFeatureKind;
|
||||||
use rustc_middle::mir;
|
use rustc_middle::mir;
|
||||||
use rustc_middle::query::TyCtxtAt;
|
use rustc_middle::query::TyCtxtAt;
|
||||||
use rustc_middle::ty::layout::{
|
use rustc_middle::ty::layout::{
|
||||||
@@ -1076,7 +1077,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
|
|||||||
.target_features
|
.target_features
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|&feature| {
|
.filter(|&feature| {
|
||||||
!feature.implied && !ecx.tcx.sess.target_features.contains(&feature.name)
|
feature.kind != TargetFeatureKind::Implied && !ecx.tcx.sess.target_features.contains(&feature.name)
|
||||||
})
|
})
|
||||||
.fold(String::new(), |mut s, feature| {
|
.fold(String::new(), |mut s, feature| {
|
||||||
if !s.is_empty() {
|
if !s.is_empty() {
|
||||||
|
|||||||
33
tests/assembly-llvm/force-target-feature.rs
Normal file
33
tests/assembly-llvm/force-target-feature.rs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
//@ only-x86_64
|
||||||
|
//@ assembly-output: emit-asm
|
||||||
|
//@ compile-flags: -C opt-level=3 -C target-feature=-avx2
|
||||||
|
//@ ignore-sgx Tests incompatible with LVI mitigations
|
||||||
|
|
||||||
|
#![feature(effective_target_features)]
|
||||||
|
|
||||||
|
use std::arch::x86_64::{__m256i, _mm256_add_epi32, _mm256_setzero_si256};
|
||||||
|
use std::ops::Add;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct AvxU32(__m256i);
|
||||||
|
|
||||||
|
impl Add<AvxU32> for AvxU32 {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
#[inline(never)]
|
||||||
|
#[unsafe(force_target_feature(enable = "avx2"))]
|
||||||
|
fn add(self, oth: AvxU32) -> AvxU32 {
|
||||||
|
// CHECK-LABEL: add:
|
||||||
|
// CHECK-NOT: callq
|
||||||
|
// CHECK: vpaddd
|
||||||
|
// CHECK: retq
|
||||||
|
Self(_mm256_add_epi32(self.0, oth.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
assert!(is_x86_feature_detected!("avx2"));
|
||||||
|
let v = AvxU32(unsafe { _mm256_setzero_si256() });
|
||||||
|
v + v;
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
error[E0658]: the `#[force_target_feature]` attribute is an experimental feature
|
||||||
|
--> $DIR/feature-gate-effective-target-features.rs:13:5
|
||||||
|
|
|
||||||
|
LL | #[unsafe(force_target_feature(enable = "avx2"))]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: see issue #143352 <https://github.com/rust-lang/rust/issues/143352> for more information
|
||||||
|
= help: add `#![feature(effective_target_features)]` to the crate attributes to enable
|
||||||
|
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||||
|
|
||||||
|
error: `#[target_feature(..)]` cannot be applied to safe trait method
|
||||||
|
--> $DIR/feature-gate-effective-target-features.rs:21:5
|
||||||
|
|
|
||||||
|
LL | #[target_feature(enable = "avx2")]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot be applied to safe trait method
|
||||||
|
LL |
|
||||||
|
LL | fn foo(&self) {}
|
||||||
|
| ------------- not an `unsafe` function
|
||||||
|
|
||||||
|
error[E0053]: method `foo` has an incompatible type for trait
|
||||||
|
--> $DIR/feature-gate-effective-target-features.rs:23:5
|
||||||
|
|
|
||||||
|
LL | fn foo(&self) {}
|
||||||
|
| ^^^^^^^^^^^^^ expected safe fn, found unsafe fn
|
||||||
|
|
|
||||||
|
note: type in trait
|
||||||
|
--> $DIR/feature-gate-effective-target-features.rs:7:5
|
||||||
|
|
|
||||||
|
LL | fn foo(&self);
|
||||||
|
| ^^^^^^^^^^^^^^
|
||||||
|
= note: expected signature `fn(&Bar2)`
|
||||||
|
found signature `#[target_features] fn(&Bar2)`
|
||||||
|
|
||||||
|
error: aborting due to 3 previous errors
|
||||||
|
|
||||||
|
Some errors have detailed explanations: E0053, E0658.
|
||||||
|
For more information about an error, try `rustc --explain E0053`.
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
warning: the feature `effective_target_features` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||||
|
--> $DIR/feature-gate-effective-target-features.rs:3:30
|
||||||
|
|
|
||||||
|
LL | #![cfg_attr(feature, feature(effective_target_features))]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: see issue #143352 <https://github.com/rust-lang/rust/issues/143352> for more information
|
||||||
|
= note: `#[warn(incomplete_features)]` on by default
|
||||||
|
|
||||||
|
error: `#[target_feature(..)]` cannot be applied to safe trait method
|
||||||
|
--> $DIR/feature-gate-effective-target-features.rs:21:5
|
||||||
|
|
|
||||||
|
LL | #[target_feature(enable = "avx2")]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot be applied to safe trait method
|
||||||
|
LL |
|
||||||
|
LL | fn foo(&self) {}
|
||||||
|
| ------------- not an `unsafe` function
|
||||||
|
|
||||||
|
error[E0053]: method `foo` has an incompatible type for trait
|
||||||
|
--> $DIR/feature-gate-effective-target-features.rs:23:5
|
||||||
|
|
|
||||||
|
LL | fn foo(&self) {}
|
||||||
|
| ^^^^^^^^^^^^^ expected safe fn, found unsafe fn
|
||||||
|
|
|
||||||
|
note: type in trait
|
||||||
|
--> $DIR/feature-gate-effective-target-features.rs:7:5
|
||||||
|
|
|
||||||
|
LL | fn foo(&self);
|
||||||
|
| ^^^^^^^^^^^^^^
|
||||||
|
= note: expected signature `fn(&Bar2)`
|
||||||
|
found signature `#[target_features] fn(&Bar2)`
|
||||||
|
|
||||||
|
error: aborting due to 2 previous errors; 1 warning emitted
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0053`.
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
//@ revisions: default feature
|
||||||
|
//@ only-x86_64
|
||||||
|
#![cfg_attr(feature, feature(effective_target_features))]
|
||||||
|
//[feature]~^ WARN the feature `effective_target_features` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||||
|
|
||||||
|
trait Foo {
|
||||||
|
fn foo(&self);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Bar;
|
||||||
|
|
||||||
|
impl Foo for Bar {
|
||||||
|
#[unsafe(force_target_feature(enable = "avx2"))]
|
||||||
|
//[default]~^ ERROR the `#[force_target_feature]` attribute is an experimental feature
|
||||||
|
fn foo(&self) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Bar2;
|
||||||
|
|
||||||
|
impl Foo for Bar2 {
|
||||||
|
#[target_feature(enable = "avx2")]
|
||||||
|
//~^ ERROR `#[target_feature(..)]` cannot be applied to safe trait method
|
||||||
|
fn foo(&self) {}
|
||||||
|
//~^ ERROR method `foo` has an incompatible type for trait
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
||||||
Reference in New Issue
Block a user