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:
Luca Versari
2025-08-18 15:09:45 +02:00
parent 6ba0ce4094
commit 291da71b2a
21 changed files with 262 additions and 61 deletions

View File

@@ -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
{ {

View File

@@ -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)),
]);
}

View File

@@ -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>,

View File

@@ -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()

View File

@@ -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 })
} }
} }
} }

View File

@@ -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),

View File

@@ -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.

View File

@@ -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),

View File

@@ -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,

View File

@@ -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)]

View File

@@ -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()

View File

@@ -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) => {

View File

@@ -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,

View File

@@ -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"
); );

View File

@@ -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));

View File

@@ -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(),
}, },

View File

@@ -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() {

View 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;
}

View File

@@ -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`.

View File

@@ -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`.

View File

@@ -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() {}