about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-07-29 09:57:44 +0000
committerbors <bors@rust-lang.org>2022-07-29 09:57:44 +0000
commit2f847b81a0d8633f200f2c2269c1c43fe9e7def3 (patch)
treec3e1a436a3c47079cbaa9ad7d15567550f2161d7
parent7dfdd64433b07239fbac50b8227b6e03a0ba8f30 (diff)
parenta8f77ade97342ea0d4ed5198be9eb6505adde42c (diff)
downloadrust-2f847b81a0d8633f200f2c2269c1c43fe9e7def3.tar.gz
rust-2f847b81a0d8633f200f2c2269c1c43fe9e7def3.zip
Auto merge of #99892 - JohnTitor:rollup-qi4fem8, r=JohnTitor
Rollup of 8 pull requests

Successful merges:

 - #99686 (add suggestion when there is a impl of external trait on pointer with wrong coherence rules)
 - #99760 (doc/rustc: describe the uefi target platforms)
 - #99766 (Htmldocck: Substitute the doc channel when blessing)
 - #99781 (Use String::from_utf8_lossy in CStr demo)
 - #99803 (Update mentions to `rustc_metadata::rmeta::Lazy`)
 - #99845 (Remove `$` prefix for bash scripts in doc)
 - #99850 (rustdoc: Remove more Clean trait implementations)
 - #99872 (Clone the `src/llvm-project` submodule if profiling is enabled)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder.rs2
