Skip to content

Commit e96036f

Browse files
authored
Rollup merge of rust-lang#69274 - LeSeulArtichaut:target-feature-11, r=hanna-kruppe
Implement RFC 2396: `#[target_feature]` 1.1 Tracking issue: rust-lang#69098 r? @nikomatsakis cc @gnzlbg @joshtriplett
2 parents 53d3bc0 + 8d9f73a commit e96036f

19 files changed

+392
-23
lines changed

src/librustc_feature/active.rs

+3
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,9 @@ declare_features! (
559559
/// Allow negative trait implementations.
560560
(active, negative_impls, "1.44.0", Some(68318), None),
561561

562+
/// Allows the use of `#[target_feature]` on safe functions.
563+
(active, target_feature_11, "1.45.0", Some(69098), None),
564+
562565
// -------------------------------------------------------------------------
563566
// feature-group-end: actual feature gates
564567
// -------------------------------------------------------------------------

src/librustc_middle/ty/error.rs

+22-1
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ use rustc_ast::ast;
33
use rustc_errors::{pluralize, Applicability, DiagnosticBuilder};
44
use rustc_hir as hir;
55
use rustc_hir::def_id::DefId;
6+
use rustc_span::symbol::sym;
67
use rustc_span::Span;
78
use rustc_target::spec::abi;
89

910
use std::borrow::Cow;
1011
use std::fmt;
12+
use std::ops::Deref;
1113

1214
#[derive(Clone, Copy, Debug, PartialEq, Eq, TypeFoldable)]
1315
pub struct ExpectedFound<T> {
@@ -58,6 +60,8 @@ pub enum TypeError<'tcx> {
5860
ConstMismatch(ExpectedFound<&'tcx ty::Const<'tcx>>),
5961

6062
IntrinsicCast,
63+
/// Safe `#[target_feature]` functions are not assignable to safe function pointers.
64+
TargetFeatureCast(DefId),
6165
}
6266

6367
pub enum UnconstrainedNumeric {
@@ -183,6 +187,10 @@ impl<'tcx> fmt::Display for TypeError<'tcx> {
183187
write!(f, "expected `{}`, found `{}`", values.expected, values.found)
184188
}
185189
IntrinsicCast => write!(f, "cannot coerce intrinsics to function pointers"),
190+
TargetFeatureCast(_) => write!(
191+
f,
192+
"cannot coerce functions with `#[target_feature]` to safe function pointers"
193+
),
186194
ObjectUnsafeCoercion(_) => write!(f, "coercion to object-unsafe trait object"),
187195
}
188196
}
@@ -193,7 +201,8 @@ impl<'tcx> TypeError<'tcx> {
193201
use self::TypeError::*;
194202
match self {
195203
CyclicTy(_) | UnsafetyMismatch(_) | Mismatch | AbiMismatch(_) | FixedArraySize(_)
196-
| Sorts(_) | IntMismatch(_) | FloatMismatch(_) | VariadicMismatch(_) => false,
204+
| Sorts(_) | IntMismatch(_) | FloatMismatch(_) | VariadicMismatch(_)
205+
| TargetFeatureCast(_) => false,
197206

198207
Mutability
199208
| TupleSize(_)
@@ -489,6 +498,18 @@ impl Trait for X {
489498
);
490499
}
491500
}
501+
TargetFeatureCast(def_id) => {
502+
let attrs = self.get_attrs(*def_id);
503+
let target_spans = attrs
504+
.deref()
505+
.iter()
506+
.filter(|attr| attr.has_name(sym::target_feature))
507+
.map(|attr| attr.span);
508+
db.note(
509+
"functions with `#[target_feature]` can only be coerced to `unsafe` function pointers"
510+
);
511+
db.span_labels(target_spans, "`#[target_feature]` added here");
512+
}
492513
_ => {}
493514
}
494515
}

src/librustc_middle/ty/structural_impls.rs

