about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-07-19 19:36:57 +0000
committerbors <bors@rust-lang.org>2022-07-19 19:36:57 +0000
commit9a7b7d5e50ab0b59c6d349bbf005680a7c880e98 (patch)
treeff10292ed61a9f79a1ecf3e5394d9defc26dd95a
parent29c5a028b0c92aa5da6a8eb6d6585a389fcf1035 (diff)
parentddb5a2638acbbe33815aefdbeff3266df4ebcd21 (diff)
downloadrust-9a7b7d5e50ab0b59c6d349bbf005680a7c880e98.tar.gz
rust-9a7b7d5e50ab0b59c6d349bbf005680a7c880e98.zip
Auto merge of #98180 - notriddle:notriddle/rustdoc-fn, r=petrochenkov,GuillaumeGomez
Improve the function pointer docs

This is #97842 but for function pointers instead of tuples. The concept is basically the same.

* Reduce duplicate impls; show `fn (T₁, T₂, …, Tₙ)` and include a sentence saying that there exists up to twelve of them.
* Show `Copy` and `Clone`.
* Show auto traits like `Send` and `Sync`, and blanket impls like `Any`.

https://notriddle.com/notriddle-rustdoc-test/std/primitive.fn.html
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs4
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0118.md10
-rw-r--r--compiler/rustc_error_messages/locales/en-US/passes.ftl4
-rw-r--r--compiler/rustc_passes/src/check_attr.rs20
-rw-r--r--compiler/rustc_passes/src/errors.rs4
-rw-r--r--compiler/rustc_span/src/symbol.rs2
-rw-r--r--compiler/rustc_typeck/src/coherence/inherent_impls.rs3
-rw-r--r--library/core/src/fmt/mod.rs2
-rw-r--r--library/core/src/hash/mod.rs2
-rw-r--r--library/core/src/primitive_docs.rs64
-rw-r--r--library/core/src/ptr/mod.rs102
-rw-r--r--library/core/src/tuple.rs2
-rw-r--r--library/std/src/primitive_docs.rs64
-rw-r--r--src/librustdoc/clean/inline.rs4
-rw-r--r--src/librustdoc/clean/mod.rs4
-rw-r--r--src/librustdoc/clean/types.rs8
-rw-r--r--src/librustdoc/html/format.rs35
-rw-r--r--src/librustdoc/json/conversions.rs2
-rw-r--r--src/test/rustdoc-ui/tuple-variadic-check.rs4
-rw-r--r--src/test/rustdoc-ui/tuple-variadic-check.stderr6
-rw-r--r--src/test/rustdoc/primitive-tuple-auto-trait.rs2
-rw-r--r--src/test/rustdoc/primitive-tuple-variadic.rs4
-rw-r--r--src/test/ui/error-codes/E0118.rs2
-rw-r--r--src/test/ui/error-codes/E0118.stderr6
-rw-r--r--src/test/ui/error-codes/E0390.rs2
-rw-r--r--src/test/ui/error-codes/E0390.stderr10
-rw-r--r--src/test/ui/feature-gates/feature-gate-rustdoc_internals.rs2
-rw-r--r--src/test/ui/feature-gates/feature-gate-rustdoc_internals.stderr6
-rw-r--r--src/test/ui/issues/issue-59488.stderr4
29 files changed, 278 insertions, 106 deletions
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index 22cb27af4fb..ad9ed798e55 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -404,8 +404,8 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
                     gate_feature_post!(self, rustdoc_internals, attr.span, msg);
                 }
 
-                if nested_meta.has_name(sym::tuple_variadic) {
-                    let msg = "`#[doc(tuple_variadic)]` is meant for internal use only";
+                if nested_meta.has_name(sym::fake_variadic) {
+                    let msg = "`#[doc(fake_variadic)]` is meant for internal use only";
                     gate_feature_post!(self, rustdoc_internals, attr.span, msg);
                 }
             }
diff --git a/compiler/rustc_error_codes/src/error_codes/E0118.md b/compiler/rustc_error_codes/src/error_codes/E0118.md
index 8033aa8384c..cfabae1a634 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0118.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0118.md
@@ -4,7 +4,7 @@ enum, union, or trait object.
 Erroneous code example:
 
 ```compile_fail,E0118
-impl fn(u8) { // error: no nominal type found for inherent implementation
+impl<T> T { // error: no nominal type found for inherent implementation
     fn get_state(&self) -> String {
         // ...
     }
@@ -20,8 +20,8 @@ trait LiveLongAndProsper {
     fn get_state(&self) -> String;
 }
 
-// and now you can implement it on fn(u8)
-impl LiveLongAndProsper for fn(u8) {
+// and now you can implement it on T
+impl<T> LiveLongAndProsper for T {
     fn get_state(&self) -> String {
         "He's dead, Jim!".to_owned()
     }
@@ -33,9 +33,9 @@ For example, `NewType` is a newtype over `Foo` in `struct NewType(Foo)`.
 Example:
 
 ```
-struct TypeWrapper(fn(u8));
+struct TypeWrapper<T>(T);
 
