From 69fe39e8a8e6a66d97836e3068a4b1f183c79ea6 Mon Sep 17 00:00:00 2001
From: Joshua Nelson <jyn514@gmail.com>
Date: Mon, 5 Jul 2021 22:50:26 -0400
Subject: [PATCH 1/5] Add primitive documentation to libcore

This works by doing two things:
- Adding links that are specific to the crate. Since not all primitive
  items are defined in `core` (due to lang_items), these need to use
  relative links and not intra-doc links.
- Duplicating `primitive_docs` in both core and std. This allows not needing CARGO_PKG_NAME to build the standard library. It also adds a tidy check to make sure they stay the same.
---
 library/core/primitive_docs/box_into_raw.md   |    1 +
 library/core/primitive_docs/fs_file.md        |    1 +
 library/core/primitive_docs/io_bufread.md     |    1 +
 library/core/primitive_docs/io_read.md        |    1 +
 library/core/primitive_docs/io_seek.md        |    1 +
 library/core/primitive_docs/io_write.md       |    1 +
 .../core/primitive_docs/net_tosocketaddrs.md  |    1 +
 library/core/primitive_docs/process_exit.md   |    1 +
 library/core/primitive_docs/string_string.md  |    1 +
 library/core/src/bool.rs                      |    6 +-
 library/core/src/char/methods.rs              |   12 +-
 library/core/src/lib.rs                       |    3 +
 library/core/src/primitive_docs.rs            | 1307 +++++++++++++++++
 library/std/primitive_docs/box_into_raw.md    |    1 +
 library/std/primitive_docs/fs_file.md         |    1 +
 library/std/primitive_docs/io_bufread.md      |    1 +
 library/std/primitive_docs/io_read.md         |    1 +
 library/std/primitive_docs/io_seek.md         |    1 +
 library/std/primitive_docs/io_write.md        |    1 +
 .../std/primitive_docs/net_tosocketaddrs.md   |    1 +
 library/std/primitive_docs/process_exit.md    |    1 +
 library/std/primitive_docs/string_string.md   |    1 +
 library/std/src/primitive_docs.rs             |   46 +-
 src/test/rustdoc/auxiliary/primitive-doc.rs   |    3 +
 src/test/rustdoc/cross-crate-primitive-doc.rs |    6 +-
 src/test/rustdoc/primitive/no_std.rs          |    5 +
 src/tools/tidy/src/lib.rs                     |    1 +
 src/tools/tidy/src/main.rs                    |    1 +
 src/tools/tidy/src/primitive_docs.rs          |   17 +
 29 files changed, 1394 insertions(+), 31 deletions(-)
 create mode 100644 library/core/primitive_docs/box_into_raw.md
 create mode 100644 library/core/primitive_docs/fs_file.md
 create mode 100644 library/core/primitive_docs/io_bufread.md
 create mode 100644 library/core/primitive_docs/io_read.md
 create mode 100644 library/core/primitive_docs/io_seek.md
 create mode 100644 library/core/primitive_docs/io_write.md
 create mode 100644 library/core/primitive_docs/net_tosocketaddrs.md
 create mode 100644 library/core/primitive_docs/process_exit.md
 create mode 100644 library/core/primitive_docs/string_string.md
 create mode 100644 library/core/src/primitive_docs.rs
 create mode 100644 library/std/primitive_docs/box_into_raw.md
 create mode 100644 library/std/primitive_docs/fs_file.md
 create mode 100644 library/std/primitive_docs/io_bufread.md
 create mode 100644 library/std/primitive_docs/io_read.md
 create mode 100644 library/std/primitive_docs/io_seek.md
 create mode 100644 library/std/primitive_docs/io_write.md
 create mode 100644 library/std/primitive_docs/net_tosocketaddrs.md
 create mode 100644 library/std/primitive_docs/process_exit.md
 create mode 100644 library/std/primitive_docs/string_string.md
 create mode 100644 src/test/rustdoc/primitive/no_std.rs
 create mode 100644 src/tools/tidy/src/primitive_docs.rs

