From 1169832f2ff1ce740dc3d68de2df3745ec4e1aef Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Thu, 16 Jun 2022 09:50:57 -0700 Subject: [PATCH 1/3] rustdoc: extend `#[doc(tuple_variadic)]` to fn pointers The attribute is also renamed `fake_variadic`. --- compiler/rustc_ast_passes/src/feature_gate.rs | 4 ++-- .../locales/en-US/passes.ftl | 4 ++-- compiler/rustc_passes/src/check_attr.rs | 20 ++++++++++++------- compiler/rustc_passes/src/errors.rs | 4 ++-- compiler/rustc_span/src/symbol.rs | 2 +- library/core/src/fmt/mod.rs | 2 +- library/core/src/hash/mod.rs | 2 +- library/core/src/primitive_docs.rs | 4 ++-- library/core/src/tuple.rs | 2 +- library/std/src/primitive_docs.rs | 4 ++-- src/librustdoc/clean/inline.rs | 4 ++-- src/librustdoc/clean/mod.rs | 4 ++-- src/librustdoc/clean/types.rs | 6 +++--- src/librustdoc/html/format.rs | 15 +++++++++++++- src/librustdoc/json/conversions.rs | 2 +- src/test/rustdoc-ui/tuple-variadic-check.rs | 4 ++-- .../rustdoc-ui/tuple-variadic-check.stderr | 6 +++--- .../rustdoc/primitive-tuple-auto-trait.rs | 2 +- src/test/rustdoc/primitive-tuple-variadic.rs | 4 ++-- .../feature-gate-rustdoc_internals.rs | 2 +- .../feature-gate-rustdoc_internals.stderr | 6 +++--- 21 files changed, 61 insertions(+), 42 deletions(-) diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index ad0e0384acf..e7004ff4bc5 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -402,8 +402,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_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 d5e65c0b442..924bc2b6bce 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -685,6 +685,7 @@ symbols! { fabsf32, fabsf64, fadd_fast, + fake_variadic, fdiv_fast, feature, fence, @@ -1460,7 +1461,6 @@ symbols! { tuple, tuple_from_req, tuple_indexing, - tuple_variadic, two_phase, ty, type_alias_enum_variants, 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..4f08b562685 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -996,7 +996,7 @@ impl (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 Clone for (T,) { fn clone(&self) -> Self { @@ -1007,7 +1007,7 @@ impl 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 Copy for (T,) { // empty 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..4f08b562685 100644 --- a/library/std/src/primitive_docs.rs +++ b/library/std/src/primitive_docs.rs @@ -996,7 +996,7 @@ impl (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 Clone for (T,) { fn clone(&self) -> Self { @@ -1007,7 +1007,7 @@ impl 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 Copy for (T,) { // 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 d6260b8ca06..a5c5d1ac182 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -2005,8 +2005,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..5ddf7ea8a85 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -2394,7 +2394,7 @@ impl Impl { pub(crate) enum ImplKind { Normal, Auto, - TupleVaradic, + FakeVaradic, Blanket(Box), } @@ -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..291e6bc2fe4 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -1165,10 +1165,23 @@ 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::Type::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` + primitive_link_fragment(f, PrimitiveType::Tuple, &format!("fn ({name}₁, {name}₂, …, {name}ₙ)"), "#trait-implementations-1", cx)?; + // Not implemented. + assert!(!bare_fn.decl.c_variadic); + // 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 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 Mine for (T,) {} trait Mine2 {} // This one is not -#[doc(tuple_variadic)] //~ ERROR +#[doc(fake_variadic)] //~ ERROR impl 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 Foo for (T₁, T₂, …, Tₙ)' -#[doc(tuple_variadic)] +#[doc(fake_variadic)] impl Foo for (T,) {} pub trait Bar {} // @has foo/trait.Bar.html // @has - '//section[@id="impl-Bar-for-(U%2C)"]/h3' 'impl Bar for (U₁, U₂, …, Uₙ)' -#[doc(tuple_variadic)] +#[doc(fake_variadic)] impl Bar for (U,) {} 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 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 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 for more information = help: add `#![feature(rustdoc_internals)]` to the crate attributes to enable From 5271e32c463e92f1afa6c54aa9dbb6a9d8454e0d Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Thu, 16 Jun 2022 14:14:38 -0700 Subject: [PATCH 2/3] Improve the function pointer docs * 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`. --- .../src/error_codes/E0118.md | 10 +- .../src/coherence/inherent_impls.rs | 3 +- library/core/src/primitive_docs.rs | 60 +++++++++-- library/core/src/ptr/mod.rs | 100 +++++++++++++----- library/std/src/primitive_docs.rs | 60 +++++++++-- src/librustdoc/clean/types.rs | 2 +- src/librustdoc/html/format.rs | 32 ++++-- src/test/ui/error-codes/E0118.rs | 2 +- src/test/ui/error-codes/E0118.stderr | 6 +- src/test/ui/error-codes/E0390.rs | 2 + src/test/ui/error-codes/E0390.stderr | 10 +- src/test/ui/issues/issue-59488.stderr | 4 +- 12 files changed, 222 insertions(+), 69 deletions(-) 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 { // 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 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); -impl TypeWrapper { +impl TypeWrapper { fn get_state(&self) -> String { "Fascinating!".to_owned() } 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/primitive_docs.rs b/library/core/src/primitive_docs.rs index 4f08b562685..b8e5461640c 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -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 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 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 Copy for fn(T) -> Ret { + // empty +} diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 509854225c4..caa279f0de5 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -1819,6 +1819,27 @@ pub fn hash(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(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 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 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 Eq for $FnTy {} + maybe_fnptr_doc! { + $($Arg)* @ + #[stable(feature = "fnptr_impls", since = "1.4.0")] + impl Eq for $FnTy {} + } - #[stable(feature = "fnptr_impls", since = "1.4.0")] - impl PartialOrd for $FnTy { - #[inline] - fn partial_cmp(&self, other: &Self) -> Option { - (*self as usize).partial_cmp(&(*other as usize)) + maybe_fnptr_doc! { + $($Arg)* @ + #[stable(feature = "fnptr_impls", since = "1.4.0")] + impl PartialOrd for $FnTy { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + (*self as usize).partial_cmp(&(*other as usize)) + } } } - #[stable(feature = "fnptr_impls", since = "1.4.0")] - impl 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 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 hash::Hash for $FnTy { - fn hash(&self, state: &mut HH) { - state.write_usize(*self as usize) + maybe_fnptr_doc! { + $($Arg)* @ + #[stable(feature = "fnptr_impls", since = "1.4.0")] + impl hash::Hash for $FnTy { + fn hash(&self, state: &mut HH) { + state.write_usize(*self as usize) + } } } - #[stable(feature = "fnptr_impls", since = "1.4.0")] - impl 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 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 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 fmt::Debug for $FnTy { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::pointer_fmt_inner(*self as usize, f) + } } } } diff --git a/library/std/src/primitive_docs.rs b/library/std/src/primitive_docs.rs index 4f08b562685..b8e5461640c 100644 --- a/library/std/src/primitive_docs.rs +++ b/library/std/src/primitive_docs.rs @@ -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 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 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 Copy for fn(T) -> Ret { + // empty +} diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 5ddf7ea8a85..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), } }) diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 291e6bc2fe4..36a47b05cb9 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -1165,18 +1165,38 @@ impl clean::Impl { 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, " -> ")?; 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 { //~ 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 { + | ^ 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/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) From ddb5a2638acbbe33815aefdbeff3266df4ebcd21 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Thu, 16 Jun 2022 14:44:09 -0700 Subject: [PATCH 3/3] Use `T` for all the function primitive docs lists --- library/core/src/ptr/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index caa279f0de5..62548b5fadd 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -1938,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 }