about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorDylan DPC <dylan.dpc@gmail.com>2020-05-02 18:27:27 +0200
committerGitHub <noreply@github.com>2020-05-02 18:27:27 +0200
commite96036fca2d9aac14a4329a5f355cceeefdde5cc (patch)
tree09b3fefd52a0fa275a32bbe26d4efd1fe66c3e6f /src
parent53d3bc02ed90eba01c5dbc5b2d0c4cabb67ffb4d (diff)
parent8d9f73a6902064e1a5508af98d77aed2ae055944 (diff)
downloadrust-e96036fca2d9aac14a4329a5f355cceeefdde5cc.tar.gz
rust-e96036fca2d9aac14a4329a5f355cceeefdde5cc.zip
Rollup merge of #69274 - LeSeulArtichaut:target-feature-11, r=hanna-kruppe
Implement RFC 2396: `#[target_feature]` 1.1

Tracking issue: #69098

r? @nikomatsakis
cc @gnzlbg @joshtriplett
Diffstat (limited to 'src')
-rw-r--r--src/librustc_feature/active.rs3
-rw-r--r--src/librustc_middle/ty/error.rs23
-rw-r--r--src/librustc_middle/ty/structural_impls.rs1
-rw-r--r--src/librustc_mir/transform/check_unsafety.rs26
-rw-r--r--src/librustc_span/symbol.rs1
-rw-r--r--src/librustc_typeck/check/coercion.rs12
-rw-r--r--src/librustc_typeck/collect.rs51
-rw-r--r--src/test/ui/macros/issue-68060.stderr10
-rw-r--r--src/test/ui/rfcs/rfc-2396-target_feature-11/check-pass.rs50
-rw-r--r--src/test/ui/rfcs/rfc-2396-target_feature-11/feature-gate-target_feature_11.rs6
-rw-r--r--src/test/ui/rfcs/rfc-2396-target_feature-11/feature-gate-target_feature_11.stderr14
-rw-r--r--src/test/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.rs10
-rw-r--r--src/test/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.stderr18
-rw-r--r--src/test/ui/rfcs/rfc-2396-target_feature-11/safe-calls.rs47
-rw-r--r--src/test/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr83
-rw-r--r--src/test/ui/rfcs/rfc-2396-target_feature-11/trait-impl.rs21
-rw-r--r--src/test/ui/rfcs/rfc-2396-target_feature-11/trait-impl.stderr11
-rw-r--r--src/test/ui/target-feature/invalid-attribute.rs6
-rw-r--r--src/test/ui/target-feature/invalid-attribute.stderr22
19 files changed, 392 insertions, 23 deletions
diff --git a/src/librustc_feature/active.rs b/src/librustc_feature/active.rs
index 7b3c599e8c7..cec9e0ce3af 100644
--- a/src/librustc_feature/active.rs
+++ b/src/librustc_feature/active.rs
@@ -559,6 +559,9 @@ declare_features! (
     /// Allow negative trait implementations.
     (active, negative_impls, "1.44.0", Some(68318), None),
 
+    /// Allows the use of `#[target_feature]` on safe functions.
+    (active, target_feature_11, "1.45.0", Some(69098), None),
+
     // -------------------------------------------------------------------------
     // feature-group-end: actual feature gates
     // -------------------------------------------------------------------------
diff --git a/src/librustc_middle/ty/error.rs b/src/librustc_middle/ty/error.rs
index d0bc0d5fabf..78a94b62d47 100644
--- a/src/librustc_middle/ty/error.rs
+++ b/src/librustc_middle/ty/error.rs
@@ -3,11 +3,13 @@ use rustc_ast::ast;
 use rustc_errors::{pluralize, Applicability, DiagnosticBuilder};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
+use rustc_span::symbol::sym;
 use rustc_span::Span;
 use rustc_target::spec::abi;
 
 use std::borrow::Cow;
 use std::fmt;
+use std::ops::Deref;
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq, TypeFoldable)]
 pub struct ExpectedFound<T> {
@@ -58,6 +60,8 @@ pub enum TypeError<'tcx> {
     ConstMismatch(ExpectedFound<&'tcx ty::Const<'tcx>>),
 
     IntrinsicCast,
+    /// Safe `#[target_feature]` functions are not assignable to safe function pointers.
+    TargetFeatureCast(DefId),
 }
 
 pub enum UnconstrainedNumeric {
@@ -183,6 +187,10 @@ impl<'tcx> fmt::Display for TypeError<'tcx> {
                 write!(f, "expected `{}`, found `{}`", values.expected, values.found)
             }
             IntrinsicCast => write!(f, "cannot coerce intrinsics to function pointers"),
+            TargetFeatureCast(_) => write!(
+                f,
+                "cannot coerce functions with `#[target_feature]` to safe function pointers"
+            ),
             ObjectUnsafeCoercion(_) => write!(f, "coercion to object-unsafe trait object"),
         }
     }