diff --git a/library/core/primitive_docs/box_into_raw.md b/library/core/primitive_docs/box_into_raw.md
new file mode 100644
index 0000000000000..9dd0344c7c7b1
--- /dev/null
+++ b/library/core/primitive_docs/box_into_raw.md
@@ -0,0 +1 @@
+../std/boxed/struct.Box.html#method.into_raw
diff --git a/library/core/primitive_docs/fs_file.md b/library/core/primitive_docs/fs_file.md
new file mode 100644
index 0000000000000..4023e340a5182
--- /dev/null
+++ b/library/core/primitive_docs/fs_file.md
@@ -0,0 +1 @@
+../std/fs/struct.File.html
diff --git a/library/core/primitive_docs/io_bufread.md b/library/core/primitive_docs/io_bufread.md
new file mode 100644
index 0000000000000..7beda2cd39085
--- /dev/null
+++ b/library/core/primitive_docs/io_bufread.md
@@ -0,0 +1 @@
+../std/io/trait.BufRead.html
diff --git a/library/core/primitive_docs/io_read.md b/library/core/primitive_docs/io_read.md
new file mode 100644
index 0000000000000..b7ecf5e273cea
--- /dev/null
+++ b/library/core/primitive_docs/io_read.md
@@ -0,0 +1 @@
+../std/io/trait.Read.html
diff --git a/library/core/primitive_docs/io_seek.md b/library/core/primitive_docs/io_seek.md
new file mode 100644
index 0000000000000..db0274d291c6f
--- /dev/null
+++ b/library/core/primitive_docs/io_seek.md
@@ -0,0 +1 @@
+../std/io/trait.Seek.html
diff --git a/library/core/primitive_docs/io_write.md b/library/core/primitive_docs/io_write.md
new file mode 100644
index 0000000000000..92a3b88a79c9c
--- /dev/null
+++ b/library/core/primitive_docs/io_write.md
@@ -0,0 +1 @@
+../std/io/trait.Write.html
diff --git a/library/core/primitive_docs/net_tosocketaddrs.md b/library/core/primitive_docs/net_tosocketaddrs.md
new file mode 100644
index 0000000000000..4daa10ddbe2b2
--- /dev/null
+++ b/library/core/primitive_docs/net_tosocketaddrs.md
@@ -0,0 +1 @@
+../std/net/trait.ToSocketAddrs.html
diff --git a/library/core/primitive_docs/process_exit.md b/library/core/primitive_docs/process_exit.md
new file mode 100644
index 0000000000000..cae34d12d5249
--- /dev/null
+++ b/library/core/primitive_docs/process_exit.md
@@ -0,0 +1 @@
+../std/process/fn.exit.html
diff --git a/library/core/primitive_docs/string_string.md b/library/core/primitive_docs/string_string.md
new file mode 100644
index 0000000000000..303dc07b1855d
--- /dev/null
+++ b/library/core/primitive_docs/string_string.md
@@ -0,0 +1 @@
+../std/string/struct.String.html
diff --git a/library/core/src/bool.rs b/library/core/src/bool.rs
index ca1c0ae759892..f14c2a4641627 100644
--- a/library/core/src/bool.rs
+++ b/library/core/src/bool.rs
@@ -2,7 +2,8 @@
 
 #[lang = "bool"]
 impl bool {
-    /// Returns `Some(t)` if the `bool` is [`true`](keyword.true.html), or `None` otherwise.
+    /// Returns `Some(t)` if the `bool` is [`true`](../std/keyword.true.html),
+    /// or `None` otherwise.
     ///
     /// # Examples
     ///
@@ -18,7 +19,8 @@ impl bool {
         if self { Some(t) } else { None }
     }
 
-    /// Returns `Some(f())` if the `bool` is [`true`](keyword.true.html), or `None` otherwise.
+    /// Returns `Some(f())` if the `bool` is [`true`](../std/keyword.true.html),
+    /// or `None` otherwise.
     ///
     /// # Examples
     ///
diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs
index e6d3ac8f2d2c5..07ffd06511885 100644
--- a/library/core/src/char/methods.rs
+++ b/library/core/src/char/methods.rs
@@ -96,7 +96,7 @@ impl char {
     /// Converts a `u32` to a `char`.
     ///
     /// Note that all `char`s are valid [`u32`]s, and can be cast to one with
-    /// [`as`](keyword.as.html):
+    /// [`as`](../std/keyword.as.html):
     ///
     /// ```
     /// let c = 'šŸ’Æ';
@@ -372,7 +372,7 @@ impl char {
     /// println!("\\u{{2764}}");
     /// ```
     ///
-    /// Using [`to_string`](string/trait.ToString.html#tymethod.to_string):
+    /// Using [`to_string`](../std/string/trait.ToString.html#tymethod.to_string):
     ///
     /// ```
     /// assert_eq!('ā¤'.escape_unicode().to_string(), "\\u{2764}");
@@ -448,7 +448,7 @@ impl char {
     /// println!("\\n");
     /// ```
     ///
-    /// Using [`to_string`](string/trait.ToString.html#tymethod.to_string):
+    /// Using [`to_string`](../std/string/trait.ToString.html#tymethod.to_string):
     ///
     /// ```
     /// assert_eq!('\n'.escape_debug().to_string(), "\\n");
@@ -502,7 +502,7 @@ impl char {
     /// println!("\\\"");
     /// ```
     ///
-    /// Using [`to_string`](string/trait.ToString.html#tymethod.to_string):
+    /// Using [`to_string`](../std/string/trait.ToString.html#tymethod.to_string):
     ///
     /// ```
     /// assert_eq!('"'.escape_default().to_string(), "\\\"");
@@ -937,7 +937,7 @@ impl char {
     /// println!("i\u{307}");
     /// ```
     ///
-    /// Using [`to_string`](string/trait.ToString.html#tymethod.to_string):
+    /// Using [`to_string`](../std/string/trait.ToString.html#tymethod.to_string):
     ///
     /// ```
     /// assert_eq!('C'.to_lowercase().to_string(), "c");
@@ -1002,7 +1002,7 @@ impl char {
     /// println!("SS");
     /// ```
     ///
-    /// Using [`to_string`](string/trait.ToString.html#tymethod.to_string):
+    /// Using [`to_string`](../std/string/trait.ToString.html#tymethod.to_string):
     ///
     /// ```
     /// assert_eq!('c'.to_uppercase().to_string(), "C");
diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs
index 06a409c6a2eb9..265ba9f1bb91b 100644
--- a/library/core/src/lib.rs
+++ b/library/core/src/lib.rs
@@ -130,6 +130,7 @@
 #![feature(decl_macro)]
 #![feature(doc_cfg)]
 #![feature(doc_notable_trait)]
+#![feature(doc_primitive)]
 #![feature(exhaustive_patterns)]
 #![feature(extern_types)]
 #![feature(fundamental)]
@@ -355,3 +356,5 @@ pub mod arch {
         /* compiler built-in */
     }
 }
+
+include!("primitive_docs.rs");
diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs
new file mode 100644
index 0000000000000..0de9126dab2fe
--- /dev/null
+++ b/library/core/src/primitive_docs.rs
@@ -0,0 +1,1307 @@
+// `library/{std,core}/src/primitive_docs.rs` should have the same contents.
+// These are different files so that relative links work properly without
+// having to have `CARGO_PKG_NAME` set, but conceptually they should always be the same.
+#[doc(primitive = "bool")]
+#[doc(alias = "true")]
+#[doc(alias = "false")]
+/// The boolean type.
+///
+/// The `bool` represents a value, which could only be either [`true`] or [`false`]. If you cast
+/// a `bool` into an integer, [`true`] will be 1 and [`false`] will be 0.
+///
+/// # Basic usage
+///
+/// `bool` implements various traits, such as [`BitAnd`], [`BitOr`], [`Not`], etc.,
+/// which allow us to perform boolean operations using `&`, `|` and `!`.
+///
+/// [`if`] requires a `bool` value as its conditional. [`assert!`], which is an
+/// important macro in testing, checks whether an expression is [`true`] and panics
+/// if it isn't.
+///
+/// ```
+/// let bool_val = true & false | false;
+/// assert!(!bool_val);
+/// ```
+///
+/// [`true`]: ../std/keyword.true.html
+/// [`false`]: ../std/keyword.false.html
+/// [`BitAnd`]: ops::BitAnd
+/// [`BitOr`]: ops::BitOr
+/// [`Not`]: ops::Not
+/// [`if`]: ../std/keyword.if.html
+///
+/// # Examples
+///
+/// A trivial example of the usage of `bool`:
+///
+/// ```
+/// let praise_the_borrow_checker = true;
+///
+/// // using the `if` conditional
+/// if praise_the_borrow_checker {
+///     println!("oh, yeah!");
+/// } else {
+///     println!("what?!!");
+/// }
+///
+/// // ... or, a match pattern
+/// match praise_the_borrow_checker {
+///     true => println!("keep praising!"),
+///     false => println!("you should praise!"),
+/// }
+/// ```
+///
+/// Also, since `bool` implements the [`Copy`] trait, we don't
+/// have to worry about the move semantics (just like the integer and float primitives).
+///
+/// Now an example of `bool` cast to integer type:
+///
+/// ```
+/// assert_eq!(true as i32, 1);
+/// assert_eq!(false as i32, 0);
+/// ```
+#[stable(feature = "rust1", since = "1.0.0")]
+mod prim_bool {}
+
+#[doc(primitive = "never")]
+#[doc(alias = "!")]
+//
+/// The `!` type, also called "never".
+///
+/// `!` represents the type of computations which never resolve to any value at all. For example,
+/// the [`exit`] function `fn exit(code: i32) -> !` exits the process without ever returning, and
+/// so returns `!`.
+///
+/// `break`, `continue` and `return` expressions also have type `!`. For example we are allowed to
+/// write:
+///
+/// ```
+/// #![feature(never_type)]
+/// # fn foo() -> u32 {
+/// let x: ! = {
+///     return 123
+/// };
+/// # }
+/// ```
+///
+/// Although the `let` is pointless here, it illustrates the meaning of `!`. Since `x` is never
+/// assigned a value (because `return` returns from the entire function), `x` can be given type
+/// `!`. We could also replace `return 123` with a `panic!` or a never-ending `loop` and this code
+/// would still be valid.
+///
+/// A more realistic usage of `!` is in this code:
+///
+/// ```
+/// # fn get_a_number() -> Option<u32> { None }
+/// # loop {
+/// let num: u32 = match get_a_number() {
+///     Some(num) => num,
+///     None => break,
+/// };
+/// # }
+/// ```
+///
+/// Both match arms must produce values of type [`u32`], but since `break` never produces a value
+/// at all we know it can never produce a value which isn't a [`u32`]. This illustrates another
+/// behaviour of the `!` type - expressions with type `!` will coerce into any other type.
+///
+/// [`u32`]: prim@u32
+#[doc = concat!("[`exit`]: ", include_str!("../primitive_docs/process_exit.md"))]
+///
+/// # `!` and generics
+///
+/// ## Infallible errors
+///
+/// The main place you'll see `!` used explicitly is in generic code. Consider the [`FromStr`]
+/// trait:
+///
+/// ```
+/// trait FromStr: Sized {
+///     type Err;
+///     fn from_str(s: &str) -> Result<Self, Self::Err>;
+/// }
+/// ```
+///
+/// When implementing this trait for [`String`] we need to pick a type for [`Err`]. And since
+/// converting a string into a string will never result in an error, the appropriate type is `!`.
+/// (Currently the type actually used is an enum with no variants, though this is only because `!`
+/// was added to Rust at a later date and it may change in the future.) With an [`Err`] type of
+/// `!`, if we have to call [`String::from_str`] for some reason the result will be a
+/// [`Result<String, !>`] which we can unpack like this:
+///
+/// ```
+/// #![feature(exhaustive_patterns)]
+/// use std::str::FromStr;
+/// let Ok(s) = String::from_str("hello");
+/// ```
+///
+/// Since the [`Err`] variant contains a `!`, it can never occur. If the `exhaustive_patterns`
+/// feature is present this means we can exhaustively match on [`Result<T, !>`] by just taking the
+/// [`Ok`] variant. This illustrates another behaviour of `!` - it can be used to "delete" certain
+/// enum variants from generic types like `Result`.
+///
+/// ## Infinite loops
+///
+/// While [`Result<T, !>`] is very useful for removing errors, `!` can also be used to remove
+/// successes as well. If we think of [`Result<T, !>`] as "if this function returns, it has not
+/// errored," we get a very intuitive idea of [`Result<!, E>`] as well: if the function returns, it
+/// *has* errored.
+///
+/// For example, consider the case of a simple web server, which can be simplified to:
+///
+/// ```ignore (hypothetical-example)
+/// loop {
+///     let (client, request) = get_request().expect("disconnected");
+///     let response = request.process();
+///     response.send(client);
+/// }
+/// ```
+///
+/// Currently, this isn't ideal, because we simply panic whenever we fail to get a new connection.
+/// Instead, we'd like to keep track of this error, like this:
+///
+/// ```ignore (hypothetical-example)
+/// loop {
+///     match get_request() {
+///         Err(err) => break err,
+///         Ok((client, request)) => {
+///             let response = request.process();
+///             response.send(client);
+///         },
+///     }
+/// }
+/// ```
+///
+/// Now, when the server disconnects, we exit the loop with an error instead of panicking. While it
+/// might be intuitive to simply return the error, we might want to wrap it in a [`Result<!, E>`]
+/// instead:
+///
+/// ```ignore (hypothetical-example)
+/// fn server_loop() -> Result<!, ConnectionError> {
+///     loop {
+///         let (client, request) = get_request()?;
+///         let response = request.process();
+///         response.send(client);
+///     }
+/// }
+/// ```
+///
+/// Now, we can use `?` instead of `match`, and the return type makes a lot more sense: if the loop
+/// ever stops, it means that an error occurred. We don't even have to wrap the loop in an `Ok`
+/// because `!` coerces to `Result<!, ConnectionError>` automatically.
+///
+/// [`String::from_str`]: str::FromStr::from_str
+#[doc = concat!("[`String`]: ", include_str!("../primitive_docs/string_string.md"))]
+/// [`FromStr`]: str::FromStr
+///
+/// # `!` and traits
+///
+/// When writing your own traits, `!` should have an `impl` whenever there is an obvious `impl`
+/// which doesn't `panic!`. The reason is that functions returning an `impl Trait` where `!`
+/// does not have an `impl` of `Trait` cannot diverge as their only possible code path. In other
+/// words, they can't return `!` from every code path. As an example, this code doesn't compile:
+///
+/// ```compile_fail
+/// use std::ops::Add;
+///
+/// fn foo() -> impl Add<u32> {
+///     unimplemented!()
+/// }
+/// ```
+///
+/// But this code does:
+///
+/// ```
+/// use std::ops::Add;
+///
+/// fn foo() -> impl Add<u32> {
+///     if true {
+///         unimplemented!()
+///     } else {
+///         0
+///     }
+/// }
+/// ```
+///
+/// The reason is that, in the first example, there are many possible types that `!` could coerce
+/// to, because many types implement `Add<u32>`. However, in the second example,
+/// the `else` branch returns a `0`, which the compiler infers from the return type to be of type
+/// `u32`. Since `u32` is a concrete type, `!` can and will be coerced to it. See issue [#36375]
+/// for more information on this quirk of `!`.
+///
+/// [#36375]: https://github.com/rust-lang/rust/issues/36375
+///
+/// As it turns out, though, most traits can have an `impl` for `!`. Take [`Debug`]
+/// for example:
+///
+/// ```
+/// #![feature(never_type)]
+/// # use std::fmt;
+/// # trait Debug {
+/// #     fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result;
+/// # }
+/// impl Debug for ! {
+///     fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+///         *self
+///     }
+/// }
+/// ```
+///
+/// Once again we're using `!`'s ability to coerce into any other type, in this case
+/// [`fmt::Result`]. Since this method takes a `&!` as an argument we know that it can never be
+/// called (because there is no value of type `!` for it to be called with). Writing `*self`
+/// essentially tells the compiler "We know that this code can never be run, so just treat the
+/// entire function body as having type [`fmt::Result`]". This pattern can be used a lot when
+/// implementing traits for `!`. Generally, any trait which only has methods which take a `self`
+/// parameter should have such an impl.
+///
+/// On the other hand, one trait which would not be appropriate to implement is [`Default`]:
+///
+/// ```
+/// trait Default {
+///     fn default() -> Self;
+/// }
+/// ```
+///
+/// Since `!` has no values, it has no default value either. It's true that we could write an
+/// `impl` for this which simply panics, but the same is true for any type (we could `impl
+/// Default` for (eg.) [`File`] by just making [`default()`] panic.)
+///
+#[doc = concat!("[`File`]: ", include_str!("../primitive_docs/fs_file.md"))]
+/// [`Debug`]: fmt::Debug
+/// [`default()`]: Default::default
+///
+#[unstable(feature = "never_type", issue = "35121")]
+mod prim_never {}
+
+#[doc(primitive = "char")]
+/// A character type.
+///
+/// The `char` type represents a single character. More specifically, since
+/// 'character' isn't a well-defined concept in Unicode, `char` is a '[Unicode
+/// scalar value]', which is similar to, but not the same as, a '[Unicode code
+/// point]'.
+///
+/// [Unicode scalar value]: https://www.unicode.org/glossary/#unicode_scalar_value
+/// [Unicode code point]: https://www.unicode.org/glossary/#code_point
+///
+/// This documentation describes a number of methods and trait implementations on the
+/// `char` type. For technical reasons, there is additional, separate
+/// documentation in [the `std::char` module](char/index.html) as well.
+///
+/// # Representation
+///
+/// `char` is always four bytes in size. This is a different representation than
+/// a given character would have as part of a [`String`]. For example:
+///
+/// ```
+/// let v = vec!['h', 'e', 'l', 'l', 'o'];
+///
+/// // five elements times four bytes for each element
+/// assert_eq!(20, v.len() * std::mem::size_of::<char>());
+///
+/// let s = String::from("hello");
+///
+/// // five elements times one byte per element
+/// assert_eq!(5, s.len() * std::mem::size_of::<u8>());
+/// ```
+///
+#[doc = concat!("[`String`]: ", include_str!("../primitive_docs/string_string.md"))]
+///
+/// As always, remember that a human intuition for 'character' might not map to
+/// Unicode's definitions. For example, despite looking similar, the 'Ć©'
+/// character is one Unicode code point while 'é' is two Unicode code points:
+///
+/// ```
+/// let mut chars = "Ć©".chars();
+/// // U+00e9: 'latin small letter e with acute'
+/// assert_eq!(Some('\u{00e9}'), chars.next());
+/// assert_eq!(None, chars.next());
+///
+/// let mut chars = "é".chars();
+/// // U+0065: 'latin small letter e'
+/// assert_eq!(Some('\u{0065}'), chars.next());
+/// // U+0301: 'combining acute accent'
+/// assert_eq!(Some('\u{0301}'), chars.next());
+/// assert_eq!(None, chars.next());
+/// ```
+///
+/// This means that the contents of the first string above _will_ fit into a
+/// `char` while the contents of the second string _will not_. Trying to create
+/// a `char` literal with the contents of the second string gives an error:
+///
+/// ```text
+/// error: character literal may only contain one codepoint: 'é'
+/// let c = 'é';
+///         ^^^
+/// ```
+///
+/// Another implication of the 4-byte fixed size of a `char` is that
+/// per-`char` processing can end up using a lot more memory:
+///
+/// ```
+/// let s = String::from("love: ā¤ļø");
+/// let v: Vec<char> = s.chars().collect();
+///
+/// assert_eq!(12, std::mem::size_of_val(&s[..]));
+/// assert_eq!(32, std::mem::size_of_val(&v[..]));
+/// ```
+#[stable(feature = "rust1", since = "1.0.0")]
+mod prim_char {}
+
+#[doc(primitive = "unit")]
+#[doc(alias = "(")]
+#[doc(alias = ")")]
+#[doc(alias = "()")]
+//
+/// The `()` type, also called "unit".
+///
+/// The `()` type has exactly one value `()`, and is used when there
+/// is no other meaningful value that could be returned. `()` is most
+/// commonly seen implicitly: functions without a `-> ...` implicitly
+/// have return type `()`, that is, these are equivalent:
+///
+/// ```rust
+/// fn long() -> () {}
+///
+/// fn short() {}
+/// ```
+///
+/// The semicolon `;` can be used to discard the result of an
+/// expression at the end of a block, making the expression (and thus
+/// the block) evaluate to `()`. For example,
+///
+/// ```rust
+/// fn returns_i64() -> i64 {
+///     1i64
+/// }
+/// fn returns_unit() {
+///     1i64;
+/// }
+///
+/// let is_i64 = {
+///     returns_i64()
+/// };
+/// let is_unit = {
+///     returns_i64();
+/// };
+/// ```
+///
+#[stable(feature = "rust1", since = "1.0.0")]
+mod prim_unit {}
+
+#[doc(primitive = "pointer")]
+#[doc(alias = "ptr")]
+#[doc(alias = "*")]
+#[doc(alias = "*const")]
+#[doc(alias = "*mut")]
+//
+/// Raw, unsafe pointers, `*const T`, and `*mut T`.
+///
+/// *[See also the `std::ptr` module](ptr).*
+///
+/// Working with raw pointers in Rust is uncommon, typically limited to a few patterns.
+/// Raw pointers can be unaligned or [`null`]. However, when a raw pointer is
+/// dereferenced (using the `*` operator), it must be non-null and aligned.
+///
+/// Storing through a raw pointer using `*ptr = data` calls `drop` on the old value, so
+/// [`write`] must be used if the type has drop glue and memory is not already
+/// initialized - otherwise `drop` would be called on the uninitialized memory.
+///
+/// Use the [`null`] and [`null_mut`] functions to create null pointers, and the
+/// [`is_null`] method of the `*const T` and `*mut T` types to check for null.
+/// The `*const T` and `*mut T` types also define the [`offset`] method, for
+/// pointer math.
+///
+/// # Common ways to create raw pointers
+///
+/// ## 1. Coerce a reference (`&T`) or mutable reference (`&mut T`).
+///
+/// ```
+/// let my_num: i32 = 10;
+/// let my_num_ptr: *const i32 = &my_num;
+/// let mut my_speed: i32 = 88;
+/// let my_speed_ptr: *mut i32 = &mut my_speed;
+/// ```
+///
+/// To get a pointer to a boxed value, dereference the box:
+///
+/// ```
+/// let my_num: Box<i32> = Box::new(10);
+/// let my_num_ptr: *const i32 = &*my_num;
+/// let mut my_speed: Box<i32> = Box::new(88);
+/// let my_speed_ptr: *mut i32 = &mut *my_speed;
+/// ```
+///
+/// This does not take ownership of the original allocation
+/// and requires no resource management later,
+/// but you must not use the pointer after its lifetime.
+///
+/// ## 2. Consume a box (`Box<T>`).
+///
+/// The [`into_raw`] function consumes a box and returns
+/// the raw pointer. It doesn't destroy `T` or deallocate any memory.
+///
+/// ```
+/// let my_speed: Box<i32> = Box::new(88);
+/// let my_speed: *mut i32 = Box::into_raw(my_speed);
+///
+/// // By taking ownership of the original `Box<T>` though
+/// // we are obligated to put it together later to be destroyed.
+/// unsafe {
+///     drop(Box::from_raw(my_speed));
+/// }
+/// ```
+///
+/// Note that here the call to [`drop`] is for clarity - it indicates
+/// that we are done with the given value and it should be destroyed.
+///
+/// ## 3. Create it using `ptr::addr_of!`
+///
+/// Instead of coercing a reference to a raw pointer, you can use the macros
+/// [`ptr::addr_of!`] (for `*const T`) and [`ptr::addr_of_mut!`] (for `*mut T`).
+/// These macros allow you to create raw pointers to fields to which you cannot
+/// create a reference (without causing undefined behaviour), such as an
+/// unaligned field. This might be necessary if packed structs or uninitialized
+/// memory is involved.
+///
+/// ```
+/// #[derive(Debug, Default, Copy, Clone)]
+/// #[repr(C, packed)]
+/// struct S {
+///     aligned: u8,
+///     unaligned: u32,
+/// }
+/// let s = S::default();
+/// let p = std::ptr::addr_of!(s.unaligned); // not allowed with coercion
+/// ```
+///
+/// ## 4. Get it from C.
+///
+/// ```
+/// # #![feature(rustc_private)]
+/// extern crate libc;
+///
+/// use std::mem;
+///
+/// unsafe {
+///     let my_num: *mut i32 = libc::malloc(mem::size_of::<i32>()) as *mut i32;
+///     if my_num.is_null() {
+///         panic!("failed to allocate memory");
+///     }
+///     libc::free(my_num as *mut libc::c_void);
+/// }
+/// ```
+///
+/// Usually you wouldn't literally use `malloc` and `free` from Rust,
+/// but C APIs hand out a lot of pointers generally, so are a common source
+/// of raw pointers in Rust.
+///
+/// [`null`]: ptr::null
+/// [`null_mut`]: ptr::null_mut
+/// [`is_null`]: pointer::is_null
+/// [`offset`]: pointer::offset
+#[doc = concat!("[`into_raw`]: ", include_str!("../primitive_docs/box_into_raw.md"))]
+/// [`drop`]: mem::drop
+/// [`write`]: ptr::write
+#[stable(feature = "rust1", since = "1.0.0")]
+mod prim_pointer {}
+
+#[doc(primitive = "array")]
+#[doc(alias = "[]")]
+#[doc(alias = "[T;N]")] // unfortunately, rustdoc doesn't have fuzzy search for aliases
+#[doc(alias = "[T; N]")]
+/// A fixed-size array, denoted `[T; N]`, for the element type, `T`, and the
+/// non-negative compile-time constant size, `N`.
+///
+/// There are two syntactic forms for creating an array:
+///
+/// * A list with each element, i.e., `[x, y, z]`.
+/// * A repeat expression `[x; N]`, which produces an array with `N` copies of `x`.
+///   The type of `x` must be [`Copy`].
+///
+/// Note that `[expr; 0]` is allowed, and produces an empty array.
+/// This will still evaluate `expr`, however, and immediately drop the resulting value, so
+/// be mindful of side effects.
+///
+/// Arrays of *any* size implement the following traits if the element type allows it:
+///
+/// - [`Copy`]
+/// - [`Clone`]
+/// - [`Debug`]
+/// - [`IntoIterator`] (implemented for `[T; N]`, `&[T; N]` and `&mut [T; N]`)
+/// - [`PartialEq`], [`PartialOrd`], [`Eq`], [`Ord`]
+/// - [`Hash`]
+/// - [`AsRef`], [`AsMut`]
+/// - [`Borrow`], [`BorrowMut`]
+///
+/// Arrays of sizes from 0 to 32 (inclusive) implement the [`Default`] trait
+/// if the element type allows it. As a stopgap, trait implementations are
+/// statically generated up to size 32.
+///
+/// Arrays coerce to [slices (`[T]`)][slice], so a slice method may be called on
+/// an array. Indeed, this provides most of the API for working with arrays.
+/// Slices have a dynamic size and do not coerce to arrays.
+///
+/// You can move elements out of an array with a [slice pattern]. If you want
+/// one element, see [`mem::replace`].
+///
+/// # Examples
+///
+/// ```
+/// let mut array: [i32; 3] = [0; 3];
+///
+/// array[1] = 1;
+/// array[2] = 2;
+///
+/// assert_eq!([1, 2], &array[1..]);
+///
+/// // This loop prints: 0 1 2
+/// for x in array {
+///     print!("{} ", x);
+/// }
+/// ```
+///
+/// You can also iterate over reference to the array's elements:
+///
+/// ```
+/// let array: [i32; 3] = [0; 3];
+///
+/// for x in &array { }
+/// ```
+///
+/// You can use a [slice pattern] to move elements out of an array:
+///
+/// ```
+/// fn move_away(_: String) { /* Do interesting things. */ }
+///
+/// let [john, roa] = ["John".to_string(), "Roa".to_string()];
+/// move_away(john);
+/// move_away(roa);
+/// ```
+///
+/// # Editions
+///
+/// Prior to Rust 1.53, arrays did not implement [`IntoIterator`] by value, so the method call
+/// `array.into_iter()` auto-referenced into a [slice iterator](slice::iter). Right now, the old
+/// behavior is preserved in the 2015 and 2018 editions of Rust for compatibility, ignoring
+/// [`IntoIterator`] by value. In the future, the behavior on the 2015 and 2018 edition
+/// might be made consistent to the behavior of later editions.
+///
+/// ```rust,edition2018
+/// // Rust 2015 and 2018:
+///
+/// # #![allow(array_into_iter)] // override our `deny(warnings)`
+/// let array: [i32; 3] = [0; 3];
+///
+/// // This creates a slice iterator, producing references to each value.
+/// for item in array.into_iter().enumerate() {
+///     let (i, x): (usize, &i32) = item;
+///     println!("array[{}] = {}", i, x);
+/// }
+///
+/// // The `array_into_iter` lint suggests this change for future compatibility:
+/// for item in array.iter().enumerate() {
+///     let (i, x): (usize, &i32) = item;
+///     println!("array[{}] = {}", i, x);
+/// }
+///
+/// // You can explicitly iterate an array by value using
+/// // `IntoIterator::into_iter` or `std::array::IntoIter::new`:
+/// for item in IntoIterator::into_iter(array).enumerate() {
+///     let (i, x): (usize, i32) = item;
+///     println!("array[{}] = {}", i, x);
+/// }
+/// ```
+///
+/// Starting in the 2021 edition, `array.into_iter()` uses `IntoIterator` normally to iterate
+/// by value, and `iter()` should be used to iterate by reference like previous editions.
+///
+/// ```rust,edition2021
+/// // Rust 2021:
+///
+/// let array: [i32; 3] = [0; 3];
+///
+/// // This iterates by reference:
+/// for item in array.iter().enumerate() {
+///     let (i, x): (usize, &i32) = item;
+///     println!("array[{}] = {}", i, x);
+/// }
+///
+/// // This iterates by value:
+/// for item in array.into_iter().enumerate() {
+///     let (i, x): (usize, i32) = item;
+///     println!("array[{}] = {}", i, x);
+/// }
+/// ```
+///
+/// Future language versions might start treating the `array.into_iter()`
+/// syntax on editions 2015 and 2018 the same as on edition 2021. So code using
+/// those older editions should still be written with this change in mind, to
+/// prevent breakage in the future. The safest way to accomplish this is to
+/// avoid the `into_iter` syntax on those editions. If an edition update is not
+/// viable/desired, there are multiple alternatives:
+/// * use `iter`, equivalent to the old behavior, creating references
+/// * use [`IntoIterator::into_iter`], equivalent to the post-2021 behavior (Rust 1.53+)
+/// * replace `for ... in array.into_iter() {` with `for ... in array {`,
+///   equivalent to the post-2021 behavior (Rust 1.53+)
+///
+/// ```rust,edition2018
+/// // Rust 2015 and 2018:
+///
+/// let array: [i32; 3] = [0; 3];
+///
+/// // This iterates by reference:
+/// for item in array.iter() {
+///     let x: &i32 = item;
+///     println!("{}", x);
+/// }
+///
+/// // This iterates by value:
+/// for item in IntoIterator::into_iter(array) {
+///     let x: i32 = item;
+///     println!("{}", x);
+/// }
+///
+/// // This iterates by value:
+/// for item in array {
+///     let x: i32 = item;
+///     println!("{}", x);
+/// }
+///
+/// // IntoIter can also start a chain.
+/// // This iterates by value:
+/// for item in IntoIterator::into_iter(array).enumerate() {
+///     let (i, x): (usize, i32) = item;
+///     println!("array[{}] = {}", i, x);
+/// }
+/// ```
+///
+/// [slice]: prim@slice
+/// [`Debug`]: fmt::Debug
+/// [`Hash`]: hash::Hash
+/// [`Borrow`]: borrow::Borrow
+/// [`BorrowMut`]: borrow::BorrowMut
+/// [slice pattern]: ../reference/patterns.html#slice-patterns
+#[stable(feature = "rust1", since = "1.0.0")]
+mod prim_array {}
+
+#[doc(primitive = "slice")]
+#[doc(alias = "[")]
+#[doc(alias = "]")]
+#[doc(alias = "[]")]
+/// A dynamically-sized view into a contiguous sequence, `[T]`. Contiguous here
+/// means that elements are laid out so that every element is the same
+/// distance from its neighbors.
+///
+/// *[See also the `std::slice` module](crate::slice).*
+///
+/// Slices are a view into a block of memory represented as a pointer and a
+/// length.
+///
+/// ```
+/// // slicing a Vec
+/// let vec = vec![1, 2, 3];
+/// let int_slice = &vec[..];
+/// // coercing an array to a slice
+/// let str_slice: &[&str] = &["one", "two", "three"];
+/// ```
+///
+/// Slices are either mutable or shared. The shared slice type is `&[T]`,
+/// while the mutable slice type is `&mut [T]`, where `T` represents the element
+/// type. For example, you can mutate the block of memory that a mutable slice
+/// points to:
+///
+/// ```
+/// let mut x = [1, 2, 3];
+/// let x = &mut x[..]; // Take a full slice of `x`.
+/// x[1] = 7;
+/// assert_eq!(x, &[1, 7, 3]);
+/// ```
+///
+/// As slices store the length of the sequence they refer to, they have twice
+/// the size of pointers to [`Sized`](marker/trait.Sized.html) types.
+/// Also see the reference on
+/// [dynamically sized types](../reference/dynamically-sized-types.html).
+///
+/// ```
+/// # use std::rc::Rc;
+/// let pointer_size = std::mem::size_of::<&u8>();
+/// assert_eq!(2 * pointer_size, std::mem::size_of::<&[u8]>());
+/// assert_eq!(2 * pointer_size, std::mem::size_of::<*const [u8]>());
+/// assert_eq!(2 * pointer_size, std::mem::size_of::<Box<[u8]>>());
+/// assert_eq!(2 * pointer_size, std::mem::size_of::<Rc<[u8]>>());
+/// ```
+#[stable(feature = "rust1", since = "1.0.0")]
+mod prim_slice {}
+
+#[doc(primitive = "str")]
+//
+/// String slices.
+///
+/// *[See also the `std::str` module](crate::str).*
+///
+/// The `str` type, also called a 'string slice', is the most primitive string
+/// type. It is usually seen in its borrowed form, `&str`. It is also the type
+/// of string literals, `&'static str`.
+///
+/// String slices are always valid UTF-8.
+///
+/// # Examples
+///
+/// String literals are string slices:
+///
+/// ```
+/// let hello = "Hello, world!";
+///
+/// // with an explicit type annotation
+/// let hello: &'static str = "Hello, world!";
+/// ```
+///
+/// They are `'static` because they're stored directly in the final binary, and
+/// so will be valid for the `'static` duration.
+///
+/// # Representation
+///
+/// A `&str` is made up of two components: a pointer to some bytes, and a
+/// length. You can look at these with the [`as_ptr`] and [`len`] methods:
+///
+/// ```
+/// use std::slice;
+/// use std::str;
+///
+/// let story = "Once upon a time...";
+///
+/// let ptr = story.as_ptr();
+/// let len = story.len();
+///
+/// // story has nineteen bytes
+/// assert_eq!(19, len);
+///
+/// // We can re-build a str out of ptr and len. This is all unsafe because
+/// // we are responsible for making sure the two components are valid:
+/// let s = unsafe {
+///     // First, we build a &[u8]...
+///     let slice = slice::from_raw_parts(ptr, len);
+///
+///     // ... and then convert that slice into a string slice
+///     str::from_utf8(slice)
+/// };
+///
+/// assert_eq!(s, Ok(story));
+/// ```
+///
+/// [`as_ptr`]: str::as_ptr
+/// [`len`]: str::len
+///
+/// Note: This example shows the internals of `&str`. `unsafe` should not be
+/// used to get a string slice under normal circumstances. Use `as_str`
+/// instead.
+#[stable(feature = "rust1", since = "1.0.0")]
+mod prim_str {}
+
+#[doc(primitive = "tuple")]
+#[doc(alias = "(")]
+#[doc(alias = ")")]
+#[doc(alias = "()")]
+//
+/// A finite heterogeneous sequence, `(T, U, ..)`.
+///
+/// Let's cover each of those in turn:
+///
+/// Tuples are *finite*. In other words, a tuple has a length. Here's a tuple
+/// of length `3`:
+///
+/// ```
+/// ("hello", 5, 'c');
+/// ```
+///
+/// 'Length' is also sometimes called 'arity' here; each tuple of a different
+/// length is a different, distinct type.
+///
+/// Tuples are *heterogeneous*. This means that each element of the tuple can
+/// have a different type. In that tuple above, it has the type:
+///
+/// ```
+/// # let _:
+/// (&'static str, i32, char)
+/// # = ("hello", 5, 'c');
+/// ```
+///
+/// Tuples are a *sequence*. This means that they can be accessed by position;
+/// this is called 'tuple indexing', and it looks like this:
+///
+/// ```rust
+/// let tuple = ("hello", 5, 'c');
+///
+/// assert_eq!(tuple.0, "hello");
+/// assert_eq!(tuple.1, 5);
+/// assert_eq!(tuple.2, 'c');
+/// ```
+///
+/// The sequential nature of the tuple applies to its implementations of various
+/// traits. For example, in [`PartialOrd`] and [`Ord`], the elements are compared
+/// sequentially until the first non-equal set is found.
+///
+/// For more about tuples, see [the book](../book/ch03-02-data-types.html#the-tuple-type).
+///
+/// # Trait implementations
+///
+/// If every type inside a tuple implements one of the following traits, then a
+/// tuple itself also implements it.
+///
+/// * [`Clone`]
+/// * [`Copy`]
+/// * [`PartialEq`]
+/// * [`Eq`]
+/// * [`PartialOrd`]
+/// * [`Ord`]
+/// * [`Debug`]
+/// * [`Default`]
+/// * [`Hash`]
+///
+/// [`Debug`]: fmt::Debug
+/// [`Hash`]: hash::Hash
+///
+/// Due to a temporary restriction in Rust's type system, these traits are only
+/// implemented on tuples of arity 12 or less. In the future, this may change.
+///
+/// # Examples
+///
+/// Basic usage:
+///
+/// ```
+/// let tuple = ("hello", 5, 'c');
+///
+/// assert_eq!(tuple.0, "hello");
+/// ```
+///
+/// Tuples are often used as a return type when you want to return more than
+/// one value:
+///
+/// ```
+/// fn calculate_point() -> (i32, i32) {
+///     // Don't do a calculation, that's not the point of the example
+///     (4, 5)
+/// }
+///
+/// let point = calculate_point();
+///
+/// assert_eq!(point.0, 4);
+/// assert_eq!(point.1, 5);
+///
+/// // Combining this with patterns can be nicer.
+///
+/// let (x, y) = calculate_point();
+///
+/// assert_eq!(x, 4);
+/// assert_eq!(y, 5);
+/// ```
+///
+#[stable(feature = "rust1", since = "1.0.0")]
+mod prim_tuple {}
+
+#[doc(primitive = "f32")]
+/// A 32-bit floating point type (specifically, the "binary32" type defined in IEEE 754-2008).
+///
+/// This type can represent a wide range of decimal numbers, like `3.5`, `27`,
+/// `-113.75`, `0.0078125`, `34359738368`, `0`, `-1`. So unlike integer types
+/// (such as `i32`), floating point types can represent non-integer numbers,
+/// too.
+///
+/// However, being able to represent this wide range of numbers comes at the
+/// cost of precision: floats can only represent some of the real numbers and
+/// calculation with floats round to a nearby representable number. For example,
+/// `5.0` and `1.0` can be exactly represented as `f32`, but `1.0 / 5.0` results
+/// in `0.20000000298023223876953125` since `0.2` cannot be exactly represented
+/// as `f32`. Note, however, that printing floats with `println` and friends will
+/// often discard insignificant digits: `println!("{}", 1.0f32 / 5.0f32)` will
+/// print `0.2`.
+///
+/// Additionally, `f32` can represent some special values:
+///
+/// - āˆ’0.0: IEEE 754 floating point numbers have a bit that indicates their sign, so āˆ’0.0 is a
+///   possible value. For comparison āˆ’0.0 = +0.0, but floating point operations can carry
+///   the sign bit through arithmetic operations. This means āˆ’0.0 Ɨ +0.0 produces āˆ’0.0 and
+///   a negative number rounded to a value smaller than a float can represent also produces āˆ’0.0.
+/// - [āˆž](#associatedconstant.INFINITY) and
+///   [āˆ’āˆž](#associatedconstant.NEG_INFINITY): these result from calculations
+///   like `1.0 / 0.0`.
+/// - [NaN (not a number)](#associatedconstant.NAN): this value results from
+///   calculations like `(-1.0).sqrt()`. NaN has some potentially unexpected
+///   behavior: it is unequal to any float, including itself! It is also neither
+///   smaller nor greater than any float, making it impossible to sort. Lastly,
+///   it is considered infectious as almost all calculations where one of the
+///   operands is NaN will also result in NaN.
+///
+/// For more information on floating point numbers, see [Wikipedia][wikipedia].
+///
+/// *[See also the `std::f32::consts` module](crate::f32::consts).*
+///
+/// [wikipedia]: https://en.wikipedia.org/wiki/Single-precision_floating-point_format
+#[stable(feature = "rust1", since = "1.0.0")]
+mod prim_f32 {}
+
+#[doc(primitive = "f64")]
+/// A 64-bit floating point type (specifically, the "binary64" type defined in IEEE 754-2008).
+///
+/// This type is very similar to [`f32`], but has increased
+/// precision by using twice as many bits. Please see [the documentation for
+/// `f32`][`f32`] or [Wikipedia on double precision
+/// values][wikipedia] for more information.
+///
+/// *[See also the `std::f64::consts` module](crate::f64::consts).*
+///
+/// [`f32`]: prim@f32
+/// [wikipedia]: https://en.wikipedia.org/wiki/Double-precision_floating-point_format
+#[stable(feature = "rust1", since = "1.0.0")]
+mod prim_f64 {}
+
+#[doc(primitive = "i8")]
+//
+/// The 8-bit signed integer type.
+#[stable(feature = "rust1", since = "1.0.0")]
+mod prim_i8 {}
+
+#[doc(primitive = "i16")]
+//
+/// The 16-bit signed integer type.
+#[stable(feature = "rust1", since = "1.0.0")]
+mod prim_i16 {}
+
+#[doc(primitive = "i32")]
+//
+/// The 32-bit signed integer type.
+#[stable(feature = "rust1", since = "1.0.0")]
+mod prim_i32 {}
+
+#[doc(primitive = "i64")]
+//
+/// The 64-bit signed integer type.
+#[stable(feature = "rust1", since = "1.0.0")]
+mod prim_i64 {}
+
+#[doc(primitive = "i128")]
+//
+/// The 128-bit signed integer type.
+#[stable(feature = "i128", since = "1.26.0")]
+mod prim_i128 {}
+
+#[doc(primitive = "u8")]
+//
+/// The 8-bit unsigned integer type.
+#[stable(feature = "rust1", since = "1.0.0")]
+mod prim_u8 {}
+
+#[doc(primitive = "u16")]
+//
+/// The 16-bit unsigned integer type.
+#[stable(feature = "rust1", since = "1.0.0")]
+mod prim_u16 {}
+
+#[doc(primitive = "u32")]
+//
+/// The 32-bit unsigned integer type.
+#[stable(feature = "rust1", since = "1.0.0")]
+mod prim_u32 {}
+
+#[doc(primitive = "u64")]
+//
+/// The 64-bit unsigned integer type.
+#[stable(feature = "rust1", since = "1.0.0")]
+mod prim_u64 {}
+
+#[doc(primitive = "u128")]
+//
+/// The 128-bit unsigned integer type.
+#[stable(feature = "i128", since = "1.26.0")]
+mod prim_u128 {}
+
+#[doc(primitive = "isize")]
+//
+/// The pointer-sized signed integer type.
+///
+/// The size of this primitive is how many bytes it takes to reference any
+/// location in memory. For example, on a 32 bit target, this is 4 bytes
+/// and on a 64 bit target, this is 8 bytes.
+#[stable(feature = "rust1", since = "1.0.0")]
+mod prim_isize {}
+
+#[doc(primitive = "usize")]
+//
+/// The pointer-sized unsigned integer type.
+///
+/// The size of this primitive is how many bytes it takes to reference any
+/// location in memory. For example, on a 32 bit target, this is 4 bytes
+/// and on a 64 bit target, this is 8 bytes.
+#[stable(feature = "rust1", since = "1.0.0")]
+mod prim_usize {}
+
+#[doc(primitive = "reference")]
+#[doc(alias = "&")]
+#[doc(alias = "&mut")]
+//
+/// References, both shared and mutable.
+///
+/// A reference represents a borrow of some owned value. You can get one by using the `&` or `&mut`
+/// operators on a value, or by using a [`ref`](../std/keyword.ref.html) or
+/// <code>[ref](../std/keyword.ref.html) [mut](../std/keyword.mut.html)</code> pattern.
+///
+/// For those familiar with pointers, a reference is just a pointer that is assumed to be
+/// aligned, not null, and pointing to memory containing a valid value of `T` - for example,
+/// <code>&[bool]</code> can only point to an allocation containing the integer values `1`
+/// ([`true`](../std/keyword.true.html)) or `0` ([`false`](../std/keyword.false.html)), but
+/// creating a <code>&[bool]</code> that points to an allocation containing
+/// the value `3` causes undefined behaviour.
+/// In fact, <code>[Option]\<&T></code> has the same memory representation as a
+/// nullable but aligned pointer, and can be passed across FFI boundaries as such.
+///
+/// In most cases, references can be used much like the original value. Field access, method
+/// calling, and indexing work the same (save for mutability rules, of course). In addition, the
+/// comparison operators transparently defer to the referent's implementation, allowing references
+/// to be compared the same as owned values.
+///
+/// References have a lifetime attached to them, which represents the scope for which the borrow is
+/// valid. A lifetime is said to "outlive" another one if its representative scope is as long or
+/// longer than the other. The `'static` lifetime is the longest lifetime, which represents the
+/// total life of the program. For example, string literals have a `'static` lifetime because the
+/// text data is embedded into the binary of the program, rather than in an allocation that needs
+/// to be dynamically managed.
+///
+/// `&mut T` references can be freely coerced into `&T` references with the same referent type, and
+/// references with longer lifetimes can be freely coerced into references with shorter ones.
+///
+/// Reference equality by address, instead of comparing the values pointed to, is accomplished via
+/// implicit reference-pointer coercion and raw pointer equality via [`ptr::eq`], while
+/// [`PartialEq`] compares values.
+///
+/// ```
+/// use std::ptr;
+///
+/// let five = 5;
+/// let other_five = 5;
+/// let five_ref = &five;
+/// let same_five_ref = &five;
+/// let other_five_ref = &other_five;
+///
+/// assert!(five_ref == same_five_ref);
+/// assert!(five_ref == other_five_ref);
+///
+/// assert!(ptr::eq(five_ref, same_five_ref));
+/// assert!(!ptr::eq(five_ref, other_five_ref));
+/// ```
+///
+/// For more information on how to use references, see [the book's section on "References and
+/// Borrowing"][book-refs].
+///
+/// [book-refs]: ../book/ch04-02-references-and-borrowing.html
+///
+/// # Trait implementations
+///
+/// The following traits are implemented for all `&T`, regardless of the type of its referent:
+///
+/// * [`Copy`]
+/// * [`Clone`] \(Note that this will not defer to `T`'s `Clone` implementation if it exists!)
+/// * [`Deref`]
+/// * [`Borrow`]
+/// * [`Pointer`]
+///
+/// [`Deref`]: ops::Deref
+/// [`Borrow`]: borrow::Borrow
+/// [`Pointer`]: fmt::Pointer
+///
+/// `&mut T` references get all of the above except `Copy` and `Clone` (to prevent creating
+/// multiple simultaneous mutable borrows), plus the following, regardless of the type of its
+/// referent:
+///
+/// * [`DerefMut`]
+/// * [`BorrowMut`]
+///
+/// [`DerefMut`]: ops::DerefMut
+/// [`BorrowMut`]: borrow::BorrowMut
+/// [bool]: prim@bool
+///
+/// The following traits are implemented on `&T` references if the underlying `T` also implements
+/// that trait:
+///
+/// * All the traits in [`std::fmt`] except [`Pointer`] and [`fmt::Write`]
+/// * [`PartialOrd`]
+/// * [`Ord`]
+/// * [`PartialEq`]
+/// * [`Eq`]
+/// * [`AsRef`]
+/// * [`Fn`] \(in addition, `&T` references get [`FnMut`] and [`FnOnce`] if `T: Fn`)
+/// * [`Hash`]
+/// * [`ToSocketAddrs`]
+///
+/// [`std::fmt`]: fmt
+/// ['Pointer`]: fmt::Pointer
+/// [`Hash`]: hash::Hash
+#[doc = concat!("[`ToSocketAddrs`]: ", include_str!("../primitive_docs/net_tosocketaddrs.md"))]
+///
+/// `&mut T` references get all of the above except `ToSocketAddrs`, plus the following, if `T`
+/// implements that trait:
+///
+/// * [`AsMut`]
+/// * [`FnMut`] \(in addition, `&mut T` references get [`FnOnce`] if `T: FnMut`)
+/// * [`fmt::Write`]
+/// * [`Iterator`]
+/// * [`DoubleEndedIterator`]
+/// * [`ExactSizeIterator`]
+/// * [`FusedIterator`]
+/// * [`TrustedLen`]
+/// * [`Send`] \(note that `&T` references only get `Send` if <code>T: [Sync]</code>)
+/// * [`io::Write`]
+/// * [`Read`]
+/// * [`Seek`]
+/// * [`BufRead`]
+///
+/// [`FusedIterator`]: iter::FusedIterator
+/// [`TrustedLen`]: iter::TrustedLen
+#[doc = concat!("[`Seek`]: ", include_str!("../primitive_docs/io_seek.md"))]
+#[doc = concat!("[`BufRead`]: ", include_str!("../primitive_docs/io_bufread.md"))]
+#[doc = concat!("[`Read`]: ", include_str!("../primitive_docs/io_read.md"))]
+#[doc = concat!("[`io::Write`]: ", include_str!("../primitive_docs/io_write.md"))]
+///
+/// Note that due to method call deref coercion, simply calling a trait method will act like they
+/// work on references as well as they do on owned values! The implementations described here are
+/// meant for generic contexts, where the final type `T` is a type parameter or otherwise not
+/// locally known.
+#[stable(feature = "rust1", since = "1.0.0")]
+mod prim_ref {}
+
+#[doc(primitive = "fn")]
+//
+/// Function pointers, like `fn(usize) -> bool`.
+///
+/// *See also the traits [`Fn`], [`FnMut`], and [`FnOnce`].*
+///
+/// [`Fn`]: ops::Fn
+/// [`FnMut`]: ops::FnMut
+/// [`FnOnce`]: ops::FnOnce
+///
+/// Function pointers are pointers that point to *code*, not data. They can be called
+/// just like functions. Like references, function pointers are, among other things, assumed to
+/// not be null, so if you want to pass a function pointer over FFI and be able to accommodate null
+/// pointers, make your type [`Option<fn()>`](core::option#options-and-pointers-nullable-pointers)
+/// with your required signature.
+///
+/// ### Safety
+///
+/// Plain function pointers are obtained by casting either plain functions, or closures that don't
+/// capture an environment:
+///
+/// ```
+/// fn add_one(x: usize) -> usize {
+///     x + 1
+/// }
+///
+/// let ptr: fn(usize) -> usize = add_one;
+/// assert_eq!(ptr(5), 6);
+///
+/// let clos: fn(usize) -> usize = |x| x + 5;
+/// assert_eq!(clos(5), 10);
+/// ```
+///
+/// In addition to varying based on their signature, function pointers come in two flavors: safe
+/// and unsafe. Plain `fn()` function pointers can only point to safe functions,
+/// while `unsafe fn()` function pointers can point to safe or unsafe functions.
+///
+/// ```
+/// fn add_one(x: usize) -> usize {
+///     x + 1
+/// }
+///
+/// unsafe fn add_one_unsafely(x: usize) -> usize {
+///     x + 1
+/// }
+///
+/// let safe_ptr: fn(usize) -> usize = add_one;
+///
+/// //ERROR: mismatched types: expected normal fn, found unsafe fn
+/// //let bad_ptr: fn(usize) -> usize = add_one_unsafely;
+///
+/// let unsafe_ptr: unsafe fn(usize) -> usize = add_one_unsafely;
+/// let really_safe_ptr: unsafe fn(usize) -> usize = add_one;
+/// ```
+///
+/// ### ABI
+///
+/// On top of that, function pointers can vary based on what ABI they use. This
+/// is achieved by adding the `extern` keyword before the type, followed by the
+/// ABI in question. The default ABI is "Rust", i.e., `fn()` is the exact same
+/// type as `extern "Rust" fn()`. A pointer to a function with C ABI would have
+/// type `extern "C" fn()`.
+///
+/// `extern "ABI" { ... }` blocks declare functions with ABI "ABI". The default
+/// here is "C", i.e., functions declared in an `extern {...}` block have "C"
+/// ABI.
+///
+/// For more information and a list of supported ABIs, see [the nomicon's
+/// section on foreign calling conventions][nomicon-abi].
+///
+/// [nomicon-abi]: ../nomicon/ffi.html#foreign-calling-conventions
+///
+/// ### Variadic functions
+///
+/// Extern function declarations with the "C" or "cdecl" ABIs can also be *variadic*, allowing them
+/// to be called with a variable number of arguments. Normal Rust functions, even those with an
+/// `extern "ABI"`, cannot be variadic. For more information, see [the nomicon's section on
+/// variadic functions][nomicon-variadic].
+///
+/// [nomicon-variadic]: ../nomicon/ffi.html#variadic-functions
+///
+/// ### Creating function pointers
+///
+/// When `bar` is the name of a function, then the expression `bar` is *not* a
+/// function pointer. Rather, it denotes a value of an unnameable type that
+/// uniquely identifies the function `bar`. The value is zero-sized because the
+/// type already identifies the function. This has the advantage that "calling"
+/// the value (it implements the `Fn*` traits) does not require dynamic
+/// dispatch.
+///
+/// This zero-sized type *coerces* to a regular function pointer. For example:
+///
+/// ```rust
+/// use std::mem;
+///
+/// fn bar(x: i32) {}
+///
+/// let not_bar_ptr = bar; // `not_bar_ptr` is zero-sized, uniquely identifying `bar`
+/// assert_eq!(mem::size_of_val(&not_bar_ptr), 0);
+///
+/// let bar_ptr: fn(i32) = not_bar_ptr; // force coercion to function pointer
+/// assert_eq!(mem::size_of_val(&bar_ptr), mem::size_of::<usize>());
+///
+/// let footgun = &bar; // this is a shared reference to the zero-sized type identifying `bar`
+/// ```
+///
+/// The last line shows that `&bar` is not a function pointer either. Rather, it
+/// is a reference to the function-specific ZST. `&bar` is basically never what you
+/// want when `bar` is a function.
+///
+/// ### Traits
+///
+/// Function pointers implement the following traits:
+///
+/// * [`Clone`]
+/// * [`PartialEq`]
+/// * [`Eq`]
+/// * [`PartialOrd`]
+/// * [`Ord`]
+/// * [`Hash`]
+/// * [`Pointer`]
+/// * [`Debug`]
+///
+/// [`Hash`]: hash::Hash
+/// [`Pointer`]: fmt::Pointer
+///
+/// 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.
+#[stable(feature = "rust1", since = "1.0.0")]
+mod prim_fn {}
diff --git a/library/std/primitive_docs/box_into_raw.md b/library/std/primitive_docs/box_into_raw.md
new file mode 100644
index 0000000000000..307b9c85bd67e
--- /dev/null
+++ b/library/std/primitive_docs/box_into_raw.md
@@ -0,0 +1 @@
+Box::into_raw
diff --git a/library/std/primitive_docs/fs_file.md b/library/std/primitive_docs/fs_file.md
new file mode 100644
index 0000000000000..13e4540835e61
--- /dev/null
+++ b/library/std/primitive_docs/fs_file.md
@@ -0,0 +1 @@
+fs::File
diff --git a/library/std/primitive_docs/io_bufread.md b/library/std/primitive_docs/io_bufread.md
new file mode 100644
index 0000000000000..bb688e3a5cc87
--- /dev/null
+++ b/library/std/primitive_docs/io_bufread.md
@@ -0,0 +1 @@
+io::BufRead
diff --git a/library/std/primitive_docs/io_read.md b/library/std/primitive_docs/io_read.md
new file mode 100644
index 0000000000000..5118d7c4888ab
--- /dev/null
+++ b/library/std/primitive_docs/io_read.md
@@ -0,0 +1 @@
+io::Read
diff --git a/library/std/primitive_docs/io_seek.md b/library/std/primitive_docs/io_seek.md
new file mode 100644
index 0000000000000..122e6df77b6d7
--- /dev/null
+++ b/library/std/primitive_docs/io_seek.md
@@ -0,0 +1 @@
+io::Seek
diff --git a/library/std/primitive_docs/io_write.md b/library/std/primitive_docs/io_write.md
new file mode 100644
index 0000000000000..15dfc907a6555
--- /dev/null
+++ b/library/std/primitive_docs/io_write.md
@@ -0,0 +1 @@
+io::Write
diff --git a/library/std/primitive_docs/net_tosocketaddrs.md b/library/std/primitive_docs/net_tosocketaddrs.md
new file mode 100644
index 0000000000000..a01f318e88771
--- /dev/null
+++ b/library/std/primitive_docs/net_tosocketaddrs.md
@@ -0,0 +1 @@
+net::ToSocketAddrs
diff --git a/library/std/primitive_docs/process_exit.md b/library/std/primitive_docs/process_exit.md
new file mode 100644
index 0000000000000..565a71375cd0e
--- /dev/null
+++ b/library/std/primitive_docs/process_exit.md
@@ -0,0 +1 @@
+process::exit
diff --git a/library/std/primitive_docs/string_string.md b/library/std/primitive_docs/string_string.md
new file mode 100644
index 0000000000000..ce7815ff91b9e
--- /dev/null
+++ b/library/std/primitive_docs/string_string.md
@@ -0,0 +1 @@
+string::String
diff --git a/library/std/src/primitive_docs.rs b/library/std/src/primitive_docs.rs
index b0ebfe648a523..0de9126dab2fe 100644
--- a/library/std/src/primitive_docs.rs
+++ b/library/std/src/primitive_docs.rs
@@ -1,3 +1,6 @@
+// `library/{std,core}/src/primitive_docs.rs` should have the same contents.
+// These are different files so that relative links work properly without
+// having to have `CARGO_PKG_NAME` set, but conceptually they should always be the same.
 #[doc(primitive = "bool")]
 #[doc(alias = "true")]
 #[doc(alias = "false")]
@@ -20,12 +23,12 @@
 /// assert!(!bool_val);
 /// ```
 ///
-/// [`true`]: keyword.true.html
-/// [`false`]: keyword.false.html
+/// [`true`]: ../std/keyword.true.html
+/// [`false`]: ../std/keyword.false.html
 /// [`BitAnd`]: ops::BitAnd
 /// [`BitOr`]: ops::BitOr
 /// [`Not`]: ops::Not
-/// [`if`]: keyword.if.html
+/// [`if`]: ../std/keyword.if.html
 ///
 /// # Examples
 ///
@@ -103,7 +106,7 @@ mod prim_bool {}
 /// behaviour of the `!` type - expressions with type `!` will coerce into any other type.
 ///
 /// [`u32`]: prim@u32
-/// [`exit`]: process::exit
+#[doc = concat!("[`exit`]: ", include_str!("../primitive_docs/process_exit.md"))]
 ///
 /// # `!` and generics
 ///
@@ -188,7 +191,7 @@ mod prim_bool {}
 /// because `!` coerces to `Result<!, ConnectionError>` automatically.
 ///
 /// [`String::from_str`]: str::FromStr::from_str
-/// [`String`]: string::String
+#[doc = concat!("[`String`]: ", include_str!("../primitive_docs/string_string.md"))]
 /// [`FromStr`]: str::FromStr
 ///
 /// # `!` and traits
@@ -264,7 +267,7 @@ mod prim_bool {}
 /// `impl` for this which simply panics, but the same is true for any type (we could `impl
 /// Default` for (eg.) [`File`] by just making [`default()`] panic.)
 ///
-/// [`File`]: fs::File
+#[doc = concat!("[`File`]: ", include_str!("../primitive_docs/fs_file.md"))]
 /// [`Debug`]: fmt::Debug
 /// [`default()`]: Default::default
 ///
@@ -272,7 +275,6 @@ mod prim_bool {}
 mod prim_never {}
 
 #[doc(primitive = "char")]
-//
 /// A character type.
 ///
 /// The `char` type represents a single character. More specifically, since
@@ -304,7 +306,7 @@ mod prim_never {}
 /// assert_eq!(5, s.len() * std::mem::size_of::<u8>());
 /// ```
 ///
-/// [`String`]: string/struct.String.html
+#[doc = concat!("[`String`]: ", include_str!("../primitive_docs/string_string.md"))]
 ///
 /// As always, remember that a human intuition for 'character' might not map to
 /// Unicode's definitions. For example, despite looking similar, the 'Ć©'
@@ -499,7 +501,7 @@ mod prim_unit {}
 /// [`null_mut`]: ptr::null_mut
 /// [`is_null`]: pointer::is_null
 /// [`offset`]: pointer::offset
-/// [`into_raw`]: Box::into_raw
+#[doc = concat!("[`into_raw`]: ", include_str!("../primitive_docs/box_into_raw.md"))]
 /// [`drop`]: mem::drop
 /// [`write`]: ptr::write
 #[stable(feature = "rust1", since = "1.0.0")]
@@ -581,9 +583,9 @@ mod prim_pointer {}
 /// # Editions
 ///
 /// Prior to Rust 1.53, arrays did not implement [`IntoIterator`] by value, so the method call
-/// `array.into_iter()` auto-referenced into a [slice iterator](slice::iter). Right now, the old behavior
-/// is preserved in the 2015 and 2018 editions of Rust for compatibility, ignoring
-/// `IntoIterator` by value. In the future, the behavior on the 2015 and 2018 edition
+/// `array.into_iter()` auto-referenced into a [slice iterator](slice::iter). Right now, the old
+/// behavior is preserved in the 2015 and 2018 editions of Rust for compatibility, ignoring
+/// [`IntoIterator`] by value. In the future, the behavior on the 2015 and 2018 edition
 /// might be made consistent to the behavior of later editions.
 ///
 /// ```rust,edition2018
@@ -1042,15 +1044,15 @@ mod prim_usize {}
 /// References, both shared and mutable.
 ///
 /// A reference represents a borrow of some owned value. You can get one by using the `&` or `&mut`
-/// operators on a value, or by using a [`ref`](keyword.ref.html) or
-/// <code>[ref](keyword.ref.html) [mut](keyword.mut.html)</code> pattern.
+/// operators on a value, or by using a [`ref`](../std/keyword.ref.html) or
+/// <code>[ref](../std/keyword.ref.html) [mut](../std/keyword.mut.html)</code> pattern.
 ///
 /// For those familiar with pointers, a reference is just a pointer that is assumed to be
 /// aligned, not null, and pointing to memory containing a valid value of `T` - for example,
 /// <code>&[bool]</code> can only point to an allocation containing the integer values `1`
-/// ([`true`](keyword.true.html)) or `0` ([`false`](keyword.false.html)), but creating a
-/// <code>&[bool]</code> that points to an allocation containing the value `3` causes
-/// undefined behaviour.
+/// ([`true`](../std/keyword.true.html)) or `0` ([`false`](../std/keyword.false.html)), but
+/// creating a <code>&[bool]</code> that points to an allocation containing
+/// the value `3` causes undefined behaviour.
 /// In fact, <code>[Option]\<&T></code> has the same memory representation as a
 /// nullable but aligned pointer, and can be passed across FFI boundaries as such.
 ///
@@ -1117,6 +1119,7 @@ mod prim_usize {}
 ///
 /// [`DerefMut`]: ops::DerefMut
 /// [`BorrowMut`]: borrow::BorrowMut
+/// [bool]: prim@bool
 ///
 /// The following traits are implemented on `&T` references if the underlying `T` also implements
 /// that trait:
@@ -1134,7 +1137,7 @@ mod prim_usize {}
 /// [`std::fmt`]: fmt
 /// ['Pointer`]: fmt::Pointer
 /// [`Hash`]: hash::Hash
-/// [`ToSocketAddrs`]: net::ToSocketAddrs
+#[doc = concat!("[`ToSocketAddrs`]: ", include_str!("../primitive_docs/net_tosocketaddrs.md"))]
 ///
 /// `&mut T` references get all of the above except `ToSocketAddrs`, plus the following, if `T`
 /// implements that trait:
@@ -1155,9 +1158,10 @@ mod prim_usize {}
 ///
 /// [`FusedIterator`]: iter::FusedIterator
 /// [`TrustedLen`]: iter::TrustedLen
-/// [`Seek`]: io::Seek
-/// [`BufRead`]: io::BufRead
-/// [`Read`]: io::Read
+#[doc = concat!("[`Seek`]: ", include_str!("../primitive_docs/io_seek.md"))]
+#[doc = concat!("[`BufRead`]: ", include_str!("../primitive_docs/io_bufread.md"))]
+#[doc = concat!("[`Read`]: ", include_str!("../primitive_docs/io_read.md"))]
+#[doc = concat!("[`io::Write`]: ", include_str!("../primitive_docs/io_write.md"))]
 ///
 /// Note that due to method call deref coercion, simply calling a trait method will act like they
 /// work on references as well as they do on owned values! The implementations described here are
diff --git a/src/test/rustdoc/auxiliary/primitive-doc.rs b/src/test/rustdoc/auxiliary/primitive-doc.rs
index a5b69740dd447..e8da852a57e63 100644
--- a/src/test/rustdoc/auxiliary/primitive-doc.rs
+++ b/src/test/rustdoc/auxiliary/primitive-doc.rs
@@ -1,5 +1,8 @@
 // compile-flags: --crate-type lib --edition 2018
 
+#![feature(no_core)]
+#![no_core]
+
 #[doc(primitive = "usize")]
 /// This is the built-in type `usize`.
 mod usize {
diff --git a/src/test/rustdoc/cross-crate-primitive-doc.rs b/src/test/rustdoc/cross-crate-primitive-doc.rs
index 05376e4680ec4..709ffa1818066 100644
--- a/src/test/rustdoc/cross-crate-primitive-doc.rs
+++ b/src/test/rustdoc/cross-crate-primitive-doc.rs
@@ -1,9 +1,11 @@
 // aux-build:primitive-doc.rs
 // compile-flags: --extern-html-root-url=primitive_doc=../ -Z unstable-options
-
-#![no_std]
+#![feature(no_core)]
+#![no_core]
 
 extern crate primitive_doc;
 
 // @has 'cross_crate_primitive_doc/fn.foo.html' '//a[@href="../primitive_doc/primitive.usize.html"]' 'usize'
+// @has 'cross_crate_primitive_doc/fn.foo.html' '//a[@href="../primitive_doc/primitive.usize.html"]' 'link'
+/// [link](usize)
 pub fn foo() -> usize { 0 }
diff --git a/src/test/rustdoc/primitive/no_std.rs b/src/test/rustdoc/primitive/no_std.rs
new file mode 100644
index 0000000000000..8d05a8e6e0136
--- /dev/null
+++ b/src/test/rustdoc/primitive/no_std.rs
@@ -0,0 +1,5 @@
+#![no_std]
+
+// @has no_std/fn.foo.html '//a/[@href="{{channel}}/core/primitive.u8.html"]' 'u8'
+// Link to [u8]
+pub fn foo() -> u8 {}
diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs
index a1c41eb99810e..35809e599266c 100644
--- a/src/tools/tidy/src/lib.rs
+++ b/src/tools/tidy/src/lib.rs
@@ -46,6 +46,7 @@ pub mod errors;
 pub mod extdeps;
 pub mod features;
 pub mod pal;
+pub mod primitive_docs;
 pub mod style;
 pub mod target_specific_tests;
 pub mod ui_tests;
diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs
index 440c352ea5320..d555f7c8e34ff 100644
--- a/src/tools/tidy/src/main.rs
+++ b/src/tools/tidy/src/main.rs
@@ -71,6 +71,7 @@ fn main() {
 
         // Checks that only make sense for the std libs.
         check!(pal, &library_path);
+        check!(primitive_docs, &library_path);
 
         // Checks that need to be done for both the compiler and std libraries.
         check!(unit_tests, &src_path);
diff --git a/src/tools/tidy/src/primitive_docs.rs b/src/tools/tidy/src/primitive_docs.rs
new file mode 100644
index 0000000000000..8476650d9b5f7
--- /dev/null
+++ b/src/tools/tidy/src/primitive_docs.rs
@@ -0,0 +1,17 @@
+//! Tidy check to make sure `library/{std,core}/src/primitive_docs.rs` are the same file.  These are
+//! different files so that relative links work properly without having to have `CARGO_PKG_NAME`
+//! set, but conceptually they should always be the same.
+
+use std::path::Path;
+
+pub fn check(library_path: &Path, bad: &mut bool) {
+    let std_name = "std/src/primitive_docs.rs";
+    let core_name = "core/src/primitive_docs.rs";
+    let std_contents = std::fs::read_to_string(library_path.join(std_name))
+        .unwrap_or_else(|e| panic!("failed to read library/{}: {}", std_name, e));
+    let core_contents = std::fs::read_to_string(library_path.join(core_name))
+        .unwrap_or_else(|e| panic!("failed to read library/{}: {}", core_name, e));
+    if std_contents != core_contents {
+        tidy_error!(bad, "library/{} and library/{} have different contents", core_name, std_name);
+    }
+}

From f78acaee0372aa18ca37ebab8d919acbe997bd1c Mon Sep 17 00:00:00 2001
From: Joshua Nelson <jyn514@gmail.com>
Date: Sat, 10 Jul 2021 23:02:10 -0400
Subject: [PATCH 2/5] downgrade some logging

---
 src/librustdoc/clean/blanket_impl.rs | 13 ++++++++-----
 src/librustdoc/clean/inline.rs       |  4 ++--
 src/librustdoc/clean/mod.rs          |  2 +-
 src/librustdoc/html/format.rs        |  2 +-
 4 files changed, 12 insertions(+), 9 deletions(-)

diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs
index ff3641d6c9ab0..15d4563ad7461 100644
--- a/src/librustdoc/clean/blanket_impl.rs
+++ b/src/librustdoc/clean/blanket_impl.rs
@@ -17,7 +17,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> {
         let param_env = self.cx.tcx.param_env(item_def_id);
         let ty = self.cx.tcx.type_of(item_def_id);
 
-        debug!("get_blanket_impls({:?})", ty);
+        trace!("get_blanket_impls({:?})", ty);
         let mut impls = Vec::new();
         for &trait_def_id in self.cx.tcx.all_traits(()).iter() {
             if !self.cx.cache.access_levels.is_public(trait_def_id)
@@ -28,9 +28,10 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> {
             // NOTE: doesn't use `for_each_relevant_impl` to avoid looking at anything besides blanket impls
             let trait_impls = self.cx.tcx.trait_impls_of(trait_def_id);
             for &impl_def_id in trait_impls.blanket_impls() {
-                debug!(
+                trace!(
                     "get_blanket_impls: Considering impl for trait '{:?}' {:?}",
-                    trait_def_id, impl_def_id
+                    trait_def_id,
+                    impl_def_id
                 );
                 let trait_ref = self.cx.tcx.impl_trait_ref(impl_def_id).unwrap();
                 let is_param = matches!(trait_ref.self_ty().kind(), ty::Param(_));
@@ -50,9 +51,11 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> {
                         // FIXME(eddyb) ignoring `obligations` might cause false positives.
                         drop(obligations);
 
-                        debug!(
+                        trace!(
                             "invoking predicate_may_hold: param_env={:?}, trait_ref={:?}, ty={:?}",
-                            param_env, trait_ref, ty
+                            param_env,
+                            trait_ref,
+                            ty
                         );
                         let predicates = self
                             .cx
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index 78e4e4ec116da..40b0175bb1595 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -447,9 +447,9 @@ crate fn build_impl(
     }
 
     let (merged_attrs, cfg) = merge_attrs(cx, parent_module.into(), load_attrs(cx, did), attrs);
-    debug!("merged_attrs={:?}", merged_attrs);
+    trace!("merged_attrs={:?}", merged_attrs);
 
-    debug!("build_impl: impl {:?} for {:?}", trait_.def_id(), for_.def_id());
+    trace!("build_impl: impl {:?} for {:?}", trait_.def_id(), for_.def_id());
     ret.push(clean::Item::from_def_id_and_attrs_and_parts(
         did,
         None,
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 7ece2acac7a40..6ffdf1df6014a 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -1406,7 +1406,7 @@ fn normalize(cx: &mut DocContext<'tcx>, ty: Ty<'_>) -> Option<Ty<'tcx>> {
 
 impl<'tcx> Clean<Type> for Ty<'tcx> {
     fn clean(&self, cx: &mut DocContext<'_>) -> Type {
-        debug!("cleaning type: {:?}", self);
+        trace!("cleaning type: {:?}", self);
         let ty = normalize(cx, self).unwrap_or(self);
         match *ty.kind() {
             ty::Never => Never,
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index ea0458034899c..cce0006f2be64 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -742,7 +742,7 @@ fn fmt_type<'cx>(
     use_absolute: bool,
     cx: &'cx Context<'_>,
 ) -> fmt::Result {
-    debug!("fmt_type(t = {:?})", t);
+    trace!("fmt_type(t = {:?})", t);
 
     match *t {
         clean::Generic(name) => write!(f, "{}", name),

From cb7e527692bd01c68f01fd373db0c49dbc8670ce Mon Sep 17 00:00:00 2001
From: Joshua Nelson <jyn514@gmail.com>
Date: Sat, 10 Jul 2021 22:25:36 -0400
Subject: [PATCH 3/5] Fix broken handling of primitive items

- Fix broken handling of primitive associated items
- Remove fragment hack

  Fixes 83083

- more logging
- Update CrateNum hacks

  The CrateNum has no relation to where in the dependency tree the crate
  is, only when it's loaded. Explicitly special-case core instead of
  assuming it will be the first DefId.

- Update and add tests
- Cache calculation of primitive locations

  This could possibly be avoided by passing a Cache into
  collect_intra_doc_links; but that's a much larger change, and doesn't
  seem valuable other than for this.
---
 src/librustdoc/clean/types.rs                 | 119 ++++++++----------
 src/librustdoc/formats/cache.rs               |  23 ++--
 src/librustdoc/html/format.rs                 |   8 +-
 src/librustdoc/json/conversions.rs            |   4 +-
 .../passes/collect_intra_doc_links.rs         |  59 ++++-----
 src/test/rustdoc-ui/intra-doc/anchors.rs      |  10 --
 src/test/rustdoc-ui/intra-doc/anchors.stderr  |  35 ++----
 src/test/rustdoc/auxiliary/issue-15318.rs     |   8 ++
 src/test/rustdoc/intra-doc/anchors.rs         |  12 ++
 .../rustdoc/intra-doc/auxiliary/my-core.rs    |   4 +
 .../intra-doc/prim-methods-external-core.rs   |   4 +-
 .../rustdoc/intra-doc/prim-methods-local.rs   |   9 +-
 src/test/rustdoc/intra-link-prim-self.rs      |   4 +-
 src/test/rustdoc/issue-15318-2.rs             |   1 +
 src/test/rustdoc/no_std-primitive.rs          |   6 +
 src/test/rustdoc/primitive/no_std.rs          |   3 +-
 16 files changed, 144 insertions(+), 165 deletions(-)
 create mode 100644 src/test/rustdoc/no_std-primitive.rs

diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 4f4952d0afb9d..be1532d450150 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -461,60 +461,20 @@ impl Item {
             .map_or(&[][..], |v| v.as_slice())
             .iter()
             .filter_map(|ItemLink { link: s, link_text, did, ref fragment }| {
-                match did {
-                    Some(did) => {
-                        if let Ok((mut href, ..)) = href(*did, cx) {
-                            if let Some(ref fragment) = *fragment {
-                                href.push('#');
-                                href.push_str(fragment);
-                            }
-                            Some(RenderedLink {
-                                original_text: s.clone(),
-                                new_text: link_text.clone(),
-                                href,
-                            })
-                        } else {
-                            None
-                        }
-                    }
-                    // FIXME(83083): using fragments as a side-channel for
-                    // primitive names is very unfortunate
-                    None => {
-                        let relative_to = &cx.current;
-                        if let Some(ref fragment) = *fragment {
-                            let url = match cx.cache().extern_locations.get(&self.def_id.krate()) {
-                                Some(&ExternalLocation::Local) => {
-                                    if relative_to[0] == "std" {
-                                        let depth = relative_to.len() - 1;
-                                        "../".repeat(depth)
-                                    } else {
-                                        let depth = relative_to.len();
-                                        format!("{}std/", "../".repeat(depth))
-                                    }
-                                }
-                                Some(ExternalLocation::Remote(ref s)) => {
-                                    format!("{}/std/", s.trim_end_matches('/'))
-                                }
-                                Some(ExternalLocation::Unknown) | None => {
-                                    format!("{}/std/", crate::DOC_RUST_LANG_ORG_CHANNEL)
-                                }
-                            };
-                            // This is a primitive so the url is done "by hand".
-                            let tail = fragment.find('#').unwrap_or_else(|| fragment.len());
-                            Some(RenderedLink {
-                                original_text: s.clone(),
-                                new_text: link_text.clone(),
-                                href: format!(
-                                    "{}primitive.{}.html{}",
-                                    url,
-                                    &fragment[..tail],
-                                    &fragment[tail..]
-                                ),
-                            })
-                        } else {
-                            panic!("This isn't a primitive?!");
-                        }
+                debug!(?did);
+                if let Ok((mut href, ..)) = href(*did, cx) {
+                    debug!(?href);
+                    if let Some(ref fragment) = *fragment {
+                        href.push('#');
+                        href.push_str(fragment);
                     }
+                    Some(RenderedLink {
+                        original_text: s.clone(),
+                        new_text: link_text.clone(),
+                        href,
+                    })
+                } else {
+                    None
                 }
             })
             .collect()
@@ -531,18 +491,10 @@ impl Item {
             .get(&self.def_id)
             .map_or(&[][..], |v| v.as_slice())
             .iter()
-            .filter_map(|ItemLink { link: s, link_text, did, fragment }| {
-                // FIXME(83083): using fragments as a side-channel for
-                // primitive names is very unfortunate
-                if did.is_some() || fragment.is_some() {
-                    Some(RenderedLink {
-                        original_text: s.clone(),
-                        new_text: link_text.clone(),
-                        href: String::new(),
-                    })
-                } else {
-                    None
-                }
+            .map(|ItemLink { link: s, link_text, .. }| RenderedLink {
+                original_text: s.clone(),
+                new_text: link_text.clone(),
+                href: String::new(),
             })
             .collect()
     }
@@ -963,7 +915,7 @@ crate struct Attributes {
     crate other_attrs: Vec<ast::Attribute>,
 }
 
-#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
 /// A link that has not yet been rendered.
 ///
 /// This link will be turned into a rendered link by [`Item::links`].
@@ -975,7 +927,7 @@ crate struct ItemLink {
     /// This may not be the same as `link` if there was a disambiguator
     /// in an intra-doc link (e.g. \[`fn@f`\])
     pub(crate) link_text: String,
-    pub(crate) did: Option<DefId>,
+    pub(crate) did: DefId,
     /// The url fragment to append to the link
     pub(crate) fragment: Option<String>,
 }
@@ -1802,6 +1754,39 @@ impl PrimitiveType {
             Never => sym::never,
         }
     }
+
+    /// Returns the DefId of the module with `doc(primitive)` for this primitive type.
+    /// Panics if there is no such module.
+    ///
+    /// This gives precedence to primitives defined in the current crate, and deprioritizes primitives defined in `core`,
+    /// but otherwise, if multiple crates define the same primitive, there is no guarantee of which will be picked.
+    /// In particular, if a crate depends on both `std` and another crate that also defines `doc(primitive)`, then
+    /// it's entirely random whether `std` or the other crate is picked. (no_std crates are usually fine unless multiple dependencies define a primitive.)
+    crate fn primitive_locations(tcx: TyCtxt<'_>) -> &FxHashMap<PrimitiveType, DefId> {
+        static PRIMITIVE_LOCATIONS: OnceCell<FxHashMap<PrimitiveType, DefId>> = OnceCell::new();
+        PRIMITIVE_LOCATIONS.get_or_init(|| {
+            let mut primitive_locations = FxHashMap::default();
+            // NOTE: technically this misses crates that are only passed with `--extern` and not loaded when checking the crate.
+            // This is a degenerate case that I don't plan to support.
+            for &crate_num in tcx.crates(()) {
+                let e = ExternalCrate { crate_num };
+                let crate_name = e.name(tcx);
+                debug!(?crate_num, ?crate_name);
+                for &(def_id, prim) in &e.primitives(tcx) {
+                    // HACK: try to link to std instead where possible
+                    if crate_name == sym::core && primitive_locations.get(&prim).is_some() {
+                        continue;
+                    }
+                    primitive_locations.insert(prim, def_id);
+                }
+            }
+            let local_primitives = ExternalCrate { crate_num: LOCAL_CRATE }.primitives(tcx);
+            for (def_id, prim) in local_primitives {
+                primitive_locations.insert(prim, def_id);
+            }
+            primitive_locations
+        })
+    }
 }
 
 impl From<ast::IntTy> for PrimitiveType {
diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs
index 1830909d94460..66fd0d9262d6b 100644
--- a/src/librustdoc/formats/cache.rs
+++ b/src/librustdoc/formats/cache.rs
@@ -6,7 +6,7 @@ use rustc_middle::middle::privacy::AccessLevels;
 use rustc_middle::ty::TyCtxt;
 use rustc_span::symbol::sym;
 
-use crate::clean::{self, GetDefId, ItemId};
+use crate::clean::{self, GetDefId, ItemId, PrimitiveType};
 use crate::config::RenderOptions;
 use crate::fold::DocFolder;
 use crate::formats::item_type::ItemType;
@@ -159,17 +159,16 @@ impl Cache {
             self.external_paths.insert(e.def_id(), (vec![name.to_string()], ItemType::Module));
         }
 
-        // Cache where all known primitives have their documentation located.
-        //
-        // Favor linking to as local extern as possible, so iterate all crates in
-        // reverse topological order.
-        for &e in krate.externs.iter().rev() {
-            for &(def_id, prim) in &e.primitives(tcx) {
-                self.primitive_locations.insert(prim, def_id);
-            }
-        }
-        for &(def_id, prim) in &krate.primitives {
-            self.primitive_locations.insert(prim, def_id);
+        // FIXME: avoid this clone (requires implementing Default manually)
+        self.primitive_locations = PrimitiveType::primitive_locations(tcx).clone();
+        for (prim, &def_id) in &self.primitive_locations {
+            let crate_name = tcx.crate_name(def_id.krate);
+            // Recall that we only allow primitive modules to be at the root-level of the crate.
+            // If that restriction is ever lifted, this will have to include the relative paths instead.
+            self.external_paths.insert(
+                def_id,
+                (vec![crate_name.to_string(), prim.as_sym().to_string()], ItemType::Primitive),
+            );
         }
 
         krate = CacheBuilder { tcx, cache: self }.fold_crate(krate);
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index cce0006f2be64..2fde0017dc80c 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -509,7 +509,11 @@ crate fn href_with_root_path(
         if shortty == ItemType::Module { fqp } else { &fqp[..fqp.len() - 1] }
     }
 
-    if !did.is_local() && !cache.access_levels.is_public(did) && !cache.document_private {
+    if !did.is_local()
+        && !cache.access_levels.is_public(did)
+        && !cache.document_private
+        && !cache.primitive_locations.values().any(|&id| id == did)
+    {
         return Err(HrefError::Private);
     }
 
@@ -517,6 +521,7 @@ crate fn href_with_root_path(
     let (fqp, shortty, mut url_parts) = match cache.paths.get(&did) {
         Some(&(ref fqp, shortty)) => (fqp, shortty, {
             let module_fqp = to_module_fqp(shortty, fqp);
+            debug!(?fqp, ?shortty, ?module_fqp);
             href_relative_parts(module_fqp, relative_to)
         }),
         None => {
@@ -548,6 +553,7 @@ crate fn href_with_root_path(
             url_parts.insert(0, root);
         }
     }
+    debug!(?url_parts);
     let last = &fqp.last().unwrap()[..];
     let filename;
     match shortty {
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index e6bbd237cda7d..f8ea7a499b234 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -30,9 +30,7 @@ impl JsonRenderer<'_> {
             .get(&item.def_id)
             .into_iter()
             .flatten()
-            .filter_map(|clean::ItemLink { link, did, .. }| {
-                did.map(|did| (link.clone(), from_item_id(did.into())))
-            })
+            .map(|clean::ItemLink { link, did, .. }| (link.clone(), from_item_id((*did).into())))
             .collect();
         let docs = item.attrs.collapsed_doc_value();
         let attrs = item
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index 61f6b4e01c18c..c0c37ee061198 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -14,7 +14,7 @@ use rustc_hir::def::{
 };
 use rustc_hir::def_id::{CrateNum, DefId};
 use rustc_middle::ty::TyCtxt;
-use rustc_middle::{bug, ty};
+use rustc_middle::{bug, span_bug, ty};
 use rustc_resolve::ParentScope;
 use rustc_session::lint::Lint;
 use rustc_span::hygiene::{MacroKind, SyntaxContext};
@@ -98,14 +98,10 @@ impl Res {
         }
     }
 
-    fn def_id(self) -> DefId {
-        self.opt_def_id().expect("called def_id() on a primitive")
-    }
-
-    fn opt_def_id(self) -> Option<DefId> {
+    fn def_id(self, tcx: TyCtxt<'_>) -> DefId {
         match self {
-            Res::Def(_, id) => Some(id),
-            Res::Primitive(_) => None,
+            Res::Def(_, id) => id,
+            Res::Primitive(prim) => *PrimitiveType::primitive_locations(tcx).get(&prim).unwrap(),
         }
     }
 
@@ -237,10 +233,7 @@ enum AnchorFailure {
     /// link, Rustdoc disallows having a user-specified anchor.
     ///
     /// Most of the time this is fine, because you can just link to the page of
-    /// the item if you want to provide your own anchor. For primitives, though,
-    /// rustdoc uses the anchor as a side channel to know which page to link to;
-    /// it doesn't show up in the generated link. Ideally, rustdoc would remove
-    /// this limitation, allowing you to link to subheaders on primitives.
+    /// the item if you want to provide your own anchor.
     RustdocAnchorConflict(Res),
 }
 
@@ -388,7 +381,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
                         ty::AssocKind::Const => "associatedconstant",
                         ty::AssocKind::Type => "associatedtype",
                     };
-                    let fragment = format!("{}#{}.{}", prim_ty.as_sym(), out, item_name);
+                    let fragment = format!("{}.{}", out, item_name);
                     (Res::Primitive(prim_ty), fragment, Some((kind.as_def_kind(), item.def_id)))
                 })
         })
@@ -475,14 +468,6 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
                     return handle_variant(self.cx, res, extra_fragment);
                 }
                 // Not a trait item; just return what we found.
-                Res::Primitive(ty) => {
-                    if extra_fragment.is_some() {
-                        return Err(ErrorKind::AnchorFailure(
-                            AnchorFailure::RustdocAnchorConflict(res),
-                        ));
-                    }
-                    return Ok((res, Some(ty.as_sym().to_string())));
-                }
                 _ => return Ok((res, extra_fragment.clone())),
             }
         }
@@ -517,6 +502,8 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
                 let (res, fragment, side_channel) =
                     self.resolve_associated_item(ty_res, item_name, ns, module_id)?;
                 let result = if extra_fragment.is_some() {
+                    // NOTE: can never be a primitive since `side_channel.is_none()` only when `res`
+                    // is a trait (and the side channel DefId is always an associated item).
                     let diag_res = side_channel.map_or(res, |(k, r)| Res::Def(k, r));
                     Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(diag_res)))
                 } else {
@@ -1152,7 +1139,7 @@ impl LinkCollector<'_, '_> {
             module_id = DefId { krate, index: CRATE_DEF_INDEX };
         }
 
-        let (mut res, mut fragment) = self.resolve_with_disambiguator_cached(
+        let (mut res, fragment) = self.resolve_with_disambiguator_cached(
             ResolutionInfo {
                 module_id,
                 dis: disambiguator,
@@ -1174,16 +1161,7 @@ impl LinkCollector<'_, '_> {
             if let Some(prim) = resolve_primitive(path_str, TypeNS) {
                 // `prim@char`
                 if matches!(disambiguator, Some(Disambiguator::Primitive)) {
-                    if fragment.is_some() {
-                        anchor_failure(
-                            self.cx,
-                            diag_info,
-                            AnchorFailure::RustdocAnchorConflict(prim),
-                        );
-                        return None;
-                    }
                     res = prim;
-                    fragment = Some(prim.name(self.cx.tcx).to_string());
                 } else {
                     // `[char]` when a `char` module is in scope
                     let candidates = vec![res, prim];
@@ -1303,12 +1281,17 @@ impl LinkCollector<'_, '_> {
                     }
                 }
 
-                Some(ItemLink { link: ori_link.link, link_text, did: None, fragment })
+                Some(ItemLink {
+                    link: ori_link.link,
+                    link_text,
+                    did: res.def_id(self.cx.tcx),
+                    fragment,
+                })
             }
             Res::Def(kind, id) => {
                 verify(kind, id)?;
                 let id = clean::register_res(self.cx, rustc_hir::def::Res::Def(kind, id));
-                Some(ItemLink { link: ori_link.link, link_text, did: Some(id), fragment })
+                Some(ItemLink { link: ori_link.link, link_text, did: id, fragment })
             }
         }
     }
@@ -2069,8 +2052,11 @@ fn anchor_failure(cx: &DocContext<'_>, diag_info: DiagnosticInfo<'_>, failure: A
             diag.span_label(sp, "invalid anchor");
         }
         if let AnchorFailure::RustdocAnchorConflict(Res::Primitive(_)) = failure {
-            diag.note("this restriction may be lifted in a future release");
-            diag.note("see https://github.com/rust-lang/rust/issues/83083 for more information");
+            if let Some(sp) = sp {
+                span_bug!(sp, "anchors should be allowed now");
+            } else {
+                bug!("anchors should be allowed now");
+            }
         }
     });
 }
@@ -2198,10 +2184,11 @@ fn handle_variant(
     use rustc_middle::ty::DefIdTree;
 
     if extra_fragment.is_some() {
+        // NOTE: `res` can never be a primitive since this function is only called when `tcx.def_kind(res) == DefKind::Variant`.
         return Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(res)));
     }
     cx.tcx
-        .parent(res.def_id())
+        .parent(res.def_id(cx.tcx))
         .map(|parent| {
             let parent_def = Res::Def(DefKind::Enum, parent);
             let variant = cx.tcx.expect_variant_res(res.as_hir_res().unwrap());
diff --git a/src/test/rustdoc-ui/intra-doc/anchors.rs b/src/test/rustdoc-ui/intra-doc/anchors.rs
index 6785cb7abeaee..34e11c7c7b7cf 100644
--- a/src/test/rustdoc-ui/intra-doc/anchors.rs
+++ b/src/test/rustdoc-ui/intra-doc/anchors.rs
@@ -37,13 +37,3 @@ pub fn bar() {}
 /// Damn enum's variants: [Enum::A#whatever].
 //~^ ERROR `Enum::A#whatever` contains an anchor
 pub fn enum_link() {}
-
-/// Primitives?
-///
-/// [u32#hello]
-//~^ ERROR `u32#hello` contains an anchor
-pub fn x() {}
-
-/// [prim@usize#x]
-//~^ ERROR `prim@usize#x` contains an anchor
-pub mod usize {}
diff --git a/src/test/rustdoc-ui/intra-doc/anchors.stderr b/src/test/rustdoc-ui/intra-doc/anchors.stderr
index d63e1ee60b3c5..0d226b277535c 100644
--- a/src/test/rustdoc-ui/intra-doc/anchors.stderr
+++ b/src/test/rustdoc-ui/intra-doc/anchors.stderr
@@ -1,19 +1,3 @@
-error: `prim@usize#x` contains an anchor, but links to builtin types are already anchored
-  --> $DIR/anchors.rs:47:6
-   |
-LL | /// [prim@usize#x]
-   |      ^^^^^^^^^^--
-   |                |
-   |                invalid anchor
-   |
-note: the lint level is defined here
-  --> $DIR/anchors.rs:1:9
-   |
-LL | #![deny(rustdoc::broken_intra_doc_links)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   = note: this restriction may be lifted in a future release
-   = note: see https://github.com/rust-lang/rust/issues/83083 for more information
-
 error: `Foo::f#hola` contains an anchor, but links to fields are already anchored
   --> $DIR/anchors.rs:25:15
    |
@@ -21,6 +5,12 @@ LL | /// Or maybe [Foo::f#hola].
    |               ^^^^^^-----
    |                     |
    |                     invalid anchor
+   |
+note: the lint level is defined here
+  --> $DIR/anchors.rs:1:9
+   |
+LL | #![deny(rustdoc::broken_intra_doc_links)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: `hello#people#!` contains multiple anchors
   --> $DIR/anchors.rs:31:28
@@ -38,16 +28,5 @@ LL | /// Damn enum's variants: [Enum::A#whatever].
    |                                   |
    |                                   invalid anchor
 
-error: `u32#hello` contains an anchor, but links to builtin types are already anchored
-  --> $DIR/anchors.rs:43:6
-   |
-LL | /// [u32#hello]
-   |      ^^^------
-   |         |
-   |         invalid anchor
-   |
-   = note: this restriction may be lifted in a future release
-   = note: see https://github.com/rust-lang/rust/issues/83083 for more information
-
-error: aborting due to 5 previous errors
+error: aborting due to 3 previous errors
 
diff --git a/src/test/rustdoc/auxiliary/issue-15318.rs b/src/test/rustdoc/auxiliary/issue-15318.rs
index 83cc31b587c29..0e1977182ceac 100644
--- a/src/test/rustdoc/auxiliary/issue-15318.rs
+++ b/src/test/rustdoc/auxiliary/issue-15318.rs
@@ -1,6 +1,14 @@
 // compile-flags: -Cmetadata=aux
 
 #![doc(html_root_url = "http://example.com/")]
+#![feature(lang_items)]
+#![no_std]
+
+#[lang = "eh_personality"]
+fn foo() {}
+
+#[panic_handler]
+fn bar(_: &core::panic::PanicInfo) -> ! { loop {} }
 
 /// dox
 #[doc(primitive = "pointer")]
diff --git a/src/test/rustdoc/intra-doc/anchors.rs b/src/test/rustdoc/intra-doc/anchors.rs
index 8ec1a7b4f9056..3d4c464960bbc 100644
--- a/src/test/rustdoc/intra-doc/anchors.rs
+++ b/src/test/rustdoc/intra-doc/anchors.rs
@@ -10,3 +10,15 @@ pub struct Something;
 ///
 /// To link to [Something#Anchor!]
 pub struct SomeOtherType;
+
+/// Primitives?
+///
+/// [u32#hello]
+// @has anchors/fn.x.html
+// @has - '//a/@href' '{{channel}}/std/primitive.u32.html#hello'
+pub fn x() {}
+
+/// [prim@usize#x]
+// @has anchors/usize/index.html
+// @has - '//a/@href' '{{channel}}/std/primitive.usize.html#x'
+pub mod usize {}
diff --git a/src/test/rustdoc/intra-doc/auxiliary/my-core.rs b/src/test/rustdoc/intra-doc/auxiliary/my-core.rs
index 54e986be9eccf..92cfd46188b01 100644
--- a/src/test/rustdoc/intra-doc/auxiliary/my-core.rs
+++ b/src/test/rustdoc/intra-doc/auxiliary/my-core.rs
@@ -2,6 +2,10 @@
 #![no_core]
 #![crate_type="rlib"]
 
+#[doc(primitive = "char")]
+/// Some char docs
+mod char {}
+
 #[lang = "char"]
 impl char {
     pub fn len_utf8(self) -> usize {
diff --git a/src/test/rustdoc/intra-doc/prim-methods-external-core.rs b/src/test/rustdoc/intra-doc/prim-methods-external-core.rs
index 9347d7bb42819..5a92a28556ede 100644
--- a/src/test/rustdoc/intra-doc/prim-methods-external-core.rs
+++ b/src/test/rustdoc/intra-doc/prim-methods-external-core.rs
@@ -9,8 +9,8 @@
 #![crate_type = "rlib"]
 
 // @has prim_methods_external_core/index.html
-// @has - '//*[@id="main"]//a[@href="{{channel}}/std/primitive.char.html"]' 'char'
-// @has - '//*[@id="main"]//a[@href="{{channel}}/std/primitive.char.html#method.len_utf8"]' 'char::len_utf8'
+// @has - '//*[@id="main"]//a[@href="../my_core/primitive.char.html"]' 'char'
+// @has - '//*[@id="main"]//a[@href="../my_core/primitive.char.html#method.len_utf8"]' 'char::len_utf8'
 
 //! A [`char`] and its [`char::len_utf8`].
 
diff --git a/src/test/rustdoc/intra-doc/prim-methods-local.rs b/src/test/rustdoc/intra-doc/prim-methods-local.rs
index 124faa9a636ff..cfb3c3842ab16 100644
--- a/src/test/rustdoc/intra-doc/prim-methods-local.rs
+++ b/src/test/rustdoc/intra-doc/prim-methods-local.rs
@@ -5,10 +5,13 @@
 
 
 // @has prim_methods_local/index.html
-// @has - '//*[@id="main"]//a[@href="{{channel}}/std/primitive.char.html"]' 'char'
-// @has - '//*[@id="main"]//a[@href="{{channel}}/std/primitive.char.html#method.len_utf8"]' 'char::len_utf8'
+// @has - '//*[@id="main"]//a[@href="primitive.char.html"]' 'char'
+// @has - '//*[@id="main"]//a[@href="primitive.char.html#method.len_utf8"]' 'char::len_utf8'
 
-//! A [`char`] and its [`char::len_utf8`].
+//! A [prim@`char`] and its [`char::len_utf8`].
+
+#[doc(primitive = "char")]
+mod char {}
 
 #[lang = "char"]
 impl char {
diff --git a/src/test/rustdoc/intra-link-prim-self.rs b/src/test/rustdoc/intra-link-prim-self.rs
index 4744c84b6226d..8c47f7ef77e51 100644
--- a/src/test/rustdoc/intra-link-prim-self.rs
+++ b/src/test/rustdoc/intra-link-prim-self.rs
@@ -7,8 +7,8 @@
 /// [Self::f]
 /// [Self::MAX]
 // @has intra_link_prim_self/primitive.usize.html
-// @has - '//a[@href="{{channel}}/std/primitive.usize.html#method.f"]' 'Self::f'
-// @has - '//a[@href="{{channel}}/std/primitive.usize.html#associatedconstant.MAX"]' 'Self::MAX'
+// @has - '//a[@href="primitive.usize.html#method.f"]' 'Self::f'
+// @has - '//a[@href="primitive.usize.html#associatedconstant.MAX"]' 'Self::MAX'
 impl usize {
     /// Some docs
     pub fn f() {}
diff --git a/src/test/rustdoc/issue-15318-2.rs b/src/test/rustdoc/issue-15318-2.rs
index 2af811ad5bbe8..f7f5052a36dd3 100644
--- a/src/test/rustdoc/issue-15318-2.rs
+++ b/src/test/rustdoc/issue-15318-2.rs
@@ -1,5 +1,6 @@
 // aux-build:issue-15318.rs
 // ignore-cross-compile
+#![no_std]
 
 extern crate issue_15318;
 
diff --git a/src/test/rustdoc/no_std-primitive.rs b/src/test/rustdoc/no_std-primitive.rs
new file mode 100644
index 0000000000000..22fd392dd36c3
--- /dev/null
+++ b/src/test/rustdoc/no_std-primitive.rs
@@ -0,0 +1,6 @@
+#![no_std]
+
+/// Link to [intra-doc link][u8]
+// @has 'no_std_primitive/fn.foo.html' '//a[@href="{{channel}}/core/primitive.u8.html"]' 'intra-doc link'
+// @has - '//a[@href="{{channel}}/core/primitive.u8.html"]' 'u8'
+pub fn foo() -> u8 {}
diff --git a/src/test/rustdoc/primitive/no_std.rs b/src/test/rustdoc/primitive/no_std.rs
index 8d05a8e6e0136..52806bd94f9a0 100644
--- a/src/test/rustdoc/primitive/no_std.rs
+++ b/src/test/rustdoc/primitive/no_std.rs
@@ -1,5 +1,6 @@
 #![no_std]
 
 // @has no_std/fn.foo.html '//a/[@href="{{channel}}/core/primitive.u8.html"]' 'u8'
-// Link to [u8]
+// @has no_std/fn.foo.html '//a/[@href="{{channel}}/core/primitive.u8.html"]' 'primitive link'
+/// Link to [primitive link][u8]
 pub fn foo() -> u8 {}

From 7b469202183c1b29db813be42614ef994c099aca Mon Sep 17 00:00:00 2001
From: Joshua Nelson <jyn514@gmail.com>
Date: Tue, 13 Jul 2021 17:41:03 -0400
Subject: [PATCH 4/5] Fix linkcheck issues

Most of these are because alloc uses `#[lang_item]` to define methods,
but core documents primitives before those methods are available.

- Fix rustdoc-js-std test

  For some reason this change made CStr not show up in the results for
  `str,u8`. Since it still shows up for str, and since it wasn't a great
  match for that query anyway, I think this is ok to let slide.

- Add test that all primitives can be linked to
- Enable `doc(primitive)` in `core` as well
- Add linkcheck exception specifically for Windows

  Ideally this would be done automatically by the linkchecker by
  replacing `\\` with forward slashes, but this PR is already a ton of
  work ...

- Don't forcibly fail linkchecking if there's a broken intra-doc link on Windows

  Previously, it would exit with a hard error if a missing file had `::`
  in it. This changes it to report a missing file instead, which allows
  adding an exception.
---
 library/core/src/char/methods.rs       |  2 +-
 library/core/src/slice/mod.rs          |  6 +++---
 src/librustdoc/clean/types.rs          |  2 +-
 src/test/rustdoc-js-std/multi-query.js |  7 ++++---
 src/test/rustdoc/primitive/no_std.rs   | 10 ++++++++++
 src/tools/linkchecker/main.rs          | 26 ++++++++++++++++++++++++++
 6 files changed, 45 insertions(+), 8 deletions(-)

diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs
index 07ffd06511885..b1dd6aef97493 100644
--- a/library/core/src/char/methods.rs
+++ b/library/core/src/char/methods.rs
@@ -24,7 +24,7 @@ impl char {
     /// decoding error.
     ///
     /// It can occur, for example, when giving ill-formed UTF-8 bytes to
-    /// [`String::from_utf8_lossy`](string/struct.String.html#method.from_utf8_lossy).
+    /// [`String::from_utf8_lossy`](../std/string/struct.String.html#method.from_utf8_lossy).
     #[stable(feature = "assoc_char_consts", since = "1.52.0")]
     pub const REPLACEMENT_CHARACTER: char = '\u{FFFD}';
 
diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs
index b67038a7d5c2c..a04e479e8570d 100644
--- a/library/core/src/slice/mod.rs
+++ b/library/core/src/slice/mod.rs
@@ -2257,9 +2257,9 @@ impl<T> [T] {
     /// assert!(match r { Ok(1..=4) => true, _ => false, });
     /// ```
     // Lint rustdoc::broken_intra_doc_links is allowed as `slice::sort_by_key` is
-    // in crate `alloc`, and as such doesn't exists yet when building `core`.
-    // links to downstream crate: #74481. Since primitives are only documented in
-    // libstd (#73423), this never leads to broken links in practice.
+    // in crate `alloc`, and as such doesn't exists yet when building `core`: #74481.
+    // This breaks links when slice is displayed in core, but changing it to use relative links
+    // would break when the item is re-exported. So allow the core links to be broken for now.
     #[allow(rustdoc::broken_intra_doc_links)]
     #[stable(feature = "slice_binary_search_by_key", since = "1.10.0")]
     #[inline]
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index be1532d450150..454602481e3c5 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -1774,7 +1774,7 @@ impl PrimitiveType {
                 debug!(?crate_num, ?crate_name);
                 for &(def_id, prim) in &e.primitives(tcx) {
                     // HACK: try to link to std instead where possible
-                    if crate_name == sym::core && primitive_locations.get(&prim).is_some() {
+                    if crate_name == sym::core && primitive_locations.contains_key(&prim) {
                         continue;
                     }
                     primitive_locations.insert(prim, def_id);
diff --git a/src/test/rustdoc-js-std/multi-query.js b/src/test/rustdoc-js-std/multi-query.js
index 01e54065189c5..1c92d019606b6 100644
--- a/src/test/rustdoc-js-std/multi-query.js
+++ b/src/test/rustdoc-js-std/multi-query.js
@@ -2,8 +2,9 @@ const QUERY = 'str,u8';
 
 const EXPECTED = {
     'others': [
-        { 'path': 'std', 'name': 'str' },
-        { 'path': 'std', 'name': 'u8' },
-        { 'path': 'std::ffi', 'name': 'CStr' },
+        { 'path': 'std', 'name': 'str', 'href': '../std/primitive.str.html' },
+        { 'path': 'std', 'name': 'u8', 'href': '../std/primitive.u8.html'  },
+        { 'path': 'std', 'name': 'str', 'href': '../std/str/index.html' },
+        { 'path': 'std', 'name': 'u8', 'href': '../std/u8/index.html' },
     ],
 };
diff --git a/src/test/rustdoc/primitive/no_std.rs b/src/test/rustdoc/primitive/no_std.rs
index 52806bd94f9a0..f0f70cb6c1881 100644
--- a/src/test/rustdoc/primitive/no_std.rs
+++ b/src/test/rustdoc/primitive/no_std.rs
@@ -1,6 +1,16 @@
 #![no_std]
+#![deny(warnings)]
+#![deny(rustdoc::broken_intra_doc_links)]
 
 // @has no_std/fn.foo.html '//a/[@href="{{channel}}/core/primitive.u8.html"]' 'u8'
 // @has no_std/fn.foo.html '//a/[@href="{{channel}}/core/primitive.u8.html"]' 'primitive link'
 /// Link to [primitive link][u8]
 pub fn foo() -> u8 {}
+
+// Test that all primitives can be linked to.
+/// [isize] [i8] [i16] [i32] [i64] [i128]
+/// [usize] [u8] [u16] [u32] [u64] [u128]
+/// [f32] [f64]
+/// [char] [bool] [str] [slice] [array] [tuple] [unit]
+/// [pointer] [reference] [fn] [never]
+pub fn bar() {}
diff --git a/src/tools/linkchecker/main.rs b/src/tools/linkchecker/main.rs
index 8cbe0a0c2e8e1..94ebbb33e8d8f 100644
--- a/src/tools/linkchecker/main.rs
+++ b/src/tools/linkchecker/main.rs
@@ -30,6 +30,7 @@ use regex::Regex;
 // If at all possible you should use intra-doc links to avoid linkcheck issues. These
 // are cases where that does not work
 // [(generated_documentation_page, &[broken_links])]
+#[rustfmt::skip]
 const LINKCHECK_EXCEPTIONS: &[(&str, &[&str])] = &[
     // These try to link to std::collections, but are defined in alloc
     // https://github.com/rust-lang/rust/issues/74481
@@ -37,6 +38,19 @@ const LINKCHECK_EXCEPTIONS: &[(&str, &[&str])] = &[
     ("std/collections/btree_set/struct.BTreeSet.html", &["#insert-and-complex-keys"]),
     ("alloc/collections/btree_map/struct.BTreeMap.html", &["#insert-and-complex-keys"]),
     ("alloc/collections/btree_set/struct.BTreeSet.html", &["#insert-and-complex-keys"]),
+
+    // These try to link to various things in std, but are defined in core.
+    // The docs in std::primitive use proper intra-doc links, so these seem fine to special-case.
+    // Most these are broken because liballoc uses `#[lang_item]` magic to define things on
+    // primitives that aren't available in core.
+    ("alloc/slice/trait.Join.html", &["#method.join"]),
+    ("alloc/slice/trait.Concat.html", &["#method.concat"]),
+    ("alloc/slice/index.html", &["#method.concat", "#method.join"]),
+    ("alloc/vec/struct.Vec.html", &["#method.sort_by_key", "#method.sort_by_cached_key"]),
+    ("core/primitive.str.html", &["#method.to_ascii_uppercase", "#method.to_ascii_lowercase"]),
+    ("core/primitive.slice.html", &["#method.to_ascii_uppercase", "#method.to_ascii_lowercase",
+                                    "core/slice::sort_by_key", "core\\slice::sort_by_key",
+                                    "#method.sort_by_cached_key"]),
 ];
 
 #[rustfmt::skip]
@@ -376,6 +390,10 @@ impl Checker {
 
     /// Load a file from disk, or from the cache if available.
     fn load_file(&mut self, file: &Path, report: &mut Report) -> (String, &FileEntry) {
+        // https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-
+        #[cfg(windows)]
+        const ERROR_INVALID_NAME: i32 = 123;
+
         let pretty_path =
             file.strip_prefix(&self.root).unwrap_or(&file).to_str().unwrap().to_string();
 
@@ -392,6 +410,14 @@ impl Checker {
                 }
                 Err(e) if e.kind() == ErrorKind::NotFound => FileEntry::Missing,
                 Err(e) => {
+                    // If a broken intra-doc link contains `::`, on windows, it will cause `ERROR_INVALID_NAME` rather than `NotFound`.
+                    // Explicitly check for that so that the broken link can be allowed in `LINKCHECK_EXCEPTIONS`.
+                    #[cfg(windows)]
+                    if e.raw_os_error() == Some(ERROR_INVALID_NAME)
+                        && file.as_os_str().to_str().map_or(false, |s| s.contains("::"))
+                    {
+                        return FileEntry::Missing;
+                    }
                     panic!("unexpected read error for {}: {}", file.display(), e);
                 }
             });

From 86fd2505c2e10fa27a49a81978cdcbabebb581a1 Mon Sep 17 00:00:00 2001
From: Guillaume Gomez <guillaume.gomez@huawei.com>
Date: Wed, 25 Aug 2021 17:57:06 +0200
Subject: [PATCH 5/5] Fix `no_core` and `no_std` rustdoc tests on Windows

This prevents the following (very strange) errors:

```
error: linking with `link.exe` failed: exit code: 1120
  |
  = note: "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Enterprise\\VC\\Tools\\MSVC\\14.29.30133\\bin\\HostX64\\x86\\link.exe" "/DEF:C:\\Users\\runneradmin\\AppData\\Local\\Temp\\rustcJih4fa\\lib.def" "/NOLOGO" "/LARGEADDRESSAWARE" "/SAFESEH" "D:\\a\\rust\\rust\\build\\i686-pc-windows-msvc\\test\\rustdoc\\issue-15318-2\\auxiliary\\issue-15318.issue_15318.0a2a8554-cgu.0.rcgu.o" "D:\\a\\rust\\rust\\build\\i686-pc-windows-msvc\\test\\rustdoc\\issue-15318-2\\auxiliary\\issue-15318.1na9aylmt25n6w3f.rcgu.o" "/LIBPATH:D:\\a\\rust\\rust\\build\\i686-pc-windows-msvc\\native\\rust-test-helpers" "/LIBPATH:D:\\a\\rust\\rust\\build\\i686-pc-windows-msvc\\test\\rustdoc\\issue-15318-2\\auxiliary" "/LIBPATH:D:\\a\\rust\\rust\\build\\i686-pc-windows-msvc\\stage2\\lib\\rustlib\\i686-pc-windows-msvc\\lib" "vcruntime.lib" "ucrt.lib" "/WHOLEARCHIVE:D:\\a\\rust\\rust\\build\\i686-pc-windows-msvc\\stage2\\lib\\rustlib\\i686-pc-windows-msvc\\lib\\librustc_std_workspace_core-78744e1360284b1e.rlib" "/WHOLEARCHIVE:D:\\a\\rust\\rust\\build\\i686-pc-windows-msvc\\stage2\\lib\\rustlib\\i686-pc-windows-msvc\\lib\\libcore-a900fa3d16956226.rlib" "D:\\a\\rust\\rust\\build\\i686-pc-windows-msvc\\stage2\\lib\\rustlib\\i686-pc-windows-msvc\\lib\\libcompiler_builtins-eb97e6b4dfd2f421.rlib" "/NXCOMPAT" "/LIBPATH:D:\\a\\rust\\rust\\build\\i686-pc-windows-msvc\\stage2\\lib\\rustlib\\i686-pc-windows-msvc\\lib" "/OUT:D:\\a\\rust\\rust\\build\\i686-pc-windows-msvc\\test\\rustdoc\\issue-15318-2\\auxiliary\\issue_15318.dll" "/OPT:REF,ICF" "/DLL" "/IMPLIB:D:\\a\\rust\\rust\\build\\i686-pc-windows-msvc\\test\\rustdoc\\issue-15318-2\\auxiliary\\issue_15318.dll.lib" "/DEBUG" "/NATVIS:D:\\a\\rust\\rust\\build\\i686-pc-windows-msvc\\stage2\\lib\\rustlib\\etc\\intrinsic.natvis" "/NATVIS:D:\\a\\rust\\rust\\build\\i686-pc-windows-msvc\\stage2\\lib\\rustlib\\etc\\liballoc.natvis" "/NATVIS:D:\\a\\rust\\rust\\build\\i686-pc-windows-msvc\\stage2\\lib\\rustlib\\etc\\libcore.natvis" "/NATVIS:D:\\a\\rust\\rust\\build\\i686-pc-windows-msvc\\stage2\\lib\\rustlib\\etc\\libstd.natvis"
  = note: LINK : warning LNK4216: Exported entry point __DllMainCRTStartup@12
             Creating library D:\a\rust\rust\build\i686-pc-windows-msvc\test\rustdoc\issue-15318-2\auxiliary\issue_15318.dll.lib and object D:\a\rust\rust\build\i686-pc-windows-msvc\test\rustdoc\issue-15318-2\auxiliary\issue_15318.dll.exp
          libcore-a900fa3d16956226.rlib(core-a900fa3d16956226.core.95dedc69-cgu.0.rcgu.o) : error LNK2019: unresolved external symbol __aulldiv referenced in function __ZN4core3num7dec2flt7decimal7Decimal10left_shift17hfb9b6c23d6ff0383E
          libcompiler_builtins-eb97e6b4dfd2f421.rlib(compiler_builtins-eb97e6b4dfd2f421.compiler_builtins.a5ef280a-cgu.51.rcgu.o) : error LNK2001: unresolved external symbol __aulldiv
          libcore-a900fa3d16956226.rlib(core-a900fa3d16956226.core.95dedc69-cgu.0.rcgu.o) : error LNK2019: unresolved external symbol __aullrem referenced in function __ZN4core3fmt3num14parse_u64_into17h90eb20517ec3bd86E
          D:\a\rust\rust\build\i686-pc-windows-msvc\test\rustdoc\issue-15318-2\auxiliary\issue_15318.dll : fatal error LNK1120: 2 unresolved externals

```
---
 src/test/rustdoc/auxiliary/issue-15318.rs     | 3 ++-
 src/test/rustdoc/cross-crate-primitive-doc.rs | 2 ++
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/src/test/rustdoc/auxiliary/issue-15318.rs b/src/test/rustdoc/auxiliary/issue-15318.rs
index 0e1977182ceac..695fa58ef1d55 100644
--- a/src/test/rustdoc/auxiliary/issue-15318.rs
+++ b/src/test/rustdoc/auxiliary/issue-15318.rs
@@ -1,5 +1,6 @@
+// no-prefer-dynamic
 // compile-flags: -Cmetadata=aux
-
+#![crate_type = "rlib"]
 #![doc(html_root_url = "http://example.com/")]
 #![feature(lang_items)]
 #![no_std]
diff --git a/src/test/rustdoc/cross-crate-primitive-doc.rs b/src/test/rustdoc/cross-crate-primitive-doc.rs
index 709ffa1818066..120b6e9747f4a 100644
--- a/src/test/rustdoc/cross-crate-primitive-doc.rs
+++ b/src/test/rustdoc/cross-crate-primitive-doc.rs
@@ -1,5 +1,7 @@
 // aux-build:primitive-doc.rs
 // compile-flags: --extern-html-root-url=primitive_doc=../ -Z unstable-options
+// ignore-windows
+
 #![feature(no_core)]
 #![no_core]