Skip to content

Commit 76ba208

Browse files
committed
Add an experimental unsafe(force_target_feature) attribute.
This uses the feature gate for rust-lang#143352, but is described in rust-lang/rfcs#3820 which is strongly tied to the experiment.
1 parent 8365fcb commit 76ba208

File tree

21 files changed

+261
-61
lines changed

21 files changed

+261
-61
lines changed

compiler/rustc_ast_lowering/src/item.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1596,7 +1596,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
15961596
let safety = self.lower_safety(h.safety, default_safety);
15971597

15981598
// Treat safe `#[target_feature]` functions as unsafe, but also remember that we did so.
1599-
let safety = if find_attr!(attrs, AttributeKind::TargetFeature { .. })
1599+
let safety = if find_attr!(attrs, AttributeKind::TargetFeature { was_forced: false, .. })
16001600
&& safety.is_safe()
16011601
&& !self.tcx.sess.target.is_like_wasm
16021602
{

compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs

Lines changed: 78 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -385,57 +385,68 @@ impl<S: Stage> AttributeParser<S> for UsedParser {
385385
}
386386
}
387387

388+
fn parse_tf_attribute<'c, S: Stage>(
389+
cx: &'c mut AcceptContext<'_, '_, S>,
390+
args: &'c ArgParser<'_>,
391+
) -> impl IntoIterator<Item = (Symbol, Span)> + 'c {
392+
let mut features = Vec::new();
393+
let ArgParser::List(list) = args else {
394+
cx.expected_list(cx.attr_span);
395+
return features;
396+
};
397+
if list.is_empty() {
398+
cx.warn_empty_attribute(cx.attr_span);
399+
return features;
400+
}
401+
for item in list.mixed() {
402+
let Some(name_value) = item.meta_item() else {
403+
cx.expected_name_value(item.span(), Some(sym::enable));
404+
return features;
405+
};
406+
407+
// Validate name
408+
let Some(name) = name_value.path().word_sym() else {
409+
cx.expected_name_value(name_value.path().span(), Some(sym::enable));
410+
return features;
411+
};
412+
if name != sym::enable {
413+
cx.expected_name_value(name_value.path().span(), Some(sym::enable));
414+
return features;
415+
}
416+
417+
// Use value
418+
let Some(name_value) = name_value.args().name_value() else {
419+
cx.expected_name_value(item.span(), Some(sym::enable));
420+
return features;
421+
};
422+
let Some(value_str) = name_value.value_as_str() else {
423+
cx.expected_string_literal(name_value.value_span, Some(name_value.value_as_lit()));
424+
return features;
425+
};
426+
for feature in value_str.as_str().split(",") {
427+
features.push((Symbol::intern(feature), item.span()));
428+
}
429+
}
430+
features
431+
}
432+
388433
pub(crate) struct TargetFeatureParser;
389434

390435
impl<S: Stage> CombineAttributeParser<S> for TargetFeatureParser {
391436
type Item = (Symbol, Span);
392437
const PATH: &[Symbol] = &[sym::target_feature];
393-
const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature(items, span);
438+
const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {
439+
features: items,
440+
attr_span: span,
441+
was_forced: false,
442+
};
394443
const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]);
395444

396445
fn extend<'c>(
397446
cx: &'c mut AcceptContext<'_, '_, S>,
398447
args: &'c ArgParser<'_>,
399448
) -> impl IntoIterator<Item = Self::Item> + 'c {
400-
let mut features = Vec::new();
401-
let ArgParser::List(list) = args else {
402-
cx.expected_list(cx.attr_span);
403-
return features;
404-
};
405-
if list.is_empty() {
406-
cx.warn_empty_attribute(cx.attr_span);
407-
return features;
408-
}
409-
for item in list.mixed() {
410-
let Some(name_value) = item.meta_item() else {
411-
cx.expected_name_value(item.span(), Some(sym::enable));
412-
return features;
413-
};
414-
415-
// Validate name
416-
let Some(name) = name_value.path().word_sym() else {
417-
cx.expected_name_value(name_value.path().span(), Some(sym::enable));
418-
return features;
419-
};
420-
if name != sym::enable {
421-
cx.expected_name_value(name_value.path().span(), Some(sym::enable));
422-
return features;
423-
}
424-
425-
// Use value
426-
let Some(name_value) = name_value.args().name_value() else {
427-
cx.expected_name_value(item.span(), Some(sym::enable));
428-
return features;
429-
};
430-
let Some(value_str) = name_value.value_as_str() else {
431-
cx.expected_string_literal(name_value.value_span, Some(name_value.value_as_lit()));
432-
return features;
433-
};
434-
for feature in value_str.as_str().split(",") {
435-
features.push((Symbol::intern(feature), item.span()));
436-
}
437-
}
438-
features
449+
parse_tf_attribute(cx, args)
439450
}
440451