@@ -193,7 +201,8 @@ impl<'tcx> TypeError<'tcx> {
         use self::TypeError::*;
         match self {
             CyclicTy(_) | UnsafetyMismatch(_) | Mismatch | AbiMismatch(_) | FixedArraySize(_)
-            | Sorts(_) | IntMismatch(_) | FloatMismatch(_) | VariadicMismatch(_) => false,
+            | Sorts(_) | IntMismatch(_) | FloatMismatch(_) | VariadicMismatch(_)
+            | TargetFeatureCast(_) => false,
 
             Mutability
             | TupleSize(_)
@@ -489,6 +498,18 @@ impl Trait for X {
                     );
                 }
             }
+            TargetFeatureCast(def_id) => {
+                let attrs = self.get_attrs(*def_id);
+                let target_spans = attrs
+                    .deref()
+                    .iter()
+                    .filter(|attr| attr.has_name(sym::target_feature))
+                    .map(|attr| attr.span);
+                db.note(
+                    "functions with `#[target_feature]` can only be coerced to `unsafe` function pointers"
+                );
+                db.span_labels(target_spans, "`#[target_feature]` added here");
+            }
             _ => {}
         }
     }
diff --git a/src/librustc_middle/ty/structural_impls.rs b/src/librustc_middle/ty/structural_impls.rs
index 1c0ffe12314..c8406a024ec 100644
--- a/src/librustc_middle/ty/structural_impls.rs
+++ b/src/librustc_middle/ty/structural_impls.rs
@@ -645,6 +645,7 @@ impl<'a, 'tcx> Lift<'tcx> for ty::error::TypeError<'a> {
             ExistentialMismatch(ref x) => return tcx.lift(x).map(ExistentialMismatch),
             ConstMismatch(ref x) => return tcx.lift(x).map(ConstMismatch),
             IntrinsicCast => IntrinsicCast,
+            TargetFeatureCast(ref x) => TargetFeatureCast(*x),
             ObjectUnsafeCoercion(ref x) => return tcx.lift(x).map(ObjectUnsafeCoercion),
         })
     }
diff --git a/src/librustc_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs
index 1c1560cbf9d..a015dc60352 100644
--- a/src/librustc_mir/transform/check_unsafety.rs
+++ b/src/librustc_mir/transform/check_unsafety.rs
@@ -19,6 +19,7 @@ use crate::util;
 
 pub struct UnsafetyChecker<'a, 'tcx> {
     body: &'a Body<'tcx>,
+    body_did: LocalDefId,
     const_context: bool,
     min_const_fn: bool,
     violations: Vec<UnsafetyViolation>,
@@ -35,6 +36,7 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
         const_context: bool,
         min_const_fn: bool,
         body: &'a Body<'tcx>,
+        body_did: LocalDefId,
         tcx: TyCtxt<'tcx>,
         param_env: ty::ParamEnv<'tcx>,
     ) -> Self {
@@ -44,6 +46,7 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
         }
         Self {
             body,
+            body_did,
             const_context,
             min_const_fn,
             violations: vec![],
@@ -87,6 +90,10 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
                         UnsafetyViolationKind::GeneralAndConstFn,
                     )
                 }