+1
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,7 @@ impl<'a, 'tcx> Lift<'tcx> for ty::error::TypeError<'a> {
645645
ExistentialMismatch(ref x) => return tcx.lift(x).map(ExistentialMismatch),
646646
ConstMismatch(ref x) => return tcx.lift(x).map(ConstMismatch),
647647
IntrinsicCast => IntrinsicCast,
648+
TargetFeatureCast(ref x) => TargetFeatureCast(*x),
648649
ObjectUnsafeCoercion(ref x) => return tcx.lift(x).map(ObjectUnsafeCoercion),
649650
})
650651
}

src/librustc_mir/transform/check_unsafety.rs

+25-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use crate::util;
1919

2020
pub struct UnsafetyChecker<'a, 'tcx> {
2121
body: &'a Body<'tcx>,
22+
body_did: LocalDefId,
2223
const_context: bool,
2324
min_const_fn: bool,
2425
violations: Vec<UnsafetyViolation>,
@@ -35,6 +36,7 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
3536
const_context: bool,
3637
min_const_fn: bool,
3738
body: &'a Body<'tcx>,
39+
body_did: LocalDefId,
3840
tcx: TyCtxt<'tcx>,
3941
param_env: ty::ParamEnv<'tcx>,
4042
) -> Self {
@@ -44,6 +46,7 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
4446
}
4547
Self {
4648
body,
49+
body_did,
4750
const_context,
4851
min_const_fn,
4952
violations: vec![],
@@ -87,6 +90,10 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
8790
UnsafetyViolationKind::GeneralAndConstFn,
8891
)
8992
}
93+
94+
if let ty::FnDef(func_id, _) = func_ty.kind {
95+
self.check_target_features(func_id);
96+
}
9097
}
9198
}
9299
self.super_terminator(terminator, location);
@@ -436,6 +443,22 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
436443
}
437444
}
438445
}
446+
447+
/// Checks whether calling `func_did` needs an `unsafe` context or not, i.e. whether
448+
/// the called function has target features the calling function hasn't.
449+
fn check_target_features(&mut self, func_did: DefId) {
450+
let callee_features = &self.tcx.codegen_fn_attrs(func_did).target_features;
451+
let self_features = &self.tcx.codegen_fn_attrs(self.body_did).target_features;
452+
453+
// Is `callee_features` a subset of `calling_features`?
454+
if !callee_features.iter().all(|feature| self_features.contains(feature)) {
455+
self.require_unsafe(
456+
"call to function with `#[target_feature]`",
457+
"can only be called if the required target features are available",
458+
UnsafetyViolationKind::GeneralAndConstFn,
459+
)
460+
}
461+
}
439462
}
440463

441464
pub(crate) fn provide(providers: &mut Providers<'_>) {
@@ -502,7 +525,8 @@ fn unsafety_check_result(tcx: TyCtxt<'_>, def_id: LocalDefId) -> UnsafetyCheckRe
502525
}
503526
hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => (true, false),
504527
};
505-
let mut checker = UnsafetyChecker::new(const_context, min_const_fn, body, tcx, param_env);
528+
let mut checker =
529+
UnsafetyChecker::new(const_context, min_const_fn, body, def_id, tcx, param_env);
506530
checker.visit_body(&body);
507531

508532
check_unused_unsafe(tcx, def_id, &checker.used_unsafe, &mut checker.inherited_blocks);

