Auto merge of #62012 - wesleywiser:const_prop_use_ecx, r=oli-obk

Use ecx for const-prop local storage

r? @oli-obk
diff --git a/Cargo.lock b/Cargo.lock
index 9af1c28..657831b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -45,6 +45,19 @@
 ]
 
 [[package]]
+name = "ammonia"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "html5ever 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tendril 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
 name = "annotate-snippets"
 version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -312,7 +325,7 @@
  "tar 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)",
  "tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "toml 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "url_serde 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -449,7 +462,7 @@
  "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)",
  "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
- "toml 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
@@ -711,6 +724,37 @@
 ]
 
 [[package]]
+name = "darling"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "darling_core 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "darling_macro 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "darling_core"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ident_case 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 0.15.35 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "darling_macro"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "darling_core 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 0.15.35 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
 name = "datafrog"
 version = "2.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1108,7 +1152,6 @@
  "regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)",
- "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -1162,6 +1205,19 @@
 ]
 
 [[package]]
+name = "html5ever"
+version = "0.23.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "markup5ever 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 0.15.35 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
 name = "http"
 version = "0.1.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1180,6 +1236,11 @@
 ]
 
 [[package]]
+name = "ident_case"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
 name = "idna"
 version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1492,6 +1553,22 @@
 ]
 
 [[package]]
+name = "markup5ever"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "phf 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)",
+ "phf_codegen 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_derive 1.0.81 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)",
+ "string_cache 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "string_cache_codegen 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tendril 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
 name = "matches"
 version = "0.1.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1526,30 +1603,30 @@
 
 [[package]]
 name = "mdbook"
-version = "0.2.3"
+version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "ammonia 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ammonia 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "elasticlunr-rs 2.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)",
+ "env_logger 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "handlebars 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "itertools 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "open 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "pulldown-cmark 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pulldown-cmark 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_derive 1.0.81 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)",
  "shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
- "toml-query 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "toml-query 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -2389,7 +2466,7 @@
  "tokio 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-process 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-timer 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "toml 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
@@ -2451,7 +2528,7 @@
 dependencies = [
  "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "mdbook 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
- "mdbook 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mdbook 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -3177,6 +3254,7 @@
  "minifier 0.0.30 (registry+https://github.com/rust-lang/crates.io-index)",
  "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "pulldown-cmark 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc-rayon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -3241,7 +3319,7 @@
  "serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)",
  "structopt 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
  "term 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "toml 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicode_categories 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -3723,9 +3801,11 @@
 name = "tidy"
 version = "0.1.0"
 dependencies = [
+ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)",
+ "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -3936,7 +4016,7 @@
 
 [[package]]
 name = "toml"
-version = "0.5.0"
+version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -3956,14 +4036,26 @@
 
 [[package]]
 name = "toml-query"
-version = "0.7.0"
+version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "is-match 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "toml-query_derive 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "toml-query_derive"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "darling 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 0.15.35 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -4300,6 +4392,7 @@
 "checksum aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9a933f4e58658d7b12defcf96dc5c720f20832deebe3e0a19efd3b6aaeeb9e"
 "checksum aho-corasick 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e6f484ae0c99fec2e858eb6134949117399f222608d84cadb3f58c1f97c2364c"
 "checksum ammonia 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a8b93ecb80665873703bf3b0a77f369c96b183d8e0afaf30a3ff5ff07dfc6409"
+"checksum ammonia 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c799ecf1ad77acb48b643e2f45b12d60ee41576287fc575031aa020de88b8f45"
 "checksum annotate-snippets 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e8bcdcd5b291ce85a78f2b9d082a8de9676c12b1840d386d67bc5eea6f9d2b4e"
 "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
 "checksum arc-swap 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1025aeae2b664ca0ea726a89d574fe8f4e77dd712d443236ad1de00379450cf6"
@@ -4354,6 +4447,9 @@
 "checksum crypto-hash 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "09de9ee0fc255ace04c7fa0763c9395a945c37c8292bb554f8d48361d1dcf1b4"
 "checksum curl 0.4.21 (registry+https://github.com/rust-lang/crates.io-index)" = "a85f2f95f2bd277d316d1aa8a477687ab4a6942258c7db7c89c187534669979c"
 "checksum curl-sys 0.4.18 (registry+https://github.com/rust-lang/crates.io-index)" = "9d91a0052d5b982887d8e829bee0faffc7218ea3c6ebd3d6c2c8f678a93c9a42"
+"checksum darling 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9158d690bc62a3a57c3e45b85e4d50de2008b39345592c64efd79345c7e24be0"
+"checksum darling_core 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)" = "d2a368589465391e127e10c9e3a08efc8df66fd49b87dc8524c764bbe7f2ef82"
+"checksum darling_macro 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)" = "244e8987bd4e174385240cde20a3657f607fb0797563c28255c353b5819a07b1"
 "checksum datafrog 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a0afaad2b26fa326569eb264b1363e8ae3357618c43982b3f285f0774ce76b69"
 "checksum derive-new 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "6ca414e896ae072546f4d789f452daaecf60ddee4c9df5dc6d5936d769e3d87c"
 "checksum derive_more 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3f57d78cf3bd45270dad4e70c21ec77a960b36c7a841ff9db76aaa775a8fb871"
@@ -4404,8 +4500,10 @@
 "checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77"
 "checksum home 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "80dff82fb58cfbbc617fb9a9184b010be0529201553cda50ad04372bc2333aff"
 "checksum html5ever 0.22.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c213fa6a618dc1da552f54f85cba74b05d8e883c92ec4e89067736938084c26e"
+"checksum html5ever 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5ce65ac8028cf5a287a7dbf6c4e0a6cf2dcf022ed5b167a81bae66ebf599a8b7"
 "checksum http 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "fe67e3678f2827030e89cc4b9e7ecd16d52f132c0b940ab5005f88e821500f6a"
 "checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114"
+"checksum ident_case 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
 "checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e"
 "checksum if_chain 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c3360c7b59e5ffa2653671fb74b4741a5d343c03f331c0a4aeda42b5c2b0ec7d"
 "checksum ignore 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8dc57fa12805f367736a38541ac1a9fc6a52812a0ca959b1d4d4b640a89eb002"
@@ -4440,9 +4538,10 @@
 "checksum macro-utils 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f2c4deaccc2ead6a28c16c0ba82f07d52b6475397415ce40876e559b0b0ea510"
 "checksum maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08cbb6b4fef96b6d77bfc40ec491b1690c779e77b05cd9f07f787ed376fd4c43"
 "checksum markup5ever 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "897636f9850c3eef4905a5540683ed53dc9393860f0846cab2c2ddf9939862ff"
+"checksum markup5ever 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f1af46a727284117e09780d05038b1ce6fc9c76cc6df183c3dae5a8955a25e21"
 "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
 "checksum mdbook 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "90b5a8d7e341ceee5db3882a06078d42661ddcfa2b3687319cc5da76ec4e782f"
-"checksum mdbook 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0ba0d44cb4089c741b9a91f3e5218298a40699c2f3a070a85014eed290c60819"
+"checksum mdbook 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0c8e2c6442721202fb3b5800741aaafcc7076cdd199b326f0a39ce52a042cd37"
 "checksum measureme 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d09de7dafa3aa334bc806447c7e4de69419723312f4b88b80b561dea66601ce8"
 "checksum memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39"
 "checksum memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2ffa2c986de11a9df78620c01eeaaf27d94d3ff02bf81bfcca953102dd0c6ff"
@@ -4611,9 +4710,10 @@
 "checksum tokio-udp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "66268575b80f4a4a710ef83d087fdfeeabdce9b74c797535fbac18a2cb906e92"
 "checksum tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "037ffc3ba0e12a0ab4aca92e5234e0dedeb48fddf6ccd260f1f150a36a9f2445"
 "checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f"
-"checksum toml 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "87c5890a989fa47ecdc7bcb4c63a77a82c18f306714104b1decfd722db17b39e"
+"checksum toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b8c96d7873fa7ef8bdeb3a9cda3ac48389b4154f32b9803b4bc26220b677b039"
 "checksum toml-query 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6854664bfc6df0360c695480836ee90e2d0c965f06db291d10be9344792d43e8"
-"checksum toml-query 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ab234a943a2363ad774020e2f9474a38a85bc4396bace01a96380144aef17db3"
+"checksum toml-query 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a24369a1894ac8224efcfd567c3d141aea360292f49888e7ec7dcc316527aebb"
+"checksum toml-query_derive 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c99ca245ec273c7e75c8ee58f47b882d0146f3c2c8495158082c6671e8b5335"
 "checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169"
 "checksum ucd-trie 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "71a9c5b1fe77426cf144cc30e49e955270f5086e31a6441dfa8b32efc09b9d77"
 "checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86"
diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs
index eac46c1..2969bbf 100644
--- a/src/bootstrap/doc.rs
+++ b/src/bootstrap/doc.rs
@@ -61,19 +61,19 @@
 // NOTE: When adding a book here, make sure to ALSO build the book by
 // adding a build step in `src/bootstrap/builder.rs`!
 book!(
-    EditionGuide, "src/doc/edition-guide", "edition-guide", RustbookVersion::MdBook2;
-    EmbeddedBook, "src/doc/embedded-book", "embedded-book", RustbookVersion::MdBook2;
-    Nomicon, "src/doc/nomicon", "nomicon", RustbookVersion::MdBook2;
+    EditionGuide, "src/doc/edition-guide", "edition-guide", RustbookVersion::Latest;
+    EmbeddedBook, "src/doc/embedded-book", "embedded-book", RustbookVersion::Latest;
+    Nomicon, "src/doc/nomicon", "nomicon", RustbookVersion::Latest;
     Reference, "src/doc/reference", "reference", RustbookVersion::MdBook1;
-    RustByExample, "src/doc/rust-by-example", "rust-by-example", RustbookVersion::MdBook2;
+    RustByExample, "src/doc/rust-by-example", "rust-by-example", RustbookVersion::Latest;
     RustcBook, "src/doc/rustc", "rustc", RustbookVersion::MdBook1;
-    RustdocBook, "src/doc/rustdoc", "rustdoc", RustbookVersion::MdBook2;
+    RustdocBook, "src/doc/rustdoc", "rustdoc", RustbookVersion::Latest;
 );
 
 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
 enum RustbookVersion {
     MdBook1,
-    MdBook2,
+    Latest,
 }
 
 fn doc_src(builder: &Builder<'_>) -> Interned<PathBuf> {
@@ -108,7 +108,7 @@
             target: self.target,
             name: INTERNER.intern_str("unstable-book"),
             src: builder.md_doc_out(self.target),
-            version: RustbookVersion::MdBook2,
+            version: RustbookVersion::Latest,
         })
     }
 }
@@ -196,7 +196,7 @@
 
         let vers = match self.version {
             RustbookVersion::MdBook1 => "1",
-            RustbookVersion::MdBook2 => "2",
+            RustbookVersion::Latest => "3",
         };
 
         builder.run(rustbook_cmd
@@ -251,7 +251,7 @@
         builder.ensure(RustbookSrc {
             target,
             name: INTERNER.intern_string(name.to_string()),
-            version: RustbookVersion::MdBook2,
+            version: RustbookVersion::Latest,
             src: doc_src(builder),
         });
 
@@ -261,7 +261,7 @@
         builder.ensure(RustbookSrc {
             target,
             name: INTERNER.intern_string(source_name),
-            version: RustbookVersion::MdBook2,
+            version: RustbookVersion::Latest,
             src: doc_src(builder),
         });
 
@@ -269,7 +269,7 @@
         builder.ensure(RustbookSrc {
             target,
             name: INTERNER.intern_string(source_name),
-            version: RustbookVersion::MdBook2,
+            version: RustbookVersion::Latest,
             src: doc_src(builder),
         });
 
@@ -277,7 +277,7 @@
         builder.ensure(RustbookSrc {
             target,
             name: INTERNER.intern_string(source_name),
-            version: RustbookVersion::MdBook2,
+            version: RustbookVersion::Latest,
             src: doc_src(builder),
         });
 
diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs
index 74caaae..2f9bd06 100644
--- a/src/bootstrap/test.rs
+++ b/src/bootstrap/test.rs
@@ -709,8 +709,8 @@
         if !builder.config.vendor {
             cmd.arg("--no-vendor");
         }
-        if !builder.config.verbose_tests {
-            cmd.arg("--quiet");
+        if builder.is_verbose() {
+            cmd.arg("--verbose");
         }
 
         let _folder = builder.fold_output(|| "tidy");
diff --git a/src/doc/book b/src/doc/book
index 62a8c6f..9aacfcc 160000
--- a/src/doc/book
+++ b/src/doc/book
@@ -1 +1 @@
-Subproject commit 62a8c6f25fbd981c80a046f3b04be9684749af3b
+Subproject commit 9aacfcc4c5b102c8cda195932addefd32fe955d2
diff --git a/src/doc/edition-guide b/src/doc/edition-guide
index 581c6cc..f8072ac 160000
--- a/src/doc/edition-guide
+++ b/src/doc/edition-guide
@@ -1 +1 @@
-Subproject commit 581c6cccfaf995394ea9dcac362dc8e731c18558
+Subproject commit f8072acde5ce29c7570d7986180bbded2d22e287
diff --git a/src/doc/embedded-book b/src/doc/embedded-book
index f0c75b7..ef27b51 160000
--- a/src/doc/embedded-book
+++ b/src/doc/embedded-book
@@ -1 +1 @@
-Subproject commit f0c75b75f9c18537b78f5d17c1015247e9a49c86
+Subproject commit ef27b517dcd0b990c888c0d7caeff52a5a115619
diff --git a/src/doc/reference b/src/doc/reference
index f8ae436..08ae27a 160000
--- a/src/doc/reference
+++ b/src/doc/reference
@@ -1 +1 @@
-Subproject commit f8ae436d936f6f4891d3c1bbb1af5865eb8aeadb
+Subproject commit 08ae27a4921ca53967656a7391c82f6c0ddd1ccc
diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example
index d8eec1d..b274729 160000
--- a/src/doc/rust-by-example
+++ b/src/doc/rust-by-example
@@ -1 +1 @@
-Subproject commit d8eec1dd65470b9a68e80ac1cba8fad0daac4916
+Subproject commit b27472962986e85c94f4183b1a6d2207660d3ed6
diff --git a/src/doc/rustc-guide b/src/doc/rustc-guide
index 3ac9cfc..f55e97c 160000
--- a/src/doc/rustc-guide
+++ b/src/doc/rustc-guide
@@ -1 +1 @@
-Subproject commit 3ac9cfc9c9ab2e366feebf18718112737f572352
+Subproject commit f55e97c145cf37fd664db2e0e2f2d05df328bf4f
diff --git a/src/doc/rustc/src/linker-plugin-lto.md b/src/doc/rustc/src/linker-plugin-lto.md
index 73a2efc..2ae726c 100644
--- a/src/doc/rustc/src/linker-plugin-lto.md
+++ b/src/doc/rustc/src/linker-plugin-lto.md
@@ -100,9 +100,10 @@
 
 The following table shows known good combinations of toolchain versions.
 
-|           | Clang 7   | Clang 8   |
+|           |  Clang 7  |  Clang 8  |
 |-----------|-----------|-----------|
 | Rust 1.34 |     ✗     |     ✓     |
-| Rust 1.35 |     ✗     |    ✓(?)   |
+| Rust 1.35 |     ✗     |     ✓     |
+| Rust 1.36 |     ✗     |     ✓     |
 
 Note that the compatibility policy for this feature might change in the future.
diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs
index aeb7f90..f7b0a5e 100644
--- a/src/liballoc/slice.rs
+++ b/src/liballoc/slice.rs
@@ -581,7 +581,9 @@
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     #[rustc_deprecated(since = "1.3.0", reason = "renamed to join")]
-    fn connect(&self, sep: &T) -> Self::Output;
+    fn connect(&self, sep: &T) -> Self::Output {
+        self.join(sep)
+    }
 }
 
 #[unstable(feature = "slice_concat_ext",
@@ -615,10 +617,6 @@
         }
         result
     }
-
-    fn connect(&self, sep: &T) -> Vec<T> {
-        self.join(sep)
-    }
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/src/liballoc/str.rs b/src/liballoc/str.rs
index 0c7d2b8..4010455 100644
--- a/src/liballoc/str.rs
+++ b/src/liballoc/str.rs
@@ -86,10 +86,6 @@
             String::from_utf8_unchecked( join_generic_copy(self, sep.as_bytes()) )
         }
     }
-
-    fn connect(&self, sep: &str) -> String {
-        self.join(sep)
-    }
 }
 
 macro_rules! spezialize_for_lengths {
diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs
index 2f6d745..17ea584 100644
--- a/src/libcore/fmt/mod.rs
+++ b/src/libcore/fmt/mod.rs
@@ -2070,19 +2070,19 @@
     () => ();
     ( $($name:ident,)+ ) => (
         #[stable(feature = "rust1", since = "1.0.0")]
-        impl<$($name:Debug),*> Debug for ($($name,)*) where last_type!($($name,)+): ?Sized {
+        impl<$($name:Debug),+> Debug for ($($name,)+) where last_type!($($name,)+): ?Sized {
             #[allow(non_snake_case, unused_assignments)]
             fn fmt(&self, f: &mut Formatter<'_>) -> Result {
                 let mut builder = f.debug_tuple("");
-                let ($(ref $name,)*) = *self;
+                let ($(ref $name,)+) = *self;
                 $(
                     builder.field(&$name);
-                )*
+                )+
 
                 builder.finish()
             }
         }
-        peel! { $($name,)* }
+        peel! { $($name,)+ }
     )
 }
 
diff --git a/src/libcore/hash/mod.rs b/src/libcore/hash/mod.rs
index 98150fd..38e3864 100644
--- a/src/libcore/hash/mod.rs
+++ b/src/libcore/hash/mod.rs
@@ -617,11 +617,11 @@
 
         ( $($name:ident)+) => (
             #[stable(feature = "rust1", since = "1.0.0")]
-            impl<$($name: Hash),*> Hash for ($($name,)*) where last_type!($($name,)+): ?Sized {
+            impl<$($name: Hash),+> Hash for ($($name,)+) where last_type!($($name,)+): ?Sized {
                 #[allow(non_snake_case)]
                 fn hash<S: Hasher>(&self, state: &mut S) {
-                    let ($(ref $name,)*) = *self;
-                    $($name.hash(state);)*
+                    let ($(ref $name,)+) = *self;
+                    $($name.hash(state);)+
                 }
             }
         );
diff --git a/src/libcore/iter/traits/accum.rs b/src/libcore/iter/traits/accum.rs
index 4eac5cb..adfb639 100644
--- a/src/libcore/iter/traits/accum.rs
+++ b/src/libcore/iter/traits/accum.rs
@@ -72,10 +72,10 @@
     ($($a:ty)*) => (
         integer_sum_product!(@impls 0, 1,
                 #[stable(feature = "iter_arith_traits", since = "1.12.0")],
-                $($a)+);
+                $($a)*);
         integer_sum_product!(@impls Wrapping(0), Wrapping(1),
                 #[stable(feature = "wrapping_iter_arith", since = "1.14.0")],
-                $(Wrapping<$a>)+);
+                $(Wrapping<$a>)*);
     );
 }
 
diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs
index 1bfb852..04c5032 100644
--- a/src/libcore/lib.rs
+++ b/src/libcore/lib.rs
@@ -69,7 +69,6 @@
 #![feature(allow_internal_unstable)]
 #![feature(arbitrary_self_types)]
 #![feature(asm)]
-#![feature(associated_type_defaults)]
 #![feature(bound_cloned)]
 #![feature(cfg_target_has_atomic)]
 #![feature(concat_idents)]
diff --git a/src/libcore/macros.rs b/src/libcore/macros.rs
index 8b44025..589061b 100644
--- a/src/libcore/macros.rs
+++ b/src/libcore/macros.rs
@@ -15,7 +15,7 @@
         $crate::panic!($msg)
     );
     ($fmt:expr, $($arg:tt)+) => ({
-        $crate::panicking::panic_fmt(format_args!($fmt, $($arg)*),
+        $crate::panicking::panic_fmt(format_args!($fmt, $($arg)+),
                                      &(file!(), line!(), __rust_unstable_column!()))
     });
 }
@@ -558,7 +558,7 @@
 #[stable(feature = "rust1", since = "1.0.0")]
 macro_rules! unimplemented {
     () => (panic!("not yet implemented"));
-    ($($arg:tt)+) => (panic!("not yet implemented: {}", format_args!($($arg)*)));
+    ($($arg:tt)+) => (panic!("not yet implemented: {}", format_args!($($arg)+)));
 }
 
 /// Indicates unfinished code.
@@ -617,7 +617,7 @@
 #[unstable(feature = "todo_macro", issue = "59277")]
 macro_rules! todo {
     () => (panic!("not yet implemented"));
-    ($($arg:tt)+) => (panic!("not yet implemented: {}", format_args!($($arg)*)));
+    ($($arg:tt)+) => (panic!("not yet implemented: {}", format_args!($($arg)+)));
 }
 
 /// Creates an array of [`MaybeUninit`].
diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs
index 74f685a..3f4ff7c 100644
--- a/src/libcore/marker.rs
+++ b/src/libcore/marker.rs
@@ -103,7 +103,7 @@
 /// `Unsize` is implemented for:
 ///
 /// - `[T; N]` is `Unsize<[T]>`
-/// - `T` is `Unsize<Trait>` when `T: Trait`
+/// - `T` is `Unsize<dyn Trait>` when `T: Trait`
 /// - `Foo<..., T, ...>` is `Unsize<Foo<..., U, ...>>` if:
 ///   - `T: Unsize<U>`
 ///   - Foo is a struct
diff --git a/src/libcore/ops/arith.rs b/src/libcore/ops/arith.rs
index 8139305..3c009d6 100644
--- a/src/libcore/ops/arith.rs
+++ b/src/libcore/ops/arith.rs
@@ -508,7 +508,7 @@
 pub trait Rem<Rhs=Self> {
     /// The resulting type after applying the `%` operator.
     #[stable(feature = "rust1", since = "1.0.0")]
-    type Output = Self;
+    type Output;
 
     /// Performs the `%` operation.
     #[must_use]
diff --git a/src/libcore/ptr/mod.rs b/src/libcore/ptr/mod.rs
index ba88fde..fccb00d7 100644
--- a/src/libcore/ptr/mod.rs
+++ b/src/libcore/ptr/mod.rs
@@ -2725,12 +2725,12 @@
 
 macro_rules! fnptr_impls_args {
     ($($Arg: ident),+) => {
-        fnptr_impls_safety_abi! { extern "Rust" fn($($Arg),*) -> Ret, $($Arg),* }
-        fnptr_impls_safety_abi! { extern "C" fn($($Arg),*) -> Ret, $($Arg),* }
-        fnptr_impls_safety_abi! { extern "C" fn($($Arg),* , ...) -> Ret, $($Arg),* }
-        fnptr_impls_safety_abi! { unsafe extern "Rust" fn($($Arg),*) -> Ret, $($Arg),* }
-        fnptr_impls_safety_abi! { unsafe extern "C" fn($($Arg),*) -> Ret, $($Arg),* }
-        fnptr_impls_safety_abi! { unsafe extern "C" fn($($Arg),* , ...) -> Ret, $($Arg),* }
+        fnptr_impls_safety_abi! { extern "Rust" fn($($Arg),+) -> Ret, $($Arg),+ }
+        fnptr_impls_safety_abi! { extern "C" fn($($Arg),+) -> Ret, $($Arg),+ }
+        fnptr_impls_safety_abi! { extern "C" fn($($Arg),+ , ...) -> Ret, $($Arg),+ }
+        fnptr_impls_safety_abi! { unsafe extern "Rust" fn($($Arg),+) -> Ret, $($Arg),+ }
+        fnptr_impls_safety_abi! { unsafe extern "C" fn($($Arg),+) -> Ret, $($Arg),+ }
+        fnptr_impls_safety_abi! { unsafe extern "C" fn($($Arg),+ , ...) -> Ret, $($Arg),+ }
     };
     () => {
         // No variadic functions with 0 parameters
diff --git a/src/libproc_macro/bridge/rpc.rs b/src/libproc_macro/bridge/rpc.rs
index 5018be7..5c2f9ec 100644
--- a/src/libproc_macro/bridge/rpc.rs
+++ b/src/libproc_macro/bridge/rpc.rs
@@ -59,7 +59,7 @@
         }
     };
     (enum $name:ident $(<$($T:ident),+>)? { $($variant:ident $(($field:ident))*),* $(,)? }) => {
-        impl<S, $($($T: Encode<S>),+)?> Encode<S> for $name $(<$($T),+>)* {
+        impl<S, $($($T: Encode<S>),+)?> Encode<S> for $name $(<$($T),+>)? {
             fn encode(self, w: &mut Writer, s: &mut S) {
                 // HACK(eddyb): `Tag` enum duplicated between the
                 // two impls as there's no other place to stash it.
@@ -79,8 +79,8 @@
             }
         }
 
-        impl<S, $($($T: for<'s> DecodeMut<'a, 's, S>),+)*> DecodeMut<'a, '_, S>
-            for $name $(<$($T),+>)*
+        impl<S, $($($T: for<'s> DecodeMut<'a, 's, S>),+)?> DecodeMut<'a, '_, S>
+            for $name $(<$($T),+>)?
         {
             fn decode(r: &mut Reader<'a>, s: &mut S) -> Self {
                 // HACK(eddyb): `Tag` enum duplicated between the
diff --git a/src/librustc/hir/itemlikevisit.rs b/src/librustc/hir/itemlikevisit.rs
index bfc9e8f..35b1812 100644
--- a/src/librustc/hir/itemlikevisit.rs
+++ b/src/librustc/hir/itemlikevisit.rs
@@ -51,7 +51,7 @@
     fn visit_impl_item(&mut self, impl_item: &'hir ImplItem);
 }
 
-pub struct DeepVisitor<'v, V: 'v> {
+pub struct DeepVisitor<'v, V> {
     visitor: &'v mut V,
 }
 
diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs
index c87ab68..e1d0d5a 100644
--- a/src/librustc/hir/lowering.rs
+++ b/src/librustc/hir/lowering.rs
@@ -1382,7 +1382,7 @@
             attrs: self.lower_attrs(&arm.attrs),
             pats: arm.pats.iter().map(|x| self.lower_pat(x)).collect(),
             guard: match arm.guard {
-                Some(Guard::If(ref x)) => Some(hir::Guard::If(P(self.lower_expr(x)))),
+                Some(ref x) => Some(hir::Guard::If(P(self.lower_expr(x)))),
                 _ => None,
             },
             body: P(self.lower_expr(&arm.body)),
@@ -4344,53 +4344,147 @@
                 let ohs = P(self.lower_expr(ohs));
                 hir::ExprKind::AddrOf(m, ohs)
             }
-            // More complicated than you might expect because the else branch
-            // might be `if let`.
-            ExprKind::If(ref cond, ref then, ref else_opt) => {
-                // `true => then`:
-                let then_pat = self.pat_bool(e.span, true);
-                let then_blk = self.lower_block(then, false);
-                let then_expr = self.expr_block(then_blk, ThinVec::new());
-                let then_arm = self.arm(hir_vec![then_pat], P(then_expr));
+            ExprKind::Let(ref pats, ref scrutinee) => {
+                // If we got here, the `let` expression is not allowed.
+                self.sess
+                    .struct_span_err(e.span, "`let` expressions are not supported here")
+                    .note("only supported directly in conditions of `if`- and `while`-expressions")
+                    .note("as well as when nested within `&&` and parenthesis in those conditions")
+                    .emit();
 
+                // For better recovery, we emit:
+                // ```
+                // match scrutinee { pats => true, _ => false }
+                // ```
+                // While this doesn't fully match the user's intent, it has key advantages:
+                // 1. We can avoid using `abort_if_errors`.
+                // 2. We can typeck both `pats` and `scrutinee`.
+                // 3. `pats` is allowed to be refutable.
+                // 4. The return type of the block is `bool` which seems like what the user wanted.
+                let scrutinee = self.lower_expr(scrutinee);
+                let then_arm = {
+                    let pats = pats.iter().map(|pat| self.lower_pat(pat)).collect();
+                    let expr = self.expr_bool(e.span, true);
+                    self.arm(pats, P(expr))
+                };
+                let else_arm = {
+                    let pats = hir_vec![self.pat_wild(e.span)];
+                    let expr = self.expr_bool(e.span, false);
+                    self.arm(pats, P(expr))
+                };
+                hir::ExprKind::Match(
+                    P(scrutinee),
+                    vec![then_arm, else_arm].into(),
+                    hir::MatchSource::Normal,
+                )
+            }
+            // FIXME(#53667): handle lowering of && and parens.
+            ExprKind::If(ref cond, ref then, ref else_opt) => {
                 // `_ => else_block` where `else_block` is `{}` if there's `None`:
                 let else_pat = self.pat_wild(e.span);
-                let else_expr = match else_opt {
-                    None => self.expr_block_empty(e.span),
-                    Some(els) => match els.node {
-                        ExprKind::IfLet(..) => {
-                            // Wrap the `if let` expr in a block.
-                            let els = self.lower_expr(els);
-                            let blk = self.block_all(els.span, hir_vec![], Some(P(els)));
-                            self.expr_block(P(blk), ThinVec::new())
-                        }
-                        _ => self.lower_expr(els),
-                    }
+                let (else_expr, contains_else_clause) = match else_opt {
+                    None => (self.expr_block_empty(e.span), false),
+                    Some(els) => (self.lower_expr(els), true),
                 };
                 let else_arm = self.arm(hir_vec![else_pat], P(else_expr));
 
-                // Lower condition:
-                let span_block = self.mark_span_with_reason(IfTemporary, cond.span, None);
-                let cond = self.lower_expr(cond);
-                // Wrap in a construct equivalent to `{ let _t = $cond; _t }` to preserve drop
-                // semantics since `if cond { ... }` don't let temporaries live outside of `cond`.
-                let cond = self.expr_drop_temps(span_block, P(cond), ThinVec::new());
+                // Handle then + scrutinee:
+                let then_blk = self.lower_block(then, false);
+                let then_expr = self.expr_block(then_blk, ThinVec::new());
+                let (then_pats, scrutinee, desugar) = match cond.node {
+                    // `<pat> => <then>`
+                    ExprKind::Let(ref pats, ref scrutinee) => {
+                        let scrutinee = self.lower_expr(scrutinee);
+                        let pats = pats.iter().map(|pat| self.lower_pat(pat)).collect();
+                        let desugar = hir::MatchSource::IfLetDesugar { contains_else_clause };
+                        (pats, scrutinee, desugar)
+                    }
+                    // `true => then`:
+                    _ => {
+                        // Lower condition:
+                        let cond = self.lower_expr(cond);
+                        // Wrap in a construct equivalent to `{ let _t = $cond; _t }`
+                        // to preserve drop semantics since `if cond { ... }`
+                        // don't let temporaries live outside of `cond`.
+                        let span_block = self.mark_span_with_reason(IfTemporary, cond.span, None);
+                        // Wrap in a construct equivalent to `{ let _t = $cond; _t }`
+                        // to preserve drop semantics since `if cond { ... }` does not
+                        // let temporaries live outside of `cond`.
+                        let cond = self.expr_drop_temps(span_block, P(cond), ThinVec::new());
 
-                hir::ExprKind::Match(
-                    P(cond),
-                    vec![then_arm, else_arm].into(),
-                    hir::MatchSource::IfDesugar {
-                        contains_else_clause: else_opt.is_some()
-                    },
-                )
+                        let desugar = hir::MatchSource::IfDesugar { contains_else_clause };
+                        let pats = hir_vec![self.pat_bool(e.span, true)];
+                        (pats, cond, desugar)
+                    }
+                };
+                let then_arm = self.arm(then_pats, P(then_expr));
+
+                hir::ExprKind::Match(P(scrutinee), vec![then_arm, else_arm].into(), desugar)
             }
-            ExprKind::While(ref cond, ref body, opt_label) => self.with_loop_scope(e.id, |this| {
-                hir::ExprKind::While(
-                    this.with_loop_condition_scope(|this| P(this.lower_expr(cond))),
-                    this.lower_block(body, false),
-                    this.lower_label(opt_label),
-                )
-            }),
+            // FIXME(#53667): handle lowering of && and parens.
+            ExprKind::While(ref cond, ref body, opt_label) => {
+                // Desugar `ExprWhileLet`
+                // from: `[opt_ident]: while let <pat> = <sub_expr> <body>`
+                if let ExprKind::Let(ref pats, ref sub_expr) = cond.node {
+                    // to:
+                    //
+                    //   [opt_ident]: loop {
+                    //     match <sub_expr> {
+                    //       <pat> => <body>,
+                    //       _ => break
+                    //     }
+                    //   }
+
+                    // Note that the block AND the condition are evaluated in the loop scope.
+                    // This is done to allow `break` from inside the condition of the loop.
+                    let (body, break_expr, sub_expr) = self.with_loop_scope(e.id, |this| {
+                        (
+                            this.lower_block(body, false),
+                            this.expr_break(e.span, ThinVec::new()),
+                            this.with_loop_condition_scope(|this| P(this.lower_expr(sub_expr))),
+                        )
+                    });
+
+                    // `<pat> => <body>`
+                    let pat_arm = {
+                        let body_expr = P(self.expr_block(body, ThinVec::new()));
+                        let pats = pats.iter().map(|pat| self.lower_pat(pat)).collect();
+                        self.arm(pats, body_expr)
+                    };
+
+                    // `_ => break`
+                    let break_arm = {
+                        let pat_under = self.pat_wild(e.span);
+                        self.arm(hir_vec![pat_under], break_expr)
+                    };
+
+                    // `match <sub_expr> { ... }`
+                    let arms = hir_vec![pat_arm, break_arm];
+                    let match_expr = self.expr(
+                        sub_expr.span,
+                        hir::ExprKind::Match(sub_expr, arms, hir::MatchSource::WhileLetDesugar),
+                        ThinVec::new(),
+                    );
+
+                    // `[opt_ident]: loop { ... }`
+                    let loop_block = P(self.block_expr(P(match_expr)));
+                    let loop_expr = hir::ExprKind::Loop(
+                        loop_block,
+                        self.lower_label(opt_label),
+                        hir::LoopSource::WhileLet,
+                    );
+                    // Add attributes to the outer returned expr node.
+                    loop_expr
+                } else {
+                    self.with_loop_scope(e.id, |this| {
+                        hir::ExprKind::While(
+                            this.with_loop_condition_scope(|this| P(this.lower_expr(cond))),
+                            this.lower_block(body, false),
+                            this.lower_label(opt_label),
+                        )
+                    })
+                }
+            }
             ExprKind::Loop(ref body, opt_label) => self.with_loop_scope(e.id, |this| {
                 hir::ExprKind::Loop(
                     this.lower_block(body, false),
@@ -4703,105 +4797,6 @@
 
             ExprKind::Err => hir::ExprKind::Err,
 
-            // Desugar `ExprIfLet`
-            // from: `if let <pat> = <sub_expr> <body> [<else_opt>]`
-            ExprKind::IfLet(ref pats, ref sub_expr, ref body, ref else_opt) => {
-                // to:
-                //
-                //   match <sub_expr> {
-                //     <pat> => <body>,
-                //     _ => [<else_opt> | ()]
-                //   }
-
-                let mut arms = vec![];
-
-                // `<pat> => <body>`
-                {
-                    let body = self.lower_block(body, false);
-                    let body_expr = P(self.expr_block(body, ThinVec::new()));
-                    let pats = pats.iter().map(|pat| self.lower_pat(pat)).collect();
-                    arms.push(self.arm(pats, body_expr));
-                }
-
-                // _ => [<else_opt>|{}]
-                {
-                    let wildcard_arm: Option<&Expr> = else_opt.as_ref().map(|p| &**p);
-                    let wildcard_pattern = self.pat_wild(e.span);
-                    let body = if let Some(else_expr) = wildcard_arm {
-                        self.lower_expr(else_expr)
-                    } else {
-                        self.expr_block_empty(e.span)
-                    };
-                    arms.push(self.arm(hir_vec![wildcard_pattern], P(body)));
-                }
-
-                let contains_else_clause = else_opt.is_some();
-
-                let sub_expr = P(self.lower_expr(sub_expr));
-
-                hir::ExprKind::Match(
-                    sub_expr,
-                    arms.into(),
-                    hir::MatchSource::IfLetDesugar {
-                        contains_else_clause,
-                    },
-                )
-            }
-
-            // Desugar `ExprWhileLet`
-            // from: `[opt_ident]: while let <pat> = <sub_expr> <body>`
-            ExprKind::WhileLet(ref pats, ref sub_expr, ref body, opt_label) => {
-                // to:
-                //
-                //   [opt_ident]: loop {
-                //     match <sub_expr> {
-                //       <pat> => <body>,
-                //       _ => break
-                //     }
-                //   }
-
-                // Note that the block AND the condition are evaluated in the loop scope.
-                // This is done to allow `break` from inside the condition of the loop.
-                let (body, break_expr, sub_expr) = self.with_loop_scope(e.id, |this| {
-                    (
-                        this.lower_block(body, false),
-                        this.expr_break(e.span, ThinVec::new()),
-                        this.with_loop_condition_scope(|this| P(this.lower_expr(sub_expr))),
-                    )
-                });
-
-                // `<pat> => <body>`
-                let pat_arm = {
-                    let body_expr = P(self.expr_block(body, ThinVec::new()));
-                    let pats = pats.iter().map(|pat| self.lower_pat(pat)).collect();
-                    self.arm(pats, body_expr)
-                };
-
-                // `_ => break`
-                let break_arm = {
-                    let pat_under = self.pat_wild(e.span);
-                    self.arm(hir_vec![pat_under], break_expr)
-                };
-
-                // `match <sub_expr> { ... }`
-                let arms = hir_vec![pat_arm, break_arm];
-                let match_expr = self.expr(
-                    sub_expr.span,
-                    hir::ExprKind::Match(sub_expr, arms, hir::MatchSource::WhileLetDesugar),
-                    ThinVec::new(),
-                );
-
-                // `[opt_ident]: loop { ... }`
-                let loop_block = P(self.block_expr(P(match_expr)));
-                let loop_expr = hir::ExprKind::Loop(
-                    loop_block,
-                    self.lower_label(opt_label),
-                    hir::LoopSource::WhileLet,
-                );
-                // Add attributes to the outer returned expr node.
-                loop_expr
-            }
-
             // Desugar `ExprForLoop`
             // from: `[opt_ident]: for <pat> in <head> <body>`
             ExprKind::ForLoop(ref pat, ref head, ref body, opt_label) => {
@@ -5463,10 +5458,15 @@
         )
     }
 
+    /// Constructs a `true` or `false` literal expression.
+    fn expr_bool(&mut self, span: Span, val: bool) -> hir::Expr {
+        let lit = Spanned { span, node: LitKind::Bool(val) };
+        self.expr(span, hir::ExprKind::Lit(lit), ThinVec::new())
+    }
+
     /// Constructs a `true` or `false` literal pattern.
     fn pat_bool(&mut self, span: Span, val: bool) -> P<hir::Pat> {
-        let lit = Spanned { span, node: LitKind::Bool(val) };
-        let expr = self.expr(span, hir::ExprKind::Lit(lit), ThinVec::new());
+        let expr = self.expr_bool(span, val);
         self.pat(span, hir::PatKind::Lit(P(expr)))
     }
 
diff --git a/src/librustc/hir/map/blocks.rs b/src/librustc/hir/map/blocks.rs
index f50037a..8c5fa97 100644
--- a/src/librustc/hir/map/blocks.rs
+++ b/src/librustc/hir/map/blocks.rs
@@ -15,7 +15,7 @@
 use crate::hir::map;
 use crate::hir::{Expr, FnDecl, Node};
 use crate::hir::intravisit::FnKind;
-use syntax::ast::{Attribute, Ident, NodeId};
+use syntax::ast::{Attribute, Ident};
 use syntax_pos::Span;
 
 /// An FnLikeNode is a Node that is like a fn, in that it has a decl
@@ -83,11 +83,11 @@
     }
 
     /// Attempts to construct a Code from presumed FnLike or Expr node input.
-    pub fn from_node(map: &map::Map<'a>, id: NodeId) -> Option<Code<'a>> {
+    pub fn from_node(map: &map::Map<'a>, id: ast::HirId) -> Option<Code<'a>> {
         match map.get(id) {
             map::Node::Block(_) => {
                 //  Use the parent, hopefully an expression node.
-                Code::from_node(map, map.get_parent_node(id))
+                Code::from_node(map, map.get_parent_node_by_hir_id(id))
             }
             map::Node::Expr(expr) => Some(Code::Expr(expr)),
             node => FnLikeNode::from_node(node).map(Code::FnLike)
diff --git a/src/librustc/hir/map/definitions.rs b/src/librustc/hir/map/definitions.rs
index 6a561f0..b4bda36 100644
--- a/src/librustc/hir/map/definitions.rs
+++ b/src/librustc/hir/map/definitions.rs
@@ -397,11 +397,6 @@
         self.node_to_hir_id[node_id]
     }
 
-    #[inline]
-    pub fn def_index_to_node_id(&self, def_index: DefIndex) -> ast::NodeId {
-        self.as_local_node_id(DefId::local(def_index)).unwrap()
-    }
-
     /// Retrieves the span of the given `DefId` if `DefId` is in the local crate, the span exists
     /// and it's not `DUMMY_SP`.
     #[inline]
diff --git a/src/librustc/hir/map/mod.rs b/src/librustc/hir/map/mod.rs
index 87da327..0b8b69b 100644
--- a/src/librustc/hir/map/mod.rs
+++ b/src/librustc/hir/map/mod.rs
@@ -291,8 +291,8 @@
         self.definitions.def_index_to_hir_id(def_id.to_def_id().index)
     }
 
-    fn def_kind(&self, node_id: NodeId) -> Option<DefKind> {
-        let node = if let Some(node) = self.find(node_id) {
+    fn def_kind(&self, hir_id: HirId) -> Option<DefKind> {
+        let node = if let Some(node) = self.find_by_hir_id(hir_id) {
             node
         } else {
             return None
@@ -347,7 +347,7 @@
                 if variant_data.ctor_hir_id().is_none() {
                     return None;
                 }
-                let ctor_of = match self.find(self.get_parent_node(node_id)) {
+                let ctor_of = match self.find_by_hir_id(self.get_parent_node_by_hir_id(hir_id)) {
                     Some(Node::Item(..)) => def::CtorOf::Struct,
                     Some(Node::Variant(..)) => def::CtorOf::Variant,
                     _ => unreachable!(),
@@ -458,7 +458,7 @@
     }
 
     pub fn body_owner_kind(&self, id: HirId) -> BodyOwnerKind {
-        match self.get_by_hir_id(id) {
+        match self.get(id) {
             Node::Item(&Item { node: ItemKind::Const(..), .. }) |
             Node::TraitItem(&TraitItem { node: TraitItemKind::Const(..), .. }) |
             Node::ImplItem(&ImplItem { node: ImplItemKind::Const(..), .. }) |
@@ -482,7 +482,7 @@
     }
 
     pub fn ty_param_owner(&self, id: HirId) -> HirId {
-        match self.get_by_hir_id(id) {
+        match self.get(id) {
             Node::Item(&Item { node: ItemKind::Trait(..), .. }) |
             Node::Item(&Item { node: ItemKind::TraitAlias(..), .. }) => id,
             Node::GenericParam(_) => self.get_parent_node_by_hir_id(id),
@@ -491,7 +491,7 @@
     }
 
     pub fn ty_param_name(&self, id: HirId) -> Name {
-        match self.get_by_hir_id(id) {
+        match self.get(id) {
             Node::Item(&Item { node: ItemKind::Trait(..), .. }) |
             Node::Item(&Item { node: ItemKind::TraitAlias(..), .. }) => kw::SelfUpper,
             Node::GenericParam(param) => param.name.ident().name,
@@ -561,20 +561,14 @@
     }
 
     /// Retrieves the `Node` corresponding to `id`, panicking if it cannot be found.
-    pub fn get(&self, id: NodeId) -> Node<'hir> {
-        let hir_id = self.node_to_hir_id(id);
-        self.get_by_hir_id(hir_id)
-    }
-
-    // FIXME(@ljedrz): replace the `NodeId` variant.
-    pub fn get_by_hir_id(&self, id: HirId) -> Node<'hir> {
+    pub fn get(&self, id: HirId) -> Node<'hir> {
         // read recorded by `find`
         self.find_by_hir_id(id).unwrap_or_else(||
             bug!("couldn't find hir id {} in the HIR map", id))
     }
 
     pub fn get_if_local(&self, id: DefId) -> Option<Node<'hir>> {
-        self.as_local_node_id(id).map(|id| self.get(id)) // read recorded by `get`
+        self.as_local_hir_id(id).map(|id| self.get(id)) // read recorded by `get`
     }
 
     pub fn get_generics(&self, id: DefId) -> Option<&'hir Generics> {
@@ -846,7 +840,7 @@
             if scope == CRATE_HIR_ID {
                 return Some(CRATE_HIR_ID);
             }
-            match self.get_by_hir_id(scope) {
+            match self.get(scope) {
                 Node::Item(i) => {
                     match i.node {
                         ItemKind::Existential(ExistTy { impl_trait_fn: None, .. }) => {}
@@ -927,28 +921,15 @@
         }
     }
 
-    pub fn expect_expr(&self, id: NodeId) -> &'hir Expr {
-        let hir_id = self.node_to_hir_id(id);
-        self.expect_expr_by_hir_id(hir_id)
-    }
-
-    // FIXME(@ljedrz): replace the `NodeId` variant.
-    pub fn expect_expr_by_hir_id(&self, id: HirId) -> &'hir Expr {
+    pub fn expect_expr(&self, id: HirId) -> &'hir Expr {
         match self.find_by_hir_id(id) { // read recorded by find
             Some(Node::Expr(expr)) => expr,
             _ => bug!("expected expr, found {}", self.node_to_string(id))
         }
     }
 
-    /// Returns the name associated with the given `NodeId`'s AST.
-    pub fn name(&self, id: NodeId) -> Name {
-        let hir_id = self.node_to_hir_id(id);
-        self.name_by_hir_id(hir_id)
-    }
-
-    // FIXME(@ljedrz): replace the `NodeId` variant.
-    pub fn name_by_hir_id(&self, id: HirId) -> Name {
-        match self.get_by_hir_id(id) {
+    pub fn name(&self, id: HirId) -> Name {
+        match self.get(id) {
             Node::Item(i) => i.ident.name,
             Node::ForeignItem(fi) => fi.ident.name,
             Node::ImplItem(ii) => ii.ident.name,
@@ -958,7 +939,7 @@
             Node::Lifetime(lt) => lt.name.ident().name,
             Node::GenericParam(param) => param.name.ident().name,
             Node::Binding(&Pat { node: PatKind::Binding(_, _, l, _), .. }) => l.name,
-            Node::Ctor(..) => self.name_by_hir_id(self.get_parent_item(id)),
+            Node::Ctor(..) => self.name(self.get_parent_item(id)),
             _ => bug!("no name for {}", self.node_to_string(id))
         }
     }
@@ -1080,7 +1061,7 @@
     }
 
     pub fn hir_to_pretty_string(&self, id: HirId) -> String {
-        print::to_string(self, |s| s.print_node(self.get_by_hir_id(id)))
+        print::to_string(self, |s| s.print_node(self.get(id)))
     }
 }
 
@@ -1407,8 +1388,8 @@
 
 pub fn provide(providers: &mut Providers<'_>) {
     providers.def_kind = |tcx, def_id| {
-        if let Some(node_id) = tcx.hir().as_local_node_id(def_id) {
-            tcx.hir().def_kind(node_id)
+        if let Some(hir_id) = tcx.hir().as_local_hir_id(def_id) {
+            tcx.hir().def_kind(hir_id)
         } else {
             bug!("calling local def_kind query provider for upstream DefId: {:?}",
                 def_id
diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs
index 9b144b1..0ddc921 100644
--- a/src/librustc/ich/impls_ty.rs
+++ b/src/librustc/ich/impls_ty.rs
@@ -175,13 +175,18 @@
         hcx: &mut StableHashingContext<'a>,
         hasher: &mut StableHasher<W>,
     ) {
-        self.bytes.hash_stable(hcx, hasher);
-        for reloc in self.relocations.iter() {
+        let mir::interpret::Allocation {
+            bytes, relocations, undef_mask, align, mutability,
+            extra: _,
+        } = self;
+        bytes.hash_stable(hcx, hasher);
+        relocations.len().hash_stable(hcx, hasher);
+        for reloc in relocations.iter() {
             reloc.hash_stable(hcx, hasher);
         }
-        self.undef_mask.hash_stable(hcx, hasher);
-        self.align.hash_stable(hcx, hasher);
-        self.mutability.hash_stable(hcx, hasher);
+        undef_mask.hash_stable(hcx, hasher);
+        align.hash_stable(hcx, hasher);
+        mutability.hash_stable(hcx, hasher);
     }
 }
 
diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs
index 321c068..d1e32b1 100644
--- a/src/librustc/infer/error_reporting/mod.rs
+++ b/src/librustc/infer/error_reporting/mod.rs
@@ -617,7 +617,7 @@
                 }
                 hir::MatchSource::TryDesugar => {
                     if let Some(ty::error::ExpectedFound { expected, .. }) = exp_found {
-                        let discrim_expr = self.tcx.hir().expect_expr_by_hir_id(discrim_hir_id);
+                        let discrim_expr = self.tcx.hir().expect_expr(discrim_hir_id);
                         let discrim_ty = if let hir::ExprKind::Call(_, args) = &discrim_expr.node {
                             let arg_expr = args.first().expect("try desugaring call w/out arg");
                             self.in_progress_tables.and_then(|tables| {
@@ -1335,7 +1335,7 @@
                             // We do this to avoid suggesting code that ends up as `T: 'a'b`,
                             // instead we suggest `T: 'a + 'b` in that case.
                             let mut has_bounds = false;
-                            if let Node::GenericParam(ref param) = hir.get_by_hir_id(id) {
+                            if let Node::GenericParam(ref param) = hir.get(id) {
                                 has_bounds = !param.bounds.is_empty();
                             }
                             let sp = hir.span(id);
@@ -1583,7 +1583,7 @@
                 format!(" for lifetime parameter `{}` in coherence check", name)
             }
             infer::UpvarRegion(ref upvar_id, _) => {
-                let var_name = self.tcx.hir().name_by_hir_id(upvar_id.var_path.hir_id);
+                let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id);
                 format!(" for capture of `{}` by closure", var_name)
             }
             infer::NLL(..) => bug!("NLL variable found in lexical phase"),
diff --git a/src/librustc/infer/error_reporting/need_type_info.rs b/src/librustc/infer/error_reporting/need_type_info.rs
index fe151bd..4426b5c 100644
--- a/src/librustc/infer/error_reporting/need_type_info.rs
+++ b/src/librustc/infer/error_reporting/need_type_info.rs
@@ -133,7 +133,7 @@
         };
 
         if let Some(body_id) = body_id {
-            let expr = self.tcx.hir().expect_expr_by_hir_id(body_id.hir_id);
+            let expr = self.tcx.hir().expect_expr(body_id.hir_id);
             local_visitor.visit_expr(expr);
         }
 
diff --git a/src/librustc/infer/error_reporting/nice_region_error/find_anon_type.rs b/src/librustc/infer/error_reporting/nice_region_error/find_anon_type.rs
index 78d1d569..283af94 100644
--- a/src/librustc/infer/error_reporting/nice_region_error/find_anon_type.rs
+++ b/src/librustc/infer/error_reporting/nice_region_error/find_anon_type.rs
@@ -29,7 +29,7 @@
         if let Some(anon_reg) = self.tcx().is_suitable_region(region) {
             let def_id = anon_reg.def_id;
             if let Some(hir_id) = self.tcx().hir().as_local_hir_id(def_id) {
-                let fndecl = match self.tcx().hir().get_by_hir_id(hir_id) {
+                let fndecl = match self.tcx().hir().get(hir_id) {
                     Node::Item(&hir::Item {
                         node: hir::ItemKind::Fn(ref fndecl, ..),
                         ..
diff --git a/src/librustc/infer/error_reporting/nice_region_error/outlives_closure.rs b/src/librustc/infer/error_reporting/nice_region_error/outlives_closure.rs
index 6ed2b67..f5a4dac 100644
--- a/src/librustc/infer/error_reporting/nice_region_error/outlives_closure.rs
+++ b/src/librustc/infer/error_reporting/nice_region_error/outlives_closure.rs
@@ -52,7 +52,7 @@
                     if let Node::Expr(Expr {
                         node: Closure(_, _, _, closure_span, None),
                         ..
-                    }) = hir.get_by_hir_id(hir_id) {
+                    }) = hir.get(hir_id) {
                         let sup_sp = sup_origin.span();
                         let origin_sp = origin.span();
                         let mut err = self.tcx().sess.struct_span_err(
diff --git a/src/librustc/infer/error_reporting/note.rs b/src/librustc/infer/error_reporting/note.rs
index cc7c13c..caed428 100644
--- a/src/librustc/infer/error_reporting/note.rs
+++ b/src/librustc/infer/error_reporting/note.rs
@@ -31,7 +31,7 @@
                               "...so that reference does not outlive borrowed content");
             }
             infer::ReborrowUpvar(span, ref upvar_id) => {
-                let var_name = self.tcx.hir().name_by_hir_id(upvar_id.var_path.hir_id);
+                let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id);
                 err.span_note(span,
                               &format!("...so that closure can access `{}`", var_name));
             }
@@ -163,7 +163,7 @@
                 err
             }
             infer::ReborrowUpvar(span, ref upvar_id) => {
-                let var_name = self.tcx.hir().name_by_hir_id(upvar_id.var_path.hir_id);
+                let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id);
                 let mut err = struct_span_err!(self.tcx.sess,
                                                span,
                                                E0313,
diff --git a/src/librustc/infer/freshen.rs b/src/librustc/infer/freshen.rs
index 7f4a817..400a538 100644
--- a/src/librustc/infer/freshen.rs
+++ b/src/librustc/infer/freshen.rs
@@ -260,7 +260,7 @@
             ConstValue::Param(_) |
             ConstValue::Scalar(_) |
             ConstValue::Slice { .. } |
-            ConstValue::ByRef(..) |
+            ConstValue::ByRef { .. } |
             ConstValue::Unevaluated(..) => {}
         }
 
diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs
index fc46fe3..5c5e630 100644
--- a/src/librustc/infer/mod.rs
+++ b/src/librustc/infer/mod.rs
@@ -265,7 +265,7 @@
     DerefPointer(Span),
 
     /// Closure bound must not outlive captured variables
-    ClosureCapture(Span, ast::NodeId),
+    ClosureCapture(Span, hir::HirId),
 
     /// Index into slice must be within its lifetime
     IndexSlice(Span),
diff --git a/src/librustc/infer/nll_relate/mod.rs b/src/librustc/infer/nll_relate/mod.rs
index 2148996..a1a93eb 100644
--- a/src/librustc/infer/nll_relate/mod.rs
+++ b/src/librustc/infer/nll_relate/mod.rs
@@ -800,7 +800,7 @@
 /// [blog post]: https://is.gd/0hKvIr
 struct TypeGeneralizer<'me, 'tcx, D>
 where
-    D: TypeRelatingDelegate<'tcx> + 'me,
+    D: TypeRelatingDelegate<'tcx>,
 {
     infcx: &'me InferCtxt<'me, 'tcx>,
 
diff --git a/src/librustc/infer/opaque_types/mod.rs b/src/librustc/infer/opaque_types/mod.rs
index 60554a3..a687b0e 100644
--- a/src/librustc/infer/opaque_types/mod.rs
+++ b/src/librustc/infer/opaque_types/mod.rs
@@ -945,8 +945,8 @@
     let mut hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
     trace!(
         "may_define_existential_type(def={:?}, opaque_node={:?})",
-        tcx.hir().get_by_hir_id(hir_id),
-        tcx.hir().get_by_hir_id(opaque_hir_id)
+        tcx.hir().get(hir_id),
+        tcx.hir().get(opaque_hir_id)
     );
 
     // Named existential types can be defined by any siblings or children of siblings.
diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs
index e2f2799..257d515 100644
--- a/src/librustc/lib.rs
+++ b/src/librustc/lib.rs
@@ -31,7 +31,6 @@
 #![deny(rust_2018_idioms)]
 #![deny(internal)]
 #![deny(unused_lifetimes)]
-#![allow(explicit_outlives_requirements)]
 
 #![feature(arbitrary_self_types)]
 #![feature(box_patterns)]
diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs
index bf054d6..0d59e32 100644
--- a/src/librustc/middle/liveness.rs
+++ b/src/librustc/middle/liveness.rs
@@ -1630,7 +1630,7 @@
                     );
 
                     if self.ir.variable_is_shorthand(var) {
-                        if let Node::Binding(pat) = self.ir.tcx.hir().get_by_hir_id(hir_id) {
+                        if let Node::Binding(pat) = self.ir.tcx.hir().get(hir_id) {
                             // Handle `ref` and `ref mut`.
                             let spans = spans.iter()
                                 .map(|_span| (pat.span, format!("{}: _", name)))
diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs
index c0f56a3..10796ab 100644
--- a/src/librustc/middle/mem_categorization.rs
+++ b/src/librustc/middle/mem_categorization.rs
@@ -344,7 +344,7 @@
         tables: &ty::TypeckTables<'_>,
         id: hir::HirId,
     ) -> MutabilityCategory {
-        let ret = match tcx.hir().get_by_hir_id(id) {
+        let ret = match tcx.hir().get(id) {
             Node::Binding(p) => match p.node {
                 PatKind::Binding(..) => {
                     let bm = *tables.pat_binding_modes()
diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs
index 93cb6ab..d9ccb9d 100644
--- a/src/librustc/middle/region.rs
+++ b/src/librustc/middle/region.rs
@@ -190,7 +190,7 @@
         }
         let span = tcx.hir().span(hir_id);
         if let ScopeData::Remainder(first_statement_index) = self.data {
-            if let Node::Block(ref blk) = tcx.hir().get_by_hir_id(hir_id) {
+            if let Node::Block(ref blk) = tcx.hir().get(hir_id) {
                 // Want span for scope starting after the
                 // indexed statement and ending at end of
                 // `blk`; reuse span of `blk` and shift `lo`
@@ -1368,7 +1368,7 @@
         // If the item is an associated const or a method,
         // record its impl/trait parent, as it can also have
         // lifetime parameters free in this body.
-        match tcx.hir().get_by_hir_id(id) {
+        match tcx.hir().get(id) {
             Node::ImplItem(_) |
             Node::TraitItem(_) => {
                 visitor.scope_tree.root_parent = Some(tcx.hir().get_parent_item(id));
diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs
index f68e18c..b8e7db9 100644
--- a/src/librustc/middle/resolve_lifetime.rs
+++ b/src/librustc/middle/resolve_lifetime.rs
@@ -1488,7 +1488,7 @@
                 }
             }
         };
-        if let Node::Lifetime(hir_lifetime) = self.tcx.hir().get_by_hir_id(lifetime.hir_id) {
+        if let Node::Lifetime(hir_lifetime) = self.tcx.hir().get(lifetime.hir_id) {
             if let Some(parent) = self.tcx.hir().find_by_hir_id(
                 self.tcx.hir().get_parent_item(hir_lifetime.hir_id))
             {
@@ -1569,7 +1569,7 @@
                 Some(LifetimeUseSet::One(lifetime)) => {
                     let hir_id = self.tcx.hir().as_local_hir_id(def_id).unwrap();
                     debug!("hir id first={:?}", hir_id);
-                    if let Some((id, span, name)) = match self.tcx.hir().get_by_hir_id(hir_id) {
+                    if let Some((id, span, name)) = match self.tcx.hir().get(hir_id) {
                         Node::Lifetime(hir_lifetime) => Some((
                             hir_lifetime.hir_id,
                             hir_lifetime.span,
@@ -1620,7 +1620,7 @@
                 }
                 None => {
                     let hir_id = self.tcx.hir().as_local_hir_id(def_id).unwrap();
-                    if let Some((id, span, name)) = match self.tcx.hir().get_by_hir_id(hir_id) {
+                    if let Some((id, span, name)) = match self.tcx.hir().get(hir_id) {
                         Node::Lifetime(hir_lifetime) => Some((
                             hir_lifetime.hir_id,
                             hir_lifetime.span,
@@ -1823,7 +1823,7 @@
                 // Do not free early-bound regions, only late-bound ones.
             } else if let Some(body_id) = outermost_body {
                 let fn_id = self.tcx.hir().body_owner(body_id);
-                match self.tcx.hir().get_by_hir_id(fn_id) {
+                match self.tcx.hir().get(fn_id) {
                     Node::Item(&hir::Item {
                         node: hir::ItemKind::Fn(..),
                         ..
@@ -2052,7 +2052,7 @@
         let mut assoc_item_kind = None;
         let mut impl_self = None;
         let parent = self.tcx.hir().get_parent_node_by_hir_id(output.hir_id);
-        let body = match self.tcx.hir().get_by_hir_id(parent) {
+        let body = match self.tcx.hir().get(parent) {
             // `fn` definitions and methods.
             Node::Item(&hir::Item {
                 node: hir::ItemKind::Fn(.., body),
diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs
index 5a1e521..38df406 100644
--- a/src/librustc/middle/stability.rs
+++ b/src/librustc/middle/stability.rs
@@ -580,7 +580,7 @@
 
             let mut diag = self.struct_span_lint_hir(lint, id, span, &msg);
             if let Some(suggestion) = suggestion {
-                if let hir::Node::Expr(_) = self.hir().get_by_hir_id(id) {
+                if let hir::Node::Expr(_) = self.hir().get(id) {
                     diag.span_suggestion(
                         span,
                         "replace the use of the deprecated item",
diff --git a/src/librustc/middle/weak_lang_items.rs b/src/librustc/middle/weak_lang_items.rs
index b6cd24c..4fb88da 100644
--- a/src/librustc/middle/weak_lang_items.rs
+++ b/src/librustc/middle/weak_lang_items.rs
@@ -147,7 +147,7 @@
         let lang_items = self.lang_items();
         let did = Some(item_def_id);
 
-        $(lang_items.$name() == did)||+
+        $(lang_items.$name() == did)||*
     }
 }
 
diff --git a/src/librustc/mir/interpret/allocation.rs b/src/librustc/mir/interpret/allocation.rs
index d7caf95..8381125 100644
--- a/src/librustc/mir/interpret/allocation.rs
+++ b/src/librustc/mir/interpret/allocation.rs
@@ -247,7 +247,7 @@
         assert_ne!(size.bytes(), 0, "0-sized accesses should never even get a `Pointer`");
         self.check_bounds(cx, ptr, size, CheckInAllocMsg::MemoryAccessTest)?;
 
-        self.mark_definedness(ptr, size, true)?;
+        self.mark_definedness(ptr, size, true);
         self.clear_relocations(cx, ptr, size)?;
 
         AllocationExtra::memory_written(self, ptr, size)?;
@@ -406,7 +406,10 @@
     {
         let val = match val {
             ScalarMaybeUndef::Scalar(scalar) => scalar,
-            ScalarMaybeUndef::Undef => return self.mark_definedness(ptr, type_size, false),
+            ScalarMaybeUndef::Undef => {
+                self.mark_definedness(ptr, type_size, false);
+                return Ok(());
+            },
         };
 
         let bytes = match val.to_bits_or_ptr(type_size, cx) {
@@ -550,16 +553,15 @@
         ptr: Pointer<Tag>,
         size: Size,
         new_state: bool,
-    ) -> InterpResult<'tcx> {
+    ) {
         if size.bytes() == 0 {
-            return Ok(());
+            return;
         }
         self.undef_mask.set_range(
             ptr.offset,
             ptr.offset + size,
             new_state,
         );
-        Ok(())
     }
 }
 
diff --git a/src/librustc/mir/interpret/value.rs b/src/librustc/mir/interpret/value.rs
index 40e8111..388c549 100644
--- a/src/librustc/mir/interpret/value.rs
+++ b/src/librustc/mir/interpret/value.rs
@@ -43,14 +43,21 @@
         end: usize,
     },
 
-    /// An allocation together with a pointer into the allocation.
-    /// Invariant: the pointer's `AllocId` resolves to the allocation.
-    /// The alignment exists to allow `const_field` to have `ByRef` access to nonprimitive fields
-    /// of `repr(packed)` structs. The alignment may be lower than the type of this constant.
-    /// This permits reads with lower alignment than what the type would normally require.
-    /// FIXME(RalfJ,oli-obk): The alignment checks are part of miri, but const eval doesn't really
-    /// need them. Disabling them may be too hard though.
-    ByRef(Pointer, Align, &'tcx Allocation),
+    /// A value not represented/representable by `Scalar` or `Slice`
+    ByRef {
+        /// The alignment exists to allow `const_field` to have `ByRef` access to nonprimitive
+        /// fields of `repr(packed)` structs. The alignment may be lower than the type of this
+        /// constant. This permits reads with lower alignment than what the type would normally
+        /// require.
+        /// FIXME(RalfJ,oli-obk): The alignment checks are part of miri, but const eval doesn't
+        /// really need them. Disabling them may be too hard though.
+        align: Align,
+        /// Offset into `alloc`
+        offset: Size,
+        /// The backing memory of the value, may contain more memory than needed for just the value
+        /// in order to share `Allocation`s between values
+        alloc: &'tcx Allocation,
+    },
 
     /// Used in the HIR by using `Unevaluated` everywhere and later normalizing to one of the other
     /// variants when the code is monomorphic enough for that.
@@ -67,7 +74,7 @@
             ConstValue::Param(_) |
             ConstValue::Infer(_) |
             ConstValue::Placeholder(_) |
-            ConstValue::ByRef(..) |
+            ConstValue::ByRef{ .. } |
             ConstValue::Unevaluated(..) |
             ConstValue::Slice { .. } => None,
             ConstValue::Scalar(val) => Some(val),
diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs
index 6e09cc0..6f9cede 100644
--- a/src/librustc/mir/mod.rs
+++ b/src/librustc/mir/mod.rs
@@ -2573,7 +2573,7 @@
 
                             if let Some(upvars) = tcx.upvars(def_id) {
                                 for (&var_id, place) in upvars.keys().zip(places) {
-                                    let var_name = tcx.hir().name_by_hir_id(var_id);
+                                    let var_name = tcx.hir().name(var_id);
                                     struct_fmt.field(&var_name.as_str(), place);
                                 }
                             }
@@ -2592,7 +2592,7 @@
 
                             if let Some(upvars) = tcx.upvars(def_id) {
                                 for (&var_id, place) in upvars.keys().zip(places) {
-                                    let var_name = tcx.hir().name_by_hir_id(var_id);
+                                    let var_name = tcx.hir().name(var_id);
                                     struct_fmt.field(&var_name.as_str(), place);
                                 }
                             }
diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs
index fc09248..895f9c6 100644
--- a/src/librustc/session/config.rs
+++ b/src/librustc/session/config.rs
@@ -330,14 +330,6 @@
             bug!("Duplicate key in CLI DepTrackingHash: {}", stringify!($opt_name))
         }
     });
-    ($opt_name:ident,
-     $opt_expr:expr,
-     $sub_hashes:expr,
-     [UNTRACKED_WITH_WARNING $warn_val:expr, $warn_text:expr, $error_format:expr]) => ({
-        if *$opt_expr == $warn_val {
-            early_warn($error_format, $warn_text)
-        }
-    });
 }
 
 macro_rules! top_level_options {
@@ -383,10 +375,6 @@
 // [UNTRACKED]
 // Incremental compilation is not influenced by this option.
 //
-// [UNTRACKED_WITH_WARNING(val, warning)]
-// The option is incompatible with incremental compilation in some way. If it
-// has the value `val`, the string `warning` is emitted as a warning.
-//
 // If you add a new option to this struct or one of the sub-structs like
 // CodegenOptions, think about how it influences incremental compilation. If in
 // doubt, specify [TRACKED], which is always "correct" but might lead to
@@ -1163,9 +1151,7 @@
         "a list of extra LLVM passes to run (space separated)"),
     llvm_args: Vec<String> = (Vec::new(), parse_list, [TRACKED],
         "a list of arguments to pass to llvm (space separated)"),
-    save_temps: bool = (false, parse_bool, [UNTRACKED_WITH_WARNING(true,
-        "`-C save-temps` might not produce all requested temporary products \
-         when incremental compilation is enabled.")],
+    save_temps: bool = (false, parse_bool, [UNTRACKED],
         "save all temporary output files during compilation"),
     rpath: bool = (false, parse_bool, [UNTRACKED],
         "set rpath values in libs/exes"),
@@ -1241,9 +1227,7 @@
         "measure time of each rustc pass"),
     time: bool = (false, parse_bool, [UNTRACKED],
         "measure time of rustc processes"),
-    time_llvm_passes: bool = (false, parse_bool, [UNTRACKED_WITH_WARNING(true,
-        "The output of `-Z time-llvm-passes` will only reflect timings of \
-         re-codegened modules when used with incremental compilation" )],
+    time_llvm_passes: bool = (false, parse_bool, [UNTRACKED],
         "measure time of each LLVM pass"),
     input_stats: bool = (false, parse_bool, [UNTRACKED],
         "gather statistics about the input"),
diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs
index 2555833..b433098 100644
--- a/src/librustc/traits/error_reporting.rs
+++ b/src/librustc/traits/error_reporting.rs
@@ -937,9 +937,9 @@
         code: &ObligationCauseCode<'tcx>,
         err: &mut DiagnosticBuilder<'tcx>,
     ) {
-        if let &ObligationCauseCode::VariableType(node_id) = code {
-            let parent_node = self.tcx.hir().get_parent_node(node_id);
-            if let Some(Node::Local(ref local)) = self.tcx.hir().find(parent_node) {
+        if let &ObligationCauseCode::VariableType(hir_id) = code {
+            let parent_node = self.tcx.hir().get_parent_node_by_hir_id(hir_id);
+            if let Some(Node::Local(ref local)) = self.tcx.hir().find_by_hir_id(parent_node) {
                 if let Some(ref expr) = local.init {
                     if let hir::ExprKind::Index(_, _) = expr.node {
                         if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(expr.span) {
diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs
index f5c91a7..8d17df1 100644
--- a/src/librustc/traits/mod.rs
+++ b/src/librustc/traits/mod.rs
@@ -188,7 +188,7 @@
     /// S { ... } must be Sized
     StructInitializerSized,
     /// Type of each variable must be Sized
-    VariableType(ast::NodeId),
+    VariableType(hir::HirId),
     /// Argument type must be Sized
     SizedArgumentType,
     /// Return type must be Sized
diff --git a/src/librustc/ty/constness.rs b/src/librustc/ty/constness.rs
index 65b8322..e4022bb 100644
--- a/src/librustc/ty/constness.rs
+++ b/src/librustc/ty/constness.rs
@@ -67,13 +67,13 @@
 }
 
 
-pub fn provide<'tcx>(providers: &mut Providers<'tcx>) {
+pub fn provide(providers: &mut Providers<'_>) {
     /// only checks whether the function has a `const` modifier
-    fn is_const_fn_raw<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool {
+    fn is_const_fn_raw(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
         let hir_id = tcx.hir().as_local_hir_id(def_id)
                               .expect("Non-local call to local provider is_const_fn");
 
-        let node = tcx.hir().get_by_hir_id(hir_id);
+        let node = tcx.hir().get(hir_id);
         if let Some(fn_like) = FnLikeNode::from_node(node) {
             fn_like.constness() == hir::Constness::Const
         } else if let hir::Node::Ctor(_) = node {
@@ -83,7 +83,7 @@
         }
     }
 
-    fn is_promotable_const_fn<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool {
+    fn is_promotable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
         tcx.is_const_fn(def_id) && match tcx.lookup_stability(def_id) {
             Some(stab) => {
                 if cfg!(debug_assertions) && stab.promotable {
@@ -101,7 +101,7 @@
         }
     }
 
-    fn const_fn_is_allowed_fn_ptr<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool {
+    fn const_fn_is_allowed_fn_ptr(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
         tcx.is_const_fn(def_id) &&
             tcx.lookup_stability(def_id)
                 .map(|stab| stab.allow_const_fn_ptr).unwrap_or(false)
diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs
index b84ebd8..28399ed 100644
--- a/src/librustc/ty/context.rs
+++ b/src/librustc/ty/context.rs
@@ -231,7 +231,7 @@
     pub err: &'tcx Const<'tcx>,
 }
 
-pub struct LocalTableInContext<'a, V: 'a> {
+pub struct LocalTableInContext<'a, V> {
     local_id_root: Option<DefId>,
     data: &'a ItemLocalMap<V>
 }
@@ -294,7 +294,7 @@
     }
 }
 
-pub struct LocalTableInContextMut<'a, V: 'a> {
+pub struct LocalTableInContextMut<'a, V> {
     local_id_root: Option<DefId>,
     data: &'a mut ItemLocalMap<V>
 }
@@ -1610,7 +1610,7 @@
     ) -> Option<Ty<'tcx>> {
         // HACK: `type_of_def_id()` will fail on these (#55796), so return None
         let hir_id = self.hir().as_local_hir_id(scope_def_id).unwrap();
-        match self.hir().get_by_hir_id(hir_id) {
+        match self.hir().get(hir_id) {
             Node::Item(item) => {
                 match item.node {
                     ItemKind::Fn(..) => { /* type_of_def_id() will work */ }
@@ -2171,7 +2171,7 @@
 
 
 /// An entry in an interner.
-struct Interned<'tcx, T: 'tcx+?Sized>(&'tcx T);
+struct Interned<'tcx, T: ?Sized>(&'tcx T);
 
 impl<'tcx, T: 'tcx+?Sized> Clone for Interned<'tcx, T> {
     fn clone(&self) -> Self {
@@ -2223,7 +2223,7 @@
 }
 
 impl<'tcx> Borrow<[CanonicalVarInfo]> for Interned<'tcx, List<CanonicalVarInfo>> {
-    fn borrow<'a>(&'a self) -> &'a [CanonicalVarInfo] {
+    fn borrow(&self) -> &[CanonicalVarInfo] {
         &self.0[..]
     }
 }
@@ -2236,13 +2236,13 @@
 
 impl<'tcx> Borrow<[ProjectionKind]>
     for Interned<'tcx, List<ProjectionKind>> {
-    fn borrow<'a>(&'a self) -> &'a [ProjectionKind] {
+    fn borrow(&self) -> &[ProjectionKind] {
         &self.0[..]
     }
 }
 
 impl<'tcx> Borrow<RegionKind> for Interned<'tcx, RegionKind> {
-    fn borrow<'a>(&'a self) -> &'a RegionKind {
+    fn borrow(&self) -> &RegionKind {
         &self.0
     }
 }
diff --git a/src/librustc/ty/fast_reject.rs b/src/librustc/ty/fast_reject.rs
index 7aab1ae..ee0d33d 100644
--- a/src/librustc/ty/fast_reject.rs
+++ b/src/librustc/ty/fast_reject.rs
@@ -55,8 +55,8 @@
 /// then we can't say much about whether two types would unify. Put another way,
 /// `can_simplify_params` should be true if type parameters appear free in `ty` and `false` if they
 /// are to be considered bound.
-pub fn simplify_type<'tcx>(
-    tcx: TyCtxt<'tcx>,
+pub fn simplify_type(
+    tcx: TyCtxt<'_>,
     ty: Ty<'_>,
     can_simplify_params: bool,
 ) -> Option<SimplifiedType> {
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index 3614103..ad98807 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -213,7 +213,7 @@
         }
     }
 
-    pub fn signature<'tcx>(&self, tcx: TyCtxt<'tcx>) -> String {
+    pub fn signature(&self, tcx: TyCtxt<'_>) -> String {
         match self.kind {
             ty::AssocKind::Method => {
                 // We skip the binder here because the binder would deanonymize all
@@ -2311,7 +2311,7 @@
     /// Returns an iterator over all fields contained
     /// by this ADT.
     #[inline]
-    pub fn all_fields<'s>(&'s self) -> impl Iterator<Item = &'s FieldDef> + Clone {
+    pub fn all_fields(&self) -> impl Iterator<Item=&FieldDef> + Clone {
         self.variants.iter().flat_map(|v| v.fields.iter())
     }
 
@@ -2777,20 +2777,6 @@
         });
     }
 
-    pub fn expr_span(self, id: NodeId) -> Span {
-        match self.hir().find(id) {
-            Some(Node::Expr(e)) => {
-                e.span
-            }
-            Some(f) => {
-                bug!("node-ID {} is not an expr: {:?}", id, f);
-            }
-            None => {
-                bug!("node-ID {} is not present in the node map", id);
-            }
-        }
-    }
-
     pub fn provided_trait_methods(self, id: DefId) -> Vec<AssocItem> {
         self.associated_items(id)
             .filter(|item| item.kind == AssocKind::Method && item.defaultness.has_value())
@@ -2805,7 +2791,7 @@
 
     pub fn opt_associated_item(self, def_id: DefId) -> Option<AssocItem> {
         let is_associated_item = if let Some(hir_id) = self.hir().as_local_hir_id(def_id) {
-            match self.hir().get_by_hir_id(hir_id) {
+            match self.hir().get(hir_id) {
                 Node::TraitItem(_) | Node::ImplItem(_) => true,
                 _ => false,
             }
@@ -3125,7 +3111,7 @@
     }
 }
 
-fn associated_item<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> AssocItem {
+fn associated_item(tcx: TyCtxt<'_>, def_id: DefId) -> AssocItem {
     let id = tcx.hir().as_local_hir_id(def_id).unwrap();
     let parent_id = tcx.hir().get_parent_item(id);
     let parent_def_id = tcx.hir().local_def_id_from_hir_id(parent_id);
@@ -3170,7 +3156,7 @@
 ///       such.
 ///     - a Error, if a type contained itself. The representability
 ///       check should catch this case.
-fn adt_sized_constraint<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> AdtSizedConstraint<'tcx> {
+fn adt_sized_constraint(tcx: TyCtxt<'_>, def_id: DefId) -> AdtSizedConstraint<'_> {
     let def = tcx.adt_def(def_id);
 
     let result = tcx.mk_type_list(def.variants.iter().flat_map(|v| {
@@ -3184,7 +3170,7 @@
     AdtSizedConstraint(result)
 }
 
-fn associated_item_def_ids<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx [DefId] {
+fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: DefId) -> &[DefId] {
     let id = tcx.hir().as_local_hir_id(def_id).unwrap();
     let item = tcx.hir().expect_item(id);
     match item.node {
@@ -3207,14 +3193,14 @@
     }
 }
 
-fn def_span<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Span {
+fn def_span(tcx: TyCtxt<'_>, def_id: DefId) -> Span {
     tcx.hir().span_if_local(def_id).unwrap()
 }
 
 /// If the given `DefId` describes an item belonging to a trait,
 /// returns the `DefId` of the trait that the trait item belongs to;
 /// otherwise, returns `None`.
-fn trait_of_item<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Option<DefId> {
+fn trait_of_item(tcx: TyCtxt<'_>, def_id: DefId) -> Option<DefId> {
     tcx.opt_associated_item(def_id)
         .and_then(|associated_item| {
             match associated_item.container {
@@ -3227,7 +3213,7 @@
 /// Yields the parent function's `DefId` if `def_id` is an `impl Trait` definition.
 pub fn is_impl_trait_defn(tcx: TyCtxt<'_>, def_id: DefId) -> Option<DefId> {
     if let Some(hir_id) = tcx.hir().as_local_hir_id(def_id) {
-        if let Node::Item(item) = tcx.hir().get_by_hir_id(hir_id) {
+        if let Node::Item(item) = tcx.hir().get(hir_id) {
             if let hir::ItemKind::Existential(ref exist_ty) = item.node {
                 return exist_ty.impl_trait_fn;
             }
@@ -3237,7 +3223,7 @@
 }
 
 /// See `ParamEnv` struct definition for details.
-fn param_env<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> ParamEnv<'tcx> {
+fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ParamEnv<'_> {
     // The param_env of an impl Trait type is its defining function's param_env
     if let Some(parent) = is_impl_trait_defn(tcx, def_id) {
         return param_env(tcx, parent);
@@ -3272,17 +3258,17 @@
     traits::normalize_param_env_or_error(tcx, def_id, unnormalized_env, cause)
 }
 
-fn crate_disambiguator<'tcx>(tcx: TyCtxt<'tcx>, crate_num: CrateNum) -> CrateDisambiguator {
+fn crate_disambiguator(tcx: TyCtxt<'_>, crate_num: CrateNum) -> CrateDisambiguator {
     assert_eq!(crate_num, LOCAL_CRATE);
     tcx.sess.local_crate_disambiguator()
 }
 
-fn original_crate_name<'tcx>(tcx: TyCtxt<'tcx>, crate_num: CrateNum) -> Symbol {
+fn original_crate_name(tcx: TyCtxt<'_>, crate_num: CrateNum) -> Symbol {
     assert_eq!(crate_num, LOCAL_CRATE);
     tcx.crate_name.clone()
 }
 
-fn crate_hash<'tcx>(tcx: TyCtxt<'tcx>, crate_num: CrateNum) -> Svh {
+fn crate_hash(tcx: TyCtxt<'_>, crate_num: CrateNum) -> Svh {
     assert_eq!(crate_num, LOCAL_CRATE);
     tcx.hir().crate_hash
 }
@@ -3302,7 +3288,7 @@
 /// If `def_id` is an issue 33140 hack impl, returns its self type; otherwise, returns `None`.
 ///
 /// See [`ImplOverlapKind::Issue33140`] for more details.
-fn issue33140_self_ty<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Option<Ty<'tcx>> {
+fn issue33140_self_ty(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Ty<'_>> {
     debug!("issue33140_self_ty({:?})", def_id);
 
     let trait_ref = tcx.impl_trait_ref(def_id).unwrap_or_else(|| {
diff --git a/src/librustc/ty/print/obsolete.rs b/src/librustc/ty/print/obsolete.rs
index 16fb334..c12402a 100644
--- a/src/librustc/ty/print/obsolete.rs
+++ b/src/librustc/ty/print/obsolete.rs
@@ -186,7 +186,7 @@
     // as well as the unprintable types of constants (see `push_type_name` for more details).
     pub fn push_const_name(&self, c: &Const<'tcx>, output: &mut String, debug: bool) {
         match c.val {
-            ConstValue::Scalar(..) | ConstValue::Slice { .. } | ConstValue::ByRef(..) => {
+            ConstValue::Scalar(..) | ConstValue::Slice { .. } | ConstValue::ByRef { .. } => {
                 // FIXME(const_generics): we could probably do a better job here.
                 write!(output, "{:?}", c).unwrap()
             }
diff --git a/src/librustc/ty/print/pretty.rs b/src/librustc/ty/print/pretty.rs
index d143cc3..cb0ac0f 100644
--- a/src/librustc/ty/print/pretty.rs
+++ b/src/librustc/ty/print/pretty.rs
@@ -600,7 +600,7 @@
                         p!(
                             write("{}{}:",
                                     sep,
-                                    self.tcx().hir().name_by_hir_id(var_id)),
+                                    self.tcx().hir().name(var_id)),
                             print(upvar_ty));
                         sep = ", ";
                     }
@@ -643,7 +643,7 @@
                         p!(
                             write("{}{}:",
                                     sep,
-                                    self.tcx().hir().name_by_hir_id(var_id)),
+                                    self.tcx().hir().name(var_id)),
                             print(upvar_ty));
                         sep = ", ";
                     }
diff --git a/src/librustc/ty/query/plumbing.rs b/src/librustc/ty/query/plumbing.rs
index 0f158d2..7d5f984 100644
--- a/src/librustc/ty/query/plumbing.rs
+++ b/src/librustc/ty/query/plumbing.rs
@@ -89,7 +89,7 @@
 
 /// A type representing the responsibility to execute the job in the `job` field.
 /// This will poison the relevant query if dropped.
-pub(super) struct JobOwner<'a, 'tcx, Q: QueryDescription<'tcx> + 'a> {
+pub(super) struct JobOwner<'a, 'tcx, Q: QueryDescription<'tcx>> {
     cache: &'a Lock<QueryCache<'tcx, Q>>,
     key: Q::Key,
     job: Lrc<QueryJob<'tcx>>,
@@ -230,7 +230,7 @@
 }
 
 /// The result of `try_get_lock`
-pub(super) enum TryGetJob<'a, 'tcx, D: QueryDescription<'tcx> + 'a> {
+pub(super) enum TryGetJob<'a, 'tcx, D: QueryDescription<'tcx>> {
     /// The query is not yet started. Contains a guard to the cache eventually used to start it.
     NotYetStarted(JobOwner<'a, 'tcx, D>),
 
diff --git a/src/librustc/ty/relate.rs b/src/librustc/ty/relate.rs
index 98fd5d1..46adb7e 100644
--- a/src/librustc/ty/relate.rs
+++ b/src/librustc/ty/relate.rs
@@ -594,7 +594,7 @@
                 ty: a.ty,
             }))
         }
-        (ConstValue::ByRef(..), _) => {
+        (ConstValue::ByRef { .. }, _) => {
             bug!(
                 "non-Scalar ConstValue encountered in super_relate_consts {:?} {:?}",
                 a,
diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs
index 4cd0fd3..3d81705 100644
--- a/src/librustc/ty/structural_impls.rs
+++ b/src/librustc/ty/structural_impls.rs
@@ -62,7 +62,7 @@
 impl fmt::Debug for ty::UpvarId {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         let name = ty::tls::with(|tcx| {
-            tcx.hir().name_by_hir_id(self.var_path.hir_id)
+            tcx.hir().name(self.var_path.hir_id)
         });
         write!(f, "UpvarId({:?};`{}`;{:?})",
             self.var_path.hir_id,
@@ -1335,7 +1335,8 @@
 impl<'tcx> TypeFoldable<'tcx> for ConstValue<'tcx> {
     fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         match *self {
-            ConstValue::ByRef(ptr, align, alloc) => ConstValue::ByRef(ptr, align, alloc),
+            ConstValue::ByRef { offset, align, alloc } =>
+                ConstValue::ByRef { offset, align, alloc },
             ConstValue::Infer(ic) => ConstValue::Infer(ic.fold_with(folder)),
             ConstValue::Param(p) => ConstValue::Param(p.fold_with(folder)),
             ConstValue::Placeholder(p) => ConstValue::Placeholder(p),
@@ -1348,7 +1349,7 @@
 
     fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
         match *self {
-            ConstValue::ByRef(..) => false,
+            ConstValue::ByRef { .. } => false,
             ConstValue::Infer(ic) => ic.visit_with(visitor),
             ConstValue::Param(p) => p.visit_with(visitor),
             ConstValue::Placeholder(_) => false,
diff --git a/src/librustc/ty/trait_def.rs b/src/librustc/ty/trait_def.rs
index c40d4d5..a7ade87 100644
--- a/src/librustc/ty/trait_def.rs
+++ b/src/librustc/ty/trait_def.rs
@@ -150,10 +150,10 @@
 }
 
 // Query provider for `trait_impls_of`.
-pub(super) fn trait_impls_of_provider<'tcx>(
-    tcx: TyCtxt<'tcx>,
+pub(super) fn trait_impls_of_provider(
+    tcx: TyCtxt<'_>,
     trait_id: DefId,
-) -> &'tcx TraitImpls {
+) -> &TraitImpls {
     let mut impls = TraitImpls::default();
 
     {
diff --git a/src/librustc/ty/walk.rs b/src/librustc/ty/walk.rs
index 234407a..c74511c 100644
--- a/src/librustc/ty/walk.rs
+++ b/src/librustc/ty/walk.rs
@@ -56,7 +56,7 @@
     }
 }
 
-pub fn walk_shallow<'tcx>(ty: Ty<'tcx>) -> smallvec::IntoIter<TypeWalkerArray<'tcx>> {
+pub fn walk_shallow(ty: Ty<'_>) -> smallvec::IntoIter<TypeWalkerArray<'_>> {
     let mut stack = SmallVec::new();
     push_subtypes(&mut stack, ty);
     stack.into_iter()
diff --git a/src/librustc_borrowck/borrowck/check_loans.rs b/src/librustc_borrowck/borrowck/check_loans.rs
index ace4442..714b7c2 100644
--- a/src/librustc_borrowck/borrowck/check_loans.rs
+++ b/src/librustc_borrowck/borrowck/check_loans.rs
@@ -191,7 +191,7 @@
     let def_id = bccx.tcx.hir().body_owner_def_id(body.id());
 
     let hir_id = bccx.tcx.hir().as_local_hir_id(def_id).unwrap();
-    let movable_generator = !match bccx.tcx.hir().get_by_hir_id(hir_id) {
+    let movable_generator = !match bccx.tcx.hir().get(hir_id) {
         Node::Expr(&hir::Expr {
             node: hir::ExprKind::Closure(.., Some(hir::GeneratorMovability::Static)),
             ..
diff --git a/src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs b/src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs
index 05c0a22..fa28663 100644
--- a/src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs
+++ b/src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs
@@ -49,7 +49,7 @@
 
     let parent = tcx.hir().get_parent_node_by_hir_id(pat.hir_id);
 
-    match tcx.hir().get_by_hir_id(parent) {
+    match tcx.hir().get(parent) {
         Node::Expr(ref e) => {
             // the enclosing expression must be a `match` or something else
             assert!(match e.node {
diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs
index 025d5ad..87c0b85 100644
--- a/src/librustc_borrowck/borrowck/mod.rs
+++ b/src/librustc_borrowck/borrowck/mod.rs
@@ -80,7 +80,7 @@
 
     let owner_id = tcx.hir().as_local_hir_id(owner_def_id).unwrap();
 
-    match tcx.hir().get_by_hir_id(owner_id) {
+    match tcx.hir().get(owner_id) {
         Node::Ctor(..) => {
             // We get invoked with anything that has MIR, but some of
             // those things (notably the synthesized constructors from
@@ -390,7 +390,7 @@
 
 fn closure_to_block(closure_id: LocalDefId, tcx: TyCtxt<'_>) -> HirId {
     let closure_id = tcx.hir().local_def_id_to_hir_id(closure_id);
-    match tcx.hir().get_by_hir_id(closure_id) {
+    match tcx.hir().get(closure_id) {
         Node::Expr(expr) => match expr.node {
             hir::ExprKind::Closure(.., body_id, _, _) => {
                 body_id.hir_id
@@ -702,7 +702,7 @@
             move_data::MovePat => (self.tcx.hir().span(hir_id), ""),
 
             move_data::Captured =>
-                (match self.tcx.hir().expect_expr_by_hir_id(hir_id).node {
+                (match self.tcx.hir().expect_expr(hir_id).node {
                     hir::ExprKind::Closure(.., fn_decl_span, _) => fn_decl_span,
                     ref r => bug!("Captured({:?}) maps to non-closure: {:?}",
                                   the_move.id, r),
@@ -896,7 +896,7 @@
                 // to implement two traits for "one operator" is not very intuitive for
                 // many programmers.
                 if err.cmt.note == mc::NoteIndex {
-                    let node = self.tcx.hir().get_by_hir_id(err.cmt.hir_id);
+                    let node = self.tcx.hir().get(err.cmt.hir_id);
 
                     // This pattern probably always matches.
                     if let Node::Expr(
@@ -1172,7 +1172,7 @@
     }
 
     fn local_binding_mode(&self, hir_id: hir::HirId) -> ty::BindingMode {
-        let pat = match self.tcx.hir().get_by_hir_id(hir_id) {
+        let pat = match self.tcx.hir().get(hir_id) {
             Node::Binding(pat) => pat,
             node => bug!("bad node for local: {:?}", node)
         };
@@ -1190,7 +1190,7 @@
 
     fn local_ty(&self, hir_id: hir::HirId) -> (Option<&hir::Ty>, bool) {
         let parent = self.tcx.hir().get_parent_node_by_hir_id(hir_id);
-        let parent_node = self.tcx.hir().get_by_hir_id(parent);
+        let parent_node = self.tcx.hir().get(parent);
 
         // The parent node is like a fn
         if let Some(fn_like) = FnLikeNode::from_node(parent_node) {
@@ -1255,7 +1255,7 @@
                     None => return
                 };
 
-                if let Node::Field(ref field) = self.tcx.hir().get_by_hir_id(hir_id) {
+                if let Node::Field(ref field) = self.tcx.hir().get(hir_id) {
                     if let Some(msg) = self.suggest_mut_for_immutable(&field.ty, false) {
                         db.span_label(field.ty.span, msg);
                     }
@@ -1394,10 +1394,10 @@
                                       out: &mut String) {
         match loan_path.kind {
             LpUpvar(ty::UpvarId { var_path: ty::UpvarPath { hir_id: id }, closure_expr_id: _ }) => {
-                out.push_str(&self.tcx.hir().name_by_hir_id(id).as_str());
+                out.push_str(&self.tcx.hir().name(id).as_str());
             }
             LpVar(id) => {
-                out.push_str(&self.tcx.hir().name_by_hir_id(id).as_str());
+                out.push_str(&self.tcx.hir().name(id).as_str());
             }
 
             LpDowncast(ref lp_base, variant_def_id) => {
diff --git a/src/librustc_codegen_llvm/builder.rs b/src/librustc_codegen_llvm/builder.rs
index e06b4d8..0709368 100644
--- a/src/librustc_codegen_llvm/builder.rs
+++ b/src/librustc_codegen_llvm/builder.rs
@@ -110,7 +110,7 @@
             unsafe {
                 llvm::$llvm_capi(self.llbuilder, $($arg,)* UNNAMED)
             }
-        })*
+        })+
     }
 }
 
diff --git a/src/librustc_codegen_llvm/consts.rs b/src/librustc_codegen_llvm/consts.rs
index 4bf91bb..e02d14a 100644
--- a/src/librustc_codegen_llvm/consts.rs
+++ b/src/librustc_codegen_llvm/consts.rs
@@ -71,7 +71,9 @@
     let static_ = cx.tcx.const_eval(param_env.and(cid))?;
 
     let alloc = match static_.val {
-        ConstValue::ByRef(ptr, align, alloc) if ptr.offset.bytes() == 0 && align == alloc.align => {
+        ConstValue::ByRef {
+            offset, align, alloc,
+        } if offset.bytes() == 0 && align == alloc.align => {
             alloc
         },
         _ => bug!("static const eval returned {:#?}", static_),
@@ -208,7 +210,7 @@
         let g = if let Some(id) = self.tcx.hir().as_local_hir_id(def_id) {
 
             let llty = self.layout_of(ty).llvm_type(self);
-            let (g, attrs) = match self.tcx.hir().get_by_hir_id(id) {
+            let (g, attrs) = match self.tcx.hir().get(id) {
                 Node::Item(&hir::Item {
                     ref attrs, span, node: hir::ItemKind::Static(..), ..
                 }) => {
diff --git a/src/librustc_codegen_llvm/lib.rs b/src/librustc_codegen_llvm/lib.rs
index 1eb6e9a..7283aa9 100644
--- a/src/librustc_codegen_llvm/lib.rs
+++ b/src/librustc_codegen_llvm/lib.rs
@@ -24,7 +24,6 @@
 #![deny(rust_2018_idioms)]
 #![deny(internal)]
 #![deny(unused_lifetimes)]
-#![allow(explicit_outlives_requirements)]
 
 use back::write::{create_target_machine, create_informational_target_machine};
 use syntax_pos::symbol::Symbol;
diff --git a/src/librustc_codegen_ssa/back/symbol_export.rs b/src/librustc_codegen_ssa/back/symbol_export.rs
index aeff73c7..b9ee82f 100644
--- a/src/librustc_codegen_ssa/back/symbol_export.rs
+++ b/src/librustc_codegen_ssa/back/symbol_export.rs
@@ -82,7 +82,7 @@
             //
             // As a result, if this id is an FFI item (foreign item) then we only
             // let it through if it's included statically.
-            match tcx.hir().get_by_hir_id(hir_id) {
+            match tcx.hir().get(hir_id) {
                 Node::ForeignItem(..) => {
                     let def_id = tcx.hir().local_def_id_from_hir_id(hir_id);
                     if tcx.is_statically_included_foreign_item(def_id) {
diff --git a/src/librustc_codegen_ssa/lib.rs b/src/librustc_codegen_ssa/lib.rs
index 71393e2..b76f098 100644
--- a/src/librustc_codegen_ssa/lib.rs
+++ b/src/librustc_codegen_ssa/lib.rs
@@ -15,7 +15,6 @@
 #![deny(rust_2018_idioms)]
 #![deny(internal)]
 #![deny(unused_lifetimes)]
-#![allow(explicit_outlives_requirements)]
 
 #![recursion_limit="256"]
 
diff --git a/src/librustc_codegen_ssa/mir/operand.rs b/src/librustc_codegen_ssa/mir/operand.rs
index c1626d3..4a6752f 100644
--- a/src/librustc_codegen_ssa/mir/operand.rs
+++ b/src/librustc_codegen_ssa/mir/operand.rs
@@ -109,8 +109,8 @@
                 let b_llval = bx.const_usize((end - start) as u64);
                 OperandValue::Pair(a_llval, b_llval)
             },
-            ConstValue::ByRef(ptr, align, alloc) => {
-                return bx.load_operand(bx.from_const_alloc(layout, align, alloc, ptr.offset));
+            ConstValue::ByRef { offset, align, alloc } => {
+                return bx.load_operand(bx.from_const_alloc(layout, align, alloc, offset));
             },
         };
 
diff --git a/src/librustc_codegen_ssa/mir/place.rs b/src/librustc_codegen_ssa/mir/place.rs
index 72aedb4..d56f39c 100644
--- a/src/librustc_codegen_ssa/mir/place.rs
+++ b/src/librustc_codegen_ssa/mir/place.rs
@@ -424,8 +424,8 @@
                 let layout = cx.layout_of(self.monomorphize(&ty));
                 match bx.tcx().const_eval(param_env.and(cid)) {
                     Ok(val) => match val.val {
-                        mir::interpret::ConstValue::ByRef(ptr, align, alloc) => {
-                            bx.cx().from_const_alloc(layout, align, alloc, ptr.offset)
+                        mir::interpret::ConstValue::ByRef { offset, align, alloc } => {
+                            bx.cx().from_const_alloc(layout, align, alloc, offset)
                         }
                         _ => bug!("promoteds should have an allocation: {:?}", val),
                     },
diff --git a/src/librustc_codegen_utils/symbol_names.rs b/src/librustc_codegen_utils/symbol_names.rs
index ba74f79..7ccd024 100644
--- a/src/librustc_codegen_utils/symbol_names.rs
+++ b/src/librustc_codegen_utils/symbol_names.rs
@@ -135,7 +135,7 @@
 
     // FIXME(eddyb) Precompute a custom symbol name based on attributes.
     let is_foreign = if let Some(id) = hir_id {
-        match tcx.hir().get_by_hir_id(id) {
+        match tcx.hir().get(id) {
             Node::ForeignItem(_) => true,
             _ => false,
         }
diff --git a/src/librustc_data_structures/bit_set.rs b/src/librustc_data_structures/bit_set.rs
index 7a11ca0..3430d83 100644
--- a/src/librustc_data_structures/bit_set.rs
+++ b/src/librustc_data_structures/bit_set.rs
@@ -5,6 +5,10 @@
 use std::marker::PhantomData;
 use std::mem;
 use std::slice;
+#[cfg(test)]
+extern crate test;
+#[cfg(test)]
+use test::Bencher;
 
 pub type Word = u64;
 pub const WORD_BYTES: usize = mem::size_of::<Word>();
@@ -177,6 +181,45 @@
         // Note: we currently don't bother trying to make a Sparse set.
         HybridBitSet::Dense(self.to_owned())
     }
+
+    /// Set `self = self | other`. In contrast to `union` returns `true` if the set contains at
+    /// least one bit that is not in `other` (i.e. `other` is not a superset of `self`).
+    ///
+    /// This is an optimization for union of a hybrid bitset.
+    fn reverse_union_sparse(&mut self, sparse: &SparseBitSet<T>) -> bool {
+        assert!(sparse.domain_size == self.domain_size);
+        self.clear_excess_bits();
+
+        let mut not_already = false;
+        // Index of the current word not yet merged.
+        let mut current_index = 0;
+        // Mask of bits that came from the sparse set in the current word.
+        let mut new_bit_mask = 0;
+        for (word_index, mask) in sparse.iter().map(|x| word_index_and_mask(*x)) {
+            // Next bit is in a word not inspected yet.
+            if word_index > current_index {
+                self.words[current_index] |= new_bit_mask;
+                // Were there any bits in the old word that did not occur in the sparse set?
+                not_already |= (self.words[current_index] ^ new_bit_mask) != 0;
+                // Check all words we skipped for any set bit.
+                not_already |= self.words[current_index+1..word_index].iter().any(|&x| x != 0);
+                // Update next word.
+                current_index = word_index;
+                // Reset bit mask, no bits have been merged yet.
+                new_bit_mask = 0;
+            }
+            // Add bit and mark it as coming from the sparse set.
+            // self.words[word_index] |= mask;
+            new_bit_mask |= mask;
+        }
+        self.words[current_index] |= new_bit_mask;
+        // Any bits in the last inspected word that were not in the sparse set?
+        not_already |= (self.words[current_index] ^ new_bit_mask) != 0;
+        // Any bits in the tail? Note `clear_excess_bits` before.
+        not_already |= self.words[current_index+1..].iter().any(|&x| x != 0);
+
+        not_already
+    }
 }
 
 /// This is implemented by all the bitsets so that BitSet::union() can be
@@ -514,10 +557,22 @@
                         changed
                     }
                     HybridBitSet::Dense(other_dense) => {
-                        // `self` is sparse and `other` is dense. Densify
-                        // `self` and then do the bitwise union.
-                        let mut new_dense = self_sparse.to_dense();
-                        let changed = new_dense.union(other_dense);
+                        // `self` is sparse and `other` is dense. To
+                        // merge them, we have two available strategies:
+                        // * Densify `self` then merge other
+                        // * Clone other then integrate bits from `self`
+                        // The second strategy requires dedicated method
+                        // since the usual `union` returns the wrong
+                        // result. In the dedicated case the computation
+                        // is slightly faster if the bits of the sparse
+                        // bitset map to only few words of the dense
+                        // representation, i.e. indices are near each
+                        // other.
+                        //
+                        // Benchmarking seems to suggest that the second
+                        // option is worth it.
+                        let mut new_dense = other_dense.clone();
+                        let changed = new_dense.reverse_union_sparse(self_sparse);
                         *self = HybridBitSet::Dense(new_dense);
                         changed
                     }
@@ -1214,3 +1269,87 @@
     }
     assert!(iter.next().is_none());
 }
+
+/// Merge dense hybrid set into empty sparse hybrid set.
+#[bench]
+fn union_hybrid_sparse_empty_to_dense(b: &mut Bencher) {
+    let mut pre_dense: HybridBitSet<usize> = HybridBitSet::new_empty(256);
+    for i in 0..10 {
+        assert!(pre_dense.insert(i));
+    }
+    let pre_sparse: HybridBitSet<usize> = HybridBitSet::new_empty(256);
+    b.iter(|| {
+        let dense = pre_dense.clone();
+        let mut sparse = pre_sparse.clone();
+        sparse.union(&dense);
+    })
+}
+
+/// Merge dense hybrid set into full hybrid set with same indices.
+#[bench]
+fn union_hybrid_sparse_full_to_dense(b: &mut Bencher) {
+    let mut pre_dense: HybridBitSet<usize> = HybridBitSet::new_empty(256);
+    for i in 0..10 {
+        assert!(pre_dense.insert(i));
+    }
+    let mut pre_sparse: HybridBitSet<usize> = HybridBitSet::new_empty(256);
+    for i in 0..SPARSE_MAX {
+        assert!(pre_sparse.insert(i));
+    }
+    b.iter(|| {
+        let dense = pre_dense.clone();
+        let mut sparse = pre_sparse.clone();
+        sparse.union(&dense);
+    })
+}
+
+/// Merge dense hybrid set into full hybrid set with indices over the whole domain.
+#[bench]
+fn union_hybrid_sparse_domain_to_dense(b: &mut Bencher) {
+    let mut pre_dense: HybridBitSet<usize> = HybridBitSet::new_empty(SPARSE_MAX*64);
+    for i in 0..10 {
+        assert!(pre_dense.insert(i));
+    }
+    let mut pre_sparse: HybridBitSet<usize> = HybridBitSet::new_empty(SPARSE_MAX*64);
+    for i in 0..SPARSE_MAX {
+        assert!(pre_sparse.insert(i*64));
+    }
+    b.iter(|| {
+        let dense = pre_dense.clone();
+        let mut sparse = pre_sparse.clone();
+        sparse.union(&dense);
+    })
+}
+
+/// Merge dense hybrid set into empty hybrid set where the domain is very small.
+#[bench]
+fn union_hybrid_sparse_empty_small_domain(b: &mut Bencher) {
+    let mut pre_dense: HybridBitSet<usize> = HybridBitSet::new_empty(SPARSE_MAX);
+    for i in 0..SPARSE_MAX {
+        assert!(pre_dense.insert(i));
+    }
+    let pre_sparse: HybridBitSet<usize> = HybridBitSet::new_empty(SPARSE_MAX);
+    b.iter(|| {
+        let dense = pre_dense.clone();
+        let mut sparse = pre_sparse.clone();
+        sparse.union(&dense);
+    })
+}
+
+/// Merge dense hybrid set into full hybrid set where the domain is very small.
+#[bench]
+fn union_hybrid_sparse_full_small_domain(b: &mut Bencher) {
+    let mut pre_dense: HybridBitSet<usize> = HybridBitSet::new_empty(SPARSE_MAX);
+    for i in 0..SPARSE_MAX {
+        assert!(pre_dense.insert(i));
+    }
+    let mut pre_sparse: HybridBitSet<usize> = HybridBitSet::new_empty(SPARSE_MAX);
+    for i in 0..SPARSE_MAX {
+        assert!(pre_sparse.insert(i));
+    }
+    b.iter(|| {
+        let dense = pre_dense.clone();
+        let mut sparse = pre_sparse.clone();
+        sparse.union(&dense);
+    })
+}
diff --git a/src/librustc_data_structures/indexed_vec.rs b/src/librustc_data_structures/indexed_vec.rs
index c7f6e54..635edbb 100644
--- a/src/librustc_data_structures/indexed_vec.rs
+++ b/src/librustc_data_structures/indexed_vec.rs
@@ -423,7 +423,7 @@
     (@derives      [$($derives:ident,)*]
      @attrs        [$(#[$attrs:meta])*]
      @type         [$type:ident]
-     @max          [$_max:expr]
+     @max          [$max:expr]
      @vis          [$v:vis]
      @debug_format [$debug_format:tt]
                    $(#[doc = $doc:expr])*
diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs
index eb89b5c..0301661 100644
--- a/src/librustc_driver/pretty.rs
+++ b/src/librustc_driver/pretty.rs
@@ -627,7 +627,7 @@
             // Find the function this expression is from.
             let mut hir_id = expr.hir_id;
             loop {
-                let node = tcx.hir().get_by_hir_id(hir_id);
+                let node = tcx.hir().get(hir_id);
                 if let Some(n) = hir::map::blocks::FnLikeNode::from_node(node) {
                     break n.body();
                 }
@@ -830,7 +830,8 @@
                                                                          box out,
                                                                          annotation.pp_ann());
                     for node_id in uii.all_matching_node_ids(hir_map) {
-                        let node = hir_map.get(node_id);
+                        let hir_id = tcx.hir().node_to_hir_id(node_id);
+                        let node = hir_map.get(hir_id);
                         pp_state.print_node(node)?;
                         pp_state.s.space()?;
                         let path = annotation.node_path(node_id)
@@ -847,7 +848,8 @@
                 s.call_with_pp_support_hir(tcx, move |_annotation, _krate| {
                     debug!("pretty printing source code {:?}", s);
                     for node_id in uii.all_matching_node_ids(tcx.hir()) {
-                        let node = tcx.hir().get(node_id);
+                        let hir_id = tcx.hir().node_to_hir_id(node_id);
+                        let node = tcx.hir().get(hir_id);
                         write!(out, "{:#?}", node)?;
                     }
                     Ok(())
@@ -909,7 +911,8 @@
                 tcx.sess.fatal(&format!("--pretty flowgraph couldn't find id: {}", nodeid))
             });
 
-            match blocks::Code::from_node(&tcx.hir(), nodeid) {
+            let hir_id = tcx.hir().node_to_hir_id(nodeid);
+            match blocks::Code::from_node(&tcx.hir(), hir_id) {
                 Some(code) => {
                     let variants = gather_flowgraph_variants(tcx.sess);
 
diff --git a/src/librustc_incremental/persist/dirty_clean.rs b/src/librustc_incremental/persist/dirty_clean.rs
index e2e4a4e..5296ed0 100644
--- a/src/librustc_incremental/persist/dirty_clean.rs
+++ b/src/librustc_incremental/persist/dirty_clean.rs
@@ -322,7 +322,7 @@
     /// Return all DepNode labels that should be asserted for this item.
     /// index=0 is the "name" used for error messages
     fn auto_labels(&mut self, item_id: hir::HirId, attr: &Attribute) -> (&'static str, Labels) {
-        let node = self.tcx.hir().get_by_hir_id(item_id);
+        let node = self.tcx.hir().get(item_id);
         let (name, labels) = match node {
             HirNode::Item(item) => {
                 match item.node {
diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs
index 5f052f6..2fb534e 100644
--- a/src/librustc_lint/types.rs
+++ b/src/librustc_lint/types.rs
@@ -219,7 +219,7 @@
                             return Some(format!("{:?}", $itypes))
                         })*
                         None
-                    },)*
+                    },)+
                     _ => None
                 }
             }
@@ -276,7 +276,7 @@
         }
 
         let par_id = cx.tcx.hir().get_parent_node_by_hir_id(e.hir_id);
-        if let Node::Expr(par_e) = cx.tcx.hir().get_by_hir_id(par_id) {
+        if let Node::Expr(par_e) = cx.tcx.hir().get(par_id) {
             if let hir::ExprKind::Struct(..) = par_e.node {
                 if is_range_literal(cx.sess(), par_e)
                     && lint_overflowing_range_endpoint(cx, lit, v, max, e, par_e, t)
@@ -315,7 +315,7 @@
     };
     if lit_val < min || lit_val > max {
         let parent_id = cx.tcx.hir().get_parent_node_by_hir_id(e.hir_id);
-        if let Node::Expr(par_e) = cx.tcx.hir().get_by_hir_id(parent_id) {
+        if let Node::Expr(par_e) = cx.tcx.hir().get(parent_id) {
             match par_e.node {
                 hir::ExprKind::Cast(..) => {
                     if let ty::Char = cx.tables.expr_ty(par_e).sty {
diff --git a/src/librustc_lint/unused.rs b/src/librustc_lint/unused.rs
index f84ce2f..b5c5fc0 100644
--- a/src/librustc_lint/unused.rs
+++ b/src/librustc_lint/unused.rs
@@ -324,20 +324,28 @@
                                 value: &ast::Expr,
                                 msg: &str,
                                 followed_by_block: bool) {
-        if let ast::ExprKind::Paren(ref inner) = value.node {
-            let necessary = followed_by_block && match inner.node {
-                ast::ExprKind::Ret(_) | ast::ExprKind::Break(..) => true,
-                _ => parser::contains_exterior_struct_lit(&inner),
-            };
-            if !necessary {
-                let expr_text = if let Ok(snippet) = cx.sess().source_map()
-                    .span_to_snippet(value.span) {
-                        snippet
-                    } else {
-                        pprust::expr_to_string(value)
-                    };
-                Self::remove_outer_parens(cx, value.span, &expr_text, msg);
+        match value.node {
+            ast::ExprKind::Paren(ref inner) => {
+                let necessary = followed_by_block && match inner.node {
+                    ast::ExprKind::Ret(_) | ast::ExprKind::Break(..) => true,
+                    _ => parser::contains_exterior_struct_lit(&inner),
+                };
+                if !necessary {
+                    let expr_text = if let Ok(snippet) = cx.sess().source_map()
+                        .span_to_snippet(value.span) {
+                            snippet
+                        } else {
+                            pprust::expr_to_string(value)
+                        };
+                    Self::remove_outer_parens(cx, value.span, &expr_text, msg);
+                }
             }
+            ast::ExprKind::Let(_, ref expr) => {
+                // FIXME(#60336): Properly handle `let true = (false && true)`
+                // actually needing the parenthesis.
+                self.check_unused_parens_expr(cx, expr, "`let` head expression", followed_by_block);
+            }
+            _ => {}
         }
     }
 
@@ -399,8 +407,6 @@
         let (value, msg, followed_by_block) = match e.node {
             If(ref cond, ..) => (cond, "`if` condition", true),
             While(ref cond, ..) => (cond, "`while` condition", true),
-            IfLet(_, ref cond, ..) => (cond, "`if let` head expression", true),
-            WhileLet(_, ref cond, ..) => (cond, "`while let` head expression", true),
             ForLoop(_, ref cond, ..) => (cond, "`for` head expression", true),
             Match(ref head, _) => (head, "`match` head expression", true),
             Ret(Some(ref value)) => (value, "`return` value", false),
diff --git a/src/librustc_mir/borrow_check/conflict_errors.rs b/src/librustc_mir/borrow_check/conflict_errors.rs
index dfaad95..f23cffe 100644
--- a/src/librustc_mir/borrow_check/conflict_errors.rs
+++ b/src/librustc_mir/borrow_check/conflict_errors.rs
@@ -853,7 +853,7 @@
                     format!(
                         "...but `{}` will be dropped here, when the function `{}` returns",
                         name,
-                        self.infcx.tcx.hir().name_by_hir_id(fn_hir_id),
+                        self.infcx.tcx.hir().name(fn_hir_id),
                     ),
                 );
 
diff --git a/src/librustc_mir/borrow_check/error_reporting.rs b/src/librustc_mir/borrow_check/error_reporting.rs
index 7eb2963..ac64cf7 100644
--- a/src/librustc_mir/borrow_check/error_reporting.rs
+++ b/src/librustc_mir/borrow_check/error_reporting.rs
@@ -349,7 +349,7 @@
                     let (&var_id, _) = self.infcx.tcx.upvars(def_id).unwrap()
                         .get_index(field.index()).unwrap();
 
-                    self.infcx.tcx.hir().name_by_hir_id(var_id).to_string()
+                    self.infcx.tcx.hir().name(var_id).to_string()
                 }
                 _ => {
                     // Might need a revision when the fields in trait RFC is implemented
@@ -659,7 +659,7 @@
             def_id, target_place, places
         );
         let hir_id = self.infcx.tcx.hir().as_local_hir_id(def_id)?;
-        let expr = &self.infcx.tcx.hir().expect_expr_by_hir_id(hir_id).node;
+        let expr = &self.infcx.tcx.hir().expect_expr(hir_id).node;
         debug!("closure_span: hir_id={:?} expr={:?}", hir_id, expr);
         if let hir::ExprKind::Closure(
             .., args_span, _
diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs
index ca8c4c5..919ed5c 100644
--- a/src/librustc_mir/borrow_check/mod.rs
+++ b/src/librustc_mir/borrow_check/mod.rs
@@ -124,14 +124,13 @@
         .flat_map(|v| v.values())
         .map(|upvar_id| {
             let var_hir_id = upvar_id.var_path.hir_id;
-            let var_node_id = tcx.hir().hir_to_node_id(var_hir_id);
             let capture = tables.upvar_capture(*upvar_id);
             let by_ref = match capture {
                 ty::UpvarCapture::ByValue => false,
                 ty::UpvarCapture::ByRef(..) => true,
             };
             let mut upvar = Upvar {
-                name: tcx.hir().name(var_node_id),
+                name: tcx.hir().name(var_hir_id),
                 var_hir_id,
                 by_ref,
                 mutability: Mutability::Not,
@@ -231,7 +230,7 @@
         |bd, i| DebugFormatted::new(&bd.move_data().inits[i]),
     ));
 
-    let movable_generator = match tcx.hir().get_by_hir_id(id) {
+    let movable_generator = match tcx.hir().get(id) {
         Node::Expr(&hir::Expr {
             node: hir::ExprKind::Closure(.., Some(hir::GeneratorMovability::Static)),
             ..
diff --git a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs
index 46b6901..3f5b2f4 100644
--- a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs
+++ b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs
@@ -237,7 +237,7 @@
 
                     if let DefiningTy::Closure(def_id, substs) = def_ty {
                         let args_span = if let hir::ExprKind::Closure(_, _, _, span, _) =
-                            tcx.hir().expect_expr_by_hir_id(mir_hir_id).node
+                            tcx.hir().expect_expr(mir_hir_id).node
                         {
                             span
                         } else {
@@ -698,7 +698,7 @@
 
         let mir_hir_id = tcx.hir().as_local_hir_id(mir_def_id).expect("non-local mir");
 
-        let (return_span, mir_description) = match tcx.hir().get_by_hir_id(mir_hir_id) {
+        let (return_span, mir_description) = match tcx.hir().get(mir_hir_id) {
             hir::Node::Expr(hir::Expr {
                 node: hir::ExprKind::Closure(_, return_ty, _, span, gen_move),
                 ..
@@ -761,7 +761,7 @@
 
         let mir_hir_id = tcx.hir().as_local_hir_id(mir_def_id).expect("non-local mir");
 
-        let yield_span = match tcx.hir().get_by_hir_id(mir_hir_id) {
+        let yield_span = match tcx.hir().get(mir_hir_id) {
             hir::Node::Expr(hir::Expr {
                 node: hir::ExprKind::Closure(_, _, _, span, _),
                 ..
diff --git a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/var_name.rs b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/var_name.rs
index 59fc411..750a132 100644
--- a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/var_name.rs
+++ b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/var_name.rs
@@ -72,7 +72,7 @@
         let upvar_hir_id = upvars[upvar_index].var_hir_id;
         debug!("get_upvar_name_and_span_for_region: upvar_hir_id={:?}", upvar_hir_id);
 
-        let upvar_name = tcx.hir().name_by_hir_id(upvar_hir_id);
+        let upvar_name = tcx.hir().name(upvar_hir_id);
         let upvar_span = tcx.hir().span(upvar_hir_id);
         debug!("get_upvar_name_and_span_for_region: upvar_name={:?} upvar_span={:?}",
                upvar_name, upvar_span);
diff --git a/src/librustc_mir/borrow_check/nll/universal_regions.rs b/src/librustc_mir/borrow_check/nll/universal_regions.rs
index fa3c7b9..a85f477 100644
--- a/src/librustc_mir/borrow_check/nll/universal_regions.rs
+++ b/src/librustc_mir/borrow_check/nll/universal_regions.rs
@@ -767,7 +767,7 @@
                 owner: fn_def_id.index,
                 local_id: *late_bound,
             };
-            let name = tcx.hir().name_by_hir_id(hir_id).as_interned_str();
+            let name = tcx.hir().name(hir_id).as_interned_str();
             let region_def_id = tcx.hir().local_def_id_from_hir_id(hir_id);
             let liberated_region = tcx.mk_region(ty::ReFree(ty::FreeRegion {
                 scope: fn_def_id,
diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs
index f795843..6606422 100644
--- a/src/librustc_mir/build/mod.rs
+++ b/src/librustc_mir/build/mod.rs
@@ -26,7 +26,7 @@
     let id = tcx.hir().as_local_hir_id(def_id).unwrap();
 
     // Figure out what primary body this item has.
-    let (body_id, return_ty_span) = match tcx.hir().get_by_hir_id(id) {
+    let (body_id, return_ty_span) = match tcx.hir().get(id) {
         Node::Expr(hir::Expr { node: hir::ExprKind::Closure(_, decl, body_id, _, _), .. })
         | Node::Item(hir::Item { node: hir::ItemKind::Fn(decl, _, _, body_id), .. })
         | Node::ImplItem(
diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs
index 284a8f4..67bcd49 100644
--- a/src/librustc_mir/const_eval.rs
+++ b/src/librustc_mir/const_eval.rs
@@ -24,7 +24,7 @@
     PlaceTy, MPlaceTy, OpTy, ImmTy, Immediate, Scalar,
     RawConst, ConstValue,
     InterpResult, InterpErrorInfo, InterpError, GlobalId, InterpretCx, StackPopCleanup,
-    Allocation, AllocId, MemoryKind,
+    Allocation, AllocId, MemoryKind, Memory,
     snapshot, RefTracking, intern_const_alloc_recursive,
 };
 
@@ -99,7 +99,7 @@
         Ok(mplace) => {
             let ptr = mplace.ptr.to_ptr().unwrap();
             let alloc = ecx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id);
-            ConstValue::ByRef(ptr, mplace.align, alloc)
+            ConstValue::ByRef { offset: ptr.offset, align: mplace.align, alloc }
         },
         // see comment on `let try_as_immediate` above
         Err(ImmTy { imm: Immediate::Scalar(x), .. }) => match x {
@@ -113,7 +113,7 @@
                 let mplace = op.to_mem_place();
                 let ptr = mplace.ptr.to_ptr().unwrap();
                 let alloc = ecx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id);
-                ConstValue::ByRef(ptr, mplace.align, alloc)
+                ConstValue::ByRef { offset: ptr.offset, align: mplace.align, alloc }
             },
         },
         Err(ImmTy { imm: Immediate::ScalarPair(a, b), .. }) => {
@@ -410,7 +410,7 @@
         _id: AllocId,
         alloc: Cow<'b, Allocation>,
         _kind: Option<MemoryKind<!>>,
-        _memory_extra: &(),
+        _memory: &Memory<'mir, 'tcx, Self>,
     ) -> (Cow<'b, Allocation<Self::PointerTag>>, Self::PointerTag) {
         // We do not use a tag so we can just cheaply forward the allocation
         (alloc, ())
@@ -419,7 +419,7 @@
     #[inline(always)]
     fn tag_static_base_pointer(
         _id: AllocId,
-        _memory_extra: &(),
+        _memory: &Memory<'mir, 'tcx, Self>,
     ) -> Self::PointerTag {
         ()
     }
@@ -541,11 +541,11 @@
         if tcx.is_static(def_id) || cid.promoted.is_some() {
             let ptr = mplace.ptr.to_ptr()?;
             Ok(tcx.mk_const(ty::Const {
-                val: ConstValue::ByRef(
-                    ptr,
-                    mplace.align,
-                    ecx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id),
-                ),
+                val: ConstValue::ByRef {
+                    offset: ptr.offset,
+                    align: mplace.align,
+                    alloc: ecx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id),
+                },
                 ty: mplace.layout.ty,
             }))
         } else {
diff --git a/src/librustc_mir/dataflow/graphviz.rs b/src/librustc_mir/dataflow/graphviz.rs
index f62ad2f..7896592 100644
--- a/src/librustc_mir/dataflow/graphviz.rs
+++ b/src/librustc_mir/dataflow/graphviz.rs
@@ -30,7 +30,7 @@
     fn flow_state(&self) -> &DataflowState<'tcx, Self::BD> { &self.flow_state.flow_state }
 }
 
-struct Graph<'a, 'tcx, MWF:'a, P> where
+struct Graph<'a, 'tcx, MWF, P> where
     MWF: MirWithFlowState<'tcx>
 {
     mbcx: &'a MWF,
diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs
index 899765a..7617d3b 100644
--- a/src/librustc_mir/dataflow/impls/borrows.rs
+++ b/src/librustc_mir/dataflow/impls/borrows.rs
@@ -193,43 +193,38 @@
         place: &Place<'tcx>
     ) {
         debug!("kill_borrows_on_place: place={:?}", place);
-        // Handle the `Place::Local(..)` case first and exit early.
-        if let Place::Base(PlaceBase::Local(local)) = place {
-            if let Some(borrow_indices) = self.borrow_set.local_map.get(&local) {
-                debug!("kill_borrows_on_place: borrow_indices={:?}", borrow_indices);
-                sets.kill_all(borrow_indices);
+
+        if let Some(local) = place.base_local() {
+            let other_borrows_of_local = self
+                .borrow_set
+                .local_map
+                .get(&local)
+                .into_iter()
+                .flat_map(|bs| bs.into_iter());
+
+            // If the borrowed place is a local with no projections, all other borrows of this
+            // local must conflict. This is purely an optimization so we don't have to call
+            // `places_conflict` for every borrow.
+            if let Place::Base(PlaceBase::Local(_)) = place {
+                sets.kill_all(other_borrows_of_local);
                 return;
             }
-        }
-
-        // Otherwise, look at all borrows that are live and if they conflict with the assignment
-        // into our place then we can kill them.
-        let mut borrows = sets.on_entry.clone();
-        let _ = borrows.union(sets.gen_set);
-        for borrow_index in borrows.iter() {
-            let borrow_data = &self.borrows()[borrow_index];
-            debug!(
-                "kill_borrows_on_place: borrow_index={:?} borrow_data={:?}",
-                borrow_index, borrow_data,
-            );
 
             // By passing `PlaceConflictBias::NoOverlap`, we conservatively assume that any given
             // pair of array indices are unequal, so that when `places_conflict` returns true, we
             // will be assured that two places being compared definitely denotes the same sets of
             // locations.
-            if places_conflict::places_conflict(
-                self.tcx,
-                self.body,
-                &borrow_data.borrowed_place,
-                place,
-                places_conflict::PlaceConflictBias::NoOverlap,
-            ) {
-                debug!(
-                    "kill_borrows_on_place: (kill) borrow_index={:?} borrow_data={:?}",
-                    borrow_index, borrow_data,
-                );
-                sets.kill(borrow_index);
-            }
+            let definitely_conflicting_borrows = other_borrows_of_local
+                .filter(|&&i| {
+                    places_conflict::places_conflict(
+                        self.tcx,
+                        self.body,
+                        &self.borrow_set.borrows[i].borrowed_place,
+                        place,
+                        places_conflict::PlaceConflictBias::NoOverlap)
+                });
+
+            sets.kill_all(definitely_conflicting_borrows);
         }
     }
 }
diff --git a/src/librustc_mir/dataflow/mod.rs b/src/librustc_mir/dataflow/mod.rs
index 0728d5b2..89b952e 100644
--- a/src/librustc_mir/dataflow/mod.rs
+++ b/src/librustc_mir/dataflow/mod.rs
@@ -181,7 +181,7 @@
 
 struct PropagationContext<'b, 'a, 'tcx, O>
 where
-    O: 'b + BitDenotation<'tcx>,
+    O: BitDenotation<'tcx>,
 {
     builder: &'b mut DataflowAnalysis<'a, 'tcx, O>,
 }
diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs
index baf9086..597f172 100644
--- a/src/librustc_mir/hair/cx/expr.rs
+++ b/src/librustc_mir/hair/cx/expr.rs
@@ -914,7 +914,7 @@
             let generics = cx.tcx.generics_of(item_def_id);
             let local_def_id = cx.tcx.hir().local_def_id_from_hir_id(hir_id);
             let index = generics.param_def_id_to_index[&local_def_id];
-            let name = cx.tcx.hir().name_by_hir_id(hir_id).as_interned_str();
+            let name = cx.tcx.hir().name(hir_id).as_interned_str();
             let val = ConstValue::Param(ty::ParamConst::new(index, name));
             ExprKind::Literal {
                 literal: cx.tcx.mk_const(
diff --git a/src/librustc_mir/hair/cx/mod.rs b/src/librustc_mir/hair/cx/mod.rs
index 4e197f1..a21d900 100644
--- a/src/librustc_mir/hair/cx/mod.rs
+++ b/src/librustc_mir/hair/cx/mod.rs
@@ -155,7 +155,7 @@
 
     pub fn pattern_from_hir(&mut self, p: &hir::Pat) -> Pattern<'tcx> {
         let tcx = self.tcx.global_tcx();
-        let p = match tcx.hir().get_by_hir_id(p.hir_id) {
+        let p = match tcx.hir().get(p.hir_id) {
             Node::Pat(p) | Node::Binding(p) => p,
             node => bug!("pattern became {:?}", node)
         };
diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs
index 974c863..fc29518 100644
--- a/src/librustc_mir/hair/pattern/_match.rs
+++ b/src/librustc_mir/hair/pattern/_match.rs
@@ -217,12 +217,12 @@
             // the easy case, deref a reference
             (ConstValue::Scalar(Scalar::Ptr(p)), x, y) if x == y => {
                 let alloc = self.tcx.alloc_map.lock().unwrap_memory(p.alloc_id);
-                ConstValue::ByRef(
-                    p,
+                ConstValue::ByRef {
+                    offset: p.offset,
                     // FIXME(oli-obk): this should be the type's layout
-                    alloc.align,
+                    align: alloc.align,
                     alloc,
-                )
+                }
             },
             // unsize array to slice if pattern is array but match value or other patterns are slice
             (ConstValue::Scalar(Scalar::Ptr(p)), ty::Array(t, n), ty::Slice(u)) => {
@@ -1436,9 +1436,10 @@
     suffix: &[Pattern<'tcx>],
 ) -> Result<bool, ErrorReported> {
     let data: &[u8] = match (const_val.val, &const_val.ty.sty) {
-        (ConstValue::ByRef(ptr, _, alloc), ty::Array(t, n)) => {
+        (ConstValue::ByRef { offset, alloc, .. }, ty::Array(t, n)) => {
             assert_eq!(*t, tcx.types.u8);
             let n = n.assert_usize(tcx).unwrap();
+            let ptr = Pointer::new(AllocId(0), offset);
             alloc.get_bytes(&tcx, ptr, Size::from_bytes(n)).unwrap()
         },
         (ConstValue::Slice { data, start, end }, ty::Slice(t)) => {
@@ -1758,9 +1759,9 @@
                     let (alloc, offset, n, ty) = match value.ty.sty {
                         ty::Array(t, n) => {
                             match value.val {
-                                ConstValue::ByRef(ptr, _, alloc) => (
+                                ConstValue::ByRef { offset, alloc, .. } => (
                                     alloc,
-                                    ptr.offset,
+                                    offset,
                                     n.unwrap_usize(cx.tcx),
                                     t,
                                 ),
@@ -1778,7 +1779,7 @@
                                     (end - start) as u64,
                                     t,
                                 ),
-                                ConstValue::ByRef(..) => {
+                                ConstValue::ByRef { .. } => {
                                     // FIXME(oli-obk): implement `deref` for `ConstValue`
                                     return None;
                                 },
diff --git a/src/librustc_mir/interpret/intern.rs b/src/librustc_mir/interpret/intern.rs
index d998f40..416b66d 100644
--- a/src/librustc_mir/interpret/intern.rs
+++ b/src/librustc_mir/interpret/intern.rs
@@ -21,7 +21,7 @@
 };
 use crate::const_eval::{CompileTimeInterpreter, CompileTimeEvalContext};
 
-struct InternVisitor<'rt, 'mir: 'rt, 'tcx: 'rt + 'mir> {
+struct InternVisitor<'rt, 'mir, 'tcx> {
     /// previously encountered safe references
     ref_tracking: &'rt mut RefTracking<(MPlaceTy<'tcx>, Mutability, InternMode)>,
     ecx: &'rt mut CompileTimeEvalContext<'mir, 'tcx>,
diff --git a/src/librustc_mir/interpret/machine.rs b/src/librustc_mir/interpret/machine.rs
index 2581c13..4eb95f2 100644
--- a/src/librustc_mir/interpret/machine.rs
+++ b/src/librustc_mir/interpret/machine.rs
@@ -11,8 +11,7 @@
 
 use super::{
     Allocation, AllocId, InterpResult, Scalar, AllocationExtra,
-    InterpretCx, PlaceTy, OpTy, ImmTy, MemoryKind, Pointer,
-    InterpErrorInfo, InterpError
+    InterpretCx, PlaceTy, OpTy, ImmTy, MemoryKind, Pointer, Memory
 };
 
 /// Whether this kind of memory is allowed to leak
@@ -178,7 +177,7 @@
         id: AllocId,
         alloc: Cow<'b, Allocation>,
         kind: Option<MemoryKind<Self::MemoryKinds>>,
-        memory_extra: &Self::MemoryExtra,
+        memory: &Memory<'mir, 'tcx, Self>,
     ) -> (Cow<'b, Allocation<Self::PointerTag, Self::AllocExtra>>, Self::PointerTag);
 
     /// Return the "base" tag for the given static allocation: the one that is used for direct
@@ -188,7 +187,7 @@
     /// for cyclic statics!
     fn tag_static_base_pointer(
         id: AllocId,
-        memory_extra: &Self::MemoryExtra,
+        memory: &Memory<'mir, 'tcx, Self>,
     ) -> Self::PointerTag;
 
     /// Executes a retagging operation
@@ -212,19 +211,19 @@
 
     fn int_to_ptr(
         int: u64,
-        _extra: &Self::MemoryExtra,
+        _mem: &Memory<'mir, 'tcx, Self>,
     ) -> InterpResult<'tcx, Pointer<Self::PointerTag>> {
         if int == 0 {
-            Err(InterpErrorInfo::from(InterpError::InvalidNullPointerUsage))
+            err!(InvalidNullPointerUsage)
         } else {
-            Err(InterpErrorInfo::from(InterpError::ReadBytesAsPointer))
+            err!(ReadBytesAsPointer)
         }
     }
 
     fn ptr_to_int(
         _ptr: Pointer<Self::PointerTag>,
-        _extra: &Self::MemoryExtra,
+        _mem: &Memory<'mir, 'tcx, Self>,
     ) -> InterpResult<'tcx, u64> {
-        Err(InterpErrorInfo::from(InterpError::ReadPointerAsBytes))
+        err!(ReadPointerAsBytes)
     }
 }
diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs
index f2a115f..de035ed 100644
--- a/src/librustc_mir/interpret/memory.rs
+++ b/src/librustc_mir/interpret/memory.rs
@@ -107,7 +107,7 @@
 
     #[inline]
     pub fn tag_static_base_pointer(&self, ptr: Pointer) -> Pointer<M::PointerTag> {
-        ptr.with_tag(M::tag_static_base_pointer(ptr.alloc_id, &self.extra))
+        ptr.with_tag(M::tag_static_base_pointer(ptr.alloc_id, &self))
     }
 
     pub fn create_fn_alloc(&mut self, instance: Instance<'tcx>) -> Pointer<M::PointerTag> {
@@ -140,7 +140,7 @@
         kind: MemoryKind<M::MemoryKinds>,
     ) -> Pointer<M::PointerTag> {
         let id = self.tcx.alloc_map.lock().reserve();
-        let (alloc, tag) = M::tag_allocation(id, Cow::Owned(alloc), Some(kind), &self.extra);
+        let (alloc, tag) = M::tag_allocation(id, Cow::Owned(alloc), Some(kind), &self);
         self.alloc_map.insert(id, (kind, alloc.into_owned()));
         Pointer::from(id).with_tag(tag)
     }
@@ -327,7 +327,7 @@
     fn get_static_alloc(
         id: AllocId,
         tcx: TyCtxtAt<'tcx>,
-        memory_extra: &M::MemoryExtra,
+        memory: &Memory<'mir, 'tcx, M>,
     ) -> InterpResult<'tcx, Cow<'tcx, Allocation<M::PointerTag, M::AllocExtra>>> {
         let alloc = tcx.alloc_map.lock().get(id);
         let alloc = match alloc {
@@ -374,7 +374,7 @@
             id, // always use the ID we got as input, not the "hidden" one.
             alloc,
             M::STATIC_KIND.map(MemoryKind::Machine),
-            memory_extra
+            memory
         ).0)
     }
 
@@ -387,7 +387,7 @@
         // `get_static_alloc` that we can actually use directly without inserting anything anywhere.
         // So the error type is `InterpResult<'tcx, &Allocation<M::PointerTag>>`.
         let a = self.alloc_map.get_or(id, || {
-            let alloc = Self::get_static_alloc(id, self.tcx, &self.extra).map_err(Err)?;
+            let alloc = Self::get_static_alloc(id, self.tcx, &self).map_err(Err)?;
             match alloc {
                 Cow::Borrowed(alloc) => {
                     // We got a ref, cheaply return that as an "error" so that the
@@ -416,11 +416,11 @@
         id: AllocId,
     ) -> InterpResult<'tcx, &mut Allocation<M::PointerTag, M::AllocExtra>> {
         let tcx = self.tcx;
-        let memory_extra = &self.extra;
+        let alloc = Self::get_static_alloc(id, tcx, &self);
         let a = self.alloc_map.get_mut_or(id, || {
             // Need to make a copy, even if `get_static_alloc` is able
             // to give us a cheap reference.
-            let alloc = Self::get_static_alloc(id, tcx, memory_extra)?;
+            let alloc = alloc?;
             if alloc.mutability == Mutability::Immutable {
                 return err!(ModifiedConstantMemory);
             }
@@ -843,7 +843,7 @@
     ) -> InterpResult<'tcx, Pointer<M::PointerTag>> {
         match scalar {
             Scalar::Ptr(ptr) => Ok(ptr),
-            _ => M::int_to_ptr(scalar.to_usize(self)?, &self.extra)
+            _ => M::int_to_ptr(scalar.to_usize(self)?, self)
         }
     }
 
@@ -854,7 +854,7 @@
     ) -> InterpResult<'tcx, u128> {
         match scalar.to_bits_or_ptr(size, self) {
             Ok(bits) => Ok(bits),
-            Err(ptr) => Ok(M::ptr_to_int(ptr, &self.extra)? as u128)
+            Err(ptr) => Ok(M::ptr_to_int(ptr, self)? as u128)
         }
     }
 }
diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs
index 1b451e0..ec391b7 100644
--- a/src/librustc_mir/interpret/operand.rs
+++ b/src/librustc_mir/interpret/operand.rs
@@ -538,10 +538,11 @@
             self.layout_of(self.monomorphize(val.ty)?)
         })?;
         let op = match val.val {
-            ConstValue::ByRef(ptr, align, _alloc) => {
+            ConstValue::ByRef { offset, align, alloc } => {
+                let id = self.tcx.alloc_map.lock().create_memory_alloc(alloc);
                 // We rely on mutability being set correctly in that allocation to prevent writes
                 // where none should happen.
-                let ptr = self.tag_static_base_pointer(ptr);
+                let ptr = self.tag_static_base_pointer(Pointer::new(id, offset));
                 Operand::Indirect(MemPlace::from_ptr(ptr, align))
             },
             ConstValue::Scalar(x) =>
diff --git a/src/librustc_mir/interpret/validity.rs b/src/librustc_mir/interpret/validity.rs
index d747edd..e42c667 100644
--- a/src/librustc_mir/interpret/validity.rs
+++ b/src/librustc_mir/interpret/validity.rs
@@ -193,7 +193,7 @@
                         // Sometimes the index is beyond the number of upvars (seen
                         // for a generator).
                         if let Some((&var_hir_id, _)) = upvars.get_index(field) {
-                            let node = self.ecx.tcx.hir().get_by_hir_id(var_hir_id);
+                            let node = self.ecx.tcx.hir().get(var_hir_id);
                             if let hir::Node::Binding(pat) = node {
                                 if let hir::PatKind::Binding(_, _, ident, _) = pat.node {
                                     name = Some(ident.name);
diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs
index a7cbe84..cb02e1a 100644
--- a/src/librustc_mir/lib.rs
+++ b/src/librustc_mir/lib.rs
@@ -30,7 +30,6 @@
 #![deny(rust_2018_idioms)]
 #![deny(internal)]
 #![deny(unused_lifetimes)]
-#![allow(explicit_outlives_requirements)]
 
 #[macro_use] extern crate log;
 #[macro_use]
diff --git a/src/librustc_mir/lints.rs b/src/librustc_mir/lints.rs
index a712088..8c815a5 100644
--- a/src/librustc_mir/lints.rs
+++ b/src/librustc_mir/lints.rs
@@ -10,7 +10,7 @@
 pub fn check(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId) {
     let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
 
-    if let Some(fn_like_node) = FnLikeNode::from_node(tcx.hir().get_by_hir_id(hir_id)) {
+    if let Some(fn_like_node) = FnLikeNode::from_node(tcx.hir().get(hir_id)) {
         check_fn_for_unconditional_recursion(tcx, fn_like_node.kind(), body, def_id);
     }
 }
diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs
index c64bb73..bb2738d 100644
--- a/src/librustc_mir/monomorphize/collector.rs
+++ b/src/librustc_mir/monomorphize/collector.rs
@@ -1262,7 +1262,7 @@
         ConstValue::Scalar(Scalar::Ptr(ptr)) =>
             collect_miri(tcx, ptr.alloc_id, output),
         ConstValue::Slice { data: alloc, start: _, end: _ } |
-        ConstValue::ByRef(_, _, alloc) => {
+        ConstValue::ByRef { alloc, .. } => {
             for &((), id) in alloc.relocations.values() {
                 collect_miri(tcx, id, output);
             }
diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs
index b5dbaaa..c7a2fdd 100644
--- a/src/librustc_mir/transform/const_prop.rs
+++ b/src/librustc_mir/transform/const_prop.rs
@@ -44,7 +44,7 @@
         let hir_id = tcx.hir().as_local_hir_id(source.def_id())
                               .expect("Non-local call to local provider is_const_fn");
 
-        let is_fn_like = FnLikeNode::from_node(tcx.hir().get_by_hir_id(hir_id)).is_some();
+        let is_fn_like = FnLikeNode::from_node(tcx.hir().get(hir_id)).is_some();
         let is_assoc_const = match tcx.def_kind(source.def_id()) {
             Some(DefKind::AssocConst) => true,
             _ => false,
diff --git a/src/librustc_mir/util/elaborate_drops.rs b/src/librustc_mir/util/elaborate_drops.rs
index 815d210..91fc19b 100644
--- a/src/librustc_mir/util/elaborate_drops.rs
+++ b/src/librustc_mir/util/elaborate_drops.rs
@@ -92,7 +92,7 @@
 #[derive(Debug)]
 struct DropCtxt<'l, 'b, 'tcx, D>
 where
-    D: DropElaborator<'b, 'tcx> + 'l,
+    D: DropElaborator<'b, 'tcx>,
 {
     elaborator: &'l mut D,
 
diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs
index 00b6db0..2da9c5a 100644
--- a/src/librustc_passes/ast_validation.rs
+++ b/src/librustc_passes/ast_validation.rs
@@ -17,13 +17,11 @@
 use syntax::feature_gate::is_builtin_attr;
 use syntax::source_map::Spanned;
 use syntax::symbol::{kw, sym};
-use syntax::ptr::P;
 use syntax::visit::{self, Visitor};
 use syntax::{span_err, struct_span_err, walk_list};
 use syntax_ext::proc_macro_decls::is_proc_macro_attr;
 use syntax_pos::{Span, MultiSpan};
 use errors::{Applicability, FatalError};
-use log::debug;
 
 #[derive(Copy, Clone, Debug)]
 struct OuterImplTrait {
@@ -319,54 +317,6 @@
         }
     }
 
-    /// With eRFC 2497, we need to check whether an expression is ambiguous and warn or error
-    /// depending on the edition, this function handles that.
-    fn while_if_let_ambiguity(&self, expr: &P<Expr>) {
-        if let Some((span, op_kind)) = self.while_if_let_expr_ambiguity(&expr) {
-            let mut err = self.err_handler().struct_span_err(
-                span, &format!("ambiguous use of `{}`", op_kind.to_string())
-            );
-
-            err.note(
-                "this will be a error until the `let_chains` feature is stabilized"
-            );
-            err.note(
-                "see rust-lang/rust#53668 for more information"
-            );
-
-            if let Ok(snippet) = self.session.source_map().span_to_snippet(span) {
-                err.span_suggestion(
-                    span, "consider adding parentheses", format!("({})", snippet),
-                    Applicability::MachineApplicable,
-                );
-            }
-
-            err.emit();
-        }
-    }
-
-    /// With eRFC 2497 adding if-let chains, there is a requirement that the parsing of
-    /// `&&` and `||` in a if-let statement be unambiguous. This function returns a span and
-    /// a `BinOpKind` (either `&&` or `||` depending on what was ambiguous) if it is determined
-    /// that the current expression parsed is ambiguous and will break in future.
-    fn while_if_let_expr_ambiguity(&self, expr: &P<Expr>) -> Option<(Span, BinOpKind)> {
-        debug!("while_if_let_expr_ambiguity: expr.node: {:?}", expr.node);
-        match &expr.node {
-            ExprKind::Binary(op, _, _) if op.node == BinOpKind::And || op.node == BinOpKind::Or => {
-                Some((expr.span, op.node))
-            },
-            ExprKind::Range(ref lhs, ref rhs, _) => {
-                let lhs_ambiguous = lhs.as_ref()
-                    .and_then(|lhs| self.while_if_let_expr_ambiguity(lhs));
-                let rhs_ambiguous = rhs.as_ref()
-                    .and_then(|rhs| self.while_if_let_expr_ambiguity(rhs));
-
-                lhs_ambiguous.or(rhs_ambiguous)
-            }
-            _ => None,
-        }
-    }
-
     fn check_fn_decl(&self, fn_decl: &FnDecl) {
         fn_decl
             .inputs
@@ -493,19 +443,17 @@
 
 impl<'a> Visitor<'a> for AstValidator<'a> {
     fn visit_expr(&mut self, expr: &'a Expr) {
-        match expr.node {
-            ExprKind::Closure(_, _, _, ref fn_decl, _, _) => {
+        match &expr.node {
+            ExprKind::Closure(_, _, _, fn_decl, _, _) => {
                 self.check_fn_decl(fn_decl);
             }
-            ExprKind::IfLet(_, ref expr, _, _) | ExprKind::WhileLet(_, ref expr, _, _) =>
-                self.while_if_let_ambiguity(&expr),
             ExprKind::InlineAsm(..) if !self.session.target.target.options.allow_asm => {
                 span_err!(self.session, expr.span, E0472, "asm! is unsupported on this target");
             }
             _ => {}
         }
 
-        visit::walk_expr(self, expr)
+        visit::walk_expr(self, expr);
     }
 
     fn visit_ty(&mut self, ty: &'a Ty) {
diff --git a/src/librustc_passes/lib.rs b/src/librustc_passes/lib.rs
index bf2f763..5f3d715 100644
--- a/src/librustc_passes/lib.rs
+++ b/src/librustc_passes/lib.rs
@@ -8,6 +8,7 @@
 
 #![feature(in_band_lifetimes)]
 #![feature(nll)]
+#![feature(bind_by_move_pattern_guards)]
 #![feature(rustc_diagnostic_macros)]
 
 #![recursion_limit="256"]
diff --git a/src/librustc_passes/loops.rs b/src/librustc_passes/loops.rs
index e1e4195..1c18322 100644
--- a/src/librustc_passes/loops.rs
+++ b/src/librustc_passes/loops.rs
@@ -116,7 +116,7 @@
                     let loop_kind = if loop_id == hir::DUMMY_HIR_ID {
                         None
                     } else {
-                        Some(match self.hir_map.expect_expr_by_hir_id(loop_id).node {
+                        Some(match self.hir_map.expect_expr(loop_id).node {
                             hir::ExprKind::While(..) => LoopKind::WhileLoop,
                             hir::ExprKind::Loop(_, _, source) => LoopKind::Loop(source),
                             ref r => span_bug!(e.span,
diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs
index ba834bf..0fdc9ac 100644
--- a/src/librustc_privacy/lib.rs
+++ b/src/librustc_privacy/lib.rs
@@ -229,14 +229,14 @@
 ) -> (ty::Visibility, Span, &'static str) {
     match tcx.hir().as_local_hir_id(def_id) {
         Some(hir_id) => {
-            let vis = match tcx.hir().get_by_hir_id(hir_id) {
+            let vis = match tcx.hir().get(hir_id) {
                 Node::Item(item) => &item.vis,
                 Node::ForeignItem(foreign_item) => &foreign_item.vis,
                 Node::TraitItem(..) | Node::Variant(..) => {
                     return def_id_visibility(tcx, tcx.hir().get_parent_did(hir_id));
                 }
                 Node::ImplItem(impl_item) => {
-                    match tcx.hir().get_by_hir_id(tcx.hir().get_parent_item(hir_id)) {
+                    match tcx.hir().get(tcx.hir().get_parent_item(hir_id)) {
                         Node::Item(item) => match &item.node {
                             hir::ItemKind::Impl(.., None, _, _) => &impl_item.vis,
                             hir::ItemKind::Impl(.., Some(trait_ref), _, _)
@@ -248,7 +248,7 @@
                 }
                 Node::Ctor(vdata) => {
                     let parent_hir_id = tcx.hir().get_parent_node_by_hir_id(hir_id);
-                    match tcx.hir().get_by_hir_id(parent_hir_id) {
+                    match tcx.hir().get(parent_hir_id) {
                         Node::Variant(..) => {
                             let parent_did = tcx.hir().local_def_id_from_hir_id(parent_hir_id);
                             let (mut ctor_vis, mut span, mut descr) = def_id_visibility(
@@ -274,7 +274,7 @@
                             return (ctor_vis, span, descr);
                         }
                         Node::Item(..) => {
-                            let item = match tcx.hir().get_by_hir_id(parent_hir_id) {
+                            let item = match tcx.hir().get(parent_hir_id) {
                                 Node::Item(item) => item,
                                 node => bug!("unexpected node kind: {:?}", node),
                             };
diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs
index 0fbd066..3b418d0 100644
--- a/src/librustc_resolve/lib.rs
+++ b/src/librustc_resolve/lib.rs
@@ -485,8 +485,6 @@
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
 enum PatternSource {
     Match,
-    IfLet,
-    WhileLet,
     Let,
     For,
     FnParam,
@@ -496,8 +494,6 @@
     fn descr(self) -> &'static str {
         match self {
             PatternSource::Match => "match binding",
-            PatternSource::IfLet => "if let binding",
-            PatternSource::WhileLet => "while let binding",
             PatternSource::Let => "let binding",
             PatternSource::For => "for binding",
             PatternSource::FnParam => "function parameter",
@@ -3057,15 +3053,9 @@
     fn resolve_arm(&mut self, arm: &Arm) {
         self.ribs[ValueNS].push(Rib::new(NormalRibKind));
 
-        let mut bindings_list = FxHashMap::default();
-        for pattern in &arm.pats {
-            self.resolve_pattern(&pattern, PatternSource::Match, &mut bindings_list);
-        }
+        self.resolve_pats(&arm.pats, PatternSource::Match);
 
-        // This has to happen *after* we determine which pat_idents are variants.
-        self.check_consistent_bindings(&arm.pats);
-
-        if let Some(ast::Guard::If(ref expr)) = arm.guard {
+        if let Some(ref expr) = arm.guard {
             self.visit_expr(expr)
         }
         self.visit_expr(&arm.body);
@@ -3073,6 +3063,16 @@
         self.ribs[ValueNS].pop();
     }
 
+    /// Arising from `source`, resolve a sequence of patterns (top level or-patterns).
+    fn resolve_pats(&mut self, pats: &[P<Pat>], source: PatternSource) {
+        let mut bindings_list = FxHashMap::default();
+        for pat in pats {
+            self.resolve_pattern(pat, source, &mut bindings_list);
+        }
+        // This has to happen *after* we determine which pat_idents are variants
+        self.check_consistent_bindings(pats);
+    }
+
     fn resolve_block(&mut self, block: &Block) {
         debug!("(resolving block) entering block");
         // Move down in the graph, if there's an anonymous module rooted here.
@@ -3151,8 +3151,7 @@
                 );
             }
             Some(..) if pat_src == PatternSource::Match ||
-                        pat_src == PatternSource::IfLet ||
-                        pat_src == PatternSource::WhileLet => {
+                        pat_src == PatternSource::Let => {
                 // `Variant1(a) | Variant2(a)`, ok
                 // Reuse definition from the first `a`.
                 res = self.ribs[ValueNS].last_mut().unwrap().bindings[&ident];
@@ -4345,41 +4344,26 @@
                 visit::walk_expr(self, expr);
             }
 
-            ExprKind::IfLet(ref pats, ref subexpression, ref if_block, ref optional_else) => {
-                self.visit_expr(subexpression);
+            ExprKind::Let(ref pats, ref scrutinee) => {
+                self.visit_expr(scrutinee);
+                self.resolve_pats(pats, PatternSource::Let);
+            }
 
+            ExprKind::If(ref cond, ref then, ref opt_else) => {
                 self.ribs[ValueNS].push(Rib::new(NormalRibKind));
-                let mut bindings_list = FxHashMap::default();
-                for pat in pats {
-                    self.resolve_pattern(pat, PatternSource::IfLet, &mut bindings_list);
-                }
-                // This has to happen *after* we determine which pat_idents are variants
-                self.check_consistent_bindings(pats);
-                self.visit_block(if_block);
+                self.visit_expr(cond);
+                self.visit_block(then);
                 self.ribs[ValueNS].pop();
 
-                optional_else.as_ref().map(|expr| self.visit_expr(expr));
+                opt_else.as_ref().map(|expr| self.visit_expr(expr));
             }
 
             ExprKind::Loop(ref block, label) => self.resolve_labeled_block(label, expr.id, &block),
 
             ExprKind::While(ref subexpression, ref block, label) => {
                 self.with_resolved_label(label, expr.id, |this| {
-                    this.visit_expr(subexpression);
-                    this.visit_block(block);
-                });
-            }
-
-            ExprKind::WhileLet(ref pats, ref subexpression, ref block, label) => {
-                self.with_resolved_label(label, expr.id, |this| {
-                    this.visit_expr(subexpression);
                     this.ribs[ValueNS].push(Rib::new(NormalRibKind));
-                    let mut bindings_list = FxHashMap::default();
-                    for pat in pats {
-                        this.resolve_pattern(pat, PatternSource::WhileLet, &mut bindings_list);
-                    }
-                    // This has to happen *after* we determine which pat_idents are variants.
-                    this.check_consistent_bindings(pats);
+                    this.visit_expr(subexpression);
                     this.visit_block(block);
                     this.ribs[ValueNS].pop();
                 });
diff --git a/src/librustc_save_analysis/dump_visitor.rs b/src/librustc_save_analysis/dump_visitor.rs
index a7f46e8..beef9f1 100644
--- a/src/librustc_save_analysis/dump_visitor.rs
+++ b/src/librustc_save_analysis/dump_visitor.rs
@@ -1531,7 +1531,8 @@
         self.process_macro_use(ex.span);
         match ex.node {
             ast::ExprKind::Struct(ref path, ref fields, ref base) => {
-                let hir_expr = self.save_ctxt.tcx.hir().expect_expr(ex.id);
+                let expr_hir_id = self.save_ctxt.tcx.hir().node_to_hir_id(ex.id);
+                let hir_expr = self.save_ctxt.tcx.hir().expect_expr(expr_hir_id);
                 let adt = match self.save_ctxt.tables.expr_ty_opt(&hir_expr) {
                     Some(ty) if ty.ty_adt_def().is_some() => ty.ty_adt_def().unwrap(),
                     _ => {
@@ -1579,17 +1580,9 @@
                 self.visit_expr(subexpression);
                 visit::walk_block(self, block);
             }
-            ast::ExprKind::WhileLet(ref pats, ref subexpression, ref block, _) => {
+            ast::ExprKind::Let(ref pats, ref scrutinee) => {
                 self.process_var_decl_multi(pats);
-                debug!("for loop, walk sub-expr: {:?}", subexpression.node);
-                self.visit_expr(subexpression);
-                visit::walk_block(self, block);
-            }
-            ast::ExprKind::IfLet(ref pats, ref subexpression, ref block, ref opt_else) => {
-                self.process_var_decl_multi(pats);
-                self.visit_expr(subexpression);
-                visit::walk_block(self, block);
-                opt_else.as_ref().map(|el| self.visit_expr(el));
+                self.visit_expr(scrutinee);
             }
             ast::ExprKind::Repeat(ref element, ref count) => {
                 self.visit_expr(element);
@@ -1616,9 +1609,8 @@
 
     fn visit_arm(&mut self, arm: &'l ast::Arm) {
         self.process_var_decl_multi(&arm.pats);
-        match arm.guard {
-            Some(ast::Guard::If(ref expr)) => self.visit_expr(expr),
-            _ => {}
+        if let Some(expr) = &arm.guard {
+            self.visit_expr(expr);
         }
         self.visit_expr(&arm.body);
     }
diff --git a/src/librustc_save_analysis/lib.rs b/src/librustc_save_analysis/lib.rs
index bc813b3..23fe150 100644
--- a/src/librustc_save_analysis/lib.rs
+++ b/src/librustc_save_analysis/lib.rs
@@ -513,7 +513,8 @@
     }
 
     pub fn get_expr_data(&self, expr: &ast::Expr) -> Option<Data> {
-        let hir_node = self.tcx.hir().expect_expr(expr.id);
+        let expr_hir_id = self.tcx.hir().node_to_hir_id(expr.id);
+        let hir_node = self.tcx.hir().expect_expr(expr_hir_id);
         let ty = self.tables.expr_ty_adjusted_opt(&hir_node);
         if ty.is_none() || ty.unwrap().sty == ty::Error {
             return None;
@@ -606,7 +607,8 @@
     }
 
     pub fn get_path_res(&self, id: NodeId) -> Res {
-        match self.tcx.hir().get(id) {
+        let hir_id = self.tcx.hir().node_to_hir_id(id);
+        match self.tcx.hir().get(hir_id) {
             Node::TraitRef(tr) => tr.path.res,
 
             Node::Item(&hir::Item {
@@ -627,7 +629,6 @@
                 node: hir::ExprKind::Struct(ref qpath, ..),
                 ..
             }) => {
-                let hir_id = self.tcx.hir().node_to_hir_id(id);
                 self.tables.qpath_res(qpath, hir_id)
             }
 
@@ -651,7 +652,6 @@
                 node: hir::TyKind::Path(ref qpath),
                 ..
             }) => {
-                let hir_id = self.tcx.hir().node_to_hir_id(id);
                 self.tables.qpath_res(qpath, hir_id)
             }
 
diff --git a/src/librustc_target/spec/mod.rs b/src/librustc_target/spec/mod.rs
index 08cf062..75821ab 100644
--- a/src/librustc_target/spec/mod.rs
+++ b/src/librustc_target/spec/mod.rs
@@ -119,19 +119,19 @@
     ($((($($flavor:tt)*), $string:expr),)*) => (
         impl LinkerFlavor {
             pub const fn one_of() -> &'static str {
-                concat!("one of: ", $($string, " ",)+)
+                concat!("one of: ", $($string, " ",)*)
             }
 
             pub fn from_str(s: &str) -> Option<Self> {
                 Some(match s {
-                    $($string => $($flavor)*,)+
+                    $($string => $($flavor)*,)*
                     _ => return None,
                 })
             }
 
             pub fn desc(&self) -> &str {
                 match *self {
-                    $($($flavor)* => $string,)+
+                    $($($flavor)* => $string,)*
                 }
             }
         }
diff --git a/src/librustc_traits/lowering/environment.rs b/src/librustc_traits/lowering/environment.rs
index d1bad6b..0173685 100644
--- a/src/librustc_traits/lowering/environment.rs
+++ b/src/librustc_traits/lowering/environment.rs
@@ -185,7 +185,7 @@
         .map(Clause::ForAll);
 
     let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
-    let node = tcx.hir().get_by_hir_id(hir_id);
+    let node = tcx.hir().get(hir_id);
 
     enum NodeKind {
         TraitImpl,
diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs
index 267f379..33d9b1f 100644
--- a/src/librustc_typeck/astconv.rs
+++ b/src/librustc_typeck/astconv.rs
@@ -123,7 +123,7 @@
     {
         let tcx = self.tcx();
         let lifetime_name = |def_id| {
-            tcx.hir().name_by_hir_id(tcx.hir().as_local_hir_id(def_id).unwrap()).as_interned_str()
+            tcx.hir().name(tcx.hir().as_local_hir_id(def_id).unwrap()).as_interned_str()
         };
 
         let r = match tcx.named_region(lifetime.hir_id) {
@@ -1253,7 +1253,7 @@
 
         if regular_traits.is_empty() && auto_traits.is_empty() {
             span_err!(tcx.sess, span, E0224,
-                "at least one non-builtin trait is required for an object type");
+                "at least one trait is required for an object type");
             return tcx.types.err;
         }
 
@@ -2004,7 +2004,7 @@
                 let item_def_id = tcx.hir().local_def_id_from_hir_id(item_id);
                 let generics = tcx.generics_of(item_def_id);
                 let index = generics.param_def_id_to_index[&def_id];
-                tcx.mk_ty_param(index, tcx.hir().name_by_hir_id(hir_id).as_interned_str())
+                tcx.mk_ty_param(index, tcx.hir().name(hir_id).as_interned_str())
             }
             Res::SelfTy(Some(_), None) => {
                 // `Self` in trait or type alias.
@@ -2194,7 +2194,7 @@
             let item_def_id = tcx.hir().local_def_id_from_hir_id(item_id);
             let generics = tcx.generics_of(item_def_id);
             let index = generics.param_def_id_to_index[&tcx.hir().local_def_id_from_hir_id(hir_id)];
-            let name = tcx.hir().name_by_hir_id(hir_id).as_interned_str();
+            let name = tcx.hir().name(hir_id).as_interned_str();
             const_.val = ConstValue::Param(ty::ParamConst::new(index, name));
         }
 
diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs
index 65a36d9..293b68c 100644
--- a/src/librustc_typeck/check/_match.rs
+++ b/src/librustc_typeck/check/_match.rs
@@ -547,7 +547,7 @@
         let tcx = self.tcx;
         if let PatKind::Binding(..) = inner.node {
             let parent_id = tcx.hir().get_parent_node_by_hir_id(pat.hir_id);
-            let parent = tcx.hir().get_by_hir_id(parent_id);
+            let parent = tcx.hir().get(parent_id);
             debug!("inner {:?} pat {:?} parent {:?}", inner, pat, parent);
             match parent {
                 hir::Node::Item(hir::Item { node: hir::ItemKind::Fn(..), .. }) |
@@ -812,10 +812,10 @@
         let match_id = hir.get_parent_node_by_hir_id(arm_id);
         let containing_id = hir.get_parent_node_by_hir_id(match_id);
 
-        let node = hir.get_by_hir_id(containing_id);
+        let node = hir.get(containing_id);
         if let Block(block) = node {
             // check that the body's parent is an fn
-            let parent = hir.get_by_hir_id(
+            let parent = hir.get(
                 hir.get_parent_node_by_hir_id(
                     hir.get_parent_node_by_hir_id(block.hir_id),
                 ),
diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs
index 42c7ff6..87807ad 100644
--- a/src/librustc_typeck/check/callee.rs
+++ b/src/librustc_typeck/check/callee.rs
@@ -245,7 +245,7 @@
         callee_span: Span,
     ) {
         let hir_id = self.tcx.hir().get_parent_node_by_hir_id(hir_id);
-        let parent_node = self.tcx.hir().get_by_hir_id(hir_id);
+        let parent_node = self.tcx.hir().get(hir_id);
         if let (
             hir::Node::Expr(hir::Expr { node: hir::ExprKind::Closure(_, _, _, sp, ..), .. }),
             hir::ExprKind::Block(..),
diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs
index a56196c..71a0ca0 100644
--- a/src/librustc_typeck/check/coercion.rs
+++ b/src/librustc_typeck/check/coercion.rs
@@ -1291,7 +1291,7 @@
                 cause.span,
                 blk_id,
             );
-            let parent = fcx.tcx.hir().get_by_hir_id(parent_id);
+            let parent = fcx.tcx.hir().get(parent_id);
             fcx.get_node_fn_decl(parent).map(|(fn_decl, _, is_main)| (fn_decl, is_main))
         } else {
             fcx.get_fn_decl(parent_id)
diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs
index 5943302..8ad67c2 100644
--- a/src/librustc_typeck/check/method/suggest.rs
+++ b/src/librustc_typeck/check/method/suggest.rs
@@ -268,7 +268,7 @@
                                         let snippet = tcx.sess.source_map().span_to_snippet(span);
                                         let filename = tcx.sess.source_map().span_to_filename(span);
 
-                                        let parent_node = self.tcx.hir().get_by_hir_id(
+                                        let parent_node = self.tcx.hir().get(
                                             self.tcx.hir().get_parent_node_by_hir_id(hir_id),
                                         );
                                         let msg = format!(
@@ -389,7 +389,7 @@
                                     Applicability::MachineApplicable,
                                 );
                             } else {
-                                let call_expr = self.tcx.hir().expect_expr_by_hir_id(
+                                let call_expr = self.tcx.hir().expect_expr(
                                     self.tcx.hir().get_parent_node_by_hir_id(expr.hir_id),
                                 );
 
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 0e83db4..0ec5c97 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -759,7 +759,7 @@
     tcx: TyCtxt<'tcx>,
     id: hir::HirId,
 ) -> Option<(hir::BodyId, Option<&'tcx hir::FnDecl>)> {
-    match tcx.hir().get_by_hir_id(id) {
+    match tcx.hir().get(id) {
         Node::Item(item) => {
             match item.node {
                 hir::ItemKind::Const(_, body) |
@@ -993,10 +993,9 @@
         if let PatKind::Binding(_, _, ident, _) = p.node {
             let var_ty = self.assign(p.span, p.hir_id, None);
 
-            let node_id = self.fcx.tcx.hir().hir_to_node_id(p.hir_id);
             if !self.fcx.tcx.features().unsized_locals {
                 self.fcx.require_type_is_sized(var_ty, p.span,
-                                               traits::VariableType(node_id));
+                                               traits::VariableType(p.hir_id));
             }
 
             debug!("Pattern binding {} is assigned to {} with type {:?}",
@@ -1214,7 +1213,7 @@
                         );
                     }
 
-                    if let Node::Item(item) = fcx.tcx.hir().get_by_hir_id(fn_id) {
+                    if let Node::Item(item) = fcx.tcx.hir().get(fn_id) {
                         if let ItemKind::Fn(_, _, ref generics, _) = item.node {
                             if !generics.params.is_empty() {
                                 fcx.tcx.sess.span_err(
@@ -1262,7 +1261,7 @@
                         );
                     }
 
-                    if let Node::Item(item) = fcx.tcx.hir().get_by_hir_id(fn_id) {
+                    if let Node::Item(item) = fcx.tcx.hir().get(fn_id) {
                         if let ItemKind::Fn(_, _, ref generics, _) = item.node {
                             if !generics.params.is_empty() {
                                 fcx.tcx.sess.span_err(
@@ -1448,8 +1447,8 @@
     };
     let param_env = ty::ParamEnv::reveal_all();
     if let Ok(static_) = tcx.const_eval(param_env.and(cid)) {
-        let alloc = if let ConstValue::ByRef(_, _, allocation) = static_.val {
-            allocation
+        let alloc = if let ConstValue::ByRef { alloc, .. } = static_.val {
+            alloc
         } else {
             bug!("Matching on non-ByRef static")
         };
@@ -3677,7 +3676,7 @@
     }
 
     fn parent_item_span(&self, id: hir::HirId) -> Option<Span> {
-        let node = self.tcx.hir().get_by_hir_id(self.tcx.hir().get_parent_item(id));
+        let node = self.tcx.hir().get(self.tcx.hir().get_parent_item(id));
         match node {
             Node::Item(&hir::Item {
                 node: hir::ItemKind::Fn(_, _, _, body_id), ..
@@ -3697,7 +3696,7 @@
 
     /// Given a function block's `HirId`, returns its `FnDecl` if it exists, or `None` otherwise.
     fn get_parent_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl, ast::Ident)> {
-        let parent = self.tcx.hir().get_by_hir_id(self.tcx.hir().get_parent_item(blk_id));
+        let parent = self.tcx.hir().get(self.tcx.hir().get_parent_item(blk_id));
         self.get_node_fn_decl(parent).map(|(fn_decl, ident, _)| (fn_decl, ident))
     }
 
@@ -3732,7 +3731,7 @@
         // Get enclosing Fn, if it is a function or a trait method, unless there's a `loop` or
         // `while` before reaching it, as block tail returns are not available in them.
         self.tcx.hir().get_return_block(blk_id).and_then(|blk_id| {
-            let parent = self.tcx.hir().get_by_hir_id(blk_id);
+            let parent = self.tcx.hir().get(blk_id);
             self.get_node_fn_decl(parent).map(|(fn_decl, _, is_main)| (fn_decl, is_main))
         })
     }
@@ -4259,7 +4258,7 @@
 
         // If our calling expression is indeed the function itself, we're good!
         // If not, generate an error that this can only be called directly.
-        if let Node::Expr(expr) = self.tcx.hir().get_by_hir_id(
+        if let Node::Expr(expr) = self.tcx.hir().get(
             self.tcx.hir().get_parent_node_by_hir_id(hir_id))
         {
             if let ExprKind::Call(ref callee, ..) = expr.node {
@@ -4335,7 +4334,7 @@
         let mut contained_in_place = false;
 
         while let hir::Node::Expr(parent_expr) =
-            self.tcx.hir().get_by_hir_id(self.tcx.hir().get_parent_node_by_hir_id(expr_id))
+            self.tcx.hir().get(self.tcx.hir().get_parent_node_by_hir_id(expr_id))
         {
             match &parent_expr.node {
                 hir::ExprKind::Assign(lhs, ..) | hir::ExprKind::AssignOp(_, lhs, ..) => {
diff --git a/src/librustc_typeck/check/upvar.rs b/src/librustc_typeck/check/upvar.rs
index bba108a..ac39757 100644
--- a/src/librustc_typeck/check/upvar.rs
+++ b/src/librustc_typeck/check/upvar.rs
@@ -652,5 +652,5 @@
 }
 
 fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> ast::Name {
-    tcx.hir().name_by_hir_id(var_hir_id)
+    tcx.hir().name(var_hir_id)
 }
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index 52cda4a..301168a 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -284,7 +284,7 @@
     let mut extend = None;
 
     let item_hir_id = tcx.hir().as_local_hir_id(item_def_id).unwrap();
-    let ast_generics = match tcx.hir().get_by_hir_id(item_hir_id) {
+    let ast_generics = match tcx.hir().get(item_hir_id) {
         Node::TraitItem(item) => &item.generics,
 
         Node::ImplItem(item) => &item.generics,
@@ -623,7 +623,7 @@
     use rustc::hir::*;
 
     let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
-    let item = match tcx.hir().get_by_hir_id(hir_id) {
+    let item = match tcx.hir().get(hir_id) {
         Node::Item(item) => item,
         _ => bug!(),
     };
@@ -693,7 +693,7 @@
     debug!("super_predicates(trait_def_id={:?})", trait_def_id);
     let trait_hir_id = tcx.hir().as_local_hir_id(trait_def_id).unwrap();
 
-    let item = match tcx.hir().get_by_hir_id(trait_hir_id) {
+    let item = match tcx.hir().get(trait_hir_id) {
         Node::Item(item) => item,
         _ => bug!("trait_node_id {} is not an item", trait_hir_id),
     };
@@ -884,7 +884,7 @@
 
     let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
 
-    let node = tcx.hir().get_by_hir_id(hir_id);
+    let node = tcx.hir().get(hir_id);
     let parent_def_id = match node {
         Node::ImplItem(_) | Node::TraitItem(_) | Node::Variant(_) |
         Node::Ctor(..) | Node::Field(_) => {
@@ -1154,7 +1154,7 @@
 
     let icx = ItemCtxt::new(tcx, def_id);
 
-    Some(match tcx.hir().get_by_hir_id(hir_id) {
+    Some(match tcx.hir().get(hir_id) {
         Node::TraitItem(item) => match item.node {
             TraitItemKind::Method(..) => {
                 let substs = InternalSubsts::identity_for_item(tcx, def_id);
@@ -1298,7 +1298,7 @@
         }
 
         Node::AnonConst(_) => {
-            let parent_node = tcx.hir().get_by_hir_id(tcx.hir().get_parent_node_by_hir_id(hir_id));
+            let parent_node = tcx.hir().get(tcx.hir().get_parent_node_by_hir_id(hir_id));
             match parent_node {
                 Node::Ty(&hir::Ty {
                     node: hir::TyKind::Array(_, ref constant),
@@ -1660,8 +1660,8 @@
     if scope == hir::CRATE_HIR_ID {
         intravisit::walk_crate(&mut locator, tcx.hir().krate());
     } else {
-        debug!("find_existential_constraints: scope={:?}", tcx.hir().get_by_hir_id(scope));
-        match tcx.hir().get_by_hir_id(scope) {
+        debug!("find_existential_constraints: scope={:?}", tcx.hir().get(scope));
+        match tcx.hir().get(scope) {
             Node::Item(ref it) => intravisit::walk_item(&mut locator, it),
             Node::ImplItem(ref it) => intravisit::walk_impl_item(&mut locator, it),
             Node::TraitItem(ref it) => intravisit::walk_trait_item(&mut locator, it),
@@ -1690,7 +1690,7 @@
 
     let icx = ItemCtxt::new(tcx, def_id);
 
-    match tcx.hir().get_by_hir_id(hir_id) {
+    match tcx.hir().get(hir_id) {
         TraitItem(hir::TraitItem {
             node: TraitItemKind::Method(sig, _),
             ..
@@ -1903,7 +1903,7 @@
         Some(hir_id) => hir_id,
         None => return tcx.predicates_of(def_id),
     };
-    let node = tcx.hir().get_by_hir_id(hir_id);
+    let node = tcx.hir().get(hir_id);
 
     let mut is_trait = None;
     let mut is_default_impl_trait = None;
diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs
index cc6f7a0..ec0f431 100644
--- a/src/librustc_typeck/lib.rs
+++ b/src/librustc_typeck/lib.rs
@@ -75,7 +75,6 @@
 #![deny(rust_2018_idioms)]
 #![deny(internal)]
 #![deny(unused_lifetimes)]
-#![allow(explicit_outlives_requirements)]
 
 #[macro_use] extern crate log;
 #[macro_use] extern crate syntax;
diff --git a/src/librustc_typeck/outlives/implicit_infer.rs b/src/librustc_typeck/outlives/implicit_infer.rs
index 1c2bb8c..f2661b4 100644
--- a/src/librustc_typeck/outlives/implicit_infer.rs
+++ b/src/librustc_typeck/outlives/implicit_infer.rs
@@ -61,7 +61,7 @@
             .hir()
             .as_local_hir_id(item_did)
             .expect("expected local def-id");
-        let item = match self.tcx.hir().get_by_hir_id(hir_id) {
+        let item = match self.tcx.hir().get(hir_id) {
             Node::Item(item) => item,
             _ => bug!(),
         };
diff --git a/src/librustc_typeck/outlives/mod.rs b/src/librustc_typeck/outlives/mod.rs
index ad538b0..63e41e0 100644
--- a/src/librustc_typeck/outlives/mod.rs
+++ b/src/librustc_typeck/outlives/mod.rs
@@ -29,7 +29,7 @@
         .as_local_hir_id(item_def_id)
         .expect("expected local def-id");
 
-    match tcx.hir().get_by_hir_id(id) {
+    match tcx.hir().get(id) {
         Node::Item(item) => match item.node {
             hir::ItemKind::Struct(..) | hir::ItemKind::Enum(..) | hir::ItemKind::Union(..) => {
                 let crate_map = tcx.inferred_outlives_crate(LOCAL_CRATE);
diff --git a/src/librustc_typeck/variance/mod.rs b/src/librustc_typeck/variance/mod.rs
index 5dbd667..1a8871a 100644
--- a/src/librustc_typeck/variance/mod.rs
+++ b/src/librustc_typeck/variance/mod.rs
@@ -48,7 +48,7 @@
         // Variance not relevant.
         span_bug!(tcx.hir().span(id), "asked to compute variance for wrong kind of item")
     };
-    match tcx.hir().get_by_hir_id(id) {
+    match tcx.hir().get(id) {
         Node::Item(item) => match item.node {
             hir::ItemKind::Enum(..) |
             hir::ItemKind::Struct(..) |
diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml
index b75212d..3158ec3 100644
--- a/src/librustdoc/Cargo.toml
+++ b/src/librustdoc/Cargo.toml
@@ -11,5 +11,6 @@
 [dependencies]
 pulldown-cmark = { version = "0.5.2", default-features = false }
 minifier = "0.0.30"
+rayon = { version = "0.2.0", package = "rustc-rayon" }
 tempfile = "3"
 parking_lot = "0.7"
diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs
index 3dcf77e..5a4dc7b 100644
--- a/src/librustdoc/clean/auto_trait.rs
+++ b/src/librustdoc/clean/auto_trait.rs
@@ -27,9 +27,7 @@
         let param_env = self.cx.tcx.param_env(param_env_def_id);
 
         debug!("get_auto_trait_impls({:?})", ty);
-        let auto_traits = self.cx.send_trait.into_iter().chain(
-            Some(self.cx.tcx.require_lang_item(lang_items::SyncTraitLangItem))
-        );
+        let auto_traits = self.cx.auto_traits.iter().cloned();
         auto_traits.filter_map(|trait_def_id| {
             let trait_ref = ty::TraitRef {
                 def_id: trait_def_id,
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index b489ccf..41a5675 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -17,8 +17,8 @@
 use rustc::middle::lang_items;
 use rustc::middle::stability;
 use rustc::mir::interpret::{GlobalId, ConstValue};
-use rustc::hir::{self, HirVec};
-use rustc::hir::def::{self, Res, DefKind, CtorKind};
+use rustc::hir;
+use rustc::hir::def::{CtorKind, DefKind, Res};
 use rustc::hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
 use rustc::ty::subst::{InternalSubsts, SubstsRef, UnpackedKind};
 use rustc::ty::{self, DefIdTree, TyCtxt, Region, RegionVid, Ty, AdtKind};
@@ -4418,72 +4418,6 @@
     r
 }
 
-// Start of code copied from rust-clippy
-
-pub fn path_to_def_local(tcx: TyCtxt<'_>, path: &[Symbol]) -> Option<DefId> {
-    let krate = tcx.hir().krate();
-    let mut items = krate.module.item_ids.clone();
-    let mut path_it = path.iter().peekable();
-
-    loop {
-        let segment = path_it.next()?;
-
-        for item_id in mem::replace(&mut items, HirVec::new()).iter() {
-            let item = tcx.hir().expect_item(item_id.id);
-            if item.ident.name == *segment {
-                if path_it.peek().is_none() {
-                    return Some(tcx.hir().local_def_id_from_hir_id(item_id.id))
-                }
-
-                items = match &item.node {
-                    &hir::ItemKind::Mod(ref m) => m.item_ids.clone(),
-                    _ => panic!("Unexpected item {:?} in path {:?} path")
-                };
-                break;
-            }
-        }
-    }
-}
-
-pub fn path_to_def(tcx: TyCtxt<'_>, path: &[Symbol]) -> Option<DefId> {
-    let crates = tcx.crates();
-
-    let krate = crates
-        .iter()
-        .find(|&&krate| tcx.crate_name(krate) == path[0]);
-
-    if let Some(krate) = krate {
-        let krate = DefId {
-            krate: *krate,
-            index: CRATE_DEF_INDEX,
-        };
-        let mut items = tcx.item_children(krate);
-        let mut path_it = path.iter().skip(1).peekable();
-
-        loop {
-            let segment = path_it.next()?;
-
-            for item in mem::replace(&mut items, &[]).iter() {
-                if item.ident.name == *segment {
-                    if path_it.peek().is_none() {
-                        return match item.res {
-                            def::Res::Def(DefKind::Trait, did) => Some(did),
-                            _ => None,
-                        }
-                    }
-
-                    items = tcx.item_children(item.res.def_id());
-                    break;
-                }
-            }
-        }
-    } else {
-        None
-    }
-}
-
-// End of code copied from rust-clippy
-
 #[derive(Eq, PartialEq, Hash, Copy, Clone, Debug)]
 enum RegionTarget<'tcx> {
     Region(Region<'tcx>),
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index 20a4f86..7da501e 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -67,13 +67,13 @@
     pub ct_substs: RefCell<FxHashMap<DefId, clean::Constant>>,
     /// Table DefId of `impl Trait` in argument position -> bounds
     pub impl_trait_bounds: RefCell<FxHashMap<DefId, Vec<clean::GenericBound>>>,
-    pub send_trait: Option<DefId>,
     pub fake_def_ids: RefCell<FxHashMap<CrateNum, DefId>>,
     pub all_fake_def_ids: RefCell<FxHashSet<DefId>>,
     /// Auto-trait or blanket impls processed so far, as `(self_ty, trait_def_id)`.
     // FIXME(eddyb) make this a `ty::TraitRef<'tcx>` set.
     pub generated_synthetics: RefCell<FxHashSet<(Ty<'tcx>, DefId)>>,
     pub all_traits: Vec<DefId>,
+    pub auto_traits: Vec<DefId>,
 }
 
 impl<'tcx> DocContext<'tcx> {
@@ -367,15 +367,10 @@
                                     .collect()
             };
 
-            let send_trait = if crate_name == Some("core".to_string()) {
-                clean::path_to_def_local(tcx, &[sym::marker, sym::Send])
-            } else {
-                clean::path_to_def(tcx, &[sym::core, sym::marker, sym::Send])
-            };
-
             let mut renderinfo = RenderInfo::default();
             renderinfo.access_levels = access_levels;
 
+            let all_traits = tcx.all_traits(LOCAL_CRATE).to_vec();
             let ctxt = DocContext {
                 tcx,
                 resolver,
@@ -388,11 +383,13 @@
                 lt_substs: Default::default(),
                 ct_substs: Default::default(),
                 impl_trait_bounds: Default::default(),
-                send_trait: send_trait,
                 fake_def_ids: Default::default(),
                 all_fake_def_ids: Default::default(),
                 generated_synthetics: Default::default(),
-                all_traits: tcx.all_traits(LOCAL_CRATE).to_vec(),
+                auto_traits: all_traits.iter().cloned().filter(|trait_def_id| {
+                    tcx.trait_is_auto(*trait_def_id)
+                }).collect(),
+                all_traits,
             };
             debug!("crate: {:?}", tcx.hir().krate());
 
diff --git a/src/librustdoc/docfs.rs b/src/librustdoc/docfs.rs
new file mode 100644
index 0000000..740947f
--- /dev/null
+++ b/src/librustdoc/docfs.rs
@@ -0,0 +1,116 @@
+//! Rustdoc's FileSystem abstraction module.
+//!
+//! On Windows this indirects IO into threads to work around performance issues
+//! with Defender (and other similar virus scanners that do blocking operations).
+//! On other platforms this is a thin shim to fs.
+//!
+//! Only calls needed to permit this workaround have been abstracted: thus
+//! fs::read is still done directly via the fs module; if in future rustdoc
+//! needs to read-after-write from a file, then it would be added to this
+//! abstraction.
+
+use errors;
+
+use std::fs;
+use std::io;
+use std::path::Path;
+use std::sync::Arc;
+use std::sync::mpsc::{channel, Receiver, Sender};
+
+macro_rules! try_err {
+    ($e:expr, $file:expr) => {{
+        match $e {
+            Ok(e) => e,
+            Err(e) => return Err(E::new(e, $file)),
+        }
+    }};
+}
+
+pub trait PathError {
+    fn new<P: AsRef<Path>>(e: io::Error, path: P) -> Self;
+}
+
+pub struct ErrorStorage {
+    sender: Option<Sender<Option<String>>>,
+    receiver: Receiver<Option<String>>,
+}
+
+impl ErrorStorage {
+    pub fn new() -> ErrorStorage {
+        let (sender, receiver) = channel();
+        ErrorStorage {
+            sender: Some(sender),
+            receiver,
+        }
+    }
+
+    /// Prints all stored errors. Returns the number of printed errors.
+    pub fn write_errors(&mut self, diag: &errors::Handler) -> usize {
+        let mut printed = 0;
+        // In order to drop the sender part of the channel.
+        self.sender = None;
+
+        for msg in self.receiver.iter() {
+            if let Some(ref error) = msg {
+                diag.struct_err(&error).emit();
+                printed += 1;
+            }
+        }
+        printed
+    }
+}
+
+pub struct DocFS {
+    sync_only: bool,
+    errors: Arc<ErrorStorage>,
+}
+
+impl DocFS {
+    pub fn new(errors: &Arc<ErrorStorage>) -> DocFS {
+        DocFS {
+            sync_only: false,
+            errors: Arc::clone(errors),
+        }
+    }
+
+    pub fn set_sync_only(&mut self, sync_only: bool) {
+        self.sync_only = sync_only;
+    }
+
+    pub fn create_dir_all<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
+        // For now, dir creation isn't a huge time consideration, do it
+        // synchronously, which avoids needing ordering between write() actions
+        // and directory creation.
+        fs::create_dir_all(path)
+    }
+
+    pub fn write<P, C, E>(&self, path: P, contents: C) -> Result<(), E>
+    where
+        P: AsRef<Path>,
+        C: AsRef<[u8]>,
+        E: PathError,
+    {
+        if !self.sync_only && cfg!(windows) {
+            // A possible future enhancement after more detailed profiling would
+            // be to create the file sync so errors are reported eagerly.
+            let contents = contents.as_ref().to_vec();
+            let path = path.as_ref().to_path_buf();
+            let sender = self.errors.sender.clone().unwrap();
+            rayon::spawn(move || {
+                match fs::write(&path, &contents) {
+                    Ok(_) => {
+                        sender.send(None)
+                            .expect(&format!("failed to send error on \"{}\"", path.display()));
+                    }
+                    Err(e) => {
+                        sender.send(Some(format!("\"{}\": {}", path.display(), e)))
+                            .expect(&format!("failed to send non-error on \"{}\"", path.display()));
+                    }
+                }
+            });
+            Ok(())
+        } else {
+            Ok(try_err!(fs::write(&path, contents), path))
+        }
+    }
+}
diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index b42a78f..f0aff96 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -35,9 +35,9 @@
 use std::error;
 use std::fmt::{self, Display, Formatter, Write as FmtWrite};
 use std::ffi::OsStr;
-use std::fs::{self, File, OpenOptions};
+use std::fs::{self, File};
 use std::io::prelude::*;
-use std::io::{self, BufWriter, BufReader};
+use std::io::{self, BufReader};
 use std::mem;
 use std::path::{PathBuf, Path, Component};
 use std::str;
@@ -61,6 +61,7 @@
 
 use crate::clean::{self, AttributesExt, Deprecation, GetDefId, SelfTy, Mutability};
 use crate::config::RenderOptions;
+use crate::docfs::{DocFS, ErrorStorage, PathError};
 use crate::doctree;
 use crate::fold::DocFolder;
 use crate::html::escape::Escape;
@@ -89,6 +90,58 @@
     }
 }
 
+#[derive(Debug)]
+pub struct Error {
+    pub file: PathBuf,
+    pub error: io::Error,
+}
+
+impl error::Error for Error {
+    fn description(&self) -> &str {
+        self.error.description()
+    }
+}
+
+impl Display for Error {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        let file = self.file.display().to_string();
+        if file.is_empty() {
+            write!(f, "{}", self.error)
+        } else {
+            write!(f, "\"{}\": {}", self.file.display(), self.error)
+        }
+    }
+}
+
+impl PathError for Error {
+    fn new<P: AsRef<Path>>(e: io::Error, path: P) -> Error {
+        Error {
+            file: path.as_ref().to_path_buf(),
+            error: e,
+        }
+    }
+}
+
+macro_rules! try_none {
+    ($e:expr, $file:expr) => ({
+        use std::io;
+        match $e {
+            Some(e) => e,
+            None => return Err(Error::new(io::Error::new(io::ErrorKind::Other, "not found"),
+                                          $file))
+        }
+    })
+}
+
+macro_rules! try_err {
+    ($e:expr, $file:expr) => ({
+        match $e {
+            Ok(e) => e,
+            Err(e) => return Err(Error::new(e, $file)),
+        }
+    })
+}
+
 /// Major driving force in all rustdoc rendering. This contains information
 /// about where in the tree-like hierarchy rendering is occurring and controls
 /// how the current page is being rendered.
@@ -156,13 +209,15 @@
     pub generate_search_filter: bool,
     /// Option disabled by default to generate files used by RLS and some other tools.
     pub generate_redirect_pages: bool,
+    /// The fs handle we are working with.
+    pub fs: DocFS,
 }
 
 impl SharedContext {
-    fn ensure_dir(&self, dst: &Path) -> io::Result<()> {
+    fn ensure_dir(&self, dst: &Path) -> Result<(), Error> {
         let mut dirs = self.created_dirs.borrow_mut();
         if !dirs.contains(dst) {
-            fs::create_dir_all(dst)?;
+            try_err!(self.fs.create_dir_all(dst), dst);
             dirs.insert(dst.to_path_buf());
         }
 
@@ -216,53 +271,6 @@
     }
 }
 
-#[derive(Debug)]
-pub struct Error {
-    pub file: PathBuf,
-    pub error: io::Error,
-}
-
-impl error::Error for Error {
-    fn description(&self) -> &str {
-        self.error.description()
-    }
-}
-
-impl Display for Error {
-    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
-        write!(f, "\"{}\": {}", self.file.display(), self.error)
-    }
-}
-
-impl Error {
-    pub fn new(e: io::Error, file: &Path) -> Error {
-        Error {
-            file: file.to_path_buf(),
-            error: e,
-        }
-    }
-}
-
-macro_rules! try_none {
-    ($e:expr, $file:expr) => ({
-        use std::io;
-        match $e {
-            Some(e) => e,
-            None => return Err(Error::new(io::Error::new(io::ErrorKind::Other, "not found"),
-                                          $file))
-        }
-    })
-}
-
-macro_rules! try_err {
-    ($e:expr, $file:expr) => ({
-        match $e {
-            Ok(e) => e,
-            Err(e) => return Err(Error::new(e, $file)),
-        }
-    })
-}
-
 /// This cache is used to store information about the `clean::Crate` being
 /// rendered in order to provide more useful documentation. This contains
 /// information like all implementors of a trait, all traits a type implements,
@@ -544,6 +552,7 @@
         },
         _ => PathBuf::new(),
     };
+    let mut errors = Arc::new(ErrorStorage::new());
     let mut scx = SharedContext {
         src_root,
         passes,
@@ -564,6 +573,7 @@
         static_root_path,
         generate_search_filter,
         generate_redirect_pages,
+        fs: DocFS::new(&errors),
     };
 
     // If user passed in `--playground-url` arg, we fill in crate name here
@@ -601,9 +611,9 @@
         }
     }
     let dst = output;
-    try_err!(fs::create_dir_all(&dst), &dst);
+    scx.ensure_dir(&dst)?;
     krate = render_sources(&dst, &mut scx, krate)?;
-    let cx = Context {
+    let mut cx = Context {
         current: Vec::new(),
         dst,
         render_redirect_pages: false,
@@ -705,10 +715,21 @@
     CACHE_KEY.with(|v| *v.borrow_mut() = cache.clone());
     CURRENT_LOCATION_KEY.with(|s| s.borrow_mut().clear());
 
+    // Write shared runs within a flock; disable thread dispatching of IO temporarily.
+    Arc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(true);
     write_shared(&cx, &krate, &*cache, index, &md_opts, diag)?;
+    Arc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(false);
 
     // And finally render the whole crate's documentation
-    cx.krate(krate)
+    let ret = cx.krate(krate);
+    let nb_errors = Arc::get_mut(&mut errors).map_or_else(|| 0, |errors| errors.write_errors(diag));
+    if ret.is_err() {
+        ret
+    } else if nb_errors > 0 {
+        Err(Error::new(io::Error::new(io::ErrorKind::Other, "I/O error"), ""))
+    } else {
+        Ok(())
+    }
 }
 
 /// Builds the search index from the collected metadata
@@ -797,13 +818,13 @@
     // 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.
 
-    write_minify(cx.dst.join(&format!("rustdoc{}.css", cx.shared.resource_suffix)),
+    write_minify(&cx.shared.fs, cx.dst.join(&format!("rustdoc{}.css", cx.shared.resource_suffix)),
                  static_files::RUSTDOC_CSS,
                  options.enable_minification)?;
-    write_minify(cx.dst.join(&format!("settings{}.css", cx.shared.resource_suffix)),
+    write_minify(&cx.shared.fs, cx.dst.join(&format!("settings{}.css", cx.shared.resource_suffix)),
                  static_files::SETTINGS_CSS,
                  options.enable_minification)?;
-    write_minify(cx.dst.join(&format!("noscript{}.css", cx.shared.resource_suffix)),
+    write_minify(&cx.shared.fs, cx.dst.join(&format!("noscript{}.css", cx.shared.resource_suffix)),
                  static_files::NOSCRIPT_CSS,
                  options.enable_minification)?;
 
@@ -815,11 +836,13 @@
         let content = try_err!(fs::read(&entry), &entry);
         let theme = try_none!(try_none!(entry.file_stem(), &entry).to_str(), &entry);
         let extension = try_none!(try_none!(entry.extension(), &entry).to_str(), &entry);
-        write(cx.dst.join(format!("{}{}.{}", theme, cx.shared.resource_suffix, extension)),
-              content.as_slice())?;
+        cx.shared.fs.write(
+            cx.dst.join(format!("{}{}.{}", theme, cx.shared.resource_suffix, extension)),
+            content.as_slice())?;
         themes.insert(theme.to_owned());
     }
 
+    let write = |p, c| { cx.shared.fs.write(p, c) };
     if (*cx.shared).layout.logo.is_empty() {
         write(cx.dst.join(&format!("rust-logo{}.png", cx.shared.resource_suffix)),
               static_files::RUST_LOGO)?;
@@ -834,11 +857,11 @@
           static_files::WHEEL_SVG)?;
     write(cx.dst.join(&format!("down-arrow{}.svg", cx.shared.resource_suffix)),
           static_files::DOWN_ARROW_SVG)?;
-    write_minify(cx.dst.join(&format!("light{}.css", cx.shared.resource_suffix)),
+    write_minify(&cx.shared.fs, cx.dst.join(&format!("light{}.css", cx.shared.resource_suffix)),
                  static_files::themes::LIGHT,
                  options.enable_minification)?;
     themes.insert("light".to_owned());
-    write_minify(cx.dst.join(&format!("dark{}.css", cx.shared.resource_suffix)),
+    write_minify(&cx.shared.fs, cx.dst.join(&format!("dark{}.css", cx.shared.resource_suffix)),
                  static_files::themes::DARK,
                  options.enable_minification)?;
     themes.insert("dark".to_owned());
@@ -847,8 +870,7 @@
     themes.sort();
     // To avoid theme switch latencies as much as possible, we put everything theme related
     // at the beginning of the html files into another js file.
-    write(cx.dst.join(&format!("theme{}.js", cx.shared.resource_suffix)),
-          format!(
+    let theme_js = format!(
 r#"var themes = document.getElementById("theme-choices");
 var themePicker = document.getElementById("theme-picker");
 
@@ -891,39 +913,45 @@
                  themes.iter()
                        .map(|s| format!("\"{}\"", s))
                        .collect::<Vec<String>>()
-                       .join(",")).as_bytes(),
+                       .join(","));
+    write(cx.dst.join(&format!("theme{}.js", cx.shared.resource_suffix)),
+          theme_js.as_bytes()
     )?;
 
-    write_minify(cx.dst.join(&format!("main{}.js", cx.shared.resource_suffix)),
+    write_minify(&cx.shared.fs, cx.dst.join(&format!("main{}.js", cx.shared.resource_suffix)),
                  static_files::MAIN_JS,
                  options.enable_minification)?;
-    write_minify(cx.dst.join(&format!("settings{}.js", cx.shared.resource_suffix)),
+    write_minify(&cx.shared.fs, cx.dst.join(&format!("settings{}.js", cx.shared.resource_suffix)),
                  static_files::SETTINGS_JS,
                  options.enable_minification)?;
     if cx.shared.include_sources {
-        write_minify(cx.dst.join(&format!("source-script{}.js", cx.shared.resource_suffix)),
-                     static_files::sidebar::SOURCE_SCRIPT,
-                     options.enable_minification)?;
+        write_minify(
+            &cx.shared.fs,
+            cx.dst.join(&format!("source-script{}.js", cx.shared.resource_suffix)),
+            static_files::sidebar::SOURCE_SCRIPT,
+            options.enable_minification)?;
     }
 
     {
-        write_minify(cx.dst.join(&format!("storage{}.js", cx.shared.resource_suffix)),
-                     &format!("var resourcesSuffix = \"{}\";{}",
-                              cx.shared.resource_suffix,
-                              static_files::STORAGE_JS),
-                     options.enable_minification)?;
+        write_minify(
+            &cx.shared.fs,
+            cx.dst.join(&format!("storage{}.js", cx.shared.resource_suffix)),
+            &format!("var resourcesSuffix = \"{}\";{}",
+                     cx.shared.resource_suffix,
+                     static_files::STORAGE_JS),
+            options.enable_minification)?;
     }
 
     if let Some(ref css) = cx.shared.css_file_extension {
         let out = cx.dst.join(&format!("theme{}.css", cx.shared.resource_suffix));
+        let buffer = try_err!(fs::read_to_string(css), css);
         if !options.enable_minification {
-            try_err!(fs::copy(css, out), css);
+            cx.shared.fs.write(&out, &buffer)?;
         } else {
-            let buffer = try_err!(fs::read_to_string(css), css);
-            write_minify(out, &buffer, options.enable_minification)?;
+            write_minify(&cx.shared.fs, out, &buffer, options.enable_minification)?;
         }
     }
-    write_minify(cx.dst.join(&format!("normalize{}.css", cx.shared.resource_suffix)),
+    write_minify(&cx.shared.fs, cx.dst.join(&format!("normalize{}.css", cx.shared.resource_suffix)),
                  static_files::NORMALIZE_CSS,
                  options.enable_minification)?;
     write(cx.dst.join("FiraSans-Regular.woff"),
@@ -999,7 +1027,6 @@
     let dst = cx.dst.join(&format!("aliases{}.js", cx.shared.resource_suffix));
     {
         let (mut all_aliases, _, _) = try_err!(collect(&dst, &krate.name, "ALIASES", false), &dst);
-        let mut w = try_err!(File::create(&dst), &dst);
         let mut output = String::with_capacity(100);
         for (alias, items) in &cache.aliases {
             if items.is_empty() {
@@ -1014,10 +1041,12 @@
         }
         all_aliases.push(format!("ALIASES[\"{}\"] = {{{}}};", krate.name, output));
         all_aliases.sort();
-        try_err!(writeln!(&mut w, "var ALIASES = {{}};"), &dst);
+        let mut v = Vec::new();
+        try_err!(writeln!(&mut v, "var ALIASES = {{}};"), &dst);
         for aliases in &all_aliases {
-            try_err!(writeln!(&mut w, "{}", aliases), &dst);
+            try_err!(writeln!(&mut v, "{}", aliases), &dst);
         }
+        cx.shared.fs.write(&dst, &v)?;
     }
 
     use std::ffi::OsString;
@@ -1101,11 +1130,12 @@
                                  &krate.name,
                                  hierarchy.to_json_string()));
         all_sources.sort();
-        let mut w = try_err!(File::create(&dst), &dst);
-        try_err!(writeln!(&mut w,
+        let mut v = Vec::new();
+        try_err!(writeln!(&mut v,
                           "var N = null;var sourcesIndex = {{}};\n{}\ncreateSourceSidebar();",
                           all_sources.join("\n")),
                  &dst);
+        cx.shared.fs.write(&dst, &v)?;
     }
 
     // Update the search index
@@ -1119,14 +1149,17 @@
     // Sort the indexes by crate so the file will be generated identically even
     // with rustdoc running in parallel.
     all_indexes.sort();
-    let mut w = try_err!(File::create(&dst), &dst);
-    try_err!(writeln!(&mut w, "var N=null,E=\"\",T=\"t\",U=\"u\",searchIndex={{}};"), &dst);
-    try_err!(write_minify_replacer(&mut w,
-                                   &format!("{}\n{}", variables.join(""), all_indexes.join("\n")),
-                                   options.enable_minification),
-             &dst);
-    try_err!(write!(&mut w, "initSearch(searchIndex);addSearchOptions(searchIndex);"), &dst);
-
+    {
+        let mut v = Vec::new();
+        try_err!(writeln!(&mut v, "var N=null,E=\"\",T=\"t\",U=\"u\",searchIndex={{}};"), &dst);
+        try_err!(write_minify_replacer(
+            &mut v,
+            &format!("{}\n{}", variables.join(""), all_indexes.join("\n")),
+            options.enable_minification),
+            &dst);
+        try_err!(write!(&mut v, "initSearch(searchIndex);addSearchOptions(searchIndex);"), &dst);
+        cx.shared.fs.write(&dst, &v)?;
+    }
     if options.enable_index_page {
         if let Some(index_page) = options.index_page.clone() {
             let mut md_opts = options.clone();
@@ -1136,7 +1169,6 @@
             crate::markdown::render(index_page, md_opts, diag, cx.edition);
         } else {
             let dst = cx.dst.join("index.html");
-            let mut w = BufWriter::new(try_err!(File::create(&dst), &dst));
             let page = layout::Page {
                 title: "Index of crates",
                 css_class: "mod",
@@ -1163,12 +1195,13 @@
                                                 SlashChecker(s), s)
                                     })
                                     .collect::<String>());
-            try_err!(layout::render(&mut w, &cx.shared.layout,
+            let mut v = Vec::new();
+            try_err!(layout::render(&mut v, &cx.shared.layout,
                                     &page, &(""), &content,
                                     cx.shared.css_file_extension.is_some(),
                                     &cx.shared.themes,
                                     cx.shared.generate_search_filter), &dst);
-            try_err!(w.flush(), &dst);
+            cx.shared.fs.write(&dst, &v)?;
         }
     }
 
@@ -1220,7 +1253,7 @@
         for part in &remote_path[..remote_path.len() - 1] {
             mydst.push(part);
         }
-        try_err!(fs::create_dir_all(&mydst), &mydst);
+        cx.shared.ensure_dir(&mydst)?;
         mydst.push(&format!("{}.{}.js",
                             remote_item_type.css_class(),
                             remote_path[remote_path.len() - 1]));
@@ -1233,19 +1266,20 @@
         // identically even with rustdoc running in parallel.
         all_implementors.sort();
 
-        let mut f = try_err!(File::create(&mydst), &mydst);
-        try_err!(writeln!(&mut f, "(function() {{var implementors = {{}};"), &mydst);
+        let mut v = Vec::new();
+        try_err!(writeln!(&mut v, "(function() {{var implementors = {{}};"), &mydst);
         for implementor in &all_implementors {
-            try_err!(writeln!(&mut f, "{}", *implementor), &mydst);
+            try_err!(writeln!(&mut v, "{}", *implementor), &mydst);
         }
-        try_err!(writeln!(&mut f, "{}", r"
+        try_err!(writeln!(&mut v, "{}", r"
             if (window.register_implementors) {
                 window.register_implementors(implementors);
             } else {
                 window.pending_implementors = implementors;
             }
         "), &mydst);
-        try_err!(writeln!(&mut f, r"}})()"), &mydst);
+        try_err!(writeln!(&mut v, r"}})()"), &mydst);
+        cx.shared.fs.write(&mydst, &v)?;
     }
     Ok(())
 }
@@ -1254,7 +1288,7 @@
                   krate: clean::Crate) -> Result<clean::Crate, Error> {
     info!("emitting source files");
     let dst = dst.join("src").join(&krate.name);
-    try_err!(fs::create_dir_all(&dst), &dst);
+    scx.ensure_dir(&dst)?;
     let mut folder = SourceCollector {
         dst,
         scx,
@@ -1262,22 +1296,17 @@
     Ok(folder.fold_crate(krate))
 }
 
-/// Writes the entire contents of a string to a destination, not attempting to
-/// catch any errors.
-fn write(dst: PathBuf, contents: &[u8]) -> Result<(), Error> {
-    Ok(try_err!(fs::write(&dst, contents), &dst))
-}
-
-fn write_minify(dst: PathBuf, contents: &str, enable_minification: bool) -> Result<(), Error> {
+fn write_minify(fs:&DocFS, dst: PathBuf, contents: &str, enable_minification: bool
+                ) -> Result<(), Error> {
     if enable_minification {
         if dst.extension() == Some(&OsStr::new("css")) {
             let res = try_none!(minifier::css::minify(contents).ok(), &dst);
-            write(dst, res.as_bytes())
+            fs.write(dst, res.as_bytes())
         } else {
-            write(dst, minifier::js::minify(contents).as_bytes())
+            fs.write(dst, minifier::js::minify(contents).as_bytes())
         }
     } else {
-        write(dst, contents.as_bytes())
+        fs.write(dst, contents.as_bytes())
     }
 }
 
@@ -1439,7 +1468,7 @@
 
 impl<'a> SourceCollector<'a> {
     /// Renders the given filename into its corresponding HTML source file.
-    fn emit_source(&mut self, filename: &FileName) -> io::Result<()> {
+    fn emit_source(&mut self, filename: &FileName) -> Result<(), Error> {
         let p = match *filename {
             FileName::Real(ref file) => file,
             _ => return Ok(()),
@@ -1449,7 +1478,7 @@
             return Ok(());
         }
 
-        let contents = fs::read_to_string(&p)?;
+        let contents = try_err!(fs::read_to_string(&p), &p);
 
         // Remove the utf-8 BOM if any
         let contents = if contents.starts_with("\u{feff}") {
@@ -1468,7 +1497,7 @@
             href.push_str(&component.to_string_lossy());
             href.push('/');
         });
-        fs::create_dir_all(&cur)?;
+        self.scx.ensure_dir(&cur)?;
         let mut fname = p.file_name()
                          .expect("source has no filename")
                          .to_os_string();
@@ -1476,7 +1505,7 @@
         cur.push(&fname);
         href.push_str(&fname.to_string_lossy());
 
-        let mut w = BufWriter::new(File::create(&cur)?);
+        let mut v = Vec::new();
         let title = format!("{} -- source", cur.file_name().expect("failed to get file name")
                                                .to_string_lossy());
         let desc = format!("Source to the Rust file `{}`.", filename);
@@ -1491,12 +1520,12 @@
             extra_scripts: &[&format!("source-files{}", self.scx.resource_suffix)],
             static_extra_scripts: &[&format!("source-script{}", self.scx.resource_suffix)],
         };
-        layout::render(&mut w, &self.scx.layout,
+        try_err!(layout::render(&mut v, &self.scx.layout,
                        &page, &(""), &Source(contents),
                        self.scx.css_file_extension.is_some(),
                        &self.scx.themes,
-                       self.scx.generate_search_filter)?;
-        w.flush()?;
+                       self.scx.generate_search_filter), &cur);
+        self.scx.fs.write(&cur, &v)?;
         self.scx.local_sources.insert(p.clone(), href);
         Ok(())
     }
@@ -2073,7 +2102,6 @@
             }
         }
 
-        let mut w = BufWriter::new(try_err!(File::create(&final_file), &final_file));
         let mut root_path = self.dst.to_str().expect("invalid path").to_owned();
         if !root_path.ends_with('/') {
             root_path.push('/');
@@ -2099,12 +2127,14 @@
         } else {
             String::new()
         };
-        try_err!(layout::render(&mut w, &self.shared.layout,
+        let mut v = Vec::new();
+        try_err!(layout::render(&mut v, &self.shared.layout,
                                 &page, &sidebar, &all,
                                 self.shared.css_file_extension.is_some(),
                                 &self.shared.themes,
                                 self.shared.generate_search_filter),
                  &final_file);
+        self.shared.fs.write(&final_file, &v)?;
 
         // Generating settings page.
         let settings = Settings::new(self.shared.static_root_path.deref().unwrap_or("./"),
@@ -2113,17 +2143,18 @@
         page.description = "Settings of Rustdoc";
         page.root_path = "./";
 
-        let mut w = BufWriter::new(try_err!(File::create(&settings_file), &settings_file));
         let mut themes = self.shared.themes.clone();
         let sidebar = "<p class='location'>Settings</p><div class='sidebar-elems'></div>";
         themes.push(PathBuf::from("settings.css"));
         let layout = self.shared.layout.clone();
-        try_err!(layout::render(&mut w, &layout,
+        let mut v = Vec::new();
+        try_err!(layout::render(&mut v, &layout,
                                 &page, &sidebar, &settings,
                                 self.shared.css_file_extension.is_some(),
                                 &themes,
                                 self.shared.generate_search_filter),
                  &settings_file);
+        self.shared.fs.write(&settings_file, &v)?;
 
         Ok(())
     }
@@ -2223,6 +2254,7 @@
             // recurse into the items of the module as well.
             let name = item.name.as_ref().unwrap().to_string();
             let mut item = Some(item);
+            let scx = self.shared.clone();
             self.recurse(name, |this| {
                 let item = item.take().unwrap();
 
@@ -2230,9 +2262,9 @@
                 this.render_item(&mut buf, &item, false).unwrap();
                 // buf will be empty if the module is stripped and there is no redirect for it
                 if !buf.is_empty() {
-                    try_err!(this.shared.ensure_dir(&this.dst), &this.dst);
+                    this.shared.ensure_dir(&this.dst)?;
                     let joint_dst = this.dst.join("index.html");
-                    try_err!(fs::write(&joint_dst, buf), &joint_dst);
+                    scx.fs.write(&joint_dst, buf)?;
                 }
 
                 let m = match item.inner {
@@ -2245,9 +2277,10 @@
                 if !this.render_redirect_pages {
                     let items = this.build_sidebar_items(&m);
                     let js_dst = this.dst.join("sidebar-items.js");
-                    let mut js_out = BufWriter::new(try_err!(File::create(&js_dst), &js_dst));
-                    try_err!(write!(&mut js_out, "initSidebarItems({});",
+                    let mut v = Vec::new();
+                    try_err!(write!(&mut v, "initSidebarItems({});",
                                     as_json(&items)), &js_dst);
+                    scx.fs.write(&js_dst, &v)?;
                 }
 
                 for item in m.items {
@@ -2264,9 +2297,9 @@
                 let name = item.name.as_ref().unwrap();
                 let item_type = item.type_();
                 let file_name = &item_path(item_type, name);
-                try_err!(self.shared.ensure_dir(&self.dst), &self.dst);
+                self.shared.ensure_dir(&self.dst)?;
                 let joint_dst = self.dst.join(file_name);
-                try_err!(fs::write(&joint_dst, buf), &joint_dst);
+                self.shared.fs.write(&joint_dst, buf)?;
 
                 if !self.render_redirect_pages {
                     all.append(full_path(self, &item), &item_type);
@@ -2276,21 +2309,18 @@
                     // URL for the page.
                     let redir_name = format!("{}.{}.html", name, item_type.name_space());
                     let redir_dst = self.dst.join(redir_name);
-                    if let Ok(redirect_out) = OpenOptions::new().create_new(true)
-                                                                .write(true)
-                                                                .open(&redir_dst) {
-                        let mut redirect_out = BufWriter::new(redirect_out);
-                        try_err!(layout::redirect(&mut redirect_out, file_name), &redir_dst);
-                    }
+                    let mut v = Vec::new();
+                    try_err!(layout::redirect(&mut v, file_name), &redir_dst);
+                    self.shared.fs.write(&redir_dst, &v)?;
                 }
                 // If the item is a macro, redirect from the old macro URL (with !)
                 // to the new one (without).
                 if item_type == ItemType::Macro {
                     let redir_name = format!("{}.{}!.html", item_type, name);
                     let redir_dst = self.dst.join(redir_name);
-                    let redirect_out = try_err!(File::create(&redir_dst), &redir_dst);
-                    let mut redirect_out = BufWriter::new(redirect_out);
-                    try_err!(layout::redirect(&mut redirect_out, file_name), &redir_dst);
+                    let mut v = Vec::new();
+                    try_err!(layout::redirect(&mut v, file_name), &redir_dst);
+                    self.shared.fs.write(&redir_dst, &v)?;
                 }
             }
         }
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index 3b4d653..7a8b088 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -59,6 +59,7 @@
 mod clean;
 mod config;
 mod core;
+mod docfs;
 mod doctree;
 mod fold;
 pub mod html {
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index 68b96a4..e6f0992 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -246,7 +246,7 @@
                     match parent_node.or(self.mod_ids.last().cloned()) {
                         Some(parent) if parent != hir::CRATE_HIR_ID => {
                             // FIXME: can we pull the parent module's name from elsewhere?
-                            Some(self.cx.tcx.hir().name_by_hir_id(parent).to_string())
+                            Some(self.cx.tcx.hir().name(parent).to_string())
                         }
                         _ => None,
                     }
diff --git a/src/librustdoc/theme.rs b/src/librustdoc/theme.rs
index 7a0ccf6..7220a05 100644
--- a/src/librustdoc/theme.rs
+++ b/src/librustdoc/theme.rs
@@ -103,16 +103,16 @@
     if let Some(&Events::StartComment(_)) = events.last() {
         return false;
     }
-    pos + 1 < v.len() && v[pos + 1] == b'/'
+    v[pos + 1] == b'/'
 }
 
 fn load_css_events(v: &[u8]) -> Vec<Events> {
     let mut pos = 0;
     let mut events = Vec::with_capacity(100);
 
-    while pos < v.len() - 1 {
+    while pos + 1 < v.len() {
         match v[pos] {
-            b'/' if pos + 1 < v.len() && v[pos + 1] == b'*' => {
+            b'/' if v[pos + 1] == b'*' => {
                 events.push(Events::StartComment(pos));
                 pos += 1;
             }
@@ -123,7 +123,7 @@
             b'\n' if previous_is_line_comment(&events) => {
                 events.push(Events::EndComment(pos));
             }
-            b'*' if pos + 1 < v.len() && v[pos + 1] == b'/' => {
+            b'*' if v[pos + 1] == b'/' => {
                 events.push(Events::EndComment(pos + 2));
                 pos += 1;
             }
@@ -264,9 +264,11 @@
     }
 }
 
-pub fn test_theme_against<P: AsRef<Path>>(f: &P, against: &CssPath, diag: &Handler)
-    -> (bool, Vec<String>)
-{
+pub fn test_theme_against<P: AsRef<Path>>(
+    f: &P,
+    against: &CssPath,
+    diag: &Handler,
+) -> (bool, Vec<String>) {
     let data = try_something!(fs::read(f), diag, (false, vec![]));
     let paths = load_css_paths(&data);
     let mut ret = vec![];
@@ -366,4 +368,16 @@
         get_differences(&other, &against, &mut ret);
         assert_eq!(ret, vec!["  Missing \"c\" rule".to_owned()]);
     }
+
+    #[test]
+    fn check_empty_css() {
+        let events = load_css_events(&[]);
+        assert_eq!(events.len(), 0);
+    }
+
+    #[test]
+    fn check_invalid_css() {
+        let events = load_css_events(b"*");
+        assert_eq!(events.len(), 0);
+    }
 }
diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs
index c94149d..781e62c 100644
--- a/src/librustdoc/visit_ast.rs
+++ b/src/librustdoc/visit_ast.rs
@@ -337,7 +337,7 @@
 
         if !self.view_item_stack.insert(res_hir_id) { return false }
 
-        let ret = match tcx.hir().get_by_hir_id(res_hir_id) {
+        let ret = match tcx.hir().get(res_hir_id) {
             Node::Item(&hir::Item { node: hir::ItemKind::Mod(ref m), .. }) if glob => {
                 let prev = mem::replace(&mut self.inlining, true);
                 for i in &m.item_ids {
diff --git a/src/libserialize/serialize.rs b/src/libserialize/serialize.rs
index 95095c7..2def2a4 100644
--- a/src/libserialize/serialize.rs
+++ b/src/libserialize/serialize.rs
@@ -739,33 +739,33 @@
 macro_rules! tuple {
     () => ();
     ( $($name:ident,)+ ) => (
-        impl<$($name:Decodable),*> Decodable for ($($name,)*) {
+        impl<$($name:Decodable),+> Decodable for ($($name,)+) {
             #[allow(non_snake_case)]
-            fn decode<D: Decoder>(d: &mut D) -> Result<($($name,)*), D::Error> {
-                let len: usize = count!($($name)*);
+            fn decode<D: Decoder>(d: &mut D) -> Result<($($name,)+), D::Error> {
+                let len: usize = count!($($name)+);
                 d.read_tuple(len, |d| {
                     let mut i = 0;
                     let ret = ($(d.read_tuple_arg({ i+=1; i-1 }, |d| -> Result<$name, D::Error> {
                         Decodable::decode(d)
-                    })?,)*);
+                    })?,)+);
                     Ok(ret)
                 })
             }
         }
-        impl<$($name:Encodable),*> Encodable for ($($name,)*) {
+        impl<$($name:Encodable),+> Encodable for ($($name,)+) {
             #[allow(non_snake_case)]
             fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
-                let ($(ref $name,)*) = *self;
+                let ($(ref $name,)+) = *self;
                 let mut n = 0;
-                $(let $name = $name; n += 1;)*
+                $(let $name = $name; n += 1;)+
                 s.emit_tuple(n, |s| {
                     let mut i = 0;
-                    $(s.emit_tuple_arg({ i+=1; i-1 }, |s| $name.encode(s))?;)*
+                    $(s.emit_tuple_arg({ i+=1; i-1 }, |s| $name.encode(s))?;)+
                     Ok(())
                 })
             }
         }
-        peel! { $($name,)* }
+        peel! { $($name,)+ }
     )
 }
 
diff --git a/src/libstd/primitive_docs.rs b/src/libstd/primitive_docs.rs
index e78a5de..65fd8c8 100644
--- a/src/libstd/primitive_docs.rs
+++ b/src/libstd/primitive_docs.rs
@@ -120,7 +120,7 @@
 /// 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
+/// 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:
 ///
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index b1a62ac..f2fac16 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -893,17 +893,12 @@
 pub struct Arm {
     pub attrs: Vec<Attribute>,
     pub pats: Vec<P<Pat>>,
-    pub guard: Option<Guard>,
+    pub guard: Option<P<Expr>>,
     pub body: P<Expr>,
     pub span: Span,
 }
 
 #[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
-pub enum Guard {
-    If(P<Expr>),
-}
-
-#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
 pub struct Field {
     pub ident: Ident,
     pub expr: P<Expr>,
@@ -1032,10 +1027,9 @@
             ExprKind::Unary(..) => ExprPrecedence::Unary,
             ExprKind::Lit(_) => ExprPrecedence::Lit,
             ExprKind::Type(..) | ExprKind::Cast(..) => ExprPrecedence::Cast,
+            ExprKind::Let(..) => ExprPrecedence::Let,
             ExprKind::If(..) => ExprPrecedence::If,
-            ExprKind::IfLet(..) => ExprPrecedence::IfLet,
             ExprKind::While(..) => ExprPrecedence::While,
-            ExprKind::WhileLet(..) => ExprPrecedence::WhileLet,
             ExprKind::ForLoop(..) => ExprPrecedence::ForLoop,
             ExprKind::Loop(..) => ExprPrecedence::Loop,
             ExprKind::Match(..) => ExprPrecedence::Match,
@@ -1116,26 +1110,20 @@
     Cast(P<Expr>, P<Ty>),
     /// A type ascription (e.g., `42: usize`).
     Type(P<Expr>, P<Ty>),
+    /// A `let pats = expr` expression that is only semantically allowed in the condition
+    /// of `if` / `while` expressions. (e.g., `if let 0 = x { .. }`).
+    ///
+    /// The `Vec<P<Pat>>` is for or-patterns at the top level.
+    /// FIXME(54883): Change this to just `P<Pat>`.
+    Let(Vec<P<Pat>>, P<Expr>),
     /// An `if` block, with an optional `else` block.
     ///
     /// `if expr { block } else { expr }`
     If(P<Expr>, P<Block>, Option<P<Expr>>),
-    /// An `if let` expression with an optional else block
-    ///
-    /// `if let pat = expr { block } else { expr }`
-    ///
-    /// This is desugared to a `match` expression.
-    IfLet(Vec<P<Pat>>, P<Expr>, P<Block>, Option<P<Expr>>),
-    /// A while loop, with an optional label
+    /// A while loop, with an optional label.
     ///
     /// `'label: while expr { block }`
     While(P<Expr>, P<Block>, Option<Label>),
-    /// A `while let` loop, with an optional label.
-    ///
-    /// `'label: while let pat = expr { block }`
-    ///
-    /// This is desugared to a combination of `loop` and `match` expressions.
-    WhileLet(Vec<P<Pat>>, P<Expr>, P<Block>, Option<Label>),
     /// A `for` loop, with an optional label.
     ///
     /// `'label: for pat in expr { block }`
diff --git a/src/libsyntax/config.rs b/src/libsyntax/config.rs
index 3b42e1d..1ab367f 100644
--- a/src/libsyntax/config.rs
+++ b/src/libsyntax/config.rs
@@ -91,10 +91,10 @@
     /// is in the original source file. Gives a compiler error if the syntax of
     /// the attribute is incorrect.
     fn process_cfg_attr(&mut self, attr: ast::Attribute) -> Vec<ast::Attribute> {
-        if !attr.check_name(sym::cfg_attr) {
+        if attr.path != sym::cfg_attr {
             return vec![attr];
         }
-        if attr.tokens.len() == 0 {
+        if attr.tokens.is_empty() {
             self.sess.span_diagnostic
                 .struct_span_err(
                     attr.span,
@@ -108,7 +108,7 @@
                        <https://doc.rust-lang.org/reference/conditional-compilation.html\
                        #the-cfg_attr-attribute>")
                 .emit();
-            return Vec::new();
+            return vec![];
         }
 
         let (cfg_predicate, expanded_attrs) = match attr.parse(self.sess, |parser| {
@@ -133,17 +133,18 @@
             Ok(result) => result,
             Err(mut e) => {
                 e.emit();
-                return Vec::new();
+                return vec![];
             }
         };
 
-        // Check feature gate and lint on zero attributes in source. Even if the feature is gated,
-        // we still compute as if it wasn't, since the emitted error will stop compilation further
-        // along the compilation.
-        if expanded_attrs.len() == 0 {
-            // FIXME: Emit unused attribute lint here.
+        // Lint on zero attributes in source.
+        if expanded_attrs.is_empty() {
+            return vec![attr];
         }
 
+        // At this point we know the attribute is considered used.
+        attr::mark_used(&attr);
+
         if attr::cfg_matches(&cfg_predicate, self.sess, self.features) {
             // We call `process_cfg_attr` recursively in case there's a
             // `cfg_attr` inside of another `cfg_attr`. E.g.
@@ -159,7 +160,7 @@
             }))
             .collect()
         } else {
-            Vec::new()
+            vec![]
         }
     }
 
diff --git a/src/libsyntax/ext/derive.rs b/src/libsyntax/ext/derive.rs
index abc451c..3b4243e 100644
--- a/src/libsyntax/ext/derive.rs
+++ b/src/libsyntax/ext/derive.rs
@@ -30,10 +30,6 @@
 
         match attr.parse_list(cx.parse_sess,
                               |parser| parser.parse_path_allowing_meta(PathStyle::Mod)) {
-            Ok(ref traits) if traits.is_empty() => {
-                cx.span_warn(attr.span, "empty trait list in `derive`");
-                false
-            }
             Ok(traits) => {
                 result.extend(traits);
                 true
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index cfd6757..945cf36 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -98,9 +98,9 @@
                             }
                         });
                     }
-                    $($(AstFragment::$Kind(ast) => vis.$mut_visit_ast(ast),)*)*
+                    $($(AstFragment::$Kind(ast) => vis.$mut_visit_ast(ast),)?)*
                     $($(AstFragment::$Kind(ast) =>
-                        ast.flat_map_in_place(|ast| vis.$flat_map_ast_elt(ast)),)*)*
+                        ast.flat_map_in_place(|ast| vis.$flat_map_ast_elt(ast)),)?)*
                 }
             }
 
@@ -108,10 +108,10 @@
                 match *self {
                     AstFragment::OptExpr(Some(ref expr)) => visitor.visit_expr(expr),
                     AstFragment::OptExpr(None) => {}
-                    $($(AstFragment::$Kind(ref ast) => visitor.$visit_ast(ast),)*)*
+                    $($(AstFragment::$Kind(ref ast) => visitor.$visit_ast(ast),)?)*
                     $($(AstFragment::$Kind(ref ast) => for ast_elt in &ast[..] {
                         visitor.$visit_ast_elt(ast_elt);
-                    })*)*
+                    })?)*
                 }
             }
         }
@@ -122,10 +122,10 @@
             }
             $($(fn $mut_visit_ast(&mut self, ast: &mut $AstTy) {
                 visit_clobber(ast, |ast| self.expand_fragment(AstFragment::$Kind(ast)).$make_ast());
-            })*)*
+            })?)*
             $($(fn $flat_map_ast_elt(&mut self, ast_elt: <$AstTy as IntoIterator>::Item) -> $AstTy {
                 self.expand_fragment(AstFragment::$Kind(smallvec![ast_elt])).$make_ast()
-            })*)*
+            })?)*
         }
 
         impl<'a> MacResult for crate::ext::tt::macro_rules::ParserAnyMacro<'a> {
diff --git a/src/libsyntax/ext/tt/macro_parser.rs b/src/libsyntax/ext/tt/macro_parser.rs
index d5da4c9..92ce377 100644
--- a/src/libsyntax/ext/tt/macro_parser.rs
+++ b/src/libsyntax/ext/tt/macro_parser.rs
@@ -825,7 +825,9 @@
     }
 
     match name {
-        sym::expr => token.can_begin_expr(),
+        sym::expr => token.can_begin_expr()
+            // This exception is here for backwards compatibility.
+            && !token.is_keyword(kw::Let),
         sym::ty => token.can_begin_type(),
         sym::ident => get_macro_name(token).is_some(),
         sym::literal => token.can_begin_literal_or_bool(),
diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs
index 7f051c2..1a448cb 100644
--- a/src/libsyntax/ext/tt/macro_rules.rs
+++ b/src/libsyntax/ext/tt/macro_rules.rs
@@ -1,37 +1,37 @@
-use crate::{ast, attr};
 use crate::edition::Edition;
-use crate::ext::base::{SyntaxExtension, SyntaxExtensionKind};
 use crate::ext::base::{DummyResult, ExtCtxt, MacResult, TTMacroExpander};
+use crate::ext::base::{SyntaxExtension, SyntaxExtensionKind};
 use crate::ext::expand::{AstFragment, AstFragmentKind};
 use crate::ext::hygiene::Transparency;
-use crate::ext::tt::macro_parser::{Success, Error, Failure};
-use crate::ext::tt::macro_parser::{MatchedSeq, MatchedNonterminal};
 use crate::ext::tt::macro_parser::{parse, parse_failure_msg};
+use crate::ext::tt::macro_parser::{Error, Failure, Success};
+use crate::ext::tt::macro_parser::{MatchedNonterminal, MatchedSeq};
 use crate::ext::tt::quoted;
 use crate::ext::tt::transcribe::transcribe;
 use crate::feature_gate::Features;
-use crate::parse::{Directory, ParseSess};
 use crate::parse::parser::Parser;
-use crate::parse::token::{self, Token, NtTT};
 use crate::parse::token::TokenKind::*;
-use crate::symbol::{Symbol, kw, sym};
+use crate::parse::token::{self, NtTT, Token};
+use crate::parse::{Directory, ParseSess};
+use crate::symbol::{kw, sym, Symbol};
 use crate::tokenstream::{DelimSpan, TokenStream, TokenTree};
+use crate::{ast, attr};
 
 use errors::FatalError;
-use syntax_pos::{Span, symbol::Ident};
 use log::debug;
+use syntax_pos::{symbol::Ident, Span};
 
-use rustc_data_structures::fx::{FxHashMap};
+use rustc_data_structures::fx::FxHashMap;
 use std::borrow::Cow;
 use std::collections::hash_map::Entry;
 use std::slice;
 
-use rustc_data_structures::sync::Lrc;
 use errors::Applicability;
+use rustc_data_structures::sync::Lrc;
 
 const VALID_FRAGMENT_NAMES_MSG: &str = "valid fragment specifiers are \
-    `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, \
-    `path`, `meta`, `tt`, `item` and `vis`";
+                                        `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, \
+                                        `literal`, `path`, `meta`, `tt`, `item` and `vis`";
 
 pub struct ParserAnyMacro<'a> {
     parser: Parser<'a>,
@@ -48,7 +48,8 @@
         let ParserAnyMacro { site_span, macro_ident, ref mut parser, arm_span } = *self;
         let fragment = panictry!(parser.parse_ast_fragment(kind, true).map_err(|mut e| {
             if parser.token == token::Eof && e.message().ends_with(", found `<eof>`") {
-                if !e.span.is_dummy() {  // early end of macro arm (#52866)
+                if !e.span.is_dummy() {
+                    // early end of macro arm (#52866)
                     e.replace_span_with(parser.sess.source_map().next_point(parser.token.span));
                 }
                 let msg = &e.message[0];
@@ -60,7 +61,8 @@
                     msg.1,
                 );
             }
-            if e.span.is_dummy() {  // Get around lack of span in error (#30128)
+            if e.span.is_dummy() {
+                // Get around lack of span in error (#30128)
                 e.replace_span_with(site_span);
                 if parser.sess.source_map().span_to_filename(arm_span).is_real() {
                     e.span_label(arm_span, "in this macro arm");
@@ -99,17 +101,11 @@
         sp: Span,
         input: TokenStream,
         def_span: Option<Span>,
-    ) -> Box<dyn MacResult+'cx> {
+    ) -> Box<dyn MacResult + 'cx> {
         if !self.valid {
             return DummyResult::any(sp);
         }
-        generic_extension(cx,
-                          sp,
-                          def_span,
-                          self.name,
-                          input,
-                          &self.lhses,
-                          &self.rhses)
+        generic_extension(cx, sp, def_span, self.name, input, &self.lhses, &self.rhses)
     }
 }
 
@@ -119,14 +115,15 @@
 }
 
 /// Given `lhses` and `rhses`, this is the new macro we create
-fn generic_extension<'cx>(cx: &'cx mut ExtCtxt<'_>,
-                          sp: Span,
-                          def_span: Option<Span>,
-                          name: ast::Ident,
-                          arg: TokenStream,
-                          lhses: &[quoted::TokenTree],
-                          rhses: &[quoted::TokenTree])
-                          -> Box<dyn MacResult+'cx> {
+fn generic_extension<'cx>(
+    cx: &'cx mut ExtCtxt<'_>,
+    sp: Span,
+    def_span: Option<Span>,
+    name: ast::Ident,
+    arg: TokenStream,
+    lhses: &[quoted::TokenTree],
+    rhses: &[quoted::TokenTree],
+) -> Box<dyn MacResult + 'cx> {
     if cx.trace_macros() {
         trace_macros_note(cx, sp, format!("expanding `{}! {{ {} }}`", name, arg));
     }
@@ -134,10 +131,11 @@
     // Which arm's failure should we report? (the one furthest along)
     let mut best_failure: Option<(Token, &str)> = None;
 
-    for (i, lhs) in lhses.iter().enumerate() { // try each arm's matchers
+    for (i, lhs) in lhses.iter().enumerate() {
+        // try each arm's matchers
         let lhs_tt = match *lhs {
             quoted::TokenTree::Delimited(_, ref delim) => &delim.tts[..],
-            _ => cx.span_bug(sp, "malformed macro lhs")
+            _ => cx.span_bug(sp, "malformed macro lhs"),
         };
 
         match TokenTree::parse(cx, lhs_tt, arg.clone()) {
@@ -173,8 +171,8 @@
                     ownership: cx.current_expansion.directory_ownership,
                 };
                 let mut p = Parser::new(cx.parse_sess(), tts, Some(directory), true, false, None);
-                p.root_module_name = cx.current_expansion.module.mod_path.last()
-                    .map(|id| id.as_str().to_string());
+                p.root_module_name =
+                    cx.current_expansion.module.mod_path.last().map(|id| id.as_str().to_string());
 
                 p.process_potential_macro_variable();
                 // Let the context choose how to interpret the result.
@@ -188,15 +186,13 @@
                     site_span: sp,
                     macro_ident: name,
                     arm_span,
-                })
+                });
             }
             Failure(token, msg) => match best_failure {
                 Some((ref best_token, _)) if best_token.span.lo() >= token.span.lo() => {}
-                _ => best_failure = Some((token, msg))
-            }
-            Error(err_sp, ref msg) => {
-                cx.span_fatal(err_sp.substitute_dummy(sp), &msg[..])
-            }
+                _ => best_failure = Some((token, msg)),
+            },
+            Error(err_sp, ref msg) => cx.span_fatal(err_sp.substitute_dummy(sp), &msg[..]),
         }
     }
 
@@ -212,7 +208,8 @@
 
     // Check whether there's a missing comma in this macro call, like `println!("{}" a);`
     if let Some((arg, comma_span)) = arg.add_comma() {
-        for lhs in lhses { // try each arm's matchers
+        for lhs in lhses {
+            // try each arm's matchers
             let lhs_tt = match *lhs {
                 quoted::TokenTree::Delimited(_, ref delim) => &delim.tts[..],
                 _ => continue,
@@ -249,7 +246,7 @@
     sess: &ParseSess,
     features: &Features,
     def: &ast::Item,
-    edition: Edition
+    edition: Edition,
 ) -> SyntaxExtension {
     let lhs_nm = ast::Ident::new(sym::lhs, def.span);
     let rhs_nm = ast::Ident::new(sym::rhs, def.span);
@@ -267,25 +264,32 @@
     // ...quasiquoting this would be nice.
     // These spans won't matter, anyways
     let argument_gram = vec![
-        quoted::TokenTree::Sequence(DelimSpan::dummy(), Lrc::new(quoted::SequenceRepetition {
-            tts: vec![
-                quoted::TokenTree::MetaVarDecl(def.span, lhs_nm, tt_spec),
-                quoted::TokenTree::token(token::FatArrow, def.span),
-                quoted::TokenTree::MetaVarDecl(def.span, rhs_nm, tt_spec),
-            ],
-            separator: Some(Token::new(
-                if body.legacy { token::Semi } else { token::Comma }, def.span
-            )),
-            op: quoted::KleeneOp::OneOrMore,
-            num_captures: 2,
-        })),
+        quoted::TokenTree::Sequence(
+            DelimSpan::dummy(),
+            Lrc::new(quoted::SequenceRepetition {
+                tts: vec![
+                    quoted::TokenTree::MetaVarDecl(def.span, lhs_nm, tt_spec),
+                    quoted::TokenTree::token(token::FatArrow, def.span),
+                    quoted::TokenTree::MetaVarDecl(def.span, rhs_nm, tt_spec),
+                ],
+                separator: Some(Token::new(
+                    if body.legacy { token::Semi } else { token::Comma },
+                    def.span,
+                )),
+                op: quoted::KleeneOp::OneOrMore,
+                num_captures: 2,
+            }),
+        ),
         // to phase into semicolon-termination instead of semicolon-separation
-        quoted::TokenTree::Sequence(DelimSpan::dummy(), Lrc::new(quoted::SequenceRepetition {
-            tts: vec![quoted::TokenTree::token(token::Semi, def.span)],
-            separator: None,
-            op: quoted::KleeneOp::ZeroOrMore,
-            num_captures: 0
-        })),
+        quoted::TokenTree::Sequence(
+            DelimSpan::dummy(),
+            Lrc::new(quoted::SequenceRepetition {
+                tts: vec![quoted::TokenTree::token(token::Semi, def.span)],
+                separator: None,
+                op: quoted::KleeneOp::ZeroOrMore,
+                num_captures: 0,
+            }),
+        ),
     ];
 
     let argument_map = match parse(sess, body.stream(), &argument_gram, None, true) {
@@ -307,8 +311,9 @@
 
     // Extract the arguments:
     let lhses = match *argument_map[&lhs_nm] {
-        MatchedSeq(ref s, _) => {
-            s.iter().map(|m| {
+        MatchedSeq(ref s, _) => s
+            .iter()
+            .map(|m| {
                 if let MatchedNonterminal(ref nt) = *m {
                     if let NtTT(ref tt) = **nt {
                         let tt = quoted::parse(
@@ -327,14 +332,15 @@
                     }
                 }
                 sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs")
-            }).collect::<Vec<quoted::TokenTree>>()
-        }
-        _ => sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs")
+            })
+            .collect::<Vec<quoted::TokenTree>>(),
+        _ => sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs"),
     };
 
     let rhses = match *argument_map[&rhs_nm] {
-        MatchedSeq(ref s, _) => {
-            s.iter().map(|m| {
+        MatchedSeq(ref s, _) => s
+            .iter()
+            .map(|m| {
                 if let MatchedNonterminal(ref nt) = *m {
                     if let NtTT(ref tt) = **nt {
                         return quoted::parse(
@@ -345,14 +351,15 @@
                             &def.attrs,
                             edition,
                             def.id,
-                        ).pop()
-                         .unwrap();
+                        )
+                        .pop()
+                        .unwrap();
                     }
                 }
                 sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs")
-            }).collect::<Vec<quoted::TokenTree>>()
-        }
-        _ => sess.span_diagnostic.span_bug(def.span, "wrong-structured rhs")
+            })
+            .collect::<Vec<quoted::TokenTree>>(),
+        _ => sess.span_diagnostic.span_bug(def.span, "wrong-structured rhs"),
     };
 
     for rhs in &rhses {
@@ -366,16 +373,12 @@
             sess,
             slice::from_ref(lhs),
             &mut FxHashMap::default(),
-            def.id
+            def.id,
         );
     }
 
-    let expander: Box<_> = Box::new(MacroRulesMacroExpander {
-        name: def.ident,
-        lhses,
-        rhses,
-        valid,
-    });
+    let expander: Box<_> =
+        Box::new(MacroRulesMacroExpander { name: def.ident, lhses, rhses, valid });
 
     let default_transparency = if attr::contains_name(&def.attrs, sym::rustc_transparent_macro) {
         Transparency::Transparent
@@ -385,29 +388,34 @@
         Transparency::Opaque
     };
 
-    let allow_internal_unstable = attr::find_by_name(&def.attrs, sym::allow_internal_unstable)
-        .map(|attr| attr
-            .meta_item_list()
-            .map(|list| list.iter()
-                .filter_map(|it| {
-                    let name = it.ident().map(|ident| ident.name);
-                    if name.is_none() {
-                        sess.span_diagnostic.span_err(it.span(),
-                            "allow internal unstable expects feature names")
-                    }
-                    name
+    let allow_internal_unstable =
+        attr::find_by_name(&def.attrs, sym::allow_internal_unstable).map(|attr| {
+            attr.meta_item_list()
+                .map(|list| {
+                    list.iter()
+                        .filter_map(|it| {
+                            let name = it.ident().map(|ident| ident.name);
+                            if name.is_none() {
+                                sess.span_diagnostic.span_err(
+                                    it.span(),
+                                    "allow internal unstable expects feature names",
+                                )
+                            }
+                            name
+                        })
+                        .collect::<Vec<Symbol>>()
+                        .into()
                 })
-                .collect::<Vec<Symbol>>().into()
-            )
-            .unwrap_or_else(|| {
-                sess.span_diagnostic.span_warn(
-                    attr.span, "allow_internal_unstable expects list of feature names. In the \
-                    future this will become a hard error. Please use `allow_internal_unstable(\
-                    foo, bar)` to only allow the `foo` and `bar` features",
-                );
-                vec![sym::allow_internal_unstable_backcompat_hack].into()
-            })
-        );
+                .unwrap_or_else(|| {
+                    sess.span_diagnostic.span_warn(
+                        attr.span,
+                        "allow_internal_unstable expects list of feature names. In the \
+                         future this will become a hard error. Please use `allow_internal_unstable(\
+                         foo, bar)` to only allow the `foo` and `bar` features",
+                    );
+                    vec![sym::allow_internal_unstable_backcompat_hack].into()
+                })
+        });
 
     let allow_internal_unsafe = attr::contains_name(&def.attrs, sym::allow_internal_unsafe);
 
@@ -418,14 +426,14 @@
         }
     }
 
-    let unstable_feature = attr::find_stability(&sess,
-                                                &def.attrs, def.span).and_then(|stability| {
-        if let attr::StabilityLevel::Unstable { issue, .. } = stability.level {
-            Some((stability.feature, issue))
-        } else {
-            None
-        }
-    });
+    let unstable_feature =
+        attr::find_stability(&sess, &def.attrs, def.span).and_then(|stability| {
+            if let attr::StabilityLevel::Unstable { issue, .. } = stability.level {
+                Some((stability.feature, issue))
+            } else {
+                None
+            }
+        });
 
     SyntaxExtension {
         kind: SyntaxExtensionKind::LegacyBang(expander),
@@ -440,10 +448,12 @@
     }
 }
 
-fn check_lhs_nt_follows(sess: &ParseSess,
-                        features: &Features,
-                        attrs: &[ast::Attribute],
-                        lhs: &quoted::TokenTree) -> bool {
+fn check_lhs_nt_follows(
+    sess: &ParseSess,
+    features: &Features,
+    attrs: &[ast::Attribute],
+    lhs: &quoted::TokenTree,
+) -> bool {
     // lhs is going to be like TokenTree::Delimited(...), where the
     // entire lhs is those tts. Or, it can be a "bare sequence", not wrapped in parens.
     if let quoted::TokenTree::Delimited(_, ref tts) = *lhs {
@@ -464,19 +474,22 @@
     for tt in tts {
         match *tt {
             TokenTree::Token(..) | TokenTree::MetaVar(..) | TokenTree::MetaVarDecl(..) => (),
-            TokenTree::Delimited(_, ref del) => if !check_lhs_no_empty_seq(sess, &del.tts) {
-                return false;
-            },
+            TokenTree::Delimited(_, ref del) => {
+                if !check_lhs_no_empty_seq(sess, &del.tts) {
+                    return false;
+                }
+            }
             TokenTree::Sequence(span, ref seq) => {
-                if seq.separator.is_none() && seq.tts.iter().all(|seq_tt| {
-                    match *seq_tt {
+                if seq.separator.is_none()
+                    && seq.tts.iter().all(|seq_tt| match *seq_tt {
                         TokenTree::MetaVarDecl(_, _, id) => id.name == sym::vis,
-                        TokenTree::Sequence(_, ref sub_seq) =>
+                        TokenTree::Sequence(_, ref sub_seq) => {
                             sub_seq.op == quoted::KleeneOp::ZeroOrMore
-                            || sub_seq.op == quoted::KleeneOp::ZeroOrOne,
+                                || sub_seq.op == quoted::KleeneOp::ZeroOrOne
+                        }
                         _ => false,
-                    }
-                }) {
+                    })
+                {
                     let sp = span.entire();
                     sess.span_diagnostic.span_err(sp, "repetition matches empty token tree");
                     return false;
@@ -517,7 +530,7 @@
                 if !check_lhs_duplicate_matcher_bindings(sess, &del.tts, metavar_names, node_id) {
                     return false;
                 }
-            },
+            }
             TokenTree::Sequence(_, ref seq) => {
                 if !check_lhs_duplicate_matcher_bindings(sess, &seq.tts, metavar_names, node_id) {
                     return false;
@@ -533,15 +546,17 @@
 fn check_rhs(sess: &ParseSess, rhs: &quoted::TokenTree) -> bool {
     match *rhs {
         quoted::TokenTree::Delimited(..) => return true,
-        _ => sess.span_diagnostic.span_err(rhs.span(), "macro rhs must be delimited")
+        _ => sess.span_diagnostic.span_err(rhs.span(), "macro rhs must be delimited"),
     }
     false
 }
 
-fn check_matcher(sess: &ParseSess,
-                 features: &Features,
-                 attrs: &[ast::Attribute],
-                 matcher: &[quoted::TokenTree]) -> bool {
+fn check_matcher(
+    sess: &ParseSess,
+    features: &Features,
+    attrs: &[ast::Attribute],
+    matcher: &[quoted::TokenTree],
+) -> bool {
     let first_sets = FirstSets::new(matcher);
     let empty_suffix = TokenSet::empty();
     let err = sess.span_diagnostic.err_count();
@@ -620,8 +635,8 @@
 
                         // Reverse scan: Sequence comes before `first`.
                         if subfirst.maybe_empty
-                           || seq_rep.op == quoted::KleeneOp::ZeroOrMore
-                           || seq_rep.op == quoted::KleeneOp::ZeroOrOne
+                            || seq_rep.op == quoted::KleeneOp::ZeroOrMore
+                            || seq_rep.op == quoted::KleeneOp::ZeroOrOne
                         {
                             // If sequence is potentially empty, then
                             // union them (preserving first emptiness).
@@ -659,7 +674,6 @@
                 TokenTree::Sequence(sp, ref seq_rep) => {
                     match self.first.get(&sp.entire()) {
                         Some(&Some(ref subfirst)) => {
-
                             // If the sequence contents can be empty, then the first
                             // token could be the separator token itself.
 
@@ -670,8 +684,8 @@
                             assert!(first.maybe_empty);
                             first.add_all(subfirst);
                             if subfirst.maybe_empty
-                               || seq_rep.op == quoted::KleeneOp::ZeroOrMore
-                               || seq_rep.op == quoted::KleeneOp::ZeroOrOne
+                                || seq_rep.op == quoted::KleeneOp::ZeroOrMore
+                                || seq_rep.op == quoted::KleeneOp::ZeroOrOne
                             {
                                 // continue scanning for more first
                                 // tokens, but also make sure we
@@ -720,7 +734,9 @@
 
 impl TokenSet {
     // Returns a set for the empty sequence.
-    fn empty() -> Self { TokenSet { tokens: Vec::new(), maybe_empty: true } }
+    fn empty() -> Self {
+        TokenSet { tokens: Vec::new(), maybe_empty: true }
+    }
 
     // Returns the set `{ tok }` for the single-token (and thus
     // non-empty) sequence [tok].
@@ -789,12 +805,14 @@
 //
 // Requires that `first_sets` is pre-computed for `matcher`;
 // see `FirstSets::new`.
-fn check_matcher_core(sess: &ParseSess,
-                      features: &Features,
-                      attrs: &[ast::Attribute],
-                      first_sets: &FirstSets,
-                      matcher: &[quoted::TokenTree],
-                      follow: &TokenSet) -> TokenSet {
+fn check_matcher_core(
+    sess: &ParseSess,
+    features: &Features,
+    attrs: &[ast::Attribute],
+    first_sets: &FirstSets,
+    matcher: &[quoted::TokenTree],
+    follow: &TokenSet,
+) -> TokenSet {
     use quoted::TokenTree;
 
     let mut last = TokenSet::empty();
@@ -804,11 +822,13 @@
     // then ensure T can also be followed by any element of FOLLOW.
     'each_token: for i in 0..matcher.len() {
         let token = &matcher[i];
-        let suffix = &matcher[i+1..];
+        let suffix = &matcher[i + 1..];
 
         let build_suffix_first = || {
             let mut s = first_sets.first(suffix);
-            if s.maybe_empty { s.add_all(follow); }
+            if s.maybe_empty {
+                s.add_all(follow);
+            }
             s
         };
 
@@ -824,7 +844,8 @@
                 let can_be_followed_by_any;
                 if let Err(bad_frag) = has_legal_fragment_specifier(sess, features, attrs, token) {
                     let msg = format!("invalid fragment specifier `{}`", bad_frag);
-                    sess.span_diagnostic.struct_span_err(token.span(), &msg)
+                    sess.span_diagnostic
+                        .struct_span_err(token.span(), &msg)
                         .help(VALID_FRAGMENT_NAMES_MSG)
                         .emit();
                     // (This eliminates false positives and duplicates
@@ -879,12 +900,8 @@
                 // At this point, `suffix_first` is built, and
                 // `my_suffix` is some TokenSet that we can use
                 // for checking the interior of `seq_rep`.
-                let next = check_matcher_core(sess,
-                                              features,
-                                              attrs,
-                                              first_sets,
-                                              &seq_rep.tts,
-                                              my_suffix);
+                let next =
+                    check_matcher_core(sess, features, attrs, first_sets, &seq_rep.tts, my_suffix);
                 if next.maybe_empty {
                     last.add_all(&next);
                 } else {
@@ -906,16 +923,17 @@
                 for next_token in &suffix_first.tokens {
                     match is_in_follow(next_token, &frag_spec.as_str()) {
                         IsInFollow::Invalid(msg, help) => {
-                            sess.span_diagnostic.struct_span_err(next_token.span(), &msg)
-                                .help(help).emit();
+                            sess.span_diagnostic
+                                .struct_span_err(next_token.span(), &msg)
+                                .help(help)
+                                .emit();
                             // don't bother reporting every source of
                             // conflict for a particular element of `last`.
                             continue 'each_last;
                         }
                         IsInFollow::Yes => {}
                         IsInFollow::No(possible) => {
-                            let may_be = if last.tokens.len() == 1 &&
-                                suffix_first.tokens.len() == 1
+                            let may_be = if last.tokens.len() == 1 && suffix_first.tokens.len() == 1
                             {
                                 "is"
                             } else {
@@ -925,12 +943,14 @@
                             let sp = next_token.span();
                             let mut err = sess.span_diagnostic.struct_span_err(
                                 sp,
-                                &format!("`${name}:{frag}` {may_be} followed by `{next}`, which \
-                                          is not allowed for `{frag}` fragments",
-                                         name=name,
-                                         frag=frag_spec,
-                                         next=quoted_tt_to_string(next_token),
-                                         may_be=may_be),
+                                &format!(
+                                    "`${name}:{frag}` {may_be} followed by `{next}`, which \
+                                     is not allowed for `{frag}` fragments",
+                                    name = name,
+                                    frag = frag_spec,
+                                    next = quoted_tt_to_string(next_token),
+                                    may_be = may_be
+                                ),
                             );
                             err.span_label(
                                 sp,
@@ -942,16 +962,18 @@
                                 &[t] => {
                                     err.note(&format!(
                                         "only {} is allowed after `{}` fragments",
-                                        t,
-                                        frag_spec,
+                                        t, frag_spec,
                                     ));
                                 }
                                 ts => {
                                     err.note(&format!(
                                         "{}{} or {}",
                                         msg,
-                                        ts[..ts.len() - 1].iter().map(|s| *s)
-                                            .collect::<Vec<_>>().join(", "),
+                                        ts[..ts.len() - 1]
+                                            .iter()
+                                            .map(|s| *s)
+                                            .collect::<Vec<_>>()
+                                            .join(", "),
                                         ts[ts.len() - 1],
                                     ));
                                 }
@@ -1026,13 +1048,13 @@
                 // since items *must* be followed by either a `;` or a `}`, we can
                 // accept anything after them
                 IsInFollow::Yes
-            },
+            }
             "block" => {
                 // anything can follow block, the braces provide an easy boundary to
                 // maintain
                 IsInFollow::Yes
-            },
-            "stmt" | "expr"  => {
+            }
+            "stmt" | "expr" => {
                 const TOKENS: &[&str] = &["`=>`", "`,`", "`;`"];
                 match tok {
                     TokenTree::Token(token) => match token.kind {
@@ -1041,7 +1063,7 @@
                     },
                     _ => IsInFollow::No(TOKENS),
                 }
-            },
+            }
             "pat" => {
                 const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`|`", "`if`", "`in`"];
                 match tok {
@@ -1052,40 +1074,48 @@
                     },
                     _ => IsInFollow::No(TOKENS),
                 }
-            },
+            }
             "path" | "ty" => {
                 const TOKENS: &[&str] = &[
-                    "`{`", "`[`", "`=>`", "`,`", "`>`","`=`", "`:`", "`;`", "`|`", "`as`",
+                    "`{`", "`[`", "`=>`", "`,`", "`>`", "`=`", "`:`", "`;`", "`|`", "`as`",
                     "`where`",
                 ];
                 match tok {
                     TokenTree::Token(token) => match token.kind {
-                        OpenDelim(token::DelimToken::Brace) |
-                        OpenDelim(token::DelimToken::Bracket) |
-                        Comma | FatArrow | Colon | Eq | Gt | BinOp(token::Shr) | Semi |
-                        BinOp(token::Or) => IsInFollow::Yes,
-                        Ident(name, false) if name == kw::As ||
-                                              name == kw::Where => IsInFollow::Yes,
+                        OpenDelim(token::DelimToken::Brace)
+                        | OpenDelim(token::DelimToken::Bracket)
+                        | Comma
+                        | FatArrow
+                        | Colon
+                        | Eq
+                        | Gt
+                        | BinOp(token::Shr)
+                        | Semi
+                        | BinOp(token::Or) => IsInFollow::Yes,
+                        Ident(name, false) if name == kw::As || name == kw::Where => {
+                            IsInFollow::Yes
+                        }
                         _ => IsInFollow::No(TOKENS),
                     },
-                    TokenTree::MetaVarDecl(_, _, frag) if frag.name == sym::block =>
-                        IsInFollow::Yes,
+                    TokenTree::MetaVarDecl(_, _, frag) if frag.name == sym::block => {
+                        IsInFollow::Yes
+                    }
                     _ => IsInFollow::No(TOKENS),
                 }
-            },
+            }
             "ident" | "lifetime" => {
                 // being a single token, idents and lifetimes are harmless
                 IsInFollow::Yes
-            },
+            }
             "literal" => {
                 // literals may be of a single token, or two tokens (negative numbers)
                 IsInFollow::Yes
-            },
+            }
             "meta" | "tt" => {
                 // being either a single token or a delimited sequence, tt is
                 // harmless
                 IsInFollow::Yes
-            },
+            }
             "vis" => {
                 // Explicitly disallow `priv`, on the off chance it comes back.
                 const TOKENS: &[&str] = &["`,`", "an ident", "a type"];
@@ -1093,30 +1123,39 @@
                     TokenTree::Token(token) => match token.kind {
                         Comma => IsInFollow::Yes,
                         Ident(name, is_raw) if is_raw || name != kw::Priv => IsInFollow::Yes,
-                        _ => if token.can_begin_type() {
-                            IsInFollow::Yes
-                        } else {
-                            IsInFollow::No(TOKENS)
+                        _ => {
+                            if token.can_begin_type() {
+                                IsInFollow::Yes
+                            } else {
+                                IsInFollow::No(TOKENS)
+                            }
                         }
                     },
-                    TokenTree::MetaVarDecl(_, _, frag) if frag.name == sym::ident
-                                                       || frag.name == sym::ty
-                                                       || frag.name == sym::path =>
-                        IsInFollow::Yes,
+                    TokenTree::MetaVarDecl(_, _, frag)
+                        if frag.name == sym::ident
+                            || frag.name == sym::ty
+                            || frag.name == sym::path =>
+                    {
+                        IsInFollow::Yes
+                    }
                     _ => IsInFollow::No(TOKENS),
                 }
-            },
+            }
             "" => IsInFollow::Yes, // kw::Invalid
-            _ => IsInFollow::Invalid(format!("invalid fragment specifier `{}`", frag),
-                                     VALID_FRAGMENT_NAMES_MSG),
+            _ => IsInFollow::Invalid(
+                format!("invalid fragment specifier `{}`", frag),
+                VALID_FRAGMENT_NAMES_MSG,
+            ),
         }
     }
 }
 
-fn has_legal_fragment_specifier(sess: &ParseSess,
-                                features: &Features,
-                                attrs: &[ast::Attribute],
-                                tok: &quoted::TokenTree) -> Result<(), String> {
+fn has_legal_fragment_specifier(
+    sess: &ParseSess,
+    features: &Features,
+    attrs: &[ast::Attribute],
+    tok: &quoted::TokenTree,
+) -> Result<(), String> {
     debug!("has_legal_fragment_specifier({:?})", tok);
     if let quoted::TokenTree::MetaVarDecl(_, _, ref frag_spec) = *tok {
         let frag_span = tok.span();
@@ -1127,11 +1166,13 @@
     Ok(())
 }
 
-fn is_legal_fragment_specifier(_sess: &ParseSess,
-                               _features: &Features,
-                               _attrs: &[ast::Attribute],
-                               frag_name: Symbol,
-                               _frag_span: Span) -> bool {
+fn is_legal_fragment_specifier(
+    _sess: &ParseSess,
+    _features: &Features,
+    _attrs: &[ast::Attribute],
+    frag_name: Symbol,
+    _frag_span: Span,
+) -> bool {
     /*
      * If new fragment specifiers are invented in nightly, `_sess`,
      * `_features`, `_attrs`, and `_frag_span` will be useful here
@@ -1139,9 +1180,20 @@
      * this function.
      */
     match frag_name {
-        sym::item | sym::block | sym::stmt | sym::expr | sym::pat |
-        sym::lifetime | sym::path | sym::ty | sym::ident | sym::meta | sym::tt |
-        sym::vis | sym::literal | kw::Invalid => true,
+        sym::item
+        | sym::block
+        | sym::stmt
+        | sym::expr
+        | sym::pat
+        | sym::lifetime
+        | sym::path
+        | sym::ty
+        | sym::ident
+        | sym::meta
+        | sym::tt
+        | sym::vis
+        | sym::literal
+        | kw::Invalid => true,
         _ => false,
     }
 }
@@ -1151,7 +1203,9 @@
         quoted::TokenTree::Token(ref token) => crate::print::pprust::token_to_string(&token),
         quoted::TokenTree::MetaVar(_, name) => format!("${}", name),
         quoted::TokenTree::MetaVarDecl(_, name, kind) => format!("${}:{}", name, kind),
-        _ => panic!("unexpected quoted::TokenTree::{{Sequence or Delimited}} \
-                     in follow set checker"),
+        _ => panic!(
+            "unexpected quoted::TokenTree::{{Sequence or Delimited}} \
+             in follow set checker"
+        ),
     }
 }
diff --git a/src/libsyntax/ext/tt/quoted.rs b/src/libsyntax/ext/tt/quoted.rs
index b52e3b7..6f5ce89 100644
--- a/src/libsyntax/ext/tt/quoted.rs
+++ b/src/libsyntax/ext/tt/quoted.rs
@@ -1,12 +1,12 @@
+use crate::ast;
 use crate::ast::NodeId;
 use crate::ext::tt::macro_parser;
 use crate::feature_gate::Features;
 use crate::parse::token::{self, Token, TokenKind};
 use crate::parse::ParseSess;
 use crate::print::pprust;
-use crate::tokenstream::{self, DelimSpan};
-use crate::ast;
 use crate::symbol::kw;
+use crate::tokenstream::{self, DelimSpan};
 
 use syntax_pos::{edition::Edition, BytePos, Span};
 
@@ -137,8 +137,7 @@
             TokenTree::Token(Token { span, .. })
             | TokenTree::MetaVar(span, _)
             | TokenTree::MetaVarDecl(span, _, _) => span,
-            TokenTree::Delimited(span, _)
-            | TokenTree::Sequence(span, _) => span.entire(),
+            TokenTree::Delimited(span, _) | TokenTree::Sequence(span, _) => span.entire(),
         }
     }
 
@@ -199,7 +198,7 @@
         match tree {
             TokenTree::MetaVar(start_sp, ident) if expect_matchers => {
                 let span = match trees.next() {
-                    Some(tokenstream::TokenTree::Token(Token { kind: token::Colon, span })) =>
+                    Some(tokenstream::TokenTree::Token(Token { kind: token::Colon, span })) => {
                         match trees.next() {
                             Some(tokenstream::TokenTree::Token(token)) => match token.ident() {
                                 Some((kind, _)) => {
@@ -209,22 +208,13 @@
                                 }
                                 _ => token.span,
                             },
-                            tree => tree
-                                .as_ref()
-                                .map(tokenstream::TokenTree::span)
-                                .unwrap_or(span),
-                        },
-                    tree => tree
-                        .as_ref()
-                        .map(tokenstream::TokenTree::span)
-                        .unwrap_or(start_sp),
+                            tree => tree.as_ref().map(tokenstream::TokenTree::span).unwrap_or(span),
+                        }
+                    }
+                    tree => tree.as_ref().map(tokenstream::TokenTree::span).unwrap_or(start_sp),
                 };
                 sess.missing_fragment_specifiers.borrow_mut().insert(span);
-                result.push(TokenTree::MetaVarDecl(
-                    span,
-                    ident,
-                    ast::Ident::invalid(),
-                ));
+                result.push(TokenTree::MetaVarDecl(span, ident, ast::Ident::invalid()));
             }
 
             // Not a metavar or no matchers allowed, so just return the tree
@@ -311,10 +301,8 @@
 
             // `tree` is followed by a random token. This is an error.
             Some(tokenstream::TokenTree::Token(token)) => {
-                let msg = format!(
-                    "expected identifier, found `{}`",
-                    pprust::token_to_string(&token),
-                );
+                let msg =
+                    format!("expected identifier, found `{}`", pprust::token_to_string(&token),);
                 sess.span_diagnostic.span_err(token.span, &msg);
                 TokenTree::MetaVar(token.span, ast::Ident::invalid())
             }
@@ -371,10 +359,7 @@
             Some(op) => Ok(Ok((op, token.span))),
             None => Ok(Err(token)),
         },
-        tree => Err(tree
-            .as_ref()
-            .map(tokenstream::TokenTree::span)
-            .unwrap_or(span)),
+        tree => Err(tree.as_ref().map(tokenstream::TokenTree::span).unwrap_or(span)),
     }
 }
 
@@ -426,8 +411,7 @@
     };
 
     // If we ever get to this point, we have experienced an "unexpected token" error
-    sess.span_diagnostic
-        .span_err(span, "expected one of: `*`, `+`, or `?`");
+    sess.span_diagnostic.span_err(span, "expected one of: `*`, `+`, or `?`");
 
     // Return a dummy
     (None, KleeneOp::ZeroOrMore)
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 7f80e20..8ec07de 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -560,6 +560,9 @@
     // Allows calling constructor functions in `const fn`.
     (active, const_constructor, "1.37.0", Some(61456), None),
 
+    // Allows `if/while p && let q = r && ...` chains.
+    (active, let_chains, "1.37.0", Some(53667), None),
+
     // #[repr(transparent)] on enums.
     (active, transparent_enums, "1.37.0", Some(60405), None),
 
@@ -577,7 +580,8 @@
 const INCOMPLETE_FEATURES: &[Symbol] = &[
     sym::impl_trait_in_bindings,
     sym::generic_associated_types,
-    sym::const_generics
+    sym::const_generics,
+    sym::let_chains,
 ];
 
 declare_features! (
@@ -2517,6 +2521,17 @@
             "attributes on function parameters are unstable"
         ));
 
+    sess
+        .let_chains_spans
+        .borrow()
+        .iter()
+        .for_each(|span| gate_feature!(
+            &ctx,
+            let_chains,
+            *span,
+            "`let` expressions in this position are experimental"
+        ));
+
     let visitor = &mut PostExpansionVisitor {
         context: &ctx,
         builtin_attributes: &*BUILTIN_ATTRIBUTE_MAP,
diff --git a/src/libsyntax/mut_visit.rs b/src/libsyntax/mut_visit.rs
index 5a5b633..e23bc02 100644
--- a/src/libsyntax/mut_visit.rs
+++ b/src/libsyntax/mut_visit.rs
@@ -131,10 +131,6 @@
         noop_visit_arm(a, self);
     }
 
-    fn visit_guard(&mut self, g: &mut Guard) {
-        noop_visit_guard(g, self);
-    }
-
     fn visit_pat(&mut self, p: &mut P<Pat>) {
         noop_visit_pat(p, self);
     }
@@ -389,17 +385,11 @@
 ) {
     visit_attrs(attrs, vis);
     visit_vec(pats, |pat| vis.visit_pat(pat));
-    visit_opt(guard, |guard| vis.visit_guard(guard));
+    visit_opt(guard, |guard| vis.visit_expr(guard));
     vis.visit_expr(body);
     vis.visit_span(span);
 }
 
-pub fn noop_visit_guard<T: MutVisitor>(g: &mut Guard, vis: &mut T) {
-    match g {
-        Guard::If(e) => vis.visit_expr(e),
-    }
-}
-
 pub fn noop_visit_ty_constraint<T: MutVisitor>(
     AssocTyConstraint { id, ident, kind, span }: &mut AssocTyConstraint,
     vis: &mut T
@@ -1110,28 +1100,20 @@
             vis.visit_ty(ty);
         }
         ExprKind::AddrOf(_m, ohs) => vis.visit_expr(ohs),
+        ExprKind::Let(pats, scrutinee) => {
+            visit_vec(pats, |pat| vis.visit_pat(pat));
+            vis.visit_expr(scrutinee);
+        }
         ExprKind::If(cond, tr, fl) => {
             vis.visit_expr(cond);
             vis.visit_block(tr);
             visit_opt(fl, |fl| vis.visit_expr(fl));
         }
-        ExprKind::IfLet(pats, expr, tr, fl) => {
-            visit_vec(pats, |pat| vis.visit_pat(pat));
-            vis.visit_expr(expr);
-            vis.visit_block(tr);
-            visit_opt(fl, |fl| vis.visit_expr(fl));
-        }
         ExprKind::While(cond, body, label) => {
             vis.visit_expr(cond);
             vis.visit_block(body);
             visit_opt(label, |label| vis.visit_label(label));
         }
-        ExprKind::WhileLet(pats, expr, body, label) => {
-            visit_vec(pats, |pat| vis.visit_pat(pat));
-            vis.visit_expr(expr);
-            vis.visit_block(body);
-            visit_opt(label, |label| vis.visit_label(label));
-        }
         ExprKind::ForLoop(pat, iter, body, label) => {
             vis.visit_pat(pat);
             vis.visit_expr(iter);
diff --git a/src/libsyntax/parse/classify.rs b/src/libsyntax/parse/classify.rs
index dfd6f45..6ebfab3 100644
--- a/src/libsyntax/parse/classify.rs
+++ b/src/libsyntax/parse/classify.rs
@@ -14,11 +14,9 @@
 pub fn expr_requires_semi_to_be_stmt(e: &ast::Expr) -> bool {
     match e.node {
         ast::ExprKind::If(..) |
-        ast::ExprKind::IfLet(..) |
         ast::ExprKind::Match(..) |
         ast::ExprKind::Block(..) |
         ast::ExprKind::While(..) |
-        ast::ExprKind::WhileLet(..) |
         ast::ExprKind::Loop(..) |
         ast::ExprKind::ForLoop(..) |
         ast::ExprKind::TryBlock(..) => false,
diff --git a/src/libsyntax/parse/lexer/mod.rs b/src/libsyntax/parse/lexer/mod.rs
index 1c44155..ead5d54 100644
--- a/src/libsyntax/parse/lexer/mod.rs
+++ b/src/libsyntax/parse/lexer/mod.rs
@@ -1491,6 +1491,7 @@
             edition: Edition::from_session(),
             ambiguous_block_expr_parse: Lock::new(FxHashMap::default()),
             param_attr_spans: Lock::new(Vec::new()),
+            let_chains_spans: Lock::new(Vec::new()),
         }
     }
 
diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs
index ba5d1d0..e19eab3 100644
--- a/src/libsyntax/parse/mod.rs
+++ b/src/libsyntax/parse/mod.rs
@@ -54,7 +54,9 @@
     /// operation token that followed it, but that the parser cannot identify without further
     /// analysis.
     pub ambiguous_block_expr_parse: Lock<FxHashMap<Span, Span>>,
-    pub param_attr_spans: Lock<Vec<Span>>
+    pub param_attr_spans: Lock<Vec<Span>>,
+    // Places where `let` exprs were used and should be feature gated according to `let_chains`.
+    pub let_chains_spans: Lock<Vec<Span>>,
 }
 
 impl ParseSess {
@@ -81,6 +83,7 @@
             edition: Edition::from_session(),
             ambiguous_block_expr_parse: Lock::new(FxHashMap::default()),
             param_attr_spans: Lock::new(Vec::new()),
+            let_chains_spans: Lock::new(Vec::new()),
         }
     }
 
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index fa697e0..f3ace84 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -3,7 +3,7 @@
 use crate::ast::{AngleBracketedArgs, ParenthesizedArgs, AttrStyle, BareFnTy};
 use crate::ast::{GenericBound, TraitBoundModifier};
 use crate::ast::Unsafety;
-use crate::ast::{Mod, AnonConst, Arg, Arm, Guard, Attribute, BindingMode, TraitItemKind};
+use crate::ast::{Mod, AnonConst, Arg, Arm, Attribute, BindingMode, TraitItemKind};
 use crate::ast::Block;
 use crate::ast::{BlockCheckMode, CaptureBy, Movability};
 use crate::ast::{Constness, Crate};
@@ -41,7 +41,7 @@
 use crate::parse::lexer::comments::{doc_comment_style, strip_doc_comment_decoration};
 use crate::parse::token::{Token, TokenKind, DelimToken};
 use crate::parse::{new_sub_parser_from_file, ParseSess, Directory, DirectoryOwnership};
-use crate::util::parser::{AssocOp, Fixity};
+use crate::util::parser::{AssocOp, Fixity, prec_let_scrutinee_needs_par};
 use crate::print::pprust;
 use crate::ptr::P;
 use crate::parse::PResult;
@@ -2215,13 +2215,8 @@
                     } else {
                         ex = ExprKind::Yield(None);
                     }
-                } else if self.token.is_keyword(kw::Let) {
-                    // Catch this syntax error here, instead of in `parse_ident`, so
-                    // that we can explicitly mention that let is not to be used as an expression
-                    let mut db = self.fatal("expected expression, found statement (`let`)");
-                    db.span_label(self.token.span, "expected expression");
-                    db.note("variable declaration using `let` is a statement");
-                    return Err(db);
+                } else if self.eat_keyword(kw::Let) {
+                    return self.parse_let_expr(attrs);
                 } else if is_span_rust_2018 && self.eat_keyword(kw::Await) {
                     let (await_hi, e_kind) = self.parse_await_macro_or_alt(lo, self.prev_span)?;
                     hi = await_hi;
@@ -2483,15 +2478,13 @@
                 attrs.extend::<Vec<_>>(expr.attrs.into());
                 expr.attrs = attrs;
                 match expr.node {
-                    ExprKind::If(..) | ExprKind::IfLet(..) => {
-                        if !expr.attrs.is_empty() {
-                            // Just point to the first attribute in there...
-                            let span = expr.attrs[0].span;
+                    ExprKind::If(..) if !expr.attrs.is_empty() => {
+                        // Just point to the first attribute in there...
+                        let span = expr.attrs[0].span;
 
-                            self.span_err(span,
-                                "attributes are not yet allowed on `if` \
-                                expressions");
-                        }
+                        self.span_err(span,
+                            "attributes are not yet allowed on `if` \
+                            expressions");
                     }
                     _ => {}
                 }
@@ -3161,13 +3154,10 @@
         }
     }
 
-    /// Parses an `if` or `if let` expression (`if` token already eaten).
+    /// Parses an `if` expression (`if` token already eaten).
     fn parse_if_expr(&mut self, attrs: ThinVec<Attribute>) -> PResult<'a, P<Expr>> {
-        if self.check_keyword(kw::Let) {
-            return self.parse_if_let_expr(attrs);
-        }
         let lo = self.prev_span;
-        let cond = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?;
+        let cond = self.parse_cond_expr()?;
 
         // Verify that the parsed `if` condition makes sense as a condition. If it is a block, then
         // verify that the last statement is either an implicit return (no `;`) or an explicit
@@ -3197,22 +3187,32 @@
         Ok(self.mk_expr(lo.to(hi), ExprKind::If(cond, thn, els), attrs))
     }
 
-    /// Parses an `if let` expression (`if` token already eaten).
-    fn parse_if_let_expr(&mut self, attrs: ThinVec<Attribute>)
-                             -> PResult<'a, P<Expr>> {
+    /// Parse the condition of a `if`- or `while`-expression
+    fn parse_cond_expr(&mut self) -> PResult<'a, P<Expr>> {
+        let cond = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?;
+
+        if let ExprKind::Let(..) = cond.node {
+            // Remove the last feature gating of a `let` expression since it's stable.
+            let last = self.sess.let_chains_spans.borrow_mut().pop();
+            debug_assert_eq!(cond.span, last.unwrap());
+        }
+
+        Ok(cond)
+    }
+
+    /// Parses a `let $pats = $expr` pseudo-expression.
+    /// The `let` token has already been eaten.
+    fn parse_let_expr(&mut self, attrs: ThinVec<Attribute>) -> PResult<'a, P<Expr>> {
         let lo = self.prev_span;
-        self.expect_keyword(kw::Let)?;
         let pats = self.parse_pats()?;
         self.expect(&token::Eq)?;
-        let expr = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?;
-        let thn = self.parse_block()?;
-        let (hi, els) = if self.eat_keyword(kw::Else) {
-            let expr = self.parse_else_expr()?;
-            (expr.span, Some(expr))
-        } else {
-            (thn.span, None)
-        };
-        Ok(self.mk_expr(lo.to(hi), ExprKind::IfLet(pats, expr, thn, els), attrs))
+        let expr = self.with_res(
+            Restrictions::NO_STRUCT_LITERAL,
+            |this| this.parse_assoc_expr_with(1 + prec_let_scrutinee_needs_par(), None.into())
+        )?;
+        let span = lo.to(expr.span);
+        self.sess.let_chains_spans.borrow_mut().push(span);
+        Ok(self.mk_expr(span, ExprKind::Let(pats, expr), attrs))
     }
 
     /// Parses `move |args| expr`.
@@ -3299,28 +3299,11 @@
     fn parse_while_expr(&mut self, opt_label: Option<Label>,
                             span_lo: Span,
                             mut attrs: ThinVec<Attribute>) -> PResult<'a, P<Expr>> {
-        if self.token.is_keyword(kw::Let) {
-            return self.parse_while_let_expr(opt_label, span_lo, attrs);
-        }
-        let cond = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?;
+        let cond = self.parse_cond_expr()?;
         let (iattrs, body) = self.parse_inner_attrs_and_block()?;
         attrs.extend(iattrs);
         let span = span_lo.to(body.span);
-        return Ok(self.mk_expr(span, ExprKind::While(cond, body, opt_label), attrs));
-    }
-
-    /// Parses a `while let` expression (`while` token already eaten).
-    fn parse_while_let_expr(&mut self, opt_label: Option<Label>,
-                                span_lo: Span,
-                                mut attrs: ThinVec<Attribute>) -> PResult<'a, P<Expr>> {
-        self.expect_keyword(kw::Let)?;
-        let pats = self.parse_pats()?;
-        self.expect(&token::Eq)?;
-        let expr = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?;
-        let (iattrs, body) = self.parse_inner_attrs_and_block()?;
-        attrs.extend(iattrs);
-        let span = span_lo.to(body.span);
-        return Ok(self.mk_expr(span, ExprKind::WhileLet(pats, expr, body, opt_label), attrs));
+        Ok(self.mk_expr(span, ExprKind::While(cond, body, opt_label), attrs))
     }
 
     // parse `loop {...}`, `loop` token already eaten
@@ -3413,7 +3396,7 @@
         let lo = self.token.span;
         let pats = self.parse_pats()?;
         let guard = if self.eat_keyword(kw::If) {
-            Some(Guard::If(self.parse_expr()?))
+            Some(self.parse_expr()?)
         } else {
             None
         };
diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs
index cc34883..ebd0dec 100644
--- a/src/libsyntax/parse/token.rs
+++ b/src/libsyntax/parse/token.rs
@@ -135,6 +135,7 @@
         kw::False,
         kw::For,
         kw::If,
+        kw::Let,
         kw::Loop,
         kw::Match,
         kw::Move,
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 0aac4f8..3f05992 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -1715,6 +1715,21 @@
         self.ann.post(self, AnnNode::Block(blk))
     }
 
+    /// Print a `let pats = scrutinee` expression.
+    pub fn print_let(&mut self, pats: &[P<ast::Pat>], scrutinee: &ast::Expr) -> io::Result<()> {
+        self.s.word("let ")?;
+
+        self.print_pats(pats)?;
+        self.s.space()?;
+
+        self.word_space("=")?;
+        self.print_expr_cond_paren(
+            scrutinee,
+            Self::cond_needs_par(scrutinee)
+            || parser::needs_par_as_let_scrutinee(scrutinee.precedence().order())
+        )
+    }
+
     fn print_else(&mut self, els: Option<&ast::Expr>) -> io::Result<()> {
         match els {
             Some(_else) => {
@@ -1729,19 +1744,6 @@
                         self.print_block(then)?;
                         self.print_else(e.as_ref().map(|e| &**e))
                     }
-                    // Another `else if let` block.
-                    ast::ExprKind::IfLet(ref pats, ref expr, ref then, ref e) => {
-                        self.cbox(INDENT_UNIT - 1)?;
-                        self.ibox(0)?;
-                        self.s.word(" else if let ")?;
-                        self.print_pats(pats)?;
-                        self.s.space()?;
-                        self.word_space("=")?;
-                        self.print_expr_as_cond(expr)?;
-                        self.s.space()?;
-                        self.print_block(then)?;
-                        self.print_else(e.as_ref().map(|e| &**e))
-                    }
                     // Final `else` block.
                     ast::ExprKind::Block(ref b, _) => {
                         self.cbox(INDENT_UNIT - 1)?;
@@ -1762,20 +1764,10 @@
     pub fn print_if(&mut self, test: &ast::Expr, blk: &ast::Block,
                     elseopt: Option<&ast::Expr>) -> io::Result<()> {
         self.head("if")?;
+
         self.print_expr_as_cond(test)?;
         self.s.space()?;
-        self.print_block(blk)?;
-        self.print_else(elseopt)
-    }
 
-    pub fn print_if_let(&mut self, pats: &[P<ast::Pat>], expr: &ast::Expr, blk: &ast::Block,
-                        elseopt: Option<&ast::Expr>) -> io::Result<()> {
-        self.head("if let")?;
-        self.print_pats(pats)?;
-        self.s.space()?;
-        self.word_space("=")?;
-        self.print_expr_as_cond(expr)?;
-        self.s.space()?;
         self.print_block(blk)?;
         self.print_else(elseopt)
     }
@@ -1807,21 +1799,18 @@
     }
 
     pub fn print_expr_maybe_paren(&mut self, expr: &ast::Expr, prec: i8) -> io::Result<()> {
-        let needs_par = expr.precedence().order() < prec;
-        if needs_par {
-            self.popen()?;
-        }
-        self.print_expr(expr)?;
-        if needs_par {
-            self.pclose()?;
-        }
-        Ok(())
+        self.print_expr_cond_paren(expr, expr.precedence().order() < prec)
     }
 
     /// Print an expr using syntax that's acceptable in a condition position, such as the `cond` in
     /// `if cond { ... }`.
     pub fn print_expr_as_cond(&mut self, expr: &ast::Expr) -> io::Result<()> {
-        let needs_par = match expr.node {
+        self.print_expr_cond_paren(expr, Self::cond_needs_par(expr))
+    }
+
+    /// Does `expr` need parenthesis when printed in a condition position?
+    fn cond_needs_par(expr: &ast::Expr) -> bool {
+        match expr.node {
             // These cases need parens due to the parse error observed in #26461: `if return {}`
             // parses as the erroneous construct `if (return {})`, not `if (return) {}`.
             ast::ExprKind::Closure(..) |
@@ -1829,8 +1818,11 @@
             ast::ExprKind::Break(..) => true,
 
             _ => parser::contains_exterior_struct_lit(expr),
-        };
+        }
+    }
 
+    /// Print `expr` or `(expr)` when `needs_par` holds.
+    fn print_expr_cond_paren(&mut self, expr: &ast::Expr, needs_par: bool) -> io::Result<()> {
         if needs_par {
             self.popen()?;
         }
@@ -1962,6 +1954,17 @@
             // of `(x as i32) < ...`. We need to convince it _not_ to do that.
             (&ast::ExprKind::Cast { .. }, ast::BinOpKind::Lt) |
             (&ast::ExprKind::Cast { .. }, ast::BinOpKind::Shl) => parser::PREC_FORCE_PAREN,
+            // We are given `(let _ = a) OP b`.
+            //
+            // - When `OP <= LAnd` we should print `let _ = a OP b` to avoid redundant parens
+            //   as the parser will interpret this as `(let _ = a) OP b`.
+            //
+            // - Otherwise, e.g. when we have `(let a = b) < c` in AST,
+            //   parens are required since the parser would interpret `let a = b < c` as
+            //   `let a = (b < c)`. To achieve this, we force parens.
+            (&ast::ExprKind::Let { .. }, _) if !parser::needs_par_as_let_scrutinee(prec) => {
+                parser::PREC_FORCE_PAREN
+            }
             _ => left_prec,
         };
 
@@ -2052,12 +2055,12 @@
                 self.word_space(":")?;
                 self.print_type(ty)?;
             }
+            ast::ExprKind::Let(ref pats, ref scrutinee) => {
+                self.print_let(pats, scrutinee)?;
+            }
             ast::ExprKind::If(ref test, ref blk, ref elseopt) => {
                 self.print_if(test, blk, elseopt.as_ref().map(|e| &**e))?;
             }
-            ast::ExprKind::IfLet(ref pats, ref expr, ref blk, ref elseopt) => {
-                self.print_if_let(pats, expr, blk, elseopt.as_ref().map(|e| &**e))?;
-            }
             ast::ExprKind::While(ref test, ref blk, opt_label) => {
                 if let Some(label) = opt_label {
                     self.print_ident(label.ident)?;
@@ -2068,19 +2071,6 @@
                 self.s.space()?;
                 self.print_block_with_attrs(blk, attrs)?;
             }
-            ast::ExprKind::WhileLet(ref pats, ref expr, ref blk, opt_label) => {
-                if let Some(label) = opt_label {
-                    self.print_ident(label.ident)?;
-                    self.word_space(":")?;
-                }
-                self.head("while let")?;
-                self.print_pats(pats)?;
-                self.s.space()?;
-                self.word_space("=")?;
-                self.print_expr_as_cond(expr)?;
-                self.s.space()?;
-                self.print_block_with_attrs(blk, attrs)?;
-            }
             ast::ExprKind::ForLoop(ref pat, ref iter, ref blk, opt_label) => {
                 if let Some(label) = opt_label {
                     self.print_ident(label.ident)?;
@@ -2663,14 +2653,10 @@
         self.print_outer_attributes(&arm.attrs)?;
         self.print_pats(&arm.pats)?;
         self.s.space()?;
-        if let Some(ref g) = arm.guard {
-            match g {
-                ast::Guard::If(ref e) => {
-                    self.word_space("if")?;
-                    self.print_expr(e)?;
-                    self.s.space()?;
-                }
-            }
+        if let Some(ref e) = arm.guard {
+            self.word_space("if")?;
+            self.print_expr(e)?;
+            self.s.space()?;
         }
         self.word_space("=>")?;
 
diff --git a/src/libsyntax/util/parser.rs b/src/libsyntax/util/parser.rs
index fcecee8..1e52186 100644
--- a/src/libsyntax/util/parser.rs
+++ b/src/libsyntax/util/parser.rs
@@ -260,6 +260,7 @@
 
     Box,
     AddrOf,
+    Let,
     Unary,
 
     Call,
@@ -277,9 +278,7 @@
     Path,
     Paren,
     If,
-    IfLet,
     While,
-    WhileLet,
     ForLoop,
     Loop,
     Match,
@@ -318,6 +317,11 @@
             // Unary, prefix
             ExprPrecedence::Box |
             ExprPrecedence::AddrOf |
+            // Here `let pats = expr` has `let pats =` as a "unary" prefix of `expr`.
+            // However, this is not exactly right. When `let _ = a` is the LHS of a binop we
+            // need parens sometimes. E.g. we can print `(let _ = a) && b` as `let _ = a && b`
+            // but we need to print `(let _ = a) < b` as-is with parens.
+            ExprPrecedence::Let |
             ExprPrecedence::Unary => PREC_PREFIX,
 
             // Unary, postfix
@@ -338,9 +342,7 @@
             ExprPrecedence::Path |
             ExprPrecedence::Paren |
             ExprPrecedence::If |
-            ExprPrecedence::IfLet |
             ExprPrecedence::While |
-            ExprPrecedence::WhileLet |
             ExprPrecedence::ForLoop |
             ExprPrecedence::Loop |
             ExprPrecedence::Match |
@@ -353,6 +355,19 @@
     }
 }
 
+/// In `let p = e`, operators with precedence `<=` this one requires parenthesis in `e`.
+crate fn prec_let_scrutinee_needs_par() -> usize {
+    AssocOp::LAnd.precedence()
+}
+
+/// Suppose we have `let _ = e` and the `order` of `e`.
+/// Is the `order` such that `e` in `let _ = e` needs parenthesis when it is on the RHS?
+///
+/// Conversely, suppose that we have `(let _ = a) OP b` and `order` is that of `OP`.
+/// Can we print this as `let _ = a OP b`?
+crate fn needs_par_as_let_scrutinee(order: i8) -> bool {
+    order <= prec_let_scrutinee_needs_par() as i8
+}
 
 /// Expressions that syntactically contain an "exterior" struct literal i.e., not surrounded by any
 /// parens or other delimiters, e.g., `X { y: 1 }`, `X { y: 1 }.method()`, `foo == X { y: 1 }` and
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index 8132024..9ec9550 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -719,6 +719,10 @@
             visitor.visit_expr(subexpression);
             visitor.visit_ty(typ)
         }
+        ExprKind::Let(ref pats, ref scrutinee) => {
+            walk_list!(visitor, visit_pat, pats);
+            visitor.visit_expr(scrutinee);
+        }
         ExprKind::If(ref head_expression, ref if_block, ref optional_else) => {
             visitor.visit_expr(head_expression);
             visitor.visit_block(if_block);
@@ -729,18 +733,6 @@
             visitor.visit_expr(subexpression);
             visitor.visit_block(block);
         }
-        ExprKind::IfLet(ref pats, ref subexpression, ref if_block, ref optional_else) => {
-            walk_list!(visitor, visit_pat, pats);
-            visitor.visit_expr(subexpression);
-            visitor.visit_block(if_block);
-            walk_list!(visitor, visit_expr, optional_else);
-        }
-        ExprKind::WhileLet(ref pats, ref subexpression, ref block, ref opt_label) => {
-            walk_list!(visitor, visit_label, opt_label);
-            walk_list!(visitor, visit_pat, pats);
-            visitor.visit_expr(subexpression);
-            visitor.visit_block(block);
-        }
         ExprKind::ForLoop(ref pattern, ref subexpression, ref block, ref opt_label) => {
             walk_list!(visitor, visit_label, opt_label);
             visitor.visit_pat(pattern);
@@ -834,10 +826,8 @@
 
 pub fn walk_arm<'a, V: Visitor<'a>>(visitor: &mut V, arm: &'a Arm) {
     walk_list!(visitor, visit_pat, &arm.pats);
-    if let Some(ref g) = &arm.guard {
-        match g {
-            Guard::If(ref e) => visitor.visit_expr(e),
-        }
+    if let Some(ref e) = &arm.guard {
+        visitor.visit_expr(e);
     }
     visitor.visit_expr(&arm.body);
     walk_list!(visitor, visit_attribute, &arm.attrs);
diff --git a/src/libsyntax_ext/deriving/mod.rs b/src/libsyntax_ext/deriving/mod.rs
index 1fe6094..e75eff2 100644
--- a/src/libsyntax_ext/deriving/mod.rs
+++ b/src/libsyntax_ext/deriving/mod.rs
@@ -88,7 +88,7 @@
                         )
                     }),
                 );
-            )*
+            )+
         }
     }
 }
diff --git a/src/libsyntax_ext/proc_macro_server.rs b/src/libsyntax_ext/proc_macro_server.rs
index b5d5a38..c9d99e5 100644
--- a/src/libsyntax_ext/proc_macro_server.rs
+++ b/src/libsyntax_ext/proc_macro_server.rs
@@ -70,7 +70,7 @@
         macro_rules! tt {
             ($ty:ident { $($field:ident $(: $value:expr)*),+ $(,)? }) => (
                 TokenTree::$ty(self::$ty {
-                    $($field $(: $value)*,)*
+                    $($field $(: $value)*,)+
                     span,
                 })
             );
diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs
index c25e65e..756bc8c 100644
--- a/src/libsyntax_pos/symbol.rs
+++ b/src/libsyntax_pos/symbol.rs
@@ -354,6 +354,7 @@
         label_break_value,
         lang,
         lang_items,
+        let_chains,
         lhs,
         lib,
         lifetime,
diff --git a/src/llvm-project b/src/llvm-project
index 788592f..1bbe0b3 160000
--- a/src/llvm-project
+++ b/src/llvm-project
@@ -1 +1 @@
-Subproject commit 788592fb2740d560d0614a77035b319b9d07aa38
+Subproject commit 1bbe0b3e1d756116cbf1fcf049555066ef929008
diff --git a/src/test/incremental/issue-59523-on-implemented-is-not-unused.rs b/src/test/incremental/issue-59523-on-implemented-is-not-unused.rs
index 3d16a15..afc0862 100644
--- a/src/test/incremental/issue-59523-on-implemented-is-not-unused.rs
+++ b/src/test/incremental/issue-59523-on-implemented-is-not-unused.rs
@@ -2,7 +2,7 @@
 // rustc_on_unimplemented, but with this bug we are seeing it fire (on
 // subsequent runs) if incremental compilation is enabled.
 
-// revisions: rpass1 rpass2
+// revisions: cfail1 cfail2
 // compile-pass
 
 #![feature(on_unimplemented)]
diff --git a/src/test/incremental/issue-59524-layout-scalar-valid-range-is-not-unused.rs b/src/test/incremental/issue-59524-layout-scalar-valid-range-is-not-unused.rs
index e4802cb..37bd8d0 100644
--- a/src/test/incremental/issue-59524-layout-scalar-valid-range-is-not-unused.rs
+++ b/src/test/incremental/issue-59524-layout-scalar-valid-range-is-not-unused.rs
@@ -3,7 +3,7 @@
 // seeing it fire (on subsequent runs) if incremental compilation is
 // enabled.
 
-// revisions: rpass1 rpass2
+// revisions: cfail1 cfail2
 // compile-pass
 
 #![feature(rustc_attrs)]
diff --git a/src/test/incremental/issue-61530.rs b/src/test/incremental/issue-61530.rs
new file mode 100644
index 0000000..06b2957
--- /dev/null
+++ b/src/test/incremental/issue-61530.rs
@@ -0,0 +1,17 @@
+#![feature(repr_simd, platform_intrinsics)]
+
+// revisions:rpass1 rpass2
+
+#[repr(simd)]
+struct I32x2(i32, i32);
+
+extern "platform-intrinsic" {
+    fn simd_shuffle2<T, U>(x: T, y: T, idx: [u32; 2]) -> U;
+}
+
+fn main() {
+    unsafe {
+        let _: I32x2 = simd_shuffle2(I32x2(1, 2), I32x2(3, 4), [0, 0]);
+        let _: I32x2 = simd_shuffle2(I32x2(1, 2), I32x2(3, 4), [0, 0]);
+    }
+}
diff --git a/src/test/incremental/no_mangle.rs b/src/test/incremental/no_mangle.rs
index 1b17886..b1c9b2b 100644
--- a/src/test/incremental/no_mangle.rs
+++ b/src/test/incremental/no_mangle.rs
@@ -1,6 +1,6 @@
-// revisions:rpass1 rpass2
+// revisions:cfail1 cfail2
+// check-pass
 // compile-flags: --crate-type cdylib
-// skip-codegen
 
 #![deny(unused_attributes)]
 
diff --git a/src/test/run-fail/rfc-1937-termination-trait/termination-trait-for-box-dyn-error.rs b/src/test/run-fail/rfc-1937-termination-trait/termination-trait-for-box-dyn-error.rs
index 5f76caa..796729a 100644
--- a/src/test/run-fail/rfc-1937-termination-trait/termination-trait-for-box-dyn-error.rs
+++ b/src/test/run-fail/rfc-1937-termination-trait/termination-trait-for-box-dyn-error.rs
@@ -1,4 +1,4 @@
-// compile-pass
+// error-pattern:returned Box<dyn Error> from main()
 // failure-status: 1
 
 use std::error::Error;
diff --git a/src/test/run-fail/rfc-1937-termination-trait/termination-trait-for-result-box-error_err.rs b/src/test/run-fail/rfc-1937-termination-trait/termination-trait-for-result-box-error_err.rs
index bd47a97..2f3a73a 100644
--- a/src/test/run-fail/rfc-1937-termination-trait/termination-trait-for-result-box-error_err.rs
+++ b/src/test/run-fail/rfc-1937-termination-trait/termination-trait-for-result-box-error_err.rs
@@ -1,4 +1,4 @@
-// compile-pass
+// error-pattern:returned Box<Error> from main()
 // failure-status: 1
 
 use std::io::{Error, ErrorKind};
diff --git a/src/test/run-pass-fulldeps/auxiliary/issue-40001-plugin.rs b/src/test/run-pass-fulldeps/auxiliary/issue-40001-plugin.rs
index 76554ea..64b795a 100644
--- a/src/test/run-pass-fulldeps/auxiliary/issue-40001-plugin.rs
+++ b/src/test/run-pass-fulldeps/auxiliary/issue-40001-plugin.rs
@@ -43,7 +43,7 @@
                 span: source_map::Span,
                 id: hir::HirId) {
 
-        let item = match cx.tcx.hir().get_by_hir_id(id) {
+        let item = match cx.tcx.hir().get(id) {
             Node::Item(item) => item,
             _ => cx.tcx.hir().expect_item(cx.tcx.hir().get_parent_item(id)),
         };
diff --git a/src/test/run-pass-fulldeps/pprust-expr-roundtrip.rs b/src/test/run-pass-fulldeps/pprust-expr-roundtrip.rs
index 7e819e2..5716e6d 100644
--- a/src/test/run-pass-fulldeps/pprust-expr-roundtrip.rs
+++ b/src/test/run-pass-fulldeps/pprust-expr-roundtrip.rs
@@ -68,7 +68,7 @@
 
     let mut g = |e| f(expr(e));
 
-    for kind in 0 .. 16 {
+    for kind in 0..=19 {
         match kind {
             0 => iter_exprs(depth - 1, &mut |e| g(ExprKind::Box(e))),
             1 => iter_exprs(depth - 1, &mut |e| g(ExprKind::Call(e, vec![]))),
@@ -79,25 +79,26 @@
                 iter_exprs(depth - 1, &mut |e| g(ExprKind::MethodCall(
                             seg.clone(), vec![make_x(), e])));
             },
-            3 => {
-                let op = Spanned { span: DUMMY_SP, node: BinOpKind::Add };
+            3..=8 => {
+                let op = Spanned {
+                    span: DUMMY_SP,
+                    node: match kind {
+                        3 => BinOpKind::Add,
+                        4 => BinOpKind::Mul,
+                        5 => BinOpKind::Shl,
+                        6 => BinOpKind::And,
+                        7 => BinOpKind::Or,
+                        8 => BinOpKind::Lt,
+                        _ => unreachable!(),
+                    }
+                };
                 iter_exprs(depth - 1, &mut |e| g(ExprKind::Binary(op, e, make_x())));
                 iter_exprs(depth - 1, &mut |e| g(ExprKind::Binary(op, make_x(), e)));
             },
-            4 => {
-                let op = Spanned { span: DUMMY_SP, node: BinOpKind::Mul };
-                iter_exprs(depth - 1, &mut |e| g(ExprKind::Binary(op, e, make_x())));
-                iter_exprs(depth - 1, &mut |e| g(ExprKind::Binary(op, make_x(), e)));
-            },
-            5 => {
-                let op = Spanned { span: DUMMY_SP, node: BinOpKind::Shl };
-                iter_exprs(depth - 1, &mut |e| g(ExprKind::Binary(op, e, make_x())));
-                iter_exprs(depth - 1, &mut |e| g(ExprKind::Binary(op, make_x(), e)));
-            },
-            6 => {
+            9 => {
                 iter_exprs(depth - 1, &mut |e| g(ExprKind::Unary(UnOp::Deref, e)));
             },
-            7 => {
+            10 => {
                 let block = P(Block {
                     stmts: Vec::new(),
                     id: DUMMY_NODE_ID,
@@ -106,7 +107,7 @@
                 });
                 iter_exprs(depth - 1, &mut |e| g(ExprKind::If(e, block.clone(), None)));
             },
-            8 => {
+            11 => {
                 let decl = P(FnDecl {
                     inputs: vec![],
                     output: FunctionRetTy::Default(DUMMY_SP),
@@ -120,33 +121,41 @@
                                           e,
                                           DUMMY_SP)));
             },
-            9 => {
+            12 => {
                 iter_exprs(depth - 1, &mut |e| g(ExprKind::Assign(e, make_x())));
                 iter_exprs(depth - 1, &mut |e| g(ExprKind::Assign(make_x(), e)));
             },
-            10 => {
+            13 => {
                 iter_exprs(depth - 1, &mut |e| g(ExprKind::Field(e, Ident::from_str("f"))));
             },
-            11 => {
+            14 => {
                 iter_exprs(depth - 1, &mut |e| g(ExprKind::Range(
                             Some(e), Some(make_x()), RangeLimits::HalfOpen)));
                 iter_exprs(depth - 1, &mut |e| g(ExprKind::Range(
                             Some(make_x()), Some(e), RangeLimits::HalfOpen)));
             },
-            12 => {
+            15 => {
                 iter_exprs(depth - 1, &mut |e| g(ExprKind::AddrOf(Mutability::Immutable, e)));
             },
-            13 => {
+            16 => {
                 g(ExprKind::Ret(None));
                 iter_exprs(depth - 1, &mut |e| g(ExprKind::Ret(Some(e))));
             },
-            14 => {
+            17 => {
                 let path = Path::from_ident(Ident::from_str("S"));
                 g(ExprKind::Struct(path, vec![], Some(make_x())));
             },
-            15 => {
+            18 => {
                 iter_exprs(depth - 1, &mut |e| g(ExprKind::Try(e)));
             },
+            19 => {
+                let ps = vec![P(Pat {
+                    id: DUMMY_NODE_ID,
+                    node: PatKind::Wild,
+                    span: DUMMY_SP,
+                })];
+                iter_exprs(depth - 1, &mut |e| g(ExprKind::Let(ps.clone(), e)))
+            },
             _ => panic!("bad counter value in iter_exprs"),
         }
     }
diff --git a/src/test/run-pass/borrowck/issue-62007-assign-box.rs b/src/test/run-pass/borrowck/issue-62007-assign-box.rs
new file mode 100644
index 0000000..f6fbea8
--- /dev/null
+++ b/src/test/run-pass/borrowck/issue-62007-assign-box.rs
@@ -0,0 +1,27 @@
+// run-pass
+
+// Issue #62007: assigning over a deref projection of a box (in this
+// case, `*list = n;`) should be able to kill all borrows of `*list`,
+// so that `*list` can be borrowed on the next iteration through the
+// loop.
+
+#![allow(dead_code)]
+
+struct List<T> {
+    value: T,
+    next: Option<Box<List<T>>>,
+}
+
+fn to_refs<T>(mut list: Box<&mut List<T>>) -> Vec<&mut T> {
+    let mut result = vec![];
+    loop {
+        result.push(&mut list.value);
+        if let Some(n) = list.next.as_mut() {
+            *list = n;
+        } else {
+            return result;
+        }
+    }
+}
+
+fn main() {}
diff --git a/src/test/run-pass/borrowck/issue-62007-assign-field.rs b/src/test/run-pass/borrowck/issue-62007-assign-field.rs
new file mode 100644
index 0000000..5b21c083
--- /dev/null
+++ b/src/test/run-pass/borrowck/issue-62007-assign-field.rs
@@ -0,0 +1,26 @@
+// run-pass
+
+// Issue #62007: assigning over a field projection (`list.0 = n;` in
+// this case) should be able to kill all borrows of `list.0`, so that
+// `list.0` can be borrowed on the next iteration through the loop.
+
+#![allow(dead_code)]
+
+struct List<T> {
+    value: T,
+    next: Option<Box<List<T>>>,
+}
+
+fn to_refs<T>(mut list: (&mut List<T>,)) -> Vec<&mut T> {
+    let mut result = vec![];
+    loop {
+        result.push(&mut (list.0).value);
+        if let Some(n) = (list.0).next.as_mut() {
+            list.0 = n;
+        } else {
+            return result;
+        }
+    }
+}
+
+fn main() {}
diff --git a/src/test/run-pass/compiletest-skip-codegen.rs b/src/test/run-pass/compiletest-skip-codegen.rs
deleted file mode 100644
index 3c0e226..0000000
--- a/src/test/run-pass/compiletest-skip-codegen.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-// Test that with the `skip-codegen` option the test isn't executed.
-
-// skip-codegen
-
-fn main() {
-    unreachable!();
-}
diff --git a/src/test/rustdoc/empty-section.rs b/src/test/rustdoc/empty-section.rs
index 619f2d6..d95f3a8 100644
--- a/src/test/rustdoc/empty-section.rs
+++ b/src/test/rustdoc/empty-section.rs
@@ -8,3 +8,6 @@
 // @!has - 'Auto Trait Implementations'
 impl !Send for Foo {}
 impl !Sync for Foo {}
+impl !std::marker::Unpin for Foo {}
+impl !std::panic::RefUnwindSafe for Foo {}
+impl !std::panic::UnwindSafe for Foo {}
diff --git a/src/test/rustdoc/issue-50159.rs b/src/test/rustdoc/issue-50159.rs
index d175c82..74502be 100644
--- a/src/test/rustdoc/issue-50159.rs
+++ b/src/test/rustdoc/issue-50159.rs
@@ -14,7 +14,7 @@
 // @has - '//code' 'impl<B> Send for Switch<B> where <B as Signal>::Item: Send'
 // @has - '//code' 'impl<B> Sync for Switch<B> where <B as Signal>::Item: Sync'
 // @count - '//*[@id="implementations-list"]/*[@class="impl"]' 0
-// @count - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]' 2
+// @count - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]' 5
 pub struct Switch<B: Signal> {
     pub inner: <B as Signal2>::Item2,
 }
diff --git a/src/test/rustdoc/synthetic_auto/basic.rs b/src/test/rustdoc/synthetic_auto/basic.rs
index d5f1269..38de531 100644
--- a/src/test/rustdoc/synthetic_auto/basic.rs
+++ b/src/test/rustdoc/synthetic_auto/basic.rs
@@ -2,7 +2,7 @@
 // @has - '//code' 'impl<T> Send for Foo<T> where T: Send'
 // @has - '//code' 'impl<T> Sync for Foo<T> where T: Sync'
 // @count - '//*[@id="implementations-list"]/*[@class="impl"]' 0
-// @count - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]' 2
+// @count - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]' 5
 pub struct Foo<T> {
     field: T,
 }
diff --git a/src/test/rustdoc/synthetic_auto/crate-local.rs b/src/test/rustdoc/synthetic_auto/crate-local.rs
new file mode 100644
index 0000000..341dd57
--- /dev/null
+++ b/src/test/rustdoc/synthetic_auto/crate-local.rs
@@ -0,0 +1,9 @@
+#![feature(optin_builtin_traits)]
+
+pub auto trait Banana {}
+
+// @has crate_local/struct.Peach.html
+// @has - '//code' 'impl Banana for Peach'
+// @has - '//code' 'impl Send for Peach'
+// @has - '//code' 'impl Sync for Peach'
+pub struct Peach;
diff --git a/src/test/rustdoc/synthetic_auto/manual.rs b/src/test/rustdoc/synthetic_auto/manual.rs
index 413ba18..4584034 100644
--- a/src/test/rustdoc/synthetic_auto/manual.rs
+++ b/src/test/rustdoc/synthetic_auto/manual.rs
@@ -6,7 +6,7 @@
 // 'impl<T> Send for Foo<T>'
 //
 // @count - '//*[@id="implementations-list"]/*[@class="impl"]' 1
-// @count - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]' 1
+// @count - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]' 4
 pub struct Foo<T> {
     field: T,
 }
diff --git a/src/test/ui/asm/asm-misplaced-option.rs b/src/test/ui/asm/asm-misplaced-option.rs
index 5c202a1..14ff4c2 100644
--- a/src/test/ui/asm/asm-misplaced-option.rs
+++ b/src/test/ui/asm/asm-misplaced-option.rs
@@ -1,3 +1,4 @@
+// check-pass
 // ignore-android
 // ignore-arm
 // ignore-aarch64
@@ -11,14 +12,11 @@
 // ignore-mips
 // ignore-mips64
 
-// compile-pass
-// skip-codegen
 #![feature(asm)]
-#![allow(dead_code, non_upper_case_globals)]
 
 #[cfg(any(target_arch = "x86",
           target_arch = "x86_64"))]
-pub fn main() {
+fn main() {
     // assignment not dead
     let mut x: isize = 0;
     unsafe {
diff --git a/src/test/ui/asm/asm-misplaced-option.stderr b/src/test/ui/asm/asm-misplaced-option.stderr
index 39d88e3..3d4b28c 100644
--- a/src/test/ui/asm/asm-misplaced-option.stderr
+++ b/src/test/ui/asm/asm-misplaced-option.stderr
@@ -1,11 +1,11 @@
 warning: unrecognized option
-  --> $DIR/asm-misplaced-option.rs:26:64
+  --> $DIR/asm-misplaced-option.rs:24:64
    |
 LL |         asm!("mov $1, $0" : "=r"(x) : "r"(5_usize), "0"(x) : : "cc");
    |                                                                ^^^^
 
 warning: expected a clobber, found an option
-  --> $DIR/asm-misplaced-option.rs:33:80
+  --> $DIR/asm-misplaced-option.rs:31:80
    |
 LL |         asm!("add $2, $1; mov $1, $0" : "=r"(x) : "r"(x), "r"(8_usize) : "cc", "volatile");
    |                                                                                ^^^^^^^^^^
diff --git a/src/test/ui/associated-types/cache/chrono-scan.rs b/src/test/ui/associated-types/cache/chrono-scan.rs
index 00d47c9..8ddd347 100644
--- a/src/test/ui/associated-types/cache/chrono-scan.rs
+++ b/src/test/ui/associated-types/cache/chrono-scan.rs
@@ -1,10 +1,10 @@
-// compile-pass
-// skip-codegen
-#![allow(warnings)]
+// check-pass
+
 pub type ParseResult<T> = Result<T, ()>;
 
-pub enum Item<'a> {     Literal(&'a str),
- }
+pub enum Item<'a> {
+    Literal(&'a str)
+}
 
 pub fn colon_or_space(s: &str) -> ParseResult<&str> {
     unimplemented!()
@@ -20,10 +20,9 @@
     macro_rules! try_consume {
         ($e:expr) => ({ let (s_, v) = try!($e); s = s_; v })
     }
-    let offset = try_consume!(timezone_offset_zulu(s.trim_left(), colon_or_space));
-    let offset = try_consume!(timezone_offset_zulu(s.trim_left(), colon_or_space));
+    let offset = try_consume!(timezone_offset_zulu(s.trim_start(), colon_or_space));
+    let offset = try_consume!(timezone_offset_zulu(s.trim_start(), colon_or_space));
     Ok(())
 }
 
-
-fn main() { }
+fn main() {}
diff --git a/src/test/ui/associated-types/cache/elision.rs b/src/test/ui/associated-types/cache/elision.rs
index 2b49cd3..b3e1ec8 100644
--- a/src/test/ui/associated-types/cache/elision.rs
+++ b/src/test/ui/associated-types/cache/elision.rs
@@ -1,10 +1,9 @@
-// compile-pass
-// skip-codegen
-#![allow(warnings)]
 // Check that you are allowed to implement using elision but write
 // trait without elision (a bug in this cropped up during
 // bootstrapping, so this is a regression test).
 
+// check-pass
+
 pub struct SplitWhitespace<'a> {
     x: &'a u8
 }
diff --git a/src/test/ui/bad/bad-lint-cap3.rs b/src/test/ui/bad/bad-lint-cap3.rs
index 4cfa0b2..c381058 100644
--- a/src/test/ui/bad/bad-lint-cap3.rs
+++ b/src/test/ui/bad/bad-lint-cap3.rs
@@ -1,10 +1,9 @@
+// check-pass
 // compile-flags: --cap-lints warn
 
 #![warn(unused)]
 #![deny(warnings)]
-// compile-pass
-// skip-codegen
-use std::option; //~ WARN
 
+use std::option; //~ WARN
 
 fn main() {}
diff --git a/src/test/ui/bad/bad-lint-cap3.stderr b/src/test/ui/bad/bad-lint-cap3.stderr
index a1ea3f7..b898d79 100644
--- a/src/test/ui/bad/bad-lint-cap3.stderr
+++ b/src/test/ui/bad/bad-lint-cap3.stderr
@@ -5,7 +5,7 @@
    |     ^^^^^^^^^^^
    |
 note: lint level defined here
-  --> $DIR/bad-lint-cap3.rs:4:9
+  --> $DIR/bad-lint-cap3.rs:5:9
    |
 LL | #![deny(warnings)]
    |         ^^^^^^^^
diff --git a/src/test/ui/coherence/coherence-projection-ok-orphan.rs b/src/test/ui/coherence/coherence-projection-ok-orphan.rs
index 652b438..b34c31d 100644
--- a/src/test/ui/coherence/coherence-projection-ok-orphan.rs
+++ b/src/test/ui/coherence/coherence-projection-ok-orphan.rs
@@ -1,11 +1,10 @@
-// compile-pass
-// skip-codegen
+// Here we do not get a coherence conflict because `Baz: Iterator`
+// does not hold and (due to the orphan rules), we can rely on that.
+
+// check-pass
 // revisions: old re
 
 #![cfg_attr(re, feature(re_rebalance_coherence))]
-#![allow(dead_code)]
-// Here we do not get a coherence conflict because `Baz: Iterator`
-// does not hold and (due to the orphan rules), we can rely on that.
 
 pub trait Foo<P> {}
 
@@ -18,5 +17,4 @@
 
 impl<A:Iterator> Foo<A::Item> for A { }
 
-
 fn main() {}
diff --git a/src/test/ui/coherence/coherence-projection-ok.rs b/src/test/ui/coherence/coherence-projection-ok.rs
index f759a9e..f4f5ca6 100644
--- a/src/test/ui/coherence/coherence-projection-ok.rs
+++ b/src/test/ui/coherence/coherence-projection-ok.rs
@@ -1,8 +1,8 @@
-// compile-pass
-// skip-codegen
+// check-pass
 // revisions: old re
 
 #![cfg_attr(re, feature(re_rebalance_coherence))]
+
 pub trait Foo<P> {}
 
 pub trait Bar {
@@ -17,5 +17,4 @@
     type Output = u32;
 }
 
-
 fn main() {}
diff --git a/src/test/ui/coherence/coherence_copy_like_err_fundamental_struct_ref.rs b/src/test/ui/coherence/coherence_copy_like_err_fundamental_struct_ref.rs
index bd8317e..0d67780 100644
--- a/src/test/ui/coherence/coherence_copy_like_err_fundamental_struct_ref.rs
+++ b/src/test/ui/coherence/coherence_copy_like_err_fundamental_struct_ref.rs
@@ -1,13 +1,11 @@
 // Test that we are able to introduce a negative constraint that
 // `MyType: !MyTrait` along with other "fundamental" wrappers.
 
+// check-pass
 // aux-build:coherence_copy_like_lib.rs
-// compile-pass
-// skip-codegen
 // revisions: old re
 
 #![cfg_attr(re, feature(re_rebalance_coherence))]
-#![allow(dead_code)]
 
 extern crate coherence_copy_like_lib as lib;
 
@@ -23,5 +21,4 @@
 // Huzzah.
 impl<'a> MyTrait for lib::MyFundamentalStruct<&'a MyType> { }
 
-
 fn main() { }
diff --git a/src/test/ui/coherence/coherence_local.rs b/src/test/ui/coherence/coherence_local.rs
index cac45b0..3eab6e0 100644
--- a/src/test/ui/coherence/coherence_local.rs
+++ b/src/test/ui/coherence/coherence_local.rs
@@ -1,13 +1,11 @@
 // Test that we are able to introduce a negative constraint that
 // `MyType: !MyTrait` along with other "fundamental" wrappers.
 
+// check-pass
 // aux-build:coherence_copy_like_lib.rs
-// compile-pass
-// skip-codegen
 // revisions: old re
 
 #![cfg_attr(re, feature(re_rebalance_coherence))]
-#![allow(dead_code)]
 
 extern crate coherence_copy_like_lib as lib;
 
@@ -22,5 +20,4 @@
 impl lib::MyCopy for lib::MyFundamentalStruct<MyType> { }
 impl lib::MyCopy for lib::MyFundamentalStruct<Box<MyType>> { }
 
-
-fn main() { }
+fn main() {}
diff --git a/src/test/ui/coherence/coherence_local_ref.rs b/src/test/ui/coherence/coherence_local_ref.rs
index a52510b..dff684c 100644
--- a/src/test/ui/coherence/coherence_local_ref.rs
+++ b/src/test/ui/coherence/coherence_local_ref.rs
@@ -1,13 +1,11 @@
 // Test that we are able to introduce a negative constraint that
 // `MyType: !MyTrait` along with other "fundamental" wrappers.
 
+// check-pass
 // aux-build:coherence_copy_like_lib.rs
-// compile-pass
-// skip-codegen
 // revisions: old re
 
 #![cfg_attr(re, feature(re_rebalance_coherence))]
-#![allow(dead_code)]
 
 extern crate coherence_copy_like_lib as lib;
 
@@ -16,5 +14,4 @@
 // naturally, legal
 impl lib::MyCopy for MyType { }
 
-
 fn main() { }
diff --git a/src/test/ui/conditional-compilation/cfg-attr-empty-is-unused.rs b/src/test/ui/conditional-compilation/cfg-attr-empty-is-unused.rs
new file mode 100644
index 0000000..4c96d6e
--- /dev/null
+++ b/src/test/ui/conditional-compilation/cfg-attr-empty-is-unused.rs
@@ -0,0 +1,13 @@
+// Check that `#[cfg_attr($PREDICATE,)]` triggers the `unused_attribute` lint.
+
+// compile-flags: --cfg TRUE
+
+#![deny(unused)]
+
+#[cfg_attr(FALSE,)] //~ ERROR unused attribute
+fn _f() {}
+
+#[cfg_attr(TRUE,)] //~ ERROR unused attribute
+fn _g() {}
+
+fn main() {}
diff --git a/src/test/ui/conditional-compilation/cfg-attr-empty-is-unused.stderr b/src/test/ui/conditional-compilation/cfg-attr-empty-is-unused.stderr
new file mode 100644
index 0000000..cd3563e
--- /dev/null
+++ b/src/test/ui/conditional-compilation/cfg-attr-empty-is-unused.stderr
@@ -0,0 +1,21 @@
+error: unused attribute
+  --> $DIR/cfg-attr-empty-is-unused.rs:7:1
+   |
+LL | #[cfg_attr(FALSE,)]
+   | ^^^^^^^^^^^^^^^^^^^
+   |
+note: lint level defined here
+  --> $DIR/cfg-attr-empty-is-unused.rs:5:9
+   |
+LL | #![deny(unused)]
+   |         ^^^^^^
+   = note: #[deny(unused_attributes)] implied by #[deny(unused)]
+
+error: unused attribute
+  --> $DIR/cfg-attr-empty-is-unused.rs:10:1
+   |
+LL | #[cfg_attr(TRUE,)]
+   | ^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/conditional-compilation/cfg_attr_path.rs b/src/test/ui/conditional-compilation/cfg_attr_path.rs
index 9e9ff66..efb718b 100644
--- a/src/test/ui/conditional-compilation/cfg_attr_path.rs
+++ b/src/test/ui/conditional-compilation/cfg_attr_path.rs
@@ -1,13 +1,12 @@
-// compile-pass
-// skip-codegen
-#![allow(dead_code)]
+// check-pass
+
 #![deny(unused_attributes)] // c.f #35584
+
 mod auxiliary {
     #[cfg_attr(any(), path = "nonexistent_file.rs")] pub mod namespaced_enums;
     #[cfg_attr(all(), path = "namespaced_enums.rs")] pub mod nonexistent_file;
 }
 
-
 fn main() {
     let _ = auxiliary::namespaced_enums::Foo::A;
     let _ = auxiliary::nonexistent_file::Foo::A;
diff --git a/src/test/ui/consts/const-eval/const_transmute.rs b/src/test/ui/consts/const-eval/const_transmute.rs
index 4726f9d..0e0e003 100644
--- a/src/test/ui/consts/const-eval/const_transmute.rs
+++ b/src/test/ui/consts/const-eval/const_transmute.rs
@@ -1,4 +1,3 @@
-// compile-pass
 // run-pass
 
 #![feature(const_fn_union)]
diff --git a/src/test/ui/consts/const-eval/enum_discr.rs b/src/test/ui/consts/const-eval/enum_discr.rs
index 4851e75..e09258f 100644
--- a/src/test/ui/consts/const-eval/enum_discr.rs
+++ b/src/test/ui/consts/const-eval/enum_discr.rs
@@ -1,4 +1,3 @@
-// compile-pass
 // run-pass
 
 enum Foo {
diff --git a/src/test/ui/consts/const-fn-stability-calls-3.rs b/src/test/ui/consts/const-fn-stability-calls-3.rs
index 44bcca8..b831dee 100644
--- a/src/test/ui/consts/const-fn-stability-calls-3.rs
+++ b/src/test/ui/consts/const-fn-stability-calls-3.rs
@@ -1,15 +1,12 @@
 // Test use of const fn from another crate without a feature gate.
 
-// compile-pass
-// skip-codegen
-#![allow(unused_variables)]
+// check-pass
 // aux-build:const_fn_lib.rs
 
 extern crate const_fn_lib;
 
 use const_fn_lib::foo;
 
-
 fn main() {
     let x = foo(); // use outside a constant is ok
 }
diff --git a/src/test/ui/derives/deriving-meta-empty-trait-list.rs b/src/test/ui/derives/deriving-meta-empty-trait-list.rs
index 8824841..4f2e31e 100644
--- a/src/test/ui/derives/deriving-meta-empty-trait-list.rs
+++ b/src/test/ui/derives/deriving-meta-empty-trait-list.rs
@@ -1,6 +1,6 @@
-// compile-pass
+#![deny(unused)]
 
-#[derive()] //~ WARNING empty trait list in `derive`
-struct Bar;
+#[derive()] //~ ERROR unused attribute
+struct _Bar;
 
 pub fn main() {}
diff --git a/src/test/ui/derives/deriving-meta-empty-trait-list.stderr b/src/test/ui/derives/deriving-meta-empty-trait-list.stderr
index f8414b6..95c94ded 100644
--- a/src/test/ui/derives/deriving-meta-empty-trait-list.stderr
+++ b/src/test/ui/derives/deriving-meta-empty-trait-list.stderr
@@ -1,6 +1,15 @@
-warning: empty trait list in `derive`
+error: unused attribute
   --> $DIR/deriving-meta-empty-trait-list.rs:3:1
    |
 LL | #[derive()]
    | ^^^^^^^^^^^
+   |
+note: lint level defined here
+  --> $DIR/deriving-meta-empty-trait-list.rs:1:9
+   |
+LL | #![deny(unused)]
+   |         ^^^^^^
+   = note: #[deny(unused_attributes)] implied by #[deny(unused)]
+
+error: aborting due to previous error
 
diff --git a/src/test/ui/expanded-cfg.rs b/src/test/ui/expanded-cfg.rs
index c98fd7f..baa161a 100644
--- a/src/test/ui/expanded-cfg.rs
+++ b/src/test/ui/expanded-cfg.rs
@@ -1,5 +1,4 @@
-// skip-codegen
-// compile-pass
+// check-pass
 
 macro_rules! mac {
     {} => {
@@ -19,5 +18,4 @@
 
 mac! {}
 
-
 fn main() {}
diff --git a/src/test/ui/feature-gate/issue-43106-gating-of-builtin-attrs.rs b/src/test/ui/feature-gate/issue-43106-gating-of-builtin-attrs.rs
index ca0a432..3187b4c 100644
--- a/src/test/ui/feature-gate/issue-43106-gating-of-builtin-attrs.rs
+++ b/src/test/ui/feature-gate/issue-43106-gating-of-builtin-attrs.rs
@@ -30,8 +30,9 @@
 // inputs are handled by each, and (2.) to ease searching for related
 // occurrences in the source text.
 
+// check-pass
+
 #![warn(unused_attributes, unknown_lints)]
-#![allow(stable_features)]
 
 // UNGATED WHITE-LISTED BUILT-IN ATTRIBUTES
 
@@ -75,7 +76,7 @@
 // see issue-43106-gating-of-stable.rs
 // see issue-43106-gating-of-unstable.rs
 // see issue-43106-gating-of-deprecated.rs
-#![windows_subsystem = "1000"]
+#![windows_subsystem = "windows"]
 
 // UNGATED CRATE-LEVEL BUILT-IN ATTRIBUTES
 
@@ -539,7 +540,7 @@
     #[export_name = "2200"] impl S { }
 }
 
-// Note that this test has a `skip-codegen`, so it
+// Note that this is a `check-pass` test, so it
 // will never invoke the linker. These are here nonetheless to point
 // out that we allow them at non-crate-level (though I do not know
 // whether they have the same effect here as at crate-level).
@@ -611,17 +612,17 @@
     #[must_use] impl S { }
 }
 
-#[windows_subsystem = "1000"]
+#[windows_subsystem = "windows"]
 mod windows_subsystem {
-    mod inner { #![windows_subsystem="1000"] }
+    mod inner { #![windows_subsystem="windows"] }
 
-    #[windows_subsystem = "1000"] fn f() { }
+    #[windows_subsystem = "windows"] fn f() { }
 
-    #[windows_subsystem = "1000"] struct S;
+    #[windows_subsystem = "windows"] struct S;
 
-    #[windows_subsystem = "1000"] type T = S;
+    #[windows_subsystem = "windows"] type T = S;
 
-    #[windows_subsystem = "1000"] impl S { }
+    #[windows_subsystem = "windows"] impl S { }
 }
 
 // BROKEN USES OF CRATE-LEVEL BUILT-IN ATTRIBUTES
diff --git a/src/test/ui/feature-gate/issue-43106-gating-of-builtin-attrs.stderr b/src/test/ui/feature-gate/issue-43106-gating-of-builtin-attrs.stderr
index c708120..e03b712 100644
--- a/src/test/ui/feature-gate/issue-43106-gating-of-builtin-attrs.stderr
+++ b/src/test/ui/feature-gate/issue-43106-gating-of-builtin-attrs.stderr
@@ -1,1182 +1,1186 @@
 warning: unknown lint: `x5400`
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:38:9
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:39:9
    |
 LL | #![warn(x5400)]
    |         ^^^^^
    |
 note: lint level defined here
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:33:28
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:35:28
    |
 LL | #![warn(unused_attributes, unknown_lints)]
    |                            ^^^^^^^^^^^^^
 
 warning: unknown lint: `x5300`
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:39:10
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:40:10
    |
 LL | #![allow(x5300)]
    |          ^^^^^
 
 warning: unknown lint: `x5200`
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:40:11
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:41:11
    |
 LL | #![forbid(x5200)]
    |           ^^^^^
 
 warning: unknown lint: `x5100`
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:41:9
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:42:9
    |
 LL | #![deny(x5100)]
    |         ^^^^^
 
 warning: unknown lint: `x5400`
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:99:8
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:100:8
    |
 LL | #[warn(x5400)]
    |        ^^^^^
 
 warning: unknown lint: `x5400`
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:102:25
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:103:25
    |
 LL |     mod inner { #![warn(x5400)] }
    |                         ^^^^^
 
 warning: unknown lint: `x5400`
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:105:12
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:106:12
    |
 LL |     #[warn(x5400)] fn f() { }
    |            ^^^^^
 
 warning: unknown lint: `x5400`
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:108:12
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:109:12
    |
 LL |     #[warn(x5400)] struct S;
    |            ^^^^^
 
 warning: unknown lint: `x5400`
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:111:12
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:112:12
    |
 LL |     #[warn(x5400)] type T = S;
    |            ^^^^^
 
 warning: unknown lint: `x5400`
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:114:12
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:115:12
    |
 LL |     #[warn(x5400)] impl S { }
    |            ^^^^^
 
 warning: unknown lint: `x5300`
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:118:9
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:119:9
    |
 LL | #[allow(x5300)]
    |         ^^^^^
 
 warning: unknown lint: `x5300`
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:121:26
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:122:26
    |
 LL |     mod inner { #![allow(x5300)] }
    |                          ^^^^^
 
 warning: unknown lint: `x5300`
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:124:13
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:125:13
    |
 LL |     #[allow(x5300)] fn f() { }
    |             ^^^^^
 
 warning: unknown lint: `x5300`
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:127:13
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:128:13
    |
 LL |     #[allow(x5300)] struct S;
    |             ^^^^^
 
 warning: unknown lint: `x5300`
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:130:13
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:131:13
    |
 LL |     #[allow(x5300)] type T = S;
    |             ^^^^^
 
 warning: unknown lint: `x5300`
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:133:13
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:134:13
    |
 LL |     #[allow(x5300)] impl S { }
    |             ^^^^^
 
 warning: unknown lint: `x5200`
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:137:10
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:138:10
    |
 LL | #[forbid(x5200)]
    |          ^^^^^
 
 warning: unknown lint: `x5200`
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:140:27
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:141:27
    |
 LL |     mod inner { #![forbid(x5200)] }
    |                           ^^^^^
 
 warning: unknown lint: `x5200`
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:143:14
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:144:14
    |
 LL |     #[forbid(x5200)] fn f() { }
    |              ^^^^^
 
 warning: unknown lint: `x5200`
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:146:14
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:147:14
    |
 LL |     #[forbid(x5200)] struct S;
    |              ^^^^^
 
 warning: unknown lint: `x5200`
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:149:14
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:150:14
    |
 LL |     #[forbid(x5200)] type T = S;
    |              ^^^^^
 
 warning: unknown lint: `x5200`
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:152:14
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:153:14
    |
 LL |     #[forbid(x5200)] impl S { }
    |              ^^^^^
 
 warning: unknown lint: `x5100`
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:156:8
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:157:8
    |
 LL | #[deny(x5100)]
    |        ^^^^^
 
 warning: unknown lint: `x5100`
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:159:25
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:160:25
    |
 LL |     mod inner { #![deny(x5100)] }
    |                         ^^^^^
 
 warning: unknown lint: `x5100`
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:162:12
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:163:12
    |
 LL |     #[deny(x5100)] fn f() { }
    |            ^^^^^
 
 warning: unknown lint: `x5100`
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:165:12
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:166:12
    |
 LL |     #[deny(x5100)] struct S;
    |            ^^^^^
 
 warning: unknown lint: `x5100`
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:168:12
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:169:12
    |
 LL |     #[deny(x5100)] type T = S;
    |            ^^^^^
 
 warning: unknown lint: `x5100`
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:171:12
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:172:12
    |
 LL |     #[deny(x5100)] impl S { }
    |            ^^^^^
 
 warning: macro_escape is a deprecated synonym for macro_use
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:455:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:456:1
    |
 LL | #[macro_escape]
    | ^^^^^^^^^^^^^^^
 
 warning: macro_escape is a deprecated synonym for macro_use
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:458:17
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:459:17
    |
 LL |     mod inner { #![macro_escape] }
    |                 ^^^^^^^^^^^^^^^^
    |
    = help: consider an outer attribute, #[macro_use] mod ...
 
+warning: the feature `rust1` has been stable since 1.0.0 and no longer requires an attribute to enable
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:89:12
+   |
+LL | #![feature(rust1)]
+   |            ^^^^^
+   |
+   = note: #[warn(stable_features)] on by default
+
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:179:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:180:5
    |
 LL |     #[macro_use] fn f() { }
    |     ^^^^^^^^^^^^
    |
 note: lint level defined here
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:33:9
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:35:9
    |
 LL | #![warn(unused_attributes, unknown_lints)]
    |         ^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:182:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:183:5
    |
 LL |     #[macro_use] struct S;
    |     ^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:185:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:186:5
    |
 LL |     #[macro_use] type T = S;
    |     ^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:188:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:189:5
    |
 LL |     #[macro_use] impl S { }
    |     ^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:195:17
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:196:17
    |
 LL |     mod inner { #![macro_export] }
    |                 ^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:198:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:199:5
    |
 LL |     #[macro_export] fn f() { }
    |     ^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:201:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:202:5
    |
 LL |     #[macro_export] struct S;
    |     ^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:204:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:205:5
    |
 LL |     #[macro_export] type T = S;
    |     ^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:207:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:208:5
    |
 LL |     #[macro_export] impl S { }
    |     ^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:192:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:193:1
    |
 LL | #[macro_export]
    | ^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:214:17
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:215:17
    |
 LL |     mod inner { #![plugin_registrar] }
    |                 ^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:219:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:220:5
    |
 LL |     #[plugin_registrar] struct S;
    |     ^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:222:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:223:5
    |
 LL |     #[plugin_registrar] type T = S;
    |     ^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:225:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:226:5
    |
 LL |     #[plugin_registrar] impl S { }
    |     ^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:211:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:212:1
    |
 LL | #[plugin_registrar]
    | ^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:232:17
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:233:17
    |
 LL |     mod inner { #![main] }
    |                 ^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:237:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:238:5
    |
 LL |     #[main] struct S;
    |     ^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:240:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:241:5
    |
 LL |     #[main] type T = S;
    |     ^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:243:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:244:5
    |
 LL |     #[main] impl S { }
    |     ^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:229:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:230:1
    |
 LL | #[main]
    | ^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:250:17
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:251:17
    |
 LL |     mod inner { #![start] }
    |                 ^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:255:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:256:5
    |
 LL |     #[start] struct S;
    |     ^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:258:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:259:5
    |
 LL |     #[start] type T = S;
    |     ^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:261:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:262:5
    |
 LL |     #[start] impl S { }
    |     ^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:247:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:248:1
    |
 LL | #[start]
    | ^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:314:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:315:5
    |
 LL |     #[path = "3800"] fn f() { }
    |     ^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:317:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:318:5
    |
 LL |     #[path = "3800"]  struct S;
    |     ^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:320:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:321:5
    |
 LL |     #[path = "3800"] type T = S;
    |     ^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:323:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:324:5
    |
 LL |     #[path = "3800"] impl S { }
    |     ^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:330:17
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:331:17
    |
 LL |     mod inner { #![automatically_derived] }
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:333:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:334:5
    |
 LL |     #[automatically_derived] fn f() { }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:336:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:337:5
    |
 LL |     #[automatically_derived] struct S;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:339:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:340:5
    |
 LL |     #[automatically_derived] type T = S;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:342:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:343:5
    |
 LL |     #[automatically_derived] impl S { }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:327:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:328:1
    |
 LL | #[automatically_derived]
    | ^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:362:17
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:363:17
    |
 LL |     mod inner { #![no_link] }
    |                 ^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:365:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:366:5
    |
 LL |     #[no_link] fn f() { }
    |     ^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:368:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:369:5
    |
 LL |     #[no_link] struct S;
    |     ^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:371:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:372:5
    |
 LL |     #[no_link]type T = S;
    |     ^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:374:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:375:5
    |
 LL |     #[no_link] impl S { }
    |     ^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:359:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:360:1
    |
 LL | #[no_link]
    | ^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:381:17
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:382:17
    |
 LL |     mod inner { #![should_panic] }
    |                 ^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:384:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:385:5
    |
 LL |     #[should_panic] fn f() { }
    |     ^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:387:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:388:5
    |
 LL |     #[should_panic] struct S;
    |     ^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:390:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:391:5
    |
 LL |     #[should_panic] type T = S;
    |     ^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:393:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:394:5
    |
 LL |     #[should_panic] impl S { }
    |     ^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:378:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:379:1
    |
 LL | #[should_panic]
    | ^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:400:17
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:401:17
    |
 LL |     mod inner { #![ignore] }
    |                 ^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:403:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:404:5
    |
 LL |     #[ignore] fn f() { }
    |     ^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:406:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:407:5
    |
 LL |     #[ignore] struct S;
    |     ^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:409:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:410:5
    |
 LL |     #[ignore] type T = S;
    |     ^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:412:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:413:5
    |
 LL |     #[ignore] impl S { }
    |     ^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:397:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:398:1
    |
 LL | #[ignore]
    | ^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:419:17
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:420:17
    |
 LL |     mod inner { #![no_implicit_prelude] }
    |                 ^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:422:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:423:5
    |
 LL |     #[no_implicit_prelude] fn f() { }
    |     ^^^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:425:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:426:5
    |
 LL |     #[no_implicit_prelude] struct S;
    |     ^^^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:428:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:429:5
    |
 LL |     #[no_implicit_prelude] type T = S;
    |     ^^^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:431:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:432:5
    |
 LL |     #[no_implicit_prelude] impl S { }
    |     ^^^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:416:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:417:1
    |
 LL | #[no_implicit_prelude]
    | ^^^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:438:17
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:439:17
    |
 LL |     mod inner { #![reexport_test_harness_main="2900"] }
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:441:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:442:5
    |
 LL |     #[reexport_test_harness_main = "2900"] fn f() { }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:444:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:445:5
    |
 LL |     #[reexport_test_harness_main = "2900"] struct S;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:447:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:448:5
    |
 LL |     #[reexport_test_harness_main = "2900"] type T = S;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:450:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:451:5
    |
 LL |     #[reexport_test_harness_main = "2900"] impl S { }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:435:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:436:1
    |
 LL | #[reexport_test_harness_main = "2900"]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:461:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:462:5
    |
 LL |     #[macro_escape] fn f() { }
    |     ^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:464:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:465:5
    |
 LL |     #[macro_escape] struct S;
    |     ^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:467:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:468:5
    |
 LL |     #[macro_escape] type T = S;
    |     ^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:470:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:471:5
    |
 LL |     #[macro_escape] impl S { }
    |     ^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:478:17
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:479:17
    |
 LL |     mod inner { #![no_std] }
    |                 ^^^^^^^^^^
 
 warning: crate-level attribute should be in the root module
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:478:17
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:479:17
    |
 LL |     mod inner { #![no_std] }
    |                 ^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:482:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:483:5
    |
 LL |     #[no_std] fn f() { }
    |     ^^^^^^^^^
 
 warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo]
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:482:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:483:5
    |
 LL |     #[no_std] fn f() { }
    |     ^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:486:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:487:5
    |
 LL |     #[no_std] struct S;
    |     ^^^^^^^^^
 
 warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo]
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:486:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:487:5
    |
 LL |     #[no_std] struct S;
    |     ^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:490:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:491:5
    |
 LL |     #[no_std] type T = S;
    |     ^^^^^^^^^
 
 warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo]
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:490:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:491:5
    |
 LL |     #[no_std] type T = S;
    |     ^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:494:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:495:5
    |
 LL |     #[no_std] impl S { }
    |     ^^^^^^^^^
 
 warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo]
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:494:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:495:5
    |
 LL |     #[no_std] impl S { }
    |     ^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:474:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:475:1
    |
 LL | #[no_std]
    | ^^^^^^^^^
 
 warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo]
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:474:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:475:1
    |
 LL | #[no_std]
    | ^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:633:17
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:634:17
    |
 LL |     mod inner { #![crate_name="0900"] }
    |                 ^^^^^^^^^^^^^^^^^^^^^
 
 warning: crate-level attribute should be in the root module
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:633:17
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:634:17
    |
 LL |     mod inner { #![crate_name="0900"] }
    |                 ^^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:637:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:638:5
    |
 LL |     #[crate_name = "0900"] fn f() { }
    |     ^^^^^^^^^^^^^^^^^^^^^^
 
 warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo]
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:637:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:638:5
    |
 LL |     #[crate_name = "0900"] fn f() { }
    |     ^^^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:641:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:642:5
    |
 LL |     #[crate_name = "0900"] struct S;
    |     ^^^^^^^^^^^^^^^^^^^^^^
 
 warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo]
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:641:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:642:5
    |
 LL |     #[crate_name = "0900"] struct S;
    |     ^^^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:645:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:646:5
    |
 LL |     #[crate_name = "0900"] type T = S;
    |     ^^^^^^^^^^^^^^^^^^^^^^
 
 warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo]
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:645:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:646:5
    |
 LL |     #[crate_name = "0900"] type T = S;
    |     ^^^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:649:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:650:5
    |
 LL |     #[crate_name = "0900"] impl S { }
    |     ^^^^^^^^^^^^^^^^^^^^^^
 
 warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo]
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:649:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:650:5
    |
 LL |     #[crate_name = "0900"] impl S { }
    |     ^^^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:629:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:630:1
    |
 LL | #[crate_name = "0900"]
    | ^^^^^^^^^^^^^^^^^^^^^^
 
 warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo]
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:629:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:630:1
    |
 LL | #[crate_name = "0900"]
    | ^^^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:658:17
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:659:17
    |
 LL |     mod inner { #![crate_type="0800"] }
    |                 ^^^^^^^^^^^^^^^^^^^^^
 
 warning: crate-level attribute should be in the root module
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:658:17
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:659:17
    |
 LL |     mod inner { #![crate_type="0800"] }
    |                 ^^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:662:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:663:5
    |
 LL |     #[crate_type = "0800"] fn f() { }
    |     ^^^^^^^^^^^^^^^^^^^^^^
 
 warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo]
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:662:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:663:5
    |
 LL |     #[crate_type = "0800"] fn f() { }
    |     ^^^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:666:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:667:5
    |
 LL |     #[crate_type = "0800"] struct S;
    |     ^^^^^^^^^^^^^^^^^^^^^^
 
 warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo]
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:666:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:667:5
    |
 LL |     #[crate_type = "0800"] struct S;
    |     ^^^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:670:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:671:5
    |
 LL |     #[crate_type = "0800"] type T = S;
    |     ^^^^^^^^^^^^^^^^^^^^^^
 
 warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo]
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:670:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:671:5
    |
 LL |     #[crate_type = "0800"] type T = S;
    |     ^^^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:674:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:675:5
    |
 LL |     #[crate_type = "0800"] impl S { }
    |     ^^^^^^^^^^^^^^^^^^^^^^
 
 warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo]
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:674:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:675:5
    |
 LL |     #[crate_type = "0800"] impl S { }
    |     ^^^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:654:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:655:1
    |
 LL | #[crate_type = "0800"]
    | ^^^^^^^^^^^^^^^^^^^^^^
 
 warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo]
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:654:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:655:1
    |
 LL | #[crate_type = "0800"]
    | ^^^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:683:17
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:684:17
    |
 LL |     mod inner { #![feature(x0600)] }
    |                 ^^^^^^^^^^^^^^^^^^
 
 warning: crate-level attribute should be in the root module
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:683:17
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:684:17
    |
 LL |     mod inner { #![feature(x0600)] }
    |                 ^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:687:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:688:5
    |
 LL |     #[feature(x0600)] fn f() { }
    |     ^^^^^^^^^^^^^^^^^
 
 warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo]
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:687:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:688:5
    |
 LL |     #[feature(x0600)] fn f() { }
    |     ^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:691:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:692:5
    |
 LL |     #[feature(x0600)] struct S;
    |     ^^^^^^^^^^^^^^^^^
 
 warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo]
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:691:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:692:5
    |
 LL |     #[feature(x0600)] struct S;
    |     ^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:695:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:696:5
    |
 LL |     #[feature(x0600)] type T = S;
    |     ^^^^^^^^^^^^^^^^^
 
 warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo]
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:695:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:696:5
    |
 LL |     #[feature(x0600)] type T = S;
    |     ^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:699:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:700:5
    |
 LL |     #[feature(x0600)] impl S { }
    |     ^^^^^^^^^^^^^^^^^
 
 warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo]
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:699:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:700:5
    |
 LL |     #[feature(x0600)] impl S { }
    |     ^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:679:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:680:1
    |
 LL | #[feature(x0600)]
    | ^^^^^^^^^^^^^^^^^
 
 warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo]
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:679:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:680:1
    |
 LL | #[feature(x0600)]
    | ^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:709:17
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:710:17
    |
 LL |     mod inner { #![no_main] }
    |                 ^^^^^^^^^^^
 
 warning: crate-level attribute should be in the root module
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:709:17
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:710:17
    |
 LL |     mod inner { #![no_main] }
    |                 ^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:713:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:714:5
    |
 LL |     #[no_main] fn f() { }
    |     ^^^^^^^^^^
 
 warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo]
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:713:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:714:5
    |
 LL |     #[no_main] fn f() { }
    |     ^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:717:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:718:5
    |
 LL |     #[no_main] struct S;
    |     ^^^^^^^^^^
 
 warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo]
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:717:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:718:5
    |
 LL |     #[no_main] struct S;
    |     ^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:721:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:722:5
    |
 LL |     #[no_main] type T = S;
    |     ^^^^^^^^^^
 
 warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo]
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:721:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:722:5
    |
 LL |     #[no_main] type T = S;
    |     ^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:725:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:726:5
    |
 LL |     #[no_main] impl S { }
    |     ^^^^^^^^^^
 
 warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo]
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:725:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:726:5
    |
 LL |     #[no_main] impl S { }
    |     ^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:705:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:706:1
    |
 LL | #[no_main]
    | ^^^^^^^^^^
 
 warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo]
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:705:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:706:1
    |
 LL | #[no_main]
    | ^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:747:17
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:748:17
    |
 LL |     mod inner { #![recursion_limit="0200"] }
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: crate-level attribute should be in the root module
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:747:17
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:748:17
    |
 LL |     mod inner { #![recursion_limit="0200"] }
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:751:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:752:5
    |
 LL |     #[recursion_limit="0200"] fn f() { }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo]
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:751:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:752:5
    |
 LL |     #[recursion_limit="0200"] fn f() { }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:755:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:756:5
    |
 LL |     #[recursion_limit="0200"] struct S;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo]
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:755:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:756:5
    |
 LL |     #[recursion_limit="0200"] struct S;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:759:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:760:5
    |
 LL |     #[recursion_limit="0200"] type T = S;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo]
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:759:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:760:5
    |
 LL |     #[recursion_limit="0200"] type T = S;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:763:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:764:5
    |
 LL |     #[recursion_limit="0200"] impl S { }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo]
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:763:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:764:5
    |
 LL |     #[recursion_limit="0200"] impl S { }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:743:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:744:1
    |
 LL | #[recursion_limit="0200"]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo]
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:743:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:744:1
    |
 LL | #[recursion_limit="0200"]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:772:17
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:773:17
    |
 LL |     mod inner { #![type_length_limit="0100"] }
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: crate-level attribute should be in the root module
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:772:17
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:773:17
    |
 LL |     mod inner { #![type_length_limit="0100"] }
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:776:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:777:5
    |
 LL |     #[type_length_limit="0100"] fn f() { }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo]
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:776:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:777:5
    |
 LL |     #[type_length_limit="0100"] fn f() { }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:780:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:781:5
    |
 LL |     #[type_length_limit="0100"] struct S;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo]
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:780:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:781:5
    |
 LL |     #[type_length_limit="0100"] struct S;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:784:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:785:5
    |
 LL |     #[type_length_limit="0100"] type T = S;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo]
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:784:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:785:5
    |
 LL |     #[type_length_limit="0100"] type T = S;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:788:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:789:5
    |
 LL |     #[type_length_limit="0100"] impl S { }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo]
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:788:5
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:789:5
    |
 LL |     #[type_length_limit="0100"] impl S { }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:768:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:769:1
    |
 LL | #[type_length_limit="0100"]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo]
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:768:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:769:1
    |
 LL | #[type_length_limit="0100"]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:43:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:44:1
    |
 LL | #![macro_export]
    | ^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:44:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:45:1
    |
 LL | #![plugin_registrar]
    | ^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:47:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:48:1
    |
 LL | #![main]
    | ^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:48:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:49:1
    |
 LL | #![start]
    | ^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:51:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:52:1
    |
 LL | #![repr()]
    | ^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:53:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:54:1
    |
 LL | #![path = "3800"]
    | ^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:54:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:55:1
    |
 LL | #![automatically_derived]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:56:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:57:1
    |
 LL | #![no_link]
    | ^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:58:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:59:1
    |
 LL | #![should_panic]
    | ^^^^^^^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:59:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:60:1
    |
 LL | #![ignore]
    | ^^^^^^^^^^
 
 warning: unused attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:65:1
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:66:1
    |
 LL | #![proc_macro_derive()]
    | ^^^^^^^^^^^^^^^^^^^^^^^
 
-error: invalid windows subsystem `1000`, only `windows` and `console` are allowed
-
-error: aborting due to previous error
-
diff --git a/src/test/ui/feature-gate/issue-43106-gating-of-deprecated.rs b/src/test/ui/feature-gate/issue-43106-gating-of-deprecated.rs
index 360d570..5e1d08d 100644
--- a/src/test/ui/feature-gate/issue-43106-gating-of-deprecated.rs
+++ b/src/test/ui/feature-gate/issue-43106-gating-of-deprecated.rs
@@ -5,8 +5,7 @@
 //
 // (For non-crate-level cases, see issue-43106-gating-of-builtin-attrs.rs)
 
-// compile-pass
-// skip-codegen
+// check-pass
 
 #![deprecated]
 
diff --git a/src/test/ui/glob-cycles.rs b/src/test/ui/glob-cycles.rs
index 84f9e5d..f354cc8 100644
--- a/src/test/ui/glob-cycles.rs
+++ b/src/test/ui/glob-cycles.rs
@@ -1,5 +1,5 @@
-// compile-pass
-// skip-codegen
+// check-pass
+
 mod foo {
     pub use bar::*;
     pub use main as f;
@@ -15,5 +15,4 @@
     pub use super::*;
 }
 
-
 pub fn main() {}
diff --git a/src/test/ui/hygiene/assoc_ty_bindings.rs b/src/test/ui/hygiene/assoc_ty_bindings.rs
index eb0ca6a..93cf759 100644
--- a/src/test/ui/hygiene/assoc_ty_bindings.rs
+++ b/src/test/ui/hygiene/assoc_ty_bindings.rs
@@ -1,8 +1,8 @@
+// check-pass
 // ignore-pretty pretty-printing is unhygienic
 
 #![feature(decl_macro, associated_type_defaults)]
-// compile-pass
-// skip-codegen
+
 trait Base {
     type AssocTy;
     fn f();
@@ -35,5 +35,4 @@
 
 mac!();
 
-
 fn main() {}
diff --git a/src/test/ui/if/if-loop.rs b/src/test/ui/if/if-loop.rs
index c799df2..06d0bdf 100644
--- a/src/test/ui/if/if-loop.rs
+++ b/src/test/ui/if/if-loop.rs
@@ -1,10 +1,8 @@
-// compile-pass
-// skip-codegen
-#![allow(warnings)]
+// check-pass
+
 // This used to ICE because the "if" being unreachable was not handled correctly
 fn err() {
     if loop {} {}
 }
 
-
 fn main() {}
diff --git a/src/test/ui/imports/import-crate-var.rs b/src/test/ui/imports/import-crate-var.rs
index ab26050..b9d146d 100644
--- a/src/test/ui/imports/import-crate-var.rs
+++ b/src/test/ui/imports/import-crate-var.rs
@@ -1,10 +1,8 @@
+// check-pass
 // aux-build:import_crate_var.rs
 
-// compile-pass
-// skip-codegen
 #[macro_use] extern crate import_crate_var;
 
-
 fn main() {
     m!();
     //~^ WARN `$crate` may not be imported
diff --git a/src/test/ui/imports/import-crate-var.stderr b/src/test/ui/imports/import-crate-var.stderr
index 4c358a8..2f8c845 100644
--- a/src/test/ui/imports/import-crate-var.stderr
+++ b/src/test/ui/imports/import-crate-var.stderr
@@ -1,5 +1,5 @@
 warning: `$crate` may not be imported
-  --> $DIR/import-crate-var.rs:9:5
+  --> $DIR/import-crate-var.rs:7:5
    |
 LL |     m!();
    |     ^^^^^
diff --git a/src/test/ui/issues/issue-11740.rs b/src/test/ui/issues/issue-11740.rs
index 47bdf08..dc10a20 100644
--- a/src/test/ui/issues/issue-11740.rs
+++ b/src/test/ui/issues/issue-11740.rs
@@ -1,6 +1,5 @@
-// compile-pass
-// skip-codegen
-#![allow(warnings)]
+// check-pass
+
 struct Attr {
     name: String,
     value: String,
@@ -21,7 +20,6 @@
     }
 }
 
-
 fn main() {
     let element = Element { attrs: Vec::new() };
     let _ = unsafe { element.get_attr("foo") };
diff --git a/src/test/ui/issues/issue-16994.rs b/src/test/ui/issues/issue-16994.rs
index d5f1b13..8d3074b 100644
--- a/src/test/ui/issues/issue-16994.rs
+++ b/src/test/ui/issues/issue-16994.rs
@@ -1,10 +1,9 @@
-// compile-pass
-// skip-codegen
+// check-pass
+
 fn cb<'a,T>(_x: Box<dyn Fn((&'a i32, &'a (Vec<&'static i32>, bool))) -> T>) -> T {
     panic!()
 }
 
-
 fn main() {
     cb(Box::new(|(k, &(ref v, b))| (*k, v.clone(), b)));
 }
diff --git a/src/test/ui/issues/issue-19601.rs b/src/test/ui/issues/issue-19601.rs
index faaa2db..176e6ba 100644
--- a/src/test/ui/issues/issue-19601.rs
+++ b/src/test/ui/issues/issue-19601.rs
@@ -1,9 +1,6 @@
-// compile-pass
-// skip-codegen
-#![allow(warnings)]
+// check-pass
+
 trait A<T> {}
 struct B<T> where B<T>: A<B<T>> { t: T }
 
-
-fn main() {
-}
+fn main() {}
diff --git a/src/test/ui/issues/issue-22603.rs b/src/test/ui/issues/issue-22603.rs
index e298316..a83e291 100644
--- a/src/test/ui/issues/issue-22603.rs
+++ b/src/test/ui/issues/issue-22603.rs
@@ -1,6 +1,7 @@
-// skip-codegen
-// compile-pass
+// check-pass
+
 #![feature(unboxed_closures, fn_traits)]
+
 struct Foo;
 
 impl<A> FnOnce<(A,)> for Foo {
diff --git a/src/test/ui/issues/issue-22789.rs b/src/test/ui/issues/issue-22789.rs
index e668012..cef4075 100644
--- a/src/test/ui/issues/issue-22789.rs
+++ b/src/test/ui/issues/issue-22789.rs
@@ -1,6 +1,7 @@
-// compile-pass
-// skip-codegen
+// check-pass
+
 #![feature(unboxed_closures, fn_traits)]
+
 fn main() {
     let k = |x: i32| { x + 1 };
     Fn::call(&k, (0,));
diff --git a/src/test/ui/issues/issue-22814.rs b/src/test/ui/issues/issue-22814.rs
index b008c75..bcc7a8a 100644
--- a/src/test/ui/issues/issue-22814.rs
+++ b/src/test/ui/issues/issue-22814.rs
@@ -3,7 +3,7 @@
 
 macro_rules! test {
 ( $($name:ident)+) => (
-    impl<$($name: Test),*> Test for ($($name,)*) {
+    impl<$($name: Test),+> Test for ($($name,)+) {
     }
 )
 }
diff --git a/src/test/ui/issues/issue-22933-1.rs b/src/test/ui/issues/issue-22933-1.rs
index 1bf8cb0..3c9aa26 100644
--- a/src/test/ui/issues/issue-22933-1.rs
+++ b/src/test/ui/issues/issue-22933-1.rs
@@ -1,6 +1,5 @@
-// compile-pass
-// skip-codegen
-#![allow(warnings)]
+// check-pass
+
 struct CNFParser {
     token: char,
 }
@@ -14,12 +13,11 @@
         self.consume_while(&(CNFParser::is_whitespace))
     }
 
-    fn consume_while(&mut self, p: &Fn(char) -> bool) {
+    fn consume_while(&mut self, p: &dyn Fn(char) -> bool) {
         while p(self.token) {
             return
         }
     }
 }
 
-
 fn main() {}
diff --git a/src/test/ui/issues/issue-24883.rs b/src/test/ui/issues/issue-24883.rs
index 1aa62d3..819a20d 100644
--- a/src/test/ui/issues/issue-24883.rs
+++ b/src/test/ui/issues/issue-24883.rs
@@ -1,5 +1,5 @@
-// compile-pass
-// skip-codegen
+// check-pass
+
 mod a {
     pub mod b { pub struct Foo; }
 
@@ -11,7 +11,6 @@
     pub use self::c::*;
 }
 
-
 fn main() {
     let _ = a::c::Bar(a::b::Foo);
     let _ = a::Bar(a::b::Foo);
diff --git a/src/test/ui/issues/issue-26614.rs b/src/test/ui/issues/issue-26614.rs
index 11c2202..b8ebbdc 100644
--- a/src/test/ui/issues/issue-26614.rs
+++ b/src/test/ui/issues/issue-26614.rs
@@ -1,6 +1,5 @@
-// compile-pass
-// skip-codegen
-#![allow(warnings)]
+// check-pass
+
 trait Mirror {
     type It;
 }
@@ -9,8 +8,6 @@
     type It = Self;
 }
 
-
-
 fn main() {
     let c: <u32 as Mirror>::It = 5;
     const CCCC: <u32 as Mirror>::It = 5;
diff --git a/src/test/ui/issues/issue-26930.rs b/src/test/ui/issues/issue-26930.rs
index abf6b93..707e71b 100644
--- a/src/test/ui/issues/issue-26930.rs
+++ b/src/test/ui/issues/issue-26930.rs
@@ -1,10 +1,8 @@
-// compile-pass
-// skip-codegen
-#![allow(unused)]
+// check-pass
+
 extern crate core;
 use core as core_export;
 use self::x::*;
 mod x {}
 
-
 fn main() {}
diff --git a/src/test/ui/issues/issue-27697.rs b/src/test/ui/issues/issue-27697.rs
new file mode 100644
index 0000000..8307001
--- /dev/null
+++ b/src/test/ui/issues/issue-27697.rs
@@ -0,0 +1,21 @@
+// run-pass
+
+use std::ops::Deref;
+
+trait MyTrait {
+    fn do_something(&self);
+    fn as_str(&self) -> &str;
+}
+
+impl Deref for dyn MyTrait {
+    type Target = str;
+    fn deref(&self) -> &Self::Target {
+        self.as_str()
+    }
+}
+
+fn trait_object_does_something(t: &dyn MyTrait) {
+    t.do_something()
+}
+
+fn main() {}
diff --git a/src/test/ui/issues/issue-29857.rs b/src/test/ui/issues/issue-29857.rs
index 5aff968..6f4c5f4 100644
--- a/src/test/ui/issues/issue-29857.rs
+++ b/src/test/ui/issues/issue-29857.rs
@@ -1,5 +1,4 @@
-// compile-pass
-// skip-codegen
+// check-pass
 
 use std::marker::PhantomData;
 
@@ -17,5 +16,4 @@
 
 impl<T: 'static, W: Bar<Output = T>> Foo<*mut T> for W {}
 
-
 fn main() {}
diff --git a/src/test/ui/issues/issue-31924-non-snake-ffi.rs b/src/test/ui/issues/issue-31924-non-snake-ffi.rs
index 5e2336c..63e42b48 100644
--- a/src/test/ui/issues/issue-31924-non-snake-ffi.rs
+++ b/src/test/ui/issues/issue-31924-non-snake-ffi.rs
@@ -1,8 +1,8 @@
-// compile-pass
-// skip-codegen
-#![deny(non_snake_case)]
-#[no_mangle]
-pub extern "C" fn SparklingGenerationForeignFunctionInterface() {}
+// check-pass
 
+#![deny(non_snake_case)]
+
+#[no_mangle]
+pub extern "C" fn SparklingGenerationForeignFunctionInterface() {} // OK
 
 fn main() {}
diff --git a/src/test/ui/issues/issue-32119.rs b/src/test/ui/issues/issue-32119.rs
index ea8824b..36adb52 100644
--- a/src/test/ui/issues/issue-32119.rs
+++ b/src/test/ui/issues/issue-32119.rs
@@ -1,6 +1,5 @@
-// compile-pass
-// skip-codegen
-#![allow(dead_code)]
+// check-pass
+
 pub type T = ();
 mod foo { pub use super::T; }
 mod bar { pub use super::T; }
@@ -15,5 +14,4 @@
     pub use self::bar::*;
 }
 
-
 fn main() {}
diff --git a/src/test/ui/issues/issue-32222.rs b/src/test/ui/issues/issue-32222.rs
index 91f53ab..4ed06bf 100644
--- a/src/test/ui/issues/issue-32222.rs
+++ b/src/test/ui/issues/issue-32222.rs
@@ -1,6 +1,4 @@
-// compile-pass
-// skip-codegen
-#![allow(warnings)]
+// check-pass
 
 mod foo {
     pub fn bar() {}
@@ -21,5 +19,4 @@
     pub use a::bar;
 }
 
-
 fn main() {}
diff --git a/src/test/ui/issues/issue-32797.rs b/src/test/ui/issues/issue-32797.rs
index 5ceb7f4..b12b929 100644
--- a/src/test/ui/issues/issue-32797.rs
+++ b/src/test/ui/issues/issue-32797.rs
@@ -1,5 +1,4 @@
-// compile-pass
-// skip-codegen
+// check-pass
 
 pub use bar::*;
 mod bar {
@@ -11,5 +10,4 @@
     pub use main as f;
 }
 
-
 pub fn main() {}
diff --git a/src/test/ui/issues/issue-32922.rs b/src/test/ui/issues/issue-32922.rs
index b06b63c..54ec44a 100644
--- a/src/test/ui/issues/issue-32922.rs
+++ b/src/test/ui/issues/issue-32922.rs
@@ -1,6 +1,4 @@
-// compile-pass
-// skip-codegen
-#![allow(warnings)]
+// check-pass
 
 macro_rules! foo { () => {
     let x = 1;
@@ -22,7 +20,6 @@
     }
 }
 
-
 fn main() {
     foo! {};
     bar! {};
diff --git a/src/test/ui/issues/issue-33241.rs b/src/test/ui/issues/issue-33241.rs
index 4d6204c..5f9f1e4 100644
--- a/src/test/ui/issues/issue-33241.rs
+++ b/src/test/ui/issues/issue-33241.rs
@@ -1,5 +1,4 @@
-// compile-pass
-// skip-codegen
+// check-pass
 
 use std::fmt;
 
@@ -7,7 +6,6 @@
 // an unsized tuple by transmuting a trait object.
 fn any<T>() -> T { unreachable!() }
 
-
 fn main() {
     let t: &(u8, dyn fmt::Debug) = any();
     println!("{:?}", &t.1);
diff --git a/src/test/ui/issues/issue-34028.rs b/src/test/ui/issues/issue-34028.rs
index 8ebc730..d761c0c 100644
--- a/src/test/ui/issues/issue-34028.rs
+++ b/src/test/ui/issues/issue-34028.rs
@@ -1,5 +1,4 @@
-// compile-pass
-// skip-codegen
+// check-pass
 
 macro_rules! m {
     () => { #[cfg(any())] fn f() {} }
@@ -8,5 +7,4 @@
 trait T {}
 impl T for () { m!(); }
 
-
 fn main() {}
diff --git a/src/test/ui/issues/issue-34171.rs b/src/test/ui/issues/issue-34171.rs
index 25a5f72..157c58c 100644
--- a/src/test/ui/issues/issue-34171.rs
+++ b/src/test/ui/issues/issue-34171.rs
@@ -1,12 +1,10 @@
-// compile-pass
-// skip-codegen
+// check-pass
 
 macro_rules! null { ($i:tt) => {} }
 macro_rules! apply_null {
     ($i:item) => { null! { $i } }
 }
 
-
 fn main() {
     apply_null!(#[cfg(all())] fn f() {});
 }
diff --git a/src/test/ui/issues/issue-34418.rs b/src/test/ui/issues/issue-34418.rs
index 6a86c27..6132f74 100644
--- a/src/test/ui/issues/issue-34418.rs
+++ b/src/test/ui/issues/issue-34418.rs
@@ -1,6 +1,4 @@
-// compile-pass
-// skip-codegen
-#![allow(unused)]
+// check-pass
 
 macro_rules! make_item {
     () => { fn f() {} }
@@ -18,5 +16,4 @@
     make_stmt! {}
 }
 
-
 fn main() {}
diff --git a/src/test/ui/issues/issue-34839.rs b/src/test/ui/issues/issue-34839.rs
index 01669f0..8ffed82 100644
--- a/src/test/ui/issues/issue-34839.rs
+++ b/src/test/ui/issues/issue-34839.rs
@@ -1,6 +1,4 @@
-// compile-pass
-// skip-codegen
-#![allow(dead_code)]
+// check-pass
 
 trait RegularExpression: Sized {
     type Text;
@@ -18,5 +16,4 @@
     Dynamic(FindCaptures<'t, ExecNoSyncStr<'r>>),
 }
 
-
 fn main() {}
diff --git a/src/test/ui/issues/issue-35570.rs b/src/test/ui/issues/issue-35570.rs
index 9bb9db6..fafef79 100644
--- a/src/test/ui/issues/issue-35570.rs
+++ b/src/test/ui/issues/issue-35570.rs
@@ -1,5 +1,4 @@
-// compile-pass
-// skip-codegen
+// check-pass
 
 use std::mem;
 
@@ -25,7 +24,6 @@
 fn takes_lifetime(_f: for<'a> fn(&'a ()) -> <() as Lifetime<'a>>::Out) {
 }
 
-
 fn main() {
     takes_lifetime(foo);
 }
diff --git a/src/test/ui/issues/issue-36116.rs b/src/test/ui/issues/issue-36116.rs
index b4bfba4..c7c70c7 100644
--- a/src/test/ui/issues/issue-36116.rs
+++ b/src/test/ui/issues/issue-36116.rs
@@ -1,8 +1,7 @@
 // Unnecessary path disambiguator is ok
 
-// compile-pass
-// skip-codegen
-#![allow(unused)]
+// check-pass
+
 macro_rules! m {
     ($p: path) => {
         let _ = $p(0);
@@ -23,5 +22,4 @@
     m!(S::<u8>);
 }
 
-
 fn main() {}
diff --git a/src/test/ui/issues/issue-36379.rs b/src/test/ui/issues/issue-36379.rs
index b2da651..3a3e6f4 100644
--- a/src/test/ui/issues/issue-36379.rs
+++ b/src/test/ui/issues/issue-36379.rs
@@ -1,7 +1,5 @@
-// compile-pass
-// skip-codegen
+// check-pass
 
 fn _test() -> impl Default { }
 
-
-fn main() { }
+fn main() {}
diff --git a/src/test/ui/issues/issue-36839.rs b/src/test/ui/issues/issue-36839.rs
index a660368..ca3d66b 100644
--- a/src/test/ui/issues/issue-36839.rs
+++ b/src/test/ui/issues/issue-36839.rs
@@ -1,5 +1,4 @@
-// compile-pass
-// skip-codegen
+// check-pass
 
 pub trait Foo {
     type Bar;
@@ -17,7 +16,6 @@
     }
 }
 
-
 fn main() {
     let _m: &dyn Broken<Assoc=()> = &();
 }
diff --git a/src/test/ui/issues/issue-37051.rs b/src/test/ui/issues/issue-37051.rs
index 1ccf5b9..9cae6cf 100644
--- a/src/test/ui/issues/issue-37051.rs
+++ b/src/test/ui/issues/issue-37051.rs
@@ -1,7 +1,7 @@
-// compile-pass
-// skip-codegen
+// check-pass
+
 #![feature(associated_type_defaults)]
-#![allow(warnings)]
+
 trait State: Sized {
     type NextState: State = StateMachineEnded;
     fn execute(self) -> Option<Self::NextState>;
@@ -15,6 +15,4 @@
     }
 }
 
-
-fn main() {
-}
+fn main() {}
diff --git a/src/test/ui/issues/issue-37366.rs b/src/test/ui/issues/issue-37366.rs
index 1c27960..6bf3a27 100644
--- a/src/test/ui/issues/issue-37366.rs
+++ b/src/test/ui/issues/issue-37366.rs
@@ -1,6 +1,6 @@
+// check-pass
 // ignore-emscripten
-// compile-pass
-// skip-codegen
+
 #![feature(asm)]
 
 macro_rules! interrupt_handler {
@@ -12,6 +12,4 @@
 }
 interrupt_handler!{}
 
-
-fn main() {
-}
+fn main() {}
diff --git a/src/test/ui/issues/issue-37510.rs b/src/test/ui/issues/issue-37510.rs
index 465e680..2081c9f 100644
--- a/src/test/ui/issues/issue-37510.rs
+++ b/src/test/ui/issues/issue-37510.rs
@@ -1,9 +1,7 @@
-// compile-pass
-// skip-codegen
+// check-pass
 
 fn foo(_: &mut i32) -> bool { true }
 
-
 fn main() {
     let opt = Some(92);
     let mut x = 62;
diff --git a/src/test/ui/issues/issue-37515.rs b/src/test/ui/issues/issue-37515.rs
index cc07bd1..caff507 100644
--- a/src/test/ui/issues/issue-37515.rs
+++ b/src/test/ui/issues/issue-37515.rs
@@ -1,10 +1,8 @@
-// skip-codegen
-// compile-pass
+// check-pass
+
 #![warn(unused)]
 
 type Z = dyn for<'x> Send;
 //~^ WARN type alias is never used
 
-
-fn main() {
-}
+fn main() {}
diff --git a/src/test/ui/issues/issue-38160.rs b/src/test/ui/issues/issue-38160.rs
index a454211..0da8b79 100644
--- a/src/test/ui/issues/issue-38160.rs
+++ b/src/test/ui/issues/issue-38160.rs
@@ -1,7 +1,5 @@
-// compile-pass
-// skip-codegen
-#![feature(associated_consts)]
-#![allow(warnings)]
+// check-pass
+
 trait MyTrait {
     const MY_CONST: &'static str;
 }
@@ -18,5 +16,4 @@
 
 my_macro!();
 
-
 fn main() {}
diff --git a/src/test/ui/issues/issue-38381.rs b/src/test/ui/issues/issue-38381.rs
index 1351376..82d4a4e 100644
--- a/src/test/ui/issues/issue-38381.rs
+++ b/src/test/ui/issues/issue-38381.rs
@@ -1,9 +1,7 @@
-// compile-pass
-// skip-codegen
+// check-pass
 
 use std::ops::Deref;
 
-
 fn main() {
     let _x: fn(&i32) -> <&i32 as Deref>::Target = unimplemented!();
 }
diff --git a/src/test/ui/issues/issue-40350.rs b/src/test/ui/issues/issue-40350.rs
index b2cc004..a39a851 100644
--- a/src/test/ui/issues/issue-40350.rs
+++ b/src/test/ui/issues/issue-40350.rs
@@ -1,6 +1,4 @@
-// compile-pass
-// skip-codegen
-#![allow(warnings)]
+// check-pass
 
 enum E {
     A = {
@@ -9,5 +7,4 @@
     }
 }
 
-
 fn main() {}
diff --git a/src/test/ui/lint/lint-unnecessary-parens.rs b/src/test/ui/lint/lint-unnecessary-parens.rs
index dc74d69..c361010 100644
--- a/src/test/ui/lint/lint-unnecessary-parens.rs
+++ b/src/test/ui/lint/lint-unnecessary-parens.rs
@@ -22,8 +22,8 @@
     match (true) { //~ ERROR unnecessary parentheses around `match` head expression
         _ => {}
     }
-    if let 1 = (1) {} //~ ERROR unnecessary parentheses around `if let` head expression
-    while let 1 = (2) {} //~ ERROR unnecessary parentheses around `while let` head expression
+    if let 1 = (1) {} //~ ERROR unnecessary parentheses around `let` head expression
+    while let 1 = (2) {} //~ ERROR unnecessary parentheses around `let` head expression
     let v = X { y: false };
     // struct lits needs parens, so these shouldn't warn.
     if (v == X { y: true }) {}
diff --git a/src/test/ui/lint/lint-unnecessary-parens.stderr b/src/test/ui/lint/lint-unnecessary-parens.stderr
index fe2ee38..dfbefd7 100644
--- a/src/test/ui/lint/lint-unnecessary-parens.stderr
+++ b/src/test/ui/lint/lint-unnecessary-parens.stderr
@@ -40,13 +40,13 @@
 LL |     match (true) {
    |           ^^^^^^ help: remove these parentheses
 
-error: unnecessary parentheses around `if let` head expression
+error: unnecessary parentheses around `let` head expression
   --> $DIR/lint-unnecessary-parens.rs:25:16
    |
 LL |     if let 1 = (1) {}
    |                ^^^ help: remove these parentheses
 
-error: unnecessary parentheses around `while let` head expression
+error: unnecessary parentheses around `let` head expression
   --> $DIR/lint-unnecessary-parens.rs:26:19
    |
 LL |     while let 1 = (2) {}
diff --git a/src/test/ui/malformed/malformed-derive-entry.rs b/src/test/ui/malformed/malformed-derive-entry.rs
index 3e53e15..a6d8863 100644
--- a/src/test/ui/malformed/malformed-derive-entry.rs
+++ b/src/test/ui/malformed/malformed-derive-entry.rs
@@ -4,9 +4,6 @@
 #[derive(Copy="bad")] //~ ERROR expected one of `)`, `,`, or `::`, found `=`
 struct Test2;
 
-#[derive()] //~ WARNING empty trait list
-struct Test3;
-
 #[derive] //~ ERROR malformed `derive` attribute input
 struct Test4;
 
diff --git a/src/test/ui/malformed/malformed-derive-entry.stderr b/src/test/ui/malformed/malformed-derive-entry.stderr
index dfbc5fa..f7500fe 100644
--- a/src/test/ui/malformed/malformed-derive-entry.stderr
+++ b/src/test/ui/malformed/malformed-derive-entry.stderr
@@ -10,14 +10,8 @@
 LL | #[derive(Copy="bad")]
    |              ^ expected one of `)`, `,`, or `::` here
 
-warning: empty trait list in `derive`
-  --> $DIR/malformed-derive-entry.rs:7:1
-   |
-LL | #[derive()]
-   | ^^^^^^^^^^^
-
 error: malformed `derive` attribute input
-  --> $DIR/malformed-derive-entry.rs:10:1
+  --> $DIR/malformed-derive-entry.rs:7:1
    |
 LL | #[derive]
    | ^^^^^^^^^ help: missing traits to be derived: `#[derive(Trait1, Trait2, ...)]`
diff --git a/src/test/ui/nll/closure-requirements/region-lbr1-does-outlive-lbr2-because-implied-bound.rs b/src/test/ui/nll/closure-requirements/region-lbr1-does-outlive-lbr2-because-implied-bound.rs
index 5a008ad..a558e8a 100644
--- a/src/test/ui/nll/closure-requirements/region-lbr1-does-outlive-lbr2-because-implied-bound.rs
+++ b/src/test/ui/nll/closure-requirements/region-lbr1-does-outlive-lbr2-because-implied-bound.rs
@@ -1,12 +1,11 @@
 // Basic test for free regions in the NLL code. This test does not
 // report an error because of the (implied) bound that `'b: 'a`.
 
+// check-pass
 // compile-flags:-Zborrowck=mir -Zverbose
-// compile-pass
-// skip-codegen
 
 fn foo<'a, 'b>(x: &'a &'b u32) -> &'a u32 {
     &**x
 }
 
-fn main() { }
+fn main() {}
diff --git a/src/test/ui/nll/issue-62007-assign-const-index.rs b/src/test/ui/nll/issue-62007-assign-const-index.rs
new file mode 100644
index 0000000..3ea5d3a
--- /dev/null
+++ b/src/test/ui/nll/issue-62007-assign-const-index.rs
@@ -0,0 +1,32 @@
+// Issue #62007: assigning over a const-index projection of an array
+// (in this case, `list[I] = n;`) should in theory be able to kill all borrows
+// of `list[0]`, so that `list[0]` could be borrowed on the next
+// iteration through the loop.
+//
+// Currently the compiler does not allow this. We may want to consider
+// loosening that restriction in the future. (However, doing so would
+// at *least* require T-lang team approval, and probably an RFC; e.g.
+// such loosening might make complicate the user's mental mode; it
+// also would make code more brittle in the face of refactorings that
+// replace constants with variables.
+
+#![allow(dead_code)]
+
+struct List<T> {
+    value: T,
+    next: Option<Box<List<T>>>,
+}
+
+fn to_refs<T>(mut list: [&mut List<T>; 2]) -> Vec<&mut T> {
+    let mut result = vec![];
+    loop {
+        result.push(&mut list[0].value); //~ ERROR cannot borrow `list[_].value` as mutable
+        if let Some(n) = list[0].next.as_mut() { //~ ERROR cannot borrow `list[_].next` as mutable
+            list[0] = n;
+        } else {
+            return result;
+        }
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/nll/issue-62007-assign-const-index.stderr b/src/test/ui/nll/issue-62007-assign-const-index.stderr
new file mode 100644
index 0000000..758a14d
--- /dev/null
+++ b/src/test/ui/nll/issue-62007-assign-const-index.stderr
@@ -0,0 +1,27 @@
+error[E0499]: cannot borrow `list[_].value` as mutable more than once at a time
+  --> $DIR/issue-62007-assign-const-index.rs:23:21
+   |
+LL | fn to_refs<T>(mut list: [&mut List<T>; 2]) -> Vec<&mut T> {
+   |                          - let's call the lifetime of this reference `'1`
+...
+LL |         result.push(&mut list[0].value);
+   |                     ^^^^^^^^^^^^^^^^^^ mutable borrow starts here in previous iteration of loop
+...
+LL |             return result;
+   |                    ------ returning this value requires that `list[_].value` is borrowed for `'1`
+
+error[E0499]: cannot borrow `list[_].next` as mutable more than once at a time
+  --> $DIR/issue-62007-assign-const-index.rs:24:26
+   |
+LL | fn to_refs<T>(mut list: [&mut List<T>; 2]) -> Vec<&mut T> {
+   |                          - let's call the lifetime of this reference `'1`
+...
+LL |         if let Some(n) = list[0].next.as_mut() {
+   |                          ^^^^^^^^^^^^---------
+   |                          |
+   |                          mutable borrow starts here in previous iteration of loop
+   |                          argument requires that `list[_].next` is borrowed for `'1`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0499`.
diff --git a/src/test/ui/nll/issue-62007-assign-differing-fields.rs b/src/test/ui/nll/issue-62007-assign-differing-fields.rs
new file mode 100644
index 0000000..29d92b7
--- /dev/null
+++ b/src/test/ui/nll/issue-62007-assign-differing-fields.rs
@@ -0,0 +1,25 @@
+// Double-check we didn't go too far with our resolution to issue
+// #62007: assigning over a field projection (`list.1 = n;` in this
+// case) should kill only borrows of `list.1`; `list.0` can *not*
+// necessarily be borrowed on the next iteration through the loop.
+
+#![allow(dead_code)]
+
+struct List<T> {
+    value: T,
+    next: Option<Box<List<T>>>,
+}
+
+fn to_refs<'a, T>(mut list: (&'a mut List<T>, &'a mut List<T>)) -> Vec<&'a mut T> {
+    let mut result = vec![];
+    loop {
+        result.push(&mut (list.0).value); //~ ERROR cannot borrow `list.0.value` as mutable
+        if let Some(n) = (list.0).next.as_mut() { //~ ERROR cannot borrow `list.0.next` as mutable
+            list.1 = n;
+        } else {
+            return result;
+        }
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/nll/issue-62007-assign-differing-fields.stderr b/src/test/ui/nll/issue-62007-assign-differing-fields.stderr
new file mode 100644
index 0000000..f942d76
--- /dev/null
+++ b/src/test/ui/nll/issue-62007-assign-differing-fields.stderr
@@ -0,0 +1,27 @@
+error[E0499]: cannot borrow `list.0.value` as mutable more than once at a time
+  --> $DIR/issue-62007-assign-differing-fields.rs:16:21
+   |
+LL | fn to_refs<'a, T>(mut list: (&'a mut List<T>, &'a mut List<T>)) -> Vec<&'a mut T> {
+   |            -- lifetime `'a` defined here
+...
+LL |         result.push(&mut (list.0).value);
+   |                     ^^^^^^^^^^^^^^^^^^^ mutable borrow starts here in previous iteration of loop
+...
+LL |             return result;
+   |                    ------ returning this value requires that `list.0.value` is borrowed for `'a`
+
+error[E0499]: cannot borrow `list.0.next` as mutable more than once at a time
+  --> $DIR/issue-62007-assign-differing-fields.rs:17:26
+   |
+LL | fn to_refs<'a, T>(mut list: (&'a mut List<T>, &'a mut List<T>)) -> Vec<&'a mut T> {
+   |            -- lifetime `'a` defined here
+...
+LL |         if let Some(n) = (list.0).next.as_mut() {
+   |                          ^^^^^^^^^^^^^---------
+   |                          |
+   |                          mutable borrow starts here in previous iteration of loop
+   |                          argument requires that `list.0.next` is borrowed for `'a`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0499`.
diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns_same_crate.rs b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns_same_crate.rs
index 5dbd38e..d1e7c3b 100644
--- a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns_same_crate.rs
+++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns_same_crate.rs
@@ -1,5 +1,5 @@
-// compile-pass
-// skip-codegen
+// check-pass
+
 #![deny(unreachable_patterns)]
 #![feature(exhaustive_patterns)]
 #![feature(never_type)]
diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns_same_crate.rs b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns_same_crate.rs
index 74922d4..8973d21 100644
--- a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns_same_crate.rs
+++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns_same_crate.rs
@@ -1,5 +1,5 @@
-// compile-pass
-// skip-codegen
+// check-pass
+
 #![deny(unreachable_patterns)]
 #![feature(exhaustive_patterns)]
 #![feature(never_type)]
diff --git a/src/test/ui/rfc-2497-if-let-chains/ast-pretty-check.rs b/src/test/ui/rfc-2497-if-let-chains/ast-pretty-check.rs
new file mode 100644
index 0000000..e66d465
--- /dev/null
+++ b/src/test/ui/rfc-2497-if-let-chains/ast-pretty-check.rs
@@ -0,0 +1,6 @@
+// compile-pass
+// compile-flags: -Z unpretty=expanded
+
+fn main() {
+    if let 0 = 1 {}
+}
diff --git a/src/test/ui/rfc-2497-if-let-chains/ast-pretty-check.stdout b/src/test/ui/rfc-2497-if-let-chains/ast-pretty-check.stdout
new file mode 100644
index 0000000..a6b15f9
--- /dev/null
+++ b/src/test/ui/rfc-2497-if-let-chains/ast-pretty-check.stdout
@@ -0,0 +1,10 @@
+#![feature(prelude_import)]
+#![no_std]
+#[prelude_import]
+use ::std::prelude::v1::*;
+#[macro_use]
+extern crate std;
+// compile-pass
+// compile-flags: -Z unpretty=expanded
+
+fn main() { if let 0 = 1 { } }
diff --git a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.rs b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.rs
new file mode 100644
index 0000000..7d1e5c3
--- /dev/null
+++ b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.rs
@@ -0,0 +1,244 @@
+// Here we test that `lowering` behaves correctly wrt. `let $pats = $expr` expressions.
+//
+// We want to make sure that `let` is banned in situations other than:
+//
+// expr =
+//   | ...
+//   | "if" expr_with_let block {"else" block}?
+//   | {label ":"}? while" expr_with_let block
+//   ;
+//
+// expr_with_let =
+//   | "let" top_pats "=" expr
+//   | expr_with_let "&&" expr_with_let
+//   | "(" expr_with_let ")"
+//   | expr
+//   ;
+//
+// To that end, we check some positions which is not part of the language above.
+
+#![feature(const_generics)]
+//~^ WARN the feature `const_generics` is incomplete
+#![feature(let_chains)] // Avoid inflating `.stderr` with overzealous gates in this test.
+//~^ WARN the feature `let_chains` is incomplete
+
+#![allow(irrefutable_let_patterns)]
+
+use std::ops::Range;
+
+fn main() {}
+
+fn nested_within_if_expr() {
+    if &let 0 = 0 {} //~ ERROR `let` expressions are not supported here
+    //~^ ERROR mismatched types
+
+    if !let 0 = 0 {} //~ ERROR `let` expressions are not supported here
+    if *let 0 = 0 {} //~ ERROR `let` expressions are not supported here
+    //~^ ERROR type `bool` cannot be dereferenced
+    if -let 0 = 0 {} //~ ERROR `let` expressions are not supported here
+    //~^ ERROR cannot apply unary operator `-` to type `bool`
+
+    fn _check_try_binds_tighter() -> Result<(), ()> {
+        if let 0 = 0? {}
+        //~^ ERROR the `?` operator can only be applied to values that implement `std::ops::Try`
+        Ok(())
+    }
+    if (let 0 = 0)? {} //~ ERROR `let` expressions are not supported here
+    //~^ ERROR the `?` operator can only be applied to values that implement `std::ops::Try`
+    //~| ERROR the `?` operator can only be used in a function that returns `Result`
+
+    if true || let 0 = 0 {} //~ ERROR `let` expressions are not supported here
+    if (true || let 0 = 0) {} //~ ERROR `let` expressions are not supported here
+    if true && (true || let 0 = 0) {} //~ ERROR `let` expressions are not supported here
+    if true || (true && let 0 = 0) {} //~ ERROR `let` expressions are not supported here
+
+    let mut x = true;
+    if x = let 0 = 0 {} //~ ERROR `let` expressions are not supported here
+    //~^ ERROR mismatched types
+
+    if true..(let 0 = 0) {} //~ ERROR `let` expressions are not supported here
+    //~^ ERROR mismatched types
+    if ..(let 0 = 0) {} //~ ERROR `let` expressions are not supported here
+    //~^ ERROR mismatched types
+    if (let 0 = 0).. {} //~ ERROR `let` expressions are not supported here
+    //~^ ERROR mismatched types
+
+    // Binds as `(let ... = true)..true &&/|| false`.
+    if let Range { start: _, end: _ } = true..true && false {}
+    //~^ ERROR `let` expressions are not supported here
+    //~| ERROR mismatched types
+    //~| ERROR mismatched types
+    if let Range { start: _, end: _ } = true..true || false {}
+    //~^ ERROR `let` expressions are not supported here
+    //~| ERROR mismatched types
+    //~| ERROR mismatched types
+
+    // Binds as `(let Range { start: F, end } = F)..(|| true)`.
+    const F: fn() -> bool = || true;
+    if let Range { start: F, end } = F..|| true {}
+    //~^ ERROR `let` expressions are not supported here
+    //~| ERROR mismatched types
+    //~| ERROR mismatched types
+    //~| ERROR mismatched types
+
+    // Binds as `(let Range { start: true, end } = t)..(&&false)`.
+    let t = &&true;
+    if let Range { start: true, end } = t..&&false {}
+    //~^ ERROR `let` expressions are not supported here
+    //~| ERROR mismatched types
+    //~| ERROR mismatched types
+    //~| ERROR mismatched types
+
+    if let true = let true = true {} //~ ERROR `let` expressions are not supported here
+}
+
+fn nested_within_while_expr() {
+    while &let 0 = 0 {} //~ ERROR `let` expressions are not supported here
+    //~^ ERROR mismatched types
+
+    while !let 0 = 0 {} //~ ERROR `let` expressions are not supported here
+    while *let 0 = 0 {} //~ ERROR `let` expressions are not supported here
+    //~^ ERROR type `bool` cannot be dereferenced
+    while -let 0 = 0 {} //~ ERROR `let` expressions are not supported here
+    //~^ ERROR cannot apply unary operator `-` to type `bool`
+
+    fn _check_try_binds_tighter() -> Result<(), ()> {
+        while let 0 = 0? {}
+        //~^ ERROR the `?` operator can only be applied to values that implement `std::ops::Try`
+        Ok(())
+    }
+    while (let 0 = 0)? {} //~ ERROR `let` expressions are not supported here
+    //~^ ERROR the `?` operator can only be applied to values that implement `std::ops::Try`
+    //~| ERROR the `?` operator can only be used in a function that returns `Result`
+
+    while true || let 0 = 0 {} //~ ERROR `let` expressions are not supported here
+    while (true || let 0 = 0) {} //~ ERROR `let` expressions are not supported here
+    while true && (true || let 0 = 0) {} //~ ERROR `let` expressions are not supported here
+    while true || (true && let 0 = 0) {} //~ ERROR `let` expressions are not supported here
+
+    let mut x = true;
+    while x = let 0 = 0 {} //~ ERROR `let` expressions are not supported here
+    //~^ ERROR mismatched types
+
+    while true..(let 0 = 0) {} //~ ERROR `let` expressions are not supported here
+    //~^ ERROR mismatched types
+    while ..(let 0 = 0) {} //~ ERROR `let` expressions are not supported here
+    //~^ ERROR mismatched types
+    while (let 0 = 0).. {} //~ ERROR `let` expressions are not supported here
+    //~^ ERROR mismatched types
+
+    // Binds as `(let ... = true)..true &&/|| false`.
+    while let Range { start: _, end: _ } = true..true && false {}
+    //~^ ERROR `let` expressions are not supported here
+    //~| ERROR mismatched types
+    //~| ERROR mismatched types
+    while let Range { start: _, end: _ } = true..true || false {}
+    //~^ ERROR `let` expressions are not supported here
+    //~| ERROR mismatched types
+    //~| ERROR mismatched types
+
+    // Binds as `(let Range { start: F, end } = F)..(|| true)`.
+    const F: fn() -> bool = || true;
+    while let Range { start: F, end } = F..|| true {}
+    //~^ ERROR `let` expressions are not supported here
+    //~| ERROR mismatched types
+    //~| ERROR mismatched types
+    //~| ERROR mismatched types
+
+    // Binds as `(let Range { start: true, end } = t)..(&&false)`.
+    let t = &&true;
+    while let Range { start: true, end } = t..&&false {}
+    //~^ ERROR `let` expressions are not supported here
+    //~| ERROR mismatched types
+    //~| ERROR mismatched types
+    //~| ERROR mismatched types
+
+    while let true = let true = true {} //~ ERROR `let` expressions are not supported here
+}
+
+fn not_error_because_clarified_intent() {
+    if let Range { start: _, end: _ } = (true..true || false) { }
+
+    if let Range { start: _, end: _ } = (true..true && false) { }
+
+    while let Range { start: _, end: _ } = (true..true || false) { }
+
+    while let Range { start: _, end: _ } = (true..true && false) { }
+}
+
+fn outside_if_and_while_expr() {
+    &let 0 = 0; //~ ERROR `let` expressions are not supported here
+
+    !let 0 = 0; //~ ERROR `let` expressions are not supported here
+    *let 0 = 0; //~ ERROR `let` expressions are not supported here
+    //~^ ERROR type `bool` cannot be dereferenced
+    -let 0 = 0; //~ ERROR `let` expressions are not supported here
+    //~^ ERROR cannot apply unary operator `-` to type `bool`
+
+    fn _check_try_binds_tighter() -> Result<(), ()> {
+        let 0 = 0?;
+        //~^ ERROR the `?` operator can only be applied to values that implement `std::ops::Try`
+        Ok(())
+    }
+    (let 0 = 0)?; //~ ERROR `let` expressions are not supported here
+    //~^ ERROR the `?` operator can only be used in a function that returns `Result`
+    //~| ERROR the `?` operator can only be applied to values that implement `std::ops::Try`
+
+    true || let 0 = 0; //~ ERROR `let` expressions are not supported here
+    (true || let 0 = 0); //~ ERROR `let` expressions are not supported here
+    true && (true || let 0 = 0); //~ ERROR `let` expressions are not supported here
+
+    let mut x = true;
+    x = let 0 = 0; //~ ERROR `let` expressions are not supported here
+
+    true..(let 0 = 0); //~ ERROR `let` expressions are not supported here
+    ..(let 0 = 0); //~ ERROR `let` expressions are not supported here
+    (let 0 = 0)..; //~ ERROR `let` expressions are not supported here
+
+    (let Range { start: _, end: _ } = true..true || false);
+    //~^ ERROR `let` expressions are not supported here
+    //~| ERROR mismatched types
+
+    (let true = let true = true);
+    //~^ ERROR `let` expressions are not supported here
+    //~| ERROR `let` expressions are not supported here
+
+    // Check function tail position.
+    &let 0 = 0
+    //~^ ERROR `let` expressions are not supported here
+    //~| ERROR mismatched types
+}
+
+// Let's make sure that `let` inside const generic arguments are considered.
+fn inside_const_generic_arguments() {
+    struct A<const B: bool>;
+    impl<const B: bool> A<{B}> { const O: u32 = 5; }
+
+    if let A::<{
+        true && let 1 = 1 //~ ERROR `let` expressions are not supported here
+        //~^ ERROR constant contains unimplemented expression type
+        //~| ERROR constant contains unimplemented expression type
+    }>::O = 5 {}
+
+    while let A::<{
+        true && let 1 = 1 //~ ERROR `let` expressions are not supported here
+        //~^ ERROR constant contains unimplemented expression type
+        //~| ERROR constant contains unimplemented expression type
+    }>::O = 5 {}
+
+    if A::<{
+        true && let 1 = 1 //~ ERROR `let` expressions are not supported here
+        //~^ ERROR constant contains unimplemented expression type
+        //~| ERROR constant contains unimplemented expression type
+    }>::O == 5 {}
+
+    // In the cases above we have `ExprKind::Block` to help us out.
+    // Below however, we would not have a block and so an implementation might go
+    // from visiting expressions to types without banning `let` expressions down the tree.
+    // This tests ensures that we are not caught by surprise should the parser
+    // admit non-IDENT expressions in const generic arguments.
+
+    if A::<
+        true && let 1 = 1 //~ ERROR expected one of `,` or `>`, found `&&`
+    >::O == 5 {}
+}
diff --git a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr
new file mode 100644
index 0000000..207d0d6
--- /dev/null
+++ b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr
@@ -0,0 +1,987 @@
+error: expected one of `,` or `>`, found `&&`
+  --> $DIR/disallowed-positions.rs:242:14
+   |
+LL |         true && let 1 = 1
+   |              ^^ expected one of `,` or `>` here
+
+warning: the feature `const_generics` is incomplete and may cause the compiler to crash
+  --> $DIR/disallowed-positions.rs:20:12
+   |
+LL | #![feature(const_generics)]
+   |            ^^^^^^^^^^^^^^
+
+warning: the feature `let_chains` is incomplete and may cause the compiler to crash
+  --> $DIR/disallowed-positions.rs:22:12
+   |
+LL | #![feature(let_chains)] // Avoid inflating `.stderr` with overzealous gates in this test.
+   |            ^^^^^^^^^^
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:32:9
+   |
+LL |     if &let 0 = 0 {}
+   |         ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:35:9
+   |
+LL |     if !let 0 = 0 {}
+   |         ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:36:9
+   |
+LL |     if *let 0 = 0 {}
+   |         ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:38:9
+   |
+LL |     if -let 0 = 0 {}
+   |         ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:46:9
+   |
+LL |     if (let 0 = 0)? {}
+   |         ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:50:16
+   |
+LL |     if true || let 0 = 0 {}
+   |                ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:51:17
+   |
+LL |     if (true || let 0 = 0) {}
+   |                 ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:52:25
+   |
+LL |     if true && (true || let 0 = 0) {}
+   |                         ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:53:25
+   |
+LL |     if true || (true && let 0 = 0) {}
+   |                         ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:56:12
+   |
+LL |     if x = let 0 = 0 {}
+   |            ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:59:15
+   |
+LL |     if true..(let 0 = 0) {}
+   |               ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:61:11
+   |
+LL |     if ..(let 0 = 0) {}
+   |           ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:63:9
+   |
+LL |     if (let 0 = 0).. {}
+   |         ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:67:8
+   |
+LL |     if let Range { start: _, end: _ } = true..true && false {}
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:71:8
+   |
+LL |     if let Range { start: _, end: _ } = true..true || false {}
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:78:8
+   |
+LL |     if let Range { start: F, end } = F..|| true {}
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:86:8
+   |
+LL |     if let Range { start: true, end } = t..&&false {}
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:92:19
+   |
+LL |     if let true = let true = true {}
+   |                   ^^^^^^^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:96:12
+   |
+LL |     while &let 0 = 0 {}
+   |            ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:99:12
+   |
+LL |     while !let 0 = 0 {}
+   |            ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:100:12
+   |
+LL |     while *let 0 = 0 {}
+   |            ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:102:12
+   |
+LL |     while -let 0 = 0 {}
+   |            ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:110:12
+   |
+LL |     while (let 0 = 0)? {}
+   |            ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:114:19
+   |
+LL |     while true || let 0 = 0 {}
+   |                   ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:115:20
+   |
+LL |     while (true || let 0 = 0) {}
+   |                    ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:116:28
+   |
+LL |     while true && (true || let 0 = 0) {}
+   |                            ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:117:28
+   |
+LL |     while true || (true && let 0 = 0) {}
+   |                            ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:120:15
+   |
+LL |     while x = let 0 = 0 {}
+   |               ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:123:18
+   |
+LL |     while true..(let 0 = 0) {}
+   |                  ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:125:14
+   |
+LL |     while ..(let 0 = 0) {}
+   |              ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:127:12
+   |
+LL |     while (let 0 = 0).. {}
+   |            ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:131:11
+   |
+LL |     while let Range { start: _, end: _ } = true..true && false {}
+   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:135:11
+   |
+LL |     while let Range { start: _, end: _ } = true..true || false {}
+   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:142:11
+   |
+LL |     while let Range { start: F, end } = F..|| true {}
+   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:150:11
+   |
+LL |     while let Range { start: true, end } = t..&&false {}
+   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:156:22
+   |
+LL |     while let true = let true = true {}
+   |                      ^^^^^^^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:170:6
+   |
+LL |     &let 0 = 0;
+   |      ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:172:6
+   |
+LL |     !let 0 = 0;
+   |      ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:173:6
+   |
+LL |     *let 0 = 0;
+   |      ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:175:6
+   |
+LL |     -let 0 = 0;
+   |      ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:183:6
+   |
+LL |     (let 0 = 0)?;
+   |      ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:187:13
+   |
+LL |     true || let 0 = 0;
+   |             ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:188:14
+   |
+LL |     (true || let 0 = 0);
+   |              ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:189:22
+   |
+LL |     true && (true || let 0 = 0);
+   |                      ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:192:9
+   |
+LL |     x = let 0 = 0;
+   |         ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:194:12
+   |
+LL |     true..(let 0 = 0);
+   |            ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:195:8
+   |
+LL |     ..(let 0 = 0);
+   |        ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:196:6
+   |
+LL |     (let 0 = 0)..;
+   |      ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:198:6
+   |
+LL |     (let Range { start: _, end: _ } = true..true || false);
+   |      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:202:6
+   |
+LL |     (let true = let true = true);
+   |      ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:202:17
+   |
+LL |     (let true = let true = true);
+   |                 ^^^^^^^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:207:6
+   |
+LL |     &let 0 = 0
+   |      ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:218:17
+   |
+LL |         true && let 1 = 1
+   |                 ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:224:17
+   |
+LL |         true && let 1 = 1
+   |                 ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/disallowed-positions.rs:230:17
+   |
+LL |         true && let 1 = 1
+   |                 ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error[E0308]: mismatched types
+  --> $DIR/disallowed-positions.rs:32:8
+   |
+LL |     if &let 0 = 0 {}
+   |        ^^^^^^^^^^ expected bool, found &bool
+   |
+   = note: expected type `bool`
+              found type `&bool`
+
+error[E0614]: type `bool` cannot be dereferenced
+  --> $DIR/disallowed-positions.rs:36:8
+   |
+LL |     if *let 0 = 0 {}
+   |        ^^^^^^^^^^
+
+error[E0600]: cannot apply unary operator `-` to type `bool`
+  --> $DIR/disallowed-positions.rs:38:8
+   |
+LL |     if -let 0 = 0 {}
+   |        ^^^^^^^^^^ cannot apply unary operator `-`
+   |
+   = note: an implementation of `std::ops::Neg` might be missing for `bool`
+
+error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try`
+  --> $DIR/disallowed-positions.rs:46:8
+   |
+LL |     if (let 0 = 0)? {}
+   |        ^^^^^^^^^^^^ the `?` operator cannot be applied to type `bool`
+   |
+   = help: the trait `std::ops::Try` is not implemented for `bool`
+   = note: required by `std::ops::Try::into_result`
+
+error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
+  --> $DIR/disallowed-positions.rs:46:8
+   |
+LL |     if (let 0 = 0)? {}
+   |        ^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()`
+   |
+   = help: the trait `std::ops::Try` is not implemented for `()`
+   = note: required by `std::ops::Try::from_error`
+
+error[E0308]: mismatched types
+  --> $DIR/disallowed-positions.rs:56:8
+   |
+LL |     if x = let 0 = 0 {}
+   |        ^^^^^^^^^^^^^
+   |        |
+   |        expected bool, found ()
+   |        help: try comparing for equality: `x == let 0 = 0`
+   |
+   = note: expected type `bool`
+              found type `()`
+
+error[E0308]: mismatched types
+  --> $DIR/disallowed-positions.rs:59:8
+   |
+LL |     if true..(let 0 = 0) {}
+   |        ^^^^^^^^^^^^^^^^^ expected bool, found struct `std::ops::Range`
+   |
+   = note: expected type `bool`
+              found type `std::ops::Range<bool>`
+
+error[E0308]: mismatched types
+  --> $DIR/disallowed-positions.rs:61:8
+   |
+LL |     if ..(let 0 = 0) {}
+   |        ^^^^^^^^^^^^^ expected bool, found struct `std::ops::RangeTo`
+   |
+   = note: expected type `bool`
+              found type `std::ops::RangeTo<bool>`
+
+error[E0308]: mismatched types
+  --> $DIR/disallowed-positions.rs:63:8
+   |
+LL |     if (let 0 = 0).. {}
+   |        ^^^^^^^^^^^^^ expected bool, found struct `std::ops::RangeFrom`
+   |
+   = note: expected type `bool`
+              found type `std::ops::RangeFrom<bool>`
+
+error[E0308]: mismatched types
+  --> $DIR/disallowed-positions.rs:67:12
+   |
+LL |     if let Range { start: _, end: _ } = true..true && false {}
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^   ---- this match expression has type `bool`
+   |            |
+   |            expected bool, found struct `std::ops::Range`
+   |
+   = note: expected type `bool`
+              found type `std::ops::Range<_>`
+
+error[E0308]: mismatched types
+  --> $DIR/disallowed-positions.rs:67:8
+   |
+LL |     if let Range { start: _, end: _ } = true..true && false {}
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected bool, found struct `std::ops::Range`
+   |
+   = note: expected type `bool`
+              found type `std::ops::Range<bool>`
+
+error[E0308]: mismatched types
+  --> $DIR/disallowed-positions.rs:71:12
+   |
+LL |     if let Range { start: _, end: _ } = true..true || false {}
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^   ---- this match expression has type `bool`
+   |            |
+   |            expected bool, found struct `std::ops::Range`
+   |
+   = note: expected type `bool`
+              found type `std::ops::Range<_>`
+
+error[E0308]: mismatched types
+  --> $DIR/disallowed-positions.rs:71:8
+   |
+LL |     if let Range { start: _, end: _ } = true..true || false {}
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected bool, found struct `std::ops::Range`
+   |
+   = note: expected type `bool`
+              found type `std::ops::Range<bool>`
+
+error[E0308]: mismatched types
+  --> $DIR/disallowed-positions.rs:78:12
+   |
+LL |     if let Range { start: F, end } = F..|| true {}
+   |            ^^^^^^^^^^^^^^^^^^^^^^^ expected fn pointer, found struct `std::ops::Range`
+   |
+   = note: expected type `fn() -> bool`
+              found type `std::ops::Range<_>`
+
+error[E0308]: mismatched types
+  --> $DIR/disallowed-positions.rs:78:41
+   |
+LL |     if let Range { start: F, end } = F..|| true {}
+   |                                         ^^^^^^^ expected bool, found closure
+   |
+   = note: expected type `bool`
+              found type `[closure@$DIR/disallowed-positions.rs:78:41: 78:48]`
+
+error[E0308]: mismatched types
+  --> $DIR/disallowed-positions.rs:78:8
+   |
+LL |     if let Range { start: F, end } = F..|| true {}
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected bool, found struct `std::ops::Range`
+   |
+   = note: expected type `bool`
+              found type `std::ops::Range<bool>`
+
+error[E0308]: mismatched types
+  --> $DIR/disallowed-positions.rs:86:12
+   |
+LL |     if let Range { start: true, end } = t..&&false {}
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^   - this match expression has type `bool`
+   |            |
+   |            expected bool, found struct `std::ops::Range`
+   |
+   = note: expected type `bool`
+              found type `std::ops::Range<_>`
+
+error[E0308]: mismatched types
+  --> $DIR/disallowed-positions.rs:86:44
+   |
+LL |     if let Range { start: true, end } = t..&&false {}
+   |                                            ^^^^^^^ expected bool, found &&bool
+   |
+   = note: expected type `bool`
+              found type `&&bool`
+
+error[E0308]: mismatched types
+  --> $DIR/disallowed-positions.rs:86:8
+   |
+LL |     if let Range { start: true, end } = t..&&false {}
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected bool, found struct `std::ops::Range`
+   |
+   = note: expected type `bool`
+              found type `std::ops::Range<bool>`
+
+error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try`
+  --> $DIR/disallowed-positions.rs:42:20
+   |
+LL |         if let 0 = 0? {}
+   |                    ^^ the `?` operator cannot be applied to type `{integer}`
+   |
+   = help: the trait `std::ops::Try` is not implemented for `{integer}`
+   = note: required by `std::ops::Try::into_result`
+
+error[E0308]: mismatched types
+  --> $DIR/disallowed-positions.rs:96:11
+   |
+LL |     while &let 0 = 0 {}
+   |           ^^^^^^^^^^ expected bool, found &bool
+   |
+   = note: expected type `bool`
+              found type `&bool`
+
+error[E0614]: type `bool` cannot be dereferenced
+  --> $DIR/disallowed-positions.rs:100:11
+   |
+LL |     while *let 0 = 0 {}
+   |           ^^^^^^^^^^
+
+error[E0600]: cannot apply unary operator `-` to type `bool`
+  --> $DIR/disallowed-positions.rs:102:11
+   |
+LL |     while -let 0 = 0 {}
+   |           ^^^^^^^^^^ cannot apply unary operator `-`
+   |
+   = note: an implementation of `std::ops::Neg` might be missing for `bool`
+
+error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try`
+  --> $DIR/disallowed-positions.rs:110:11
+   |
+LL |     while (let 0 = 0)? {}
+   |           ^^^^^^^^^^^^ the `?` operator cannot be applied to type `bool`
+   |
+   = help: the trait `std::ops::Try` is not implemented for `bool`
+   = note: required by `std::ops::Try::into_result`
+
+error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
+  --> $DIR/disallowed-positions.rs:110:11
+   |
+LL |     while (let 0 = 0)? {}
+   |           ^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()`
+   |
+   = help: the trait `std::ops::Try` is not implemented for `()`
+   = note: required by `std::ops::Try::from_error`
+
+error[E0308]: mismatched types
+  --> $DIR/disallowed-positions.rs:120:11
+   |
+LL |     while x = let 0 = 0 {}
+   |           ^^^^^^^^^^^^^
+   |           |
+   |           expected bool, found ()
+   |           help: try comparing for equality: `x == let 0 = 0`
+   |
+   = note: expected type `bool`
+              found type `()`
+
+error[E0308]: mismatched types
+  --> $DIR/disallowed-positions.rs:123:11
+   |
+LL |     while true..(let 0 = 0) {}
+   |           ^^^^^^^^^^^^^^^^^ expected bool, found struct `std::ops::Range`
+   |
+   = note: expected type `bool`
+              found type `std::ops::Range<bool>`
+
+error[E0308]: mismatched types
+  --> $DIR/disallowed-positions.rs:125:11
+   |
+LL |     while ..(let 0 = 0) {}
+   |           ^^^^^^^^^^^^^ expected bool, found struct `std::ops::RangeTo`
+   |
+   = note: expected type `bool`
+              found type `std::ops::RangeTo<bool>`
+
+error[E0308]: mismatched types
+  --> $DIR/disallowed-positions.rs:127:11
+   |
+LL |     while (let 0 = 0).. {}
+   |           ^^^^^^^^^^^^^ expected bool, found struct `std::ops::RangeFrom`
+   |
+   = note: expected type `bool`
+              found type `std::ops::RangeFrom<bool>`
+
+error[E0308]: mismatched types
+  --> $DIR/disallowed-positions.rs:131:15
+   |
+LL |     while let Range { start: _, end: _ } = true..true && false {}
+   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^   ---- this match expression has type `bool`
+   |               |
+   |               expected bool, found struct `std::ops::Range`
+   |
+   = note: expected type `bool`
+              found type `std::ops::Range<_>`
+
+error[E0308]: mismatched types
+  --> $DIR/disallowed-positions.rs:131:11
+   |
+LL |     while let Range { start: _, end: _ } = true..true && false {}
+   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected bool, found struct `std::ops::Range`
+   |
+   = note: expected type `bool`
+              found type `std::ops::Range<bool>`
+
+error[E0308]: mismatched types
+  --> $DIR/disallowed-positions.rs:135:15
+   |
+LL |     while let Range { start: _, end: _ } = true..true || false {}
+   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^   ---- this match expression has type `bool`
+   |               |
+   |               expected bool, found struct `std::ops::Range`
+   |
+   = note: expected type `bool`
+              found type `std::ops::Range<_>`
+
+error[E0308]: mismatched types
+  --> $DIR/disallowed-positions.rs:135:11
+   |
+LL |     while let Range { start: _, end: _ } = true..true || false {}
+   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected bool, found struct `std::ops::Range`
+   |
+   = note: expected type `bool`
+              found type `std::ops::Range<bool>`
+
+error[E0308]: mismatched types
+  --> $DIR/disallowed-positions.rs:142:15
+   |
+LL |     while let Range { start: F, end } = F..|| true {}
+   |               ^^^^^^^^^^^^^^^^^^^^^^^ expected fn pointer, found struct `std::ops::Range`
+   |
+   = note: expected type `fn() -> bool`
+              found type `std::ops::Range<_>`
+
+error[E0308]: mismatched types
+  --> $DIR/disallowed-positions.rs:142:44
+   |
+LL |     while let Range { start: F, end } = F..|| true {}
+   |                                            ^^^^^^^ expected bool, found closure
+   |
+   = note: expected type `bool`
+              found type `[closure@$DIR/disallowed-positions.rs:142:44: 142:51]`
+
+error[E0308]: mismatched types
+  --> $DIR/disallowed-positions.rs:142:11
+   |
+LL |     while let Range { start: F, end } = F..|| true {}
+   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected bool, found struct `std::ops::Range`
+   |
+   = note: expected type `bool`
+              found type `std::ops::Range<bool>`
+
+error[E0308]: mismatched types
+  --> $DIR/disallowed-positions.rs:150:15
+   |
+LL |     while let Range { start: true, end } = t..&&false {}
+   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^   - this match expression has type `bool`
+   |               |
+   |               expected bool, found struct `std::ops::Range`
+   |
+   = note: expected type `bool`
+              found type `std::ops::Range<_>`
+
+error[E0308]: mismatched types
+  --> $DIR/disallowed-positions.rs:150:47
+   |
+LL |     while let Range { start: true, end } = t..&&false {}
+   |                                               ^^^^^^^ expected bool, found &&bool
+   |
+   = note: expected type `bool`
+              found type `&&bool`
+
+error[E0308]: mismatched types
+  --> $DIR/disallowed-positions.rs:150:11
+   |
+LL |     while let Range { start: true, end } = t..&&false {}
+   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected bool, found struct `std::ops::Range`
+   |
+   = note: expected type `bool`
+              found type `std::ops::Range<bool>`
+
+error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try`
+  --> $DIR/disallowed-positions.rs:106:23
+   |
+LL |         while let 0 = 0? {}
+   |                       ^^ the `?` operator cannot be applied to type `{integer}`
+   |
+   = help: the trait `std::ops::Try` is not implemented for `{integer}`
+   = note: required by `std::ops::Try::into_result`
+
+error[E0614]: type `bool` cannot be dereferenced
+  --> $DIR/disallowed-positions.rs:173:5
+   |
+LL |     *let 0 = 0;
+   |     ^^^^^^^^^^
+
+error[E0600]: cannot apply unary operator `-` to type `bool`
+  --> $DIR/disallowed-positions.rs:175:5
+   |
+LL |     -let 0 = 0;
+   |     ^^^^^^^^^^ cannot apply unary operator `-`
+   |
+   = note: an implementation of `std::ops::Neg` might be missing for `bool`
+
+error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try`
+  --> $DIR/disallowed-positions.rs:183:5
+   |
+LL |     (let 0 = 0)?;
+   |     ^^^^^^^^^^^^ the `?` operator cannot be applied to type `bool`
+   |
+   = help: the trait `std::ops::Try` is not implemented for `bool`
+   = note: required by `std::ops::Try::into_result`
+
+error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
+  --> $DIR/disallowed-positions.rs:183:5
+   |
+LL |     (let 0 = 0)?;
+   |     ^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()`
+   |
+   = help: the trait `std::ops::Try` is not implemented for `()`
+   = note: required by `std::ops::Try::from_error`
+
+error[E0308]: mismatched types
+  --> $DIR/disallowed-positions.rs:198:10
+   |
+LL |     (let Range { start: _, end: _ } = true..true || false);
+   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^   ---- this match expression has type `bool`
+   |          |
+   |          expected bool, found struct `std::ops::Range`
+   |
+   = note: expected type `bool`
+              found type `std::ops::Range<_>`
+
+error[E0308]: mismatched types
+  --> $DIR/disallowed-positions.rs:207:5
+   |
+LL | fn outside_if_and_while_expr() {
+   |                                - help: try adding a return type: `-> &bool`
+...
+LL |     &let 0 = 0
+   |     ^^^^^^^^^^ expected (), found &bool
+   |
+   = note: expected type `()`
+              found type `&bool`
+
+error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try`
+  --> $DIR/disallowed-positions.rs:179:17
+   |
+LL |         let 0 = 0?;
+   |                 ^^ the `?` operator cannot be applied to type `{integer}`
+   |
+   = help: the trait `std::ops::Try` is not implemented for `{integer}`
+   = note: required by `std::ops::Try::into_result`
+
+error[E0019]: constant contains unimplemented expression type
+  --> $DIR/disallowed-positions.rs:218:25
+   |
+LL |         true && let 1 = 1
+   |                         ^
+
+error[E0019]: constant contains unimplemented expression type
+  --> $DIR/disallowed-positions.rs:218:21
+   |
+LL |         true && let 1 = 1
+   |                     ^
+
+error[E0019]: constant contains unimplemented expression type
+  --> $DIR/disallowed-positions.rs:224:25
+   |
+LL |         true && let 1 = 1
+   |                         ^
+
+error[E0019]: constant contains unimplemented expression type
+  --> $DIR/disallowed-positions.rs:224:21
+   |
+LL |         true && let 1 = 1
+   |                     ^
+
+error[E0019]: constant contains unimplemented expression type
+  --> $DIR/disallowed-positions.rs:230:25
+   |
+LL |         true && let 1 = 1
+   |                         ^
+
+error[E0019]: constant contains unimplemented expression type
+  --> $DIR/disallowed-positions.rs:230:21
+   |
+LL |         true && let 1 = 1
+   |                     ^
+
+error: aborting due to 109 previous errors
+
+Some errors have detailed explanations: E0019, E0277, E0308, E0600, E0614.
+For more information about an error, try `rustc --explain E0019`.
diff --git a/src/test/ui/rfc-2497-if-let-chains/feature-gate.rs b/src/test/ui/rfc-2497-if-let-chains/feature-gate.rs
new file mode 100644
index 0000000..6498766
--- /dev/null
+++ b/src/test/ui/rfc-2497-if-let-chains/feature-gate.rs
@@ -0,0 +1,136 @@
+// gate-test-let_chains
+
+// Here we test feature gating for ´let_chains`.
+// See `disallowed-positions.rs` for the grammar
+// defining the language for gated allowed positions.
+
+#![allow(irrefutable_let_patterns)]
+
+use std::ops::Range;
+
+fn _if() {
+    if let 0 = 1 {} // Stable!
+
+    if (let 0 = 1) {}
+    //~^ ERROR `let` expressions in this position are experimental [E0658]
+    //~| ERROR `let` expressions are not supported here
+
+    if (((let 0 = 1))) {}
+    //~^ ERROR `let` expressions in this position are experimental [E0658]
+    //~| ERROR `let` expressions are not supported here
+
+    if true && let 0 = 1 {}
+    //~^ ERROR `let` expressions in this position are experimental [E0658]
+    //~| ERROR `let` expressions are not supported here
+
+    if let 0 = 1 && true {}
+    //~^ ERROR `let` expressions in this position are experimental [E0658]
+    //~| ERROR `let` expressions are not supported here
+
+    if (let 0 = 1) && true {}
+    //~^ ERROR `let` expressions in this position are experimental [E0658]
+    //~| ERROR `let` expressions are not supported here
+
+    if true && (let 0 = 1) {}
+    //~^ ERROR `let` expressions in this position are experimental [E0658]
+    //~| ERROR `let` expressions are not supported here
+
+    if (let 0 = 1) && (let 0 = 1) {}
+    //~^ ERROR `let` expressions in this position are experimental [E0658]
+    //~| ERROR `let` expressions in this position are experimental [E0658]
+    //~| ERROR `let` expressions are not supported here
+    //~| ERROR `let` expressions are not supported here
+
+    if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
+    //~^ ERROR `let` expressions in this position are experimental [E0658]
+    //~| ERROR `let` expressions in this position are experimental [E0658]
+    //~| ERROR `let` expressions in this position are experimental [E0658]
+    //~| ERROR `let` expressions in this position are experimental [E0658]
+    //~| ERROR `let` expressions in this position are experimental [E0658]
+    //~| ERROR `let` expressions are not supported here
+    //~| ERROR `let` expressions are not supported here
+    //~| ERROR `let` expressions are not supported here
+    //~| ERROR `let` expressions are not supported here
+    //~| ERROR `let` expressions are not supported here
+
+    if let Range { start: _, end: _ } = (true..true) && false {}
+    //~^ ERROR `let` expressions in this position are experimental [E0658]
+    //~| ERROR `let` expressions are not supported here
+}
+
+fn _while() {
+    while let 0 = 1 {} // Stable!
+
+    while (let 0 = 1) {}
+    //~^ ERROR `let` expressions in this position are experimental [E0658]
+    //~| ERROR `let` expressions are not supported here
+
+    while (((let 0 = 1))) {}
+    //~^ ERROR `let` expressions in this position are experimental [E0658]
+    //~| ERROR `let` expressions are not supported here
+
+    while true && let 0 = 1 {}
+    //~^ ERROR `let` expressions in this position are experimental [E0658]
+    //~| ERROR `let` expressions are not supported here
+
+    while let 0 = 1 && true {}
+    //~^ ERROR `let` expressions in this position are experimental [E0658]
+    //~| ERROR `let` expressions are not supported here
+
+    while (let 0 = 1) && true {}
+    //~^ ERROR `let` expressions in this position are experimental [E0658]
+    //~| ERROR `let` expressions are not supported here
+
+    while true && (let 0 = 1) {}
+    //~^ ERROR `let` expressions in this position are experimental [E0658]
+    //~| ERROR `let` expressions are not supported here
+
+    while (let 0 = 1) && (let 0 = 1) {}
+    //~^ ERROR `let` expressions in this position are experimental [E0658]
+    //~| ERROR `let` expressions in this position are experimental [E0658]
+    //~| ERROR `let` expressions are not supported here
+    //~| ERROR `let` expressions are not supported here
+
+    while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
+    //~^ ERROR `let` expressions in this position are experimental [E0658]
+    //~| ERROR `let` expressions in this position are experimental [E0658]
+    //~| ERROR `let` expressions in this position are experimental [E0658]
+    //~| ERROR `let` expressions in this position are experimental [E0658]
+    //~| ERROR `let` expressions in this position are experimental [E0658]
+    //~| ERROR `let` expressions are not supported here
+    //~| ERROR `let` expressions are not supported here
+    //~| ERROR `let` expressions are not supported here
+    //~| ERROR `let` expressions are not supported here
+    //~| ERROR `let` expressions are not supported here
+
+    while let Range { start: _, end: _ } = (true..true) && false {}
+    //~^ ERROR `let` expressions in this position are experimental [E0658]
+    //~| ERROR `let` expressions are not supported here
+}
+
+fn _macros() {
+    macro_rules! noop_expr { ($e:expr) => {}; }
+
+    noop_expr!((let 0 = 1));
+    //~^ ERROR `let` expressions in this position are experimental [E0658]
+
+    macro_rules! use_expr {
+        ($e:expr) => {
+            if $e {}
+            while $e {}
+        }
+    }
+    use_expr!((let 0 = 1 && 0 == 0));
+    //~^ ERROR `let` expressions in this position are experimental [E0658]
+    //~| ERROR `let` expressions are not supported here
+    use_expr!((let 0 = 1));
+    //~^ ERROR `let` expressions in this position are experimental [E0658]
+    //~| ERROR `let` expressions are not supported here
+    #[cfg(FALSE)] (let 0 = 1);
+    //~^ ERROR `let` expressions in this position are experimental [E0658]
+    use_expr!(let 0 = 1);
+    //~^ ERROR no rules expected the token `let`
+    // ^--- FIXME(53667): Consider whether `Let` can be added to `ident_can_begin_expr`.
+}
+
+fn main() {}
diff --git a/src/test/ui/rfc-2497-if-let-chains/feature-gate.stderr b/src/test/ui/rfc-2497-if-let-chains/feature-gate.stderr
new file mode 100644
index 0000000..6167427
--- /dev/null
+++ b/src/test/ui/rfc-2497-if-let-chains/feature-gate.stderr
@@ -0,0 +1,570 @@
+error: no rules expected the token `let`
+  --> $DIR/feature-gate.rs:131:15
+   |
+LL |     macro_rules! use_expr {
+   |     --------------------- when calling this macro
+...
+LL |     use_expr!(let 0 = 1);
+   |               ^^^ no rules expected this token in macro call
+
+error[E0658]: `let` expressions in this position are experimental
+  --> $DIR/feature-gate.rs:14:9
+   |
+LL |     if (let 0 = 1) {}
+   |         ^^^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/53667
+   = help: add #![feature(let_chains)] to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are experimental
+  --> $DIR/feature-gate.rs:18:11
+   |
+LL |     if (((let 0 = 1))) {}
+   |           ^^^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/53667
+   = help: add #![feature(let_chains)] to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are experimental
+  --> $DIR/feature-gate.rs:22:16
+   |
+LL |     if true && let 0 = 1 {}
+   |                ^^^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/53667
+   = help: add #![feature(let_chains)] to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are experimental
+  --> $DIR/feature-gate.rs:26:8
+   |
+LL |     if let 0 = 1 && true {}
+   |        ^^^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/53667
+   = help: add #![feature(let_chains)] to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are experimental
+  --> $DIR/feature-gate.rs:30:9
+   |
+LL |     if (let 0 = 1) && true {}
+   |         ^^^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/53667
+   = help: add #![feature(let_chains)] to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are experimental
+  --> $DIR/feature-gate.rs:34:17
+   |
+LL |     if true && (let 0 = 1) {}
+   |                 ^^^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/53667
+   = help: add #![feature(let_chains)] to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are experimental
+  --> $DIR/feature-gate.rs:38:9
+   |
+LL |     if (let 0 = 1) && (let 0 = 1) {}
+   |         ^^^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/53667
+   = help: add #![feature(let_chains)] to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are experimental
+  --> $DIR/feature-gate.rs:38:24
+   |
+LL |     if (let 0 = 1) && (let 0 = 1) {}
+   |                        ^^^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/53667
+   = help: add #![feature(let_chains)] to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are experimental
+  --> $DIR/feature-gate.rs:44:8
+   |
+LL |     if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
+   |        ^^^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/53667
+   = help: add #![feature(let_chains)] to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are experimental
+  --> $DIR/feature-gate.rs:44:21
+   |
+LL |     if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
+   |                     ^^^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/53667
+   = help: add #![feature(let_chains)] to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are experimental
+  --> $DIR/feature-gate.rs:44:35
+   |
+LL |     if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
+   |                                   ^^^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/53667
+   = help: add #![feature(let_chains)] to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are experimental
+  --> $DIR/feature-gate.rs:44:48
+   |
+LL |     if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
+   |                                                ^^^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/53667
+   = help: add #![feature(let_chains)] to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are experimental
+  --> $DIR/feature-gate.rs:44:61
+   |
+LL |     if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
+   |                                                             ^^^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/53667
+   = help: add #![feature(let_chains)] to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are experimental
+  --> $DIR/feature-gate.rs:56:8
+   |
+LL |     if let Range { start: _, end: _ } = (true..true) && false {}
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/53667
+   = help: add #![feature(let_chains)] to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are experimental
+  --> $DIR/feature-gate.rs:64:12
+   |
+LL |     while (let 0 = 1) {}
+   |            ^^^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/53667
+   = help: add #![feature(let_chains)] to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are experimental
+  --> $DIR/feature-gate.rs:68:14
+   |
+LL |     while (((let 0 = 1))) {}
+   |              ^^^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/53667
+   = help: add #![feature(let_chains)] to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are experimental
+  --> $DIR/feature-gate.rs:72:19
+   |
+LL |     while true && let 0 = 1 {}
+   |                   ^^^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/53667
+   = help: add #![feature(let_chains)] to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are experimental
+  --> $DIR/feature-gate.rs:76:11
+   |
+LL |     while let 0 = 1 && true {}
+   |           ^^^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/53667
+   = help: add #![feature(let_chains)] to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are experimental
+  --> $DIR/feature-gate.rs:80:12
+   |
+LL |     while (let 0 = 1) && true {}
+   |            ^^^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/53667
+   = help: add #![feature(let_chains)] to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are experimental
+  --> $DIR/feature-gate.rs:84:20
+   |
+LL |     while true && (let 0 = 1) {}
+   |                    ^^^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/53667
+   = help: add #![feature(let_chains)] to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are experimental
+  --> $DIR/feature-gate.rs:88:12
+   |
+LL |     while (let 0 = 1) && (let 0 = 1) {}
+   |            ^^^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/53667
+   = help: add #![feature(let_chains)] to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are experimental
+  --> $DIR/feature-gate.rs:88:27
+   |
+LL |     while (let 0 = 1) && (let 0 = 1) {}
+   |                           ^^^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/53667
+   = help: add #![feature(let_chains)] to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are experimental
+  --> $DIR/feature-gate.rs:94:11
+   |
+LL |     while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
+   |           ^^^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/53667
+   = help: add #![feature(let_chains)] to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are experimental
+  --> $DIR/feature-gate.rs:94:24
+   |
+LL |     while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
+   |                        ^^^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/53667
+   = help: add #![feature(let_chains)] to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are experimental
+  --> $DIR/feature-gate.rs:94:38
+   |
+LL |     while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
+   |                                      ^^^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/53667
+   = help: add #![feature(let_chains)] to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are experimental
+  --> $DIR/feature-gate.rs:94:51
+   |
+LL |     while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
+   |                                                   ^^^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/53667
+   = help: add #![feature(let_chains)] to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are experimental
+  --> $DIR/feature-gate.rs:94:64
+   |
+LL |     while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
+   |                                                                ^^^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/53667
+   = help: add #![feature(let_chains)] to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are experimental
+  --> $DIR/feature-gate.rs:106:11
+   |
+LL |     while let Range { start: _, end: _ } = (true..true) && false {}
+   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/53667
+   = help: add #![feature(let_chains)] to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are experimental
+  --> $DIR/feature-gate.rs:129:20
+   |
+LL |     #[cfg(FALSE)] (let 0 = 1);
+   |                    ^^^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/53667
+   = help: add #![feature(let_chains)] to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are experimental
+  --> $DIR/feature-gate.rs:114:17
+   |
+LL |     noop_expr!((let 0 = 1));
+   |                 ^^^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/53667
+   = help: add #![feature(let_chains)] to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are experimental
+  --> $DIR/feature-gate.rs:123:16
+   |
+LL |     use_expr!((let 0 = 1 && 0 == 0));
+   |                ^^^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/53667
+   = help: add #![feature(let_chains)] to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are experimental
+  --> $DIR/feature-gate.rs:126:16
+   |
+LL |     use_expr!((let 0 = 1));
+   |                ^^^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/53667
+   = help: add #![feature(let_chains)] to the crate attributes to enable
+
+error: `let` expressions are not supported here
+  --> $DIR/feature-gate.rs:14:9
+   |
+LL |     if (let 0 = 1) {}
+   |         ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/feature-gate.rs:18:11
+   |
+LL |     if (((let 0 = 1))) {}
+   |           ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/feature-gate.rs:22:16
+   |
+LL |     if true && let 0 = 1 {}
+   |                ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/feature-gate.rs:26:8
+   |
+LL |     if let 0 = 1 && true {}
+   |        ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/feature-gate.rs:30:9
+   |
+LL |     if (let 0 = 1) && true {}
+   |         ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/feature-gate.rs:34:17
+   |
+LL |     if true && (let 0 = 1) {}
+   |                 ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/feature-gate.rs:38:9
+   |
+LL |     if (let 0 = 1) && (let 0 = 1) {}
+   |         ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/feature-gate.rs:38:24
+   |
+LL |     if (let 0 = 1) && (let 0 = 1) {}
+   |                        ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/feature-gate.rs:44:8
+   |
+LL |     if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
+   |        ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/feature-gate.rs:44:21
+   |
+LL |     if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
+   |                     ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/feature-gate.rs:44:35
+   |
+LL |     if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
+   |                                   ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/feature-gate.rs:44:48
+   |
+LL |     if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
+   |                                                ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/feature-gate.rs:44:61
+   |
+LL |     if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
+   |                                                             ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/feature-gate.rs:56:8
+   |
+LL |     if let Range { start: _, end: _ } = (true..true) && false {}
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/feature-gate.rs:64:12
+   |
+LL |     while (let 0 = 1) {}
+   |            ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/feature-gate.rs:68:14
+   |
+LL |     while (((let 0 = 1))) {}
+   |              ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/feature-gate.rs:72:19
+   |
+LL |     while true && let 0 = 1 {}
+   |                   ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/feature-gate.rs:76:11
+   |
+LL |     while let 0 = 1 && true {}
+   |           ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/feature-gate.rs:80:12
+   |
+LL |     while (let 0 = 1) && true {}
+   |            ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/feature-gate.rs:84:20
+   |
+LL |     while true && (let 0 = 1) {}
+   |                    ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/feature-gate.rs:88:12
+   |
+LL |     while (let 0 = 1) && (let 0 = 1) {}
+   |            ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/feature-gate.rs:88:27
+   |
+LL |     while (let 0 = 1) && (let 0 = 1) {}
+   |                           ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/feature-gate.rs:94:11
+   |
+LL |     while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
+   |           ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/feature-gate.rs:94:24
+   |
+LL |     while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
+   |                        ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/feature-gate.rs:94:38
+   |
+LL |     while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
+   |                                      ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/feature-gate.rs:94:51
+   |
+LL |     while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
+   |                                                   ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/feature-gate.rs:94:64
+   |
+LL |     while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
+   |                                                                ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/feature-gate.rs:106:11
+   |
+LL |     while let Range { start: _, end: _ } = (true..true) && false {}
+   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/feature-gate.rs:123:16
+   |
+LL |     use_expr!((let 0 = 1 && 0 == 0));
+   |                ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: `let` expressions are not supported here
+  --> $DIR/feature-gate.rs:126:16
+   |
+LL |     use_expr!((let 0 = 1));
+   |                ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+error: aborting due to 63 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/rfc-2497-if-let-chains/protect-precedences.rs b/src/test/ui/rfc-2497-if-let-chains/protect-precedences.rs
new file mode 100644
index 0000000..1de4e5b
--- /dev/null
+++ b/src/test/ui/rfc-2497-if-let-chains/protect-precedences.rs
@@ -0,0 +1,18 @@
+// run-pass
+
+#![allow(irrefutable_let_patterns)]
+
+use std::ops::Range;
+
+fn main() {
+    let x: bool;
+    // This should associate as: `(x = (true && false));`.
+    x = true && false;
+    assert!(!x);
+
+    fn _f1() -> bool {
+        // Should associate as `(let _ = (return (true && false)))`.
+        if let _ = return true && false {};
+    }
+    assert!(!_f1());
+}
diff --git a/src/test/ui/rfc-2497-if-let-chains/syntax-ambiguity-2015.rs b/src/test/ui/rfc-2497-if-let-chains/syntax-ambiguity-2015.rs
deleted file mode 100644
index d79798d..0000000
--- a/src/test/ui/rfc-2497-if-let-chains/syntax-ambiguity-2015.rs
+++ /dev/null
@@ -1,38 +0,0 @@
-// edition:2015
-
-// Enabling `ireffutable_let_patterns` isn't necessary for what this tests, but it makes coming up
-// with examples easier.
-
-#[allow(irrefutable_let_patterns)]
-fn main() {
-    use std::ops::Range;
-
-    if let Range { start: _, end: _ } = true..true && false { }
-    //~^ ERROR ambiguous use of `&&`
-
-    if let Range { start: _, end: _ } = true..true || false { }
-    //~^ ERROR ambiguous use of `||`
-
-    while let Range { start: _, end: _ } = true..true && false { }
-    //~^ ERROR ambiguous use of `&&`
-
-    while let Range { start: _, end: _ } = true..true || false { }
-    //~^ ERROR ambiguous use of `||`
-
-    if let true = false && false { }
-    //~^ ERROR ambiguous use of `&&`
-
-    while let true = (1 == 2) && false { }
-    //~^ ERROR ambiguous use of `&&`
-
-    // The following cases are not an error as parenthesis are used to
-    // clarify intent:
-
-    if let Range { start: _, end: _ } = true..(true || false) { }
-
-    if let Range { start: _, end: _ } = true..(true && false) { }
-
-    while let Range { start: _, end: _ } = true..(true || false) { }
-
-    while let Range { start: _, end: _ } = true..(true && false) { }
-}
diff --git a/src/test/ui/rfc-2497-if-let-chains/syntax-ambiguity-2015.stderr b/src/test/ui/rfc-2497-if-let-chains/syntax-ambiguity-2015.stderr
deleted file mode 100644
index 2cd59fe..0000000
--- a/src/test/ui/rfc-2497-if-let-chains/syntax-ambiguity-2015.stderr
+++ /dev/null
@@ -1,56 +0,0 @@
-error: ambiguous use of `&&`
-  --> $DIR/syntax-ambiguity-2015.rs:10:47
-   |
-LL |     if let Range { start: _, end: _ } = true..true && false { }
-   |                                               ^^^^^^^^^^^^^ help: consider adding parentheses: `(true && false)`
-   |
-   = note: this will be a error until the `let_chains` feature is stabilized
-   = note: see rust-lang/rust#53668 for more information
-
-error: ambiguous use of `||`
-  --> $DIR/syntax-ambiguity-2015.rs:13:47
-   |
-LL |     if let Range { start: _, end: _ } = true..true || false { }
-   |                                               ^^^^^^^^^^^^^ help: consider adding parentheses: `(true || false)`
-   |
-   = note: this will be a error until the `let_chains` feature is stabilized
-   = note: see rust-lang/rust#53668 for more information
-
-error: ambiguous use of `&&`
-  --> $DIR/syntax-ambiguity-2015.rs:16:50
-   |
-LL |     while let Range { start: _, end: _ } = true..true && false { }
-   |                                                  ^^^^^^^^^^^^^ help: consider adding parentheses: `(true && false)`
-   |
-   = note: this will be a error until the `let_chains` feature is stabilized
-   = note: see rust-lang/rust#53668 for more information
-
-error: ambiguous use of `||`
-  --> $DIR/syntax-ambiguity-2015.rs:19:50
-   |
-LL |     while let Range { start: _, end: _ } = true..true || false { }
-   |                                                  ^^^^^^^^^^^^^ help: consider adding parentheses: `(true || false)`
-   |
-   = note: this will be a error until the `let_chains` feature is stabilized
-   = note: see rust-lang/rust#53668 for more information
-
-error: ambiguous use of `&&`
-  --> $DIR/syntax-ambiguity-2015.rs:22:19
-   |
-LL |     if let true = false && false { }
-   |                   ^^^^^^^^^^^^^^ help: consider adding parentheses: `(false && false)`
-   |
-   = note: this will be a error until the `let_chains` feature is stabilized
-   = note: see rust-lang/rust#53668 for more information
-
-error: ambiguous use of `&&`
-  --> $DIR/syntax-ambiguity-2015.rs:25:22
-   |
-LL |     while let true = (1 == 2) && false { }
-   |                      ^^^^^^^^^^^^^^^^^ help: consider adding parentheses: `((1 == 2) && false)`
-   |
-   = note: this will be a error until the `let_chains` feature is stabilized
-   = note: see rust-lang/rust#53668 for more information
-
-error: aborting due to 6 previous errors
-
diff --git a/src/test/ui/rfc-2497-if-let-chains/syntax-ambiguity-2018.rs b/src/test/ui/rfc-2497-if-let-chains/syntax-ambiguity-2018.rs
deleted file mode 100644
index 687bf65..0000000
--- a/src/test/ui/rfc-2497-if-let-chains/syntax-ambiguity-2018.rs
+++ /dev/null
@@ -1,38 +0,0 @@
-// edition:2018
-
-// Enabling `ireffutable_let_patterns` isn't necessary for what this tests, but it makes coming up
-// with examples easier.
-
-#[allow(irrefutable_let_patterns)]
-fn main() {
-    use std::ops::Range;
-
-    if let Range { start: _, end: _ } = true..true && false { }
-    //~^ ERROR ambiguous use of `&&`
-
-    if let Range { start: _, end: _ } = true..true || false { }
-    //~^ ERROR ambiguous use of `||`
-
-    while let Range { start: _, end: _ } = true..true && false { }
-    //~^ ERROR ambiguous use of `&&`
-
-    while let Range { start: _, end: _ } = true..true || false { }
-    //~^ ERROR ambiguous use of `||`
-
-    if let true = false && false { }
-    //~^ ERROR ambiguous use of `&&`
-
-    while let true = (1 == 2) && false { }
-    //~^ ERROR ambiguous use of `&&`
-
-    // The following cases are not an error as parenthesis are used to
-    // clarify intent:
-
-    if let Range { start: _, end: _ } = true..(true || false) { }
-
-    if let Range { start: _, end: _ } = true..(true && false) { }
-
-    while let Range { start: _, end: _ } = true..(true || false) { }
-
-    while let Range { start: _, end: _ } = true..(true && false) { }
-}
diff --git a/src/test/ui/rfc-2497-if-let-chains/syntax-ambiguity-2018.stderr b/src/test/ui/rfc-2497-if-let-chains/syntax-ambiguity-2018.stderr
deleted file mode 100644
index cbba2d7..0000000
--- a/src/test/ui/rfc-2497-if-let-chains/syntax-ambiguity-2018.stderr
+++ /dev/null
@@ -1,56 +0,0 @@
-error: ambiguous use of `&&`
-  --> $DIR/syntax-ambiguity-2018.rs:10:47
-   |
-LL |     if let Range { start: _, end: _ } = true..true && false { }
-   |                                               ^^^^^^^^^^^^^ help: consider adding parentheses: `(true && false)`
-   |
-   = note: this will be a error until the `let_chains` feature is stabilized
-   = note: see rust-lang/rust#53668 for more information
-
-error: ambiguous use of `||`
-  --> $DIR/syntax-ambiguity-2018.rs:13:47
-   |
-LL |     if let Range { start: _, end: _ } = true..true || false { }
-   |                                               ^^^^^^^^^^^^^ help: consider adding parentheses: `(true || false)`
-   |
-   = note: this will be a error until the `let_chains` feature is stabilized
-   = note: see rust-lang/rust#53668 for more information
-
-error: ambiguous use of `&&`
-  --> $DIR/syntax-ambiguity-2018.rs:16:50
-   |
-LL |     while let Range { start: _, end: _ } = true..true && false { }
-   |                                                  ^^^^^^^^^^^^^ help: consider adding parentheses: `(true && false)`
-   |
-   = note: this will be a error until the `let_chains` feature is stabilized
-   = note: see rust-lang/rust#53668 for more information
-
-error: ambiguous use of `||`
-  --> $DIR/syntax-ambiguity-2018.rs:19:50
-   |
-LL |     while let Range { start: _, end: _ } = true..true || false { }
-   |                                                  ^^^^^^^^^^^^^ help: consider adding parentheses: `(true || false)`
-   |
-   = note: this will be a error until the `let_chains` feature is stabilized
-   = note: see rust-lang/rust#53668 for more information
-
-error: ambiguous use of `&&`
-  --> $DIR/syntax-ambiguity-2018.rs:22:19
-   |
-LL |     if let true = false && false { }
-   |                   ^^^^^^^^^^^^^^ help: consider adding parentheses: `(false && false)`
-   |
-   = note: this will be a error until the `let_chains` feature is stabilized
-   = note: see rust-lang/rust#53668 for more information
-
-error: ambiguous use of `&&`
-  --> $DIR/syntax-ambiguity-2018.rs:25:22
-   |
-LL |     while let true = (1 == 2) && false { }
-   |                      ^^^^^^^^^^^^^^^^^ help: consider adding parentheses: `((1 == 2) && false)`
-   |
-   = note: this will be a error until the `let_chains` feature is stabilized
-   = note: see rust-lang/rust#53668 for more information
-
-error: aborting due to 6 previous errors
-
diff --git a/src/test/ui/single-use-lifetime/one-use-in-fn-return.rs b/src/test/ui/single-use-lifetime/one-use-in-fn-return.rs
index 876a0a5..92b25cb 100644
--- a/src/test/ui/single-use-lifetime/one-use-in-fn-return.rs
+++ b/src/test/ui/single-use-lifetime/one-use-in-fn-return.rs
@@ -1,20 +1,16 @@
-// compile-pass
-
-#![deny(single_use_lifetimes)]
-#![allow(dead_code)]
-#![allow(unused_variables)]
-
 // Test that we DO NOT warn when lifetime name is used only
 // once in a fn return type -- using `'_` is not legal there,
 // as it must refer back to an argument.
 //
 // (Normally, using `'static` would be preferred, but there are
 // times when that is not what you want.)
-//
-// run-pass
+
+// compile-pass
+
+#![deny(single_use_lifetimes)]
 
 fn b<'a>() -> &'a u32 { // OK: used only in return type
     &22
 }
 
-fn main() { }
+fn main() {}
diff --git a/src/test/ui/traits/trait-alias/trait-alias-only-maybe-bound.rs b/src/test/ui/traits/trait-alias/trait-alias-only-maybe-bound.rs
index d6c611d..e4abf31 100644
--- a/src/test/ui/traits/trait-alias/trait-alias-only-maybe-bound.rs
+++ b/src/test/ui/traits/trait-alias/trait-alias-only-maybe-bound.rs
@@ -11,12 +11,12 @@
 
 // Straight list expansion:
 type _T0 = dyn _1;
-//~^ ERROR at least one non-builtin trait is required for an object type [E0224]
+//~^ ERROR at least one trait is required for an object type [E0224]
 
 // Twice:
 trait _2 = _1 + _1;
 
 type _T1 = dyn _2;
-//~^ ERROR at least one non-builtin trait is required for an object type [E0224]
+//~^ ERROR at least one trait is required for an object type [E0224]
 
 fn main() {}
diff --git a/src/test/ui/traits/trait-alias/trait-alias-only-maybe-bound.stderr b/src/test/ui/traits/trait-alias/trait-alias-only-maybe-bound.stderr
index d4f77200..6de79fa 100644
--- a/src/test/ui/traits/trait-alias/trait-alias-only-maybe-bound.stderr
+++ b/src/test/ui/traits/trait-alias/trait-alias-only-maybe-bound.stderr
@@ -1,10 +1,10 @@
-error[E0224]: at least one non-builtin trait is required for an object type
+error[E0224]: at least one trait is required for an object type
   --> $DIR/trait-alias-only-maybe-bound.rs:13:12
    |
 LL | type _T0 = dyn _1;
    |            ^^^^^^
 
-error[E0224]: at least one non-builtin trait is required for an object type
+error[E0224]: at least one trait is required for an object type
   --> $DIR/trait-alias-only-maybe-bound.rs:19:12
    |
 LL | type _T1 = dyn _2;
diff --git a/src/test/ui/traits/trait-object-macro-matcher.rs b/src/test/ui/traits/trait-object-macro-matcher.rs
index 5ec1572..a685256 100644
--- a/src/test/ui/traits/trait-object-macro-matcher.rs
+++ b/src/test/ui/traits/trait-object-macro-matcher.rs
@@ -8,5 +8,5 @@
     m!(dyn Copy + Send + 'static);
     //~^ ERROR the trait `std::marker::Copy` cannot be made into an object
     m!(dyn 'static + Send);
-    m!(dyn 'static +); //~ ERROR at least one non-builtin trait is required for an object type
+    m!(dyn 'static +); //~ ERROR at least one trait is required for an object type
 }
diff --git a/src/test/ui/traits/trait-object-macro-matcher.stderr b/src/test/ui/traits/trait-object-macro-matcher.stderr
index 0b84c3d..6b5effa 100644
--- a/src/test/ui/traits/trait-object-macro-matcher.stderr
+++ b/src/test/ui/traits/trait-object-macro-matcher.stderr
@@ -1,4 +1,4 @@
-error[E0224]: at least one non-builtin trait is required for an object type
+error[E0224]: at least one trait is required for an object type
   --> $DIR/trait-object-macro-matcher.rs:11:8
    |
 LL |     m!(dyn 'static +);
diff --git a/src/test/ui/traits/trait-object-vs-lifetime-2.rs b/src/test/ui/traits/trait-object-vs-lifetime-2.rs
index 8a9b8e7..0b33dc7 100644
--- a/src/test/ui/traits/trait-object-vs-lifetime-2.rs
+++ b/src/test/ui/traits/trait-object-vs-lifetime-2.rs
@@ -5,7 +5,7 @@
 fn g() where
     'static: 'static,
     dyn 'static +: 'static + Copy,
-    //~^ ERROR at least one non-builtin trait is required for an object type
+    //~^ ERROR at least one trait is required for an object type
 {}
 
 fn main() {}
diff --git a/src/test/ui/traits/trait-object-vs-lifetime-2.stderr b/src/test/ui/traits/trait-object-vs-lifetime-2.stderr
index ef5e240..014d380 100644
--- a/src/test/ui/traits/trait-object-vs-lifetime-2.stderr
+++ b/src/test/ui/traits/trait-object-vs-lifetime-2.stderr
@@ -1,4 +1,4 @@
-error[E0224]: at least one non-builtin trait is required for an object type
+error[E0224]: at least one trait is required for an object type
   --> $DIR/trait-object-vs-lifetime-2.rs:7:5
    |
 LL |     dyn 'static +: 'static + Copy,
diff --git a/src/test/ui/traits/trait-object-vs-lifetime.rs b/src/test/ui/traits/trait-object-vs-lifetime.rs
index 803b293..e0ff734 100644
--- a/src/test/ui/traits/trait-object-vs-lifetime.rs
+++ b/src/test/ui/traits/trait-object-vs-lifetime.rs
@@ -7,11 +7,11 @@
     // `'static` is a lifetime argument, `'static +` is a type argument
     let _: S<'static, u8>;
     let _: S<'static, dyn 'static +>;
-    //~^ at least one non-builtin trait is required for an object type
+    //~^ at least one trait is required for an object type
     let _: S<'static, 'static>;
     //~^ ERROR wrong number of lifetime arguments: expected 1, found 2
     //~| ERROR wrong number of type arguments: expected 1, found 0
     let _: S<dyn 'static +, 'static>;
     //~^ ERROR lifetime arguments must be declared prior to type arguments
-    //~| ERROR at least one non-builtin trait is required for an object type
+    //~| ERROR at least one trait is required for an object type
 }
diff --git a/src/test/ui/traits/trait-object-vs-lifetime.stderr b/src/test/ui/traits/trait-object-vs-lifetime.stderr
index be1af59..be19587 100644
--- a/src/test/ui/traits/trait-object-vs-lifetime.stderr
+++ b/src/test/ui/traits/trait-object-vs-lifetime.stderr
@@ -4,7 +4,7 @@
 LL |     let _: S<dyn 'static +, 'static>;
    |                             ^^^^^^^
 
-error[E0224]: at least one non-builtin trait is required for an object type
+error[E0224]: at least one trait is required for an object type
   --> $DIR/trait-object-vs-lifetime.rs:9:23
    |
 LL |     let _: S<'static, dyn 'static +>;
@@ -22,7 +22,7 @@
 LL |     let _: S<'static, 'static>;
    |            ^^^^^^^^^^^^^^^^^^^ expected 1 type argument
 
-error[E0224]: at least one non-builtin trait is required for an object type
+error[E0224]: at least one trait is required for an object type
   --> $DIR/trait-object-vs-lifetime.rs:14:14
    |
 LL |     let _: S<dyn 'static +, 'static>;
diff --git a/src/test/ui/traits/wf-trait-object-only-maybe-bound.rs b/src/test/ui/traits/wf-trait-object-only-maybe-bound.rs
index 1b83d24..3e6db3e 100644
--- a/src/test/ui/traits/wf-trait-object-only-maybe-bound.rs
+++ b/src/test/ui/traits/wf-trait-object-only-maybe-bound.rs
@@ -1,7 +1,7 @@
 // Test that `dyn ?Sized` (i.e., a trait object with only a maybe buond) is not allowed.
 
 type _0 = dyn ?Sized;
-//~^ ERROR at least one non-builtin trait is required for an object type [E0224]
+//~^ ERROR at least one trait is required for an object type [E0224]
 //~| ERROR ?Trait` is not permitted in trait object types
 
 fn main() {}
diff --git a/src/test/ui/traits/wf-trait-object-only-maybe-bound.stderr b/src/test/ui/traits/wf-trait-object-only-maybe-bound.stderr
index 0cfb389..8cc97ad 100644
--- a/src/test/ui/traits/wf-trait-object-only-maybe-bound.stderr
+++ b/src/test/ui/traits/wf-trait-object-only-maybe-bound.stderr
@@ -4,7 +4,7 @@
 LL | type _0 = dyn ?Sized;
    |               ^^^^^^
 
-error[E0224]: at least one non-builtin trait is required for an object type
+error[E0224]: at least one trait is required for an object type
   --> $DIR/wf-trait-object-only-maybe-bound.rs:3:11
    |
 LL | type _0 = dyn ?Sized;
diff --git a/src/tools/clippy b/src/tools/clippy
index 149a988..5a11ed7 160000
--- a/src/tools/clippy
+++ b/src/tools/clippy
@@ -1 +1 @@
-Subproject commit 149a988146e1e23849338cefe8df7823e1ca3853
+Subproject commit 5a11ed7b92cc4cf40a4568a8fc1ff54b198c333b
diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs
index ab5594f..6ce7461 100644
--- a/src/tools/compiletest/src/header.rs
+++ b/src/tools/compiletest/src/header.rs
@@ -290,6 +290,13 @@
     }
 }
 
+#[derive(Clone, Copy, PartialEq, Debug)]
+pub enum PassMode {
+    Check,
+    Build,
+    Run,
+}
+
 #[derive(Clone, Debug)]
 pub struct TestProps {
     // Lines that should be expected, in order, on standard out
@@ -349,14 +356,10 @@
     // testing harness and used when generating compilation
     // arguments. (In particular, it propagates to the aux-builds.)
     pub incremental_dir: Option<PathBuf>,
-    // Specifies that a test must actually compile without errors.
-    pub compile_pass: bool,
+    // How far should the test proceed while still passing.
+    pub pass_mode: Option<PassMode>,
     // rustdoc will test the output of the `--test` option
     pub check_test_line_numbers_match: bool,
-    // The test must be compiled and run successfully. Only used in UI tests for now.
-    pub run_pass: bool,
-    // Skip any codegen step and running the executable. Only for run-pass.
-    pub skip_codegen: bool,
     // Do not pass `-Z ui-testing` to UI tests
     pub disable_ui_testing_normalization: bool,
     // customized normalization rules
@@ -396,10 +399,8 @@
             pretty_compare_only: false,
             forbid_output: vec![],
             incremental_dir: None,
-            compile_pass: false,
+            pass_mode: None,
             check_test_line_numbers_match: false,
-            run_pass: false,
-            skip_codegen: false,
             disable_ui_testing_normalization: false,
             normalize_stdout: vec![],
             normalize_stderr: vec![],
@@ -525,18 +526,7 @@
                 self.check_test_line_numbers_match = config.parse_check_test_line_numbers_match(ln);
             }
 
-            if !self.run_pass {
-                self.run_pass = config.parse_run_pass(ln);
-            }
-
-            if !self.compile_pass {
-                // run-pass implies compile_pass
-                self.compile_pass = config.parse_compile_pass(ln) || self.run_pass;
-            }
-
-            if !self.skip_codegen {
-                self.skip_codegen = config.parse_skip_codegen(ln);
-            }
+            self.update_pass_mode(ln, cfg, config);
 
             if !self.disable_ui_testing_normalization {
                 self.disable_ui_testing_normalization =
@@ -583,6 +573,41 @@
             }
         }
     }
+
+    fn update_pass_mode(&mut self, ln: &str, revision: Option<&str>, config: &Config) {
+        let check_no_run = |s| {
+            if config.mode != Mode::Ui && config.mode != Mode::Incremental {
+                panic!("`{}` header is only supported in UI and incremental tests", s);
+            }
+            if config.mode == Mode::Incremental &&
+                !revision.map_or(false, |r| r.starts_with("cfail")) &&
+                !self.revisions.iter().all(|r| r.starts_with("cfail")) {
+                panic!("`{}` header is only supported in `cfail` incremental tests", s);
+            }
+        };
+        let pass_mode = if config.parse_name_directive(ln, "check-pass") {
+            check_no_run("check-pass");
+            Some(PassMode::Check)
+        } else if config.parse_name_directive(ln, "build-pass") {
+            check_no_run("build-pass");
+            Some(PassMode::Build)
+        } else if config.parse_name_directive(ln, "compile-pass") /* compatibility */ {
+            check_no_run("compile-pass");
+            Some(PassMode::Build)
+        } else if config.parse_name_directive(ln, "run-pass") {
+            if config.mode != Mode::Ui && config.mode != Mode::RunPass /* compatibility */ {
+                panic!("`run-pass` header is only supported in UI tests")
+            }
+            Some(PassMode::Run)
+        } else {
+            None
+        };
+        match (self.pass_mode, pass_mode) {
+            (None, Some(_)) => self.pass_mode = pass_mode,
+            (Some(_), Some(_)) => panic!("multiple `*-pass` headers in a single test"),
+            (_, None) => {}
+        }
+    }
 }
 
 fn iter_header(testfile: &Path, cfg: Option<&str>, it: &mut dyn FnMut(&str)) {
@@ -710,10 +735,6 @@
         }
     }
 
-    fn parse_compile_pass(&self, line: &str) -> bool {
-        self.parse_name_directive(line, "compile-pass")
-    }
-
     fn parse_disable_ui_testing_normalization(&self, line: &str) -> bool {
         self.parse_name_directive(line, "disable-ui-testing-normalization")
     }
@@ -722,14 +743,6 @@
         self.parse_name_directive(line, "check-test-line-numbers-match")
     }
 
-    fn parse_run_pass(&self, line: &str) -> bool {
-        self.parse_name_directive(line, "run-pass")
-    }
-
-    fn parse_skip_codegen(&self, line: &str) -> bool {
-        self.parse_name_directive(line, "skip-codegen")
-    }
-
     fn parse_assembly_output(&self, line: &str) -> Option<String> {
         self.parse_name_value_directive(line, "assembly-output")
             .map(|r| r.trim().to_string())
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index d87bd66..8b52a52 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -10,7 +10,7 @@
 use crate::common::{Incremental, MirOpt, RunMake, Ui, JsDocTest, Assembly};
 use diff;
 use crate::errors::{self, Error, ErrorKind};
-use crate::header::TestProps;
+use crate::header::{TestProps, PassMode};
 use crate::json;
 use regex::{Captures, Regex};
 use rustfix::{apply_suggestions, get_suggestions_from_json, Filter};
@@ -310,20 +310,19 @@
     }
 
     fn should_run_successfully(&self) -> bool {
-        let run_pass = match self.config.mode {
+        match self.config.mode {
             RunPass => true,
-            Ui => self.props.run_pass,
-            _ => unimplemented!(),
-        };
-        return run_pass && !self.props.skip_codegen;
+            Ui => self.props.pass_mode == Some(PassMode::Run),
+            mode => panic!("unimplemented for mode {:?}", mode),
+        }
     }
 
     fn should_compile_successfully(&self) -> bool {
         match self.config.mode {
-            CompileFail => self.props.compile_pass,
+            CompileFail => false,
             RunPass => true,
             JsDocTest => true,
-            Ui => self.props.compile_pass,
+            Ui => self.props.pass_mode.is_some(),
             Incremental => {
                 let revision = self.revision
                     .expect("incremental tests require a list of revisions");
@@ -331,7 +330,7 @@
                     true
                 } else if revision.starts_with("cfail") {
                     // FIXME: would be nice if incremental revs could start with "cpass"
-                    self.props.compile_pass
+                    self.props.pass_mode.is_some()
                 } else {
                     panic!("revision name must begin with rpass, rfail, or cfail");
                 }
@@ -433,11 +432,9 @@
             "run-pass tests with expected warnings should be moved to ui/"
         );
 
-        if !self.props.skip_codegen {
-            let proc_res = self.exec_compiled_test();
-            if !proc_res.status.success() {
-                self.fatal_proc_rec("test run failed!", &proc_res);
-            }
+        let proc_res = self.exec_compiled_test();
+        if !proc_res.status.success() {
+            self.fatal_proc_rec("test run failed!", &proc_res);
         }
     }
 
@@ -1344,7 +1341,7 @@
     fn check_error_patterns(&self, output_to_check: &str, proc_res: &ProcRes) {
         debug!("check_error_patterns");
         if self.props.error_patterns.is_empty() {
-            if self.props.compile_pass {
+            if self.props.pass_mode.is_some() {
                 return;
             } else {
                 self.fatal(&format!(
@@ -1971,7 +1968,7 @@
             }
         }
 
-        if self.props.skip_codegen {
+        if self.props.pass_mode == Some(PassMode::Check) {
             assert!(
                 !self
                     .props
diff --git a/src/tools/miri b/src/tools/miri
index e4b298b..c65fbc4 160000
--- a/src/tools/miri
+++ b/src/tools/miri
@@ -1 +1 @@
-Subproject commit e4b298bc4f5a7e86eb78404e25de08f179091e03
+Subproject commit c65fbc49d7b9269b277b7207f1044ee851f16b16
diff --git a/src/tools/rustbook/Cargo.toml b/src/tools/rustbook/Cargo.toml
index d2b1a7b..54549e4 100644
--- a/src/tools/rustbook/Cargo.toml
+++ b/src/tools/rustbook/Cargo.toml
@@ -8,9 +8,8 @@
 [dependencies]
 clap = "2.25.0"
 
-[dependencies.mdbook_2]
-package = "mdbook"
-version = "0.2.3"
+[dependencies.mdbook]
+version = "0.3.0"
 default-features = false
 features = ["search"]
 
diff --git a/src/tools/rustbook/src/main.rs b/src/tools/rustbook/src/main.rs
index cfc1bc6..04e48dd 100644
--- a/src/tools/rustbook/src/main.rs
+++ b/src/tools/rustbook/src/main.rs
@@ -10,8 +10,8 @@
 use mdbook_1::{MDBook as MDBook1};
 use mdbook_1::errors::{Result as Result1};
 
-use mdbook_2::{MDBook as MDBook2};
-use mdbook_2::errors::{Result as Result2};
+use mdbook::MDBook;
+use mdbook::errors::Result;
 
 fn main() {
     let d_message = "-d, --dest-dir=[dest-dir]
@@ -48,8 +48,8 @@
                         ::std::process::exit(101);
                     }
                 }
-                Some("2") => {
-                    if let Err(e) = build_2(sub_matches) {
+                Some("2") | Some("3") => {
+                    if let Err(e) = build(sub_matches) {
                         eprintln!("Error: {}", e);
 
                         for cause in e.iter().skip(1) {
@@ -60,7 +60,7 @@
                     }
                 }
                 _ => {
-                    panic!("Invalid mdBook version! Select '1' or '2'");
+                    panic!("Invalid mdBook version! Select '1' or '2' or '3'");
                 }
             };
         },
@@ -86,9 +86,9 @@
 }
 
 // Build command implementation
-pub fn build_2(args: &ArgMatches<'_>) -> Result2<()> {
+pub fn build(args: &ArgMatches<'_>) -> Result<()> {
     let book_dir = get_book_dir(args);
-    let mut book = MDBook2::load(&book_dir)?;
+    let mut book = MDBook::load(&book_dir)?;
 
     // Set this to allow us to catch bugs in advance.
     book.config.build.create_missing = false;
diff --git a/src/tools/tidy/Cargo.toml b/src/tools/tidy/Cargo.toml
index eeac6cf..43cae31 100644
--- a/src/tools/tidy/Cargo.toml
+++ b/src/tools/tidy/Cargo.toml
@@ -8,3 +8,5 @@
 regex = "1"
 serde = { version = "1.0.8", features = ["derive"] }
 serde_json = "1.0.2"
+lazy_static = "1"
+walkdir = "2"
diff --git a/src/tools/tidy/src/bins.rs b/src/tools/tidy/src/bins.rs
index 610d1d8..680585a 100644
--- a/src/tools/tidy/src/bins.rs
+++ b/src/tools/tidy/src/bins.rs
@@ -25,16 +25,17 @@
         }
     }
 
-    super::walk(path,
+    super::walk_no_read(path,
                 &mut |path| super::filter_dirs(path) || path.ends_with("src/etc"),
-                &mut |file| {
+                &mut |entry| {
+        let file = entry.path();
         let filename = file.file_name().unwrap().to_string_lossy();
         let extensions = [".py", ".sh"];
         if extensions.iter().any(|e| filename.ends_with(e)) {
             return;
         }
 
-        let metadata = t!(fs::symlink_metadata(&file), &file);
+        let metadata = t!(entry.metadata(), file);
         if metadata.mode() & 0o111 != 0 {
             let rel_path = file.strip_prefix(path).unwrap();
             let git_friendly_path = rel_path.to_str().unwrap().replace("\\", "/");
diff --git a/src/tools/tidy/src/errors.rs b/src/tools/tidy/src/errors.rs
index ef1000e..1bc2774 100644
--- a/src/tools/tidy/src/errors.rs
+++ b/src/tools/tidy/src/errors.rs
@@ -4,24 +4,19 @@
 //! statistics about the error codes.
 
 use std::collections::HashMap;
-use std::fs::File;
-use std::io::prelude::*;
 use std::path::Path;
 
 pub fn check(path: &Path, bad: &mut bool) {
-    let mut contents = String::new();
     let mut map: HashMap<_, Vec<_>> = HashMap::new();
     super::walk(path,
                 &mut |path| super::filter_dirs(path) || path.ends_with("src/test"),
-                &mut |file| {
+                &mut |entry, contents| {
+        let file = entry.path();
         let filename = file.file_name().unwrap().to_string_lossy();
         if filename != "error_codes.rs" {
             return
         }
 
-        contents.truncate(0);
-        t!(t!(File::open(file)).read_to_string(&mut contents));
-
         // In the `register_long_diagnostics!` macro, entries look like this:
         //
         // ```
diff --git a/src/tools/tidy/src/features.rs b/src/tools/tidy/src/features.rs
index 637f10c..1841beb 100644
--- a/src/tools/tidy/src/features.rs
+++ b/src/tools/tidy/src/features.rs
@@ -11,11 +11,10 @@
 
 use std::collections::HashMap;
 use std::fmt;
-use std::fs::{self, File};
-use std::io::prelude::*;
+use std::fs;
 use std::path::Path;
 
-use regex::{Regex, escape};
+use regex::Regex;
 
 mod version;
 use version::Version;
@@ -51,20 +50,48 @@
 
 pub type Features = HashMap<String, Feature>;
 
-pub fn check(path: &Path, bad: &mut bool, quiet: bool) {
+pub struct CollectedFeatures {
+    pub lib: Features,
+    pub lang: Features,
+}
+
+// Currently only used for unstable book generation
+pub fn collect_lib_features(base_src_path: &Path) -> Features {
+    let mut lib_features = Features::new();
+
+    // This library feature is defined in the `compiler_builtins` crate, which
+    // has been moved out-of-tree. Now it can no longer be auto-discovered by
+    // `tidy`, because we need to filter out its (submodule) directory. Manually
+    // add it to the set of known library features so we can still generate docs.
+    lib_features.insert("compiler_builtins_lib".to_owned(), Feature {
+        level: Status::Unstable,
+        since: None,
+        has_gate_test: false,
+        tracking_issue: None,
+    });
+
+    map_lib_features(base_src_path,
+                     &mut |res, _, _| {
+        if let Ok((name, feature)) = res {
+            lib_features.insert(name.to_owned(), feature);
+        }
+    });
+   lib_features
+}
+
+pub fn check(path: &Path, bad: &mut bool, verbose: bool) -> CollectedFeatures {
     let mut features = collect_lang_features(path, bad);
     assert!(!features.is_empty());
 
     let lib_features = get_and_check_lib_features(path, bad, &features);
     assert!(!lib_features.is_empty());
 
-    let mut contents = String::new();
-
     super::walk_many(&[&path.join("test/ui"),
                        &path.join("test/ui-fulldeps"),
                        &path.join("test/compile-fail")],
                      &mut |path| super::filter_dirs(path),
-                     &mut |file| {
+                     &mut |entry, contents| {
+        let file = entry.path();
         let filename = file.file_name().unwrap().to_string_lossy();
         if !filename.ends_with(".rs") || filename == "features.rs" ||
            filename == "diagnostic_list.rs" {
@@ -74,9 +101,6 @@
         let filen_underscore = filename.replace('-',"_").replace(".rs","");
         let filename_is_gate_test = test_filen_gate(&filen_underscore, &mut features);
 
-        contents.truncate(0);
-        t!(t!(File::open(&file), &file).read_to_string(&mut contents));
-
         for (i, line) in contents.lines().enumerate() {
             let mut err = |msg: &str| {
                 tidy_error!(bad, "{}:{}: {}", file.display(), i + 1, msg);
@@ -130,21 +154,23 @@
     }
 
     if *bad {
-        return;
+        return CollectedFeatures { lib: lib_features, lang: features };
     }
-    if quiet {
+
+    if verbose {
+        let mut lines = Vec::new();
+        lines.extend(format_features(&features, "lang"));
+        lines.extend(format_features(&lib_features, "lib"));
+
+        lines.sort();
+        for line in lines {
+            println!("* {}", line);
+        }
+    } else {
         println!("* {} features", features.len());
-        return;
     }
 
-    let mut lines = Vec::new();
-    lines.extend(format_features(&features, "lang"));
-    lines.extend(format_features(&lib_features, "lib"));
-
-    lines.sort();
-    for line in lines {
-        println!("* {}", line);
-    }
+    CollectedFeatures { lib: lib_features, lang: features }
 }
 
 fn format_features<'a>(features: &'a Features, family: &'a str) -> impl Iterator<Item = String> + 'a {
@@ -159,8 +185,19 @@
 }
 
 fn find_attr_val<'a>(line: &'a str, attr: &str) -> Option<&'a str> {
-    let r = Regex::new(&format!(r#"{}\s*=\s*"([^"]*)""#, escape(attr)))
-        .expect("malformed regex for find_attr_val");
+    lazy_static::lazy_static! {
+        static ref ISSUE: Regex = Regex::new(r#"issue\s*=\s*"([^"]*)""#).unwrap();
+        static ref FEATURE: Regex = Regex::new(r#"feature\s*=\s*"([^"]*)""#).unwrap();
+        static ref SINCE: Regex = Regex::new(r#"since\s*=\s*"([^"]*)""#).unwrap();
+    }
+
+    let r = match attr {
+        "issue" => &*ISSUE,
+        "feature" => &*FEATURE,
+        "since" => &*SINCE,
+        _ => unimplemented!("{} not handled", attr),
+    };
+
     r.captures(line)
         .and_then(|c| c.get(1))
         .map(|m| m.as_str())
@@ -175,9 +212,11 @@
 }
 
 fn test_filen_gate(filen_underscore: &str, features: &mut Features) -> bool {
-    if filen_underscore.starts_with("feature_gate") {
+    let prefix = "feature_gate_";
+    if filen_underscore.starts_with(prefix) {
         for (n, f) in features.iter_mut() {
-            if filen_underscore == format!("feature_gate_{}", n) {
+            // Equivalent to filen_underscore == format!("feature_gate_{}", n)
+            if &filen_underscore[prefix.len()..] == n {
                 f.has_gate_test = true;
                 return true;
             }
@@ -295,32 +334,6 @@
         .collect()
 }
 
-pub fn collect_lib_features(base_src_path: &Path) -> Features {
-    let mut lib_features = Features::new();
-
-    // This library feature is defined in the `compiler_builtins` crate, which
-    // has been moved out-of-tree. Now it can no longer be auto-discovered by
-    // `tidy`, because we need to filter out its (submodule) directory. Manually
-    // add it to the set of known library features so we can still generate docs.
-    lib_features.insert("compiler_builtins_lib".to_owned(), Feature {
-        level: Status::Unstable,
-        since: None,
-        has_gate_test: false,
-        tracking_issue: None,
-    });
-
-    map_lib_features(base_src_path,
-                     &mut |res, _, _| {
-        if let Ok((name, feature)) = res {
-            if lib_features.contains_key(name) {
-                return;
-            }
-            lib_features.insert(name.to_owned(), feature);
-        }
-    });
-   lib_features
-}
-
 fn get_and_check_lib_features(base_src_path: &Path,
                               bad: &mut bool,
                               lang_features: &Features) -> Features {
@@ -355,20 +368,25 @@
 
 fn map_lib_features(base_src_path: &Path,
                     mf: &mut dyn FnMut(Result<(&str, Feature), &str>, &Path, usize)) {
-    let mut contents = String::new();
     super::walk(base_src_path,
                 &mut |path| super::filter_dirs(path) || path.ends_with("src/test"),
-                &mut |file| {
+                &mut |entry, contents| {
+        let file = entry.path();
         let filename = file.file_name().unwrap().to_string_lossy();
         if !filename.ends_with(".rs") || filename == "features.rs" ||
            filename == "diagnostic_list.rs" {
             return;
         }
 
-        contents.truncate(0);
-        t!(t!(File::open(&file), &file).read_to_string(&mut contents));
+        // This is an early exit -- all the attributes we're concerned with must contain this:
+        // * rustc_const_unstable(
+        // * unstable(
+        // * stable(
+        if !contents.contains("stable(") {
+            return;
+        }
 
-        let mut becoming_feature: Option<(String, Feature)> = None;
+        let mut becoming_feature: Option<(&str, Feature)> = None;
         for (i, line) in contents.lines().enumerate() {
             macro_rules! err {
                 ($msg:expr) => {{
@@ -447,7 +465,7 @@
             if line.contains(']') {
                 mf(Ok((feature_name, feature)), file, i + 1);
             } else {
-                becoming_feature = Some((feature_name.to_owned(), feature));
+                becoming_feature = Some((feature_name, feature));
             }
         }
     });
diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs
index d06c997..a0bf0b0 100644
--- a/src/tools/tidy/src/lib.rs
+++ b/src/tools/tidy/src/lib.rs
@@ -3,7 +3,9 @@
 //! This library contains the tidy lints and exposes it
 //! to be used by tools.
 
-use std::fs;
+use walkdir::{DirEntry, WalkDir};
+use std::fs::File;
+use std::io::Read;
 
 use std::path::Path;
 
@@ -65,25 +67,35 @@
     skip.iter().any(|p| path.ends_with(p))
 }
 
-fn walk_many(paths: &[&Path], skip: &mut dyn FnMut(&Path) -> bool, f: &mut dyn FnMut(&Path)) {
+
+fn walk_many(
+    paths: &[&Path], skip: &mut dyn FnMut(&Path) -> bool, f: &mut dyn FnMut(&DirEntry, &str)
+) {
     for path in paths {
         walk(path, skip, f);
     }
 }
 
-fn walk(path: &Path, skip: &mut dyn FnMut(&Path) -> bool, f: &mut dyn FnMut(&Path)) {
-    if let Ok(dir) = fs::read_dir(path) {
-        for entry in dir {
-            let entry = t!(entry);
-            let kind = t!(entry.file_type());
-            let path = entry.path();
-            if kind.is_dir() {
-                if !skip(&path) {
-                    walk(&path, skip, f);
-                }
-            } else {
-                f(&path);
+fn walk(path: &Path, skip: &mut dyn FnMut(&Path) -> bool, f: &mut dyn FnMut(&DirEntry, &str)) {
+    let mut contents = String::new();
+    walk_no_read(path, skip, &mut |entry| {
+        contents.clear();
+        if t!(File::open(entry.path()), entry.path()).read_to_string(&mut contents).is_err() {
+            contents.clear();
+        }
+        f(&entry, &contents);
+    });
+}
+
+fn walk_no_read(path: &Path, skip: &mut dyn FnMut(&Path) -> bool, f: &mut dyn FnMut(&DirEntry)) {
+    let walker = WalkDir::new(path).into_iter()
+        .filter_entry(|e| !skip(e.path()));
+    for entry in walker {
+        if let Ok(entry) = entry {
+            if entry.file_type().is_dir() {
+                continue;
             }
+            f(&entry);
         }
     }
 }
diff --git a/src/tools/tidy/src/libcoretest.rs b/src/tools/tidy/src/libcoretest.rs
index b15b9c3..ea92f98 100644
--- a/src/tools/tidy/src/libcoretest.rs
+++ b/src/tools/tidy/src/libcoretest.rs
@@ -4,29 +4,22 @@
 //! item. All tests must be written externally in `libcore/tests`.
 
 use std::path::Path;
-use std::fs::read_to_string;
 
 pub fn check(path: &Path, bad: &mut bool) {
     let libcore_path = path.join("libcore");
     super::walk(
         &libcore_path,
         &mut |subpath| t!(subpath.strip_prefix(&libcore_path)).starts_with("tests"),
-        &mut |subpath| {
+        &mut |entry, contents| {
+            let subpath = entry.path();
             if let Some("rs") = subpath.extension().and_then(|e| e.to_str()) {
-                match read_to_string(subpath) {
-                    Ok(contents) => {
-                        if contents.contains("#[test]") {
-                            tidy_error!(
-                                bad,
-                                "{} contains #[test]; libcore tests must be placed inside \
-                                `src/libcore/tests/`",
-                                subpath.display()
-                            );
-                        }
-                    }
-                    Err(err) => {
-                        panic!("failed to read file {:?}: {}", subpath, err);
-                    }
+                if contents.contains("#[test]") {
+                    tidy_error!(
+                        bad,
+                        "{} contains #[test]; libcore tests must be placed inside \
+                        `src/libcore/tests/`",
+                        subpath.display()
+                    );
                 }
             }
         },
diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs
index eef3719..918762e 100644
--- a/src/tools/tidy/src/main.rs
+++ b/src/tools/tidy/src/main.rs
@@ -19,14 +19,14 @@
     let args: Vec<String> = env::args().skip(1).collect();
 
     let mut bad = false;
-    let quiet = args.iter().any(|s| *s == "--quiet");
+    let verbose = args.iter().any(|s| *s == "--verbose");
     bins::check(&path, &mut bad);
     style::check(&path, &mut bad);
     errors::check(&path, &mut bad);
     cargo::check(&path, &mut bad);
-    features::check(&path, &mut bad, quiet);
+    let collected = features::check(&path, &mut bad, verbose);
     pal::check(&path, &mut bad);
-    unstable_book::check(&path, &mut bad);
+    unstable_book::check(&path, collected, &mut bad);
     libcoretest::check(&path, &mut bad);
     if !args.iter().any(|s| *s == "--no-vendor") {
         deps::check(&path, &mut bad);
diff --git a/src/tools/tidy/src/pal.rs b/src/tools/tidy/src/pal.rs
index d4a6cf7..c6bb163 100644
--- a/src/tools/tidy/src/pal.rs
+++ b/src/tools/tidy/src/pal.rs
@@ -31,8 +31,6 @@
 //! platform-specific cfgs are allowed. Not sure yet how to deal with
 //! this in the long term.
 
-use std::fs::File;
-use std::io::Read;
 use std::path::Path;
 use std::iter::Iterator;
 
@@ -87,29 +85,26 @@
 ];
 
 pub fn check(path: &Path, bad: &mut bool) {
-    let mut contents = String::new();
     // Sanity check that the complex parsing here works.
     let mut saw_target_arch = false;
     let mut saw_cfg_bang = false;
-    super::walk(path, &mut super::filter_dirs, &mut |file| {
+    super::walk(path, &mut super::filter_dirs, &mut |entry, contents| {
+        let file = entry.path();
         let filestr = file.to_string_lossy().replace("\\", "/");
         if !filestr.ends_with(".rs") { return }
 
         let is_exception_path = EXCEPTION_PATHS.iter().any(|s| filestr.contains(&**s));
         if is_exception_path { return }
 
-        check_cfgs(&mut contents, &file, bad, &mut saw_target_arch, &mut saw_cfg_bang);
+        check_cfgs(contents, &file, bad, &mut saw_target_arch, &mut saw_cfg_bang);
     });
 
     assert!(saw_target_arch);
     assert!(saw_cfg_bang);
 }
 
-fn check_cfgs(contents: &mut String, file: &Path,
+fn check_cfgs(contents: &str, file: &Path,
               bad: &mut bool, saw_target_arch: &mut bool, saw_cfg_bang: &mut bool) {
-    contents.truncate(0);
-    t!(t!(File::open(file), file).read_to_string(contents));
-
     // For now it's ok to have platform-specific code after 'mod tests'.
     let mod_tests_idx = find_test_mod(contents);
     let contents = &contents[..mod_tests_idx];
diff --git a/src/tools/tidy/src/style.rs b/src/tools/tidy/src/style.rs
index e860f2e..4a159d9 100644
--- a/src/tools/tidy/src/style.rs
+++ b/src/tools/tidy/src/style.rs
@@ -13,8 +13,6 @@
 //! A number of these checks can be opted-out of with various directives of the form:
 //! `// ignore-tidy-CHECK-NAME`.
 
-use std::fs::File;
-use std::io::prelude::*;
 use std::path::Path;
 
 const COLS: usize = 100;
@@ -109,7 +107,11 @@
     Ignore(bool),
 }
 
-fn contains_ignore_directive(contents: &String, check: &str) -> Directive {
+fn contains_ignore_directive(can_contain: bool, contents: &str, check: &str) -> Directive {
+    if !can_contain {
+        return Directive::Deny;
+    }
+    // Update `can_contain` when changing this
     if contents.contains(&format!("// ignore-tidy-{}", check)) ||
         contents.contains(&format!("# ignore-tidy-{}", check)) {
         Directive::Ignore(false)
@@ -129,8 +131,8 @@
 }
 
 pub fn check(path: &Path, bad: &mut bool) {
-    let mut contents = String::new();
-    super::walk(path, &mut super::filter_dirs, &mut |file| {
+    super::walk(path, &mut super::filter_dirs, &mut |entry, contents| {
+        let file = entry.path();
         let filename = file.file_name().unwrap().to_string_lossy();
         let extensions = [".rs", ".py", ".js", ".sh", ".c", ".cpp", ".h"];
         if extensions.iter().all(|e| !filename.ends_with(e)) ||
@@ -138,19 +140,19 @@
             return
         }
 
-        contents.truncate(0);
-        t!(t!(File::open(file), file).read_to_string(&mut contents));
-
         if contents.is_empty() {
             tidy_error!(bad, "{}: empty file", file.display());
         }
 
-        let mut skip_cr = contains_ignore_directive(&contents, "cr");
-        let mut skip_tab = contains_ignore_directive(&contents, "tab");
-        let mut skip_line_length = contains_ignore_directive(&contents, "linelength");
-        let mut skip_file_length = contains_ignore_directive(&contents, "filelength");
-        let mut skip_end_whitespace = contains_ignore_directive(&contents, "end-whitespace");
-        let mut skip_copyright = contains_ignore_directive(&contents, "copyright");
+        let can_contain = contents.contains("// ignore-tidy-") ||
+            contents.contains("# ignore-tidy-");
+        let mut skip_cr = contains_ignore_directive(can_contain, &contents, "cr");
+        let mut skip_tab = contains_ignore_directive(can_contain, &contents, "tab");
+        let mut skip_line_length = contains_ignore_directive(can_contain, &contents, "linelength");
+        let mut skip_file_length = contains_ignore_directive(can_contain, &contents, "filelength");
+        let mut skip_end_whitespace =
+            contains_ignore_directive(can_contain, &contents, "end-whitespace");
+        let mut skip_copyright = contains_ignore_directive(can_contain, &contents, "copyright");
         let mut leading_new_lines = false;
         let mut trailing_new_lines = 0;
         let mut lines = 0;
diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs
index b572b52..2c52cec 100644
--- a/src/tools/tidy/src/ui_tests.rs
+++ b/src/tools/tidy/src/ui_tests.rs
@@ -4,10 +4,9 @@
 use std::path::Path;
 
 pub fn check(path: &Path, bad: &mut bool) {
-    super::walk_many(
-        &[&path.join("test/ui"), &path.join("test/ui-fulldeps")],
-        &mut |_| false,
-        &mut |file_path| {
+    for path in &[&path.join("test/ui"), &path.join("test/ui-fulldeps")] {
+        super::walk_no_read(path, &mut |_| false, &mut |entry| {
+            let file_path = entry.path();
             if let Some(ext) = file_path.extension() {
                 if ext == "stderr" || ext == "stdout" {
                     // Test output filenames have one of the formats:
@@ -45,6 +44,6 @@
                     }
                 }
             }
-        },
-    );
+        });
+    }
 }
diff --git a/src/tools/tidy/src/unstable_book.rs b/src/tools/tidy/src/unstable_book.rs
index f7e40ce..fb63520 100644
--- a/src/tools/tidy/src/unstable_book.rs
+++ b/src/tools/tidy/src/unstable_book.rs
@@ -1,7 +1,7 @@
 use std::collections::BTreeSet;
 use std::fs;
-use std::path;
-use crate::features::{collect_lang_features, collect_lib_features, Features, Status};
+use std::path::{PathBuf, Path};
+use crate::features::{CollectedFeatures, Features, Feature, Status};
 
 pub const PATH_STR: &str = "doc/unstable-book";
 
@@ -12,19 +12,19 @@
 pub const LIB_FEATURES_DIR: &str = "src/library-features";
 
 /// Builds the path to the Unstable Book source directory from the Rust 'src' directory.
-pub fn unstable_book_path(base_src_path: &path::Path) -> path::PathBuf {
+pub fn unstable_book_path(base_src_path: &Path) -> PathBuf {
     base_src_path.join(PATH_STR)
 }
 
 /// Builds the path to the directory where the features are documented within the Unstable Book
 /// source directory.
-pub fn unstable_book_lang_features_path(base_src_path: &path::Path) -> path::PathBuf {
+pub fn unstable_book_lang_features_path(base_src_path: &Path) -> PathBuf {
     unstable_book_path(base_src_path).join(LANG_FEATURES_DIR)
 }
 
 /// Builds the path to the directory where the features are documented within the Unstable Book
 /// source directory.
-pub fn unstable_book_lib_features_path(base_src_path: &path::Path) -> path::PathBuf {
+pub fn unstable_book_lib_features_path(base_src_path: &Path) -> PathBuf {
     unstable_book_path(base_src_path).join(LIB_FEATURES_DIR)
 }
 
@@ -45,7 +45,7 @@
         .collect()
 }
 
-pub fn collect_unstable_book_section_file_names(dir: &path::Path) -> BTreeSet<String> {
+pub fn collect_unstable_book_section_file_names(dir: &Path) -> BTreeSet<String> {
     fs::read_dir(dir)
         .expect("could not read directory")
         .map(|entry| entry.expect("could not read directory entry"))
@@ -60,7 +60,7 @@
 ///
 /// * hyphens replaced by underscores,
 /// * the markdown suffix ('.md') removed.
-fn collect_unstable_book_lang_features_section_file_names(base_src_path: &path::Path)
+fn collect_unstable_book_lang_features_section_file_names(base_src_path: &Path)
                                                           -> BTreeSet<String> {
     collect_unstable_book_section_file_names(&unstable_book_lang_features_path(base_src_path))
 }
@@ -69,18 +69,26 @@
 ///
 /// * hyphens replaced by underscores,
 /// * the markdown suffix ('.md') removed.
-fn collect_unstable_book_lib_features_section_file_names(base_src_path: &path::Path)
-                                                         -> BTreeSet<String> {
+fn collect_unstable_book_lib_features_section_file_names(base_src_path: &Path) -> BTreeSet<String> {
     collect_unstable_book_section_file_names(&unstable_book_lib_features_path(base_src_path))
 }
 
-pub fn check(path: &path::Path, bad: &mut bool) {
-    // Library features
-
-    let lang_features = collect_lang_features(path, bad);
-    let lib_features = collect_lib_features(path).into_iter().filter(|&(ref name, _)| {
+pub fn check(path: &Path, features: CollectedFeatures, bad: &mut bool) {
+    let lang_features = features.lang;
+    let mut lib_features = features.lib.into_iter().filter(|&(ref name, _)| {
         !lang_features.contains_key(name)
-    }).collect();
+    }).collect::<Features>();
+
+    // This library feature is defined in the `compiler_builtins` crate, which
+    // has been moved out-of-tree. Now it can no longer be auto-discovered by
+    // `tidy`, because we need to filter out its (submodule) directory. Manually
+    // add it to the set of known library features so we can still generate docs.
+    lib_features.insert("compiler_builtins_lib".to_owned(), Feature {
+        level: Status::Unstable,
+        since: None,
+        has_gate_test: false,
+        tracking_issue: None,
+    });
 
     // Library features
     let unstable_lib_feature_names = collect_unstable_feature_names(&lib_features);