+
+                if let ty::FnDef(func_id, _) = func_ty.kind {
+                    self.check_target_features(func_id);
+                }
             }
         }
         self.super_terminator(terminator, location);
@@ -436,6 +443,22 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
             }
         }
     }
+
+    /// Checks whether calling `func_did` needs an `unsafe` context or not, i.e. whether
+    /// the called function has target features the calling function hasn't.
+    fn check_target_features(&mut self, func_did: DefId) {
+        let callee_features = &self.tcx.codegen_fn_attrs(func_did).target_features;
+        let self_features = &self.tcx.codegen_fn_attrs(self.body_did).target_features;
+
+        // Is `callee_features` a subset of `calling_features`?
+        if !callee_features.iter().all(|feature| self_features.contains(feature)) {
+            self.require_unsafe(
+                "call to function with `#[target_feature]`",
+                "can only be called if the required target features are available",
+                UnsafetyViolationKind::GeneralAndConstFn,
+            )
+        }
+    }
 }
 
 pub(crate) fn provide(providers: &mut Providers<'_>) {
@@ -502,7 +525,8 @@ fn unsafety_check_result(tcx: TyCtxt<'_>, def_id: LocalDefId) -> UnsafetyCheckRe
         }
         hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => (true, false),
     };
-    let mut checker = UnsafetyChecker::new(const_context, min_const_fn, body, tcx, param_env);
+    let mut checker =
+        UnsafetyChecker::new(const_context, min_const_fn, body, def_id, tcx, param_env);
     checker.visit_body(&body);
 
     check_unused_unsafe(tcx, def_id, &checker.used_unsafe, &mut checker.inherited_blocks);