src/librustc_span/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,7 @@ symbols! {
722722
suggestion,
723723
sync_trait,
724724
target_feature,
725+
target_feature_11,
725726
target_has_atomic,
726727
target_has_atomic_load_store,
727728
target_thread_local,

src/librustc_typeck/check/coercion.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -691,12 +691,22 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
691691
debug!("coerce_from_fn_item(a={:?}, b={:?})", a, b);
692692

693693
match b.kind {
694-
ty::FnPtr(_) => {
694+
ty::FnPtr(b_sig) => {
695695
let a_sig = a.fn_sig(self.tcx);
696696
// Intrinsics are not coercible to function pointers
697697
if a_sig.abi() == Abi::RustIntrinsic || a_sig.abi() == Abi::PlatformIntrinsic {
698698
return Err(TypeError::IntrinsicCast);
699699
}
700+
701+
// Safe `#[target_feature]` functions are not assignable to safe fn pointers (RFC 2396).
702+
if let ty::FnDef(def_id, _) = a.kind {
703+
if b_sig.unsafety() == hir::Unsafety::Normal
704+
&& !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty()
705+
{
706+
return Err(TypeError::TargetFeatureCast(def_id));
707+
}
708+
}
709+
700710
let InferOk { value: a_sig, mut obligations } =
701711
self.normalize_associated_types_in_as_infer_ok(self.cause.span, &a_sig);
702712

src/librustc_typeck/collect.rs

+43-8
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use rustc_hir::def::{CtorKind, DefKind, Res};
2929
use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE};
3030
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
3131
use rustc_hir::weak_lang_items;
32-
use rustc_hir::{GenericParamKind, Node, Unsafety};
32+
use rustc_hir::{GenericParamKind, Node};
3333
use rustc_middle::hir::map::blocks::FnLikeNode;
3434
use rustc_middle::hir::map::Map;
3535
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
@@ -2404,13 +2404,12 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
24042404
codegen_fn_attrs.export_name = Some(s);
24052405
}
24062406
} else if attr.check_name(sym::target_feature) {
2407-
if tcx.is_closure(id) || tcx.fn_sig(id).unsafety() == Unsafety::Normal {
2408-
let msg = "`#[target_feature(..)]` can only be applied to `unsafe` functions";
2409-
tcx.sess
2410-
.struct_span_err(attr.span, msg)
2411-
.span_label(attr.span, "can only be applied to `unsafe` functions")
2412-
.span_label(tcx.def_span(id), "not an `unsafe` function")
2413-
.emit();
2407+
if !tcx.features().target_feature_11 {
2408+
check_target_feature_safe_fn(tcx, id, attr.span);
2409+
} else if let Some(local_id) = id.as_local() {
2410+
if tcx.fn_sig(id).unsafety() == hir::Unsafety::Normal {
2411+
check_target_feature_trait_unsafe(tcx, local_id, attr.span);
2412+
}
24142413
}
24152414
from_target_feature(tcx, id, attr, &whitelist, &mut codegen_fn_attrs.target_features);
24162415
} else if attr.check_name(sym::linkage) {
@@ -2657,3 +2656,39 @@ fn check_link_name_xor_ordinal(
26572656
tcx.sess.err(msg);
26582657
}
26592658
}
2659+
2660+
/// Checks the function annotated with `#[target_feature]` is unsafe,
2661+
/// reporting an error if it isn't.
2662+
fn check_target_feature_safe_fn(tcx: TyCtxt<'_>, id: DefId, attr_span: Span) {
2663+
if tcx.is_closure(id) || tcx.fn_sig(id).unsafety() == hir::Unsafety::Normal {
2664+
let mut err = feature_err(
2665+
&tcx.sess.parse_sess,
2666+
sym::target_feature_11,
2667+
attr_span,
2668+
"`#[target_feature(..)]` can only be applied to `unsafe` functions",
2669+
);
2670+
err.span_label(tcx.def_span(id), "not an `unsafe` function");
2671+
err.emit();
2672+
}
2673+
}
2674+
2675+
/// Checks the function annotated with `#[target_feature]` is not a safe
2676+
/// trait method implementation, reporting an error if it is.
2677+
fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId, attr_span: Span) {
2678+
let hir_id = tcx.hir().as_local_hir_id(id);
2679+
let node = tcx.hir().get(hir_id);
2680+
if let Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }) = node {
2681+
let parent_id = tcx.hir().get_parent_item(hir_id);
2682+
let parent_item = tcx.hir().expect_item(parent_id);
2683+
if let hir::ItemKind::Impl { of_trait: Some(_), .. } = parent_item.kind {
2684+
tcx.sess
2685+
.struct_span_err(
2686+
attr_span,
2687+
"`#[target_feature(..)]` cannot be applied to safe trait method",
2688+
)
2689+
.span_label(attr_span, "cannot be applied to safe trait method")
2690+
.span_label(tcx.def_span(id), "not an `unsafe` function")
2691+
.emit();
2692+
}
2693+
}
2694+
}

