Rollup merge of #62085 - JohnTitor:add-test-for-issue-38591, r=Centril

Add test for issue-38591

Closes #38591

r? @pnkfelix
diff --git a/Cargo.lock b/Cargo.lock
index 40b8cf5..eaec52a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -149,19 +149,6 @@
 ]
 
 [[package]]
-name = "bit-set"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "bit-vec 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "bit-vec"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
 name = "bitflags"
 version = "0.9.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -312,7 +299,6 @@
  "opener 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "openssl 0.10.16 (registry+https://github.com/rust-lang/crates.io-index)",
  "pretty_env_logger 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "proptest 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-workspace-hack 1.0.0",
  "rustfix 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -329,7 +315,6 @@
  "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)",
- "varisat 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
@@ -1156,11 +1141,6 @@
 
 [[package]]
 name = "hashbrown"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "hashbrown"
 version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
@@ -1399,11 +1379,6 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
-name = "leb128"
-version = "0.2.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
 name = "libc"
 version = "0.2.54"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1881,14 +1856,6 @@
 ]
 
 [[package]]
-name = "ordered-float"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
 name = "ordermap"
 version = "0.3.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1957,24 +1924,6 @@
 ]
 
 [[package]]
-name = "partial_ref"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "partial_ref_derive 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "partial_ref_derive"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "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 = "percent-encoding"
 version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2137,25 +2086,6 @@
 ]
 
 [[package]]
-name = "proptest"
-version = "0.9.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "bit-set 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
- "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "rand_chacha 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "rand_xorshift 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "regex-syntax 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "rusty-fork 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
 name = "pulldown-cmark"
 version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3326,17 +3256,6 @@
 ]
 
 [[package]]
-name = "rusty-fork"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "wait-timeout 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
 name = "ryu"
 version = "0.2.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3801,9 +3720,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]]
@@ -4179,76 +4100,6 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
-name = "varisat"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "leb128 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "ordered-float 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "partial_ref 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)",
- "varisat-checker 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "varisat-dimacs 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "varisat-formula 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "varisat-internal-macros 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "varisat-internal-proof 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "vec_mut_scan 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "varisat-checker"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "hashbrown 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
- "varisat-dimacs 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "varisat-formula 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "varisat-internal-proof 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "varisat-dimacs"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "varisat-formula 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "varisat-formula"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "varisat-internal-macros"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "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)",
- "regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "syn 0.15.35 (registry+https://github.com/rust-lang/crates.io-index)",
- "synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "varisat-internal-proof"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "varisat-formula 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
 name = "vcpkg"
 version = "0.2.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4259,11 +4110,6 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
-name = "vec_mut_scan"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
 name = "vergen"
 version = "3.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4292,14 +4138,6 @@
 ]
 
 [[package]]
-name = "wait-timeout"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
 name = "walkdir"
 version = "2.2.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4401,8 +4239,6 @@
 "checksum autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0e49efa51329a5fd37e7c79db4621af617cd4e3e5bc224939808d076077077bf"
 "checksum backtrace 0.3.29 (registry+https://github.com/rust-lang/crates.io-index)" = "2d631cd7af21b7ff796293f1990104e3cdb606852863bac32f000c193aa35dfb"
 "checksum backtrace-sys 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)" = "6ea90dd7b012b3d1a2cb6bec16670a0db2c95d4e931e84f4047e0460c1b34c8d"
-"checksum bit-set 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6f1efcc46c18245a69c38fcc5cc650f16d3a59d034f3106e9ed63748f695730a"
-"checksum bit-vec 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4440d5cb623bb7390ae27fec0bb6c61111969860f8e3ae198bfa0663645e67cf"
 "checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5"
 "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
 "checksum blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400"
@@ -4492,7 +4328,6 @@
 "checksum globset 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ef4feaabe24a0a658fd9cf4a9acf6ed284f045c77df0f49020ba3245cfb7b454"
 "checksum handlebars 0.32.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d89ec99d1594f285d4590fc32bac5f75cdab383f1123d504d27862c644a807dd"
 "checksum handlebars 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d82e5750d8027a97b9640e3fefa66bbaf852a35228e1c90790efd13c4b09c166"
-"checksum hashbrown 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "29fba9abe4742d586dfd0c06ae4f7e73a1c2d86b856933509b269d82cdf06e18"
 "checksum hashbrown 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9529213c67695ca2d146e6f263b7b72df8fa973368beadf767e8ed80c03f2f36"
 "checksum heck 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ea04fa3ead4e05e51a7c806fc07271fdbde4e246a6c6d1efd52e72230b771b82"
 "checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77"
@@ -4520,7 +4355,6 @@
 "checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73"
 "checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14"
 "checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f"
-"checksum leb128 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3576a87f2ba00f6f106fdfcd16db1d698d648a26ad8e0573cad8537c3c362d2a"
 "checksum libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)" = "c6785aa7dd976f5fbf3b71cfd9cd49d7f783c1ff565a858d71031c6c313aa5c6"
 "checksum libgit2-sys 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "941a41e23f77323b8c9d2ee118aec9ee39dfc176078c18b4757d3bad049d9ff7"
 "checksum libnghttp2-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d75d7966bda4730b722d1eab8e668df445368a24394bae9fc1e8dc0ab3dbe4f4"
@@ -4566,15 +4400,12 @@
 "checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
 "checksum openssl-src 111.1.0+1.1.1a (registry+https://github.com/rust-lang/crates.io-index)" = "26bb632127731bf4ac49bf86a5dde12d2ca0918c2234fc39d79d4da2ccbc6da7"
 "checksum openssl-sys 0.9.43 (registry+https://github.com/rust-lang/crates.io-index)" = "33c86834957dd5b915623e94f2f4ab2c70dd8f6b70679824155d5ae21dbd495d"
-"checksum ordered-float 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "18869315e81473c951eb56ad5558bbc56978562d3ecfb87abb7a1e944cea4518"
 "checksum ordermap 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a86ed3f5f244b372d6b1a00b72ef7f8876d0bc6a78a4c9985c53614041512063"
 "checksum ordslice 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dd20eec3dbe4376829cb7d80ae6ac45e0a766831dca50202ff2d40db46a8a024"
 "checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37"
 "checksum packed_simd 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "25d36de864f7218ec5633572a800109bbe5a1cc8d9d95a967f3daf93ea7e6ddc"
 "checksum parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab41b4aed082705d1056416ae4468b6ea99d52599ecf3169b00088d43113e337"
 "checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9"
-"checksum partial_ref 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b2b85fa89a02abf59d36821c373b5ed38c8e075505f1a08618b000fce81229bc"
-"checksum partial_ref_derive 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "759319b785d033e4279ec98fb2d1fb767a1af5b6a8996086c07168169cff079b"
 "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831"
 "checksum pest 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0fce5d8b5cc33983fc74f78ad552b5522ab41442c4ca91606e4236eb4b5ceefc"
 "checksum pest 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "54f0c72a98d8ab3c99560bfd16df8059cc10e1f9a8e83e6e3b97718dd766e9c3"
@@ -4593,7 +4424,6 @@
 "checksum pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a029430f0d744bc3d15dd474d591bed2402b645d024583082b9f63bb936dac6"
 "checksum pretty_env_logger 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df8b3f4e0475def7d9c2e5de8e5a1306949849761e107b360d03e98eafaffd61"
 "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
-"checksum proptest 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "24f5844db2f839e97e3021980975f6ebf8691d9b9b2ca67ed3feb38dc3edb52c"
 "checksum pulldown-cmark 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d6fdf85cda6cadfae5428a54661d431330b312bc767ddbc57adbedc24da66e32"
 "checksum pulldown-cmark 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "051e60ace841b3bfecd402fe5051c06cb3bec4a6e6fdd060a37aa8eb829a1db3"
 "checksum punycode 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6ddd112cca70a4d30883b2d21568a1d376ff8be4758649f64f973c6845128ad3"
@@ -4646,7 +4476,6 @@
 "checksum rustc_tools_util 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b725dadae9fabc488df69a287f5a99c5eaf5d10853842a8a3dfac52476f544ee"
 "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
 "checksum rustfix 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "af7c21531a91512a4a51b490be6ba1c8eff34fdda0dc5bf87dc28d86748aac56"
-"checksum rusty-fork 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9591f190d2852720b679c21f66ad929f9f1d7bb09d1193c26167586029d8489c"
 "checksum ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7"
 "checksum same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8f20c4be53a8a1ff4c1f1b2bd14570d2f634628709752f0702ecdd2b3f9a5267"
 "checksum schannel 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "0e1a231dc10abf6749cfa5d7767f25888d484201accbd919b66ab5413c502d56"
@@ -4729,20 +4558,12 @@
 "checksum utf-8 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f1262dfab4c30d5cb7c07026be00ee343a6cf5027fdc0104a9160f354e5db75c"
 "checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737"
 "checksum utf8parse 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8772a4ccbb4e89959023bc5b7cb8623a795caa7092d99f3aa9501b9484d4557d"
-"checksum varisat 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a2640f5949bcd945ffdb030f5f336d0a5da8fe8ddab8e8230e2e030ea0623cfa"
-"checksum varisat-checker 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a44da8d5e25b089d66fb3d14ae87994e2f7ba7f86ff396b7c490083d8a9a0a7b"
-"checksum varisat-dimacs 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f992cf40560ad73983369414fcc5a42fb9c9e39ae7ff215c75725f9c6785f0b9"
-"checksum varisat-formula 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "78d44ab5c6de769e855c77add5b0efa73ed3320b06485f04c8d3fad9b2eb9997"
-"checksum varisat-internal-macros 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0e76c43d9badf53d22b0edd25667d65b7b67167e2cce249c9d1e3ca0f02dc81c"
-"checksum varisat-internal-proof 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5a7553f03a4a8581410fb1813add70ce54e481d0e3eb1ca2cc1754faf46ff9ad"
 "checksum vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "def296d3eb3b12371b2c7d0e83bfe1403e4db2d7a0bba324a12b21c4ee13143d"
 "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
-"checksum vec_mut_scan 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d5668931075a8dfe6eb3e9e585d06f0ab4d9b377663e94d135ef51933ff9f6"
 "checksum vergen 3.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6aba5e34f93dc7051dfad05b98a18e9156f27e7b431fe1d2398cb6061c0a1dba"
 "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
 "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
 "checksum vte 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4f42f536e22f7fcbb407639765c8fd78707a33109301f834a594758bedd6e8cf"
-"checksum wait-timeout 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b9f3bf741a801531993db6478b95682117471f76916f5e690dd8d45395b09349"
 "checksum walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "9d9d7ed3431229a144296213105a390676cc49c9b6a72bd19f3176c98e129fa1"
 "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
 "checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0"
diff --git a/src/bootstrap/cc_detect.rs b/src/bootstrap/cc_detect.rs
index dfc243b..400375c 100644
--- a/src/bootstrap/cc_detect.rs
+++ b/src/bootstrap/cc_detect.rs
@@ -95,30 +95,40 @@
         };
 
         build.cc.insert(target, compiler);
+        let cflags = build.cflags(target, GitRepo::Rustc);
+
+        // If we use llvm-libunwind, we will need a C++ compiler as well for all targets
+        // We'll need one anyways if the target triple is also a host triple
+        let mut cfg = cc::Build::new();
+        cfg.cargo_metadata(false).opt_level(2).warnings(false).debug(false).cpp(true)
+            .target(&target).host(&build.build);
+
+        let cxx_configured = if let Some(cxx) = config.and_then(|c| c.cxx.as_ref()) {
+            cfg.compiler(cxx);
+            true
+        } else if build.hosts.contains(&target) || build.build == target {
+            set_compiler(&mut cfg, Language::CPlusPlus, target, config, build);
+            true
+        } else {
+            false
+        };
+
+        if cxx_configured {
+            let compiler = cfg.get_compiler();
+            build.cxx.insert(target, compiler);
+        }
+
         build.verbose(&format!("CC_{} = {:?}", &target, build.cc(target)));
-        build.verbose(&format!("CFLAGS_{} = {:?}", &target, build.cflags(target, GitRepo::Rustc)));
+        build.verbose(&format!("CFLAGS_{} = {:?}", &target, cflags));
+        if let Ok(cxx) = build.cxx(target) {
+            build.verbose(&format!("CXX_{} = {:?}", &target, cxx));
+            build.verbose(&format!("CXXFLAGS_{} = {:?}", &target, cflags));
+        }
         if let Some(ar) = ar {
             build.verbose(&format!("AR_{} = {:?}", &target, ar));
             build.ar.insert(target, ar);
         }
     }
-
-    // For all host triples we need to find a C++ compiler as well
-    let hosts = build.hosts.iter().cloned().chain(iter::once(build.build)).collect::<HashSet<_>>();
-    for host in hosts.into_iter() {
-        let mut cfg = cc::Build::new();
-        cfg.cargo_metadata(false).opt_level(2).warnings(false).debug(false).cpp(true)
-           .target(&host).host(&build.build);
-        let config = build.config.target_config.get(&host);
-        if let Some(cxx) = config.and_then(|c| c.cxx.as_ref()) {
-            cfg.compiler(cxx);
-        } else {
-            set_compiler(&mut cfg, Language::CPlusPlus, host, config, build);
-        }
-        let compiler = cfg.get_compiler();
-        build.verbose(&format!("CXX_{} = {:?}", host, compiler.path()));
-        build.cxx.insert(host, compiler);
-    }
 }
 
 fn set_compiler(cfg: &mut cc::Build,
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/librustc/hir/intravisit.rs b/src/librustc/hir/intravisit.rs
index 1777d76..9c05f18 100644
--- a/src/librustc/hir/intravisit.rs
+++ b/src/librustc/hir/intravisit.rs
@@ -1056,7 +1056,7 @@
         }
         ExprKind::AssignOp(_, ref left_expression, ref right_expression) => {
             visitor.visit_expr(right_expression);
-            visitor.visit_expr(left_expression)
+            visitor.visit_expr(left_expression);
         }
         ExprKind::Field(ref subexpression, ident) => {
             visitor.visit_expr(subexpression);
diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs
index 36b29ae..1b8e299 100644
--- a/src/librustc/hir/lowering.rs
+++ b/src/librustc/hir/lowering.rs
@@ -148,11 +148,11 @@
 
 pub trait Resolver {
     /// Resolve a path generated by the lowerer when expanding `for`, `if let`, etc.
-    fn resolve_hir_path(
+    fn resolve_ast_path(
         &mut self,
         path: &ast::Path,
         is_value: bool,
-    ) -> hir::Path;
+    ) -> Res<NodeId>;
 
     /// Obtain resolution for a `NodeId` with a single resolution.
     fn get_partial_res(&mut self, id: NodeId) -> Option<PartialRes>;
@@ -167,7 +167,7 @@
     /// This should only return `None` during testing.
     fn definitions(&mut self) -> &mut Definitions;
 
-    /// Given suffix `["b", "c", "d"]`, creates a HIR path for `[::crate_root]::b::c::d` and
+    /// Given suffix `["b", "c", "d"]`, creates an AST path for `[::crate_root]::b::c::d` and
     /// resolves it based on `is_value`.
     fn resolve_str_path(
         &mut self,
@@ -175,7 +175,7 @@
         crate_root: Option<Symbol>,
         components: &[Symbol],
         is_value: bool,
-    ) -> hir::Path;
+    ) -> (ast::Path, Res<NodeId>);
 }
 
 /// Context of `impl Trait` in code, which determines whether it is allowed in an HIR subtree,
@@ -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)),
@@ -5546,16 +5546,26 @@
         params: Option<P<hir::GenericArgs>>,
         is_value: bool,
     ) -> hir::Path {
-        let mut path = self.resolver
+        let (path, res) = self.resolver
             .resolve_str_path(span, self.crate_root, components, is_value);
-        path.segments.last_mut().unwrap().args = params;
 
-        for seg in path.segments.iter_mut() {
-            if seg.hir_id.is_some() {
-                seg.hir_id = Some(self.next_id());
+        let mut segments: Vec<_> = path.segments.iter().map(|segment| {
+            let res = self.expect_full_res(segment.id);
+            hir::PathSegment {
+                ident: segment.ident,
+                hir_id: Some(self.lower_node_id(segment.id)),
+                res: Some(self.lower_res(res)),
+                infer_args: true,
+                args: None,
             }
+        }).collect();
+        segments.last_mut().unwrap().args = params;
+
+        hir::Path {
+            span,
+            res: res.map_id(|_| panic!("unexpected node_id")),
+            segments: segments.into(),
         }
-        path
     }
 
     fn ty_path(&mut self, mut hir_id: hir::HirId, span: Span, qpath: hir::QPath) -> hir::Ty {
diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs
index 5c5e630..9a5c726 100644
--- a/src/librustc/infer/mod.rs
+++ b/src/librustc/infer/mod.rs
@@ -171,6 +171,7 @@
     /// Track how many errors were reported when this infcx is created.
     /// If the number of errors increases, that's also a sign (line
     /// `tained_by_errors`) to avoid reporting certain kinds of errors.
+    // FIXME(matthewjasper) Merge into `tainted_by_errors_flag`
     err_count_on_creation: usize,
 
     /// This flag is true while there is an active snapshot.
diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs
index d9ccb9d..114684b 100644
--- a/src/librustc/middle/region.rs
+++ b/src/librustc/middle/region.rs
@@ -371,7 +371,12 @@
 
     // The number of expressions and patterns visited in the current body
     expr_and_pat_count: usize,
-
+    // When this is `true`, we record the `Scopes` we encounter
+    // when processing a Yield expression. This allows us to fix
+    // up their indices.
+    pessimistic_yield: bool,
+    // Stores scopes when pessimistic_yield is true.
+    fixup_scopes: Vec<Scope>,
     // Generated scope tree:
     scope_tree: ScopeTree,
 
@@ -947,12 +952,107 @@
         }
     }
 
+    let prev_pessimistic = visitor.pessimistic_yield;
+
+    // Ordinarily, we can rely on the visit order of HIR intravisit
+    // to correspond to the actual execution order of statements.
+    // However, there's a weird corner case with compund assignment
+    // operators (e.g. `a += b`). The evaluation order depends on whether
+    // or not the operator is overloaded (e.g. whether or not a trait
+    // like AddAssign is implemented).
+
+    // For primitive types (which, despite having a trait impl, don't actually
+    // end up calling it), the evluation order is right-to-left. For example,
+    // the following code snippet:
+    //
+    //    let y = &mut 0;
+    //    *{println!("LHS!"); y} += {println!("RHS!"); 1};
+    //
+    // will print:
+    //
+    // RHS!
+    // LHS!
+    //
+    // However, if the operator is used on a non-primitive type,
+    // the evaluation order will be left-to-right, since the operator
+    // actually get desugared to a method call. For example, this
+    // nearly identical code snippet:
+    //
+    //     let y = &mut String::new();
+    //    *{println!("LHS String"); y} += {println!("RHS String"); "hi"};
+    //
+    // will print:
+    // LHS String
+    // RHS String
+    //
+    // To determine the actual execution order, we need to perform
+    // trait resolution. Unfortunately, we need to be able to compute
+    // yield_in_scope before type checking is even done, as it gets
+    // used by AST borrowcheck.
+    //
+    // Fortunately, we don't need to know the actual execution order.
+    // It suffices to know the 'worst case' order with respect to yields.
+    // Specifically, we need to know the highest 'expr_and_pat_count'
+    // that we could assign to the yield expression. To do this,
+    // we pick the greater of the two values from the left-hand
+    // and right-hand expressions. This makes us overly conservative
+    // about what types could possibly live across yield points,
+    // but we will never fail to detect that a type does actually
+    // live across a yield point. The latter part is critical -
+    // we're already overly conservative about what types will live
+    // across yield points, as the generated MIR will determine
+    // when things are actually live. However, for typecheck to work
+    // properly, we can't miss any types.
+
+
     match expr.node {
         // Manually recurse over closures, because they are the only
         // case of nested bodies that share the parent environment.
         hir::ExprKind::Closure(.., body, _, _) => {
             let body = visitor.tcx.hir().body(body);
             visitor.visit_body(body);
+        },
+        hir::ExprKind::AssignOp(_, ref left_expr, ref right_expr) => {
+            debug!("resolve_expr - enabling pessimistic_yield, was previously {}",
+                   prev_pessimistic);
+
+            let start_point = visitor.fixup_scopes.len();
+            visitor.pessimistic_yield = true;
+
+            // If the actual execution order turns out to be right-to-left,
+            // then we're fine. However, if the actual execution order is left-to-right,
+            // then we'll assign too low a count to any `yield` expressions
+            // we encounter in 'right_expression' - they should really occur after all of the
+            // expressions in 'left_expression'.
+            visitor.visit_expr(&right_expr);
+            visitor.pessimistic_yield = prev_pessimistic;
+
+            debug!("resolve_expr - restoring pessimistic_yield to {}", prev_pessimistic);
+            visitor.visit_expr(&left_expr);
+            debug!("resolve_expr - fixing up counts to {}", visitor.expr_and_pat_count);
+
+            // Remove and process any scopes pushed by the visitor
+            let target_scopes = visitor.fixup_scopes.drain(start_point..);
+
+            for scope in target_scopes {
+                let mut yield_data = visitor.scope_tree.yield_in_scope.get_mut(&scope).unwrap();
+                let count = yield_data.expr_and_pat_count;
+                let span = yield_data.span;
+
+                // expr_and_pat_count never decreases. Since we recorded counts in yield_in_scope
+                // before walking the left-hand side, it should be impossible for the recorded
+                // count to be greater than the left-hand side count.
+                if count > visitor.expr_and_pat_count {
+                    bug!("Encountered greater count {} at span {:?} - expected no greater than {}",
+                         count, span, visitor.expr_and_pat_count);
+                }
+                let new_count = visitor.expr_and_pat_count;
+                debug!("resolve_expr - increasing count for scope {:?} from {} to {} at span {:?}",
+                       scope, count, new_count, span);
+
+                yield_data.expr_and_pat_count = new_count;
+            }
+
         }
 
         _ => intravisit::walk_expr(visitor, expr)
@@ -972,6 +1072,10 @@
                 source: *source,
             };
             visitor.scope_tree.yield_in_scope.insert(scope, data);
+            if visitor.pessimistic_yield {
+                debug!("resolve_expr in pessimistic_yield - marking scope {:?} for fixup", scope);
+                visitor.fixup_scopes.push(scope);
+            }
 
             // Keep traversing up while we can.
             match visitor.scope_tree.parent_map.get(&scope) {
@@ -1360,6 +1464,8 @@
                 var_parent: None,
             },
             terminating_scopes: Default::default(),
+            pessimistic_yield: false,
+            fixup_scopes: vec![],
         };
 
         let body = tcx.hir().body(body_id);