diff --git a/src/librustc_span/symbol.rs b/src/librustc_span/symbol.rs
index 1575e6fd533..74d73404acd 100644
--- a/src/librustc_span/symbol.rs
+++ b/src/librustc_span/symbol.rs
@@ -722,6 +722,7 @@ symbols! {
         suggestion,
         sync_trait,
         target_feature,
+        target_feature_11,
         target_has_atomic,
         target_has_atomic_load_store,
         target_thread_local,
diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs
index e3b16eaaef2..bf36b2cdffe 100644
--- a/src/librustc_typeck/check/coercion.rs
+++ b/src/librustc_typeck/check/coercion.rs
@@ -691,12 +691,22 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
         debug!("coerce_from_fn_item(a={:?}, b={:?})", a, b);
 
         match b.kind {
-            ty::FnPtr(_) => {
+            ty::FnPtr(b_sig) => {
                 let a_sig = a.fn_sig(self.tcx);
                 // Intrinsics are not coercible to function pointers
                 if a_sig.abi() == Abi::RustIntrinsic || a_sig.abi() == Abi::PlatformIntrinsic {
                     return Err(TypeError::IntrinsicCast);
                 }
+
+                // Safe `#[target_feature]` functions are not assignable to safe fn pointers (RFC 2396).
+                if let ty::FnDef(def_id, _) = a.kind {
+                    if b_sig.unsafety() == hir::Unsafety::Normal
+                        && !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty()
+                    {
+                        return Err(TypeError::TargetFeatureCast(def_id));
+                    }
+                }
+
                 let InferOk { value: a_sig, mut obligations } =
                     self.normalize_associated_types_in_as_infer_ok(self.cause.span, &a_sig);
 
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index e6aa53dd4f3..b6ad763ec39 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -29,7 +29,7 @@ use rustc_hir::def::{CtorKind, DefKind, Res};
 use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE};
 use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
 use rustc_hir::weak_lang_items;
-use rustc_hir::{GenericParamKind, Node, Unsafety};
+use rustc_hir::{GenericParamKind, Node};
 use rustc_middle::hir::map::blocks::FnLikeNode;
 use rustc_middle::hir::map::Map;
 use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
@@ -2404,13 +2404,12 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
                 codegen_fn_attrs.export_name = Some(s);
             }
         } else if attr.check_name(sym::target_feature) {
-            if tcx.is_closure(id) || tcx.fn_sig(id).unsafety() == Unsafety::Normal {
-                let msg = "`#[target_feature(..)]` can only be applied to `unsafe` functions";
-                tcx.sess
-                    .struct_span_err(attr.span, msg)
-                    .span_label(attr.span, "can only be applied to `unsafe` functions")
-                    .span_label(tcx.def_span(id), "not an `unsafe` function")
-                    .emit();
+            if !tcx.features().target_feature_11 {
+                check_target_feature_safe_fn(tcx, id, attr.span);
+            } else if let Some(local_id) = id.as_local() {
+                if tcx.fn_sig(id).unsafety() == hir::Unsafety::Normal {
+                    check_target_feature_trait_unsafe(tcx, local_id, attr.span);
+                }
             }
             from_target_feature(tcx, id, attr, &whitelist, &mut codegen_fn_attrs.target_features);
         } else if attr.check_name(sym::linkage) {
@@ -2657,3 +2656,39 @@ fn check_link_name_xor_ordinal(
         tcx.sess.err(msg);
     }
 }
+
+/// Checks the function annotated with `#[target_feature]` is unsafe,
+/// reporting an error if it isn't.
+fn check_target_feature_safe_fn(tcx: TyCtxt<'_>, id: DefId, attr_span: Span) {
+    if tcx.is_closure(id) || tcx.fn_sig(id).unsafety() == hir::Unsafety::Normal {
+        let mut err = feature_err(
+            &tcx.sess.parse_sess,
+            sym::target_feature_11,
+            attr_span,
+            "`#[target_feature(..)]` can only be applied to `unsafe` functions",
+        );
+        err.span_label(tcx.def_span(id), "not an `unsafe` function");
+        err.emit();
+    }
+}
+
+/// Checks the function annotated with `#[target_feature]` is not a safe
+/// trait method implementation, reporting an error if it is.
+fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId, attr_span: Span) {
+    let hir_id = tcx.hir().as_local_hir_id(id);
+    let node = tcx.hir().get(hir_id);
+    if let Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }) = node {
+        let parent_id = tcx.hir().get_parent_item(hir_id);
+        let parent_item = tcx.hir().expect_item(parent_id);
+        if let hir::ItemKind::Impl { of_trait: Some(_), .. } = parent_item.kind {
+            tcx.sess
+                .struct_span_err(
+                    attr_span,
+                    "`#[target_feature(..)]` cannot be applied to safe trait method",
+                )
+                .span_label(attr_span, "cannot be applied to safe trait method")
+                .span_label(tcx.def_span(id), "not an `unsafe` function")
+                .emit();
+        }
+    }
+}
diff --git a/src/test/ui/macros/issue-68060.stderr b/src/test/ui/macros/issue-68060.stderr
index 3ea49e614e6..22187c4a409 100644
--- a/src/test/ui/macros/issue-68060.stderr
+++ b/src/test/ui/macros/issue-68060.stderr
@@ -1,11 +1,14 @@
-error: `#[target_feature(..)]` can only be applied to `unsafe` functions
+error[E0658]: `#[target_feature(..)]` can only be applied to `unsafe` functions
   --> $DIR/issue-68060.rs:6:13
    |
 LL |             #[target_feature(enable = "")]
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can only be applied to `unsafe` functions
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 ...
 LL |             |_| (),
    |             ------ not an `unsafe` function
+   |
+   = note: see issue #69098 <https://github.com/rust-lang/rust/issues/69098> for more information
+   = help: add `#![feature(target_feature_11)]` to the crate attributes to enable
 
 error: the feature named `` is not valid for this target
   --> $DIR/issue-68060.rs:6:30
