about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-11-27 14:29:12 +0000
committerbors <bors@rust-lang.org>2021-11-27 14:29:12 +0000
commit686e313a9aa14107c8631ffe48fa09110a7692db (patch)
tree7bfc7136d580c906876d0aba06ab4cc4a77579e4
parent5fd3a5c7c175f228afaf5fc6ff00c177b83d8055 (diff)
parent073b1208f0389f89ade1e60401edc99c7a113a50 (diff)
downloadrust-686e313a9aa14107c8631ffe48fa09110a7692db.tar.gz
rust-686e313a9aa14107c8631ffe48fa09110a7692db.zip
Auto merge of #91288 - matthiaskrgr:rollup-yp5h41r, r=matthiaskrgr
Rollup of 6 pull requests

Successful merges:

 - #83791 (Weaken guarantee around advancing underlying iterators in zip)
 - #90995 (Document non-guarantees for Hash)
 - #91057 (Expand `available_parallelism` docs in anticipation of cgroup quota support)
 - #91062 (rustdoc: Consolidate static-file replacement mechanism)
 - #91208 (Account for incorrect `where T::Assoc = Ty` bound)
 - #91266 (Use non-generic inner function for pointer formatting)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs49
-rw-r--r--compiler/rustc_ast_passes/src/lib.rs1
-rw-r--r--library/core/src/fmt/mod.rs42
-rw-r--r--library/core/src/hash/mod.rs13
-rw-r--r--library/core/src/iter/traits/iterator.rs6
-rw-r--r--library/std/src/thread/mod.rs9
-rw-r--r--src/librustdoc/config.rs2
-rw-r--r--src/librustdoc/error.rs5
-rw-r--r--src/librustdoc/html/layout.rs26
-rw-r--r--src/librustdoc/html/render/context.rs18
-rw-r--r--src/librustdoc/html/render/mod.rs38
-rw-r--r--src/librustdoc/html/render/write_shared.rs86
-rw-r--r--src/librustdoc/html/static/css/rustdoc.css11
-rw-r--r--src/librustdoc/html/static/js/main.js33
-rw-r--r--src/librustdoc/html/static/js/storage.js7
-rw-r--r--src/librustdoc/html/templates/page.html22
-rw-r--r--src/test/rustdoc/static-root-path.rs2
-rw-r--r--src/test/ui/generic-associated-types/equality-bound.rs15
-rw-r--r--src/test/ui/generic-associated-types/equality-bound.stderr43
-rw-r--r--src/test/ui/generic-associated-types/missing-bounds.fixed5
-rw-r--r--src/test/ui/generic-associated-types/missing-bounds.rs5
-rw-r--r--src/test/ui/generic-associated-types/missing-bounds.stderr30
22 files changed, 307 insertions, 161 deletions
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index 1822ba6ec99..efc30121987 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -23,7 +23,7 @@ use rustc_span::symbol::{kw, sym, Ident};
 use rustc_span::Span;
 use rustc_target::spec::abi;
 use std::mem;
-use std::ops::DerefMut;
+use std::ops::{Deref, DerefMut};
 
 const MORE_EXTERN: &str =
     "for more information, visit https://doc.rust-lang.org/std/keyword.extern.html";
@@ -1714,6 +1714,53 @@ fn deny_equality_constraints(
             }
         }
     }