src/test/ui/macros/issue-68060.stderr

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
error: `#[target_feature(..)]` can only be applied to `unsafe` functions
1+
error[E0658]: `#[target_feature(..)]` can only be applied to `unsafe` functions
22
--> $DIR/issue-68060.rs:6:13
33
|
44
LL | #[target_feature(enable = "")]
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can only be applied to `unsafe` functions
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
66
...
77
LL | |_| (),
88
| ------ not an `unsafe` function
9+
|
10+
= note: see issue #69098 <https://github.com/rust-lang/rust/issues/69098> for more information
11+
= help: add `#![feature(target_feature_11)]` to the crate attributes to enable
912

1013
error: the feature named `` is not valid for this target
1114
--> $DIR/issue-68060.rs:6:30
@@ -21,4 +24,5 @@ LL | #[track_caller]
2124

2225
error: aborting due to 3 previous errors
2326

24-
For more information about this error, try `rustc --explain E0737`.
27+
Some errors have detailed explanations: E0658, E0737.
28+
For more information about an error, try `rustc --explain E0658`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Tests the new rules added by RFC 2396, including:
2+
// - applying `#[target_feature]` to safe functions is allowed
3+
// - calling functions with `#[target_feature]` is allowed in
4+
// functions which have (at least) the same features
5+
// - calling functions with `#[target_feature]` is allowed in
6+
// unsafe contexts
7+
// - functions with `#[target_feature]` can coerce to unsafe fn pointers
8+
9+
// check-pass
10+
// only-x86_64
11+
12+
#![feature(target_feature_11)]
13+
14+
#[target_feature(enable = "sse2")]
15+
const fn sse2() {}
16+
17+
#[cfg(target_feature = "sse2")]
18+
const SSE2_ONLY: () = unsafe {
19+
sse2();
20+
};
21+
22+
#[target_feature(enable = "sse2")]
23+
fn also_sse2() {
24+
sse2();
25+
}
26+
27+
#[target_feature(enable = "sse2")]
28+
#[target_feature(enable = "avx")]
29+
fn sse2_and_avx() {
30+
sse2();
31+
}
32+
33+
struct Foo;
34+
35+
impl Foo {
36+
#[target_feature(enable = "sse2")]
37+
fn sse2(&self) {
38+
sse2();
39+
}
40+
}
41+
42+
fn main() {
43+
if cfg!(target_feature = "sse2") {
44+
unsafe {
45+
sse2();
46+
Foo.sse2();
47+
}
48+
}
49+
let sse2_ptr: unsafe fn() = sse2;
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// only-x86_64
2+
3+
#[target_feature(enable = "sse2")] //~ ERROR can only be applied to `unsafe` functions
4+
fn foo() {}
5+
6+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error[E0658]: `#[target_feature(..)]` can only be applied to `unsafe` functions
2+
--> $DIR/feature-gate-target_feature_11.rs:3:1
3+
|
4+
LL | #[target_feature(enable = "sse2")]
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
LL | fn foo() {}
7+
| ----------- not an `unsafe` function
8+
|
9+
= note: see issue #69098 <https://github.com/rust-lang/rust/issues/69098> for more information
10+
= help: add `#![feature(target_feature_11)]` to the crate attributes to enable
11+
12+
error: aborting due to previous error
13+
14+
For more information about this error, try `rustc --explain E0658`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// only-x86_64
2+
3+
#![feature(target_feature_11)]
4+
5+
#[target_feature(enable = "sse2")]
6+
fn foo() {}
7+
8+
fn main() {
9+
let foo: fn() = foo; //~ ERROR mismatched types
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/fn-ptr.rs:9:21
3+
|
4+
LL | #[target_feature(enable = "sse2")]
5+
| ---------------------------------- `#[target_feature]` added here
6+
...
7+
LL | let foo: fn() = foo;
8+
| ---- ^^^ cannot coerce functions with `#[target_feature]` to safe function pointers
9+
| |
10+
| expected due to this
11+
|
12+
= note: expected fn pointer `fn()`
13+
found fn item `fn() {foo}`
14+
= note: functions with `#[target_feature]` can only be coerced to `unsafe` function pointers
15+
16+
error: aborting due to previous error
17+
18+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)