@@ -21,4 +24,5 @@ LL |             #[track_caller]
 
 error: aborting due to 3 previous errors
 
-For more information about this error, try `rustc --explain E0737`.
+Some errors have detailed explanations: E0658, E0737.
+For more information about an error, try `rustc --explain E0658`.
diff --git a/src/test/ui/rfcs/rfc-2396-target_feature-11/check-pass.rs b/src/test/ui/rfcs/rfc-2396-target_feature-11/check-pass.rs
new file mode 100644
index 00000000000..58a2c271ecf
--- /dev/null
+++ b/src/test/ui/rfcs/rfc-2396-target_feature-11/check-pass.rs
@@ -0,0 +1,50 @@
+// Tests the new rules added by RFC 2396, including:
+// - applying `#[target_feature]` to safe functions is allowed
+// - calling functions with `#[target_feature]` is allowed in
+//   functions which have (at least) the same features
+// - calling functions with `#[target_feature]` is allowed in
+//   unsafe contexts
+// - functions with `#[target_feature]` can coerce to unsafe fn pointers
+
+// check-pass
+// only-x86_64
+
+#![feature(target_feature_11)]
+
+#[target_feature(enable = "sse2")]
+const fn sse2() {}
+
+#[cfg(target_feature = "sse2")]
+const SSE2_ONLY: () = unsafe {
+    sse2();
+};
+
+#[target_feature(enable = "sse2")]
+fn also_sse2() {
+    sse2();
+}
+
+#[target_feature(enable = "sse2")]
+#[target_feature(enable = "avx")]
+fn sse2_and_avx() {
+    sse2();
+}
+
+struct Foo;
+
+impl Foo {
+    #[target_feature(enable = "sse2")]
+    fn sse2(&self) {
+        sse2();
+    }
+}
+
+fn main() {
+    if cfg!(target_feature = "sse2") {
+        unsafe {
+            sse2();
+            Foo.sse2();
+        }
+    }
+    let sse2_ptr: unsafe fn() = sse2;
+}
diff --git a/src/test/ui/rfcs/rfc-2396-target_feature-11/feature-gate-target_feature_11.rs b/src/test/ui/rfcs/rfc-2396-target_feature-11/feature-gate-target_feature_11.rs
new file mode 100644
index 00000000000..975d7a1f694
--- /dev/null
+++ b/src/test/ui/rfcs/rfc-2396-target_feature-11/feature-gate-target_feature_11.rs
@@ -0,0 +1,6 @@
+// only-x86_64
+
+#[target_feature(enable = "sse2")] //~ ERROR can only be applied to `unsafe` functions
+fn foo() {}
+
+fn main() {}
diff --git a/src/test/ui/rfcs/rfc-2396-target_feature-11/feature-gate-target_feature_11.stderr b/src/test/ui/rfcs/rfc-2396-target_feature-11/feature-gate-target_feature_11.stderr
new file mode 100644
index 00000000000..413890f436d
--- /dev/null
+++ b/src/test/ui/rfcs/rfc-2396-target_feature-11/feature-gate-target_feature_11.stderr
@@ -0,0 +1,14 @@
+error[E0658]: `#[target_feature(..)]` can only be applied to `unsafe` functions
+  --> $DIR/feature-gate-target_feature_11.rs:3:1
+   |
+LL | #[target_feature(enable = "sse2")]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | fn foo() {}
+   | ----------- not an `unsafe` function
+   |
+   = note: see issue #69098 <https://github.com/rust-lang/rust/issues/69098> for more information
+   = help: add `#![feature(target_feature_11)]` to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.rs b/src/test/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.rs
new file mode 100644
index 00000000000..3ecea5c5313
--- /dev/null
+++ b/src/test/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.rs
@@ -0,0 +1,10 @@
+// only-x86_64
+
+#![feature(target_feature_11)]
+
+#[target_feature(enable = "sse2")]
+fn foo() {}
+
+fn main() {
+    let foo: fn() = foo; //~ ERROR mismatched types
+}
diff --git a/src/test/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.stderr b/src/test/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.stderr
new file mode 100644
index 00000000000..06cfdde3fb9
--- /dev/null
+++ b/src/test/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.stderr
@@ -0,0 +1,18 @@
+error[E0308]: mismatched types
+  --> $DIR/fn-ptr.rs:9:21
+   |
+LL | #[target_feature(enable = "sse2")]
+   | ---------------------------------- `#[target_feature]` added here
+...
+LL |     let foo: fn() = foo;
+   |              ----   ^^^ cannot coerce functions with `#[target_feature]` to safe function pointers
+   |              |
+   |              expected due to this
+   |
+   = note: expected fn pointer `fn()`
+                 found fn item `fn() {foo}`
+   = note: functions with `#[target_feature]` can only be coerced to `unsafe` function pointers
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/rfcs/rfc-2396-target_feature-11/safe-calls.rs b/src/test/ui/rfcs/rfc-2396-target_feature-11/safe-calls.rs
new file mode 100644
index 00000000000..8da3affc447
--- /dev/null
+++ b/src/test/ui/rfcs/rfc-2396-target_feature-11/safe-calls.rs
@@ -0,0 +1,47 @@
+// only-x86_64
+
+#![feature(target_feature_11)]
+
+#[target_feature(enable = "sse2")]
+const fn sse2() {}
+
+#[target_feature(enable = "avx")]
+#[target_feature(enable = "bmi2")]
+fn avx_bmi2() {}
+
+struct Quux;
+
+impl Quux {
+    #[target_feature(enable = "avx")]
+    #[target_feature(enable = "bmi2")]
+    fn avx_bmi2(&self) {}
+}
+
+fn foo() {
+    sse2();              //~ ERROR call to function with `#[target_feature]` is unsafe
+    avx_bmi2();          //~ ERROR call to function with `#[target_feature]` is unsafe
+    Quux.avx_bmi2();     //~ ERROR call to function with `#[target_feature]` is unsafe
+}
+
+#[target_feature(enable = "sse2")]
+fn bar() {
+    avx_bmi2();          //~ ERROR call to function with `#[target_feature]` is unsafe
+    Quux.avx_bmi2();     //~ ERROR call to function with `#[target_feature]` is unsafe
+}
+
+#[target_feature(enable = "avx")]
+fn baz() {
+    sse2();              //~ ERROR call to function with `#[target_feature]` is unsafe
+    avx_bmi2();          //~ ERROR call to function with `#[target_feature]` is unsafe
+    Quux.avx_bmi2();     //~ ERROR call to function with `#[target_feature]` is unsafe
+}
+
+#[target_feature(enable = "avx")]
+#[target_feature(enable = "bmi2")]
+fn qux() {
+    sse2();              //~ ERROR call to function with `#[target_feature]` is unsafe
+}
+
+const name: () = sse2(); //~ ERROR call to function with `#[target_feature]` is unsafe
+
+fn main() {}
diff --git a/src/test/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr b/src/test/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr
new file mode 100644
index 00000000000..b9f748640b5
--- /dev/null
+++ b/src/test/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr
@@ -0,0 +1,83 @@
+error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
+  --> $DIR/safe-calls.rs:21:5
+   |
+LL |     sse2();
+   |     ^^^^^^ call to function with `#[target_feature]`
+   |
+   = note: can only be called if the required target features are available
+
+error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
+  --> $DIR/safe-calls.rs:22:5
+   |
+LL |     avx_bmi2();
+   |     ^^^^^^^^^^ call to function with `#[target_feature]`
+   |
+   = note: can only be called if the required target features are available
+
+error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
+  --> $DIR/safe-calls.rs:23:5
+   |
+LL |     Quux.avx_bmi2();
+   |     ^^^^^^^^^^^^^^^ call to function with `#[target_feature]`
+   |
+   = note: can only be called if the required target features are available
+
+error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
+  --> $DIR/safe-calls.rs:28:5
+   |
+LL |     avx_bmi2();
+   |     ^^^^^^^^^^ call to function with `#[target_feature]`
+   |
+   = note: can only be called if the required target features are available
+
+error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
+  --> $DIR/safe-calls.rs:29:5
+   |
+LL |     Quux.avx_bmi2();
+   |     ^^^^^^^^^^^^^^^ call to function with `#[target_feature]`
+   |
+   = note: can only be called if the required target features are available
+
+error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
+  --> $DIR/safe-calls.rs:34:5
+   |
+LL |     sse2();
+   |     ^^^^^^ call to function with `#[target_feature]`
+   |
+   = note: can only be called if the required target features are available
+
+error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
+  --> $DIR/safe-calls.rs:35:5
+   |
+LL |     avx_bmi2();
+   |     ^^^^^^^^^^ call to function with `#[target_feature]`
+   |
+   = note: can only be called if the required target features are available
+
+error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
+  --> $DIR/safe-calls.rs:36:5
+   |
+LL |     Quux.avx_bmi2();
+   |     ^^^^^^^^^^^^^^^ call to function with `#[target_feature]`
+   |
+   = note: can only be called if the required target features are available
+
+error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
+  --> $DIR/safe-calls.rs:42:5
+   |
+LL |     sse2();
+   |     ^^^^^^ call to function with `#[target_feature]`
+   |
+   = note: can only be called if the required target features are available
+
+error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
+  --> $DIR/safe-calls.rs:45:18
+   |
+LL | const name: () = sse2();
+   |                  ^^^^^^ call to function with `#[target_feature]`
+   |
+   = note: can only be called if the required target features are available
+
+error: aborting due to 10 previous errors
+
+For more information about this error, try `rustc --explain E0133`.
diff --git a/src/test/ui/rfcs/rfc-2396-target_feature-11/trait-impl.rs b/src/test/ui/rfcs/rfc-2396-target_feature-11/trait-impl.rs
new file mode 100644
index 00000000000..7314fa8cced
--- /dev/null
+++ b/src/test/ui/rfcs/rfc-2396-target_feature-11/trait-impl.rs
@@ -0,0 +1,21 @@
+// only-x86_64
+
+#![feature(target_feature_11)]
+
+trait Foo {
+    fn foo(&self);
+    unsafe fn unsf_foo(&self);
+}
+
+struct Bar;
+
+impl Foo for Bar {
+    #[target_feature(enable = "sse2")]
+    //~^ ERROR cannot be applied to safe trait method
+    fn foo(&self) {}
+
+    #[target_feature(enable = "sse2")]
+    unsafe fn unsf_foo(&self) {}
+}
+
+fn main() {}
diff --git a/src/test/ui/rfcs/rfc-2396-target_feature-11/trait-impl.stderr b/src/test/ui/rfcs/rfc-2396-target_feature-11/trait-impl.stderr
new file mode 100644
index 00000000000..3c56e0fc5c6
--- /dev/null
+++ b/src/test/ui/rfcs/rfc-2396-target_feature-11/trait-impl.stderr
@@ -0,0 +1,11 @@
+error: `#[target_feature(..)]` cannot be applied to safe trait method
+  --> $DIR/trait-impl.rs:13:5
+   |
+LL |     #[target_feature(enable = "sse2")]
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot be applied to safe trait method
+LL |
+LL |     fn foo(&self) {}
+   |     ---------------- not an `unsafe` function
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/target-feature/invalid-attribute.rs b/src/test/ui/target-feature/invalid-attribute.rs
index 19c8c3dd488..63b1951a716 100644
--- a/src/test/ui/target-feature/invalid-attribute.rs
+++ b/src/test/ui/target-feature/invalid-attribute.rs
@@ -26,7 +26,7 @@ unsafe fn foo() {}
 
 #[target_feature(enable = "sse2")]
 //~^ ERROR `#[target_feature(..)]` can only be applied to `unsafe` functions
