]> git.lizzy.rs Git - rust.git/commitdiff
Improve the function pointer docs
authorMichael Howell <michael@notriddle.com>
Thu, 16 Jun 2022 21:14:38 +0000 (14:14 -0700)
committerMichael Howell <michael@notriddle.com>
Tue, 19 Jul 2022 15:52:24 +0000 (08:52 -0700)
* Reduce duplicate impls; show only the `fn (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`.

12 files changed:
compiler/rustc_error_codes/src/error_codes/E0118.md
compiler/rustc_typeck/src/coherence/inherent_impls.rs
library/core/src/primitive_docs.rs
library/core/src/ptr/mod.rs
library/std/src/primitive_docs.rs
src/librustdoc/clean/types.rs
src/librustdoc/html/format.rs
src/test/ui/error-codes/E0118.rs
src/test/ui/error-codes/E0118.stderr
src/test/ui/error-codes/E0390.rs
src/test/ui/error-codes/E0390.stderr
src/test/ui/issues/issue-59488.stderr

index 8033aa8384c2e1276f742b8dd37070774da23ef8..cfabae1a6346d6a2d0217d4622efb5706d06ed2f 100644 (file)
@@ -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()
     }
index 7a9b874b5e45643ae4e7992929cde3eea76e4b61..52aad636fd88ac887da8d5b229c7f94ae72c6d01 100644 (file)
@@ -219,8 +219,9 @@ fn check_item(&mut self, id: hir::ItemId) {
             | 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,
index 4f08b5626851bab29c015bad2a900151a1c50b06..b8e5461640c0581ea4a242fb7724aea2140c01da 100644 (file)
@@ -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
+}
index 509854225c4f3e5be294702a5ebe6dcf75821478..caa279f0de579188b1e2cd6f35fbe9e09929670e 100644 (file)
@@ -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)
+                }
             }
         }
     }
index 4f08b5626851bab29c015bad2a900151a1c50b06..b8e5461640c0581ea4a242fb7724aea2140c01da 100644 (file)
@@ -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
+}
index 5ddf7ea8a85f4c1cddb4325a73eae99c04211683..8c08f77667904769c4a855f9f7ae5cd06bccda90 100644 (file)
@@ -1841,7 +1841,7 @@ pub(crate) fn simplified_types() -> &'static SimplifiedTypes {
                 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),
             }
         })
index 291e6bc2fe4c7c5c7076c92fb47eb1c75ddd6ff1..36a47b05cb9d674b0dadd2a817fac9e35bdf7c3b 100644 (file)
@@ -1165,18 +1165,38 @@ pub(crate) fn print<'a, 'tcx: 'a>(
 
             if let clean::Type::Tuple(types) = &self.for_ &&
                 let [clean::Type::Generic(name)] = &types[..] &&
-                (self.kind.is_fake_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::Type::BareFunction(bare_fn) = &self.for_ &&
+            } 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()) {
+                (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!("fn ({name}₁, {name}₂, …, {name}ₙ)"), "#trait-implementations-1", cx)?;
-                // Not implemented.
-                assert!(!bare_fn.decl.c_variadic);
+
+                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, " -> ")?;
index aaef8113b8ae243bd7ea3a78be5aafe005faf4fa..a61ba7bbf328cee6aaaa5efde5e4e9125ec9f048 100644 (file)
@@ -1,4 +1,4 @@
-impl fn(u8) { //~ ERROR E0118
+impl<T> T { //~ ERROR E0118
     fn get_state(&self) -> String {
        String::new()
     }
index 296fb5d664a227308b5341cc8df579997e971328..8c6fa7947a8537746be07f1c35f7d2eaf8881367 100644 (file)
@@ -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
 
index 4eb59a053b45b50f289b871ab8da398d54135f0b..507483dec2ee263c1b99c3ecda0946131774b5b4 100644 (file)
@@ -4,5 +4,7 @@ struct Foo {
 
 impl *mut Foo {} //~ ERROR E0390
 
+impl fn(Foo) {} //~ ERROR E0390
+
 fn main() {
 }
index e635d4ec196a271351a9dd1d8b52c68f2dc4077d..0e5a9ca762b768ff2bde803695dfcac6697a9108 100644 (file)
@@ -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`.
index 76a47c49bbafb40be760823fffec771f98d4fc3d..7ce3dedaa887bf7f6a777d0444967c2f1412eff8 100644 (file)
@@ -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)