-impl TypeWrapper {
+impl<T> TypeWrapper<T> {
     fn get_state(&self) -> String {
         "Fascinating!".to_owned()
     }
diff --git a/compiler/rustc_error_messages/locales/en-US/passes.ftl b/compiler/rustc_error_messages/locales/en-US/passes.ftl
index e4c9a4dad7b..04c67cf8ff7 100644
--- a/compiler/rustc_error_messages/locales/en-US/passes.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/passes.ftl
@@ -81,8 +81,8 @@ passes-doc-keyword-not-mod = `#[doc(keyword = "...")]` should be used on modules
 
 passes-doc-keyword-invalid-ident = `{$doc_keyword}` is not a valid identifier
 
-passes-doc-tuple-variadic-not-first =
-    `#[doc(tuple_variadic)]` must be used on the first of a set of tuple trait impls with varying arity
+passes-doc-fake-variadic-not-valid =
+    `#[doc(fake_variadic)]` must be used on the first of a set of tuple or fn pointer trait impls with varying arity
 
 passes-doc-keyword-only-impl = `#[doc(keyword = "...")]` should be used on impl blocks
 
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index d96e7d3efe8..f88997f884a 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -706,14 +706,20 @@ impl CheckAttrVisitor<'_> {
         true
     }
 
-    fn check_doc_tuple_variadic(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool {
+    fn check_doc_fake_variadic(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool {
         match self.tcx.hir().find(hir_id).and_then(|node| match node {
             hir::Node::Item(item) => Some(&item.kind),
             _ => None,
         }) {
             Some(ItemKind::Impl(ref i)) => {
-                if !matches!(&i.self_ty.kind, hir::TyKind::Tup([_])) {
-                    self.tcx.sess.emit_err(errors::DocTupleVariadicNotFirst { span: meta.span() });
+                let is_valid = matches!(&i.self_ty.kind, hir::TyKind::Tup([_]))
+                    || if let hir::TyKind::BareFn(bare_fn_ty) = &i.self_ty.kind {
+                        bare_fn_ty.decl.inputs.len() == 1
+                    } else {
+                        false
+                    };
+                if !is_valid {
+                    self.tcx.sess.emit_err(errors::DocFakeVariadicNotValid { span: meta.span() });
                     return false;
                 }
             }
@@ -887,9 +893,9 @@ impl CheckAttrVisitor<'_> {
                             is_valid = false
                         }
 
-                        sym::tuple_variadic
-                            if !self.check_attr_not_crate_level(meta, hir_id, "tuple_variadic")
-                                || !self.check_doc_tuple_variadic(meta, hir_id) =>
+                        sym::fake_variadic
+                            if !self.check_attr_not_crate_level(meta, hir_id, "fake_variadic")
+                                || !self.check_doc_fake_variadic(meta, hir_id) =>
                         {
                             is_valid = false
                         }
@@ -939,7 +945,7 @@ impl CheckAttrVisitor<'_> {
                         | sym::notable_trait
                         | sym::passes
                         | sym::plugins
-                        | sym::tuple_variadic => {}
+                        | sym::fake_variadic => {}
 
                         sym::test => {
                             if !self.check_test_attr(meta, hir_id) {
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index f8e8720ab54..fcd1e9363b1 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -212,8 +212,8 @@ pub struct DocKeywordInvalidIdent {
 }
 
 #[derive(SessionDiagnostic)]
-#[error(passes::doc_tuple_variadic_not_first)]
-pub struct DocTupleVariadicNotFirst {
+#[error(passes::doc_fake_variadic_not_valid)]
+pub struct DocFakeVariadicNotValid {
     #[primary_span]
     pub span: Span,
 }
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 3ccd3fcbafa..ec1d2c39b80 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -696,6 +696,7 @@ symbols! {
         fabsf32,
         fabsf64,
         fadd_fast,
+        fake_variadic,
         fdiv_fast,
         feature,
         fence,
@@ -1471,7 +1472,6 @@ symbols! {
         tuple,
         tuple_from_req,
         tuple_indexing,
-        tuple_variadic,
         two_phase,
         ty,
         type_alias_enum_variants,
diff --git a/compiler/rustc_typeck/src/coherence/inherent_impls.rs b/compiler/rustc_typeck/src/coherence/inherent_impls.rs
index 7a9b874b5e4..52aad636fd8 100644
--- a/compiler/rustc_typeck/src/coherence/inherent_impls.rs
+++ b/compiler/rustc_typeck/src/coherence/inherent_impls.rs
@@ -219,8 +219,9 @@ impl<'tcx> InherentCollect<'tcx> {
             | ty::RawPtr(_)
             | ty::Ref(..)
             | ty::Never
+            | ty::FnPtr(_)
             | ty::Tuple(..) => self.check_primitive_impl(item.def_id, self_ty, items, ty.span),
-            ty::FnPtr(_) | ty::Projection(..) | ty::Opaque(..) | ty::Param(_) => {
+            ty::Projection(..) | ty::Opaque(..) | ty::Param(_) => {
                 let mut err = struct_span_err!(
                     self.tcx.sess,
                     ty.span,
diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs
index 6c3bb7229e5..372141e0933 100644
--- a/library/core/src/fmt/mod.rs
+++ b/library/core/src/fmt/mod.rs
@@ -2562,7 +2562,7 @@ macro_rules! tuple {
 
 macro_rules! maybe_tuple_doc {
     ($a:ident @ #[$meta:meta] $item:item) => {
-        #[doc(tuple_variadic)]
+        #[cfg_attr(not(bootstrap), doc(fake_variadic))]
         #[doc = "This trait is implemented for tuples up to twelve items long."]
         #[$meta]
         $item
diff --git a/library/core/src/hash/mod.rs b/library/core/src/hash/mod.rs
index cf428e0b1cf..5974562aced 100644
--- a/library/core/src/hash/mod.rs
+++ b/library/core/src/hash/mod.rs
@@ -900,7 +900,7 @@ mod impls {
 
     macro_rules! maybe_tuple_doc {
         ($a:ident @ #[$meta:meta] $item:item) => {
-            #[doc(tuple_variadic)]
+            #[cfg_attr(not(bootstrap), doc(fake_variadic))]
             #[doc = "This trait is implemented for tuples up to twelve items long."]
             #[$meta]
             $item
diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs
index 401c8159988..b8e5461640c 100644
--- a/library/core/src/primitive_docs.rs
+++ b/library/core/src/primitive_docs.rs
@@ -996,7 +996,7 @@ impl<T> (T,) {}
 // Fake impl that's only really used for docs.
 #[cfg(doc)]
 #[stable(feature = "rust1", since = "1.0.0")]
-#[doc(tuple_variadic)]
+#[cfg_attr(not(bootstrap), doc(fake_variadic))]
 /// This trait is implemented on arbitrary-length tuples.
 impl<T: Clone> Clone for (T,) {
     fn clone(&self) -> Self {
@@ -1007,7 +1007,7 @@ impl<T: Clone> Clone for (T,) {
 // Fake impl that's only really used for docs.
 #[cfg(doc)]
 #[stable(feature = "rust1", since = "1.0.0")]
-#[doc(tuple_variadic)]
+#[cfg_attr(not(bootstrap), doc(fake_variadic))]
 /// This trait is implemented on arbitrary-length tuples.
 impl<T: Copy> Copy for (T,) {
     // empty
@@ -1441,11 +1441,16 @@ mod prim_ref {}
 /// Note that all of this is not portable to platforms where function pointers and data pointers
 /// have different sizes.
 ///
-/// ### Traits
+/// ### Trait implementations
 ///
-/// Function pointers implement the following traits:
+/// In this documentation the shorthand `fn (T₁, T₂, …, Tₙ)` is used to represent non-variadic
+/// function pointers of varying length. Note that this is a convenience notation to avoid
+/// repetitive documentation, not valid Rust syntax.
+///
+/// Due to a temporary restriction in Rust's type system, these traits are only implemented on
+/// functions that take 12 arguments or less, with the `"Rust"` and `"C"` ABIs. In the future, this
+/// may change:
 ///
-/// * [`Clone`]
 /// * [`PartialEq`]
 /// * [`Eq`]
 /// * [`PartialOrd`]
@@ -1454,15 +1459,50 @@ mod prim_ref {}
 /// * [`Pointer`]
 /// * [`Debug`]
 ///
+/// The following traits are implemented for function pointers with any number of arguments and
+/// any ABI. These traits have implementations that are automatically generated by the compiler,
+/// so are not limited by missing language features:
+///
+/// * [`Clone`]
+/// * [`Copy`]
+/// * [`Send`]
+/// * [`Sync`]
+/// * [`Unpin`]
+/// * [`UnwindSafe`]
+/// * [`RefUnwindSafe`]
+///
 /// [`Hash`]: hash::Hash
 /// [`Pointer`]: fmt::Pointer
+/// [`UnwindSafe`]: panic::UnwindSafe
+/// [`RefUnwindSafe`]: panic::RefUnwindSafe
 ///
-/// Due to a temporary restriction in Rust's type system, these traits are only implemented on
-/// functions that take 12 arguments or less, with the `"Rust"` and `"C"` ABIs. In the future, this
-/// may change.
-///
-/// In addition, function pointers of *any* signature, ABI, or safety are [`Copy`], and all *safe*
-/// function pointers implement [`Fn`], [`FnMut`], and [`FnOnce`]. This works because these traits
-/// are specially known to the compiler.
+/// In addition, all *safe* function pointers implement [`Fn`], [`FnMut`], and [`FnOnce`], because
+/// these traits are specially known to the compiler.
 #[stable(feature = "rust1", since = "1.0.0")]
 mod prim_fn {}
+
+// Required to make auto trait impls render.
+// See src/librustdoc/passes/collect_trait_impls.rs:collect_trait_impls
+#[doc(hidden)]
+#[cfg(not(bootstrap))]
+impl<Ret, T> fn(T) -> Ret {}
+
+// Fake impl that's only really used for docs.
+#[cfg(doc)]
+#[stable(feature = "rust1", since = "1.0.0")]
+#[cfg_attr(not(bootstrap), doc(fake_variadic))]
+/// This trait is implemented on function pointers with any number of arguments.
+impl<Ret, T> Clone for fn(T) -> Ret {
+    fn clone(&self) -> Self {
+        loop {}
+    }
+}
+
+// Fake impl that's only really used for docs.
+#[cfg(doc)]
+#[stable(feature = "rust1", since = "1.0.0")]
+#[cfg_attr(not(bootstrap), doc(fake_variadic))]
+/// This trait is implemented on function pointers with any number of arguments.
+impl<Ret, T> Copy for fn(T) -> Ret {
+    // empty
+}
diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs
index 509854225c4..62548b5fadd 100644
--- a/library/core/src/ptr/mod.rs
+++ b/library/core/src/ptr/mod.rs
@@ -1819,6 +1819,27 @@ pub fn hash<T: ?Sized, S: hash::Hasher>(hashee: *const T, into: &mut S) {
     hashee.hash(into);
 }
 
+// If this is a unary fn pointer, it adds a doc comment.
+// Otherwise, it hides the docs entirely.
+macro_rules! maybe_fnptr_doc {
+    (@ #[$meta:meta] $item:item) => {
+        #[doc(hidden)]
+        #[$meta]
+        $item
+    };
+    ($a:ident @ #[$meta:meta] $item:item) => {
+        #[cfg_attr(not(bootstrap), doc(fake_variadic))]
+        #[doc = "This trait is implemented for function pointers with up to twelve arguments."]
+        #[$meta]
+        $item
+    };
+    ($a:ident $($rest_a:ident)+ @ #[$meta:meta] $item:item) => {
+        #[doc(hidden)]
+        #[$meta]
+        $item
+    };
+}
+
 // FIXME(strict_provenance_magic): function pointers have buggy codegen that
 // necessitates casting to a usize to get the backend to do the right thing.
 // for now I will break AVR to silence *a billion* lints. We should probably
@@ -1827,51 +1848,72 @@ pub fn hash<T: ?Sized, S: hash::Hasher>(hashee: *const T, into: &mut S) {
 // Impls for function pointers
 macro_rules! fnptr_impls_safety_abi {
     ($FnTy: ty, $($Arg: ident),*) => {
-        #[stable(feature = "fnptr_impls", since = "1.4.0")]
-        impl<Ret, $($Arg),*> PartialEq for $FnTy {
-            #[inline]
-            fn eq(&self, other: &Self) -> bool {
-                *self as usize == *other as usize
+        maybe_fnptr_doc! {
+            $($Arg)* @
+            #[stable(feature = "fnptr_impls", since = "1.4.0")]
+            impl<Ret, $($Arg),*> PartialEq for $FnTy {
+                #[inline]
+                fn eq(&self, other: &Self) -> bool {
+                    *self as usize == *other as usize
+                }
             }
         }
 
-        #[stable(feature = "fnptr_impls", since = "1.4.0")]
-        impl<Ret, $($Arg),*> Eq for $FnTy {}
+        maybe_fnptr_doc! {
+            $($Arg)* @
+            #[stable(feature = "fnptr_impls", since = "1.4.0")]
+            impl<Ret, $($Arg),*> Eq for $FnTy {}
+        }
 
-        #[stable(feature = "fnptr_impls", since = "1.4.0")]
-        impl<Ret, $($Arg),*> PartialOrd for $FnTy {
-            #[inline]
-            fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
-                (*self as usize).partial_cmp(&(*other as usize))
+        maybe_fnptr_doc! {
+            $($Arg)* @
+            #[stable(feature = "fnptr_impls", since = "1.4.0")]
+            impl<Ret, $($Arg),*> PartialOrd for $FnTy {
+                #[inline]
+                fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+                    (*self as usize).partial_cmp(&(*other as usize))
+                }
             }
         }
 
-        #[stable(feature = "fnptr_impls", since = "1.4.0")]
-        impl<Ret, $($Arg),*> Ord for $FnTy {
-            #[inline]
-            fn cmp(&self, other: &Self) -> Ordering {
-                (*self as usize).cmp(&(*other as usize))
+        maybe_fnptr_doc! {
+            $($Arg)* @
+            #[stable(feature = "fnptr_impls", since = "1.4.0")]
+            impl<Ret, $($Arg),*> Ord for $FnTy {
+                #[inline]
+                fn cmp(&self, other: &Self) -> Ordering {
+                    (*self as usize).cmp(&(*other as usize))
+                }
             }
         }
 
-        #[stable(feature = "fnptr_impls", since = "1.4.0")]
-        impl<Ret, $($Arg),*> hash::Hash for $FnTy {
-            fn hash<HH: hash::Hasher>(&self, state: &mut HH) {
-                state.write_usize(*self as usize)
+        maybe_fnptr_doc! {
+            $($Arg)* @
+            #[stable(feature = "fnptr_impls", since = "1.4.0")]
+            impl<Ret, $($Arg),*> hash::Hash for $FnTy {
+                fn hash<HH: hash::Hasher>(&self, state: &mut HH) {
+                    state.write_usize(*self as usize)
+                }
             }
         }
 
-        #[stable(feature = "fnptr_impls", since = "1.4.0")]
-        impl<Ret, $($Arg),*> fmt::Pointer for $FnTy {
-            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-                fmt::pointer_fmt_inner(*self as usize, f)
+        maybe_fnptr_doc! {
+            $($Arg)* @
+            #[stable(feature = "fnptr_impls", since = "1.4.0")]
+            impl<Ret, $($Arg),*> fmt::Pointer for $FnTy {
+                fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+                    fmt::pointer_fmt_inner(*self as usize, f)
+                }
             }
         }
 
-        #[stable(feature = "fnptr_impls", since = "1.4.0")]
-        impl<Ret, $($Arg),*> fmt::Debug for $FnTy {
-            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-                fmt::pointer_fmt_inner(*self as usize, f)
+        maybe_fnptr_doc! {
+            $($Arg)* @
+            #[stable(feature = "fnptr_impls", since = "1.4.0")]
+            impl<Ret, $($Arg),*> fmt::Debug for $FnTy {
+                fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+                    fmt::pointer_fmt_inner(*self as usize, f)
+                }
             }
         }
     }
@@ -1896,7 +1938,7 @@ macro_rules! fnptr_impls_args {
 }
 
 fnptr_impls_args! {}
-fnptr_impls_args! { A }
+fnptr_impls_args! { T }
 fnptr_impls_args! { A, B }
 fnptr_impls_args! { A, B, C }
 fnptr_impls_args! { A, B, C, D }
diff --git a/library/core/src/tuple.rs b/library/core/src/tuple.rs
index b664b0bb866..d189e6400f1 100644
--- a/library/core/src/tuple.rs
+++ b/library/core/src/tuple.rs
@@ -107,7 +107,7 @@ macro_rules! tuple_impls {
 // Otherwise, it hides the docs entirely.
 macro_rules! maybe_tuple_doc {
     ($a:ident @ #[$meta:meta] $item:item) => {
-        #[doc(tuple_variadic)]
+        #[cfg_attr(not(bootstrap), doc(fake_variadic))]
         #[doc = "This trait is implemented for tuples up to twelve items long."]
         #[$meta]
         $item
diff --git a/library/std/src/primitive_docs.rs b/library/std/src/primitive_docs.rs
index 401c8159988..b8e5461640c 100644
--- a/library/std/src/primitive_docs.rs
+++ b/library/std/src/primitive_docs.rs
@@ -996,7 +996,7 @@ impl<T> (T,) {}
 // Fake impl that's only really used for docs.
 #[cfg(doc)]
 #[stable(feature = "rust1", since = "1.0.0")]
-#[doc(tuple_variadic)]
+#[cfg_attr(not(bootstrap), doc(fake_variadic))]
 /// This trait is implemented on arbitrary-length tuples.
 impl<T: Clone> Clone for (T,) {
     fn clone(&self) -> Self {
@@ -1007,7 +1007,7 @@ impl<T: Clone> Clone for (T,) {
 // Fake impl that's only really used for docs.
 #[cfg(doc)]
 #[stable(feature = "rust1", since = "1.0.0")]
-#[doc(tuple_variadic)]
+#[cfg_attr(not(bootstrap), doc(fake_variadic))]
 /// This trait is implemented on arbitrary-length tuples.
 impl<T: Copy> Copy for (T,) {
     // empty
@@ -1441,11 +1441,16 @@ mod prim_ref {}
 /// Note that all of this is not portable to platforms where function pointers and data pointers
 /// have different sizes.
 ///
-/// ### Traits
+/// ### Trait implementations
 ///
-/// Function pointers implement the following traits:
+/// In this documentation the shorthand `fn (T₁, T₂, …, Tₙ)` is used to represent non-variadic
+/// function pointers of varying length. Note that this is a convenience notation to avoid
+/// repetitive documentation, not valid Rust syntax.
+///
+/// Due to a temporary restriction in Rust's type system, these traits are only implemented on
+/// functions that take 12 arguments or less, with the `"Rust"` and `"C"` ABIs. In the future, this
+/// may change:
 ///
-/// * [`Clone`]
 /// * [`PartialEq`]
 /// * [`Eq`]
 /// * [`PartialOrd`]
@@ -1454,15 +1459,50 @@ mod prim_ref {}
 /// * [`Pointer`]
 /// * [`Debug`]
 ///
+/// The following traits are implemented for function pointers with any number of arguments and
+/// any ABI. These traits have implementations that are automatically generated by the compiler,
+/// so are not limited by missing language features:
+///
+/// * [`Clone`]
+/// * [`Copy`]
+/// * [`Send`]
+/// * [`Sync`]
+/// * [`Unpin`]
+/// * [`UnwindSafe`]
+/// * [`RefUnwindSafe`]
+///
 /// [`Hash`]: hash::Hash
 /// [`Pointer`]: fmt::Pointer
+/// [`UnwindSafe`]: panic::UnwindSafe
+/// [`RefUnwindSafe`]: panic::RefUnwindSafe
 ///
-/// Due to a temporary restriction in Rust's type system, these traits are only implemented on
-/// functions that take 12 arguments or less, with the `"Rust"` and `"C"` ABIs. In the future, this
-/// may change.
-///
-/// In addition, function pointers of *any* signature, ABI, or safety are [`Copy`], and all *safe*
-/// function pointers implement [`Fn`], [`FnMut`], and [`FnOnce`]. This works because these traits
-/// are specially known to the compiler.
+/// In addition, all *safe* function pointers implement [`Fn`], [`FnMut`], and [`FnOnce`], because
+/// these traits are specially known to the compiler.
 #[stable(feature = "rust1", since = "1.0.0")]
 mod prim_fn {}
+
+// Required to make auto trait impls render.
+// See src/librustdoc/passes/collect_trait_impls.rs:collect_trait_impls
+#[doc(hidden)]
+#[cfg(not(bootstrap))]
+impl<Ret, T> fn(T) -> Ret {}
+
+// Fake impl that's only really used for docs.
+#[cfg(doc)]
+#[stable(feature = "rust1", since = "1.0.0")]
+#[cfg_attr(not(bootstrap), doc(fake_variadic))]
+/// This trait is implemented on function pointers with any number of arguments.
+impl<Ret, T> Clone for fn(T) -> Ret {
+    fn clone(&self) -> Self {
+        loop {}
+    }
+}
+
+// Fake impl that's only really used for docs.
+#[cfg(doc)]
+#[stable(feature = "rust1", since = "1.0.0")]
+#[cfg_attr(not(bootstrap), doc(fake_variadic))]
+/// This trait is implemented on function pointers with any number of arguments.
+impl<Ret, T> Copy for fn(T) -> Ret {
+    // empty
+}
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index 23af7d2ef1e..ce10ca9aa3d 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -506,8 +506,8 @@ pub(crate) fn build_impl(
             for_,
             items: trait_items,
             polarity,
-            kind: if utils::has_doc_flag(tcx, did, sym::tuple_variadic) {
-                ImplKind::TupleVaradic
+            kind: if utils::has_doc_flag(tcx, did, sym::fake_variadic) {
+                ImplKind::FakeVaradic
             } else {
                 ImplKind::Normal
             },
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 671b40dbfe6..9865601da5f 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -2037,8 +2037,8 @@ fn clean_impl<'tcx>(
             for_,
             items,
             polarity: tcx.impl_polarity(def_id),
-            kind: if utils::has_doc_flag(tcx, def_id.to_def_id(), sym::tuple_variadic) {
-                ImplKind::TupleVaradic
+            kind: if utils::has_doc_flag(tcx, def_id.to_def_id(), sym::fake_variadic) {
+                ImplKind::FakeVaradic
             } else {
                 ImplKind::Normal
             },
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index d29ba2dedaf..8c08f776679 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -1841,7 +1841,7 @@ impl PrimitiveType {
                 Reference => [RefSimplifiedType(Mutability::Not), RefSimplifiedType(Mutability::Mut)].into_iter().collect(),
                 // FIXME: This will be wrong if we ever add inherent impls
                 // for function pointers.
-                Fn => ArrayVec::new(),
+                Fn => single(FunctionSimplifiedType(1)),
                 Never => single(NeverSimplifiedType),
             }
         })
@@ -2394,7 +2394,7 @@ impl Impl {
 pub(crate) enum ImplKind {
     Normal,
     Auto,
-    TupleVaradic,
+    FakeVaradic,
     Blanket(Box<Type>),
 }
 
@@ -2407,8 +2407,8 @@ impl ImplKind {
         matches!(self, ImplKind::Blanket(_))
     }
 
-    pub(crate) fn is_tuple_variadic(&self) -> bool {
-        matches!(self, ImplKind::TupleVaradic)
+    pub(crate) fn is_fake_variadic(&self) -> bool {
+        matches!(self, ImplKind::FakeVaradic)
     }
 
     pub(crate) fn as_blanket_ty(&self) -> Option<&Type> {
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 9f46ab54d3e..36a47b05cb9 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -1165,10 +1165,43 @@ impl clean::Impl {
 
             if let clean::Type::Tuple(types) = &self.for_ &&
                 let [clean::Type::Generic(name)] = &types[..] &&
-                (self.kind.is_tuple_variadic() || self.kind.is_auto()) {
+                (self.kind.is_fake_variadic() || self.kind.is_auto())
+            {
                 // Hardcoded anchor library/core/src/primitive_docs.rs
                 // Link should match `# Trait implementations`
                 primitive_link_fragment(f, PrimitiveType::Tuple, &format!("({name}₁, {name}₂, …, {name}ₙ)"), "#trait-implementations-1", cx)?;
+            } else if let clean::BareFunction(bare_fn) = &self.for_ &&
+                let [clean::Argument { type_: clean::Type::Generic(name), .. }] = &bare_fn.decl.inputs.values[..] &&
+                (self.kind.is_fake_variadic() || self.kind.is_auto())
+            {
+                // Hardcoded anchor library/core/src/primitive_docs.rs
+                // Link should match `# Trait implementations`
+
+                let hrtb = bare_fn.print_hrtb_with_space(cx);
+                let unsafety = bare_fn.unsafety.print_with_space();
+                let abi = print_abi_with_space(bare_fn.abi);
+                if f.alternate() {
+                    write!(
+                        f,
+                        "{hrtb:#}{unsafety}{abi:#}",
+                    )?;
+                } else {
+                    write!(
+                        f,
+                        "{hrtb}{unsafety}{abi}",
+                    )?;
+                }
+                let ellipsis = if bare_fn.decl.c_variadic {
+                    ", ..."
+                } else {
+                    ""
+                };
+                primitive_link_fragment(f, PrimitiveType::Tuple, &format!("fn ({name}₁, {name}₂, …, {name}ₙ{ellipsis})"), "#trait-implementations-1", cx)?;
+                // Write output.
+                if let clean::FnRetTy::Return(ty) = &bare_fn.decl.output {
+                    write!(f, " -> ")?;
+                    fmt_type(ty, f, use_absolute, cx)?;
+                }
             } else if let Some(ty) = self.kind.as_blanket_ty() {
                 fmt_type(ty, f, use_absolute, cx)?;
             } else {
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index 2598b9b0b28..9000ab472d9 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -574,7 +574,7 @@ impl FromWithTcx<clean::Impl> for Impl {
         let trait_ = trait_.map(|path| clean::Type::Path { path }.into_tcx(tcx));
         // FIXME: use something like ImplKind in JSON?
         let (synthetic, blanket_impl) = match kind {
-            clean::ImplKind::Normal | clean::ImplKind::TupleVaradic => (false, None),
+            clean::ImplKind::Normal | clean::ImplKind::FakeVaradic => (false, None),
             clean::ImplKind::Auto => (true, None),
             clean::ImplKind::Blanket(ty) => (false, Some(*ty)),
         };
diff --git a/src/test/rustdoc-ui/tuple-variadic-check.rs b/src/test/rustdoc-ui/tuple-variadic-check.rs
index 11ce2dbe280..505de53481f 100644
--- a/src/test/rustdoc-ui/tuple-variadic-check.rs
+++ b/src/test/rustdoc-ui/tuple-variadic-check.rs
@@ -3,13 +3,13 @@
 trait Mine {}
 
 // This one is fine
-#[doc(tuple_variadic)]
+#[doc(fake_variadic)]
 impl<T> Mine for (T,) {}
 
 trait Mine2 {}
 
 // This one is not
-#[doc(tuple_variadic)] //~ ERROR
+#[doc(fake_variadic)] //~ ERROR
 impl<T, U> Mine for (T,U) {}
 
 fn main() {}
diff --git a/src/test/rustdoc-ui/tuple-variadic-check.stderr b/src/test/rustdoc-ui/tuple-variadic-check.stderr
index 358d06d6a42..d127fb858d1 100644
--- a/src/test/rustdoc-ui/tuple-variadic-check.stderr
+++ b/src/test/rustdoc-ui/tuple-variadic-check.stderr
@@ -1,8 +1,8 @@
-error: `#[doc(tuple_variadic)]` must be used on the first of a set of tuple trait impls with varying arity
+error: `#[doc(fake_variadic)]` must be used on the first of a set of tuple or fn pointer trait impls with varying arity
   --> $DIR/tuple-variadic-check.rs:12:7
    |
-LL | #[doc(tuple_variadic)]
-   |       ^^^^^^^^^^^^^^
+LL | #[doc(fake_variadic)]
+   |       ^^^^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/rustdoc/primitive-tuple-auto-trait.rs b/src/test/rustdoc/primitive-tuple-auto-trait.rs
index 71b0b077009..a2fbbf078d5 100644
--- a/src/test/rustdoc/primitive-tuple-auto-trait.rs
+++ b/src/test/rustdoc/primitive-tuple-auto-trait.rs
@@ -16,7 +16,7 @@
 // @has - '//h2[@id="trait-implementations-1"]' 'Trait implementations'
 /// # Trait implementations
 ///
-/// This header is hard-coded in the HTML format linking for `#[doc(tuple_variadics)]`.
+/// This header is hard-coded in the HTML format linking for `#[doc(fake_variadics)]`.
 /// To make sure it gets linked correctly, we need to make sure the hardcoded anchor
 /// in the code matches what rustdoc generates for the header.
 mod tuple_prim {}
diff --git a/src/test/rustdoc/primitive-tuple-variadic.rs b/src/test/rustdoc/primitive-tuple-variadic.rs
index 4fd6254f674..db7cfd60c71 100644
--- a/src/test/rustdoc/primitive-tuple-variadic.rs
+++ b/src/test/rustdoc/primitive-tuple-variadic.rs
@@ -7,12 +7,12 @@ pub trait Foo {}
 
 // @has foo/trait.Foo.html
 // @has - '//section[@id="impl-Foo-for-(T%2C)"]/h3' 'impl<T> Foo for (T₁, T₂, …, Tₙ)'
-#[doc(tuple_variadic)]
+#[doc(fake_variadic)]
 impl<T> Foo for (T,) {}
 
 pub trait Bar {}
 
 // @has foo/trait.Bar.html
 // @has - '//section[@id="impl-Bar-for-(U%2C)"]/h3' 'impl<U: Foo> Bar for (U₁, U₂, …, Uₙ)'
-#[doc(tuple_variadic)]
+#[doc(fake_variadic)]
 impl<U: Foo> Bar for (U,) {}
diff --git a/src/test/ui/error-codes/E0118.rs b/src/test/ui/error-codes/E0118.rs
index aaef8113b8a..a61ba7bbf32 100644
--- a/src/test/ui/error-codes/E0118.rs
+++ b/src/test/ui/error-codes/E0118.rs
@@ -1,4 +1,4 @@
-impl fn(u8) { //~ ERROR E0118
+impl<T> T { //~ ERROR E0118
     fn get_state(&self) -> String {
        String::new()
     }
diff --git a/src/test/ui/error-codes/E0118.stderr b/src/test/ui/error-codes/E0118.stderr
index 296fb5d664a..8c6fa7947a8 100644
--- a/src/test/ui/error-codes/E0118.stderr
+++ b/src/test/ui/error-codes/E0118.stderr
@@ -1,8 +1,8 @@
 error[E0118]: no nominal type found for inherent implementation
-  --> $DIR/E0118.rs:1:6
+  --> $DIR/E0118.rs:1:9
    |
-LL | impl fn(u8) {
-   |      ^^^^^^ impl requires a nominal type
+LL | impl<T> T {
+   |         ^ impl requires a nominal type
    |
    = note: either implement a trait on it or create a newtype to wrap it instead
 
diff --git a/src/test/ui/error-codes/E0390.rs b/src/test/ui/error-codes/E0390.rs
index 4eb59a053b4..507483dec2e 100644
--- a/src/test/ui/error-codes/E0390.rs
+++ b/src/test/ui/error-codes/E0390.rs
@@ -4,5 +4,7 @@ struct Foo {
 
 impl *mut Foo {} //~ ERROR E0390
 
+impl fn(Foo) {} //~ ERROR E0390
+
 fn main() {
 }
diff --git a/src/test/ui/error-codes/E0390.stderr b/src/test/ui/error-codes/E0390.stderr
index e635d4ec196..0e5a9ca762b 100644
--- a/src/test/ui/error-codes/E0390.stderr
+++ b/src/test/ui/error-codes/E0390.stderr
@@ -6,6 +6,14 @@ LL | impl *mut Foo {}
    |
    = help: consider using an extension trait instead
 
-error: aborting due to previous error
+error[E0390]: cannot define inherent `impl` for primitive types
+  --> $DIR/E0390.rs:7:6
+   |
+LL | impl fn(Foo) {}
+   |      ^^^^^^^
+   |
+   = help: consider using an extension trait instead
+
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0390`.
diff --git a/src/test/ui/feature-gates/feature-gate-rustdoc_internals.rs b/src/test/ui/feature-gates/feature-gate-rustdoc_internals.rs
index 6a144412d07..58306a4cfc9 100644
--- a/src/test/ui/feature-gates/feature-gate-rustdoc_internals.rs
+++ b/src/test/ui/feature-gates/feature-gate-rustdoc_internals.rs
@@ -4,7 +4,7 @@ mod foo {}
 
 trait Mine {}
 
-#[doc(tuple_variadic)]  //~ ERROR: `#[doc(tuple_variadic)]` is meant for internal use only
+#[doc(fake_variadic)]  //~ ERROR: `#[doc(fake_variadic)]` is meant for internal use only
 impl<T> Mine for (T,) {}
 
 fn main() {}
diff --git a/src/test/ui/feature-gates/feature-gate-rustdoc_internals.stderr b/src/test/ui/feature-gates/feature-gate-rustdoc_internals.stderr
index 9fe08afd4f0..c4272a2c04c 100644
--- a/src/test/ui/feature-gates/feature-gate-rustdoc_internals.stderr
+++ b/src/test/ui/feature-gates/feature-gate-rustdoc_internals.stderr
@@ -7,11 +7,11 @@ LL | #[doc(keyword = "match")]
    = note: see issue #90418 <https://github.com/rust-lang/rust/issues/90418> for more information
    = help: add `#![feature(rustdoc_internals)]` to the crate attributes to enable
 
-error[E0658]: `#[doc(tuple_variadic)]` is meant for internal use only
+error[E0658]: `#[doc(fake_variadic)]` is meant for internal use only
   --> $DIR/feature-gate-rustdoc_internals.rs:7:1
    |
-LL | #[doc(tuple_variadic)]
-   | ^^^^^^^^^^^^^^^^^^^^^^
+LL | #[doc(fake_variadic)]
+   | ^^^^^^^^^^^^^^^^^^^^^
    |
    = note: see issue #90418 <https://github.com/rust-lang/rust/issues/90418> for more information
    = help: add `#![feature(rustdoc_internals)]` to the crate attributes to enable
diff --git a/src/test/ui/issues/issue-59488.stderr b/src/test/ui/issues/issue-59488.stderr
index 76a47c49bba..7ce3dedaa88 100644
--- a/src/test/ui/issues/issue-59488.stderr
+++ b/src/test/ui/issues/issue-59488.stderr
@@ -96,13 +96,13 @@ LL |     assert_eq!(Foo::Bar, i);
    = help: the trait `Debug` is not implemented for `fn(usize) -> Foo {Foo::Bar}`
    = help: the following other types implement trait `Debug`:
              extern "C" fn() -> Ret
-             extern "C" fn(A) -> Ret
-             extern "C" fn(A, ...) -> Ret
              extern "C" fn(A, B) -> Ret
              extern "C" fn(A, B, ...) -> Ret
              extern "C" fn(A, B, C) -> Ret
              extern "C" fn(A, B, C, ...) -> Ret
              extern "C" fn(A, B, C, D) -> Ret
+             extern "C" fn(A, B, C, D, ...) -> Ret
+             extern "C" fn(A, B, C, D, E) -> Ret
            and 68 others
    = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)