-//~| NOTE can only be applied to `unsafe` functions
+//~| NOTE see issue #69098
 fn bar() {}
 //~^ NOTE not an `unsafe` function
 
@@ -72,7 +72,7 @@ trait Quux {
 impl Quux for Foo {
     #[target_feature(enable = "sse2")]
     //~^ ERROR `#[target_feature(..)]` can only be applied to `unsafe` functions
-    //~| NOTE can only be applied to `unsafe` functions
+    //~| NOTE see issue #69098
     fn foo() {}
     //~^ NOTE not an `unsafe` function
 }
@@ -84,7 +84,7 @@ fn main() {
     }
     #[target_feature(enable = "sse2")]
     //~^ ERROR `#[target_feature(..)]` can only be applied to `unsafe` functions
-    //~| NOTE can only be applied to `unsafe` functions
+    //~| NOTE see issue #69098
     || {};
     //~^ NOTE not an `unsafe` function
 }
diff --git a/src/test/ui/target-feature/invalid-attribute.stderr b/src/test/ui/target-feature/invalid-attribute.stderr
index 76273d66ac2..21d6aa218ec 100644
--- a/src/test/ui/target-feature/invalid-attribute.stderr
+++ b/src/test/ui/target-feature/invalid-attribute.stderr
@@ -22,14 +22,17 @@ error: malformed `target_feature` attribute input
 LL | #[target_feature(disable = "baz")]
    |                  ^^^^^^^^^^^^^^^ help: must be of the form: `enable = ".."`
 
