about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_ast_lowering/src/delegation.rs17
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs28
-rw-r--r--compiler/rustc_codegen_ssa/src/codegen_attrs.rs12
-rw-r--r--compiler/rustc_hir/src/hir.rs36
-rw-r--r--compiler/rustc_hir_analysis/src/collect.rs17
-rw-r--r--compiler/rustc_hir_pretty/src/lib.rs12
-rw-r--r--compiler/rustc_hir_typeck/src/coercion.rs13
-rw-r--r--compiler/rustc_hir_typeck/src/lib.rs2
-rw-r--r--compiler/rustc_middle/src/middle/codegen_fn_attrs.rs3
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs1
-rw-r--r--compiler/rustc_middle/src/ty/print/pretty.rs9
-rw-r--r--compiler/rustc_mir_build/src/check_unsafety.rs34
-rw-r--r--compiler/rustc_resolve/src/late.rs7
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs68
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs6
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/region.rs8
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs12
-rw-r--r--src/librustdoc/clean/mod.rs2
-rw-r--r--src/librustdoc/clean/types.rs19
-rw-r--r--src/librustdoc/html/format.rs9
-rw-r--r--src/librustdoc/html/render/print_item.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/derive.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/doc/missing_headers.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/inherent_to_string.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/mod.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/new_without_default.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/ptr.rs2
-rw-r--r--tests/ui/async-await/async-closures/fn-exception-target-features.stderr5
-rw-r--r--tests/ui/macros/issue-68060.rs2
-rw-r--r--tests/ui/macros/issue-68060.stderr10
-rw-r--r--tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.stderr7
-rw-r--r--tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.rs6
-rw-r--r--tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr24
-rw-r--r--tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.rs1
-rw-r--r--tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.stderr19
-rw-r--r--tests/ui/target-feature/invalid-attribute.rs4
-rw-r--r--tests/ui/target-feature/invalid-attribute.stderr24
39 files changed, 319 insertions, 120 deletions
diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs
index 758f1dc1c35..266e77c0e02 100644
--- a/compiler/rustc_ast_lowering/src/delegation.rs
+++ b/compiler/rustc_ast_lowering/src/delegation.rs
@@ -188,7 +188,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
     ) -> hir::FnSig<'hir> {
         let header = if let Some(local_sig_id) = sig_id.as_local() {
             match self.resolver.delegation_fn_sigs.get(&local_sig_id) {
-                Some(sig) => self.lower_fn_header(sig.header, hir::Safety::Safe),
+                Some(sig) => self.lower_fn_header(
+                    sig.header,
+                    // HACK: we override the default safety instead of generating attributes from the ether.
+                    // We are not forwarding the attributes, as the delegation fn sigs are collected on the ast,
+                    // and here we need the hir attributes.
+                    if sig.target_feature { hir::Safety::Unsafe } else { hir::Safety::Safe },
+                    &[],
+                ),
                 None => self.generate_header_error(),
             }
         } else {
@@ -198,7 +205,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 Asyncness::No => hir::IsAsync::NotAsync,
             };
             hir::FnHeader {
-                safety: sig.safety,
+                safety: if self.tcx.codegen_fn_attrs(sig_id).safe_target_features {
+                    hir::HeaderSafety::SafeTargetFeatures
+                } else {
+                    hir::HeaderSafety::Normal(sig.safety)
+                },
                 constness: self.tcx.constness(sig_id),
                 asyncness,
                 abi: sig.abi,
@@ -384,7 +395,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
 
     fn generate_header_error(&self) -> hir::FnHeader {
         hir::FnHeader {
-            safety: hir::Safety::Safe,
+            safety: hir::Safety::Safe.into(),
             constness: hir::Constness::NotConst,
             asyncness: hir::IsAsync::NotAsync,
             abi: abi::Abi::Rust,
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index 6fce9116938..61d7da429f8 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -231,7 +231,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     });
                     let sig = hir::FnSig {
                         decl,
-                        header: this.lower_fn_header(*header, hir::Safety::Safe),
+                        header: this.lower_fn_header(*header, hir::Safety::Safe, attrs),
                         span: this.lower_span(*fn_sig_span),
                     };
                     hir::ItemKind::Fn { sig, generics, body: body_id, has_body: body.is_some() }
@@ -610,7 +610,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
     fn lower_foreign_item(&mut self, i: &ForeignItem) -> &'hir hir::ForeignItem<'hir> {
         let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id);
         let owner_id = hir_id.expect_owner();
-        self.lower_attrs(hir_id, &i.attrs);
+        let attrs = self.lower_attrs(hir_id, &i.attrs);
         let item = hir::ForeignItem {
             owner_id,
             ident: self.lower_ident(i.ident),
@@ -634,7 +634,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                         });
 
                     // Unmarked safety in unsafe block defaults to unsafe.
-                    let header = self.lower_fn_header(sig.header, hir::Safety::Unsafe);
+                    let header = self.lower_fn_header(sig.header, hir::Safety::Unsafe, attrs);
 
                     hir::ForeignItemKind::Fn(
                         hir::FnSig { header, decl, span: self.lower_span(sig.span) },
@@ -776,6 +776,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     i.id,
                     FnDeclKind::Trait,
                     sig.header.coroutine_kind,
+                    attrs,
                 );
                 (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names)), false)
             }
@@ -795,6 +796,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     i.id,
                     FnDeclKind::Trait,
                     sig.header.coroutine_kind,
+                    attrs,
                 );
                 (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id)), true)
             }
@@ -911,6 +913,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     i.id,
                     if self.is_in_trait_impl { FnDeclKind::Impl } else { FnDeclKind::Inherent },
                     sig.header.coroutine_kind,
+                    attrs,
                 );
 
                 (generics, hir::ImplItemKind::Fn(sig, body_id))
@@ -1339,8 +1342,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
         id: NodeId,
         kind: FnDeclKind,
         coroutine_kind: Option<CoroutineKind>,