diff --git a/src/librustc/mir/interpret/allocation.rs b/src/librustc/mir/interpret/allocation.rs
index 8381125..c8bf250 100644
--- a/src/librustc/mir/interpret/allocation.rs
+++ b/src/librustc/mir/interpret/allocation.rs
@@ -6,44 +6,13 @@
 
 use crate::ty::layout::{Size, Align};
 use syntax::ast::Mutability;
-use std::{iter, fmt::{self, Display}};
+use std::iter;
 use crate::mir;
-use std::ops::{Deref, DerefMut};
+use std::ops::{Range, Deref, DerefMut};
 use rustc_data_structures::sorted_map::SortedMap;
-use rustc_macros::HashStable;
 use rustc_target::abi::HasDataLayout;
 use std::borrow::Cow;
 
-/// Used by `check_bounds` to indicate whether the pointer needs to be just inbounds
-/// or also inbounds of a *live* allocation.
-#[derive(Debug, Copy, Clone, RustcEncodable, RustcDecodable, HashStable)]
-pub enum InboundsCheck {
-    Live,
-    MaybeDead,
-}
-
-/// Used by `check_in_alloc` to indicate context of check
-#[derive(Debug, Copy, Clone, RustcEncodable, RustcDecodable, HashStable)]
-pub enum CheckInAllocMsg {
-    MemoryAccessTest,
-    NullPointerTest,
-    PointerArithmeticTest,
-    InboundsTest,
-}
-
-impl Display for CheckInAllocMsg {
-    /// When this is printed as an error the context looks like this
-    /// "{test name} failed: pointer must be in-bounds at offset..."
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "{}", match *self {
-            CheckInAllocMsg::MemoryAccessTest => "Memory access",
-            CheckInAllocMsg::NullPointerTest => "Null pointer test",
-            CheckInAllocMsg::PointerArithmeticTest => "Pointer arithmetic",
-            CheckInAllocMsg::InboundsTest => "Inbounds test",
-        })
-    }
-}
-
 #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)]
 pub struct Allocation<Tag=(),Extra=()> {
     /// The actual bytes of the allocation.
@@ -146,37 +115,30 @@
 
 impl<'tcx> ::serialize::UseSpecializedDecodable for &'tcx Allocation {}
 
-/// Alignment and bounds checks
-impl<'tcx, Tag, Extra> Allocation<Tag, Extra> {
-    /// Checks if the pointer is "in-bounds". Notice that a pointer pointing at the end
-    /// of an allocation (i.e., at the first *inaccessible* location) *is* considered
-    /// in-bounds!  This follows C's/LLVM's rules.
-    /// If you want to check bounds before doing a memory access, better use `check_bounds`.
-    fn check_bounds_ptr(
-        &self,
-        ptr: Pointer<Tag>,
-        msg: CheckInAllocMsg,
-    ) -> InterpResult<'tcx> {
-        let allocation_size = self.bytes.len() as u64;
-        ptr.check_in_alloc(Size::from_bytes(allocation_size), msg)
-    }
-
-    /// Checks if the memory range beginning at `ptr` and of size `Size` is "in-bounds".
-    #[inline(always)]
-    pub fn check_bounds(
-        &self,
-        cx: &impl HasDataLayout,
-        ptr: Pointer<Tag>,
-        size: Size,
-        msg: CheckInAllocMsg,
-    ) -> InterpResult<'tcx> {
-        // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow)
-        self.check_bounds_ptr(ptr.offset(size, cx)?, msg)
-    }
-}
-
 /// Byte accessors
 impl<'tcx, Tag: Copy, Extra: AllocationExtra<Tag>> Allocation<Tag, Extra> {
+    /// Just a small local helper function to avoid a bit of code repetition.
+    /// Returns the range of this allocation that was meant.
+    #[inline]
+    fn check_bounds(
+        &self,
+        offset: Size,
+        size: Size
+    ) -> Range<usize> {
+        let end = offset + size; // this does overflow checking
+        assert_eq!(
+            end.bytes() as usize as u64, end.bytes(),
+            "cannot handle this access on this host architecture"
+        );
+        let end = end.bytes() as usize;
+        assert!(
+            end <= self.bytes.len(),
+            "Out-of-bounds access at offset {}, size {} in allocation of size {}",
+            offset.bytes(), size.bytes(), self.bytes.len()
+        );
+        (offset.bytes() as usize)..end
+    }
+
     /// The last argument controls whether we error out when there are undefined
     /// or pointer bytes. You should never call this, call `get_bytes` or
     /// `get_bytes_with_undef_and_ptr` instead,
@@ -184,16 +146,17 @@
     /// This function also guarantees that the resulting pointer will remain stable
     /// even when new allocations are pushed to the `HashMap`. `copy_repeatedly` relies
     /// on that.
+    ///
+    /// It is the caller's responsibility to check bounds and alignment beforehand.
     fn get_bytes_internal(
         &self,
         cx: &impl HasDataLayout,
         ptr: Pointer<Tag>,
         size: Size,
         check_defined_and_ptr: bool,
-        msg: CheckInAllocMsg,
     ) -> InterpResult<'tcx, &[u8]>
     {
-        self.check_bounds(cx, ptr, size, msg)?;
+        let range = self.check_bounds(ptr.offset, size);
 
         if check_defined_and_ptr {
             self.check_defined(ptr, size)?;
@@ -205,12 +168,13 @@
 
         AllocationExtra::memory_read(self, ptr, size)?;
 
-        assert_eq!(ptr.offset.bytes() as usize as u64, ptr.offset.bytes());
-        assert_eq!(size.bytes() as usize as u64, size.bytes());
-        let offset = ptr.offset.bytes() as usize;
-        Ok(&self.bytes[offset..offset + size.bytes() as usize])
+        Ok(&self.bytes[range])
     }
 
+    /// Check that these bytes are initialized and not pointer bytes, and then return them
+    /// as a slice.
+    ///
+    /// It is the caller's responsibility to check bounds and alignment beforehand.
     #[inline]
     pub fn get_bytes(
         &self,
@@ -219,11 +183,13 @@
         size: Size,
     ) -> InterpResult<'tcx, &[u8]>
     {
-        self.get_bytes_internal(cx, ptr, size, true, CheckInAllocMsg::MemoryAccessTest)
+        self.get_bytes_internal(cx, ptr, size, true)
     }
 
     /// It is the caller's responsibility to handle undefined and pointer bytes.
     /// However, this still checks that there are no relocations on the *edges*.
+    ///
+    /// It is the caller's responsibility to check bounds and alignment beforehand.
     #[inline]
     pub fn get_bytes_with_undef_and_ptr(
         &self,
@@ -232,11 +198,13 @@
         size: Size,
     ) -> InterpResult<'tcx, &[u8]>
     {
-        self.get_bytes_internal(cx, ptr, size, false, CheckInAllocMsg::MemoryAccessTest)
+        self.get_bytes_internal(cx, ptr, size, false)
     }
 
     /// Just calling this already marks everything as defined and removes relocations,
     /// so be sure to actually put data there!
+    ///
+    /// It is the caller's responsibility to check bounds and alignment beforehand.
     pub fn get_bytes_mut(
         &mut self,
         cx: &impl HasDataLayout,
@@ -244,18 +212,14 @@
         size: Size,
     ) -> InterpResult<'tcx, &mut [u8]>
     {
-        assert_ne!(size.bytes(), 0, "0-sized accesses should never even get a `Pointer`");
-        self.check_bounds(cx, ptr, size, CheckInAllocMsg::MemoryAccessTest)?;
+        let range = self.check_bounds(ptr.offset, size);
 
         self.mark_definedness(ptr, size, true);
         self.clear_relocations(cx, ptr, size)?;
 
         AllocationExtra::memory_written(self, ptr, size)?;
 
-        assert_eq!(ptr.offset.bytes() as usize as u64, ptr.offset.bytes());
-        assert_eq!(size.bytes() as usize as u64, size.bytes());
-        let offset = ptr.offset.bytes() as usize;
-        Ok(&mut self.bytes[offset..offset + size.bytes() as usize])
+        Ok(&mut self.bytes[range])
     }
 }
 
@@ -276,9 +240,10 @@
                 let size_with_null = Size::from_bytes((size + 1) as u64);
                 // Go through `get_bytes` for checks and AllocationExtra hooks.
                 // We read the null, so we include it in the request, but we want it removed
-                // from the result!
+                // from the result, so we do subslicing.
                 Ok(&self.get_bytes(cx, ptr, size_with_null)?[..size])
             }
+            // This includes the case where `offset` is out-of-bounds to begin with.
             None => err!(UnterminatedCString(ptr.erase_tag())),
         }
     }
@@ -306,7 +271,7 @@
 
     /// Writes `src` to the memory starting at `ptr.offset`.
     ///
-    /// Will do bounds checks on the allocation.
+    /// It is the caller's responsibility to check bounds and alignment beforehand.
     pub fn write_bytes(
         &mut self,
         cx: &impl HasDataLayout,
@@ -320,6 +285,8 @@
     }
 
     /// Sets `count` bytes starting at `ptr.offset` with `val`. Basically `memset`.
+    ///
+    /// It is the caller's responsibility to check bounds and alignment beforehand.
     pub fn write_repeat(
         &mut self,
         cx: &impl HasDataLayout,
@@ -342,7 +309,7 @@
     /// * in oder to obtain a `Pointer` we need to check for ZSTness anyway due to integer pointers
     ///   being valid for ZSTs
     ///
-    /// Note: This function does not do *any* alignment checks, you need to do these before calling
+    /// It is the caller's responsibility to check bounds and alignment beforehand.
     pub fn read_scalar(
         &self,
         cx: &impl HasDataLayout,
@@ -378,7 +345,9 @@
         Ok(ScalarMaybeUndef::Scalar(Scalar::from_uint(bits, size)))
     }
 
-    /// Note: This function does not do *any* alignment checks, you need to do these before calling
+    /// Read a pointer-sized scalar.
+    ///
+    /// It is the caller's responsibility to check bounds and alignment beforehand.
     pub fn read_ptr_sized(
         &self,
         cx: &impl HasDataLayout,
@@ -395,7 +364,7 @@
     /// * in oder to obtain a `Pointer` we need to check for ZSTness anyway due to integer pointers
     ///   being valid for ZSTs
     ///
-    /// Note: This function does not do *any* alignment checks, you need to do these before calling
+    /// It is the caller's responsibility to check bounds and alignment beforehand.
     pub fn write_scalar(
         &mut self,
         cx: &impl HasDataLayout,
@@ -435,7 +404,9 @@
         Ok(())
     }
 
-    /// Note: This function does not do *any* alignment checks, you need to do these before calling
+    /// Write a pointer-sized scalar.
+    ///
+    /// It is the caller's responsibility to check bounds and alignment beforehand.
     pub fn write_ptr_sized(
         &mut self,
         cx: &impl HasDataLayout,
diff --git a/src/librustc/mir/interpret/mod.rs b/src/librustc/mir/interpret/mod.rs
index a36c788..1b29425 100644
--- a/src/librustc/mir/interpret/mod.rs
+++ b/src/librustc/mir/interpret/mod.rs
@@ -17,12 +17,9 @@
 
 pub use self::value::{Scalar, ScalarMaybeUndef, RawConst, ConstValue};
 
-pub use self::allocation::{
-    InboundsCheck, Allocation, AllocationExtra,
-    Relocations, UndefMask, CheckInAllocMsg,
-};
+pub use self::allocation::{Allocation, AllocationExtra, Relocations, UndefMask};
 
-pub use self::pointer::{Pointer, PointerArithmetic};
+pub use self::pointer::{Pointer, PointerArithmetic, CheckInAllocMsg};
 
 use std::fmt;
 use crate::mir;
diff --git a/src/librustc/mir/interpret/pointer.rs b/src/librustc/mir/interpret/pointer.rs
index 26002a4..a17bc1f 100644
--- a/src/librustc/mir/interpret/pointer.rs
+++ b/src/librustc/mir/interpret/pointer.rs
@@ -1,13 +1,35 @@
-use std::fmt;
+use std::fmt::{self, Display};
 
 use crate::mir;
 use crate::ty::layout::{self, HasDataLayout, Size};
 use rustc_macros::HashStable;
 
 use super::{
-    AllocId, InterpResult, CheckInAllocMsg
+    AllocId, InterpResult,
 };
 
+/// Used by `check_in_alloc` to indicate context of check
+#[derive(Debug, Copy, Clone, RustcEncodable, RustcDecodable, HashStable)]
+pub enum CheckInAllocMsg {
+    MemoryAccessTest,
+    NullPointerTest,
+    PointerArithmeticTest,
+    InboundsTest,
+}
+
+impl Display for CheckInAllocMsg {
+    /// When this is printed as an error the context looks like this
+    /// "{test name} failed: pointer must be in-bounds at offset..."
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "{}", match *self {
+            CheckInAllocMsg::MemoryAccessTest => "Memory access",
+            CheckInAllocMsg::NullPointerTest => "Null pointer test",
+            CheckInAllocMsg::PointerArithmeticTest => "Pointer arithmetic",
+            CheckInAllocMsg::InboundsTest => "Inbounds test",
+        })
+    }
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // Pointer arithmetic
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs
index d04b9ac..bb4ef2d 100644
--- a/src/librustc/session/mod.rs
+++ b/src/librustc/session/mod.rs
@@ -320,8 +320,13 @@
         self.diagnostic().abort_if_errors();
     }
     pub fn compile_status(&self) -> Result<(), ErrorReported> {
-        compile_result_from_err_count(self.err_count())
+        if self.has_errors() {
+            Err(ErrorReported)
+        } else {
+            Ok(())
+        }
     }
+    // FIXME(matthewjasper) Remove this method, it should never be needed.
     pub fn track_errors<F, T>(&self, f: F) -> Result<T, ErrorReported>
     where
         F: FnOnce() -> T,
@@ -1388,11 +1393,3 @@
 }
 
 pub type CompileResult = Result<(), ErrorReported>;
-
-pub fn compile_result_from_err_count(err_count: usize) -> CompileResult {
-    if err_count == 0 {
-        Ok(())
-    } else {
-        Err(ErrorReported)
-    }
-}
diff --git a/src/librustc/ty/inhabitedness/mod.rs b/src/librustc/ty/inhabitedness/mod.rs
index 5ce7508..0d96e5e 100644
--- a/src/librustc/ty/inhabitedness/mod.rs
+++ b/src/librustc/ty/inhabitedness/mod.rs
@@ -97,7 +97,7 @@
         self.ty_inhabitedness_forest(ty).contains(self, module)
     }
 
-    pub fn is_ty_uninhabited_from_all_modules(self, ty: Ty<'tcx>) -> bool {
+    pub fn is_ty_uninhabited_from_any_module(self, ty: Ty<'tcx>) -> bool {
         !self.ty_inhabitedness_forest(ty).is_empty()
     }
 
diff --git a/src/librustc_data_structures/bit_set.rs b/src/librustc_data_structures/bit_set.rs
index 3430d83..5d8388d 100644
--- a/src/librustc_data_structures/bit_set.rs
+++ b/src/librustc_data_structures/bit_set.rs
@@ -316,11 +316,6 @@
     }
 }
 
-pub trait BitSetOperator {
-    /// Combine one bitset into another.
-    fn join<T: Idx>(&self, inout_set: &mut BitSet<T>, in_set: &BitSet<T>) -> bool;
-}
-
 #[inline]
 fn bitwise<Op>(out_vec: &mut [Word], in_vec: &[Word], op: Op) -> bool
     where Op: Fn(Word, Word) -> Word