-error: `#[target_feature(..)]` can only be applied to `unsafe` functions
+error[E0658]: `#[target_feature(..)]` can only be applied to `unsafe` functions
   --> $DIR/invalid-attribute.rs:27:1
    |
 LL | #[target_feature(enable = "sse2")]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can only be applied to `unsafe` functions
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 ...
 LL | fn bar() {}
    | ----------- not an `unsafe` function
+   |
+   = note: see issue #69098 <https://github.com/rust-lang/rust/issues/69098> for more information
+   = help: add `#![feature(target_feature_11)]` to the crate attributes to enable
 
 error: attribute should be applied to a function
   --> $DIR/invalid-attribute.rs:33:1
@@ -91,23 +94,30 @@ error: cannot use `#[inline(always)]` with `#[target_feature]`
 LL | #[inline(always)]
    | ^^^^^^^^^^^^^^^^^
 
-error: `#[target_feature(..)]` can only be applied to `unsafe` functions
+error[E0658]: `#[target_feature(..)]` can only be applied to `unsafe` functions
   --> $DIR/invalid-attribute.rs:85:5
    |
 LL |     #[target_feature(enable = "sse2")]
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can only be applied to `unsafe` functions
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 ...
 LL |     || {};
    |     ----- not an `unsafe` function
+   |
+   = note: see issue #69098 <https://github.com/rust-lang/rust/issues/69098> for more information
+   = help: add `#![feature(target_feature_11)]` to the crate attributes to enable
 
-error: `#[target_feature(..)]` can only be applied to `unsafe` functions
+error[E0658]: `#[target_feature(..)]` can only be applied to `unsafe` functions
   --> $DIR/invalid-attribute.rs:73:5
    |
 LL |     #[target_feature(enable = "sse2")]
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can only be applied to `unsafe` functions
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 ...
 LL |     fn foo() {}
    |     ----------- not an `unsafe` function
+   |
+   = note: see issue #69098 <https://github.com/rust-lang/rust/issues/69098> for more information
+   = help: add `#![feature(target_feature_11)]` to the crate attributes to enable
 
 error: aborting due to 14 previous errors
 
+For more information about this error, try `rustc --explain E0658`.