+        attrs: &[hir::Attribute],
     ) -> (&'hir hir::Generics<'hir>, hir::FnSig<'hir>) {
-        let header = self.lower_fn_header(sig.header, hir::Safety::Safe);
+        let header = self.lower_fn_header(sig.header, hir::Safety::Safe, attrs);
         let itctx = ImplTraitContext::Universal;
         let (generics, decl) = self.lower_generics(generics, id, itctx, |this| {
             this.lower_fn_decl(&sig.decl, id, sig.span, kind, coroutine_kind)
@@ -1352,14 +1356,28 @@ impl<'hir> LoweringContext<'_, 'hir> {
         &mut self,
         h: FnHeader,
         default_safety: hir::Safety,
+        attrs: &[hir::Attribute],
     ) -> hir::FnHeader {
         let asyncness = if let Some(CoroutineKind::Async { span, .. }) = h.coroutine_kind {
             hir::IsAsync::Async(span)
         } else {
             hir::IsAsync::NotAsync
         };
+
+        let safety = self.lower_safety(h.safety, default_safety);
+
+        // Treat safe `#[target_feature]` functions as unsafe, but also remember that we did so.
+        let safety = if attrs.iter().any(|attr| attr.has_name(sym::target_feature))
+            && safety.is_safe()
+            && !self.tcx.sess.target.is_like_wasm
+        {
+            hir::HeaderSafety::SafeTargetFeatures
+        } else {
+            safety.into()
+        };
+
         hir::FnHeader {
-            safety: self.lower_safety(h.safety, default_safety),
+            safety,
             asyncness,
             constness: self.lower_constness(h.constness),
             abi: self.lower_extern(h.ext),
diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
index 37b53bb5bea..1daa17fbaf3 100644
--- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
+++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
@@ -250,10 +250,14 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
                 }
             }
             sym::target_feature => {
-                if !tcx.is_closure_like(did.to_def_id())
-                    && let Some(fn_sig) = fn_sig()
-                    && fn_sig.skip_binder().safety().is_safe()
-                {
+                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");
+                    continue;
+                };
+                let safe_target_features =
+                    matches!(sig.header.safety, hir::HeaderSafety::SafeTargetFeatures);
+                codegen_fn_attrs.safe_target_features = safe_target_features;
+                if safe_target_features {
                     if tcx.sess.target.is_like_wasm || tcx.sess.opts.actually_rustdoc {
                         // The `#[target_feature]` attribute is allowed on
                         // WebAssembly targets on all functions, including safe
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index dd96b30fefc..740c3ee8c07 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -3762,9 +3762,30 @@ impl fmt::Display for Constness {
     }
 }
 
+/// The actualy safety specified in syntax. We may treat
+/// its safety different within the type system to create a
+/// "sound by default" system that needs checking this enum
+/// explicitly to allow unsafe operations.
+#[derive(Copy, Clone, Debug, HashStable_Generic, PartialEq, Eq)]
+pub enum HeaderSafety {
+    /// A safe function annotated with `#[target_features]`.
+    /// The type system treats this function as an unsafe function,
+    /// but safety checking will check this enum to treat it as safe
+    /// and allowing calling other safe target feature functions with
+    /// the same features without requiring an additional unsafe block.
+    SafeTargetFeatures,
+    Normal(Safety),
+}
+
+impl From<Safety> for HeaderSafety {
+    fn from(v: Safety) -> Self {
+        Self::Normal(v)
+    }
+}
+
 #[derive(Copy, Clone, Debug, HashStable_Generic)]
 pub struct FnHeader {
-    pub safety: Safety,
+    pub safety: HeaderSafety,
     pub constness: Constness,
     pub asyncness: IsAsync,
     pub abi: ExternAbi,
@@ -3780,7 +3801,18 @@ impl FnHeader {
     }
 
     pub fn is_unsafe(&self) -> bool {
-        self.safety.is_unsafe()
+        self.safety().is_unsafe()
+    }
+
+    pub fn is_safe(&self) -> bool {
+        self.safety().is_safe()
+    }
+
+    pub fn safety(&self) -> Safety {
+        match self.safety {
+            HeaderSafety::SafeTargetFeatures => Safety::Unsafe,
+            HeaderSafety::Normal(safety) => safety,
+        }
     }
 }
 
diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs
index d41b03640b6..86c6532c97d 100644
--- a/compiler/rustc_hir_analysis/src/collect.rs
+++ b/compiler/rustc_hir_analysis/src/collect.rs
@@ -1336,7 +1336,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFn
             {
                 icx.lowerer().lower_fn_ty(
                     hir_id,
-                    sig.header.safety,
+                    sig.header.safety(),
                     sig.header.abi,
                     sig.decl,
                     Some(generics),
@@ -1351,13 +1351,18 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFn
             kind: TraitItemKind::Fn(FnSig { header, decl, span: _ }, _),
             generics,
             ..
-        }) => {
-            icx.lowerer().lower_fn_ty(hir_id, header.safety, header.abi, decl, Some(generics), None)
-        }
+        }) => icx.lowerer().lower_fn_ty(
+            hir_id,
+            header.safety(),
+            header.abi,
+            decl,
+            Some(generics),
+            None,
+        ),
 
         ForeignItem(&hir::ForeignItem { kind: ForeignItemKind::Fn(sig, _, _), .. }) => {
             let abi = tcx.hir().get_foreign_abi(hir_id);
-            compute_sig_of_foreign_fn_decl(tcx, def_id, sig.decl, abi, sig.header.safety)
+            compute_sig_of_foreign_fn_decl(tcx, def_id, sig.decl, abi, sig.header.safety())
         }
 
         Ctor(data) | Variant(hir::Variant { data, .. }) if data.ctor().is_some() => {
@@ -1405,7 +1410,7 @@ fn lower_fn_sig_recovering_infer_ret_ty<'tcx>(
 
     icx.lowerer().lower_fn_ty(
         icx.tcx().local_def_id_to_hir_id(def_id),
-        sig.header.safety,
+        sig.header.safety(),
         sig.header.abi,
         sig.decl,
         Some(generics),
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index 39b6823cf0e..3ff6acd79fc 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -2407,7 +2407,7 @@ impl<'a> State<'a> {
         self.print_fn(
             decl,
             hir::FnHeader {
-                safety,
+                safety: safety.into(),
                 abi,
                 constness: hir::Constness::NotConst,
                 asyncness: hir::IsAsync::NotAsync,
@@ -2423,12 +2423,20 @@ impl<'a> State<'a> {
     fn print_fn_header_info(&mut self, header: hir::FnHeader) {
         self.print_constness(header.constness);
 
+        let safety = match header.safety {
+            hir::HeaderSafety::SafeTargetFeatures => {
+                self.word_nbsp("#[target_feature]");
+                hir::Safety::Safe
+            }
+            hir::HeaderSafety::Normal(safety) => safety,
+        };
+
         match header.asyncness {
             hir::IsAsync::NotAsync => {}
             hir::IsAsync::Async(_) => self.word_nbsp("async"),
         }
 
-        self.print_safety(header.safety);
+        self.print_safety(safety);
 
         if header.abi != ExternAbi::Rust {
             self.word_nbsp("extern");
diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs
index 9ebc7a4657e..ec7c1efa38e 100644
--- a/compiler/rustc_hir_typeck/src/coercion.rs
+++ b/compiler/rustc_hir_typeck/src/coercion.rs
@@ -932,10 +932,17 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
                         return Err(TypeError::ForceInlineCast);
                     }
 
-                    // Safe `#[target_feature]` functions are not assignable to safe fn pointers
-                    // (RFC 2396).
+                    let fn_attrs = self.tcx.codegen_fn_attrs(def_id);
+                    if matches!(fn_attrs.inline, InlineAttr::Force { .. }) {
+                        return Err(TypeError::ForceInlineCast);
+                    }
+
+                    // FIXME(target_feature): Safe `#[target_feature]` functions could be cast to safe fn pointers (RFC 2396),
+                    // as you can already write that "cast" in user code by wrapping a target_feature fn call in a closure,
+                    // which is safe. This is sound because you already need to be executing code that is satisfying the target
+                    // feature constraints..
                     if b_hdr.safety.is_safe()
-                        && !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty()
+                        && self.tcx.codegen_fn_attrs(def_id).safe_target_features
                     {
                         return Err(TypeError::TargetFeatureCast(def_id));
                     }
diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs
index a406ec9a8fb..cb21961f36b 100644
--- a/compiler/rustc_hir_typeck/src/lib.rs
+++ b/compiler/rustc_hir_typeck/src/lib.rs
@@ -139,7 +139,7 @@ fn typeck_with_fallback<'tcx>(
             // type that has an infer in it, lower the type directly so that it'll
             // be correctly filled with infer. We'll use this inference to provide
             // a suggestion later on.
-            fcx.lowerer().lower_fn_ty(id, header.safety, header.abi, decl, None, None)
+            fcx.lowerer().lower_fn_ty(id, header.safety(), header.abi, decl, None, None)
         } else {
             tcx.fn_sig(def_id).instantiate_identity()
         };
diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
index 16d868300db..e05f42af6fd 100644
--- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
+++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
@@ -30,6 +30,8 @@ pub struct CodegenFnAttrs {
     /// features (only enabled features are supported right now).
     /// Implied target features have already been applied.
     pub target_features: Vec<TargetFeature>,
+    /// Whether the function was declared safe, but has target features
+    pub safe_target_features: bool,
     /// The `#[linkage = "..."]` attribute on Rust-defined items and the value we found.
     pub linkage: Option<Linkage>,
     /// The `#[linkage = "..."]` attribute on foreign items and the value we found.
@@ -150,6 +152,7 @@ impl CodegenFnAttrs {
             link_name: None,
             link_ordinal: None,
             target_features: vec![],
+            safe_target_features: false,
             linkage: None,
             import_linkage: None,
             link_section: None,
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index bf0ccdc0f10..d2875fb3794 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -222,6 +222,7 @@ pub struct DelegationFnSig {
     pub param_count: usize,
     pub has_self: bool,
     pub c_variadic: bool,
+    pub target_feature: bool,
 }
 
 #[derive(Clone, Copy, Debug)]
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index 9fe1caa4b58..ac900edefe1 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -690,7 +690,14 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
                 if with_reduced_queries() {
                     p!(print_def_path(def_id, args));
                 } else {
-                    let sig = self.tcx().fn_sig(def_id).instantiate(self.tcx(), args);
+                    let mut sig = self.tcx().fn_sig(def_id).instantiate(self.tcx(), args);
+                    if self.tcx().codegen_fn_attrs(def_id).safe_target_features {
+                        p!("#[target_features] ");
+                        sig = sig.map_bound(|mut sig| {
+                            sig.safety = hir::Safety::Safe;
+                            sig
+                        });
+                    }
                     p!(print(sig), " {{", print_value_path(def_id, args), "}}");
                 }
             }
diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs
index f7071eb139f..6279d0f94af 100644
--- a/compiler/rustc_mir_build/src/check_unsafety.rs
+++ b/compiler/rustc_mir_build/src/check_unsafety.rs
@@ -478,19 +478,27 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
                 return; // don't visit the whole expression
             }
             ExprKind::Call { fun, ty: _, args: _, from_hir_call: _, fn_span: _ } => {
-                if self.thir[fun].ty.fn_sig(self.tcx).safety().is_unsafe() {
-                    let func_id = if let ty::FnDef(func_id, _) = self.thir[fun].ty.kind() {
+                let fn_ty = self.thir[fun].ty;
+                let sig = fn_ty.fn_sig(self.tcx);
+                let (callee_features, safe_target_features): (&[_], _) = match fn_ty.kind() {
+                    ty::FnDef(func_id, ..) => {
+                        let cg_attrs = self.tcx.codegen_fn_attrs(func_id);
+                        (&cg_attrs.target_features, cg_attrs.safe_target_features)
+                    }
+                    _ => (&[], false),
+                };
+                if sig.safety().is_unsafe() && !safe_target_features {
+                    let func_id = if let ty::FnDef(func_id, _) = fn_ty.kind() {
                         Some(*func_id)
                     } else {
                         None
                     };
                     self.requires_unsafe(expr.span, CallToUnsafeFunction(func_id));
-                } else if let &ty::FnDef(func_did, _) = self.thir[fun].ty.kind() {
+                } else if let &ty::FnDef(func_did, _) = fn_ty.kind() {
                     // If the called function has target features the calling function hasn't,
                     // the call requires `unsafe`. Don't check this on wasm
                     // targets, though. For more information on wasm see the
                     // is_like_wasm check in hir_analysis/src/collect.rs
-                    let callee_features = &self.tcx.codegen_fn_attrs(func_did).target_features;
                     if !self.tcx.sess.target.options.is_like_wasm
                         && !callee_features.iter().all(|feature| {
                             self.body_target_features.iter().any(|f| f.name == feature.name)
@@ -739,7 +747,10 @@ impl UnsafeOpKind {
     ) {
         let parent_id = tcx.hir().get_parent_item(hir_id);
         let parent_owner = tcx.hir_owner_node(parent_id);
-        let should_suggest = parent_owner.fn_sig().is_some_and(|sig| sig.header.is_unsafe());
+        let should_suggest = parent_owner.fn_sig().is_some_and(|sig| {
+            // Do not suggest for safe target_feature functions
+            matches!(sig.header.safety, hir::HeaderSafety::Normal(hir::Safety::Unsafe))
+        });
         let unsafe_not_inherited_note = if should_suggest {
             suggest_unsafe_block.then(|| {
                 let body_span = tcx.hir().body(parent_owner.body_id().unwrap()).value.span;
@@ -902,7 +913,7 @@ impl UnsafeOpKind {
             {
                 true
             } else if let Some(sig) = tcx.hir().fn_sig_by_hir_id(*id)
-                && sig.header.is_unsafe()
+                && matches!(sig.header.safety, hir::HeaderSafety::Normal(hir::Safety::Unsafe))
             {
                 true
             } else {
@@ -1111,7 +1122,16 @@ pub(crate) fn check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) {
 
     let hir_id = tcx.local_def_id_to_hir_id(def);
     let safety_context = tcx.hir().fn_sig_by_hir_id(hir_id).map_or(SafetyContext::Safe, |fn_sig| {
-        if fn_sig.header.safety.is_unsafe() { SafetyContext::UnsafeFn } else { SafetyContext::Safe }
+        match fn_sig.header.safety {
+            // We typeck the body as safe, but otherwise treat it as unsafe everywhere else.
+            // Call sites to other SafeTargetFeatures functions are checked explicitly and don't need
+            // to care about safety of the body.
+            hir::HeaderSafety::SafeTargetFeatures => SafetyContext::Safe,
+            hir::HeaderSafety::Normal(safety) => match safety {
+                hir::Safety::Unsafe => SafetyContext::UnsafeFn,
+                hir::Safety::Safe => SafetyContext::Safe,
+            },
+        }
     });
     let body_target_features = &tcx.body_codegen_attrs(def.to_def_id()).target_features;
     let mut warnings = Vec::new();
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 7324d3fe786..c4aeaf478bd 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -5019,12 +5019,13 @@ struct ItemInfoCollector<'a, 'ra, 'tcx> {
 }
 
 impl ItemInfoCollector<'_, '_, '_> {
-    fn collect_fn_info(&mut self, sig: &FnSig, id: NodeId) {
+    fn collect_fn_info(&mut self, sig: &FnSig, id: NodeId, attrs: &[Attribute]) {
         let sig = DelegationFnSig {
             header: sig.header,
             param_count: sig.decl.inputs.len(),
             has_self: sig.decl.has_self(),
             c_variadic: sig.decl.c_variadic(),
+            target_feature: attrs.iter().any(|attr| attr.has_name(sym::target_feature)),
         };
         self.r.delegation_fn_sigs.insert(self.r.local_def_id(id), sig);
     }
@@ -5043,7 +5044,7 @@ impl<'ast> Visitor<'ast> for ItemInfoCollector<'_, '_, '_> {
             | ItemKind::Trait(box Trait { ref generics, .. })
             | ItemKind::TraitAlias(ref generics, _) => {
                 if let ItemKind::Fn(box Fn { ref sig, .. }) = &item.kind {
-                    self.collect_fn_info(sig, item.id);
+                    self.collect_fn_info(sig, item.id, &item.attrs);
                 }
 
                 let def_id = self.r.local_def_id(item.id);
@@ -5076,7 +5077,7 @@ impl<'ast> Visitor<'ast> for ItemInfoCollector<'_, '_, '_> {
 
     fn visit_assoc_item(&mut self, item: &'ast AssocItem, ctxt: AssocCtxt) {
         if let AssocItemKind::Fn(box Fn { ref sig, .. }) = &item.kind {
-            self.collect_fn_info(sig, item.id);
+            self.collect_fn_info(sig, item.id, &item.attrs);
         }
         visit::walk_assoc_item(self, item, ctxt);
     }
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
index 53300c95fa7..7032f7b9d31 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
@@ -824,9 +824,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     fn cmp_fn_sig(
         &self,
         sig1: &ty::PolyFnSig<'tcx>,
-        fn_def1: Option<(DefId, &'tcx [ty::GenericArg<'tcx>])>,
+        fn_def1: Option<(DefId, Option<&'tcx [ty::GenericArg<'tcx>]>)>,
         sig2: &ty::PolyFnSig<'tcx>,
-        fn_def2: Option<(DefId, &'tcx [ty::GenericArg<'tcx>])>,
+        fn_def2: Option<(DefId, Option<&'tcx [ty::GenericArg<'tcx>]>)>,
     ) -> (DiagStyledString, DiagStyledString) {
         let sig1 = &(self.normalize_fn_sig)(*sig1);
         let sig2 = &(self.normalize_fn_sig)(*sig2);
@@ -850,8 +850,20 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
 
         // unsafe extern "C" for<'a> fn(&'a T) -> &'a T
         // ^^^^^^
-        values.0.push(sig1.safety.prefix_str(), sig1.safety != sig2.safety);
-        values.1.push(sig2.safety.prefix_str(), sig1.safety != sig2.safety);
+        let safety = |fn_def, sig: ty::FnSig<'_>| match fn_def {
+            None => sig.safety.prefix_str(),
+            Some((did, _)) => {
+                if self.tcx.codegen_fn_attrs(did).safe_target_features {
+                    "#[target_features] "
+                } else {
+                    sig.safety.prefix_str()
+                }
+            }
+        };
+        let safety1 = safety(fn_def1, sig1);
+        let safety2 = safety(fn_def2, sig2);
+        values.0.push(safety1, safety1 != safety2);
+        values.1.push(safety2, safety1 != safety2);
 
         // unsafe extern "C" for<'a> fn(&'a T) -> &'a T
         //        ^^^^^^^^^^
@@ -932,23 +944,23 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             (values.1).0.extend(x2.0);
         }
 
-        let fmt = |(did, args)| format!(" {{{}}}", self.tcx.def_path_str_with_args(did, args));
+        let fmt = |did, args| format!(" {{{}}}", self.tcx.def_path_str_with_args(did, args));
 
         match (fn_def1, fn_def2) {
-            (None, None) => {}
-            (Some(fn_def1), Some(fn_def2)) => {
-                let path1 = fmt(fn_def1);
-                let path2 = fmt(fn_def2);
+            (Some((fn_def1, Some(fn_args1))), Some((fn_def2, Some(fn_args2)))) => {
+                let path1 = fmt(fn_def1, fn_args1);
+                let path2 = fmt(fn_def2, fn_args2);
                 let same_path = path1 == path2;
                 values.0.push(path1, !same_path);
                 values.1.push(path2, !same_path);
             }
-            (Some(fn_def1), None) => {
-                values.0.push_highlighted(fmt(fn_def1));
+            (Some((fn_def1, Some(fn_args1))), None) => {
+                values.0.push_highlighted(fmt(fn_def1, fn_args1));
             }
-            (None, Some(fn_def2)) => {
-                values.1.push_highlighted(fmt(fn_def2));
+            (None, Some((fn_def2, Some(fn_args2)))) => {
+                values.1.push_highlighted(fmt(fn_def2, fn_args2));
             }
+            _ => {}
         }
 
         values
@@ -1339,17 +1351,22 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             (ty::FnDef(did1, args1), ty::FnDef(did2, args2)) => {
                 let sig1 = self.tcx.fn_sig(*did1).instantiate(self.tcx, args1);
                 let sig2 = self.tcx.fn_sig(*did2).instantiate(self.tcx, args2);
-                self.cmp_fn_sig(&sig1, Some((*did1, args1)), &sig2, Some((*did2, args2)))
+                self.cmp_fn_sig(
+                    &sig1,
+                    Some((*did1, Some(args1))),
+                    &sig2,
+                    Some((*did2, Some(args2))),
+                )
             }
 
             (ty::FnDef(did1, args1), ty::FnPtr(sig_tys2, hdr2)) => {
                 let sig1 = self.tcx.fn_sig(*did1).instantiate(self.tcx, args1);
-                self.cmp_fn_sig(&sig1, Some((*did1, args1)), &sig_tys2.with(*hdr2), None)
+                self.cmp_fn_sig(&sig1, Some((*did1, Some(args1))), &sig_tys2.with(*hdr2), None)
             }
 
             (ty::FnPtr(sig_tys1, hdr1), ty::FnDef(did2, args2)) => {
                 let sig2 = self.tcx.fn_sig(*did2).instantiate(self.tcx, args2);
-                self.cmp_fn_sig(&sig_tys1.with(*hdr1), None, &sig2, Some((*did2, args2)))
+                self.cmp_fn_sig(&sig_tys1.with(*hdr1), None, &sig2, Some((*did2, Some(args2))))
             }
 
             (ty::FnPtr(sig_tys1, hdr1), ty::FnPtr(sig_tys2, hdr2)) => {
@@ -1531,7 +1548,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                         (false, Mismatch::Fixed("existential projection"))
                     }
                 };
-                let Some(vals) = self.values_str(values) else {
+                let Some(vals) = self.values_str(values, cause) else {
                     // Derived error. Cancel the emitter.
                     // NOTE(eddyb) this was `.cancel()`, but `diag`
                     // is borrowed, so we can't fully defuse it.
@@ -1956,7 +1973,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         })
         | ObligationCauseCode::BlockTailExpression(.., source)) = code
             && let hir::MatchSource::TryDesugar(_) = source
-            && let Some((expected_ty, found_ty, _)) = self.values_str(trace.values)
+            && let Some((expected_ty, found_ty, _)) = self.values_str(trace.values, &trace.cause)
         {
             suggestions.push(TypeErrorAdditionalDiags::TryCannotConvert {
                 found: found_ty.content(),
@@ -2085,6 +2102,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     fn values_str(
         &self,
         values: ValuePairs<'tcx>,
+        cause: &ObligationCause<'tcx>,
     ) -> Option<(DiagStyledString, DiagStyledString, Option<PathBuf>)> {
         match values {
             ValuePairs::Regions(exp_found) => self.expected_found_str(exp_found),
@@ -2109,7 +2127,19 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 if exp_found.references_error() {
                     return None;
                 }
-                let (exp, fnd) = self.cmp_fn_sig(&exp_found.expected, None, &exp_found.found, None);
+                let (fn_def1, fn_def2) = if let ObligationCauseCode::CompareImplItem {
+                    impl_item_def_id,
+                    trait_item_def_id,
+                    ..
+                } = *cause.code()
+                {
+                    (Some((trait_item_def_id, None)), Some((impl_item_def_id.to_def_id(), None)))
+                } else {
+                    (None, None)
+                };
+
+                let (exp, fnd) =
+                    self.cmp_fn_sig(&exp_found.expected, fn_def1, &exp_found.found, fn_def2);
                 Some((exp, fnd, None))
             }
         }
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs
index 2cfccc57c97..1dd09fe7aaf 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs
@@ -461,9 +461,11 @@ impl<T> Trait<T> for X {
                     (ty::FnPtr(_, hdr), ty::FnDef(def_id, _))
                     | (ty::FnDef(def_id, _), ty::FnPtr(_, hdr)) => {
                         if tcx.fn_sig(def_id).skip_binder().safety() < hdr.safety {
-                            diag.note(
+                            if !tcx.codegen_fn_attrs(def_id).safe_target_features {
+                                diag.note(
                                 "unsafe functions cannot be coerced into safe function pointers",
-                            );
+                                );
+                            }
                         }
                     }
                     (ty::Adt(_, _), ty::Adt(def, args))
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs
index 98b5fb2052f..3acca47025c 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs
@@ -221,7 +221,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             infer::Subtype(ref trace) => RegionOriginNote::WithRequirement {
                 span: trace.cause.span,
                 requirement: ObligationCauseAsDiagArg(trace.cause.clone()),
-                expected_found: self.values_str(trace.values).map(|(e, f, _)| (e, f)),
+                expected_found: self.values_str(trace.values, &trace.cause).map(|(e, f, _)| (e, f)),
             }
             .add_to_diag(err),
             infer::Reborrow(span) => {
@@ -946,8 +946,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
 
         if let infer::Subtype(ref sup_trace) = sup_origin
             && let infer::Subtype(ref sub_trace) = sub_origin
-            && let Some((sup_expected, sup_found, _)) = self.values_str(sup_trace.values)
-            && let Some((sub_expected, sub_found, _)) = self.values_str(sub_trace.values)
+            && let Some((sup_expected, sup_found, _)) =
+                self.values_str(sup_trace.values, &sup_trace.cause)
+            && let Some((sub_expected, sub_found, _)) =
+                self.values_str(sub_trace.values, &sup_trace.cause)
             && sub_expected == sup_expected
             && sub_found == sup_found
         {
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs
index 51efe39a7bc..2d248d00066 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs
@@ -205,9 +205,15 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
 
             if self_ty.is_fn() {
                 let fn_sig = self_ty.fn_sig(self.tcx);
-                let shortname = match fn_sig.safety() {
-                    hir::Safety::Safe => "fn",
-                    hir::Safety::Unsafe => "unsafe fn",
+                let shortname = if let ty::FnDef(def_id, _) = self_ty.kind()
+                    && self.tcx.codegen_fn_attrs(def_id).safe_target_features
+                {
+                    "#[target_feature] fn"
+                } else {
+                    match fn_sig.safety() {
+                        hir::Safety::Safe => "fn",
+                        hir::Safety::Unsafe => "unsafe fn",
+                    }
                 };
                 flags.push((sym::_Self, Some(shortname.to_owned())));
             }
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 2ed2df799dd..f619b2c66ac 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -3094,7 +3094,7 @@ fn clean_maybe_renamed_foreign_item<'tcx>(
         let kind = match item.kind {
             hir::ForeignItemKind::Fn(sig, names, generics) => ForeignFunctionItem(
                 clean_function(cx, &sig, generics, FunctionArgs::Names(names)),
-                sig.header.safety,
+                sig.header.safety(),
             ),
             hir::ForeignItemKind::Static(ty, mutability, safety) => ForeignStaticItem(
                 Static { type_: Box::new(clean_ty(ty, cx)), mutability, expr: None },
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index dcee96978d2..ff1c0294fb8 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -668,17 +668,28 @@ impl Item {
                 ty::Asyncness::Yes => hir::IsAsync::Async(DUMMY_SP),
                 ty::Asyncness::No => hir::IsAsync::NotAsync,
             };
-            hir::FnHeader { safety: sig.safety(), abi: sig.abi(), constness, asyncness }
+            hir::FnHeader {
+                safety: if tcx.codegen_fn_attrs(def_id).safe_target_features {
+                    hir::HeaderSafety::SafeTargetFeatures
+                } else {
+                    sig.safety().into()
+                },
+                abi: sig.abi(),
+                constness,
+                asyncness,
+            }
         }
         let header = match self.kind {
             ItemKind::ForeignFunctionItem(_, safety) => {
                 let def_id = self.def_id().unwrap();
                 let abi = tcx.fn_sig(def_id).skip_binder().abi();
                 hir::FnHeader {
-                    safety: if abi == ExternAbi::RustIntrinsic {
-                        intrinsic_operation_unsafety(tcx, def_id.expect_local())
+                    safety: if tcx.codegen_fn_attrs(def_id).safe_target_features {
+                        hir::HeaderSafety::SafeTargetFeatures
+                    } else if abi == ExternAbi::RustIntrinsic {
+                        intrinsic_operation_unsafety(tcx, def_id.expect_local()).into()
                     } else {
-                        safety
+                        safety.into()
                     },
                     abi,
                     constness: if tcx.is_const_fn(def_id) {
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 621abd53501..92935c72b47 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -1637,6 +1637,15 @@ impl PrintWithSpace for hir::Safety {
     }
 }
 
+impl PrintWithSpace for hir::HeaderSafety {
+    fn print_with_space(&self) -> &str {
+        match self {
+            hir::HeaderSafety::SafeTargetFeatures => "",
+            hir::HeaderSafety::Normal(safety) => safety.print_with_space(),
+        }
+    }
+}
+
 impl PrintWithSpace for hir::IsAsync {
     fn print_with_space(&self) -> &str {
         match self {
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index e8230e63c0f..1376bdb2e90 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -469,7 +469,7 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl
 
                 let unsafety_flag = match myitem.kind {
                     clean::FunctionItem(_) | clean::ForeignFunctionItem(..)
-                        if myitem.fn_header(tcx).unwrap().safety.is_unsafe() =>
+                        if myitem.fn_header(tcx).unwrap().is_unsafe() =>
                     {
                         "<sup title=\"unsafe function\">⚠</sup>"
                     }
diff --git a/src/tools/clippy/clippy_lints/src/derive.rs b/src/tools/clippy/clippy_lints/src/derive.rs
index 7c2f5efd8dd..91ddbb44ff8 100644
--- a/src/tools/clippy/clippy_lints/src/derive.rs
+++ b/src/tools/clippy/clippy_lints/src/derive.rs
@@ -419,7 +419,7 @@ impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> {
         id: LocalDefId,
     ) -> Self::Result {
         if let Some(header) = kind.header()
-            && header.safety.is_unsafe()
+            && header.is_unsafe()
         {
             ControlFlow::Break(())
         } else {
diff --git a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs
index 3e2b7055de4..8e2af6bf14a 100644
--- a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs
+++ b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs
@@ -32,7 +32,7 @@ pub fn check(
     }
 
     let span = cx.tcx.def_span(owner_id);
-    match (headers.safety, sig.header.safety) {
+    match (headers.safety, sig.header.safety()) {
         (false, Safety::Unsafe) => span_lint(
             cx,
             MISSING_SAFETY_DOC,
diff --git a/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs b/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs
index 017571c38db..854fe144c29 100644
--- a/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs
@@ -34,7 +34,7 @@ pub fn check_fn(cx: &LateContext<'_>, kind: FnKind<'_>, decl: &FnDecl<'_>, body:
         ImplicitSelfKind::None => return,
     };
 
-    let name = if sig.header.safety.is_unsafe() {
+    let name = if sig.header.is_unsafe() {
         name.strip_suffix("_unchecked").unwrap_or(name)
     } else {
         name
diff --git a/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs b/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs
index 3ded8dc3012..8a74951ef63 100644
--- a/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs
@@ -20,8 +20,8 @@ pub(super) fn check_fn<'tcx>(
     def_id: LocalDefId,
 ) {
     let safety = match kind {
-        intravisit::FnKind::ItemFn(_, _, hir::FnHeader { safety, .. }) => safety,
-        intravisit::FnKind::Method(_, sig) => sig.header.safety,
+        intravisit::FnKind::ItemFn(_, _, header) => header.safety(),
+        intravisit::FnKind::Method(_, sig) => sig.header.safety(),
         intravisit::FnKind::Closure => return,
     };
 
@@ -31,7 +31,7 @@ pub(super) fn check_fn<'tcx>(
 pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
     if let hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(eid)) = item.kind {
         let body = cx.tcx.hir().body(eid);
-        check_raw_ptr(cx, sig.header.safety, sig.decl, body, item.owner_id.def_id);
+        check_raw_ptr(cx, sig.header.safety(), sig.decl, body, item.owner_id.def_id);
     }
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/inherent_to_string.rs b/src/tools/clippy/clippy_lints/src/inherent_to_string.rs
index e096dd25175..415b47adac5 100644
--- a/src/tools/clippy/clippy_lints/src/inherent_to_string.rs
+++ b/src/tools/clippy/clippy_lints/src/inherent_to_string.rs
@@ -95,7 +95,7 @@ impl<'tcx> LateLintPass<'tcx> for InherentToString {
         if let ImplItemKind::Fn(ref signature, _) = impl_item.kind
             // #11201
             && let header = signature.header
-            && header.safety.is_safe()
+            && header.is_safe()
             && header.abi == Abi::Rust
             && impl_item.ident.name == sym::to_string
             && let decl = signature.decl
diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs
index 51351f6b7cd..3965c4d4087 100644
--- a/src/tools/clippy/clippy_lints/src/methods/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs
@@ -5309,7 +5309,7 @@ fn lint_binary_expr_with_method_call(cx: &LateContext<'_>, info: &mut BinaryExpr
 }
 
 const FN_HEADER: hir::FnHeader = hir::FnHeader {
-    safety: hir::Safety::Safe,
+    safety: hir::HeaderSafety::Normal(hir::Safety::Safe),
     constness: hir::Constness::NotConst,
     asyncness: hir::IsAsync::NotAsync,
     abi: rustc_target::spec::abi::Abi::Rust,
diff --git a/src/tools/clippy/clippy_lints/src/new_without_default.rs b/src/tools/clippy/clippy_lints/src/new_without_default.rs
index abdce69e764..688374b5676 100644
--- a/src/tools/clippy/clippy_lints/src/new_without_default.rs
+++ b/src/tools/clippy/clippy_lints/src/new_without_default.rs
@@ -75,7 +75,7 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault {
                     if let hir::ImplItemKind::Fn(ref sig, _) = impl_item.kind {
                         let name = impl_item.ident.name;
                         let id = impl_item.owner_id;
-                        if sig.header.safety.is_unsafe() {
+                        if sig.header.is_unsafe() {
                             // can't be implemented for unsafe new
                             return;
                         }
diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs
index a86926d8416..506adf0f2cc 100644
--- a/src/tools/clippy/clippy_lints/src/ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/ptr.rs
@@ -541,7 +541,7 @@ fn check_mut_from_ref<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Optio
             .collect();
         if let Some(args) = args
             && !args.is_empty()
-            && body.is_none_or(|body| sig.header.safety.is_unsafe() || contains_unsafe_block(cx, body.value))
+            && body.is_none_or(|body| sig.header.is_unsafe() || contains_unsafe_block(cx, body.value))
         {
             span_lint_and_then(
                 cx,
diff --git a/tests/ui/async-await/async-closures/fn-exception-target-features.stderr b/tests/ui/async-await/async-closures/fn-exception-target-features.stderr
index db5895108bb..37977b45250 100644
--- a/tests/ui/async-await/async-closures/fn-exception-target-features.stderr
+++ b/tests/ui/async-await/async-closures/fn-exception-target-features.stderr
@@ -1,11 +1,12 @@
-error[E0277]: the trait bound `fn() -> Pin<Box<(dyn Future<Output = ()> + 'static)>> {target_feature}: AsyncFn()` is not satisfied
+error[E0277]: the trait bound `#[target_features] fn() -> Pin<Box<(dyn Future<Output = ()> + 'static)>> {target_feature}: AsyncFn()` is not satisfied
   --> $DIR/fn-exception-target-features.rs:16:10
    |
 LL |     test(target_feature);
-   |     ---- ^^^^^^^^^^^^^^ the trait `AsyncFn()` is not implemented for fn item `fn() -> Pin<Box<(dyn Future<Output = ()> + 'static)>> {target_feature}`
+   |     ---- ^^^^^^^^^^^^^^ unsatisfied trait bound
    |     |
    |     required by a bound introduced by this call
    |
+   = help: the trait `AsyncFn()` is not implemented for fn item `#[target_features] fn() -> Pin<Box<(dyn Future<Output = ()> + 'static)>> {target_feature}`
 note: required by a bound in `test`
   --> $DIR/fn-exception-target-features.rs:13:17
    |
diff --git a/tests/ui/macros/issue-68060.rs b/tests/ui/macros/issue-68060.rs
index 1a826bd60e0..4eddb96848c 100644
--- a/tests/ui/macros/issue-68060.rs
+++ b/tests/ui/macros/issue-68060.rs
@@ -3,8 +3,6 @@ fn main() {
         .map(
             #[target_feature(enable = "")]
             //~^ ERROR: attribute should be applied to a function
-            //~| ERROR: feature named `` is not valid
-            //~| NOTE: `` is not valid for this target
             #[track_caller]
             //~^ ERROR: `#[track_caller]` on closures is currently unstable
             //~| NOTE: see issue #87417
diff --git a/tests/ui/macros/issue-68060.stderr b/tests/ui/macros/issue-68060.stderr
index 5724a9ea438..ef2246d5bd6 100644
--- a/tests/ui/macros/issue-68060.stderr
+++ b/tests/ui/macros/issue-68060.stderr
@@ -7,14 +7,8 @@ LL |             #[target_feature(enable = "")]
 LL |             |_| (),
    |             ------ not a function definition
 
-error: the feature named `` is not valid for this target
-  --> $DIR/issue-68060.rs:4:30
-   |
-LL |             #[target_feature(enable = "")]
-   |                              ^^^^^^^^^^^ `` is not valid for this target
-
 error[E0658]: `#[track_caller]` on closures is currently unstable
-  --> $DIR/issue-68060.rs:8:13
+  --> $DIR/issue-68060.rs:6:13
    |
 LL |             #[track_caller]
    |             ^^^^^^^^^^^^^^^
@@ -23,6 +17,6 @@ LL |             #[track_caller]
    = help: add `#![feature(closure_track_caller)]` 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: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.stderr
index cc941be5479..a2bda229d10 100644
--- a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.stderr
+++ b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.stderr
@@ -10,13 +10,8 @@ LL |     let foo: fn() = foo;
    |              expected due to this
    |
    = note: expected fn pointer `fn()`
-                 found fn item `fn() {foo}`
-   = note: fn items are distinct from fn pointers
+                 found fn item `#[target_features] fn() {foo}`
    = note: functions with `#[target_feature]` can only be coerced to `unsafe` function pointers
-help: consider casting to a fn pointer
-   |
-LL |     let foo: fn() = foo as fn();
-   |                     ~~~~~~~~~~~
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.rs b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.rs
index 3c370a1b8f3..3eae79faf42 100644
--- a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.rs
+++ b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.rs
@@ -21,9 +21,9 @@ fn call_once(f: impl FnOnce()) {
 }
 
 fn main() {
-    call(foo); //~ ERROR expected a `Fn()` closure, found `fn() {foo}`
-    call_mut(foo); //~ ERROR expected a `FnMut()` closure, found `fn() {foo}`
-    call_once(foo); //~ ERROR expected a `FnOnce()` closure, found `fn() {foo}`
+    call(foo); //~ ERROR expected a `Fn()` closure, found `#[target_features] fn() {foo}`
+    call_mut(foo); //~ ERROR expected a `FnMut()` closure, found `#[target_features] fn() {foo}`
+    call_once(foo); //~ ERROR expected a `FnOnce()` closure, found `#[target_features] fn() {foo}`
 
     call(foo_unsafe);
     //~^ ERROR expected a `Fn()` closure, found `unsafe fn() {foo_unsafe}`
diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr
index 4c07f4d6b99..2915b9ad1b3 100644
--- a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr
+++ b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr
@@ -1,13 +1,13 @@
-error[E0277]: expected a `Fn()` closure, found `fn() {foo}`
+error[E0277]: expected a `Fn()` closure, found `#[target_features] fn() {foo}`
   --> $DIR/fn-traits.rs:24:10
    |
 LL |     call(foo);
-   |     ---- ^^^ expected an `Fn()` closure, found `fn() {foo}`
+   |     ---- ^^^ expected an `Fn()` closure, found `#[target_features] fn() {foo}`
    |     |
    |     required by a bound introduced by this call
    |
-   = help: the trait `Fn()` is not implemented for fn item `fn() {foo}`
-   = note: wrap the `fn() {foo}` in a closure with no arguments: `|| { /* code */ }`
+   = help: the trait `Fn()` is not implemented for fn item `#[target_features] fn() {foo}`
+   = note: wrap the `#[target_features] fn() {foo}` in a closure with no arguments: `|| { /* code */ }`
    = note: `#[target_feature]` functions do not implement the `Fn` traits
 note: required by a bound in `call`
   --> $DIR/fn-traits.rs:11:17
@@ -15,16 +15,16 @@ note: required by a bound in `call`
 LL | fn call(f: impl Fn()) {
    |                 ^^^^ required by this bound in `call`
 
-error[E0277]: expected a `FnMut()` closure, found `fn() {foo}`
+error[E0277]: expected a `FnMut()` closure, found `#[target_features] fn() {foo}`
   --> $DIR/fn-traits.rs:25:14
    |
 LL |     call_mut(foo);
-   |     -------- ^^^ expected an `FnMut()` closure, found `fn() {foo}`
+   |     -------- ^^^ expected an `FnMut()` closure, found `#[target_features] fn() {foo}`
    |     |
    |     required by a bound introduced by this call
    |
-   = help: the trait `FnMut()` is not implemented for fn item `fn() {foo}`
-   = note: wrap the `fn() {foo}` in a closure with no arguments: `|| { /* code */ }`
+   = help: the trait `FnMut()` is not implemented for fn item `#[target_features] fn() {foo}`
+   = note: wrap the `#[target_features] fn() {foo}` in a closure with no arguments: `|| { /* code */ }`
    = note: `#[target_feature]` functions do not implement the `Fn` traits
 note: required by a bound in `call_mut`
   --> $DIR/fn-traits.rs:15:25
@@ -32,16 +32,16 @@ note: required by a bound in `call_mut`
 LL | fn call_mut(mut f: impl FnMut()) {
    |                         ^^^^^^^ required by this bound in `call_mut`
 
-error[E0277]: expected a `FnOnce()` closure, found `fn() {foo}`
+error[E0277]: expected a `FnOnce()` closure, found `#[target_features] fn() {foo}`
   --> $DIR/fn-traits.rs:26:15
    |
 LL |     call_once(foo);
-   |     --------- ^^^ expected an `FnOnce()` closure, found `fn() {foo}`
+   |     --------- ^^^ expected an `FnOnce()` closure, found `#[target_features] fn() {foo}`
    |     |
    |     required by a bound introduced by this call
    |
-   = help: the trait `FnOnce()` is not implemented for fn item `fn() {foo}`
-   = note: wrap the `fn() {foo}` in a closure with no arguments: `|| { /* code */ }`
+   = help: the trait `FnOnce()` is not implemented for fn item `#[target_features] fn() {foo}`
+   = note: wrap the `#[target_features] fn() {foo}` in a closure with no arguments: `|| { /* code */ }`
    = note: `#[target_feature]` functions do not implement the `Fn` traits
 note: required by a bound in `call_once`
   --> $DIR/fn-traits.rs:19:22
diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.rs b/tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.rs
index df575b0f6b6..a2ac6ff45fc 100644
--- a/tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.rs
+++ b/tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.rs
@@ -13,6 +13,7 @@ impl Foo for Bar {
     #[target_feature(enable = "sse2")]
     //~^ ERROR cannot be applied to safe trait method
     fn foo(&self) {}
+    //~^ ERROR method `foo` has an incompatible type for trait
 
     #[target_feature(enable = "sse2")]
     unsafe fn unsf_foo(&self) {}
diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.stderr
index 00efbb52f15..1ab1fad64cc 100644
--- a/tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.stderr
+++ b/tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.stderr
@@ -7,8 +7,22 @@ LL |
 LL |     fn foo(&self) {}
    |     ------------- not an `unsafe` function
 
+error[E0053]: method `foo` has an incompatible type for trait
+  --> $DIR/trait-impl.rs:15:5
+   |
+LL |     fn foo(&self) {}
+   |     ^^^^^^^^^^^^^ expected safe fn, found unsafe fn
+   |
+note: type in trait
+  --> $DIR/trait-impl.rs:6:5
+   |
+LL |     fn foo(&self);
+   |     ^^^^^^^^^^^^^^
+   = note: expected signature `fn(&Bar)`
+              found signature `#[target_features] fn(&Bar)`
+
 error: `#[target_feature(..)]` cannot be applied to safe trait method
-  --> $DIR/trait-impl.rs:22:5
+  --> $DIR/trait-impl.rs:23:5
    |
 LL |     #[target_feature(enable = "sse2")]
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot be applied to safe trait method
@@ -16,5 +30,6 @@ LL |
 LL |     fn foo(&self) {}
    |     ------------- not an `unsafe` function
 
-error: aborting due to 2 previous errors
+error: aborting due to 3 previous errors
 
+For more information about this error, try `rustc --explain E0053`.
diff --git a/tests/ui/target-feature/invalid-attribute.rs b/tests/ui/target-feature/invalid-attribute.rs
index 2f951c4a00a..c0f5b6b2fb2 100644
--- a/tests/ui/target-feature/invalid-attribute.rs
+++ b/tests/ui/target-feature/invalid-attribute.rs
@@ -97,6 +97,7 @@ impl Foo {}
 
 trait Quux {
     fn foo(); //~ NOTE `foo` from trait
+    //~^ NOTE: type in trait
 }
 
 impl Quux for Foo {
@@ -106,6 +107,9 @@ impl Quux for Foo {
     //~| NOTE: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
     fn foo() {}
     //~^ NOTE not an `unsafe` function
+    //~| ERROR: incompatible type for trait
+    //~| NOTE: expected safe fn, found unsafe fn
+    //~| NOTE: expected signature `fn()`
 }
 
 fn main() {
diff --git a/tests/ui/target-feature/invalid-attribute.stderr b/tests/ui/target-feature/invalid-attribute.stderr
index bf48911edec..10fcf65bb9a 100644
--- a/tests/ui/target-feature/invalid-attribute.stderr
+++ b/tests/ui/target-feature/invalid-attribute.stderr
@@ -126,7 +126,7 @@ LL | impl Foo {}
    | ----------- not a function definition
 
 error: attribute should be applied to a function definition
-  --> $DIR/invalid-attribute.rs:112:5
+  --> $DIR/invalid-attribute.rs:116:5
    |
 LL |       #[target_feature(enable = "sse2")]
    |       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -138,7 +138,7 @@ LL | |     }
    | |_____- not a function definition
 
 error: attribute should be applied to a function definition
-  --> $DIR/invalid-attribute.rs:120:5
+  --> $DIR/invalid-attribute.rs:124:5
    |
 LL |     #[target_feature(enable = "sse2")]
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -193,7 +193,7 @@ LL |     fn foo();
    |     --------- `foo` from trait
 
 error[E0658]: `#[target_feature(..)]` can only be applied to `unsafe` functions
-  --> $DIR/invalid-attribute.rs:103:5
+  --> $DIR/invalid-attribute.rs:104:5
    |
 LL |     #[target_feature(enable = "sse2")]
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -205,7 +205,21 @@ LL |     fn foo() {}
    = help: add `#![feature(target_feature_11)]` 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: aborting due to 23 previous errors
+error[E0053]: method `foo` has an incompatible type for trait
+  --> $DIR/invalid-attribute.rs:108:5
+   |
+LL |     fn foo() {}
+   |     ^^^^^^^^ expected safe fn, found unsafe fn
+   |
+note: type in trait
+  --> $DIR/invalid-attribute.rs:99:5
+   |
+LL |     fn foo();
+   |     ^^^^^^^^^
+   = note: expected signature `fn()`
+              found signature `#[target_features] fn()`
+
+error: aborting due to 24 previous errors
 
-Some errors have detailed explanations: E0046, E0658.
+Some errors have detailed explanations: E0046, E0053, E0658.
 For more information about an error, try `rustc --explain E0046`.