-rw-r--r--compiler/rustc_metadata/src/rmeta/mod.rs18
-rw-r--r--compiler/rustc_metadata/src/rmeta/table.rs4
-rw-r--r--compiler/rustc_typeck/src/coherence/orphan.rs37
-rw-r--r--library/core/src/ffi/c_str.rs6
-rw-r--r--src/bootstrap/compile.rs5
-rw-r--r--src/doc/rustc/src/SUMMARY.md1
-rw-r--r--src/doc/rustc/src/platform-support/unknown-uefi.md254
-rw-r--r--src/doc/rustc/src/targets/custom.md4
-rw-r--r--src/etc/htmldocck.py1
-rw-r--r--src/librustdoc/clean/inline.rs7
-rw-r--r--src/librustdoc/clean/mod.rs49
-rw-r--r--src/librustdoc/clean/types.rs4
-rw-r--r--src/test/ui/errors/issue-99572-impl-trait-on-pointer.rs25
-rw-r--r--src/test/ui/errors/issue-99572-impl-trait-on-pointer.stderr31
15 files changed, 398 insertions, 50 deletions
diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs
index 8fa703a7760..6b0b5ac7da9 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder.rs
@@ -83,7 +83,7 @@ pub(crate) struct CrateMetadata {
 
     // --- Some data pre-decoded from the metadata blob, usually for performance ---
     /// NOTE(eddyb) we pass `'static` to a `'tcx` parameter because this
-    /// lifetime is only used behind `Lazy`, and therefore acts like a
+    /// lifetime is only used behind `LazyValue`, `LazyArray`, or `LazyTable`, and therefore acts like a
     /// universal (`for<'tcx>`), that is paired up with whichever `TyCtxt`
     /// is being used to decode those values.
     root: CrateRoot,
diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs
index 0f291f92647..23198a85369 100644
--- a/compiler/rustc_metadata/src/rmeta/mod.rs
+++ b/compiler/rustc_metadata/src/rmeta/mod.rs
@@ -66,13 +66,13 @@ pub const METADATA_HEADER: &[u8] = &[b'r', b'u', b's', b't', 0, 0, 0, METADATA_V
 ///
 /// Metadata is effective a tree, encoded in post-order,
 /// and with the root's position written next to the header.
-/// That means every single `Lazy` points to some previous
+/// That means every single `LazyValue` points to some previous
 /// location in the metadata and is part of a larger node.
 ///
-/// The first `Lazy` in a node is encoded as the backwards
+/// The first `LazyValue` in a node is encoded as the backwards
 /// distance from the position where the containing node
-/// starts and where the `Lazy` points to, while the rest
-/// use the forward distance from the previous `Lazy`.
+/// starts and where the `LazyValue` points to, while the rest
+/// use the forward distance from the previous `LazyValue`.
 /// Distances start at 1, as 0-byte nodes are invalid.
 /// Also invalid are nodes being referred in a different
 /// order than they were encoded in.
@@ -94,12 +94,12 @@ impl<T> LazyValue<T> {
 
 /// A list of lazily-decoded values.
 ///
-/// Unlike `Lazy<Vec<T>>`, the length is encoded next to the
+/// Unlike `LazyValue<Vec<T>>`, the length is encoded next to the
 /// position, not at the position, which means that the length
 /// doesn't need to be known before encoding all the elements.
 ///
 /// If the length is 0, no position is encoded, but otherwise,
-/// the encoding is that of `Lazy`, with the distinction that
+/// the encoding is that of `LazyArray`, with the distinction that
 /// the minimal distance the length of the sequence, i.e.
 /// it's assumed there's no 0-byte element in the sequence.
 struct LazyArray<T> {
@@ -167,17 +167,17 @@ impl<I, T> Clone for LazyTable<I, T> {
     }
 }
 
-/// Encoding / decoding state for `Lazy`.
+/// Encoding / decoding state for `Lazy`s (`LazyValue`, `LazyArray`, and `LazyTable`).
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
 enum LazyState {
     /// Outside of a metadata node.
     NoNode,
 
-    /// Inside a metadata node, and before any `Lazy`.
+    /// Inside a metadata node, and before any `Lazy`s.
     /// The position is that of the node itself.
     NodeStart(NonZeroUsize),
 
-    /// Inside a metadata node, with a previous `Lazy`.
+    /// Inside a metadata node, with a previous `Lazy`s.
     /// The position is where that previous `Lazy` would start.
     Previous(NonZeroUsize),
 }
diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs
index 42759f0a652..21841ae2532 100644
--- a/compiler/rustc_metadata/src/rmeta/table.rs
+++ b/compiler/rustc_metadata/src/rmeta/table.rs
@@ -141,7 +141,7 @@ fixed_size_enum! {
     }
 }
 
-// We directly encode `DefPathHash` because a `Lazy` would encur a 25% cost.
+// We directly encode `DefPathHash` because a `LazyValue` would incur a 25% cost.
 impl FixedSizeEncoding for Option<DefPathHash> {
     type ByteArray = [u8; 16];
 
@@ -159,7 +159,7 @@ impl FixedSizeEncoding for Option<DefPathHash> {
     }
 }
 
-// We directly encode RawDefId because using a `Lazy` would incur a 50% overhead in the worst case.
+// We directly encode RawDefId because using a `LazyValue` would incur a 50% overhead in the worst case.
 impl FixedSizeEncoding for Option<RawDefId> {
     type ByteArray = [u8; 8];
 
diff --git a/compiler/rustc_typeck/src/coherence/orphan.rs b/compiler/rustc_typeck/src/coherence/orphan.rs
index 697ef7bc022..1608550aa6a 100644
--- a/compiler/rustc_typeck/src/coherence/orphan.rs
+++ b/compiler/rustc_typeck/src/coherence/orphan.rs
@@ -3,7 +3,7 @@
 
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::struct_span_err;
-use rustc_errors::ErrorGuaranteed;
+use rustc_errors::{Diagnostic, ErrorGuaranteed};
 use rustc_hir as hir;
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_middle::ty::subst::GenericArgKind;
@@ -107,6 +107,7 @@ fn do_orphan_check_impl<'tcx>(
         Err(err) => emit_orphan_check_error(
             tcx,
             sp,
+            item.span,
             tr.path.span,
             trait_ref.self_ty(),
             impl_.self_ty.span,
@@ -207,6 +208,7 @@ fn do_orphan_check_impl<'tcx>(
 fn emit_orphan_check_error<'tcx>(
     tcx: TyCtxt<'tcx>,
     sp: Span,
+    full_impl_span: Span,
     trait_span: Span,
     self_ty: Ty<'tcx>,
     self_ty_span: Span,
@@ -247,8 +249,20 @@ fn emit_orphan_check_error<'tcx>(
                     ty::Slice(_) => (this, " because slices are always foreign"),
                     ty::Array(..) => (this, " because arrays are always foreign"),
                     ty::Tuple(..) => (this, " because tuples are always foreign"),
+                    ty::RawPtr(ptr_ty) => {
+                        emit_newtype_suggestion_for_raw_ptr(
+                            full_impl_span,
+                            self_ty,
+                            self_ty_span,
+                            ptr_ty,
+                            &mut err,
+                        );
+
+                        (format!("`{}`", ty), " because raw pointers are always foreign")
+                    }
                     _ => (format!("`{}`", ty), ""),
                 };
+
                 let msg = format!("{} is not defined in the current crate{}", ty, postfix);
                 if *is_target_ty {
                     // Point at `D<A>` in `impl<A, B> for C<B> in D<A>`
@@ -330,6 +344,27 @@ fn emit_orphan_check_error<'tcx>(
     })
 }
 
+fn emit_newtype_suggestion_for_raw_ptr(
+    full_impl_span: Span,
+    self_ty: Ty<'_>,
+    self_ty_span: Span,
+    ptr_ty: &ty::TypeAndMut<'_>,
+    diag: &mut Diagnostic,
+) {
+    if !self_ty.needs_subst() {
+        let mut_key = if ptr_ty.mutbl == rustc_middle::mir::Mutability::Mut { "mut " } else { "" };
+        let msg_sugg = "consider introducing a new wrapper type".to_owned();
+        let sugg = vec![
+            (
+                full_impl_span.shrink_to_lo(),
+                format!("struct WrapperType(*{}{});\n\n", mut_key, ptr_ty.ty),
+            ),
+            (self_ty_span, "WrapperType".to_owned()),
+        ];
+        diag.multipart_suggestion(msg_sugg, sugg, rustc_errors::Applicability::MaybeIncorrect);
+    }
+}
+
 /// Lint impls of auto traits if they are likely to have
 /// unsound or surprising effects on auto impls.
 fn lint_auto_trait_impl<'tcx>(
diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs
index ee9baf811e2..59066a33c96 100644
--- a/library/core/src/ffi/c_str.rs
+++ b/library/core/src/ffi/c_str.rs
@@ -65,9 +65,9 @@ use crate::str;
 /// extern "C" { fn my_string() -> *const c_char; }
 ///
 /// fn my_string_safe() -> String {
-///     unsafe {
-///         CStr::from_ptr(my_string()).to_string_lossy().into_owned()
-///     }
+///     let cstr = unsafe { CStr::from_ptr(my_string()) };
+///     // Get copy-on-write Cow<'_, str>, then guarantee a freshly-owned String allocation
+///     String::from_utf8_lossy(cstr.to_bytes()).to_string()
 /// }
 ///
 /// println!("string: {}", my_string_safe());
diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs
index aa7cda46bda..dd2b9d59366 100644
--- a/src/bootstrap/compile.rs
+++ b/src/bootstrap/compile.rs
@@ -111,6 +111,11 @@ impl Step for Std {
 
         builder.update_submodule(&Path::new("library").join("stdarch"));
 
+        // Profiler information requires LLVM's compiler-rt
+        if builder.config.profiler {
+            builder.update_submodule(&Path::new("src/llvm-project"));
+        }
+
         let mut target_deps = builder.ensure(StartupObjects { compiler, target });
 
         let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target);
diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md
index f6348b2bddc..736c30694cd 100644
--- a/src/doc/rustc/src/SUMMARY.md
+++ b/src/doc/rustc/src/SUMMARY.md
@@ -28,6 +28,7 @@
     - [riscv32imac-unknown-xous-elf](platform-support/riscv32imac-unknown-xous-elf.md)
     - [*-pc-windows-gnullvm](platform-support/pc-windows-gnullvm.md)
     - [*-unknown-openbsd](platform-support/openbsd.md)
+    - [\*-unknown-uefi](platform-support/unknown-uefi.md)
     - [wasm64-unknown-unknown](platform-support/wasm64-unknown-unknown.md)
     - [x86_64-unknown-none](platform-support/x86_64-unknown-none.md)
 - [Targets](targets/index.md)
diff --git a/src/doc/rustc/src/platform-support/unknown-uefi.md b/src/doc/rustc/src/platform-support/unknown-uefi.md
new file mode 100644
index 00000000000..8f90d9c7453
--- /dev/null
+++ b/src/doc/rustc/src/platform-support/unknown-uefi.md
@@ -0,0 +1,254 @@
+# `*-unknown-uefi`
+
+**Tier: 3**
+
+Unified Extensible Firmware Interface (UEFI) targets for application, driver,
+and core UEFI binaries.
+
+Available targets:
+
+- `aarch64-unknown-uefi`
+- `i686-unknown-uefi`
+- `x86_64-unknown-uefi`
+
+## Target maintainers
+
+- David Rheinsberg ([@dvdhrm](https://github.com/dvdhrm))
+- Nicholas Bishop ([@nicholasbishop](https://github.com/nicholasbishop))
+
+## Requirements
+
+All UEFI targets can be used as `no-std` environments via cross-compilation.
+Support for `std` is missing, but actively worked on. `alloc` is supported if
+an allocator is provided by the user. No host tools are supported.
+
+The UEFI environment resembles the environment for Microsoft Windows, with some
+minor differences. Therefore, cross-compiling for UEFI works with the same
+tools as cross-compiling for Windows. The target binaries are PE32+ encoded,
+the calling convention is different for each architecture, but matches what
+Windows uses (if the architecture is supported by Windows). The special
+`efiapi` Rust calling-convention chooses the right ABI for the target platform
+(`extern "C"` is incorrect on Intel targets at least). The specification has an
+elaborate section on the different supported calling-conventions, if more
+details are desired.
+
+MMX, SSE, and other FP-units are disabled by default, to allow for compilation
+of core UEFI code that runs before they are set up. This can be overridden for
+individual compilations via rustc command-line flags. Not all firmwares
+correctly configure those units, though, so careful inspection is required.
+
+As native to PE32+, binaries are position-dependent, but can be relocated at
+runtime if their desired location is unavailable. The code must be statically
+linked. Dynamic linking is not supported. Code is shared via UEFI interfaces,
+rather than dynamic linking. Additionally, UEFI forbids running code on
+anything but the boot CPU/thread, nor is interrupt-usage allowed (apart from
+the timer interrupt). Device drivers are required to use polling methods.
+
+UEFI uses a single address-space to run all code in. Multiple applications can
+be loaded simultaneously and are dispatched via cooperative multitasking on a
+single stack.
+
+By default, the UEFI targets use the `link`-flavor of the LLVM linker `lld` to
+link binaries into the final PE32+ file suffixed with `*.efi`. The PE subsystem
+is set to `EFI_APPLICATION`, but can be modified by passing `/subsystem:<...>`
+to the linker. Similarly, the entry-point is to to `efi_main` but can be
+changed via `/entry:<...>`. The panic-strategy is set to `abort`,
+
+The UEFI specification is available online for free:
+[UEFI Specification Directory](https://uefi.org/specifications)
+
+## Building rust for UEFI targets
+
+Rust can be built for the UEFI targets by enabling them in the `rustc` build
+configuration. Note that you can only build the standard libraries. The
+compiler and host tools currently cannot be compiled for UEFI targets. A sample
+configuration would be:
+
+```toml
+[build]
+build-stage = 1
+target = ["x86_64-unknown-uefi"]
+```
+
+## Building Rust programs
+
+Rust does not yet ship pre-compiled artifacts for this target. To compile for
+this target, you will either need to build Rust with the target enabled (see
+"Building rust for UEFI targets" above), or build your own copy of `core` by
+using `build-std`, `cargo-buildx`, or similar.
+
+A native build with the unstable `build-std`-feature can be achieved via:
+
+```sh
+cargo +nightly build \
+    -Zbuild-std=core,compiler_builtins \
+    -Zbuild-std-features=compiler-builtins-mem \
+    --target x86_64-unknown-uefi
+```
+
+Alternatively, you can install `cargo-xbuild` via
+`cargo install --force cargo-xbuild` and build for the UEFI targets via:
+
+```sh
+cargo \
+    +nightly \
+    xbuild \
+    --target x86_64-unknown-uefi
+```
+
+## Testing
+
+UEFI applications can be copied into the ESP on any UEFI system and executed
+via the firmware boot menu. The qemu suite allows emulating UEFI systems and
+executing UEFI applications as well. See its documentation for details.
+
+The [uefi-run](https://github.com/Richard-W/uefi-run) rust tool is a simple
+wrapper around `qemu` that can spawn UEFI applications in qemu. You can install
+it via `cargo install uefi-run` and execute qemu applications as
+`uefi-run ./application.efi`.
+
+## Cross-compilation toolchains and C code
+
+There are 3 common ways to compile native C code for UEFI targets:
+
+- Use the official SDK by Intel:
+  [Tianocore/EDK2](https://github.com/tianocore/edk2). This supports a
+  multitude of platforms, comes with the full specification transposed into C,
+  lots of examples and build-system integrations. This is also the only
+  officially supported platform by Intel, and is used by many major firmware
+  implementations. Any code compiled via the SDK is compatible to rust binaries
+  compiled for the UEFI targets. You can link them directly into your rust
+  binaries, or call into each other via UEFI protocols.
+- Use the **GNU-EFI** suite. This approach is used by many UEFI applications
+  in the Linux/OSS ecosystem. The GCC compiler is used to compile ELF binaries,
+  and linked with a pre-loader that converts the ELF binary to PE32+
+  **at runtime**. You can combine such binaries with the rust UEFI targets only
+  via UEFI protocols. Linking both into the same executable will fail, since
+  one is an ELF executable, and one a PE32+. If linking to **GNU-EFI**
+  executables is desired, you must compile your rust code natively for the same
+  GNU target as **GNU-EFI** and use their pre-loader. This requires careful
+  consideration about which calling-convention to use when calling into native
+  UEFI protocols, or calling into linked **GNU-EFI** code (similar to how these
+  differences need to be accounted for when writing **GNU-EFI** C code).
+- Use native Windows targets. This means compiling your C code for the Windows
+  platform as if it was the UEFI platform. This works for static libraries, but
+  needs adjustments when linking into an UEFI executable. You can, however,
+  link such static libraries seemlessly into rust code compiled for UEFI
+  targets. Be wary of any includes that are not specifically suitable for UEFI
+  targets (especially the C standard library includes are not always
+  compatible). Freestanding compilations are recommended to avoid
+  incompatibilites.
+
+## Ecosystem
+
+The rust language has a long history of supporting UEFI targets. Many crates
+have been developed to provide access to UEFI protocols and make UEFI
+programming more ergonomic in rust. The following list is a short overview (in
+alphabetical ordering):
+
+- **efi**: *Ergonomic Rust bindings for writing UEFI applications*. Provides
+  _rustified_ access to UEFI protocols, implements allocators and a safe
+  environment to write UEFI applications.
+- **r-efi**: *UEFI Reference Specification Protocol Constants and Definitions*.
+  A pure transpose of the UEFI specification into rust. This provides the raw
+  definitions from the specification, without any extended helpers or
+  _rustification_. It serves as baseline to implement any more elaborate rust
+  UEFI layers.
+- **uefi-rs**: *Safe and easy-to-use wrapper for building UEFI apps*. An
+  elaborate library providing safe abstractions for UEFI protocols and
+  features. It implements allocators and provides an execution environment to
+  UEFI applications written in rust.
+- **uefi-run**: *Run UEFI applications*. A small wrapper around _qemu_ to spawn
+  UEFI applications in an emulated `x86_64` machine.
+
+## Example: Freestanding
+
+The following code is a valid UEFI application returning immediately upon
+execution with an exit code of 0. A panic handler is provided. This is executed
+by rust on panic. For simplicity, we simply end up in an infinite loop.
+
+Note that as of rust-1.31.0, all features used here are stabilized. No unstable
+features are required, nor do we rely on nightly compilers. However, if you do
+not compile rustc for the UEFI targets, you need a nightly compiler to support
+the `-Z build-std` flag.
+
+This example can be compiled as binary crate via `cargo`:
+
+```sh
+cargo +nightly build \
+    -Zbuild-std=core,compiler_builtins \
+    -Zbuild-std-features=compiler-builtins-mem \
+    --target x86_64-unknown-uefi
+```
+
+```rust,ignore (platform-specific,eh-personality-is-unstable)
+#![no_main]
+#![no_std]
+
+#[panic_handler]
+fn panic_handler(_info: &core::panic::PanicInfo) -> ! {
+    loop {}
+}
+
+#[export_name = "efi_main"]
+pub extern "C" fn main(_h: *mut core::ffi::c_void, _st: *mut core::ffi::c_void) -> usize {
+    0
+}
+```
+
+## Example: Hello World
+
+This is an example UEFI application that prints "Hello World!", then waits for
+key input before it exits. It serves as base example how to write UEFI
+applications without any helper modules other than the standalone UEFI protocol
+definitions provided by the `r-efi` crate.
+
+This extends the "Freestanding" example and builds upon its setup. See there
+for instruction how to compile this as binary crate.
+
+Note that UEFI uses UTF-16 strings. Since rust literals are UTF-8, we have to
+use an open-coded, zero-terminated, UTF-16 array as argument to
+`output_string()`. Similarly to the panic handler, real applications should
+rather use UTF-16 modules.
+
+```rust,ignore (platform-specific,eh-personality-is-unstable)
+#![no_main]
+#![no_std]
+
+use r_efi::efi;
+
+#[panic_handler]
+fn panic_handler(_info: &core::panic::PanicInfo) -> ! {
+    loop {}
+}
+
+#[export_name = "efi_main"]
+pub extern "C" fn main(_h: efi::Handle, st: *mut efi::SystemTable) -> efi::Status {
+    let s = [
+        0x0048u16, 0x0065u16, 0x006cu16, 0x006cu16, 0x006fu16, // "Hello"
+        0x0020u16, //                                             " "
+        0x0057u16, 0x006fu16, 0x0072u16, 0x006cu16, 0x0064u16, // "World"
+        0x0021u16, //                                             "!"
+        0x000au16, //                                             "\n"
+        0x0000u16, //                                             NUL
+    ];
+
+    // Print "Hello World!".
+    let r =
+        unsafe { ((*(*st).con_out).output_string)((*st).con_out, s.as_ptr() as *mut efi::Char16) };
+    if r.is_error() {
+        return r;
+    }
+
+    // Wait for key input, by waiting on the `wait_for_key` event hook.
+    let r = unsafe {
+        let mut x: usize = 0;
+        ((*(*st).boot_services).wait_for_event)(1, &mut (*(*st).con_in).wait_for_key, &mut x)
+    };
+    if r.is_error() {
+        return r;
+    }
+
+    efi::Status::SUCCESS
+}
+```
diff --git a/src/doc/rustc/src/targets/custom.md b/src/doc/rustc/src/targets/custom.md
index 98e113a663b..27ef2f49eee 100644
--- a/src/doc/rustc/src/targets/custom.md
+++ b/src/doc/rustc/src/targets/custom.md
@@ -5,13 +5,13 @@ If you'd like to build for a target that is not yet supported by `rustc`, you ca
 are JSON. To see the JSON for the host target, you can run:
 
 ```bash
-$ rustc +nightly -Z unstable-options --print target-spec-json
+rustc +nightly -Z unstable-options --print target-spec-json
 ```
 
 To see it for a different target, add the `--target` flag:
 
 ```bash
-$ rustc +nightly -Z unstable-options --target=wasm32-unknown-unknown --print target-spec-json
+rustc +nightly -Z unstable-options --target=wasm32-unknown-unknown --print target-spec-json
 ```
 
 To use a custom target, see the (unstable) [`build-std` feature](https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#build-std) of `cargo`.
diff --git a/src/etc/htmldocck.py b/src/etc/htmldocck.py
index 70b6af717cd..d02ac9d9c0a 100644
--- a/src/etc/htmldocck.py
+++ b/src/etc/htmldocck.py
@@ -440,6 +440,7 @@ def check_snapshot(snapshot_name, actual_tree, normalize_to_text):
 
         if bless:
             with open(snapshot_path, 'w') as snapshot_file:
+                actual_str = actual_str.replace(channel, "{{channel}}")
                 snapshot_file.write(actual_str)
         else:
             print('--- expected ---\n')
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index 4b91f7ba096..731d8766686 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -17,7 +17,8 @@ use rustc_span::symbol::{kw, sym, Symbol};
 
 use crate::clean::{
     self, clean_fn_decl_from_did_and_sig, clean_middle_field, clean_middle_ty, clean_ty,
-    clean_ty_generics, utils, Attributes, AttributesExt, Clean, ImplKind, ItemId, Type, Visibility,
+    clean_ty_generics, clean_visibility, utils, Attributes, AttributesExt, Clean, ImplKind, ItemId,
+    Type, Visibility,
 };
 use crate::core::DocContext;
 use crate::formats::item_type::ItemType;
@@ -134,7 +135,7 @@ pub(crate) fn try_inline(
     );
     if let Some(import_def_id) = import_def_id {
         // The visibility needs to reflect the one from the reexport and not from the "source" DefId.
-        item.visibility = cx.tcx.visibility(import_def_id).clean(cx);
+        item.visibility = clean_visibility(cx.tcx.visibility(import_def_id));
     }
     ret.push(item);
     Some(ret)
@@ -599,7 +600,7 @@ fn build_macro(
     match CStore::from_tcx(cx.tcx).load_macro_untracked(def_id, cx.sess()) {
         LoadedMacro::MacroDef(item_def, _) => {
             if let ast::ItemKind::MacroDef(ref def) = item_def.kind {
-                let vis = cx.tcx.visibility(import_def_id.unwrap_or(def_id)).clean(cx);
+                let vis = clean_visibility(cx.tcx.visibility(import_def_id.unwrap_or(def_id)));
                 clean::MacroItem(clean::Macro {
                     source: utils::display_macro_source(cx, name, def, def_id, vis),
                 })
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 2f2fbc9d4ba..624eec57e83 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -1812,32 +1812,25 @@ fn is_field_vis_inherited(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
     }
 }
 
-impl<'tcx> Clean<'tcx, Visibility> for ty::Visibility {
-    fn clean(&self, _cx: &mut DocContext<'_>) -> Visibility {
-        match *self {
-            ty::Visibility::Public => Visibility::Public,
-            // NOTE: this is not quite right: `ty` uses `Invisible` to mean 'private',
-            // while rustdoc really does mean inherited. That means that for enum variants, such as
-            // `pub enum E { V }`, `V` will be marked as `Public` by `ty`, but as `Inherited` by rustdoc.
-            // Various parts of clean override `tcx.visibility` explicitly to make sure this distinction is captured.
-            ty::Visibility::Invisible => Visibility::Inherited,
-            ty::Visibility::Restricted(module) => Visibility::Restricted(module),
-        }
-    }
-}
-
-impl<'tcx> Clean<'tcx, VariantStruct> for rustc_hir::VariantData<'tcx> {
-    fn clean(&self, cx: &mut DocContext<'tcx>) -> VariantStruct {
-        VariantStruct {
-            struct_type: CtorKind::from_hir(self),
-            fields: self.fields().iter().map(|x| clean_field(x, cx)).collect(),
-        }
+pub(crate) fn clean_visibility(vis: ty::Visibility) -> Visibility {
+    match vis {
+        ty::Visibility::Public => Visibility::Public,
+        // NOTE: this is not quite right: `ty` uses `Invisible` to mean 'private',
+        // while rustdoc really does mean inherited. That means that for enum variants, such as
+        // `pub enum E { V }`, `V` will be marked as `Public` by `ty`, but as `Inherited` by rustdoc.
+        // Various parts of clean override `tcx.visibility` explicitly to make sure this distinction is captured.
+        ty::Visibility::Invisible => Visibility::Inherited,
+        ty::Visibility::Restricted(module) => Visibility::Restricted(module),
     }
 }
 
-impl<'tcx> Clean<'tcx, Vec<Item>> for hir::VariantData<'tcx> {
-    fn clean(&self, cx: &mut DocContext<'tcx>) -> Vec<Item> {
-        self.fields().iter().map(|x| clean_field(x, cx)).collect()
+fn clean_variant_data<'tcx>(
+    variant: &hir::VariantData<'tcx>,
+    cx: &mut DocContext<'tcx>,
+) -> VariantStruct {
+    VariantStruct {
+        struct_type: CtorKind::from_hir(variant),
+        fields: variant.fields().iter().map(|x| clean_field(x, cx)).collect(),
     }
 }
 
@@ -1863,8 +1856,10 @@ impl<'tcx> Clean<'tcx, Item> for ty::VariantDef {
 impl<'tcx> Clean<'tcx, Variant> for hir::VariantData<'tcx> {
     fn clean(&self, cx: &mut DocContext<'tcx>) -> Variant {
         match self {
-            hir::VariantData::Struct(..) => Variant::Struct(self.clean(cx)),
-            hir::VariantData::Tuple(..) => Variant::Tuple(self.clean(cx)),
+            hir::VariantData::Struct(..) => Variant::Struct(clean_variant_data(self, cx)),
+            hir::VariantData::Tuple(..) => {
+                Variant::Tuple(self.fields().iter().map(|x| clean_field(x, cx)).collect())
+            }
             hir::VariantData::Unit(..) => Variant::CLike,
         }
     }
@@ -1983,7 +1978,7 @@ fn clean_maybe_renamed_item<'tcx>(
                 clean_fn_or_proc_macro(item, sig, generics, body_id, &mut name, cx)
             }
             ItemKind::Macro(ref macro_def, _) => {
-                let ty_vis = cx.tcx.visibility(def_id).clean(cx);
+                let ty_vis = clean_visibility(cx.tcx.visibility(def_id));
                 MacroItem(Macro {
                     source: display_macro_source(cx, name, macro_def, def_id, ty_vis),
                 })
@@ -2112,7 +2107,7 @@ fn clean_extern_crate<'tcx>(
         name: Some(name),
         attrs: Box::new(attrs.clean(cx)),
         item_id: crate_def_id.into(),
-        visibility: ty_vis.clean(cx),
+        visibility: clean_visibility(ty_vis),
         kind: box ExternCrateItem { src: orig_name },
         cfg: attrs.cfg(cx.tcx, &cx.cache.hidden_cfg),
     }]
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 83d8ed3fc87..a5d27a94034 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -37,7 +37,7 @@ use crate::clean::cfg::Cfg;
 use crate::clean::external_path;
 use crate::clean::inline::{self, print_inlined_const};
 use crate::clean::utils::{is_literal_expr, print_const_expr, print_evaluated_const};
-use crate::clean::Clean;
+use crate::clean::{clean_visibility, Clean};
 use crate::core::DocContext;
 use crate::formats::cache::Cache;
 use crate::formats::item_type::ItemType;
@@ -499,7 +499,7 @@ impl Item {
         let visibility = if matches!(&kind, ItemKind::KeywordItem | ItemKind::PrimitiveItem(..)) {
             Visibility::Public
         } else {
-            cx.tcx.visibility(def_id).clean(cx)
+            clean_visibility(cx.tcx.visibility(def_id))
         };
 
         Item { item_id: def_id.into(), kind: box kind, name, attrs, visibility, cfg }
diff --git a/src/test/ui/errors/issue-99572-impl-trait-on-pointer.rs b/src/test/ui/errors/issue-99572-impl-trait-on-pointer.rs
new file mode 100644
index 00000000000..272c6bd3fb7
--- /dev/null
+++ b/src/test/ui/errors/issue-99572-impl-trait-on-pointer.rs
@@ -0,0 +1,25 @@
+// Emit additional suggestion to correct the trait implementation
+// on a pointer
+use std::{fmt, marker};
+
+struct LocalType;
+
+impl fmt::Display for *mut LocalType {
+//~^ ERROR only traits defined in the current crate can be implemented for arbitrary types
+//~| NOTE impl doesn't use only types from inside the current crate
+//~| NOTE `*mut LocalType` is not defined in the current crate because raw pointers are always foreign
+//~| NOTE define and implement a trait or new type instead
+//~| HELP consider introducing a new wrapper type
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "This not compile")
+    }
+}
+
+impl<T> marker::Copy for *mut T {
+//~^ ERROR only traits defined in the current crate can be implemented for arbitrary types
+//~| NOTE impl doesn't use only types from inside the current crate
+//~| NOTE `*mut T` is not defined in the current crate because raw pointers are always foreign
+//~| NOTE define and implement a trait or new type instead
+}
+
+fn main() {}
diff --git a/src/test/ui/errors/issue-99572-impl-trait-on-pointer.stderr b/src/test/ui/errors/issue-99572-impl-trait-on-pointer.stderr
new file mode 100644
index 00000000000..78d7a47deaa
--- /dev/null
+++ b/src/test/ui/errors/issue-99572-impl-trait-on-pointer.stderr
@@ -0,0 +1,31 @@
+error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+  --> $DIR/issue-99572-impl-trait-on-pointer.rs:7:1
+   |
+LL | impl fmt::Display for *mut LocalType {
+   | ^^^^^^^^^^^^^^^^^^^^^^--------------
+   | |                     |
+   | |                     `*mut LocalType` is not defined in the current crate because raw pointers are always foreign
+   | impl doesn't use only types from inside the current crate
+   |
+   = note: define and implement a trait or new type instead
+help: consider introducing a new wrapper type
+   |
+LL + struct WrapperType(*mut LocalType);
+LL + 
+LL ~ impl fmt::Display for WrapperType {
+   |
+
+error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+  --> $DIR/issue-99572-impl-trait-on-pointer.rs:18:1
+   |
+LL | impl<T> marker::Copy for *mut T {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^------
+   | |                        |
+   | |                        `*mut T` is not defined in the current crate because raw pointers are always foreign
+   | impl doesn't use only types from inside the current crate
+   |
+   = note: define and implement a trait or new type instead
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0117`.