+    // Given `A: Foo, A::Bar = RhsTy`, suggest `A: Foo<Bar = RhsTy>`.
+    if let TyKind::Path(None, full_path) = &predicate.lhs_ty.kind {
+        if let [potential_param, potential_assoc] = &full_path.segments[..] {
+            for param in &generics.params {
+                if param.ident == potential_param.ident {
+                    for bound in &param.bounds {
+                        if let ast::GenericBound::Trait(trait_ref, TraitBoundModifier::None) = bound
+                        {
+                            if let [trait_segment] = &trait_ref.trait_ref.path.segments[..] {
+                                let assoc = pprust::path_to_string(&ast::Path::from_ident(
+                                    potential_assoc.ident,
+                                ));
+                                let ty = pprust::ty_to_string(&predicate.rhs_ty);
+                                let (args, span) = match &trait_segment.args {
+                                    Some(args) => match args.deref() {
+                                        ast::GenericArgs::AngleBracketed(args) => {
+                                            let Some(arg) = args.args.last() else {
+                                                continue;
+                                            };
+                                            (
+                                                format!(", {} = {}", assoc, ty),
+                                                arg.span().shrink_to_hi(),
+                                            )
+                                        }
+                                        _ => continue,
+                                    },
+                                    None => (
+                                        format!("<{} = {}>", assoc, ty),
+                                        trait_segment.span().shrink_to_hi(),
+                                    ),
+                                };
+                                err.multipart_suggestion(
+                                    &format!(
+                                        "if `{}::{}` is an associated type you're trying to set, \
+                                        use the associated type binding syntax",
+                                        trait_segment.ident, potential_assoc.ident,
+                                    ),
+                                    vec![(span, args), (predicate.span, String::new())],
+                                    Applicability::MaybeIncorrect,
+                                );
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
     err.note(
         "see issue #20041 <https://github.com/rust-lang/rust/issues/20041> for more information",
     );
diff --git a/compiler/rustc_ast_passes/src/lib.rs b/compiler/rustc_ast_passes/src/lib.rs
index 47666670b2b..adc4d117b80 100644
--- a/compiler/rustc_ast_passes/src/lib.rs
+++ b/compiler/rustc_ast_passes/src/lib.rs
@@ -6,6 +6,7 @@
 
 #![feature(iter_is_partitioned)]
 #![feature(box_patterns)]
+#![feature(let_else)]
 #![recursion_limit = "256"]
 
 pub mod ast_validation;
diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs
index 80d3270d73c..6fc3cd0b7c4 100644
--- a/library/core/src/fmt/mod.rs
+++ b/library/core/src/fmt/mod.rs
@@ -2186,28 +2186,34 @@ impl Display for char {
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T: ?Sized> Pointer for *const T {
     fn fmt(&self, f: &mut Formatter<'_>) -> Result {
-        let old_width = f.width;
-        let old_flags = f.flags;
-
-        // The alternate flag is already treated by LowerHex as being special-
-        // it denotes whether to prefix with 0x. We use it to work out whether
-        // or not to zero extend, and then unconditionally set it to get the
-        // prefix.
-        if f.alternate() {
-            f.flags |= 1 << (FlagV1::SignAwareZeroPad as u32);
-
-            if f.width.is_none() {
-                f.width = Some((usize::BITS / 4) as usize + 2);
+        /// Since the formatting will be identical for all pointer types, use a non-monomorphized
+        /// implementation for the actual formatting to reduce the amount of codegen work needed
+        fn inner(ptr: *const (), f: &mut Formatter<'_>) -> Result {
+            let old_width = f.width;
+            let old_flags = f.flags;
+
+            // The alternate flag is already treated by LowerHex as being special-
+            // it denotes whether to prefix with 0x. We use it to work out whether
+            // or not to zero extend, and then unconditionally set it to get the
+            // prefix.
+            if f.alternate() {
+                f.flags |= 1 << (FlagV1::SignAwareZeroPad as u32);
+
+                if f.width.is_none() {
+                    f.width = Some((usize::BITS / 4) as usize + 2);
+                }
             }
-        }
-        f.flags |= 1 << (FlagV1::Alternate as u32);
+            f.flags |= 1 << (FlagV1::Alternate as u32);
+
+            let ret = LowerHex::fmt(&(ptr as usize), f);
 
-        let ret = LowerHex::fmt(&(*self as *const () as usize), f);
+            f.width = old_width;
+            f.flags = old_flags;
 
-        f.width = old_width;
-        f.flags = old_flags;
+            ret
+        }
 
-        ret
+        inner(*self as *const (), f)
     }
 }
 
diff --git a/library/core/src/hash/mod.rs b/library/core/src/hash/mod.rs
index 540160bc4c2..3ff84cc9672 100644
--- a/library/core/src/hash/mod.rs
+++ b/library/core/src/hash/mod.rs
@@ -164,6 +164,19 @@ mod sip;
 /// `0xFF` byte to the `Hasher` so that the values `("ab", "c")` and `("a",
 /// "bc")` hash differently.
 ///
+/// ## Portability
+///
+/// Due to differences in endianness and type sizes, data fed by `Hash` to a `Hasher`
+/// should not be considered portable across platforms. Additionally the data passed by most
+/// standard library types should not be considered stable between compiler versions.
+///
+/// This means tests shouldn't probe hard-coded hash values or data fed to a `Hasher` and
+/// instead should check consistency with `Eq`.
+///
+/// Serialization formats intended to be portable between platforms or compiler versions should
+/// either avoid encoding hashes or only rely on `Hash` and `Hasher` implementations that
+/// provide additional guarantees.
+///
 /// [`HashMap`]: ../../std/collections/struct.HashMap.html
 /// [`HashSet`]: ../../std/collections/struct.HashSet.html
 /// [`hash`]: Hash::hash
diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs
index 588758fed9c..f3ef6b3d018 100644
--- a/library/core/src/iter/traits/iterator.rs
+++ b/library/core/src/iter/traits/iterator.rs
@@ -458,8 +458,10 @@ pub trait Iterator {
     /// In other words, it zips two iterators together, into a single one.
     ///
     /// If either iterator returns [`None`], [`next`] from the zipped iterator
-    /// will return [`None`]. If the first iterator returns [`None`], `zip` will
-    /// short-circuit and `next` will not be called on the second iterator.
+    /// will return [`None`].
+    /// If the zipped iterator has no more elements to return then each further attempt to advance
+    /// it will first try to advance the first iterator at most one time and if it still yielded an item
+    /// try to advance the second iterator at most one time.
     ///
     /// # Examples
     ///
diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs
index 39b53b51bfa..343d3ef8dc5 100644
--- a/library/std/src/thread/mod.rs
+++ b/library/std/src/thread/mod.rs
@@ -1460,9 +1460,12 @@ fn _assert_sync_and_send() {
 /// The purpose of this API is to provide an easy and portable way to query
 /// the default amount of parallelism the program should use. Among other things it
 /// does not expose information on NUMA regions, does not account for
-/// differences in (co)processor capabilities, and will not modify the program's
-/// global state in order to more accurately query the amount of available
-/// parallelism.
+/// differences in (co)processor capabilities or current system load,
+/// and will not modify the program's global state in order to more accurately
+/// query the amount of available parallelism.
+///
+/// Where both fixed steady-state and burst limits are available the steady-state
+/// capacity will be used to ensure more predictable latencies.
 ///
 /// Resource limits can be changed during the runtime of a program, therefore the value is
 /// not cached and instead recomputed every time this function is called. It should not be
diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs
index 7c23117cce3..04bcade156a 100644
--- a/src/librustdoc/config.rs
+++ b/src/librustdoc/config.rs
@@ -552,7 +552,7 @@ impl Options {
                     ))
                     .emit();
                 }
-                themes.push(StylePath { path: theme_file, disabled: true });
+                themes.push(StylePath { path: theme_file });
             }
         }
 
diff --git a/src/librustdoc/error.rs b/src/librustdoc/error.rs
index 82d0002b98b..8eadbf63f33 100644
--- a/src/librustdoc/error.rs
+++ b/src/librustdoc/error.rs
@@ -39,7 +39,10 @@ macro_rules! try_none {
         match $e {
             Some(e) => e,
             None => {
-                return Err(Error::new(io::Error::new(io::ErrorKind::Other, "not found"), $file));
+                return Err(<crate::error::Error as crate::docfs::PathError>::new(
+                    io::Error::new(io::ErrorKind::Other, "not found"),
+                    $file,
+                ));
             }
         }
     }};
diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs
index 71d7cc1a09d..3d3fa3aaeaa 100644
--- a/src/librustdoc/html/layout.rs
+++ b/src/librustdoc/html/layout.rs
@@ -2,8 +2,8 @@ use std::path::PathBuf;
 
 use rustc_data_structures::fx::FxHashMap;
 
+use crate::error::Error;
 use crate::externalfiles::ExternalHtml;
-use crate::html::escape::Escape;
 use crate::html::format::{Buffer, Print};
 use crate::html::render::{ensure_trailing_slash, StylePath};
 
@@ -50,10 +50,11 @@ struct PageLayout<'a> {
     static_root_path: &'a str,
     page: &'a Page<'a>,
     layout: &'a Layout,
-    style_files: String,
+    themes: Vec<String>,
     sidebar: String,
     content: String,
     krate_with_trailing_slash: String,
+    crate rustdoc_version: &'a str,
 }
 
 crate fn render<T: Print, S: Print>(
@@ -66,29 +67,24 @@ crate fn render<T: Print, S: Print>(
 ) -> String {
     let static_root_path = page.get_static_root_path();
     let krate_with_trailing_slash = ensure_trailing_slash(&layout.krate).to_string();
-    let style_files = style_files
+    let mut themes: Vec<String> = style_files
         .iter()
-        .filter_map(|t| t.path.file_stem().map(|stem| (stem, t.disabled)))
-        .filter_map(|t| t.0.to_str().map(|path| (path, t.1)))
-        .map(|t| {
-            format!(
-                r#"<link rel="stylesheet" type="text/css" href="{}.css" {} {}>"#,
-                Escape(&format!("{}{}{}", static_root_path, t.0, page.resource_suffix)),
-                if t.1 { "disabled" } else { "" },
-                if t.0 == "light" { "id=\"themeStyle\"" } else { "" }
-            )
-        })
-        .collect::<String>();
+        .map(StylePath::basename)
+        .collect::<Result<_, Error>>()
+        .unwrap_or_default();
+    themes.sort();
+    let rustdoc_version = rustc_interface::util::version_str().unwrap_or("unknown version");
     let content = Buffer::html().to_display(t); // Note: This must happen before making the sidebar.
     let sidebar = Buffer::html().to_display(sidebar);
     let teractx = tera::Context::from_serialize(PageLayout {
         static_root_path,
         page,
         layout,
-        style_files,
+        themes,
         sidebar,
         content,
         krate_with_trailing_slash,
+        rustdoc_version,
     })
     .unwrap();
     templates.render("page.html", &teractx).unwrap()
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index 069862efde6..365d959ad9f 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -504,9 +504,9 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
         // by the browser as the theme stylesheet. The theme system (hackily) works by
         // changing the href to this stylesheet. All other themes are disabled to
         // prevent rule conflicts
-        scx.style_files.push(StylePath { path: PathBuf::from("light.css"), disabled: false });
-        scx.style_files.push(StylePath { path: PathBuf::from("dark.css"), disabled: true });
-        scx.style_files.push(StylePath { path: PathBuf::from("ayu.css"), disabled: true });
+        scx.style_files.push(StylePath { path: PathBuf::from("light.css") });
+        scx.style_files.push(StylePath { path: PathBuf::from("dark.css") });
+        scx.style_files.push(StylePath { path: PathBuf::from("ayu.css") });
 
         let dst = output;
         scx.ensure_dir(&dst)?;
@@ -596,9 +596,13 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
         page.description = "Settings of Rustdoc";
         page.root_path = "./";
 
-        let mut style_files = self.shared.style_files.clone();
         let sidebar = "<h2 class=\"location\">Settings</h2><div class=\"sidebar-elems\"></div>";
-        style_files.push(StylePath { path: PathBuf::from("settings.css"), disabled: false });
+        let theme_names: Vec<String> = self
+            .shared
+            .style_files
+            .iter()
+            .map(StylePath::basename)
+            .collect::<Result<_, Error>>()?;
         let v = layout::render(
             &self.shared.templates,
             &self.shared.layout,
@@ -607,9 +611,9 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
             settings(
                 self.shared.static_root_path.as_deref().unwrap_or("./"),
                 &self.shared.resource_suffix,
-                &self.shared.style_files,
+                theme_names,
             )?,
-            &style_files,
+            &self.shared.style_files,
         );
         self.shared.fs.write(settings_file, v)?;
         if let Some(ref redirections) = self.shared.redirections {
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 20a200f0484..08022d526fe 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -64,7 +64,6 @@ use serde::ser::SerializeSeq;
 use serde::{Serialize, Serializer};
 
 use crate::clean::{self, ItemId, RenderedLink, SelfTy};
-use crate::docfs::PathError;
 use crate::error::Error;
 use crate::formats::cache::Cache;
 use crate::formats::item_type::ItemType;
@@ -173,8 +172,12 @@ impl Serialize for TypeWithKind {
 crate struct StylePath {
     /// The path to the theme
     crate path: PathBuf,
-    /// What the `disabled` attribute should be set to in the HTML tag
-    crate disabled: bool,
+}
+
+impl StylePath {
+    pub fn basename(&self) -> Result<String, Error> {
+        Ok(try_none!(try_none!(self.path.file_stem(), &self.path).to_str(), &self.path).to_string())
+    }
 }
 
 fn write_srclink(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer) {
@@ -353,7 +356,7 @@ enum Setting {
         js_data_name: &'static str,
         description: &'static str,
         default_value: &'static str,
-        options: Vec<(String, String)>,
+        options: Vec<String>,
     },
 }
 
@@ -393,10 +396,9 @@ impl Setting {
                 options
                     .iter()
                     .map(|opt| format!(
-                        "<option value=\"{}\" {}>{}</option>",
-                        opt.0,
-                        if opt.0 == default_value { "selected" } else { "" },
-                        opt.1,
+                        "<option value=\"{name}\" {}>{name}</option>",
+                        if opt == default_value { "selected" } else { "" },
+                        name = opt,
                     ))
                     .collect::<String>(),
                 root_path,
@@ -421,18 +423,7 @@ impl<T: Into<Setting>> From<(&'static str, Vec<T>)> for Setting {
     }
 }
 
-fn settings(root_path: &str, suffix: &str, themes: &[StylePath]) -> Result<String, Error> {
-    let theme_names: Vec<(String, String)> = themes
-        .iter()
-        .map(|entry| {
-            let theme =
-                try_none!(try_none!(entry.path.file_stem(), &entry.path).to_str(), &entry.path)
-                    .to_string();
-
-            Ok((theme.clone(), theme))
-        })
-        .collect::<Result<_, Error>>()?;
-
+fn settings(root_path: &str, suffix: &str, theme_names: Vec<String>) -> Result<String, Error> {
     // (id, explanation, default value)
     let settings: &[Setting] = &[
         (
@@ -469,10 +460,11 @@ fn settings(root_path: &str, suffix: &str, themes: &[StylePath]) -> Result<Strin
             <span class=\"in-band\">Rustdoc settings</span>\
         </h1>\
         <div class=\"settings\">{}</div>\
-        <script src=\"{}settings{}.js\"></script>",
+        <link rel=\"stylesheet\" href=\"{root_path}settings{suffix}.css\">\
+        <script src=\"{root_path}settings{suffix}.js\"></script>",
         settings.iter().map(|s| s.display(root_path, suffix)).collect::<String>(),
-        root_path,
-        suffix
+        root_path = root_path,
+        suffix = suffix
     ))
 }
 
diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs
index 2d3b2490677..0d5ba8e80d2 100644
--- a/src/librustdoc/html/render/write_shared.rs
+++ b/src/librustdoc/html/render/write_shared.rs
@@ -181,42 +181,34 @@ pub(super) fn write_shared(
         cx.write_shared(SharedResource::InvocationSpecific { basename: p }, content, &options.emit)
     };
 
-    fn add_background_image_to_css(
-        cx: &Context<'_>,
-        css: &mut String,
-        rule: &str,
-        file: &'static str,
-    ) {
-        css.push_str(&format!(
-            "{} {{ background-image: url({}); }}",
-            rule,
-            SharedResource::ToolchainSpecific { basename: file }
+    // Given "foo.svg", return e.g. "url(\"foo1.58.0.svg\")"
+    fn ver_url(cx: &Context<'_>, basename: &'static str) -> String {
+        format!(
+            "url(\"{}\")",
+            SharedResource::ToolchainSpecific { basename }
                 .path(cx)
                 .file_name()
                 .unwrap()
                 .to_str()
                 .unwrap()
-        ))
+        )
     }
 
-    // Add all the static files. These may already exist, but we just
-    // overwrite them anyway to make sure that they're fresh and up-to-date.
-    let mut rustdoc_css = static_files::RUSTDOC_CSS.to_owned();
-    add_background_image_to_css(
-        cx,
-        &mut rustdoc_css,
-        "details.undocumented[open] > summary::before, \
-         details.rustdoc-toggle[open] > summary::before, \
-         details.rustdoc-toggle[open] > summary.hideme::before",
-        "toggle-minus.svg",
-    );
-    add_background_image_to_css(
+    // We use the AUTOREPLACE mechanism to inject into our static JS and CSS certain
+    // values that are only known at doc build time. Since this mechanism is somewhat
+    // surprising when reading the code, please limit it to rustdoc.css.
+    write_minify(
+        "rustdoc.css",
+        static_files::RUSTDOC_CSS
+            .replace(
+                "/* AUTOREPLACE: */url(\"toggle-minus.svg\")",
+                &ver_url(cx, "toggle-minus.svg"),
+            )
+            .replace("/* AUTOREPLACE: */url(\"toggle-plus.svg\")", &ver_url(cx, "toggle-plus.svg"))
+            .replace("/* AUTOREPLACE: */url(\"down-arrow.svg\")", &ver_url(cx, "down-arrow.svg")),
         cx,
-        &mut rustdoc_css,
-        "details.undocumented > summary::before, details.rustdoc-toggle > summary::before",
-        "toggle-plus.svg",
-    );
-    write_minify("rustdoc.css", rustdoc_css, cx, options)?;
+        options,
+    )?;
 
     // Add all the static files. These may already exist, but we just
     // overwrite them anyway to make sure that they're fresh and up-to-date.
@@ -228,12 +220,12 @@ pub(super) fn write_shared(
     let mut themes: FxHashSet<String> = FxHashSet::default();
 
     for entry in &cx.shared.style_files {
-        let theme = try_none!(try_none!(entry.path.file_stem(), &entry.path).to_str(), &entry.path);
+        let theme = entry.basename()?;
         let extension =
             try_none!(try_none!(entry.path.extension(), &entry.path).to_str(), &entry.path);
 
         // Handle the official themes
-        match theme {
+        match theme.as_str() {
             "light" => write_minify("light.css", static_files::themes::LIGHT, cx, options)?,
             "dark" => write_minify("dark.css", static_files::themes::DARK, cx, options)?,
             "ayu" => write_minify("ayu.css", static_files::themes::AYU, cx, options)?,
@@ -265,26 +257,7 @@ pub(super) fn write_shared(
     let mut themes: Vec<&String> = themes.iter().collect();
     themes.sort();
 
-    // FIXME: this should probably not be a toolchain file since it depends on `--theme`.
-    // But it seems a shame to copy it over and over when it's almost always the same.
-    // Maybe we can change the representation to move this out of main.js?
-    write_minify(
-        "main.js",
-        static_files::MAIN_JS
-            .replace(
-                "/* INSERT THEMES HERE */",
-                &format!(" = {}", serde_json::to_string(&themes).unwrap()),
-            )
-            .replace(
-                "/* INSERT RUSTDOC_VERSION HERE */",
-                &format!(
-                    "rustdoc {}",
-                    rustc_interface::util::version_str().unwrap_or("unknown version")
-                ),
-            ),
-        cx,
-        options,
-    )?;
+    write_minify("main.js", static_files::MAIN_JS, cx, options)?;
     write_minify("search.js", static_files::SEARCH_JS, cx, options)?;
     write_minify("settings.js", static_files::SETTINGS_JS, cx, options)?;
 
@@ -292,18 +265,7 @@ pub(super) fn write_shared(
         write_minify("source-script.js", static_files::sidebar::SOURCE_SCRIPT, cx, options)?;
     }
 
-    {
-        write_minify(
-            "storage.js",
-            format!(
-                "var resourcesSuffix = \"{}\";{}",
-                cx.shared.resource_suffix,
-                static_files::STORAGE_JS
-            ),
-            cx,
-            options,
-        )?;
-    }
+    write_minify("storage.js", static_files::STORAGE_JS, cx, options)?;
 
     if cx.shared.layout.scrape_examples_extension {
         cx.write_minify(
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index 479b5210f9e..fceb508bc4f 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -828,6 +828,7 @@ h2.small-section-header > .anchor {
 	background-color: transparent;
 	background-size: 20px;
 	background-position: calc(100% - 1px) 56%;
+	background-image: /* AUTOREPLACE: */url("down-arrow.svg");
 }
 .search-container > .top-button {
 	position: absolute;
@@ -1610,6 +1611,16 @@ details.rustdoc-toggle[open] > summary.hideme > span {
 	display: none;
 }
 
+details.undocumented[open] > summary::before,
+details.rustdoc-toggle[open] > summary::before,
+details.rustdoc-toggle[open] > summary.hideme::before {
+	background-image: /* AUTOREPLACE: */url("toggle-minus.svg");
+}
+
+details.undocumented > summary::before, details.rustdoc-toggle > summary::before {
+	background-image: /* AUTOREPLACE: */url("toggle-plus.svg");
+}
+
 details.rustdoc-toggle[open] > summary::before,
 details.rustdoc-toggle[open] > summary.hideme::before {
 	width: 17px;
diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js
index c9fa72cbaab..5661d497334 100644
--- a/src/librustdoc/html/static/js/main.js
+++ b/src/librustdoc/html/static/js/main.js
@@ -37,14 +37,29 @@ if (!DOMTokenList.prototype.remove) {
     };
 }
 
-(function () {
-    var rustdocVars = document.getElementById("rustdoc-vars");
-    if (rustdocVars) {
-        window.rootPath = rustdocVars.attributes["data-root-path"].value;
-        window.currentCrate = rustdocVars.attributes["data-current-crate"].value;
-        window.searchJS = rustdocVars.attributes["data-search-js"].value;
-        window.searchIndexJS = rustdocVars.attributes["data-search-index-js"].value;
+// Get a value from the rustdoc-vars div, which is used to convey data from
+// Rust to the JS. If there is no such element, return null.
+function getVar(name) {
+    var el = document.getElementById("rustdoc-vars");
+    if (el) {
+        return el.attributes["data-" + name].value;
+    } else {
+        return null;
     }
+}
+
+// Given a basename (e.g. "storage") and an extension (e.g. ".js"), return a URL
+// for a resource under the root-path, with the resource-suffix.
+function resourcePath(basename, extension) {
+    return getVar("root-path") + basename + getVar("resource-suffix") + extension;
+}
+
+
+(function () {
+    window.rootPath = getVar("root-path");
+    window.currentCrate = getVar("current-crate");
+    window.searchJS =  resourcePath("search", ".js");
+    window.searchIndexJS = resourcePath("search-index", ".js");
     var sidebarVars = document.getElementById("sidebar-vars");
     if (sidebarVars) {
         window.sidebarCurrent = {
@@ -115,7 +130,7 @@ function hideThemeButtonState() {
 (function () {
     var themeChoices = getThemesElement();
     var themePicker = getThemePickerElement();
-    var availableThemes/* INSERT THEMES HERE */;
+    var availableThemes = getVar("themes").split(",");
 
     function switchThemeButtonState() {
         if (themeChoices.style.display === "block") {
@@ -980,7 +995,7 @@ function hideThemeButtonState() {
         var rustdoc_version = document.createElement("span");
         rustdoc_version.className = "bottom";
         var rustdoc_version_code = document.createElement("code");
-        rustdoc_version_code.innerText = "/* INSERT RUSTDOC_VERSION HERE */";
+        rustdoc_version_code.innerText = "rustdoc " + getVar("rustdoc-version");
         rustdoc_version.appendChild(rustdoc_version_code);
 
         container.appendChild(rustdoc_version);
diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js
index 78ed17e6899..606c237aea7 100644
--- a/src/librustdoc/html/static/js/storage.js
+++ b/src/librustdoc/html/static/js/storage.js
@@ -1,5 +1,3 @@
-// From rust:
-/* global resourcesSuffix */
 var darkThemes = ["dark", "ayu"];
 window.currentTheme = document.getElementById("themeStyle");
 window.mainTheme = document.getElementById("mainThemeStyle");
@@ -107,9 +105,8 @@ function getCurrentValue(name) {
 }
 
 function switchTheme(styleElem, mainStyleElem, newTheme, saveTheme) {
-    var fullBasicCss = "rustdoc" + resourcesSuffix + ".css";
-    var fullNewTheme = newTheme + resourcesSuffix + ".css";
-    var newHref = mainStyleElem.href.replace(fullBasicCss, fullNewTheme);
+    var newHref = mainStyleElem.href.replace(
+        /\/rustdoc([^/]*)\.css/, "/" + newTheme + "$1" + ".css");
 
     // If this new value comes from a system setting or from the previously
     // saved theme, no need to save it.
diff --git a/src/librustdoc/html/templates/page.html b/src/librustdoc/html/templates/page.html
index cf57d4cf3aa..2a783c6da57 100644
--- a/src/librustdoc/html/templates/page.html
+++ b/src/librustdoc/html/templates/page.html
@@ -12,7 +12,16 @@
     <link rel="stylesheet" type="text/css" {# -#}
           href="{{static_root_path | safe}}rustdoc{{page.resource_suffix}}.css" {# -#}
           id="mainThemeStyle"> {#- -#}
-    {{- style_files | safe -}}
+    {%- for theme in themes -%}
+        <link rel="stylesheet" type="text/css" {# -#}
+            href="{{static_root_path | safe}}{{theme}}{{page.resource_suffix}}.css" {#- -#}
+        {%- if theme == "light" -%}
+            id="themeStyle"
+        {%- else -%}
+            disabled
+        {%- endif -%}
+        >
+    {%- endfor -%}
     <script id="default-settings" {# -#}
       {% for k, v in layout.default_settings %}
         data-{{k}}="{{v}}"
@@ -49,11 +58,6 @@
             href="{{static_root_path | safe}}favicon{{page.resource_suffix}}.svg"> {#- -#}
     {%- endif -%}
     {{- layout.external_html.in_header | safe -}}
-    <style type="text/css"> {#- -#}
-    #crate-search{ {#- -#}
-        background-image:url("{{static_root_path | safe}}down-arrow{{page.resource_suffix}}.svg"); {#- -#}
-    } {#- -#}
-    </style> {#- -#}
 </head> {#- -#}
 <body class="rustdoc {{page.css_class}}"> {#- -#}
     <!--[if lte IE 11]> {#- -#}
@@ -114,8 +118,10 @@
     <div id="rustdoc-vars" {# -#}
          data-root-path="{{page.root_path | safe}}" {# -#}
          data-current-crate="{{layout.krate}}" {# -#}
-         data-search-index-js="{{page.root_path | safe}}search-index{{page.resource_suffix}}.js" {# -#}
-         data-search-js="{{static_root_path | safe}}search{{page.resource_suffix}}.js"> {#- -#}
+         data-themes="{{themes | join(sep=",") }}" {# -#}
+         data-resource-suffix="{{page.resource_suffix}}" {# -#}
+         data-rustdoc-version="{{rustdoc_version}}" {# -#}
+    > {#- -#}
     </div>
 </body> {#- -#}
 </html> {#- -#}
diff --git a/src/test/rustdoc/static-root-path.rs b/src/test/rustdoc/static-root-path.rs
index 2f7c89c5f1e..f1d49b9fcb2 100644
--- a/src/test/rustdoc/static-root-path.rs
+++ b/src/test/rustdoc/static-root-path.rs
@@ -3,7 +3,7 @@
 // @has static_root_path/struct.SomeStruct.html
 // @matches - '"/cache/main\.js"'
 // @!matches - '"\.\./main\.js"'
-// @matches - '"\.\./search-index\.js"'
+// @matches - 'data-root-path="\.\./"'
 // @!matches - '"/cache/search-index\.js"'
 pub struct SomeStruct;
 
diff --git a/src/test/ui/generic-associated-types/equality-bound.rs b/src/test/ui/generic-associated-types/equality-bound.rs
new file mode 100644
index 00000000000..fcc2da8014f
--- /dev/null
+++ b/src/test/ui/generic-associated-types/equality-bound.rs
@@ -0,0 +1,15 @@
+fn sum<I: Iterator<Item = ()>>(i: I) -> i32 where I::Item = i32 {
+//~^ ERROR equality constraints are not yet supported in `where` clauses
+    panic!()
+}
+fn sum2<I: Iterator>(i: I) -> i32 where I::Item = i32 {
+//~^ ERROR equality constraints are not yet supported in `where` clauses
+    panic!()
+}
+fn sum3<J: Iterator>(i: J) -> i32 where I::Item = i32 {
+//~^ ERROR equality constraints are not yet supported in `where` clauses
+//~| ERROR failed to resolve: use of undeclared type `I`
+    panic!()
+}
+
+fn main() {}
diff --git a/src/test/ui/generic-associated-types/equality-bound.stderr b/src/test/ui/generic-associated-types/equality-bound.stderr
new file mode 100644
index 00000000000..27432641958
--- /dev/null
+++ b/src/test/ui/generic-associated-types/equality-bound.stderr
@@ -0,0 +1,43 @@
+error: equality constraints are not yet supported in `where` clauses
+  --> $DIR/equality-bound.rs:1:51
+   |
+LL | fn sum<I: Iterator<Item = ()>>(i: I) -> i32 where I::Item = i32 {
+   |                                                   ^^^^^^^^^^^^^ not supported
+   |
+   = note: see issue #20041 <https://github.com/rust-lang/rust/issues/20041> for more information
+help: if `Iterator::Item` is an associated type you're trying to set, use the associated type binding syntax
+   |
+LL - fn sum<I: Iterator<Item = ()>>(i: I) -> i32 where I::Item = i32 {
+LL + fn sum<I: Iterator<Item = (), Item = i32>>(i: I) -> i32 where  {
+   | 
+
+error: equality constraints are not yet supported in `where` clauses
+  --> $DIR/equality-bound.rs:5:41
+   |
+LL | fn sum2<I: Iterator>(i: I) -> i32 where I::Item = i32 {
+   |                                         ^^^^^^^^^^^^^ not supported
+   |
+   = note: see issue #20041 <https://github.com/rust-lang/rust/issues/20041> for more information
+help: if `Iterator::Item` is an associated type you're trying to set, use the associated type binding syntax
+   |
+LL - fn sum2<I: Iterator>(i: I) -> i32 where I::Item = i32 {
+LL + fn sum2<I: Iterator<Item = i32>>(i: I) -> i32 where  {
+   | 
+
+error: equality constraints are not yet supported in `where` clauses
+  --> $DIR/equality-bound.rs:9:41
+   |
+LL | fn sum3<J: Iterator>(i: J) -> i32 where I::Item = i32 {
+   |                                         ^^^^^^^^^^^^^ not supported
+   |
+   = note: see issue #20041 <https://github.com/rust-lang/rust/issues/20041> for more information
+
+error[E0433]: failed to resolve: use of undeclared type `I`
+  --> $DIR/equality-bound.rs:9:41
+   |
+LL | fn sum3<J: Iterator>(i: J) -> i32 where I::Item = i32 {
+   |                                         ^ use of undeclared type `I`
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0433`.
diff --git a/src/test/ui/generic-associated-types/missing-bounds.fixed b/src/test/ui/generic-associated-types/missing-bounds.fixed
index 54478d16282..0e234120a51 100644
--- a/src/test/ui/generic-associated-types/missing-bounds.fixed
+++ b/src/test/ui/generic-associated-types/missing-bounds.fixed
@@ -34,11 +34,12 @@ impl<B: std::ops::Add<Output = B>> Add for D<B> {
 
 struct E<B>(B);
 
-impl<B: Add> Add for E<B> where B: Add<Output = B> {
+impl<B: Add> Add for E<B> where B: Add<Output = B>, B: Add<Output = B> {
+    //~^ ERROR equality constraints are not yet supported in `where` clauses
     type Output = Self;
 
     fn add(self, rhs: Self) -> Self {
-        Self(self.0 + rhs.0)
+        Self(self.0 + rhs.0) //~ ERROR mismatched types
     }
 }
 
diff --git a/src/test/ui/generic-associated-types/missing-bounds.rs b/src/test/ui/generic-associated-types/missing-bounds.rs
index 962d2db9476..ffafff5e9f5 100644
--- a/src/test/ui/generic-associated-types/missing-bounds.rs
+++ b/src/test/ui/generic-associated-types/missing-bounds.rs
@@ -34,11 +34,12 @@ impl<B> Add for D<B> {
 
 struct E<B>(B);
 
-impl<B: Add> Add for E<B> where B: Add<Output = B> {
+impl<B: Add> Add for E<B> where <B as Add>::Output = B {
+    //~^ ERROR equality constraints are not yet supported in `where` clauses
     type Output = Self;
 
     fn add(self, rhs: Self) -> Self {
-        Self(self.0 + rhs.0)
+        Self(self.0 + rhs.0) //~ ERROR mismatched types
     }
 }
 
diff --git a/src/test/ui/generic-associated-types/missing-bounds.stderr b/src/test/ui/generic-associated-types/missing-bounds.stderr
index 4d33fe84829..c9603b8d1ea 100644
--- a/src/test/ui/generic-associated-types/missing-bounds.stderr
+++ b/src/test/ui/generic-associated-types/missing-bounds.stderr
@@ -1,3 +1,15 @@
+error: equality constraints are not yet supported in `where` clauses
+  --> $DIR/missing-bounds.rs:37:33
+   |
+LL | impl<B: Add> Add for E<B> where <B as Add>::Output = B {
+   |                                 ^^^^^^^^^^^^^^^^^^^^^^ not supported
+   |
+   = note: see issue #20041 <https://github.com/rust-lang/rust/issues/20041> for more information
+help: if `Output` is an associated type you're trying to set, use the associated type binding syntax
+   |
+LL | impl<B: Add> Add for E<B> where B: Add<Output = B> {
+   |                                 ~~~~~~~~~~~~~~~~~~
+
 error[E0308]: mismatched types
   --> $DIR/missing-bounds.rs:11:11
    |
@@ -43,7 +55,23 @@ help: consider restricting type parameter `B`
 LL | impl<B: std::ops::Add<Output = B>> Add for D<B> {
    |       +++++++++++++++++++++++++++
 
-error: aborting due to 3 previous errors
+error[E0308]: mismatched types
+  --> $DIR/missing-bounds.rs:42:14
+   |
+LL | impl<B: Add> Add for E<B> where <B as Add>::Output = B {
+   |      - this type parameter
+...
+LL |         Self(self.0 + rhs.0)
+   |              ^^^^^^^^^^^^^^ expected type parameter `B`, found associated type
+   |
+   = note: expected type parameter `B`
+             found associated type `<B as Add>::Output`
+help: consider further restricting type parameter `B`
+   |
+LL | impl<B: Add> Add for E<B> where <B as Add>::Output = B, B: Add<Output = B> {
+   |                                                       ++++++++++++++++++++
+
+error: aborting due to 5 previous errors
 
 Some errors have detailed explanations: E0308, E0369.
 For more information about an error, try `rustc --explain E0308`.