diff --git a/src/librustc_errors/lib.rs b/src/librustc_errors/lib.rs
index 05cee6d..70bd25a 100644
--- a/src/librustc_errors/lib.rs
+++ b/src/librustc_errors/lib.rs
@@ -307,7 +307,12 @@
 pub struct Handler {
     pub flags: HandlerFlags,
 
+    /// The number of errors that have been emitted, including duplicates.
+    ///
+    /// This is not necessarily the count that's reported to the user once
+    /// compilation ends.
     err_count: AtomicUsize,
+    deduplicated_err_count: AtomicUsize,
     emitter: Lock<Box<dyn Emitter + sync::Send>>,
     continue_after_error: AtomicBool,
     delayed_span_bugs: Lock<Vec<Diagnostic>>,
@@ -352,7 +357,7 @@
 
 impl Drop for Handler {
     fn drop(&mut self) {
-        if self.err_count() == 0 {
+        if !self.has_errors() {
             let mut bugs = self.delayed_span_bugs.borrow_mut();
             let has_bugs = !bugs.is_empty();
             for bug in bugs.drain(..) {
@@ -407,6 +412,7 @@
         Handler {
             flags,
             err_count: AtomicUsize::new(0),
+            deduplicated_err_count: AtomicUsize::new(0),
             emitter: Lock::new(e),
             continue_after_error: AtomicBool::new(true),
             delayed_span_bugs: Lock::new(Vec::new()),
@@ -428,6 +434,7 @@
     pub fn reset_err_count(&self) {
         // actually frees the underlying memory (which `clear` would not do)
         *self.emitted_diagnostics.borrow_mut() = Default::default();
+        self.deduplicated_err_count.store(0, SeqCst);
         self.err_count.store(0, SeqCst);
     }
 
@@ -660,10 +667,10 @@
     }
 
     pub fn print_error_count(&self, registry: &Registry) {
-        let s = match self.err_count() {
+        let s = match self.deduplicated_err_count.load(SeqCst) {
             0 => return,
             1 => "aborting due to previous error".to_string(),
-            _ => format!("aborting due to {} previous errors", self.err_count())
+            count => format!("aborting due to {} previous errors", count)
         };
         if self.treat_err_as_bug() {
             return;
@@ -705,10 +712,9 @@
     }
 
     pub fn abort_if_errors(&self) {
-        if self.err_count() == 0 {
-            return;
+        if self.has_errors() {
+            FatalError.raise();
         }
-        FatalError.raise();
     }
     pub fn emit(&self, msp: &MultiSpan, msg: &str, lvl: Level) {
         if lvl == Warning && !self.flags.can_emit_warnings {
@@ -770,9 +776,12 @@
         if self.emitted_diagnostics.borrow_mut().insert(diagnostic_hash) {
             self.emitter.borrow_mut().emit_diagnostic(db);
             if db.is_error() {
-                self.bump_err_count();
+                self.deduplicated_err_count.fetch_add(1, SeqCst);
             }
         }
+        if db.is_error() {
+            self.bump_err_count();
+        }
     }
 
     pub fn emit_artifact_notification(&self, path: &Path, artifact_type: &str) {
diff --git a/src/librustc_interface/passes.rs b/src/librustc_interface/passes.rs
index 69cb696..c1b6e34 100644
--- a/src/librustc_interface/passes.rs
+++ b/src/librustc_interface/passes.rs
@@ -959,7 +959,7 @@
     // lot of annoying errors in the compile-fail tests (basically,
     // lint warnings and so on -- kindck used to do this abort, but
     // kindck is gone now). -nmatsakis
-    if sess.err_count() > 0 {
+    if sess.has_errors() {
         return Err(ErrorReported);
     }
 
diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs
index 67bcd49..887ef4b 100644
--- a/src/librustc_mir/const_eval.rs
+++ b/src/librustc_mir/const_eval.rs
@@ -15,7 +15,6 @@
 use rustc::ty::layout::{self, LayoutOf, VariantIdx};
 use rustc::ty::subst::Subst;
 use rustc::traits::Reveal;
-use rustc::util::common::ErrorReported;
 use rustc_data_structures::fx::FxHashMap;
 
 use syntax::source_map::{Span, DUMMY_SP};
@@ -655,19 +654,12 @@
         if tcx.is_static(def_id) {
             // Ensure that if the above error was either `TooGeneric` or `Reported`
             // an error must be reported.
-            let reported_err = tcx.sess.track_errors(|| {
-                err.report_as_error(ecx.tcx,
-                                    "could not evaluate static initializer")
-            });
-            match reported_err {
-                Ok(v) => {
-                    tcx.sess.delay_span_bug(err.span,
-                                        &format!("static eval failure did not emit an error: {:#?}",
-                                        v));
-                    v
-                },
-                Err(ErrorReported) => ErrorHandled::Reported,
-            }
+            let v = err.report_as_error(ecx.tcx, "could not evaluate static initializer");
+            tcx.sess.delay_span_bug(
+                err.span,
+                &format!("static eval failure did not emit an error: {:#?}", v)
+            );
+            v
         } else if def_id.is_local() {
             // constant defined in this crate, we can figure out a lint level!
             match tcx.def_kind(def_id) {
diff --git a/src/librustc_mir/dataflow/at_location.rs b/src/librustc_mir/dataflow/at_location.rs
index 9cba34b..7735528 100644
--- a/src/librustc_mir/dataflow/at_location.rs
+++ b/src/librustc_mir/dataflow/at_location.rs
@@ -4,7 +4,7 @@
 use rustc::mir::{BasicBlock, Location};
 use rustc_data_structures::bit_set::{BitIter, BitSet, HybridBitSet};
 
-use crate::dataflow::{BitDenotation, BlockSets, DataflowResults};
+use crate::dataflow::{BitDenotation, DataflowResults, GenKillSet};
 use crate::dataflow::move_paths::{HasMoveData, MovePathIndex};
 
 use std::iter;
@@ -66,8 +66,7 @@
 {
     base_results: DataflowResults<'tcx, BD>,
     curr_state: BitSet<BD::Idx>,
-    stmt_gen: HybridBitSet<BD::Idx>,
-    stmt_kill: HybridBitSet<BD::Idx>,
+    stmt_trans: GenKillSet<BD::Idx>,
 }
 
 impl<'tcx, BD> FlowAtLocation<'tcx, BD>
@@ -89,19 +88,17 @@
     where
         F: FnMut(BD::Idx),
     {
-        self.stmt_gen.iter().for_each(f)
+        self.stmt_trans.gen_set.iter().for_each(f)
     }
 
     pub fn new(results: DataflowResults<'tcx, BD>) -> Self {
         let bits_per_block = results.sets().bits_per_block();
         let curr_state = BitSet::new_empty(bits_per_block);
-        let stmt_gen = HybridBitSet::new_empty(bits_per_block);
-        let stmt_kill = HybridBitSet::new_empty(bits_per_block);
+        let stmt_trans = GenKillSet::from_elem(HybridBitSet::new_empty(bits_per_block));
         FlowAtLocation {
             base_results: results,
-            curr_state: curr_state,
-            stmt_gen: stmt_gen,
-            stmt_kill: stmt_kill,
+            curr_state,
+            stmt_trans,
         }
     }
 
@@ -127,8 +124,7 @@
         F: FnOnce(BitIter<'_, BD::Idx>),
     {
         let mut curr_state = self.curr_state.clone();
-        curr_state.union(&self.stmt_gen);
-        curr_state.subtract(&self.stmt_kill);
+        self.stmt_trans.apply(&mut curr_state);
         f(curr_state.iter());
     }
 
@@ -142,68 +138,41 @@
     where BD: BitDenotation<'tcx>
 {
     fn reset_to_entry_of(&mut self, bb: BasicBlock) {
-        self.curr_state.overwrite(self.base_results.sets().on_entry_set_for(bb.index()));
+        self.curr_state.overwrite(self.base_results.sets().entry_set_for(bb.index()));
     }
 
     fn reset_to_exit_of(&mut self, bb: BasicBlock) {
         self.reset_to_entry_of(bb);
-        self.curr_state.union(self.base_results.sets().gen_set_for(bb.index()));
-        self.curr_state.subtract(self.base_results.sets().kill_set_for(bb.index()));
+        let trans = self.base_results.sets().trans_for(bb.index());
+        trans.apply(&mut self.curr_state)
     }
 
     fn reconstruct_statement_effect(&mut self, loc: Location) {
-        self.stmt_gen.clear();
-        self.stmt_kill.clear();
-        {
-            let mut sets = BlockSets {
-                on_entry: &mut self.curr_state,
-                gen_set: &mut self.stmt_gen,
-                kill_set: &mut self.stmt_kill,
-            };
-            self.base_results
-                .operator()
-                .before_statement_effect(&mut sets, loc);
-        }
-        self.apply_local_effect(loc);
-
-        let mut sets = BlockSets {
-            on_entry: &mut self.curr_state,
-            gen_set: &mut self.stmt_gen,
-            kill_set: &mut self.stmt_kill,
-        };
+        self.stmt_trans.clear();
         self.base_results
             .operator()
-            .statement_effect(&mut sets, loc);
+            .before_statement_effect(&mut self.stmt_trans, loc);
+        self.stmt_trans.apply(&mut self.curr_state);
+
+        self.base_results
+            .operator()
+            .statement_effect(&mut self.stmt_trans, loc);
     }
 
     fn reconstruct_terminator_effect(&mut self, loc: Location) {
-        self.stmt_gen.clear();
-        self.stmt_kill.clear();
-        {
-            let mut sets = BlockSets {
-                on_entry: &mut self.curr_state,
-                gen_set: &mut self.stmt_gen,
-                kill_set: &mut self.stmt_kill,
-            };
-            self.base_results
-                .operator()
-                .before_terminator_effect(&mut sets, loc);
-        }
-        self.apply_local_effect(loc);
-
-        let mut sets = BlockSets {
-            on_entry: &mut self.curr_state,
-            gen_set: &mut self.stmt_gen,
-            kill_set: &mut self.stmt_kill,
-        };
+        self.stmt_trans.clear();
         self.base_results
             .operator()
-            .terminator_effect(&mut sets, loc);
+            .before_terminator_effect(&mut self.stmt_trans, loc);
+        self.stmt_trans.apply(&mut self.curr_state);
+
+        self.base_results
+            .operator()
+            .terminator_effect(&mut self.stmt_trans, loc);
     }
 
     fn apply_local_effect(&mut self, _loc: Location) {
-        self.curr_state.union(&self.stmt_gen);
-        self.curr_state.subtract(&self.stmt_kill);
+        self.stmt_trans.apply(&mut self.curr_state)
     }
 }
 
diff --git a/src/librustc_mir/dataflow/graphviz.rs b/src/librustc_mir/dataflow/graphviz.rs
index 7896592..b0d8581 100644
--- a/src/librustc_mir/dataflow/graphviz.rs
+++ b/src/librustc_mir/dataflow/graphviz.rs
@@ -170,7 +170,7 @@
 
         write!(w, "<tr>")?;
         // Entry
-        dump_set_for!(on_entry_set_for, interpret_set);
+        dump_set_for!(entry_set_for, interpret_set);
 
         // MIR statements
         write!(w, "<td>")?;
@@ -208,7 +208,7 @@
         write!(w, "<tr>")?;
 
         // Entry
-        let set = flow.sets.on_entry_set_for(i);
+        let set = flow.sets.entry_set_for(i);
         write!(w, "<td>{:?}</td>", dot::escape_html(&set.to_string()))?;
 
         // Terminator
@@ -221,13 +221,10 @@
         }
         write!(w, "</td>")?;
 
-        // Gen
-        let set = flow.sets.gen_set_for(i);
-        write!(w, "<td>{:?}</td>", dot::escape_html(&format!("{:?}", set)))?;
-
-        // Kill
-        let set = flow.sets.kill_set_for(i);
-        write!(w, "<td>{:?}</td>", dot::escape_html(&format!("{:?}", set)))?;
+        // Gen/Kill
+        let trans = flow.sets.trans_for(i);
+        write!(w, "<td>{:?}</td>", dot::escape_html(&format!("{:?}", trans.gen_set)))?;
+        write!(w, "<td>{:?}</td>", dot::escape_html(&format!("{:?}", trans.kill_set)))?;
 
         write!(w, "</tr>")?;
 
diff --git a/src/librustc_mir/dataflow/impls/borrowed_locals.rs b/src/librustc_mir/dataflow/impls/borrowed_locals.rs
index 069ce3a..0f7f37f 100644
--- a/src/librustc_mir/dataflow/impls/borrowed_locals.rs
+++ b/src/librustc_mir/dataflow/impls/borrowed_locals.rs
@@ -2,7 +2,7 @@
 
 use rustc::mir::*;
 use rustc::mir::visit::Visitor;
-use crate::dataflow::BitDenotation;
+use crate::dataflow::{BitDenotation, GenKillSet};
 
 /// This calculates if any part of a MIR local could have previously been borrowed.
 /// This means that once a local has been borrowed, its bit will be set
@@ -33,39 +33,39 @@
         self.body.local_decls.len()
     }
 
-    fn start_block_effect(&self, _sets: &mut BitSet<Local>) {
+    fn start_block_effect(&self, _on_entry: &mut BitSet<Local>) {
         // Nothing is borrowed on function entry
     }
 
     fn statement_effect(&self,
-                        sets: &mut BlockSets<'_, Local>,
+                        trans: &mut GenKillSet<Local>,
                         loc: Location) {
         let stmt = &self.body[loc.block].statements[loc.statement_index];
 
         BorrowedLocalsVisitor {
-            sets,
+            trans,
         }.visit_statement(stmt, loc);
 
         // StorageDead invalidates all borrows and raw pointers to a local
         match stmt.kind {
-            StatementKind::StorageDead(l) => sets.kill(l),
+            StatementKind::StorageDead(l) => trans.kill(l),
             _ => (),
         }
     }
 
     fn terminator_effect(&self,
-                         sets: &mut BlockSets<'_, Local>,
+                         trans: &mut GenKillSet<Local>,
                          loc: Location) {
         let terminator = self.body[loc.block].terminator();
         BorrowedLocalsVisitor {
-            sets,
+            trans,
         }.visit_terminator(terminator, loc);
         match &terminator.kind {
             // Drop terminators borrows the location
             TerminatorKind::Drop { location, .. } |
             TerminatorKind::DropAndReplace { location, .. } => {
                 if let Some(local) = find_local(location) {
-                    sets.gen(local);
+                    trans.gen(local);
                 }
             }
             _ => (),
@@ -83,22 +83,13 @@
     }
 }
 
-impl<'a, 'tcx> BitSetOperator for HaveBeenBorrowedLocals<'a, 'tcx> {
-    #[inline]
-    fn join<T: Idx>(&self, inout_set: &mut BitSet<T>, in_set: &BitSet<T>) -> bool {
-        inout_set.union(in_set) // "maybe" means we union effects of both preds
-    }
+impl<'a, 'tcx> BottomValue for HaveBeenBorrowedLocals<'a, 'tcx> {
+    // bottom = unborrowed
+    const BOTTOM_VALUE: bool = false;
 }
 
-impl<'a, 'tcx> InitialFlow for HaveBeenBorrowedLocals<'a, 'tcx> {
-    #[inline]
-    fn bottom_value() -> bool {
-        false // bottom = unborrowed
-    }
-}
-
-struct BorrowedLocalsVisitor<'b, 'c> {
-    sets: &'b mut BlockSets<'c, Local>,
+struct BorrowedLocalsVisitor<'gk> {
+    trans: &'gk mut GenKillSet<Local>,
 }
 
 fn find_local<'tcx>(place: &Place<'tcx>) -> Option<Local> {
@@ -117,13 +108,13 @@
     })
 }
 
-impl<'tcx, 'b, 'c> Visitor<'tcx> for BorrowedLocalsVisitor<'b, 'c> {
+impl<'tcx> Visitor<'tcx> for BorrowedLocalsVisitor<'_> {
     fn visit_rvalue(&mut self,
                     rvalue: &Rvalue<'tcx>,
                     location: Location) {
         if let Rvalue::Ref(_, _, ref place) = *rvalue {
             if let Some(local) = find_local(place) {
-                self.sets.gen(local);
+                self.trans.gen(local);
             }
         }
 
diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs
index 7617d3b..53d00d4 100644
--- a/src/librustc_mir/dataflow/impls/borrows.rs
+++ b/src/librustc_mir/dataflow/impls/borrows.rs
@@ -5,11 +5,11 @@
 use rustc::ty::TyCtxt;
 use rustc::ty::RegionVid;
 
-use rustc_data_structures::bit_set::{BitSet, BitSetOperator};
+use rustc_data_structures::bit_set::BitSet;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::indexed_vec::{Idx, IndexVec};
 
-use crate::dataflow::{BitDenotation, BlockSets, InitialFlow};
+use crate::dataflow::{BitDenotation, BottomValue, GenKillSet};
 use crate::borrow_check::nll::region_infer::RegionInferenceContext;
 use crate::borrow_check::nll::ToRegionVid;
 use crate::borrow_check::places_conflict;
@@ -168,7 +168,7 @@
     /// Add all borrows to the kill set, if those borrows are out of scope at `location`.
     /// That means they went out of a nonlexical scope
     fn kill_loans_out_of_scope_at_location(&self,
-                                           sets: &mut BlockSets<'_, BorrowIndex>,
+                                           trans: &mut GenKillSet<BorrowIndex>,
                                            location: Location) {
         // NOTE: The state associated with a given `location`
         // reflects the dataflow on entry to the statement.
@@ -182,14 +182,14 @@
         // region, then setting that gen-bit will override any
         // potential kill introduced here.
         if let Some(indices) = self.borrows_out_of_scope_at_location.get(&location) {
-            sets.kill_all(indices);
+            trans.kill_all(indices);
         }
     }
 
     /// Kill any borrows that conflict with `place`.
     fn kill_borrows_on_place(
         &self,
-        sets: &mut BlockSets<'_, BorrowIndex>,
+        trans: &mut GenKillSet<BorrowIndex>,
         place: &Place<'tcx>
     ) {
         debug!("kill_borrows_on_place: place={:?}", place);
@@ -206,7 +206,7 @@
             // 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);
+                trans.kill_all(other_borrows_of_local);
                 return;
             }
 
@@ -224,7 +224,7 @@
                         places_conflict::PlaceConflictBias::NoOverlap)
                 });
 
-            sets.kill_all(definitely_conflicting_borrows);
+            trans.kill_all(definitely_conflicting_borrows);
         }
     }
 }
@@ -236,21 +236,24 @@
         self.borrow_set.borrows.len() * 2
     }
 
-    fn start_block_effect(&self, _entry_set: &mut BitSet<BorrowIndex>) {
+    fn start_block_effect(&self, _entry_set: &mut BitSet<Self::Idx>) {
         // no borrows of code region_scopes have been taken prior to
-        // function execution, so this method has no effect on
-        // `_sets`.
+        // function execution, so this method has no effect.
     }
 
     fn before_statement_effect(&self,
-                               sets: &mut BlockSets<'_, BorrowIndex>,
+                               trans: &mut GenKillSet<Self::Idx>,
                                location: Location) {
-        debug!("Borrows::before_statement_effect sets: {:?} location: {:?}", sets, location);
-        self.kill_loans_out_of_scope_at_location(sets, location);
+        debug!("Borrows::before_statement_effect trans: {:?} location: {:?}",
+               trans, location);
+        self.kill_loans_out_of_scope_at_location(trans, location);
     }
 
-    fn statement_effect(&self, sets: &mut BlockSets<'_, BorrowIndex>, location: Location) {
-        debug!("Borrows::statement_effect: sets={:?} location={:?}", sets, location);
+    fn statement_effect(&self,
+                        trans: &mut GenKillSet<Self::Idx>,
+                        location: Location) {
+        debug!("Borrows::statement_effect: trans={:?} location={:?}",
+               trans, location);
 
         let block = &self.body.basic_blocks().get(location.block).unwrap_or_else(|| {
             panic!("could not find block at location {:?}", location);
@@ -264,7 +267,7 @@
             mir::StatementKind::Assign(ref lhs, ref rhs) => {
                 // Make sure there are no remaining borrows for variables
                 // that are assigned over.
-                self.kill_borrows_on_place(sets, lhs);
+                self.kill_borrows_on_place(trans, lhs);
 
                 if let mir::Rvalue::Ref(_, _, ref place) = **rhs {
                     if place.ignore_borrow(
@@ -278,20 +281,20 @@
                         panic!("could not find BorrowIndex for location {:?}", location);
                     });
 
-                    sets.gen(*index);
+                    trans.gen(*index);
                 }
             }
 
             mir::StatementKind::StorageDead(local) => {
                 // Make sure there are no remaining borrows for locals that
                 // are gone out of scope.
-                self.kill_borrows_on_place(sets, &Place::Base(PlaceBase::Local(local)));
+                self.kill_borrows_on_place(trans, &Place::Base(PlaceBase::Local(local)));
             }
 
             mir::StatementKind::InlineAsm(ref asm) => {
                 for (output, kind) in asm.outputs.iter().zip(&asm.asm.outputs) {
                     if !kind.is_indirect && !kind.is_rw {
-                        self.kill_borrows_on_place(sets, output);
+                        self.kill_borrows_on_place(trans, output);
                     }
                 }
             }
@@ -307,13 +310,16 @@
     }
 
     fn before_terminator_effect(&self,
-                                sets: &mut BlockSets<'_, BorrowIndex>,
+                                trans: &mut GenKillSet<Self::Idx>,
                                 location: Location) {
-        debug!("Borrows::before_terminator_effect sets: {:?} location: {:?}", sets, location);
-        self.kill_loans_out_of_scope_at_location(sets, location);
+        debug!("Borrows::before_terminator_effect: trans={:?} location={:?}",
+               trans, location);
+        self.kill_loans_out_of_scope_at_location(trans, location);
     }
 
-    fn terminator_effect(&self, _: &mut BlockSets<'_, BorrowIndex>, _: Location) {}
+    fn terminator_effect(&self,
+                         _: &mut GenKillSet<Self::Idx>,
+                         _: Location) {}
 
     fn propagate_call_return(
         &self,
@@ -325,16 +331,7 @@
     }
 }
 
-impl<'a, 'tcx> BitSetOperator for Borrows<'a, 'tcx> {
-    #[inline]
-    fn join<T: Idx>(&self, inout_set: &mut BitSet<T>, in_set: &BitSet<T>) -> bool {
-        inout_set.union(in_set) // "maybe" means we union effects of both preds
-    }
-}
-
-impl<'a, 'tcx> InitialFlow for Borrows<'a, 'tcx> {
-    #[inline]
-    fn bottom_value() -> bool {
-        false // bottom = nothing is reserved or activated yet
-    }
+impl<'a, 'tcx> BottomValue for Borrows<'a, 'tcx> {
+    /// bottom = nothing is reserved or activated yet;
+    const BOTTOM_VALUE: bool = false;
 }
diff --git a/src/librustc_mir/dataflow/impls/mod.rs b/src/librustc_mir/dataflow/impls/mod.rs
index 0a572d2..065cfe8 100644
--- a/src/librustc_mir/dataflow/impls/mod.rs
+++ b/src/librustc_mir/dataflow/impls/mod.rs
@@ -4,7 +4,7 @@
 
 use rustc::ty::TyCtxt;
 use rustc::mir::{self, Body, Location};
-use rustc_data_structures::bit_set::{BitSet, BitSetOperator};
+use rustc_data_structures::bit_set::BitSet;
 use rustc_data_structures::indexed_vec::Idx;
 
 use super::MoveDataParamEnv;
@@ -12,7 +12,7 @@
 use crate::util::elaborate_drops::DropFlagState;
 
 use super::move_paths::{HasMoveData, MoveData, MovePathIndex, InitIndex, InitKind};
-use super::{BitDenotation, BlockSets, InitialFlow};
+use super::{BitDenotation, BottomValue, GenKillSet};
 
 use super::drop_flag_effects_for_function_entry;
 use super::drop_flag_effects_for_location;
@@ -226,34 +226,37 @@
 }
 
 impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> {
-    fn update_bits(sets: &mut BlockSets<'_, MovePathIndex>, path: MovePathIndex,
+    fn update_bits(trans: &mut GenKillSet<MovePathIndex>,
+                   path: MovePathIndex,
                    state: DropFlagState)
     {
         match state {
-            DropFlagState::Absent => sets.kill(path),
-            DropFlagState::Present => sets.gen(path),
+            DropFlagState::Absent => trans.kill(path),
+            DropFlagState::Present => trans.gen(path),
         }
     }
 }
 
 impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> {
-    fn update_bits(sets: &mut BlockSets<'_, MovePathIndex>, path: MovePathIndex,
+    fn update_bits(trans: &mut GenKillSet<MovePathIndex>,
+                   path: MovePathIndex,
                    state: DropFlagState)
     {
         match state {
-            DropFlagState::Absent => sets.gen(path),
-            DropFlagState::Present => sets.kill(path),
+            DropFlagState::Absent => trans.gen(path),
+            DropFlagState::Present => trans.kill(path),
         }
     }
 }
 
 impl<'a, 'tcx> DefinitelyInitializedPlaces<'a, 'tcx> {
-    fn update_bits(sets: &mut BlockSets<'_, MovePathIndex>, path: MovePathIndex,
+    fn update_bits(trans: &mut GenKillSet<MovePathIndex>,
+                   path: MovePathIndex,
                    state: DropFlagState)
     {
         match state {
-            DropFlagState::Absent => sets.kill(path),
-            DropFlagState::Present => sets.gen(path),
+            DropFlagState::Absent => trans.kill(path),
+            DropFlagState::Present => trans.gen(path),
         }
     }
 }
@@ -275,24 +278,24 @@
     }
 
     fn statement_effect(&self,
-                        sets: &mut BlockSets<'_, MovePathIndex>,
+                        trans: &mut GenKillSet<Self::Idx>,
                         location: Location)
     {
         drop_flag_effects_for_location(
             self.tcx, self.body, self.mdpe,
             location,
-            |path, s| Self::update_bits(sets, path, s)
+            |path, s| Self::update_bits(trans, path, s)
         )
     }
 
     fn terminator_effect(&self,
-                         sets: &mut BlockSets<'_, MovePathIndex>,
+                         trans: &mut GenKillSet<Self::Idx>,
                          location: Location)
     {
         drop_flag_effects_for_location(
             self.tcx, self.body, self.mdpe,
             location,
-            |path, s| Self::update_bits(sets, path, s)
+            |path, s| Self::update_bits(trans, path, s)
         )
     }
 
@@ -333,24 +336,24 @@
     }
 
     fn statement_effect(&self,
-                        sets: &mut BlockSets<'_, MovePathIndex>,
+                        trans: &mut GenKillSet<Self::Idx>,
                         location: Location)
     {
         drop_flag_effects_for_location(
             self.tcx, self.body, self.mdpe,
             location,
-            |path, s| Self::update_bits(sets, path, s)
+            |path, s| Self::update_bits(trans, path, s)
         )
     }
 
     fn terminator_effect(&self,
-                         sets: &mut BlockSets<'_, MovePathIndex>,
+                         trans: &mut GenKillSet<Self::Idx>,
                          location: Location)
     {
         drop_flag_effects_for_location(
             self.tcx, self.body, self.mdpe,
             location,
-            |path, s| Self::update_bits(sets, path, s)
+            |path, s| Self::update_bits(trans, path, s)
         )
     }
 
@@ -389,24 +392,24 @@
     }
 
     fn statement_effect(&self,
-                        sets: &mut BlockSets<'_, MovePathIndex>,
+                        trans: &mut GenKillSet<Self::Idx>,
                         location: Location)
     {
         drop_flag_effects_for_location(
             self.tcx, self.body, self.mdpe,
             location,
-            |path, s| Self::update_bits(sets, path, s)
+            |path, s| Self::update_bits(trans, path, s)
         )
     }
 
     fn terminator_effect(&self,
-                         sets: &mut BlockSets<'_, MovePathIndex>,
+                         trans: &mut GenKillSet<Self::Idx>,
                          location: Location)
     {
         drop_flag_effects_for_location(
             self.tcx, self.body, self.mdpe,
             location,
-            |path, s| Self::update_bits(sets, path, s)
+            |path, s| Self::update_bits(trans, path, s)
         )
     }
 
@@ -439,7 +442,7 @@
     }
 
     fn statement_effect(&self,
-                        sets: &mut BlockSets<'_, InitIndex>,
+                        trans: &mut GenKillSet<Self::Idx>,
                         location: Location) {
         let (_, body, move_data) = (self.tcx, self.body, self.move_data());
         let stmt = &body[location.block].statements[location.statement_index];
@@ -449,7 +452,7 @@
 
         debug!("statement {:?} at loc {:?} initializes move_indexes {:?}",
                stmt, location, &init_loc_map[location]);
-        sets.gen_all(&init_loc_map[location]);
+        trans.gen_all(&init_loc_map[location]);
 
         match stmt.kind {
             mir::StatementKind::StorageDead(local) => {
@@ -458,14 +461,14 @@
                 let move_path_index = rev_lookup.find_local(local);
                 debug!("stmt {:?} at loc {:?} clears the ever initialized status of {:?}",
                         stmt, location, &init_path_map[move_path_index]);
-                sets.kill_all(&init_path_map[move_path_index]);
+                trans.kill_all(&init_path_map[move_path_index]);
             }
             _ => {}
         }
     }
 
     fn terminator_effect(&self,
-                         sets: &mut BlockSets<'_, InitIndex>,
+                         trans: &mut GenKillSet<Self::Idx>,
                          location: Location)
     {
         let (body, move_data) = (self.body, self.move_data());
@@ -473,7 +476,7 @@
         let init_loc_map = &move_data.init_loc_map;
         debug!("terminator {:?} at loc {:?} initializes move_indexes {:?}",
                term, location, &init_loc_map[location]);
-        sets.gen_all(
+        trans.gen_all(
             init_loc_map[location].iter().filter(|init_index| {
                 move_data.inits[**init_index].kind != InitKind::NonPanicPathOnly
             })
@@ -502,68 +505,22 @@
     }
 }
 
-impl<'a, 'tcx> BitSetOperator for MaybeInitializedPlaces<'a, 'tcx> {
-    #[inline]
-    fn join<T: Idx>(&self, inout_set: &mut BitSet<T>, in_set: &BitSet<T>) -> bool {
-        inout_set.union(in_set) // "maybe" means we union effects of both preds
-    }
+impl<'a, 'tcx> BottomValue for MaybeInitializedPlaces<'a, 'tcx> {
+    /// bottom = uninitialized
+    const BOTTOM_VALUE: bool = false;
 }
 
-impl<'a, 'tcx> BitSetOperator for MaybeUninitializedPlaces<'a, 'tcx> {
-    #[inline]
-    fn join<T: Idx>(&self, inout_set: &mut BitSet<T>, in_set: &BitSet<T>) -> bool {
-        inout_set.union(in_set) // "maybe" means we union effects of both preds
-    }
+impl<'a, 'tcx> BottomValue for MaybeUninitializedPlaces<'a, 'tcx> {
+    /// bottom = initialized (start_block_effect counters this at outset)
+    const BOTTOM_VALUE: bool = false;
 }
 
-impl<'a, 'tcx> BitSetOperator for DefinitelyInitializedPlaces<'a, 'tcx> {
-    #[inline]
-    fn join<T: Idx>(&self, inout_set: &mut BitSet<T>, in_set: &BitSet<T>) -> bool {
-        inout_set.intersect(in_set) // "definitely" means we intersect effects of both preds
-    }
+impl<'a, 'tcx> BottomValue for DefinitelyInitializedPlaces<'a, 'tcx> {
+    /// bottom = initialized (start_block_effect counters this at outset)
+    const BOTTOM_VALUE: bool = true;
 }
 
-impl<'a, 'tcx> BitSetOperator for EverInitializedPlaces<'a, 'tcx> {
-    #[inline]
-    fn join<T: Idx>(&self, inout_set: &mut BitSet<T>, in_set: &BitSet<T>) -> bool {
-        inout_set.union(in_set) // inits from both preds are in scope
-    }
-}
-
-// The way that dataflow fixed point iteration works, you want to
-// start at bottom and work your way to a fixed point. Control-flow
-// merges will apply the `join` operator to each block entry's current
-// state (which starts at that bottom value).
-//
-// This means, for propagation across the graph, that you either want
-// to start at all-zeroes and then use Union as your merge when
-// propagating, or you start at all-ones and then use Intersect as
-// your merge when propagating.
-
-impl<'a, 'tcx> InitialFlow for MaybeInitializedPlaces<'a, 'tcx> {
-    #[inline]
-    fn bottom_value() -> bool {
-        false // bottom = uninitialized
-    }
-}
-
-impl<'a, 'tcx> InitialFlow for MaybeUninitializedPlaces<'a, 'tcx> {
-    #[inline]
-    fn bottom_value() -> bool {
-        false // bottom = initialized (start_block_effect counters this at outset)
-    }
-}
-
-impl<'a, 'tcx> InitialFlow for DefinitelyInitializedPlaces<'a, 'tcx> {
-    #[inline]
-    fn bottom_value() -> bool {
-        true // bottom = initialized (start_block_effect counters this at outset)
-    }
-}
-
-impl<'a, 'tcx> InitialFlow for EverInitializedPlaces<'a, 'tcx> {
-    #[inline]
-    fn bottom_value() -> bool {
-        false // bottom = no initialized variables by default
-    }
+impl<'a, 'tcx> BottomValue for EverInitializedPlaces<'a, 'tcx> {
+    /// bottom = no initialized variables by default
+    const BOTTOM_VALUE: bool = false;
 }
diff --git a/src/librustc_mir/dataflow/impls/storage_liveness.rs b/src/librustc_mir/dataflow/impls/storage_liveness.rs
index d7575b0..d200399 100644
--- a/src/librustc_mir/dataflow/impls/storage_liveness.rs
+++ b/src/librustc_mir/dataflow/impls/storage_liveness.rs
@@ -26,24 +26,24 @@
         self.body.local_decls.len()
     }
 
-    fn start_block_effect(&self, _sets: &mut BitSet<Local>) {
+    fn start_block_effect(&self, _on_entry: &mut BitSet<Local>) {
         // Nothing is live on function entry
     }
 
     fn statement_effect(&self,
-                        sets: &mut BlockSets<'_, Local>,
+                        trans: &mut GenKillSet<Local>,
                         loc: Location) {
         let stmt = &self.body[loc.block].statements[loc.statement_index];
 
         match stmt.kind {
-            StatementKind::StorageLive(l) => sets.gen(l),
-            StatementKind::StorageDead(l) => sets.kill(l),
+            StatementKind::StorageLive(l) => trans.gen(l),
+            StatementKind::StorageDead(l) => trans.kill(l),
             _ => (),
         }
     }
 
     fn terminator_effect(&self,
-                         _sets: &mut BlockSets<'_, Local>,
+                         _trans: &mut GenKillSet<Local>,
                          _loc: Location) {
         // Terminators have no effect
     }
@@ -59,16 +59,7 @@
     }
 }
 
-impl<'a, 'tcx> BitSetOperator for MaybeStorageLive<'a, 'tcx> {
-    #[inline]
-    fn join<T: Idx>(&self, inout_set: &mut BitSet<T>, in_set: &BitSet<T>) -> bool {
-        inout_set.union(in_set) // "maybe" means we union effects of both preds
-    }
-}
-
-impl<'a, 'tcx> InitialFlow for MaybeStorageLive<'a, 'tcx> {
-    #[inline]
-    fn bottom_value() -> bool {
-        false // bottom = dead
-    }
+impl<'a, 'tcx> BottomValue for MaybeStorageLive<'a, 'tcx> {
+    /// bottom = dead
+    const BOTTOM_VALUE: bool = false;
 }
diff --git a/src/librustc_mir/dataflow/mod.rs b/src/librustc_mir/dataflow/mod.rs
index 89b952e..80f65a9 100644
--- a/src/librustc_mir/dataflow/mod.rs
+++ b/src/librustc_mir/dataflow/mod.rs
@@ -1,7 +1,7 @@
 use syntax::ast::{self, MetaItem};
 use syntax::symbol::{Symbol, sym};
 
-use rustc_data_structures::bit_set::{BitSet, BitSetOperator, HybridBitSet};
+use rustc_data_structures::bit_set::{BitSet, HybridBitSet};
 use rustc_data_structures::indexed_vec::Idx;
 use rustc_data_structures::work_queue::WorkQueue;
 
@@ -131,7 +131,7 @@
     p: P,
 ) -> DataflowResults<'tcx, BD>
 where
-    BD: BitDenotation<'tcx> + InitialFlow,
+    BD: BitDenotation<'tcx>,
     P: Fn(&BD, BD::Idx) -> DebugFormatted,
 {
     let flow_state = DataflowAnalysis::new(body, dead_unwinds, bd);
@@ -199,42 +199,27 @@
     }
 
     fn build_sets(&mut self) {
-        // First we need to build the entry-, gen- and kill-sets.
-
-        {
-            let sets = &mut self.flow_state.sets.for_block(mir::START_BLOCK.index());
-            self.flow_state.operator.start_block_effect(&mut sets.on_entry);
-        }
-
+        // Build the transfer function for each block.
         for (bb, data) in self.body.basic_blocks().iter_enumerated() {
             let &mir::BasicBlockData { ref statements, ref terminator, is_cleanup: _ } = data;
 
-            let mut interim_state;
-            let sets = &mut self.flow_state.sets.for_block(bb.index());
-            let track_intrablock = BD::accumulates_intrablock_state();
-            if track_intrablock {
-                debug!("swapping in mutable on_entry, initially {:?}", sets.on_entry);
-                interim_state = sets.on_entry.to_owned();
-                sets.on_entry = &mut interim_state;
-            }
+            let trans = self.flow_state.sets.trans_mut_for(bb.index());
             for j_stmt in 0..statements.len() {
                 let location = Location { block: bb, statement_index: j_stmt };
-                self.flow_state.operator.before_statement_effect(sets, location);
-                self.flow_state.operator.statement_effect(sets, location);
-                if track_intrablock {
-                    sets.apply_local_effect();
-                }
+                self.flow_state.operator.before_statement_effect(trans, location);
+                self.flow_state.operator.statement_effect(trans, location);
             }
 
             if terminator.is_some() {
                 let location = Location { block: bb, statement_index: statements.len() };
-                self.flow_state.operator.before_terminator_effect(sets, location);
-                self.flow_state.operator.terminator_effect(sets, location);
-                if track_intrablock {
-                    sets.apply_local_effect();
-                }
+                self.flow_state.operator.before_terminator_effect(trans, location);
+                self.flow_state.operator.terminator_effect(trans, location);
             }
         }
+
+        // Initialize the flow state at entry to the start block.
+        let on_entry = self.flow_state.sets.entry_set_mut_for(mir::START_BLOCK.index());
+        self.flow_state.operator.start_block_effect(on_entry);
     }
 }
 
@@ -247,14 +232,12 @@
             WorkQueue::with_all(self.builder.body.basic_blocks().len());
         let body = self.builder.body;
         while let Some(bb) = dirty_queue.pop() {
+            let (on_entry, trans) = self.builder.flow_state.sets.get_mut(bb.index());
+            debug_assert!(in_out.words().len() == on_entry.words().len());
+            in_out.overwrite(on_entry);
+            trans.apply(in_out);
+
             let bb_data = &body[bb];
-            {
-                let sets = self.builder.flow_state.sets.for_block(bb.index());
-                debug_assert!(in_out.words().len() == sets.on_entry.words().len());
-                in_out.overwrite(sets.on_entry);
-                in_out.union(sets.gen_set);
-                in_out.subtract(sets.kill_set);
-            }
             self.builder.propagate_bits_into_graph_successors_of(
                 in_out, (bb, bb_data), &mut dirty_queue);
         }
@@ -366,33 +349,27 @@
                                                         result: &DataflowResults<'tcx, T>,
                                                         body: &Body<'tcx>)
     -> BitSet<T::Idx> {
-    let mut on_entry = result.sets().on_entry_set_for(loc.block.index()).to_owned();
-    let mut kill_set = on_entry.to_hybrid();
-    let mut gen_set = kill_set.clone();
+    let mut trans = GenKill::from_elem(HybridBitSet::new_empty(analysis.bits_per_block()));
 
-    {
-        let mut sets = BlockSets {
-            on_entry: &mut on_entry,
-            kill_set: &mut kill_set,
-            gen_set: &mut gen_set,
-        };
-
-        for stmt in 0..loc.statement_index {
-            let mut stmt_loc = loc;
-            stmt_loc.statement_index = stmt;
-            analysis.before_statement_effect(&mut sets, stmt_loc);
-            analysis.statement_effect(&mut sets, stmt_loc);
-        }
-
-        // Apply the pre-statement effect of the statement we're evaluating.
-        if loc.statement_index == body[loc.block].statements.len() {
-            analysis.before_terminator_effect(&mut sets, loc);
-        } else {
-            analysis.before_statement_effect(&mut sets, loc);
-        }
+    for stmt in 0..loc.statement_index {
+        let mut stmt_loc = loc;
+        stmt_loc.statement_index = stmt;
+        analysis.before_statement_effect(&mut trans, stmt_loc);
+        analysis.statement_effect(&mut trans, stmt_loc);
     }
 
-    gen_set.to_dense()
+    // Apply the pre-statement effect of the statement we're evaluating.
+    if loc.statement_index == body[loc.block].statements.len() {
+        analysis.before_terminator_effect(&mut trans, loc);
+    } else {
+        analysis.before_statement_effect(&mut trans, loc);
+    }
+
+    // Apply the transfer function for all preceding statements to the fixpoint
+    // at the start of the block.
+    let mut state = result.sets().entry_set_for(loc.block.index()).to_owned();
+    trans.apply(&mut state);
+    state
 }
 
 pub struct DataflowAnalysis<'a, 'tcx, O>
@@ -462,36 +439,8 @@
     }
 }
 
-#[derive(Debug)]
-pub struct AllSets<E: Idx> {
-    /// Analysis bitwidth for each block.
-    bits_per_block: usize,
-
-    /// For each block, bits valid on entry to the block.
-    on_entry_sets: Vec<BitSet<E>>,
-
-    /// For each block, bits generated by executing the statements +
-    /// terminator in the block -- with one caveat. In particular, for
-    /// *call terminators*, the effect of storing the destination is
-    /// not included, since that only takes effect on the **success**
-    /// edge (and not the unwind edge).
-    gen_sets: Vec<HybridBitSet<E>>,
-
-    /// For each block, bits killed by executing the statements +
-    /// terminator in the block -- with one caveat. In particular, for
-    /// *call terminators*, the effect of storing the destination is
-    /// not included, since that only takes effect on the **success**
-    /// edge (and not the unwind edge).
-    kill_sets: Vec<HybridBitSet<E>>,
-}
-
-/// Triple of sets associated with a given block.
-///
-/// Generally, one sets up `on_entry`, `gen_set`, and `kill_set` for
-/// each block individually, and then runs the dataflow analysis which
-/// iteratively modifies the various `on_entry` sets (but leaves the
-/// other two sets unchanged, since they represent the effect of the
-/// block, which should be invariant over the course of the analysis).
+/// A 2-tuple representing the "gen" and "kill" bitsets during
+/// dataflow analysis.
 ///
 /// It is best to ensure that the intersection of `gen_set` and
 /// `kill_set` is empty; otherwise the results of the dataflow will
@@ -499,21 +448,32 @@
 /// killed during the iteration. (This is such a good idea that the
 /// `fn gen` and `fn kill` methods that set their state enforce this
 /// for you.)
-#[derive(Debug)]
-pub struct BlockSets<'a, E: Idx> {
-    /// Dataflow state immediately before control flow enters the given block.
-    pub(crate) on_entry: &'a mut BitSet<E>,
-
-    /// Bits that are set to 1 by the time we exit the given block. Hybrid
-    /// because it usually contains only 0 or 1 elements.
-    pub(crate) gen_set: &'a mut HybridBitSet<E>,
-
-    /// Bits that are set to 0 by the time we exit the given block. Hybrid
-    /// because it usually contains only 0 or 1 elements.
-    pub(crate) kill_set: &'a mut HybridBitSet<E>,
+#[derive(Debug, Clone, Copy)]
+pub struct GenKill<T> {
+    pub(crate) gen_set: T,
+    pub(crate) kill_set: T,
 }
 
-impl<'a, E:Idx> BlockSets<'a, E> {
+type GenKillSet<T> = GenKill<HybridBitSet<T>>;
+
+impl<T> GenKill<T> {
+    /// Creates a new tuple where `gen_set == kill_set == elem`.
+    pub(crate) fn from_elem(elem: T) -> Self
+        where T: Clone
+    {
+        GenKill {
+            gen_set: elem.clone(),
+            kill_set: elem,
+        }
+    }
+}
+
+impl<E:Idx> GenKillSet<E> {
+    pub(crate) fn clear(&mut self) {
+        self.gen_set.clear();
+        self.kill_set.clear();
+    }
+
     fn gen(&mut self, e: E) {
         self.gen_set.insert(e);
         self.kill_set.remove(e);
@@ -541,73 +501,93 @@
         }
     }
 
-    fn apply_local_effect(&mut self) {
-        self.on_entry.union(self.gen_set);
-        self.on_entry.subtract(self.kill_set);
+    /// Computes `(set ∪ gen) - kill` and assigns the result to `set`.
+    pub(crate) fn apply(&self, set: &mut BitSet<E>) {
+        set.union(&self.gen_set);
+        set.subtract(&self.kill_set);
     }
 }
 
+#[derive(Debug)]
+pub struct AllSets<E: Idx> {
+    /// Analysis bitwidth for each block.
+    bits_per_block: usize,
+
+    /// For each block, bits valid on entry to the block.
+    on_entry: Vec<BitSet<E>>,
+
+    /// The transfer function of each block expressed as the set of bits
+    /// generated and killed by executing the statements + terminator in the
+    /// block -- with one caveat. In particular, for *call terminators*, the
+    /// effect of storing the destination is not included, since that only takes
+    /// effect on the **success** edge (and not the unwind edge).
+    trans: Vec<GenKillSet<E>>,
+}
+
 impl<E:Idx> AllSets<E> {
     pub fn bits_per_block(&self) -> usize { self.bits_per_block }
-    pub fn for_block(&mut self, block_idx: usize) -> BlockSets<'_, E> {
-        BlockSets {
-            on_entry: &mut self.on_entry_sets[block_idx],
-            gen_set: &mut self.gen_sets[block_idx],
-            kill_set: &mut self.kill_sets[block_idx],
-        }
+
+    pub fn get_mut(&mut self, block_idx: usize) -> (&mut BitSet<E>, &mut GenKillSet<E>) {
+        (&mut self.on_entry[block_idx], &mut self.trans[block_idx])
     }
 
-    pub fn on_entry_set_for(&self, block_idx: usize) -> &BitSet<E> {
-        &self.on_entry_sets[block_idx]
+    pub fn trans_for(&self, block_idx: usize) -> &GenKillSet<E> {
+        &self.trans[block_idx]
+    }
+    pub fn trans_mut_for(&mut self, block_idx: usize) -> &mut GenKillSet<E> {
+        &mut self.trans[block_idx]
+    }
+    pub fn entry_set_for(&self, block_idx: usize) -> &BitSet<E> {
+        &self.on_entry[block_idx]
+    }
+    pub fn entry_set_mut_for(&mut self, block_idx: usize) -> &mut BitSet<E> {
+        &mut self.on_entry[block_idx]
     }
     pub fn gen_set_for(&self, block_idx: usize) -> &HybridBitSet<E> {
-        &self.gen_sets[block_idx]
+        &self.trans_for(block_idx).gen_set
     }
     pub fn kill_set_for(&self, block_idx: usize) -> &HybridBitSet<E> {
-        &self.kill_sets[block_idx]
+        &self.trans_for(block_idx).kill_set
     }
 }
 
 /// Parameterization for the precise form of data flow that is used.
-/// `InitialFlow` handles initializing the bitvectors before any
-/// code is inspected by the analysis. Analyses that need more nuanced
-/// initialization (e.g., they need to consult the results of some other
-/// dataflow analysis to set up the initial bitvectors) should not
-/// implement this.
-pub trait InitialFlow {
-    /// Specifies the initial value for each bit in the `on_entry` set
-    fn bottom_value() -> bool;
+///
+/// `BottomValue` determines whether the initial entry set for each basic block is empty or full.
+/// This also determines the semantics of the lattice `join` operator used to merge dataflow
+/// results, since dataflow works by starting at the bottom and moving monotonically to a fixed
+/// point.
+///
+/// This means, for propagation across the graph, that you either want to start at all-zeroes and
+/// then use Union as your merge when propagating, or you start at all-ones and then use Intersect
+/// as your merge when propagating.
+pub trait BottomValue {
+    /// Specifies the initial value for each bit in the entry set for each basic block.
+    const BOTTOM_VALUE: bool;
+
+    /// Merges `in_set` into `inout_set`, returning `true` if `inout_set` changed.
+    #[inline]
+    fn join<T: Idx>(&self, inout_set: &mut BitSet<T>, in_set: &BitSet<T>) -> bool {
+        if Self::BOTTOM_VALUE == false {
+            inout_set.union(in_set)
+        } else {
+            inout_set.intersect(in_set)
+        }
+    }
 }
 
-pub trait BitDenotation<'tcx>: BitSetOperator {
+/// A specific flavor of dataflow analysis.
+///
+/// To run a dataflow analysis, one sets up an initial state for the
+/// `START_BLOCK` via `start_block_effect` and a transfer function (`trans`)
+/// for each block individually. The entry set for all other basic blocks is
+/// initialized to `Self::BOTTOM_VALUE`. The dataflow analysis then
+/// iteratively modifies the various entry sets (but leaves the the transfer
+/// function unchanged).
+pub trait BitDenotation<'tcx>: BottomValue {
     /// Specifies what index type is used to access the bitvector.
     type Idx: Idx;
 
-    /// Some analyses want to accumulate knowledge within a block when
-    /// analyzing its statements for building the gen/kill sets. Override
-    /// this method to return true in such cases.
-    ///
-    /// When this returns true, the statement-effect (re)construction
-    /// will clone the `on_entry` state and pass along a reference via
-    /// `sets.on_entry` to that local clone into `statement_effect` and
-    /// `terminator_effect`).
-    ///
-    /// When it's false, no local clone is constructed; instead a
-    /// reference directly into `on_entry` is passed along via
-    /// `sets.on_entry` instead, which represents the flow state at
-    /// the block's start, not necessarily the state immediately prior
-    /// to the statement/terminator under analysis.
-    ///
-    /// In either case, the passed reference is mutable, but this is a
-    /// wart from using the `BlockSets` type in the API; the intention
-    /// is that the `statement_effect` and `terminator_effect` methods
-    /// mutate only the gen/kill sets.
-    //
-    // FIXME: we should consider enforcing the intention described in
-    // the previous paragraph by passing the three sets in separate
-    // parameters to encode their distinct mutabilities.
-    fn accumulates_intrablock_state() -> bool { false }
-
     /// A name describing the dataflow analysis that this
     /// `BitDenotation` is supporting. The name should be something
     /// suitable for plugging in as part of a filename (i.e., avoid
@@ -640,7 +620,7 @@
     /// applied, in that order, before moving for the next
     /// statement.
     fn before_statement_effect(&self,
-                               _sets: &mut BlockSets<'_, Self::Idx>,
+                               _trans: &mut GenKillSet<Self::Idx>,
                                _location: Location) {}
 
     /// Mutates the block-sets (the flow sets for the given
@@ -654,7 +634,7 @@
     /// `bb_data` is the sequence of statements identified by `bb` in
     /// the MIR.
     fn statement_effect(&self,
-                        sets: &mut BlockSets<'_, Self::Idx>,
+                        trans: &mut GenKillSet<Self::Idx>,
                         location: Location);
 
     /// Similar to `terminator_effect`, except it applies
@@ -669,7 +649,7 @@
     /// applied, in that order, before moving for the next
     /// terminator.
     fn before_terminator_effect(&self,
-                                _sets: &mut BlockSets<'_, Self::Idx>,
+                                _trans: &mut GenKillSet<Self::Idx>,
                                 _location: Location) {}
 
     /// Mutates the block-sets (the flow sets for the given
@@ -683,7 +663,7 @@
     /// The effects applied here cannot depend on which branch the
     /// terminator took.
     fn terminator_effect(&self,
-                         sets: &mut BlockSets<'_, Self::Idx>,
+                         trans: &mut GenKillSet<Self::Idx>,
                          location: Location);
 
     /// Mutates the block-sets according to the (flow-dependent)
@@ -718,17 +698,16 @@
 {
     pub fn new(body: &'a Body<'tcx>,
                dead_unwinds: &'a BitSet<mir::BasicBlock>,
-               denotation: D) -> Self where D: InitialFlow {
+               denotation: D) -> Self {
         let bits_per_block = denotation.bits_per_block();
         let num_blocks = body.basic_blocks().len();
 
-        let on_entry_sets = if D::bottom_value() {
+        let on_entry = if D::BOTTOM_VALUE == true {
             vec![BitSet::new_filled(bits_per_block); num_blocks]
         } else {
             vec![BitSet::new_empty(bits_per_block); num_blocks]
         };
-        let gen_sets = vec![HybridBitSet::new_empty(bits_per_block); num_blocks];
-        let kill_sets = gen_sets.clone();
+        let nop = GenKill::from_elem(HybridBitSet::new_empty(bits_per_block));
 
         DataflowAnalysis {
             body,
@@ -736,9 +715,8 @@
             flow_state: DataflowState {
                 sets: AllSets {
                     bits_per_block,
-                    on_entry_sets,
-                    gen_sets,
-                    kill_sets,
+                    on_entry,
+                    trans: vec![nop; num_blocks],
                 },
                 operator: denotation,
             }
@@ -836,7 +814,7 @@
                                          in_out: &BitSet<D::Idx>,
                                          bb: mir::BasicBlock,
                                          dirty_queue: &mut WorkQueue<mir::BasicBlock>) {
-        let entry_set = &mut self.flow_state.sets.for_block(bb.index()).on_entry;
+        let entry_set = self.flow_state.sets.entry_set_mut_for(bb.index());
         let set_changed = self.flow_state.operator.join(entry_set, &in_out);
         if set_changed {
             dirty_queue.insert(bb);
diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs
index 4afa4a0..c6e762b 100644
--- a/src/librustc_mir/interpret/eval_context.rs
+++ b/src/librustc_mir/interpret/eval_context.rs
@@ -319,7 +319,7 @@
         t: T,
     ) -> InterpResult<'tcx, T> {
         match self.stack.last() {
-            Some(frame) => Ok(self.monomorphize_with_substs(t, frame.instance.substs)),
+            Some(frame) => Ok(self.monomorphize_with_substs(t, frame.instance.substs)?),
             None => if t.needs_subst() {
                 err!(TooGeneric).into()
             } else {
@@ -332,11 +332,16 @@
         &self,
         t: T,
         substs: SubstsRef<'tcx>
-    ) -> T {
+    ) -> InterpResult<'tcx, T> {
         // miri doesn't care about lifetimes, and will choke on some crazy ones
         // let's simply get rid of them
         let substituted = t.subst(*self.tcx, substs);
-        self.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), substituted)
+
+        if substituted.needs_subst() {
+            return err!(TooGeneric);
+        }
+
+        Ok(self.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), substituted))
     }
 
     pub fn layout_of_local(
@@ -349,7 +354,7 @@
             None => {
                 let layout = crate::interpret::operand::from_known_layout(layout, || {
                     let local_ty = frame.body.local_decls[local].ty;
-                    let local_ty = self.monomorphize_with_substs(local_ty, frame.instance.substs);
+                    let local_ty = self.monomorphize_with_substs(local_ty, frame.instance.substs)?;
                     self.layout_of(local_ty)
                 })?;
                 // Layouts of locals are requested a lot, so we cache them.
@@ -437,7 +442,7 @@
                 Ok(Some((size.align_to(align), align)))
             }
             ty::Dynamic(..) => {
-                let vtable = metadata.expect("dyn trait fat ptr must have vtable").to_ptr()?;
+                let vtable = metadata.expect("dyn trait fat ptr must have vtable");
                 // the second entry in the vtable is the dynamic size of the object.
                 Ok(Some(self.read_size_and_align_from_vtable(vtable)?))
             }
diff --git a/src/librustc_mir/interpret/intern.rs b/src/librustc_mir/interpret/intern.rs
index 416b66d..f0d64e2 100644
--- a/src/librustc_mir/interpret/intern.rs
+++ b/src/librustc_mir/interpret/intern.rs
@@ -3,7 +3,6 @@
 //! After a const evaluation has computed a value, before we destroy the const evaluator's session
 //! memory, we need to extract all memory allocations to the global memory pool so they stay around.
 
-use rustc::ty::layout::LayoutOf;
 use rustc::ty::{Ty, TyCtxt, ParamEnv, self};
 use rustc::mir::interpret::{
     InterpResult, ErrorHandled,
@@ -143,18 +142,15 @@
         // Handle Reference types, as these are the only relocations supported by const eval.
         // Raw pointers (and boxes) are handled by the `leftover_relocations` logic.
         let ty = mplace.layout.ty;
-        if let ty::Ref(_, _, mutability) = ty.sty {
+        if let ty::Ref(_, referenced_ty, mutability) = ty.sty {
             let value = self.ecx.read_immediate(mplace.into())?;
             // Handle trait object vtables
             if let Ok(meta) = value.to_meta() {
-                let layout = self.ecx.layout_of(ty.builtin_deref(true).unwrap().ty)?;
-                if layout.is_unsized() {
-                    if let ty::Dynamic(..) = self.ecx.tcx.struct_tail(layout.ty).sty {
-                        if let Ok(vtable) = meta.unwrap().to_ptr() {
-                            // explitly choose `Immutable` here, since vtables are immutable, even
-                            // if the reference of the fat pointer is mutable
-                            self.intern_shallow(vtable, Mutability::Immutable)?;
-                        }
+                if let ty::Dynamic(..) = self.ecx.tcx.struct_tail(referenced_ty).sty {
+                    if let Ok(vtable) = meta.unwrap().to_ptr() {
+                        // explitly choose `Immutable` here, since vtables are immutable, even
+                        // if the reference of the fat pointer is mutable
+                        self.intern_shallow(vtable, Mutability::Immutable)?;
                     }
                 }
             }
@@ -178,8 +174,14 @@
                     (InternMode::Static, hir::Mutability::MutMutable) => {},
                     // we statically prevent `&mut T` via `const_qualif` and double check this here
                     (InternMode::ConstBase, hir::Mutability::MutMutable) |
-                    (InternMode::Const, hir::Mutability::MutMutable) =>
-                        bug!("const qualif failed to prevent mutable references"),
+                    (InternMode::Const, hir::Mutability::MutMutable) => {
+                        match referenced_ty.sty {
+                            ty::Array(_, n) if n.unwrap_usize(self.ecx.tcx.tcx) == 0 => {}
+                            ty::Slice(_)
+                                if value.to_meta().unwrap().unwrap().to_usize(self.ecx)? == 0 => {}
+                            _ => bug!("const qualif failed to prevent mutable references"),
+                        }
+                    },
                 }
                 // Compute the mutability with which we'll start visiting the allocation. This is
                 // what gets changed when we encounter an `UnsafeCell`
diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs
index de035ed..c3eec67 100644
--- a/src/librustc_mir/interpret/memory.rs
+++ b/src/librustc_mir/interpret/memory.rs
@@ -19,8 +19,7 @@
 use super::{
     Pointer, AllocId, Allocation, GlobalId, AllocationExtra,
     InterpResult, Scalar, InterpError, GlobalAlloc, PointerArithmetic,
-    Machine, AllocMap, MayLeak, ErrorHandled, CheckInAllocMsg, InboundsCheck,
-    InterpError::ValidationFailure,
+    Machine, AllocMap, MayLeak, ErrorHandled, CheckInAllocMsg,
 };
 
 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
@@ -44,6 +43,17 @@
     }
 }
 
+/// Used by `get_size_and_align` to indicate whether the allocation needs to be live.
+#[derive(Debug, Copy, Clone)]
+pub enum AllocCheck {
+    /// Allocation must be live and not a function pointer.
+    Dereferencable,
+    /// Allocations needs to be live, but may be a function pointer.
+    Live,
+    /// Allocation may be dead.
+    MaybeDead,
+}
+
 // `Memory` has to depend on the `Machine` because some of its operations
 // (e.g., `get`) call a `Machine` hook.
 pub struct Memory<'mir, 'tcx, M: Machine<'mir, 'tcx>> {
@@ -248,63 +258,93 @@
         Ok(())
     }
 
-    /// Checks that the pointer is aligned AND non-NULL. This supports ZSTs in two ways:
-    /// You can pass a scalar, and a `Pointer` does not have to actually still be allocated.
-    pub fn check_align(
+    /// Check if the given scalar is allowed to do a memory access of given `size`
+    /// and `align`. On success, returns `None` for zero-sized accesses (where
+    /// nothing else is left to do) and a `Pointer` to use for the actual access otherwise.
+    /// Crucially, if the input is a `Pointer`, we will test it for liveness
+    /// *even of* the size is 0.
+    ///
+    /// Everyone accessing memory based on a `Scalar` should use this method to get the
+    /// `Pointer` they need. And even if you already have a `Pointer`, call this method
+    /// to make sure it is sufficiently aligned and not dangling.  Not doing that may
+    /// cause ICEs.
+    pub fn check_ptr_access(
         &self,
-        ptr: Scalar<M::PointerTag>,
-        required_align: Align
-    ) -> InterpResult<'tcx> {
-        // Check non-NULL/Undef, extract offset
-        let (offset, alloc_align) = match ptr.to_bits_or_ptr(self.pointer_size(), self) {
-            Err(ptr) => {
-                // check this is not NULL -- which we can ensure only if this is in-bounds
-                // of some (potentially dead) allocation.
-                let align = self.check_bounds_ptr(ptr, InboundsCheck::MaybeDead,
-                                                  CheckInAllocMsg::NullPointerTest)?;
-                (ptr.offset.bytes(), align)
+        sptr: Scalar<M::PointerTag>,
+        size: Size,
+        align: Align,
+    ) -> InterpResult<'tcx, Option<Pointer<M::PointerTag>>> {
+        fn check_offset_align(offset: u64, align: Align) -> InterpResult<'static> {
+            if offset % align.bytes() == 0 {
+                Ok(())
+            } else {
+                // The biggest power of two through which `offset` is divisible.
+                let offset_pow2 = 1 << offset.trailing_zeros();
+                err!(AlignmentCheckFailed {
+                    has: Align::from_bytes(offset_pow2).unwrap(),
+                    required: align,
+                })
             }
-            Ok(data) => {
-                // check this is not NULL
-                if data == 0 {
+        }
+
+        // Normalize to a `Pointer` if we definitely need one.
+        let normalized = if size.bytes() == 0 {
+            // Can be an integer, just take what we got.  We do NOT `force_bits` here;
+            // if this is already a `Pointer` we want to do the bounds checks!
+            sptr
+        } else {
+            // A "real" access, we must get a pointer.
+            Scalar::Ptr(self.force_ptr(sptr)?)
+        };
+        Ok(match normalized.to_bits_or_ptr(self.pointer_size(), self) {
+            Ok(bits) => {
+                let bits = bits as u64; // it's ptr-sized
+                assert!(size.bytes() == 0);
+                // Must be non-NULL and aligned.
+                if bits == 0 {
                     return err!(InvalidNullPointerUsage);
                 }
-                // the "base address" is 0 and hence always aligned
-                (data as u64, required_align)
+                check_offset_align(bits, align)?;
+                None
             }
-        };
-        // Check alignment
-        if alloc_align.bytes() < required_align.bytes() {
-            return err!(AlignmentCheckFailed {
-                has: alloc_align,
-                required: required_align,
-            });
-        }
-        if offset % required_align.bytes() == 0 {
-            Ok(())
-        } else {
-            let has = offset % required_align.bytes();
-            err!(AlignmentCheckFailed {
-                has: Align::from_bytes(has).unwrap(),
-                required: required_align,
-            })
-        }
+            Err(ptr) => {
+                let (allocation_size, alloc_align) =
+                    self.get_size_and_align(ptr.alloc_id, AllocCheck::Dereferencable)?;
+                // Test bounds. This also ensures non-NULL.
+                // It is sufficient to check this for the end pointer. The addition
+                // checks for overflow.
+                let end_ptr = ptr.offset(size, self)?;
+                end_ptr.check_in_alloc(allocation_size, CheckInAllocMsg::MemoryAccessTest)?;
+                // Test align. Check this last; if both bounds and alignment are violated
+                // we want the error to be about the bounds.
+                if alloc_align.bytes() < align.bytes() {
+                    // The allocation itself is not aligned enough.
+                    // FIXME: Alignment check is too strict, depending on the base address that
+                    // got picked we might be aligned even if this check fails.
+                    // We instead have to fall back to converting to an integer and checking
+                    // the "real" alignment.
+                    return err!(AlignmentCheckFailed {
+                        has: alloc_align,
+                        required: align,
+                    });
+                }
+                check_offset_align(ptr.offset.bytes(), align)?;
+
+                // We can still be zero-sized in this branch, in which case we have to
+                // return `None`.
+                if size.bytes() == 0 { None } else { Some(ptr) }
+            }
+        })
     }
 
-    /// Checks if the pointer is "in-bounds". Notice that a pointer pointing at the end
-    /// of an allocation (i.e., at the first *inaccessible* location) *is* considered
-    /// in-bounds!  This follows C's/LLVM's rules.
-    /// If you want to check bounds before doing a memory access, better first obtain
-    /// an `Allocation` and call `check_bounds`.
-    pub fn check_bounds_ptr(
+    /// Test if the pointer might be NULL.
+    pub fn ptr_may_be_null(
         &self,
         ptr: Pointer<M::PointerTag>,
-        liveness: InboundsCheck,
-        msg: CheckInAllocMsg,
-    ) -> InterpResult<'tcx, Align> {
-        let (allocation_size, align) = self.get_size_and_align(ptr.alloc_id, liveness)?;
-        ptr.check_in_alloc(allocation_size, msg)?;
-        Ok(align)
+    ) -> bool {
+        let (size, _align) = self.get_size_and_align(ptr.alloc_id, AllocCheck::MaybeDead)
+            .expect("alloc info with MaybeDead cannot fail");
+        ptr.check_in_alloc(size, CheckInAllocMsg::NullPointerTest).is_err()
     }
 }
 
@@ -443,13 +483,14 @@
         }
     }
 
-    /// Obtain the size and alignment of an allocation, even if that allocation has been deallocated
+    /// Obtain the size and alignment of an allocation, even if that allocation has
+    /// been deallocated.
     ///
-    /// If `liveness` is `InboundsCheck::MaybeDead`, this function always returns `Ok`
+    /// If `liveness` is `AllocCheck::MaybeDead`, this function always returns `Ok`.
     pub fn get_size_and_align(
         &self,
         id: AllocId,
-        liveness: InboundsCheck,
+        liveness: AllocCheck,
     ) -> InterpResult<'static, (Size, Align)> {
         if let Ok(alloc) = self.get(id) {
             return Ok((Size::from_bytes(alloc.bytes.len() as u64), alloc.align));
@@ -459,7 +500,14 @@
         let alloc = self.tcx.alloc_map.lock().get(id);
         // Could also be a fn ptr or extern static
         match alloc {
-            Some(GlobalAlloc::Function(..)) => Ok((Size::ZERO, Align::from_bytes(1).unwrap())),
+            Some(GlobalAlloc::Function(..)) => {
+                if let AllocCheck::Dereferencable = liveness {
+                    // The caller requested no function pointers.
+                    err!(DerefFunctionPointer)
+                } else {
+                    Ok((Size::ZERO, Align::from_bytes(1).unwrap()))
+                }
+            }
             // `self.get` would also work, but can cause cycles if a static refers to itself
             Some(GlobalAlloc::Static(did)) => {
                 // The only way `get` couldn't have worked here is if this is an extern static
@@ -471,17 +519,15 @@
             }
             _ => {
                 if let Ok(alloc) = self.get(id) {
-                    return Ok((Size::from_bytes(alloc.bytes.len() as u64), alloc.align));
+                    Ok((Size::from_bytes(alloc.bytes.len() as u64), alloc.align))
                 }
-                match liveness {
-                    InboundsCheck::MaybeDead => {
-                        // Must be a deallocated pointer
-                        self.dead_alloc_map.get(&id).cloned().ok_or_else(||
-                            ValidationFailure("allocation missing in dead_alloc_map".to_string())
-                                .into()
-                        )
-                    },
-                    InboundsCheck::Live => err!(DanglingPointerDeref),
+                else if let AllocCheck::MaybeDead = liveness {
+                    // Deallocated pointers are allowed, we should be able to find
+                    // them in the map.
+                    Ok(*self.dead_alloc_map.get(&id)
+                        .expect("deallocated pointers should all be recorded in `dead_alloc_map`"))
+                } else {
+                    err!(DanglingPointerDeref)
                 }
             },
         }
@@ -629,24 +675,22 @@
     }
 }
 
-/// Byte Accessors
+/// Reading and writing.
 impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
+    /// Performs appropriate bounds checks.
     pub fn read_bytes(
         &self,
         ptr: Scalar<M::PointerTag>,
         size: Size,
     ) -> InterpResult<'tcx, &[u8]> {
-        if size.bytes() == 0 {
-            Ok(&[])
-        } else {
-            let ptr = self.force_ptr(ptr)?;
-            self.get(ptr.alloc_id)?.get_bytes(self, ptr, size)
-        }
+        let ptr = match self.check_ptr_access(ptr, size, Align::from_bytes(1).unwrap())? {
+            Some(ptr) => ptr,
+            None => return Ok(&[]), // zero-sized access
+        };
+        self.get(ptr.alloc_id)?.get_bytes(self, ptr, size)
     }
-}
 
-/// Reading and writing.
-impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
+    /// Performs appropriate bounds checks.
     pub fn copy(
         &mut self,
         src: Scalar<M::PointerTag>,
@@ -659,6 +703,7 @@
         self.copy_repeatedly(src, src_align, dest, dest_align, size, 1, nonoverlapping)
     }
 
+    /// Performs appropriate bounds checks.
     pub fn copy_repeatedly(
         &mut self,
         src: Scalar<M::PointerTag>,
@@ -669,15 +714,14 @@
         length: u64,
         nonoverlapping: bool,
     ) -> InterpResult<'tcx> {
-        self.check_align(src, src_align)?;
-        self.check_align(dest, dest_align)?;
-        if size.bytes() == 0 {
-            // Nothing to do for ZST, other than checking alignment and
-            // non-NULLness which already happened.
-            return Ok(());
-        }
-        let src = self.force_ptr(src)?;
-        let dest = self.force_ptr(dest)?;
+        // We need to check *both* before early-aborting due to the size being 0.
+        let (src, dest) = match (self.check_ptr_access(src, size, src_align)?,
+                self.check_ptr_access(dest, size * length, dest_align)?)
+        {
+            (Some(src), Some(dest)) => (src, dest),
+            // One of the two sizes is 0.
+            _ => return Ok(()),
+        };
 
         // first copy the relocations to a temporary buffer, because
         // `get_bytes_mut` will clear the relocations, which is correct,
diff --git a/src/librustc_mir/interpret/mod.rs b/src/librustc_mir/interpret/mod.rs
index 0293a83..259bd6a 100644
--- a/src/librustc_mir/interpret/mod.rs
+++ b/src/librustc_mir/interpret/mod.rs
@@ -24,7 +24,7 @@
 
 pub use self::place::{Place, PlaceTy, MemPlace, MPlaceTy};
 
-pub use self::memory::{Memory, MemoryKind};
+pub use self::memory::{Memory, MemoryKind, AllocCheck};
 
 pub use self::machine::{Machine, AllocMap, MayLeak};
 
diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs
index ec391b7..c72078f 100644
--- a/src/librustc_mir/interpret/operand.rs
+++ b/src/librustc_mir/interpret/operand.rs
@@ -9,9 +9,9 @@
 };
 
 use rustc::mir::interpret::{
-    GlobalId, AllocId, CheckInAllocMsg,
+    GlobalId, AllocId,
     ConstValue, Pointer, Scalar,
-    InterpResult, InterpError, InboundsCheck,
+    InterpResult, InterpError,
     sign_extend, truncate,
 };
 use super::{
@@ -226,19 +226,14 @@
         }
         let (ptr, ptr_align) = mplace.to_scalar_ptr_align();
 
-        if mplace.layout.is_zst() {
-            // Not all ZSTs have a layout we would handle below, so just short-circuit them
-            // all here.
-            self.memory.check_align(ptr, ptr_align)?;
-            return Ok(Some(ImmTy {
+        let ptr = match self.memory.check_ptr_access(ptr, mplace.layout.size, ptr_align)? {
+            Some(ptr) => ptr,
+            None => return Ok(Some(ImmTy { // zero-sized type
                 imm: Immediate::Scalar(Scalar::zst().into()),
                 layout: mplace.layout,
-            }));
-        }
+            })),
+        };
 
-        // check for integer pointers before alignment to report better errors
-        let ptr = self.force_ptr(ptr)?;
-        self.memory.check_align(ptr.into(), ptr_align)?;
         match mplace.layout.abi {
             layout::Abi::Scalar(..) => {
                 let scalar = self.memory
@@ -250,17 +245,18 @@
                 }))
             }
             layout::Abi::ScalarPair(ref a, ref b) => {
+                // We checked `ptr_align` above, so all fields will have the alignment they need.
+                // We would anyway check against `ptr_align.restrict_for_offset(b_offset)`,
+                // which `ptr.offset(b_offset)` cannot possibly fail to satisfy.
                 let (a, b) = (&a.value, &b.value);
                 let (a_size, b_size) = (a.size(self), b.size(self));
                 let a_ptr = ptr;
                 let b_offset = a_size.align_to(b.align(self).abi);
-                assert!(b_offset.bytes() > 0); // we later use the offset to test which field to use
+                assert!(b_offset.bytes() > 0); // we later use the offset to tell apart the fields
                 let b_ptr = ptr.offset(b_offset, self)?;
                 let a_val = self.memory
                     .get(ptr.alloc_id)?
                     .read_scalar(self, a_ptr, a_size)?;
-                let b_align = ptr_align.restrict_for_offset(b_offset);
-                self.memory.check_align(b_ptr.into(), b_align)?;
                 let b_val = self.memory
                     .get(ptr.alloc_id)?
                     .read_scalar(self, b_ptr, b_size)?;
@@ -639,8 +635,7 @@
                     Err(ptr) => {
                         // The niche must be just 0 (which an inbounds pointer value never is)
                         let ptr_valid = niche_start == 0 && variants_start == variants_end &&
-                            self.memory.check_bounds_ptr(ptr, InboundsCheck::MaybeDead,
-                                                         CheckInAllocMsg::NullPointerTest).is_ok();
+                            !self.memory.ptr_may_be_null(ptr);
                         if !ptr_valid {
                             return err!(InvalidDiscriminant(raw_discr.erase_tag().into()));
                         }
diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs
index 1285549..1351b5b 100644
--- a/src/librustc_mir/interpret/place.rs
+++ b/src/librustc_mir/interpret/place.rs
@@ -222,9 +222,9 @@
     }
 
     #[inline]
-    pub(super) fn vtable(self) -> InterpResult<'tcx, Pointer<Tag>> {
+    pub(super) fn vtable(self) -> Scalar<Tag> {
         match self.layout.ty.sty {
-            ty::Dynamic(..) => self.mplace.meta.unwrap().to_ptr(),
+            ty::Dynamic(..) => self.mplace.meta.unwrap(),
             _ => bug!("vtable not supported on type {:?}", self.layout.ty),
         }
     }
@@ -746,15 +746,13 @@
         // type things are read at. In case `src_val` is a `ScalarPair`, we don't do any magic here
         // to handle padding properly, which is only correct if we never look at this data with the
         // wrong type.
+        assert!(!dest.layout.is_unsized());
 
-        // Nothing to do for ZSTs, other than checking alignment
-        if dest.layout.is_zst() {
-            return self.memory.check_align(ptr, ptr_align);
-        }
+        let ptr = match self.memory.check_ptr_access(ptr, dest.layout.size, ptr_align)? {
+            Some(ptr) => ptr,
+            None => return Ok(()), // zero-sized access
+        };
 
-        // check for integer pointers before alignment to report better errors
-        let ptr = self.force_ptr(ptr)?;
-        self.memory.check_align(ptr.into(), ptr_align)?;
         let tcx = &*self.tcx;
         // FIXME: We should check that there are dest.layout.size many bytes available in
         // memory.  The code below is not sufficient, with enough padding it might not
@@ -771,6 +769,9 @@
                 )
             }
             Immediate::ScalarPair(a_val, b_val) => {
+                // We checked `ptr_align` above, so all fields will have the alignment they need.
+                // We would anyway check against `ptr_align.restrict_for_offset(b_offset)`,
+                // which `ptr.offset(b_offset)` cannot possibly fail to satisfy.
                 let (a, b) = match dest.layout.abi {
                     layout::Abi::ScalarPair(ref a, ref b) => (&a.value, &b.value),
                     _ => bug!("write_immediate_to_mplace: invalid ScalarPair layout: {:#?}",
@@ -778,11 +779,8 @@
                 };
                 let (a_size, b_size) = (a.size(self), b.size(self));
                 let b_offset = a_size.align_to(b.align(self).abi);
-                let b_align = ptr_align.restrict_for_offset(b_offset);
                 let b_ptr = ptr.offset(b_offset, self)?;
 
-                self.memory.check_align(b_ptr.into(), b_align)?;
-
                 // It is tempting to verify `b_offset` against `layout.fields.offset(1)`,
                 // but that does not work: We could be a newtype around a pair, then the
                 // fields do not match the `ScalarPair` components.
@@ -1053,7 +1051,7 @@
     /// Also return some more information so drop doesn't have to run the same code twice.
     pub(super) fn unpack_dyn_trait(&self, mplace: MPlaceTy<'tcx, M::PointerTag>)
     -> InterpResult<'tcx, (ty::Instance<'tcx>, MPlaceTy<'tcx, M::PointerTag>)> {
-        let vtable = mplace.vtable()?; // also sanity checks the type
+        let vtable = mplace.vtable(); // also sanity checks the type
         let (instance, ty) = self.read_drop_type_from_vtable(vtable)?;
         let layout = self.layout_of(ty)?;
 
diff --git a/src/librustc_mir/interpret/terminator.rs b/src/librustc_mir/interpret/terminator.rs
index 190f781..a29611e 100644
--- a/src/librustc_mir/interpret/terminator.rs
+++ b/src/librustc_mir/interpret/terminator.rs
@@ -388,12 +388,10 @@
                             ));
                         }
                     } else {
-                        let callee_layout =
-                            self.layout_of_local(self.frame(), mir::RETURN_PLACE, None)?;
-                        if !callee_layout.abi.is_uninhabited() {
-                            return err!(FunctionRetMismatch(
-                                self.tcx.types.never, callee_layout.ty
-                            ));
+                        let local = mir::RETURN_PLACE;
+                        let ty = self.frame().body.local_decls[local].ty;
+                        if !self.tcx.is_ty_uninhabited_from_any_module(ty) {
+                            return err!(FunctionRetMismatch(self.tcx.types.never, ty));
                         }
                     }
                     Ok(())
@@ -425,12 +423,15 @@
                     }
                 };
                 // Find and consult vtable
-                let vtable = receiver_place.vtable()?;
-                self.memory.check_align(vtable.into(), self.tcx.data_layout.pointer_align.abi)?;
-                let fn_ptr = self.memory.get(vtable.alloc_id)?.read_ptr_sized(
-                    self,
-                    vtable.offset(ptr_size * (idx as u64 + 3), self)?,
-                )?.to_ptr()?;
+                let vtable = receiver_place.vtable();
+                let vtable_slot = vtable.ptr_offset(ptr_size * (idx as u64 + 3), self)?;
+                let vtable_slot = self.memory.check_ptr_access(
+                    vtable_slot,
+                    ptr_size,
+                    self.tcx.data_layout.pointer_align.abi,
+                )?.expect("cannot be a ZST");
+                let fn_ptr = self.memory.get(vtable_slot.alloc_id)?
+                    .read_ptr_sized(self, vtable_slot)?.to_ptr()?;
                 let instance = self.memory.get_fn(fn_ptr)?;
 
                 // `*mut receiver_place.layout.ty` is almost the layout that we
diff --git a/src/librustc_mir/interpret/traits.rs b/src/librustc_mir/interpret/traits.rs
index 4ae0ee5..5d2f268 100644
--- a/src/librustc_mir/interpret/traits.rs
+++ b/src/librustc_mir/interpret/traits.rs
@@ -101,10 +101,14 @@
     /// Returns the drop fn instance as well as the actual dynamic type
     pub fn read_drop_type_from_vtable(
         &self,
-        vtable: Pointer<M::PointerTag>,
+        vtable: Scalar<M::PointerTag>,
     ) -> InterpResult<'tcx, (ty::Instance<'tcx>, Ty<'tcx>)> {
         // we don't care about the pointee type, we just want a pointer
-        self.memory.check_align(vtable.into(), self.tcx.data_layout.pointer_align.abi)?;
+        let vtable = self.memory.check_ptr_access(
+            vtable,
+            self.tcx.data_layout.pointer_size,
+            self.tcx.data_layout.pointer_align.abi,
+        )?.expect("cannot be a ZST");
         let drop_fn = self.memory
             .get(vtable.alloc_id)?
             .read_ptr_sized(self, vtable)?
@@ -113,20 +117,28 @@
         trace!("Found drop fn: {:?}", drop_instance);
         let fn_sig = drop_instance.ty(*self.tcx).fn_sig(*self.tcx);
         let fn_sig = self.tcx.normalize_erasing_late_bound_regions(self.param_env, &fn_sig);
-        // the drop function takes *mut T where T is the type being dropped, so get that
+        // The drop function takes `*mut T` where `T` is the type being dropped, so get that.
         let ty = fn_sig.inputs()[0].builtin_deref(true).unwrap().ty;
         Ok((drop_instance, ty))
     }
 
     pub fn read_size_and_align_from_vtable(
         &self,
-        vtable: Pointer<M::PointerTag>,
+        vtable: Scalar<M::PointerTag>,
     ) -> InterpResult<'tcx, (Size, Align)> {
         let pointer_size = self.pointer_size();
-        self.memory.check_align(vtable.into(), self.tcx.data_layout.pointer_align.abi)?;
+        // We check for size = 3*ptr_size, that covers the drop fn (unused here),
+        // the size, and the align (which we read below).
+        let vtable = self.memory.check_ptr_access(
+            vtable,
+            3*pointer_size,
+            self.tcx.data_layout.pointer_align.abi,
+        )?.expect("cannot be a ZST");
         let alloc = self.memory.get(vtable.alloc_id)?;
-        let size = alloc.read_ptr_sized(self, vtable.offset(pointer_size, self)?)?
-            .to_bits(pointer_size)? as u64;
+        let size = alloc.read_ptr_sized(
+            self,
+            vtable.offset(pointer_size, self)?
+        )?.to_bits(pointer_size)? as u64;
         let align = alloc.read_ptr_sized(
             self,
             vtable.offset(pointer_size * 2, self)?,
diff --git a/src/librustc_mir/interpret/validity.rs b/src/librustc_mir/interpret/validity.rs
index e42c667..b2a159f 100644
--- a/src/librustc_mir/interpret/validity.rs
+++ b/src/librustc_mir/interpret/validity.rs
@@ -3,11 +3,11 @@
 
 use syntax_pos::symbol::{sym, Symbol};
 use rustc::hir;
-use rustc::ty::layout::{self, Size, Align, TyLayout, LayoutOf, VariantIdx};
+use rustc::ty::layout::{self, TyLayout, LayoutOf, VariantIdx};
 use rustc::ty;
 use rustc_data_structures::fx::FxHashSet;
 use rustc::mir::interpret::{
-    Scalar, GlobalAlloc, InterpResult, InterpError, CheckInAllocMsg,
+    GlobalAlloc, InterpResult, InterpError,
 };
 
 use std::hash::Hash;
@@ -365,8 +365,16 @@
                     let tail = self.ecx.tcx.struct_tail(layout.ty);
                     match tail.sty {
                         ty::Dynamic(..) => {
-                            let vtable = try_validation!(meta.unwrap().to_ptr(),
-                                "non-pointer vtable in fat pointer", self.path);
+                            let vtable = meta.unwrap();
+                            try_validation!(
+                                self.ecx.memory.check_ptr_access(
+                                    vtable,
+                                    3*self.ecx.tcx.data_layout.pointer_size, // drop, size, align
+                                    self.ecx.tcx.data_layout.pointer_align.abi,
+                                ),
+                                "dangling or unaligned vtable pointer or too small vtable",
+                                self.path
+                            );
                             try_validation!(self.ecx.read_drop_type_from_vtable(vtable),
                                 "invalid drop fn in vtable", self.path);
                             try_validation!(self.ecx.read_size_and_align_from_vtable(vtable),
@@ -384,16 +392,19 @@
                             bug!("Unexpected unsized type tail: {:?}", tail),
                     }
                 }
-                // Make sure this is non-NULL and aligned
+                // Make sure this is dereferencable and all.
                 let (size, align) = self.ecx.size_and_align_of(meta, layout)?
                     // for the purpose of validity, consider foreign types to have
                     // alignment and size determined by the layout (size will be 0,
                     // alignment should take attributes into account).
                     .unwrap_or_else(|| (layout.size, layout.align.abi));
-                match self.ecx.memory.check_align(ptr, align) {
-                    Ok(_) => {},
+                let ptr: Option<_> = match self.ecx.memory.check_ptr_access(ptr, size, align) {
+                    Ok(ptr) => ptr,
                     Err(err) => {
-                        info!("{:?} is not aligned to {:?}", ptr, align);
+                        info!(
+                            "{:?} did not pass access check for size {:?}, align {:?}",
+                            ptr, size, align
+                        );
                         match err.kind {
                             InterpError::InvalidNullPointerUsage =>
                                 return validation_failure!("NULL reference", self.path),
@@ -401,23 +412,23 @@
                                 return validation_failure!(format!("unaligned reference \
                                     (required {} byte alignment but found {})",
                                     required.bytes(), has.bytes()), self.path),
+                            InterpError::ReadBytesAsPointer =>
+                                return validation_failure!(
+                                    "integer pointer in non-ZST reference",
+                                    self.path
+                                ),
                             _ =>
                                 return validation_failure!(
-                                    "dangling (out-of-bounds) reference (might be NULL at \
-                                        run-time)",
+                                    "dangling (not entirely in bounds) reference",
                                     self.path
                                 ),
                         }
                     }
-                }
+                };
                 // Recursive checking
                 if let Some(ref mut ref_tracking) = self.ref_tracking_for_consts {
                     let place = self.ecx.ref_to_mplace(value)?;
-                    // FIXME(RalfJ): check ZST for inbound pointers
-                    if size != Size::ZERO {
-                        // Non-ZST also have to be dereferencable
-                        let ptr = try_validation!(place.ptr.to_ptr(),
-                            "integer pointer in non-ZST reference", self.path);
+                    if let Some(ptr) = ptr { // not a ZST
                         // Skip validation entirely for some external statics
                         let alloc_kind = self.ecx.tcx.alloc_map.lock().get(ptr.alloc_id);
                         if let Some(GlobalAlloc::Static(did)) = alloc_kind {
@@ -429,18 +440,10 @@
                                 return Ok(());
                             }
                         }
-                        // Maintain the invariant that the place we are checking is
-                        // already verified to be in-bounds.
-                        try_validation!(
-                            self.ecx.memory
-                                .get(ptr.alloc_id)?
-                                .check_bounds(self.ecx, ptr, size, CheckInAllocMsg::InboundsTest),
-                            "dangling (not entirely in bounds) reference", self.path);
                     }
                     // Check if we have encountered this pointer+layout combination
-                    // before.  Proceed recursively even for integer pointers, no
-                    // reason to skip them! They are (recursively) valid for some ZST,
-                    // but not for others (e.g., `!` is a ZST).
+                    // before.  Proceed recursively even for ZST, no
+                    // reason to skip them! E.g., `!` is a ZST and we want to validate it.
                     let path = &self.path;
                     ref_tracking.track(place, || {
                         // We need to clone the path anyway, make sure it gets created
@@ -499,14 +502,8 @@
         let bits = match value.to_bits_or_ptr(op.layout.size, self.ecx) {
             Err(ptr) => {
                 if lo == 1 && hi == max_hi {
-                    // only NULL is not allowed.
-                    // We can call `check_align` to check non-NULL-ness, but have to also look
-                    // for function pointers.
-                    let non_null =
-                        self.ecx.memory.check_align(
-                            Scalar::Ptr(ptr), Align::from_bytes(1).unwrap()
-                        ).is_ok();
-                    if !non_null {
+                    // Only NULL is the niche.  So make sure the ptr is NOT NULL.
+                    if self.ecx.memory.ptr_may_be_null(ptr) {
                         // These conditions are just here to improve the diagnostics so we can
                         // differentiate between null pointers and dangling pointers
                         if self.ref_tracking_for_consts.is_some() &&
diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs
index 9092cf2..c7a2fdd 100644
--- a/src/librustc_mir/transform/const_prop.rs
+++ b/src/librustc_mir/transform/const_prop.rs
@@ -1,6 +1,8 @@
 //! Propagates constants for early reporting of statically known
 //! assertion failures
 
+use std::cell::Cell;
+
 use rustc::hir::def::DefKind;
 use rustc::mir::{
     AggregateKind, Constant, Location, Place, PlaceBase, Body, Operand, Rvalue,
@@ -21,7 +23,8 @@
 };
 
 use crate::interpret::{
-    self, InterpretCx, ScalarMaybeUndef, Immediate, OpTy, ImmTy, MemoryKind,
+    self, InterpretCx, ScalarMaybeUndef, Immediate, OpTy,
+    ImmTy, MemoryKind, StackPopCleanup, LocalValue, LocalState,
 };
 use crate::const_eval::{
     CompileTimeInterpreter, error_to_const_error, eval_promoted, mk_eval_cx,
@@ -56,21 +59,54 @@
 
         trace!("ConstProp starting for {:?}", source.def_id());
 
+        // Steal some data we need from `body`.
+        let source_scope_local_data = std::mem::replace(
+            &mut body.source_scope_local_data,
+            ClearCrossCrate::Clear
+        );
+        let promoted = std::mem::replace(
+            &mut body.promoted,
+            IndexVec::new()
+        );
+
+        let dummy_body =
+            &Body::new(
+                body.basic_blocks().clone(),
+                Default::default(),
+                ClearCrossCrate::Clear,
+                Default::default(),
+                None,
+                body.local_decls.clone(),
+                Default::default(),
+                body.arg_count,
+                Default::default(),
+                tcx.def_span(source.def_id()),
+                Default::default(),
+            );
+
         // FIXME(oli-obk, eddyb) Optimize locals (or even local paths) to hold
         // constants, instead of just checking for const-folding succeeding.
         // That would require an uniform one-def no-mutation analysis
         // and RPO (or recursing when needing the value of a local).
-        let mut optimization_finder = ConstPropagator::new(body, tcx, source);
+        let mut optimization_finder = ConstPropagator::new(
+            body,
+            dummy_body,
+            source_scope_local_data,
+            promoted,
+            tcx,
+            source
+        );
         optimization_finder.visit_body(body);
 
         // put back the data we stole from `mir`
+        let (source_scope_local_data, promoted) = optimization_finder.release_stolen_data();
         std::mem::replace(
             &mut body.source_scope_local_data,
-            optimization_finder.source_scope_local_data
+            source_scope_local_data
         );
         std::mem::replace(
             &mut body.promoted,
-            optimization_finder.promoted
+            promoted
         );
 
         trace!("ConstProp done for {:?}", source.def_id());
@@ -84,7 +120,6 @@
     ecx: InterpretCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>,
     tcx: TyCtxt<'tcx>,
     source: MirSource<'tcx>,
-    places: IndexVec<Local, Option<Const<'tcx>>>,
     can_const_prop: IndexVec<Local, bool>,
     param_env: ParamEnv<'tcx>,
     source_scope_local_data: ClearCrossCrate<IndexVec<SourceScope, SourceScopeLocalData>>,
@@ -117,21 +152,28 @@
 
 impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
     fn new(
-        body: &mut Body<'tcx>,
+        body: &Body<'tcx>,
+        dummy_body: &'mir Body<'tcx>,
+        source_scope_local_data: ClearCrossCrate<IndexVec<SourceScope, SourceScopeLocalData>>,
+        promoted: IndexVec<Promoted, Body<'tcx>>,
         tcx: TyCtxt<'tcx>,
         source: MirSource<'tcx>,
     ) -> ConstPropagator<'mir, 'tcx> {
-        let param_env = tcx.param_env(source.def_id());
-        let ecx = mk_eval_cx(tcx, tcx.def_span(source.def_id()), param_env);
+        let def_id = source.def_id();
+        let param_env = tcx.param_env(def_id);
+        let span = tcx.def_span(def_id);
+        let mut ecx = mk_eval_cx(tcx, span, param_env);
         let can_const_prop = CanConstProp::check(body);
-        let source_scope_local_data = std::mem::replace(
-            &mut body.source_scope_local_data,
-            ClearCrossCrate::Clear
-        );
-        let promoted = std::mem::replace(
-            &mut body.promoted,
-            IndexVec::new()
-        );
+
+        ecx.push_stack_frame(
+            Instance::new(def_id, &InternalSubsts::identity_for_item(tcx, def_id)),
+            span,
+            dummy_body,
+            None,
+            StackPopCleanup::None {
+                cleanup: false,
+            },
+        ).expect("failed to push initial stack frame");
 
         ConstPropagator {
             ecx,
@@ -139,7 +181,6 @@
             source,
             param_env,
             can_const_prop,
-            places: IndexVec::from_elem(None, &body.local_decls),
             source_scope_local_data,
             //FIXME(wesleywiser) we can't steal this because `Visitor::super_visit_body()` needs it
             local_decls: body.local_decls.clone(),
@@ -147,6 +188,51 @@
         }
     }
 
+    fn release_stolen_data(
+        self,
+    ) -> (
+        ClearCrossCrate<IndexVec<SourceScope, SourceScopeLocalData>>,
+        IndexVec<Promoted, Body<'tcx>>,
+    ) {
+        (self.source_scope_local_data, self.promoted)
+    }
+
+    fn get_const(&self, local: Local) -> Option<Const<'tcx>> {
+        let l = &self.ecx.frame().locals[local];
+
+        // If the local is `Unitialized` or `Dead` then we haven't propagated a value into it.
+        //
+        // `InterpretCx::access_local()` mostly takes care of this for us however, for ZSTs,
+        // it will synthesize a value for us. In doing so, that will cause the
+        // `get_const(l).is_empty()` assert right before we call `set_const()` in `visit_statement`
+        // to fail.
+        if let LocalValue::Uninitialized | LocalValue::Dead = l.value {
+            return None;
+        }
+
+        self.ecx.access_local(self.ecx.frame(), local, None).ok()
+    }
+
+    fn set_const(&mut self, local: Local, c: Const<'tcx>) {
+        let frame = self.ecx.frame_mut();
+
+        if let Some(layout) = frame.locals[local].layout.get() {
+            debug_assert_eq!(c.layout, layout);
+        }
+
+        frame.locals[local] = LocalState {
+            value: LocalValue::Live(*c),
+            layout: Cell::new(Some(c.layout)),
+        };
+    }
+
+    fn remove_const(&mut self, local: Local) {
+        self.ecx.frame_mut().locals[local] = LocalState {
+            value: LocalValue::Uninitialized,
+            layout: Cell::new(None),
+        };
+    }
+
     fn use_ecx<F, T>(
         &mut self,
         source_info: SourceInfo,
@@ -296,7 +382,7 @@
         trace!("eval_place(place={:?})", place);
         place.iterate(|place_base, place_projection| {
             let mut eval = match place_base {
-                PlaceBase::Local(loc) => self.places[*loc].clone()?,
+                PlaceBase::Local(loc) => self.get_const(*loc).clone()?,
                 PlaceBase::Static(box Static {kind: StaticKind::Promoted(promoted), ..}) => {
                     let generics = self.tcx.generics_of(self.source.def_id());
                     if generics.requires_monomorphization(self.tcx) {
@@ -699,8 +785,8 @@
                         trace!("checking whether {:?} can be stored to {:?}", value, local);
                         if self.can_const_prop[local] {
                             trace!("storing {:?} to {:?}", value, local);
-                            assert!(self.places[local].is_none());
-                            self.places[local] = Some(value);
+                            assert!(self.get_const(local).is_none());
+                            self.set_const(local, value);
 
                             if self.should_const_prop() {
                                 self.replace_with_const(
@@ -740,7 +826,7 @@
                                     place = &proj.base;
                                 }
                                 if let Place::Base(PlaceBase::Local(local)) = *place {
-                                    self.places[local] = None;
+                                    self.remove_const(local);
                                 }
                             },
                             Operand::Constant(_) => {}
diff --git a/src/librustc_mir/transform/elaborate_drops.rs b/src/librustc_mir/transform/elaborate_drops.rs
index d805c66..b2b489b 100644
--- a/src/librustc_mir/transform/elaborate_drops.rs
+++ b/src/librustc_mir/transform/elaborate_drops.rs
@@ -95,7 +95,7 @@
         };
 
         let mut init_data = InitializationData {
-            live: flow_inits.sets().on_entry_set_for(bb.index()).to_owned(),
+            live: flow_inits.sets().entry_set_for(bb.index()).to_owned(),
             dead: BitSet::new_empty(env.move_data.move_paths.len()),
         };
         debug!("find_dead_unwinds @ {:?}: {:?}; init_data={:?}",
@@ -304,9 +304,9 @@
 
     fn initialization_data_at(&self, loc: Location) -> InitializationData {
         let mut data = InitializationData {
-            live: self.flow_inits.sets().on_entry_set_for(loc.block.index())
+            live: self.flow_inits.sets().entry_set_for(loc.block.index())
                 .to_owned(),
-            dead: self.flow_uninits.sets().on_entry_set_for(loc.block.index())
+            dead: self.flow_uninits.sets().entry_set_for(loc.block.index())
                 .to_owned(),
         };
         for stmt in 0..loc.statement_index {
diff --git a/src/librustc_mir/transform/rustc_peek.rs b/src/librustc_mir/transform/rustc_peek.rs
index c4601229..f12309c 100644
--- a/src/librustc_mir/transform/rustc_peek.rs
+++ b/src/librustc_mir/transform/rustc_peek.rs
@@ -18,7 +18,6 @@
 };
 use crate::dataflow::move_paths::{MovePathIndex, LookupResult};
 use crate::dataflow::move_paths::{HasMoveData, MoveData};
-use crate::dataflow;
 
 use crate::dataflow::has_rustc_mir_with;
 
@@ -133,9 +132,8 @@
         }
     };
 
-    let mut on_entry = results.0.sets.on_entry_set_for(bb.index()).to_owned();
-    let mut gen_set = results.0.sets.gen_set_for(bb.index()).clone();
-    let mut kill_set = results.0.sets.kill_set_for(bb.index()).clone();
+    let mut on_entry = results.0.sets.entry_set_for(bb.index()).to_owned();
+    let mut trans = results.0.sets.trans_for(bb.index()).clone();
 
     // Emulate effect of all statements in the block up to (but not
     // including) the borrow within `peek_arg_place`. Do *not* include
@@ -143,10 +141,6 @@
     // of the argument at time immediate preceding Call to
     // `rustc_peek`).
 
-    let mut sets = dataflow::BlockSets { on_entry: &mut on_entry,
-                                         gen_set: &mut gen_set,
-                                         kill_set: &mut kill_set };
-
     for (j, stmt) in statements.iter().enumerate() {
         debug!("rustc_peek: ({:?},{}) {:?}", bb, j, stmt);
         let (place, rvalue) = match stmt.kind {
@@ -170,7 +164,7 @@
                 // Okay, our search is over.
                 match move_data.rev_lookup.find(peeking_at_place) {
                     LookupResult::Exact(peek_mpi) => {
-                        let bit_state = sets.on_entry.contains(peek_mpi);
+                        let bit_state = on_entry.contains(peek_mpi);
                         debug!("rustc_peek({:?} = &{:?}) bit_state: {}",
                                place, peeking_at_place, bit_state);
                         if !bit_state {
@@ -197,18 +191,18 @@
         debug!("rustc_peek: computing effect on place: {:?} ({:?}) in stmt: {:?}",
                place, lhs_mpi, stmt);
         // reset GEN and KILL sets before emulating their effect.
-        sets.gen_set.clear();
-        sets.kill_set.clear();
+        trans.clear();
         results.0.operator.before_statement_effect(
-            &mut sets, Location { block: bb, statement_index: j });
+            &mut trans,
+            Location { block: bb, statement_index: j });
         results.0.operator.statement_effect(
-            &mut sets, Location { block: bb, statement_index: j });
-        sets.on_entry.union(sets.gen_set);
-        sets.on_entry.subtract(sets.kill_set);
+            &mut trans,
+            Location { block: bb, statement_index: j });
+        trans.apply(&mut on_entry);
     }
 
     results.0.operator.before_terminator_effect(
-        &mut sets,
+        &mut trans,
         Location { block: bb, statement_index: statements.len() });
 
     tcx.sess.span_err(span, &format!("rustc_peek: MIR did not match \
diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs
index a9e4ffd..81adfac 100644
--- a/src/librustc_resolve/lib.rs
+++ b/src/librustc_resolve/lib.rs
@@ -1744,12 +1744,12 @@
 /// This interface is used through the AST→HIR step, to embed full paths into the HIR. After that
 /// the resolver is no longer needed as all the relevant information is inline.
 impl<'a> hir::lowering::Resolver for Resolver<'a> {
-    fn resolve_hir_path(
+    fn resolve_ast_path(
         &mut self,
         path: &ast::Path,
         is_value: bool,
-    ) -> hir::Path {
-        self.resolve_hir_path_cb(path, is_value,
+    ) -> Res {
+        self.resolve_ast_path_cb(path, is_value,
                                  |resolver, span, error| resolve_error(resolver, span, error))
     }
 
@@ -1759,7 +1759,7 @@
         crate_root: Option<Symbol>,
         components: &[Symbol],
         is_value: bool
-    ) -> hir::Path {
+    ) -> (ast::Path, Res) {
         let root = if crate_root.is_some() {
             kw::PathRoot
         } else {
@@ -1777,7 +1777,8 @@
             segments,
         };
 
-        self.resolve_hir_path(&path, is_value)
+        let res = self.resolve_ast_path(&path, is_value);
+        (path, res)
     }
 
     fn get_partial_res(&mut self, id: NodeId) -> Option<PartialRes> {
@@ -1803,7 +1804,7 @@
     /// and also it's a private type. Fortunately rustdoc doesn't need to know the error,
     /// just that an error occurred.
     pub fn resolve_str_path_error(&mut self, span: Span, path_str: &str, is_value: bool)
-        -> Result<hir::Path, ()> {
+        -> Result<(ast::Path, Res), ()> {
         let mut errored = false;
 
         let path = if path_str.starts_with("::") {
@@ -1826,29 +1827,29 @@
                     .collect(),
             }
         };
-        let path = self.resolve_hir_path_cb(&path, is_value, |_, _, _| errored = true);
-        if errored || path.res == def::Res::Err {
+        let res = self.resolve_ast_path_cb(&path, is_value, |_, _, _| errored = true);
+        if errored || res == def::Res::Err {
             Err(())
         } else {
-            Ok(path)
+            Ok((path, res))
         }
     }
 
-    /// Like `resolve_hir_path`, but takes a callback in case there was an error.
-    fn resolve_hir_path_cb<F>(
+    /// Like `resolve_ast_path`, but takes a callback in case there was an error.
+    // FIXME(eddyb) use `Result` or something instead of callbacks.
+    fn resolve_ast_path_cb<F>(
         &mut self,
         path: &ast::Path,
         is_value: bool,
         error_callback: F,
-    ) -> hir::Path
+    ) -> Res
         where F: for<'c, 'b> FnOnce(&'c mut Resolver<'_>, Span, ResolutionError<'b>)
     {
         let namespace = if is_value { ValueNS } else { TypeNS };
         let span = path.span;
-        let segments = &path.segments;
         let path = Segment::from_path(&path);
         // FIXME(Manishearth): intra-doc links won't get warned of epoch changes.
-        let res = match self.resolve_path_without_parent_scope(&path, Some(namespace), true,
+        match self.resolve_path_without_parent_scope(&path, Some(namespace), true,
                                                                span, CrateLint::No) {
             PathResult::Module(ModuleOrUniformRoot::Module(module)) =>
                 module.res().unwrap(),
@@ -1869,19 +1870,6 @@
                 });
                 Res::Err
             }
-        };
-
-        let segments: Vec<_> = segments.iter().map(|seg| {
-            let mut hir_seg = hir::PathSegment::from_ident(seg.ident);
-            hir_seg.res = Some(self.partial_res_map.get(&seg.id).map_or(def::Res::Err, |p| {
-                p.base_res().map_id(|_| panic!("unexpected node_id"))
-            }));
-            hir_seg
-        }).collect();
-        hir::Path {
-            span,
-            res: res.map_id(|_| panic!("unexpected node_id")),
-            segments: segments.into(),
         }
     }
 
@@ -3055,7 +3043,7 @@
 
         self.resolve_pats(&arm.pats, PatternSource::Match);
 
-        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);
diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs
index 5623016..392a46a 100644
--- a/src/librustc_resolve/macros.rs
+++ b/src/librustc_resolve/macros.rs
@@ -1022,6 +1022,12 @@
 
     fn suggest_macro_name(&mut self, name: Symbol, kind: MacroKind,
                           err: &mut DiagnosticBuilder<'a>, span: Span) {
+        if kind == MacroKind::Derive && (name.as_str() == "Send" || name.as_str() == "Sync") {
+            let msg = format!("unsafe traits like `{}` should be implemented explicitly", name);
+            err.span_note(span, &msg);
+            return;
+        }
+
         // First check if this is a locally-defined bang macro.
         let suggestion = if let MacroKind::Bang = kind {
             find_best_match_for_name(
diff --git a/src/librustc_save_analysis/dump_visitor.rs b/src/librustc_save_analysis/dump_visitor.rs
index 09406f7..beef9f1 100644
--- a/src/librustc_save_analysis/dump_visitor.rs
+++ b/src/librustc_save_analysis/dump_visitor.rs
@@ -1609,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/span_utils.rs b/src/librustc_save_analysis/span_utils.rs
index 5831b0b..8905f47 100644
--- a/src/librustc_save_analysis/span_utils.rs
+++ b/src/librustc_save_analysis/span_utils.rs
@@ -2,8 +2,6 @@
 
 use crate::generated_code;
 
-use std::cell::Cell;
-
 use syntax::parse::lexer::{self, StringReader};
 use syntax::parse::token::{self, TokenKind};
 use syntax_pos::*;
@@ -11,16 +9,12 @@
 #[derive(Clone)]
 pub struct SpanUtils<'a> {
     pub sess: &'a Session,
-    // FIXME given that we clone SpanUtils all over the place, this err_count is
-    // probably useless and any logic relying on it is bogus.
-    pub err_count: Cell<isize>,
 }
 
 impl<'a> SpanUtils<'a> {
     pub fn new(sess: &'a Session) -> SpanUtils<'a> {
         SpanUtils {
             sess,
-            err_count: Cell::new(0),
         }
     }
 
diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs
index 8ca1b85..85da325 100644
--- a/src/librustc_typeck/check/expr.rs
+++ b/src/librustc_typeck/check/expr.rs
@@ -565,7 +565,7 @@
                 // else an error would have been flagged by the
                 // `loops` pass for using break with an expression
                 // where you are not supposed to.
-                assert!(expr_opt.is_none() || self.tcx.sess.err_count() > 0);
+                assert!(expr_opt.is_none() || self.tcx.sess.has_errors());
             }
 
             ctxt.may_break = true;
@@ -577,10 +577,8 @@
             // this can only happen if the `break` was not
             // inside a loop at all, which is caught by the
             // loop-checking pass.
-            if self.tcx.sess.err_count() == 0 {
-                self.tcx.sess.delay_span_bug(expr.span,
-                    "break was outside loop, but no error was emitted");
-            }
+            self.tcx.sess.delay_span_bug(expr.span,
+                "break was outside loop, but no error was emitted");
 
             // We still need to assign a type to the inner expression to
             // prevent the ICE in #43162.
diff --git a/src/librustc_typeck/check/generator_interior.rs b/src/librustc_typeck/check/generator_interior.rs
index 72f42c8..0bd078d 100644
--- a/src/librustc_typeck/check/generator_interior.rs
+++ b/src/librustc_typeck/check/generator_interior.rs
@@ -28,6 +28,10 @@
               source_span: Span) {
         use syntax_pos::DUMMY_SP;
 
+        debug!("generator_interior: attempting to record type {:?} {:?} {:?} {:?}",
+               ty, scope, expr, source_span);
+
+
         let live_across_yield = scope.map(|s| {
             self.region_scope_tree.yield_in_scope(s).and_then(|yield_data| {
                 // If we are recording an expression that is the last yield
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 0ec5c97..b53159f 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -527,6 +527,8 @@
     /// checking this function. On exit, if we find that *more* errors
     /// have been reported, we will skip regionck and other work that
     /// expects the types within the function to be consistent.
+    // FIXME(matthewjasper) This should not exist, and it's not correct
+    // if type checking is run in parallel.
     err_count_on_creation: usize,
 
     ret_coercion: Option<RefCell<DynamicCoerceMany<'tcx>>>,
@@ -696,11 +698,9 @@
     fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem) { }
 }
 
-pub fn check_wf_new<'tcx>(tcx: TyCtxt<'tcx>) -> Result<(), ErrorReported> {
-    tcx.sess.track_errors(|| {
-        let mut visit = wfcheck::CheckTypeWellFormedVisitor::new(tcx);
-        tcx.hir().krate().par_visit_all_item_likes(&mut visit);
-    })
+pub fn check_wf_new<'tcx>(tcx: TyCtxt<'tcx>) {
+    let mut visit = wfcheck::CheckTypeWellFormedVisitor::new(tcx);
+    tcx.hir().krate().par_visit_all_item_likes(&mut visit);
 }
 
 fn check_mod_item_types<'tcx>(tcx: TyCtxt<'tcx>, module_def_id: DefId) {
@@ -2128,8 +2128,8 @@
         &self.tcx.sess
     }
 
-    pub fn err_count_since_creation(&self) -> usize {
-        self.tcx.sess.err_count() - self.err_count_on_creation
+    pub fn errors_reported_since_creation(&self) -> bool {
+        self.tcx.sess.err_count() > self.err_count_on_creation
     }
 
     /// Produces warning on the given node, if the current point in the
@@ -4375,7 +4375,7 @@
         } else if let ty::Error = leaf_ty.sty {
             // If there is already another error, do not emit
             // an error for not using a type Parameter.
-            assert!(tcx.sess.err_count() > 0);
+            assert!(tcx.sess.has_errors());
             return;
         }
     }
diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs
index 5c71039..5313e1d 100644
--- a/src/librustc_typeck/check/regionck.rs
+++ b/src/librustc_typeck/check/regionck.rs
@@ -123,7 +123,7 @@
         // standalone expr (e.g., the `E` in a type like `[u32; E]`).
         rcx.outlives_environment.save_implied_bounds(id);
 
-        if self.err_count_since_creation() == 0 {
+        if !self.errors_reported_since_creation() {
             // regionck assumes typeck succeeded
             rcx.visit_body(body);
             rcx.visit_region_obligations(id);
@@ -173,7 +173,7 @@
             self.param_env,
         );
 
-        if self.err_count_since_creation() == 0 {
+        if !self.errors_reported_since_creation() {
             // regionck assumes typeck succeeded
             rcx.visit_fn_body(fn_id, body, self.tcx.hir().span(fn_id));
         }
diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs
index ec0f431..b505679 100644
--- a/src/librustc_typeck/lib.rs
+++ b/src/librustc_typeck/lib.rs
@@ -320,6 +320,7 @@
 
     // this ensures that later parts of type checking can assume that items
     // have valid types and not error
+    // FIXME(matthewjasper) We shouldn't need to do this.
     tcx.sess.track_errors(|| {
         time(tcx.sess, "type collecting", || {
             for &module in tcx.hir().krate().modules.keys() {
@@ -352,7 +353,9 @@
         })?;
     }
 
-    time(tcx.sess, "wf checking", || check::check_wf_new(tcx))?;
+    tcx.sess.track_errors(|| {
+        time(tcx.sess, "wf checking", || check::check_wf_new(tcx));
+    })?;
 
     time(tcx.sess, "item-types checking", || {
         for &module in tcx.hir().krate().modules.keys() {
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index 7da501e..29ee59d 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -346,7 +346,7 @@
         // current architecture.
         let resolver = abort_on_err(compiler.expansion(), sess).peek().1.clone();
 
-        if sess.err_count() > 0 {
+        if sess.has_errors() {
             sess.fatal("Compilation failed, aborting rustdoc");
         }
 
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index e6f0992..bb85fe8 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -71,15 +71,16 @@
                 })
             });
 
-            if let Ok(result) = result {
+            if let Ok((_, res)) = result {
+                let res = res.map_id(|_| panic!("unexpected node_id"));
                 // In case this is a trait item, skip the
                 // early return and try looking for the trait.
-                let value = match result.res {
+                let value = match res {
                     Res::Def(DefKind::Method, _) | Res::Def(DefKind::AssocConst, _) => true,
                     Res::Def(DefKind::AssocTy, _) => false,
-                    Res::Def(DefKind::Variant, _) => return handle_variant(cx, result.res),
+                    Res::Def(DefKind::Variant, _) => return handle_variant(cx, res),
                     // Not a trait item; just return what we found.
-                    _ => return Ok((result.res, None))
+                    _ => return Ok((res, None))
                 };
 
                 if value != (ns == ValueNS) {
@@ -129,10 +130,11 @@
 
             // FIXME: `with_scope` requires the `NodeId` of a module.
             let node_id = cx.tcx.hir().hir_to_node_id(id);
-            let ty = cx.enter_resolver(|resolver| resolver.with_scope(node_id, |resolver| {
+            let (_, ty_res) = cx.enter_resolver(|resolver| resolver.with_scope(node_id, |resolver| {
                     resolver.resolve_str_path_error(DUMMY_SP, &path, false)
             }))?;
-            match ty.res {
+            let ty_res = ty_res.map_id(|_| panic!("unexpected node_id"));
+            match ty_res {
                 Res::Def(DefKind::Struct, did)
                 | Res::Def(DefKind::Union, did)
                 | Res::Def(DefKind::Enum, did)
@@ -147,7 +149,7 @@
                             ty::AssocKind::Const if ns == ValueNS => "associatedconstant",
                             _ => return Err(())
                         };
-                        Ok((ty.res, Some(format!("{}.{}", out, item_name))))
+                        Ok((ty_res, Some(format!("{}.{}", out, item_name))))
                     } else {
                         match cx.tcx.type_of(did).sty {
                             ty::Adt(def, _) => {
@@ -159,7 +161,7 @@
                                        .iter()
                                        .find(|item| item.ident.name == item_name)
                                 } {
-                                    Ok((ty.res,
+                                    Ok((ty_res,
                                         Some(format!("{}.{}",
                                                      if def.is_enum() {
                                                          "variant"
@@ -193,7 +195,7 @@
                             _ => return Err(())
                         };
 
-                        Ok((ty.res, Some(format!("{}.{}", kind, item_name))))
+                        Ok((ty_res, Some(format!("{}.{}", kind, item_name))))
                     } else {
                         Err(())
                     }
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 2170420..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>,
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/mut_visit.rs b/src/libsyntax/mut_visit.rs
index 35aa2ee..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
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index b2003e2..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};
@@ -3396,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/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 164fe2f..3f05992 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -2653,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/visit.rs b/src/libsyntax/visit.rs
index 4d96114..9ec9550 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -826,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/bounds.rs b/src/libsyntax_ext/deriving/bounds.rs
index c7b805e..d5b8a00 100644
--- a/src/libsyntax_ext/deriving/bounds.rs
+++ b/src/libsyntax_ext/deriving/bounds.rs
@@ -6,14 +6,6 @@
 use syntax::ext::base::{Annotatable, ExtCtxt};
 use syntax_pos::Span;
 
-pub fn expand_deriving_unsafe_bound(cx: &mut ExtCtxt<'_>,
-                                    span: Span,
-                                    _: &MetaItem,
-                                    _: &Annotatable,
-                                    _: &mut dyn FnMut(Annotatable)) {
-    cx.span_err(span, "this unsafe trait should be implemented explicitly");
-}
-
 pub fn expand_deriving_copy(cx: &mut ExtCtxt<'_>,
                             span: Span,
                             mitem: &MetaItem,
diff --git a/src/libsyntax_ext/deriving/mod.rs b/src/libsyntax_ext/deriving/mod.rs
index e75eff2..aa9913d 100644
--- a/src/libsyntax_ext/deriving/mod.rs
+++ b/src/libsyntax_ext/deriving/mod.rs
@@ -111,8 +111,6 @@
 
     "Default" => default::expand_deriving_default,
 
-    "Send" => bounds::expand_deriving_unsafe_bound,
-    "Sync" => bounds::expand_deriving_unsafe_bound,
     "Copy" => bounds::expand_deriving_copy,
 
     // deprecated
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/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/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/run-pass/generator/addassign-yield.rs b/src/test/run-pass/generator/addassign-yield.rs
new file mode 100644
index 0000000..6a41793
--- /dev/null
+++ b/src/test/run-pass/generator/addassign-yield.rs
@@ -0,0 +1,34 @@
+// Regression test for broken MIR error (#61442)
+// Due to the two possible evaluation orders for
+// a '+=' expression (depending on whether or not the 'AddAssign' trait
+// is being used), we were failing to account for all types that might
+// possibly be live across a yield point.
+
+#![feature(generators)]
+
+fn foo() {
+    let _x = static || {
+        let mut s = String::new();
+        s += { yield; "" };
+    };
+
+    let _y = static || {
+        let x = &mut 0;
+        *{ yield; x } += match String::new() { _ => 0 };
+    };
+
+    // Please don't ever actually write something like this
+    let _z = static || {
+        let x = &mut 0;
+        *{
+            let inner = &mut 1;
+            *{ yield (); inner } += match String::new() { _ => 1};
+            yield;
+            x
+        } += match String::new() { _ => 2 };
+    };
+}
+
+fn main() {
+    foo()
+}
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_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-eval/union-ub-fat-ptr.stderr b/src/test/ui/consts/const-eval/union-ub-fat-ptr.stderr
index 5048a97..19db90c 100644
--- a/src/test/ui/consts/const-eval/union-ub-fat-ptr.stderr
+++ b/src/test/ui/consts/const-eval/union-ub-fat-ptr.stderr
@@ -42,7 +42,7 @@
   --> $DIR/union-ub-fat-ptr.rs:97:1
    |
 LL | const D: &dyn Trait = unsafe { DynTransmute { repr: DynRepr { ptr: &92, vtable: &3 } }.rust};
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid drop fn in vtable
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling or unaligned vtable pointer or too small vtable
    |
    = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
 
@@ -50,7 +50,7 @@
   --> $DIR/union-ub-fat-ptr.rs:100:1
    |
 LL | const E: &dyn Trait = unsafe { DynTransmute { repr2: DynRepr2 { ptr: &92, vtable: &3 } }.rust};
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid drop fn in vtable
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling or unaligned vtable pointer or too small vtable
    |
    = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
 
@@ -58,7 +58,7 @@
   --> $DIR/union-ub-fat-ptr.rs:103:1
    |
 LL | const F: &dyn Trait = unsafe { DynTransmute { bad: BadDynRepr { ptr: &92, vtable: 3 } }.rust};
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-pointer vtable in fat pointer
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling or unaligned vtable pointer or too small vtable
    |
    = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
 
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/consts/enum-discr-type-err.rs b/src/test/ui/consts/enum-discr-type-err.rs
new file mode 100644
index 0000000..d66c4f4
--- /dev/null
+++ b/src/test/ui/consts/enum-discr-type-err.rs
@@ -0,0 +1,29 @@
+// Test that we mark enum discriminant values as having errors, even when the
+// diagnostic is deduplicated.
+
+struct F;
+struct T;
+
+impl F {
+    const V: i32 = 0;
+}
+
+impl T {
+    const V: i32 = 0;
+}
+
+macro_rules! mac {
+    ($( $v: ident = $s: ident,)*) => {
+        enum E {
+            $( $v = $s::V, )*
+            //~^ ERROR mismatched types
+        }
+    }
+}
+
+mac! {
+    A = F,
+    B = T,
+}
+
+fn main() {}
diff --git a/src/test/ui/consts/enum-discr-type-err.stderr b/src/test/ui/consts/enum-discr-type-err.stderr
new file mode 100644
index 0000000..3c4fac7
--- /dev/null
+++ b/src/test/ui/consts/enum-discr-type-err.stderr
@@ -0,0 +1,19 @@
+error[E0308]: mismatched types
+  --> $DIR/enum-discr-type-err.rs:18:21
+   |
+LL |               $( $v = $s::V, )*
+   |                       ^^^^^ expected isize, found i32
+...
+LL | / mac! {
+LL | |     A = F,
+LL | |     B = T,
+LL | | }
+   | |_- in this macro invocation
+help: you can convert an `i32` to `isize` and panic if the converted value wouldn't fit
+   |
+LL |             $( $v = $s::V.try_into().unwrap(), )*
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/consts/issue-62045.rs b/src/test/ui/consts/issue-62045.rs
new file mode 100644
index 0000000..9f41ed9
--- /dev/null
+++ b/src/test/ui/consts/issue-62045.rs
@@ -0,0 +1,5 @@
+// compile-pass
+
+fn main() {
+    assert_eq!(&mut [0; 1][..], &mut []);
+}
diff --git a/src/test/ui/consts/miri_unleashed/mutable_references_ice.rs b/src/test/ui/consts/miri_unleashed/mutable_references_ice.rs
index 4a77534..4fcd89a 100644
--- a/src/test/ui/consts/miri_unleashed/mutable_references_ice.rs
+++ b/src/test/ui/consts/miri_unleashed/mutable_references_ice.rs
@@ -3,6 +3,7 @@
 // rustc-env:RUST_BACKTRACE=0
 // normalize-stderr-test "note: rustc 1.* running on .*" -> "note: rustc VERSION running on TARGET"
 // normalize-stderr-test "note: compiler flags: .*" -> "note: compiler flags: FLAGS"
+// normalize-stderr-test "interpret/intern.rs:[0-9]*:[0-9]*" -> "interpret/intern.rs:LL:CC"
 
 #![allow(const_err)]
 
diff --git a/src/test/ui/consts/miri_unleashed/mutable_references_ice.stderr b/src/test/ui/consts/miri_unleashed/mutable_references_ice.stderr
index ff2351e..82569e2 100644
--- a/src/test/ui/consts/miri_unleashed/mutable_references_ice.stderr
+++ b/src/test/ui/consts/miri_unleashed/mutable_references_ice.stderr
@@ -1,12 +1,12 @@
 warning: skipping const checks
-  --> $DIR/mutable_references_ice.rs:26:9
+  --> $DIR/mutable_references_ice.rs:27:9
    |
 LL |         *MUH.x.get() = 99;
    |         ^^^^^^^^^^^^^^^^^
 
 thread 'rustc' panicked at 'assertion failed: `(left != right)`
   left: `Const`,
- right: `Const`: UnsafeCells are not allowed behind references in constants. This should have been prevented statically by const qualification. If this were allowed one would be able to change a constant at one use site and other use sites may arbitrarily decide to change, too.', src/librustc_mir/interpret/intern.rs:127:17
+ right: `Const`: UnsafeCells are not allowed behind references in constants. This should have been prevented statically by const qualification. If this were allowed one would be able to change a constant at one use site and other use sites may arbitrarily decide to change, too.', src/librustc_mir/interpret/intern.rs:LL:CC
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
 
 error: internal compiler error: unexpected panic
diff --git a/src/test/ui/consts/uninhabited-const-issue-61744.rs b/src/test/ui/consts/uninhabited-const-issue-61744.rs
new file mode 100644
index 0000000..21fbbf8
--- /dev/null
+++ b/src/test/ui/consts/uninhabited-const-issue-61744.rs
@@ -0,0 +1,19 @@
+// compile-fail
+
+pub const unsafe fn fake_type<T>() -> T {
+    hint_unreachable()
+}
+
+pub const unsafe fn hint_unreachable() -> ! {
+    fake_type() //~ ERROR any use of this value will cause an error
+}
+
+trait Const {
+    const CONSTANT: i32 = unsafe { fake_type() };
+}
+
+impl <T> Const for T {}
+
+pub fn main() -> () {
+    dbg!(i32::CONSTANT); //~ ERROR erroneous constant used
+}
diff --git a/src/test/ui/consts/uninhabited-const-issue-61744.stderr b/src/test/ui/consts/uninhabited-const-issue-61744.stderr
new file mode 100644
index 0000000..5c28554
--- /dev/null
+++ b/src/test/ui/consts/uninhabited-const-issue-61744.stderr
@@ -0,0 +1,24 @@
+error: any use of this value will cause an error
+  --> $DIR/uninhabited-const-issue-61744.rs:8:5
+   |
+LL |     fake_type()
+   |     ^^^^^^^^^^^
+   |     |
+   |     tried to call a function with return type T passing return place of type !
+   |     inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5
+   |     inside call to `fake_type::<i32>` at $DIR/uninhabited-const-issue-61744.rs:12:36
+...
+LL |     const CONSTANT: i32 = unsafe { fake_type() };
+   |     ---------------------------------------------
+   |
+   = note: #[deny(const_err)] on by default
+
+error[E0080]: erroneous constant used
+  --> $DIR/uninhabited-const-issue-61744.rs:18:10
+   |
+LL |     dbg!(i32::CONSTANT);
+   |          ^^^^^^^^^^^^^ referenced constant has errors
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0080`.
diff --git a/src/test/ui/derives/deriving-bounds.rs b/src/test/ui/derives/deriving-bounds.rs
index 607cfa1..52659bd 100644
--- a/src/test/ui/derives/deriving-bounds.rs
+++ b/src/test/ui/derives/deriving-bounds.rs
@@ -1,9 +1,9 @@
 #[derive(Send)]
-//~^ ERROR this unsafe trait should be implemented explicitly
+//~^ ERROR cannot find derive macro `Send` in this scope
 struct Test;
 
 #[derive(Sync)]
-//~^ ERROR this unsafe trait should be implemented explicitly
+//~^ ERROR cannot find derive macro `Sync` in this scope
 struct Test1;
 
 pub fn main() {}
diff --git a/src/test/ui/derives/deriving-bounds.stderr b/src/test/ui/derives/deriving-bounds.stderr
index deb84fd..99976da 100644
--- a/src/test/ui/derives/deriving-bounds.stderr
+++ b/src/test/ui/derives/deriving-bounds.stderr
@@ -1,10 +1,22 @@
-error: this unsafe trait should be implemented explicitly
+error: cannot find derive macro `Send` in this scope
+  --> $DIR/deriving-bounds.rs:1:10
+   |
+LL | #[derive(Send)]
+   |          ^^^^
+   |
+note: unsafe traits like `Send` should be implemented explicitly
   --> $DIR/deriving-bounds.rs:1:10
    |
 LL | #[derive(Send)]
    |          ^^^^
 
-error: this unsafe trait should be implemented explicitly
+error: cannot find derive macro `Sync` in this scope
+  --> $DIR/deriving-bounds.rs:5:10
+   |
+LL | #[derive(Sync)]
+   |          ^^^^
+   |
+note: unsafe traits like `Sync` should be implemented explicitly
   --> $DIR/deriving-bounds.rs:5:10
    |
 LL | #[derive(Sync)]
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-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-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-33571.rs b/src/test/ui/issues/issue-33571.rs
index 223bbc3..147fb3f 100644
--- a/src/test/ui/issues/issue-33571.rs
+++ b/src/test/ui/issues/issue-33571.rs
@@ -1,5 +1,5 @@
 #[derive(Clone,
-         Sync, //~ ERROR this unsafe trait should be implemented explicitly
+         Sync, //~ ERROR cannot find derive macro `Sync` in this scope
          Copy)]
 enum Foo {}
 
diff --git a/src/test/ui/issues/issue-33571.stderr b/src/test/ui/issues/issue-33571.stderr
index 5d83a08..78e7202 100644
--- a/src/test/ui/issues/issue-33571.stderr
+++ b/src/test/ui/issues/issue-33571.stderr
@@ -1,4 +1,10 @@
-error: this unsafe trait should be implemented explicitly
+error: cannot find derive macro `Sync` in this scope
+  --> $DIR/issue-33571.rs:2:10
+   |
+LL |          Sync,
+   |          ^^^^
+   |
+note: unsafe traits like `Sync` should be implemented explicitly
   --> $DIR/issue-33571.rs:2:10
    |
 LL |          Sync,
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/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/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/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/tools/cargo b/src/tools/cargo
index 807429e..4c1fa54 160000
--- a/src/tools/cargo
+++ b/src/tools/cargo
@@ -1 +1 @@
-Subproject commit 807429e1b6da4e2ec52488ef2f59e77068c31e1f
+Subproject commit 4c1fa54d10f58d69ac9ff55be68e1b1c25ecb816
diff --git a/src/tools/clippy b/src/tools/clippy
index 5a11ed7..8c80b65 160000
--- a/src/tools/clippy
+++ b/src/tools/clippy
@@ -1 +1 @@
-Subproject commit 5a11ed7b92cc4cf40a4568a8fc1ff54b198c333b
+Subproject commit 8c80b65f10213c6e5f4c6d8113e8f8c9477af568
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/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);