441452
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
@@ -449,3 +460,30 @@ impl<S: Stage> CombineAttributeParser<S> for TargetFeatureParser {
449460
Warn(Target::MacroDef),
450461
]);
451462
}
463+
464+
pub(crate) struct ForceTargetFeatureParser;
465+
466+
impl<S: Stage> CombineAttributeParser<S> for ForceTargetFeatureParser {
467+
type Item = (Symbol, Span);
468+
const PATH: &[Symbol] = &[sym::force_target_feature];
469+
const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {
470+
features: items,
471+
attr_span: span,
472+
was_forced: true,
473+
};
474+
const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]);
475+
476+
fn extend<'c>(
477+
cx: &'c mut AcceptContext<'_, '_, S>,
478+
args: &'c ArgParser<'_>,
479+
) -> impl IntoIterator<Item = Self::Item> + 'c {
480+
parse_tf_attribute(cx, args)
481+
}
482+
483+
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
484+
Allow(Target::Fn),
485+
Allow(Target::Method(MethodKind::Inherent)),
486+
Allow(Target::Method(MethodKind::Trait { body: true })),
487+
Allow(Target::Method(MethodKind::TraitImpl)),
488+
]);
489+
}

compiler/rustc_attr_parsing/src/context.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ use crate::attributes::allow_unstable::{
2121
};
2222
use crate::attributes::body::CoroutineParser;
2323
use crate::attributes::codegen_attrs::{
24-
ColdParser, CoverageParser, ExportNameParser, NakedParser, NoMangleParser, OptimizeParser,
25-
TargetFeatureParser, TrackCallerParser, UsedParser,
24+
ColdParser, CoverageParser, ExportNameParser, ForceTargetFeatureParser, NakedParser,
25+
NoMangleParser, OptimizeParser, TargetFeatureParser, TrackCallerParser, UsedParser,
2626
};
2727
use crate::attributes::confusables::ConfusablesParser;
2828
use crate::attributes::deprecation::DeprecationParser;
@@ -161,6 +161,7 @@ attribute_parsers!(
161161
// tidy-alphabetical-start
162162
Combine<AllowConstFnUnstableParser>,
163163
Combine<AllowInternalUnstableParser>,
164+
Combine<ForceTargetFeatureParser>,
164165
Combine<ReprParser>,
165166
Combine<TargetFeatureParser>,
166167
Combine<UnstableFeatureBoundParser>,

compiler/rustc_codegen_ssa/src/codegen_attrs.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -188,15 +188,15 @@ fn process_builtin_attrs(
188188
}
189189
}
190190
AttributeKind::Optimize(optimize, _) => codegen_fn_attrs.optimize = *optimize,
191-
AttributeKind::TargetFeature(features, attr_span) => {
191+
AttributeKind::TargetFeature { features, attr_span, was_forced } => {
192192
let Some(sig) = tcx.hir_node_by_def_id(did).fn_sig() else {
193193
tcx.dcx().span_delayed_bug(*attr_span, "target_feature applied to non-fn");
194194
continue;
195195
};
196196
let safe_target_features =
197197
matches!(sig.header.safety, hir::HeaderSafety::SafeTargetFeatures);
198198
codegen_fn_attrs.safe_target_features = safe_target_features;
199-
if safe_target_features {
199+
if safe_target_features && !was_forced {
200200
if tcx.sess.target.is_like_wasm || tcx.sess.opts.actually_rustdoc {
201201
// The `#[target_feature]` attribute is allowed on
202202
// WebAssembly targets on all functions. Prior to stabilizing
@@ -227,6 +227,7 @@ fn process_builtin_attrs(
227227
tcx,
228228
did,
229229
features,
230+
*was_forced,
230231
rust_target_features,
231232
&mut codegen_fn_attrs.target_features,
232233
);
@@ -457,7 +458,7 @@ fn check_result(
457458
.collect(),
458459
) {
459460
let span =
460-
find_attr!(tcx.get_all_attrs(did), AttributeKind::TargetFeature(_, span) => *span)
461+
find_attr!(tcx.get_all_attrs(did), AttributeKind::TargetFeature{attr_span: span, ..} => *span)
461462
.unwrap_or_else(|| tcx.def_span(did));
462463

463464
tcx.dcx()

compiler/rustc_codegen_ssa/src/target_features.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use rustc_data_structures::unord::{UnordMap, UnordSet};
33
use rustc_hir::attrs::InstructionSetAttr;
44
use rustc_hir::def::DefKind;
55
use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
6-
use rustc_middle::middle::codegen_fn_attrs::TargetFeature;
6+
use rustc_middle::middle::codegen_fn_attrs::{TargetFeature, TargetFeatureKind};
77
use rustc_middle::query::Providers;
88
use rustc_middle::ty::TyCtxt;
99
use rustc_session::Session;
@@ -22,6 +22,7 @@ pub(crate) fn from_target_feature_attr(
2222
tcx: TyCtxt<'_>,
2323
did: LocalDefId,
2424
features: &[(Symbol, Span)],
25+
was_forced: bool,
2526
rust_target_features: &UnordMap<String, target_features::Stability>,
2627
target_features: &mut Vec<TargetFeature>,
2728
) {
@@ -88,7 +89,14 @@ pub(crate) fn from_target_feature_attr(
8889
}
8990
}
9091
}
91-
target_features.push(TargetFeature { name, implied: name != feature })
92+
let kind = if name != feature {
93+
TargetFeatureKind::Implied
94+
} else if was_forced {
95+
TargetFeatureKind::Forced
96+
} else {
97+
TargetFeatureKind::Enabled
98+
};
99+
target_features.push(TargetFeature { name, kind })
92100
}
93101
}
94102
}

compiler/rustc_feature/src/builtin_attrs.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -744,6 +744,10 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
744744
template!(List: &["set"], "https://doc.rust-lang.org/reference/attributes/codegen.html#the-instruction_set-attribute"),
745745
ErrorPreceding, EncodeCrossCrate::No
746746
),
747+
gated!(
748+
unsafe force_target_feature, Normal, template!(List: &[r#"enable = "name""#]),
749+
DuplicatesOk, EncodeCrossCrate::No, effective_target_features, experimental!(force_target_feature)
750+
),
747751
gated!(
748752
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,
749753
EncodeCrossCrate::No, sanitize, experimental!(sanitize),

compiler/rustc_feature/src/unstable.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,8 @@ declare_features! (
480480
(unstable, doc_cfg_hide, "1.57.0", Some(43781)),
481481
/// Allows `#[doc(masked)]`.
482482
(unstable, doc_masked, "1.21.0", Some(44027)),
483+
/// Allows features to allow target_feature to better interact with traits.
484+
(incomplete, effective_target_features, "CURRENT_RUSTC_VERSION", Some(143352)),
483485
/// Allows the .use postfix syntax `x.use` and use closures `use |x| { ... }`
484486
(incomplete, ergonomic_clones, "1.87.0", Some(132290)),
485487
/// Allows exhaustive pattern matching on types that contain uninhabited types.

compiler/rustc_hir/src/attrs/data_structures.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -498,8 +498,9 @@ pub enum AttributeKind {
498498
/// Represents `#[rustc_std_internal_symbol]`.
499499
StdInternalSymbol(Span),
500500

501-
/// Represents `#[target_feature(enable = "...")]`
502-
TargetFeature(ThinVec<(Symbol, Span)>, Span),
501+
/// Represents `#[target_feature(enable = "...")]` and
502+
/// `#[unsafe(force_target_feature(enable = "...")]`.
503+
TargetFeature { features: ThinVec<(Symbol, Span)>, attr_span: Span, was_forced: bool },
503504

504505
/// Represents `#[track_caller]`
505506
TrackCaller(Span),

compiler/rustc_hir/src/attrs/encode_cross_crate.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ impl AttributeKind {
7878
SpecializationTrait(..) => No,
7979
Stability { .. } => Yes,
8080
StdInternalSymbol(..) => No,
81-
TargetFeature(..) => No,
81+
TargetFeature { .. } => No,
8282
TrackCaller(..) => Yes,
8383
TypeConst(..) => Yes,
8484
UnsafeSpecializationMarker(..) => No,

compiler/rustc_middle/src/middle/codegen_fn_attrs.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,23 @@ pub struct CodegenFnAttrs {
7676
pub patchable_function_entry: Option<PatchableFunctionEntry>,
7777
}
7878

79+
#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable, PartialEq, Eq)]
80+
pub enum TargetFeatureKind {
81+
/// The feature is implied by another feature, rather than explicitly added by the
82+
/// `#[target_feature]` attribute
83+
Implied,
84+
/// The feature is added by the regular `target_feature` attribute.
85+
Enabled,
86+
/// The feature is added by the unsafe `force_target_feature` attribute.
87+
Forced,
88+
}
89+
7990
#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)]
8091
pub struct TargetFeature {
8192
/// The name of the target feature (e.g. "avx")
8293
pub name: Symbol,
83-
/// The feature is implied by another feature, rather than explicitly added by the
84-
/// `#[target_feature]` attribute
85-
pub implied: bool,
94+
/// The way this feature was enabled.
95+
pub kind: TargetFeatureKind,
8696
}
8797

8898
#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)]

0 commit comments

Comments
 (0)